dsh-codex-connect 0.1.0-alpha.4.7 → 0.1.0-alpha.4.9

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
@@ -12,6 +12,8 @@ Install `dsh-codex-connect` into one requested DeepSeek Harness profile without
12
12
 
13
13
  ## Install and validate
14
14
 
15
+ The only verified combination is DSH plugin API packages `0.1.0-rc.6`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`. Upgrade the DSH API packages and pi-ai together, then rerun `dsh-codex-connect doctor --json` and `pnpm --silent run check:compatibility`; the contract does not make claims about future versions.
16
+
15
17
  1. Check `dsh --version` or `dsh --help`. From a Harness checkout use `pnpm dsh`.
16
18
  2. Install the package:
17
19
 
@@ -19,7 +21,7 @@ Install `dsh-codex-connect` into one requested DeepSeek Harness profile without
19
21
  dsh plugin --profile web add dsh-codex-connect@alpha
20
22
  ```
21
23
 
22
- After `0.1.0-alpha.4.7` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.7`. If npm is unavailable after its matching GitHub prerelease is created, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.7'`.
24
+ After `0.1.0-alpha.4.9` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.9`. If npm is unavailable after its matching GitHub prerelease is created, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.9'`.
23
25
 
24
26
  3. Run `dsh --profile web --dump-config` and require exactly one `llm-openai-codex` row loading `dsh-codex-connect`.
25
27
  4. Confirm the effective `agent-default-model` and `web.searchProvider` values are unchanged from before installation.
@@ -31,6 +33,17 @@ Install `dsh-codex-connect` into one requested DeepSeek Harness profile without
31
33
 
32
34
  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.
33
35
 
36
+ ### Remote browser access
37
+
38
+ The default Web OAuth boundary is loopback-only. When DSH runs on one device and you open it from another device on a trusted network through an IP address or domain, run the following on the device that runs DSH with the exact origin from the browser address bar:
39
+
40
+ ```sh
41
+ dsh plugin --profile web exec dsh-codex-connect trust-origin http://192.168.1.20:3080
42
+ dsh plugin --profile web exec dsh-codex-connect trusted-origins
43
+ ```
44
+
45
+ The value is a full `http://` or `https://` origin including its port, not a bare device IP and not a path/query/fragment. Use `untrust-origin <origin>` to remove it. Restrict this to a trusted network and never expose the route publicly; use an SSH tunnel when that is safer. The Web client does not edit this list.
46
+
34
47
  ## Optional configuration
35
48
 
36
49
  Use **Settings → Plugins → Plugin configuration → Codex Connect** for live, staged Save/Discard edits. The package row accepts the same `enableSearch` and `enableImageTool` fields as its composition base, both defaulting to `false`. Enabling search registers a provider but does not select it; selecting `web.searchProvider: openai-codex` is a second explicit profile change. Setting `agent-default-model` to `openai-codex` is also a separate explicit change.
package/README.md CHANGED
@@ -28,7 +28,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
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
- To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.7`. If npm is unavailable after the matching GitHub prerelease exists, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.7'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
31
+ To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.9`. If npm is unavailable after the matching GitHub prerelease exists, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.9'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
32
32
 
33
33
  ### 2. Start Harness
34
34
 
@@ -149,12 +149,22 @@ Selecting Codex as the profile's global search route is another explicit change:
149
149
  - `doctor` reads process and filesystem metadata only. `doctor --json` emits exactly one secret-free JSON document with schema version 1, package/version/Node metadata, credential-file state and safe mode, capabilities, conflict status, and hints. It omits the absolute credential path and OAuth, account, and expiry data.
150
150
  - `status --json` emits only signed-in or signed-out state with package metadata. `status --json` reads the credential only to determine sign-in state, but never prints credential contents or starts OAuth.
151
151
  - OAuth is stored separately at `$DSH_HOME/.openai-codex-auth.json` (`~/.dsh` by default). `~/.codex/auth.json` is never copied or modified. The parent directory and file use owner-only permissions where supported, writes are atomic, and refresh writes use a cross-process file lock.
152
+ - By default, the OAuth routes accept loopback browser requests only. When DSH runs on one device and you open it from another device on a trusted network, approve the browser address-bar origin explicitly on the device that runs DSH:
153
+
154
+ ```sh
155
+ dsh plugin --profile web exec dsh-codex-connect trust-origin http://192.168.1.20:3080
156
+ dsh plugin --profile web exec dsh-codex-connect trusted-origins
157
+ dsh plugin --profile web exec dsh-codex-connect untrust-origin http://192.168.1.20:3080
158
+ ```
159
+
160
+ Replace the example with the exact origin from the browser address bar, including scheme and port; do not enter the accessing device's IP, a bare host, a path, a query, or a fragment. Trust only a network you control, never expose this route to the public Internet, and use an SSH tunnel as the fallback when explicit network trust is not appropriate. The browser page only displays and copies this command; it never changes the allowlist itself.
152
161
  - If startup reports an `openai-codex` collision, an old `dsh-codex` bundle or manual provider row may already own the adapter. Inspect the effective configuration and remove only the confirmed conflicting owner. Do not delete auth files or unrelated providers.
153
162
  - Removing the package does not delete OAuth state. Run `logout` only when credential removal is intended.
154
163
 
155
164
  ## Compatibility and security boundary
156
165
 
157
- - Alpha compatibility targets the current Harness `0.1.0-rc.5` main-line composition and compatible `0.1.0-rc.6` plugin APIs, Node.js `^22.19.0 || >=24.0.0`, and the pinned `@earendil-works/pi-ai` Codex provider.
166
+ - The only verified compatibility combination is DSH plugin API packages `0.1.0-rc.6`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`; see [compatibility.json](compatibility.json).
167
+ - 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.
158
168
  - ChatGPT plan eligibility, model access, quotas, and backend behavior are controlled by OpenAI and may change.
159
169
  - The Codex endpoint does not enforce the ordinary Responses `max_output_tokens` field. Harness compaction still works, but that summary cap cannot be imposed server-side on this route.
160
170
  - Shell, filesystem, skills, MCP, subagents, approvals, permissions, attachments, session persistence, compaction, and recovery continue to come from the active Harness profile.
@@ -170,6 +180,10 @@ pnpm install --frozen-lockfile
170
180
  pnpm run check
171
181
  ```
172
182
 
183
+ ## Releases
184
+
185
+ Maintainers publish alpha versions through the [manual OIDC release workflow](.github/workflows/release.yml); see the [alpha release runbook](RELEASING.md) for the separate, short-lived `latest` promotion step.
186
+
173
187
  ## Legal / Acknowledgements
174
188
 
175
189
  Copyright 2026 Frank Song for the modifications and additional work in Codex Connect. This project includes software derived from [Yan-Zero/dsh-codex](https://github.com/Yan-Zero/dsh-codex); Copyright 2026 Yan-Zero is retained for the upstream material. Both are distributed under Apache-2.0, with details in [NOTICE](NOTICE). This project is not affiliated with or endorsed by OpenAI, ChatGPT, Codex, DeepSeek, or DeepSeek Harness.
@@ -0,0 +1,28 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "engines": {
4
+ "node": "^22.19.0 || >=24.0.0"
5
+ },
6
+ "dshPluginApi": {
7
+ "version": "0.1.0-rc.6",
8
+ "packages": [
9
+ "@deepseek-ai/dsh-agent",
10
+ "@deepseek-ai/dsh-atomic-write",
11
+ "@deepseek-ai/dsh-attachment",
12
+ "@deepseek-ai/dsh-home-paths",
13
+ "@deepseek-ai/dsh-host-webserver",
14
+ "@deepseek-ai/dsh-invariants",
15
+ "@deepseek-ai/dsh-llm",
16
+ "@deepseek-ai/dsh-llm-pi-ai",
17
+ "@deepseek-ai/dsh-fs",
18
+ "@deepseek-ai/dsh-session",
19
+ "@deepseek-ai/dsh-settings",
20
+ "@deepseek-ai/dsh-tools",
21
+ "@deepseek-ai/dsh-web"
22
+ ]
23
+ },
24
+ "piAi": {
25
+ "package": "@earendil-works/pi-ai",
26
+ "version": "0.82.1"
27
+ }
28
+ }
package/docs/README.zh.md CHANGED
@@ -28,7 +28,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
28
28
 
29
29
  预期结果:包被加入该 profile。这个动作不会更改 profile 的默认模型或全局搜索路由。
30
30
 
31
- 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.7`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.7'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
31
+ 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.9`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.9'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
32
32
 
33
33
  ### 2. 启动 Harness
34
34
 
@@ -149,12 +149,22 @@ dsh plugin --profile web exec dsh-codex-connect doctor --json
149
149
  - `doctor` 只读取进程与文件系统元数据。`doctor --json` 只输出一条可解析的非敏感 JSON,包含 schema version 1、包/版本/Node 信息、认证文件状态与安全 mode、能力、冲突状态和提示;它省略认证文件绝对路径以及 OAuth、账户和过期时间信息。
150
150
  - `status --json` 只输出 signed-in 或 signed-out 状态及包元数据。它只为判断登录态读取认证文件,但不会输出认证文件内容或启动 OAuth。
151
151
  - OAuth 单独存储于 `$DSH_HOME/.openai-codex-auth.json`(默认 `~/.dsh`)。`~/.codex/auth.json` 不会被复制或修改。支持的平台上,父目录与文件使用仅所有者可访问权限;写入采用原子替换,刷新写入使用跨进程文件锁。
152
+ - 默认情况下,OAuth 路由只接受 loopback 浏览器请求。当 DSH 在一台设备运行,而你从可信网络中的另一台设备打开 DSH 时,请在运行 DSH 的设备上显式批准浏览器地址栏中的 origin:
153
+
154
+ ```sh
155
+ dsh plugin --profile web exec dsh-codex-connect trust-origin http://192.168.1.20:3080
156
+ dsh plugin --profile web exec dsh-codex-connect trusted-origins
157
+ dsh plugin --profile web exec dsh-codex-connect untrust-origin http://192.168.1.20:3080
158
+ ```
159
+
160
+ 将示例替换为浏览器地址栏中的精确 origin,包括协议和端口;不要填写当前访问设备的 IP、裸主机、路径、query 或 fragment。只在你信任的网络中使用,不要把该路由暴露到公网;不适合显式信任网络时,请使用 SSH tunnel。浏览器页面只会显示并复制这条命令,不会自行修改授权列表。
152
161
  - 启动报告 `openai-codex` 冲突时,旧 `dsh-codex` bundle 或手动 provider 配置可能已占用该 adapter。先检查有效配置,只移除已确认的冲突所有者。不要删除认证文件或无关 provider。
153
162
  - 移除包不会删除 OAuth 状态;只有确实需要删除凭据时才运行 `logout`。
154
163
 
155
164
  ## 兼容性与安全边界
156
165
 
157
- - Alpha 面向当前 Harness `0.1.0-rc.5` 主线组合与兼容的 `0.1.0-rc.6` 插件 API、Node.js `^22.19.0 || >=24.0.0` 和固定版本的 `@earendil-works/pi-ai` Codex provider。
166
+ - 当前唯一已验证的兼容组合是 DSH 插件 API packages `0.1.0-rc.6`、`@earendil-works/pi-ai` `0.82.1` 和 Node.js `^22.19.0 || >=24.0.0`;详见 [compatibility.json](../compatibility.json)。
167
+ - 升级时请将 DSH 插件 API packages 与 `@earendil-works/pi-ai` 作为一组升级,再运行 `dsh-codex-connect doctor --json` 和兼容性检查。本契约不对未来版本作判断。
158
168
  - ChatGPT 套餐资格、模型权限、额度和后端行为由 OpenAI 控制,可能变化。
159
169
  - Codex 端点不会强制普通 Responses 的 `max_output_tokens` 字段。Harness 压缩仍可工作,但这个摘要上限不能由服务端在该路由上强制。
160
170
  - shell、文件系统、skills、MCP、subagents、审批、权限、附件、会话持久化、压缩与恢复继续由当前 Harness profile 提供。
@@ -170,6 +180,10 @@ pnpm install --frozen-lockfile
170
180
  pnpm run check
171
181
  ```
172
182
 
183
+ ## 发布
184
+
185
+ 维护者通过[手动 OIDC 发布 workflow](../.github/workflows/release.yml)发布 Alpha;`latest` 的独立短期提升步骤见 [Alpha 发布清单](../RELEASING.md)。
186
+
173
187
  ## 法律与致谢
174
188
 
175
189
  Codex Connect 的修改与新增工作 Copyright 2026 Frank Song。本项目包含派生自 [Yan-Zero/dsh-codex](https://github.com/Yan-Zero/dsh-codex) 的软件;上游内容继续保留 Copyright 2026 Yan-Zero。两部分均按 Apache-2.0 发布,详情见 [NOTICE](../NOTICE)。本项目与 OpenAI、ChatGPT、Codex、DeepSeek 或 DeepSeek Harness 不存在隶属关系,也未获得其背书。
package/docs/design.md CHANGED
@@ -10,9 +10,9 @@ The Host registers `llm-openai-codex` as the plugin-owned settings namespace and
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.
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.
14
14
 
15
- 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 must come from a loopback peer with a loopback Host and, when supplied, an exact loopback HTTP(S) Origin. 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.
15
+ 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
16
 
17
17
  ## Search and images
18
18
 
package/docs/design.zh.md CHANGED
@@ -10,9 +10,9 @@ Host 将 `llm-openai-codex` 注册为插件自有 settings namespace,并在 LL
10
10
 
11
11
  ## OAuth 持久化
12
12
 
13
- 插件使用 `$DSH_HOME/.openai-codex-auth.json`,与 Codex CLI/Desktop 状态分离。文件格式严格且有版本号;POSIX 上会拒绝组/其他用户可读文件。父目录和文件按仅所有者权限创建,写入采用原子替换,刷新修改使用 Harness 跨进程文件锁,返回给调用方的是凭据副本。
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 修改。
14
14
 
15
- 为兼容迁移,设置页路由、OAuth 路径和 provider id 不改名。浏览器请求必须来自 loopback 对端,并带有 loopback Host;若带 Origin,则必须与该本地 HTTP(S) 源精确匹配。登录挑战只接受不含凭据的 HTTPS 地址;30 秒内未得到地址、provider 已结束但没有地址、退出登录或插件卸载时,所有 waiter 都会被清理。只有显式登录会输出授权 URL 或代码;状态输出会脱敏。doctor 只用 `lstat` 检查元数据,不打开文件。
15
+ 为兼容迁移,设置页路由、OAuth 路径和 provider id 不改名。浏览器请求默认只允许 loopback;远程请求必须使用当前 sidecar 中的精确有效 HTTP(S) origin,不能带 cross-site Fetch Metadata,若带 Origin 还必须精确匹配。每次请求都会重新读取 sidecar;未知字段或错误 mode 会快速失败。登录挑战只接受不含凭据的 HTTPS 地址;30 秒内未得到地址、provider 已结束但没有地址、退出登录或插件卸载时,所有 waiter 都会被清理。只有显式登录会输出授权 URL 或代码;状态输出会脱敏。doctor 只用 `lstat` 检查元数据,不打开文件。
16
16
 
17
17
  ## 搜索与图片
18
18
 
package/lib/bin.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { A as loginOpenAICodex, E as CODEX_CONNECT_VERSION, I as openAICodexAuthPath, M as openAICodexAuthStatus, j as logoutOpenAICodex, w as diagnoseOpenAICodex } from "./src-Bk1Nq6B4.js";
2
+ import { E as CODEX_CONNECT_VERSION, J as loginOpenAICodex, U as OpenAICodexTrustedOriginsStore, W as normalizeTrustedOrigin, X as openAICodexAuthStatus, Y as logoutOpenAICodex, et as openAICodexAuthPath, w as diagnoseOpenAICodex } from "./src-DaDIQ3_t.js";
3
+ import { fileURLToPath } from "node:url";
3
4
  import { spawn } from "node:child_process";
4
5
  import { realpathSync } from "node:fs";
5
6
  import { createInterface } from "node:readline/promises";
6
- import { fileURLToPath } from "node:url";
7
7
  //#region src/bin.ts
8
8
  /** Standalone credential CLI for the optional OpenAI Codex bundle. */
9
9
  const JSON_SCHEMA_VERSION = 1;
@@ -65,22 +65,30 @@ async function answerPrompt(prompt, deviceCode, question) {
65
65
  function printHelp() {
66
66
  process.stdout.write([
67
67
  "Usage: dsh-codex-connect <doctor|login|logout|status> [--device-code|--json]",
68
+ " dsh-codex-connect trust-origin <origin>",
69
+ " dsh-codex-connect trusted-origins [--json]",
70
+ " dsh-codex-connect untrust-origin <origin>",
68
71
  "",
69
72
  " doctor inspect secret-free runtime and OAuth file metadata",
70
73
  " login sign in with a separate ChatGPT OAuth session",
71
74
  " logout remove the dsh credential without changing ~/.codex",
72
75
  " status report non-secret dsh credential state",
76
+ " trust-origin allow one exact browser origin to reach Web OAuth routes",
77
+ " trusted-origins list the currently allowed browser origins",
78
+ " untrust-origin remove one exact browser origin from the allowlist",
73
79
  " --device-code use headless device-code login (login only)",
74
- " --json emit one secret-free JSON document (doctor/status only)",
80
+ " --json emit one secret-free JSON document (doctor/status/trusted-origins only)",
75
81
  ""
76
82
  ].join("\n"));
77
83
  }
78
84
  function doctorExitCode(report) {
79
- return report.credentialFile.state === "permissions-too-broad" || report.credentialFile.state === "not-a-regular-file" || report.credentialFile.state === "unreadable-metadata" ? 1 : 0;
85
+ const credentialFailure = report.credentialFile.state === "permissions-too-broad" || report.credentialFile.state === "not-a-regular-file" || report.credentialFile.state === "unreadable-metadata";
86
+ const compatibilityFailure = report.compatibility !== void 0 && report.compatibility.status !== "compatible";
87
+ return credentialFailure || compatibilityFailure ? 1 : 0;
80
88
  }
81
89
  /** Project the diagnostic report without its absolute credential pathname. */
82
90
  function doctorJson(report) {
83
- return {
91
+ const result = {
84
92
  schemaVersion: JSON_SCHEMA_VERSION,
85
93
  package: report.package,
86
94
  version: report.version,
@@ -93,6 +101,8 @@ function doctorJson(report) {
93
101
  providerConflict: report.providerConflict,
94
102
  hints: report.hints
95
103
  };
104
+ if (report.compatibility !== void 0) result.compatibility = report.compatibility;
105
+ return result;
96
106
  }
97
107
  function printJson(value) {
98
108
  process.stdout.write(`${JSON.stringify(value)}\n`);
@@ -104,14 +114,24 @@ async function run(argv) {
104
114
  return 0;
105
115
  }
106
116
  const [rawAction, ...flags] = argv;
107
- if (rawAction !== "doctor" && rawAction !== "login" && rawAction !== "logout" && rawAction !== "status") {
108
- process.stderr.write(`dsh-codex-connect: expected doctor, login, logout, or status; got ${JSON.stringify(rawAction)}\n`);
117
+ if (![
118
+ "doctor",
119
+ "login",
120
+ "logout",
121
+ "status",
122
+ "trust-origin",
123
+ "trusted-origins",
124
+ "untrust-origin"
125
+ ].includes(rawAction)) {
126
+ process.stderr.write(`dsh-codex-connect: expected doctor, login, logout, status, trust-origin, trusted-origins, or untrust-origin; got ${JSON.stringify(rawAction)}\n`);
109
127
  return 1;
110
128
  }
111
129
  const action = rawAction;
112
- const deviceCode = flags.includes("--device-code");
113
- const jsonOutput = flags.includes("--json");
114
- if (flags.filter((flag) => flag !== "--device-code" && flag !== "--json").length > 0 || deviceCode && action !== "login" || jsonOutput && (action === "login" || action === "logout" || deviceCode)) {
130
+ const originArgument = action === "trust-origin" || action === "untrust-origin" ? flags[0] : void 0;
131
+ const optionFlags = action === "trust-origin" || action === "untrust-origin" ? flags.slice(1) : flags;
132
+ const deviceCode = optionFlags.includes("--device-code");
133
+ const jsonOutput = optionFlags.includes("--json");
134
+ if (optionFlags.filter((flag) => flag !== "--device-code" && flag !== "--json").length > 0 || deviceCode && action !== "login" || jsonOutput && (action === "login" || action === "logout" || deviceCode) || (action === "trust-origin" || action === "untrust-origin") && (originArgument === void 0 || optionFlags.length !== 0)) {
115
135
  process.stderr.write(`dsh-codex-connect: invalid options for ${action}: ${flags.join(" ")}\n`);
116
136
  return 1;
117
137
  }
@@ -126,6 +146,7 @@ async function run(argv) {
126
146
  process.stdout.write([
127
147
  `Codex Connect ${report.version} on ${report.node}`,
128
148
  `OAuth file metadata: ${report.credentialFile.state} (${report.credentialFile.path})`,
149
+ ...report.compatibility === void 0 ? [] : [`Compatibility: ${report.compatibility.status} (Node ${report.compatibility.node.installed ?? "unknown"}; DSH API ${report.compatibility.packages["@deepseek-ai/dsh-llm"].installed ?? "unknown"}; pi-ai ${report.compatibility.packages["@earendil-works/pi-ai"].installed ?? "unknown"})`],
129
150
  `Optional capability defaults: search=${report.capabilities.search ? "enabled" : "disabled"}, imageTool=${report.capabilities.imageTool ? "enabled" : "disabled"}`,
130
151
  "Harness defaults: unchanged by this plugin",
131
152
  ...report.hints.map((hint) => `Hint: ${hint}`),
@@ -153,6 +174,31 @@ async function run(argv) {
153
174
  process.stdout.write(`Codex Connect: signed in${suffix}\n`);
154
175
  return 0;
155
176
  }
177
+ case "trusted-origins": {
178
+ const origins = await new OpenAICodexTrustedOriginsStore().list();
179
+ if (jsonOutput) printJson({
180
+ schemaVersion: JSON_SCHEMA_VERSION,
181
+ origins
182
+ });
183
+ else for (const origin of origins) process.stdout.write(`${origin}\n`);
184
+ return 0;
185
+ }
186
+ case "trust-origin": {
187
+ if (originArgument === void 0) return 1;
188
+ const normalized = normalizeTrustedOrigin(originArgument);
189
+ const origins = await new OpenAICodexTrustedOriginsStore().trust(originArgument);
190
+ process.stdout.write(`Trusted browser origin: ${normalized}\n`);
191
+ process.stdout.write(`Trusted origins: ${origins.join(", ") || "(none)"}\n`);
192
+ return 0;
193
+ }
194
+ case "untrust-origin": {
195
+ if (originArgument === void 0) return 1;
196
+ const normalized = normalizeTrustedOrigin(originArgument);
197
+ const origins = await new OpenAICodexTrustedOriginsStore().untrust(originArgument);
198
+ process.stdout.write(`Untrusted browser origin: ${normalized}\n`);
199
+ process.stdout.write(`Trusted origins: ${origins.join(", ") || "(none)"}\n`);
200
+ return 0;
201
+ }
156
202
  case "logout":
157
203
  await logoutOpenAICodex();
158
204
  process.stdout.write(`Codex Connect: signed out; removed ${openAICodexAuthPath()}\n`);