dsh-codex-connect 0.1.0-alpha.4.7 → 0.1.0-alpha.4.8
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 +14 -1
- package/README.md +16 -2
- package/compatibility.json +28 -0
- package/docs/README.zh.md +16 -2
- package/docs/design.md +2 -2
- package/docs/design.zh.md +2 -2
- package/lib/bin.js +56 -10
- package/lib/client.js +88 -7
- package/lib/index.d.ts +66 -1
- package/lib/index.js +2 -2
- package/lib/{src-Bk1Nq6B4.js → src-DztMY1zT.js} +379 -26
- package/package.json +20 -16
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.
|
|
24
|
+
After `0.1.0-alpha.4.8` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.8`. 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.8'`.
|
|
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.
|
|
31
|
+
To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.8`. 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.8'`. 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
|
-
-
|
|
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.
|
|
31
|
+
如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.8`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.8'`。本地 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
|
-
-
|
|
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
|
|
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
|
|
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 {
|
|
2
|
+
import { E as CODEX_CONNECT_VERSION, G as openAICodexAuthStatus, R as OpenAICodexTrustedOriginsStore, U as loginOpenAICodex, W as logoutOpenAICodex, Y as openAICodexAuthPath, w as diagnoseOpenAICodex, z as normalizeTrustedOrigin } from "./src-DztMY1zT.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
|
-
|
|
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
|
-
|
|
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 (
|
|
108
|
-
|
|
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
|
|
113
|
-
const
|
|
114
|
-
|
|
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`);
|
package/lib/client.js
CHANGED
|
@@ -573,6 +573,19 @@ window.__ModuleLoader__.load({
|
|
|
573
573
|
borderRadius: 999,
|
|
574
574
|
background: "var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.08))"
|
|
575
575
|
};
|
|
576
|
+
const commandStyle = {
|
|
577
|
+
margin: 0,
|
|
578
|
+
padding: "10px 12px",
|
|
579
|
+
overflowX: "auto",
|
|
580
|
+
borderRadius: 8,
|
|
581
|
+
background: "var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.06))",
|
|
582
|
+
color: "var(--dsw-alias-label-primary)",
|
|
583
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
584
|
+
fontSize: 13,
|
|
585
|
+
lineHeight: "20px",
|
|
586
|
+
whiteSpace: "pre-wrap",
|
|
587
|
+
overflowWrap: "anywhere"
|
|
588
|
+
};
|
|
576
589
|
function progressFillStyle(percent) {
|
|
577
590
|
return {
|
|
578
591
|
width: `${Math.max(0, Math.min(100, percent))}%`,
|
|
@@ -666,9 +679,17 @@ window.__ModuleLoader__.load({
|
|
|
666
679
|
height: 9,
|
|
667
680
|
borderRadius: "50%",
|
|
668
681
|
flex: "0 0 auto",
|
|
669
|
-
background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" || status === "reauth-required" ? "var(--dsw-alias-state-error-primary, #d92d20)" : status === "signing-in" || status === "loading" ? "var(--dsw-alias-brand-primary, #1677ff)" : "var(--dsw-alias-label-dimmed, #9aa0a6)"
|
|
682
|
+
background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" || status === "reauth-required" || status === "remote-web-origin-not-trusted" ? "var(--dsw-alias-state-error-primary, #d92d20)" : status === "signing-in" || status === "loading" ? "var(--dsw-alias-brand-primary, #1677ff)" : "var(--dsw-alias-label-dimmed, #9aa0a6)"
|
|
670
683
|
};
|
|
671
684
|
}
|
|
685
|
+
var AccountRequestError = class extends Error {
|
|
686
|
+
code;
|
|
687
|
+
constructor(code, message) {
|
|
688
|
+
super(message);
|
|
689
|
+
this.code = code;
|
|
690
|
+
this.name = "AccountRequestError";
|
|
691
|
+
}
|
|
692
|
+
};
|
|
672
693
|
async function jsonRequest(path, method = "GET", signal) {
|
|
673
694
|
const response = await fetch(path, {
|
|
674
695
|
method,
|
|
@@ -678,8 +699,8 @@ window.__ModuleLoader__.load({
|
|
|
678
699
|
});
|
|
679
700
|
const value = await response.json().catch(() => void 0);
|
|
680
701
|
if (!response.ok) {
|
|
681
|
-
const
|
|
682
|
-
throw new
|
|
702
|
+
const code = typeof value === "object" && value !== null && "error" in value && typeof value.error === "string" ? value.error : `HTTP ${response.status}`;
|
|
703
|
+
throw new AccountRequestError(code, code);
|
|
683
704
|
}
|
|
684
705
|
return value;
|
|
685
706
|
}
|
|
@@ -688,7 +709,10 @@ window.__ModuleLoader__.load({
|
|
|
688
709
|
if (t === void 0) throw new Error("OpenAI Codex settings requires its translation function");
|
|
689
710
|
const [status, setStatus] = (0, react.useState)({ status: "loading" });
|
|
690
711
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
712
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
713
|
+
const [copyFailed, setCopyFailed] = (0, react.useState)(false);
|
|
691
714
|
const mounted = (0, react.useRef)(true);
|
|
715
|
+
const trustedOriginCommand = `dsh plugin --profile web exec dsh-codex-connect trust-origin ${window.location.origin}`;
|
|
692
716
|
(0, react.useEffect)(() => {
|
|
693
717
|
mounted.current = true;
|
|
694
718
|
return () => {
|
|
@@ -700,7 +724,7 @@ window.__ModuleLoader__.load({
|
|
|
700
724
|
const nextStatus = await jsonRequest(OPENAI_CODEX_AUTH_STATUS_PATH, "GET", signal);
|
|
701
725
|
if (mounted.current && signal?.aborted !== true) setStatus(nextStatus);
|
|
702
726
|
} catch (error) {
|
|
703
|
-
if (mounted.current && signal?.aborted !== true) setStatus({
|
|
727
|
+
if (mounted.current && signal?.aborted !== true) setStatus(error instanceof AccountRequestError && error.code === "remote-web-origin-not-trusted" ? { status: "remote-web-origin-not-trusted" } : {
|
|
704
728
|
status: "error",
|
|
705
729
|
message: error instanceof Error ? error.message : t("requestFailed")
|
|
706
730
|
});
|
|
@@ -746,7 +770,7 @@ window.__ModuleLoader__.load({
|
|
|
746
770
|
popup.location.replace(challenge.url);
|
|
747
771
|
} catch (error) {
|
|
748
772
|
popup?.close();
|
|
749
|
-
if (mounted.current) setStatus({
|
|
773
|
+
if (mounted.current) setStatus(error instanceof AccountRequestError && error.code === "remote-web-origin-not-trusted" ? { status: "remote-web-origin-not-trusted" } : {
|
|
750
774
|
status: "error",
|
|
751
775
|
message: error instanceof Error ? error.message : t("requestFailed")
|
|
752
776
|
});
|
|
@@ -754,6 +778,16 @@ window.__ModuleLoader__.load({
|
|
|
754
778
|
if (mounted.current) setBusy(false);
|
|
755
779
|
}
|
|
756
780
|
};
|
|
781
|
+
const copyTrustedOriginCommand = async () => {
|
|
782
|
+
setCopyFailed(false);
|
|
783
|
+
try {
|
|
784
|
+
if (navigator.clipboard?.writeText === void 0) throw new Error("clipboard unavailable");
|
|
785
|
+
await navigator.clipboard.writeText(trustedOriginCommand);
|
|
786
|
+
if (mounted.current) setCopied(true);
|
|
787
|
+
} catch {
|
|
788
|
+
if (mounted.current) setCopyFailed(true);
|
|
789
|
+
}
|
|
790
|
+
};
|
|
757
791
|
const signOut = async () => {
|
|
758
792
|
setBusy(true);
|
|
759
793
|
try {
|
|
@@ -768,7 +802,7 @@ window.__ModuleLoader__.load({
|
|
|
768
802
|
if (mounted.current) setBusy(false);
|
|
769
803
|
}
|
|
770
804
|
};
|
|
771
|
-
const label = status.status === "signed-in" ? t("signedIn") : status.status === "loading" ? t("loadingAccount") : status.status === "signing-in" ? t("signingIn") : status.status === "reauth-required" ? t("reauthRequired") : status.status === "error" ? t("requestFailed") : t("signedOut");
|
|
805
|
+
const label = status.status === "signed-in" ? t("signedIn") : status.status === "loading" ? t("loadingAccount") : status.status === "signing-in" ? t("signingIn") : status.status === "reauth-required" ? t("reauthRequired") : status.status === "remote-web-origin-not-trusted" ? t("remoteOriginTitle") : status.status === "error" ? t("requestFailed") : t("signedOut");
|
|
772
806
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
773
807
|
style: embedded ? embeddedPageStyle : pageStyle,
|
|
774
808
|
...embedded ? { "aria-label": t("title") } : { "aria-labelledby": "openai-codex-settings-title" },
|
|
@@ -798,7 +832,7 @@ window.__ModuleLoader__.load({
|
|
|
798
832
|
"aria-hidden": "true",
|
|
799
833
|
style: dotStyle(status.status)
|
|
800
834
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
801
|
-
}), status.status === "loading" ? null : status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
835
|
+
}), status.status === "loading" || status.status === "remote-web-origin-not-trusted" ? null : status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
802
836
|
type: "button",
|
|
803
837
|
style: buttonStyle,
|
|
804
838
|
disabled: busy,
|
|
@@ -820,6 +854,41 @@ window.__ModuleLoader__.load({
|
|
|
820
854
|
style: errorStyle,
|
|
821
855
|
children: status.message
|
|
822
856
|
}) : null,
|
|
857
|
+
status.status === "remote-web-origin-not-trusted" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
858
|
+
style: {
|
|
859
|
+
display: "flex",
|
|
860
|
+
flexDirection: "column",
|
|
861
|
+
gap: 10
|
|
862
|
+
},
|
|
863
|
+
children: [
|
|
864
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
865
|
+
style: errorStyle,
|
|
866
|
+
children: t("remoteOriginDescription")
|
|
867
|
+
}),
|
|
868
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
869
|
+
style: bodyStyle,
|
|
870
|
+
children: t("remoteOriginCommandHelp")
|
|
871
|
+
}),
|
|
872
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
873
|
+
style: commandStyle,
|
|
874
|
+
children: trustedOriginCommand
|
|
875
|
+
}),
|
|
876
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
877
|
+
style: rowStyle,
|
|
878
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
879
|
+
type: "button",
|
|
880
|
+
style: buttonStyle,
|
|
881
|
+
onClick: () => {
|
|
882
|
+
copyTrustedOriginCommand();
|
|
883
|
+
},
|
|
884
|
+
children: copied ? t("remoteOriginCopied") : t("remoteOriginCopy")
|
|
885
|
+
}), copyFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
886
|
+
style: errorStyle,
|
|
887
|
+
children: t("remoteOriginCopyFailed")
|
|
888
|
+
}) : null]
|
|
889
|
+
})
|
|
890
|
+
]
|
|
891
|
+
}) : null,
|
|
823
892
|
status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageLimits, {
|
|
824
893
|
usage: status.usage,
|
|
825
894
|
...status.quotaError === void 0 ? {} : { quotaError: status.quotaError },
|
|
@@ -958,6 +1027,12 @@ window.__ModuleLoader__.load({
|
|
|
958
1027
|
available: "Available",
|
|
959
1028
|
quotaUnavailable: "Usage limits are temporarily unavailable.",
|
|
960
1029
|
requestFailed: "The OpenAI Codex account request failed.",
|
|
1030
|
+
remoteOriginTitle: "Remote browser origin is not trusted",
|
|
1031
|
+
remoteOriginDescription: "Codex Connect accepts browser OAuth requests only from trusted local pages or origins explicitly approved by the device owner.",
|
|
1032
|
+
remoteOriginCommandHelp: "Run this command manually on the device that runs DSH, then reload this page. This page never runs it:",
|
|
1033
|
+
remoteOriginCopy: "Copy trust command",
|
|
1034
|
+
remoteOriginCopied: "Copied",
|
|
1035
|
+
remoteOriginCopyFailed: "Copy was unavailable. Copy the command manually.",
|
|
961
1036
|
capabilitiesHeading: "Optional capabilities",
|
|
962
1037
|
capabilitiesIntro: "Choose which extra Codex capabilities this dsh profile may register.",
|
|
963
1038
|
enableSearch: "Enable Codex search provider",
|
|
@@ -1017,6 +1092,12 @@ window.__ModuleLoader__.load({
|
|
|
1017
1092
|
available: "可用",
|
|
1018
1093
|
quotaUnavailable: "暂时无法获取使用额度。",
|
|
1019
1094
|
requestFailed: "OpenAI Codex 账户请求失败。",
|
|
1095
|
+
remoteOriginTitle: "未信任远程浏览器 origin",
|
|
1096
|
+
remoteOriginDescription: "Codex Connect 只接受来自本机页面或设备所有者明确批准的 origin 的浏览器 OAuth 请求。",
|
|
1097
|
+
remoteOriginCommandHelp: "请在运行 DSH 的设备上手动执行下面的服务器命令,然后重新加载此页面。本页面不会自动执行:",
|
|
1098
|
+
remoteOriginCopy: "复制授权命令",
|
|
1099
|
+
remoteOriginCopied: "已复制",
|
|
1100
|
+
remoteOriginCopyFailed: "当前无法访问剪贴板,请手动复制命令。",
|
|
1020
1101
|
capabilitiesHeading: "可选能力",
|
|
1021
1102
|
capabilitiesIntro: "选择允许此 dsh profile 注册哪些额外的 Codex 能力。",
|
|
1022
1103
|
enableSearch: "启用 Codex 搜索提供方",
|
package/lib/index.d.ts
CHANGED
|
@@ -7,6 +7,68 @@ import { Context } from "@deepseek-ai/cordis";
|
|
|
7
7
|
/** Stable Codex tool name. */
|
|
8
8
|
declare const VIEW_IMAGE_TOOL_NAME = "view_image";
|
|
9
9
|
//#endregion
|
|
10
|
+
//#region src/compatibility.d.ts
|
|
11
|
+
declare const COMPATIBILITY_SCHEMA_VERSION: 1;
|
|
12
|
+
declare const SUPPORTED_NODE_RANGE = "^22.19.0 || >=24.0.0";
|
|
13
|
+
declare const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.0-rc.6";
|
|
14
|
+
declare const SUPPORTED_PI_AI_VERSION = "0.82.1";
|
|
15
|
+
declare const PI_AI_PACKAGE = "@earendil-works/pi-ai";
|
|
16
|
+
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-web"];
|
|
17
|
+
declare const COMPATIBILITY_PACKAGES: readonly ["@deepseek-ai/dsh-llm", "@deepseek-ai/dsh-llm-pi-ai", "@earendil-works/pi-ai"];
|
|
18
|
+
type CompatibilityPackageName = (typeof COMPATIBILITY_PACKAGES)[number];
|
|
19
|
+
type CompatibilityStatus = 'compatible' | 'incompatible' | 'unknown';
|
|
20
|
+
interface CompatibilityEntry {
|
|
21
|
+
supported: string;
|
|
22
|
+
installed: string | null;
|
|
23
|
+
status: CompatibilityStatus;
|
|
24
|
+
}
|
|
25
|
+
interface CompatibilityReport {
|
|
26
|
+
schemaVersion: typeof COMPATIBILITY_SCHEMA_VERSION;
|
|
27
|
+
status: CompatibilityStatus;
|
|
28
|
+
node: CompatibilityEntry;
|
|
29
|
+
packages: Record<CompatibilityPackageName, CompatibilityEntry>;
|
|
30
|
+
}
|
|
31
|
+
interface CompatibilityEvaluationInput {
|
|
32
|
+
/** Node version to evaluate; defaults to the running process in detectCompatibility. */
|
|
33
|
+
nodeVersion?: string | null;
|
|
34
|
+
/** Alias accepted by callers that already group installed values. */
|
|
35
|
+
node?: string | null;
|
|
36
|
+
/** Installed package versions keyed by package name. */
|
|
37
|
+
packageVersions?: Partial<Record<CompatibilityPackageName, string | null | undefined>>;
|
|
38
|
+
/** Alias accepted by callers that already group installed values. */
|
|
39
|
+
packages?: Partial<Record<CompatibilityPackageName, string | null | undefined>>;
|
|
40
|
+
/** Nested installed values are useful when feeding a captured diagnostic fixture. */
|
|
41
|
+
installed?: {
|
|
42
|
+
node?: string | null;
|
|
43
|
+
packages?: Partial<Record<CompatibilityPackageName, string | null | undefined>>;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
interface CompatibilityDetectionOptions extends CompatibilityEvaluationInput {
|
|
47
|
+
/** Test seam for package metadata resolution; no package paths are returned. */
|
|
48
|
+
readPackageVersion?: (name: CompatibilityPackageName) => string | null | undefined | Promise<string | null | undefined>;
|
|
49
|
+
}
|
|
50
|
+
/** Public contract data mirrored by compatibility.json without importing JSON at runtime. */
|
|
51
|
+
declare const COMPATIBILITY_CONTRACT: {
|
|
52
|
+
readonly schemaVersion: 1;
|
|
53
|
+
readonly engines: {
|
|
54
|
+
readonly node: "^22.19.0 || >=24.0.0";
|
|
55
|
+
};
|
|
56
|
+
readonly dshPluginApi: {
|
|
57
|
+
readonly version: "0.1.0-rc.6";
|
|
58
|
+
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-web"];
|
|
59
|
+
};
|
|
60
|
+
readonly piAi: {
|
|
61
|
+
readonly package: "@earendil-works/pi-ai";
|
|
62
|
+
readonly version: "0.82.1";
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/** Evaluate a captured set of versions without touching the filesystem. */
|
|
66
|
+
declare function evaluateCompatibility(input?: CompatibilityEvaluationInput): CompatibilityReport;
|
|
67
|
+
/** Alias for callers that prefer assessment terminology. */
|
|
68
|
+
declare const assessCompatibility: typeof evaluateCompatibility;
|
|
69
|
+
/** Read installed package metadata and return only versions and statuses. */
|
|
70
|
+
declare function detectCompatibility(options?: CompatibilityDetectionOptions): Promise<CompatibilityReport>;
|
|
71
|
+
//#endregion
|
|
10
72
|
//#region src/doctor.d.ts
|
|
11
73
|
/** Inputs that are safe to obtain without booting OAuth. */
|
|
12
74
|
interface OpenAICodexDiagnosticOptions {
|
|
@@ -18,6 +80,8 @@ interface OpenAICodexDiagnosticOptions {
|
|
|
18
80
|
enableSearch?: boolean;
|
|
19
81
|
/** Whether the optional image tool is enabled. */
|
|
20
82
|
enableImageTool?: boolean;
|
|
83
|
+
/** Optional pure-function seam for compatibility checks in tests/diagnostic callers. */
|
|
84
|
+
compatibilityOptions?: CompatibilityDetectionOptions;
|
|
21
85
|
}
|
|
22
86
|
interface OpenAICodexDiagnosticReport {
|
|
23
87
|
package: 'dsh-codex-connect';
|
|
@@ -36,6 +100,7 @@ interface OpenAICodexDiagnosticReport {
|
|
|
36
100
|
changesHarnessSearchRoute: false;
|
|
37
101
|
};
|
|
38
102
|
providerConflict: boolean;
|
|
103
|
+
compatibility: CompatibilityReport;
|
|
39
104
|
hints: string[];
|
|
40
105
|
}
|
|
41
106
|
/** Actionable message for legacy/manual `openai-codex` adapter collisions. */
|
|
@@ -330,4 +395,4 @@ declare const Config: z<Config>;
|
|
|
330
395
|
*/
|
|
331
396
|
declare function apply(ctx: Context, config: Config): void;
|
|
332
397
|
//#endregion
|
|
333
|
-
export { Config, 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, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexIndividualLimit, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, type OpenAICodexUsage, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, decodeOpenAICodexSettings, diagnoseOpenAICodex, inject, installOpenAICodexSearchEvent, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
|
|
398
|
+
export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, type CompatibilityDetectionOptions, type CompatibilityEntry, type CompatibilityEvaluationInput, type CompatibilityPackageName, type CompatibilityReport, type CompatibilityStatus, Config, 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, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexIndividualLimit, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, type OpenAICodexUsage, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, installOpenAICodexSearchEvent, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
export { Config, 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, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, OpenAICodexCredentialStore, OpenAICodexSearchProvider, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, decodeOpenAICodexSettings, diagnoseOpenAICodex, inject, installOpenAICodexSearchEvent, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
|
|
1
|
+
import { A as DSH_PLUGIN_API_PACKAGES, B as OPENAI_CODEX_USAGE_URL, C as assertNoOpenAICodexProviderConflict, D as COMPATIBILITY_CONTRACT, F as assessCompatibility, G as openAICodexAuthStatus, H as readOpenAICodexRateLimits, I as detectCompatibility, J as OpenAICodexCredentialStore, K as OPENAI_CODEX_AUTH_FILENAME, L as evaluateCompatibility, M as SUPPORTED_DSH_PLUGIN_API_VERSION, N as SUPPORTED_NODE_RANGE, O as COMPATIBILITY_PACKAGES, P as SUPPORTED_PI_AI_VERSION, S as VIEW_IMAGE_TOOL_NAME, T as openAICodexConflictMessage, U as loginOpenAICodex, V as parseOpenAICodexUsage, W as logoutOpenAICodex, Y as openAICodexAuthPath, _ as decodeOpenAICodexSettings, a as name, b as installOpenAICodexSearchEvent, c as OPENAI_CODEX_SEARCH_URL, d as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, f as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, g as OPENAI_CODEX_SETTINGS_NAMESPACE, h as DEFAULT_OPENAI_CODEX_SETTINGS, i as inject, j as PI_AI_PACKAGE, k as COMPATIBILITY_SCHEMA_VERSION, l as OpenAICodexSearchProvider, m as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, n as OPENAI_CODEX_SETTINGS_NS, o as OPENAI_CODEX_BASE_URL, p as DEFAULT_OPENAI_CODEX_SEARCH_MODE, q as OPENAI_CODEX_PROVIDER, r as apply, s as OPENAI_CODEX_SEARCH_PROVIDER, t as Config, u as mapOpenAICodexSearchResponse, v as resolveOpenAICodexSettings, w as diagnoseOpenAICodex, x as recordOpenAICodexSearchRequest, y as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT } from "./src-DztMY1zT.js";
|
|
2
|
+
export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, Config, 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, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, OpenAICodexCredentialStore, OpenAICodexSearchProvider, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, installOpenAICodexSearchEvent, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
|
|
@@ -6,9 +6,10 @@ import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-code
|
|
|
6
6
|
import { createUserMessage, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
7
7
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
8
8
|
import { lstat, mkdir, readFile, rm, stat } from "node:fs/promises";
|
|
9
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
9
|
+
import { basename, dirname, join, parse, resolve } from "node:path";
|
|
10
10
|
import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
|
|
11
11
|
import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
12
13
|
import { AttachmentId } from "@deepseek-ai/dsh-attachment";
|
|
13
14
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
14
15
|
import { lookup } from "node:dns/promises";
|
|
@@ -29,16 +30,16 @@ const OPENAI_CODEX_AUTH_FILENAME = ".openai-codex-auth.json";
|
|
|
29
30
|
/** Current on-disk format; pre-release readers reject every other version. */
|
|
30
31
|
const AUTH_FORMAT_VERSION = 1;
|
|
31
32
|
/** Whether a filesystem error reports an absent path. */
|
|
32
|
-
function isENOENT(error) {
|
|
33
|
+
function isENOENT$1(error) {
|
|
33
34
|
return error?.code === "ENOENT";
|
|
34
35
|
}
|
|
35
36
|
/** Reject a credential document readable by another POSIX user. */
|
|
36
|
-
async function assertOwnerOnly(filename) {
|
|
37
|
+
async function assertOwnerOnly$1(filename) {
|
|
37
38
|
let mode;
|
|
38
39
|
try {
|
|
39
40
|
mode = (await stat(filename)).mode;
|
|
40
41
|
} catch (error) {
|
|
41
|
-
if (isENOENT(error)) return;
|
|
42
|
+
if (isENOENT$1(error)) return;
|
|
42
43
|
throw error;
|
|
43
44
|
}
|
|
44
45
|
/* v8 ignore next -- native Windows coverage takes the mode-less branch */
|
|
@@ -48,7 +49,7 @@ async function assertOwnerOnly(filename) {
|
|
|
48
49
|
/* v8 ignore stop */
|
|
49
50
|
}
|
|
50
51
|
/** Validate the strict JSON document without quoting token-bearing input. */
|
|
51
|
-
function parseDocument(text, filename) {
|
|
52
|
+
function parseDocument$1(text, filename) {
|
|
52
53
|
let value;
|
|
53
54
|
try {
|
|
54
55
|
value = JSON.parse(text);
|
|
@@ -105,15 +106,15 @@ var OpenAICodexCredentialStore = class {
|
|
|
105
106
|
}
|
|
106
107
|
/** Read and validate the current document without acquiring the writer lock. */
|
|
107
108
|
async readCurrent() {
|
|
108
|
-
await assertOwnerOnly(this.filename);
|
|
109
|
+
await assertOwnerOnly$1(this.filename);
|
|
109
110
|
let text;
|
|
110
111
|
try {
|
|
111
112
|
text = await readFile(this.filename, "utf8");
|
|
112
113
|
} catch (error) {
|
|
113
|
-
if (isENOENT(error)) return void 0;
|
|
114
|
+
if (isENOENT$1(error)) return void 0;
|
|
114
115
|
throw error;
|
|
115
116
|
}
|
|
116
|
-
return cloneCredential(parseDocument(text, this.filename).credential);
|
|
117
|
+
return cloneCredential(parseDocument$1(text, this.filename).credential);
|
|
117
118
|
}
|
|
118
119
|
/** @inheritdoc */
|
|
119
120
|
async read(providerId) {
|
|
@@ -137,7 +138,7 @@ var OpenAICodexCredentialStore = class {
|
|
|
137
138
|
const current = await this.readCurrent();
|
|
138
139
|
const candidate = await fn(current);
|
|
139
140
|
if (candidate === void 0) return current;
|
|
140
|
-
const document = parseDocument(JSON.stringify({
|
|
141
|
+
const document = parseDocument$1(JSON.stringify({
|
|
141
142
|
version: AUTH_FORMAT_VERSION,
|
|
142
143
|
credential: candidate
|
|
143
144
|
}), this.filename);
|
|
@@ -405,6 +406,172 @@ const OPENAI_CODEX_AUTH_STATUS_PATH = "/plugins/dsh-openai-codex/auth/status";
|
|
|
405
406
|
const OPENAI_CODEX_AUTH_LOGIN_PATH = "/plugins/dsh-openai-codex/auth/login";
|
|
406
407
|
/** Plugin-owned logout endpoint consumed by its browser half. */
|
|
407
408
|
const OPENAI_CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-openai-codex/auth/logout";
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/trusted-origins.ts
|
|
411
|
+
/** Owner-only allowlist for browser origins that may reach the Web OAuth routes. */
|
|
412
|
+
/** Basename of the DSH-home-scoped browser-origin allowlist. */
|
|
413
|
+
const OPENAI_CODEX_TRUSTED_ORIGINS_FILENAME = ".openai-codex-trusted-origins.json";
|
|
414
|
+
/** Only supported policy mode; a future mode must not be silently accepted. */
|
|
415
|
+
const TRUSTED_ORIGINS_MODE = "allowlist";
|
|
416
|
+
/** Whether a filesystem error reports an absent path. */
|
|
417
|
+
function isENOENT(error) {
|
|
418
|
+
return error?.code === "ENOENT";
|
|
419
|
+
}
|
|
420
|
+
/** Reject a sidecar readable by another POSIX user. */
|
|
421
|
+
async function assertOwnerOnly(filename) {
|
|
422
|
+
let metadata;
|
|
423
|
+
try {
|
|
424
|
+
metadata = await lstat(filename);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
if (isENOENT(error)) return;
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
if (!metadata.isFile()) throw new Error(`openai-codex: ${filename} is not a regular file`);
|
|
430
|
+
/* v8 ignore next -- native Windows coverage takes the mode-less branch */
|
|
431
|
+
if (process.platform === "win32") return;
|
|
432
|
+
/* v8 ignore start -- POSIX tests cover this branch; Windows cannot express it */
|
|
433
|
+
if ((metadata.mode & 63) !== 0) throw new Error(`openai-codex: ${filename} is readable beyond its owner (mode ${(metadata.mode & 511).toString(8)}); run "chmod 600 ${filename}" before starting again`);
|
|
434
|
+
/* v8 ignore stop */
|
|
435
|
+
}
|
|
436
|
+
/** Reject malformed input without echoing its contents into an error. */
|
|
437
|
+
function parseDocument(text, filename) {
|
|
438
|
+
let value;
|
|
439
|
+
try {
|
|
440
|
+
value = JSON.parse(text);
|
|
441
|
+
} catch {
|
|
442
|
+
throw new Error(`openai-codex: ${filename} is not valid JSON`);
|
|
443
|
+
}
|
|
444
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`openai-codex: ${filename} must contain an object`);
|
|
445
|
+
const document = value;
|
|
446
|
+
if (Object.keys(document).some((key) => ![
|
|
447
|
+
"version",
|
|
448
|
+
"mode",
|
|
449
|
+
"origins"
|
|
450
|
+
].includes(key))) throw new Error(`openai-codex: ${filename} contains an unknown top-level field`);
|
|
451
|
+
if (document["version"] !== 1) throw new Error(`openai-codex: ${filename} has unsupported trusted-origins format version ${String(document["version"])}`);
|
|
452
|
+
if (document["mode"] !== "allowlist") throw new Error(`openai-codex: ${filename} has unsupported trusted-origins mode`);
|
|
453
|
+
const rawOrigins = document["origins"];
|
|
454
|
+
if (!Array.isArray(rawOrigins)) throw new Error(`openai-codex: ${filename} origins must be an array`);
|
|
455
|
+
const origins = /* @__PURE__ */ new Set();
|
|
456
|
+
for (const rawOrigin of rawOrigins) {
|
|
457
|
+
if (typeof rawOrigin !== "string") throw new Error(`openai-codex: ${filename} origins must contain strings`);
|
|
458
|
+
try {
|
|
459
|
+
origins.add(normalizeTrustedOrigin(rawOrigin));
|
|
460
|
+
} catch {
|
|
461
|
+
throw new Error(`openai-codex: ${filename} contains an invalid trusted origin`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return {
|
|
465
|
+
version: 1,
|
|
466
|
+
mode: TRUSTED_ORIGINS_MODE,
|
|
467
|
+
origins: [...origins].sort()
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Normalize one exact browser origin.
|
|
472
|
+
*
|
|
473
|
+
* Only HTTP(S) origins are accepted. Credentials, non-root paths, queries,
|
|
474
|
+
* fragments, wildcards, and CIDR-looking host paths are rejected. WHATWG URL
|
|
475
|
+
* normalization lowercases the scheme/host and removes default ports.
|
|
476
|
+
*/
|
|
477
|
+
function normalizeTrustedOrigin(rawOrigin) {
|
|
478
|
+
if (typeof rawOrigin !== "string" || rawOrigin.length === 0 || rawOrigin.trim() !== rawOrigin) throw new Error("trusted origin must be a non-empty URL without surrounding whitespace");
|
|
479
|
+
let origin;
|
|
480
|
+
try {
|
|
481
|
+
origin = new URL(rawOrigin);
|
|
482
|
+
} catch {
|
|
483
|
+
throw new Error("trusted origin must be a valid URL");
|
|
484
|
+
}
|
|
485
|
+
if (origin.protocol !== "http:" && origin.protocol !== "https:") throw new Error("trusted origin protocol must be http or https");
|
|
486
|
+
if (origin.username !== "" || origin.password !== "") throw new Error("trusted origin must not contain credentials");
|
|
487
|
+
if (origin.pathname !== "/" || origin.search !== "" || origin.hash !== "") throw new Error("trusted origin must not contain a path, query, or fragment");
|
|
488
|
+
if (origin.hostname === "" || origin.hostname.includes("*")) throw new Error("trusted origin host must be exact");
|
|
489
|
+
if (origin.pathname !== "/" || /(?:^|\/)\d+\/\d+$/u.test(rawOrigin)) throw new Error("trusted origin must not be a CIDR or path");
|
|
490
|
+
if (origin.origin === "null") throw new Error("trusted origin must have an HTTP(S) host");
|
|
491
|
+
return origin.origin;
|
|
492
|
+
}
|
|
493
|
+
/** Resolve the sidecar path under one DSH home. */
|
|
494
|
+
function openAICodexTrustedOriginsPath(dshHome) {
|
|
495
|
+
return resolve(join(resolveDshHome(dshHome), OPENAI_CODEX_TRUSTED_ORIGINS_FILENAME));
|
|
496
|
+
}
|
|
497
|
+
/** File-backed exact-origin allowlist. */
|
|
498
|
+
var OpenAICodexTrustedOriginsStore = class {
|
|
499
|
+
/** Absolute sidecar path. */
|
|
500
|
+
filename;
|
|
501
|
+
constructor(filename = openAICodexTrustedOriginsPath()) {
|
|
502
|
+
this.filename = resolve(filename);
|
|
503
|
+
}
|
|
504
|
+
async readCurrent() {
|
|
505
|
+
await assertOwnerOnly(this.filename);
|
|
506
|
+
let text;
|
|
507
|
+
try {
|
|
508
|
+
text = await readFile(this.filename, "utf8");
|
|
509
|
+
} catch (error) {
|
|
510
|
+
if (isENOENT(error)) return {
|
|
511
|
+
version: 1,
|
|
512
|
+
mode: TRUSTED_ORIGINS_MODE,
|
|
513
|
+
origins: []
|
|
514
|
+
};
|
|
515
|
+
throw error;
|
|
516
|
+
}
|
|
517
|
+
return parseDocument(text, this.filename);
|
|
518
|
+
}
|
|
519
|
+
/** Read the current canonical list without acquiring the writer lock. */
|
|
520
|
+
async list() {
|
|
521
|
+
return [...(await this.readCurrent()).origins];
|
|
522
|
+
}
|
|
523
|
+
/** Whether an exact normalized origin is currently trusted. */
|
|
524
|
+
async has(origin) {
|
|
525
|
+
const normalized = normalizeTrustedOrigin(origin);
|
|
526
|
+
return (await this.readCurrent()).origins.includes(normalized);
|
|
527
|
+
}
|
|
528
|
+
/** Add one origin idempotently and return the resulting sorted list. */
|
|
529
|
+
async trust(origin) {
|
|
530
|
+
const normalized = normalizeTrustedOrigin(origin);
|
|
531
|
+
await mkdir(dirname(this.filename), {
|
|
532
|
+
recursive: true,
|
|
533
|
+
mode: 448
|
|
534
|
+
});
|
|
535
|
+
return withFileLock(this.filename, async () => {
|
|
536
|
+
const current = await this.readCurrent();
|
|
537
|
+
if (current.origins.includes(normalized)) return [...current.origins];
|
|
538
|
+
const next = {
|
|
539
|
+
version: 1,
|
|
540
|
+
mode: TRUSTED_ORIGINS_MODE,
|
|
541
|
+
origins: [...current.origins, normalized].sort()
|
|
542
|
+
};
|
|
543
|
+
await writeFileAtomic(this.filename, `${JSON.stringify(next, null, 2)}\n`, {
|
|
544
|
+
mode: 384,
|
|
545
|
+
dirMode: 448
|
|
546
|
+
});
|
|
547
|
+
return [...next.origins];
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
/** Remove one origin idempotently and return the resulting sorted list. */
|
|
551
|
+
async untrust(origin) {
|
|
552
|
+
const normalized = normalizeTrustedOrigin(origin);
|
|
553
|
+
await mkdir(dirname(this.filename), {
|
|
554
|
+
recursive: true,
|
|
555
|
+
mode: 448
|
|
556
|
+
});
|
|
557
|
+
return withFileLock(this.filename, async () => {
|
|
558
|
+
const current = await this.readCurrent();
|
|
559
|
+
if (!current.origins.includes(normalized)) return [...current.origins];
|
|
560
|
+
const next = {
|
|
561
|
+
version: 1,
|
|
562
|
+
mode: TRUSTED_ORIGINS_MODE,
|
|
563
|
+
origins: current.origins.filter((candidate) => candidate !== normalized)
|
|
564
|
+
};
|
|
565
|
+
await writeFileAtomic(this.filename, `${JSON.stringify(next, null, 2)}\n`, {
|
|
566
|
+
mode: 384,
|
|
567
|
+
dirMode: 448
|
|
568
|
+
});
|
|
569
|
+
return [...next.origins];
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
/** Stable, non-sensitive error returned when a browser origin needs CLI trust. */
|
|
574
|
+
const REMOTE_WEB_ORIGIN_NOT_TRUSTED = "remote-web-origin-not-trusted";
|
|
408
575
|
/** Redact provider diagnostics before they cross to the browser. */
|
|
409
576
|
function safeMessage(error) {
|
|
410
577
|
return (error instanceof Error ? error.message : String(error)).replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[redacted token]").replace(/(\b(?:code|token|refresh_token|access_token)=)[^&\s]+/giu, "$1[redacted]").slice(0, 1e3);
|
|
@@ -569,25 +736,61 @@ function loopbackHost(rawHost) {
|
|
|
569
736
|
}
|
|
570
737
|
function exactOrigin(req, rawHost, rawOrigin) {
|
|
571
738
|
try {
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
const encrypted = req.socket.encrypted === true;
|
|
575
|
-
return origin.origin === new URL(`${encrypted ? "https" : "http"}://${rawHost}`).origin;
|
|
739
|
+
const effective = normalizeTrustedOrigin(`${req.socket.encrypted === true ? "https" : "http"}://${rawHost}`);
|
|
740
|
+
return normalizeTrustedOrigin(rawOrigin) === effective;
|
|
576
741
|
} catch {
|
|
577
742
|
return false;
|
|
578
743
|
}
|
|
579
744
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
745
|
+
function effectiveOrigin(req, rawHost) {
|
|
746
|
+
try {
|
|
747
|
+
return normalizeTrustedOrigin(`${req.socket.encrypted === true ? "https" : "http"}://${rawHost}`);
|
|
748
|
+
} catch {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function sameOriginMetadata(req, host) {
|
|
587
753
|
const origin = req.headers.origin;
|
|
588
754
|
if (origin === void 0) return true;
|
|
589
755
|
return typeof origin === "string" && exactOrigin(req, host, origin);
|
|
590
756
|
}
|
|
757
|
+
/** Evaluate one request against loopback defaults and the current sidecar. */
|
|
758
|
+
async function trustedRequestDecision(req, trustedOrigins = new OpenAICodexTrustedOriginsStore()) {
|
|
759
|
+
const remote = req.socket.remoteAddress;
|
|
760
|
+
const localPeer = remote === "127.0.0.1" || remote === "::1" || remote === "::ffff:127.0.0.1";
|
|
761
|
+
const fetchSite = req.headers["sec-fetch-site"];
|
|
762
|
+
if (typeof fetchSite === "string" ? fetchSite.trim().toLowerCase() === "cross-site" : Array.isArray(fetchSite) && fetchSite.some((value) => value.trim().toLowerCase() === "cross-site")) return {
|
|
763
|
+
trusted: false,
|
|
764
|
+
error: "forbidden"
|
|
765
|
+
};
|
|
766
|
+
const host = req.headers.host;
|
|
767
|
+
if (typeof host !== "string") return {
|
|
768
|
+
trusted: false,
|
|
769
|
+
error: "forbidden"
|
|
770
|
+
};
|
|
771
|
+
const origin = effectiveOrigin(req, host);
|
|
772
|
+
if (origin === void 0) return {
|
|
773
|
+
trusted: false,
|
|
774
|
+
error: "forbidden"
|
|
775
|
+
};
|
|
776
|
+
if (!sameOriginMetadata(req, host)) return {
|
|
777
|
+
trusted: false,
|
|
778
|
+
error: "forbidden"
|
|
779
|
+
};
|
|
780
|
+
if (localPeer && loopbackHost(host)) return { trusted: true };
|
|
781
|
+
try {
|
|
782
|
+
if (await trustedOrigins.has(origin)) return { trusted: true };
|
|
783
|
+
} catch {
|
|
784
|
+
return {
|
|
785
|
+
trusted: false,
|
|
786
|
+
error: "forbidden"
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
return {
|
|
790
|
+
trusted: false,
|
|
791
|
+
error: REMOTE_WEB_ORIGIN_NOT_TRUSTED
|
|
792
|
+
};
|
|
793
|
+
}
|
|
591
794
|
function json(res, status, value) {
|
|
592
795
|
res.writeHead(status, {
|
|
593
796
|
"content-type": "application/json; charset=utf-8",
|
|
@@ -597,16 +800,24 @@ function json(res, status, value) {
|
|
|
597
800
|
res.end(JSON.stringify(value));
|
|
598
801
|
}
|
|
599
802
|
/** Register the plugin-owned OAuth routes when the Web server is composed. */
|
|
600
|
-
function registerOpenAICodexAuthRoutes(ctx, store) {
|
|
803
|
+
function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride) {
|
|
601
804
|
const auth = new OpenAICodexWebAuth(store);
|
|
805
|
+
const storedFilename = store.filename;
|
|
806
|
+
const trustedOrigins = trustedOriginsOverride ?? (typeof storedFilename === "string" ? new OpenAICodexTrustedOriginsStore(join(dirname(storedFilename), ".openai-codex-trusted-origins.json")) : new OpenAICodexTrustedOriginsStore());
|
|
602
807
|
ctx.effect(() => {
|
|
808
|
+
const authorize = async (req, res) => {
|
|
809
|
+
const decision = await trustedRequestDecision(req, trustedOrigins);
|
|
810
|
+
if (decision.trusted) return true;
|
|
811
|
+
json(res, 403, { error: decision.error });
|
|
812
|
+
return false;
|
|
813
|
+
};
|
|
603
814
|
const routes = [
|
|
604
815
|
ctx.webServer.register({
|
|
605
816
|
kind: "exact",
|
|
606
817
|
path: OPENAI_CODEX_AUTH_STATUS_PATH,
|
|
607
818
|
handler: async (req, res) => {
|
|
608
819
|
if (req.method !== "GET") return json(res, 405, { error: "method not allowed" });
|
|
609
|
-
if (!
|
|
820
|
+
if (!await authorize(req, res)) return;
|
|
610
821
|
json(res, 200, await auth.status());
|
|
611
822
|
}
|
|
612
823
|
}),
|
|
@@ -615,7 +826,7 @@ function registerOpenAICodexAuthRoutes(ctx, store) {
|
|
|
615
826
|
path: OPENAI_CODEX_AUTH_LOGIN_PATH,
|
|
616
827
|
handler: async (req, res) => {
|
|
617
828
|
if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
|
|
618
|
-
if (!
|
|
829
|
+
if (!await authorize(req, res)) return;
|
|
619
830
|
try {
|
|
620
831
|
json(res, 200, await auth.signIn());
|
|
621
832
|
} catch (error) {
|
|
@@ -628,7 +839,7 @@ function registerOpenAICodexAuthRoutes(ctx, store) {
|
|
|
628
839
|
path: OPENAI_CODEX_AUTH_LOGOUT_PATH,
|
|
629
840
|
handler: async (req, res) => {
|
|
630
841
|
if (req.method !== "POST") return json(res, 405, { error: "method not allowed" });
|
|
631
|
-
if (!
|
|
842
|
+
if (!await authorize(req, res)) return;
|
|
632
843
|
try {
|
|
633
844
|
await auth.signOut();
|
|
634
845
|
json(res, 200, { ok: true });
|
|
@@ -645,8 +856,146 @@ function registerOpenAICodexAuthRoutes(ctx, store) {
|
|
|
645
856
|
}, "dsh-codex-connect: Web OAuth routes");
|
|
646
857
|
}
|
|
647
858
|
//#endregion
|
|
859
|
+
//#region src/compatibility.ts
|
|
860
|
+
const COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
861
|
+
const SUPPORTED_NODE_RANGE = "^22.19.0 || >=24.0.0";
|
|
862
|
+
const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.0-rc.6";
|
|
863
|
+
const SUPPORTED_PI_AI_VERSION = "0.82.1";
|
|
864
|
+
const PI_AI_PACKAGE = "@earendil-works/pi-ai";
|
|
865
|
+
const DSH_PLUGIN_API_PACKAGES = [
|
|
866
|
+
"@deepseek-ai/dsh-agent",
|
|
867
|
+
"@deepseek-ai/dsh-atomic-write",
|
|
868
|
+
"@deepseek-ai/dsh-attachment",
|
|
869
|
+
"@deepseek-ai/dsh-home-paths",
|
|
870
|
+
"@deepseek-ai/dsh-host-webserver",
|
|
871
|
+
"@deepseek-ai/dsh-invariants",
|
|
872
|
+
"@deepseek-ai/dsh-llm",
|
|
873
|
+
"@deepseek-ai/dsh-llm-pi-ai",
|
|
874
|
+
"@deepseek-ai/dsh-fs",
|
|
875
|
+
"@deepseek-ai/dsh-session",
|
|
876
|
+
"@deepseek-ai/dsh-settings",
|
|
877
|
+
"@deepseek-ai/dsh-tools",
|
|
878
|
+
"@deepseek-ai/dsh-web"
|
|
879
|
+
];
|
|
880
|
+
const COMPATIBILITY_PACKAGES = [
|
|
881
|
+
"@deepseek-ai/dsh-llm",
|
|
882
|
+
"@deepseek-ai/dsh-llm-pi-ai",
|
|
883
|
+
PI_AI_PACKAGE
|
|
884
|
+
];
|
|
885
|
+
/** Public contract data mirrored by compatibility.json without importing JSON at runtime. */
|
|
886
|
+
const COMPATIBILITY_CONTRACT = {
|
|
887
|
+
schemaVersion: 1,
|
|
888
|
+
engines: { node: SUPPORTED_NODE_RANGE },
|
|
889
|
+
dshPluginApi: {
|
|
890
|
+
version: SUPPORTED_DSH_PLUGIN_API_VERSION,
|
|
891
|
+
packages: DSH_PLUGIN_API_PACKAGES
|
|
892
|
+
},
|
|
893
|
+
piAi: {
|
|
894
|
+
package: PI_AI_PACKAGE,
|
|
895
|
+
version: SUPPORTED_PI_AI_VERSION
|
|
896
|
+
}
|
|
897
|
+
};
|
|
898
|
+
const PACKAGE_JSON_SEARCH_DEPTH = 8;
|
|
899
|
+
function compareVersion(left, right) {
|
|
900
|
+
return left === right ? "compatible" : "incompatible";
|
|
901
|
+
}
|
|
902
|
+
function parseNodeVersion(value) {
|
|
903
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9A-Za-z.-]+)?$/u.exec(value.trim());
|
|
904
|
+
if (match === null) return void 0;
|
|
905
|
+
const major = Number(match[1]);
|
|
906
|
+
const minor = Number(match[2]);
|
|
907
|
+
const patch = Number(match[3]);
|
|
908
|
+
if (![
|
|
909
|
+
major,
|
|
910
|
+
minor,
|
|
911
|
+
patch
|
|
912
|
+
].every(Number.isSafeInteger)) return void 0;
|
|
913
|
+
return [
|
|
914
|
+
major,
|
|
915
|
+
minor,
|
|
916
|
+
patch
|
|
917
|
+
];
|
|
918
|
+
}
|
|
919
|
+
function nodeStatus(value) {
|
|
920
|
+
if (value === void 0 || value === null || value.trim() === "") return "unknown";
|
|
921
|
+
const parsed = parseNodeVersion(value);
|
|
922
|
+
if (parsed === void 0) return "unknown";
|
|
923
|
+
const [major, minor, patch] = parsed;
|
|
924
|
+
if (major === 22) return minor > 19 || minor === 19 && patch >= 0 ? "compatible" : "incompatible";
|
|
925
|
+
return major >= 24 ? "compatible" : "incompatible";
|
|
926
|
+
}
|
|
927
|
+
function packageEntry(supported, installed) {
|
|
928
|
+
return {
|
|
929
|
+
supported,
|
|
930
|
+
installed: installed ?? null,
|
|
931
|
+
status: installed === void 0 || installed === null || installed === "" ? "unknown" : compareVersion(installed, supported)
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
function nodeEntry(installed) {
|
|
935
|
+
return {
|
|
936
|
+
supported: SUPPORTED_NODE_RANGE,
|
|
937
|
+
installed: installed ?? null,
|
|
938
|
+
status: nodeStatus(installed)
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function aggregateStatus(entries) {
|
|
942
|
+
if (entries.some((entry) => entry.status === "incompatible")) return "incompatible";
|
|
943
|
+
if (entries.some((entry) => entry.status === "unknown")) return "unknown";
|
|
944
|
+
return "compatible";
|
|
945
|
+
}
|
|
946
|
+
/** Evaluate a captured set of versions without touching the filesystem. */
|
|
947
|
+
function evaluateCompatibility(input = {}) {
|
|
948
|
+
const installedNode = input.nodeVersion ?? input.node ?? input.installed?.node;
|
|
949
|
+
const suppliedPackages = input.packageVersions ?? input.packages ?? input.installed?.packages ?? {};
|
|
950
|
+
const packages = {
|
|
951
|
+
"@deepseek-ai/dsh-llm": packageEntry(SUPPORTED_DSH_PLUGIN_API_VERSION, suppliedPackages["@deepseek-ai/dsh-llm"]),
|
|
952
|
+
"@deepseek-ai/dsh-llm-pi-ai": packageEntry(SUPPORTED_DSH_PLUGIN_API_VERSION, suppliedPackages["@deepseek-ai/dsh-llm-pi-ai"]),
|
|
953
|
+
[PI_AI_PACKAGE]: packageEntry(SUPPORTED_PI_AI_VERSION, suppliedPackages[PI_AI_PACKAGE])
|
|
954
|
+
};
|
|
955
|
+
const node = nodeEntry(installedNode);
|
|
956
|
+
return {
|
|
957
|
+
schemaVersion: 1,
|
|
958
|
+
status: aggregateStatus([node, ...Object.values(packages)]),
|
|
959
|
+
node,
|
|
960
|
+
packages
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
/** Alias for callers that prefer assessment terminology. */
|
|
964
|
+
const assessCompatibility = evaluateCompatibility;
|
|
965
|
+
async function readPackageVersionFromEntry(name) {
|
|
966
|
+
let entry;
|
|
967
|
+
try {
|
|
968
|
+
const resolved = import.meta.resolve(name);
|
|
969
|
+
if (!resolved.startsWith("file:")) return void 0;
|
|
970
|
+
entry = fileURLToPath(resolved);
|
|
971
|
+
} catch {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
let directory = dirname(entry);
|
|
975
|
+
for (let depth = 0; depth < PACKAGE_JSON_SEARCH_DEPTH; depth += 1) {
|
|
976
|
+
const candidate = join(directory, "package.json");
|
|
977
|
+
try {
|
|
978
|
+
const parsed = JSON.parse(await readFile(candidate, "utf8"));
|
|
979
|
+
if (parsed.name === name && typeof parsed.version === "string") return parsed.version;
|
|
980
|
+
} catch {}
|
|
981
|
+
const parent = parse(directory).root === directory ? directory : dirname(directory);
|
|
982
|
+
if (parent === directory) break;
|
|
983
|
+
directory = parent;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
/** Read installed package metadata and return only versions and statuses. */
|
|
987
|
+
async function detectCompatibility(options = {}) {
|
|
988
|
+
const readVersion = options.readPackageVersion ?? readPackageVersionFromEntry;
|
|
989
|
+
const packageVersions = options.packageVersions ?? options.packages ?? options.installed?.packages;
|
|
990
|
+
const resolvedPackages = packageVersions === void 0 ? Object.fromEntries(await Promise.all(COMPATIBILITY_PACKAGES.map(async (name) => [name, await readVersion(name)]))) : packageVersions;
|
|
991
|
+
return evaluateCompatibility({
|
|
992
|
+
nodeVersion: options.nodeVersion ?? options.node ?? options.installed?.node ?? process.version,
|
|
993
|
+
packageVersions: resolvedPackages
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
//#endregion
|
|
648
997
|
//#region src/version.ts
|
|
649
|
-
const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.
|
|
998
|
+
const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.8";
|
|
650
999
|
//#endregion
|
|
651
1000
|
//#region src/doctor.ts
|
|
652
1001
|
/** Secret-free diagnostics and duplicate-provider guidance. */
|
|
@@ -678,6 +1027,7 @@ async function diagnoseOpenAICodex(options = {}) {
|
|
|
678
1027
|
state = error?.code === "ENOENT" ? "missing" : "unreadable-metadata";
|
|
679
1028
|
}
|
|
680
1029
|
const providerConflict = options.providerIds?.includes("openai-codex") ?? false;
|
|
1030
|
+
const compatibility = await detectCompatibility(options.compatibilityOptions);
|
|
681
1031
|
const hints = [];
|
|
682
1032
|
if (state === "missing") hints.push("Sign in only when you are ready; installation does not start OAuth.");
|
|
683
1033
|
if (state === "permissions-too-broad") hints.push(`Restrict the OAuth file to its owner before use (current mode ${mode}).`);
|
|
@@ -685,6 +1035,8 @@ async function diagnoseOpenAICodex(options = {}) {
|
|
|
685
1035
|
if (state === "unreadable-metadata") hints.push("Harness could not inspect the OAuth file metadata; check the parent directory and file ownership.");
|
|
686
1036
|
if (providerConflict) hints.push(openAICodexConflictMessage());
|
|
687
1037
|
if (!providerConflict) hints.push("If Harness reports a duplicate openai-codex adapter, remove the legacy bundle or manual provider row.");
|
|
1038
|
+
if (compatibility.status === "incompatible") hints.push("Compatibility mismatch: install the declared DSH plugin API versions and pin @earendil-works/pi-ai to 0.82.1, then run doctor again; no files are changed automatically.");
|
|
1039
|
+
else if (compatibility.status === "unknown") hints.push("Compatibility is unknown: verify the declared DSH plugin API and @earendil-works/pi-ai versions, then run doctor again.");
|
|
688
1040
|
return {
|
|
689
1041
|
package: "dsh-codex-connect",
|
|
690
1042
|
version: CODEX_CONNECT_VERSION,
|
|
@@ -702,6 +1054,7 @@ async function diagnoseOpenAICodex(options = {}) {
|
|
|
702
1054
|
changesHarnessSearchRoute: false
|
|
703
1055
|
},
|
|
704
1056
|
providerConflict,
|
|
1057
|
+
compatibility,
|
|
705
1058
|
hints
|
|
706
1059
|
};
|
|
707
1060
|
}
|
|
@@ -1509,4 +1862,4 @@ function apply(ctx, config) {
|
|
|
1509
1862
|
scheduleCapabilities();
|
|
1510
1863
|
}
|
|
1511
1864
|
//#endregion
|
|
1512
|
-
export {
|
|
1865
|
+
export { DSH_PLUGIN_API_PACKAGES as A, OPENAI_CODEX_USAGE_URL as B, assertNoOpenAICodexProviderConflict as C, COMPATIBILITY_CONTRACT as D, CODEX_CONNECT_VERSION as E, assessCompatibility as F, openAICodexAuthStatus as G, readOpenAICodexRateLimits as H, detectCompatibility as I, OpenAICodexCredentialStore as J, OPENAI_CODEX_AUTH_FILENAME as K, evaluateCompatibility as L, SUPPORTED_DSH_PLUGIN_API_VERSION as M, SUPPORTED_NODE_RANGE as N, COMPATIBILITY_PACKAGES as O, SUPPORTED_PI_AI_VERSION as P, OpenAICodexTrustedOriginsStore as R, VIEW_IMAGE_TOOL_NAME as S, openAICodexConflictMessage as T, loginOpenAICodex as U, parseOpenAICodexUsage as V, logoutOpenAICodex as W, openAICodexAuthPath as Y, decodeOpenAICodexSettings as _, name as a, installOpenAICodexSearchEvent as b, OPENAI_CODEX_SEARCH_URL as c, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as d, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS as f, OPENAI_CODEX_SETTINGS_NAMESPACE as g, DEFAULT_OPENAI_CODEX_SETTINGS as h, inject as i, PI_AI_PACKAGE as j, COMPATIBILITY_SCHEMA_VERSION as k, OpenAICodexSearchProvider as l, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as m, OPENAI_CODEX_SETTINGS_NS as n, OPENAI_CODEX_BASE_URL as o, DEFAULT_OPENAI_CODEX_SEARCH_MODE as p, OPENAI_CODEX_PROVIDER as q, apply as r, OPENAI_CODEX_SEARCH_PROVIDER as s, Config as t, mapOpenAICodexSearchResponse as u, resolveOpenAICodexSettings as v, diagnoseOpenAICodex as w, recordOpenAICodexSearchRequest as x, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT as y, normalizeTrustedOrigin as z };
|
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.
|
|
5
|
+
"version": "0.1.0-alpha.4.8",
|
|
6
6
|
"author": "Frank Song",
|
|
7
7
|
"contributors": [
|
|
8
8
|
"Yan-Zero (original dsh-codex author)"
|
|
@@ -42,9 +42,11 @@
|
|
|
42
42
|
"build": "tsdown",
|
|
43
43
|
"test": "vitest run",
|
|
44
44
|
"typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
|
|
45
|
+
"check:compatibility": "node scripts/check-compatibility.mjs",
|
|
45
46
|
"pack:check": "node scripts/check-pack.mjs",
|
|
46
47
|
"check:dsh-install": "node scripts/check-dsh-install.mjs",
|
|
47
|
-
"check": "
|
|
48
|
+
"check:release-workflow": "node scripts/check-release-workflow.mjs",
|
|
49
|
+
"check": "pnpm run check:release-workflow && pnpm run lint && pnpm run typecheck && pnpm run test && pnpm run build && pnpm --silent run check:compatibility && pnpm run pack:check",
|
|
48
50
|
"prepublishOnly": "pnpm --config.minimum-release-age=0 run check"
|
|
49
51
|
},
|
|
50
52
|
"exports": {
|
|
@@ -58,10 +60,12 @@
|
|
|
58
60
|
},
|
|
59
61
|
"./client": "./lib/client.js",
|
|
60
62
|
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
63
|
+
"./compatibility.json": "./compatibility.json",
|
|
61
64
|
"./package.json": "./package.json"
|
|
62
65
|
},
|
|
63
66
|
"files": [
|
|
64
67
|
"lib",
|
|
68
|
+
"compatibility.json",
|
|
65
69
|
"cordis.patch.yml",
|
|
66
70
|
"docs",
|
|
67
71
|
"INSTALL.md",
|
|
@@ -86,21 +90,21 @@
|
|
|
86
90
|
}
|
|
87
91
|
},
|
|
88
92
|
"peerDependencies": {
|
|
89
|
-
"@earendil-works/pi-ai": "
|
|
93
|
+
"@earendil-works/pi-ai": "0.82.1",
|
|
90
94
|
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
91
|
-
"@deepseek-ai/dsh-agent": "
|
|
92
|
-
"@deepseek-ai/dsh-atomic-write": "
|
|
93
|
-
"@deepseek-ai/dsh-attachment": "
|
|
94
|
-
"@deepseek-ai/dsh-home-paths": "
|
|
95
|
-
"@deepseek-ai/dsh-host-webserver": "
|
|
96
|
-
"@deepseek-ai/dsh-invariants": "
|
|
97
|
-
"@deepseek-ai/dsh-llm": "
|
|
98
|
-
"@deepseek-ai/dsh-llm-pi-ai": "
|
|
99
|
-
"@deepseek-ai/dsh-fs": "
|
|
100
|
-
"@deepseek-ai/dsh-session": "
|
|
101
|
-
"@deepseek-ai/dsh-settings": "
|
|
102
|
-
"@deepseek-ai/dsh-tools": "
|
|
103
|
-
"@deepseek-ai/dsh-web": "
|
|
95
|
+
"@deepseek-ai/dsh-agent": "0.1.0-rc.6",
|
|
96
|
+
"@deepseek-ai/dsh-atomic-write": "0.1.0-rc.6",
|
|
97
|
+
"@deepseek-ai/dsh-attachment": "0.1.0-rc.6",
|
|
98
|
+
"@deepseek-ai/dsh-home-paths": "0.1.0-rc.6",
|
|
99
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.0-rc.6",
|
|
100
|
+
"@deepseek-ai/dsh-invariants": "0.1.0-rc.6",
|
|
101
|
+
"@deepseek-ai/dsh-llm": "0.1.0-rc.6",
|
|
102
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.1.0-rc.6",
|
|
103
|
+
"@deepseek-ai/dsh-fs": "0.1.0-rc.6",
|
|
104
|
+
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
|
|
105
|
+
"@deepseek-ai/dsh-settings": "0.1.0-rc.6",
|
|
106
|
+
"@deepseek-ai/dsh-tools": "0.1.0-rc.6",
|
|
107
|
+
"@deepseek-ai/dsh-web": "0.1.0-rc.6",
|
|
104
108
|
"@deepseek-ai/schemastery": "^3.18.1-rc.1",
|
|
105
109
|
"react": "^18.2.0"
|
|
106
110
|
},
|