@wport/cli 0.9.3 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.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/config-store.ts","../src/lib/channel.ts","../src/lib/global-opts.ts","../src/lib/path-utils.ts","../src/commands/jobs/view.ts","../src/lib/io-helpers.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/credentials-store.ts","../src/lib/oauth.ts","../src/lib/enterprise-client.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","../src/commands/auth/login.ts","../src/lib/browser-open.ts","../src/commands/auth/whoami.ts","../src/lib/personal-client.ts","../src/commands/auth/logout.ts","../src/commands/sessions/list.ts","../src/commands/sessions/revoke.ts","../src/commands/sessions/index.ts","../src/commands/personal/resumes/schema.ts","../src/commands/personal/resumes/validate.ts","../src/commands/personal/resumes/template.ts","../src/commands/personal/resumes/list.ts","../src/commands/personal/resumes/view.ts","../src/commands/personal/resumes/export.ts","../src/commands/personal/resumes/create.ts","../src/lib/resume-orchestrator.ts","../src/commands/personal/resumes/update.ts","../src/commands/personal/resumes/copy.ts","../src/commands/personal/resumes/publish.ts","../src/commands/personal/resumes/delete.ts","../src/commands/personal/resumes/index.ts","../src/commands/personal/apply/index.ts","../src/commands/personal/apply/message-input.ts","../src/commands/personal/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { isCliError, ExitCode, isWportError, exitCodeForError } 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';\nimport { registerLoginCommand } from './commands/auth/login';\nimport { registerWhoamiCommand } from './commands/auth/whoami';\nimport { registerLogoutCommand } from './commands/auth/logout';\nimport { registerSessionsCommand } from './commands/sessions';\nimport { registerPersonalCommand } from './commands/personal';\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);\nregisterLoginCommand(program);\nregisterWhoamiCommand(program);\nregisterLogoutCommand(program);\nregisterSessionsCommand(program);\nregisterPersonalCommand(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\tif (isWportError(err)) {\n\t\tprintError(err.message, color);\n\t\tprocess.exit(exitCodeForError(err));\n\t}\n\n\tconst fallbackMessage = err instanceof Error ? err.message : String(err);\n\tprintError(fallbackMessage, color);\n\tprocess.exit(ExitCode.ServerOrNetworkError);\n}\n","import {\n\tWportError, WportHttpError, WportNetworkError, WportInvalidArgumentError, isWportError,\n} from '@wport/core';\n\nexport 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 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\n/** core 拋的 WportError(無 exitCode)→ CLI exit code。CliError 自己帶 exitCode,不走這裡。 */\nexport function exitCodeForError(err: unknown): ExitCodeValue {\n\tif (err instanceof WportInvalidArgumentError) return ExitCode.InvalidArgument;\n\tif (err instanceof WportHttpError) {\n\t\treturn err.status >= 400 && err.status < 500 ? ExitCode.ServerClientError : ExitCode.ServerOrNetworkError;\n\t}\n\tif (err instanceof WportNetworkError) return ExitCode.ServerOrNetworkError;\n\treturn ExitCode.ServerOrNetworkError;\n}\n\n// core 類 alias:保留舊名的 instanceof 身分(resume-orchestrator/apply/company 等的 catch 靠這個對得上 core 拋的錯)。\nexport { WportError, WportHttpError, WportNetworkError, WportInvalidArgumentError, isWportError };\nexport const ServerClientHttpError = WportHttpError;\nexport const NetworkError = WportNetworkError;\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, buildUserAgent, createApiClient, throwForHttpStatus } from '@wport/core';\nimport { CLI_SOURCE, 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 '@wport/core';\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\tuserAgent: buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\t\tsource: CLI_SOURCE,\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 {\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","// build 時由 tsup `define` 把 __CHANNEL__ 烙成字面值(見 tsup.config.ts / src/global.d.ts)。\n// vitest 不走 tsup,未注入時 typeof 為 'undefined' → 視為 prod(fail-safe)。\nconst CHANNEL_BASE_URL: Record<string, string> = {\n\tprod: 'https://api.wport.me',\n\tdev: 'https://developers.wport.me/v2',\n};\n\n/** 讀取 build 時烙進的 channel;未定義(未走 tsup / 未注入)時回 'prod'。 */\nexport function currentChannel(): string {\n\treturn typeof __CHANNEL__ === 'string' ? __CHANNEL__ : 'prod';\n}\n\n/** channel → 預設 API base;未知 channel 一律 fallback prod。 */\nexport function channelBaseUrl(channel: string): string {\n\treturn CHANNEL_BASE_URL[channel] ?? CHANNEL_BASE_URL.prod;\n}\n\n/** 非 prod channel 回一行提示(給 stderr 用,含換行);prod 回 null。 */\nexport function channelBanner(): string | null {\n\tconst channel = currentChannel();\n\tif (channel === 'prod') return null;\n\treturn `[${channel}] targeting the dev backend\\n`;\n}\n","import type { Command } from 'commander';\nimport { ALLOWED_LOCALES, loadConfig, type Locale, type CliConfig } from './config-store';\nimport { channelBaseUrl, currentChannel } from './channel';\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.)\n// 預設 base 由 build 時烙進的 channel 決定(prod → api.wport.me、dev → developers.wport.me/v2)。\n// 覆寫優先序不變:--api > WPORT_API_BASE > 此預設。base 仍不落地 config(SSRF 面不變)。\nconst DEFAULT_BASE_URL = channelBaseUrl(currentChannel());\nexport const API_BASE_ENV_VAR = 'WPORT_API_BASE';\n// pm_48 交付 B(BR-039):CLI 每個請求標 `X-Source: cli`(三端 audit 鏈;後端交付 A 已上線收錄)。\n// 單一常數收斂,四個出口(jobs createApiClient / enterprise transport / personal / oauth)共用。\nexport const CLI_SOURCE = 'cli';\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","import type { Command } from 'commander';\nimport {\n\tbuildUserAgent,\n\tcreateApiClient,\n\tthrowForHttpStatus,\n\tunwrapDataResponse,\n\ttype ApiClient,\n} from '@wport/core';\nimport { CLI_SOURCE, 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 '@wport/core';\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\tuserAgent: buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\t\tsource: CLI_SOURCE,\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 { 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 { 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 { buildUserAgent, createApiClient } from '@wport/core';\nimport { CLI_SOURCE, resolveContext, type ResolvedContext } from '../lib/global-opts';\nimport { getConfigPath } from '../lib/config-store';\nimport { ExitCode } from '../lib/errors';\nimport { loadPersonalCredentials } from '../lib/credentials-store';\nimport { requestDeviceCode, type OauthRequestOptions } from '../lib/oauth';\nimport { currentChannel } from '../lib/channel';\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\n/**\n * Capability boundary an agent MUST know before scripting talent workflows (issue #106 P1-4):\n * an Enterprise API Key can only read/act on the company's *applied* talent pool. The visit\n * tab is not available yet (`talents list --tab visit` → 400), and there is no active candidate\n * search over an API Key — the public `/api/search-talent` endpoint requires an interactive JWT\n * (company mode) session and returns 401 for an API Key. Surfacing this in `doctor` stops an\n * agent from discovering these boundaries the hard way via a bare 40x.\n */\nexport const ENTERPRISE_TALENT_BOUNDARY_NOTES = [\n\t'`enterprise talents` covers your APPLIED pool only (list / view / respond).',\n\t'The visit tab is not available yet: `talents list --tab visit` returns 400.',\n\t'There is NO active candidate search over an API Key. `/api/search-talent` needs an',\n\t'interactive JWT (company-mode) session and returns 401 for an API Key — by design,',\n\t'not a setup error. Do not script talent-sourcing against an Enterprise API Key.',\n];\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, personal login state, 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 reachable = await runDoctor(ctx);\n\t\t\tif (!reachable) process.exit(ExitCode.ServerOrNetworkError);\n\t\t});\n}\n\n/**\n * doctor 主體邏輯抽成獨立函式(沿 performLogin/performWhoami 慣例),讓輸出內容可被測試斷言,\n * 不必透過 commander 整條 parse 流程重跑一次。回傳值 = 既有「公開 API 是否可達」判斷,\n * 決定 registerDoctorCommand 是否以非 0 exit code 結束——AS 連通性檢查只是輔助診斷資訊,\n * 不影響這個回傳值(見 probeAuthServer 註解)。\n */\nexport async function runDoctor(ctx: ResolvedContext): Promise<boolean> {\n\tconst line = (s = '') => process.stdout.write(s + '\\n');\n\n\tline(`wport-cli ${__CLI_VERSION__}`);\n\tline(` bundled schema fingerprint: ${__SCHEMA_HASH__}`);\n\tline('');\n\n\tline('Resolved configuration:');\n\tline(` API base URL: ${ctx.baseUrl}`);\n\tline(` channel: ${currentChannel()}`);\n\tline(` locale: ${ctx.locale}`);\n\tline(` timeout: ${ctx.timeoutMs}ms`);\n\tconst cfgPath = getConfigPath();\n\tline(` config file: ${cfgPath}${existsSync(cfgPath) ? '' : ' (not present)'}`);\n\tline('');\n\n\tline('Personal account (OAuth):');\n\tfor (const l of describePersonalLoginLines()) line(` ${l}`);\n\tline('');\n\n\tline('Server connectivity:');\n\tconst reachable = await probeServer(ctx, line);\n\tawait probeAuthServer(ctx, line);\n\tline('');\n\n\tline('Known server behaviours (read this before scripting an agent):');\n\tline(\n\t\t` • Sort is server-controlled. These query params are silently ignored: ${SILENT_IGNORED_PARAMS.join(', ')}.`\n\t);\n\tline(' • jobs search sorts by publish date, or by relevance when --keyword is set.');\n\tline(' • jobs view --batch caps parallelism (default 5, max 20) to stay friendly to the API.');\n\tline('');\n\n\tline('Enterprise talent scope (Enterprise API Key):');\n\tfor (const note of ENTERPRISE_TALENT_BOUNDARY_NOTES) line(` • ${note}`);\n\tline('');\n\n\tline('Schema drift:');\n\tline(' The fingerprint above identifies the OpenAPI contract this CLI was built against.');\n\tline(' Automated drift detection needs a server-side schema-version endpoint, which is');\n\tline(' not available yet — for now, compare fingerprints manually after a server release. [TODO]');\n\n\treturn reachable;\n}\n\n/**\n * 個人線登入態:只讀 credentials 檔、不打任何請求、不觸發 refresh —— doctor 是唯讀診斷工具,\n * 若順手在這裡 refresh 或呼叫 API,會讓「跑一次 doctor」產生副作用(消耗 access token 續期,\n * 甚至因為過期/被撤銷的 refresh token 觸發整批撤銷),這不是診斷工具該做的事。\n *\n * 整段包 try/catch:credentials.json 壞損時 `loadPersonalCredentials` 會丟 CliError\n * (ConfigCorrupt)——doctor 存在的目的正是要診斷「壞掉的東西」,不能自己被這個錯誤拖著\n * 整個指令一起死在這個區塊,讓 Server connectivity 等後續檢查都沒機會跑。\n */\nfunction describePersonalLoginLines(): string[] {\n\ttry {\n\t\tconst creds = loadPersonalCredentials();\n\t\tif (!creds) return ['not logged in (run `wport login` to sign in)'];\n\n\t\tconst expiresAtMs = Date.parse(creds.expires_at);\n\t\tif (Number.isNaN(expiresAtMs)) {\n\t\t\treturn [\n\t\t\t\t'logged in',\n\t\t\t\t`access token expiry is unreadable (\"${creds.expires_at}\") — try \\`wport login --force\\` to re-authenticate`,\n\t\t\t];\n\t\t}\n\t\tconst expired = expiresAtMs <= Date.now();\n\t\treturn [\n\t\t\t'logged in',\n\t\t\texpired\n\t\t\t\t? `access token expired at ${creds.expires_at} (refreshes automatically on next request)`\n\t\t\t\t: `access token valid until ${creds.expires_at}`,\n\t\t];\n\t} catch (err) {\n\t\treturn [`personal credentials file appears corrupted: ${err instanceof Error ? err.message : String(err)}`];\n\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({\n\t\t\tbaseUrl: ctx.baseUrl,\n\t\t\tlocale: ctx.locale,\n\t\t\ttimeoutMs: ctx.timeoutMs,\n\t\t\tuserAgent: buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\tsource: CLI_SOURCE,\n\t\t});\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\n/** device_name 送給 AS 讓伺服器端的 session/device 列表看得出這是診斷探測,不是真的登入嘗試。 */\nconst DOCTOR_PROBE_DEVICE_NAME = 'wport doctor (connectivity check)';\n\n/**\n * AS(OAuth Authorization Server)連通性:`POST /oauth/device/code` 帶 device_name,走一次完整\n * 的 device-code 請求形狀。這是唯一不需要 Authorization、對「只是在跑 doctor」這件事本身無害的\n * unauthenticated 端點——產生的 device code 沒人會去 poll,`expires_in` 秒後在伺服器端自然失效,\n * 不留殘留狀態。失敗只印一行、不拋出、不中斷其餘檢查,也不影響 runDoctor 的回傳值——AS 連通性\n * 目前只是輔助診斷資訊,doctor 的整體 exit code 仍只看既有的公開 API 探測(probeServer)。\n */\nasync function probeAuthServer(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number },\n\tline: (s?: string) => void\n): Promise<void> {\n\tconst opts: OauthRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\ttry {\n\t\tawait requestDeviceCode(opts, DOCTOR_PROBE_DEVICE_NAME);\n\t\tline(' ✓ auth server reachable (device code endpoint)');\n\t} catch (err) {\n\t\tline(` ! auth server check failed: ${err instanceof Error ? err.message : String(err)}`);\n\t}\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { describePersonalLoginLines };\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';\nimport { currentChannel } from './channel';\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\n/** 個人線 OAuth session(device flow 取得)。與 enterprise Credentials 各自獨立成一區。 */\nexport interface PersonalCredentials {\n\taccess_token: string; // wpt_ 前綴\n\trefresh_token: string;\n\texpires_at: string; // ISO;access token 到期時刻(寫入時以 now + expires_in 算出)\n\tdisplay_name: string;\n\temail: string;\n\tsession_created_at: string; // ISO\n}\n\nexport type KeySource = 'flag' | 'env' | 'file';\n\nexport interface ResolvedKey {\n\tkey: string;\n\tsource: KeySource;\n}\n\n/**\n * 檔案格式 v2(內部,不 export):{ version: 2, enterprise?: Credentials, personal?: PersonalCredentials }。\n * 舊版 flat 格式(api_key 在頂層)讀入時視為 { version: 2, enterprise: <flat> }。\n * 任何 save 系列/delete 系列函式一律以此讀-改-寫,保留另一區不受影響。\n */\ninterface RawCredentialsFile {\n\tenterprise?: unknown;\n\tpersonal?: unknown;\n}\n\nconst paths = envPaths('wport', { suffix: '' });\n\n// 憑證檔名依 build channel 分檔:dev 寫 credentials-dev.json,避免切版時 token 互蓋 / 誤用打錯後端。\n// fail-safe 與 channelBaseUrl 一致:僅 'dev' 走分檔,prod 與任何未知 channel 一律 credentials.json\n// —— 既有使用者零遷移,且不把任意 channel 字串內插進檔名路徑。\nexport function getCredentialsPath(): string {\n\tconst fileName = currentChannel() === 'dev' ? 'credentials-dev.json' : 'credentials.json';\n\treturn join(paths.config, fileName);\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\n/**\n * 讀取原始檔案,拆成 enterprise/personal 兩個「未驗證」子物件。\n *\n * strict=true(給 load* 用):JSON 壞掉或結構不對 → 丟 ConfigCorrupt(既有行為,讀路徑要能明確告警)。\n * strict=false(給 save 系列/delete 系列的讀-改-寫合併用):JSON 壞掉 → 當成空檔處理,讓\n * save 可以放心覆寫重建(保住錯誤訊息裡「Run \"wport enterprise login\" to recreate it」的復原承諾);\n * 另一區的內容在此只搬運、不解析驗證,避免「存 A 卻因為 B 區壞掉而炸開」的跨區污染。\n */\nfunction readRawFile(strict: boolean): RawCredentialsFile {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return {};\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, 'utf8'));\n\t} catch (err) {\n\t\tif (!strict) return {};\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') {\n\t\tif (!strict) return {};\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\t// 舊版 flat 格式偵測:v2 檔案不會在頂層放 api_key,看到就代表整份物件本身即 enterprise 區。\n\tif (typeof raw.api_key === 'string') {\n\t\treturn { enterprise: raw };\n\t}\n\treturn { enterprise: raw.enterprise, personal: raw.personal };\n}\n\nfunction parseEnterpriseCredentials(raw: unknown): Credentials {\n\tconst path = getCredentialsPath();\n\tif (!raw || typeof raw !== 'object' || typeof (raw 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 r = raw as Record<string, unknown>;\n\tconst apiKey = r.api_key as string;\n\treturn {\n\t\tapi_key: apiKey,\n\t\tcompany_name: typeof r.company_name === 'string' ? r.company_name : '',\n\t\tkey_last4: typeof r.key_last4 === 'string' ? r.key_last4 : apiKey.slice(-4),\n\t\tsaved_at: typeof r.saved_at === 'string' ? r.saved_at : '',\n\t};\n}\n\n// personal 區只硬驗 access_token/refresh_token/expires_at(鏡像 enterprise 只硬驗 api_key 的慣例);\n// 其餘顯示用欄位 soft-default 成空字串,不因為缺 email/display_name 就把整份憑證判死。\nfunction parsePersonalCredentials(raw: unknown): PersonalCredentials {\n\tconst path = getCredentialsPath();\n\tif (\n\t\t!raw ||\n\t\ttypeof raw !== 'object' ||\n\t\ttypeof (raw as Record<string, unknown>).access_token !== 'string' ||\n\t\ttypeof (raw as Record<string, unknown>).refresh_token !== 'string' ||\n\t\ttypeof (raw as Record<string, unknown>).expires_at !== 'string'\n\t) {\n\t\tthrow new CliError(\n\t\t\t`Personal credentials at ${path} are malformed or incomplete. Run \"wport login\" to sign in again.`,\n\t\t\tExitCode.ConfigCorrupt\n\t\t);\n\t}\n\tconst r = raw as Record<string, unknown>;\n\treturn {\n\t\taccess_token: r.access_token as string,\n\t\trefresh_token: r.refresh_token as string,\n\t\texpires_at: r.expires_at as string,\n\t\tdisplay_name: typeof r.display_name === 'string' ? r.display_name : '',\n\t\temail: typeof r.email === 'string' ? r.email : '',\n\t\tsession_created_at: typeof r.session_created_at === 'string' ? r.session_created_at : '',\n\t};\n}\n\n// Atomic write 模式照抄 config-store.saveConfig:tmpfile(0o600) → rename。\n// credentials 比 config 更敏感,所以分檔(config 可能被使用者貼進 issue 除錯)。\nfunction writeFileAtomic0600(path: string, content: string): void {\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, content);\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\n/** file 中未指定的區塊 = undefined = 不寫入該欄位(v2 殼本身仍會寫出)。 */\nfunction writeCredentialsFile(file: RawCredentialsFile): void {\n\tconst body: Record<string, unknown> = { version: 2 };\n\tif (file.enterprise !== undefined) body.enterprise = file.enterprise;\n\tif (file.personal !== undefined) body.personal = file.personal;\n\twriteFileAtomic0600(getCredentialsPath(), JSON.stringify(body, null, 2) + '\\n');\n}\n\nexport function loadCredentials(): Credentials | null {\n\tconst { enterprise } = readRawFile(true);\n\tif (enterprise === undefined) return null;\n\treturn parseEnterpriseCredentials(enterprise);\n}\n\nexport function saveCredentials(creds: Credentials): void {\n\tconst { personal } = readRawFile(false);\n\twriteCredentialsFile({ enterprise: creds, personal });\n}\n\nexport function deleteCredentials(): boolean {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return false;\n\tconst { enterprise, personal } = readRawFile(false);\n\t// 兩區都解析不出來(JSON 壞損、或合法 JSON 但無可辨識的 enterprise/personal 結構):\n\t// 維持 Task1 不變量「既有 export 維持不變(enterprise 命令零改動)」——舊版 deleteCredentials\n\t// 從不解析內容,檔案存在就整個刪除並回 true。這裡沒有 personal 資料要保留,直接比照辦理,\n\t// 否則 `wport enterprise logout` 對壞掉的檔案會靜默回 false 且留下無法復原的殘骸。\n\tif (enterprise === undefined && personal === undefined) {\n\t\tunlinkSync(path);\n\t\treturn true;\n\t}\n\tif (enterprise === undefined) return false;\n\t// 兩區都空了才整檔刪除;personal 還在就只清 enterprise 區、保留 personal(見既有\n\t// 'deleteCredentials returns true when file existed, false otherwise' 測試:單區時檔案要整個消失)。\n\tif (personal === undefined) {\n\t\tunlinkSync(path);\n\t} else {\n\t\twriteCredentialsFile({ personal });\n\t}\n\treturn true;\n}\n\nexport function loadPersonalCredentials(): PersonalCredentials | null {\n\tconst { personal } = readRawFile(true);\n\tif (personal === undefined) return null;\n\treturn parsePersonalCredentials(personal);\n}\n\nexport function savePersonalCredentials(p: PersonalCredentials): void {\n\tconst { enterprise } = readRawFile(false);\n\twriteCredentialsFile({ enterprise, personal: p });\n}\n\nexport function deletePersonalCredentials(): boolean {\n\tconst { enterprise, personal } = readRawFile(false);\n\tif (personal === undefined) return false;\n\twriteCredentialsFile({ enterprise });\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 { buildUserAgent, fetchWithTimeout, throwForHttpStatus } from '@wport/core';\nimport { CliError, ExitCode } from './errors';\nimport { OAUTH_BASE, type DeviceCodeResponse, type TokenResponse } from '@wport/core';\nimport { CLI_SOURCE } from './global-opts';\n\nexport interface OauthRequestOptions {\n\tbaseUrl: string; // resolveContext 產出,已去尾斜線\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\nconst EXPIRED_MESSAGE = 'The device code expired before authorization completed. Run `wport login` to try again.';\n\n/**\n * OAuth 端點手寫 wrapper。與 enterprise-client / personal-client 不同:這幾支端點不帶\n * Authorization(device_code / refresh_token 本身就是憑證,回應也是 RFC 標準 raw JSON,\n * 無 W101 慣用的 DataResponse wrapper)。刻意「不」在這裡就地丟錯 —— 呼叫端各自的狀態機\n * (尤其 pollForToken 的四種 device-grant 錯誤碼)需要親眼看到 status/body 才能分派,\n * 提早丟錯會讓 caller 沒機會做狀態機判斷。\n */\nasync function oauthPost(\n\topts: OauthRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>\n): Promise<{ status: number; body: unknown }> {\n\tconst url = new URL(`${opts.baseUrl}${path}`);\n\tconst request = new Request(url, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Accept-Language': opts.locale,\n\t\t\t'User-Agent': buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\t'X-Source': CLI_SOURCE,\n\t\t\tAccept: 'application/json',\n\t\t\t'Content-Type': 'application/json',\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t});\n\tconst res = await fetchWithTimeout(request, opts.timeoutMs);\n\tconst respBody: unknown = await res.json().catch(() => null);\n\treturn { status: res.status, body: respBody };\n}\n\n/** OAuth 錯誤 body 恆為 RFC 標準 `{ error: string }`(design doc §3.5);缺席回 null 讓呼叫端 fallback。 */\nfunction oauthErrorCode(body: unknown): string | null {\n\tif (body && typeof body === 'object' && typeof (body as Record<string, unknown>).error === 'string') {\n\t\treturn (body as Record<string, unknown>).error as string;\n\t}\n\treturn null;\n}\n\n/** `POST /oauth/device/code`。deviceName 為 null/空字串時不帶該欄位(後端視為未命名裝置)。 */\nexport async function requestDeviceCode(opts: OauthRequestOptions, deviceName: string | null): Promise<DeviceCodeResponse> {\n\tconst body: Record<string, unknown> = {};\n\tif (deviceName) body.device_name = deviceName;\n\tconst { status, body: respBody } = await oauthPost(opts, `${OAUTH_BASE}/device/code`, body);\n\tif (status < 200 || status >= 300) throwForHttpStatus(status, respBody);\n\treturn respBody as DeviceCodeResponse;\n}\n\n/**\n * RFC 8628 輪詢狀態機。interval 起始值 = `device.interval`(秒);`slow_down` 時 interval +5 秒\n * 續輪,往後沿用新值;`authorization_pending` 用當前 interval 續輪;`access_denied` /\n * `expired_token` 直接 CliError exit 3。第一次嘗試不 sleep —— 使用者剛看到 user_code / 瀏覽器\n * 才要開,讓第一次輪詢立即發生沒有意義上的壞處,只有「這次沒過」才需要等待再試。\n *\n * 額外的本地 deadline(`device.expires_in` 秒)是防禦性判斷:伺服器理論上會在裝置碼過期後\n * 回 `expired_token`,但避免因網路延遲、殘留連線等情況導致無窮迴圈,仍在本機端加一道保險,\n * 觸發時視同 expired 處理。sleep 可注入,測試斷言呼叫次數與間隔(預設值才是正式環境的真等待)。\n */\nexport async function pollForToken(\n\topts: OauthRequestOptions,\n\tdevice: DeviceCodeResponse,\n\tsleep: (ms: number) => Promise<void> = (ms) => new Promise((resolve) => setTimeout(resolve, ms))\n): Promise<TokenResponse> {\n\tlet intervalSec = device.interval;\n\tconst deadline = Date.now() + device.expires_in * 1000;\n\n\tfor (;;) {\n\t\tconst { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {\n\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n\t\t\tdevice_code: device.device_code,\n\t\t});\n\t\tif (status >= 200 && status < 300) return body as TokenResponse;\n\n\t\tconst code = oauthErrorCode(body);\n\t\tif (code === 'slow_down') {\n\t\t\tintervalSec += 5;\n\t\t} else if (code === 'access_denied') {\n\t\t\tthrow new CliError('Authorization was denied on the device.', ExitCode.ServerClientError);\n\t\t} else if (code === 'expired_token') {\n\t\t\tthrow new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);\n\t\t} else if (code !== 'authorization_pending') {\n\t\t\t// 未知碼(理論上不該出現在 device grant 的 invalid_grant 等):無法辨識就走通用 HTTP 錯誤路徑,\n\t\t\t// 400 經 throwForHttpStatus 一樣落在 exit 3,行為與明確分派的四碼一致。\n\t\t\tthrowForHttpStatus(status, body);\n\t\t}\n\n\t\tif (Date.now() >= deadline) throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);\n\t\tawait sleep(intervalSec * 1000);\n\t}\n}\n\n/** `POST /oauth/token`(refresh grant)。`invalid_grant` 涵蓋單純過期與 reuse-detection 全撤兩種情境,訊息統一導向重新登入。 */\nexport async function refreshAccessToken(opts: OauthRequestOptions, refreshToken: string): Promise<TokenResponse> {\n\tconst { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {\n\t\tgrant_type: 'refresh_token',\n\t\trefresh_token: refreshToken,\n\t});\n\tif (status >= 200 && status < 300) return body as TokenResponse;\n\tif (oauthErrorCode(body) === 'invalid_grant') {\n\t\tthrow new CliError('Your session is no longer valid. Run `wport login` to sign in again.', ExitCode.ServerClientError);\n\t}\n\tthrowForHttpStatus(status, body);\n}\n\n/** `POST /oauth/revoke`(RFC 7009)。契約上一律 200 空物件、無 revoke oracle,這裡仍對非 2xx 做防禦性處理。 */\nexport async function revokeRefreshToken(opts: OauthRequestOptions, refreshToken: string): Promise<void> {\n\tconst { status, body } = await oauthPost(opts, `${OAUTH_BASE}/revoke`, { token: refreshToken });\n\tif (status < 200 || status >= 300) throwForHttpStatus(status, body);\n}\n","import { buildUserAgent, WportHttpError } from '@wport/core';\nimport * as transport from '@wport/core';\nimport type {\n\tEnterpriseRequestOptions as TransportRequestOptions,\n\tEnterpriseWriteExtra,\n\tEnterpriseGetResult,\n\tEnterprisePostResult,\n} from '@wport/core';\nimport { CLI_SOURCE } from './global-opts';\nimport { printWarn } from './output';\n\nexport type { EnterpriseWriteExtra, EnterpriseGetResult, EnterprisePostResult };\n\n/**\n * CLI 呼叫端仍用這個 4 欄位形狀(不含 `userAgent`)——wrapper 自己補 `userAgent`\n * 再轉呼叫 transport,命令端組 opts 的地方不用改。\n */\nexport type EnterpriseRequestOptions = Omit<TransportRequestOptions, 'userAgent'>;\n\nfunction withUserAgent(opts: EnterpriseRequestOptions): TransportRequestOptions {\n\treturn { ...opts, userAgent: buildUserAgent('wport-cli', __CLI_VERSION__), source: CLI_SOURCE };\n}\n\n/**\n * spec §5 + pm_41 §3:401/403 附情境提示;400 帶 publish gate missing_fields 時列出缺欄。\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 *\n * 回傳**新的 `WportHttpError`**(保留原 `status`/`body`,非 `CliError`)——沿用 `WportHttpError`\n * 而非裸 `WportError` 是刻意的:頂層 `exitCodeForError` 靠 `instanceof WportHttpError` + `status`\n * 判斷 exit code(401/403/400 仍要落在 3),裸 `WportError` 會落到預設的 4,改變對外 exit code 契約。\n */\nfunction decorateEnterpriseError(err: unknown): unknown {\n\tif (!(err instanceof WportHttpError)) return err;\n\tconst base = err.message;\n\tif (err.status === 401) {\n\t\treturn new WportHttpError(\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\terr.status,\n\t\t\terr.body\n\t\t);\n\t}\n\tif (err.status === 403) {\n\t\treturn new WportHttpError(\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\terr.status,\n\t\t\terr.body\n\t\t);\n\t}\n\tif (err.status === 400) {\n\t\t// issue #106 P2:publish gate 失敗回 `data.missing_fields`,但翻譯後的 message 只有中文一句話,\n\t\t// 不含欄位清單 → agent 不知道缺哪些。把結構化 missing_fields 明列進錯誤訊息(消費端可 parse)。\n\t\tconst missingFields = extractMissingFields(err.body);\n\t\tif (missingFields.length > 0) {\n\t\t\treturn new WportHttpError(\n\t\t\t\t`${base} — Missing required fields: ${missingFields.join(', ')}. ` +\n\t\t\t\t\t'Fill them via `wport enterprise jobs update <enc_id> ...` (or the web console), then publish.',\n\t\t\t\terr.status,\n\t\t\t\terr.body\n\t\t\t);\n\t\t}\n\t}\n\treturn err;\n}\n\n/**\n * 從錯誤 body 取 publish gate 的 `data.missing_fields`(string[]);缺席 / 型別不符回 []。\n * 契約來源:`validate-job-completeness-for-publish.rule.ts` 丟\n * `BadRequestException({ path, data: { missing_fields } })`,全域 i18n filter 保留 `data`。\n */\nfunction extractMissingFields(body: unknown): string[] {\n\tif (!body || typeof body !== 'object') return [];\n\tconst data = (body as { data?: unknown }).data;\n\tif (!data || typeof data !== 'object') return [];\n\tconst fields = (data as { missing_fields?: unknown }).missing_fields;\n\tif (!Array.isArray(fields)) return [];\n\treturn fields.filter((f): f is string => typeof f === 'string');\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\nasync function wrap<T extends EnterpriseGetResult | EnterprisePostResult>(p: Promise<T>): Promise<T> {\n\tlet res: T;\n\ttry {\n\t\tres = await p;\n\t} catch (err) {\n\t\tthrow decorateEnterpriseError(err);\n\t}\n\twarnIfRateLimitLow(res.headers);\n\treturn res;\n}\n\nexport function enterpriseGet(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tquery?: Record<string, string | number | undefined>\n): Promise<EnterpriseGetResult> {\n\treturn wrap(transport.enterpriseGet(withUserAgent(opts), path, query));\n}\n\nexport function enterprisePost(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn wrap(transport.enterprisePost(withUserAgent(opts), path, body, extra));\n}\n\nexport function enterprisePatch(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn wrap(transport.enterprisePatch(withUserAgent(opts), path, body, extra));\n}\n\nexport function enterpriseDelete(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn wrap(transport.enterpriseDelete(withUserAgent(opts), path, extra));\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';\nimport { channelBanner } from '../../lib/channel';\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\tconst banner = channelBanner();\n\tif (banner) process.stderr.write(banner);\n\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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\t// issue #106 P1-3:legacy 來源子表為空時,後端於 copy 回應帶 incomplete_fields。人類模式明示,\n\t// 讓使用者知道 publish 前要補哪些欄(json 模式已含此欄,不重複印)。defensive 讀,欄位缺席即略過。\n\tconst incomplete = extractStringArray(result.incomplete_fields);\n\tif (incomplete.length > 0) {\n\t\tprocess.stdout.write(` ⚠ Incomplete for publish — fill before \\`jobs publish\\`: ${incomplete.join(', ')}\\n`);\n\t}\n}\n\n/** defensive:把 unknown 收成 string[](非陣列 / 非字串元素一律濾掉)。 */\nfunction extractStringArray(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter((v): v is string => typeof v === 'string');\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` 已於 0.7.0 全面撤銷(產品決議:職缺狀態只有上下架,無「關閉」態;\n * GUI 不存在此概念,CLI 關閉的職缺會在企業後台卡成無法操作的殭屍列)。\n * 保留隱藏 stub:老腳本/agent 打到時給明確遷移訊息並 exit 2、**不發請求**。\n */\nexport const JOBS_CLOSE_REMOVED_MESSAGE =\n\t'`jobs close` was removed in @wport/cli 0.7.0 — the \"closed\" job state has been revoked product-wide ' +\n\t'(jobs are only published/unpublished). Use `jobs unpublish <enc_id>` to take a job off the board, ' +\n\t'or `jobs delete <enc_id> --confirm` to remove it.';\n\nexport function registerEnterpriseJobsClose(parent: Command): void {\n\tparent\n\t\t.command('close [enc_id]', { hidden: true })\n\t\t.description('(removed in 0.7.0)')\n\t\t.allowUnknownOption(true)\n\t\t.action(async () => {\n\t\t\tthrow new CliError(JOBS_CLOSE_REMOVED_MESSAGE, ExitCode.InvalidArgument);\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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\tencJobId?: 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\t// enc_job_id 為 optional:帶則後端解密後精準選該職缺的聊天室,不帶則回覆最近一筆應徵(向後相容)。\n\t// CLI 端不解密、直接帶加密 id;空白視同未帶。\n\tconst encJobId = flags.encJobId?.trim();\n\tconst payload: Record<string, unknown> = { subject, body };\n\tif (encJobId) payload.enc_job_id = encJobId;\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\tpayload,\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('--enc-job-id <enc_id>', 'reply to a specific job posting; omit to reply to the most recent application')\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\t// Scope note (issue #106 P1-4): applied pool only — visit tab not available yet (`--tab visit` → 400)\n\t// and no active candidate search over an API Key (`/api/search-talent` needs a JWT; see `wport doctor`).\n\tconst talents = parent\n\t\t.command('talents')\n\t\t.description(\n\t\t\t'Browse & respond to your applied talent pool (visit tab n/a, no active candidate search — see `wport doctor`)'\n\t\t);\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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)。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 '@wport/core';\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 '@wport/core';\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","import type { Command } from 'commander';\nimport { hostname as osHostname } from 'node:os';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { requestDeviceCode, pollForToken, type OauthRequestOptions } from '../../lib/oauth';\nimport { loadPersonalCredentials, savePersonalCredentials } from '../../lib/credentials-store';\nimport { openInBrowser } from '../../lib/browser-open';\nimport { channelBanner } from '../../lib/channel';\n\nexport interface LoginFlags {\n\tnoBrowser?: boolean;\n\tforce?: boolean;\n}\n\n/** 可注入的相依,測試用假實作取代真的等待/開瀏覽器/主機名稱。 */\nexport interface LoginDeps {\n\tsleep?: (ms: number) => Promise<void>;\n\topenBrowser?: (url: string) => boolean;\n\thostname?: () => string;\n}\n\n/** 已登入時的識別字串;display_name/email 本階段可能皆為空字串(見下方大段註解),故逐層 fallback。 */\nfunction describeIdentity(displayName: string, email: string): string {\n\tif (displayName && email) return `${displayName} (${email})`;\n\tif (displayName) return displayName;\n\tif (email) return email;\n\treturn 'this device';\n}\n\n/**\n * `wport login` 核心流程(RFC 8628 device authorization grant)。\n *\n * display_name/email 本階段以空字串存入 credentials —— AS 的 TokenResponse 不含身分資訊,\n * v1 沒有 profile 端點可回填;Task 5(whoami)改走 `GET /oauth/sessions` 的 is_current 撈\n * 裝置名/建立時間做替代身分顯示,這裡先誠實存空值,等 spec B profile 端點落地後再回填。\n */\nexport async function performLogin(ctx: ResolvedContext, flags: LoginFlags, deps: LoginDeps = {}): Promise<void> {\n\tconst banner = channelBanner();\n\tif (banner) process.stderr.write(banner);\n\n\tconst openBrowser = deps.openBrowser ?? openInBrowser;\n\tconst hostnameFn = deps.hostname ?? osHostname;\n\n\tif (!flags.force) {\n\t\tconst existing = loadPersonalCredentials();\n\t\tif (existing) {\n\t\t\tprocess.stdout.write(`Already logged in as ${describeIdentity(existing.display_name, existing.email)}.\\n`);\n\t\t\tprocess.stdout.write('Run `wport login --force` to sign in again.\\n');\n\t\t\treturn;\n\t\t}\n\t}\n\n\tconst oauthOpts: OauthRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst device = await requestDeviceCode(oauthOpts, hostnameFn());\n\n\t// user_code 是使用者要抄去網頁輸入的一次性代碼,獨立換行、留白讓它在終端機裡醒目好認。\n\tprocess.stdout.write(`\\nEnter this code when prompted:\\n\\n ${device.user_code}\\n\\n`);\n\tprocess.stdout.write(`Visit: ${device.verification_uri}\\n`);\n\n\tif (!flags.noBrowser && process.stdout.isTTY) {\n\t\tconst opened = openBrowser(device.verification_uri_complete);\n\t\tif (!opened) {\n\t\t\tprocess.stdout.write(`Open this URL manually: ${device.verification_uri_complete}\\n`);\n\t\t} else {\n\t\t\t// 即使成功呼叫開瀏覽器的指令也照印網址——SSH 顯示器分離場景下,指令本身可能成功\n\t\t\t// 執行但沒有畫面能顯示瀏覽器,使用者仍需要這行網址才能自己拿去別的裝置開。\n\t\t\tprocess.stdout.write(`Opened in your browser. If nothing appeared, visit: ${device.verification_uri_complete}\\n`);\n\t\t}\n\t}\n\n\tconst tokens = await pollForToken(oauthOpts, device, deps.sleep);\n\n\tconst now = Date.now();\n\tsavePersonalCredentials({\n\t\taccess_token: tokens.access_token,\n\t\trefresh_token: tokens.refresh_token,\n\t\texpires_at: new Date(now + tokens.expires_in * 1000).toISOString(),\n\t\tdisplay_name: '',\n\t\temail: '',\n\t\tsession_created_at: new Date(now).toISOString(),\n\t});\n\n\tprocess.stdout.write('Logged in.\\n');\n}\n\nexport function registerLoginCommand(program: Command): void {\n\tprogram\n\t\t.command('login')\n\t\t.description('Sign in to your wport personal account (OAuth device authorization flow)')\n\t\t.option('--no-browser', 'do not try to open a browser automatically')\n\t\t.option('--force', 'sign in again even if already logged in')\n\t\t.action(async (opts: { browser?: boolean; force?: boolean }, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tawait performLogin(ctx, { noBrowser: opts.browser === false, force: opts.force });\n\t\t});\n}\n","import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\n\n/** spawn 相容型別:測試可注入假實作,不必真的產生行程。 */\nexport type SpawnFn = (command: string, args: readonly string[], options: SpawnOptions) => ChildProcess;\n\ninterface BrowserCommand {\n\tcommand: string;\n\targs: string[];\n}\n\n/**\n * URL 安全閘(security-checklist:API 回應是外部輸入,不得進 shell):\n * - 僅開 http(s)——`javascript:`/`file:` 等 scheme 交給瀏覽器以外的 handler 是攻擊面\n * - win32 額外拒開含 cmd metacharacters 的 URL:`cmd /c start` 的參數會被 cmd.exe 重新解析,\n * spawn 的陣列參數形式擋不住 `&`/`|` 這類串接(`?a=1&b=2` 會在 & 後當新指令執行)。\n * 合法的 device-flow 驗證頁 URL(`https://wport.me/activate?user_code=XXXX-XXXX`)不含\n * 這些字元,拒開零誤傷;被拒時回 false,呼叫端 fallback 印 URL 讓使用者自己貼。\n */\nfunction isSafeToOpen(url: string, platform: NodeJS.Platform): boolean {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(url);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return false;\n\tif (platform === 'win32' && /[&|^<>%\"]/.test(url)) return false;\n\treturn true;\n}\n\n/** 依平台決定開瀏覽器要跑的指令;不支援的平台(如 sunos/aix 等罕見值)回 null 讓呼叫端 fallback。 */\nfunction commandFor(platform: NodeJS.Platform, url: string): BrowserCommand | null {\n\tswitch (platform) {\n\t\tcase 'darwin':\n\t\t\treturn { command: 'open', args: [url] };\n\t\tcase 'win32':\n\t\t\t// cmd /c start 的第一個參數會被當成視窗標題吃掉;空字串佔位避免 URL 被誤判成標題(常見坑)。\n\t\t\treturn { command: 'cmd', args: ['/c', 'start', '\"\"', url] };\n\t\tcase 'linux':\n\t\t\treturn { command: 'xdg-open', args: [url] };\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\n/**\n * 跨平台開啟瀏覽器。detached + unref:CLI 進程不該被瀏覽器行程拖住——瀏覽器可能長駐執行,\n * 若父子行程綁在一起,CLI 會被迫等到使用者關掉瀏覽器才結束,體感上完全不合理。\n *\n * 失敗處理分兩種情況:\n * - 同步丟例外(不支援的平台 / spawn 呼叫本身炸掉)→ try/catch 接住,回 false,\n * 呼叫端(login 命令)在這之上疊一層 fallback:印出網址讓使用者自己複製貼上。\n * - 非同步 'error' event(例如指令真的不存在的 ENOENT)→ spawn() 是先同步回傳 ChildProcess,\n * 錯誤才在下一輪事件迴圈以 'error' event 送達;此時函式早已同步回傳 true。EventEmitter 對\n * 沒人監聽的 'error' event 會直接重新丟出、讓整個 process crash,所以這裡必須 attach 一個\n * 被動的 listener 吞掉它,換取「CLI 不會被開瀏覽器這種錦上添花的動作拖垮」——代價是這個情境下\n * 回傳值仍是(已經回傳過的)true,無法回頭改成 false。\n */\nexport function openInBrowser(url: string, spawnFn: SpawnFn = spawn): boolean {\n\tif (!isSafeToOpen(url, process.platform)) return false;\n\tconst resolved = commandFor(process.platform, url);\n\tif (!resolved) return false;\n\ttry {\n\t\tconst child = spawnFn(resolved.command, resolved.args, { detached: true, stdio: 'ignore' });\n\t\tchild.on('error', () => {\n\t\t\t// 故意不做事:目的只是避免 unhandled 'error' event 讓 process crash,\n\t\t\t// 此時已經來不及回報失敗給呼叫端了。\n\t\t});\n\t\tchild.unref();\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import type { Command } from 'commander';\nimport { unwrapDataArray } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../lib/personal-client';\nimport { loadPersonalCredentials } from '../../lib/credentials-store';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { printJson, sanitizeForTerminal } from '../../lib/output';\nimport { OAUTH_SESSIONS_BASE, SessionItem } from '@wport/core';\n\nconst PLACEHOLDER = '—';\n\n/**\n * 先 sanitize 再判空:device_name 等欄位是伺服器回應內容的往返值(見 SessionItem 契約),\n * 視同 untrusted 輸出處理——同一批資料稍後也會餵給 Task 6 的 `sessions list`(其他裝置名\n * 真的可被其他登入端任意設定),這裡先建立一致的防護慣例,不要等 Task 6 才補。\n * 先 sanitize 再檢查是否為空,避免「整串都是控制字元」的值繞過空值判斷、印出空白而非佔位符。\n */\nfunction display(value: string | null | undefined): string {\n\tconst clean = sanitizeForTerminal(value ?? '');\n\treturn clean.length > 0 ? clean : PLACEHOLDER;\n}\n\n/**\n * `wport whoami` 核心:`GET /oauth/sessions` 找出 `is_current` 那筆,顯示裝置名/伺服器端 session\n * 時間戳 + 本機登入時間。\n *\n * v1 沒有身分(display_name/email)可顯示 —— AS 的 TokenResponse 不含身分資訊,profile 端點待\n * spec B 落地(見 login.ts 大段註解);這裡改用 sessions API 的裝置資訊當替代身分佐證。\n *\n * 未登入時不重複判斷 —— `personalGet` 本身在 credentials 缺席時就會丟 CliError exit 3\n * (訊息含 `wport login`),這裡讓錯誤直接上拋給 top-level handler,避免與 personal-client\n * 的登入態檢查邏輯重複一份。\n */\nexport async function performWhoami(ctx: ResolvedContext): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);\n\tconst sessions = unwrapDataArray<SessionItem>(body);\n\tconst current = sessions.find((s) => s.is_current);\n\t// 呼叫到這裡代表 personalGet 剛才已用本機 personal 憑證通過認證,local 區必然存在。\n\tconst localLoginAt = loadPersonalCredentials()?.session_created_at ?? null;\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({\n\t\t\tlogged_in: true,\n\t\t\tdevice_name: current?.device_name ?? null,\n\t\t\tsession_created_at: current?.created_at ?? null,\n\t\t\tlast_used_at: current?.last_used_at ?? null,\n\t\t\tlocal_login_at: localLoginAt,\n\t\t});\n\t\treturn;\n\t}\n\n\tconst lines = [\n\t\t'Logged in.',\n\t\t`device: ${display(current?.device_name)}`,\n\t\t`session created: ${display(current?.created_at)}`,\n\t\t`last used: ${display(current?.last_used_at)}`,\n\t\t`local login time: ${display(localLoginAt)}`,\n\t];\n\tprocess.stdout.write(lines.join('\\n') + '\\n');\n}\n\nexport function registerWhoamiCommand(program: Command): void {\n\tprogram\n\t\t.command('whoami')\n\t\t.description('Show your current personal login session (contacts the server)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tawait performWhoami(resolveContext(command));\n\t\t});\n}\n","import { buildUserAgent, extractErrorMessage, fetchWithTimeout, throwForHttpStatus } from '@wport/core';\nimport { CliError, ExitCode } from './errors';\nimport { loadPersonalCredentials, savePersonalCredentials, type PersonalCredentials } from './credentials-store';\nimport { CLI_SOURCE } from './global-opts';\nimport { refreshAccessToken } from './oauth';\nimport { printWarn } from './output';\n\nexport interface PersonalRequestOptions {\n\tbaseUrl: string; // resolveContext 產出,已去尾斜線\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\nexport interface PersonalResult {\n\tbody: unknown;\n\theaders: Headers;\n}\n\n/** 寫入請求可選 headers。resumes 各寫入端點皆帶 Idempotency-Key(create/update 編排每節各自獨立一把)。 */\nexport interface PersonalWriteExtra {\n\tidempotencyKey?: string;\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';\n\n/** access_token 距 expires_at 不到這個緩衝就視為「即將過期」,提前 refresh 換新,避免發出注定 401 的請求。 */\nconst EXPIRY_SKEW_MS = 30_000;\n\nconst NOT_LOGGED_IN_MESSAGE = 'Not logged in. Run `wport login`.';\n\n/**\n * 確保回傳的 personal credentials 尚未過期:距 expires_at 不到 30 秒(含無法解析成合法時間戳,\n * `Date.parse` 回 NaN 時比較恆為 false)就視為需要 refresh,一律走 refreshAndSave 換新再回傳。\n * personal 區缺席(未登入)→ CliError exit 3,訊息導向 `wport login`。\n */\nasync function ensureFreshCredentials(opts: PersonalRequestOptions): Promise<PersonalCredentials> {\n\tconst creds = loadPersonalCredentials();\n\tif (!creds) throw new CliError(NOT_LOGGED_IN_MESSAGE, ExitCode.ServerClientError);\n\tconst msUntilExpiry = Date.parse(creds.expires_at) - Date.now();\n\tif (msUntilExpiry >= EXPIRY_SKEW_MS) return creds;\n\treturn refreshAndSave(opts, creds);\n}\n\n/**\n * 呼叫 refresh grant 換新 token pair 並整份寫回 credentials store;display_name/email/\n * session_created_at 沿舊值(AS 的 TokenResponse 不含身分資訊)。`refreshAccessToken` 對\n * `invalid_grant` 已丟出 CliError exit 3(訊息含 `wport login`),這裡不攔截,讓呼叫端\n * 統一透過同一條錯誤路徑上拋;寫回失敗(磁碟錯誤等)也照常丟錯,不吞。\n */\nasync function refreshAndSave(opts: PersonalRequestOptions, creds: PersonalCredentials): Promise<PersonalCredentials> {\n\tconst tokens = await refreshAccessToken(opts, creds.refresh_token);\n\tconst updated: PersonalCredentials = {\n\t\t...creds,\n\t\taccess_token: tokens.access_token,\n\t\trefresh_token: tokens.refresh_token,\n\t\texpires_at: new Date(Date.now() + tokens.expires_in * 1000).toISOString(),\n\t};\n\tsavePersonalCredentials(updated);\n\treturn updated;\n}\n\ninterface RawAttemptResult {\n\tstatus: number;\n\tbody: unknown;\n\theaders: Headers;\n}\n\n/**\n * 送出單次請求(不含 refresh/retry 邏輯)。headers 形狀同 enterprise-client:UA / Accept-Language /\n * Accept 恆帶;有 body 才帶 Content-Type;`extra.idempotencyKey` 給了才帶 `Idempotency-Key`。\n */\nasync function attemptRequest(\n\topts: PersonalRequestOptions,\n\tmethod: HttpMethod,\n\tpath: string,\n\taccessToken: string,\n\tbody: Record<string, unknown> | undefined,\n\tquery: Record<string, string | number | undefined> | undefined,\n\textra: PersonalWriteExtra | undefined\n): Promise<RawAttemptResult> {\n\tconst url = new URL(`${opts.baseUrl}${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 headers: Record<string, string> = {\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t'Accept-Language': opts.locale,\n\t\t'User-Agent': buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t'X-Source': CLI_SOURCE,\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\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\treturn { status: res.status, body: respBody, headers: res.headers };\n}\n\n/**\n * personal API 請求共用核心:expires_at 預判 refresh(ensureFreshCredentials)→ 發請求 → 401 →\n * refresh 一次並重試一次(防迴圈:重試後仍非 2xx 就直接丟出,不再迴圈)。refresh 併發單飛不需要\n * 處理——CLI 為單命令生命週期,同一進程內不會有並行請求互搶 refresh。\n */\nasync function personalRequest(\n\topts: PersonalRequestOptions,\n\tmethod: HttpMethod,\n\tpath: string,\n\tbody: Record<string, unknown> | undefined,\n\tquery: Record<string, string | number | undefined> | undefined,\n\textra: PersonalWriteExtra | undefined\n): Promise<PersonalResult> {\n\tconst creds = await ensureFreshCredentials(opts);\n\tlet result = await attemptRequest(opts, method, path, creds.access_token, body, query, extra);\n\tif (result.status === 401) {\n\t\tconst refreshed = await refreshAndSave(opts, creds);\n\t\tresult = await attemptRequest(opts, method, path, refreshed.access_token, body, query, extra);\n\t}\n\tif (result.status < 200 || result.status >= 300) throwPersonalHttpError(result.status, result.body);\n\twarnIfRateLimitLow(result.headers);\n\treturn { body: result.body, headers: result.headers };\n}\n\n/** personal API 唯讀 GET。path 為完整路徑(呼叫端自帶 `/api/v1/personal/...` 或 `/oauth/sessions`)。 */\nexport function personalGet(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tquery?: Record<string, string | number | undefined>\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'GET', path, undefined, query, undefined);\n}\n\n/** personal API 寫入 POST。 */\nexport function personalPost(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: PersonalWriteExtra\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'POST', path, body, undefined, extra);\n}\n\n/** personal API 寫入 PATCH(如 published-status toggle)。 */\nexport function personalPatch(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: PersonalWriteExtra\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'PATCH', path, body, undefined, extra);\n}\n\n/** personal API 寫入 PUT(如改名、portfolio_links bulk 覆寫)。 */\nexport function personalPut(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: PersonalWriteExtra\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'PUT', path, body, undefined, extra);\n}\n\n/** personal API 寫入 DELETE。無 body。 */\nexport function personalDelete(opts: PersonalRequestOptions, path: string, extra?: PersonalWriteExtra): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'DELETE', path, undefined, undefined, extra);\n}\n\n/**\n * 401 訊息改為個人線提示(re-run `wport login`);其餘狀態碼沿用 `throwForHttpStatus` 通用路徑。\n * 走到這裡代表 refresh + 重試一次後仍失敗(或本來就是非 401 的 4xx/5xx)——不再嘗試第二次 refresh。\n */\nfunction throwPersonalHttpError(status: number, body: unknown): never {\n\tif (status === 401) {\n\t\tconst base = extractErrorMessage(body) ?? `HTTP ${status}`;\n\t\tthrow new CliError(\n\t\t\t`${base} — Your session may have expired or the request was rejected. Run \\`wport login\\` to sign in again.`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n\tthrowForHttpStatus(status, body);\n}\n\n/** 剩餘配額 <10% 時 stderr 提醒(不阻斷)。邏輯複製自 enterprise-client.warnIfRateLimitLow(同款 rate limit headers)。 */\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 type { Command } from 'commander';\nimport { revokeRefreshToken, type OauthRequestOptions } from '../../lib/oauth';\nimport { loadPersonalCredentials, deletePersonalCredentials } from '../../lib/credentials-store';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { printWarn } from '../../lib/output';\n\n/**\n * `wport logout` 核心(RFC 7009 revoke + 本機清除)。\n *\n * revoke 失敗(server 不可達、逾時、5xx 等 —— 不管哪一種)都不阻擋登出:使用者下 `logout`\n * 指令的意圖是清掉「這台裝置」的本機 session,伺服器端撤銷只是順手做掉;做不到也要讓本機\n * 憑證照樣消失,不然使用者會卡在「以為登出了但其實沒有」的狀態,之後 personal-client 拿著\n * 同一份憑證繼續發請求。因此這裡刻意 catch 所有例外(不只 NetworkError),只印警告續行。\n *\n * 未登入時直接印訊息、正常返回(exit 0)——冪等,重複執行 logout 不該被當成錯誤。\n */\nexport async function performLogout(ctx: ResolvedContext): Promise<void> {\n\tconst creds = loadPersonalCredentials();\n\tif (!creds) {\n\t\tprocess.stdout.write('Not logged in.\\n');\n\t\treturn;\n\t}\n\n\tconst opts: OauthRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\ttry {\n\t\tawait revokeRefreshToken(opts, creds.refresh_token);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tprintWarn(`Failed to revoke the session on the server: ${message}. Removing local credentials anyway.`, ctx.color);\n\t}\n\n\tdeletePersonalCredentials();\n\tprocess.stdout.write('Logged out.\\n');\n}\n\nexport function registerLogoutCommand(program: Command): void {\n\tprogram\n\t\t.command('logout')\n\t\t.description('Sign out of your wport personal account (revokes the refresh token and clears local credentials)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tawait performLogout(resolveContext(command));\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { unwrapDataArray } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { printJson, printTable } from '../../lib/output';\nimport { OAUTH_SESSIONS_BASE, SessionItem } from '@wport/core';\n\n/** 格式化沿 enterprise/keys/list.ts 的 formatDate 模式(每個 list 命令各自複製一份,檔案小而聚焦)。 */\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\n/**\n * `sessions list` 核心:GET /oauth/sessions → 列出所有存活的登入 session(跨裝置)。\n * `is_current` 標記本機這台裝置,方便使用者辨識忘記登出的其他裝置(DR-7 失竊自救場景的前置偵查)。\n */\nexport async function runSessionsList(ctx: ResolvedContext): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);\n\tconst sessions = unwrapDataArray<SessionItem>(body);\n\n\tif (ctx.format === 'json') {\n\t\tprintJson(sessions);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tsessions,\n\t\t[\n\t\t\t{ header: 'ENC_ID', value: (r) => r.enc_id.slice(0, 14) },\n\t\t\t{ header: 'DEVICE', value: (r) => r.device_name ?? '', maxWidth: 24 },\n\t\t\t{ header: 'CREATED', value: (r) => formatDate(r.created_at), maxWidth: 12 },\n\t\t\t{ header: 'LAST_USED', value: (r) => formatDate(r.last_used_at), maxWidth: 12 },\n\t\t\t{ header: 'CURRENT', value: (r) => (r.is_current ? '*' : '') },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerSessionsList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your active personal login sessions (devices)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tawait runSessionsList(resolveContext(command));\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { formatDate };\n","import type { Command } from 'commander';\nimport { OAUTH_SESSIONS_BASE } from '@wport/core';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalDelete, type PersonalRequestOptions } from '../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { printJson } from '../../lib/output';\n\ninterface RevokeFlags {\n\tallOthers?: boolean;\n}\n\n/** `DELETE /oauth/sessions/others` 契約:DataResponse<{ revoked_count }>(SoT = personal-sessions.controller.ts)。 */\ninterface RevokeOthersResult {\n\trevoked_count: number;\n}\n\n/**\n * `sessions revoke` 核心。`<enc_id>` 與 `--all-others` 互斥擇一 —— 都給或都缺都是使用者輸入錯誤,\n * 本地擋下(exit 2),不發任何請求。\n *\n * 撤銷「目前這台裝置」的 session 時,本機 credentials 刻意**不**主動清除:CLI 沒有廉價的方法\n * 分辨「這個 enc_id 是不是自己」(要另外打一次 GET /oauth/sessions 找 is_current 比對,換不到\n * 什麼),讓下一次 personal API 請求自然收到 401、走 personal-client 既有的「Run `wport login`」\n * 提示路徑即可——這與「在另一台裝置上撤銷本機 session」的收尾路徑完全一致,不需要為「自己撤自己」\n * 開特例。\n */\nexport async function runSessionsRevoke(\n\tctx: ResolvedContext,\n\tencId: string | undefined,\n\tallOthers: boolean\n): Promise<void> {\n\tconst hasEncId = encId !== undefined;\n\tif (hasEncId === allOthers) {\n\t\tthrow new CliError('Provide exactly one of <enc_id> or --all-others', ExitCode.InvalidArgument);\n\t}\n\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\n\tif (allOthers) {\n\t\tconst { body } = await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/others`);\n\t\tconst result = unwrapDataResponse<RevokeOthersResult>(body);\n\t\tif (ctx.format === 'json') {\n\t\t\tprintJson(result);\n\t\t\treturn;\n\t\t}\n\t\tprocess.stdout.write(`Revoked ${result.revoked_count} other session(s).\\n`);\n\t\treturn;\n\t}\n\n\tconst trimmed = encId!.trim();\n\tif (!trimmed) throw new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t// 端點回 MsgResponse({ success, statusCode, message }),無 `data` 欄位 —— 不走 unwrapDataResponse\n\t// (那需要 `data` 鍵存在,硬套會誤丟「missing wrapper」)。回應內容本身不影響本地輸出。\n\tawait personalDelete(opts, `${OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: trimmed, revoked: true });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Revoked session ${trimmed}.\\n`);\n}\n\nexport function registerSessionsRevoke(parent: Command): void {\n\tparent\n\t\t.command('revoke [enc_id]')\n\t\t.description('Revoke a session by enc_id, or every other session with --all-others')\n\t\t.option('--all-others', 'revoke every session except the current one')\n\t\t.action(async (encId: string | undefined, flags: RevokeFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tawait runSessionsRevoke(ctx, encId, flags.allOthers === true);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { registerSessionsList } from './list';\nimport { registerSessionsRevoke } from './revoke';\n\nexport function registerSessionsCommand(program: Command): void {\n\tconst sessions = program.command('sessions').description('List and revoke your personal login sessions (devices)');\n\tregisterSessionsList(sessions);\n\tregisterSessionsRevoke(sessions);\n}\n","import type { Command } from 'commander';\nimport { buildAggregateJsonSchema } from '@wport/core';\nimport { printJson } from '../../../lib/output';\n\n/**\n * 離線命令:純從 definition.ts SoT 推導 JSON Schema,不發請求、不需登入。\n * 沒有 --output table 分支 —— schema 本質是文件而非資料列表,永遠印 JSON(同 config get 慣例)。\n */\nexport function registerPersonalResumesSchema(parent: Command): void {\n\tparent\n\t\t.command('schema')\n\t\t.description('Print the resume aggregate JSON Schema (offline, no login required)')\n\t\t.option('--section <name>', 'print only the given section (e.g. education, work_experience)')\n\t\t.action((opts: { section?: string }) => {\n\t\t\tprintJson(buildAggregateJsonSchema(opts.section));\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { validateAggregate } from '@wport/core';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { readJsonInput } from '../../../lib/io-helpers';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\n\ninterface ValidateFlags {\n\tfile: string;\n}\n\n/**\n * `resumes validate` 核心:離線本地驗證,不發請求、不需登入。通過印 `Valid.`(json:`{valid:true}`);\n * 失敗逐條印 `path: message` 到 stderr(json 模式改印 `{valid:false, issues}` 到 stdout),\n * 最後一律丟 CliError(exit 2) 讓 top-level handler 收斂 exit code —— 同 `jobs batch` 慣例:\n * 結果先印出,再丟錯誤觸發非 0 exit,命令本身不必碰 process.exit。\n */\nexport function runResumesValidate(ctx: ResolvedContext, filePath: string): void {\n\tconst input = readJsonInput(filePath, { timeoutMs: ctx.timeoutMs });\n\tconst issues = validateAggregate(input);\n\n\tif (issues.length === 0) {\n\t\tif (ctx.format === 'json') {\n\t\t\tprintJson({ valid: true });\n\t\t} else {\n\t\t\tprocess.stdout.write('Valid.\\n');\n\t\t}\n\t\treturn;\n\t}\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({ valid: false, issues });\n\t} else {\n\t\tfor (const issue of issues) {\n\t\t\tprocess.stderr.write(`${sanitizeForTerminal(issue.path)}: ${sanitizeForTerminal(issue.message)}\\n`);\n\t\t}\n\t}\n\tthrow new CliError(`${issues.length} validation issue(s) found`, ExitCode.InvalidArgument);\n}\n\nexport function registerPersonalResumesValidate(parent: Command): void {\n\tparent\n\t\t.command('validate')\n\t\t.description('Validate a resume aggregate JSON file locally (offline, no login required)')\n\t\t.requiredOption('--file <path>', 'path to a JSON resume aggregate, or \"-\" for stdin')\n\t\t.action((flags: ValidateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\trunResumesValidate(ctx, flags.file);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { existsSync, writeFileSync } from 'node:fs';\nimport { buildTemplate } from '@wport/core';\nimport { InvalidArgumentError } from '../../../lib/errors';\nimport { printJson } from '../../../lib/output';\n\ninterface TemplateFlags {\n\tout?: string;\n}\n\n/**\n * `resumes template` 核心:離線命令,不發請求、不需登入,依 SECTIONS 產出範例骨架(buildTemplate())。\n * 沒有 `--out` → 印到 stdout(同 schema 命令慣例,永遠 JSON,不看 --output table/json);\n * 有 `--out` → 寫入該路徑,檔案已存在則拒絕覆蓋(exit 2)而非靜默蓋掉使用者現有內容。\n */\nexport function runResumesTemplate(outPath?: string): void {\n\tconst template = buildTemplate();\n\n\tif (outPath === undefined) {\n\t\tprintJson(template);\n\t\treturn;\n\t}\n\n\tif (existsSync(outPath)) {\n\t\tthrow new InvalidArgumentError(`File already exists: ${outPath} (refusing to overwrite — remove it or choose a different --out path)`);\n\t}\n\n\ttry {\n\t\twriteFileSync(outPath, `${JSON.stringify(template, null, 2)}\\n`, 'utf8');\n\t} catch (err) {\n\t\tthrow new InvalidArgumentError(`Failed to write template to ${outPath}: ${(err as NodeJS.ErrnoException).message}`);\n\t}\n\n\tprocess.stdout.write(`Wrote template to ${outPath}\\n`);\n}\n\nexport function registerPersonalResumesTemplate(parent: Command): void {\n\tparent\n\t\t.command('template')\n\t\t.description('Print (or write) a resume aggregate skeleton with example values for every field (offline, no login required)')\n\t\t.option('--out <file>', 'write the template to this file instead of stdout (fails if the file already exists)')\n\t\t.action((flags: TemplateFlags) => {\n\t\t\trunResumesTemplate(flags.out);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { dim, printJson, printTable } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\nimport { PERSONAL_RESUMES_BASE, type ResumeListData, type ResumeQuota } from '@wport/core';\n\ninterface ListFlags {\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/** 同 design doc §3.4「list 預設欄位」,也是表格顯示的欄位集合(is_disabled/disable_reason 不列入)。 */\nconst MINIMAL_LIST_FIELDS = ['enc_id', 'name', 'updated_at', 'is_complete', 'is_published'];\n\n/** 格式化沿 enterprise/keys/list.ts 的 formatDate 模式(每個 list 命令各自複製一份,檔案小而聚焦)。 */\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\n/** 表格尾行摘要:`{used}/{max} used, can create.` 或 `..., cannot create (reason_code).`(無 reason_code 則省略括號)。 */\nfunction formatQuotaLine(quota: ResumeQuota): string {\n\tconst status = quota.can_create ? 'can create' : `cannot create${quota.reason_code ? ` (${quota.reason_code})` : ''}`;\n\treturn `${quota.used}/${quota.max_resumes} used, ${status}.`;\n}\n\n/**\n * `resumes list` 共用抓取邏輯:GET /api/v1/personal/resumes → unwrap `{resumes, quota}`。\n * `resumes export`(export.ts)也需要先拿到全部 enc_id 才能逐份 view,故抽成獨立函式共用,\n * 而不是各自重複一份 personalGet + unwrapDataResponse。\n */\nexport async function fetchResumeList(opts: PersonalRequestOptions): Promise<ResumeListData> {\n\tconst { body } = await personalGet(opts, PERSONAL_RESUMES_BASE);\n\treturn unwrapDataResponse<ResumeListData>(body);\n}\n\n/**\n * `resumes list` 核心。data 為 `{resumes, quota}`(非陣列,R1 端點形狀),故不能用\n * `unwrapDataArray`——quota 是 Agent 判斷能否 create 的關鍵資訊,一律隨 resumes 一起印出\n * (--fields/--minimal 只投影 resumes 陣列,quota 原樣保留,DR-10)。\n */\nexport async function runResumesList(ctx: ResolvedContext, flags: ListFlags): 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 opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst data = await fetchResumeList(opts);\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 ? { ...data, resumes: data.resumes.map((r) => pickPaths(r, projection)) } : data);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tdata.resumes,\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: 'UPDATED', value: (r) => formatDate(r.updated_at), maxWidth: 12 },\n\t\t\t{ header: 'COMPLETE', value: (r) => (r.is_complete ? 'yes' : 'no') },\n\t\t\t{ header: 'PUBLISHED', value: (r) => (r.is_published ? 'yes' : 'no') },\n\t\t],\n\t\tctx.color\n\t);\n\tprocess.stdout.write(dim(formatQuotaLine(data.quota), ctx.color) + '\\n');\n}\n\nexport function registerPersonalResumesList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your personal resumes')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths, applied to each resume)')\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\tawait runResumesList(resolveContext(command), flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { formatDate, formatQuotaLine, MINIMAL_LIST_FIELDS };\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, printTable } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\nimport { PERSONAL_RESUMES_BASE, type AggregateResume } from '@wport/core';\nimport { SECTIONS, type SectionDef } from '@wport/core';\n\ninterface ViewFlags {\n\tfields?: string;\n}\n\n/**\n * `resumes view` / `resumes export` 共用抓取邏輯:GET /resumes/{enc_id} → unwrap 成\n * `AggregateResume`。export.ts 逐份呼叫這支函式,不重複一份 personalGet + unwrap。\n * enc_id 走 encodeURIComponent(同 talents/view.ts 慣例),避免內含特殊字元破壞路徑。\n */\nexport async function fetchResumeAggregate(opts: PersonalRequestOptions, encId: string): Promise<AggregateResume> {\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 personalGet(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);\n\treturn unwrapDataResponse<AggregateResume>(body);\n}\n\ninterface SectionSummaryRow {\n\tsection: string;\n\tsummary: string;\n}\n\n/**\n * table 模式不印整份巢狀 JSON(太長、不利終端機閱讀,聚合完整內容一律走 `--output json`\n * 或 `--fields`,同 plan Task 13 規格「json 為主用途」)。改印每節摘要:\n * - array 節(education/certificate/language/portfolio_links)→ 筆數\n * - wrapper 節(work_experience)→ `has_no_work_experience` 為 true 時印固定文字,否則印筆數\n * - object / scalar 節(professional_skills/job_condition/background/autobiography)→\n * present/absent(object 節額外檢查 `Object.keys().length`,避免 `{}` 這種「技術上非 null\n * 但語意上没資料」的殼被誤判成 present)\n * 節的走訪順序沿用 SECTIONS(resume-schema/definition.ts 這份 SoT),避免這裡另建一份節清單、\n * 未來新增/調整節時兩處要同步改。\n */\nfunction summarizeSections(resume: AggregateResume): SectionSummaryRow[] {\n\tconst rows: SectionSummaryRow[] = [{ section: 'name', summary: resume.name }];\n\tfor (const def of SECTIONS) {\n\t\trows.push({ section: def.key, summary: summarizeSection(def, resume) });\n\t}\n\treturn rows;\n}\n\nfunction summarizeSection(def: SectionDef, resume: AggregateResume): string {\n\tconst value = (resume as unknown as Record<string, unknown>)[def.key];\n\tif (def.kind === 'array') {\n\t\treturn `${Array.isArray(value) ? value.length : 0} item(s)`;\n\t}\n\tif (def.kind === 'wrapper') {\n\t\tconst wrapper = value as { has_no_work_experience?: boolean; items?: unknown[] } | null | undefined;\n\t\tif (wrapper?.has_no_work_experience) return 'no work experience';\n\t\treturn `${Array.isArray(wrapper?.items) ? (wrapper.items as unknown[]).length : 0} item(s)`;\n\t}\n\tif (def.kind === 'scalar') {\n\t\treturn typeof value === 'string' && value.length > 0 ? 'present' : 'absent';\n\t}\n\t// def.kind === 'object'\n\treturn value && typeof value === 'object' && Object.keys(value as object).length > 0 ? 'present' : 'absent';\n}\n\n/**\n * `resumes view` 核心:GET /resumes/{enc_id} → 一次回全節的 AggregateResume(R2)。\n * `--fields` 優先於 `--output`(同 company/jobs/campaigns view 慣例:投影一律印 JSON,\n * 不管 --output table/json),因為投影結果本就是任意巢狀路徑挑出的欄位,表格無從呈現。\n */\nexport async function runResumesView(ctx: ResolvedContext, encId: string, flags: ViewFlags): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst resume = await fetchResumeAggregate(opts, encId);\n\n\tif (flags.fields) {\n\t\tprintJson(pickPaths(resume, parseFieldsList(flags.fields)));\n\t\treturn;\n\t}\n\tif (ctx.format === 'json') {\n\t\tprintJson(resume);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tsummarizeSections(resume),\n\t\t[\n\t\t\t{ header: 'SECTION', value: (r) => r.section, maxWidth: 20 },\n\t\t\t{ header: 'SUMMARY', value: (r) => r.summary, maxWidth: 40 },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerPersonalResumesView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_id>')\n\t\t.description('View one resume aggregate (all sections)')\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\tawait runResumesView(resolveContext(command), encId, flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { summarizeSections };\n","import type { Command } from 'commander';\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { fetchResumeList } from './list';\nimport { fetchResumeAggregate } from './view';\nimport type { PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\n\ninterface ExportFlags {\n\tout?: string;\n}\n\n/**\n * server 回傳的 `enc_id` 正常只含英數字/底線/連字號,理論上不該有路徑分隔符。防禦性擋掉含\n * `/` `\\` 的值——沒有這層檢查,`resume-${encId}.json` 這種字串拼接一旦 encId 帶\n * `../../../../tmp/evil` 之類的內容,`path.join(outDir, filename)` 會把 `..` 段正規化掉、\n * 讓寫入路徑跳脫 outDir(已用 node -e 實測驗證過,不是紙上談兵)。伺服器被入侵或契約跑掉時\n * 這是最後一道防線,不依賴信任 server 回應內容。\n */\nfunction safeResumeFilename(encId: string): string {\n\tif (encId.includes('/') || encId.includes('\\\\') || encId === '.' || encId === '..') {\n\t\tthrow new CliError(\n\t\t\t`Unexpected enc_id from server: \"${encId}\" (contains path separators; refusing to write outside --out)`,\n\t\t\tExitCode.ServerOrNetworkError\n\t\t);\n\t}\n\treturn `resume-${encId}.json`;\n}\n\n/**\n * `resumes export` 核心:list 找出全部 enc_id → 逐份 view 抓聚合 → 各寫一檔\n * `resume-<enc_id>.json`。配額上限 ≤6 份(quota.max_resumes),循序拉取即可,\n * 不需要 `lib/concurrency.ts` 的併發控制;單節失敗語義(create/update 編排的 partial\n * success)在這裡不適用——view 本身不是分節寫入,中途失敗直接讓錯誤上拋(不寫殘缺檔)。\n *\n * `outDir` 要求呼叫端已決議好預設值(`--out` 未帶時的 `process.cwd()` 由\n * `registerPersonalResumesExport` 的 action 處理)——不把 `process.cwd()` 塞進這支函式的\n * 預設參數,方便測試永遠帶明確路徑、不必 mock 全域 `process.cwd`。\n */\nexport async function runResumesExport(ctx: ResolvedContext, outDir: string): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst { resumes } = await fetchResumeList(opts);\n\n\tmkdirSync(outDir, { recursive: true });\n\n\tfor (const item of resumes) {\n\t\t// 檔名安全檢查放在發請求「之前」:enc_id 若真的帶跳脫路徑,寧可不浪費這次 view 請求就先擋下來。\n\t\tconst filePath = join(outDir, safeResumeFilename(item.enc_id));\n\t\tconst aggregate = await fetchResumeAggregate(opts, item.enc_id);\n\t\twriteFileSync(filePath, `${JSON.stringify(aggregate, null, 2)}\\n`, 'utf8');\n\t}\n\n\tprocess.stdout.write(`Exported ${resumes.length} resume(s) to ${outDir}.\\n`);\n}\n\nexport function registerPersonalResumesExport(parent: Command): void {\n\tparent\n\t\t.command('export')\n\t\t.description('Export all your resumes as one JSON file per resume')\n\t\t.option('--out <dir>', 'destination directory (default: current directory)')\n\t\t.action(async (flags: ExportFlags, command: Command) => {\n\t\t\tawait runResumesExport(resolveContext(command), flags.out ?? process.cwd());\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { safeResumeFilename };\n","import type { Command } from 'commander';\nimport { validateAggregate } from '@wport/core';\nimport { createAggregate, type OrchestrationResult } from '../../../lib/resume-orchestrator';\nimport type { PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, printTable, sanitizeForTerminal } from '../../../lib/output';\n\ninterface CreateFlags {\n\tfile: string;\n}\n\n/**\n * `resumes create` 核心:本地 `validateAggregate` 先驗(有 issue → exit 2,不發任何請求,\n * 訊息呈現同 `resumes validate` 慣例)→ 通過才進 `createAggregate` 編排(建殼 → rename →\n * 依序逐節寫入)。結果先印出(json/table),再視 `allOk` 決定是否丟 CliError 觸發非 0 exit ——\n * 同 `jobs batch` 慣例:腳本先讀到完整結果,才看到非 0 exit code。\n */\nexport async function runResumesCreate(ctx: ResolvedContext, filePath: string): Promise<void> {\n\tconst input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });\n\tconst issues = validateAggregate(input);\n\tif (issues.length > 0) {\n\t\tif (ctx.format === 'json') {\n\t\t\tprintJson({ valid: false, issues });\n\t\t} else {\n\t\t\tfor (const issue of issues) {\n\t\t\t\tprocess.stderr.write(`${sanitizeForTerminal(issue.path)}: ${sanitizeForTerminal(issue.message)}\\n`);\n\t\t\t}\n\t\t}\n\t\tthrow new CliError(`${issues.length} validation issue(s) found — fix locally before creating (no request sent)`, ExitCode.InvalidArgument);\n\t}\n\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst result = await createAggregate(opts, input);\n\tprintOrchestrationResult(ctx, result);\n\n\tif (!result.allOk) {\n\t\tconst failed = result.reports.filter((r) => !r.ok).map((r) => r.section);\n\t\tthrow new CliError(\n\t\t\t`Resume ${result.enc_id} was created but ${failed.length} section(s) failed to write: ${failed.join(', ')}. ` +\n\t\t\t\t`Run \\`wport personal resumes update ${result.enc_id} --section <name> --file <file>\\` to retry the failed section(s).`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n}\n\n/**\n * create/update 共用的結果呈現:json 印 `{enc_id, reports, all_ok}`;table 先印 enc_id 前綴行\n * (create 情境下這是使用者唯一得知新 enc_id 的地方),再每節一行 ✓/✗ + error。update.ts 各自\n * 複製一份(檔案小而聚焦,同 list.ts formatDate 慣例),不額外拉一個共用模組。\n */\nfunction printOrchestrationResult(ctx: ResolvedContext, result: OrchestrationResult): void {\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: result.enc_id, reports: result.reports, all_ok: result.allOk });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Resume: ${sanitizeForTerminal(result.enc_id ?? '')}\\n`);\n\tprintTable(\n\t\tresult.reports,\n\t\t[\n\t\t\t{ header: 'SECTION', value: (r) => r.section, maxWidth: 20 },\n\t\t\t{ header: 'STATUS', value: (r) => (r.ok ? '✓' : '✗') },\n\t\t\t{ header: 'ERROR', value: (r) => r.error ?? '', maxWidth: 60 },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerPersonalResumesCreate(parent: Command): void {\n\tparent\n\t\t.command('create')\n\t\t.description('Create a resume from a JSON aggregate file (validates locally first; use \"-\" for stdin)')\n\t\t.requiredOption('--file <path>', 'path to a JSON resume aggregate, or \"-\" for stdin')\n\t\t.action(async (flags: CreateFlags, command: Command) => {\n\t\t\tawait runResumesCreate(resolveContext(command), flags.file);\n\t\t});\n}\n","/**\n * `resumes create` / `resumes update` 的聚合編排核心(US-1 核心,design doc §3.4/plan Task 14)。\n * 把一份聚合 JSON 拆成 spec B 對應的多個分節寫入請求,依 `SECTION_WRITE_PLAN`\n *(personal-types.ts)派工,逐節記錄成敗、**不回滾**——單節失敗不影響其他節繼續寫入,已成功\n * 寫入的節與(create 情境下)已建的殼都保留,讓使用者用 `update --section <失敗節>` 補寫,\n * 不必整份重來。\n *\n * create 專屬的「建殼」前置步驟例外:失敗代表根本沒有 enc_id 可續寫任何節,屬全有全無的\n * 中止點,直接 throw(不落入分節失敗的 report 陣列)。rename(②b)與其後的分節寫入則一律\n * 走「失敗記報告、繼續下一步」的一般路徑。\n *\n * 本地 `validateAggregate` 前置驗證是呼叫端(create.ts 命令層)的責任,這支檔案不重複驗證,\n * 維持「本地驗證=快速失敗、編排=發請求」的分工(update 不做本地整份驗證,見 updateAggregate\n * 註解)。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalPost, personalPut, type PersonalRequestOptions } from './personal-client';\nimport { CliError, ExitCode, ServerClientHttpError } from './errors';\nimport { PERSONAL_RESUMES_BASE, SECTION_WRITE_PLAN, type UpdateResumeNameDto } from '@wport/core';\nimport { SECTIONS, getSection, type AggregateSectionKey } from '@wport/core';\n\nexport interface SectionReport {\n\tsection: string;\n\tok: boolean;\n\terror?: string;\n}\n\nexport interface OrchestrationResult {\n\tenc_id: string | null;\n\treports: SectionReport[];\n\tallOk: boolean;\n}\n\ntype WriteMode = 'create' | 'update';\n\n/** 寫入端點(POST/PUT/PATCH/DELETE 全部)必帶 Idempotency-Key(design doc §5.1);每次呼叫各自一把。 */\nfunction freshIdempotencyKey(): { idempotencyKey: string } {\n\treturn { idempotencyKey: randomUUID() };\n}\n\n/** CliError 與 core 錯誤(ServerClientHttpError/NetworkError,即 core 的 WportHttpError/WportNetworkError alias)皆已是可讀訊息;其餘走 String() 兜底。 */\nfunction describeError(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\n/** 錯誤 body 的機器可判讀 `error_code`(design doc §5.1:CLI 判斷 key `error_code` 優先於 `statusCode`)。 */\nfunction extractErrorCode(body: unknown): string | null {\n\tif (body && typeof body === 'object' && typeof (body as Record<string, unknown>).error_code === 'string') {\n\t\treturn (body as Record<string, unknown>).error_code as string;\n\t}\n\treturn null;\n}\n\n/**\n * 建履歷殼(R3:`POST /api/v1/personal/resumes`,空 body,D8;photo_url 唯讀本就不入殼)。\n * 與其餘寫入步驟不同:失敗代表整個 create 無以為繼(沒有 enc_id 可續寫任何節),直接中止並\n * 依 `error_code` 給引導訊息(BR-002 `profile_incomplete` / BR-010 `resume_limit_reached`),\n * 不落入「記 report 繼續」的一般分節失敗路徑;其他 4xx/5xx 原樣上拋(已經是可讀的 core 錯誤 WportHttpError/WportError)。\n */\nasync function createShell(opts: PersonalRequestOptions): Promise<string> {\n\ttry {\n\t\tconst { body } = await personalPost(opts, PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());\n\t\treturn unwrapDataResponse<{ enc_id: string; name: string }>(body).enc_id;\n\t} catch (err) {\n\t\tif (err instanceof ServerClientHttpError) {\n\t\t\tconst code = extractErrorCode(err.body);\n\t\t\tif (code === 'profile_incomplete') {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t'Cannot create resume: your member profile is incomplete. Complete the required profile fields first, then retry (server: profile_incomplete).',\n\t\t\t\t\tExitCode.ServerClientError\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (code === 'resume_limit_reached') {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t'Cannot create resume: you have reached your resume limit. Delete an existing resume first, then retry (server: resume_limit_reached).',\n\t\t\t\t\tExitCode.ServerClientError\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthrow err;\n\t}\n}\n\n/** `name` 型別收窄為 string:呼叫端已用 `aggregate.name !== undefined` 排除 undefined,其餘型別不符交給 server 422 把關(update.ts 註解),這裡不重複加 runtime 檢查。 */\nasync function renameResume(opts: PersonalRequestOptions, encId: string, name: string): Promise<void> {\n\tawait personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name } satisfies UpdateResumeNameDto, freshIdempotencyKey());\n}\n\n/** work_experience item 的 is_current:聚合格式讀 boolean,寫入 DTO 收 0/1(讀寫不對稱,design doc §3.4)。 */\nfunction toWorkExperienceWriteItem(item: Record<string, unknown>): Record<string, unknown> {\n\tconst { is_current, ...rest } = item;\n\treturn { ...rest, is_current: is_current ? 1 : 0 };\n}\n\n/**\n * 從 item 取出 R2 聚合讀取帶回的 `enc_id`(round-trip 用),回傳 [有效 enc_id | null, 剝除後的\n * body]。空白字串視同缺席;create 模式呼叫端直接丟棄 enc_id(新履歷不可能有既有 item id,\n * view 複製來的聚合直接 create 也能過),update 模式據此分派 PUT(更新既有)/ POST(新增)。\n */\nfunction splitItemEncId(item: Record<string, unknown>): [string | null, Record<string, unknown>] {\n\tconst { enc_id, ...rest } = item;\n\tconst valid = typeof enc_id === 'string' && enc_id.trim() ? enc_id.trim() : null;\n\treturn [valid, rest];\n}\n\n/**\n * 依 `SECTION_WRITE_PLAN` 分派單節寫入。`mode` 的作用:\n * - `single-post`:create 用 POST(新建),update 用 PUT(單物件節整份覆蓋,無 delete)\n * - `per-item-post` / `work-experience` 的 items:update 模式下 item 帶 `enc_id`(view round-trip)\n * → 走 item 級 PUT 更新既有;無 `enc_id` → POST 新增(plan Task 14「帶 enc_id 走 PUT」)。\n * create 模式一律剝除 enc_id 後 POST。**不做宣告式刪除**——不在輸入內的既有 item 不會被刪\n * (portfolio_links 的 bulk-put 例外,該端點本身是宣告式)。\n * - `bulk-put`:item 的 `enc_id` 對映為後端 DTO 的 `encId`(update 保留既有連結;create 一律 null)\n *\n * 刻意不對 `value` 做結構性防呆(是否為陣列/物件):呼叫端已篩掉 `undefined`,其餘型別錯誤\n * 讓 JS 原生地丟出(例如對非陣列 `for...of`),統一由 `attemptStep` 接住記成該節失敗 ——\n * 不重複 validate.ts 已有的型別檢查邏輯。\n */\nasync function writeSection(\n\topts: PersonalRequestOptions,\n\tencId: string,\n\tkey: AggregateSectionKey,\n\tvalue: unknown,\n\tmode: WriteMode\n): Promise<void> {\n\tconst plan = SECTION_WRITE_PLAN[key];\n\tconst base = `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;\n\n\tswitch (plan.kind) {\n\t\tcase 'per-item-post': {\n\t\t\tfor (const item of value as Record<string, unknown>[]) {\n\t\t\t\tconst [itemEncId, body] = splitItemEncId(item);\n\t\t\t\tif (mode === 'update' && itemEncId) {\n\t\t\t\t\tawait personalPut(opts, `${base}${plan.path}/${encodeURIComponent(itemEncId)}`, body, freshIdempotencyKey());\n\t\t\t\t} else {\n\t\t\t\t\tawait personalPost(opts, `${base}${plan.path}`, body, freshIdempotencyKey());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcase 'work-experience': {\n\t\t\tconst wrapper = value as { has_no_work_experience?: boolean; items: Record<string, unknown>[] };\n\t\t\tif (wrapper.has_no_work_experience) {\n\t\t\t\tawait personalPost(opts, `${base}${plan.path}/no-work-experience`, {}, freshIdempotencyKey());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (const item of wrapper.items) {\n\t\t\t\tconst [itemEncId, rest] = splitItemEncId(item);\n\t\t\t\tconst body = toWorkExperienceWriteItem(rest);\n\t\t\t\tif (mode === 'update' && itemEncId) {\n\t\t\t\t\t// 既有 web DTO 慣例:work-experience 的 PUT 以 body 內 `encId`(camelCase 例外)定位單筆\n\t\t\t\t\tawait personalPut(opts, `${base}${plan.path}`, { encId: itemEncId, ...body }, freshIdempotencyKey());\n\t\t\t\t} else {\n\t\t\t\t\tawait personalPost(opts, `${base}${plan.path}`, body, freshIdempotencyKey());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcase 'single-post': {\n\t\t\tconst body = key === 'autobiography' ? { autobiography: value } : (value as Record<string, unknown>);\n\t\t\tconst write = mode === 'create' ? personalPost : personalPut;\n\t\t\tawait write(opts, `${base}${plan.path}`, body, freshIdempotencyKey());\n\t\t\treturn;\n\t\t}\n\t\tcase 'bulk-put': {\n\t\t\tconst items = value as Record<string, unknown>[];\n\t\t\tawait personalPut(\n\t\t\t\topts,\n\t\t\t\t`${base}${plan.path}`,\n\t\t\t\t{\n\t\t\t\t\tportfolio_links: items.map((item) => {\n\t\t\t\t\t\tconst [itemEncId, rest] = splitItemEncId(item);\n\t\t\t\t\t\treturn { encId: mode === 'update' ? itemEncId : null, ...rest };\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tfreshIdempotencyKey()\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/** 單一步驟(rename 或某一節)成敗記錄成一筆 SectionReport;失敗不拋出,讓呼叫端繼續下一步(不回滾)。 */\nasync function attemptStep(section: string, run: () => Promise<void>): Promise<SectionReport> {\n\ttry {\n\t\tawait run();\n\t\treturn { section, ok: true };\n\t} catch (err) {\n\t\treturn { section, ok: false, error: describeError(err) };\n\t}\n}\n\n/**\n * `create --file` 編排核心:\n * ① 建殼(R3,失敗中止,見 createShell)\n * ② 聚合有 `name` → rename(D8:後端建殼不收 name,由 CLI 補;失敗記報告、不中止)\n * ③ 依 SECTIONS 固定順序,僅寫聚合檔內出現的節(單物件節用 POST)\n */\nexport async function createAggregate(opts: PersonalRequestOptions, aggregate: Record<string, unknown>): Promise<OrchestrationResult> {\n\tconst encId = await createShell(opts);\n\tconst reports: SectionReport[] = [];\n\n\tif (aggregate.name !== undefined) {\n\t\treports.push(await attemptStep('name', () => renameResume(opts, encId, aggregate.name as string)));\n\t}\n\n\tfor (const section of SECTIONS) {\n\t\tconst value = aggregate[section.key];\n\t\tif (value === undefined) continue;\n\t\treports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, 'create')));\n\t}\n\n\treturn { enc_id: encId, reports, allOk: reports.every((r) => r.ok) };\n}\n\n/**\n * `update <enc_id> --file [--section]` 編排核心。resume 已存在(無建殼步)。\n *\n * 頂層 `name`:整份模式(無 `onlySection`)下若聚合檔帶 `name` → 比照 create ②b 走 R4 rename\n * (記入 reports section='name')——否則 view → 改名 → update 的改動會被靜默丟棄,且 CLI 將完全\n * 無法改名(final review low finding,2026-07-30 補)。`--section` 模式維持只寫指定節,`name`\n * 不是節、不在 `--section` 值域。\n *\n * `onlySection` 給定 → 只寫該節:未知節名或該節在聚合檔內缺席都是本地能擋的輸入錯誤,直接\n * CliError exit 2(不發請求)——未知節名訊息列出合法節名,mirror to-json-schema.ts 的\n * `buildAggregateJsonSchema` 慣例。未給 `onlySection` → 出現的節全寫,語意同 create③。\n *\n * 不做本地整份 `validateAggregate`:`--section` 模式下輸入檔可能只含單節內容,套用整份聚合\n * 驗證器(要求頂層 `name` 必填)會誤判;交給 server 422 把關,partial success 記入逐節報告。\n */\nexport async function updateAggregate(\n\topts: PersonalRequestOptions,\n\tencId: string,\n\taggregate: Record<string, unknown>,\n\tonlySection?: string\n): Promise<OrchestrationResult> {\n\tconst reports: SectionReport[] = [];\n\n\tif (onlySection !== undefined) {\n\t\tconst section = getSection(onlySection);\n\t\tif (!section) {\n\t\t\tconst allowed = SECTIONS.map((s) => s.key).join(', ');\n\t\t\tthrow new CliError(`Unknown section \"${onlySection}\". Allowed: ${allowed}`, ExitCode.InvalidArgument);\n\t\t}\n\t\tconst value = aggregate[onlySection];\n\t\tif (value === undefined) {\n\t\t\tthrow new CliError(`Section \"${onlySection}\" has no data in the input file`, ExitCode.InvalidArgument);\n\t\t}\n\t\treports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, 'update')));\n\t\treturn { enc_id: encId, reports, allOk: reports.every((r) => r.ok) };\n\t}\n\n\tif (aggregate.name !== undefined) {\n\t\treports.push(await attemptStep('name', () => renameResume(opts, encId, aggregate.name as string)));\n\t}\n\n\tfor (const section of SECTIONS) {\n\t\tconst value = aggregate[section.key];\n\t\tif (value === undefined) continue;\n\t\treports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, 'update')));\n\t}\n\n\treturn { enc_id: encId, reports, allOk: reports.every((r) => r.ok) };\n}\n","import type { Command } from 'commander';\nimport { updateAggregate, type OrchestrationResult } from '../../../lib/resume-orchestrator';\nimport type { PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, printTable, sanitizeForTerminal } from '../../../lib/output';\n\ninterface UpdateFlags {\n\tfile: string;\n\tsection?: string;\n}\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/**\n * `resumes update` 核心:無本地整份 `validateAggregate` 前置驗證 —— `--section` 模式下輸入\n * 檔可能只含單節內容,套用整份聚合驗證器(要求頂層 `name` 必填)會誤判;交給 server 422\n * 把關,partial success 記入逐節報告(見 resume-orchestrator.ts 的 updateAggregate 註解)。\n * 結果先印出(json/table),再視 `allOk` 決定是否丟 CliError 觸發非 0 exit(同 create.ts/\n * `jobs batch` 慣例)。\n */\nexport async function runResumesUpdate(\n\tctx: ResolvedContext,\n\tencId: string,\n\tfilePath: string,\n\tflags: UpdateFlags\n): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst result = await updateAggregate(opts, trimmed, input, flags.section);\n\tprintOrchestrationResult(ctx, result);\n\n\tif (!result.allOk) {\n\t\tconst failed = result.reports.filter((r) => !r.ok).map((r) => r.section);\n\t\tthrow new CliError(\n\t\t\t`Resume ${result.enc_id} was partially updated — ${failed.length} section(s) failed: ${failed.join(', ')}. ` +\n\t\t\t\t`Fix the input and retry with --section <name>.`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n}\n\n/**\n * create/update 共用的結果呈現,各自複製一份(檔案小而聚焦,同 list.ts formatDate 慣例):\n * json 印 `{enc_id, reports, all_ok}`;table 印 enc_id 前綴行 + 每節一行 ✓/✗ + error。\n */\nfunction printOrchestrationResult(ctx: ResolvedContext, result: OrchestrationResult): void {\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: result.enc_id, reports: result.reports, all_ok: result.allOk });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Resume: ${sanitizeForTerminal(result.enc_id ?? '')}\\n`);\n\tprintTable(\n\t\tresult.reports,\n\t\t[\n\t\t\t{ header: 'SECTION', value: (r) => r.section, maxWidth: 20 },\n\t\t\t{ header: 'STATUS', value: (r) => (r.ok ? '✓' : '✗') },\n\t\t\t{ header: 'ERROR', value: (r) => r.error ?? '', maxWidth: 60 },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerPersonalResumesUpdate(parent: Command): void {\n\tparent\n\t\t.command('update <enc_id>')\n\t\t.description('Update a resume aggregate (whole file, or one section with --section)')\n\t\t.requiredOption('--file <path>', 'path to a JSON resume aggregate (or single-section content), or \"-\" for stdin')\n\t\t.option('--section <name>', 'only write this section (e.g. education, work_experience)')\n\t\t.action(async (encId: string, flags: UpdateFlags, command: Command) => {\n\t\t\tawait runResumesUpdate(resolveContext(command), encId, flags.file, flags);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalPost, personalPut, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_RESUMES_BASE, type UpdateResumeNameDto } from '@wport/core';\n\ninterface CopyFlags {\n\tname?: string;\n}\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\nfunction printCopyResult(ctx: ResolvedContext, sourceEncId: string, newEncId: string): void {\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: newEncId });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Copied resume ${sanitizeForTerminal(sourceEncId)} → new resume: ${sanitizeForTerminal(newEncId)}\\n`);\n}\n\n/**\n * `resumes copy` 核心(R5,design doc §5.2):`POST /resumes/{enc_id}/duplicate`(空 body,D8)\n * → 201 `{enc_id}`(server 命名「原名 - 複製」,不回 name)。422 `resume_limit_reached` 不特別\n * 攔截改寫訊息——`personalPost` 對非 2xx 已轉成 `ServerClientHttpError`(exit 3),原樣透傳即是\n * plan Task 15 規格要求的「透傳」,與 create 編排 `createShell` 的專屬引導訊息刻意不同。\n *\n * `--name` 給定 → 複製成功後續發 `PUT /resumes/{新enc_id}/name`(D8 兩步編排,同\n * resume-orchestrator.ts `createAggregate` 的 rename 步驟)。複製本身已成功時就先印出結果\n * (新 enc_id 上到 stdout,script 可讀);rename 失敗不影響已存在的複製——新履歷還在,只是留著\n * 伺服器預設命名,故不回滾、只讓錯誤帶著新 enc_id 上拋(exit 3),不再另外印一次 Warning 重複\n * 同一件事(結果已經在 stdout,錯誤訊息只留在 stderr 這一個管道)。\n */\nexport async function runResumesCopy(ctx: ResolvedContext, encId: string, name: string | undefined): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\n\tconst { body } = await personalPost(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/duplicate`, {}, { idempotencyKey: randomUUID() });\n\tconst { enc_id: newEncId } = unwrapDataResponse<{ enc_id: string }>(body);\n\n\tprintCopyResult(ctx, trimmed, newEncId);\n\n\tif (name === undefined) return;\n\n\ttry {\n\t\tawait personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name } satisfies UpdateResumeNameDto, { idempotencyKey: randomUUID() });\n\t} catch (err) {\n\t\tconst reason = err instanceof Error ? err.message : String(err);\n\t\tthrow new CliError(\n\t\t\t`Resume ${trimmed} was copied to ${newEncId}, but renaming it to \"${name}\" failed: ${reason}. ` +\n\t\t\t\t'The copy exists under its server-assigned default name — this CLI has no standalone rename command in v1.',\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n}\n\nexport function registerPersonalResumesCopy(parent: Command): void {\n\tparent\n\t\t.command('copy <enc_id>')\n\t\t.description('Duplicate a resume (counts toward your resume limit)')\n\t\t.option('--name <name>', 'rename the new copy after duplicating')\n\t\t.action(async (encId: string, flags: CopyFlags, command: Command) => {\n\t\t\tawait runResumesCopy(resolveContext(command), encId, flags.name);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalPatch, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_RESUMES_BASE, type PersonalUpdatePublishedStatusDto } from '@wport/core';\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/**\n * `resumes publish` / `resumes unpublish` 共用(R6,design doc §5.2):\n * `PATCH /resumes/{enc_id}/published-status` body `{target_status: boolean}`(spec B 已裁必填,\n * 非 toggle)→ `{is_published}`。可逆操作,不需 `--confirm`(同 campaigns publish/unpublish 慣例,\n * 有別於 resumes delete)。\n *\n * 輸出用伺服器回傳的 `is_published`,不直接回顯呼叫端要求的 `target_status`——避免伺服器實際\n * 狀態與請求不一致時(理論上不該發生,但別預設請求即結果)誤導使用者。\n */\nexport async function runResumesPublishTransition(ctx: ResolvedContext, encId: string, action: 'publish' | 'unpublish'): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst targetStatus = action === 'publish';\n\n\tconst { body } = await personalPatch(\n\t\topts,\n\t\t`${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,\n\t\t{ target_status: targetStatus } satisfies PersonalUpdatePublishedStatusDto,\n\t\t{ idempotencyKey: randomUUID() }\n\t);\n\tconst result = unwrapDataResponse<{ is_published: boolean }>(body);\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: trimmed, is_published: result.is_published });\n\t\treturn;\n\t}\n\tconst verb = action === 'publish' ? 'Published' : 'Unpublished';\n\tprocess.stdout.write(`${verb} resume: ${sanitizeForTerminal(trimmed)}\\n`);\n}\n\nexport function registerPersonalResumesPublish(parent: Command): void {\n\tparent\n\t\t.command('publish <enc_id>')\n\t\t.description('Publish a resume (make it visible to employers)')\n\t\t.action(async (encId: string, _flags: Record<string, never>, command: Command) => {\n\t\t\tawait runResumesPublishTransition(resolveContext(command), encId, 'publish');\n\t\t});\n}\n\nexport function registerPersonalResumesUnpublish(parent: Command): void {\n\tparent\n\t\t.command('unpublish <enc_id>')\n\t\t.description('Unpublish a resume (hide it from employers)')\n\t\t.action(async (encId: string, _flags: Record<string, never>, command: Command) => {\n\t\t\tawait runResumesPublishTransition(resolveContext(command), encId, 'unpublish');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { personalDelete, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_RESUMES_BASE } from '@wport/core';\n\ninterface DeleteFlags {\n\tconfirm?: boolean;\n}\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/**\n * `resumes delete` 核心(R7,design doc §5.2):破壞性、不可逆——無 `--confirm` 一律本地\n * exit 2、**不發請求**(同 jobs delete 慣例)。confirm 檢查先於 enc_id 驗證(同 jobs delete\n * 的 `runJobsDelete` 順序)。\n *\n * DELETE 成功後端回 `DataResponse(null)`(軟刪,`data` 為 null)——不 unwrap/deref(否則對 null\n * 取屬性會丟原生 TypeError,同 jobs/lifecycle.ts 的 F-001 教訓);用呼叫時已驗證過的 enc_id\n * 回報刪除結果,不依賴回應 body。\n */\nexport async function runResumesDelete(ctx: ResolvedContext, encId: string, confirm: boolean): 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\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\n\tawait personalDelete(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`, { idempotencyKey: randomUUID() });\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: trimmed, deleted: true });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Deleted resume: ${sanitizeForTerminal(trimmed)}\\n`);\n}\n\nexport function registerPersonalResumesDelete(parent: Command): void {\n\tparent\n\t\t.command('delete <enc_id>')\n\t\t.description('Delete a resume (destructive; requires --confirm)')\n\t\t.option('--confirm', 'confirm this destructive, irreversible delete')\n\t\t.action(async (encId: string, flags: DeleteFlags, command: Command) => {\n\t\t\tawait runResumesDelete(resolveContext(command), encId, flags.confirm === true);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { registerPersonalResumesSchema } from './schema';\nimport { registerPersonalResumesValidate } from './validate';\nimport { registerPersonalResumesTemplate } from './template';\nimport { registerPersonalResumesList } from './list';\nimport { registerPersonalResumesView } from './view';\nimport { registerPersonalResumesExport } from './export';\nimport { registerPersonalResumesCreate } from './create';\nimport { registerPersonalResumesUpdate } from './update';\nimport { registerPersonalResumesCopy } from './copy';\nimport { registerPersonalResumesPublish, registerPersonalResumesUnpublish } from './publish';\nimport { registerPersonalResumesDelete } from './delete';\n\n// Task 13 掛 list/view/export;Task 14 補 create/update;Task 15(checkpoint 3 收尾)\n// 補上 copy/publish/unpublish/delete,同一份 index.ts 逐步長大。\nexport function registerPersonalResumesCommand(parent: Command): void {\n\tconst resumes = parent.command('resumes').description('Manage your personal resumes');\n\tregisterPersonalResumesSchema(resumes);\n\tregisterPersonalResumesValidate(resumes);\n\tregisterPersonalResumesTemplate(resumes);\n\tregisterPersonalResumesList(resumes);\n\tregisterPersonalResumesView(resumes);\n\tregisterPersonalResumesExport(resumes);\n\tregisterPersonalResumesCreate(resumes);\n\tregisterPersonalResumesUpdate(resumes);\n\tregisterPersonalResumesCopy(resumes);\n\tregisterPersonalResumesPublish(resumes);\n\tregisterPersonalResumesUnpublish(resumes);\n\tregisterPersonalResumesDelete(resumes);\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { personalPost, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode, ServerClientHttpError } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_APPLICATIONS_BASE } from '@wport/core';\nimport { resolveMessage, validateBatchItems, type ApplyBatchItem } from './message-input';\n\ninterface ApplyFlags {\n\tresume?: string;\n\tmessage?: string;\n\tmessageFile?: string;\n\tfile?: string;\n\tconfirm?: boolean;\n}\n\n/** 把 409/429 的結構化 body 轉成人類可讀訊息(保留 exit 3)。其餘錯誤原樣上拋。 */\nfunction rethrowApplyError(err: unknown): never {\n\tif (err instanceof ServerClientHttpError) {\n\t\tconst body = (err.body ?? null) as Record<string, unknown> | null;\n\t\tconst code = body?.error_code;\n\t\tif (err.status === 409 && code === 'application_cooldown') {\n\t\t\tthrow new CliError(`Application in cooldown. Next apply at: ${body?.next_apply_at ?? 'unknown'}`, ExitCode.ServerClientError);\n\t\t}\n\t\tif (err.status === 409 && code === 'job_not_available') {\n\t\t\tthrow new CliError('Job is no longer available (closed or removed).', ExitCode.ServerClientError);\n\t\t}\n\t\tif (err.status === 429 && code === 'apply_daily_limit_reached') {\n\t\t\tthrow new CliError(\n\t\t\t\t`Daily application limit reached (${body?.used}/${body?.limit}). Resets at: ${body?.resets_at ?? 'tomorrow'}`,\n\t\t\t\tExitCode.ServerClientError\n\t\t\t);\n\t\t}\n\t}\n\tthrow err;\n}\n\nexport async function runPersonalApply(\n\tctx: ResolvedContext,\n\targs: { encJobId: string; encResumeId: string; message: string; confirm: boolean }\n): Promise<void> {\n\tif (!args.confirm) {\n\t\tthrow new CliError(\n\t\t\t'Refusing to apply without --confirm (application is visible to the employer)',\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst body = { enc_job_id: args.encJobId, enc_resume_id: args.encResumeId, application_message: args.message };\n\tlet result;\n\ttry {\n\t\tresult = await personalPost(opts, PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: randomUUID() });\n\t} catch (err) {\n\t\trethrowApplyError(err);\n\t}\n\tconst data = (result.body as { data?: Record<string, unknown> })?.data ?? {};\n\tif (ctx.format === 'json') {\n\t\tprintJson(data);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Applied. application: ${sanitizeForTerminal(String(data.enc_application_id ?? ''))}\\n`);\n\tconst q = data.quota as { used?: number; limit?: number; remaining?: number } | undefined;\n\tif (q) process.stdout.write(`Daily quota: ${q.used}/${q.limit} used, ${q.remaining} remaining\\n`);\n}\n\nexport function registerPersonalApplyCommand(parent: Command): void {\n\tparent\n\t\t.command('apply [job_enc_id]')\n\t\t.description('Apply to a job (single) or batch via --file (requires --confirm)')\n\t\t.option('--resume <enc_id>', 'resume enc_id to apply with (single mode)')\n\t\t.option('--message <text>', 'application message (1~2000 chars)')\n\t\t.option('--message-file <path>', 'read application message from file (use - for stdin)')\n\t\t.option('--file <path>', 'batch JSON file { \"applications\": [...] } (max 20)')\n\t\t.option('--confirm', 'confirm this outward-facing action')\n\t\t.action(async (jobEncId: string | undefined, flags: ApplyFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tif (flags.file) {\n\t\t\t\tawait runPersonalApplyBatch(ctx, { file: flags.file, confirm: flags.confirm === true });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!jobEncId || !flags.resume) {\n\t\t\t\tthrow new CliError('single apply requires <job_enc_id> and --resume', ExitCode.InvalidArgument);\n\t\t\t}\n\t\t\tconst message = resolveMessage({ message: flags.message, messageFile: flags.messageFile });\n\t\t\tawait runPersonalApply(ctx, {\n\t\t\t\tencJobId: jobEncId,\n\t\t\t\tencResumeId: flags.resume,\n\t\t\t\tmessage,\n\t\t\t\tconfirm: flags.confirm === true,\n\t\t\t});\n\t\t});\n}\n\nexport async function runPersonalApplyBatch(\n\tctx: ResolvedContext,\n\targs: { file: string; confirm: boolean }\n): Promise<void> {\n\tif (!args.confirm) {\n\t\tthrow new CliError(\n\t\t\t'Refusing to apply without --confirm (applications are visible to employers)',\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(args.file, 'utf8'));\n\t} catch {\n\t\tthrow new CliError(`Cannot read/parse batch file: ${args.file}`, ExitCode.InvalidArgument);\n\t}\n\tconst items: ApplyBatchItem[] = validateBatchItems((parsed as { applications?: unknown })?.applications);\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tlet result;\n\ttry {\n\t\tresult = await personalPost(opts, `${PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: randomUUID() });\n\t} catch (err) {\n\t\trethrowApplyError(err);\n\t}\n\tconst data = (result.body as { data?: Record<string, unknown> })?.data ?? {};\n\tif (ctx.format === 'json') {\n\t\tprintJson(data);\n\t\treturn;\n\t}\n\tconst succeeded = (data.succeeded as unknown[]) ?? [];\n\tconst failed = (data.failed as { index: number; enc_job_id: string; error_code: string }[]) ?? [];\n\tprocess.stdout.write(`Batch applied: ${succeeded.length} succeeded, ${failed.length} failed\\n`);\n\tfor (const f of failed) {\n\t\tprocess.stdout.write(` #${f.index} ${sanitizeForTerminal(f.enc_job_id)}: ${sanitizeForTerminal(f.error_code)}\\n`);\n\t}\n}\n","import { readFileSync } from 'node:fs';\nimport { CliError, ExitCode } from '../../../lib/errors';\n\nconst MAX_MESSAGE = 2000;\n\nexport interface ApplyBatchItem {\n\tenc_job_id: string;\n\tenc_resume_id: string;\n\tapplication_message: string;\n}\n\nfunction defaultReadStdin(): string {\n\treturn readFileSync(0, 'utf8');\n}\n\nfunction validateMessageText(raw: string): string {\n\tconst trimmed = raw.trim();\n\tif (trimmed.length < 1) {\n\t\tthrow new CliError('application message must not be empty (1~2000 chars) 應徵訊息必填', ExitCode.InvalidArgument);\n\t}\n\tif (trimmed.length > MAX_MESSAGE) {\n\t\tthrow new CliError(`application message exceeds ${MAX_MESSAGE} chars 應徵訊息不可超過 2000 字`, ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/** 單筆 --message / --message-file 互斥二擇一必填;`-` 讀 stdin。違規丟 exit 2。 */\nexport function resolveMessage(opts: { message?: string; messageFile?: string; readStdin?: () => string }): string {\n\tconst hasMsg = opts.message !== undefined;\n\tconst hasFile = opts.messageFile !== undefined;\n\tif (hasMsg && hasFile) {\n\t\tthrow new CliError('--message and --message-file are mutually exclusive 互斥', ExitCode.InvalidArgument);\n\t}\n\tif (!hasMsg && !hasFile) {\n\t\tthrow new CliError('--message or --message-file is required 應徵訊息必填', ExitCode.InvalidArgument);\n\t}\n\tif (hasMsg) return validateMessageText(opts.message as string);\n\tconst file = opts.messageFile as string;\n\tconst raw = file === '-' ? (opts.readStdin ?? defaultReadStdin)() : readFileSync(file, 'utf8');\n\treturn validateMessageText(raw);\n}\n\n/** 批次 ≤20 + 每筆欄位齊 + message 1~2000。違規丟 exit 2。回正規化(trim)後的項目。 */\nexport function validateBatchItems(items: unknown): ApplyBatchItem[] {\n\tif (!Array.isArray(items) || items.length < 1) {\n\t\tthrow new CliError('batch file must contain a non-empty \"applications\" array', ExitCode.InvalidArgument);\n\t}\n\tif (items.length > 20) {\n\t\tthrow new CliError(`batch exceeds 20 items (got ${items.length}) 批次上限 20 筆`, ExitCode.InvalidArgument);\n\t}\n\treturn items.map((it, i) => {\n\t\tconst o = it as Record<string, unknown>;\n\t\tconst jobId = typeof o?.enc_job_id === 'string' ? o.enc_job_id.trim() : '';\n\t\tconst resumeId = typeof o?.enc_resume_id === 'string' ? o.enc_resume_id.trim() : '';\n\t\tif (!jobId || !resumeId) {\n\t\t\tthrow new CliError(`batch item #${i}: enc_job_id and enc_resume_id required`, ExitCode.InvalidArgument);\n\t\t}\n\t\tconst message = validateMessageText(typeof o?.application_message === 'string' ? o.application_message : '');\n\t\treturn { enc_job_id: jobId, enc_resume_id: resumeId, application_message: message };\n\t});\n}\n","import type { Command } from 'commander';\nimport { registerPersonalResumesCommand } from './resumes';\nimport { registerPersonalApplyCommand } from './apply';\n\nexport function registerPersonalCommand(program: Command): void {\n\tconst personal = program.command('personal').description('Manage your personal resumes, profile and applications');\n\tregisterPersonalResumesCommand(personal);\n\tregisterPersonalApplyCommand(personal);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uBAAwB;;;ACAxB,kBAEO;AAEA,IAAM,WAAW;AAAA,EACvB,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,eAAe;AAChB;AAIO,IAAM,WAAN,cAAuB,MAAM;AAAA,EAC1B;AAAA,EAET,YAAY,SAAiB,UAAyB;AACrD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EACjB;AACD;AAEO,IAAM,uBAAN,cAAmC,SAAS;AAAA,EAClD,YAAY,SAAiB;AAC5B,UAAM,SAAS,SAAS,eAAe;AACvC,SAAK,OAAO;AAAA,EACb;AACD;AAWO,SAAS,WAAW,KAA+B;AACzD,SAAO,eAAe;AACvB;AAGO,SAAS,iBAAiB,KAA6B;AAC7D,MAAI,eAAe,sCAA2B,QAAO,SAAS;AAC9D,MAAI,eAAe,4BAAgB;AAClC,WAAO,IAAI,UAAU,OAAO,IAAI,SAAS,MAAM,SAAS,oBAAoB,SAAS;AAAA,EACtF;AACA,MAAI,eAAe,8BAAmB,QAAO,SAAS;AACtD,SAAO,SAAS;AACjB;AAIO,IAAM,wBAAwB;AAC9B,IAAM,eAAe;;;ACzD5B,wBAAkB;AAClB,wBAAe;AAiBf,IAAM,uBAAuB,IAAI;AAAA,EAChC;AAAA;AAAA,IAEC;AAAA;AAAA,IAEA;AAAA;AAAA;AAAA;AAAA,IAIA;AAAA,EACD,EAAE,KAAK,GAAG;AAAA,EACV;AACD;AAMA,IAAM,uBAAuB,IAAI,OAAO,2CAA2C,GAAG;AAItF,IAAM,0BAA0B,IAAI,OAAO,0DAA0D,GAAG;AAEjG,SAAS,oBAAoB,GAAmB;AACtD,SAAO,EAAE,QAAQ,sBAAsB,EAAE,EAAE,QAAQ,sBAAsB,EAAE;AAC5E;AAEO,SAAS,6BAA6B,GAAmB;AAC/D,SAAO,EAAE,QAAQ,sBAAsB,EAAE,EAAE,QAAQ,UAAU,IAAI,EAAE,QAAQ,yBAAyB,EAAE;AACvG;AAEO,SAAS,oBAAoB,UAA4C;AAC/E,MAAI,aAAa,QAAW;AAC3B,WAAO,QAAQ,OAAO,QAAQ,UAAU;AAAA,EACzC;AACA,MAAI,aAAa,UAAU,aAAa,SAAS;AAChD,UAAM,IAAI,SAAS,qBAAqB,QAAQ,2BAA2B,SAAS,eAAe;AAAA,EACpG;AACA,SAAO;AACR;AAEO,SAAS,eAAe,SAAuC;AACrE,MAAI,YAAY,KAAM,QAAO;AAC7B,MAAI,QAAQ,IAAI,SAAU,QAAO;AACjC,SAAO,QAAQ,OAAO,SAAS;AAChC;AAEO,SAAS,UAAU,OAAsB;AAC/C,UAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,IAAI,IAAI;AAC3D;AAQO,SAAS,gBAAgB,OAAsB;AACrD,UAAQ,OAAO,MAAM,KAAK,UAAU,KAAK,IAAI,IAAI;AAClD;AAQO,SAAS,WAAc,MAAW,SAA2B,OAAsB;AACzF,MAAI,KAAK,WAAW,GAAG;AACtB,YAAQ,OAAO,MAAM,QAAQ,kBAAAA,QAAG,IAAI,gBAAgB,IAAI,gBAAgB;AACxE;AAAA,EACD;AACA,QAAM,QAAQ,IAAI,kBAAAC,QAAM;AAAA,IACvB,MAAM,QAAQ,IAAI,CAAC,MAAO,QAAQ,kBAAAD,QAAG,KAAK,EAAE,MAAM,IAAI,EAAE,MAAO;AAAA,IAC/D,OAAO,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,EAAE;AAAA,IAC9B,WAAW,QAAQ,IAAI,CAAC,MAAM,EAAE,YAAY,IAAI;AAAA,IAChD,UAAU;AAAA,EACX,CAAC;AACD,aAAW,OAAO,MAAM;AAEvB,UAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,oBAAoB,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;AAAA,EACjE;AACA,UAAQ,OAAO,MAAM,MAAM,SAAS,IAAI,IAAI;AAC7C;AAEO,SAAS,WAAW,SAAiB,OAAsB;AACjE,QAAM,SAAS,QAAQ,kBAAAA,QAAG,IAAI,QAAQ,IAAI;AAE1C,UAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,oBAAoB,OAAO,CAAC;AAAA,CAAI;AACnE;AAEO,SAAS,UAAU,SAAiB,OAAsB;AAChE,QAAM,SAAS,QAAQ,kBAAAA,QAAG,OAAO,UAAU,IAAI;AAC/C,UAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,oBAAoB,OAAO,CAAC;AAAA,CAAI;AACnE;AAEO,SAAS,IAAI,MAAc,OAAwB;AACzD,SAAO,QAAQ,kBAAAA,QAAG,IAAI,IAAI,IAAI;AAC/B;;;ACpHA,IAAAE,kBAA6B;AAC7B,IAAAC,eAAqF;;;ACFrF,qBAUO;AACP,uBAA8B;AAC9B,uBAAqB;AAId,IAAM,kBAAkB,CAAC,SAAS,SAAS,SAAS,SAAS,OAAO;AAGpE,IAAM,iBAAiB,CAAC,SAAS,MAAM;AAS9C,IAAM,cAAc,CAAC,UAAU,UAAU,YAAY;AAWrD,IAAM,yBAAyB,CAAC,cAAc;AAI9C,IAAM,uBAA4D;AAAA,EACjE,cAAc;AACf;AAIA,IAAI,oBAAoB;AASjB,SAAS,YAAY,KAA+B;AAC1D,SAAQ,YAAkC,SAAS,GAAG;AACvD;AAEO,SAAS,sBAAsB,KAAyC;AAC9E,SAAQ,uBAA6C,SAAS,GAAG;AAClE;AAEA,IAAM,YAAQ,iBAAAC,SAAS,SAAS,EAAE,QAAQ,GAAG,CAAC;AAEvC,SAAS,gBAAwB;AACvC,aAAO,uBAAK,MAAM,QAAQ,aAAa;AACxC;AAOO,SAAS,YAAY,KAAyB;AACpD,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AAC1D,UAAM,IAAI,SAAS,gCAAgC,SAAS,aAAa;AAAA,EAC1E;AACA,QAAM,QAAQ;AAKd,MAAI,CAAC,mBAAmB;AACvB,eAAW,OAAO,wBAAwB;AACzC,UAAI,OAAO,OAAO;AACjB,4BAAoB;AACpB,kBAAU,eAAe,GAAG,0CAA0C,qBAAqB,GAAG,CAAC,IAAI,KAAK;AAAA,MACzG;AAAA,IACD;AAAA,EACD;AAEA,QAAM,MAAiB,CAAC;AACxB,aAAW,OAAO,aAAa;AAC9B,QAAI,EAAE,OAAO,OAAQ;AACrB,UAAM,QAAQ,MAAM,GAAG;AACvB,QAAI;AACH,YAAM,UAAU,kBAAkB,KAAK,OAAO,KAAK,CAAC;AAGpD,aAAO,OAAO,KAAK,EAAE,CAAC,GAAG,GAAG,QAAQ,CAAC;AAAA,IACtC,SAAS,KAAK;AACb,UAAI,eAAe,UAAU;AAC5B,cAAM,IAAI,SAAS,eAAe,GAAG,cAAc,IAAI,OAAO,IAAI,SAAS,aAAa;AAAA,MACzF;AACA,YAAM;AAAA,IACP;AAAA,EACD;AACA,SAAO;AACR;AAEO,SAAS,aAAwB;AACvC,QAAM,OAAO,cAAc;AAC3B,MAAI,KAAC,2BAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACJ,MAAI;AACH,cAAM,6BAAa,MAAM,MAAM;AAAA,EAChC,SAAS,KAAK;AACb,UAAM,IAAI,SAAS,4BAA4B,IAAI,KAAM,IAAc,OAAO,IAAI,SAAS,aAAa;AAAA,EACzG;AACA,MAAI;AACJ,MAAI;AACH,aAAS,KAAK,MAAM,GAAG;AAAA,EACxB,SAAS,KAAK;AACb,UAAM,IAAI,SAAS,2BAA2B,IAAI,KAAM,IAAc,OAAO,IAAI,SAAS,aAAa;AAAA,EACxG;AACA,SAAO,YAAY,MAAM;AAC1B;AAEO,SAAS,WAAW,QAAyB;AACnD,QAAM,OAAO,cAAc;AAC3B,oCAAU,0BAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAI5C,QAAM,UAAU,GAAG,IAAI,QAAQ,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AACxD,QAAM,SAAK,yBAAS,SAAS,KAAK,GAAK;AACvC,MAAI;AACH,kCAAU,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAAA,EACrD,SAAS,KAAK;AACb,kCAAU,EAAE;AACZ,QAAI;AACH,qCAAW,OAAO;AAAA,IACnB,QAAQ;AAAA,IAER;AACA,UAAM;AAAA,EACP;AACA,gCAAU,EAAE;AAKZ,MAAI,QAAQ,aAAa,SAAS;AACjC,QAAI;AACH,oCAAU,SAAS,GAAK;AAAA,IACzB,SAAS,KAAK;AACb;AAAA,QACC,2CAA4C,IAAc,OAAO;AAAA,QAEjE;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAEA,iCAAW,SAAS,IAAI;AACzB;AAEO,SAAS,kBAAuC,KAAQ,OAAkC;AAChG,UAAQ,KAAK;AAAA,IACZ,KAAK,UAAU;AACd,UAAI,CAAE,gBAAsC,SAAS,KAAK,GAAG;AAC5D,cAAM,IAAI;AAAA,UACT,mBAAmB,KAAK,eAAe,gBAAgB,KAAK,IAAI,CAAC;AAAA,UACjE,SAAS;AAAA,QACV;AAAA,MACD;AACA,aAAO;AAAA,IACR;AAAA,IACA,KAAK,UAAU;AACd,UAAI,CAAE,eAAqC,SAAS,KAAK,GAAG;AAC3D,cAAM,IAAI;AAAA,UACT,mBAAmB,KAAK,eAAe,eAAe,KAAK,IAAI,CAAC;AAAA,UAChE,SAAS;AAAA,QACV;AAAA,MACD;AACA,aAAO;AAAA,IACR;AAAA,IACA,KAAK,cAAc;AAClB,YAAM,IAAI,OAAO,KAAK;AACtB,UAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,OAAO,IAAI,KAAS;AACnD,cAAM,IAAI;AAAA,UACT,6DAA6D,KAAK;AAAA,UAClE,SAAS;AAAA,QACV;AAAA,MACD;AACA,aAAO;AAAA,IACR;AAAA,EACD;AACD;;;ACxMA,IAAM,mBAA2C;AAAA,EAChD,MAAM;AAAA,EACN,KAAK;AACN;AAGO,SAAS,iBAAyB;AACxC,SAAO,OAAkC,SAAc;AACxD;AAGO,SAAS,eAAe,SAAyB;AACvD,SAAO,iBAAiB,OAAO,KAAK,iBAAiB;AACtD;AAGO,SAAS,gBAA+B;AAC9C,QAAM,UAAU,eAAe;AAC/B,MAAI,YAAY,OAAQ,QAAO;AAC/B,SAAO,IAAI,OAAO;AAAA;AACnB;;;ACVA,IAAM,mBAAmB,eAAe,eAAe,CAAC;AACjD,IAAM,mBAAmB;AAGzB,IAAM,aAAa;AAC1B,IAAM,iBAAyB;AAC/B,IAAM,qBAAqB;AAmBpB,SAAS,eAAe,SAAmC;AACjE,QAAM,UAAU,QAAQ,gBAAgB;AACxC,QAAM,SAAS,WAAW;AAE1B,SAAO;AAAA,IACN,SAAS,eAAe,QAAQ,GAAG;AAAA,IACnC,QAAQ,cAAc,QAAQ,MAAM,MAAM;AAAA,IAC1C,WAAW,eAAe,QAAQ,SAAS,MAAM;AAAA,IACjD,QAAQ,oBAAoB,QAAQ,MAAM;AAAA,IAC1C,OAAO,eAAe,QAAQ,UAAU,KAAK;AAAA,IAC7C;AAAA,EACD;AACD;AAOA,SAAS,eAAe,UAAsC;AAC7D,QAAM,UAAU,QAAQ,IAAI,gBAAgB,GAAG,KAAK;AACpD,MAAI,aAAa,OAAW,QAAO,gBAAgB,UAAU,OAAO;AACpE,MAAI,QAAS,QAAO,gBAAgB,SAAS,GAAG,gBAAgB,UAAU;AAC1E,SAAO;AACR;AAEA,SAAS,gBAAgB,KAAa,QAAwB;AAC7D,MAAI;AACJ,MAAI;AACH,UAAM,IAAI,IAAI,GAAG;AAAA,EAClB,QAAQ;AACP,UAAM,IAAI,SAAS,6BAA6B,MAAM,KAAK,GAAG,IAAI,SAAS,eAAe;AAAA,EAC3F;AACA,MAAI,IAAI,aAAa,YAAY,IAAI,aAAa,SAAS;AAC1D,UAAM,IAAI;AAAA,MACT,qBAAqB,MAAM,+BAA+B,IAAI,QAAQ;AAAA,MACtE,SAAS;AAAA,IACV;AAAA,EACD;AACA,SAAO,IAAI,QAAQ,OAAO,EAAE;AAC7B;AAEA,SAAS,cAAc,UAA8B,QAA2B;AAC/E,QAAM,MAAM,YAAY,OAAO,UAAU;AACzC,MAAI,CAAE,gBAAsC,SAAS,GAAG,GAAG;AAC1D,UAAM,IAAI,SAAS,mBAAmB,GAAG,eAAe,gBAAgB,KAAK,IAAI,CAAC,IAAI,SAAS,eAAe;AAAA,EAC/G;AACA,SAAO;AACR;AAEA,SAAS,eAAe,UAA8B,QAA2B;AAChF,QAAM,MAAM,YAAY,OAAO,cAAc;AAC7C,MAAI,CAAC,OAAO,UAAU,GAAG,KAAK,MAAM,OAAO,MAAM,KAAS;AACzD,UAAM,IAAI,SAAS,qBAAqB,GAAG,kCAAkC,SAAS,eAAe;AAAA,EACtG;AACA,SAAO;AACR;;;ACnFO,SAAS,QAAQ,KAAc,YAA6B;AAClE,QAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,MAAI,MAAe;AACnB,aAAW,KAAK,OAAO;AACtB,QAAI,OAAO,OAAO,QAAQ,YAAY,OAAO,UAAU,eAAe,KAAK,KAAK,CAAC,GAAG;AACnF,YAAO,IAAgC,CAAC;AAAA,IACzC,OAAO;AACN,aAAO;AAAA,IACR;AAAA,EACD;AACA,SAAO;AACR;AAUO,SAAS,UAAU,KAAcC,QAA0C;AACjF,QAAM,MAA+B,CAAC;AACtC,aAAW,KAAKA,QAAO;AACtB,UAAM,IAAI,QAAQ,KAAK,CAAC;AACxB,QAAI,CAAC,IAAI,MAAM,SAAY,OAAO;AAAA,EACnC;AACA,SAAO;AACR;AAMO,SAAS,gBAAgB,KAAuB;AACtD,QAAM,SAAS,IACb,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAChB,MAAI,OAAO,WAAW,GAAG;AACxB,UAAM,IAAI,SAAS,6CAA6C,SAAS,eAAe;AAAA,EACzF;AACA,SAAO;AACR;;;AJ5BA,IAAM,wBAAwB,CAAC,UAAU,SAAS,gBAAgB,gBAAgB,gBAAgB;AAwB3F,SAAS,mBAAmB,QAAuB;AACzD,SACE,QAAQ,QAAQ,EAChB;AAAA,IACA;AAAA,EAGD,EACC,OAAO,wBAAwB,4CAA4C,EAC3E,OAAO,4BAA4B,yCAAyC,EAC5E,OAAO,4BAA4B,sCAAsC,EACzE,OAAO,kBAAkB,2BAA2B,CAAC,MAAM,OAAO,CAAC,CAAC,EACpE,OAAO,uBAAuB,mCAAmC,CAAC,MAAM,OAAO,CAAC,CAAC,EACjF,OAAO,uBAAuB,6DAA6D,EAC3F;AAAA,IACA;AAAA,IACA;AAAA,EACD,EACC,OAAO,aAAa,0BAA0B,sBAAsB,KAAK,GAAG,CAAC,qBAAqB,EAClG,OAAO,OAAO,OAAoB,YAAqB;AACvD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,SAAS,oBAAoB,KAAK;AACxC,UAAM,QAAQ,WAAW,KAAK;AAE9B,UAAM,aAAS,8BAAgB;AAAA,MAC9B,SAAS,IAAI;AAAA,MACb,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf,eAAW,6BAAe,aAAa,OAAe;AAAA,MACtD,QAAQ;AAAA,IACT,CAAC;AAGD,UAAM,EAAE,MAAM,OAAO,SAAS,IAAI,MAAM,OAAO,IAAI,oBAAoB;AAAA,MACtE,QAAQ,EAAE,MAAM;AAAA,IACjB,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,sCAAmB,SAAS,QAAQ,KAAK;AAE3D,UAAM,YAAQ,8BAA+B,IAAI;AAEjD,QAAI,IAAI,WAAW,QAAQ;AAI1B,YAAM,OAAO,SAAS,EAAE,GAAG,OAAO,MAAM,MAAM,KAAK,IAAI,CAAC,SAAS,UAAU,MAAM,MAAM,CAAC,EAAE,IAAI;AAC9F,gBAAU,IAAI;AACd;AAAA,IACD;AAIA,QAAI,QAAQ;AACX,gBAAU,uFAAuF,IAAI,KAAK;AAAA,IAC3G;AAEA;AAAA,MACC,MAAM;AAAA,MACN;AAAA,QACC,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,SAAS,EAAE,UAAU,IAAI,EAAE,EAAE;AAAA,QAC/D,EAAE,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,UAAU,GAAG;AAAA,QAC7D,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAM,EAAE,gBAAgB,IAAI,UAAU,GAAG;AAAA,QACtE,EAAE,QAAQ,YAAY,OAAO,CAAC,MAAM,EAAE,gBAAgB,IAAI,UAAU,GAAG;AAAA,QACvE,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,EAAE,kBAAkB,IAAI,UAAU,GAAG;AAAA,QACvE,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAM,WAAW,EAAE,UAAU,GAAG,UAAU,GAAG;AAAA,MAC3E;AAAA,MACA,IAAI;AAAA,IACL;AAEA,UAAM,OAAO,gBAAgB,MAAM,WAAW,IAAI,MAAM,UAAU,KAAK,MAAM,KAAK,MAAM,OAAO,MAAM,UAAU;AAC/G,UAAM,OACL,MAAM,aAAa,MAAM,cAAc,oCAAoC,MAAM,cAAc,CAAC,KAAK;AACtG,YAAQ,OAAO,MAAM,IAAI,OAAO,MAAM,IAAI,KAAK,IAAI,IAAI;AAAA,EACxD,CAAC;AACH;AAEA,SAAS,oBAAoB,OAA0C;AACtE,MAAI,MAAM,UAAU,MAAM,SAAS;AAClC,UAAM,IAAI,SAAS,8CAA8C,SAAS,eAAe;AAAA,EAC1F;AACA,MAAI,MAAM,QAAS,QAAO,CAAC,GAAG,qBAAqB;AACnD,MAAI,MAAM,OAAQ,QAAO,gBAAgB,MAAM,MAAM;AACrD,SAAO;AACR;AAEA,SAAS,WAAW,OAAiC;AACpD,MAAI,MAAM,WAAW;AAGpB,WAAO,cAAc,MAAM,SAAS;AAAA,EACrC;AACA,QAAM,IAAiB,CAAC;AACxB,MAAI,MAAM,QAAS,GAAE,UAAU,MAAM;AACrC,MAAI,MAAM,UAAU,OAAQ,GAAE,aAAa,MAAM;AACjD,MAAI,MAAM,UAAU,OAAQ,GAAE,2BAA2B,MAAM;AAC/D,MAAI,MAAM,SAAS,OAAW,GAAE,cAAc,MAAM;AACpD,MAAI,MAAM,aAAa,OAAW,GAAE,WAAW,MAAM;AACrD,SAAO;AACR;AAEA,SAAS,cAAc,MAAuC;AAC7D,MAAI;AACJ,MAAI;AACH,cAAM,8BAAa,MAAM,MAAM;AAAA,EAChC,SAAS,KAAK;AACb,UAAM,IAAI,SAAS,iCAAiC,IAAI,KAAM,IAAc,OAAO,IAAI,SAAS,eAAe;AAAA,EAChH;AACA,MAAI;AACH,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AACnE,YAAM,IAAI;AAAA,QACT,MAAM,QAAQ,MAAM,IACjB,gEACA;AAAA,MACJ;AAAA,IACD;AACA,WAAO;AAAA,EACR,SAAS,KAAK;AACb,UAAM,IAAI,SAAS,mBAAmB,IAAI,KAAM,IAAc,OAAO,IAAI,SAAS,eAAe;AAAA,EAClG;AACD;AAEA,SAAS,SAAS,GAAW,KAAqB;AAGjD,MAAI,EAAE,UAAU,IAAK,QAAO;AAI5B,QAAM,QAAQ,MAAM,KAAK,CAAC;AAC1B,MAAI,MAAM,UAAU,IAAK,QAAO;AAChC,SAAO,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI;AAC3C;AAEA,SAAS,WAAW,GAA+B;AAClD,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,uBAAuB,KAAK,CAAC;AACvC,SAAO,IAAI,EAAE,CAAC,IAAI;AACnB;;;AKzLA,IAAAC,eAMO;AAKP,IAAAC,eAAmC;;;ACZnC,IAAAC,kBAAuC;AAiChC,SAAS,iBAAiB,OAAqB;AACrD,MAAI,QAAQ,MAAM,OAAO;AACxB,UAAM,IAAI,qBAAqB,GAAG,KAAK,gEAAgE;AAAA,EACxG;AACD;AAGA,IAAM,2BAA2B;AA2B1B,SAAS,eAAe,OAAe,UAAiC,CAAC,GAAW;AAC1F,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,MAAM,QAAQ,OAAO,KAAK;AAChC,mBAAiB,KAAK;AACtB,QAAM,SAAmB,CAAC;AAC1B,QAAM,MAAM,OAAO,MAAM,KAAK,IAAI;AAClC,QAAM,UAAU,IAAI,WAAW,IAAI,kBAAkB,CAAC,CAAC;AACvD,QAAM,WAAW,IAAI,IAAI;AACzB,aAAS;AACR,QAAI;AACJ,QAAI;AACH,sBAAY,0BAAS,GAAG,KAAK,GAAG,IAAI,QAAQ,IAAI;AAAA,IACjD,SAAS,KAAK;AACb,YAAM,OAAQ,IAA8B;AAC5C,UAAI,SAAS,UAAU;AACtB,YAAI,IAAI,IAAI,UAAU;AACrB,gBAAM,IAAI,qBAAqB,GAAG,KAAK,qBAAqB,SAAS,4BAA4B;AAAA,QAClG;AACA,gBAAQ,KAAK,SAAS,GAAG,GAAG,CAAC;AAC7B;AAAA,MACD;AACA,UAAI,SAAS,MAAO;AACpB,YAAM,IAAI,qBAAqB,GAAG,KAAK,2BAA4B,IAAc,OAAO,EAAE;AAAA,IAC3F;AACA,QAAI,cAAc,EAAG;AACrB,WAAO,KAAK,OAAO,KAAK,IAAI,SAAS,GAAG,SAAS,CAAC,CAAC;AAAA,EACpD;AACA,SAAO,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM;AAC7C;AAWO,SAAS,aAAa,YAAoB,UAA+B,CAAC,GAAoB;AACpG,QAAM,YAAY,QAAQ,cAAc,CAAC,UAAkB,eAAe,KAAK;AAC/E,MAAI,CAAC,QAAQ,MAAM,SAAS,CAAC,QAAQ,OAAO,OAAO;AAClD,WAAO,QAAQ,QAAQ,UAAU,OAAO,EAAE,KAAK,CAAC;AAAA,EACjD;AACA,UAAQ,OAAO,MAAM,UAAU;AAC/B,SAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC/C,UAAM,QAAQ,QAAQ;AACtB,UAAM,WAAW,IAAI;AACrB,UAAM,OAAO;AACb,UAAM,YAAY,MAAM;AACxB,QAAI,MAAM;AACV,UAAM,UAAU,MAAY;AAC3B,YAAM,WAAW,KAAK;AACtB,YAAM,MAAM;AACZ,YAAM,IAAI,QAAQ,MAAM;AAAA,IACzB;AACA,UAAM,SAAS,CAAC,UAAwB;AACvC,iBAAW,MAAM,OAAO;AACvB,YAAI,OAAO,KAAK;AAEf,kBAAQ;AACR,kBAAQ,OAAO,MAAM,IAAI;AACzB,iBAAO,IAAI,SAAS,WAAW,SAAS,eAAe,CAAC;AACxD;AAAA,QACD;AACA,YAAI,OAAO,QAAQ,OAAO,MAAM;AAC/B,kBAAQ;AACR,kBAAQ,OAAO,MAAM,IAAI;AACzB,kBAAQ,IAAI,KAAK,CAAC;AAClB;AAAA,QACD;AACA,YAAI,OAAO,UAAO,OAAO,MAAM;AAC9B,gBAAM,IAAI,MAAM,GAAG,EAAE;AACrB;AAAA,QACD;AACA,eAAO;AAAA,MACR;AAAA,IACD;AACA,UAAM,GAAG,QAAQ,MAAM;AAAA,EACxB,CAAC;AACF;AAgBO,SAAS,cAAc,QAAgB,UAAgC,CAAC,GAAY;AAC1F,QAAM,YAAY,QAAQ,cAAc,CAAC,UAAkB,eAAe,OAAO,EAAE,WAAW,QAAQ,UAAU,CAAC;AACjH,MAAI;AACJ,MAAI,WAAW,KAAK;AACnB,UAAM,UAAU,UAAU;AAAA,EAC3B,OAAO;AACN,QAAI;AACH,gBAAM,8BAAa,QAAQ,MAAM;AAAA,IAClC,SAAS,KAAK;AACb,YAAM,IAAI;AAAA,QACT,uBAAuB,MAAM,MAAO,IAA8B,QAAS,IAAc,OAAO;AAAA,MACjG;AAAA,IACD;AAAA,EACD;AACA,MAAI,CAAC,IAAI,KAAK,GAAG;AAChB,UAAM,IAAI,qBAAqB,4CAAuC;AAAA,EACvE;AACA,MAAI;AACH,WAAO,KAAK,MAAM,GAAG;AAAA,EACtB,QAAQ;AAGP,UAAM,IAAI,qBAAqB,sCAAsC;AAAA,EACtE;AACD;AAOO,SAAS,cAAc,QAAgB,UAAgC,CAAC,GAAW;AACzF,QAAM,YAAY,QAAQ,cAAc,CAAC,UAAkB,eAAe,OAAO,EAAE,WAAW,QAAQ,UAAU,CAAC;AACjH,MAAI;AACJ,MAAI,WAAW,KAAK;AACnB,UAAM,UAAU,eAAe;AAAA,EAChC,OAAO;AACN,QAAI;AACH,gBAAM,8BAAa,QAAQ,MAAM;AAAA,IAClC,SAAS,KAAK;AACb,YAAM,IAAI;AAAA,QACT,4BAA4B,MAAM,MAAO,IAA8B,QAAS,IAAc,OAAO;AAAA,MACtG;AAAA,IACD;AAAA,EACD;AACA,MAAI,CAAC,IAAI,KAAK,GAAG;AAChB,UAAM,IAAI,qBAAqB,kDAA6C;AAAA,EAC7E;AACA,SAAO;AACR;AAMO,SAAS,eAAe,QAAgB,UAAgC,CAAC,GAA4B;AAC3G,QAAM,SAAS,cAAc,QAAQ,OAAO;AAC5C,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AACnE,UAAM,IAAI;AAAA,MACT,oCAAoC,MAAM,QAAQ,MAAM,IAAI,aAAa,OAAO,MAAM;AAAA,IACvF;AAAA,EACD;AACA,SAAO;AACR;;;ADpNA,IAAAC,qBAAe;AASf,IAAM,4BAA4B;AAClC,IAAM,wBAAwB;AA0CvB,SAAS,iBAAiB,QAAuB;AACvD,SACE,QAAQ,eAAe,EACvB,YAAY,wDAAwD,EACpE,OAAO,kBAAkB,8EAA8E,EACvG;AAAA,IACA;AAAA,IACA;AAAA,EACD,EACC;AAAA,IACA;AAAA,IACA;AAAA,EACD,EACC;AAAA,IACA;AAAA,IACA,kDAAkD,yBAAyB,SAAS,qBAAqB;AAAA,IACzG,CAAC,MAAM,OAAO,CAAC;AAAA,EAChB,EACC,OAAO,OAAO,UAAkB,OAAkB,YAAqB;AACvE,UAAM,MAAM,eAAe,OAAO;AAClC,QAAI,MAAM,SAAS,MAAM,QAAQ;AAChC,YAAM,IAAI;AAAA,QACT;AAAA,QACA,SAAS;AAAA,MACV;AAAA,IACD;AAEA,UAAM,aAAS,8BAAgB;AAAA,MAC9B,SAAS,IAAI;AAAA,MACb,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf,eAAW,6BAAe,aAAa,OAAe;AAAA,MACtD,QAAQ;AAAA,IACT,CAAC;AAED,QAAI,MAAM,OAAO;AAChB,YAAM,aAAa,UAAU,OAAO,QAAQ,IAAI,SAAS;AACzD;AAAA,IACD;AAEA,UAAM,QAAQ,aAAa,MAAM,eAAe,UAAU,EAAE,WAAW,IAAI,UAAU,CAAC,EAAE,KAAK,IAAI;AACjG,QAAI,CAAC,OAAO;AACX,YAAM,IAAI,SAAS,sBAAsB,SAAS,eAAe;AAAA,IAClE;AAEA,UAAM,EAAE,MAAM,OAAO,SAAS,IAAI,MAAM,OAAO,IAAI,0BAA0B;AAAA,MAC5E,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE;AAAA,IAC3B,CAAC;AACD,QAAI,CAAC,SAAS,GAAI,sCAAmB,SAAS,QAAQ,KAAK;AAE3D,UAAM,UAAM,iCAA4B,IAAI;AAE5C,QAAI,MAAM,QAAQ;AAKjB,gBAAU,UAAU,KAAK,gBAAgB,MAAM,MAAM,CAAC,CAAC;AACvD;AAAA,IACD;AAEA,QAAI,MAAM,OAAO;AAChB,YAAM,IAAI,QAAQ,KAAK,MAAM,KAAK;AAClC,UAAI,MAAM,QAAW;AACpB,cAAM,IAAI,SAAS,UAAU,MAAM,KAAK,6BAA6B,SAAS,eAAe;AAAA,MAC9F;AAKA,YAAM,MAAM,OAAO,MAAM,WAAW,6BAA6B,CAAC,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC;AAC/F,cAAQ,OAAO,MAAM,MAAM,IAAI;AAC/B;AAAA,IACD;AAEA,QAAI,IAAI,WAAW,QAAQ;AAC1B,gBAAU,GAAG;AACb;AAAA,IACD;AAEA,mBAAe,KAAK,OAAO,IAAI,KAAK;AAAA,EACrC,CAAC;AACH;AAOA,eAAe,aAAa,UAAkB,OAAkB,QAAmB,WAAkC;AACpH,MAAI,aAAa,KAAK;AACrB,UAAM,IAAI,SAAS,qEAAqE,SAAS,eAAe;AAAA,EACjH;AACA,QAAM,SAAS,gBAAgB,eAAe,kBAAkB,EAAE,UAAU,CAAC,CAAC;AAC9E,MAAI,OAAO,WAAW,GAAG;AACxB,UAAM,IAAI,SAAS,6BAA6B,SAAS,eAAe;AAAA,EACzE;AACA,QAAM,cAAc,wBAAwB,MAAM,WAAW;AAC7D,QAAM,UAAU,mBAAmB,KAAK;AACxC,QAAM,UAAU,MAAM,SAAS,QAAQ,aAAa,CAAC,UAAU,SAAS,QAAQ,KAAK,GAAG,OAAO;AAC/F,aAAW,UAAU,QAAS,iBAAgB,MAAM;AACrD;AAEA,eAAe,SAAS,QAAmB,OAAiC;AAC3E,QAAM,EAAE,MAAM,OAAO,SAAS,IAAI,MAAM,OAAO,IAAI,0BAA0B;AAAA,IAC5E,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE;AAAA,EAC3B,CAAC;AACD,MAAI,CAAC,SAAS,GAAI,sCAAmB,SAAS,QAAQ,KAAK;AAC3D,aAAO,iCAA4B,IAAI;AACxC;AAOA,eAAe,SACd,QACA,aACA,UACA,SACyB;AACzB,aAAO,iCAAmB,QAAQ,aAAa,OAAO,UAAgC;AACrF,QAAI;AACH,YAAM,MAAM,MAAM,SAAS,KAAK;AAChC,aAAO,EAAE,QAAQ,OAAO,IAAI,MAAM,MAAM,QAAQ,GAAG,EAAE;AAAA,IACtD,SAAS,KAAK;AACb,aAAO,EAAE,QAAQ,OAAO,IAAI,OAAO,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,IAC5F;AAAA,EACD,CAAC;AACF;AAEA,SAAS,mBAAmB,OAAgC;AAC3D,MAAI,MAAM,QAAQ;AACjB,UAAMC,SAAQ,gBAAgB,MAAM,MAAM;AAC1C,WAAO,CAAC,QAAQ,UAAU,KAAKA,MAAK;AAAA,EACrC;AACA,MAAI,MAAM,OAAO;AAChB,UAAM,OAAO,MAAM;AAGnB,WAAO,CAAC,QAAQ,QAAQ,KAAK,IAAI,KAAK;AAAA,EACvC;AACA,SAAO,CAAC,QAAQ;AACjB;AAEA,SAAS,gBAAgB,KAAuB;AAC/C,SAAO,IACL,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AACjB;AAEA,SAAS,wBAAwB,KAAiC;AACjE,QAAM,IAAI,OAAO;AACjB,MAAI,CAAC,OAAO,UAAU,CAAC,KAAK,IAAI,KAAK,IAAI,uBAAuB;AAC/D,UAAM,IAAI;AAAA,MACT,kDAAkD,qBAAqB,SAAS,GAAG;AAAA,MACnF,SAAS;AAAA,IACV;AAAA,EACD;AACA,SAAO;AACR;AAEA,SAAS,eAAe,KAAc,OAAe,OAAsB;AAC1E,QAAM,QAAQ,CAACC,OAAe,QAAQ,mBAAAC,QAAG,KAAKD,EAAC,IAAIA;AAGnD,QAAM,IAAI,CAAC,MAA0C,IAAI,oBAAoB,CAAC,IAAI;AAClF,QAAM,OAAO,IAAI,YAAY,CAAC;AAC9B,QAAM,UAAU,IAAI,uBAAuB,CAAC;AAE5C,QAAM,QAAkB,CAAC;AACzB,MAAI,KAAK,UAAW,OAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,KAAK,SAAS,CAAC,EAAE;AAC7E,MAAI,QAAQ,aAAc,OAAM,KAAK,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,QAAQ,YAAY,CAAC,EAAE;AACzF,MAAI,KAAK,aAAc,OAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,YAAY,CAAC,EAAE;AACnF,MAAI,KAAK,eAAgB,OAAM,KAAK,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE,KAAK,cAAc,CAAC,EAAE;AACvF,MAAI,KAAK,oBAAqB,OAAM,KAAK,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,KAAK,mBAAmB,CAAC,EAAE;AACjG,MAAI,KAAK,mBAAoB,OAAM,KAAK,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,KAAK,kBAAkB,CAAC,EAAE;AAE/F,QAAM,KAAK,IAAI,eAAe,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC;AAChD,MAAI,QAAQ,eAAgB,OAAM,KAAK,IAAI,mBAAmB,EAAE,QAAQ,cAAc,CAAC,IAAI,KAAK,CAAC;AACjG,UAAQ,OAAO,MAAM,MAAM,KAAK,IAAI,IAAI,IAAI;AAE5C,MAAI,IAAI,iBAAiB;AACxB,YAAQ,OAAO,MAAM,OAAO,MAAM,aAAa,IAAI,IAAI;AAKvD,YAAQ,OAAO,MAAM,kBAAkB,IAAI,eAAe,IAAI,IAAI;AAAA,EACnE;AAEA,UAAQ,OAAO;AAAA,IACd,OACC;AAAA,MACC;AAAA,MACA;AAAA,IACD,IACA;AAAA,EACF;AACD;AAMA,SAAS,UAAU,GAAmB;AACrC,SAAO,EACL,QAAQ,oCAAoC,IAAI,EAChD,QAAQ,YAAY,EAAE,EACtB,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG,EACrB,QAAQ,SAAS,GAAG,EACpB,QAAQ,SAAS,GAAG,EACpB,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG,EACrB,QAAQ,WAAW,MAAM,EACzB,KAAK;AACR;AAEA,SAAS,kBAAkB,MAAsB;AAChD,SAAO,6BAA6B,UAAU,IAAI,CAAC;AACpD;;;AE7RO,SAAS,oBAAoBE,UAAwB;AAC3D,QAAM,OAAOA,SAAQ,QAAQ,MAAM,EAAE,YAAY,qCAAqC;AACtF,qBAAmB,IAAI;AACvB,mBAAiB,IAAI;AACtB;;;ACHO,SAAS,kBAAkB,QAAuB;AACxD,SACE,QAAQ,mBAAmB,EAC3B,YAAY,sDAAsD,EAClE,OAAO,CAAC,KAAa,UAAkB;AACvC,QAAI,sBAAsB,GAAG,GAAG;AAC/B,YAAM,IAAI;AAAA,QACT,eAAe,GAAG,mCAAmC,gBAAgB;AAAA,QAErE,SAAS;AAAA,MACV;AAAA,IACD;AACA,QAAI,CAAC,YAAY,GAAG,GAAG;AACtB,YAAM,IAAI;AAAA,QACT,uBAAuB,GAAG;AAAA,QAC1B,SAAS;AAAA,MACV;AAAA,IACD;AACA,UAAM,UAAU,kBAAkB,KAAK,KAAK;AAC5C,UAAM,SAAS,WAAW;AAG1B,WAAO,OAAO,QAAQ,EAAE,CAAC,GAAG,GAAG,QAAQ,CAAC;AACxC,eAAW,MAAM;AACjB,YAAQ,OAAO,MAAM,OAAO,GAAG,MAAM,KAAK,UAAU,OAAO,CAAC;AAAA,CAAI;AAAA,EACjE,CAAC;AACH;;;AC1BO,SAAS,kBAAkB,QAAuB;AACxD,SACE,QAAQ,WAAW,EACnB,YAAY,sEAAsE,EAClF,OAAO,CAAC,QAA4B;AACpC,UAAM,SAAS,WAAW;AAC1B,QAAI,QAAQ,QAAW;AACtB,gBAAU,MAAM;AAChB;AAAA,IACD;AACA,QAAI,CAAC,YAAY,GAAG,GAAG;AACtB,YAAM,IAAI;AAAA,QACT,uBAAuB,GAAG;AAAA,QAC1B,SAAS;AAAA,MACV;AAAA,IACD;AACA,UAAM,QAAS,OAAmC,GAAG;AACrD,QAAI,UAAU,QAAW;AACxB,cAAQ,OAAO,MAAM,IAAI;AACzB;AAAA,IACD;AACA,YAAQ,OAAO,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK,KAAK,IAAI;AAAA,EACxF,CAAC;AACH;;;ACzBO,SAAS,mBAAmB,QAAuB;AACzD,SACE,QAAQ,MAAM,EACd,YAAY,qEAAqE,EACjF,OAAO,MAAM;AACb,YAAQ,OAAO,MAAM,cAAc,IAAI,IAAI;AAAA,EAC5C,CAAC;AACH;;;ACTA,IAAAC,kBAAuC;AAIhC,SAAS,oBAAoB,QAAuB;AAC1D,SACE,QAAQ,OAAO,EACf,YAAY,wBAAwB,EACpC,OAAO,eAAe,8BAA8B,EACpD,OAAO,OAAO,SAA8B;AAC5C,UAAM,OAAO,cAAc;AAC3B,QAAI,KAAC,4BAAW,IAAI,GAAG;AACtB,cAAQ,OAAO,MAAM,6BAA6B;AAClD;AAAA,IACD;AACA,QAAI,CAAC,KAAK,OAAO;AAChB,YAAM,KAAK,MAAM,YAAY,oBAAoB,IAAI,UAAU;AAC/D,UAAI,CAAC,IAAI;AACR,gBAAQ,OAAO,MAAM,YAAY;AACjC;AAAA,MACD;AAAA,IACD;AACA,QAAI;AACH,sCAAW,IAAI;AAAA,IAChB,SAAS,KAAK;AACb,YAAM,IAAI;AACV,UAAI,EAAE,SAAS,UAAU;AAExB,gBAAQ,OAAO,MAAM,+CAA+C;AACpE;AAAA,MACD;AACA,YAAM,IAAI,qBAAqB,8BAA8B,IAAI,KAAK,EAAE,OAAO,EAAE;AAAA,IAClF;AACA,YAAQ,OAAO,MAAM,WAAW,IAAI;AAAA,CAAI;AAAA,EACzC,CAAC;AACH;AAEA,SAAS,YAAY,QAAkC;AACtD,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC/B,YAAQ,OAAO,MAAM,MAAM;AAC3B,QAAI,MAAM;AACV,YAAQ,MAAM,YAAY,MAAM;AAChC,UAAM,SAAS,CAAC,UAAkB;AACjC,aAAO;AACP,YAAM,KAAK,IAAI,QAAQ,IAAI;AAC3B,UAAI,MAAM,GAAG;AACZ,gBAAQ;AACR,cAAM,SAAS,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY;AACnD,gBAAQ,WAAW,OAAO,WAAW,KAAK;AAAA,MAC3C;AAAA,IACD;AACA,UAAM,QAAQ,MAAM;AACnB,cAAQ;AACR,cAAQ,OAAO,MAAM,gCAA2B;AAChD,cAAQ,KAAK;AAAA,IACd;AACA,UAAM,UAAU,MAAM;AACrB,cAAQ,MAAM,eAAe,QAAQ,MAAM;AAC3C,cAAQ,MAAM,eAAe,OAAO,KAAK;AACzC,cAAQ,MAAM,MAAM;AAAA,IACrB;AACA,YAAQ,MAAM,GAAG,QAAQ,MAAM;AAC/B,YAAQ,MAAM,GAAG,OAAO,KAAK;AAAA,EAC9B,CAAC;AACF;;;AC3DO,SAAS,sBAAsBC,UAAwB;AAC7D,QAAM,SAASA,SAAQ,QAAQ,QAAQ,EAAE,YAAY,0BAA0B;AAC/E,oBAAkB,MAAM;AACxB,oBAAkB,MAAM;AACxB,qBAAmB,MAAM;AACzB,sBAAoB,MAAM;AAC3B;;;ACXA,IAAAC,kBAA2B;AAC3B,IAAAC,eAAgD;;;ACFhD,IAAAC,kBAUO;AACP,IAAAC,oBAA8B;AAC9B,IAAAC,oBAAqB;AAKd,IAAM,kBAAkB;AACxB,IAAM,aAAa;AAG1B,IAAM,iBAAiB,WAAW,SAAS;AAoC3C,IAAMC,aAAQ,kBAAAC,SAAS,SAAS,EAAE,QAAQ,GAAG,CAAC;AAKvC,SAAS,qBAA6B;AAC5C,QAAM,WAAW,eAAe,MAAM,QAAQ,yBAAyB;AACvE,aAAO,wBAAKD,OAAM,QAAQ,QAAQ;AACnC;AAEO,SAAS,iBAAiB,KAAsB;AACtD,SAAO,IAAI,WAAW,UAAU,KAAK,IAAI,UAAU,kBAAkB,CAAC,KAAK,KAAK,GAAG;AACpF;AAGO,SAAS,QAAQ,KAAqB;AAC5C,SAAO,GAAG,UAAU,2BAAO,IAAI,MAAM,EAAE,CAAC;AACzC;AAUA,SAAS,YAAY,QAAqC;AACzD,QAAM,OAAO,mBAAmB;AAChC,MAAI,KAAC,4BAAW,IAAI,EAAG,QAAO,CAAC;AAC/B,MAAI;AACJ,MAAI;AACH,aAAS,KAAK,UAAM,8BAAa,MAAM,MAAM,CAAC;AAAA,EAC/C,SAAS,KAAK;AACb,QAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,UAAM,IAAI;AAAA,MACT,iCAAiC,IAAI,KAAM,IAAc,OAAO;AAAA,MAChE,SAAS;AAAA,IACV;AAAA,EACD;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AAC1C,QAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,UAAM,IAAI;AAAA,MACT,uBAAuB,IAAI;AAAA,MAC3B,SAAS;AAAA,IACV;AAAA,EACD;AACA,QAAM,MAAM;AAEZ,MAAI,OAAO,IAAI,YAAY,UAAU;AACpC,WAAO,EAAE,YAAY,IAAI;AAAA,EAC1B;AACA,SAAO,EAAE,YAAY,IAAI,YAAY,UAAU,IAAI,SAAS;AAC7D;AAEA,SAAS,2BAA2B,KAA2B;AAC9D,QAAM,OAAO,mBAAmB;AAChC,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,OAAQ,IAAgC,YAAY,UAAU;AACpG,UAAM,IAAI;AAAA,MACT,uBAAuB,IAAI;AAAA,MAC3B,SAAS;AAAA,IACV;AAAA,EACD;AACA,QAAM,IAAI;AACV,QAAM,SAAS,EAAE;AACjB,SAAO;AAAA,IACN,SAAS;AAAA,IACT,cAAc,OAAO,EAAE,iBAAiB,WAAW,EAAE,eAAe;AAAA,IACpE,WAAW,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY,OAAO,MAAM,EAAE;AAAA,IAC1E,UAAU,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAAA,EACzD;AACD;AAIA,SAAS,yBAAyB,KAAmC;AACpE,QAAM,OAAO,mBAAmB;AAChC,MACC,CAAC,OACD,OAAO,QAAQ,YACf,OAAQ,IAAgC,iBAAiB,YACzD,OAAQ,IAAgC,kBAAkB,YAC1D,OAAQ,IAAgC,eAAe,UACtD;AACD,UAAM,IAAI;AAAA,MACT,2BAA2B,IAAI;AAAA,MAC/B,SAAS;AAAA,IACV;AAAA,EACD;AACA,QAAM,IAAI;AACV,SAAO;AAAA,IACN,cAAc,EAAE;AAAA,IAChB,eAAe,EAAE;AAAA,IACjB,YAAY,EAAE;AAAA,IACd,cAAc,OAAO,EAAE,iBAAiB,WAAW,EAAE,eAAe;AAAA,IACpE,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;AAAA,IAC/C,oBAAoB,OAAO,EAAE,uBAAuB,WAAW,EAAE,qBAAqB;AAAA,EACvF;AACD;AAIA,SAAS,oBAAoB,MAAc,SAAuB;AACjE,qCAAU,2BAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,QAAM,UAAU,GAAG,IAAI,QAAQ,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AACxD,QAAM,SAAK,0BAAS,SAAS,KAAK,GAAK;AACvC,MAAI;AACH,mCAAU,IAAI,OAAO;AAAA,EACtB,SAAS,KAAK;AACb,mCAAU,EAAE;AACZ,QAAI;AACH,sCAAW,OAAO;AAAA,IACnB,QAAQ;AAAA,IAER;AACA,UAAM;AAAA,EACP;AACA,iCAAU,EAAE;AACZ,MAAI,QAAQ,aAAa,SAAS;AACjC,QAAI;AACH,qCAAU,SAAS,GAAK;AAAA,IACzB,SAAS,KAAK;AACb;AAAA,QACC,gDAAiD,IAAc,OAAO;AAAA,QAEtE;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACA,kCAAW,SAAS,IAAI;AACzB;AAGA,SAAS,qBAAqB,MAAgC;AAC7D,QAAM,OAAgC,EAAE,SAAS,EAAE;AACnD,MAAI,KAAK,eAAe,OAAW,MAAK,aAAa,KAAK;AAC1D,MAAI,KAAK,aAAa,OAAW,MAAK,WAAW,KAAK;AACtD,sBAAoB,mBAAmB,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,IAAI;AAC/E;AAEO,SAAS,kBAAsC;AACrD,QAAM,EAAE,WAAW,IAAI,YAAY,IAAI;AACvC,MAAI,eAAe,OAAW,QAAO;AACrC,SAAO,2BAA2B,UAAU;AAC7C;AAEO,SAAS,gBAAgB,OAA0B;AACzD,QAAM,EAAE,SAAS,IAAI,YAAY,KAAK;AACtC,uBAAqB,EAAE,YAAY,OAAO,SAAS,CAAC;AACrD;AAEO,SAAS,oBAA6B;AAC5C,QAAM,OAAO,mBAAmB;AAChC,MAAI,KAAC,4BAAW,IAAI,EAAG,QAAO;AAC9B,QAAM,EAAE,YAAY,SAAS,IAAI,YAAY,KAAK;AAKlD,MAAI,eAAe,UAAa,aAAa,QAAW;AACvD,oCAAW,IAAI;AACf,WAAO;AAAA,EACR;AACA,MAAI,eAAe,OAAW,QAAO;AAGrC,MAAI,aAAa,QAAW;AAC3B,oCAAW,IAAI;AAAA,EAChB,OAAO;AACN,yBAAqB,EAAE,SAAS,CAAC;AAAA,EAClC;AACA,SAAO;AACR;AAEO,SAAS,0BAAsD;AACrE,QAAM,EAAE,SAAS,IAAI,YAAY,IAAI;AACrC,MAAI,aAAa,OAAW,QAAO;AACnC,SAAO,yBAAyB,QAAQ;AACzC;AAEO,SAAS,wBAAwB,GAA8B;AACrE,QAAM,EAAE,WAAW,IAAI,YAAY,KAAK;AACxC,uBAAqB,EAAE,YAAY,UAAU,EAAE,CAAC;AACjD;AAEO,SAAS,4BAAqC;AACpD,QAAM,EAAE,YAAY,SAAS,IAAI,YAAY,KAAK;AAClD,MAAI,aAAa,OAAW,QAAO;AACnC,uBAAqB,EAAE,WAAW,CAAC;AACnC,SAAO;AACR;AAMO,SAAS,cAAc,WAAiC;AAC9D,MAAI,cAAc,QAAW;AAC5B,iBAAa,WAAW,WAAW;AACnC,WAAO,EAAE,KAAK,WAAW,QAAQ,OAAO;AAAA,EACzC;AACA,QAAM,UAAU,QAAQ,IAAI,eAAe,GAAG,KAAK;AACnD,MAAI,SAAS;AACZ,iBAAa,SAAS,GAAG,eAAe,UAAU;AAClD,WAAO,EAAE,KAAK,SAAS,QAAQ,MAAM;AAAA,EACtC;AACA,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,MAAO,QAAO,EAAE,KAAK,MAAM,SAAS,QAAQ,OAAO;AACvD,QAAM,IAAI;AAAA,IACT,6DAA6D,eAAe;AAAA,IAC5E,SAAS;AAAA,EACV;AACD;AAEA,SAAS,aAAa,KAAa,QAAsB;AACxD,MAAI,CAAC,iBAAiB,GAAG,GAAG;AAE3B,UAAM,IAAI,SAAS,gBAAgB,MAAM,mBAAmB,UAAU,QAAQ,SAAS,eAAe;AAAA,EACvG;AACD;;;ACpRA,IAAAE,eAAqE;AAErE,IAAAC,eAAwE;AASxE,IAAM,kBAAkB;AASxB,eAAe,UACd,MACA,MACA,MAC6C;AAC7C,QAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,GAAG,IAAI,EAAE;AAC5C,QAAM,UAAU,IAAI,QAAQ,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS;AAAA,MACR,mBAAmB,KAAK;AAAA,MACxB,kBAAc,6BAAe,aAAa,OAAe;AAAA,MACzD,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,gBAAgB;AAAA,IACjB;AAAA,IACA,MAAM,KAAK,UAAU,IAAI;AAAA,EAC1B,CAAC;AACD,QAAM,MAAM,UAAM,+BAAiB,SAAS,KAAK,SAAS;AAC1D,QAAM,WAAoB,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAC3D,SAAO,EAAE,QAAQ,IAAI,QAAQ,MAAM,SAAS;AAC7C;AAGA,SAAS,eAAe,MAA8B;AACrD,MAAI,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAAiC,UAAU,UAAU;AACpG,WAAQ,KAAiC;AAAA,EAC1C;AACA,SAAO;AACR;AAGA,eAAsB,kBAAkB,MAA2B,YAAwD;AAC1H,QAAM,OAAgC,CAAC;AACvC,MAAI,WAAY,MAAK,cAAc;AACnC,QAAM,EAAE,QAAQ,MAAM,SAAS,IAAI,MAAM,UAAU,MAAM,GAAG,uBAAU,gBAAgB,IAAI;AAC1F,MAAI,SAAS,OAAO,UAAU,IAAK,sCAAmB,QAAQ,QAAQ;AACtE,SAAO;AACR;AAYA,eAAsB,aACrB,MACA,QACA,QAAuC,CAAC,OAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC,GACtE;AACzB,MAAI,cAAc,OAAO;AACzB,QAAM,WAAW,KAAK,IAAI,IAAI,OAAO,aAAa;AAElD,aAAS;AACR,UAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,UAAU,MAAM,GAAG,uBAAU,UAAU;AAAA,MACrE,YAAY;AAAA,MACZ,aAAa,OAAO;AAAA,IACrB,CAAC;AACD,QAAI,UAAU,OAAO,SAAS,IAAK,QAAO;AAE1C,UAAM,OAAO,eAAe,IAAI;AAChC,QAAI,SAAS,aAAa;AACzB,qBAAe;AAAA,IAChB,WAAW,SAAS,iBAAiB;AACpC,YAAM,IAAI,SAAS,2CAA2C,SAAS,iBAAiB;AAAA,IACzF,WAAW,SAAS,iBAAiB;AACpC,YAAM,IAAI,SAAS,iBAAiB,SAAS,iBAAiB;AAAA,IAC/D,WAAW,SAAS,yBAAyB;AAG5C,2CAAmB,QAAQ,IAAI;AAAA,IAChC;AAEA,QAAI,KAAK,IAAI,KAAK,SAAU,OAAM,IAAI,SAAS,iBAAiB,SAAS,iBAAiB;AAC1F,UAAM,MAAM,cAAc,GAAI;AAAA,EAC/B;AACD;AAGA,eAAsB,mBAAmB,MAA2B,cAA8C;AACjH,QAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,UAAU,MAAM,GAAG,uBAAU,UAAU;AAAA,IACrE,YAAY;AAAA,IACZ,eAAe;AAAA,EAChB,CAAC;AACD,MAAI,UAAU,OAAO,SAAS,IAAK,QAAO;AAC1C,MAAI,eAAe,IAAI,MAAM,iBAAiB;AAC7C,UAAM,IAAI,SAAS,wEAAwE,SAAS,iBAAiB;AAAA,EACtH;AACA,uCAAmB,QAAQ,IAAI;AAChC;AAGA,eAAsB,mBAAmB,MAA2B,cAAqC;AACxG,QAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,UAAU,MAAM,GAAG,uBAAU,WAAW,EAAE,OAAO,aAAa,CAAC;AAC9F,MAAI,SAAS,OAAO,UAAU,IAAK,sCAAmB,QAAQ,IAAI;AACnE;;;AFvGO,IAAM,wBAAwB,CAAC,WAAW,OAAO;AAUjD,IAAM,mCAAmC;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAEO,SAAS,sBAAsBC,UAAwB;AAC7D,EAAAA,SACE,QAAQ,QAAQ,EAChB;AAAA,IACA;AAAA,EACD,EACC,OAAO,OAAO,OAAgB,YAAqB;AACnD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,YAAY,MAAM,UAAU,GAAG;AACrC,QAAI,CAAC,UAAW,SAAQ,KAAK,SAAS,oBAAoB;AAAA,EAC3D,CAAC;AACH;AAQA,eAAsB,UAAU,KAAwC;AACvE,QAAM,OAAO,CAAC,IAAI,OAAO,QAAQ,OAAO,MAAM,IAAI,IAAI;AAEtD,OAAK,aAAa,OAAe,EAAE;AACnC,OAAK,iCAAiC,cAAe,EAAE;AACvD,OAAK,EAAE;AAEP,OAAK,yBAAyB;AAC9B,OAAK,mBAAmB,IAAI,OAAO,EAAE;AACrC,OAAK,mBAAmB,eAAe,CAAC,EAAE;AAC1C,OAAK,mBAAmB,IAAI,MAAM,EAAE;AACpC,OAAK,mBAAmB,IAAI,SAAS,IAAI;AACzC,QAAM,UAAU,cAAc;AAC9B,OAAK,mBAAmB,OAAO,OAAG,4BAAW,OAAO,IAAI,KAAK,gBAAgB,EAAE;AAC/E,OAAK,EAAE;AAEP,OAAK,2BAA2B;AAChC,aAAW,KAAK,2BAA2B,EAAG,MAAK,KAAK,CAAC,EAAE;AAC3D,OAAK,EAAE;AAEP,OAAK,sBAAsB;AAC3B,QAAM,YAAY,MAAM,YAAY,KAAK,IAAI;AAC7C,QAAM,gBAAgB,KAAK,IAAI;AAC/B,OAAK,EAAE;AAEP,OAAK,gEAAgE;AACrE;AAAA,IACC,gFAA2E,sBAAsB,KAAK,IAAI,CAAC;AAAA,EAC5G;AACA,OAAK,oFAA+E;AACpF,OAAK,8FAAyF;AAC9F,OAAK,EAAE;AAEP,OAAK,+CAA+C;AACpD,aAAW,QAAQ,iCAAkC,MAAK,YAAO,IAAI,EAAE;AACvE,OAAK,EAAE;AAEP,OAAK,eAAe;AACpB,OAAK,qFAAqF;AAC1F,OAAK,mFAAmF;AACxF,OAAK,kGAA6F;AAElG,SAAO;AACR;AAWA,SAAS,6BAAuC;AAC/C,MAAI;AACH,UAAM,QAAQ,wBAAwB;AACtC,QAAI,CAAC,MAAO,QAAO,CAAC,8CAA8C;AAElE,UAAM,cAAc,KAAK,MAAM,MAAM,UAAU;AAC/C,QAAI,OAAO,MAAM,WAAW,GAAG;AAC9B,aAAO;AAAA,QACN;AAAA,QACA,uCAAuC,MAAM,UAAU;AAAA,MACxD;AAAA,IACD;AACA,UAAM,UAAU,eAAe,KAAK,IAAI;AACxC,WAAO;AAAA,MACN;AAAA,MACA,UACG,2BAA2B,MAAM,UAAU,+CAC3C,4BAA4B,MAAM,UAAU;AAAA,IAChD;AAAA,EACD,SAAS,KAAK;AACb,WAAO,CAAC,gDAAgD,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EAC3G;AACD;AAQA,eAAe,YACd,KACA,MACmB;AACnB,MAAI;AACH,UAAM,aAAS,8BAAgB;AAAA,MAC9B,SAAS,IAAI;AAAA,MACb,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,MACf,eAAW,6BAAe,aAAa,OAAe;AAAA,MACtD,QAAQ;AAAA,IACT,CAAC;AACD,UAAM,EAAE,SAAS,IAAI,MAAM,OAAO,IAAI,oBAAoB,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;AAChG,QAAI,SAAS,IAAI;AAChB,WAAK,4BAAuB,SAAS,MAAM,GAAG;AAAA,IAC/C,OAAO;AACN,WAAK,4CAA4C,SAAS,MAAM,EAAE;AAAA,IACnE;AACA,WAAO;AAAA,EACR,SAAS,KAAK;AACb,SAAK,yBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAC3E,WAAO;AAAA,EACR;AACD;AAGA,IAAM,2BAA2B;AASjC,eAAe,gBACd,KACA,MACgB;AAChB,QAAM,OAA4B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AACvG,MAAI;AACH,UAAM,kBAAkB,MAAM,wBAAwB;AACtD,SAAK,uDAAkD;AAAA,EACxD,SAAS,KAAK;AACb,SAAK,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,EAAE;AAAA,EACzF;AACD;;;AGvLA,IAAAC,eAA+C;AAC/C,gBAA2B;AAkB3B,SAAS,cAAc,MAAyD;AAC/E,SAAO,EAAE,GAAG,MAAM,eAAW,6BAAe,aAAa,OAAe,GAAG,QAAQ,WAAW;AAC/F;AAqBA,SAAS,wBAAwB,KAAuB;AACvD,MAAI,EAAE,eAAe,6BAAiB,QAAO;AAC7C,QAAM,OAAO,IAAI;AACjB,MAAI,IAAI,WAAW,KAAK;AACvB,WAAO,IAAI;AAAA,MACV,GAAG,IAAI;AAAA,MAGP,IAAI;AAAA,MACJ,IAAI;AAAA,IACL;AAAA,EACD;AACA,MAAI,IAAI,WAAW,KAAK;AACvB,WAAO,IAAI;AAAA,MACV,GAAG,IAAI;AAAA,MAEP,IAAI;AAAA,MACJ,IAAI;AAAA,IACL;AAAA,EACD;AACA,MAAI,IAAI,WAAW,KAAK;AAGvB,UAAM,gBAAgB,qBAAqB,IAAI,IAAI;AACnD,QAAI,cAAc,SAAS,GAAG;AAC7B,aAAO,IAAI;AAAA,QACV,GAAG,IAAI,oCAA+B,cAAc,KAAK,IAAI,CAAC;AAAA,QAE9D,IAAI;AAAA,QACJ,IAAI;AAAA,MACL;AAAA,IACD;AAAA,EACD;AACA,SAAO;AACR;AAOA,SAAS,qBAAqB,MAAyB;AACtD,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,CAAC;AAC/C,QAAM,OAAQ,KAA4B;AAC1C,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,CAAC;AAC/C,QAAM,SAAU,KAAsC;AACtD,MAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO,CAAC;AACpC,SAAO,OAAO,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAC/D;AAGA,SAAS,mBAAmB,SAAwB;AACnD,QAAM,YAAY,OAAO,QAAQ,IAAI,uBAAuB,CAAC;AAC7D,QAAM,QAAQ,OAAO,QAAQ,IAAI,mBAAmB,CAAC;AACrD,MAAI,OAAO,SAAS,SAAS,KAAK,OAAO,SAAS,KAAK,KAAK,QAAQ,KAAK,YAAY,QAAQ,KAAK;AACjG,cAAU,gCAAgC,SAAS,IAAI,KAAK,oCAAoC,KAAK;AAAA,EACtG;AACD;AAEA,eAAe,KAA2D,GAA2B;AACpG,MAAI;AACJ,MAAI;AACH,UAAM,MAAM;AAAA,EACb,SAAS,KAAK;AACb,UAAM,wBAAwB,GAAG;AAAA,EAClC;AACA,qBAAmB,IAAI,OAAO;AAC9B,SAAO;AACR;AAEO,SAASC,eACf,MACA,MACA,OAC+B;AAC/B,SAAO,KAAe,wBAAc,cAAc,IAAI,GAAG,MAAM,KAAK,CAAC;AACtE;AAEO,SAASC,gBACf,MACA,MACA,MACA,OACgC;AAChC,SAAO,KAAe,yBAAe,cAAc,IAAI,GAAG,MAAM,MAAM,KAAK,CAAC;AAC7E;AAEO,SAASC,iBACf,MACA,MACA,MACA,OACgC;AAChC,SAAO,KAAe,0BAAgB,cAAc,IAAI,GAAG,MAAM,MAAM,KAAK,CAAC;AAC9E;AAEO,SAASC,kBACf,MACA,MACA,OACgC;AAChC,SAAO,KAAe,2BAAiB,cAAc,IAAI,GAAG,MAAM,KAAK,CAAC;AACzE;;;ACzHA,eAAsB,aAAa,KAAmB,KAA4B;AACjF,QAAM,SAAS,cAAc;AAC7B,MAAI,OAAQ,SAAQ,OAAO,MAAM,MAAM;AAEvC,MAAI,CAAC,iBAAiB,GAAG,GAAG;AAE3B,UAAM,IAAI;AAAA,MACT,mCAAmC,UAAU;AAAA,MAC7C,SAAS;AAAA,IACV;AAAA,EACD;AAGA,QAAM,EAAE,KAAK,IAAI,MAAMC,eAAc,EAAE,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK;AACnE,kBAAgB;AAAA,IACf,SAAS;AAAA,IACT,cAAc,mBAAmB,IAAI;AAAA,IACrC,WAAW,IAAI,MAAM,EAAE;AAAA,IACvB,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,EAClC,CAAC;AACF;AAOA,SAAS,mBAAmB,MAAuB;AAClD,MAAI,QAAQ,OAAO,SAAS,UAAU;AACrC,UAAM,OAAQ,KAA4B;AAC1C,QAAI,QAAQ,OAAO,SAAS,UAAU;AACrC,YAAM,UAAW,KAA+B;AAChD,UAAI,WAAW,OAAO,YAAY,UAAU;AAC3C,cAAM,OAAQ,QAA+B;AAC7C,YAAI,OAAO,SAAS,SAAU,QAAO;AAAA,MACtC;AAAA,IACD;AAAA,EACD;AACA,SAAO;AACR;AAEO,SAAS,wBAAwB,QAAuB;AAC9D,SACE,QAAQ,OAAO,EACf,YAAY,8EAA8E,EAC1F,OAAO,OAAO,QAAiB,YAAqB;AACpD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,MAAM,MAAM,aAAa,uBAAuB,UAAU,QAAQ;AACxE,UAAM,aAAa,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU,GAAG,GAAG;AAC9F,YAAQ,OAAO,MAAM,kBAAkB,QAAQ,GAAG,CAAC,aAAa,mBAAmB,CAAC;AAAA,CAAI;AACxF,QAAI,QAAQ,aAAa,SAAS;AACjC;AAAA,QACC,mFAAmF,eAAe;AAAA,QAClG,IAAI;AAAA,MACL;AAAA,IACD;AAAA,EACD,CAAC;AACH;;;AC7EO,SAAS,yBAAyB,QAAuB;AAC/D,SACE,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,OAAO,MAAM;AACb,UAAM,UAAU,kBAAkB;AAClC,YAAQ,OAAO;AAAA,MACd,UAAU,uBAAuB,mBAAmB,CAAC;AAAA,IAAO;AAAA,IAC7D;AAAA,EACD,CAAC;AACH;;;ACNO,SAAS,yBAAyB,QAAuB;AAC/D,SACE,QAAQ,QAAQ,EAChB,YAAY,0EAA0E,EACtF,OAAO,CAAC,QAAiB,YAAqB;AAC9C,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,WAAW,cAAc,QAAQ,MAAM;AAC7C,UAAM,QAAQ,SAAS,WAAW,SAAS,gBAAgB,IAAI;AAC/D,UAAM,QAAQ;AAAA,MACb,YAAY,QAAQ,SAAS,GAAG,CAAC;AAAA,MACjC,YAAY,SAAS,MAAM;AAAA,MAC3B,YAAY,OAAO,gBAAgB,WAAW;AAAA,IAC/C;AACA,QAAI,OAAO,SAAU,OAAM,KAAK,YAAY,MAAM,QAAQ,EAAE;AAC5D,YAAQ,OAAO,MAAM,MAAM,KAAK,IAAI,IAAI,IAAI;AAAA,EAC7C,CAAC;AACH;;;ACtBA,IAAAC,eAAmC;AAcnC,SAAS,IAAI,OAAmC;AAC/C,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,OAAO,KAAK,IAAI;AAC9E;AASA,eAAsB,SACrB,KACA,QACgB;AAChB,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,EACD;AACA,QAAM,YAAQ,iCAAoC,IAAI;AAEtD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,KAAK;AACf;AAAA,EACD;AAEA,QAAM,QAAQ,MAAM,SAAS,CAAC;AAC9B,QAAM,OAAO,MAAM,cAAc,CAAC;AAClC,QAAM,QAAQ;AAAA,IACb,mBAAmB,MAAM,UAAU,QAAG;AAAA,IACtC,mBAAmB,IAAI,MAAM,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,CAAC,WAAW,IAAI,MAAM,SAAS,CAAC;AAAA,IACvF,mBAAmB,IAAI,KAAK,KAAK,CAAC,eAAe,IAAI,KAAK,cAAc,CAAC;AAAA,EAC1E;AACA,UAAQ,OAAO,MAAM,MAAM,KAAK,IAAI,IAAI,IAAI;AAC5C,UAAQ,OAAO;AAAA,IACd,IAAI,2FAA2F,IAAI,KAAK,IAAI;AAAA,EAC7G;AACD;AAEO,SAAS,wBAAwB,QAAuB;AAC9D,SACE,QAAQ,OAAO,EACf,YAAY,wDAAwD,EACpE,OAAO,OAAO,QAAiB,YAAqB;AACpD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,SAAS,KAAK,GAAG;AAAA,EACxB,CAAC;AACH;;;AC/DA,IAAAC,gBAAgC;AAqBhC,IAAM,aAAqC,EAAE,WAAW,GAAG,aAAa,EAAE;AAE1E,IAAM,sBAAsB,CAAC,UAAU,aAAa,UAAU,YAAY;AAmB1E,SAAS,cAAc,KAA6C;AACnE,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,OAAO,WAAY,QAAO,WAAW,GAAG;AAC5C,QAAM,IAAI;AAAA,IACT,qBAAqB,GAAG,eAAe,OAAO,KAAK,UAAU,EAAE,KAAK,IAAI,CAAC;AAAA,IACzE,SAAS;AAAA,EACV;AACD;AAEO,SAAS,aAAa,QAAoC;AAChE,MAAI,WAAW,EAAG,QAAO;AACzB,MAAI,WAAW,EAAG,QAAO;AACzB,SAAO,WAAW,SAAY,KAAK,OAAO,MAAM;AACjD;AAEA,SAASC,YAAW,OAA0C;AAC7D,SAAO,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI;AAC7C;AAGA,SAAS,YAAY,OAA0C;AAC9D,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,OAAO,KAAK,IAAI;AAC9E;AAMA,eAAsB,sBACrB,KACA,QACA,OACgB;AAChB,MAAI,MAAM,UAAU,MAAM,SAAS;AAClC,UAAM,IAAI,SAAS,8CAA8C,SAAS,eAAe;AAAA,EAC1F;AACA,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,IACA;AAAA,MACC,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,SAAS,MAAM;AAAA,MACf,QAAQ,cAAc,MAAM,MAAM;AAAA,IACnC;AAAA,EACD;AACA,QAAM,YAAQ,+BAAmC,IAAI;AAErD,QAAM,aAAa,MAAM,UAAU,sBAAsB,MAAM,SAAS,gBAAgB,MAAM,MAAM,IAAI;AACxG,MAAI,cAAc,IAAI,WAAW,QAAQ;AACxC,cAAU,aAAa,EAAE,GAAG,OAAO,MAAM,MAAM,KAAK,IAAI,CAAC,QAAQ,UAAU,KAAK,UAAU,CAAC,EAAE,IAAI,KAAK;AACtG;AAAA,EACD;AAEA;AAAA,IACC,MAAM;AAAA,IACN;AAAA,MACC,EAAE,QAAQ,UAAU,OAAO,CAAC,OAAO,EAAE,UAAU,IAAI,MAAM,GAAG,EAAE,EAAE;AAAA,MAChE,EAAE,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,aAAa,IAAI,UAAU,GAAG;AAAA,MACjE,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,aAAa,EAAE,MAAM,EAAE;AAAA,MACzD,EAAE,QAAQ,aAAa,OAAO,CAAC,MAAM,YAAY,EAAE,SAAS,EAAE;AAAA,MAC9D,EAAE,QAAQ,aAAa,OAAO,CAAC,MAAMD,YAAW,EAAE,YAAY,GAAG,UAAU,GAAG;AAAA,MAC9E,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAMA,YAAW,EAAE,UAAU,GAAG,UAAU,GAAG;AAAA,IAC3E;AAAA,IACA,IAAI;AAAA,EACL;AACA,QAAM,OAAO,gBAAgB,MAAM,WAAW,IAAI,MAAM,UAAU,KAAK,MAAM,KAAK,MAAM,OAAO,MAAM,UAAU;AAC/G,QAAM,OACL,MAAM,aAAa,MAAM,cAAc,6CAA6C,MAAM,cAAc,CAAC,KAAK;AAC/G,UAAQ,OAAO,MAAM,IAAI,OAAO,MAAM,IAAI,KAAK,IAAI,IAAI;AACxD;AAEO,SAAS,2BAA2B,QAAuB;AACjE,SACE,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,OAAO,cAAc,gDAAgD,CAAC,MAAM,OAAO,CAAC,CAAC,EACrF,OAAO,mBAAmB,0DAA0D,CAAC,MAAM,OAAO,CAAC,CAAC,EACpG,OAAO,kBAAkB,6BAA6B,EACtD,OAAO,oBAAoB,2CAA2C,EACtE,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,aAAa,eAAe,oBAAoB,KAAK,GAAG,CAAC,UAAU,EAC1E,OAAO,OAAO,OAAkB,YAAqB;AACrD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,sBAAsB,KAAK,KAAK,KAAK;AAAA,EAC5C,CAAC;AACH;;;AClIA,IAAAE,gBAAmC;AAuBnC,IAAM,gBAAuC,CAAC,UAAU,aAAa,QAAQ,UAAU,cAAc,YAAY;AAEjH,SAAS,kBAAkB,KAAoC;AAC9D,QAAM,MAAM,KAAK,IAAI,GAAG,cAAc,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI;AAC9D,QAAM,QAAkB,CAAC;AACzB,aAAW,SAAS,eAAe;AAClC,UAAM,MAAM,IAAI,KAAK;AACrB,QAAI,QAAQ,QAAQ,QAAQ,OAAW;AACvC,UAAM,QAAQ,UAAU,WAAW,aAAa,GAAa,IAAI,OAAO,GAAG;AAC3E,UAAM,KAAK,IAAI,QAAQ,KAAK,OAAO,MAAM,CAAC,CAAC,GAAG,oBAAoB,KAAK,CAAC,EAAE;AAAA,EAC3E;AACA,SAAO;AACR;AAEO,SAAS,2BAA2B,QAAuB;AACjE,SACE,QAAQ,eAAe,EACvB,YAAY,uCAAuC,EACnD,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,OAAO,OAAe,OAAkB,YAAqB;AACpE,UAAM,MAAM,eAAe,OAAO;AAClC,QAAI,CAAC,MAAM,KAAK,GAAG;AAClB,YAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,IACxE;AACA,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,MACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,QAAQ,IAAI;AAAA,MAClF,SAAS,mBAAmB,MAAM,KAAK,CAAC,CAAC;AAAA,IAC1C;AACA,UAAM,UAAM,kCAAwC,IAAI;AAExD,QAAI,MAAM,QAAQ;AACjB,gBAAU,UAAU,KAAK,gBAAgB,MAAM,MAAM,CAAC,CAAC;AACvD;AAAA,IACD;AACA,QAAI,IAAI,WAAW,QAAQ;AAC1B,gBAAU,GAAG;AACb;AAAA,IACD;AACA,YAAQ,OAAO,MAAM,kBAAkB,GAAG,EAAE,KAAK,IAAI,IAAI,IAAI;AAAA,EAC9D,CAAC;AACH;;;ACjEA,yBAA2B;AAC3B,IAAAC,gBAAmC;AAwBnC,eAAsB,cACrB,KACA,QACA,QACA,gBACgB;AAChB,QAAM,UAAU,eAAe,QAAQ,EAAE,WAAW,IAAI,UAAU,CAAC;AACnE,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,IACA;AAAA,IACA,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,cAAU,kCAA+B,IAAI;AACnD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,OAAO;AACjB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,gBAAgB,QAAQ,UAAU,EAAE;AAAA,CAAI;AAC9D;AAEO,SAAS,6BAA6B,QAAuB;AACnE,SACE,QAAQ,QAAQ,EAChB,YAAY,+DAA+D,EAC3E,eAAe,iBAAiB,2CAA2C,EAC3E,OAAO,2BAA2B,yEAAyE,EAC3G,OAAO,OAAO,OAAoB,YAAqB;AACvD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,cAAc,KAAK,KAAK,MAAM,MAAgB,MAAM,sBAAkB,+BAAW,CAAC;AAAA,EACzF,CAAC;AACH;;;AC1DA,IAAAC,sBAA2B;AAC3B,IAAAC,gBAAmC;;;ACa5B,SAAS,aAAa,OAAuB;AACnD,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS,OAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AACrF,SAAO;AACR;;;ADEA,eAAsB,cACrB,KACA,QACA,OACA,QACA,gBACA,SACgB;AAChB,QAAM,UAAU,aAAa,KAAK;AAClC,QAAM,UAAU,eAAe,QAAQ,EAAE,WAAW,IAAI,UAAU,CAAC;AACnE,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,SAAS,mBAAmB,OAAO,CAAC;AAAA,IACpC;AAAA,IACA,EAAE,gBAAgB,QAAQ;AAAA,EAC3B;AACA,QAAM,cAAU,kCAA+B,IAAI;AACnD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,OAAO;AACjB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,gBAAgB,QAAQ,UAAU,OAAO;AAAA,CAAI;AACnE;AAEO,SAAS,6BAA6B,QAAuB;AACnE,SACE,QAAQ,iBAAiB,EACzB,YAAY,oEAAoE,EAChF,eAAe,iBAAiB,mDAAmD,EACnF,OAAO,2BAA2B,8DAA8D,EAChG,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAoB,YAAqB;AACtE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,cAAc,KAAK,KAAK,OAAO,MAAM,MAAgB,MAAM,sBAAkB,gCAAW,GAAG,MAAM,OAAO;AAAA,EAC/G,CAAC;AACH;;;AEzDA,IAAAC,sBAA2B;AAC3B,IAAAC,gBAAmC;AAenC,eAAsB,kBACrB,KACA,QACA,OACA,QACA,gBACgB;AAChB,QAAM,UAAU,aAAa,KAAK;AAClC,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,SAAS,mBAAmB,OAAO,CAAC,IAAI,MAAM;AAAA,IAC9C,CAAC;AAAA,IACD,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,aAAS,kCAA+B,IAAI;AAClD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,MAAM;AAChB;AAAA,EACD;AACA,QAAM,OAAO,WAAW,YAAY,cAAc;AAClD,UAAQ,OAAO,MAAM,GAAG,IAAI,SAAS,OAAO,UAAU,OAAO;AAAA,CAAI;AAClE;AAMA,eAAsB,cACrB,KACA,QACA,OACA,SACA,gBACgB;AAChB,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,oEAAoE,SAAS,eAAe;AAAA,EAChH;AACA,QAAM,UAAU,aAAa,KAAK;AAClC,QAAMC;AAAA,IACL,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,SAAS,mBAAmB,OAAO,CAAC;AAAA,IACpC,EAAE,eAAe;AAAA,EAClB;AAGA,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,QAAQ,SAAS,SAAS,KAAK,CAAC;AAC5C;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,gBAAgB,OAAO;AAAA,CAAI;AACjD;AAMA,eAAsB,YAAY,KAAe,QAAgB,OAAe,gBAAuC;AACtH,QAAM,UAAU,aAAa,KAAK;AAClC,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,SAAS,mBAAmB,OAAO,CAAC;AAAA,IACpC,CAAC;AAAA,IACD,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,aAAS,kCAA+B,IAAI;AAClD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,MAAM;AAChB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,cAAc,OAAO,sBAAiB,OAAO,UAAU,WAAW;AAAA,CAAI;AAG3F,QAAM,aAAa,mBAAmB,OAAO,iBAAiB;AAC9D,MAAI,WAAW,SAAS,GAAG;AAC1B,YAAQ,OAAO,MAAM,wEAA8D,WAAW,KAAK,IAAI,CAAC;AAAA,CAAI;AAAA,EAC7G;AACD;AAGA,SAAS,mBAAmB,OAA0B;AACrD,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MAAM,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAC9D;AAEO,SAAS,2BAA2B,QAAuB;AACjE,SACE,QAAQ,eAAe,EACvB,YAAY,iDAAiD,EAC7D,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAuB,YAAqB;AACzE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,EAAE,IAAI,IAAI,cAAe,QAAQ,gBAAgB,EAA0B,MAAM;AACvF,UAAM,YAAY,KAAK,KAAK,OAAO,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EACxE,CAAC;AACH;AAOO,IAAM,6BACZ;AAIM,SAAS,4BAA4B,QAAuB;AAClE,SACE,QAAQ,kBAAkB,EAAE,QAAQ,KAAK,CAAC,EAC1C,YAAY,oBAAoB,EAChC,mBAAmB,IAAI,EACvB,OAAO,YAAY;AACnB,UAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,EACxE,CAAC;AACH;AAEO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,kBAAkB,EAC1B,YAAY,uBAAuB,EACnC,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAuB,YAAqB;AACzE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,EAAE,IAAI,IAAI,cAAe,QAAQ,gBAAgB,EAA0B,MAAM;AACvF,UAAM,kBAAkB,KAAK,KAAK,OAAO,WAAW,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EACzF,CAAC;AACH;AAEO,SAAS,gCAAgC,QAAuB;AACtE,SACE,QAAQ,oBAAoB,EAC5B,YAAY,qCAAqC,EACjD,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAuB,YAAqB;AACzE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,EAAE,IAAI,IAAI,cAAe,QAAQ,gBAAgB,EAA0B,MAAM;AACvF,UAAM,kBAAkB,KAAK,KAAK,OAAO,aAAa,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EAC3F,CAAC;AACH;AAEO,SAAS,6BAA6B,QAAuB;AACnE,SACE,QAAQ,iBAAiB,EACzB,YAAY,wDAAwD,EACpE,OAAO,aAAa,+CAA+C,EACnE,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAuB,YAAqB;AACzE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,EAAE,IAAI,IAAI,cAAe,QAAQ,gBAAgB,EAA0B,MAAM;AACvF,UAAM,cAAc,KAAK,KAAK,OAAO,MAAM,YAAY,MAAM,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EAClG,CAAC;AACH;;;ACvKA,IAAAC,sBAA2B;AAC3B,IAAAC,gBAAmC;AAqBnC,IAAM,YAAY;AAClB,IAAM,YAAY;AAOlB,eAAsB,aACrB,KACA,QACA,QACA,SACA,gBACgB;AAChB,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,kDAAkD,SAAS,eAAe;AAAA,EAC9F;AACA,QAAM,UAAU,eAAe,QAAQ,EAAE,WAAW,IAAI,UAAU,CAAC;AACnE,QAAM,OAAO,QAAQ;AACrB,MAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,aAAa,KAAK,SAAS,WAAW;AAC/E,UAAM,IAAI;AAAA,MACT,8CAA8C,SAAS,OAAO,SAAS;AAAA,MACvE,SAAS;AAAA,IACV;AAAA,EACD;AACA,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,IACA;AAAA,IACA,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,aAAS,kCAAgC,IAAI;AACnD,QAAM,YAAY,OAAO,aAAa,CAAC;AACvC,QAAM,SAAS,OAAO,UAAU,CAAC;AAEjC,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,MAAM;AAAA,EACjB,OAAO;AACN,YAAQ,OAAO,MAAM,UAAU,UAAU,MAAM,eAAe,OAAO,MAAM,eAAe,KAAK,MAAM;AAAA,CAAM;AAC3G,eAAW,KAAK;AACf,cAAQ,OAAO,MAAM,WAAW,EAAE,KAAK,KAAK,oBAAoB,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;AAAA,CAAI;AAC5F,eAAW,KAAK;AACf,cAAQ,OAAO,MAAM,WAAW,EAAE,KAAK,KAAK,oBAAoB,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;AAAA,CAAI;AAAA,EACjG;AAGA,MAAI,OAAO,SAAS,GAAG;AACtB,UAAM,IAAI,SAAS,GAAG,OAAO,MAAM,OAAO,KAAK,MAAM,kCAAkC,SAAS,iBAAiB;AAAA,EAClH;AACD;AAEO,SAAS,4BAA4B,QAAuB;AAClE,SACE,QAAQ,OAAO,EACf,YAAY,qFAAqF,EACjG,eAAe,iBAAiB,yDAAyD,EACzF,OAAO,aAAa,yBAAyB,EAC7C,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAmB,YAAqB;AACtD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,EAAE,IAAI,IAAI,cAAe,QAAQ,gBAAgB,EAA0B,MAAM;AACvF,UAAM,aAAa,KAAK,KAAK,MAAM,MAAgB,MAAM,YAAY,MAAM,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EAChH,CAAC;AACH;;;ACzEO,SAAS,8BAA8B,QAAuB;AACpE,QAAM,OAAO,OAAO,QAAQ,MAAM,EAAE,YAAY,kCAAkC;AAClF,6BAA2B,IAAI;AAC/B,6BAA2B,IAAI;AAC/B,+BAA6B,IAAI;AACjC,+BAA6B,IAAI;AACjC,gCAA8B,IAAI;AAClC,kCAAgC,IAAI;AACpC,+BAA6B,IAAI;AACjC,6BAA2B,IAAI;AAC/B,8BAA4B,IAAI;AAChC,8BAA4B,IAAI;AACjC;;;ACzBA,IAAAC,gBAAgC;AAmBhC,SAASC,YAAW,OAA0C;AAC7D,SAAO,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI;AAC7C;AAEA,SAAS,aAAa,QAAsC;AAC3D,SAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,KAAK,GAAG,IAAI;AACnD;AAMA,eAAsB,YACrB,KACA,QACgB;AAChB,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,EACD;AACA,QAAM,WAAO,+BAAmC,IAAI;AAEpD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,IAAI;AACd;AAAA,EACD;AAEA;AAAA,IACC;AAAA,IACA;AAAA,MACC,EAAE,QAAQ,UAAU,OAAO,CAAC,OAAO,EAAE,UAAU,IAAI,MAAM,GAAG,EAAE,EAAE;AAAA,MAChE,EAAE,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,QAAQ,IAAI,UAAU,GAAG;AAAA,MAC3D,EAAE,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG;AAAA,MACnD,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,aAAa,EAAE,MAAM,GAAG,UAAU,GAAG;AAAA,MACvE,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG;AAAA,MACjD,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAMD,YAAW,EAAE,UAAU,GAAG,UAAU,GAAG;AAAA,MAC1E,EAAE,QAAQ,aAAa,OAAO,CAAC,MAAMA,YAAW,EAAE,YAAY,GAAG,UAAU,GAAG;AAAA,IAC/E;AAAA,IACA,IAAI;AAAA,EACL;AACA,QAAM,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE;AACzD,UAAQ,OAAO,MAAM,IAAI,GAAG,KAAK,MAAM,YAAY,MAAM,YAAY,IAAI,KAAK,IAAI,IAAI;AACvF;AAEO,SAAS,2BAA2B,QAAuB;AACjE,SACE,QAAQ,MAAM,EACd,YAAY,qCAAqC,EACjD,OAAO,OAAO,QAAiB,YAAqB;AACpD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,YAAY,KAAK,GAAG;AAAA,EAC3B,CAAC;AACH;;;ACzEA,IAAAE,gBAAmC;AASnC,IAAM,6BAA6B,CAAC,IAAI,IAAI,EAAE;AAmBvC,SAAS,mBAAmB,KAA6C;AAC/E,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,CAAE,2BAAiD,SAAS,GAAG,GAAG;AACrE,UAAM,IAAI;AAAA,MACT,yBAAyB,GAAG,cAAc,2BAA2B,KAAK,IAAI,CAAC;AAAA,MAC/E,SAAS;AAAA,IACV;AAAA,EACD;AACA,SAAO;AACR;AAQA,eAAsB,cACrB,KACA,QACA,OACA,OACgB;AAChB,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS,OAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AACrF,QAAM,aAAa,mBAAmB,MAAM,UAAU;AAEtD,QAAM,cAAuC,CAAC;AAC9C,MAAI,eAAe,OAAW,aAAY,cAAc;AAExD,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,SAAS,mBAAmB,OAAO,CAAC;AAAA,IACpC;AAAA,EACD;AACA,QAAM,aAAS,kCAAwC,IAAI;AAE3D,MAAI,IAAI,WAAW,QAAQ;AAE1B,cAAU,MAAM;AAAA,EACjB,OAAO;AACN,UAAM,YAAY,OAAO,WAAW;AACpC,UAAM,QAAQ,MAAM,SAAS,YAAY,QAAQ,SAAS;AAC1D,UAAM,QAAQ;AAAA,MACb,gBAAgB,oBAAoB,KAAK,CAAC;AAAA,MAC1C,gBAAgB,oBAAoB,OAAO,UAAU,EAAE,CAAC;AAAA,MACxD,gBAAgB,qBAAqB,OAAO,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;AAAA,MACpE,gBAAgB,oBAAoB,OAAO,cAAc,EAAE,CAAC;AAAA,IAC7D;AACA,YAAQ,OAAO,MAAM,MAAM,KAAK,IAAI,IAAI,IAAI;AAC5C,QAAI,CAAC,MAAM,QAAQ;AAClB,cAAQ,OAAO,MAAM,IAAI,oDAAoD,IAAI,KAAK,IAAI,IAAI;AAAA,IAC/F;AAAA,EACD;AAGA;AAAA,IACC,gDAAgD,eAAe;AAAA,IAE/D,IAAI;AAAA,EACL;AACD;AAEO,SAAS,6BAA6B,QAAuB;AACnE,SACE,QAAQ,iBAAiB,EACzB,YAAY,wEAAwE,EACpF,OAAO,qBAAqB,uDAAuD,CAAC,MAAM,OAAO,CAAC,CAAC,EACnG,OAAO,YAAY,iEAAiE,EACpF,OAAO,OAAO,OAAe,OAAoB,YAAqB;AACtE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AAExC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,cAAc,KAAK,KAAK,OAAO,KAAK;AAAA,EAC3C,CAAC;AACH;;;ACrGO,SAAS,8BAA8B,QAAuB;AACpE,QAAM,OAAO,OAAO,QAAQ,MAAM,EAAE,YAAY,uCAAuC;AACvF,6BAA2B,IAAI;AAC/B,+BAA6B,IAAI;AAClC;;;ACPA,IAAAC,gBAAmC;AAkBnC,IAAM,wBAAgD;AAAA,EACrD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACJ;AAEA,SAAS,oBAAoB,QAAoC;AAChE,MAAI,WAAW,OAAW,QAAO;AACjC,SAAO,sBAAsB,MAAM,KAAK,OAAO,MAAM;AACtD;AAGA,SAAS,YAAY,SAAgD;AACpE,QAAM,OAAO,QAAQ,cAAc;AACnC,QAAM,SAAS,QAAQ,gBAAgB;AACvC,MAAI,CAAC,QAAQ,CAAC,OAAQ,QAAO;AAC7B,SAAO,CAAC,MAAM,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAC/C;AAOA,SAAS,cAAc,SAAgD;AACtE,MAAI,QAAQ,mBAAmB,QAAQ,QAAQ,mBAAmB,OAAW,QAAO;AACpF,MAAI,QAAQ,wBAAwB,EAAG,QAAO;AAC9C,SAAO,OAAO,QAAQ,cAAc;AACrC;AAGA,SAAS,cAAc,SAAgD;AACtE,SAAO,QAAQ,WAAW;AAC3B;AAEA,IAAMC,iBAAuC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAEA,SAASC,mBAAkB,SAAsC;AAChE,QAAM,MAAM,KAAK,IAAI,GAAGD,eAAc,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI;AAC9D,QAAM,UAA8C;AAAA,IACnD,MAAM,QAAQ;AAAA,IACd,gBAAgB,QAAQ,kBAAkB;AAAA,IAC1C,QAAQ,oBAAoB,QAAQ,MAAM;AAAA,IAC1C,SAAS,QAAQ,WAAW;AAAA,IAC5B,OAAO,YAAY,OAAO;AAAA,IAC1B,SAAS,cAAc,OAAO;AAAA,IAC9B,SAAS,cAAc,OAAO;AAAA,IAC9B,UAAU,QAAQ,YAAY;AAAA,EAC/B;AACA,QAAM,QAAkB,CAAC;AACzB,aAAW,SAASA,gBAAe;AAClC,UAAM,QAAQ,QAAQ,KAAK;AAC3B,QAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,GAAI;AAC3D,UAAM,KAAK,IAAI,QAAQ,KAAK,OAAO,MAAM,CAAC,CAAC,GAAG,oBAAoB,KAAK,CAAC,EAAE;AAAA,EAC3E;AACA,SAAO;AACR;AAEA,eAAsB,eACrB,KACA,QACA,OACgB;AAChB,QAAM,EAAE,KAAK,IAAI,MAAME;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,EACD;AACA,QAAM,cAAU,kCAAsC,IAAI;AAE1D,MAAI,MAAM,QAAQ;AACjB,cAAU,UAAU,SAAS,gBAAgB,MAAM,MAAM,CAAC,CAAC;AAC3D;AAAA,EACD;AACA,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,OAAO;AACjB;AAAA,EACD;AACA,UAAQ,OAAO,MAAMD,mBAAkB,OAAO,EAAE,KAAK,IAAI,IAAI,IAAI;AAClE;AAKO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,MAAM,EACd,YAAY,+BAA+B,EAC3C,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,OAAO,OAAkB,YAAqB;AACrD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,eAAe,KAAK,KAAK,KAAK;AAAA,EACrC,CAAC;AACH;;;AC1HA,IAAAE,sBAA2B;AAC3B,IAAAC,gBAAmC;;;ACwB5B,IAAM,eAAsC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAGO,IAAM,qBAA4C,CAAC,eAAe,qBAAqB,aAAa;AAWpG,IAAM,mBAA0C;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;;;ADrDA,IAAM,wBAA+C;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAOA,IAAM,+BAAsD;AAAA,EAC3D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AA+BA,eAAsB,2BACrB,OACA,KACA,QACiC;AACjC,QAAM,iBAAiB,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,iBAAiB,SAAS,CAAC,CAAC;AACpF,MAAI,eAAe,SAAS,GAAG;AAC9B,UAAM,IAAI,qBAAqB,kCAAkC,eAAe,KAAK,IAAI,CAAC,EAAE;AAAA,EAC7F;AAEA,QAAM,iBAAiB,oBAAI,IAAY,CAAC,GAAG,cAAc,GAAG,kBAAkB,CAAC;AAC/E,QAAM,eAAe,OAAO,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC;AAC5E,MAAI,aAAa,SAAS,GAAG;AAC5B,UAAM,IAAI,qBAAqB,qBAAqB,aAAa,KAAK,IAAI,CAAC,EAAE;AAAA,EAC9E;AAEA,QAAM,qBAAqB,aAAa,KAAK,CAAC,MAAM,KAAK,KAAK;AAC9D,QAAM,2BAA2B,mBAAmB,KAAK,CAAC,MAAM,KAAK,KAAK;AAC1E,MAAI,CAAC,sBAAsB,CAAC,0BAA0B;AAErD,UAAM,IAAI,qBAAqB,qCAAqC;AAAA,EACrE;AAKA,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,EACD;AACA,QAAM,cAAU,kCAAsC,IAAI;AAE1D,QAAM,wBAAwB,6BAA6B,OAAO,CAAC,MAAM,EAAE,KAAK,QAAQ;AACxF,MAAI,sBAAsB,SAAS,GAAG;AAKrC,UAAM,IAAI;AAAA,MACT,+EAA+E,sBAAsB,KAAK,IAAI,CAAC;AAAA,MAC/G,SAAS;AAAA,IACV;AAAA,EACD;AAEA,MAAI,QAAwC;AAC5C,MAAI,oBAAoB;AACvB,UAAM,SAAkC,CAAC;AACzC,eAAW,SAAS,cAAc;AACjC,aAAO,KAAK,IAAI,SAAS,QAAQ,MAAM,KAAK,IAAI,QAAQ,KAAK;AAAA,IAC9D;AACA,UAAM,kBAAkB,sBAAsB,OAAO,CAAC,MAAM;AAC3D,YAAM,IAAI,OAAO,CAAC;AAClB,aAAO,MAAM,QAAQ,MAAM,UAAa,MAAM;AAAA,IAC/C,CAAC;AACD,QAAI,gBAAgB,SAAS,GAAG;AAC/B,YAAM,CAAC,KAAK,IAAI;AAChB,YAAM,IAAI;AAAA,QACT,2BAA2B,KAAK,oFAC9B,gBAAgB,SAAS,IAAI,mBAAmB,gBAAgB,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,MAAM;AAAA,MAC5F;AAAA,IACD;AACA,YAAQ;AAAA,EACT;AAEA,MAAI,eAA+C;AACnD,MAAI,0BAA0B;AAC7B,mBAAe,CAAC;AAChB,eAAW,SAAS,oBAAoB;AACvC,UAAI,SAAS,MAAO,cAAa,KAAK,IAAI,MAAM,KAAK;AAAA,IACtD;AAAA,EACD;AAEA,SAAO,EAAE,OAAO,aAAa;AAC9B;AASA,SAAS,iCAAiC,MAAuD;AAChG,SACC,CAAC,CAAC,QACF,OAAO,SAAS,YAChB,MAAM,QAAS,KAAiC,gBAAgB,KAChE,OAAQ,KAAiC,mBAAmB;AAE9D;AAGA,SAAS,mBAAmB,SAA4B,QAAgC;AACvF,MAAI,WAAW,QAAQ;AACtB,cAAU,OAAO;AACjB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,oBAAoB,QAAQ,QAAQ,EAAE;AAAA,CAAI;AAChE;AAiBO,SAAS,6BACf,YACA,mBACA,OACkD;AAClD,QAAM,aAAa,cAAc;AACjC,MAAI,cAAc,MAAM,mBAAmB,QAAW;AACrD,UAAM,IAAI;AAAA,MACT;AAAA,IAED;AAAA,EACD;AACA,SAAO;AAAA,IACN,UAAU,aAAc,MAAM,uBAAuB,MAAM,sBAAkB,gCAAW,IAAK;AAAA,IAC7F,iBAAiB,oBACb,MAAM,8BAA8B,MAAM,sBAAkB,gCAAW,IACxE;AAAA,EACJ;AACD;AA8BA,eAAsB,iBACrB,KACA,QACA,QACA,kBACA,UAAgC,CAAC,GACjB;AAChB,QAAM,QAAQ,eAAe,QAAQ,OAAO;AAC5C,QAAM,aAAa,aAAa,KAAK,CAAC,MAAM,KAAK,KAAK;AACtD,QAAM,oBAAoB,mBAAmB,KAAK,CAAC,MAAM,KAAK,KAAK;AACnE,QAAM,EAAE,UAAU,gBAAgB,IAAI,6BAA6B,YAAY,mBAAmB,gBAAgB;AAElH,QAAM,WAAW,MAAM,2BAA2B,OAAO,KAAK,MAAM;AAEpE,QAAM,cAAc,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAEjG,MAAI;AACJ,MAAI,YAAY;AACf,UAAM,EAAE,KAAK,IAAI,MAAMC,iBAAgB,aAAa,kBAAkB,SAAS,OAAkC;AAAA,MAChH,gBAAgB;AAAA,IACjB,CAAC;AACD,6BAAqB,kCAAsC,IAAI;AAAA,EAChE;AAEA,MAAI,CAAC,mBAAmB;AAEvB,uBAAmB,oBAAyC,IAAI,MAAM;AACtE;AAAA,EACD;AAEA,MAAI;AACH,UAAM,EAAE,KAAK,IAAI,MAAMA;AAAA,MACtB;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,EAAE,gBAAgB,gBAAgB;AAAA,IACnC;AACA,UAAM,aAAS,kCAA+E,IAAI;AAClG,uBAAmB,OAAO,SAAS,IAAI,MAAM;AAAA,EAC9C,SAAS,KAAK;AACb,QAAI,CAAC,YAAY;AAEhB,YAAM;AAAA,IACP;AAIA,UAAM,mBAAmB,eAAe,wBAAwB,IAAI,OAAO;AAC3E,UAAM,UAAU,iCAAiC,gBAAgB,IAAI,mBAAmB;AAExF,QAAI,IAAI,WAAW,QAAQ;AAC1B,gBAAU;AAAA,QACT,eAAe;AAAA,QACf,eAAe;AAAA,QACf,oBAAoB,UACjB,EAAE,kBAAkB,QAAQ,kBAAkB,gBAAgB,QAAQ,eAAe,IACrF,EAAE,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAAE;AAAA,MAChE,CAAC;AAAA,IACF,OAAO;AACN,cAAQ,OAAO,MAAM,gDAAgD;AACrE,UAAI,SAAS;AACZ,gBAAQ,OAAO;AAAA,UACd,qCAAqC,QAAQ,oBAAoB,CAAC,GAAG,KAAK,IAAI,CAAC,qBAAqB,QAAQ,cAAc;AAAA;AAAA,QAC3H;AAAA,MACD,OAAO;AACN,gBAAQ,OAAO;AAAA,UACd,wCAAwC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA;AAAA,QACzF;AAAA,MACD;AAAA,IACD;AACA,UAAM;AAAA,EACP;AACD;AAYO,SAAS,gCAAgC,QAAuB;AACtE,SACE,QAAQ,QAAQ,EAChB;AAAA,IACA;AAAA,EAED,EACC,eAAe,iBAAiB,uDAAuD,EACvF,OAAO,2BAA2B,0EAA0E,EAC5G,OAAO,iCAAiC,8DAA8D,EACtG;AAAA,IACA;AAAA,IACA;AAAA,EACD,EACC,OAAO,OAAO,OAAoB,YAAqB;AACvD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM;AAAA,MACL;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN;AAAA,QACC,gBAAgB,MAAM;AAAA,QACtB,qBAAqB,MAAM;AAAA,QAC3B,4BAA4B,MAAM;AAAA,MACnC;AAAA,MACA,EAAE,WAAW,IAAI,UAAU;AAAA,IAC5B;AAAA,EACD,CAAC;AACH;;;AEtVA,IAAAC,sBAA2B;AAC3B,IAAAC,kBAAmD;AACnD,IAAAC,oBAAwB;AACxB,IAAAC,gBAAqD;AAerD,IAAM,4BAAoD;AAAA,EACzD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AACV;AAGA,IAAM,wBAAwB,IAAI,OAAO;AAezC,SAAS,iBAAiB,MAA6B;AACtD,MAAI,KAAC,4BAAW,IAAI,GAAG;AACtB,UAAM,IAAI,qBAAqB,mBAAmB,IAAI,EAAE;AAAA,EACzD;AACA,QAAM,WAAO,0BAAS,IAAI;AAC1B,MAAI,CAAC,KAAK,OAAO,GAAG;AACnB,UAAM,IAAI,qBAAqB,uBAAuB,IAAI,EAAE;AAAA,EAC7D;AAEA,QAAM,UAAM,2BAAQ,IAAI,EAAE,YAAY;AACtC,QAAM,cAAc,0BAA0B,GAAG;AACjD,MAAI,CAAC,aAAa;AACjB,UAAM,IAAI;AAAA,MACT,+BAA+B,OAAO,QAAQ,qBAAgB,OAAO,KAAK,yBAAyB,EAAE,KAAK,IAAI,CAAC;AAAA,IAChH;AAAA,EACD;AAEA,MAAI,KAAK,QAAQ,GAAG;AACnB,UAAM,IAAI,qBAAqB,kBAAkB,IAAI,EAAE;AAAA,EACxD;AACA,MAAI,KAAK,OAAO,uBAAuB;AACtC,UAAM,IAAI,qBAAqB,mBAAmB,KAAK,IAAI,eAAe,qBAAqB,eAAe;AAAA,EAC/G;AAEA,QAAM,YAAQ,8BAAa,IAAI;AAC/B,SAAO,EAAE,aAAa,UAAU,KAAK,MAAM,MAAM;AAClD;AAwBA,SAAS,2BAA2B,OAA+E;AAClH,MAAI,MAAM,gBAAgB;AACzB,WAAO;AAAA,MACN,YAAY,GAAG,MAAM,cAAc;AAAA,MACnC,YAAY,GAAG,MAAM,cAAc;AAAA,IACpC;AAAA,EACD;AACA,SAAO,EAAE,gBAAY,gCAAW,GAAG,gBAAY,gCAAW,EAAE;AAC7D;AAGA,SAAS,gBAAgB,QAA6B,QAAgC;AACrF,MAAI,WAAW,QAAQ;AACtB,cAAU,MAAM;AAChB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,iBAAiB,OAAO,QAAQ;AAAA,CAAI;AAC1D;AAWA,eAAsB,qBACrB,KACA,QACA,MACA,kBACgB;AAChB,QAAM,EAAE,aAAa,UAAU,MAAM,IAAI,iBAAiB,IAAI;AAC9D,QAAM,EAAE,YAAY,WAAW,IAAI,2BAA2B,gBAAgB;AAE9E,QAAM,cAAc,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAEjG,QAAM,EAAE,MAAM,YAAY,IAAI,MAAMC;AAAA,IACnC;AAAA,IACA;AAAA,IACA,EAAE,cAAc,aAAa,WAAW,SAAS;AAAA,IACjD,EAAE,gBAAgB,WAAW;AAAA,EAC9B;AACA,QAAM,cAAU,kCAAwC,WAAW;AAEnE,QAAM,aAAa,IAAI,QAAQ,QAAQ,YAAY;AAAA,IAClD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,YAAY;AAAA,IACvC,MAAM;AAAA,EACP,CAAC;AACD,QAAM,cAAc,UAAM,gCAAiB,YAAY,IAAI,SAAS;AACpE,MAAI,CAAC,YAAY,IAAI;AAKpB,UAAM,IAAI,aAAa,qCAAqC,YAAY,MAAM,EAAE;AAAA,EACjF;AAEA,QAAM,EAAE,MAAM,YAAY,IAAI,MAAMA;AAAA,IACnC;AAAA,IACA;AAAA,IACA,EAAE,QAAQ,QAAQ,OAAO;AAAA,IACzB,EAAE,gBAAgB,WAAW;AAAA,EAC9B;AACA,QAAM,aAAS,kCAAwC,WAAW;AAClE,kBAAgB,QAAQ,IAAI,MAAM;AACnC;AAeO,SAAS,8BAA8B,QAAuB;AACpE,QAAM,OAAO,OAAO,QAAQ,MAAM,EAAE,YAAY,0BAA0B;AAC1E,OACE,QAAQ,eAAe,EACvB,YAAY,+DAA+D,EAC3E,OAAO,2BAA2B,2EAA2E,EAC7G,OAAO,OAAO,MAAc,OAAwB,YAAqB;AACzE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,qBAAqB,KAAK,KAAK,MAAM,EAAE,gBAAgB,MAAM,eAAe,CAAC;AAAA,EACpF,CAAC;AACH;;;ACtLO,SAAS,iCAAiC,QAAuB;AACvE,QAAM,UAAU,OAAO,QAAQ,SAAS,EAAE,YAAY,0CAA0C;AAChG,gCAA8B,OAAO;AACrC,kCAAgC,OAAO;AACvC,gCAA8B,OAAO;AACtC;;;ACRA,IAAAC,gBAAgC;AAqBhC,IAAM,oBAAoB;AAE1B,IAAMC,uBAAsB,CAAC,iBAAiB,kBAAkB,qBAAqB,YAAY;AAcjG,SAASC,YAAW,OAA0C;AAC7D,SAAO,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI;AAC7C;AAEA,eAAsB,yBACrB,KACA,QACA,OACgB;AAChB,MAAI,MAAM,UAAU,MAAM,SAAS;AAClC,UAAM,IAAI,SAAS,8CAA8C,SAAS,eAAe;AAAA,EAC1F;AACA,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,IACA;AAAA,MACC,KAAK,MAAM;AAAA,MACX,YAAY,MAAM;AAAA,MAClB,SAAS,MAAM;AAAA,MACf,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM,YAAY;AAAA,IAC7B;AAAA,EACD;AACA,QAAM,YAAQ,+BAAsC,IAAI;AAExD,QAAM,aAAa,MAAM,UAAUF,uBAAsB,MAAM,SAAS,gBAAgB,MAAM,MAAM,IAAI;AACxG,MAAI,cAAc,IAAI,WAAW,QAAQ;AACxC,cAAU,aAAa,EAAE,GAAG,OAAO,MAAM,MAAM,KAAK,IAAI,CAAC,QAAQ,UAAU,KAAK,UAAU,CAAC,EAAE,IAAI,KAAK;AACtG;AAAA,EACD;AAEA;AAAA,IACC,MAAM;AAAA,IACN;AAAA,MACC,EAAE,QAAQ,aAAa,OAAO,CAAC,OAAO,EAAE,iBAAiB,IAAI,MAAM,GAAG,EAAE,EAAE;AAAA,MAC1E,EAAE,QAAQ,aAAa,OAAO,CAAC,MAAM,EAAE,kBAAkB,IAAI,UAAU,GAAG;AAAA,MAC1E,EAAE,QAAQ,aAAa,OAAO,CAAC,MAAM,EAAE,qBAAqB,IAAI,UAAU,GAAG;AAAA,MAC7E,EAAE,QAAQ,cAAc,OAAO,CAAC,MAAM,EAAE,qBAAqB,IAAI,UAAU,GAAG;AAAA,MAC9E,EAAE,QAAQ,eAAe,OAAO,CAAC,MAAM,EAAE,qBAAqB,IAAI,UAAU,GAAG;AAAA,MAC/E,EAAE,QAAQ,cAAc,OAAO,CAAC,MAAMC,YAAW,EAAE,UAAU,GAAG,UAAU,GAAG;AAAA,MAC7E,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAO,EAAE,YAAY,QAAQ,KAAM;AAAA,IAChE;AAAA,IACA,IAAI;AAAA,EACL;AACA,QAAM,OAAO,gBAAgB,MAAM,WAAW,IAAI,MAAM,UAAU,KAAK,MAAM,KAAK,MAAM,OAAO,MAAM,UAAU;AAC/G,QAAM,OACL,MAAM,aAAa,MAAM,cAAc,gDAAgD,MAAM,cAAc,CAAC,KAAK;AAClH,UAAQ,OAAO,MAAM,IAAI,OAAO,MAAM,IAAI,KAAK,IAAI,IAAI;AACxD;AAEO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,MAAM,EACd,YAAY,qCAAqC,EACjD,OAAO,eAAe,6EAA6E,EACnG,OAAO,sBAAsB,8BAA8B,EAC3D,OAAO,sBAAsB,kCAAkC,EAC/D,OAAO,iBAAiB,yCAAyC,EACjE,OAAO,eAAe,0CAA0C,EAChE,OAAO,kBAAkB,gDAAgD,CAAC,MAAM,OAAO,CAAC,CAAC,EACzF,OAAO,uBAAuB,2BAA2B,iBAAiB,cAAc,CAAC,MAAM,OAAO,CAAC,CAAC,EACxG,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,aAAa,eAAeD,qBAAoB,KAAK,GAAG,CAAC,UAAU,EAC1E,OAAO,OAAO,OAAkB,YAAqB;AACrD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,yBAAyB,KAAK,KAAK,KAAK;AAAA,EAC/C,CAAC;AACH;;;AC5GA,IAAAG,gBAAmC;AAanC,eAAsB,yBACrB,KACA,QACA,aACA,OACgB;AAChB,QAAM,UAAU,YAAY,KAAK;AACjC,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,mCAAmC,SAAS,eAAe;AAAA,EAC/E;AACA,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,YAAY,mBAAmB,OAAO,CAAC;AAAA,EACxC;AACA,QAAM,aAAS,kCAA4C,IAAI;AAC/D,MAAI,MAAM,QAAQ;AACjB,cAAU,UAAU,QAAQ,gBAAgB,MAAM,MAAM,CAAC,CAAC;AAC1D;AAAA,EACD;AACA,YAAU,MAAM;AACjB;AAEO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,sBAAsB,EAC9B,YAAY,qFAAqF,EACjG,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,OAAO,aAAqB,OAAkB,YAAqB;AAC1E,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,yBAAyB,KAAK,KAAK,aAAa,KAAK;AAAA,EAC5D,CAAC;AACH;;;AC/CA,IAAAC,sBAA2B;AAC3B,IAAAC,gBAAmC;AA2B5B,SAAS,mBACf,OACA,UAAkC,CAAC,GAC1B;AACT,QAAM,UAAU,MAAM,SAAS;AAC/B,QAAM,cAAc,MAAM,aAAa;AACvC,MAAI,YAAY,aAAa;AAC5B,UAAM,IAAI,SAAS,gDAAgD,SAAS,eAAe;AAAA,EAC5F;AACA,MAAI,SAAS;AACZ,QAAI,CAAC,MAAM,KAAM,KAAK,EAAG,OAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAChG,WAAO,MAAM;AAAA,EACd;AACA,SAAO,cAAc,MAAM,UAAW,EAAE,WAAW,QAAQ,UAAU,CAAC;AACvE;AAEA,eAAsB,4BACrB,KACA,QACA,aACA,OACA,gBACgB;AAChB,QAAM,YAAY,YAAY,KAAK;AACnC,MAAI,CAAC,WAAW;AACf,UAAM,IAAI,SAAS,mCAAmC,SAAS,eAAe;AAAA,EAC/E;AACA,QAAM,WAAW,MAAM,WAAW,IAAI,KAAK;AAC3C,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,+CAA+C,SAAS,eAAe;AAAA,EAC3F;AACA,QAAM,OAAO,mBAAmB,OAAO,EAAE,WAAW,IAAI,UAAU,CAAC;AAInE,QAAM,WAAW,MAAM,UAAU,KAAK;AACtC,QAAM,UAAmC,EAAE,SAAS,KAAK;AACzD,MAAI,SAAU,SAAQ,aAAa;AAEnC,QAAM,EAAE,MAAM,SAAS,IAAI,MAAMC;AAAA,IAChC,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,YAAY,mBAAmB,SAAS,CAAC;AAAA,IACzC;AAAA,IACA,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,aAAS,kCAAkC,QAAQ;AACzD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,MAAM;AAChB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,cAAc,OAAO,iBAAiB,SAAS,aAAa,OAAO,WAAW,WAAW;AAAA,CAAK;AACpH;AAEO,SAAS,iCAAiC,QAAuB;AACvE,SACE,QAAQ,yBAAyB,EACjC,YAAY,gFAAgF,EAC5F,eAAe,uBAAuB,2BAA2B,EACjE,OAAO,iBAAiB,8BAA8B,EACtD,OAAO,sBAAsB,iDAAiD,EAC9E,OAAO,yBAAyB,+EAA+E,EAC/G,OAAO,2BAA2B,uEAAuE,EACzG,OAAO,OAAO,aAAqB,OAAqB,YAAqB;AAC7E,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,4BAA4B,KAAK,KAAK,aAAa,OAAO,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EACrG,CAAC;AACH;;;AC3FO,SAAS,iCAAiC,QAAuB;AAGvE,QAAM,UAAU,OACd,QAAQ,SAAS,EACjB;AAAA,IACA;AAAA,EACD;AACD,gCAA8B,OAAO;AACrC,gCAA8B,OAAO;AACrC,mCAAiC,OAAO;AACzC;;;ACfA,IAAAC,sBAA2B;AAC3B,IAAAC,gBAAmC;AAuBnC,eAAsB,kBACrB,KACA,QACA,QACA,gBACgB;AAChB,QAAM,eAAe,eAAe,QAAQ,EAAE,WAAW,IAAI,UAAU,CAAC;AACxE,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,IACA;AAAA,IACA,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,cAAU,kCAAoC,IAAI;AACxD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,OAAO;AACjB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,qBAAqB,QAAQ,UAAU,EAAE;AAAA,CAAI;AACnE;AAEO,SAAS,kCAAkC,QAAuB;AACxE,SACE,QAAQ,QAAQ,EAChB,YAAY,wEAAwE,EACpF,eAAe,iBAAiB,gDAAgD,EAChF,OAAO,2BAA2B,yEAAyE,EAC3G,OAAO,OAAO,OAAoB,YAAqB;AACvD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,kBAAkB,KAAK,KAAK,MAAM,MAAgB,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EAC7F,CAAC;AACH;;;ACzDA,IAAAC,gBAAgC;AAkBhC,IAAMC,cAAqC,EAAE,MAAM,GAAG,QAAQ,EAAE;AAEhE,IAAMC,uBAAsB,CAAC,UAAU,QAAQ,UAAU,WAAW;AAgBpE,SAASC,eAAc,KAA6C;AACnE,MAAI,QAAQ,OAAW,QAAO;AAC9B,MAAI,OAAO,UAAU,eAAe,KAAKF,aAAY,GAAG,EAAG,QAAOA,YAAW,GAAG;AAChF,QAAM,IAAI,SAAS,qBAAqB,GAAG,eAAe,OAAO,KAAKA,WAAU,EAAE,KAAK,IAAI,CAAC,IAAI,SAAS,eAAe;AACzH;AAEO,SAASG,cAAa,QAAoC;AAChE,MAAI,WAAW,EAAG,QAAO;AACzB,MAAI,WAAW,EAAG,QAAO;AACzB,SAAO,WAAW,SAAY,KAAK,OAAO,MAAM;AACjD;AAEA,SAASC,aAAY,OAA0C;AAC9D,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,OAAO,KAAK,IAAI;AAC9E;AAEA,eAAsB,2BACrB,KACA,QACA,OACgB;AAChB,MAAI,MAAM,UAAU,MAAM,SAAS;AAClC,UAAM,IAAI,SAAS,8CAA8C,SAAS,eAAe;AAAA,EAC1F;AACA,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E;AAAA,IACA;AAAA,MACC,aAAa,MAAM;AAAA,MACnB,UAAU,MAAM;AAAA,MAChB,SAAS,MAAM;AAAA,MACf,QAAQH,eAAc,MAAM,MAAM;AAAA,IACnC;AAAA,EACD;AACA,QAAM,YAAQ,+BAA8B,IAAI;AAEhD,QAAM,aAAa,MAAM,UAAUD,uBAAsB,MAAM,SAAS,gBAAgB,MAAM,MAAM,IAAI;AACxG,MAAI,cAAc,IAAI,WAAW,QAAQ;AACxC,cAAU,aAAa,EAAE,GAAG,OAAO,MAAM,MAAM,KAAK,IAAI,CAAC,QAAQ,UAAU,KAAK,UAAU,CAAC,EAAE,IAAI,KAAK;AACtG;AAAA,EACD;AAEA;AAAA,IACC,MAAM;AAAA,IACN;AAAA,MACC,EAAE,QAAQ,UAAU,OAAO,CAAC,OAAO,EAAE,UAAU,IAAI,MAAM,GAAG,EAAE,EAAE;AAAA,MAChE,EAAE,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,QAAQ,IAAI,UAAU,GAAG;AAAA,MAC3D,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAME,cAAa,EAAE,MAAM,EAAE;AAAA,MACzD,EAAE,QAAQ,QAAQ,OAAO,CAAC,MAAMC,aAAY,EAAE,SAAS,EAAE;AAAA,MACzD,EAAE,QAAQ,MAAM,OAAO,CAAC,MAAMA,aAAY,EAAE,EAAE,EAAE;AAAA,MAChD,EAAE,QAAQ,YAAY,OAAO,CAAC,MAAMA,aAAY,EAAE,cAAc,EAAE;AAAA,IACnE;AAAA,IACA,IAAI;AAAA,EACL;AACA,QAAM,OAAO,gBAAgB,MAAM,WAAW,IAAI,MAAM,UAAU,KAAK,MAAM,KAAK,MAAM,OAAO,MAAM,UAAU;AAC/G,QAAM,OACL,MAAM,aAAa,MAAM,cAAc,kDAAkD,MAAM,cAAc,CAAC,KAAK;AACpH,UAAQ,OAAO,MAAM,IAAI,OAAO,MAAM,IAAI,KAAK,IAAI,IAAI;AACxD;AAEO,SAAS,gCAAgC,QAAuB;AACtE,SACE,QAAQ,MAAM,EACd,YAAY,iCAAiC,EAC7C,OAAO,kBAAkB,gDAAgD,CAAC,MAAM,OAAO,CAAC,CAAC,EACzF,OAAO,uBAAuB,wCAAwC,CAAC,MAAM,OAAO,CAAC,CAAC,EACtF,OAAO,sBAAsB,iCAAiC,EAC9D,OAAO,oBAAoB,iCAAiC,EAC5D,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,aAAa,eAAeH,qBAAoB,KAAK,GAAG,CAAC,UAAU,EAC1E,OAAO,OAAO,OAAkB,YAAqB;AACrD,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,2BAA2B,KAAK,KAAK,KAAK;AAAA,EACjD,CAAC;AACH;;;AChHA,IAAAK,sBAA2B;AAC3B,IAAAC,gBAAmC;AAgBnC,eAAsB,sBACrB,KACA,QACA,OACA,QACA,gBACgB;AAChB,QAAM,UAAU,aAAa,KAAK;AAClC,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,cAAc,mBAAmB,OAAO,CAAC,IAAI,MAAM;AAAA,IACnD,CAAC;AAAA,IACD,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,aAAS,kCAAoC,IAAI;AACvD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,MAAM;AAChB;AAAA,EACD;AACA,QAAM,OAAO,WAAW,YAAY,cAAc;AAClD,UAAQ,OAAO,MAAM,GAAG,IAAI,cAAc,OAAO,UAAU,OAAO;AAAA,CAAI;AACvE;AAEO,SAAS,mCAAmC,QAAuB;AACzE,SACE,QAAQ,kBAAkB,EAC1B,YAAY,2CAA2C,EACvD,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAuB,YAAqB;AACzE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,EAAE,IAAI,IAAI,cAAe,QAAQ,gBAAgB,EAA0B,MAAM;AACvF,UAAM,sBAAsB,KAAK,KAAK,OAAO,WAAW,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EAC7F,CAAC;AACH;AAEO,SAAS,qCAAqC,QAAuB;AAC3E,SACE,QAAQ,oBAAoB,EAC5B,YAAY,+CAA+C,EAC3D,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAuB,YAAqB;AACzE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,EAAE,IAAI,IAAI,cAAe,QAAQ,gBAAgB,EAA0B,MAAM;AACvF,UAAM,sBAAsB,KAAK,KAAK,OAAO,aAAa,MAAM,sBAAkB,gCAAW,CAAC;AAAA,EAC/F,CAAC;AACH;;;AC9DA,IAAAC,uBAA2B;AAC3B,IAAAC,gBAAmC;AAkBnC,eAAsB,kBACrB,KACA,QACA,OACA,QACA,gBACgB;AAChB,QAAM,UAAU,aAAa,KAAK;AAClC,QAAM,eAAe,eAAe,QAAQ,EAAE,WAAW,IAAI,UAAU,CAAC;AACxE,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,cAAc,mBAAmB,OAAO,CAAC;AAAA,IACzC;AAAA,IACA,EAAE,eAAe;AAAA,EAClB;AACA,QAAM,cAAU,kCAAoC,IAAI;AACxD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,OAAO;AACjB;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,qBAAqB,QAAQ,UAAU,OAAO;AAAA,CAAI;AACxE;AAEO,SAAS,kCAAkC,QAAuB;AACxE,SACE,QAAQ,iBAAiB,EACzB,YAAY,wEAAwE,EACpF,eAAe,iBAAiB,wDAAwD,EACxF,OAAO,2BAA2B,8CAA8C,EAChF,OAAO,OAAO,OAAe,OAAoB,YAAqB;AACtE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,kBAAkB,KAAK,KAAK,OAAO,MAAM,MAAgB,MAAM,sBAAkB,iCAAW,CAAC;AAAA,EACpG,CAAC;AACH;;;ACtDA,IAAAC,gBAAmC;AAanC,eAAsB,2BACrB,KACA,QACA,OACA,OACgB;AAChB,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,EACxE;AACA,QAAM,EAAE,KAAK,IAAI,MAAMC;AAAA,IACtB,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,WAAW,OAAO;AAAA,IAC7E,cAAc,mBAAmB,OAAO,CAAC;AAAA,EAC1C;AACA,QAAM,eAAW,kCAA4C,IAAI;AACjE,MAAI,MAAM,QAAQ;AACjB,cAAU,UAAU,UAAU,gBAAgB,MAAM,MAAM,CAAC,CAAC;AAC5D;AAAA,EACD;AACA,YAAU,QAAQ;AACnB;AAEO,SAAS,gCAAgC,QAAuB;AACtE,SACE,QAAQ,eAAe,EACvB,YAAY,+CAA+C,EAC3D,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,OAAO,OAAe,OAAkB,YAAqB;AACpE,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,EAAE,IAAI,IAAI,cAAc,QAAQ,MAAM;AAC5C,UAAM,2BAA2B,KAAK,KAAK,OAAO,KAAK;AAAA,EACxD,CAAC;AACH;;;ACxCO,SAAS,mCAAmC,QAAuB;AACzE,QAAM,YAAY,OAAO,QAAQ,WAAW,EAAE,YAAY,mCAAmC;AAC7F,kCAAgC,SAAS;AACzC,kCAAgC,SAAS;AACzC,oCAAkC,SAAS;AAC3C,oCAAkC,SAAS;AAC3C,qCAAmC,SAAS;AAC5C,uCAAqC,SAAS;AAC/C;;;ACLO,SAAS,0BAA0BC,UAAwB;AACjE,QAAM,aAAaA,SACjB,QAAQ,YAAY,EACpB,YAAY,6DAA6D,EACzE,OAAO,mBAAmB,gFAAgF;AAC5G,0BAAwB,UAAU;AAClC,2BAAyB,UAAU;AACnC,2BAAyB,UAAU;AACnC,0BAAwB,UAAU;AAClC,gCAA8B,UAAU;AACxC,gCAA8B,UAAU;AACxC,mCAAiC,UAAU;AAC3C,mCAAiC,UAAU;AAC3C,qCAAmC,UAAU;AAC9C;;;ACxBA,qBAAuC;;;ACDvC,gCAA4D;AAkB5D,SAAS,aAAa,KAAa,UAAoC;AACtE,MAAI;AACJ,MAAI;AACH,aAAS,IAAI,IAAI,GAAG;AAAA,EACrB,QAAQ;AACP,WAAO;AAAA,EACR;AACA,MAAI,OAAO,aAAa,YAAY,OAAO,aAAa,QAAS,QAAO;AACxE,MAAI,aAAa,WAAW,YAAY,KAAK,GAAG,EAAG,QAAO;AAC1D,SAAO;AACR;AAGA,SAAS,WAAW,UAA2B,KAAoC;AAClF,UAAQ,UAAU;AAAA,IACjB,KAAK;AACJ,aAAO,EAAE,SAAS,QAAQ,MAAM,CAAC,GAAG,EAAE;AAAA,IACvC,KAAK;AAEJ,aAAO,EAAE,SAAS,OAAO,MAAM,CAAC,MAAM,SAAS,MAAM,GAAG,EAAE;AAAA,IAC3D,KAAK;AACJ,aAAO,EAAE,SAAS,YAAY,MAAM,CAAC,GAAG,EAAE;AAAA,IAC3C;AACC,aAAO;AAAA,EACT;AACD;AAeO,SAAS,cAAc,KAAa,UAAmB,iCAAgB;AAC7E,MAAI,CAAC,aAAa,KAAK,QAAQ,QAAQ,EAAG,QAAO;AACjD,QAAM,WAAW,WAAW,QAAQ,UAAU,GAAG;AACjD,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI;AACH,UAAM,QAAQ,QAAQ,SAAS,SAAS,SAAS,MAAM,EAAE,UAAU,MAAM,OAAO,SAAS,CAAC;AAC1F,UAAM,GAAG,SAAS,MAAM;AAAA,IAGxB,CAAC;AACD,UAAM,MAAM;AACZ,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;;;ADpDA,SAAS,iBAAiB,aAAqB,OAAuB;AACrE,MAAI,eAAe,MAAO,QAAO,GAAG,WAAW,KAAK,KAAK;AACzD,MAAI,YAAa,QAAO;AACxB,MAAI,MAAO,QAAO;AAClB,SAAO;AACR;AASA,eAAsBC,cAAa,KAAsB,OAAmB,OAAkB,CAAC,GAAkB;AAChH,QAAM,SAAS,cAAc;AAC7B,MAAI,OAAQ,SAAQ,OAAO,MAAM,MAAM;AAEvC,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,aAAa,KAAK,YAAY,eAAAC;AAEpC,MAAI,CAAC,MAAM,OAAO;AACjB,UAAM,WAAW,wBAAwB;AACzC,QAAI,UAAU;AACb,cAAQ,OAAO,MAAM,wBAAwB,iBAAiB,SAAS,cAAc,SAAS,KAAK,CAAC;AAAA,CAAK;AACzG,cAAQ,OAAO,MAAM,+CAA+C;AACpE;AAAA,IACD;AAAA,EACD;AAEA,QAAM,YAAiC,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC5G,QAAM,SAAS,MAAM,kBAAkB,WAAW,WAAW,CAAC;AAG9D,UAAQ,OAAO,MAAM;AAAA;AAAA;AAAA,IAAyC,OAAO,SAAS;AAAA;AAAA,CAAM;AACpF,UAAQ,OAAO,MAAM,UAAU,OAAO,gBAAgB;AAAA,CAAI;AAE1D,MAAI,CAAC,MAAM,aAAa,QAAQ,OAAO,OAAO;AAC7C,UAAM,SAAS,YAAY,OAAO,yBAAyB;AAC3D,QAAI,CAAC,QAAQ;AACZ,cAAQ,OAAO,MAAM,2BAA2B,OAAO,yBAAyB;AAAA,CAAI;AAAA,IACrF,OAAO;AAGN,cAAQ,OAAO,MAAM,uDAAuD,OAAO,yBAAyB;AAAA,CAAI;AAAA,IACjH;AAAA,EACD;AAEA,QAAM,SAAS,MAAM,aAAa,WAAW,QAAQ,KAAK,KAAK;AAE/D,QAAM,MAAM,KAAK,IAAI;AACrB,0BAAwB;AAAA,IACvB,cAAc,OAAO;AAAA,IACrB,eAAe,OAAO;AAAA,IACtB,YAAY,IAAI,KAAK,MAAM,OAAO,aAAa,GAAI,EAAE,YAAY;AAAA,IACjE,cAAc;AAAA,IACd,OAAO;AAAA,IACP,oBAAoB,IAAI,KAAK,GAAG,EAAE,YAAY;AAAA,EAC/C,CAAC;AAED,UAAQ,OAAO,MAAM,cAAc;AACpC;AAEO,SAAS,qBAAqBC,UAAwB;AAC5D,EAAAA,SACE,QAAQ,OAAO,EACf,YAAY,0EAA0E,EACtF,OAAO,gBAAgB,4CAA4C,EACnE,OAAO,WAAW,yCAAyC,EAC3D,OAAO,OAAO,MAA8C,YAAqB;AACjF,UAAM,MAAM,eAAe,OAAO;AAClC,UAAMF,cAAa,KAAK,EAAE,WAAW,KAAK,YAAY,OAAO,OAAO,KAAK,MAAM,CAAC;AAAA,EACjF,CAAC;AACH;;;AE7FA,IAAAG,gBAAgC;;;ACDhC,IAAAC,gBAA0F;AA0B1F,IAAM,iBAAiB;AAEvB,IAAM,wBAAwB;AAO9B,eAAe,uBAAuB,MAA4D;AACjG,QAAM,QAAQ,wBAAwB;AACtC,MAAI,CAAC,MAAO,OAAM,IAAI,SAAS,uBAAuB,SAAS,iBAAiB;AAChF,QAAM,gBAAgB,KAAK,MAAM,MAAM,UAAU,IAAI,KAAK,IAAI;AAC9D,MAAI,iBAAiB,eAAgB,QAAO;AAC5C,SAAO,eAAe,MAAM,KAAK;AAClC;AAQA,eAAe,eAAe,MAA8B,OAA0D;AACrH,QAAM,SAAS,MAAM,mBAAmB,MAAM,MAAM,aAAa;AACjE,QAAM,UAA+B;AAAA,IACpC,GAAG;AAAA,IACH,cAAc,OAAO;AAAA,IACrB,eAAe,OAAO;AAAA,IACtB,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,aAAa,GAAI,EAAE,YAAY;AAAA,EACzE;AACA,0BAAwB,OAAO;AAC/B,SAAO;AACR;AAYA,eAAe,eACd,MACA,QACA,MACA,aACA,MACA,OACA,OAC4B;AAC5B,QAAM,MAAM,IAAI,IAAI,GAAG,KAAK,OAAO,GAAG,IAAI,EAAE;AAC5C,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAS,CAAC,CAAC,GAAG;AACjD,QAAI,MAAM,OAAW,KAAI,aAAa,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,EACvD;AACA,QAAM,UAAkC;AAAA,IACvC,eAAe,UAAU,WAAW;AAAA,IACpC,mBAAmB,KAAK;AAAA,IACxB,kBAAc,8BAAe,aAAa,OAAe;AAAA,IACzD,YAAY;AAAA,IACZ,QAAQ;AAAA,EACT;AACA,MAAI,SAAS,OAAW,SAAQ,cAAc,IAAI;AAClD,MAAI,OAAO,eAAgB,SAAQ,iBAAiB,IAAI,MAAM;AAC9D,QAAM,UAAU,IAAI,QAAQ,KAAK;AAAA,IAChC;AAAA,IACA;AAAA,IACA,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EACnD,CAAC;AACD,QAAM,MAAM,UAAM,gCAAiB,SAAS,KAAK,SAAS;AAC1D,QAAM,WAAoB,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAC3D,SAAO,EAAE,QAAQ,IAAI,QAAQ,MAAM,UAAU,SAAS,IAAI,QAAQ;AACnE;AAOA,eAAe,gBACd,MACA,QACA,MACA,MACA,OACA,OAC0B;AAC1B,QAAM,QAAQ,MAAM,uBAAuB,IAAI;AAC/C,MAAI,SAAS,MAAM,eAAe,MAAM,QAAQ,MAAM,MAAM,cAAc,MAAM,OAAO,KAAK;AAC5F,MAAI,OAAO,WAAW,KAAK;AAC1B,UAAM,YAAY,MAAM,eAAe,MAAM,KAAK;AAClD,aAAS,MAAM,eAAe,MAAM,QAAQ,MAAM,UAAU,cAAc,MAAM,OAAO,KAAK;AAAA,EAC7F;AACA,MAAI,OAAO,SAAS,OAAO,OAAO,UAAU,IAAK,wBAAuB,OAAO,QAAQ,OAAO,IAAI;AAClG,EAAAC,oBAAmB,OAAO,OAAO;AACjC,SAAO,EAAE,MAAM,OAAO,MAAM,SAAS,OAAO,QAAQ;AACrD;AAGO,SAAS,YACf,MACA,MACA,OAC0B;AAC1B,SAAO,gBAAgB,MAAM,OAAO,MAAM,QAAW,OAAO,MAAS;AACtE;AAGO,SAAS,aACf,MACA,MACA,MACA,OAC0B;AAC1B,SAAO,gBAAgB,MAAM,QAAQ,MAAM,MAAM,QAAW,KAAK;AAClE;AAGO,SAAS,cACf,MACA,MACA,MACA,OAC0B;AAC1B,SAAO,gBAAgB,MAAM,SAAS,MAAM,MAAM,QAAW,KAAK;AACnE;AAGO,SAAS,YACf,MACA,MACA,MACA,OAC0B;AAC1B,SAAO,gBAAgB,MAAM,OAAO,MAAM,MAAM,QAAW,KAAK;AACjE;AAGO,SAAS,eAAe,MAA8B,MAAc,OAAqD;AAC/H,SAAO,gBAAgB,MAAM,UAAU,MAAM,QAAW,QAAW,KAAK;AACzE;AAMA,SAAS,uBAAuB,QAAgB,MAAsB;AACrE,MAAI,WAAW,KAAK;AACnB,UAAM,WAAO,mCAAoB,IAAI,KAAK,QAAQ,MAAM;AACxD,UAAM,IAAI;AAAA,MACT,GAAG,IAAI;AAAA,MACP,SAAS;AAAA,IACV;AAAA,EACD;AACA,wCAAmB,QAAQ,IAAI;AAChC;AAGA,SAASA,oBAAmB,SAAwB;AACnD,QAAM,YAAY,OAAO,QAAQ,IAAI,uBAAuB,CAAC;AAC7D,QAAM,QAAQ,OAAO,QAAQ,IAAI,mBAAmB,CAAC;AACrD,MAAI,OAAO,SAAS,SAAS,KAAK,OAAO,SAAS,KAAK,KAAK,QAAQ,KAAK,YAAY,QAAQ,KAAK;AACjG,cAAU,gCAAgC,SAAS,IAAI,KAAK,oCAAoC,KAAK;AAAA,EACtG;AACD;;;AD3LA,IAAAC,gBAAiD;AAEjD,IAAM,cAAc;AAQpB,SAAS,QAAQ,OAA0C;AAC1D,QAAM,QAAQ,oBAAoB,SAAS,EAAE;AAC7C,SAAO,MAAM,SAAS,IAAI,QAAQ;AACnC;AAaA,eAAsB,cAAc,KAAqC;AACxE,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,EAAE,KAAK,IAAI,MAAM,YAAY,MAAM,iCAAmB;AAC5D,QAAM,eAAW,+BAA6B,IAAI;AAClD,QAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU;AAEjD,QAAM,eAAe,wBAAwB,GAAG,sBAAsB;AAEtE,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU;AAAA,MACT,WAAW;AAAA,MACX,aAAa,SAAS,eAAe;AAAA,MACrC,oBAAoB,SAAS,cAAc;AAAA,MAC3C,cAAc,SAAS,gBAAgB;AAAA,MACvC,gBAAgB;AAAA,IACjB,CAAC;AACD;AAAA,EACD;AAEA,QAAM,QAAQ;AAAA,IACb;AAAA,IACA,sBAAsB,QAAQ,SAAS,WAAW,CAAC;AAAA,IACnD,sBAAsB,QAAQ,SAAS,UAAU,CAAC;AAAA,IAClD,sBAAsB,QAAQ,SAAS,YAAY,CAAC;AAAA,IACpD,sBAAsB,QAAQ,YAAY,CAAC;AAAA,EAC5C;AACA,UAAQ,OAAO,MAAM,MAAM,KAAK,IAAI,IAAI,IAAI;AAC7C;AAEO,SAAS,sBAAsBC,UAAwB;AAC7D,EAAAA,SACE,QAAQ,QAAQ,EAChB,YAAY,gEAAgE,EAC5E,OAAO,OAAO,QAAiB,YAAqB;AACpD,UAAM,cAAc,eAAe,OAAO,CAAC;AAAA,EAC5C,CAAC;AACH;;;AEpDA,eAAsB,cAAc,KAAqC;AACxE,QAAM,QAAQ,wBAAwB;AACtC,MAAI,CAAC,OAAO;AACX,YAAQ,OAAO,MAAM,kBAAkB;AACvC;AAAA,EACD;AAEA,QAAM,OAA4B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AACvG,MAAI;AACH,UAAM,mBAAmB,MAAM,MAAM,aAAa;AAAA,EACnD,SAAS,KAAK;AACb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAU,+CAA+C,OAAO,wCAAwC,IAAI,KAAK;AAAA,EAClH;AAEA,4BAA0B;AAC1B,UAAQ,OAAO,MAAM,eAAe;AACrC;AAEO,SAAS,sBAAsBC,UAAwB;AAC7D,EAAAA,SACE,QAAQ,QAAQ,EAChB,YAAY,kGAAkG,EAC9G,OAAO,OAAO,QAAiB,YAAqB;AACpD,UAAM,cAAc,eAAe,OAAO,CAAC;AAAA,EAC5C,CAAC;AACH;;;ACzCA,IAAAC,gBAAgC;AAIhC,IAAAC,gBAAiD;AAGjD,SAASC,YAAW,OAA0C;AAC7D,SAAO,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI;AAC7C;AAMA,eAAsB,gBAAgB,KAAqC;AAC1E,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,EAAE,KAAK,IAAI,MAAM,YAAY,MAAM,iCAAmB;AAC5D,QAAM,eAAW,+BAA6B,IAAI;AAElD,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,QAAQ;AAClB;AAAA,EACD;AAEA;AAAA,IACC;AAAA,IACA;AAAA,MACC,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,EAAE,OAAO,MAAM,GAAG,EAAE,EAAE;AAAA,MACxD,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,EAAE,eAAe,IAAI,UAAU,GAAG;AAAA,MACpE,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAMA,YAAW,EAAE,UAAU,GAAG,UAAU,GAAG;AAAA,MAC1E,EAAE,QAAQ,aAAa,OAAO,CAAC,MAAMA,YAAW,EAAE,YAAY,GAAG,UAAU,GAAG;AAAA,MAC9E,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAO,EAAE,aAAa,MAAM,GAAI;AAAA,IAC9D;AAAA,IACA,IAAI;AAAA,EACL;AACD;AAEO,SAAS,qBAAqB,QAAuB;AAC3D,SACE,QAAQ,MAAM,EACd,YAAY,oDAAoD,EAChE,OAAO,OAAO,QAAiB,YAAqB;AACpD,UAAM,gBAAgB,eAAe,OAAO,CAAC;AAAA,EAC9C,CAAC;AACH;;;AC7CA,IAAAC,gBAAoC;AACpC,IAAAA,gBAAmC;AAyBnC,eAAsB,kBACrB,KACA,OACA,WACgB;AAChB,QAAM,WAAW,UAAU;AAC3B,MAAI,aAAa,WAAW;AAC3B,UAAM,IAAI,SAAS,mDAAmD,SAAS,eAAe;AAAA,EAC/F;AAEA,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAE1G,MAAI,WAAW;AACd,UAAM,EAAE,KAAK,IAAI,MAAM,eAAe,MAAM,GAAG,iCAAmB,SAAS;AAC3E,UAAM,aAAS,kCAAuC,IAAI;AAC1D,QAAI,IAAI,WAAW,QAAQ;AAC1B,gBAAU,MAAM;AAChB;AAAA,IACD;AACA,YAAQ,OAAO,MAAM,WAAW,OAAO,aAAa;AAAA,CAAsB;AAC1E;AAAA,EACD;AAEA,QAAM,UAAU,MAAO,KAAK;AAC5B,MAAI,CAAC,QAAS,OAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAGrF,QAAM,eAAe,MAAM,GAAG,iCAAmB,IAAI,mBAAmB,OAAO,CAAC,EAAE;AAClF,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,QAAQ,SAAS,SAAS,KAAK,CAAC;AAC5C;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,mBAAmB,OAAO;AAAA,CAAK;AACrD;AAEO,SAAS,uBAAuB,QAAuB;AAC7D,SACE,QAAQ,iBAAiB,EACzB,YAAY,sEAAsE,EAClF,OAAO,gBAAgB,6CAA6C,EACpE,OAAO,OAAO,OAA2B,OAAoB,YAAqB;AAClF,UAAM,MAAM,eAAe,OAAO;AAClC,UAAM,kBAAkB,KAAK,OAAO,MAAM,cAAc,IAAI;AAAA,EAC7D,CAAC;AACH;;;ACnEO,SAAS,wBAAwBC,UAAwB;AAC/D,QAAM,WAAWA,SAAQ,QAAQ,UAAU,EAAE,YAAY,wDAAwD;AACjH,uBAAqB,QAAQ;AAC7B,yBAAuB,QAAQ;AAChC;;;ACPA,IAAAC,gBAAyC;AAOlC,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,QAAQ,EAChB,YAAY,qEAAqE,EACjF,OAAO,oBAAoB,gEAAgE,EAC3F,OAAO,CAAC,SAA+B;AACvC,kBAAU,wCAAyB,KAAK,OAAO,CAAC;AAAA,EACjD,CAAC;AACH;;;ACfA,IAAAC,gBAAkC;AAgB3B,SAAS,mBAAmB,KAAsB,UAAwB;AAChF,QAAM,QAAQ,cAAc,UAAU,EAAE,WAAW,IAAI,UAAU,CAAC;AAClE,QAAM,aAAS,iCAAkB,KAAK;AAEtC,MAAI,OAAO,WAAW,GAAG;AACxB,QAAI,IAAI,WAAW,QAAQ;AAC1B,gBAAU,EAAE,OAAO,KAAK,CAAC;AAAA,IAC1B,OAAO;AACN,cAAQ,OAAO,MAAM,UAAU;AAAA,IAChC;AACA;AAAA,EACD;AAEA,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,OAAO,OAAO,OAAO,CAAC;AAAA,EACnC,OAAO;AACN,eAAW,SAAS,QAAQ;AAC3B,cAAQ,OAAO,MAAM,GAAG,oBAAoB,MAAM,IAAI,CAAC,KAAK,oBAAoB,MAAM,OAAO,CAAC;AAAA,CAAI;AAAA,IACnG;AAAA,EACD;AACA,QAAM,IAAI,SAAS,GAAG,OAAO,MAAM,8BAA8B,SAAS,eAAe;AAC1F;AAEO,SAAS,gCAAgC,QAAuB;AACtE,SACE,QAAQ,UAAU,EAClB,YAAY,4EAA4E,EACxF,eAAe,iBAAiB,mDAAmD,EACnF,OAAO,CAAC,OAAsB,YAAqB;AACnD,UAAM,MAAM,eAAe,OAAO;AAClC,uBAAmB,KAAK,MAAM,IAAI;AAAA,EACnC,CAAC;AACH;;;AChDA,IAAAC,kBAA0C;AAC1C,IAAAC,gBAA8B;AAavB,SAAS,mBAAmB,SAAwB;AAC1D,QAAM,eAAW,6BAAc;AAE/B,MAAI,YAAY,QAAW;AAC1B,cAAU,QAAQ;AAClB;AAAA,EACD;AAEA,UAAI,4BAAW,OAAO,GAAG;AACxB,UAAM,IAAI,qBAAqB,wBAAwB,OAAO,4EAAuE;AAAA,EACtI;AAEA,MAAI;AACH,uCAAc,SAAS,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAAA,EACxE,SAAS,KAAK;AACb,UAAM,IAAI,qBAAqB,+BAA+B,OAAO,KAAM,IAA8B,OAAO,EAAE;AAAA,EACnH;AAEA,UAAQ,OAAO,MAAM,qBAAqB,OAAO;AAAA,CAAI;AACtD;AAEO,SAAS,gCAAgC,QAAuB;AACtE,SACE,QAAQ,UAAU,EAClB,YAAY,+GAA+G,EAC3H,OAAO,gBAAgB,sFAAsF,EAC7G,OAAO,CAAC,UAAyB;AACjC,uBAAmB,MAAM,GAAG;AAAA,EAC7B,CAAC;AACH;;;AC3CA,IAAAC,gBAAmC;AAMnC,IAAAC,gBAA6E;AAQ7E,IAAMC,uBAAsB,CAAC,UAAU,QAAQ,cAAc,eAAe,cAAc;AAG1F,SAASC,YAAW,OAA0C;AAC7D,SAAO,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI;AAC7C;AAGA,SAAS,gBAAgB,OAA4B;AACpD,QAAM,SAAS,MAAM,aAAa,eAAe,gBAAgB,MAAM,cAAc,KAAK,MAAM,WAAW,MAAM,EAAE;AACnH,SAAO,GAAG,MAAM,IAAI,IAAI,MAAM,WAAW,UAAU,MAAM;AAC1D;AAOA,eAAsB,gBAAgB,MAAuD;AAC5F,QAAM,EAAE,KAAK,IAAI,MAAM,YAAY,MAAM,mCAAqB;AAC9D,aAAO,kCAAmC,IAAI;AAC/C;AAOA,eAAsB,eAAe,KAAsB,OAAiC;AAC3F,MAAI,MAAM,UAAU,MAAM,SAAS;AAClC,UAAM,IAAI,SAAS,8CAA8C,SAAS,eAAe;AAAA,EAC1F;AACA,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,OAAO,MAAM,gBAAgB,IAAI;AAEvC,QAAM,aAAa,MAAM,UAAUD,uBAAsB,MAAM,SAAS,gBAAgB,MAAM,MAAM,IAAI;AACxG,MAAI,cAAc,IAAI,WAAW,QAAQ;AACxC,cAAU,aAAa,EAAE,GAAG,MAAM,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,EAAE,IAAI,IAAI;AACrG;AAAA,EACD;AAEA;AAAA,IACC,KAAK;AAAA,IACL;AAAA,MACC,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAM,EAAE,OAAO,MAAM,GAAG,EAAE,EAAE;AAAA,MACxD,EAAE,QAAQ,QAAQ,OAAO,CAAC,MAAM,EAAE,MAAM,UAAU,GAAG;AAAA,MACrD,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAMC,YAAW,EAAE,UAAU,GAAG,UAAU,GAAG;AAAA,MAC1E,EAAE,QAAQ,YAAY,OAAO,CAAC,MAAO,EAAE,cAAc,QAAQ,KAAM;AAAA,MACnE,EAAE,QAAQ,aAAa,OAAO,CAAC,MAAO,EAAE,eAAe,QAAQ,KAAM;AAAA,IACtE;AAAA,IACA,IAAI;AAAA,EACL;AACA,UAAQ,OAAO,MAAM,IAAI,gBAAgB,KAAK,KAAK,GAAG,IAAI,KAAK,IAAI,IAAI;AACxE;AAEO,SAAS,4BAA4B,QAAuB;AAClE,SACE,QAAQ,MAAM,EACd,YAAY,4BAA4B,EACxC,OAAO,mBAAmB,uFAAuF,EACjH,OAAO,aAAa,eAAeD,qBAAoB,KAAK,GAAG,CAAC,UAAU,EAC1E,OAAO,OAAO,OAAkB,YAAqB;AACrD,UAAM,eAAe,eAAe,OAAO,GAAG,KAAK;AAAA,EACpD,CAAC;AACH;;;AC9EA,IAAAE,gBAAmC;AAMnC,IAAAC,gBAA4D;AAC5D,IAAAA,gBAA0C;AAW1C,eAAsB,qBAAqB,MAA8B,OAAyC;AACjH,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,EACxE;AACA,QAAM,EAAE,KAAK,IAAI,MAAM,YAAY,MAAM,GAAG,mCAAqB,IAAI,mBAAmB,OAAO,CAAC,EAAE;AAClG,aAAO,kCAAoC,IAAI;AAChD;AAkBA,SAAS,kBAAkB,QAA8C;AACxE,QAAM,OAA4B,CAAC,EAAE,SAAS,QAAQ,SAAS,OAAO,KAAK,CAAC;AAC5E,aAAW,OAAO,wBAAU;AAC3B,SAAK,KAAK,EAAE,SAAS,IAAI,KAAK,SAAS,iBAAiB,KAAK,MAAM,EAAE,CAAC;AAAA,EACvE;AACA,SAAO;AACR;AAEA,SAAS,iBAAiB,KAAiB,QAAiC;AAC3E,QAAM,QAAS,OAA8C,IAAI,GAAG;AACpE,MAAI,IAAI,SAAS,SAAS;AACzB,WAAO,GAAG,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS,CAAC;AAAA,EAClD;AACA,MAAI,IAAI,SAAS,WAAW;AAC3B,UAAM,UAAU;AAChB,QAAI,SAAS,uBAAwB,QAAO;AAC5C,WAAO,GAAG,MAAM,QAAQ,SAAS,KAAK,IAAK,QAAQ,MAAoB,SAAS,CAAC;AAAA,EAClF;AACA,MAAI,IAAI,SAAS,UAAU;AAC1B,WAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,YAAY;AAAA,EACpE;AAEA,SAAO,SAAS,OAAO,UAAU,YAAY,OAAO,KAAK,KAAe,EAAE,SAAS,IAAI,YAAY;AACpG;AAOA,eAAsB,eAAe,KAAsB,OAAe,OAAiC;AAC1G,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,SAAS,MAAM,qBAAqB,MAAM,KAAK;AAErD,MAAI,MAAM,QAAQ;AACjB,cAAU,UAAU,QAAQ,gBAAgB,MAAM,MAAM,CAAC,CAAC;AAC1D;AAAA,EACD;AACA,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,MAAM;AAChB;AAAA,EACD;AAEA;AAAA,IACC,kBAAkB,MAAM;AAAA,IACxB;AAAA,MACC,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,GAAG;AAAA,MAC3D,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,GAAG;AAAA,IAC5D;AAAA,IACA,IAAI;AAAA,EACL;AACD;AAEO,SAAS,4BAA4B,QAAuB;AAClE,SACE,QAAQ,eAAe,EACvB,YAAY,0CAA0C,EACtD,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,OAAO,OAAe,OAAkB,YAAqB;AACpE,UAAM,eAAe,eAAe,OAAO,GAAG,OAAO,KAAK;AAAA,EAC3D,CAAC;AACH;;;ACxGA,IAAAC,kBAAyC;AACzC,IAAAC,oBAAqB;AAkBrB,SAAS,mBAAmB,OAAuB;AAClD,MAAI,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,KAAK,UAAU,OAAO,UAAU,MAAM;AACnF,UAAM,IAAI;AAAA,MACT,mCAAmC,KAAK;AAAA,MACxC,SAAS;AAAA,IACV;AAAA,EACD;AACA,SAAO,UAAU,KAAK;AACvB;AAYA,eAAsB,iBAAiB,KAAsB,QAA+B;AAC3F,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,EAAE,QAAQ,IAAI,MAAM,gBAAgB,IAAI;AAE9C,iCAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AAErC,aAAW,QAAQ,SAAS;AAE3B,UAAM,eAAW,wBAAK,QAAQ,mBAAmB,KAAK,MAAM,CAAC;AAC7D,UAAM,YAAY,MAAM,qBAAqB,MAAM,KAAK,MAAM;AAC9D,uCAAc,UAAU,GAAG,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAAA,EAC1E;AAEA,UAAQ,OAAO,MAAM,YAAY,QAAQ,MAAM,iBAAiB,MAAM;AAAA,CAAK;AAC5E;AAEO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,QAAQ,EAChB,YAAY,qDAAqD,EACjE,OAAO,eAAe,oDAAoD,EAC1E,OAAO,OAAO,OAAoB,YAAqB;AACvD,UAAM,iBAAiB,eAAe,OAAO,GAAG,MAAM,OAAO,QAAQ,IAAI,CAAC;AAAA,EAC3E,CAAC;AACH;;;AC/DA,IAAAC,gBAAkC;;;ACelC,IAAAC,uBAA2B;AAC3B,IAAAC,gBAAmC;AAGnC,IAAAC,gBAAoF;AACpF,IAAAA,gBAA+D;AAiB/D,SAAS,sBAAkD;AAC1D,SAAO,EAAE,oBAAgB,iCAAW,EAAE;AACvC;AAGA,SAAS,cAAc,KAAsB;AAC5C,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACvD;AAGA,SAAS,iBAAiB,MAA8B;AACvD,MAAI,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAAiC,eAAe,UAAU;AACzG,WAAQ,KAAiC;AAAA,EAC1C;AACA,SAAO;AACR;AAQA,eAAe,YAAY,MAA+C;AACzE,MAAI;AACH,UAAM,EAAE,KAAK,IAAI,MAAM,aAAa,MAAM,qCAAuB,CAAC,GAAG,oBAAoB,CAAC;AAC1F,eAAO,kCAAqD,IAAI,EAAE;AAAA,EACnE,SAAS,KAAK;AACb,QAAI,eAAe,uBAAuB;AACzC,YAAM,OAAO,iBAAiB,IAAI,IAAI;AACtC,UAAI,SAAS,sBAAsB;AAClC,cAAM,IAAI;AAAA,UACT;AAAA,UACA,SAAS;AAAA,QACV;AAAA,MACD;AACA,UAAI,SAAS,wBAAwB;AACpC,cAAM,IAAI;AAAA,UACT;AAAA,UACA,SAAS;AAAA,QACV;AAAA,MACD;AAAA,IACD;AACA,UAAM;AAAA,EACP;AACD;AAGA,eAAe,aAAa,MAA8B,OAAe,MAA6B;AACrG,QAAM,YAAY,MAAM,GAAG,mCAAqB,IAAI,mBAAmB,KAAK,CAAC,SAAS,EAAE,KAAK,GAAiC,oBAAoB,CAAC;AACpJ;AAGA,SAAS,0BAA0B,MAAwD;AAC1F,QAAM,EAAE,YAAY,GAAG,KAAK,IAAI;AAChC,SAAO,EAAE,GAAG,MAAM,YAAY,aAAa,IAAI,EAAE;AAClD;AAOA,SAAS,eAAe,MAAyE;AAChG,QAAM,EAAE,QAAQ,GAAG,KAAK,IAAI;AAC5B,QAAM,QAAQ,OAAO,WAAW,YAAY,OAAO,KAAK,IAAI,OAAO,KAAK,IAAI;AAC5E,SAAO,CAAC,OAAO,IAAI;AACpB;AAeA,eAAe,aACd,MACA,OACA,KACA,OACA,MACgB;AAChB,QAAM,OAAO,iCAAmB,GAAG;AACnC,QAAM,OAAO,GAAG,mCAAqB,IAAI,mBAAmB,KAAK,CAAC;AAElE,UAAQ,KAAK,MAAM;AAAA,IAClB,KAAK,iBAAiB;AACrB,iBAAW,QAAQ,OAAoC;AACtD,cAAM,CAAC,WAAW,IAAI,IAAI,eAAe,IAAI;AAC7C,YAAI,SAAS,YAAY,WAAW;AACnC,gBAAM,YAAY,MAAM,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,mBAAmB,SAAS,CAAC,IAAI,MAAM,oBAAoB,CAAC;AAAA,QAC5G,OAAO;AACN,gBAAM,aAAa,MAAM,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,MAAM,oBAAoB,CAAC;AAAA,QAC5E;AAAA,MACD;AACA;AAAA,IACD;AAAA,IACA,KAAK,mBAAmB;AACvB,YAAM,UAAU;AAChB,UAAI,QAAQ,wBAAwB;AACnC,cAAM,aAAa,MAAM,GAAG,IAAI,GAAG,KAAK,IAAI,uBAAuB,CAAC,GAAG,oBAAoB,CAAC;AAC5F;AAAA,MACD;AACA,iBAAW,QAAQ,QAAQ,OAAO;AACjC,cAAM,CAAC,WAAW,IAAI,IAAI,eAAe,IAAI;AAC7C,cAAM,OAAO,0BAA0B,IAAI;AAC3C,YAAI,SAAS,YAAY,WAAW;AAEnC,gBAAM,YAAY,MAAM,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,EAAE,OAAO,WAAW,GAAG,KAAK,GAAG,oBAAoB,CAAC;AAAA,QACpG,OAAO;AACN,gBAAM,aAAa,MAAM,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,MAAM,oBAAoB,CAAC;AAAA,QAC5E;AAAA,MACD;AACA;AAAA,IACD;AAAA,IACA,KAAK,eAAe;AACnB,YAAM,OAAO,QAAQ,kBAAkB,EAAE,eAAe,MAAM,IAAK;AACnE,YAAM,QAAQ,SAAS,WAAW,eAAe;AACjD,YAAM,MAAM,MAAM,GAAG,IAAI,GAAG,KAAK,IAAI,IAAI,MAAM,oBAAoB,CAAC;AACpE;AAAA,IACD;AAAA,IACA,KAAK,YAAY;AAChB,YAAM,QAAQ;AACd,YAAM;AAAA,QACL;AAAA,QACA,GAAG,IAAI,GAAG,KAAK,IAAI;AAAA,QACnB;AAAA,UACC,iBAAiB,MAAM,IAAI,CAAC,SAAS;AACpC,kBAAM,CAAC,WAAW,IAAI,IAAI,eAAe,IAAI;AAC7C,mBAAO,EAAE,OAAO,SAAS,WAAW,YAAY,MAAM,GAAG,KAAK;AAAA,UAC/D,CAAC;AAAA,QACF;AAAA,QACA,oBAAoB;AAAA,MACrB;AACA;AAAA,IACD;AAAA,EACD;AACD;AAGA,eAAe,YAAY,SAAiB,KAAkD;AAC7F,MAAI;AACH,UAAM,IAAI;AACV,WAAO,EAAE,SAAS,IAAI,KAAK;AAAA,EAC5B,SAAS,KAAK;AACb,WAAO,EAAE,SAAS,IAAI,OAAO,OAAO,cAAc,GAAG,EAAE;AAAA,EACxD;AACD;AAQA,eAAsB,gBAAgB,MAA8B,WAAkE;AACrI,QAAM,QAAQ,MAAM,YAAY,IAAI;AACpC,QAAM,UAA2B,CAAC;AAElC,MAAI,UAAU,SAAS,QAAW;AACjC,YAAQ,KAAK,MAAM,YAAY,QAAQ,MAAM,aAAa,MAAM,OAAO,UAAU,IAAc,CAAC,CAAC;AAAA,EAClG;AAEA,aAAW,WAAW,wBAAU;AAC/B,UAAM,QAAQ,UAAU,QAAQ,GAAG;AACnC,QAAI,UAAU,OAAW;AACzB,YAAQ,KAAK,MAAM,YAAY,QAAQ,KAAK,MAAM,aAAa,MAAM,OAAO,QAAQ,KAAK,OAAO,QAAQ,CAAC,CAAC;AAAA,EAC3G;AAEA,SAAO,EAAE,QAAQ,OAAO,SAAS,OAAO,QAAQ,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE;AACpE;AAiBA,eAAsB,gBACrB,MACA,OACA,WACA,aAC+B;AAC/B,QAAM,UAA2B,CAAC;AAElC,MAAI,gBAAgB,QAAW;AAC9B,UAAM,cAAU,0BAAW,WAAW;AACtC,QAAI,CAAC,SAAS;AACb,YAAM,UAAU,uBAAS,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,IAAI;AACpD,YAAM,IAAI,SAAS,oBAAoB,WAAW,eAAe,OAAO,IAAI,SAAS,eAAe;AAAA,IACrG;AACA,UAAM,QAAQ,UAAU,WAAW;AACnC,QAAI,UAAU,QAAW;AACxB,YAAM,IAAI,SAAS,YAAY,WAAW,mCAAmC,SAAS,eAAe;AAAA,IACtG;AACA,YAAQ,KAAK,MAAM,YAAY,QAAQ,KAAK,MAAM,aAAa,MAAM,OAAO,QAAQ,KAAK,OAAO,QAAQ,CAAC,CAAC;AAC1G,WAAO,EAAE,QAAQ,OAAO,SAAS,OAAO,QAAQ,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE;AAAA,EACpE;AAEA,MAAI,UAAU,SAAS,QAAW;AACjC,YAAQ,KAAK,MAAM,YAAY,QAAQ,MAAM,aAAa,MAAM,OAAO,UAAU,IAAc,CAAC,CAAC;AAAA,EAClG;AAEA,aAAW,WAAW,wBAAU;AAC/B,UAAM,QAAQ,UAAU,QAAQ,GAAG;AACnC,QAAI,UAAU,OAAW;AACzB,YAAQ,KAAK,MAAM,YAAY,QAAQ,KAAK,MAAM,aAAa,MAAM,OAAO,QAAQ,KAAK,OAAO,QAAQ,CAAC,CAAC;AAAA,EAC3G;AAEA,SAAO,EAAE,QAAQ,OAAO,SAAS,OAAO,QAAQ,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE;AACpE;;;ADtPA,eAAsB,iBAAiB,KAAsB,UAAiC;AAC7F,QAAM,QAAQ,eAAe,UAAU,EAAE,WAAW,IAAI,UAAU,CAAC;AACnE,QAAM,aAAS,iCAAkB,KAAK;AACtC,MAAI,OAAO,SAAS,GAAG;AACtB,QAAI,IAAI,WAAW,QAAQ;AAC1B,gBAAU,EAAE,OAAO,OAAO,OAAO,CAAC;AAAA,IACnC,OAAO;AACN,iBAAW,SAAS,QAAQ;AAC3B,gBAAQ,OAAO,MAAM,GAAG,oBAAoB,MAAM,IAAI,CAAC,KAAK,oBAAoB,MAAM,OAAO,CAAC;AAAA,CAAI;AAAA,MACnG;AAAA,IACD;AACA,UAAM,IAAI,SAAS,GAAG,OAAO,MAAM,mFAA8E,SAAS,eAAe;AAAA,EAC1I;AAEA,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,SAAS,MAAM,gBAAgB,MAAM,KAAK;AAChD,2BAAyB,KAAK,MAAM;AAEpC,MAAI,CAAC,OAAO,OAAO;AAClB,UAAM,SAAS,OAAO,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO;AACvE,UAAM,IAAI;AAAA,MACT,UAAU,OAAO,MAAM,oBAAoB,OAAO,MAAM,gCAAgC,OAAO,KAAK,IAAI,CAAC,yCACjE,OAAO,MAAM;AAAA,MACrD,SAAS;AAAA,IACV;AAAA,EACD;AACD;AAOA,SAAS,yBAAyB,KAAsB,QAAmC;AAC1F,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,QAAQ,OAAO,QAAQ,SAAS,OAAO,SAAS,QAAQ,OAAO,MAAM,CAAC;AAClF;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,WAAW,oBAAoB,OAAO,UAAU,EAAE,CAAC;AAAA,CAAI;AAC5E;AAAA,IACC,OAAO;AAAA,IACP;AAAA,MACC,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,GAAG;AAAA,MAC3D,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAO,EAAE,KAAK,WAAM,SAAK;AAAA,MACrD,EAAE,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,UAAU,GAAG;AAAA,IAC9D;AAAA,IACA,IAAI;AAAA,EACL;AACD;AAEO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,QAAQ,EAChB,YAAY,yFAAyF,EACrG,eAAe,iBAAiB,mDAAmD,EACnF,OAAO,OAAO,OAAoB,YAAqB;AACvD,UAAM,iBAAiB,eAAe,OAAO,GAAG,MAAM,IAAI;AAAA,EAC3D,CAAC;AACH;;;AE/DA,SAASC,cAAa,OAAuB;AAC5C,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,EACxE;AACA,SAAO;AACR;AASA,eAAsB,iBACrB,KACA,OACA,UACA,OACgB;AAChB,QAAM,UAAUA,cAAa,KAAK;AAClC,QAAM,QAAQ,eAAe,UAAU,EAAE,WAAW,IAAI,UAAU,CAAC;AACnE,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,SAAS,MAAM,gBAAgB,MAAM,SAAS,OAAO,MAAM,OAAO;AACxE,EAAAC,0BAAyB,KAAK,MAAM;AAEpC,MAAI,CAAC,OAAO,OAAO;AAClB,UAAM,SAAS,OAAO,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO;AACvE,UAAM,IAAI;AAAA,MACT,UAAU,OAAO,MAAM,iCAA4B,OAAO,MAAM,uBAAuB,OAAO,KAAK,IAAI,CAAC;AAAA,MAExG,SAAS;AAAA,IACV;AAAA,EACD;AACD;AAMA,SAASA,0BAAyB,KAAsB,QAAmC;AAC1F,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,QAAQ,OAAO,QAAQ,SAAS,OAAO,SAAS,QAAQ,OAAO,MAAM,CAAC;AAClF;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,WAAW,oBAAoB,OAAO,UAAU,EAAE,CAAC;AAAA,CAAI;AAC5E;AAAA,IACC,OAAO;AAAA,IACP;AAAA,MACC,EAAE,QAAQ,WAAW,OAAO,CAAC,MAAM,EAAE,SAAS,UAAU,GAAG;AAAA,MAC3D,EAAE,QAAQ,UAAU,OAAO,CAAC,MAAO,EAAE,KAAK,WAAM,SAAK;AAAA,MACrD,EAAE,QAAQ,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,UAAU,GAAG;AAAA,IAC9D;AAAA,IACA,IAAI;AAAA,EACL;AACD;AAEO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,iBAAiB,EACzB,YAAY,uEAAuE,EACnF,eAAe,iBAAiB,+EAA+E,EAC/G,OAAO,oBAAoB,2DAA2D,EACtF,OAAO,OAAO,OAAe,OAAoB,YAAqB;AACtE,UAAM,iBAAiB,eAAe,OAAO,GAAG,OAAO,MAAM,MAAM,KAAK;AAAA,EACzE,CAAC;AACH;;;AChFA,IAAAC,uBAA2B;AAC3B,IAAAC,gBAAmC;AAKnC,IAAAC,gBAAgE;AAOhE,SAASC,cAAa,OAAuB;AAC5C,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,EACxE;AACA,SAAO;AACR;AAEA,SAAS,gBAAgB,KAAsB,aAAqB,UAAwB;AAC3F,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,QAAQ,SAAS,CAAC;AAC9B;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,iBAAiB,oBAAoB,WAAW,CAAC,uBAAkB,oBAAoB,QAAQ,CAAC;AAAA,CAAI;AAC1H;AAcA,eAAsB,eAAe,KAAsB,OAAe,MAAyC;AAClH,QAAM,UAAUA,cAAa,KAAK;AAClC,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAE1G,QAAM,EAAE,KAAK,IAAI,MAAM,aAAa,MAAM,GAAG,mCAAqB,IAAI,mBAAmB,OAAO,CAAC,cAAc,CAAC,GAAG,EAAE,oBAAgB,iCAAW,EAAE,CAAC;AACnJ,QAAM,EAAE,QAAQ,SAAS,QAAI,kCAAuC,IAAI;AAExE,kBAAgB,KAAK,SAAS,QAAQ;AAEtC,MAAI,SAAS,OAAW;AAExB,MAAI;AACH,UAAM,YAAY,MAAM,GAAG,mCAAqB,IAAI,mBAAmB,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAiC,EAAE,oBAAgB,iCAAW,EAAE,CAAC;AAAA,EAClK,SAAS,KAAK;AACb,UAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,UAAM,IAAI;AAAA,MACT,UAAU,OAAO,kBAAkB,QAAQ,yBAAyB,IAAI,aAAa,MAAM;AAAA,MAE3F,SAAS;AAAA,IACV;AAAA,EACD;AACD;AAEO,SAAS,4BAA4B,QAAuB;AAClE,SACE,QAAQ,eAAe,EACvB,YAAY,sDAAsD,EAClE,OAAO,iBAAiB,uCAAuC,EAC/D,OAAO,OAAO,OAAe,OAAkB,YAAqB;AACpE,UAAM,eAAe,eAAe,OAAO,GAAG,OAAO,MAAM,IAAI;AAAA,EAChE,CAAC;AACH;;;ACxEA,IAAAC,uBAA2B;AAC3B,IAAAC,gBAAmC;AAKnC,IAAAC,gBAA6E;AAG7E,SAASC,cAAa,OAAuB;AAC5C,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,EACxE;AACA,SAAO;AACR;AAWA,eAAsB,4BAA4B,KAAsB,OAAe,QAAgD;AACtI,QAAM,UAAUA,cAAa,KAAK;AAClC,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,eAAe,WAAW;AAEhC,QAAM,EAAE,KAAK,IAAI,MAAM;AAAA,IACtB;AAAA,IACA,GAAG,mCAAqB,IAAI,mBAAmB,OAAO,CAAC;AAAA,IACvD,EAAE,eAAe,aAAa;AAAA,IAC9B,EAAE,oBAAgB,iCAAW,EAAE;AAAA,EAChC;AACA,QAAM,aAAS,kCAA8C,IAAI;AAEjE,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,QAAQ,SAAS,cAAc,OAAO,aAAa,CAAC;AAChE;AAAA,EACD;AACA,QAAM,OAAO,WAAW,YAAY,cAAc;AAClD,UAAQ,OAAO,MAAM,GAAG,IAAI,YAAY,oBAAoB,OAAO,CAAC;AAAA,CAAI;AACzE;AAEO,SAAS,+BAA+B,QAAuB;AACrE,SACE,QAAQ,kBAAkB,EAC1B,YAAY,iDAAiD,EAC7D,OAAO,OAAO,OAAe,QAA+B,YAAqB;AACjF,UAAM,4BAA4B,eAAe,OAAO,GAAG,OAAO,SAAS;AAAA,EAC5E,CAAC;AACH;AAEO,SAAS,iCAAiC,QAAuB;AACvE,SACE,QAAQ,oBAAoB,EAC5B,YAAY,6CAA6C,EACzD,OAAO,OAAO,OAAe,QAA+B,YAAqB;AACjF,UAAM,4BAA4B,eAAe,OAAO,GAAG,OAAO,WAAW;AAAA,EAC9E,CAAC;AACH;;;AC/DA,IAAAC,uBAA2B;AAK3B,IAAAC,gBAAsC;AAOtC,SAASC,cAAa,OAAuB;AAC5C,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,4BAA4B,SAAS,eAAe;AAAA,EACxE;AACA,SAAO;AACR;AAWA,eAAsB,iBAAiB,KAAsB,OAAe,SAAiC;AAC5G,MAAI,CAAC,SAAS;AACb,UAAM,IAAI,SAAS,oEAAoE,SAAS,eAAe;AAAA,EAChH;AACA,QAAM,UAAUA,cAAa,KAAK;AAClC,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAE1G,QAAM,eAAe,MAAM,GAAG,mCAAqB,IAAI,mBAAmB,OAAO,CAAC,IAAI,EAAE,oBAAgB,iCAAW,EAAE,CAAC;AAEtH,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,EAAE,QAAQ,SAAS,SAAS,KAAK,CAAC;AAC5C;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,mBAAmB,oBAAoB,OAAO,CAAC;AAAA,CAAI;AACzE;AAEO,SAAS,8BAA8B,QAAuB;AACpE,SACE,QAAQ,iBAAiB,EACzB,YAAY,mDAAmD,EAC/D,OAAO,aAAa,+CAA+C,EACnE,OAAO,OAAO,OAAe,OAAoB,YAAqB;AACtE,UAAM,iBAAiB,eAAe,OAAO,GAAG,OAAO,MAAM,YAAY,IAAI;AAAA,EAC9E,CAAC;AACH;;;ACvCO,SAAS,+BAA+B,QAAuB;AACrE,QAAM,UAAU,OAAO,QAAQ,SAAS,EAAE,YAAY,8BAA8B;AACpF,gCAA8B,OAAO;AACrC,kCAAgC,OAAO;AACvC,kCAAgC,OAAO;AACvC,8BAA4B,OAAO;AACnC,8BAA4B,OAAO;AACnC,gCAA8B,OAAO;AACrC,gCAA8B,OAAO;AACrC,gCAA8B,OAAO;AACrC,8BAA4B,OAAO;AACnC,iCAA+B,OAAO;AACtC,mCAAiC,OAAO;AACxC,gCAA8B,OAAO;AACtC;;;AC5BA,IAAAC,uBAA2B;AAC3B,IAAAC,mBAA6B;AAK7B,IAAAC,gBAA2C;;;ACP3C,IAAAC,mBAA6B;AAG7B,IAAM,cAAc;AAQpB,SAAS,mBAA2B;AACnC,aAAO,+BAAa,GAAG,MAAM;AAC9B;AAEA,SAAS,oBAAoB,KAAqB;AACjD,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,QAAQ,SAAS,GAAG;AACvB,UAAM,IAAI,SAAS,6FAA+D,SAAS,eAAe;AAAA,EAC3G;AACA,MAAI,QAAQ,SAAS,aAAa;AACjC,UAAM,IAAI,SAAS,+BAA+B,WAAW,uEAA0B,SAAS,eAAe;AAAA,EAChH;AACA,SAAO;AACR;AAGO,SAAS,eAAe,MAAoF;AAClH,QAAM,SAAS,KAAK,YAAY;AAChC,QAAM,UAAU,KAAK,gBAAgB;AACrC,MAAI,UAAU,SAAS;AACtB,UAAM,IAAI,SAAS,oEAA0D,SAAS,eAAe;AAAA,EACtG;AACA,MAAI,CAAC,UAAU,CAAC,SAAS;AACxB,UAAM,IAAI,SAAS,gFAAkD,SAAS,eAAe;AAAA,EAC9F;AACA,MAAI,OAAQ,QAAO,oBAAoB,KAAK,OAAiB;AAC7D,QAAM,OAAO,KAAK;AAClB,QAAM,MAAM,SAAS,OAAO,KAAK,aAAa,kBAAkB,QAAI,+BAAa,MAAM,MAAM;AAC7F,SAAO,oBAAoB,GAAG;AAC/B;AAGO,SAAS,mBAAmB,OAAkC;AACpE,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,GAAG;AAC9C,UAAM,IAAI,SAAS,4DAA4D,SAAS,eAAe;AAAA,EACxG;AACA,MAAI,MAAM,SAAS,IAAI;AACtB,UAAM,IAAI,SAAS,+BAA+B,MAAM,MAAM,wCAAe,SAAS,eAAe;AAAA,EACtG;AACA,SAAO,MAAM,IAAI,CAAC,IAAI,MAAM;AAC3B,UAAM,IAAI;AACV,UAAM,QAAQ,OAAO,GAAG,eAAe,WAAW,EAAE,WAAW,KAAK,IAAI;AACxE,UAAM,WAAW,OAAO,GAAG,kBAAkB,WAAW,EAAE,cAAc,KAAK,IAAI;AACjF,QAAI,CAAC,SAAS,CAAC,UAAU;AACxB,YAAM,IAAI,SAAS,eAAe,CAAC,2CAA2C,SAAS,eAAe;AAAA,IACvG;AACA,UAAM,UAAU,oBAAoB,OAAO,GAAG,wBAAwB,WAAW,EAAE,sBAAsB,EAAE;AAC3G,WAAO,EAAE,YAAY,OAAO,eAAe,UAAU,qBAAqB,QAAQ;AAAA,EACnF,CAAC;AACF;;;ADzCA,SAAS,kBAAkB,KAAqB;AAC/C,MAAI,eAAe,uBAAuB;AACzC,UAAM,OAAQ,IAAI,QAAQ;AAC1B,UAAM,OAAO,MAAM;AACnB,QAAI,IAAI,WAAW,OAAO,SAAS,wBAAwB;AAC1D,YAAM,IAAI,SAAS,2CAA2C,MAAM,iBAAiB,SAAS,IAAI,SAAS,iBAAiB;AAAA,IAC7H;AACA,QAAI,IAAI,WAAW,OAAO,SAAS,qBAAqB;AACvD,YAAM,IAAI,SAAS,mDAAmD,SAAS,iBAAiB;AAAA,IACjG;AACA,QAAI,IAAI,WAAW,OAAO,SAAS,6BAA6B;AAC/D,YAAM,IAAI;AAAA,QACT,oCAAoC,MAAM,IAAI,IAAI,MAAM,KAAK,iBAAiB,MAAM,aAAa,UAAU;AAAA,QAC3G,SAAS;AAAA,MACV;AAAA,IACD;AAAA,EACD;AACA,QAAM;AACP;AAEA,eAAsB,iBACrB,KACA,MACgB;AAChB,MAAI,CAAC,KAAK,SAAS;AAClB,UAAM,IAAI;AAAA,MACT;AAAA,MACA,SAAS;AAAA,IACV;AAAA,EACD;AACA,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,QAAM,OAAO,EAAE,YAAY,KAAK,UAAU,eAAe,KAAK,aAAa,qBAAqB,KAAK,QAAQ;AAC7G,MAAI;AACJ,MAAI;AACH,aAAS,MAAM,aAAa,MAAM,0CAA4B,MAAM,EAAE,oBAAgB,iCAAW,EAAE,CAAC;AAAA,EACrG,SAAS,KAAK;AACb,sBAAkB,GAAG;AAAA,EACtB;AACA,QAAM,OAAQ,OAAO,MAA6C,QAAQ,CAAC;AAC3E,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,IAAI;AACd;AAAA,EACD;AACA,UAAQ,OAAO,MAAM,yBAAyB,oBAAoB,OAAO,KAAK,sBAAsB,EAAE,CAAC,CAAC;AAAA,CAAI;AAC5G,QAAM,IAAI,KAAK;AACf,MAAI,EAAG,SAAQ,OAAO,MAAM,gBAAgB,EAAE,IAAI,IAAI,EAAE,KAAK,UAAU,EAAE,SAAS;AAAA,CAAc;AACjG;AAEO,SAAS,6BAA6B,QAAuB;AACnE,SACE,QAAQ,oBAAoB,EAC5B,YAAY,kEAAkE,EAC9E,OAAO,qBAAqB,2CAA2C,EACvE,OAAO,oBAAoB,oCAAoC,EAC/D,OAAO,yBAAyB,sDAAsD,EACtF,OAAO,iBAAiB,oDAAoD,EAC5E,OAAO,aAAa,oCAAoC,EACxD,OAAO,OAAO,UAA8B,OAAmB,YAAqB;AACpF,UAAM,MAAM,eAAe,OAAO;AAClC,QAAI,MAAM,MAAM;AACf,YAAM,sBAAsB,KAAK,EAAE,MAAM,MAAM,MAAM,SAAS,MAAM,YAAY,KAAK,CAAC;AACtF;AAAA,IACD;AACA,QAAI,CAAC,YAAY,CAAC,MAAM,QAAQ;AAC/B,YAAM,IAAI,SAAS,mDAAmD,SAAS,eAAe;AAAA,IAC/F;AACA,UAAM,UAAU,eAAe,EAAE,SAAS,MAAM,SAAS,aAAa,MAAM,YAAY,CAAC;AACzF,UAAM,iBAAiB,KAAK;AAAA,MAC3B,UAAU;AAAA,MACV,aAAa,MAAM;AAAA,MACnB;AAAA,MACA,SAAS,MAAM,YAAY;AAAA,IAC5B,CAAC;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,sBACrB,KACA,MACgB;AAChB,MAAI,CAAC,KAAK,SAAS;AAClB,UAAM,IAAI;AAAA,MACT;AAAA,MACA,SAAS;AAAA,IACV;AAAA,EACD;AACA,MAAI;AACJ,MAAI;AACH,aAAS,KAAK,UAAM,+BAAa,KAAK,MAAM,MAAM,CAAC;AAAA,EACpD,QAAQ;AACP,UAAM,IAAI,SAAS,iCAAiC,KAAK,IAAI,IAAI,SAAS,eAAe;AAAA,EAC1F;AACA,QAAM,QAA0B,mBAAoB,QAAuC,YAAY;AACvG,QAAM,OAA+B,EAAE,SAAS,IAAI,SAAS,QAAQ,IAAI,QAAQ,WAAW,IAAI,UAAU;AAC1G,MAAI;AACJ,MAAI;AACH,aAAS,MAAM,aAAa,MAAM,GAAG,wCAA0B,UAAU,EAAE,cAAc,MAAM,GAAG,EAAE,oBAAgB,iCAAW,EAAE,CAAC;AAAA,EACnI,SAAS,KAAK;AACb,sBAAkB,GAAG;AAAA,EACtB;AACA,QAAM,OAAQ,OAAO,MAA6C,QAAQ,CAAC;AAC3E,MAAI,IAAI,WAAW,QAAQ;AAC1B,cAAU,IAAI;AACd;AAAA,EACD;AACA,QAAM,YAAa,KAAK,aAA2B,CAAC;AACpD,QAAM,SAAU,KAAK,UAA0E,CAAC;AAChG,UAAQ,OAAO,MAAM,kBAAkB,UAAU,MAAM,eAAe,OAAO,MAAM;AAAA,CAAW;AAC9F,aAAW,KAAK,QAAQ;AACvB,YAAQ,OAAO,MAAM,MAAM,EAAE,KAAK,IAAI,oBAAoB,EAAE,UAAU,CAAC,KAAK,oBAAoB,EAAE,UAAU,CAAC;AAAA,CAAI;AAAA,EAClH;AACD;;;AE9HO,SAAS,wBAAwBC,UAAwB;AAC/D,QAAM,WAAWA,SAAQ,QAAQ,UAAU,EAAE,YAAY,wDAAwD;AACjH,iCAA+B,QAAQ;AACvC,+BAA6B,QAAQ;AACtC;;;A1EKA,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QACE,KAAK,OAAO,EACZ,YAAY,8EAAyE,EACrF,QAAQ,SAAiB,iBAAiB,wBAAwB,EAClE,OAAO,mBAAmB,+DAA+D,EACzF,OAAO,eAAe,uBAAuB,EAC7C,OAAO,kBAAkB,6BAA6B,EACtD,OAAO,cAAc,sBAAsB,EAC3C,OAAO,kBAAkB,gCAAgC,CAAC,MAAM,OAAO,CAAC,CAAC;AAE3E,oBAAoB,OAAO;AAC3B,sBAAsB,OAAO;AAC7B,sBAAsB,OAAO;AAC7B,0BAA0B,OAAO;AACjC,qBAAqB,OAAO;AAC5B,sBAAsB,OAAO;AAC7B,sBAAsB,OAAO;AAC7B,wBAAwB,OAAO;AAC/B,wBAAwB,OAAO;AAE/B,QAAQ,aAAa;AAErB,QACE,WAAW,QAAQ,IAAI,EACvB,KAAK,MAAM,QAAQ,KAAK,SAAS,OAAO,CAAC,EACzC,MAAM,CAAC,QAAiB,oBAAoB,GAAG,CAAC;AAElD,SAAS,oBAAoB,KAAqB;AACjD,QAAM,QAAQ,eAAe,KAAK;AAGlC,MAAI,OAAO,OAAO,QAAQ,YAAY,UAAU,KAAK;AACpD,UAAM,eAAe;AACrB,QAAI,aAAa,SAAS,6BAA6B,aAAa,SAAS,qBAAqB;AACjG,cAAQ,KAAK,SAAS,OAAO;AAAA,IAC9B;AAGA,QAAI,aAAa,QAAS,YAAW,aAAa,SAAS,KAAK;AAChE,YAAQ,KAAK,SAAS,eAAe;AAAA,EACtC;AAEA,MAAI,WAAW,GAAG,GAAG;AACpB,eAAW,IAAI,SAAS,KAAK;AAC7B,YAAQ,KAAK,IAAI,QAAQ;AAAA,EAC1B;AAEA,UAAI,0BAAa,GAAG,GAAG;AACtB,eAAW,IAAI,SAAS,KAAK;AAC7B,YAAQ,KAAK,iBAAiB,GAAG,CAAC;AAAA,EACnC;AAEA,QAAM,kBAAkB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACvE,aAAW,iBAAiB,KAAK;AACjC,UAAQ,KAAK,SAAS,oBAAoB;AAC3C;","names":["pc","Table","import_node_fs","import_core","envPaths","paths","import_core","import_core","import_node_fs","import_picocolors","paths","s","pc","program","import_node_fs","program","import_node_fs","import_core","import_node_fs","import_node_path","import_env_paths","paths","envPaths","import_core","import_core","program","import_core","enterpriseGet","enterprisePost","enterprisePatch","enterpriseDelete","enterpriseGet","import_core","enterpriseGet","import_core","formatDate","enterpriseGet","import_core","enterpriseGet","import_core","enterprisePost","import_node_crypto","import_core","enterprisePatch","import_node_crypto","import_core","enterprisePatch","enterpriseDelete","enterprisePost","import_node_crypto","import_core","enterprisePost","import_core","formatDate","enterpriseGet","import_core","enterprisePost","import_core","DETAIL_FIELDS","renderDetailLines","enterpriseGet","import_node_crypto","import_core","enterpriseGet","enterprisePatch","import_node_crypto","import_node_fs","import_node_path","import_core","enterprisePost","import_core","MINIMAL_LIST_FIELDS","formatDate","enterpriseGet","import_core","enterpriseGet","import_node_crypto","import_core","enterprisePost","import_node_crypto","import_core","enterprisePost","import_core","STATUS_MAP","MINIMAL_LIST_FIELDS","mapStatusFlag","formatStatus","formatCount","enterpriseGet","import_node_crypto","import_core","enterprisePatch","import_node_crypto","import_core","enterprisePatch","import_core","enterpriseGet","program","performLogin","osHostname","program","import_core","import_core","warnIfRateLimitLow","import_core","program","program","import_core","import_core","formatDate","import_core","program","import_core","import_core","import_node_fs","import_core","import_core","import_core","MINIMAL_LIST_FIELDS","formatDate","import_core","import_core","import_node_fs","import_node_path","import_core","import_node_crypto","import_core","import_core","requireEncId","printOrchestrationResult","import_node_crypto","import_core","import_core","requireEncId","import_node_crypto","import_core","import_core","requireEncId","import_node_crypto","import_core","requireEncId","import_node_crypto","import_node_fs","import_core","import_node_fs","program"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/errors.ts","../src/lib/output.ts","../src/commands/jobs/search.ts","../src/lib/config-store.ts","../src/lib/channel.ts","../src/lib/global-opts.ts","../src/lib/path-utils.ts","../src/commands/jobs/view.ts","../src/lib/io-helpers.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/credentials-store.ts","../src/lib/oauth.ts","../src/lib/enterprise-client.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/block-replace-shared.ts","../src/commands/enterprise/company/types.ts","../src/commands/enterprise/company/logo.ts","../src/commands/enterprise/company/replace.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","../src/commands/auth/login.ts","../src/lib/browser-open.ts","../src/commands/auth/whoami.ts","../src/lib/personal-client.ts","../src/commands/auth/logout.ts","../src/commands/sessions/list.ts","../src/commands/sessions/revoke.ts","../src/commands/sessions/index.ts","../src/commands/personal/resumes/schema.ts","../src/commands/personal/resumes/validate.ts","../src/commands/personal/resumes/template.ts","../src/commands/personal/resumes/list.ts","../src/commands/personal/resumes/view.ts","../src/commands/personal/resumes/export.ts","../src/commands/personal/resumes/create.ts","../src/lib/resume-orchestrator.ts","../src/commands/personal/resumes/update.ts","../src/commands/personal/resumes/copy.ts","../src/commands/personal/resumes/publish.ts","../src/commands/personal/resumes/delete.ts","../src/commands/personal/resumes/index.ts","../src/commands/personal/apply/index.ts","../src/commands/personal/apply/message-input.ts","../src/commands/personal/index.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { isCliError, ExitCode, isWportError, exitCodeForError } 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';\nimport { registerLoginCommand } from './commands/auth/login';\nimport { registerWhoamiCommand } from './commands/auth/whoami';\nimport { registerLogoutCommand } from './commands/auth/logout';\nimport { registerSessionsCommand } from './commands/sessions';\nimport { registerPersonalCommand } from './commands/personal';\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);\nregisterLoginCommand(program);\nregisterWhoamiCommand(program);\nregisterLogoutCommand(program);\nregisterSessionsCommand(program);\nregisterPersonalCommand(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\tif (isWportError(err)) {\n\t\tprintError(err.message, color);\n\t\tprocess.exit(exitCodeForError(err));\n\t}\n\n\tconst fallbackMessage = err instanceof Error ? err.message : String(err);\n\tprintError(fallbackMessage, color);\n\tprocess.exit(ExitCode.ServerOrNetworkError);\n}\n","import {\n\tWportError, WportHttpError, WportNetworkError, WportInvalidArgumentError, isWportError,\n} from '@wport/core';\n\nexport 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 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\n/** core 拋的 WportError(無 exitCode)→ CLI exit code。CliError 自己帶 exitCode,不走這裡。 */\nexport function exitCodeForError(err: unknown): ExitCodeValue {\n\tif (err instanceof WportInvalidArgumentError) return ExitCode.InvalidArgument;\n\tif (err instanceof WportHttpError) {\n\t\treturn err.status >= 400 && err.status < 500 ? ExitCode.ServerClientError : ExitCode.ServerOrNetworkError;\n\t}\n\tif (err instanceof WportNetworkError) return ExitCode.ServerOrNetworkError;\n\treturn ExitCode.ServerOrNetworkError;\n}\n\n// core 類 alias:保留舊名的 instanceof 身分(resume-orchestrator/apply/company 等的 catch 靠這個對得上 core 拋的錯)。\nexport { WportError, WportHttpError, WportNetworkError, WportInvalidArgumentError, isWportError };\nexport const ServerClientHttpError = WportHttpError;\nexport const NetworkError = WportNetworkError;\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, buildUserAgent, createApiClient, throwForHttpStatus } from '@wport/core';\nimport { CLI_SOURCE, 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 '@wport/core';\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\tuserAgent: buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\t\tsource: CLI_SOURCE,\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 {\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","// build 時由 tsup `define` 把 __CHANNEL__ 烙成字面值(見 tsup.config.ts / src/global.d.ts)。\n// vitest 不走 tsup,未注入時 typeof 為 'undefined' → 視為 prod(fail-safe)。\nconst CHANNEL_BASE_URL: Record<string, string> = {\n\tprod: 'https://api.wport.me',\n\tdev: 'https://developers.wport.me/v2',\n};\n\n/** 讀取 build 時烙進的 channel;未定義(未走 tsup / 未注入)時回 'prod'。 */\nexport function currentChannel(): string {\n\treturn typeof __CHANNEL__ === 'string' ? __CHANNEL__ : 'prod';\n}\n\n/** channel → 預設 API base;未知 channel 一律 fallback prod。 */\nexport function channelBaseUrl(channel: string): string {\n\treturn CHANNEL_BASE_URL[channel] ?? CHANNEL_BASE_URL.prod;\n}\n\n/** 非 prod channel 回一行提示(給 stderr 用,含換行);prod 回 null。 */\nexport function channelBanner(): string | null {\n\tconst channel = currentChannel();\n\tif (channel === 'prod') return null;\n\treturn `[${channel}] targeting the dev backend\\n`;\n}\n","import type { Command } from 'commander';\nimport { ALLOWED_LOCALES, loadConfig, type Locale, type CliConfig } from './config-store';\nimport { channelBaseUrl, currentChannel } from './channel';\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.)\n// 預設 base 由 build 時烙進的 channel 決定(prod → api.wport.me、dev → developers.wport.me/v2)。\n// 覆寫優先序不變:--api > WPORT_API_BASE > 此預設。base 仍不落地 config(SSRF 面不變)。\nconst DEFAULT_BASE_URL = channelBaseUrl(currentChannel());\nexport const API_BASE_ENV_VAR = 'WPORT_API_BASE';\n// pm_48 交付 B(BR-039):CLI 每個請求標 `X-Source: cli`(三端 audit 鏈;後端交付 A 已上線收錄)。\n// 單一常數收斂,四個出口(jobs createApiClient / enterprise transport / personal / oauth)共用。\nexport const CLI_SOURCE = 'cli';\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","import type { Command } from 'commander';\nimport {\n\tbuildUserAgent,\n\tcreateApiClient,\n\tthrowForHttpStatus,\n\tunwrapDataResponse,\n\ttype ApiClient,\n} from '@wport/core';\nimport { CLI_SOURCE, 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 '@wport/core';\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\tuserAgent: buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\t\tsource: CLI_SOURCE,\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 { 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 { 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 { buildUserAgent, createApiClient } from '@wport/core';\nimport { CLI_SOURCE, resolveContext, type ResolvedContext } from '../lib/global-opts';\nimport { getConfigPath } from '../lib/config-store';\nimport { ExitCode } from '../lib/errors';\nimport { loadPersonalCredentials } from '../lib/credentials-store';\nimport { requestDeviceCode, type OauthRequestOptions } from '../lib/oauth';\nimport { currentChannel } from '../lib/channel';\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\n/**\n * Capability boundary an agent MUST know before scripting talent workflows (issue #106 P1-4):\n * an Enterprise API Key can only read/act on the company's *applied* talent pool. The visit\n * tab is not available yet (`talents list --tab visit` → 400), and there is no active candidate\n * search over an API Key — the public `/api/search-talent` endpoint requires an interactive JWT\n * (company mode) session and returns 401 for an API Key. Surfacing this in `doctor` stops an\n * agent from discovering these boundaries the hard way via a bare 40x.\n */\nexport const ENTERPRISE_TALENT_BOUNDARY_NOTES = [\n\t'`enterprise talents` covers your APPLIED pool only (list / view / respond).',\n\t'The visit tab is not available yet: `talents list --tab visit` returns 400.',\n\t'There is NO active candidate search over an API Key. `/api/search-talent` needs an',\n\t'interactive JWT (company-mode) session and returns 401 for an API Key — by design,',\n\t'not a setup error. Do not script talent-sourcing against an Enterprise API Key.',\n];\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, personal login state, 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 reachable = await runDoctor(ctx);\n\t\t\tif (!reachable) process.exit(ExitCode.ServerOrNetworkError);\n\t\t});\n}\n\n/**\n * doctor 主體邏輯抽成獨立函式(沿 performLogin/performWhoami 慣例),讓輸出內容可被測試斷言,\n * 不必透過 commander 整條 parse 流程重跑一次。回傳值 = 既有「公開 API 是否可達」判斷,\n * 決定 registerDoctorCommand 是否以非 0 exit code 結束——AS 連通性檢查只是輔助診斷資訊,\n * 不影響這個回傳值(見 probeAuthServer 註解)。\n */\nexport async function runDoctor(ctx: ResolvedContext): Promise<boolean> {\n\tconst line = (s = '') => process.stdout.write(s + '\\n');\n\n\tline(`wport-cli ${__CLI_VERSION__}`);\n\tline(` bundled schema fingerprint: ${__SCHEMA_HASH__}`);\n\tline('');\n\n\tline('Resolved configuration:');\n\tline(` API base URL: ${ctx.baseUrl}`);\n\tline(` channel: ${currentChannel()}`);\n\tline(` locale: ${ctx.locale}`);\n\tline(` timeout: ${ctx.timeoutMs}ms`);\n\tconst cfgPath = getConfigPath();\n\tline(` config file: ${cfgPath}${existsSync(cfgPath) ? '' : ' (not present)'}`);\n\tline('');\n\n\tline('Personal account (OAuth):');\n\tfor (const l of describePersonalLoginLines()) line(` ${l}`);\n\tline('');\n\n\tline('Server connectivity:');\n\tconst reachable = await probeServer(ctx, line);\n\tawait probeAuthServer(ctx, line);\n\tline('');\n\n\tline('Known server behaviours (read this before scripting an agent):');\n\tline(\n\t\t` • Sort is server-controlled. These query params are silently ignored: ${SILENT_IGNORED_PARAMS.join(', ')}.`\n\t);\n\tline(' • jobs search sorts by publish date, or by relevance when --keyword is set.');\n\tline(' • jobs view --batch caps parallelism (default 5, max 20) to stay friendly to the API.');\n\tline('');\n\n\tline('Enterprise talent scope (Enterprise API Key):');\n\tfor (const note of ENTERPRISE_TALENT_BOUNDARY_NOTES) line(` • ${note}`);\n\tline('');\n\n\tline('Schema drift:');\n\tline(' The fingerprint above identifies the OpenAPI contract this CLI was built against.');\n\tline(' Automated drift detection needs a server-side schema-version endpoint, which is');\n\tline(' not available yet — for now, compare fingerprints manually after a server release. [TODO]');\n\n\treturn reachable;\n}\n\n/**\n * 個人線登入態:只讀 credentials 檔、不打任何請求、不觸發 refresh —— doctor 是唯讀診斷工具,\n * 若順手在這裡 refresh 或呼叫 API,會讓「跑一次 doctor」產生副作用(消耗 access token 續期,\n * 甚至因為過期/被撤銷的 refresh token 觸發整批撤銷),這不是診斷工具該做的事。\n *\n * 整段包 try/catch:credentials.json 壞損時 `loadPersonalCredentials` 會丟 CliError\n * (ConfigCorrupt)——doctor 存在的目的正是要診斷「壞掉的東西」,不能自己被這個錯誤拖著\n * 整個指令一起死在這個區塊,讓 Server connectivity 等後續檢查都沒機會跑。\n */\nfunction describePersonalLoginLines(): string[] {\n\ttry {\n\t\tconst creds = loadPersonalCredentials();\n\t\tif (!creds) return ['not logged in (run `wport login` to sign in)'];\n\n\t\tconst expiresAtMs = Date.parse(creds.expires_at);\n\t\tif (Number.isNaN(expiresAtMs)) {\n\t\t\treturn [\n\t\t\t\t'logged in',\n\t\t\t\t`access token expiry is unreadable (\"${creds.expires_at}\") — try \\`wport login --force\\` to re-authenticate`,\n\t\t\t];\n\t\t}\n\t\tconst expired = expiresAtMs <= Date.now();\n\t\treturn [\n\t\t\t'logged in',\n\t\t\texpired\n\t\t\t\t? `access token expired at ${creds.expires_at} (refreshes automatically on next request)`\n\t\t\t\t: `access token valid until ${creds.expires_at}`,\n\t\t];\n\t} catch (err) {\n\t\treturn [`personal credentials file appears corrupted: ${err instanceof Error ? err.message : String(err)}`];\n\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({\n\t\t\tbaseUrl: ctx.baseUrl,\n\t\t\tlocale: ctx.locale,\n\t\t\ttimeoutMs: ctx.timeoutMs,\n\t\t\tuserAgent: buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\tsource: CLI_SOURCE,\n\t\t});\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\n/** device_name 送給 AS 讓伺服器端的 session/device 列表看得出這是診斷探測,不是真的登入嘗試。 */\nconst DOCTOR_PROBE_DEVICE_NAME = 'wport doctor (connectivity check)';\n\n/**\n * AS(OAuth Authorization Server)連通性:`POST /oauth/device/code` 帶 device_name,走一次完整\n * 的 device-code 請求形狀。這是唯一不需要 Authorization、對「只是在跑 doctor」這件事本身無害的\n * unauthenticated 端點——產生的 device code 沒人會去 poll,`expires_in` 秒後在伺服器端自然失效,\n * 不留殘留狀態。失敗只印一行、不拋出、不中斷其餘檢查,也不影響 runDoctor 的回傳值——AS 連通性\n * 目前只是輔助診斷資訊,doctor 的整體 exit code 仍只看既有的公開 API 探測(probeServer)。\n */\nasync function probeAuthServer(\n\tctx: { baseUrl: string; locale: string; timeoutMs: number },\n\tline: (s?: string) => void\n): Promise<void> {\n\tconst opts: OauthRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\ttry {\n\t\tawait requestDeviceCode(opts, DOCTOR_PROBE_DEVICE_NAME);\n\t\tline(' ✓ auth server reachable (device code endpoint)');\n\t} catch (err) {\n\t\tline(` ! auth server check failed: ${err instanceof Error ? err.message : String(err)}`);\n\t}\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { describePersonalLoginLines };\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';\nimport { currentChannel } from './channel';\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\n/** 個人線 OAuth session(device flow 取得)。與 enterprise Credentials 各自獨立成一區。 */\nexport interface PersonalCredentials {\n\taccess_token: string; // wpt_ 前綴\n\trefresh_token: string;\n\texpires_at: string; // ISO;access token 到期時刻(寫入時以 now + expires_in 算出)\n\tdisplay_name: string;\n\temail: string;\n\tsession_created_at: string; // ISO\n}\n\nexport type KeySource = 'flag' | 'env' | 'file';\n\nexport interface ResolvedKey {\n\tkey: string;\n\tsource: KeySource;\n}\n\n/**\n * 檔案格式 v2(內部,不 export):{ version: 2, enterprise?: Credentials, personal?: PersonalCredentials }。\n * 舊版 flat 格式(api_key 在頂層)讀入時視為 { version: 2, enterprise: <flat> }。\n * 任何 save 系列/delete 系列函式一律以此讀-改-寫,保留另一區不受影響。\n */\ninterface RawCredentialsFile {\n\tenterprise?: unknown;\n\tpersonal?: unknown;\n}\n\nconst paths = envPaths('wport', { suffix: '' });\n\n// 憑證檔名依 build channel 分檔:dev 寫 credentials-dev.json,避免切版時 token 互蓋 / 誤用打錯後端。\n// fail-safe 與 channelBaseUrl 一致:僅 'dev' 走分檔,prod 與任何未知 channel 一律 credentials.json\n// —— 既有使用者零遷移,且不把任意 channel 字串內插進檔名路徑。\nexport function getCredentialsPath(): string {\n\tconst fileName = currentChannel() === 'dev' ? 'credentials-dev.json' : 'credentials.json';\n\treturn join(paths.config, fileName);\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\n/**\n * 讀取原始檔案,拆成 enterprise/personal 兩個「未驗證」子物件。\n *\n * strict=true(給 load* 用):JSON 壞掉或結構不對 → 丟 ConfigCorrupt(既有行為,讀路徑要能明確告警)。\n * strict=false(給 save 系列/delete 系列的讀-改-寫合併用):JSON 壞掉 → 當成空檔處理,讓\n * save 可以放心覆寫重建(保住錯誤訊息裡「Run \"wport enterprise login\" to recreate it」的復原承諾);\n * 另一區的內容在此只搬運、不解析驗證,避免「存 A 卻因為 B 區壞掉而炸開」的跨區污染。\n */\nfunction readRawFile(strict: boolean): RawCredentialsFile {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return {};\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(path, 'utf8'));\n\t} catch (err) {\n\t\tif (!strict) return {};\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') {\n\t\tif (!strict) return {};\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\t// 舊版 flat 格式偵測:v2 檔案不會在頂層放 api_key,看到就代表整份物件本身即 enterprise 區。\n\tif (typeof raw.api_key === 'string') {\n\t\treturn { enterprise: raw };\n\t}\n\treturn { enterprise: raw.enterprise, personal: raw.personal };\n}\n\nfunction parseEnterpriseCredentials(raw: unknown): Credentials {\n\tconst path = getCredentialsPath();\n\tif (!raw || typeof raw !== 'object' || typeof (raw 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 r = raw as Record<string, unknown>;\n\tconst apiKey = r.api_key as string;\n\treturn {\n\t\tapi_key: apiKey,\n\t\tcompany_name: typeof r.company_name === 'string' ? r.company_name : '',\n\t\tkey_last4: typeof r.key_last4 === 'string' ? r.key_last4 : apiKey.slice(-4),\n\t\tsaved_at: typeof r.saved_at === 'string' ? r.saved_at : '',\n\t};\n}\n\n// personal 區只硬驗 access_token/refresh_token/expires_at(鏡像 enterprise 只硬驗 api_key 的慣例);\n// 其餘顯示用欄位 soft-default 成空字串,不因為缺 email/display_name 就把整份憑證判死。\nfunction parsePersonalCredentials(raw: unknown): PersonalCredentials {\n\tconst path = getCredentialsPath();\n\tif (\n\t\t!raw ||\n\t\ttypeof raw !== 'object' ||\n\t\ttypeof (raw as Record<string, unknown>).access_token !== 'string' ||\n\t\ttypeof (raw as Record<string, unknown>).refresh_token !== 'string' ||\n\t\ttypeof (raw as Record<string, unknown>).expires_at !== 'string'\n\t) {\n\t\tthrow new CliError(\n\t\t\t`Personal credentials at ${path} are malformed or incomplete. Run \"wport login\" to sign in again.`,\n\t\t\tExitCode.ConfigCorrupt\n\t\t);\n\t}\n\tconst r = raw as Record<string, unknown>;\n\treturn {\n\t\taccess_token: r.access_token as string,\n\t\trefresh_token: r.refresh_token as string,\n\t\texpires_at: r.expires_at as string,\n\t\tdisplay_name: typeof r.display_name === 'string' ? r.display_name : '',\n\t\temail: typeof r.email === 'string' ? r.email : '',\n\t\tsession_created_at: typeof r.session_created_at === 'string' ? r.session_created_at : '',\n\t};\n}\n\n// Atomic write 模式照抄 config-store.saveConfig:tmpfile(0o600) → rename。\n// credentials 比 config 更敏感,所以分檔(config 可能被使用者貼進 issue 除錯)。\nfunction writeFileAtomic0600(path: string, content: string): void {\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, content);\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\n/** file 中未指定的區塊 = undefined = 不寫入該欄位(v2 殼本身仍會寫出)。 */\nfunction writeCredentialsFile(file: RawCredentialsFile): void {\n\tconst body: Record<string, unknown> = { version: 2 };\n\tif (file.enterprise !== undefined) body.enterprise = file.enterprise;\n\tif (file.personal !== undefined) body.personal = file.personal;\n\twriteFileAtomic0600(getCredentialsPath(), JSON.stringify(body, null, 2) + '\\n');\n}\n\nexport function loadCredentials(): Credentials | null {\n\tconst { enterprise } = readRawFile(true);\n\tif (enterprise === undefined) return null;\n\treturn parseEnterpriseCredentials(enterprise);\n}\n\nexport function saveCredentials(creds: Credentials): void {\n\tconst { personal } = readRawFile(false);\n\twriteCredentialsFile({ enterprise: creds, personal });\n}\n\nexport function deleteCredentials(): boolean {\n\tconst path = getCredentialsPath();\n\tif (!existsSync(path)) return false;\n\tconst { enterprise, personal } = readRawFile(false);\n\t// 兩區都解析不出來(JSON 壞損、或合法 JSON 但無可辨識的 enterprise/personal 結構):\n\t// 維持 Task1 不變量「既有 export 維持不變(enterprise 命令零改動)」——舊版 deleteCredentials\n\t// 從不解析內容,檔案存在就整個刪除並回 true。這裡沒有 personal 資料要保留,直接比照辦理,\n\t// 否則 `wport enterprise logout` 對壞掉的檔案會靜默回 false 且留下無法復原的殘骸。\n\tif (enterprise === undefined && personal === undefined) {\n\t\tunlinkSync(path);\n\t\treturn true;\n\t}\n\tif (enterprise === undefined) return false;\n\t// 兩區都空了才整檔刪除;personal 還在就只清 enterprise 區、保留 personal(見既有\n\t// 'deleteCredentials returns true when file existed, false otherwise' 測試:單區時檔案要整個消失)。\n\tif (personal === undefined) {\n\t\tunlinkSync(path);\n\t} else {\n\t\twriteCredentialsFile({ personal });\n\t}\n\treturn true;\n}\n\nexport function loadPersonalCredentials(): PersonalCredentials | null {\n\tconst { personal } = readRawFile(true);\n\tif (personal === undefined) return null;\n\treturn parsePersonalCredentials(personal);\n}\n\nexport function savePersonalCredentials(p: PersonalCredentials): void {\n\tconst { enterprise } = readRawFile(false);\n\twriteCredentialsFile({ enterprise, personal: p });\n}\n\nexport function deletePersonalCredentials(): boolean {\n\tconst { enterprise, personal } = readRawFile(false);\n\tif (personal === undefined) return false;\n\twriteCredentialsFile({ enterprise });\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 { buildUserAgent, fetchWithTimeout, throwForHttpStatus } from '@wport/core';\nimport { CliError, ExitCode } from './errors';\nimport { OAUTH_BASE, type DeviceCodeResponse, type TokenResponse } from '@wport/core';\nimport { CLI_SOURCE } from './global-opts';\n\nexport interface OauthRequestOptions {\n\tbaseUrl: string; // resolveContext 產出,已去尾斜線\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\nconst EXPIRED_MESSAGE = 'The device code expired before authorization completed. Run `wport login` to try again.';\n\n/**\n * OAuth 端點手寫 wrapper。與 enterprise-client / personal-client 不同:這幾支端點不帶\n * Authorization(device_code / refresh_token 本身就是憑證,回應也是 RFC 標準 raw JSON,\n * 無 W101 慣用的 DataResponse wrapper)。刻意「不」在這裡就地丟錯 —— 呼叫端各自的狀態機\n * (尤其 pollForToken 的四種 device-grant 錯誤碼)需要親眼看到 status/body 才能分派,\n * 提早丟錯會讓 caller 沒機會做狀態機判斷。\n */\nasync function oauthPost(\n\topts: OauthRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>\n): Promise<{ status: number; body: unknown }> {\n\tconst url = new URL(`${opts.baseUrl}${path}`);\n\tconst request = new Request(url, {\n\t\tmethod: 'POST',\n\t\theaders: {\n\t\t\t'Accept-Language': opts.locale,\n\t\t\t'User-Agent': buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t\t'X-Source': CLI_SOURCE,\n\t\t\tAccept: 'application/json',\n\t\t\t'Content-Type': 'application/json',\n\t\t},\n\t\tbody: JSON.stringify(body),\n\t});\n\tconst res = await fetchWithTimeout(request, opts.timeoutMs);\n\tconst respBody: unknown = await res.json().catch(() => null);\n\treturn { status: res.status, body: respBody };\n}\n\n/** OAuth 錯誤 body 恆為 RFC 標準 `{ error: string }`(design doc §3.5);缺席回 null 讓呼叫端 fallback。 */\nfunction oauthErrorCode(body: unknown): string | null {\n\tif (body && typeof body === 'object' && typeof (body as Record<string, unknown>).error === 'string') {\n\t\treturn (body as Record<string, unknown>).error as string;\n\t}\n\treturn null;\n}\n\n/** `POST /oauth/device/code`。deviceName 為 null/空字串時不帶該欄位(後端視為未命名裝置)。 */\nexport async function requestDeviceCode(opts: OauthRequestOptions, deviceName: string | null): Promise<DeviceCodeResponse> {\n\tconst body: Record<string, unknown> = {};\n\tif (deviceName) body.device_name = deviceName;\n\tconst { status, body: respBody } = await oauthPost(opts, `${OAUTH_BASE}/device/code`, body);\n\tif (status < 200 || status >= 300) throwForHttpStatus(status, respBody);\n\treturn respBody as DeviceCodeResponse;\n}\n\n/**\n * RFC 8628 輪詢狀態機。interval 起始值 = `device.interval`(秒);`slow_down` 時 interval +5 秒\n * 續輪,往後沿用新值;`authorization_pending` 用當前 interval 續輪;`access_denied` /\n * `expired_token` 直接 CliError exit 3。第一次嘗試不 sleep —— 使用者剛看到 user_code / 瀏覽器\n * 才要開,讓第一次輪詢立即發生沒有意義上的壞處,只有「這次沒過」才需要等待再試。\n *\n * 額外的本地 deadline(`device.expires_in` 秒)是防禦性判斷:伺服器理論上會在裝置碼過期後\n * 回 `expired_token`,但避免因網路延遲、殘留連線等情況導致無窮迴圈,仍在本機端加一道保險,\n * 觸發時視同 expired 處理。sleep 可注入,測試斷言呼叫次數與間隔(預設值才是正式環境的真等待)。\n */\nexport async function pollForToken(\n\topts: OauthRequestOptions,\n\tdevice: DeviceCodeResponse,\n\tsleep: (ms: number) => Promise<void> = (ms) => new Promise((resolve) => setTimeout(resolve, ms))\n): Promise<TokenResponse> {\n\tlet intervalSec = device.interval;\n\tconst deadline = Date.now() + device.expires_in * 1000;\n\n\tfor (;;) {\n\t\tconst { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {\n\t\t\tgrant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n\t\t\tdevice_code: device.device_code,\n\t\t});\n\t\tif (status >= 200 && status < 300) return body as TokenResponse;\n\n\t\tconst code = oauthErrorCode(body);\n\t\tif (code === 'slow_down') {\n\t\t\tintervalSec += 5;\n\t\t} else if (code === 'access_denied') {\n\t\t\tthrow new CliError('Authorization was denied on the device.', ExitCode.ServerClientError);\n\t\t} else if (code === 'expired_token') {\n\t\t\tthrow new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);\n\t\t} else if (code !== 'authorization_pending') {\n\t\t\t// 未知碼(理論上不該出現在 device grant 的 invalid_grant 等):無法辨識就走通用 HTTP 錯誤路徑,\n\t\t\t// 400 經 throwForHttpStatus 一樣落在 exit 3,行為與明確分派的四碼一致。\n\t\t\tthrowForHttpStatus(status, body);\n\t\t}\n\n\t\tif (Date.now() >= deadline) throw new CliError(EXPIRED_MESSAGE, ExitCode.ServerClientError);\n\t\tawait sleep(intervalSec * 1000);\n\t}\n}\n\n/** `POST /oauth/token`(refresh grant)。`invalid_grant` 涵蓋單純過期與 reuse-detection 全撤兩種情境,訊息統一導向重新登入。 */\nexport async function refreshAccessToken(opts: OauthRequestOptions, refreshToken: string): Promise<TokenResponse> {\n\tconst { status, body } = await oauthPost(opts, `${OAUTH_BASE}/token`, {\n\t\tgrant_type: 'refresh_token',\n\t\trefresh_token: refreshToken,\n\t});\n\tif (status >= 200 && status < 300) return body as TokenResponse;\n\tif (oauthErrorCode(body) === 'invalid_grant') {\n\t\tthrow new CliError('Your session is no longer valid. Run `wport login` to sign in again.', ExitCode.ServerClientError);\n\t}\n\tthrowForHttpStatus(status, body);\n}\n\n/** `POST /oauth/revoke`(RFC 7009)。契約上一律 200 空物件、無 revoke oracle,這裡仍對非 2xx 做防禦性處理。 */\nexport async function revokeRefreshToken(opts: OauthRequestOptions, refreshToken: string): Promise<void> {\n\tconst { status, body } = await oauthPost(opts, `${OAUTH_BASE}/revoke`, { token: refreshToken });\n\tif (status < 200 || status >= 300) throwForHttpStatus(status, body);\n}\n","import { buildUserAgent, WportHttpError } from '@wport/core';\nimport * as transport from '@wport/core';\nimport type {\n\tEnterpriseRequestOptions as TransportRequestOptions,\n\tEnterpriseWriteExtra,\n\tEnterpriseGetResult,\n\tEnterprisePostResult,\n} from '@wport/core';\nimport { CLI_SOURCE } from './global-opts';\nimport { printWarn } from './output';\n\nexport type { EnterpriseWriteExtra, EnterpriseGetResult, EnterprisePostResult };\n\n/**\n * CLI 呼叫端仍用這個 4 欄位形狀(不含 `userAgent`)——wrapper 自己補 `userAgent`\n * 再轉呼叫 transport,命令端組 opts 的地方不用改。\n */\nexport type EnterpriseRequestOptions = Omit<TransportRequestOptions, 'userAgent'>;\n\nfunction withUserAgent(opts: EnterpriseRequestOptions): TransportRequestOptions {\n\treturn { ...opts, userAgent: buildUserAgent('wport-cli', __CLI_VERSION__), source: CLI_SOURCE };\n}\n\n/**\n * spec §5 + pm_41 §3:401/403 附情境提示;400 帶 publish gate missing_fields 時列出缺欄。\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 *\n * 回傳**新的 `WportHttpError`**(保留原 `status`/`body`,非 `CliError`)——沿用 `WportHttpError`\n * 而非裸 `WportError` 是刻意的:頂層 `exitCodeForError` 靠 `instanceof WportHttpError` + `status`\n * 判斷 exit code(401/403/400 仍要落在 3),裸 `WportError` 會落到預設的 4,改變對外 exit code 契約。\n */\n/**\n * spec §3.1.2/§3.2.4 的五個新 error_code(block 四區塊整段取代 + descriptions 影片連結)。\n * 判斷優先序固定為 `error_code` > HTTP status —— 這五碼在 body 頂層 additive 出現時,\n * 不管 HTTP status 是什麼(含既有 401/403/400 已有專屬提示的 status),一律優先用這裡的\n * 訊息;`message` 隨 locale 變動不可靠,只認 `error_code`。\n */\ninterface EnterpriseErrorCodeBody {\n\terror_code: 'block_max_exceeded' | 'unsupported_video_provider' | 'block_item_forbidden' | 'profile_revision_conflict' | 'profile_revision_required';\n\tblock?: string;\n\tlimit?: number;\n\tcurrent_revision?: string;\n}\n\nfunction extractErrorCodeBody(body: unknown): EnterpriseErrorCodeBody | undefined {\n\tif (!body || typeof body !== 'object') return undefined;\n\tconst b = body as Record<string, unknown>;\n\tconst code = b.error_code;\n\tif (\n\t\tcode !== 'block_max_exceeded' &&\n\t\tcode !== 'unsupported_video_provider' &&\n\t\tcode !== 'block_item_forbidden' &&\n\t\tcode !== 'profile_revision_conflict' &&\n\t\tcode !== 'profile_revision_required'\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\terror_code: code,\n\t\tblock: typeof b.block === 'string' ? b.block : undefined,\n\t\tlimit: typeof b.limit === 'number' ? b.limit : undefined,\n\t\tcurrent_revision: typeof b.current_revision === 'string' ? b.current_revision : undefined,\n\t};\n}\n\n/** spec §3.2.4:五個 error_code 各自的裝飾訊息。回傳 `undefined` 代表這不是本次案子關心的 code。 */\nfunction decorateByErrorCode(base: string, err: WportHttpError, info: EnterpriseErrorCodeBody): WportHttpError {\n\tswitch (info.error_code) {\n\t\tcase 'block_max_exceeded':\n\t\t\treturn new WportHttpError(\n\t\t\t\t`${base} — Block \"${info.block ?? 'unknown'}\" exceeds its item limit` +\n\t\t\t\t\t(info.limit !== undefined ? ` (max ${info.limit})` : '') +\n\t\t\t\t\t'. Run `wport enterprise company view --output json` to see the current items before replacing.',\n\t\t\t\terr.status,\n\t\t\t\terr.body\n\t\t\t);\n\t\tcase 'unsupported_video_provider':\n\t\t\treturn new WportHttpError(\n\t\t\t\t`${base} — Unsupported video URL. Accepted forms: ` +\n\t\t\t\t\t'https://www.youtube.com/watch?v=<id>, https://youtu.be/<id>, ' +\n\t\t\t\t\t'https://vimeo.com/<id>, https://player.vimeo.com/video/<id>.',\n\t\t\t\terr.status,\n\t\t\t\terr.body\n\t\t\t);\n\t\tcase 'block_item_forbidden':\n\t\t\treturn new WportHttpError(\n\t\t\t\t`${base} — This enc_id (block \"${info.block ?? 'unknown'}\") does not belong to your company or no longer exists. ` +\n\t\t\t\t\t'Run `wport enterprise company view` to get the latest enc_id list.',\n\t\t\t\terr.status,\n\t\t\t\terr.body\n\t\t\t);\n\t\tcase 'profile_revision_conflict':\n\t\t\treturn new WportHttpError(\n\t\t\t\t`${base} — Block \"${info.block ?? 'unknown'}\" changed since you last read it` +\n\t\t\t\t\t(info.current_revision ? ` (current revision: ${info.current_revision})` : '') +\n\t\t\t\t\t'. This CLI will not auto-retry: re-run `wport enterprise company view` to see the latest state, then confirm again.',\n\t\t\t\terr.status,\n\t\t\t\terr.body\n\t\t\t);\n\t\tcase 'profile_revision_required':\n\t\t\treturn new WportHttpError(\n\t\t\t\t`${base} — This request needs an If-Match revision (block \"${info.block ?? 'unknown'}\") and none was recognized. ` +\n\t\t\t\t\t'This usually means the CLI is out of date — upgrade it, or re-run `wport enterprise company view` to fetch a fresh revision before retrying.',\n\t\t\t\terr.status,\n\t\t\t\terr.body\n\t\t\t);\n\t}\n}\n\nfunction decorateEnterpriseError(err: unknown): unknown {\n\tif (!(err instanceof WportHttpError)) return err;\n\tconst base = err.message;\n\tconst errorCodeInfo = extractErrorCodeBody(err.body);\n\tif (errorCodeInfo) return decorateByErrorCode(base, err, errorCodeInfo);\n\tif (err.status === 401) {\n\t\treturn new WportHttpError(\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\terr.status,\n\t\t\terr.body\n\t\t);\n\t}\n\tif (err.status === 403) {\n\t\treturn new WportHttpError(\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\terr.status,\n\t\t\terr.body\n\t\t);\n\t}\n\tif (err.status === 400) {\n\t\t// issue #106 P2:publish gate 失敗回 `data.missing_fields`,但翻譯後的 message 只有中文一句話,\n\t\t// 不含欄位清單 → agent 不知道缺哪些。把結構化 missing_fields 明列進錯誤訊息(消費端可 parse)。\n\t\tconst missingFields = extractMissingFields(err.body);\n\t\tif (missingFields.length > 0) {\n\t\t\treturn new WportHttpError(\n\t\t\t\t`${base} — Missing required fields: ${missingFields.join(', ')}. ` +\n\t\t\t\t\t'Fill them via `wport enterprise jobs update <enc_id> ...` (or the web console), then publish.',\n\t\t\t\terr.status,\n\t\t\t\terr.body\n\t\t\t);\n\t\t}\n\t}\n\treturn err;\n}\n\n/**\n * 從錯誤 body 取 publish gate 的 `data.missing_fields`(string[]);缺席 / 型別不符回 []。\n * 契約來源:`validate-job-completeness-for-publish.rule.ts` 丟\n * `BadRequestException({ path, data: { missing_fields } })`,全域 i18n filter 保留 `data`。\n */\nfunction extractMissingFields(body: unknown): string[] {\n\tif (!body || typeof body !== 'object') return [];\n\tconst data = (body as { data?: unknown }).data;\n\tif (!data || typeof data !== 'object') return [];\n\tconst fields = (data as { missing_fields?: unknown }).missing_fields;\n\tif (!Array.isArray(fields)) return [];\n\treturn fields.filter((f): f is string => typeof f === 'string');\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\nasync function wrap<T extends EnterpriseGetResult | EnterprisePostResult>(p: Promise<T>): Promise<T> {\n\tlet res: T;\n\ttry {\n\t\tres = await p;\n\t} catch (err) {\n\t\tthrow decorateEnterpriseError(err);\n\t}\n\twarnIfRateLimitLow(res.headers);\n\treturn res;\n}\n\nexport function enterpriseGet(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tquery?: Record<string, string | number | undefined>\n): Promise<EnterpriseGetResult> {\n\treturn wrap(transport.enterpriseGet(withUserAgent(opts), path, query));\n}\n\nexport function enterprisePost(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn wrap(transport.enterprisePost(withUserAgent(opts), path, body, extra));\n}\n\nexport function enterprisePatch(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn wrap(transport.enterprisePatch(withUserAgent(opts), path, body, extra));\n}\n\n/** 企業 API 寫入 PUT(company profile 四區塊整段取代)。四支端點皆強制 Idempotency-Key + If-Match。 */\nexport function enterprisePut(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn wrap(transport.enterprisePut(withUserAgent(opts), path, body, extra));\n}\n\nexport function enterpriseDelete(\n\topts: EnterpriseRequestOptions,\n\tpath: string,\n\textra?: EnterpriseWriteExtra\n): Promise<EnterprisePostResult> {\n\treturn wrap(transport.enterpriseDelete(withUserAgent(opts), path, extra));\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';\nimport { channelBanner } from '../../lib/channel';\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\tconst banner = channelBanner();\n\tif (banner) process.stderr.write(banner);\n\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 '@wport/core';\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 * quota 語意(PRD DR-8,talent#233 起 prod 生效):月配額計「所有認證通過的 API 呼叫」\n * (1 call = 1 count;whoami/usage 與 key rotate 豁免)——顯示需點明計量範圍,\n * 使用者才不會把豁免端點的「不動」誤讀成計數壞掉(issue #41/#43 的教訓)。\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)} calls 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(\n\t\t\t'Monthly quota meters every authenticated API call; whoami/usage and key rotate are exempt.',\n\t\t\tctx.color\n\t\t) + '\\n'\n\t);\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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\t// issue #106 P1-3:legacy 來源子表為空時,後端於 copy 回應帶 incomplete_fields。人類模式明示,\n\t// 讓使用者知道 publish 前要補哪些欄(json 模式已含此欄,不重複印)。defensive 讀,欄位缺席即略過。\n\tconst incomplete = extractStringArray(result.incomplete_fields);\n\tif (incomplete.length > 0) {\n\t\tprocess.stdout.write(` ⚠ Incomplete for publish — fill before \\`jobs publish\\`: ${incomplete.join(', ')}\\n`);\n\t}\n}\n\n/** defensive:把 unknown 收成 string[](非陣列 / 非字串元素一律濾掉)。 */\nfunction extractStringArray(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter((v): v is string => typeof v === 'string');\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` 已於 0.7.0 全面撤銷(產品決議:職缺狀態只有上下架,無「關閉」態;\n * GUI 不存在此概念,CLI 關閉的職缺會在企業後台卡成無法操作的殭屍列)。\n * 保留隱藏 stub:老腳本/agent 打到時給明確遷移訊息並 exit 2、**不發請求**。\n */\nexport const JOBS_CLOSE_REMOVED_MESSAGE =\n\t'`jobs close` was removed in @wport/cli 0.7.0 — the \"closed\" job state has been revoked product-wide ' +\n\t'(jobs are only published/unpublished). Use `jobs unpublish <enc_id>` to take a job off the board, ' +\n\t'or `jobs delete <enc_id> --confirm` to remove it.';\n\nexport function registerEnterpriseJobsClose(parent: Command): void {\n\tparent\n\t\t.command('close [enc_id]', { hidden: true })\n\t\t.description('(removed in 0.7.0)')\n\t\t.allowUnknownOption(true)\n\t\t.action(async () => {\n\t\t\tthrow new CliError(JOBS_CLOSE_REMOVED_MESSAGE, ExitCode.InvalidArgument);\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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\n/**\n * 五個可整段取代區塊摘要(spec §3.2.1):table 模式只印筆數,`block_revisions` 是 opaque\n * If-Match token,只在 `--output json` 顯示(讀→改→寫的前提),table 模式一律不印。\n * 固定順序,欄位缺席(GET 契約漂移或本來就沒有)當 0 筆處理,不 throw。\n */\nconst BLOCK_SUMMARY_FIELDS: ReadonlyArray<string> = ['pain_points', 'awards', 'milestones', 'qa_items', 'video_links'];\n\nfunction renderBlockSummaryLines(company: EnterpriseCompany): string[] {\n\tconst width = Math.max(...BLOCK_SUMMARY_FIELDS.map((f) => f.length)) + 3;\n\treturn BLOCK_SUMMARY_FIELDS.map((field) => {\n\t\tconst value = company[field];\n\t\tconst count = Array.isArray(value) ? value.length : 0;\n\t\treturn `${(field + ':').padEnd(width)}${count}`;\n\t});\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\tconst lines = [...renderDetailLines(company), '', ...renderBlockSummaryLines(company)];\n\tprocess.stdout.write(lines.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, renderBlockSummaryLines };\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport {\n\tunwrapDataResponse,\n\ttype EnterpriseCompanyBasicUpdateDto,\n\ttype EnterpriseCompanyDescriptionsUpdateDto,\n} from '@wport/core';\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, printWarn } from '../../../lib/output';\nimport type { WriteCtx } from '../jobs/write-shared';\nimport {\n\tassertValidSubmittedItems,\n\tenforceDeletionConfirmation,\n\tpreviewDeletions,\n\tsummarizeItemsDiff,\n\ttype DeletableCandidate,\n\ttype ExistingBlockItem,\n} from './block-replace-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/**\n\t * 待送 `PATCH /company/basic` 的整段合併結果;使用者輸入完全沒碰到任何 basic 欄位時為 `null`。\n\t * fresh-eyes F-1:型別改用 codegen `EnterpriseCompanyBasicUpdateDto`(`BASIC_FIELDS` 與該 DTO\n\t * 的 properties 集合完全對齊,見 `types.ts` BASIC_FIELDS 註解)取代原本的 `Record<string, unknown>`。\n\t */\n\tbasic: EnterpriseCompanyBasicUpdateDto | null;\n\t/**\n\t * 待送 `PATCH /company/descriptions` 的部分欄位;使用者輸入完全沒碰到任何 description 欄位時為 `null`。\n\t * fresh-eyes F-1:型別改用 codegen `EnterpriseCompanyDescriptionsUpdateDto`。\n\t */\n\tdescriptions: EnterpriseCompanyDescriptionsUpdateDto | null;\n\t/**\n\t * 輸入含 `video_links` 時,來自同一次 preflight GET 的 `block_revisions.video_links`(`If-Match`\n\t * 用)與現況 `video_links` 陣列(刪除預覽用);輸入不含 `video_links` 時皆為 `undefined`\n\t * (呼叫端據此判斷本次 PATCH /company/descriptions 是否該帶 `If-Match`,見 spec §3.2.2)。\n\t */\n\tvideoLinksRevision?: string;\n\tcurrentVideoLinks?: ExistingBlockItem[];\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: EnterpriseCompanyBasicUpdateDto | null = null;\n\tif (inputHasBasicField) {\n\t\t// 動態逐欄位組(BASIC_FIELDS 是 ReadonlyArray<string>),先用 Record<string, unknown> 建構,\n\t\t// 最後整段 cast 成 codegen DTO——欄位集合已對齊(見 CompanyUpdatePayloads.basic 註解),\n\t\t// 這裡的 cast 不是繞過驗證,missingRequired 檢查已涵蓋 DTO 的 required 欄位。\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 as EnterpriseCompanyBasicUpdateDto;\n\t}\n\n\tlet descriptions: EnterpriseCompanyDescriptionsUpdateDto | null = null;\n\tif (inputHasDescriptionField) {\n\t\tconst partial: Record<string, unknown> = {};\n\t\tfor (const field of DESCRIPTION_FIELDS) {\n\t\t\tif (field in input) partial[field] = input[field];\n\t\t}\n\t\tdescriptions = partial as EnterpriseCompanyDescriptionsUpdateDto;\n\t}\n\n\t// `video_links` 不能只靠上面的機械帶過(spec §3.2.2):後端只在 body 帶 `video_links` 時才要求\n\t// `If-Match`,且刪除需走 block-replace-shared 的確認流程。這裡從同一次 preflight GET(`current`,\n\t// 上面已經拿到)取出 revision 與現況陣列,交給 runCompanyUpdate 決定 If-Match/刪除預覽,\n\t// 不另外多打一次 GET。\n\tlet videoLinksRevision: string | undefined;\n\tlet currentVideoLinks: ExistingBlockItem[] | undefined;\n\tif ('video_links' in input) {\n\t\tcurrentVideoLinks = (current.video_links as ExistingBlockItem[] | undefined) ?? [];\n\t\tvideoLinksRevision = (current.block_revisions as Record<string, string> | undefined)?.video_links;\n\t\tif (!videoLinksRevision) {\n\t\t\t// 防禦性檢查:同 CONTRACT_REQUIRED_GET_FIELDS 慣例——Card A 承諾 block_revisions 一定帶\n\t\t\t// video_links key,一旦缺席代表後端契約漂移,不是使用者輸入錯誤。\n\t\t\tthrow new CliError(\n\t\t\t\t'Backend contract appears broken: GET /company response is missing block_revisions.video_links',\n\t\t\t\tExitCode.ServerOrNetworkError\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { basic, descriptions, videoLinksRevision, currentVideoLinks };\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 & { color?: boolean },\n\tapiKey: string,\n\tsource: string,\n\tidempotencyFlags: UpdateIdempotencyFlags,\n\toptions: ReadJsonInputOptions = {},\n\tconfirm = false\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 needsVideoLinks = 'video_links' in input;\n\tif (needsVideoLinks) {\n\t\t// fresh-eyes F-2:`video_links` 走的是同一套 block-replace-shared previewDeletions,\n\t\t// 需要同款「陣列+元素皆為 non-null 物件」的本地驗證,且必須在 buildCompanyUpdatePayloads\n\t\t// 的 GET 之前就擋下——這個判斷不需要現況資料,沒必要為了注定失敗的輸入多打一次網路。\n\t\tassertValidSubmittedItems(input.video_links, 'video_links');\n\t}\n\tconst { basicKey, descriptionsKey } = resolveUpdateIdempotencyKeys(needsBasic, needsDescriptions, idempotencyFlags);\n\n\tconst payloads = await buildCompanyUpdatePayloads(input, ctx, apiKey);\n\n\tif (needsVideoLinks) {\n\t\t// spec §3.2.2:任何一筆既有影片被移除(含非空漏一筆與 `video_links: []`)都要走\n\t\t// block-replace-shared 同一套刪除預覽 + confirm 規則,不另寫一份判斷。\n\t\tconst submitted = (payloads.descriptions?.video_links as DeletableCandidate[] | undefined) ?? [];\n\t\tconst deletions = previewDeletions(payloads.currentVideoLinks ?? [], submitted, (item) => String(item.url ?? ''));\n\t\tenforceDeletionConfirmation('video_links', deletions, confirm, ctx.color ?? false);\n\t}\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{\n\t\t\t\tidempotencyKey: descriptionsKey,\n\t\t\t\t// 只有輸入帶 video_links 時才附 If-Match——後端只在這種情況要求它(spec §3.2.2);\n\t\t\t\t// 沒帶 video_links 時絕不能傳這個 key(用條件式 spread,不是傳 `undefined`)。\n\t\t\t\t...(needsVideoLinks ? { ifMatch: payloads.videoLinksRevision } : {}),\n\t\t\t}\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 (needsVideoLinks && err instanceof ServerClientHttpError && err.status === 409) {\n\t\t\t// spec §3.2.2:video_links 的 409(revision conflict)絕不自動重送(同 replace.ts 對四區塊\n\t\t\t// 409 的既有慣例)。重新 GET 一次算出 video_links 差異、連同(若有)partial-failure body 的\n\t\t\t// updated_sections/failed_section 一起印出,本地 exit 2,交還使用者重新確認。\n\t\t\tconst { body: freshBody } = await enterpriseGet(requestOpts, '/company');\n\t\t\tconst freshCurrent = unwrapDataResponse<EnterpriseCompany>(freshBody);\n\t\t\tconst freshVideoLinks = (freshCurrent.video_links as ExistingBlockItem[] | undefined) ?? [];\n\t\t\tconst diff = summarizeItemsDiff(payloads.currentVideoLinks ?? [], freshVideoLinks);\n\t\t\tconst partialBody = isDescriptionsPartialFailureBody(err.body) ? err.body : undefined;\n\n\t\t\tprintWarn(`Server state for \"video_links\" changed since your read: ${diff}.`, ctx.color ?? false);\n\n\t\t\tconst descriptionsError = {\n\t\t\t\tupdated_sections: partialBody?.updated_sections ?? [],\n\t\t\t\tfailed_section: partialBody?.failed_section ?? 'video_links',\n\t\t\t\tvideo_links_diff: diff,\n\t\t\t};\n\t\t\tif (ctx.format === 'json') {\n\t\t\t\tprintJson(\n\t\t\t\t\tneedsBasic\n\t\t\t\t\t\t? { basic_updated: true, basic_company: basicResultCompany, descriptions_error: descriptionsError }\n\t\t\t\t\t\t: { descriptions_error: descriptionsError }\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tif (needsBasic) process.stdout.write('Basic company info was updated successfully.\\n');\n\t\t\t\tprocess.stdout.write(\n\t\t\t\t\t`Updated sections before failure: ${descriptionsError.updated_sections.join(', ')}; failed section: ${descriptionsError.failed_section}\\n`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthrow new InvalidArgumentError(\n\t\t\t\t`Refusing to auto-retry \"video_links\" update after a stale revision conflict (409). ${diff}. ` +\n\t\t\t\t\t'Re-run `wport enterprise company view` and retry `company update --confirm` against the current state.'\n\t\t\t);\n\t\t}\n\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\tconfirm?: boolean;\n}\n\n/**\n * `company update` 註冊(pm_41 Card B Task 3;2026-09-04 spec 批 E 擴節:`video_links` 需要\n * `--confirm` 才能刪除既有影片,同 `company replace` 的既有慣例)。\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\t\t'is_representative_visible / is_establishment_date_visible / is_directors_visible ' +\n\t\t\t\t'default to 0 (hidden) when not set. ' +\n\t\t\t\t'video_links (in descriptions) uses optimistic locking (If-Match, handled automatically ' +\n\t\t\t\t'from your latest `company view`); removing any existing video requires --confirm.'\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.option(\n\t\t\t'--confirm',\n\t\t\t'confirm deletion of existing video_links items not present in the input (required when video_links is provided and would remove any existing item)'\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\tflags.confirm === true\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","import { InvalidArgumentError } from '../../../lib/errors';\nimport { printWarn } from '../../../lib/output';\n\n/**\n * 四區塊整段取代(`company replace`)與 descriptions `video_links` 刪除(`company update`,\n * pm_38 spec §3.2.2)共用的「刪除預覽 + confirm 規則」(spec §3.2.3 第 2 點)。批 D 只消費它\n * 給 `replace.ts`;批 E 的 `company update` video_links 應沿用同一套規則,不要另外寫一份。\n */\n\n/** request body 裡「送出的」一筆項目:只有 `enc_id` 對 diff 有意義(新項目通常省略)。 */\nexport interface DeletableCandidate {\n\tenc_id?: string | null;\n}\n\n/** GET /company 回傳的「現況」一筆項目:`enc_id` 恆存在。 */\nexport interface ExistingBlockItem {\n\tenc_id: string;\n\t[key: string]: unknown;\n}\n\nexport interface DeletionPreviewEntry {\n\tenc_id: string;\n\tsummary: string;\n}\n\n/**\n * 驗證「使用者送出的 items/video_links 陣列」本身是陣列、且每一筆都是 non-null 物件\n * (不是 array,不是字串/數字/布林等 primitive)。\n *\n * fresh-eyes F-2:`previewDeletions` 內部直接讀 `i.enc_id`,若某筆是 `null` 會丟未捕捉的\n * `TypeError`(`Cannot read properties of null`),被頂層 `exitCodeForError` 的預設分支誤判成\n * `ServerOrNetworkError`(exit 4)——這是純粹的使用者輸入錯誤,必須和其他本地輸入驗證一樣是\n * `InvalidArgumentError`(exit 2)。更隱蔽的是非物件的 primitive(例如一個裸字串):\n * `typeof i.enc_id === 'string'` 對字串取 `.enc_id` 只會得到 `undefined`,不會拋錯,該筆會被\n * `previewDeletions` 悄悄濾掉、然後原封不動地被送進 PUT/PATCH request body——同樣是必須本地擋下\n * 的輸入錯誤,只是不會馬上炸,危害更大。\n *\n * `company replace`(`items`)與 `company update` 的 `video_links` 共用同一套規則,呼叫端在\n * 讀完 `--file`/stdin 之後、呼叫 `previewDeletions` 之前(且必須在任何網路呼叫之前)都要過這關。\n */\nexport function assertValidSubmittedItems(value: unknown, label: string): Record<string, unknown>[] {\n\tif (!Array.isArray(value)) {\n\t\tthrow new InvalidArgumentError(\n\t\t\t`\"${label}\" must be a JSON array, got ${value === null ? 'null' : typeof value}`\n\t\t);\n\t}\n\tvalue.forEach((item, index) => {\n\t\tif (item === null || typeof item !== 'object' || Array.isArray(item)) {\n\t\t\tthrow new InvalidArgumentError(\n\t\t\t\t`\"${label}[${index}]\" must be a JSON object, got ` +\n\t\t\t\t\t(item === null ? 'null' : Array.isArray(item) ? 'an array' : typeof item)\n\t\t\t);\n\t\t}\n\t});\n\treturn value as Record<string, unknown>[];\n}\n\n/**\n * 現況 vs. 本次送出的項目比對出「即將被刪除」的清單:現況存在、但送出項目沒有帶同一個\n * `enc_id` 的項目。**不限 `submitted: []`**——非空但漏列一筆也算刪除(spec §3.2.3 第 2 點:\n * 「只要會移除任何一筆」)。\n *\n * 前提:`submitted` 每一筆都已是 non-null 物件(呼叫端先過 `assertValidSubmittedItems`)——\n * 本函式不再自己防禦 `null`/primitive 元素,那類輸入錯誤應在更早的地方本地擋下並回 exit 2,\n * 而不是讓這裡的 `i.enc_id` 存取意外丟出未分類的 `TypeError`。\n */\nexport function previewDeletions<T extends ExistingBlockItem>(\n\tcurrent: readonly T[],\n\tsubmitted: readonly DeletableCandidate[],\n\tsummarize: (item: T) => string\n): DeletionPreviewEntry[] {\n\tconst submittedIds = new Set(\n\t\tsubmitted.filter((i): i is { enc_id: string } => typeof i.enc_id === 'string').map((i) => i.enc_id)\n\t);\n\treturn current\n\t\t.filter((item) => !submittedIds.has(item.enc_id))\n\t\t.map((item) => ({ enc_id: item.enc_id, summary: summarize(item) }));\n}\n\n/**\n * 有刪除就先印出清單(stderr,供人/agent 判讀),再依 `confirm` 決定要不要往下走。\n *\n * 本專案沒有 TTY 互動 prompt 機制(`jobs delete`/`jobs batch`/`resumes delete` 皆是\n * 「無 `--confirm` 一律本地 exit 2、不分是否為互動終端機」),刻意不新造一套 readline\n * prompt——那會偏離 Existing-First 與 YAGNI。因此這裡的規則統一為:**不論 TTY 與否,\n * 有刪除就必須顯式 `--confirm`**,滿足 spec §3.2.3 第 2 點「non-interactive 必須顯式帶\n * `--confirm`,否則 exit 2」這個可驗收的行為;沒有互動 prompt 的分支。\n */\nexport function enforceDeletionConfirmation(\n\tlabel: string,\n\tdeletions: readonly DeletionPreviewEntry[],\n\tconfirm: boolean,\n\tcolor: boolean\n): void {\n\tif (deletions.length === 0) return;\n\tprintWarn(\n\t\t`This replace will delete ${deletions.length} existing \"${label}\" item(s): ` +\n\t\t\tdeletions.map((d) => `${d.enc_id} (${d.summary || 'untitled'})`).join('; '),\n\t\tcolor\n\t);\n\tif (!confirm) {\n\t\tthrow new InvalidArgumentError(\n\t\t\t`Refusing to replace \"${label}\" without --confirm: ${deletions.length} existing item(s) would be ` +\n\t\t\t\t'deleted (see the warning above for the enc_id list). Re-run with --confirm to proceed.'\n\t\t);\n\t}\n}\n\n/**\n * 兩份「現況」快照(409 前 vs. 409 後重新 GET)的差異摘要,供 409 conflict 訊息使用。\n * 只比對 enc_id 集合的增減——足以讓使用者知道「誰動了它」,不需要逐欄位深比對。\n */\nexport function summarizeItemsDiff(\n\tbefore: readonly ExistingBlockItem[],\n\tafter: readonly ExistingBlockItem[]\n): string {\n\tconst beforeIds = new Set(before.map((i) => i.enc_id));\n\tconst afterIds = new Set(after.map((i) => i.enc_id));\n\tconst added = after.filter((i) => !beforeIds.has(i.enc_id)).map((i) => i.enc_id);\n\tconst removed = before.filter((i) => !afterIds.has(i.enc_id)).map((i) => i.enc_id);\n\tconst parts: string[] = [];\n\tif (added.length > 0) parts.push(`added since your read: ${added.join(', ')}`);\n\tif (removed.length > 0) parts.push(`removed since your read: ${removed.join(', ')}`);\n\tif (parts.length === 0) parts.push('item content or order changed (enc_id set unchanged)');\n\treturn parts.join('; ');\n}\n","/**\n * 企業公司資訊型別與欄位分組(pm_41 Card B Task 0;2026-09-04 spec 批 E 擴節)。\n * 欄位對齊 server EnterpriseCompanyVm(enterprise-company.vm.ts)。\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/** 負責人/設立日期/董監事 + 三個顯示開關(spec §2.1、§3.1;`is_uniform_number_visible` 明確不開放,見 FORBIDDEN_FIELDS)。 */\n\trepresentative_name?: string | null;\n\testablishment_date?: string | null;\n\tdirectors?: string[] | null;\n\tis_representative_visible?: number;\n\tis_establishment_date_visible?: number;\n\tis_directors_visible?: number;\n\t/** 五個可整段取代區塊(讀端點才有;`company replace` 用其中四個,`video_links` 由 `company update` 的 descriptions 節寫入)。 */\n\tpain_points?: Array<Record<string, unknown>>;\n\tawards?: Array<Record<string, unknown>>;\n\tmilestones?: Array<Record<string, unknown>>;\n\tqa_items?: Array<Record<string, unknown>>;\n\tvideo_links?: Array<Record<string, unknown>>;\n\t/** 五個區塊各自的 opaque revision(`If-Match` 用;不可解析內容,spec §3.1.1)。 */\n\tblock_revisions?: Record<string, string>;\n\t[k: string]: unknown;\n}\n\n/**\n * `PATCH /company/basic` 欄位(6 必填 + 10 選填,`uniform_number` 唯讀不在此列)。\n * 2026-09-04 spec 批 E 擴節:負責人/設立日期/董監事 + 三個顯示開關(不含 `is_uniform_number_visible`,\n * 見 FORBIDDEN_FIELDS 與 spec §1「明確不做」)。\n */\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\t'representative_name',\n\t'establishment_date',\n\t'directors',\n\t'is_representative_visible',\n\t'is_establishment_date_visible',\n\t'is_directors_visible',\n];\n\n/**\n * `PATCH /company/descriptions` 欄位(4 個各自獨立選填,非 atomic)。\n * 2026-09-04 spec 批 E 擴節:新增 `video_links`——與其餘三個不同,`video_links` 若出現在輸入中\n * 需要額外的樂觀鎖(`If-Match`)與刪除確認流程,見 `update.ts` 對 `video_links` 的專門處理,\n * 不能只靠這份清單機械帶過。\n */\nexport const DESCRIPTION_FIELDS: ReadonlyArray<string> = ['description', 'products_services', 'latest_news', 'video_links'];\n\n/**\n * 本地擋 CLI 輸入用的禁止欄位清單。\n *\n * - `uniform_number`:唯讀(BR-024)。\n * - `is_uniform_number_visible`:DB 有此欄(`CompanyInfo.ts:249`),2026-09-04 決定**不開放**給\n * enterprise API/CLI(spec §1「明確不做」)——不是尚未落地,是刻意排除,往後也不會加。\n * - `banner_url`/`photo_1-3`/`video_1-3`:banner 上傳與照片另案(spec §1「明確不做」),\n * 本批未落地,仍是 best-effort 提前擋。\n *\n * 2026-09-04 spec 批 E 移除的舊 placeholder 名稱:`foundation_date`(真實欄位是\n * `establishment_date`)、`representative`(真實欄位是 `representative_name`)、`directors`\n * (名稱本身即真實欄位,現已在 BASIC_FIELDS)、`milestones`/`awards`/`qa`(`qa` 錯名,真實是\n * `qa_items`;三者現由 `company replace <section>` 落地,不再是 CLI 完全不支援的欄位)。\n */\nexport const FORBIDDEN_FIELDS: ReadonlyArray<string> = [\n\t'uniform_number',\n\t'is_uniform_number_visible',\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];\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 '@wport/core';\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 { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse, WportHttpError, type EnterpriseCompanyBlockReplaceResultDto } from '@wport/core';\nimport { CliError, ExitCode, InvalidArgumentError } from '../../../lib/errors';\nimport { enterpriseGet, enterprisePut } from '../../../lib/enterprise-client';\nimport { resolveApiKey } from '../../../lib/credentials-store';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { readJsonObject, type ReadJsonInputOptions } from '../../../lib/io-helpers';\nimport { printJson, printTable, printWarn } from '../../../lib/output';\nimport {\n\tassertValidSubmittedItems,\n\tenforceDeletionConfirmation,\n\tpreviewDeletions,\n\tsummarizeItemsDiff,\n\ttype ExistingBlockItem,\n} from './block-replace-shared';\n\n/**\n * `wport enterprise company replace <section>`(spec §3.2.3):四區塊整段取代\n * (pain-points/awards/milestones/qa-items)。動詞固定 `replace`——未列出的既有項目\n * 一律刪除,不是 add/update。\n */\nexport const REPLACE_SECTIONS = ['pain-points', 'awards', 'milestones', 'qa-items'] as const;\nexport type ReplaceSection = (typeof REPLACE_SECTIONS)[number];\n\ninterface SectionConfig {\n\t/** `PUT /company/<...>` 相對 path。 */\n\tpath: string;\n\t/** GET /company 回傳陣列欄位名,同時也是 block_revisions 的 key(兩者恆同名)。 */\n\tdataKey: string;\n\t/** 人類可讀摘要(刪除預覽 / table 輸出共用),各區塊的「標題型」欄位不同。 */\n\tsummarize: (item: ExistingBlockItem) => string;\n}\n\nconst SECTION_CONFIG: Record<ReplaceSection, SectionConfig> = {\n\t'pain-points': {\n\t\tpath: '/company/pain-points',\n\t\tdataKey: 'pain_points',\n\t\tsummarize: (i) => String(i.title ?? ''),\n\t},\n\tawards: {\n\t\tpath: '/company/awards',\n\t\tdataKey: 'awards',\n\t\tsummarize: (i) => String(i.name ?? ''),\n\t},\n\tmilestones: {\n\t\tpath: '/company/milestones',\n\t\tdataKey: 'milestones',\n\t\tsummarize: (i) => String(i.title ?? ''),\n\t},\n\t'qa-items': {\n\t\tpath: '/company/qa-items',\n\t\tdataKey: 'qa_items',\n\t\tsummarize: (i) => String(i.question ?? ''),\n\t},\n};\n\nfunction isReplaceSection(v: string): v is ReplaceSection {\n\treturn (REPLACE_SECTIONS as readonly string[]).includes(v);\n}\n\n/**\n * fresh-eyes F-1:改用 codegen 型別 `EnterpriseCompanyBlockReplaceResultDto`(`components['schemas']\n * .EnterpriseCompanyBlockReplaceResult`)取代原本手寫的 `BlockReplaceResult`,對齊 spec §3.3/\n * `personal-types.ts:53-56` 既有模式。`revision` 直接沿用 codegen 型別;`items` 覆寫成\n * `ExistingBlockItem[]`——上游 OpenAPI schema 對 `items` 的元素只標了 `{ type: 'object' }`\n * (見 `enterprise-schema.d.ts` 的 `Record<string, never>[]`,未進一步建模成個別 `*ResponseItem`\n * 聯集),沿用 codegen 原樣會讓 `.enc_id`/`config.summarize()` 完全存取不到;覆寫單一欄位而非整個\n * 手寫,讓其餘欄位(`revision`)仍然吃 codegen 型別、契約漂移時仍能在編譯期擋下。\n */\nexport type BlockReplaceResult = Omit<EnterpriseCompanyBlockReplaceResultDto, 'items'> & {\n\titems?: ExistingBlockItem[];\n};\n\nfunction printReplaceResult(\n\tresult: BlockReplaceResult,\n\tconfig: SectionConfig,\n\tformat: 'table' | 'json',\n\tcolor: boolean\n): void {\n\tif (format === 'json') {\n\t\tprintJson(result);\n\t\treturn;\n\t}\n\tprintTable(\n\t\tresult.items ?? [],\n\t\t[\n\t\t\t{ header: 'enc_id', value: (r: ExistingBlockItem) => String(r.enc_id ?? '') },\n\t\t\t{ header: 'summary', value: (r: ExistingBlockItem) => config.summarize(r) },\n\t\t],\n\t\tcolor\n\t);\n\t// fresh-eyes F-4(NIT):table 模式刻意印 revision,spec §3.2.1「revision 只在 JSON 顯示」是\n\t// 針對 `company view`(避免被當成人類可讀版本號),對 `replace` 本身沒有禁止——replace 之後\n\t// 使用者常常要立刻拿這個 opaque token 做下一次寫入的 If-Match,table 模式也印出來省一次\n\t// `--output json`。不要因為看到 view 的規則就誤以為這裡漏改。\n\tprocess.stdout.write(`revision: ${result.revision ?? ''}\\n`);\n}\n\n/**\n * `company replace` 核心(spec §3.2.3)。\n *\n * 順序(早退在前,網路呼叫在後,同 `company update` 的既有慣例):\n * 1. section enum 本地驗證 —— 不需要任何輸入或網路即可判斷\n * 2. 讀 `--file`/stdin,驗證 `{ items: [...] }` 形狀\n * 3. `GET /company` 一次:同時取得現況項目(刪除預覽用)與目標 revision(If-Match 用)\n * 4. 用現況 vs. 送出項目算刪除預覽,套用 confirm 規則(`block-replace-shared`,\n * §3.2.3 第 2 點——非空漏一筆也算刪除,不限 `items: []`)\n * 5. `PUT` 帶 If-Match;409 一律不自動重送(§3.2.3 第 3 點)——改成重新 GET、印出\n * server 與本地快照的差異、exit 2,要求使用者重新確認\n */\nexport async function runCompanyReplace(\n\tctx: ResolvedContext,\n\tapiKey: string,\n\tsection: string,\n\tsource: string,\n\tconfirm: boolean,\n\tidempotencyKey: string,\n\toptions: ReadJsonInputOptions = {}\n): Promise<void> {\n\tif (!isReplaceSection(section)) {\n\t\tthrow new InvalidArgumentError(`Invalid section \"${section}\". Allowed: ${REPLACE_SECTIONS.join(', ')}`);\n\t}\n\tconst config = SECTION_CONFIG[section];\n\n\tconst input = readJsonObject(source, options);\n\tif (!Array.isArray(input.items)) {\n\t\tthrow new InvalidArgumentError('Input must be a JSON object of the form { \"items\": [...] }');\n\t}\n\t// fresh-eyes F-2:光靠 `Array.isArray` 不夠——陣列裡的元素仍可能是 `null` 或其他 primitive,\n\t// 那類輸入必須在這裡(任何網路呼叫之前)就被擋成 exit 2,不能留到 previewDeletions 才炸。\n\tconst items = assertValidSubmittedItems(input.items, 'items');\n\n\tconst requestOpts = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs, apiKey };\n\n\tconst { body: getBody } = await enterpriseGet(requestOpts, '/company');\n\tconst current = unwrapDataResponse<Record<string, unknown>>(getBody);\n\tconst currentItems = (current[config.dataKey] as ExistingBlockItem[] | undefined) ?? [];\n\tconst revision = (current.block_revisions as Record<string, string> | undefined)?.[config.dataKey];\n\tif (!revision) {\n\t\t// 防禦性檢查:Card A(spec §3.1.1) 承諾 block_revisions 一定帶五個 key。理論上不該發生,\n\t\t// 一旦發生代表後端契約漂移,不是使用者輸入錯誤——同 company/update.ts 的\n\t\t// CONTRACT_REQUIRED_GET_FIELDS 慣例,用 ServerOrNetworkError(exit 4)。\n\t\tthrow new CliError(\n\t\t\t`Backend contract appears broken: GET /company response is missing block_revisions.${config.dataKey}`,\n\t\t\tExitCode.ServerOrNetworkError\n\t\t);\n\t}\n\n\tconst deletions = previewDeletions(currentItems, items, config.summarize);\n\tenforceDeletionConfirmation(section, deletions, confirm, ctx.color);\n\n\ttry {\n\t\tconst { body } = await enterprisePut(requestOpts, config.path, { items }, { idempotencyKey, ifMatch: revision });\n\t\tconst result = unwrapDataResponse<BlockReplaceResult>(body);\n\t\tprintReplaceResult(result, config, ctx.format, ctx.color);\n\t} catch (err) {\n\t\tif (err instanceof WportHttpError && err.status === 409) {\n\t\t\t// spec §3.2.3 第 3 點:409 絕不自動重送。重新 GET 一次算出差異、印出來,\n\t\t\t// 然後本地 exit 2 —— 不再呼叫第二次 PUT,交還使用者重新確認。\n\t\t\tconst { body: freshBody } = await enterpriseGet(requestOpts, '/company');\n\t\t\tconst freshCurrent = unwrapDataResponse<Record<string, unknown>>(freshBody);\n\t\t\tconst freshItems = (freshCurrent[config.dataKey] as ExistingBlockItem[] | undefined) ?? [];\n\t\t\tconst diff = summarizeItemsDiff(currentItems, freshItems);\n\t\t\tprintWarn(`Server state for \"${section}\" changed since your read: ${diff}.`, ctx.color);\n\t\t\tthrow new InvalidArgumentError(\n\t\t\t\t`Refusing to auto-retry \"${section}\" replace after a stale revision conflict (409). ${diff}. ` +\n\t\t\t\t\t'Re-run `wport enterprise company view` and retry `company replace --confirm` against the current state.'\n\t\t\t);\n\t\t}\n\t\tthrow err;\n\t}\n}\n\ninterface ReplaceFlags {\n\tfile?: string;\n\tconfirm?: boolean;\n\tidempotencyKey?: string;\n}\n\nexport function registerEnterpriseCompanyReplace(parent: Command): void {\n\tparent\n\t\t.command('replace <section>')\n\t\t.description(\n\t\t\t`Replace an entire company profile block from a JSON file. <section> is one of: ${REPLACE_SECTIONS.join('|')}. ` +\n\t\t\t\t'This is a full replacement, not an add/update: any existing item not listed in the input is deleted.'\n\t\t)\n\t\t.requiredOption('--file <path>', 'path to a JSON body { \"items\": [...] }, or \"-\" for stdin')\n\t\t.option('--confirm', 'confirm deletion of existing items that are not present in the input')\n\t\t.option('--idempotency-key <key>', 'reuse across retries (default: a fresh UUID)')\n\t\t.action(async (section: string, flags: ReplaceFlags, 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 runCompanyReplace(\n\t\t\t\tctx,\n\t\t\t\tkey,\n\t\t\t\tsection,\n\t\t\t\tflags.file as string,\n\t\t\t\tflags.confirm === true,\n\t\t\t\tflags.idempotencyKey ?? randomUUID(),\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__ = { SECTION_CONFIG, isReplaceSection };\n","import type { Command } from 'commander';\nimport { registerEnterpriseCompanyView } from './view';\nimport { registerEnterpriseCompanyUpdate } from './update';\nimport { registerEnterpriseCompanyLogo } from './logo';\nimport { registerEnterpriseCompanyReplace } from './replace';\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\tregisterEnterpriseCompanyReplace(company);\n}\n","// apps/cli/src/commands/enterprise/talents/list.ts\nimport type { Command } from 'commander';\nimport { asPaginatedBody } from '@wport/core';\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 '@wport/core';\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 '@wport/core';\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\tencJobId?: 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\t// enc_job_id 為 optional:帶則後端解密後精準選該職缺的聊天室,不帶則回覆最近一筆應徵(向後相容)。\n\t// CLI 端不解密、直接帶加密 id;空白視同未帶。\n\tconst encJobId = flags.encJobId?.trim();\n\tconst payload: Record<string, unknown> = { subject, body };\n\tif (encJobId) payload.enc_job_id = encJobId;\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\tpayload,\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('--enc-job-id <enc_id>', 'reply to a specific job posting; omit to reply to the most recent application')\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\t// Scope note (issue #106 P1-4): applied pool only — visit tab not available yet (`--tab visit` → 400)\n\t// and no active candidate search over an API Key (`/api/search-talent` needs a JWT; see `wport doctor`).\n\tconst talents = parent\n\t\t.command('talents')\n\t\t.description(\n\t\t\t'Browse & respond to your applied talent pool (visit tab n/a, no active candidate search — see `wport doctor`)'\n\t\t);\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 '@wport/core';\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 '@wport/core';\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 '@wport/core';\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)。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 '@wport/core';\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 '@wport/core';\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","import type { Command } from 'commander';\nimport { hostname as osHostname } from 'node:os';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { requestDeviceCode, pollForToken, type OauthRequestOptions } from '../../lib/oauth';\nimport { loadPersonalCredentials, savePersonalCredentials } from '../../lib/credentials-store';\nimport { openInBrowser } from '../../lib/browser-open';\nimport { channelBanner } from '../../lib/channel';\n\nexport interface LoginFlags {\n\tnoBrowser?: boolean;\n\tforce?: boolean;\n}\n\n/** 可注入的相依,測試用假實作取代真的等待/開瀏覽器/主機名稱。 */\nexport interface LoginDeps {\n\tsleep?: (ms: number) => Promise<void>;\n\topenBrowser?: (url: string) => boolean;\n\thostname?: () => string;\n}\n\n/** 已登入時的識別字串;display_name/email 本階段可能皆為空字串(見下方大段註解),故逐層 fallback。 */\nfunction describeIdentity(displayName: string, email: string): string {\n\tif (displayName && email) return `${displayName} (${email})`;\n\tif (displayName) return displayName;\n\tif (email) return email;\n\treturn 'this device';\n}\n\n/**\n * `wport login` 核心流程(RFC 8628 device authorization grant)。\n *\n * display_name/email 本階段以空字串存入 credentials —— AS 的 TokenResponse 不含身分資訊,\n * v1 沒有 profile 端點可回填;Task 5(whoami)改走 `GET /oauth/sessions` 的 is_current 撈\n * 裝置名/建立時間做替代身分顯示,這裡先誠實存空值,等 spec B profile 端點落地後再回填。\n */\nexport async function performLogin(ctx: ResolvedContext, flags: LoginFlags, deps: LoginDeps = {}): Promise<void> {\n\tconst banner = channelBanner();\n\tif (banner) process.stderr.write(banner);\n\n\tconst openBrowser = deps.openBrowser ?? openInBrowser;\n\tconst hostnameFn = deps.hostname ?? osHostname;\n\n\tif (!flags.force) {\n\t\tconst existing = loadPersonalCredentials();\n\t\tif (existing) {\n\t\t\tprocess.stdout.write(`Already logged in as ${describeIdentity(existing.display_name, existing.email)}.\\n`);\n\t\t\tprocess.stdout.write('Run `wport login --force` to sign in again.\\n');\n\t\t\treturn;\n\t\t}\n\t}\n\n\tconst oauthOpts: OauthRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst device = await requestDeviceCode(oauthOpts, hostnameFn());\n\n\t// user_code 是使用者要抄去網頁輸入的一次性代碼,獨立換行、留白讓它在終端機裡醒目好認。\n\tprocess.stdout.write(`\\nEnter this code when prompted:\\n\\n ${device.user_code}\\n\\n`);\n\tprocess.stdout.write(`Visit: ${device.verification_uri}\\n`);\n\n\tif (!flags.noBrowser && process.stdout.isTTY) {\n\t\tconst opened = openBrowser(device.verification_uri_complete);\n\t\tif (!opened) {\n\t\t\tprocess.stdout.write(`Open this URL manually: ${device.verification_uri_complete}\\n`);\n\t\t} else {\n\t\t\t// 即使成功呼叫開瀏覽器的指令也照印網址——SSH 顯示器分離場景下,指令本身可能成功\n\t\t\t// 執行但沒有畫面能顯示瀏覽器,使用者仍需要這行網址才能自己拿去別的裝置開。\n\t\t\tprocess.stdout.write(`Opened in your browser. If nothing appeared, visit: ${device.verification_uri_complete}\\n`);\n\t\t}\n\t}\n\n\tconst tokens = await pollForToken(oauthOpts, device, deps.sleep);\n\n\tconst now = Date.now();\n\tsavePersonalCredentials({\n\t\taccess_token: tokens.access_token,\n\t\trefresh_token: tokens.refresh_token,\n\t\texpires_at: new Date(now + tokens.expires_in * 1000).toISOString(),\n\t\tdisplay_name: '',\n\t\temail: '',\n\t\tsession_created_at: new Date(now).toISOString(),\n\t});\n\n\tprocess.stdout.write('Logged in.\\n');\n}\n\nexport function registerLoginCommand(program: Command): void {\n\tprogram\n\t\t.command('login')\n\t\t.description('Sign in to your wport personal account (OAuth device authorization flow)')\n\t\t.option('--no-browser', 'do not try to open a browser automatically')\n\t\t.option('--force', 'sign in again even if already logged in')\n\t\t.action(async (opts: { browser?: boolean; force?: boolean }, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tawait performLogin(ctx, { noBrowser: opts.browser === false, force: opts.force });\n\t\t});\n}\n","import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';\n\n/** spawn 相容型別:測試可注入假實作,不必真的產生行程。 */\nexport type SpawnFn = (command: string, args: readonly string[], options: SpawnOptions) => ChildProcess;\n\ninterface BrowserCommand {\n\tcommand: string;\n\targs: string[];\n}\n\n/**\n * URL 安全閘(security-checklist:API 回應是外部輸入,不得進 shell):\n * - 僅開 http(s)——`javascript:`/`file:` 等 scheme 交給瀏覽器以外的 handler 是攻擊面\n * - win32 額外拒開含 cmd metacharacters 的 URL:`cmd /c start` 的參數會被 cmd.exe 重新解析,\n * spawn 的陣列參數形式擋不住 `&`/`|` 這類串接(`?a=1&b=2` 會在 & 後當新指令執行)。\n * 合法的 device-flow 驗證頁 URL(`https://wport.me/activate?user_code=XXXX-XXXX`)不含\n * 這些字元,拒開零誤傷;被拒時回 false,呼叫端 fallback 印 URL 讓使用者自己貼。\n */\nfunction isSafeToOpen(url: string, platform: NodeJS.Platform): boolean {\n\tlet parsed: URL;\n\ttry {\n\t\tparsed = new URL(url);\n\t} catch {\n\t\treturn false;\n\t}\n\tif (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return false;\n\tif (platform === 'win32' && /[&|^<>%\"]/.test(url)) return false;\n\treturn true;\n}\n\n/** 依平台決定開瀏覽器要跑的指令;不支援的平台(如 sunos/aix 等罕見值)回 null 讓呼叫端 fallback。 */\nfunction commandFor(platform: NodeJS.Platform, url: string): BrowserCommand | null {\n\tswitch (platform) {\n\t\tcase 'darwin':\n\t\t\treturn { command: 'open', args: [url] };\n\t\tcase 'win32':\n\t\t\t// cmd /c start 的第一個參數會被當成視窗標題吃掉;空字串佔位避免 URL 被誤判成標題(常見坑)。\n\t\t\treturn { command: 'cmd', args: ['/c', 'start', '\"\"', url] };\n\t\tcase 'linux':\n\t\t\treturn { command: 'xdg-open', args: [url] };\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\n/**\n * 跨平台開啟瀏覽器。detached + unref:CLI 進程不該被瀏覽器行程拖住——瀏覽器可能長駐執行,\n * 若父子行程綁在一起,CLI 會被迫等到使用者關掉瀏覽器才結束,體感上完全不合理。\n *\n * 失敗處理分兩種情況:\n * - 同步丟例外(不支援的平台 / spawn 呼叫本身炸掉)→ try/catch 接住,回 false,\n * 呼叫端(login 命令)在這之上疊一層 fallback:印出網址讓使用者自己複製貼上。\n * - 非同步 'error' event(例如指令真的不存在的 ENOENT)→ spawn() 是先同步回傳 ChildProcess,\n * 錯誤才在下一輪事件迴圈以 'error' event 送達;此時函式早已同步回傳 true。EventEmitter 對\n * 沒人監聽的 'error' event 會直接重新丟出、讓整個 process crash,所以這裡必須 attach 一個\n * 被動的 listener 吞掉它,換取「CLI 不會被開瀏覽器這種錦上添花的動作拖垮」——代價是這個情境下\n * 回傳值仍是(已經回傳過的)true,無法回頭改成 false。\n */\nexport function openInBrowser(url: string, spawnFn: SpawnFn = spawn): boolean {\n\tif (!isSafeToOpen(url, process.platform)) return false;\n\tconst resolved = commandFor(process.platform, url);\n\tif (!resolved) return false;\n\ttry {\n\t\tconst child = spawnFn(resolved.command, resolved.args, { detached: true, stdio: 'ignore' });\n\t\tchild.on('error', () => {\n\t\t\t// 故意不做事:目的只是避免 unhandled 'error' event 讓 process crash,\n\t\t\t// 此時已經來不及回報失敗給呼叫端了。\n\t\t});\n\t\tchild.unref();\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","import type { Command } from 'commander';\nimport { unwrapDataArray } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../lib/personal-client';\nimport { loadPersonalCredentials } from '../../lib/credentials-store';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { printJson, sanitizeForTerminal } from '../../lib/output';\nimport { OAUTH_SESSIONS_BASE, SessionItem } from '@wport/core';\n\nconst PLACEHOLDER = '—';\n\n/**\n * 先 sanitize 再判空:device_name 等欄位是伺服器回應內容的往返值(見 SessionItem 契約),\n * 視同 untrusted 輸出處理——同一批資料稍後也會餵給 Task 6 的 `sessions list`(其他裝置名\n * 真的可被其他登入端任意設定),這裡先建立一致的防護慣例,不要等 Task 6 才補。\n * 先 sanitize 再檢查是否為空,避免「整串都是控制字元」的值繞過空值判斷、印出空白而非佔位符。\n */\nfunction display(value: string | null | undefined): string {\n\tconst clean = sanitizeForTerminal(value ?? '');\n\treturn clean.length > 0 ? clean : PLACEHOLDER;\n}\n\n/**\n * `wport whoami` 核心:`GET /oauth/sessions` 找出 `is_current` 那筆,顯示裝置名/伺服器端 session\n * 時間戳 + 本機登入時間。\n *\n * v1 沒有身分(display_name/email)可顯示 —— AS 的 TokenResponse 不含身分資訊,profile 端點待\n * spec B 落地(見 login.ts 大段註解);這裡改用 sessions API 的裝置資訊當替代身分佐證。\n *\n * 未登入時不重複判斷 —— `personalGet` 本身在 credentials 缺席時就會丟 CliError exit 3\n * (訊息含 `wport login`),這裡讓錯誤直接上拋給 top-level handler,避免與 personal-client\n * 的登入態檢查邏輯重複一份。\n */\nexport async function performWhoami(ctx: ResolvedContext): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);\n\tconst sessions = unwrapDataArray<SessionItem>(body);\n\tconst current = sessions.find((s) => s.is_current);\n\t// 呼叫到這裡代表 personalGet 剛才已用本機 personal 憑證通過認證,local 區必然存在。\n\tconst localLoginAt = loadPersonalCredentials()?.session_created_at ?? null;\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({\n\t\t\tlogged_in: true,\n\t\t\tdevice_name: current?.device_name ?? null,\n\t\t\tsession_created_at: current?.created_at ?? null,\n\t\t\tlast_used_at: current?.last_used_at ?? null,\n\t\t\tlocal_login_at: localLoginAt,\n\t\t});\n\t\treturn;\n\t}\n\n\tconst lines = [\n\t\t'Logged in.',\n\t\t`device: ${display(current?.device_name)}`,\n\t\t`session created: ${display(current?.created_at)}`,\n\t\t`last used: ${display(current?.last_used_at)}`,\n\t\t`local login time: ${display(localLoginAt)}`,\n\t];\n\tprocess.stdout.write(lines.join('\\n') + '\\n');\n}\n\nexport function registerWhoamiCommand(program: Command): void {\n\tprogram\n\t\t.command('whoami')\n\t\t.description('Show your current personal login session (contacts the server)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tawait performWhoami(resolveContext(command));\n\t\t});\n}\n","import { buildUserAgent, extractErrorMessage, fetchWithTimeout, throwForHttpStatus } from '@wport/core';\nimport { CliError, ExitCode } from './errors';\nimport { loadPersonalCredentials, savePersonalCredentials, type PersonalCredentials } from './credentials-store';\nimport { CLI_SOURCE } from './global-opts';\nimport { refreshAccessToken } from './oauth';\nimport { printWarn } from './output';\n\nexport interface PersonalRequestOptions {\n\tbaseUrl: string; // resolveContext 產出,已去尾斜線\n\tlocale: string;\n\ttimeoutMs: number;\n}\n\nexport interface PersonalResult {\n\tbody: unknown;\n\theaders: Headers;\n}\n\n/** 寫入請求可選 headers。resumes 各寫入端點皆帶 Idempotency-Key(create/update 編排每節各自獨立一把)。 */\nexport interface PersonalWriteExtra {\n\tidempotencyKey?: string;\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';\n\n/** access_token 距 expires_at 不到這個緩衝就視為「即將過期」,提前 refresh 換新,避免發出注定 401 的請求。 */\nconst EXPIRY_SKEW_MS = 30_000;\n\nconst NOT_LOGGED_IN_MESSAGE = 'Not logged in. Run `wport login`.';\n\n/**\n * 確保回傳的 personal credentials 尚未過期:距 expires_at 不到 30 秒(含無法解析成合法時間戳,\n * `Date.parse` 回 NaN 時比較恆為 false)就視為需要 refresh,一律走 refreshAndSave 換新再回傳。\n * personal 區缺席(未登入)→ CliError exit 3,訊息導向 `wport login`。\n */\nasync function ensureFreshCredentials(opts: PersonalRequestOptions): Promise<PersonalCredentials> {\n\tconst creds = loadPersonalCredentials();\n\tif (!creds) throw new CliError(NOT_LOGGED_IN_MESSAGE, ExitCode.ServerClientError);\n\tconst msUntilExpiry = Date.parse(creds.expires_at) - Date.now();\n\tif (msUntilExpiry >= EXPIRY_SKEW_MS) return creds;\n\treturn refreshAndSave(opts, creds);\n}\n\n/**\n * 呼叫 refresh grant 換新 token pair 並整份寫回 credentials store;display_name/email/\n * session_created_at 沿舊值(AS 的 TokenResponse 不含身分資訊)。`refreshAccessToken` 對\n * `invalid_grant` 已丟出 CliError exit 3(訊息含 `wport login`),這裡不攔截,讓呼叫端\n * 統一透過同一條錯誤路徑上拋;寫回失敗(磁碟錯誤等)也照常丟錯,不吞。\n */\nasync function refreshAndSave(opts: PersonalRequestOptions, creds: PersonalCredentials): Promise<PersonalCredentials> {\n\tconst tokens = await refreshAccessToken(opts, creds.refresh_token);\n\tconst updated: PersonalCredentials = {\n\t\t...creds,\n\t\taccess_token: tokens.access_token,\n\t\trefresh_token: tokens.refresh_token,\n\t\texpires_at: new Date(Date.now() + tokens.expires_in * 1000).toISOString(),\n\t};\n\tsavePersonalCredentials(updated);\n\treturn updated;\n}\n\ninterface RawAttemptResult {\n\tstatus: number;\n\tbody: unknown;\n\theaders: Headers;\n}\n\n/**\n * 送出單次請求(不含 refresh/retry 邏輯)。headers 形狀同 enterprise-client:UA / Accept-Language /\n * Accept 恆帶;有 body 才帶 Content-Type;`extra.idempotencyKey` 給了才帶 `Idempotency-Key`。\n */\nasync function attemptRequest(\n\topts: PersonalRequestOptions,\n\tmethod: HttpMethod,\n\tpath: string,\n\taccessToken: string,\n\tbody: Record<string, unknown> | undefined,\n\tquery: Record<string, string | number | undefined> | undefined,\n\textra: PersonalWriteExtra | undefined\n): Promise<RawAttemptResult> {\n\tconst url = new URL(`${opts.baseUrl}${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 headers: Record<string, string> = {\n\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t'Accept-Language': opts.locale,\n\t\t'User-Agent': buildUserAgent('wport-cli', __CLI_VERSION__),\n\t\t'X-Source': CLI_SOURCE,\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\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\treturn { status: res.status, body: respBody, headers: res.headers };\n}\n\n/**\n * personal API 請求共用核心:expires_at 預判 refresh(ensureFreshCredentials)→ 發請求 → 401 →\n * refresh 一次並重試一次(防迴圈:重試後仍非 2xx 就直接丟出,不再迴圈)。refresh 併發單飛不需要\n * 處理——CLI 為單命令生命週期,同一進程內不會有並行請求互搶 refresh。\n */\nasync function personalRequest(\n\topts: PersonalRequestOptions,\n\tmethod: HttpMethod,\n\tpath: string,\n\tbody: Record<string, unknown> | undefined,\n\tquery: Record<string, string | number | undefined> | undefined,\n\textra: PersonalWriteExtra | undefined\n): Promise<PersonalResult> {\n\tconst creds = await ensureFreshCredentials(opts);\n\tlet result = await attemptRequest(opts, method, path, creds.access_token, body, query, extra);\n\tif (result.status === 401) {\n\t\tconst refreshed = await refreshAndSave(opts, creds);\n\t\tresult = await attemptRequest(opts, method, path, refreshed.access_token, body, query, extra);\n\t}\n\tif (result.status < 200 || result.status >= 300) throwPersonalHttpError(result.status, result.body);\n\twarnIfRateLimitLow(result.headers);\n\treturn { body: result.body, headers: result.headers };\n}\n\n/** personal API 唯讀 GET。path 為完整路徑(呼叫端自帶 `/api/v1/personal/...` 或 `/oauth/sessions`)。 */\nexport function personalGet(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tquery?: Record<string, string | number | undefined>\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'GET', path, undefined, query, undefined);\n}\n\n/** personal API 寫入 POST。 */\nexport function personalPost(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: PersonalWriteExtra\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'POST', path, body, undefined, extra);\n}\n\n/** personal API 寫入 PATCH(如 published-status toggle)。 */\nexport function personalPatch(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: PersonalWriteExtra\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'PATCH', path, body, undefined, extra);\n}\n\n/** personal API 寫入 PUT(如改名、portfolio_links bulk 覆寫)。 */\nexport function personalPut(\n\topts: PersonalRequestOptions,\n\tpath: string,\n\tbody: Record<string, unknown>,\n\textra?: PersonalWriteExtra\n): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'PUT', path, body, undefined, extra);\n}\n\n/** personal API 寫入 DELETE。無 body。 */\nexport function personalDelete(opts: PersonalRequestOptions, path: string, extra?: PersonalWriteExtra): Promise<PersonalResult> {\n\treturn personalRequest(opts, 'DELETE', path, undefined, undefined, extra);\n}\n\n/**\n * 401 訊息改為個人線提示(re-run `wport login`);其餘狀態碼沿用 `throwForHttpStatus` 通用路徑。\n * 走到這裡代表 refresh + 重試一次後仍失敗(或本來就是非 401 的 4xx/5xx)——不再嘗試第二次 refresh。\n */\nfunction throwPersonalHttpError(status: number, body: unknown): never {\n\tif (status === 401) {\n\t\tconst base = extractErrorMessage(body) ?? `HTTP ${status}`;\n\t\tthrow new CliError(\n\t\t\t`${base} — Your session may have expired or the request was rejected. Run \\`wport login\\` to sign in again.`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n\tthrowForHttpStatus(status, body);\n}\n\n/** 剩餘配額 <10% 時 stderr 提醒(不阻斷)。邏輯複製自 enterprise-client.warnIfRateLimitLow(同款 rate limit headers)。 */\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 type { Command } from 'commander';\nimport { revokeRefreshToken, type OauthRequestOptions } from '../../lib/oauth';\nimport { loadPersonalCredentials, deletePersonalCredentials } from '../../lib/credentials-store';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { printWarn } from '../../lib/output';\n\n/**\n * `wport logout` 核心(RFC 7009 revoke + 本機清除)。\n *\n * revoke 失敗(server 不可達、逾時、5xx 等 —— 不管哪一種)都不阻擋登出:使用者下 `logout`\n * 指令的意圖是清掉「這台裝置」的本機 session,伺服器端撤銷只是順手做掉;做不到也要讓本機\n * 憑證照樣消失,不然使用者會卡在「以為登出了但其實沒有」的狀態,之後 personal-client 拿著\n * 同一份憑證繼續發請求。因此這裡刻意 catch 所有例外(不只 NetworkError),只印警告續行。\n *\n * 未登入時直接印訊息、正常返回(exit 0)——冪等,重複執行 logout 不該被當成錯誤。\n */\nexport async function performLogout(ctx: ResolvedContext): Promise<void> {\n\tconst creds = loadPersonalCredentials();\n\tif (!creds) {\n\t\tprocess.stdout.write('Not logged in.\\n');\n\t\treturn;\n\t}\n\n\tconst opts: OauthRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\ttry {\n\t\tawait revokeRefreshToken(opts, creds.refresh_token);\n\t} catch (err) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tprintWarn(`Failed to revoke the session on the server: ${message}. Removing local credentials anyway.`, ctx.color);\n\t}\n\n\tdeletePersonalCredentials();\n\tprocess.stdout.write('Logged out.\\n');\n}\n\nexport function registerLogoutCommand(program: Command): void {\n\tprogram\n\t\t.command('logout')\n\t\t.description('Sign out of your wport personal account (revokes the refresh token and clears local credentials)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tawait performLogout(resolveContext(command));\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { unwrapDataArray } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { printJson, printTable } from '../../lib/output';\nimport { OAUTH_SESSIONS_BASE, SessionItem } from '@wport/core';\n\n/** 格式化沿 enterprise/keys/list.ts 的 formatDate 模式(每個 list 命令各自複製一份,檔案小而聚焦)。 */\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\n/**\n * `sessions list` 核心:GET /oauth/sessions → 列出所有存活的登入 session(跨裝置)。\n * `is_current` 標記本機這台裝置,方便使用者辨識忘記登出的其他裝置(DR-7 失竊自救場景的前置偵查)。\n */\nexport async function runSessionsList(ctx: ResolvedContext): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst { body } = await personalGet(opts, OAUTH_SESSIONS_BASE);\n\tconst sessions = unwrapDataArray<SessionItem>(body);\n\n\tif (ctx.format === 'json') {\n\t\tprintJson(sessions);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tsessions,\n\t\t[\n\t\t\t{ header: 'ENC_ID', value: (r) => r.enc_id.slice(0, 14) },\n\t\t\t{ header: 'DEVICE', value: (r) => r.device_name ?? '', maxWidth: 24 },\n\t\t\t{ header: 'CREATED', value: (r) => formatDate(r.created_at), maxWidth: 12 },\n\t\t\t{ header: 'LAST_USED', value: (r) => formatDate(r.last_used_at), maxWidth: 12 },\n\t\t\t{ header: 'CURRENT', value: (r) => (r.is_current ? '*' : '') },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerSessionsList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your active personal login sessions (devices)')\n\t\t.action(async (_flags: unknown, command: Command) => {\n\t\t\tawait runSessionsList(resolveContext(command));\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { formatDate };\n","import type { Command } from 'commander';\nimport { OAUTH_SESSIONS_BASE } from '@wport/core';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalDelete, type PersonalRequestOptions } from '../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../lib/global-opts';\nimport { CliError, ExitCode } from '../../lib/errors';\nimport { printJson } from '../../lib/output';\n\ninterface RevokeFlags {\n\tallOthers?: boolean;\n}\n\n/** `DELETE /oauth/sessions/others` 契約:DataResponse<{ revoked_count }>(SoT = personal-sessions.controller.ts)。 */\ninterface RevokeOthersResult {\n\trevoked_count: number;\n}\n\n/**\n * `sessions revoke` 核心。`<enc_id>` 與 `--all-others` 互斥擇一 —— 都給或都缺都是使用者輸入錯誤,\n * 本地擋下(exit 2),不發任何請求。\n *\n * 撤銷「目前這台裝置」的 session 時,本機 credentials 刻意**不**主動清除:CLI 沒有廉價的方法\n * 分辨「這個 enc_id 是不是自己」(要另外打一次 GET /oauth/sessions 找 is_current 比對,換不到\n * 什麼),讓下一次 personal API 請求自然收到 401、走 personal-client 既有的「Run `wport login`」\n * 提示路徑即可——這與「在另一台裝置上撤銷本機 session」的收尾路徑完全一致,不需要為「自己撤自己」\n * 開特例。\n */\nexport async function runSessionsRevoke(\n\tctx: ResolvedContext,\n\tencId: string | undefined,\n\tallOthers: boolean\n): Promise<void> {\n\tconst hasEncId = encId !== undefined;\n\tif (hasEncId === allOthers) {\n\t\tthrow new CliError('Provide exactly one of <enc_id> or --all-others', ExitCode.InvalidArgument);\n\t}\n\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\n\tif (allOthers) {\n\t\tconst { body } = await personalDelete(opts, `${OAUTH_SESSIONS_BASE}/others`);\n\t\tconst result = unwrapDataResponse<RevokeOthersResult>(body);\n\t\tif (ctx.format === 'json') {\n\t\t\tprintJson(result);\n\t\t\treturn;\n\t\t}\n\t\tprocess.stdout.write(`Revoked ${result.revoked_count} other session(s).\\n`);\n\t\treturn;\n\t}\n\n\tconst trimmed = encId!.trim();\n\tif (!trimmed) throw new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t// 端點回 MsgResponse({ success, statusCode, message }),無 `data` 欄位 —— 不走 unwrapDataResponse\n\t// (那需要 `data` 鍵存在,硬套會誤丟「missing wrapper」)。回應內容本身不影響本地輸出。\n\tawait personalDelete(opts, `${OAUTH_SESSIONS_BASE}/${encodeURIComponent(trimmed)}`);\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: trimmed, revoked: true });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Revoked session ${trimmed}.\\n`);\n}\n\nexport function registerSessionsRevoke(parent: Command): void {\n\tparent\n\t\t.command('revoke [enc_id]')\n\t\t.description('Revoke a session by enc_id, or every other session with --all-others')\n\t\t.option('--all-others', 'revoke every session except the current one')\n\t\t.action(async (encId: string | undefined, flags: RevokeFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tawait runSessionsRevoke(ctx, encId, flags.allOthers === true);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { registerSessionsList } from './list';\nimport { registerSessionsRevoke } from './revoke';\n\nexport function registerSessionsCommand(program: Command): void {\n\tconst sessions = program.command('sessions').description('List and revoke your personal login sessions (devices)');\n\tregisterSessionsList(sessions);\n\tregisterSessionsRevoke(sessions);\n}\n","import type { Command } from 'commander';\nimport { buildAggregateJsonSchema } from '@wport/core';\nimport { printJson } from '../../../lib/output';\n\n/**\n * 離線命令:純從 definition.ts SoT 推導 JSON Schema,不發請求、不需登入。\n * 沒有 --output table 分支 —— schema 本質是文件而非資料列表,永遠印 JSON(同 config get 慣例)。\n */\nexport function registerPersonalResumesSchema(parent: Command): void {\n\tparent\n\t\t.command('schema')\n\t\t.description('Print the resume aggregate JSON Schema (offline, no login required)')\n\t\t.option('--section <name>', 'print only the given section (e.g. education, work_experience)')\n\t\t.action((opts: { section?: string }) => {\n\t\t\tprintJson(buildAggregateJsonSchema(opts.section));\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { validateAggregate } from '@wport/core';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { readJsonInput } from '../../../lib/io-helpers';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\n\ninterface ValidateFlags {\n\tfile: string;\n}\n\n/**\n * `resumes validate` 核心:離線本地驗證,不發請求、不需登入。通過印 `Valid.`(json:`{valid:true}`);\n * 失敗逐條印 `path: message` 到 stderr(json 模式改印 `{valid:false, issues}` 到 stdout),\n * 最後一律丟 CliError(exit 2) 讓 top-level handler 收斂 exit code —— 同 `jobs batch` 慣例:\n * 結果先印出,再丟錯誤觸發非 0 exit,命令本身不必碰 process.exit。\n */\nexport function runResumesValidate(ctx: ResolvedContext, filePath: string): void {\n\tconst input = readJsonInput(filePath, { timeoutMs: ctx.timeoutMs });\n\tconst issues = validateAggregate(input);\n\n\tif (issues.length === 0) {\n\t\tif (ctx.format === 'json') {\n\t\t\tprintJson({ valid: true });\n\t\t} else {\n\t\t\tprocess.stdout.write('Valid.\\n');\n\t\t}\n\t\treturn;\n\t}\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({ valid: false, issues });\n\t} else {\n\t\tfor (const issue of issues) {\n\t\t\tprocess.stderr.write(`${sanitizeForTerminal(issue.path)}: ${sanitizeForTerminal(issue.message)}\\n`);\n\t\t}\n\t}\n\tthrow new CliError(`${issues.length} validation issue(s) found`, ExitCode.InvalidArgument);\n}\n\nexport function registerPersonalResumesValidate(parent: Command): void {\n\tparent\n\t\t.command('validate')\n\t\t.description('Validate a resume aggregate JSON file locally (offline, no login required)')\n\t\t.requiredOption('--file <path>', 'path to a JSON resume aggregate, or \"-\" for stdin')\n\t\t.action((flags: ValidateFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\trunResumesValidate(ctx, flags.file);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { existsSync, writeFileSync } from 'node:fs';\nimport { buildTemplate } from '@wport/core';\nimport { InvalidArgumentError } from '../../../lib/errors';\nimport { printJson } from '../../../lib/output';\n\ninterface TemplateFlags {\n\tout?: string;\n}\n\n/**\n * `resumes template` 核心:離線命令,不發請求、不需登入,依 SECTIONS 產出範例骨架(buildTemplate())。\n * 沒有 `--out` → 印到 stdout(同 schema 命令慣例,永遠 JSON,不看 --output table/json);\n * 有 `--out` → 寫入該路徑,檔案已存在則拒絕覆蓋(exit 2)而非靜默蓋掉使用者現有內容。\n */\nexport function runResumesTemplate(outPath?: string): void {\n\tconst template = buildTemplate();\n\n\tif (outPath === undefined) {\n\t\tprintJson(template);\n\t\treturn;\n\t}\n\n\tif (existsSync(outPath)) {\n\t\tthrow new InvalidArgumentError(`File already exists: ${outPath} (refusing to overwrite — remove it or choose a different --out path)`);\n\t}\n\n\ttry {\n\t\twriteFileSync(outPath, `${JSON.stringify(template, null, 2)}\\n`, 'utf8');\n\t} catch (err) {\n\t\tthrow new InvalidArgumentError(`Failed to write template to ${outPath}: ${(err as NodeJS.ErrnoException).message}`);\n\t}\n\n\tprocess.stdout.write(`Wrote template to ${outPath}\\n`);\n}\n\nexport function registerPersonalResumesTemplate(parent: Command): void {\n\tparent\n\t\t.command('template')\n\t\t.description('Print (or write) a resume aggregate skeleton with example values for every field (offline, no login required)')\n\t\t.option('--out <file>', 'write the template to this file instead of stdout (fails if the file already exists)')\n\t\t.action((flags: TemplateFlags) => {\n\t\t\trunResumesTemplate(flags.out);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { dim, printJson, printTable } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\nimport { PERSONAL_RESUMES_BASE, type ResumeListData, type ResumeQuota } from '@wport/core';\n\ninterface ListFlags {\n\tfields?: string;\n\tminimal?: boolean;\n}\n\n/** 同 design doc §3.4「list 預設欄位」,也是表格顯示的欄位集合(is_disabled/disable_reason 不列入)。 */\nconst MINIMAL_LIST_FIELDS = ['enc_id', 'name', 'updated_at', 'is_complete', 'is_published'];\n\n/** 格式化沿 enterprise/keys/list.ts 的 formatDate 模式(每個 list 命令各自複製一份,檔案小而聚焦)。 */\nfunction formatDate(value: string | null | undefined): string {\n\treturn value ? String(value).slice(0, 10) : '';\n}\n\n/** 表格尾行摘要:`{used}/{max} used, can create.` 或 `..., cannot create (reason_code).`(無 reason_code 則省略括號)。 */\nfunction formatQuotaLine(quota: ResumeQuota): string {\n\tconst status = quota.can_create ? 'can create' : `cannot create${quota.reason_code ? ` (${quota.reason_code})` : ''}`;\n\treturn `${quota.used}/${quota.max_resumes} used, ${status}.`;\n}\n\n/**\n * `resumes list` 共用抓取邏輯:GET /api/v1/personal/resumes → unwrap `{resumes, quota}`。\n * `resumes export`(export.ts)也需要先拿到全部 enc_id 才能逐份 view,故抽成獨立函式共用,\n * 而不是各自重複一份 personalGet + unwrapDataResponse。\n */\nexport async function fetchResumeList(opts: PersonalRequestOptions): Promise<ResumeListData> {\n\tconst { body } = await personalGet(opts, PERSONAL_RESUMES_BASE);\n\treturn unwrapDataResponse<ResumeListData>(body);\n}\n\n/**\n * `resumes list` 核心。data 為 `{resumes, quota}`(非陣列,R1 端點形狀),故不能用\n * `unwrapDataArray`——quota 是 Agent 判斷能否 create 的關鍵資訊,一律隨 resumes 一起印出\n * (--fields/--minimal 只投影 resumes 陣列,quota 原樣保留,DR-10)。\n */\nexport async function runResumesList(ctx: ResolvedContext, flags: ListFlags): 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 opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst data = await fetchResumeList(opts);\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 ? { ...data, resumes: data.resumes.map((r) => pickPaths(r, projection)) } : data);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tdata.resumes,\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: 'UPDATED', value: (r) => formatDate(r.updated_at), maxWidth: 12 },\n\t\t\t{ header: 'COMPLETE', value: (r) => (r.is_complete ? 'yes' : 'no') },\n\t\t\t{ header: 'PUBLISHED', value: (r) => (r.is_published ? 'yes' : 'no') },\n\t\t],\n\t\tctx.color\n\t);\n\tprocess.stdout.write(dim(formatQuotaLine(data.quota), ctx.color) + '\\n');\n}\n\nexport function registerPersonalResumesList(parent: Command): void {\n\tparent\n\t\t.command('list')\n\t\t.description('List your personal resumes')\n\t\t.option('--fields <list>', 'output selected fields as JSON (comma-separated dotted paths, applied to each resume)')\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\tawait runResumesList(resolveContext(command), flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { formatDate, formatQuotaLine, MINIMAL_LIST_FIELDS };\n","import type { Command } from 'commander';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalGet, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, printTable } from '../../../lib/output';\nimport { parseFieldsList, pickPaths } from '../../../lib/path-utils';\nimport { PERSONAL_RESUMES_BASE, type AggregateResume } from '@wport/core';\nimport { SECTIONS, type SectionDef } from '@wport/core';\n\ninterface ViewFlags {\n\tfields?: string;\n}\n\n/**\n * `resumes view` / `resumes export` 共用抓取邏輯:GET /resumes/{enc_id} → unwrap 成\n * `AggregateResume`。export.ts 逐份呼叫這支函式,不重複一份 personalGet + unwrap。\n * enc_id 走 encodeURIComponent(同 talents/view.ts 慣例),避免內含特殊字元破壞路徑。\n */\nexport async function fetchResumeAggregate(opts: PersonalRequestOptions, encId: string): Promise<AggregateResume> {\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 personalGet(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`);\n\treturn unwrapDataResponse<AggregateResume>(body);\n}\n\ninterface SectionSummaryRow {\n\tsection: string;\n\tsummary: string;\n}\n\n/**\n * table 模式不印整份巢狀 JSON(太長、不利終端機閱讀,聚合完整內容一律走 `--output json`\n * 或 `--fields`,同 plan Task 13 規格「json 為主用途」)。改印每節摘要:\n * - array 節(education/certificate/language/portfolio_links)→ 筆數\n * - wrapper 節(work_experience)→ `has_no_work_experience` 為 true 時印固定文字,否則印筆數\n * - object / scalar 節(professional_skills/job_condition/background/autobiography)→\n * present/absent(object 節額外檢查 `Object.keys().length`,避免 `{}` 這種「技術上非 null\n * 但語意上没資料」的殼被誤判成 present)\n * 節的走訪順序沿用 SECTIONS(resume-schema/definition.ts 這份 SoT),避免這裡另建一份節清單、\n * 未來新增/調整節時兩處要同步改。\n */\nfunction summarizeSections(resume: AggregateResume): SectionSummaryRow[] {\n\tconst rows: SectionSummaryRow[] = [{ section: 'name', summary: resume.name }];\n\tfor (const def of SECTIONS) {\n\t\trows.push({ section: def.key, summary: summarizeSection(def, resume) });\n\t}\n\treturn rows;\n}\n\nfunction summarizeSection(def: SectionDef, resume: AggregateResume): string {\n\tconst value = (resume as unknown as Record<string, unknown>)[def.key];\n\tif (def.kind === 'array') {\n\t\treturn `${Array.isArray(value) ? value.length : 0} item(s)`;\n\t}\n\tif (def.kind === 'wrapper') {\n\t\tconst wrapper = value as { has_no_work_experience?: boolean; items?: unknown[] } | null | undefined;\n\t\tif (wrapper?.has_no_work_experience) return 'no work experience';\n\t\treturn `${Array.isArray(wrapper?.items) ? (wrapper.items as unknown[]).length : 0} item(s)`;\n\t}\n\tif (def.kind === 'scalar') {\n\t\treturn typeof value === 'string' && value.length > 0 ? 'present' : 'absent';\n\t}\n\t// def.kind === 'object'\n\treturn value && typeof value === 'object' && Object.keys(value as object).length > 0 ? 'present' : 'absent';\n}\n\n/**\n * `resumes view` 核心:GET /resumes/{enc_id} → 一次回全節的 AggregateResume(R2)。\n * `--fields` 優先於 `--output`(同 company/jobs/campaigns view 慣例:投影一律印 JSON,\n * 不管 --output table/json),因為投影結果本就是任意巢狀路徑挑出的欄位,表格無從呈現。\n */\nexport async function runResumesView(ctx: ResolvedContext, encId: string, flags: ViewFlags): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst resume = await fetchResumeAggregate(opts, encId);\n\n\tif (flags.fields) {\n\t\tprintJson(pickPaths(resume, parseFieldsList(flags.fields)));\n\t\treturn;\n\t}\n\tif (ctx.format === 'json') {\n\t\tprintJson(resume);\n\t\treturn;\n\t}\n\n\tprintTable(\n\t\tsummarizeSections(resume),\n\t\t[\n\t\t\t{ header: 'SECTION', value: (r) => r.section, maxWidth: 20 },\n\t\t\t{ header: 'SUMMARY', value: (r) => r.summary, maxWidth: 40 },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerPersonalResumesView(parent: Command): void {\n\tparent\n\t\t.command('view <enc_id>')\n\t\t.description('View one resume aggregate (all sections)')\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\tawait runResumesView(resolveContext(command), encId, flags);\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { summarizeSections };\n","import type { Command } from 'commander';\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { fetchResumeList } from './list';\nimport { fetchResumeAggregate } from './view';\nimport type { PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\n\ninterface ExportFlags {\n\tout?: string;\n}\n\n/**\n * server 回傳的 `enc_id` 正常只含英數字/底線/連字號,理論上不該有路徑分隔符。防禦性擋掉含\n * `/` `\\` 的值——沒有這層檢查,`resume-${encId}.json` 這種字串拼接一旦 encId 帶\n * `../../../../tmp/evil` 之類的內容,`path.join(outDir, filename)` 會把 `..` 段正規化掉、\n * 讓寫入路徑跳脫 outDir(已用 node -e 實測驗證過,不是紙上談兵)。伺服器被入侵或契約跑掉時\n * 這是最後一道防線,不依賴信任 server 回應內容。\n */\nfunction safeResumeFilename(encId: string): string {\n\tif (encId.includes('/') || encId.includes('\\\\') || encId === '.' || encId === '..') {\n\t\tthrow new CliError(\n\t\t\t`Unexpected enc_id from server: \"${encId}\" (contains path separators; refusing to write outside --out)`,\n\t\t\tExitCode.ServerOrNetworkError\n\t\t);\n\t}\n\treturn `resume-${encId}.json`;\n}\n\n/**\n * `resumes export` 核心:list 找出全部 enc_id → 逐份 view 抓聚合 → 各寫一檔\n * `resume-<enc_id>.json`。配額上限 ≤6 份(quota.max_resumes),循序拉取即可,\n * 不需要 `lib/concurrency.ts` 的併發控制;單節失敗語義(create/update 編排的 partial\n * success)在這裡不適用——view 本身不是分節寫入,中途失敗直接讓錯誤上拋(不寫殘缺檔)。\n *\n * `outDir` 要求呼叫端已決議好預設值(`--out` 未帶時的 `process.cwd()` 由\n * `registerPersonalResumesExport` 的 action 處理)——不把 `process.cwd()` 塞進這支函式的\n * 預設參數,方便測試永遠帶明確路徑、不必 mock 全域 `process.cwd`。\n */\nexport async function runResumesExport(ctx: ResolvedContext, outDir: string): Promise<void> {\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst { resumes } = await fetchResumeList(opts);\n\n\tmkdirSync(outDir, { recursive: true });\n\n\tfor (const item of resumes) {\n\t\t// 檔名安全檢查放在發請求「之前」:enc_id 若真的帶跳脫路徑,寧可不浪費這次 view 請求就先擋下來。\n\t\tconst filePath = join(outDir, safeResumeFilename(item.enc_id));\n\t\tconst aggregate = await fetchResumeAggregate(opts, item.enc_id);\n\t\twriteFileSync(filePath, `${JSON.stringify(aggregate, null, 2)}\\n`, 'utf8');\n\t}\n\n\tprocess.stdout.write(`Exported ${resumes.length} resume(s) to ${outDir}.\\n`);\n}\n\nexport function registerPersonalResumesExport(parent: Command): void {\n\tparent\n\t\t.command('export')\n\t\t.description('Export all your resumes as one JSON file per resume')\n\t\t.option('--out <dir>', 'destination directory (default: current directory)')\n\t\t.action(async (flags: ExportFlags, command: Command) => {\n\t\t\tawait runResumesExport(resolveContext(command), flags.out ?? process.cwd());\n\t\t});\n}\n\n// Internal helpers exposed for tests. Not part of the public CLI API.\nexport const __test__ = { safeResumeFilename };\n","import type { Command } from 'commander';\nimport { validateAggregate } from '@wport/core';\nimport { createAggregate, type OrchestrationResult } from '../../../lib/resume-orchestrator';\nimport type { PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, printTable, sanitizeForTerminal } from '../../../lib/output';\n\ninterface CreateFlags {\n\tfile: string;\n}\n\n/**\n * `resumes create` 核心:本地 `validateAggregate` 先驗(有 issue → exit 2,不發任何請求,\n * 訊息呈現同 `resumes validate` 慣例)→ 通過才進 `createAggregate` 編排(建殼 → rename →\n * 依序逐節寫入)。結果先印出(json/table),再視 `allOk` 決定是否丟 CliError 觸發非 0 exit ——\n * 同 `jobs batch` 慣例:腳本先讀到完整結果,才看到非 0 exit code。\n */\nexport async function runResumesCreate(ctx: ResolvedContext, filePath: string): Promise<void> {\n\tconst input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });\n\tconst issues = validateAggregate(input);\n\tif (issues.length > 0) {\n\t\tif (ctx.format === 'json') {\n\t\t\tprintJson({ valid: false, issues });\n\t\t} else {\n\t\t\tfor (const issue of issues) {\n\t\t\t\tprocess.stderr.write(`${sanitizeForTerminal(issue.path)}: ${sanitizeForTerminal(issue.message)}\\n`);\n\t\t\t}\n\t\t}\n\t\tthrow new CliError(`${issues.length} validation issue(s) found — fix locally before creating (no request sent)`, ExitCode.InvalidArgument);\n\t}\n\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst result = await createAggregate(opts, input);\n\tprintOrchestrationResult(ctx, result);\n\n\tif (!result.allOk) {\n\t\tconst failed = result.reports.filter((r) => !r.ok).map((r) => r.section);\n\t\tthrow new CliError(\n\t\t\t`Resume ${result.enc_id} was created but ${failed.length} section(s) failed to write: ${failed.join(', ')}. ` +\n\t\t\t\t`Run \\`wport personal resumes update ${result.enc_id} --section <name> --file <file>\\` to retry the failed section(s).`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n}\n\n/**\n * create/update 共用的結果呈現:json 印 `{enc_id, reports, all_ok}`;table 先印 enc_id 前綴行\n * (create 情境下這是使用者唯一得知新 enc_id 的地方),再每節一行 ✓/✗ + error。update.ts 各自\n * 複製一份(檔案小而聚焦,同 list.ts formatDate 慣例),不額外拉一個共用模組。\n */\nfunction printOrchestrationResult(ctx: ResolvedContext, result: OrchestrationResult): void {\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: result.enc_id, reports: result.reports, all_ok: result.allOk });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Resume: ${sanitizeForTerminal(result.enc_id ?? '')}\\n`);\n\tprintTable(\n\t\tresult.reports,\n\t\t[\n\t\t\t{ header: 'SECTION', value: (r) => r.section, maxWidth: 20 },\n\t\t\t{ header: 'STATUS', value: (r) => (r.ok ? '✓' : '✗') },\n\t\t\t{ header: 'ERROR', value: (r) => r.error ?? '', maxWidth: 60 },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerPersonalResumesCreate(parent: Command): void {\n\tparent\n\t\t.command('create')\n\t\t.description('Create a resume from a JSON aggregate file (validates locally first; use \"-\" for stdin)')\n\t\t.requiredOption('--file <path>', 'path to a JSON resume aggregate, or \"-\" for stdin')\n\t\t.action(async (flags: CreateFlags, command: Command) => {\n\t\t\tawait runResumesCreate(resolveContext(command), flags.file);\n\t\t});\n}\n","/**\n * `resumes create` / `resumes update` 的聚合編排核心(US-1 核心,design doc §3.4/plan Task 14)。\n * 把一份聚合 JSON 拆成 spec B 對應的多個分節寫入請求,依 `SECTION_WRITE_PLAN`\n *(personal-types.ts)派工,逐節記錄成敗、**不回滾**——單節失敗不影響其他節繼續寫入,已成功\n * 寫入的節與(create 情境下)已建的殼都保留,讓使用者用 `update --section <失敗節>` 補寫,\n * 不必整份重來。\n *\n * create 專屬的「建殼」前置步驟例外:失敗代表根本沒有 enc_id 可續寫任何節,屬全有全無的\n * 中止點,直接 throw(不落入分節失敗的 report 陣列)。rename(②b)與其後的分節寫入則一律\n * 走「失敗記報告、繼續下一步」的一般路徑。\n *\n * 本地 `validateAggregate` 前置驗證是呼叫端(create.ts 命令層)的責任,這支檔案不重複驗證,\n * 維持「本地驗證=快速失敗、編排=發請求」的分工(update 不做本地整份驗證,見 updateAggregate\n * 註解)。\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalPost, personalPut, type PersonalRequestOptions } from './personal-client';\nimport { CliError, ExitCode, ServerClientHttpError } from './errors';\nimport { PERSONAL_RESUMES_BASE, SECTION_WRITE_PLAN, type UpdateResumeNameDto } from '@wport/core';\nimport { SECTIONS, getSection, type AggregateSectionKey } from '@wport/core';\n\nexport interface SectionReport {\n\tsection: string;\n\tok: boolean;\n\terror?: string;\n}\n\nexport interface OrchestrationResult {\n\tenc_id: string | null;\n\treports: SectionReport[];\n\tallOk: boolean;\n}\n\ntype WriteMode = 'create' | 'update';\n\n/** 寫入端點(POST/PUT/PATCH/DELETE 全部)必帶 Idempotency-Key(design doc §5.1);每次呼叫各自一把。 */\nfunction freshIdempotencyKey(): { idempotencyKey: string } {\n\treturn { idempotencyKey: randomUUID() };\n}\n\n/** CliError 與 core 錯誤(ServerClientHttpError/NetworkError,即 core 的 WportHttpError/WportNetworkError alias)皆已是可讀訊息;其餘走 String() 兜底。 */\nfunction describeError(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n\n/** 錯誤 body 的機器可判讀 `error_code`(design doc §5.1:CLI 判斷 key `error_code` 優先於 `statusCode`)。 */\nfunction extractErrorCode(body: unknown): string | null {\n\tif (body && typeof body === 'object' && typeof (body as Record<string, unknown>).error_code === 'string') {\n\t\treturn (body as Record<string, unknown>).error_code as string;\n\t}\n\treturn null;\n}\n\n/**\n * 建履歷殼(R3:`POST /api/v1/personal/resumes`,空 body,D8;photo_url 唯讀本就不入殼)。\n * 與其餘寫入步驟不同:失敗代表整個 create 無以為繼(沒有 enc_id 可續寫任何節),直接中止並\n * 依 `error_code` 給引導訊息(BR-002 `profile_incomplete` / BR-010 `resume_limit_reached`),\n * 不落入「記 report 繼續」的一般分節失敗路徑;其他 4xx/5xx 原樣上拋(已經是可讀的 core 錯誤 WportHttpError/WportError)。\n */\nasync function createShell(opts: PersonalRequestOptions): Promise<string> {\n\ttry {\n\t\tconst { body } = await personalPost(opts, PERSONAL_RESUMES_BASE, {}, freshIdempotencyKey());\n\t\treturn unwrapDataResponse<{ enc_id: string; name: string }>(body).enc_id;\n\t} catch (err) {\n\t\tif (err instanceof ServerClientHttpError) {\n\t\t\tconst code = extractErrorCode(err.body);\n\t\t\tif (code === 'profile_incomplete') {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t'Cannot create resume: your member profile is incomplete. Complete the required profile fields first, then retry (server: profile_incomplete).',\n\t\t\t\t\tExitCode.ServerClientError\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (code === 'resume_limit_reached') {\n\t\t\t\tthrow new CliError(\n\t\t\t\t\t'Cannot create resume: you have reached your resume limit. Delete an existing resume first, then retry (server: resume_limit_reached).',\n\t\t\t\t\tExitCode.ServerClientError\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tthrow err;\n\t}\n}\n\n/** `name` 型別收窄為 string:呼叫端已用 `aggregate.name !== undefined` 排除 undefined,其餘型別不符交給 server 422 把關(update.ts 註解),這裡不重複加 runtime 檢查。 */\nasync function renameResume(opts: PersonalRequestOptions, encId: string, name: string): Promise<void> {\n\tawait personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}/name`, { name } satisfies UpdateResumeNameDto, freshIdempotencyKey());\n}\n\n/** work_experience item 的 is_current:聚合格式讀 boolean,寫入 DTO 收 0/1(讀寫不對稱,design doc §3.4)。 */\nfunction toWorkExperienceWriteItem(item: Record<string, unknown>): Record<string, unknown> {\n\tconst { is_current, ...rest } = item;\n\treturn { ...rest, is_current: is_current ? 1 : 0 };\n}\n\n/**\n * 從 item 取出 R2 聚合讀取帶回的 `enc_id`(round-trip 用),回傳 [有效 enc_id | null, 剝除後的\n * body]。空白字串視同缺席;create 模式呼叫端直接丟棄 enc_id(新履歷不可能有既有 item id,\n * view 複製來的聚合直接 create 也能過),update 模式據此分派 PUT(更新既有)/ POST(新增)。\n */\nfunction splitItemEncId(item: Record<string, unknown>): [string | null, Record<string, unknown>] {\n\tconst { enc_id, ...rest } = item;\n\tconst valid = typeof enc_id === 'string' && enc_id.trim() ? enc_id.trim() : null;\n\treturn [valid, rest];\n}\n\n/**\n * 依 `SECTION_WRITE_PLAN` 分派單節寫入。`mode` 的作用:\n * - `single-post`:create 用 POST(新建),update 用 PUT(單物件節整份覆蓋,無 delete)\n * - `per-item-post` / `work-experience` 的 items:update 模式下 item 帶 `enc_id`(view round-trip)\n * → 走 item 級 PUT 更新既有;無 `enc_id` → POST 新增(plan Task 14「帶 enc_id 走 PUT」)。\n * create 模式一律剝除 enc_id 後 POST。**不做宣告式刪除**——不在輸入內的既有 item 不會被刪\n * (portfolio_links 的 bulk-put 例外,該端點本身是宣告式)。\n * - `bulk-put`:item 的 `enc_id` 對映為後端 DTO 的 `encId`(update 保留既有連結;create 一律 null)\n *\n * 刻意不對 `value` 做結構性防呆(是否為陣列/物件):呼叫端已篩掉 `undefined`,其餘型別錯誤\n * 讓 JS 原生地丟出(例如對非陣列 `for...of`),統一由 `attemptStep` 接住記成該節失敗 ——\n * 不重複 validate.ts 已有的型別檢查邏輯。\n */\nasync function writeSection(\n\topts: PersonalRequestOptions,\n\tencId: string,\n\tkey: AggregateSectionKey,\n\tvalue: unknown,\n\tmode: WriteMode\n): Promise<void> {\n\tconst plan = SECTION_WRITE_PLAN[key];\n\tconst base = `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(encId)}`;\n\n\tswitch (plan.kind) {\n\t\tcase 'per-item-post': {\n\t\t\tfor (const item of value as Record<string, unknown>[]) {\n\t\t\t\tconst [itemEncId, body] = splitItemEncId(item);\n\t\t\t\tif (mode === 'update' && itemEncId) {\n\t\t\t\t\tawait personalPut(opts, `${base}${plan.path}/${encodeURIComponent(itemEncId)}`, body, freshIdempotencyKey());\n\t\t\t\t} else {\n\t\t\t\t\tawait personalPost(opts, `${base}${plan.path}`, body, freshIdempotencyKey());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcase 'work-experience': {\n\t\t\tconst wrapper = value as { has_no_work_experience?: boolean; items: Record<string, unknown>[] };\n\t\t\tif (wrapper.has_no_work_experience) {\n\t\t\t\tawait personalPost(opts, `${base}${plan.path}/no-work-experience`, {}, freshIdempotencyKey());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (const item of wrapper.items) {\n\t\t\t\tconst [itemEncId, rest] = splitItemEncId(item);\n\t\t\t\tconst body = toWorkExperienceWriteItem(rest);\n\t\t\t\tif (mode === 'update' && itemEncId) {\n\t\t\t\t\t// 既有 web DTO 慣例:work-experience 的 PUT 以 body 內 `encId`(camelCase 例外)定位單筆\n\t\t\t\t\tawait personalPut(opts, `${base}${plan.path}`, { encId: itemEncId, ...body }, freshIdempotencyKey());\n\t\t\t\t} else {\n\t\t\t\t\tawait personalPost(opts, `${base}${plan.path}`, body, freshIdempotencyKey());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tcase 'single-post': {\n\t\t\tconst body = key === 'autobiography' ? { autobiography: value } : (value as Record<string, unknown>);\n\t\t\tconst write = mode === 'create' ? personalPost : personalPut;\n\t\t\tawait write(opts, `${base}${plan.path}`, body, freshIdempotencyKey());\n\t\t\treturn;\n\t\t}\n\t\tcase 'bulk-put': {\n\t\t\tconst items = value as Record<string, unknown>[];\n\t\t\tawait personalPut(\n\t\t\t\topts,\n\t\t\t\t`${base}${plan.path}`,\n\t\t\t\t{\n\t\t\t\t\tportfolio_links: items.map((item) => {\n\t\t\t\t\t\tconst [itemEncId, rest] = splitItemEncId(item);\n\t\t\t\t\t\treturn { encId: mode === 'update' ? itemEncId : null, ...rest };\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\tfreshIdempotencyKey()\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/** 單一步驟(rename 或某一節)成敗記錄成一筆 SectionReport;失敗不拋出,讓呼叫端繼續下一步(不回滾)。 */\nasync function attemptStep(section: string, run: () => Promise<void>): Promise<SectionReport> {\n\ttry {\n\t\tawait run();\n\t\treturn { section, ok: true };\n\t} catch (err) {\n\t\treturn { section, ok: false, error: describeError(err) };\n\t}\n}\n\n/**\n * `create --file` 編排核心:\n * ① 建殼(R3,失敗中止,見 createShell)\n * ② 聚合有 `name` → rename(D8:後端建殼不收 name,由 CLI 補;失敗記報告、不中止)\n * ③ 依 SECTIONS 固定順序,僅寫聚合檔內出現的節(單物件節用 POST)\n */\nexport async function createAggregate(opts: PersonalRequestOptions, aggregate: Record<string, unknown>): Promise<OrchestrationResult> {\n\tconst encId = await createShell(opts);\n\tconst reports: SectionReport[] = [];\n\n\tif (aggregate.name !== undefined) {\n\t\treports.push(await attemptStep('name', () => renameResume(opts, encId, aggregate.name as string)));\n\t}\n\n\tfor (const section of SECTIONS) {\n\t\tconst value = aggregate[section.key];\n\t\tif (value === undefined) continue;\n\t\treports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, 'create')));\n\t}\n\n\treturn { enc_id: encId, reports, allOk: reports.every((r) => r.ok) };\n}\n\n/**\n * `update <enc_id> --file [--section]` 編排核心。resume 已存在(無建殼步)。\n *\n * 頂層 `name`:整份模式(無 `onlySection`)下若聚合檔帶 `name` → 比照 create ②b 走 R4 rename\n * (記入 reports section='name')——否則 view → 改名 → update 的改動會被靜默丟棄,且 CLI 將完全\n * 無法改名(final review low finding,2026-07-30 補)。`--section` 模式維持只寫指定節,`name`\n * 不是節、不在 `--section` 值域。\n *\n * `onlySection` 給定 → 只寫該節:未知節名或該節在聚合檔內缺席都是本地能擋的輸入錯誤,直接\n * CliError exit 2(不發請求)——未知節名訊息列出合法節名,mirror to-json-schema.ts 的\n * `buildAggregateJsonSchema` 慣例。未給 `onlySection` → 出現的節全寫,語意同 create③。\n *\n * 不做本地整份 `validateAggregate`:`--section` 模式下輸入檔可能只含單節內容,套用整份聚合\n * 驗證器(要求頂層 `name` 必填)會誤判;交給 server 422 把關,partial success 記入逐節報告。\n */\nexport async function updateAggregate(\n\topts: PersonalRequestOptions,\n\tencId: string,\n\taggregate: Record<string, unknown>,\n\tonlySection?: string\n): Promise<OrchestrationResult> {\n\tconst reports: SectionReport[] = [];\n\n\tif (onlySection !== undefined) {\n\t\tconst section = getSection(onlySection);\n\t\tif (!section) {\n\t\t\tconst allowed = SECTIONS.map((s) => s.key).join(', ');\n\t\t\tthrow new CliError(`Unknown section \"${onlySection}\". Allowed: ${allowed}`, ExitCode.InvalidArgument);\n\t\t}\n\t\tconst value = aggregate[onlySection];\n\t\tif (value === undefined) {\n\t\t\tthrow new CliError(`Section \"${onlySection}\" has no data in the input file`, ExitCode.InvalidArgument);\n\t\t}\n\t\treports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, 'update')));\n\t\treturn { enc_id: encId, reports, allOk: reports.every((r) => r.ok) };\n\t}\n\n\tif (aggregate.name !== undefined) {\n\t\treports.push(await attemptStep('name', () => renameResume(opts, encId, aggregate.name as string)));\n\t}\n\n\tfor (const section of SECTIONS) {\n\t\tconst value = aggregate[section.key];\n\t\tif (value === undefined) continue;\n\t\treports.push(await attemptStep(section.key, () => writeSection(opts, encId, section.key, value, 'update')));\n\t}\n\n\treturn { enc_id: encId, reports, allOk: reports.every((r) => r.ok) };\n}\n","import type { Command } from 'commander';\nimport { updateAggregate, type OrchestrationResult } from '../../../lib/resume-orchestrator';\nimport type { PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { readJsonObject } from '../../../lib/io-helpers';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, printTable, sanitizeForTerminal } from '../../../lib/output';\n\ninterface UpdateFlags {\n\tfile: string;\n\tsection?: string;\n}\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/**\n * `resumes update` 核心:無本地整份 `validateAggregate` 前置驗證 —— `--section` 模式下輸入\n * 檔可能只含單節內容,套用整份聚合驗證器(要求頂層 `name` 必填)會誤判;交給 server 422\n * 把關,partial success 記入逐節報告(見 resume-orchestrator.ts 的 updateAggregate 註解)。\n * 結果先印出(json/table),再視 `allOk` 決定是否丟 CliError 觸發非 0 exit(同 create.ts/\n * `jobs batch` 慣例)。\n */\nexport async function runResumesUpdate(\n\tctx: ResolvedContext,\n\tencId: string,\n\tfilePath: string,\n\tflags: UpdateFlags\n): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst input = readJsonObject(filePath, { timeoutMs: ctx.timeoutMs });\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst result = await updateAggregate(opts, trimmed, input, flags.section);\n\tprintOrchestrationResult(ctx, result);\n\n\tif (!result.allOk) {\n\t\tconst failed = result.reports.filter((r) => !r.ok).map((r) => r.section);\n\t\tthrow new CliError(\n\t\t\t`Resume ${result.enc_id} was partially updated — ${failed.length} section(s) failed: ${failed.join(', ')}. ` +\n\t\t\t\t`Fix the input and retry with --section <name>.`,\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n}\n\n/**\n * create/update 共用的結果呈現,各自複製一份(檔案小而聚焦,同 list.ts formatDate 慣例):\n * json 印 `{enc_id, reports, all_ok}`;table 印 enc_id 前綴行 + 每節一行 ✓/✗ + error。\n */\nfunction printOrchestrationResult(ctx: ResolvedContext, result: OrchestrationResult): void {\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: result.enc_id, reports: result.reports, all_ok: result.allOk });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Resume: ${sanitizeForTerminal(result.enc_id ?? '')}\\n`);\n\tprintTable(\n\t\tresult.reports,\n\t\t[\n\t\t\t{ header: 'SECTION', value: (r) => r.section, maxWidth: 20 },\n\t\t\t{ header: 'STATUS', value: (r) => (r.ok ? '✓' : '✗') },\n\t\t\t{ header: 'ERROR', value: (r) => r.error ?? '', maxWidth: 60 },\n\t\t],\n\t\tctx.color\n\t);\n}\n\nexport function registerPersonalResumesUpdate(parent: Command): void {\n\tparent\n\t\t.command('update <enc_id>')\n\t\t.description('Update a resume aggregate (whole file, or one section with --section)')\n\t\t.requiredOption('--file <path>', 'path to a JSON resume aggregate (or single-section content), or \"-\" for stdin')\n\t\t.option('--section <name>', 'only write this section (e.g. education, work_experience)')\n\t\t.action(async (encId: string, flags: UpdateFlags, command: Command) => {\n\t\t\tawait runResumesUpdate(resolveContext(command), encId, flags.file, flags);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalPost, personalPut, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_RESUMES_BASE, type UpdateResumeNameDto } from '@wport/core';\n\ninterface CopyFlags {\n\tname?: string;\n}\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\nfunction printCopyResult(ctx: ResolvedContext, sourceEncId: string, newEncId: string): void {\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: newEncId });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Copied resume ${sanitizeForTerminal(sourceEncId)} → new resume: ${sanitizeForTerminal(newEncId)}\\n`);\n}\n\n/**\n * `resumes copy` 核心(R5,design doc §5.2):`POST /resumes/{enc_id}/duplicate`(空 body,D8)\n * → 201 `{enc_id}`(server 命名「原名 - 複製」,不回 name)。422 `resume_limit_reached` 不特別\n * 攔截改寫訊息——`personalPost` 對非 2xx 已轉成 `ServerClientHttpError`(exit 3),原樣透傳即是\n * plan Task 15 規格要求的「透傳」,與 create 編排 `createShell` 的專屬引導訊息刻意不同。\n *\n * `--name` 給定 → 複製成功後續發 `PUT /resumes/{新enc_id}/name`(D8 兩步編排,同\n * resume-orchestrator.ts `createAggregate` 的 rename 步驟)。複製本身已成功時就先印出結果\n * (新 enc_id 上到 stdout,script 可讀);rename 失敗不影響已存在的複製——新履歷還在,只是留著\n * 伺服器預設命名,故不回滾、只讓錯誤帶著新 enc_id 上拋(exit 3),不再另外印一次 Warning 重複\n * 同一件事(結果已經在 stdout,錯誤訊息只留在 stderr 這一個管道)。\n */\nexport async function runResumesCopy(ctx: ResolvedContext, encId: string, name: string | undefined): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\n\tconst { body } = await personalPost(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/duplicate`, {}, { idempotencyKey: randomUUID() });\n\tconst { enc_id: newEncId } = unwrapDataResponse<{ enc_id: string }>(body);\n\n\tprintCopyResult(ctx, trimmed, newEncId);\n\n\tif (name === undefined) return;\n\n\ttry {\n\t\tawait personalPut(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(newEncId)}/name`, { name } satisfies UpdateResumeNameDto, { idempotencyKey: randomUUID() });\n\t} catch (err) {\n\t\tconst reason = err instanceof Error ? err.message : String(err);\n\t\tthrow new CliError(\n\t\t\t`Resume ${trimmed} was copied to ${newEncId}, but renaming it to \"${name}\" failed: ${reason}. ` +\n\t\t\t\t'The copy exists under its server-assigned default name — this CLI has no standalone rename command in v1.',\n\t\t\tExitCode.ServerClientError\n\t\t);\n\t}\n}\n\nexport function registerPersonalResumesCopy(parent: Command): void {\n\tparent\n\t\t.command('copy <enc_id>')\n\t\t.description('Duplicate a resume (counts toward your resume limit)')\n\t\t.option('--name <name>', 'rename the new copy after duplicating')\n\t\t.action(async (encId: string, flags: CopyFlags, command: Command) => {\n\t\t\tawait runResumesCopy(resolveContext(command), encId, flags.name);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { unwrapDataResponse } from '@wport/core';\nimport { personalPatch, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_RESUMES_BASE, type PersonalUpdatePublishedStatusDto } from '@wport/core';\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/**\n * `resumes publish` / `resumes unpublish` 共用(R6,design doc §5.2):\n * `PATCH /resumes/{enc_id}/published-status` body `{target_status: boolean}`(spec B 已裁必填,\n * 非 toggle)→ `{is_published}`。可逆操作,不需 `--confirm`(同 campaigns publish/unpublish 慣例,\n * 有別於 resumes delete)。\n *\n * 輸出用伺服器回傳的 `is_published`,不直接回顯呼叫端要求的 `target_status`——避免伺服器實際\n * 狀態與請求不一致時(理論上不該發生,但別預設請求即結果)誤導使用者。\n */\nexport async function runResumesPublishTransition(ctx: ResolvedContext, encId: string, action: 'publish' | 'unpublish'): Promise<void> {\n\tconst trimmed = requireEncId(encId);\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst targetStatus = action === 'publish';\n\n\tconst { body } = await personalPatch(\n\t\topts,\n\t\t`${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}/published-status`,\n\t\t{ target_status: targetStatus } satisfies PersonalUpdatePublishedStatusDto,\n\t\t{ idempotencyKey: randomUUID() }\n\t);\n\tconst result = unwrapDataResponse<{ is_published: boolean }>(body);\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: trimmed, is_published: result.is_published });\n\t\treturn;\n\t}\n\tconst verb = action === 'publish' ? 'Published' : 'Unpublished';\n\tprocess.stdout.write(`${verb} resume: ${sanitizeForTerminal(trimmed)}\\n`);\n}\n\nexport function registerPersonalResumesPublish(parent: Command): void {\n\tparent\n\t\t.command('publish <enc_id>')\n\t\t.description('Publish a resume (make it visible to employers)')\n\t\t.action(async (encId: string, _flags: Record<string, never>, command: Command) => {\n\t\t\tawait runResumesPublishTransition(resolveContext(command), encId, 'publish');\n\t\t});\n}\n\nexport function registerPersonalResumesUnpublish(parent: Command): void {\n\tparent\n\t\t.command('unpublish <enc_id>')\n\t\t.description('Unpublish a resume (hide it from employers)')\n\t\t.action(async (encId: string, _flags: Record<string, never>, command: Command) => {\n\t\t\tawait runResumesPublishTransition(resolveContext(command), encId, 'unpublish');\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { personalDelete, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_RESUMES_BASE } from '@wport/core';\n\ninterface DeleteFlags {\n\tconfirm?: boolean;\n}\n\n/** 同 view.ts 慣例:檔案小而聚焦,各檔各自複製一份,不額外拉一個共用模組。 */\nfunction requireEncId(encId: string): string {\n\tconst trimmed = encId.trim();\n\tif (!trimmed) {\n\t\tthrow new CliError('enc_id must not be empty', ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/**\n * `resumes delete` 核心(R7,design doc §5.2):破壞性、不可逆——無 `--confirm` 一律本地\n * exit 2、**不發請求**(同 jobs delete 慣例)。confirm 檢查先於 enc_id 驗證(同 jobs delete\n * 的 `runJobsDelete` 順序)。\n *\n * DELETE 成功後端回 `DataResponse(null)`(軟刪,`data` 為 null)——不 unwrap/deref(否則對 null\n * 取屬性會丟原生 TypeError,同 jobs/lifecycle.ts 的 F-001 教訓);用呼叫時已驗證過的 enc_id\n * 回報刪除結果,不依賴回應 body。\n */\nexport async function runResumesDelete(ctx: ResolvedContext, encId: string, confirm: boolean): 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\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\n\tawait personalDelete(opts, `${PERSONAL_RESUMES_BASE}/${encodeURIComponent(trimmed)}`, { idempotencyKey: randomUUID() });\n\n\tif (ctx.format === 'json') {\n\t\tprintJson({ enc_id: trimmed, deleted: true });\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Deleted resume: ${sanitizeForTerminal(trimmed)}\\n`);\n}\n\nexport function registerPersonalResumesDelete(parent: Command): void {\n\tparent\n\t\t.command('delete <enc_id>')\n\t\t.description('Delete a resume (destructive; requires --confirm)')\n\t\t.option('--confirm', 'confirm this destructive, irreversible delete')\n\t\t.action(async (encId: string, flags: DeleteFlags, command: Command) => {\n\t\t\tawait runResumesDelete(resolveContext(command), encId, flags.confirm === true);\n\t\t});\n}\n","import type { Command } from 'commander';\nimport { registerPersonalResumesSchema } from './schema';\nimport { registerPersonalResumesValidate } from './validate';\nimport { registerPersonalResumesTemplate } from './template';\nimport { registerPersonalResumesList } from './list';\nimport { registerPersonalResumesView } from './view';\nimport { registerPersonalResumesExport } from './export';\nimport { registerPersonalResumesCreate } from './create';\nimport { registerPersonalResumesUpdate } from './update';\nimport { registerPersonalResumesCopy } from './copy';\nimport { registerPersonalResumesPublish, registerPersonalResumesUnpublish } from './publish';\nimport { registerPersonalResumesDelete } from './delete';\n\n// Task 13 掛 list/view/export;Task 14 補 create/update;Task 15(checkpoint 3 收尾)\n// 補上 copy/publish/unpublish/delete,同一份 index.ts 逐步長大。\nexport function registerPersonalResumesCommand(parent: Command): void {\n\tconst resumes = parent.command('resumes').description('Manage your personal resumes');\n\tregisterPersonalResumesSchema(resumes);\n\tregisterPersonalResumesValidate(resumes);\n\tregisterPersonalResumesTemplate(resumes);\n\tregisterPersonalResumesList(resumes);\n\tregisterPersonalResumesView(resumes);\n\tregisterPersonalResumesExport(resumes);\n\tregisterPersonalResumesCreate(resumes);\n\tregisterPersonalResumesUpdate(resumes);\n\tregisterPersonalResumesCopy(resumes);\n\tregisterPersonalResumesPublish(resumes);\n\tregisterPersonalResumesUnpublish(resumes);\n\tregisterPersonalResumesDelete(resumes);\n}\n","import type { Command } from 'commander';\nimport { randomUUID } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { personalPost, type PersonalRequestOptions } from '../../../lib/personal-client';\nimport { resolveContext, type ResolvedContext } from '../../../lib/global-opts';\nimport { CliError, ExitCode, ServerClientHttpError } from '../../../lib/errors';\nimport { printJson, sanitizeForTerminal } from '../../../lib/output';\nimport { PERSONAL_APPLICATIONS_BASE } from '@wport/core';\nimport { resolveMessage, validateBatchItems, type ApplyBatchItem } from './message-input';\n\ninterface ApplyFlags {\n\tresume?: string;\n\tmessage?: string;\n\tmessageFile?: string;\n\tfile?: string;\n\tconfirm?: boolean;\n}\n\n/** 把 409/429 的結構化 body 轉成人類可讀訊息(保留 exit 3)。其餘錯誤原樣上拋。 */\nfunction rethrowApplyError(err: unknown): never {\n\tif (err instanceof ServerClientHttpError) {\n\t\tconst body = (err.body ?? null) as Record<string, unknown> | null;\n\t\tconst code = body?.error_code;\n\t\tif (err.status === 409 && code === 'application_cooldown') {\n\t\t\tthrow new CliError(`Application in cooldown. Next apply at: ${body?.next_apply_at ?? 'unknown'}`, ExitCode.ServerClientError);\n\t\t}\n\t\tif (err.status === 409 && code === 'job_not_available') {\n\t\t\tthrow new CliError('Job is no longer available (closed or removed).', ExitCode.ServerClientError);\n\t\t}\n\t\tif (err.status === 429 && code === 'apply_daily_limit_reached') {\n\t\t\tthrow new CliError(\n\t\t\t\t`Daily application limit reached (${body?.used}/${body?.limit}). Resets at: ${body?.resets_at ?? 'tomorrow'}`,\n\t\t\t\tExitCode.ServerClientError\n\t\t\t);\n\t\t}\n\t}\n\tthrow err;\n}\n\nexport async function runPersonalApply(\n\tctx: ResolvedContext,\n\targs: { encJobId: string; encResumeId: string; message: string; confirm: boolean }\n): Promise<void> {\n\tif (!args.confirm) {\n\t\tthrow new CliError(\n\t\t\t'Refusing to apply without --confirm (application is visible to the employer)',\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tconst body = { enc_job_id: args.encJobId, enc_resume_id: args.encResumeId, application_message: args.message };\n\tlet result;\n\ttry {\n\t\tresult = await personalPost(opts, PERSONAL_APPLICATIONS_BASE, body, { idempotencyKey: randomUUID() });\n\t} catch (err) {\n\t\trethrowApplyError(err);\n\t}\n\tconst data = (result.body as { data?: Record<string, unknown> })?.data ?? {};\n\tif (ctx.format === 'json') {\n\t\tprintJson(data);\n\t\treturn;\n\t}\n\tprocess.stdout.write(`Applied. application: ${sanitizeForTerminal(String(data.enc_application_id ?? ''))}\\n`);\n\tconst q = data.quota as { used?: number; limit?: number; remaining?: number } | undefined;\n\tif (q) process.stdout.write(`Daily quota: ${q.used}/${q.limit} used, ${q.remaining} remaining\\n`);\n}\n\nexport function registerPersonalApplyCommand(parent: Command): void {\n\tparent\n\t\t.command('apply [job_enc_id]')\n\t\t.description('Apply to a job (single) or batch via --file (requires --confirm)')\n\t\t.option('--resume <enc_id>', 'resume enc_id to apply with (single mode)')\n\t\t.option('--message <text>', 'application message (1~2000 chars)')\n\t\t.option('--message-file <path>', 'read application message from file (use - for stdin)')\n\t\t.option('--file <path>', 'batch JSON file { \"applications\": [...] } (max 20)')\n\t\t.option('--confirm', 'confirm this outward-facing action')\n\t\t.action(async (jobEncId: string | undefined, flags: ApplyFlags, command: Command) => {\n\t\t\tconst ctx = resolveContext(command);\n\t\t\tif (flags.file) {\n\t\t\t\tawait runPersonalApplyBatch(ctx, { file: flags.file, confirm: flags.confirm === true });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!jobEncId || !flags.resume) {\n\t\t\t\tthrow new CliError('single apply requires <job_enc_id> and --resume', ExitCode.InvalidArgument);\n\t\t\t}\n\t\t\tconst message = resolveMessage({ message: flags.message, messageFile: flags.messageFile });\n\t\t\tawait runPersonalApply(ctx, {\n\t\t\t\tencJobId: jobEncId,\n\t\t\t\tencResumeId: flags.resume,\n\t\t\t\tmessage,\n\t\t\t\tconfirm: flags.confirm === true,\n\t\t\t});\n\t\t});\n}\n\nexport async function runPersonalApplyBatch(\n\tctx: ResolvedContext,\n\targs: { file: string; confirm: boolean }\n): Promise<void> {\n\tif (!args.confirm) {\n\t\tthrow new CliError(\n\t\t\t'Refusing to apply without --confirm (applications are visible to employers)',\n\t\t\tExitCode.InvalidArgument\n\t\t);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(readFileSync(args.file, 'utf8'));\n\t} catch {\n\t\tthrow new CliError(`Cannot read/parse batch file: ${args.file}`, ExitCode.InvalidArgument);\n\t}\n\tconst items: ApplyBatchItem[] = validateBatchItems((parsed as { applications?: unknown })?.applications);\n\tconst opts: PersonalRequestOptions = { baseUrl: ctx.baseUrl, locale: ctx.locale, timeoutMs: ctx.timeoutMs };\n\tlet result;\n\ttry {\n\t\tresult = await personalPost(opts, `${PERSONAL_APPLICATIONS_BASE}/batch`, { applications: items }, { idempotencyKey: randomUUID() });\n\t} catch (err) {\n\t\trethrowApplyError(err);\n\t}\n\tconst data = (result.body as { data?: Record<string, unknown> })?.data ?? {};\n\tif (ctx.format === 'json') {\n\t\tprintJson(data);\n\t\treturn;\n\t}\n\tconst succeeded = (data.succeeded as unknown[]) ?? [];\n\tconst failed = (data.failed as { index: number; enc_job_id: string; error_code: string }[]) ?? [];\n\tprocess.stdout.write(`Batch applied: ${succeeded.length} succeeded, ${failed.length} failed\\n`);\n\tfor (const f of failed) {\n\t\tprocess.stdout.write(` #${f.index} ${sanitizeForTerminal(f.enc_job_id)}: ${sanitizeForTerminal(f.error_code)}\\n`);\n\t}\n}\n","import { readFileSync } from 'node:fs';\nimport { CliError, ExitCode } from '../../../lib/errors';\n\nconst MAX_MESSAGE = 2000;\n\nexport interface ApplyBatchItem {\n\tenc_job_id: string;\n\tenc_resume_id: string;\n\tapplication_message: string;\n}\n\nfunction defaultReadStdin(): string {\n\treturn readFileSync(0, 'utf8');\n}\n\nfunction validateMessageText(raw: string): string {\n\tconst trimmed = raw.trim();\n\tif (trimmed.length < 1) {\n\t\tthrow new CliError('application message must not be empty (1~2000 chars) 應徵訊息必填', ExitCode.InvalidArgument);\n\t}\n\tif (trimmed.length > MAX_MESSAGE) {\n\t\tthrow new CliError(`application message exceeds ${MAX_MESSAGE} chars 應徵訊息不可超過 2000 字`, ExitCode.InvalidArgument);\n\t}\n\treturn trimmed;\n}\n\n/** 單筆 --message / --message-file 互斥二擇一必填;`-` 讀 stdin。違規丟 exit 2。 */\nexport function resolveMessage(opts: { message?: string; messageFile?: string; readStdin?: () => string }): string {\n\tconst hasMsg = opts.message !== undefined;\n\tconst hasFile = opts.messageFile !== undefined;\n\tif (hasMsg && hasFile) {\n\t\tthrow new CliError('--message and --message-file are mutually exclusive 互斥', ExitCode.InvalidArgument);\n\t}\n\tif (!hasMsg && !hasFile) {\n\t\tthrow new CliError('--message or --message-file is required 應徵訊息必填', ExitCode.InvalidArgument);\n\t}\n\tif (hasMsg) return validateMessageText(opts.message as string);\n\tconst file = opts.messageFile as string;\n\tconst raw = file === '-' ? (opts.readStdin ?? defaultReadStdin)() : readFileSync(file, 'utf8');\n\treturn validateMessageText(raw);\n}\n\n/** 批次 ≤20 + 每筆欄位齊 + message 1~2000。違規丟 exit 2。回正規化(trim)後的項目。 */\nexport function validateBatchItems(items: unknown): ApplyBatchItem[] {\n\tif (!Array.isArray(items) || items.length < 1) {\n\t\tthrow new CliError('batch file must contain a non-empty \"applications\" array', ExitCode.InvalidArgument);\n\t}\n\tif (items.length > 20) {\n\t\tthrow new CliError(`batch exceeds 20 items (got ${items.length}) 批次上限 20 筆`, ExitCode.InvalidArgument);\n\t}\n\treturn items.map((it, i) => {\n\t\tconst o = it as Record<string, unknown>;\n\t\tconst jobId = typeof o?.enc_job_id === 'string' ? o.enc_job_id.trim() : '';\n\t\tconst resumeId = typeof o?.enc_resume_id === 'string' ? o.enc_resume_id.trim() : '';\n\t\tif (!jobId || !resumeId) {\n\t\t\tthrow new CliError(`batch item #${i}: enc_job_id and enc_resume_id required`, ExitCode.InvalidArgument);\n\t\t}\n\t\tconst message = validateMessageText(typeof o?.application_message === 'string' ? o.application_message : '');\n\t\treturn { enc_job_id: jobId, enc_resume_id: resumeId, application_message: message };\n\t});\n}\n","import type { Command } from 'commander';\nimport { registerPersonalResumesCommand } from './resumes';\nimport { registerPersonalApplyCommand } from './apply';\n\nexport function registerPersonalCommand(program: Command): void {\n\tconst personal = program.command('personal').description('Manage your personal resumes, profile and applications');\n\tregisterPersonalResumesCommand(personal);\n\tregisterPersonalApplyCommand(personal);\n}\n"],"mappings":"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","names":["pc","Table","import_node_fs","import_core","envPaths","paths","import_core","import_core","import_node_fs","import_picocolors","paths","s","pc","program","import_node_fs","program","import_node_fs","import_core","import_node_fs","import_node_path","import_env_paths","paths","envPaths","import_core","import_core","program","import_core","enterpriseGet","enterprisePost","enterprisePatch","enterprisePut","enterpriseDelete","enterpriseGet","import_core","enterpriseGet","import_core","formatDate","enterpriseGet","import_core","enterpriseGet","import_core","enterprisePost","import_node_crypto","import_core","enterprisePatch","import_node_crypto","import_core","enterprisePatch","enterpriseDelete","enterprisePost","import_node_crypto","import_core","enterprisePost","import_core","formatDate","enterpriseGet","import_core","enterprisePost","import_core","DETAIL_FIELDS","renderDetailLines","enterpriseGet","import_node_crypto","import_core","enterpriseGet","enterprisePatch","import_node_crypto","import_node_fs","import_node_path","import_core","enterprisePost","import_node_crypto","import_core","enterpriseGet","enterprisePut","import_core","MINIMAL_LIST_FIELDS","formatDate","enterpriseGet","import_core","enterpriseGet","import_node_crypto","import_core","enterprisePost","import_node_crypto","import_core","enterprisePost","import_core","STATUS_MAP","MINIMAL_LIST_FIELDS","mapStatusFlag","formatStatus","formatCount","enterpriseGet","import_node_crypto","import_core","enterprisePatch","import_node_crypto","import_core","enterprisePatch","import_core","enterpriseGet","program","performLogin","osHostname","program","import_core","import_core","warnIfRateLimitLow","import_core","program","program","import_core","import_core","formatDate","import_core","program","import_core","import_core","import_node_fs","import_core","import_core","import_core","MINIMAL_LIST_FIELDS","formatDate","import_core","import_core","import_node_fs","import_node_path","import_core","import_node_crypto","import_core","import_core","requireEncId","printOrchestrationResult","import_node_crypto","import_core","import_core","requireEncId","import_node_crypto","import_core","import_core","requireEncId","import_node_crypto","import_core","requireEncId","import_node_crypto","import_node_fs","import_core","import_node_fs","program"]}