dsh-codex-connect 0.1.0-alpha.4.8 → 0.1.0-alpha.4.9

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.6`, `@earendi
21
21
  dsh plugin --profile web add dsh-codex-connect@alpha
22
22
  ```
23
23
 
24
- After `0.1.0-alpha.4.8` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.8`. If npm is unavailable after its matching GitHub prerelease is created, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.8'`.
24
+ After `0.1.0-alpha.4.9` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.9`. 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.9'`.
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.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.8`. If npm is unavailable after the matching GitHub prerelease exists, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.8'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
31
+ To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.9`. 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.9'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
32
32
 
33
33
  ### 2. Start Harness
34
34
 
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.8`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.8'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
31
+ 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.9`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.9'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
32
32
 
33
33
  ### 2. 启动 Harness
34
34
 
package/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { E as CODEX_CONNECT_VERSION, G as openAICodexAuthStatus, R as OpenAICodexTrustedOriginsStore, U as loginOpenAICodex, W as logoutOpenAICodex, Y as openAICodexAuthPath, w as diagnoseOpenAICodex, z as normalizeTrustedOrigin } from "./src-DztMY1zT.js";
2
+ import { E as CODEX_CONNECT_VERSION, J as loginOpenAICodex, U as OpenAICodexTrustedOriginsStore, W as normalizeTrustedOrigin, X as openAICodexAuthStatus, Y as logoutOpenAICodex, et as openAICodexAuthPath, w as diagnoseOpenAICodex } from "./src-DaDIQ3_t.js";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { spawn } from "node:child_process";
5
5
  import { realpathSync } from "node:fs";
package/lib/client.js CHANGED
@@ -18,12 +18,12 @@ window.__ModuleLoader__.load({
18
18
  searchContextSize: "medium",
19
19
  searchMaxOutputTokens: 1e4
20
20
  });
21
- function isRecord(value) {
21
+ function isRecord$2(value) {
22
22
  return typeof value === "object" && value !== null && !Array.isArray(value);
23
23
  }
24
24
  /** Narrow the redacted settings wire payload before it enters React state. */
25
25
  function decodeOpenAICodexSettings(value) {
26
- if (!isRecord(value)) return void 0;
26
+ if (!isRecord$2(value)) return void 0;
27
27
  const enableSearch = value["enableSearch"];
28
28
  const enableImageTool = value["enableImageTool"];
29
29
  const searchModel = value["searchModel"];
@@ -463,7 +463,7 @@ window.__ModuleLoader__.load({
463
463
  //#region src/client/OpenAICodexSettings.tsx
464
464
  /** Plugin-owned OpenAI Codex account controls used inside Plugin configuration. */
465
465
  const POLL_INTERVAL_MS = 1e3;
466
- const USAGE_POLL_INTERVAL_MS = 6e4;
466
+ const USAGE_POLL_INTERVAL_MS$1 = 6e4;
467
467
  const pageStyle = {
468
468
  display: "flex",
469
469
  flexDirection: "column",
@@ -600,11 +600,21 @@ window.__ModuleLoader__.load({
600
600
  const hours = seconds / 3600;
601
601
  return Number.isInteger(hours) ? t("hourLimit", { count: hours }) : t("usageWindow");
602
602
  }
603
- function formatPercent(percent) {
603
+ function formatPercent$1(percent) {
604
604
  return new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(percent);
605
605
  }
606
+ /** Format a server-declared Unix-second reset in the user's local timezone. */
607
+ function formatOpenAICodexResetAt(resetAt) {
608
+ if (resetAt === void 0 || !Number.isSafeInteger(resetAt) || resetAt <= 0) return void 0;
609
+ const date = /* @__PURE__ */ new Date(resetAt * 1e3);
610
+ if (!Number.isFinite(date.getTime())) return void 0;
611
+ return new Intl.DateTimeFormat(void 0, {
612
+ dateStyle: "medium",
613
+ timeStyle: "short"
614
+ }).format(date);
615
+ }
606
616
  function QuotaBar({ label, percent, detail, t }) {
607
- const display = formatPercent(percent);
617
+ const display = formatPercent$1(percent);
608
618
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
609
619
  style: quotaGroupStyle,
610
620
  children: [
@@ -646,6 +656,7 @@ window.__ModuleLoader__.load({
646
656
  }), limit.windows.map((window) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaBar, {
647
657
  label: windowLabel(window.windowSeconds, t),
648
658
  percent: window.remainingPercent,
659
+ detail: t("resetAt", { time: formatOpenAICodexResetAt(window.resetAt) ?? t("resetUnavailable") }),
649
660
  t
650
661
  }, window.windowSeconds))]
651
662
  }, limit.id)),
@@ -738,7 +749,7 @@ window.__ModuleLoader__.load({
738
749
  };
739
750
  }, [refresh]);
740
751
  (0, react.useEffect)(() => {
741
- const interval = status.status === "signing-in" ? POLL_INTERVAL_MS : status.status === "signed-in" ? USAGE_POLL_INTERVAL_MS : void 0;
752
+ const interval = status.status === "signing-in" ? POLL_INTERVAL_MS : status.status === "signed-in" ? USAGE_POLL_INTERVAL_MS$1 : void 0;
742
753
  if (interval === void 0) return;
743
754
  const controller = new AbortController();
744
755
  const timer = window.setInterval(() => {
@@ -944,14 +955,34 @@ window.__ModuleLoader__.load({
944
955
  };
945
956
  const chevronStyle = {
946
957
  flex: "0 0 auto",
947
- fontSize: 18,
948
- lineHeight: 1,
949
- transition: "transform 120ms ease"
958
+ color: "var(--dsw-alias-label-tertiary)",
959
+ transition: "transform 160ms ease"
950
960
  };
951
961
  const cardBodyStyle = {
952
962
  borderTop: "1px solid var(--dsw-alias-border-l2)",
953
963
  padding: "16px 14px 18px"
954
964
  };
965
+ /** The same 14px outline glyph used by DSH's native PluginCard. */
966
+ function NativeChevronDown({ open }) {
967
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
968
+ "aria-hidden": "true",
969
+ style: {
970
+ ...chevronStyle,
971
+ transform: open ? "rotate(180deg)" : "none"
972
+ },
973
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
974
+ width: "14",
975
+ height: "14",
976
+ viewBox: "0 0 14 14",
977
+ fill: "none",
978
+ xmlns: "http://www.w3.org/2000/svg",
979
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
980
+ d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
981
+ fill: "currentColor"
982
+ })
983
+ })
984
+ });
985
+ }
955
986
  /** Render account management as one expandable Plugin configuration card. */
956
987
  function OpenAICodexPluginCard({ t, configScope }) {
957
988
  if (t === void 0) throw new Error("OpenAI Codex plugin card requires its translation function");
@@ -976,14 +1007,7 @@ window.__ModuleLoader__.load({
976
1007
  style: descriptionStyle,
977
1008
  children: t("intro")
978
1009
  })]
979
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
980
- "aria-hidden": "true",
981
- style: {
982
- ...chevronStyle,
983
- transform: open ? "rotate(180deg)" : "none"
984
- },
985
- children: "⌄"
986
- })]
1010
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NativeChevronDown, { open })]
987
1011
  }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
988
1012
  style: cardBodyStyle,
989
1013
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenAICodexSettings, {
@@ -995,6 +1019,424 @@ window.__ModuleLoader__.load({
995
1019
  });
996
1020
  }
997
1021
  //#endregion
1022
+ //#region src/client/OpenAICodexQuotaIndicator.tsx
1023
+ /** Compact weekly Codex quota indicator for the Composer tool row. */
1024
+ const WEEK_SECONDS = 604800;
1025
+ const USAGE_POLL_INTERVAL_MS = 6e4;
1026
+ const CODEX_PROVIDER$1 = "openai-codex";
1027
+ const SPARK_MODEL = "gpt-5.3-codex-spark";
1028
+ const SPARK_QUOTA_ID = "codex_bengalfox";
1029
+ function isRecord$1(value) {
1030
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1031
+ }
1032
+ function isWindow(value) {
1033
+ if (!isRecord$1(value)) return false;
1034
+ const remainingPercent = value["remainingPercent"];
1035
+ const windowSeconds = value["windowSeconds"];
1036
+ const resetAt = value["resetAt"];
1037
+ return typeof remainingPercent === "number" && Number.isFinite(remainingPercent) && remainingPercent >= 0 && remainingPercent <= 100 && typeof windowSeconds === "number" && Number.isSafeInteger(windowSeconds) && windowSeconds > 0 && (resetAt === void 0 || typeof resetAt === "number" && Number.isSafeInteger(resetAt) && resetAt > 0 && Number.isFinite((/* @__PURE__ */ new Date(resetAt * 1e3)).getTime()));
1038
+ }
1039
+ function usageFromStatus(value) {
1040
+ if (!isRecord$1(value) || value["status"] !== "signed-in") return void 0;
1041
+ const usage = value["usage"];
1042
+ if (!isRecord$1(usage) || !Array.isArray(usage["rateLimits"])) return void 0;
1043
+ const rateLimits = usage["rateLimits"];
1044
+ for (const limit of rateLimits) {
1045
+ if (!isRecord$1(limit) || typeof limit["id"] !== "string" || !Array.isArray(limit["windows"])) return void 0;
1046
+ if (!limit["windows"].every(isWindow)) return void 0;
1047
+ }
1048
+ return usage;
1049
+ }
1050
+ function weeklyQuotaOf(usage, model) {
1051
+ const quotaId = model === SPARK_MODEL ? SPARK_QUOTA_ID : "codex";
1052
+ return usage.rateLimits.find((limit) => limit.id === quotaId)?.windows.find((window) => window.windowSeconds === WEEK_SECONDS);
1053
+ }
1054
+ function isGptModel(state) {
1055
+ const current = state.current;
1056
+ return state.status === "ready" && current?.provider === CODEX_PROVIDER$1 && typeof current.model === "string" && current.model.toLowerCase().startsWith("gpt-");
1057
+ }
1058
+ function formatPercent(percent) {
1059
+ return new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(percent);
1060
+ }
1061
+ const QUOTA_PROGRESS_WIDTH_PX = 48;
1062
+ const QUOTA_PROGRESS_TRACK_HEIGHT_PX = 6;
1063
+ function boundedQuotaPercent(remainingPercent) {
1064
+ return Math.min(100, Math.max(0, remainingPercent));
1065
+ }
1066
+ function quotaProgressColor(remainingPercent) {
1067
+ const bounded = boundedQuotaPercent(remainingPercent);
1068
+ if (bounded >= 60) return {
1069
+ name: "green",
1070
+ value: "var(--dsw-alias-state-success-primary, #22c55e)"
1071
+ };
1072
+ if (bounded >= 40) return {
1073
+ name: "yellow",
1074
+ value: "var(--dsw-alias-state-warn-primary, #eab308)"
1075
+ };
1076
+ if (bounded >= 20) return {
1077
+ name: "orange",
1078
+ value: "#f97316"
1079
+ };
1080
+ return {
1081
+ name: "red",
1082
+ value: "var(--dsw-alias-state-error-primary, #ef4444)"
1083
+ };
1084
+ }
1085
+ function subscribeDirectory$1(directory, listener) {
1086
+ return directory.subscribe(listener);
1087
+ }
1088
+ /** Render nothing until an eligible GPT Codex session has a usable weekly quota. */
1089
+ function OpenAICodexQuotaIndicator({ directory, t }) {
1090
+ const directoryState = (0, react.useSyncExternalStore)((listener) => subscribeDirectory$1(directory, listener), () => directory.getSnapshot(), () => directory.getSnapshot());
1091
+ const eligible = isGptModel(directoryState);
1092
+ const [request, setRequest] = (0, react.useState)({ status: "loading" });
1093
+ const [isHovered, setIsHovered] = (0, react.useState)(false);
1094
+ const [isFocused, setIsFocused] = (0, react.useState)(false);
1095
+ const tooltipId = (0, react.useId)();
1096
+ (0, react.useEffect)(() => {
1097
+ if (!eligible) {
1098
+ setRequest({ status: "hidden" });
1099
+ return;
1100
+ }
1101
+ const controller = new AbortController();
1102
+ let inFlight = false;
1103
+ let disposed = false;
1104
+ const refresh = async () => {
1105
+ if (inFlight || disposed) return;
1106
+ inFlight = true;
1107
+ try {
1108
+ const response = await fetch(OPENAI_CODEX_AUTH_STATUS_PATH, {
1109
+ method: "GET",
1110
+ credentials: "same-origin",
1111
+ headers: { accept: "application/json" },
1112
+ signal: controller.signal
1113
+ });
1114
+ const value = await response.json().catch(() => void 0);
1115
+ const usage = response.ok ? usageFromStatus(value) : void 0;
1116
+ if (!disposed && !controller.signal.aborted) setRequest(usage === void 0 ? { status: "hidden" } : {
1117
+ status: "ready",
1118
+ usage
1119
+ });
1120
+ } catch {
1121
+ if (!disposed && !controller.signal.aborted) setRequest({ status: "hidden" });
1122
+ } finally {
1123
+ inFlight = false;
1124
+ }
1125
+ };
1126
+ setRequest({ status: "loading" });
1127
+ refresh();
1128
+ const timer = window.setInterval(() => {
1129
+ refresh();
1130
+ }, USAGE_POLL_INTERVAL_MS);
1131
+ return () => {
1132
+ disposed = true;
1133
+ window.clearInterval(timer);
1134
+ controller.abort();
1135
+ };
1136
+ }, [eligible]);
1137
+ if (!eligible || request.status !== "ready" || request.usage === void 0) return null;
1138
+ const weekly = weeklyQuotaOf(request.usage, directoryState.current?.model);
1139
+ if (weekly === void 0) return null;
1140
+ const summary = t("composerWeeklyQuotaSummary", {
1141
+ percent: formatPercent(weekly.remainingPercent),
1142
+ time: formatOpenAICodexResetAt(weekly.resetAt) ?? t("resetUnavailable")
1143
+ });
1144
+ const boundedPercent = boundedQuotaPercent(weekly.remainingPercent);
1145
+ const progressColor = quotaProgressColor(weekly.remainingPercent);
1146
+ const tooltipVisible = isHovered || isFocused;
1147
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1148
+ role: "status",
1149
+ "data-openai-codex-quota": "weekly",
1150
+ "aria-label": summary,
1151
+ "aria-describedby": tooltipVisible ? tooltipId : void 0,
1152
+ tabIndex: 0,
1153
+ onMouseEnter: () => {
1154
+ setIsHovered(true);
1155
+ },
1156
+ onMouseLeave: () => {
1157
+ setIsHovered(false);
1158
+ },
1159
+ onFocus: () => {
1160
+ setIsFocused(true);
1161
+ },
1162
+ onBlur: () => {
1163
+ setIsFocused(false);
1164
+ },
1165
+ style: {
1166
+ display: "inline-flex",
1167
+ width: `${QUOTA_PROGRESS_WIDTH_PX}px`,
1168
+ height: "28px",
1169
+ position: "relative",
1170
+ alignItems: "center",
1171
+ justifyContent: "center"
1172
+ },
1173
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1174
+ "aria-hidden": "true",
1175
+ "data-openai-codex-quota-track": "weekly",
1176
+ style: {
1177
+ display: "block",
1178
+ width: `${QUOTA_PROGRESS_WIDTH_PX}px`,
1179
+ height: `${QUOTA_PROGRESS_TRACK_HEIGHT_PX}px`,
1180
+ borderRadius: "999px",
1181
+ backgroundColor: "var(--dsw-alias-border-l2)",
1182
+ overflow: "hidden"
1183
+ },
1184
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1185
+ "aria-hidden": "true",
1186
+ "data-openai-codex-quota-progress": "weekly",
1187
+ "data-openai-codex-quota-color": progressColor.name,
1188
+ style: {
1189
+ display: "block",
1190
+ width: `${boundedPercent}%`,
1191
+ height: "100%",
1192
+ borderRadius: "inherit",
1193
+ backgroundColor: progressColor.value
1194
+ }
1195
+ })
1196
+ }), tooltipVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1197
+ id: tooltipId,
1198
+ role: "tooltip",
1199
+ "data-openai-codex-quota-tooltip": "weekly",
1200
+ style: {
1201
+ position: "absolute",
1202
+ bottom: "calc(100% + 6px)",
1203
+ left: "50%",
1204
+ transform: "translateX(-50%)",
1205
+ zIndex: 1e3,
1206
+ whiteSpace: "nowrap",
1207
+ pointerEvents: "none",
1208
+ padding: "4px 8px",
1209
+ borderRadius: "6px",
1210
+ backgroundColor: "var(--dsw-specific-tip, #1f2329)",
1211
+ color: "var(--dsw-alias-label-primary, #ffffff)",
1212
+ boxShadow: "var(--dsw-shadow-lv2, 0 4px 12px rgb(0 0 0 / 12%))",
1213
+ fontSize: "12px",
1214
+ lineHeight: "18px"
1215
+ },
1216
+ children: summary
1217
+ }) : null]
1218
+ });
1219
+ }
1220
+ //#endregion
1221
+ //#region src/fast-mode-paths.ts
1222
+ /** Node-free Fast Mode route constants shared by Host and browser halves. */
1223
+ /** GET/POST endpoint for one conversation's process-local Fast Mode state. */
1224
+ const OPENAI_CODEX_FAST_MODE_PATH = "/plugins/dsh-openai-codex/fast-mode";
1225
+ //#endregion
1226
+ //#region src/client/OpenAICodexFastModeToggle.tsx
1227
+ /** Per-conversation OpenAI Codex Fast Mode control for the Composer row. */
1228
+ const CODEX_PROVIDER = "openai-codex";
1229
+ const FAST_MODE_ACTIVE_COLOR = "#f97316";
1230
+ function isRecord(value) {
1231
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1232
+ }
1233
+ function readEnabled(value) {
1234
+ if (!isRecord(value) || typeof value["enabled"] !== "boolean") return void 0;
1235
+ return value["enabled"];
1236
+ }
1237
+ function isEligible(state) {
1238
+ const current = state.current;
1239
+ return state.status === "ready" && current?.provider === CODEX_PROVIDER && typeof current.model === "string" && current.model.startsWith("gpt-");
1240
+ }
1241
+ function subscribeDirectory(directory, listener) {
1242
+ return directory.subscribe(listener);
1243
+ }
1244
+ function requestUrl(sessionId) {
1245
+ return `${OPENAI_CODEX_FAST_MODE_PATH}?sessionId=${encodeURIComponent(sessionId)}`;
1246
+ }
1247
+ /**
1248
+ * Render a real SVG lightning button only for GPT models on the exact Codex
1249
+ * provider. Host state is read and written through the session-addressed
1250
+ * route; no global model slot or persistent settings are changed.
1251
+ */
1252
+ function OpenAICodexFastModeToggle({ directory, sessionId, t }) {
1253
+ const eligible = isEligible((0, react.useSyncExternalStore)((listener) => subscribeDirectory(directory, listener), () => directory.getSnapshot(), () => directory.getSnapshot()));
1254
+ const [state, setState] = (0, react.useState)({
1255
+ status: "loading",
1256
+ enabled: false
1257
+ });
1258
+ const [tooltipVisible, setTooltipVisible] = (0, react.useState)(false);
1259
+ const controllerRef = (0, react.useRef)(void 0);
1260
+ const tooltipId = (0, react.useId)();
1261
+ (0, react.useEffect)(() => () => {
1262
+ controllerRef.current?.abort();
1263
+ }, []);
1264
+ (0, react.useEffect)(() => {
1265
+ controllerRef.current?.abort();
1266
+ controllerRef.current = void 0;
1267
+ if (!eligible) {
1268
+ setState({
1269
+ status: "loading",
1270
+ enabled: false
1271
+ });
1272
+ return;
1273
+ }
1274
+ const controller = new AbortController();
1275
+ controllerRef.current = controller;
1276
+ let disposed = false;
1277
+ setState({
1278
+ status: "loading",
1279
+ enabled: false
1280
+ });
1281
+ (async () => {
1282
+ try {
1283
+ const response = await fetch(requestUrl(sessionId), {
1284
+ method: "GET",
1285
+ credentials: "same-origin",
1286
+ headers: { accept: "application/json" },
1287
+ signal: controller.signal
1288
+ });
1289
+ const enabled = response.ok ? readEnabled(await response.json().catch(() => void 0)) : void 0;
1290
+ if (!disposed && !controller.signal.aborted) setState(enabled === void 0 ? {
1291
+ status: "error",
1292
+ enabled: false
1293
+ } : {
1294
+ status: "ready",
1295
+ enabled
1296
+ });
1297
+ } catch {
1298
+ if (!disposed && !controller.signal.aborted) setState({
1299
+ status: "error",
1300
+ enabled: false
1301
+ });
1302
+ } finally {
1303
+ if (controllerRef.current === controller) controllerRef.current = void 0;
1304
+ }
1305
+ })();
1306
+ return () => {
1307
+ disposed = true;
1308
+ controller.abort();
1309
+ if (controllerRef.current === controller) controllerRef.current = void 0;
1310
+ };
1311
+ }, [eligible, sessionId]);
1312
+ if (!eligible) return null;
1313
+ const busy = state.status !== "ready";
1314
+ const title = state.status === "loading" ? t("fastModeLoadingTitle") : state.status === "error" ? t("fastModeUnavailableTitle") : state.enabled ? t("fastModeEnabledTitle") : t("fastModeDisabledTitle");
1315
+ const toggle = () => {
1316
+ if (state.status !== "ready" || busy) return;
1317
+ controllerRef.current?.abort();
1318
+ const controller = new AbortController();
1319
+ controllerRef.current = controller;
1320
+ const next = !state.enabled;
1321
+ setState((current) => ({
1322
+ ...current,
1323
+ status: "loading"
1324
+ }));
1325
+ (async () => {
1326
+ try {
1327
+ const response = await fetch(OPENAI_CODEX_FAST_MODE_PATH, {
1328
+ method: "POST",
1329
+ credentials: "same-origin",
1330
+ headers: {
1331
+ accept: "application/json",
1332
+ "content-type": "application/json"
1333
+ },
1334
+ body: JSON.stringify({
1335
+ sessionId,
1336
+ enabled: next
1337
+ }),
1338
+ signal: controller.signal
1339
+ });
1340
+ const enabled = response.ok ? readEnabled(await response.json().catch(() => void 0)) : void 0;
1341
+ if (!controller.signal.aborted) setState(enabled === void 0 ? {
1342
+ status: "error",
1343
+ enabled: state.enabled
1344
+ } : {
1345
+ status: "ready",
1346
+ enabled
1347
+ });
1348
+ } catch {
1349
+ if (!controller.signal.aborted) setState({
1350
+ status: "error",
1351
+ enabled: state.enabled
1352
+ });
1353
+ } finally {
1354
+ if (controllerRef.current === controller) controllerRef.current = void 0;
1355
+ }
1356
+ })();
1357
+ };
1358
+ const active = state.enabled;
1359
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1360
+ onMouseEnter: () => {
1361
+ setTooltipVisible(true);
1362
+ },
1363
+ onMouseLeave: () => {
1364
+ setTooltipVisible(false);
1365
+ },
1366
+ onFocus: () => {
1367
+ setTooltipVisible(true);
1368
+ },
1369
+ onBlur: () => {
1370
+ setTooltipVisible(false);
1371
+ },
1372
+ style: {
1373
+ display: "inline-flex",
1374
+ position: "relative",
1375
+ width: 30,
1376
+ height: 30
1377
+ },
1378
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1379
+ type: "button",
1380
+ "data-openai-codex-fast-mode": active ? "on" : "off",
1381
+ "aria-label": title,
1382
+ "aria-describedby": tooltipVisible ? tooltipId : void 0,
1383
+ "aria-pressed": active,
1384
+ "aria-busy": busy,
1385
+ disabled: busy,
1386
+ onClick: toggle,
1387
+ style: {
1388
+ display: "inline-flex",
1389
+ alignItems: "center",
1390
+ justifyContent: "center",
1391
+ width: 30,
1392
+ height: 30,
1393
+ padding: 0,
1394
+ border: 0,
1395
+ borderRadius: 8,
1396
+ background: "transparent",
1397
+ color: active ? FAST_MODE_ACTIVE_COLOR : "var(--dsw-alias-label-secondary)",
1398
+ cursor: busy ? "default" : "pointer",
1399
+ opacity: busy ? .6 : 1
1400
+ },
1401
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1402
+ width: "16",
1403
+ height: "16",
1404
+ viewBox: "0 0 24 24",
1405
+ "aria-hidden": "true",
1406
+ focusable: "false",
1407
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1408
+ "data-openai-codex-fast-mode-bolt": active ? "filled" : "outline",
1409
+ d: "M13.1 2.75 5.35 13.1h5.8l-.95 8.15 8.45-11.2h-5.9l.35-7.3Z",
1410
+ fill: active ? "currentColor" : "none",
1411
+ stroke: "currentColor",
1412
+ strokeWidth: "1.8",
1413
+ strokeLinejoin: "round"
1414
+ })
1415
+ })
1416
+ }), tooltipVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1417
+ id: tooltipId,
1418
+ role: "tooltip",
1419
+ style: {
1420
+ position: "absolute",
1421
+ left: "50%",
1422
+ bottom: "calc(100% + 8px)",
1423
+ zIndex: 1e3,
1424
+ transform: "translateX(-50%)",
1425
+ padding: "4px 8px",
1426
+ borderRadius: 6,
1427
+ background: "var(--dsw-specific-tip, #1f2329)",
1428
+ boxShadow: "var(--dsw-shadow-lv2)",
1429
+ color: "var(--dsw-alias-label-primary, #fff)",
1430
+ fontSize: 12,
1431
+ lineHeight: "18px",
1432
+ whiteSpace: "nowrap",
1433
+ pointerEvents: "none"
1434
+ },
1435
+ children: title
1436
+ })]
1437
+ });
1438
+ }
1439
+ //#endregion
998
1440
  //#region src/client/locales.ts
999
1441
  /** English copy for the OpenAI Codex Plugin configuration card. */
1000
1442
  const en = {
@@ -1020,6 +1462,14 @@ window.__ModuleLoader__.load({
1020
1462
  hourLimit: "{count}-hour limit",
1021
1463
  usageWindow: "Usage window",
1022
1464
  percentRemaining: "{percent}% remaining",
1465
+ resetAt: "Resets {time}",
1466
+ resetUnavailable: "Reset time unavailable",
1467
+ composerWeeklyQuota: "Codex weekly quota",
1468
+ composerWeeklyQuotaSummary: "Codex weekly quota: {percent}% remaining; resets {time}",
1469
+ fastModeEnabledTitle: "Current: 1.5× speed, with faster quota consumption. Click to switch to Standard speed.",
1470
+ fastModeDisabledTitle: "Current: Standard speed. Click to enable 1.5× speed.",
1471
+ fastModeLoadingTitle: "Fast Mode state is loading for this conversation.",
1472
+ fastModeUnavailableTitle: "Fast Mode is unavailable for this conversation.",
1023
1473
  monthlyLimit: "Monthly credit limit",
1024
1474
  exactRemaining: "{remaining} of {limit} credits remaining",
1025
1475
  credits: "Credits",
@@ -1085,6 +1535,14 @@ window.__ModuleLoader__.load({
1085
1535
  hourLimit: "{count} 小时额度",
1086
1536
  usageWindow: "使用额度",
1087
1537
  percentRemaining: "剩余 {percent}%",
1538
+ resetAt: "{time} 重置",
1539
+ resetUnavailable: "重置时间不可用",
1540
+ composerWeeklyQuota: "Codex 周额度",
1541
+ composerWeeklyQuotaSummary: "Codex 周额度:剩余 {percent}%;重置时间 {time}",
1542
+ fastModeEnabledTitle: "当前:1.5 倍速度,额度消耗更快。点击切换到标准速度",
1543
+ fastModeDisabledTitle: "当前:标准速度。点击开启 1.5 倍速度",
1544
+ fastModeLoadingTitle: "正在加载此对话的 Fast Mode 状态。",
1545
+ fastModeUnavailableTitle: "此对话暂时无法使用 Fast Mode。",
1088
1546
  monthlyLimit: "每月信用额度",
1089
1547
  exactRemaining: "剩余 {remaining} / {limit} credits",
1090
1548
  credits: "Credits",
@@ -1159,6 +1617,22 @@ window.__ModuleLoader__.load({
1159
1617
  configScope
1160
1618
  })
1161
1619
  }, OpenAICodexPluginCard));
1620
+ ctx.inject(["slots", "modelDirectories"], (scope) => {
1621
+ scope.slots.inject("conversation.input.right", () => scope.slots.register({
1622
+ name: "conversation.input.right",
1623
+ id: "openai-codex-fast-mode",
1624
+ order: 10,
1625
+ locale: namespace,
1626
+ inject: (sessionId) => ({ directory: scope.modelDirectories.directoryFor(sessionId).store })
1627
+ }, OpenAICodexFastModeToggle));
1628
+ scope.slots.inject("conversation.input.right", () => scope.slots.register({
1629
+ name: "conversation.input.right",
1630
+ id: "openai-codex-quota",
1631
+ order: 20,
1632
+ locale: namespace,
1633
+ inject: (sessionId) => ({ directory: scope.modelDirectories.directoryFor(sessionId).store })
1634
+ }, OpenAICodexQuotaIndicator));
1635
+ });
1162
1636
  }
1163
1637
  //#endregion
1164
1638
  exports.apply = apply;
package/lib/index.d.ts CHANGED
@@ -153,6 +153,8 @@ interface OpenAICodexRateLimitWindow {
153
153
  readonly remainingPercent: number;
154
154
  /** Server-declared rolling-window length in seconds. */
155
155
  readonly windowSeconds: number;
156
+ /** Server-declared reset time as Unix seconds, when supplied and valid. */
157
+ readonly resetAt?: number;
156
158
  }
157
159
  /** One separately metered Codex quota bucket. */
158
160
  interface OpenAICodexRateLimit {
@@ -363,6 +365,49 @@ declare function logoutOpenAICodex(store?: OpenAICodexCredentialStore): Promise<
363
365
  */
364
366
  declare function openAICodexAuthStatus(store?: OpenAICodexCredentialStore): Promise<OpenAICodexAuthStatus>;
365
367
  //#endregion
368
+ //#region src/fast-mode.d.ts
369
+ /** Process-local, per-session OpenAI Codex Fast Mode state. */
370
+ /** Maximum number of enabled sessions retained by one plugin instance. */
371
+ declare const OPENAI_CODEX_FAST_MODE_MAX_SESSIONS = 256;
372
+ /** Maximum UTF-16 code units accepted for an opaque DSH session id. */
373
+ declare const OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH = 256;
374
+ /**
375
+ * Validate the opaque session identity used by the Fast Mode registry.
376
+ *
377
+ * The registry deliberately does not interpret or normalize session ids. It
378
+ * only rejects values that cannot safely serve as a bounded map key.
379
+ */
380
+ declare function isFastModeSessionId(value: unknown): value is string;
381
+ /**
382
+ * In-memory Fast Mode registry. Entries are positive-only: disabling a
383
+ * session removes its key, and an insertion over the bound evicts the least
384
+ * recently touched key. A new plugin instance starts with an empty map.
385
+ */
386
+ declare class FastModeRegistry {
387
+ private readonly maxSessions;
388
+ private readonly enabledSessions;
389
+ constructor(maxSessions?: number);
390
+ /** Number of currently enabled sessions. */
391
+ get size(): number;
392
+ /** Read one session without exposing the map or any credential state. */
393
+ isEnabled(sessionId: unknown): boolean;
394
+ /** Alias useful to callers that model this as a boolean setting. */
395
+ get(sessionId: unknown): boolean;
396
+ /** Enable or disable exactly one opaque session id. */
397
+ set(sessionId: unknown, enabled: boolean): void;
398
+ /** Explicitly named alias for callers that avoid boolean-setting verbs. */
399
+ setEnabled(sessionId: unknown, enabled: boolean): void;
400
+ /** Disable one session and forget its key. */
401
+ delete(sessionId: unknown): void;
402
+ /** Remove all process-local state during an explicit lifecycle teardown. */
403
+ clear(): void;
404
+ }
405
+ //#endregion
406
+ //#region src/fast-mode-paths.d.ts
407
+ /** Node-free Fast Mode route constants shared by Host and browser halves. */
408
+ /** GET/POST endpoint for one conversation's process-local Fast Mode state. */
409
+ declare const OPENAI_CODEX_FAST_MODE_PATH = "/plugins/dsh-openai-codex/fast-mode";
410
+ //#endregion
366
411
  //#region src/index.d.ts
367
412
  /** Stable Cordis plugin name. */
368
413
  declare const name = "llm-openai-codex";
@@ -395,4 +440,4 @@ declare const Config: z<Config>;
395
440
  */
396
441
  declare function apply(ctx: Context, config: Config): void;
397
442
  //#endregion
398
- export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, type CompatibilityDetectionOptions, type CompatibilityEntry, type CompatibilityEvaluationInput, type CompatibilityPackageName, type CompatibilityReport, type CompatibilityStatus, Config, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexIndividualLimit, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, type OpenAICodexUsage, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, installOpenAICodexSearchEvent, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
443
+ 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, 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_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, type OpenAICodexAuthStatus, OpenAICodexCredentialStore, type OpenAICodexCredits, type OpenAICodexDiagnosticOptions, type OpenAICodexDiagnosticReport, type OpenAICodexIndividualLimit, type OpenAICodexRateLimit, type OpenAICodexRateLimitWindow, type OpenAICodexSearchContextSize, type OpenAICodexSearchMode, OpenAICodexSearchProvider, type OpenAICodexSearchProviderOptions, type OpenAICodexSearchRequestRecord, type OpenAICodexSettingsConfig, type OpenAICodexUsage, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, installOpenAICodexSearchEvent, isFastModeSessionId, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as DSH_PLUGIN_API_PACKAGES, B as OPENAI_CODEX_USAGE_URL, C as assertNoOpenAICodexProviderConflict, D as COMPATIBILITY_CONTRACT, F as assessCompatibility, G as openAICodexAuthStatus, H as readOpenAICodexRateLimits, I as detectCompatibility, J as OpenAICodexCredentialStore, K as OPENAI_CODEX_AUTH_FILENAME, L as evaluateCompatibility, M as SUPPORTED_DSH_PLUGIN_API_VERSION, N as SUPPORTED_NODE_RANGE, O as COMPATIBILITY_PACKAGES, P as SUPPORTED_PI_AI_VERSION, S as VIEW_IMAGE_TOOL_NAME, T as openAICodexConflictMessage, U as loginOpenAICodex, V as parseOpenAICodexUsage, W as logoutOpenAICodex, Y as openAICodexAuthPath, _ as decodeOpenAICodexSettings, a as name, b as installOpenAICodexSearchEvent, c as OPENAI_CODEX_SEARCH_URL, d as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, f as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, g as OPENAI_CODEX_SETTINGS_NAMESPACE, h as DEFAULT_OPENAI_CODEX_SETTINGS, i as inject, j as PI_AI_PACKAGE, k as COMPATIBILITY_SCHEMA_VERSION, l as OpenAICodexSearchProvider, m as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, n as OPENAI_CODEX_SETTINGS_NS, o as OPENAI_CODEX_BASE_URL, p as DEFAULT_OPENAI_CODEX_SEARCH_MODE, q as OPENAI_CODEX_PROVIDER, r as apply, s as OPENAI_CODEX_SEARCH_PROVIDER, t as Config, u as mapOpenAICodexSearchResponse, v as resolveOpenAICodexSettings, w as diagnoseOpenAICodex, x as recordOpenAICodexSearchRequest, y as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT } from "./src-DztMY1zT.js";
2
- export { COMPATIBILITY_CONTRACT, COMPATIBILITY_PACKAGES, COMPATIBILITY_SCHEMA_VERSION, Config, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, DEFAULT_OPENAI_CODEX_SEARCH_MODE, DEFAULT_OPENAI_CODEX_SEARCH_MODEL, DEFAULT_OPENAI_CODEX_SETTINGS, DSH_PLUGIN_API_PACKAGES, OPENAI_CODEX_AUTH_FILENAME, OPENAI_CODEX_BASE_URL, OPENAI_CODEX_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, OpenAICodexCredentialStore, OpenAICodexSearchProvider, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, installOpenAICodexSearchEvent, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
1
+ import { $ as OpenAICodexCredentialStore, A as DSH_PLUGIN_API_PACKAGES, B as OPENAI_CODEX_FAST_MODE_MAX_SESSIONS, C as assertNoOpenAICodexProviderConflict, D as COMPATIBILITY_CONTRACT, F as assessCompatibility, G as OPENAI_CODEX_USAGE_URL, H as isFastModeSessionId, I as detectCompatibility, J as loginOpenAICodex, K as parseOpenAICodexUsage, L as evaluateCompatibility, M as SUPPORTED_DSH_PLUGIN_API_VERSION, N as SUPPORTED_NODE_RANGE, O as COMPATIBILITY_PACKAGES, P as SUPPORTED_PI_AI_VERSION, Q as OPENAI_CODEX_PROVIDER, R as OPENAI_CODEX_FAST_MODE_PATH, S as VIEW_IMAGE_TOOL_NAME, T as openAICodexConflictMessage, V as OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH, X as openAICodexAuthStatus, Y as logoutOpenAICodex, Z as OPENAI_CODEX_AUTH_FILENAME, _ as decodeOpenAICodexSettings, a as name, b as installOpenAICodexSearchEvent, c as OPENAI_CODEX_SEARCH_URL, d as DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE, et as openAICodexAuthPath, f as DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS, g as OPENAI_CODEX_SETTINGS_NAMESPACE, h as DEFAULT_OPENAI_CODEX_SETTINGS, i as inject, j as PI_AI_PACKAGE, k as COMPATIBILITY_SCHEMA_VERSION, l as OpenAICodexSearchProvider, m as DEFAULT_OPENAI_CODEX_SEARCH_MODEL, n as OPENAI_CODEX_SETTINGS_NS, o as OPENAI_CODEX_BASE_URL, p as DEFAULT_OPENAI_CODEX_SEARCH_MODE, q as readOpenAICodexRateLimits, r as apply, s as OPENAI_CODEX_SEARCH_PROVIDER, t as Config, u as mapOpenAICodexSearchResponse, v as resolveOpenAICodexSettings, w as diagnoseOpenAICodex, x as recordOpenAICodexSearchRequest, y as OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, z as FastModeRegistry } from "./src-DaDIQ3_t.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, 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_PROVIDER, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT, OPENAI_CODEX_SEARCH_PROVIDER, OPENAI_CODEX_SEARCH_URL, OPENAI_CODEX_SETTINGS_NAMESPACE, OPENAI_CODEX_SETTINGS_NS, OPENAI_CODEX_USAGE_URL, OpenAICodexCredentialStore, OpenAICodexSearchProvider, PI_AI_PACKAGE, SUPPORTED_DSH_PLUGIN_API_VERSION, SUPPORTED_NODE_RANGE, SUPPORTED_PI_AI_VERSION, VIEW_IMAGE_TOOL_NAME, apply, assertNoOpenAICodexProviderConflict, assessCompatibility, decodeOpenAICodexSettings, detectCompatibility, diagnoseOpenAICodex, evaluateCompatibility, inject, installOpenAICodexSearchEvent, isFastModeSessionId, loginOpenAICodex, logoutOpenAICodex, mapOpenAICodexSearchResponse, name, openAICodexAuthPath, openAICodexAuthStatus, openAICodexConflictMessage, parseOpenAICodexUsage, readOpenAICodexRateLimits, recordOpenAICodexSearchRequest, resolveOpenAICodexSettings };
@@ -170,9 +170,36 @@ const OPENAI_CODEX_STREAM_IDLE_TIMEOUT_MS = 3e5;
170
170
  * the explicit override supplied by this plugin; it never discovers an API
171
171
  * key from the environment or persistent api-key credentials.
172
172
  */
173
- function requestProvider(provider) {
173
+ function isPayloadRecord(value) {
174
+ return typeof value === "object" && value !== null && !Array.isArray(value);
175
+ }
176
+ /** Add the request-scoped Fast Mode hint without changing auth or other options. */
177
+ function withOpenAICodexFastMode(provider, fastMode) {
178
+ const streamSimple = provider.streamSimple;
174
179
  return {
175
180
  ...provider,
181
+ streamSimple(model, context, options) {
182
+ const sessionId = options?.sessionId;
183
+ if (!(provider.id === "openai-codex" && model.provider === "openai-codex" && fastMode !== void 0 && fastMode.isEnabled(sessionId))) return streamSimple.call(provider, model, context, options);
184
+ const previousOnPayload = options?.onPayload;
185
+ const nextOptions = {
186
+ ...options,
187
+ async onPayload(payload, payloadModel) {
188
+ const replaced = await previousOnPayload?.(payload, payloadModel);
189
+ const nextPayload = replaced === void 0 ? payload : replaced;
190
+ return isPayloadRecord(nextPayload) ? {
191
+ ...nextPayload,
192
+ service_tier: "priority"
193
+ } : nextPayload;
194
+ }
195
+ };
196
+ return streamSimple.call(provider, model, context, nextOptions);
197
+ }
198
+ };
199
+ }
200
+ function requestProvider(provider, fastMode) {
201
+ return {
202
+ ...withOpenAICodexFastMode(provider, fastMode),
176
203
  auth: {
177
204
  ...provider.auth,
178
205
  apiKey: {
@@ -194,7 +221,7 @@ function requestProvider(provider) {
194
221
  * resolution, streaming, reasoning metadata, and compaction behavior; this
195
222
  * plugin supplies its provider-native OAuth token for each request.
196
223
  */
197
- function createOpenAICodexAdapter(credentials, resolveAttachments) {
224
+ function createOpenAICodexAdapter(credentials, resolveAttachments, fastMode) {
198
225
  const provider = openaiCodexProvider();
199
226
  const profiles = /* @__PURE__ */ new Map([[OPENAI_CODEX_PROVIDER, {
200
227
  provider: OPENAI_CODEX_PROVIDER,
@@ -202,7 +229,7 @@ function createOpenAICodexAdapter(credentials, resolveAttachments) {
202
229
  streamIdleTimeoutMs: OPENAI_CODEX_STREAM_IDLE_TIMEOUT_MS,
203
230
  retryPolicy: resolveRetryPolicy(void 0, "dsh-codex-connect retryPolicy"),
204
231
  configuredMaxTokens: /* @__PURE__ */ new Map(),
205
- piProvider: requestProvider(provider)
232
+ piProvider: requestProvider(provider, fastMode)
206
233
  }]]);
207
234
  const models = createModels({ credentials });
208
235
  models.setProvider(provider);
@@ -277,6 +304,16 @@ function isOpenAICodexReauthRequiredError(error) {
277
304
  function isRecord$2(value) {
278
305
  return typeof value === "object" && value !== null && !Array.isArray(value);
279
306
  }
307
+ /** JavaScript Date's maximum representable instant, expressed in Unix seconds. */
308
+ const MAX_DATE_UNIX_SECONDS = Math.floor(864e10);
309
+ function parseResetAt(record) {
310
+ if (!Object.hasOwn(record, "reset_at")) return void 0;
311
+ const value = record["reset_at"];
312
+ if (value === null) return void 0;
313
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0 || value > MAX_DATE_UNIX_SECONDS) throw new Error("OpenAI Codex returned an invalid rate-limit reset time");
314
+ if (!Number.isFinite((/* @__PURE__ */ new Date(value * 1e3)).getTime())) throw new Error("OpenAI Codex returned an invalid rate-limit reset time");
315
+ return value;
316
+ }
280
317
  function parseWindow(value) {
281
318
  if (value === void 0 || value === null) return void 0;
282
319
  if (!isRecord$2(value)) throw new Error("OpenAI Codex returned a malformed rate-limit window");
@@ -284,9 +321,11 @@ function parseWindow(value) {
284
321
  const windowSeconds = value["limit_window_seconds"];
285
322
  if (typeof usedPercent !== "number" || !Number.isFinite(usedPercent) || usedPercent < 0 || usedPercent > 100) throw new Error("OpenAI Codex returned an invalid used percentage");
286
323
  if (typeof windowSeconds !== "number" || !Number.isInteger(windowSeconds) || windowSeconds <= 0) throw new Error("OpenAI Codex returned an invalid rate-limit window duration");
324
+ const resetAt = parseResetAt(value);
287
325
  return {
288
326
  remainingPercent: 100 - usedPercent,
289
- windowSeconds
327
+ windowSeconds,
328
+ ...resetAt === void 0 ? {} : { resetAt }
290
329
  };
291
330
  }
292
331
  function parseLimit(id, name, value) {
@@ -570,6 +609,85 @@ var OpenAICodexTrustedOriginsStore = class {
570
609
  });
571
610
  }
572
611
  };
612
+ //#endregion
613
+ //#region src/fast-mode.ts
614
+ /** Process-local, per-session OpenAI Codex Fast Mode state. */
615
+ /** Maximum number of enabled sessions retained by one plugin instance. */
616
+ const OPENAI_CODEX_FAST_MODE_MAX_SESSIONS = 256;
617
+ /** Maximum UTF-16 code units accepted for an opaque DSH session id. */
618
+ const OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH = 256;
619
+ /**
620
+ * Validate the opaque session identity used by the Fast Mode registry.
621
+ *
622
+ * The registry deliberately does not interpret or normalize session ids. It
623
+ * only rejects values that cannot safely serve as a bounded map key.
624
+ */
625
+ function isFastModeSessionId(value) {
626
+ return typeof value === "string" && value.trim().length > 0 && value.length <= 256;
627
+ }
628
+ /**
629
+ * In-memory Fast Mode registry. Entries are positive-only: disabling a
630
+ * session removes its key, and an insertion over the bound evicts the least
631
+ * recently touched key. A new plugin instance starts with an empty map.
632
+ */
633
+ var FastModeRegistry = class {
634
+ maxSessions;
635
+ enabledSessions = /* @__PURE__ */ new Map();
636
+ constructor(maxSessions = 256) {
637
+ this.maxSessions = maxSessions;
638
+ if (!Number.isSafeInteger(maxSessions) || maxSessions < 1 || maxSessions > 256) throw new RangeError("Fast Mode registry capacity is out of bounds");
639
+ }
640
+ /** Number of currently enabled sessions. */
641
+ get size() {
642
+ return this.enabledSessions.size;
643
+ }
644
+ /** Read one session without exposing the map or any credential state. */
645
+ isEnabled(sessionId) {
646
+ if (!isFastModeSessionId(sessionId)) return false;
647
+ if (this.enabledSessions.get(sessionId) === void 0) return false;
648
+ this.enabledSessions.delete(sessionId);
649
+ this.enabledSessions.set(sessionId, true);
650
+ return true;
651
+ }
652
+ /** Alias useful to callers that model this as a boolean setting. */
653
+ get(sessionId) {
654
+ return this.isEnabled(sessionId);
655
+ }
656
+ /** Enable or disable exactly one opaque session id. */
657
+ set(sessionId, enabled) {
658
+ if (!isFastModeSessionId(sessionId)) throw new TypeError("Invalid Fast Mode session id");
659
+ if (typeof enabled !== "boolean") throw new TypeError("Fast Mode enabled must be boolean");
660
+ if (!enabled) {
661
+ this.enabledSessions.delete(sessionId);
662
+ return;
663
+ }
664
+ this.enabledSessions.delete(sessionId);
665
+ while (this.enabledSessions.size >= this.maxSessions) {
666
+ const oldest = this.enabledSessions.keys().next().value;
667
+ if (oldest === void 0) break;
668
+ this.enabledSessions.delete(oldest);
669
+ }
670
+ this.enabledSessions.set(sessionId, true);
671
+ }
672
+ /** Explicitly named alias for callers that avoid boolean-setting verbs. */
673
+ setEnabled(sessionId, enabled) {
674
+ this.set(sessionId, enabled);
675
+ }
676
+ /** Disable one session and forget its key. */
677
+ delete(sessionId) {
678
+ if (!isFastModeSessionId(sessionId)) return;
679
+ this.enabledSessions.delete(sessionId);
680
+ }
681
+ /** Remove all process-local state during an explicit lifecycle teardown. */
682
+ clear() {
683
+ this.enabledSessions.clear();
684
+ }
685
+ };
686
+ //#endregion
687
+ //#region src/fast-mode-paths.ts
688
+ /** Node-free Fast Mode route constants shared by Host and browser halves. */
689
+ /** GET/POST endpoint for one conversation's process-local Fast Mode state. */
690
+ const OPENAI_CODEX_FAST_MODE_PATH = "/plugins/dsh-openai-codex/fast-mode";
573
691
  /** Stable, non-sensitive error returned when a browser origin needs CLI trust. */
574
692
  const REMOTE_WEB_ORIGIN_NOT_TRUSTED = "remote-web-origin-not-trusted";
575
693
  /** Redact provider diagnostics before they cross to the browser. */
@@ -799,10 +917,83 @@ function json(res, status, value) {
799
917
  });
800
918
  res.end(JSON.stringify(value));
801
919
  }
920
+ function header(req, name) {
921
+ const value = req.headers[name];
922
+ if (Array.isArray(value)) return value[0];
923
+ return value;
924
+ }
925
+ function contentLength(req) {
926
+ const raw = header(req, "content-length");
927
+ if (raw === void 0) return void 0;
928
+ if (!/^\d+$/u.test(raw.trim())) throw new TypeError("Fast Mode request content length is invalid");
929
+ const value = Number(raw);
930
+ if (!Number.isSafeInteger(value)) throw new TypeError("Fast Mode request content length is invalid");
931
+ return value;
932
+ }
933
+ /** Collect one small JSON body without exposing or logging its contents. */
934
+ async function readFastModeBody(req) {
935
+ const declared = contentLength(req);
936
+ if (declared !== void 0 && (!Number.isFinite(declared) || declared > 4096)) throw new RangeError("Fast Mode request body is too large");
937
+ const chunks = [];
938
+ let total = 0;
939
+ const iterable = req;
940
+ if (typeof req[Symbol.asyncIterator] === "function") for await (const chunk of iterable) {
941
+ const bytes = typeof chunk === "string" ? Buffer.from(chunk) : new Uint8Array(chunk);
942
+ total += bytes.byteLength;
943
+ if (total > 4096) throw new RangeError("Fast Mode request body is too large");
944
+ chunks.push(bytes);
945
+ }
946
+ else {
947
+ const body = req.body;
948
+ if (typeof body === "string") {
949
+ const bytes = Buffer.from(body);
950
+ if (bytes.byteLength > 4096) throw new RangeError("Fast Mode request body is too large");
951
+ chunks.push(bytes);
952
+ } else if (body instanceof Uint8Array) {
953
+ if (body.byteLength > 4096) throw new RangeError("Fast Mode request body is too large");
954
+ chunks.push(new Uint8Array(body));
955
+ } else if (body !== void 0) throw new TypeError("Fast Mode request body is invalid");
956
+ }
957
+ const bytes = Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)));
958
+ if (bytes.byteLength === 0) throw new TypeError("Fast Mode request body is invalid");
959
+ let text;
960
+ try {
961
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
962
+ } catch {
963
+ throw new TypeError("Fast Mode request body is invalid");
964
+ }
965
+ try {
966
+ return JSON.parse(text);
967
+ } catch {
968
+ throw new TypeError("Fast Mode request body is invalid");
969
+ }
970
+ }
971
+ function fastModeSessionIdFromQuery(req) {
972
+ const rawUrl = req.url;
973
+ if (typeof rawUrl !== "string") return void 0;
974
+ try {
975
+ const values = new URL(rawUrl, "http://dsh.invalid").searchParams.getAll("sessionId");
976
+ return values.length === 1 && isFastModeSessionId(values[0]) ? values[0] : void 0;
977
+ } catch {
978
+ return;
979
+ }
980
+ }
981
+ function fastModeBody(value) {
982
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
983
+ const record = value;
984
+ if (Object.keys(record).length !== 2) return void 0;
985
+ const sessionId = record["sessionId"];
986
+ const enabled = record["enabled"];
987
+ return isFastModeSessionId(sessionId) && typeof enabled === "boolean" ? {
988
+ sessionId,
989
+ enabled
990
+ } : void 0;
991
+ }
802
992
  /** Register the plugin-owned OAuth routes when the Web server is composed. */
803
- function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride) {
993
+ function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride, fastModeOverride) {
804
994
  const auth = new OpenAICodexWebAuth(store);
805
995
  const storedFilename = store.filename;
996
+ const fastMode = fastModeOverride ?? new FastModeRegistry();
806
997
  const trustedOrigins = trustedOriginsOverride ?? (typeof storedFilename === "string" ? new OpenAICodexTrustedOriginsStore(join(dirname(storedFilename), ".openai-codex-trusted-origins.json")) : new OpenAICodexTrustedOriginsStore());
807
998
  ctx.effect(() => {
808
999
  const authorize = async (req, res) => {
@@ -847,6 +1038,29 @@ function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride) {
847
1038
  json(res, 500, { error: safeMessage(error) });
848
1039
  }
849
1040
  }
1041
+ }),
1042
+ ctx.webServer.register({
1043
+ kind: "exact",
1044
+ path: OPENAI_CODEX_FAST_MODE_PATH,
1045
+ handler: async (req, res) => {
1046
+ if (req.method !== "GET" && req.method !== "POST") return json(res, 405, { error: "method not allowed" });
1047
+ if (!await authorize(req, res)) return;
1048
+ if (req.method === "GET") {
1049
+ const sessionId = fastModeSessionIdFromQuery(req);
1050
+ if (sessionId === void 0) return json(res, 400, { error: "invalid input" });
1051
+ return json(res, 200, { enabled: fastMode.isEnabled(sessionId) });
1052
+ }
1053
+ const type = header(req, "content-type");
1054
+ if (type === void 0 || !/^application\/json(?:\s*;|$)/iu.test(type.trim())) return json(res, 415, { error: "unsupported content type" });
1055
+ try {
1056
+ const body = fastModeBody(await readFastModeBody(req));
1057
+ if (body === void 0) return json(res, 400, { error: "invalid input" });
1058
+ fastMode.set(body.sessionId, body.enabled);
1059
+ return json(res, 200, { enabled: fastMode.isEnabled(body.sessionId) });
1060
+ } catch (error) {
1061
+ return json(res, error instanceof RangeError ? 413 : 400, { error: error instanceof RangeError ? "request body too large" : "invalid input" });
1062
+ }
1063
+ }
850
1064
  })
851
1065
  ];
852
1066
  return async () => {
@@ -995,7 +1209,7 @@ async function detectCompatibility(options = {}) {
995
1209
  }
996
1210
  //#endregion
997
1211
  //#region src/version.ts
998
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.8";
1212
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.9";
999
1213
  //#endregion
1000
1214
  //#region src/doctor.ts
1001
1215
  /** Secret-free diagnostics and duplicate-provider guidance. */
@@ -1760,8 +1974,9 @@ const Config = z.object({
1760
1974
  function apply(ctx, config) {
1761
1975
  let current = () => config;
1762
1976
  const credentials = new OpenAICodexCredentialStore();
1977
+ const fastMode = new FastModeRegistry();
1763
1978
  assertNoOpenAICodexProviderConflict(ctx.llm.listProviders().map((provider) => provider.id));
1764
- ctx.llm.registerAdapter([OPENAI_CODEX_PROVIDER], createOpenAICodexAdapter(credentials, () => ctx.get("attachments")));
1979
+ ctx.llm.registerAdapter([OPENAI_CODEX_PROVIDER], createOpenAICodexAdapter(credentials, () => ctx.get("attachments"), fastMode));
1765
1980
  ctx.llm.registerConfigurableProviders([{
1766
1981
  provider: OPENAI_CODEX_PROVIDER,
1767
1982
  displayName: "OpenAI Codex",
@@ -1769,7 +1984,7 @@ function apply(ctx, config) {
1769
1984
  settingsPath: [],
1770
1985
  declared: false
1771
1986
  }]);
1772
- ctx.inject(["webServer"], (webCtx) => registerOpenAICodexAuthRoutes(webCtx, credentials));
1987
+ ctx.inject(["webServer"], (webCtx) => registerOpenAICodexAuthRoutes(webCtx, credentials, void 0, fastMode));
1773
1988
  let stopped = false;
1774
1989
  let searchFiber;
1775
1990
  let searchRegistration;
@@ -1862,4 +2077,4 @@ function apply(ctx, config) {
1862
2077
  scheduleCapabilities();
1863
2078
  }
1864
2079
  //#endregion
1865
- export { DSH_PLUGIN_API_PACKAGES as A, OPENAI_CODEX_USAGE_URL as B, assertNoOpenAICodexProviderConflict as C, COMPATIBILITY_CONTRACT as D, CODEX_CONNECT_VERSION as E, assessCompatibility as F, openAICodexAuthStatus as G, readOpenAICodexRateLimits as H, detectCompatibility as I, OpenAICodexCredentialStore as J, OPENAI_CODEX_AUTH_FILENAME as K, evaluateCompatibility as L, SUPPORTED_DSH_PLUGIN_API_VERSION as M, SUPPORTED_NODE_RANGE as N, COMPATIBILITY_PACKAGES as O, SUPPORTED_PI_AI_VERSION as P, OpenAICodexTrustedOriginsStore as R, VIEW_IMAGE_TOOL_NAME as S, openAICodexConflictMessage as T, loginOpenAICodex as U, parseOpenAICodexUsage as V, logoutOpenAICodex as W, openAICodexAuthPath as Y, decodeOpenAICodexSettings as _, name as a, installOpenAICodexSearchEvent as b, OPENAI_CODEX_SEARCH_URL as c, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as d, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS as f, OPENAI_CODEX_SETTINGS_NAMESPACE as g, DEFAULT_OPENAI_CODEX_SETTINGS as h, inject as i, PI_AI_PACKAGE as j, COMPATIBILITY_SCHEMA_VERSION as k, OpenAICodexSearchProvider as l, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as m, OPENAI_CODEX_SETTINGS_NS as n, OPENAI_CODEX_BASE_URL as o, DEFAULT_OPENAI_CODEX_SEARCH_MODE as p, OPENAI_CODEX_PROVIDER as q, apply as r, OPENAI_CODEX_SEARCH_PROVIDER as s, Config as t, mapOpenAICodexSearchResponse as u, resolveOpenAICodexSettings as v, diagnoseOpenAICodex as w, recordOpenAICodexSearchRequest as x, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT as y, normalizeTrustedOrigin as z };
2080
+ export { OpenAICodexCredentialStore as $, DSH_PLUGIN_API_PACKAGES as A, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS as B, assertNoOpenAICodexProviderConflict as C, COMPATIBILITY_CONTRACT as D, CODEX_CONNECT_VERSION as E, assessCompatibility as F, OPENAI_CODEX_USAGE_URL as G, isFastModeSessionId as H, detectCompatibility as I, loginOpenAICodex as J, parseOpenAICodexUsage as K, evaluateCompatibility as L, SUPPORTED_DSH_PLUGIN_API_VERSION as M, SUPPORTED_NODE_RANGE as N, COMPATIBILITY_PACKAGES as O, SUPPORTED_PI_AI_VERSION as P, OPENAI_CODEX_PROVIDER as Q, OPENAI_CODEX_FAST_MODE_PATH as R, VIEW_IMAGE_TOOL_NAME as S, openAICodexConflictMessage as T, OpenAICodexTrustedOriginsStore as U, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH as V, normalizeTrustedOrigin as W, openAICodexAuthStatus as X, logoutOpenAICodex as Y, OPENAI_CODEX_AUTH_FILENAME as Z, decodeOpenAICodexSettings as _, name as a, installOpenAICodexSearchEvent as b, OPENAI_CODEX_SEARCH_URL as c, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as d, openAICodexAuthPath 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, PI_AI_PACKAGE as j, COMPATIBILITY_SCHEMA_VERSION as k, OpenAICodexSearchProvider as l, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as m, OPENAI_CODEX_SETTINGS_NS as n, OPENAI_CODEX_BASE_URL as o, DEFAULT_OPENAI_CODEX_SEARCH_MODE as p, readOpenAICodexRateLimits as q, apply as r, OPENAI_CODEX_SEARCH_PROVIDER as s, Config as t, mapOpenAICodexSearchResponse as u, resolveOpenAICodexSettings as v, diagnoseOpenAICodex as w, recordOpenAICodexSearchRequest as x, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT as y, FastModeRegistry 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.8",
5
+ "version": "0.1.0-alpha.4.9",
6
6
  "author": "Frank Song",
7
7
  "contributors": [
8
8
  "Yan-Zero (original dsh-codex author)"
@@ -82,6 +82,8 @@
82
82
  "@deepseek-ai/dsh-api-remotes",
83
83
  "@deepseek-ai/dsh-client-connection",
84
84
  "@deepseek-ai/dsh-client-runtime",
85
+ "@deepseek-ai/dsh-client-ui-conversation",
86
+ "@deepseek-ai/dsh-client-ui-model-selection",
85
87
  "@deepseek-ai/dsh-client-ui-settings",
86
88
  "@deepseek-ai/dsh-client-ui-settings-plugins",
87
89
  "@deepseek-ai/dsh-client-locale"
@@ -121,6 +123,8 @@
121
123
  "@deepseek-ai/dsh-client-locale": "0.1.0-rc.6",
122
124
  "@deepseek-ai/dsh-client-connection": "0.1.0-rc.6",
123
125
  "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.6",
126
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.6",
127
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.0-rc.6",
124
128
  "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.6",
125
129
  "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.0-rc.6",
126
130
  "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.6",