@wport/cli 0.9.1-dev.0 → 0.9.2

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
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  var import_commander = require("commander");
28
28
 
29
29
  // src/lib/errors.ts
30
+ var import_core = require("@wport/core");
30
31
  var ExitCode = {
31
32
  Success: 0,
32
33
  InvalidArgument: 2,
@@ -48,27 +49,19 @@ var InvalidArgumentError = class extends CliError {
48
49
  this.name = "InvalidArgumentError";
49
50
  }
50
51
  };
51
- var ServerClientHttpError = class extends CliError {
52
- status;
53
- body;
54
- constructor(message, status, body) {
55
- super(message, ExitCode.ServerClientError);
56
- this.name = "ServerClientHttpError";
57
- this.status = status;
58
- this.body = body;
59
- }
60
- };
61
- var NetworkError = class extends CliError {
62
- cause;
63
- constructor(message, cause) {
64
- super(message, ExitCode.ServerOrNetworkError);
65
- this.name = "NetworkError";
66
- this.cause = cause;
67
- }
68
- };
69
52
  function isCliError(err) {
70
53
  return err instanceof CliError;
71
54
  }
55
+ function exitCodeForError(err) {
56
+ if (err instanceof import_core.WportInvalidArgumentError) return ExitCode.InvalidArgument;
57
+ if (err instanceof import_core.WportHttpError) {
58
+ return err.status >= 400 && err.status < 500 ? ExitCode.ServerClientError : ExitCode.ServerOrNetworkError;
59
+ }
60
+ if (err instanceof import_core.WportNetworkError) return ExitCode.ServerOrNetworkError;
61
+ return ExitCode.ServerOrNetworkError;
62
+ }
63
+ var ServerClientHttpError = import_core.WportHttpError;
64
+ var NetworkError = import_core.WportNetworkError;
72
65
 
73
66
  // src/lib/output.ts
74
67
  var import_cli_table3 = __toESM(require("cli-table3"));
@@ -146,96 +139,7 @@ function dim(text, color) {
146
139
 
147
140
  // src/commands/jobs/search.ts
148
141
  var import_node_fs2 = require("fs");
149
-
150
- // src/lib/api-client.ts
151
- var import_openapi_fetch = __toESM(require("openapi-fetch"));
152
- function createApiClient(opts) {
153
- return (0, import_openapi_fetch.default)({
154
- baseUrl: opts.baseUrl,
155
- headers: {
156
- "Accept-Language": opts.locale,
157
- "User-Agent": buildUserAgent(),
158
- Accept: "application/json"
159
- },
160
- fetch: (request) => fetchWithTimeout(request, opts.timeoutMs)
161
- });
162
- }
163
- function fetchWithTimeout(request, timeoutMs) {
164
- const timedRequest = new Request(request, { signal: AbortSignal.timeout(timeoutMs) });
165
- return fetch(timedRequest).catch((err) => {
166
- if (isTimeoutAbort(err)) {
167
- throw new NetworkError(`Request timed out after ${timeoutMs}ms`, err);
168
- }
169
- const code = err?.cause?.code;
170
- const detail = code ? code : err?.message ?? String(err);
171
- throw new NetworkError(`Cannot reach upstream: ${detail}`, err);
172
- });
173
- }
174
- function isTimeoutAbort(err) {
175
- if (err && typeof err === "object" && "name" in err) {
176
- const name = err.name;
177
- return name === "TimeoutError" || name === "AbortError";
178
- }
179
- return false;
180
- }
181
- function buildUserAgent() {
182
- return `wport-cli/${"0.9.1-dev.0"} (node ${process.version}; ${process.platform})`;
183
- }
184
- function unwrapDataResponse(body) {
185
- if (body && typeof body === "object" && "success" in body && "data" in body) {
186
- return body.data;
187
- }
188
- throw new CliError("Unexpected response shape: missing { success, data } wrapper", ExitCode.ServerOrNetworkError);
189
- }
190
- function unwrapDataArray(body) {
191
- const data = unwrapDataResponse(body);
192
- if (!Array.isArray(data)) {
193
- throw new CliError("Unexpected response shape: `data` is not an array", ExitCode.ServerOrNetworkError);
194
- }
195
- return data;
196
- }
197
- function asPaginatedBody(body) {
198
- if (!body || typeof body !== "object") {
199
- throw new CliError("Unexpected response shape: not an object", ExitCode.ServerOrNetworkError);
200
- }
201
- const b = body;
202
- if (!Array.isArray(b.data)) {
203
- throw new CliError("Unexpected response shape: missing `data` array", ExitCode.ServerOrNetworkError);
204
- }
205
- for (const key of ["currentPage", "totalPages", "pageSize", "totalCount"]) {
206
- if (typeof b[key] !== "number") {
207
- throw new CliError(`Unexpected response shape: missing or non-numeric "${key}"`, ExitCode.ServerOrNetworkError);
208
- }
209
- }
210
- return body;
211
- }
212
- function throwForHttpStatus(status, body) {
213
- const message = extractErrorMessage(body) ?? `HTTP ${status}`;
214
- if (status >= 400 && status < 500) {
215
- throw new ServerClientHttpError(message, status, body);
216
- }
217
- throw new CliError(message, ExitCode.ServerOrNetworkError);
218
- }
219
- function extractErrorMessage(body) {
220
- if (typeof body === "string") return body;
221
- if (body && typeof body === "object") {
222
- const obj = body;
223
- if (Array.isArray(obj.message)) {
224
- const parts = obj.message.filter((m) => typeof m === "string");
225
- if (parts.length > 0) return parts.join("; ");
226
- }
227
- if (typeof obj.message === "string") return obj.message;
228
- if (obj.message && typeof obj.message === "object") {
229
- try {
230
- return JSON.stringify(obj.message);
231
- } catch {
232
- }
233
- }
234
- if (typeof obj.path === "string") return obj.path;
235
- if (typeof obj.error === "string") return obj.error;
236
- }
237
- return null;
238
- }
142
+ var import_core2 = require("@wport/core");
239
143
 
240
144
  // src/lib/config-store.ts
241
145
  var import_node_fs = require("fs");
@@ -372,7 +276,7 @@ var CHANNEL_BASE_URL = {
372
276
  dev: "https://developers.wport.me/v2"
373
277
  };
374
278
  function currentChannel() {
375
- return true ? "dev" : "prod";
279
+ return true ? "prod" : "prod";
376
280
  }
377
281
  function channelBaseUrl(channel) {
378
282
  return CHANNEL_BASE_URL[channel] ?? CHANNEL_BASE_URL.prod;
@@ -387,6 +291,7 @@ function channelBanner() {
387
291
  // src/lib/global-opts.ts
388
292
  var DEFAULT_BASE_URL = channelBaseUrl(currentChannel());
389
293
  var API_BASE_ENV_VAR = "WPORT_API_BASE";
294
+ var CLI_SOURCE = "cli";
390
295
  var DEFAULT_LOCALE = "zh-TW";
391
296
  var DEFAULT_TIMEOUT_MS = 1e4;
392
297
  function resolveContext(command) {
@@ -478,16 +383,18 @@ function registerJobsSearch(parent) {
478
383
  const ctx = resolveContext(command);
479
384
  const fields = resolveSearchFields(flags);
480
385
  const query = buildQuery(flags);
481
- const client = createApiClient({
386
+ const client = (0, import_core2.createApiClient)({
482
387
  baseUrl: ctx.baseUrl,
483
388
  locale: ctx.locale,
484
- timeoutMs: ctx.timeoutMs
389
+ timeoutMs: ctx.timeoutMs,
390
+ userAgent: (0, import_core2.buildUserAgent)("wport-cli", "0.9.2"),
391
+ source: CLI_SOURCE
485
392
  });
486
393
  const { data, error, response } = await client.GET("/api/jobs/search", {
487
394
  params: { query }
488
395
  });
489
- if (!response.ok) throwForHttpStatus(response.status, error);
490
- const paged = asPaginatedBody(data);
396
+ if (!response.ok) (0, import_core2.throwForHttpStatus)(response.status, error);
397
+ const paged = (0, import_core2.asPaginatedBody)(data);
491
398
  if (ctx.format === "json") {
492
399
  const body = fields ? { ...paged, data: paged.data.map((item) => pickPaths(item, fields)) } : paged;
493
400
  printJson(body);
@@ -564,21 +471,9 @@ function formatDate(s) {
564
471
  return m ? m[1] : s;
565
472
  }
566
473
 
567
- // src/lib/concurrency.ts
568
- async function mapWithConcurrency(items, limit, fn) {
569
- const results = new Array(items.length);
570
- let cursor = 0;
571
- async function worker() {
572
- for (; ; ) {
573
- const index = cursor++;
574
- if (index >= items.length) return;
575
- results[index] = await fn(items[index], index);
576
- }
577
- }
578
- const workerCount = Math.min(Math.max(1, limit), items.length);
579
- await Promise.all(Array.from({ length: workerCount }, () => worker()));
580
- return results;
581
- }
474
+ // src/commands/jobs/view.ts
475
+ var import_core3 = require("@wport/core");
476
+ var import_core4 = require("@wport/core");
582
477
 
583
478
  // src/lib/io-helpers.ts
584
479
  var import_node_fs3 = require("fs");
@@ -733,10 +628,12 @@ function registerJobsView(parent) {
733
628
  ExitCode.InvalidArgument
734
629
  );
735
630
  }
736
- const client = createApiClient({
631
+ const client = (0, import_core3.createApiClient)({
737
632
  baseUrl: ctx.baseUrl,
738
633
  locale: ctx.locale,
739
- timeoutMs: ctx.timeoutMs
634
+ timeoutMs: ctx.timeoutMs,
635
+ userAgent: (0, import_core3.buildUserAgent)("wport-cli", "0.9.2"),
636
+ source: CLI_SOURCE
740
637
  });
741
638
  if (flags.batch) {
742
639
  await runBatchView(encIdArg, flags, client, ctx.timeoutMs);
@@ -749,8 +646,8 @@ function registerJobsView(parent) {
749
646
  const { data, error, response } = await client.GET("/api/jobs/{encId}/view", {
750
647
  params: { path: { encId } }
751
648
  });
752
- if (!response.ok) throwForHttpStatus(response.status, error);
753
- const job = unwrapDataResponse(data);
649
+ if (!response.ok) (0, import_core3.throwForHttpStatus)(response.status, error);
650
+ const job = (0, import_core3.unwrapDataResponse)(data);
754
651
  if (flags.fields) {
755
652
  printJson(pickPaths(job, parseFieldsList(flags.fields)));
756
653
  return;
@@ -788,11 +685,11 @@ async function fetchJob(client, encId) {
788
685
  const { data, error, response } = await client.GET("/api/jobs/{encId}/view", {
789
686
  params: { path: { encId } }
790
687
  });
791
- if (!response.ok) throwForHttpStatus(response.status, error);
792
- return unwrapDataResponse(data);
688
+ if (!response.ok) (0, import_core3.throwForHttpStatus)(response.status, error);
689
+ return (0, import_core3.unwrapDataResponse)(data);
793
690
  }
794
691
  async function runBatch(encIds, concurrency, fetchOne, project) {
795
- return mapWithConcurrency(encIds, concurrency, async (encId) => {
692
+ return (0, import_core4.mapWithConcurrency)(encIds, concurrency, async (encId) => {
796
693
  try {
797
694
  const job = await fetchOne(encId);
798
695
  return { enc_id: encId, ok: true, data: project(job) };
@@ -989,6 +886,7 @@ function registerConfigCommand(program2) {
989
886
 
990
887
  // src/commands/doctor.ts
991
888
  var import_node_fs6 = require("fs");
889
+ var import_core7 = require("@wport/core");
992
890
 
993
891
  // src/lib/credentials-store.ts
994
892
  var import_node_fs5 = require("fs");
@@ -1165,24 +1063,9 @@ function ensureFormat(key, source) {
1165
1063
  }
1166
1064
  }
1167
1065
 
1168
- // src/lib/personal-types.ts
1169
- var PERSONAL_RESUMES_BASE = "/api/v1/personal/resumes";
1170
- var PERSONAL_APPLICATIONS_BASE = "/api/v1/personal/applications";
1171
- var OAUTH_BASE = "/api/oauth";
1172
- var OAUTH_SESSIONS_BASE = `${OAUTH_BASE}/sessions`;
1173
- var SECTION_WRITE_PLAN = {
1174
- education: { kind: "per-item-post", path: "/education" },
1175
- work_experience: { kind: "work-experience", path: "/work-experience" },
1176
- certificate: { kind: "per-item-post", path: "/certificate" },
1177
- language: { kind: "per-item-post", path: "/language" },
1178
- professional_skills: { kind: "single-post", path: "/professional-skills" },
1179
- autobiography: { kind: "single-post", path: "/autobiography" },
1180
- job_condition: { kind: "single-post", path: "/job-condition" },
1181
- portfolio_links: { kind: "bulk-put", path: "/portfolio-links" },
1182
- background: { kind: "single-post", path: "/background" }
1183
- };
1184
-
1185
1066
  // src/lib/oauth.ts
1067
+ var import_core5 = require("@wport/core");
1068
+ var import_core6 = require("@wport/core");
1186
1069
  var EXPIRED_MESSAGE = "The device code expired before authorization completed. Run `wport login` to try again.";
1187
1070
  async function oauthPost(opts, path, body) {
1188
1071
  const url = new URL(`${opts.baseUrl}${path}`);
@@ -1190,13 +1073,14 @@ async function oauthPost(opts, path, body) {
1190
1073
  method: "POST",
1191
1074
  headers: {
1192
1075
  "Accept-Language": opts.locale,
1193
- "User-Agent": buildUserAgent(),
1076
+ "User-Agent": (0, import_core5.buildUserAgent)("wport-cli", "0.9.2"),
1077
+ "X-Source": CLI_SOURCE,
1194
1078
  Accept: "application/json",
1195
1079
  "Content-Type": "application/json"
1196
1080
  },
1197
1081
  body: JSON.stringify(body)
1198
1082
  });
1199
- const res = await fetchWithTimeout(request, opts.timeoutMs);
1083
+ const res = await (0, import_core5.fetchWithTimeout)(request, opts.timeoutMs);
1200
1084
  const respBody = await res.json().catch(() => null);
1201
1085
  return { status: res.status, body: respBody };
1202
1086
  }
@@ -1209,15 +1093,15 @@ function oauthErrorCode(body) {
1209
1093
  async function requestDeviceCode(opts, deviceName) {
1210
1094
  const body = {};
1211
1095
  if (deviceName) body.device_name = deviceName;
1212
- const { status, body: respBody } = await oauthPost(opts, `${OAUTH_BASE}/device/code`, body);
1213
- if (status < 200 || status >= 300) throwForHttpStatus(status, respBody);
1096
+ const { status, body: respBody } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/device/code`, body);
1097
+ if (status < 200 || status >= 300) (0, import_core5.throwForHttpStatus)(status, respBody);
1214
1098
  return respBody;
1215
1099
  }
1216
1100
  async function pollForToken(opts, device, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
1217
1101
  let intervalSec = device.interval;
1218
1102
  const deadline = Date.now() + device.expires_in * 1e3;
1219
1103
  for (; ; ) {
1220
- const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {
1104
+ const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/token`, {
1221
1105
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1222
1106
  device_code: device.device_code
1223
1107
  });
@@ -1230,14 +1114,14 @@ async function pollForToken(opts, device, sleep = (ms) => new Promise((resolve)
1230
1114
  } else if (code === "expired_token") {
1231
1115
  throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);
1232
1116
  } else if (code !== "authorization_pending") {
1233
- throwForHttpStatus(status, body);
1117
+ (0, import_core5.throwForHttpStatus)(status, body);
1234
1118
  }
1235
1119
  if (Date.now() >= deadline) throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);
1236
1120
  await sleep(intervalSec * 1e3);
1237
1121
  }
1238
1122
  }
1239
1123
  async function refreshAccessToken(opts, refreshToken) {
1240
- const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {
1124
+ const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/token`, {
1241
1125
  grant_type: "refresh_token",
1242
1126
  refresh_token: refreshToken
1243
1127
  });
@@ -1245,11 +1129,11 @@ async function refreshAccessToken(opts, refreshToken) {
1245
1129
  if (oauthErrorCode(body) === "invalid_grant") {
1246
1130
  throw new CliError("Your session is no longer valid. Run `wport login` to sign in again.", ExitCode.ServerClientError);
1247
1131
  }
1248
- throwForHttpStatus(status, body);
1132
+ (0, import_core5.throwForHttpStatus)(status, body);
1249
1133
  }
1250
1134
  async function revokeRefreshToken(opts, refreshToken) {
1251
- const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/revoke`, { token: refreshToken });
1252
- if (status < 200 || status >= 300) throwForHttpStatus(status, body);
1135
+ const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/revoke`, { token: refreshToken });
1136
+ if (status < 200 || status >= 300) (0, import_core5.throwForHttpStatus)(status, body);
1253
1137
  }
1254
1138
 
1255
1139
  // src/commands/doctor.ts
@@ -1272,7 +1156,7 @@ function registerDoctorCommand(program2) {
1272
1156
  }
1273
1157
  async function runDoctor(ctx) {
1274
1158
  const line = (s = "") => process.stdout.write(s + "\n");
1275
- line(`wport-cli ${"0.9.1-dev.0"}`);
1159
+ line(`wport-cli ${"0.9.2"}`);
1276
1160
  line(` bundled schema fingerprint: ${"839e8a891dfb"}`);
1277
1161
  line("");
1278
1162
  line("Resolved configuration:");
@@ -1328,7 +1212,13 @@ function describePersonalLoginLines() {
1328
1212
  }
1329
1213
  async function probeServer(ctx, line) {
1330
1214
  try {
1331
- const client = createApiClient({ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs });
1215
+ const client = (0, import_core7.createApiClient)({
1216
+ baseUrl: ctx.baseUrl,
1217
+ locale: ctx.locale,
1218
+ timeoutMs: ctx.timeoutMs,
1219
+ userAgent: (0, import_core7.buildUserAgent)("wport-cli", "0.9.2"),
1220
+ source: CLI_SOURCE
1221
+ });
1332
1222
  const { response } = await client.GET("/api/jobs/search", { params: { query: { pageSize: 1 } } });
1333
1223
  if (response.ok) {
1334
1224
  line(` \u2713 reachable (HTTP ${response.status})`);
@@ -1353,81 +1243,39 @@ async function probeAuthServer(ctx, line) {
1353
1243
  }
1354
1244
 
1355
1245
  // src/lib/enterprise-client.ts
1356
- var ENTERPRISE_PREFIX = "/api/v1/enterprise";
1357
- async function enterpriseGet(opts, path, query) {
1358
- const url = new URL(`${opts.baseUrl}${ENTERPRISE_PREFIX}${path}`);
1359
- for (const [k, v] of Object.entries(query ?? {})) {
1360
- if (v !== void 0) url.searchParams.set(k, String(v));
1361
- }
1362
- const request = new Request(url, {
1363
- headers: {
1364
- Authorization: `Bearer ${opts.apiKey}`,
1365
- "Accept-Language": opts.locale,
1366
- "User-Agent": buildUserAgent(),
1367
- Accept: "application/json"
1368
- }
1369
- });
1370
- const res = await fetchWithTimeout(request, opts.timeoutMs);
1371
- const body = await res.json().catch(() => null);
1372
- if (!res.ok) throwEnterpriseHttpError(res.status, body);
1373
- warnIfRateLimitLow(res.headers);
1374
- return { body, headers: res.headers };
1375
- }
1376
- async function enterpriseWrite(method, opts, path, body, extra) {
1377
- const url = new URL(`${opts.baseUrl}${ENTERPRISE_PREFIX}${path}`);
1378
- const headers = {
1379
- Authorization: `Bearer ${opts.apiKey}`,
1380
- "Accept-Language": opts.locale,
1381
- "User-Agent": buildUserAgent(),
1382
- Accept: "application/json"
1383
- };
1384
- if (body !== void 0) headers["Content-Type"] = "application/json";
1385
- if (extra?.idempotencyKey) headers["Idempotency-Key"] = extra.idempotencyKey;
1386
- if (extra?.ifMatch) headers["If-Match"] = extra.ifMatch;
1387
- const request = new Request(url, {
1388
- method,
1389
- headers,
1390
- body: body !== void 0 ? JSON.stringify(body) : void 0
1391
- });
1392
- const res = await fetchWithTimeout(request, opts.timeoutMs);
1393
- const respBody = await res.json().catch(() => null);
1394
- if (!res.ok) throwEnterpriseHttpError(res.status, respBody);
1395
- warnIfRateLimitLow(res.headers);
1396
- return { body: respBody, headers: res.headers };
1397
- }
1398
- function enterprisePost(opts, path, body, extra) {
1399
- return enterpriseWrite("POST", opts, path, body, extra);
1400
- }
1401
- function enterprisePatch(opts, path, body, extra) {
1402
- return enterpriseWrite("PATCH", opts, path, body, extra);
1403
- }
1404
- function enterpriseDelete(opts, path, extra) {
1405
- return enterpriseWrite("DELETE", opts, path, void 0, extra);
1406
- }
1407
- function throwEnterpriseHttpError(status, body) {
1408
- const base = extractErrorMessage(body) ?? `HTTP ${status}`;
1409
- if (status === 401) {
1410
- throw new CliError(
1246
+ var import_core8 = require("@wport/core");
1247
+ var transport = __toESM(require("@wport/core"));
1248
+ function withUserAgent(opts) {
1249
+ return { ...opts, userAgent: (0, import_core8.buildUserAgent)("wport-cli", "0.9.2"), source: CLI_SOURCE };
1250
+ }
1251
+ function decorateEnterpriseError(err) {
1252
+ if (!(err instanceof import_core8.WportHttpError)) return err;
1253
+ const base = err.message;
1254
+ if (err.status === 401) {
1255
+ return new import_core8.WportHttpError(
1411
1256
  `${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\`.`,
1412
- ExitCode.ServerClientError
1257
+ err.status,
1258
+ err.body
1413
1259
  );
1414
1260
  }
1415
- if (status === 403) {
1416
- throw new CliError(
1261
+ if (err.status === 403) {
1262
+ return new import_core8.WportHttpError(
1417
1263
  `${base} \u2014 Your key may lack the required scope; rotate or issue a key that includes it. If your company account has been suspended, please contact support.`,
1418
- ExitCode.ServerClientError
1264
+ err.status,
1265
+ err.body
1419
1266
  );
1420
1267
  }
1421
- if (status === 400) {
1422
- const missingFields = extractMissingFields(body);
1268
+ if (err.status === 400) {
1269
+ const missingFields = extractMissingFields(err.body);
1423
1270
  if (missingFields.length > 0) {
1424
- throw new CliError(
1271
+ return new import_core8.WportHttpError(
1425
1272
  `${base} \u2014 Missing required fields: ${missingFields.join(", ")}. Fill them via \`wport enterprise jobs update <enc_id> ...\` (or the web console), then publish.`,
1426
- ExitCode.ServerClientError
1273
+ err.status,
1274
+ err.body
1427
1275
  );
1428
1276
  }
1429
1277
  }
1430
- throwForHttpStatus(status, body);
1278
+ return err;
1431
1279
  }
1432
1280
  function extractMissingFields(body) {
1433
1281
  if (!body || typeof body !== "object") return [];
@@ -1444,6 +1292,28 @@ function warnIfRateLimitLow(headers) {
1444
1292
  printWarn(`Rate limit nearly exhausted: ${remaining}/${limit} requests remaining this window.`, false);
1445
1293
  }
1446
1294
  }
1295
+ async function wrap(p) {
1296
+ let res;
1297
+ try {
1298
+ res = await p;
1299
+ } catch (err) {
1300
+ throw decorateEnterpriseError(err);
1301
+ }
1302
+ warnIfRateLimitLow(res.headers);
1303
+ return res;
1304
+ }
1305
+ function enterpriseGet2(opts, path, query) {
1306
+ return wrap(transport.enterpriseGet(withUserAgent(opts), path, query));
1307
+ }
1308
+ function enterprisePost2(opts, path, body, extra) {
1309
+ return wrap(transport.enterprisePost(withUserAgent(opts), path, body, extra));
1310
+ }
1311
+ function enterprisePatch2(opts, path, body, extra) {
1312
+ return wrap(transport.enterprisePatch(withUserAgent(opts), path, body, extra));
1313
+ }
1314
+ function enterpriseDelete2(opts, path, extra) {
1315
+ return wrap(transport.enterpriseDelete(withUserAgent(opts), path, extra));
1316
+ }
1447
1317
 
1448
1318
  // src/commands/enterprise/login.ts
1449
1319
  async function performLogin(ctx, key) {
@@ -1455,7 +1325,7 @@ async function performLogin(ctx, key) {
1455
1325
  ExitCode.InvalidArgument
1456
1326
  );
1457
1327
  }
1458
- const { body } = await enterpriseGet({ ...ctx, apiKey: key }, "/me");
1328
+ const { body } = await enterpriseGet2({ ...ctx, apiKey: key }, "/me");
1459
1329
  saveCredentials({
1460
1330
  api_key: key,
1461
1331
  company_name: extractCompanyName(body),
@@ -1520,15 +1390,16 @@ function registerEnterpriseWhoami(parent) {
1520
1390
  }
1521
1391
 
1522
1392
  // src/commands/enterprise/usage.ts
1393
+ var import_core9 = require("@wport/core");
1523
1394
  function num(value) {
1524
1395
  return typeof value === "number" && Number.isFinite(value) ? String(value) : "\u2014";
1525
1396
  }
1526
1397
  async function runUsage(ctx, apiKey) {
1527
- const { body } = await enterpriseGet(
1398
+ const { body } = await enterpriseGet2(
1528
1399
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1529
1400
  "/usage"
1530
1401
  );
1531
- const usage = unwrapDataResponse(body);
1402
+ const usage = (0, import_core9.unwrapDataResponse)(body);
1532
1403
  if (ctx.format === "json") {
1533
1404
  printJson(usage);
1534
1405
  return;
@@ -1555,6 +1426,7 @@ function registerEnterpriseUsage(parent) {
1555
1426
  }
1556
1427
 
1557
1428
  // src/commands/enterprise/jobs/list.ts
1429
+ var import_core10 = require("@wport/core");
1558
1430
  var STATUS_MAP = { published: 1, unpublished: 0 };
1559
1431
  var MINIMAL_LIST_FIELDS = ["enc_id", "job_title", "status", "updated_at"];
1560
1432
  function mapStatusFlag(raw) {
@@ -1580,7 +1452,7 @@ async function runEnterpriseJobsList(ctx, apiKey, flags) {
1580
1452
  if (flags.fields && flags.minimal) {
1581
1453
  throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
1582
1454
  }
1583
- const { body } = await enterpriseGet(
1455
+ const { body } = await enterpriseGet2(
1584
1456
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1585
1457
  "/jobs",
1586
1458
  {
@@ -1590,7 +1462,7 @@ async function runEnterpriseJobsList(ctx, apiKey, flags) {
1590
1462
  status: mapStatusFlag(flags.status)
1591
1463
  }
1592
1464
  );
1593
- const paged = asPaginatedBody(body);
1465
+ const paged = (0, import_core10.asPaginatedBody)(body);
1594
1466
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS : flags.fields ? parseFieldsList(flags.fields) : void 0;
1595
1467
  if (projection || ctx.format === "json") {
1596
1468
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -1622,6 +1494,7 @@ function registerEnterpriseJobsList(parent) {
1622
1494
  }
1623
1495
 
1624
1496
  // src/commands/enterprise/jobs/view.ts
1497
+ var import_core11 = require("@wport/core");
1625
1498
  var DETAIL_FIELDS = ["enc_id", "job_title", "code", "status", "created_at", "updated_at"];
1626
1499
  function renderDetailLines(job) {
1627
1500
  const pad = Math.max(...DETAIL_FIELDS.map((f) => f.length)) + 1;
@@ -1642,11 +1515,11 @@ function registerEnterpriseJobsView(parent) {
1642
1515
  }
1643
1516
  const globals = command.optsWithGlobals();
1644
1517
  const { key } = resolveApiKey(globals.apiKey);
1645
- const { body } = await enterpriseGet(
1518
+ const { body } = await enterpriseGet2(
1646
1519
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey: key },
1647
1520
  `/jobs/${encodeURIComponent(encId.trim())}`
1648
1521
  );
1649
- const job = unwrapDataResponse(body);
1522
+ const job = (0, import_core11.unwrapDataResponse)(body);
1650
1523
  if (flags.fields) {
1651
1524
  printJson(pickPaths(job, parseFieldsList(flags.fields)));
1652
1525
  return;
@@ -1661,15 +1534,16 @@ function registerEnterpriseJobsView(parent) {
1661
1534
 
1662
1535
  // src/commands/enterprise/jobs/create.ts
1663
1536
  var import_node_crypto = require("crypto");
1537
+ var import_core12 = require("@wport/core");
1664
1538
  async function runJobsCreate(ctx, apiKey, source, idempotencyKey) {
1665
1539
  const jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
1666
- const { body } = await enterprisePost(
1540
+ const { body } = await enterprisePost2(
1667
1541
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1668
1542
  "/jobs",
1669
1543
  jobBody,
1670
1544
  { idempotencyKey }
1671
1545
  );
1672
- const created = unwrapDataResponse(body);
1546
+ const created = (0, import_core12.unwrapDataResponse)(body);
1673
1547
  if (ctx.format === "json") {
1674
1548
  printJson(created);
1675
1549
  return;
@@ -1688,6 +1562,7 @@ function registerEnterpriseJobsCreate(parent) {
1688
1562
 
1689
1563
  // src/commands/enterprise/jobs/update.ts
1690
1564
  var import_node_crypto2 = require("crypto");
1565
+ var import_core13 = require("@wport/core");
1691
1566
 
1692
1567
  // src/commands/enterprise/jobs/write-shared.ts
1693
1568
  function requireEncId(encId) {
@@ -1700,13 +1575,13 @@ function requireEncId(encId) {
1700
1575
  async function runJobsUpdate(ctx, apiKey, encId, source, idempotencyKey, ifMatch) {
1701
1576
  const trimmed = requireEncId(encId);
1702
1577
  const jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
1703
- const { body } = await enterprisePatch(
1578
+ const { body } = await enterprisePatch2(
1704
1579
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1705
1580
  `/jobs/${encodeURIComponent(trimmed)}`,
1706
1581
  jobBody,
1707
1582
  { idempotencyKey, ifMatch }
1708
1583
  );
1709
- const updated = unwrapDataResponse(body);
1584
+ const updated = (0, import_core13.unwrapDataResponse)(body);
1710
1585
  if (ctx.format === "json") {
1711
1586
  printJson(updated);
1712
1587
  return;
@@ -1725,15 +1600,16 @@ function registerEnterpriseJobsUpdate(parent) {
1725
1600
 
1726
1601
  // src/commands/enterprise/jobs/lifecycle.ts
1727
1602
  var import_node_crypto3 = require("crypto");
1603
+ var import_core14 = require("@wport/core");
1728
1604
  async function runJobsTransition(ctx, apiKey, encId, action, idempotencyKey) {
1729
1605
  const trimmed = requireEncId(encId);
1730
- const { body } = await enterprisePatch(
1606
+ const { body } = await enterprisePatch2(
1731
1607
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1732
1608
  `/jobs/${encodeURIComponent(trimmed)}/${action}`,
1733
1609
  {},
1734
1610
  { idempotencyKey }
1735
1611
  );
1736
- const result = unwrapDataResponse(body);
1612
+ const result = (0, import_core14.unwrapDataResponse)(body);
1737
1613
  if (ctx.format === "json") {
1738
1614
  printJson(result);
1739
1615
  return;
@@ -1747,7 +1623,7 @@ async function runJobsDelete(ctx, apiKey, encId, confirm, idempotencyKey) {
1747
1623
  throw new CliError("Refusing to delete without --confirm (destructive, irreversible)", ExitCode.InvalidArgument);
1748
1624
  }
1749
1625
  const trimmed = requireEncId(encId);
1750
- await enterpriseDelete(
1626
+ await enterpriseDelete2(
1751
1627
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1752
1628
  `/jobs/${encodeURIComponent(trimmed)}`,
1753
1629
  { idempotencyKey }
@@ -1761,13 +1637,13 @@ async function runJobsDelete(ctx, apiKey, encId, confirm, idempotencyKey) {
1761
1637
  }
1762
1638
  async function runJobsCopy(ctx, apiKey, encId, idempotencyKey) {
1763
1639
  const trimmed = requireEncId(encId);
1764
- const { body } = await enterprisePost(
1640
+ const { body } = await enterprisePost2(
1765
1641
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1766
1642
  `/jobs/${encodeURIComponent(trimmed)}/copy`,
1767
1643
  {},
1768
1644
  { idempotencyKey }
1769
1645
  );
1770
- const result = unwrapDataResponse(body);
1646
+ const result = (0, import_core14.unwrapDataResponse)(body);
1771
1647
  if (ctx.format === "json") {
1772
1648
  printJson(result);
1773
1649
  return;
@@ -1821,6 +1697,7 @@ function registerEnterpriseJobsDelete(parent) {
1821
1697
 
1822
1698
  // src/commands/enterprise/jobs/batch.ts
1823
1699
  var import_node_crypto4 = require("crypto");
1700
+ var import_core15 = require("@wport/core");
1824
1701
  var BATCH_MIN = 1;
1825
1702
  var BATCH_MAX = 10;
1826
1703
  async function runJobsBatch(ctx, apiKey, source, confirm, idempotencyKey) {
@@ -1835,13 +1712,13 @@ async function runJobsBatch(ctx, apiKey, source, confirm, idempotencyKey) {
1835
1712
  ExitCode.InvalidArgument
1836
1713
  );
1837
1714
  }
1838
- const { body } = await enterprisePost(
1715
+ const { body } = await enterprisePost2(
1839
1716
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1840
1717
  "/jobs/batch",
1841
1718
  payload,
1842
1719
  { idempotencyKey }
1843
1720
  );
1844
- const result = unwrapDataResponse(body);
1721
+ const result = (0, import_core15.unwrapDataResponse)(body);
1845
1722
  const succeeded = result.succeeded ?? [];
1846
1723
  const failed = result.failed ?? [];
1847
1724
  if (ctx.format === "json") {
@@ -1884,6 +1761,7 @@ function registerEnterpriseJobsCommand(parent) {
1884
1761
  }
1885
1762
 
1886
1763
  // src/commands/enterprise/keys/list.ts
1764
+ var import_core16 = require("@wport/core");
1887
1765
  function formatDate3(value) {
1888
1766
  return value ? String(value).slice(0, 10) : "";
1889
1767
  }
@@ -1891,11 +1769,11 @@ function formatScopes(scopes) {
1891
1769
  return Array.isArray(scopes) ? scopes.join(",") : "";
1892
1770
  }
1893
1771
  async function runKeysList(ctx, apiKey) {
1894
- const { body } = await enterpriseGet(
1772
+ const { body } = await enterpriseGet2(
1895
1773
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1896
1774
  "/keys"
1897
1775
  );
1898
- const keys = unwrapDataArray(body);
1776
+ const keys = (0, import_core16.unwrapDataArray)(body);
1899
1777
  if (ctx.format === "json") {
1900
1778
  printJson(keys);
1901
1779
  return;
@@ -1926,6 +1804,7 @@ function registerEnterpriseKeysList(parent) {
1926
1804
  }
1927
1805
 
1928
1806
  // src/commands/enterprise/keys/rotate.ts
1807
+ var import_core17 = require("@wport/core");
1929
1808
  var ENTERPRISE_KEY_EXPIRY_DAYS = [30, 60, 90];
1930
1809
  function validateExpiryDays(raw) {
1931
1810
  if (raw === void 0) return void 0;
@@ -1943,12 +1822,12 @@ async function runKeysRotate(ctx, apiKey, encId, flags) {
1943
1822
  const expiryDays = validateExpiryDays(flags.expiryDays);
1944
1823
  const requestBody = {};
1945
1824
  if (expiryDays !== void 0) requestBody.expiry_days = expiryDays;
1946
- const { body } = await enterprisePost(
1825
+ const { body } = await enterprisePost2(
1947
1826
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1948
1827
  `/keys/${encodeURIComponent(trimmed)}/rotate`,
1949
1828
  requestBody
1950
1829
  );
1951
- const issued = unwrapDataResponse(body);
1830
+ const issued = (0, import_core17.unwrapDataResponse)(body);
1952
1831
  if (ctx.format === "json") {
1953
1832
  printJson(issued);
1954
1833
  } else {
@@ -1987,6 +1866,7 @@ function registerEnterpriseKeysCommand(parent) {
1987
1866
  }
1988
1867
 
1989
1868
  // src/commands/enterprise/company/view.ts
1869
+ var import_core18 = require("@wport/core");
1990
1870
  var COMPANY_STATUS_LABELS = {
1991
1871
  0: "not_submitted",
1992
1872
  1: "pending_review",
@@ -2042,11 +1922,11 @@ function renderDetailLines2(company) {
2042
1922
  return lines;
2043
1923
  }
2044
1924
  async function runCompanyView(ctx, apiKey, flags) {
2045
- const { body } = await enterpriseGet(
1925
+ const { body } = await enterpriseGet2(
2046
1926
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2047
1927
  "/company"
2048
1928
  );
2049
- const company = unwrapDataResponse(body);
1929
+ const company = (0, import_core18.unwrapDataResponse)(body);
2050
1930
  if (flags.fields) {
2051
1931
  printJson(pickPaths(company, parseFieldsList(flags.fields)));
2052
1932
  return;
@@ -2068,6 +1948,7 @@ function registerEnterpriseCompanyView(parent) {
2068
1948
 
2069
1949
  // src/commands/enterprise/company/update.ts
2070
1950
  var import_node_crypto5 = require("crypto");
1951
+ var import_core19 = require("@wport/core");
2071
1952
 
2072
1953
  // src/commands/enterprise/company/types.ts
2073
1954
  var BASIC_FIELDS = [
@@ -2133,11 +2014,11 @@ async function buildCompanyUpdatePayloads(input, ctx, apiKey) {
2133
2014
  if (!inputHasBasicField && !inputHasDescriptionField) {
2134
2015
  throw new InvalidArgumentError("No writable company fields provided");
2135
2016
  }
2136
- const { body } = await enterpriseGet(
2017
+ const { body } = await enterpriseGet2(
2137
2018
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2138
2019
  "/company"
2139
2020
  );
2140
- const current = unwrapDataResponse(body);
2021
+ const current = (0, import_core19.unwrapDataResponse)(body);
2141
2022
  const missingContractFields = CONTRACT_REQUIRED_GET_FIELDS.filter((f) => !(f in current));
2142
2023
  if (missingContractFields.length > 0) {
2143
2024
  throw new CliError(
@@ -2204,23 +2085,23 @@ async function runCompanyUpdate(ctx, apiKey, source, idempotencyFlags, options =
2204
2085
  const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
2205
2086
  let basicResultCompany;
2206
2087
  if (needsBasic) {
2207
- const { body } = await enterprisePatch(requestOpts, "/company/basic", payloads.basic, {
2088
+ const { body } = await enterprisePatch2(requestOpts, "/company/basic", payloads.basic, {
2208
2089
  idempotencyKey: basicKey
2209
2090
  });
2210
- basicResultCompany = unwrapDataResponse(body);
2091
+ basicResultCompany = (0, import_core19.unwrapDataResponse)(body);
2211
2092
  }
2212
2093
  if (!needsDescriptions) {
2213
2094
  printCompanyResult(basicResultCompany, ctx.format);
2214
2095
  return;
2215
2096
  }
2216
2097
  try {
2217
- const { body } = await enterprisePatch(
2098
+ const { body } = await enterprisePatch2(
2218
2099
  requestOpts,
2219
2100
  "/company/descriptions",
2220
2101
  payloads.descriptions,
2221
2102
  { idempotencyKey: descriptionsKey }
2222
2103
  );
2223
- const result = unwrapDataResponse(body);
2104
+ const result = (0, import_core19.unwrapDataResponse)(body);
2224
2105
  printCompanyResult(result.company, ctx.format);
2225
2106
  } catch (err) {
2226
2107
  if (!needsBasic) {
@@ -2279,6 +2160,7 @@ function registerEnterpriseCompanyUpdate(parent) {
2279
2160
  var import_node_crypto6 = require("crypto");
2280
2161
  var import_node_fs7 = require("fs");
2281
2162
  var import_node_path3 = require("path");
2163
+ var import_core20 = require("@wport/core");
2282
2164
  var EXTENSION_TO_CONTENT_TYPE = {
2283
2165
  ".png": "image/png",
2284
2166
  ".jpg": "image/jpeg",
@@ -2330,29 +2212,29 @@ async function runCompanyLogoUpload(ctx, apiKey, path, idempotencyFlags) {
2330
2212
  const { contentType, fileSize, bytes } = inspectLocalFile(path);
2331
2213
  const { presignKey, confirmKey } = resolveLogoIdempotencyKeys(idempotencyFlags);
2332
2214
  const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
2333
- const { body: presignBody } = await enterprisePost(
2215
+ const { body: presignBody } = await enterprisePost2(
2334
2216
  requestOpts,
2335
2217
  "/company/logo/presign",
2336
2218
  { content_type: contentType, file_size: fileSize },
2337
2219
  { idempotencyKey: presignKey }
2338
2220
  );
2339
- const presign = unwrapDataResponse(presignBody);
2221
+ const presign = (0, import_core20.unwrapDataResponse)(presignBody);
2340
2222
  const putRequest = new Request(presign.upload_url, {
2341
2223
  method: "PUT",
2342
2224
  headers: { "Content-Type": contentType },
2343
2225
  body: bytes
2344
2226
  });
2345
- const putResponse = await fetchWithTimeout(putRequest, ctx.timeoutMs);
2227
+ const putResponse = await (0, import_core20.fetchWithTimeout)(putRequest, ctx.timeoutMs);
2346
2228
  if (!putResponse.ok) {
2347
2229
  throw new NetworkError(`Failed to upload file to S3: HTTP ${putResponse.status}`);
2348
2230
  }
2349
- const { body: confirmBody } = await enterprisePost(
2231
+ const { body: confirmBody } = await enterprisePost2(
2350
2232
  requestOpts,
2351
2233
  "/company/logo/confirm",
2352
2234
  { s3_key: presign.s3_key },
2353
2235
  { idempotencyKey: confirmKey }
2354
2236
  );
2355
- const result = unwrapDataResponse(confirmBody);
2237
+ const result = (0, import_core20.unwrapDataResponse)(confirmBody);
2356
2238
  printLogoResult(result, ctx.format);
2357
2239
  }
2358
2240
  function registerEnterpriseCompanyLogo(parent) {
@@ -2374,6 +2256,7 @@ function registerEnterpriseCompanyCommand(parent) {
2374
2256
  }
2375
2257
 
2376
2258
  // src/commands/enterprise/talents/list.ts
2259
+ var import_core21 = require("@wport/core");
2377
2260
  var DEFAULT_PAGE_SIZE = 20;
2378
2261
  var MINIMAL_LIST_FIELDS2 = ["enc_resume_id", "candidate_name", "applied_job_title", "applied_at"];
2379
2262
  function formatDate4(value) {
@@ -2383,7 +2266,7 @@ async function runEnterpriseTalentsList(ctx, apiKey, flags) {
2383
2266
  if (flags.fields && flags.minimal) {
2384
2267
  throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
2385
2268
  }
2386
- const { body } = await enterpriseGet(
2269
+ const { body } = await enterpriseGet2(
2387
2270
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2388
2271
  "/talents",
2389
2272
  {
@@ -2396,7 +2279,7 @@ async function runEnterpriseTalentsList(ctx, apiKey, flags) {
2396
2279
  pageSize: flags.pageSize ?? DEFAULT_PAGE_SIZE
2397
2280
  }
2398
2281
  );
2399
- const paged = asPaginatedBody(body);
2282
+ const paged = (0, import_core21.asPaginatedBody)(body);
2400
2283
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS2 : flags.fields ? parseFieldsList(flags.fields) : void 0;
2401
2284
  if (projection || ctx.format === "json") {
2402
2285
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -2429,16 +2312,17 @@ function registerEnterpriseTalentsList(parent) {
2429
2312
  }
2430
2313
 
2431
2314
  // src/commands/enterprise/talents/view.ts
2315
+ var import_core22 = require("@wport/core");
2432
2316
  async function runEnterpriseTalentsView(ctx, apiKey, encResumeId, flags) {
2433
2317
  const trimmed = encResumeId.trim();
2434
2318
  if (!trimmed) {
2435
2319
  throw new CliError("enc_resume_id must not be empty", ExitCode.InvalidArgument);
2436
2320
  }
2437
- const { body } = await enterpriseGet(
2321
+ const { body } = await enterpriseGet2(
2438
2322
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2439
2323
  `/talents/${encodeURIComponent(trimmed)}`
2440
2324
  );
2441
- const resume = unwrapDataResponse(body);
2325
+ const resume = (0, import_core22.unwrapDataResponse)(body);
2442
2326
  if (flags.fields) {
2443
2327
  printJson(pickPaths(resume, parseFieldsList(flags.fields)));
2444
2328
  return;
@@ -2456,6 +2340,7 @@ function registerEnterpriseTalentsView(parent) {
2456
2340
 
2457
2341
  // src/commands/enterprise/talents/respond.ts
2458
2342
  var import_node_crypto7 = require("crypto");
2343
+ var import_core23 = require("@wport/core");
2459
2344
  function resolveRespondBody(flags, options = {}) {
2460
2345
  const hasBody = flags.body !== void 0;
2461
2346
  const hasBodyFile = flags.bodyFile !== void 0;
@@ -2481,13 +2366,13 @@ async function runEnterpriseTalentsRespond(ctx, apiKey, encResumeId, flags, idem
2481
2366
  const encJobId = flags.encJobId?.trim();
2482
2367
  const payload = { subject, body };
2483
2368
  if (encJobId) payload.enc_job_id = encJobId;
2484
- const { body: respBody } = await enterprisePost(
2369
+ const { body: respBody } = await enterprisePost2(
2485
2370
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2486
2371
  `/talents/${encodeURIComponent(trimmedId)}/respond`,
2487
2372
  payload,
2488
2373
  { idempotencyKey }
2489
2374
  );
2490
- const result = unwrapDataResponse(respBody);
2375
+ const result = (0, import_core23.unwrapDataResponse)(respBody);
2491
2376
  if (ctx.format === "json") {
2492
2377
  printJson(result);
2493
2378
  return;
@@ -2516,15 +2401,16 @@ function registerEnterpriseTalentsCommand(parent) {
2516
2401
 
2517
2402
  // src/commands/enterprise/campaigns/create.ts
2518
2403
  var import_node_crypto8 = require("crypto");
2404
+ var import_core24 = require("@wport/core");
2519
2405
  async function runCampaignCreate(ctx, apiKey, source, idempotencyKey) {
2520
2406
  const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
2521
- const { body } = await enterprisePost(
2407
+ const { body } = await enterprisePost2(
2522
2408
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2523
2409
  "/campaigns",
2524
2410
  campaignBody,
2525
2411
  { idempotencyKey }
2526
2412
  );
2527
- const created = unwrapDataResponse(body);
2413
+ const created = (0, import_core24.unwrapDataResponse)(body);
2528
2414
  if (ctx.format === "json") {
2529
2415
  printJson(created);
2530
2416
  return;
@@ -2542,6 +2428,7 @@ function registerEnterpriseCampaignsCreate(parent) {
2542
2428
  }
2543
2429
 
2544
2430
  // src/commands/enterprise/campaigns/list.ts
2431
+ var import_core25 = require("@wport/core");
2545
2432
  var STATUS_MAP2 = { open: 1, closed: 0 };
2546
2433
  var MINIMAL_LIST_FIELDS3 = ["enc_id", "name", "status", "job_count"];
2547
2434
  function mapStatusFlag2(raw) {
@@ -2561,7 +2448,7 @@ async function runEnterpriseCampaignsList(ctx, apiKey, flags) {
2561
2448
  if (flags.fields && flags.minimal) {
2562
2449
  throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
2563
2450
  }
2564
- const { body } = await enterpriseGet(
2451
+ const { body } = await enterpriseGet2(
2565
2452
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2566
2453
  "/campaigns",
2567
2454
  {
@@ -2571,7 +2458,7 @@ async function runEnterpriseCampaignsList(ctx, apiKey, flags) {
2571
2458
  status: mapStatusFlag2(flags.status)
2572
2459
  }
2573
2460
  );
2574
- const paged = asPaginatedBody(body);
2461
+ const paged = (0, import_core25.asPaginatedBody)(body);
2575
2462
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS3 : flags.fields ? parseFieldsList(flags.fields) : void 0;
2576
2463
  if (projection || ctx.format === "json") {
2577
2464
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -2604,15 +2491,16 @@ function registerEnterpriseCampaignsList(parent) {
2604
2491
 
2605
2492
  // src/commands/enterprise/campaigns/lifecycle.ts
2606
2493
  var import_node_crypto9 = require("crypto");
2494
+ var import_core26 = require("@wport/core");
2607
2495
  async function runCampaignTransition(ctx, apiKey, encId, action, idempotencyKey) {
2608
2496
  const trimmed = requireEncId(encId);
2609
- const { body } = await enterprisePatch(
2497
+ const { body } = await enterprisePatch2(
2610
2498
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2611
2499
  `/campaigns/${encodeURIComponent(trimmed)}/${action}`,
2612
2500
  {},
2613
2501
  { idempotencyKey }
2614
2502
  );
2615
- const result = unwrapDataResponse(body);
2503
+ const result = (0, import_core26.unwrapDataResponse)(body);
2616
2504
  if (ctx.format === "json") {
2617
2505
  printJson(result);
2618
2506
  return;
@@ -2638,16 +2526,17 @@ function registerEnterpriseCampaignsUnpublish(parent) {
2638
2526
 
2639
2527
  // src/commands/enterprise/campaigns/update.ts
2640
2528
  var import_node_crypto10 = require("crypto");
2529
+ var import_core27 = require("@wport/core");
2641
2530
  async function runCampaignUpdate(ctx, apiKey, encId, source, idempotencyKey) {
2642
2531
  const trimmed = requireEncId(encId);
2643
2532
  const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
2644
- const { body } = await enterprisePatch(
2533
+ const { body } = await enterprisePatch2(
2645
2534
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2646
2535
  `/campaigns/${encodeURIComponent(trimmed)}`,
2647
2536
  campaignBody,
2648
2537
  { idempotencyKey }
2649
2538
  );
2650
- const updated = unwrapDataResponse(body);
2539
+ const updated = (0, import_core27.unwrapDataResponse)(body);
2651
2540
  if (ctx.format === "json") {
2652
2541
  printJson(updated);
2653
2542
  return;
@@ -2665,16 +2554,17 @@ function registerEnterpriseCampaignsUpdate(parent) {
2665
2554
  }
2666
2555
 
2667
2556
  // src/commands/enterprise/campaigns/view.ts
2557
+ var import_core28 = require("@wport/core");
2668
2558
  async function runEnterpriseCampaignsView(ctx, apiKey, encId, flags) {
2669
2559
  const trimmed = encId.trim();
2670
2560
  if (!trimmed) {
2671
2561
  throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
2672
2562
  }
2673
- const { body } = await enterpriseGet(
2563
+ const { body } = await enterpriseGet2(
2674
2564
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2675
2565
  `/campaigns/${encodeURIComponent(trimmed)}`
2676
2566
  );
2677
- const campaign = unwrapDataResponse(body);
2567
+ const campaign = (0, import_core28.unwrapDataResponse)(body);
2678
2568
  if (flags.fields) {
2679
2569
  printJson(pickPaths(campaign, parseFieldsList(flags.fields)));
2680
2570
  return;
@@ -2818,7 +2708,11 @@ function registerLoginCommand(program2) {
2818
2708
  });
2819
2709
  }
2820
2710
 
2711
+ // src/commands/auth/whoami.ts
2712
+ var import_core30 = require("@wport/core");
2713
+
2821
2714
  // src/lib/personal-client.ts
2715
+ var import_core29 = require("@wport/core");
2822
2716
  var EXPIRY_SKEW_MS = 3e4;
2823
2717
  var NOT_LOGGED_IN_MESSAGE = "Not logged in. Run `wport login`.";
2824
2718
  async function ensureFreshCredentials(opts) {
@@ -2847,7 +2741,8 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
2847
2741
  const headers = {
2848
2742
  Authorization: `Bearer ${accessToken}`,
2849
2743
  "Accept-Language": opts.locale,
2850
- "User-Agent": buildUserAgent(),
2744
+ "User-Agent": (0, import_core29.buildUserAgent)("wport-cli", "0.9.2"),
2745
+ "X-Source": CLI_SOURCE,
2851
2746
  Accept: "application/json"
2852
2747
  };
2853
2748
  if (body !== void 0) headers["Content-Type"] = "application/json";
@@ -2857,7 +2752,7 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
2857
2752
  headers,
2858
2753
  body: body !== void 0 ? JSON.stringify(body) : void 0
2859
2754
  });
2860
- const res = await fetchWithTimeout(request, opts.timeoutMs);
2755
+ const res = await (0, import_core29.fetchWithTimeout)(request, opts.timeoutMs);
2861
2756
  const respBody = await res.json().catch(() => null);
2862
2757
  return { status: res.status, body: respBody, headers: res.headers };
2863
2758
  }
@@ -2889,13 +2784,13 @@ function personalDelete(opts, path, extra) {
2889
2784
  }
2890
2785
  function throwPersonalHttpError(status, body) {
2891
2786
  if (status === 401) {
2892
- const base = extractErrorMessage(body) ?? `HTTP ${status}`;
2787
+ const base = (0, import_core29.extractErrorMessage)(body) ?? `HTTP ${status}`;
2893
2788
  throw new CliError(
2894
2789
  `${base} \u2014 Your session may have expired or the request was rejected. Run \`wport login\` to sign in again.`,
2895
2790
  ExitCode.ServerClientError
2896
2791
  );
2897
2792
  }
2898
- throwForHttpStatus(status, body);
2793
+ (0, import_core29.throwForHttpStatus)(status, body);
2899
2794
  }
2900
2795
  function warnIfRateLimitLow2(headers) {
2901
2796
  const remaining = Number(headers.get("x-ratelimit-remaining"));
@@ -2906,6 +2801,7 @@ function warnIfRateLimitLow2(headers) {
2906
2801
  }
2907
2802
 
2908
2803
  // src/commands/auth/whoami.ts
2804
+ var import_core31 = require("@wport/core");
2909
2805
  var PLACEHOLDER = "\u2014";
2910
2806
  function display(value) {
2911
2807
  const clean = sanitizeForTerminal(value ?? "");
@@ -2913,8 +2809,8 @@ function display(value) {
2913
2809
  }
2914
2810
  async function performWhoami(ctx) {
2915
2811
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
2916
- const { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);
2917
- const sessions = unwrapDataArray(body);
2812
+ const { body } = await personalGet(opts, import_core31.OAUTH_SESSIONS_BASE);
2813
+ const sessions = (0, import_core30.unwrapDataArray)(body);
2918
2814
  const current = sessions.find((s) => s.is_current);
2919
2815
  const localLoginAt = loadPersonalCredentials()?.session_created_at ?? null;
2920
2816
  if (ctx.format === "json") {
@@ -2966,13 +2862,15 @@ function registerLogoutCommand(program2) {
2966
2862
  }
2967
2863
 
2968
2864
  // src/commands/sessions/list.ts
2865
+ var import_core32 = require("@wport/core");
2866
+ var import_core33 = require("@wport/core");
2969
2867
  function formatDate5(value) {
2970
2868
  return value ? String(value).slice(0, 10) : "";
2971
2869
  }
2972
2870
  async function runSessionsList(ctx) {
2973
2871
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
2974
- const { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);
2975
- const sessions = unwrapDataArray(body);
2872
+ const { body } = await personalGet(opts, import_core33.OAUTH_SESSIONS_BASE);
2873
+ const sessions = (0, import_core32.unwrapDataArray)(body);
2976
2874
  if (ctx.format === "json") {
2977
2875
  printJson(sessions);
2978
2876
  return;
@@ -2996,6 +2894,8 @@ function registerSessionsList(parent) {
2996
2894
  }
2997
2895
 
2998
2896
  // src/commands/sessions/revoke.ts
2897
+ var import_core34 = require("@wport/core");
2898
+ var import_core35 = require("@wport/core");
2999
2899
  async function runSessionsRevoke(ctx, encId, allOthers) {
3000
2900
  const hasEncId = encId !== void 0;
3001
2901
  if (hasEncId === allOthers) {
@@ -3003,8 +2903,8 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
3003
2903
  }
3004
2904
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
3005
2905
  if (allOthers) {
3006
- const { body } = await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/others`);
3007
- const result = unwrapDataResponse(body);
2906
+ const { body } = await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/others`);
2907
+ const result = (0, import_core35.unwrapDataResponse)(body);
3008
2908
  if (ctx.format === "json") {
3009
2909
  printJson(result);
3010
2910
  return;
@@ -3015,7 +2915,7 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
3015
2915
  }
3016
2916
  const trimmed = encId.trim();
3017
2917
  if (!trimmed) throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
3018
- await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
2918
+ await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
3019
2919
  if (ctx.format === "json") {
3020
2920
  printJson({ enc_id: trimmed, revoked: true });
3021
2921
  return;
@@ -3037,949 +2937,19 @@ function registerSessionsCommand(program2) {
3037
2937
  registerSessionsRevoke(sessions);
3038
2938
  }
3039
2939
 
3040
- // src/lib/resume-schema/definition.ts
3041
- var DEGREE_TYPE_VALUES = ["phd", "master", "bachelor", "associate", "senior_high", "junior_or_below"];
3042
- var EDUCATION_STATUS_VALUES = ["graduated", "dropped", "studying"];
3043
- var JOB_FEATURE_CODE_VALUES = ["dispatch", "executive", "full_time", "internship", "part_time"];
3044
- var WORKING_HOUR_TYPE_VALUES = ["day_shift", "evening_shift", "night_shift", "rotating_shift", "weekend_shift"];
3045
- var AVAILABLE_START_TYPE_VALUES = ["available_after_hired", "available_custom_date"];
3046
- var AVAILABLE_START_PERIOD_VALUES = ["week", "two_weeks", "month", "two_months", "three_months", "anytime"];
3047
- var SALARY_EXPECTATION_TYPE_VALUES = ["negotiable", "company_rules", "custom"];
3048
- var SALARY_UNIT_VALUES = ["hourly", "daily", "monthly", "yearly"];
3049
- var PROFICIENCY_LEVEL_VALUES = ["beginner", "daily_convo", "proficient", "native"];
3050
- var JOB_STATUS_VALUES = ["employed", "military", "student", "unemployed"];
3051
- var VEHICLE_OR_LICENSE_TYPE_VALUES = [
3052
- "bus",
3053
- "heavy_motorcycle",
3054
- "light_motorcycle",
3055
- "light_car",
3056
- "pro_bus",
3057
- "pro_small_car",
3058
- "pro_trailer",
3059
- "pro_truck",
3060
- "scooter",
3061
- "trailer",
3062
- "truck"
3063
- ];
3064
- var CURRENT_YEAR = (/* @__PURE__ */ new Date()).getFullYear();
3065
- var TOP_LEVEL_FIELDS = {
3066
- name: {
3067
- type: "string",
3068
- required: true,
3069
- minLength: 1,
3070
- maxLength: 100,
3071
- description: "\u5C65\u6B77\u540D\u7A31",
3072
- example: "\u6211\u7684\u5C65\u6B77\u540D\u7A31"
3073
- }
3074
- };
3075
- var educationSection = {
3076
- key: "education",
3077
- kind: "array",
3078
- description: "\u5B78\u6B77\uFF0C\u53EF\u591A\u7B46",
3079
- fields: {
3080
- school_name: {
3081
- type: "string",
3082
- required: true,
3083
- minLength: 1,
3084
- maxLength: 80,
3085
- description: "\u5B78\u6821\u540D\u7A31",
3086
- example: "National Taiwan University"
3087
- },
3088
- degree_code: {
3089
- type: "enum",
3090
- required: true,
3091
- enumValues: DEGREE_TYPE_VALUES,
3092
- description: "\u5B78\u6B77\u7B49\u7D1A",
3093
- example: "bachelor"
3094
- },
3095
- department: {
3096
- type: "string",
3097
- required: true,
3098
- minLength: 1,
3099
- maxLength: 80,
3100
- description: "\u4E3B\u4FEE\u79D1\u7CFB\u540D\u7A31",
3101
- example: "Computer Science"
3102
- },
3103
- minor_department: {
3104
- type: "string",
3105
- required: false,
3106
- minLength: 1,
3107
- maxLength: 80,
3108
- description: "\u526F\u4FEE\u79D1\u7CFB\u540D\u7A31",
3109
- example: "Mathematics"
3110
- },
3111
- department_class_code: {
3112
- type: "string",
3113
- required: false,
3114
- description: "\u4E3B\u4FEE\u79D1\u7CFB\u985E\u5225\u4EE3\u78BC",
3115
- example: "engineering"
3116
- },
3117
- minor_department_class_code: {
3118
- type: "string",
3119
- required: false,
3120
- description: "\u526F\u4FEE\u79D1\u7CFB\u985E\u5225\u4EE3\u78BC",
3121
- example: "industry_machinery"
3122
- },
3123
- edu_status_code: {
3124
- type: "enum",
3125
- required: true,
3126
- enumValues: EDUCATION_STATUS_VALUES,
3127
- description: "\u5C31\u5B78\u72C0\u614B",
3128
- example: "studying"
3129
- },
3130
- start_year: {
3131
- type: "integer",
3132
- required: true,
3133
- min: 1900,
3134
- max: CURRENT_YEAR,
3135
- description: "\u5165\u5B78\u5E74\u4EFD",
3136
- example: 2015
3137
- },
3138
- start_month: {
3139
- type: "integer",
3140
- required: true,
3141
- min: 1,
3142
- max: 12,
3143
- description: "\u5165\u5B78\u6708\u4EFD",
3144
- example: 9
3145
- },
3146
- end_year: {
3147
- type: "integer",
3148
- required: false,
3149
- min: 1900,
3150
- max: CURRENT_YEAR,
3151
- description: "\u7562\u696D\u5E74\u4EFD\uFF08edu_status_code \u70BA graduated/dropped \u6642\u5FC5\u586B\uFF09",
3152
- example: 2019
3153
- },
3154
- end_month: {
3155
- type: "integer",
3156
- required: false,
3157
- min: 1,
3158
- max: 12,
3159
- description: "\u7562\u696D\u6708\u4EFD\uFF08edu_status_code \u70BA graduated/dropped \u6642\u5FC5\u586B\uFF09",
3160
- example: 6
3161
- },
3162
- experience: {
3163
- type: "string",
3164
- required: false,
3165
- htmlText: true,
3166
- maxLength: 2e3,
3167
- description: "\u5728\u6821\u7D93\u6B77\uFF08\u5BCC\u6587\u672C\uFF09",
3168
- example: "<p>Served as the president of the student council.</p>"
3169
- }
3170
- },
3171
- crossFieldRules: [
3172
- {
3173
- name: "education-graduation-date",
3174
- message: 'When edu_status_code is "graduated" or "dropped", end_year/end_month are required and the end date must be after start_year/start_month and not later than today.',
3175
- check(item) {
3176
- const status = item.edu_status_code;
3177
- if (status !== "graduated" && status !== "dropped") return true;
3178
- const { end_year, end_month, start_year, start_month } = item;
3179
- if (typeof end_year !== "number" || typeof end_month !== "number") return false;
3180
- if (typeof start_year !== "number" || typeof start_month !== "number") return true;
3181
- const start = new Date(start_year, start_month - 1);
3182
- const end = new Date(end_year, end_month - 1);
3183
- return end > start && end <= /* @__PURE__ */ new Date();
3184
- }
3185
- }
3186
- ]
3187
- };
3188
- var workExperienceSection = {
3189
- key: "work_experience",
3190
- kind: "wrapper",
3191
- description: "\u5DE5\u4F5C\u7D93\u9A57\uFF08\u5305\u88DD\u5C64\u542B\u300C\u7121\u5DE5\u4F5C\u7D93\u9A57\u300D\u65D7\u6A19\uFF09",
3192
- wrapperFields: {
3193
- has_no_work_experience: {
3194
- type: "boolean",
3195
- required: false,
3196
- description: "\u662F\u5426\u7121\u5DE5\u4F5C\u7D93\u9A57\uFF08true \u6642 items \u5FC5\u70BA\u7A7A\u9663\u5217\uFF09",
3197
- example: false
3198
- }
3199
- },
3200
- fields: {
3201
- job_title: {
3202
- type: "string",
3203
- required: true,
3204
- maxLength: 100,
3205
- description: "\u8077\u7A31",
3206
- example: "Software Engineer"
3207
- },
3208
- job_type: {
3209
- type: "enum",
3210
- required: true,
3211
- enumValues: JOB_FEATURE_CODE_VALUES,
3212
- description: "\u8077\u52D9\u985E\u578B",
3213
- example: "full_time"
3214
- },
3215
- company_name: {
3216
- type: "string",
3217
- required: true,
3218
- maxLength: 200,
3219
- description: "\u516C\u53F8\u540D\u7A31",
3220
- example: "Tech Corp"
3221
- },
3222
- start_year: {
3223
- type: "integer",
3224
- required: true,
3225
- min: 1900,
3226
- max: CURRENT_YEAR,
3227
- description: "\u4EFB\u8077\u958B\u59CB\u5E74\u4EFD",
3228
- example: 2020
3229
- },
3230
- start_month: {
3231
- type: "integer",
3232
- required: true,
3233
- min: 1,
3234
- max: 12,
3235
- description: "\u4EFB\u8077\u958B\u59CB\u6708\u4EFD",
3236
- example: 1
3237
- },
3238
- end_year: {
3239
- type: "integer",
3240
- required: false,
3241
- min: 1900,
3242
- max: CURRENT_YEAR,
3243
- description: "\u4EFB\u8077\u7D50\u675F\u5E74\u4EFD\uFF08is_current \u70BA false \u6642\u5FC5\u586B\uFF09",
3244
- example: 2022
3245
- },
3246
- end_month: {
3247
- type: "integer",
3248
- required: false,
3249
- min: 1,
3250
- max: 12,
3251
- description: "\u4EFB\u8077\u7D50\u675F\u6708\u4EFD\uFF08is_current \u70BA false \u6642\u5FC5\u586B\uFF09",
3252
- example: 12
3253
- },
3254
- is_current: {
3255
- type: "boolean",
3256
- required: true,
3257
- description: "\u662F\u5426\u5728\u8077\u4E2D\uFF08\u8B80\u5BEB\u4E0D\u5C0D\u7A31\uFF1A\u805A\u5408\u683C\u5F0F\u53D6 read shape \u70BA boolean\uFF0Corchestrator \u5BEB\u5165\u6642\u8F49 0/1\uFF09",
3258
- example: false
3259
- },
3260
- job_description: {
3261
- type: "string",
3262
- required: false,
3263
- htmlText: true,
3264
- maxLength: 1e4,
3265
- description: "\u5DE5\u4F5C\u5167\u5BB9\u63CF\u8FF0\uFF08\u5BCC\u6587\u672C\uFF09",
3266
- example: "<p>Built and maintained internal developer tools.</p>"
3267
- }
3268
- },
3269
- crossFieldRules: [
3270
- {
3271
- name: "work-experience-end-date",
3272
- message: "When is_current is true, end_year/end_month must not be present. When is_current is false, end_year/end_month are required and the end date must be on/after the start date and not later than today.",
3273
- check(item) {
3274
- if (typeof item.is_current !== "boolean") return true;
3275
- if (item.is_current) {
3276
- return item.end_year === void 0 && item.end_month === void 0;
3277
- }
3278
- const { end_year, end_month, start_year, start_month } = item;
3279
- if (typeof end_year !== "number" || typeof end_month !== "number") return false;
3280
- if (typeof start_year !== "number" || typeof start_month !== "number") return true;
3281
- const start = new Date(start_year, start_month - 1);
3282
- const end = new Date(end_year, end_month - 1);
3283
- return end >= start && end <= /* @__PURE__ */ new Date();
3284
- }
3285
- },
3286
- {
3287
- name: "work-experience-no-experience-items-empty",
3288
- message: "When has_no_work_experience is true, items must be an empty array.",
3289
- check(wrapper) {
3290
- if (wrapper.has_no_work_experience !== true) return true;
3291
- return Array.isArray(wrapper.items) && wrapper.items.length === 0;
3292
- }
3293
- }
3294
- ]
3295
- };
3296
- var certificateSection = {
3297
- key: "certificate",
3298
- kind: "array",
3299
- description: "\u8B49\u7167\uFF0C\u53EF\u591A\u7B46",
3300
- fields: {
3301
- certificate_code: {
3302
- type: "string",
3303
- required: true,
3304
- description: "\u8B49\u7167\u4EE3\u78BC",
3305
- example: "4001001014"
3306
- },
3307
- issue_year: {
3308
- type: "integer",
3309
- required: true,
3310
- min: 1900,
3311
- max: 2100,
3312
- description: "\u8D77\u59CB\u5E74\u4EFD\uFF08\u683C\u5F0F\uFF1AYYYY\uFF09",
3313
- example: 2023
3314
- },
3315
- issue_month: {
3316
- type: "integer",
3317
- required: true,
3318
- min: 1,
3319
- max: 12,
3320
- description: "\u8D77\u59CB\u6708\u4EFD\uFF08\u683C\u5F0F\uFF1AMM\uFF09",
3321
- example: 1
3322
- },
3323
- expiry_year: {
3324
- type: "integer",
3325
- required: false,
3326
- min: 1900,
3327
- max: 2100,
3328
- description: "\u5230\u671F\u5E74\u4EFD\uFF08is_permanent \u70BA false \u6642\u5FC5\u586B\uFF09",
3329
- example: 2025
3330
- },
3331
- expiry_month: {
3332
- type: "integer",
3333
- required: false,
3334
- min: 1,
3335
- max: 12,
3336
- description: "\u5230\u671F\u6708\u4EFD\uFF08is_permanent \u70BA false \u6642\u5FC5\u586B\uFF09",
3337
- example: 1
3338
- },
3339
- is_permanent: {
3340
- type: "boolean",
3341
- required: true,
3342
- description: "\u662F\u5426\u6C38\u4E45\u6709\u6548",
3343
- example: false
3344
- }
3345
- },
3346
- crossFieldRules: [
3347
- {
3348
- name: "certificate-expiry-required",
3349
- message: "When is_permanent is false, expiry_year/expiry_month are required and must not be earlier than issue_year/issue_month.",
3350
- check(item) {
3351
- if (item.is_permanent !== false) return true;
3352
- const { expiry_year, expiry_month, issue_year, issue_month } = item;
3353
- if (typeof expiry_year !== "number" || typeof expiry_month !== "number") return false;
3354
- if (typeof issue_year !== "number" || typeof issue_month !== "number") return true;
3355
- return expiry_year * 12 + expiry_month >= issue_year * 12 + issue_month;
3356
- }
3357
- }
3358
- ]
3359
- };
3360
- var languageSection = {
3361
- key: "language",
3362
- kind: "array",
3363
- description: "\u8A9E\u8A00\u80FD\u529B\uFF0C\u53EF\u591A\u7B46",
3364
- fields: {
3365
- code: {
3366
- type: "string",
3367
- required: true,
3368
- description: "\u6280\u80FD\u8A9E\u8A00\u4EE3\u78BC\uFF08\u5C0D\u61C9 skill_languages \u8CC7\u6599\u8868\u4E2D\u7684 code\uFF09",
3369
- example: "Chinese"
3370
- },
3371
- proficiency_level_code: {
3372
- type: "enum",
3373
- required: true,
3374
- enumValues: PROFICIENCY_LEVEL_VALUES,
3375
- description: "\u719F\u7DF4\u7A0B\u5EA6",
3376
- example: "proficient"
3377
- }
3378
- }
3379
- };
3380
- var professionalSkillsSection = {
3381
- key: "professional_skills",
3382
- kind: "object",
3383
- description: "\u5C08\u696D\u6280\u80FD",
3384
- fields: {
3385
- tech_tool_codes: {
3386
- type: "string[]",
3387
- required: true,
3388
- minItems: 1,
3389
- maxItems: 10,
3390
- description: "\u5DE5\u5177\u4EE3\u78BC\u9663\u5217\uFF08\u6700\u591A 10 \u500B\uFF09",
3391
- example: ["12001001045", "12001001044"]
3392
- },
3393
- other_tool_description: {
3394
- type: "string",
3395
- required: false,
3396
- htmlText: true,
3397
- maxLength: 2e3,
3398
- description: "\u5176\u4ED6\u64C5\u9577\u5DE5\u5177\u63CF\u8FF0\uFF08\u5BCC\u6587\u672C\uFF0C\u5BE6\u969B\u6587\u5B57\u4E0A\u9650 2000 \u5B57\uFF09",
3399
- example: "\u719F\u6089 Photoshop \u548C Illustrator"
3400
- },
3401
- job_skill_codes: {
3402
- type: "string[]",
3403
- required: true,
3404
- minItems: 1,
3405
- maxItems: 10,
3406
- description: "\u6280\u80FD\u4EE3\u78BC\u9663\u5217\uFF08\u6700\u591A 10 \u500B\uFF09",
3407
- example: ["5001001006", "5001001007"]
3408
- },
3409
- other_job_skill_description: {
3410
- type: "string",
3411
- required: false,
3412
- htmlText: true,
3413
- maxLength: 2e3,
3414
- description: "\u5176\u4ED6\u5DE5\u4F5C\u6280\u80FD\u63CF\u8FF0\uFF08\u5BCC\u6587\u672C\uFF0C\u5BE6\u969B\u6587\u5B57\u4E0A\u9650 2000 \u5B57\uFF09",
3415
- example: "\u719F\u6089 Scrum \u548C\u654F\u6377\u958B\u767C\u6D41\u7A0B"
3416
- }
3417
- }
3418
- };
3419
- var autobiographySection = {
3420
- key: "autobiography",
3421
- kind: "scalar",
3422
- description: "\u81EA\u50B3\uFF08\u7D14\u5B57\u4E32\u7BC0\uFF09",
3423
- valueDef: {
3424
- type: "string",
3425
- required: true,
3426
- htmlText: true,
3427
- maxLength: 3e3,
3428
- description: "\u81EA\u50B3\u5167\u5BB9\uFF08\u5BCC\u6587\u672C\u683C\u5F0F\uFF09\uFF0C\u5BE6\u969B\u6587\u5B57\u9577\u5EA6\u4E0D\u8D85\u904E 3000 \u5B57",
3429
- example: "<p>This is my <strong>autobiography</strong>.</p>"
3430
- }
3431
- };
3432
- var jobConditionSection = {
3433
- key: "job_condition",
3434
- kind: "object",
3435
- description: "\u5E0C\u671B\u5DE5\u4F5C\u689D\u4EF6",
3436
- fields: {
3437
- feature_codes: {
3438
- type: "enum[]",
3439
- required: true,
3440
- enumValues: JOB_FEATURE_CODE_VALUES,
3441
- description: "\u5E0C\u671B\u6027\u8CEA\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
3442
- example: ["full_time", "part_time"]
3443
- },
3444
- working_hour_type_codes: {
3445
- type: "enum[]",
3446
- required: true,
3447
- enumValues: WORKING_HOUR_TYPE_VALUES,
3448
- description: "\u4E0A\u73ED\u6642\u6BB5\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
3449
- example: ["day_shift", "evening_shift"]
3450
- },
3451
- available_start_type_code: {
3452
- type: "enum",
3453
- required: true,
3454
- enumValues: AVAILABLE_START_TYPE_VALUES,
3455
- description: "\u53EF\u4E0A\u73ED\u6642\u9593\u985E\u578B\u4EE3\u78BC",
3456
- example: "available_after_hired"
3457
- },
3458
- available_start_date: {
3459
- type: "date",
3460
- required: false,
3461
- description: "\u81EA\u8A02\u53EF\u4E0A\u73ED\u65E5\u671F\uFF08available_start_type_code \u70BA available_custom_date \u6642\u5FC5\u586B\uFF09",
3462
- example: "2025-05-01"
3463
- },
3464
- available_start_period_code: {
3465
- type: "enum",
3466
- required: false,
3467
- enumValues: AVAILABLE_START_PERIOD_VALUES,
3468
- description: "\u9304\u53D6\u5F8C\u671F\u9593\u4EE3\u78BC\uFF08available_start_type_code \u70BA available_after_hired \u6642\u5FC5\u586B\uFF09",
3469
- example: "week"
3470
- },
3471
- salary_expectation_type_code: {
3472
- type: "enum",
3473
- required: true,
3474
- enumValues: SALARY_EXPECTATION_TYPE_VALUES,
3475
- description: "\u5E0C\u671B\u5F85\u9047\u985E\u578B\u4EE3\u78BC",
3476
- example: "negotiable"
3477
- },
3478
- salary_unit_code: {
3479
- type: "enum",
3480
- required: false,
3481
- enumValues: SALARY_UNIT_VALUES,
3482
- description: "\u85AA\u8CC7\u55AE\u4F4D\u4EE3\u78BC\uFF08salary_expectation_type_code \u70BA custom \u6642\u5FC5\u586B\uFF09",
3483
- example: "monthly"
3484
- },
3485
- salary_amount: {
3486
- type: "number",
3487
- required: false,
3488
- min: 0,
3489
- description: "\u81EA\u8A02\u85AA\u8CC7\u91D1\u984D\uFF08salary_expectation_type_code \u70BA custom \u6642\u5FC5\u586B\uFF09",
3490
- example: 5e4
3491
- },
3492
- expected_salary_currency_code: {
3493
- type: "string",
3494
- required: false,
3495
- minLength: 3,
3496
- maxLength: 3,
3497
- description: "\u671F\u671B\u85AA\u8CC7\u5E63\u5225\u4EE3\u78BC\uFF08ISO 4217\uFF0Csalary_expectation_type_code \u70BA custom \u6642\u5FC5\u586B\uFF09",
3498
- example: "TWD"
3499
- },
3500
- area_codes: {
3501
- type: "string[]",
3502
- required: false,
3503
- description: "\u5E0C\u671B\u5730\u9EDE\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
3504
- example: ["6001005009", "6001006008"]
3505
- },
3506
- job_classification_codes: {
3507
- type: "string[]",
3508
- required: false,
3509
- description: "\u5E0C\u671B\u8077\u985E\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
3510
- example: ["2002002006", "2003002007"]
3511
- }
3512
- },
3513
- crossFieldRules: [
3514
- {
3515
- name: "job-condition-available-start",
3516
- message: 'When available_start_type_code is "available_custom_date", available_start_date is required. When it is "available_after_hired", available_start_period_code is required.',
3517
- check(item) {
3518
- const code = item.available_start_type_code;
3519
- if (code === "available_custom_date") {
3520
- const v = item.available_start_date;
3521
- return v !== void 0 && v !== null && v !== "";
3522
- }
3523
- if (code === "available_after_hired") {
3524
- const v = item.available_start_period_code;
3525
- return v !== void 0 && v !== null && v !== "";
3526
- }
3527
- return true;
3528
- }
3529
- },
3530
- {
3531
- name: "job-condition-salary-expectation",
3532
- message: 'When salary_expectation_type_code is "custom", salary_unit_code, salary_amount, and expected_salary_currency_code are all required.',
3533
- check(item) {
3534
- if (item.salary_expectation_type_code !== "custom") return true;
3535
- const hasUnit = item.salary_unit_code !== void 0 && item.salary_unit_code !== null && item.salary_unit_code !== "";
3536
- const hasAmount = typeof item.salary_amount === "number";
3537
- const hasCurrency = typeof item.expected_salary_currency_code === "string" && item.expected_salary_currency_code.length > 0;
3538
- return hasUnit && hasAmount && hasCurrency;
3539
- }
3540
- }
3541
- ]
3542
- };
3543
- var portfolioLinksSection = {
3544
- key: "portfolio_links",
3545
- kind: "array",
3546
- maxItems: 5,
3547
- description: "\u4F5C\u54C1\u96C6\u9023\u7D50\uFF0C\u6700\u591A 5 \u7B46",
3548
- fields: {
3549
- link_title: {
3550
- type: "string",
3551
- required: true,
3552
- minLength: 1,
3553
- maxLength: 50,
3554
- description: "\u4F5C\u54C1\u96C6\u6A19\u984C",
3555
- example: "My Portfolio"
3556
- },
3557
- link_url: {
3558
- type: "string",
3559
- required: true,
3560
- minLength: 1,
3561
- maxLength: 2083,
3562
- description: "\u4F5C\u54C1\u96C6\u9023\u7D50\uFF08URL\uFF09",
3563
- example: "https://myportfolio.com"
3564
- }
3565
- }
3566
- };
3567
- var backgroundSection = {
3568
- key: "background",
3569
- kind: "object",
3570
- description: "\u80CC\u666F\u8CC7\u8A0A\uFF08\u99D5\u7167\uFF0F\u8ECA\u8F1B\uFF0F\u5175\u5F79\u72C0\u614B\uFF09",
3571
- fields: {
3572
- // identity_types(身分種類)刻意不列:屬會員個資唯讀例外(pm_27 欄位),寫聚合不收,
3573
- // validate 對此鍵給 read-only 專屬訊息而非泛用 unknown-field(見 design doc §4.2)。
3574
- driving_license_types: {
3575
- type: "enum[]",
3576
- required: false,
3577
- enumValues: VEHICLE_OR_LICENSE_TYPE_VALUES,
3578
- description: "\u99D5\u99DB\u57F7\u7167\u985E\u578B\u5217\u8868",
3579
- example: ["scooter", "light_motorcycle"]
3580
- },
3581
- vehicle_types: {
3582
- type: "enum[]",
3583
- required: false,
3584
- enumValues: VEHICLE_OR_LICENSE_TYPE_VALUES,
3585
- description: "\u8ECA\u8F1B\u985E\u578B\u5217\u8868",
3586
- example: ["scooter"]
3587
- },
3588
- job_status: {
3589
- type: "enum",
3590
- required: false,
3591
- enumValues: JOB_STATUS_VALUES,
3592
- description: "\u5DE5\u4F5C\u72C0\u614B",
3593
- example: "employed"
3594
- }
3595
- }
3596
- };
3597
- var SECTIONS = [
3598
- educationSection,
3599
- workExperienceSection,
3600
- certificateSection,
3601
- languageSection,
3602
- professionalSkillsSection,
3603
- autobiographySection,
3604
- jobConditionSection,
3605
- portfolioLinksSection,
3606
- backgroundSection
3607
- ];
3608
- function getSection(key) {
3609
- return SECTIONS.find((s) => s.key === key);
3610
- }
3611
-
3612
- // src/lib/resume-schema/to-json-schema.ts
3613
- var JSON_SCHEMA_DRAFT = "http://json-schema.org/draft-07/schema#";
3614
- function fieldDefToJsonSchema(field) {
3615
- const out = {};
3616
- switch (field.type) {
3617
- case "string":
3618
- out.type = "string";
3619
- if (field.minLength !== void 0) out.minLength = field.minLength;
3620
- if (field.maxLength !== void 0) out.maxLength = field.maxLength;
3621
- break;
3622
- case "number":
3623
- out.type = "number";
3624
- if (field.min !== void 0) out.minimum = field.min;
3625
- if (field.max !== void 0) out.maximum = field.max;
3626
- break;
3627
- case "integer":
3628
- out.type = "integer";
3629
- if (field.min !== void 0) out.minimum = field.min;
3630
- if (field.max !== void 0) out.maximum = field.max;
3631
- break;
3632
- case "boolean":
3633
- out.type = "boolean";
3634
- break;
3635
- case "enum":
3636
- out.type = "string";
3637
- out.enum = field.enumValues ? [...field.enumValues] : [];
3638
- break;
3639
- case "string[]":
3640
- out.type = "array";
3641
- out.items = { type: "string" };
3642
- if (field.minItems !== void 0) out.minItems = field.minItems;
3643
- if (field.maxItems !== void 0) out.maxItems = field.maxItems;
3644
- break;
3645
- case "enum[]":
3646
- out.type = "array";
3647
- out.items = { type: "string", enum: field.enumValues ? [...field.enumValues] : [] };
3648
- if (field.minItems !== void 0) out.minItems = field.minItems;
3649
- if (field.maxItems !== void 0) out.maxItems = field.maxItems;
3650
- break;
3651
- case "date":
3652
- out.type = "string";
3653
- out.format = "date";
3654
- break;
3655
- }
3656
- out.description = field.htmlText ? `${field.description}(\u5B57\u6578\u4E0A\u9650\u8A08\u7B97\u65B9\u5F0F:\u5148\u525D\u9664 HTML tag,\u4EE5\u7D14\u6587\u5B57\u9577\u5EA6\u8A08\u7B97)` : field.description;
3657
- if (field.example !== void 0) out.example = field.example;
3658
- return out;
3659
- }
3660
- function fieldsToPropertiesAndRequired(fields) {
3661
- const properties = {};
3662
- const required = [];
3663
- for (const [key, field] of Object.entries(fields)) {
3664
- properties[key] = fieldDefToJsonSchema(field);
3665
- if (field.required) required.push(key);
3666
- }
3667
- return { properties, required };
3668
- }
3669
- function buildSectionSchema(section) {
3670
- if (section.kind === "scalar") {
3671
- return fieldDefToJsonSchema(section.valueDef);
3672
- }
3673
- if (section.kind === "object") {
3674
- const { properties, required } = fieldsToPropertiesAndRequired(section.fields);
3675
- return {
3676
- type: "object",
3677
- description: section.description,
3678
- additionalProperties: false,
3679
- required,
3680
- properties
3681
- };
3682
- }
3683
- if (section.kind === "array") {
3684
- const { properties, required } = fieldsToPropertiesAndRequired(section.fields);
3685
- const schema = {
3686
- type: "array",
3687
- description: section.description,
3688
- items: { type: "object", additionalProperties: false, required, properties }
3689
- };
3690
- if (section.maxItems !== void 0) schema.maxItems = section.maxItems;
3691
- return schema;
3692
- }
3693
- const wrapperParts = fieldsToPropertiesAndRequired(section.wrapperFields ?? {});
3694
- const itemParts = fieldsToPropertiesAndRequired(section.fields);
3695
- return {
3696
- type: "object",
3697
- description: section.description,
3698
- additionalProperties: false,
3699
- required: [...wrapperParts.required, "items"],
3700
- properties: {
3701
- ...wrapperParts.properties,
3702
- items: {
3703
- type: "array",
3704
- items: { type: "object", additionalProperties: false, required: itemParts.required, properties: itemParts.properties }
3705
- }
3706
- }
3707
- };
3708
- }
3709
- function buildAggregateJsonSchema(sectionKey) {
3710
- if (sectionKey !== void 0) {
3711
- const section = getSection(sectionKey);
3712
- if (!section) {
3713
- const allowed = SECTIONS.map((s) => s.key).join(", ");
3714
- throw new CliError(`Unknown section "${sectionKey}". Allowed: ${allowed}`, ExitCode.InvalidArgument);
3715
- }
3716
- return buildSectionSchema(section);
3717
- }
3718
- const properties = {
3719
- name: fieldDefToJsonSchema(TOP_LEVEL_FIELDS.name)
3720
- };
3721
- for (const section of SECTIONS) {
3722
- properties[section.key] = buildSectionSchema(section);
3723
- }
3724
- return {
3725
- $schema: JSON_SCHEMA_DRAFT,
3726
- title: "wport resume aggregate",
3727
- description: "wport \u5C65\u6B77\u805A\u5408\u683C\u5F0F(\u4F9B personal resumes create/update/validate \u4F7F\u7528)\u3002view \u8F38\u51FA\u53E6\u542B\u552F\u8B80\u7684 photo_url(\u6703\u54E1\u500B\u8CC7),\u4E0D\u5728\u6B64\u5BEB\u5165\u805A\u5408 schema \u5167,\u5982\u9700\u66F4\u65B0\u8ACB\u8D70 profile \u76F8\u95DC\u6D41\u7A0B\u3002",
3728
- type: "object",
3729
- additionalProperties: false,
3730
- required: ["name"],
3731
- properties
3732
- };
3733
- }
3734
-
3735
2940
  // src/commands/personal/resumes/schema.ts
2941
+ var import_core36 = require("@wport/core");
3736
2942
  function registerPersonalResumesSchema(parent) {
3737
2943
  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) => {
3738
- printJson(buildAggregateJsonSchema(opts.section));
3739
- });
3740
- }
3741
-
3742
- // src/lib/resume-schema/validate.ts
3743
- var TOP_LEVEL_READ_ONLY_KEYS = /* @__PURE__ */ new Set(["photo_url"]);
3744
- var BACKGROUND_READ_ONLY_KEYS = /* @__PURE__ */ new Set(["identity_types"]);
3745
- var NO_READ_ONLY_KEYS = /* @__PURE__ */ new Set();
3746
- function itemAllowedKeys(fields) {
3747
- return /* @__PURE__ */ new Set([...Object.keys(fields), "enc_id"]);
3748
- }
3749
- function isPlainObject(value) {
3750
- return typeof value === "object" && value !== null && !Array.isArray(value);
3751
- }
3752
- function describeType(value) {
3753
- if (value === null) return "null";
3754
- if (Array.isArray(value)) return "an array";
3755
- return typeof value;
3756
- }
3757
- function unknownFieldMessage(key) {
3758
- return `Unknown field "${key}" \u2014 not part of the resume schema`;
3759
- }
3760
- function readOnlyFieldMessage(key) {
3761
- return `Field "${key}" is read-only (member profile data) \u2014 not part of the resume write aggregate`;
3762
- }
3763
- function stripHtml2(s) {
3764
- return s.replace(/<[^>]*>/g, "");
3765
- }
3766
- function isValidDateString(s) {
3767
- if (!/^\d{4}-\d{2}-\d{2}$/.test(s)) return false;
3768
- const [y, m, d] = s.split("-").map(Number);
3769
- const date = new Date(Date.UTC(y, m - 1, d));
3770
- return date.getUTCFullYear() === y && date.getUTCMonth() === m - 1 && date.getUTCDate() === d;
3771
- }
3772
- function checkUnknownKeys(obj, allowedKeys, readOnlyKeys, pathFor, issues) {
3773
- for (const key of Object.keys(obj)) {
3774
- if (allowedKeys.has(key)) continue;
3775
- if (readOnlyKeys.has(key)) {
3776
- issues.push({ path: pathFor(key), message: readOnlyFieldMessage(key) });
3777
- continue;
3778
- }
3779
- issues.push({ path: pathFor(key), message: unknownFieldMessage(key) });
3780
- }
3781
- }
3782
- function checkFieldValue(field, value, path, issues) {
3783
- switch (field.type) {
3784
- case "string": {
3785
- if (typeof value !== "string") {
3786
- issues.push({ path, message: `Expected a string (got ${describeType(value)})` });
3787
- return;
3788
- }
3789
- const text = field.htmlText ? stripHtml2(value) : value;
3790
- if (field.minLength !== void 0 && text.length < field.minLength) {
3791
- issues.push({ path, message: `Must be at least ${field.minLength} characters (got ${text.length})` });
3792
- }
3793
- if (field.maxLength !== void 0 && text.length > field.maxLength) {
3794
- issues.push({ path, message: `Must be at most ${field.maxLength} characters (got ${text.length})` });
3795
- }
3796
- return;
3797
- }
3798
- case "number": {
3799
- if (typeof value !== "number" || Number.isNaN(value)) {
3800
- issues.push({ path, message: `Expected a number (got ${describeType(value)})` });
3801
- return;
3802
- }
3803
- checkRange(field, value, path, issues);
3804
- return;
3805
- }
3806
- case "integer": {
3807
- if (typeof value !== "number" || !Number.isInteger(value)) {
3808
- issues.push({ path, message: `Expected an integer (got ${describeType(value)})` });
3809
- return;
3810
- }
3811
- checkRange(field, value, path, issues);
3812
- return;
3813
- }
3814
- case "boolean": {
3815
- if (typeof value !== "boolean") issues.push({ path, message: `Expected a boolean (got ${describeType(value)})` });
3816
- return;
3817
- }
3818
- case "enum": {
3819
- const allowed = field.enumValues ?? [];
3820
- if (typeof value !== "string" || !allowed.includes(value)) {
3821
- issues.push({ path, message: `Invalid value ${JSON.stringify(value)} \u2014 allowed: ${allowed.join(", ")}` });
3822
- }
3823
- return;
3824
- }
3825
- case "string[]": {
3826
- if (!Array.isArray(value)) {
3827
- issues.push({ path, message: `Expected an array (got ${describeType(value)})` });
3828
- return;
3829
- }
3830
- value.forEach((v, i) => {
3831
- if (typeof v !== "string") issues.push({ path: `${path}[${i}]`, message: `Expected a string (got ${describeType(v)})` });
3832
- });
3833
- checkItemsCount(field, value, path, issues);
3834
- return;
3835
- }
3836
- case "enum[]": {
3837
- const allowed = field.enumValues ?? [];
3838
- if (!Array.isArray(value)) {
3839
- issues.push({ path, message: `Expected an array (got ${describeType(value)})` });
3840
- return;
3841
- }
3842
- value.forEach((v, i) => {
3843
- if (typeof v !== "string" || !allowed.includes(v)) {
3844
- issues.push({ path: `${path}[${i}]`, message: `Invalid value ${JSON.stringify(v)} \u2014 allowed: ${allowed.join(", ")}` });
3845
- }
3846
- });
3847
- checkItemsCount(field, value, path, issues);
3848
- return;
3849
- }
3850
- case "date": {
3851
- if (typeof value !== "string" || !isValidDateString(value)) {
3852
- issues.push({ path, message: `Expected a date string in YYYY-MM-DD format (got ${JSON.stringify(value)})` });
3853
- }
3854
- return;
3855
- }
3856
- }
3857
- }
3858
- function checkRange(field, value, path, issues) {
3859
- if (field.min !== void 0 && value < field.min) issues.push({ path, message: `Must be >= ${field.min} (got ${value})` });
3860
- if (field.max !== void 0 && value > field.max) issues.push({ path, message: `Must be <= ${field.max} (got ${value})` });
3861
- }
3862
- function checkItemsCount(field, value, path, issues) {
3863
- if (field.minItems !== void 0 && value.length < field.minItems) {
3864
- issues.push({ path, message: `Must have at least ${field.minItems} item(s) (got ${value.length})` });
3865
- }
3866
- if (field.maxItems !== void 0 && value.length > field.maxItems) {
3867
- issues.push({ path, message: `Must have at most ${field.maxItems} item(s) (got ${value.length})` });
3868
- }
3869
- }
3870
- function checkFields(obj, fields, pathFor, issues) {
3871
- for (const [key, field] of Object.entries(fields)) {
3872
- const value = obj[key];
3873
- const path = pathFor(key);
3874
- if (value === void 0) {
3875
- if (field.required) issues.push({ path, message: `Missing required field "${key}"` });
3876
- continue;
3877
- }
3878
- checkFieldValue(field, value, path, issues);
3879
- }
3880
- }
3881
- function checkCrossFieldRules(rules, item, path, issues) {
3882
- for (const rule of rules ?? []) {
3883
- if (!rule.check(item)) issues.push({ path, message: rule.message });
3884
- }
3885
- }
3886
- function validateArraySection(section, value, issues) {
3887
- const path = section.key;
3888
- if (!Array.isArray(value)) {
3889
- issues.push({ path, message: `Expected an array (got ${describeType(value)})` });
3890
- return;
3891
- }
3892
- if (section.maxItems !== void 0 && value.length > section.maxItems) {
3893
- issues.push({ path, message: `Must have at most ${section.maxItems} item(s) (got ${value.length})` });
3894
- }
3895
- value.forEach((item, i) => {
3896
- const itemPath = `${path}[${i}]`;
3897
- if (!isPlainObject(item)) {
3898
- issues.push({ path: itemPath, message: `Expected an object (got ${describeType(item)})` });
3899
- return;
3900
- }
3901
- checkUnknownKeys(item, itemAllowedKeys(section.fields), NO_READ_ONLY_KEYS, (k) => `${itemPath}.${k}`, issues);
3902
- checkFields(item, section.fields, (k) => `${itemPath}.${k}`, issues);
3903
- checkCrossFieldRules(section.crossFieldRules, item, itemPath, issues);
2944
+ printJson((0, import_core36.buildAggregateJsonSchema)(opts.section));
3904
2945
  });
3905
2946
  }
3906
- function validateObjectSection(section, value, issues) {
3907
- const path = section.key;
3908
- if (!isPlainObject(value)) {
3909
- issues.push({ path, message: `Expected an object (got ${describeType(value)})` });
3910
- return;
3911
- }
3912
- const readOnly = section.key === "background" ? BACKGROUND_READ_ONLY_KEYS : NO_READ_ONLY_KEYS;
3913
- checkUnknownKeys(value, new Set(Object.keys(section.fields)), readOnly, (k) => `${path}.${k}`, issues);
3914
- checkFields(value, section.fields, (k) => `${path}.${k}`, issues);
3915
- checkCrossFieldRules(section.crossFieldRules, value, path, issues);
3916
- }
3917
- function validateScalarSection(section, value, issues) {
3918
- checkFieldValue(section.valueDef, value, section.key, issues);
3919
- }
3920
- function validateWrapperSection(section, value, issues) {
3921
- const path = section.key;
3922
- if (!isPlainObject(value)) {
3923
- issues.push({ path, message: `Expected an object (got ${describeType(value)})` });
3924
- return;
3925
- }
3926
- const wrapperFields = section.wrapperFields ?? {};
3927
- const allowedTopKeys = /* @__PURE__ */ new Set([...Object.keys(wrapperFields), "items"]);
3928
- checkUnknownKeys(value, allowedTopKeys, NO_READ_ONLY_KEYS, (k) => `${path}.${k}`, issues);
3929
- checkFields(value, wrapperFields, (k) => `${path}.${k}`, issues);
3930
- const items = value.items;
3931
- const itemsPath = `${path}.items`;
3932
- if (items === void 0) {
3933
- issues.push({ path: itemsPath, message: 'Missing required field "items"' });
3934
- } else if (!Array.isArray(items)) {
3935
- issues.push({ path: itemsPath, message: `Expected an array (got ${describeType(items)})` });
3936
- } else {
3937
- items.forEach((item, i) => {
3938
- const itemPath = `${itemsPath}[${i}]`;
3939
- if (!isPlainObject(item)) {
3940
- issues.push({ path: itemPath, message: `Expected an object (got ${describeType(item)})` });
3941
- return;
3942
- }
3943
- checkUnknownKeys(item, itemAllowedKeys(section.fields), NO_READ_ONLY_KEYS, (k) => `${itemPath}.${k}`, issues);
3944
- checkFields(item, section.fields, (k) => `${itemPath}.${k}`, issues);
3945
- checkCrossFieldRules(section.crossFieldRules, item, itemPath, issues);
3946
- });
3947
- }
3948
- checkCrossFieldRules(section.crossFieldRules, value, path, issues);
3949
- }
3950
- function validateAggregate(input) {
3951
- if (!isPlainObject(input)) {
3952
- return [{ path: "(root)", message: `Input must be a JSON object at the top level (got ${describeType(input)})` }];
3953
- }
3954
- const issues = [];
3955
- const allowedTopKeys = /* @__PURE__ */ new Set(["name", ...SECTIONS.map((s) => s.key)]);
3956
- checkUnknownKeys(input, allowedTopKeys, TOP_LEVEL_READ_ONLY_KEYS, (k) => k, issues);
3957
- checkFields(input, TOP_LEVEL_FIELDS, (k) => k, issues);
3958
- for (const section of SECTIONS) {
3959
- const value = input[section.key];
3960
- if (value === void 0) continue;
3961
- switch (section.kind) {
3962
- case "array":
3963
- validateArraySection(section, value, issues);
3964
- break;
3965
- case "object":
3966
- validateObjectSection(section, value, issues);
3967
- break;
3968
- case "scalar":
3969
- validateScalarSection(section, value, issues);
3970
- break;
3971
- case "wrapper":
3972
- validateWrapperSection(section, value, issues);
3973
- break;
3974
- }
3975
- }
3976
- return issues;
3977
- }
3978
2947
 
3979
2948
  // src/commands/personal/resumes/validate.ts
2949
+ var import_core37 = require("@wport/core");
3980
2950
  function runResumesValidate(ctx, filePath) {
3981
2951
  const input = readJsonInput(filePath, { timeoutMs: ctx.timeoutMs });
3982
- const issues = validateAggregate(input);
2952
+ const issues = (0, import_core37.validateAggregate)(input);
3983
2953
  if (issues.length === 0) {
3984
2954
  if (ctx.format === "json") {
3985
2955
  printJson({ valid: true });
@@ -4007,43 +2977,9 @@ function registerPersonalResumesValidate(parent) {
4007
2977
 
4008
2978
  // src/commands/personal/resumes/template.ts
4009
2979
  var import_node_fs8 = require("fs");
4010
-
4011
- // src/lib/resume-schema/template.ts
4012
- function buildFieldsExample(fields) {
4013
- const out = {};
4014
- for (const [key, field] of Object.entries(fields)) {
4015
- if (field.example !== void 0) out[key] = field.example;
4016
- }
4017
- return out;
4018
- }
4019
- function buildSectionTemplate(section) {
4020
- switch (section.kind) {
4021
- case "scalar":
4022
- return section.valueDef.example;
4023
- case "object":
4024
- return buildFieldsExample(section.fields);
4025
- case "array":
4026
- return [buildFieldsExample(section.fields)];
4027
- case "wrapper":
4028
- return {
4029
- ...buildFieldsExample(section.wrapperFields ?? {}),
4030
- items: [buildFieldsExample(section.fields)]
4031
- };
4032
- }
4033
- }
4034
- function buildTemplate() {
4035
- const template = {
4036
- name: TOP_LEVEL_FIELDS.name.example
4037
- };
4038
- for (const section of SECTIONS) {
4039
- template[section.key] = buildSectionTemplate(section);
4040
- }
4041
- return template;
4042
- }
4043
-
4044
- // src/commands/personal/resumes/template.ts
2980
+ var import_core38 = require("@wport/core");
4045
2981
  function runResumesTemplate(outPath) {
4046
- const template = buildTemplate();
2982
+ const template = (0, import_core38.buildTemplate)();
4047
2983
  if (outPath === void 0) {
4048
2984
  printJson(template);
4049
2985
  return;
@@ -4067,6 +3003,8 @@ function registerPersonalResumesTemplate(parent) {
4067
3003
  }
4068
3004
 
4069
3005
  // src/commands/personal/resumes/list.ts
3006
+ var import_core39 = require("@wport/core");
3007
+ var import_core40 = require("@wport/core");
4070
3008
  var MINIMAL_LIST_FIELDS4 = ["enc_id", "name", "updated_at", "is_complete", "is_published"];
4071
3009
  function formatDate6(value) {
4072
3010
  return value ? String(value).slice(0, 10) : "";
@@ -4076,8 +3014,8 @@ function formatQuotaLine(quota) {
4076
3014
  return `${quota.used}/${quota.max_resumes} used, ${status}.`;
4077
3015
  }
4078
3016
  async function fetchResumeList(opts) {
4079
- const { body } = await personalGet(opts, PERSONAL_RESUMES_BASE);
4080
- return unwrapDataResponse(body);
3017
+ const { body } = await personalGet(opts, import_core40.PERSONAL_RESUMES_BASE);
3018
+ return (0, import_core39.unwrapDataResponse)(body);
4081
3019
  }
4082
3020
  async function runResumesList(ctx, flags) {
4083
3021
  if (flags.fields && flags.minimal) {
@@ -4110,17 +3048,20 @@ function registerPersonalResumesList(parent) {
4110
3048
  }
4111
3049
 
4112
3050
  // src/commands/personal/resumes/view.ts
3051
+ var import_core41 = require("@wport/core");
3052
+ var import_core42 = require("@wport/core");
3053
+ var import_core43 = require("@wport/core");
4113
3054
  async function fetchResumeAggregate(opts, encId) {
4114
3055
  const trimmed = encId.trim();
4115
3056
  if (!trimmed) {
4116
3057
  throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
4117
3058
  }
4118
- const { body } = await personalGet(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
4119
- return unwrapDataResponse(body);
3059
+ const { body } = await personalGet(opts, `${import_core42.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
3060
+ return (0, import_core41.unwrapDataResponse)(body);
4120
3061
  }
4121
3062
  function summarizeSections(resume) {
4122
3063
  const rows = [{ section: "name", summary: resume.name }];
4123
- for (const def of SECTIONS) {
3064
+ for (const def of import_core43.SECTIONS) {
4124
3065
  rows.push({ section: def.key, summary: summarizeSection(def, resume) });
4125
3066
  }
4126
3067
  return rows;
@@ -4197,8 +3138,14 @@ function registerPersonalResumesExport(parent) {
4197
3138
  });
4198
3139
  }
4199
3140
 
3141
+ // src/commands/personal/resumes/create.ts
3142
+ var import_core47 = require("@wport/core");
3143
+
4200
3144
  // src/lib/resume-orchestrator.ts
4201
3145
  var import_node_crypto11 = require("crypto");
3146
+ var import_core44 = require("@wport/core");
3147
+ var import_core45 = require("@wport/core");
3148
+ var import_core46 = require("@wport/core");
4202
3149
  function freshIdempotencyKey() {
4203
3150
  return { idempotencyKey: (0, import_node_crypto11.randomUUID)() };
4204
3151
  }
@@ -4213,8 +3160,8 @@ function extractErrorCode(body) {
4213
3160
  }
4214
3161
  async function createShell(opts) {
4215
3162
  try {
4216
- const { body } = await personalPost(opts, PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());
4217
- return unwrapDataResponse(body).enc_id;
3163
+ const { body } = await personalPost(opts, import_core45.PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());
3164
+ return (0, import_core44.unwrapDataResponse)(body).enc_id;
4218
3165
  } catch (err) {
4219
3166
  if (err instanceof ServerClientHttpError) {
4220
3167
  const code = extractErrorCode(err.body);
@@ -4235,7 +3182,7 @@ async function createShell(opts) {
4235
3182
  }
4236
3183
  }
4237
3184
  async function renameResume(opts, encId, name) {
4238
- await personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name }, freshIdempotencyKey());
3185
+ await personalPut(opts, `${import_core45.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name }, freshIdempotencyKey());
4239
3186
  }
4240
3187
  function toWorkExperienceWriteItem(item) {
4241
3188
  const { is_current, ...rest } = item;
@@ -4247,8 +3194,8 @@ function splitItemEncId(item) {
4247
3194
  return [valid, rest];
4248
3195
  }
4249
3196
  async function writeSection(opts, encId, key, value, mode) {
4250
- const plan = SECTION_WRITE_PLAN[key];
4251
- const base = `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;
3197
+ const plan = import_core45.SECTION_WRITE_PLAN[key];
3198
+ const base = `${import_core45.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;
4252
3199
  switch (plan.kind) {
4253
3200
  case "per-item-post": {
4254
3201
  for (const item of value) {
@@ -4315,7 +3262,7 @@ async function createAggregate(opts, aggregate) {
4315
3262
  if (aggregate.name !== void 0) {
4316
3263
  reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
4317
3264
  }
4318
- for (const section of SECTIONS) {
3265
+ for (const section of import_core46.SECTIONS) {
4319
3266
  const value = aggregate[section.key];
4320
3267
  if (value === void 0) continue;
4321
3268
  reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "create")));
@@ -4325,9 +3272,9 @@ async function createAggregate(opts, aggregate) {
4325
3272
  async function updateAggregate(opts, encId, aggregate, onlySection) {
4326
3273
  const reports = [];
4327
3274
  if (onlySection !== void 0) {
4328
- const section = getSection(onlySection);
3275
+ const section = (0, import_core46.getSection)(onlySection);
4329
3276
  if (!section) {
4330
- const allowed = SECTIONS.map((s) => s.key).join(", ");
3277
+ const allowed = import_core46.SECTIONS.map((s) => s.key).join(", ");
4331
3278
  throw new CliError(`Unknown section "${onlySection}". Allowed: ${allowed}`, ExitCode.InvalidArgument);
4332
3279
  }
4333
3280
  const value = aggregate[onlySection];
@@ -4340,7 +3287,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
4340
3287
  if (aggregate.name !== void 0) {
4341
3288
  reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
4342
3289
  }
4343
- for (const section of SECTIONS) {
3290
+ for (const section of import_core46.SECTIONS) {
4344
3291
  const value = aggregate[section.key];
4345
3292
  if (value === void 0) continue;
4346
3293
  reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "update")));
@@ -4351,7 +3298,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
4351
3298
  // src/commands/personal/resumes/create.ts
4352
3299
  async function runResumesCreate(ctx, filePath) {
4353
3300
  const input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });
4354
- const issues = validateAggregate(input);
3301
+ const issues = (0, import_core47.validateAggregate)(input);
4355
3302
  if (issues.length > 0) {
4356
3303
  if (ctx.format === "json") {
4357
3304
  printJson({ valid: false, issues });
@@ -4444,6 +3391,8 @@ function registerPersonalResumesUpdate(parent) {
4444
3391
 
4445
3392
  // src/commands/personal/resumes/copy.ts
4446
3393
  var import_node_crypto12 = require("crypto");
3394
+ var import_core48 = require("@wport/core");
3395
+ var import_core49 = require("@wport/core");
4447
3396
  function requireEncId3(encId) {
4448
3397
  const trimmed = encId.trim();
4449
3398
  if (!trimmed) {
@@ -4462,12 +3411,12 @@ function printCopyResult(ctx, sourceEncId, newEncId) {
4462
3411
  async function runResumesCopy(ctx, encId, name) {
4463
3412
  const trimmed = requireEncId3(encId);
4464
3413
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
4465
- const { body } = await personalPost(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/duplicate`, {}, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
4466
- const { enc_id: newEncId } = unwrapDataResponse(body);
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);
4467
3416
  printCopyResult(ctx, trimmed, newEncId);
4468
3417
  if (name === void 0) return;
4469
3418
  try {
4470
- await personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name }, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
3419
+ await personalPut(opts, `${import_core49.PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name }, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
4471
3420
  } catch (err) {
4472
3421
  const reason = err instanceof Error ? err.message : String(err);
4473
3422
  throw new CliError(
@@ -4484,6 +3433,8 @@ function registerPersonalResumesCopy(parent) {
4484
3433
 
4485
3434
  // src/commands/personal/resumes/publish.ts
4486
3435
  var import_node_crypto13 = require("crypto");
3436
+ var import_core50 = require("@wport/core");
3437
+ var import_core51 = require("@wport/core");
4487
3438
  function requireEncId4(encId) {
4488
3439
  const trimmed = encId.trim();
4489
3440
  if (!trimmed) {
@@ -4497,11 +3448,11 @@ async function runResumesPublishTransition(ctx, encId, action) {
4497
3448
  const targetStatus = action === "publish";
4498
3449
  const { body } = await personalPatch(
4499
3450
  opts,
4500
- `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
3451
+ `${import_core51.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
4501
3452
  { target_status: targetStatus },
4502
3453
  { idempotencyKey: (0, import_node_crypto13.randomUUID)() }
4503
3454
  );
4504
- const result = unwrapDataResponse(body);
3455
+ const result = (0, import_core50.unwrapDataResponse)(body);
4505
3456
  if (ctx.format === "json") {
4506
3457
  printJson({ enc_id: trimmed, is_published: result.is_published });
4507
3458
  return;
@@ -4523,6 +3474,7 @@ function registerPersonalResumesUnpublish(parent) {
4523
3474
 
4524
3475
  // src/commands/personal/resumes/delete.ts
4525
3476
  var import_node_crypto14 = require("crypto");
3477
+ var import_core52 = require("@wport/core");
4526
3478
  function requireEncId5(encId) {
4527
3479
  const trimmed = encId.trim();
4528
3480
  if (!trimmed) {
@@ -4536,7 +3488,7 @@ async function runResumesDelete(ctx, encId, confirm) {
4536
3488
  }
4537
3489
  const trimmed = requireEncId5(encId);
4538
3490
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
4539
- await personalDelete(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`, { idempotencyKey: (0, import_node_crypto14.randomUUID)() });
3491
+ await personalDelete(opts, `${import_core52.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`, { idempotencyKey: (0, import_node_crypto14.randomUUID)() });
4540
3492
  if (ctx.format === "json") {
4541
3493
  printJson({ enc_id: trimmed, deleted: true });
4542
3494
  return;
@@ -4570,6 +3522,7 @@ function registerPersonalResumesCommand(parent) {
4570
3522
  // src/commands/personal/apply/index.ts
4571
3523
  var import_node_crypto15 = require("crypto");
4572
3524
  var import_node_fs11 = require("fs");
3525
+ var import_core53 = require("@wport/core");
4573
3526
 
4574
3527
  // src/commands/personal/apply/message-input.ts
4575
3528
  var import_node_fs10 = require("fs");
@@ -4651,7 +3604,7 @@ async function runPersonalApply(ctx, args) {
4651
3604
  const body = { enc_job_id: args.encJobId, enc_resume_id: args.encResumeId, application_message: args.message };
4652
3605
  let result;
4653
3606
  try {
4654
- result = await personalPost(opts, PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
3607
+ result = await personalPost(opts, import_core53.PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
4655
3608
  } catch (err) {
4656
3609
  rethrowApplyError(err);
4657
3610
  }
@@ -4702,7 +3655,7 @@ async function runPersonalApplyBatch(ctx, args) {
4702
3655
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
4703
3656
  let result;
4704
3657
  try {
4705
- result = await personalPost(opts, `${PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
3658
+ result = await personalPost(opts, `${import_core53.PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
4706
3659
  } catch (err) {
4707
3660
  rethrowApplyError(err);
4708
3661
  }
@@ -4730,7 +3683,7 @@ function registerPersonalCommand(program2) {
4730
3683
 
4731
3684
  // src/index.ts
4732
3685
  var program = new import_commander.Command();
4733
- program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.9.1-dev.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));
3686
+ program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.9.2", "-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));
4734
3687
  registerJobsCommand(program);
4735
3688
  registerConfigCommand(program);
4736
3689
  registerDoctorCommand(program);
@@ -4756,6 +3709,10 @@ function handleTopLevelError(err) {
4756
3709
  printError(err.message, color);
4757
3710
  process.exit(err.exitCode);
4758
3711
  }
3712
+ if ((0, import_core.isWportError)(err)) {
3713
+ printError(err.message, color);
3714
+ process.exit(exitCodeForError(err));
3715
+ }
4759
3716
  const fallbackMessage = err instanceof Error ? err.message : String(err);
4760
3717
  printError(fallbackMessage, color);
4761
3718
  process.exit(ExitCode.ServerOrNetworkError);