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 +1 -1
- package/README.md +1 -1
- package/docs/README.zh.md +1 -1
- package/lib/bin.js +1 -1
- package/lib/client.js +491 -17
- package/lib/index.d.ts +46 -1
- package/lib/index.js +2 -2
- package/lib/{src-DztMY1zT.js → src-DaDIQ3_t.js} +224 -9
- package/package.json +5 -1
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.
|
|
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.
|
|
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.
|
|
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,
|
|
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
|
-
|
|
948
|
-
|
|
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)(
|
|
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
|
|
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
|
|
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.
|
|
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,
|
|
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.
|
|
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",
|