dsh-codex-connect 0.1.0-alpha.4.19 → 0.1.0-alpha.4.21
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 +28 -5
- package/README.i18n.yaml +2 -2
- package/README.md +39 -5
- package/cordis.patch.yml +1 -0
- package/docs/README.zh.md +39 -5
- package/docs/agent-notes/original-image-fork-access.md +7 -0
- package/lib/bin.js +368 -1
- package/lib/client.js +427 -34
- package/lib/index.d.ts +84 -3
- package/lib/index.js +2 -2
- package/lib/{src-CiOi4nFr.js → src-Cxq99TvF.js} +946 -176
- package/package.json +6 -2
package/lib/client.js
CHANGED
|
@@ -11,8 +11,35 @@ window.__ModuleLoader__.load({
|
|
|
11
11
|
/** Node-free settings contract shared by the Host plugin and browser card. */
|
|
12
12
|
/** Stable Harness settings namespace owned by this plugin. */
|
|
13
13
|
const OPENAI_CODEX_SETTINGS_NAMESPACE = "llm-openai-codex";
|
|
14
|
+
/** Suggested local HTTP proxy shown by the settings UI; it is never enabled by default. */
|
|
15
|
+
const DEFAULT_OPENAI_CODEX_PROXY_URL = "http://127.0.0.1:7890";
|
|
16
|
+
/**
|
|
17
|
+
* Normalize the credential-free HTTP proxy URL accepted by Codex Connect.
|
|
18
|
+
* Paths, query strings, fragments, and embedded credentials are rejected so
|
|
19
|
+
* the value remains an origin rather than an opaque request target.
|
|
20
|
+
*/
|
|
21
|
+
function normalizeOpenAICodexProxyUrl(value) {
|
|
22
|
+
if (typeof value !== "string" || value.trim().length === 0) return void 0;
|
|
23
|
+
try {
|
|
24
|
+
const parsed = new URL(value.trim());
|
|
25
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return void 0;
|
|
26
|
+
if (parsed.username !== "" || parsed.password !== "") return void 0;
|
|
27
|
+
if (parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "") return void 0;
|
|
28
|
+
if (parsed.hostname.length === 0) return void 0;
|
|
29
|
+
if (parsed.port !== "" && (!/^\d+$/u.test(parsed.port) || Number(parsed.port) < 1 || Number(parsed.port) > 65535)) return void 0;
|
|
30
|
+
return parsed.origin;
|
|
31
|
+
} catch {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Whether a value is a supported, canonical HTTP(S) proxy origin. */
|
|
36
|
+
function isValidOpenAICodexProxyUrl(value) {
|
|
37
|
+
return normalizeOpenAICodexProxyUrl(value) !== void 0;
|
|
38
|
+
}
|
|
14
39
|
Object.freeze({
|
|
15
40
|
models: void 0,
|
|
41
|
+
enableProxy: false,
|
|
42
|
+
proxyUrl: DEFAULT_OPENAI_CODEX_PROXY_URL,
|
|
16
43
|
enableSearch: false,
|
|
17
44
|
enableImageTool: false,
|
|
18
45
|
enableImageGeneration: false,
|
|
@@ -28,6 +55,8 @@ window.__ModuleLoader__.load({
|
|
|
28
55
|
function decodeOpenAICodexSettings(value) {
|
|
29
56
|
if (!isRecord$2(value)) return void 0;
|
|
30
57
|
const models = value["models"];
|
|
58
|
+
const enableProxy = value["enableProxy"];
|
|
59
|
+
const proxyUrl = value["proxyUrl"];
|
|
31
60
|
const enableSearch = value["enableSearch"];
|
|
32
61
|
const enableImageTool = value["enableImageTool"];
|
|
33
62
|
const enableImageGeneration = value["enableImageGeneration"];
|
|
@@ -36,6 +65,8 @@ window.__ModuleLoader__.load({
|
|
|
36
65
|
const searchContextSize = value["searchContextSize"];
|
|
37
66
|
const searchMaxOutputTokens = value["searchMaxOutputTokens"];
|
|
38
67
|
if (models !== void 0 && (!Array.isArray(models) || models.some((model) => typeof model !== "string"))) return void 0;
|
|
68
|
+
if (enableProxy !== void 0 && typeof enableProxy !== "boolean") return void 0;
|
|
69
|
+
if (proxyUrl !== void 0 && (typeof proxyUrl !== "string" || !isValidOpenAICodexProxyUrl(proxyUrl))) return void 0;
|
|
39
70
|
if (typeof enableSearch !== "boolean" || typeof enableImageTool !== "boolean") return void 0;
|
|
40
71
|
if (enableImageGeneration !== void 0 && typeof enableImageGeneration !== "boolean") return void 0;
|
|
41
72
|
if (typeof searchModel !== "string" || searchModel.trim().length === 0) return void 0;
|
|
@@ -44,6 +75,8 @@ window.__ModuleLoader__.load({
|
|
|
44
75
|
if (typeof searchMaxOutputTokens !== "number" || !Number.isInteger(searchMaxOutputTokens) || searchMaxOutputTokens < 1) return void 0;
|
|
45
76
|
return {
|
|
46
77
|
models: models === void 0 ? void 0 : [...new Set(models)],
|
|
78
|
+
enableProxy: enableProxy ?? false,
|
|
79
|
+
proxyUrl: proxyUrl === void 0 ? DEFAULT_OPENAI_CODEX_PROXY_URL : normalizeOpenAICodexProxyUrl(proxyUrl),
|
|
47
80
|
enableSearch,
|
|
48
81
|
enableImageTool,
|
|
49
82
|
enableImageGeneration: enableImageGeneration ?? false,
|
|
@@ -87,6 +120,13 @@ window.__ModuleLoader__.load({
|
|
|
87
120
|
return catalog;
|
|
88
121
|
}
|
|
89
122
|
//#endregion
|
|
123
|
+
//#region src/proxy-paths.ts
|
|
124
|
+
/** Node-free route constants shared by the Host and browser plugin halves. */
|
|
125
|
+
/** Detect bounded local/environment proxy candidates without changing settings. */
|
|
126
|
+
const OPENAI_CODEX_PROXY_DETECT_PATH = "/plugins/dsh-openai-codex/proxy/detect";
|
|
127
|
+
/** Test one manually entered proxy origin without changing settings. */
|
|
128
|
+
const OPENAI_CODEX_PROXY_TEST_PATH = "/plugins/dsh-openai-codex/proxy/test";
|
|
129
|
+
//#endregion
|
|
90
130
|
//#region src/client/OpenAICodexConfiguration.tsx
|
|
91
131
|
/** Staged optional-capability editor inside the OpenAI Codex plugin card. */
|
|
92
132
|
const sectionStyle$1 = {
|
|
@@ -223,6 +263,8 @@ window.__ModuleLoader__.load({
|
|
|
223
263
|
};
|
|
224
264
|
const CONFIG_FIELDS = [
|
|
225
265
|
"models",
|
|
266
|
+
"enableProxy",
|
|
267
|
+
"proxyUrl",
|
|
226
268
|
"enableSearch",
|
|
227
269
|
"enableImageTool",
|
|
228
270
|
"enableImageGeneration",
|
|
@@ -250,6 +292,10 @@ window.__ModuleLoader__.load({
|
|
|
250
292
|
const [feedback, setFeedback] = (0, react.useState)("idle");
|
|
251
293
|
const [modelCatalog, setModelCatalog] = (0, react.useState)();
|
|
252
294
|
const [modelCatalogError, setModelCatalogError] = (0, react.useState)(false);
|
|
295
|
+
const [proxyDetection, setProxyDetection] = (0, react.useState)({ status: "idle" });
|
|
296
|
+
const [manualProxy, setManualProxy] = (0, react.useState)(false);
|
|
297
|
+
const [manualProbe, setManualProbe] = (0, react.useState)();
|
|
298
|
+
const [manualProbeBusy, setManualProbeBusy] = (0, react.useState)(false);
|
|
253
299
|
(0, react.useEffect)(() => {
|
|
254
300
|
if (scope === void 0) return;
|
|
255
301
|
const controller = new AbortController();
|
|
@@ -291,10 +337,73 @@ window.__ModuleLoader__.load({
|
|
|
291
337
|
setDraft(scope?.getSnapshot().value);
|
|
292
338
|
setDirty(false);
|
|
293
339
|
setFeedback("idle");
|
|
340
|
+
setProxyDetection({ status: "idle" });
|
|
341
|
+
setManualProbe(void 0);
|
|
342
|
+
};
|
|
343
|
+
const detectProxy = async () => {
|
|
344
|
+
setProxyDetection({ status: "detecting" });
|
|
345
|
+
try {
|
|
346
|
+
const response = await fetch(OPENAI_CODEX_PROXY_DETECT_PATH, {
|
|
347
|
+
method: "POST",
|
|
348
|
+
credentials: "same-origin",
|
|
349
|
+
headers: { accept: "application/json" }
|
|
350
|
+
});
|
|
351
|
+
if (!response.ok) throw new Error(`proxy detection failed: ${String(response.status)}`);
|
|
352
|
+
const value = await response.json();
|
|
353
|
+
if (!Array.isArray(value.candidates) || !Array.isArray(value.results)) throw new Error("proxy detection response was invalid");
|
|
354
|
+
setProxyDetection(value.candidates.length > 0 ? {
|
|
355
|
+
status: "candidate",
|
|
356
|
+
candidates: value.candidates
|
|
357
|
+
} : {
|
|
358
|
+
status: "failed",
|
|
359
|
+
results: value.results
|
|
360
|
+
});
|
|
361
|
+
} catch {
|
|
362
|
+
setProxyDetection({
|
|
363
|
+
status: "failed",
|
|
364
|
+
results: []
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
const useProxy = (proxyUrl) => {
|
|
369
|
+
if (draft === void 0 || !isValidOpenAICodexProxyUrl(proxyUrl)) return;
|
|
370
|
+
setDraft({
|
|
371
|
+
...draft,
|
|
372
|
+
proxyUrl,
|
|
373
|
+
enableProxy: true
|
|
374
|
+
});
|
|
375
|
+
setDirty(true);
|
|
376
|
+
setFeedback("idle");
|
|
377
|
+
setManualProxy(true);
|
|
378
|
+
setManualProbe(void 0);
|
|
379
|
+
setProxyDetection({ status: "idle" });
|
|
380
|
+
};
|
|
381
|
+
const testManualProxy = async () => {
|
|
382
|
+
if (draft === void 0 || !isValidOpenAICodexProxyUrl(draft.proxyUrl)) return;
|
|
383
|
+
setManualProbeBusy(true);
|
|
384
|
+
try {
|
|
385
|
+
const path = `${OPENAI_CODEX_PROXY_TEST_PATH}?proxyUrl=${encodeURIComponent(draft.proxyUrl)}`;
|
|
386
|
+
const response = await fetch(path, {
|
|
387
|
+
method: "POST",
|
|
388
|
+
credentials: "same-origin",
|
|
389
|
+
headers: { accept: "application/json" }
|
|
390
|
+
});
|
|
391
|
+
if (!response.ok) throw new Error(`proxy test failed: ${String(response.status)}`);
|
|
392
|
+
setManualProbe(await response.json());
|
|
393
|
+
} catch {
|
|
394
|
+
setManualProbe({
|
|
395
|
+
proxyUrl: draft.proxyUrl,
|
|
396
|
+
reachable: false,
|
|
397
|
+
classification: "connect-failure"
|
|
398
|
+
});
|
|
399
|
+
} finally {
|
|
400
|
+
setManualProbeBusy(false);
|
|
401
|
+
}
|
|
294
402
|
};
|
|
295
403
|
const validModel = draft !== void 0 && draft.searchModel.trim().length > 0;
|
|
296
404
|
const validTokens = draft !== void 0 && Number.isInteger(draft.searchMaxOutputTokens) && draft.searchMaxOutputTokens > 0;
|
|
297
|
-
const
|
|
405
|
+
const validProxy = draft !== void 0 && isValidOpenAICodexProxyUrl(draft.proxyUrl);
|
|
406
|
+
const valid = validModel && validTokens && validProxy;
|
|
298
407
|
const save = async () => {
|
|
299
408
|
if (scope === void 0 || draft === void 0 || !snapshot.writable || !valid) return;
|
|
300
409
|
const desired = {
|
|
@@ -398,6 +507,135 @@ window.__ModuleLoader__.load({
|
|
|
398
507
|
}, model.id);
|
|
399
508
|
})
|
|
400
509
|
}),
|
|
510
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
511
|
+
style: { paddingTop: 4 },
|
|
512
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
513
|
+
style: headingStyle,
|
|
514
|
+
children: t("networkHeading")
|
|
515
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
516
|
+
style: {
|
|
517
|
+
...bodyStyle$2,
|
|
518
|
+
marginTop: 4
|
|
519
|
+
},
|
|
520
|
+
children: t("networkIntro")
|
|
521
|
+
})]
|
|
522
|
+
}),
|
|
523
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
524
|
+
style: bodyStyle$2,
|
|
525
|
+
role: "status",
|
|
526
|
+
children: draft.enableProxy ? t("customProxyActive", { proxyUrl: draft.proxyUrl }) : t("directConnection")
|
|
527
|
+
}),
|
|
528
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
529
|
+
style: buttonsStyle,
|
|
530
|
+
children: [
|
|
531
|
+
!draft.enableProxy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
532
|
+
type: "button",
|
|
533
|
+
style: buttonStyle$2,
|
|
534
|
+
onClick: () => {
|
|
535
|
+
detectProxy();
|
|
536
|
+
},
|
|
537
|
+
disabled: proxyDetection.status === "detecting",
|
|
538
|
+
children: proxyDetection.status === "detecting" ? t("detectingProxy") : t("detectProxy")
|
|
539
|
+
}) : null,
|
|
540
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
541
|
+
type: "button",
|
|
542
|
+
style: buttonStyle$2,
|
|
543
|
+
onClick: () => {
|
|
544
|
+
setManualProxy(true);
|
|
545
|
+
},
|
|
546
|
+
children: t("configureProxyManually")
|
|
547
|
+
}),
|
|
548
|
+
draft.enableProxy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
549
|
+
type: "button",
|
|
550
|
+
style: buttonStyle$2,
|
|
551
|
+
onClick: () => {
|
|
552
|
+
update("enableProxy", false);
|
|
553
|
+
},
|
|
554
|
+
children: t("disableProxy")
|
|
555
|
+
}) : null
|
|
556
|
+
]
|
|
557
|
+
}),
|
|
558
|
+
proxyDetection.status === "candidate" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
559
|
+
style: fieldsetStyle,
|
|
560
|
+
role: "status",
|
|
561
|
+
children: [
|
|
562
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
563
|
+
style: bodyStyle$2,
|
|
564
|
+
children: t("proxyCandidatesFound")
|
|
565
|
+
}),
|
|
566
|
+
proxyDetection.candidates.map((candidate) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
567
|
+
style: actionsStyle,
|
|
568
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
569
|
+
style: modelIdStyle,
|
|
570
|
+
children: candidate.proxyUrl
|
|
571
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
572
|
+
type: "button",
|
|
573
|
+
style: primaryButtonStyle$2,
|
|
574
|
+
onClick: () => {
|
|
575
|
+
useProxy(candidate.proxyUrl);
|
|
576
|
+
},
|
|
577
|
+
children: t("useThisProxy")
|
|
578
|
+
})]
|
|
579
|
+
}, candidate.proxyUrl)),
|
|
580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
581
|
+
type: "button",
|
|
582
|
+
style: buttonStyle$2,
|
|
583
|
+
onClick: () => {
|
|
584
|
+
setProxyDetection({ status: "idle" });
|
|
585
|
+
},
|
|
586
|
+
children: t("keepDirectConnection")
|
|
587
|
+
})
|
|
588
|
+
]
|
|
589
|
+
}) : null,
|
|
590
|
+
proxyDetection.status === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
591
|
+
style: errorStyle$2,
|
|
592
|
+
role: "alert",
|
|
593
|
+
children: t("proxyDetectionFailed")
|
|
594
|
+
}) : null,
|
|
595
|
+
manualProxy ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
596
|
+
style: fieldsetStyle,
|
|
597
|
+
children: [
|
|
598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
599
|
+
style: formFieldStyle,
|
|
600
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
601
|
+
style: labelStyle,
|
|
602
|
+
children: t("proxyAddress")
|
|
603
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
604
|
+
style: controlStyle,
|
|
605
|
+
value: draft.proxyUrl,
|
|
606
|
+
"aria-invalid": !isValidOpenAICodexProxyUrl(draft.proxyUrl),
|
|
607
|
+
onChange: (event) => {
|
|
608
|
+
update("proxyUrl", event.currentTarget.value);
|
|
609
|
+
}
|
|
610
|
+
})]
|
|
611
|
+
}),
|
|
612
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
613
|
+
style: buttonsStyle,
|
|
614
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
615
|
+
type: "button",
|
|
616
|
+
style: buttonStyle$2,
|
|
617
|
+
disabled: !isValidOpenAICodexProxyUrl(draft.proxyUrl) || manualProbeBusy,
|
|
618
|
+
onClick: () => {
|
|
619
|
+
testManualProxy();
|
|
620
|
+
},
|
|
621
|
+
children: manualProbeBusy ? t("testingProxy") : t("testProxy")
|
|
622
|
+
}), !draft.enableProxy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
623
|
+
type: "button",
|
|
624
|
+
style: primaryButtonStyle$2,
|
|
625
|
+
disabled: !isValidOpenAICodexProxyUrl(draft.proxyUrl),
|
|
626
|
+
onClick: () => {
|
|
627
|
+
useProxy(draft.proxyUrl);
|
|
628
|
+
},
|
|
629
|
+
children: t("useThisProxy")
|
|
630
|
+
}) : null]
|
|
631
|
+
}),
|
|
632
|
+
manualProbe === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
633
|
+
style: manualProbe.reachable ? successStyle : errorStyle$2,
|
|
634
|
+
role: "status",
|
|
635
|
+
children: manualProbe.reachable ? t("proxyTestSucceeded", { status: manualProbe.status ?? manualProbe.classification }) : t("proxyTestFailed", { reason: manualProbe.classification })
|
|
636
|
+
})
|
|
637
|
+
]
|
|
638
|
+
}) : null,
|
|
401
639
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
402
640
|
style: { paddingTop: 4 },
|
|
403
641
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
@@ -575,6 +813,11 @@ window.__ModuleLoader__.load({
|
|
|
575
813
|
role: "alert",
|
|
576
814
|
children: t("invalidSearchTokens")
|
|
577
815
|
}) : null,
|
|
816
|
+
!validProxy && draft !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
817
|
+
style: errorStyle$2,
|
|
818
|
+
role: "alert",
|
|
819
|
+
children: t("invalidProxyUrl")
|
|
820
|
+
}) : null,
|
|
578
821
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
579
822
|
style: bodyStyle$2,
|
|
580
823
|
children: t("routingNote")
|
|
@@ -621,7 +864,8 @@ window.__ModuleLoader__.load({
|
|
|
621
864
|
"search-stability",
|
|
622
865
|
"image-generation",
|
|
623
866
|
"oauth-history",
|
|
624
|
-
"model-visibility"
|
|
867
|
+
"model-visibility",
|
|
868
|
+
"proxy-connection"
|
|
625
869
|
];
|
|
626
870
|
function isHighlightKind(value) {
|
|
627
871
|
return typeof value === "string" && HIGHLIGHT_KINDS.includes(value);
|
|
@@ -1125,7 +1369,8 @@ window.__ModuleLoader__.load({
|
|
|
1125
1369
|
"search-stability": "updateHighlightSearchStability",
|
|
1126
1370
|
"image-generation": "updateHighlightImageGeneration",
|
|
1127
1371
|
"oauth-history": "updateHighlightOauthHistory",
|
|
1128
|
-
"model-visibility": "updateHighlightModelVisibility"
|
|
1372
|
+
"model-visibility": "updateHighlightModelVisibility",
|
|
1373
|
+
"proxy-connection": "updateHighlightProxyConnection"
|
|
1129
1374
|
};
|
|
1130
1375
|
const compatibilityTitleKeys = {
|
|
1131
1376
|
compatible: "compatibilityCompatibleTitle",
|
|
@@ -2567,6 +2812,24 @@ window.__ModuleLoader__.load({
|
|
|
2567
2812
|
modelCatalogFailed: "Available models could not be loaded.",
|
|
2568
2813
|
capabilitiesHeading: "Optional capabilities",
|
|
2569
2814
|
capabilitiesIntro: "Choose which extra Codex capabilities this dsh profile may register.",
|
|
2815
|
+
networkHeading: "Network connection",
|
|
2816
|
+
networkIntro: "Direct connection is the default. Proxy detection only tests a bounded set of local and environment candidates; nothing is saved until you choose Use this proxy and save.",
|
|
2817
|
+
directConnection: "Direct connection",
|
|
2818
|
+
customProxyActive: "Custom proxy selected: {proxyUrl}",
|
|
2819
|
+
detectProxy: "Detect proxy",
|
|
2820
|
+
detectingProxy: "Detecting…",
|
|
2821
|
+
configureProxyManually: "Configure manually",
|
|
2822
|
+
disableProxy: "Disable proxy",
|
|
2823
|
+
proxyCandidatesFound: "Working proxy candidates found. Choose one to stage it for activation.",
|
|
2824
|
+
keepDirectConnection: "Keep direct connection",
|
|
2825
|
+
useThisProxy: "Use this proxy",
|
|
2826
|
+
proxyDetectionFailed: "No working proxy candidate was found. Check DNS, connection refused, timeout, TLS, or proxy authentication errors, then try again.",
|
|
2827
|
+
proxyAddress: "Custom proxy address",
|
|
2828
|
+
testProxy: "Test proxy",
|
|
2829
|
+
testingProxy: "Testing…",
|
|
2830
|
+
proxyTestSucceeded: "Proxy reached the Codex endpoint (HTTP {status}).",
|
|
2831
|
+
proxyTestFailed: "Proxy test failed: {reason}.",
|
|
2832
|
+
invalidProxyUrl: "Enter an HTTP(S) proxy origin without credentials or a path.",
|
|
2570
2833
|
enableSearch: "Enable Codex search provider",
|
|
2571
2834
|
enableSearchHelp: "Makes OpenAI Codex available as a search provider. It does not select the global search route.",
|
|
2572
2835
|
searchModel: "Search model",
|
|
@@ -2606,12 +2869,18 @@ window.__ModuleLoader__.load({
|
|
|
2606
2869
|
unknownResult: "The image result could not be displayed safely.",
|
|
2607
2870
|
imageDetails: "Image details",
|
|
2608
2871
|
imageDetail: "{name}: {format} · {width} × {height} · {size}",
|
|
2872
|
+
originalImageDetail: "Original {name}: {format} · {width} × {height} · {size}",
|
|
2873
|
+
previewImageDetail: "Conversation preview: {format} · {width} × {height} · {size}",
|
|
2609
2874
|
download: "Download",
|
|
2610
2875
|
downloadNamed: "Download {name}",
|
|
2876
|
+
downloadOriginal: "Download original",
|
|
2877
|
+
downloadOriginalNamed: "Download original {name}",
|
|
2878
|
+
downloadPreview: "Download preview",
|
|
2879
|
+
downloadPreviewNamed: "Download preview {name}",
|
|
2611
2880
|
downloading: "Downloading…",
|
|
2612
2881
|
downloadFailed: "Download failed. Retry",
|
|
2613
2882
|
image: "Generated image",
|
|
2614
|
-
open: "Open
|
|
2883
|
+
open: "Open image preview",
|
|
2615
2884
|
openNamed: "Open {name}",
|
|
2616
2885
|
loading: "Loading image",
|
|
2617
2886
|
loadFailed: "Image could not be loaded. Retry",
|
|
@@ -2675,6 +2944,7 @@ window.__ModuleLoader__.load({
|
|
|
2675
2944
|
updateHighlightImageGeneration: "Added optional GPT Image generation in the Codex Connect settings and a preview card in the conversation.",
|
|
2676
2945
|
updateHighlightOauthHistory: "Added safer session-history migration and a sign-in fallback when the account needs to reconnect.",
|
|
2677
2946
|
updateHighlightModelVisibility: "You can now choose which Codex models appear in model selectors.",
|
|
2947
|
+
updateHighlightProxyConnection: "Added opt-in Codex proxy detection with explicit confirmation and a visible direct-connection fallback.",
|
|
2678
2948
|
viewTechnicalDetails: "View technical details",
|
|
2679
2949
|
hideTechnicalDetails: "Hide technical details",
|
|
2680
2950
|
technicalDetailsHeading: "Technical changes",
|
|
@@ -2751,6 +3021,24 @@ window.__ModuleLoader__.load({
|
|
|
2751
3021
|
modelCatalogFailed: "无法加载可用模型。",
|
|
2752
3022
|
capabilitiesHeading: "可选能力",
|
|
2753
3023
|
capabilitiesIntro: "选择允许此 dsh profile 注册哪些额外的 Codex 能力。",
|
|
3024
|
+
networkHeading: "网络连接",
|
|
3025
|
+
networkIntro: "默认使用直连。代理检测只测试有限的本机和环境变量候选地址;只有你选择“使用此代理”并保存后才会写入设置。",
|
|
3026
|
+
directConnection: "直连",
|
|
3027
|
+
customProxyActive: "已选择自定义代理:{proxyUrl}",
|
|
3028
|
+
detectProxy: "检测代理",
|
|
3029
|
+
detectingProxy: "正在检测…",
|
|
3030
|
+
configureProxyManually: "手动配置",
|
|
3031
|
+
disableProxy: "停用代理",
|
|
3032
|
+
proxyCandidatesFound: "找到了可用的代理候选地址。请选择一个,将其暂存以便启用。",
|
|
3033
|
+
keepDirectConnection: "保持直连",
|
|
3034
|
+
useThisProxy: "使用此代理",
|
|
3035
|
+
proxyDetectionFailed: "没有找到可用代理。请检查 DNS、连接被拒绝、超时、TLS 或代理认证错误后重试。",
|
|
3036
|
+
proxyAddress: "自定义代理地址",
|
|
3037
|
+
testProxy: "测试代理",
|
|
3038
|
+
testingProxy: "正在测试…",
|
|
3039
|
+
proxyTestSucceeded: "代理已到达 Codex 端点(HTTP {status})。",
|
|
3040
|
+
proxyTestFailed: "代理测试失败:{reason}。",
|
|
3041
|
+
invalidProxyUrl: "请输入不含凭据或路径的 HTTP(S) 代理 origin。",
|
|
2754
3042
|
enableSearch: "启用 Codex 搜索提供方",
|
|
2755
3043
|
enableSearchHelp: "让 OpenAI Codex 可被选作搜索提供方,但不会自动改动全局搜索路由。",
|
|
2756
3044
|
searchModel: "搜索模型",
|
|
@@ -2790,12 +3078,18 @@ window.__ModuleLoader__.load({
|
|
|
2790
3078
|
unknownResult: "无法安全显示这次图片结果。",
|
|
2791
3079
|
imageDetails: "图片详情",
|
|
2792
3080
|
imageDetail: "{name}:{format} · {width} × {height} · {size}",
|
|
3081
|
+
originalImageDetail: "原图 {name}:{format} · {width} × {height} · {size}",
|
|
3082
|
+
previewImageDetail: "对话预览:{format} · {width} × {height} · {size}",
|
|
2793
3083
|
download: "下载",
|
|
2794
3084
|
downloadNamed: "下载 {name}",
|
|
3085
|
+
downloadOriginal: "下载原图",
|
|
3086
|
+
downloadOriginalNamed: "下载原图 {name}",
|
|
3087
|
+
downloadPreview: "下载预览图",
|
|
3088
|
+
downloadPreviewNamed: "下载预览图 {name}",
|
|
2795
3089
|
downloading: "正在下载…",
|
|
2796
3090
|
downloadFailed: "下载失败,重试",
|
|
2797
3091
|
image: "生成的图片",
|
|
2798
|
-
open: "
|
|
3092
|
+
open: "打开图片预览",
|
|
2799
3093
|
openNamed: "打开 {name}",
|
|
2800
3094
|
loading: "正在加载图片",
|
|
2801
3095
|
loadFailed: "图片加载失败,重试",
|
|
@@ -2859,6 +3153,7 @@ window.__ModuleLoader__.load({
|
|
|
2859
3153
|
updateHighlightImageGeneration: "在 Codex Connect 设置中加入可选的 GPT Image 生图,并在对话里展示生成结果卡片。",
|
|
2860
3154
|
updateHighlightOauthHistory: "增加更安全的会话历史迁移,并在账号需要重新登录时提供备用登录路径。",
|
|
2861
3155
|
updateHighlightModelVisibility: "现在可以选择哪些 Codex 模型显示在模型选择菜单中。",
|
|
3156
|
+
updateHighlightProxyConnection: "增加可选的 Codex 代理检测,启用前需要明确确认,并始终保留直连回退入口。",
|
|
2862
3157
|
viewTechnicalDetails: "查看完整技术说明",
|
|
2863
3158
|
hideTechnicalDetails: "收起技术说明",
|
|
2864
3159
|
technicalDetailsHeading: "技术变更",
|
|
@@ -2882,6 +3177,38 @@ window.__ModuleLoader__.load({
|
|
|
2882
3177
|
dismissUpdate: "稍后提醒"
|
|
2883
3178
|
};
|
|
2884
3179
|
//#endregion
|
|
3180
|
+
//#region src/image-assets-contract.ts
|
|
3181
|
+
/** Same-origin endpoint serving one session-owned original generated image. */
|
|
3182
|
+
const OPENAI_CODEX_ORIGINAL_IMAGE_PATH = "/plugins/dsh-codex-connect/images/original";
|
|
3183
|
+
/** Opaque identifier format for one plugin-owned original image. */
|
|
3184
|
+
const OPENAI_CODEX_IMAGE_ASSET_ID_PATTERN = /^img_[0-9a-f]{32}$/u;
|
|
3185
|
+
function positiveSafeInteger$1(value) {
|
|
3186
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
3187
|
+
}
|
|
3188
|
+
/** Decode session-log metadata without trusting an asset id, filename, or media type. */
|
|
3189
|
+
function decodeOpenAICodexOriginalImageRef(value) {
|
|
3190
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
3191
|
+
const candidate = value;
|
|
3192
|
+
if (typeof candidate.assetId !== "string" || !OPENAI_CODEX_IMAGE_ASSET_ID_PATTERN.test(candidate.assetId) || candidate.mediaType !== "image/png" && candidate.mediaType !== "image/jpeg" && candidate.mediaType !== "image/webp" || !positiveSafeInteger$1(candidate.width) || !positiveSafeInteger$1(candidate.height) || !positiveSafeInteger$1(candidate.bytes) || candidate.bytes > 50331648 || typeof candidate.name !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(candidate.name) || typeof candidate.sha256 !== "string" || !/^[0-9a-f]{64}$/u.test(candidate.sha256)) return void 0;
|
|
3193
|
+
return {
|
|
3194
|
+
assetId: candidate.assetId,
|
|
3195
|
+
mediaType: candidate.mediaType,
|
|
3196
|
+
width: candidate.width,
|
|
3197
|
+
height: candidate.height,
|
|
3198
|
+
bytes: candidate.bytes,
|
|
3199
|
+
name: candidate.name,
|
|
3200
|
+
sha256: candidate.sha256
|
|
3201
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
/** Build a same-origin URL without allowing either opaque id to become a path segment. */
|
|
3204
|
+
function openAICodexOriginalImageUrl(sessionId, assetId) {
|
|
3205
|
+
const query = new URLSearchParams({
|
|
3206
|
+
sessionId,
|
|
3207
|
+
assetId
|
|
3208
|
+
});
|
|
3209
|
+
return `${OPENAI_CODEX_ORIGINAL_IMAGE_PATH}?${query.toString()}`;
|
|
3210
|
+
}
|
|
3211
|
+
//#endregion
|
|
2885
3212
|
//#region src/image-presentation.ts
|
|
2886
3213
|
/** Stable metadata marker for generated image result views. */
|
|
2887
3214
|
const IMAGE_PRESENTATION_KIND = "codex-connect-images";
|
|
@@ -2909,10 +3236,32 @@ window.__ModuleLoader__.load({
|
|
|
2909
3236
|
if (typeof value !== "object" || value === null) return void 0;
|
|
2910
3237
|
const candidate = value;
|
|
2911
3238
|
if (candidate.kind !== "codex-connect-images" || typeof candidate.prompt !== "string" || candidate.prompt.length < 1 || candidate.prompt.length > 32e3 || !Array.isArray(candidate.images) || candidate.images.length < 1 || candidate.images.length > 4) return void 0;
|
|
2912
|
-
|
|
2913
|
-
|
|
3239
|
+
if (candidate.schemaVersion === void 0) {
|
|
3240
|
+
const previews = candidate.images.map(imageRef);
|
|
3241
|
+
if (previews.some((image) => image === void 0)) return void 0;
|
|
3242
|
+
return {
|
|
3243
|
+
kind: IMAGE_PRESENTATION_KIND,
|
|
3244
|
+
schemaVersion: 1,
|
|
3245
|
+
prompt: candidate.prompt,
|
|
3246
|
+
images: previews.map((preview) => ({ preview }))
|
|
3247
|
+
};
|
|
3248
|
+
}
|
|
3249
|
+
if (candidate.schemaVersion !== 1) return void 0;
|
|
3250
|
+
const images = [];
|
|
3251
|
+
for (const value of candidate.images) {
|
|
3252
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
3253
|
+
const entry = value;
|
|
3254
|
+
const preview = imageRef(entry.preview);
|
|
3255
|
+
const original = decodeOpenAICodexOriginalImageRef(entry.original);
|
|
3256
|
+
if (preview === void 0 || original === void 0) return void 0;
|
|
3257
|
+
images.push({
|
|
3258
|
+
preview,
|
|
3259
|
+
original
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
2914
3262
|
return {
|
|
2915
3263
|
kind: IMAGE_PRESENTATION_KIND,
|
|
3264
|
+
schemaVersion: 1,
|
|
2916
3265
|
prompt: candidate.prompt,
|
|
2917
3266
|
images
|
|
2918
3267
|
};
|
|
@@ -3508,7 +3857,19 @@ window.__ModuleLoader__.load({
|
|
|
3508
3857
|
children: t("actionFailed")
|
|
3509
3858
|
}) : null;
|
|
3510
3859
|
}
|
|
3511
|
-
function
|
|
3860
|
+
function triggerDownload(url, name) {
|
|
3861
|
+
const anchor = document.createElement("a");
|
|
3862
|
+
anchor.href = url;
|
|
3863
|
+
anchor.download = name;
|
|
3864
|
+
anchor.rel = "noopener";
|
|
3865
|
+
document.body.append(anchor);
|
|
3866
|
+
try {
|
|
3867
|
+
anchor.click();
|
|
3868
|
+
} finally {
|
|
3869
|
+
anchor.remove();
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
function DownloadButton({ label, onDownload, t }) {
|
|
3512
3873
|
const [state, setState] = (0, react.useState)("idle");
|
|
3513
3874
|
const alive = (0, react.useRef)(true);
|
|
3514
3875
|
(0, react.useEffect)(() => () => {
|
|
@@ -3518,17 +3879,7 @@ window.__ModuleLoader__.load({
|
|
|
3518
3879
|
if (state === "pending") return;
|
|
3519
3880
|
setState("pending");
|
|
3520
3881
|
try {
|
|
3521
|
-
|
|
3522
|
-
const anchor = document.createElement("a");
|
|
3523
|
-
anchor.href = url;
|
|
3524
|
-
anchor.download = image.name ?? t("image");
|
|
3525
|
-
anchor.rel = "noopener";
|
|
3526
|
-
document.body.append(anchor);
|
|
3527
|
-
try {
|
|
3528
|
-
anchor.click();
|
|
3529
|
-
} finally {
|
|
3530
|
-
anchor.remove();
|
|
3531
|
-
}
|
|
3882
|
+
await onDownload();
|
|
3532
3883
|
if (alive.current) setState("idle");
|
|
3533
3884
|
} catch {
|
|
3534
3885
|
if (alive.current) setState("failed");
|
|
@@ -3552,6 +3903,22 @@ window.__ModuleLoader__.load({
|
|
|
3552
3903
|
children: status
|
|
3553
3904
|
})] });
|
|
3554
3905
|
}
|
|
3906
|
+
async function downloadOriginal(sessionId, original) {
|
|
3907
|
+
const response = await fetch(openAICodexOriginalImageUrl(sessionId, original.assetId), {
|
|
3908
|
+
method: "GET",
|
|
3909
|
+
headers: { accept: original.mediaType },
|
|
3910
|
+
credentials: "same-origin"
|
|
3911
|
+
});
|
|
3912
|
+
if (!response.ok) throw new Error("Original image download failed");
|
|
3913
|
+
const blob = await response.blob();
|
|
3914
|
+
if (blob.size !== original.bytes) throw new Error("Original image download was incomplete");
|
|
3915
|
+
const url = URL.createObjectURL(blob);
|
|
3916
|
+
try {
|
|
3917
|
+
triggerDownload(url, original.name);
|
|
3918
|
+
} finally {
|
|
3919
|
+
URL.revokeObjectURL(url);
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3555
3922
|
function PromptPanel({ prompt, t }) {
|
|
3556
3923
|
const [copyState, setCopyState] = (0, react.useState)("idle");
|
|
3557
3924
|
const [hovered, setHovered] = (0, react.useState)(false);
|
|
@@ -3868,7 +4235,7 @@ window.__ModuleLoader__.load({
|
|
|
3868
4235
|
style: header,
|
|
3869
4236
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("completed") })
|
|
3870
4237
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexImageGallery, {
|
|
3871
|
-
images: decoded.images.map((
|
|
4238
|
+
images: decoded.images.map((image) => ({ attachment: image.preview })),
|
|
3872
4239
|
load,
|
|
3873
4240
|
align: "start",
|
|
3874
4241
|
labels: galleryLabels
|
|
@@ -3913,12 +4280,21 @@ window.__ModuleLoader__.load({
|
|
|
3913
4280
|
flexWrap: "wrap",
|
|
3914
4281
|
gap: 8
|
|
3915
4282
|
},
|
|
3916
|
-
children: decoded.images.
|
|
3917
|
-
image
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
4283
|
+
children: decoded.images.flatMap((image, index) => {
|
|
4284
|
+
const suffix = image.preview.name ?? String(index + 1);
|
|
4285
|
+
const exactOriginal = image.original;
|
|
4286
|
+
return [...exactOriginal === void 0 ? [] : [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DownloadButton, {
|
|
4287
|
+
onDownload: () => downloadOriginal(sessionId, exactOriginal),
|
|
4288
|
+
t,
|
|
4289
|
+
label: decoded.images.length === 1 ? t("downloadOriginal") : t("downloadOriginalNamed", { name: exactOriginal.name })
|
|
4290
|
+
}, `${image.preview.attachmentId}:original`)], /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DownloadButton, {
|
|
4291
|
+
onDownload: async () => {
|
|
4292
|
+
triggerDownload(await load(image.preview), image.preview.name ?? t("image"));
|
|
4293
|
+
},
|
|
4294
|
+
t,
|
|
4295
|
+
label: image.original === void 0 ? decoded.images.length === 1 ? t("download") : t("downloadNamed", { name: suffix }) : decoded.images.length === 1 ? t("downloadPreview") : t("downloadPreviewNamed", { name: suffix })
|
|
4296
|
+
}, `${image.preview.attachmentId}:preview`)];
|
|
4297
|
+
})
|
|
3922
4298
|
}),
|
|
3923
4299
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
|
|
3924
4300
|
style: {
|
|
@@ -3934,20 +4310,37 @@ window.__ModuleLoader__.load({
|
|
|
3934
4310
|
gap: 4,
|
|
3935
4311
|
marginTop: 6
|
|
3936
4312
|
},
|
|
3937
|
-
children: decoded.images.
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
4313
|
+
children: decoded.images.flatMap((image, index) => {
|
|
4314
|
+
const preview = image.preview;
|
|
4315
|
+
const original = image.original;
|
|
4316
|
+
const name = preview.name ?? String(index + 1);
|
|
4317
|
+
if (original === void 0) return [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("imageDetail", {
|
|
4318
|
+
name,
|
|
4319
|
+
format: formatMediaType(preview.mediaType),
|
|
4320
|
+
width: preview.width,
|
|
4321
|
+
height: preview.height,
|
|
4322
|
+
size: formatBytes(preview.bytes)
|
|
4323
|
+
}) }, preview.attachmentId)];
|
|
4324
|
+
return [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("originalImageDetail", {
|
|
4325
|
+
name: original.name,
|
|
4326
|
+
format: formatMediaType(original.mediaType),
|
|
4327
|
+
width: original.width,
|
|
4328
|
+
height: original.height,
|
|
4329
|
+
size: formatBytes(original.bytes)
|
|
4330
|
+
}) }, `${preview.attachmentId}:original`), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("previewImageDetail", {
|
|
4331
|
+
format: formatMediaType(preview.mediaType),
|
|
4332
|
+
width: preview.width,
|
|
4333
|
+
height: preview.height,
|
|
4334
|
+
size: formatBytes(preview.bytes)
|
|
4335
|
+
}) }, `${preview.attachmentId}:preview`)];
|
|
4336
|
+
})
|
|
3944
4337
|
})] })
|
|
3945
4338
|
] })
|
|
3946
4339
|
});
|
|
3947
4340
|
}
|
|
3948
4341
|
//#endregion
|
|
3949
4342
|
//#region src/version.ts
|
|
3950
|
-
const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.
|
|
4343
|
+
const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.21";
|
|
3951
4344
|
//#endregion
|
|
3952
4345
|
//#region src/client/index.tsx
|
|
3953
4346
|
/** Stable browser-plugin name. */
|