@wport/cli 0.9.1-dev.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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;
@@ -478,16 +382,17 @@ function registerJobsSearch(parent) {
478
382
  const ctx = resolveContext(command);
479
383
  const fields = resolveSearchFields(flags);
480
384
  const query = buildQuery(flags);
481
- const client = createApiClient({
385
+ const client = (0, import_core2.createApiClient)({
482
386
  baseUrl: ctx.baseUrl,
483
387
  locale: ctx.locale,
484
- timeoutMs: ctx.timeoutMs
388
+ timeoutMs: ctx.timeoutMs,
389
+ userAgent: (0, import_core2.buildUserAgent)("wport-cli", "0.9.1")
485
390
  });
486
391
  const { data, error, response } = await client.GET("/api/jobs/search", {
487
392
  params: { query }
488
393
  });
489
- if (!response.ok) throwForHttpStatus(response.status, error);
490
- const paged = asPaginatedBody(data);
394
+ if (!response.ok) (0, import_core2.throwForHttpStatus)(response.status, error);
395
+ const paged = (0, import_core2.asPaginatedBody)(data);
491
396
  if (ctx.format === "json") {
492
397
  const body = fields ? { ...paged, data: paged.data.map((item) => pickPaths(item, fields)) } : paged;
493
398
  printJson(body);
@@ -564,21 +469,9 @@ function formatDate(s) {
564
469
  return m ? m[1] : s;
565
470
  }
566
471
 
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
- }
472
+ // src/commands/jobs/view.ts
473
+ var import_core3 = require("@wport/core");
474
+ var import_core4 = require("@wport/core");
582
475
 
583
476
  // src/lib/io-helpers.ts
584
477
  var import_node_fs3 = require("fs");
@@ -733,10 +626,11 @@ function registerJobsView(parent) {
733
626
  ExitCode.InvalidArgument
734
627
  );
735
628
  }
736
- const client = createApiClient({
629
+ const client = (0, import_core3.createApiClient)({
737
630
  baseUrl: ctx.baseUrl,
738
631
  locale: ctx.locale,
739
- timeoutMs: ctx.timeoutMs
632
+ timeoutMs: ctx.timeoutMs,
633
+ userAgent: (0, import_core3.buildUserAgent)("wport-cli", "0.9.1")
740
634
  });
741
635
  if (flags.batch) {
742
636
  await runBatchView(encIdArg, flags, client, ctx.timeoutMs);
@@ -749,8 +643,8 @@ function registerJobsView(parent) {
749
643
  const { data, error, response } = await client.GET("/api/jobs/{encId}/view", {
750
644
  params: { path: { encId } }
751
645
  });
752
- if (!response.ok) throwForHttpStatus(response.status, error);
753
- const job = unwrapDataResponse(data);
646
+ if (!response.ok) (0, import_core3.throwForHttpStatus)(response.status, error);
647
+ const job = (0, import_core3.unwrapDataResponse)(data);
754
648
  if (flags.fields) {
755
649
  printJson(pickPaths(job, parseFieldsList(flags.fields)));
756
650
  return;
@@ -788,11 +682,11 @@ async function fetchJob(client, encId) {
788
682
  const { data, error, response } = await client.GET("/api/jobs/{encId}/view", {
789
683
  params: { path: { encId } }
790
684
  });
791
- if (!response.ok) throwForHttpStatus(response.status, error);
792
- return unwrapDataResponse(data);
685
+ if (!response.ok) (0, import_core3.throwForHttpStatus)(response.status, error);
686
+ return (0, import_core3.unwrapDataResponse)(data);
793
687
  }
794
688
  async function runBatch(encIds, concurrency, fetchOne, project) {
795
- return mapWithConcurrency(encIds, concurrency, async (encId) => {
689
+ return (0, import_core4.mapWithConcurrency)(encIds, concurrency, async (encId) => {
796
690
  try {
797
691
  const job = await fetchOne(encId);
798
692
  return { enc_id: encId, ok: true, data: project(job) };
@@ -989,6 +883,7 @@ function registerConfigCommand(program2) {
989
883
 
990
884
  // src/commands/doctor.ts
991
885
  var import_node_fs6 = require("fs");
886
+ var import_core7 = require("@wport/core");
992
887
 
993
888
  // src/lib/credentials-store.ts
994
889
  var import_node_fs5 = require("fs");
@@ -1165,24 +1060,9 @@ function ensureFormat(key, source) {
1165
1060
  }
1166
1061
  }
1167
1062
 
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
1063
  // src/lib/oauth.ts
1064
+ var import_core5 = require("@wport/core");
1065
+ var import_core6 = require("@wport/core");
1186
1066
  var EXPIRED_MESSAGE = "The device code expired before authorization completed. Run `wport login` to try again.";
1187
1067
  async function oauthPost(opts, path, body) {
1188
1068
  const url = new URL(`${opts.baseUrl}${path}`);
@@ -1190,13 +1070,13 @@ async function oauthPost(opts, path, body) {
1190
1070
  method: "POST",
1191
1071
  headers: {
1192
1072
  "Accept-Language": opts.locale,
1193
- "User-Agent": buildUserAgent(),
1073
+ "User-Agent": (0, import_core5.buildUserAgent)("wport-cli", "0.9.1"),
1194
1074
  Accept: "application/json",
1195
1075
  "Content-Type": "application/json"
1196
1076
  },
1197
1077
  body: JSON.stringify(body)
1198
1078
  });
1199
- const res = await fetchWithTimeout(request, opts.timeoutMs);
1079
+ const res = await (0, import_core5.fetchWithTimeout)(request, opts.timeoutMs);
1200
1080
  const respBody = await res.json().catch(() => null);
1201
1081
  return { status: res.status, body: respBody };
1202
1082
  }
@@ -1209,15 +1089,15 @@ function oauthErrorCode(body) {
1209
1089
  async function requestDeviceCode(opts, deviceName) {
1210
1090
  const body = {};
1211
1091
  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);
1092
+ const { status, body: respBody } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/device/code`, body);
1093
+ if (status < 200 || status >= 300) (0, import_core5.throwForHttpStatus)(status, respBody);
1214
1094
  return respBody;
1215
1095
  }
1216
1096
  async function pollForToken(opts, device, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
1217
1097
  let intervalSec = device.interval;
1218
1098
  const deadline = Date.now() + device.expires_in * 1e3;
1219
1099
  for (; ; ) {
1220
- const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {
1100
+ const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/token`, {
1221
1101
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1222
1102
  device_code: device.device_code
1223
1103
  });
@@ -1230,14 +1110,14 @@ async function pollForToken(opts, device, sleep = (ms) => new Promise((resolve)
1230
1110
  } else if (code === "expired_token") {
1231
1111
  throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);
1232
1112
  } else if (code !== "authorization_pending") {
1233
- throwForHttpStatus(status, body);
1113
+ (0, import_core5.throwForHttpStatus)(status, body);
1234
1114
  }
1235
1115
  if (Date.now() >= deadline) throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);
1236
1116
  await sleep(intervalSec * 1e3);
1237
1117
  }
1238
1118
  }
1239
1119
  async function refreshAccessToken(opts, refreshToken) {
1240
- const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {
1120
+ const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/token`, {
1241
1121
  grant_type: "refresh_token",
1242
1122
  refresh_token: refreshToken
1243
1123
  });
@@ -1245,11 +1125,11 @@ async function refreshAccessToken(opts, refreshToken) {
1245
1125
  if (oauthErrorCode(body) === "invalid_grant") {
1246
1126
  throw new CliError("Your session is no longer valid. Run `wport login` to sign in again.", ExitCode.ServerClientError);
1247
1127
  }
1248
- throwForHttpStatus(status, body);
1128
+ (0, import_core5.throwForHttpStatus)(status, body);
1249
1129
  }
1250
1130
  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);
1131
+ const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/revoke`, { token: refreshToken });
1132
+ if (status < 200 || status >= 300) (0, import_core5.throwForHttpStatus)(status, body);
1253
1133
  }
1254
1134
 
1255
1135
  // src/commands/doctor.ts
@@ -1272,7 +1152,7 @@ function registerDoctorCommand(program2) {
1272
1152
  }
1273
1153
  async function runDoctor(ctx) {
1274
1154
  const line = (s = "") => process.stdout.write(s + "\n");
1275
- line(`wport-cli ${"0.9.1-dev.0"}`);
1155
+ line(`wport-cli ${"0.9.1"}`);
1276
1156
  line(` bundled schema fingerprint: ${"839e8a891dfb"}`);
1277
1157
  line("");
1278
1158
  line("Resolved configuration:");
@@ -1328,7 +1208,12 @@ function describePersonalLoginLines() {
1328
1208
  }
1329
1209
  async function probeServer(ctx, line) {
1330
1210
  try {
1331
- const client = createApiClient({ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs });
1211
+ const client = (0, import_core7.createApiClient)({
1212
+ baseUrl: ctx.baseUrl,
1213
+ locale: ctx.locale,
1214
+ timeoutMs: ctx.timeoutMs,
1215
+ userAgent: (0, import_core7.buildUserAgent)("wport-cli", "0.9.1")
1216
+ });
1332
1217
  const { response } = await client.GET("/api/jobs/search", { params: { query: { pageSize: 1 } } });
1333
1218
  if (response.ok) {
1334
1219
  line(` \u2713 reachable (HTTP ${response.status})`);
@@ -1353,81 +1238,39 @@ async function probeAuthServer(ctx, line) {
1353
1238
  }
1354
1239
 
1355
1240
  // 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(
1241
+ var import_core8 = require("@wport/core");
1242
+ var transport = __toESM(require("@wport/core"));
1243
+ function withUserAgent(opts) {
1244
+ return { ...opts, userAgent: (0, import_core8.buildUserAgent)("wport-cli", "0.9.1") };
1245
+ }
1246
+ function decorateEnterpriseError(err) {
1247
+ if (!(err instanceof import_core8.WportHttpError)) return err;
1248
+ const base = err.message;
1249
+ if (err.status === 401) {
1250
+ return new import_core8.WportHttpError(
1411
1251
  `${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
1252
+ err.status,
1253
+ err.body
1413
1254
  );
1414
1255
  }
1415
- if (status === 403) {
1416
- throw new CliError(
1256
+ if (err.status === 403) {
1257
+ return new import_core8.WportHttpError(
1417
1258
  `${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
1259
+ err.status,
1260
+ err.body
1419
1261
  );
1420
1262
  }
1421
- if (status === 400) {
1422
- const missingFields = extractMissingFields(body);
1263
+ if (err.status === 400) {
1264
+ const missingFields = extractMissingFields(err.body);
1423
1265
  if (missingFields.length > 0) {
1424
- throw new CliError(
1266
+ return new import_core8.WportHttpError(
1425
1267
  `${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
1268
+ err.status,
1269
+ err.body
1427
1270
  );
1428
1271
  }
1429
1272
  }
1430
- throwForHttpStatus(status, body);
1273
+ return err;
1431
1274
  }
1432
1275
  function extractMissingFields(body) {
1433
1276
  if (!body || typeof body !== "object") return [];
@@ -1444,6 +1287,28 @@ function warnIfRateLimitLow(headers) {
1444
1287
  printWarn(`Rate limit nearly exhausted: ${remaining}/${limit} requests remaining this window.`, false);
1445
1288
  }
1446
1289
  }
1290
+ async function wrap(p) {
1291
+ let res;
1292
+ try {
1293
+ res = await p;
1294
+ } catch (err) {
1295
+ throw decorateEnterpriseError(err);
1296
+ }
1297
+ warnIfRateLimitLow(res.headers);
1298
+ return res;
1299
+ }
1300
+ function enterpriseGet2(opts, path, query) {
1301
+ return wrap(transport.enterpriseGet(withUserAgent(opts), path, query));
1302
+ }
1303
+ function enterprisePost2(opts, path, body, extra) {
1304
+ return wrap(transport.enterprisePost(withUserAgent(opts), path, body, extra));
1305
+ }
1306
+ function enterprisePatch2(opts, path, body, extra) {
1307
+ return wrap(transport.enterprisePatch(withUserAgent(opts), path, body, extra));
1308
+ }
1309
+ function enterpriseDelete2(opts, path, extra) {
1310
+ return wrap(transport.enterpriseDelete(withUserAgent(opts), path, extra));
1311
+ }
1447
1312
 
1448
1313
  // src/commands/enterprise/login.ts
1449
1314
  async function performLogin(ctx, key) {
@@ -1455,7 +1320,7 @@ async function performLogin(ctx, key) {
1455
1320
  ExitCode.InvalidArgument
1456
1321
  );
1457
1322
  }
1458
- const { body } = await enterpriseGet({ ...ctx, apiKey: key }, "/me");
1323
+ const { body } = await enterpriseGet2({ ...ctx, apiKey: key }, "/me");
1459
1324
  saveCredentials({
1460
1325
  api_key: key,
1461
1326
  company_name: extractCompanyName(body),
@@ -1520,15 +1385,16 @@ function registerEnterpriseWhoami(parent) {
1520
1385
  }
1521
1386
 
1522
1387
  // src/commands/enterprise/usage.ts
1388
+ var import_core9 = require("@wport/core");
1523
1389
  function num(value) {
1524
1390
  return typeof value === "number" && Number.isFinite(value) ? String(value) : "\u2014";
1525
1391
  }
1526
1392
  async function runUsage(ctx, apiKey) {
1527
- const { body } = await enterpriseGet(
1393
+ const { body } = await enterpriseGet2(
1528
1394
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1529
1395
  "/usage"
1530
1396
  );
1531
- const usage = unwrapDataResponse(body);
1397
+ const usage = (0, import_core9.unwrapDataResponse)(body);
1532
1398
  if (ctx.format === "json") {
1533
1399
  printJson(usage);
1534
1400
  return;
@@ -1555,6 +1421,7 @@ function registerEnterpriseUsage(parent) {
1555
1421
  }
1556
1422
 
1557
1423
  // src/commands/enterprise/jobs/list.ts
1424
+ var import_core10 = require("@wport/core");
1558
1425
  var STATUS_MAP = { published: 1, unpublished: 0 };
1559
1426
  var MINIMAL_LIST_FIELDS = ["enc_id", "job_title", "status", "updated_at"];
1560
1427
  function mapStatusFlag(raw) {
@@ -1580,7 +1447,7 @@ async function runEnterpriseJobsList(ctx, apiKey, flags) {
1580
1447
  if (flags.fields && flags.minimal) {
1581
1448
  throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
1582
1449
  }
1583
- const { body } = await enterpriseGet(
1450
+ const { body } = await enterpriseGet2(
1584
1451
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1585
1452
  "/jobs",
1586
1453
  {
@@ -1590,7 +1457,7 @@ async function runEnterpriseJobsList(ctx, apiKey, flags) {
1590
1457
  status: mapStatusFlag(flags.status)
1591
1458
  }
1592
1459
  );
1593
- const paged = asPaginatedBody(body);
1460
+ const paged = (0, import_core10.asPaginatedBody)(body);
1594
1461
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS : flags.fields ? parseFieldsList(flags.fields) : void 0;
1595
1462
  if (projection || ctx.format === "json") {
1596
1463
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -1622,6 +1489,7 @@ function registerEnterpriseJobsList(parent) {
1622
1489
  }
1623
1490
 
1624
1491
  // src/commands/enterprise/jobs/view.ts
1492
+ var import_core11 = require("@wport/core");
1625
1493
  var DETAIL_FIELDS = ["enc_id", "job_title", "code", "status", "created_at", "updated_at"];
1626
1494
  function renderDetailLines(job) {
1627
1495
  const pad = Math.max(...DETAIL_FIELDS.map((f) => f.length)) + 1;
@@ -1642,11 +1510,11 @@ function registerEnterpriseJobsView(parent) {
1642
1510
  }
1643
1511
  const globals = command.optsWithGlobals();
1644
1512
  const { key } = resolveApiKey(globals.apiKey);
1645
- const { body } = await enterpriseGet(
1513
+ const { body } = await enterpriseGet2(
1646
1514
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey: key },
1647
1515
  `/jobs/${encodeURIComponent(encId.trim())}`
1648
1516
  );
1649
- const job = unwrapDataResponse(body);
1517
+ const job = (0, import_core11.unwrapDataResponse)(body);
1650
1518
  if (flags.fields) {
1651
1519
  printJson(pickPaths(job, parseFieldsList(flags.fields)));
1652
1520
  return;
@@ -1661,15 +1529,16 @@ function registerEnterpriseJobsView(parent) {
1661
1529
 
1662
1530
  // src/commands/enterprise/jobs/create.ts
1663
1531
  var import_node_crypto = require("crypto");
1532
+ var import_core12 = require("@wport/core");
1664
1533
  async function runJobsCreate(ctx, apiKey, source, idempotencyKey) {
1665
1534
  const jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
1666
- const { body } = await enterprisePost(
1535
+ const { body } = await enterprisePost2(
1667
1536
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1668
1537
  "/jobs",
1669
1538
  jobBody,
1670
1539
  { idempotencyKey }
1671
1540
  );
1672
- const created = unwrapDataResponse(body);
1541
+ const created = (0, import_core12.unwrapDataResponse)(body);
1673
1542
  if (ctx.format === "json") {
1674
1543
  printJson(created);
1675
1544
  return;
@@ -1688,6 +1557,7 @@ function registerEnterpriseJobsCreate(parent) {
1688
1557
 
1689
1558
  // src/commands/enterprise/jobs/update.ts
1690
1559
  var import_node_crypto2 = require("crypto");
1560
+ var import_core13 = require("@wport/core");
1691
1561
 
1692
1562
  // src/commands/enterprise/jobs/write-shared.ts
1693
1563
  function requireEncId(encId) {
@@ -1700,13 +1570,13 @@ function requireEncId(encId) {
1700
1570
  async function runJobsUpdate(ctx, apiKey, encId, source, idempotencyKey, ifMatch) {
1701
1571
  const trimmed = requireEncId(encId);
1702
1572
  const jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
1703
- const { body } = await enterprisePatch(
1573
+ const { body } = await enterprisePatch2(
1704
1574
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1705
1575
  `/jobs/${encodeURIComponent(trimmed)}`,
1706
1576
  jobBody,
1707
1577
  { idempotencyKey, ifMatch }
1708
1578
  );
1709
- const updated = unwrapDataResponse(body);
1579
+ const updated = (0, import_core13.unwrapDataResponse)(body);
1710
1580
  if (ctx.format === "json") {
1711
1581
  printJson(updated);
1712
1582
  return;
@@ -1725,15 +1595,16 @@ function registerEnterpriseJobsUpdate(parent) {
1725
1595
 
1726
1596
  // src/commands/enterprise/jobs/lifecycle.ts
1727
1597
  var import_node_crypto3 = require("crypto");
1598
+ var import_core14 = require("@wport/core");
1728
1599
  async function runJobsTransition(ctx, apiKey, encId, action, idempotencyKey) {
1729
1600
  const trimmed = requireEncId(encId);
1730
- const { body } = await enterprisePatch(
1601
+ const { body } = await enterprisePatch2(
1731
1602
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1732
1603
  `/jobs/${encodeURIComponent(trimmed)}/${action}`,
1733
1604
  {},
1734
1605
  { idempotencyKey }
1735
1606
  );
1736
- const result = unwrapDataResponse(body);
1607
+ const result = (0, import_core14.unwrapDataResponse)(body);
1737
1608
  if (ctx.format === "json") {
1738
1609
  printJson(result);
1739
1610
  return;
@@ -1747,7 +1618,7 @@ async function runJobsDelete(ctx, apiKey, encId, confirm, idempotencyKey) {
1747
1618
  throw new CliError("Refusing to delete without --confirm (destructive, irreversible)", ExitCode.InvalidArgument);
1748
1619
  }
1749
1620
  const trimmed = requireEncId(encId);
1750
- await enterpriseDelete(
1621
+ await enterpriseDelete2(
1751
1622
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1752
1623
  `/jobs/${encodeURIComponent(trimmed)}`,
1753
1624
  { idempotencyKey }
@@ -1761,13 +1632,13 @@ async function runJobsDelete(ctx, apiKey, encId, confirm, idempotencyKey) {
1761
1632
  }
1762
1633
  async function runJobsCopy(ctx, apiKey, encId, idempotencyKey) {
1763
1634
  const trimmed = requireEncId(encId);
1764
- const { body } = await enterprisePost(
1635
+ const { body } = await enterprisePost2(
1765
1636
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1766
1637
  `/jobs/${encodeURIComponent(trimmed)}/copy`,
1767
1638
  {},
1768
1639
  { idempotencyKey }
1769
1640
  );
1770
- const result = unwrapDataResponse(body);
1641
+ const result = (0, import_core14.unwrapDataResponse)(body);
1771
1642
  if (ctx.format === "json") {
1772
1643
  printJson(result);
1773
1644
  return;
@@ -1821,6 +1692,7 @@ function registerEnterpriseJobsDelete(parent) {
1821
1692
 
1822
1693
  // src/commands/enterprise/jobs/batch.ts
1823
1694
  var import_node_crypto4 = require("crypto");
1695
+ var import_core15 = require("@wport/core");
1824
1696
  var BATCH_MIN = 1;
1825
1697
  var BATCH_MAX = 10;
1826
1698
  async function runJobsBatch(ctx, apiKey, source, confirm, idempotencyKey) {
@@ -1835,13 +1707,13 @@ async function runJobsBatch(ctx, apiKey, source, confirm, idempotencyKey) {
1835
1707
  ExitCode.InvalidArgument
1836
1708
  );
1837
1709
  }
1838
- const { body } = await enterprisePost(
1710
+ const { body } = await enterprisePost2(
1839
1711
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1840
1712
  "/jobs/batch",
1841
1713
  payload,
1842
1714
  { idempotencyKey }
1843
1715
  );
1844
- const result = unwrapDataResponse(body);
1716
+ const result = (0, import_core15.unwrapDataResponse)(body);
1845
1717
  const succeeded = result.succeeded ?? [];
1846
1718
  const failed = result.failed ?? [];
1847
1719
  if (ctx.format === "json") {
@@ -1884,6 +1756,7 @@ function registerEnterpriseJobsCommand(parent) {
1884
1756
  }
1885
1757
 
1886
1758
  // src/commands/enterprise/keys/list.ts
1759
+ var import_core16 = require("@wport/core");
1887
1760
  function formatDate3(value) {
1888
1761
  return value ? String(value).slice(0, 10) : "";
1889
1762
  }
@@ -1891,11 +1764,11 @@ function formatScopes(scopes) {
1891
1764
  return Array.isArray(scopes) ? scopes.join(",") : "";
1892
1765
  }
1893
1766
  async function runKeysList(ctx, apiKey) {
1894
- const { body } = await enterpriseGet(
1767
+ const { body } = await enterpriseGet2(
1895
1768
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1896
1769
  "/keys"
1897
1770
  );
1898
- const keys = unwrapDataArray(body);
1771
+ const keys = (0, import_core16.unwrapDataArray)(body);
1899
1772
  if (ctx.format === "json") {
1900
1773
  printJson(keys);
1901
1774
  return;
@@ -1926,6 +1799,7 @@ function registerEnterpriseKeysList(parent) {
1926
1799
  }
1927
1800
 
1928
1801
  // src/commands/enterprise/keys/rotate.ts
1802
+ var import_core17 = require("@wport/core");
1929
1803
  var ENTERPRISE_KEY_EXPIRY_DAYS = [30, 60, 90];
1930
1804
  function validateExpiryDays(raw) {
1931
1805
  if (raw === void 0) return void 0;
@@ -1943,12 +1817,12 @@ async function runKeysRotate(ctx, apiKey, encId, flags) {
1943
1817
  const expiryDays = validateExpiryDays(flags.expiryDays);
1944
1818
  const requestBody = {};
1945
1819
  if (expiryDays !== void 0) requestBody.expiry_days = expiryDays;
1946
- const { body } = await enterprisePost(
1820
+ const { body } = await enterprisePost2(
1947
1821
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
1948
1822
  `/keys/${encodeURIComponent(trimmed)}/rotate`,
1949
1823
  requestBody
1950
1824
  );
1951
- const issued = unwrapDataResponse(body);
1825
+ const issued = (0, import_core17.unwrapDataResponse)(body);
1952
1826
  if (ctx.format === "json") {
1953
1827
  printJson(issued);
1954
1828
  } else {
@@ -1987,6 +1861,7 @@ function registerEnterpriseKeysCommand(parent) {
1987
1861
  }
1988
1862
 
1989
1863
  // src/commands/enterprise/company/view.ts
1864
+ var import_core18 = require("@wport/core");
1990
1865
  var COMPANY_STATUS_LABELS = {
1991
1866
  0: "not_submitted",
1992
1867
  1: "pending_review",
@@ -2042,11 +1917,11 @@ function renderDetailLines2(company) {
2042
1917
  return lines;
2043
1918
  }
2044
1919
  async function runCompanyView(ctx, apiKey, flags) {
2045
- const { body } = await enterpriseGet(
1920
+ const { body } = await enterpriseGet2(
2046
1921
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2047
1922
  "/company"
2048
1923
  );
2049
- const company = unwrapDataResponse(body);
1924
+ const company = (0, import_core18.unwrapDataResponse)(body);
2050
1925
  if (flags.fields) {
2051
1926
  printJson(pickPaths(company, parseFieldsList(flags.fields)));
2052
1927
  return;
@@ -2068,6 +1943,7 @@ function registerEnterpriseCompanyView(parent) {
2068
1943
 
2069
1944
  // src/commands/enterprise/company/update.ts
2070
1945
  var import_node_crypto5 = require("crypto");
1946
+ var import_core19 = require("@wport/core");
2071
1947
 
2072
1948
  // src/commands/enterprise/company/types.ts
2073
1949
  var BASIC_FIELDS = [
@@ -2133,11 +2009,11 @@ async function buildCompanyUpdatePayloads(input, ctx, apiKey) {
2133
2009
  if (!inputHasBasicField && !inputHasDescriptionField) {
2134
2010
  throw new InvalidArgumentError("No writable company fields provided");
2135
2011
  }
2136
- const { body } = await enterpriseGet(
2012
+ const { body } = await enterpriseGet2(
2137
2013
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2138
2014
  "/company"
2139
2015
  );
2140
- const current = unwrapDataResponse(body);
2016
+ const current = (0, import_core19.unwrapDataResponse)(body);
2141
2017
  const missingContractFields = CONTRACT_REQUIRED_GET_FIELDS.filter((f) => !(f in current));
2142
2018
  if (missingContractFields.length > 0) {
2143
2019
  throw new CliError(
@@ -2204,23 +2080,23 @@ async function runCompanyUpdate(ctx, apiKey, source, idempotencyFlags, options =
2204
2080
  const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
2205
2081
  let basicResultCompany;
2206
2082
  if (needsBasic) {
2207
- const { body } = await enterprisePatch(requestOpts, "/company/basic", payloads.basic, {
2083
+ const { body } = await enterprisePatch2(requestOpts, "/company/basic", payloads.basic, {
2208
2084
  idempotencyKey: basicKey
2209
2085
  });
2210
- basicResultCompany = unwrapDataResponse(body);
2086
+ basicResultCompany = (0, import_core19.unwrapDataResponse)(body);
2211
2087
  }
2212
2088
  if (!needsDescriptions) {
2213
2089
  printCompanyResult(basicResultCompany, ctx.format);
2214
2090
  return;
2215
2091
  }
2216
2092
  try {
2217
- const { body } = await enterprisePatch(
2093
+ const { body } = await enterprisePatch2(
2218
2094
  requestOpts,
2219
2095
  "/company/descriptions",
2220
2096
  payloads.descriptions,
2221
2097
  { idempotencyKey: descriptionsKey }
2222
2098
  );
2223
- const result = unwrapDataResponse(body);
2099
+ const result = (0, import_core19.unwrapDataResponse)(body);
2224
2100
  printCompanyResult(result.company, ctx.format);
2225
2101
  } catch (err) {
2226
2102
  if (!needsBasic) {
@@ -2279,6 +2155,7 @@ function registerEnterpriseCompanyUpdate(parent) {
2279
2155
  var import_node_crypto6 = require("crypto");
2280
2156
  var import_node_fs7 = require("fs");
2281
2157
  var import_node_path3 = require("path");
2158
+ var import_core20 = require("@wport/core");
2282
2159
  var EXTENSION_TO_CONTENT_TYPE = {
2283
2160
  ".png": "image/png",
2284
2161
  ".jpg": "image/jpeg",
@@ -2330,29 +2207,29 @@ async function runCompanyLogoUpload(ctx, apiKey, path, idempotencyFlags) {
2330
2207
  const { contentType, fileSize, bytes } = inspectLocalFile(path);
2331
2208
  const { presignKey, confirmKey } = resolveLogoIdempotencyKeys(idempotencyFlags);
2332
2209
  const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
2333
- const { body: presignBody } = await enterprisePost(
2210
+ const { body: presignBody } = await enterprisePost2(
2334
2211
  requestOpts,
2335
2212
  "/company/logo/presign",
2336
2213
  { content_type: contentType, file_size: fileSize },
2337
2214
  { idempotencyKey: presignKey }
2338
2215
  );
2339
- const presign = unwrapDataResponse(presignBody);
2216
+ const presign = (0, import_core20.unwrapDataResponse)(presignBody);
2340
2217
  const putRequest = new Request(presign.upload_url, {
2341
2218
  method: "PUT",
2342
2219
  headers: { "Content-Type": contentType },
2343
2220
  body: bytes
2344
2221
  });
2345
- const putResponse = await fetchWithTimeout(putRequest, ctx.timeoutMs);
2222
+ const putResponse = await (0, import_core20.fetchWithTimeout)(putRequest, ctx.timeoutMs);
2346
2223
  if (!putResponse.ok) {
2347
2224
  throw new NetworkError(`Failed to upload file to S3: HTTP ${putResponse.status}`);
2348
2225
  }
2349
- const { body: confirmBody } = await enterprisePost(
2226
+ const { body: confirmBody } = await enterprisePost2(
2350
2227
  requestOpts,
2351
2228
  "/company/logo/confirm",
2352
2229
  { s3_key: presign.s3_key },
2353
2230
  { idempotencyKey: confirmKey }
2354
2231
  );
2355
- const result = unwrapDataResponse(confirmBody);
2232
+ const result = (0, import_core20.unwrapDataResponse)(confirmBody);
2356
2233
  printLogoResult(result, ctx.format);
2357
2234
  }
2358
2235
  function registerEnterpriseCompanyLogo(parent) {
@@ -2374,6 +2251,7 @@ function registerEnterpriseCompanyCommand(parent) {
2374
2251
  }
2375
2252
 
2376
2253
  // src/commands/enterprise/talents/list.ts
2254
+ var import_core21 = require("@wport/core");
2377
2255
  var DEFAULT_PAGE_SIZE = 20;
2378
2256
  var MINIMAL_LIST_FIELDS2 = ["enc_resume_id", "candidate_name", "applied_job_title", "applied_at"];
2379
2257
  function formatDate4(value) {
@@ -2383,7 +2261,7 @@ async function runEnterpriseTalentsList(ctx, apiKey, flags) {
2383
2261
  if (flags.fields && flags.minimal) {
2384
2262
  throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
2385
2263
  }
2386
- const { body } = await enterpriseGet(
2264
+ const { body } = await enterpriseGet2(
2387
2265
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2388
2266
  "/talents",
2389
2267
  {
@@ -2396,7 +2274,7 @@ async function runEnterpriseTalentsList(ctx, apiKey, flags) {
2396
2274
  pageSize: flags.pageSize ?? DEFAULT_PAGE_SIZE
2397
2275
  }
2398
2276
  );
2399
- const paged = asPaginatedBody(body);
2277
+ const paged = (0, import_core21.asPaginatedBody)(body);
2400
2278
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS2 : flags.fields ? parseFieldsList(flags.fields) : void 0;
2401
2279
  if (projection || ctx.format === "json") {
2402
2280
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -2429,16 +2307,17 @@ function registerEnterpriseTalentsList(parent) {
2429
2307
  }
2430
2308
 
2431
2309
  // src/commands/enterprise/talents/view.ts
2310
+ var import_core22 = require("@wport/core");
2432
2311
  async function runEnterpriseTalentsView(ctx, apiKey, encResumeId, flags) {
2433
2312
  const trimmed = encResumeId.trim();
2434
2313
  if (!trimmed) {
2435
2314
  throw new CliError("enc_resume_id must not be empty", ExitCode.InvalidArgument);
2436
2315
  }
2437
- const { body } = await enterpriseGet(
2316
+ const { body } = await enterpriseGet2(
2438
2317
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2439
2318
  `/talents/${encodeURIComponent(trimmed)}`
2440
2319
  );
2441
- const resume = unwrapDataResponse(body);
2320
+ const resume = (0, import_core22.unwrapDataResponse)(body);
2442
2321
  if (flags.fields) {
2443
2322
  printJson(pickPaths(resume, parseFieldsList(flags.fields)));
2444
2323
  return;
@@ -2456,6 +2335,7 @@ function registerEnterpriseTalentsView(parent) {
2456
2335
 
2457
2336
  // src/commands/enterprise/talents/respond.ts
2458
2337
  var import_node_crypto7 = require("crypto");
2338
+ var import_core23 = require("@wport/core");
2459
2339
  function resolveRespondBody(flags, options = {}) {
2460
2340
  const hasBody = flags.body !== void 0;
2461
2341
  const hasBodyFile = flags.bodyFile !== void 0;
@@ -2481,13 +2361,13 @@ async function runEnterpriseTalentsRespond(ctx, apiKey, encResumeId, flags, idem
2481
2361
  const encJobId = flags.encJobId?.trim();
2482
2362
  const payload = { subject, body };
2483
2363
  if (encJobId) payload.enc_job_id = encJobId;
2484
- const { body: respBody } = await enterprisePost(
2364
+ const { body: respBody } = await enterprisePost2(
2485
2365
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2486
2366
  `/talents/${encodeURIComponent(trimmedId)}/respond`,
2487
2367
  payload,
2488
2368
  { idempotencyKey }
2489
2369
  );
2490
- const result = unwrapDataResponse(respBody);
2370
+ const result = (0, import_core23.unwrapDataResponse)(respBody);
2491
2371
  if (ctx.format === "json") {
2492
2372
  printJson(result);
2493
2373
  return;
@@ -2516,15 +2396,16 @@ function registerEnterpriseTalentsCommand(parent) {
2516
2396
 
2517
2397
  // src/commands/enterprise/campaigns/create.ts
2518
2398
  var import_node_crypto8 = require("crypto");
2399
+ var import_core24 = require("@wport/core");
2519
2400
  async function runCampaignCreate(ctx, apiKey, source, idempotencyKey) {
2520
2401
  const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
2521
- const { body } = await enterprisePost(
2402
+ const { body } = await enterprisePost2(
2522
2403
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2523
2404
  "/campaigns",
2524
2405
  campaignBody,
2525
2406
  { idempotencyKey }
2526
2407
  );
2527
- const created = unwrapDataResponse(body);
2408
+ const created = (0, import_core24.unwrapDataResponse)(body);
2528
2409
  if (ctx.format === "json") {
2529
2410
  printJson(created);
2530
2411
  return;
@@ -2542,6 +2423,7 @@ function registerEnterpriseCampaignsCreate(parent) {
2542
2423
  }
2543
2424
 
2544
2425
  // src/commands/enterprise/campaigns/list.ts
2426
+ var import_core25 = require("@wport/core");
2545
2427
  var STATUS_MAP2 = { open: 1, closed: 0 };
2546
2428
  var MINIMAL_LIST_FIELDS3 = ["enc_id", "name", "status", "job_count"];
2547
2429
  function mapStatusFlag2(raw) {
@@ -2561,7 +2443,7 @@ async function runEnterpriseCampaignsList(ctx, apiKey, flags) {
2561
2443
  if (flags.fields && flags.minimal) {
2562
2444
  throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
2563
2445
  }
2564
- const { body } = await enterpriseGet(
2446
+ const { body } = await enterpriseGet2(
2565
2447
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2566
2448
  "/campaigns",
2567
2449
  {
@@ -2571,7 +2453,7 @@ async function runEnterpriseCampaignsList(ctx, apiKey, flags) {
2571
2453
  status: mapStatusFlag2(flags.status)
2572
2454
  }
2573
2455
  );
2574
- const paged = asPaginatedBody(body);
2456
+ const paged = (0, import_core25.asPaginatedBody)(body);
2575
2457
  const projection = flags.minimal ? MINIMAL_LIST_FIELDS3 : flags.fields ? parseFieldsList(flags.fields) : void 0;
2576
2458
  if (projection || ctx.format === "json") {
2577
2459
  printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
@@ -2604,15 +2486,16 @@ function registerEnterpriseCampaignsList(parent) {
2604
2486
 
2605
2487
  // src/commands/enterprise/campaigns/lifecycle.ts
2606
2488
  var import_node_crypto9 = require("crypto");
2489
+ var import_core26 = require("@wport/core");
2607
2490
  async function runCampaignTransition(ctx, apiKey, encId, action, idempotencyKey) {
2608
2491
  const trimmed = requireEncId(encId);
2609
- const { body } = await enterprisePatch(
2492
+ const { body } = await enterprisePatch2(
2610
2493
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2611
2494
  `/campaigns/${encodeURIComponent(trimmed)}/${action}`,
2612
2495
  {},
2613
2496
  { idempotencyKey }
2614
2497
  );
2615
- const result = unwrapDataResponse(body);
2498
+ const result = (0, import_core26.unwrapDataResponse)(body);
2616
2499
  if (ctx.format === "json") {
2617
2500
  printJson(result);
2618
2501
  return;
@@ -2638,16 +2521,17 @@ function registerEnterpriseCampaignsUnpublish(parent) {
2638
2521
 
2639
2522
  // src/commands/enterprise/campaigns/update.ts
2640
2523
  var import_node_crypto10 = require("crypto");
2524
+ var import_core27 = require("@wport/core");
2641
2525
  async function runCampaignUpdate(ctx, apiKey, encId, source, idempotencyKey) {
2642
2526
  const trimmed = requireEncId(encId);
2643
2527
  const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
2644
- const { body } = await enterprisePatch(
2528
+ const { body } = await enterprisePatch2(
2645
2529
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2646
2530
  `/campaigns/${encodeURIComponent(trimmed)}`,
2647
2531
  campaignBody,
2648
2532
  { idempotencyKey }
2649
2533
  );
2650
- const updated = unwrapDataResponse(body);
2534
+ const updated = (0, import_core27.unwrapDataResponse)(body);
2651
2535
  if (ctx.format === "json") {
2652
2536
  printJson(updated);
2653
2537
  return;
@@ -2665,16 +2549,17 @@ function registerEnterpriseCampaignsUpdate(parent) {
2665
2549
  }
2666
2550
 
2667
2551
  // src/commands/enterprise/campaigns/view.ts
2552
+ var import_core28 = require("@wport/core");
2668
2553
  async function runEnterpriseCampaignsView(ctx, apiKey, encId, flags) {
2669
2554
  const trimmed = encId.trim();
2670
2555
  if (!trimmed) {
2671
2556
  throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
2672
2557
  }
2673
- const { body } = await enterpriseGet(
2558
+ const { body } = await enterpriseGet2(
2674
2559
  { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
2675
2560
  `/campaigns/${encodeURIComponent(trimmed)}`
2676
2561
  );
2677
- const campaign = unwrapDataResponse(body);
2562
+ const campaign = (0, import_core28.unwrapDataResponse)(body);
2678
2563
  if (flags.fields) {
2679
2564
  printJson(pickPaths(campaign, parseFieldsList(flags.fields)));
2680
2565
  return;
@@ -2818,7 +2703,11 @@ function registerLoginCommand(program2) {
2818
2703
  });
2819
2704
  }
2820
2705
 
2706
+ // src/commands/auth/whoami.ts
2707
+ var import_core30 = require("@wport/core");
2708
+
2821
2709
  // src/lib/personal-client.ts
2710
+ var import_core29 = require("@wport/core");
2822
2711
  var EXPIRY_SKEW_MS = 3e4;
2823
2712
  var NOT_LOGGED_IN_MESSAGE = "Not logged in. Run `wport login`.";
2824
2713
  async function ensureFreshCredentials(opts) {
@@ -2847,7 +2736,7 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
2847
2736
  const headers = {
2848
2737
  Authorization: `Bearer ${accessToken}`,
2849
2738
  "Accept-Language": opts.locale,
2850
- "User-Agent": buildUserAgent(),
2739
+ "User-Agent": (0, import_core29.buildUserAgent)("wport-cli", "0.9.1"),
2851
2740
  Accept: "application/json"
2852
2741
  };
2853
2742
  if (body !== void 0) headers["Content-Type"] = "application/json";
@@ -2857,7 +2746,7 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
2857
2746
  headers,
2858
2747
  body: body !== void 0 ? JSON.stringify(body) : void 0
2859
2748
  });
2860
- const res = await fetchWithTimeout(request, opts.timeoutMs);
2749
+ const res = await (0, import_core29.fetchWithTimeout)(request, opts.timeoutMs);
2861
2750
  const respBody = await res.json().catch(() => null);
2862
2751
  return { status: res.status, body: respBody, headers: res.headers };
2863
2752
  }
@@ -2889,13 +2778,13 @@ function personalDelete(opts, path, extra) {
2889
2778
  }
2890
2779
  function throwPersonalHttpError(status, body) {
2891
2780
  if (status === 401) {
2892
- const base = extractErrorMessage(body) ?? `HTTP ${status}`;
2781
+ const base = (0, import_core29.extractErrorMessage)(body) ?? `HTTP ${status}`;
2893
2782
  throw new CliError(
2894
2783
  `${base} \u2014 Your session may have expired or the request was rejected. Run \`wport login\` to sign in again.`,
2895
2784
  ExitCode.ServerClientError
2896
2785
  );
2897
2786
  }
2898
- throwForHttpStatus(status, body);
2787
+ (0, import_core29.throwForHttpStatus)(status, body);
2899
2788
  }
2900
2789
  function warnIfRateLimitLow2(headers) {
2901
2790
  const remaining = Number(headers.get("x-ratelimit-remaining"));
@@ -2906,6 +2795,7 @@ function warnIfRateLimitLow2(headers) {
2906
2795
  }
2907
2796
 
2908
2797
  // src/commands/auth/whoami.ts
2798
+ var import_core31 = require("@wport/core");
2909
2799
  var PLACEHOLDER = "\u2014";
2910
2800
  function display(value) {
2911
2801
  const clean = sanitizeForTerminal(value ?? "");
@@ -2913,8 +2803,8 @@ function display(value) {
2913
2803
  }
2914
2804
  async function performWhoami(ctx) {
2915
2805
  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);
2806
+ const { body } = await personalGet(opts, import_core31.OAUTH_SESSIONS_BASE);
2807
+ const sessions = (0, import_core30.unwrapDataArray)(body);
2918
2808
  const current = sessions.find((s) => s.is_current);
2919
2809
  const localLoginAt = loadPersonalCredentials()?.session_created_at ?? null;
2920
2810
  if (ctx.format === "json") {
@@ -2966,13 +2856,15 @@ function registerLogoutCommand(program2) {
2966
2856
  }
2967
2857
 
2968
2858
  // src/commands/sessions/list.ts
2859
+ var import_core32 = require("@wport/core");
2860
+ var import_core33 = require("@wport/core");
2969
2861
  function formatDate5(value) {
2970
2862
  return value ? String(value).slice(0, 10) : "";
2971
2863
  }
2972
2864
  async function runSessionsList(ctx) {
2973
2865
  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);
2866
+ const { body } = await personalGet(opts, import_core33.OAUTH_SESSIONS_BASE);
2867
+ const sessions = (0, import_core32.unwrapDataArray)(body);
2976
2868
  if (ctx.format === "json") {
2977
2869
  printJson(sessions);
2978
2870
  return;
@@ -2996,6 +2888,8 @@ function registerSessionsList(parent) {
2996
2888
  }
2997
2889
 
2998
2890
  // src/commands/sessions/revoke.ts
2891
+ var import_core34 = require("@wport/core");
2892
+ var import_core35 = require("@wport/core");
2999
2893
  async function runSessionsRevoke(ctx, encId, allOthers) {
3000
2894
  const hasEncId = encId !== void 0;
3001
2895
  if (hasEncId === allOthers) {
@@ -3003,8 +2897,8 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
3003
2897
  }
3004
2898
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
3005
2899
  if (allOthers) {
3006
- const { body } = await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/others`);
3007
- const result = unwrapDataResponse(body);
2900
+ const { body } = await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/others`);
2901
+ const result = (0, import_core35.unwrapDataResponse)(body);
3008
2902
  if (ctx.format === "json") {
3009
2903
  printJson(result);
3010
2904
  return;
@@ -3015,7 +2909,7 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
3015
2909
  }
3016
2910
  const trimmed = encId.trim();
3017
2911
  if (!trimmed) throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
3018
- await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
2912
+ await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
3019
2913
  if (ctx.format === "json") {
3020
2914
  printJson({ enc_id: trimmed, revoked: true });
3021
2915
  return;
@@ -3037,949 +2931,19 @@ function registerSessionsCommand(program2) {
3037
2931
  registerSessionsRevoke(sessions);
3038
2932
  }
3039
2933
 
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
2934
  // src/commands/personal/resumes/schema.ts
2935
+ var import_core36 = require("@wport/core");
3736
2936
  function registerPersonalResumesSchema(parent) {
3737
2937
  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);
2938
+ printJson((0, import_core36.buildAggregateJsonSchema)(opts.section));
3904
2939
  });
3905
2940
  }
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
2941
 
3979
2942
  // src/commands/personal/resumes/validate.ts
2943
+ var import_core37 = require("@wport/core");
3980
2944
  function runResumesValidate(ctx, filePath) {
3981
2945
  const input = readJsonInput(filePath, { timeoutMs: ctx.timeoutMs });
3982
- const issues = validateAggregate(input);
2946
+ const issues = (0, import_core37.validateAggregate)(input);
3983
2947
  if (issues.length === 0) {
3984
2948
  if (ctx.format === "json") {
3985
2949
  printJson({ valid: true });
@@ -4007,43 +2971,9 @@ function registerPersonalResumesValidate(parent) {
4007
2971
 
4008
2972
  // src/commands/personal/resumes/template.ts
4009
2973
  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
2974
+ var import_core38 = require("@wport/core");
4045
2975
  function runResumesTemplate(outPath) {
4046
- const template = buildTemplate();
2976
+ const template = (0, import_core38.buildTemplate)();
4047
2977
  if (outPath === void 0) {
4048
2978
  printJson(template);
4049
2979
  return;
@@ -4067,6 +2997,8 @@ function registerPersonalResumesTemplate(parent) {
4067
2997
  }
4068
2998
 
4069
2999
  // src/commands/personal/resumes/list.ts
3000
+ var import_core39 = require("@wport/core");
3001
+ var import_core40 = require("@wport/core");
4070
3002
  var MINIMAL_LIST_FIELDS4 = ["enc_id", "name", "updated_at", "is_complete", "is_published"];
4071
3003
  function formatDate6(value) {
4072
3004
  return value ? String(value).slice(0, 10) : "";
@@ -4076,8 +3008,8 @@ function formatQuotaLine(quota) {
4076
3008
  return `${quota.used}/${quota.max_resumes} used, ${status}.`;
4077
3009
  }
4078
3010
  async function fetchResumeList(opts) {
4079
- const { body } = await personalGet(opts, PERSONAL_RESUMES_BASE);
4080
- return unwrapDataResponse(body);
3011
+ const { body } = await personalGet(opts, import_core40.PERSONAL_RESUMES_BASE);
3012
+ return (0, import_core39.unwrapDataResponse)(body);
4081
3013
  }
4082
3014
  async function runResumesList(ctx, flags) {
4083
3015
  if (flags.fields && flags.minimal) {
@@ -4110,17 +3042,20 @@ function registerPersonalResumesList(parent) {
4110
3042
  }
4111
3043
 
4112
3044
  // src/commands/personal/resumes/view.ts
3045
+ var import_core41 = require("@wport/core");
3046
+ var import_core42 = require("@wport/core");
3047
+ var import_core43 = require("@wport/core");
4113
3048
  async function fetchResumeAggregate(opts, encId) {
4114
3049
  const trimmed = encId.trim();
4115
3050
  if (!trimmed) {
4116
3051
  throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
4117
3052
  }
4118
- const { body } = await personalGet(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
4119
- return unwrapDataResponse(body);
3053
+ const { body } = await personalGet(opts, `${import_core42.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
3054
+ return (0, import_core41.unwrapDataResponse)(body);
4120
3055
  }
4121
3056
  function summarizeSections(resume) {
4122
3057
  const rows = [{ section: "name", summary: resume.name }];
4123
- for (const def of SECTIONS) {
3058
+ for (const def of import_core43.SECTIONS) {
4124
3059
  rows.push({ section: def.key, summary: summarizeSection(def, resume) });
4125
3060
  }
4126
3061
  return rows;
@@ -4197,8 +3132,14 @@ function registerPersonalResumesExport(parent) {
4197
3132
  });
4198
3133
  }
4199
3134
 
3135
+ // src/commands/personal/resumes/create.ts
3136
+ var import_core47 = require("@wport/core");
3137
+
4200
3138
  // src/lib/resume-orchestrator.ts
4201
3139
  var import_node_crypto11 = require("crypto");
3140
+ var import_core44 = require("@wport/core");
3141
+ var import_core45 = require("@wport/core");
3142
+ var import_core46 = require("@wport/core");
4202
3143
  function freshIdempotencyKey() {
4203
3144
  return { idempotencyKey: (0, import_node_crypto11.randomUUID)() };
4204
3145
  }
@@ -4213,8 +3154,8 @@ function extractErrorCode(body) {
4213
3154
  }
4214
3155
  async function createShell(opts) {
4215
3156
  try {
4216
- const { body } = await personalPost(opts, PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());
4217
- return unwrapDataResponse(body).enc_id;
3157
+ const { body } = await personalPost(opts, import_core45.PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());
3158
+ return (0, import_core44.unwrapDataResponse)(body).enc_id;
4218
3159
  } catch (err) {
4219
3160
  if (err instanceof ServerClientHttpError) {
4220
3161
  const code = extractErrorCode(err.body);
@@ -4235,7 +3176,7 @@ async function createShell(opts) {
4235
3176
  }
4236
3177
  }
4237
3178
  async function renameResume(opts, encId, name) {
4238
- await personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name }, freshIdempotencyKey());
3179
+ await personalPut(opts, `${import_core45.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name }, freshIdempotencyKey());
4239
3180
  }
4240
3181
  function toWorkExperienceWriteItem(item) {
4241
3182
  const { is_current, ...rest } = item;
@@ -4247,8 +3188,8 @@ function splitItemEncId(item) {
4247
3188
  return [valid, rest];
4248
3189
  }
4249
3190
  async function writeSection(opts, encId, key, value, mode) {
4250
- const plan = SECTION_WRITE_PLAN[key];
4251
- const base = `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;
3191
+ const plan = import_core45.SECTION_WRITE_PLAN[key];
3192
+ const base = `${import_core45.PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;
4252
3193
  switch (plan.kind) {
4253
3194
  case "per-item-post": {
4254
3195
  for (const item of value) {
@@ -4315,7 +3256,7 @@ async function createAggregate(opts, aggregate) {
4315
3256
  if (aggregate.name !== void 0) {
4316
3257
  reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
4317
3258
  }
4318
- for (const section of SECTIONS) {
3259
+ for (const section of import_core46.SECTIONS) {
4319
3260
  const value = aggregate[section.key];
4320
3261
  if (value === void 0) continue;
4321
3262
  reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "create")));
@@ -4325,9 +3266,9 @@ async function createAggregate(opts, aggregate) {
4325
3266
  async function updateAggregate(opts, encId, aggregate, onlySection) {
4326
3267
  const reports = [];
4327
3268
  if (onlySection !== void 0) {
4328
- const section = getSection(onlySection);
3269
+ const section = (0, import_core46.getSection)(onlySection);
4329
3270
  if (!section) {
4330
- const allowed = SECTIONS.map((s) => s.key).join(", ");
3271
+ const allowed = import_core46.SECTIONS.map((s) => s.key).join(", ");
4331
3272
  throw new CliError(`Unknown section "${onlySection}". Allowed: ${allowed}`, ExitCode.InvalidArgument);
4332
3273
  }
4333
3274
  const value = aggregate[onlySection];
@@ -4340,7 +3281,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
4340
3281
  if (aggregate.name !== void 0) {
4341
3282
  reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
4342
3283
  }
4343
- for (const section of SECTIONS) {
3284
+ for (const section of import_core46.SECTIONS) {
4344
3285
  const value = aggregate[section.key];
4345
3286
  if (value === void 0) continue;
4346
3287
  reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "update")));
@@ -4351,7 +3292,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
4351
3292
  // src/commands/personal/resumes/create.ts
4352
3293
  async function runResumesCreate(ctx, filePath) {
4353
3294
  const input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });
4354
- const issues = validateAggregate(input);
3295
+ const issues = (0, import_core47.validateAggregate)(input);
4355
3296
  if (issues.length > 0) {
4356
3297
  if (ctx.format === "json") {
4357
3298
  printJson({ valid: false, issues });
@@ -4444,6 +3385,8 @@ function registerPersonalResumesUpdate(parent) {
4444
3385
 
4445
3386
  // src/commands/personal/resumes/copy.ts
4446
3387
  var import_node_crypto12 = require("crypto");
3388
+ var import_core48 = require("@wport/core");
3389
+ var import_core49 = require("@wport/core");
4447
3390
  function requireEncId3(encId) {
4448
3391
  const trimmed = encId.trim();
4449
3392
  if (!trimmed) {
@@ -4462,12 +3405,12 @@ function printCopyResult(ctx, sourceEncId, newEncId) {
4462
3405
  async function runResumesCopy(ctx, encId, name) {
4463
3406
  const trimmed = requireEncId3(encId);
4464
3407
  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);
3408
+ const { body } = await personalPost(opts, `${import_core49.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/duplicate`, {}, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
3409
+ const { enc_id: newEncId } = (0, import_core48.unwrapDataResponse)(body);
4467
3410
  printCopyResult(ctx, trimmed, newEncId);
4468
3411
  if (name === void 0) return;
4469
3412
  try {
4470
- await personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name }, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
3413
+ await personalPut(opts, `${import_core49.PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name }, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
4471
3414
  } catch (err) {
4472
3415
  const reason = err instanceof Error ? err.message : String(err);
4473
3416
  throw new CliError(
@@ -4484,6 +3427,8 @@ function registerPersonalResumesCopy(parent) {
4484
3427
 
4485
3428
  // src/commands/personal/resumes/publish.ts
4486
3429
  var import_node_crypto13 = require("crypto");
3430
+ var import_core50 = require("@wport/core");
3431
+ var import_core51 = require("@wport/core");
4487
3432
  function requireEncId4(encId) {
4488
3433
  const trimmed = encId.trim();
4489
3434
  if (!trimmed) {
@@ -4497,11 +3442,11 @@ async function runResumesPublishTransition(ctx, encId, action) {
4497
3442
  const targetStatus = action === "publish";
4498
3443
  const { body } = await personalPatch(
4499
3444
  opts,
4500
- `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
3445
+ `${import_core51.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
4501
3446
  { target_status: targetStatus },
4502
3447
  { idempotencyKey: (0, import_node_crypto13.randomUUID)() }
4503
3448
  );
4504
- const result = unwrapDataResponse(body);
3449
+ const result = (0, import_core50.unwrapDataResponse)(body);
4505
3450
  if (ctx.format === "json") {
4506
3451
  printJson({ enc_id: trimmed, is_published: result.is_published });
4507
3452
  return;
@@ -4523,6 +3468,7 @@ function registerPersonalResumesUnpublish(parent) {
4523
3468
 
4524
3469
  // src/commands/personal/resumes/delete.ts
4525
3470
  var import_node_crypto14 = require("crypto");
3471
+ var import_core52 = require("@wport/core");
4526
3472
  function requireEncId5(encId) {
4527
3473
  const trimmed = encId.trim();
4528
3474
  if (!trimmed) {
@@ -4536,7 +3482,7 @@ async function runResumesDelete(ctx, encId, confirm) {
4536
3482
  }
4537
3483
  const trimmed = requireEncId5(encId);
4538
3484
  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)() });
3485
+ await personalDelete(opts, `${import_core52.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`, { idempotencyKey: (0, import_node_crypto14.randomUUID)() });
4540
3486
  if (ctx.format === "json") {
4541
3487
  printJson({ enc_id: trimmed, deleted: true });
4542
3488
  return;
@@ -4570,6 +3516,7 @@ function registerPersonalResumesCommand(parent) {
4570
3516
  // src/commands/personal/apply/index.ts
4571
3517
  var import_node_crypto15 = require("crypto");
4572
3518
  var import_node_fs11 = require("fs");
3519
+ var import_core53 = require("@wport/core");
4573
3520
 
4574
3521
  // src/commands/personal/apply/message-input.ts
4575
3522
  var import_node_fs10 = require("fs");
@@ -4651,7 +3598,7 @@ async function runPersonalApply(ctx, args) {
4651
3598
  const body = { enc_job_id: args.encJobId, enc_resume_id: args.encResumeId, application_message: args.message };
4652
3599
  let result;
4653
3600
  try {
4654
- result = await personalPost(opts, PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
3601
+ result = await personalPost(opts, import_core53.PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
4655
3602
  } catch (err) {
4656
3603
  rethrowApplyError(err);
4657
3604
  }
@@ -4702,7 +3649,7 @@ async function runPersonalApplyBatch(ctx, args) {
4702
3649
  const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
4703
3650
  let result;
4704
3651
  try {
4705
- result = await personalPost(opts, `${PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
3652
+ result = await personalPost(opts, `${import_core53.PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: (0, import_node_crypto15.randomUUID)() });
4706
3653
  } catch (err) {
4707
3654
  rethrowApplyError(err);
4708
3655
  }
@@ -4730,7 +3677,7 @@ function registerPersonalCommand(program2) {
4730
3677
 
4731
3678
  // src/index.ts
4732
3679
  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));
3680
+ program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.9.1", "-v, --version", "output the CLI version").option("--lang <locale>", "Accept-Language locale: zh-TW | en-US | vi-VN | th-TH | id-ID").option("--api <url>", "override API base URL").option("--output <fmt>", "output format: table | json").option("--no-color", "disable color output").option("--timeout <ms>", "HTTP timeout in milliseconds", (v) => Number(v));
4734
3681
  registerJobsCommand(program);
4735
3682
  registerConfigCommand(program);
4736
3683
  registerDoctorCommand(program);
@@ -4756,6 +3703,10 @@ function handleTopLevelError(err) {
4756
3703
  printError(err.message, color);
4757
3704
  process.exit(err.exitCode);
4758
3705
  }
3706
+ if ((0, import_core.isWportError)(err)) {
3707
+ printError(err.message, color);
3708
+ process.exit(exitCodeForError(err));
3709
+ }
4759
3710
  const fallbackMessage = err instanceof Error ? err.message : String(err);
4760
3711
  printError(fallbackMessage, color);
4761
3712
  process.exit(ExitCode.ServerOrNetworkError);