dsh-codex-connect 0.1.0-alpha.4.25 → 0.1.0-alpha.4.26

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/INSTALL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Installation Runbook for CLI Agents
2
2
 
3
- Alpha 4.25 is verified with DSH `0.1.2-alpha.5` and its declared pi-ai range `^0.84.2`; the verified registry installation resolves pi-ai `0.84.4`.
3
+ Alpha 4.26 is verified with DSH `0.1.2-rc.1` and its declared pi-ai range `^0.84.2`.
4
4
 
5
5
  Install `dsh-codex-connect` into one requested DeepSeek Harness profile without changing its current default model, search route, global configuration, or OAuth state.
6
6
 
@@ -23,13 +23,14 @@ Check `dsh --version` before changing the requested profile. Use `dsh --help` to
23
23
  | `0.1.0-rc.7` | `0.1.0-alpha.4.14` |
24
24
  | `0.1.1-rc.2` | `0.1.0-alpha.4.21` |
25
25
  | `0.1.2-alpha.2` | `0.1.0-alpha.4.23` |
26
+ | `0.1.2-rc.1` | `0.1.0-alpha.4.26` |
26
27
  | `0.1.2-alpha.5` | `0.1.0-alpha.4.25` |
27
28
 
28
29
  If your exact DSH version is unknown or not listed, stop and verify the combination before installing. Do not blindly install `dsh-codex-connect@alpha`: `alpha` is a moving tag, not a compatibility guarantee. Do not infer support for newer DSH versions from these rows.
29
30
 
30
- Alpha 4.25's verified contract is DSH plugin API packages `0.1.2-alpha.5`, `@earendil-works/pi-ai` `^0.84.2` (resolved as `0.84.4` during verification), and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.24 remains an earlier verified choice for the same DSH version, Alpha 4.23 remains the verified choice for DSH `0.1.2-alpha.2`, Alpha 4.21 remains the verified choice for DSH `0.1.1-rc.2`, and staying on DSH `0.1.0-rc.7` means selecting Alpha 4.14. Upgrading DSH is a separate decision: upgrade the DSH API packages and pi-ai together, then rerun `dsh-codex-connect doctor --json` and `pnpm --silent run check:compatibility` for the selected combination.
31
+ Alpha 4.26's verified contract is DSH plugin API packages `0.1.2-rc.1`, `@earendil-works/pi-ai` `^0.84.2`, and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.25 remains the verified choice for DSH `0.1.2-alpha.5`, Alpha 4.23 remains the verified choice for DSH `0.1.2-alpha.2`, Alpha 4.21 remains the verified choice for DSH `0.1.1-rc.2`, and staying on DSH `0.1.0-rc.7` means selecting Alpha 4.14. Upgrading DSH is a separate decision: upgrade the DSH API packages and pi-ai together, then rerun `dsh-codex-connect doctor --json` and `pnpm --silent run check:compatibility` for the selected combination.
31
32
 
32
- These choices reflect the repository's existing verification record, not a new installation or runtime probe. This guidance does not fix upstream DSH compatibility or resolve [Issue #64](https://github.com/franksong2702/dsh-codex-connect/issues/64).
33
+ The Alpha 4.26 row reflects a fresh isolated installation and runtime probe. Historical rows remain the repository's existing verification record. This guidance does not change upstream DSH behavior or resolve [Issue #64](https://github.com/franksong2702/dsh-codex-connect/issues/64).
33
34
 
34
35
  ### Install the selected version and validate
35
36
 
@@ -52,23 +53,30 @@ These choices reflect the repository's existing verification record, not a new i
52
53
  dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.23
53
54
  ```
54
55
 
56
+ For DSH `0.1.2-rc.1`, use Alpha 4.26:
57
+
58
+ ```sh
59
+ dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.26
60
+ ```
61
+
55
62
  For DSH `0.1.2-alpha.5`, use Alpha 4.25:
56
63
 
57
64
  ```sh
58
65
  dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.25
59
66
  ```
60
67
 
61
- If npm is unavailable after the matching GitHub prerelease is created, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.21'` only for the DSH `0.1.1-rc.2` combination, `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.23'` only for the DSH `0.1.2-alpha.2` combination, or `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.25'` only for the DSH `0.1.2-alpha.5` combination.
68
+ If npm is unavailable after the matching GitHub prerelease is created, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.21'` only for the DSH `0.1.1-rc.2` combination, `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.23'` only for the DSH `0.1.2-alpha.2` combination, `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.25'` only for the DSH `0.1.2-alpha.5` combination, or `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.26'` only for the DSH `0.1.2-rc.1` combination.
62
69
 
63
- 3. Run `dsh --profile web --dump-config` and require exactly one `llm-openai-codex` row loading `dsh-codex-connect`.
64
- 4. Confirm the effective `agent-default-model` and `web.searchProvider` values are unchanged from before installation.
65
- 5. Run secret-free diagnostics:
70
+ 3. Run `dsh web --help` once to compose the installed profile without starting the server. DSH `0.1.2-rc.1` prepares profile plugin dependency fallback during this step.
71
+ 4. Run `dsh --profile web --dump-config` and require exactly one `llm-openai-codex` row loading `dsh-codex-connect`.
72
+ 5. Confirm the effective `agent-default-model` and `web.searchProvider` values are unchanged from before installation.
73
+ 6. Run secret-free diagnostics:
66
74
 
67
75
  ```sh
68
76
  dsh plugin --profile web exec dsh-codex-connect doctor
69
77
  ```
70
78
 
71
- 6. If the user explicitly requests login, open **Settings → Plugins → Plugin configuration → Codex Connect**, or check `status` and then use `login` or `login --device-code`. OAuth approval belongs to the user.
79
+ 7. If the user explicitly requests login, open **Settings → Plugins → Plugin configuration → Codex Connect**, or check `status` and then use `login` or `login --device-code`. OAuth approval belongs to the user.
72
80
 
73
81
  Alpha 4.25 offers the same account actions in **Settings → Models → Openai-Codex**, plus a shared **More settings** dialog for model visibility, proxy, search, image, context-budget, and Auto-review controls. The original Plugin settings entry remains available; neither entry automatically starts login or changes model/search defaults.
74
82
 
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # git hash-object README.md docs/README.zh.md
5
- README.md: 65a8d94c57ae1bd869f5247f158cb1f8d221f4c0
6
- docs/README.zh.md: 1bdd0641d14d6c27da312ce9ac954fbb2664beae
5
+ README.md: c2fb988676d46f1d05d96b2e665df6c4b0505fce
6
+ docs/README.zh.md: 54386abdfc31e6d3a243892ee9f8508642033b59
package/README.md CHANGED
@@ -18,19 +18,23 @@ The setup and image-result screenshots in this English guide are captured from t
18
18
 
19
19
  ## Quick start (about five minutes)
20
20
 
21
- This quick start targets DSH `0.1.2-alpha.5` with Codex Connect Alpha 4.25. Check `dsh --version` first. For DSH `0.1.2-alpha.2`, `0.1.1-rc.2`, or `0.1.0-rc.7`, select the matching plugin version in [INSTALL.md](INSTALL.md). This guide uses the `web` profile; replace `web` with the name of the Harness profile you already use. From a DeepSeek Harness source checkout, prefix the commands with `pnpm`.
21
+ This quick start targets DSH `0.1.2-rc.1` with Codex Connect Alpha 4.26. Check `dsh --version` first. For DSH `0.1.2-alpha.5`, `0.1.2-alpha.2`, `0.1.1-rc.2`, or `0.1.0-rc.7`, select the matching plugin version in [INSTALL.md](INSTALL.md). This guide uses the `web` profile; replace `web` with the name of the Harness profile you already use. From a DeepSeek Harness source checkout, prefix the commands with `pnpm`.
22
22
 
23
23
  ### 1. Install the plugin into one profile
24
24
 
25
25
  ```sh
26
- dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.25
26
+ dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.26
27
27
  ```
28
28
 
29
29
  Expected result: the package is added to that profile. This does not change the profile's default model or global search route.
30
30
 
31
31
  Use the exact version above to keep the verified DSH and plugin pair reproducible. `alpha` is a moving npm tag, not a compatibility guarantee.
32
32
 
33
- ### What's new in Alpha 4.25
33
+ ### What's new in Alpha 4.26
34
+
35
+ - Add verified compatibility with DSH `0.1.2-rc.1`, including an isolated install, profile composition, diagnostics, model-catalog, and disposal canary.
36
+
37
+ ### Previously in Alpha 4.25
34
38
 
35
39
  - Show the exact 5-hour and weekly quota windows returned by ChatGPT for the selected Codex model. Missing windows stay hidden, Spark remains separate, and plan names do not suppress server data.
36
40
  - Separate verified compatibility from maintainer follow-up. The settings card no longer treats missing verification as a known runtime failure or recommends a plugin downgrade.
@@ -322,7 +326,7 @@ The command sends one fixed, synthetic no-op to the hidden reviewer through the
322
326
 
323
327
  ## Compatibility and security boundary
324
328
 
325
- - Alpha 4.25 is verified with DSH plugin API packages `0.1.2-alpha.5`, `@earendil-works/pi-ai` `^0.84.2` (resolved as `0.84.4` during verification), and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.24 remains an earlier verified choice for the same DSH version, Alpha 4.23 remains the verified choice for DSH `0.1.2-alpha.2`, and Alpha 4.21 remains verified with DSH `0.1.1-rc.2` and pi-ai `0.82.1`. [verified-compatibility.json](verified-compatibility.json) records the exact pairs; see [INSTALL.md](INSTALL.md) for installation commands.
329
+ - Alpha 4.26 is verified with DSH plugin API packages `0.1.2-rc.1`, `@earendil-works/pi-ai` `^0.84.2`, and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.25 remains the verified choice for DSH `0.1.2-alpha.5`, Alpha 4.23 remains the verified choice for DSH `0.1.2-alpha.2`, and Alpha 4.21 remains verified with DSH `0.1.1-rc.2` and pi-ai `0.82.1`. [verified-compatibility.json](verified-compatibility.json) records the exact pairs; see [INSTALL.md](INSTALL.md) for installation commands.
326
330
  - The new DSH client splits its former runtime into Session Controller, Settings, Store, and Renderer packages. Codex Connect uses those public interfaces for settings and image actions. DSH owns normalized preview encoding and dimensions; Codex Connect retains the exact original image separately.
327
331
  - Upgrade the DSH plugin API packages and `@earendil-works/pi-ai` as one group, then run `dsh-codex-connect doctor --json` and the compatibility check again. This contract does not make claims about future versions.
328
332
  - When the daily upstream check finds a new `latest` or `next` DSH candidate, it installs Codex Connect into an isolated profile, boots the installed model runtime without OAuth credentials, verifies model and reasoning-effort discovery, and confirms provider disposal. Live sign-in, quota, and model requests still require manual validation in the test profile.
@@ -4,7 +4,7 @@
4
4
  "node": "^22.19.0 || >=24.0.0"
5
5
  },
6
6
  "dshPluginApi": {
7
- "version": "0.1.2-alpha.5",
7
+ "version": "0.1.2-rc.1",
8
8
  "packages": [
9
9
  "@deepseek-ai/dsh-agent",
10
10
  "@deepseek-ai/dsh-atomic-write",
package/docs/README.zh.md CHANGED
@@ -18,19 +18,23 @@
18
18
 
19
19
  ## 五分钟快速开始
20
20
 
21
- 本快速指南适用于 DSH `0.1.2-alpha.5` 与 Codex Connect Alpha 4.25。请先运行 `dsh --version`。如果使用 DSH `0.1.2-alpha.2`、`0.1.1-rc.2` 或 `0.1.0-rc.7`,请在 [INSTALL.md](../INSTALL.md) 中选择匹配的插件版本。本指南使用 `web` profile;请把 `web` 替换成你已经在用的 Harness profile 名称。如果在 DeepSeek Harness 源码 checkout 中运行,请在命令前加 `pnpm`。
21
+ 本快速指南适用于 DSH `0.1.2-rc.1` 与 Codex Connect Alpha 4.26。请先运行 `dsh --version`。如果使用 DSH `0.1.2-alpha.5`、`0.1.2-alpha.2`、`0.1.1-rc.2` 或 `0.1.0-rc.7`,请在 [INSTALL.md](../INSTALL.md) 中选择匹配的插件版本。本指南使用 `web` profile;请把 `web` 替换成你已经在用的 Harness profile 名称。如果在 DeepSeek Harness 源码 checkout 中运行,请在命令前加 `pnpm`。
22
22
 
23
23
  ### 1. 将插件装入一个 profile
24
24
 
25
25
  ```sh
26
- dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.25
26
+ dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.26
27
27
  ```
28
28
 
29
29
  预期结果:包被加入该 profile。这个动作不会更改 profile 的默认模型或全局搜索路由。
30
30
 
31
31
  请使用上面的精确版本,确保已验证的 DSH 与插件组合可以复现。`alpha` 是会移动的 npm 标签,不是兼容性保证。
32
32
 
33
- ### Alpha 4.25 更新内容
33
+ ### Alpha 4.26 更新内容
34
+
35
+ - 新增对 DSH `0.1.2-rc.1` 的已验证兼容性,覆盖隔离安装、profile 组合、诊断、模型目录和卸载清理 canary。
36
+
37
+ ### Alpha 4.25 既有内容
34
38
 
35
39
  - 显示 ChatGPT 为当前 Codex 模型实际返回的 5 小时和每周额度窗口。缺少的窗口不会凭空出现,Spark 额度保持独立,也不会因套餐名称而隐藏服务端数据。
36
40
  - 将“当前组合是否已验证”和“是否需要提醒维护者”分开判断。缺少验证记录不再被说成已知运行失败,也不会建议降级插件。
@@ -324,7 +328,7 @@ dsh plugin --profile web exec dsh-codex-connect auto-review-probe --json
324
328
 
325
329
  ## 兼容性与安全边界
326
330
 
327
- - Alpha 4.25 已与 DSH 插件 API packages `0.1.2-alpha.5`、`@earendil-works/pi-ai` `^0.84.2`(验证时解析为 `0.84.4`)和 Node.js `^22.19.0 || >=24.0.0` 完成验证。Alpha 4.24 仍是同一 DSH 版本较早的已验证选择;Alpha 4.23 仍是 DSH `0.1.2-alpha.2` 的已验证选择;Alpha 4.21 仍与 DSH `0.1.1-rc.2` 和 pi-ai `0.82.1` 保持已验证状态。[verified-compatibility.json](../verified-compatibility.json) 记录精确组合;安装命令见 [INSTALL.md](../INSTALL.md)。
331
+ - Alpha 4.26 已与 DSH 插件 API packages `0.1.2-rc.1`、`@earendil-works/pi-ai` `^0.84.2` 和 Node.js `^22.19.0 || >=24.0.0` 完成验证。Alpha 4.25 仍是 DSH `0.1.2-alpha.5` 的已验证选择;Alpha 4.23 仍是 DSH `0.1.2-alpha.2` 的已验证选择;Alpha 4.21 仍与 DSH `0.1.1-rc.2` 和 pi-ai `0.82.1` 保持已验证状态。[verified-compatibility.json](../verified-compatibility.json) 记录精确组合;安装命令见 [INSTALL.md](../INSTALL.md)。
328
332
  - 新版 DSH 将原来的 client runtime 拆分为 Session Controller、Settings、Store 和 Renderer 包。Codex Connect 通过这些公开接口接入设置和图片操作。规范化预览的编码与尺寸由 DSH 决定;Codex Connect 另行保留字节完全一致的原图。
329
333
  - 升级时请将 DSH 插件 API packages 与 `@earendil-works/pi-ai` 作为一组升级,再运行 `dsh-codex-connect doctor --json` 和兼容性检查。本契约不对未来版本作判断。
330
334
  - 每日上游检查发现新的 DSH `latest` 或 `next` 候选版本时,会把 Codex Connect 安装到隔离 Profile 中,在没有 OAuth 凭据的情况下启动已安装的模型运行时,验证模型与推理强度发现,并确认提供方可被正确卸载。真实登录、额度和模型请求仍需在测试 Profile 中人工验证。
@@ -1,4 +1,4 @@
1
1
  # Bilingual-pair consistency record for the design document. Re-record with:
2
2
  # git hash-object docs/design.md docs/design.zh.md
3
- design.md: 86c383527dad511eb3458fb6c6389d5f8e9e9c16
4
- design.zh.md: f6112594d2c88ebab0f35e5f8f8dd2e949e611b5
3
+ design.md: 6a633995a2ca0261f6c01f2b17710bf64dca87c7
4
+ design.zh.md: 6bf9359f089f52ef6b8e821f9a9aeab784e020bd
package/docs/design.md CHANGED
@@ -10,7 +10,9 @@ The Host registers `llm-openai-codex` as the plugin-owned capability settings na
10
10
 
11
11
  ## OAuth persistence
12
12
 
13
- The plugin uses `$DSH_HOME/.openai-codex-auth.json`, separate from Codex CLI/Desktop state. The file format is strict and versioned. POSIX reads reject group/world-accessible files. Parent directories and files are created with owner-only modes, writes are atomic, and refresh mutations use the Harness cross-process file lock. Callers receive cloned credentials. Browser-origin trust is a separate `$DSH_HOME/.openai-codex-trusted-origins.json` sidecar with `version: 1`, `mode: "allowlist"`, and exact normalized HTTP(S) origins; it never contains OAuth material and is changed only by the standalone CLI.
13
+ The plugin uses `$DSH_HOME/.openai-codex-auth.json`, separate from Codex CLI/Desktop state. Format version 2 stores at most 16 OAuth accounts and names exactly one active account. Existing version-1 documents remain readable and migrate on the first credential mutation; that migration first creates the owner-only `.openai-codex-auth.json.v1-backup` rollback copy. A pre-existing rollback copy must be owner-only and match the current version-1 document or migration stops before replacing the main document. Removing any account or signing out deletes the rollback copy so removed credentials do not persist there. Documents are limited to 512 KiB. POSIX reads reject group/world-accessible files, non-regular files are rejected, writes are atomic, and mutations use the Harness cross-process file lock. Callers receive cloned credentials.
14
+
15
+ Browser account routes expose deterministic plugin-derived keys, local display labels, masked email addresses when present in the OAuth token, and active state. They never expose the provider's account id or token fields. Activation and deletion are serialized with browser OAuth. After OAuth resolution, quota, image-generation, and Auto-review requests resolve the account id stored with that exact access token, so a concurrent account switch cannot combine credentials from different accounts. Deleting the active account while another remains requires the caller to name the replacement; provider-global logout deletes every account. Browser-origin trust is a separate `$DSH_HOME/.openai-codex-trusted-origins.json` sidecar with `version: 1`, `mode: "allowlist"`, and exact normalized HTTP(S) origins; it never contains OAuth material and is changed only by the standalone CLI.
14
16
 
15
17
  The settings routes and CLI reuse the existing OAuth path and route names for migration compatibility. Only an explicit login operation emits an authorization URL or code. Browser requests are accepted by default only from loopback; a remote request must use an exact effective HTTP(S) origin in the current sidecar, must not carry cross-site Fetch Metadata, and must match any supplied Origin exactly. The sidecar is re-read for every request, and unknown fields or modes fail closed. A login challenge accepts only credential-free HTTPS URLs and fails closed after 30 seconds or when the provider finishes without a URL; logout and disposal cancel pending waiters. Status responses are redacted. Doctor uses `lstat` metadata and never opens the document.
16
18
 
@@ -26,4 +28,4 @@ Before registration the plugin checks current provider ids. An existing `openai-
26
28
 
27
29
  ## Compatibility boundary
28
30
 
29
- Alpha 4.25 pins Harness `0.1.2-alpha.5` development dependencies and follows its pi-ai range `^0.84.2`; the verified release lockfile selects pi-ai `0.84.4`. Supported Node.js remains `^22.19.0 || >=24.0.0`. The keyed `settings.plugin.item` integration remains, while client types come from their Session Controller, Settings, Store, and Renderer owners. The published compatibility record lists the exact Alpha 4.25 and DSH `0.1.2-alpha.5` pair. Backend eligibility, quotas, models, service-side context capacity, and protocol details remain controlled upstream. Tests use temporary OAuth documents and mocked network responses; CI does not perform real authentication.
31
+ Alpha 4.26 pins Harness `0.1.2-rc.1` development dependencies and follows its pi-ai range `^0.84.2`. Supported Node.js remains `^22.19.0 || >=24.0.0`. The keyed `settings.plugin.item` integration remains, while client types come from their Session Controller, Settings, Store, and Renderer owners. The published compatibility record lists the exact Alpha 4.26 and DSH `0.1.2-rc.1` pair. Backend eligibility, quotas, models, service-side context capacity, and protocol details remain controlled upstream. Tests use temporary OAuth documents and mocked network responses; CI does not perform real authentication.
package/docs/design.zh.md CHANGED
@@ -10,7 +10,9 @@ Host 将 `llm-openai-codex` 注册为插件自有的能力 settings namespace。
10
10
 
11
11
  ## OAuth 持久化
12
12
 
13
- 插件使用 `$DSH_HOME/.openai-codex-auth.json`,与 Codex CLI/Desktop 状态分离。文件格式严格且有版本号;POSIX 上会拒绝组/其他用户可读文件。父目录和文件按仅所有者权限创建,写入采用原子替换,刷新修改使用 Harness 跨进程文件锁,返回给调用方的是凭据副本。浏览器 origin 授权单独存放于 `$DSH_HOME/.openai-codex-trusted-origins.json`,格式为 `version: 1`、`mode: "allowlist"` 和规范化的精确 HTTP(S) origin;其中不含 OAuth 内容,且只能通过独立 CLI 修改。
13
+ 插件使用 `$DSH_HOME/.openai-codex-auth.json`,与 Codex CLI/Desktop 状态分离。格式 version 2 最多保存 16 个 OAuth 账户,并明确指定唯一当前账户。原 version 1 文件仍可读取,第一次修改凭据时才迁移;迁移前会先生成仅所有者可读的 `.openai-codex-auth.json.v1-backup` 回退副本。若该副本已存在,它必须仅所有者可读且与当前 version 1 文件一致,否则迁移会在替换主文件前停止。移除任一账户或执行退出登录时会删除该副本,避免已移除凭据继续留存。文档大小上限为 512 KiB。POSIX 上拒绝组/其他用户可读文件,也拒绝非普通文件;写入采用原子替换,所有修改使用 Harness 跨进程文件锁,返回给调用方的是凭据副本。
14
+
15
+ 浏览器账户路由只返回插件派生的稳定键、本地显示名称、OAuth token 中存在邮箱时的脱敏地址以及当前账户状态,不返回 provider 原始 account id 或 token 字段。账户切换和删除与浏览器 OAuth 串行执行;OAuth 解析完成后,额度、图片生成和 Auto-review 请求会查找与该 access token 精确配套的 account id,因此并发切换账户不会混用两个账户的凭据。仍有其他账户时,删除当前账户必须由调用方明确指定接替账户;provider 级退出登录会删除全部账户。浏览器 origin 授权单独存放于 `$DSH_HOME/.openai-codex-trusted-origins.json`,格式为 `version: 1`、`mode: "allowlist"` 和规范化的精确 HTTP(S) origin;其中不含 OAuth 内容,且只能通过独立 CLI 修改。
14
16
 
15
17
  为兼容迁移,设置页路由、OAuth 路径和 provider id 不改名。浏览器请求默认只允许 loopback;远程请求必须使用当前 sidecar 中的精确有效 HTTP(S) origin,不能带 cross-site Fetch Metadata,若带 Origin 还必须精确匹配。每次请求都会重新读取 sidecar;未知字段或错误 mode 会快速失败。登录挑战只接受不含凭据的 HTTPS 地址;30 秒内未得到地址、provider 已结束但没有地址、退出登录或插件卸载时,所有 waiter 都会被清理。只有显式登录会输出授权 URL 或代码;状态输出会脱敏。doctor 只用 `lstat` 检查元数据,不打开文件。
16
18
 
@@ -22,4 +24,4 @@ Host 将 `llm-openai-codex` 注册为插件自有的能力 settings namespace。
22
24
 
23
25
  注册前检查现有 provider id;发现 `openai-codex` 已被占用时,给出旧 bundle 或手动 provider 配置的定向迁移提示。boot-free CLI doctor 只报告包/运行时版本、OAuth 路径元数据、能力默认值和安全提示。
24
26
 
25
- Alpha 4.25 固定使用 Harness `0.1.2-alpha.5` 开发依赖,并跟随其 pi-ai 版本范围 `^0.84.2`;已验证的发布锁文件选择 pi-ai `0.84.4`。Node.js 支持范围仍为 `^22.19.0 || >=24.0.0`。keyed `settings.plugin.item` 集成保持不变,客户端类型继续从 Session Controller、Settings、Store 和 Renderer 的所属包导入。已发布兼容性记录列出 Alpha 4.25 与 DSH `0.1.2-alpha.5` 的精确组合。资格、额度、模型、服务端上下文容量和后端协议仍由上游控制。测试仅使用临时 OAuth 文档和模拟网络响应,CI 不执行真实认证。
27
+ Alpha 4.26 固定使用 Harness `0.1.2-rc.1` 开发依赖,并跟随其 pi-ai 版本范围 `^0.84.2`。Node.js 支持范围仍为 `^22.19.0 || >=24.0.0`。keyed `settings.plugin.item` 集成保持不变,客户端类型继续从 Session Controller、Settings、Store 和 Renderer 的所属包导入。已发布兼容性记录列出 Alpha 4.26 与 DSH `0.1.2-rc.1` 的精确组合。资格、额度、模型、服务端上下文容量和后端协议仍由上游控制。测试仅使用临时 OAuth 文档和模拟网络响应,CI 不执行真实认证。
package/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { At as logoutOpenAICodex, Ft as OpenAICodexCredentialStore, H as DSH_PLUGIN_API_PACKAGES, It as openAICodexAuthPath, Mt as openAICodexModelCatalog, N as CODEX_CONNECT_VERSION, Pt as OPENAI_CODEX_PROVIDER, Tt as normalizeTrustedOrigin, X as readInstalledPackageVersion, Y as evaluateCompatibility, c as migrateOpenAICodexSearchHistory, d as OPENAI_CODEX_BASE_URL, gt as normalizeOpenAICodexProxyUrl, j as diagnoseOpenAICodex, jt as openAICodexAuthStatus, kt as loginOpenAICodex, l as CODEX_AUTO_REVIEW_MODEL, u as probeCodexAutoReview, wt as OpenAICodexTrustedOriginsStore } from "./src-Di2xxpDv.js";
2
+ import { At as logoutOpenAICodex, H as DSH_PLUGIN_API_PACKAGES, Lt as OPENAI_CODEX_PROVIDER, Mt as openAICodexModelCatalog, N as CODEX_CONNECT_VERSION, Rt as OpenAICodexCredentialStore, Tt as normalizeTrustedOrigin, X as readInstalledPackageVersion, Y as evaluateCompatibility, c as migrateOpenAICodexSearchHistory, d as OPENAI_CODEX_BASE_URL, gt as normalizeOpenAICodexProxyUrl, j as diagnoseOpenAICodex, jt as openAICodexAuthStatus, kt as loginOpenAICodex, l as CODEX_AUTO_REVIEW_MODEL, u as probeCodexAutoReview, wt as OpenAICodexTrustedOriginsStore, zt as openAICodexAuthPath } from "./src-FSN1b9Tk.js";
3
3
  import { createHash } from "node:crypto";
4
4
  import { Agent, ProxyAgent, fetch } from "undici";
5
5
  import { fileURLToPath } from "node:url";
@@ -208,7 +208,7 @@ var CodexCapabilityDiagnostics = class {
208
208
  packageVersions: versions
209
209
  });
210
210
  const missing = Object.values(versions).some((value) => value === null) || !/^v?\d+\.\d+\.\d+$/u.test(versions["node"] ?? "");
211
- const runtime = DSH_PLUGIN_API_PACKAGES.some((name) => versions[name] !== null && versions[name] !== "0.1.2-alpha.5") || compatibility.status === "incompatible" ? result("rejected", "declared-version-mismatch", "Use DSH API 0.1.2-alpha.5 and pi-ai ^0.84.2 together. DSH 0.1.0-rc.7 requires Codex Connect 0.1.0-alpha.4.14.") : missing || compatibility.status === "unknown" ? result("unknown", "version-metadata-unavailable", "Run this command from the plugin installation in the intended profile.") : result("supported", "declared-host-versions-match", "Host package versions satisfy the declared requirements; this is not a live profile or browser compatibility test.");
211
+ const runtime = DSH_PLUGIN_API_PACKAGES.some((name) => versions[name] !== null && versions[name] !== "0.1.2-rc.1") || compatibility.status === "incompatible" ? result("rejected", "declared-version-mismatch", "Use DSH API 0.1.2-rc.1 and pi-ai ^0.84.2 together. DSH 0.1.0-rc.7 requires Codex Connect 0.1.0-alpha.4.14.") : missing || compatibility.status === "unknown" ? result("unknown", "version-metadata-unavailable", "Run this command from the plugin installation in the intended profile.") : result("supported", "declared-host-versions-match", "Host package versions satisfy the declared requirements; this is not a live profile or browser compatibility test.");
212
212
  const model = this.deps.catalog().find((item) => item.id === request.model)?.id ?? null;
213
213
  const unknownNetwork = result("unknown", "not-probed", "Run capabilities --model <catalog-id> --probe explicitly; this sends a fixed short request and may consume quota.");
214
214
  const checks = {
package/lib/client.js CHANGED
@@ -5725,7 +5725,7 @@ window.__ModuleLoader__.load({
5725
5725
  }
5726
5726
  //#endregion
5727
5727
  //#region src/version.ts
5728
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.25";
5728
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.26";
5729
5729
  //#endregion
5730
5730
  //#region src/client/OpenAICodexModelsCard.tsx
5731
5731
  /** Compact Models account entry with quota disclosure and shared configuration. */
package/lib/index.d.ts CHANGED
@@ -1,13 +1,29 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
- import { AuthInteraction, Credential, CredentialInfo, CredentialStore } from "@earendil-works/pi-ai";
2
+ import { AuthInteraction, Credential, CredentialInfo, CredentialStore, OAuthCredential } from "@earendil-works/pi-ai";
3
3
  import "@deepseek-ai/dsh-tools";
4
4
  import { Context, Service } from "@deepseek-ai/cordis";
5
5
  import { WebSearchProvider, WebSearchRequest, WebSearchResult } from "@deepseek-ai/dsh-web";
6
+ //#region src/account-profile.d.ts
7
+ type OpenAICodexAccountProfileSource = 'oauth' | 'generated';
8
+ //#endregion
6
9
  //#region src/store.d.ts
7
10
  /** Provider route and pi-ai provider id owned by this bundle. */
8
11
  declare const OPENAI_CODEX_PROVIDER = "openai-codex";
9
12
  /** Basename of the OAuth document inside the Harness home. */
10
13
  declare const OPENAI_CODEX_AUTH_FILENAME = ".openai-codex-auth.json";
14
+ /** Maximum number of stored OpenAI Codex accounts. */
15
+ declare const OPENAI_CODEX_ACCOUNT_LIMIT = 16;
16
+ /** Maximum serialized credential document size. */
17
+ declare const OPENAI_CODEX_AUTH_DOCUMENT_LIMIT: number;
18
+ /** Suffix used for the one-time version-1 rollback copy. */
19
+ declare const OPENAI_CODEX_AUTH_V1_BACKUP_SUFFIX = ".v1-backup";
20
+ interface OpenAICodexAccountSummary {
21
+ accountKey: string;
22
+ displayName: string;
23
+ maskedEmail?: string;
24
+ profileSource: OpenAICodexAccountProfileSource;
25
+ active: boolean;
26
+ }
11
27
  /**
12
28
  * Resolve the default OAuth document path.
13
29
  * @param dshHome - optional Harness-home override.
@@ -18,16 +34,28 @@ declare function openAICodexAuthPath(dshHome?: string): string;
18
34
  declare class OpenAICodexCredentialStore implements CredentialStore {
19
35
  /** Absolute credential document path. */
20
36
  readonly filename: string;
37
+ /** Owner-only version-1 rollback copy, created at the first migration write. */
38
+ readonly version1BackupFilename: string;
21
39
  /**
22
40
  * @param filename - explicit document path, defaulting under `$DSH_HOME`.
23
41
  */
24
42
  constructor(filename?: string);
25
43
  /** Read and validate the current document without acquiring the writer lock. */
26
- private readCurrent;
44
+ private readDocument;
45
+ private readDocumentAt;
46
+ private writeDocument;
27
47
  /** @inheritdoc */
28
48
  read(providerId: string): Promise<Credential | undefined>;
29
49
  /** @inheritdoc */
30
50
  list(): Promise<readonly CredentialInfo[]>;
51
+ /** List browser-safe account summaries without exposing provider account ids. */
52
+ accounts(): Promise<readonly OpenAICodexAccountSummary[]>;
53
+ /** Resolve the account id stored with one exact access token. */
54
+ accountIdForAccess(access: string): Promise<string | undefined>;
55
+ /** Select a stored account using its browser-safe key. */
56
+ activate(selectedAccountKey: string): Promise<OAuthCredential>;
57
+ /** Remove one account; active removal requires an explicit stored replacement. */
58
+ removeAccount(selectedAccountKey: string, replacementAccountKey?: string): Promise<void>;
31
59
  /** @inheritdoc */
32
60
  modify(providerId: string, fn: (current: Credential | undefined) => Promise<Credential | undefined>): Promise<Credential | undefined>;
33
61
  /** @inheritdoc */
@@ -180,7 +208,7 @@ declare const IMAGE_GENERATE_TOOL_NAME = "codex_connect_image_generate";
180
208
  //#region src/compatibility.d.ts
181
209
  declare const COMPATIBILITY_SCHEMA_VERSION: 1;
182
210
  declare const SUPPORTED_NODE_RANGE = "^22.19.0 || >=24.0.0";
183
- declare const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.2-alpha.5";
211
+ declare const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.2-rc.1";
184
212
  declare const SUPPORTED_PI_AI_RANGE = "^0.84.2";
185
213
  declare const PI_AI_PACKAGE = "@earendil-works/pi-ai";
186
214
  declare const DSH_PLUGIN_API_PACKAGES: readonly ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-attachment", "@deepseek-ai/dsh-home-paths", "@deepseek-ai/dsh-host-webserver", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-pi-ai", "@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-util-values", "@deepseek-ai/dsh-web"];
@@ -224,7 +252,7 @@ declare const COMPATIBILITY_CONTRACT: {
224
252
  readonly node: "^22.19.0 || >=24.0.0";
225
253
  };
226
254
  readonly dshPluginApi: {
227
- readonly version: "0.1.2-alpha.5";
255
+ readonly version: "0.1.2-rc.1";
228
256
  readonly packages: readonly ["@deepseek-ai/dsh-agent", "@deepseek-ai/dsh-atomic-write", "@deepseek-ai/dsh-attachment", "@deepseek-ai/dsh-home-paths", "@deepseek-ai/dsh-host-webserver", "@deepseek-ai/dsh-invariants", "@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-pi-ai", "@deepseek-ai/dsh-fs", "@deepseek-ai/dsh-session", "@deepseek-ai/dsh-settings", "@deepseek-ai/dsh-tools", "@deepseek-ai/dsh-util-values", "@deepseek-ai/dsh-web"];
229
257
  };
230
258
  readonly piAi: {
@@ -731,4 +759,4 @@ declare const Config: z<Config>;
731
759
  */
732
760
  declare function apply(ctx: Context, config: Config): void;
733
761
  //#endregion
734
- export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, type CompatibilityDetectionOptions, type CompatibilityEntry, type CompatibilityEvaluationInput, type CompatibilityPackageName, type CompatibilityReport, type CompatibilityStatus, Config, DEFAULT_OPENAI_CODEX_PROXY_URL, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, FastModeRegistry, FastModeRegistry as OpenAICodexFastModeRegistry, type GeneratedImagePayload, IMAGE_GENERATE_TOOL_NAME, type ImageGenerationRequest, type ImageGenerationResponse, type ImageRequestContext, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, OPENAI_CODEX_FAST_MODE_PATH, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, OPENAI_CODEX_IMAGE_GENERATION_URL, OPENAI_CODEX_IMAGE_MAX_COUNT, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, OPENAI_CODEX_LOCAL_PROXY_CANDIDATES, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_PROXY_CANDIDATE_LIMIT, OPENAI_CODEX_PROXY_DETECT_PATH, OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS, OPENAI_CODEX_PROXY_PROBE_URL, OPENAI_CODEX_PROXY_TEST_PATH, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_TRANSPORT_API_VERSION, OPENAI_CODEX_TRANSPORT_ERROR_CODES, OPENAI_CODEX_TRANSPORT_SERVICE, OPENAI_CODEX_UPDATE_PATH, OPENAI_CODEX_USAGE_URL, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexHistoryMigrationFile, type OpenAICodexHistoryMigrationOptions, type OpenAICodexHistoryMigrationResult, type OpenAICodexIndividualLimit, OpenAICodexProxyManager, type OpenAICodexProxyProbeClassification, type OpenAICodexProxyProbeResult, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, OpenAICodexTransport, OpenAICodexTransportError, type OpenAICodexTransportErrorCode, type OpenAICodexTransportV1, type OpenAICodexUpdateResult, type OpenAICodexUsage, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_RANGE, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, checkForOpenAICodexUpdate, compareOpenAICodexVersions, decodeOpenAICodexSettings, detectCompatibility, detectOpenAICodexProxies, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, isValidOpenAICodexContextWindowOverrides, isValidOpenAICodexProxyUrl, listOpenAICodexProxyCandidates, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, migrateOpenAICodexSearchHistory, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUpdateResult, parseOpenAICodexUsage, parseOpenAICodexVersion, readOpenAICodexRateLimits, resolveOpenAICodexProxyUrl, resolveOpenAICodexSettings };
762
+ export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, type CompatibilityDetectionOptions, type CompatibilityEntry, type CompatibilityEvaluationInput, type CompatibilityPackageName, type CompatibilityReport, type CompatibilityStatus, Config, DEFAULT_OPENAI_CODEX_PROXY_URL, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, FastModeRegistry, FastModeRegistry as OpenAICodexFastModeRegistry, type GeneratedImagePayload, IMAGE_GENERATE_TOOL_NAME, type ImageGenerationRequest, type ImageGenerationResponse, type ImageRequestContext, OPENAI_CODEX_ACCOUNT_LIMIT, OPENAI_CODEX_AUTH_DOCUMENT_LIMIT, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_AUTH_V1_BACKUP_SUFFIX, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, OPENAI_CODEX_FAST_MODE_PATH, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, OPENAI_CODEX_IMAGE_GENERATION_URL, OPENAI_CODEX_IMAGE_MAX_COUNT, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, OPENAI_CODEX_LOCAL_PROXY_CANDIDATES, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_PROXY_CANDIDATE_LIMIT, OPENAI_CODEX_PROXY_DETECT_PATH, OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS, OPENAI_CODEX_PROXY_PROBE_URL, OPENAI_CODEX_PROXY_TEST_PATH, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_TRANSPORT_API_VERSION, OPENAI_CODEX_TRANSPORT_ERROR_CODES, OPENAI_CODEX_TRANSPORT_SERVICE, OPENAI_CODEX_UPDATE_PATH, OPENAI_CODEX_USAGE_URL, type OpenAICodexAccountSummary, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexHistoryMigrationFile, type OpenAICodexHistoryMigrationOptions, type OpenAICodexHistoryMigrationResult, type OpenAICodexIndividualLimit, OpenAICodexProxyManager, type OpenAICodexProxyProbeClassification, type OpenAICodexProxyProbeResult, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, OpenAICodexTransport, OpenAICodexTransportError, type OpenAICodexTransportErrorCode, type OpenAICodexTransportV1, type OpenAICodexUpdateResult, type OpenAICodexUsage, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_RANGE, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, checkForOpenAICodexUpdate, compareOpenAICodexVersions, decodeOpenAICodexSettings, detectCompatibility, detectOpenAICodexProxies, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, isValidOpenAICodexContextWindowOverrides, isValidOpenAICodexProxyUrl, listOpenAICodexProxyCandidates, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, migrateOpenAICodexSearchHistory, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUpdateResult, parseOpenAICodexUsage, parseOpenAICodexVersion, readOpenAICodexRateLimits, resolveOpenAICodexProxyUrl, resolveOpenAICodexSettings };
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as OPENAI_CODEX_LOCAL_PROXY_CANDIDATES, A as assertNoOpenAICodexProviderConflict, At as logoutOpenAICodex, B as COMPATIBILITY_PACKAGES, C as OPENAI_CODEX_TRANSPORT_ERROR_CODES, Ct as isFastModeSessionId, D as isOpenAICodexTransportError, Dt as parseOpenAICodexUsage, E as OpenAICodexTransportError, Et as OPENAI_CODEX_USAGE_URL, F as checkForOpenAICodexUpdate, Ft as OpenAICodexCredentialStore, G as SUPPORTED_NODE_RANGE, H as DSH_PLUGIN_API_PACKAGES, I as compareOpenAICodexVersions, It as openAICodexAuthPath, J as detectCompatibility, K as SUPPORTED_PI_AI_RANGE, L as parseOpenAICodexUpdateResult, M as openAICodexConflictMessage, Nt as OPENAI_CODEX_AUTH_FILENAME, O as VIEW_IMAGE_TOOL_NAME, Ot as readOpenAICodexRateLimits, P as OPENAI_CODEX_UPDATE_PATH, Pt as OPENAI_CODEX_PROVIDER, Q as OPENAI_CODEX_PROXY_TEST_PATH, R as parseOpenAICodexVersion, S as OPENAI_CODEX_TRANSPORT_API_VERSION, St as OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, T as OpenAICodexTransport, U as PI_AI_PACKAGE, V as COMPATIBILITY_SCHEMA_VERSION, W as SUPPORTED_DSH_PLUGIN_API_VERSION, Y as evaluateCompatibility, Z as OPENAI_CODEX_PROXY_DETECT_PATH, _ as OPENAI_CODEX_IMAGE_MAX_COUNT, _t as resolveOpenAICodexProxyUrl, a as name, at as listOpenAICodexProxyCandidates, b as OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, bt as FastModeRegistry, c as migrateOpenAICodexSearchHistory, ct as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, d as OPENAI_CODEX_BASE_URL, dt as DEFAULT_OPENAI_CODEX_SETTINGS, et as OPENAI_CODEX_PROXY_CANDIDATE_LIMIT, f as OPENAI_CODEX_SEARCH_PROVIDER, ft as OPENAI_CODEX_SETTINGS_NAMESPACE, g as OPENAI_CODEX_IMAGE_GENERATION_URL, h as mapOpenAICodexSearchResponse, ht as isValidOpenAICodexProxyUrl, i as inject, it as detectOpenAICodexProxies, j as diagnoseOpenAICodex, jt as openAICodexAuthStatus, k as IMAGE_GENERATE_TOOL_NAME, kt as loginOpenAICodex, lt as DEFAULT_OPENAI_CODEX_SEARCH_MODE, m as OpenAICodexSearchProvider, mt as isValidOpenAICodexContextWindowOverrides, n as OPENAI_CODEX_SETTINGS_NS, nt as OPENAI_CODEX_PROXY_PROBE_URL, o as OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, ot as DEFAULT_OPENAI_CODEX_PROXY_URL, p as OPENAI_CODEX_SEARCH_URL, pt as decodeOpenAICodexSettings, q as assessCompatibility, r as apply, rt as OpenAICodexProxyManager, s as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, st as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, t as Config, tt as OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS, ut as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, v as OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, vt as resolveOpenAICodexSettings, w as OPENAI_CODEX_TRANSPORT_SERVICE, x as OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, xt as OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, y as OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, yt as OPENAI_CODEX_FAST_MODE_PATH, z as COMPATIBILITY_CONTRACT } from "./src-Di2xxpDv.js";
2
- export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, Config, DEFAULT_OPENAI_CODEX_PROXY_URL, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, FastModeRegistry, FastModeRegistry as OpenAICodexFastModeRegistry, IMAGE_GENERATE_TOOL_NAME, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, OPENAI_CODEX_FAST_MODE_PATH, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, OPENAI_CODEX_IMAGE_GENERATION_URL, OPENAI_CODEX_IMAGE_MAX_COUNT, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, OPENAI_CODEX_LOCAL_PROXY_CANDIDATES, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_PROXY_CANDIDATE_LIMIT, OPENAI_CODEX_PROXY_DETECT_PATH, OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS, OPENAI_CODEX_PROXY_PROBE_URL, OPENAI_CODEX_PROXY_TEST_PATH, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_TRANSPORT_API_VERSION, OPENAI_CODEX_TRANSPORT_ERROR_CODES, OPENAI_CODEX_TRANSPORT_SERVICE, OPENAI_CODEX_UPDATE_PATH, OPENAI_CODEX_USAGE_URL, OpenAICodexCredentialStore, OpenAICodexProxyManager, OpenAICodexSearchProvider, OpenAICodexTransport, OpenAICodexTransportError, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_RANGE, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, checkForOpenAICodexUpdate, compareOpenAICodexVersions, decodeOpenAICodexSettings, detectCompatibility, detectOpenAICodexProxies, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, isValidOpenAICodexContextWindowOverrides, isValidOpenAICodexProxyUrl, listOpenAICodexProxyCandidates, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, migrateOpenAICodexSearchHistory, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUpdateResult, parseOpenAICodexUsage, parseOpenAICodexVersion, readOpenAICodexRateLimits, resolveOpenAICodexProxyUrl, resolveOpenAICodexSettings };
1
+ import { $ as OPENAI_CODEX_LOCAL_PROXY_CANDIDATES, A as assertNoOpenAICodexProviderConflict, At as logoutOpenAICodex, B as COMPATIBILITY_PACKAGES, C as OPENAI_CODEX_TRANSPORT_ERROR_CODES, Ct as isFastModeSessionId, D as isOpenAICodexTransportError, Dt as parseOpenAICodexUsage, E as OpenAICodexTransportError, Et as OPENAI_CODEX_USAGE_URL, F as checkForOpenAICodexUpdate, Ft as OPENAI_CODEX_AUTH_FILENAME, G as SUPPORTED_NODE_RANGE, H as DSH_PLUGIN_API_PACKAGES, I as compareOpenAICodexVersions, It as OPENAI_CODEX_AUTH_V1_BACKUP_SUFFIX, J as detectCompatibility, K as SUPPORTED_PI_AI_RANGE, L as parseOpenAICodexUpdateResult, Lt as OPENAI_CODEX_PROVIDER, M as openAICodexConflictMessage, Nt as OPENAI_CODEX_ACCOUNT_LIMIT, O as VIEW_IMAGE_TOOL_NAME, Ot as readOpenAICodexRateLimits, P as OPENAI_CODEX_UPDATE_PATH, Pt as OPENAI_CODEX_AUTH_DOCUMENT_LIMIT, Q as OPENAI_CODEX_PROXY_TEST_PATH, R as parseOpenAICodexVersion, Rt as OpenAICodexCredentialStore, S as OPENAI_CODEX_TRANSPORT_API_VERSION, St as OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, T as OpenAICodexTransport, U as PI_AI_PACKAGE, V as COMPATIBILITY_SCHEMA_VERSION, W as SUPPORTED_DSH_PLUGIN_API_VERSION, Y as evaluateCompatibility, Z as OPENAI_CODEX_PROXY_DETECT_PATH, _ as OPENAI_CODEX_IMAGE_MAX_COUNT, _t as resolveOpenAICodexProxyUrl, a as name, at as listOpenAICodexProxyCandidates, b as OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, bt as FastModeRegistry, c as migrateOpenAICodexSearchHistory, ct as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, d as OPENAI_CODEX_BASE_URL, dt as DEFAULT_OPENAI_CODEX_SETTINGS, et as OPENAI_CODEX_PROXY_CANDIDATE_LIMIT, f as OPENAI_CODEX_SEARCH_PROVIDER, ft as OPENAI_CODEX_SETTINGS_NAMESPACE, g as OPENAI_CODEX_IMAGE_GENERATION_URL, h as mapOpenAICodexSearchResponse, ht as isValidOpenAICodexProxyUrl, i as inject, it as detectOpenAICodexProxies, j as diagnoseOpenAICodex, jt as openAICodexAuthStatus, k as IMAGE_GENERATE_TOOL_NAME, kt as loginOpenAICodex, lt as DEFAULT_OPENAI_CODEX_SEARCH_MODE, m as OpenAICodexSearchProvider, mt as isValidOpenAICodexContextWindowOverrides, n as OPENAI_CODEX_SETTINGS_NS, nt as OPENAI_CODEX_PROXY_PROBE_URL, o as OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, ot as DEFAULT_OPENAI_CODEX_PROXY_URL, p as OPENAI_CODEX_SEARCH_URL, pt as decodeOpenAICodexSettings, q as assessCompatibility, r as apply, rt as OpenAICodexProxyManager, s as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, st as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, t as Config, tt as OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS, ut as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, v as OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, vt as resolveOpenAICodexSettings, w as OPENAI_CODEX_TRANSPORT_SERVICE, x as OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, xt as OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, y as OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, yt as OPENAI_CODEX_FAST_MODE_PATH, z as COMPATIBILITY_CONTRACT, zt as openAICodexAuthPath } from "./src-FSN1b9Tk.js";
2
+ export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, Config, DEFAULT_OPENAI_CODEX_PROXY_URL, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, FastModeRegistry, FastModeRegistry as OpenAICodexFastModeRegistry, IMAGE_GENERATE_TOOL_NAME, OPENAI_CODEX_ACCOUNT_LIMIT, OPENAI_CODEX_AUTH_DOCUMENT_LIMIT, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_AUTH_V1_BACKUP_SUFFIX, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, OPENAI_CODEX_FAST_MODE_PATH, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, OPENAI_CODEX_IMAGE_GENERATION_URL, OPENAI_CODEX_IMAGE_MAX_COUNT, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, OPENAI_CODEX_LOCAL_PROXY_CANDIDATES, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_PROXY_CANDIDATE_LIMIT, OPENAI_CODEX_PROXY_DETECT_PATH, OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS, OPENAI_CODEX_PROXY_PROBE_URL, OPENAI_CODEX_PROXY_TEST_PATH, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_TRANSPORT_API_VERSION, OPENAI_CODEX_TRANSPORT_ERROR_CODES, OPENAI_CODEX_TRANSPORT_SERVICE, OPENAI_CODEX_UPDATE_PATH, OPENAI_CODEX_USAGE_URL, OpenAICodexCredentialStore, OpenAICodexProxyManager, OpenAICodexSearchProvider, OpenAICodexTransport, OpenAICodexTransportError, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_RANGE, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, checkForOpenAICodexUpdate, compareOpenAICodexVersions, decodeOpenAICodexSettings, detectCompatibility, detectOpenAICodexProxies, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, isValidOpenAICodexContextWindowOverrides, isValidOpenAICodexProxyUrl, listOpenAICodexProxyCandidates, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, migrateOpenAICodexSearchHistory, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUpdateResult, parseOpenAICodexUsage, parseOpenAICodexVersion, readOpenAICodexRateLimits, resolveOpenAICodexProxyUrl, resolveOpenAICodexSettings };
@@ -9,6 +9,7 @@ import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
9
9
  import { link, lstat, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
10
10
  import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
11
11
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
12
+ import { Buffer as Buffer$1 } from "node:buffer";
12
13
  import { AsyncLocalStorage } from "node:async_hooks";
13
14
  import { Agent, Dispatcher, ProxyAgent, fetch as fetch$1, getGlobalDispatcher, setGlobalDispatcher } from "undici";
14
15
  import { fileURLToPath } from "node:url";
@@ -23,6 +24,55 @@ import { Service } from "@deepseek-ai/cordis";
23
24
  import { constants } from "node:fs";
24
25
  import { WebError } from "@deepseek-ai/dsh-web";
25
26
  import { constants as constants$1, zstdCompressSync, zstdDecompressSync } from "node:zlib";
27
+ //#region src/account-profile.ts
28
+ /** Safe browser labels derived locally from OpenAI Codex OAuth credentials. */
29
+ const PROFILE_CLAIM = "https://api.openai.com/profile";
30
+ const MAX_JWT_PAYLOAD_LENGTH = 65536;
31
+ const MAX_LABEL_LENGTH = 128;
32
+ const MAX_EMAIL_LENGTH = 320;
33
+ function boundedText$1(value, maximum) {
34
+ if (typeof value !== "string") return void 0;
35
+ const normalized = value.replace(/[\u0000-\u001f\u007f]/gu, "").trim();
36
+ return normalized.length > 0 && normalized.length <= maximum ? normalized : void 0;
37
+ }
38
+ function maskEmail(email) {
39
+ const separator = email.lastIndexOf("@");
40
+ if (separator <= 0 || separator === email.length - 1) return "••••";
41
+ const local = email.slice(0, separator);
42
+ const domain = email.slice(separator + 1);
43
+ return `${local.slice(0, Math.min(2, local.length))}••@${domain}`;
44
+ }
45
+ function decodeOauthProfile(access) {
46
+ const payload = access.split(".")[1];
47
+ if (payload === void 0 || payload.length === 0 || payload.length > MAX_JWT_PAYLOAD_LENGTH) return {};
48
+ try {
49
+ const decoded = JSON.parse(Buffer$1.from(payload, "base64url").toString("utf8"));
50
+ if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) return {};
51
+ const profile = decoded[PROFILE_CLAIM];
52
+ if (typeof profile !== "object" || profile === null || Array.isArray(profile)) return {};
53
+ const record = profile;
54
+ const name = boundedText$1(record["name"], MAX_LABEL_LENGTH);
55
+ const email = boundedText$1(record["email"], MAX_EMAIL_LENGTH);
56
+ return {
57
+ ...name === void 0 ? {} : { name },
58
+ ...email === void 0 ? {} : { email }
59
+ };
60
+ } catch {
61
+ return {};
62
+ }
63
+ }
64
+ /** Resolve display-only labels without network access or provider account identifiers. */
65
+ function resolveOpenAICodexAccountProfiles(credentials) {
66
+ return credentials.map((credential, index) => {
67
+ const oauth = decodeOauthProfile(credential.access);
68
+ return {
69
+ displayName: oauth.name ?? `ChatGPT account ${String(index + 1)}`,
70
+ ...oauth.email === void 0 ? {} : { maskedEmail: maskEmail(oauth.email) },
71
+ source: oauth.name === void 0 && oauth.email === void 0 ? "generated" : "oauth"
72
+ };
73
+ });
74
+ }
75
+ //#endregion
26
76
  //#region src/store.ts
27
77
  /**
28
78
  * Owner-only persistent OAuth credential storage for the OpenAI Codex bundle.
@@ -32,40 +82,28 @@ import { constants as constants$1, zstdCompressSync, zstdDecompressSync } from "
32
82
  const OPENAI_CODEX_PROVIDER = "openai-codex";
33
83
  /** Basename of the OAuth document inside the Harness home. */
34
84
  const OPENAI_CODEX_AUTH_FILENAME = ".openai-codex-auth.json";
35
- /** Current on-disk format; pre-release readers reject every other version. */
36
- const AUTH_FORMAT_VERSION = 1;
85
+ /** Current multi-account on-disk format. */
86
+ const AUTH_FORMAT_VERSION = 2;
87
+ /** Maximum number of stored OpenAI Codex accounts. */
88
+ const OPENAI_CODEX_ACCOUNT_LIMIT = 16;
89
+ /** Maximum serialized credential document size. */
90
+ const OPENAI_CODEX_AUTH_DOCUMENT_LIMIT = 524288;
91
+ /** Suffix used for the one-time version-1 rollback copy. */
92
+ const OPENAI_CODEX_AUTH_V1_BACKUP_SUFFIX = ".v1-backup";
37
93
  /** Whether a filesystem error reports an absent path. */
38
94
  function isENOENT$1(error) {
39
95
  return error?.code === "ENOENT";
40
96
  }
41
97
  /** Reject a credential document readable by another POSIX user. */
42
- async function assertOwnerOnly$1(filename) {
43
- let mode;
44
- try {
45
- mode = (await stat(filename)).mode;
46
- } catch (error) {
47
- if (isENOENT$1(error)) return;
48
- throw error;
49
- }
98
+ function assertOwnerOnly$1(filename, mode) {
50
99
  /* v8 ignore next -- native Windows coverage takes the mode-less branch */
51
100
  if (process.platform === "win32") return;
52
101
  /* v8 ignore start -- POSIX tests cover this branch; Windows cannot express it */
53
102
  if ((mode & 63) !== 0) throw new Error(`openai-codex: ${filename} is readable beyond its owner (mode ${(mode & 511).toString(8)}); run "chmod 600 ${filename}" before starting again`);
54
103
  /* v8 ignore stop */
55
104
  }
56
- /** Validate the strict JSON document without quoting token-bearing input. */
57
- function parseDocument$2(text, filename) {
58
- let value;
59
- try {
60
- value = JSON.parse(text);
61
- } catch {
62
- throw new Error(`openai-codex: ${filename} is not valid JSON`);
63
- }
64
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`openai-codex: ${filename} must contain an object`);
65
- const document = value;
66
- if (document["version"] !== AUTH_FORMAT_VERSION) throw new Error(`openai-codex: ${filename} has unsupported auth format version ${String(document["version"])}`);
67
- if (Object.keys(document).some((key) => key !== "version" && key !== "credential")) throw new Error(`openai-codex: ${filename} contains an unknown top-level field`);
68
- const raw = document["credential"];
105
+ /** Validate one OAuth credential without quoting token-bearing input. */
106
+ function parseCredential(raw, filename) {
69
107
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) throw new Error(`openai-codex: ${filename} credential must be an object`);
70
108
  const credential = raw;
71
109
  if (Object.keys(credential).some((key) => ![
@@ -82,15 +120,65 @@ function parseDocument$2(text, filename) {
82
120
  "accountId"
83
121
  ]) if (typeof credential[key] !== "string" || credential[key].length === 0) throw new Error(`openai-codex: ${filename} credential ${key} must be a non-empty string`);
84
122
  if (typeof credential["expires"] !== "number" || !Number.isFinite(credential["expires"]) || credential["expires"] <= 0) throw new Error(`openai-codex: ${filename} credential expires must be a positive finite number`);
123
+ return credential;
124
+ }
125
+ /** Validate the strict JSON document without quoting token-bearing input. */
126
+ function parseDocument$2(text, filename) {
127
+ let value;
128
+ try {
129
+ value = JSON.parse(text);
130
+ } catch {
131
+ throw new Error(`openai-codex: ${filename} is not valid JSON`);
132
+ }
133
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`openai-codex: ${filename} must contain an object`);
134
+ const document = value;
135
+ if (document["version"] === 1) {
136
+ if (Object.keys(document).some((key) => key !== "version" && key !== "credential")) throw new Error(`openai-codex: ${filename} contains an unknown top-level field`);
137
+ return {
138
+ version: 1,
139
+ credential: parseCredential(document["credential"], filename)
140
+ };
141
+ }
142
+ if (document["version"] !== AUTH_FORMAT_VERSION) throw new Error(`openai-codex: ${filename} has unsupported auth format version ${String(document["version"])}`);
143
+ if (Object.keys(document).some((key) => ![
144
+ "version",
145
+ "activeAccountId",
146
+ "credentials"
147
+ ].includes(key))) throw new Error(`openai-codex: ${filename} contains an unknown top-level field`);
148
+ if (typeof document["activeAccountId"] !== "string" || document["activeAccountId"].length === 0) throw new Error(`openai-codex: ${filename} activeAccountId must be a non-empty string`);
149
+ if (!Array.isArray(document["credentials"]) || document["credentials"].length === 0) throw new Error(`openai-codex: ${filename} credentials must be a non-empty array`);
150
+ if (document["credentials"].length > 16) throw new Error(`openai-codex: ${filename} exceeds the ${String(16)} account limit`);
151
+ const credentials = document["credentials"].map((raw) => parseCredential(raw, filename));
152
+ const accountIds = new Set(credentials.map((credential) => credential.accountId));
153
+ if (accountIds.size !== credentials.length) throw new Error(`openai-codex: ${filename} contains duplicate accountId values`);
154
+ if (!accountIds.has(document["activeAccountId"])) throw new Error(`openai-codex: ${filename} activeAccountId does not identify a stored credential`);
85
155
  return {
86
156
  version: AUTH_FORMAT_VERSION,
87
- credential
157
+ activeAccountId: document["activeAccountId"],
158
+ credentials
88
159
  };
89
160
  }
90
161
  /** Detach a credential from callers that may mutate provider-owned extras. */
91
162
  function cloneCredential(credential) {
92
163
  return structuredClone(credential);
93
164
  }
165
+ function documentCredentials(document) {
166
+ return document.version === 1 ? [document.credential] : document.credentials;
167
+ }
168
+ function activeCredential(document) {
169
+ if (document.version === 1) return document.credential;
170
+ const active = document.credentials.find((credential) => credential.accountId === document.activeAccountId);
171
+ if (active === void 0) throw new Error("openai-codex: active credential invariant failed");
172
+ return active;
173
+ }
174
+ function accountKey$1(accountId) {
175
+ return `acct_${createHash("sha256").update(accountId).digest("base64url")}`;
176
+ }
177
+ function serializeDocument(document) {
178
+ const text = `${JSON.stringify(document, null, 2)}\n`;
179
+ if (Buffer.byteLength(text) > 524288) throw new Error(`openai-codex: credential document exceeds ${String(OPENAI_CODEX_AUTH_DOCUMENT_LIMIT)} bytes`);
180
+ return text;
181
+ }
94
182
  /**
95
183
  * Resolve the default OAuth document path.
96
184
  * @param dshHome - optional Harness-home override.
@@ -103,35 +191,140 @@ function openAICodexAuthPath(dshHome) {
103
191
  var OpenAICodexCredentialStore = class {
104
192
  /** Absolute credential document path. */
105
193
  filename;
194
+ /** Owner-only version-1 rollback copy, created at the first migration write. */
195
+ version1BackupFilename;
106
196
  /**
107
197
  * @param filename - explicit document path, defaulting under `$DSH_HOME`.
108
198
  */
109
199
  constructor(filename = openAICodexAuthPath()) {
110
200
  this.filename = resolve(filename);
201
+ this.version1BackupFilename = join(dirname(this.filename), `${basename(this.filename)}${OPENAI_CODEX_AUTH_V1_BACKUP_SUFFIX}`);
111
202
  }
112
203
  /** Read and validate the current document without acquiring the writer lock. */
113
- async readCurrent() {
114
- await assertOwnerOnly$1(this.filename);
115
- let text;
204
+ async readDocument() {
205
+ return this.readDocumentAt(this.filename);
206
+ }
207
+ async readDocumentAt(filename) {
208
+ let handle;
116
209
  try {
117
- text = await readFile(this.filename, "utf8");
210
+ handle = await open(filename, "r");
118
211
  } catch (error) {
119
212
  if (isENOENT$1(error)) return void 0;
120
213
  throw error;
121
214
  }
122
- return cloneCredential(parseDocument$2(text, this.filename).credential);
215
+ try {
216
+ const info = await handle.stat();
217
+ if (!info.isFile()) throw new Error(`openai-codex: ${filename} must be a regular file`);
218
+ assertOwnerOnly$1(filename, info.mode);
219
+ if (info.size > 524288) throw new Error(`openai-codex: ${filename} exceeds ${String(OPENAI_CODEX_AUTH_DOCUMENT_LIMIT)} bytes`);
220
+ return parseDocument$2(await handle.readFile("utf8"), filename);
221
+ } finally {
222
+ await handle.close();
223
+ }
224
+ }
225
+ async writeDocument(document, previous) {
226
+ const text = serializeDocument(document);
227
+ if (previous?.version === 1) {
228
+ const existingBackup = await this.readDocumentAt(this.version1BackupFilename);
229
+ if (existingBackup === void 0) await writeFileAtomic(this.version1BackupFilename, serializeDocument(previous), {
230
+ mode: 384,
231
+ dirMode: 448
232
+ });
233
+ else if (existingBackup.version !== 1 || serializeDocument(existingBackup) !== serializeDocument(previous)) throw new Error(`openai-codex: ${this.version1BackupFilename} rollback copy does not match the current version 1 credential`);
234
+ }
235
+ await writeFileAtomic(this.filename, text, {
236
+ mode: 384,
237
+ dirMode: 448
238
+ });
123
239
  }
124
240
  /** @inheritdoc */
125
241
  async read(providerId) {
126
- return providerId === "openai-codex" ? this.readCurrent() : void 0;
242
+ if (providerId !== "openai-codex") return void 0;
243
+ const document = await this.readDocument();
244
+ return document === void 0 ? void 0 : cloneCredential(activeCredential(document));
127
245
  }
128
246
  /** @inheritdoc */
129
247
  async list() {
130
- return await this.readCurrent() === void 0 ? [] : [{
248
+ return await this.readDocument() === void 0 ? [] : [{
131
249
  providerId: OPENAI_CODEX_PROVIDER,
132
250
  type: "oauth"
133
251
  }];
134
252
  }
253
+ /** List browser-safe account summaries without exposing provider account ids. */
254
+ async accounts() {
255
+ const document = await this.readDocument();
256
+ if (document === void 0) return [];
257
+ const credentials = documentCredentials(document);
258
+ const profiles = resolveOpenAICodexAccountProfiles(credentials);
259
+ const activeAccountId = activeCredential(document).accountId;
260
+ return credentials.map((credential, index) => ({
261
+ accountKey: accountKey$1(credential.accountId),
262
+ displayName: profiles[index].displayName,
263
+ ...profiles[index].maskedEmail === void 0 ? {} : { maskedEmail: profiles[index].maskedEmail },
264
+ profileSource: profiles[index].source,
265
+ active: credential.accountId === activeAccountId
266
+ }));
267
+ }
268
+ /** Resolve the account id stored with one exact access token. */
269
+ async accountIdForAccess(access) {
270
+ const document = await this.readDocument();
271
+ if (document === void 0) return void 0;
272
+ return documentCredentials(document).find((credential) => credential.access === access)?.accountId;
273
+ }
274
+ /** Select a stored account using its browser-safe key. */
275
+ async activate(selectedAccountKey) {
276
+ await mkdir(dirname(this.filename), {
277
+ recursive: true,
278
+ mode: 448
279
+ });
280
+ return withFileLock(this.filename, async () => {
281
+ const document = await this.readDocument();
282
+ if (document === void 0) throw new Error("openai-codex: account not found");
283
+ const credentials = documentCredentials(document);
284
+ const selected = credentials.find((credential) => accountKey$1(credential.accountId) === selectedAccountKey);
285
+ if (selected === void 0) throw new Error("openai-codex: account not found");
286
+ await this.writeDocument({
287
+ version: AUTH_FORMAT_VERSION,
288
+ activeAccountId: selected.accountId,
289
+ credentials: credentials.map(cloneCredential)
290
+ }, document);
291
+ return cloneCredential(selected);
292
+ });
293
+ }
294
+ /** Remove one account; active removal requires an explicit stored replacement. */
295
+ async removeAccount(selectedAccountKey, replacementAccountKey) {
296
+ await mkdir(dirname(this.filename), {
297
+ recursive: true,
298
+ mode: 448
299
+ });
300
+ await withFileLock(this.filename, async () => {
301
+ const document = await this.readDocument();
302
+ if (document === void 0) throw new Error("openai-codex: account not found");
303
+ const credentials = [...documentCredentials(document)];
304
+ const removeIndex = credentials.findIndex((credential) => accountKey$1(credential.accountId) === selectedAccountKey);
305
+ if (removeIndex < 0) throw new Error("openai-codex: account not found");
306
+ const removed = credentials[removeIndex];
307
+ const active = activeCredential(document);
308
+ const remaining = credentials.filter((_, index) => index !== removeIndex);
309
+ let nextActive = active.accountId;
310
+ if (removed.accountId === active.accountId && remaining.length > 0) {
311
+ if (replacementAccountKey === void 0) throw new Error("openai-codex: removing the active account requires replacementAccountKey");
312
+ const replacement = remaining.find((credential) => accountKey$1(credential.accountId) === replacementAccountKey);
313
+ if (replacement === void 0) throw new Error("openai-codex: replacement account not found");
314
+ nextActive = replacement.accountId;
315
+ } else if (replacementAccountKey !== void 0) throw new Error("openai-codex: replacementAccountKey is only valid when removing the active account");
316
+ await rm(this.version1BackupFilename, { force: true });
317
+ if (remaining.length === 0) {
318
+ await rm(this.filename, { force: true });
319
+ return;
320
+ }
321
+ await this.writeDocument({
322
+ version: AUTH_FORMAT_VERSION,
323
+ activeAccountId: nextActive,
324
+ credentials: remaining.map(cloneCredential)
325
+ });
326
+ });
327
+ }
135
328
  /** @inheritdoc */
136
329
  async modify(providerId, fn) {
137
330
  if (providerId !== "openai-codex") throw new Error(`openai-codex: credential store does not own provider "${providerId}"`);
@@ -140,18 +333,22 @@ var OpenAICodexCredentialStore = class {
140
333
  mode: 448
141
334
  });
142
335
  return withFileLock(this.filename, async () => {
143
- const current = await this.readCurrent();
336
+ const currentDocument = await this.readDocument();
337
+ const current = currentDocument === void 0 ? void 0 : cloneCredential(activeCredential(currentDocument));
144
338
  const candidate = await fn(current);
145
339
  if (candidate === void 0) return current;
146
- const document = parseDocument$2(JSON.stringify({
340
+ const validated = parseCredential(candidate, this.filename);
341
+ const credentials = currentDocument === void 0 ? [] : [...documentCredentials(currentDocument)];
342
+ const existingIndex = credentials.findIndex((credential) => credential.accountId === validated.accountId);
343
+ if (existingIndex >= 0) credentials[existingIndex] = validated;
344
+ else credentials.push(validated);
345
+ if (credentials.length > 16) throw new Error(`openai-codex: credential store accepts at most ${String(16)} accounts`);
346
+ await this.writeDocument({
147
347
  version: AUTH_FORMAT_VERSION,
148
- credential: candidate
149
- }), this.filename);
150
- await writeFileAtomic(this.filename, `${JSON.stringify(document, null, 2)}\n`, {
151
- mode: 384,
152
- dirMode: 448
153
- });
154
- return cloneCredential(document.credential);
348
+ activeAccountId: validated.accountId,
349
+ credentials: credentials.map(cloneCredential)
350
+ }, currentDocument);
351
+ return cloneCredential(validated);
155
352
  });
156
353
  }
157
354
  /** @inheritdoc */
@@ -161,7 +358,10 @@ var OpenAICodexCredentialStore = class {
161
358
  recursive: true,
162
359
  mode: 448
163
360
  });
164
- await withFileLock(this.filename, () => rm(this.filename, { force: true }));
361
+ await withFileLock(this.filename, async () => {
362
+ await rm(this.filename, { force: true });
363
+ await rm(this.version1BackupFilename, { force: true });
364
+ });
165
365
  }
166
366
  };
167
367
  //#endregion
@@ -528,10 +728,8 @@ function parseOpenAICodexUsage(value) {
528
728
  async function readOpenAICodexRateLimits(store) {
529
729
  const models = createModels({ credentials: store });
530
730
  models.setProvider(openaiCodexProvider());
531
- const auth = await models.getAuth(OPENAI_CODEX_PROVIDER);
532
- const credential = await store.read(OPENAI_CODEX_PROVIDER);
533
- const access = auth?.auth.apiKey;
534
- const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
731
+ const access = (await models.getAuth(OPENAI_CODEX_PROVIDER))?.auth.apiKey;
732
+ const accountId = access === void 0 ? void 0 : await store.accountIdForAccess(access);
535
733
  if (access === void 0 || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("OpenAI Codex is signed out");
536
734
  const response = await fetch(OPENAI_CODEX_USAGE_URL, {
537
735
  method: "GET",
@@ -568,6 +766,8 @@ const OPENAI_CODEX_AUTH_LOGIN_PATH = "/plugins/dsh-openai-codex/auth/login";
568
766
  const OPENAI_CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-openai-codex/auth/logout";
569
767
  /** Cancel only the pending authorization; never delete a stored credential. */
570
768
  const OPENAI_CODEX_AUTH_CANCEL_PATH = "/plugins/dsh-openai-codex/auth/cancel";
769
+ /** List, activate, and remove stored OpenAI Codex accounts. */
770
+ const OPENAI_CODEX_AUTH_ACCOUNTS_PATH = "/plugins/dsh-openai-codex/auth/accounts";
571
771
  //#endregion
572
772
  //#region src/trusted-origins.ts
573
773
  /** Owner-only allowlist for browser origins that may reach the Web OAuth routes. */
@@ -881,6 +1081,14 @@ var OpenAICodexWebAuth = class {
881
1081
  async cancel() {
882
1082
  await this.finishLogin("cancel");
883
1083
  }
1084
+ /** Cancel pending OAuth and select one already stored account. */
1085
+ async activateAccount(accountKey) {
1086
+ return this.mutateStoredAccount(() => this.store.activate(accountKey));
1087
+ }
1088
+ /** Cancel pending OAuth and remove exactly one stored account. */
1089
+ async removeAccount(accountKey, replacementAccountKey) {
1090
+ return this.mutateStoredAccount(() => this.store.removeAccount(accountKey, replacementAccountKey));
1091
+ }
884
1092
  /** Stop the owned callback listener during plugin disposal. */
885
1093
  async dispose() {
886
1094
  this.disposed = true;
@@ -905,6 +1113,28 @@ var OpenAICodexWebAuth = class {
905
1113
  if (this.transition === transition) this.transition = void 0;
906
1114
  }
907
1115
  }
1116
+ async mutateStoredAccount(operation) {
1117
+ while (this.transition !== void 0) await this.transition;
1118
+ if (this.disposed) throw new Error("OpenAI Codex plugin disposed");
1119
+ const login = this.operation;
1120
+ this.cancelSignIn(/* @__PURE__ */ new Error("OpenAI Codex sign-in cancelled"));
1121
+ let result;
1122
+ const transition = (async () => {
1123
+ await login?.catch(() => void 0);
1124
+ this.challenge = void 0;
1125
+ await operation();
1126
+ result = await this.readStoredStatus();
1127
+ this.state = result;
1128
+ })();
1129
+ this.transition = transition;
1130
+ try {
1131
+ await transition;
1132
+ if (result === void 0) throw new Error("openai-codex: account mutation did not produce status");
1133
+ return result;
1134
+ } finally {
1135
+ if (this.transition === transition) this.transition = void 0;
1136
+ }
1137
+ }
908
1138
  start() {
909
1139
  const cancellation = new AbortController();
910
1140
  this.cancellation = cancellation;
@@ -1153,6 +1383,44 @@ function fastModeBody(value) {
1153
1383
  enabled
1154
1384
  } : void 0;
1155
1385
  }
1386
+ function accountKey(value) {
1387
+ return typeof value === "string" && /^acct_[A-Za-z0-9_-]{43}$/u.test(value) ? value : void 0;
1388
+ }
1389
+ function activateAccountBody(value) {
1390
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1391
+ const record = value;
1392
+ if (Object.keys(record).length !== 1) return void 0;
1393
+ const parsed = accountKey(record["accountKey"]);
1394
+ return parsed === void 0 ? void 0 : { accountKey: parsed };
1395
+ }
1396
+ function removeAccountBody(value) {
1397
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1398
+ const record = value;
1399
+ if (Object.keys(record).some((key) => key !== "accountKey" && key !== "replacementAccountKey")) return void 0;
1400
+ const selected = accountKey(record["accountKey"]);
1401
+ if (selected === void 0) return void 0;
1402
+ if (record["replacementAccountKey"] === void 0) return { accountKey: selected };
1403
+ const replacement = accountKey(record["replacementAccountKey"]);
1404
+ return replacement === void 0 ? void 0 : {
1405
+ accountKey: selected,
1406
+ replacementAccountKey: replacement
1407
+ };
1408
+ }
1409
+ function accountMutationError(error) {
1410
+ const message = safeMessage(error);
1411
+ if (/account not found/u.test(message)) return {
1412
+ status: 404,
1413
+ error: message
1414
+ };
1415
+ if (/requires replacementAccountKey|only valid when removing/u.test(message)) return {
1416
+ status: 409,
1417
+ error: message
1418
+ };
1419
+ return {
1420
+ status: 500,
1421
+ error: message
1422
+ };
1423
+ }
1156
1424
  /** Register the plugin-owned OAuth routes when the Web server is composed. */
1157
1425
  function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride, fastModeOverride, proxyManager, resolveProxyUrl, authorizationTimeoutMs) {
1158
1426
  const auth = new OpenAICodexWebAuth(store, {
@@ -1177,7 +1445,11 @@ function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride, fastM
1177
1445
  handler: async (req, res) => {
1178
1446
  if (req.method !== "GET") return json$4(res, 405, { error: "method not allowed" });
1179
1447
  if (!await authorize(req, res)) return;
1180
- json$4(res, 200, await auth.status());
1448
+ const [status, accounts] = await Promise.all([auth.status(), store.accounts()]);
1449
+ json$4(res, 200, {
1450
+ ...status,
1451
+ accounts
1452
+ });
1181
1453
  }
1182
1454
  }),
1183
1455
  ctx.webServer.register({
@@ -1221,6 +1493,33 @@ function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride, fastM
1221
1493
  }
1222
1494
  }
1223
1495
  }),
1496
+ ctx.webServer.register({
1497
+ kind: "exact",
1498
+ path: OPENAI_CODEX_AUTH_ACCOUNTS_PATH,
1499
+ handler: async (req, res) => {
1500
+ if (req.method !== "GET" && req.method !== "POST" && req.method !== "DELETE") return json$4(res, 405, { error: "method not allowed" });
1501
+ if (!await authorize(req, res)) return;
1502
+ if (req.method === "GET") return json$4(res, 200, { accounts: await store.accounts() });
1503
+ const type = header(req, "content-type");
1504
+ if (type === void 0 || !/^application\/json(?:\s*;|$)/iu.test(type.trim())) return json$4(res, 415, { error: "unsupported content type" });
1505
+ try {
1506
+ const raw = await readFastModeBody(req);
1507
+ if (req.method === "POST") {
1508
+ const body = activateAccountBody(raw);
1509
+ if (body === void 0) return json$4(res, 400, { error: "invalid input" });
1510
+ return json$4(res, 200, await auth.activateAccount(body.accountKey));
1511
+ }
1512
+ const body = removeAccountBody(raw);
1513
+ if (body === void 0) return json$4(res, 400, { error: "invalid input" });
1514
+ return json$4(res, 200, await auth.removeAccount(body.accountKey, body.replacementAccountKey));
1515
+ } catch (error) {
1516
+ if (error instanceof RangeError) return json$4(res, 413, { error: "request body too large" });
1517
+ if (error instanceof TypeError) return json$4(res, 400, { error: "invalid input" });
1518
+ const mapped = accountMutationError(error);
1519
+ return json$4(res, mapped.status, { error: mapped.error });
1520
+ }
1521
+ }
1522
+ }),
1224
1523
  ctx.webServer.register({
1225
1524
  kind: "exact",
1226
1525
  path: OPENAI_CODEX_FAST_MODE_PATH,
@@ -1510,7 +1809,10 @@ var OpenAICodexProxyManager = class {
1510
1809
  agentFor(proxyUrl) {
1511
1810
  let agent = this.agents.get(proxyUrl);
1512
1811
  if (agent !== void 0) return agent;
1513
- agent = new ProxyAgent(proxyUrl);
1812
+ agent = new ProxyAgent({
1813
+ uri: proxyUrl,
1814
+ proxyTunnel: true
1815
+ });
1514
1816
  this.agents.set(proxyUrl, agent);
1515
1817
  return agent;
1516
1818
  }
@@ -1678,7 +1980,7 @@ function registerOpenAICodexProxyRoutes(ctx, trustedOrigins, manager) {
1678
1980
  //#region src/compatibility.ts
1679
1981
  const COMPATIBILITY_SCHEMA_VERSION = 1;
1680
1982
  const SUPPORTED_NODE_RANGE = "^22.19.0 || >=24.0.0";
1681
- const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.2-alpha.5";
1983
+ const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.2-rc.1";
1682
1984
  const SUPPORTED_PI_AI_RANGE = "^0.84.2";
1683
1985
  const PI_AI_PACKAGE = "@earendil-works/pi-ai";
1684
1986
  const DSH_PLUGIN_API_PACKAGES = [
@@ -2563,7 +2865,7 @@ function registerOpenAICodexOriginalImageRoute(ctx, trustedOrigins, assets) {
2563
2865
  }
2564
2866
  //#endregion
2565
2867
  //#region src/version.ts
2566
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.25";
2868
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.26";
2567
2869
  //#endregion
2568
2870
  //#region src/doctor.ts
2569
2871
  /** Secret-free diagnostics and duplicate-provider guidance. */
@@ -3576,9 +3878,8 @@ var OpenAICodexTransport = class extends Service {
3576
3878
  } catch {
3577
3879
  throw new OpenAICodexTransportError(OPENAI_CODEX_TRANSPORT_ERROR_CODES.reauthRequired);
3578
3880
  }
3579
- const refreshed = await this.credentials.read(OPENAI_CODEX_PROVIDER);
3580
3881
  const access = auth?.auth.apiKey;
3581
- const accountId = refreshed?.type === "oauth" ? refreshed.accountId : void 0;
3882
+ const accountId = access === void 0 ? void 0 : await this.credentials.accountIdForAccess(access);
3582
3883
  if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new OpenAICodexTransportError(OPENAI_CODEX_TRANSPORT_ERROR_CODES.reauthRequired);
3583
3884
  if (isAborted(context.signal)) throw new OpenAICodexTransportError(OPENAI_CODEX_TRANSPORT_ERROR_CODES.canceled);
3584
3885
  const traceId = randomUUID();
@@ -4394,8 +4695,7 @@ var OpenAICodexAutoReviewBackend = class {
4394
4695
  }, AUTO_REVIEW_TIMEOUT_MS);
4395
4696
  try {
4396
4697
  const access = (await this.models.getAuth(OPENAI_CODEX_PROVIDER, { signal: controller.signal }))?.auth.apiKey;
4397
- const stored = await this.credentialStore.read(OPENAI_CODEX_PROVIDER);
4398
- const accountId = stored?.type === "oauth" && typeof stored.accountId === "string" ? stored.accountId : void 0;
4698
+ const accountId = access === void 0 ? void 0 : await this.credentialStore.accountIdForAccess(access);
4399
4699
  if (access === void 0 || access.length === 0 || accountId === void 0 || accountId.length === 0) return { status: "unavailable" };
4400
4700
  const response = await this.proxyManager.run(this.resolveProxyUrl(), () => fetch$1(`${OPENAI_CODEX_BASE_URL}/responses`, {
4401
4701
  method: "POST",
@@ -5279,4 +5579,4 @@ function apply(ctx, config) {
5279
5579
  scheduleCapabilities();
5280
5580
  }
5281
5581
  //#endregion
5282
- export { OPENAI_CODEX_LOCAL_PROXY_CANDIDATES as $, assertNoOpenAICodexProviderConflict as A, logoutOpenAICodex as At, COMPATIBILITY_PACKAGES as B, OPENAI_CODEX_TRANSPORT_ERROR_CODES as C, isFastModeSessionId as Ct, isOpenAICodexTransportError as D, parseOpenAICodexUsage as Dt, OpenAICodexTransportError as E, OPENAI_CODEX_USAGE_URL as Et, checkForOpenAICodexUpdate as F, OpenAICodexCredentialStore as Ft, SUPPORTED_NODE_RANGE as G, DSH_PLUGIN_API_PACKAGES as H, compareOpenAICodexVersions as I, openAICodexAuthPath as It, detectCompatibility as J, SUPPORTED_PI_AI_RANGE as K, parseOpenAICodexUpdateResult as L, openAICodexConflictMessage as M, openAICodexModelCatalog as Mt, CODEX_CONNECT_VERSION as N, OPENAI_CODEX_AUTH_FILENAME as Nt, VIEW_IMAGE_TOOL_NAME as O, readOpenAICodexRateLimits as Ot, OPENAI_CODEX_UPDATE_PATH as P, OPENAI_CODEX_PROVIDER as Pt, OPENAI_CODEX_PROXY_TEST_PATH as Q, parseOpenAICodexVersion as R, OPENAI_CODEX_TRANSPORT_API_VERSION as S, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH as St, OpenAICodexTransport as T, normalizeTrustedOrigin as Tt, PI_AI_PACKAGE as U, COMPATIBILITY_SCHEMA_VERSION as V, SUPPORTED_DSH_PLUGIN_API_VERSION as W, readInstalledPackageVersion as X, evaluateCompatibility as Y, OPENAI_CODEX_PROXY_DETECT_PATH as Z, OPENAI_CODEX_IMAGE_MAX_COUNT as _, resolveOpenAICodexProxyUrl as _t, name as a, listOpenAICodexProxyCandidates as at, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH as b, FastModeRegistry as bt, migrateOpenAICodexSearchHistory as c, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS as ct, OPENAI_CODEX_BASE_URL as d, DEFAULT_OPENAI_CODEX_SETTINGS as dt, OPENAI_CODEX_PROXY_CANDIDATE_LIMIT as et, OPENAI_CODEX_SEARCH_PROVIDER as f, OPENAI_CODEX_SETTINGS_NAMESPACE as ft, OPENAI_CODEX_IMAGE_GENERATION_URL as g, normalizeOpenAICodexProxyUrl as gt, mapOpenAICodexSearchResponse as h, isValidOpenAICodexProxyUrl as ht, inject as i, detectOpenAICodexProxies as it, diagnoseOpenAICodex as j, openAICodexAuthStatus as jt, IMAGE_GENERATE_TOOL_NAME as k, loginOpenAICodex as kt, CODEX_AUTO_REVIEW_MODEL as l, DEFAULT_OPENAI_CODEX_SEARCH_MODE as lt, OpenAICodexSearchProvider as m, isValidOpenAICodexContextWindowOverrides as mt, OPENAI_CODEX_SETTINGS_NS as n, OPENAI_CODEX_PROXY_PROBE_URL as nt, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX as o, DEFAULT_OPENAI_CODEX_PROXY_URL as ot, OPENAI_CODEX_SEARCH_URL as p, decodeOpenAICodexSettings as pt, assessCompatibility as q, apply as r, OpenAICodexProxyManager as rt, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT as s, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as st, Config as t, OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS as tt, probeCodexAutoReview as u, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as ut, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES as v, resolveOpenAICodexSettings as vt, OPENAI_CODEX_TRANSPORT_SERVICE as w, OpenAICodexTrustedOriginsStore as wt, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS as x, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS as xt, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES as y, OPENAI_CODEX_FAST_MODE_PATH as yt, COMPATIBILITY_CONTRACT as z };
5582
+ export { OPENAI_CODEX_LOCAL_PROXY_CANDIDATES as $, assertNoOpenAICodexProviderConflict as A, logoutOpenAICodex as At, COMPATIBILITY_PACKAGES as B, OPENAI_CODEX_TRANSPORT_ERROR_CODES as C, isFastModeSessionId as Ct, isOpenAICodexTransportError as D, parseOpenAICodexUsage as Dt, OpenAICodexTransportError as E, OPENAI_CODEX_USAGE_URL as Et, checkForOpenAICodexUpdate as F, OPENAI_CODEX_AUTH_FILENAME as Ft, SUPPORTED_NODE_RANGE as G, DSH_PLUGIN_API_PACKAGES as H, compareOpenAICodexVersions as I, OPENAI_CODEX_AUTH_V1_BACKUP_SUFFIX as It, detectCompatibility as J, SUPPORTED_PI_AI_RANGE as K, parseOpenAICodexUpdateResult as L, OPENAI_CODEX_PROVIDER as Lt, openAICodexConflictMessage as M, openAICodexModelCatalog as Mt, CODEX_CONNECT_VERSION as N, OPENAI_CODEX_ACCOUNT_LIMIT as Nt, VIEW_IMAGE_TOOL_NAME as O, readOpenAICodexRateLimits as Ot, OPENAI_CODEX_UPDATE_PATH as P, OPENAI_CODEX_AUTH_DOCUMENT_LIMIT as Pt, OPENAI_CODEX_PROXY_TEST_PATH as Q, parseOpenAICodexVersion as R, OpenAICodexCredentialStore as Rt, OPENAI_CODEX_TRANSPORT_API_VERSION as S, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH as St, OpenAICodexTransport as T, normalizeTrustedOrigin as Tt, PI_AI_PACKAGE as U, COMPATIBILITY_SCHEMA_VERSION as V, SUPPORTED_DSH_PLUGIN_API_VERSION as W, readInstalledPackageVersion as X, evaluateCompatibility as Y, OPENAI_CODEX_PROXY_DETECT_PATH as Z, OPENAI_CODEX_IMAGE_MAX_COUNT as _, resolveOpenAICodexProxyUrl as _t, name as a, listOpenAICodexProxyCandidates as at, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH as b, FastModeRegistry as bt, migrateOpenAICodexSearchHistory as c, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS as ct, OPENAI_CODEX_BASE_URL as d, DEFAULT_OPENAI_CODEX_SETTINGS as dt, OPENAI_CODEX_PROXY_CANDIDATE_LIMIT as et, OPENAI_CODEX_SEARCH_PROVIDER as f, OPENAI_CODEX_SETTINGS_NAMESPACE as ft, OPENAI_CODEX_IMAGE_GENERATION_URL as g, normalizeOpenAICodexProxyUrl as gt, mapOpenAICodexSearchResponse as h, isValidOpenAICodexProxyUrl as ht, inject as i, detectOpenAICodexProxies as it, diagnoseOpenAICodex as j, openAICodexAuthStatus as jt, IMAGE_GENERATE_TOOL_NAME as k, loginOpenAICodex as kt, CODEX_AUTO_REVIEW_MODEL as l, DEFAULT_OPENAI_CODEX_SEARCH_MODE as lt, OpenAICodexSearchProvider as m, isValidOpenAICodexContextWindowOverrides as mt, OPENAI_CODEX_SETTINGS_NS as n, OPENAI_CODEX_PROXY_PROBE_URL as nt, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX as o, DEFAULT_OPENAI_CODEX_PROXY_URL as ot, OPENAI_CODEX_SEARCH_URL as p, decodeOpenAICodexSettings as pt, assessCompatibility as q, apply as r, OpenAICodexProxyManager as rt, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT as s, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as st, Config as t, OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS as tt, probeCodexAutoReview as u, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as ut, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES as v, resolveOpenAICodexSettings as vt, OPENAI_CODEX_TRANSPORT_SERVICE as w, OpenAICodexTrustedOriginsStore as wt, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS as x, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS as xt, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES as y, OPENAI_CODEX_FAST_MODE_PATH as yt, COMPATIBILITY_CONTRACT as z, openAICodexAuthPath as zt };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-codex-connect",
3
3
  "displayName": "Codex Connect",
4
4
  "description": "ChatGPT OAuth and Codex models for DeepSeek Harness.",
5
- "version": "0.1.0-alpha.4.25",
5
+ "version": "0.1.0-alpha.4.26",
6
6
  "author": "Frank Song",
7
7
  "contributors": [
8
8
  "Yan-Zero (original dsh-codex author)"
@@ -78,7 +78,7 @@
78
78
  "NOTICE"
79
79
  ],
80
80
  "dependencies": {
81
- "undici": "^7.29.0"
81
+ "undici": "^8.10.0"
82
82
  },
83
83
  "license": "Apache-2.0",
84
84
  "dsh": {
@@ -104,24 +104,24 @@
104
104
  "peerDependencies": {
105
105
  "@earendil-works/pi-ai": "^0.84.2",
106
106
  "@deepseek-ai/cordis": "^4.0.2",
107
- "@deepseek-ai/dsh-agent": "0.1.2-alpha.5",
108
- "@deepseek-ai/dsh-atomic-write": "0.1.2-alpha.5",
109
- "@deepseek-ai/dsh-attachment": "0.1.2-alpha.5",
110
- "@deepseek-ai/dsh-client-ui-layout": "0.1.2-alpha.5",
111
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.5",
112
- "@deepseek-ai/dsh-client-ui-tool": "0.1.2-alpha.5",
113
- "@deepseek-ai/dsh-commands": "0.1.2-alpha.5",
114
- "@deepseek-ai/dsh-home-paths": "0.1.2-alpha.5",
115
- "@deepseek-ai/dsh-host-webserver": "0.1.2-alpha.5",
116
- "@deepseek-ai/dsh-invariants": "0.1.2-alpha.5",
117
- "@deepseek-ai/dsh-llm": "0.1.2-alpha.5",
118
- "@deepseek-ai/dsh-llm-pi-ai": "0.1.2-alpha.5",
119
- "@deepseek-ai/dsh-fs": "0.1.2-alpha.5",
120
- "@deepseek-ai/dsh-session": "0.1.2-alpha.5",
121
- "@deepseek-ai/dsh-settings": "0.1.2-alpha.5",
122
- "@deepseek-ai/dsh-tools": "0.1.2-alpha.5",
123
- "@deepseek-ai/dsh-util-values": "0.1.2-alpha.5",
124
- "@deepseek-ai/dsh-web": "0.1.2-alpha.5",
107
+ "@deepseek-ai/dsh-agent": "0.1.2-rc.1",
108
+ "@deepseek-ai/dsh-atomic-write": "0.1.2-rc.1",
109
+ "@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
110
+ "@deepseek-ai/dsh-client-ui-layout": "0.1.2-rc.1",
111
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
112
+ "@deepseek-ai/dsh-client-ui-tool": "0.1.2-rc.1",
113
+ "@deepseek-ai/dsh-commands": "0.1.2-rc.1",
114
+ "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
115
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
116
+ "@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
117
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
118
+ "@deepseek-ai/dsh-llm-pi-ai": "0.1.2-rc.1",
119
+ "@deepseek-ai/dsh-fs": "0.1.2-rc.1",
120
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
121
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
122
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
123
+ "@deepseek-ai/dsh-util-values": "0.1.2-rc.1",
124
+ "@deepseek-ai/dsh-web": "0.1.2-rc.1",
125
125
  "@deepseek-ai/schemastery": "^3.18.2",
126
126
  "react": "^18.2.0"
127
127
  },
@@ -135,61 +135,61 @@
135
135
  "@deepseek-ai/cordis": "^4.0.2",
136
136
  "@deepseek-ai/cordis-plugin-include": "1.0.7",
137
137
  "@deepseek-ai/cordis-plugin-loader": "1.0.3",
138
- "@deepseek-ai/dsh-agent": "0.1.2-alpha.5",
139
- "@deepseek-ai/dsh-agent-default-model": "0.1.2-alpha.5",
140
- "@deepseek-ai/dsh-agent-presets": "0.1.2-alpha.5",
141
- "@deepseek-ai/dsh-api-remotes": "0.1.2-alpha.5",
142
- "@deepseek-ai/dsh-api-gateway": "0.1.2-alpha.5",
143
- "@deepseek-ai/dsh-api-session-controller": "0.1.2-alpha.5",
144
- "@deepseek-ai/dsh-atomic-write": "0.1.2-alpha.5",
145
- "@deepseek-ai/dsh-attachment": "0.1.2-alpha.5",
146
- "@deepseek-ai/dsh-attachment-local": "0.1.2-alpha.5",
147
- "@deepseek-ai/dsh-authorization": "0.1.2-alpha.5",
148
- "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.5",
149
- "@deepseek-ai/dsh-client-connection": "0.1.2-alpha.5",
150
- "@deepseek-ai/dsh-client-store": "0.1.2-alpha.5",
151
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-alpha.5",
152
- "@deepseek-ai/dsh-client-ui-layout": "0.1.2-alpha.5",
153
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.5",
154
- "@deepseek-ai/dsh-client-ui-model-selection": "0.1.2-alpha.5",
155
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-alpha.5",
156
- "@deepseek-ai/dsh-client-ui-settings": "0.1.2-alpha.5",
157
- "@deepseek-ai/dsh-client-ui-settings-models": "0.1.2-alpha.5",
158
- "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.2-alpha.5",
159
- "@deepseek-ai/dsh-client-ui-session": "0.1.2-alpha.5",
160
- "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.5",
161
- "@deepseek-ai/dsh-client-ui-tool": "0.1.2-alpha.5",
162
- "@deepseek-ai/dsh-code-runtime": "0.1.2-alpha.5",
163
- "@deepseek-ai/dsh-commands": "0.1.2-alpha.5",
164
- "@deepseek-ai/dsh-file-reference": "0.1.2-alpha.5",
165
- "@deepseek-ai/dsh-fs": "0.1.2-alpha.5",
166
- "@deepseek-ai/dsh-fs-local": "0.1.2-alpha.5",
167
- "@deepseek-ai/dsh-home-paths": "0.1.2-alpha.5",
168
- "@deepseek-ai/dsh-host-webserver": "0.1.2-alpha.5",
169
- "@deepseek-ai/dsh-invariants": "0.1.2-alpha.5",
170
- "@deepseek-ai/dsh-llm": "0.1.2-alpha.5",
171
- "@deepseek-ai/dsh-llm-pi-ai": "0.1.2-alpha.5",
172
- "@deepseek-ai/dsh-launch-environment": "0.1.2-alpha.5",
173
- "@deepseek-ai/dsh-native-command": "0.1.2-alpha.5",
174
- "@deepseek-ai/dsh-sandbox": "0.1.2-alpha.5",
175
- "@deepseek-ai/dsh-scope": "0.1.2-alpha.5",
176
- "@deepseek-ai/dsh-session": "0.1.2-alpha.5",
177
- "@deepseek-ai/dsh-session-persistence": "0.1.2-alpha.5",
178
- "@deepseek-ai/dsh-session-projection": "0.1.2-alpha.5",
179
- "@deepseek-ai/dsh-session-query": "0.1.2-alpha.5",
180
- "@deepseek-ai/dsh-session-title": "0.1.2-alpha.5",
181
- "@deepseek-ai/dsh-settings": "0.1.2-alpha.5",
182
- "@deepseek-ai/dsh-skill": "0.1.2-alpha.5",
183
- "@deepseek-ai/dsh-storage": "0.1.2-alpha.5",
184
- "@deepseek-ai/dsh-storage-domain": "0.1.2-alpha.5",
185
- "@deepseek-ai/dsh-subagent": "0.1.2-alpha.5",
186
- "@deepseek-ai/dsh-system-prompt": "0.1.2-alpha.5",
187
- "@deepseek-ai/dsh-tools": "0.1.2-alpha.5",
188
- "@deepseek-ai/dsh-typert-registry": "0.1.2-alpha.5",
189
- "@deepseek-ai/dsh-user-approval": "0.1.2-alpha.5",
190
- "@deepseek-ai/dsh-util-values": "0.1.2-alpha.5",
191
- "@deepseek-ai/dsh-web": "0.1.2-alpha.5",
192
- "@deepseek-ai/dsh-workspace": "0.1.2-alpha.5",
138
+ "@deepseek-ai/dsh-agent": "0.1.2-rc.1",
139
+ "@deepseek-ai/dsh-agent-default-model": "0.1.2-rc.1",
140
+ "@deepseek-ai/dsh-agent-presets": "0.1.2-rc.1",
141
+ "@deepseek-ai/dsh-api-remotes": "0.1.2-rc.1",
142
+ "@deepseek-ai/dsh-api-gateway": "0.1.2-rc.1",
143
+ "@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
144
+ "@deepseek-ai/dsh-atomic-write": "0.1.2-rc.1",
145
+ "@deepseek-ai/dsh-attachment": "0.1.2-rc.1",
146
+ "@deepseek-ai/dsh-attachment-local": "0.1.2-rc.1",
147
+ "@deepseek-ai/dsh-authorization": "0.1.2-rc.1",
148
+ "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1",
149
+ "@deepseek-ai/dsh-client-connection": "0.1.2-rc.1",
150
+ "@deepseek-ai/dsh-client-store": "0.1.2-rc.1",
151
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
152
+ "@deepseek-ai/dsh-client-ui-layout": "0.1.2-rc.1",
153
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
154
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.2-rc.1",
155
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
156
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.2-rc.1",
157
+ "@deepseek-ai/dsh-client-ui-settings-models": "0.1.2-rc.1",
158
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.2-rc.1",
159
+ "@deepseek-ai/dsh-client-ui-session": "0.1.2-rc.1",
160
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
161
+ "@deepseek-ai/dsh-client-ui-tool": "0.1.2-rc.1",
162
+ "@deepseek-ai/dsh-code-runtime": "0.1.2-rc.1",
163
+ "@deepseek-ai/dsh-commands": "0.1.2-rc.1",
164
+ "@deepseek-ai/dsh-file-reference": "0.1.2-rc.1",
165
+ "@deepseek-ai/dsh-fs": "0.1.2-rc.1",
166
+ "@deepseek-ai/dsh-fs-local": "0.1.2-rc.1",
167
+ "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
168
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-rc.1",
169
+ "@deepseek-ai/dsh-invariants": "0.1.2-rc.1",
170
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
171
+ "@deepseek-ai/dsh-llm-pi-ai": "0.1.2-rc.1",
172
+ "@deepseek-ai/dsh-launch-environment": "0.1.2-rc.1",
173
+ "@deepseek-ai/dsh-native-command": "0.1.2-rc.1",
174
+ "@deepseek-ai/dsh-sandbox": "0.1.2-rc.1",
175
+ "@deepseek-ai/dsh-scope": "0.1.2-rc.1",
176
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
177
+ "@deepseek-ai/dsh-session-persistence": "0.1.2-rc.1",
178
+ "@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
179
+ "@deepseek-ai/dsh-session-query": "0.1.2-rc.1",
180
+ "@deepseek-ai/dsh-session-title": "0.1.2-rc.1",
181
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
182
+ "@deepseek-ai/dsh-skill": "0.1.2-rc.1",
183
+ "@deepseek-ai/dsh-storage": "0.1.2-rc.1",
184
+ "@deepseek-ai/dsh-storage-domain": "0.1.2-rc.1",
185
+ "@deepseek-ai/dsh-subagent": "0.1.2-rc.1",
186
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
187
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
188
+ "@deepseek-ai/dsh-typert-registry": "0.1.2-rc.1",
189
+ "@deepseek-ai/dsh-user-approval": "0.1.2-rc.1",
190
+ "@deepseek-ai/dsh-util-values": "0.1.2-rc.1",
191
+ "@deepseek-ai/dsh-web": "0.1.2-rc.1",
192
+ "@deepseek-ai/dsh-workspace": "0.1.2-rc.1",
193
193
  "@deepseek-ai/schemastery": "3.18.2",
194
194
  "@testing-library/react": "^16.3.2",
195
195
  "@types/node": "^22.20.0",