@wport/cli 0.9.3 → 0.10.1

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/index.js CHANGED
@@ -387,7 +387,7 @@ function registerJobsSearch(parent) {
387
387
  baseUrl: ctx.baseUrl,
388
388
  locale: ctx.locale,
389
389
  timeoutMs: ctx.timeoutMs,
390
- userAgent: (0, import_core2.buildUserAgent)("wport-cli", "0.9.3"),
390
+ userAgent: (0, import_core2.buildUserAgent)("wport-cli", "0.10.1"),
391
391
  source: CLI_SOURCE
392
392
  });
393
393
  const { data, error, response } = await client.GET("/api/jobs/search", {
@@ -632,7 +632,7 @@ function registerJobsView(parent) {
632
632
  baseUrl: ctx.baseUrl,
633
633
  locale: ctx.locale,
634
634
  timeoutMs: ctx.timeoutMs,
635
- userAgent: (0, import_core3.buildUserAgent)("wport-cli", "0.9.3"),
635
+ userAgent: (0, import_core3.buildUserAgent)("wport-cli", "0.10.1"),
636
636
  source: CLI_SOURCE
637
637
  });
638
638
  if (flags.batch) {
@@ -1073,7 +1073,7 @@ async function oauthPost(opts, path, body) {
1073
1073
  method: "POST",
1074
1074
  headers: {
1075
1075
  "Accept-Language": opts.locale,
1076
- "User-Agent": (0, import_core5.buildUserAgent)("wport-cli", "0.9.3"),
1076
+ "User-Agent": (0, import_core5.buildUserAgent)("wport-cli", "0.10.1"),
1077
1077
  "X-Source": CLI_SOURCE,
1078
1078
  Accept: "application/json",
1079
1079
  "Content-Type": "application/json"
@@ -1156,7 +1156,7 @@ function registerDoctorCommand(program2) {
1156
1156
  }
1157
1157
  async function runDoctor(ctx) {
1158
1158
  const line = (s = "") => process.stdout.write(s + "\n");
1159
- line(`wport-cli ${"0.9.3"}`);
1159
+ line(`wport-cli ${"0.10.1"}`);
1160
1160
  line(` bundled schema fingerprint: ${"839e8a891dfb"}`);
1161
1161
  line("");
1162
1162
  line("Resolved configuration:");
@@ -1216,7 +1216,7 @@ async function probeServer(ctx, line) {
1216
1216
  baseUrl: ctx.baseUrl,
1217
1217
  locale: ctx.locale,
1218
1218
  timeoutMs: ctx.timeoutMs,
1219
- userAgent: (0, import_core7.buildUserAgent)("wport-cli", "0.9.3"),
1219
+ userAgent: (0, import_core7.buildUserAgent)("wport-cli", "0.10.1"),
1220
1220
  source: CLI_SOURCE
1221
1221
  });
1222
1222
  const { response } = await client.GET("/api/jobs/search", { params: { query: { pageSize: 1 } } });
@@ -1246,11 +1246,61 @@ async function probeAuthServer(ctx, line) {
1246
1246
  var import_core8 = require("@wport/core");
1247
1247
  var transport = __toESM(require("@wport/core"));
1248
1248
  function withUserAgent(opts) {
1249
- return { ...opts, userAgent: (0, import_core8.buildUserAgent)("wport-cli", "0.9.3"), source: CLI_SOURCE };
1249
+ return { ...opts, userAgent: (0, import_core8.buildUserAgent)("wport-cli", "0.10.1"), source: CLI_SOURCE };
1250
+ }
1251
+ function extractErrorCodeBody(body) {
1252
+ if (!body || typeof body !== "object") return void 0;
1253
+ const b = body;
1254
+ const code = b.error_code;
1255
+ if (code !== "block_max_exceeded" && code !== "unsupported_video_provider" && code !== "block_item_forbidden" && code !== "profile_revision_conflict" && code !== "profile_revision_required") {
1256
+ return void 0;
1257
+ }
1258
+ return {
1259
+ error_code: code,
1260
+ block: typeof b.block === "string" ? b.block : void 0,
1261
+ limit: typeof b.limit === "number" ? b.limit : void 0,
1262
+ current_revision: typeof b.current_revision === "string" ? b.current_revision : void 0
1263
+ };
1264
+ }
1265
+ function decorateByErrorCode(base, err, info) {
1266
+ switch (info.error_code) {
1267
+ case "block_max_exceeded":
1268
+ return new import_core8.WportHttpError(
1269
+ `${base} \u2014 Block "${info.block ?? "unknown"}" exceeds its item limit` + (info.limit !== void 0 ? ` (max ${info.limit})` : "") + ". Run `wport enterprise company view --output json` to see the current items before replacing.",
1270
+ err.status,
1271
+ err.body
1272
+ );
1273
+ case "unsupported_video_provider":
1274
+ return new import_core8.WportHttpError(
1275
+ `${base} \u2014 Unsupported video URL. Accepted forms: https://www.youtube.com/watch?v=<id>, https://youtu.be/<id>, https://vimeo.com/<id>, https://player.vimeo.com/video/<id>.`,
1276
+ err.status,
1277
+ err.body
1278
+ );
1279
+ case "block_item_forbidden":
1280
+ return new import_core8.WportHttpError(
1281
+ `${base} \u2014 This enc_id (block "${info.block ?? "unknown"}") does not belong to your company or no longer exists. Run \`wport enterprise company view\` to get the latest enc_id list.`,
1282
+ err.status,
1283
+ err.body
1284
+ );
1285
+ case "profile_revision_conflict":
1286
+ return new import_core8.WportHttpError(
1287
+ `${base} \u2014 Block "${info.block ?? "unknown"}" changed since you last read it` + (info.current_revision ? ` (current revision: ${info.current_revision})` : "") + ". This CLI will not auto-retry: re-run `wport enterprise company view` to see the latest state, then confirm again.",
1288
+ err.status,
1289
+ err.body
1290
+ );
1291
+ case "profile_revision_required":
1292
+ return new import_core8.WportHttpError(
1293
+ `${base} \u2014 This request needs an If-Match revision (block "${info.block ?? "unknown"}") and none was recognized. This usually means the CLI is out of date \u2014 upgrade it, or re-run \`wport enterprise company view\` to fetch a fresh revision before retrying.`,
1294
+ err.status,
1295
+ err.body
1296
+ );
1297
+ }
1250
1298
  }
1251
1299
  function decorateEnterpriseError(err) {
1252
1300
  if (!(err instanceof import_core8.WportHttpError)) return err;
1253
1301
  const base = err.message;
1302
+ const errorCodeInfo = extractErrorCodeBody(err.body);
1303
+ if (errorCodeInfo) return decorateByErrorCode(base, err, errorCodeInfo);
1254
1304
  if (err.status === 401) {
1255
1305
  return new import_core8.WportHttpError(
1256
1306
  `${base} \u2014 If your key has expired, rotate it in place: \`wport enterprise keys rotate <enc_id>\` (an expired key is still accepted for rotate). If it was revoked or is incorrect, obtain a valid key and run \`wport enterprise login\`.`,
@@ -1311,6 +1361,9 @@ function enterprisePost2(opts, path, body, extra) {
1311
1361
  function enterprisePatch2(opts, path, body, extra) {
1312
1362
  return wrap(transport.enterprisePatch(withUserAgent(opts), path, body, extra));
1313
1363
  }
1364
+ function enterprisePut2(opts, path, body, extra) {
1365
+ return wrap(transport.enterprisePut(withUserAgent(opts), path, body, extra));
1366
+ }
1314
1367
  function enterpriseDelete2(opts, path, extra) {
1315
1368
  return wrap(transport.enterpriseDelete(withUserAgent(opts), path, extra));
1316
1369
  }
@@ -1408,10 +1461,16 @@ async function runUsage(ctx, apiKey) {
1408
1461
  const rate = usage.rate_limit ?? {};
1409
1462
  const lines = [
1410
1463
  `period: ${usage.period ?? "\u2014"}`,
1411
- `monthly quota: ${num(quota.used)} / ${num(quota.limit)} used (${num(quota.remaining)} remaining)`,
1464
+ `monthly quota: ${num(quota.used)} / ${num(quota.limit)} calls used (${num(quota.remaining)} remaining)`,
1412
1465
  `rate limit: ${num(rate.limit)} requests / ${num(rate.window_seconds)}s per key`
1413
1466
  ];
1414
1467
  process.stdout.write(lines.join("\n") + "\n");
1468
+ process.stdout.write(
1469
+ dim(
1470
+ "Monthly quota meters every authenticated API call; whoami/usage and key rotate are exempt.",
1471
+ ctx.color
1472
+ ) + "\n"
1473
+ );
1415
1474
  process.stdout.write(
1416
1475
  dim("Live per-window rate-limit headroom is reported via response headers on write requests.", ctx.color) + "\n"
1417
1476
  );
@@ -1921,6 +1980,15 @@ function renderDetailLines2(company) {
1921
1980
  }
1922
1981
  return lines;
1923
1982
  }
1983
+ var BLOCK_SUMMARY_FIELDS = ["pain_points", "awards", "milestones", "qa_items", "video_links"];
1984
+ function renderBlockSummaryLines(company) {
1985
+ const width = Math.max(...BLOCK_SUMMARY_FIELDS.map((f) => f.length)) + 3;
1986
+ return BLOCK_SUMMARY_FIELDS.map((field) => {
1987
+ const value = company[field];
1988
+ const count = Array.isArray(value) ? value.length : 0;
1989
+ return `${(field + ":").padEnd(width)}${count}`;
1990
+ });
1991
+ }
1924
1992
  async function runCompanyView(ctx, apiKey, flags) {
1925
1993
  const { body } = await enterpriseGet2(
1926
1994
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
@@ -1935,7 +2003,8 @@ async function runCompanyView(ctx, apiKey, flags) {
1935
2003
  printJson(company);
1936
2004
  return;
1937
2005
  }
1938
- process.stdout.write(renderDetailLines2(company).join("\n") + "\n");
2006
+ const lines = [...renderDetailLines2(company), "", ...renderBlockSummaryLines(company)];
2007
+ process.stdout.write(lines.join("\n") + "\n");
1939
2008
  }
1940
2009
  function registerEnterpriseCompanyView(parent) {
1941
2010
  parent.command("view").description("View your company information").option("--fields <list>", "output selected fields as JSON (comma-separated dotted paths)").action(async (flags, command) => {
@@ -1950,6 +2019,52 @@ function registerEnterpriseCompanyView(parent) {
1950
2019
  var import_node_crypto5 = require("crypto");
1951
2020
  var import_core19 = require("@wport/core");
1952
2021
 
2022
+ // src/commands/enterprise/company/block-replace-shared.ts
2023
+ function assertValidSubmittedItems(value, label) {
2024
+ if (!Array.isArray(value)) {
2025
+ throw new InvalidArgumentError(
2026
+ `"${label}" must be a JSON array, got ${value === null ? "null" : typeof value}`
2027
+ );
2028
+ }
2029
+ value.forEach((item, index) => {
2030
+ if (item === null || typeof item !== "object" || Array.isArray(item)) {
2031
+ throw new InvalidArgumentError(
2032
+ `"${label}[${index}]" must be a JSON object, got ` + (item === null ? "null" : Array.isArray(item) ? "an array" : typeof item)
2033
+ );
2034
+ }
2035
+ });
2036
+ return value;
2037
+ }
2038
+ function previewDeletions(current, submitted, summarize) {
2039
+ const submittedIds = new Set(
2040
+ submitted.filter((i) => typeof i.enc_id === "string").map((i) => i.enc_id)
2041
+ );
2042
+ return current.filter((item) => !submittedIds.has(item.enc_id)).map((item) => ({ enc_id: item.enc_id, summary: summarize(item) }));
2043
+ }
2044
+ function enforceDeletionConfirmation(label, deletions, confirm, color) {
2045
+ if (deletions.length === 0) return;
2046
+ printWarn(
2047
+ `This replace will delete ${deletions.length} existing "${label}" item(s): ` + deletions.map((d) => `${d.enc_id} (${d.summary || "untitled"})`).join("; "),
2048
+ color
2049
+ );
2050
+ if (!confirm) {
2051
+ throw new InvalidArgumentError(
2052
+ `Refusing to replace "${label}" without --confirm: ${deletions.length} existing item(s) would be deleted (see the warning above for the enc_id list). Re-run with --confirm to proceed.`
2053
+ );
2054
+ }
2055
+ }
2056
+ function summarizeItemsDiff(before, after) {
2057
+ const beforeIds = new Set(before.map((i) => i.enc_id));
2058
+ const afterIds = new Set(after.map((i) => i.enc_id));
2059
+ const added = after.filter((i) => !beforeIds.has(i.enc_id)).map((i) => i.enc_id);
2060
+ const removed = before.filter((i) => !afterIds.has(i.enc_id)).map((i) => i.enc_id);
2061
+ const parts = [];
2062
+ if (added.length > 0) parts.push(`added since your read: ${added.join(", ")}`);
2063
+ if (removed.length > 0) parts.push(`removed since your read: ${removed.join(", ")}`);
2064
+ if (parts.length === 0) parts.push("item content or order changed (enc_id set unchanged)");
2065
+ return parts.join("; ");
2066
+ }
2067
+
1953
2068
  // src/commands/enterprise/company/types.ts
1954
2069
  var BASIC_FIELDS = [
1955
2070
  "name",
@@ -1961,24 +2076,25 @@ var BASIC_FIELDS = [
1961
2076
  "capital_show_status",
1962
2077
  "website",
1963
2078
  "area_code",
1964
- "address"
2079
+ "address",
2080
+ "representative_name",
2081
+ "establishment_date",
2082
+ "directors",
2083
+ "is_representative_visible",
2084
+ "is_establishment_date_visible",
2085
+ "is_directors_visible"
1965
2086
  ];
1966
- var DESCRIPTION_FIELDS = ["description", "products_services", "latest_news"];
2087
+ var DESCRIPTION_FIELDS = ["description", "products_services", "latest_news", "video_links"];
1967
2088
  var FORBIDDEN_FIELDS = [
1968
2089
  "uniform_number",
2090
+ "is_uniform_number_visible",
1969
2091
  "banner_url",
1970
2092
  "photo_1",
1971
2093
  "photo_2",
1972
2094
  "photo_3",
1973
2095
  "video_1",
1974
2096
  "video_2",
1975
- "video_3",
1976
- "foundation_date",
1977
- "representative",
1978
- "directors",
1979
- "milestones",
1980
- "awards",
1981
- "qa"
2097
+ "video_3"
1982
2098
  ];
1983
2099
 
1984
2100
  // src/commands/enterprise/company/update.ts
@@ -2046,12 +2162,25 @@ async function buildCompanyUpdatePayloads(input, ctx, apiKey) {
2046
2162
  }
2047
2163
  let descriptions = null;
2048
2164
  if (inputHasDescriptionField) {
2049
- descriptions = {};
2165
+ const partial = {};
2050
2166
  for (const field of DESCRIPTION_FIELDS) {
2051
- if (field in input) descriptions[field] = input[field];
2167
+ if (field in input) partial[field] = input[field];
2052
2168
  }
2169
+ descriptions = partial;
2053
2170
  }
2054
- return { basic, descriptions };
2171
+ let videoLinksRevision;
2172
+ let currentVideoLinks;
2173
+ if ("video_links" in input) {
2174
+ currentVideoLinks = current.video_links ?? [];
2175
+ videoLinksRevision = current.block_revisions?.video_links;
2176
+ if (!videoLinksRevision) {
2177
+ throw new CliError(
2178
+ "Backend contract appears broken: GET /company response is missing block_revisions.video_links",
2179
+ ExitCode.ServerOrNetworkError
2180
+ );
2181
+ }
2182
+ }
2183
+ return { basic, descriptions, videoLinksRevision, currentVideoLinks };
2055
2184
  }
2056
2185
  function isDescriptionsPartialFailureBody(body) {
2057
2186
  return !!body && typeof body === "object" && Array.isArray(body.updated_sections) && typeof body.failed_section === "string";
@@ -2076,12 +2205,21 @@ function resolveUpdateIdempotencyKeys(needsBasic, needsDescriptions, flags) {
2076
2205
  descriptionsKey: needsDescriptions ? flags.descriptionsIdempotencyKey ?? flags.idempotencyKey ?? (0, import_node_crypto5.randomUUID)() : void 0
2077
2206
  };
2078
2207
  }
2079
- async function runCompanyUpdate(ctx, apiKey, source, idempotencyFlags, options = {}) {
2208
+ async function runCompanyUpdate(ctx, apiKey, source, idempotencyFlags, options = {}, confirm = false) {
2080
2209
  const input = readJsonObject(source, options);
2081
2210
  const needsBasic = BASIC_FIELDS.some((f) => f in input);
2082
2211
  const needsDescriptions = DESCRIPTION_FIELDS.some((f) => f in input);
2212
+ const needsVideoLinks = "video_links" in input;
2213
+ if (needsVideoLinks) {
2214
+ assertValidSubmittedItems(input.video_links, "video_links");
2215
+ }
2083
2216
  const { basicKey, descriptionsKey } = resolveUpdateIdempotencyKeys(needsBasic, needsDescriptions, idempotencyFlags);
2084
2217
  const payloads = await buildCompanyUpdatePayloads(input, ctx, apiKey);
2218
+ if (needsVideoLinks) {
2219
+ const submitted = payloads.descriptions?.video_links ?? [];
2220
+ const deletions = previewDeletions(payloads.currentVideoLinks ?? [], submitted, (item) => String(item.url ?? ""));
2221
+ enforceDeletionConfirmation("video_links", deletions, confirm, ctx.color ?? false);
2222
+ }
2085
2223
  const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
2086
2224
  let basicResultCompany;
2087
2225
  if (needsBasic) {
@@ -2099,11 +2237,43 @@ async function runCompanyUpdate(ctx, apiKey, source, idempotencyFlags, options =
2099
2237
  requestOpts,
2100
2238
  "/company/descriptions",
2101
2239
  payloads.descriptions,
2102
- { idempotencyKey: descriptionsKey }
2240
+ {
2241
+ idempotencyKey: descriptionsKey,
2242
+ // 只有輸入帶 video_links 時才附 If-Match——後端只在這種情況要求它(spec §3.2.2);
2243
+ // 沒帶 video_links 時絕不能傳這個 key(用條件式 spread,不是傳 `undefined`)。
2244
+ ...needsVideoLinks ? { ifMatch: payloads.videoLinksRevision } : {}
2245
+ }
2103
2246
  );
2104
2247
  const result = (0, import_core19.unwrapDataResponse)(body);
2105
2248
  printCompanyResult(result.company, ctx.format);
2106
2249
  } catch (err) {
2250
+ if (needsVideoLinks && err instanceof ServerClientHttpError && err.status === 409) {
2251
+ const { body: freshBody } = await enterpriseGet2(requestOpts, "/company");
2252
+ const freshCurrent = (0, import_core19.unwrapDataResponse)(freshBody);
2253
+ const freshVideoLinks = freshCurrent.video_links ?? [];
2254
+ const diff = summarizeItemsDiff(payloads.currentVideoLinks ?? [], freshVideoLinks);
2255
+ const partialBody = isDescriptionsPartialFailureBody(err.body) ? err.body : void 0;
2256
+ printWarn(`Server state for "video_links" changed since your read: ${diff}.`, ctx.color ?? false);
2257
+ const descriptionsError = {
2258
+ updated_sections: partialBody?.updated_sections ?? [],
2259
+ failed_section: partialBody?.failed_section ?? "video_links",
2260
+ video_links_diff: diff
2261
+ };
2262
+ if (ctx.format === "json") {
2263
+ printJson(
2264
+ needsBasic ? { basic_updated: true, basic_company: basicResultCompany, descriptions_error: descriptionsError } : { descriptions_error: descriptionsError }
2265
+ );
2266
+ } else {
2267
+ if (needsBasic) process.stdout.write("Basic company info was updated successfully.\n");
2268
+ process.stdout.write(
2269
+ `Updated sections before failure: ${descriptionsError.updated_sections.join(", ")}; failed section: ${descriptionsError.failed_section}
2270
+ `
2271
+ );
2272
+ }
2273
+ throw new InvalidArgumentError(
2274
+ `Refusing to auto-retry "video_links" update after a stale revision conflict (409). ${diff}. Re-run \`wport enterprise company view\` and retry \`company update --confirm\` against the current state.`
2275
+ );
2276
+ }
2107
2277
  if (!needsBasic) {
2108
2278
  throw err;
2109
2279
  }
@@ -2134,10 +2304,13 @@ async function runCompanyUpdate(ctx, apiKey, source, idempotencyFlags, options =
2134
2304
  }
2135
2305
  function registerEnterpriseCompanyUpdate(parent) {
2136
2306
  parent.command("update").description(
2137
- "Update your company basic info and/or descriptions from a JSON file (uniform_number is read-only and cannot be changed via this command)"
2307
+ "Update your company basic info and/or descriptions from a JSON file (uniform_number is read-only and cannot be changed via this command). is_representative_visible / is_establishment_date_visible / is_directors_visible default to 0 (hidden) when not set. video_links (in descriptions) uses optimistic locking (If-Match, handled automatically from your latest `company view`); removing any existing video requires --confirm."
2138
2308
  ).requiredOption("--file <path>", 'path to a partial JSON company body, or "-" for stdin').option("--idempotency-key <key>", "single-section update only: reuse across retries (default: a fresh UUID)").option("--basic-idempotency-key <key>", "two-section update: idempotency key for PATCH /company/basic").option(
2139
2309
  "--descriptions-idempotency-key <key>",
2140
2310
  "two-section update: idempotency key for PATCH /company/descriptions"
2311
+ ).option(
2312
+ "--confirm",
2313
+ "confirm deletion of existing video_links items not present in the input (required when video_links is provided and would remove any existing item)"
2141
2314
  ).action(async (flags, command) => {
2142
2315
  const ctx = resolveContext(command);
2143
2316
  const globals = command.optsWithGlobals();
@@ -2151,7 +2324,8 @@ function registerEnterpriseCompanyUpdate(parent) {
2151
2324
  basicIdempotencyKey: flags.basicIdempotencyKey,
2152
2325
  descriptionsIdempotencyKey: flags.descriptionsIdempotencyKey
2153
2326
  },
2154
- { timeoutMs: ctx.timeoutMs }
2327
+ { timeoutMs: ctx.timeoutMs },
2328
+ flags.confirm === true
2155
2329
  );
2156
2330
  });
2157
2331
  }
@@ -2247,16 +2421,122 @@ function registerEnterpriseCompanyLogo(parent) {
2247
2421
  });
2248
2422
  }
2249
2423
 
2424
+ // src/commands/enterprise/company/replace.ts
2425
+ var import_node_crypto7 = require("crypto");
2426
+ var import_core21 = require("@wport/core");
2427
+ var REPLACE_SECTIONS = ["pain-points", "awards", "milestones", "qa-items"];
2428
+ var SECTION_CONFIG = {
2429
+ "pain-points": {
2430
+ path: "/company/pain-points",
2431
+ dataKey: "pain_points",
2432
+ summarize: (i) => String(i.title ?? "")
2433
+ },
2434
+ awards: {
2435
+ path: "/company/awards",
2436
+ dataKey: "awards",
2437
+ summarize: (i) => String(i.name ?? "")
2438
+ },
2439
+ milestones: {
2440
+ path: "/company/milestones",
2441
+ dataKey: "milestones",
2442
+ summarize: (i) => String(i.title ?? "")
2443
+ },
2444
+ "qa-items": {
2445
+ path: "/company/qa-items",
2446
+ dataKey: "qa_items",
2447
+ summarize: (i) => String(i.question ?? "")
2448
+ }
2449
+ };
2450
+ function isReplaceSection(v) {
2451
+ return REPLACE_SECTIONS.includes(v);
2452
+ }
2453
+ function printReplaceResult(result, config, format, color) {
2454
+ if (format === "json") {
2455
+ printJson(result);
2456
+ return;
2457
+ }
2458
+ printTable(
2459
+ result.items ?? [],
2460
+ [
2461
+ { header: "enc_id", value: (r) => String(r.enc_id ?? "") },
2462
+ { header: "summary", value: (r) => config.summarize(r) }
2463
+ ],
2464
+ color
2465
+ );
2466
+ process.stdout.write(`revision: ${result.revision ?? ""}
2467
+ `);
2468
+ }
2469
+ async function runCompanyReplace(ctx, apiKey, section, source, confirm, idempotencyKey, options = {}) {
2470
+ if (!isReplaceSection(section)) {
2471
+ throw new InvalidArgumentError(`Invalid section "${section}". Allowed: ${REPLACE_SECTIONS.join(", ")}`);
2472
+ }
2473
+ const config = SECTION_CONFIG[section];
2474
+ const input = readJsonObject(source, options);
2475
+ if (!Array.isArray(input.items)) {
2476
+ throw new InvalidArgumentError('Input must be a JSON object of the form { "items": [...] }');
2477
+ }
2478
+ const items = assertValidSubmittedItems(input.items, "items");
2479
+ const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
2480
+ const { body: getBody } = await enterpriseGet2(requestOpts, "/company");
2481
+ const current = (0, import_core21.unwrapDataResponse)(getBody);
2482
+ const currentItems = current[config.dataKey] ?? [];
2483
+ const revision = current.block_revisions?.[config.dataKey];
2484
+ if (!revision) {
2485
+ throw new CliError(
2486
+ `Backend contract appears broken: GET /company response is missing block_revisions.${config.dataKey}`,
2487
+ ExitCode.ServerOrNetworkError
2488
+ );
2489
+ }
2490
+ const deletions = previewDeletions(currentItems, items, config.summarize);
2491
+ enforceDeletionConfirmation(section, deletions, confirm, ctx.color);
2492
+ try {
2493
+ const { body } = await enterprisePut2(requestOpts, config.path, { items }, { idempotencyKey, ifMatch: revision });
2494
+ const result = (0, import_core21.unwrapDataResponse)(body);
2495
+ printReplaceResult(result, config, ctx.format, ctx.color);
2496
+ } catch (err) {
2497
+ if (err instanceof import_core21.WportHttpError && err.status === 409) {
2498
+ const { body: freshBody } = await enterpriseGet2(requestOpts, "/company");
2499
+ const freshCurrent = (0, import_core21.unwrapDataResponse)(freshBody);
2500
+ const freshItems = freshCurrent[config.dataKey] ?? [];
2501
+ const diff = summarizeItemsDiff(currentItems, freshItems);
2502
+ printWarn(`Server state for "${section}" changed since your read: ${diff}.`, ctx.color);
2503
+ throw new InvalidArgumentError(
2504
+ `Refusing to auto-retry "${section}" replace after a stale revision conflict (409). ${diff}. Re-run \`wport enterprise company view\` and retry \`company replace --confirm\` against the current state.`
2505
+ );
2506
+ }
2507
+ throw err;
2508
+ }
2509
+ }
2510
+ function registerEnterpriseCompanyReplace(parent) {
2511
+ parent.command("replace <section>").description(
2512
+ `Replace an entire company profile block from a JSON file. <section> is one of: ${REPLACE_SECTIONS.join("|")}. This is a full replacement, not an add/update: any existing item not listed in the input is deleted.`
2513
+ ).requiredOption("--file <path>", 'path to a JSON body { "items": [...] }, or "-" for stdin').option("--confirm", "confirm deletion of existing items that are not present in the input").option("--idempotency-key <key>", "reuse across retries (default: a fresh UUID)").action(async (section, flags, command) => {
2514
+ const ctx = resolveContext(command);
2515
+ const globals = command.optsWithGlobals();
2516
+ const { key } = resolveApiKey(globals.apiKey);
2517
+ await runCompanyReplace(
2518
+ ctx,
2519
+ key,
2520
+ section,
2521
+ flags.file,
2522
+ flags.confirm === true,
2523
+ flags.idempotencyKey ?? (0, import_node_crypto7.randomUUID)(),
2524
+ { timeoutMs: ctx.timeoutMs }
2525
+ );
2526
+ });
2527
+ }
2528
+
2250
2529
  // src/commands/enterprise/company/index.ts
2251
2530
  function registerEnterpriseCompanyCommand(parent) {
2252
2531
  const company = parent.command("company").description("View and manage your company information");
2253
2532
  registerEnterpriseCompanyView(company);
2254
2533
  registerEnterpriseCompanyUpdate(company);
2255
2534
  registerEnterpriseCompanyLogo(company);
2535
+ registerEnterpriseCompanyReplace(company);
2256
2536
  }
2257
2537
 
2258
2538
  // src/commands/enterprise/talents/list.ts
2259
- var import_core21 = require("@wport/core");
2539
+ var import_core22 = require("@wport/core");
2260
2540
  var DEFAULT_PAGE_SIZE = 20;
2261
2541
  var MINIMAL_LIST_FIELDS2 = ["enc_resume_id", "candidate_name", "applied_job_title", "applied_at"];
2262
2542
  function formatDate4(value) {
@@ -2279,7 +2559,7 @@ async function runEnterpriseTalentsList(ctx, apiKey, flags) {
2279
2559
  pageSize: flags.pageSize ?? DEFAULT_PAGE_SIZE
2280
2560
  }
2281
2561
  );
2282
- const paged = (0, import_core21.asPaginatedBody)(body);
2562
+ const paged = (0, import_core22.asPaginatedBody)(body);
2283
2563
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS2 : flags.fields ? parseFieldsList(flags.fields) : void 0;
2284
2564
  if (projection || ctx.format === "json") {
2285
2565
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -2312,7 +2592,7 @@ function registerEnterpriseTalentsList(parent) {
2312
2592
  }
2313
2593
 
2314
2594
  // src/commands/enterprise/talents/view.ts
2315
- var import_core22 = require("@wport/core");
2595
+ var import_core23 = require("@wport/core");
2316
2596
  async function runEnterpriseTalentsView(ctx, apiKey, encResumeId, flags) {
2317
2597
  const trimmed = encResumeId.trim();
2318
2598
  if (!trimmed) {
@@ -2322,7 +2602,7 @@ async function runEnterpriseTalentsView(ctx, apiKey, encResumeId, flags) {
2322
2602
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2323
2603
  `/talents/${encodeURIComponent(trimmed)}`
2324
2604
  );
2325
- const resume = (0, import_core22.unwrapDataResponse)(body);
2605
+ const resume = (0, import_core23.unwrapDataResponse)(body);
2326
2606
  if (flags.fields) {
2327
2607
  printJson(pickPaths(resume, parseFieldsList(flags.fields)));
2328
2608
  return;
@@ -2339,8 +2619,8 @@ function registerEnterpriseTalentsView(parent) {
2339
2619
  }
2340
2620
 
2341
2621
  // src/commands/enterprise/talents/respond.ts
2342
- var import_node_crypto7 = require("crypto");
2343
- var import_core23 = require("@wport/core");
2622
+ var import_node_crypto8 = require("crypto");
2623
+ var import_core24 = require("@wport/core");
2344
2624
  function resolveRespondBody(flags, options = {}) {
2345
2625
  const hasBody = flags.body !== void 0;
2346
2626
  const hasBodyFile = flags.bodyFile !== void 0;
@@ -2372,7 +2652,7 @@ async function runEnterpriseTalentsRespond(ctx, apiKey, encResumeId, flags, idem
2372
2652
  payload,
2373
2653
  { idempotencyKey }
2374
2654
  );
2375
- const result = (0, import_core23.unwrapDataResponse)(respBody);
2655
+ const result = (0, import_core24.unwrapDataResponse)(respBody);
2376
2656
  if (ctx.format === "json") {
2377
2657
  printJson(result);
2378
2658
  return;
@@ -2385,7 +2665,7 @@ function registerEnterpriseTalentsRespond(parent) {
2385
2665
  const ctx = resolveContext(command);
2386
2666
  const globals = command.optsWithGlobals();
2387
2667
  const { key } = resolveApiKey(globals.apiKey);
2388
- await runEnterpriseTalentsRespond(ctx, key, encResumeId, flags, flags.idempotencyKey ?? (0, import_node_crypto7.randomUUID)());
2668
+ await runEnterpriseTalentsRespond(ctx, key, encResumeId, flags, flags.idempotencyKey ?? (0, import_node_crypto8.randomUUID)());
2389
2669
  });
2390
2670
  }
2391
2671
 
@@ -2400,8 +2680,8 @@ function registerEnterpriseTalentsCommand(parent) {
2400
2680
  }
2401
2681
 
2402
2682
  // src/commands/enterprise/campaigns/create.ts
2403
- var import_node_crypto8 = require("crypto");
2404
- var import_core24 = require("@wport/core");
2683
+ var import_node_crypto9 = require("crypto");
2684
+ var import_core25 = require("@wport/core");
2405
2685
  async function runCampaignCreate(ctx, apiKey, source, idempotencyKey) {
2406
2686
  const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
2407
2687
  const { body } = await enterprisePost2(
@@ -2410,7 +2690,7 @@ async function runCampaignCreate(ctx, apiKey, source, idempotencyKey) {
2410
2690
  campaignBody,
2411
2691
  { idempotencyKey }
2412
2692
  );
2413
- const created = (0, import_core24.unwrapDataResponse)(body);
2693
+ const created = (0, import_core25.unwrapDataResponse)(body);
2414
2694
  if (ctx.format === "json") {
2415
2695
  printJson(created);
2416
2696
  return;
@@ -2423,12 +2703,12 @@ function registerEnterpriseCampaignsCreate(parent) {
2423
2703
  const ctx = resolveContext(command);
2424
2704
  const globals = command.optsWithGlobals();
2425
2705
  const { key } = resolveApiKey(globals.apiKey);
2426
- await runCampaignCreate(ctx, key, flags.file, flags.idempotencyKey ?? (0, import_node_crypto8.randomUUID)());
2706
+ await runCampaignCreate(ctx, key, flags.file, flags.idempotencyKey ?? (0, import_node_crypto9.randomUUID)());
2427
2707
  });
2428
2708
  }
2429
2709
 
2430
2710
  // src/commands/enterprise/campaigns/list.ts
2431
- var import_core25 = require("@wport/core");
2711
+ var import_core26 = require("@wport/core");
2432
2712
  var STATUS_MAP2 = { open: 1, closed: 0 };
2433
2713
  var MINIMAL_LIST_FIELDS3 = ["enc_id", "name", "status", "job_count"];
2434
2714
  function mapStatusFlag2(raw) {
@@ -2458,7 +2738,7 @@ async function runEnterpriseCampaignsList(ctx, apiKey, flags) {
2458
2738
  status: mapStatusFlag2(flags.status)
2459
2739
  }
2460
2740
  );
2461
- const paged = (0, import_core25.asPaginatedBody)(body);
2741
+ const paged = (0, import_core26.asPaginatedBody)(body);
2462
2742
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS3 : flags.fields ? parseFieldsList(flags.fields) : void 0;
2463
2743
  if (projection || ctx.format === "json") {
2464
2744
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -2490,8 +2770,8 @@ function registerEnterpriseCampaignsList(parent) {
2490
2770
  }
2491
2771
 
2492
2772
  // src/commands/enterprise/campaigns/lifecycle.ts
2493
- var import_node_crypto9 = require("crypto");
2494
- var import_core26 = require("@wport/core");
2773
+ var import_node_crypto10 = require("crypto");
2774
+ var import_core27 = require("@wport/core");
2495
2775
  async function runCampaignTransition(ctx, apiKey, encId, action, idempotencyKey) {
2496
2776
  const trimmed = requireEncId(encId);
2497
2777
  const { body } = await enterprisePatch2(
@@ -2500,7 +2780,7 @@ async function runCampaignTransition(ctx, apiKey, encId, action, idempotencyKey)
2500
2780
  {},
2501
2781
  { idempotencyKey }
2502
2782
  );
2503
- const result = (0, import_core26.unwrapDataResponse)(body);
2783
+ const result = (0, import_core27.unwrapDataResponse)(body);
2504
2784
  if (ctx.format === "json") {
2505
2785
  printJson(result);
2506
2786
  return;
@@ -2513,20 +2793,20 @@ function registerEnterpriseCampaignsPublish(parent) {
2513
2793
  parent.command("publish <enc_id>").description("Publish (activate) a recruitment campaign").option("--idempotency-key <key>", "reuse across retries (default: a fresh UUID)").action(async (encId, flags, command) => {
2514
2794
  const ctx = resolveContext(command);
2515
2795
  const { key } = resolveApiKey(command.optsWithGlobals().apiKey);
2516
- await runCampaignTransition(ctx, key, encId, "publish", flags.idempotencyKey ?? (0, import_node_crypto9.randomUUID)());
2796
+ await runCampaignTransition(ctx, key, encId, "publish", flags.idempotencyKey ?? (0, import_node_crypto10.randomUUID)());
2517
2797
  });
2518
2798
  }
2519
2799
  function registerEnterpriseCampaignsUnpublish(parent) {
2520
2800
  parent.command("unpublish <enc_id>").description("Unpublish (deactivate) a recruitment campaign").option("--idempotency-key <key>", "reuse across retries (default: a fresh UUID)").action(async (encId, flags, command) => {
2521
2801
  const ctx = resolveContext(command);
2522
2802
  const { key } = resolveApiKey(command.optsWithGlobals().apiKey);
2523
- await runCampaignTransition(ctx, key, encId, "unpublish", flags.idempotencyKey ?? (0, import_node_crypto9.randomUUID)());
2803
+ await runCampaignTransition(ctx, key, encId, "unpublish", flags.idempotencyKey ?? (0, import_node_crypto10.randomUUID)());
2524
2804
  });
2525
2805
  }
2526
2806
 
2527
2807
  // src/commands/enterprise/campaigns/update.ts
2528
- var import_node_crypto10 = require("crypto");
2529
- var import_core27 = require("@wport/core");
2808
+ var import_node_crypto11 = require("crypto");
2809
+ var import_core28 = require("@wport/core");
2530
2810
  async function runCampaignUpdate(ctx, apiKey, encId, source, idempotencyKey) {
2531
2811
  const trimmed = requireEncId(encId);
2532
2812
  const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
@@ -2536,7 +2816,7 @@ async function runCampaignUpdate(ctx, apiKey, encId, source, idempotencyKey) {
2536
2816
  campaignBody,
2537
2817
  { idempotencyKey }
2538
2818
  );
2539
- const updated = (0, import_core27.unwrapDataResponse)(body);
2819
+ const updated = (0, import_core28.unwrapDataResponse)(body);
2540
2820
  if (ctx.format === "json") {
2541
2821
  printJson(updated);
2542
2822
  return;
@@ -2549,12 +2829,12 @@ function registerEnterpriseCampaignsUpdate(parent) {
2549
2829
  const ctx = resolveContext(command);
2550
2830
  const globals = command.optsWithGlobals();
2551
2831
  const { key } = resolveApiKey(globals.apiKey);
2552
- await runCampaignUpdate(ctx, key, encId, flags.file, flags.idempotencyKey ?? (0, import_node_crypto10.randomUUID)());
2832
+ await runCampaignUpdate(ctx, key, encId, flags.file, flags.idempotencyKey ?? (0, import_node_crypto11.randomUUID)());
2553
2833
  });
2554
2834
  }
2555
2835
 
2556
2836
  // src/commands/enterprise/campaigns/view.ts
2557
- var import_core28 = require("@wport/core");
2837
+ var import_core29 = require("@wport/core");
2558
2838
  async function runEnterpriseCampaignsView(ctx, apiKey, encId, flags) {
2559
2839
  const trimmed = encId.trim();
2560
2840
  if (!trimmed) {
@@ -2564,7 +2844,7 @@ async function runEnterpriseCampaignsView(ctx, apiKey, encId, flags) {
2564
2844
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2565
2845
  `/campaigns/${encodeURIComponent(trimmed)}`
2566
2846
  );
2567
- const campaign = (0, import_core28.unwrapDataResponse)(body);
2847
+ const campaign = (0, import_core29.unwrapDataResponse)(body);
2568
2848
  if (flags.fields) {
2569
2849
  printJson(pickPaths(campaign, parseFieldsList(flags.fields)));
2570
2850
  return;
@@ -2709,10 +2989,10 @@ function registerLoginCommand(program2) {
2709
2989
  }
2710
2990
 
2711
2991
  // src/commands/auth/whoami.ts
2712
- var import_core30 = require("@wport/core");
2992
+ var import_core31 = require("@wport/core");
2713
2993
 
2714
2994
  // src/lib/personal-client.ts
2715
- var import_core29 = require("@wport/core");
2995
+ var import_core30 = require("@wport/core");
2716
2996
  var EXPIRY_SKEW_MS = 3e4;
2717
2997
  var NOT_LOGGED_IN_MESSAGE = "Not logged in. Run `wport login`.";
2718
2998
  async function ensureFreshCredentials(opts) {
@@ -2741,7 +3021,7 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
2741
3021
  const headers = {
2742
3022
  Authorization: `Bearer ${accessToken}`,
2743
3023
  "Accept-Language": opts.locale,
2744
- "User-Agent": (0, import_core29.buildUserAgent)("wport-cli", "0.9.3"),
3024
+ "User-Agent": (0, import_core30.buildUserAgent)("wport-cli", "0.10.1"),
2745
3025
  "X-Source": CLI_SOURCE,
2746
3026
  Accept: "application/json"
2747
3027
  };
@@ -2752,7 +3032,7 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
2752
3032
  headers,
2753
3033
  body: body !== void 0 ? JSON.stringify(body) : void 0
2754
3034
  });
2755
- const res = await (0, import_core29.fetchWithTimeout)(request, opts.timeoutMs);
3035
+ const res = await (0, import_core30.fetchWithTimeout)(request, opts.timeoutMs);
2756
3036
  const respBody = await res.json().catch(() => null);
2757
3037
  return { status: res.status, body: respBody, headers: res.headers };
2758
3038
  }
@@ -2784,13 +3064,13 @@ function personalDelete(opts, path, extra) {
2784
3064
  }
2785
3065
  function throwPersonalHttpError(status, body) {
2786
3066
  if (status === 401) {
2787
- const base = (0, import_core29.extractErrorMessage)(body) ?? `HTTP ${status}`;
3067
+ const base = (0, import_core30.extractErrorMessage)(body) ?? `HTTP ${status}`;
2788
3068
  throw new CliError(
2789
3069
  `${base} \u2014 Your session may have expired or the request was rejected. Run \`wport login\` to sign in again.`,
2790
3070
  ExitCode.ServerClientError
2791
3071
  );
2792
3072
  }
2793
- (0, import_core29.throwForHttpStatus)(status, body);
3073
+ (0, import_core30.throwForHttpStatus)(status, body);
2794
3074
  }
2795
3075
  function warnIfRateLimitLow2(headers) {
2796
3076
  const remaining = Number(headers.get("x-ratelimit-remaining"));
@@ -2801,7 +3081,7 @@ function warnIfRateLimitLow2(headers) {
2801
3081
  }
2802
3082
 
2803
3083
  // src/commands/auth/whoami.ts
2804
- var import_core31 = require("@wport/core");
3084
+ var import_core32 = require("@wport/core");
2805
3085
  var PLACEHOLDER = "\u2014";
2806
3086
  function display(value) {
2807
3087
  const clean = sanitizeForTerminal(value ?? "");
@@ -2809,8 +3089,8 @@ function display(value) {
2809
3089
  }
2810
3090
  async function performWhoami(ctx) {
2811
3091
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
2812
- const { body } = await personalGet(opts, import_core31.OAUTH_SESSIONS_BASE);
2813
- const sessions = (0, import_core30.unwrapDataArray)(body);
3092
+ const { body } = await personalGet(opts, import_core32.OAUTH_SESSIONS_BASE);
3093
+ const sessions = (0, import_core31.unwrapDataArray)(body);
2814
3094
  const current = sessions.find((s) => s.is_current);
2815
3095
  const localLoginAt = loadPersonalCredentials()?.session_created_at ?? null;
2816
3096
  if (ctx.format === "json") {
@@ -2862,15 +3142,15 @@ function registerLogoutCommand(program2) {
2862
3142
  }
2863
3143
 
2864
3144
  // src/commands/sessions/list.ts
2865
- var import_core32 = require("@wport/core");
2866
3145
  var import_core33 = require("@wport/core");
3146
+ var import_core34 = require("@wport/core");
2867
3147
  function formatDate5(value) {
2868
3148
  return value ? String(value).slice(0, 10) : "";
2869
3149
  }
2870
3150
  async function runSessionsList(ctx) {
2871
3151
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
2872
- const { body } = await personalGet(opts, import_core33.OAUTH_SESSIONS_BASE);
2873
- const sessions = (0, import_core32.unwrapDataArray)(body);
3152
+ const { body } = await personalGet(opts, import_core34.OAUTH_SESSIONS_BASE);
3153
+ const sessions = (0, import_core33.unwrapDataArray)(body);
2874
3154
  if (ctx.format === "json") {
2875
3155
  printJson(sessions);
2876
3156
  return;
@@ -2894,8 +3174,8 @@ function registerSessionsList(parent) {
2894
3174
  }
2895
3175
 
2896
3176
  // src/commands/sessions/revoke.ts
2897
- var import_core34 = require("@wport/core");
2898
3177
  var import_core35 = require("@wport/core");
3178
+ var import_core36 = require("@wport/core");
2899
3179
  async function runSessionsRevoke(ctx, encId, allOthers) {
2900
3180
  const hasEncId = encId !== void 0;
2901
3181
  if (hasEncId === allOthers) {
@@ -2903,8 +3183,8 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
2903
3183
  }
2904
3184
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
2905
3185
  if (allOthers) {
2906
- const { body } = await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/others`);
2907
- const result = (0, import_core35.unwrapDataResponse)(body);
3186
+ const { body } = await personalDelete(opts, `${import_core35.OAUTH_SESSIONS_BASE}/others`);
3187
+ const result = (0, import_core36.unwrapDataResponse)(body);
2908
3188
  if (ctx.format === "json") {
2909
3189
  printJson(result);
2910
3190
  return;
@@ -2915,7 +3195,7 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
2915
3195
  }
2916
3196
  const trimmed = encId.trim();
2917
3197
  if (!trimmed) throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
2918
- await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
3198
+ await personalDelete(opts, `${import_core35.OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
2919
3199
  if (ctx.format === "json") {
2920
3200
  printJson({ enc_id: trimmed, revoked: true });
2921
3201
  return;
@@ -2938,18 +3218,18 @@ function registerSessionsCommand(program2) {
2938
3218
  }
2939
3219
 
2940
3220
  // src/commands/personal/resumes/schema.ts
2941
- var import_core36 = require("@wport/core");
3221
+ var import_core37 = require("@wport/core");
2942
3222
  function registerPersonalResumesSchema(parent) {
2943
3223
  parent.command("schema").description("Print the resume aggregate JSON Schema (offline, no login required)").option("--section <name>", "print only the given section (e.g. education, work_experience)").action((opts) => {
2944
- printJson((0, import_core36.buildAggregateJsonSchema)(opts.section));
3224
+ printJson((0, import_core37.buildAggregateJsonSchema)(opts.section));
2945
3225
  });
2946
3226
  }
2947
3227
 
2948
3228
  // src/commands/personal/resumes/validate.ts
2949
- var import_core37 = require("@wport/core");
3229
+ var import_core38 = require("@wport/core");
2950
3230
  function runResumesValidate(ctx, filePath) {
2951
3231
  const input = readJsonInput(filePath, { timeoutMs: ctx.timeoutMs });
2952
- const issues = (0, import_core37.validateAggregate)(input);
3232
+ const issues = (0, import_core38.validateAggregate)(input);
2953
3233
  if (issues.length === 0) {
2954
3234
  if (ctx.format === "json") {
2955
3235
  printJson({ valid: true });
@@ -2977,9 +3257,9 @@ function registerPersonalResumesValidate(parent) {
2977
3257
 
2978
3258
  // src/commands/personal/resumes/template.ts
2979
3259
  var import_node_fs8 = require("fs");
2980
- var import_core38 = require("@wport/core");
3260
+ var import_core39 = require("@wport/core");
2981
3261
  function runResumesTemplate(outPath) {
2982
- const template = (0, import_core38.buildTemplate)();
3262
+ const template = (0, import_core39.buildTemplate)();
2983
3263
  if (outPath === void 0) {
2984
3264
  printJson(template);
2985
3265
  return;
@@ -3003,8 +3283,8 @@ function registerPersonalResumesTemplate(parent) {
3003
3283
  }
3004
3284
 
3005
3285
  // src/commands/personal/resumes/list.ts
3006
- var import_core39 = require("@wport/core");
3007
3286
  var import_core40 = require("@wport/core");
3287
+ var import_core41 = require("@wport/core");
3008
3288
  var MINIMAL_LIST_FIELDS4 = ["enc_id", "name", "updated_at", "is_complete", "is_published"];
3009
3289
  function formatDate6(value) {
3010
3290
  return value ? String(value).slice(0, 10) : "";
@@ -3014,8 +3294,8 @@ function formatQuotaLine(quota) {
3014
3294
  return `${quota.used}/${quota.max_resumes} used, ${status}.`;
3015
3295
  }
3016
3296
  async function fetchResumeList(opts) {
3017
- const { body } = await personalGet(opts, import_core40.PERSONAL_RESUMES_BASE);
3018
- return (0, import_core39.unwrapDataResponse)(body);
3297
+ const { body } = await personalGet(opts, import_core41.PERSONAL_RESUMES_BASE);
3298
+ return (0, import_core40.unwrapDataResponse)(body);
3019
3299
  }
3020
3300
  async function runResumesList(ctx, flags) {
3021
3301
  if (flags.fields && flags.minimal) {
@@ -3048,20 +3328,20 @@ function registerPersonalResumesList(parent) {
3048
3328
  }
3049
3329
 
3050
3330
  // src/commands/personal/resumes/view.ts
3051
- var import_core41 = require("@wport/core");
3052
3331
  var import_core42 = require("@wport/core");
3053
3332
  var import_core43 = require("@wport/core");
3333
+ var import_core44 = require("@wport/core");
3054
3334
  async function fetchResumeAggregate(opts, encId) {
3055
3335
  const trimmed = encId.trim();
3056
3336
  if (!trimmed) {
3057
3337
  throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
3058
3338
  }
3059
- const { body } = await personalGet(opts, `${import_core42.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
3060
- return (0, import_core41.unwrapDataResponse)(body);
3339
+ const { body } = await personalGet(opts, `${import_core43.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
3340
+ return (0, import_core42.unwrapDataResponse)(body);
3061
3341
  }
3062
3342
  function summarizeSections(resume) {
3063
3343
  const rows = [{ section: "name", summary: resume.name }];
3064
- for (const def of import_core43.SECTIONS) {
3344
+ for (const def of import_core44.SECTIONS) {
3065
3345
  rows.push({ section: def.key, summary: summarizeSection(def, resume) });
3066
3346
  }
3067
3347
  return rows;
@@ -3139,15 +3419,15 @@ function registerPersonalResumesExport(parent) {
3139
3419
  }
3140
3420
 
3141
3421
  // src/commands/personal/resumes/create.ts
3142
- var import_core47 = require("@wport/core");
3422
+ var import_core48 = require("@wport/core");
3143
3423
 
3144
3424
  // src/lib/resume-orchestrator.ts
3145
- var import_node_crypto11 = require("crypto");
3146
- var import_core44 = require("@wport/core");
3425
+ var import_node_crypto12 = require("crypto");
3147
3426
  var import_core45 = require("@wport/core");
3148
3427
  var import_core46 = require("@wport/core");
3428
+ var import_core47 = require("@wport/core");
3149
3429
  function freshIdempotencyKey() {
3150
- return { idempotencyKey: (0, import_node_crypto11.randomUUID)() };
3430
+ return { idempotencyKey: (0, import_node_crypto12.randomUUID)() };
3151
3431
  }
3152
3432
  function describeError(err) {
3153
3433
  return err instanceof Error ? err.message : String(err);
@@ -3160,8 +3440,8 @@ function extractErrorCode(body) {
3160
3440
  }
3161
3441
  async function createShell(opts) {
3162
3442
  try {
3163
- const { body } = await personalPost(opts, import_core45.PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());
3164
- return (0, import_core44.unwrapDataResponse)(body).enc_id;
3443
+ const { body } = await personalPost(opts, import_core46.PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());
3444
+ return (0, import_core45.unwrapDataResponse)(body).enc_id;
3165
3445
  } catch (err) {
3166
3446
  if (err instanceof ServerClientHttpError) {
3167
3447
  const code = extractErrorCode(err.body);
@@ -3182,7 +3462,7 @@ async function createShell(opts) {
3182
3462
  }
3183
3463
  }
3184
3464
  async function renameResume(opts, encId, name) {
3185
- await personalPut(opts, `${import_core45.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name }, freshIdempotencyKey());
3465
+ await personalPut(opts, `${import_core46.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name }, freshIdempotencyKey());
3186
3466
  }
3187
3467
  function toWorkExperienceWriteItem(item) {
3188
3468
  const { is_current, ...rest } = item;
@@ -3194,8 +3474,8 @@ function splitItemEncId(item) {
3194
3474
  return [valid, rest];
3195
3475
  }
3196
3476
  async function writeSection(opts, encId, key, value, mode) {
3197
- const plan = import_core45.SECTION_WRITE_PLAN[key];
3198
- const base = `${import_core45.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;
3477
+ const plan = import_core46.SECTION_WRITE_PLAN[key];
3478
+ const base = `${import_core46.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;
3199
3479
  switch (plan.kind) {
3200
3480
  case "per-item-post": {
3201
3481
  for (const item of value) {
@@ -3262,7 +3542,7 @@ async function createAggregate(opts, aggregate) {
3262
3542
  if (aggregate.name !== void 0) {
3263
3543
  reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
3264
3544
  }
3265
- for (const section of import_core46.SECTIONS) {
3545
+ for (const section of import_core47.SECTIONS) {
3266
3546
  const value = aggregate[section.key];
3267
3547
  if (value === void 0) continue;
3268
3548
  reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "create")));
@@ -3272,9 +3552,9 @@ async function createAggregate(opts, aggregate) {
3272
3552
  async function updateAggregate(opts, encId, aggregate, onlySection) {
3273
3553
  const reports = [];
3274
3554
  if (onlySection !== void 0) {
3275
- const section = (0, import_core46.getSection)(onlySection);
3555
+ const section = (0, import_core47.getSection)(onlySection);
3276
3556
  if (!section) {
3277
- const allowed = import_core46.SECTIONS.map((s) => s.key).join(", ");
3557
+ const allowed = import_core47.SECTIONS.map((s) => s.key).join(", ");
3278
3558
  throw new CliError(`Unknown section "${onlySection}". Allowed: ${allowed}`, ExitCode.InvalidArgument);
3279
3559
  }
3280
3560
  const value = aggregate[onlySection];
@@ -3287,7 +3567,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
3287
3567
  if (aggregate.name !== void 0) {
3288
3568
  reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
3289
3569
  }
3290
- for (const section of import_core46.SECTIONS) {
3570
+ for (const section of import_core47.SECTIONS) {
3291
3571
  const value = aggregate[section.key];
3292
3572
  if (value === void 0) continue;
3293
3573
  reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "update")));
@@ -3298,7 +3578,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
3298
3578
  // src/commands/personal/resumes/create.ts
3299
3579
  async function runResumesCreate(ctx, filePath) {
3300
3580
  const input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });
3301
- const issues = (0, import_core47.validateAggregate)(input);
3581
+ const issues = (0, import_core48.validateAggregate)(input);
3302
3582
  if (issues.length > 0) {
3303
3583
  if (ctx.format === "json") {
3304
3584
  printJson({ valid: false, issues });
@@ -3390,9 +3670,9 @@ function registerPersonalResumesUpdate(parent) {
3390
3670
  }
3391
3671
 
3392
3672
  // src/commands/personal/resumes/copy.ts
3393
- var import_node_crypto12 = require("crypto");
3394
- var import_core48 = require("@wport/core");
3673
+ var import_node_crypto13 = require("crypto");
3395
3674
  var import_core49 = require("@wport/core");
3675
+ var import_core50 = require("@wport/core");
3396
3676
  function requireEncId3(encId) {
3397
3677
  const trimmed = encId.trim();
3398
3678
  if (!trimmed) {
@@ -3411,12 +3691,12 @@ function printCopyResult(ctx, sourceEncId, newEncId) {
3411
3691
  async function runResumesCopy(ctx, encId, name) {
3412
3692
  const trimmed = requireEncId3(encId);
3413
3693
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
3414
- const { body } = await personalPost(opts, `${import_core49.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/duplicate`, {}, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
3415
- const { enc_id: newEncId } = (0, import_core48.unwrapDataResponse)(body);
3694
+ const { body } = await personalPost(opts, `${import_core50.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/duplicate`, {}, { idempotencyKey: (0, import_node_crypto13.randomUUID)() });
3695
+ const { enc_id: newEncId } = (0, import_core49.unwrapDataResponse)(body);
3416
3696
  printCopyResult(ctx, trimmed, newEncId);
3417
3697
  if (name === void 0) return;
3418
3698
  try {
3419
- await personalPut(opts, `${import_core49.PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name }, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
3699
+ await personalPut(opts, `${import_core50.PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name }, { idempotencyKey: (0, import_node_crypto13.randomUUID)() });
3420
3700
  } catch (err) {
3421
3701
  const reason = err instanceof Error ? err.message : String(err);
3422
3702
  throw new CliError(
@@ -3432,9 +3712,9 @@ function registerPersonalResumesCopy(parent) {
3432
3712
  }
3433
3713
 
3434
3714
  // src/commands/personal/resumes/publish.ts
3435
- var import_node_crypto13 = require("crypto");
3436
- var import_core50 = require("@wport/core");
3715
+ var import_node_crypto14 = require("crypto");
3437
3716
  var import_core51 = require("@wport/core");
3717
+ var import_core52 = require("@wport/core");
3438
3718
  function requireEncId4(encId) {
3439
3719
  const trimmed = encId.trim();
3440
3720
  if (!trimmed) {
@@ -3448,11 +3728,11 @@ async function runResumesPublishTransition(ctx, encId, action) {
3448
3728
  const targetStatus = action === "publish";
3449
3729
  const { body } = await personalPatch(
3450
3730
  opts,
3451
- `${import_core51.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
3731
+ `${import_core52.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
3452
3732
  { target_status: targetStatus },
3453
- { idempotencyKey: (0, import_node_crypto13.randomUUID)() }
3733
+ { idempotencyKey: (0, import_node_crypto14.randomUUID)() }
3454
3734
  );
3455
- const result = (0, import_core50.unwrapDataResponse)(body);
3735
+ const result = (0, import_core51.unwrapDataResponse)(body);
3456
3736
  if (ctx.format === "json") {
3457
3737
  printJson({ enc_id: trimmed, is_published: result.is_published });
3458
3738
  return;
@@ -3473,8 +3753,8 @@ function registerPersonalResumesUnpublish(parent) {
3473
3753
  }
3474
3754
 
3475
3755
  // src/commands/personal/resumes/delete.ts
3476
- var import_node_crypto14 = require("crypto");
3477
- var import_core52 = require("@wport/core");
3756
+ var import_node_crypto15 = require("crypto");
3757
+ var import_core53 = require("@wport/core");
3478
3758
  function requireEncId5(encId) {
3479
3759
  const trimmed = encId.trim();
3480
3760
  if (!trimmed) {
@@ -3488,7 +3768,7 @@ async function runResumesDelete(ctx, encId, confirm) {
3488
3768
  }
3489
3769
  const trimmed = requireEncId5(encId);
3490
3770
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
3491
- await personalDelete(opts, `${import_core52.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`, { idempotencyKey: (0, import_node_crypto14.randomUUID)() });
3771
+ await personalDelete(opts, `${import_core53.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
3492
3772
  if (ctx.format === "json") {
3493
3773
  printJson({ enc_id: trimmed, deleted: true });
3494
3774
  return;
@@ -3520,9 +3800,9 @@ function registerPersonalResumesCommand(parent) {
3520
3800
  }
3521
3801
 
3522
3802
  // src/commands/personal/apply/index.ts
3523
- var import_node_crypto15 = require("crypto");
3803
+ var import_node_crypto16 = require("crypto");
3524
3804
  var import_node_fs11 = require("fs");
3525
- var import_core53 = require("@wport/core");
3805
+ var import_core54 = require("@wport/core");
3526
3806
 
3527
3807
  // src/commands/personal/apply/message-input.ts
3528
3808
  var import_node_fs10 = require("fs");
@@ -3604,7 +3884,7 @@ async function runPersonalApply(ctx, args) {
3604
3884
  const body = { enc_job_id: args.encJobId, enc_resume_id: args.encResumeId, application_message: args.message };
3605
3885
  let result;
3606
3886
  try {
3607
- result = await personalPost(opts, import_core53.PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
3887
+ result = await personalPost(opts, import_core54.PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: (0, import_node_crypto16.randomUUID)() });
3608
3888
  } catch (err) {
3609
3889
  rethrowApplyError(err);
3610
3890
  }
@@ -3655,7 +3935,7 @@ async function runPersonalApplyBatch(ctx, args) {
3655
3935
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
3656
3936
  let result;
3657
3937
  try {
3658
- result = await personalPost(opts, `${import_core53.PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
3938
+ result = await personalPost(opts, `${import_core54.PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: (0, import_node_crypto16.randomUUID)() });
3659
3939
  } catch (err) {
3660
3940
  rethrowApplyError(err);
3661
3941
  }
@@ -3683,7 +3963,7 @@ function registerPersonalCommand(program2) {
3683
3963
 
3684
3964
  // src/index.ts
3685
3965
  var program = new import_commander.Command();
3686
- program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.9.3", "-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));
3966
+ program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.10.1", "-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));
3687
3967
  registerJobsCommand(program);
3688
3968
  registerConfigCommand(program);
3689
3969
  registerDoctorCommand(program);