dsh-codex-connect 0.1.0-alpha.4.16 → 0.1.0-alpha.4.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/INSTALL.md CHANGED
@@ -12,7 +12,7 @@ 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.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.16 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should 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.
15
+ The only verified combination is DSH plugin API packages `0.1.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.18 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should 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
16
 
17
17
  1. Check `dsh --version` or `dsh --help`. From a Harness checkout use `pnpm dsh`.
18
18
  2. Install the package:
@@ -21,7 +21,7 @@ The only verified combination is DSH plugin API packages `0.1.1-rc.2`, `@earendi
21
21
  dsh plugin --profile web add dsh-codex-connect@alpha
22
22
  ```
23
23
 
24
- After `0.1.0-alpha.4.16` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.16`. 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.16'`.
24
+ After `0.1.0-alpha.4.18` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.18`. 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.18'`.
25
25
 
26
26
  3. Run `dsh --profile web --dump-config` and require exactly one `llm-openai-codex` row loading `dsh-codex-connect`.
27
27
  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: f3b46026b1b23633738915667fa6e20c9513c290
6
- docs/README.zh.md: ec299175d4383a1f07af207378be9aaa0b8edbe2
5
+ README.md: a08627cda39b43cbc75d48f50370d8dcb7511365
6
+ docs/README.zh.md: 3b018afca725285aa933eccc8b1c65599f1bf242
package/README.md CHANGED
@@ -28,13 +28,13 @@ 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.16`. 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.16'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
31
+ To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.18`. 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.18'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
32
32
 
33
33
  ### Version updates
34
34
 
35
- Codex Connect checks public package metadata and this repository's `verified-compatibility.json` periodically through the DSH Web server. The same card reads the locally loaded DSH package version, shows it beside the latest DSH version recorded by this project, and compares the installed and latest plugin versions without repeating equal values. Local version detection uses package metadata already available to the plugin and does not require a DSH Core change.
35
+ Codex Connect checks public package metadata and this repository's `verified-compatibility.json` periodically through the DSH Web server. The same card reads the locally loaded DSH package version, shows it beside the latest DSH version recorded by this project, and evaluates the exact installed plugin and DSH version pair. Local version detection uses package metadata already available to the plugin and does not require a DSH Core change.
36
36
 
37
- The compatibility record lists exact plugin and DSH versions rather than assuming every later release remains compatible. Maintainers can add a newly verified DSH version to the repository file without publishing another plugin release. A green result means the installed plugin was verified; yellow means the latest plugin was verified and should be installed first; red means neither the installed plugin nor the latest published plugin has been verified yet; gray means the record could not be checked. A red result includes a prefilled GitHub issue link so users can remind the maintainer without composing a report from scratch.
37
+ The compatibility record lists exact plugin and DSH versions rather than assuming every later release remains compatible. Maintainers can add a newly verified DSH version to the repository file without publishing another plugin release. A green result means the installed pair was verified; yellow means the latest plugin was verified with the installed DSH version and should be installed first; red means the installed DSH version is known but neither the installed plugin nor the latest published plugin has a matching record; gray means the installed DSH version is not recorded or the public record could not be checked. A red result includes a prefilled GitHub issue link for the installed DSH version so users can remind the maintainer without composing a report from scratch.
38
38
 
39
39
  When a newer plugin version is available, a frame-wide DSH notice appears even if you switch conversations. It first shows the user-facing changes between your installed version and the newest version; technical release notes are available as a secondary detail, or from the release page. The plugin never runs an upgrade command by itself.
40
40
 
@@ -223,7 +223,7 @@ Selecting Codex as the profile's global search route is another explicit change:
223
223
 
224
224
  ## Compatibility and security boundary
225
225
 
226
- - The only verified compatibility combination is DSH plugin API packages `0.1.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`; see [compatibility.json](compatibility.json). Alpha 4.16 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should upgrade to the rc.2 API packages.
226
+ - The only verified compatibility combination is DSH plugin API packages `0.1.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`; see [compatibility.json](compatibility.json). Alpha 4.18 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should upgrade to the rc.2 API packages.
227
227
  - 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.
228
228
  - ChatGPT plan eligibility, model access, quotas, and backend behavior are controlled by OpenAI and may change.
229
229
  - 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.
package/docs/README.zh.md CHANGED
@@ -28,13 +28,13 @@ 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.16`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.16'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
31
+ 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.18`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.18'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
32
32
 
33
33
  ### 版本更新提醒
34
34
 
35
- Codex Connect 会通过 DSH Web 服务定期检查公开的 npm 包信息,以及本仓库维护的 `verified-compatibility.json`。同一张卡片会读取本机实际加载的 DSH 软件包版本,与本项目记录的最新 DSH 版本并列显示,并在不重复相同版本号的前提下比较当前与最新插件版本。本机版本检测只使用插件已经能够取得的软件包元数据,不要求 DSH Core 作出任何修改。
35
+ Codex Connect 会通过 DSH Web 服务定期检查公开的 npm 包信息,以及本仓库维护的 `verified-compatibility.json`。同一张卡片会读取本机实际加载的 DSH 软件包版本,与本项目记录的最新 DSH 版本并列显示,并对当前安装的插件版本和 DSH 版本这一组精确组合进行判断。本机版本检测只使用插件已经能够取得的软件包元数据,不要求 DSH Core 作出任何修改。
36
36
 
37
- 兼容性清单按插件版本和 DSH 版本精确记录,不假设后续版本天然兼容。维护者确认新的 DSH 版本后,只需更新仓库文件,不必重新发布插件。绿色表示当前插件已经通过验证;黄色表示最新插件已经通过验证,应先升级插件;红色表示当前插件和最新发布的插件都尚未通过验证;灰色表示暂时无法检查清单。红色状态会提供预填内容的 GitHub Issue 链接,让用户无需自行组织报告就能提醒维护者。
37
+ 兼容性清单按插件版本和 DSH 版本精确记录,不假设后续版本天然兼容。维护者确认新的 DSH 版本后,只需更新仓库文件,不必重新发布插件。绿色表示当前安装组合已经通过验证;黄色表示最新插件已与当前 DSH 版本通过验证,应先升级插件;红色表示清单中已有当前 DSH 版本,但当前插件和最新发布的插件都没有对应记录;灰色表示清单尚未记录当前 DSH 版本,或暂时无法检查公开清单。红色状态会按当前安装的 DSH 版本提供预填内容的 GitHub Issue 链接,让用户无需自行组织报告就能提醒维护者。
38
38
 
39
39
  发现插件新版本后,即使你切换了对话,DSH 界面也会显示全局更新提醒。提醒会先展示从你当前版本到最新版本之间对用户有用的功能;完整技术发布说明放在次级详情或发布页面中。插件不会自动执行升级命令。
40
40
 
@@ -223,7 +223,7 @@ dsh plugin --profile web exec dsh-codex-connect doctor --json
223
223
 
224
224
  ## 兼容性与安全边界
225
225
 
226
- - 当前唯一已验证的兼容组合是 DSH 插件 API packages `0.1.1-rc.2`、`@earendil-works/pi-ai` `0.82.1` 和 Node.js `^22.19.0 || >=24.0.0`;详见 [compatibility.json](../compatibility.json)。Alpha 4.16 使用 rc.2 的 keyed 插件配置 slot;旧版 DSH API packages 用户应升级到 rc.2 API packages。
226
+ - 当前唯一已验证的兼容组合是 DSH 插件 API packages `0.1.1-rc.2`、`@earendil-works/pi-ai` `0.82.1` 和 Node.js `^22.19.0 || >=24.0.0`;详见 [compatibility.json](../compatibility.json)。Alpha 4.18 使用 rc.2 的 keyed 插件配置 slot;旧版 DSH API packages 用户应升级到 rc.2 API packages。
227
227
  - 升级时请将 DSH 插件 API packages 与 `@earendil-works/pi-ai` 作为一组升级,再运行 `dsh-codex-connect doctor --json` 和兼容性检查。本契约不对未来版本作判断。
228
228
  - ChatGPT 套餐资格、模型权限、额度和后端行为由 OpenAI 控制,可能变化。
229
229
  - Codex 端点不会强制普通 Responses 的 `max_output_tokens` 字段。Harness 压缩仍可工作,但这个摘要上限不能由服务端在该路由上强制。
package/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { L as diagnoseOpenAICodex, c as migrateOpenAICodexSearchHistory, ct as normalizeTrustedOrigin, ft as loginOpenAICodex, mt as openAICodexAuthStatus, pt as logoutOpenAICodex, st as OpenAICodexTrustedOriginsStore, vt as openAICodexAuthPath, z as CODEX_CONNECT_VERSION } from "./src-C-GNEOjg.js";
2
+ import { L as diagnoseOpenAICodex, c as migrateOpenAICodexSearchHistory, ct as normalizeTrustedOrigin, ft as loginOpenAICodex, mt as openAICodexAuthStatus, pt as logoutOpenAICodex, st as OpenAICodexTrustedOriginsStore, vt as openAICodexAuthPath, z as CODEX_CONNECT_VERSION } from "./src-DY3uYj_g.js";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { realpathSync } from "node:fs";
5
5
  import { spawn } from "node:child_process";
package/lib/client.js CHANGED
@@ -580,6 +580,7 @@ window.__ModuleLoader__.load({
580
580
  if (typeof latestVersion !== "string" || parseOpenAICodexVersion(latestVersion) === void 0) return void 0;
581
581
  const compatibility = parseOpenAICodexDshCompatibilityAdvice(record["compatibility"], latestVersion);
582
582
  if (compatibility === void 0) return void 0;
583
+ if (currentDshVersion === void 0 && compatibility.status !== "unverified") return void 0;
583
584
  if (record["status"] === "up-to-date") return {
584
585
  status: "up-to-date",
585
586
  currentVersion,
@@ -631,10 +632,18 @@ window.__ModuleLoader__.load({
631
632
  if (status !== "compatible" && status !== "plugin-update-required" && status !== "not-yet-compatible" && status !== "unverified") return void 0;
632
633
  if (record["latestPluginVersion"] !== latestPluginVersion) return void 0;
633
634
  const latestDshVersion = record["latestDshVersion"];
634
- if (status === "unverified") return latestDshVersion === void 0 ? {
635
- status,
636
- latestPluginVersion
637
- } : void 0;
635
+ if (status === "unverified") {
636
+ if (latestDshVersion === void 0) return {
637
+ status,
638
+ latestPluginVersion
639
+ };
640
+ if (typeof latestDshVersion !== "string" || parseOpenAICodexVersion(latestDshVersion) === void 0) return void 0;
641
+ return {
642
+ status,
643
+ latestPluginVersion,
644
+ latestDshVersion
645
+ };
646
+ }
638
647
  if (typeof latestDshVersion !== "string" || parseOpenAICodexVersion(latestDshVersion) === void 0) return void 0;
639
648
  return {
640
649
  status,
@@ -728,10 +737,9 @@ window.__ModuleLoader__.load({
728
737
  writeCached(result) {
729
738
  if (result.status === "unavailable") return;
730
739
  try {
731
- const { currentDshVersion: _runtimeVersion, ...remoteResult } = result;
732
740
  storage()?.setItem(OPENAI_CODEX_UPDATE_CACHE_KEY, JSON.stringify({
733
741
  checkedAt: Date.now(),
734
- result: remoteResult
742
+ result
735
743
  }));
736
744
  } catch {}
737
745
  }
@@ -740,6 +748,7 @@ window.__ModuleLoader__.load({
740
748
  if (this.disposed || this.request !== void 0) return;
741
749
  const controller = new AbortController();
742
750
  this.request = controller;
751
+ let currentDshVersion;
743
752
  this.setSnapshot({
744
753
  status: "checking",
745
754
  currentVersion: this.currentVersion,
@@ -754,15 +763,12 @@ window.__ModuleLoader__.load({
754
763
  });
755
764
  const runtimeValue = await runtimeResponse.json().catch(() => void 0);
756
765
  const rawCurrentDshVersion = (typeof runtimeValue === "object" && runtimeValue !== null && !Array.isArray(runtimeValue) ? runtimeValue : void 0)?.["currentDshVersion"];
757
- const currentDshVersion = runtimeResponse.ok && typeof rawCurrentDshVersion === "string" && parseOpenAICodexVersion(rawCurrentDshVersion) !== void 0 ? rawCurrentDshVersion : void 0;
766
+ currentDshVersion = runtimeResponse.ok && typeof rawCurrentDshVersion === "string" && parseOpenAICodexVersion(rawCurrentDshVersion) !== void 0 ? rawCurrentDshVersion : void 0;
758
767
  const currentDsh = currentDshVersion === void 0 ? {} : { currentDshVersion };
759
768
  if (!force) {
760
769
  const cached = this.readCached();
761
- if (cached !== void 0) {
762
- this.setSnapshot(resultSnapshot({
763
- ...cached,
764
- ...currentDsh
765
- }, this.dismissedNotice()));
770
+ if (cached !== void 0 && cached.currentVersion === this.currentVersion && cached.currentDshVersion === currentDshVersion) {
771
+ this.setSnapshot(resultSnapshot(cached, this.dismissedNotice()));
766
772
  return;
767
773
  }
768
774
  }
@@ -776,18 +782,17 @@ window.__ModuleLoader__.load({
776
782
  const safeResult = (response.ok ? parseOpenAICodexUpdateResult(value) : void 0) ?? {
777
783
  status: "unavailable",
778
784
  currentVersion: this.currentVersion,
785
+ ...currentDsh,
779
786
  reason: "registry-unavailable"
780
787
  };
781
788
  this.writeCached(safeResult);
782
- this.setSnapshot(resultSnapshot({
783
- ...safeResult,
784
- ...currentDsh
785
- }, this.dismissedNotice()));
789
+ this.setSnapshot(resultSnapshot(safeResult, this.dismissedNotice()));
786
790
  } catch {
787
791
  if (!controller.signal.aborted && !this.disposed) {
788
792
  const unavailable = {
789
793
  status: "unavailable",
790
794
  currentVersion: this.currentVersion,
795
+ ...currentDshVersion === void 0 ? {} : { currentDshVersion },
791
796
  reason: "registry-unavailable"
792
797
  };
793
798
  this.writeCached(unavailable);
@@ -1026,10 +1031,10 @@ window.__ModuleLoader__.load({
1026
1031
  latest
1027
1032
  });
1028
1033
  }
1029
- function compatibilityIssueUrl(currentVersion, latestPluginVersion, latestDshVersion) {
1034
+ function compatibilityIssueUrl(currentVersion, latestPluginVersion, currentDshVersion) {
1030
1035
  const params = new URLSearchParams({
1031
- title: `Verify Codex Connect compatibility with DSH ${latestDshVersion}`,
1032
- body: `The compatibility card reports that Codex Connect ${currentVersion} is not verified with DSH ${latestDshVersion}. The latest published plugin version shown is ${latestPluginVersion}. Please verify or update the public compatibility record.`
1036
+ title: `Verify Codex Connect compatibility with DSH ${currentDshVersion}`,
1037
+ body: `The compatibility card reports that Codex Connect ${currentVersion} is not verified with DSH ${currentDshVersion}. The latest published plugin version shown is ${latestPluginVersion}. Please verify or update the public compatibility record.`
1033
1038
  });
1034
1039
  return `${OPENAI_CODEX_REPOSITORY_URL}/issues/new?${params.toString()}`;
1035
1040
  }
@@ -1154,28 +1159,20 @@ window.__ModuleLoader__.load({
1154
1159
  const [copyFailed, setCopyFailed] = (0, react.useState)(false);
1155
1160
  const [recheckRequested, setRecheckRequested] = (0, react.useState)(false);
1156
1161
  const compatibility = snapshot.compatibility;
1157
- let displayedCompatibilityStatus = compatibility?.status;
1158
1162
  let compatibilityTitleKey = compatibility === void 0 ? void 0 : compatibilityTitleKeys[compatibility.status];
1159
1163
  let compatibilityBodyKey = compatibility === void 0 ? void 0 : compatibilityBodyKeys[compatibility.status];
1160
1164
  if (compatibility?.status === "compatible") {
1161
- if (snapshot.currentDshVersion === void 0 || compatibility.latestDshVersion === void 0) {
1162
- displayedCompatibilityStatus = "unverified";
1163
- compatibilityTitleKey = "compatibilityCurrentDshUnknownTitle";
1164
- compatibilityBodyKey = "compatibilityCurrentDshUnknownBody";
1165
- } else {
1166
- const comparison = compareOpenAICodexVersions(snapshot.currentDshVersion, compatibility.latestDshVersion);
1167
- if (comparison === 0) {
1168
- compatibilityTitleKey = "compatibilityCurrentTitle";
1169
- compatibilityBodyKey = "compatibilityCurrentBody";
1170
- } else if (comparison > 0) {
1171
- displayedCompatibilityStatus = "unverified";
1172
- compatibilityTitleKey = "compatibilityCurrentDshNewerTitle";
1173
- compatibilityBodyKey = "compatibilityCurrentDshNewerBody";
1174
- }
1175
- }
1165
+ compatibilityTitleKey = "compatibilityCurrentTitle";
1166
+ compatibilityBodyKey = "compatibilityCurrentBody";
1167
+ } else if (compatibility?.status === "unverified" && snapshot.currentDshVersion === void 0) {
1168
+ compatibilityTitleKey = "compatibilityCurrentDshUnknownTitle";
1169
+ compatibilityBodyKey = "compatibilityCurrentDshUnknownBody";
1170
+ } else if (compatibility?.status === "unverified" && compatibility.latestDshVersion !== void 0 && snapshot.currentDshVersion !== void 0 && compareOpenAICodexVersions(snapshot.currentDshVersion, compatibility.latestDshVersion) > 0) {
1171
+ compatibilityTitleKey = "compatibilityCurrentDshNewerTitle";
1172
+ compatibilityBodyKey = "compatibilityCurrentDshNewerBody";
1176
1173
  }
1177
1174
  const compatibilityWarning = compatibility?.status === "plugin-update-required" || compatibility?.status === "not-yet-compatible";
1178
- const noticeKey = latestVersion === void 0 ? void 0 : `${latestVersion}:${compatibility?.latestDshVersion ?? "unknown"}:${compatibility?.status ?? "none"}`;
1175
+ const noticeKey = latestVersion === void 0 ? void 0 : `${snapshot.currentVersion}:${latestVersion}:${snapshot.currentDshVersion ?? "unknown"}:${compatibility?.latestDshVersion ?? "unknown"}:${compatibility?.status ?? "none"}`;
1179
1176
  if (overlay && (!compatibilityWarning && snapshot.status !== "update-available" || noticeKey === void 0 || snapshot.dismissedNotice === noticeKey)) return null;
1180
1177
  const available = snapshot.status === "update-available";
1181
1178
  const technicalDetails = available && technicalDetailsOpen;
@@ -1212,12 +1209,12 @@ window.__ModuleLoader__.load({
1212
1209
  }),
1213
1210
  compatibility === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1214
1211
  style: compatibilityStyle,
1215
- "data-compatibility-status": displayedCompatibilityStatus,
1212
+ "data-compatibility-status": compatibility.status,
1216
1213
  children: [
1217
1214
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", {
1218
1215
  style: titleStyle$1,
1219
1216
  children: [
1220
- compatibilityIcons[displayedCompatibilityStatus ?? compatibility.status],
1217
+ compatibilityIcons[compatibility.status],
1221
1218
  " ",
1222
1219
  t(compatibilityTitleKey ?? compatibilityTitleKeys[compatibility.status])
1223
1220
  ]
@@ -1234,8 +1231,8 @@ window.__ModuleLoader__.load({
1234
1231
  style: bodyStyle$1,
1235
1232
  children: t(compatibilityBodyKey ?? compatibilityBodyKeys[compatibility.status])
1236
1233
  }),
1237
- compatibility.status === "not-yet-compatible" && compatibility.latestDshVersion !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1238
- href: compatibilityIssueUrl(snapshot.currentVersion, compatibility.latestPluginVersion, compatibility.latestDshVersion),
1234
+ compatibility.status === "not-yet-compatible" && snapshot.currentDshVersion !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1235
+ href: compatibilityIssueUrl(snapshot.currentVersion, compatibility.latestPluginVersion, snapshot.currentDshVersion),
1239
1236
  target: "_blank",
1240
1237
  rel: "noopener noreferrer",
1241
1238
  style: textButtonStyle,
@@ -1864,8 +1861,7 @@ window.__ModuleLoader__.load({
1864
1861
  const cardStyle = {
1865
1862
  overflow: "hidden",
1866
1863
  border: "1px solid var(--dsw-alias-border-l2)",
1867
- borderRadius: 10,
1868
- background: "var(--dsw-alias-bg-module-platform)"
1864
+ borderRadius: 10
1869
1865
  };
1870
1866
  const headerStyle = {
1871
1867
  boxSizing: "border-box",
@@ -1934,7 +1930,10 @@ window.__ModuleLoader__.load({
1934
1930
  const [open, setOpen] = (0, react.useState)(false);
1935
1931
  const title = t("title");
1936
1932
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
1937
- style: cardStyle,
1933
+ style: {
1934
+ ...cardStyle,
1935
+ background: `var(--dsw-alias-bg-layer-${open ? "2" : "3"})`
1936
+ },
1938
1937
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1939
1938
  type: "button",
1940
1939
  style: headerStyle,
@@ -2474,6 +2473,8 @@ window.__ModuleLoader__.load({
2474
2473
  imageDetail: "{name}: {format} · {width} × {height} · {size}",
2475
2474
  download: "Download",
2476
2475
  downloadNamed: "Download {name}",
2476
+ downloading: "Downloading…",
2477
+ downloadFailed: "Download failed. Retry",
2477
2478
  image: "Generated image",
2478
2479
  open: "Open original image",
2479
2480
  openNamed: "Open {name}",
@@ -2498,7 +2499,7 @@ window.__ModuleLoader__.load({
2498
2499
  updateHeading: "Updates and compatibility",
2499
2500
  compatibilityCompatibleTitle: "You can update DSH",
2500
2501
  compatibilityCompatibleBody: "This Codex Connect version has been verified with the latest DSH version shown here.",
2501
- compatibilityCurrentTitle: "Up to date and compatible",
2502
+ compatibilityCurrentTitle: "Current combination verified",
2502
2503
  compatibilityCurrentBody: "This installed DSH and Codex Connect combination has been verified.",
2503
2504
  compatibilityCurrentDshUnknownTitle: "Current DSH version unavailable",
2504
2505
  compatibilityCurrentDshUnknownBody: "The latest DSH compatibility record is available, but this running DSH version could not be detected.",
@@ -2650,6 +2651,8 @@ window.__ModuleLoader__.load({
2650
2651
  imageDetail: "{name}:{format} · {width} × {height} · {size}",
2651
2652
  download: "下载",
2652
2653
  downloadNamed: "下载 {name}",
2654
+ downloading: "正在下载…",
2655
+ downloadFailed: "下载失败,重试",
2653
2656
  image: "生成的图片",
2654
2657
  open: "打开原图",
2655
2658
  openNamed: "打开 {name}",
@@ -2674,7 +2677,7 @@ window.__ModuleLoader__.load({
2674
2677
  updateHeading: "更新与兼容性",
2675
2678
  compatibilityCompatibleTitle: "可以升级 DSH",
2676
2679
  compatibilityCompatibleBody: "当前 Codex Connect 已通过这里所示最新 DSH 版本的兼容性验证。",
2677
- compatibilityCurrentTitle: "已是最新,兼容正常",
2680
+ compatibilityCurrentTitle: "当前组合已验证兼容",
2678
2681
  compatibilityCurrentBody: "当前安装的 DSH 与 Codex Connect 组合已通过兼容性验证。",
2679
2682
  compatibilityCurrentDshUnknownTitle: "无法检测当前 DSH 版本",
2680
2683
  compatibilityCurrentDshUnknownBody: "可以取得最新 DSH 的兼容性记录,但无法检测当前运行的 DSH 版本。",
@@ -3271,6 +3274,17 @@ window.__ModuleLoader__.load({
3271
3274
  overflowWrap: "break-word",
3272
3275
  pointerEvents: "none"
3273
3276
  };
3277
+ const visuallyHidden = {
3278
+ position: "absolute",
3279
+ width: 1,
3280
+ height: 1,
3281
+ padding: 0,
3282
+ margin: -1,
3283
+ overflow: "hidden",
3284
+ clip: "rect(0 0 0 0)",
3285
+ whiteSpace: "nowrap",
3286
+ border: 0
3287
+ };
3274
3288
  function contentText(content) {
3275
3289
  for (const block of content) if (typeof block === "object" && block !== null && block.type === "text" && typeof block.text === "string") return block.text;
3276
3290
  }
@@ -3352,6 +3366,50 @@ window.__ModuleLoader__.load({
3352
3366
  children: t("actionFailed")
3353
3367
  }) : null;
3354
3368
  }
3369
+ function DownloadButton({ image, label, load, t }) {
3370
+ const [state, setState] = (0, react.useState)("idle");
3371
+ const alive = (0, react.useRef)(true);
3372
+ (0, react.useEffect)(() => () => {
3373
+ alive.current = false;
3374
+ }, []);
3375
+ async function download() {
3376
+ if (state === "pending") return;
3377
+ setState("pending");
3378
+ try {
3379
+ const url = await load(image);
3380
+ const anchor = document.createElement("a");
3381
+ anchor.href = url;
3382
+ anchor.download = image.name ?? t("image");
3383
+ anchor.rel = "noopener";
3384
+ document.body.append(anchor);
3385
+ try {
3386
+ anchor.click();
3387
+ } finally {
3388
+ anchor.remove();
3389
+ }
3390
+ if (alive.current) setState("idle");
3391
+ } catch {
3392
+ if (alive.current) setState("failed");
3393
+ }
3394
+ }
3395
+ const status = state === "pending" ? t("downloading") : state === "failed" ? t("downloadFailed") : void 0;
3396
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3397
+ type: "button",
3398
+ style: action,
3399
+ disabled: state === "pending",
3400
+ "aria-busy": state === "pending",
3401
+ "data-download-state": state,
3402
+ onClick: () => {
3403
+ download();
3404
+ },
3405
+ children: status ?? label
3406
+ }), status === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3407
+ role: "status",
3408
+ "aria-live": "polite",
3409
+ style: visuallyHidden,
3410
+ children: status
3411
+ })] });
3412
+ }
3355
3413
  function PromptPanel({ prompt, t }) {
3356
3414
  const [copyState, setCopyState] = (0, react.useState)("idle");
3357
3415
  const [hovered, setHovered] = (0, react.useState)(false);
@@ -3662,16 +3720,6 @@ window.__ModuleLoader__.load({
3662
3720
  children: t("unknownResult")
3663
3721
  })]
3664
3722
  });
3665
- async function download(image) {
3666
- const url = await load(image);
3667
- const anchor = document.createElement("a");
3668
- anchor.href = url;
3669
- anchor.download = image.name ?? t("image");
3670
- anchor.rel = "noopener";
3671
- document.body.append(anchor);
3672
- anchor.click();
3673
- anchor.remove();
3674
- }
3675
3723
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ResponsiveCard, {
3676
3724
  label: t("completed"),
3677
3725
  visual: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -3723,13 +3771,11 @@ window.__ModuleLoader__.load({
3723
3771
  flexWrap: "wrap",
3724
3772
  gap: 8
3725
3773
  },
3726
- children: decoded.images.map((image, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3727
- type: "button",
3728
- style: action,
3729
- onClick: () => {
3730
- download(image);
3731
- },
3732
- children: decoded.images.length === 1 ? t("download") : t("downloadNamed", { name: image.name ?? String(index + 1) })
3774
+ children: decoded.images.map((image, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DownloadButton, {
3775
+ image,
3776
+ load,
3777
+ t,
3778
+ label: decoded.images.length === 1 ? t("download") : t("downloadNamed", { name: image.name ?? String(index + 1) })
3733
3779
  }, image.attachmentId))
3734
3780
  }),
3735
3781
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
@@ -3759,7 +3805,7 @@ window.__ModuleLoader__.load({
3759
3805
  }
3760
3806
  //#endregion
3761
3807
  //#region src/version.ts
3762
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.16";
3808
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.18";
3763
3809
  //#endregion
3764
3810
  //#region src/client/index.tsx
3765
3811
  /** Stable browser-plugin name. */
package/lib/index.d.ts CHANGED
@@ -519,6 +519,7 @@ interface ParsedVersion {
519
519
  }
520
520
  interface UpdateCheckOptions {
521
521
  currentVersion: string;
522
+ currentDshVersion?: string;
522
523
  fetchImpl?: FetchImpl;
523
524
  timeoutMs?: number;
524
525
  }
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as assessCompatibility, A as OPENAI_CODEX_TRANSPORT_SERVICE, B as OPENAI_CODEX_UPDATE_PATH, C as OPENAI_CODEX_IMAGE_MAX_COUNT, D as OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, E as OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, F as IMAGE_GENERATE_TOOL_NAME, G as COMPATIBILITY_CONTRACT, H as compareOpenAICodexVersions, I as assertNoOpenAICodexProviderConflict, J as DSH_PLUGIN_API_PACKAGES, K as COMPATIBILITY_PACKAGES, L as diagnoseOpenAICodex, M as OpenAICodexTransportError, N as isOpenAICodexTransportError, O as OPENAI_CODEX_TRANSPORT_API_VERSION, P as VIEW_IMAGE_TOOL_NAME, Q as SUPPORTED_PI_AI_VERSION, R as openAICodexConflictMessage, S as OPENAI_CODEX_IMAGE_GENERATION_URL, T as OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, U as parseOpenAICodexUpdateResult, V as checkForOpenAICodexUpdate, W as parseOpenAICodexVersion, X as SUPPORTED_DSH_PLUGIN_API_VERSION, Y as PI_AI_PACKAGE, Z as SUPPORTED_NODE_RANGE, _ as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, _t as OpenAICodexCredentialStore, a as name, at as OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, b as decodeOpenAICodexSettings, c as migrateOpenAICodexSearchHistory, d as OPENAI_CODEX_SEARCH_URL, dt as readOpenAICodexRateLimits, et as detectCompatibility, f as OpenAICodexSearchProvider, ft as loginOpenAICodex, g as DEFAULT_OPENAI_CODEX_SEARCH_MODE, gt as OPENAI_CODEX_PROVIDER, h as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, ht as OPENAI_CODEX_AUTH_FILENAME, i as inject, it as OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, j as OpenAICodexTransport, k as OPENAI_CODEX_TRANSPORT_ERROR_CODES, l as OPENAI_CODEX_BASE_URL, lt as OPENAI_CODEX_USAGE_URL, m as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, mt as openAICodexAuthStatus, n as OPENAI_CODEX_SETTINGS_NS, nt as OPENAI_CODEX_FAST_MODE_PATH, o as OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, ot as isFastModeSessionId, p as mapOpenAICodexSearchResponse, pt as logoutOpenAICodex, q as COMPATIBILITY_SCHEMA_VERSION, r as apply, rt as FastModeRegistry, s as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, t as Config, tt as evaluateCompatibility, u as OPENAI_CODEX_SEARCH_PROVIDER, ut as parseOpenAICodexUsage, v as DEFAULT_OPENAI_CODEX_SETTINGS, vt as openAICodexAuthPath, w as OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, x as resolveOpenAICodexSettings, y as OPENAI_CODEX_SETTINGS_NAMESPACE } from "./src-C-GNEOjg.js";
1
+ import { $ as assessCompatibility, A as OPENAI_CODEX_TRANSPORT_SERVICE, B as OPENAI_CODEX_UPDATE_PATH, C as OPENAI_CODEX_IMAGE_MAX_COUNT, D as OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, E as OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, F as IMAGE_GENERATE_TOOL_NAME, G as COMPATIBILITY_CONTRACT, H as compareOpenAICodexVersions, I as assertNoOpenAICodexProviderConflict, J as DSH_PLUGIN_API_PACKAGES, K as COMPATIBILITY_PACKAGES, L as diagnoseOpenAICodex, M as OpenAICodexTransportError, N as isOpenAICodexTransportError, O as OPENAI_CODEX_TRANSPORT_API_VERSION, P as VIEW_IMAGE_TOOL_NAME, Q as SUPPORTED_PI_AI_VERSION, R as openAICodexConflictMessage, S as OPENAI_CODEX_IMAGE_GENERATION_URL, T as OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, U as parseOpenAICodexUpdateResult, V as checkForOpenAICodexUpdate, W as parseOpenAICodexVersion, X as SUPPORTED_DSH_PLUGIN_API_VERSION, Y as PI_AI_PACKAGE, Z as SUPPORTED_NODE_RANGE, _ as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, _t as OpenAICodexCredentialStore, a as name, at as OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, b as decodeOpenAICodexSettings, c as migrateOpenAICodexSearchHistory, d as OPENAI_CODEX_SEARCH_URL, dt as readOpenAICodexRateLimits, et as detectCompatibility, f as OpenAICodexSearchProvider, ft as loginOpenAICodex, g as DEFAULT_OPENAI_CODEX_SEARCH_MODE, gt as OPENAI_CODEX_PROVIDER, h as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, ht as OPENAI_CODEX_AUTH_FILENAME, i as inject, it as OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, j as OpenAICodexTransport, k as OPENAI_CODEX_TRANSPORT_ERROR_CODES, l as OPENAI_CODEX_BASE_URL, lt as OPENAI_CODEX_USAGE_URL, m as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, mt as openAICodexAuthStatus, n as OPENAI_CODEX_SETTINGS_NS, nt as OPENAI_CODEX_FAST_MODE_PATH, o as OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, ot as isFastModeSessionId, p as mapOpenAICodexSearchResponse, pt as logoutOpenAICodex, q as COMPATIBILITY_SCHEMA_VERSION, r as apply, rt as FastModeRegistry, s as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, t as Config, tt as evaluateCompatibility, u as OPENAI_CODEX_SEARCH_PROVIDER, ut as parseOpenAICodexUsage, v as DEFAULT_OPENAI_CODEX_SETTINGS, vt as openAICodexAuthPath, w as OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, x as resolveOpenAICodexSettings, y as OPENAI_CODEX_SETTINGS_NAMESPACE } from "./src-DY3uYj_g.js";
2
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, FastModeRegistry, FastModeRegistry as OpenAICodexFastModeRegistry, IMAGE_GENERATE_TOOL_NAME, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, OPENAI_CODEX_FAST_MODE_PATH, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, OPENAI_CODEX_IMAGE_GENERATION_URL, OPENAI_CODEX_IMAGE_MAX_COUNT, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, OPENAI_CODEX_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_TRANSPORT_API_VERSION, OPENAI_CODEX_TRANSPORT_ERROR_CODES, OPENAI_CODEX_TRANSPORT_SERVICE, OPENAI_CODEX_UPDATE_PATH, OPENAI_CODEX_USAGE_URL, OpenAICodexCredentialStore, OpenAICodexSearchProvider, OpenAICodexTransport, OpenAICodexTransportError, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, checkForOpenAICodexUpdate, compareOpenAICodexVersions, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, migrateOpenAICodexSearchHistory, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUpdateResult, parseOpenAICodexUsage, parseOpenAICodexVersion, readOpenAICodexRateLimits, resolveOpenAICodexSettings };
@@ -1361,12 +1361,17 @@ function parseOpenAICodexVerifiedCompatibility(value) {
1361
1361
  };
1362
1362
  }
1363
1363
  /** Combine installed, published, and repository-verified versions into one user decision. */
1364
- function evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion, catalog) {
1364
+ function evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion, currentDshVersion, catalog) {
1365
1365
  if (catalog === void 0) return {
1366
1366
  status: "unverified",
1367
1367
  latestPluginVersion
1368
1368
  };
1369
- const verified = (version) => catalog.pluginVersions.some((plugin) => plugin.version === version && plugin.verifiedDshVersions.includes(catalog.latestDshVersion));
1369
+ if (currentDshVersion === void 0) return {
1370
+ status: "unverified",
1371
+ latestPluginVersion,
1372
+ latestDshVersion: catalog.latestDshVersion
1373
+ };
1374
+ const verified = (version) => catalog.pluginVersions.some((plugin) => plugin.version === version && plugin.verifiedDshVersions.includes(currentDshVersion));
1370
1375
  if (verified(currentVersion)) return {
1371
1376
  status: "compatible",
1372
1377
  latestPluginVersion,
@@ -1378,7 +1383,7 @@ function evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion
1378
1383
  latestDshVersion: catalog.latestDshVersion
1379
1384
  };
1380
1385
  return {
1381
- status: "not-yet-compatible",
1386
+ status: currentDshVersion === catalog.latestDshVersion || catalog.pluginVersions.some((plugin) => plugin.verifiedDshVersions.includes(currentDshVersion)) ? "not-yet-compatible" : "unverified",
1382
1387
  latestPluginVersion,
1383
1388
  latestDshVersion: catalog.latestDshVersion
1384
1389
  };
@@ -1485,21 +1490,23 @@ async function releaseDetails(version, fetchImpl, timeoutMs) {
1485
1490
  return {};
1486
1491
  }
1487
1492
  }
1488
- async function dshCompatibilityAdvice(currentVersion, latestPluginVersion, fetchImpl, timeoutMs) {
1493
+ async function dshCompatibilityAdvice(currentVersion, latestPluginVersion, currentDshVersion, fetchImpl, timeoutMs) {
1489
1494
  try {
1490
1495
  const { response, text } = await fetchBounded(fetchImpl, OPENAI_CODEX_VERIFIED_COMPATIBILITY_URL, OPENAI_CODEX_UPDATE_MAX_COMPATIBILITY_BYTES, timeoutMs, { accept: "application/json" });
1491
- if (!response.ok) return evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion);
1492
- return evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion, parseOpenAICodexVerifiedCompatibility(JSON.parse(text)));
1496
+ if (!response.ok) return evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion, currentDshVersion);
1497
+ return evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion, currentDshVersion, parseOpenAICodexVerifiedCompatibility(JSON.parse(text)));
1493
1498
  } catch {
1494
- return evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion);
1499
+ return evaluateOpenAICodexDshCompatibility(currentVersion, latestPluginVersion, currentDshVersion);
1495
1500
  }
1496
1501
  }
1497
1502
  /** Check npm's public dist-tags and enrich an available update with public release notes. */
1498
1503
  async function checkForOpenAICodexUpdate(options) {
1499
- const { currentVersion } = options;
1504
+ const { currentVersion, currentDshVersion } = options;
1505
+ const currentDsh = currentDshVersion === void 0 ? {} : { currentDshVersion };
1500
1506
  if (parseOpenAICodexVersion(currentVersion) === void 0) return {
1501
1507
  status: "unavailable",
1502
1508
  currentVersion,
1509
+ ...currentDsh,
1503
1510
  reason: "invalid-current-version"
1504
1511
  };
1505
1512
  const fetchImpl = options.fetchImpl ?? fetch;
@@ -1510,6 +1517,7 @@ async function checkForOpenAICodexUpdate(options) {
1510
1517
  if (!response.ok) return {
1511
1518
  status: "unavailable",
1512
1519
  currentVersion,
1520
+ ...currentDsh,
1513
1521
  reason: "registry-unavailable"
1514
1522
  };
1515
1523
  metadata = JSON.parse(text);
@@ -1517,6 +1525,7 @@ async function checkForOpenAICodexUpdate(options) {
1517
1525
  return {
1518
1526
  status: "unavailable",
1519
1527
  currentVersion,
1528
+ ...currentDsh,
1520
1529
  reason: error instanceof SyntaxError || error instanceof RangeError ? "invalid-registry-response" : "registry-unavailable"
1521
1530
  };
1522
1531
  }
@@ -1524,23 +1533,29 @@ async function checkForOpenAICodexUpdate(options) {
1524
1533
  if (candidates.length === 0) return {
1525
1534
  status: "unavailable",
1526
1535
  currentVersion,
1536
+ ...currentDsh,
1527
1537
  reason: "invalid-registry-response"
1528
1538
  };
1529
1539
  const latestVersion = candidates.reduce((best, candidate) => compareOpenAICodexVersions(candidate, best) > 0 ? candidate : best);
1530
- if (compareOpenAICodexVersions(latestVersion, currentVersion) <= 0) return {
1531
- status: "up-to-date",
1532
- currentVersion,
1533
- latestVersion: currentVersion,
1534
- compatibility: await dshCompatibilityAdvice(currentVersion, currentVersion, fetchImpl, timeoutMs)
1535
- };
1540
+ if (compareOpenAICodexVersions(latestVersion, currentVersion) <= 0) {
1541
+ const compatibility = await dshCompatibilityAdvice(currentVersion, currentVersion, currentDshVersion, fetchImpl, timeoutMs);
1542
+ return {
1543
+ status: "up-to-date",
1544
+ currentVersion,
1545
+ ...currentDsh,
1546
+ latestVersion: currentVersion,
1547
+ compatibility
1548
+ };
1549
+ }
1536
1550
  const [highlightResult, details, compatibility] = await Promise.all([
1537
1551
  releaseHighlights(currentVersion, latestVersion, fetchImpl, timeoutMs),
1538
1552
  releaseDetails(latestVersion, fetchImpl, timeoutMs),
1539
- dshCompatibilityAdvice(currentVersion, latestVersion, fetchImpl, timeoutMs)
1553
+ dshCompatibilityAdvice(currentVersion, latestVersion, currentDshVersion, fetchImpl, timeoutMs)
1540
1554
  ]);
1541
1555
  return {
1542
1556
  status: "update-available",
1543
1557
  currentVersion,
1558
+ ...currentDsh,
1544
1559
  latestVersion,
1545
1560
  releaseUrl: releaseUrl(latestVersion),
1546
1561
  highlights: highlightResult.highlights,
@@ -1572,6 +1587,7 @@ function parseOpenAICodexUpdateResult(value) {
1572
1587
  if (typeof latestVersion !== "string" || parseOpenAICodexVersion(latestVersion) === void 0) return void 0;
1573
1588
  const compatibility = parseOpenAICodexDshCompatibilityAdvice(record["compatibility"], latestVersion);
1574
1589
  if (compatibility === void 0) return void 0;
1590
+ if (currentDshVersion === void 0 && compatibility.status !== "unverified") return void 0;
1575
1591
  if (record["status"] === "up-to-date") return {
1576
1592
  status: "up-to-date",
1577
1593
  currentVersion,
@@ -1623,10 +1639,18 @@ function parseOpenAICodexDshCompatibilityAdvice(value, latestPluginVersion) {
1623
1639
  if (status !== "compatible" && status !== "plugin-update-required" && status !== "not-yet-compatible" && status !== "unverified") return void 0;
1624
1640
  if (record["latestPluginVersion"] !== latestPluginVersion) return void 0;
1625
1641
  const latestDshVersion = record["latestDshVersion"];
1626
- if (status === "unverified") return latestDshVersion === void 0 ? {
1627
- status,
1628
- latestPluginVersion
1629
- } : void 0;
1642
+ if (status === "unverified") {
1643
+ if (latestDshVersion === void 0) return {
1644
+ status,
1645
+ latestPluginVersion
1646
+ };
1647
+ if (typeof latestDshVersion !== "string" || parseOpenAICodexVersion(latestDshVersion) === void 0) return void 0;
1648
+ return {
1649
+ status,
1650
+ latestPluginVersion,
1651
+ latestDshVersion
1652
+ };
1653
+ }
1630
1654
  if (typeof latestDshVersion !== "string" || parseOpenAICodexVersion(latestDshVersion) === void 0) return void 0;
1631
1655
  return {
1632
1656
  status,
@@ -1690,6 +1714,7 @@ function registerOpenAICodexUpdateRoutes(ctx, options, trustedOrigins) {
1690
1714
  try {
1691
1715
  result = await checkForOpenAICodexUpdate({
1692
1716
  currentVersion: options.currentVersion,
1717
+ ...currentDshVersion === void 0 ? {} : { currentDshVersion },
1693
1718
  ...options.fetchImpl === void 0 ? {} : { fetchImpl: options.fetchImpl },
1694
1719
  timeoutMs: options.timeoutMs ?? 8e3
1695
1720
  });
@@ -1697,13 +1722,11 @@ function registerOpenAICodexUpdateRoutes(ctx, options, trustedOrigins) {
1697
1722
  result = {
1698
1723
  status: "unavailable",
1699
1724
  currentVersion: options.currentVersion,
1725
+ ...currentDshVersion === void 0 ? {} : { currentDshVersion },
1700
1726
  reason: "registry-unavailable"
1701
1727
  };
1702
1728
  }
1703
- return json(res, 200, {
1704
- ...result,
1705
- ...currentDshVersion === void 0 ? {} : { currentDshVersion }
1706
- });
1729
+ return json(res, 200, result);
1707
1730
  }
1708
1731
  });
1709
1732
  return () => {
@@ -1714,7 +1737,7 @@ function registerOpenAICodexUpdateRoutes(ctx, options, trustedOrigins) {
1714
1737
  }
1715
1738
  //#endregion
1716
1739
  //#region src/version.ts
1717
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.16";
1740
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.18";
1718
1741
  //#endregion
1719
1742
  //#region src/doctor.ts
1720
1743
  /** Secret-free diagnostics and duplicate-provider guidance. */
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.16",
5
+ "version": "0.1.0-alpha.4.18",
6
6
  "author": "Frank Song",
7
7
  "contributors": [
8
8
  "Yan-Zero (original dsh-codex author)"
@@ -41,6 +41,7 @@
41
41
  "lint": "node scripts/lint.mjs",
42
42
  "build": "tsdown",
43
43
  "test": "vitest run",
44
+ "test:browser": "vitest run --config vitest.browser.config.ts",
44
45
  "typecheck": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
45
46
  "check:compatibility": "node scripts/check-compatibility.mjs",
46
47
  "check:canary-workflow": "node scripts/check-canary-workflow.mjs && node scripts/check-dsh-next-contract.mjs",
@@ -156,7 +157,9 @@
156
157
  "@types/node": "^22.20.0",
157
158
  "@types/react": "~18.3.1",
158
159
  "@types/react-dom": "~18.3.1",
160
+ "@vitest/browser-playwright": "4.1.10",
159
161
  "jsdom": "29.1.1",
162
+ "playwright": "1.62.1",
160
163
  "react": "^18.2.0",
161
164
  "react-dom": "^18.2.0",
162
165
  "tsdown": "^0.22.2",