dsh-codex-subscription 1.1.5 → 1.2.0

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/AGENTS.md CHANGED
@@ -6,7 +6,7 @@ Use this guide when a user asks an Agent to install, update, verify, or remove
6
6
  ## Safety
7
7
 
8
8
  - Confirm the target DSH installation and profile. Use `web` only when it is the user's target.
9
- - Use the exact `1.1.5` package below; do not install a moving branch.
9
+ - Use the exact `1.2.0` package below; do not install a moving branch.
10
10
  - Never print OAuth credentials, account IDs, authorization callbacks, or credential-store contents.
11
11
  - Preserve the DSH profile, unrelated plugins, sessions, and saved sign-in.
12
12
  - Do not start, stop, or restart DSH without explicit permission.
@@ -38,19 +38,19 @@ For an official DSH installation run through npm, keep the complete `npx` prefix
38
38
  run command does not create a global `dsh` command:
39
39
 
40
40
  ```sh
41
- npx -y @deepseek-ai/dsh@0.1.1-rc.1 plugin --profile web add dsh-codex-subscription@1.1.5
41
+ npx -y @deepseek-ai/dsh@0.1.1-rc.1 plugin --profile web add dsh-codex-subscription@1.2.0
42
42
  ```
43
43
 
44
44
  With an existing global `dsh` command:
45
45
 
46
46
  ```sh
47
- dsh plugin --profile web add dsh-codex-subscription@1.1.5
47
+ dsh plugin --profile web add dsh-codex-subscription@1.2.0
48
48
  ```
49
49
 
50
50
  From a DSH-Portable folder:
51
51
 
52
52
  ```powershell
53
- .\dsh.exe plugin --profile web add dsh-codex-subscription@1.1.5
53
+ .\dsh.exe plugin --profile web add dsh-codex-subscription@1.2.0
54
54
  ```
55
55
 
56
56
  Use the same `add` command to update or repair. This is the complete package-changing operation.
@@ -70,7 +70,7 @@ For DSH-Portable, replace `dsh` with `.\dsh.exe`. Static acceptance requires:
70
70
 
71
71
  For the official npm route, run the same checks with `npx -y @deepseek-ai/dsh@0.1.1-rc.1` in place of `dsh`.
72
72
 
73
- 1. `dsh-codex-subscription` version `1.1.5` appears exactly once.
73
+ 1. `dsh-codex-subscription` version `1.2.0` appears exactly once.
74
74
  2. `codex-subscription` appears exactly once in the composed config.
75
75
  3. No unrelated plugin or profile was changed and DSH was not restarted.
76
76
 
package/DIRECTORY.md ADDED
@@ -0,0 +1,30 @@
1
+ # Directory listing copy
2
+
3
+ Use this factual copy when listing `dsh-codex-subscription` in a DSH plugin directory. The repository README remains the installation authority.
4
+
5
+ ## Short description
6
+
7
+ Use a ChatGPT subscription in DeepSeek Harness through independent OAuth sign-in, with Codex models, backend-reported quota, subscription web search, image generation, and no silent paid fallback.
8
+
9
+ ## 中文短描述
10
+
11
+ 通过独立 OAuth 登录在 DeepSeek Harness 中使用 ChatGPT 订阅,提供 Codex 模型、服务端实际额度、订阅搜索和图片生成,不会静默切换其他付费路由。
12
+
13
+ ## Facts
14
+
15
+ - Package: `dsh-codex-subscription`
16
+ - Provider: `openai-codex`
17
+ - Authentication: independent ChatGPT OAuth managed on the DSH host
18
+ - Codex CLI required: no
19
+ - OpenAI API key required: no
20
+ - Platforms: official DSH plus DSH-Portable product forms for Windows, macOS, and Linux
21
+ - Main capabilities: Codex models, quota, web search, image generation, optional composer quota, optional Fast mode, secret-free support diagnostics
22
+ - Safety boundary: subscription failures remain visible and never silently fall back to another paid provider
23
+
24
+ ## Install
25
+
26
+ ```sh
27
+ dsh plugin --profile web add dsh-codex-subscription
28
+ ```
29
+
30
+ Do not describe this plugin as reusing a local Codex CLI login, copying `~/.codex/auth.json`, requiring Windows, or providing Claude, Grok, Gemini, or GitHub Copilot subscriptions.
package/README.en.md CHANGED
@@ -12,7 +12,7 @@ No OpenAI API key or Codex CLI. Models, search, quota, and image generation stay
12
12
  [![MIT](https://img.shields.io/badge/license-MIT-111111.svg)](LICENSE)
13
13
  [![Star](https://img.shields.io/github/stars/WSL043/dsh-codex-subscription?style=flat&logo=github&label=Star)](https://github.com/WSL043/dsh-codex-subscription/stargazers)
14
14
 
15
- [Agent install](#let-an-agent-install-it-recommended) · [Windows install](#manual-windows-install) · [Update and uninstall](#update-and-uninstall) · [简体中文](https://github.com/WSL043/dsh-codex-subscription/blob/main/README.md)
15
+ [Three-step start](#three-step-start) · [Agent install](#let-an-agent-install-it-recommended) · [Update and uninstall](#update-and-uninstall) · [简体中文](https://github.com/WSL043/dsh-codex-subscription/blob/main/README.md)
16
16
 
17
17
  </div>
18
18
 
@@ -20,14 +20,34 @@ No OpenAI API key or Codex CLI. Models, search, quota, and image generation stay
20
20
  <img src="https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/readme-hero-en.webp" width="900" alt="Your Codex subscription inside DSH: subscription models, web search, live quota, and image generation">
21
21
  </p>
22
22
 
23
+ ## Three-step start
24
+
25
+ 1. **Install the plugin.** On Windows, open PowerShell and run the line below. Existing `dsh` and DSH-Portable users can use the standard command that follows.
26
+
27
+ ```powershell
28
+ irm 'https://github.com/WSL043/dsh-codex-subscription/releases/latest/download/dsh-codex-setup.ps1' | iex
29
+ ```
30
+
31
+ 2. **Sign in.** Restart DSH yourself, open **Settings -> Codex**, and choose browser sign-in. No Codex CLI and no pasted token are required.
32
+ 3. **Use Codex.** Select a Codex model. Quota, subscription search, image generation, and Fast mode remain inside DSH.
33
+
34
+ With an existing `dsh` command, install with:
35
+
36
+ ```sh
37
+ dsh plugin --profile web add dsh-codex-subscription
38
+ ```
39
+
40
+ From a DSH-Portable product folder, run `./dsh plugin ...` (`.\dsh.exe plugin ...` in Windows PowerShell). See below for the complete official npm, Agent, update, and uninstall routes.
41
+
23
42
  ## Why this plugin
24
43
 
25
44
  | Capability | What you get |
26
45
  | --- | --- |
27
- | **Codex image generation** | Describe an image in a DSH conversation and view the generated result in that session |
28
46
  | **Subscription models** | Sign in to ChatGPT and use Codex without an OpenAI API key or Codex CLI |
29
- | **Subscription search** | Explicitly choose DSH default search or Codex subscription search |
47
+ | **Recoverable and diagnosable** | Sign-in state reconciles automatically; Settings can create a support report without credentials or account identifiers |
30
48
  | **Visible quota** | Keep backend-provided standard Codex, Spark, and other limits separate |
49
+ | **Subscription search** | Explicitly choose DSH default search or Codex subscription search |
50
+ | **Codex image generation** | Describe an image in a DSH conversation and view the generated result in that session |
31
51
  | **Fast mode (Beta)** | Switch between Standard and Fast directly in the composer |
32
52
 
33
53
  These capabilities reuse the same local ChatGPT sign-in. Subscription routing failures stay visible and never silently switch to another paid route.
@@ -49,7 +69,7 @@ These capabilities reuse the same local ChatGPT sign-in. Subscription routing fa
49
69
 
50
70
  This plugin is currently compatible through DeepSeek Harness `0.1.1-rc.1` and requires a ChatGPT account that currently has Codex access.
51
71
 
52
- - Do not want to configure Node.js? Use the [community DSH-Portable package](https://github.com/WSL043/DSH-Portable).
72
+ - Do not want to configure Node.js? Use [DSH-Portable](https://github.com/WSL043/DSH-Portable), a community desktop distribution with portable and installed editions for Windows plus desktop packages for macOS and Linux.
53
73
  - Prefer the official route? Follow the [DeepSeek Harness run guide](https://github.com/deepseek-ai/deepseek-harness#run).
54
74
 
55
75
  ## Install
@@ -124,6 +144,7 @@ Restart DSH manually after installation, then:
124
144
  - Separate standard Codex, Codex-Spark, Credits, and other independent limits;
125
145
  - Optional composer quota for the selected Codex model (Beta, off by default);
126
146
  - Standard or Fast mode for supported Codex models directly in the composer (Beta);
147
+ - A copyable support report in Settings that excludes OAuth credentials, account identifiers, and authorization timestamps;
127
148
  - Visible errors when subscription routing is unavailable, with no silent paid fallback.
128
149
 
129
150
  ### Composer quota
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [![MIT](https://img.shields.io/badge/license-MIT-111111.svg)](LICENSE)
14
14
  [![Star](https://img.shields.io/github/stars/WSL043/dsh-codex-subscription?style=flat&logo=github&label=Star)](https://github.com/WSL043/dsh-codex-subscription/stargazers)
15
15
 
16
- [交给 Agent 安装](#交给-agent推荐) · [Windows 安装](#windows-手动安装) · [更新与卸载](#更新与卸载) · [English](https://github.com/WSL043/dsh-codex-subscription/blob/main/README.en.md)
16
+ [三步开始](#三步开始) · [交给 Agent 安装](#交给-agent推荐) · [更新与卸载](#更新与卸载) · [English](https://github.com/WSL043/dsh-codex-subscription/blob/main/README.en.md)
17
17
 
18
18
  </div>
19
19
 
@@ -21,14 +21,34 @@
21
21
  <img src="https://raw.githubusercontent.com/WSL043/dsh-codex-subscription/main/docs/assets/readme-hero.webp" width="900" alt="Codex 订阅直接用在 DSH:订阅模型、联网搜索、实时额度和图片生成">
22
22
  </p>
23
23
 
24
+ ## 三步开始
25
+
26
+ 1. **安装插件**:Windows 打开 PowerShell,运行下面一行;已有 `dsh` 或 DSH-Portable 的用户也可使用后面的标准命令。
27
+
28
+ ```powershell
29
+ irm 'https://github.com/WSL043/dsh-codex-subscription/releases/latest/download/dsh-codex-setup.ps1' | iex
30
+ ```
31
+
32
+ 2. **登录订阅**:手动重启 DSH,打开 **设置 -> Codex 订阅**,点击浏览器登录。无需 Codex CLI,也不要粘贴 token。
33
+ 3. **开始使用**:在模型选择器中选择 Codex;额度、订阅搜索、图片生成和高速模式都在 DSH 内使用。
34
+
35
+ 已有 `dsh` 命令时,标准安装命令是:
36
+
37
+ ```sh
38
+ dsh plugin --profile web add dsh-codex-subscription
39
+ ```
40
+
41
+ DSH-Portable 用户在产品目录中运行 `./dsh plugin ...`,Windows PowerShell 使用 `.\dsh.exe plugin ...`。完整的官方 npm、Agent 安装、更新和卸载方式见下文。
42
+
24
43
  ## 核心优势
25
44
 
26
45
  | 能力 | 用户得到什么 |
27
46
  | --- | --- |
28
- | **Codex 图片生成** | 在 DSH 对话里直接描述画面,生成结果会显示在当前会话中 |
29
47
  | **订阅模型直连** | 登录 ChatGPT 后直接使用 Codex,不需要 OpenAI API Key 或 Codex CLI |
30
- | **订阅搜索** | 可在 DSH 默认搜索与 Codex 订阅搜索之间明确切换 |
48
+ | **可恢复、可诊断** | 登录状态会自动对账;设置页可生成不含凭据和账号标识的支持报告 |
31
49
  | **额度可见** | 普通 Codex、Spark 等服务端实际返回的额度分开显示 |
50
+ | **订阅搜索** | 可在 DSH 默认搜索与 Codex 订阅搜索之间明确切换 |
51
+ | **Codex 图片生成** | 在 DSH 对话里直接描述画面,生成结果会显示在当前会话中 |
32
52
  | **高速模式(Beta)** | 直接在输入框切换标准或高速,无需离开当前会话 |
33
53
 
34
54
  这些能力共用同一份本机 ChatGPT 登录。订阅路由失败时会明确报错,不会静默切换到其他付费路由。
@@ -50,7 +70,7 @@
50
70
 
51
71
  本插件当前适配到 DeepSeek Harness `0.1.1-rc.1`,并需要一个当前具有 Codex 使用资格的 ChatGPT 账户。
52
72
 
53
- - 不想配置 Node.js:使用 [DSH-Portable(社区便携包)](https://github.com/WSL043/DSH-Portable)
73
+ - 不想配置 Node.js:使用 [DSH-Portable](https://github.com/WSL043/DSH-Portable)。这是社区桌面分发,提供 Windows 便携版和安装版,以及 macOS、Linux 桌面包;
54
74
  - 想按官方方式运行:查看 [DeepSeek Harness 官方说明](https://github.com/deepseek-ai/deepseek-harness#run)。
55
75
 
56
76
  ## 安装
@@ -124,6 +144,7 @@ DSH-Portable 请把 `dsh` 换成 `./dsh`(PowerShell 使用 `.\dsh.exe`)。
124
144
  - 普通 Codex、Codex-Spark、Credits 等独立额度分开显示;
125
145
  - 输入框可显示当前 Codex 模型的剩余额度(Beta,默认关闭);
126
146
  - 输入框可为支持的 Codex 模型切换标准或高速模式(Beta);
147
+ - 设置页可生成并复制无敏感信息的支持诊断,报告不包含 OAuth 凭据、账号标识或授权时间;
127
148
  - 订阅路由不可用时明确报错,不会静默切换到其他付费路由。
128
149
 
129
150
  ### 输入框额度
@@ -175,6 +196,7 @@ DSH-Portable 在其目录中把上述 `dsh` 换成 `.\dsh.exe`。
175
196
  - **找不到 DSH-Portable**:进入它的目录后重新执行安装命令,或直接运行 `.\dsh.exe plugin --profile web add dsh-codex-subscription`;
176
197
  - **电脑上有多个 DSH**:单行助手会合并正在运行的副本与常用目录内检测到的 Portable,输入对应编号即可;交给 Agent 时让它进入目标目录,或明确传入 `-DshPath`;
177
198
  - **安装仍然失败**:把上面的 Agent 文档链接发给 Agent,不要删除 profile 或随意修改系统 PATH。
199
+ - **需要提交问题**:在设置页底部生成“支持诊断”并复制到 Issue。报告不含凭据和账号标识;仍不要附上登录链接、授权码或浏览器回调地址。
178
200
 
179
201
  ## 边界与支持
180
202
 
package/lib/client.js CHANGED
@@ -79,6 +79,34 @@ window.__ModuleLoader__.load({
79
79
  };
80
80
  }
81
81
  //#endregion
82
+ //#region src/login-progress.js
83
+ /** Reconcile a login flow with the credential store without exposing credentials. */
84
+ async function readLoginProgress({ flow, readFlow, readAccount }) {
85
+ try {
86
+ const nextFlow = await readFlow();
87
+ if (nextFlow.phase !== "authenticated") return { flow: nextFlow };
88
+ return {
89
+ flow: nextFlow,
90
+ account: await readAccount()
91
+ };
92
+ } catch (flowError) {
93
+ try {
94
+ const account = await readAccount();
95
+ if (account?.authenticated === true) return {
96
+ flow: {
97
+ id: flow.id,
98
+ method: flow.method,
99
+ phase: "authenticated",
100
+ authenticated: true
101
+ },
102
+ account,
103
+ recovered: true
104
+ };
105
+ } catch {}
106
+ throw flowError;
107
+ }
108
+ }
109
+ //#endregion
82
110
  //#region src/client.jsx
83
111
  const inject = [
84
112
  "slots",
@@ -112,6 +140,12 @@ window.__ModuleLoader__.load({
112
140
  failed: "登录失败,请重试。",
113
141
  loadFailed: "无法读取账户状态。",
114
142
  accountRetry: "重试",
143
+ diagnostics: "支持诊断",
144
+ diagnosticsHint: "生成不含凭据、账号标识和授权时间的诊断信息。",
145
+ diagnosticsLoad: "生成诊断",
146
+ diagnosticsCopy: "复制诊断",
147
+ diagnosticsCopied: "已复制",
148
+ diagnosticsFailed: "无法生成诊断信息。",
115
149
  searchTitle: "搜索来源",
116
150
  searchDsh: "DSH 默认",
117
151
  searchDshHint: "当前搜索服务",
@@ -195,6 +229,12 @@ window.__ModuleLoader__.load({
195
229
  failed: "Sign-in failed. Try again.",
196
230
  loadFailed: "Could not read account status.",
197
231
  accountRetry: "Retry",
232
+ diagnostics: "Support diagnostics",
233
+ diagnosticsHint: "Create a report without credentials, account identifiers, or authorization timestamps.",
234
+ diagnosticsLoad: "Create report",
235
+ diagnosticsCopy: "Copy report",
236
+ diagnosticsCopied: "Copied",
237
+ diagnosticsFailed: "Could not create diagnostics.",
198
238
  searchTitle: "Search source",
199
239
  searchDsh: "DSH default",
200
240
  searchDshHint: "Current search service",
@@ -276,6 +316,7 @@ window.__ModuleLoader__.load({
276
316
  .codexSubscriptionFlow p{font-size:13px;line-height:20px;color:var(--dsw-alias-label-secondary)}.codexSubscriptionCode{width:max-content;max-width:100%;font:600 16px/22px ui-monospace,SFMono-Regular,Consolas,monospace;letter-spacing:.08em;overflow-wrap:anywhere}
277
317
  .codexSubscriptionError{font-size:13px;line-height:20px;color:var(--dsw-alias-state-error-primary)}.codexSubscriptionInput{width:100%;box-sizing:border-box}
278
318
  .codexSubscriptionRecover{display:flex;align-items:center;justify-content:space-between;gap:12px}.codexSubscriptionRecover .codexSubscriptionError{flex:1}.codexSubscriptionRecover button{flex:0 0 auto}
319
+ .codexSubscriptionDiagnostics pre{max-height:240px;margin:0;padding:10px 12px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/17px ui-monospace,SFMono-Regular,Consolas,monospace;color:var(--dsw-alias-label-secondary)}
279
320
  .codexSubscriptionSectionTitle{display:flex;flex:1;min-width:0;flex-direction:column;gap:2px}.codexSubscriptionFreshness{font-size:11px;line-height:17px;color:var(--dsw-alias-label-tertiary)}
280
321
  .codexSubscriptionRefresh{flex:0 0 auto;min-width:72px;width:max-content;white-space:nowrap!important;word-break:keep-all!important;overflow-wrap:normal!important;writing-mode:horizontal-tb!important}.codexSubscriptionRefresh *{white-space:nowrap!important;word-break:keep-all!important;writing-mode:horizontal-tb!important}
281
322
  .codexSubscriptionEmpty{padding:18px;border:1px dashed var(--dsw-alias-border-l3);border-radius:10px;text-align:center;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}
@@ -985,12 +1026,17 @@ window.__ModuleLoader__.load({
985
1026
  "cancelled"
986
1027
  ].includes(flow.phase)) return void 0;
987
1028
  const timer = window.setInterval(() => {
988
- call("login/status", { id: flow.id }).then((next) => {
989
- setFlow(next);
990
- if (next.phase === "authenticated") call("status").then((account) => {
991
- setAccount(account);
1029
+ readLoginProgress({
1030
+ flow,
1031
+ readFlow: () => call("login/status", { id: flow.id }),
1032
+ readAccount: () => call("status")
1033
+ }).then((next) => {
1034
+ setFlow(next.flow);
1035
+ setError(void 0);
1036
+ if (next.account !== void 0) {
1037
+ setAccount(next.account);
992
1038
  notifyQuickQuota();
993
- });
1039
+ }
994
1040
  }).catch(() => setError(t("failed")));
995
1041
  }, 800);
996
1042
  return () => window.clearInterval(timer);
@@ -1006,7 +1052,21 @@ window.__ModuleLoader__.load({
1006
1052
  const cancel = () => {
1007
1053
  if (flow?.id === void 0) return;
1008
1054
  setBusy(true);
1009
- call("login/cancel", { id: flow.id }).then(setFlow).finally(() => setBusy(false));
1055
+ call("login/cancel", { id: flow.id }).then((next) => {
1056
+ setFlow(next);
1057
+ return call("status").then((account) => {
1058
+ if (account.authenticated === true) {
1059
+ setAccount(account);
1060
+ setFlow({
1061
+ ...next,
1062
+ phase: "authenticated",
1063
+ authenticated: true
1064
+ });
1065
+ setError(void 0);
1066
+ notifyQuickQuota();
1067
+ }
1068
+ });
1069
+ }).catch(() => setError(t("failed"))).finally(() => setBusy(false));
1010
1070
  };
1011
1071
  const submit = (event) => {
1012
1072
  event.preventDefault();
@@ -1157,6 +1217,57 @@ window.__ModuleLoader__.load({
1157
1217
  })]
1158
1218
  });
1159
1219
  }
1220
+ function DiagnosticsCard({ rpc, t }) {
1221
+ const [report, setReport] = (0, react.useState)();
1222
+ const [busy, setBusy] = (0, react.useState)(false);
1223
+ const [copied, setCopied] = (0, react.useState)(false);
1224
+ const [error, setError] = (0, react.useState)(false);
1225
+ const load = () => {
1226
+ setBusy(true);
1227
+ setError(false);
1228
+ setCopied(false);
1229
+ rpc.call(CHANNEL, "diagnostics", {}).then(unwrap).then(setReport).catch(() => setError(true)).finally(() => setBusy(false));
1230
+ };
1231
+ const copy = () => {
1232
+ if (report === void 0) return;
1233
+ navigator.clipboard.writeText(JSON.stringify(report, null, 2)).then(() => setCopied(true)).catch(() => setError(true));
1234
+ };
1235
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1236
+ className: "codexSubscriptionCard codexSubscriptionDiagnostics",
1237
+ children: [
1238
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1239
+ className: "codexSubscriptionSectionHead",
1240
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1241
+ className: "codexSubscriptionSectionTitle",
1242
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("diagnostics") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1243
+ className: "codexSubscriptionNote",
1244
+ children: t("diagnosticsHint")
1245
+ })]
1246
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1247
+ className: "codexSubscriptionActions",
1248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1249
+ type: "button",
1250
+ variant: "outline",
1251
+ disabled: busy,
1252
+ onClick: load,
1253
+ children: t("diagnosticsLoad")
1254
+ }), report === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1255
+ type: "button",
1256
+ variant: "outline",
1257
+ onClick: copy,
1258
+ children: copied ? t("diagnosticsCopied") : t("diagnosticsCopy")
1259
+ })]
1260
+ })]
1261
+ }),
1262
+ report === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", { children: JSON.stringify(report, null, 2) }),
1263
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1264
+ className: "codexSubscriptionError",
1265
+ role: "alert",
1266
+ children: t("diagnosticsFailed")
1267
+ }) : null
1268
+ ]
1269
+ });
1270
+ }
1160
1271
  function ResetTime({ resetsAt, t }) {
1161
1272
  const date = Number.isSafeInteger(resetsAt) ? validDate(resetsAt * 1e3) : void 0;
1162
1273
  if (date === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("resetUnknown") });
@@ -1383,6 +1494,10 @@ window.__ModuleLoader__.load({
1383
1494
  t,
1384
1495
  signedIn: account.authenticated === true,
1385
1496
  resetKey
1497
+ }),
1498
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DiagnosticsCard, {
1499
+ rpc,
1500
+ t
1386
1501
  })
1387
1502
  ]
1388
1503
  });
package/lib/index.js CHANGED
@@ -373,7 +373,6 @@ var CodexLoginCoordinator = class {
373
373
  };
374
374
  session.controller.abort(/* @__PURE__ */ new Error("Codex login cancelled"));
375
375
  }
376
- await Promise.resolve(session.run).catch(() => void 0);
377
376
  return this.read(id);
378
377
  }
379
378
  async logout(options) {
@@ -495,7 +494,10 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0 } = {
495
494
  streamSimple: (model, context, options) => provider.streamSimple(model, context, withSpeed(model, options))
496
495
  });
497
496
  }
498
- const USER_AGENT = `dsh-codex-subscription/1.1.5`;
497
+ //#endregion
498
+ //#region src/version.js
499
+ const PACKAGE_VERSION = "1.2.0";
500
+ const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
499
501
  //#endregion
500
502
  //#region src/codex-search.js
501
503
  const CODEX_SEARCH_PROVIDER_ID = "codex-subscription";
@@ -823,6 +825,43 @@ function createCodexImageTool(options) {
823
825
  });
824
826
  }
825
827
  //#endregion
828
+ //#region src/diagnostics.js
829
+ /** Build a support report that deliberately excludes OAuth and account metadata. */
830
+ async function createSubscriptionDiagnostics({ auth, preferences }) {
831
+ let account = { status: "unknown" };
832
+ const issues = [];
833
+ try {
834
+ account = { status: (await auth.status()).authenticated === true ? "signed-in" : "signed-out" };
835
+ } catch {
836
+ issues.push({ code: "account-status-unavailable" });
837
+ }
838
+ return {
839
+ schemaVersion: 1,
840
+ package: "dsh-codex-subscription",
841
+ version: PACKAGE_VERSION,
842
+ runtime: { node: process.version },
843
+ account,
844
+ provider: {
845
+ id: "openai-codex",
846
+ transport: "sse",
847
+ cache: {
848
+ owner: "dsh/pi-ai",
849
+ retention: "short"
850
+ }
851
+ },
852
+ capabilities: {
853
+ models: true,
854
+ quota: true,
855
+ search: true,
856
+ imageGeneration: true,
857
+ composerQuota: true,
858
+ speedMode: true
859
+ },
860
+ configuration: preferences.status(),
861
+ issues
862
+ };
863
+ }
864
+ //#endregion
826
865
  //#region src/usage.js
827
866
  const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
828
867
  const DEFAULT_TTL_MS = 6e4;
@@ -1024,8 +1063,18 @@ const publicError = (code, message) => ({
1024
1063
  details: { issues: [] }
1025
1064
  }
1026
1065
  });
1027
- function createSubscriptionRpcHandler({ authHandler, usageReader, preferences }) {
1066
+ function createSubscriptionRpcHandler({ authHandler, usageReader, preferences, diagnosticsReader }) {
1028
1067
  return async (endpoint, payload, signal) => {
1068
+ if (endpoint === "diagnostics") try {
1069
+ signal.throwIfAborted();
1070
+ return {
1071
+ ok: true,
1072
+ value: await diagnosticsReader()
1073
+ };
1074
+ } catch (error) {
1075
+ if (signal.aborted) throw error;
1076
+ return publicError("internal", "Could not create support diagnostics");
1077
+ }
1029
1078
  if (endpoint === "preferences/status" || endpoint === "preferences/update") try {
1030
1079
  signal.throwIfAborted();
1031
1080
  if (endpoint === "preferences/update") {
@@ -1170,7 +1219,8 @@ function apply(ctx) {
1170
1219
  select(settings.get());
1171
1220
  return settings.watch(select);
1172
1221
  }, "codex-subscription: search provider selection");
1173
- const coordinator = new CodexLoginCoordinator(createCodexAuthService(authModels, store));
1222
+ const auth = createCodexAuthService(authModels, store);
1223
+ const coordinator = new CodexLoginCoordinator(auth);
1174
1224
  const usageReader = createCodexUsageReader({
1175
1225
  getAuth: resolveAuth,
1176
1226
  readCredential: (options) => store.read(PROVIDER, options)
@@ -1178,9 +1228,13 @@ function apply(ctx) {
1178
1228
  const handler = createSubscriptionRpcHandler({
1179
1229
  authHandler: createCodexRpcHandler(coordinator, { openExternal: openCodexAuthUrl }),
1180
1230
  usageReader,
1181
- preferences
1231
+ preferences,
1232
+ diagnosticsReader: () => createSubscriptionDiagnostics({
1233
+ auth,
1234
+ preferences
1235
+ })
1182
1236
  });
1183
1237
  ctx.effect(() => ctx.connection.rpc.handle(CHANNEL, handler, { authority: "loopback" }), "codex-subscription: loopback account RPC");
1184
1238
  }
1185
1239
  //#endregion
1186
- export { CODEX_IMAGE_GENERATION_URL, CODEX_IMAGE_TOOL_NAME, CODEX_USAGE_URL, CodexLoginCoordinator, DshOAuthCredentialStore, apply, assertCodexAuthUrl, commandForCodexAuthUrl, createCodexAuthService, createCodexImageTool, createCodexRpcHandler, createCodexUsageReader, createSearchProviderSwitcher, createSubscriptionRpcHandler, decodeCodexPng, inject, name, openCodexAuthUrl, parseCodexUsage };
1240
+ export { CODEX_IMAGE_GENERATION_URL, CODEX_IMAGE_TOOL_NAME, CODEX_USAGE_URL, CodexLoginCoordinator, DshOAuthCredentialStore, apply, assertCodexAuthUrl, commandForCodexAuthUrl, createCodexAuthService, createCodexImageTool, createCodexRpcHandler, createCodexUsageReader, createSearchProviderSwitcher, createSubscriptionDiagnostics, createSubscriptionRpcHandler, decodeCodexPng, inject, name, openCodexAuthUrl, parseCodexUsage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "1.1.5",
3
+ "version": "1.2.0",
4
4
  "description": "ChatGPT and Codex subscriptions for DeepSeek Harness with OAuth login, image generation, quota, and subscription web search",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -15,6 +15,7 @@
15
15
  "cordis.patch.yml",
16
16
  "README.md",
17
17
  "README.en.md",
18
+ "DIRECTORY.md",
18
19
  "AGENTS.md",
19
20
  "compatibility.json",
20
21
  "LICENSE",
@@ -46,7 +47,7 @@
46
47
  "deepseek-harness",
47
48
  "dsh",
48
49
  "openai-codex",
49
- "codex-cli",
50
+ "chatgpt-oauth",
50
51
  "chatgpt",
51
52
  "chatgpt-plus",
52
53
  "subscription",