@wport/cli 0.2.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/lib/errors.ts","../src/lib/output.ts","../src/commands/jobs/search.ts","../src/lib/api-client.ts","../src/lib/config-store.ts","../src/lib/global-opts.ts","../src/lib/path-utils.ts","../src/lib/concurrency.ts","../src/lib/io-helpers.ts","../src/commands/jobs/view.ts","../src/commands/jobs/index.ts","../src/commands/config/set.ts","../src/commands/config/get.ts","../src/commands/config/path.ts","../src/commands/config/reset.ts","../src/commands/config/index.ts","../src/commands/doctor.ts","../src/lib/enterprise-client.ts","../src/lib/credentials-store.ts","../src/commands/enterprise/login.ts","../src/commands/enterprise/logout.ts","../src/commands/enterprise/whoami.ts","../src/commands/enterprise/jobs/list.ts","../src/commands/enterprise/jobs/view.ts","../src/commands/enterprise/jobs/index.ts","../src/commands/enterprise/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { isCliError, ExitCode } from './lib/errors';\nimport { isColorEnabled, printError } from './lib/output';\nimport { registerJobsCommand } from './commands/jobs';\nimport { registerConfigCommand } from './commands/config';\nimport { registerDoctorCommand } from './commands/doctor';\nimport { registerEnterpriseCommand } from './commands/enterprise';\n\nconst program = new Command();\n\nprogram\n\t.name('wport')\n\t.description('wport CLI — terminal interface to the W101 Talent Search Hub public API')\n\t.version(__CLI_VERSION__, '-v, --version', 'output the CLI version')\n\t.option('--lang <locale>', 'Accept-Language locale: zh-TW | en-US | vi-VN | th-TH | id-ID')\n\t.option('--api <url>', 'override API base URL')\n\t.option('--output <fmt>', 'output format: table | json')\n\t.option('--no-color', 'disable color output')\n\t.option('--timeout <ms>', 'HTTP timeout in milliseconds', (v) => Number(v));\n\nregisterJobsCommand(program);\nregisterConfigCommand(program);\nregisterDoctorCommand(program);\nregisterEnterpriseCommand(program);\n\nprogram.exitOverride();\n\nprogram\n\t.parseAsync(process.argv)\n\t.then(() => process.exit(ExitCode.Success))\n\t.catch((err: unknown) => handleTopLevelError(err));\n\nfunction handleTopLevelError(err: unknown): never {\n\tconst color = isColorEnabled(false);\n\n\t// commander throws CommanderError on its own validation paths (help, version, unknown option).\n\tif (err && typeof err === 'object' && 'code' in err) {\n\t\tconst commanderErr = err as { code?: string; exitCode?: number; message?: string };\n\t\tif (commanderErr.code === 'commander.helpDisplayed' || commanderErr.code === 'commander.version') {\n\t\t\tprocess.exit(ExitCode.Success);\n\t\t}\n\t\t// commander 自己的 exitCode 多半是 1,不在 CLI 的 contract(0/2/3/4/5)內。\n\t\t// 任何 parsing / usage 失敗都當 InvalidArgument(2)。\n\t\tif (commanderErr.message) printError(commanderErr.message, color);\n\t\tprocess.exit(ExitCode.InvalidArgument);\n\t}\n\n\tif (isCliError(err)) {\n\t\tprintError(err.message, color);\n\t\tprocess.exit(err.exitCode);\n\t}\n\n\tconst fallbackMessage = err instanceof Error ? err.message : String(err);\n\tprintError(fallbackMessage, color);\n\tprocess.exit(ExitCode.ServerOrNetworkError);\n}\n","export const ExitCode = {\n\tSuccess: 0,\n\tInvalidArgument: 2,\n\tServerClientError: 3,\n\tServerOrNetworkError: 4,\n\tConfigCorrupt: 5,\n} as const;\n\nexport type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];\n\nexport class CliError extends Error {\n\treadonly exitCode: ExitCodeValue;\n\n\tconstructor(message: string, exitCode: ExitCodeValue) {\n\t\tsuper(message);\n\t\tthis.name = 'CliError';\n\t\tthis.exitCode = exitCode;\n\t}\n}\n\nexport class InvalidArgumentError extends CliError {\n\tconstructor(message: string) {\n\t\tsuper(message, ExitCode.InvalidArgument);\n\t\tthis.name = 'InvalidArgumentError';\n\t}\n}\n\nexport class ServerClientHttpError extends CliError {\n\treadonly status: number;\n\treadonly body: unknown;\n\tconstructor(message: string, status: number, body: unknown) {\n\t\tsuper(message, ExitCode.ServerClientError);\n\t\tthis.name = 'ServerClientHttpError';\n\t\tthis.status = status;\n\t\tthis.body = body;\n\t}\n}\n\nexport class NetworkError extends CliError {\n\treadonly cause?: unknown;\n\tconstructor(message: string, cause?: unknown) {\n\t\tsuper(message, ExitCode.ServerOrNetworkError);\n\t\tthis.name = 'NetworkError';\n\t\tthis.cause = cause;\n\t}\n}\n\nexport class ConfigCorruptError extends CliError {\n\treadonly path?: string;\n\tconstructor(message: string, path?: string) {\n\t\tsuper(message, ExitCode.ConfigCorrupt);\n\t\tthis.name = 'ConfigCorruptError';\n\t\tthis.path = path;\n\t}\n}\n\nexport function isCliError(err: unknown): err is CliError {\n\treturn err instanceof CliError;\n}\n","import Table from 'cli-table3';\nimport pc from 'picocolors';\nimport { CliError, ExitCode } from './errors';\n\nexport type OutputFormat = 'table' | 'json';\n\n/**\n * 把 untrusted 字串(從 API 回來的 employer-controlled 內容)變成終端機可安全列印的形式。\n *\n * 防的是 terminal escape injection:\n * - CSI / OSC / DCS 等 ESC 開頭序列(清螢幕、改 title、移動游標、假超連結 phishing 等)\n * - 其他 C0 / C1 控制字元(保留 \\t \\n \\r 三個合法格式化字元)\n *\n * JSON 模式不需要 sanitize:JSON.stringify 會把 < 0x20 的字元 escape 成 \\uXXXX。\n * 只有 table / plain-text 印到 stdout/stderr 的字串走這個 helper。\n *\n * 用 new RegExp(string) 建構,所有控制字元以 \\\\uNNNN 形式撰寫,避免 source 內含 literal 控制字元。\n */\nconst ANSI_ESCAPE_SEQUENCE = new RegExp(\n\t[\n\t\t// CSI: ESC [ params intermediates final\n\t\t'\\\\u001B\\\\[[0-?]*[ -/]*[@-~]',\n\t\t// OSC: ESC ] payload (any chars except BEL/ESC) terminated by BEL or ESC \\\n\t\t'\\\\u001B\\\\][^\\\\u0007\\\\u001B]*(?:\\\\u0007|\\\\u001B\\\\\\\\)',\n\t\t// Two-char escapes: ESC + Fe final byte (0x40-0x5F = @ A B ... Z [ \\ ] ^ _).\n\t\t// 涵蓋 CSI([) / OSC(]) / DCS(P) / SOS(X) / ST(\\\\) / PM(^) / APC(_) intro。\n\t\t// CSI / OSC regex 在前面 OR-分支會先匹配對應序列;這條兜底所有未覆蓋 Fe。\n\t\t'\\\\u001B[@-_]',\n\t].join('|'),\n\t'g'\n);\n\n// 單行用:剝所有 C0(含 \\t \\n \\r)、DEL、C1。table cell、label-prefixed 標題、\n// error / warning 訊息都走這條 —— 即便 ANSI 已剝乾淨,殘留 \\r 仍能把 cursor 拉回\n// 行首蓋掉前面的內容;\\n 會打斷表格排版、可能偽造後續 row;\\t 寬度可變、\n// 搞壞 cli-table3 對齊。\nconst CONTROL_CHARS_STRICT = new RegExp('[\\\\u0000-\\\\u001F\\\\u007F\\\\u0080-\\\\u009F]', 'g');\n\n// 多行用:內部先把 \\r\\n / lone \\r 正規化成 \\n,再剝其他 C0(含 \\t)、DEL、C1。\n// 保留 \\n 作為合法段落分隔。Caller 不需事先做正規化。\nconst CONTROL_CHARS_MULTILINE = new RegExp('[\\\\u0000-\\\\u0009\\\\u000B-\\\\u001F\\\\u007F\\\\u0080-\\\\u009F]', 'g');\n\nexport function sanitizeForTerminal(s: string): string {\n\treturn s.replace(ANSI_ESCAPE_SEQUENCE, '').replace(CONTROL_CHARS_STRICT, '');\n}\n\nexport function sanitizeForTerminalMultiline(s: string): string {\n\treturn s.replace(ANSI_ESCAPE_SEQUENCE, '').replace(/\\r\\n?/g, '\\n').replace(CONTROL_CHARS_MULTILINE, '');\n}\n\nexport function resolveOutputFormat(explicit: string | undefined): OutputFormat {\n\tif (explicit === undefined) {\n\t\treturn process.stdout.isTTY ? 'table' : 'json';\n\t}\n\tif (explicit !== 'json' && explicit !== 'table') {\n\t\tthrow new CliError(`Invalid --output \"${explicit}\". Allowed: table, json`, ExitCode.InvalidArgument);\n\t}\n\treturn explicit;\n}\n\nexport function isColorEnabled(noColor: boolean | undefined): boolean {\n\tif (noColor === true) return false;\n\tif (process.env.NO_COLOR) return false;\n\treturn process.stdout.isTTY ?? false;\n}\n\nexport function printJson(value: unknown): void {\n\tprocess.stdout.write(JSON.stringify(value, null, 2) + '\\n');\n}\n\n/**\n * Emit one newline-delimited JSON record (ND-JSON). JSON.stringify escapes control\n * chars to \\uXXXX, so employer-controlled string values are terminal-safe without\n * extra sanitization. Used by `jobs view --batch`, where one record per line keeps a\n * single failure isolated to its own line.\n */\nexport function printNdjsonLine(value: unknown): void {\n\tprocess.stdout.write(JSON.stringify(value) + '\\n');\n}\n\nexport interface TableColumn<T> {\n\theader: string;\n\tvalue: (row: T) => string;\n\tmaxWidth?: number;\n}\n\nexport function printTable<T>(rows: T[], columns: TableColumn<T>[], color: boolean): void {\n\tif (rows.length === 0) {\n\t\tprocess.stdout.write(color ? pc.dim('(no results)\\n') : '(no results)\\n');\n\t\treturn;\n\t}\n\tconst table = new Table({\n\t\thead: columns.map((c) => (color ? pc.bold(c.header) : c.header)),\n\t\tstyle: { head: [], border: [] },\n\t\tcolWidths: columns.map((c) => c.maxWidth ?? null),\n\t\twordWrap: true,\n\t});\n\tfor (const row of rows) {\n\t\t// 每個 cell 的字串都過 sanitize:防 employer-controlled 內容(title/company/area/salary 等)注入 escape。\n\t\ttable.push(columns.map((c) => sanitizeForTerminal(c.value(row))));\n\t}\n\tprocess.stdout.write(table.toString() + '\\n');\n}\n\nexport function printError(message: string, color: boolean): void {\n\tconst prefix = color ? pc.red('Error:') : 'Error:';\n\t// Server 回的 error message 也視為 untrusted,sanitize。\n\tprocess.stderr.write(`${prefix} ${sanitizeForTerminal(message)}\\n`);\n}\n\nexport function printWarn(message: string, color: boolean): void {\n\tconst prefix = color ? pc.yellow('Warning:') : 'Warning:';\n\tprocess.stderr.write(`${prefix} ${sanitizeForTerminal(message)}\\n`);\n}\n\nexport function dim(text: string, color: boolean): string {\n\treturn color ? pc.dim(text) : text;\n}\n","import type { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport { asPaginatedBody, createApiClient, throwForHttpStatus } from '../../lib/api-client';\nimport { resolveContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { dim, printJson, printTable, printWarn } from '../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../lib/path-utils';\nimport type { operations } from '../../generated/schema';\n\ninterface SearchFlags {\n\tkeyword?: string;\n\tlocation?: string[];\n\tcategory?: string[];\n\tpage?: number;\n\tpageSize?: number;\n\tjsonQuery?: string;\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/**\n * Compact field set for `--minimal` — the columns an agent almost always wants from a\n * search result, mirroring the table view minus the noise. Keeps `enc_id` first so the\n * output is directly pipeable into `jobs view -`.\n */\nconst MINIMAL_SEARCH_FIELDS = ['enc_id', 'title', 'company_name', 'area_display', 'salary_display'];\n\n/**\n * Query type derived from the generated OpenAPI schema for GET /api/jobs/search.\n * Adding a new field on the backend DTO + re-running gen:openapi will make this type\n * widen automatically; any flag we forget to map will surface as a TS error inside\n * buildQuery() instead of being silently dropped at runtime.\n */\ntype SearchQuery = NonNullable<operations['JobsController_searchJobs']['parameters']['query']>;\n\ninterface JobSearchItem {\n\tenc_id?: string;\n\tenc_company_id?: string;\n\tcompany_name?: string;\n\tcompany_logo_url?: string;\n\ttitle?: string;\n\tarea_display?: string;\n\tsalary_display?: string;\n\tsalary_currency_code?: string | null;\n\ttags?: string[];\n\tupdated_at?: string;\n\t[k: string]: unknown;\n}\n\nexport function registerJobsSearch(parent: Command): void {\n\tparent\n\t\t.command('search')\n\t\t.description(\n\t\t\t'Search public job listings. Sort is server-controlled (publish date, or relevance when --keyword is set); ' +\n\t\t\t\t'the orderBy / order query params are silently ignored, so this CLI intentionally exposes no sort flags. ' +\n\t\t\t\t'Run `wport doctor` for the full list of server quirks.'\n\t\t)\n\t\t.option('-k, --keyword <text>', 'keyword search (title / company name etc.)')\n\t\t.option('-l, --location <code...>', 'area code (repeatable, e.g. 6001001000)')\n\t\t.option('-c, --category <code...>', 'job classification code (repeatable)')\n\t\t.option('-p, --page <n>', 'page number (default 1)', (v) => Number(v))\n\t\t.option('-s, --page-size <n>', 'page size (default 10, max 100)', (v) => Number(v))\n\t\t.option('--json-query <file>', 'read full query body from JSON file (overrides other flags)')\n\t\t.option(\n\t\t\t'--fields <list>',\n\t\t\t'keep only these fields in each JSON result (comma-separated dotted paths, e.g. enc_id,title). JSON output only.'\n\t\t)\n\t\t.option('--minimal', `shorthand for --fields ${MINIMAL_SEARCH_FIELDS.join(',')}. JSON output only.`)\n\t\t.action(async (flags: SearchFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst fields = resolveSearchFields(flags);\n\t\t\tconst query = buildQuery(flags);\n\n\t\t\tconst client = createApiClient({\n\t\t\t\tbaseUrl: ctx.baseUrl,\n\t\t\t\tlocale: ctx.locale,\n\t\t\t\ttimeoutMs: ctx.timeoutMs,\n\t\t\t});\n\n\t\t\t// openapi-fetch consumes the response body itself; use `data` (success) or `error` (non-2xx).\n\t\t\tconst { data, error, response } = await client.GET('/api/jobs/search', {\n\t\t\t\tparams: { query },\n\t\t\t});\n\t\t\tif (!response.ok) throwForHttpStatus(response.status, error);\n\n\t\t\tconst paged = asPaginatedBody<JobSearchItem>(data);\n\n\t\t\tif (ctx.format === 'json') {\n\t\t\t\t// Field projection is client-side: it trims tokens the model has to read, not\n\t\t\t\t// bytes-on-the-wire (the server has no projection param). Pagination metadata is\n\t\t\t\t// preserved by spreading `paged` and only replacing `data`.\n\t\t\t\tconst body = fields ? { ...paged, data: paged.data.map((item) => pickPaths(item, fields)) } : paged;\n\t\t\t\tprintJson(body);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Table columns are fixed; --fields / --minimal are a JSON-only affordance. Warn\n\t\t\t// rather than silently ignore so the flag doesn't look broken.\n\t\t\tif (fields) {\n\t\t\t\tprintWarn('--fields / --minimal only affect JSON output; ignored for table. Use --output json.', ctx.color);\n\t\t\t}\n\n\t\t\tprintTable(\n\t\t\t\tpaged.data,\n\t\t\t\t[\n\t\t\t\t\t{ header: 'ENC_ID', value: (r) => truncate(r.enc_id ?? '', 14) },\n\t\t\t\t\t{ header: 'TITLE', value: (r) => r.title ?? '', maxWidth: 36 },\n\t\t\t\t\t{ header: 'COMPANY', value: (r) => r.company_name ?? '', maxWidth: 20 },\n\t\t\t\t\t{ header: 'LOCATION', value: (r) => r.area_display ?? '', maxWidth: 18 },\n\t\t\t\t\t{ header: 'SALARY', value: (r) => r.salary_display ?? '', maxWidth: 18 },\n\t\t\t\t\t{ header: 'UPDATED', value: (r) => formatDate(r.updated_at), maxWidth: 12 },\n\t\t\t\t],\n\t\t\t\tctx.color\n\t\t\t);\n\n\t\t\tconst head = `Showing page ${paged.currentPage}/${paged.totalPages} (${paged.data.length} of ${paged.totalCount} results).`;\n\t\t\tconst hint =\n\t\t\t\tpaged.totalPages > paged.currentPage ? ` Next: wport jobs search --page ${paged.currentPage + 1}` : '';\n\t\t\tprocess.stdout.write(dim(head + hint, ctx.color) + '\\n');\n\t\t});\n}\n\nfunction resolveSearchFields(flags: SearchFlags): string[] | undefined {\n\tif (flags.fields && flags.minimal) {\n\t\tthrow new CliError('Use either --fields or --minimal, not both', ExitCode.InvalidArgument);\n\t}\n\tif (flags.minimal) return [...MINIMAL_SEARCH_FIELDS];\n\tif (flags.fields) return parseFieldsList(flags.fields);\n\treturn undefined;\n}\n\nfunction buildQuery(flags: SearchFlags): SearchQuery {\n\tif (flags.jsonQuery) {\n\t\t// User-supplied JSON escape hatch — we trust the caller. Runtime validation will come\n\t\t// from the server side; this is the one spot the typed query is deliberately relaxed.\n\t\treturn readJsonQuery(flags.jsonQuery) as SearchQuery;\n\t}\n\tconst q: SearchQuery = {};\n\tif (flags.keyword) q.keyword = flags.keyword;\n\tif (flags.location?.length) q.area_codes = flags.location;\n\tif (flags.category?.length) q.job_classification_codes = flags.category;\n\tif (flags.page !== undefined) q.currentPage = flags.page;\n\tif (flags.pageSize !== undefined) q.pageSize = flags.pageSize;\n\treturn q;\n}\n\nfunction readJsonQuery(path: string): Record<string, unknown> {\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(path, 'utf8');\n\t} catch (err) {\n\t\tthrow new CliError(`Cannot read --json-query file ${path}: ${(err as Error).message}`, ExitCode.InvalidArgument);\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(raw);\n\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\n\t\t\t\tArray.isArray(parsed)\n\t\t\t\t\t? 'JSON root is an array; expected an object with query fields'\n\t\t\t\t\t: 'JSON root must be an object'\n\t\t\t);\n\t\t}\n\t\treturn parsed as Record<string, unknown>;\n\t} catch (err) {\n\t\tthrow new CliError(`Invalid JSON in ${path}: ${(err as Error).message}`, ExitCode.InvalidArgument);\n\t}\n}\n\nfunction truncate(s: string, max: number): string {\n\t// Fast path: UTF-16 code units (string.length) are always >= codepoint count,\n\t// so if byte-length already fits we don't need codepoint counting.\n\tif (s.length <= max) return s;\n\t// Array.from iterates by code-point, so surrogate pairs (emoji, supplementary\n\t// plane chars) aren't split mid-character. Doesn't handle grapheme clusters\n\t// (combining marks), but covers the common bilingual / emoji case.\n\tconst chars = Array.from(s);\n\tif (chars.length <= max) return s;\n\treturn chars.slice(0, max - 1).join('') + '…';\n}\n\nfunction formatDate(s: string | undefined): string {\n\tif (!s) return '';\n\tconst m = /^(\\d{4}-\\d{2}-\\d{2})/.exec(s);\n\treturn m ? m[1] : s;\n}\n","import createClient, { type Client } from 'openapi-fetch';\nimport type { paths } from '../generated/schema';\nimport { CliError, ExitCode, NetworkError, ServerClientHttpError } from './errors';\n\nexport interface ApiClientOptions {\n\tbaseUrl: string;\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\nexport type ApiClient = Client<paths>;\n\n// Connection reuse for batch / repeated requests is handled by Node's built-in fetch\n// (undici): its global dispatcher pools keep-alive connections per origin, so multiple\n// requests from one process to the same host already reuse TCP/TLS without any setup\n// here. We deliberately don't ship a custom `undici` Agent — it'd add a runtime\n// dependency to nudge keepAliveTimeout (~4s default → longer) for near-zero gain on the\n// short-lived bursts `jobs view --batch` produces.\nexport function createApiClient(opts: ApiClientOptions): ApiClient {\n\treturn createClient<paths>({\n\t\tbaseUrl: opts.baseUrl,\n\t\theaders: {\n\t\t\t'Accept-Language': opts.locale,\n\t\t\t'User-Agent': buildUserAgent(),\n\t\t\tAccept: 'application/json',\n\t\t},\n\t\tfetch: (request: Request) => fetchWithTimeout(request, opts.timeoutMs),\n\t});\n}\n\nexport function fetchWithTimeout(request: Request, timeoutMs: number): Promise<Response> {\n\t// AbortSignal.timeout is available since Node 17.3 (engines requires >=18.17).\n\t// New Request inherits method/headers/body but overrides signal.\n\tconst timedRequest = new Request(request, { signal: AbortSignal.timeout(timeoutMs) });\n\treturn fetch(timedRequest).catch((err: unknown) => {\n\t\tif (isTimeoutAbort(err)) {\n\t\t\tthrow new NetworkError(`Request timed out after ${timeoutMs}ms`, err);\n\t\t}\n\t\t// TypeError: fetch failed → ECONNREFUSED / ENOTFOUND / TLS / proxy\n\t\tconst code = (err as { cause?: { code?: string } })?.cause?.code;\n\t\tconst detail = code ? code : ((err as Error)?.message ?? String(err));\n\t\tthrow new NetworkError(`Cannot reach upstream: ${detail}`, err);\n\t});\n}\n\nfunction isTimeoutAbort(err: unknown): boolean {\n\tif (err && typeof err === 'object' && 'name' in err) {\n\t\tconst name = (err as { name?: string }).name;\n\t\treturn name === 'TimeoutError' || name === 'AbortError';\n\t}\n\treturn false;\n}\n\nexport function buildUserAgent(): string {\n\treturn `wport-cli/${__CLI_VERSION__} (node ${process.version}; ${process.platform})`;\n}\n\n/**\n * W101 後端 `DataResponse<T>` wrapper:`{ success, statusCode, message, data: T }`。\n * 用於回傳「單筆」payload 的端點(例如 GET /api/jobs/:encId/view)。\n *\n * Shape 不符直接 throw CliError,不再 silent cast 把 wrapper 當 payload 回。\n *\n * 注意:`PaginatedResponse` 是另一種扁平結構\n * `{ success, statusCode, message, data: T[], currentPage, totalPages, pageSize, totalCount }`,\n * 不要對 paginated 回應用此函式 —— 會丟失 pagination 欄位。\n */\nexport function unwrapDataResponse<T>(body: unknown): T {\n\tif (body && typeof body === 'object' && 'success' in body && 'data' in body) {\n\t\treturn (body as { data: T }).data;\n\t}\n\tthrow new CliError('Unexpected response shape: missing { success, data } wrapper', ExitCode.ServerOrNetworkError);\n}\n\n/**\n * W101 後端 `PaginatedResponse<T>` wrapper(扁平):\n * `{ success, statusCode, message, data: T[], currentPage, totalPages, pageSize, totalCount }`。\n * 直接 cast,不剝外層;callers 從 `.data` 拿 list,從頂層欄位拿分頁 metadata。\n */\nexport interface PaginatedBody<T> {\n\tsuccess?: boolean;\n\tstatusCode?: number;\n\tmessage?: unknown;\n\tdata: T[];\n\tcurrentPage: number;\n\ttotalPages: number;\n\tpageSize: number;\n\ttotalCount: number;\n}\n\nexport function asPaginatedBody<T>(body: unknown): PaginatedBody<T> {\n\tif (!body || typeof body !== 'object') {\n\t\tthrow new CliError('Unexpected response shape: not an object', ExitCode.ServerOrNetworkError);\n\t}\n\tconst b = body as Record<string, unknown>;\n\tif (!Array.isArray(b.data)) {\n\t\tthrow new CliError('Unexpected response shape: missing `data` array', ExitCode.ServerOrNetworkError);\n\t}\n\tfor (const key of ['currentPage', 'totalPages', 'pageSize', 'totalCount'] as const) {\n\t\tif (typeof b[key] !== 'number') {\n\t\t\tthrow new CliError(`Unexpected response shape: missing or non-numeric \"${key}\"`, ExitCode.ServerOrNetworkError);\n\t\t}\n\t}\n\treturn body as PaginatedBody<T>;\n}\n\nexport function throwForHttpStatus(status: number, body: unknown): never {\n\tconst message = extractErrorMessage(body) ?? `HTTP ${status}`;\n\tif (status >= 400 && status < 500) {\n\t\tthrow new ServerClientHttpError(message, status, body);\n\t}\n\tthrow new CliError(message, ExitCode.ServerOrNetworkError);\n}\n\nexport function extractErrorMessage(body: unknown): string | null {\n\tif (typeof body === 'string') return body;\n\tif (body && typeof body === 'object') {\n\t\tconst obj = body as Record<string, unknown>;\n\t\t// NestJS class-validator default: message: string[]\n\t\tif (Array.isArray(obj.message)) {\n\t\t\tconst parts = obj.message.filter((m): m is string => typeof m === 'string');\n\t\t\tif (parts.length > 0) return parts.join('; ');\n\t\t}\n\t\tif (typeof obj.message === 'string') return obj.message;\n\t\t// i18n object 或 nested → 印 JSON tail\n\t\tif (obj.message && typeof obj.message === 'object') {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(obj.message);\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t}\n\t\t// Enterprise API error body 帶 i18n path(如 error.enterprise.invalid_api_key),\n\t\t// message 缺席時讓 path 當最後可讀資訊(guard 直接丟 { path } 的情境)。\n\t\tif (typeof obj.path === 'string') return obj.path;\n\t\tif (typeof obj.error === 'string') return obj.error;\n\t}\n\treturn null;\n}\n","import {\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\tchmodSync,\n\topenSync,\n\twriteSync,\n\tcloseSync,\n\trenameSync,\n\tunlinkSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport envPaths from 'env-paths';\nimport { CliError, ExitCode } from './errors';\nimport { printWarn } from './output';\n\nexport const ALLOWED_LOCALES = ['zh-TW', 'en-US', 'vi-VN', 'th-TH', 'id-ID'] as const;\nexport type Locale = (typeof ALLOWED_LOCALES)[number];\n\nexport const ALLOWED_OUTPUT = ['table', 'json'] as const;\nexport type OutputPref = (typeof ALLOWED_OUTPUT)[number];\n\nexport interface CliConfig {\n\tlocale?: Locale;\n\toutput?: OutputPref;\n\ttimeout_ms?: number;\n}\n\nconst CONFIG_KEYS = ['locale', 'output', 'timeout_ms'] as const satisfies readonly (keyof CliConfig)[];\nexport type ConfigKey = (typeof CONFIG_KEYS)[number];\n\n/**\n * Keys recognised in older configs but no longer settable. Detected in parseConfig\n * to surface a one-time deprecation warning instead of silently dropping (which the\n * forward-compat path would do for genuinely unknown keys).\n *\n * `api_base_url` was removed in 0.1.2 — SSRF / credential exfil surface for a flag\n * external users don't actually need. Override via `WPORT_API_BASE` env var or `--api`.\n */\nconst DEPRECATED_CONFIG_KEYS = ['api_base_url'] as const;\ntype DeprecatedConfigKey = (typeof DEPRECATED_CONFIG_KEYS)[number];\n\n/** Per-key migration hint shown when a deprecated key is found in an existing config. */\nconst DEPRECATED_KEY_HINTS: Record<DeprecatedConfigKey, string> = {\n\tapi_base_url: 'Set the WPORT_API_BASE env var (or use --api) instead.',\n};\n\n// One-time latch so repeated loadConfig() calls (e.g. inside a batch run) emit the\n// deprecation notice at most once per process rather than spamming stderr.\nlet deprecationWarned = false;\n\n/** Per-key value type. validateAndCoerce<K> returns ConfigValueMap[K]. */\ntype ConfigValueMap = {\n\tlocale: Locale;\n\toutput: OutputPref;\n\ttimeout_ms: number;\n};\n\nexport function isConfigKey(key: string): key is ConfigKey {\n\treturn (CONFIG_KEYS as readonly string[]).includes(key);\n}\n\nexport function isDeprecatedConfigKey(key: string): key is DeprecatedConfigKey {\n\treturn (DEPRECATED_CONFIG_KEYS as readonly string[]).includes(key);\n}\n\nconst paths = envPaths('wport', { suffix: '' });\n\nexport function getConfigPath(): string {\n\treturn join(paths.config, 'config.json');\n}\n\n/**\n * Trust boundary:把外部 JSON object 收成型別正確的 CliConfig。\n * 每個 key 都走 validateAndCoerce,不認識的 key 直接 drop(forward compatible,\n * 未來新版多塞了 key、舊 CLI 不會炸)。\n */\nexport function parseConfig(raw: unknown): CliConfig {\n\tif (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n\t\tthrow new CliError('Config must be a JSON object', ExitCode.ConfigCorrupt);\n\t}\n\tconst input = raw as Record<string, unknown>;\n\n\t// Surface (once per process) any deprecated key still sitting in the user's config.\n\t// We warn + drop rather than error, so upgrading from <=0.1.1 never hard-fails; the\n\t// key's actual replacement (WPORT_API_BASE) is resolved elsewhere in global-opts.\n\tif (!deprecationWarned) {\n\t\tfor (const dep of DEPRECATED_CONFIG_KEYS) {\n\t\t\tif (dep in input) {\n\t\t\t\tdeprecationWarned = true;\n\t\t\t\tprintWarn(`Config key \"${dep}\" was removed in 0.1.2 and is ignored. ${DEPRECATED_KEY_HINTS[dep]}`, false);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst out: CliConfig = {};\n\tfor (const key of CONFIG_KEYS) {\n\t\tif (!(key in input)) continue;\n\t\tconst value = input[key];\n\t\ttry {\n\t\t\tconst coerced = validateAndCoerce(key, String(value));\n\t\t\t// Object.assign 形式避免 TS 5.5 在 `out[key] = coerced` 上推不出 ConfigValueMap[K]→CliConfig[K]\n\t\t\t// 的對應關係。validateAndCoerce 已是 generic、型別正確;此處只是 indexed assignment 的繞道。\n\t\t\tObject.assign(out, { [key]: coerced });\n\t\t} catch (err) {\n\t\t\tif (err instanceof CliError) {\n\t\t\t\tthrow new CliError(`Config key \"${key}\" invalid: ${err.message}`, ExitCode.ConfigCorrupt);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\treturn out;\n}\n\nexport function loadConfig(): CliConfig {\n\tconst path = getConfigPath();\n\tif (!existsSync(path)) return {};\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(path, 'utf8');\n\t} catch (err) {\n\t\tthrow new CliError(`Failed to read config at ${path}: ${(err as Error).message}`, ExitCode.ConfigCorrupt);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\tthrow new CliError(`Failed to parse JSON at ${path}: ${(err as Error).message}`, ExitCode.ConfigCorrupt);\n\t}\n\treturn parseConfig(parsed);\n}\n\nexport function saveConfig(config: CliConfig): void {\n\tconst path = getConfigPath();\n\tmkdirSync(dirname(path), { recursive: true });\n\n\t// Atomic: write tmpfile (mode 0o600 from open()) → rename to final path (POSIX atomic).\n\t// 即使中途 crash,舊檔仍完整、不會留 truncated JSON 觸發 ConfigCorrupt 鎖死 CLI。\n\tconst tmpPath = `${path}.tmp.${process.pid}.${Date.now()}`;\n\tconst fd = openSync(tmpPath, 'w', 0o600);\n\ttry {\n\t\twriteSync(fd, JSON.stringify(config, null, 2) + '\\n');\n\t} catch (err) {\n\t\tcloseSync(fd);\n\t\ttry {\n\t\t\tunlinkSync(tmpPath);\n\t\t} catch {\n\t\t\t/* best effort cleanup */\n\t\t}\n\t\tthrow err;\n\t}\n\tcloseSync(fd);\n\n\t// POSIX: openSync's mode is masked by umask (umask can only narrow, never widen).\n\t// chmod restores 0o600 in case a permissive umask stripped owner bits; it cannot\n\t// expose the file to group/other. On Windows chmodSync is a no-op, skip entirely.\n\tif (process.platform !== 'win32') {\n\t\ttry {\n\t\t\tchmodSync(tmpPath, 0o600);\n\t\t} catch (err) {\n\t\t\tprintWarn(\n\t\t\t\t`Failed to chmod 0600 on config tmpfile: ${(err as Error).message}. ` +\n\t\t\t\t\t'Other users on this system may be able to read CLI config.',\n\t\t\t\tfalse\n\t\t\t);\n\t\t}\n\t}\n\n\trenameSync(tmpPath, path);\n}\n\nexport function validateAndCoerce<K extends ConfigKey>(key: K, value: string): ConfigValueMap[K] {\n\tswitch (key) {\n\t\tcase 'locale': {\n\t\t\tif (!(ALLOWED_LOCALES as readonly string[]).includes(value)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Invalid locale \"${value}\". Allowed: ${ALLOWED_LOCALES.join(', ')}`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn value as ConfigValueMap[K];\n\t\t}\n\t\tcase 'output': {\n\t\t\tif (!(ALLOWED_OUTPUT as readonly string[]).includes(value)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Invalid output \"${value}\". Allowed: ${ALLOWED_OUTPUT.join(', ')}`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn value as ConfigValueMap[K];\n\t\t}\n\t\tcase 'timeout_ms': {\n\t\t\tconst n = Number(value);\n\t\t\tif (!Number.isInteger(n) || n < 100 || n > 600_000) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`timeout_ms must be an integer between 100 and 600000 (got ${value})`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn n as ConfigValueMap[K];\n\t\t}\n\t}\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = {\n\tresetDeprecationWarning(): void {\n\t\tdeprecationWarned = false;\n\t},\n};\n","import type { Command } from 'commander';\nimport { ALLOWED_LOCALES, loadConfig, type Locale, type CliConfig } from './config-store';\nimport { CliError, ExitCode } from './errors';\nimport { resolveOutputFormat, isColorEnabled, type OutputFormat } from './output';\n\n// Production public API. Local development overrides via the WPORT_API_BASE env var\n// or the `--api` flag. (The `api_base_url` config key was removed in 0.1.2 — keeping a\n// persisted, mutable base URL on disk is an SSRF / credential-exfil surface that\n// external users don't need.)\nconst DEFAULT_BASE_URL = 'https://api.wport.me';\nexport const API_BASE_ENV_VAR = 'WPORT_API_BASE';\nconst DEFAULT_LOCALE: Locale = 'zh-TW';\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport interface ResolvedContext {\n\tbaseUrl: string;\n\tlocale: Locale;\n\ttimeoutMs: number;\n\tformat: OutputFormat;\n\tcolor: boolean;\n\tconfig: CliConfig;\n}\n\ninterface RawGlobals {\n\tlang?: string;\n\tapi?: string;\n\toutput?: string;\n\tcolor?: boolean;\n\ttimeout?: number;\n}\n\nexport function resolveContext(command: Command): ResolvedContext {\n\tconst globals = command.optsWithGlobals() as RawGlobals;\n\tconst config = loadConfig();\n\n\treturn {\n\t\tbaseUrl: resolveBaseUrl(globals.api),\n\t\tlocale: resolveLocale(globals.lang, config),\n\t\ttimeoutMs: resolveTimeout(globals.timeout, config),\n\t\tformat: resolveOutputFormat(globals.output),\n\t\tcolor: isColorEnabled(globals.color === false),\n\t\tconfig,\n\t};\n}\n\n/**\n * Resolve the API base URL. Precedence: `--api` flag > WPORT_API_BASE env var > default.\n * The env var is just as untrusted as the (removed) config key, so it gets the same\n * http(s)-only validation to keep the SSRF surface closed.\n */\nfunction resolveBaseUrl(override: string | undefined): string {\n\tconst fromEnv = process.env[API_BASE_ENV_VAR]?.trim();\n\tif (override !== undefined) return validateBaseUrl(override, '--api');\n\tif (fromEnv) return validateBaseUrl(fromEnv, `${API_BASE_ENV_VAR} env var`);\n\treturn DEFAULT_BASE_URL;\n}\n\nfunction validateBaseUrl(raw: string, source: string): string {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(raw);\n\t} catch {\n\t\tthrow new CliError(`Invalid API base URL from ${source}: ${raw}`, ExitCode.InvalidArgument);\n\t}\n\tif (url.protocol !== 'https:' && url.protocol !== 'http:') {\n\t\tthrow new CliError(\n\t\t\t`API base URL from ${source} must be http or https (got ${url.protocol})`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\treturn raw.replace(/\\/$/, '');\n}\n\nfunction resolveLocale(override: string | undefined, config: CliConfig): Locale {\n\tconst raw = override ?? config.locale ?? DEFAULT_LOCALE;\n\tif (!(ALLOWED_LOCALES as readonly string[]).includes(raw)) {\n\t\tthrow new CliError(`Invalid --lang \"${raw}\". Allowed: ${ALLOWED_LOCALES.join(', ')}`, ExitCode.InvalidArgument);\n\t}\n\treturn raw as Locale;\n}\n\nfunction resolveTimeout(override: number | undefined, config: CliConfig): number {\n\tconst raw = override ?? config.timeout_ms ?? DEFAULT_TIMEOUT_MS;\n\tif (!Number.isInteger(raw) || raw < 100 || raw > 600_000) {\n\t\tthrow new CliError(`Invalid --timeout ${raw} (must be integer 100..600000)`, ExitCode.InvalidArgument);\n\t}\n\treturn raw;\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = {\n\tresolveBaseUrl,\n\tDEFAULT_BASE_URL,\n};\n","import { CliError, ExitCode } from './errors';\n\n/**\n * Read a value out of a nested object by dotted path (e.g. `job_info.job_title`).\n * Returns undefined if any segment is missing.\n *\n * Uses hasOwnProperty (not the `in` operator / direct index) so a path segment can\n * never traverse into `__proto__` / `constructor` and walk the prototype chain —\n * the input objects are server-controlled, so this is a deliberate safety boundary.\n */\nexport function getPath(obj: unknown, dottedPath: string): unknown {\n\tconst parts = dottedPath.split('.');\n\tlet cur: unknown = obj;\n\tfor (const p of parts) {\n\t\tif (cur && typeof cur === 'object' && Object.prototype.hasOwnProperty.call(cur, p)) {\n\t\t\tcur = (cur as Record<string, unknown>)[p];\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn cur;\n}\n\n/**\n * Project an object down to a set of dotted paths, keyed by the path string itself\n * (so `pickPaths(job, ['job_info.job_title'])` → `{ 'job_info.job_title': '...' }`).\n *\n * Every requested path becomes a key so the shape is predictable across a list of\n * heterogeneous items: a missing path yields `null` rather than being dropped, which\n * keeps each row in `jobs search --fields` structurally identical for downstream tools.\n */\nexport function pickPaths(obj: unknown, paths: string[]): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const p of paths) {\n\t\tconst v = getPath(obj, p);\n\t\tout[p] = v === undefined ? null : v;\n\t}\n\treturn out;\n}\n\n/**\n * Parse a comma-separated `--fields` value into a trimmed, non-empty list.\n * Throws InvalidArgument if the result is empty (e.g. `--fields ,,`).\n */\nexport function parseFieldsList(raw: string): string[] {\n\tconst fields = raw\n\t\t.split(',')\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean);\n\tif (fields.length === 0) {\n\t\tthrow new CliError('--fields requires at least one field name', ExitCode.InvalidArgument);\n\t}\n\treturn fields;\n}\n","/**\n * Run `fn` over `items` with at most `limit` in flight at once, returning results in\n * the SAME order as the input (not completion order) so callers can correlate output\n * rows back to their input without threading an index through.\n *\n * `fn` is expected to handle its own errors (e.g. resolve to an error-shaped result);\n * a rejection from `fn` will reject the whole batch, so the batch caller wraps each\n * unit in try/catch to keep one failure from sinking the rest.\n */\nexport async function mapWithConcurrency<T, R>(\n\titems: readonly T[],\n\tlimit: number,\n\tfn: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n\tconst results = new Array<R>(items.length);\n\tlet cursor = 0;\n\n\tasync function worker(): Promise<void> {\n\t\tfor (;;) {\n\t\t\tconst index = cursor++;\n\t\t\tif (index >= items.length) return;\n\t\t\tresults[index] = await fn(items[index], index);\n\t\t}\n\t}\n\n\tconst workerCount = Math.min(Math.max(1, limit), items.length);\n\tawait Promise.all(Array.from({ length: workerCount }, () => worker()));\n\treturn results;\n}\n","import { readSync } from 'node:fs';\nimport { CliError, ExitCode, InvalidArgumentError } from './errors';\n\n/**\n * Reject with `CliError(ServerOrNetworkError)` if a promise doesn't settle within `ms`.\n *\n * Reserved for async user / network input paths that v0.2 will add (streaming jobs,\n * interactive prompts with deadlines). Existing call sites either use their own\n * mechanism (`api-client` uses `AbortSignal.timeout`, `reset.ts` uses an `'end'`\n * handler) or are synchronous (`readFileSync(0)`). Removing this until then would\n * just churn the import graph; keeping it documents the contract.\n *\n * @internal — exported for future call sites, not part of public CLI API\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst t = setTimeout(() => {\n\t\t\treject(new CliError(`Timed out after ${ms}ms: ${label}`, ExitCode.ServerOrNetworkError));\n\t\t}, ms);\n\t\tpromise.then(\n\t\t\t(v) => {\n\t\t\t\tclearTimeout(t);\n\t\t\t\tresolve(v);\n\t\t\t},\n\t\t\t(err) => {\n\t\t\t\tclearTimeout(t);\n\t\t\t\treject(err);\n\t\t\t}\n\t\t);\n\t});\n}\n\n/** Convenience: throw InvalidArgumentError on TTY stdin reads with no piped input. */\nexport function ensureStdinPiped(label: string): void {\n\tif (process.stdin.isTTY) {\n\t\tthrow new InvalidArgumentError(`${label}: no data on stdin (run via pipe, or pass the value as an arg)`);\n\t}\n}\n\n/** Standalone backstop when a caller can't supply a `--timeout`-derived bound. */\nconst DEFAULT_STDIN_TIMEOUT_MS = 30_000;\n\nexport interface ReadPipedStdinOptions {\n\t/**\n\t * Upper bound (ms) on total time spent waiting for a slow / hung upstream pipe.\n\t * Callers pass the resolved `--timeout` so the limit is user-tunable; falls back to\n\t * DEFAULT_STDIN_TIMEOUT_MS when omitted.\n\t */\n\ttimeoutMs?: number;\n\t/** Injectable clock for tests; defaults to `Date.now`. */\n\tnow?: () => number;\n}\n\n/**\n * Synchronously drain piped stdin to a string, tolerating EAGAIN.\n *\n * A single `readFileSync(0)` / `readSync` can throw EAGAIN when stdin is a non-blocking\n * pipe whose upstream process is still producing (e.g. `wport jobs search ... | jq ... |\n * wport jobs view - --batch`): the fd has no data *right now* but isn't at EOF either.\n * Naively letting that throw makes the documented pipe workflow fail intermittently. We\n * retry on EAGAIN with a ~1ms synchronous sleep (Atomics.wait, to avoid a hot spin) and\n * stop on a zero-byte read or EOF.\n *\n * If the upstream neither produces data nor closes the pipe, the EAGAIN retry would spin\n * forever (the global `--timeout` only bounds HTTP, not stdin). We cap the total wait with\n * `timeoutMs` and surface a timeout as InvalidArgumentError rather than hanging silently.\n */\nexport function readPipedStdin(label: string, options: ReadPipedStdinOptions = {}): string {\n\tconst timeoutMs = options.timeoutMs ?? DEFAULT_STDIN_TIMEOUT_MS;\n\tconst now = options.now ?? Date.now;\n\tensureStdinPiped(label);\n\tconst chunks: Buffer[] = [];\n\tconst buf = Buffer.alloc(64 * 1024);\n\tconst sleeper = new Int32Array(new SharedArrayBuffer(4));\n\tconst deadline = now() + timeoutMs;\n\tfor (;;) {\n\t\tlet bytesRead: number;\n\t\ttry {\n\t\t\tbytesRead = readSync(0, buf, 0, buf.length, null);\n\t\t} catch (err) {\n\t\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\t\tif (code === 'EAGAIN') {\n\t\t\t\tif (now() > deadline) {\n\t\t\t\t\tthrow new InvalidArgumentError(`${label}: timed out after ${timeoutMs}ms waiting for piped stdin`);\n\t\t\t\t}\n\t\t\t\tAtomics.wait(sleeper, 0, 0, 1); // sleep ~1ms, then retry\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (code === 'EOF') break;\n\t\t\tthrow new InvalidArgumentError(`${label}: failed to read stdin: ${(err as Error).message}`);\n\t\t}\n\t\tif (bytesRead === 0) break;\n\t\tchunks.push(Buffer.from(buf.subarray(0, bytesRead)));\n\t}\n\treturn Buffer.concat(chunks).toString('utf8');\n}\n\nexport interface PromptSecretOptions {\n\t/** 注入點,預設用本檔的 readPipedStdin(測試可換 fake)。 */\n\treadPiped?: (label: string) => string;\n}\n\n/**\n * 互動式讀 secret:TTY 時 raw mode 隱藏輸入(不 echo、不進 shell history);\n * 非 TTY(CI / pipe)時直接讀整個 stdin。供 `wport enterprise login` 用。\n */\nexport function promptSecret(promptText: string, options: PromptSecretOptions = {}): Promise<string> {\n\tconst readPiped = options.readPiped ?? ((label: string) => readPipedStdin(label));\n\tif (!process.stdin.isTTY || !process.stdout.isTTY) {\n\t\treturn Promise.resolve(readPiped('login').trim());\n\t}\n\tprocess.stdout.write(promptText);\n\treturn new Promise<string>((resolve, reject) => {\n\t\tconst stdin = process.stdin;\n\t\tstdin.setRawMode(true);\n\t\tstdin.resume();\n\t\tstdin.setEncoding('utf8');\n\t\tlet buf = '';\n\t\tconst cleanup = (): void => {\n\t\t\tstdin.setRawMode(false);\n\t\t\tstdin.pause();\n\t\t\tstdin.off('data', onData);\n\t\t};\n\t\tconst onData = (chunk: string): void => {\n\t\t\tfor (const ch of chunk) {\n\t\t\t\tif (ch === '\u0003') {\n\t\t\t\t\t// Ctrl-C\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\treject(new CliError('Aborted', ExitCode.InvalidArgument));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (ch === '\\r' || ch === '\\n') {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tresolve(buf.trim());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (ch === '' || ch === '\\b') {\n\t\t\t\t\tbuf = buf.slice(0, -1);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbuf += ch;\n\t\t\t}\n\t\t};\n\t\tstdin.on('data', onData);\n\t});\n}\n","import type { Command } from 'commander';\nimport { createApiClient, throwForHttpStatus, unwrapDataResponse, type ApiClient } from '../../lib/api-client';\nimport { resolveContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { dim, printJson, printNdjsonLine, sanitizeForTerminal, sanitizeForTerminalMultiline } from '../../lib/output';\nimport { getPath, parseFieldsList, pickPaths } from '../../lib/path-utils';\nimport { mapWithConcurrency } from '../../lib/concurrency';\nimport { readPipedStdin } from '../../lib/io-helpers';\nimport pc from 'picocolors';\n\ninterface ViewFlags {\n\tfield?: string;\n\tfields?: string;\n\tbatch?: boolean;\n\tconcurrency?: number;\n}\n\nconst DEFAULT_BATCH_CONCURRENCY = 5;\nconst MAX_BATCH_CONCURRENCY = 20;\n\n/** One ND-JSON record emitted per enc_id in --batch mode. */\ninterface BatchResult {\n\tenc_id: string;\n\tok: boolean;\n\tdata?: unknown;\n\terror?: string;\n}\n\n/** Projects a fetched job down to whatever --field / --fields asked for (or the whole job). */\ntype JobProjector = (job: JobView) => unknown;\n\n/**\n * JobViewVM 是嵌套結構(見 src/modules/jobs/view-models/job-view.vm.ts)。\n * 這裡只列我們顯示時會碰到的欄位,其他欄位走 [k: string]: unknown 保留。\n */\ninterface JobView {\n\tcompany_header_info?: {\n\t\tcompany_name?: string;\n\t\tcompany_icon_url?: string;\n\t\tenc_company_id?: string;\n\t\t[k: string]: unknown;\n\t};\n\tjob_description?: string;\n\tjob_info?: {\n\t\tjob_title?: string;\n\t\tarea_display?: string;\n\t\tsalary_display?: string;\n\t\tjob_feature_display?: string | null;\n\t\texperience_display?: string | null;\n\t\t[k: string]: unknown;\n\t};\n\tjob_information?: Record<string, unknown>;\n\trecruitment_conditions?: Record<string, unknown>;\n\tbenefits?: Record<string, unknown>;\n\tabout_company?: Record<string, unknown> | null;\n\tapplication_method?: Record<string, unknown> | null;\n\tstructured_data?: Record<string, unknown> | null;\n\t[k: string]: unknown;\n}\n\nexport function registerJobsView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_id>')\n\t\t.description('View a single job. Pass \"-\" to read enc_id from stdin.')\n\t\t.option('--field <path>', 'output a single field as a raw value (dotted paths, e.g. job_info.job_title)')\n\t\t.option(\n\t\t\t'--fields <list>',\n\t\t\t'output selected fields as a JSON object (comma-separated dotted paths, e.g. job_info.job_title,company_header_info.company_name)'\n\t\t)\n\t\t.option(\n\t\t\t'--batch',\n\t\t\t'read newline-separated enc_ids from stdin and emit one ND-JSON record per job (requires \"-\" as the enc_id arg)'\n\t\t)\n\t\t.option(\n\t\t\t'--concurrency <n>',\n\t\t\t`max parallel requests in --batch mode (default ${DEFAULT_BATCH_CONCURRENCY}, max ${MAX_BATCH_CONCURRENCY})`,\n\t\t\t(v) => Number(v)\n\t\t)\n\t\t.action(async (encIdArg: string, flags: ViewFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tif (flags.field && flags.fields) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t'Use either --field (single raw value) or --fields (JSON object), not both',\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst client = createApiClient({\n\t\t\t\tbaseUrl: ctx.baseUrl,\n\t\t\t\tlocale: ctx.locale,\n\t\t\t\ttimeoutMs: ctx.timeoutMs,\n\t\t\t});\n\n\t\t\tif (flags.batch) {\n\t\t\t\tawait runBatchView(encIdArg, flags, client, ctx.timeoutMs);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst encId = encIdArg === '-' ? readPipedStdin('view -', { timeoutMs: ctx.timeoutMs }).trim() : encIdArg;\n\t\t\tif (!encId) {\n\t\t\t\tthrow new CliError('enc_id is required', ExitCode.InvalidArgument);\n\t\t\t}\n\n\t\t\tconst { data, error, response } = await client.GET('/api/jobs/{encId}/view', {\n\t\t\t\tparams: { path: { encId } },\n\t\t\t});\n\t\t\tif (!response.ok) throwForHttpStatus(response.status, error);\n\n\t\t\tconst job = unwrapDataResponse<JobView>(data);\n\n\t\t\tif (flags.fields) {\n\t\t\t\t// Multi-field projection → JSON object keyed by dotted path. printJson uses\n\t\t\t\t// JSON.stringify, which escapes control chars to \\uXXXX, so employer-controlled\n\t\t\t\t// string values are safe without extra sanitization (same rationale as the\n\t\t\t\t// json branch below).\n\t\t\t\tprintJson(pickPaths(job, parseFieldsList(flags.fields)));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (flags.field) {\n\t\t\t\tconst v = getPath(job, flags.field);\n\t\t\t\tif (v === undefined) {\n\t\t\t\t\tthrow new CliError(`Field \"${flags.field}\" not present in response`, ExitCode.InvalidArgument);\n\t\t\t\t}\n\t\t\t\t// String values are employer-controlled and printed raw — sanitize. The multiline\n\t\t\t\t// variant preserves \\n (e.g. when --field selects job_description) but still strips\n\t\t\t\t// every other control char including \\r and \\t. JSON.stringify already escapes\n\t\t\t\t// < 0x20 to \\uXXXX so non-string paths don't need extra handling.\n\t\t\t\tconst out = typeof v === 'string' ? sanitizeForTerminalMultiline(v) : JSON.stringify(v, null, 2);\n\t\t\t\tprocess.stdout.write(out + '\\n');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (ctx.format === 'json') {\n\t\t\t\tprintJson(job);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\trenderJobTable(job, encId, ctx.color);\n\t\t});\n}\n\n/**\n * --batch orchestration: read enc_ids off stdin, fetch each (bounded parallelism),\n * and stream one ND-JSON record per job. One failed enc_id becomes an `ok:false` line\n * rather than aborting the whole run. Output order matches input order.\n */\nasync function runBatchView(encIdArg: string, flags: ViewFlags, client: ApiClient, timeoutMs: number): Promise<void> {\n\tif (encIdArg !== '-') {\n\t\tthrow new CliError('--batch reads enc_ids from stdin; pass \"-\" as the enc_id argument', ExitCode.InvalidArgument);\n\t}\n\tconst encIds = parseBatchInput(readPipedStdin('view - --batch', { timeoutMs }));\n\tif (encIds.length === 0) {\n\t\tthrow new CliError('No enc_ids found on stdin', ExitCode.InvalidArgument);\n\t}\n\tconst concurrency = resolveBatchConcurrency(flags.concurrency);\n\tconst project = makeBatchProjector(flags);\n\tconst results = await runBatch(encIds, concurrency, (encId) => fetchJob(client, encId), project);\n\tfor (const record of results) printNdjsonLine(record);\n}\n\nasync function fetchJob(client: ApiClient, encId: string): Promise<JobView> {\n\tconst { data, error, response } = await client.GET('/api/jobs/{encId}/view', {\n\t\tparams: { path: { encId } },\n\t});\n\tif (!response.ok) throwForHttpStatus(response.status, error);\n\treturn unwrapDataResponse<JobView>(data);\n}\n\n/**\n * Pure batch driver (injectable fetcher) so the success/failure-mix behaviour is\n * testable without a live API. Each unit is wrapped so a rejected fetch turns into an\n * `ok:false` record instead of rejecting the whole batch.\n */\nasync function runBatch(\n\tencIds: string[],\n\tconcurrency: number,\n\tfetchOne: (encId: string) => Promise<JobView>,\n\tproject: JobProjector\n): Promise<BatchResult[]> {\n\treturn mapWithConcurrency(encIds, concurrency, async (encId): Promise<BatchResult> => {\n\t\ttry {\n\t\t\tconst job = await fetchOne(encId);\n\t\t\treturn { enc_id: encId, ok: true, data: project(job) };\n\t\t} catch (err) {\n\t\t\treturn { enc_id: encId, ok: false, error: err instanceof Error ? err.message : String(err) };\n\t\t}\n\t});\n}\n\nfunction makeBatchProjector(flags: ViewFlags): JobProjector {\n\tif (flags.fields) {\n\t\tconst paths = parseFieldsList(flags.fields);\n\t\treturn (job) => pickPaths(job, paths);\n\t}\n\tif (flags.field) {\n\t\tconst path = flags.field;\n\t\t// Missing path → null (keeps every record's shape stable across the batch),\n\t\t// unlike single-view --field which errors on a missing path.\n\t\treturn (job) => getPath(job, path) ?? null;\n\t}\n\treturn (job) => job;\n}\n\nfunction parseBatchInput(raw: string): string[] {\n\treturn raw\n\t\t.split('\\n')\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean);\n}\n\nfunction resolveBatchConcurrency(raw: number | undefined): number {\n\tconst n = raw ?? DEFAULT_BATCH_CONCURRENCY;\n\tif (!Number.isInteger(n) || n < 1 || n > MAX_BATCH_CONCURRENCY) {\n\t\tthrow new CliError(\n\t\t\t`--concurrency must be an integer between 1 and ${MAX_BATCH_CONCURRENCY} (got ${raw})`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\treturn n;\n}\n\nfunction renderJobTable(job: JobView, encId: string, color: boolean): void {\n\tconst label = (s: string) => (color ? pc.bold(s) : s);\n\t// All API string values are employer-controlled; sanitize before printing to defend\n\t// against terminal escape injection (clear screen, OSC 8 phishing hyperlinks, etc.).\n\tconst s = (v: string | undefined | null): string => (v ? sanitizeForTerminal(v) : '');\n\tconst info = job.job_info ?? {};\n\tconst company = job.company_header_info ?? {};\n\n\tconst lines: string[] = [];\n\tif (info.job_title) lines.push(`${label('Title:')} ${s(info.job_title)}`);\n\tif (company.company_name) lines.push(`${label('Company:')} ${s(company.company_name)}`);\n\tif (info.area_display) lines.push(`${label('Location:')} ${s(info.area_display)}`);\n\tif (info.salary_display) lines.push(`${label('Salary:')} ${s(info.salary_display)}`);\n\tif (info.job_feature_display) lines.push(`${label('Type:')} ${s(info.job_feature_display)}`);\n\tif (info.experience_display) lines.push(`${label('Experience:')} ${s(info.experience_display)}`);\n\t// encId comes from the CLI arg (user-provided) but pass through sanitize as a defense-in-depth.\n\tlines.push(dim(`enc_id: ${s(encId)}`, color));\n\tif (company.enc_company_id) lines.push(dim(`enc_company_id: ${s(company.enc_company_id)}`, color));\n\tprocess.stdout.write(lines.join('\\n') + '\\n');\n\n\tif (job.job_description) {\n\t\tprocess.stdout.write('\\n' + label('Description') + '\\n');\n\t\t// stripHtml only removes tags; sanitize afterwards in case the rich-text source\n\t\t// embedded raw escape sequences inside text nodes. Use the multiline variant so the\n\t\t// description's paragraph breaks (\\n) are preserved while \\r / \\t / other controls\n\t\t// are still stripped.\n\t\tprocess.stdout.write(renderDescription(job.job_description) + '\\n');\n\t}\n\n\tprocess.stdout.write(\n\t\t'\\n' +\n\t\t\tdim(\n\t\t\t\t'Tip: use --output json (or --field <dotted.path>, e.g. --field job_info.salary_display) for scripting.',\n\t\t\t\tcolor\n\t\t\t) +\n\t\t\t'\\n'\n\t);\n}\n\n/**\n * 後端 job_description 可能含 HTML(rich text);CLI 終端機列印時剝掉 tag。\n * 不做完整 HTML 解析 —— 只把 tag 拿掉、& 實體做最常見的還原。\n */\nfunction stripHtml(s: string): string {\n\treturn s\n\t\t.replace(/<\\/?(p|br|div|li|h[1-6])[^>]*>/gi, '\\n')\n\t\t.replace(/<[^>]+>/g, '')\n\t\t.replace(/&nbsp;/g, ' ')\n\t\t.replace(/&amp;/g, '&')\n\t\t.replace(/&lt;/g, '<')\n\t\t.replace(/&gt;/g, '>')\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&#39;/g, \"'\")\n\t\t.replace(/\\n{3,}/g, '\\n\\n')\n\t\t.trim();\n}\n\nfunction renderDescription(html: string): string {\n\treturn sanitizeForTerminalMultiline(stripHtml(html));\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = {\n\tstripHtml,\n\tgetPath,\n\trenderDescription,\n\trunBatch,\n\tmakeBatchProjector,\n\tparseBatchInput,\n\tresolveBatchConcurrency,\n};\n","import type { Command } from 'commander';\nimport { registerJobsSearch } from './search';\nimport { registerJobsView } from './view';\n\nexport function registerJobsCommand(program: Command): void {\n\tconst jobs = program.command('jobs').description('Search and view public job listings');\n\tregisterJobsSearch(jobs);\n\tregisterJobsView(jobs);\n}\n","import type { Command } from 'commander';\nimport { isConfigKey, isDeprecatedConfigKey, loadConfig, saveConfig, validateAndCoerce } from '../../lib/config-store';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { API_BASE_ENV_VAR } from '../../lib/global-opts';\n\nexport function registerConfigSet(parent: Command): void {\n\tparent\n\t\t.command('set <key> <value>')\n\t\t.description('Set a config value. Keys: locale, output, timeout_ms')\n\t\t.action((key: string, value: string) => {\n\t\t\tif (isDeprecatedConfigKey(key)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Config key \"${key}\" was removed in 0.1.2. Set the ${API_BASE_ENV_VAR} env var ` +\n\t\t\t\t\t\t`(or use the --api flag) instead.`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isConfigKey(key)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Unknown config key \"${key}\". Allowed: locale, output, timeout_ms`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst coerced = validateAndCoerce(key, value);\n\t\t\tconst config = loadConfig();\n\t\t\t// Object.assign 形式避免 TS 5.5 在 `config[key] = coerced` 上推不出\n\t\t\t// ConfigValueMap[K]→CliConfig[K] 的對應;validateAndCoerce 已 generic、型別正確。\n\t\t\tObject.assign(config, { [key]: coerced });\n\t\t\tsaveConfig(config);\n\t\t\tprocess.stdout.write(`Set ${key} = ${JSON.stringify(coerced)}\\n`);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { isConfigKey, loadConfig } from '../../lib/config-store';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { printJson } from '../../lib/output';\n\nexport function registerConfigGet(parent: Command): void {\n\tparent\n\t\t.command('get [key]')\n\t\t.description('Print config value(s). With no key, prints the whole config as JSON.')\n\t\t.action((key: string | undefined) => {\n\t\t\tconst config = loadConfig();\n\t\t\tif (key === undefined) {\n\t\t\t\tprintJson(config);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!isConfigKey(key)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Unknown config key \"${key}\". Allowed: locale, output, timeout_ms`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst value = (config as Record<string, unknown>)[key];\n\t\t\tif (value === undefined) {\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprocess.stdout.write((typeof value === 'string' ? value : JSON.stringify(value)) + '\\n');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { getConfigPath } from '../../lib/config-store';\n\nexport function registerConfigPath(parent: Command): void {\n\tparent\n\t\t.command('path')\n\t\t.description('Print the path to the config file (regardless of whether it exists)')\n\t\t.action(() => {\n\t\t\tprocess.stdout.write(getConfigPath() + '\\n');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { existsSync, unlinkSync } from 'node:fs';\nimport { getConfigPath } from '../../lib/config-store';\nimport { InvalidArgumentError } from '../../lib/errors';\n\nexport function registerConfigReset(parent: Command): void {\n\tparent\n\t\t.command('reset')\n\t\t.description('Delete the config file')\n\t\t.option('-f, --force', 'skip the confirmation prompt')\n\t\t.action(async (opts: { force?: boolean }) => {\n\t\t\tconst path = getConfigPath();\n\t\t\tif (!existsSync(path)) {\n\t\t\t\tprocess.stdout.write('No config file to delete.\\n');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!opts.force) {\n\t\t\t\tconst ok = await promptYesNo(`Delete config at ${path}? [y/N] `);\n\t\t\t\tif (!ok) {\n\t\t\t\t\tprocess.stdout.write('Aborted.\\n');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tunlinkSync(path);\n\t\t\t} catch (err) {\n\t\t\t\tconst e = err as NodeJS.ErrnoException;\n\t\t\t\tif (e.code === 'ENOENT') {\n\t\t\t\t\t// Race with another process: someone deleted it between exists & unlink.\n\t\t\t\t\tprocess.stdout.write('No config file to delete (already removed).\\n');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthrow new InvalidArgumentError(`Failed to delete config at ${path}: ${e.message}`);\n\t\t\t}\n\t\t\tprocess.stdout.write(`Deleted ${path}\\n`);\n\t\t});\n}\n\nfunction promptYesNo(prompt: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tprocess.stdout.write(prompt);\n\t\tlet buf = '';\n\t\tprocess.stdin.setEncoding('utf8');\n\t\tconst onData = (chunk: string) => {\n\t\t\tbuf += chunk;\n\t\t\tconst nl = buf.indexOf('\\n');\n\t\t\tif (nl >= 0) {\n\t\t\t\tcleanup();\n\t\t\t\tconst answer = buf.slice(0, nl).trim().toLowerCase();\n\t\t\t\tresolve(answer === 'y' || answer === 'yes');\n\t\t\t}\n\t\t};\n\t\tconst onEnd = () => {\n\t\t\tcleanup();\n\t\t\tprocess.stdout.write('\\n(no input — aborting)\\n');\n\t\t\tresolve(false);\n\t\t};\n\t\tconst cleanup = () => {\n\t\t\tprocess.stdin.removeListener('data', onData);\n\t\t\tprocess.stdin.removeListener('end', onEnd);\n\t\t\tprocess.stdin.pause();\n\t\t};\n\t\tprocess.stdin.on('data', onData);\n\t\tprocess.stdin.on('end', onEnd);\n\t});\n}\n","import type { Command } from 'commander';\nimport { registerConfigSet } from './set';\nimport { registerConfigGet } from './get';\nimport { registerConfigPath } from './path';\nimport { registerConfigReset } from './reset';\n\nexport function registerConfigCommand(program: Command): void {\n\tconst config = program.command('config').description('Manage CLI configuration');\n\tregisterConfigSet(config);\n\tregisterConfigGet(config);\n\tregisterConfigPath(config);\n\tregisterConfigReset(config);\n}\n","import type { Command } from 'commander';\nimport { existsSync } from 'node:fs';\nimport { createApiClient } from '../lib/api-client';\nimport { resolveContext } from '../lib/global-opts';\nimport { getConfigPath } from '../lib/config-store';\nimport { ExitCode } from '../lib/errors';\n\n/**\n * Query params the server accepts syntactically but silently ignores — surfacing them\n * here is the whole point: an agent reading `wport doctor` learns not to try to control\n * sort order (it can't), instead of discovering it the hard way via wrong-but-no-error\n * results. The CLI deliberately doesn't expose flags for these.\n */\nexport const SILENT_IGNORED_PARAMS = ['orderBy', 'order'];\n\nexport function registerDoctorCommand(program: Command): void {\n\tprogram\n\t\t.command('doctor')\n\t\t.description(\n\t\t\t'Diagnose CLI setup: resolved config, server reachability, schema fingerprint, and known server quirks.'\n\t\t)\n\t\t.action(async (_opts: unknown, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst line = (s = '') => process.stdout.write(s + '\\n');\n\n\t\t\tline(`wport-cli ${__CLI_VERSION__}`);\n\t\t\tline(` bundled schema fingerprint: ${__SCHEMA_HASH__}`);\n\t\t\tline('');\n\n\t\t\tline('Resolved configuration:');\n\t\t\tline(` API base URL: ${ctx.baseUrl}`);\n\t\t\tline(` locale: ${ctx.locale}`);\n\t\t\tline(` timeout: ${ctx.timeoutMs}ms`);\n\t\t\tconst cfgPath = getConfigPath();\n\t\t\tline(` config file: ${cfgPath}${existsSync(cfgPath) ? '' : ' (not present)'}`);\n\t\t\tline('');\n\n\t\t\tline('Server connectivity:');\n\t\t\tconst reachable = await probeServer(ctx, line);\n\t\t\tline('');\n\n\t\t\tline('Known server behaviours (read this before scripting an agent):');\n\t\t\tline(\n\t\t\t\t` • Sort is server-controlled. These query params are silently ignored: ${SILENT_IGNORED_PARAMS.join(', ')}.`\n\t\t\t);\n\t\t\tline(' • jobs search sorts by publish date, or by relevance when --keyword is set.');\n\t\t\tline(' • jobs view --batch caps parallelism (default 5, max 20) to stay friendly to the API.');\n\t\t\tline('');\n\n\t\t\tline('Schema drift:');\n\t\t\tline(' The fingerprint above identifies the OpenAPI contract this CLI was built against.');\n\t\t\tline(' Automated drift detection needs a server-side schema-version endpoint, which is');\n\t\t\tline(' not available yet — for now, compare fingerprints manually after a server release. [TODO]');\n\n\t\t\tif (!reachable) process.exit(ExitCode.ServerOrNetworkError);\n\t\t});\n}\n\n/**\n * Lightweight reachability probe: a 1-result search hits the real public endpoint\n * without pulling a meaningful payload. A network-layer failure is a hard \"unreachable\"\n * (caller exits non-zero); an HTTP response of any status still proves the host is\n * reachable, so we report the status but don't treat it as a connectivity failure.\n */\nasync function probeServer(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number },\n\tline: (s?: string) => void\n): Promise<boolean> {\n\ttry {\n\t\tconst client = createApiClient({ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs });\n\t\tconst { response } = await client.GET('/api/jobs/search', { params: { query: { pageSize: 1 } } });\n\t\tif (response.ok) {\n\t\t\tline(` ✓ reachable (HTTP ${response.status})`);\n\t\t} else {\n\t\t\tline(` ! reachable, but server responded HTTP ${response.status}`);\n\t\t}\n\t\treturn true;\n\t} catch (err) {\n\t\tline(` ✗ unreachable: ${err instanceof Error ? err.message : String(err)}`);\n\t\treturn false;\n\t}\n}\n","import { buildUserAgent, extractErrorMessage, fetchWithTimeout, throwForHttpStatus } from './api-client';\nimport { CliError, ExitCode } from './errors';\nimport { printWarn } from './output';\n\nexport interface EnterpriseRequestOptions {\n\tbaseUrl: string; // resolveContext 產出,已去尾斜線\n\tlocale: string;\n\ttimeoutMs: number;\n\tapiKey: string;\n}\n\nexport interface EnterpriseGetResult {\n\tbody: unknown;\n\theaders: Headers;\n}\n\nconst ENTERPRISE_PREFIX = '/api/v1/enterprise';\n\n/**\n * 企業 API 唯讀 GET。手寫 wrapper 而非 openapi-fetch:openapi.yaml 缺 read 端點\n * response schema(spec §4.1 修訂),typed client 在這裡沒有可生成的型別。\n * timeout / 網路錯誤分類複用 api-client.fetchWithTimeout。\n */\nexport async function enterpriseGet(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tquery?: Record<string, string | number | undefined>\n): Promise<EnterpriseGetResult> {\n\tconst url = new URL(`${opts.baseUrl}${ENTERPRISE_PREFIX}${path}`);\n\tfor (const [k, v] of Object.entries(query ?? {})) {\n\t\tif (v !== undefined) url.searchParams.set(k, String(v));\n\t}\n\tconst request = new Request(url, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${opts.apiKey}`,\n\t\t\t'Accept-Language': opts.locale,\n\t\t\t'User-Agent': buildUserAgent(),\n\t\t\tAccept: 'application/json',\n\t\t},\n\t});\n\tconst res = await fetchWithTimeout(request, opts.timeoutMs);\n\tconst body: unknown = await res.json().catch(() => null);\n\tif (!res.ok) throwEnterpriseHttpError(res.status, body);\n\twarnIfRateLimitLow(res.headers);\n\treturn { body, headers: res.headers };\n}\n\n/** spec §5:401/403 加情境提示;其他狀態走共用 throwForHttpStatus。 */\nfunction throwEnterpriseHttpError(status: number, body: unknown): never {\n\tconst base = extractErrorMessage(body) ?? `HTTP ${status}`;\n\tif (status === 401) {\n\t\tthrow new CliError(\n\t\t\t`${base}\\nKey may have been revoked — run \\`wport enterprise login\\` to re-authenticate.`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n\tif (status === 403) {\n\t\tthrow new CliError(\n\t\t\t`${base}\\nIf your company account has been suspended, please contact support.`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n\tthrowForHttpStatus(status, body);\n}\n\n/** spec §5:剩餘配額 <10% 時 stderr 提醒(不阻斷;stderr 不污染 json stdout)。 */\nfunction warnIfRateLimitLow(headers: Headers): void {\n\tconst remaining = Number(headers.get('x-ratelimit-remaining'));\n\tconst limit = Number(headers.get('x-ratelimit-limit'));\n\tif (Number.isFinite(remaining) && Number.isFinite(limit) && limit > 0 && remaining / limit < 0.1) {\n\t\tprintWarn(`Rate limit nearly exhausted: ${remaining}/${limit} requests remaining this window.`, false);\n\t}\n}\n","import {\n\texistsSync,\n\treadFileSync,\n\tmkdirSync,\n\topenSync,\n\twriteSync,\n\tcloseSync,\n\tchmodSync,\n\trenameSync,\n\tunlinkSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport envPaths from 'env-paths';\nimport { CliError, ExitCode } from './errors';\nimport { printWarn } from './output';\n\nexport const API_KEY_ENV_VAR = 'WPORT_API_KEY';\nexport const KEY_PREFIX = 'wpk_live_';\n// Server contract(spec §2.1):wpk_live_ + 32 高熵字元。下限驗證擋手滑貼半截;\n// 不驗上限——server 端才是 key 有效性的唯一權威。\nconst KEY_MIN_LENGTH = KEY_PREFIX.length + 32;\n\nexport interface Credentials {\n\tapi_key: string;\n\tcompany_name: string;\n\tkey_last4: string;\n\tsaved_at: string;\n}\n\nexport type KeySource = 'flag' | 'env' | 'file';\n\nexport interface ResolvedKey {\n\tkey: string;\n\tsource: KeySource;\n}\n\nconst paths = envPaths('wport', { suffix: '' });\n\nexport function getCredentialsPath(): string {\n\treturn join(paths.config, 'credentials.json');\n}\n\nexport function isValidKeyFormat(key: string): boolean {\n\treturn key.startsWith(KEY_PREFIX) && key.length >= KEY_MIN_LENGTH && !/\\s/.test(key);\n}\n\n/** 任何輸出顯示 key 一律走這裡:只露末四碼。 */\nexport function maskKey(key: string): string {\n\treturn `${KEY_PREFIX}••••${key.slice(-4)}`;\n}\n\nexport function loadCredentials(): Credentials | null {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return null;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, 'utf8'));\n\t} catch (err) {\n\t\tthrow new CliError(\n\t\t\t`Failed to read credentials at ${path}: ${(err as Error).message}. Run \"wport enterprise login\" to recreate it.`,\n\t\t\tExitCode.ConfigCorrupt\n\t\t);\n\t}\n\tif (!parsed || typeof parsed !== 'object' || typeof (parsed as Record<string, unknown>).api_key !== 'string') {\n\t\tthrow new CliError(\n\t\t\t`Credentials file at ${path} is malformed. Run \"wport enterprise login\" to recreate it.`,\n\t\t\tExitCode.ConfigCorrupt\n\t\t);\n\t}\n\tconst raw = parsed as Record<string, unknown>;\n\tconst apiKey = raw.api_key as string;\n\treturn {\n\t\tapi_key: apiKey,\n\t\tcompany_name: typeof raw.company_name === 'string' ? raw.company_name : '',\n\t\tkey_last4: typeof raw.key_last4 === 'string' ? raw.key_last4 : apiKey.slice(-4),\n\t\tsaved_at: typeof raw.saved_at === 'string' ? raw.saved_at : '',\n\t};\n}\n\n// Atomic write 模式照抄 config-store.saveConfig:tmpfile(0o600) → rename。\n// credentials 比 config 更敏感,所以分檔(config 可能被使用者貼進 issue 除錯)。\nexport function saveCredentials(creds: Credentials): void {\n\tconst path = getCredentialsPath();\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst tmpPath = `${path}.tmp.${process.pid}.${Date.now()}`;\n\tconst fd = openSync(tmpPath, 'w', 0o600);\n\ttry {\n\t\twriteSync(fd, JSON.stringify(creds, null, 2) + '\\n');\n\t} catch (err) {\n\t\tcloseSync(fd);\n\t\ttry {\n\t\t\tunlinkSync(tmpPath);\n\t\t} catch {\n\t\t\t/* best effort cleanup */\n\t\t}\n\t\tthrow err;\n\t}\n\tcloseSync(fd);\n\tif (process.platform !== 'win32') {\n\t\ttry {\n\t\t\tchmodSync(tmpPath, 0o600);\n\t\t} catch (err) {\n\t\t\tprintWarn(\n\t\t\t\t`Failed to chmod 0600 on credentials tmpfile: ${(err as Error).message}. ` +\n\t\t\t\t\t'Other users on this system may be able to read your API key.',\n\t\t\t\tfalse\n\t\t\t);\n\t\t}\n\t}\n\trenameSync(tmpPath, path);\n}\n\nexport function deleteCredentials(): boolean {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return false;\n\tunlinkSync(path);\n\treturn true;\n}\n\n/**\n * Key 解析 precedence:--api-key flag > WPORT_API_KEY env > credentials.json。\n * 與 base url 解析(--api > WPORT_API_BASE > default,global-opts.ts)同款心智模型。\n */\nexport function resolveApiKey(flagValue?: string): ResolvedKey {\n\tif (flagValue !== undefined) {\n\t\tensureFormat(flagValue, '--api-key');\n\t\treturn { key: flagValue, source: 'flag' };\n\t}\n\tconst fromEnv = process.env[API_KEY_ENV_VAR]?.trim();\n\tif (fromEnv) {\n\t\tensureFormat(fromEnv, `${API_KEY_ENV_VAR} env var`);\n\t\treturn { key: fromEnv, source: 'env' };\n\t}\n\tconst creds = loadCredentials();\n\tif (creds) return { key: creds.api_key, source: 'file' };\n\tthrow new CliError(\n\t\t`No API key found. Run \"wport enterprise login\" or set the ${API_KEY_ENV_VAR} env var.`,\n\t\tExitCode.InvalidArgument\n\t);\n}\n\nfunction ensureFormat(key: string, source: string): void {\n\tif (!isValidKeyFormat(key)) {\n\t\t// 錯誤訊息絕不 echo key 原文(可能是手滑貼進來的其他 secret)\n\t\tthrow new CliError(`API key from ${source} is not a valid ${KEY_PREFIX} key`, ExitCode.InvalidArgument);\n\t}\n}\n","import type { Command } from 'commander';\nimport { resolveContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { promptSecret } from '../../lib/io-helpers';\nimport { enterpriseGet } from '../../lib/enterprise-client';\nimport {\n\tAPI_KEY_ENV_VAR,\n\tKEY_PREFIX,\n\tgetCredentialsPath,\n\tisValidKeyFormat,\n\tmaskKey,\n\tsaveCredentials,\n} from '../../lib/credentials-store';\nimport { printWarn } from '../../lib/output';\n\nexport interface LoginContext {\n\tbaseUrl: string;\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\n/** login 核心(與 commander 解耦供測試):驗格式 → 打 API 驗 key → 存檔。 */\nexport async function performLogin(ctx: LoginContext, key: string): Promise<void> {\n\tif (!isValidKeyFormat(key)) {\n\t\t// 不 echo 輸入原文 —— 可能是手滑貼進來的其他 secret\n\t\tthrow new CliError(\n\t\t\t`That does not look like a valid ${KEY_PREFIX} key. Nothing was saved.`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\t// 任何非 200 都由 enterpriseGet 丟出(401/403 → CliError 附情境提示),不落檔。\n\t// 打 GET /me 兼作 key 驗證與公司名取得(取代舊的 /jobs?pageSize=1 驗證)。\n\tconst { body } = await enterpriseGet({ ...ctx, apiKey: key }, '/me');\n\tsaveCredentials({\n\t\tapi_key: key,\n\t\tcompany_name: extractCompanyName(body),\n\t\tkey_last4: key.slice(-4),\n\t\tsaved_at: new Date().toISOString(),\n\t});\n}\n\n/**\n * 從 GET /me 的 DataResponse({ data: { company: { enc_id, name } } })取公司名。\n * shape 非預期時回空字串 —— login 已驗 key(200),不該因回應格式小變動而失敗;\n * company_name 缺失只讓 whoami 退回顯示 (unknown)。\n */\nfunction extractCompanyName(body: unknown): string {\n\tif (body && typeof body === 'object') {\n\t\tconst data = (body as { data?: unknown }).data;\n\t\tif (data && typeof data === 'object') {\n\t\t\tconst company = (data as { company?: unknown }).company;\n\t\t\tif (company && typeof company === 'object') {\n\t\t\t\tconst name = (company as { name?: unknown }).name;\n\t\t\t\tif (typeof name === 'string') return name;\n\t\t\t}\n\t\t}\n\t}\n\treturn '';\n}\n\nexport function registerEnterpriseLogin(parent: Command): void {\n\tparent\n\t\t.command('login')\n\t\t.description('Validate and save an enterprise API key (prompts securely; pipe stdin in CI)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst key = await promptSecret(`Paste your API key (${KEY_PREFIX}...): `);\n\t\t\tawait performLogin({ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs }, key);\n\t\t\tprocess.stdout.write(`Logged in. Key ${maskKey(key)} saved to ${getCredentialsPath()}\\n`);\n\t\t\tif (process.platform === 'win32') {\n\t\t\t\tprintWarn(\n\t\t\t\t\t`On Windows file permissions are best-effort. For stricter isolation, prefer the ${API_KEY_ENV_VAR} env var.`,\n\t\t\t\t\tctx.color\n\t\t\t\t);\n\t\t\t}\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { deleteCredentials, getCredentialsPath } from '../../lib/credentials-store';\n\nexport function registerEnterpriseLogout(parent: Command): void {\n\tparent\n\t\t.command('logout')\n\t\t.description('Delete the saved enterprise API key')\n\t\t.action(() => {\n\t\t\tconst deleted = deleteCredentials();\n\t\t\tprocess.stdout.write(\n\t\t\t\tdeleted ? `Logged out. Removed ${getCredentialsPath()}\\n` : 'No saved credentials to remove.\\n'\n\t\t\t);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { loadCredentials, maskKey, resolveApiKey } from '../../lib/credentials-store';\n\ninterface WhoamiGlobals {\n\tapiKey?: string;\n}\n\nexport function registerEnterpriseWhoami(parent: Command): void {\n\tparent\n\t\t.command('whoami')\n\t\t.description('Show which enterprise key is in effect (offline; reads local state only)')\n\t\t.action((_flags: unknown, command: Command) => {\n\t\t\tconst globals = command.optsWithGlobals() as WhoamiGlobals;\n\t\t\tconst resolved = resolveApiKey(globals.apiKey);\n\t\t\tconst creds = resolved.source === 'file' ? loadCredentials() : null;\n\t\t\tconst lines = [\n\t\t\t\t`key: ${maskKey(resolved.key)}`,\n\t\t\t\t`source: ${resolved.source}`,\n\t\t\t\t`company: ${creds?.company_name || '(unknown)'}`,\n\t\t\t];\n\t\t\tif (creds?.saved_at) lines.push(`saved: ${creds.saved_at}`);\n\t\t\tprocess.stdout.write(lines.join('\\n') + '\\n');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { asPaginatedBody } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { dim, printJson, printTable } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\n\ninterface ListFlags {\n\tpage?: number;\n\tpageSize?: number;\n\tkeyword?: string;\n\tstatus?: string;\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/**\n * Server 契約(EnterpriseJobsQueryDto.status: number,0=未刊登,1=已刊登)。\n * openapi.yaml 寫的 active|inactive|deleted 是 drift,送字串會 400 —— 一律走這個映射。\n */\nconst STATUS_MAP: Record<string, number> = { published: 1, unpublished: 0 };\n\nconst MINIMAL_LIST_FIELDS = ['enc_id', 'job_title', 'status', 'updated_at'];\n\n/** 欄位對齊 server EnterpriseJobVm(enterprise-job.vm.ts)。 */\ninterface EnterpriseJobItem {\n\tenc_id?: string;\n\tjob_title?: string | null;\n\tcode?: string | null;\n\tstatus?: number;\n\tcreated_at?: string | null;\n\tupdated_at?: string | null;\n\t[k: string]: unknown;\n}\n\nfunction mapStatusFlag(raw: string | undefined): number | undefined {\n\tif (raw === undefined) return undefined;\n\tif (raw in STATUS_MAP) return STATUS_MAP[raw];\n\tthrow new CliError(\n\t\t`Invalid --status \"${raw}\". Allowed: ${Object.keys(STATUS_MAP).join(', ')}`,\n\t\tExitCode.InvalidArgument\n\t);\n}\n\nexport function formatStatus(status: number | undefined): string {\n\tif (status === 1) return 'published';\n\tif (status === 0) return 'unpublished';\n\treturn status === undefined ? '' : String(status);\n}\n\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\nexport function registerEnterpriseJobsList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your company job postings')\n\t\t.option('--page <n>', 'page number (server: currentPage, default 1)', (v) => Number(v))\n\t\t.option('--page-size <n>', 'items per page (server: pageSize, default 10, max 100)', (v) => Number(v))\n\t\t.option('--keyword <kw>', 'filter by job title keyword')\n\t\t.option('--status <state>', 'filter by status: published | unpublished')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.option('--minimal', `output only ${MINIMAL_LIST_FIELDS.join(',')} as JSON`)\n\t\t.action(async (flags: ListFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tif (flags.fields && flags.minimal) {\n\t\t\t\tthrow new CliError('Use either --fields or --minimal, not both', ExitCode.InvalidArgument);\n\t\t\t}\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tconst { body } = await enterpriseGet(\n\t\t\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey: key },\n\t\t\t\t'/jobs',\n\t\t\t\t{\n\t\t\t\t\tcurrentPage: flags.page,\n\t\t\t\t\tpageSize: flags.pageSize,\n\t\t\t\t\tkeyword: flags.keyword,\n\t\t\t\t\tstatus: mapStatusFlag(flags.status),\n\t\t\t\t}\n\t\t\t);\n\t\t\tconst paged = asPaginatedBody<EnterpriseJobItem>(body);\n\n\t\t\tconst projection = flags.minimal ? MINIMAL_LIST_FIELDS : flags.fields ? parseFieldsList(flags.fields) : undefined;\n\t\t\tif (projection || ctx.format === 'json') {\n\t\t\t\tprintJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tprintTable(\n\t\t\t\tpaged.data,\n\t\t\t\t[\n\t\t\t\t\t{ header: 'ENC_ID', value: (r) => (r.enc_id ?? '').slice(0, 14) },\n\t\t\t\t\t{ header: 'TITLE', value: (r) => r.job_title ?? '', maxWidth: 36 },\n\t\t\t\t\t{ header: 'STATUS', value: (r) => formatStatus(r.status) },\n\t\t\t\t\t{ header: 'CODE', value: (r) => r.code ?? '', maxWidth: 16 },\n\t\t\t\t\t{ header: 'UPDATED', value: (r) => formatDate(r.updated_at), maxWidth: 12 },\n\t\t\t\t],\n\t\t\t\tctx.color\n\t\t\t);\n\t\t\tconst head = `Showing page ${paged.currentPage}/${paged.totalPages} (${paged.data.length} of ${paged.totalCount} jobs).`;\n\t\t\tconst hint =\n\t\t\t\tpaged.totalPages > paged.currentPage\n\t\t\t\t\t? ` Next: wport enterprise jobs list --page ${paged.currentPage + 1}`\n\t\t\t\t\t: '';\n\t\t\tprocess.stdout.write(dim(head + hint, ctx.color) + '\\n');\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { mapStatusFlag, formatStatus };\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\nimport { formatStatus } from './list';\n\ninterface ViewFlags {\n\tfields?: string;\n}\n\ninterface EnterpriseJobDetail {\n\tenc_id?: string;\n\tjob_title?: string | null;\n\tcode?: string | null;\n\tstatus?: number;\n\tcreated_at?: string | null;\n\tupdated_at?: string | null;\n\t[k: string]: unknown;\n}\n\nconst DETAIL_FIELDS: ReadonlyArray<string> = ['enc_id', 'job_title', 'code', 'status', 'created_at', 'updated_at'];\n\nfunction renderDetailLines(job: EnterpriseJobDetail): string[] {\n\tconst pad = Math.max(...DETAIL_FIELDS.map((f) => f.length)) + 1;\n\tconst lines: string[] = [];\n\tfor (const field of DETAIL_FIELDS) {\n\t\tconst raw = job[field];\n\t\tif (raw === null || raw === undefined) continue;\n\t\tconst value = field === 'status' ? formatStatus(raw as number) : String(raw);\n\t\tlines.push(`${(field + ':').padEnd(pad + 1)}${sanitizeForTerminal(value)}`);\n\t}\n\treturn lines;\n}\n\nexport function registerEnterpriseJobsView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_id>')\n\t\t.description('View one of your company job postings')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.action(async (encId: string, flags: ViewFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tif (!encId.trim()) {\n\t\t\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t\t\t}\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tconst { body } = await enterpriseGet(\n\t\t\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey: key },\n\t\t\t\t`/jobs/${encodeURIComponent(encId.trim())}`\n\t\t\t);\n\t\t\tconst job = unwrapDataResponse<EnterpriseJobDetail>(body);\n\n\t\t\tif (flags.fields) {\n\t\t\t\tprintJson(pickPaths(job, parseFieldsList(flags.fields)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (ctx.format === 'json') {\n\t\t\t\tprintJson(job);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprocess.stdout.write(renderDetailLines(job).join('\\n') + '\\n');\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { renderDetailLines };\n","import type { Command } from 'commander';\nimport { registerEnterpriseJobsList } from './list';\nimport { registerEnterpriseJobsView } from './view';\n\nexport function registerEnterpriseJobsCommand(parent: Command): void {\n\tconst jobs = parent.command('jobs').description('List and view your company job postings');\n\tregisterEnterpriseJobsList(jobs);\n\tregisterEnterpriseJobsView(jobs);\n}\n","import type { Command } from 'commander';\nimport { registerEnterpriseLogin } from './login';\nimport { registerEnterpriseLogout } from './logout';\nimport { registerEnterpriseWhoami } from './whoami';\nimport { registerEnterpriseJobsCommand } from './jobs';\n\nexport function registerEnterpriseCommand(program: Command): void {\n\tconst enterprise = program\n\t\t.command('enterprise')\n\t\t.description('Manage your company job postings with an enterprise API key')\n\t\t.option('--api-key <key>', 'one-off API key (prefer \"wport enterprise login\" or the WPORT_API_KEY env var)');\n\tregisterEnterpriseLogin(enterprise);\n\tregisterEnterpriseLogout(enterprise);\n\tregisterEnterpriseWhoami(enterprise);\n\tregisterEnterpriseJobsCommand(enterprise);\n}\n"],"mappings":"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","names":["pc","Table","import_node_fs","createClient","envPaths","paths","import_node_fs","import_picocolors","paths","s","pc","program","import_node_fs","program","import_node_fs","program","import_node_fs","import_node_path","import_env_paths","paths","envPaths","formatDate","program"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/errors.ts","../src/lib/output.ts","../src/commands/jobs/search.ts","../src/lib/api-client.ts","../src/lib/config-store.ts","../src/lib/global-opts.ts","../src/lib/path-utils.ts","../src/lib/concurrency.ts","../src/lib/io-helpers.ts","../src/commands/jobs/view.ts","../src/commands/jobs/index.ts","../src/commands/config/set.ts","../src/commands/config/get.ts","../src/commands/config/path.ts","../src/commands/config/reset.ts","../src/commands/config/index.ts","../src/commands/doctor.ts","../src/lib/enterprise-client.ts","../src/lib/credentials-store.ts","../src/commands/enterprise/login.ts","../src/commands/enterprise/logout.ts","../src/commands/enterprise/whoami.ts","../src/commands/enterprise/usage.ts","../src/commands/enterprise/jobs/list.ts","../src/commands/enterprise/jobs/view.ts","../src/commands/enterprise/jobs/create.ts","../src/commands/enterprise/jobs/update.ts","../src/commands/enterprise/jobs/write-shared.ts","../src/commands/enterprise/jobs/lifecycle.ts","../src/commands/enterprise/jobs/batch.ts","../src/commands/enterprise/jobs/index.ts","../src/commands/enterprise/keys/list.ts","../src/commands/enterprise/keys/rotate.ts","../src/commands/enterprise/keys/index.ts","../src/commands/enterprise/company/view.ts","../src/commands/enterprise/company/update.ts","../src/commands/enterprise/company/types.ts","../src/commands/enterprise/company/logo.ts","../src/commands/enterprise/company/index.ts","../src/commands/enterprise/talents/list.ts","../src/commands/enterprise/talents/view.ts","../src/commands/enterprise/talents/respond.ts","../src/commands/enterprise/talents/index.ts","../src/commands/enterprise/campaigns/create.ts","../src/commands/enterprise/campaigns/list.ts","../src/commands/enterprise/campaigns/lifecycle.ts","../src/commands/enterprise/campaigns/update.ts","../src/commands/enterprise/campaigns/view.ts","../src/commands/enterprise/campaigns/index.ts","../src/commands/enterprise/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { isCliError, ExitCode } from './lib/errors';\nimport { isColorEnabled, printError } from './lib/output';\nimport { registerJobsCommand } from './commands/jobs';\nimport { registerConfigCommand } from './commands/config';\nimport { registerDoctorCommand } from './commands/doctor';\nimport { registerEnterpriseCommand } from './commands/enterprise';\n\nconst program = new Command();\n\nprogram\n\t.name('wport')\n\t.description('wport CLI — terminal interface to the W101 Talent Search Hub public API')\n\t.version(__CLI_VERSION__, '-v, --version', 'output the CLI version')\n\t.option('--lang <locale>', 'Accept-Language locale: zh-TW | en-US | vi-VN | th-TH | id-ID')\n\t.option('--api <url>', 'override API base URL')\n\t.option('--output <fmt>', 'output format: table | json')\n\t.option('--no-color', 'disable color output')\n\t.option('--timeout <ms>', 'HTTP timeout in milliseconds', (v) => Number(v));\n\nregisterJobsCommand(program);\nregisterConfigCommand(program);\nregisterDoctorCommand(program);\nregisterEnterpriseCommand(program);\n\nprogram.exitOverride();\n\nprogram\n\t.parseAsync(process.argv)\n\t.then(() => process.exit(ExitCode.Success))\n\t.catch((err: unknown) => handleTopLevelError(err));\n\nfunction handleTopLevelError(err: unknown): never {\n\tconst color = isColorEnabled(false);\n\n\t// commander throws CommanderError on its own validation paths (help, version, unknown option).\n\tif (err && typeof err === 'object' && 'code' in err) {\n\t\tconst commanderErr = err as { code?: string; exitCode?: number; message?: string };\n\t\tif (commanderErr.code === 'commander.helpDisplayed' || commanderErr.code === 'commander.version') {\n\t\t\tprocess.exit(ExitCode.Success);\n\t\t}\n\t\t// commander 自己的 exitCode 多半是 1,不在 CLI 的 contract(0/2/3/4/5)內。\n\t\t// 任何 parsing / usage 失敗都當 InvalidArgument(2)。\n\t\tif (commanderErr.message) printError(commanderErr.message, color);\n\t\tprocess.exit(ExitCode.InvalidArgument);\n\t}\n\n\tif (isCliError(err)) {\n\t\tprintError(err.message, color);\n\t\tprocess.exit(err.exitCode);\n\t}\n\n\tconst fallbackMessage = err instanceof Error ? err.message : String(err);\n\tprintError(fallbackMessage, color);\n\tprocess.exit(ExitCode.ServerOrNetworkError);\n}\n","export const ExitCode = {\n\tSuccess: 0,\n\tInvalidArgument: 2,\n\tServerClientError: 3,\n\tServerOrNetworkError: 4,\n\tConfigCorrupt: 5,\n} as const;\n\nexport type ExitCodeValue = (typeof ExitCode)[keyof typeof ExitCode];\n\nexport class CliError extends Error {\n\treadonly exitCode: ExitCodeValue;\n\n\tconstructor(message: string, exitCode: ExitCodeValue) {\n\t\tsuper(message);\n\t\tthis.name = 'CliError';\n\t\tthis.exitCode = exitCode;\n\t}\n}\n\nexport class InvalidArgumentError extends CliError {\n\tconstructor(message: string) {\n\t\tsuper(message, ExitCode.InvalidArgument);\n\t\tthis.name = 'InvalidArgumentError';\n\t}\n}\n\nexport class ServerClientHttpError extends CliError {\n\treadonly status: number;\n\treadonly body: unknown;\n\tconstructor(message: string, status: number, body: unknown) {\n\t\tsuper(message, ExitCode.ServerClientError);\n\t\tthis.name = 'ServerClientHttpError';\n\t\tthis.status = status;\n\t\tthis.body = body;\n\t}\n}\n\nexport class NetworkError extends CliError {\n\treadonly cause?: unknown;\n\tconstructor(message: string, cause?: unknown) {\n\t\tsuper(message, ExitCode.ServerOrNetworkError);\n\t\tthis.name = 'NetworkError';\n\t\tthis.cause = cause;\n\t}\n}\n\nexport class ConfigCorruptError extends CliError {\n\treadonly path?: string;\n\tconstructor(message: string, path?: string) {\n\t\tsuper(message, ExitCode.ConfigCorrupt);\n\t\tthis.name = 'ConfigCorruptError';\n\t\tthis.path = path;\n\t}\n}\n\nexport function isCliError(err: unknown): err is CliError {\n\treturn err instanceof CliError;\n}\n","import Table from 'cli-table3';\nimport pc from 'picocolors';\nimport { CliError, ExitCode } from './errors';\n\nexport type OutputFormat = 'table' | 'json';\n\n/**\n * 把 untrusted 字串(從 API 回來的 employer-controlled 內容)變成終端機可安全列印的形式。\n *\n * 防的是 terminal escape injection:\n * - CSI / OSC / DCS 等 ESC 開頭序列(清螢幕、改 title、移動游標、假超連結 phishing 等)\n * - 其他 C0 / C1 控制字元(保留 \\t \\n \\r 三個合法格式化字元)\n *\n * JSON 模式不需要 sanitize:JSON.stringify 會把 < 0x20 的字元 escape 成 \\uXXXX。\n * 只有 table / plain-text 印到 stdout/stderr 的字串走這個 helper。\n *\n * 用 new RegExp(string) 建構,所有控制字元以 \\\\uNNNN 形式撰寫,避免 source 內含 literal 控制字元。\n */\nconst ANSI_ESCAPE_SEQUENCE = new RegExp(\n\t[\n\t\t// CSI: ESC [ params intermediates final\n\t\t'\\\\u001B\\\\[[0-?]*[ -/]*[@-~]',\n\t\t// OSC: ESC ] payload (any chars except BEL/ESC) terminated by BEL or ESC \\\n\t\t'\\\\u001B\\\\][^\\\\u0007\\\\u001B]*(?:\\\\u0007|\\\\u001B\\\\\\\\)',\n\t\t// Two-char escapes: ESC + Fe final byte (0x40-0x5F = @ A B ... Z [ \\ ] ^ _).\n\t\t// 涵蓋 CSI([) / OSC(]) / DCS(P) / SOS(X) / ST(\\\\) / PM(^) / APC(_) intro。\n\t\t// CSI / OSC regex 在前面 OR-分支會先匹配對應序列;這條兜底所有未覆蓋 Fe。\n\t\t'\\\\u001B[@-_]',\n\t].join('|'),\n\t'g'\n);\n\n// 單行用:剝所有 C0(含 \\t \\n \\r)、DEL、C1。table cell、label-prefixed 標題、\n// error / warning 訊息都走這條 —— 即便 ANSI 已剝乾淨,殘留 \\r 仍能把 cursor 拉回\n// 行首蓋掉前面的內容;\\n 會打斷表格排版、可能偽造後續 row;\\t 寬度可變、\n// 搞壞 cli-table3 對齊。\nconst CONTROL_CHARS_STRICT = new RegExp('[\\\\u0000-\\\\u001F\\\\u007F\\\\u0080-\\\\u009F]', 'g');\n\n// 多行用:內部先把 \\r\\n / lone \\r 正規化成 \\n,再剝其他 C0(含 \\t)、DEL、C1。\n// 保留 \\n 作為合法段落分隔。Caller 不需事先做正規化。\nconst CONTROL_CHARS_MULTILINE = new RegExp('[\\\\u0000-\\\\u0009\\\\u000B-\\\\u001F\\\\u007F\\\\u0080-\\\\u009F]', 'g');\n\nexport function sanitizeForTerminal(s: string): string {\n\treturn s.replace(ANSI_ESCAPE_SEQUENCE, '').replace(CONTROL_CHARS_STRICT, '');\n}\n\nexport function sanitizeForTerminalMultiline(s: string): string {\n\treturn s.replace(ANSI_ESCAPE_SEQUENCE, '').replace(/\\r\\n?/g, '\\n').replace(CONTROL_CHARS_MULTILINE, '');\n}\n\nexport function resolveOutputFormat(explicit: string | undefined): OutputFormat {\n\tif (explicit === undefined) {\n\t\treturn process.stdout.isTTY ? 'table' : 'json';\n\t}\n\tif (explicit !== 'json' && explicit !== 'table') {\n\t\tthrow new CliError(`Invalid --output \"${explicit}\". Allowed: table, json`, ExitCode.InvalidArgument);\n\t}\n\treturn explicit;\n}\n\nexport function isColorEnabled(noColor: boolean | undefined): boolean {\n\tif (noColor === true) return false;\n\tif (process.env.NO_COLOR) return false;\n\treturn process.stdout.isTTY ?? false;\n}\n\nexport function printJson(value: unknown): void {\n\tprocess.stdout.write(JSON.stringify(value, null, 2) + '\\n');\n}\n\n/**\n * Emit one newline-delimited JSON record (ND-JSON). JSON.stringify escapes control\n * chars to \\uXXXX, so employer-controlled string values are terminal-safe without\n * extra sanitization. Used by `jobs view --batch`, where one record per line keeps a\n * single failure isolated to its own line.\n */\nexport function printNdjsonLine(value: unknown): void {\n\tprocess.stdout.write(JSON.stringify(value) + '\\n');\n}\n\nexport interface TableColumn<T> {\n\theader: string;\n\tvalue: (row: T) => string;\n\tmaxWidth?: number;\n}\n\nexport function printTable<T>(rows: T[], columns: TableColumn<T>[], color: boolean): void {\n\tif (rows.length === 0) {\n\t\tprocess.stdout.write(color ? pc.dim('(no results)\\n') : '(no results)\\n');\n\t\treturn;\n\t}\n\tconst table = new Table({\n\t\thead: columns.map((c) => (color ? pc.bold(c.header) : c.header)),\n\t\tstyle: { head: [], border: [] },\n\t\tcolWidths: columns.map((c) => c.maxWidth ?? null),\n\t\twordWrap: true,\n\t});\n\tfor (const row of rows) {\n\t\t// 每個 cell 的字串都過 sanitize:防 employer-controlled 內容(title/company/area/salary 等)注入 escape。\n\t\ttable.push(columns.map((c) => sanitizeForTerminal(c.value(row))));\n\t}\n\tprocess.stdout.write(table.toString() + '\\n');\n}\n\nexport function printError(message: string, color: boolean): void {\n\tconst prefix = color ? pc.red('Error:') : 'Error:';\n\t// Server 回的 error message 也視為 untrusted,sanitize。\n\tprocess.stderr.write(`${prefix} ${sanitizeForTerminal(message)}\\n`);\n}\n\nexport function printWarn(message: string, color: boolean): void {\n\tconst prefix = color ? pc.yellow('Warning:') : 'Warning:';\n\tprocess.stderr.write(`${prefix} ${sanitizeForTerminal(message)}\\n`);\n}\n\nexport function dim(text: string, color: boolean): string {\n\treturn color ? pc.dim(text) : text;\n}\n","import type { Command } from 'commander';\nimport { readFileSync } from 'node:fs';\nimport { asPaginatedBody, createApiClient, throwForHttpStatus } from '../../lib/api-client';\nimport { resolveContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { dim, printJson, printTable, printWarn } from '../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../lib/path-utils';\nimport type { operations } from '../../generated/schema';\n\ninterface SearchFlags {\n\tkeyword?: string;\n\tlocation?: string[];\n\tcategory?: string[];\n\tpage?: number;\n\tpageSize?: number;\n\tjsonQuery?: string;\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/**\n * Compact field set for `--minimal` — the columns an agent almost always wants from a\n * search result, mirroring the table view minus the noise. Keeps `enc_id` first so the\n * output is directly pipeable into `jobs view -`.\n */\nconst MINIMAL_SEARCH_FIELDS = ['enc_id', 'title', 'company_name', 'area_display', 'salary_display'];\n\n/**\n * Query type derived from the generated OpenAPI schema for GET /api/jobs/search.\n * Adding a new field on the backend DTO + re-running gen:openapi will make this type\n * widen automatically; any flag we forget to map will surface as a TS error inside\n * buildQuery() instead of being silently dropped at runtime.\n */\ntype SearchQuery = NonNullable<operations['JobsController_searchJobs']['parameters']['query']>;\n\ninterface JobSearchItem {\n\tenc_id?: string;\n\tenc_company_id?: string;\n\tcompany_name?: string;\n\tcompany_logo_url?: string;\n\ttitle?: string;\n\tarea_display?: string;\n\tsalary_display?: string;\n\tsalary_currency_code?: string | null;\n\ttags?: string[];\n\tupdated_at?: string;\n\t[k: string]: unknown;\n}\n\nexport function registerJobsSearch(parent: Command): void {\n\tparent\n\t\t.command('search')\n\t\t.description(\n\t\t\t'Search public job listings. Sort is server-controlled (publish date, or relevance when --keyword is set); ' +\n\t\t\t\t'the orderBy / order query params are silently ignored, so this CLI intentionally exposes no sort flags. ' +\n\t\t\t\t'Run `wport doctor` for the full list of server quirks.'\n\t\t)\n\t\t.option('-k, --keyword <text>', 'keyword search (title / company name etc.)')\n\t\t.option('-l, --location <code...>', 'area code (repeatable, e.g. 6001001000)')\n\t\t.option('-c, --category <code...>', 'job classification code (repeatable)')\n\t\t.option('-p, --page <n>', 'page number (default 1)', (v) => Number(v))\n\t\t.option('-s, --page-size <n>', 'page size (default 10, max 100)', (v) => Number(v))\n\t\t.option('--json-query <file>', 'read full query body from JSON file (overrides other flags)')\n\t\t.option(\n\t\t\t'--fields <list>',\n\t\t\t'keep only these fields in each JSON result (comma-separated dotted paths, e.g. enc_id,title). JSON output only.'\n\t\t)\n\t\t.option('--minimal', `shorthand for --fields ${MINIMAL_SEARCH_FIELDS.join(',')}. JSON output only.`)\n\t\t.action(async (flags: SearchFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst fields = resolveSearchFields(flags);\n\t\t\tconst query = buildQuery(flags);\n\n\t\t\tconst client = createApiClient({\n\t\t\t\tbaseUrl: ctx.baseUrl,\n\t\t\t\tlocale: ctx.locale,\n\t\t\t\ttimeoutMs: ctx.timeoutMs,\n\t\t\t});\n\n\t\t\t// openapi-fetch consumes the response body itself; use `data` (success) or `error` (non-2xx).\n\t\t\tconst { data, error, response } = await client.GET('/api/jobs/search', {\n\t\t\t\tparams: { query },\n\t\t\t});\n\t\t\tif (!response.ok) throwForHttpStatus(response.status, error);\n\n\t\t\tconst paged = asPaginatedBody<JobSearchItem>(data);\n\n\t\t\tif (ctx.format === 'json') {\n\t\t\t\t// Field projection is client-side: it trims tokens the model has to read, not\n\t\t\t\t// bytes-on-the-wire (the server has no projection param). Pagination metadata is\n\t\t\t\t// preserved by spreading `paged` and only replacing `data`.\n\t\t\t\tconst body = fields ? { ...paged, data: paged.data.map((item) => pickPaths(item, fields)) } : paged;\n\t\t\t\tprintJson(body);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Table columns are fixed; --fields / --minimal are a JSON-only affordance. Warn\n\t\t\t// rather than silently ignore so the flag doesn't look broken.\n\t\t\tif (fields) {\n\t\t\t\tprintWarn('--fields / --minimal only affect JSON output; ignored for table. Use --output json.', ctx.color);\n\t\t\t}\n\n\t\t\tprintTable(\n\t\t\t\tpaged.data,\n\t\t\t\t[\n\t\t\t\t\t{ header: 'ENC_ID', value: (r) => truncate(r.enc_id ?? '', 14) },\n\t\t\t\t\t{ header: 'TITLE', value: (r) => r.title ?? '', maxWidth: 36 },\n\t\t\t\t\t{ header: 'COMPANY', value: (r) => r.company_name ?? '', maxWidth: 20 },\n\t\t\t\t\t{ header: 'LOCATION', value: (r) => r.area_display ?? '', maxWidth: 18 },\n\t\t\t\t\t{ header: 'SALARY', value: (r) => r.salary_display ?? '', maxWidth: 18 },\n\t\t\t\t\t{ header: 'UPDATED', value: (r) => formatDate(r.updated_at), maxWidth: 12 },\n\t\t\t\t],\n\t\t\t\tctx.color\n\t\t\t);\n\n\t\t\tconst head = `Showing page ${paged.currentPage}/${paged.totalPages} (${paged.data.length} of ${paged.totalCount} results).`;\n\t\t\tconst hint =\n\t\t\t\tpaged.totalPages > paged.currentPage ? ` Next: wport jobs search --page ${paged.currentPage + 1}` : '';\n\t\t\tprocess.stdout.write(dim(head + hint, ctx.color) + '\\n');\n\t\t});\n}\n\nfunction resolveSearchFields(flags: SearchFlags): string[] | undefined {\n\tif (flags.fields && flags.minimal) {\n\t\tthrow new CliError('Use either --fields or --minimal, not both', ExitCode.InvalidArgument);\n\t}\n\tif (flags.minimal) return [...MINIMAL_SEARCH_FIELDS];\n\tif (flags.fields) return parseFieldsList(flags.fields);\n\treturn undefined;\n}\n\nfunction buildQuery(flags: SearchFlags): SearchQuery {\n\tif (flags.jsonQuery) {\n\t\t// User-supplied JSON escape hatch — we trust the caller. Runtime validation will come\n\t\t// from the server side; this is the one spot the typed query is deliberately relaxed.\n\t\treturn readJsonQuery(flags.jsonQuery) as SearchQuery;\n\t}\n\tconst q: SearchQuery = {};\n\tif (flags.keyword) q.keyword = flags.keyword;\n\tif (flags.location?.length) q.area_codes = flags.location;\n\tif (flags.category?.length) q.job_classification_codes = flags.category;\n\tif (flags.page !== undefined) q.currentPage = flags.page;\n\tif (flags.pageSize !== undefined) q.pageSize = flags.pageSize;\n\treturn q;\n}\n\nfunction readJsonQuery(path: string): Record<string, unknown> {\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(path, 'utf8');\n\t} catch (err) {\n\t\tthrow new CliError(`Cannot read --json-query file ${path}: ${(err as Error).message}`, ExitCode.InvalidArgument);\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(raw);\n\t\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\t\tthrow new Error(\n\t\t\t\tArray.isArray(parsed)\n\t\t\t\t\t? 'JSON root is an array; expected an object with query fields'\n\t\t\t\t\t: 'JSON root must be an object'\n\t\t\t);\n\t\t}\n\t\treturn parsed as Record<string, unknown>;\n\t} catch (err) {\n\t\tthrow new CliError(`Invalid JSON in ${path}: ${(err as Error).message}`, ExitCode.InvalidArgument);\n\t}\n}\n\nfunction truncate(s: string, max: number): string {\n\t// Fast path: UTF-16 code units (string.length) are always >= codepoint count,\n\t// so if byte-length already fits we don't need codepoint counting.\n\tif (s.length <= max) return s;\n\t// Array.from iterates by code-point, so surrogate pairs (emoji, supplementary\n\t// plane chars) aren't split mid-character. Doesn't handle grapheme clusters\n\t// (combining marks), but covers the common bilingual / emoji case.\n\tconst chars = Array.from(s);\n\tif (chars.length <= max) return s;\n\treturn chars.slice(0, max - 1).join('') + '…';\n}\n\nfunction formatDate(s: string | undefined): string {\n\tif (!s) return '';\n\tconst m = /^(\\d{4}-\\d{2}-\\d{2})/.exec(s);\n\treturn m ? m[1] : s;\n}\n","import createClient, { type Client } from 'openapi-fetch';\nimport type { paths } from '../generated/schema';\nimport { CliError, ExitCode, NetworkError, ServerClientHttpError } from './errors';\n\nexport interface ApiClientOptions {\n\tbaseUrl: string;\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\nexport type ApiClient = Client<paths>;\n\n// Connection reuse for batch / repeated requests is handled by Node's built-in fetch\n// (undici): its global dispatcher pools keep-alive connections per origin, so multiple\n// requests from one process to the same host already reuse TCP/TLS without any setup\n// here. We deliberately don't ship a custom `undici` Agent — it'd add a runtime\n// dependency to nudge keepAliveTimeout (~4s default → longer) for near-zero gain on the\n// short-lived bursts `jobs view --batch` produces.\nexport function createApiClient(opts: ApiClientOptions): ApiClient {\n\treturn createClient<paths>({\n\t\tbaseUrl: opts.baseUrl,\n\t\theaders: {\n\t\t\t'Accept-Language': opts.locale,\n\t\t\t'User-Agent': buildUserAgent(),\n\t\t\tAccept: 'application/json',\n\t\t},\n\t\tfetch: (request: Request) => fetchWithTimeout(request, opts.timeoutMs),\n\t});\n}\n\nexport function fetchWithTimeout(request: Request, timeoutMs: number): Promise<Response> {\n\t// AbortSignal.timeout is available since Node 17.3 (engines requires >=18.17).\n\t// New Request inherits method/headers/body but overrides signal.\n\tconst timedRequest = new Request(request, { signal: AbortSignal.timeout(timeoutMs) });\n\treturn fetch(timedRequest).catch((err: unknown) => {\n\t\tif (isTimeoutAbort(err)) {\n\t\t\tthrow new NetworkError(`Request timed out after ${timeoutMs}ms`, err);\n\t\t}\n\t\t// TypeError: fetch failed → ECONNREFUSED / ENOTFOUND / TLS / proxy\n\t\tconst code = (err as { cause?: { code?: string } })?.cause?.code;\n\t\tconst detail = code ? code : ((err as Error)?.message ?? String(err));\n\t\tthrow new NetworkError(`Cannot reach upstream: ${detail}`, err);\n\t});\n}\n\nfunction isTimeoutAbort(err: unknown): boolean {\n\tif (err && typeof err === 'object' && 'name' in err) {\n\t\tconst name = (err as { name?: string }).name;\n\t\treturn name === 'TimeoutError' || name === 'AbortError';\n\t}\n\treturn false;\n}\n\nexport function buildUserAgent(): string {\n\treturn `wport-cli/${__CLI_VERSION__} (node ${process.version}; ${process.platform})`;\n}\n\n/**\n * W101 後端 `DataResponse<T>` wrapper:`{ success, statusCode, message, data: T }`。\n * 用於回傳「單筆」payload 的端點(例如 GET /api/jobs/:encId/view)。\n *\n * Shape 不符直接 throw CliError,不再 silent cast 把 wrapper 當 payload 回。\n *\n * 注意:`PaginatedResponse` 是另一種扁平結構\n * `{ success, statusCode, message, data: T[], currentPage, totalPages, pageSize, totalCount }`,\n * 不要對 paginated 回應用此函式 —— 會丟失 pagination 欄位。\n */\nexport function unwrapDataResponse<T>(body: unknown): T {\n\tif (body && typeof body === 'object' && 'success' in body && 'data' in body) {\n\t\treturn (body as { data: T }).data;\n\t}\n\tthrow new CliError('Unexpected response shape: missing { success, data } wrapper', ExitCode.ServerOrNetworkError);\n}\n\n/**\n * W101 後端 `DataResponse<T[]>`(非分頁、data 為陣列):`{ success, statusCode, message, data: T[] }`。\n * 用於回傳「陣列」但不帶分頁 metadata 的端點(例如 GET /keys)。\n *\n * 先剝 wrapper(複用 unwrapDataResponse)再驗 data 為陣列。契約漂移時丟清晰 CliError,\n * 對齊 asPaginatedBody 的守衛行為,避免 caller 的 .filter/.map 丟原生 TypeError。\n */\nexport function unwrapDataArray<T>(body: unknown): T[] {\n\tconst data = unwrapDataResponse<unknown>(body);\n\tif (!Array.isArray(data)) {\n\t\tthrow new CliError('Unexpected response shape: `data` is not an array', ExitCode.ServerOrNetworkError);\n\t}\n\treturn data as T[];\n}\n\n/**\n * W101 後端 `PaginatedResponse<T>` wrapper(扁平):\n * `{ success, statusCode, message, data: T[], currentPage, totalPages, pageSize, totalCount }`。\n * 直接 cast,不剝外層;callers 從 `.data` 拿 list,從頂層欄位拿分頁 metadata。\n */\nexport interface PaginatedBody<T> {\n\tsuccess?: boolean;\n\tstatusCode?: number;\n\tmessage?: unknown;\n\tdata: T[];\n\tcurrentPage: number;\n\ttotalPages: number;\n\tpageSize: number;\n\ttotalCount: number;\n}\n\nexport function asPaginatedBody<T>(body: unknown): PaginatedBody<T> {\n\tif (!body || typeof body !== 'object') {\n\t\tthrow new CliError('Unexpected response shape: not an object', ExitCode.ServerOrNetworkError);\n\t}\n\tconst b = body as Record<string, unknown>;\n\tif (!Array.isArray(b.data)) {\n\t\tthrow new CliError('Unexpected response shape: missing `data` array', ExitCode.ServerOrNetworkError);\n\t}\n\tfor (const key of ['currentPage', 'totalPages', 'pageSize', 'totalCount'] as const) {\n\t\tif (typeof b[key] !== 'number') {\n\t\t\tthrow new CliError(`Unexpected response shape: missing or non-numeric \"${key}\"`, ExitCode.ServerOrNetworkError);\n\t\t}\n\t}\n\treturn body as PaginatedBody<T>;\n}\n\nexport function throwForHttpStatus(status: number, body: unknown): never {\n\tconst message = extractErrorMessage(body) ?? `HTTP ${status}`;\n\tif (status >= 400 && status < 500) {\n\t\tthrow new ServerClientHttpError(message, status, body);\n\t}\n\tthrow new CliError(message, ExitCode.ServerOrNetworkError);\n}\n\nexport function extractErrorMessage(body: unknown): string | null {\n\tif (typeof body === 'string') return body;\n\tif (body && typeof body === 'object') {\n\t\tconst obj = body as Record<string, unknown>;\n\t\t// NestJS class-validator default: message: string[]\n\t\tif (Array.isArray(obj.message)) {\n\t\t\tconst parts = obj.message.filter((m): m is string => typeof m === 'string');\n\t\t\tif (parts.length > 0) return parts.join('; ');\n\t\t}\n\t\tif (typeof obj.message === 'string') return obj.message;\n\t\t// i18n object 或 nested → 印 JSON tail\n\t\tif (obj.message && typeof obj.message === 'object') {\n\t\t\ttry {\n\t\t\t\treturn JSON.stringify(obj.message);\n\t\t\t} catch {\n\t\t\t\t/* ignore */\n\t\t\t}\n\t\t}\n\t\t// Enterprise API error body 帶 i18n path(如 error.enterprise.invalid_api_key),\n\t\t// message 缺席時讓 path 當最後可讀資訊(guard 直接丟 { path } 的情境)。\n\t\tif (typeof obj.path === 'string') return obj.path;\n\t\tif (typeof obj.error === 'string') return obj.error;\n\t}\n\treturn null;\n}\n","import {\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\tchmodSync,\n\topenSync,\n\twriteSync,\n\tcloseSync,\n\trenameSync,\n\tunlinkSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport envPaths from 'env-paths';\nimport { CliError, ExitCode } from './errors';\nimport { printWarn } from './output';\n\nexport const ALLOWED_LOCALES = ['zh-TW', 'en-US', 'vi-VN', 'th-TH', 'id-ID'] as const;\nexport type Locale = (typeof ALLOWED_LOCALES)[number];\n\nexport const ALLOWED_OUTPUT = ['table', 'json'] as const;\nexport type OutputPref = (typeof ALLOWED_OUTPUT)[number];\n\nexport interface CliConfig {\n\tlocale?: Locale;\n\toutput?: OutputPref;\n\ttimeout_ms?: number;\n}\n\nconst CONFIG_KEYS = ['locale', 'output', 'timeout_ms'] as const satisfies readonly (keyof CliConfig)[];\nexport type ConfigKey = (typeof CONFIG_KEYS)[number];\n\n/**\n * Keys recognised in older configs but no longer settable. Detected in parseConfig\n * to surface a one-time deprecation warning instead of silently dropping (which the\n * forward-compat path would do for genuinely unknown keys).\n *\n * `api_base_url` was removed in 0.1.2 — SSRF / credential exfil surface for a flag\n * external users don't actually need. Override via `WPORT_API_BASE` env var or `--api`.\n */\nconst DEPRECATED_CONFIG_KEYS = ['api_base_url'] as const;\ntype DeprecatedConfigKey = (typeof DEPRECATED_CONFIG_KEYS)[number];\n\n/** Per-key migration hint shown when a deprecated key is found in an existing config. */\nconst DEPRECATED_KEY_HINTS: Record<DeprecatedConfigKey, string> = {\n\tapi_base_url: 'Set the WPORT_API_BASE env var (or use --api) instead.',\n};\n\n// One-time latch so repeated loadConfig() calls (e.g. inside a batch run) emit the\n// deprecation notice at most once per process rather than spamming stderr.\nlet deprecationWarned = false;\n\n/** Per-key value type. validateAndCoerce<K> returns ConfigValueMap[K]. */\ntype ConfigValueMap = {\n\tlocale: Locale;\n\toutput: OutputPref;\n\ttimeout_ms: number;\n};\n\nexport function isConfigKey(key: string): key is ConfigKey {\n\treturn (CONFIG_KEYS as readonly string[]).includes(key);\n}\n\nexport function isDeprecatedConfigKey(key: string): key is DeprecatedConfigKey {\n\treturn (DEPRECATED_CONFIG_KEYS as readonly string[]).includes(key);\n}\n\nconst paths = envPaths('wport', { suffix: '' });\n\nexport function getConfigPath(): string {\n\treturn join(paths.config, 'config.json');\n}\n\n/**\n * Trust boundary:把外部 JSON object 收成型別正確的 CliConfig。\n * 每個 key 都走 validateAndCoerce,不認識的 key 直接 drop(forward compatible,\n * 未來新版多塞了 key、舊 CLI 不會炸)。\n */\nexport function parseConfig(raw: unknown): CliConfig {\n\tif (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n\t\tthrow new CliError('Config must be a JSON object', ExitCode.ConfigCorrupt);\n\t}\n\tconst input = raw as Record<string, unknown>;\n\n\t// Surface (once per process) any deprecated key still sitting in the user's config.\n\t// We warn + drop rather than error, so upgrading from <=0.1.1 never hard-fails; the\n\t// key's actual replacement (WPORT_API_BASE) is resolved elsewhere in global-opts.\n\tif (!deprecationWarned) {\n\t\tfor (const dep of DEPRECATED_CONFIG_KEYS) {\n\t\t\tif (dep in input) {\n\t\t\t\tdeprecationWarned = true;\n\t\t\t\tprintWarn(`Config key \"${dep}\" was removed in 0.1.2 and is ignored. ${DEPRECATED_KEY_HINTS[dep]}`, false);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst out: CliConfig = {};\n\tfor (const key of CONFIG_KEYS) {\n\t\tif (!(key in input)) continue;\n\t\tconst value = input[key];\n\t\ttry {\n\t\t\tconst coerced = validateAndCoerce(key, String(value));\n\t\t\t// Object.assign 形式避免 TS 5.5 在 `out[key] = coerced` 上推不出 ConfigValueMap[K]→CliConfig[K]\n\t\t\t// 的對應關係。validateAndCoerce 已是 generic、型別正確;此處只是 indexed assignment 的繞道。\n\t\t\tObject.assign(out, { [key]: coerced });\n\t\t} catch (err) {\n\t\t\tif (err instanceof CliError) {\n\t\t\t\tthrow new CliError(`Config key \"${key}\" invalid: ${err.message}`, ExitCode.ConfigCorrupt);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\treturn out;\n}\n\nexport function loadConfig(): CliConfig {\n\tconst path = getConfigPath();\n\tif (!existsSync(path)) return {};\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(path, 'utf8');\n\t} catch (err) {\n\t\tthrow new CliError(`Failed to read config at ${path}: ${(err as Error).message}`, ExitCode.ConfigCorrupt);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch (err) {\n\t\tthrow new CliError(`Failed to parse JSON at ${path}: ${(err as Error).message}`, ExitCode.ConfigCorrupt);\n\t}\n\treturn parseConfig(parsed);\n}\n\nexport function saveConfig(config: CliConfig): void {\n\tconst path = getConfigPath();\n\tmkdirSync(dirname(path), { recursive: true });\n\n\t// Atomic: write tmpfile (mode 0o600 from open()) → rename to final path (POSIX atomic).\n\t// 即使中途 crash,舊檔仍完整、不會留 truncated JSON 觸發 ConfigCorrupt 鎖死 CLI。\n\tconst tmpPath = `${path}.tmp.${process.pid}.${Date.now()}`;\n\tconst fd = openSync(tmpPath, 'w', 0o600);\n\ttry {\n\t\twriteSync(fd, JSON.stringify(config, null, 2) + '\\n');\n\t} catch (err) {\n\t\tcloseSync(fd);\n\t\ttry {\n\t\t\tunlinkSync(tmpPath);\n\t\t} catch {\n\t\t\t/* best effort cleanup */\n\t\t}\n\t\tthrow err;\n\t}\n\tcloseSync(fd);\n\n\t// POSIX: openSync's mode is masked by umask (umask can only narrow, never widen).\n\t// chmod restores 0o600 in case a permissive umask stripped owner bits; it cannot\n\t// expose the file to group/other. On Windows chmodSync is a no-op, skip entirely.\n\tif (process.platform !== 'win32') {\n\t\ttry {\n\t\t\tchmodSync(tmpPath, 0o600);\n\t\t} catch (err) {\n\t\t\tprintWarn(\n\t\t\t\t`Failed to chmod 0600 on config tmpfile: ${(err as Error).message}. ` +\n\t\t\t\t\t'Other users on this system may be able to read CLI config.',\n\t\t\t\tfalse\n\t\t\t);\n\t\t}\n\t}\n\n\trenameSync(tmpPath, path);\n}\n\nexport function validateAndCoerce<K extends ConfigKey>(key: K, value: string): ConfigValueMap[K] {\n\tswitch (key) {\n\t\tcase 'locale': {\n\t\t\tif (!(ALLOWED_LOCALES as readonly string[]).includes(value)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Invalid locale \"${value}\". Allowed: ${ALLOWED_LOCALES.join(', ')}`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn value as ConfigValueMap[K];\n\t\t}\n\t\tcase 'output': {\n\t\t\tif (!(ALLOWED_OUTPUT as readonly string[]).includes(value)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Invalid output \"${value}\". Allowed: ${ALLOWED_OUTPUT.join(', ')}`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn value as ConfigValueMap[K];\n\t\t}\n\t\tcase 'timeout_ms': {\n\t\t\tconst n = Number(value);\n\t\t\tif (!Number.isInteger(n) || n < 100 || n > 600_000) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`timeout_ms must be an integer between 100 and 600000 (got ${value})`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn n as ConfigValueMap[K];\n\t\t}\n\t}\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = {\n\tresetDeprecationWarning(): void {\n\t\tdeprecationWarned = false;\n\t},\n};\n","import type { Command } from 'commander';\nimport { ALLOWED_LOCALES, loadConfig, type Locale, type CliConfig } from './config-store';\nimport { CliError, ExitCode } from './errors';\nimport { resolveOutputFormat, isColorEnabled, type OutputFormat } from './output';\n\n// Production public API. Local development overrides via the WPORT_API_BASE env var\n// or the `--api` flag. (The `api_base_url` config key was removed in 0.1.2 — keeping a\n// persisted, mutable base URL on disk is an SSRF / credential-exfil surface that\n// external users don't need.)\nconst DEFAULT_BASE_URL = 'https://api.wport.me';\nexport const API_BASE_ENV_VAR = 'WPORT_API_BASE';\nconst DEFAULT_LOCALE: Locale = 'zh-TW';\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport interface ResolvedContext {\n\tbaseUrl: string;\n\tlocale: Locale;\n\ttimeoutMs: number;\n\tformat: OutputFormat;\n\tcolor: boolean;\n\tconfig: CliConfig;\n}\n\ninterface RawGlobals {\n\tlang?: string;\n\tapi?: string;\n\toutput?: string;\n\tcolor?: boolean;\n\ttimeout?: number;\n}\n\nexport function resolveContext(command: Command): ResolvedContext {\n\tconst globals = command.optsWithGlobals() as RawGlobals;\n\tconst config = loadConfig();\n\n\treturn {\n\t\tbaseUrl: resolveBaseUrl(globals.api),\n\t\tlocale: resolveLocale(globals.lang, config),\n\t\ttimeoutMs: resolveTimeout(globals.timeout, config),\n\t\tformat: resolveOutputFormat(globals.output),\n\t\tcolor: isColorEnabled(globals.color === false),\n\t\tconfig,\n\t};\n}\n\n/**\n * Resolve the API base URL. Precedence: `--api` flag > WPORT_API_BASE env var > default.\n * The env var is just as untrusted as the (removed) config key, so it gets the same\n * http(s)-only validation to keep the SSRF surface closed.\n */\nfunction resolveBaseUrl(override: string | undefined): string {\n\tconst fromEnv = process.env[API_BASE_ENV_VAR]?.trim();\n\tif (override !== undefined) return validateBaseUrl(override, '--api');\n\tif (fromEnv) return validateBaseUrl(fromEnv, `${API_BASE_ENV_VAR} env var`);\n\treturn DEFAULT_BASE_URL;\n}\n\nfunction validateBaseUrl(raw: string, source: string): string {\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(raw);\n\t} catch {\n\t\tthrow new CliError(`Invalid API base URL from ${source}: ${raw}`, ExitCode.InvalidArgument);\n\t}\n\tif (url.protocol !== 'https:' && url.protocol !== 'http:') {\n\t\tthrow new CliError(\n\t\t\t`API base URL from ${source} must be http or https (got ${url.protocol})`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\treturn raw.replace(/\\/$/, '');\n}\n\nfunction resolveLocale(override: string | undefined, config: CliConfig): Locale {\n\tconst raw = override ?? config.locale ?? DEFAULT_LOCALE;\n\tif (!(ALLOWED_LOCALES as readonly string[]).includes(raw)) {\n\t\tthrow new CliError(`Invalid --lang \"${raw}\". Allowed: ${ALLOWED_LOCALES.join(', ')}`, ExitCode.InvalidArgument);\n\t}\n\treturn raw as Locale;\n}\n\nfunction resolveTimeout(override: number | undefined, config: CliConfig): number {\n\tconst raw = override ?? config.timeout_ms ?? DEFAULT_TIMEOUT_MS;\n\tif (!Number.isInteger(raw) || raw < 100 || raw > 600_000) {\n\t\tthrow new CliError(`Invalid --timeout ${raw} (must be integer 100..600000)`, ExitCode.InvalidArgument);\n\t}\n\treturn raw;\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = {\n\tresolveBaseUrl,\n\tDEFAULT_BASE_URL,\n};\n","import { CliError, ExitCode } from './errors';\n\n/**\n * Read a value out of a nested object by dotted path (e.g. `job_info.job_title`).\n * Returns undefined if any segment is missing.\n *\n * Uses hasOwnProperty (not the `in` operator / direct index) so a path segment can\n * never traverse into `__proto__` / `constructor` and walk the prototype chain —\n * the input objects are server-controlled, so this is a deliberate safety boundary.\n */\nexport function getPath(obj: unknown, dottedPath: string): unknown {\n\tconst parts = dottedPath.split('.');\n\tlet cur: unknown = obj;\n\tfor (const p of parts) {\n\t\tif (cur && typeof cur === 'object' && Object.prototype.hasOwnProperty.call(cur, p)) {\n\t\t\tcur = (cur as Record<string, unknown>)[p];\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\treturn cur;\n}\n\n/**\n * Project an object down to a set of dotted paths, keyed by the path string itself\n * (so `pickPaths(job, ['job_info.job_title'])` → `{ 'job_info.job_title': '...' }`).\n *\n * Every requested path becomes a key so the shape is predictable across a list of\n * heterogeneous items: a missing path yields `null` rather than being dropped, which\n * keeps each row in `jobs search --fields` structurally identical for downstream tools.\n */\nexport function pickPaths(obj: unknown, paths: string[]): Record<string, unknown> {\n\tconst out: Record<string, unknown> = {};\n\tfor (const p of paths) {\n\t\tconst v = getPath(obj, p);\n\t\tout[p] = v === undefined ? null : v;\n\t}\n\treturn out;\n}\n\n/**\n * Parse a comma-separated `--fields` value into a trimmed, non-empty list.\n * Throws InvalidArgument if the result is empty (e.g. `--fields ,,`).\n */\nexport function parseFieldsList(raw: string): string[] {\n\tconst fields = raw\n\t\t.split(',')\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean);\n\tif (fields.length === 0) {\n\t\tthrow new CliError('--fields requires at least one field name', ExitCode.InvalidArgument);\n\t}\n\treturn fields;\n}\n","/**\n * Run `fn` over `items` with at most `limit` in flight at once, returning results in\n * the SAME order as the input (not completion order) so callers can correlate output\n * rows back to their input without threading an index through.\n *\n * `fn` is expected to handle its own errors (e.g. resolve to an error-shaped result);\n * a rejection from `fn` will reject the whole batch, so the batch caller wraps each\n * unit in try/catch to keep one failure from sinking the rest.\n */\nexport async function mapWithConcurrency<T, R>(\n\titems: readonly T[],\n\tlimit: number,\n\tfn: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n\tconst results = new Array<R>(items.length);\n\tlet cursor = 0;\n\n\tasync function worker(): Promise<void> {\n\t\tfor (;;) {\n\t\t\tconst index = cursor++;\n\t\t\tif (index >= items.length) return;\n\t\t\tresults[index] = await fn(items[index], index);\n\t\t}\n\t}\n\n\tconst workerCount = Math.min(Math.max(1, limit), items.length);\n\tawait Promise.all(Array.from({ length: workerCount }, () => worker()));\n\treturn results;\n}\n","import { readSync, readFileSync } from 'node:fs';\nimport { CliError, ExitCode, InvalidArgumentError } from './errors';\n\n/**\n * Reject with `CliError(ServerOrNetworkError)` if a promise doesn't settle within `ms`.\n *\n * Reserved for async user / network input paths that v0.2 will add (streaming jobs,\n * interactive prompts with deadlines). Existing call sites either use their own\n * mechanism (`api-client` uses `AbortSignal.timeout`, `reset.ts` uses an `'end'`\n * handler) or are synchronous (`readFileSync(0)`). Removing this until then would\n * just churn the import graph; keeping it documents the contract.\n *\n * @internal — exported for future call sites, not part of public CLI API\n */\nexport function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst t = setTimeout(() => {\n\t\t\treject(new CliError(`Timed out after ${ms}ms: ${label}`, ExitCode.ServerOrNetworkError));\n\t\t}, ms);\n\t\tpromise.then(\n\t\t\t(v) => {\n\t\t\t\tclearTimeout(t);\n\t\t\t\tresolve(v);\n\t\t\t},\n\t\t\t(err) => {\n\t\t\t\tclearTimeout(t);\n\t\t\t\treject(err);\n\t\t\t}\n\t\t);\n\t});\n}\n\n/** Convenience: throw InvalidArgumentError on TTY stdin reads with no piped input. */\nexport function ensureStdinPiped(label: string): void {\n\tif (process.stdin.isTTY) {\n\t\tthrow new InvalidArgumentError(`${label}: no data on stdin (run via pipe, or pass the value as an arg)`);\n\t}\n}\n\n/** Standalone backstop when a caller can't supply a `--timeout`-derived bound. */\nconst DEFAULT_STDIN_TIMEOUT_MS = 30_000;\n\nexport interface ReadPipedStdinOptions {\n\t/**\n\t * Upper bound (ms) on total time spent waiting for a slow / hung upstream pipe.\n\t * Callers pass the resolved `--timeout` so the limit is user-tunable; falls back to\n\t * DEFAULT_STDIN_TIMEOUT_MS when omitted.\n\t */\n\ttimeoutMs?: number;\n\t/** Injectable clock for tests; defaults to `Date.now`. */\n\tnow?: () => number;\n}\n\n/**\n * Synchronously drain piped stdin to a string, tolerating EAGAIN.\n *\n * A single `readFileSync(0)` / `readSync` can throw EAGAIN when stdin is a non-blocking\n * pipe whose upstream process is still producing (e.g. `wport jobs search ... | jq ... |\n * wport jobs view - --batch`): the fd has no data *right now* but isn't at EOF either.\n * Naively letting that throw makes the documented pipe workflow fail intermittently. We\n * retry on EAGAIN with a ~1ms synchronous sleep (Atomics.wait, to avoid a hot spin) and\n * stop on a zero-byte read or EOF.\n *\n * If the upstream neither produces data nor closes the pipe, the EAGAIN retry would spin\n * forever (the global `--timeout` only bounds HTTP, not stdin). We cap the total wait with\n * `timeoutMs` and surface a timeout as InvalidArgumentError rather than hanging silently.\n */\nexport function readPipedStdin(label: string, options: ReadPipedStdinOptions = {}): string {\n\tconst timeoutMs = options.timeoutMs ?? DEFAULT_STDIN_TIMEOUT_MS;\n\tconst now = options.now ?? Date.now;\n\tensureStdinPiped(label);\n\tconst chunks: Buffer[] = [];\n\tconst buf = Buffer.alloc(64 * 1024);\n\tconst sleeper = new Int32Array(new SharedArrayBuffer(4));\n\tconst deadline = now() + timeoutMs;\n\tfor (;;) {\n\t\tlet bytesRead: number;\n\t\ttry {\n\t\t\tbytesRead = readSync(0, buf, 0, buf.length, null);\n\t\t} catch (err) {\n\t\t\tconst code = (err as NodeJS.ErrnoException).code;\n\t\t\tif (code === 'EAGAIN') {\n\t\t\t\tif (now() > deadline) {\n\t\t\t\t\tthrow new InvalidArgumentError(`${label}: timed out after ${timeoutMs}ms waiting for piped stdin`);\n\t\t\t\t}\n\t\t\t\tAtomics.wait(sleeper, 0, 0, 1); // sleep ~1ms, then retry\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (code === 'EOF') break;\n\t\t\tthrow new InvalidArgumentError(`${label}: failed to read stdin: ${(err as Error).message}`);\n\t\t}\n\t\tif (bytesRead === 0) break;\n\t\tchunks.push(Buffer.from(buf.subarray(0, bytesRead)));\n\t}\n\treturn Buffer.concat(chunks).toString('utf8');\n}\n\nexport interface PromptSecretOptions {\n\t/** 注入點,預設用本檔的 readPipedStdin(測試可換 fake)。 */\n\treadPiped?: (label: string) => string;\n}\n\n/**\n * 互動式讀 secret:TTY 時 raw mode 隱藏輸入(不 echo、不進 shell history);\n * 非 TTY(CI / pipe)時直接讀整個 stdin。供 `wport enterprise login` 用。\n */\nexport function promptSecret(promptText: string, options: PromptSecretOptions = {}): Promise<string> {\n\tconst readPiped = options.readPiped ?? ((label: string) => readPipedStdin(label));\n\tif (!process.stdin.isTTY || !process.stdout.isTTY) {\n\t\treturn Promise.resolve(readPiped('login').trim());\n\t}\n\tprocess.stdout.write(promptText);\n\treturn new Promise<string>((resolve, reject) => {\n\t\tconst stdin = process.stdin;\n\t\tstdin.setRawMode(true);\n\t\tstdin.resume();\n\t\tstdin.setEncoding('utf8');\n\t\tlet buf = '';\n\t\tconst cleanup = (): void => {\n\t\t\tstdin.setRawMode(false);\n\t\t\tstdin.pause();\n\t\t\tstdin.off('data', onData);\n\t\t};\n\t\tconst onData = (chunk: string): void => {\n\t\t\tfor (const ch of chunk) {\n\t\t\t\tif (ch === '\u0003') {\n\t\t\t\t\t// Ctrl-C\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\treject(new CliError('Aborted', ExitCode.InvalidArgument));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (ch === '\\r' || ch === '\\n') {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\t\tresolve(buf.trim());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (ch === '' || ch === '\\b') {\n\t\t\t\t\tbuf = buf.slice(0, -1);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tbuf += ch;\n\t\t\t}\n\t\t};\n\t\tstdin.on('data', onData);\n\t});\n}\n\nexport interface ReadJsonInputOptions {\n\t/** 傳給 readPipedStdin 的上限(通常帶 resolved --timeout)。 */\n\ttimeoutMs?: number;\n\t/** 注入點,預設用 readPipedStdin;測試可換 fake,避免真的讀 fd 0。 */\n\treadStdin?: (label: string) => string;\n}\n\n/**\n * 讀 `--file <path>` 的 JSON body 供寫入命令(jobs create/update/batch)使用。\n * `path === '-'` 讀 stdin(管線 / here-doc)。body 直送後端驗證,CLI 只負責讀取 + parse。\n *\n * 讀不到檔 / 空輸入 / JSON parse 失敗 → InvalidArgumentError(exit 2、不發請求),\n * 錯誤訊息不含檔案內容(可能含敏感資料)。\n */\nexport function readJsonInput(source: string, options: ReadJsonInputOptions = {}): unknown {\n\tconst readStdin = options.readStdin ?? ((label: string) => readPipedStdin(label, { timeoutMs: options.timeoutMs }));\n\tlet raw: string;\n\tif (source === '-') {\n\t\traw = readStdin('--file -');\n\t} else {\n\t\ttry {\n\t\t\traw = readFileSync(source, 'utf8');\n\t\t} catch (err) {\n\t\t\tthrow new InvalidArgumentError(\n\t\t\t\t`Cannot read --file \"${source}\": ${(err as NodeJS.ErrnoException).code ?? (err as Error).message}`\n\t\t\t);\n\t\t}\n\t}\n\tif (!raw.trim()) {\n\t\tthrow new InvalidArgumentError('Input is empty — expected a JSON body');\n\t}\n\ttry {\n\t\treturn JSON.parse(raw);\n\t} catch {\n\t\t// 不夾帶 err.message:Node 的 JSON.parse SyntaxError 會把輸入片段放進訊息,\n\t\t// 可能外洩 --file 內容(如 secret)。固定訊息,守住上面 docstring 的承諾。\n\t\tthrow new InvalidArgumentError('Invalid JSON in input (parse failed)');\n\t}\n}\n\n/**\n * 讀 `--body-file <path>` 之類的純文字輸入(respond 信件內文)。\n * 結構鏡射 readJsonInput(source==='-' 讀 stdin,否則 readFileSync),但**回傳原始字串、不 parse**。\n * 讀不到檔 / 空輸入 → InvalidArgumentError(exit 2、不發請求);錯誤訊息不含檔案內容。\n */\nexport function readTextInput(source: string, options: ReadJsonInputOptions = {}): string {\n\tconst readStdin = options.readStdin ?? ((label: string) => readPipedStdin(label, { timeoutMs: options.timeoutMs }));\n\tlet raw: string;\n\tif (source === '-') {\n\t\traw = readStdin('--body-file -');\n\t} else {\n\t\ttry {\n\t\t\traw = readFileSync(source, 'utf8');\n\t\t} catch (err) {\n\t\t\tthrow new InvalidArgumentError(\n\t\t\t\t`Cannot read --body-file \"${source}\": ${(err as NodeJS.ErrnoException).code ?? (err as Error).message}`\n\t\t\t);\n\t\t}\n\t}\n\tif (!raw.trim()) {\n\t\tthrow new InvalidArgumentError('Input is empty — expected message body text');\n\t}\n\treturn raw;\n}\n\n/**\n * 同 readJsonInput,但要求 parse 結果是 JSON 物件(非陣列 / 非純量)。\n * jobs create/update 的 body、batch 的 `{jobs:[...]}` 外層皆為物件;先本地擋,錯誤更清楚。\n */\nexport function readJsonObject(source: string, options: ReadJsonInputOptions = {}): Record<string, unknown> {\n\tconst parsed = readJsonInput(source, options);\n\tif (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n\t\tthrow new InvalidArgumentError(\n\t\t\t`Input must be a JSON object, got ${Array.isArray(parsed) ? 'an array' : typeof parsed}`\n\t\t);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n","import type { Command } from 'commander';\nimport { createApiClient, throwForHttpStatus, unwrapDataResponse, type ApiClient } from '../../lib/api-client';\nimport { resolveContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { dim, printJson, printNdjsonLine, sanitizeForTerminal, sanitizeForTerminalMultiline } from '../../lib/output';\nimport { getPath, parseFieldsList, pickPaths } from '../../lib/path-utils';\nimport { mapWithConcurrency } from '../../lib/concurrency';\nimport { readPipedStdin } from '../../lib/io-helpers';\nimport pc from 'picocolors';\n\ninterface ViewFlags {\n\tfield?: string;\n\tfields?: string;\n\tbatch?: boolean;\n\tconcurrency?: number;\n}\n\nconst DEFAULT_BATCH_CONCURRENCY = 5;\nconst MAX_BATCH_CONCURRENCY = 20;\n\n/** One ND-JSON record emitted per enc_id in --batch mode. */\ninterface BatchResult {\n\tenc_id: string;\n\tok: boolean;\n\tdata?: unknown;\n\terror?: string;\n}\n\n/** Projects a fetched job down to whatever --field / --fields asked for (or the whole job). */\ntype JobProjector = (job: JobView) => unknown;\n\n/**\n * JobViewVM 是嵌套結構(見 src/modules/jobs/view-models/job-view.vm.ts)。\n * 這裡只列我們顯示時會碰到的欄位,其他欄位走 [k: string]: unknown 保留。\n */\ninterface JobView {\n\tcompany_header_info?: {\n\t\tcompany_name?: string;\n\t\tcompany_icon_url?: string;\n\t\tenc_company_id?: string;\n\t\t[k: string]: unknown;\n\t};\n\tjob_description?: string;\n\tjob_info?: {\n\t\tjob_title?: string;\n\t\tarea_display?: string;\n\t\tsalary_display?: string;\n\t\tjob_feature_display?: string | null;\n\t\texperience_display?: string | null;\n\t\t[k: string]: unknown;\n\t};\n\tjob_information?: Record<string, unknown>;\n\trecruitment_conditions?: Record<string, unknown>;\n\tbenefits?: Record<string, unknown>;\n\tabout_company?: Record<string, unknown> | null;\n\tapplication_method?: Record<string, unknown> | null;\n\tstructured_data?: Record<string, unknown> | null;\n\t[k: string]: unknown;\n}\n\nexport function registerJobsView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_id>')\n\t\t.description('View a single job. Pass \"-\" to read enc_id from stdin.')\n\t\t.option('--field <path>', 'output a single field as a raw value (dotted paths, e.g. job_info.job_title)')\n\t\t.option(\n\t\t\t'--fields <list>',\n\t\t\t'output selected fields as a JSON object (comma-separated dotted paths, e.g. job_info.job_title,company_header_info.company_name)'\n\t\t)\n\t\t.option(\n\t\t\t'--batch',\n\t\t\t'read newline-separated enc_ids from stdin and emit one ND-JSON record per job (requires \"-\" as the enc_id arg)'\n\t\t)\n\t\t.option(\n\t\t\t'--concurrency <n>',\n\t\t\t`max parallel requests in --batch mode (default ${DEFAULT_BATCH_CONCURRENCY}, max ${MAX_BATCH_CONCURRENCY})`,\n\t\t\t(v) => Number(v)\n\t\t)\n\t\t.action(async (encIdArg: string, flags: ViewFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tif (flags.field && flags.fields) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t'Use either --field (single raw value) or --fields (JSON object), not both',\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst client = createApiClient({\n\t\t\t\tbaseUrl: ctx.baseUrl,\n\t\t\t\tlocale: ctx.locale,\n\t\t\t\ttimeoutMs: ctx.timeoutMs,\n\t\t\t});\n\n\t\t\tif (flags.batch) {\n\t\t\t\tawait runBatchView(encIdArg, flags, client, ctx.timeoutMs);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst encId = encIdArg === '-' ? readPipedStdin('view -', { timeoutMs: ctx.timeoutMs }).trim() : encIdArg;\n\t\t\tif (!encId) {\n\t\t\t\tthrow new CliError('enc_id is required', ExitCode.InvalidArgument);\n\t\t\t}\n\n\t\t\tconst { data, error, response } = await client.GET('/api/jobs/{encId}/view', {\n\t\t\t\tparams: { path: { encId } },\n\t\t\t});\n\t\t\tif (!response.ok) throwForHttpStatus(response.status, error);\n\n\t\t\tconst job = unwrapDataResponse<JobView>(data);\n\n\t\t\tif (flags.fields) {\n\t\t\t\t// Multi-field projection → JSON object keyed by dotted path. printJson uses\n\t\t\t\t// JSON.stringify, which escapes control chars to \\uXXXX, so employer-controlled\n\t\t\t\t// string values are safe without extra sanitization (same rationale as the\n\t\t\t\t// json branch below).\n\t\t\t\tprintJson(pickPaths(job, parseFieldsList(flags.fields)));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (flags.field) {\n\t\t\t\tconst v = getPath(job, flags.field);\n\t\t\t\tif (v === undefined) {\n\t\t\t\t\tthrow new CliError(`Field \"${flags.field}\" not present in response`, ExitCode.InvalidArgument);\n\t\t\t\t}\n\t\t\t\t// String values are employer-controlled and printed raw — sanitize. The multiline\n\t\t\t\t// variant preserves \\n (e.g. when --field selects job_description) but still strips\n\t\t\t\t// every other control char including \\r and \\t. JSON.stringify already escapes\n\t\t\t\t// < 0x20 to \\uXXXX so non-string paths don't need extra handling.\n\t\t\t\tconst out = typeof v === 'string' ? sanitizeForTerminalMultiline(v) : JSON.stringify(v, null, 2);\n\t\t\t\tprocess.stdout.write(out + '\\n');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (ctx.format === 'json') {\n\t\t\t\tprintJson(job);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\trenderJobTable(job, encId, ctx.color);\n\t\t});\n}\n\n/**\n * --batch orchestration: read enc_ids off stdin, fetch each (bounded parallelism),\n * and stream one ND-JSON record per job. One failed enc_id becomes an `ok:false` line\n * rather than aborting the whole run. Output order matches input order.\n */\nasync function runBatchView(encIdArg: string, flags: ViewFlags, client: ApiClient, timeoutMs: number): Promise<void> {\n\tif (encIdArg !== '-') {\n\t\tthrow new CliError('--batch reads enc_ids from stdin; pass \"-\" as the enc_id argument', ExitCode.InvalidArgument);\n\t}\n\tconst encIds = parseBatchInput(readPipedStdin('view - --batch', { timeoutMs }));\n\tif (encIds.length === 0) {\n\t\tthrow new CliError('No enc_ids found on stdin', ExitCode.InvalidArgument);\n\t}\n\tconst concurrency = resolveBatchConcurrency(flags.concurrency);\n\tconst project = makeBatchProjector(flags);\n\tconst results = await runBatch(encIds, concurrency, (encId) => fetchJob(client, encId), project);\n\tfor (const record of results) printNdjsonLine(record);\n}\n\nasync function fetchJob(client: ApiClient, encId: string): Promise<JobView> {\n\tconst { data, error, response } = await client.GET('/api/jobs/{encId}/view', {\n\t\tparams: { path: { encId } },\n\t});\n\tif (!response.ok) throwForHttpStatus(response.status, error);\n\treturn unwrapDataResponse<JobView>(data);\n}\n\n/**\n * Pure batch driver (injectable fetcher) so the success/failure-mix behaviour is\n * testable without a live API. Each unit is wrapped so a rejected fetch turns into an\n * `ok:false` record instead of rejecting the whole batch.\n */\nasync function runBatch(\n\tencIds: string[],\n\tconcurrency: number,\n\tfetchOne: (encId: string) => Promise<JobView>,\n\tproject: JobProjector\n): Promise<BatchResult[]> {\n\treturn mapWithConcurrency(encIds, concurrency, async (encId): Promise<BatchResult> => {\n\t\ttry {\n\t\t\tconst job = await fetchOne(encId);\n\t\t\treturn { enc_id: encId, ok: true, data: project(job) };\n\t\t} catch (err) {\n\t\t\treturn { enc_id: encId, ok: false, error: err instanceof Error ? err.message : String(err) };\n\t\t}\n\t});\n}\n\nfunction makeBatchProjector(flags: ViewFlags): JobProjector {\n\tif (flags.fields) {\n\t\tconst paths = parseFieldsList(flags.fields);\n\t\treturn (job) => pickPaths(job, paths);\n\t}\n\tif (flags.field) {\n\t\tconst path = flags.field;\n\t\t// Missing path → null (keeps every record's shape stable across the batch),\n\t\t// unlike single-view --field which errors on a missing path.\n\t\treturn (job) => getPath(job, path) ?? null;\n\t}\n\treturn (job) => job;\n}\n\nfunction parseBatchInput(raw: string): string[] {\n\treturn raw\n\t\t.split('\\n')\n\t\t.map((s) => s.trim())\n\t\t.filter(Boolean);\n}\n\nfunction resolveBatchConcurrency(raw: number | undefined): number {\n\tconst n = raw ?? DEFAULT_BATCH_CONCURRENCY;\n\tif (!Number.isInteger(n) || n < 1 || n > MAX_BATCH_CONCURRENCY) {\n\t\tthrow new CliError(\n\t\t\t`--concurrency must be an integer between 1 and ${MAX_BATCH_CONCURRENCY} (got ${raw})`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\treturn n;\n}\n\nfunction renderJobTable(job: JobView, encId: string, color: boolean): void {\n\tconst label = (s: string) => (color ? pc.bold(s) : s);\n\t// All API string values are employer-controlled; sanitize before printing to defend\n\t// against terminal escape injection (clear screen, OSC 8 phishing hyperlinks, etc.).\n\tconst s = (v: string | undefined | null): string => (v ? sanitizeForTerminal(v) : '');\n\tconst info = job.job_info ?? {};\n\tconst company = job.company_header_info ?? {};\n\n\tconst lines: string[] = [];\n\tif (info.job_title) lines.push(`${label('Title:')} ${s(info.job_title)}`);\n\tif (company.company_name) lines.push(`${label('Company:')} ${s(company.company_name)}`);\n\tif (info.area_display) lines.push(`${label('Location:')} ${s(info.area_display)}`);\n\tif (info.salary_display) lines.push(`${label('Salary:')} ${s(info.salary_display)}`);\n\tif (info.job_feature_display) lines.push(`${label('Type:')} ${s(info.job_feature_display)}`);\n\tif (info.experience_display) lines.push(`${label('Experience:')} ${s(info.experience_display)}`);\n\t// encId comes from the CLI arg (user-provided) but pass through sanitize as a defense-in-depth.\n\tlines.push(dim(`enc_id: ${s(encId)}`, color));\n\tif (company.enc_company_id) lines.push(dim(`enc_company_id: ${s(company.enc_company_id)}`, color));\n\tprocess.stdout.write(lines.join('\\n') + '\\n');\n\n\tif (job.job_description) {\n\t\tprocess.stdout.write('\\n' + label('Description') + '\\n');\n\t\t// stripHtml only removes tags; sanitize afterwards in case the rich-text source\n\t\t// embedded raw escape sequences inside text nodes. Use the multiline variant so the\n\t\t// description's paragraph breaks (\\n) are preserved while \\r / \\t / other controls\n\t\t// are still stripped.\n\t\tprocess.stdout.write(renderDescription(job.job_description) + '\\n');\n\t}\n\n\tprocess.stdout.write(\n\t\t'\\n' +\n\t\t\tdim(\n\t\t\t\t'Tip: use --output json (or --field <dotted.path>, e.g. --field job_info.salary_display) for scripting.',\n\t\t\t\tcolor\n\t\t\t) +\n\t\t\t'\\n'\n\t);\n}\n\n/**\n * 後端 job_description 可能含 HTML(rich text);CLI 終端機列印時剝掉 tag。\n * 不做完整 HTML 解析 —— 只把 tag 拿掉、& 實體做最常見的還原。\n */\nfunction stripHtml(s: string): string {\n\treturn s\n\t\t.replace(/<\\/?(p|br|div|li|h[1-6])[^>]*>/gi, '\\n')\n\t\t.replace(/<[^>]+>/g, '')\n\t\t.replace(/&nbsp;/g, ' ')\n\t\t.replace(/&amp;/g, '&')\n\t\t.replace(/&lt;/g, '<')\n\t\t.replace(/&gt;/g, '>')\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&#39;/g, \"'\")\n\t\t.replace(/\\n{3,}/g, '\\n\\n')\n\t\t.trim();\n}\n\nfunction renderDescription(html: string): string {\n\treturn sanitizeForTerminalMultiline(stripHtml(html));\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = {\n\tstripHtml,\n\tgetPath,\n\trenderDescription,\n\trunBatch,\n\tmakeBatchProjector,\n\tparseBatchInput,\n\tresolveBatchConcurrency,\n};\n","import type { Command } from 'commander';\nimport { registerJobsSearch } from './search';\nimport { registerJobsView } from './view';\n\nexport function registerJobsCommand(program: Command): void {\n\tconst jobs = program.command('jobs').description('Search and view public job listings');\n\tregisterJobsSearch(jobs);\n\tregisterJobsView(jobs);\n}\n","import type { Command } from 'commander';\nimport { isConfigKey, isDeprecatedConfigKey, loadConfig, saveConfig, validateAndCoerce } from '../../lib/config-store';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { API_BASE_ENV_VAR } from '../../lib/global-opts';\n\nexport function registerConfigSet(parent: Command): void {\n\tparent\n\t\t.command('set <key> <value>')\n\t\t.description('Set a config value. Keys: locale, output, timeout_ms')\n\t\t.action((key: string, value: string) => {\n\t\t\tif (isDeprecatedConfigKey(key)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Config key \"${key}\" was removed in 0.1.2. Set the ${API_BASE_ENV_VAR} env var ` +\n\t\t\t\t\t\t`(or use the --api flag) instead.`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!isConfigKey(key)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Unknown config key \"${key}\". Allowed: locale, output, timeout_ms`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst coerced = validateAndCoerce(key, value);\n\t\t\tconst config = loadConfig();\n\t\t\t// Object.assign 形式避免 TS 5.5 在 `config[key] = coerced` 上推不出\n\t\t\t// ConfigValueMap[K]→CliConfig[K] 的對應;validateAndCoerce 已 generic、型別正確。\n\t\t\tObject.assign(config, { [key]: coerced });\n\t\t\tsaveConfig(config);\n\t\t\tprocess.stdout.write(`Set ${key} = ${JSON.stringify(coerced)}\\n`);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { isConfigKey, loadConfig } from '../../lib/config-store';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { printJson } from '../../lib/output';\n\nexport function registerConfigGet(parent: Command): void {\n\tparent\n\t\t.command('get [key]')\n\t\t.description('Print config value(s). With no key, prints the whole config as JSON.')\n\t\t.action((key: string | undefined) => {\n\t\t\tconst config = loadConfig();\n\t\t\tif (key === undefined) {\n\t\t\t\tprintJson(config);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!isConfigKey(key)) {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t`Unknown config key \"${key}\". Allowed: locale, output, timeout_ms`,\n\t\t\t\t\tExitCode.InvalidArgument\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst value = (config as Record<string, unknown>)[key];\n\t\t\tif (value === undefined) {\n\t\t\t\tprocess.stdout.write('\\n');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprocess.stdout.write((typeof value === 'string' ? value : JSON.stringify(value)) + '\\n');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { getConfigPath } from '../../lib/config-store';\n\nexport function registerConfigPath(parent: Command): void {\n\tparent\n\t\t.command('path')\n\t\t.description('Print the path to the config file (regardless of whether it exists)')\n\t\t.action(() => {\n\t\t\tprocess.stdout.write(getConfigPath() + '\\n');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { existsSync, unlinkSync } from 'node:fs';\nimport { getConfigPath } from '../../lib/config-store';\nimport { InvalidArgumentError } from '../../lib/errors';\n\nexport function registerConfigReset(parent: Command): void {\n\tparent\n\t\t.command('reset')\n\t\t.description('Delete the config file')\n\t\t.option('-f, --force', 'skip the confirmation prompt')\n\t\t.action(async (opts: { force?: boolean }) => {\n\t\t\tconst path = getConfigPath();\n\t\t\tif (!existsSync(path)) {\n\t\t\t\tprocess.stdout.write('No config file to delete.\\n');\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!opts.force) {\n\t\t\t\tconst ok = await promptYesNo(`Delete config at ${path}? [y/N] `);\n\t\t\t\tif (!ok) {\n\t\t\t\t\tprocess.stdout.write('Aborted.\\n');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tunlinkSync(path);\n\t\t\t} catch (err) {\n\t\t\t\tconst e = err as NodeJS.ErrnoException;\n\t\t\t\tif (e.code === 'ENOENT') {\n\t\t\t\t\t// Race with another process: someone deleted it between exists & unlink.\n\t\t\t\t\tprocess.stdout.write('No config file to delete (already removed).\\n');\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthrow new InvalidArgumentError(`Failed to delete config at ${path}: ${e.message}`);\n\t\t\t}\n\t\t\tprocess.stdout.write(`Deleted ${path}\\n`);\n\t\t});\n}\n\nfunction promptYesNo(prompt: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tprocess.stdout.write(prompt);\n\t\tlet buf = '';\n\t\tprocess.stdin.setEncoding('utf8');\n\t\tconst onData = (chunk: string) => {\n\t\t\tbuf += chunk;\n\t\t\tconst nl = buf.indexOf('\\n');\n\t\t\tif (nl >= 0) {\n\t\t\t\tcleanup();\n\t\t\t\tconst answer = buf.slice(0, nl).trim().toLowerCase();\n\t\t\t\tresolve(answer === 'y' || answer === 'yes');\n\t\t\t}\n\t\t};\n\t\tconst onEnd = () => {\n\t\t\tcleanup();\n\t\t\tprocess.stdout.write('\\n(no input — aborting)\\n');\n\t\t\tresolve(false);\n\t\t};\n\t\tconst cleanup = () => {\n\t\t\tprocess.stdin.removeListener('data', onData);\n\t\t\tprocess.stdin.removeListener('end', onEnd);\n\t\t\tprocess.stdin.pause();\n\t\t};\n\t\tprocess.stdin.on('data', onData);\n\t\tprocess.stdin.on('end', onEnd);\n\t});\n}\n","import type { Command } from 'commander';\nimport { registerConfigSet } from './set';\nimport { registerConfigGet } from './get';\nimport { registerConfigPath } from './path';\nimport { registerConfigReset } from './reset';\n\nexport function registerConfigCommand(program: Command): void {\n\tconst config = program.command('config').description('Manage CLI configuration');\n\tregisterConfigSet(config);\n\tregisterConfigGet(config);\n\tregisterConfigPath(config);\n\tregisterConfigReset(config);\n}\n","import type { Command } from 'commander';\nimport { existsSync } from 'node:fs';\nimport { createApiClient } from '../lib/api-client';\nimport { resolveContext } from '../lib/global-opts';\nimport { getConfigPath } from '../lib/config-store';\nimport { ExitCode } from '../lib/errors';\n\n/**\n * Query params the server accepts syntactically but silently ignores — surfacing them\n * here is the whole point: an agent reading `wport doctor` learns not to try to control\n * sort order (it can't), instead of discovering it the hard way via wrong-but-no-error\n * results. The CLI deliberately doesn't expose flags for these.\n */\nexport const SILENT_IGNORED_PARAMS = ['orderBy', 'order'];\n\nexport function registerDoctorCommand(program: Command): void {\n\tprogram\n\t\t.command('doctor')\n\t\t.description(\n\t\t\t'Diagnose CLI setup: resolved config, server reachability, schema fingerprint, and known server quirks.'\n\t\t)\n\t\t.action(async (_opts: unknown, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst line = (s = '') => process.stdout.write(s + '\\n');\n\n\t\t\tline(`wport-cli ${__CLI_VERSION__}`);\n\t\t\tline(` bundled schema fingerprint: ${__SCHEMA_HASH__}`);\n\t\t\tline('');\n\n\t\t\tline('Resolved configuration:');\n\t\t\tline(` API base URL: ${ctx.baseUrl}`);\n\t\t\tline(` locale: ${ctx.locale}`);\n\t\t\tline(` timeout: ${ctx.timeoutMs}ms`);\n\t\t\tconst cfgPath = getConfigPath();\n\t\t\tline(` config file: ${cfgPath}${existsSync(cfgPath) ? '' : ' (not present)'}`);\n\t\t\tline('');\n\n\t\t\tline('Server connectivity:');\n\t\t\tconst reachable = await probeServer(ctx, line);\n\t\t\tline('');\n\n\t\t\tline('Known server behaviours (read this before scripting an agent):');\n\t\t\tline(\n\t\t\t\t` • Sort is server-controlled. These query params are silently ignored: ${SILENT_IGNORED_PARAMS.join(', ')}.`\n\t\t\t);\n\t\t\tline(' • jobs search sorts by publish date, or by relevance when --keyword is set.');\n\t\t\tline(' • jobs view --batch caps parallelism (default 5, max 20) to stay friendly to the API.');\n\t\t\tline('');\n\n\t\t\tline('Schema drift:');\n\t\t\tline(' The fingerprint above identifies the OpenAPI contract this CLI was built against.');\n\t\t\tline(' Automated drift detection needs a server-side schema-version endpoint, which is');\n\t\t\tline(' not available yet — for now, compare fingerprints manually after a server release. [TODO]');\n\n\t\t\tif (!reachable) process.exit(ExitCode.ServerOrNetworkError);\n\t\t});\n}\n\n/**\n * Lightweight reachability probe: a 1-result search hits the real public endpoint\n * without pulling a meaningful payload. A network-layer failure is a hard \"unreachable\"\n * (caller exits non-zero); an HTTP response of any status still proves the host is\n * reachable, so we report the status but don't treat it as a connectivity failure.\n */\nasync function probeServer(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number },\n\tline: (s?: string) => void\n): Promise<boolean> {\n\ttry {\n\t\tconst client = createApiClient({ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs });\n\t\tconst { response } = await client.GET('/api/jobs/search', { params: { query: { pageSize: 1 } } });\n\t\tif (response.ok) {\n\t\t\tline(` ✓ reachable (HTTP ${response.status})`);\n\t\t} else {\n\t\t\tline(` ! reachable, but server responded HTTP ${response.status}`);\n\t\t}\n\t\treturn true;\n\t} catch (err) {\n\t\tline(` ✗ unreachable: ${err instanceof Error ? err.message : String(err)}`);\n\t\treturn false;\n\t}\n}\n","import { buildUserAgent, extractErrorMessage, fetchWithTimeout, throwForHttpStatus } from './api-client';\nimport { CliError, ExitCode } from './errors';\nimport { printWarn } from './output';\n\nexport interface EnterpriseRequestOptions {\n\tbaseUrl: string; // resolveContext 產出,已去尾斜線\n\tlocale: string;\n\ttimeoutMs: number;\n\tapiKey: string;\n}\n\nexport interface EnterpriseGetResult {\n\tbody: unknown;\n\theaders: Headers;\n}\n\nexport interface EnterprisePostResult {\n\tbody: unknown;\n\theaders: Headers;\n}\n\n/** 寫入請求可選 headers。jobs 寫入端點強制 Idempotency-Key;update 支援 If-Match 樂觀鎖。 */\nexport interface EnterpriseWriteExtra {\n\t/** 帶上則加 `Idempotency-Key` header。jobs create/update/publish/unpublish/delete/batch 後端強制此 header(缺 → 400)。 */\n\tidempotencyKey?: string;\n\t/** 帶上則加 `If-Match` header(jobs update 樂觀鎖,值為目標 updated_at;版本不符 → 409)。 */\n\tifMatch?: string;\n}\n\nconst ENTERPRISE_PREFIX = '/api/v1/enterprise';\n\n/**\n * 企業 API 唯讀 GET。手寫 wrapper 而非 openapi-fetch:openapi.yaml 缺 read 端點\n * response schema(spec §4.1 修訂),typed client 在這裡沒有可生成的型別。\n * timeout / 網路錯誤分類複用 api-client.fetchWithTimeout。\n */\nexport async function enterpriseGet(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tquery?: Record<string, string | number | undefined>\n): Promise<EnterpriseGetResult> {\n\tconst url = new URL(`${opts.baseUrl}${ENTERPRISE_PREFIX}${path}`);\n\tfor (const [k, v] of Object.entries(query ?? {})) {\n\t\tif (v !== undefined) url.searchParams.set(k, String(v));\n\t}\n\tconst request = new Request(url, {\n\t\theaders: {\n\t\t\tAuthorization: `Bearer ${opts.apiKey}`,\n\t\t\t'Accept-Language': opts.locale,\n\t\t\t'User-Agent': buildUserAgent(),\n\t\t\tAccept: 'application/json',\n\t\t},\n\t});\n\tconst res = await fetchWithTimeout(request, opts.timeoutMs);\n\tconst body: unknown = await res.json().catch(() => null);\n\tif (!res.ok) throwEnterpriseHttpError(res.status, body);\n\twarnIfRateLimitLow(res.headers);\n\treturn { body, headers: res.headers };\n}\n\n/**\n * 企業 API 寫入共用底層(POST/PATCH/DELETE)。手寫 wrapper(與 enterpriseGet 同理由:\n * openapi.yaml 缺 write 端點 response schema)。timeout / 網路錯誤分類複用 fetchWithTimeout;\n * 4xx/5xx 走 throwEnterpriseHttpError。\n *\n * headers:有 body 才帶 Content-Type;`extra.idempotencyKey` → `Idempotency-Key`;\n * `extra.ifMatch` → `If-Match`。呼叫端決定要不要帶(keys rotate 不帶;jobs 寫入必帶)。\n */\nasync function enterpriseWrite(\n\tmethod: 'POST' | 'PATCH' | 'DELETE',\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody?: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\tconst url = new URL(`${opts.baseUrl}${ENTERPRISE_PREFIX}${path}`);\n\tconst headers: Record<string, string> = {\n\t\tAuthorization: `Bearer ${opts.apiKey}`,\n\t\t'Accept-Language': opts.locale,\n\t\t'User-Agent': buildUserAgent(),\n\t\tAccept: 'application/json',\n\t};\n\tif (body !== undefined) headers['Content-Type'] = 'application/json';\n\tif (extra?.idempotencyKey) headers['Idempotency-Key'] = extra.idempotencyKey;\n\tif (extra?.ifMatch) headers['If-Match'] = extra.ifMatch;\n\tconst request = new Request(url, {\n\t\tmethod,\n\t\theaders,\n\t\tbody: body !== undefined ? JSON.stringify(body) : undefined,\n\t});\n\tconst res = await fetchWithTimeout(request, opts.timeoutMs);\n\tconst respBody: unknown = await res.json().catch(() => null);\n\tif (!res.ok) throwEnterpriseHttpError(res.status, respBody);\n\twarnIfRateLimitLow(res.headers);\n\treturn { body: respBody, headers: res.headers };\n}\n\n/**\n * 企業 API 寫入 POST。\n *\n * ⚠️ keys rotate 呼叫時**不帶** `extra`(後端 rotate 端點不掛 idempotency interceptor);\n * jobs create/batch 則必帶 `extra.idempotencyKey`(後端強制,缺 → 400)。\n */\nexport function enterprisePost(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn enterpriseWrite('POST', opts, path, body, extra);\n}\n\n/** 企業 API 寫入 PATCH(jobs update / publish / unpublish)。jobs 端點強制 Idempotency-Key。 */\nexport function enterprisePatch(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn enterpriseWrite('PATCH', opts, path, body, extra);\n}\n\n/** 企業 API 寫入 DELETE(jobs delete)。無 body;jobs 端點強制 Idempotency-Key。 */\nexport function enterpriseDelete(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn enterpriseWrite('DELETE', opts, path, undefined, extra);\n}\n\n/**\n * spec §5 + pm_41 §3:401/403 附情境提示。\n *\n * ⚠️ 為何是「合併提示」而非按子類精準分流(2026-07-01 staging live smoke 實測校正):\n * 後端全域 `I18nExceptionFilter` 會把 guard 丟的 `{ path: 'error.enterprise.expired_api_key' }`\n * 翻成 `message` 後**丟掉 path**,實際 body 只有 `{ message(已翻譯), error, statusCode }`。\n * 過期 / 撤銷 / 無效在 client 端**同為 401、無機器可辨識訊號**(訊息是 DB i18n、5 語系、會改字,\n * 不可 regex)。所以無法「拆開給不同 next-step」,改給一則涵蓋兩種修法的提示。\n *\n * 這仍解掉 backlog §3.3 的核心痛點:舊版只寫「run login」,過期 key 重 login 仍 401 是死路;\n * 現在明確點出「過期 → keys rotate(過期 key 仍可 rotate)」這條出路。\n * 精準分流需後端在錯誤契約放穩定 `code`(全域 filter 變更),列為後端 follow-up。\n *\n * 提示一律單行:頂層 printError 走 sanitizeForTerminal 會剝掉 \\n。\n */\nfunction throwEnterpriseHttpError(status: number, body: unknown): never {\n\tconst base = extractErrorMessage(body) ?? `HTTP ${status}`;\n\tif (status === 401) {\n\t\tthrow new CliError(\n\t\t\t`${base} — If your key has expired, rotate it in place: \\`wport enterprise keys rotate <enc_id>\\` ` +\n\t\t\t\t'(an expired key is still accepted for rotate). ' +\n\t\t\t\t'If it was revoked or is incorrect, obtain a valid key and run `wport enterprise login`.',\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n\tif (status === 403) {\n\t\tthrow new CliError(\n\t\t\t`${base} — Your key may lack the required scope; rotate or issue a key that includes it. ` +\n\t\t\t\t'If your company account has been suspended, please contact support.',\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n\tthrowForHttpStatus(status, body);\n}\n\n/** spec §5:剩餘配額 <10% 時 stderr 提醒(不阻斷;stderr 不污染 json stdout)。 */\nfunction warnIfRateLimitLow(headers: Headers): void {\n\tconst remaining = Number(headers.get('x-ratelimit-remaining'));\n\tconst limit = Number(headers.get('x-ratelimit-limit'));\n\tif (Number.isFinite(remaining) && Number.isFinite(limit) && limit > 0 && remaining / limit < 0.1) {\n\t\tprintWarn(`Rate limit nearly exhausted: ${remaining}/${limit} requests remaining this window.`, false);\n\t}\n}\n","import {\n\texistsSync,\n\treadFileSync,\n\tmkdirSync,\n\topenSync,\n\twriteSync,\n\tcloseSync,\n\tchmodSync,\n\trenameSync,\n\tunlinkSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport envPaths from 'env-paths';\nimport { CliError, ExitCode } from './errors';\nimport { printWarn } from './output';\n\nexport const API_KEY_ENV_VAR = 'WPORT_API_KEY';\nexport const KEY_PREFIX = 'wpk_live_';\n// Server contract(spec §2.1):wpk_live_ + 32 高熵字元。下限驗證擋手滑貼半截;\n// 不驗上限——server 端才是 key 有效性的唯一權威。\nconst KEY_MIN_LENGTH = KEY_PREFIX.length + 32;\n\nexport interface Credentials {\n\tapi_key: string;\n\tcompany_name: string;\n\tkey_last4: string;\n\tsaved_at: string;\n}\n\nexport type KeySource = 'flag' | 'env' | 'file';\n\nexport interface ResolvedKey {\n\tkey: string;\n\tsource: KeySource;\n}\n\nconst paths = envPaths('wport', { suffix: '' });\n\nexport function getCredentialsPath(): string {\n\treturn join(paths.config, 'credentials.json');\n}\n\nexport function isValidKeyFormat(key: string): boolean {\n\treturn key.startsWith(KEY_PREFIX) && key.length >= KEY_MIN_LENGTH && !/\\s/.test(key);\n}\n\n/** 任何輸出顯示 key 一律走這裡:只露末四碼。 */\nexport function maskKey(key: string): string {\n\treturn `${KEY_PREFIX}••••${key.slice(-4)}`;\n}\n\nexport function loadCredentials(): Credentials | null {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return null;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, 'utf8'));\n\t} catch (err) {\n\t\tthrow new CliError(\n\t\t\t`Failed to read credentials at ${path}: ${(err as Error).message}. Run \"wport enterprise login\" to recreate it.`,\n\t\t\tExitCode.ConfigCorrupt\n\t\t);\n\t}\n\tif (!parsed || typeof parsed !== 'object' || typeof (parsed as Record<string, unknown>).api_key !== 'string') {\n\t\tthrow new CliError(\n\t\t\t`Credentials file at ${path} is malformed. Run \"wport enterprise login\" to recreate it.`,\n\t\t\tExitCode.ConfigCorrupt\n\t\t);\n\t}\n\tconst raw = parsed as Record<string, unknown>;\n\tconst apiKey = raw.api_key as string;\n\treturn {\n\t\tapi_key: apiKey,\n\t\tcompany_name: typeof raw.company_name === 'string' ? raw.company_name : '',\n\t\tkey_last4: typeof raw.key_last4 === 'string' ? raw.key_last4 : apiKey.slice(-4),\n\t\tsaved_at: typeof raw.saved_at === 'string' ? raw.saved_at : '',\n\t};\n}\n\n// Atomic write 模式照抄 config-store.saveConfig:tmpfile(0o600) → rename。\n// credentials 比 config 更敏感,所以分檔(config 可能被使用者貼進 issue 除錯)。\nexport function saveCredentials(creds: Credentials): void {\n\tconst path = getCredentialsPath();\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst tmpPath = `${path}.tmp.${process.pid}.${Date.now()}`;\n\tconst fd = openSync(tmpPath, 'w', 0o600);\n\ttry {\n\t\twriteSync(fd, JSON.stringify(creds, null, 2) + '\\n');\n\t} catch (err) {\n\t\tcloseSync(fd);\n\t\ttry {\n\t\t\tunlinkSync(tmpPath);\n\t\t} catch {\n\t\t\t/* best effort cleanup */\n\t\t}\n\t\tthrow err;\n\t}\n\tcloseSync(fd);\n\tif (process.platform !== 'win32') {\n\t\ttry {\n\t\t\tchmodSync(tmpPath, 0o600);\n\t\t} catch (err) {\n\t\t\tprintWarn(\n\t\t\t\t`Failed to chmod 0600 on credentials tmpfile: ${(err as Error).message}. ` +\n\t\t\t\t\t'Other users on this system may be able to read your API key.',\n\t\t\t\tfalse\n\t\t\t);\n\t\t}\n\t}\n\trenameSync(tmpPath, path);\n}\n\nexport function deleteCredentials(): boolean {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return false;\n\tunlinkSync(path);\n\treturn true;\n}\n\n/**\n * Key 解析 precedence:--api-key flag > WPORT_API_KEY env > credentials.json。\n * 與 base url 解析(--api > WPORT_API_BASE > default,global-opts.ts)同款心智模型。\n */\nexport function resolveApiKey(flagValue?: string): ResolvedKey {\n\tif (flagValue !== undefined) {\n\t\tensureFormat(flagValue, '--api-key');\n\t\treturn { key: flagValue, source: 'flag' };\n\t}\n\tconst fromEnv = process.env[API_KEY_ENV_VAR]?.trim();\n\tif (fromEnv) {\n\t\tensureFormat(fromEnv, `${API_KEY_ENV_VAR} env var`);\n\t\treturn { key: fromEnv, source: 'env' };\n\t}\n\tconst creds = loadCredentials();\n\tif (creds) return { key: creds.api_key, source: 'file' };\n\tthrow new CliError(\n\t\t`No API key found. Run \"wport enterprise login\" or set the ${API_KEY_ENV_VAR} env var.`,\n\t\tExitCode.InvalidArgument\n\t);\n}\n\nfunction ensureFormat(key: string, source: string): void {\n\tif (!isValidKeyFormat(key)) {\n\t\t// 錯誤訊息絕不 echo key 原文(可能是手滑貼進來的其他 secret)\n\t\tthrow new CliError(`API key from ${source} is not a valid ${KEY_PREFIX} key`, ExitCode.InvalidArgument);\n\t}\n}\n","import type { Command } from 'commander';\nimport { resolveContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { promptSecret } from '../../lib/io-helpers';\nimport { enterpriseGet } from '../../lib/enterprise-client';\nimport {\n\tAPI_KEY_ENV_VAR,\n\tKEY_PREFIX,\n\tgetCredentialsPath,\n\tisValidKeyFormat,\n\tmaskKey,\n\tsaveCredentials,\n} from '../../lib/credentials-store';\nimport { printWarn } from '../../lib/output';\n\nexport interface LoginContext {\n\tbaseUrl: string;\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\n/** login 核心(與 commander 解耦供測試):驗格式 → 打 API 驗 key → 存檔。 */\nexport async function performLogin(ctx: LoginContext, key: string): Promise<void> {\n\tif (!isValidKeyFormat(key)) {\n\t\t// 不 echo 輸入原文 —— 可能是手滑貼進來的其他 secret\n\t\tthrow new CliError(\n\t\t\t`That does not look like a valid ${KEY_PREFIX} key. Nothing was saved.`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\t// 任何非 200 都由 enterpriseGet 丟出(401/403 → CliError 附情境提示),不落檔。\n\t// 打 GET /me 兼作 key 驗證與公司名取得(取代舊的 /jobs?pageSize=1 驗證)。\n\tconst { body } = await enterpriseGet({ ...ctx, apiKey: key }, '/me');\n\tsaveCredentials({\n\t\tapi_key: key,\n\t\tcompany_name: extractCompanyName(body),\n\t\tkey_last4: key.slice(-4),\n\t\tsaved_at: new Date().toISOString(),\n\t});\n}\n\n/**\n * 從 GET /me 的 DataResponse({ data: { company: { enc_id, name } } })取公司名。\n * shape 非預期時回空字串 —— login 已驗 key(200),不該因回應格式小變動而失敗;\n * company_name 缺失只讓 whoami 退回顯示 (unknown)。\n */\nfunction extractCompanyName(body: unknown): string {\n\tif (body && typeof body === 'object') {\n\t\tconst data = (body as { data?: unknown }).data;\n\t\tif (data && typeof data === 'object') {\n\t\t\tconst company = (data as { company?: unknown }).company;\n\t\t\tif (company && typeof company === 'object') {\n\t\t\t\tconst name = (company as { name?: unknown }).name;\n\t\t\t\tif (typeof name === 'string') return name;\n\t\t\t}\n\t\t}\n\t}\n\treturn '';\n}\n\nexport function registerEnterpriseLogin(parent: Command): void {\n\tparent\n\t\t.command('login')\n\t\t.description('Validate and save an enterprise API key (prompts securely; pipe stdin in CI)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst key = await promptSecret(`Paste your API key (${KEY_PREFIX}...): `);\n\t\t\tawait performLogin({ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs }, key);\n\t\t\tprocess.stdout.write(`Logged in. Key ${maskKey(key)} saved to ${getCredentialsPath()}\\n`);\n\t\t\tif (process.platform === 'win32') {\n\t\t\t\tprintWarn(\n\t\t\t\t\t`On Windows file permissions are best-effort. For stricter isolation, prefer the ${API_KEY_ENV_VAR} env var.`,\n\t\t\t\t\tctx.color\n\t\t\t\t);\n\t\t\t}\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { deleteCredentials, getCredentialsPath } from '../../lib/credentials-store';\n\nexport function registerEnterpriseLogout(parent: Command): void {\n\tparent\n\t\t.command('logout')\n\t\t.description('Delete the saved enterprise API key')\n\t\t.action(() => {\n\t\t\tconst deleted = deleteCredentials();\n\t\t\tprocess.stdout.write(\n\t\t\t\tdeleted ? `Logged out. Removed ${getCredentialsPath()}\\n` : 'No saved credentials to remove.\\n'\n\t\t\t);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { loadCredentials, maskKey, resolveApiKey } from '../../lib/credentials-store';\n\ninterface WhoamiGlobals {\n\tapiKey?: string;\n}\n\nexport function registerEnterpriseWhoami(parent: Command): void {\n\tparent\n\t\t.command('whoami')\n\t\t.description('Show which enterprise key is in effect (offline; reads local state only)')\n\t\t.action((_flags: unknown, command: Command) => {\n\t\t\tconst globals = command.optsWithGlobals() as WhoamiGlobals;\n\t\t\tconst resolved = resolveApiKey(globals.apiKey);\n\t\t\tconst creds = resolved.source === 'file' ? loadCredentials() : null;\n\t\t\tconst lines = [\n\t\t\t\t`key: ${maskKey(resolved.key)}`,\n\t\t\t\t`source: ${resolved.source}`,\n\t\t\t\t`company: ${creds?.company_name || '(unknown)'}`,\n\t\t\t];\n\t\t\tif (creds?.saved_at) lines.push(`saved: ${creds.saved_at}`);\n\t\t\tprocess.stdout.write(lines.join('\\n') + '\\n');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '../../lib/api-client';\nimport { enterpriseGet } from '../../lib/enterprise-client';\nimport { resolveApiKey } from '../../lib/credentials-store';\nimport { resolveContext } from '../../lib/global-opts';\nimport { dim, printJson, type OutputFormat } from '../../lib/output';\n\n/** 欄位對齊 server EnterpriseUsageVm(enterprise-usage.vm.ts,pm_41 批次 2)。 */\nexport interface EnterpriseUsage {\n\tperiod?: string;\n\tquota?: { limit?: number; used?: number; remaining?: number };\n\trate_limit?: { limit?: number; window_seconds?: number };\n\t[k: string]: unknown;\n}\n\nfunction num(value: number | undefined): string {\n\treturn typeof value === 'number' && Number.isFinite(value) ? String(value) : '—';\n}\n\n/**\n * `usage` 核心:驗 key → GET /usage → 輸出當期用量摘要。與 commander 註冊分離便於測試。\n * GET /usage 回 DataResponse(單一物件,非陣列)。\n *\n * rate_limit 為「每把 key 的設定上限」(非即時剩餘);即時 per-window 剩餘由被限流端點\n * (jobs 寫入)的 x-ratelimit-remaining response header 呈現,此讀端點不掛 throttle。\n */\nexport async function runUsage(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat; color: boolean },\n\tapiKey: string\n): Promise<void> {\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/usage'\n\t);\n\tconst usage = unwrapDataResponse<EnterpriseUsage>(body);\n\n\tif (ctx.format === 'json') {\n\t\tprintJson(usage);\n\t\treturn;\n\t}\n\n\tconst quota = usage.quota ?? {};\n\tconst rate = usage.rate_limit ?? {};\n\tconst lines = [\n\t\t`period: ${usage.period ?? '—'}`,\n\t\t`monthly quota: ${num(quota.used)} / ${num(quota.limit)} used (${num(quota.remaining)} remaining)`,\n\t\t`rate limit: ${num(rate.limit)} requests / ${num(rate.window_seconds)}s per key`,\n\t];\n\tprocess.stdout.write(lines.join('\\n') + '\\n');\n\tprocess.stdout.write(\n\t\tdim('Live per-window rate-limit headroom is reported via response headers on write requests.', ctx.color) + '\\n'\n\t);\n}\n\nexport function registerEnterpriseUsage(parent: Command): void {\n\tparent\n\t\t.command('usage')\n\t\t.description('Show this month API quota usage and rate-limit ceiling')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runUsage(ctx, key);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { num };\n","import type { Command } from 'commander';\nimport { asPaginatedBody } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { dim, printJson, printTable, type OutputFormat } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\n\ninterface ListFlags {\n\tpage?: number;\n\tpageSize?: number;\n\tkeyword?: string;\n\tstatus?: string;\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/**\n * Server 契約(EnterpriseJobsQueryDto.status: number,0=未刊登,1=已刊登)。\n * openapi.yaml 寫的 active|inactive|deleted 是 drift,送字串會 400 —— 一律走這個映射。\n */\nconst STATUS_MAP: Record<string, number> = { published: 1, unpublished: 0 };\n\nconst MINIMAL_LIST_FIELDS = ['enc_id', 'job_title', 'status', 'updated_at'];\n\n/** 欄位對齊 server EnterpriseJobVm(enterprise-job.vm.ts)。 */\ninterface EnterpriseJobItem {\n\tenc_id?: string;\n\tjob_title?: string | null;\n\tcode?: string | null;\n\tstatus?: number;\n\tcreated_at?: string | null;\n\tupdated_at?: string | null;\n\t// pm_41 list stats:published_at / clicks_7d 已有值;其餘為 null 佔位(後端後續補值,契約形狀不變)。\n\tpublished_at?: string | null;\n\tclicks_7d?: number | null;\n\tapplications?: number | null;\n\tvisits_7d?: number | null;\n\tpublisher_display?: string | null;\n\t[k: string]: unknown;\n}\n\nfunction mapStatusFlag(raw: string | undefined): number | undefined {\n\tif (raw === undefined) return undefined;\n\tif (raw in STATUS_MAP) return STATUS_MAP[raw];\n\tthrow new CliError(\n\t\t`Invalid --status \"${raw}\". Allowed: ${Object.keys(STATUS_MAP).join(', ')}`,\n\t\tExitCode.InvalidArgument\n\t);\n}\n\nexport function formatStatus(status: number | undefined): string {\n\tif (status === 1) return 'published';\n\tif (status === 0) return 'unpublished';\n\treturn status === undefined ? '' : String(status);\n}\n\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\n/** 數值型統計欄:0 照印,null/未定義(尚未接的佔位欄)以 — 呈現。 */\nfunction formatCount(value: number | null | undefined): string {\n\treturn typeof value === 'number' && Number.isFinite(value) ? String(value) : '—';\n}\n\n/**\n * `enterprise jobs list` 核心:驗 key → GET /jobs → 輸出分頁職缺列表(含 pm_41 list stats)。\n * 與 commander 註冊分離便於測試(mock enterpriseGet 即可 smoke)。\n */\nexport async function runEnterpriseJobsList(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat; color: boolean },\n\tapiKey: string,\n\tflags: ListFlags\n): Promise<void> {\n\tif (flags.fields && flags.minimal) {\n\t\tthrow new CliError('Use either --fields or --minimal, not both', ExitCode.InvalidArgument);\n\t}\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/jobs',\n\t\t{\n\t\t\tcurrentPage: flags.page,\n\t\t\tpageSize: flags.pageSize,\n\t\t\tkeyword: flags.keyword,\n\t\t\tstatus: mapStatusFlag(flags.status),\n\t\t}\n\t);\n\tconst paged = asPaginatedBody<EnterpriseJobItem>(body);\n\n\tconst projection = flags.minimal ? MINIMAL_LIST_FIELDS : flags.fields ? parseFieldsList(flags.fields) : undefined;\n\tif (projection || ctx.format === 'json') {\n\t\tprintJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tpaged.data,\n\t\t[\n\t\t\t{ header: 'ENC_ID', value: (r) => (r.enc_id ?? '').slice(0, 14) },\n\t\t\t{ header: 'TITLE', value: (r) => r.job_title ?? '', maxWidth: 36 },\n\t\t\t{ header: 'STATUS', value: (r) => formatStatus(r.status) },\n\t\t\t{ header: 'CLICKS_7D', value: (r) => formatCount(r.clicks_7d) },\n\t\t\t{ header: 'PUBLISHED', value: (r) => formatDate(r.published_at), maxWidth: 12 },\n\t\t\t{ header: 'UPDATED', value: (r) => formatDate(r.updated_at), maxWidth: 12 },\n\t\t],\n\t\tctx.color\n\t);\n\tconst head = `Showing page ${paged.currentPage}/${paged.totalPages} (${paged.data.length} of ${paged.totalCount} jobs).`;\n\tconst hint =\n\t\tpaged.totalPages > paged.currentPage ? ` Next: wport enterprise jobs list --page ${paged.currentPage + 1}` : '';\n\tprocess.stdout.write(dim(head + hint, ctx.color) + '\\n');\n}\n\nexport function registerEnterpriseJobsList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your company job postings')\n\t\t.option('--page <n>', 'page number (server: currentPage, default 1)', (v) => Number(v))\n\t\t.option('--page-size <n>', 'items per page (server: pageSize, default 10, max 100)', (v) => Number(v))\n\t\t.option('--keyword <kw>', 'filter by job title keyword')\n\t\t.option('--status <state>', 'filter by status: published | unpublished')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.option('--minimal', `output only ${MINIMAL_LIST_FIELDS.join(',')} as JSON`)\n\t\t.action(async (flags: ListFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runEnterpriseJobsList(ctx, key, flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { mapStatusFlag, formatStatus, formatCount };\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\nimport { formatStatus } from './list';\n\ninterface ViewFlags {\n\tfields?: string;\n}\n\ninterface EnterpriseJobDetail {\n\tenc_id?: string;\n\tjob_title?: string | null;\n\tcode?: string | null;\n\tstatus?: number;\n\tcreated_at?: string | null;\n\tupdated_at?: string | null;\n\t[k: string]: unknown;\n}\n\nconst DETAIL_FIELDS: ReadonlyArray<string> = ['enc_id', 'job_title', 'code', 'status', 'created_at', 'updated_at'];\n\nfunction renderDetailLines(job: EnterpriseJobDetail): string[] {\n\tconst pad = Math.max(...DETAIL_FIELDS.map((f) => f.length)) + 1;\n\tconst lines: string[] = [];\n\tfor (const field of DETAIL_FIELDS) {\n\t\tconst raw = job[field];\n\t\tif (raw === null || raw === undefined) continue;\n\t\tconst value = field === 'status' ? formatStatus(raw as number) : String(raw);\n\t\tlines.push(`${(field + ':').padEnd(pad + 1)}${sanitizeForTerminal(value)}`);\n\t}\n\treturn lines;\n}\n\nexport function registerEnterpriseJobsView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_id>')\n\t\t.description('View one of your company job postings')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.action(async (encId: string, flags: ViewFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tif (!encId.trim()) {\n\t\t\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t\t\t}\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tconst { body } = await enterpriseGet(\n\t\t\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey: key },\n\t\t\t\t`/jobs/${encodeURIComponent(encId.trim())}`\n\t\t\t);\n\t\t\tconst job = unwrapDataResponse<EnterpriseJobDetail>(body);\n\n\t\t\tif (flags.fields) {\n\t\t\t\tprintJson(pickPaths(job, parseFieldsList(flags.fields)));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (ctx.format === 'json') {\n\t\t\t\tprintJson(job);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprocess.stdout.write(renderDetailLines(job).join('\\n') + '\\n');\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { renderDetailLines };\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePost } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { printJson } from '../../../lib/output';\nimport type { WriteCtx } from './write-shared';\n\ninterface CreateFlags {\n\tfile?: string;\n\tidempotencyKey?: string;\n}\n\n/** create/update 回應:後端只回 { enc_id }(enterprise-jobs.controller)。 */\nexport interface CreatedJob {\n\tenc_id?: string;\n\t[k: string]: unknown;\n}\n\n/**\n * `jobs create` 核心:讀 --file/stdin 的 JSON body → POST /jobs(201)→ 回 { enc_id }。\n * body 直送後端驗證(欄位/巢狀 salary·work_area 皆由 server DTO 把關)。\n * idempotencyKey 由呼叫端決定(後端強制帶,缺 → 400)。\n */\nexport async function runJobsCreate(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tsource: string,\n\tidempotencyKey: string\n): Promise<void> {\n\tconst jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });\n\tconst { body } = await enterprisePost(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/jobs',\n\t\tjobBody,\n\t\t{ idempotencyKey }\n\t);\n\tconst created = unwrapDataResponse<CreatedJob>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(created);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Created job: ${created.enc_id ?? ''}\\n`);\n}\n\nexport function registerEnterpriseJobsCreate(parent: Command): void {\n\tparent\n\t\t.command('create')\n\t\t.description('Create a job posting from a JSON file (use \"-\" to read stdin)')\n\t\t.requiredOption('--file <path>', 'path to a JSON job body, or \"-\" for stdin')\n\t\t.option('--idempotency-key <key>', 'reuse across retries to avoid duplicate creates (default: a fresh UUID)')\n\t\t.action(async (flags: CreateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runJobsCreate(ctx, key, flags.file as string, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePatch } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { printJson } from '../../../lib/output';\nimport type { CreatedJob } from './create';\nimport { requireEncId, type WriteCtx } from './write-shared';\n\ninterface UpdateFlags {\n\tfile?: string;\n\tidempotencyKey?: string;\n\tifMatch?: string;\n}\n\n/**\n * `jobs update` 核心:讀 --file/stdin 的 partial JSON → PATCH /jobs/:enc_id(200)。\n * --if-match 帶目標 updated_at → 樂觀鎖(版本不符後端回 409)。\n */\nexport async function runJobsUpdate(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tencId: string,\n\tsource: string,\n\tidempotencyKey: string,\n\tifMatch?: string\n): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst jobBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });\n\tconst { body } = await enterprisePatch(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/jobs/${encodeURIComponent(trimmed)}`,\n\t\tjobBody,\n\t\t{ idempotencyKey, ifMatch }\n\t);\n\tconst updated = unwrapDataResponse<CreatedJob>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(updated);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Updated job: ${updated.enc_id ?? trimmed}\\n`);\n}\n\nexport function registerEnterpriseJobsUpdate(parent: Command): void {\n\tparent\n\t\t.command('update <enc_id>')\n\t\t.description('Update a job posting from a JSON file (partial; use \"-\" for stdin)')\n\t\t.requiredOption('--file <path>', 'path to a partial JSON job body, or \"-\" for stdin')\n\t\t.option('--if-match <updated_at>', \"optimistic lock: the job's current updated_at (409 if stale)\")\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: UpdateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runJobsUpdate(ctx, key, encId, flags.file as string, flags.idempotencyKey ?? randomUUID(), flags.ifMatch);\n\t\t});\n}\n","import { CliError, ExitCode } from '../../../lib/errors';\nimport type { OutputFormat } from '../../../lib/output';\n\n/**\n * 企業職缺寫入命令共用的 resolved context 子集。\n * 寫入命令目前不使用 `color`(輸出非表格著色),故不納入。\n */\nexport interface WriteCtx {\n\tbaseUrl: string;\n\tlocale: string;\n\ttimeoutMs: number;\n\tformat: OutputFormat;\n}\n\n/** enc_id 去空白 + 非空檢查(空字串 → exit 2、不發請求)。 */\nexport function requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) throw new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\treturn trimmed;\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterpriseDelete, enterprisePatch, enterprisePost } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson } from '../../../lib/output';\nimport type { CreatedJob } from './create';\nimport { requireEncId, type WriteCtx } from './write-shared';\n\ninterface LifecycleFlags {\n\tidempotencyKey?: string;\n\tconfirm?: boolean;\n}\n\n/** publish / unpublish 共用:PATCH /jobs/:enc_id/{action}(空 body,帶 Idempotency-Key)。 */\nexport async function runJobsTransition(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tencId: string,\n\taction: 'publish' | 'unpublish',\n\tidempotencyKey: string\n): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst { body } = await enterprisePatch(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/jobs/${encodeURIComponent(trimmed)}/${action}`,\n\t\t{},\n\t\t{ idempotencyKey }\n\t);\n\tconst result = unwrapDataResponse<CreatedJob>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(result);\n\t\treturn;\n\t}\n\tconst verb = action === 'publish' ? 'Published' : 'Unpublished';\n\tprocess.stdout.write(`${verb} job: ${result.enc_id ?? trimmed}\\n`);\n}\n\n/**\n * `jobs delete` 核心:破壞性寫入 —— 無 `--confirm` 一律本地 exit 2、**不發請求**(PRD §6.2)。\n * 帶 Idempotency-Key(後端強制)。\n */\nexport async function runJobsDelete(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tencId: string,\n\tconfirm: boolean,\n\tidempotencyKey: string\n): Promise<void> {\n\tif (!confirm) {\n\t\tthrow new CliError('Refusing to delete without --confirm (destructive, irreversible)', ExitCode.InvalidArgument);\n\t}\n\tconst trimmed = requireEncId(encId);\n\tawait enterpriseDelete(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/jobs/${encodeURIComponent(trimmed)}`,\n\t\t{ idempotencyKey }\n\t);\n\t// DELETE 後端回 `DataResponse(null, ...)`(data 為 null、無 enc_id)→ 不 unwrap/deref(否則\n\t// null.enc_id 在 table 模式下丟 TypeError);用呼叫時的 enc_id 回報刪除結果。\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: trimmed, deleted: true });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Deleted job: ${trimmed}\\n`);\n}\n\n/**\n * `jobs copy` 核心:POST /jobs/:enc_id/copy(空 body,帶 Idempotency-Key)→ 回新職缺\n * { enc_id, code, created_at }(新職缺為未刊登草稿)。後端強制 Idempotency-Key。\n */\nexport async function runJobsCopy(ctx: WriteCtx, apiKey: string, encId: string, idempotencyKey: string): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst { body } = await enterprisePost(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/jobs/${encodeURIComponent(trimmed)}/copy`,\n\t\t{},\n\t\t{ idempotencyKey }\n\t);\n\tconst result = unwrapDataResponse<CreatedJob>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(result);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Copied job ${trimmed} → new draft: ${result.enc_id ?? '(unknown)'}\\n`);\n}\n\nexport function registerEnterpriseJobsCopy(parent: Command): void {\n\tparent\n\t\t.command('copy <enc_id>')\n\t\t.description('Copy a job posting into a new unpublished draft')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: LifecycleFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runJobsCopy(ctx, key, encId, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n\n/**\n * `jobs close` 核心:破壞性、**不可逆**(關閉後無法再上架,需 copy 重開)——無 `--confirm`\n * 一律本地 exit 2、**不發請求**(PRD §6.2,對齊 delete)。PATCH /jobs/:enc_id/close(帶 Idempotency-Key)。\n */\nexport async function runJobsClose(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tencId: string,\n\tconfirm: boolean,\n\tidempotencyKey: string\n): Promise<void> {\n\tif (!confirm) {\n\t\tthrow new CliError(\n\t\t\t'Refusing to close without --confirm (irreversible; a closed job cannot be reopened — copy it to relist)',\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\tconst trimmed = requireEncId(encId);\n\tconst { body } = await enterprisePatch(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/jobs/${encodeURIComponent(trimmed)}/close`,\n\t\t{},\n\t\t{ idempotencyKey }\n\t);\n\tconst result = unwrapDataResponse<CreatedJob>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(result);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Closed job: ${result.enc_id ?? trimmed}\\n`);\n}\n\nexport function registerEnterpriseJobsClose(parent: Command): void {\n\tparent\n\t\t.command('close <enc_id>')\n\t\t.description('Close (finalize) a job posting (destructive, irreversible; requires --confirm)')\n\t\t.option('--confirm', 'confirm this irreversible close (a closed job cannot be reopened)')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: LifecycleFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runJobsClose(ctx, key, encId, flags.confirm === true, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n\nexport function registerEnterpriseJobsPublish(parent: Command): void {\n\tparent\n\t\t.command('publish <enc_id>')\n\t\t.description('Publish a job posting')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: LifecycleFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runJobsTransition(ctx, key, encId, 'publish', flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n\nexport function registerEnterpriseJobsUnpublish(parent: Command): void {\n\tparent\n\t\t.command('unpublish <enc_id>')\n\t\t.description('Unpublish (take down) a job posting')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: LifecycleFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runJobsTransition(ctx, key, encId, 'unpublish', flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n\nexport function registerEnterpriseJobsDelete(parent: Command): void {\n\tparent\n\t\t.command('delete <enc_id>')\n\t\t.description('Delete a job posting (destructive; requires --confirm)')\n\t\t.option('--confirm', 'confirm this destructive, irreversible delete')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: LifecycleFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runJobsDelete(ctx, key, encId, flags.confirm === true, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePost } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport type { WriteCtx } from './write-shared';\n\ninterface BatchFlags {\n\tfile?: string;\n\tconfirm?: boolean;\n\tidempotencyKey?: string;\n}\n\n/** 對齊後端 207 body:data = { succeeded:[{index,enc_id}], failed:[{index,error_path}] }。 */\nexport interface BatchResult {\n\tsucceeded?: { index: number; enc_id: string }[];\n\tfailed?: { index: number; error_path: string }[];\n}\n\nconst BATCH_MIN = 1;\nconst BATCH_MAX = 10;\n\n/**\n * `jobs batch` 核心:讀 {jobs:[...]}(1..10)→ POST /jobs/batch(207 multi-status)。\n * 破壞性/大量寫入 → 無 `--confirm` 本地 exit 2、不發請求。逐筆獨立成敗;有 failed → exit 3\n * (結果已印出供腳本解析)。帶 Idempotency-Key(後端強制)。\n */\nexport async function runJobsBatch(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tsource: string,\n\tconfirm: boolean,\n\tidempotencyKey: string\n): Promise<void> {\n\tif (!confirm) {\n\t\tthrow new CliError('Refusing to run batch create without --confirm', ExitCode.InvalidArgument);\n\t}\n\tconst payload = readJsonObject(source, { timeoutMs: ctx.timeoutMs });\n\tconst jobs = payload.jobs;\n\tif (!Array.isArray(jobs) || jobs.length < BATCH_MIN || jobs.length > BATCH_MAX) {\n\t\tthrow new CliError(\n\t\t\t`Batch input must be { \"jobs\": [...] } with ${BATCH_MIN} to ${BATCH_MAX} items`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\tconst { body } = await enterprisePost(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/jobs/batch',\n\t\tpayload,\n\t\t{ idempotencyKey }\n\t);\n\tconst result = unwrapDataResponse<BatchResult>(body);\n\tconst succeeded = result.succeeded ?? [];\n\tconst failed = result.failed ?? [];\n\n\tif (ctx.format === 'json') {\n\t\tprintJson(result);\n\t} else {\n\t\tprocess.stdout.write(`Batch: ${succeeded.length} succeeded, ${failed.length} failed (of ${jobs.length}).\\n`);\n\t\tfor (const s of succeeded)\n\t\t\tprocess.stdout.write(` ok [${s.index}] ${sanitizeForTerminal(String(s.enc_id ?? ''))}\\n`);\n\t\tfor (const f of failed)\n\t\t\tprocess.stdout.write(` fail [${f.index}] ${sanitizeForTerminal(String(f.error_path ?? ''))}\\n`);\n\t}\n\n\t// 部分成功也算未全成 → 非 0 exit 讓腳本偵測(結果已印出)。\n\tif (failed.length > 0) {\n\t\tthrow new CliError(`${failed.length} of ${jobs.length} job(s) failed in batch create`, ExitCode.ServerClientError);\n\t}\n}\n\nexport function registerEnterpriseJobsBatch(parent: Command): void {\n\tparent\n\t\t.command('batch')\n\t\t.description('Batch-create up to 10 jobs from a JSON file ({ \"jobs\": [...] }; requires --confirm)')\n\t\t.requiredOption('--file <path>', 'path to a JSON { \"jobs\": [...] } body, or \"-\" for stdin')\n\t\t.option('--confirm', 'confirm this bulk write')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (flags: BatchFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runJobsBatch(ctx, key, flags.file as string, flags.confirm === true, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { registerEnterpriseJobsList } from './list';\nimport { registerEnterpriseJobsView } from './view';\nimport { registerEnterpriseJobsCreate } from './create';\nimport { registerEnterpriseJobsUpdate } from './update';\nimport {\n\tregisterEnterpriseJobsPublish,\n\tregisterEnterpriseJobsUnpublish,\n\tregisterEnterpriseJobsDelete,\n\tregisterEnterpriseJobsCopy,\n\tregisterEnterpriseJobsClose,\n} from './lifecycle';\nimport { registerEnterpriseJobsBatch } from './batch';\n\nexport function registerEnterpriseJobsCommand(parent: Command): void {\n\tconst jobs = parent.command('jobs').description('Manage your company job postings');\n\tregisterEnterpriseJobsList(jobs);\n\tregisterEnterpriseJobsView(jobs);\n\tregisterEnterpriseJobsCreate(jobs);\n\tregisterEnterpriseJobsUpdate(jobs);\n\tregisterEnterpriseJobsPublish(jobs);\n\tregisterEnterpriseJobsUnpublish(jobs);\n\tregisterEnterpriseJobsDelete(jobs);\n\tregisterEnterpriseJobsCopy(jobs);\n\tregisterEnterpriseJobsClose(jobs);\n\tregisterEnterpriseJobsBatch(jobs);\n}\n","import type { Command } from 'commander';\nimport { unwrapDataArray } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { dim, printJson, printTable, type OutputFormat } from '../../../lib/output';\n\n/** 欄位對齊 server EnterpriseKeyVm(enterprise-key.vm.ts,pm_41 KEY-M-1)。明文不在此。 */\nexport interface EnterpriseKeyItem {\n\tenc_id?: string;\n\tname?: string | null;\n\tkey_prefix?: string;\n\tkey_last4?: string | null;\n\tscopes?: string[];\n\tstatus?: 'active' | 'expired' | 'revoked' | string;\n\texpires_at?: string | null;\n\tlast_used_at?: string | null;\n\t[k: string]: unknown;\n}\n\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\nfunction formatScopes(scopes: string[] | undefined): string {\n\treturn Array.isArray(scopes) ? scopes.join(',') : '';\n}\n\n/**\n * `keys list` 核心:驗 key → GET /keys → 輸出。與 commander 註冊分離,方便測試\n * (同 login.performLogin 模式)。GET /keys 回 DataResponse(非分頁),data 是陣列。\n */\nexport async function runKeysList(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat; color: boolean },\n\tapiKey: string\n): Promise<void> {\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/keys'\n\t);\n\tconst keys = unwrapDataArray<EnterpriseKeyItem>(body);\n\n\tif (ctx.format === 'json') {\n\t\tprintJson(keys);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tkeys,\n\t\t[\n\t\t\t{ header: 'ENC_ID', value: (r) => (r.enc_id ?? '').slice(0, 14) },\n\t\t\t{ header: 'NAME', value: (r) => r.name ?? '', maxWidth: 24 },\n\t\t\t{ header: 'LAST4', value: (r) => r.key_last4 ?? '' },\n\t\t\t{ header: 'SCOPES', value: (r) => formatScopes(r.scopes), maxWidth: 28 },\n\t\t\t{ header: 'STATUS', value: (r) => r.status ?? '' },\n\t\t\t{ header: 'EXPIRES', value: (r) => formatDate(r.expires_at), maxWidth: 12 },\n\t\t\t{ header: 'LAST_USED', value: (r) => formatDate(r.last_used_at), maxWidth: 12 },\n\t\t],\n\t\tctx.color\n\t);\n\tconst active = keys.filter((k) => k.status === 'active').length;\n\tprocess.stdout.write(dim(`${keys.length} key(s), ${active} active.`, ctx.color) + '\\n');\n}\n\nexport function registerEnterpriseKeysList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your company API keys (masked)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runKeysList(ctx, key);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { formatDate, formatScopes };\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePost } from '../../../lib/enterprise-client';\nimport { API_KEY_ENV_VAR, maskKey, resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { dim, printJson, printWarn, sanitizeForTerminal, type OutputFormat } from '../../../lib/output';\n\n// 對齊 server ENTERPRISE_KEY_EXPIRY_DAYS(enterprise-api-key.constants.ts)。CLI 是獨立套件、\n// 不能 import server code,故在此複製一份;server 端 @IsIn 才是唯一權威,本地只做提前擋。\nconst ENTERPRISE_KEY_EXPIRY_DAYS = [30, 60, 90] as const;\n\ninterface RotateFlags {\n\texpiryDays?: number;\n\treveal?: boolean;\n}\n\n/** 回應對齊 server EnterpriseKeyIssuedVm(enterprise-key.vm.ts,pm_41 KEY-M-2)。 */\nexport interface EnterpriseKeyIssued {\n\tapi_key?: string;\n\tenc_id?: string;\n\tname?: string | null;\n\tscopes?: string[];\n\texpires_at?: string | null;\n\tkey_last4?: string | null;\n\t[k: string]: unknown;\n}\n\n/** --expiry-days 若帶,必須是 30/60/90(server EnterpriseRotateKeyDto @IsIn)。本地先擋,省一次請求。 */\nexport function validateExpiryDays(raw: number | undefined): number | undefined {\n\tif (raw === undefined) return undefined;\n\tif (!(ENTERPRISE_KEY_EXPIRY_DAYS as readonly number[]).includes(raw)) {\n\t\tthrow new CliError(\n\t\t\t`Invalid --expiry-days ${raw}. Allowed: ${ENTERPRISE_KEY_EXPIRY_DAYS.join(', ')}`,\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\treturn raw;\n}\n\n/**\n * `keys rotate` 核心:POST /keys/:enc_id/rotate → 回新明文(僅一次)。與 commander 分離便於測試。\n *\n * 注意:呼叫端已 resolveApiKey,**不可**在本地擋過期 key —— rotate 是唯一「拿過期 key\n * 當 Bearer 仍可過」的端點(server EnterpriseApiKeyRotateGuard,pm_41 §3.5)。\n */\nexport async function runKeysRotate(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat; color: boolean },\n\tapiKey: string,\n\tencId: string,\n\tflags: RotateFlags\n): Promise<void> {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) throw new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\tconst expiryDays = validateExpiryDays(flags.expiryDays);\n\n\tconst requestBody: Record<string, unknown> = {};\n\tif (expiryDays !== undefined) requestBody.expiry_days = expiryDays;\n\n\tconst { body } = await enterprisePost(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/keys/${encodeURIComponent(trimmed)}/rotate`,\n\t\trequestBody\n\t);\n\tconst issued = unwrapDataResponse<EnterpriseKeyIssued>(body);\n\n\tif (ctx.format === 'json') {\n\t\t// json 模式帶完整明文 api_key(供腳本擷取,PRD §6.7/§12.1)。\n\t\tprintJson(issued);\n\t} else {\n\t\tconst plaintext = issued.api_key ?? '';\n\t\tconst shown = flags.reveal ? plaintext : maskKey(plaintext);\n\t\tconst lines = [\n\t\t\t`New API key: ${sanitizeForTerminal(shown)}`,\n\t\t\t`enc_id: ${sanitizeForTerminal(issued.enc_id ?? '')}`,\n\t\t\t`scopes: ${sanitizeForTerminal((issued.scopes ?? []).join(','))}`,\n\t\t\t`expires_at: ${sanitizeForTerminal(issued.expires_at ?? '')}`,\n\t\t];\n\t\tprocess.stdout.write(lines.join('\\n') + '\\n');\n\t\tif (!flags.reveal) {\n\t\t\tprocess.stdout.write(dim('Re-run with --reveal to print the full key once.', ctx.color) + '\\n');\n\t\t}\n\t}\n\n\t// 舊 key 立即失效(PRD §12.1):務必提醒更新憑證,否則下次呼叫用舊 key 會 401。stderr 不污染 json stdout。\n\tprintWarn(\n\t\t`The previous key is now invalid. Update your ${API_KEY_ENV_VAR} env var / credential store, ` +\n\t\t\t'or run \"wport enterprise login\" with the new key.',\n\t\tctx.color\n\t);\n}\n\nexport function registerEnterpriseKeysRotate(parent: Command): void {\n\tparent\n\t\t.command('rotate <enc_id>')\n\t\t.description('Rotate an API key in place (issues a new key, invalidates the old one)')\n\t\t.option('--expiry-days <n>', 'new key lifetime in days (30 | 60 | 90; default 90)', (v) => Number(v))\n\t\t.option('--reveal', 'print the full new key (default masks all but the last 4 chars)')\n\t\t.action(async (encId: string, flags: RotateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\t// resolveApiKey 只驗格式,不驗到期 —— 過期 key 仍是合法 wpk_live_ 格式,會放行(rotate 需要)。\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runKeysRotate(ctx, key, encId, flags);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { registerEnterpriseKeysList } from './list';\nimport { registerEnterpriseKeysRotate } from './rotate';\n\nexport function registerEnterpriseKeysCommand(parent: Command): void {\n\tconst keys = parent.command('keys').description('List and rotate your company API keys');\n\tregisterEnterpriseKeysList(keys);\n\tregisterEnterpriseKeysRotate(keys);\n}\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { printJson, sanitizeForTerminal, type OutputFormat } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\nimport type { EnterpriseCompany } from './types';\n\ninterface ViewFlags {\n\tfields?: string;\n}\n\n/**\n * `companies.status`(entity 註解):0=未上傳註冊文件,1=待審核,2=已通過審核,3=審核未通過。\n * 與 jobs 的 published/unpublished 是不同 domain(審核狀態 vs 刊登旗標),不可共用\n * jobs/list.ts 的 `formatStatus`。目前 CLI/後端皆無既有 company 狀態 formatter,\n * 此處自建、僅供 view 這支命令用。\n */\nconst COMPANY_STATUS_LABELS: Record<number, string> = {\n\t0: 'not_submitted',\n\t1: 'pending_review',\n\t2: 'approved',\n\t3: 'rejected',\n};\n\nfunction formatCompanyStatus(status: number | undefined): string {\n\tif (status === undefined) return '';\n\treturn COMPANY_STATUS_LABELS[status] ?? String(status);\n}\n\n/** phone_code + phone_number 合併成一行可讀字串(例:`mobile 0912345678`)。任一缺失則印有值的那個。 */\nfunction formatPhone(company: EnterpriseCompany): string | undefined {\n\tconst code = company.phone_code ?? undefined;\n\tconst number = company.phone_number ?? undefined;\n\tif (!code && !number) return undefined;\n\treturn [code, number].filter(Boolean).join(' ');\n}\n\n/**\n * 資本額顯示:`capital_show_status` 是 client-side-only 顯示旗標(API 一律回傳原始\n * `capital_amount`,是否顯示由前端/CLI 自行決定)。0 → 顯示 \"not displayed\" 而非金額本身,\n * 避免洩漏公司不想公開的資本額;`capital_amount` 為 null/undefined 則整行省略。\n */\nfunction formatCapital(company: EnterpriseCompany): string | undefined {\n\tif (company.capital_amount === null || company.capital_amount === undefined) return undefined;\n\tif (company.capital_show_status === 0) return 'not displayed';\n\treturn String(company.capital_amount);\n}\n\n/** area_code 是內部代碼、非人類可讀地名;顯示以 `address`(可讀地址字串)為主。 */\nfunction formatAddress(company: EnterpriseCompany): string | undefined {\n\treturn company.address ?? undefined;\n}\n\nconst DETAIL_FIELDS: ReadonlyArray<string> = [\n\t'name',\n\t'uniform_number',\n\t'status',\n\t'website',\n\t'phone',\n\t'address',\n\t'capital',\n\t'logo_url',\n];\n\nfunction renderDetailLines(company: EnterpriseCompany): string[] {\n\tconst pad = Math.max(...DETAIL_FIELDS.map((f) => f.length)) + 1;\n\tconst derived: Record<string, string | undefined> = {\n\t\tname: company.name,\n\t\tuniform_number: company.uniform_number ?? undefined,\n\t\tstatus: formatCompanyStatus(company.status),\n\t\twebsite: company.website ?? undefined,\n\t\tphone: formatPhone(company),\n\t\taddress: formatAddress(company),\n\t\tcapital: formatCapital(company),\n\t\tlogo_url: company.logo_url ?? undefined,\n\t};\n\tconst lines: string[] = [];\n\tfor (const field of DETAIL_FIELDS) {\n\t\tconst value = derived[field];\n\t\tif (value === null || value === undefined || value === '') continue;\n\t\tlines.push(`${(field + ':').padEnd(pad + 1)}${sanitizeForTerminal(value)}`);\n\t}\n\treturn lines;\n}\n\nexport async function runCompanyView(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat },\n\tapiKey: string,\n\tflags: ViewFlags\n): Promise<void> {\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/company'\n\t);\n\tconst company = unwrapDataResponse<EnterpriseCompany>(body);\n\n\tif (flags.fields) {\n\t\tprintJson(pickPaths(company, parseFieldsList(flags.fields)));\n\t\treturn;\n\t}\n\tif (ctx.format === 'json') {\n\t\tprintJson(company);\n\t\treturn;\n\t}\n\tprocess.stdout.write(renderDetailLines(company).join('\\n') + '\\n');\n}\n\n/**\n * `company view` 註冊(pm_41 Card B Task 1)。\n */\nexport function registerEnterpriseCompanyView(parent: Command): void {\n\tparent\n\t\t.command('view')\n\t\t.description('View your company information')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.action(async (flags: ViewFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runCompanyView(ctx, key, flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { renderDetailLines, formatCompanyStatus };\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { CliError, ExitCode, InvalidArgumentError, ServerClientHttpError } from '../../../lib/errors';\nimport { enterpriseGet, enterprisePatch } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { readJsonObject, type ReadJsonInputOptions } from '../../../lib/io-helpers';\nimport { printJson } from '../../../lib/output';\nimport type { WriteCtx } from '../jobs/write-shared';\nimport { BASIC_FIELDS, DESCRIPTION_FIELDS, FORBIDDEN_FIELDS, type EnterpriseCompany } from './types';\n\n/** `PATCH /company/basic` 的 6 必填欄位(對齊 `EnterpriseCompanyBasicUpdateDto`,CO-A3)。 */\nconst REQUIRED_BASIC_FIELDS: ReadonlyArray<string> = [\n\t'name',\n\t'industry_category_code',\n\t'phone_code',\n\t'phone_number',\n\t'area_code',\n\t'address',\n];\n\n/**\n * Card A `GET /company`(`EnterpriseCompanyVm`)承諾一定會回傳的 update-ready 欄位。\n * 用來防禦 Card A/Card B 之間的 contract drift(見 Task 2 brief 第 5 點):用 `in`\n * 而非 falsy 檢查,因為 `null`/`0`/`false` 都是合法值,只有「完全沒有這個 key」才代表契約壞了。\n */\nconst CONTRACT_REQUIRED_GET_FIELDS: ReadonlyArray<string> = [\n\t'industry_category_code',\n\t'area_code',\n\t'employee_count_range_code',\n\t'capital_amount',\n\t'capital_show_status',\n\t'latest_news',\n\t'uniform_number',\n];\n\nexport interface CompanyUpdatePayloads {\n\t/** 待送 `PATCH /company/basic` 的整段合併結果;使用者輸入完全沒碰到任何 basic 欄位時為 `null`。 */\n\tbasic: Record<string, unknown> | null;\n\t/** 待送 `PATCH /company/descriptions` 的部分欄位;使用者輸入完全沒碰到任何 description 欄位時為 `null`。 */\n\tdescriptions: Record<string, unknown> | null;\n}\n\n/**\n * 用已解析好的 JSON 物件,做本地驗證後讀目前公司資料合併成可送出的 PATCH payload\n * (pm_41 Card B Task 2;Task 3 review 後改為接收已解析物件,見下方 Note)。\n * 只組資料、不發 PATCH——`runCompanyUpdate` 負責用回傳值呼叫\n * `PATCH /company/basic`/`PATCH /company/descriptions` 並處理 idempotency key。\n *\n * Note(Task 3 reviewer finding 修正):本函式不再自己呼叫 `readJsonObject` 讀\n * `--file`/stdin——讀取已上移到呼叫端 `runCompanyUpdate`,讓它能在這裡的 GET 呼叫\n * 之前,先用解析好的 input 判斷 needsBasic/needsDescriptions 並完成 idempotency-key\n * 驗證。`readJsonObject(source, options)` 全流程只能呼叫一次(stdin 只能讀一次),\n * 因此本函式的參數改成 `input: Record<string, unknown>`,呼叫端只讀一次、傳進來。\n *\n * 驗證與早退順序(見各步驟註解說明原因):\n * 1. 本地擋 FORBIDDEN_FIELDS(唯讀/v1.x 尚未支援欄位)\n * 2. 本地擋不在 BASIC_FIELDS ∪ DESCRIPTION_FIELDS 的未知欄位\n * 3. 「輸入完全沒有可寫欄位」快速失敗——刻意排在 GET 呼叫之前,因為這個判斷不需要\n * GET 的回應內容,沒必要為了一個注定要失敗的請求多打一次網路\n * 4. GET 現況、防禦性檢查 Card A 契約欄位是否齊全\n * 5. 合併 basic payload;必填欄位缺值(使用者沒給、目前公司資料也沒有)本地擋在 PATCH 之前\n * 6. 組 description payload(僅取使用者輸入中出現的欄位,不與現況合併——descriptions\n * 端點是各自獨立選填,partial 是常態,不同於 basic 的 full-section update)\n */\nexport async function buildCompanyUpdatePayloads(\n\tinput: Record<string, unknown>,\n\tctx: { baseUrl: string; locale: string; timeoutMs: number },\n\tapiKey: string\n): Promise<CompanyUpdatePayloads> {\n\tconst forbiddenFound = Object.keys(input).filter((k) => FORBIDDEN_FIELDS.includes(k));\n\tif (forbiddenFound.length > 0) {\n\t\tthrow new InvalidArgumentError(`Field(s) not writable via CLI: ${forbiddenFound.join(', ')}`);\n\t}\n\n\tconst writableFields = new Set<string>([...BASIC_FIELDS, ...DESCRIPTION_FIELDS]);\n\tconst unknownFound = Object.keys(input).filter((k) => !writableFields.has(k));\n\tif (unknownFound.length > 0) {\n\t\tthrow new InvalidArgumentError(`Unknown field(s): ${unknownFound.join(', ')}`);\n\t}\n\n\tconst inputHasBasicField = BASIC_FIELDS.some((f) => f in input);\n\tconst inputHasDescriptionField = DESCRIPTION_FIELDS.some((f) => f in input);\n\tif (!inputHasBasicField && !inputHasDescriptionField) {\n\t\t// 不需要 GET 的回應內容就能判斷,刻意排在 GET 呼叫之前,省一次不必要的網路請求。\n\t\tthrow new InvalidArgumentError('No writable company fields provided');\n\t}\n\n\t// descriptions-only 的輸入不需要現況合併,但仍需要 GET 一次來滿足下面 basic 的合併需求;\n\t// basic 的 full-section 合併規則(CO-A3)要求即使只改一個 basic 欄位,也要整段送出,\n\t// 因此不論本次是否觸及 basic 欄位,都固定發這一次 GET,行為單純、不需要條件式跳過。\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/company'\n\t);\n\tconst current = unwrapDataResponse<EnterpriseCompany>(body);\n\n\tconst missingContractFields = CONTRACT_REQUIRED_GET_FIELDS.filter((f) => !(f in current));\n\tif (missingContractFields.length > 0) {\n\t\t// 這是防禦性檢查,理論上不該發生(Card A VM 承諾一定回這些欄位);一旦發生代表\n\t\t// Card A/Card B 契約漂移,不是使用者輸入錯誤,用 ServerOrNetworkError(exit 4)\n\t\t// 與其他「後端回應不如預期」的情況(見 unwrapDataResponse)保持一致,\n\t\t// 和上面 InvalidArgumentError(exit 2,使用者輸入錯)區隔開來。\n\t\tthrow new CliError(\n\t\t\t`Backend contract appears broken: GET /company response is missing field(s): ${missingContractFields.join(', ')}`,\n\t\t\tExitCode.ServerOrNetworkError\n\t\t);\n\t}\n\n\tlet basic: Record<string, unknown> | null = null;\n\tif (inputHasBasicField) {\n\t\tconst merged: Record<string, unknown> = {};\n\t\tfor (const field of BASIC_FIELDS) {\n\t\t\tmerged[field] = field in input ? input[field] : current[field];\n\t\t}\n\t\tconst missingRequired = REQUIRED_BASIC_FIELDS.filter((f) => {\n\t\t\tconst v = merged[f];\n\t\t\treturn v === null || v === undefined || v === '';\n\t\t});\n\t\tif (missingRequired.length > 0) {\n\t\t\tconst [first] = missingRequired;\n\t\t\tthrow new InvalidArgumentError(\n\t\t\t\t`Missing required field '${first}' — not provided in input and not present on your current company profile` +\n\t\t\t\t\t(missingRequired.length > 1 ? ` (also missing: ${missingRequired.slice(1).join(', ')})` : '')\n\t\t\t);\n\t\t}\n\t\tbasic = merged;\n\t}\n\n\tlet descriptions: Record<string, unknown> | null = null;\n\tif (inputHasDescriptionField) {\n\t\tdescriptions = {};\n\t\tfor (const field of DESCRIPTION_FIELDS) {\n\t\t\tif (field in input) descriptions[field] = input[field];\n\t\t}\n\t}\n\n\treturn { basic, descriptions };\n}\n\n/** Card A `PATCH /company/descriptions` 422 partial-failure body(見本檔 module doc 與 Task 3 brief)。 */\ninterface DescriptionsPartialFailureBody {\n\tpath?: string;\n\tupdated_sections?: string[];\n\tfailed_section?: string;\n}\n\nfunction isDescriptionsPartialFailureBody(body: unknown): body is DescriptionsPartialFailureBody {\n\treturn (\n\t\t!!body &&\n\t\ttypeof body === 'object' &&\n\t\tArray.isArray((body as Record<string, unknown>).updated_sections) &&\n\t\ttypeof (body as Record<string, unknown>).failed_section === 'string'\n\t);\n}\n\n/** 印出最終公司狀態(成功路徑共用):JSON 印整份物件,預設文字只印公司名稱一行。 */\nfunction printCompanyResult(company: EnterpriseCompany, format: 'table' | 'json'): void {\n\tif (format === 'json') {\n\t\tprintJson(company);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Updated company: ${company.name ?? ''}\\n`);\n}\n\nexport interface UpdateIdempotencyFlags {\n\tidempotencyKey?: string;\n\tbasicIdempotencyKey?: string;\n\tdescriptionsIdempotencyKey?: string;\n}\n\n/**\n * 決定 basic / descriptions 兩段各自要用的 idempotency key。\n *\n * 規則(Task 3 brief):\n * - 只有一段要送:`--idempotency-key` 可用;沒給就各自 fresh UUID。\n * - 兩段都要送:`--idempotency-key` 不可用(同一把 key 打兩個不同 payload 有誤用重放風險),\n * 必須改用 `--basic-idempotency-key` / `--descriptions-idempotency-key`(或都不給、各自 fresh UUID)。\n * 這個檢查必須在任何網路呼叫之前完成(本地 exit 2)。\n */\nexport function resolveUpdateIdempotencyKeys(\n\tneedsBasic: boolean,\n\tneedsDescriptions: boolean,\n\tflags: UpdateIdempotencyFlags\n): { basicKey?: string; descriptionsKey?: string } {\n\tconst bothNeeded = needsBasic && needsDescriptions;\n\tif (bothNeeded && flags.idempotencyKey !== undefined) {\n\t\tthrow new InvalidArgumentError(\n\t\t\t'Both basic and descriptions sections changed: use --basic-idempotency-key and ' +\n\t\t\t\t'--descriptions-idempotency-key instead of --idempotency-key, to avoid reusing the same key for two different payloads'\n\t\t);\n\t}\n\treturn {\n\t\tbasicKey: needsBasic ? (flags.basicIdempotencyKey ?? flags.idempotencyKey ?? randomUUID()) : undefined,\n\t\tdescriptionsKey: needsDescriptions\n\t\t\t? (flags.descriptionsIdempotencyKey ?? flags.idempotencyKey ?? randomUUID())\n\t\t\t: undefined,\n\t};\n}\n\n/**\n * `company update` 核心(pm_41 Card B Task 3):讀一次 `--file`/stdin JSON、判斷本次\n * 觸及哪些區段並驗證 idempotency-key 旗標(皆不需網路),再交給 `buildCompanyUpdatePayloads`\n * 組好 payload 後,依實際變更的區段送出 `PATCH /company/basic` 與/或\n * `PATCH /company/descriptions`。\n *\n * 讀取與早退順序(Task 3 reviewer finding 修正——原本 `resolveUpdateIdempotencyKeys` 排在\n * `buildCompanyUpdatePayloads` 之後,導致使用者打錯 idempotency-key 旗標時,仍會先觸發\n * `buildCompanyUpdatePayloads` 內部的 `GET /company` 才被擋下來,不符合「idempotency-key\n * 驗證不該有任何網路副作用」的要求):\n * 1. `readJsonObject(source, options)` 讀一次 JSON(全流程僅此一次,stdin 只能讀一次)\n * 2. 用解析好的 input 判斷 needsBasic/needsDescriptions(`BASIC_FIELDS`/`DESCRIPTION_FIELDS`\n * 是否命中,不需要 GET 回應)\n * 3. `resolveUpdateIdempotencyKeys` 本地驗證 idempotency-key 旗標組合 —— 與下一步\n * `buildCompanyUpdatePayloads` 內部的 forbidden/unknown-field 檢查何者先做不影響正確性\n * (兩者都在任何網路呼叫之前),這裡選擇先做 idempotency-key 驗證單純是因為它不需要\n * 再次檢查欄位合法性,可以直接用第 2 步算出的 needsBasic/needsDescriptions\n * 4. `buildCompanyUpdatePayloads(input, ctx, apiKey)`:本地擋 forbidden/unknown 欄位 →\n * GET 現況 → 合併 → 組 payload(見該函式 docstring)\n *\n * 呼叫順序固定 basic 先、descriptions 後(對齊 Task 2 內部欄位分組順序,也是任意但合理的預設;\n * 兩段彼此欄位不重疊、順序本身不影響最終狀態,唯一有影響的是「第二段失敗時,第一段是否已 commit」,\n * 見下方 partial-failure 分支)。\n *\n * 最終公司狀態的來源:兩段都送時,優先採用 descriptions PATCH 回應內的 `company`\n * (Card A `updateDescriptions` 內部在自己寫入後重跑同一套 view-building 邏輯,\n * 這份 `company` 已經是「basic + descriptions 都寫入後」的最新狀態),不再多打一次 GET。\n */\nexport async function runCompanyUpdate(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tsource: string,\n\tidempotencyFlags: UpdateIdempotencyFlags,\n\toptions: ReadJsonInputOptions = {}\n): Promise<void> {\n\tconst input = readJsonObject(source, options);\n\tconst needsBasic = BASIC_FIELDS.some((f) => f in input);\n\tconst needsDescriptions = DESCRIPTION_FIELDS.some((f) => f in input);\n\tconst { basicKey, descriptionsKey } = resolveUpdateIdempotencyKeys(needsBasic, needsDescriptions, idempotencyFlags);\n\n\tconst payloads = await buildCompanyUpdatePayloads(input, ctx, apiKey);\n\n\tconst requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };\n\n\tlet basicResultCompany: EnterpriseCompany | undefined;\n\tif (needsBasic) {\n\t\tconst { body } = await enterprisePatch(requestOpts, '/company/basic', payloads.basic as Record<string, unknown>, {\n\t\t\tidempotencyKey: basicKey,\n\t\t});\n\t\tbasicResultCompany = unwrapDataResponse<EnterpriseCompany>(body);\n\t}\n\n\tif (!needsDescriptions) {\n\t\t// basic-only(或兩段都沒有——buildCompanyUpdatePayloads 已擋掉這個情況,不會到這裡)。\n\t\tprintCompanyResult(basicResultCompany as EnterpriseCompany, ctx.format);\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst { body } = await enterprisePatch(\n\t\t\trequestOpts,\n\t\t\t'/company/descriptions',\n\t\t\tpayloads.descriptions as Record<string, unknown>,\n\t\t\t{ idempotencyKey: descriptionsKey }\n\t\t);\n\t\tconst result = unwrapDataResponse<{ updated_sections: string[]; company: EnterpriseCompany }>(body);\n\t\tprintCompanyResult(result.company, ctx.format);\n\t} catch (err) {\n\t\tif (!needsBasic) {\n\t\t\t// descriptions-only 送出失敗:沒有「basic 已成功」需要保留,正常拋出讓頂層印 err.message。\n\t\t\tthrow err;\n\t\t}\n\t\t// basic 已成功、descriptions 這次呼叫失敗:兩種 sub-case(brief 要求都要保留輸出):\n\t\t// (a) descriptions 呼叫整段失敗(網路錯誤、或非 partial 的 4xx,body 沒有 updated_sections/failed_section)\n\t\t// (b) descriptions 端點自己內部 partial-failure(422,body 帶 updated_sections/failed_section)\n\t\tconst descriptionsBody = err instanceof ServerClientHttpError ? err.body : undefined;\n\t\tconst partial = isDescriptionsPartialFailureBody(descriptionsBody) ? descriptionsBody : undefined;\n\n\t\tif (ctx.format === 'json') {\n\t\t\tprintJson({\n\t\t\t\tbasic_updated: true,\n\t\t\t\tbasic_company: basicResultCompany,\n\t\t\t\tdescriptions_error: partial\n\t\t\t\t\t? { updated_sections: partial.updated_sections, failed_section: partial.failed_section }\n\t\t\t\t\t: { message: err instanceof Error ? err.message : String(err) },\n\t\t\t});\n\t\t} else {\n\t\t\tprocess.stdout.write('Basic company info was updated successfully.\\n');\n\t\t\tif (partial) {\n\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t`Updated sections before failure: ${(partial.updated_sections ?? []).join(', ')}; failed section: ${partial.failed_section}\\n`\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t`Descriptions update failed entirely: ${err instanceof Error ? err.message : String(err)}\\n`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthrow err;\n\t}\n}\n\ninterface UpdateFlags {\n\tfile?: string;\n\tidempotencyKey?: string;\n\tbasicIdempotencyKey?: string;\n\tdescriptionsIdempotencyKey?: string;\n}\n\n/**\n * `company update` 註冊(pm_41 Card B Task 3)。\n */\nexport function registerEnterpriseCompanyUpdate(parent: Command): void {\n\tparent\n\t\t.command('update')\n\t\t.description(\n\t\t\t'Update your company basic info and/or descriptions from a JSON file ' +\n\t\t\t\t'(uniform_number is read-only and cannot be changed via this command)'\n\t\t)\n\t\t.requiredOption('--file <path>', 'path to a partial JSON company body, or \"-\" for stdin')\n\t\t.option('--idempotency-key <key>', 'single-section update only: reuse across retries (default: a fresh UUID)')\n\t\t.option('--basic-idempotency-key <key>', 'two-section update: idempotency key for PATCH /company/basic')\n\t\t.option(\n\t\t\t'--descriptions-idempotency-key <key>',\n\t\t\t'two-section update: idempotency key for PATCH /company/descriptions'\n\t\t)\n\t\t.action(async (flags: UpdateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runCompanyUpdate(\n\t\t\t\tctx,\n\t\t\t\tkey,\n\t\t\t\tflags.file as string,\n\t\t\t\t{\n\t\t\t\t\tidempotencyKey: flags.idempotencyKey,\n\t\t\t\t\tbasicIdempotencyKey: flags.basicIdempotencyKey,\n\t\t\t\t\tdescriptionsIdempotencyKey: flags.descriptionsIdempotencyKey,\n\t\t\t\t},\n\t\t\t\t{ timeoutMs: ctx.timeoutMs }\n\t\t\t);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { REQUIRED_BASIC_FIELDS, CONTRACT_REQUIRED_GET_FIELDS };\n","/**\n * 企業公司資訊型別與欄位分組(pm_41 Card B Task 0)。\n * 欄位對齊 server EnterpriseCompanyVm(enterprise-company.vm.ts,pm_41 Card A CO-1)。\n */\nexport interface EnterpriseCompany {\n\tname?: string;\n\tuniform_number?: string | null;\n\tstatus?: number;\n\tlogo_url?: string | null;\n\tdescription?: string | null;\n\tproducts_services?: string | null;\n\tlatest_news?: string | null;\n\tcustom_welfare?: string | null;\n\tcapital_amount?: number | null;\n\tcapital_show_status?: number | null;\n\tphone_code?: string | null;\n\tphone_number?: string | null;\n\twebsite?: string | null;\n\tarea_code?: string | null;\n\taddress?: string | null;\n\tindustry_category_code?: string | null;\n\temployee_count_range_code?: string | null;\n\t[k: string]: unknown;\n}\n\n/** `PATCH /company/basic` 欄位(6 必填 + 4 選填,`uniform_number` 唯讀不在此列)。 */\nexport const BASIC_FIELDS: ReadonlyArray<string> = [\n\t'name',\n\t'industry_category_code',\n\t'phone_code',\n\t'phone_number',\n\t'employee_count_range_code',\n\t'capital_amount',\n\t'capital_show_status',\n\t'website',\n\t'area_code',\n\t'address',\n];\n\n/** `PATCH /company/descriptions` 欄位(3 個各自獨立選填,非 atomic)。 */\nexport const DESCRIPTION_FIELDS: ReadonlyArray<string> = ['description', 'products_services', 'latest_news'];\n\n/**\n * 本地擋 CLI 輸入用的禁止欄位清單:`uniform_number`(唯讀,BR-024)+ pm_38 v1.x 欄位\n * 的 placeholder 名稱(banner / 3 照片 / 3 影片 / capital_show_status 以外的顯示 flags /\n * 設立日期 / 負責人 / 董監事 / 里程碑 / 得獎 / QA)。\n *\n * ⚠️ 這些 placeholder 名稱目前在任何 spec 都不存在對應欄位(entity 尚無這些欄位,\n * Card A VM 本就白名單排除,見 enterprise-company.vm.ts 檔案註解)。此清單是\n * best-effort 提前擋,不是保證完整清單;pm_38 v1.x 正式定案欄位名稱後需回頭校正。\n */\nexport const FORBIDDEN_FIELDS: ReadonlyArray<string> = [\n\t'uniform_number',\n\t'banner_url',\n\t'photo_1',\n\t'photo_2',\n\t'photo_3',\n\t'video_1',\n\t'video_2',\n\t'video_3',\n\t'foundation_date',\n\t'representative',\n\t'directors',\n\t'milestones',\n\t'awards',\n\t'qa',\n];\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { extname } from 'node:path';\nimport { fetchWithTimeout, unwrapDataResponse } from '../../../lib/api-client';\nimport { InvalidArgumentError, NetworkError } from '../../../lib/errors';\nimport { enterprisePost } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { printJson } from '../../../lib/output';\nimport type { WriteCtx } from '../jobs/write-shared';\nimport type { EnterpriseCompany } from './types';\n\n/**\n * 允許的 logo 副檔名 → MIME type(鏡射後端\n * `ALLOWED_FILE_TYPES.COMPANY_LOGO.MIME_TYPES`,src/common/constants/file-upload.constants.ts)。\n * 這支 CLI 沒有任何 MIME-detection 套件依賴(package.json 僅有 cli-table3/commander/env-paths/\n * openapi-fetch/picocolors),為了 3 筆對應關係不值得新增依賴,改用 `path.extname()` 做本地判斷。\n */\nconst EXTENSION_TO_CONTENT_TYPE: Record<string, string> = {\n\t'.png': 'image/png',\n\t'.jpg': 'image/jpeg',\n\t'.jpeg': 'image/jpeg',\n};\n\n/** 後端 `FILE_UPLOAD_LIMITS.COMPANY_LOGO_MAX_SIZE`(2MB)。 */\nconst COMPANY_LOGO_MAX_SIZE = 2 * 1024 * 1024;\n\ninterface LocalFileInfo {\n\tcontentType: string;\n\tfileSize: number;\n\tbytes: Buffer;\n}\n\n/**\n * 本地檔案檢查(存在/是檔案/副檔名合法/大小合法),全部在任何網路呼叫之前完成。\n *\n * 大小下限採 `size > 0`(拒絕 0 byte 檔案),對齊後端 DTO 的 `@Min(1)`(見\n * `EnterpriseCompanyLogoPresignDto.file_size`)——brief 寫「1..2MB」不是字面上要求至少\n * 1MB,而是「大於 0、最多 2MB」。\n */\nfunction inspectLocalFile(path: string): LocalFileInfo {\n\tif (!existsSync(path)) {\n\t\tthrow new InvalidArgumentError(`File not found: ${path}`);\n\t}\n\tconst stat = statSync(path);\n\tif (!stat.isFile()) {\n\t\tthrow new InvalidArgumentError(`Not a regular file: ${path}`);\n\t}\n\n\tconst ext = extname(path).toLowerCase();\n\tconst contentType = EXTENSION_TO_CONTENT_TYPE[ext];\n\tif (!contentType) {\n\t\tthrow new InvalidArgumentError(\n\t\t\t`Unsupported file extension \"${ext || '(none)'}\" — allowed: ${Object.keys(EXTENSION_TO_CONTENT_TYPE).join(', ')}`\n\t\t);\n\t}\n\n\tif (stat.size <= 0) {\n\t\tthrow new InvalidArgumentError(`File is empty: ${path}`);\n\t}\n\tif (stat.size > COMPANY_LOGO_MAX_SIZE) {\n\t\tthrow new InvalidArgumentError(`File too large: ${stat.size} bytes (max ${COMPANY_LOGO_MAX_SIZE} bytes / 2MB)`);\n\t}\n\n\tconst bytes = readFileSync(path);\n\treturn { contentType, fileSize: stat.size, bytes };\n}\n\ninterface LogoPresignResponse {\n\tupload_url: string;\n\ts3_key: string;\n\texpires_in: number;\n}\n\ninterface LogoConfirmResponse {\n\tlogo_url: string;\n\tcompany: EnterpriseCompany;\n}\n\nexport interface LogoUploadIdempotencyFlags {\n\t/**\n\t * 基底 key;presign/confirm 各自衍生獨立實際 key(`${base}-presign` / `${base}-confirm`),\n\t * 不直接把同一個字面值重複用在兩次呼叫上——即使 presign→PUT→confirm 是同一次使用者操作,\n\t * 兩次 POST 打的是不同 endpoint、不同 payload,沿用 Task 3 review 對「同一把 key 打兩個不同\n\t * payload」的疑慮(見 update.ts `resolveUpdateIdempotencyKeys` 的判斷)。不給旗標則各自\n\t * fresh `randomUUID()`。\n\t */\n\tidempotencyKey?: string;\n}\n\nfunction resolveLogoIdempotencyKeys(flags: LogoUploadIdempotencyFlags): { presignKey: string; confirmKey: string } {\n\tif (flags.idempotencyKey) {\n\t\treturn {\n\t\t\tpresignKey: `${flags.idempotencyKey}-presign`,\n\t\t\tconfirmKey: `${flags.idempotencyKey}-confirm`,\n\t\t};\n\t}\n\treturn { presignKey: randomUUID(), confirmKey: randomUUID() };\n}\n\n/** 印出最終結果:JSON 印整份 confirm 回應(`{ logo_url, company }`);文字只印一行。 */\nfunction printLogoResult(result: LogoConfirmResponse, format: 'table' | 'json'): void {\n\tif (format === 'json') {\n\t\tprintJson(result);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Updated logo: ${result.logo_url}\\n`);\n}\n\n/**\n * `company logo upload <path>` 核心(pm_41 Card B Task 4):\n * 本地檔案檢查 → `POST /company/logo/presign` → PUT bytes 直傳 S3 presigned URL →\n * `POST /company/logo/confirm` → 印結果。\n *\n * S3 PUT 刻意不經過 `enterprisePost`/`enterpriseGet`/`enterprisePatch`:那組 helper 是打\n * `/api/v1/enterprise` API server,presigned URL 是完全不同的 host(S3 bucket),改用\n * `fetchWithTimeout`(`api-client.ts`)直接組 `Request` 送出,檔案位元組完全不進 API server。\n */\nexport async function runCompanyLogoUpload(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tpath: string,\n\tidempotencyFlags: LogoUploadIdempotencyFlags\n): Promise<void> {\n\tconst { contentType, fileSize, bytes } = inspectLocalFile(path);\n\tconst { presignKey, confirmKey } = resolveLogoIdempotencyKeys(idempotencyFlags);\n\n\tconst requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };\n\n\tconst { body: presignBody } = await enterprisePost(\n\t\trequestOpts,\n\t\t'/company/logo/presign',\n\t\t{ content_type: contentType, file_size: fileSize },\n\t\t{ idempotencyKey: presignKey }\n\t);\n\tconst presign = unwrapDataResponse<LogoPresignResponse>(presignBody);\n\n\tconst putRequest = new Request(presign.upload_url, {\n\t\tmethod: 'PUT',\n\t\theaders: { 'Content-Type': contentType },\n\t\tbody: bytes,\n\t});\n\tconst putResponse = await fetchWithTimeout(putRequest, ctx.timeoutMs);\n\tif (!putResponse.ok) {\n\t\t// S3 上傳失敗不是我們自己 API 的 4xx(那走 ServerClientHttpError/exit 3),也不是單純的\n\t\t// DNS/timeout 網路錯(那走 fetchWithTimeout 內建分類);歸類為 NetworkError(exit 4)——\n\t\t// 對呼叫端而言同樣是「上游基礎設施沒能完成」,且與 unwrapDataResponse 的\n\t\t// ServerOrNetworkError 用途一致(後端契約 / 上游依賴異常,非使用者輸入錯)。\n\t\tthrow new NetworkError(`Failed to upload file to S3: HTTP ${putResponse.status}`);\n\t}\n\n\tconst { body: confirmBody } = await enterprisePost(\n\t\trequestOpts,\n\t\t'/company/logo/confirm',\n\t\t{ s3_key: presign.s3_key },\n\t\t{ idempotencyKey: confirmKey }\n\t);\n\tconst result = unwrapDataResponse<LogoConfirmResponse>(confirmBody);\n\tprintLogoResult(result, ctx.format);\n}\n\ninterface LogoUploadFlags {\n\tidempotencyKey?: string;\n}\n\n/**\n * `company logo` 註冊(pm_41 Card B Task 4)。\n *\n * `logo` 本身是命令群組(非直接掛 `<file>` 引數),底下掛 `upload <path>` 子命令,對齊\n * plan 的字面命令介面 `wport enterprise company logo upload <path>`(Task 0 scaffolding\n * 原本把 `logo` 寫成直接吃 `<file>` 引數,這裡改成群組 + 子命令,做法比照\n * `jobs`:group + 多個子命令,見 `../jobs/index.ts`)。目前只有一個子命令,故直接在本檔\n * inline 註冊,不另開 `logo/` 目錄。\n */\nexport function registerEnterpriseCompanyLogo(parent: Command): void {\n\tconst logo = parent.command('logo').description('Manage your company logo');\n\tlogo\n\t\t.command('upload <path>')\n\t\t.description('Upload and set your company logo (presign + upload + confirm)')\n\t\t.option('--idempotency-key <key>', 'base key for presign/confirm (each derives its own; default: fresh UUIDs)')\n\t\t.action(async (path: string, flags: LogoUploadFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runCompanyLogoUpload(ctx, key, path, { idempotencyKey: flags.idempotencyKey });\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { inspectLocalFile, resolveLogoIdempotencyKeys };\n","import type { Command } from 'commander';\nimport { registerEnterpriseCompanyView } from './view';\nimport { registerEnterpriseCompanyUpdate } from './update';\nimport { registerEnterpriseCompanyLogo } from './logo';\n\nexport function registerEnterpriseCompanyCommand(parent: Command): void {\n\tconst company = parent.command('company').description('View and manage your company information');\n\tregisterEnterpriseCompanyView(company);\n\tregisterEnterpriseCompanyUpdate(company);\n\tregisterEnterpriseCompanyLogo(company);\n}\n","// apps/cli/src/commands/enterprise/talents/list.ts\nimport type { Command } from 'commander';\nimport { asPaginatedBody } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { dim, printJson, printTable, type OutputFormat } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\n\ninterface ListFlags {\n\ttab?: string;\n\tjob?: string;\n\tkeyword?: string;\n\tfrom?: string;\n\tto?: string;\n\tpage?: number;\n\tpageSize?: number;\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/** PRD §6.5:未帶 -s 時預設 page-size = 20(後端 PaginationDto 預設 10,故 CLI 顯式帶)。 */\nconst DEFAULT_PAGE_SIZE = 20;\n\nconst MINIMAL_LIST_FIELDS = ['enc_resume_id', 'candidate_name', 'applied_job_title', 'applied_at'];\n\n/** 欄位對齊 server EnterpriseTalentListVm。PII-safe:無任何聯絡方式欄位;禁止 spread 未知欄位進表格。 */\ninterface EnterpriseTalentItem {\n\tenc_resume_id?: string;\n\tcandidate_name?: string | null;\n\thighest_education?: string | null;\n\tlatest_experience?: string | null;\n\tapplied_job_title?: string | null;\n\tapplied_at?: string | null;\n\tis_viewed?: boolean;\n\t[k: string]: unknown;\n}\n\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\nexport async function runEnterpriseTalentsList(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat; color: boolean },\n\tapiKey: string,\n\tflags: ListFlags\n): Promise<void> {\n\tif (flags.fields && flags.minimal) {\n\t\tthrow new CliError('Use either --fields or --minimal, not both', ExitCode.InvalidArgument);\n\t}\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/talents',\n\t\t{\n\t\t\ttab: flags.tab,\n\t\t\tenc_job_id: flags.job,\n\t\t\tkeyword: flags.keyword,\n\t\t\tstart_date: flags.from,\n\t\t\tend_date: flags.to,\n\t\t\tcurrentPage: flags.page,\n\t\t\tpageSize: flags.pageSize ?? DEFAULT_PAGE_SIZE,\n\t\t}\n\t);\n\tconst paged = asPaginatedBody<EnterpriseTalentItem>(body);\n\n\tconst projection = flags.minimal ? MINIMAL_LIST_FIELDS : flags.fields ? parseFieldsList(flags.fields) : undefined;\n\tif (projection || ctx.format === 'json') {\n\t\tprintJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tpaged.data,\n\t\t[\n\t\t\t{ header: 'RESUME_ID', value: (r) => (r.enc_resume_id ?? '').slice(0, 14) },\n\t\t\t{ header: 'CANDIDATE', value: (r) => r.candidate_name ?? '', maxWidth: 20 },\n\t\t\t{ header: 'EDUCATION', value: (r) => r.highest_education ?? '', maxWidth: 18 },\n\t\t\t{ header: 'EXPERIENCE', value: (r) => r.latest_experience ?? '', maxWidth: 28 },\n\t\t\t{ header: 'APPLIED_JOB', value: (r) => r.applied_job_title ?? '', maxWidth: 24 },\n\t\t\t{ header: 'APPLIED_AT', value: (r) => formatDate(r.applied_at), maxWidth: 12 },\n\t\t\t{ header: 'VIEWED', value: (r) => (r.is_viewed ? 'yes' : 'no') },\n\t\t],\n\t\tctx.color\n\t);\n\tconst head = `Showing page ${paged.currentPage}/${paged.totalPages} (${paged.data.length} of ${paged.totalCount} applicants).`;\n\tconst hint =\n\t\tpaged.totalPages > paged.currentPage ? ` Next: wport enterprise talents list --page ${paged.currentPage + 1}` : '';\n\tprocess.stdout.write(dim(head + hint, ctx.color) + '\\n');\n}\n\nexport function registerEnterpriseTalentsList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List applicants in your talent pool')\n\t\t.option('--tab <tab>', 'applied | visit (default applied; visit is not yet supported by the server)')\n\t\t.option('--job <enc_job_id>', 'filter by job posting enc_id')\n\t\t.option('-k, --keyword <kw>', 'filter by candidate name keyword')\n\t\t.option('--from <date>', 'applied on/after this date (YYYY-MM-DD)')\n\t\t.option('--to <date>', 'applied on/before this date (YYYY-MM-DD)')\n\t\t.option('-p, --page <n>', 'page number (server: currentPage, default 1)', (v) => Number(v))\n\t\t.option('-s, --page-size <n>', `items per page (default ${DEFAULT_PAGE_SIZE}, max 100)`, (v) => Number(v))\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.option('--minimal', `output only ${MINIMAL_LIST_FIELDS.join(',')} as JSON`)\n\t\t.action(async (flags: ListFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runEnterpriseTalentsList(ctx, key, flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { DEFAULT_PAGE_SIZE, formatDate };\n","// apps/cli/src/commands/enterprise/talents/view.ts\nimport type { Command } from 'commander';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\n\ninterface ViewFlags {\n\tfields?: string;\n}\n\n/** 履歷預覽為巢狀物件(後端 getFindTalentResumePreview 輸出)→ 一律 JSON(不做表格)。 */\nexport async function runEnterpriseTalentsView(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number },\n\tapiKey: string,\n\tencResumeId: string,\n\tflags: ViewFlags\n): Promise<void> {\n\tconst trimmed = encResumeId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_resume_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/talents/${encodeURIComponent(trimmed)}`\n\t);\n\tconst resume = unwrapDataResponse<Record<string, unknown>>(body);\n\tif (flags.fields) {\n\t\tprintJson(pickPaths(resume, parseFieldsList(flags.fields)));\n\t\treturn;\n\t}\n\tprintJson(resume);\n}\n\nexport function registerEnterpriseTalentsView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_resume_id>')\n\t\t.description('View one applicant resume preview (company-scoped; PII visibility per access level)')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.action(async (encResumeId: string, flags: ViewFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runEnterpriseTalentsView(ctx, key, encResumeId, flags);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePost } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { readTextInput } from '../../../lib/io-helpers';\nimport { printJson, type OutputFormat } from '../../../lib/output';\n\ninterface RespondFlags {\n\tsubject?: string;\n\tbody?: string;\n\tbodyFile?: string;\n\tidempotencyKey?: string;\n}\n\n/** respond 回應:後端回 { enc_resume_id, sent_at }(不含信件內容 / 求職者聯絡方式)。 */\ninterface RespondResult {\n\tenc_resume_id?: string;\n\tsent_at?: string;\n\t[k: string]: unknown;\n}\n\n/**\n * 信件內文來源:--body / --body-file 擇一必填、互斥(both 或 neither → exit 2、不發請求)。\n * --body-file 讀檔 / stdin 純文字(readTextInput,內含空輸入檢查)。\n */\nexport function resolveRespondBody(\n\tflags: { body?: string; bodyFile?: string },\n\toptions: { timeoutMs?: number } = {}\n): string {\n\tconst hasBody = flags.body !== undefined;\n\tconst hasBodyFile = flags.bodyFile !== undefined;\n\tif (hasBody === hasBodyFile) {\n\t\tthrow new CliError('Provide exactly one of --body or --body-file', ExitCode.InvalidArgument);\n\t}\n\tif (hasBody) {\n\t\tif (!flags.body!.trim()) throw new CliError('--body must not be empty', ExitCode.InvalidArgument);\n\t\treturn flags.body!;\n\t}\n\treturn readTextInput(flags.bodyFile!, { timeoutMs: options.timeoutMs });\n}\n\nexport async function runEnterpriseTalentsRespond(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat },\n\tapiKey: string,\n\tencResumeId: string,\n\tflags: RespondFlags,\n\tidempotencyKey: string\n): Promise<void> {\n\tconst trimmedId = encResumeId.trim();\n\tif (!trimmedId) {\n\t\tthrow new CliError('enc_resume_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\tconst subject = (flags.subject ?? '').trim();\n\tif (!subject) {\n\t\tthrow new CliError('--subject is required and must not be empty', ExitCode.InvalidArgument);\n\t}\n\tconst body = resolveRespondBody(flags, { timeoutMs: ctx.timeoutMs });\n\n\tconst { body: respBody } = await enterprisePost(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/talents/${encodeURIComponent(trimmedId)}/respond`,\n\t\t{ subject, body },\n\t\t{ idempotencyKey }\n\t);\n\tconst result = unwrapDataResponse<RespondResult>(respBody);\n\tif (ctx.format === 'json') {\n\t\tprintJson(result);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Replied to ${result.enc_resume_id ?? trimmedId}; sent at ${result.sent_at ?? '(unknown)'}.\\n`);\n}\n\nexport function registerEnterpriseTalentsRespond(parent: Command): void {\n\tparent\n\t\t.command('respond <enc_resume_id>')\n\t\t.description('Reply to an applicant (in-app message; inherits the company daily reply quota)')\n\t\t.requiredOption('--subject <subject>', 'message subject (max 200)')\n\t\t.option('--body <text>', 'message body text (max 5000)')\n\t\t.option('--body-file <path>', 'read message body from a file, or \"-\" for stdin')\n\t\t.option('--idempotency-key <key>', 'reuse across retries to avoid duplicate sends (default: a fresh UUID)')\n\t\t.action(async (encResumeId: string, flags: RespondFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runEnterpriseTalentsRespond(ctx, key, encResumeId, flags, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { resolveRespondBody };\n","// apps/cli/src/commands/enterprise/talents/index.ts\nimport type { Command } from 'commander';\nimport { registerEnterpriseTalentsList } from './list';\nimport { registerEnterpriseTalentsView } from './view';\nimport { registerEnterpriseTalentsRespond } from './respond';\n\nexport function registerEnterpriseTalentsCommand(parent: Command): void {\n\tconst talents = parent.command('talents').description('Browse and respond to applicants in your talent pool');\n\tregisterEnterpriseTalentsList(talents);\n\tregisterEnterpriseTalentsView(talents);\n\tregisterEnterpriseTalentsRespond(talents);\n}\n","// apps/cli/src/commands/enterprise/campaigns/create.ts\nimport type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePost } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { printJson } from '../../../lib/output';\nimport type { WriteCtx } from '../jobs/write-shared';\n\ninterface CreateFlags {\n\tfile?: string;\n\tidempotencyKey?: string;\n}\n\n/** create/update 回應:後端回含 { enc_id } 的 campaign VM。 */\nexport interface CreatedCampaign {\n\tenc_id?: string;\n\t[k: string]: unknown;\n}\n\n/**\n * `campaigns create` 核心:讀 --file/stdin JSON → POST /campaigns(201)→ 回含 enc_id 的結果。\n * body 直送後端 CreateCampaignDto 驗證;enc_job_ids 由後端 controller 解密(CLI 不碰)。\n */\nexport async function runCampaignCreate(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tsource: string,\n\tidempotencyKey: string\n): Promise<void> {\n\tconst campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });\n\tconst { body } = await enterprisePost(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/campaigns',\n\t\tcampaignBody,\n\t\t{ idempotencyKey }\n\t);\n\tconst created = unwrapDataResponse<CreatedCampaign>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(created);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Created campaign: ${created.enc_id ?? ''}\\n`);\n}\n\nexport function registerEnterpriseCampaignsCreate(parent: Command): void {\n\tparent\n\t\t.command('create')\n\t\t.description('Create a recruitment campaign from a JSON file (use \"-\" to read stdin)')\n\t\t.requiredOption('--file <path>', 'path to a JSON campaign body, or \"-\" for stdin')\n\t\t.option('--idempotency-key <key>', 'reuse across retries to avoid duplicate creates (default: a fresh UUID)')\n\t\t.action(async (flags: CreateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runCampaignCreate(ctx, key, flags.file as string, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n","// apps/cli/src/commands/enterprise/campaigns/list.ts\nimport type { Command } from 'commander';\nimport { asPaginatedBody } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { dim, printJson, printTable, type OutputFormat } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\n\ninterface ListFlags {\n\tpage?: number;\n\tpageSize?: number;\n\tkeyword?: string;\n\tstatus?: string;\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/** Server QueryCampaignsDto.status: number(0=關閉, 1=開啟)。CLI flag open/closed 映射。 */\nconst STATUS_MAP: Record<string, number> = { open: 1, closed: 0 };\n\nconst MINIMAL_LIST_FIELDS = ['enc_id', 'name', 'status', 'job_count'];\n\n/** 欄位對齊 server CampaignListItemVm。 */\ninterface CampaignItem {\n\tenc_id?: string;\n\tname?: string | null;\n\tslug?: string | null;\n\tstatus?: number;\n\tpv?: number | null;\n\tvisitors_count?: number | null;\n\tjob_count?: number | null;\n\tis_all_selected?: boolean;\n\tlanding_url?: string | null;\n\t[k: string]: unknown;\n}\n\nfunction mapStatusFlag(raw: string | undefined): number | undefined {\n\tif (raw === undefined) return undefined;\n\tif (Object.prototype.hasOwnProperty.call(STATUS_MAP, raw)) return STATUS_MAP[raw];\n\tthrow new CliError(`Invalid --status \"${raw}\". Allowed: ${Object.keys(STATUS_MAP).join(', ')}`, ExitCode.InvalidArgument);\n}\n\nexport function formatStatus(status: number | undefined): string {\n\tif (status === 1) return 'open';\n\tif (status === 0) return 'closed';\n\treturn status === undefined ? '' : String(status);\n}\n\nfunction formatCount(value: number | null | undefined): string {\n\treturn typeof value === 'number' && Number.isFinite(value) ? String(value) : '—';\n}\n\nexport async function runEnterpriseCampaignsList(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number; format: OutputFormat; color: boolean },\n\tapiKey: string,\n\tflags: ListFlags\n): Promise<void> {\n\tif (flags.fields && flags.minimal) {\n\t\tthrow new CliError('Use either --fields or --minimal, not both', ExitCode.InvalidArgument);\n\t}\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t'/campaigns',\n\t\t{\n\t\t\tcurrentPage: flags.page,\n\t\t\tpageSize: flags.pageSize,\n\t\t\tkeyword: flags.keyword,\n\t\t\tstatus: mapStatusFlag(flags.status),\n\t\t}\n\t);\n\tconst paged = asPaginatedBody<CampaignItem>(body);\n\n\tconst projection = flags.minimal ? MINIMAL_LIST_FIELDS : flags.fields ? parseFieldsList(flags.fields) : undefined;\n\tif (projection || ctx.format === 'json') {\n\t\tprintJson(projection ? { ...paged, data: paged.data.map((row) => pickPaths(row, projection)) } : paged);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tpaged.data,\n\t\t[\n\t\t\t{ header: 'ENC_ID', value: (r) => (r.enc_id ?? '').slice(0, 14) },\n\t\t\t{ header: 'NAME', value: (r) => r.name ?? '', maxWidth: 32 },\n\t\t\t{ header: 'STATUS', value: (r) => formatStatus(r.status) },\n\t\t\t{ header: 'JOBS', value: (r) => formatCount(r.job_count) },\n\t\t\t{ header: 'PV', value: (r) => formatCount(r.pv) },\n\t\t\t{ header: 'VISITORS', value: (r) => formatCount(r.visitors_count) },\n\t\t],\n\t\tctx.color\n\t);\n\tconst head = `Showing page ${paged.currentPage}/${paged.totalPages} (${paged.data.length} of ${paged.totalCount} campaigns).`;\n\tconst hint =\n\t\tpaged.totalPages > paged.currentPage ? ` Next: wport enterprise campaigns list --page ${paged.currentPage + 1}` : '';\n\tprocess.stdout.write(dim(head + hint, ctx.color) + '\\n');\n}\n\nexport function registerEnterpriseCampaignsList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your recruitment campaigns')\n\t\t.option('-p, --page <n>', 'page number (server: currentPage, default 1)', (v) => Number(v))\n\t\t.option('-s, --page-size <n>', 'items per page (default 10, max 100)', (v) => Number(v))\n\t\t.option('-k, --keyword <kw>', 'filter by campaign name keyword')\n\t\t.option('--status <state>', 'filter by status: open | closed')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.option('--minimal', `output only ${MINIMAL_LIST_FIELDS.join(',')} as JSON`)\n\t\t.action(async (flags: ListFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runEnterpriseCampaignsList(ctx, key, flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { mapStatusFlag, formatStatus, formatCount };\n","// apps/cli/src/commands/enterprise/campaigns/lifecycle.ts\nimport type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePatch } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { printJson } from '../../../lib/output';\nimport { requireEncId, type WriteCtx } from '../jobs/write-shared';\nimport type { CreatedCampaign } from './create';\n\ninterface LifecycleFlags {\n\tidempotencyKey?: string;\n}\n\n/**\n * publish / unpublish 共用:PATCH /campaigns/:enc_id/{action}(空 body,帶 Idempotency-Key)。\n * 可逆操作 → 不需 --confirm(有別於 jobs delete/close)。publish 超過 10 active 時後端回 400 → 透傳。\n */\nexport async function runCampaignTransition(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tencId: string,\n\taction: 'publish' | 'unpublish',\n\tidempotencyKey: string\n): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst { body } = await enterprisePatch(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/campaigns/${encodeURIComponent(trimmed)}/${action}`,\n\t\t{},\n\t\t{ idempotencyKey }\n\t);\n\tconst result = unwrapDataResponse<CreatedCampaign>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(result);\n\t\treturn;\n\t}\n\tconst verb = action === 'publish' ? 'Published' : 'Unpublished';\n\tprocess.stdout.write(`${verb} campaign: ${result.enc_id ?? trimmed}\\n`);\n}\n\nexport function registerEnterpriseCampaignsPublish(parent: Command): void {\n\tparent\n\t\t.command('publish <enc_id>')\n\t\t.description('Publish (activate) a recruitment campaign')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: LifecycleFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runCampaignTransition(ctx, key, encId, 'publish', flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n\nexport function registerEnterpriseCampaignsUnpublish(parent: Command): void {\n\tparent\n\t\t.command('unpublish <enc_id>')\n\t\t.description('Unpublish (deactivate) a recruitment campaign')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: LifecycleFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst { key } = resolveApiKey((command.optsWithGlobals() as { apiKey?: string }).apiKey);\n\t\t\tawait runCampaignTransition(ctx, key, encId, 'unpublish', flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n","// apps/cli/src/commands/enterprise/campaigns/update.ts\nimport type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterprisePatch } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { printJson } from '../../../lib/output';\nimport { requireEncId, type WriteCtx } from '../jobs/write-shared';\nimport type { CreatedCampaign } from './create';\n\ninterface UpdateFlags {\n\tfile?: string;\n\tidempotencyKey?: string;\n}\n\n/**\n * `campaigns update` 核心:讀 --file/stdin JSON → PATCH /campaigns/:enc_id(200)。\n * body 直送後端 UpdateCampaignDto 驗證;enc_job_ids 由後端解密。\n */\nexport async function runCampaignUpdate(\n\tctx: WriteCtx,\n\tapiKey: string,\n\tencId: string,\n\tsource: string,\n\tidempotencyKey: string\n): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst campaignBody = readJsonObject(source, { timeoutMs: ctx.timeoutMs });\n\tconst { body } = await enterprisePatch(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/campaigns/${encodeURIComponent(trimmed)}`,\n\t\tcampaignBody,\n\t\t{ idempotencyKey }\n\t);\n\tconst updated = unwrapDataResponse<CreatedCampaign>(body);\n\tif (ctx.format === 'json') {\n\t\tprintJson(updated);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Updated campaign: ${updated.enc_id ?? trimmed}\\n`);\n}\n\nexport function registerEnterpriseCampaignsUpdate(parent: Command): void {\n\tparent\n\t\t.command('update <enc_id>')\n\t\t.description('Update a recruitment campaign from a JSON file (use \"-\" to read stdin)')\n\t\t.requiredOption('--file <path>', 'path to a partial JSON campaign body, or \"-\" for stdin')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (encId: string, flags: UpdateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runCampaignUpdate(ctx, key, encId, flags.file as string, flags.idempotencyKey ?? randomUUID());\n\t\t});\n}\n","// apps/cli/src/commands/enterprise/campaigns/view.ts\nimport type { Command } from 'commander';\nimport { unwrapDataResponse } from '../../../lib/api-client';\nimport { enterpriseGet } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\n\ninterface ViewFlags {\n\tfields?: string;\n}\n\n/** campaign 詳情含職缺清單(巢狀)→ 一律 JSON(不做表格)。 */\nexport async function runEnterpriseCampaignsView(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number },\n\tapiKey: string,\n\tencId: string,\n\tflags: ViewFlags\n): Promise<void> {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\tconst { body } = await enterpriseGet(\n\t\t{ baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey },\n\t\t`/campaigns/${encodeURIComponent(trimmed)}`\n\t);\n\tconst campaign = unwrapDataResponse<Record<string, unknown>>(body);\n\tif (flags.fields) {\n\t\tprintJson(pickPaths(campaign, parseFieldsList(flags.fields)));\n\t\treturn;\n\t}\n\tprintJson(campaign);\n}\n\nexport function registerEnterpriseCampaignsView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_id>')\n\t\t.description('View one recruitment campaign (with its jobs)')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths)')\n\t\t.action(async (encId: string, flags: ViewFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tconst globals = command.optsWithGlobals() as { apiKey?: string };\n\t\t\tconst { key } = resolveApiKey(globals.apiKey);\n\t\t\tawait runEnterpriseCampaignsView(ctx, key, encId, flags);\n\t\t});\n}\n","// apps/cli/src/commands/enterprise/campaigns/index.ts\nimport type { Command } from 'commander';\nimport { registerEnterpriseCampaignsCreate } from './create';\nimport { registerEnterpriseCampaignsList } from './list';\nimport { registerEnterpriseCampaignsPublish, registerEnterpriseCampaignsUnpublish } from './lifecycle';\nimport { registerEnterpriseCampaignsUpdate } from './update';\nimport { registerEnterpriseCampaignsView } from './view';\n\nexport function registerEnterpriseCampaignsCommand(parent: Command): void {\n\tconst campaigns = parent.command('campaigns').description('Manage your recruitment campaigns');\n\tregisterEnterpriseCampaignsList(campaigns);\n\tregisterEnterpriseCampaignsView(campaigns);\n\tregisterEnterpriseCampaignsCreate(campaigns);\n\tregisterEnterpriseCampaignsUpdate(campaigns);\n\tregisterEnterpriseCampaignsPublish(campaigns);\n\tregisterEnterpriseCampaignsUnpublish(campaigns);\n}\n","import type { Command } from 'commander';\nimport { registerEnterpriseLogin } from './login';\nimport { registerEnterpriseLogout } from './logout';\nimport { registerEnterpriseWhoami } from './whoami';\nimport { registerEnterpriseUsage } from './usage';\nimport { registerEnterpriseJobsCommand } from './jobs';\nimport { registerEnterpriseKeysCommand } from './keys';\nimport { registerEnterpriseCompanyCommand } from './company';\nimport { registerEnterpriseTalentsCommand } from './talents';\nimport { registerEnterpriseCampaignsCommand } from './campaigns';\n\nexport function registerEnterpriseCommand(program: Command): void {\n\tconst enterprise = program\n\t\t.command('enterprise')\n\t\t.description('Manage your company job postings with an enterprise API key')\n\t\t.option('--api-key <key>', 'one-off API key (prefer \"wport enterprise login\" or the WPORT_API_KEY env var)');\n\tregisterEnterpriseLogin(enterprise);\n\tregisterEnterpriseLogout(enterprise);\n\tregisterEnterpriseWhoami(enterprise);\n\tregisterEnterpriseUsage(enterprise);\n\tregisterEnterpriseJobsCommand(enterprise);\n\tregisterEnterpriseKeysCommand(enterprise);\n\tregisterEnterpriseCompanyCommand(enterprise);\n\tregisterEnterpriseTalentsCommand(enterprise);\n\tregisterEnterpriseCampaignsCommand(enterprise);\n}\n"],"mappings":"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","names":["pc","Table","import_node_fs","createClient","envPaths","paths","import_node_fs","import_picocolors","paths","s","pc","program","import_node_fs","program","import_node_fs","program","import_node_fs","import_node_path","import_env_paths","paths","envPaths","formatDate","import_node_crypto","import_node_crypto","import_node_crypto","formatDate","DETAIL_FIELDS","renderDetailLines","import_node_crypto","import_node_crypto","import_node_fs","import_node_path","MINIMAL_LIST_FIELDS","formatDate","import_node_crypto","import_node_crypto","STATUS_MAP","MINIMAL_LIST_FIELDS","mapStatusFlag","formatStatus","formatCount","import_node_crypto","import_node_crypto","program"]}