dsh-codex-connect 0.1.0-alpha.4.3
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 +72 -0
- package/LICENSE +202 -0
- package/MIGRATION.md +12 -0
- package/NOTICE +15 -0
- package/README.i18n.yaml +6 -0
- package/README.md +110 -0
- package/README.zh.md +93 -0
- package/cordis.patch.yml +8 -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 +145 -0
- package/lib/client.js +1086 -0
- package/lib/index.d.ts +333 -0
- package/lib/index.js +2 -0
- package/lib/invariant.d.ts +14 -0
- package/lib/invariant.js +15 -0
- package/lib/src-ILOioCkA.js +1484 -0
- package/package.json +145 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1086 @@
|
|
|
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(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(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 = 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
|
+
function progressFillStyle(percent) {
|
|
577
|
+
return {
|
|
578
|
+
width: `${Math.max(0, Math.min(100, percent))}%`,
|
|
579
|
+
height: "100%",
|
|
580
|
+
borderRadius: "inherit",
|
|
581
|
+
background: "var(--dsw-alias-brand-primary, #1677ff)"
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
function windowLabel(seconds, t) {
|
|
585
|
+
if (seconds === 18e3) return t("fiveHourLimit");
|
|
586
|
+
if (seconds === 604800) return t("weeklyLimit");
|
|
587
|
+
const hours = seconds / 3600;
|
|
588
|
+
return Number.isInteger(hours) ? t("hourLimit", { count: hours }) : t("usageWindow");
|
|
589
|
+
}
|
|
590
|
+
function formatPercent(percent) {
|
|
591
|
+
return new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(percent);
|
|
592
|
+
}
|
|
593
|
+
function QuotaBar({ label, percent, detail, t }) {
|
|
594
|
+
const display = formatPercent(percent);
|
|
595
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
596
|
+
style: quotaGroupStyle,
|
|
597
|
+
children: [
|
|
598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
599
|
+
style: quotaLabelStyle,
|
|
600
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("percentRemaining", { percent: display }) })]
|
|
601
|
+
}),
|
|
602
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
603
|
+
style: progressTrackStyle,
|
|
604
|
+
role: "progressbar",
|
|
605
|
+
"aria-label": label,
|
|
606
|
+
"aria-valuemin": 0,
|
|
607
|
+
"aria-valuemax": 100,
|
|
608
|
+
"aria-valuenow": percent,
|
|
609
|
+
"aria-valuetext": t("percentRemaining", { percent: display }),
|
|
610
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: progressFillStyle(percent) })
|
|
611
|
+
}),
|
|
612
|
+
detail === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
613
|
+
style: bodyStyle,
|
|
614
|
+
children: detail
|
|
615
|
+
})
|
|
616
|
+
]
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
function UsageLimits({ usage, quotaError, t }) {
|
|
620
|
+
const hasData = usage.rateLimits.length > 0 || usage.credits !== void 0 || usage.individualLimit !== void 0;
|
|
621
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
622
|
+
style: quotaListStyle,
|
|
623
|
+
children: [
|
|
624
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
625
|
+
style: quotaTitleStyle,
|
|
626
|
+
children: t("usageLimits")
|
|
627
|
+
}),
|
|
628
|
+
usage.rateLimits.map((limit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
629
|
+
style: quotaGroupStyle,
|
|
630
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
|
|
631
|
+
style: quotaTitleStyle,
|
|
632
|
+
children: limit.name ?? limit.id
|
|
633
|
+
}), limit.windows.map((window) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaBar, {
|
|
634
|
+
label: windowLabel(window.windowSeconds, t),
|
|
635
|
+
percent: window.remainingPercent,
|
|
636
|
+
t
|
|
637
|
+
}, window.windowSeconds))]
|
|
638
|
+
}, limit.id)),
|
|
639
|
+
usage.individualLimit === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaBar, {
|
|
640
|
+
label: t("monthlyLimit"),
|
|
641
|
+
percent: usage.individualLimit.remainingPercent,
|
|
642
|
+
detail: t("exactRemaining", {
|
|
643
|
+
remaining: usage.individualLimit.remaining,
|
|
644
|
+
limit: usage.individualLimit.limit
|
|
645
|
+
}),
|
|
646
|
+
t
|
|
647
|
+
}),
|
|
648
|
+
usage.credits === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
649
|
+
style: quotaLabelStyle,
|
|
650
|
+
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 })]
|
|
651
|
+
}),
|
|
652
|
+
!hasData && quotaError === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
653
|
+
style: bodyStyle,
|
|
654
|
+
children: t("quotaUnavailable")
|
|
655
|
+
}) : null,
|
|
656
|
+
quotaError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
657
|
+
style: errorStyle,
|
|
658
|
+
children: t("quotaUnavailable")
|
|
659
|
+
})
|
|
660
|
+
]
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
function dotStyle(status) {
|
|
664
|
+
return {
|
|
665
|
+
width: 9,
|
|
666
|
+
height: 9,
|
|
667
|
+
borderRadius: "50%",
|
|
668
|
+
flex: "0 0 auto",
|
|
669
|
+
background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" ? "var(--dsw-alias-state-error-primary, #d92d20)" : status === "signing-in" || status === "loading" ? "var(--dsw-alias-brand-primary, #1677ff)" : "var(--dsw-alias-label-dimmed, #9aa0a6)"
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
async function jsonRequest(path, method = "GET", signal) {
|
|
673
|
+
const response = await fetch(path, {
|
|
674
|
+
method,
|
|
675
|
+
headers: { accept: "application/json" },
|
|
676
|
+
credentials: "same-origin",
|
|
677
|
+
...signal === void 0 ? {} : { signal }
|
|
678
|
+
});
|
|
679
|
+
const value = await response.json().catch(() => void 0);
|
|
680
|
+
if (!response.ok) {
|
|
681
|
+
const message = typeof value === "object" && value !== null && "error" in value && typeof value.error === "string" ? value.error : `HTTP ${response.status}`;
|
|
682
|
+
throw new Error(message);
|
|
683
|
+
}
|
|
684
|
+
return value;
|
|
685
|
+
}
|
|
686
|
+
/** OpenAI Codex account status and OAuth actions. */
|
|
687
|
+
function OpenAICodexSettings({ t, configScope, embedded = false }) {
|
|
688
|
+
if (t === void 0) throw new Error("OpenAI Codex settings requires its translation function");
|
|
689
|
+
const [status, setStatus] = (0, react.useState)({ status: "loading" });
|
|
690
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
691
|
+
const mounted = (0, react.useRef)(true);
|
|
692
|
+
(0, react.useEffect)(() => {
|
|
693
|
+
mounted.current = true;
|
|
694
|
+
return () => {
|
|
695
|
+
mounted.current = false;
|
|
696
|
+
};
|
|
697
|
+
}, []);
|
|
698
|
+
const refresh = (0, react.useCallback)(async (signal) => {
|
|
699
|
+
try {
|
|
700
|
+
const nextStatus = await jsonRequest(OPENAI_CODEX_AUTH_STATUS_PATH, "GET", signal);
|
|
701
|
+
if (mounted.current && signal?.aborted !== true) setStatus(nextStatus);
|
|
702
|
+
} catch (error) {
|
|
703
|
+
if (mounted.current && signal?.aborted !== true) setStatus({
|
|
704
|
+
status: "error",
|
|
705
|
+
message: error instanceof Error ? error.message : t("requestFailed")
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
}, [t]);
|
|
709
|
+
(0, react.useEffect)(() => {
|
|
710
|
+
const controller = new AbortController();
|
|
711
|
+
refresh(controller.signal);
|
|
712
|
+
return () => {
|
|
713
|
+
controller.abort();
|
|
714
|
+
};
|
|
715
|
+
}, [refresh]);
|
|
716
|
+
(0, react.useEffect)(() => {
|
|
717
|
+
const interval = status.status === "signing-in" ? POLL_INTERVAL_MS : status.status === "signed-in" ? USAGE_POLL_INTERVAL_MS : void 0;
|
|
718
|
+
if (interval === void 0) return;
|
|
719
|
+
const controller = new AbortController();
|
|
720
|
+
const timer = window.setInterval(() => {
|
|
721
|
+
refresh(controller.signal);
|
|
722
|
+
}, interval);
|
|
723
|
+
return () => {
|
|
724
|
+
window.clearInterval(timer);
|
|
725
|
+
controller.abort();
|
|
726
|
+
};
|
|
727
|
+
}, [refresh, status.status]);
|
|
728
|
+
const signIn = async () => {
|
|
729
|
+
const popup = window.open("about:blank", "_blank");
|
|
730
|
+
if (popup === null) {
|
|
731
|
+
setStatus({
|
|
732
|
+
status: "error",
|
|
733
|
+
message: t("popupBlocked")
|
|
734
|
+
});
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
popup.opener = null;
|
|
738
|
+
setBusy(true);
|
|
739
|
+
setStatus({ status: "signing-in" });
|
|
740
|
+
try {
|
|
741
|
+
const challenge = await jsonRequest(OPENAI_CODEX_AUTH_LOGIN_PATH, "POST");
|
|
742
|
+
if (!mounted.current) {
|
|
743
|
+
popup.close();
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
popup.location.replace(challenge.url);
|
|
747
|
+
} catch (error) {
|
|
748
|
+
popup?.close();
|
|
749
|
+
if (mounted.current) setStatus({
|
|
750
|
+
status: "error",
|
|
751
|
+
message: error instanceof Error ? error.message : t("requestFailed")
|
|
752
|
+
});
|
|
753
|
+
} finally {
|
|
754
|
+
if (mounted.current) setBusy(false);
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
const signOut = async () => {
|
|
758
|
+
setBusy(true);
|
|
759
|
+
try {
|
|
760
|
+
await jsonRequest(OPENAI_CODEX_AUTH_LOGOUT_PATH, "POST");
|
|
761
|
+
if (mounted.current) setStatus({ status: "signed-out" });
|
|
762
|
+
} catch (error) {
|
|
763
|
+
if (mounted.current) setStatus({
|
|
764
|
+
status: "error",
|
|
765
|
+
message: error instanceof Error ? error.message : t("requestFailed")
|
|
766
|
+
});
|
|
767
|
+
} finally {
|
|
768
|
+
if (mounted.current) setBusy(false);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
const label = status.status === "signed-in" ? t("signedIn") : status.status === "loading" ? t("loadingAccount") : status.status === "signing-in" ? t("signingIn") : status.status === "error" ? t("requestFailed") : t("signedOut");
|
|
772
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
773
|
+
style: embedded ? embeddedPageStyle : pageStyle,
|
|
774
|
+
...embedded ? { "aria-label": t("title") } : { "aria-labelledby": "openai-codex-settings-title" },
|
|
775
|
+
children: [embedded ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
776
|
+
id: "openai-codex-settings-title",
|
|
777
|
+
style: titleStyle,
|
|
778
|
+
children: t("title")
|
|
779
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
780
|
+
style: {
|
|
781
|
+
...bodyStyle,
|
|
782
|
+
marginTop: 6
|
|
783
|
+
},
|
|
784
|
+
children: t("intro")
|
|
785
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
786
|
+
style: embedded ? embeddedCardStyle : cardStyle$1,
|
|
787
|
+
children: [
|
|
788
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
789
|
+
style: quotaTitleStyle,
|
|
790
|
+
children: t("accountHeading")
|
|
791
|
+
}),
|
|
792
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
793
|
+
style: rowStyle,
|
|
794
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
795
|
+
style: statusStyle,
|
|
796
|
+
role: "status",
|
|
797
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
798
|
+
"aria-hidden": "true",
|
|
799
|
+
style: dotStyle(status.status)
|
|
800
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
|
|
801
|
+
}), status.status === "loading" ? null : status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
802
|
+
type: "button",
|
|
803
|
+
style: buttonStyle,
|
|
804
|
+
disabled: busy,
|
|
805
|
+
onClick: () => {
|
|
806
|
+
signOut();
|
|
807
|
+
},
|
|
808
|
+
children: busy ? t("working") : t("logout")
|
|
809
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
810
|
+
type: "button",
|
|
811
|
+
style: primaryButtonStyle,
|
|
812
|
+
disabled: busy,
|
|
813
|
+
onClick: () => {
|
|
814
|
+
signIn();
|
|
815
|
+
},
|
|
816
|
+
children: busy ? t("working") : status.status === "error" ? t("loginAgain") : t("login")
|
|
817
|
+
})]
|
|
818
|
+
}),
|
|
819
|
+
status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
820
|
+
style: errorStyle,
|
|
821
|
+
children: status.message
|
|
822
|
+
}) : null,
|
|
823
|
+
status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageLimits, {
|
|
824
|
+
usage: status.usage,
|
|
825
|
+
...status.quotaError === void 0 ? {} : { quotaError: status.quotaError },
|
|
826
|
+
t
|
|
827
|
+
}) : null,
|
|
828
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenAICodexConfiguration, {
|
|
829
|
+
t,
|
|
830
|
+
...configScope === void 0 ? {} : { scope: configScope }
|
|
831
|
+
})
|
|
832
|
+
]
|
|
833
|
+
})]
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/client/OpenAICodexPluginCard.tsx
|
|
838
|
+
/** OpenAI Codex account card contributed to Harness Plugin configuration. */
|
|
839
|
+
const cardStyle = {
|
|
840
|
+
overflow: "hidden",
|
|
841
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
842
|
+
borderRadius: 10,
|
|
843
|
+
background: "var(--dsw-alias-bg-module-platform)"
|
|
844
|
+
};
|
|
845
|
+
const headerStyle = {
|
|
846
|
+
boxSizing: "border-box",
|
|
847
|
+
width: "100%",
|
|
848
|
+
display: "flex",
|
|
849
|
+
alignItems: "center",
|
|
850
|
+
justifyContent: "space-between",
|
|
851
|
+
gap: 16,
|
|
852
|
+
border: 0,
|
|
853
|
+
padding: "13px 14px",
|
|
854
|
+
background: "transparent",
|
|
855
|
+
color: "var(--dsw-alias-label-primary)",
|
|
856
|
+
font: "inherit",
|
|
857
|
+
textAlign: "left",
|
|
858
|
+
cursor: "pointer"
|
|
859
|
+
};
|
|
860
|
+
const headTextStyle = {
|
|
861
|
+
display: "flex",
|
|
862
|
+
minWidth: 0,
|
|
863
|
+
flexDirection: "column",
|
|
864
|
+
gap: 3
|
|
865
|
+
};
|
|
866
|
+
const nameStyle = {
|
|
867
|
+
fontSize: 14,
|
|
868
|
+
lineHeight: "20px",
|
|
869
|
+
fontWeight: 600
|
|
870
|
+
};
|
|
871
|
+
const descriptionStyle = {
|
|
872
|
+
fontSize: 13,
|
|
873
|
+
lineHeight: "18px",
|
|
874
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
875
|
+
};
|
|
876
|
+
const chevronStyle = {
|
|
877
|
+
flex: "0 0 auto",
|
|
878
|
+
fontSize: 18,
|
|
879
|
+
lineHeight: 1,
|
|
880
|
+
transition: "transform 120ms ease"
|
|
881
|
+
};
|
|
882
|
+
const cardBodyStyle = {
|
|
883
|
+
borderTop: "1px solid var(--dsw-alias-border-l2)",
|
|
884
|
+
padding: "16px 14px 18px"
|
|
885
|
+
};
|
|
886
|
+
/** Render account management as one expandable Plugin configuration card. */
|
|
887
|
+
function OpenAICodexPluginCard({ t, configScope }) {
|
|
888
|
+
if (t === void 0) throw new Error("OpenAI Codex plugin card requires its translation function");
|
|
889
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
890
|
+
const title = t("title");
|
|
891
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
892
|
+
style: cardStyle,
|
|
893
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
894
|
+
type: "button",
|
|
895
|
+
style: headerStyle,
|
|
896
|
+
"aria-expanded": open,
|
|
897
|
+
"aria-label": `${t(open ? "collapse" : "expand")}: ${title}`,
|
|
898
|
+
onClick: () => {
|
|
899
|
+
setOpen(!open);
|
|
900
|
+
},
|
|
901
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
902
|
+
style: headTextStyle,
|
|
903
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
904
|
+
style: nameStyle,
|
|
905
|
+
children: title
|
|
906
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
907
|
+
style: descriptionStyle,
|
|
908
|
+
children: t("intro")
|
|
909
|
+
})]
|
|
910
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
911
|
+
"aria-hidden": "true",
|
|
912
|
+
style: {
|
|
913
|
+
...chevronStyle,
|
|
914
|
+
transform: open ? "rotate(180deg)" : "none"
|
|
915
|
+
},
|
|
916
|
+
children: "⌄"
|
|
917
|
+
})]
|
|
918
|
+
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
919
|
+
style: cardBodyStyle,
|
|
920
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpenAICodexSettings, {
|
|
921
|
+
t,
|
|
922
|
+
embedded: true,
|
|
923
|
+
...configScope === void 0 ? {} : { configScope }
|
|
924
|
+
})
|
|
925
|
+
}) : null]
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
//#endregion
|
|
929
|
+
//#region src/client/locales.ts
|
|
930
|
+
/** English copy for the OpenAI Codex Plugin configuration card. */
|
|
931
|
+
const en = {
|
|
932
|
+
title: "Codex Connect",
|
|
933
|
+
intro: "Use your ChatGPT subscription in dsh without an API key.",
|
|
934
|
+
accountHeading: "ChatGPT account",
|
|
935
|
+
expand: "Expand settings",
|
|
936
|
+
collapse: "Collapse settings",
|
|
937
|
+
loadingAccount: "Loading account…",
|
|
938
|
+
signedOut: "Not signed in",
|
|
939
|
+
signingIn: "Waiting for browser authorization…",
|
|
940
|
+
signedIn: "Signed in",
|
|
941
|
+
login: "Sign in with ChatGPT",
|
|
942
|
+
loginAgain: "Sign in again",
|
|
943
|
+
logout: "Sign out",
|
|
944
|
+
working: "Working…",
|
|
945
|
+
retry: "Retry",
|
|
946
|
+
popupBlocked: "The browser blocked the sign-in window. Allow pop-ups for this dsh page and retry.",
|
|
947
|
+
usageLimits: "Usage limits",
|
|
948
|
+
fiveHourLimit: "5-hour limit",
|
|
949
|
+
weeklyLimit: "Weekly limit",
|
|
950
|
+
hourLimit: "{count}-hour limit",
|
|
951
|
+
usageWindow: "Usage window",
|
|
952
|
+
percentRemaining: "{percent}% remaining",
|
|
953
|
+
monthlyLimit: "Monthly credit limit",
|
|
954
|
+
exactRemaining: "{remaining} of {limit} credits remaining",
|
|
955
|
+
credits: "Credits",
|
|
956
|
+
unlimited: "Unlimited",
|
|
957
|
+
available: "Available",
|
|
958
|
+
quotaUnavailable: "Usage limits are temporarily unavailable.",
|
|
959
|
+
requestFailed: "The OpenAI Codex account request failed.",
|
|
960
|
+
capabilitiesHeading: "Optional capabilities",
|
|
961
|
+
capabilitiesIntro: "Choose which extra Codex capabilities this dsh profile may register.",
|
|
962
|
+
enableSearch: "Enable Codex search provider",
|
|
963
|
+
enableSearchHelp: "Makes OpenAI Codex available as a search provider. It does not select the global search route.",
|
|
964
|
+
searchModel: "Search model",
|
|
965
|
+
searchMode: "Web access",
|
|
966
|
+
modeCached: "Cached",
|
|
967
|
+
modeIndexed: "Indexed",
|
|
968
|
+
modeLive: "Live web",
|
|
969
|
+
searchContextSize: "Search context",
|
|
970
|
+
contextLow: "Low",
|
|
971
|
+
contextMedium: "Medium",
|
|
972
|
+
contextHigh: "High",
|
|
973
|
+
searchMaxOutputTokens: "Maximum search output tokens",
|
|
974
|
+
enableImageTool: "Enable view_image tool",
|
|
975
|
+
enableImageToolHelp: "Allows approved local reads and public-network image fetches for vision-capable models.",
|
|
976
|
+
routingNote: "These settings never change the default model or the profile’s global search route.",
|
|
977
|
+
settingsLoading: "Loading plugin settings…",
|
|
978
|
+
settingsUnavailable: "Plugin settings are unavailable in this dsh profile.",
|
|
979
|
+
settingsReadOnly: "This profile exposes plugin settings as read-only.",
|
|
980
|
+
invalidSearchModel: "Enter a search model.",
|
|
981
|
+
invalidSearchTokens: "Maximum search output tokens must be a positive whole number.",
|
|
982
|
+
save: "Save changes",
|
|
983
|
+
saving: "Saving…",
|
|
984
|
+
discard: "Discard",
|
|
985
|
+
settingsSaved: "Saved",
|
|
986
|
+
settingsSaveFailed: "The settings could not be saved. The current Host values were restored."
|
|
987
|
+
};
|
|
988
|
+
/** Chinese copy for the OpenAI Codex Plugin configuration card. */
|
|
989
|
+
const zh = {
|
|
990
|
+
title: "Codex Connect",
|
|
991
|
+
intro: "使用 ChatGPT 订阅在 dsh 中调用模型,无需 API Key。",
|
|
992
|
+
accountHeading: "ChatGPT 账户",
|
|
993
|
+
expand: "展开设置",
|
|
994
|
+
collapse: "折叠设置",
|
|
995
|
+
loadingAccount: "正在加载账户信息…",
|
|
996
|
+
signedOut: "尚未登录",
|
|
997
|
+
signingIn: "正在等待浏览器授权…",
|
|
998
|
+
signedIn: "已登录",
|
|
999
|
+
login: "使用 ChatGPT 登录",
|
|
1000
|
+
loginAgain: "重新登录",
|
|
1001
|
+
logout: "退出登录",
|
|
1002
|
+
working: "处理中…",
|
|
1003
|
+
retry: "重试",
|
|
1004
|
+
popupBlocked: "浏览器阻止了登录窗口。请允许此 dsh 页面弹出窗口后重试。",
|
|
1005
|
+
usageLimits: "使用额度",
|
|
1006
|
+
fiveHourLimit: "5 小时额度",
|
|
1007
|
+
weeklyLimit: "每周额度",
|
|
1008
|
+
hourLimit: "{count} 小时额度",
|
|
1009
|
+
usageWindow: "使用额度",
|
|
1010
|
+
percentRemaining: "剩余 {percent}%",
|
|
1011
|
+
monthlyLimit: "每月信用额度",
|
|
1012
|
+
exactRemaining: "剩余 {remaining} / {limit} credits",
|
|
1013
|
+
credits: "Credits",
|
|
1014
|
+
unlimited: "无限",
|
|
1015
|
+
available: "可用",
|
|
1016
|
+
quotaUnavailable: "暂时无法获取使用额度。",
|
|
1017
|
+
requestFailed: "OpenAI Codex 账户请求失败。",
|
|
1018
|
+
capabilitiesHeading: "可选能力",
|
|
1019
|
+
capabilitiesIntro: "选择允许此 dsh profile 注册哪些额外的 Codex 能力。",
|
|
1020
|
+
enableSearch: "启用 Codex 搜索提供方",
|
|
1021
|
+
enableSearchHelp: "让 OpenAI Codex 可被选作搜索提供方,但不会自动改动全局搜索路由。",
|
|
1022
|
+
searchModel: "搜索模型",
|
|
1023
|
+
searchMode: "联网方式",
|
|
1024
|
+
modeCached: "缓存",
|
|
1025
|
+
modeIndexed: "索引",
|
|
1026
|
+
modeLive: "实时联网",
|
|
1027
|
+
searchContextSize: "搜索上下文",
|
|
1028
|
+
contextLow: "低",
|
|
1029
|
+
contextMedium: "中",
|
|
1030
|
+
contextHigh: "高",
|
|
1031
|
+
searchMaxOutputTokens: "搜索最大输出 Tokens",
|
|
1032
|
+
enableImageTool: "启用 view_image 工具",
|
|
1033
|
+
enableImageToolHelp: "允许具备视觉能力的模型在审批后读取本地图片或获取公网图片。",
|
|
1034
|
+
routingNote: "这些设置绝不会改动默认模型,也不会接管此 profile 的全局搜索路由。",
|
|
1035
|
+
settingsLoading: "正在加载插件设置…",
|
|
1036
|
+
settingsUnavailable: "此 dsh profile 无法使用插件设置。",
|
|
1037
|
+
settingsReadOnly: "此 profile 的插件设置为只读。",
|
|
1038
|
+
invalidSearchModel: "请输入搜索模型。",
|
|
1039
|
+
invalidSearchTokens: "搜索最大输出 Tokens 必须是正整数。",
|
|
1040
|
+
save: "保存更改",
|
|
1041
|
+
saving: "正在保存…",
|
|
1042
|
+
discard: "放弃更改",
|
|
1043
|
+
settingsSaved: "已保存",
|
|
1044
|
+
settingsSaveFailed: "设置未能保存,已恢复 Host 当前值。"
|
|
1045
|
+
};
|
|
1046
|
+
//#endregion
|
|
1047
|
+
//#region src/client/index.tsx
|
|
1048
|
+
/** Stable browser-plugin name. */
|
|
1049
|
+
const name = "dsh-codex-connect-client";
|
|
1050
|
+
/** Client services required by the Plugin configuration contribution. */
|
|
1051
|
+
const inject = [
|
|
1052
|
+
"slots",
|
|
1053
|
+
"locale",
|
|
1054
|
+
"connection",
|
|
1055
|
+
"remote",
|
|
1056
|
+
"settingsScope"
|
|
1057
|
+
];
|
|
1058
|
+
/** Register account copy and the OpenAI Codex card under Plugin configuration. */
|
|
1059
|
+
function apply(ctx) {
|
|
1060
|
+
const namespace = "settings.openai-codex";
|
|
1061
|
+
ctx.effect(() => ctx.locale.register(namespace, {
|
|
1062
|
+
zh,
|
|
1063
|
+
en
|
|
1064
|
+
}), "dsh-codex-connect: settings copy");
|
|
1065
|
+
const t = ctx.locale.bind(namespace);
|
|
1066
|
+
const configScope = ctx.settingsScope.bind({
|
|
1067
|
+
namespace: OPENAI_CODEX_SETTINGS_NAMESPACE,
|
|
1068
|
+
decode: decodeOpenAICodexSettings
|
|
1069
|
+
});
|
|
1070
|
+
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
1071
|
+
name: "settings.plugin.item",
|
|
1072
|
+
id: "openai-codex",
|
|
1073
|
+
order: 30,
|
|
1074
|
+
inject: () => ({
|
|
1075
|
+
t,
|
|
1076
|
+
configScope
|
|
1077
|
+
})
|
|
1078
|
+
}, OpenAICodexPluginCard));
|
|
1079
|
+
}
|
|
1080
|
+
//#endregion
|
|
1081
|
+
exports.apply = apply;
|
|
1082
|
+
exports.inject = inject;
|
|
1083
|
+
exports.name = name;
|
|
1084
|
+
return module.exports;
|
|
1085
|
+
}
|
|
1086
|
+
});
|