dsh-codex-connect 0.1.0-alpha.4.4 → 0.1.0-alpha.4.6

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
@@ -19,7 +19,7 @@ Install `dsh-codex-connect` into one requested DeepSeek Harness profile without
19
19
  dsh plugin --profile web add dsh-codex-connect@alpha
20
20
  ```
21
21
 
22
- To pin this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.4`. If npm is unavailable, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.4'`.
22
+ After `0.1.0-alpha.4.6` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.6`. 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.6'`.
23
23
 
24
24
  3. Run `dsh --profile web --dump-config` and require exactly one `llm-openai-codex` row loading `dsh-codex-connect`.
25
25
  4. Confirm the effective `agent-default-model` and `web.searchProvider` values are unchanged from before installation.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # git hash-object README.md docs/README.zh.md
5
- README.md: 16d921296b08e7b01cb9cf3cf5ade516544acba5
6
- docs/README.zh.md: 3de5c7920bf27ec7e72cc8d67941578a31b5a00b
5
+ README.md: 6eedfff99162f49458c9fc6f5fc4dff502aa7dfc
6
+ docs/README.zh.md: 3fa64da86d3c1f1305f67a5e29e4255ca309eccb
package/README.md CHANGED
@@ -6,10 +6,34 @@ English | [中文](docs/README.zh.md)
6
6
 
7
7
  Connect your ChatGPT subscription to DeepSeek Harness with OAuth, user-controlled defaults, Harness-native approvals, diagnostics, and reliable session recovery.
8
8
 
9
+ <p align="center">
10
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/hero.jpg" alt="Codex Connect — ChatGPT OAuth for DeepSeek Harness" width="100%">
11
+ </p>
12
+
9
13
  `dsh-codex-connect` adds the `openai-codex` model catalog and a separate ChatGPT OAuth login. Models run through Harness's normal LLM service, so streaming, tool calls, reasoning replay, compaction, filesystem controls, permission gates, and approval prompts remain Harness-owned. It does not turn a ChatGPT subscription into an OpenAI Platform API credential.
10
14
 
11
15
  Installation is additive. The bundle does not replace the current default model or search route, and its standalone search provider and `view_image` tool are disabled until explicitly enabled.
12
16
 
17
+ ## See it in Harness
18
+
19
+ Sign in and manage the plugin from **Settings → Plugins → Plugin configuration → Codex Connect**.
20
+
21
+ <p align="center">
22
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/oauth-status.jpg" alt="Codex Connect ChatGPT OAuth status inside Harness plugin configuration" width="720">
23
+ </p>
24
+
25
+ Optional Codex search and `view_image` capabilities remain explicit, profile-scoped choices:
26
+
27
+ <p align="center">
28
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/plugin-configuration.jpg" alt="Codex Connect optional capability settings in DeepSeek Harness" width="720">
29
+ </p>
30
+
31
+ Codex models then appear in Harness's normal model picker alongside the existing providers:
32
+
33
+ <p align="center">
34
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/model-selector.jpg" alt="OpenAI Codex models in the DeepSeek Harness model picker" width="320">
35
+ </p>
36
+
13
37
  ## Install
14
38
 
15
39
  ```sh
@@ -17,7 +41,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
17
41
  dsh web
18
42
  ```
19
43
 
20
- To pin this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.4`. If npm is unavailable, use the GitHub tag fallback: `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.4'`. From a DeepSeek Harness source checkout, prefix commands with `pnpm`. For a local checkout, install `link:/absolute/path/to/dsh-codex-connect`.
44
+ After `0.1.0-alpha.4.6` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.6`. 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.6'`. From a DeepSeek Harness source checkout, prefix commands with `pnpm`. For a local checkout, install `link:/absolute/path/to/dsh-codex-connect`.
21
45
 
22
46
  Sign in from **Settings → Plugins → Plugin configuration → Codex Connect → Sign in with ChatGPT**, or use the CLI:
23
47
 
@@ -29,6 +53,8 @@ dsh plugin --profile web exec dsh-codex-connect doctor
29
53
 
30
54
  The doctor command reads process and filesystem metadata only. It never opens the OAuth document or prints a token, authorization URL, authorization code, account id, or auth-file content.
31
55
 
56
+ For automation, `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/expiry data. `status --json` emits only signed-in or signed-out state with package metadata. `doctor --json` reads metadata only; `status --json` reads the credential solely to determine sign-in state, but neither mode prints credential contents or starts OAuth. Signed-out status still exits with code 1.
57
+
32
58
  ## Explicit configuration
33
59
 
34
60
  Open **Settings → Plugins → Plugin configuration → Codex Connect** to manage the ChatGPT account and optional capabilities in one card. Changes use Harness's revision-fenced settings store and apply live. **Save changes** affects only this plugin's capability section; it never selects a default model or global search route.
package/docs/README.zh.md CHANGED
@@ -6,10 +6,34 @@
6
6
 
7
7
  通过 OAuth 将你的 ChatGPT 订阅连接到 DeepSeek Harness,同时保留用户自主默认项、Harness 原生审批、非敏感诊断和可靠的会话恢复。
8
8
 
9
+ <p align="center">
10
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/hero.jpg" alt="Codex Connect — 通过 ChatGPT OAuth 连接 DeepSeek Harness" width="100%">
11
+ </p>
12
+
9
13
  `dsh-codex-connect` 提供 `openai-codex` 模型目录和独立的 ChatGPT OAuth 登录。模型仍走 Harness 标准 LLM 服务,因此流式输出、工具调用、reasoning replay、压缩、文件系统控制、权限门禁和审批提示仍由 Harness 负责。ChatGPT 订阅不会因此变成 OpenAI Platform API 凭据。
10
14
 
11
15
  安装是增量的:bundle 不会替换当前主模型或搜索路由;独立搜索提供方和 `view_image` 工具也默认关闭,必须显式开启。
12
16
 
17
+ ## 在 Harness 中的样子
18
+
19
+ 在 **设置 → 插件 → 插件配置 → Codex Connect** 中登录并管理插件。
20
+
21
+ <p align="center">
22
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/oauth-status.jpg" alt="Harness 插件配置中的 Codex Connect ChatGPT OAuth 状态" width="720">
23
+ </p>
24
+
25
+ Codex 搜索与 `view_image` 都是显式、按 profile 控制的可选能力:
26
+
27
+ <p align="center">
28
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/plugin-configuration.jpg" alt="DeepSeek Harness 中的 Codex Connect 可选能力设置" width="720">
29
+ </p>
30
+
31
+ Codex 模型会和现有提供方一起出现在 Harness 原生模型选择器中:
32
+
33
+ <p align="center">
34
+ <img src="https://raw.githubusercontent.com/franksong2702/dsh-codex-connect/main/docs/assets/model-selector.jpg" alt="DeepSeek Harness 模型选择器中的 OpenAI Codex 模型" width="320">
35
+ </p>
36
+
13
37
  ## 安装
14
38
 
15
39
  ```sh
@@ -17,7 +41,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
17
41
  dsh web
18
42
  ```
19
43
 
20
- 如需精确固定此版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.4`。若 npm 不可用,可使用 GitHub tag 兜底:`dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.4'`。在 DeepSeek Harness 源码 checkout 中运行时,在命令前加 `pnpm`。本地开发可安装 `link:/absolute/path/to/dsh-codex-connect`。
44
+ 在 `0.1.0-alpha.4.6` 发布后,如需精确固定此版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.6`。在对应 GitHub prerelease 已创建且 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.6'`。在 DeepSeek Harness 源码 checkout 中运行时,在命令前加 `pnpm`。本地开发可安装 `link:/absolute/path/to/dsh-codex-connect`。
21
45
 
22
46
  可在 **设置 → 插件 → 插件配置 → Codex Connect → 使用 ChatGPT 登录**,也可使用 CLI:
23
47
 
@@ -29,6 +53,8 @@ dsh plugin --profile web exec dsh-codex-connect doctor
29
53
 
30
54
  `doctor` 只读取进程与文件系统元数据,不打开 OAuth 文件,也不会输出 token、授权 URL、授权码、账户 ID 或认证文件内容。
31
55
 
56
+ 供脚本使用时,`doctor --json` 只输出一条可解析的非敏感 JSON,包含 schema version 1、包/版本/Node 信息、认证文件状态与安全 mode、能力、冲突状态和提示;它省略认证文件绝对路径以及 OAuth、账户和过期时间信息。`status --json` 只输出 signed-in 或 signed-out 状态及包元数据。`doctor --json` 只读取元数据;`status --json` 仅为判断登录态读取认证文件,但两者都不会输出认证文件内容或启动 OAuth;signed-out 状态仍返回退出码 1。
57
+
32
58
  ## 显式配置
33
59
 
34
60
  打开 **设置 → 插件 → 插件配置 → Codex Connect**,可以在同一张卡片中管理 ChatGPT 账户和可选能力。更改通过 Harness 带 revision 防护的设置存储保存,并即时生效;**保存更改**只影响本插件的能力配置,绝不会选择默认模型或全局搜索路由。
Binary file
Binary file
Binary file
package/lib/bin.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { A as logoutOpenAICodex, F as openAICodexAuthPath, j as openAICodexAuthStatus, k as loginOpenAICodex, w as diagnoseOpenAICodex } from "./src-BY1nv_E6.js";
2
+ import { A as loginOpenAICodex, E as CODEX_CONNECT_VERSION, I as openAICodexAuthPath, M as openAICodexAuthStatus, j as logoutOpenAICodex, w as diagnoseOpenAICodex } from "./src-KKqkbCFT.js";
3
3
  import { spawn } from "node:child_process";
4
4
  import { realpathSync } from "node:fs";
5
5
  import { createInterface } from "node:readline/promises";
6
6
  import { fileURLToPath } from "node:url";
7
7
  //#region src/bin.ts
8
8
  /** Standalone credential CLI for the optional OpenAI Codex bundle. */
9
+ const JSON_SCHEMA_VERSION = 1;
9
10
  /** Open one trusted HTTPS URL with the platform browser, best effort. */
10
11
  function openBrowser(rawUrl) {
11
12
  const url = new URL(rawUrl);
@@ -63,16 +64,39 @@ async function answerPrompt(prompt, deviceCode, question) {
63
64
  /** Print the standalone command help. */
64
65
  function printHelp() {
65
66
  process.stdout.write([
66
- "Usage: dsh-codex-connect <doctor|login|logout|status> [--device-code]",
67
+ "Usage: dsh-codex-connect <doctor|login|logout|status> [--device-code|--json]",
67
68
  "",
68
69
  " doctor inspect secret-free runtime and OAuth file metadata",
69
70
  " login sign in with a separate ChatGPT OAuth session",
70
71
  " logout remove the dsh credential without changing ~/.codex",
71
72
  " status report non-secret dsh credential state",
72
73
  " --device-code use headless device-code login (login only)",
74
+ " --json emit one secret-free JSON document (doctor/status only)",
73
75
  ""
74
76
  ].join("\n"));
75
77
  }
78
+ 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;
80
+ }
81
+ /** Project the diagnostic report without its absolute credential pathname. */
82
+ function doctorJson(report) {
83
+ return {
84
+ schemaVersion: JSON_SCHEMA_VERSION,
85
+ package: report.package,
86
+ version: report.version,
87
+ node: report.node,
88
+ credentialFile: {
89
+ state: report.credentialFile.state,
90
+ ...report.credentialFile.mode === void 0 ? {} : { mode: report.credentialFile.mode }
91
+ },
92
+ capabilities: report.capabilities,
93
+ providerConflict: report.providerConflict,
94
+ hints: report.hints
95
+ };
96
+ }
97
+ function printJson(value) {
98
+ process.stdout.write(`${JSON.stringify(value)}\n`);
99
+ }
76
100
  /** Execute one boot-free credential command. */
77
101
  async function run(argv) {
78
102
  if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
@@ -85,7 +109,9 @@ async function run(argv) {
85
109
  return 1;
86
110
  }
87
111
  const action = rawAction;
88
- if (flags.filter((flag) => flag !== "--device-code").length > 0 || flags.includes("--device-code") && action !== "login") {
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)) {
89
115
  process.stderr.write(`dsh-codex-connect: invalid options for ${action}: ${flags.join(" ")}\n`);
90
116
  return 1;
91
117
  }
@@ -93,6 +119,10 @@ async function run(argv) {
93
119
  switch (action) {
94
120
  case "doctor": {
95
121
  const report = await diagnoseOpenAICodex();
122
+ if (jsonOutput) {
123
+ printJson(doctorJson(report));
124
+ return doctorExitCode(report);
125
+ }
96
126
  process.stdout.write([
97
127
  `Codex Connect ${report.version} on ${report.node}`,
98
128
  `OAuth file metadata: ${report.credentialFile.state} (${report.credentialFile.path})`,
@@ -101,10 +131,19 @@ async function run(argv) {
101
131
  ...report.hints.map((hint) => `Hint: ${hint}`),
102
132
  ""
103
133
  ].join("\n"));
104
- return report.credentialFile.state === "permissions-too-broad" || report.credentialFile.state === "not-a-regular-file" || report.credentialFile.state === "unreadable-metadata" ? 1 : 0;
134
+ return doctorExitCode(report);
105
135
  }
106
136
  case "status": {
107
137
  const status = await openAICodexAuthStatus();
138
+ if (jsonOutput) {
139
+ printJson({
140
+ schemaVersion: JSON_SCHEMA_VERSION,
141
+ package: "dsh-codex-connect",
142
+ version: CODEX_CONNECT_VERSION,
143
+ status: status.authenticated ? "signed-in" : "signed-out"
144
+ });
145
+ return status.authenticated ? 0 : 1;
146
+ }
108
147
  if (!status.authenticated) {
109
148
  process.stdout.write("Codex Connect: signed out\n");
110
149
  return 1;
@@ -125,7 +164,7 @@ async function run(argv) {
125
164
  });
126
165
  try {
127
166
  await loginOpenAICodex({
128
- prompt: (prompt) => answerPrompt(prompt, flags.includes("--device-code"), (text, options) => readline.question(text, options)),
167
+ prompt: (prompt) => answerPrompt(prompt, deviceCode, (text, options) => readline.question(text, options)),
129
168
  notify: (event) => notify(event, true)
130
169
  });
131
170
  } finally {
package/lib/client.js CHANGED
@@ -666,7 +666,7 @@ window.__ModuleLoader__.load({
666
666
  height: 9,
667
667
  borderRadius: "50%",
668
668
  flex: "0 0 auto",
669
- background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" ? "var(--dsw-alias-state-error-primary, #d92d20)" : status === "signing-in" || status === "loading" ? "var(--dsw-alias-brand-primary, #1677ff)" : "var(--dsw-alias-label-dimmed, #9aa0a6)"
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)"
670
670
  };
671
671
  }
672
672
  async function jsonRequest(path, method = "GET", signal) {
@@ -768,7 +768,7 @@ window.__ModuleLoader__.load({
768
768
  if (mounted.current) setBusy(false);
769
769
  }
770
770
  };
771
- const label = status.status === "signed-in" ? t("signedIn") : status.status === "loading" ? t("loadingAccount") : status.status === "signing-in" ? t("signingIn") : status.status === "error" ? t("requestFailed") : t("signedOut");
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");
772
772
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
773
773
  style: embedded ? embeddedPageStyle : pageStyle,
774
774
  ...embedded ? { "aria-label": t("title") } : { "aria-labelledby": "openai-codex-settings-title" },
@@ -813,10 +813,10 @@ window.__ModuleLoader__.load({
813
813
  onClick: () => {
814
814
  signIn();
815
815
  },
816
- children: busy ? t("working") : status.status === "error" ? t("loginAgain") : t("login")
816
+ children: busy ? t("working") : status.status === "error" || status.status === "reauth-required" ? t("loginAgain") : t("login")
817
817
  })]
818
818
  }),
819
- status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
819
+ status.status === "error" || status.status === "reauth-required" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
820
820
  style: errorStyle,
821
821
  children: status.message
822
822
  }) : null,
@@ -938,6 +938,7 @@ window.__ModuleLoader__.load({
938
938
  signedOut: "Not signed in",
939
939
  signingIn: "Waiting for browser authorization…",
940
940
  signedIn: "Signed in",
941
+ reauthRequired: "Sign in again",
941
942
  login: "Sign in with ChatGPT",
942
943
  loginAgain: "Sign in again",
943
944
  logout: "Sign out",
@@ -996,6 +997,7 @@ window.__ModuleLoader__.load({
996
997
  signedOut: "尚未登录",
997
998
  signingIn: "正在等待浏览器授权…",
998
999
  signedIn: "已登录",
1000
+ reauthRequired: "需要重新登录",
999
1001
  login: "使用 ChatGPT 登录",
1000
1002
  loginAgain: "重新登录",
1001
1003
  logout: "退出登录",
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as logoutOpenAICodex, C as assertNoOpenAICodexProviderConflict, D as parseOpenAICodexUsage, E as OPENAI_CODEX_USAGE_URL, F as openAICodexAuthPath, M as OPENAI_CODEX_AUTH_FILENAME, N as OPENAI_CODEX_PROVIDER, O as readOpenAICodexRateLimits, P as OpenAICodexCredentialStore, S as VIEW_IMAGE_TOOL_NAME, T as openAICodexConflictMessage, _ 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 openAICodexAuthStatus, k as loginOpenAICodex, 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, 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-BY1nv_E6.js";
1
+ import { A as loginOpenAICodex, C as assertNoOpenAICodexProviderConflict, D as OPENAI_CODEX_USAGE_URL, F as OpenAICodexCredentialStore, I as openAICodexAuthPath, M as openAICodexAuthStatus, N as OPENAI_CODEX_AUTH_FILENAME, O as parseOpenAICodexUsage, P as OPENAI_CODEX_PROVIDER, S as VIEW_IMAGE_TOOL_NAME, T as openAICodexConflictMessage, _ 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 logoutOpenAICodex, k as readOpenAICodexRateLimits, 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, 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-KKqkbCFT.js";
2
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 };
@@ -252,6 +252,27 @@ async function openAICodexAuthStatus(store = new OpenAICodexCredentialStore()) {
252
252
  /** Fixed endpoint used by the official Codex client for ChatGPT rate limits. */
253
253
  const OPENAI_CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
254
254
  const USAGE_REQUEST_TIMEOUT_MS = 15e3;
255
+ /** Stable public discriminant for an expired or revoked Codex OAuth session. */
256
+ const OPENAI_CODEX_REAUTH_REQUIRED_CODE = "OPENAI_CODEX_REAUTH_REQUIRED";
257
+ /** Fixed, secret-free message for a browser-facing reauthorization prompt. */
258
+ const OPENAI_CODEX_REAUTH_REQUIRED_MESSAGE = "OpenAI Codex authorization must be renewed";
259
+ /**
260
+ * Raised when the usage endpoint rejects the current OAuth session.
261
+ *
262
+ * The error intentionally carries no response, credential, or account data so
263
+ * callers can safely pass its fixed message across the Web boundary.
264
+ */
265
+ var OpenAICodexReauthRequiredError = class extends Error {
266
+ code = OPENAI_CODEX_REAUTH_REQUIRED_CODE;
267
+ constructor() {
268
+ super(OPENAI_CODEX_REAUTH_REQUIRED_MESSAGE);
269
+ this.name = "OpenAICodexReauthRequiredError";
270
+ }
271
+ };
272
+ /** Identify the dedicated reauthorization failure without comparing messages. */
273
+ function isOpenAICodexReauthRequiredError(error) {
274
+ return error instanceof OpenAICodexReauthRequiredError;
275
+ }
255
276
  function isRecord$2(value) {
256
277
  return typeof value === "object" && value !== null && !Array.isArray(value);
257
278
  }
@@ -363,7 +384,10 @@ async function readOpenAICodexRateLimits(store) {
363
384
  },
364
385
  signal: AbortSignal.timeout(USAGE_REQUEST_TIMEOUT_MS)
365
386
  });
366
- if (!response.ok) throw new Error(response.status === 401 || response.status === 403 ? "OpenAI Codex sign-in needs to be renewed" : `OpenAI Codex usage request failed with HTTP ${response.status}`);
387
+ if (!response.ok) {
388
+ if (response.status === 401 || response.status === 403) throw new OpenAICodexReauthRequiredError();
389
+ throw new Error(`OpenAI Codex usage request failed with HTTP ${response.status}`);
390
+ }
367
391
  let value;
368
392
  try {
369
393
  value = await response.json();
@@ -507,6 +531,10 @@ var OpenAICodexWebAuth = class {
507
531
  usage: await readOpenAICodexRateLimits(this.store)
508
532
  };
509
533
  } catch (error) {
534
+ if (isOpenAICodexReauthRequiredError(error)) return {
535
+ status: "reauth-required",
536
+ message: OPENAI_CODEX_REAUTH_REQUIRED_MESSAGE
537
+ };
510
538
  return {
511
539
  status: "signed-in",
512
540
  usage: { rateLimits: [] },
@@ -618,7 +646,7 @@ function registerOpenAICodexAuthRoutes(ctx, store) {
618
646
  }
619
647
  //#endregion
620
648
  //#region src/version.ts
621
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.4";
649
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.6";
622
650
  //#endregion
623
651
  //#region src/doctor.ts
624
652
  /** Secret-free diagnostics and duplicate-provider guidance. */
@@ -1481,4 +1509,4 @@ function apply(ctx, config) {
1481
1509
  scheduleCapabilities();
1482
1510
  }
1483
1511
  //#endregion
1484
- export { logoutOpenAICodex as A, assertNoOpenAICodexProviderConflict as C, parseOpenAICodexUsage as D, OPENAI_CODEX_USAGE_URL as E, openAICodexAuthPath as F, OPENAI_CODEX_AUTH_FILENAME as M, OPENAI_CODEX_PROVIDER as N, readOpenAICodexRateLimits as O, OpenAICodexCredentialStore as P, VIEW_IMAGE_TOOL_NAME as S, openAICodexConflictMessage as T, 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, openAICodexAuthStatus as j, loginOpenAICodex 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, 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 };
1512
+ export { loginOpenAICodex as A, assertNoOpenAICodexProviderConflict as C, OPENAI_CODEX_USAGE_URL as D, CODEX_CONNECT_VERSION as E, OpenAICodexCredentialStore as F, openAICodexAuthPath as I, openAICodexAuthStatus as M, OPENAI_CODEX_AUTH_FILENAME as N, parseOpenAICodexUsage as O, OPENAI_CODEX_PROVIDER as P, VIEW_IMAGE_TOOL_NAME as S, openAICodexConflictMessage as T, 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, logoutOpenAICodex as j, readOpenAICodexRateLimits 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, 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 };
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.4",
5
+ "version": "0.1.0-alpha.4.6",
6
6
  "author": "Frank Song",
7
7
  "contributors": [
8
8
  "Yan-Zero (original dsh-codex author)"
@@ -43,6 +43,7 @@
43
43
  "test": "vitest run",
44
44
  "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
45
45
  "pack:check": "node scripts/check-pack.mjs",
46
+ "check:dsh-install": "node scripts/check-dsh-install.mjs",
46
47
  "check": "pnpm run lint && pnpm run typecheck && pnpm run test && pnpm run build && pnpm run pack:check",
47
48
  "prepublishOnly": "pnpm --config.minimum-release-age=0 run check"
48
49
  },