dsh-model-proxy-switch 0.0.0-stage → 1.0.0

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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -2
  3. package/client.js +473 -0
  4. package/cordis.patch.yml +7 -0
  5. package/icon.svg +13 -0
  6. package/index.js +341 -0
  7. package/locale/en.json +25 -0
  8. package/locale/zh.json +25 -0
  9. package/package.json +55 -4
  10. package/vendor/http-proxy.js +542 -0
  11. package/vendor/node_modules/undici/LICENSE +21 -0
  12. package/vendor/node_modules/undici/README.md +741 -0
  13. package/vendor/node_modules/undici/docs/docs/api/Agent.md +84 -0
  14. package/vendor/node_modules/undici/docs/docs/api/BalancedPool.md +99 -0
  15. package/vendor/node_modules/undici/docs/docs/api/CacheStorage.md +30 -0
  16. package/vendor/node_modules/undici/docs/docs/api/CacheStore.md +164 -0
  17. package/vendor/node_modules/undici/docs/docs/api/Client.md +290 -0
  18. package/vendor/node_modules/undici/docs/docs/api/ClientStats.md +27 -0
  19. package/vendor/node_modules/undici/docs/docs/api/Connector.md +115 -0
  20. package/vendor/node_modules/undici/docs/docs/api/ContentType.md +57 -0
  21. package/vendor/node_modules/undici/docs/docs/api/Cookies.md +128 -0
  22. package/vendor/node_modules/undici/docs/docs/api/Debug.md +62 -0
  23. package/vendor/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +317 -0
  24. package/vendor/node_modules/undici/docs/docs/api/Dispatcher.md +1393 -0
  25. package/vendor/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +159 -0
  26. package/vendor/node_modules/undici/docs/docs/api/Errors.md +49 -0
  27. package/vendor/node_modules/undici/docs/docs/api/EventSource.md +45 -0
  28. package/vendor/node_modules/undici/docs/docs/api/Fetch.md +60 -0
  29. package/vendor/node_modules/undici/docs/docs/api/GlobalInstallation.md +139 -0
  30. package/vendor/node_modules/undici/docs/docs/api/H2CClient.md +263 -0
  31. package/vendor/node_modules/undici/docs/docs/api/MockAgent.md +603 -0
  32. package/vendor/node_modules/undici/docs/docs/api/MockCallHistory.md +197 -0
  33. package/vendor/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +43 -0
  34. package/vendor/node_modules/undici/docs/docs/api/MockClient.md +81 -0
  35. package/vendor/node_modules/undici/docs/docs/api/MockErrors.md +12 -0
  36. package/vendor/node_modules/undici/docs/docs/api/MockPool.md +555 -0
  37. package/vendor/node_modules/undici/docs/docs/api/Pool.md +84 -0
  38. package/vendor/node_modules/undici/docs/docs/api/PoolStats.md +35 -0
  39. package/vendor/node_modules/undici/docs/docs/api/ProxyAgent.md +229 -0
  40. package/vendor/node_modules/undici/docs/docs/api/RedirectHandler.md +93 -0
  41. package/vendor/node_modules/undici/docs/docs/api/RetryAgent.md +50 -0
  42. package/vendor/node_modules/undici/docs/docs/api/RetryHandler.md +118 -0
  43. package/vendor/node_modules/undici/docs/docs/api/RoundRobinPool.md +145 -0
  44. package/vendor/node_modules/undici/docs/docs/api/SnapshotAgent.md +616 -0
  45. package/vendor/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +275 -0
  46. package/vendor/node_modules/undici/docs/docs/api/Util.md +25 -0
  47. package/vendor/node_modules/undici/docs/docs/api/WebSocket.md +141 -0
  48. package/vendor/node_modules/undici/docs/docs/api/api-lifecycle.md +91 -0
  49. package/vendor/node_modules/undici/docs/docs/best-practices/client-certificate.md +64 -0
  50. package/vendor/node_modules/undici/docs/docs/best-practices/crawling.md +58 -0
  51. package/vendor/node_modules/undici/docs/docs/best-practices/mocking-request.md +190 -0
  52. package/vendor/node_modules/undici/docs/docs/best-practices/proxy.md +127 -0
  53. package/vendor/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +224 -0
  54. package/vendor/node_modules/undici/docs/docs/best-practices/writing-tests.md +63 -0
  55. package/vendor/node_modules/undici/index-fetch.js +65 -0
  56. package/vendor/node_modules/undici/index.d.ts +3 -0
  57. package/vendor/node_modules/undici/index.js +234 -0
  58. package/vendor/node_modules/undici/lib/api/abort-signal.js +59 -0
  59. package/vendor/node_modules/undici/lib/api/api-connect.js +110 -0
  60. package/vendor/node_modules/undici/lib/api/api-pipeline.js +252 -0
  61. package/vendor/node_modules/undici/lib/api/api-request.js +214 -0
  62. package/vendor/node_modules/undici/lib/api/api-stream.js +209 -0
  63. package/vendor/node_modules/undici/lib/api/api-upgrade.js +111 -0
  64. package/vendor/node_modules/undici/lib/api/index.js +7 -0
  65. package/vendor/node_modules/undici/lib/api/readable.js +580 -0
  66. package/vendor/node_modules/undici/lib/cache/memory-cache-store.js +279 -0
  67. package/vendor/node_modules/undici/lib/cache/sqlite-cache-store.js +467 -0
  68. package/vendor/node_modules/undici/lib/core/connect.js +159 -0
  69. package/vendor/node_modules/undici/lib/core/constants.js +143 -0
  70. package/vendor/node_modules/undici/lib/core/diagnostics.js +227 -0
  71. package/vendor/node_modules/undici/lib/core/errors.js +477 -0
  72. package/vendor/node_modules/undici/lib/core/request.js +485 -0
  73. package/vendor/node_modules/undici/lib/core/socks5-client.js +422 -0
  74. package/vendor/node_modules/undici/lib/core/socks5-utils.js +212 -0
  75. package/vendor/node_modules/undici/lib/core/symbols.js +76 -0
  76. package/vendor/node_modules/undici/lib/core/tree.js +160 -0
  77. package/vendor/node_modules/undici/lib/core/util.js +992 -0
  78. package/vendor/node_modules/undici/lib/dispatcher/agent.js +162 -0
  79. package/vendor/node_modules/undici/lib/dispatcher/balanced-pool.js +221 -0
  80. package/vendor/node_modules/undici/lib/dispatcher/client-h1.js +1744 -0
  81. package/vendor/node_modules/undici/lib/dispatcher/client-h2.js +1056 -0
  82. package/vendor/node_modules/undici/lib/dispatcher/client.js +669 -0
  83. package/vendor/node_modules/undici/lib/dispatcher/dispatcher-base.js +196 -0
  84. package/vendor/node_modules/undici/lib/dispatcher/dispatcher.js +48 -0
  85. package/vendor/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +146 -0
  86. package/vendor/node_modules/undici/lib/dispatcher/fixed-queue.js +135 -0
  87. package/vendor/node_modules/undici/lib/dispatcher/h2c-client.js +51 -0
  88. package/vendor/node_modules/undici/lib/dispatcher/pool-base.js +214 -0
  89. package/vendor/node_modules/undici/lib/dispatcher/pool.js +118 -0
  90. package/vendor/node_modules/undici/lib/dispatcher/proxy-agent.js +319 -0
  91. package/vendor/node_modules/undici/lib/dispatcher/retry-agent.js +35 -0
  92. package/vendor/node_modules/undici/lib/dispatcher/round-robin-pool.js +137 -0
  93. package/vendor/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +260 -0
  94. package/vendor/node_modules/undici/lib/encoding/index.js +33 -0
  95. package/vendor/node_modules/undici/lib/global.js +59 -0
  96. package/vendor/node_modules/undici/lib/handler/cache-handler.js +824 -0
  97. package/vendor/node_modules/undici/lib/handler/cache-revalidation-handler.js +124 -0
  98. package/vendor/node_modules/undici/lib/handler/decorator-handler.js +67 -0
  99. package/vendor/node_modules/undici/lib/handler/deduplication-handler.js +504 -0
  100. package/vendor/node_modules/undici/lib/handler/redirect-handler.js +245 -0
  101. package/vendor/node_modules/undici/lib/handler/retry-handler.js +504 -0
  102. package/vendor/node_modules/undici/lib/handler/unwrap-handler.js +106 -0
  103. package/vendor/node_modules/undici/lib/handler/wrap-handler.js +105 -0
  104. package/vendor/node_modules/undici/lib/interceptor/cache.js +621 -0
  105. package/vendor/node_modules/undici/lib/interceptor/decompress.js +561 -0
  106. package/vendor/node_modules/undici/lib/interceptor/deduplicate.js +117 -0
  107. package/vendor/node_modules/undici/lib/interceptor/dns.js +571 -0
  108. package/vendor/node_modules/undici/lib/interceptor/dump.js +99 -0
  109. package/vendor/node_modules/undici/lib/interceptor/redirect.js +21 -0
  110. package/vendor/node_modules/undici/lib/interceptor/response-error.js +95 -0
  111. package/vendor/node_modules/undici/lib/interceptor/retry.js +19 -0
  112. package/vendor/node_modules/undici/lib/llhttp/.gitkeep +0 -0
  113. package/vendor/node_modules/undici/lib/llhttp/constants.d.ts +195 -0
  114. package/vendor/node_modules/undici/lib/llhttp/constants.js +531 -0
  115. package/vendor/node_modules/undici/lib/llhttp/llhttp-wasm.js +15 -0
  116. package/vendor/node_modules/undici/lib/llhttp/llhttp_simd-wasm.js +15 -0
  117. package/vendor/node_modules/undici/lib/llhttp/utils.d.ts +2 -0
  118. package/vendor/node_modules/undici/lib/llhttp/utils.js +12 -0
  119. package/vendor/node_modules/undici/lib/mock/mock-agent.js +232 -0
  120. package/vendor/node_modules/undici/lib/mock/mock-call-history.js +248 -0
  121. package/vendor/node_modules/undici/lib/mock/mock-client.js +68 -0
  122. package/vendor/node_modules/undici/lib/mock/mock-errors.js +29 -0
  123. package/vendor/node_modules/undici/lib/mock/mock-interceptor.js +209 -0
  124. package/vendor/node_modules/undici/lib/mock/mock-pool.js +68 -0
  125. package/vendor/node_modules/undici/lib/mock/mock-symbols.js +32 -0
  126. package/vendor/node_modules/undici/lib/mock/mock-utils.js +486 -0
  127. package/vendor/node_modules/undici/lib/mock/pending-interceptors-formatter.js +43 -0
  128. package/vendor/node_modules/undici/lib/mock/snapshot-agent.js +353 -0
  129. package/vendor/node_modules/undici/lib/mock/snapshot-recorder.js +588 -0
  130. package/vendor/node_modules/undici/lib/mock/snapshot-utils.js +158 -0
  131. package/vendor/node_modules/undici/lib/util/cache.js +676 -0
  132. package/vendor/node_modules/undici/lib/util/date.js +670 -0
  133. package/vendor/node_modules/undici/lib/util/promise.js +28 -0
  134. package/vendor/node_modules/undici/lib/util/runtime-features.js +124 -0
  135. package/vendor/node_modules/undici/lib/util/stats.js +32 -0
  136. package/vendor/node_modules/undici/lib/util/timers.js +425 -0
  137. package/vendor/node_modules/undici/lib/web/cache/cache.js +864 -0
  138. package/vendor/node_modules/undici/lib/web/cache/cachestorage.js +152 -0
  139. package/vendor/node_modules/undici/lib/web/cache/util.js +45 -0
  140. package/vendor/node_modules/undici/lib/web/cookies/constants.js +12 -0
  141. package/vendor/node_modules/undici/lib/web/cookies/index.js +201 -0
  142. package/vendor/node_modules/undici/lib/web/cookies/parse.js +315 -0
  143. package/vendor/node_modules/undici/lib/web/cookies/util.js +371 -0
  144. package/vendor/node_modules/undici/lib/web/eventsource/eventsource-stream.js +575 -0
  145. package/vendor/node_modules/undici/lib/web/eventsource/eventsource.js +504 -0
  146. package/vendor/node_modules/undici/lib/web/eventsource/util.js +29 -0
  147. package/vendor/node_modules/undici/lib/web/fetch/LICENSE +21 -0
  148. package/vendor/node_modules/undici/lib/web/fetch/body.js +509 -0
  149. package/vendor/node_modules/undici/lib/web/fetch/constants.js +131 -0
  150. package/vendor/node_modules/undici/lib/web/fetch/data-url.js +596 -0
  151. package/vendor/node_modules/undici/lib/web/fetch/formdata-parser.js +586 -0
  152. package/vendor/node_modules/undici/lib/web/fetch/formdata.js +259 -0
  153. package/vendor/node_modules/undici/lib/web/fetch/global.js +40 -0
  154. package/vendor/node_modules/undici/lib/web/fetch/headers.js +719 -0
  155. package/vendor/node_modules/undici/lib/web/fetch/index.js +2413 -0
  156. package/vendor/node_modules/undici/lib/web/fetch/request.js +1115 -0
  157. package/vendor/node_modules/undici/lib/web/fetch/response.js +641 -0
  158. package/vendor/node_modules/undici/lib/web/fetch/util.js +1522 -0
  159. package/vendor/node_modules/undici/lib/web/infra/index.js +243 -0
  160. package/vendor/node_modules/undici/lib/web/subresource-integrity/Readme.md +9 -0
  161. package/vendor/node_modules/undici/lib/web/subresource-integrity/subresource-integrity.js +307 -0
  162. package/vendor/node_modules/undici/lib/web/webidl/index.js +1006 -0
  163. package/vendor/node_modules/undici/lib/web/websocket/connection.js +339 -0
  164. package/vendor/node_modules/undici/lib/web/websocket/constants.js +126 -0
  165. package/vendor/node_modules/undici/lib/web/websocket/events.js +331 -0
  166. package/vendor/node_modules/undici/lib/web/websocket/frame.js +133 -0
  167. package/vendor/node_modules/undici/lib/web/websocket/permessage-deflate.js +105 -0
  168. package/vendor/node_modules/undici/lib/web/websocket/receiver.js +520 -0
  169. package/vendor/node_modules/undici/lib/web/websocket/sender.js +109 -0
  170. package/vendor/node_modules/undici/lib/web/websocket/stream/websocketerror.js +104 -0
  171. package/vendor/node_modules/undici/lib/web/websocket/stream/websocketstream.js +496 -0
  172. package/vendor/node_modules/undici/lib/web/websocket/util.js +347 -0
  173. package/vendor/node_modules/undici/lib/web/websocket/websocket.js +758 -0
  174. package/vendor/node_modules/undici/package.json +152 -0
  175. package/vendor/node_modules/undici/scripts/strip-comments.js +10 -0
  176. package/vendor/node_modules/undici/types/README.md +6 -0
  177. package/vendor/node_modules/undici/types/agent.d.ts +32 -0
  178. package/vendor/node_modules/undici/types/api.d.ts +43 -0
  179. package/vendor/node_modules/undici/types/balanced-pool.d.ts +30 -0
  180. package/vendor/node_modules/undici/types/cache-interceptor.d.ts +179 -0
  181. package/vendor/node_modules/undici/types/cache.d.ts +36 -0
  182. package/vendor/node_modules/undici/types/client-stats.d.ts +15 -0
  183. package/vendor/node_modules/undici/types/client.d.ts +153 -0
  184. package/vendor/node_modules/undici/types/connector.d.ts +36 -0
  185. package/vendor/node_modules/undici/types/content-type.d.ts +21 -0
  186. package/vendor/node_modules/undici/types/cookies.d.ts +30 -0
  187. package/vendor/node_modules/undici/types/diagnostics-channel.d.ts +74 -0
  188. package/vendor/node_modules/undici/types/dispatcher.d.ts +275 -0
  189. package/vendor/node_modules/undici/types/env-http-proxy-agent.d.ts +22 -0
  190. package/vendor/node_modules/undici/types/errors.d.ts +177 -0
  191. package/vendor/node_modules/undici/types/eventsource.d.ts +66 -0
  192. package/vendor/node_modules/undici/types/fetch.d.ts +231 -0
  193. package/vendor/node_modules/undici/types/formdata.d.ts +114 -0
  194. package/vendor/node_modules/undici/types/global-dispatcher.d.ts +9 -0
  195. package/vendor/node_modules/undici/types/global-origin.d.ts +7 -0
  196. package/vendor/node_modules/undici/types/h2c-client.d.ts +73 -0
  197. package/vendor/node_modules/undici/types/handlers.d.ts +14 -0
  198. package/vendor/node_modules/undici/types/header.d.ts +160 -0
  199. package/vendor/node_modules/undici/types/index.d.ts +91 -0
  200. package/vendor/node_modules/undici/types/interceptors.d.ts +82 -0
  201. package/vendor/node_modules/undici/types/mock-agent.d.ts +68 -0
  202. package/vendor/node_modules/undici/types/mock-call-history.d.ts +111 -0
  203. package/vendor/node_modules/undici/types/mock-client.d.ts +27 -0
  204. package/vendor/node_modules/undici/types/mock-errors.d.ts +12 -0
  205. package/vendor/node_modules/undici/types/mock-interceptor.d.ts +94 -0
  206. package/vendor/node_modules/undici/types/mock-pool.d.ts +27 -0
  207. package/vendor/node_modules/undici/types/patch.d.ts +29 -0
  208. package/vendor/node_modules/undici/types/pool-stats.d.ts +19 -0
  209. package/vendor/node_modules/undici/types/pool.d.ts +41 -0
  210. package/vendor/node_modules/undici/types/proxy-agent.d.ts +29 -0
  211. package/vendor/node_modules/undici/types/readable.d.ts +68 -0
  212. package/vendor/node_modules/undici/types/retry-agent.d.ts +8 -0
  213. package/vendor/node_modules/undici/types/retry-handler.d.ts +125 -0
  214. package/vendor/node_modules/undici/types/round-robin-pool.d.ts +41 -0
  215. package/vendor/node_modules/undici/types/snapshot-agent.d.ts +109 -0
  216. package/vendor/node_modules/undici/types/socks5-proxy-agent.d.ts +25 -0
  217. package/vendor/node_modules/undici/types/util.d.ts +18 -0
  218. package/vendor/node_modules/undici/types/utility.d.ts +7 -0
  219. package/vendor/node_modules/undici/types/webidl.d.ts +347 -0
  220. package/vendor/node_modules/undici/types/websocket.d.ts +188 -0
@@ -0,0 +1,542 @@
1
+ //#region lib/types/policy.js
2
+ /**
3
+ * Proxy policy resolution: the pure, transport-free half of this package. It turns the launch
4
+ * environment into one {@link ProxyPolicy}, and answers which proxy
5
+ * (if any) a given URL goes through.
6
+ *
7
+ * Nothing here imports `undici`, so the module stays loadable in the browser-worker runtime that
8
+ * evaluates `dsh-web-fetch-http` without a Node transport.
9
+ * @module @deepseek-ai/dsh-http-proxy/policy
10
+ */
11
+ /**
12
+ * Loopback entries merged into every policy's `noProxy`. A proxy that also serves the harness's own
13
+ * loopback traffic turns the Web UI, the Connection transport, and every local test server into a
14
+ * routing loop, so the bypass is not optional.
15
+ *
16
+ * `::1` and `[::1]` are both listed because the resolved string is also handed to undici, whose
17
+ * matcher reads a bare `::1` as host `:` port `1` and therefore never bypasses it.
18
+ */
19
+ const LOOPBACK_NO_PROXY = [
20
+ "localhost",
21
+ "127.0.0.1",
22
+ "::1",
23
+ "[::1]"
24
+ ];
25
+ /**
26
+ * The environment names each policy field owns, lowercase first — undici reads the lowercase name
27
+ * first, so both casings are always written or cleared together.
28
+ */
29
+ const POLICY_ENV_NAMES = {
30
+ httpProxy: ["http_proxy", "HTTP_PROXY"],
31
+ httpsProxy: ["https_proxy", "HTTPS_PROXY"],
32
+ noProxy: ["no_proxy", "NO_PROXY"]
33
+ };
34
+ /**
35
+ * Every environment name that carries proxy configuration, including the `ALL_PROXY` fallback this
36
+ * package resolves but never writes back. A caller that must isolate a child from the machine's
37
+ * network policy clears exactly these.
38
+ */
39
+ const PROXY_ENV_NAMES = [
40
+ ...Object.values(POLICY_ENV_NAMES).flat(),
41
+ "all_proxy",
42
+ "ALL_PROXY"
43
+ ];
44
+ /** Proxy URL schemes this package routes through. Everything else is reported, never silently dropped. */
45
+ const SUPPORTED_PROTOCOLS = new Set(["http:", "https:"]);
46
+ /** Schemes recognised well enough to name in a diagnostic instead of calling them malformed. */
47
+ const SOCKS_PROTOCOLS = new Set([
48
+ "socks:",
49
+ "socks4:",
50
+ "socks4a:",
51
+ "socks5:",
52
+ "socks5h:"
53
+ ]);
54
+ /** A policy that proxies nothing. Callers that have not installed a policy resolve URLs against this. */
55
+ const DIRECT_POLICY = {
56
+ noProxy: "",
57
+ source: "none"
58
+ };
59
+ /**
60
+ * Read one environment name in undici's precedence order — lowercase first, uppercase as the
61
+ * fallback — treating a blank value as unset. Blank matters: undici's own `??` chain lets an empty
62
+ * lowercase name shadow a populated uppercase one.
63
+ *
64
+ * @param env - the launch environment snapshot to read.
65
+ * @param lower - the lowercase variable name.
66
+ * @returns the trimmed value and the name that supplied it, or `undefined` when neither is set.
67
+ */
68
+ function readEnv(env, lower) {
69
+ for (const name of [lower, lower.toUpperCase()]) {
70
+ const value = env.get(name)?.value.trim();
71
+ if (value !== void 0 && value !== "") return {
72
+ value,
73
+ name
74
+ };
75
+ }
76
+ }
77
+ /** A slot nobody filled. */
78
+ const ABSENT = { kind: "absent" };
79
+ /**
80
+ * Validate one candidate proxy URL.
81
+ *
82
+ * @param candidate - the raw value and the origin to name in a diagnostic.
83
+ * @param diagnostics - collector the rejection is appended to.
84
+ * @returns the candidate's usability, distinguishing a rejected slot from an empty one.
85
+ */
86
+ function acceptProxyUrl(candidate, diagnostics) {
87
+ if (candidate === void 0) return ABSENT;
88
+ const parsed = URL.parse(candidate.value);
89
+ if (parsed === null) {
90
+ diagnostics.push({
91
+ kind: "invalid",
92
+ origin: candidate.name,
93
+ message: `${candidate.name} is not a valid URL; connecting directly`
94
+ });
95
+ return { kind: "rejected" };
96
+ }
97
+ if (SOCKS_PROTOCOLS.has(parsed.protocol)) {
98
+ diagnostics.push({
99
+ kind: "socks",
100
+ origin: candidate.name,
101
+ message: `${candidate.name} names a SOCKS proxy, which is not supported; connecting directly for that scheme — set an http:// or https:// proxy URL instead`
102
+ });
103
+ return { kind: "rejected" };
104
+ }
105
+ if (!SUPPORTED_PROTOCOLS.has(parsed.protocol)) {
106
+ diagnostics.push({
107
+ kind: "invalid",
108
+ origin: candidate.name,
109
+ message: `${candidate.name} uses the unsupported ${parsed.protocol}// scheme; connecting directly for that scheme — set an http:// or https:// proxy URL instead`
110
+ });
111
+ return { kind: "rejected" };
112
+ }
113
+ return {
114
+ kind: "accepted",
115
+ value: candidate.value
116
+ };
117
+ }
118
+ /**
119
+ * Whether a proxy URL is one this package accepts: parseable, with an `http:` or `https:` scheme.
120
+ * The same test {@link acceptProxyUrl} applies, without its diagnostics.
121
+ *
122
+ * @param value - the proxy URL as an environment variable holds it.
123
+ * @returns true when the URL would be accepted.
124
+ */
125
+ function isSupportedProxyUrl(value) {
126
+ const parsed = URL.parse(value);
127
+ return parsed !== null && SUPPORTED_PROTOCOLS.has(parsed.protocol);
128
+ }
129
+ /**
130
+ * Resolve one scheme's proxy from its own slot, then the fallbacks — but only when the scheme's own
131
+ * slot was empty. A rejected slot keeps that scheme direct, so the diagnostic and the route agree.
132
+ *
133
+ * @param own - what the scheme's own name supplied.
134
+ * @param fallbacks - values to try in order when `own` is absent.
135
+ * @returns the proxy URL for that scheme, or `undefined` for a direct connection.
136
+ */
137
+ function resolveScheme(own, ...fallbacks) {
138
+ if (own.kind === "accepted") return own.value;
139
+ if (own.kind === "rejected") return void 0;
140
+ return fallbacks.find((value) => value !== void 0);
141
+ }
142
+ /**
143
+ * Merge {@link LOOPBACK_NO_PROXY} into a bypass list, preserving the caller's entries and order.
144
+ * A list of `*` already bypasses everything and is returned unchanged.
145
+ *
146
+ * @param noProxy - the bypass list as the environment supplied it.
147
+ * @returns the effective bypass list.
148
+ */
149
+ function withLoopback(noProxy) {
150
+ const entries = (noProxy ?? "").split(/[,\s]+/).map((entry) => entry.trim()).filter((entry) => entry !== "");
151
+ if (entries.includes("*")) return "*";
152
+ const present = new Set(entries.map((entry) => entry.toLowerCase()));
153
+ return [...entries, ...LOOPBACK_NO_PROXY.filter((entry) => !present.has(entry))].join(",");
154
+ }
155
+ /**
156
+ * Split one bypass entry into host and optional port.
157
+ *
158
+ * A bare IPv6 literal carries several colons and no port, so only a single-colon entry splits;
159
+ * a bracketed literal takes its port from after the bracket. Getting this wrong is how undici
160
+ * turns `::1` into host `:` port `1`.
161
+ *
162
+ * @param entry - one already-trimmed bypass entry.
163
+ * @returns the entry's host and, when it carries one, its port.
164
+ */
165
+ function splitHostPort(entry) {
166
+ if (entry.startsWith("[")) {
167
+ const close = entry.indexOf("]");
168
+ if (close !== -1) {
169
+ const rest = entry.slice(close + 1);
170
+ const host = entry.slice(1, close);
171
+ return rest.startsWith(":") ? {
172
+ host,
173
+ port: rest.slice(1)
174
+ } : { host };
175
+ }
176
+ }
177
+ const colon = entry.indexOf(":");
178
+ if (colon !== -1 && entry.indexOf(":", colon + 1) === -1) return {
179
+ host: entry.slice(0, colon),
180
+ port: entry.slice(colon + 1)
181
+ };
182
+ return { host: entry };
183
+ }
184
+ /** One IPv4 octet, so a loopback match cannot accept `127.999.1.1`. */
185
+ const OCTET = "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)";
186
+ /** The whole `127.0.0.0/8` block, not just its first address. */
187
+ const LOOPBACK_IPV4 = new RegExp(`^127\\.${OCTET}\\.${OCTET}\\.${OCTET}$`);
188
+ /**
189
+ * Whether a host names this machine.
190
+ *
191
+ * A proxy cannot meaningfully reach one: it would resolve the address in its own network, and a
192
+ * proxy running on this machine would reach a service that only listens on loopback. The bypass
193
+ * list carries {@link LOOPBACK_NO_PROXY} for the consumers that read an environment rather than a
194
+ * policy, but those are four literal entries — matching them alone leaves `127.0.0.2`, the whole
195
+ * rest of `127.0.0.0/8`, and the IPv4-mapped spelling routed through the proxy.
196
+ *
197
+ * @param hostname - a URL's hostname, bracketed or not.
198
+ * @returns true when the host is loopback or the unspecified address.
199
+ */
200
+ function isLoopbackHost(hostname) {
201
+ const host = hostname.replace(/^\[|\]$/g, "").replace(/\.$/, "").toLowerCase();
202
+ if (host === "localhost" || host.endsWith(".localhost")) return true;
203
+ if (host === "::1" || host === "::" || host === "0.0.0.0") return true;
204
+ const mappedHigh = /^::ffff:([0-9a-f]{1,4}):[0-9a-f]{1,4}$/.exec(host)?.[1];
205
+ if (mappedHigh !== void 0) return Number.parseInt(mappedHigh, 16) >>> 8 === 127;
206
+ return LOOPBACK_IPV4.test(host.startsWith("::ffff:") ? host.slice(7) : host);
207
+ }
208
+ /**
209
+ * Decide whether a bypass list exempts one URL. An entry names a host and matches it together with
210
+ * every subdomain under it — `example.com` also bypasses `api.example.com` — and a leading `.` or
211
+ * `*.` is accepted as the same thing; an entry may carry a `:port`, and `*` bypasses everything.
212
+ * CIDR notation is not matched —
213
+ * an operating system's bypass list often carries `10.0.0.0/8`, which must be rewritten as suffixes.
214
+ *
215
+ * @param noProxy - the effective bypass list.
216
+ * @param url - the request URL.
217
+ * @returns true when the URL must bypass the proxy.
218
+ */
219
+ function bypassesProxy(noProxy, url) {
220
+ const host = url.hostname.replace(/^\[|\]$/g, "").replace(/\.$/, "").toLowerCase();
221
+ const port = url.port !== "" ? url.port : url.protocol === "https:" ? "443" : "80";
222
+ for (const raw of noProxy.split(/[,\s]+/)) {
223
+ const entry = raw.trim().toLowerCase();
224
+ if (entry === "") continue;
225
+ if (entry === "*") return true;
226
+ const split = splitHostPort(entry);
227
+ if (split.port !== void 0 && split.port !== port) continue;
228
+ const candidate = split.host.replace(/^\*?\./, "").replace(/\.$/, "");
229
+ if (candidate === "") continue;
230
+ if (host === candidate || host.endsWith(`.${candidate}`)) return true;
231
+ }
232
+ return false;
233
+ }
234
+ /**
235
+ * Resolve the outbound proxy policy for this process.
236
+ *
237
+ * A scheme's own variable wins, then `ALL_PROXY`, then — for HTTPS only — the HTTP proxy, matching
238
+ * undici so this function and the installed dispatcher never disagree about one URL.
239
+ *
240
+ * @param env - the launch environment, whose own layering already prefers real variables over `.env` files.
241
+ * @returns the policy to install plus every rejected candidate.
242
+ */
243
+ function resolveProxyPolicy(env) {
244
+ const diagnostics = [];
245
+ const all = acceptProxyUrl(readEnv(env, "all_proxy"), diagnostics);
246
+ const allValue = all.kind === "accepted" ? all.value : void 0;
247
+ const envHttp = acceptProxyUrl(readEnv(env, "http_proxy"), diagnostics);
248
+ const envHttps = acceptProxyUrl(readEnv(env, "https_proxy"), diagnostics);
249
+ const httpProxy = resolveScheme(envHttp, allValue);
250
+ const httpsProxy = resolveScheme(envHttps, allValue, httpProxy);
251
+ if (httpProxy === void 0 && httpsProxy === void 0) return {
252
+ policy: DIRECT_POLICY,
253
+ diagnostics
254
+ };
255
+ return {
256
+ policy: {
257
+ ...httpProxy === void 0 ? {} : { httpProxy },
258
+ ...httpsProxy === void 0 ? {} : { httpsProxy },
259
+ noProxy: withLoopback(readEnv(env, "no_proxy")?.value),
260
+ source: "env"
261
+ },
262
+ diagnostics
263
+ };
264
+ }
265
+ /**
266
+ * Resolve which proxy one URL goes through under a policy.
267
+ *
268
+ * This is the single answer both the installed dispatcher and `dsh-web-fetch-http` consult, so a URL
269
+ * can never be pinned to a resolved address by one and tunnelled by the other.
270
+ *
271
+ * @param policy - the active policy.
272
+ * @param url - the request URL.
273
+ * @returns the proxy URL to tunnel through, or `undefined` for a direct connection.
274
+ */
275
+ function proxyForUrl(policy, url) {
276
+ const proxy = url.protocol === "https:" ? policy.httpsProxy : url.protocol === "http:" ? policy.httpProxy : void 0;
277
+ if (proxy === void 0) return void 0;
278
+ if (isLoopbackHost(url.hostname)) return void 0;
279
+ // dsh-model-proxy patch: an optional allowlist narrows routing to named
280
+ // hosts only; the bypass matcher is reused, so `*` still means everything.
281
+ if (typeof policy.hosts === "string" && policy.hosts.trim() !== "" && !bypassesProxy(policy.hosts, url)) return void 0;
282
+ return bypassesProxy(policy.noProxy, url) ? void 0 : proxy;
283
+ }
284
+ //#endregion
285
+ //#region lib/types/install.js
286
+ /**
287
+ * Proxy installation: the transport half of this package. It owns undici's global dispatcher and the
288
+ * process-wide record of which policy is active.
289
+ *
290
+ * `undici` is imported dynamically so the pure {@link ProxyPolicy} half stays loadable where no Node
291
+ * transport exists, matching how `dsh-web-fetch-http` defers its own transport import.
292
+ * @module @deepseek-ai/dsh-http-proxy/install
293
+ */
294
+ /** The active policy, or `undefined` until one is installed. Process-wide, like the dispatcher it tracks. */
295
+ let active;
296
+ /**
297
+ * The proxy environment as the user exported it, or `undefined` when no policy is installed.
298
+ *
299
+ * Owned by the OUTERMOST install: one layered over the launcher's would otherwise record the outer
300
+ * policy's published values as if the user had written them, and
301
+ * hand every child a normalization the user never asked for.
302
+ *
303
+ * {@link proxyEnvironmentForChild} keeps a value the user set rather than the one this process resolved from
304
+ * it, so a SOCKS proxy `curl` can use is not replaced by an HTTP proxy named for another scheme.
305
+ */
306
+ let inheritedProxyEnv;
307
+ /** The dispatcher installed with {@link active}, so a route can hand back the one already routing. */
308
+ let installed;
309
+ /** A route that sends nothing through a proxy, shared because it carries no per-request state. */
310
+ const DIRECT_ROUTE = { proxied: false };
311
+ /**
312
+ * Decide how to send one request, and hand back the transport that decision assumed.
313
+ *
314
+ * @param url - the request URL.
315
+ * @returns the proxied route with its proxy URL and dispatcher, or the direct route.
316
+ */
317
+ function proxyRouteFor(url) {
318
+ const policy = active;
319
+ const dispatcher = installed;
320
+ if (policy === void 0 || dispatcher === void 0) return DIRECT_ROUTE;
321
+ const proxy = proxyForUrl(policy, url);
322
+ return proxy === void 0 ? DIRECT_ROUTE : {
323
+ proxied: true,
324
+ proxy,
325
+ dispatcher
326
+ };
327
+ }
328
+ /**
329
+ * Publish a policy through the proxy environment variables, which is how the consumers that read an
330
+ * environment rather than a policy object — `node:http`'s `proxyEnv` and every spawned child — see
331
+ * the one resolved answer, including the `ALL_PROXY` fallback and the merged loopback bypass that
332
+ * neither derives on its own. The global dispatcher does not read these; it routes by the policy.
333
+ *
334
+ * @param policy - the policy to publish.
335
+ * @returns a function restoring every name this call changed.
336
+ */
337
+ function applyPolicyEnv(policy) {
338
+ const previousInherited = inheritedProxyEnv;
339
+ inheritedProxyEnv = previousInherited ?? snapshotProxyEnv();
340
+ const published = {};
341
+ for (const [field, names] of Object.entries(POLICY_ENV_NAMES)) {
342
+ const value = policy[field];
343
+ for (const name of names) published[name] = value;
344
+ }
345
+ const restore = writeProxyEnv(published);
346
+ return () => {
347
+ restore();
348
+ inheritedProxyEnv = previousInherited;
349
+ };
350
+ }
351
+ /**
352
+ * Read every proxy name this package publishes, as `process.env` holds it now.
353
+ *
354
+ * @returns one entry per name in {@link POLICY_ENV_NAMES}; `undefined` marks an absent name.
355
+ */
356
+ function snapshotProxyEnv() {
357
+ const snapshot = {};
358
+ for (const names of Object.values(POLICY_ENV_NAMES)) for (const name of names) snapshot[name] = process.env[name];
359
+ return snapshot;
360
+ }
361
+ /**
362
+ * Set every proxy name to the value `values` holds for it, removing a name whose value is `undefined`.
363
+ *
364
+ * @param values - the value each name in {@link POLICY_ENV_NAMES} should hold.
365
+ * @returns a function restoring every name to what it held before this call.
366
+ */
367
+ function writeProxyEnv(values) {
368
+ const previous = snapshotProxyEnv();
369
+ for (const name of Object.keys(previous)) {
370
+ const value = values[name];
371
+ if (value === void 0) Reflect.deleteProperty(process.env, name);
372
+ else process.env[name] = value;
373
+ }
374
+ return () => {
375
+ for (const [name, value] of Object.entries(previous)) if (value === void 0) Reflect.deleteProperty(process.env, name);
376
+ else process.env[name] = value;
377
+ };
378
+ }
379
+ /**
380
+ * Build the global dispatcher for one policy.
381
+ *
382
+ * Routing runs through {@link proxyForUrl} per origin, so `fetch` and every caller that asks where a
383
+ * URL goes read the same answer from the same matcher. undici's `EnvHttpProxyAgent` cannot express
384
+ * this policy: with no `HTTPS_PROXY` present it reuses the HTTP proxy for `https:`, which would
385
+ * tunnel a scheme this package deliberately keeps direct after refusing the SOCKS or malformed URL
386
+ * the user named for it — the route and the diagnostic would then disagree.
387
+ *
388
+ * @param policy - the policy to route by; it must proxy at least one scheme.
389
+ * @returns the dispatcher to install, owning every per-origin agent its factory created.
390
+ */
391
+ async function createPolicyDispatcher(policy) {
392
+ const { Agent, Pool, ProxyAgent } = await import("undici");
393
+ return new Agent({ factory(origin, options) {
394
+ const passed = options;
395
+ const proxy = proxyForUrl(policy, new URL(origin.toString()));
396
+ if (proxy !== void 0) return new ProxyAgent({
397
+ ...passed,
398
+ uri: proxy
399
+ });
400
+ return new Pool(origin, passed);
401
+ } });
402
+ }
403
+ /**
404
+ * Route this process's outbound HTTP through `policy`.
405
+ *
406
+ * Installing replaces undici's global dispatcher, which is what Node's built-in `fetch` resolves, so
407
+ * every caller that issues a plain `fetch()` is covered without knowing this package exists. A policy
408
+ * that proxies nothing installs a direct dispatcher and leaves the environment untouched.
409
+ *
410
+ * A worker thread has its own `globalThis` and so its own dispatcher; installing here does not
411
+ * reach it. No worker installs one today: both this repository ships — the workflow engine and the
412
+ * PTC runtime — evaluate model-authored scripts, which must not receive a proxy URL that may carry
413
+ * credentials. A worker that needs the policy has to be handed one explicitly and install it itself.
414
+ *
415
+ * @param policy - the resolved policy to install.
416
+ * @returns a disposer restoring the previous dispatcher, policy, and environment, then closing the agent.
417
+ */
418
+ async function installGlobalProxy(policy) {
419
+ const previousPolicy = active;
420
+ if (policy.source === "none") {
421
+ if (previousPolicy === void 0) {
422
+ active = policy;
423
+ return () => {
424
+ active = previousPolicy;
425
+ return Promise.resolve();
426
+ };
427
+ }
428
+ const previousInstalled = installed;
429
+ const restoreEnv = inheritedProxyEnv === void 0 ? void 0 : writeProxyEnv(inheritedProxyEnv);
430
+ const undici = await import("undici");
431
+ const previous = undici.getGlobalDispatcher();
432
+ const direct = new undici.Agent();
433
+ undici.setGlobalDispatcher(direct);
434
+ active = policy;
435
+ installed = void 0;
436
+ return async () => {
437
+ undici.setGlobalDispatcher(previous);
438
+ active = previousPolicy;
439
+ installed = previousInstalled;
440
+ restoreEnv?.();
441
+ await direct.close();
442
+ };
443
+ }
444
+ const restoreEnv = applyPolicyEnv(policy);
445
+ const { getGlobalDispatcher, setGlobalDispatcher } = await import("undici");
446
+ const previousDispatcher = getGlobalDispatcher();
447
+ const previousInstalled = installed;
448
+ const agent = await createPolicyDispatcher(policy);
449
+ setGlobalDispatcher(agent);
450
+ active = policy;
451
+ installed = agent;
452
+ return async () => {
453
+ setGlobalDispatcher(previousDispatcher);
454
+ active = previousPolicy;
455
+ installed = previousInstalled;
456
+ restoreEnv();
457
+ await agent.close();
458
+ };
459
+ }
460
+ /**
461
+ * The proxy environment a spawned child needs.
462
+ *
463
+ * A child inherits the parent environment, which this process rewrote to its own resolved policy.
464
+ * Handing that normalization straight through would replace values the user set for other tools, so
465
+ * each proxy name the user exported is restored to what they wrote: a SOCKS proxy `curl` uses is
466
+ * not swapped for the HTTP one this package fell back to for that scheme.
467
+ *
468
+ * A scheme the user named in neither casing carries the resolved value instead of being removed.
469
+ * Without that the child's routing silently diverges from its parent's: `NODE_USE_ENV_PROXY` does
470
+ * not read `ALL_PROXY`, so a child of a parent that resolved its proxy from that name would connect
471
+ * directly while the parent proxies.
472
+ *
473
+ * The bypass list is always the resolved one. It only ever adds the loopback entries to what
474
+ * the user wrote, so nothing is lost, and the child stops sending its own localhost traffic to a
475
+ * proxy that cannot route it.
476
+ *
477
+ * The flag reaches only Node 22.21+ and 24+; an older runtime keeps that child direct. Such a child
478
+ * also matches bypass entries with Node's own `NO_PROXY` rules, which differ from this package's in
479
+ * their separators and IPv4-range support. Non-Node children (curl, git, pnpm) ignore the flag and
480
+ * read the variables themselves.
481
+ *
482
+ * The flag is withheld when a proxy value the child receives is one this package refused. Node
483
+ * parses `HTTP_PROXY` and `HTTPS_PROXY` under that flag before running the program, and exits on a
484
+ * scheme other than `http:` or `https:` — so a SOCKS value kept for `curl` would stop every Node
485
+ * child from starting. Without the flag such a child connects directly, as this process already
486
+ * reported for that scheme, and `curl` still reads the value it was kept for.
487
+ *
488
+ * A worker thread is deliberately NOT served here — see the workflow engine, which runs
489
+ * model-authored scripts and must not receive a proxy URL that may carry credentials.
490
+ *
491
+ * @returns names to apply to the child environment, where `undefined` means remove, or an empty
492
+ * object when no proxy is active.
493
+ */
494
+ function proxyEnvironmentForChild() {
495
+ const policy = active;
496
+ const inherited = inheritedProxyEnv;
497
+ if (policy === void 0 || policy.source === "none" || inherited === void 0) return {};
498
+ const overlay = { NODE_USE_ENV_PROXY: "1" };
499
+ for (const [field, names] of Object.entries(POLICY_ENV_NAMES)) {
500
+ const resolved = policy[field];
501
+ const named = field !== "noProxy" && names.some((name) => inherited[name] !== void 0);
502
+ for (const name of names) overlay[name] = named ? inherited[name] : resolved;
503
+ }
504
+ if ([...POLICY_ENV_NAMES.httpProxy, ...POLICY_ENV_NAMES.httpsProxy].some((name) => overlay[name] !== void 0 && !isSupportedProxyUrl(overlay[name]))) delete overlay.NODE_USE_ENV_PROXY;
505
+ return overlay;
506
+ }
507
+ /**
508
+ * Resolve this process's proxy policy from `env` and install it.
509
+ *
510
+ * Resolution, reporting, and installation are one operation because no caller needs them apart: the
511
+ * launcher does all three in sequence before the first plugin mounts, and a policy resolved but not
512
+ * installed routes nothing.
513
+ *
514
+ * A value the environment supplies but this package cannot use is reported and skipped rather than
515
+ * thrown: the variable may have been exported for another tool, and a proxy the harness cannot use
516
+ * must not stop the agent from starting.
517
+ *
518
+ * @param env - the launch environment, whose own layering already prefers real variables over `.env` files.
519
+ * @param report - receives one message per rejected value, in the order the values were considered.
520
+ * @returns a disposer restoring the previous dispatcher, policy, and environment.
521
+ */
522
+ async function installProxyFromEnvironment(env, report, options) {
523
+ const { policy, diagnostics } = resolveProxyPolicy(env);
524
+ for (const diagnostic of diagnostics) report(diagnostic.message);
525
+ // dsh-model-proxy patch: callers may narrow the policy to an allowlist of
526
+ // hosts; anything else is left to the resolved policy untouched.
527
+ if (typeof options?.hosts === "string") policy.hosts = options.hosts;
528
+ return await installGlobalProxy(policy);
529
+ }
530
+ /**
531
+ * The environment overlay that removes every proxy name from a spawned child.
532
+ *
533
+ * A harness that replays a recorded session must reach its own fixture server, not the proxy a
534
+ * developer or a CI runner exported; `undefined` is how a spawn removes a name it inherits.
535
+ *
536
+ * @returns one entry per proxy name, each `undefined`.
537
+ */
538
+ function clearedProxyEnv() {
539
+ return Object.fromEntries(PROXY_ENV_NAMES.map((name) => [name, void 0]));
540
+ }
541
+ //#endregion
542
+ export { clearedProxyEnv, installProxyFromEnvironment, proxyEnvironmentForChild, proxyRouteFor };
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Matteo Collina and Undici contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.