@eddyskywalker/dsh-chatgpt-subscription 0.1.28 → 0.1.30

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.
Files changed (40) hide show
  1. package/lib/client.js +373 -454
  2. package/lib/client.js.map +1 -1
  3. package/lib/index.js +330 -280
  4. package/lib/types/client/CodexSubscriptionSection.d.ts +2 -2
  5. package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
  6. package/lib/types/client/api.d.ts +1 -2
  7. package/lib/types/client/api.d.ts.map +1 -1
  8. package/lib/types/client/index.d.ts.map +1 -1
  9. package/lib/types/client/locales.d.ts +49 -59
  10. package/lib/types/client/locales.d.ts.map +1 -1
  11. package/lib/types/client/quota.d.ts.map +1 -1
  12. package/lib/types/client/styles.d.ts.map +1 -1
  13. package/lib/types/compat.d.ts +1 -0
  14. package/lib/types/compat.d.ts.map +1 -1
  15. package/lib/types/host/codex-fetch.d.ts +10 -0
  16. package/lib/types/host/codex-fetch.d.ts.map +1 -0
  17. package/lib/types/host/model-catalog.d.ts.map +1 -1
  18. package/lib/types/host/preferences.d.ts.map +1 -1
  19. package/lib/types/host/proxy-manager.d.ts +29 -0
  20. package/lib/types/host/proxy-manager.d.ts.map +1 -0
  21. package/lib/types/host/responses-mapper.d.ts.map +1 -1
  22. package/lib/types/host/routes.d.ts +2 -1
  23. package/lib/types/host/routes.d.ts.map +1 -1
  24. package/lib/types/host/search-provider-switcher.d.ts +2 -1
  25. package/lib/types/host/search-provider-switcher.d.ts.map +1 -1
  26. package/lib/types/index.d.ts +2 -0
  27. package/lib/types/index.d.ts.map +1 -1
  28. package/lib/types/shared/contracts.d.ts +11 -38
  29. package/lib/types/shared/contracts.d.ts.map +1 -1
  30. package/lib/types/shared/preferences.d.ts +2 -1
  31. package/lib/types/shared/preferences.d.ts.map +1 -1
  32. package/package.json +10 -11
  33. package/lib/types/client/SubagentSettingsSection.d.ts +0 -9
  34. package/lib/types/client/SubagentSettingsSection.d.ts.map +0 -1
  35. package/lib/types/host/subagent-context-adapter.d.ts +0 -24
  36. package/lib/types/host/subagent-context-adapter.d.ts.map +0 -1
  37. package/lib/types/host/subagent-model-preference.d.ts +0 -5
  38. package/lib/types/host/subagent-model-preference.d.ts.map +0 -1
  39. package/lib/types/host/subagent-policy.d.ts +0 -17
  40. package/lib/types/host/subagent-policy.d.ts.map +0 -1
package/lib/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import { CallId, HarnessError, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
2
+ import { WebError } from "@deepseek-ai/dsh-web";
2
3
  import { createUserMessage } from "@deepseek-ai/dsh-llm/message";
3
4
  import { defineTool } from "@deepseek-ai/dsh-tools";
4
5
  import { createHash, randomBytes, randomUUID } from "node:crypto";
5
- import { WebError } from "@deepseek-ai/dsh-web";
6
6
  import http from "node:http";
7
+ import { execSync, spawn } from "node:child_process";
8
+ import { ProxyAgent, fetch as fetch$1 } from "undici";
7
9
  import { settingsNamespace } from "@deepseek-ai/dsh-settings";
8
10
  import z from "@deepseek-ai/schemastery";
9
- import { spawn } from "node:child_process";
10
11
  import { constants } from "node:fs";
11
12
  import { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
12
13
  import { homedir } from "node:os";
13
14
  import { dirname, join } from "node:path";
14
- import { delegationDepthOf } from "@deepseek-ai/dsh-subagent";
15
15
  //#region src/compat.ts
16
16
  /**
17
17
  * Compatibility constants for the ChatGPT-backed Codex flow. The backend and
@@ -39,6 +39,7 @@ const CODEX_ORIGINATOR = "opencode";
39
39
  const CODEX_IMAGE_TOOL_NAME = "codex_image_generate";
40
40
  const CODEX_IMAGE_MODEL = "gpt-image-2";
41
41
  const CODEX_SEARCH_PROVIDER_ID = "codex-subscription";
42
+ const CODEX_FETCH_PROVIDER_ID = "codex-subscription";
42
43
  const QUOTA_MIN_UPSTREAM_INTERVAL_MS = 15e3;
43
44
  const OAUTH_AUTHORIZE_URL = `${CHATGPT_OAUTH_ISSUER}/oauth/authorize`;
44
45
  const OAUTH_TOKEN_URL = `${CHATGPT_OAUTH_ISSUER}/oauth/token`;
@@ -162,7 +163,8 @@ function listCodexModels(preferences) {
162
163
  }
163
164
  function resolveCodexModel(model, preferences) {
164
165
  const entry = resolveCodexCatalogEntry(model);
165
- const configuredContextWindow = isConfigurableContextModelId(model) ? preferences?.status().contextWindowOverrides[model] : void 0;
166
+ const status = preferences?.status();
167
+ const configuredContextWindow = isConfigurableContextModelId(model) ? status?.contextWindowOverrides[model] : void 0;
166
168
  const efforts = reasoningEffortsForModel(model);
167
169
  return {
168
170
  provider: PROVIDER_ID,
@@ -230,6 +232,79 @@ var CodexChatGptAdapter = class extends LlmAdapter {
230
232
  }
231
233
  };
232
234
  //#endregion
235
+ //#region src/host/codex-fetch.ts
236
+ const DEFAULT_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
237
+ const DEFAULT_MAX_BODY_CHARS = 1e5;
238
+ function createCodexFetchProvider(options = {}) {
239
+ const fetchFn = options.fetchFn ?? fetch;
240
+ const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
241
+ const maxBodyChars = options.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;
242
+ return {
243
+ id: CODEX_FETCH_PROVIDER_ID,
244
+ available: () => true,
245
+ async fetch(request, signal) {
246
+ const targetUrl = request.url.trim();
247
+ let parsedUrl;
248
+ try {
249
+ parsedUrl = new URL(targetUrl);
250
+ } catch (error) {
251
+ throw new WebError(`invalid URL: ${targetUrl}`, "WEB_INVALID_URL", { cause: error });
252
+ }
253
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new WebError(`unsupported URL scheme "${parsedUrl.protocol}" (only http and https are allowed)`, "WEB_INVALID_URL");
254
+ let response;
255
+ try {
256
+ response = await fetchFn(targetUrl, {
257
+ method: "GET",
258
+ headers: {
259
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
260
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,text/plain;q=0.8,*/*;q=0.5"
261
+ },
262
+ redirect: "follow",
263
+ signal
264
+ });
265
+ } catch (error) {
266
+ if (signal?.aborted) throw new WebError("web fetch aborted", "WEB_ABORTED", { cause: error });
267
+ throw new WebError(`web fetch failed: ${error instanceof Error ? error.message : String(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
268
+ }
269
+ const contentType = response.headers.get("content-type") || "";
270
+ const mime = contentType.replace(/;.*$/s, "").trim().toLowerCase();
271
+ const kind = mime === "text/html" || mime === "application/xhtml+xml" ? "html" : "text";
272
+ let charset = "utf-8";
273
+ const match = /;\s*charset\s*=\s*"?([^";]+)"?/i.exec(contentType);
274
+ if (match?.[1]) charset = match[1].trim().toLowerCase();
275
+ let decoder;
276
+ try {
277
+ decoder = new TextDecoder(charset);
278
+ } catch {
279
+ decoder = new TextDecoder("utf-8");
280
+ }
281
+ let rawBytes;
282
+ let truncatedByBytes = false;
283
+ try {
284
+ const buffer = await response.arrayBuffer();
285
+ if (buffer.byteLength > maxResponseBytes) {
286
+ rawBytes = new Uint8Array(buffer.slice(0, maxResponseBytes));
287
+ truncatedByBytes = true;
288
+ } else rawBytes = new Uint8Array(buffer);
289
+ } catch (error) {
290
+ throw new WebError(`failed to read response body: ${error instanceof Error ? error.message : String(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
291
+ }
292
+ const decodedText = decoder.decode(rawBytes);
293
+ const truncatedByChars = decodedText.length > maxBodyChars;
294
+ const finalContent = truncatedByChars ? decodedText.slice(0, maxBodyChars) : decodedText;
295
+ return {
296
+ url: response.url || targetUrl,
297
+ statusCode: response.status,
298
+ body: {
299
+ kind,
300
+ content: finalContent
301
+ },
302
+ truncated: truncatedByBytes || truncatedByChars
303
+ };
304
+ }
305
+ };
306
+ }
307
+ //#endregion
233
308
  //#region src/host/wire-auth.ts
234
309
  function codexHeaders(credentials, sessionId) {
235
310
  const dshAgent = attributionHeaders()["user-agent"] ?? "dsh/unknown";
@@ -502,10 +577,7 @@ function sendSearch(fetchFn, credentials, query, model, id, signal) {
502
577
  id,
503
578
  model,
504
579
  input: query,
505
- commands: {
506
- search_query: [{ q: query }],
507
- response_length: "short"
508
- },
580
+ commands: { search_query: [{ q: query }] },
509
581
  settings: {
510
582
  allowed_callers: ["direct"],
511
583
  external_web_access: true
@@ -1075,6 +1147,154 @@ async function oauthErrorIdentifier(response) {
1075
1147
  return null;
1076
1148
  }
1077
1149
  //#endregion
1150
+ //#region src/host/proxy-manager.ts
1151
+ const SYSTEM_PROXY_CACHE_TTL_MS = 5e3;
1152
+ function normalizeProxyUrl(rawUrl) {
1153
+ const trimmed = rawUrl.trim();
1154
+ if (!trimmed) return "";
1155
+ if (/^https?:\/\//i.test(trimmed) || /^socks5?:\/\//i.test(trimmed)) return trimmed;
1156
+ return `http://${trimmed}`;
1157
+ }
1158
+ function parseWindowsProxyRegistry(stdout) {
1159
+ const enableMatch = stdout.match(/ProxyEnable\s+REG_DWORD\s+(0x[0-9a-fA-F]+|\d+)/i);
1160
+ if (!enableMatch) return null;
1161
+ if ((enableMatch[1].startsWith("0x") ? parseInt(enableMatch[1], 16) : parseInt(enableMatch[1], 10)) !== 1) return null;
1162
+ const serverMatch = stdout.match(/ProxyServer\s+REG_SZ\s+([^\r\n]+)/i);
1163
+ if (!serverMatch) return null;
1164
+ const rawServer = serverMatch[1].trim();
1165
+ if (!rawServer) return null;
1166
+ if (rawServer.includes("=")) {
1167
+ const pairs = rawServer.split(";");
1168
+ const map = {};
1169
+ for (const pair of pairs) {
1170
+ const [proto, addr] = pair.split("=").map((s) => s.trim());
1171
+ if (proto && addr) map[proto.toLowerCase()] = addr;
1172
+ }
1173
+ const target = map.https || map.http || map.socks;
1174
+ if (target) {
1175
+ if (map.socks && !map.https && !map.http) return normalizeProxyUrl(target.startsWith("socks") ? target : `socks5://${target}`);
1176
+ return normalizeProxyUrl(target);
1177
+ }
1178
+ return null;
1179
+ }
1180
+ return normalizeProxyUrl(rawServer);
1181
+ }
1182
+ function parseMacOsScutilProxy(stdout) {
1183
+ const httpsEnable = /HTTPSEnable\s*:\s*1/i.test(stdout);
1184
+ const httpEnable = /HTTPEnable\s*:\s*1/i.test(stdout);
1185
+ const socksEnable = /SOCKSEnable\s*:\s*1/i.test(stdout);
1186
+ if (httpsEnable) {
1187
+ const host = stdout.match(/HTTPSProxy\s*:\s*([^\s\r\n]+)/i)?.[1];
1188
+ const port = stdout.match(/HTTPSPort\s*:\s*(\d+)/i)?.[1];
1189
+ if (host && port) return normalizeProxyUrl(`${host}:${port}`);
1190
+ }
1191
+ if (httpEnable) {
1192
+ const host = stdout.match(/HTTPProxy\s*:\s*([^\s\r\n]+)/i)?.[1];
1193
+ const port = stdout.match(/HTTPPort\s*:\s*(\d+)/i)?.[1];
1194
+ if (host && port) return normalizeProxyUrl(`${host}:${port}`);
1195
+ }
1196
+ if (socksEnable) {
1197
+ const host = stdout.match(/SOCKSProxy\s*:\s*([^\s\r\n]+)/i)?.[1];
1198
+ const port = stdout.match(/SOCKSPort\s*:\s*(\d+)/i)?.[1];
1199
+ if (host && port) return normalizeProxyUrl(`socks5://${host}:${port}`);
1200
+ }
1201
+ return null;
1202
+ }
1203
+ function parseEnvProxy(env = process.env) {
1204
+ const proxy = env.HTTPS_PROXY || env.https_proxy || env.HTTP_PROXY || env.http_proxy || env.ALL_PROXY || env.all_proxy;
1205
+ if (!proxy || !proxy.trim()) return null;
1206
+ return normalizeProxyUrl(proxy);
1207
+ }
1208
+ function detectSystemProxy(platform = process.platform, env = process.env) {
1209
+ try {
1210
+ if (platform === "win32") {
1211
+ const detected = parseWindowsProxyRegistry(execSync("reg query \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\"", {
1212
+ timeout: 1500,
1213
+ encoding: "utf-8",
1214
+ stdio: [
1215
+ "ignore",
1216
+ "pipe",
1217
+ "ignore"
1218
+ ]
1219
+ }));
1220
+ if (detected) return detected;
1221
+ } else if (platform === "darwin") {
1222
+ const detected = parseMacOsScutilProxy(execSync("scutil --proxy", {
1223
+ timeout: 1500,
1224
+ encoding: "utf-8",
1225
+ stdio: [
1226
+ "ignore",
1227
+ "pipe",
1228
+ "ignore"
1229
+ ]
1230
+ }));
1231
+ if (detected) return detected;
1232
+ }
1233
+ } catch {}
1234
+ return parseEnvProxy(env);
1235
+ }
1236
+ var ProxyManager = class {
1237
+ getPreferences;
1238
+ baseFetch;
1239
+ systemProxyDetector;
1240
+ logger;
1241
+ cachedSystemProxy = null;
1242
+ lastSystemProxyCheck = 0;
1243
+ agents = /* @__PURE__ */ new Map();
1244
+ constructor(options) {
1245
+ this.getPreferences = options.getPreferences;
1246
+ this.baseFetch = options.baseFetch ?? fetch;
1247
+ this.systemProxyDetector = options.systemProxyDetector ?? (() => detectSystemProxy());
1248
+ this.logger = options.logger;
1249
+ }
1250
+ getSystemProxy(force = false) {
1251
+ const now = Date.now();
1252
+ if (!force && now - this.lastSystemProxyCheck < SYSTEM_PROXY_CACHE_TTL_MS) return this.cachedSystemProxy;
1253
+ this.lastSystemProxyCheck = now;
1254
+ try {
1255
+ this.cachedSystemProxy = this.systemProxyDetector();
1256
+ } catch (error) {
1257
+ this.cachedSystemProxy = null;
1258
+ this.logger?.warn?.(`[dsh-chatgpt-subscription] Failed to detect system proxy: ${error instanceof Error ? error.message : String(error)}`);
1259
+ }
1260
+ return this.cachedSystemProxy;
1261
+ }
1262
+ resolveActiveProxyUrl() {
1263
+ const prefs = this.getPreferences();
1264
+ const mode = prefs.proxyMode ?? "auto";
1265
+ if (mode === "direct") return null;
1266
+ if (mode === "custom") return prefs.customProxyUrl ? normalizeProxyUrl(prefs.customProxyUrl) : null;
1267
+ return this.getSystemProxy();
1268
+ }
1269
+ getOrCreateAgent(proxyUrl) {
1270
+ let agent = this.agents.get(proxyUrl);
1271
+ if (!agent) {
1272
+ agent = new ProxyAgent(proxyUrl);
1273
+ this.agents.set(proxyUrl, agent);
1274
+ }
1275
+ return agent;
1276
+ }
1277
+ createFetch() {
1278
+ return async (input, init) => {
1279
+ const activeProxy = this.resolveActiveProxyUrl();
1280
+ if (!activeProxy) return this.baseFetch(input, init);
1281
+ try {
1282
+ const agent = this.getOrCreateAgent(activeProxy);
1283
+ return await fetch$1(input, {
1284
+ ...init,
1285
+ dispatcher: agent
1286
+ });
1287
+ } catch (error) {
1288
+ throw error;
1289
+ }
1290
+ };
1291
+ }
1292
+ dispose() {
1293
+ for (const agent of this.agents.values()) agent.destroy().catch(() => void 0);
1294
+ this.agents.clear();
1295
+ }
1296
+ };
1297
+ //#endregion
1078
1298
  //#region src/shared/preferences.ts
1079
1299
  const PREFERENCES_NAMESPACE = "dsh-chatgpt-subscription";
1080
1300
  const DEFAULT_PREFERENCES = {
@@ -1083,17 +1303,15 @@ const DEFAULT_PREFERENCES = {
1083
1303
  outputVerbosity: null,
1084
1304
  visibleModelIds: [...DEFAULT_VISIBLE_CODEX_MODEL_IDS],
1085
1305
  searchProvider: "dsh",
1086
- subagentProvider: "codex-chatgpt",
1087
- subagentModel: "gpt-5.6-sol",
1088
- subagentReasoningEffort: "medium",
1089
- subagentContextWindow: 272e3,
1090
- subagentMaxDepth: 3,
1091
- subagentMaxAgents: 8,
1092
1306
  contextWindowOverrides: {
1093
1307
  "gpt-5.6-sol": 272e3,
1094
1308
  "gpt-5.6-terra": 272e3,
1095
1309
  "gpt-5.6-luna": 272e3
1096
- }
1310
+ },
1311
+ subagentContextWindow: null,
1312
+ subagentMaxDepth: null,
1313
+ proxyMode: "auto",
1314
+ customProxyUrl: null
1097
1315
  };
1098
1316
  const SEARCH_PROVIDER_CODEX = "codex";
1099
1317
  function isSearchProviderPreference(value) {
@@ -1102,6 +1320,9 @@ function isSearchProviderPreference(value) {
1102
1320
  function isCodexOutputVerbosity(value) {
1103
1321
  return value === "low" || value === "medium" || value === "high";
1104
1322
  }
1323
+ function isProxyMode(value) {
1324
+ return value === "auto" || value === "custom" || value === "direct";
1325
+ }
1105
1326
  //#endregion
1106
1327
  //#region src/host/preferences.ts
1107
1328
  function registerPreferenceStore(settings) {
@@ -1116,17 +1337,19 @@ function registerPreferenceStore(settings) {
1116
1337
  ]).default(DEFAULT_PREFERENCES.outputVerbosity),
1117
1338
  visibleModelIds: z.array(z.string()).default(DEFAULT_PREFERENCES.visibleModelIds),
1118
1339
  searchProvider: z.union([z.const("dsh"), z.const(SEARCH_PROVIDER_CODEX)]).default(DEFAULT_PREFERENCES.searchProvider),
1119
- subagentProvider: z.string().default(DEFAULT_PREFERENCES.subagentProvider),
1120
- subagentModel: z.string().default(DEFAULT_PREFERENCES.subagentModel),
1121
- subagentReasoningEffort: z.union([z.string(), z.const(null)]).default(DEFAULT_PREFERENCES.subagentReasoningEffort),
1122
- subagentContextWindow: z.number().step(1).min(1).default(DEFAULT_PREFERENCES.subagentContextWindow),
1123
- subagentMaxDepth: z.number().step(1).min(0).max(3).default(DEFAULT_PREFERENCES.subagentMaxDepth),
1124
- subagentMaxAgents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_PREFERENCES.subagentMaxAgents),
1125
1340
  contextWindowOverrides: z.object({
1126
1341
  "gpt-5.6-sol": z.number().step(1).min(1).max(GPT_56_MAX_CONTEXT_WINDOW).default(DEFAULT_PREFERENCES.contextWindowOverrides["gpt-5.6-sol"]),
1127
1342
  "gpt-5.6-terra": z.number().step(1).min(1).max(GPT_56_MAX_CONTEXT_WINDOW).default(DEFAULT_PREFERENCES.contextWindowOverrides["gpt-5.6-terra"]),
1128
1343
  "gpt-5.6-luna": z.number().step(1).min(1).max(GPT_56_MAX_CONTEXT_WINDOW).default(DEFAULT_PREFERENCES.contextWindowOverrides["gpt-5.6-luna"])
1129
- }).default(DEFAULT_PREFERENCES.contextWindowOverrides)
1344
+ }).default(DEFAULT_PREFERENCES.contextWindowOverrides),
1345
+ subagentContextWindow: z.union([z.number().step(1).min(1), z.const(null)]).default(DEFAULT_PREFERENCES.subagentContextWindow),
1346
+ subagentMaxDepth: z.union([z.number().step(1).min(0).max(3), z.const(null)]).default(DEFAULT_PREFERENCES.subagentMaxDepth),
1347
+ proxyMode: z.union([
1348
+ z.const("auto"),
1349
+ z.const("custom"),
1350
+ z.const("direct")
1351
+ ]).default(DEFAULT_PREFERENCES.proxyMode),
1352
+ customProxyUrl: z.union([z.string(), z.const(null)]).default(DEFAULT_PREFERENCES.customProxyUrl)
1130
1353
  })));
1131
1354
  }
1132
1355
  var SettingsPreferenceStore = class {
@@ -1153,16 +1376,21 @@ var SettingsPreferenceStore = class {
1153
1376
  if (!isSearchProviderPreference(patch.searchProvider)) throw new PreferenceError("Unsupported search provider preference.");
1154
1377
  normalized.searchProvider = patch.searchProvider;
1155
1378
  }
1156
- if (patch.subagentProvider !== void 0) normalized.subagentProvider = patch.subagentProvider;
1157
- if (patch.subagentModel !== void 0) normalized.subagentModel = patch.subagentModel;
1158
- if (patch.subagentReasoningEffort !== void 0) normalized.subagentReasoningEffort = patch.subagentReasoningEffort;
1159
- if (patch.subagentContextWindow !== void 0) normalized.subagentContextWindow = patch.subagentContextWindow;
1160
- if (patch.subagentMaxDepth !== void 0) normalized.subagentMaxDepth = patch.subagentMaxDepth;
1161
- if (patch.subagentMaxAgents !== void 0) normalized.subagentMaxAgents = patch.subagentMaxAgents;
1162
1379
  if (patch.contextWindowOverrides !== void 0) normalized.contextWindowOverrides = {
1163
1380
  ...this.scope.get().contextWindowOverrides,
1164
1381
  ...patch.contextWindowOverrides
1165
1382
  };
1383
+ if (patch.subagentContextWindow !== void 0) normalized.subagentContextWindow = patch.subagentContextWindow;
1384
+ if (patch.subagentMaxDepth !== void 0) normalized.subagentMaxDepth = patch.subagentMaxDepth;
1385
+ if (patch.proxyMode !== void 0) {
1386
+ if (!isProxyMode(patch.proxyMode)) throw new PreferenceError("Unsupported proxy mode preference.");
1387
+ normalized.proxyMode = patch.proxyMode;
1388
+ }
1389
+ if (patch.customProxyUrl !== void 0) if (patch.customProxyUrl !== null) {
1390
+ const trimmed = patch.customProxyUrl.trim();
1391
+ if (trimmed.length > 0 && !/^https?:\/\//i.test(trimmed) && !/^socks5?:\/\//i.test(trimmed)) normalized.customProxyUrl = `http://${trimmed}`;
1392
+ else normalized.customProxyUrl = trimmed.length === 0 ? null : trimmed;
1393
+ } else normalized.customProxyUrl = null;
1166
1394
  await this.scope.update(normalized);
1167
1395
  return this.status();
1168
1396
  }
@@ -1223,6 +1451,7 @@ async function buildResponsesPayload(options, attachments, localRawImages = {},
1223
1451
  const instructionParts = [
1224
1452
  options.system?.trim(),
1225
1453
  ...options.messages.filter((message) => message.role === "system").map((message) => blocksToText(message.content).trim()),
1454
+ progressExplanationInstruction(options.tools),
1226
1455
  sandboxToolInstruction(options.tools, sandboxRetryTools),
1227
1456
  commandToolInstruction(options.tools),
1228
1457
  runCodeInstruction(options.tools)
@@ -1305,6 +1534,10 @@ async function buildResponsesPayload(options, attachments, localRawImages = {},
1305
1534
  } : { effort: options.reasoningEffort };
1306
1535
  return payload;
1307
1536
  }
1537
+ function progressExplanationInstruction(tools) {
1538
+ if (!tools?.length) return void 0;
1539
+ return "Progress and tool execution rule: when executing multi-step tasks or invoking tools, output 1-2 concise sentences of progress, intent, or intermediate findings before each tool call. Keep progress text brief, professional, and factual. Only present the comprehensive final answer and summary in the final turn after all tool operations are completed.";
1540
+ }
1308
1541
  function runCodeInstruction(tools) {
1309
1542
  if (!tools?.some((tool) => tool.name === "run_code")) return void 0;
1310
1543
  return "run_code compatibility rule: its code is parsed as strict JavaScript/TypeScript before execution. Shell commands are nested string data: JavaScript template literals may consume ${...}, backticks, backslashes, and escape sequences before PowerShell, Bash, or POSIX sh sees them. On Windows, avoid embedding PowerShell containing $, ${...}, backslashes, or here-strings in template literals; String.raw does not disable ${...} interpolation. On Linux, prefer ordinary quoted strings or write a script file before invoking bash/sh, especially for commands containing backticks or ${...}. Prefer arrays of ordinary quoted strings joined with \"\\n\", escaping backslashes, or use a file-write tool for large scripts.";
@@ -2379,61 +2612,11 @@ function identityKey(credentials) {
2379
2612
  return credentials.accountId ?? credentials.email ?? credentials.planType ?? "signed-in";
2380
2613
  }
2381
2614
  //#endregion
2382
- //#region src/host/subagent-context-adapter.ts
2383
- /**
2384
- * A private provider route that delegates streaming to a selected registered
2385
- * provider while exposing a user-selected effective context window to DSH.
2386
- */
2387
- var SubagentContextAdapter = class SubagentContextAdapter extends LlmAdapter {
2388
- llm;
2389
- selection;
2390
- static provider = "dsh-subagent-model-override";
2391
- constructor(llm, selection) {
2392
- super();
2393
- this.llm = llm;
2394
- this.selection = selection;
2395
- }
2396
- providerInfo() {
2397
- return {
2398
- id: SubagentContextAdapter.provider,
2399
- name: "Subagent model override"
2400
- };
2401
- }
2402
- async listModels() {
2403
- return [];
2404
- }
2405
- async resolveModel(_provider, _model, signal) {
2406
- const selected = this.selection();
2407
- return {
2408
- ...await this.llm.resolveModelInfo(selected.provider, selected.model, signal),
2409
- provider: SubagentContextAdapter.provider,
2410
- id: selected.model,
2411
- context: { contextWindow: selected.contextWindow }
2412
- };
2413
- }
2414
- stream(options) {
2415
- const selected = this.selection();
2416
- const { preparedCall: _preparedCall, ...forwarded } = options;
2417
- return this.llm.stream({
2418
- ...forwarded,
2419
- provider: selected.provider,
2420
- model: selected.model
2421
- });
2422
- }
2423
- };
2424
- //#endregion
2425
2615
  //#region src/host/routes.ts
2426
2616
  const MAX_BODY_BYTES = 64 * 1024;
2427
- function registerRoutes(ctx, oauth, usage, preferences) {
2617
+ function registerRoutes(ctx, oauth, usage, preferences, proxyManager) {
2428
2618
  const handler = async (request, response) => {
2429
2619
  const url = new URL(request.url ?? "/", "http://dsh.local");
2430
- if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/models`) {
2431
- json(response, {
2432
- ok: true,
2433
- value: await subagentModelCatalog(ctx)
2434
- });
2435
- return;
2436
- }
2437
2620
  if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/status`) {
2438
2621
  const oauthStatus = await oauth.status();
2439
2622
  json(response, {
@@ -2441,7 +2624,9 @@ function registerRoutes(ctx, oauth, usage, preferences) {
2441
2624
  value: {
2442
2625
  ...oauthStatus,
2443
2626
  quota: await usage.status(oauthStatus.authenticated),
2444
- preferences: preferences.status()
2627
+ preferences: preferences.status(),
2628
+ detectedProxy: proxyManager?.getSystemProxy() ?? null,
2629
+ activeProxy: proxyManager?.resolveActiveProxyUrl() ?? null
2445
2630
  }
2446
2631
  });
2447
2632
  return;
@@ -2674,30 +2859,6 @@ function readPreferencesUpdate(value, current) {
2674
2859
  if (value.searchProvider !== "dsh" && value.searchProvider !== "codex") throw new PreferenceError("searchProvider must be dsh or codex.");
2675
2860
  patch.searchProvider = value.searchProvider;
2676
2861
  }
2677
- if ("subagentProvider" in value) {
2678
- if (typeof value.subagentProvider !== "string" || value.subagentProvider === "") throw new PreferenceError("subagentProvider must be a non-empty string.");
2679
- patch.subagentProvider = value.subagentProvider;
2680
- }
2681
- if ("subagentModel" in value) {
2682
- if (typeof value.subagentModel !== "string" || value.subagentModel === "") throw new PreferenceError("subagentModel must be a non-empty string.");
2683
- patch.subagentModel = value.subagentModel;
2684
- }
2685
- if ("subagentReasoningEffort" in value) {
2686
- if (value.subagentReasoningEffort !== null && (typeof value.subagentReasoningEffort !== "string" || value.subagentReasoningEffort === "")) throw new PreferenceError("subagentReasoningEffort must be null or a non-empty string.");
2687
- patch.subagentReasoningEffort = value.subagentReasoningEffort;
2688
- }
2689
- if ("subagentContextWindow" in value) {
2690
- if (!Number.isSafeInteger(value.subagentContextWindow) || value.subagentContextWindow < 1) throw new PreferenceError("subagentContextWindow must be a positive integer.");
2691
- patch.subagentContextWindow = value.subagentContextWindow;
2692
- }
2693
- if ("subagentMaxDepth" in value) {
2694
- if (!Number.isSafeInteger(value.subagentMaxDepth) || value.subagentMaxDepth < 0 || value.subagentMaxDepth > 3) throw new PreferenceError(`subagentMaxDepth must be an integer from 0 to 3.`);
2695
- patch.subagentMaxDepth = value.subagentMaxDepth;
2696
- }
2697
- if ("subagentMaxAgents" in value) {
2698
- if (!Number.isSafeInteger(value.subagentMaxAgents) || value.subagentMaxAgents < 1) throw new PreferenceError("subagentMaxAgents must be a positive integer.");
2699
- patch.subagentMaxAgents = value.subagentMaxAgents;
2700
- }
2701
2862
  if ("contextWindowOverrides" in value) {
2702
2863
  if (!isRecord(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
2703
2864
  const overrides = {};
@@ -2708,55 +2869,24 @@ function readPreferencesUpdate(value, current) {
2708
2869
  }
2709
2870
  patch.contextWindowOverrides = overrides;
2710
2871
  }
2872
+ if ("subagentContextWindow" in value) {
2873
+ if (value.subagentContextWindow !== null && (!Number.isSafeInteger(value.subagentContextWindow) || value.subagentContextWindow < 1)) throw new PreferenceError("subagentContextWindow must be null or a positive integer.");
2874
+ patch.subagentContextWindow = value.subagentContextWindow;
2875
+ }
2876
+ if ("subagentMaxDepth" in value) {
2877
+ if (value.subagentMaxDepth !== null && (!Number.isSafeInteger(value.subagentMaxDepth) || value.subagentMaxDepth < 0 || value.subagentMaxDepth > 3)) throw new PreferenceError(`subagentMaxDepth must be null or an integer from 0 to 3.`);
2878
+ patch.subagentMaxDepth = value.subagentMaxDepth;
2879
+ }
2880
+ if ("proxyMode" in value) {
2881
+ if (value.proxyMode !== "auto" && value.proxyMode !== "custom" && value.proxyMode !== "direct") throw new PreferenceError("proxyMode must be auto, custom, or direct.");
2882
+ patch.proxyMode = value.proxyMode;
2883
+ }
2884
+ if ("customProxyUrl" in value) {
2885
+ if (value.customProxyUrl !== null && typeof value.customProxyUrl !== "string") throw new PreferenceError("customProxyUrl must be a string or null.");
2886
+ patch.customProxyUrl = value.customProxyUrl;
2887
+ }
2711
2888
  return patch;
2712
2889
  }
2713
- async function subagentModelCatalog(ctx) {
2714
- const results = await Promise.all(ctx.llm.listProviders().filter((provider) => provider.id !== SubagentContextAdapter.provider).map(async (provider) => {
2715
- try {
2716
- const models = await ctx.llm.listModels(provider.id);
2717
- return {
2718
- kind: "provider",
2719
- provider: {
2720
- id: provider.id,
2721
- name: provider.name,
2722
- models: await Promise.all(models.map(async (model) => {
2723
- const resolved = await ctx.llm.resolveModelInfo(provider.id, model.id);
2724
- return {
2725
- id: model.id,
2726
- name: model.name,
2727
- ...model.description === void 0 ? {} : { description: model.description },
2728
- ...resolved.context === void 0 ? {} : {
2729
- contextWindow: resolved.context.contextWindow,
2730
- maxContextWindow: provider.id === "codex-chatgpt" && isConfigurableContextModelId(model.id) ? GPT_56_MAX_CONTEXT_WINDOW : resolved.context.contextWindow
2731
- },
2732
- ...resolved.reasoning === void 0 ? {} : { reasoning: {
2733
- efforts: resolved.reasoning.efforts.map((effort) => ({
2734
- id: effort.id,
2735
- name: effort.name,
2736
- ...effort.description === void 0 ? {} : { description: effort.description }
2737
- })),
2738
- ...resolved.reasoning.defaultEffort === void 0 ? {} : { defaultEffort: resolved.reasoning.defaultEffort }
2739
- } }
2740
- };
2741
- }))
2742
- }
2743
- };
2744
- } catch (error) {
2745
- return {
2746
- kind: "failure",
2747
- failure: {
2748
- id: provider.id,
2749
- name: provider.name,
2750
- message: error instanceof Error ? error.message : String(error)
2751
- }
2752
- };
2753
- }
2754
- }));
2755
- return {
2756
- providers: results.filter((result) => result.kind === "provider").map((result) => result.provider),
2757
- failures: results.filter((result) => result.kind === "failure").map((result) => result.failure)
2758
- };
2759
- }
2760
2890
  function json(response, envelope, status = 200) {
2761
2891
  response.writeHead(status, {
2762
2892
  "content-type": "application/json; charset=utf-8",
@@ -3118,7 +3248,8 @@ function createPlatformTokenStore(platform = process.platform) {
3118
3248
  //#region src/host/search-provider-switcher.ts
3119
3249
  var SearchProviderSwitcher = class {
3120
3250
  loader;
3121
- originalProvider;
3251
+ originalSearchProvider;
3252
+ originalFetchProvider;
3122
3253
  initialized = false;
3123
3254
  constructor(loader) {
3124
3255
  this.loader = loader;
@@ -3128,14 +3259,19 @@ var SearchProviderSwitcher = class {
3128
3259
  if (entry === null) return;
3129
3260
  const config = currentConfig(entry);
3130
3261
  if (!this.initialized) {
3131
- this.originalProvider = typeof config.searchProvider === "string" && config.searchProvider !== "codex-subscription" ? config.searchProvider : void 0;
3262
+ this.originalSearchProvider = typeof config.searchProvider === "string" && config.searchProvider !== "codex-subscription" ? config.searchProvider : void 0;
3263
+ this.originalFetchProvider = typeof config.fetchProvider === "string" && config.fetchProvider !== "codex-subscription" ? config.fetchProvider : void 0;
3132
3264
  this.initialized = true;
3133
3265
  }
3134
- const selected = preference === "codex" ? CODEX_SEARCH_PROVIDER_ID : this.originalProvider;
3135
- if (config.searchProvider === selected) return;
3266
+ const selected = preference === "codex" ? CODEX_SEARCH_PROVIDER_ID : void 0;
3267
+ const nextSearch = selected ?? this.originalSearchProvider;
3268
+ const nextFetch = selected ? CODEX_FETCH_PROVIDER_ID : this.originalFetchProvider;
3269
+ if (config.searchProvider === nextSearch && config.fetchProvider === nextFetch) return;
3136
3270
  const nextConfig = { ...config };
3137
- if (selected === void 0) delete nextConfig.searchProvider;
3138
- else nextConfig.searchProvider = selected;
3271
+ if (nextSearch === void 0) delete nextConfig.searchProvider;
3272
+ else nextConfig.searchProvider = nextSearch;
3273
+ if (nextFetch === void 0) delete nextConfig.fetchProvider;
3274
+ else nextConfig.fetchProvider = nextFetch;
3139
3275
  await entry.update({ config: nextConfig }, true);
3140
3276
  }
3141
3277
  findWebEntry() {
@@ -3151,100 +3287,6 @@ function currentConfig(entry) {
3151
3287
  return typeof config === "object" && config !== null && !Array.isArray(config) ? config : {};
3152
3288
  }
3153
3289
  //#endregion
3154
- //#region src/host/subagent-model-preference.ts
3155
- /** Apply the configured provider/model/reasoning route to every delegated Agent. */
3156
- function installSubagentModelPreference(ctx, preferences) {
3157
- return ctx.on("agent/request", async (payload, next) => {
3158
- const resolved = await next();
3159
- if (payload.agent.session.header.origin !== "subagent") return resolved;
3160
- const selection = preferences.status();
3161
- return {
3162
- ...resolved,
3163
- provider: SubagentContextAdapter.provider,
3164
- model: selection.subagentModel,
3165
- ...selection.subagentReasoningEffort === null ? { reasoningEffort: void 0 } : { reasoningEffort: ReasoningEffortId(selection.subagentReasoningEffort) }
3166
- };
3167
- });
3168
- }
3169
- //#endregion
3170
- //#region src/host/subagent-policy.ts
3171
- const MANAGED_DELEGATION_TOOLS = /* @__PURE__ */ new Set(["subagent", "subagent_fork"]);
3172
- /**
3173
- * Enforce global limits with a user-facing preflight on built-in delegation
3174
- * tools and an authoritative synchronous Agent-publication gate. The latter
3175
- * also covers workflows, Ralph, and nested subagents without depending on the
3176
- * parent's provider or model. Throwing from `agent/created` vetoes and rolls
3177
- * back the new child before its loop starts; already-running children remain.
3178
- */
3179
- function installSubagentPolicy(ctx, preferences) {
3180
- const disposePreflight = ctx.on("tools/pre-execute", async (exec, next) => {
3181
- const parent = exec.agent;
3182
- if (parent === void 0 || !MANAGED_DELEGATION_TOOLS.has(exec.name)) return next();
3183
- const { subagentMaxDepth, subagentMaxAgents } = preferences.status();
3184
- const attemptedDepth = delegationDepthOf(parent) + 1;
3185
- if (attemptedDepth > subagentMaxDepth) return {
3186
- kind: "deny",
3187
- reason: `Subagent nesting depth limit reached: attempted depth ${attemptedDepth}, configured maximum ${subagentMaxDepth}.`
3188
- };
3189
- const activeAgents = activeDescendantCount(ctx, subagentTreeRoot(ctx, parent).id);
3190
- if (activeAgents >= subagentMaxAgents) return {
3191
- kind: "deny",
3192
- reason: `Subagent limit reached: ${activeAgents} active descendants in the Agent tree, configured maximum ${subagentMaxAgents}. Wait for one to finish or raise the global limit in Subagents settings.`
3193
- };
3194
- return next();
3195
- });
3196
- const disposePublicationGate = ctx.on("agent/created", ({ agent }) => {
3197
- if (agent.session.header.origin !== "subagent") return;
3198
- const { subagentMaxDepth, subagentMaxAgents } = preferences.status();
3199
- const depth = delegationDepthOf(agent);
3200
- if (depth > subagentMaxDepth) throw new Error(`Subagent nesting depth limit reached: attempted depth ${depth}, configured maximum ${subagentMaxDepth}.`);
3201
- const activeAgents = activeDescendantCount(ctx, subagentTreeRoot(ctx, agent).id);
3202
- if (activeAgents > subagentMaxAgents) throw new Error(`Subagent limit reached: attempted ${activeAgents} active descendants in the Agent tree, configured maximum ${subagentMaxAgents}. Wait for one to finish or raise the global limit in Subagents settings.`);
3203
- });
3204
- return () => {
3205
- disposePublicationGate();
3206
- disposePreflight();
3207
- };
3208
- }
3209
- /** Return the highest currently live ancestor that owns this subagent tree. */
3210
- function subagentTreeRoot(ctx, agent) {
3211
- let current = agent;
3212
- const visited = /* @__PURE__ */ new Set([agent.id]);
3213
- while (current.session.header.origin === "subagent") {
3214
- const parentId = current.session.header.parentSession;
3215
- if (parentId === void 0 || visited.has(parentId)) break;
3216
- const parent = ctx.agents.get(parentId);
3217
- if (parent === void 0) break;
3218
- visited.add(parent.id);
3219
- current = parent;
3220
- }
3221
- return current;
3222
- }
3223
- /** Return currently live descendants in one caller's subagent tree. */
3224
- function activeDescendantIds(ctx, parentId) {
3225
- const childrenByParent = /* @__PURE__ */ new Map();
3226
- for (const agent of ctx.agents.list()) {
3227
- if (agent.session.header.origin !== "subagent") continue;
3228
- const directParent = agent.session.header.parentSession;
3229
- if (directParent === void 0) continue;
3230
- const children = childrenByParent.get(directParent) ?? [];
3231
- children.push(agent.id);
3232
- childrenByParent.set(directParent, children);
3233
- }
3234
- const descendants = /* @__PURE__ */ new Set();
3235
- const pending = [...childrenByParent.get(parentId) ?? []];
3236
- while (pending.length > 0) {
3237
- const id = pending.pop();
3238
- if (descendants.has(id)) continue;
3239
- descendants.add(id);
3240
- pending.push(...childrenByParent.get(id) ?? []);
3241
- }
3242
- return descendants;
3243
- }
3244
- function activeDescendantCount(ctx, parentId) {
3245
- return activeDescendantIds(ctx, parentId).size;
3246
- }
3247
- //#endregion
3248
3290
  //#region src/index.ts
3249
3291
  const inject = [
3250
3292
  "webServer",
@@ -3253,53 +3295,61 @@ const inject = [
3253
3295
  "tools",
3254
3296
  "web",
3255
3297
  "settings",
3256
- "loader",
3257
- "agents"
3298
+ "loader"
3258
3299
  ];
3259
3300
  function apply(ctx) {
3260
- const oauth = new OAuthService(createPlatformTokenStore(), { logger: ctx.logger });
3261
- const usage = new UsageService(oauth);
3301
+ const store = createPlatformTokenStore();
3262
3302
  const preferences = registerPreferenceStore(ctx.settings);
3263
- const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
3264
- localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
3265
- onGenerationFinished: () => usage.invalidate(),
3266
- outputVerbosity: () => preferences.status().outputVerbosity,
3267
- fastMode: () => preferences.status().fastMode
3268
- }), preferences);
3269
3303
  ctx.effect(() => {
3304
+ const proxyManager = new ProxyManager({
3305
+ getPreferences: () => preferences.status(),
3306
+ logger: ctx.logger
3307
+ });
3308
+ const proxyFetch = proxyManager.createFetch();
3309
+ const oauth = new OAuthService(store, {
3310
+ fetchFn: proxyFetch,
3311
+ logger: ctx.logger
3312
+ });
3313
+ const usage = new UsageService(oauth, { fetchFn: proxyFetch });
3314
+ const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
3315
+ fetchFn: proxyFetch,
3316
+ localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
3317
+ onGenerationFinished: () => usage.invalidate(),
3318
+ outputVerbosity: () => preferences.status().outputVerbosity,
3319
+ fastMode: () => preferences.status().fastMode
3320
+ }), preferences);
3270
3321
  const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
3271
3322
  const applySearchPreference = (searchProvider = preferences.status().searchProvider) => {
3272
3323
  searchSwitcher.select(searchProvider).catch((error) => {
3273
3324
  ctx.logger.warn(`[dsh-chatgpt-subscription] Search provider preference could not be applied: ${error instanceof Error ? error.message : String(error)}`);
3274
3325
  });
3275
3326
  };
3276
- const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences);
3327
+ const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager);
3277
3328
  const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID], adapter);
3278
- const subagentContextAdapter = new SubagentContextAdapter(ctx.llm, () => {
3279
- const selected = preferences.status();
3280
- return {
3281
- provider: selected.subagentProvider,
3282
- model: selected.subagentModel,
3283
- contextWindow: selected.subagentContextWindow
3284
- };
3285
- });
3286
- const disposeSubagentContextAdapter = ctx.llm.registerAdapter([SubagentContextAdapter.provider], subagentContextAdapter);
3287
- const disposeSubagentModelPreference = installSubagentModelPreference(ctx, preferences);
3288
- const disposeSubagentPolicy = installSubagentPolicy(ctx, preferences);
3289
- const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments));
3290
- const disposeSearchProvider = ctx.web.registerSearchProvider(createCodexSearchProvider(oauth));
3329
+ const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments, { fetchFn: proxyFetch }));
3330
+ let disposeWebProviders = () => {};
3331
+ const registerWebProviders = () => {
3332
+ disposeWebProviders();
3333
+ if (ctx.web) {
3334
+ const d1 = ctx.web.registerSearchProvider(createCodexSearchProvider(oauth, { fetchFn: proxyFetch }));
3335
+ const d2 = ctx.web.registerFetchProvider(createCodexFetchProvider({ fetchFn: proxyFetch }));
3336
+ disposeWebProviders = () => {
3337
+ d1();
3338
+ d2();
3339
+ };
3340
+ }
3341
+ };
3342
+ registerWebProviders();
3291
3343
  const disposePreferenceWatch = preferences.watch((next) => applySearchPreference(next.searchProvider));
3292
3344
  applySearchPreference();
3293
3345
  return () => {
3294
3346
  disposePreferenceWatch();
3295
- disposeSubagentPolicy();
3296
- disposeSubagentModelPreference();
3297
- disposeSearchProvider();
3347
+ disposeWebProviders();
3298
3348
  disposeImageTool();
3299
- disposeSubagentContextAdapter();
3300
3349
  disposeAdapter();
3301
3350
  disposeRoutes();
3302
3351
  oauth.dispose();
3352
+ proxyManager.dispose();
3303
3353
  };
3304
3354
  }, "dsh-chatgpt-subscription: adapter, routes, and lifecycle");
3305
3355
  }
@@ -3307,4 +3357,4 @@ function localWebServerBaseUrl(host, port) {
3307
3357
  return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
3308
3358
  }
3309
3359
  //#endregion
3310
- export { CodexChatGptAdapter, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, inject, mapCodexUsage, parseCodexUsage, parseResponsesStream };
3360
+ export { CodexChatGptAdapter, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, detectSystemProxy, inject, mapCodexUsage, parseCodexUsage, parseResponsesStream };