job-pro 1.0.13 → 1.0.15

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.
Files changed (2) hide show
  1. package/dist/index.js +42 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -127,7 +127,8 @@ USAGE
127
127
  job-pro profile show print the loaded profile
128
128
  job-pro find <keyword> search ALL 50 companies in parallel
129
129
  [--limit N] [--companies a,b,c]
130
- [--timeout ms] [--compact | --text]
130
+ [--timeout ms] [--apply-ready]
131
+ [--compact | --text]
131
132
  job-pro --version
132
133
  job-pro help
133
134
 
@@ -164,6 +165,7 @@ VERBS (same surface for every company)
164
165
  pass "-" to read resume from stdin
165
166
  resume-check <resume-text-or--> structural sanity check on a resume
166
167
  apply <post_id> stage an application (Phase 2 dry-run)
168
+ --schema dump raw schema (no profile needed)
167
169
  --print-form emit a fillable JSON template
168
170
  --form-file <path> merge per-job answers
169
171
  --interactive prompt for unanswered fields
@@ -420,6 +422,7 @@ async function runCompany(adapter, company, rawArgs) {
420
422
  if (verb === "apply") {
421
423
  const reallySubmit = args.includes("--really-submit");
422
424
  const printForm = args.includes("--print-form");
425
+ const schemaOnly = args.includes("--schema");
423
426
  const interactive = args.includes("--interactive");
424
427
  const remember = args.includes("--remember");
425
428
  const { args: aDebug, value: debugUrl } = popFlagValue(args, "--debug-submit-to");
@@ -506,7 +509,7 @@ async function runCompany(adapter, company, rawArgs) {
506
509
  void aBatch;
507
510
  const postId = args[0];
508
511
  if (!postId)
509
- die(`usage: job-pro ${company} apply <post_id> [--print-form | --form-file <path> | --interactive [--remember] | --batch <file>] [--debug-submit-to <url> | --really-submit]`);
512
+ die(`usage: job-pro ${company} apply <post_id> [--schema | --print-form | --form-file <path> | --interactive [--remember] | --batch <file>] [--debug-submit-to <url> | --really-submit]`);
510
513
  const fetchSchema = adapter.fetchApplicationSchema;
511
514
  if (typeof fetchSchema !== "function") {
512
515
  return emit({
@@ -527,6 +530,11 @@ async function runCompany(adapter, company, rawArgs) {
527
530
  if (!sr.ok || !sr.schema) {
528
531
  return emit({ ok: false, source: company, post_id: postId, message: sr.message ?? "unknown error" }, compact);
529
532
  }
533
+ // --schema short-circuits everything (and crucially doesn't need a
534
+ // profile). Useful for recon: "what fields does this job ask?".
535
+ if (schemaOnly) {
536
+ return emit({ ok: true, source: company, post_id: postId, schema: sr.schema }, compact);
537
+ }
530
538
  const prof = loadProfile();
531
539
  if (!prof.ok) {
532
540
  return emit({
@@ -1088,10 +1096,22 @@ async function main() {
1088
1096
  if (cmd === "find") {
1089
1097
  const compact = args.includes("--compact");
1090
1098
  const textMode = args.includes("--text");
1099
+ const applyReadyOnly = args.includes("--apply-ready");
1091
1100
  const keyword = args[1];
1092
1101
  if (!keyword || keyword.startsWith("--")) {
1093
- die(`usage: job-pro find <keyword> [--limit N] [--companies a,b,c] [--timeout ms] [--compact | --text]`);
1102
+ die(`usage: job-pro find <keyword> [--limit N] [--companies a,b,c] [--timeout ms] [--apply-ready] [--compact | --text]`);
1094
1103
  }
1104
+ // Static apply-readiness map. Source of truth for what kind of submission
1105
+ // each adapter supports — kept in sync with apply-smoke's submit_kind tally.
1106
+ const ANON_ADAPTERS = new Set(["xpeng", "weride", "hoyoverse"]);
1107
+ const EXTERNAL_ADAPTERS = new Set(["hikvision", "cicc", "cainiao", "webank", "unitree"]);
1108
+ const applyStatusFor = (adapterKey) => {
1109
+ if (EXTERNAL_ADAPTERS.has(adapterKey))
1110
+ return "external";
1111
+ if (ANON_ADAPTERS.has(adapterKey))
1112
+ return "anon";
1113
+ return loadSession(adapterKey) ? "session" : "missing-session";
1114
+ };
1095
1115
  const { args: aLimit, value: limitStr } = popFlagValue(args, "--limit");
1096
1116
  const { args: aCompanies, value: companiesStr } = popFlagValue(aLimit, "--companies");
1097
1117
  const { args: aTimeout, value: timeoutStr } = popFlagValue(aCompanies, "--timeout");
@@ -1126,7 +1146,7 @@ async function main() {
1126
1146
  return { company, ok: false, count: 0, positions: [], message: r.message ?? "search failed", elapsed_ms: elapsed };
1127
1147
  }
1128
1148
  const positions = Array.isArray(r?.positions) ? r.positions.slice(0, limit) : [];
1129
- return { company, ok: true, count: positions.length, positions, elapsed_ms: elapsed };
1149
+ return { company, ok: true, count: positions.length, positions, apply_status: applyStatusFor(company), elapsed_ms: elapsed };
1130
1150
  }
1131
1151
  catch (err) {
1132
1152
  const elapsed = Date.now() - t0;
@@ -1139,13 +1159,24 @@ async function main() {
1139
1159
  }
1140
1160
  }));
1141
1161
  const totalMs = Date.now() - startedAt;
1142
- const withHits = settled.filter((r) => r.count > 0);
1162
+ const allHits = settled.filter((r) => r.count > 0);
1163
+ const withHits = applyReadyOnly
1164
+ ? allHits.filter((r) => r.apply_status === "anon" || r.apply_status === "session")
1165
+ : allHits;
1143
1166
  const total = withHits.reduce((s, r) => s + r.count, 0);
1144
1167
  const failed = settled.filter((r) => !r.ok).map((r) => ({ company: r.company, message: r.message }));
1145
1168
  if (textMode) {
1146
- console.log(`\nfind "${keyword}" ${total} hit(s) across ${withHits.length}/${scope.length} companies (${totalMs}ms)\n`);
1169
+ const STATUS_ICON = {
1170
+ anon: "✅",
1171
+ session: "🟢",
1172
+ "missing-session": "🟡",
1173
+ external: "⛔",
1174
+ };
1175
+ const filterNote = applyReadyOnly ? " [apply-ready only]" : "";
1176
+ console.log(`\nfind "${keyword}" — ${total} hit(s) across ${withHits.length}/${scope.length} companies (${totalMs}ms)${filterNote}\n`);
1147
1177
  for (const r of withHits) {
1148
- console.log(`▌ ${r.company} (${r.count})`);
1178
+ const icon = STATUS_ICON[r.apply_status ?? ""] ?? "?";
1179
+ console.log(`${icon} ${r.company} (${r.count}) — ${r.apply_status}`);
1149
1180
  for (const p of r.positions) {
1150
1181
  const title = (p.title ?? "").trim().replace(/\s+/g, " ");
1151
1182
  const loc = (p.work_cities ?? "").trim();
@@ -1155,6 +1186,10 @@ async function main() {
1155
1186
  }
1156
1187
  console.log("");
1157
1188
  }
1189
+ const hiddenCount = allHits.length - withHits.length;
1190
+ if (applyReadyOnly && hiddenCount > 0) {
1191
+ console.log(`(${hiddenCount} company-bucket(s) hidden — missing-session / external)\n`);
1192
+ }
1158
1193
  if (failed.length > 0) {
1159
1194
  console.log(`Failed (${failed.length}):`);
1160
1195
  for (const f of failed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-pro",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Query Chinese big-tech campus recruiting from your terminal. 50 companies, all 50 live. 46 via each company's own API; the 4 with no public canonical feed (Hikvision, CICC, Cainiao, WeBank) surfaced via Liepin as a clearly-labeled third-party fallback. No signup, no token, no server.",
5
5
  "homepage": "https://job.ha7ch.com",
6
6
  "repository": "https://github.com/HA7CH/job-pro",