@wport/cli 0.5.0 → 0.6.0
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/CHANGELOG.md +20 -0
- package/dist/index.js +47 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [0.6.0] — 2026-07-08
|
|
9
|
+
|
|
10
|
+
issue #106 實測回饋:讓 agent 更容易讀懂錯誤與能力邊界,不必踩裸 40x 才知道。
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`enterprise jobs publish` 失敗明列缺欄**:BR-008 publish gate 回 400(`data.missing_fields`)時,錯誤訊息直接列出缺哪些欄位(如 `Missing required fields: work_hours, headcount`),並提示以 `jobs update` 補齊;不再只有翻譯後的中文一句話。
|
|
15
|
+
- **`enterprise jobs copy` 顯示 `incomplete_fields`**:複製 legacy 職缺(來源子表為空)時,人類模式印出 `⚠ Incomplete for publish — fill before \`jobs publish\`: …`;json 模式回應含 `incomplete_fields`。
|
|
16
|
+
- **`wport doctor` 新增「Enterprise talent scope」區塊**:明示 Enterprise API Key 僅存取「已投遞」人才庫(`talents list --tab visit` 尚未開放、回 400),且**無主動搜尋**——`/api/search-talent` 需互動式 JWT(企業模式)session,API Key → 401(設計如此,非設定錯誤)。
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- `enterprise talents` 群組說明對齊現況:applied pool only、無主動搜尋(詳見 `wport doctor`)。
|
|
21
|
+
|
|
22
|
+
### Notes
|
|
23
|
+
|
|
24
|
+
- 上述錯誤/邊界提示與後端無耦合,對任何後端版本皆生效;`incomplete_fields` 顯示需後端已含 issue #106 copy 回應擴充(已於 backend `main`)。
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
8
28
|
## [0.5.0] — 2026-07-07
|
|
9
29
|
|
|
10
30
|
pm_41 企業 CLI 補完:人才庫、召募活動、公司資料、用量查詢,以及職缺複製/關閉。命令樹至此完整覆蓋 PRD §6.1 企業模式全部命令。
|
package/dist/index.js
CHANGED
|
@@ -179,7 +179,7 @@ function isTimeoutAbort(err) {
|
|
|
179
179
|
return false;
|
|
180
180
|
}
|
|
181
181
|
function buildUserAgent() {
|
|
182
|
-
return `wport-cli/${"0.
|
|
182
|
+
return `wport-cli/${"0.6.0"} (node ${process.version}; ${process.platform})`;
|
|
183
183
|
}
|
|
184
184
|
function unwrapDataResponse(body) {
|
|
185
185
|
if (body && typeof body === "object" && "success" in body && "data" in body) {
|
|
@@ -972,13 +972,20 @@ function registerConfigCommand(program2) {
|
|
|
972
972
|
// src/commands/doctor.ts
|
|
973
973
|
var import_node_fs5 = require("fs");
|
|
974
974
|
var SILENT_IGNORED_PARAMS = ["orderBy", "order"];
|
|
975
|
+
var ENTERPRISE_TALENT_BOUNDARY_NOTES = [
|
|
976
|
+
"`enterprise talents` covers your APPLIED pool only (list / view / respond).",
|
|
977
|
+
"The visit tab is not available yet: `talents list --tab visit` returns 400.",
|
|
978
|
+
"There is NO active candidate search over an API Key. `/api/search-talent` needs an",
|
|
979
|
+
"interactive JWT (company-mode) session and returns 401 for an API Key \u2014 by design,",
|
|
980
|
+
"not a setup error. Do not script talent-sourcing against an Enterprise API Key."
|
|
981
|
+
];
|
|
975
982
|
function registerDoctorCommand(program2) {
|
|
976
983
|
program2.command("doctor").description(
|
|
977
984
|
"Diagnose CLI setup: resolved config, server reachability, schema fingerprint, and known server quirks."
|
|
978
985
|
).action(async (_opts, command) => {
|
|
979
986
|
const ctx = resolveContext(command);
|
|
980
987
|
const line = (s = "") => process.stdout.write(s + "\n");
|
|
981
|
-
line(`wport-cli ${"0.
|
|
988
|
+
line(`wport-cli ${"0.6.0"}`);
|
|
982
989
|
line(` bundled schema fingerprint: ${"839e8a891dfb"}`);
|
|
983
990
|
line("");
|
|
984
991
|
line("Resolved configuration:");
|
|
@@ -998,6 +1005,9 @@ function registerDoctorCommand(program2) {
|
|
|
998
1005
|
line(" \u2022 jobs search sorts by publish date, or by relevance when --keyword is set.");
|
|
999
1006
|
line(" \u2022 jobs view --batch caps parallelism (default 5, max 20) to stay friendly to the API.");
|
|
1000
1007
|
line("");
|
|
1008
|
+
line("Enterprise talent scope (Enterprise API Key):");
|
|
1009
|
+
for (const note of ENTERPRISE_TALENT_BOUNDARY_NOTES) line(` \u2022 ${note}`);
|
|
1010
|
+
line("");
|
|
1001
1011
|
line("Schema drift:");
|
|
1002
1012
|
line(" The fingerprint above identifies the OpenAPI contract this CLI was built against.");
|
|
1003
1013
|
line(" Automated drift detection needs a server-side schema-version endpoint, which is");
|
|
@@ -1087,8 +1097,25 @@ function throwEnterpriseHttpError(status, body) {
|
|
|
1087
1097
|
ExitCode.ServerClientError
|
|
1088
1098
|
);
|
|
1089
1099
|
}
|
|
1100
|
+
if (status === 400) {
|
|
1101
|
+
const missingFields = extractMissingFields(body);
|
|
1102
|
+
if (missingFields.length > 0) {
|
|
1103
|
+
throw new CliError(
|
|
1104
|
+
`${base} \u2014 Missing required fields: ${missingFields.join(", ")}. Fill them via \`wport enterprise jobs update <enc_id> ...\` (or the web console), then publish.`,
|
|
1105
|
+
ExitCode.ServerClientError
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1090
1109
|
throwForHttpStatus(status, body);
|
|
1091
1110
|
}
|
|
1111
|
+
function extractMissingFields(body) {
|
|
1112
|
+
if (!body || typeof body !== "object") return [];
|
|
1113
|
+
const data = body.data;
|
|
1114
|
+
if (!data || typeof data !== "object") return [];
|
|
1115
|
+
const fields = data.missing_fields;
|
|
1116
|
+
if (!Array.isArray(fields)) return [];
|
|
1117
|
+
return fields.filter((f) => typeof f === "string");
|
|
1118
|
+
}
|
|
1092
1119
|
function warnIfRateLimitLow(headers) {
|
|
1093
1120
|
const remaining = Number(headers.get("x-ratelimit-remaining"));
|
|
1094
1121
|
const limit = Number(headers.get("x-ratelimit-limit"));
|
|
@@ -1525,6 +1552,15 @@ async function runJobsCopy(ctx, apiKey, encId, idempotencyKey) {
|
|
|
1525
1552
|
}
|
|
1526
1553
|
process.stdout.write(`Copied job ${trimmed} \u2192 new draft: ${result.enc_id ?? "(unknown)"}
|
|
1527
1554
|
`);
|
|
1555
|
+
const incomplete = extractStringArray(result.incomplete_fields);
|
|
1556
|
+
if (incomplete.length > 0) {
|
|
1557
|
+
process.stdout.write(` \u26A0 Incomplete for publish \u2014 fill before \`jobs publish\`: ${incomplete.join(", ")}
|
|
1558
|
+
`);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
function extractStringArray(value) {
|
|
1562
|
+
if (!Array.isArray(value)) return [];
|
|
1563
|
+
return value.filter((v) => typeof v === "string");
|
|
1528
1564
|
}
|
|
1529
1565
|
function registerEnterpriseJobsCopy(parent) {
|
|
1530
1566
|
parent.command("copy <enc_id>").description("Copy a job posting into a new unpublished draft").option("--idempotency-key <key>", "reuse across retries (default: a fresh UUID)").action(async (encId, flags, command) => {
|
|
@@ -2243,10 +2279,13 @@ async function runEnterpriseTalentsRespond(ctx, apiKey, encResumeId, flags, idem
|
|
|
2243
2279
|
throw new CliError("--subject is required and must not be empty", ExitCode.InvalidArgument);
|
|
2244
2280
|
}
|
|
2245
2281
|
const body = resolveRespondBody(flags, { timeoutMs: ctx.timeoutMs });
|
|
2282
|
+
const encJobId = flags.encJobId?.trim();
|
|
2283
|
+
const payload = { subject, body };
|
|
2284
|
+
if (encJobId) payload.enc_job_id = encJobId;
|
|
2246
2285
|
const { body: respBody } = await enterprisePost(
|
|
2247
2286
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2248
2287
|
`/talents/${encodeURIComponent(trimmedId)}/respond`,
|
|
2249
|
-
|
|
2288
|
+
payload,
|
|
2250
2289
|
{ idempotencyKey }
|
|
2251
2290
|
);
|
|
2252
2291
|
const result = unwrapDataResponse(respBody);
|
|
@@ -2258,7 +2297,7 @@ async function runEnterpriseTalentsRespond(ctx, apiKey, encResumeId, flags, idem
|
|
|
2258
2297
|
`);
|
|
2259
2298
|
}
|
|
2260
2299
|
function registerEnterpriseTalentsRespond(parent) {
|
|
2261
|
-
parent.command("respond <enc_resume_id>").description("Reply to an applicant (in-app message; inherits the company daily reply quota)").requiredOption("--subject <subject>", "message subject (max 200)").option("--body <text>", "message body text (max 5000)").option("--body-file <path>", 'read message body from a file, or "-" for stdin').option("--idempotency-key <key>", "reuse across retries to avoid duplicate sends (default: a fresh UUID)").action(async (encResumeId, flags, command) => {
|
|
2300
|
+
parent.command("respond <enc_resume_id>").description("Reply to an applicant (in-app message; inherits the company daily reply quota)").requiredOption("--subject <subject>", "message subject (max 200)").option("--body <text>", "message body text (max 5000)").option("--body-file <path>", 'read message body from a file, or "-" for stdin').option("--enc-job-id <enc_id>", "reply to a specific job posting; omit to reply to the most recent application").option("--idempotency-key <key>", "reuse across retries to avoid duplicate sends (default: a fresh UUID)").action(async (encResumeId, flags, command) => {
|
|
2262
2301
|
const ctx = resolveContext(command);
|
|
2263
2302
|
const globals = command.optsWithGlobals();
|
|
2264
2303
|
const { key } = resolveApiKey(globals.apiKey);
|
|
@@ -2268,7 +2307,9 @@ function registerEnterpriseTalentsRespond(parent) {
|
|
|
2268
2307
|
|
|
2269
2308
|
// src/commands/enterprise/talents/index.ts
|
|
2270
2309
|
function registerEnterpriseTalentsCommand(parent) {
|
|
2271
|
-
const talents = parent.command("talents").description(
|
|
2310
|
+
const talents = parent.command("talents").description(
|
|
2311
|
+
"Browse & respond to your applied talent pool (visit tab n/a, no active candidate search \u2014 see `wport doctor`)"
|
|
2312
|
+
);
|
|
2272
2313
|
registerEnterpriseTalentsList(talents);
|
|
2273
2314
|
registerEnterpriseTalentsView(talents);
|
|
2274
2315
|
registerEnterpriseTalentsRespond(talents);
|
|
@@ -2477,7 +2518,7 @@ function registerEnterpriseCommand(program2) {
|
|
|
2477
2518
|
|
|
2478
2519
|
// src/index.ts
|
|
2479
2520
|
var program = new import_commander.Command();
|
|
2480
|
-
program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.
|
|
2521
|
+
program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.6.0", "-v, --version", "output the CLI version").option("--lang <locale>", "Accept-Language locale: zh-TW | en-US | vi-VN | th-TH | id-ID").option("--api <url>", "override API base URL").option("--output <fmt>", "output format: table | json").option("--no-color", "disable color output").option("--timeout <ms>", "HTTP timeout in milliseconds", (v) => Number(v));
|
|
2481
2522
|
registerJobsCommand(program);
|
|
2482
2523
|
registerConfigCommand(program);
|
|
2483
2524
|
registerDoctorCommand(program);
|