# Changelog

`@wport/cli` 變更紀錄。格式參考 [Keep a Changelog](https://keepachangelog.com/)，
版號遵循 [Semantic Versioning](https://semver.org/)。

---

## [0.10.2] — 2026-09-10

### Fixed

- **`enterprise company replace` 正確區分 409 衝突原因**：`profile_revision_conflict` 仍提示重新讀取並確認；`idempotency_conflict` 改提示更換 `Idempotency-Key`，不再誤導使用者重新讀取資料。`company update` 的 `video_links` 路徑同步修正。
- 修正企業公司區塊刪除確認訊息的空格，並在 `replace --help` 與 README 補充硬刪、`enc_id` 備份還原限制與範例。

## [0.10.1] — 2026-09-10

### Changed

- **`enterprise usage` 輸出標明計量語意**：月配額行改為 `X / Y calls used`，並加一行說明
  「所有認證通過的 API 呼叫計數；`whoami`/`usage` 與 key rotate 豁免」——對齊後端 2026-09-10
  上線的 quota PRD 對齊（talent#233：guard 層 per-request 計數，原先只計職缺建立）。
  純顯示變更，wire 契約與 exit code 不變。（0.10.0 已以 `next` tag 發布、不可覆寫，故以
  patch 版收納本變更。）

## [0.10.0] — 2026-09-09

### Added

- **`wport enterprise company replace <section>`（新指令）**：整段取代四個公司內容區塊
  （`pain-points` / `awards` / `milestones` / `qa-items`）。輸入為 `{ "items": [...] }`，
  走 `--file <path>` 或 `-`（stdin）。

  **這是整段取代，不是 add/update：未列於輸入中的既有項目會被刪除。** 因此指令會在每次寫入前
  先 `GET /company`、比對 `enc_id`，只要有任何項目將被移除（包含「非空但漏掉一筆」，
  不只 `items: []`），就列出即將刪除的 `enc_id` 與摘要並要求 `--confirm`，否則 exit 2。

  寫入使用同一次 preflight 取得的 `block_revisions.<block>` 作為 `If-Match` 樂觀鎖；
  遇 409（stale revision）**不自動重送**，改為重新 `GET` 顯示差異後 exit 2。
  預設帶 `Idempotency-Key`（`randomUUID()`），可用 `--idempotency-key` 覆寫。

- **`company update` 的 `basic` 節新增六個可寫欄位**：`representative_name`、
  `establishment_date`、`directors`（`string[]`），以及三個公開頁顯示開關
  `is_representative_visible` / `is_establishment_date_visible` / `is_directors_visible`
  （皆 `0|1`，**未設時預設 0 = 隱藏**）。

  `is_uniform_number_visible` **刻意不開放**（保留在 `FORBIDDEN_FIELDS`），
  `uniform_number` 維持唯讀。

- **`company update` 的 `descriptions` 節新增 `video_links`**（≤3，YouTube / Vimeo）。
  與同節其他三個欄位不同：帶 `video_links` 時會自動附上 `If-Match`（樂觀鎖），
  且與 `description` 在後端是**同一筆原子寫入**。移除既有影片（含漏一部與 `video_links: []`
  清空）套用與 `replace` **相同的** `--confirm` 規則；428／409 不自動重送。

  只送 `video_links` 而不送 `description` 時，CLI 會以 preflight 讀到的現值回填，
  不會清空既有的公司介紹。

- **`company view` 表格模式新增區塊摘要**（五個區塊各自的筆數）。
  `block_revisions` **只在 `--output json` 顯示**——它是 opaque token，不是人類可讀的版本號。

- **可程式判讀的錯誤碼**：新增五個 `error_code` 的專屬訊息裝飾
  （`profile_revision_required` / `profile_revision_conflict` / `block_max_exceeded` /
  `block_item_forbidden` / `unsupported_video_provider`）。分流一律依 `error_code`
  優先於 HTTP status，**不解析多語系 `message`**。

- **enterprise 線導入 OpenAPI codegen**：契約凍結自 talent repo commit
  `4cf6ca74b4f50dc256b2536b5101c2b9fed3c422`，產物與 source manifest 進版控，
  CI 新增 `enterprise schema drift gate`（驗 sha256 → 驗 prefix → 跑 codegen →
  `git diff --exit-code`）。新指令的型別取自 `components['schemas']`。

### Changed

- 依賴 bump：`@wport/core` `^0.2.0` → `^0.3.0`（0.x 的 caret 不跨 minor，需顯式升）。
  core 0.3.0 為純 additive（新增 `enterprisePut` 與 enterprise schema 型別匯出），
  無移除任何既有匯出，CLI 既有行為不受影響。

### ⚠️ 後端相依

本版新增的指令與欄位需要 talent 後端 **2026-09-09 或之後的版本**
（PR hotfire-digital/W101-TalentSearchHub#232，已於該日上正式站）。
對更舊的後端使用新欄位會因 `forbidNonWhitelisted` 被回 400。
既有指令不受影響。

## [0.9.3] — 2026-08-26

### Changed

- 依賴 bump:`@wport/core` `^0.1.0` → `^0.2.0`(0.x 的 caret 不跨 minor,需顯式升)。
  CLI 對外行為零變更:指令、輸出、exit code 契約(0/2/3/4/5)均不受影響——core 0.2.0 的
  5xx 錯誤自 `WportError` 改為帶 `status` 的 `WportHttpError`,兩者在 `exitCodeForError`
  同落 exit 4(review F-008 附帶核對 + 743 tests 驗證)。
- 姊妹套件同步發版(pm_48 交付 C 的 SDK 地基):
  - **`@wport/core` 0.2.0**:`throwForHttpStatus` 任何非 2xx(含 5xx)一律拋帶 `status`/`body`
    的 `WportHttpError`(先前 5xx 拋裸 `WportError`,下游拿不到 http status);`buildUserAgent`
    改 `globalThis` 存取 `process`(非 Node runtime / 無 node types 的 tsc 環境不再炸)。
  - **`@wport/sdk` 0.2.0**:新增 `enterprise.me()`(GET /me)/`enterprise.usage()`、
    `enterprise.jobs.view/publish/unpublish/copy`;`WportClientOptions.source` 覆寫
    (BR-039 通道標記,預設仍 `typescript_sdk`);`CompanyMe` 對齊 wire 實形
    `{ company: { enc_id, name } }`;`__SDK_VERSION__` 加 typeof 防禦(source-bundling
    消費者不再 ReferenceError)。此三包即 `mcp.wport.me` remote MCP(交付 C)的執行底座。

## [0.9.2] — 2026-08-24

### Added

- **請求來源標記(pm_48 交付 B,BR-039)**:CLI 對 talent 後端的每個請求帶 `X-Source: cli`
  header——單一常數 `CLI_SOURCE` 收斂,四個出口共用(jobs typed client、enterprise transport、
  personal client、OAuth device flow)。後端(交付 A)已上線,將來源落入 API access log 的
  `source` 欄供 audit。S3 presigned PUT 刻意**不帶**(自訂 header 會破 SigV4 簽章)。
  對外行為零變更:指令、輸出、exit code 契約均不受影響。
- 姊妹套件 `@wport/core` / `@wport/sdk` 同步發 **0.1.1**:exports map 修正——`import` /
  `require` 條件各自對應正確的 types 檔(`index.d.ts` / `index.d.cts`),並導出
  `./package.json`;修正 TypeScript `moduleResolution: node16 / bundler` 下的型別解析。

## [0.9.1] — 2026-08-17

### Changed

- **內部重構(pm_48 交付 B):轉 npm workspaces monorepo**——共用引擎抽出為 `@wport/core`
  (typed REST client、typed errors、enterprise transport、concurrency、resume-schema),
  CLI 改為其消費者。**對外行為零變更**:指令、exit code 契約(0/2/3/4/5)、
  schema fingerprint(`839e8a891dfb`)均與 0.9.0 一致(669 tests + whole-branch review 驗證)。
- 新姊妹套件:`@wport/sdk`(程式化存取,同能力邊界)與 `@wport/core`(內部引擎)自本版起發布。
- npm 發布素材補齊:package 內 README/CHANGELOG/LICENSE(先前 `files` 指向 repo root 造成漏檔)。

## [0.9.0] — 2026-08-10

### Added

- **個人線(pm_47)· `wport personal apply` 投遞**(對接 talent `/api/v1/personal/applications`):
  - 單筆:`personal apply <job_enc_id> --resume <enc_id> --message <text> | --message-file <path|-> --confirm`
    ——message 互斥二擇一必填、trim 後 1~2000 字**本地先驗**(超長/空 exit 2 不發請求)、`-` 讀 stdin;
    投遞對雇主可見,無 `--confirm` 一律 exit 2 不發請求;自動附 `Idempotency-Key`。
  - 批次:`personal apply --file <batch.json> --confirm`——`{"applications":[…]}` ≤20 筆與逐筆 message
    本地先驗(超限 exit 2);輸出 succeeded/failed 表,`--output json` 給完整陣列。
  - **錯誤分流**:冷卻 409(印 `next_apply_at`)、職缺終態 409、日上限 429(印剩餘額度 / `resets_at`)
    → 統一 exit 3 + 人類可讀;其餘 4xx 沿既有 exit code 契約。
- codegen:凍結 `docs/handoff/swagger-wport-personal-api-v1.{json,yaml}` 納入 applications 端點,
  `PERSONAL_APPLICATIONS_BASE` 常數;`gen:openapi:personal` 經 `--keep-prefix` 自動含 applications。

## [0.8.0] — 2026-07-30

### Added

- **個人線(pm_47)· OAuth 認證基座**:`wport login`(RFC 8628 device flow,自動開瀏覽器、
  `--no-browser`/`--force`)、`whoami`、`logout`(RFC 7009 revoke)、
  `sessions list / revoke <enc_id> [--all-others]`(session 治理、失竊自救)。
  access token 過期自動 refresh(rotation),使用者無感;token 全程不落 log、不 echo。
- **`wport personal resumes` 全套**(GUI parity,US-1 北極星):
  - 本地三件套(離線可用):`schema [--section]`(聚合格式 JSON Schema 自描述)、
    `template`(骨架 full.json)、`validate --file`(unknown-field reject + 欄位級錯誤清單)
  - API 面:`list`(含 quota)、`view`(聚合全節)、`create --file`(聚合編排:建殼 → rename →
    逐節寫入、partial success 不回滾)、`update <enc_id> --file [--section]`(item 帶 `enc_id`
    走更新、頂層 `name` 走改名)、`copy`、`publish`/`unpublish`、`delete --confirm`、`export`
- **credentials.json 升級 v2 雙區**:enterprise(`wpk_live_`)與 personal(OAuth)並存,
  舊格式自動遷移、零使用者動作。
- `doctor` 增列個人線登入態與 OAuth AS 連通性探測。

### Notes

- 個人線需後端 personal API(W101-TalentSearchHub PR #139)——**已部署 prod / staging**,
  本版即為全功能可用。
- `update` 陣列節語義:item 帶 `enc_id`(來自 `view` 的 round-trip)→ 更新該筆;無 `enc_id`
  → 一律新增。**不做宣告式刪除**——不在輸入檔內的既有 item 不會被刪(`portfolio_links`
  例外,該端點本身為宣告式 bulk upsert,不在陣列中的連結會被刪)。部分失敗重試前請先
  `view` 取得已寫入 item 的 `enc_id`,避免重複新增。

---

## [0.7.1] — 2026-07-23

### Changed

- **原始碼移至獨立 repo**：`@wport/cli` 自 W101-TalentSearchHub monorepo 的 `apps/cli`
  拆分為獨立 repo [hotfire-digital/wport-cli](https://github.com/hotfire-digital/wport-cli)
  （`git filter-repo` 抽取，完整保留開發歷史）。
- `package.json` 補上 `repository` / `homepage` / `bugs.url`，本版起 npm 頁面
  正確顯示原始碼連結。
- `gen:openapi` 改走 `scripts/gen-from-remote.cjs`（原 monorepo 相對路徑已不存在）。

### Notes

- CLI 功能與 0.7.0 完全相同，純 metadata / 建置流程調整。

---

## [0.7.0] — 2026-07-15

### Removed

- **`enterprise jobs close` 全面撤銷**（產品決議：職缺狀態只有上下架，無「關閉」態）。
  「已關閉（status=10）」是 GUI 完全不存在的第三狀態——被 CLI 關閉的職缺會在企業後台
  顯示成無法再上架、無法操作的殭屍列，卡住整個管理流程。命令改為隱藏 stub：
  打到時印出遷移訊息（改用 `jobs unpublish` 下架、或 `jobs delete --confirm` 刪除）
  並 exit 2、不發任何請求。
- 後端 `PATCH /api/v1/enterprise/jobs/:enc_id/close` 端點同步移除（同一 backend PR）。
  舊版 CLI（0.5.x–0.6.x）呼叫 `jobs close` 將收到 404——請升級至 0.7.0。

### Notes

- 已被關閉的存量職缺由後端資料復原 DML 統一改回「未刊登」（可重新上架），
  詳見 backend repo `docs/ddl/V20260715*__tsh-pm_41-jobs-close-revoke-restore.sql`。

---

## [0.6.0] — 2026-07-08

issue #106 實測回饋：讓 agent 更容易讀懂錯誤與能力邊界，不必踩裸 40x 才知道。

### Added

- **`enterprise jobs publish` 失敗明列缺欄**：BR-008 publish gate 回 400（`data.missing_fields`）時，錯誤訊息直接列出缺哪些欄位（如 `Missing required fields: work_hours, headcount`），並提示以 `jobs update` 補齊；不再只有翻譯後的中文一句話。
- **`enterprise jobs copy` 顯示 `incomplete_fields`**：複製 legacy 職缺（來源子表為空）時，人類模式印出 `⚠ Incomplete for publish — fill before \`jobs publish\`: …`；json 模式回應含 `incomplete_fields`。
- **`wport doctor` 新增「Enterprise talent scope」區塊**：明示 Enterprise API Key 僅存取「已投遞」人才庫（`talents list --tab visit` 尚未開放、回 400），且**無主動搜尋**——`/api/search-talent` 需互動式 JWT（企業模式）session，API Key → 401（設計如此，非設定錯誤）。

### Changed

- `enterprise talents` 群組說明對齊現況：applied pool only、無主動搜尋（詳見 `wport doctor`）。

### Notes

- 上述錯誤/邊界提示與後端無耦合，對任何後端版本皆生效；`incomplete_fields` 顯示需後端已含 issue #106 copy 回應擴充（已於 backend `main`）。

---

## [0.5.0] — 2026-07-07

pm_41 企業 CLI 補完：人才庫、召募活動、公司資料、用量查詢，以及職缺複製/關閉。命令樹至此完整覆蓋 PRD §6.1 企業模式全部命令。

### Added

- `wport enterprise talents`：`list`（`--tab` / `--job` / `--keyword` / `--from` / `--to` / 分頁 / `--fields` / `--minimal`）、`view <enc_resume_id>`、`respond <enc_resume_id> --subject --body|--body-file`。強制分頁、PII 白名單、無 bulk export。
- `wport enterprise campaigns`：`list` / `view` / `create --file` / `update --file` / `publish` / `unpublish`（可逆，故無 `--confirm`；不開放 delete）。
- `wport enterprise company`：`view` / `update --file` / `logo upload`（v1 子集欄位；`uniform_number` 唯讀，update DTO 天然不含）。
- `wport enterprise usage`：查月配額與 rate limit 剩餘（`GET /api/v1/enterprise/usage`）。
- `wport enterprise jobs copy <enc_id>` / `jobs close <enc_id>`（close 為破壞性、需 `--confirm`）；`jobs list` 新增統計欄位 `published_at` / `clicks_7d`（形狀向後相容）。
- 新增 `readTextInput()` helper：支援 `--body-file`（檔案或 `-` stdin）讀入純文字訊息內文。

### Notes

- 所有寫入命令自動產生 `Idempotency-Key`（可用 `--idempotency-key` 覆寫）；破壞性寫入（`jobs delete` / `jobs close` / `jobs batch`）無 `--confirm` 一律本地 exit 2、不發請求。
- ⚠️ 需要後端已部署對應 pm_41 端點（talents / campaigns / company / usage / jobs copy·close）。對未部署環境會回 404。

## [0.4.0] — 2026-07-02

企業 API 金鑰治理 + 職缺寫入。

### Added

- `wport enterprise keys`：`list`（顯示 enc_id + 末 4 碼掩碼）、`rotate <enc_id>`（key-for-key 換發，`--reveal` 才顯示新明文全文、`--expiry-days` 設到期）。過期金鑰仍可用於 `rotate`。
- `wport enterprise jobs` 寫入全套：`create --file` / `update --file` / `publish` / `unpublish` / `delete`（需 `--confirm`）/ `batch --file`。

### Changed

- 修正 `enterprise-client` 的 401 提示：金鑰**過期**時引導 `wport enterprise keys rotate`（重 login 同一把過期 key 仍 401、解不了），撤銷/錯誤才引導 `login`。

## [0.2.2] — 2026-06-30

**無 CLI 邏輯/原始碼變動**（`src/` 行為與 0.2.1 相同），純清理打包進 npm 的文件死連結。registry 上 0.2.1 的 `repository` / `homepage` 與 README/CHANGELOG 仍指向私有 `W101-TalentSearchHub` repo（公開使用者點了 404），npm 已發布版本不可覆寫，故 bump PATCH 重新發布以覆蓋呈現。

### Docs

- 移除 README 中指向私有 repo 的死連結（issue tracker、CHANGELOG blob 連結），保留說明文字；CHANGELOG 改引用隨套件出貨的 `CHANGELOG.md`。
- 移除 CHANGELOG `PR #15` 殘留的舊 owner（`w101-admin`）死連結，去連結化為純文字。

## [0.2.1] — 2026-06-12

### Changed

- `enterprise login` 改打新的 `GET /api/v1/enterprise/me` 端點驗證 API key（取代舊的
  `GET /jobs?pageSize=1`），並從回應取回公司名存入 `credentials.json`。
- `enterprise whoami` 現在會顯示綁定的公司名稱（先前因 server 無身分端點而固定顯示 `(unknown)`）。

> ⚠️ **需要後端部署 `GET /api/v1/enterprise/me`（PM-37 增量）**。對尚未部署該端點的環境，
> `enterprise login` 會回 404；升級前請確認目標環境後端已上線 `/me`。

## [0.2.0] — 2026-06-12

### Added

- `wport enterprise` command tree: `login` / `logout` / `whoami` + `jobs list` / `jobs view`
  against the PM-37 enterprise API (`/api/v1/enterprise`), Bearer `wpk_live_` key auth.
- Key storage: `credentials.json` (mode 600, atomic write), precedence
  `--api-key` > `WPORT_API_KEY` > saved file; keys always masked to last 4 chars.
- `--status published|unpublished` filter (mapped to the server's numeric contract; sending the
  openapi.yaml string enum `active|inactive|deleted` is rejected at the CLI layer to prevent 400s).

---

## [0.1.3] — 2026-06-02

npm 頁面呈現優化，**無 CLI 邏輯/原始碼變動**（`src/` 行為與 0.1.2 相同）。唯一的執行層差異是版號字串：`tsup.config.ts` 在 build 時把 `pkg.version` 經 `__CLI_VERSION__` define 嵌入 `dist/index.js`，故重 build 後 `wport --version` 會印 `0.1.3`、`dist` 非與 0.1.2 二進位一致（除版號字串外行為相同，比照 0.1.1 的描述）。因 npm 已發布版本不可覆寫、需 republish 才會反映到 npmjs.com，故 bump PATCH。

### Docs

- **`package.json` metadata 補齊**：新增 `repository`（含 `directory: apps/cli`）、`homepage`、`author`、`bugs`（email channel，與 README `## Support` 一致），讓 npmjs.com sidebar 連結與署名完整。`keywords` 擴充（`ndjson` / `jq` / `agent` / `terminal` / `wport-cli` / `job-search-api`）。
- **`files` 加入 `CHANGELOG.md`**：changelog 一併進 tarball。
- **README**：標題下加 npm version / node / license badge；Quick start 後新增 Example output（table + JSON）區塊；開頭 pre-release 免責濃縮；修正 `CHANGELOG.md` 相對連結為絕對 GitHub URL（原連結在 npmjs.com 上 404）。

---

## [0.1.2] — 2026-05-29

agent-ready + contract-hardened 一輪。版號維持 PATCH（0.x 階段 SemVer §4 不保證穩定、實際使用者趨近零、移除項有 graceful degradation），但下方 ⚠️ BREAKING 仍明確標注。

### ⚠️ BREAKING

- **移除 `api_base_url` config key**。改用 `WPORT_API_BASE` 環境變數或 `--api` flag 覆寫 base URL。
  - 理由：把可變 base URL 持久化在磁碟上是 SSRF / credential-exfil 攻擊面，外部使用者並不需要（沒有 localhost / staging 場景）；未來加入登入後風險更高。
  - **不會 hard-fail**：既有 config 仍含 `api_base_url` 時，CLI 在載入時印一次 deprecation 警告並忽略該 key（不會 crash）。
  - `wport config set api_base_url <x>` 會明確報錯並引導改用 `WPORT_API_BASE`。

### Added

- **`jobs search --fields <list>` / `--minimal`**：JSON 輸出時只保留指定欄位（comma-separated dotted paths）。`--minimal` 是 `enc_id,title,company_name,area_display,salary_display` 的捷徑。大幅降低 agent 讀取 response 的 token 量（client-side 過濾，省 token 非省 bytes-on-wire）。`--fields` 與 `--minimal` 互斥；table 模式不受影響並會提示改用 `--output json`。
- **`jobs view --fields <list>`**：多欄位投影成 JSON object（既有 `--field` 單欄位純值輸出保留）。`--field` 與 `--fields` 互斥。
- **`jobs view --batch [--concurrency <n>]`**：從 stdin 讀 newline-separated enc_ids（需以 `-` 作 enc_id 參數），bounded 並行（預設 5、最大 20）逐筆查詢，輸出 ND-JSON（每行 `{ enc_id, ok, data? , error? }`）。單筆失敗不影響其餘，輸出順序對齊輸入順序。一個 process 處理多筆，省去 N 次 Node cold start。
- **`wport doctor`**：診斷指令，列出 resolved config（base URL / locale / timeout / config 檔位置）、server 連通性探測、bundled schema fingerprint，以及「server 會 silent ignore `orderBy` / `order`」等 agent 需知的行為。連不上 server 時 exit `ServerOrNetworkError`(4)，其餘 exit 0。
- **bundled schema fingerprint（`__SCHEMA_HASH__`）**：build 時對 `src/generated/schema.d.ts` 取 SHA-256 前 12 字嵌入，供 `wport doctor` 顯示以人工比對 schema drift（自動偵測待後端提供 schema-version endpoint）。

### Changed

- **`jobs search --help` 文案**：明講 sort 由 server 控制、`orderBy` / `order` 會被 silently ignored，故 CLI 不開排序 flag。
- **移除未使用的 `ora` 依賴**（dead dependency；程式碼無任何 import）。
- **HTTP 連線重用**：明確記錄「交給 Node 內建 fetch（undici）的 global dispatcher connection pooling」，刻意不 ship 自訂 `undici` Agent（near-zero 收益、避免新增 runtime dependency）。

### Fixed

- **piped stdin 讀取的 EAGAIN**：`jobs view -`（與 `--batch`）改用可重試的 `readPipedStdin`，解決上游為另一個 process 時（如 `search | jq | view - --batch`）`readFileSync(0)` 偶發 `EAGAIN: resource temporarily unavailable` 而中斷的問題。

---

## [0.1.1] — 2026-05-27

### Changed

- **README `## Support` 段聯絡 email**：從 `a39795979@gmail.com`（個人 Gmail，0.1.0 誤用）改成 `yao@wport.me`（公司 email）。`0.1.0` 已 published 到 npm registry、無法事後修改 README 內容，故 bump 0.1.1 republish。
- PUBLISH-CHECKLIST 與 ROADMAP 對應 §「對外支援 channel」決策紀錄同步修正。

### Notes

- `0.1.0` 在 npm registry 上仍存在但 README 含 personal Gmail。建議使用者直接 `npm install @wport/cli@0.1.1` 或不指定版本（會解析到 latest = 0.1.1）。維護者後續可能 unpublish `0.1.0`（72h 內、無依賴限制下允許）以完整移除 Gmail 痕跡。
- 沒有程式碼變更；`dist/index.js` 內容跟 0.1.0 二進位一致（除版號字串）。

---

## [0.1.0] — TBD（pre-release）

對應 PR #15。

### Added

- `wport jobs search` — 對接公開 `GET /api/jobs/search`：keyword、location、category、page、page-size、`--json-query` 進階逃生口。
- `wport jobs view <enc_id>` — 對接 `GET /api/jobs/{encId}/view`。支援 `--field <dotted.path>`（例 `--field job_info.salary_display`）、stdin (`-`) 取 enc_id。
- `wport config <set|get|path|reset>` — 持久化 `locale` / `api_base_url` / `output` / `timeout_ms`。Config 檔權限 0600，位置依 `env-paths` 跨平台。
- 全域 flags：`--lang`（Accept-Language）、`--api`、`--output`（TTY 偵測 table/json）、`--no-color`、`--timeout`、`-v/--version`、`-h/--help`。
- OpenAPI codegen pipeline（`gen:openapi:prod` / `gen:openapi:remote`）：透過 `scripts/gen-from-remote.cjs` filter `KEEP_PATHS`，跳過 upstream 不相關端點的壞 `$ref`。
- Typed `paths` schema 從 prod swagger 產生；query 型別取自 `operations['JobsController_searchJobs']['parameters']['query']`，後端 DTO 變動會在 CLI 端 surface 成 TS error。
- Pagination 對齊 `PaginatedResponse` 扁平結構：`{ success, statusCode, message, data: T[], currentPage, totalPages, pageSize, totalCount }`。

### Security

- Terminal escape / control-character sanitization，分兩級：
  - `sanitizeForTerminal`（strict，單行）：剝全部 C0（含 `\t \n \r`）、DEL、C1、ESC-initiated 序列。printTable cells、`view` 各欄位標題、error/warning 訊息走這條。
  - `sanitizeForTerminalMultiline`（描述本文、`--field` 字串輸出）：保留 `\n`，正規化 `\r\n` / lone `\r` 為 `\n`，剝其他控制字元。
- JSON 模式不需 sanitize：`JSON.stringify` 自動 escape `< 0x20` 字元成 `\uXXXX`。
- 防禦的攻擊：CSI clear-screen / OSC 0 set-window-title / OSC 8 phishing hyperlink / SGR 偽造 error 訊息 / NUL DEL C1 / CR overwrite / LF row injection / TAB align break。

### Notes

- 預設 API base URL：`https://api.wport.me`。本機開發用 `wport config set api_base_url http://localhost:3000` 或 `--api` flag 覆寫。
- 預設 page size：10（對齊後端 `PaginationDto`）。Server 端強制上限 100。
- 排序由 server 決定：filter 搜尋按 publish date desc、keyword 搜尋按 relevance score。CLI 不暴露 sort flag（後端 service hardcode 不讀 client `orderBy` / `order`）。
- Rate limit：1200 req/min/IP（read-heavy throttle）。互動使用足夠；不要 scripted multi-process scraping。
- `--field <path>` 支援 dotted path 因為 `JobViewVM` 是嵌套（`job_info.{job_title,area_display,salary_display,...}` / `company_header_info.company_name` / `job_description`）。
