dsh-codex-connect 0.1.0-alpha.4.13 → 0.1.0-alpha.4.14

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
@@ -21,7 +21,7 @@ The only verified combination is DSH plugin API packages `0.1.0-rc.7`, `@earendi
21
21
  dsh plugin --profile web add dsh-codex-connect@alpha
22
22
  ```
23
23
 
24
- After `0.1.0-alpha.4.13` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.13`. 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.13'`.
24
+ After `0.1.0-alpha.4.14` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.14`. 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.14'`.
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/MIGRATION.md CHANGED
@@ -10,3 +10,21 @@
10
10
  6. Run `--dump-config`, then `dsh-codex-connect doctor`. Do not run OAuth again when `status` already reports signed in.
11
11
 
12
12
  Rollback is the inverse package swap. Do not delete or copy the separate OAuth file during either direction. If Harness reports a duplicate `openai-codex` adapter, the old bundle or a manual provider row is still active; resolve that one row instead of changing credentials.
13
+
14
+ ## Repairing search history written by Alpha 4.10
15
+
16
+ Alpha 4.10 briefly wrote `web/openai-codex-search-llm-request` as a required private Session event. Because an external plugin cannot extend the Host persistence vocabulary across independent module instances, a newer Harness can refuse to read those histories after the event writer is removed.
17
+
18
+ Upgrade Codex Connect, then inspect the default `$DSH_HOME/sessions` root without changing it:
19
+
20
+ ```sh
21
+ dsh plugin --profile web exec dsh-codex-connect migrate-history --json
22
+ ```
23
+
24
+ If the dry run reports affected events, stop every DSH process that can write this Session root and apply the migration. `--confirm-stopped` is required together with `--apply`:
25
+
26
+ ```sh
27
+ dsh plugin --profile web exec dsh-codex-connect migrate-history --apply --confirm-stopped --json
28
+ ```
29
+
30
+ The migration changes only that retired event's envelope by adding `"ignorable": true`. It preserves event data, sequence, time, and the concatenated Zstandard frame layout, and creates `session.jsonl.zstd.pre-codex-search-history-migration` beside every changed artifact before replacing it. Keep that backup until you have reopened and verified the repaired Session. Re-running the command is safe. For a non-default JSONL persistence root, pass `--root /absolute/path/to/sessions`. SQLite and uncompressed JSONL stores are not modified by this command. Applying fails closed when the filesystem cannot create the required same-directory hard-link backup. Applying is also fail-closed on Windows; Windows users can run the dry-run only.
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: 2f74e9c6f07f649354b00d8055bc2470a2e85c1c
6
- docs/README.zh.md: a8efb3f3f0380f864bf54cbe5b47487ffd52b52c
5
+ README.md: c496db682e79e0cd36471a2c592de4284819b450
6
+ docs/README.zh.md: 718224bb667a95a9b20fabf59f3d5d5732923790
package/README.md CHANGED
@@ -28,7 +28,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
28
28
 
29
29
  Expected result: the package is added to that profile. This does not change the profile's default model or global search route.
30
30
 
31
- To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.13`. 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.13'`. 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.14`. 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.14'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
32
32
 
33
33
  ### 2. Start Harness
34
34
 
@@ -50,7 +50,7 @@ Expected result: a fresh installation shows **Not signed in** and a **Sign in wi
50
50
 
51
51
  ### 4. Sign in with ChatGPT
52
52
 
53
- Click **Sign in with ChatGPT** and complete the browser approval yourself. Do not copy an authorization URL, code, token, or account identifier into an issue, log, or configuration file.
53
+ Click **Sign in with ChatGPT** and complete the browser approval yourself. If an embedded WebView blocks the sign-in window, use the displayed **Open ChatGPT sign-in page** link to continue in your system browser. Do not copy an authorization URL, code, token, or account identifier into an issue, log, or configuration file.
54
54
 
55
55
  Expected result: the account area changes to **Signed in**. The screenshot below is the successful end state after this step; it is not the initial sign-in screen.
56
56
 
@@ -164,6 +164,7 @@ Selecting Codex as the profile's global search route is another explicit change:
164
164
  - If the card says **Sign in again** or the server asks for reauthentication, click that action and complete the same safe browser flow. It preserves this plugin's capability settings and does not silently change your default model or global search route. Do not run `logout` just to renew a session.
165
165
  - `doctor` reads process and filesystem metadata only. `doctor --json` emits exactly one secret-free JSON document with schema version 1, package/version/Node metadata, credential-file state and safe mode, capabilities, conflict status, and hints. It omits the absolute credential path and OAuth, account, and expiry data.
166
166
  - `status --json` emits only signed-in or signed-out state with package metadata. `status --json` reads the credential only to determine sign-in state, but never prints credential contents or starts OAuth.
167
+ - Alpha 4.10 users whose search histories fail with an unknown `web/openai-codex-search-llm-request` event can run `dsh-codex-connect migrate-history --json`, stop DSH, then apply the reported repair with `migrate-history --apply --confirm-stopped --json`. The command is dry-run by default, backs up every changed compressed JSONL artifact, and is dry-run only on Windows; see [MIGRATION.md](MIGRATION.md).
167
168
  - OAuth is stored separately at `$DSH_HOME/.openai-codex-auth.json` (`~/.dsh` by default). `~/.codex/auth.json` is never copied or modified. The parent directory and file use owner-only permissions where supported, writes are atomic, and refresh writes use a cross-process file lock.
168
169
  - By default, the OAuth routes accept loopback browser requests only. When DSH runs on one device and you open it from another device on a trusted network, approve the browser address-bar origin explicitly on the device that runs DSH:
169
170
 
package/docs/README.zh.md CHANGED
@@ -28,7 +28,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
28
28
 
29
29
  预期结果:包被加入该 profile。这个动作不会更改 profile 的默认模型或全局搜索路由。
30
30
 
31
- 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.13`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.13'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
31
+ 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.14`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.14'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
32
32
 
33
33
  ### 2. 启动 Harness
34
34
 
@@ -50,7 +50,7 @@ dsh web
50
50
 
51
51
  ### 4. 使用 ChatGPT 登录
52
52
 
53
- 点击 **使用 ChatGPT 登录**,并自行完成浏览器审批。不要把授权 URL、授权码、token 或账户标识复制到 Issue、日志或配置文件中。
53
+ 点击 **使用 ChatGPT 登录**,并自行完成浏览器审批。如果内嵌 WebView 阻止登录窗口,请点击页面显示的 **打开 ChatGPT 登录页面**,在系统浏览器中继续。不要把授权 URL、授权码、token 或账户标识复制到 Issue、日志或配置文件中。
54
54
 
55
55
  预期结果:账户区变为 **已登录**。下图展示的是完成本步骤后的成功状态,不是开始登录前的页面。
56
56
 
@@ -163,6 +163,7 @@ dsh plugin --profile web exec dsh-codex-connect doctor --json
163
163
 
164
164
  - 卡片显示 **重新登录**,或服务端要求重新认证时,点击该操作并完成同一套安全的浏览器流程。它会保留本插件的能力配置,不会偷偷改动默认模型或全局搜索路由。不要为了刷新会话而运行 `logout`。
165
165
  - `doctor` 只读取进程与文件系统元数据。`doctor --json` 只输出一条可解析的非敏感 JSON,包含 schema version 1、包/版本/Node 信息、认证文件状态与安全 mode、能力、冲突状态和提示;它省略认证文件绝对路径以及 OAuth、账户和过期时间信息。
166
+ - Alpha 4.10 用户若因未知的 `web/openai-codex-search-llm-request` 事件而无法读取搜索历史,可先运行 `dsh-codex-connect migrate-history --json`,停止 DSH 后再以 `migrate-history --apply --confirm-stopped --json` 应用修复。该命令默认只预检,会备份每个被修改的压缩 JSONL 文件,Windows 仅支持预检;详见 [MIGRATION.md](../MIGRATION.md)。
166
167
  - `status --json` 只输出 signed-in 或 signed-out 状态及包元数据。它只为判断登录态读取认证文件,但不会输出认证文件内容或启动 OAuth。
167
168
  - OAuth 单独存储于 `$DSH_HOME/.openai-codex-auth.json`(默认 `~/.dsh`)。`~/.codex/auth.json` 不会被复制或修改。支持的平台上,父目录与文件使用仅所有者可访问权限;写入采用原子替换,刷新写入使用跨进程文件锁。
168
169
  - 默认情况下,OAuth 路由只接受 loopback 浏览器请求。当 DSH 在一台设备运行,而你从可信网络中的另一台设备打开 DSH 时,请在运行 DSH 的设备上显式批准浏览器地址栏中的 origin:
package/lib/bin.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { $ as OpenAICodexTrustedOriginsStore, I as CODEX_CONNECT_VERSION, P as diagnoseOpenAICodex, at as logoutOpenAICodex, et as normalizeTrustedOrigin, it as loginOpenAICodex, ot as openAICodexAuthStatus, ut as openAICodexAuthPath } from "./src-zOltFEBG.js";
2
+ import { L as diagnoseOpenAICodex, c as migrateOpenAICodexSearchHistory, ct as logoutOpenAICodex, lt as openAICodexAuthStatus, nt as OpenAICodexTrustedOriginsStore, pt as openAICodexAuthPath, rt as normalizeTrustedOrigin, st as loginOpenAICodex, z as CODEX_CONNECT_VERSION } from "./src-15cHQKm5.js";
3
3
  import { fileURLToPath } from "node:url";
4
- import { spawn } from "node:child_process";
5
4
  import { realpathSync } from "node:fs";
5
+ import { spawn } from "node:child_process";
6
6
  import { createInterface } from "node:readline/promises";
7
7
  //#region src/bin.ts
8
8
  /** Standalone credential CLI for the optional OpenAI Codex bundle. */
@@ -65,6 +65,7 @@ async function answerPrompt(prompt, deviceCode, question) {
65
65
  function printHelp() {
66
66
  process.stdout.write([
67
67
  "Usage: dsh-codex-connect <doctor|login|logout|status> [--device-code|--json]",
68
+ " dsh-codex-connect migrate-history [--apply --confirm-stopped] [--root <path>] [--json]",
68
69
  " dsh-codex-connect trust-origin <origin>",
69
70
  " dsh-codex-connect trusted-origins [--json]",
70
71
  " dsh-codex-connect untrust-origin <origin>",
@@ -72,12 +73,13 @@ function printHelp() {
72
73
  " doctor inspect secret-free runtime and OAuth file metadata",
73
74
  " login sign in with a separate ChatGPT OAuth session",
74
75
  " logout remove the dsh credential without changing ~/.codex",
76
+ " migrate-history find or repair Alpha 4.10 private search events (dry-run by default)",
75
77
  " status report non-secret dsh credential state",
76
78
  " trust-origin allow one exact browser origin to reach Web OAuth routes",
77
79
  " trusted-origins list the currently allowed browser origins",
78
80
  " untrust-origin remove one exact browser origin from the allowlist",
79
81
  " --device-code use headless device-code login (login only)",
80
- " --json emit one secret-free JSON document (doctor/status/trusted-origins only)",
82
+ " --json emit one JSON document (doctor/status/trusted-origins/migrate-history)",
81
83
  ""
82
84
  ].join("\n"));
83
85
  }
@@ -118,12 +120,13 @@ async function run(argv) {
118
120
  "doctor",
119
121
  "login",
120
122
  "logout",
123
+ "migrate-history",
121
124
  "status",
122
125
  "trust-origin",
123
126
  "trusted-origins",
124
127
  "untrust-origin"
125
128
  ].includes(rawAction)) {
126
- process.stderr.write(`dsh-codex-connect: expected doctor, login, logout, status, trust-origin, trusted-origins, or untrust-origin; got ${JSON.stringify(rawAction)}\n`);
129
+ process.stderr.write(`dsh-codex-connect: expected doctor, login, logout, migrate-history, status, trust-origin, trusted-origins, or untrust-origin; got ${JSON.stringify(rawAction)}\n`);
127
130
  return 1;
128
131
  }
129
132
  const action = rawAction;
@@ -131,7 +134,25 @@ async function run(argv) {
131
134
  const optionFlags = action === "trust-origin" || action === "untrust-origin" ? flags.slice(1) : flags;
132
135
  const deviceCode = optionFlags.includes("--device-code");
133
136
  const jsonOutput = optionFlags.includes("--json");
134
- if (optionFlags.filter((flag) => flag !== "--device-code" && flag !== "--json").length > 0 || deviceCode && action !== "login" || jsonOutput && (action === "login" || action === "logout" || deviceCode) || (action === "trust-origin" || action === "untrust-origin") && (originArgument === void 0 || optionFlags.length !== 0)) {
137
+ let migrationRoot;
138
+ let migrationApply = false;
139
+ let migrationConfirmStopped = false;
140
+ const unknown = [];
141
+ if (action === "migrate-history") for (let index = 0; index < optionFlags.length; index += 1) {
142
+ const flag = optionFlags[index];
143
+ if (flag === "--apply") {
144
+ if (migrationApply) unknown.push(flag ?? "");
145
+ migrationApply = true;
146
+ } else if (flag === "--confirm-stopped") {
147
+ if (migrationConfirmStopped) unknown.push(flag ?? "");
148
+ migrationConfirmStopped = true;
149
+ } else if (flag === "--json") {} else if (flag === "--root" && migrationRoot === void 0 && optionFlags[index + 1] !== void 0 && !optionFlags[index + 1]?.startsWith("--")) {
150
+ migrationRoot = optionFlags[index + 1];
151
+ index += 1;
152
+ } else unknown.push(flag ?? "");
153
+ }
154
+ else unknown.push(...optionFlags.filter((flag) => flag !== "--device-code" && flag !== "--json"));
155
+ if (unknown.length > 0 || deviceCode && action !== "login" || jsonOutput && (action === "login" || action === "logout" || deviceCode) || action === "migrate-history" && (migrationConfirmStopped && !migrationApply || migrationApply && !migrationConfirmStopped) || (action === "trust-origin" || action === "untrust-origin") && (originArgument === void 0 || optionFlags.length !== 0)) {
135
156
  process.stderr.write(`dsh-codex-connect: invalid options for ${action}: ${flags.join(" ")}\n`);
136
157
  return 1;
137
158
  }
@@ -154,6 +175,23 @@ async function run(argv) {
154
175
  ].join("\n"));
155
176
  return doctorExitCode(report);
156
177
  }
178
+ case "migrate-history": {
179
+ const result = await migrateOpenAICodexSearchHistory({
180
+ apply: migrationApply,
181
+ ...migrationConfirmStopped ? { confirmStopped: true } : {},
182
+ ...migrationRoot === void 0 ? {} : { root: migrationRoot }
183
+ });
184
+ if (jsonOutput) printJson({
185
+ schemaVersion: JSON_SCHEMA_VERSION,
186
+ ...result
187
+ });
188
+ else {
189
+ const verb = result.mode === "apply" ? "Repaired" : "Found";
190
+ process.stdout.write(`${verb} ${result.changedEvents} legacy Codex search event(s) in ${result.changedFiles} session file(s) under ${result.root}.\n`);
191
+ if (result.mode === "dry-run" && result.changedEvents > 0) process.stdout.write("Stop DSH, then run again with --apply --confirm-stopped to create backups and repair these histories.\n");
192
+ }
193
+ return 0;
194
+ }
157
195
  case "status": {
158
196
  const status = await openAICodexAuthStatus();
159
197
  if (jsonOutput) {
package/lib/client.js CHANGED
@@ -745,6 +745,7 @@ window.__ModuleLoader__.load({
745
745
  const [busy, setBusy] = (0, react.useState)(false);
746
746
  const [copied, setCopied] = (0, react.useState)(false);
747
747
  const [copyFailed, setCopyFailed] = (0, react.useState)(false);
748
+ const [loginUrl, setLoginUrl] = (0, react.useState)();
748
749
  const mounted = (0, react.useRef)(true);
749
750
  const trustedOriginCommand = `dsh plugin --profile web exec dsh-codex-connect trust-origin ${window.location.origin}`;
750
751
  (0, react.useEffect)(() => {
@@ -783,27 +784,27 @@ window.__ModuleLoader__.load({
783
784
  controller.abort();
784
785
  };
785
786
  }, [refresh, status.status]);
787
+ (0, react.useEffect)(() => {
788
+ if (status.status !== "signing-in") setLoginUrl(void 0);
789
+ }, [status.status]);
786
790
  const signIn = async () => {
787
791
  const popup = window.open("about:blank", "_blank");
788
- if (popup === null) {
789
- setStatus({
790
- status: "error",
791
- message: t("popupBlocked")
792
- });
793
- return;
794
- }
795
- popup.opener = null;
792
+ if (popup !== null) popup.opener = null;
796
793
  setBusy(true);
797
794
  setStatus({ status: "signing-in" });
798
795
  try {
799
796
  const challenge = await jsonRequest(OPENAI_CODEX_AUTH_LOGIN_PATH, "POST");
800
797
  if (!mounted.current) {
801
- popup.close();
798
+ popup?.close();
802
799
  return;
803
800
  }
804
- popup.location.replace(challenge.url);
801
+ if (popup !== null) {
802
+ setLoginUrl(void 0);
803
+ popup.location.replace(challenge.url);
804
+ } else setLoginUrl(challenge.url);
805
805
  } catch (error) {
806
806
  popup?.close();
807
+ setLoginUrl(void 0);
807
808
  if (mounted.current) setStatus(error instanceof AccountRequestError && error.code === "remote-web-origin-not-trusted" ? { status: "remote-web-origin-not-trusted" } : {
808
809
  status: "error",
809
810
  message: error instanceof Error ? error.message : t("requestFailed")
@@ -874,7 +875,7 @@ window.__ModuleLoader__.load({
874
875
  signOut();
875
876
  },
876
877
  children: busy ? t("working") : t("logout")
877
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
878
+ }) : status.status === "signing-in" && loginUrl !== void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
878
879
  type: "button",
879
880
  style: primaryButtonStyle,
880
881
  disabled: busy,
@@ -884,6 +885,29 @@ window.__ModuleLoader__.load({
884
885
  children: busy ? t("working") : status.status === "error" || status.status === "reauth-required" ? t("loginAgain") : t("login")
885
886
  })]
886
887
  }),
888
+ loginUrl === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
889
+ style: {
890
+ display: "flex",
891
+ flexDirection: "column",
892
+ alignItems: "flex-start",
893
+ gap: 10
894
+ },
895
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
896
+ style: bodyStyle,
897
+ children: t("popupBlockedFallback")
898
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
899
+ href: loginUrl,
900
+ target: "_blank",
901
+ rel: "noopener noreferrer",
902
+ style: {
903
+ ...primaryButtonStyle,
904
+ display: "inline-flex",
905
+ alignItems: "center",
906
+ textDecoration: "none"
907
+ },
908
+ children: t("openLoginInBrowser")
909
+ })]
910
+ }),
887
911
  status.status === "error" || status.status === "reauth-required" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
888
912
  style: errorStyle,
889
913
  children: status.message
@@ -1479,6 +1503,8 @@ window.__ModuleLoader__.load({
1479
1503
  working: "Working…",
1480
1504
  retry: "Retry",
1481
1505
  popupBlocked: "The browser blocked the sign-in window. Allow pop-ups for this dsh page and retry.",
1506
+ popupBlockedFallback: "The sign-in window could not be opened automatically. Use the link below to continue in your browser.",
1507
+ openLoginInBrowser: "Open ChatGPT sign-in page",
1482
1508
  usageLimits: "Usage limits",
1483
1509
  fiveHourLimit: "5-hour limit",
1484
1510
  weeklyLimit: "Weekly limit",
@@ -1576,6 +1602,8 @@ window.__ModuleLoader__.load({
1576
1602
  working: "处理中…",
1577
1603
  retry: "重试",
1578
1604
  popupBlocked: "浏览器阻止了登录窗口。请允许此 dsh 页面弹出窗口后重试。",
1605
+ popupBlockedFallback: "无法自动打开登录窗口。请使用下面的链接在浏览器中继续登录。",
1606
+ openLoginInBrowser: "打开 ChatGPT 登录页面",
1579
1607
  usageLimits: "使用额度",
1580
1608
  fiveHourLimit: "5 小时额度",
1581
1609
  weeklyLimit: "每周额度",
package/lib/index.d.ts CHANGED
@@ -471,6 +471,41 @@ declare class FastModeRegistry {
471
471
  /** GET/POST endpoint for one conversation's process-local Fast Mode state. */
472
472
  declare const OPENAI_CODEX_FAST_MODE_PATH = "/plugins/dsh-openai-codex/fast-mode";
473
473
  //#endregion
474
+ //#region src/history-migration.d.ts
475
+ /** Offline compatibility migration for the private Codex search event emitted by Alpha 4.10. */
476
+ /** Private event written by Alpha 4.10 before the provider stopped persisting it. */
477
+ declare const OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT = "web/openai-codex-search-llm-request";
478
+ /** Backup suffix created beside every changed session artifact. */
479
+ declare const OPENAI_CODEX_HISTORY_BACKUP_SUFFIX = ".pre-codex-search-history-migration";
480
+ interface OpenAICodexHistoryMigrationOptions {
481
+ /** Apply changes; omitted/false performs a read-only dry run. */
482
+ readonly apply?: boolean;
483
+ /** Required acknowledgement that every DSH writer using this root is stopped. */
484
+ readonly confirmStopped?: boolean;
485
+ /** Explicit JSONL persistence root. Defaults to `<DSH_HOME>/sessions`. */
486
+ readonly root?: string;
487
+ /** Optional Harness home override used only when `root` is omitted. */
488
+ readonly dshHome?: string;
489
+ }
490
+ interface OpenAICodexHistoryMigrationFile {
491
+ readonly path: string;
492
+ readonly changedEvents: number;
493
+ readonly backupPath?: string;
494
+ }
495
+ interface OpenAICodexHistoryMigrationResult {
496
+ readonly mode: 'apply' | 'dry-run';
497
+ readonly root: string;
498
+ readonly changedFiles: number;
499
+ readonly changedEvents: number;
500
+ readonly files: readonly OpenAICodexHistoryMigrationFile[];
501
+ }
502
+ /**
503
+ * Mark the retired Alpha 4.10 search event ignorable in compressed JSONL logs.
504
+ * Applying is an offline maintenance operation and fails closed without the
505
+ * caller's explicit acknowledgement that all DSH writers are stopped.
506
+ */
507
+ declare function migrateOpenAICodexSearchHistory(options?: OpenAICodexHistoryMigrationOptions): Promise<OpenAICodexHistoryMigrationResult>;
508
+ //#endregion
474
509
  //#region src/index.d.ts
475
510
  declare module '@deepseek-ai/cordis' {
476
511
  interface Context {
@@ -511,4 +546,4 @@ declare const Config: z<Config>;
511
546
  */
512
547
  declare function apply(ctx: Context, config: Config): void;
513
548
  //#endregion
514
- export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, type CompatibilityDetectionOptions, type CompatibilityEntry, type CompatibilityEvaluationInput, type CompatibilityPackageName, type CompatibilityReport, type CompatibilityStatus, Config, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, FastModeRegistry, FastModeRegistry as OpenAICodexFastModeRegistry, type GeneratedImagePayload, IMAGE_GENERATE_TOOL_NAME, type ImageGenerationRequest, type ImageGenerationResponse, type ImageRequestContext, 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_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_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_USAGE_URL, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexIndividualLimit, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, OpenAICodexTransport, OpenAICodexTransportError, type OpenAICodexTransportErrorCode, type OpenAICodexTransportV1, type OpenAICodexUsage, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, resolveOpenAICodexSettings };
549
+ export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, type CompatibilityDetectionOptions, type CompatibilityEntry, type CompatibilityEvaluationInput, type CompatibilityPackageName, type CompatibilityReport, type CompatibilityStatus, Config, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, FastModeRegistry, FastModeRegistry as OpenAICodexFastModeRegistry, type GeneratedImagePayload, IMAGE_GENERATE_TOOL_NAME, type ImageGenerationRequest, type ImageGenerationResponse, type ImageRequestContext, 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_USAGE_URL, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexHistoryMigrationFile, type OpenAICodexHistoryMigrationOptions, type OpenAICodexHistoryMigrationResult, type OpenAICodexIndividualLimit, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, OpenAICodexTransport, OpenAICodexTransportError, type OpenAICodexTransportErrorCode, type OpenAICodexTransportV1, type OpenAICodexUsage, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, migrateOpenAICodexSearchHistory, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, resolveOpenAICodexSettings };
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as isOpenAICodexTransportError, B as DSH_PLUGIN_API_PACKAGES, C as OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH, D as OPENAI_CODEX_TRANSPORT_SERVICE, E as OPENAI_CODEX_TRANSPORT_ERROR_CODES, F as openAICodexConflictMessage, G as assessCompatibility, H as SUPPORTED_DSH_PLUGIN_API_VERSION, J as OPENAI_CODEX_FAST_MODE_PATH, K as detectCompatibility, L as COMPATIBILITY_CONTRACT, M as IMAGE_GENERATE_TOOL_NAME, N as assertNoOpenAICodexProviderConflict, O as OpenAICodexTransport, P as diagnoseOpenAICodex, Q as isFastModeSessionId, R as COMPATIBILITY_PACKAGES, S as OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, T as OPENAI_CODEX_TRANSPORT_API_VERSION, U as SUPPORTED_NODE_RANGE, V as PI_AI_PACKAGE, W as SUPPORTED_PI_AI_VERSION, X as OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, Y as FastModeRegistry, Z as OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, _ as decodeOpenAICodexSettings, a as name, at as logoutOpenAICodex, b as OPENAI_CODEX_IMAGE_MAX_COUNT, c as OPENAI_CODEX_SEARCH_URL, ct as OPENAI_CODEX_PROVIDER, 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, it as loginOpenAICodex, j as VIEW_IMAGE_TOOL_NAME, k as OpenAICodexTransportError, l as OpenAICodexSearchProvider, lt as OpenAICodexCredentialStore, m as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, n as OPENAI_CODEX_SETTINGS_NS, nt as parseOpenAICodexUsage, o as OPENAI_CODEX_BASE_URL, ot as openAICodexAuthStatus, p as DEFAULT_OPENAI_CODEX_SEARCH_MODE, q as evaluateCompatibility, r as apply, rt as readOpenAICodexRateLimits, s as OPENAI_CODEX_SEARCH_PROVIDER, st as OPENAI_CODEX_AUTH_FILENAME, t as Config, tt as OPENAI_CODEX_USAGE_URL, u as mapOpenAICodexSearchResponse, ut as openAICodexAuthPath, v as resolveOpenAICodexSettings, w as OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS, x as OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, y as OPENAI_CODEX_IMAGE_GENERATION_URL, z as COMPATIBILITY_SCHEMA_VERSION } from "./src-zOltFEBG.js";
2
- export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, Config, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, 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_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_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_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, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, resolveOpenAICodexSettings };
1
+ import { $ as OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, A as OPENAI_CODEX_TRANSPORT_SERVICE, B as COMPATIBILITY_CONTRACT, 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 SUPPORTED_DSH_PLUGIN_API_VERSION, H as COMPATIBILITY_SCHEMA_VERSION, I as assertNoOpenAICodexProviderConflict, J as assessCompatibility, K as SUPPORTED_NODE_RANGE, L as diagnoseOpenAICodex, M as OpenAICodexTransportError, N as isOpenAICodexTransportError, O as OPENAI_CODEX_TRANSPORT_API_VERSION, P as VIEW_IMAGE_TOOL_NAME, Q as FastModeRegistry, R as openAICodexConflictMessage, S as OPENAI_CODEX_IMAGE_GENERATION_URL, T as OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES, U as DSH_PLUGIN_API_PACKAGES, V as COMPATIBILITY_PACKAGES, W as PI_AI_PACKAGE, X as evaluateCompatibility, Y as detectCompatibility, Z as OPENAI_CODEX_FAST_MODE_PATH, _ as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, a as name, at as parseOpenAICodexUsage, b as decodeOpenAICodexSettings, c as migrateOpenAICodexSearchHistory, ct as logoutOpenAICodex, d as OPENAI_CODEX_SEARCH_URL, dt as OPENAI_CODEX_PROVIDER, et as OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, f as OpenAICodexSearchProvider, ft as OpenAICodexCredentialStore, g as DEFAULT_OPENAI_CODEX_SEARCH_MODE, h as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, i as inject, it as OPENAI_CODEX_USAGE_URL, j as OpenAICodexTransport, k as OPENAI_CODEX_TRANSPORT_ERROR_CODES, l as OPENAI_CODEX_BASE_URL, lt as openAICodexAuthStatus, m as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, n as OPENAI_CODEX_SETTINGS_NS, o as OPENAI_CODEX_HISTORY_BACKUP_SUFFIX, ot as readOpenAICodexRateLimits, p as mapOpenAICodexSearchResponse, pt as openAICodexAuthPath, q as SUPPORTED_PI_AI_VERSION, r as apply, s as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, st as loginOpenAICodex, t as Config, tt as isFastModeSessionId, u as OPENAI_CODEX_SEARCH_PROVIDER, ut as OPENAI_CODEX_AUTH_FILENAME, v as DEFAULT_OPENAI_CODEX_SETTINGS, w as OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES, x as resolveOpenAICodexSettings, y as OPENAI_CODEX_SETTINGS_NAMESPACE } from "./src-15cHQKm5.js";
2
+ export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, Config, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, 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_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, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, isFastModeSessionId, isOpenAICodexTransportError, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, migrateOpenAICodexSearchHistory, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, resolveOpenAICodexSettings };
@@ -1,11 +1,11 @@
1
- import { randomUUID } from "node:crypto";
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { deepEqualJson, installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
4
4
  import { createModels } from "@earendil-works/pi-ai";
5
5
  import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
6
6
  import { createUserMessage, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
7
7
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
8
- import { lstat, mkdir, readFile, rm, stat } from "node:fs/promises";
8
+ import { link, lstat, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
9
9
  import { basename, dirname, join, parse, resolve } from "node:path";
10
10
  import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
11
11
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
@@ -18,6 +18,8 @@ import { request as request$1 } from "node:https";
18
18
  import { BlockList, isIP } from "node:net";
19
19
  import { Service } from "@deepseek-ai/cordis";
20
20
  import { WebError } from "@deepseek-ai/dsh-web";
21
+ import { constants } from "node:fs";
22
+ import { constants as constants$1, zstdCompressSync, zstdDecompressSync } from "node:zlib";
21
23
  //#region src/store.ts
22
24
  /**
23
25
  * Owner-only persistent OAuth credential storage for the OpenAI Codex bundle.
@@ -1209,7 +1211,7 @@ async function detectCompatibility(options = {}) {
1209
1211
  }
1210
1212
  //#endregion
1211
1213
  //#region src/version.ts
1212
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.13";
1214
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.14";
1213
1215
  //#endregion
1214
1216
  //#region src/doctor.ts
1215
1217
  /** Secret-free diagnostics and duplicate-provider guidance. */
@@ -2471,6 +2473,268 @@ var OpenAICodexSearchProvider = class {
2471
2473
  }
2472
2474
  };
2473
2475
  //#endregion
2476
+ //#region src/history-migration.ts
2477
+ /** Offline compatibility migration for the private Codex search event emitted by Alpha 4.10. */
2478
+ /** Private event written by Alpha 4.10 before the provider stopped persisting it. */
2479
+ const OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT = "web/openai-codex-search-llm-request";
2480
+ /** Backup suffix created beside every changed session artifact. */
2481
+ const OPENAI_CODEX_HISTORY_BACKUP_SUFFIX = ".pre-codex-search-history-migration";
2482
+ const ZSTD_MAGIC = 4247762216;
2483
+ const CHECKSUM_OPTIONS = { params: { [constants$1.ZSTD_c_checksumFlag]: 1 } };
2484
+ const STABLE_READ_ATTEMPTS = 3;
2485
+ function scanZstdFrames(buffer) {
2486
+ const frames = [];
2487
+ let offset = 0;
2488
+ while (offset < buffer.length) {
2489
+ const start = offset;
2490
+ if (buffer.length - offset < 4) throw new Error(`incomplete Zstandard frame magic at byte ${offset}`);
2491
+ if (buffer.readUInt32LE(offset) !== ZSTD_MAGIC) throw new Error(`invalid Zstandard frame magic at byte ${offset}`);
2492
+ offset += 4;
2493
+ if (offset === buffer.length) throw new Error(`incomplete Zstandard frame descriptor at byte ${offset}`);
2494
+ const descriptor = buffer.readUInt8(offset);
2495
+ offset += 1;
2496
+ if ((descriptor & 24) !== 0) throw new Error(`reserved Zstandard frame-header bit at byte ${offset - 1}`);
2497
+ const contentSizeFlag = descriptor >>> 6;
2498
+ const singleSegment = (descriptor & 32) !== 0;
2499
+ const checksum = (descriptor & 4) !== 0;
2500
+ const dictionaryFlag = descriptor & 3;
2501
+ const dictionaryBytes = dictionaryFlag === 3 ? 4 : dictionaryFlag;
2502
+ const contentSizeBytes = contentSizeFlag === 0 ? singleSegment ? 1 : 0 : 1 << contentSizeFlag;
2503
+ const remainingHeaderBytes = (singleSegment ? 0 : 1) + dictionaryBytes + contentSizeBytes;
2504
+ if (buffer.length - offset < remainingHeaderBytes) throw new Error(`incomplete Zstandard frame header at byte ${start}`);
2505
+ offset += remainingHeaderBytes;
2506
+ for (;;) {
2507
+ if (buffer.length - offset < 3) throw new Error(`incomplete Zstandard block header at byte ${offset}`);
2508
+ const blockHeader = buffer.readUIntLE(offset, 3);
2509
+ offset += 3;
2510
+ const lastBlock = (blockHeader & 1) !== 0;
2511
+ const blockType = blockHeader >>> 1 & 3;
2512
+ const blockSize = blockHeader >>> 3;
2513
+ if (blockType === 3) throw new Error(`reserved Zstandard block type at byte ${offset - 3}`);
2514
+ const payloadBytes = blockType === 1 ? 1 : blockSize;
2515
+ if (buffer.length - offset < payloadBytes) throw new Error(`incomplete Zstandard block payload at byte ${offset}`);
2516
+ offset += payloadBytes;
2517
+ if (lastBlock) break;
2518
+ }
2519
+ if (checksum) {
2520
+ if (buffer.length - offset < 4) throw new Error(`incomplete Zstandard checksum at byte ${offset}`);
2521
+ offset += 4;
2522
+ }
2523
+ frames.push({
2524
+ start,
2525
+ end: offset
2526
+ });
2527
+ }
2528
+ return frames;
2529
+ }
2530
+ function markLegacyEventIgnorable(line) {
2531
+ let record;
2532
+ try {
2533
+ record = JSON.parse(line);
2534
+ } catch {
2535
+ return;
2536
+ }
2537
+ if (typeof record !== "object" || record === null || Array.isArray(record)) return void 0;
2538
+ const event = record;
2539
+ if (event["type"] !== "web/openai-codex-search-llm-request" || event["ignorable"] === true) return void 0;
2540
+ if (event["ignorable"] !== void 0) throw new Error(`legacy Codex search event seq ${String(event["seq"])} has an unexpected ignorable value`);
2541
+ let objectEnd = line.length - 1;
2542
+ while (objectEnd >= 0 && /\s/u.test(line[objectEnd] ?? "")) objectEnd -= 1;
2543
+ if (line[objectEnd] !== "}") throw new Error(`legacy Codex search event seq ${String(event["seq"])} is not a JSON object`);
2544
+ return `${line.slice(0, objectEnd)},"ignorable":true${line.slice(objectEnd)}`;
2545
+ }
2546
+ function rewriteFrame(frame) {
2547
+ const lines = zstdDecompressSync(frame).toString("utf8").split("\n");
2548
+ let changedEvents = 0;
2549
+ for (let index = 0; index < lines.length; index += 1) {
2550
+ const line = lines[index];
2551
+ if (line === void 0 || line.length === 0) continue;
2552
+ const migrated = markLegacyEventIgnorable(line);
2553
+ if (migrated === void 0) continue;
2554
+ lines[index] = migrated;
2555
+ changedEvents += 1;
2556
+ }
2557
+ if (changedEvents === 0) return {
2558
+ frame,
2559
+ changedEvents
2560
+ };
2561
+ return {
2562
+ frame: zstdCompressSync(Buffer.from(lines.join("\n")), CHECKSUM_OPTIONS),
2563
+ changedEvents
2564
+ };
2565
+ }
2566
+ function validateMigration(original, migrated, expectedChanges) {
2567
+ const beforeFrames = scanZstdFrames(original);
2568
+ const afterFrames = scanZstdFrames(migrated);
2569
+ if (beforeFrames.length !== afterFrames.length) throw new Error("Zstandard frame count changed during migration");
2570
+ let changes = 0;
2571
+ for (let index = 0; index < beforeFrames.length; index += 1) {
2572
+ const beforeRange = beforeFrames[index];
2573
+ const afterRange = afterFrames[index];
2574
+ if (beforeRange === void 0 || afterRange === void 0) throw new Error("missing Zstandard frame during validation");
2575
+ const beforeLines = zstdDecompressSync(original.subarray(beforeRange.start, beforeRange.end)).toString("utf8").split("\n");
2576
+ const afterLines = zstdDecompressSync(migrated.subarray(afterRange.start, afterRange.end)).toString("utf8").split("\n");
2577
+ if (beforeLines.length !== afterLines.length) throw new Error(`logical line count changed in frame ${index}`);
2578
+ for (let line = 0; line < beforeLines.length; line += 1) {
2579
+ if (beforeLines[line] === afterLines[line]) continue;
2580
+ const expected = markLegacyEventIgnorable(beforeLines[line] ?? "");
2581
+ if (expected === void 0) throw new Error(`non-target record changed in frame ${index}, line ${line + 1}`);
2582
+ if (afterLines[line] !== expected) throw new Error(`legacy event changed beyond its ignorable marker in frame ${index}, line ${line + 1}`);
2583
+ changes += 1;
2584
+ }
2585
+ }
2586
+ if (changes !== expectedChanges) throw new Error(`validated ${changes} changes, expected ${expectedChanges}`);
2587
+ }
2588
+ function revision(metadata) {
2589
+ return [
2590
+ metadata.dev,
2591
+ metadata.ino,
2592
+ metadata.size,
2593
+ metadata.mtimeNs,
2594
+ metadata.ctimeNs
2595
+ ].join(":");
2596
+ }
2597
+ async function readStableFile(path) {
2598
+ for (let attempt = 0; attempt < STABLE_READ_ATTEMPTS; attempt += 1) {
2599
+ const before = revision(await stat(path, { bigint: true }));
2600
+ const content = await readFile(path);
2601
+ if (before === revision(await stat(path, { bigint: true }))) return content;
2602
+ }
2603
+ throw new Error(`session kept changing during ${STABLE_READ_ATTEMPTS} stable-read attempts: ${path}`);
2604
+ }
2605
+ function renderMigration(original) {
2606
+ const frames = scanZstdFrames(original);
2607
+ const output = [];
2608
+ let changedEvents = 0;
2609
+ for (const frame of frames) {
2610
+ const rewritten = rewriteFrame(original.subarray(frame.start, frame.end));
2611
+ output.push(rewritten.frame);
2612
+ changedEvents += rewritten.changedEvents;
2613
+ }
2614
+ const migrated = Buffer.concat(output);
2615
+ if (changedEvents > 0) validateMigration(original, migrated, changedEvents);
2616
+ return {
2617
+ migrated,
2618
+ changedEvents
2619
+ };
2620
+ }
2621
+ async function* sessionArtifacts(root) {
2622
+ const pending = [root];
2623
+ while (pending.length > 0) {
2624
+ const current = pending.pop();
2625
+ if (current === void 0) continue;
2626
+ let entries;
2627
+ try {
2628
+ entries = await readdir(current, { withFileTypes: true });
2629
+ } catch (error) {
2630
+ if (error.code === "ENOENT") continue;
2631
+ throw error;
2632
+ }
2633
+ for (const entry of entries) {
2634
+ const path = join(current, entry.name);
2635
+ if (entry.isDirectory()) pending.push(path);
2636
+ else if (entry.isFile() && entry.name === "session.jsonl.zstd") yield path;
2637
+ }
2638
+ }
2639
+ }
2640
+ async function syncParentDirectory(path) {
2641
+ const directory = await open(dirname(path), "r");
2642
+ try {
2643
+ await directory.sync();
2644
+ } finally {
2645
+ await directory.close();
2646
+ }
2647
+ }
2648
+ async function migrateArtifact(path, apply) {
2649
+ if (!apply) {
2650
+ const { changedEvents } = renderMigration(await readStableFile(path));
2651
+ return changedEvents === 0 ? void 0 : {
2652
+ path,
2653
+ changedEvents
2654
+ };
2655
+ }
2656
+ return withFileLock(path, async () => {
2657
+ const original = await readStableFile(path);
2658
+ const { migrated, changedEvents } = renderMigration(original);
2659
+ if (changedEvents === 0) return void 0;
2660
+ const metadata = await stat(path);
2661
+ const sourceIdentity = await stat(path, { bigint: true });
2662
+ const backupPath = path + OPENAI_CODEX_HISTORY_BACKUP_SUFFIX;
2663
+ try {
2664
+ await link(path, backupPath);
2665
+ } catch (error) {
2666
+ if (error.code !== "EEXIST") throw error;
2667
+ }
2668
+ const backupHandle = await open(backupPath, constants.O_RDONLY | constants.O_NOFOLLOW);
2669
+ try {
2670
+ const backupIdentity = await backupHandle.stat({ bigint: true });
2671
+ if (!backupIdentity.isFile()) throw new Error(`migration backup path is not a regular file: ${backupPath}`);
2672
+ if (backupIdentity.dev !== sourceIdentity.dev || backupIdentity.ino !== sourceIdentity.ino) throw new Error(`migration backup does not reference the current Session artifact: ${backupPath}`);
2673
+ if (!(await backupHandle.readFile()).equals(original)) throw new Error(`migration backup already exists with different content: ${backupPath}`);
2674
+ await backupHandle.sync();
2675
+ } finally {
2676
+ await backupHandle.close();
2677
+ }
2678
+ await syncParentDirectory(backupPath);
2679
+ const temporary = `${path}.codex-search-history-${randomBytes(6).toString("hex")}.tmp`;
2680
+ try {
2681
+ await writeFile(temporary, migrated, {
2682
+ flag: "wx",
2683
+ mode: metadata.mode & 511
2684
+ });
2685
+ const handle = await open(temporary, "r");
2686
+ try {
2687
+ await handle.sync();
2688
+ } finally {
2689
+ await handle.close();
2690
+ }
2691
+ if (!(await readStableFile(path)).equals(original)) throw new Error(`session changed while migration was prepared: ${path}`);
2692
+ await rename(temporary, path);
2693
+ try {
2694
+ await syncParentDirectory(path);
2695
+ } catch (error) {
2696
+ const message = error instanceof Error ? error.message : String(error);
2697
+ throw new Error(`session was repaired and backed up at ${backupPath}, but synchronizing its directory failed: ${message}`, { cause: error });
2698
+ }
2699
+ if (!(await readStableFile(backupPath)).equals(original)) throw new Error(`a Session writer changed the preserved backup during migration; stop DSH and restore ${backupPath}`);
2700
+ } finally {
2701
+ await rm(temporary, { force: true });
2702
+ }
2703
+ return {
2704
+ path,
2705
+ changedEvents,
2706
+ backupPath
2707
+ };
2708
+ });
2709
+ }
2710
+ /**
2711
+ * Mark the retired Alpha 4.10 search event ignorable in compressed JSONL logs.
2712
+ * Applying is an offline maintenance operation and fails closed without the
2713
+ * caller's explicit acknowledgement that all DSH writers are stopped.
2714
+ */
2715
+ async function migrateOpenAICodexSearchHistory(options = {}) {
2716
+ const apply = options.apply === true;
2717
+ if (apply && options.confirmStopped !== true) throw new Error("refusing to rewrite Session history without confirmStopped=true after stopping every DSH writer");
2718
+ if (apply && process.platform === "win32") throw new Error("applying this history migration is not supported on Windows; dry-run only");
2719
+ const root = resolve(options.root ?? join(resolveDshHome(options.dshHome), "sessions"));
2720
+ const files = [];
2721
+ for await (const path of sessionArtifacts(root)) try {
2722
+ const result = await migrateArtifact(path, apply);
2723
+ if (result !== void 0) files.push(result);
2724
+ } catch (error) {
2725
+ const partial = apply && files.length > 0 ? `; ${files.length} earlier file(s) were already repaired and remain backed up` : "";
2726
+ const message = error instanceof Error ? error.message : String(error);
2727
+ throw new Error(`Codex search history migration failed at ${path}${partial}: ${message}`, { cause: error });
2728
+ }
2729
+ return {
2730
+ mode: apply ? "apply" : "dry-run",
2731
+ root,
2732
+ changedFiles: files.length,
2733
+ changedEvents: files.reduce((sum, file) => sum + file.changedEvents, 0),
2734
+ files
2735
+ };
2736
+ }
2737
+ //#endregion
2474
2738
  //#region src/index.ts
2475
2739
  /** Stable Cordis plugin name. */
2476
2740
  const name = "llm-openai-codex";
@@ -2637,4 +2901,4 @@ function apply(ctx, config) {
2637
2901
  scheduleCapabilities();
2638
2902
  }
2639
2903
  //#endregion
2640
- export { OpenAICodexTrustedOriginsStore as $, isOpenAICodexTransportError as A, DSH_PLUGIN_API_PACKAGES as B, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH as C, OPENAI_CODEX_TRANSPORT_SERVICE as D, OPENAI_CODEX_TRANSPORT_ERROR_CODES as E, openAICodexConflictMessage as F, assessCompatibility as G, SUPPORTED_DSH_PLUGIN_API_VERSION as H, CODEX_CONNECT_VERSION as I, OPENAI_CODEX_FAST_MODE_PATH as J, detectCompatibility as K, COMPATIBILITY_CONTRACT as L, IMAGE_GENERATE_TOOL_NAME as M, assertNoOpenAICodexProviderConflict as N, OpenAICodexTransport as O, diagnoseOpenAICodex as P, isFastModeSessionId as Q, COMPATIBILITY_PACKAGES as R, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES as S, OPENAI_CODEX_TRANSPORT_API_VERSION as T, SUPPORTED_NODE_RANGE as U, PI_AI_PACKAGE as V, SUPPORTED_PI_AI_VERSION as W, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS as X, FastModeRegistry as Y, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH as Z, decodeOpenAICodexSettings as _, name as a, logoutOpenAICodex as at, OPENAI_CODEX_IMAGE_MAX_COUNT as b, OPENAI_CODEX_SEARCH_URL as c, OPENAI_CODEX_PROVIDER as ct, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as d, normalizeTrustedOrigin as et, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS as f, OPENAI_CODEX_SETTINGS_NAMESPACE as g, DEFAULT_OPENAI_CODEX_SETTINGS as h, inject as i, loginOpenAICodex as it, VIEW_IMAGE_TOOL_NAME as j, OpenAICodexTransportError as k, OpenAICodexSearchProvider as l, OpenAICodexCredentialStore as lt, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as m, OPENAI_CODEX_SETTINGS_NS as n, parseOpenAICodexUsage as nt, OPENAI_CODEX_BASE_URL as o, openAICodexAuthStatus as ot, DEFAULT_OPENAI_CODEX_SEARCH_MODE as p, evaluateCompatibility as q, apply as r, readOpenAICodexRateLimits as rt, OPENAI_CODEX_SEARCH_PROVIDER as s, OPENAI_CODEX_AUTH_FILENAME as st, Config as t, OPENAI_CODEX_USAGE_URL as tt, mapOpenAICodexSearchResponse as u, openAICodexAuthPath as ut, resolveOpenAICodexSettings as v, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS as w, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES as x, OPENAI_CODEX_IMAGE_GENERATION_URL as y, COMPATIBILITY_SCHEMA_VERSION as z };
2904
+ export { OPENAI_CODEX_FAST_MODE_MAX_SESSIONS as $, OPENAI_CODEX_TRANSPORT_SERVICE as A, COMPATIBILITY_CONTRACT as B, OPENAI_CODEX_IMAGE_MAX_COUNT as C, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS as D, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH as E, IMAGE_GENERATE_TOOL_NAME as F, SUPPORTED_DSH_PLUGIN_API_VERSION as G, COMPATIBILITY_SCHEMA_VERSION as H, assertNoOpenAICodexProviderConflict as I, assessCompatibility as J, SUPPORTED_NODE_RANGE as K, diagnoseOpenAICodex as L, OpenAICodexTransportError as M, isOpenAICodexTransportError as N, OPENAI_CODEX_TRANSPORT_API_VERSION as O, VIEW_IMAGE_TOOL_NAME as P, FastModeRegistry as Q, openAICodexConflictMessage as R, OPENAI_CODEX_IMAGE_GENERATION_URL as S, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES as T, DSH_PLUGIN_API_PACKAGES as U, COMPATIBILITY_PACKAGES as V, PI_AI_PACKAGE as W, evaluateCompatibility as X, detectCompatibility as Y, OPENAI_CODEX_FAST_MODE_PATH as Z, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as _, name as a, parseOpenAICodexUsage as at, decodeOpenAICodexSettings as b, migrateOpenAICodexSearchHistory as c, logoutOpenAICodex as ct, OPENAI_CODEX_SEARCH_URL as d, OPENAI_CODEX_PROVIDER as dt, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH as et, OpenAICodexSearchProvider as f, OpenAICodexCredentialStore as ft, DEFAULT_OPENAI_CODEX_SEARCH_MODE as g, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS as h, inject as i, OPENAI_CODEX_USAGE_URL as it, OpenAICodexTransport as j, OPENAI_CODEX_TRANSPORT_ERROR_CODES as k, OPENAI_CODEX_BASE_URL as l, openAICodexAuthStatus as lt, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as m, OPENAI_CODEX_SETTINGS_NS as n, OpenAICodexTrustedOriginsStore as nt, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX as o, readOpenAICodexRateLimits as ot, mapOpenAICodexSearchResponse as p, openAICodexAuthPath as pt, SUPPORTED_PI_AI_VERSION as q, apply as r, normalizeTrustedOrigin as rt, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT as s, loginOpenAICodex as st, Config as t, isFastModeSessionId as tt, OPENAI_CODEX_SEARCH_PROVIDER as u, OPENAI_CODEX_AUTH_FILENAME as ut, DEFAULT_OPENAI_CODEX_SETTINGS as v, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES as w, resolveOpenAICodexSettings as x, OPENAI_CODEX_SETTINGS_NAMESPACE as y, CODEX_CONNECT_VERSION as z };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-codex-connect",
3
3
  "displayName": "Codex Connect",
4
4
  "description": "ChatGPT OAuth and Codex models for DeepSeek Harness.",
5
- "version": "0.1.0-alpha.4.13",
5
+ "version": "0.1.0-alpha.4.14",
6
6
  "author": "Frank Song",
7
7
  "contributors": [
8
8
  "Yan-Zero (original dsh-codex author)"