@openclaw/proxyline 0.1.0 → 0.2.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.
- package/CHANGELOG.md +11 -1
- package/README.md +25 -9
- package/dist/connect.d.ts +1 -0
- package/dist/connect.d.ts.map +1 -1
- package/dist/connect.js +30 -1
- package/dist/env.d.ts +12 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +202 -0
- package/dist/index.d.ts +4 -50
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -521
- package/dist/node-http.d.ts +52 -0
- package/dist/node-http.d.ts.map +1 -0
- package/dist/node-http.js +702 -0
- package/dist/runtime.d.ts +4 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +512 -0
- package/dist/shared.d.ts +1 -0
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +3 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +17 -14
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAGV,eAAe,EACf,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AA6kBpB,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAqF3E;AAED,eAAO,MAAM,kBAAkB,yBAAmB,CAAC"}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import https from "node:https";
|
|
3
|
+
import { Agent as UndiciAgent, Dispatcher, FormData as UndiciFormData, Headers as UndiciHeaders, Request as UndiciRequest, Response as UndiciResponse, errors as undiciErrors, fetch as undiciFetch, getGlobalDispatcher, ProxyAgent as UndiciProxyAgent, setGlobalDispatcher, } from "undici";
|
|
4
|
+
import { createAmbientProxyResolver, EMPTY_PROXY_ENV, resolveAmbientProxyForUrl, readProxyEnv, } from "./env.js";
|
|
5
|
+
import { bindNodeHttpMethod, createDirectNodeAgent, createNodeProxyAgent, } from "./node-http.js";
|
|
6
|
+
import { formatUrl, ProxylineError, redactProxyUrl, resolveProxyTlsCa, } from "./shared.js";
|
|
7
|
+
let activeRuntime;
|
|
8
|
+
// Node's global fetch types come from bundled undici-types, while the runtime
|
|
9
|
+
// implementation intentionally delegates to this package's undici dependency.
|
|
10
|
+
const proxylineHeaders = UndiciHeaders;
|
|
11
|
+
const proxylineRequest = UndiciRequest;
|
|
12
|
+
const proxylineResponse = UndiciResponse;
|
|
13
|
+
const proxylineFormData = UndiciFormData;
|
|
14
|
+
function getRequestDispatcher(request) {
|
|
15
|
+
for (const symbol of Object.getOwnPropertySymbols(request)) {
|
|
16
|
+
if (symbol.description !== "dispatcher") {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
return Reflect.get(request, symbol);
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function isFetchRequestLike(value) {
|
|
24
|
+
if (typeof value !== "object" || value === null) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const record = value;
|
|
28
|
+
return (typeof record.url === "string" &&
|
|
29
|
+
typeof record.method === "string" &&
|
|
30
|
+
typeof record.arrayBuffer === "function" &&
|
|
31
|
+
record.headers !== undefined);
|
|
32
|
+
}
|
|
33
|
+
async function createProxylineRequestFromRequestLike(request, options) {
|
|
34
|
+
const init = {
|
|
35
|
+
headers: request.headers,
|
|
36
|
+
method: request.method,
|
|
37
|
+
};
|
|
38
|
+
if (request.cache !== undefined) {
|
|
39
|
+
init.cache = request.cache;
|
|
40
|
+
}
|
|
41
|
+
if (request.credentials !== undefined) {
|
|
42
|
+
init.credentials = request.credentials;
|
|
43
|
+
}
|
|
44
|
+
if (request.integrity !== undefined) {
|
|
45
|
+
init.integrity = request.integrity;
|
|
46
|
+
}
|
|
47
|
+
if (request.keepalive !== undefined) {
|
|
48
|
+
init.keepalive = request.keepalive;
|
|
49
|
+
}
|
|
50
|
+
if (request.mode !== undefined) {
|
|
51
|
+
init.mode = request.mode;
|
|
52
|
+
}
|
|
53
|
+
if (request.redirect !== undefined) {
|
|
54
|
+
init.redirect = request.redirect;
|
|
55
|
+
}
|
|
56
|
+
if (request.referrer !== undefined) {
|
|
57
|
+
init.referrer = request.referrer;
|
|
58
|
+
}
|
|
59
|
+
if (request.referrerPolicy !== undefined) {
|
|
60
|
+
init.referrerPolicy = request.referrerPolicy;
|
|
61
|
+
}
|
|
62
|
+
if (options.preserveDispatcher) {
|
|
63
|
+
const dispatcher = getRequestDispatcher(request);
|
|
64
|
+
if (dispatcher !== undefined) {
|
|
65
|
+
Reflect.set(init, "dispatcher", dispatcher);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (request.signal !== undefined) {
|
|
69
|
+
init.signal = request.signal;
|
|
70
|
+
}
|
|
71
|
+
if (options.includeBody &&
|
|
72
|
+
request.body !== null &&
|
|
73
|
+
request.method !== "GET" &&
|
|
74
|
+
request.method !== "HEAD") {
|
|
75
|
+
init.body = request.body;
|
|
76
|
+
init.duplex = "half";
|
|
77
|
+
}
|
|
78
|
+
const requestUnknown = Reflect.construct(proxylineRequest, [request.url, init]);
|
|
79
|
+
if (!(requestUnknown instanceof proxylineRequest)) {
|
|
80
|
+
throw new TypeError("Proxyline failed to normalize a fetch Request.");
|
|
81
|
+
}
|
|
82
|
+
return requestUnknown;
|
|
83
|
+
}
|
|
84
|
+
function requestInitOverridesBody(init) {
|
|
85
|
+
if (typeof init !== "object" || init === null) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return "body" in init;
|
|
89
|
+
}
|
|
90
|
+
async function normalizeFetchInput(input, init, options) {
|
|
91
|
+
if ((input instanceof proxylineRequest && options.preserveDispatcher) || !isFetchRequestLike(input)) {
|
|
92
|
+
return input;
|
|
93
|
+
}
|
|
94
|
+
return await createProxylineRequestFromRequestLike(input, {
|
|
95
|
+
includeBody: !requestInitOverridesBody(init),
|
|
96
|
+
preserveDispatcher: options.preserveDispatcher,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function stripFetchDispatcher(init) {
|
|
100
|
+
if (typeof init !== "object" || init === null) {
|
|
101
|
+
return init;
|
|
102
|
+
}
|
|
103
|
+
const sanitized = Object.create(init);
|
|
104
|
+
Reflect.defineProperty(sanitized, "dispatcher", {
|
|
105
|
+
configurable: true,
|
|
106
|
+
enumerable: true,
|
|
107
|
+
value: undefined,
|
|
108
|
+
writable: true,
|
|
109
|
+
});
|
|
110
|
+
return sanitized;
|
|
111
|
+
}
|
|
112
|
+
const proxylineFetch = async (input, init) => {
|
|
113
|
+
const managedMode = activeRuntime?.mode === "managed";
|
|
114
|
+
const normalizedInput = await normalizeFetchInput(input, init, {
|
|
115
|
+
preserveDispatcher: !managedMode,
|
|
116
|
+
});
|
|
117
|
+
const normalizedInit = managedMode ? stripFetchDispatcher(init) : init;
|
|
118
|
+
const response = await Reflect.apply(undiciFetch, undefined, normalizedInit === undefined ? [normalizedInput] : [normalizedInput, normalizedInit]);
|
|
119
|
+
if (!(response instanceof proxylineResponse)) {
|
|
120
|
+
throw new TypeError("Proxyline fetch returned a non-Response value.");
|
|
121
|
+
}
|
|
122
|
+
return response;
|
|
123
|
+
};
|
|
124
|
+
function normalizeProxyUrl(value) {
|
|
125
|
+
if (value === undefined) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const url = value instanceof URL ? new URL(value.href) : new URL(value);
|
|
129
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
130
|
+
throw new ProxylineError("UNSUPPORTED_PROXY_PROTOCOL", `Proxyline only supports http:// and https:// proxy endpoints in this slice: ${url.protocol}`);
|
|
131
|
+
}
|
|
132
|
+
return url;
|
|
133
|
+
}
|
|
134
|
+
function emit(onEvent, event) {
|
|
135
|
+
onEvent?.(event);
|
|
136
|
+
}
|
|
137
|
+
function isProxyableUrlProtocol(protocol) {
|
|
138
|
+
return protocol === "http:" ||
|
|
139
|
+
protocol === "https:" ||
|
|
140
|
+
protocol === "ws:" ||
|
|
141
|
+
protocol === "wss:";
|
|
142
|
+
}
|
|
143
|
+
function shouldBypassManagedProxy(bypassPolicy, url, surface) {
|
|
144
|
+
if (bypassPolicy === undefined) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return bypassPolicy({ surface, url: formatUrl(url) });
|
|
148
|
+
}
|
|
149
|
+
function createManagedProxyResolver(proxyUrl, bypassPolicy) {
|
|
150
|
+
const redactedProxyUrl = redactProxyUrl(proxyUrl);
|
|
151
|
+
return {
|
|
152
|
+
active: true,
|
|
153
|
+
describeProxy: () => redactedProxyUrl,
|
|
154
|
+
explain: (url, surface) => {
|
|
155
|
+
const formattedUrl = formatUrl(url);
|
|
156
|
+
if (!isProxyableUrlProtocol(new URL(url).protocol)) {
|
|
157
|
+
return {
|
|
158
|
+
kind: "direct",
|
|
159
|
+
reason: "managed-proxy-unsupported-url-scheme",
|
|
160
|
+
surface,
|
|
161
|
+
url: formattedUrl,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (shouldBypassManagedProxy(bypassPolicy, url, surface)) {
|
|
165
|
+
return {
|
|
166
|
+
kind: "direct",
|
|
167
|
+
reason: "managed-proxy-bypass-policy",
|
|
168
|
+
surface,
|
|
169
|
+
url: formattedUrl,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
kind: "proxied",
|
|
174
|
+
reason: "managed-proxy-active",
|
|
175
|
+
surface,
|
|
176
|
+
url: formattedUrl,
|
|
177
|
+
proxyUrl: redactedProxyUrl,
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
getProxyForUrl: (url) => {
|
|
181
|
+
const protocol = new URL(url).protocol;
|
|
182
|
+
return isProxyableUrlProtocol(protocol) &&
|
|
183
|
+
!shouldBypassManagedProxy(bypassPolicy, url, "unknown")
|
|
184
|
+
? proxyUrl.href
|
|
185
|
+
: "";
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function createUndiciProxyDispatcher(options, proxyCa) {
|
|
190
|
+
if (options.mode === "ambient") {
|
|
191
|
+
if (!options.active) {
|
|
192
|
+
return new UndiciAgent();
|
|
193
|
+
}
|
|
194
|
+
return new AmbientUndiciDispatcher(options.env, proxyCa);
|
|
195
|
+
}
|
|
196
|
+
return new ManagedUndiciDispatcher(options.resolver, proxyCa);
|
|
197
|
+
}
|
|
198
|
+
class ManagedUndiciDispatcher extends Dispatcher {
|
|
199
|
+
#directDispatcher = new UndiciAgent();
|
|
200
|
+
#proxyCa;
|
|
201
|
+
#proxyDispatchers = new Map();
|
|
202
|
+
#resolver;
|
|
203
|
+
#closedError;
|
|
204
|
+
constructor(resolver, proxyCa) {
|
|
205
|
+
super();
|
|
206
|
+
this.#resolver = resolver;
|
|
207
|
+
this.#proxyCa = proxyCa;
|
|
208
|
+
}
|
|
209
|
+
dispatch(options, handler) {
|
|
210
|
+
if (this.#closedError !== undefined) {
|
|
211
|
+
if (handler.onError === undefined) {
|
|
212
|
+
throw this.#closedError;
|
|
213
|
+
}
|
|
214
|
+
handler.onError(this.#closedError);
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const url = resolveUndiciDispatchUrl(options);
|
|
218
|
+
const proxyUrl = url === undefined ? "" : this.#resolver.getProxyForUrl(url);
|
|
219
|
+
const dispatcher = proxyUrl === "" ? this.#directDispatcher : this.#proxyDispatcher(proxyUrl);
|
|
220
|
+
return dispatcher.dispatch(options, handler);
|
|
221
|
+
}
|
|
222
|
+
close(callback) {
|
|
223
|
+
const closing = this.#closeAll();
|
|
224
|
+
if (callback === undefined) {
|
|
225
|
+
return closing;
|
|
226
|
+
}
|
|
227
|
+
closing.then(callback, callback);
|
|
228
|
+
}
|
|
229
|
+
destroy(errorOrCallback, callback) {
|
|
230
|
+
const error = typeof errorOrCallback === "function" ? null : errorOrCallback ?? null;
|
|
231
|
+
const destroyCallback = typeof errorOrCallback === "function" ? errorOrCallback : callback;
|
|
232
|
+
const destroying = this.#destroyAll(error);
|
|
233
|
+
if (destroyCallback === undefined) {
|
|
234
|
+
return destroying;
|
|
235
|
+
}
|
|
236
|
+
destroying.then(destroyCallback, destroyCallback);
|
|
237
|
+
}
|
|
238
|
+
#proxyDispatcher(proxyUrl) {
|
|
239
|
+
const existing = this.#proxyDispatchers.get(proxyUrl);
|
|
240
|
+
if (existing !== undefined) {
|
|
241
|
+
return existing;
|
|
242
|
+
}
|
|
243
|
+
const dispatcher = new UndiciProxyAgent({
|
|
244
|
+
uri: proxyUrl,
|
|
245
|
+
...(this.#proxyCa !== undefined ? { proxyTls: { ca: this.#proxyCa } } : {}),
|
|
246
|
+
});
|
|
247
|
+
this.#proxyDispatchers.set(proxyUrl, dispatcher);
|
|
248
|
+
return dispatcher;
|
|
249
|
+
}
|
|
250
|
+
async #closeAll() {
|
|
251
|
+
this.#closedError ??= new undiciErrors.ClientClosedError();
|
|
252
|
+
const proxyDispatchers = [...this.#proxyDispatchers.values()];
|
|
253
|
+
this.#proxyDispatchers.clear();
|
|
254
|
+
await Promise.all([
|
|
255
|
+
this.#directDispatcher.close(),
|
|
256
|
+
...proxyDispatchers.map((dispatcher) => dispatcher.close()),
|
|
257
|
+
]);
|
|
258
|
+
}
|
|
259
|
+
async #destroyAll(error) {
|
|
260
|
+
this.#closedError ??= error ?? new undiciErrors.ClientDestroyedError();
|
|
261
|
+
const proxyDispatchers = [...this.#proxyDispatchers.values()];
|
|
262
|
+
this.#proxyDispatchers.clear();
|
|
263
|
+
await Promise.all([
|
|
264
|
+
this.#directDispatcher.destroy(error),
|
|
265
|
+
...proxyDispatchers.map((dispatcher) => dispatcher.destroy(error)),
|
|
266
|
+
]);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
class AmbientUndiciDispatcher extends Dispatcher {
|
|
270
|
+
#directDispatcher = new UndiciAgent();
|
|
271
|
+
#env;
|
|
272
|
+
#proxyCa;
|
|
273
|
+
#proxyDispatchers = new Map();
|
|
274
|
+
#closedError;
|
|
275
|
+
constructor(env, proxyCa) {
|
|
276
|
+
super();
|
|
277
|
+
this.#env = env;
|
|
278
|
+
this.#proxyCa = proxyCa;
|
|
279
|
+
}
|
|
280
|
+
dispatch(options, handler) {
|
|
281
|
+
if (this.#closedError !== undefined) {
|
|
282
|
+
if (handler.onError === undefined) {
|
|
283
|
+
throw this.#closedError;
|
|
284
|
+
}
|
|
285
|
+
handler.onError(this.#closedError);
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
const url = resolveUndiciDispatchUrl(options);
|
|
289
|
+
const proxyUrl = url === undefined ? undefined : resolveAmbientProxyForUrl(url, this.#env);
|
|
290
|
+
const dispatcher = proxyUrl === undefined ? this.#directDispatcher : this.#proxyDispatcher(proxyUrl);
|
|
291
|
+
return dispatcher.dispatch(options, handler);
|
|
292
|
+
}
|
|
293
|
+
close(callback) {
|
|
294
|
+
const closing = this.#closeAll();
|
|
295
|
+
if (callback === undefined) {
|
|
296
|
+
return closing;
|
|
297
|
+
}
|
|
298
|
+
closing.then(callback, callback);
|
|
299
|
+
}
|
|
300
|
+
destroy(errorOrCallback, callback) {
|
|
301
|
+
const error = typeof errorOrCallback === "function" ? null : errorOrCallback ?? null;
|
|
302
|
+
const destroyCallback = typeof errorOrCallback === "function" ? errorOrCallback : callback;
|
|
303
|
+
const destroying = this.#destroyAll(error);
|
|
304
|
+
if (destroyCallback === undefined) {
|
|
305
|
+
return destroying;
|
|
306
|
+
}
|
|
307
|
+
destroying.then(destroyCallback, destroyCallback);
|
|
308
|
+
}
|
|
309
|
+
#proxyDispatcher(proxyUrl) {
|
|
310
|
+
const existing = this.#proxyDispatchers.get(proxyUrl);
|
|
311
|
+
if (existing !== undefined) {
|
|
312
|
+
return existing;
|
|
313
|
+
}
|
|
314
|
+
const dispatcher = new UndiciProxyAgent({
|
|
315
|
+
uri: proxyUrl,
|
|
316
|
+
...(this.#proxyCa !== undefined ? { proxyTls: { ca: this.#proxyCa } } : {}),
|
|
317
|
+
});
|
|
318
|
+
this.#proxyDispatchers.set(proxyUrl, dispatcher);
|
|
319
|
+
return dispatcher;
|
|
320
|
+
}
|
|
321
|
+
async #closeAll() {
|
|
322
|
+
this.#closedError ??= new undiciErrors.ClientClosedError();
|
|
323
|
+
const proxyDispatchers = [...this.#proxyDispatchers.values()];
|
|
324
|
+
this.#proxyDispatchers.clear();
|
|
325
|
+
await Promise.all([
|
|
326
|
+
this.#directDispatcher.close(),
|
|
327
|
+
...proxyDispatchers.map((dispatcher) => dispatcher.close()),
|
|
328
|
+
]);
|
|
329
|
+
}
|
|
330
|
+
async #destroyAll(error) {
|
|
331
|
+
this.#closedError ??= error ?? new undiciErrors.ClientDestroyedError();
|
|
332
|
+
const proxyDispatchers = [...this.#proxyDispatchers.values()];
|
|
333
|
+
this.#proxyDispatchers.clear();
|
|
334
|
+
await Promise.all([
|
|
335
|
+
this.#directDispatcher.destroy(error),
|
|
336
|
+
...proxyDispatchers.map((dispatcher) => dispatcher.destroy(error)),
|
|
337
|
+
]);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function resolveUndiciDispatchUrl(options) {
|
|
341
|
+
if (options.origin !== undefined) {
|
|
342
|
+
const origin = options.origin.toString().replace(/\/$/, "");
|
|
343
|
+
const path = options.path.startsWith("/") ? options.path : `/${options.path}`;
|
|
344
|
+
return new URL(`${origin}${path}`).href;
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
return new URL(options.path).href;
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function restoreNodeHttpSnapshot(snapshot) {
|
|
354
|
+
http.request = snapshot.httpRequest;
|
|
355
|
+
http.get = snapshot.httpGet;
|
|
356
|
+
http.globalAgent = snapshot.httpGlobalAgent;
|
|
357
|
+
https.request = snapshot.httpsRequest;
|
|
358
|
+
https.get = snapshot.httpsGet;
|
|
359
|
+
https.globalAgent = snapshot.httpsGlobalAgent;
|
|
360
|
+
}
|
|
361
|
+
function installRuntime(resolver, dispatcherOptions, proxyCa) {
|
|
362
|
+
if (activeRuntime !== undefined) {
|
|
363
|
+
throw new ProxylineError("RUNTIME_ALREADY_ACTIVE", "Proxyline already has an active runtime.");
|
|
364
|
+
}
|
|
365
|
+
const snapshot = {
|
|
366
|
+
httpRequest: http.request,
|
|
367
|
+
httpGet: http.get,
|
|
368
|
+
httpGlobalAgent: http.globalAgent,
|
|
369
|
+
httpsRequest: https.request,
|
|
370
|
+
httpsGet: https.get,
|
|
371
|
+
httpsGlobalAgent: https.globalAgent,
|
|
372
|
+
};
|
|
373
|
+
const nodeHttpAgent = createNodeProxyAgent(resolver, proxyCa, "http");
|
|
374
|
+
const nodeHttpsAgent = createNodeProxyAgent(resolver, proxyCa, "https");
|
|
375
|
+
const originalDispatcher = getGlobalDispatcher();
|
|
376
|
+
const originalFetch = globalThis.fetch;
|
|
377
|
+
const originalFormData = globalThis.FormData;
|
|
378
|
+
const originalHeaders = globalThis.Headers;
|
|
379
|
+
const originalRequest = globalThis.Request;
|
|
380
|
+
const originalResponse = globalThis.Response;
|
|
381
|
+
const installedDispatcher = createUndiciProxyDispatcher(dispatcherOptions, proxyCa);
|
|
382
|
+
const runtime = {
|
|
383
|
+
installedDispatcher,
|
|
384
|
+
mode: dispatcherOptions.mode,
|
|
385
|
+
nodeHttpAgent,
|
|
386
|
+
nodeHttpsAgent,
|
|
387
|
+
originalDispatcher,
|
|
388
|
+
originalFetch,
|
|
389
|
+
originalFormData,
|
|
390
|
+
originalHeaders,
|
|
391
|
+
originalRequest,
|
|
392
|
+
originalResponse,
|
|
393
|
+
snapshot,
|
|
394
|
+
};
|
|
395
|
+
activeRuntime = runtime;
|
|
396
|
+
try {
|
|
397
|
+
http.globalAgent = nodeHttpAgent;
|
|
398
|
+
https.globalAgent = nodeHttpsAgent;
|
|
399
|
+
http.request = bindNodeHttpMethod(snapshot.httpRequest, () => createNodeProxyAgent(resolver, proxyCa, "http"));
|
|
400
|
+
http.get = bindNodeHttpMethod(snapshot.httpGet, () => createNodeProxyAgent(resolver, proxyCa, "http"));
|
|
401
|
+
https.request = bindNodeHttpMethod(snapshot.httpsRequest, () => createNodeProxyAgent(resolver, proxyCa, "https"));
|
|
402
|
+
https.get = bindNodeHttpMethod(snapshot.httpsGet, () => createNodeProxyAgent(resolver, proxyCa, "https"));
|
|
403
|
+
setGlobalDispatcher(installedDispatcher);
|
|
404
|
+
globalThis.fetch = proxylineFetch;
|
|
405
|
+
globalThis.FormData = proxylineFormData;
|
|
406
|
+
globalThis.Headers = proxylineHeaders;
|
|
407
|
+
globalThis.Request = proxylineRequest;
|
|
408
|
+
globalThis.Response = proxylineResponse;
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
restoreNodeHttpSnapshot(snapshot);
|
|
412
|
+
setGlobalDispatcher(originalDispatcher);
|
|
413
|
+
globalThis.fetch = originalFetch;
|
|
414
|
+
globalThis.FormData = originalFormData;
|
|
415
|
+
globalThis.Headers = originalHeaders;
|
|
416
|
+
globalThis.Request = originalRequest;
|
|
417
|
+
globalThis.Response = originalResponse;
|
|
418
|
+
activeRuntime = undefined;
|
|
419
|
+
void installedDispatcher.destroy();
|
|
420
|
+
nodeHttpAgent.destroy();
|
|
421
|
+
nodeHttpsAgent.destroy();
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
return runtime;
|
|
425
|
+
}
|
|
426
|
+
function stopRuntime(runtime) {
|
|
427
|
+
if (activeRuntime !== runtime) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
restoreNodeHttpSnapshot(runtime.snapshot);
|
|
431
|
+
setGlobalDispatcher(runtime.originalDispatcher);
|
|
432
|
+
globalThis.fetch = runtime.originalFetch;
|
|
433
|
+
globalThis.FormData = runtime.originalFormData;
|
|
434
|
+
globalThis.Headers = runtime.originalHeaders;
|
|
435
|
+
globalThis.Request = runtime.originalRequest;
|
|
436
|
+
globalThis.Response = runtime.originalResponse;
|
|
437
|
+
void runtime.installedDispatcher.destroy();
|
|
438
|
+
runtime.nodeHttpAgent.destroy();
|
|
439
|
+
runtime.nodeHttpsAgent.destroy();
|
|
440
|
+
activeRuntime = undefined;
|
|
441
|
+
}
|
|
442
|
+
export function installProxyline(options) {
|
|
443
|
+
const proxyUrl = options.mode === "managed" ? normalizeProxyUrl(options.proxyUrl) : undefined;
|
|
444
|
+
if (options.mode === "managed" && proxyUrl === undefined) {
|
|
445
|
+
throw new ProxylineError("MANAGED_PROXY_URL_REQUIRED", "Proxyline managed mode requires an explicit proxyUrl.");
|
|
446
|
+
}
|
|
447
|
+
let stopped = false;
|
|
448
|
+
const proxyCa = resolveProxyTlsCa(options.proxyTls);
|
|
449
|
+
const ambientEnv = proxyUrl === undefined ? readProxyEnv() : undefined;
|
|
450
|
+
const resolver = proxyUrl !== undefined
|
|
451
|
+
? createManagedProxyResolver(proxyUrl, options.bypassPolicy)
|
|
452
|
+
: createAmbientProxyResolver(ambientEnv ?? EMPTY_PROXY_ENV);
|
|
453
|
+
const redactedProxyUrl = resolver.describeProxy();
|
|
454
|
+
const hasActiveProxy = resolver.active;
|
|
455
|
+
const runtime = hasActiveProxy
|
|
456
|
+
? installRuntime(resolver, proxyUrl !== undefined
|
|
457
|
+
? { mode: "managed", resolver }
|
|
458
|
+
: { mode: "ambient", env: ambientEnv ?? EMPTY_PROXY_ENV, active: hasActiveProxy }, proxyCa)
|
|
459
|
+
: undefined;
|
|
460
|
+
emit(options.onEvent, {
|
|
461
|
+
type: "runtime.installed",
|
|
462
|
+
mode: options.mode,
|
|
463
|
+
active: hasActiveProxy,
|
|
464
|
+
...(redactedProxyUrl ? { proxyUrl: redactedProxyUrl } : {}),
|
|
465
|
+
});
|
|
466
|
+
const handle = {
|
|
467
|
+
mode: options.mode,
|
|
468
|
+
active: hasActiveProxy,
|
|
469
|
+
...(redactedProxyUrl ? { proxyUrl: redactedProxyUrl } : {}),
|
|
470
|
+
createNodeAgent: () => {
|
|
471
|
+
if (!hasActiveProxy || stopped) {
|
|
472
|
+
return createDirectNodeAgent();
|
|
473
|
+
}
|
|
474
|
+
return createNodeProxyAgent(resolver, proxyCa);
|
|
475
|
+
},
|
|
476
|
+
createUndiciDispatcher: () => stopped
|
|
477
|
+
? new UndiciAgent()
|
|
478
|
+
: createUndiciProxyDispatcher(proxyUrl !== undefined
|
|
479
|
+
? { mode: "managed", resolver }
|
|
480
|
+
: { mode: "ambient", env: ambientEnv ?? EMPTY_PROXY_ENV, active: hasActiveProxy }, proxyCa),
|
|
481
|
+
createWebSocketAgent: () => {
|
|
482
|
+
if (!hasActiveProxy || stopped) {
|
|
483
|
+
return createDirectNodeAgent();
|
|
484
|
+
}
|
|
485
|
+
return createNodeProxyAgent(resolver, proxyCa);
|
|
486
|
+
},
|
|
487
|
+
explain: (url, explainOptions) => {
|
|
488
|
+
const decision = stopped
|
|
489
|
+
? {
|
|
490
|
+
kind: "direct",
|
|
491
|
+
reason: "runtime-stopped",
|
|
492
|
+
surface: explainOptions?.surface ?? "unknown",
|
|
493
|
+
url: formatUrl(url),
|
|
494
|
+
}
|
|
495
|
+
: resolver.explain(url, explainOptions?.surface ?? "unknown");
|
|
496
|
+
emit(options.onEvent, { type: "decision", decision });
|
|
497
|
+
return decision;
|
|
498
|
+
},
|
|
499
|
+
stop: () => {
|
|
500
|
+
if (stopped) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
stopped = true;
|
|
504
|
+
if (runtime !== undefined) {
|
|
505
|
+
stopRuntime(runtime);
|
|
506
|
+
}
|
|
507
|
+
emit(options.onEvent, { type: "runtime.stopped", mode: options.mode });
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
return handle;
|
|
511
|
+
}
|
|
512
|
+
export const installGlobalProxy = installProxyline;
|
package/dist/shared.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export declare class ProxylineError extends Error {
|
|
|
7
7
|
constructor(code: string, message: string);
|
|
8
8
|
}
|
|
9
9
|
export declare function resolveProxyTlsCa(options: ProxylineTlsOptions | undefined): string | undefined;
|
|
10
|
+
export declare function formatUrl(value: string | URL): string;
|
|
10
11
|
export declare function redactProxyUrl(value: string | URL): string;
|
|
11
12
|
//# sourceMappingURL=shared.d.ts.map
|
package/dist/shared.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAKjD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,mBAAmB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAW9F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,MAAM,CAO1D"}
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACzC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAEH,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAKjD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,mBAAmB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAW9F;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,MAAM,CAErD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,MAAM,CAO1D"}
|
package/dist/shared.js
CHANGED
|
@@ -19,6 +19,9 @@ export function resolveProxyTlsCa(options) {
|
|
|
19
19
|
}
|
|
20
20
|
return undefined;
|
|
21
21
|
}
|
|
22
|
+
export function formatUrl(value) {
|
|
23
|
+
return value instanceof URL ? value.href : new URL(value).href;
|
|
24
|
+
}
|
|
22
25
|
export function redactProxyUrl(value) {
|
|
23
26
|
const url = value instanceof URL ? new URL(value.href) : new URL(value);
|
|
24
27
|
url.username = "";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Agent as HttpAgent } from "node:http";
|
|
2
|
+
import type { Dispatcher } from "undici";
|
|
3
|
+
import type { ProxylineTlsOptions } from "./shared.js";
|
|
4
|
+
export type ProxylineMode = "managed" | "ambient";
|
|
5
|
+
export type ProxylineSurface = "node-http" | "node-https" | "undici" | "websocket" | "connect" | "unknown";
|
|
6
|
+
export type ProxylineOptions = Readonly<{
|
|
7
|
+
mode: ProxylineMode;
|
|
8
|
+
proxyUrl?: string | URL;
|
|
9
|
+
proxyTls?: ProxylineTlsOptions;
|
|
10
|
+
bypassPolicy?: ProxylineBypassPolicy;
|
|
11
|
+
onEvent?: (event: ProxylineEvent) => void;
|
|
12
|
+
}>;
|
|
13
|
+
export type ProxylineDecision = Readonly<{
|
|
14
|
+
kind: "proxied" | "direct" | "blocked";
|
|
15
|
+
reason: string;
|
|
16
|
+
surface: ProxylineSurface;
|
|
17
|
+
url: string;
|
|
18
|
+
proxyUrl?: string;
|
|
19
|
+
}>;
|
|
20
|
+
export type ProxylineBypassRequest = Readonly<{
|
|
21
|
+
surface: ProxylineSurface;
|
|
22
|
+
url: string;
|
|
23
|
+
}>;
|
|
24
|
+
export type ProxylineBypassPolicy = (request: ProxylineBypassRequest) => boolean;
|
|
25
|
+
export type ProxylineEvent = Readonly<{
|
|
26
|
+
type: "runtime.installed";
|
|
27
|
+
mode: ProxylineMode;
|
|
28
|
+
active: boolean;
|
|
29
|
+
proxyUrl?: string;
|
|
30
|
+
}> | Readonly<{
|
|
31
|
+
type: "runtime.stopped";
|
|
32
|
+
mode: ProxylineMode;
|
|
33
|
+
}> | Readonly<{
|
|
34
|
+
type: "decision";
|
|
35
|
+
decision: ProxylineDecision;
|
|
36
|
+
}> | Readonly<{
|
|
37
|
+
type: "warning";
|
|
38
|
+
code: string;
|
|
39
|
+
message: string;
|
|
40
|
+
}>;
|
|
41
|
+
export type ExplainOptions = Readonly<{
|
|
42
|
+
surface?: ProxylineSurface;
|
|
43
|
+
}>;
|
|
44
|
+
export type ProxylineHandle = Readonly<{
|
|
45
|
+
mode: ProxylineMode;
|
|
46
|
+
active: boolean;
|
|
47
|
+
proxyUrl?: string;
|
|
48
|
+
createNodeAgent: () => HttpAgent;
|
|
49
|
+
createUndiciDispatcher: () => Dispatcher;
|
|
50
|
+
createWebSocketAgent: () => HttpAgent;
|
|
51
|
+
explain: (url: string | URL, options?: ExplainOptions) => ProxylineDecision;
|
|
52
|
+
stop: () => void;
|
|
53
|
+
}>;
|
|
54
|
+
export type ProxyResolver = Readonly<{
|
|
55
|
+
active: boolean;
|
|
56
|
+
describeProxy: () => string | undefined;
|
|
57
|
+
explain: (url: string | URL, surface: ProxylineSurface) => ProxylineDecision;
|
|
58
|
+
getProxyForUrl: (url: string) => string;
|
|
59
|
+
}>;
|
|
60
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/proxyline",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Process-global proxy routing for Node.js.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"url": "https://github.com/openclaw/proxyline/issues"
|
|
18
18
|
},
|
|
19
19
|
"homepage": "https://proxyline.dev",
|
|
20
|
+
"packageManager": "pnpm@10.33.2",
|
|
20
21
|
"exports": {
|
|
21
22
|
".": {
|
|
22
23
|
"types": "./dist/index.d.ts",
|
|
@@ -31,27 +32,29 @@
|
|
|
31
32
|
"README.md",
|
|
32
33
|
"LICENSE"
|
|
33
34
|
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.build.json",
|
|
37
|
+
"check": "pnpm build && tsc --noEmit && pnpm coverage:run",
|
|
38
|
+
"coverage": "pnpm build && pnpm coverage:run",
|
|
39
|
+
"coverage:run": "node scripts/run-coverage.mjs",
|
|
40
|
+
"docs:build": "node scripts/build-docs-site.mjs",
|
|
41
|
+
"package:dry-run": "pnpm pack --dry-run",
|
|
42
|
+
"prepack": "node scripts/prepack-build.mjs",
|
|
43
|
+
"publish:dry-run": "pnpm publish --dry-run --access public",
|
|
44
|
+
"test": "pnpm build && tsx --test test/index.test.ts test/e2e.test.ts test/package.test.ts",
|
|
45
|
+
"typecheck": "pnpm build && tsc --noEmit"
|
|
46
|
+
},
|
|
34
47
|
"devDependencies": {
|
|
35
|
-
"@types/node": "^20.19.
|
|
48
|
+
"@types/node": "^20.19.40",
|
|
36
49
|
"@types/ws": "^8.18.1",
|
|
37
50
|
"tsx": "^4.21.0",
|
|
38
51
|
"typescript": "^5.9.3",
|
|
39
52
|
"ws": "^8.20.0"
|
|
40
53
|
},
|
|
41
54
|
"engines": {
|
|
42
|
-
"node": ">=20"
|
|
55
|
+
"node": ">=20.18.1"
|
|
43
56
|
},
|
|
44
57
|
"dependencies": {
|
|
45
|
-
"proxy-agent": "^8.0.1",
|
|
46
58
|
"undici": "^7.25.0"
|
|
47
|
-
},
|
|
48
|
-
"scripts": {
|
|
49
|
-
"build": "tsc -p tsconfig.build.json",
|
|
50
|
-
"check": "pnpm typecheck && pnpm test && pnpm build",
|
|
51
|
-
"docs:build": "node scripts/build-docs-site.mjs",
|
|
52
|
-
"package:dry-run": "pnpm pack --dry-run",
|
|
53
|
-
"publish:dry-run": "pnpm publish --dry-run --access public",
|
|
54
|
-
"test": "tsx --test test/index.test.ts test/e2e.test.ts",
|
|
55
|
-
"typecheck": "tsc --noEmit"
|
|
56
59
|
}
|
|
57
|
-
}
|
|
60
|
+
}
|