cclawd 1.0.2 → 1.0.4

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 (68) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/plugin-sdk/active-listener-CN-tMEvN.js +35 -0
  3. package/dist/plugin-sdk/api-key-rotation-CimGYMBc.js +176 -0
  4. package/dist/plugin-sdk/audio-preflight-C-xXBoE2.js +51 -0
  5. package/dist/plugin-sdk/audio-transcription-runner-CTIHpebA.js +2173 -0
  6. package/dist/plugin-sdk/audit-membership-runtime-BFatB2LJ.js +58 -0
  7. package/dist/plugin-sdk/channel-activity-DO0FEzyj.js +95 -0
  8. package/dist/plugin-sdk/channel-web-Da-__nUF.js +2238 -0
  9. package/dist/plugin-sdk/commands-registry-6no2NNrY.js +1118 -0
  10. package/dist/plugin-sdk/compact.runtime-CCoclu5e.js +35 -0
  11. package/dist/plugin-sdk/config-B9ODwgpz.js +37426 -0
  12. package/dist/plugin-sdk/deliver-B1fFpKjV.js +1757 -0
  13. package/dist/plugin-sdk/deliver-runtime-DB-VRMe1.js +15 -0
  14. package/dist/plugin-sdk/deps-send-discord.runtime-DklqycYG.js +15 -0
  15. package/dist/plugin-sdk/deps-send-imessage.runtime-Chs8zeon.js +14 -0
  16. package/dist/plugin-sdk/deps-send-signal.runtime-clW9aSJP.js +13 -0
  17. package/dist/plugin-sdk/deps-send-slack.runtime-BUx0LYY1.js +13 -0
  18. package/dist/plugin-sdk/deps-send-telegram.runtime-LECSHgMG.js +16 -0
  19. package/dist/plugin-sdk/deps-send-whatsapp.runtime-D2d65fw0.js +40 -0
  20. package/dist/plugin-sdk/diagnostic-CxIvS-C2.js +315 -0
  21. package/dist/plugin-sdk/dispatch-BqlR4dPx.js +105863 -0
  22. package/dist/plugin-sdk/env-b9k1PHMI.js +34 -0
  23. package/dist/plugin-sdk/fetch-PoxzAANT.js +326 -0
  24. package/dist/plugin-sdk/fetch-guard-4UVSZ0uS.js +164 -0
  25. package/dist/plugin-sdk/image-Ch6M4tnJ.js +2420 -0
  26. package/dist/plugin-sdk/image-runtime-CSh2o5wY.js +8 -0
  27. package/dist/plugin-sdk/index.js +35 -35
  28. package/dist/plugin-sdk/ir-CugsqGH8.js +1312 -0
  29. package/dist/plugin-sdk/local-roots-adnEg9zb.js +217 -0
  30. package/dist/plugin-sdk/logger-D6zRubj0.js +1164 -0
  31. package/dist/plugin-sdk/login-CYvkQ0At.js +54 -0
  32. package/dist/plugin-sdk/login-qr-ll4NtaT5.js +316 -0
  33. package/dist/plugin-sdk/manager-CHy8IclH.js +3959 -0
  34. package/dist/plugin-sdk/manager-runtime-C70EkEr7.js +11 -0
  35. package/dist/plugin-sdk/outbound-Wzs2iN7X.js +216 -0
  36. package/dist/plugin-sdk/outbound-attachment-khXJwucX.js +17 -0
  37. package/dist/plugin-sdk/paths-BtVqCdw4.js +3063 -0
  38. package/dist/plugin-sdk/pi-model-discovery-Dh4ziodY.js +131 -0
  39. package/dist/plugin-sdk/pi-model-discovery-runtime-b83Xe-HT.js +8 -0
  40. package/dist/plugin-sdk/pi-tools.before-tool-call.runtime-C1z5CDBF.js +349 -0
  41. package/dist/plugin-sdk/proxy-fetch-CJEmoBxi.js +54 -0
  42. package/dist/plugin-sdk/pw-ai-Dj3Cvlzl.js +1990 -0
  43. package/dist/plugin-sdk/qmd-manager-egHUAseQ.js +1581 -0
  44. package/dist/plugin-sdk/resolve-outbound-target-BiICvIKs.js +38 -0
  45. package/dist/plugin-sdk/runtime-whatsapp-login.runtime-DNApufzW.js +9 -0
  46. package/dist/plugin-sdk/runtime-whatsapp-outbound.runtime-CBmtfIQ8.js +13 -0
  47. package/dist/plugin-sdk/send-CScblaI4.js +532 -0
  48. package/dist/plugin-sdk/send-CeHhnld6.js +407 -0
  49. package/dist/plugin-sdk/send-DP_c8JfR.js +3277 -0
  50. package/dist/plugin-sdk/send-Dc5fI6e8.js +495 -0
  51. package/dist/plugin-sdk/send-l-77_s1_.js +2507 -0
  52. package/dist/plugin-sdk/session-CkOKZaqa.js +166 -0
  53. package/dist/plugin-sdk/signal.js +2 -2
  54. package/dist/plugin-sdk/skill-commands-BohYCgkq.js +336 -0
  55. package/dist/plugin-sdk/slash-commands.runtime-DpLfVTM6.js +8 -0
  56. package/dist/plugin-sdk/slash-dispatch.runtime-CASMHwpm.js +35 -0
  57. package/dist/plugin-sdk/slash-skill-commands.runtime-D7rrJEci.js +9 -0
  58. package/dist/plugin-sdk/sqlite-CJE3X7Mv.js +1005 -0
  59. package/dist/plugin-sdk/subagent-registry-runtime-B1oo5bih.js +35 -0
  60. package/dist/plugin-sdk/tables-D5VgpTmm.js +53 -0
  61. package/dist/plugin-sdk/target-errors-C6zZ_OpA.js +191 -0
  62. package/dist/plugin-sdk/tokens-DUnJnpMS.js +50 -0
  63. package/dist/plugin-sdk/web-TfUM1nSi.js +39 -0
  64. package/dist/plugin-sdk/whatsapp-actions-DuWJ0j1r.js +71 -0
  65. package/extensions/mfa-auth/index.ts +36 -17
  66. package/extensions/mfa-auth/src/auth-manager.ts +4 -0
  67. package/extensions/mfa-auth/src/notification-service.ts +5 -1
  68. package/package.json +1 -1
@@ -0,0 +1,34 @@
1
+ import { a as createSubsystemLogger } from "./logger-D6zRubj0.js";
2
+ //#region src/utils/boolean.ts
3
+ const DEFAULT_TRUTHY = [
4
+ "true",
5
+ "1",
6
+ "yes",
7
+ "on"
8
+ ];
9
+ const DEFAULT_FALSY = [
10
+ "false",
11
+ "0",
12
+ "no",
13
+ "off"
14
+ ];
15
+ const DEFAULT_TRUTHY_SET = new Set(DEFAULT_TRUTHY);
16
+ const DEFAULT_FALSY_SET = new Set(DEFAULT_FALSY);
17
+ function parseBooleanValue(value, options = {}) {
18
+ if (typeof value === "boolean") return value;
19
+ if (typeof value !== "string") return;
20
+ const normalized = value.trim().toLowerCase();
21
+ if (!normalized) return;
22
+ const truthy = options.truthy ?? DEFAULT_TRUTHY;
23
+ const falsy = options.falsy ?? DEFAULT_FALSY;
24
+ const truthySet = truthy === DEFAULT_TRUTHY ? DEFAULT_TRUTHY_SET : new Set(truthy);
25
+ const falsySet = falsy === DEFAULT_FALSY ? DEFAULT_FALSY_SET : new Set(falsy);
26
+ if (truthySet.has(normalized)) return true;
27
+ if (falsySet.has(normalized)) return false;
28
+ }
29
+ createSubsystemLogger("env");
30
+ function isTruthyEnvValue(value) {
31
+ return parseBooleanValue(value) === true;
32
+ }
33
+ //#endregion
34
+ export { parseBooleanValue as n, isTruthyEnvValue as t };
@@ -0,0 +1,326 @@
1
+ import { a as createSubsystemLogger } from "./logger-D6zRubj0.js";
2
+ import { t as isTruthyEnvValue } from "./env-b9k1PHMI.js";
3
+ import { t as resolveFetch } from "./fetch-BSKpf2dM.js";
4
+ import { t as getProxyUrlFromFetch } from "./proxy-fetch-CJEmoBxi.js";
5
+ import { readFileSync } from "node:fs";
6
+ import "node:fs/promises";
7
+ import process$1 from "node:process";
8
+ import * as dns from "node:dns";
9
+ import { Agent, EnvHttpProxyAgent, ProxyAgent, fetch } from "undici";
10
+ //#region src/infra/wsl.ts
11
+ function isWSLEnv() {
12
+ if (process.env.WSL_INTEROP || process.env.WSL_DISTRO_NAME || process.env.WSLENV) return true;
13
+ return false;
14
+ }
15
+ /**
16
+ * Synchronously check if running in WSL.
17
+ * Checks env vars first, then /proc/version.
18
+ */
19
+ function isWSLSync() {
20
+ if (process.platform !== "linux") return false;
21
+ if (isWSLEnv()) return true;
22
+ try {
23
+ const release = readFileSync("/proc/version", "utf8").toLowerCase();
24
+ return release.includes("microsoft") || release.includes("wsl");
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+ /**
30
+ * Synchronously check if running in WSL2.
31
+ */
32
+ function isWSL2Sync() {
33
+ if (!isWSLSync()) return false;
34
+ try {
35
+ const version = readFileSync("/proc/version", "utf8").toLowerCase();
36
+ return version.includes("wsl2") || version.includes("microsoft-standard");
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+ //#endregion
42
+ //#region src/telegram/network-config.ts
43
+ const TELEGRAM_DISABLE_AUTO_SELECT_FAMILY_ENV = "OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY";
44
+ const TELEGRAM_ENABLE_AUTO_SELECT_FAMILY_ENV = "OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY";
45
+ const TELEGRAM_DNS_RESULT_ORDER_ENV = "OPENCLAW_TELEGRAM_DNS_RESULT_ORDER";
46
+ let wsl2SyncCache;
47
+ function isWSL2SyncCached() {
48
+ if (typeof wsl2SyncCache === "boolean") return wsl2SyncCache;
49
+ wsl2SyncCache = isWSL2Sync();
50
+ return wsl2SyncCache;
51
+ }
52
+ function resolveTelegramAutoSelectFamilyDecision(params) {
53
+ const env = params?.env ?? process$1.env;
54
+ const nodeMajor = typeof params?.nodeMajor === "number" ? params.nodeMajor : Number(process$1.versions.node.split(".")[0]);
55
+ if (isTruthyEnvValue(env["OPENCLAW_TELEGRAM_ENABLE_AUTO_SELECT_FAMILY"])) return {
56
+ value: true,
57
+ source: `env:${TELEGRAM_ENABLE_AUTO_SELECT_FAMILY_ENV}`
58
+ };
59
+ if (isTruthyEnvValue(env["OPENCLAW_TELEGRAM_DISABLE_AUTO_SELECT_FAMILY"])) return {
60
+ value: false,
61
+ source: `env:${TELEGRAM_DISABLE_AUTO_SELECT_FAMILY_ENV}`
62
+ };
63
+ if (typeof params?.network?.autoSelectFamily === "boolean") return {
64
+ value: params.network.autoSelectFamily,
65
+ source: "config"
66
+ };
67
+ if (isWSL2SyncCached()) return {
68
+ value: false,
69
+ source: "default-wsl2"
70
+ };
71
+ if (Number.isFinite(nodeMajor) && nodeMajor >= 22) return {
72
+ value: true,
73
+ source: "default-node22"
74
+ };
75
+ return { value: null };
76
+ }
77
+ /**
78
+ * Resolve DNS result order setting for Telegram network requests.
79
+ * Some networks/ISPs have issues with IPv6 causing fetch failures.
80
+ * Setting "ipv4first" prioritizes IPv4 addresses in DNS resolution.
81
+ *
82
+ * Priority:
83
+ * 1. Environment variable OPENCLAW_TELEGRAM_DNS_RESULT_ORDER
84
+ * 2. Config: channels.telegram.network.dnsResultOrder
85
+ * 3. Default: "ipv4first" on Node 22+ (to work around common IPv6 issues)
86
+ */
87
+ function resolveTelegramDnsResultOrderDecision(params) {
88
+ const env = params?.env ?? process$1.env;
89
+ const nodeMajor = typeof params?.nodeMajor === "number" ? params.nodeMajor : Number(process$1.versions.node.split(".")[0]);
90
+ const envValue = env[TELEGRAM_DNS_RESULT_ORDER_ENV]?.trim().toLowerCase();
91
+ if (envValue === "ipv4first" || envValue === "verbatim") return {
92
+ value: envValue,
93
+ source: `env:${TELEGRAM_DNS_RESULT_ORDER_ENV}`
94
+ };
95
+ const configValue = (params?.network)?.dnsResultOrder?.trim().toLowerCase();
96
+ if (configValue === "ipv4first" || configValue === "verbatim") return {
97
+ value: configValue,
98
+ source: "config"
99
+ };
100
+ if (Number.isFinite(nodeMajor) && nodeMajor >= 22) return {
101
+ value: "ipv4first",
102
+ source: "default-node22"
103
+ };
104
+ return { value: null };
105
+ }
106
+ //#endregion
107
+ //#region src/telegram/fetch.ts
108
+ const log = createSubsystemLogger("telegram/network");
109
+ const TELEGRAM_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS = 300;
110
+ const TELEGRAM_API_HOSTNAME = "api.telegram.org";
111
+ const FALLBACK_RETRY_ERROR_CODES = [
112
+ "ETIMEDOUT",
113
+ "ENETUNREACH",
114
+ "EHOSTUNREACH",
115
+ "UND_ERR_CONNECT_TIMEOUT",
116
+ "UND_ERR_SOCKET"
117
+ ];
118
+ const IPV4_FALLBACK_RULES = [{
119
+ name: "fetch-failed-envelope",
120
+ matches: ({ message }) => message.includes("fetch failed")
121
+ }, {
122
+ name: "known-network-code",
123
+ matches: ({ codes }) => FALLBACK_RETRY_ERROR_CODES.some((code) => codes.has(code))
124
+ }];
125
+ function normalizeDnsResultOrder(value) {
126
+ if (value === "ipv4first" || value === "verbatim") return value;
127
+ return null;
128
+ }
129
+ function createDnsResultOrderLookup(order) {
130
+ if (!order) return;
131
+ const lookup = dns.lookup;
132
+ return (hostname, options, callback) => {
133
+ lookup(hostname, {
134
+ ...typeof options === "number" ? { family: options } : options ? { ...options } : {},
135
+ order,
136
+ verbatim: order === "verbatim"
137
+ }, callback);
138
+ };
139
+ }
140
+ function buildTelegramConnectOptions(params) {
141
+ const connect = {};
142
+ if (params.forceIpv4) {
143
+ connect.family = 4;
144
+ connect.autoSelectFamily = false;
145
+ } else if (typeof params.autoSelectFamily === "boolean") {
146
+ connect.autoSelectFamily = params.autoSelectFamily;
147
+ connect.autoSelectFamilyAttemptTimeout = TELEGRAM_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS;
148
+ }
149
+ const lookup = createDnsResultOrderLookup(params.dnsResultOrder);
150
+ if (lookup) connect.lookup = lookup;
151
+ return Object.keys(connect).length > 0 ? connect : null;
152
+ }
153
+ function shouldBypassEnvProxyForTelegramApi(env = process.env) {
154
+ const noProxyValue = env.no_proxy ?? env.NO_PROXY ?? "";
155
+ if (!noProxyValue) return false;
156
+ if (noProxyValue === "*") return true;
157
+ const targetHostname = TELEGRAM_API_HOSTNAME.toLowerCase();
158
+ const targetPort = 443;
159
+ const noProxyEntries = noProxyValue.split(/[,\s]/);
160
+ for (let i = 0; i < noProxyEntries.length; i++) {
161
+ const entry = noProxyEntries[i];
162
+ if (!entry) continue;
163
+ const parsed = entry.match(/^(.+):(\d+)$/);
164
+ const entryHostname = (parsed ? parsed[1] : entry).replace(/^\*?\./, "").toLowerCase();
165
+ const entryPort = parsed ? Number.parseInt(parsed[2], 10) : 0;
166
+ if (entryPort && entryPort !== targetPort) continue;
167
+ if (targetHostname === entryHostname || targetHostname.slice(-(entryHostname.length + 1)) === `.${entryHostname}`) return true;
168
+ }
169
+ return false;
170
+ }
171
+ function hasEnvHttpProxyForTelegramApi(env = process.env) {
172
+ const httpProxy = env.http_proxy ?? env.HTTP_PROXY;
173
+ const httpsProxy = env.https_proxy ?? env.HTTPS_PROXY;
174
+ return Boolean(httpProxy) || Boolean(httpsProxy);
175
+ }
176
+ function createTelegramDispatcher(params) {
177
+ const connect = buildTelegramConnectOptions({
178
+ autoSelectFamily: params.autoSelectFamily,
179
+ dnsResultOrder: params.dnsResultOrder,
180
+ forceIpv4: params.forceIpv4
181
+ });
182
+ const explicitProxyUrl = params.proxyUrl?.trim();
183
+ if (explicitProxyUrl) {
184
+ const proxyOptions = connect ? {
185
+ uri: explicitProxyUrl,
186
+ proxyTls: connect
187
+ } : explicitProxyUrl;
188
+ try {
189
+ return {
190
+ dispatcher: new ProxyAgent(proxyOptions),
191
+ mode: "explicit-proxy"
192
+ };
193
+ } catch (err) {
194
+ const reason = err instanceof Error ? err.message : String(err);
195
+ throw new Error(`explicit proxy dispatcher init failed: ${reason}`, { cause: err });
196
+ }
197
+ }
198
+ if (params.useEnvProxy) {
199
+ const proxyOptions = connect ? {
200
+ connect,
201
+ proxyTls: connect
202
+ } : void 0;
203
+ try {
204
+ return {
205
+ dispatcher: new EnvHttpProxyAgent(proxyOptions),
206
+ mode: "env-proxy"
207
+ };
208
+ } catch (err) {
209
+ log.warn(`env proxy dispatcher init failed; falling back to direct dispatcher: ${err instanceof Error ? err.message : String(err)}`);
210
+ }
211
+ }
212
+ return {
213
+ dispatcher: new Agent(connect ? { connect } : void 0),
214
+ mode: "direct"
215
+ };
216
+ }
217
+ function withDispatcherIfMissing(init, dispatcher) {
218
+ if (init?.dispatcher) return init ?? {};
219
+ return init ? {
220
+ ...init,
221
+ dispatcher
222
+ } : { dispatcher };
223
+ }
224
+ function resolveWrappedFetch(fetchImpl) {
225
+ return resolveFetch(fetchImpl) ?? fetchImpl;
226
+ }
227
+ function logResolverNetworkDecisions(params) {
228
+ if (params.autoSelectDecision.value !== null) {
229
+ const sourceLabel = params.autoSelectDecision.source ? ` (${params.autoSelectDecision.source})` : "";
230
+ log.info(`autoSelectFamily=${params.autoSelectDecision.value}${sourceLabel}`);
231
+ }
232
+ if (params.dnsDecision.value !== null) {
233
+ const sourceLabel = params.dnsDecision.source ? ` (${params.dnsDecision.source})` : "";
234
+ log.info(`dnsResultOrder=${params.dnsDecision.value}${sourceLabel}`);
235
+ }
236
+ }
237
+ function collectErrorCodes(err) {
238
+ const codes = /* @__PURE__ */ new Set();
239
+ const queue = [err];
240
+ const seen = /* @__PURE__ */ new Set();
241
+ while (queue.length > 0) {
242
+ const current = queue.shift();
243
+ if (!current || seen.has(current)) continue;
244
+ seen.add(current);
245
+ if (typeof current === "object") {
246
+ const code = current.code;
247
+ if (typeof code === "string" && code.trim()) codes.add(code.trim().toUpperCase());
248
+ const cause = current.cause;
249
+ if (cause && !seen.has(cause)) queue.push(cause);
250
+ const errors = current.errors;
251
+ if (Array.isArray(errors)) {
252
+ for (const nested of errors) if (nested && !seen.has(nested)) queue.push(nested);
253
+ }
254
+ }
255
+ }
256
+ return codes;
257
+ }
258
+ function formatErrorCodes(err) {
259
+ const codes = [...collectErrorCodes(err)];
260
+ return codes.length > 0 ? codes.join(",") : "none";
261
+ }
262
+ function shouldRetryWithIpv4Fallback(err) {
263
+ const ctx = {
264
+ message: err && typeof err === "object" && "message" in err ? String(err.message).toLowerCase() : "",
265
+ codes: collectErrorCodes(err)
266
+ };
267
+ for (const rule of IPV4_FALLBACK_RULES) if (!rule.matches(ctx)) return false;
268
+ return true;
269
+ }
270
+ function resolveTelegramFetch(proxyFetch, options) {
271
+ const autoSelectDecision = resolveTelegramAutoSelectFamilyDecision({ network: options?.network });
272
+ const dnsDecision = resolveTelegramDnsResultOrderDecision({ network: options?.network });
273
+ logResolverNetworkDecisions({
274
+ autoSelectDecision,
275
+ dnsDecision
276
+ });
277
+ const explicitProxyUrl = proxyFetch ? getProxyUrlFromFetch(proxyFetch) : void 0;
278
+ const undiciSourceFetch = resolveWrappedFetch(fetch);
279
+ const sourceFetch = explicitProxyUrl ? undiciSourceFetch : proxyFetch ? resolveWrappedFetch(proxyFetch) : undiciSourceFetch;
280
+ if (proxyFetch && !explicitProxyUrl) return sourceFetch;
281
+ const dnsResultOrder = normalizeDnsResultOrder(dnsDecision.value);
282
+ const useEnvProxy = !explicitProxyUrl && hasEnvHttpProxyForTelegramApi();
283
+ const defaultDispatcherResolution = createTelegramDispatcher({
284
+ autoSelectFamily: autoSelectDecision.value,
285
+ dnsResultOrder,
286
+ useEnvProxy,
287
+ forceIpv4: false,
288
+ proxyUrl: explicitProxyUrl
289
+ });
290
+ const defaultDispatcher = defaultDispatcherResolution.dispatcher;
291
+ const shouldBypassEnvProxy = shouldBypassEnvProxyForTelegramApi();
292
+ const allowStickyIpv4Fallback = defaultDispatcherResolution.mode === "direct" || defaultDispatcherResolution.mode === "env-proxy" && shouldBypassEnvProxy;
293
+ const stickyShouldUseEnvProxy = defaultDispatcherResolution.mode === "env-proxy";
294
+ let stickyIpv4FallbackEnabled = false;
295
+ let stickyIpv4Dispatcher = null;
296
+ const resolveStickyIpv4Dispatcher = () => {
297
+ if (!stickyIpv4Dispatcher) stickyIpv4Dispatcher = createTelegramDispatcher({
298
+ autoSelectFamily: false,
299
+ dnsResultOrder: "ipv4first",
300
+ useEnvProxy: stickyShouldUseEnvProxy,
301
+ forceIpv4: true,
302
+ proxyUrl: explicitProxyUrl
303
+ }).dispatcher;
304
+ return stickyIpv4Dispatcher;
305
+ };
306
+ return (async (input, init) => {
307
+ const callerProvidedDispatcher = Boolean(init?.dispatcher);
308
+ const initialInit = withDispatcherIfMissing(init, stickyIpv4FallbackEnabled ? resolveStickyIpv4Dispatcher() : defaultDispatcher);
309
+ try {
310
+ return await sourceFetch(input, initialInit);
311
+ } catch (err) {
312
+ if (shouldRetryWithIpv4Fallback(err)) {
313
+ if (callerProvidedDispatcher) return sourceFetch(input, init ?? {});
314
+ if (!allowStickyIpv4Fallback) throw err;
315
+ if (!stickyIpv4FallbackEnabled) {
316
+ stickyIpv4FallbackEnabled = true;
317
+ log.warn(`fetch fallback: enabling sticky IPv4-only dispatcher (codes=${formatErrorCodes(err)})`);
318
+ }
319
+ return sourceFetch(input, withDispatcherIfMissing(init, resolveStickyIpv4Dispatcher()));
320
+ }
321
+ throw err;
322
+ }
323
+ });
324
+ }
325
+ //#endregion
326
+ export { isWSLSync as i, isWSL2Sync as n, isWSLEnv as r, resolveTelegramFetch as t };
@@ -0,0 +1,164 @@
1
+ import { $s as createPinnedDispatcher, Qs as closeDispatcher, Xs as hasProxyEnvConfigured, Zs as SsrFBlockedError, rc as resolvePinnedHostnameWithPolicy } from "./config-B9ODwgpz.js";
2
+ import { i as logWarn } from "./logger-D6zRubj0.js";
3
+ import { t as bindAbortRelay } from "./fetch-timeout-D2j7yeE1.js";
4
+ import { EnvHttpProxyAgent } from "undici";
5
+ //#region src/infra/net/fetch-guard.ts
6
+ const GUARDED_FETCH_MODE = {
7
+ STRICT: "strict",
8
+ TRUSTED_ENV_PROXY: "trusted_env_proxy"
9
+ };
10
+ const DEFAULT_MAX_REDIRECTS = 3;
11
+ const CROSS_ORIGIN_REDIRECT_SAFE_HEADERS = new Set([
12
+ "accept",
13
+ "accept-encoding",
14
+ "accept-language",
15
+ "cache-control",
16
+ "content-language",
17
+ "content-type",
18
+ "if-match",
19
+ "if-modified-since",
20
+ "if-none-match",
21
+ "if-unmodified-since",
22
+ "pragma",
23
+ "range",
24
+ "user-agent"
25
+ ]);
26
+ function withStrictGuardedFetchMode(params) {
27
+ return {
28
+ ...params,
29
+ mode: GUARDED_FETCH_MODE.STRICT
30
+ };
31
+ }
32
+ function withTrustedEnvProxyGuardedFetchMode(params) {
33
+ return {
34
+ ...params,
35
+ mode: GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY
36
+ };
37
+ }
38
+ function resolveGuardedFetchMode(params) {
39
+ if (params.mode) return params.mode;
40
+ if (params.proxy === "env" && params.dangerouslyAllowEnvProxyWithoutPinnedDns === true) return GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY;
41
+ return GUARDED_FETCH_MODE.STRICT;
42
+ }
43
+ function isRedirectStatus(status) {
44
+ return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
45
+ }
46
+ function retainSafeHeadersForCrossOriginRedirect(init) {
47
+ if (!init?.headers) return init;
48
+ const incoming = new Headers(init.headers);
49
+ const headers = new Headers();
50
+ for (const [key, value] of incoming.entries()) if (CROSS_ORIGIN_REDIRECT_SAFE_HEADERS.has(key.toLowerCase())) headers.set(key, value);
51
+ return {
52
+ ...init,
53
+ headers
54
+ };
55
+ }
56
+ function buildAbortSignal(params) {
57
+ const { timeoutMs, signal } = params;
58
+ if (!timeoutMs && !signal) return {
59
+ signal: void 0,
60
+ cleanup: () => {}
61
+ };
62
+ if (!timeoutMs) return {
63
+ signal,
64
+ cleanup: () => {}
65
+ };
66
+ const controller = new AbortController();
67
+ const timeoutId = setTimeout(controller.abort.bind(controller), timeoutMs);
68
+ const onAbort = bindAbortRelay(controller);
69
+ if (signal) if (signal.aborted) controller.abort();
70
+ else signal.addEventListener("abort", onAbort, { once: true });
71
+ const cleanup = () => {
72
+ clearTimeout(timeoutId);
73
+ if (signal) signal.removeEventListener("abort", onAbort);
74
+ };
75
+ return {
76
+ signal: controller.signal,
77
+ cleanup
78
+ };
79
+ }
80
+ async function fetchWithSsrFGuard(params) {
81
+ const fetcher = params.fetchImpl ?? globalThis.fetch;
82
+ if (!fetcher) throw new Error("fetch is not available");
83
+ const maxRedirects = typeof params.maxRedirects === "number" && Number.isFinite(params.maxRedirects) ? Math.max(0, Math.floor(params.maxRedirects)) : DEFAULT_MAX_REDIRECTS;
84
+ const mode = resolveGuardedFetchMode(params);
85
+ const { signal, cleanup } = buildAbortSignal({
86
+ timeoutMs: params.timeoutMs,
87
+ signal: params.signal
88
+ });
89
+ let released = false;
90
+ const release = async (dispatcher) => {
91
+ if (released) return;
92
+ released = true;
93
+ cleanup();
94
+ await closeDispatcher(dispatcher ?? void 0);
95
+ };
96
+ const visited = /* @__PURE__ */ new Set();
97
+ let currentUrl = params.url;
98
+ let currentInit = params.init ? { ...params.init } : void 0;
99
+ let redirectCount = 0;
100
+ while (true) {
101
+ let parsedUrl;
102
+ try {
103
+ parsedUrl = new URL(currentUrl);
104
+ } catch {
105
+ await release();
106
+ throw new Error("Invalid URL: must be http or https");
107
+ }
108
+ if (!["http:", "https:"].includes(parsedUrl.protocol)) {
109
+ await release();
110
+ throw new Error("Invalid URL: must be http or https");
111
+ }
112
+ let dispatcher = null;
113
+ try {
114
+ const pinned = await resolvePinnedHostnameWithPolicy(parsedUrl.hostname, {
115
+ lookupFn: params.lookupFn,
116
+ policy: params.policy
117
+ });
118
+ if (mode === GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY && hasProxyEnvConfigured()) dispatcher = new EnvHttpProxyAgent();
119
+ else if (params.pinDns !== false) dispatcher = createPinnedDispatcher(pinned);
120
+ const init = {
121
+ ...currentInit ? { ...currentInit } : {},
122
+ redirect: "manual",
123
+ ...dispatcher ? { dispatcher } : {},
124
+ ...signal ? { signal } : {}
125
+ };
126
+ const response = await fetcher(parsedUrl.toString(), init);
127
+ if (isRedirectStatus(response.status)) {
128
+ const location = response.headers.get("location");
129
+ if (!location) {
130
+ await release(dispatcher);
131
+ throw new Error(`Redirect missing location header (${response.status})`);
132
+ }
133
+ redirectCount += 1;
134
+ if (redirectCount > maxRedirects) {
135
+ await release(dispatcher);
136
+ throw new Error(`Too many redirects (limit: ${maxRedirects})`);
137
+ }
138
+ const nextParsedUrl = new URL(location, parsedUrl);
139
+ const nextUrl = nextParsedUrl.toString();
140
+ if (visited.has(nextUrl)) {
141
+ await release(dispatcher);
142
+ throw new Error("Redirect loop detected");
143
+ }
144
+ if (nextParsedUrl.origin !== parsedUrl.origin) currentInit = retainSafeHeadersForCrossOriginRedirect(currentInit);
145
+ visited.add(nextUrl);
146
+ response.body?.cancel();
147
+ await closeDispatcher(dispatcher);
148
+ currentUrl = nextUrl;
149
+ continue;
150
+ }
151
+ return {
152
+ response,
153
+ finalUrl: currentUrl,
154
+ release: async () => release(dispatcher)
155
+ };
156
+ } catch (err) {
157
+ if (err instanceof SsrFBlockedError) logWarn(`security: blocked URL fetch (${params.auditContext ?? "url-fetch"}) target=${parsedUrl.origin}${parsedUrl.pathname} reason=${err.message}`);
158
+ await release(dispatcher);
159
+ throw err;
160
+ }
161
+ }
162
+ }
163
+ //#endregion
164
+ export { withStrictGuardedFetchMode as n, withTrustedEnvProxyGuardedFetchMode as r, fetchWithSsrFGuard as t };