@wport/cli 0.9.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/CHANGELOG.md +11 -0
- package/README.md +34 -11
- package/dist/index.js +260 -1285
- package/dist/index.js.map +1 -1
- package/package.json +3 -10
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.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");
|
|
@@ -366,8 +270,26 @@ function validateAndCoerce(key, value) {
|
|
|
366
270
|
}
|
|
367
271
|
}
|
|
368
272
|
|
|
273
|
+
// src/lib/channel.ts
|
|
274
|
+
var CHANNEL_BASE_URL = {
|
|
275
|
+
prod: "https://api.wport.me",
|
|
276
|
+
dev: "https://developers.wport.me/v2"
|
|
277
|
+
};
|
|
278
|
+
function currentChannel() {
|
|
279
|
+
return true ? "prod" : "prod";
|
|
280
|
+
}
|
|
281
|
+
function channelBaseUrl(channel) {
|
|
282
|
+
return CHANNEL_BASE_URL[channel] ?? CHANNEL_BASE_URL.prod;
|
|
283
|
+
}
|
|
284
|
+
function channelBanner() {
|
|
285
|
+
const channel = currentChannel();
|
|
286
|
+
if (channel === "prod") return null;
|
|
287
|
+
return `[${channel}] targeting the dev backend
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
|
|
369
291
|
// src/lib/global-opts.ts
|
|
370
|
-
var DEFAULT_BASE_URL =
|
|
292
|
+
var DEFAULT_BASE_URL = channelBaseUrl(currentChannel());
|
|
371
293
|
var API_BASE_ENV_VAR = "WPORT_API_BASE";
|
|
372
294
|
var DEFAULT_LOCALE = "zh-TW";
|
|
373
295
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
@@ -460,16 +382,17 @@ function registerJobsSearch(parent) {
|
|
|
460
382
|
const ctx = resolveContext(command);
|
|
461
383
|
const fields = resolveSearchFields(flags);
|
|
462
384
|
const query = buildQuery(flags);
|
|
463
|
-
const client = createApiClient({
|
|
385
|
+
const client = (0, import_core2.createApiClient)({
|
|
464
386
|
baseUrl: ctx.baseUrl,
|
|
465
387
|
locale: ctx.locale,
|
|
466
|
-
timeoutMs: ctx.timeoutMs
|
|
388
|
+
timeoutMs: ctx.timeoutMs,
|
|
389
|
+
userAgent: (0, import_core2.buildUserAgent)("wport-cli", "0.9.1")
|
|
467
390
|
});
|
|
468
391
|
const { data, error, response } = await client.GET("/api/jobs/search", {
|
|
469
392
|
params: { query }
|
|
470
393
|
});
|
|
471
|
-
if (!response.ok) throwForHttpStatus(response.status, error);
|
|
472
|
-
const paged = asPaginatedBody(data);
|
|
394
|
+
if (!response.ok) (0, import_core2.throwForHttpStatus)(response.status, error);
|
|
395
|
+
const paged = (0, import_core2.asPaginatedBody)(data);
|
|
473
396
|
if (ctx.format === "json") {
|
|
474
397
|
const body = fields ? { ...paged, data: paged.data.map((item) => pickPaths(item, fields)) } : paged;
|
|
475
398
|
printJson(body);
|
|
@@ -546,21 +469,9 @@ function formatDate(s) {
|
|
|
546
469
|
return m ? m[1] : s;
|
|
547
470
|
}
|
|
548
471
|
|
|
549
|
-
// src/
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
let cursor = 0;
|
|
553
|
-
async function worker() {
|
|
554
|
-
for (; ; ) {
|
|
555
|
-
const index = cursor++;
|
|
556
|
-
if (index >= items.length) return;
|
|
557
|
-
results[index] = await fn(items[index], index);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
const workerCount = Math.min(Math.max(1, limit), items.length);
|
|
561
|
-
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
562
|
-
return results;
|
|
563
|
-
}
|
|
472
|
+
// src/commands/jobs/view.ts
|
|
473
|
+
var import_core3 = require("@wport/core");
|
|
474
|
+
var import_core4 = require("@wport/core");
|
|
564
475
|
|
|
565
476
|
// src/lib/io-helpers.ts
|
|
566
477
|
var import_node_fs3 = require("fs");
|
|
@@ -715,10 +626,11 @@ function registerJobsView(parent) {
|
|
|
715
626
|
ExitCode.InvalidArgument
|
|
716
627
|
);
|
|
717
628
|
}
|
|
718
|
-
const client = createApiClient({
|
|
629
|
+
const client = (0, import_core3.createApiClient)({
|
|
719
630
|
baseUrl: ctx.baseUrl,
|
|
720
631
|
locale: ctx.locale,
|
|
721
|
-
timeoutMs: ctx.timeoutMs
|
|
632
|
+
timeoutMs: ctx.timeoutMs,
|
|
633
|
+
userAgent: (0, import_core3.buildUserAgent)("wport-cli", "0.9.1")
|
|
722
634
|
});
|
|
723
635
|
if (flags.batch) {
|
|
724
636
|
await runBatchView(encIdArg, flags, client, ctx.timeoutMs);
|
|
@@ -731,8 +643,8 @@ function registerJobsView(parent) {
|
|
|
731
643
|
const { data, error, response } = await client.GET("/api/jobs/{encId}/view", {
|
|
732
644
|
params: { path: { encId } }
|
|
733
645
|
});
|
|
734
|
-
if (!response.ok) throwForHttpStatus(response.status, error);
|
|
735
|
-
const job = unwrapDataResponse(data);
|
|
646
|
+
if (!response.ok) (0, import_core3.throwForHttpStatus)(response.status, error);
|
|
647
|
+
const job = (0, import_core3.unwrapDataResponse)(data);
|
|
736
648
|
if (flags.fields) {
|
|
737
649
|
printJson(pickPaths(job, parseFieldsList(flags.fields)));
|
|
738
650
|
return;
|
|
@@ -770,11 +682,11 @@ async function fetchJob(client, encId) {
|
|
|
770
682
|
const { data, error, response } = await client.GET("/api/jobs/{encId}/view", {
|
|
771
683
|
params: { path: { encId } }
|
|
772
684
|
});
|
|
773
|
-
if (!response.ok) throwForHttpStatus(response.status, error);
|
|
774
|
-
return unwrapDataResponse(data);
|
|
685
|
+
if (!response.ok) (0, import_core3.throwForHttpStatus)(response.status, error);
|
|
686
|
+
return (0, import_core3.unwrapDataResponse)(data);
|
|
775
687
|
}
|
|
776
688
|
async function runBatch(encIds, concurrency, fetchOne, project) {
|
|
777
|
-
return mapWithConcurrency(encIds, concurrency, async (encId) => {
|
|
689
|
+
return (0, import_core4.mapWithConcurrency)(encIds, concurrency, async (encId) => {
|
|
778
690
|
try {
|
|
779
691
|
const job = await fetchOne(encId);
|
|
780
692
|
return { enc_id: encId, ok: true, data: project(job) };
|
|
@@ -971,6 +883,7 @@ function registerConfigCommand(program2) {
|
|
|
971
883
|
|
|
972
884
|
// src/commands/doctor.ts
|
|
973
885
|
var import_node_fs6 = require("fs");
|
|
886
|
+
var import_core7 = require("@wport/core");
|
|
974
887
|
|
|
975
888
|
// src/lib/credentials-store.ts
|
|
976
889
|
var import_node_fs5 = require("fs");
|
|
@@ -981,7 +894,8 @@ var KEY_PREFIX = "wpk_live_";
|
|
|
981
894
|
var KEY_MIN_LENGTH = KEY_PREFIX.length + 32;
|
|
982
895
|
var paths2 = (0, import_env_paths2.default)("wport", { suffix: "" });
|
|
983
896
|
function getCredentialsPath() {
|
|
984
|
-
|
|
897
|
+
const fileName = currentChannel() === "dev" ? "credentials-dev.json" : "credentials.json";
|
|
898
|
+
return (0, import_node_path2.join)(paths2.config, fileName);
|
|
985
899
|
}
|
|
986
900
|
function isValidKeyFormat(key) {
|
|
987
901
|
return key.startsWith(KEY_PREFIX) && key.length >= KEY_MIN_LENGTH && !/\s/.test(key);
|
|
@@ -1146,24 +1060,9 @@ function ensureFormat(key, source) {
|
|
|
1146
1060
|
}
|
|
1147
1061
|
}
|
|
1148
1062
|
|
|
1149
|
-
// src/lib/personal-types.ts
|
|
1150
|
-
var PERSONAL_RESUMES_BASE = "/api/v1/personal/resumes";
|
|
1151
|
-
var PERSONAL_APPLICATIONS_BASE = "/api/v1/personal/applications";
|
|
1152
|
-
var OAUTH_BASE = "/api/oauth";
|
|
1153
|
-
var OAUTH_SESSIONS_BASE = `${OAUTH_BASE}/sessions`;
|
|
1154
|
-
var SECTION_WRITE_PLAN = {
|
|
1155
|
-
education: { kind: "per-item-post", path: "/education" },
|
|
1156
|
-
work_experience: { kind: "work-experience", path: "/work-experience" },
|
|
1157
|
-
certificate: { kind: "per-item-post", path: "/certificate" },
|
|
1158
|
-
language: { kind: "per-item-post", path: "/language" },
|
|
1159
|
-
professional_skills: { kind: "single-post", path: "/professional-skills" },
|
|
1160
|
-
autobiography: { kind: "single-post", path: "/autobiography" },
|
|
1161
|
-
job_condition: { kind: "single-post", path: "/job-condition" },
|
|
1162
|
-
portfolio_links: { kind: "bulk-put", path: "/portfolio-links" },
|
|
1163
|
-
background: { kind: "single-post", path: "/background" }
|
|
1164
|
-
};
|
|
1165
|
-
|
|
1166
1063
|
// src/lib/oauth.ts
|
|
1064
|
+
var import_core5 = require("@wport/core");
|
|
1065
|
+
var import_core6 = require("@wport/core");
|
|
1167
1066
|
var EXPIRED_MESSAGE = "The device code expired before authorization completed. Run `wport login` to try again.";
|
|
1168
1067
|
async function oauthPost(opts, path, body) {
|
|
1169
1068
|
const url = new URL(`${opts.baseUrl}${path}`);
|
|
@@ -1171,13 +1070,13 @@ async function oauthPost(opts, path, body) {
|
|
|
1171
1070
|
method: "POST",
|
|
1172
1071
|
headers: {
|
|
1173
1072
|
"Accept-Language": opts.locale,
|
|
1174
|
-
"User-Agent": buildUserAgent(),
|
|
1073
|
+
"User-Agent": (0, import_core5.buildUserAgent)("wport-cli", "0.9.1"),
|
|
1175
1074
|
Accept: "application/json",
|
|
1176
1075
|
"Content-Type": "application/json"
|
|
1177
1076
|
},
|
|
1178
1077
|
body: JSON.stringify(body)
|
|
1179
1078
|
});
|
|
1180
|
-
const res = await fetchWithTimeout(request, opts.timeoutMs);
|
|
1079
|
+
const res = await (0, import_core5.fetchWithTimeout)(request, opts.timeoutMs);
|
|
1181
1080
|
const respBody = await res.json().catch(() => null);
|
|
1182
1081
|
return { status: res.status, body: respBody };
|
|
1183
1082
|
}
|
|
@@ -1190,15 +1089,15 @@ function oauthErrorCode(body) {
|
|
|
1190
1089
|
async function requestDeviceCode(opts, deviceName) {
|
|
1191
1090
|
const body = {};
|
|
1192
1091
|
if (deviceName) body.device_name = deviceName;
|
|
1193
|
-
const { status, body: respBody } = await oauthPost(opts, `${OAUTH_BASE}/device/code`, body);
|
|
1194
|
-
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);
|
|
1195
1094
|
return respBody;
|
|
1196
1095
|
}
|
|
1197
1096
|
async function pollForToken(opts, device, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
1198
1097
|
let intervalSec = device.interval;
|
|
1199
1098
|
const deadline = Date.now() + device.expires_in * 1e3;
|
|
1200
1099
|
for (; ; ) {
|
|
1201
|
-
const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {
|
|
1100
|
+
const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/token`, {
|
|
1202
1101
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
1203
1102
|
device_code: device.device_code
|
|
1204
1103
|
});
|
|
@@ -1211,14 +1110,14 @@ async function pollForToken(opts, device, sleep = (ms) => new Promise((resolve)
|
|
|
1211
1110
|
} else if (code === "expired_token") {
|
|
1212
1111
|
throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);
|
|
1213
1112
|
} else if (code !== "authorization_pending") {
|
|
1214
|
-
throwForHttpStatus(status, body);
|
|
1113
|
+
(0, import_core5.throwForHttpStatus)(status, body);
|
|
1215
1114
|
}
|
|
1216
1115
|
if (Date.now() >= deadline) throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);
|
|
1217
1116
|
await sleep(intervalSec * 1e3);
|
|
1218
1117
|
}
|
|
1219
1118
|
}
|
|
1220
1119
|
async function refreshAccessToken(opts, refreshToken) {
|
|
1221
|
-
const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {
|
|
1120
|
+
const { status, body } = await oauthPost(opts, `${import_core6.OAUTH_BASE}/token`, {
|
|
1222
1121
|
grant_type: "refresh_token",
|
|
1223
1122
|
refresh_token: refreshToken
|
|
1224
1123
|
});
|
|
@@ -1226,11 +1125,11 @@ async function refreshAccessToken(opts, refreshToken) {
|
|
|
1226
1125
|
if (oauthErrorCode(body) === "invalid_grant") {
|
|
1227
1126
|
throw new CliError("Your session is no longer valid. Run `wport login` to sign in again.", ExitCode.ServerClientError);
|
|
1228
1127
|
}
|
|
1229
|
-
throwForHttpStatus(status, body);
|
|
1128
|
+
(0, import_core5.throwForHttpStatus)(status, body);
|
|
1230
1129
|
}
|
|
1231
1130
|
async function revokeRefreshToken(opts, refreshToken) {
|
|
1232
|
-
const { status, body } = await oauthPost(opts, `${OAUTH_BASE}/revoke`, { token: refreshToken });
|
|
1233
|
-
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);
|
|
1234
1133
|
}
|
|
1235
1134
|
|
|
1236
1135
|
// src/commands/doctor.ts
|
|
@@ -1253,11 +1152,12 @@ function registerDoctorCommand(program2) {
|
|
|
1253
1152
|
}
|
|
1254
1153
|
async function runDoctor(ctx) {
|
|
1255
1154
|
const line = (s = "") => process.stdout.write(s + "\n");
|
|
1256
|
-
line(`wport-cli ${"0.9.
|
|
1155
|
+
line(`wport-cli ${"0.9.1"}`);
|
|
1257
1156
|
line(` bundled schema fingerprint: ${"839e8a891dfb"}`);
|
|
1258
1157
|
line("");
|
|
1259
1158
|
line("Resolved configuration:");
|
|
1260
1159
|
line(` API base URL: ${ctx.baseUrl}`);
|
|
1160
|
+
line(` channel: ${currentChannel()}`);
|
|
1261
1161
|
line(` locale: ${ctx.locale}`);
|
|
1262
1162
|
line(` timeout: ${ctx.timeoutMs}ms`);
|
|
1263
1163
|
const cfgPath = getConfigPath();
|
|
@@ -1308,7 +1208,12 @@ function describePersonalLoginLines() {
|
|
|
1308
1208
|
}
|
|
1309
1209
|
async function probeServer(ctx, line) {
|
|
1310
1210
|
try {
|
|
1311
|
-
const client =
|
|
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
|
+
});
|
|
1312
1217
|
const { response } = await client.GET("/api/jobs/search", { params: { query: { pageSize: 1 } } });
|
|
1313
1218
|
if (response.ok) {
|
|
1314
1219
|
line(` \u2713 reachable (HTTP ${response.status})`);
|
|
@@ -1333,81 +1238,39 @@ async function probeAuthServer(ctx, line) {
|
|
|
1333
1238
|
}
|
|
1334
1239
|
|
|
1335
1240
|
// src/lib/enterprise-client.ts
|
|
1336
|
-
var
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
"User-Agent": buildUserAgent(),
|
|
1347
|
-
Accept: "application/json"
|
|
1348
|
-
}
|
|
1349
|
-
});
|
|
1350
|
-
const res = await fetchWithTimeout(request, opts.timeoutMs);
|
|
1351
|
-
const body = await res.json().catch(() => null);
|
|
1352
|
-
if (!res.ok) throwEnterpriseHttpError(res.status, body);
|
|
1353
|
-
warnIfRateLimitLow(res.headers);
|
|
1354
|
-
return { body, headers: res.headers };
|
|
1355
|
-
}
|
|
1356
|
-
async function enterpriseWrite(method, opts, path, body, extra) {
|
|
1357
|
-
const url = new URL(`${opts.baseUrl}${ENTERPRISE_PREFIX}${path}`);
|
|
1358
|
-
const headers = {
|
|
1359
|
-
Authorization: `Bearer ${opts.apiKey}`,
|
|
1360
|
-
"Accept-Language": opts.locale,
|
|
1361
|
-
"User-Agent": buildUserAgent(),
|
|
1362
|
-
Accept: "application/json"
|
|
1363
|
-
};
|
|
1364
|
-
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
1365
|
-
if (extra?.idempotencyKey) headers["Idempotency-Key"] = extra.idempotencyKey;
|
|
1366
|
-
if (extra?.ifMatch) headers["If-Match"] = extra.ifMatch;
|
|
1367
|
-
const request = new Request(url, {
|
|
1368
|
-
method,
|
|
1369
|
-
headers,
|
|
1370
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
1371
|
-
});
|
|
1372
|
-
const res = await fetchWithTimeout(request, opts.timeoutMs);
|
|
1373
|
-
const respBody = await res.json().catch(() => null);
|
|
1374
|
-
if (!res.ok) throwEnterpriseHttpError(res.status, respBody);
|
|
1375
|
-
warnIfRateLimitLow(res.headers);
|
|
1376
|
-
return { body: respBody, headers: res.headers };
|
|
1377
|
-
}
|
|
1378
|
-
function enterprisePost(opts, path, body, extra) {
|
|
1379
|
-
return enterpriseWrite("POST", opts, path, body, extra);
|
|
1380
|
-
}
|
|
1381
|
-
function enterprisePatch(opts, path, body, extra) {
|
|
1382
|
-
return enterpriseWrite("PATCH", opts, path, body, extra);
|
|
1383
|
-
}
|
|
1384
|
-
function enterpriseDelete(opts, path, extra) {
|
|
1385
|
-
return enterpriseWrite("DELETE", opts, path, void 0, extra);
|
|
1386
|
-
}
|
|
1387
|
-
function throwEnterpriseHttpError(status, body) {
|
|
1388
|
-
const base = extractErrorMessage(body) ?? `HTTP ${status}`;
|
|
1389
|
-
if (status === 401) {
|
|
1390
|
-
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(
|
|
1391
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\`.`,
|
|
1392
|
-
|
|
1252
|
+
err.status,
|
|
1253
|
+
err.body
|
|
1393
1254
|
);
|
|
1394
1255
|
}
|
|
1395
|
-
if (status === 403) {
|
|
1396
|
-
|
|
1256
|
+
if (err.status === 403) {
|
|
1257
|
+
return new import_core8.WportHttpError(
|
|
1397
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.`,
|
|
1398
|
-
|
|
1259
|
+
err.status,
|
|
1260
|
+
err.body
|
|
1399
1261
|
);
|
|
1400
1262
|
}
|
|
1401
|
-
if (status === 400) {
|
|
1402
|
-
const missingFields = extractMissingFields(body);
|
|
1263
|
+
if (err.status === 400) {
|
|
1264
|
+
const missingFields = extractMissingFields(err.body);
|
|
1403
1265
|
if (missingFields.length > 0) {
|
|
1404
|
-
|
|
1266
|
+
return new import_core8.WportHttpError(
|
|
1405
1267
|
`${base} \u2014 Missing required fields: ${missingFields.join(", ")}. Fill them via \`wport enterprise jobs update <enc_id> ...\` (or the web console), then publish.`,
|
|
1406
|
-
|
|
1268
|
+
err.status,
|
|
1269
|
+
err.body
|
|
1407
1270
|
);
|
|
1408
1271
|
}
|
|
1409
1272
|
}
|
|
1410
|
-
|
|
1273
|
+
return err;
|
|
1411
1274
|
}
|
|
1412
1275
|
function extractMissingFields(body) {
|
|
1413
1276
|
if (!body || typeof body !== "object") return [];
|
|
@@ -1424,16 +1287,40 @@ function warnIfRateLimitLow(headers) {
|
|
|
1424
1287
|
printWarn(`Rate limit nearly exhausted: ${remaining}/${limit} requests remaining this window.`, false);
|
|
1425
1288
|
}
|
|
1426
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
|
+
}
|
|
1427
1312
|
|
|
1428
1313
|
// src/commands/enterprise/login.ts
|
|
1429
1314
|
async function performLogin(ctx, key) {
|
|
1315
|
+
const banner = channelBanner();
|
|
1316
|
+
if (banner) process.stderr.write(banner);
|
|
1430
1317
|
if (!isValidKeyFormat(key)) {
|
|
1431
1318
|
throw new CliError(
|
|
1432
1319
|
`That does not look like a valid ${KEY_PREFIX} key. Nothing was saved.`,
|
|
1433
1320
|
ExitCode.InvalidArgument
|
|
1434
1321
|
);
|
|
1435
1322
|
}
|
|
1436
|
-
const { body } = await
|
|
1323
|
+
const { body } = await enterpriseGet2({ ...ctx, apiKey: key }, "/me");
|
|
1437
1324
|
saveCredentials({
|
|
1438
1325
|
api_key: key,
|
|
1439
1326
|
company_name: extractCompanyName(body),
|
|
@@ -1498,15 +1385,16 @@ function registerEnterpriseWhoami(parent) {
|
|
|
1498
1385
|
}
|
|
1499
1386
|
|
|
1500
1387
|
// src/commands/enterprise/usage.ts
|
|
1388
|
+
var import_core9 = require("@wport/core");
|
|
1501
1389
|
function num(value) {
|
|
1502
1390
|
return typeof value === "number" && Number.isFinite(value) ? String(value) : "\u2014";
|
|
1503
1391
|
}
|
|
1504
1392
|
async function runUsage(ctx, apiKey) {
|
|
1505
|
-
const { body } = await
|
|
1393
|
+
const { body } = await enterpriseGet2(
|
|
1506
1394
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1507
1395
|
"/usage"
|
|
1508
1396
|
);
|
|
1509
|
-
const usage = unwrapDataResponse(body);
|
|
1397
|
+
const usage = (0, import_core9.unwrapDataResponse)(body);
|
|
1510
1398
|
if (ctx.format === "json") {
|
|
1511
1399
|
printJson(usage);
|
|
1512
1400
|
return;
|
|
@@ -1533,6 +1421,7 @@ function registerEnterpriseUsage(parent) {
|
|
|
1533
1421
|
}
|
|
1534
1422
|
|
|
1535
1423
|
// src/commands/enterprise/jobs/list.ts
|
|
1424
|
+
var import_core10 = require("@wport/core");
|
|
1536
1425
|
var STATUS_MAP = { published: 1, unpublished: 0 };
|
|
1537
1426
|
var MINIMAL_LIST_FIELDS = ["enc_id", "job_title", "status", "updated_at"];
|
|
1538
1427
|
function mapStatusFlag(raw) {
|
|
@@ -1558,7 +1447,7 @@ async function runEnterpriseJobsList(ctx, apiKey, flags) {
|
|
|
1558
1447
|
if (flags.fields && flags.minimal) {
|
|
1559
1448
|
throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
|
|
1560
1449
|
}
|
|
1561
|
-
const { body } = await
|
|
1450
|
+
const { body } = await enterpriseGet2(
|
|
1562
1451
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1563
1452
|
"/jobs",
|
|
1564
1453
|
{
|
|
@@ -1568,7 +1457,7 @@ async function runEnterpriseJobsList(ctx, apiKey, flags) {
|
|
|
1568
1457
|
status: mapStatusFlag(flags.status)
|
|
1569
1458
|
}
|
|
1570
1459
|
);
|
|
1571
|
-
const paged = asPaginatedBody(body);
|
|
1460
|
+
const paged = (0, import_core10.asPaginatedBody)(body);
|
|
1572
1461
|
const projection = flags.minimal ? MINIMAL_LIST_FIELDS : flags.fields ? parseFieldsList(flags.fields) : void 0;
|
|
1573
1462
|
if (projection || ctx.format === "json") {
|
|
1574
1463
|
printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
|
|
@@ -1600,6 +1489,7 @@ function registerEnterpriseJobsList(parent) {
|
|
|
1600
1489
|
}
|
|
1601
1490
|
|
|
1602
1491
|
// src/commands/enterprise/jobs/view.ts
|
|
1492
|
+
var import_core11 = require("@wport/core");
|
|
1603
1493
|
var DETAIL_FIELDS = ["enc_id", "job_title", "code", "status", "created_at", "updated_at"];
|
|
1604
1494
|
function renderDetailLines(job) {
|
|
1605
1495
|
const pad = Math.max(...DETAIL_FIELDS.map((f) => f.length)) + 1;
|
|
@@ -1620,11 +1510,11 @@ function registerEnterpriseJobsView(parent) {
|
|
|
1620
1510
|
}
|
|
1621
1511
|
const globals = command.optsWithGlobals();
|
|
1622
1512
|
const { key } = resolveApiKey(globals.apiKey);
|
|
1623
|
-
const { body } = await
|
|
1513
|
+
const { body } = await enterpriseGet2(
|
|
1624
1514
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey: key },
|
|
1625
1515
|
`/jobs/${encodeURIComponent(encId.trim())}`
|
|
1626
1516
|
);
|
|
1627
|
-
const job = unwrapDataResponse(body);
|
|
1517
|
+
const job = (0, import_core11.unwrapDataResponse)(body);
|
|
1628
1518
|
if (flags.fields) {
|
|
1629
1519
|
printJson(pickPaths(job, parseFieldsList(flags.fields)));
|
|
1630
1520
|
return;
|
|
@@ -1639,15 +1529,16 @@ function registerEnterpriseJobsView(parent) {
|
|
|
1639
1529
|
|
|
1640
1530
|
// src/commands/enterprise/jobs/create.ts
|
|
1641
1531
|
var import_node_crypto = require("crypto");
|
|
1532
|
+
var import_core12 = require("@wport/core");
|
|
1642
1533
|
async function runJobsCreate(ctx, apiKey, source, idempotencyKey) {
|
|
1643
1534
|
const jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
|
|
1644
|
-
const { body } = await
|
|
1535
|
+
const { body } = await enterprisePost2(
|
|
1645
1536
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1646
1537
|
"/jobs",
|
|
1647
1538
|
jobBody,
|
|
1648
1539
|
{ idempotencyKey }
|
|
1649
1540
|
);
|
|
1650
|
-
const created = unwrapDataResponse(body);
|
|
1541
|
+
const created = (0, import_core12.unwrapDataResponse)(body);
|
|
1651
1542
|
if (ctx.format === "json") {
|
|
1652
1543
|
printJson(created);
|
|
1653
1544
|
return;
|
|
@@ -1666,6 +1557,7 @@ function registerEnterpriseJobsCreate(parent) {
|
|
|
1666
1557
|
|
|
1667
1558
|
// src/commands/enterprise/jobs/update.ts
|
|
1668
1559
|
var import_node_crypto2 = require("crypto");
|
|
1560
|
+
var import_core13 = require("@wport/core");
|
|
1669
1561
|
|
|
1670
1562
|
// src/commands/enterprise/jobs/write-shared.ts
|
|
1671
1563
|
function requireEncId(encId) {
|
|
@@ -1678,13 +1570,13 @@ function requireEncId(encId) {
|
|
|
1678
1570
|
async function runJobsUpdate(ctx, apiKey, encId, source, idempotencyKey, ifMatch) {
|
|
1679
1571
|
const trimmed = requireEncId(encId);
|
|
1680
1572
|
const jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
|
|
1681
|
-
const { body } = await
|
|
1573
|
+
const { body } = await enterprisePatch2(
|
|
1682
1574
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1683
1575
|
`/jobs/${encodeURIComponent(trimmed)}`,
|
|
1684
1576
|
jobBody,
|
|
1685
1577
|
{ idempotencyKey, ifMatch }
|
|
1686
1578
|
);
|
|
1687
|
-
const updated = unwrapDataResponse(body);
|
|
1579
|
+
const updated = (0, import_core13.unwrapDataResponse)(body);
|
|
1688
1580
|
if (ctx.format === "json") {
|
|
1689
1581
|
printJson(updated);
|
|
1690
1582
|
return;
|
|
@@ -1703,15 +1595,16 @@ function registerEnterpriseJobsUpdate(parent) {
|
|
|
1703
1595
|
|
|
1704
1596
|
// src/commands/enterprise/jobs/lifecycle.ts
|
|
1705
1597
|
var import_node_crypto3 = require("crypto");
|
|
1598
|
+
var import_core14 = require("@wport/core");
|
|
1706
1599
|
async function runJobsTransition(ctx, apiKey, encId, action, idempotencyKey) {
|
|
1707
1600
|
const trimmed = requireEncId(encId);
|
|
1708
|
-
const { body } = await
|
|
1601
|
+
const { body } = await enterprisePatch2(
|
|
1709
1602
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1710
1603
|
`/jobs/${encodeURIComponent(trimmed)}/${action}`,
|
|
1711
1604
|
{},
|
|
1712
1605
|
{ idempotencyKey }
|
|
1713
1606
|
);
|
|
1714
|
-
const result = unwrapDataResponse(body);
|
|
1607
|
+
const result = (0, import_core14.unwrapDataResponse)(body);
|
|
1715
1608
|
if (ctx.format === "json") {
|
|
1716
1609
|
printJson(result);
|
|
1717
1610
|
return;
|
|
@@ -1725,7 +1618,7 @@ async function runJobsDelete(ctx, apiKey, encId, confirm, idempotencyKey) {
|
|
|
1725
1618
|
throw new CliError("Refusing to delete without --confirm (destructive, irreversible)", ExitCode.InvalidArgument);
|
|
1726
1619
|
}
|
|
1727
1620
|
const trimmed = requireEncId(encId);
|
|
1728
|
-
await
|
|
1621
|
+
await enterpriseDelete2(
|
|
1729
1622
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1730
1623
|
`/jobs/${encodeURIComponent(trimmed)}`,
|
|
1731
1624
|
{ idempotencyKey }
|
|
@@ -1739,13 +1632,13 @@ async function runJobsDelete(ctx, apiKey, encId, confirm, idempotencyKey) {
|
|
|
1739
1632
|
}
|
|
1740
1633
|
async function runJobsCopy(ctx, apiKey, encId, idempotencyKey) {
|
|
1741
1634
|
const trimmed = requireEncId(encId);
|
|
1742
|
-
const { body } = await
|
|
1635
|
+
const { body } = await enterprisePost2(
|
|
1743
1636
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1744
1637
|
`/jobs/${encodeURIComponent(trimmed)}/copy`,
|
|
1745
1638
|
{},
|
|
1746
1639
|
{ idempotencyKey }
|
|
1747
1640
|
);
|
|
1748
|
-
const result = unwrapDataResponse(body);
|
|
1641
|
+
const result = (0, import_core14.unwrapDataResponse)(body);
|
|
1749
1642
|
if (ctx.format === "json") {
|
|
1750
1643
|
printJson(result);
|
|
1751
1644
|
return;
|
|
@@ -1799,6 +1692,7 @@ function registerEnterpriseJobsDelete(parent) {
|
|
|
1799
1692
|
|
|
1800
1693
|
// src/commands/enterprise/jobs/batch.ts
|
|
1801
1694
|
var import_node_crypto4 = require("crypto");
|
|
1695
|
+
var import_core15 = require("@wport/core");
|
|
1802
1696
|
var BATCH_MIN = 1;
|
|
1803
1697
|
var BATCH_MAX = 10;
|
|
1804
1698
|
async function runJobsBatch(ctx, apiKey, source, confirm, idempotencyKey) {
|
|
@@ -1813,13 +1707,13 @@ async function runJobsBatch(ctx, apiKey, source, confirm, idempotencyKey) {
|
|
|
1813
1707
|
ExitCode.InvalidArgument
|
|
1814
1708
|
);
|
|
1815
1709
|
}
|
|
1816
|
-
const { body } = await
|
|
1710
|
+
const { body } = await enterprisePost2(
|
|
1817
1711
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1818
1712
|
"/jobs/batch",
|
|
1819
1713
|
payload,
|
|
1820
1714
|
{ idempotencyKey }
|
|
1821
1715
|
);
|
|
1822
|
-
const result = unwrapDataResponse(body);
|
|
1716
|
+
const result = (0, import_core15.unwrapDataResponse)(body);
|
|
1823
1717
|
const succeeded = result.succeeded ?? [];
|
|
1824
1718
|
const failed = result.failed ?? [];
|
|
1825
1719
|
if (ctx.format === "json") {
|
|
@@ -1862,6 +1756,7 @@ function registerEnterpriseJobsCommand(parent) {
|
|
|
1862
1756
|
}
|
|
1863
1757
|
|
|
1864
1758
|
// src/commands/enterprise/keys/list.ts
|
|
1759
|
+
var import_core16 = require("@wport/core");
|
|
1865
1760
|
function formatDate3(value) {
|
|
1866
1761
|
return value ? String(value).slice(0, 10) : "";
|
|
1867
1762
|
}
|
|
@@ -1869,11 +1764,11 @@ function formatScopes(scopes) {
|
|
|
1869
1764
|
return Array.isArray(scopes) ? scopes.join(",") : "";
|
|
1870
1765
|
}
|
|
1871
1766
|
async function runKeysList(ctx, apiKey) {
|
|
1872
|
-
const { body } = await
|
|
1767
|
+
const { body } = await enterpriseGet2(
|
|
1873
1768
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1874
1769
|
"/keys"
|
|
1875
1770
|
);
|
|
1876
|
-
const keys = unwrapDataArray(body);
|
|
1771
|
+
const keys = (0, import_core16.unwrapDataArray)(body);
|
|
1877
1772
|
if (ctx.format === "json") {
|
|
1878
1773
|
printJson(keys);
|
|
1879
1774
|
return;
|
|
@@ -1904,6 +1799,7 @@ function registerEnterpriseKeysList(parent) {
|
|
|
1904
1799
|
}
|
|
1905
1800
|
|
|
1906
1801
|
// src/commands/enterprise/keys/rotate.ts
|
|
1802
|
+
var import_core17 = require("@wport/core");
|
|
1907
1803
|
var ENTERPRISE_KEY_EXPIRY_DAYS = [30, 60, 90];
|
|
1908
1804
|
function validateExpiryDays(raw) {
|
|
1909
1805
|
if (raw === void 0) return void 0;
|
|
@@ -1921,12 +1817,12 @@ async function runKeysRotate(ctx, apiKey, encId, flags) {
|
|
|
1921
1817
|
const expiryDays = validateExpiryDays(flags.expiryDays);
|
|
1922
1818
|
const requestBody = {};
|
|
1923
1819
|
if (expiryDays !== void 0) requestBody.expiry_days = expiryDays;
|
|
1924
|
-
const { body } = await
|
|
1820
|
+
const { body } = await enterprisePost2(
|
|
1925
1821
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
1926
1822
|
`/keys/${encodeURIComponent(trimmed)}/rotate`,
|
|
1927
1823
|
requestBody
|
|
1928
1824
|
);
|
|
1929
|
-
const issued = unwrapDataResponse(body);
|
|
1825
|
+
const issued = (0, import_core17.unwrapDataResponse)(body);
|
|
1930
1826
|
if (ctx.format === "json") {
|
|
1931
1827
|
printJson(issued);
|
|
1932
1828
|
} else {
|
|
@@ -1965,6 +1861,7 @@ function registerEnterpriseKeysCommand(parent) {
|
|
|
1965
1861
|
}
|
|
1966
1862
|
|
|
1967
1863
|
// src/commands/enterprise/company/view.ts
|
|
1864
|
+
var import_core18 = require("@wport/core");
|
|
1968
1865
|
var COMPANY_STATUS_LABELS = {
|
|
1969
1866
|
0: "not_submitted",
|
|
1970
1867
|
1: "pending_review",
|
|
@@ -2020,11 +1917,11 @@ function renderDetailLines2(company) {
|
|
|
2020
1917
|
return lines;
|
|
2021
1918
|
}
|
|
2022
1919
|
async function runCompanyView(ctx, apiKey, flags) {
|
|
2023
|
-
const { body } = await
|
|
1920
|
+
const { body } = await enterpriseGet2(
|
|
2024
1921
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2025
1922
|
"/company"
|
|
2026
1923
|
);
|
|
2027
|
-
const company = unwrapDataResponse(body);
|
|
1924
|
+
const company = (0, import_core18.unwrapDataResponse)(body);
|
|
2028
1925
|
if (flags.fields) {
|
|
2029
1926
|
printJson(pickPaths(company, parseFieldsList(flags.fields)));
|
|
2030
1927
|
return;
|
|
@@ -2046,6 +1943,7 @@ function registerEnterpriseCompanyView(parent) {
|
|
|
2046
1943
|
|
|
2047
1944
|
// src/commands/enterprise/company/update.ts
|
|
2048
1945
|
var import_node_crypto5 = require("crypto");
|
|
1946
|
+
var import_core19 = require("@wport/core");
|
|
2049
1947
|
|
|
2050
1948
|
// src/commands/enterprise/company/types.ts
|
|
2051
1949
|
var BASIC_FIELDS = [
|
|
@@ -2111,11 +2009,11 @@ async function buildCompanyUpdatePayloads(input, ctx, apiKey) {
|
|
|
2111
2009
|
if (!inputHasBasicField && !inputHasDescriptionField) {
|
|
2112
2010
|
throw new InvalidArgumentError("No writable company fields provided");
|
|
2113
2011
|
}
|
|
2114
|
-
const { body } = await
|
|
2012
|
+
const { body } = await enterpriseGet2(
|
|
2115
2013
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2116
2014
|
"/company"
|
|
2117
2015
|
);
|
|
2118
|
-
const current = unwrapDataResponse(body);
|
|
2016
|
+
const current = (0, import_core19.unwrapDataResponse)(body);
|
|
2119
2017
|
const missingContractFields = CONTRACT_REQUIRED_GET_FIELDS.filter((f) => !(f in current));
|
|
2120
2018
|
if (missingContractFields.length > 0) {
|
|
2121
2019
|
throw new CliError(
|
|
@@ -2182,23 +2080,23 @@ async function runCompanyUpdate(ctx, apiKey, source, idempotencyFlags, options =
|
|
|
2182
2080
|
const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
|
|
2183
2081
|
let basicResultCompany;
|
|
2184
2082
|
if (needsBasic) {
|
|
2185
|
-
const { body } = await
|
|
2083
|
+
const { body } = await enterprisePatch2(requestOpts, "/company/basic", payloads.basic, {
|
|
2186
2084
|
idempotencyKey: basicKey
|
|
2187
2085
|
});
|
|
2188
|
-
basicResultCompany = unwrapDataResponse(body);
|
|
2086
|
+
basicResultCompany = (0, import_core19.unwrapDataResponse)(body);
|
|
2189
2087
|
}
|
|
2190
2088
|
if (!needsDescriptions) {
|
|
2191
2089
|
printCompanyResult(basicResultCompany, ctx.format);
|
|
2192
2090
|
return;
|
|
2193
2091
|
}
|
|
2194
2092
|
try {
|
|
2195
|
-
const { body } = await
|
|
2093
|
+
const { body } = await enterprisePatch2(
|
|
2196
2094
|
requestOpts,
|
|
2197
2095
|
"/company/descriptions",
|
|
2198
2096
|
payloads.descriptions,
|
|
2199
2097
|
{ idempotencyKey: descriptionsKey }
|
|
2200
2098
|
);
|
|
2201
|
-
const result = unwrapDataResponse(body);
|
|
2099
|
+
const result = (0, import_core19.unwrapDataResponse)(body);
|
|
2202
2100
|
printCompanyResult(result.company, ctx.format);
|
|
2203
2101
|
} catch (err) {
|
|
2204
2102
|
if (!needsBasic) {
|
|
@@ -2257,6 +2155,7 @@ function registerEnterpriseCompanyUpdate(parent) {
|
|
|
2257
2155
|
var import_node_crypto6 = require("crypto");
|
|
2258
2156
|
var import_node_fs7 = require("fs");
|
|
2259
2157
|
var import_node_path3 = require("path");
|
|
2158
|
+
var import_core20 = require("@wport/core");
|
|
2260
2159
|
var EXTENSION_TO_CONTENT_TYPE = {
|
|
2261
2160
|
".png": "image/png",
|
|
2262
2161
|
".jpg": "image/jpeg",
|
|
@@ -2308,29 +2207,29 @@ async function runCompanyLogoUpload(ctx, apiKey, path, idempotencyFlags) {
|
|
|
2308
2207
|
const { contentType, fileSize, bytes } = inspectLocalFile(path);
|
|
2309
2208
|
const { presignKey, confirmKey } = resolveLogoIdempotencyKeys(idempotencyFlags);
|
|
2310
2209
|
const requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };
|
|
2311
|
-
const { body: presignBody } = await
|
|
2210
|
+
const { body: presignBody } = await enterprisePost2(
|
|
2312
2211
|
requestOpts,
|
|
2313
2212
|
"/company/logo/presign",
|
|
2314
2213
|
{ content_type: contentType, file_size: fileSize },
|
|
2315
2214
|
{ idempotencyKey: presignKey }
|
|
2316
2215
|
);
|
|
2317
|
-
const presign = unwrapDataResponse(presignBody);
|
|
2216
|
+
const presign = (0, import_core20.unwrapDataResponse)(presignBody);
|
|
2318
2217
|
const putRequest = new Request(presign.upload_url, {
|
|
2319
2218
|
method: "PUT",
|
|
2320
2219
|
headers: { "Content-Type": contentType },
|
|
2321
2220
|
body: bytes
|
|
2322
2221
|
});
|
|
2323
|
-
const putResponse = await fetchWithTimeout(putRequest, ctx.timeoutMs);
|
|
2222
|
+
const putResponse = await (0, import_core20.fetchWithTimeout)(putRequest, ctx.timeoutMs);
|
|
2324
2223
|
if (!putResponse.ok) {
|
|
2325
2224
|
throw new NetworkError(`Failed to upload file to S3: HTTP ${putResponse.status}`);
|
|
2326
2225
|
}
|
|
2327
|
-
const { body: confirmBody } = await
|
|
2226
|
+
const { body: confirmBody } = await enterprisePost2(
|
|
2328
2227
|
requestOpts,
|
|
2329
2228
|
"/company/logo/confirm",
|
|
2330
2229
|
{ s3_key: presign.s3_key },
|
|
2331
2230
|
{ idempotencyKey: confirmKey }
|
|
2332
2231
|
);
|
|
2333
|
-
const result = unwrapDataResponse(confirmBody);
|
|
2232
|
+
const result = (0, import_core20.unwrapDataResponse)(confirmBody);
|
|
2334
2233
|
printLogoResult(result, ctx.format);
|
|
2335
2234
|
}
|
|
2336
2235
|
function registerEnterpriseCompanyLogo(parent) {
|
|
@@ -2352,6 +2251,7 @@ function registerEnterpriseCompanyCommand(parent) {
|
|
|
2352
2251
|
}
|
|
2353
2252
|
|
|
2354
2253
|
// src/commands/enterprise/talents/list.ts
|
|
2254
|
+
var import_core21 = require("@wport/core");
|
|
2355
2255
|
var DEFAULT_PAGE_SIZE = 20;
|
|
2356
2256
|
var MINIMAL_LIST_FIELDS2 = ["enc_resume_id", "candidate_name", "applied_job_title", "applied_at"];
|
|
2357
2257
|
function formatDate4(value) {
|
|
@@ -2361,7 +2261,7 @@ async function runEnterpriseTalentsList(ctx, apiKey, flags) {
|
|
|
2361
2261
|
if (flags.fields && flags.minimal) {
|
|
2362
2262
|
throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
|
|
2363
2263
|
}
|
|
2364
|
-
const { body } = await
|
|
2264
|
+
const { body } = await enterpriseGet2(
|
|
2365
2265
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2366
2266
|
"/talents",
|
|
2367
2267
|
{
|
|
@@ -2374,7 +2274,7 @@ async function runEnterpriseTalentsList(ctx, apiKey, flags) {
|
|
|
2374
2274
|
pageSize: flags.pageSize ?? DEFAULT_PAGE_SIZE
|
|
2375
2275
|
}
|
|
2376
2276
|
);
|
|
2377
|
-
const paged = asPaginatedBody(body);
|
|
2277
|
+
const paged = (0, import_core21.asPaginatedBody)(body);
|
|
2378
2278
|
const projection = flags.minimal ? MINIMAL_LIST_FIELDS2 : flags.fields ? parseFieldsList(flags.fields) : void 0;
|
|
2379
2279
|
if (projection || ctx.format === "json") {
|
|
2380
2280
|
printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
|
|
@@ -2407,16 +2307,17 @@ function registerEnterpriseTalentsList(parent) {
|
|
|
2407
2307
|
}
|
|
2408
2308
|
|
|
2409
2309
|
// src/commands/enterprise/talents/view.ts
|
|
2310
|
+
var import_core22 = require("@wport/core");
|
|
2410
2311
|
async function runEnterpriseTalentsView(ctx, apiKey, encResumeId, flags) {
|
|
2411
2312
|
const trimmed = encResumeId.trim();
|
|
2412
2313
|
if (!trimmed) {
|
|
2413
2314
|
throw new CliError("enc_resume_id must not be empty", ExitCode.InvalidArgument);
|
|
2414
2315
|
}
|
|
2415
|
-
const { body } = await
|
|
2316
|
+
const { body } = await enterpriseGet2(
|
|
2416
2317
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2417
2318
|
`/talents/${encodeURIComponent(trimmed)}`
|
|
2418
2319
|
);
|
|
2419
|
-
const resume = unwrapDataResponse(body);
|
|
2320
|
+
const resume = (0, import_core22.unwrapDataResponse)(body);
|
|
2420
2321
|
if (flags.fields) {
|
|
2421
2322
|
printJson(pickPaths(resume, parseFieldsList(flags.fields)));
|
|
2422
2323
|
return;
|
|
@@ -2434,6 +2335,7 @@ function registerEnterpriseTalentsView(parent) {
|
|
|
2434
2335
|
|
|
2435
2336
|
// src/commands/enterprise/talents/respond.ts
|
|
2436
2337
|
var import_node_crypto7 = require("crypto");
|
|
2338
|
+
var import_core23 = require("@wport/core");
|
|
2437
2339
|
function resolveRespondBody(flags, options = {}) {
|
|
2438
2340
|
const hasBody = flags.body !== void 0;
|
|
2439
2341
|
const hasBodyFile = flags.bodyFile !== void 0;
|
|
@@ -2459,13 +2361,13 @@ async function runEnterpriseTalentsRespond(ctx, apiKey, encResumeId, flags, idem
|
|
|
2459
2361
|
const encJobId = flags.encJobId?.trim();
|
|
2460
2362
|
const payload = { subject, body };
|
|
2461
2363
|
if (encJobId) payload.enc_job_id = encJobId;
|
|
2462
|
-
const { body: respBody } = await
|
|
2364
|
+
const { body: respBody } = await enterprisePost2(
|
|
2463
2365
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2464
2366
|
`/talents/${encodeURIComponent(trimmedId)}/respond`,
|
|
2465
2367
|
payload,
|
|
2466
2368
|
{ idempotencyKey }
|
|
2467
2369
|
);
|
|
2468
|
-
const result = unwrapDataResponse(respBody);
|
|
2370
|
+
const result = (0, import_core23.unwrapDataResponse)(respBody);
|
|
2469
2371
|
if (ctx.format === "json") {
|
|
2470
2372
|
printJson(result);
|
|
2471
2373
|
return;
|
|
@@ -2494,15 +2396,16 @@ function registerEnterpriseTalentsCommand(parent) {
|
|
|
2494
2396
|
|
|
2495
2397
|
// src/commands/enterprise/campaigns/create.ts
|
|
2496
2398
|
var import_node_crypto8 = require("crypto");
|
|
2399
|
+
var import_core24 = require("@wport/core");
|
|
2497
2400
|
async function runCampaignCreate(ctx, apiKey, source, idempotencyKey) {
|
|
2498
2401
|
const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
|
|
2499
|
-
const { body } = await
|
|
2402
|
+
const { body } = await enterprisePost2(
|
|
2500
2403
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2501
2404
|
"/campaigns",
|
|
2502
2405
|
campaignBody,
|
|
2503
2406
|
{ idempotencyKey }
|
|
2504
2407
|
);
|
|
2505
|
-
const created = unwrapDataResponse(body);
|
|
2408
|
+
const created = (0, import_core24.unwrapDataResponse)(body);
|
|
2506
2409
|
if (ctx.format === "json") {
|
|
2507
2410
|
printJson(created);
|
|
2508
2411
|
return;
|
|
@@ -2520,6 +2423,7 @@ function registerEnterpriseCampaignsCreate(parent) {
|
|
|
2520
2423
|
}
|
|
2521
2424
|
|
|
2522
2425
|
// src/commands/enterprise/campaigns/list.ts
|
|
2426
|
+
var import_core25 = require("@wport/core");
|
|
2523
2427
|
var STATUS_MAP2 = { open: 1, closed: 0 };
|
|
2524
2428
|
var MINIMAL_LIST_FIELDS3 = ["enc_id", "name", "status", "job_count"];
|
|
2525
2429
|
function mapStatusFlag2(raw) {
|
|
@@ -2539,7 +2443,7 @@ async function runEnterpriseCampaignsList(ctx, apiKey, flags) {
|
|
|
2539
2443
|
if (flags.fields && flags.minimal) {
|
|
2540
2444
|
throw new CliError("Use either --fields or --minimal, not both", ExitCode.InvalidArgument);
|
|
2541
2445
|
}
|
|
2542
|
-
const { body } = await
|
|
2446
|
+
const { body } = await enterpriseGet2(
|
|
2543
2447
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2544
2448
|
"/campaigns",
|
|
2545
2449
|
{
|
|
@@ -2549,7 +2453,7 @@ async function runEnterpriseCampaignsList(ctx, apiKey, flags) {
|
|
|
2549
2453
|
status: mapStatusFlag2(flags.status)
|
|
2550
2454
|
}
|
|
2551
2455
|
);
|
|
2552
|
-
const paged = asPaginatedBody(body);
|
|
2456
|
+
const paged = (0, import_core25.asPaginatedBody)(body);
|
|
2553
2457
|
const projection = flags.minimal ? MINIMAL_LIST_FIELDS3 : flags.fields ? parseFieldsList(flags.fields) : void 0;
|
|
2554
2458
|
if (projection || ctx.format === "json") {
|
|
2555
2459
|
printJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);
|
|
@@ -2582,15 +2486,16 @@ function registerEnterpriseCampaignsList(parent) {
|
|
|
2582
2486
|
|
|
2583
2487
|
// src/commands/enterprise/campaigns/lifecycle.ts
|
|
2584
2488
|
var import_node_crypto9 = require("crypto");
|
|
2489
|
+
var import_core26 = require("@wport/core");
|
|
2585
2490
|
async function runCampaignTransition(ctx, apiKey, encId, action, idempotencyKey) {
|
|
2586
2491
|
const trimmed = requireEncId(encId);
|
|
2587
|
-
const { body } = await
|
|
2492
|
+
const { body } = await enterprisePatch2(
|
|
2588
2493
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2589
2494
|
`/campaigns/${encodeURIComponent(trimmed)}/${action}`,
|
|
2590
2495
|
{},
|
|
2591
2496
|
{ idempotencyKey }
|
|
2592
2497
|
);
|
|
2593
|
-
const result = unwrapDataResponse(body);
|
|
2498
|
+
const result = (0, import_core26.unwrapDataResponse)(body);
|
|
2594
2499
|
if (ctx.format === "json") {
|
|
2595
2500
|
printJson(result);
|
|
2596
2501
|
return;
|
|
@@ -2616,16 +2521,17 @@ function registerEnterpriseCampaignsUnpublish(parent) {
|
|
|
2616
2521
|
|
|
2617
2522
|
// src/commands/enterprise/campaigns/update.ts
|
|
2618
2523
|
var import_node_crypto10 = require("crypto");
|
|
2524
|
+
var import_core27 = require("@wport/core");
|
|
2619
2525
|
async function runCampaignUpdate(ctx, apiKey, encId, source, idempotencyKey) {
|
|
2620
2526
|
const trimmed = requireEncId(encId);
|
|
2621
2527
|
const campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });
|
|
2622
|
-
const { body } = await
|
|
2528
|
+
const { body } = await enterprisePatch2(
|
|
2623
2529
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2624
2530
|
`/campaigns/${encodeURIComponent(trimmed)}`,
|
|
2625
2531
|
campaignBody,
|
|
2626
2532
|
{ idempotencyKey }
|
|
2627
2533
|
);
|
|
2628
|
-
const updated = unwrapDataResponse(body);
|
|
2534
|
+
const updated = (0, import_core27.unwrapDataResponse)(body);
|
|
2629
2535
|
if (ctx.format === "json") {
|
|
2630
2536
|
printJson(updated);
|
|
2631
2537
|
return;
|
|
@@ -2643,16 +2549,17 @@ function registerEnterpriseCampaignsUpdate(parent) {
|
|
|
2643
2549
|
}
|
|
2644
2550
|
|
|
2645
2551
|
// src/commands/enterprise/campaigns/view.ts
|
|
2552
|
+
var import_core28 = require("@wport/core");
|
|
2646
2553
|
async function runEnterpriseCampaignsView(ctx, apiKey, encId, flags) {
|
|
2647
2554
|
const trimmed = encId.trim();
|
|
2648
2555
|
if (!trimmed) {
|
|
2649
2556
|
throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
|
|
2650
2557
|
}
|
|
2651
|
-
const { body } = await
|
|
2558
|
+
const { body } = await enterpriseGet2(
|
|
2652
2559
|
{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },
|
|
2653
2560
|
`/campaigns/${encodeURIComponent(trimmed)}`
|
|
2654
2561
|
);
|
|
2655
|
-
const campaign = unwrapDataResponse(body);
|
|
2562
|
+
const campaign = (0, import_core28.unwrapDataResponse)(body);
|
|
2656
2563
|
if (flags.fields) {
|
|
2657
2564
|
printJson(pickPaths(campaign, parseFieldsList(flags.fields)));
|
|
2658
2565
|
return;
|
|
@@ -2744,6 +2651,8 @@ function describeIdentity(displayName, email) {
|
|
|
2744
2651
|
return "this device";
|
|
2745
2652
|
}
|
|
2746
2653
|
async function performLogin2(ctx, flags, deps = {}) {
|
|
2654
|
+
const banner = channelBanner();
|
|
2655
|
+
if (banner) process.stderr.write(banner);
|
|
2747
2656
|
const openBrowser = deps.openBrowser ?? openInBrowser;
|
|
2748
2657
|
const hostnameFn = deps.hostname ?? import_node_os.hostname;
|
|
2749
2658
|
if (!flags.force) {
|
|
@@ -2794,7 +2703,11 @@ function registerLoginCommand(program2) {
|
|
|
2794
2703
|
});
|
|
2795
2704
|
}
|
|
2796
2705
|
|
|
2706
|
+
// src/commands/auth/whoami.ts
|
|
2707
|
+
var import_core30 = require("@wport/core");
|
|
2708
|
+
|
|
2797
2709
|
// src/lib/personal-client.ts
|
|
2710
|
+
var import_core29 = require("@wport/core");
|
|
2798
2711
|
var EXPIRY_SKEW_MS = 3e4;
|
|
2799
2712
|
var NOT_LOGGED_IN_MESSAGE = "Not logged in. Run `wport login`.";
|
|
2800
2713
|
async function ensureFreshCredentials(opts) {
|
|
@@ -2823,7 +2736,7 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
|
|
|
2823
2736
|
const headers = {
|
|
2824
2737
|
Authorization: `Bearer ${accessToken}`,
|
|
2825
2738
|
"Accept-Language": opts.locale,
|
|
2826
|
-
"User-Agent": buildUserAgent(),
|
|
2739
|
+
"User-Agent": (0, import_core29.buildUserAgent)("wport-cli", "0.9.1"),
|
|
2827
2740
|
Accept: "application/json"
|
|
2828
2741
|
};
|
|
2829
2742
|
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
@@ -2833,7 +2746,7 @@ async function attemptRequest(opts, method, path, accessToken, body, query, extr
|
|
|
2833
2746
|
headers,
|
|
2834
2747
|
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
2835
2748
|
});
|
|
2836
|
-
const res = await fetchWithTimeout(request, opts.timeoutMs);
|
|
2749
|
+
const res = await (0, import_core29.fetchWithTimeout)(request, opts.timeoutMs);
|
|
2837
2750
|
const respBody = await res.json().catch(() => null);
|
|
2838
2751
|
return { status: res.status, body: respBody, headers: res.headers };
|
|
2839
2752
|
}
|
|
@@ -2865,13 +2778,13 @@ function personalDelete(opts, path, extra) {
|
|
|
2865
2778
|
}
|
|
2866
2779
|
function throwPersonalHttpError(status, body) {
|
|
2867
2780
|
if (status === 401) {
|
|
2868
|
-
const base = extractErrorMessage(body) ?? `HTTP ${status}`;
|
|
2781
|
+
const base = (0, import_core29.extractErrorMessage)(body) ?? `HTTP ${status}`;
|
|
2869
2782
|
throw new CliError(
|
|
2870
2783
|
`${base} \u2014 Your session may have expired or the request was rejected. Run \`wport login\` to sign in again.`,
|
|
2871
2784
|
ExitCode.ServerClientError
|
|
2872
2785
|
);
|
|
2873
2786
|
}
|
|
2874
|
-
throwForHttpStatus(status, body);
|
|
2787
|
+
(0, import_core29.throwForHttpStatus)(status, body);
|
|
2875
2788
|
}
|
|
2876
2789
|
function warnIfRateLimitLow2(headers) {
|
|
2877
2790
|
const remaining = Number(headers.get("x-ratelimit-remaining"));
|
|
@@ -2882,6 +2795,7 @@ function warnIfRateLimitLow2(headers) {
|
|
|
2882
2795
|
}
|
|
2883
2796
|
|
|
2884
2797
|
// src/commands/auth/whoami.ts
|
|
2798
|
+
var import_core31 = require("@wport/core");
|
|
2885
2799
|
var PLACEHOLDER = "\u2014";
|
|
2886
2800
|
function display(value) {
|
|
2887
2801
|
const clean = sanitizeForTerminal(value ?? "");
|
|
@@ -2889,8 +2803,8 @@ function display(value) {
|
|
|
2889
2803
|
}
|
|
2890
2804
|
async function performWhoami(ctx) {
|
|
2891
2805
|
const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
|
|
2892
|
-
const { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);
|
|
2893
|
-
const sessions = unwrapDataArray(body);
|
|
2806
|
+
const { body } = await personalGet(opts, import_core31.OAUTH_SESSIONS_BASE);
|
|
2807
|
+
const sessions = (0, import_core30.unwrapDataArray)(body);
|
|
2894
2808
|
const current = sessions.find((s) => s.is_current);
|
|
2895
2809
|
const localLoginAt = loadPersonalCredentials()?.session_created_at ?? null;
|
|
2896
2810
|
if (ctx.format === "json") {
|
|
@@ -2942,13 +2856,15 @@ function registerLogoutCommand(program2) {
|
|
|
2942
2856
|
}
|
|
2943
2857
|
|
|
2944
2858
|
// src/commands/sessions/list.ts
|
|
2859
|
+
var import_core32 = require("@wport/core");
|
|
2860
|
+
var import_core33 = require("@wport/core");
|
|
2945
2861
|
function formatDate5(value) {
|
|
2946
2862
|
return value ? String(value).slice(0, 10) : "";
|
|
2947
2863
|
}
|
|
2948
2864
|
async function runSessionsList(ctx) {
|
|
2949
2865
|
const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
|
|
2950
|
-
const { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);
|
|
2951
|
-
const sessions = unwrapDataArray(body);
|
|
2866
|
+
const { body } = await personalGet(opts, import_core33.OAUTH_SESSIONS_BASE);
|
|
2867
|
+
const sessions = (0, import_core32.unwrapDataArray)(body);
|
|
2952
2868
|
if (ctx.format === "json") {
|
|
2953
2869
|
printJson(sessions);
|
|
2954
2870
|
return;
|
|
@@ -2972,6 +2888,8 @@ function registerSessionsList(parent) {
|
|
|
2972
2888
|
}
|
|
2973
2889
|
|
|
2974
2890
|
// src/commands/sessions/revoke.ts
|
|
2891
|
+
var import_core34 = require("@wport/core");
|
|
2892
|
+
var import_core35 = require("@wport/core");
|
|
2975
2893
|
async function runSessionsRevoke(ctx, encId, allOthers) {
|
|
2976
2894
|
const hasEncId = encId !== void 0;
|
|
2977
2895
|
if (hasEncId === allOthers) {
|
|
@@ -2979,8 +2897,8 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
|
|
|
2979
2897
|
}
|
|
2980
2898
|
const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
|
|
2981
2899
|
if (allOthers) {
|
|
2982
|
-
const { body } = await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/others`);
|
|
2983
|
-
const result = unwrapDataResponse(body);
|
|
2900
|
+
const { body } = await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/others`);
|
|
2901
|
+
const result = (0, import_core35.unwrapDataResponse)(body);
|
|
2984
2902
|
if (ctx.format === "json") {
|
|
2985
2903
|
printJson(result);
|
|
2986
2904
|
return;
|
|
@@ -2991,7 +2909,7 @@ async function runSessionsRevoke(ctx, encId, allOthers) {
|
|
|
2991
2909
|
}
|
|
2992
2910
|
const trimmed = encId.trim();
|
|
2993
2911
|
if (!trimmed) throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
|
|
2994
|
-
await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
|
|
2912
|
+
await personalDelete(opts, `${import_core34.OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);
|
|
2995
2913
|
if (ctx.format === "json") {
|
|
2996
2914
|
printJson({ enc_id: trimmed, revoked: true });
|
|
2997
2915
|
return;
|
|
@@ -3013,949 +2931,19 @@ function registerSessionsCommand(program2) {
|
|
|
3013
2931
|
registerSessionsRevoke(sessions);
|
|
3014
2932
|
}
|
|
3015
2933
|
|
|
3016
|
-
// src/lib/resume-schema/definition.ts
|
|
3017
|
-
var DEGREE_TYPE_VALUES = ["phd", "master", "bachelor", "associate", "senior_high", "junior_or_below"];
|
|
3018
|
-
var EDUCATION_STATUS_VALUES = ["graduated", "dropped", "studying"];
|
|
3019
|
-
var JOB_FEATURE_CODE_VALUES = ["dispatch", "executive", "full_time", "internship", "part_time"];
|
|
3020
|
-
var WORKING_HOUR_TYPE_VALUES = ["day_shift", "evening_shift", "night_shift", "rotating_shift", "weekend_shift"];
|
|
3021
|
-
var AVAILABLE_START_TYPE_VALUES = ["available_after_hired", "available_custom_date"];
|
|
3022
|
-
var AVAILABLE_START_PERIOD_VALUES = ["week", "two_weeks", "month", "two_months", "three_months", "anytime"];
|
|
3023
|
-
var SALARY_EXPECTATION_TYPE_VALUES = ["negotiable", "company_rules", "custom"];
|
|
3024
|
-
var SALARY_UNIT_VALUES = ["hourly", "daily", "monthly", "yearly"];
|
|
3025
|
-
var PROFICIENCY_LEVEL_VALUES = ["beginner", "daily_convo", "proficient", "native"];
|
|
3026
|
-
var JOB_STATUS_VALUES = ["employed", "military", "student", "unemployed"];
|
|
3027
|
-
var VEHICLE_OR_LICENSE_TYPE_VALUES = [
|
|
3028
|
-
"bus",
|
|
3029
|
-
"heavy_motorcycle",
|
|
3030
|
-
"light_motorcycle",
|
|
3031
|
-
"light_car",
|
|
3032
|
-
"pro_bus",
|
|
3033
|
-
"pro_small_car",
|
|
3034
|
-
"pro_trailer",
|
|
3035
|
-
"pro_truck",
|
|
3036
|
-
"scooter",
|
|
3037
|
-
"trailer",
|
|
3038
|
-
"truck"
|
|
3039
|
-
];
|
|
3040
|
-
var CURRENT_YEAR = (/* @__PURE__ */ new Date()).getFullYear();
|
|
3041
|
-
var TOP_LEVEL_FIELDS = {
|
|
3042
|
-
name: {
|
|
3043
|
-
type: "string",
|
|
3044
|
-
required: true,
|
|
3045
|
-
minLength: 1,
|
|
3046
|
-
maxLength: 100,
|
|
3047
|
-
description: "\u5C65\u6B77\u540D\u7A31",
|
|
3048
|
-
example: "\u6211\u7684\u5C65\u6B77\u540D\u7A31"
|
|
3049
|
-
}
|
|
3050
|
-
};
|
|
3051
|
-
var educationSection = {
|
|
3052
|
-
key: "education",
|
|
3053
|
-
kind: "array",
|
|
3054
|
-
description: "\u5B78\u6B77\uFF0C\u53EF\u591A\u7B46",
|
|
3055
|
-
fields: {
|
|
3056
|
-
school_name: {
|
|
3057
|
-
type: "string",
|
|
3058
|
-
required: true,
|
|
3059
|
-
minLength: 1,
|
|
3060
|
-
maxLength: 80,
|
|
3061
|
-
description: "\u5B78\u6821\u540D\u7A31",
|
|
3062
|
-
example: "National Taiwan University"
|
|
3063
|
-
},
|
|
3064
|
-
degree_code: {
|
|
3065
|
-
type: "enum",
|
|
3066
|
-
required: true,
|
|
3067
|
-
enumValues: DEGREE_TYPE_VALUES,
|
|
3068
|
-
description: "\u5B78\u6B77\u7B49\u7D1A",
|
|
3069
|
-
example: "bachelor"
|
|
3070
|
-
},
|
|
3071
|
-
department: {
|
|
3072
|
-
type: "string",
|
|
3073
|
-
required: true,
|
|
3074
|
-
minLength: 1,
|
|
3075
|
-
maxLength: 80,
|
|
3076
|
-
description: "\u4E3B\u4FEE\u79D1\u7CFB\u540D\u7A31",
|
|
3077
|
-
example: "Computer Science"
|
|
3078
|
-
},
|
|
3079
|
-
minor_department: {
|
|
3080
|
-
type: "string",
|
|
3081
|
-
required: false,
|
|
3082
|
-
minLength: 1,
|
|
3083
|
-
maxLength: 80,
|
|
3084
|
-
description: "\u526F\u4FEE\u79D1\u7CFB\u540D\u7A31",
|
|
3085
|
-
example: "Mathematics"
|
|
3086
|
-
},
|
|
3087
|
-
department_class_code: {
|
|
3088
|
-
type: "string",
|
|
3089
|
-
required: false,
|
|
3090
|
-
description: "\u4E3B\u4FEE\u79D1\u7CFB\u985E\u5225\u4EE3\u78BC",
|
|
3091
|
-
example: "engineering"
|
|
3092
|
-
},
|
|
3093
|
-
minor_department_class_code: {
|
|
3094
|
-
type: "string",
|
|
3095
|
-
required: false,
|
|
3096
|
-
description: "\u526F\u4FEE\u79D1\u7CFB\u985E\u5225\u4EE3\u78BC",
|
|
3097
|
-
example: "industry_machinery"
|
|
3098
|
-
},
|
|
3099
|
-
edu_status_code: {
|
|
3100
|
-
type: "enum",
|
|
3101
|
-
required: true,
|
|
3102
|
-
enumValues: EDUCATION_STATUS_VALUES,
|
|
3103
|
-
description: "\u5C31\u5B78\u72C0\u614B",
|
|
3104
|
-
example: "studying"
|
|
3105
|
-
},
|
|
3106
|
-
start_year: {
|
|
3107
|
-
type: "integer",
|
|
3108
|
-
required: true,
|
|
3109
|
-
min: 1900,
|
|
3110
|
-
max: CURRENT_YEAR,
|
|
3111
|
-
description: "\u5165\u5B78\u5E74\u4EFD",
|
|
3112
|
-
example: 2015
|
|
3113
|
-
},
|
|
3114
|
-
start_month: {
|
|
3115
|
-
type: "integer",
|
|
3116
|
-
required: true,
|
|
3117
|
-
min: 1,
|
|
3118
|
-
max: 12,
|
|
3119
|
-
description: "\u5165\u5B78\u6708\u4EFD",
|
|
3120
|
-
example: 9
|
|
3121
|
-
},
|
|
3122
|
-
end_year: {
|
|
3123
|
-
type: "integer",
|
|
3124
|
-
required: false,
|
|
3125
|
-
min: 1900,
|
|
3126
|
-
max: CURRENT_YEAR,
|
|
3127
|
-
description: "\u7562\u696D\u5E74\u4EFD\uFF08edu_status_code \u70BA graduated/dropped \u6642\u5FC5\u586B\uFF09",
|
|
3128
|
-
example: 2019
|
|
3129
|
-
},
|
|
3130
|
-
end_month: {
|
|
3131
|
-
type: "integer",
|
|
3132
|
-
required: false,
|
|
3133
|
-
min: 1,
|
|
3134
|
-
max: 12,
|
|
3135
|
-
description: "\u7562\u696D\u6708\u4EFD\uFF08edu_status_code \u70BA graduated/dropped \u6642\u5FC5\u586B\uFF09",
|
|
3136
|
-
example: 6
|
|
3137
|
-
},
|
|
3138
|
-
experience: {
|
|
3139
|
-
type: "string",
|
|
3140
|
-
required: false,
|
|
3141
|
-
htmlText: true,
|
|
3142
|
-
maxLength: 2e3,
|
|
3143
|
-
description: "\u5728\u6821\u7D93\u6B77\uFF08\u5BCC\u6587\u672C\uFF09",
|
|
3144
|
-
example: "<p>Served as the president of the student council.</p>"
|
|
3145
|
-
}
|
|
3146
|
-
},
|
|
3147
|
-
crossFieldRules: [
|
|
3148
|
-
{
|
|
3149
|
-
name: "education-graduation-date",
|
|
3150
|
-
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.',
|
|
3151
|
-
check(item) {
|
|
3152
|
-
const status = item.edu_status_code;
|
|
3153
|
-
if (status !== "graduated" && status !== "dropped") return true;
|
|
3154
|
-
const { end_year, end_month, start_year, start_month } = item;
|
|
3155
|
-
if (typeof end_year !== "number" || typeof end_month !== "number") return false;
|
|
3156
|
-
if (typeof start_year !== "number" || typeof start_month !== "number") return true;
|
|
3157
|
-
const start = new Date(start_year, start_month - 1);
|
|
3158
|
-
const end = new Date(end_year, end_month - 1);
|
|
3159
|
-
return end > start && end <= /* @__PURE__ */ new Date();
|
|
3160
|
-
}
|
|
3161
|
-
}
|
|
3162
|
-
]
|
|
3163
|
-
};
|
|
3164
|
-
var workExperienceSection = {
|
|
3165
|
-
key: "work_experience",
|
|
3166
|
-
kind: "wrapper",
|
|
3167
|
-
description: "\u5DE5\u4F5C\u7D93\u9A57\uFF08\u5305\u88DD\u5C64\u542B\u300C\u7121\u5DE5\u4F5C\u7D93\u9A57\u300D\u65D7\u6A19\uFF09",
|
|
3168
|
-
wrapperFields: {
|
|
3169
|
-
has_no_work_experience: {
|
|
3170
|
-
type: "boolean",
|
|
3171
|
-
required: false,
|
|
3172
|
-
description: "\u662F\u5426\u7121\u5DE5\u4F5C\u7D93\u9A57\uFF08true \u6642 items \u5FC5\u70BA\u7A7A\u9663\u5217\uFF09",
|
|
3173
|
-
example: false
|
|
3174
|
-
}
|
|
3175
|
-
},
|
|
3176
|
-
fields: {
|
|
3177
|
-
job_title: {
|
|
3178
|
-
type: "string",
|
|
3179
|
-
required: true,
|
|
3180
|
-
maxLength: 100,
|
|
3181
|
-
description: "\u8077\u7A31",
|
|
3182
|
-
example: "Software Engineer"
|
|
3183
|
-
},
|
|
3184
|
-
job_type: {
|
|
3185
|
-
type: "enum",
|
|
3186
|
-
required: true,
|
|
3187
|
-
enumValues: JOB_FEATURE_CODE_VALUES,
|
|
3188
|
-
description: "\u8077\u52D9\u985E\u578B",
|
|
3189
|
-
example: "full_time"
|
|
3190
|
-
},
|
|
3191
|
-
company_name: {
|
|
3192
|
-
type: "string",
|
|
3193
|
-
required: true,
|
|
3194
|
-
maxLength: 200,
|
|
3195
|
-
description: "\u516C\u53F8\u540D\u7A31",
|
|
3196
|
-
example: "Tech Corp"
|
|
3197
|
-
},
|
|
3198
|
-
start_year: {
|
|
3199
|
-
type: "integer",
|
|
3200
|
-
required: true,
|
|
3201
|
-
min: 1900,
|
|
3202
|
-
max: CURRENT_YEAR,
|
|
3203
|
-
description: "\u4EFB\u8077\u958B\u59CB\u5E74\u4EFD",
|
|
3204
|
-
example: 2020
|
|
3205
|
-
},
|
|
3206
|
-
start_month: {
|
|
3207
|
-
type: "integer",
|
|
3208
|
-
required: true,
|
|
3209
|
-
min: 1,
|
|
3210
|
-
max: 12,
|
|
3211
|
-
description: "\u4EFB\u8077\u958B\u59CB\u6708\u4EFD",
|
|
3212
|
-
example: 1
|
|
3213
|
-
},
|
|
3214
|
-
end_year: {
|
|
3215
|
-
type: "integer",
|
|
3216
|
-
required: false,
|
|
3217
|
-
min: 1900,
|
|
3218
|
-
max: CURRENT_YEAR,
|
|
3219
|
-
description: "\u4EFB\u8077\u7D50\u675F\u5E74\u4EFD\uFF08is_current \u70BA false \u6642\u5FC5\u586B\uFF09",
|
|
3220
|
-
example: 2022
|
|
3221
|
-
},
|
|
3222
|
-
end_month: {
|
|
3223
|
-
type: "integer",
|
|
3224
|
-
required: false,
|
|
3225
|
-
min: 1,
|
|
3226
|
-
max: 12,
|
|
3227
|
-
description: "\u4EFB\u8077\u7D50\u675F\u6708\u4EFD\uFF08is_current \u70BA false \u6642\u5FC5\u586B\uFF09",
|
|
3228
|
-
example: 12
|
|
3229
|
-
},
|
|
3230
|
-
is_current: {
|
|
3231
|
-
type: "boolean",
|
|
3232
|
-
required: true,
|
|
3233
|
-
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",
|
|
3234
|
-
example: false
|
|
3235
|
-
},
|
|
3236
|
-
job_description: {
|
|
3237
|
-
type: "string",
|
|
3238
|
-
required: false,
|
|
3239
|
-
htmlText: true,
|
|
3240
|
-
maxLength: 1e4,
|
|
3241
|
-
description: "\u5DE5\u4F5C\u5167\u5BB9\u63CF\u8FF0\uFF08\u5BCC\u6587\u672C\uFF09",
|
|
3242
|
-
example: "<p>Built and maintained internal developer tools.</p>"
|
|
3243
|
-
}
|
|
3244
|
-
},
|
|
3245
|
-
crossFieldRules: [
|
|
3246
|
-
{
|
|
3247
|
-
name: "work-experience-end-date",
|
|
3248
|
-
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.",
|
|
3249
|
-
check(item) {
|
|
3250
|
-
if (typeof item.is_current !== "boolean") return true;
|
|
3251
|
-
if (item.is_current) {
|
|
3252
|
-
return item.end_year === void 0 && item.end_month === void 0;
|
|
3253
|
-
}
|
|
3254
|
-
const { end_year, end_month, start_year, start_month } = item;
|
|
3255
|
-
if (typeof end_year !== "number" || typeof end_month !== "number") return false;
|
|
3256
|
-
if (typeof start_year !== "number" || typeof start_month !== "number") return true;
|
|
3257
|
-
const start = new Date(start_year, start_month - 1);
|
|
3258
|
-
const end = new Date(end_year, end_month - 1);
|
|
3259
|
-
return end >= start && end <= /* @__PURE__ */ new Date();
|
|
3260
|
-
}
|
|
3261
|
-
},
|
|
3262
|
-
{
|
|
3263
|
-
name: "work-experience-no-experience-items-empty",
|
|
3264
|
-
message: "When has_no_work_experience is true, items must be an empty array.",
|
|
3265
|
-
check(wrapper) {
|
|
3266
|
-
if (wrapper.has_no_work_experience !== true) return true;
|
|
3267
|
-
return Array.isArray(wrapper.items) && wrapper.items.length === 0;
|
|
3268
|
-
}
|
|
3269
|
-
}
|
|
3270
|
-
]
|
|
3271
|
-
};
|
|
3272
|
-
var certificateSection = {
|
|
3273
|
-
key: "certificate",
|
|
3274
|
-
kind: "array",
|
|
3275
|
-
description: "\u8B49\u7167\uFF0C\u53EF\u591A\u7B46",
|
|
3276
|
-
fields: {
|
|
3277
|
-
certificate_code: {
|
|
3278
|
-
type: "string",
|
|
3279
|
-
required: true,
|
|
3280
|
-
description: "\u8B49\u7167\u4EE3\u78BC",
|
|
3281
|
-
example: "4001001014"
|
|
3282
|
-
},
|
|
3283
|
-
issue_year: {
|
|
3284
|
-
type: "integer",
|
|
3285
|
-
required: true,
|
|
3286
|
-
min: 1900,
|
|
3287
|
-
max: 2100,
|
|
3288
|
-
description: "\u8D77\u59CB\u5E74\u4EFD\uFF08\u683C\u5F0F\uFF1AYYYY\uFF09",
|
|
3289
|
-
example: 2023
|
|
3290
|
-
},
|
|
3291
|
-
issue_month: {
|
|
3292
|
-
type: "integer",
|
|
3293
|
-
required: true,
|
|
3294
|
-
min: 1,
|
|
3295
|
-
max: 12,
|
|
3296
|
-
description: "\u8D77\u59CB\u6708\u4EFD\uFF08\u683C\u5F0F\uFF1AMM\uFF09",
|
|
3297
|
-
example: 1
|
|
3298
|
-
},
|
|
3299
|
-
expiry_year: {
|
|
3300
|
-
type: "integer",
|
|
3301
|
-
required: false,
|
|
3302
|
-
min: 1900,
|
|
3303
|
-
max: 2100,
|
|
3304
|
-
description: "\u5230\u671F\u5E74\u4EFD\uFF08is_permanent \u70BA false \u6642\u5FC5\u586B\uFF09",
|
|
3305
|
-
example: 2025
|
|
3306
|
-
},
|
|
3307
|
-
expiry_month: {
|
|
3308
|
-
type: "integer",
|
|
3309
|
-
required: false,
|
|
3310
|
-
min: 1,
|
|
3311
|
-
max: 12,
|
|
3312
|
-
description: "\u5230\u671F\u6708\u4EFD\uFF08is_permanent \u70BA false \u6642\u5FC5\u586B\uFF09",
|
|
3313
|
-
example: 1
|
|
3314
|
-
},
|
|
3315
|
-
is_permanent: {
|
|
3316
|
-
type: "boolean",
|
|
3317
|
-
required: true,
|
|
3318
|
-
description: "\u662F\u5426\u6C38\u4E45\u6709\u6548",
|
|
3319
|
-
example: false
|
|
3320
|
-
}
|
|
3321
|
-
},
|
|
3322
|
-
crossFieldRules: [
|
|
3323
|
-
{
|
|
3324
|
-
name: "certificate-expiry-required",
|
|
3325
|
-
message: "When is_permanent is false, expiry_year/expiry_month are required and must not be earlier than issue_year/issue_month.",
|
|
3326
|
-
check(item) {
|
|
3327
|
-
if (item.is_permanent !== false) return true;
|
|
3328
|
-
const { expiry_year, expiry_month, issue_year, issue_month } = item;
|
|
3329
|
-
if (typeof expiry_year !== "number" || typeof expiry_month !== "number") return false;
|
|
3330
|
-
if (typeof issue_year !== "number" || typeof issue_month !== "number") return true;
|
|
3331
|
-
return expiry_year * 12 + expiry_month >= issue_year * 12 + issue_month;
|
|
3332
|
-
}
|
|
3333
|
-
}
|
|
3334
|
-
]
|
|
3335
|
-
};
|
|
3336
|
-
var languageSection = {
|
|
3337
|
-
key: "language",
|
|
3338
|
-
kind: "array",
|
|
3339
|
-
description: "\u8A9E\u8A00\u80FD\u529B\uFF0C\u53EF\u591A\u7B46",
|
|
3340
|
-
fields: {
|
|
3341
|
-
code: {
|
|
3342
|
-
type: "string",
|
|
3343
|
-
required: true,
|
|
3344
|
-
description: "\u6280\u80FD\u8A9E\u8A00\u4EE3\u78BC\uFF08\u5C0D\u61C9 skill_languages \u8CC7\u6599\u8868\u4E2D\u7684 code\uFF09",
|
|
3345
|
-
example: "Chinese"
|
|
3346
|
-
},
|
|
3347
|
-
proficiency_level_code: {
|
|
3348
|
-
type: "enum",
|
|
3349
|
-
required: true,
|
|
3350
|
-
enumValues: PROFICIENCY_LEVEL_VALUES,
|
|
3351
|
-
description: "\u719F\u7DF4\u7A0B\u5EA6",
|
|
3352
|
-
example: "proficient"
|
|
3353
|
-
}
|
|
3354
|
-
}
|
|
3355
|
-
};
|
|
3356
|
-
var professionalSkillsSection = {
|
|
3357
|
-
key: "professional_skills",
|
|
3358
|
-
kind: "object",
|
|
3359
|
-
description: "\u5C08\u696D\u6280\u80FD",
|
|
3360
|
-
fields: {
|
|
3361
|
-
tech_tool_codes: {
|
|
3362
|
-
type: "string[]",
|
|
3363
|
-
required: true,
|
|
3364
|
-
minItems: 1,
|
|
3365
|
-
maxItems: 10,
|
|
3366
|
-
description: "\u5DE5\u5177\u4EE3\u78BC\u9663\u5217\uFF08\u6700\u591A 10 \u500B\uFF09",
|
|
3367
|
-
example: ["12001001045", "12001001044"]
|
|
3368
|
-
},
|
|
3369
|
-
other_tool_description: {
|
|
3370
|
-
type: "string",
|
|
3371
|
-
required: false,
|
|
3372
|
-
htmlText: true,
|
|
3373
|
-
maxLength: 2e3,
|
|
3374
|
-
description: "\u5176\u4ED6\u64C5\u9577\u5DE5\u5177\u63CF\u8FF0\uFF08\u5BCC\u6587\u672C\uFF0C\u5BE6\u969B\u6587\u5B57\u4E0A\u9650 2000 \u5B57\uFF09",
|
|
3375
|
-
example: "\u719F\u6089 Photoshop \u548C Illustrator"
|
|
3376
|
-
},
|
|
3377
|
-
job_skill_codes: {
|
|
3378
|
-
type: "string[]",
|
|
3379
|
-
required: true,
|
|
3380
|
-
minItems: 1,
|
|
3381
|
-
maxItems: 10,
|
|
3382
|
-
description: "\u6280\u80FD\u4EE3\u78BC\u9663\u5217\uFF08\u6700\u591A 10 \u500B\uFF09",
|
|
3383
|
-
example: ["5001001006", "5001001007"]
|
|
3384
|
-
},
|
|
3385
|
-
other_job_skill_description: {
|
|
3386
|
-
type: "string",
|
|
3387
|
-
required: false,
|
|
3388
|
-
htmlText: true,
|
|
3389
|
-
maxLength: 2e3,
|
|
3390
|
-
description: "\u5176\u4ED6\u5DE5\u4F5C\u6280\u80FD\u63CF\u8FF0\uFF08\u5BCC\u6587\u672C\uFF0C\u5BE6\u969B\u6587\u5B57\u4E0A\u9650 2000 \u5B57\uFF09",
|
|
3391
|
-
example: "\u719F\u6089 Scrum \u548C\u654F\u6377\u958B\u767C\u6D41\u7A0B"
|
|
3392
|
-
}
|
|
3393
|
-
}
|
|
3394
|
-
};
|
|
3395
|
-
var autobiographySection = {
|
|
3396
|
-
key: "autobiography",
|
|
3397
|
-
kind: "scalar",
|
|
3398
|
-
description: "\u81EA\u50B3\uFF08\u7D14\u5B57\u4E32\u7BC0\uFF09",
|
|
3399
|
-
valueDef: {
|
|
3400
|
-
type: "string",
|
|
3401
|
-
required: true,
|
|
3402
|
-
htmlText: true,
|
|
3403
|
-
maxLength: 3e3,
|
|
3404
|
-
description: "\u81EA\u50B3\u5167\u5BB9\uFF08\u5BCC\u6587\u672C\u683C\u5F0F\uFF09\uFF0C\u5BE6\u969B\u6587\u5B57\u9577\u5EA6\u4E0D\u8D85\u904E 3000 \u5B57",
|
|
3405
|
-
example: "<p>This is my <strong>autobiography</strong>.</p>"
|
|
3406
|
-
}
|
|
3407
|
-
};
|
|
3408
|
-
var jobConditionSection = {
|
|
3409
|
-
key: "job_condition",
|
|
3410
|
-
kind: "object",
|
|
3411
|
-
description: "\u5E0C\u671B\u5DE5\u4F5C\u689D\u4EF6",
|
|
3412
|
-
fields: {
|
|
3413
|
-
feature_codes: {
|
|
3414
|
-
type: "enum[]",
|
|
3415
|
-
required: true,
|
|
3416
|
-
enumValues: JOB_FEATURE_CODE_VALUES,
|
|
3417
|
-
description: "\u5E0C\u671B\u6027\u8CEA\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
|
|
3418
|
-
example: ["full_time", "part_time"]
|
|
3419
|
-
},
|
|
3420
|
-
working_hour_type_codes: {
|
|
3421
|
-
type: "enum[]",
|
|
3422
|
-
required: true,
|
|
3423
|
-
enumValues: WORKING_HOUR_TYPE_VALUES,
|
|
3424
|
-
description: "\u4E0A\u73ED\u6642\u6BB5\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
|
|
3425
|
-
example: ["day_shift", "evening_shift"]
|
|
3426
|
-
},
|
|
3427
|
-
available_start_type_code: {
|
|
3428
|
-
type: "enum",
|
|
3429
|
-
required: true,
|
|
3430
|
-
enumValues: AVAILABLE_START_TYPE_VALUES,
|
|
3431
|
-
description: "\u53EF\u4E0A\u73ED\u6642\u9593\u985E\u578B\u4EE3\u78BC",
|
|
3432
|
-
example: "available_after_hired"
|
|
3433
|
-
},
|
|
3434
|
-
available_start_date: {
|
|
3435
|
-
type: "date",
|
|
3436
|
-
required: false,
|
|
3437
|
-
description: "\u81EA\u8A02\u53EF\u4E0A\u73ED\u65E5\u671F\uFF08available_start_type_code \u70BA available_custom_date \u6642\u5FC5\u586B\uFF09",
|
|
3438
|
-
example: "2025-05-01"
|
|
3439
|
-
},
|
|
3440
|
-
available_start_period_code: {
|
|
3441
|
-
type: "enum",
|
|
3442
|
-
required: false,
|
|
3443
|
-
enumValues: AVAILABLE_START_PERIOD_VALUES,
|
|
3444
|
-
description: "\u9304\u53D6\u5F8C\u671F\u9593\u4EE3\u78BC\uFF08available_start_type_code \u70BA available_after_hired \u6642\u5FC5\u586B\uFF09",
|
|
3445
|
-
example: "week"
|
|
3446
|
-
},
|
|
3447
|
-
salary_expectation_type_code: {
|
|
3448
|
-
type: "enum",
|
|
3449
|
-
required: true,
|
|
3450
|
-
enumValues: SALARY_EXPECTATION_TYPE_VALUES,
|
|
3451
|
-
description: "\u5E0C\u671B\u5F85\u9047\u985E\u578B\u4EE3\u78BC",
|
|
3452
|
-
example: "negotiable"
|
|
3453
|
-
},
|
|
3454
|
-
salary_unit_code: {
|
|
3455
|
-
type: "enum",
|
|
3456
|
-
required: false,
|
|
3457
|
-
enumValues: SALARY_UNIT_VALUES,
|
|
3458
|
-
description: "\u85AA\u8CC7\u55AE\u4F4D\u4EE3\u78BC\uFF08salary_expectation_type_code \u70BA custom \u6642\u5FC5\u586B\uFF09",
|
|
3459
|
-
example: "monthly"
|
|
3460
|
-
},
|
|
3461
|
-
salary_amount: {
|
|
3462
|
-
type: "number",
|
|
3463
|
-
required: false,
|
|
3464
|
-
min: 0,
|
|
3465
|
-
description: "\u81EA\u8A02\u85AA\u8CC7\u91D1\u984D\uFF08salary_expectation_type_code \u70BA custom \u6642\u5FC5\u586B\uFF09",
|
|
3466
|
-
example: 5e4
|
|
3467
|
-
},
|
|
3468
|
-
expected_salary_currency_code: {
|
|
3469
|
-
type: "string",
|
|
3470
|
-
required: false,
|
|
3471
|
-
minLength: 3,
|
|
3472
|
-
maxLength: 3,
|
|
3473
|
-
description: "\u671F\u671B\u85AA\u8CC7\u5E63\u5225\u4EE3\u78BC\uFF08ISO 4217\uFF0Csalary_expectation_type_code \u70BA custom \u6642\u5FC5\u586B\uFF09",
|
|
3474
|
-
example: "TWD"
|
|
3475
|
-
},
|
|
3476
|
-
area_codes: {
|
|
3477
|
-
type: "string[]",
|
|
3478
|
-
required: false,
|
|
3479
|
-
description: "\u5E0C\u671B\u5730\u9EDE\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
|
|
3480
|
-
example: ["6001005009", "6001006008"]
|
|
3481
|
-
},
|
|
3482
|
-
job_classification_codes: {
|
|
3483
|
-
type: "string[]",
|
|
3484
|
-
required: false,
|
|
3485
|
-
description: "\u5E0C\u671B\u8077\u985E\u4EE3\u78BC\uFF08\u591A\u9078\uFF09",
|
|
3486
|
-
example: ["2002002006", "2003002007"]
|
|
3487
|
-
}
|
|
3488
|
-
},
|
|
3489
|
-
crossFieldRules: [
|
|
3490
|
-
{
|
|
3491
|
-
name: "job-condition-available-start",
|
|
3492
|
-
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.',
|
|
3493
|
-
check(item) {
|
|
3494
|
-
const code = item.available_start_type_code;
|
|
3495
|
-
if (code === "available_custom_date") {
|
|
3496
|
-
const v = item.available_start_date;
|
|
3497
|
-
return v !== void 0 && v !== null && v !== "";
|
|
3498
|
-
}
|
|
3499
|
-
if (code === "available_after_hired") {
|
|
3500
|
-
const v = item.available_start_period_code;
|
|
3501
|
-
return v !== void 0 && v !== null && v !== "";
|
|
3502
|
-
}
|
|
3503
|
-
return true;
|
|
3504
|
-
}
|
|
3505
|
-
},
|
|
3506
|
-
{
|
|
3507
|
-
name: "job-condition-salary-expectation",
|
|
3508
|
-
message: 'When salary_expectation_type_code is "custom", salary_unit_code, salary_amount, and expected_salary_currency_code are all required.',
|
|
3509
|
-
check(item) {
|
|
3510
|
-
if (item.salary_expectation_type_code !== "custom") return true;
|
|
3511
|
-
const hasUnit = item.salary_unit_code !== void 0 && item.salary_unit_code !== null && item.salary_unit_code !== "";
|
|
3512
|
-
const hasAmount = typeof item.salary_amount === "number";
|
|
3513
|
-
const hasCurrency = typeof item.expected_salary_currency_code === "string" && item.expected_salary_currency_code.length > 0;
|
|
3514
|
-
return hasUnit && hasAmount && hasCurrency;
|
|
3515
|
-
}
|
|
3516
|
-
}
|
|
3517
|
-
]
|
|
3518
|
-
};
|
|
3519
|
-
var portfolioLinksSection = {
|
|
3520
|
-
key: "portfolio_links",
|
|
3521
|
-
kind: "array",
|
|
3522
|
-
maxItems: 5,
|
|
3523
|
-
description: "\u4F5C\u54C1\u96C6\u9023\u7D50\uFF0C\u6700\u591A 5 \u7B46",
|
|
3524
|
-
fields: {
|
|
3525
|
-
link_title: {
|
|
3526
|
-
type: "string",
|
|
3527
|
-
required: true,
|
|
3528
|
-
minLength: 1,
|
|
3529
|
-
maxLength: 50,
|
|
3530
|
-
description: "\u4F5C\u54C1\u96C6\u6A19\u984C",
|
|
3531
|
-
example: "My Portfolio"
|
|
3532
|
-
},
|
|
3533
|
-
link_url: {
|
|
3534
|
-
type: "string",
|
|
3535
|
-
required: true,
|
|
3536
|
-
minLength: 1,
|
|
3537
|
-
maxLength: 2083,
|
|
3538
|
-
description: "\u4F5C\u54C1\u96C6\u9023\u7D50\uFF08URL\uFF09",
|
|
3539
|
-
example: "https://myportfolio.com"
|
|
3540
|
-
}
|
|
3541
|
-
}
|
|
3542
|
-
};
|
|
3543
|
-
var backgroundSection = {
|
|
3544
|
-
key: "background",
|
|
3545
|
-
kind: "object",
|
|
3546
|
-
description: "\u80CC\u666F\u8CC7\u8A0A\uFF08\u99D5\u7167\uFF0F\u8ECA\u8F1B\uFF0F\u5175\u5F79\u72C0\u614B\uFF09",
|
|
3547
|
-
fields: {
|
|
3548
|
-
// identity_types(身分種類)刻意不列:屬會員個資唯讀例外(pm_27 欄位),寫聚合不收,
|
|
3549
|
-
// validate 對此鍵給 read-only 專屬訊息而非泛用 unknown-field(見 design doc §4.2)。
|
|
3550
|
-
driving_license_types: {
|
|
3551
|
-
type: "enum[]",
|
|
3552
|
-
required: false,
|
|
3553
|
-
enumValues: VEHICLE_OR_LICENSE_TYPE_VALUES,
|
|
3554
|
-
description: "\u99D5\u99DB\u57F7\u7167\u985E\u578B\u5217\u8868",
|
|
3555
|
-
example: ["scooter", "light_motorcycle"]
|
|
3556
|
-
},
|
|
3557
|
-
vehicle_types: {
|
|
3558
|
-
type: "enum[]",
|
|
3559
|
-
required: false,
|
|
3560
|
-
enumValues: VEHICLE_OR_LICENSE_TYPE_VALUES,
|
|
3561
|
-
description: "\u8ECA\u8F1B\u985E\u578B\u5217\u8868",
|
|
3562
|
-
example: ["scooter"]
|
|
3563
|
-
},
|
|
3564
|
-
job_status: {
|
|
3565
|
-
type: "enum",
|
|
3566
|
-
required: false,
|
|
3567
|
-
enumValues: JOB_STATUS_VALUES,
|
|
3568
|
-
description: "\u5DE5\u4F5C\u72C0\u614B",
|
|
3569
|
-
example: "employed"
|
|
3570
|
-
}
|
|
3571
|
-
}
|
|
3572
|
-
};
|
|
3573
|
-
var SECTIONS = [
|
|
3574
|
-
educationSection,
|
|
3575
|
-
workExperienceSection,
|
|
3576
|
-
certificateSection,
|
|
3577
|
-
languageSection,
|
|
3578
|
-
professionalSkillsSection,
|
|
3579
|
-
autobiographySection,
|
|
3580
|
-
jobConditionSection,
|
|
3581
|
-
portfolioLinksSection,
|
|
3582
|
-
backgroundSection
|
|
3583
|
-
];
|
|
3584
|
-
function getSection(key) {
|
|
3585
|
-
return SECTIONS.find((s) => s.key === key);
|
|
3586
|
-
}
|
|
3587
|
-
|
|
3588
|
-
// src/lib/resume-schema/to-json-schema.ts
|
|
3589
|
-
var JSON_SCHEMA_DRAFT = "http://json-schema.org/draft-07/schema#";
|
|
3590
|
-
function fieldDefToJsonSchema(field) {
|
|
3591
|
-
const out = {};
|
|
3592
|
-
switch (field.type) {
|
|
3593
|
-
case "string":
|
|
3594
|
-
out.type = "string";
|
|
3595
|
-
if (field.minLength !== void 0) out.minLength = field.minLength;
|
|
3596
|
-
if (field.maxLength !== void 0) out.maxLength = field.maxLength;
|
|
3597
|
-
break;
|
|
3598
|
-
case "number":
|
|
3599
|
-
out.type = "number";
|
|
3600
|
-
if (field.min !== void 0) out.minimum = field.min;
|
|
3601
|
-
if (field.max !== void 0) out.maximum = field.max;
|
|
3602
|
-
break;
|
|
3603
|
-
case "integer":
|
|
3604
|
-
out.type = "integer";
|
|
3605
|
-
if (field.min !== void 0) out.minimum = field.min;
|
|
3606
|
-
if (field.max !== void 0) out.maximum = field.max;
|
|
3607
|
-
break;
|
|
3608
|
-
case "boolean":
|
|
3609
|
-
out.type = "boolean";
|
|
3610
|
-
break;
|
|
3611
|
-
case "enum":
|
|
3612
|
-
out.type = "string";
|
|
3613
|
-
out.enum = field.enumValues ? [...field.enumValues] : [];
|
|
3614
|
-
break;
|
|
3615
|
-
case "string[]":
|
|
3616
|
-
out.type = "array";
|
|
3617
|
-
out.items = { type: "string" };
|
|
3618
|
-
if (field.minItems !== void 0) out.minItems = field.minItems;
|
|
3619
|
-
if (field.maxItems !== void 0) out.maxItems = field.maxItems;
|
|
3620
|
-
break;
|
|
3621
|
-
case "enum[]":
|
|
3622
|
-
out.type = "array";
|
|
3623
|
-
out.items = { type: "string", enum: field.enumValues ? [...field.enumValues] : [] };
|
|
3624
|
-
if (field.minItems !== void 0) out.minItems = field.minItems;
|
|
3625
|
-
if (field.maxItems !== void 0) out.maxItems = field.maxItems;
|
|
3626
|
-
break;
|
|
3627
|
-
case "date":
|
|
3628
|
-
out.type = "string";
|
|
3629
|
-
out.format = "date";
|
|
3630
|
-
break;
|
|
3631
|
-
}
|
|
3632
|
-
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;
|
|
3633
|
-
if (field.example !== void 0) out.example = field.example;
|
|
3634
|
-
return out;
|
|
3635
|
-
}
|
|
3636
|
-
function fieldsToPropertiesAndRequired(fields) {
|
|
3637
|
-
const properties = {};
|
|
3638
|
-
const required = [];
|
|
3639
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
3640
|
-
properties[key] = fieldDefToJsonSchema(field);
|
|
3641
|
-
if (field.required) required.push(key);
|
|
3642
|
-
}
|
|
3643
|
-
return { properties, required };
|
|
3644
|
-
}
|
|
3645
|
-
function buildSectionSchema(section) {
|
|
3646
|
-
if (section.kind === "scalar") {
|
|
3647
|
-
return fieldDefToJsonSchema(section.valueDef);
|
|
3648
|
-
}
|
|
3649
|
-
if (section.kind === "object") {
|
|
3650
|
-
const { properties, required } = fieldsToPropertiesAndRequired(section.fields);
|
|
3651
|
-
return {
|
|
3652
|
-
type: "object",
|
|
3653
|
-
description: section.description,
|
|
3654
|
-
additionalProperties: false,
|
|
3655
|
-
required,
|
|
3656
|
-
properties
|
|
3657
|
-
};
|
|
3658
|
-
}
|
|
3659
|
-
if (section.kind === "array") {
|
|
3660
|
-
const { properties, required } = fieldsToPropertiesAndRequired(section.fields);
|
|
3661
|
-
const schema = {
|
|
3662
|
-
type: "array",
|
|
3663
|
-
description: section.description,
|
|
3664
|
-
items: { type: "object", additionalProperties: false, required, properties }
|
|
3665
|
-
};
|
|
3666
|
-
if (section.maxItems !== void 0) schema.maxItems = section.maxItems;
|
|
3667
|
-
return schema;
|
|
3668
|
-
}
|
|
3669
|
-
const wrapperParts = fieldsToPropertiesAndRequired(section.wrapperFields ?? {});
|
|
3670
|
-
const itemParts = fieldsToPropertiesAndRequired(section.fields);
|
|
3671
|
-
return {
|
|
3672
|
-
type: "object",
|
|
3673
|
-
description: section.description,
|
|
3674
|
-
additionalProperties: false,
|
|
3675
|
-
required: [...wrapperParts.required, "items"],
|
|
3676
|
-
properties: {
|
|
3677
|
-
...wrapperParts.properties,
|
|
3678
|
-
items: {
|
|
3679
|
-
type: "array",
|
|
3680
|
-
items: { type: "object", additionalProperties: false, required: itemParts.required, properties: itemParts.properties }
|
|
3681
|
-
}
|
|
3682
|
-
}
|
|
3683
|
-
};
|
|
3684
|
-
}
|
|
3685
|
-
function buildAggregateJsonSchema(sectionKey) {
|
|
3686
|
-
if (sectionKey !== void 0) {
|
|
3687
|
-
const section = getSection(sectionKey);
|
|
3688
|
-
if (!section) {
|
|
3689
|
-
const allowed = SECTIONS.map((s) => s.key).join(", ");
|
|
3690
|
-
throw new CliError(`Unknown section "${sectionKey}". Allowed: ${allowed}`, ExitCode.InvalidArgument);
|
|
3691
|
-
}
|
|
3692
|
-
return buildSectionSchema(section);
|
|
3693
|
-
}
|
|
3694
|
-
const properties = {
|
|
3695
|
-
name: fieldDefToJsonSchema(TOP_LEVEL_FIELDS.name)
|
|
3696
|
-
};
|
|
3697
|
-
for (const section of SECTIONS) {
|
|
3698
|
-
properties[section.key] = buildSectionSchema(section);
|
|
3699
|
-
}
|
|
3700
|
-
return {
|
|
3701
|
-
$schema: JSON_SCHEMA_DRAFT,
|
|
3702
|
-
title: "wport resume aggregate",
|
|
3703
|
-
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",
|
|
3704
|
-
type: "object",
|
|
3705
|
-
additionalProperties: false,
|
|
3706
|
-
required: ["name"],
|
|
3707
|
-
properties
|
|
3708
|
-
};
|
|
3709
|
-
}
|
|
3710
|
-
|
|
3711
2934
|
// src/commands/personal/resumes/schema.ts
|
|
2935
|
+
var import_core36 = require("@wport/core");
|
|
3712
2936
|
function registerPersonalResumesSchema(parent) {
|
|
3713
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) => {
|
|
3714
|
-
printJson(buildAggregateJsonSchema(opts.section));
|
|
2938
|
+
printJson((0, import_core36.buildAggregateJsonSchema)(opts.section));
|
|
3715
2939
|
});
|
|
3716
2940
|
}
|
|
3717
2941
|
|
|
3718
|
-
// src/lib/resume-schema/validate.ts
|
|
3719
|
-
var TOP_LEVEL_READ_ONLY_KEYS = /* @__PURE__ */ new Set(["photo_url"]);
|
|
3720
|
-
var BACKGROUND_READ_ONLY_KEYS = /* @__PURE__ */ new Set(["identity_types"]);
|
|
3721
|
-
var NO_READ_ONLY_KEYS = /* @__PURE__ */ new Set();
|
|
3722
|
-
function itemAllowedKeys(fields) {
|
|
3723
|
-
return /* @__PURE__ */ new Set([...Object.keys(fields), "enc_id"]);
|
|
3724
|
-
}
|
|
3725
|
-
function isPlainObject(value) {
|
|
3726
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3727
|
-
}
|
|
3728
|
-
function describeType(value) {
|
|
3729
|
-
if (value === null) return "null";
|
|
3730
|
-
if (Array.isArray(value)) return "an array";
|
|
3731
|
-
return typeof value;
|
|
3732
|
-
}
|
|
3733
|
-
function unknownFieldMessage(key) {
|
|
3734
|
-
return `Unknown field "${key}" \u2014 not part of the resume schema`;
|
|
3735
|
-
}
|
|
3736
|
-
function readOnlyFieldMessage(key) {
|
|
3737
|
-
return `Field "${key}" is read-only (member profile data) \u2014 not part of the resume write aggregate`;
|
|
3738
|
-
}
|
|
3739
|
-
function stripHtml2(s) {
|
|
3740
|
-
return s.replace(/<[^>]*>/g, "");
|
|
3741
|
-
}
|
|
3742
|
-
function isValidDateString(s) {
|
|
3743
|
-
if (!/^\d{4}-\d{2}-\d{2}$/.test(s)) return false;
|
|
3744
|
-
const [y, m, d] = s.split("-").map(Number);
|
|
3745
|
-
const date = new Date(Date.UTC(y, m - 1, d));
|
|
3746
|
-
return date.getUTCFullYear() === y && date.getUTCMonth() === m - 1 && date.getUTCDate() === d;
|
|
3747
|
-
}
|
|
3748
|
-
function checkUnknownKeys(obj, allowedKeys, readOnlyKeys, pathFor, issues) {
|
|
3749
|
-
for (const key of Object.keys(obj)) {
|
|
3750
|
-
if (allowedKeys.has(key)) continue;
|
|
3751
|
-
if (readOnlyKeys.has(key)) {
|
|
3752
|
-
issues.push({ path: pathFor(key), message: readOnlyFieldMessage(key) });
|
|
3753
|
-
continue;
|
|
3754
|
-
}
|
|
3755
|
-
issues.push({ path: pathFor(key), message: unknownFieldMessage(key) });
|
|
3756
|
-
}
|
|
3757
|
-
}
|
|
3758
|
-
function checkFieldValue(field, value, path, issues) {
|
|
3759
|
-
switch (field.type) {
|
|
3760
|
-
case "string": {
|
|
3761
|
-
if (typeof value !== "string") {
|
|
3762
|
-
issues.push({ path, message: `Expected a string (got ${describeType(value)})` });
|
|
3763
|
-
return;
|
|
3764
|
-
}
|
|
3765
|
-
const text = field.htmlText ? stripHtml2(value) : value;
|
|
3766
|
-
if (field.minLength !== void 0 && text.length < field.minLength) {
|
|
3767
|
-
issues.push({ path, message: `Must be at least ${field.minLength} characters (got ${text.length})` });
|
|
3768
|
-
}
|
|
3769
|
-
if (field.maxLength !== void 0 && text.length > field.maxLength) {
|
|
3770
|
-
issues.push({ path, message: `Must be at most ${field.maxLength} characters (got ${text.length})` });
|
|
3771
|
-
}
|
|
3772
|
-
return;
|
|
3773
|
-
}
|
|
3774
|
-
case "number": {
|
|
3775
|
-
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
3776
|
-
issues.push({ path, message: `Expected a number (got ${describeType(value)})` });
|
|
3777
|
-
return;
|
|
3778
|
-
}
|
|
3779
|
-
checkRange(field, value, path, issues);
|
|
3780
|
-
return;
|
|
3781
|
-
}
|
|
3782
|
-
case "integer": {
|
|
3783
|
-
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
3784
|
-
issues.push({ path, message: `Expected an integer (got ${describeType(value)})` });
|
|
3785
|
-
return;
|
|
3786
|
-
}
|
|
3787
|
-
checkRange(field, value, path, issues);
|
|
3788
|
-
return;
|
|
3789
|
-
}
|
|
3790
|
-
case "boolean": {
|
|
3791
|
-
if (typeof value !== "boolean") issues.push({ path, message: `Expected a boolean (got ${describeType(value)})` });
|
|
3792
|
-
return;
|
|
3793
|
-
}
|
|
3794
|
-
case "enum": {
|
|
3795
|
-
const allowed = field.enumValues ?? [];
|
|
3796
|
-
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
3797
|
-
issues.push({ path, message: `Invalid value ${JSON.stringify(value)} \u2014 allowed: ${allowed.join(", ")}` });
|
|
3798
|
-
}
|
|
3799
|
-
return;
|
|
3800
|
-
}
|
|
3801
|
-
case "string[]": {
|
|
3802
|
-
if (!Array.isArray(value)) {
|
|
3803
|
-
issues.push({ path, message: `Expected an array (got ${describeType(value)})` });
|
|
3804
|
-
return;
|
|
3805
|
-
}
|
|
3806
|
-
value.forEach((v, i) => {
|
|
3807
|
-
if (typeof v !== "string") issues.push({ path: `${path}[${i}]`, message: `Expected a string (got ${describeType(v)})` });
|
|
3808
|
-
});
|
|
3809
|
-
checkItemsCount(field, value, path, issues);
|
|
3810
|
-
return;
|
|
3811
|
-
}
|
|
3812
|
-
case "enum[]": {
|
|
3813
|
-
const allowed = field.enumValues ?? [];
|
|
3814
|
-
if (!Array.isArray(value)) {
|
|
3815
|
-
issues.push({ path, message: `Expected an array (got ${describeType(value)})` });
|
|
3816
|
-
return;
|
|
3817
|
-
}
|
|
3818
|
-
value.forEach((v, i) => {
|
|
3819
|
-
if (typeof v !== "string" || !allowed.includes(v)) {
|
|
3820
|
-
issues.push({ path: `${path}[${i}]`, message: `Invalid value ${JSON.stringify(v)} \u2014 allowed: ${allowed.join(", ")}` });
|
|
3821
|
-
}
|
|
3822
|
-
});
|
|
3823
|
-
checkItemsCount(field, value, path, issues);
|
|
3824
|
-
return;
|
|
3825
|
-
}
|
|
3826
|
-
case "date": {
|
|
3827
|
-
if (typeof value !== "string" || !isValidDateString(value)) {
|
|
3828
|
-
issues.push({ path, message: `Expected a date string in YYYY-MM-DD format (got ${JSON.stringify(value)})` });
|
|
3829
|
-
}
|
|
3830
|
-
return;
|
|
3831
|
-
}
|
|
3832
|
-
}
|
|
3833
|
-
}
|
|
3834
|
-
function checkRange(field, value, path, issues) {
|
|
3835
|
-
if (field.min !== void 0 && value < field.min) issues.push({ path, message: `Must be >= ${field.min} (got ${value})` });
|
|
3836
|
-
if (field.max !== void 0 && value > field.max) issues.push({ path, message: `Must be <= ${field.max} (got ${value})` });
|
|
3837
|
-
}
|
|
3838
|
-
function checkItemsCount(field, value, path, issues) {
|
|
3839
|
-
if (field.minItems !== void 0 && value.length < field.minItems) {
|
|
3840
|
-
issues.push({ path, message: `Must have at least ${field.minItems} item(s) (got ${value.length})` });
|
|
3841
|
-
}
|
|
3842
|
-
if (field.maxItems !== void 0 && value.length > field.maxItems) {
|
|
3843
|
-
issues.push({ path, message: `Must have at most ${field.maxItems} item(s) (got ${value.length})` });
|
|
3844
|
-
}
|
|
3845
|
-
}
|
|
3846
|
-
function checkFields(obj, fields, pathFor, issues) {
|
|
3847
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
3848
|
-
const value = obj[key];
|
|
3849
|
-
const path = pathFor(key);
|
|
3850
|
-
if (value === void 0) {
|
|
3851
|
-
if (field.required) issues.push({ path, message: `Missing required field "${key}"` });
|
|
3852
|
-
continue;
|
|
3853
|
-
}
|
|
3854
|
-
checkFieldValue(field, value, path, issues);
|
|
3855
|
-
}
|
|
3856
|
-
}
|
|
3857
|
-
function checkCrossFieldRules(rules, item, path, issues) {
|
|
3858
|
-
for (const rule of rules ?? []) {
|
|
3859
|
-
if (!rule.check(item)) issues.push({ path, message: rule.message });
|
|
3860
|
-
}
|
|
3861
|
-
}
|
|
3862
|
-
function validateArraySection(section, value, issues) {
|
|
3863
|
-
const path = section.key;
|
|
3864
|
-
if (!Array.isArray(value)) {
|
|
3865
|
-
issues.push({ path, message: `Expected an array (got ${describeType(value)})` });
|
|
3866
|
-
return;
|
|
3867
|
-
}
|
|
3868
|
-
if (section.maxItems !== void 0 && value.length > section.maxItems) {
|
|
3869
|
-
issues.push({ path, message: `Must have at most ${section.maxItems} item(s) (got ${value.length})` });
|
|
3870
|
-
}
|
|
3871
|
-
value.forEach((item, i) => {
|
|
3872
|
-
const itemPath = `${path}[${i}]`;
|
|
3873
|
-
if (!isPlainObject(item)) {
|
|
3874
|
-
issues.push({ path: itemPath, message: `Expected an object (got ${describeType(item)})` });
|
|
3875
|
-
return;
|
|
3876
|
-
}
|
|
3877
|
-
checkUnknownKeys(item, itemAllowedKeys(section.fields), NO_READ_ONLY_KEYS, (k) => `${itemPath}.${k}`, issues);
|
|
3878
|
-
checkFields(item, section.fields, (k) => `${itemPath}.${k}`, issues);
|
|
3879
|
-
checkCrossFieldRules(section.crossFieldRules, item, itemPath, issues);
|
|
3880
|
-
});
|
|
3881
|
-
}
|
|
3882
|
-
function validateObjectSection(section, value, issues) {
|
|
3883
|
-
const path = section.key;
|
|
3884
|
-
if (!isPlainObject(value)) {
|
|
3885
|
-
issues.push({ path, message: `Expected an object (got ${describeType(value)})` });
|
|
3886
|
-
return;
|
|
3887
|
-
}
|
|
3888
|
-
const readOnly = section.key === "background" ? BACKGROUND_READ_ONLY_KEYS : NO_READ_ONLY_KEYS;
|
|
3889
|
-
checkUnknownKeys(value, new Set(Object.keys(section.fields)), readOnly, (k) => `${path}.${k}`, issues);
|
|
3890
|
-
checkFields(value, section.fields, (k) => `${path}.${k}`, issues);
|
|
3891
|
-
checkCrossFieldRules(section.crossFieldRules, value, path, issues);
|
|
3892
|
-
}
|
|
3893
|
-
function validateScalarSection(section, value, issues) {
|
|
3894
|
-
checkFieldValue(section.valueDef, value, section.key, issues);
|
|
3895
|
-
}
|
|
3896
|
-
function validateWrapperSection(section, value, issues) {
|
|
3897
|
-
const path = section.key;
|
|
3898
|
-
if (!isPlainObject(value)) {
|
|
3899
|
-
issues.push({ path, message: `Expected an object (got ${describeType(value)})` });
|
|
3900
|
-
return;
|
|
3901
|
-
}
|
|
3902
|
-
const wrapperFields = section.wrapperFields ?? {};
|
|
3903
|
-
const allowedTopKeys = /* @__PURE__ */ new Set([...Object.keys(wrapperFields), "items"]);
|
|
3904
|
-
checkUnknownKeys(value, allowedTopKeys, NO_READ_ONLY_KEYS, (k) => `${path}.${k}`, issues);
|
|
3905
|
-
checkFields(value, wrapperFields, (k) => `${path}.${k}`, issues);
|
|
3906
|
-
const items = value.items;
|
|
3907
|
-
const itemsPath = `${path}.items`;
|
|
3908
|
-
if (items === void 0) {
|
|
3909
|
-
issues.push({ path: itemsPath, message: 'Missing required field "items"' });
|
|
3910
|
-
} else if (!Array.isArray(items)) {
|
|
3911
|
-
issues.push({ path: itemsPath, message: `Expected an array (got ${describeType(items)})` });
|
|
3912
|
-
} else {
|
|
3913
|
-
items.forEach((item, i) => {
|
|
3914
|
-
const itemPath = `${itemsPath}[${i}]`;
|
|
3915
|
-
if (!isPlainObject(item)) {
|
|
3916
|
-
issues.push({ path: itemPath, message: `Expected an object (got ${describeType(item)})` });
|
|
3917
|
-
return;
|
|
3918
|
-
}
|
|
3919
|
-
checkUnknownKeys(item, itemAllowedKeys(section.fields), NO_READ_ONLY_KEYS, (k) => `${itemPath}.${k}`, issues);
|
|
3920
|
-
checkFields(item, section.fields, (k) => `${itemPath}.${k}`, issues);
|
|
3921
|
-
checkCrossFieldRules(section.crossFieldRules, item, itemPath, issues);
|
|
3922
|
-
});
|
|
3923
|
-
}
|
|
3924
|
-
checkCrossFieldRules(section.crossFieldRules, value, path, issues);
|
|
3925
|
-
}
|
|
3926
|
-
function validateAggregate(input) {
|
|
3927
|
-
if (!isPlainObject(input)) {
|
|
3928
|
-
return [{ path: "(root)", message: `Input must be a JSON object at the top level (got ${describeType(input)})` }];
|
|
3929
|
-
}
|
|
3930
|
-
const issues = [];
|
|
3931
|
-
const allowedTopKeys = /* @__PURE__ */ new Set(["name", ...SECTIONS.map((s) => s.key)]);
|
|
3932
|
-
checkUnknownKeys(input, allowedTopKeys, TOP_LEVEL_READ_ONLY_KEYS, (k) => k, issues);
|
|
3933
|
-
checkFields(input, TOP_LEVEL_FIELDS, (k) => k, issues);
|
|
3934
|
-
for (const section of SECTIONS) {
|
|
3935
|
-
const value = input[section.key];
|
|
3936
|
-
if (value === void 0) continue;
|
|
3937
|
-
switch (section.kind) {
|
|
3938
|
-
case "array":
|
|
3939
|
-
validateArraySection(section, value, issues);
|
|
3940
|
-
break;
|
|
3941
|
-
case "object":
|
|
3942
|
-
validateObjectSection(section, value, issues);
|
|
3943
|
-
break;
|
|
3944
|
-
case "scalar":
|
|
3945
|
-
validateScalarSection(section, value, issues);
|
|
3946
|
-
break;
|
|
3947
|
-
case "wrapper":
|
|
3948
|
-
validateWrapperSection(section, value, issues);
|
|
3949
|
-
break;
|
|
3950
|
-
}
|
|
3951
|
-
}
|
|
3952
|
-
return issues;
|
|
3953
|
-
}
|
|
3954
|
-
|
|
3955
2942
|
// src/commands/personal/resumes/validate.ts
|
|
2943
|
+
var import_core37 = require("@wport/core");
|
|
3956
2944
|
function runResumesValidate(ctx, filePath) {
|
|
3957
2945
|
const input = readJsonInput(filePath, { timeoutMs: ctx.timeoutMs });
|
|
3958
|
-
const issues = validateAggregate(input);
|
|
2946
|
+
const issues = (0, import_core37.validateAggregate)(input);
|
|
3959
2947
|
if (issues.length === 0) {
|
|
3960
2948
|
if (ctx.format === "json") {
|
|
3961
2949
|
printJson({ valid: true });
|
|
@@ -3983,43 +2971,9 @@ function registerPersonalResumesValidate(parent) {
|
|
|
3983
2971
|
|
|
3984
2972
|
// src/commands/personal/resumes/template.ts
|
|
3985
2973
|
var import_node_fs8 = require("fs");
|
|
3986
|
-
|
|
3987
|
-
// src/lib/resume-schema/template.ts
|
|
3988
|
-
function buildFieldsExample(fields) {
|
|
3989
|
-
const out = {};
|
|
3990
|
-
for (const [key, field] of Object.entries(fields)) {
|
|
3991
|
-
if (field.example !== void 0) out[key] = field.example;
|
|
3992
|
-
}
|
|
3993
|
-
return out;
|
|
3994
|
-
}
|
|
3995
|
-
function buildSectionTemplate(section) {
|
|
3996
|
-
switch (section.kind) {
|
|
3997
|
-
case "scalar":
|
|
3998
|
-
return section.valueDef.example;
|
|
3999
|
-
case "object":
|
|
4000
|
-
return buildFieldsExample(section.fields);
|
|
4001
|
-
case "array":
|
|
4002
|
-
return [buildFieldsExample(section.fields)];
|
|
4003
|
-
case "wrapper":
|
|
4004
|
-
return {
|
|
4005
|
-
...buildFieldsExample(section.wrapperFields ?? {}),
|
|
4006
|
-
items: [buildFieldsExample(section.fields)]
|
|
4007
|
-
};
|
|
4008
|
-
}
|
|
4009
|
-
}
|
|
4010
|
-
function buildTemplate() {
|
|
4011
|
-
const template = {
|
|
4012
|
-
name: TOP_LEVEL_FIELDS.name.example
|
|
4013
|
-
};
|
|
4014
|
-
for (const section of SECTIONS) {
|
|
4015
|
-
template[section.key] = buildSectionTemplate(section);
|
|
4016
|
-
}
|
|
4017
|
-
return template;
|
|
4018
|
-
}
|
|
4019
|
-
|
|
4020
|
-
// src/commands/personal/resumes/template.ts
|
|
2974
|
+
var import_core38 = require("@wport/core");
|
|
4021
2975
|
function runResumesTemplate(outPath) {
|
|
4022
|
-
const template = buildTemplate();
|
|
2976
|
+
const template = (0, import_core38.buildTemplate)();
|
|
4023
2977
|
if (outPath === void 0) {
|
|
4024
2978
|
printJson(template);
|
|
4025
2979
|
return;
|
|
@@ -4043,6 +2997,8 @@ function registerPersonalResumesTemplate(parent) {
|
|
|
4043
2997
|
}
|
|
4044
2998
|
|
|
4045
2999
|
// src/commands/personal/resumes/list.ts
|
|
3000
|
+
var import_core39 = require("@wport/core");
|
|
3001
|
+
var import_core40 = require("@wport/core");
|
|
4046
3002
|
var MINIMAL_LIST_FIELDS4 = ["enc_id", "name", "updated_at", "is_complete", "is_published"];
|
|
4047
3003
|
function formatDate6(value) {
|
|
4048
3004
|
return value ? String(value).slice(0, 10) : "";
|
|
@@ -4052,8 +3008,8 @@ function formatQuotaLine(quota) {
|
|
|
4052
3008
|
return `${quota.used}/${quota.max_resumes} used, ${status}.`;
|
|
4053
3009
|
}
|
|
4054
3010
|
async function fetchResumeList(opts) {
|
|
4055
|
-
const { body } = await personalGet(opts, PERSONAL_RESUMES_BASE);
|
|
4056
|
-
return unwrapDataResponse(body);
|
|
3011
|
+
const { body } = await personalGet(opts, import_core40.PERSONAL_RESUMES_BASE);
|
|
3012
|
+
return (0, import_core39.unwrapDataResponse)(body);
|
|
4057
3013
|
}
|
|
4058
3014
|
async function runResumesList(ctx, flags) {
|
|
4059
3015
|
if (flags.fields && flags.minimal) {
|
|
@@ -4086,17 +3042,20 @@ function registerPersonalResumesList(parent) {
|
|
|
4086
3042
|
}
|
|
4087
3043
|
|
|
4088
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");
|
|
4089
3048
|
async function fetchResumeAggregate(opts, encId) {
|
|
4090
3049
|
const trimmed = encId.trim();
|
|
4091
3050
|
if (!trimmed) {
|
|
4092
3051
|
throw new CliError("enc_id must not be empty", ExitCode.InvalidArgument);
|
|
4093
3052
|
}
|
|
4094
|
-
const { body } = await personalGet(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
|
|
4095
|
-
return unwrapDataResponse(body);
|
|
3053
|
+
const { body } = await personalGet(opts, `${import_core42.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);
|
|
3054
|
+
return (0, import_core41.unwrapDataResponse)(body);
|
|
4096
3055
|
}
|
|
4097
3056
|
function summarizeSections(resume) {
|
|
4098
3057
|
const rows = [{ section: "name", summary: resume.name }];
|
|
4099
|
-
for (const def of SECTIONS) {
|
|
3058
|
+
for (const def of import_core43.SECTIONS) {
|
|
4100
3059
|
rows.push({ section: def.key, summary: summarizeSection(def, resume) });
|
|
4101
3060
|
}
|
|
4102
3061
|
return rows;
|
|
@@ -4173,8 +3132,14 @@ function registerPersonalResumesExport(parent) {
|
|
|
4173
3132
|
});
|
|
4174
3133
|
}
|
|
4175
3134
|
|
|
3135
|
+
// src/commands/personal/resumes/create.ts
|
|
3136
|
+
var import_core47 = require("@wport/core");
|
|
3137
|
+
|
|
4176
3138
|
// src/lib/resume-orchestrator.ts
|
|
4177
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");
|
|
4178
3143
|
function freshIdempotencyKey() {
|
|
4179
3144
|
return { idempotencyKey: (0, import_node_crypto11.randomUUID)() };
|
|
4180
3145
|
}
|
|
@@ -4189,8 +3154,8 @@ function extractErrorCode(body) {
|
|
|
4189
3154
|
}
|
|
4190
3155
|
async function createShell(opts) {
|
|
4191
3156
|
try {
|
|
4192
|
-
const { body } = await personalPost(opts, PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());
|
|
4193
|
-
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;
|
|
4194
3159
|
} catch (err) {
|
|
4195
3160
|
if (err instanceof ServerClientHttpError) {
|
|
4196
3161
|
const code = extractErrorCode(err.body);
|
|
@@ -4211,7 +3176,7 @@ async function createShell(opts) {
|
|
|
4211
3176
|
}
|
|
4212
3177
|
}
|
|
4213
3178
|
async function renameResume(opts, encId, name) {
|
|
4214
|
-
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());
|
|
4215
3180
|
}
|
|
4216
3181
|
function toWorkExperienceWriteItem(item) {
|
|
4217
3182
|
const { is_current, ...rest } = item;
|
|
@@ -4223,8 +3188,8 @@ function splitItemEncId(item) {
|
|
|
4223
3188
|
return [valid, rest];
|
|
4224
3189
|
}
|
|
4225
3190
|
async function writeSection(opts, encId, key, value, mode) {
|
|
4226
|
-
const plan = SECTION_WRITE_PLAN[key];
|
|
4227
|
-
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)}`;
|
|
4228
3193
|
switch (plan.kind) {
|
|
4229
3194
|
case "per-item-post": {
|
|
4230
3195
|
for (const item of value) {
|
|
@@ -4291,7 +3256,7 @@ async function createAggregate(opts, aggregate) {
|
|
|
4291
3256
|
if (aggregate.name !== void 0) {
|
|
4292
3257
|
reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
|
|
4293
3258
|
}
|
|
4294
|
-
for (const section of SECTIONS) {
|
|
3259
|
+
for (const section of import_core46.SECTIONS) {
|
|
4295
3260
|
const value = aggregate[section.key];
|
|
4296
3261
|
if (value === void 0) continue;
|
|
4297
3262
|
reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "create")));
|
|
@@ -4301,9 +3266,9 @@ async function createAggregate(opts, aggregate) {
|
|
|
4301
3266
|
async function updateAggregate(opts, encId, aggregate, onlySection) {
|
|
4302
3267
|
const reports = [];
|
|
4303
3268
|
if (onlySection !== void 0) {
|
|
4304
|
-
const section = getSection(onlySection);
|
|
3269
|
+
const section = (0, import_core46.getSection)(onlySection);
|
|
4305
3270
|
if (!section) {
|
|
4306
|
-
const allowed = SECTIONS.map((s) => s.key).join(", ");
|
|
3271
|
+
const allowed = import_core46.SECTIONS.map((s) => s.key).join(", ");
|
|
4307
3272
|
throw new CliError(`Unknown section "${onlySection}". Allowed: ${allowed}`, ExitCode.InvalidArgument);
|
|
4308
3273
|
}
|
|
4309
3274
|
const value = aggregate[onlySection];
|
|
@@ -4316,7 +3281,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
|
|
|
4316
3281
|
if (aggregate.name !== void 0) {
|
|
4317
3282
|
reports.push(await attemptStep("name", () => renameResume(opts, encId, aggregate.name)));
|
|
4318
3283
|
}
|
|
4319
|
-
for (const section of SECTIONS) {
|
|
3284
|
+
for (const section of import_core46.SECTIONS) {
|
|
4320
3285
|
const value = aggregate[section.key];
|
|
4321
3286
|
if (value === void 0) continue;
|
|
4322
3287
|
reports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, "update")));
|
|
@@ -4327,7 +3292,7 @@ async function updateAggregate(opts, encId, aggregate, onlySection) {
|
|
|
4327
3292
|
// src/commands/personal/resumes/create.ts
|
|
4328
3293
|
async function runResumesCreate(ctx, filePath) {
|
|
4329
3294
|
const input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });
|
|
4330
|
-
const issues = validateAggregate(input);
|
|
3295
|
+
const issues = (0, import_core47.validateAggregate)(input);
|
|
4331
3296
|
if (issues.length > 0) {
|
|
4332
3297
|
if (ctx.format === "json") {
|
|
4333
3298
|
printJson({ valid: false, issues });
|
|
@@ -4420,6 +3385,8 @@ function registerPersonalResumesUpdate(parent) {
|
|
|
4420
3385
|
|
|
4421
3386
|
// src/commands/personal/resumes/copy.ts
|
|
4422
3387
|
var import_node_crypto12 = require("crypto");
|
|
3388
|
+
var import_core48 = require("@wport/core");
|
|
3389
|
+
var import_core49 = require("@wport/core");
|
|
4423
3390
|
function requireEncId3(encId) {
|
|
4424
3391
|
const trimmed = encId.trim();
|
|
4425
3392
|
if (!trimmed) {
|
|
@@ -4438,12 +3405,12 @@ function printCopyResult(ctx, sourceEncId, newEncId) {
|
|
|
4438
3405
|
async function runResumesCopy(ctx, encId, name) {
|
|
4439
3406
|
const trimmed = requireEncId3(encId);
|
|
4440
3407
|
const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
|
|
4441
|
-
const { body } = await personalPost(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/duplicate`, {}, { idempotencyKey: (0, import_node_crypto12.randomUUID)() });
|
|
4442
|
-
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);
|
|
4443
3410
|
printCopyResult(ctx, trimmed, newEncId);
|
|
4444
3411
|
if (name === void 0) return;
|
|
4445
3412
|
try {
|
|
4446
|
-
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)() });
|
|
4447
3414
|
} catch (err) {
|
|
4448
3415
|
const reason = err instanceof Error ? err.message : String(err);
|
|
4449
3416
|
throw new CliError(
|
|
@@ -4460,6 +3427,8 @@ function registerPersonalResumesCopy(parent) {
|
|
|
4460
3427
|
|
|
4461
3428
|
// src/commands/personal/resumes/publish.ts
|
|
4462
3429
|
var import_node_crypto13 = require("crypto");
|
|
3430
|
+
var import_core50 = require("@wport/core");
|
|
3431
|
+
var import_core51 = require("@wport/core");
|
|
4463
3432
|
function requireEncId4(encId) {
|
|
4464
3433
|
const trimmed = encId.trim();
|
|
4465
3434
|
if (!trimmed) {
|
|
@@ -4473,11 +3442,11 @@ async function runResumesPublishTransition(ctx, encId, action) {
|
|
|
4473
3442
|
const targetStatus = action === "publish";
|
|
4474
3443
|
const { body } = await personalPatch(
|
|
4475
3444
|
opts,
|
|
4476
|
-
`${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
|
|
3445
|
+
`${import_core51.PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,
|
|
4477
3446
|
{ target_status: targetStatus },
|
|
4478
3447
|
{ idempotencyKey: (0, import_node_crypto13.randomUUID)() }
|
|
4479
3448
|
);
|
|
4480
|
-
const result = unwrapDataResponse(body);
|
|
3449
|
+
const result = (0, import_core50.unwrapDataResponse)(body);
|
|
4481
3450
|
if (ctx.format === "json") {
|
|
4482
3451
|
printJson({ enc_id: trimmed, is_published: result.is_published });
|
|
4483
3452
|
return;
|
|
@@ -4499,6 +3468,7 @@ function registerPersonalResumesUnpublish(parent) {
|
|
|
4499
3468
|
|
|
4500
3469
|
// src/commands/personal/resumes/delete.ts
|
|
4501
3470
|
var import_node_crypto14 = require("crypto");
|
|
3471
|
+
var import_core52 = require("@wport/core");
|
|
4502
3472
|
function requireEncId5(encId) {
|
|
4503
3473
|
const trimmed = encId.trim();
|
|
4504
3474
|
if (!trimmed) {
|
|
@@ -4512,7 +3482,7 @@ async function runResumesDelete(ctx, encId, confirm) {
|
|
|
4512
3482
|
}
|
|
4513
3483
|
const trimmed = requireEncId5(encId);
|
|
4514
3484
|
const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
|
|
4515
|
-
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)() });
|
|
4516
3486
|
if (ctx.format === "json") {
|
|
4517
3487
|
printJson({ enc_id: trimmed, deleted: true });
|
|
4518
3488
|
return;
|
|
@@ -4546,6 +3516,7 @@ function registerPersonalResumesCommand(parent) {
|
|
|
4546
3516
|
// src/commands/personal/apply/index.ts
|
|
4547
3517
|
var import_node_crypto15 = require("crypto");
|
|
4548
3518
|
var import_node_fs11 = require("fs");
|
|
3519
|
+
var import_core53 = require("@wport/core");
|
|
4549
3520
|
|
|
4550
3521
|
// src/commands/personal/apply/message-input.ts
|
|
4551
3522
|
var import_node_fs10 = require("fs");
|
|
@@ -4627,7 +3598,7 @@ async function runPersonalApply(ctx, args) {
|
|
|
4627
3598
|
const body = { enc_job_id: args.encJobId, enc_resume_id: args.encResumeId, application_message: args.message };
|
|
4628
3599
|
let result;
|
|
4629
3600
|
try {
|
|
4630
|
-
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)() });
|
|
4631
3602
|
} catch (err) {
|
|
4632
3603
|
rethrowApplyError(err);
|
|
4633
3604
|
}
|
|
@@ -4678,7 +3649,7 @@ async function runPersonalApplyBatch(ctx, args) {
|
|
|
4678
3649
|
const opts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };
|
|
4679
3650
|
let result;
|
|
4680
3651
|
try {
|
|
4681
|
-
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)() });
|
|
4682
3653
|
} catch (err) {
|
|
4683
3654
|
rethrowApplyError(err);
|
|
4684
3655
|
}
|
|
@@ -4706,7 +3677,7 @@ function registerPersonalCommand(program2) {
|
|
|
4706
3677
|
|
|
4707
3678
|
// src/index.ts
|
|
4708
3679
|
var program = new import_commander.Command();
|
|
4709
|
-
program.name("wport").description("wport CLI \u2014 terminal interface to the W101 Talent Search Hub public API").version("0.9.
|
|
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));
|
|
4710
3681
|
registerJobsCommand(program);
|
|
4711
3682
|
registerConfigCommand(program);
|
|
4712
3683
|
registerDoctorCommand(program);
|
|
@@ -4732,6 +3703,10 @@ function handleTopLevelError(err) {
|
|
|
4732
3703
|
printError(err.message, color);
|
|
4733
3704
|
process.exit(err.exitCode);
|
|
4734
3705
|
}
|
|
3706
|
+
if ((0, import_core.isWportError)(err)) {
|
|
3707
|
+
printError(err.message, color);
|
|
3708
|
+
process.exit(exitCodeForError(err));
|
|
3709
|
+
}
|
|
4735
3710
|
const fallbackMessage = err instanceof Error ? err.message : String(err);
|
|
4736
3711
|
printError(fallbackMessage, color);
|
|
4737
3712
|
process.exit(ExitCode.ServerOrNetworkError);
|