dsh-codex-connect 0.1.0-alpha.4.10
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 +89 -0
- package/LICENSE +202 -0
- package/MIGRATION.md +12 -0
- package/NOTICE +15 -0
- package/README.i18n.yaml +6 -0
- package/README.md +193 -0
- package/compatibility.json +28 -0
- package/cordis.patch.yml +8 -0
- package/docs/README.zh.md +193 -0
- package/docs/assets/en/hero.jpg +0 -0
- package/docs/assets/en/model-selector.jpg +0 -0
- package/docs/assets/en/oauth-status.jpg +0 -0
- package/docs/assets/en/plugin-configuration.jpg +0 -0
- package/docs/assets/en/plugin-entry.jpg +0 -0
- package/docs/assets/hero.jpg +0 -0
- package/docs/assets/model-selector.jpg +0 -0
- package/docs/assets/oauth-status.jpg +0 -0
- package/docs/assets/plugin-configuration.jpg +0 -0
- package/docs/assets/zh/hero.jpg +0 -0
- package/docs/assets/zh/model-selector.jpg +0 -0
- package/docs/assets/zh/oauth-status.jpg +0 -0
- package/docs/assets/zh/plugin-configuration.jpg +0 -0
- package/docs/assets/zh/plugin-entry.jpg +0 -0
- package/docs/design.i18n.yaml +4 -0
- package/docs/design.md +29 -0
- package/docs/design.zh.md +25 -0
- package/lib/bin.d.ts +6 -0
- package/lib/bin.js +230 -0
- package/lib/client.js +1642 -0
- package/lib/index.d.ts +443 -0
- package/lib/index.js +2 -0
- package/lib/invariant.d.ts +14 -0
- package/lib/invariant.js +15 -0
- package/lib/src-DLX8Rs0k.js +2080 -0
- package/package.json +155 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1642 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-codex-connect",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
//#region src/settings-contract.ts
|
|
10
|
+
/** Node-free settings contract shared by the Host plugin and browser card. */
|
|
11
|
+
/** Stable Harness settings namespace owned by this plugin. */
|
|
12
|
+
const OPENAI_CODEX_SETTINGS_NAMESPACE = "llm-openai-codex";
|
|
13
|
+
Object.freeze({
|
|
14
|
+
enableSearch: false,
|
|
15
|
+
enableImageTool: false,
|
|
16
|
+
searchModel: "gpt-5.6-sol",
|
|
17
|
+
searchMode: "cached",
|
|
18
|
+
searchContextSize: "medium",
|
|
19
|
+
searchMaxOutputTokens: 1e4
|
|
20
|
+
});
|
|
21
|
+
function isRecord$2(value) {
|
|
22
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23
|
+
}
|
|
24
|
+
/** Narrow the redacted settings wire payload before it enters React state. */
|
|
25
|
+
function decodeOpenAICodexSettings(value) {
|
|
26
|
+
if (!isRecord$2(value)) return void 0;
|
|
27
|
+
const enableSearch = value["enableSearch"];
|
|
28
|
+
const enableImageTool = value["enableImageTool"];
|
|
29
|
+
const searchModel = value["searchModel"];
|
|
30
|
+
const searchMode = value["searchMode"];
|
|
31
|
+
const searchContextSize = value["searchContextSize"];
|
|
32
|
+
const searchMaxOutputTokens = value["searchMaxOutputTokens"];
|
|
33
|
+
if (typeof enableSearch !== "boolean" || typeof enableImageTool !== "boolean") return void 0;
|
|
34
|
+
if (typeof searchModel !== "string" || searchModel.trim().length === 0) return void 0;
|
|
35
|
+
if (searchMode !== "cached" && searchMode !== "indexed" && searchMode !== "live") return void 0;
|
|
36
|
+
if (searchContextSize !== "low" && searchContextSize !== "medium" && searchContextSize !== "high") return void 0;
|
|
37
|
+
if (typeof searchMaxOutputTokens !== "number" || !Number.isInteger(searchMaxOutputTokens) || searchMaxOutputTokens < 1) return void 0;
|
|
38
|
+
return {
|
|
39
|
+
enableSearch,
|
|
40
|
+
enableImageTool,
|
|
41
|
+
searchModel,
|
|
42
|
+
searchMode,
|
|
43
|
+
searchContextSize,
|
|
44
|
+
searchMaxOutputTokens
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/auth-paths.ts
|
|
49
|
+
/** Node-free route constants shared by the Host and browser plugin halves. */
|
|
50
|
+
/** Plugin-owned status endpoint consumed by its browser half. */
|
|
51
|
+
const OPENAI_CODEX_AUTH_STATUS_PATH = "/plugins/dsh-openai-codex/auth/status";
|
|
52
|
+
/** Plugin-owned browser-login endpoint consumed by its browser half. */
|
|
53
|
+
const OPENAI_CODEX_AUTH_LOGIN_PATH = "/plugins/dsh-openai-codex/auth/login";
|
|
54
|
+
/** Plugin-owned logout endpoint consumed by its browser half. */
|
|
55
|
+
const OPENAI_CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-openai-codex/auth/logout";
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/client/OpenAICodexConfiguration.tsx
|
|
58
|
+
/** Staged optional-capability editor inside the OpenAI Codex plugin card. */
|
|
59
|
+
const sectionStyle = {
|
|
60
|
+
display: "flex",
|
|
61
|
+
flexDirection: "column",
|
|
62
|
+
gap: 14,
|
|
63
|
+
paddingTop: 18,
|
|
64
|
+
borderTop: "1px solid var(--dsw-alias-border-l2)"
|
|
65
|
+
};
|
|
66
|
+
const headingStyle = {
|
|
67
|
+
margin: 0,
|
|
68
|
+
fontSize: 14,
|
|
69
|
+
lineHeight: "20px",
|
|
70
|
+
fontWeight: 600,
|
|
71
|
+
color: "var(--dsw-alias-label-primary)"
|
|
72
|
+
};
|
|
73
|
+
const bodyStyle$1 = {
|
|
74
|
+
margin: 0,
|
|
75
|
+
fontSize: 13,
|
|
76
|
+
lineHeight: "20px",
|
|
77
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
78
|
+
};
|
|
79
|
+
const fieldsetStyle = {
|
|
80
|
+
display: "flex",
|
|
81
|
+
flexDirection: "column",
|
|
82
|
+
gap: 13,
|
|
83
|
+
margin: 0,
|
|
84
|
+
padding: 0,
|
|
85
|
+
border: 0
|
|
86
|
+
};
|
|
87
|
+
const toggleRowStyle = {
|
|
88
|
+
display: "flex",
|
|
89
|
+
alignItems: "flex-start",
|
|
90
|
+
gap: 10,
|
|
91
|
+
cursor: "pointer"
|
|
92
|
+
};
|
|
93
|
+
const toggleCopyStyle = {
|
|
94
|
+
display: "flex",
|
|
95
|
+
flexDirection: "column",
|
|
96
|
+
gap: 2
|
|
97
|
+
};
|
|
98
|
+
const labelStyle = {
|
|
99
|
+
fontSize: 13,
|
|
100
|
+
lineHeight: "20px",
|
|
101
|
+
fontWeight: 500,
|
|
102
|
+
color: "var(--dsw-alias-label-primary)"
|
|
103
|
+
};
|
|
104
|
+
const formGridStyle = {
|
|
105
|
+
display: "grid",
|
|
106
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(190px, 1fr))",
|
|
107
|
+
gap: 12
|
|
108
|
+
};
|
|
109
|
+
const formFieldStyle = {
|
|
110
|
+
display: "flex",
|
|
111
|
+
flexDirection: "column",
|
|
112
|
+
gap: 6
|
|
113
|
+
};
|
|
114
|
+
const controlStyle = {
|
|
115
|
+
boxSizing: "border-box",
|
|
116
|
+
width: "100%",
|
|
117
|
+
minHeight: 36,
|
|
118
|
+
padding: "7px 10px",
|
|
119
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
120
|
+
borderRadius: 8,
|
|
121
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
122
|
+
color: "var(--dsw-alias-label-primary)",
|
|
123
|
+
font: "inherit",
|
|
124
|
+
fontSize: 13
|
|
125
|
+
};
|
|
126
|
+
const actionsStyle = {
|
|
127
|
+
display: "flex",
|
|
128
|
+
alignItems: "center",
|
|
129
|
+
justifyContent: "space-between",
|
|
130
|
+
flexWrap: "wrap",
|
|
131
|
+
gap: 10
|
|
132
|
+
};
|
|
133
|
+
const buttonsStyle = {
|
|
134
|
+
display: "flex",
|
|
135
|
+
gap: 8
|
|
136
|
+
};
|
|
137
|
+
const buttonStyle$1 = {
|
|
138
|
+
boxSizing: "border-box",
|
|
139
|
+
minHeight: 34,
|
|
140
|
+
padding: "6px 14px",
|
|
141
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
142
|
+
borderRadius: 18,
|
|
143
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
144
|
+
color: "var(--dsw-alias-label-primary)",
|
|
145
|
+
font: "inherit",
|
|
146
|
+
fontSize: 13,
|
|
147
|
+
cursor: "pointer"
|
|
148
|
+
};
|
|
149
|
+
const primaryButtonStyle$1 = {
|
|
150
|
+
...buttonStyle$1,
|
|
151
|
+
borderColor: "var(--dsw-alias-brand-primary)",
|
|
152
|
+
background: "var(--dsw-alias-brand-primary)",
|
|
153
|
+
color: "white"
|
|
154
|
+
};
|
|
155
|
+
const errorStyle$1 = {
|
|
156
|
+
...bodyStyle$1,
|
|
157
|
+
color: "var(--dsw-alias-state-error-primary)"
|
|
158
|
+
};
|
|
159
|
+
const successStyle = {
|
|
160
|
+
...bodyStyle$1,
|
|
161
|
+
color: "var(--dsw-alias-state-success-primary, #16825d)"
|
|
162
|
+
};
|
|
163
|
+
const UNAVAILABLE_SNAPSHOT = {
|
|
164
|
+
status: "unavailable",
|
|
165
|
+
value: void 0,
|
|
166
|
+
base: void 0,
|
|
167
|
+
user: void 0,
|
|
168
|
+
revision: void 0,
|
|
169
|
+
writable: false,
|
|
170
|
+
mode: "memory"
|
|
171
|
+
};
|
|
172
|
+
const CONFIG_FIELDS = [
|
|
173
|
+
"enableSearch",
|
|
174
|
+
"enableImageTool",
|
|
175
|
+
"searchModel",
|
|
176
|
+
"searchMode",
|
|
177
|
+
"searchContextSize",
|
|
178
|
+
"searchMaxOutputTokens"
|
|
179
|
+
];
|
|
180
|
+
function sameConfig(left, right) {
|
|
181
|
+
return left !== void 0 && right !== void 0 && CONFIG_FIELDS.every((field) => left[field] === right[field]);
|
|
182
|
+
}
|
|
183
|
+
/** Edit the Host-owned llm-openai-codex settings section with Save/Discard staging. */
|
|
184
|
+
function OpenAICodexConfiguration({ scope, t }) {
|
|
185
|
+
const subscribe = (0, react.useCallback)((listener) => scope?.subscribe(listener) ?? (() => void 0), [scope]);
|
|
186
|
+
const getSnapshot = (0, react.useCallback)(() => scope?.getSnapshot() ?? UNAVAILABLE_SNAPSHOT, [scope]);
|
|
187
|
+
const snapshot = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
188
|
+
const [draft, setDraft] = (0, react.useState)(snapshot.value);
|
|
189
|
+
const [dirty, setDirty] = (0, react.useState)(false);
|
|
190
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
191
|
+
const [feedback, setFeedback] = (0, react.useState)("idle");
|
|
192
|
+
(0, react.useEffect)(() => {
|
|
193
|
+
if (!dirty && !busy) setDraft(snapshot.value);
|
|
194
|
+
}, [
|
|
195
|
+
busy,
|
|
196
|
+
dirty,
|
|
197
|
+
snapshot.revision,
|
|
198
|
+
snapshot.value
|
|
199
|
+
]);
|
|
200
|
+
const update = (field, value) => {
|
|
201
|
+
setDraft((current) => current === void 0 ? current : {
|
|
202
|
+
...current,
|
|
203
|
+
[field]: value
|
|
204
|
+
});
|
|
205
|
+
setDirty(true);
|
|
206
|
+
setFeedback("idle");
|
|
207
|
+
};
|
|
208
|
+
const discard = () => {
|
|
209
|
+
setDraft(scope?.getSnapshot().value);
|
|
210
|
+
setDirty(false);
|
|
211
|
+
setFeedback("idle");
|
|
212
|
+
};
|
|
213
|
+
const validModel = draft !== void 0 && draft.searchModel.trim().length > 0;
|
|
214
|
+
const validTokens = draft !== void 0 && Number.isInteger(draft.searchMaxOutputTokens) && draft.searchMaxOutputTokens > 0;
|
|
215
|
+
const valid = validModel && validTokens;
|
|
216
|
+
const save = async () => {
|
|
217
|
+
if (scope === void 0 || draft === void 0 || !snapshot.writable || !valid) return;
|
|
218
|
+
const desired = {
|
|
219
|
+
...draft,
|
|
220
|
+
searchModel: draft.searchModel.trim()
|
|
221
|
+
};
|
|
222
|
+
setBusy(true);
|
|
223
|
+
setFeedback("idle");
|
|
224
|
+
try {
|
|
225
|
+
for (const field of CONFIG_FIELDS) {
|
|
226
|
+
if (scope.getSnapshot().value?.[field] === desired[field]) continue;
|
|
227
|
+
await scope.set(field, desired[field]);
|
|
228
|
+
if (scope.getSnapshot().value?.[field] !== desired[field]) throw new Error(`Host refused ${field}`);
|
|
229
|
+
}
|
|
230
|
+
const accepted = scope.getSnapshot().value;
|
|
231
|
+
if (!sameConfig(accepted, desired)) throw new Error("Host returned a different configuration");
|
|
232
|
+
setDraft(accepted);
|
|
233
|
+
setDirty(false);
|
|
234
|
+
setFeedback("saved");
|
|
235
|
+
} catch {
|
|
236
|
+
setDraft(scope.getSnapshot().value);
|
|
237
|
+
setDirty(false);
|
|
238
|
+
setFeedback("error");
|
|
239
|
+
} finally {
|
|
240
|
+
setBusy(false);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const loading = snapshot.status === "loading";
|
|
244
|
+
const editable = snapshot.status === "ready" && snapshot.writable && !busy;
|
|
245
|
+
const searchDisabled = !editable || draft?.enableSearch !== true;
|
|
246
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
247
|
+
style: sectionStyle,
|
|
248
|
+
"aria-labelledby": "openai-codex-capabilities-title",
|
|
249
|
+
children: [
|
|
250
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
251
|
+
id: "openai-codex-capabilities-title",
|
|
252
|
+
style: headingStyle,
|
|
253
|
+
children: t("capabilitiesHeading")
|
|
254
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
255
|
+
style: {
|
|
256
|
+
...bodyStyle$1,
|
|
257
|
+
marginTop: 4
|
|
258
|
+
},
|
|
259
|
+
children: t("capabilitiesIntro")
|
|
260
|
+
})] }),
|
|
261
|
+
loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
262
|
+
style: bodyStyle$1,
|
|
263
|
+
role: "status",
|
|
264
|
+
children: t("settingsLoading")
|
|
265
|
+
}) : null,
|
|
266
|
+
snapshot.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
267
|
+
style: errorStyle$1,
|
|
268
|
+
role: "alert",
|
|
269
|
+
children: t("settingsUnavailable")
|
|
270
|
+
}) : null,
|
|
271
|
+
snapshot.status === "ready" && !snapshot.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
272
|
+
style: errorStyle$1,
|
|
273
|
+
role: "alert",
|
|
274
|
+
children: t("settingsReadOnly")
|
|
275
|
+
}) : null,
|
|
276
|
+
draft === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
277
|
+
style: fieldsetStyle,
|
|
278
|
+
disabled: !editable,
|
|
279
|
+
children: [
|
|
280
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
281
|
+
style: toggleRowStyle,
|
|
282
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
283
|
+
type: "checkbox",
|
|
284
|
+
checked: draft.enableSearch,
|
|
285
|
+
onChange: (event) => {
|
|
286
|
+
update("enableSearch", event.currentTarget.checked);
|
|
287
|
+
}
|
|
288
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
289
|
+
style: toggleCopyStyle,
|
|
290
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
291
|
+
style: labelStyle,
|
|
292
|
+
children: t("enableSearch")
|
|
293
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
294
|
+
style: bodyStyle$1,
|
|
295
|
+
children: t("enableSearchHelp")
|
|
296
|
+
})]
|
|
297
|
+
})]
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
300
|
+
style: formGridStyle,
|
|
301
|
+
"aria-disabled": searchDisabled,
|
|
302
|
+
children: [
|
|
303
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
304
|
+
style: formFieldStyle,
|
|
305
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
306
|
+
style: labelStyle,
|
|
307
|
+
children: t("searchModel")
|
|
308
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
309
|
+
style: controlStyle,
|
|
310
|
+
value: draft.searchModel,
|
|
311
|
+
disabled: searchDisabled,
|
|
312
|
+
"aria-invalid": !validModel,
|
|
313
|
+
onChange: (event) => {
|
|
314
|
+
update("searchModel", event.currentTarget.value);
|
|
315
|
+
}
|
|
316
|
+
})]
|
|
317
|
+
}),
|
|
318
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
319
|
+
style: formFieldStyle,
|
|
320
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
321
|
+
style: labelStyle,
|
|
322
|
+
children: t("searchMode")
|
|
323
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
324
|
+
style: controlStyle,
|
|
325
|
+
value: draft.searchMode,
|
|
326
|
+
disabled: searchDisabled,
|
|
327
|
+
onChange: (event) => {
|
|
328
|
+
update("searchMode", event.currentTarget.value);
|
|
329
|
+
},
|
|
330
|
+
children: [
|
|
331
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
332
|
+
value: "cached",
|
|
333
|
+
children: t("modeCached")
|
|
334
|
+
}),
|
|
335
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
336
|
+
value: "indexed",
|
|
337
|
+
children: t("modeIndexed")
|
|
338
|
+
}),
|
|
339
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
340
|
+
value: "live",
|
|
341
|
+
children: t("modeLive")
|
|
342
|
+
})
|
|
343
|
+
]
|
|
344
|
+
})]
|
|
345
|
+
}),
|
|
346
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
347
|
+
style: formFieldStyle,
|
|
348
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
349
|
+
style: labelStyle,
|
|
350
|
+
children: t("searchContextSize")
|
|
351
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
352
|
+
style: controlStyle,
|
|
353
|
+
value: draft.searchContextSize,
|
|
354
|
+
disabled: searchDisabled,
|
|
355
|
+
onChange: (event) => {
|
|
356
|
+
update("searchContextSize", event.currentTarget.value);
|
|
357
|
+
},
|
|
358
|
+
children: [
|
|
359
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
360
|
+
value: "low",
|
|
361
|
+
children: t("contextLow")
|
|
362
|
+
}),
|
|
363
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
364
|
+
value: "medium",
|
|
365
|
+
children: t("contextMedium")
|
|
366
|
+
}),
|
|
367
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
368
|
+
value: "high",
|
|
369
|
+
children: t("contextHigh")
|
|
370
|
+
})
|
|
371
|
+
]
|
|
372
|
+
})]
|
|
373
|
+
}),
|
|
374
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
375
|
+
style: formFieldStyle,
|
|
376
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
377
|
+
style: labelStyle,
|
|
378
|
+
children: t("searchMaxOutputTokens")
|
|
379
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
380
|
+
style: controlStyle,
|
|
381
|
+
type: "number",
|
|
382
|
+
min: 1,
|
|
383
|
+
step: 1,
|
|
384
|
+
value: draft.searchMaxOutputTokens,
|
|
385
|
+
disabled: searchDisabled,
|
|
386
|
+
"aria-invalid": !validTokens,
|
|
387
|
+
onChange: (event) => {
|
|
388
|
+
update("searchMaxOutputTokens", event.currentTarget.valueAsNumber);
|
|
389
|
+
}
|
|
390
|
+
})]
|
|
391
|
+
})
|
|
392
|
+
]
|
|
393
|
+
}),
|
|
394
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
395
|
+
style: toggleRowStyle,
|
|
396
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
397
|
+
type: "checkbox",
|
|
398
|
+
checked: draft.enableImageTool,
|
|
399
|
+
onChange: (event) => {
|
|
400
|
+
update("enableImageTool", event.currentTarget.checked);
|
|
401
|
+
}
|
|
402
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
403
|
+
style: toggleCopyStyle,
|
|
404
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
405
|
+
style: labelStyle,
|
|
406
|
+
children: t("enableImageTool")
|
|
407
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
408
|
+
style: bodyStyle$1,
|
|
409
|
+
children: t("enableImageToolHelp")
|
|
410
|
+
})]
|
|
411
|
+
})]
|
|
412
|
+
})
|
|
413
|
+
]
|
|
414
|
+
}),
|
|
415
|
+
!validModel && draft !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
416
|
+
style: errorStyle$1,
|
|
417
|
+
role: "alert",
|
|
418
|
+
children: t("invalidSearchModel")
|
|
419
|
+
}) : null,
|
|
420
|
+
!validTokens && draft !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
421
|
+
style: errorStyle$1,
|
|
422
|
+
role: "alert",
|
|
423
|
+
children: t("invalidSearchTokens")
|
|
424
|
+
}) : null,
|
|
425
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
426
|
+
style: bodyStyle$1,
|
|
427
|
+
children: t("routingNote")
|
|
428
|
+
}),
|
|
429
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
430
|
+
style: actionsStyle,
|
|
431
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
432
|
+
"aria-live": "polite",
|
|
433
|
+
children: [feedback === "saved" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
434
|
+
style: successStyle,
|
|
435
|
+
children: t("settingsSaved")
|
|
436
|
+
}) : null, feedback === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
437
|
+
style: errorStyle$1,
|
|
438
|
+
children: t("settingsSaveFailed")
|
|
439
|
+
}) : null]
|
|
440
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
441
|
+
style: buttonsStyle,
|
|
442
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
443
|
+
type: "button",
|
|
444
|
+
style: buttonStyle$1,
|
|
445
|
+
disabled: !dirty || busy,
|
|
446
|
+
onClick: discard,
|
|
447
|
+
children: t("discard")
|
|
448
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
449
|
+
type: "button",
|
|
450
|
+
style: primaryButtonStyle$1,
|
|
451
|
+
disabled: !dirty || !valid || !snapshot.writable || busy,
|
|
452
|
+
onClick: () => {
|
|
453
|
+
save();
|
|
454
|
+
},
|
|
455
|
+
children: busy ? t("saving") : t("save")
|
|
456
|
+
})]
|
|
457
|
+
})]
|
|
458
|
+
})
|
|
459
|
+
]
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region src/client/OpenAICodexSettings.tsx
|
|
464
|
+
/** Plugin-owned OpenAI Codex account controls used inside Plugin configuration. */
|
|
465
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
466
|
+
const USAGE_POLL_INTERVAL_MS$1 = 6e4;
|
|
467
|
+
const pageStyle = {
|
|
468
|
+
display: "flex",
|
|
469
|
+
flexDirection: "column",
|
|
470
|
+
gap: 18,
|
|
471
|
+
maxWidth: 720
|
|
472
|
+
};
|
|
473
|
+
const titleStyle = {
|
|
474
|
+
margin: 0,
|
|
475
|
+
fontSize: 20,
|
|
476
|
+
lineHeight: "28px",
|
|
477
|
+
fontWeight: 600,
|
|
478
|
+
color: "var(--dsw-alias-label-primary)"
|
|
479
|
+
};
|
|
480
|
+
const bodyStyle = {
|
|
481
|
+
margin: 0,
|
|
482
|
+
fontSize: 14,
|
|
483
|
+
lineHeight: "22px",
|
|
484
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
485
|
+
};
|
|
486
|
+
const cardStyle$1 = {
|
|
487
|
+
display: "flex",
|
|
488
|
+
flexDirection: "column",
|
|
489
|
+
gap: 14,
|
|
490
|
+
padding: "18px 20px",
|
|
491
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
492
|
+
borderRadius: 12,
|
|
493
|
+
background: "var(--dsw-alias-bg-module-platform)"
|
|
494
|
+
};
|
|
495
|
+
const embeddedPageStyle = {
|
|
496
|
+
...pageStyle,
|
|
497
|
+
gap: 0,
|
|
498
|
+
maxWidth: "none"
|
|
499
|
+
};
|
|
500
|
+
const embeddedCardStyle = {
|
|
501
|
+
...cardStyle$1,
|
|
502
|
+
padding: 0,
|
|
503
|
+
border: 0,
|
|
504
|
+
borderRadius: 0,
|
|
505
|
+
background: "transparent"
|
|
506
|
+
};
|
|
507
|
+
const rowStyle = {
|
|
508
|
+
display: "flex",
|
|
509
|
+
alignItems: "center",
|
|
510
|
+
justifyContent: "space-between",
|
|
511
|
+
flexWrap: "wrap",
|
|
512
|
+
gap: 12
|
|
513
|
+
};
|
|
514
|
+
const statusStyle = {
|
|
515
|
+
display: "flex",
|
|
516
|
+
alignItems: "center",
|
|
517
|
+
gap: 9,
|
|
518
|
+
fontSize: 15,
|
|
519
|
+
fontWeight: 500,
|
|
520
|
+
color: "var(--dsw-alias-label-primary)"
|
|
521
|
+
};
|
|
522
|
+
const buttonStyle = {
|
|
523
|
+
boxSizing: "border-box",
|
|
524
|
+
minHeight: 34,
|
|
525
|
+
padding: "6px 14px",
|
|
526
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
527
|
+
borderRadius: 18,
|
|
528
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
529
|
+
color: "var(--dsw-alias-label-primary)",
|
|
530
|
+
font: "inherit",
|
|
531
|
+
fontSize: 14,
|
|
532
|
+
cursor: "pointer"
|
|
533
|
+
};
|
|
534
|
+
const primaryButtonStyle = {
|
|
535
|
+
...buttonStyle,
|
|
536
|
+
borderColor: "var(--dsw-alias-brand-primary)",
|
|
537
|
+
background: "var(--dsw-alias-brand-primary)",
|
|
538
|
+
color: "white"
|
|
539
|
+
};
|
|
540
|
+
const errorStyle = {
|
|
541
|
+
...bodyStyle,
|
|
542
|
+
color: "var(--dsw-alias-state-error-primary)"
|
|
543
|
+
};
|
|
544
|
+
const quotaListStyle = {
|
|
545
|
+
display: "flex",
|
|
546
|
+
flexDirection: "column",
|
|
547
|
+
gap: 18,
|
|
548
|
+
paddingTop: 2
|
|
549
|
+
};
|
|
550
|
+
const quotaGroupStyle = {
|
|
551
|
+
display: "flex",
|
|
552
|
+
flexDirection: "column",
|
|
553
|
+
gap: 10
|
|
554
|
+
};
|
|
555
|
+
const quotaTitleStyle = {
|
|
556
|
+
margin: 0,
|
|
557
|
+
fontSize: 14,
|
|
558
|
+
lineHeight: "20px",
|
|
559
|
+
fontWeight: 600,
|
|
560
|
+
color: "var(--dsw-alias-label-primary)"
|
|
561
|
+
};
|
|
562
|
+
const quotaLabelStyle = {
|
|
563
|
+
display: "flex",
|
|
564
|
+
justifyContent: "space-between",
|
|
565
|
+
gap: 12,
|
|
566
|
+
fontSize: 13,
|
|
567
|
+
lineHeight: "20px",
|
|
568
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
569
|
+
};
|
|
570
|
+
const progressTrackStyle = {
|
|
571
|
+
height: 8,
|
|
572
|
+
overflow: "hidden",
|
|
573
|
+
borderRadius: 999,
|
|
574
|
+
background: "var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.08))"
|
|
575
|
+
};
|
|
576
|
+
const commandStyle = {
|
|
577
|
+
margin: 0,
|
|
578
|
+
padding: "10px 12px",
|
|
579
|
+
overflowX: "auto",
|
|
580
|
+
borderRadius: 8,
|
|
581
|
+
background: "var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.06))",
|
|
582
|
+
color: "var(--dsw-alias-label-primary)",
|
|
583
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
584
|
+
fontSize: 13,
|
|
585
|
+
lineHeight: "20px",
|
|
586
|
+
whiteSpace: "pre-wrap",
|
|
587
|
+
overflowWrap: "anywhere"
|
|
588
|
+
};
|
|
589
|
+
function progressFillStyle(percent) {
|
|
590
|
+
return {
|
|
591
|
+
width: `${Math.max(0, Math.min(100, percent))}%`,
|
|
592
|
+
height: "100%",
|
|
593
|
+
borderRadius: "inherit",
|
|
594
|
+
background: "var(--dsw-alias-brand-primary, #1677ff)"
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function windowLabel(seconds, t) {
|
|
598
|
+
if (seconds === 18e3) return t("fiveHourLimit");
|
|
599
|
+
if (seconds === 604800) return t("weeklyLimit");
|
|
600
|
+
const hours = seconds / 3600;
|
|
601
|
+
return Number.isInteger(hours) ? t("hourLimit", { count: hours }) : t("usageWindow");
|
|
602
|
+
}
|
|
603
|
+
function formatPercent$1(percent) {
|
|
604
|
+
return new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(percent);
|
|
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
|
+
}
|
|
616
|
+
function QuotaBar({ label, percent, detail, t }) {
|
|
617
|
+
const display = formatPercent$1(percent);
|
|
618
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
619
|
+
style: quotaGroupStyle,
|
|
620
|
+
children: [
|
|
621
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
622
|
+
style: quotaLabelStyle,
|
|
623
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("percentRemaining", { percent: display }) })]
|
|
624
|
+
}),
|
|
625
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
626
|
+
style: progressTrackStyle,
|
|
627
|
+
role: "progressbar",
|
|
628
|
+
"aria-label": label,
|
|
629
|
+
"aria-valuemin": 0,
|
|
630
|
+
"aria-valuemax": 100,
|
|
631
|
+
"aria-valuenow": percent,
|
|
632
|
+
"aria-valuetext": t("percentRemaining", { percent: display }),
|
|
633
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: progressFillStyle(percent) })
|
|
634
|
+
}),
|
|
635
|
+
detail === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
636
|
+
style: bodyStyle,
|
|
637
|
+
children: detail
|
|
638
|
+
})
|
|
639
|
+
]
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function UsageLimits({ usage, quotaError, t }) {
|
|
643
|
+
const hasData = usage.rateLimits.length > 0 || usage.credits !== void 0 || usage.individualLimit !== void 0;
|
|
644
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
645
|
+
style: quotaListStyle,
|
|
646
|
+
children: [
|
|
647
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
648
|
+
style: quotaTitleStyle,
|
|
649
|
+
children: t("usageLimits")
|
|
650
|
+
}),
|
|
651
|
+
usage.rateLimits.map((limit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
652
|
+
style: quotaGroupStyle,
|
|
653
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
654
|
+
style: quotaTitleStyle,
|
|
655
|
+
children: limit.name ?? limit.id
|
|
656
|
+
}), limit.windows.map((window) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaBar, {
|
|
657
|
+
label: windowLabel(window.windowSeconds, t),
|
|
658
|
+
percent: window.remainingPercent,
|
|
659
|
+
detail: t("resetAt", { time: formatOpenAICodexResetAt(window.resetAt) ?? t("resetUnavailable") }),
|
|
660
|
+
t
|
|
661
|
+
}, window.windowSeconds))]
|
|
662
|
+
}, limit.id)),
|
|
663
|
+
usage.individualLimit === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaBar, {
|
|
664
|
+
label: t("monthlyLimit"),
|
|
665
|
+
percent: usage.individualLimit.remainingPercent,
|
|
666
|
+
detail: t("exactRemaining", {
|
|
667
|
+
remaining: usage.individualLimit.remaining,
|
|
668
|
+
limit: usage.individualLimit.limit
|
|
669
|
+
}),
|
|
670
|
+
t
|
|
671
|
+
}),
|
|
672
|
+
usage.credits === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
673
|
+
style: quotaLabelStyle,
|
|
674
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("credits") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: usage.credits.unlimited ? t("unlimited") : usage.credits.balance === void 0 ? t("available") : usage.credits.balance })]
|
|
675
|
+
}),
|
|
676
|
+
!hasData && quotaError === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
677
|
+
style: bodyStyle,
|
|
678
|
+
children: t("quotaUnavailable")
|
|
679
|
+
}) : null,
|
|
680
|
+
quotaError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
681
|
+
style: errorStyle,
|
|
682
|
+
children: t("quotaUnavailable")
|
|
683
|
+
})
|
|
684
|
+
]
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
function dotStyle(status) {
|
|
688
|
+
return {
|
|
689
|
+
width: 9,
|
|
690
|
+
height: 9,
|
|
691
|
+
borderRadius: "50%",
|
|
692
|
+
flex: "0 0 auto",
|
|
693
|
+
background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" || status === "reauth-required" || status === "remote-web-origin-not-trusted" ? "var(--dsw-alias-state-error-primary, #d92d20)" : status === "signing-in" || status === "loading" ? "var(--dsw-alias-brand-primary, #1677ff)" : "var(--dsw-alias-label-dimmed, #9aa0a6)"
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
var AccountRequestError = class extends Error {
|
|
697
|
+
code;
|
|
698
|
+
constructor(code, message) {
|
|
699
|
+
super(message);
|
|
700
|
+
this.code = code;
|
|
701
|
+
this.name = "AccountRequestError";
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
async function jsonRequest(path, method = "GET", signal) {
|
|
705
|
+
const response = await fetch(path, {
|
|
706
|
+
method,
|
|
707
|
+
headers: { accept: "application/json" },
|
|
708
|
+
credentials: "same-origin",
|
|
709
|
+
...signal === void 0 ? {} : { signal }
|
|
710
|
+
});
|
|
711
|
+
const value = await response.json().catch(() => void 0);
|
|
712
|
+
if (!response.ok) {
|
|
713
|
+
const code = typeof value === "object" && value !== null && "error" in value && typeof value.error === "string" ? value.error : `HTTP ${response.status}`;
|
|
714
|
+
throw new AccountRequestError(code, code);
|
|
715
|
+
}
|
|
716
|
+
return value;
|
|
717
|
+
}
|
|
718
|
+
/** OpenAI Codex account status and OAuth actions. */
|
|
719
|
+
function OpenAICodexSettings({ t, configScope, embedded = false }) {
|
|
720
|
+
if (t === void 0) throw new Error("OpenAI Codex settings requires its translation function");
|
|
721
|
+
const [status, setStatus] = (0, react.useState)({ status: "loading" });
|
|
722
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
723
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
724
|
+
const [copyFailed, setCopyFailed] = (0, react.useState)(false);
|
|
725
|
+
const mounted = (0, react.useRef)(true);
|
|
726
|
+
const trustedOriginCommand = `dsh plugin --profile web exec dsh-codex-connect trust-origin ${window.location.origin}`;
|
|
727
|
+
(0, react.useEffect)(() => {
|
|
728
|
+
mounted.current = true;
|
|
729
|
+
return () => {
|
|
730
|
+
mounted.current = false;
|
|
731
|
+
};
|
|
732
|
+
}, []);
|
|
733
|
+
const refresh = (0, react.useCallback)(async (signal) => {
|
|
734
|
+
try {
|
|
735
|
+
const nextStatus = await jsonRequest(OPENAI_CODEX_AUTH_STATUS_PATH, "GET", signal);
|
|
736
|
+
if (mounted.current && signal?.aborted !== true) setStatus(nextStatus);
|
|
737
|
+
} catch (error) {
|
|
738
|
+
if (mounted.current && signal?.aborted !== true) setStatus(error instanceof AccountRequestError && error.code === "remote-web-origin-not-trusted" ? { status: "remote-web-origin-not-trusted" } : {
|
|
739
|
+
status: "error",
|
|
740
|
+
message: error instanceof Error ? error.message : t("requestFailed")
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
}, [t]);
|
|
744
|
+
(0, react.useEffect)(() => {
|
|
745
|
+
const controller = new AbortController();
|
|
746
|
+
refresh(controller.signal);
|
|
747
|
+
return () => {
|
|
748
|
+
controller.abort();
|
|
749
|
+
};
|
|
750
|
+
}, [refresh]);
|
|
751
|
+
(0, react.useEffect)(() => {
|
|
752
|
+
const interval = status.status === "signing-in" ? POLL_INTERVAL_MS : status.status === "signed-in" ? USAGE_POLL_INTERVAL_MS$1 : void 0;
|
|
753
|
+
if (interval === void 0) return;
|
|
754
|
+
const controller = new AbortController();
|
|
755
|
+
const timer = window.setInterval(() => {
|
|
756
|
+
refresh(controller.signal);
|
|
757
|
+
}, interval);
|
|
758
|
+
return () => {
|
|
759
|
+
window.clearInterval(timer);
|
|
760
|
+
controller.abort();
|
|
761
|
+
};
|
|
762
|
+
}, [refresh, status.status]);
|
|
763
|
+
const signIn = async () => {
|
|
764
|
+
const popup = window.open("about:blank", "_blank");
|
|
765
|
+
if (popup === null) {
|
|
766
|
+
setStatus({
|
|
767
|
+
status: "error",
|
|
768
|
+
message: t("popupBlocked")
|
|
769
|
+
});
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
popup.opener = null;
|
|
773
|
+
setBusy(true);
|
|
774
|
+
setStatus({ status: "signing-in" });
|
|
775
|
+
try {
|
|
776
|
+
const challenge = await jsonRequest(OPENAI_CODEX_AUTH_LOGIN_PATH, "POST");
|
|
777
|
+
if (!mounted.current) {
|
|
778
|
+
popup.close();
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
popup.location.replace(challenge.url);
|
|
782
|
+
} catch (error) {
|
|
783
|
+
popup?.close();
|
|
784
|
+
if (mounted.current) setStatus(error instanceof AccountRequestError && error.code === "remote-web-origin-not-trusted" ? { status: "remote-web-origin-not-trusted" } : {
|
|
785
|
+
status: "error",
|
|
786
|
+
message: error instanceof Error ? error.message : t("requestFailed")
|
|
787
|
+
});
|
|
788
|
+
} finally {
|
|
789
|
+
if (mounted.current) setBusy(false);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
const copyTrustedOriginCommand = async () => {
|
|
793
|
+
setCopyFailed(false);
|
|
794
|
+
try {
|
|
795
|
+
if (navigator.clipboard?.writeText === void 0) throw new Error("clipboard unavailable");
|
|
796
|
+
await navigator.clipboard.writeText(trustedOriginCommand);
|
|
797
|
+
if (mounted.current) setCopied(true);
|
|
798
|
+
} catch {
|
|
799
|
+
if (mounted.current) setCopyFailed(true);
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
const signOut = async () => {
|
|
803
|
+
setBusy(true);
|
|
804
|
+
try {
|
|
805
|
+
await jsonRequest(OPENAI_CODEX_AUTH_LOGOUT_PATH, "POST");
|
|
806
|
+
if (mounted.current) setStatus({ status: "signed-out" });
|
|
807
|
+
} catch (error) {
|
|
808
|
+
if (mounted.current) setStatus({
|
|
809
|
+
status: "error",
|
|
810
|
+
message: error instanceof Error ? error.message : t("requestFailed")
|
|
811
|
+
});
|
|
812
|
+
} finally {
|
|
813
|
+
if (mounted.current) setBusy(false);
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
const label = status.status === "signed-in" ? t("signedIn") : status.status === "loading" ? t("loadingAccount") : status.status === "signing-in" ? t("signingIn") : status.status === "reauth-required" ? t("reauthRequired") : status.status === "remote-web-origin-not-trusted" ? t("remoteOriginTitle") : status.status === "error" ? t("requestFailed") : t("signedOut");
|
|
817
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
818
|
+
style: embedded ? embeddedPageStyle : pageStyle,
|
|
819
|
+
...embedded ? { "aria-label": t("title") } : { "aria-labelledby": "openai-codex-settings-title" },
|
|
820
|
+
children: [embedded ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
821
|
+
id: "openai-codex-settings-title",
|
|
822
|
+
style: titleStyle,
|
|
823
|
+
children: t("title")
|
|
824
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
825
|
+
style: {
|
|
826
|
+
...bodyStyle,
|
|
827
|
+
marginTop: 6
|
|
828
|
+
},
|
|
829
|
+
children: t("intro")
|
|
830
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
831
|
+
style: embedded ? embeddedCardStyle : cardStyle$1,
|
|
832
|
+
children: [
|
|
833
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
834
|
+
style: quotaTitleStyle,
|
|
835
|
+
children: t("accountHeading")
|
|
836
|
+
}),
|
|
837
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
838
|
+
style: rowStyle,
|
|
839
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
840
|
+
style: statusStyle,
|
|
841
|
+
role: "status",
|
|
842
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
843
|
+
"aria-hidden": "true",
|
|
844
|
+
style: dotStyle(status.status)
|
|
845
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
846
|
+
}), status.status === "loading" || status.status === "remote-web-origin-not-trusted" ? null : status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
847
|
+
type: "button",
|
|
848
|
+
style: buttonStyle,
|
|
849
|
+
disabled: busy,
|
|
850
|
+
onClick: () => {
|
|
851
|
+
signOut();
|
|
852
|
+
},
|
|
853
|
+
children: busy ? t("working") : t("logout")
|
|
854
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
855
|
+
type: "button",
|
|
856
|
+
style: primaryButtonStyle,
|
|
857
|
+
disabled: busy,
|
|
858
|
+
onClick: () => {
|
|
859
|
+
signIn();
|
|
860
|
+
},
|
|
861
|
+
children: busy ? t("working") : status.status === "error" || status.status === "reauth-required" ? t("loginAgain") : t("login")
|
|
862
|
+
})]
|
|
863
|
+
}),
|
|
864
|
+
status.status === "error" || status.status === "reauth-required" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
865
|
+
style: errorStyle,
|
|
866
|
+
children: status.message
|
|
867
|
+
}) : null,
|
|
868
|
+
status.status === "remote-web-origin-not-trusted" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
869
|
+
style: {
|
|
870
|
+
display: "flex",
|
|
871
|
+
flexDirection: "column",
|
|
872
|
+
gap: 10
|
|
873
|
+
},
|
|
874
|
+
children: [
|
|
875
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
876
|
+
style: errorStyle,
|
|
877
|
+
children: t("remoteOriginDescription")
|
|
878
|
+
}),
|
|
879
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
880
|
+
style: bodyStyle,
|
|
881
|
+
children: t("remoteOriginCommandHelp")
|
|
882
|
+
}),
|
|
883
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
884
|
+
style: commandStyle,
|
|
885
|
+
children: trustedOriginCommand
|
|
886
|
+
}),
|
|
887
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
888
|
+
style: rowStyle,
|
|
889
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
890
|
+
type: "button",
|
|
891
|
+
style: buttonStyle,
|
|
892
|
+
onClick: () => {
|
|
893
|
+
copyTrustedOriginCommand();
|
|
894
|
+
},
|
|
895
|
+
children: copied ? t("remoteOriginCopied") : t("remoteOriginCopy")
|
|
896
|
+
}), copyFailed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
897
|
+
style: errorStyle,
|
|
898
|
+
children: t("remoteOriginCopyFailed")
|
|
899
|
+
}) : null]
|
|
900
|
+
})
|
|
901
|
+
]
|
|
902
|
+
}) : null,
|
|
903
|
+
status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageLimits, {
|
|
904
|
+
usage: status.usage,
|
|
905
|
+
...status.quotaError === void 0 ? {} : { quotaError: status.quotaError },
|
|
906
|
+
t
|
|
907
|
+
}) : null,
|
|
908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenAICodexConfiguration, {
|
|
909
|
+
t,
|
|
910
|
+
...configScope === void 0 ? {} : { scope: configScope }
|
|
911
|
+
})
|
|
912
|
+
]
|
|
913
|
+
})]
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
//#endregion
|
|
917
|
+
//#region src/client/OpenAICodexPluginCard.tsx
|
|
918
|
+
/** OpenAI Codex account card contributed to Harness Plugin configuration. */
|
|
919
|
+
const cardStyle = {
|
|
920
|
+
overflow: "hidden",
|
|
921
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
922
|
+
borderRadius: 10,
|
|
923
|
+
background: "var(--dsw-alias-bg-module-platform)"
|
|
924
|
+
};
|
|
925
|
+
const headerStyle = {
|
|
926
|
+
boxSizing: "border-box",
|
|
927
|
+
width: "100%",
|
|
928
|
+
display: "flex",
|
|
929
|
+
alignItems: "center",
|
|
930
|
+
justifyContent: "space-between",
|
|
931
|
+
gap: 16,
|
|
932
|
+
border: 0,
|
|
933
|
+
padding: "13px 14px",
|
|
934
|
+
background: "transparent",
|
|
935
|
+
color: "var(--dsw-alias-label-primary)",
|
|
936
|
+
font: "inherit",
|
|
937
|
+
textAlign: "left",
|
|
938
|
+
cursor: "pointer"
|
|
939
|
+
};
|
|
940
|
+
const headTextStyle = {
|
|
941
|
+
display: "flex",
|
|
942
|
+
minWidth: 0,
|
|
943
|
+
flexDirection: "column",
|
|
944
|
+
gap: 3
|
|
945
|
+
};
|
|
946
|
+
const nameStyle = {
|
|
947
|
+
fontSize: 14,
|
|
948
|
+
lineHeight: "20px",
|
|
949
|
+
fontWeight: 600
|
|
950
|
+
};
|
|
951
|
+
const descriptionStyle = {
|
|
952
|
+
fontSize: 13,
|
|
953
|
+
lineHeight: "18px",
|
|
954
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
955
|
+
};
|
|
956
|
+
const chevronStyle = {
|
|
957
|
+
flex: "0 0 auto",
|
|
958
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
959
|
+
transition: "transform 160ms ease"
|
|
960
|
+
};
|
|
961
|
+
const cardBodyStyle = {
|
|
962
|
+
borderTop: "1px solid var(--dsw-alias-border-l2)",
|
|
963
|
+
padding: "16px 14px 18px"
|
|
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
|
+
}
|
|
986
|
+
/** Render account management as one expandable Plugin configuration card. */
|
|
987
|
+
function OpenAICodexPluginCard({ t, configScope }) {
|
|
988
|
+
if (t === void 0) throw new Error("OpenAI Codex plugin card requires its translation function");
|
|
989
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
990
|
+
const title = t("title");
|
|
991
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
992
|
+
style: cardStyle,
|
|
993
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
994
|
+
type: "button",
|
|
995
|
+
style: headerStyle,
|
|
996
|
+
"aria-expanded": open,
|
|
997
|
+
"aria-label": `${t(open ? "collapse" : "expand")}: ${title}`,
|
|
998
|
+
onClick: () => {
|
|
999
|
+
setOpen(!open);
|
|
1000
|
+
},
|
|
1001
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1002
|
+
style: headTextStyle,
|
|
1003
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1004
|
+
style: nameStyle,
|
|
1005
|
+
children: title
|
|
1006
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1007
|
+
style: descriptionStyle,
|
|
1008
|
+
children: t("intro")
|
|
1009
|
+
})]
|
|
1010
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NativeChevronDown, { open })]
|
|
1011
|
+
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1012
|
+
style: cardBodyStyle,
|
|
1013
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenAICodexSettings, {
|
|
1014
|
+
t,
|
|
1015
|
+
embedded: true,
|
|
1016
|
+
...configScope === void 0 ? {} : { configScope }
|
|
1017
|
+
})
|
|
1018
|
+
}) : null]
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
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
|
|
1440
|
+
//#region src/client/locales.ts
|
|
1441
|
+
/** English copy for the OpenAI Codex Plugin configuration card. */
|
|
1442
|
+
const en = {
|
|
1443
|
+
title: "Codex Connect",
|
|
1444
|
+
intro: "Use your ChatGPT subscription in dsh without an API key.",
|
|
1445
|
+
accountHeading: "ChatGPT account",
|
|
1446
|
+
expand: "Expand settings",
|
|
1447
|
+
collapse: "Collapse settings",
|
|
1448
|
+
loadingAccount: "Loading account…",
|
|
1449
|
+
signedOut: "Not signed in",
|
|
1450
|
+
signingIn: "Waiting for browser authorization…",
|
|
1451
|
+
signedIn: "Signed in",
|
|
1452
|
+
reauthRequired: "Sign in again",
|
|
1453
|
+
login: "Sign in with ChatGPT",
|
|
1454
|
+
loginAgain: "Sign in again",
|
|
1455
|
+
logout: "Sign out",
|
|
1456
|
+
working: "Working…",
|
|
1457
|
+
retry: "Retry",
|
|
1458
|
+
popupBlocked: "The browser blocked the sign-in window. Allow pop-ups for this dsh page and retry.",
|
|
1459
|
+
usageLimits: "Usage limits",
|
|
1460
|
+
fiveHourLimit: "5-hour limit",
|
|
1461
|
+
weeklyLimit: "Weekly limit",
|
|
1462
|
+
hourLimit: "{count}-hour limit",
|
|
1463
|
+
usageWindow: "Usage window",
|
|
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.",
|
|
1473
|
+
monthlyLimit: "Monthly credit limit",
|
|
1474
|
+
exactRemaining: "{remaining} of {limit} credits remaining",
|
|
1475
|
+
credits: "Credits",
|
|
1476
|
+
unlimited: "Unlimited",
|
|
1477
|
+
available: "Available",
|
|
1478
|
+
quotaUnavailable: "Usage limits are temporarily unavailable.",
|
|
1479
|
+
requestFailed: "The OpenAI Codex account request failed.",
|
|
1480
|
+
remoteOriginTitle: "Remote browser origin is not trusted",
|
|
1481
|
+
remoteOriginDescription: "Codex Connect accepts browser OAuth requests only from trusted local pages or origins explicitly approved by the device owner.",
|
|
1482
|
+
remoteOriginCommandHelp: "Run this command manually on the device that runs DSH, then reload this page. This page never runs it:",
|
|
1483
|
+
remoteOriginCopy: "Copy trust command",
|
|
1484
|
+
remoteOriginCopied: "Copied",
|
|
1485
|
+
remoteOriginCopyFailed: "Copy was unavailable. Copy the command manually.",
|
|
1486
|
+
capabilitiesHeading: "Optional capabilities",
|
|
1487
|
+
capabilitiesIntro: "Choose which extra Codex capabilities this dsh profile may register.",
|
|
1488
|
+
enableSearch: "Enable Codex search provider",
|
|
1489
|
+
enableSearchHelp: "Makes OpenAI Codex available as a search provider. It does not select the global search route.",
|
|
1490
|
+
searchModel: "Search model",
|
|
1491
|
+
searchMode: "Web access",
|
|
1492
|
+
modeCached: "Cached",
|
|
1493
|
+
modeIndexed: "Indexed",
|
|
1494
|
+
modeLive: "Live web",
|
|
1495
|
+
searchContextSize: "Search context",
|
|
1496
|
+
contextLow: "Low",
|
|
1497
|
+
contextMedium: "Medium",
|
|
1498
|
+
contextHigh: "High",
|
|
1499
|
+
searchMaxOutputTokens: "Maximum search output tokens",
|
|
1500
|
+
enableImageTool: "Enable view_image tool",
|
|
1501
|
+
enableImageToolHelp: "Allows approved local reads and public-network image fetches for vision-capable models.",
|
|
1502
|
+
routingNote: "These settings never change the default model or the profile’s global search route.",
|
|
1503
|
+
settingsLoading: "Loading plugin settings…",
|
|
1504
|
+
settingsUnavailable: "Plugin settings are unavailable in this dsh profile.",
|
|
1505
|
+
settingsReadOnly: "This profile exposes plugin settings as read-only.",
|
|
1506
|
+
invalidSearchModel: "Enter a search model.",
|
|
1507
|
+
invalidSearchTokens: "Maximum search output tokens must be a positive whole number.",
|
|
1508
|
+
save: "Save changes",
|
|
1509
|
+
saving: "Saving…",
|
|
1510
|
+
discard: "Discard",
|
|
1511
|
+
settingsSaved: "Saved",
|
|
1512
|
+
settingsSaveFailed: "The settings could not be saved. The current Host values were restored."
|
|
1513
|
+
};
|
|
1514
|
+
/** Chinese copy for the OpenAI Codex Plugin configuration card. */
|
|
1515
|
+
const zh = {
|
|
1516
|
+
title: "Codex Connect",
|
|
1517
|
+
intro: "使用 ChatGPT 订阅在 dsh 中调用模型,无需 API Key。",
|
|
1518
|
+
accountHeading: "ChatGPT 账户",
|
|
1519
|
+
expand: "展开设置",
|
|
1520
|
+
collapse: "折叠设置",
|
|
1521
|
+
loadingAccount: "正在加载账户信息…",
|
|
1522
|
+
signedOut: "尚未登录",
|
|
1523
|
+
signingIn: "正在等待浏览器授权…",
|
|
1524
|
+
signedIn: "已登录",
|
|
1525
|
+
reauthRequired: "需要重新登录",
|
|
1526
|
+
login: "使用 ChatGPT 登录",
|
|
1527
|
+
loginAgain: "重新登录",
|
|
1528
|
+
logout: "退出登录",
|
|
1529
|
+
working: "处理中…",
|
|
1530
|
+
retry: "重试",
|
|
1531
|
+
popupBlocked: "浏览器阻止了登录窗口。请允许此 dsh 页面弹出窗口后重试。",
|
|
1532
|
+
usageLimits: "使用额度",
|
|
1533
|
+
fiveHourLimit: "5 小时额度",
|
|
1534
|
+
weeklyLimit: "每周额度",
|
|
1535
|
+
hourLimit: "{count} 小时额度",
|
|
1536
|
+
usageWindow: "使用额度",
|
|
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。",
|
|
1546
|
+
monthlyLimit: "每月信用额度",
|
|
1547
|
+
exactRemaining: "剩余 {remaining} / {limit} credits",
|
|
1548
|
+
credits: "Credits",
|
|
1549
|
+
unlimited: "无限",
|
|
1550
|
+
available: "可用",
|
|
1551
|
+
quotaUnavailable: "暂时无法获取使用额度。",
|
|
1552
|
+
requestFailed: "OpenAI Codex 账户请求失败。",
|
|
1553
|
+
remoteOriginTitle: "未信任远程浏览器 origin",
|
|
1554
|
+
remoteOriginDescription: "Codex Connect 只接受来自本机页面或设备所有者明确批准的 origin 的浏览器 OAuth 请求。",
|
|
1555
|
+
remoteOriginCommandHelp: "请在运行 DSH 的设备上手动执行下面的服务器命令,然后重新加载此页面。本页面不会自动执行:",
|
|
1556
|
+
remoteOriginCopy: "复制授权命令",
|
|
1557
|
+
remoteOriginCopied: "已复制",
|
|
1558
|
+
remoteOriginCopyFailed: "当前无法访问剪贴板,请手动复制命令。",
|
|
1559
|
+
capabilitiesHeading: "可选能力",
|
|
1560
|
+
capabilitiesIntro: "选择允许此 dsh profile 注册哪些额外的 Codex 能力。",
|
|
1561
|
+
enableSearch: "启用 Codex 搜索提供方",
|
|
1562
|
+
enableSearchHelp: "让 OpenAI Codex 可被选作搜索提供方,但不会自动改动全局搜索路由。",
|
|
1563
|
+
searchModel: "搜索模型",
|
|
1564
|
+
searchMode: "联网方式",
|
|
1565
|
+
modeCached: "缓存",
|
|
1566
|
+
modeIndexed: "索引",
|
|
1567
|
+
modeLive: "实时联网",
|
|
1568
|
+
searchContextSize: "搜索上下文",
|
|
1569
|
+
contextLow: "低",
|
|
1570
|
+
contextMedium: "中",
|
|
1571
|
+
contextHigh: "高",
|
|
1572
|
+
searchMaxOutputTokens: "搜索最大输出 Tokens",
|
|
1573
|
+
enableImageTool: "启用 view_image 工具",
|
|
1574
|
+
enableImageToolHelp: "允许具备视觉能力的模型在审批后读取本地图片或获取公网图片。",
|
|
1575
|
+
routingNote: "这些设置绝不会改动默认模型,也不会接管此 profile 的全局搜索路由。",
|
|
1576
|
+
settingsLoading: "正在加载插件设置…",
|
|
1577
|
+
settingsUnavailable: "此 dsh profile 无法使用插件设置。",
|
|
1578
|
+
settingsReadOnly: "此 profile 的插件设置为只读。",
|
|
1579
|
+
invalidSearchModel: "请输入搜索模型。",
|
|
1580
|
+
invalidSearchTokens: "搜索最大输出 Tokens 必须是正整数。",
|
|
1581
|
+
save: "保存更改",
|
|
1582
|
+
saving: "正在保存…",
|
|
1583
|
+
discard: "放弃更改",
|
|
1584
|
+
settingsSaved: "已保存",
|
|
1585
|
+
settingsSaveFailed: "设置未能保存,已恢复 Host 当前值。"
|
|
1586
|
+
};
|
|
1587
|
+
//#endregion
|
|
1588
|
+
//#region src/client/index.tsx
|
|
1589
|
+
/** Stable browser-plugin name. */
|
|
1590
|
+
const name = "dsh-codex-connect-client";
|
|
1591
|
+
/** Client services required by the Plugin configuration contribution. */
|
|
1592
|
+
const inject = [
|
|
1593
|
+
"slots",
|
|
1594
|
+
"locale",
|
|
1595
|
+
"connection",
|
|
1596
|
+
"remote",
|
|
1597
|
+
"settingsScope"
|
|
1598
|
+
];
|
|
1599
|
+
/** Register account copy and the OpenAI Codex card under Plugin configuration. */
|
|
1600
|
+
function apply(ctx) {
|
|
1601
|
+
const namespace = "settings.openai-codex";
|
|
1602
|
+
ctx.effect(() => ctx.locale.register(namespace, {
|
|
1603
|
+
zh,
|
|
1604
|
+
en
|
|
1605
|
+
}), "dsh-codex-connect: settings copy");
|
|
1606
|
+
const t = ctx.locale.bind(namespace);
|
|
1607
|
+
const configScope = ctx.settingsScope.bind({
|
|
1608
|
+
namespace: OPENAI_CODEX_SETTINGS_NAMESPACE,
|
|
1609
|
+
decode: decodeOpenAICodexSettings
|
|
1610
|
+
});
|
|
1611
|
+
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
1612
|
+
name: "settings.plugin.item",
|
|
1613
|
+
key: OPENAI_CODEX_SETTINGS_NAMESPACE,
|
|
1614
|
+
inject: () => ({
|
|
1615
|
+
t,
|
|
1616
|
+
configScope
|
|
1617
|
+
})
|
|
1618
|
+
}, OpenAICodexPluginCard));
|
|
1619
|
+
ctx.inject(["slots", "modelDirectories"], (scope) => {
|
|
1620
|
+
scope.slots.inject("conversation.input.right", () => scope.slots.register({
|
|
1621
|
+
name: "conversation.input.right",
|
|
1622
|
+
id: "openai-codex-fast-mode",
|
|
1623
|
+
order: 10,
|
|
1624
|
+
locale: namespace,
|
|
1625
|
+
inject: (sessionId) => ({ directory: scope.modelDirectories.directoryFor(sessionId).store })
|
|
1626
|
+
}, OpenAICodexFastModeToggle));
|
|
1627
|
+
scope.slots.inject("conversation.input.right", () => scope.slots.register({
|
|
1628
|
+
name: "conversation.input.right",
|
|
1629
|
+
id: "openai-codex-quota",
|
|
1630
|
+
order: 20,
|
|
1631
|
+
locale: namespace,
|
|
1632
|
+
inject: (sessionId) => ({ directory: scope.modelDirectories.directoryFor(sessionId).store })
|
|
1633
|
+
}, OpenAICodexQuotaIndicator));
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
//#endregion
|
|
1637
|
+
exports.apply = apply;
|
|
1638
|
+
exports.inject = inject;
|
|
1639
|
+
exports.name = name;
|
|
1640
|
+
return module.exports;
|
|
1641
|
+
}
|
|
1642
|
+
});
|