dsh-codex-connect 0.1.0-alpha.4.19 → 0.1.0-alpha.4.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/INSTALL.md CHANGED
@@ -12,7 +12,7 @@ Install `dsh-codex-connect` into one requested DeepSeek Harness profile without
12
12
 
13
13
  ## Install and validate
14
14
 
15
- The only verified combination is DSH plugin API packages `0.1.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.19 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should upgrade the DSH API packages and pi-ai together, then rerun `dsh-codex-connect doctor --json` and `pnpm --silent run check:compatibility`; the contract does not make claims about future versions.
15
+ The only verified combination is DSH plugin API packages `0.1.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`. Alpha 4.20 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should upgrade the DSH API packages and pi-ai together, then rerun `dsh-codex-connect doctor --json` and `pnpm --silent run check:compatibility`; the contract does not make claims about future versions.
16
16
 
17
17
  1. Check `dsh --version` or `dsh --help`. From a Harness checkout use `pnpm dsh`.
18
18
  2. Install the package:
@@ -21,7 +21,7 @@ The only verified combination is DSH plugin API packages `0.1.1-rc.2`, `@earendi
21
21
  dsh plugin --profile web add dsh-codex-connect@alpha
22
22
  ```
23
23
 
24
- After `0.1.0-alpha.4.19` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.19`. If npm is unavailable after its matching GitHub prerelease is created, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.19'`.
24
+ After `0.1.0-alpha.4.20` is published, pin it exactly with `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.20`. If npm is unavailable after its matching GitHub prerelease is created, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.20'`.
25
25
 
26
26
  3. Run `dsh --profile web --dump-config` and require exactly one `llm-openai-codex` row loading `dsh-codex-connect`.
27
27
  4. Confirm the effective `agent-default-model` and `web.searchProvider` values are unchanged from before installation.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # git hash-object README.md docs/README.zh.md
5
- README.md: ecad858eb993cfc54b97eb8d8d0518feb0b23f17
6
- docs/README.zh.md: 2d61465cfd3fde855d912ed39363563b46730a23
5
+ README.md: 29729f3c078749ca04dc1d648c496c7b57bf3b17
6
+ docs/README.zh.md: a7141cd531059e44771f763a8e5b75d529fc3f83
package/README.md CHANGED
@@ -28,7 +28,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
28
28
 
29
29
  Expected result: the package is added to that profile. This does not change the profile's default model or global search route.
30
30
 
31
- To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.19`. If npm is unavailable after the matching GitHub prerelease exists, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.19'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
31
+ To reproduce this release exactly, use `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.20`. If npm is unavailable after the matching GitHub prerelease exists, use `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.20'`. A local checkout can be installed as `link:/absolute/path/to/dsh-codex-connect`.
32
32
 
33
33
  ### Version updates
34
34
 
@@ -139,6 +139,7 @@ The installed bundle is intentionally inert beyond model-provider registration:
139
139
  ```yaml
140
140
  - id: llm-openai-codex
141
141
  config:
142
+ enableProxy: false
142
143
  enableSearch: false
143
144
  enableImageTool: false
144
145
  enableImageGeneration: false
@@ -146,6 +147,14 @@ The installed bundle is intentionally inert beyond model-provider registration:
146
147
 
147
148
  Open **Settings → Plugins → Plugin configuration → Codex Connect** to manage the account and these options in one card. **Save changes** affects only this plugin's capability section and applies live. It never selects a default model or a global search route.
148
149
 
150
+ ### Network connection and proxy detection
151
+
152
+ Codex Connect uses a **Direct connection** by default. A proxy is optional and applies only to this plugin's Codex requests: model streaming, OAuth login and token refresh, usage, standalone search, and image generation. Other providers and unscoped network requests keep the process's original dispatcher.
153
+
154
+ Select **Detect proxy** to test only the standard proxy environment variables and the documented loopback candidates `127.0.0.1:7890`, `127.0.0.1:7897`, and `127.0.0.1:10809`. Detection makes no model call, consumes no quota, and does not write settings. A response from the canonical Codex endpoint proves network reachability; `401/403`, proxy `407`, DNS, refused connection, timeout, TLS, and CONNECT failures remain separate diagnostics.
155
+
156
+ Choose **Use this proxy** only after reviewing a candidate, then click **Save changes**. **Configure manually** lets you test a credential-free HTTP(S) proxy origin before activation. **Disable proxy** is always available. A failed probe leaves the previous mode unchanged, and an enabled proxy failure is shown as an actionable error; Codex Connect never silently retries the request through a direct connection.
157
+
149
158
  ### Enable only the capability you intend to use
150
159
 
151
160
  - `enableSearch: true` registers Codex as an available search provider. It does not select the profile's global search route.
@@ -210,6 +219,8 @@ Selecting Codex as the profile's global search route is another explicit change:
210
219
  | Field | Default | Values |
211
220
  |---|---:|---|
212
221
  | `models` | full catalog | Codex model id array; empty hides all entries |
222
+ | `enableProxy` | `false` | boolean; direct connection unless explicitly enabled |
223
+ | `proxyUrl` | `http://127.0.0.1:7890` (inactive placeholder) | Credential-free HTTP(S) proxy origin |
213
224
  | `enableSearch` | `false` | boolean |
214
225
  | `enableImageTool` | `false` | boolean |
215
226
  | `enableImageGeneration` | `false` | boolean |
@@ -239,7 +250,7 @@ Selecting Codex as the profile's global search route is another explicit change:
239
250
 
240
251
  ## Compatibility and security boundary
241
252
 
242
- - The only verified compatibility combination is DSH plugin API packages `0.1.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`; see [compatibility.json](compatibility.json). Alpha 4.19 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should upgrade to the rc.2 API packages.
253
+ - The only verified compatibility combination is DSH plugin API packages `0.1.1-rc.2`, `@earendil-works/pi-ai` `0.82.1`, and Node.js `^22.19.0 || >=24.0.0`; see [compatibility.json](compatibility.json). Alpha 4.20 uses the rc.2 keyed Plugin configuration slot; users of older DSH API packages should upgrade to the rc.2 API packages.
243
254
  - Upgrade the DSH plugin API packages and `@earendil-works/pi-ai` as one group, then run `dsh-codex-connect doctor --json` and the compatibility check again. This contract does not make claims about future versions.
244
255
  - When the daily upstream check finds a new `latest` or `next` DSH candidate, it installs Codex Connect into an isolated profile, boots the installed model runtime without OAuth credentials, verifies model and reasoning-effort discovery, and confirms provider disposal. Live sign-in, quota, and model requests still require manual validation in the test profile.
245
256
  - ChatGPT plan eligibility, model access, quotas, and backend behavior are controlled by OpenAI and may change.
package/cordis.patch.yml CHANGED
@@ -4,6 +4,7 @@
4
4
  - id: llm-openai-codex
5
5
  name: dsh-codex-connect
6
6
  config:
7
+ enableProxy: false
7
8
  enableSearch: false
8
9
  enableImageTool: false
9
10
  enableImageGeneration: false
package/docs/README.zh.md CHANGED
@@ -28,7 +28,7 @@ dsh plugin --profile web add dsh-codex-connect@alpha
28
28
 
29
29
  预期结果:包被加入该 profile。这个动作不会更改 profile 的默认模型或全局搜索路由。
30
30
 
31
- 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.19`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.19'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
31
+ 如需精确复现这个版本,使用 `dsh plugin --profile web add dsh-codex-connect@0.1.0-alpha.4.20`。对应 GitHub prerelease 已创建但 npm 不可用时,可使用 `dsh plugin --profile web add 'github:franksong2702/dsh-codex-connect#v0.1.0-alpha.4.20'`。本地 checkout 可安装为 `link:/absolute/path/to/dsh-codex-connect`。
32
32
 
33
33
  ### 版本更新提醒
34
34
 
@@ -139,6 +139,7 @@ dsh plugin --profile web exec dsh-codex-connect doctor --json
139
139
  ```yaml
140
140
  - id: llm-openai-codex
141
141
  config:
142
+ enableProxy: false
142
143
  enableSearch: false
143
144
  enableImageTool: false
144
145
  enableImageGeneration: false
@@ -146,6 +147,14 @@ dsh plugin --profile web exec dsh-codex-connect doctor --json
146
147
 
147
148
  打开 **设置 → 插件 → 插件配置 → Codex Connect**,即可在同一张卡片中管理账户和这些选项。**保存更改**只影响本插件的能力配置并即时生效,绝不会选择默认模型或全局搜索路由。
148
149
 
150
+ ### 网络连接与代理检测
151
+
152
+ Codex Connect 默认使用**直连**。代理是可选项,只作用于本插件的 Codex 请求:模型流式请求、OAuth 登录和 token 刷新、额度、独立搜索以及图片生成。其他提供方和未进入插件作用域的网络请求仍使用进程原来的 dispatcher。
153
+
154
+ 点击 **检测代理** 时,只会测试标准代理环境变量,以及文档列出的本机候选地址:`127.0.0.1:7890`、`127.0.0.1:7897` 和 `127.0.0.1:10809`。检测不调用模型、不消耗额度,也不会写入设置。规范 Codex 端点返回任何 HTTP 响应都表示网络可达;`401/403`、代理 `407`、DNS、连接被拒绝、超时、TLS 和 CONNECT 失败会分别显示为诊断类别。
155
+
156
+ 请先检查候选地址,再选择 **使用此代理**,最后点击 **保存更改**。**手动配置**可以先测试一个不带凭据的 HTTP(S) proxy origin,再启用它。**停用代理**始终可用。检测失败会保留原来的模式;代理已启用但请求失败时,界面会给出可操作的错误,绝不会静默改走直连。
157
+
149
158
  ### 只开启你准备使用的能力
150
159
 
151
160
  - `enableSearch: true` 会把 Codex 注册为可选择的搜索提供方,不会把它选为 profile 的全局搜索路由。
@@ -210,6 +219,8 @@ dsh plugin --profile web exec dsh-codex-connect doctor --json
210
219
  | 字段 | 默认值 | 可选值 |
211
220
  |---|---:|---|
212
221
  | `models` | 完整目录 | Codex model id 数组;空数组隐藏全部条目 |
222
+ | `enableProxy` | `false` | boolean;除非显式启用,否则使用直连 |
223
+ | `proxyUrl` | `http://127.0.0.1:7890`(未启用的占位值) | 不带凭据的 HTTP(S) proxy origin |
213
224
  | `enableSearch` | `false` | boolean |
214
225
  | `enableImageTool` | `false` | boolean |
215
226
  | `enableImageGeneration` | `false` | boolean |
@@ -239,7 +250,7 @@ dsh plugin --profile web exec dsh-codex-connect doctor --json
239
250
 
240
251
  ## 兼容性与安全边界
241
252
 
242
- - 当前唯一已验证的兼容组合是 DSH 插件 API packages `0.1.1-rc.2`、`@earendil-works/pi-ai` `0.82.1` 和 Node.js `^22.19.0 || >=24.0.0`;详见 [compatibility.json](../compatibility.json)。Alpha 4.19 使用 rc.2 的 keyed 插件配置 slot;旧版 DSH API packages 用户应升级到 rc.2 API packages。
253
+ - 当前唯一已验证的兼容组合是 DSH 插件 API packages `0.1.1-rc.2`、`@earendil-works/pi-ai` `0.82.1` 和 Node.js `^22.19.0 || >=24.0.0`;详见 [compatibility.json](../compatibility.json)。Alpha 4.20 使用 rc.2 的 keyed 插件配置 slot;旧版 DSH API packages 用户应升级到 rc.2 API packages。
243
254
  - 升级时请将 DSH 插件 API packages 与 `@earendil-works/pi-ai` 作为一组升级,再运行 `dsh-codex-connect doctor --json` 和兼容性检查。本契约不对未来版本作判断。
244
255
  - 每日上游检查发现新的 DSH `latest` 或 `next` 候选版本时,会把 Codex Connect 安装到隔离 Profile 中,在没有 OAuth 凭据的情况下启动已安装的模型运行时,验证模型与推理强度发现,并确认提供方可被正确卸载。真实登录、额度和模型请求仍需在测试 Profile 中人工验证。
245
256
  - ChatGPT 套餐资格、模型权限、额度和后端行为由 OpenAI 控制,可能变化。
package/lib/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { L as diagnoseOpenAICodex, c as migrateOpenAICodexSearchHistory, ct as normalizeTrustedOrigin, ft as loginOpenAICodex, mt as openAICodexAuthStatus, pt as logoutOpenAICodex, st as OpenAICodexTrustedOriginsStore, vt as openAICodexAuthPath, z as CODEX_CONNECT_VERSION } from "./src-CiOi4nFr.js";
2
+ import { At as openAICodexAuthPath, Et as openAICodexAuthStatus, Tt as logoutOpenAICodex, bt as normalizeTrustedOrigin, c as migrateOpenAICodexSearchHistory, j as CODEX_CONNECT_VERSION, k as diagnoseOpenAICodex, wt as loginOpenAICodex, yt as OpenAICodexTrustedOriginsStore } from "./src-B2d5HLN5.js";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { realpathSync } from "node:fs";
5
5
  import { spawn } from "node:child_process";
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 valid = validModel && validTokens;
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",
@@ -2675,6 +2938,7 @@ window.__ModuleLoader__.load({
2675
2938
  updateHighlightImageGeneration: "Added optional GPT Image generation in the Codex Connect settings and a preview card in the conversation.",
2676
2939
  updateHighlightOauthHistory: "Added safer session-history migration and a sign-in fallback when the account needs to reconnect.",
2677
2940
  updateHighlightModelVisibility: "You can now choose which Codex models appear in model selectors.",
2941
+ updateHighlightProxyConnection: "Added opt-in Codex proxy detection with explicit confirmation and a visible direct-connection fallback.",
2678
2942
  viewTechnicalDetails: "View technical details",
2679
2943
  hideTechnicalDetails: "Hide technical details",
2680
2944
  technicalDetailsHeading: "Technical changes",
@@ -2751,6 +3015,24 @@ window.__ModuleLoader__.load({
2751
3015
  modelCatalogFailed: "无法加载可用模型。",
2752
3016
  capabilitiesHeading: "可选能力",
2753
3017
  capabilitiesIntro: "选择允许此 dsh profile 注册哪些额外的 Codex 能力。",
3018
+ networkHeading: "网络连接",
3019
+ networkIntro: "默认使用直连。代理检测只测试有限的本机和环境变量候选地址;只有你选择“使用此代理”并保存后才会写入设置。",
3020
+ directConnection: "直连",
3021
+ customProxyActive: "已选择自定义代理:{proxyUrl}",
3022
+ detectProxy: "检测代理",
3023
+ detectingProxy: "正在检测…",
3024
+ configureProxyManually: "手动配置",
3025
+ disableProxy: "停用代理",
3026
+ proxyCandidatesFound: "找到了可用的代理候选地址。请选择一个,将其暂存以便启用。",
3027
+ keepDirectConnection: "保持直连",
3028
+ useThisProxy: "使用此代理",
3029
+ proxyDetectionFailed: "没有找到可用代理。请检查 DNS、连接被拒绝、超时、TLS 或代理认证错误后重试。",
3030
+ proxyAddress: "自定义代理地址",
3031
+ testProxy: "测试代理",
3032
+ testingProxy: "正在测试…",
3033
+ proxyTestSucceeded: "代理已到达 Codex 端点(HTTP {status})。",
3034
+ proxyTestFailed: "代理测试失败:{reason}。",
3035
+ invalidProxyUrl: "请输入不含凭据或路径的 HTTP(S) 代理 origin。",
2754
3036
  enableSearch: "启用 Codex 搜索提供方",
2755
3037
  enableSearchHelp: "让 OpenAI Codex 可被选作搜索提供方,但不会自动改动全局搜索路由。",
2756
3038
  searchModel: "搜索模型",
@@ -2859,6 +3141,7 @@ window.__ModuleLoader__.load({
2859
3141
  updateHighlightImageGeneration: "在 Codex Connect 设置中加入可选的 GPT Image 生图,并在对话里展示生成结果卡片。",
2860
3142
  updateHighlightOauthHistory: "增加更安全的会话历史迁移,并在账号需要重新登录时提供备用登录路径。",
2861
3143
  updateHighlightModelVisibility: "现在可以选择哪些 Codex 模型显示在模型选择菜单中。",
3144
+ updateHighlightProxyConnection: "增加可选的 Codex 代理检测,启用前需要明确确认,并始终保留直连回退入口。",
2862
3145
  viewTechnicalDetails: "查看完整技术说明",
2863
3146
  hideTechnicalDetails: "收起技术说明",
2864
3147
  technicalDetailsHeading: "技术变更",
@@ -3947,7 +4230,7 @@ window.__ModuleLoader__.load({
3947
4230
  }
3948
4231
  //#endregion
3949
4232
  //#region src/version.ts
3950
- const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.19";
4233
+ const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.20";
3951
4234
  //#endregion
3952
4235
  //#region src/client/index.tsx
3953
4236
  /** Stable browser-plugin name. */