@eddyskywalker/dsh-chatgpt-subscription 0.2.16 → 0.2.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ - 修复开启系统代理的机器上 `web_fetch` 必然失败的问题:DSH 内置抓取 provider 在连接前解析、校验并固定目标地址,而代理工具(Clash/Mihomo 等)的 fake-ip DNS 会把域名解析成 `198.18.0.0/15` 里的保留地址(实测 `api.github.com` → `198.18.0.17`),于是每次调用都以 `WEB_BLOCKED_URL`(`resolves to a non-public IP address`)结束——代理根本没被用上。DSH 只在进程环境变量里读到代理时才走代理,看不到系统代理。现在只要插件配置了可用代理(系统代理自动检测或自定义代理),`web_fetch` 就改用本插件的抓取 provider:由代理解析源站,与 DSH 对“走代理的请求”采用的语义一致;未配置代理时仍由内置 provider 抓取,其解析与固定策略不变。
6
+ - 抓取 provider 新增地址策略 `src/host/fetch-address-policy.ts`,保留内置 provider 安全边界中不需要 DNS 的那一半:URL 里写明的 IP 字面量只有全球可路由单播才放行(loopback、私网、链路本地、CGNAT、多播、保留地址、IPv6 转换与隧道前缀一律拒绝);域名用本机解析器检查一次,落在私网(含 `localhost`、`127.0.0.1.nip.io` 这类)一律拒绝;只有代理的 fake-ip 答案被接受,本机解析不出的域名交给代理处理。与内置 provider 的差别是不再固定(pin)连接地址——fake-ip 环境下这一步无法成立,已在 README 的安全边界中写明。
7
+ - 代理偏好在运行时变化(例如系统代理 ↔ 直连)会重新选择抓取后端;选择 ChatGPT 搜索来源时同时切换搜索与抓取的行为保持不变。
8
+ - `ProxyManager` 新增系统代理探测的观察点(`onSystemProxyDetected`):只在「从未知变为已知代理」时通知——探测失败与「本机没有代理」都读作 `null`,因此不会因为一次 `reg query` / `scutil` 抖动就把已经可用的路由拆掉。插件据此在代理迟于 DSH 出现时自动重新选择抓取后端,否则内置 provider 会一直占到进程结束。
9
+ - 新增 `test/fetch-address-policy.test.ts`(地址分类、fake-ip 识别、私网解析拒绝、解析失败放行),并扩充 `test/codex-fetch.test.ts`、`test/search-provider-switcher.test.ts`、`test/web-provider-lifecycle.test.ts` 覆盖抓取 provider 的拒绝路径与后端切换。
10
+
3
11
  ## 0.2.15 - 2026-09-11
4
12
 
5
13
  - 新增子代理模型授权守卫:DSH 的「Subagent」允许列表(`subagent-model-selection`)原本只在模型**显式**填写 `provider`/`model` 时生效,而委派调用不填路由时子代理会继承父级模型——于是 DeepSeek 会话里的子代理仍会在未授权模型上运行。插件现在在 Host 工具注册表上注册一个单调守卫(`ctx.tools.guard`),当调用会话(或最近的、记录了策略的祖先会话)带有允许列表时,任何生效路由不在此列表内的委派都会在子代理启动前被拒绝,拒绝理由里带上全部已授权路由,模型据此重试即可选中合规模型。
package/README.md CHANGED
@@ -121,7 +121,9 @@ DSH 模型选择器应显示 **“Codex(ChatGPT 订阅)”**。6 Astra 与 G
121
121
 
122
122
  **设置 → Codex 订阅 → 网络代理** 同时控制 GPT 与 Antigravity(Gemini)的 Host 请求,可选择系统代理(自动检测)、自定义代理或直连。Gemini 模型生成、网页登录后的令牌交换、令牌刷新、账号信息、项目发现、配额与模型目录查询均使用此设置;修改后对后续请求生效,无需重启 DSH。浏览器中的 Google 授权页面使用浏览器自己的网络设置。
123
123
 
124
- **搜索与抓取来源** 同时切换 DSH 的搜索和网页抓取后端。选择 ChatGPT 来源后,网页由本插件在 Host 抓取,并使用上述网络代理设置;纯 TUN 模式可使用直连,流量由虚拟网卡接管。来源切换即时生效,DSH web 服务重载后会重新注册插件后端,并保留切回 DSH 默认来源所需的配置。选择 DSH 默认来源时,抓取仍遵循 DSH 自身的代理及非公网地址校验策略。
124
+ **搜索与抓取来源** 切换 DSH 的搜索后端;网页抓取后端在有可用代理时自动改用本插件。选择 ChatGPT 来源后,网页由本插件在 Host 抓取,并使用上述网络代理设置;纯 TUN 模式可使用直连,流量由虚拟网卡接管。来源切换即时生效,DSH web 服务重载后会重新注册插件后端,并保留切回 DSH 默认来源所需的配置。
125
+
126
+ **网页抓取后端(web_fetch)** DSH 内置抓取 provider 会先解析域名、校验并固定解析结果,而且只在进程环境变量里读到代理时才走代理——系统代理对它不可见。代理工具(Clash/Mihomo 等)常把域名解析成自己的 fake-ip 地址(默认 `198.18.0.0/15`),于是内置 provider 直接以 `WEB_BLOCKED_URL`(resolves to a non-public IP address)拒绝,代理根本没被用上。因此只要插件配置了可用代理(**网络代理** 选系统代理且检测到,或填写自定义代理),`web_fetch` 就改用本插件的 provider:由代理解析源站,与 DSH 对“走代理的请求”采用的语义一致;未配置代理时仍由 DSH 内置 provider 抓取,保留其解析与固定策略。若在纯 TUN 模式下把代理设为**直连**,内置 provider 会重新接管,此时可改回系统代理让插件接管抓取。代理如果在 DSH 启动之后才可用(代理工具后启动,或首次探测失败),插件会在下一次探测到代理时重新选择抓取后端,不必重启或改设置。
125
127
 
126
128
  卸载前建议先在设置页点击 **“注销”**,它会删除当前平台的凭据和 Host 内存中的额度缓存。
127
129
 
@@ -170,6 +172,7 @@ Antigravity 的 Gemini 用量以流结束时的上游累计计数为准,缓存
170
172
  - Windows token bundle 使用 CurrentUser DPAPI;明文只经过 Host 内存和 PowerShell stdin/stdout;
171
173
  - macOS token bundle 存入登录钥匙串,通过系统 `security` 命令读写;明文只经过 Host 内存和 `security` 命令行参数,Keychain 在本机加密保存;
172
174
  - Linux token bundle 原子写入当前用户私有文件并在读取时校验普通文件、所有者与 `0600` 权限,同时拒绝符号链接;该文件没有应用层加密,同 UID 进程、root、备份和磁盘快照仍可读取;
175
+ - 本插件抓取 provider 的地址策略:URL 里直接写明的 IP 字面量只有在全球可路由单播时才放行(loopback、私网、链路本地、CGNAT、多播、保留地址、IPv6 转换与隧道前缀一律拒绝);域名会先用本机解析器检查一次,解析结果落在私网时同样拒绝,只有代理的 fake-ip 地址(`198.18.0.0/15`)被接受——那正是本机代理声称拥有该域名的表现;本机解析不出的域名交给代理处理。与 DSH 内置 provider 的差别是不固定(pin)连接地址,因为 fake-ip 环境下这一步无法成立;
173
176
  - 所有平台的 token 都不会进入浏览器、`settings.yaml` 或日志;
174
177
  - 工具调用只有在参数是完整 JSON 对象时才产生可执行的 `block-end`;畸形参数终止本次生成;
175
178
  - 所有修改状态的路由只接受同源 JSON POST,并校验 `Origin` 与 `Host`。
@@ -212,6 +215,7 @@ npm pack --dry-run
212
215
  | 额度显示旧数据 | 设置页会保留最后成功值;等待 15 秒节流窗口后手动刷新 |
213
216
  | 429 | 插件遵守 `Retry-After`,不会高频轮询;模型请求由 DSH retry policy 有界重试 |
214
217
  | 模型不可用 | 检查 ChatGPT 套餐、workspace 权限与当前模型可用性 |
218
+ | `web_fetch` 报 `resolves to a non-public IP address` | 机器启用了系统代理(fake-ip DNS),但插件没有可用代理可接管抓取:在 **设置 → Codex 订阅 → 网络代理** 选择系统代理(自动检测)或填写自定义代理 |
215
219
  | DPAPI 读取失败 | 确认 DSH 以创建凭据时的同一 Windows 用户运行;必要时清理凭据后重新登录 |
216
220
  | Linux 凭据存储不可用 | 确认凭据属于当前用户且权限为 `0600`,父目录权限为 `0700`;修复权限或注销后重新登录 |
217
221
  | Linux 上工具调用语法错误 | 确认 DSH 暴露的是 `bash`、`sh` 或 `shell`,并使用相应的 Bash/POSIX 语法与 `/` 路径 |
package/lib/index.js CHANGED
@@ -3,6 +3,8 @@ import z from "@deepseek-ai/schemastery";
3
3
  import * as LlmModule from "@deepseek-ai/dsh-llm";
4
4
  import { HarnessError, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
5
5
  import { WebError } from "@deepseek-ai/dsh-web";
6
+ import { lookup } from "node:dns/promises";
7
+ import { isIP } from "node:net";
6
8
  import { createUserMessage } from "@deepseek-ai/dsh-llm/message";
7
9
  import { defineTool } from "@deepseek-ai/dsh-tools";
8
10
  import { createHash, randomBytes, randomUUID } from "node:crypto";
@@ -274,6 +276,225 @@ var CodexChatGptAdapter = class extends LlmAdapter {
274
276
  }
275
277
  };
276
278
  //#endregion
279
+ //#region src/host/fetch-address-policy.ts
280
+ /**
281
+ * Destination policy for this plugin's fetch provider.
282
+ *
283
+ * DSH's built-in provider resolves every destination and validates and pins the
284
+ * address before it connects. That cannot work on a machine whose proxy answers
285
+ * DNS with its own fake-ip range (`198.18.0.0/15` for Clash/Mihomo, the usual
286
+ * companion of a system proxy): the hostname is ordinary, the answer is reserved
287
+ * space, and every request is refused before the proxy that could have resolved
288
+ * it is ever consulted. This provider exists for that machine, so it cannot
289
+ * validate names the way the built-in one does.
290
+ *
291
+ * It keeps the half of the policy that needs no resolution — an address the URL
292
+ * states outright is refused unless it is globally reachable unicast, because a
293
+ * proxy on this machine must not become a path into loopback or a LAN — and it
294
+ * also refuses a name this machine resolves into private space. Only the proxy's
295
+ * own fake-ip answers are accepted, since that is what the proxy's name
296
+ * resolution looks like from here; the URL hostname still reaches the proxy
297
+ * intact, so the proxy decides where the name really goes.
298
+ *
299
+ * @module dsh-chatgpt-subscription/fetch-address-policy
300
+ */
301
+ /**
302
+ * Every IPv4 range a fetch may not target: "this network", private space,
303
+ * carrier-grade NAT, loopback, link-local, IETF assignments, documentation,
304
+ * the 6to4 relay anycast, the benchmarking range the mainstream proxy tools use
305
+ * for fake-ip answers, multicast, and reserved space including the broadcast
306
+ * address.
307
+ */
308
+ const NON_PUBLIC_IPV4 = [
309
+ "0.0.0.0/8",
310
+ "10.0.0.0/8",
311
+ "100.64.0.0/10",
312
+ "127.0.0.0/8",
313
+ "169.254.0.0/16",
314
+ "172.16.0.0/12",
315
+ "192.0.0.0/24",
316
+ "192.0.2.0/24",
317
+ "192.88.99.0/24",
318
+ "192.168.0.0/16",
319
+ "198.18.0.0/15",
320
+ "198.51.100.0/24",
321
+ "203.0.113.0/24",
322
+ "224.0.0.0/4",
323
+ "240.0.0.0/4"
324
+ ];
325
+ /** The fake-ip range the mainstream proxy tools hand out for the names they own. */
326
+ const FAKE_IP_RANGE = "198.18.0.0/15";
327
+ /**
328
+ * Resolve one hostname to every address the local resolver returns, or to no
329
+ * addresses when it cannot.
330
+ *
331
+ * A failure is not evidence of a local destination: the configured proxy
332
+ * resolves the origin itself, so a name this machine cannot resolve must not
333
+ * fail a request the proxy could have served.
334
+ *
335
+ * @param hostname - the URL hostname, without brackets.
336
+ * @returns the resolved addresses, or an empty list when resolution failed.
337
+ */
338
+ async function lookupHostAddresses(hostname) {
339
+ try {
340
+ return (await lookup(hostname, {
341
+ all: true,
342
+ verbatim: true
343
+ })).map((answer) => answer.address);
344
+ } catch {
345
+ return [];
346
+ }
347
+ }
348
+ /**
349
+ * Whether a host is stated as an IP address rather than as a name.
350
+ *
351
+ * @param hostname - a URL hostname, bracketed or not.
352
+ * @returns true when the host is an IPv4 or IPv6 literal.
353
+ */
354
+ function isIpLiteral(hostname) {
355
+ return isIP(unbracket(hostname)) !== 0;
356
+ }
357
+ /**
358
+ * Whether an address is one only a proxy's fake-ip resolver hands out. The IANA
359
+ * benchmarking range carries no real host, so an answer inside it is this
360
+ * machine's proxy claiming the name, not a private destination.
361
+ *
362
+ * @param address - a textual IPv4 (or IPv4-mapped IPv6) address.
363
+ * @returns true when the address is inside the proxy fake-ip range.
364
+ */
365
+ function isProxyFakeIpAddress(address) {
366
+ const value = ipv4Value(unbracket(address).replace(/^::ffff:/i, ""));
367
+ return value !== void 0 && inRange(value, FAKE_IP_RANGE);
368
+ }
369
+ /**
370
+ * Whether an address is globally reachable unicast. IPv4-mapped IPv6 is
371
+ * classified by the IPv4 address it embeds; every other IPv6 address is public
372
+ * only inside `2000::/3`, which excludes loopback, unique-local, link-local,
373
+ * multicast, and the transition and translation prefixes whose real destination
374
+ * cannot be seen from the address alone.
375
+ *
376
+ * @param address - a textual IPv4 or IPv6 address, bracketed or not.
377
+ * @returns true only for a public unicast destination.
378
+ */
379
+ function isPublicIpAddress(address) {
380
+ const unbracketed = unbracket(address);
381
+ const family = isIP(unbracketed);
382
+ if (family === 4) return isPublicIpv4(unbracketed);
383
+ if (family !== 6) return false;
384
+ const mapped = unbracketed.replace(/^::ffff:/i, "");
385
+ if (isIP(mapped) === 4) return isPublicIpv4(mapped);
386
+ return isPublicIpv6(unbracketed);
387
+ }
388
+ /**
389
+ * Whether a hostname is an IP literal no request may target.
390
+ *
391
+ * @param hostname - a URL hostname, bracketed or not.
392
+ * @returns true when the host is a literal address that is not public.
393
+ */
394
+ function isNonPublicIpLiteral(hostname) {
395
+ const unbracketed = unbracket(hostname);
396
+ return isIP(unbracketed) !== 0 && !isPublicIpAddress(unbracketed);
397
+ }
398
+ /**
399
+ * Refuse a fetch destination the local network policy exists to keep out of
400
+ * reach, before any request is made.
401
+ *
402
+ * @param hostname - the URL hostname, bracketed or not.
403
+ * @param addresses - every address the local resolver returned for the host.
404
+ * @throws WebError `WEB_BLOCKED_URL` for a non-public literal, or for a name
405
+ * this machine resolves into private space that is not the proxy's fake-ip.
406
+ */
407
+ function assertPublicFetchTarget(hostname, addresses) {
408
+ if (isNonPublicIpLiteral(hostname)) throw new WebError(`URL hostname "${hostname}" is not a public IP address`, "WEB_BLOCKED_URL");
409
+ for (const address of addresses) {
410
+ if (isPublicIpAddress(address) || isProxyFakeIpAddress(address)) continue;
411
+ throw new WebError(`URL hostname "${hostname}" resolves to a non-public IP address`, "WEB_BLOCKED_URL");
412
+ }
413
+ }
414
+ /** Whether a dotted quad is public, using {@link NON_PUBLIC_IPV4}. */
415
+ function isPublicIpv4(address) {
416
+ const value = ipv4Value(address);
417
+ if (value === void 0) return false;
418
+ return !NON_PUBLIC_IPV4.some((cidr) => inRange(value, cidr));
419
+ }
420
+ /** Whether a 32-bit address sits inside one `a.b.c.d/prefix` range. */
421
+ function inRange(value, cidr) {
422
+ const slash = cidr.indexOf("/");
423
+ const network = ipv4Value(cidr.slice(0, slash));
424
+ const prefix = Number(cidr.slice(slash + 1));
425
+ if (network === void 0 || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) return false;
426
+ const mask = prefix === 0 ? 0 : 4294967295 << 32 - prefix >>> 0;
427
+ return (value & mask) >>> 0 === (network & mask) >>> 0;
428
+ }
429
+ /** Parse a dotted quad into its 32-bit value, or `undefined` when it is not one. */
430
+ function ipv4Value(address) {
431
+ const parts = address.split(".");
432
+ if (parts.length !== 4) return void 0;
433
+ let value = 0;
434
+ for (const part of parts) {
435
+ if (!/^\d{1,3}$/.test(part)) return void 0;
436
+ const octet = Number(part);
437
+ if (octet > 255) return void 0;
438
+ value = (value << 8 | octet) >>> 0;
439
+ }
440
+ return value;
441
+ }
442
+ /** Whether an IPv6 literal is inside `2000::/3` and outside its blocked sub-ranges. */
443
+ function isPublicIpv6(address) {
444
+ const words = ipv6Words(address);
445
+ if (words === void 0) return false;
446
+ const [first = 0, second = 0] = words;
447
+ if ((first >> 8 & 224) !== 32) return false;
448
+ if (first === 8193) {
449
+ if (second === 3512) return false;
450
+ if (second === 0) return false;
451
+ if (second === 2) return false;
452
+ if ((second & 65520) === 16) return false;
453
+ if ((second & 65520) === 32) return false;
454
+ }
455
+ if (first === 8194) return false;
456
+ if (first === 16383 && second <= 4095) return false;
457
+ return true;
458
+ }
459
+ /** Parse an IPv6 literal into its eight 16-bit words, or `undefined` when it is not one. */
460
+ function ipv6Words(address) {
461
+ const zone = address.indexOf("%");
462
+ const halves = (zone === -1 ? address : address.slice(0, zone)).split("::");
463
+ if (halves.length > 2) return void 0;
464
+ const head = parseIpv6Half(halves[0] ?? "");
465
+ if (head === void 0) return void 0;
466
+ if (halves.length === 1) return head.length === 8 ? head : void 0;
467
+ const tail = parseIpv6Half(halves[1] ?? "");
468
+ if (tail === void 0) return void 0;
469
+ const gap = 8 - head.length - tail.length;
470
+ if (gap < 1) return void 0;
471
+ return [
472
+ ...head,
473
+ ...new Array(gap).fill(0),
474
+ ...tail
475
+ ];
476
+ }
477
+ /** Parse one '::'-free half of an IPv6 literal, expanding a trailing dotted quad. */
478
+ function parseIpv6Half(segment) {
479
+ if (segment === "") return [];
480
+ const words = [];
481
+ for (const piece of segment.split(":")) {
482
+ if (piece.includes(".")) {
483
+ const value = ipv4Value(piece);
484
+ if (value === void 0) return void 0;
485
+ words.push(value >>> 16, value & 65535);
486
+ continue;
487
+ }
488
+ if (!/^[0-9a-fA-F]{1,4}$/.test(piece)) return void 0;
489
+ words.push(parseInt(piece, 16));
490
+ }
491
+ return words;
492
+ }
493
+ /** WHATWG URL keeps brackets around an IPv6 hostname; IP parsers do not. */
494
+ function unbracket(hostname) {
495
+ return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
496
+ }
497
+ //#endregion
277
498
  //#region src/host/codex-fetch.ts
278
499
  const DEFAULT_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
279
500
  const DEFAULT_MAX_BODY_CHARS = 1e5;
@@ -281,6 +502,7 @@ function createCodexFetchProvider(options = {}) {
281
502
  const fetchFn = options.fetchFn ?? fetch;
282
503
  const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
283
504
  const maxBodyChars = options.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;
505
+ const resolveHostAddresses = options.resolveHostAddresses ?? lookupHostAddresses;
284
506
  return {
285
507
  id: CODEX_FETCH_PROVIDER_ID,
286
508
  available: () => true,
@@ -293,6 +515,7 @@ function createCodexFetchProvider(options = {}) {
293
515
  throw new WebError(`invalid URL: ${targetUrl}`, "WEB_INVALID_URL", { cause: error });
294
516
  }
295
517
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new WebError(`unsupported URL scheme "${parsedUrl.protocol}" (only http and https are allowed)`, "WEB_INVALID_URL");
518
+ assertPublicFetchTarget(parsedUrl.hostname, isIpLiteral(parsedUrl.hostname) ? [] : await resolveHostAddresses(parsedUrl.hostname));
296
519
  let response;
297
520
  try {
298
521
  response = await fetchFn(targetUrl, {
@@ -1291,6 +1514,8 @@ var ProxyManager = class {
1291
1514
  logger;
1292
1515
  cachedSystemProxy = null;
1293
1516
  lastSystemProxyCheck = 0;
1517
+ detected = false;
1518
+ proxyListeners = /* @__PURE__ */ new Set();
1294
1519
  agents = /* @__PURE__ */ new Map();
1295
1520
  constructor(options) {
1296
1521
  this.getPreferences = options.getPreferences;
@@ -1301,15 +1526,37 @@ var ProxyManager = class {
1301
1526
  getSystemProxy(force = false) {
1302
1527
  const now = Date.now();
1303
1528
  if (!force && now - this.lastSystemProxyCheck < SYSTEM_PROXY_CACHE_TTL_MS) return this.cachedSystemProxy;
1529
+ const previous = this.cachedSystemProxy;
1530
+ const hadDetected = this.detected;
1304
1531
  this.lastSystemProxyCheck = now;
1532
+ this.detected = true;
1305
1533
  try {
1306
1534
  this.cachedSystemProxy = this.systemProxyDetector();
1307
1535
  } catch (error) {
1308
1536
  this.cachedSystemProxy = null;
1309
1537
  this.logger?.warn?.(`[dsh-chatgpt-subscription] Failed to detect system proxy: ${error instanceof Error ? error.message : String(error)}`);
1310
1538
  }
1539
+ if (hadDetected && previous === null && this.cachedSystemProxy !== null) for (const listener of [...this.proxyListeners]) try {
1540
+ listener(this.cachedSystemProxy);
1541
+ } catch {}
1311
1542
  return this.cachedSystemProxy;
1312
1543
  }
1544
+ /**
1545
+ * Observe the system proxy becoming known.
1546
+ *
1547
+ * A proxy that appears after startup — or a first detection that failed — otherwise leaves every
1548
+ * consumer on the decision it made at load, because `null` reads the same for "no proxy" and for
1549
+ * "detection failed".
1550
+ *
1551
+ * @param listener - called with the detected proxy URL; a throw from it is ignored.
1552
+ * @returns the disposer that stops observing.
1553
+ */
1554
+ onSystemProxyDetected(listener) {
1555
+ this.proxyListeners.add(listener);
1556
+ return () => {
1557
+ this.proxyListeners.delete(listener);
1558
+ };
1559
+ }
1313
1560
  resolveActiveProxyUrl() {
1314
1561
  const prefs = this.getPreferences();
1315
1562
  const mode = prefs.proxyMode ?? "auto";
@@ -1341,6 +1588,7 @@ var ProxyManager = class {
1341
1588
  };
1342
1589
  }
1343
1590
  dispose() {
1591
+ this.proxyListeners.clear();
1344
1592
  for (const agent of this.agents.values()) agent.destroy().catch(() => void 0);
1345
1593
  this.agents.clear();
1346
1594
  }
@@ -3538,7 +3786,7 @@ var SearchProviderSwitcher = class {
3538
3786
  constructor(loader) {
3539
3787
  this.loader = loader;
3540
3788
  }
3541
- async select(preference) {
3789
+ async select(preference, options = {}) {
3542
3790
  const entry = this.findWebEntry();
3543
3791
  if (entry === null) return;
3544
3792
  const config = currentConfig(entry);
@@ -3547,9 +3795,9 @@ var SearchProviderSwitcher = class {
3547
3795
  this.originalFetchProvider = typeof config.fetchProvider === "string" && config.fetchProvider !== "codex-subscription" ? config.fetchProvider : void 0;
3548
3796
  this.initialized = true;
3549
3797
  }
3550
- const selected = preference === "codex" ? CODEX_SEARCH_PROVIDER_ID : void 0;
3551
- const nextSearch = selected ?? this.originalSearchProvider;
3552
- const nextFetch = selected ? CODEX_FETCH_PROVIDER_ID : this.originalFetchProvider;
3798
+ const codexSelected = preference === SEARCH_PROVIDER_CODEX;
3799
+ const nextSearch = codexSelected ? CODEX_SEARCH_PROVIDER_ID : this.originalSearchProvider;
3800
+ const nextFetch = codexSelected || options.pluginFetch === true ? CODEX_FETCH_PROVIDER_ID : this.originalFetchProvider;
3553
3801
  if (config.searchProvider === nextSearch && config.fetchProvider === nextFetch) return;
3554
3802
  const nextConfig = { ...config };
3555
3803
  if (nextSearch === void 0) delete nextConfig.searchProvider;
@@ -5958,9 +6206,10 @@ function apply(ctx, pluginConfig = {}) {
5958
6206
  reasoningSummary: () => preferences.status().reasoningSummary
5959
6207
  }), preferences);
5960
6208
  const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
5961
- const applySearchPreference = (searchProvider = preferences.status().searchProvider) => {
5962
- searchSwitcher.select(searchProvider).catch((error) => {
5963
- ctx.logger.warn(`[dsh-chatgpt-subscription] Search provider preference could not be applied: ${error instanceof Error ? error.message : String(error)}`);
6209
+ const applyWebProviders = (current = preferences.status()) => {
6210
+ const pluginFetch = proxyManager.resolveActiveProxyUrl() !== null;
6211
+ searchSwitcher.select(current.searchProvider, { pluginFetch }).catch((error) => {
6212
+ ctx.logger.warn(`[dsh-chatgpt-subscription] Web provider selection could not be applied: ${error instanceof Error ? error.message : String(error)}`);
5964
6213
  });
5965
6214
  };
5966
6215
  const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager);
@@ -5969,10 +6218,14 @@ function apply(ctx, pluginConfig = {}) {
5969
6218
  ctx.inject(["web"], (ctx) => {
5970
6219
  ctx.web.registerSearchProvider(createCodexSearchProvider(oauth, { fetchFn: proxyFetch }));
5971
6220
  ctx.web.registerFetchProvider(createCodexFetchProvider({ fetchFn: proxyFetch }));
5972
- applySearchPreference();
6221
+ applyWebProviders();
6222
+ });
6223
+ const disposePreferenceWatch = preferences.watch((next) => applyWebProviders(next));
6224
+ const disposeProxyWatch = proxyManager.onSystemProxyDetected(() => {
6225
+ applyWebProviders();
5973
6226
  });
5974
- const disposePreferenceWatch = preferences.watch((next) => applySearchPreference(next.searchProvider));
5975
6227
  return () => {
6228
+ disposeProxyWatch();
5976
6229
  disposePreferenceWatch();
5977
6230
  disposeImageTool();
5978
6231
  disposeAdapter();
@@ -1,9 +1,16 @@
1
1
  import { type WebFetchProvider } from '@deepseek-ai/dsh-web';
2
2
  type FetchLike = typeof fetch;
3
+ /** Resolves a hostname to every address this machine's resolver returns. */
4
+ export type HostAddressResolver = (hostname: string) => Promise<readonly string[]>;
3
5
  export interface CodexFetchProviderOptions {
4
6
  fetchFn?: FetchLike;
5
7
  maxResponseBytes?: number;
6
8
  maxBodyChars?: number;
9
+ /**
10
+ * Address lookup behind the destination policy. Overridden only by focused
11
+ * tests; the policy itself is what every request goes through.
12
+ */
13
+ resolveHostAddresses?: HostAddressResolver;
7
14
  }
8
15
  export declare function createCodexFetchProvider(options?: CodexFetchProviderOptions): WebFetchProvider;
9
16
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"codex-fetch.d.ts","sourceRoot":"","sources":["../../../src/host/codex-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,gBAAgB,EAA6C,MAAM,sBAAsB,CAAA;AAGjH,KAAK,SAAS,GAAG,OAAO,KAAK,CAAA;AAE7B,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAKD,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAA8B,GACtC,gBAAgB,CAqFlB"}
1
+ {"version":3,"file":"codex-fetch.d.ts","sourceRoot":"","sources":["../../../src/host/codex-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,gBAAgB,EAA6C,MAAM,sBAAsB,CAAA;AAIjH,KAAK,SAAS,GAAG,OAAO,KAAK,CAAA;AAE7B,4EAA4E;AAC5E,MAAM,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAA;AAElF,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,mBAAmB,CAAA;CAC3C;AAKD,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,yBAA8B,GACtC,gBAAgB,CAgGlB"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Destination policy for this plugin's fetch provider.
3
+ *
4
+ * DSH's built-in provider resolves every destination and validates and pins the
5
+ * address before it connects. That cannot work on a machine whose proxy answers
6
+ * DNS with its own fake-ip range (`198.18.0.0/15` for Clash/Mihomo, the usual
7
+ * companion of a system proxy): the hostname is ordinary, the answer is reserved
8
+ * space, and every request is refused before the proxy that could have resolved
9
+ * it is ever consulted. This provider exists for that machine, so it cannot
10
+ * validate names the way the built-in one does.
11
+ *
12
+ * It keeps the half of the policy that needs no resolution — an address the URL
13
+ * states outright is refused unless it is globally reachable unicast, because a
14
+ * proxy on this machine must not become a path into loopback or a LAN — and it
15
+ * also refuses a name this machine resolves into private space. Only the proxy's
16
+ * own fake-ip answers are accepted, since that is what the proxy's name
17
+ * resolution looks like from here; the URL hostname still reaches the proxy
18
+ * intact, so the proxy decides where the name really goes.
19
+ *
20
+ * @module dsh-chatgpt-subscription/fetch-address-policy
21
+ */
22
+ /**
23
+ * Resolve one hostname to every address the local resolver returns, or to no
24
+ * addresses when it cannot.
25
+ *
26
+ * A failure is not evidence of a local destination: the configured proxy
27
+ * resolves the origin itself, so a name this machine cannot resolve must not
28
+ * fail a request the proxy could have served.
29
+ *
30
+ * @param hostname - the URL hostname, without brackets.
31
+ * @returns the resolved addresses, or an empty list when resolution failed.
32
+ */
33
+ export declare function lookupHostAddresses(hostname: string): Promise<readonly string[]>;
34
+ /**
35
+ * Whether a host is stated as an IP address rather than as a name.
36
+ *
37
+ * @param hostname - a URL hostname, bracketed or not.
38
+ * @returns true when the host is an IPv4 or IPv6 literal.
39
+ */
40
+ export declare function isIpLiteral(hostname: string): boolean;
41
+ /**
42
+ * Whether an address is one only a proxy's fake-ip resolver hands out. The IANA
43
+ * benchmarking range carries no real host, so an answer inside it is this
44
+ * machine's proxy claiming the name, not a private destination.
45
+ *
46
+ * @param address - a textual IPv4 (or IPv4-mapped IPv6) address.
47
+ * @returns true when the address is inside the proxy fake-ip range.
48
+ */
49
+ export declare function isProxyFakeIpAddress(address: string): boolean;
50
+ /**
51
+ * Whether an address is globally reachable unicast. IPv4-mapped IPv6 is
52
+ * classified by the IPv4 address it embeds; every other IPv6 address is public
53
+ * only inside `2000::/3`, which excludes loopback, unique-local, link-local,
54
+ * multicast, and the transition and translation prefixes whose real destination
55
+ * cannot be seen from the address alone.
56
+ *
57
+ * @param address - a textual IPv4 or IPv6 address, bracketed or not.
58
+ * @returns true only for a public unicast destination.
59
+ */
60
+ export declare function isPublicIpAddress(address: string): boolean;
61
+ /**
62
+ * Whether a hostname is an IP literal no request may target.
63
+ *
64
+ * @param hostname - a URL hostname, bracketed or not.
65
+ * @returns true when the host is a literal address that is not public.
66
+ */
67
+ export declare function isNonPublicIpLiteral(hostname: string): boolean;
68
+ /**
69
+ * Refuse a fetch destination the local network policy exists to keep out of
70
+ * reach, before any request is made.
71
+ *
72
+ * @param hostname - the URL hostname, bracketed or not.
73
+ * @param addresses - every address the local resolver returned for the host.
74
+ * @throws WebError `WEB_BLOCKED_URL` for a non-public literal, or for a name
75
+ * this machine resolves into private space that is not the proxy's fake-ip.
76
+ */
77
+ export declare function assertPublicFetchTarget(hostname: string, addresses: readonly string[]): void;
78
+ //# sourceMappingURL=fetch-address-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-address-policy.d.ts","sourceRoot":"","sources":["../../../src/host/fetch-address-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAkCH;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAOtF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ1D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAQ5F"}
@@ -13,6 +13,8 @@ export interface ParseEnvProxyOptions {
13
13
  }
14
14
  export declare function parseEnvProxy(env?: Record<string, string | undefined>, options?: ParseEnvProxyOptions): string | null;
15
15
  export declare function detectSystemProxy(platform?: NodeJS.Platform, env?: Record<string, string | undefined>): string | null;
16
+ /** Called with the proxy URL the first time detection reports one after a `null`. */
17
+ export type SystemProxyListener = (proxyUrl: string) => void;
16
18
  export interface ProxyFetchOptions {
17
19
  getPreferences: () => Pick<SubscriptionPreferencesDto, 'proxyMode' | 'customProxyUrl'>;
18
20
  baseFetch?: FetchLike;
@@ -26,9 +28,22 @@ export declare class ProxyManager {
26
28
  private readonly logger?;
27
29
  private cachedSystemProxy;
28
30
  private lastSystemProxyCheck;
31
+ private detected;
32
+ private readonly proxyListeners;
29
33
  private readonly agents;
30
34
  constructor(options: ProxyFetchOptions);
31
35
  getSystemProxy(force?: boolean): string | null;
36
+ /**
37
+ * Observe the system proxy becoming known.
38
+ *
39
+ * A proxy that appears after startup — or a first detection that failed — otherwise leaves every
40
+ * consumer on the decision it made at load, because `null` reads the same for "no proxy" and for
41
+ * "detection failed".
42
+ *
43
+ * @param listener - called with the detected proxy URL; a throw from it is ignored.
44
+ * @returns the disposer that stops observing.
45
+ */
46
+ onSystemProxyDetected(listener: SystemProxyListener): () => void;
32
47
  resolveActiveProxyUrl(): string | null;
33
48
  private getOrCreateAgent;
34
49
  createFetch(): FetchLike;
@@ -1 +1 @@
1
- {"version":3,"file":"proxy-manager.d.ts","sourceRoot":"","sources":["../../../src/host/proxy-manager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAa,0BAA0B,EAAE,MAAM,wBAAwB,CAAA;AAEnF,MAAM,MAAM,SAAS,GAAG,OAAO,KAAK,CAAA;AAIpC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqCvE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBnE;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,wBAAgB,aAAa,CAC3B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,EACrD,OAAO,GAAE,oBAAyB,GACjC,MAAM,GAAG,IAAI,CA2Bf;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,GAAG,IAAI,CAuBpJ;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,IAAI,CAAC,0BAA0B,EAAE,WAAW,GAAG,gBAAgB,CAAC,CAAA;IACtF,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IACzC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CAClD;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwE;IACvG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA0C;IAElE,OAAO,CAAC,iBAAiB,CAAsB;IAC/C,OAAO,CAAC,oBAAoB,CAAI;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;gBAE3C,OAAO,EAAE,iBAAiB;IAOtC,cAAc,CAAC,KAAK,UAAQ,GAAG,MAAM,GAAG,IAAI;IAe5C,qBAAqB,IAAI,MAAM,GAAG,IAAI;IAgBtC,OAAO,CAAC,gBAAgB;IASxB,WAAW,IAAI,SAAS;IAsBxB,OAAO,IAAI,IAAI;CAMhB"}
1
+ {"version":3,"file":"proxy-manager.d.ts","sourceRoot":"","sources":["../../../src/host/proxy-manager.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAa,0BAA0B,EAAE,MAAM,wBAAwB,CAAA;AAEnF,MAAM,MAAM,SAAS,GAAG,OAAO,KAAK,CAAA;AAIpC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAqCvE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBnE;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,wBAAgB,aAAa,CAC3B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,EACrD,OAAO,GAAE,oBAAyB,GACjC,MAAM,GAAG,IAAI,CA2Bf;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAAE,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,GAAG,IAAI,CAuBpJ;AAED,qFAAqF;AACrF,MAAM,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;AAE5D,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,IAAI,CAAC,0BAA0B,EAAE,WAAW,GAAG,gBAAgB,CAAC,CAAA;IACtF,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IACzC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAA;CAClD;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwE;IACvG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAA0C;IAElE,OAAO,CAAC,iBAAiB,CAAsB;IAC/C,OAAO,CAAC,oBAAoB,CAAI;IAChC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;gBAE3C,OAAO,EAAE,iBAAiB;IAOtC,cAAc,CAAC,KAAK,UAAQ,GAAG,MAAM,GAAG,IAAI;IA8B5C;;;;;;;;;OASG;IACH,qBAAqB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,IAAI;IAKhE,qBAAqB,IAAI,MAAM,GAAG,IAAI;IAgBtC,OAAO,CAAC,gBAAgB;IASxB,WAAW,IAAI,SAAS;IAsBxB,OAAO,IAAI,IAAI;CAOhB"}
@@ -3,13 +3,27 @@ import type { SearchProviderPreference } from '../shared/contracts.ts';
3
3
  interface LoaderLike {
4
4
  entries(): Iterable<Entry>;
5
5
  }
6
+ /** What one selection asks for on top of the settings preference. */
7
+ export interface WebProviderSelectionOptions {
8
+ /**
9
+ * Whether this plugin's fetch provider must serve the `web_fetch` tool.
10
+ *
11
+ * DSH's built-in provider resolves and pins every destination before it
12
+ * connects and routes through a proxy only when the process environment names
13
+ * one, so an OS-level proxy plus the fake-ip DNS that usually comes with it
14
+ * fails every fetch with `WEB_BLOCKED_URL`. Selecting this plugin's provider
15
+ * hands the origin's resolution to the configured proxy — the same
16
+ * proxied-hop semantics DSH applies to a URL it routes through a proxy.
17
+ */
18
+ readonly pluginFetch?: boolean;
19
+ }
6
20
  export declare class SearchProviderSwitcher {
7
21
  private readonly loader;
8
22
  private originalSearchProvider;
9
23
  private originalFetchProvider;
10
24
  private initialized;
11
25
  constructor(loader: LoaderLike);
12
- select(preference: SearchProviderPreference): Promise<void>;
26
+ select(preference: SearchProviderPreference, options?: WebProviderSelectionOptions): Promise<void>;
13
27
  private findWebEntry;
14
28
  }
15
29
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"search-provider-switcher.d.ts","sourceRoot":"","sources":["../../../src/host/search-provider-switcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AAG9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAEtE,UAAU,UAAU;IAClB,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;CAC3B;AAED,qBAAa,sBAAsB;IAKrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,sBAAsB,CAAoB;IAClD,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,WAAW,CAAQ;gBAEE,MAAM,EAAE,UAAU;IAEzC,MAAM,CAAC,UAAU,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCjE,OAAO,CAAC,YAAY;CAOrB"}
1
+ {"version":3,"file":"search-provider-switcher.d.ts","sourceRoot":"","sources":["../../../src/host/search-provider-switcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mCAAmC,CAAA;AAG9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAEtE,UAAU,UAAU;IAClB,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAA;CAC3B;AAED,qEAAqE;AACrE,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;;;OASG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,qBAAa,sBAAsB;IAKrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,sBAAsB,CAAoB;IAClD,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,WAAW,CAAQ;gBAEE,MAAM,EAAE,UAAU;IAEzC,MAAM,CAAC,UAAU,EAAE,wBAAwB,EAAE,OAAO,GAAE,2BAAgC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkC5G,OAAO,CAAC,YAAY;CAOrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAqCxC,yDAAyD;AACzD,MAAM,WAAW,MAAM;IACrB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAA;CAC9C;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAI3B,CAAA;AAEF,eAAO,MAAM,MAAM,UAAqE,CAAA;AAExF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,GAAE,MAAW,GAAG,IAAI,CA0FnE;AAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACzE,OAAO,EACL,kCAAkC,EAClC,qBAAqB,EACrB,mBAAmB,EACnB,2BAA2B,EAC3B,sBAAsB,EACtB,iCAAiC,EACjC,4BAA4B,EAC5B,kBAAkB,EAClB,cAAc,EACd,gCAAgC,EAChC,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAClG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAsCxC,yDAAyD;AACzD,MAAM,WAAW,MAAM;IACrB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAA;CAC9C;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAI3B,CAAA;AAEF,eAAO,MAAM,MAAM,UAAqE,CAAA;AAExF,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,GAAE,MAAW,GAAG,IAAI,CA8GnE;AAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACzE,OAAO,EACL,kCAAkC,EAClC,qBAAqB,EACrB,mBAAmB,EACnB,2BAA2B,EAC3B,sBAAsB,EACtB,iCAAiC,EACjC,4BAA4B,EAC5B,kBAAkB,EAClB,cAAc,EACd,gCAAgC,EAChC,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAA;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAClF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACtF,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAA;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAA;AAClE,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,cAAc,EACd,iBAAiB,GAClB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAClG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eddyskywalker/dsh-chatgpt-subscription",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "DSH provider plugin for Codex access through a ChatGPT subscription.",
5
5
  "author": {
6
6
  "name": "eddyskywalker",