job-pro 1.0.87 → 1.0.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/apply.js CHANGED
@@ -1164,13 +1164,17 @@ export async function executeCdpRealBrowser(staged, session, target) {
1164
1164
  if (target.kind === "dry-run") {
1165
1165
  return { ok: false, posted_to: "dry-run (no network)", message: "dry-run requested — no HTTP call fired", steps: [] };
1166
1166
  }
1167
- if (target.kind === "upstream" && !session) {
1167
+ // Non-anon adapters need session.json (the SPA's login cookies); anon
1168
+ // multipart adapters (Greenhouse/Lever boards) can fire the apply form
1169
+ // without a session — the DOM walker handles those too.
1170
+ const needsSession = staged.submit_kind !== "multipart-anon";
1171
+ if (target.kind === "upstream" && needsSession && !session) {
1168
1172
  return {
1169
1173
  ok: false,
1170
1174
  posted_to: staged.apply_url,
1171
- message: "executeCdpRealBrowser requires session.json (the SPA's login cookies need to be in " +
1172
- "the puppeteer browser before navigation). Capture via extension/, drop under " +
1173
- "~/.jobpro/<adapter>.session.json.",
1175
+ message: "executeCdpRealBrowser requires session.json for non-anon adapters " +
1176
+ "(the SPA's login cookies need to be in the puppeteer browser before " +
1177
+ "navigation). Run `job-pro extension` to capture one.",
1174
1178
  steps: [],
1175
1179
  };
1176
1180
  }
@@ -1223,12 +1227,22 @@ export async function executeCdpRealBrowser(staged, session, target) {
1223
1227
  await new Promise((resolve) => setTimeout(resolve, 3000));
1224
1228
  return { kind: "debug" };
1225
1229
  }
1226
- // Try to click the "投递" / "立即投递" / "Apply" button to open the modal.
1230
+ // Try to click the apply button. The label patterns we see across
1231
+ // the 45 verified adapters: 投递, 立即投递, 投递简历, 在线投递, 申请,
1232
+ // 立即申请, 申请职位, 申请岗位, 网申, Apply, Apply Now, Submit
1233
+ // Application. Exclude common no-go labels like "查看投递", "我的投递",
1234
+ // "投递记录" that link to the user's history.
1227
1235
  const clickedApply = await page.evaluate(() => {
1228
- const candidates = Array.from(document.querySelectorAll('button, a'));
1236
+ const include = /(?:^|[^查我])(?:投递|申请|网申|Apply)/i;
1237
+ const exclude = /(?:查看|我的|历史|记录|状态|进度|history)/i;
1238
+ const candidates = Array.from(document.querySelectorAll('button, a, [role="button"]'));
1229
1239
  for (const el of candidates) {
1230
1240
  const t = (el.textContent ?? "").trim();
1231
- if (/^投递$|^立即投递$|^申请$|^Apply$/i.test(t)) {
1241
+ if (!t || t.length > 30)
1242
+ continue; // long labels rarely Apply buttons
1243
+ if (exclude.test(t))
1244
+ continue;
1245
+ if (include.test(t)) {
1232
1246
  el.click();
1233
1247
  return t;
1234
1248
  }
@@ -1280,12 +1294,19 @@ export async function executeCdpRealBrowser(staged, session, target) {
1280
1294
  catch (err) {
1281
1295
  steps.push({ step: "upload-resume", url: page.url(), status: 0, ok: false, message: String(err) });
1282
1296
  }
1283
- // Click the modal's submit button (typically "确认投递" / "提交").
1297
+ // Click the modal's submit button. Common labels: 确认投递, 提交,
1298
+ // 完成, 立即提交, 确认提交, Submit, Confirm. Exclude "取消", "关闭".
1284
1299
  const submittedLabel = await page.evaluate(() => {
1300
+ const include = /(?:确认投递|提交|确认提交|确认申请|完成|Submit|Confirm)/i;
1301
+ const exclude = /(?:取消|关闭|返回|Cancel|Close|Back)/i;
1285
1302
  const candidates = Array.from(document.querySelectorAll('button, [role="button"]'));
1286
1303
  for (const el of candidates) {
1287
1304
  const t = (el.textContent ?? "").trim();
1288
- if (/^(确认投递|提交|完成|Submit)$/i.test(t)) {
1305
+ if (!t || t.length > 30)
1306
+ continue;
1307
+ if (exclude.test(t))
1308
+ continue;
1309
+ if (include.test(t)) {
1289
1310
  el.click();
1290
1311
  return t;
1291
1312
  }
package/dist/index.js CHANGED
@@ -235,6 +235,9 @@ VERBS (same surface for every company)
235
235
  --debug-submit-to <url> verify wire format
236
236
  --debug-submit ↑ shorthand → httpbin.org/post
237
237
  --really-submit actually fire (env-gated)
238
+ --via-cdp drive a puppeteer browser through
239
+ the SPA's apply form (DOM-based,
240
+ bypasses API body-shape uncertainty)
238
241
  --allow-stale-session bypass 30-day session-age gate
239
242
  memory list | get <k> | set k=v | event <kind> [payload] | clear
240
243
 
@@ -484,6 +487,7 @@ async function runCompany(adapter, company, rawArgs) {
484
487
  }
485
488
  if (verb === "apply") {
486
489
  const reallySubmit = args.includes("--really-submit");
490
+ const viaCdp = args.includes("--via-cdp");
487
491
  const printForm = args.includes("--print-form");
488
492
  const schemaOnly = args.includes("--schema");
489
493
  const interactive = args.includes("--interactive");
@@ -793,14 +797,24 @@ async function runCompany(adapter, company, rawArgs) {
793
797
  }
794
798
  // Family executors: each takes (staged, session, target) and returns
795
799
  // a MultiStepResult. All gate on session.json existing.
796
- const familyExecutor = kind === "feishu-3-step" ? executeFeishu3Step :
797
- kind === "moka-aes" ? executeMokaApply :
798
- kind === "beisen-wecruit" ? executeBeisenWecruit :
799
- kind === "beisen-italent" ? executeBeisenITalent :
800
- kind === "cdp-real-browser" ? executeCdpRealBrowser :
801
- null;
800
+ // --via-cdp forces the puppeteer DOM-driven path for any adapter,
801
+ // bypassing the API endpoint and any body-shape uncertainty. The
802
+ // CDP executor walks the SPA's apply form like a human: click
803
+ // "投递", fill name/email/phone, upload resume, click submit.
804
+ // Slower + needs Chrome, but reliable when API is uncertain.
805
+ const familyExecutor = viaCdp ? executeCdpRealBrowser :
806
+ kind === "feishu-3-step" ? executeFeishu3Step :
807
+ kind === "moka-aes" ? executeMokaApply :
808
+ kind === "beisen-wecruit" ? executeBeisenWecruit :
809
+ kind === "beisen-italent" ? executeBeisenITalent :
810
+ kind === "cdp-real-browser" ? executeCdpRealBrowser :
811
+ null;
802
812
  if (familyExecutor) {
803
- if (!session) {
813
+ // --via-cdp on multipart-anon (xpeng/weride/hoyoverse) doesn't
814
+ // need a session — Greenhouse/Lever forms accept anon submits.
815
+ // The CDP executor's own check is relaxed for multipart-anon.
816
+ const sessionRequired = !(viaCdp && kind === "multipart-anon");
817
+ if (sessionRequired && !session) {
804
818
  return emit({
805
819
  ok: false,
806
820
  source: company,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-pro",
3
- "version": "1.0.87",
3
+ "version": "1.0.89",
4
4
  "description": "Query Chinese big-tech campus recruiting + auto-apply from your terminal. 50 companies, all 50 live (46 via official APIs, 4 via Liepin third-party fallback). 45/50 with end-to-end verified apply endpoints; 5 structurally-external (Liepin IM × 4 + Unitree WeChat). No signup, no token, no server.",
5
5
  "homepage": "https://job.ha7ch.com",
6
6
  "repository": "https://github.com/HA7CH/job-pro",