@hraness/kb 0.17.1

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 (200) hide show
  1. package/DISCLOSURE +43 -0
  2. package/LICENSE +21 -0
  3. package/README.md +545 -0
  4. package/dist/agent-context.js +35 -0
  5. package/dist/agent-guide-audit.js +18 -0
  6. package/dist/attachments.js +19 -0
  7. package/dist/authoring.js +33 -0
  8. package/dist/benchmark.js +19 -0
  9. package/dist/capture.js +49 -0
  10. package/dist/cli.js +3646 -0
  11. package/dist/clip/acquire.js +57 -0
  12. package/dist/clip/args.js +21 -0
  13. package/dist/clip/bounded-byte-buffer.js +9 -0
  14. package/dist/clip/browser-profiles.js +17 -0
  15. package/dist/clip/bundle-reader.js +11 -0
  16. package/dist/clip/cli.js +27 -0
  17. package/dist/clip/cookies.js +21 -0
  18. package/dist/clip/defuddle-worker.js +84 -0
  19. package/dist/clip/doctor.js +32 -0
  20. package/dist/clip/jobs.js +784 -0
  21. package/dist/clip/network-proxy.js +9 -0
  22. package/dist/clip/network.js +28 -0
  23. package/dist/clip/persist.js +30 -0
  24. package/dist/clip/refresh.js +11 -0
  25. package/dist/clip/terminal.js +11 -0
  26. package/dist/evaluation-builder.js +12238 -0
  27. package/dist/evaluation-kb.js +26 -0
  28. package/dist/evaluation.js +35 -0
  29. package/dist/git.js +39 -0
  30. package/dist/graph.js +39 -0
  31. package/dist/index-01jj6rbv.js +1103 -0
  32. package/dist/index-06c9ctr6.js +738 -0
  33. package/dist/index-0kavxzqj.js +2480 -0
  34. package/dist/index-1gwbassd.js +743 -0
  35. package/dist/index-1n418kb9.js +827 -0
  36. package/dist/index-1vrd1rmn.js +56 -0
  37. package/dist/index-1xxnjn0d.js +171 -0
  38. package/dist/index-3rm7cz6h.js +373 -0
  39. package/dist/index-3v2z4f0q.js +328 -0
  40. package/dist/index-48pz4jpc.js +392 -0
  41. package/dist/index-4j3tt0c3.js +234 -0
  42. package/dist/index-5n05se68.js +307 -0
  43. package/dist/index-5vwpzb5a.js +764 -0
  44. package/dist/index-6g2pv9d2.js +370 -0
  45. package/dist/index-7fzc32gf.js +712 -0
  46. package/dist/index-84x0vjjp.js +432 -0
  47. package/dist/index-adx6khj5.js +488 -0
  48. package/dist/index-b88v3vtm.js +805 -0
  49. package/dist/index-cv6fh7z5.js +462 -0
  50. package/dist/index-cxfrakt7.js +1067 -0
  51. package/dist/index-d13v9ckt.js +150 -0
  52. package/dist/index-dycjbhw2.js +1943 -0
  53. package/dist/index-dyqwejk5.js +531 -0
  54. package/dist/index-e5fbsywq.js +658 -0
  55. package/dist/index-f984hw45.js +927 -0
  56. package/dist/index-gh719d91.js +63 -0
  57. package/dist/index-hgve9rh2.js +2046 -0
  58. package/dist/index-hya40gb2.js +474 -0
  59. package/dist/index-j4zgmzjr.js +152 -0
  60. package/dist/index-jsmvyyvf.js +1166 -0
  61. package/dist/index-kh0pwvdd.js +1974 -0
  62. package/dist/index-mqx4nd6v.js +119 -0
  63. package/dist/index-mxxxytys.js +1254 -0
  64. package/dist/index-n5dd7r0v.js +711 -0
  65. package/dist/index-npg9z1a4.js +431 -0
  66. package/dist/index-pj501bh1.js +140 -0
  67. package/dist/index-qry4vhxk.js +1041 -0
  68. package/dist/index-s2gw5aw9.js +199 -0
  69. package/dist/index-vvdj6kca.js +70 -0
  70. package/dist/index-w2zc0vwa.js +571 -0
  71. package/dist/index-x3fthpsc.js +585 -0
  72. package/dist/index-zr53sf63.js +44 -0
  73. package/dist/index-zxdy5pby.js +2728 -0
  74. package/dist/index-zzhgcwyt.js +966 -0
  75. package/dist/index.js +453 -0
  76. package/dist/navigation.js +13 -0
  77. package/dist/pdf.js +85 -0
  78. package/dist/percolate.js +24 -0
  79. package/dist/portfolio.js +77 -0
  80. package/dist/query.js +29 -0
  81. package/dist/repository-memory.js +60 -0
  82. package/dist/sdk.js +36 -0
  83. package/dist/search-rules.js +34 -0
  84. package/dist/search.js +22 -0
  85. package/dist/semantic.js +39 -0
  86. package/dist/source-inbox.js +17 -0
  87. package/dist/untrusted-content.js +32 -0
  88. package/dist/url-intelligence.js +74 -0
  89. package/dist/workflow.js +23 -0
  90. package/dist/workflows/decision-context.js +20 -0
  91. package/dist/workflows/explain-change.js +8 -0
  92. package/dist/workflows/index.js +28 -0
  93. package/dist/workflows/plan-radar.js +10 -0
  94. package/package.json +409 -0
  95. package/skills/kb/AGENTS.md +19 -0
  96. package/skills/kb/SKILL.md +82 -0
  97. package/skills/kb/agents/openai.yaml +4 -0
  98. package/skills/kb/references/pdf-review.md +51 -0
  99. package/skills/kb/references/percolate.md +144 -0
  100. package/skills/kb/references/plan-structure.md +105 -0
  101. package/skills/kb/references/plan.md +115 -0
  102. package/skills/kb/references/query.md +231 -0
  103. package/skills/kb/references/refresh.md +133 -0
  104. package/skills/kb/references/save-pdf.md +125 -0
  105. package/skills/kb/references/save-url.md +178 -0
  106. package/skills/kb/references/url-authentication.md +75 -0
  107. package/skills/kb/references/url-platforms.md +25 -0
  108. package/src/agent-context.ts +1241 -0
  109. package/src/agent-guide-audit.ts +750 -0
  110. package/src/attachments.ts +723 -0
  111. package/src/authoring.ts +1725 -0
  112. package/src/benchmark.ts +362 -0
  113. package/src/capture.ts +68 -0
  114. package/src/cli.ts +3660 -0
  115. package/src/clip/acquire.ts +1273 -0
  116. package/src/clip/archive-today.ts +402 -0
  117. package/src/clip/args.ts +423 -0
  118. package/src/clip/assets.ts +262 -0
  119. package/src/clip/bounded-byte-buffer.ts +75 -0
  120. package/src/clip/browser-profiles.ts +372 -0
  121. package/src/clip/bundle-reader.ts +608 -0
  122. package/src/clip/capture.ts +1054 -0
  123. package/src/clip/cli.ts +181 -0
  124. package/src/clip/cookies.ts +512 -0
  125. package/src/clip/doctor.ts +1110 -0
  126. package/src/clip/extract.ts +868 -0
  127. package/src/clip/jobs.ts +959 -0
  128. package/src/clip/lib.ts +861 -0
  129. package/src/clip/media.ts +1474 -0
  130. package/src/clip/metadata-search-tool/Cargo.lock +2498 -0
  131. package/src/clip/metadata-search-tool/Cargo.toml +18 -0
  132. package/src/clip/metadata-search-tool/runner.ts +685 -0
  133. package/src/clip/metadata-search-tool/src/main.rs +389 -0
  134. package/src/clip/metadata-search.ts +687 -0
  135. package/src/clip/network-proxy.ts +627 -0
  136. package/src/clip/network.ts +950 -0
  137. package/src/clip/package-root.ts +54 -0
  138. package/src/clip/persist.ts +1472 -0
  139. package/src/clip/platforms.ts +1824 -0
  140. package/src/clip/refresh.ts +197 -0
  141. package/src/clip/structured.ts +432 -0
  142. package/src/clip/terminal.ts +197 -0
  143. package/src/clip/url-intelligence.ts +581 -0
  144. package/src/clip/url-metadata-backfill.ts +616 -0
  145. package/src/clip/url-metadata-cli.ts +251 -0
  146. package/src/clip/url-metadata.ts +1397 -0
  147. package/src/evaluation-analysis-v2.ts +1440 -0
  148. package/src/evaluation-builder.ts +2539 -0
  149. package/src/evaluation-corpus-authoring.ts +2052 -0
  150. package/src/evaluation-evidence.ts +1147 -0
  151. package/src/evaluation-execution-v2.ts +426 -0
  152. package/src/evaluation-implementation.ts +131 -0
  153. package/src/evaluation-kb-closure.ts +1430 -0
  154. package/src/evaluation-kb-runner-v2.ts +735 -0
  155. package/src/evaluation-kb-v2.ts +2567 -0
  156. package/src/evaluation-kb.ts +1018 -0
  157. package/src/evaluation-measurement-v2.ts +1462 -0
  158. package/src/evaluation-packing-v2.ts +452 -0
  159. package/src/evaluation-redaction.ts +28 -0
  160. package/src/evaluation-v2.ts +3857 -0
  161. package/src/evaluation.ts +1220 -0
  162. package/src/git.ts +1276 -0
  163. package/src/graph.ts +1487 -0
  164. package/src/index.ts +20 -0
  165. package/src/init.ts +123 -0
  166. package/src/navigation.ts +277 -0
  167. package/src/note-lock.ts +529 -0
  168. package/src/pdf/args.ts +202 -0
  169. package/src/pdf/capture.ts +298 -0
  170. package/src/pdf/cli.ts +159 -0
  171. package/src/pdf/extract.ts +303 -0
  172. package/src/pdf/layout.ts +618 -0
  173. package/src/pdf/markdown.ts +154 -0
  174. package/src/pdf/model.ts +234 -0
  175. package/src/pdf/ocr.ts +190 -0
  176. package/src/pdf/persist.ts +314 -0
  177. package/src/pdf/source.ts +135 -0
  178. package/src/pdf/tools.ts +146 -0
  179. package/src/pdf.ts +92 -0
  180. package/src/percolate.ts +877 -0
  181. package/src/portfolio-audit.ts +574 -0
  182. package/src/portfolio-identity.ts +162 -0
  183. package/src/portfolio-registry.ts +473 -0
  184. package/src/portfolio.ts +765 -0
  185. package/src/query.ts +646 -0
  186. package/src/repository-memory.ts +1150 -0
  187. package/src/sdk.ts +1452 -0
  188. package/src/search-rules.ts +712 -0
  189. package/src/search.ts +710 -0
  190. package/src/semantic-runtime.ts +1139 -0
  191. package/src/semantic.ts +2480 -0
  192. package/src/source-inbox.ts +232 -0
  193. package/src/untrusted-content.ts +401 -0
  194. package/src/url-intelligence.ts +104 -0
  195. package/src/vault.ts +636 -0
  196. package/src/workflow.ts +653 -0
  197. package/src/workflows/decision-context.ts +81 -0
  198. package/src/workflows/explain-change.ts +73 -0
  199. package/src/workflows/index.ts +3 -0
  200. package/src/workflows/plan-radar.ts +101 -0
@@ -0,0 +1,571 @@
1
+ // @bun
2
+ import {
3
+ FetchFailure,
4
+ createPinnedLookup,
5
+ resolveSafeNetworkTarget
6
+ } from "./index-e5fbsywq.js";
7
+ import {
8
+ BoundedByteBuffer
9
+ } from "./index-gh719d91.js";
10
+
11
+ // src/clip/network-proxy.ts
12
+ import {
13
+ createServer,
14
+ request as requestHttp
15
+ } from "http";
16
+ import { connect as connectTcp, Socket } from "net";
17
+ var defaultTimeoutMs = 30000;
18
+ var defaultMaxHeaderBytes = 32 * 1024;
19
+ var defaultMaxConnections = 64;
20
+ var defaultMaxRequestBodyBytes = 16 * 1024 * 1024;
21
+ var defaultMaxTransferredBytes = 1024 * 1024 * 1024;
22
+ var maxConnectAddresses = 16;
23
+ var hopByHopHeaders = new Set([
24
+ "connection",
25
+ "keep-alive",
26
+ "proxy-authenticate",
27
+ "proxy-authorization",
28
+ "proxy-connection",
29
+ "te",
30
+ "trailer",
31
+ "transfer-encoding",
32
+ "upgrade"
33
+ ]);
34
+
35
+ class ProxyRequestTooLarge extends Error {
36
+ constructor() {
37
+ super("proxy request body exceeded its limit");
38
+ this.name = "ProxyRequestTooLarge";
39
+ }
40
+ }
41
+ function positiveBoundedInteger(value, fallback, maximum) {
42
+ if (value === undefined)
43
+ return fallback;
44
+ if (!Number.isSafeInteger(value) || value < 1)
45
+ throw new Error("network proxy limits must be positive integers");
46
+ return Math.min(value, maximum);
47
+ }
48
+ function connectionNamedHeaders(headers) {
49
+ const names = new Set;
50
+ const connection = headers["connection"];
51
+ const values = Array.isArray(connection) ? connection : connection === undefined ? [] : [connection];
52
+ for (const value of values) {
53
+ if (typeof value !== "string")
54
+ continue;
55
+ for (const name of value.split(",")) {
56
+ const normalized = name.trim().toLowerCase();
57
+ if (normalized !== "")
58
+ names.add(normalized);
59
+ }
60
+ }
61
+ return names;
62
+ }
63
+ function forwardedHeaders(headers) {
64
+ const named = connectionNamedHeaders(headers);
65
+ const forwarded = {};
66
+ for (const [name, value] of Object.entries(headers)) {
67
+ const normalized = name.toLowerCase();
68
+ if (hopByHopHeaders.has(normalized) || named.has(normalized) || normalized === "host")
69
+ continue;
70
+ if (value !== undefined)
71
+ forwarded[normalized] = value;
72
+ }
73
+ return forwarded;
74
+ }
75
+ function responseHeaders(headers) {
76
+ const named = connectionNamedHeaders(headers);
77
+ const forwarded = {};
78
+ for (const [name, value] of Object.entries(headers)) {
79
+ const normalized = name.toLowerCase();
80
+ if (hopByHopHeaders.has(normalized) || named.has(normalized))
81
+ continue;
82
+ if (value !== undefined)
83
+ forwarded[normalized] = value;
84
+ }
85
+ return forwarded;
86
+ }
87
+ function proxyStatusFor(error) {
88
+ if (error instanceof ProxyRequestTooLarge)
89
+ return 413;
90
+ if (error instanceof FetchFailure && (error.code === "private-network" || error.code === "invalid-url"))
91
+ return 403;
92
+ if (error instanceof FetchFailure && error.code === "timeout")
93
+ return 504;
94
+ return 502;
95
+ }
96
+ function readRequestBody(incoming, maxBytes) {
97
+ return new Promise((resolve, reject) => {
98
+ const bytes = new BoundedByteBuffer(maxBytes);
99
+ let settled = false;
100
+ const cleanup = () => {
101
+ incoming.off("data", onData);
102
+ incoming.off("end", onEnd);
103
+ incoming.off("error", onError);
104
+ incoming.off("aborted", onAborted);
105
+ };
106
+ const fail = (error) => {
107
+ if (settled)
108
+ return;
109
+ settled = true;
110
+ cleanup();
111
+ reject(error);
112
+ };
113
+ const onData = (value) => {
114
+ const chunk = Buffer.isBuffer(value) ? value : typeof value === "string" ? Buffer.from(value) : value instanceof Uint8Array ? Buffer.from(value) : null;
115
+ if (chunk === null) {
116
+ fail(new Error("proxy request yielded a non-byte chunk"));
117
+ return;
118
+ }
119
+ if (!bytes.append(chunk)) {
120
+ fail(new ProxyRequestTooLarge);
121
+ return;
122
+ }
123
+ };
124
+ const onEnd = () => {
125
+ if (settled)
126
+ return;
127
+ settled = true;
128
+ cleanup();
129
+ const body = bytes.toUint8Array();
130
+ resolve(Buffer.from(body.buffer, body.byteOffset, body.byteLength));
131
+ };
132
+ const onError = (error) => fail(error);
133
+ const onAborted = () => fail(new Error("proxy request was aborted"));
134
+ incoming.on("data", onData);
135
+ incoming.once("end", onEnd);
136
+ incoming.once("error", onError);
137
+ incoming.once("aborted", onAborted);
138
+ });
139
+ }
140
+ function finishSocket(socket, status) {
141
+ if (socket.destroyed)
142
+ return;
143
+ const reason = status === 403 ? "Forbidden" : status === 504 ? "Gateway Timeout" : "Bad Gateway";
144
+ socket.end(`HTTP/1.1 ${status} ${reason}\r
145
+ Connection: close\r
146
+ Content-Length: 0\r
147
+ \r
148
+ `);
149
+ }
150
+ function finishResponse(response, status) {
151
+ if (response.headersSent || response.destroyed) {
152
+ response.destroy();
153
+ return;
154
+ }
155
+ response.writeHead(status, {
156
+ "Cache-Control": "no-store",
157
+ Connection: "close",
158
+ "Content-Length": "0"
159
+ });
160
+ response.end();
161
+ }
162
+ function connectAuthority(authority) {
163
+ if (authority === "" || authority.length > 1024 || /[\s\\/?#@]/.test(authority)) {
164
+ throw new FetchFailure("invalid-url", "invalid CONNECT authority");
165
+ }
166
+ let target;
167
+ try {
168
+ target = new URL(`https://${authority}/`);
169
+ } catch (error) {
170
+ throw new FetchFailure("invalid-url", "invalid CONNECT authority", { cause: error });
171
+ }
172
+ if (target.hostname === "" || target.username !== "" || target.password !== "") {
173
+ throw new FetchFailure("invalid-url", "invalid CONNECT authority");
174
+ }
175
+ return target;
176
+ }
177
+ function targetPort(target) {
178
+ const raw = target.port;
179
+ const fallback = target.protocol === "https:" ? 443 : 80;
180
+ const port = raw === "" ? fallback : Number(raw);
181
+ if (!Number.isSafeInteger(port) || port < 1 || port > 65535) {
182
+ throw new FetchFailure("invalid-url", "destination port is invalid");
183
+ }
184
+ return port;
185
+ }
186
+ function trackSocket(socket, sockets, timeoutMs) {
187
+ sockets.add(socket);
188
+ if (socket instanceof Socket)
189
+ socket.setTimeout(timeoutMs, () => socket.destroy());
190
+ socket.once("close", () => sockets.delete(socket));
191
+ }
192
+ async function connectPinned(addresses, port, timeoutMs, sockets, connectAddress, isClosing) {
193
+ const deadline = Date.now() + timeoutMs;
194
+ let lastError;
195
+ for (const address of addresses.slice(0, maxConnectAddresses)) {
196
+ if (isClosing())
197
+ throw new FetchFailure("network", "proxy is closing");
198
+ try {
199
+ const socket = await new Promise((resolve, reject) => {
200
+ const remainingMs = deadline - Date.now();
201
+ if (remainingMs <= 0) {
202
+ reject(new FetchFailure("timeout", "proxy connection timed out"));
203
+ return;
204
+ }
205
+ const candidate = connectAddress(address, port);
206
+ trackSocket(candidate, sockets, timeoutMs);
207
+ let settled = false;
208
+ const timeout = setTimeout(() => {
209
+ if (settled)
210
+ return;
211
+ settled = true;
212
+ candidate.destroy();
213
+ reject(new FetchFailure("timeout", "proxy connection timed out"));
214
+ }, remainingMs);
215
+ candidate.once("connect", () => {
216
+ if (settled)
217
+ return;
218
+ settled = true;
219
+ clearTimeout(timeout);
220
+ resolve(candidate);
221
+ });
222
+ candidate.once("error", (error) => {
223
+ if (settled)
224
+ return;
225
+ settled = true;
226
+ clearTimeout(timeout);
227
+ candidate.destroy();
228
+ reject(error);
229
+ });
230
+ candidate.once("close", () => {
231
+ if (settled)
232
+ return;
233
+ settled = true;
234
+ clearTimeout(timeout);
235
+ reject(new FetchFailure("network", "proxy connection closed before it was established"));
236
+ });
237
+ });
238
+ if (isClosing()) {
239
+ socket.destroy();
240
+ throw new FetchFailure("network", "proxy is closing");
241
+ }
242
+ return socket;
243
+ } catch (error) {
244
+ if (isClosing())
245
+ throw new FetchFailure("network", "proxy is closing", { cause: error });
246
+ if (error instanceof FetchFailure && error.code === "timeout")
247
+ throw error;
248
+ lastError = error;
249
+ }
250
+ }
251
+ if (Date.now() >= deadline) {
252
+ throw new FetchFailure("timeout", "proxy connection timed out", { cause: lastError });
253
+ }
254
+ throw new FetchFailure("network", "could not connect to validated destination", { cause: lastError });
255
+ }
256
+ function parseAbsoluteHttpTarget(raw) {
257
+ if (raw === undefined || raw.length > 16 * 1024) {
258
+ throw new FetchFailure("invalid-url", "invalid proxy request target");
259
+ }
260
+ let target;
261
+ try {
262
+ target = new URL(raw);
263
+ } catch (error) {
264
+ throw new FetchFailure("invalid-url", "proxy requests require an absolute HTTP URL", { cause: error });
265
+ }
266
+ if (target.protocol !== "http:" || target.hash !== "") {
267
+ throw new FetchFailure("invalid-url", "absolute proxy requests must use HTTP");
268
+ }
269
+ return target;
270
+ }
271
+ async function startNetworkProxy(options) {
272
+ const timeoutMs = positiveBoundedInteger(options.timeoutMs, defaultTimeoutMs, 10 * 60000);
273
+ const maxHeaderBytes = positiveBoundedInteger(options.maxHeaderBytes, defaultMaxHeaderBytes, 1024 * 1024);
274
+ const maxConnections = positiveBoundedInteger(options.maxConnections, defaultMaxConnections, 1024);
275
+ const maxRequestBodyBytes = positiveBoundedInteger(options.maxRequestBodyBytes, defaultMaxRequestBodyBytes, 1024 * 1024 * 1024);
276
+ const maxTransferredBytes = positiveBoundedInteger(options.maxTransferredBytes, defaultMaxTransferredBytes, Number.MAX_SAFE_INTEGER);
277
+ const sockets = new Set;
278
+ const httpUpstreams = new Set;
279
+ let activeRequests = 0;
280
+ let transferredBytes = 0;
281
+ let closing = false;
282
+ const reserveRequest = () => {
283
+ if (closing || activeRequests >= maxConnections)
284
+ return false;
285
+ activeRequests += 1;
286
+ return true;
287
+ };
288
+ const releaseRequest = () => {
289
+ activeRequests = Math.max(0, activeRequests - 1);
290
+ };
291
+ const accountTransfer = (bytes) => {
292
+ transferredBytes += bytes;
293
+ return transferredBytes <= maxTransferredBytes;
294
+ };
295
+ const forgetHttpUpstream = (upstream) => {
296
+ if (upstream.settled)
297
+ return;
298
+ upstream.settled = true;
299
+ httpUpstreams.delete(upstream);
300
+ upstream.detachDownstream();
301
+ };
302
+ const destroyHttpUpstream = (upstream) => {
303
+ if (upstream.settled)
304
+ return;
305
+ upstream.settled = true;
306
+ httpUpstreams.delete(upstream);
307
+ upstream.detachDownstream();
308
+ upstream.response?.destroy();
309
+ upstream.request.destroy();
310
+ upstream.socket?.destroy();
311
+ };
312
+ const resolveTarget = (target) => resolveSafeNetworkTarget(target, {
313
+ allowPrivateNetwork: options.allowPrivateNetwork,
314
+ timeoutMs,
315
+ ...options.resolveHostname === undefined ? {} : { resolveHostname: options.resolveHostname }
316
+ });
317
+ const connectAddress = options.connectAddress ?? ((address2, port) => connectTcp({ host: address2.address, port, family: address2.family }));
318
+ const server = createServer({
319
+ maxHeaderSize: maxHeaderBytes,
320
+ headersTimeout: timeoutMs,
321
+ requestTimeout: timeoutMs,
322
+ keepAliveTimeout: Math.min(timeoutMs, 5000)
323
+ });
324
+ server.maxConnections = maxConnections;
325
+ server.on("request", (incoming, outgoing) => {
326
+ if (!reserveRequest()) {
327
+ finishResponse(outgoing, 503);
328
+ return;
329
+ }
330
+ let released = false;
331
+ const release = () => {
332
+ if (released)
333
+ return;
334
+ released = true;
335
+ releaseRequest();
336
+ };
337
+ let httpUpstream = null;
338
+ let downstreamClosed = false;
339
+ let detachDownstream = () => {
340
+ return;
341
+ };
342
+ const closeHttpUpstream = () => {
343
+ downstreamClosed = true;
344
+ if (httpUpstream === null) {
345
+ detachDownstream();
346
+ return;
347
+ }
348
+ destroyHttpUpstream(httpUpstream);
349
+ };
350
+ const closeIncompleteIncoming = () => {
351
+ if (incoming.aborted || !incoming.complete)
352
+ closeHttpUpstream();
353
+ };
354
+ detachDownstream = () => {
355
+ incoming.off("aborted", closeHttpUpstream);
356
+ incoming.off("error", closeHttpUpstream);
357
+ incoming.off("close", closeIncompleteIncoming);
358
+ incoming.socket.off("error", closeHttpUpstream);
359
+ incoming.socket.off("close", closeHttpUpstream);
360
+ outgoing.off("error", closeHttpUpstream);
361
+ };
362
+ incoming.once("aborted", closeHttpUpstream);
363
+ incoming.once("error", closeHttpUpstream);
364
+ incoming.once("close", closeIncompleteIncoming);
365
+ incoming.socket.once("error", closeHttpUpstream);
366
+ incoming.socket.once("close", closeHttpUpstream);
367
+ outgoing.once("error", closeHttpUpstream);
368
+ outgoing.once("close", () => {
369
+ release();
370
+ closeHttpUpstream();
371
+ });
372
+ outgoing.once("finish", release);
373
+ (async () => {
374
+ let target;
375
+ try {
376
+ target = parseAbsoluteHttpTarget(incoming.url);
377
+ const declared = incoming.headers["content-length"];
378
+ if (typeof declared === "string") {
379
+ const length = Number(declared);
380
+ if (!Number.isSafeInteger(length) || length < 0 || length > maxRequestBodyBytes) {
381
+ finishResponse(outgoing, 413);
382
+ incoming.destroy();
383
+ return;
384
+ }
385
+ }
386
+ const bodyPromise = readRequestBody(incoming, maxRequestBodyBytes);
387
+ const [addresses, body] = await Promise.all([resolveTarget(target), bodyPromise]);
388
+ const address2 = addresses[0];
389
+ if (address2 === undefined)
390
+ throw new FetchFailure("dns", "destination had no validated address");
391
+ if (downstreamClosed || incoming.aborted || outgoing.destroyed || closing)
392
+ return;
393
+ const hostname = target.hostname.startsWith("[") && target.hostname.endsWith("]") ? target.hostname.slice(1, -1) : target.hostname;
394
+ const headers = forwardedHeaders(incoming.headers);
395
+ headers.host = target.host;
396
+ const requestOptions = {
397
+ protocol: "http:",
398
+ hostname,
399
+ port: targetPort(target),
400
+ method: incoming.method ?? "GET",
401
+ path: `${target.pathname}${target.search}`,
402
+ headers,
403
+ lookup: createPinnedLookup(address2),
404
+ family: address2.family,
405
+ agent: false,
406
+ maxHeaderSize: maxHeaderBytes
407
+ };
408
+ const upstream = requestHttp(requestOptions, (response) => {
409
+ const tracked2 = httpUpstream;
410
+ if (tracked2 === null || tracked2.settled) {
411
+ response.destroy();
412
+ return;
413
+ }
414
+ tracked2.response = response;
415
+ response.once("close", () => forgetHttpUpstream(tracked2));
416
+ if (outgoing.destroyed || closing) {
417
+ destroyHttpUpstream(tracked2);
418
+ return;
419
+ }
420
+ outgoing.writeHead(response.statusCode ?? 502, response.statusMessage, responseHeaders(response.headers));
421
+ response.on("data", (chunk) => {
422
+ const size = typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk instanceof Uint8Array ? chunk.byteLength : 0;
423
+ if (!accountTransfer(size)) {
424
+ response.destroy(new Error("proxy transfer limit exceeded"));
425
+ outgoing.destroy();
426
+ }
427
+ });
428
+ response.once("error", () => {
429
+ destroyHttpUpstream(tracked2);
430
+ outgoing.destroy();
431
+ });
432
+ response.pipe(outgoing);
433
+ });
434
+ httpUpstream = {
435
+ request: upstream,
436
+ detachDownstream,
437
+ response: null,
438
+ socket: null,
439
+ settled: false
440
+ };
441
+ httpUpstreams.add(httpUpstream);
442
+ const tracked = httpUpstream;
443
+ upstream.once("socket", (socket) => {
444
+ if (tracked.settled || closing) {
445
+ socket.destroy();
446
+ return;
447
+ }
448
+ tracked.socket = socket;
449
+ trackSocket(socket, sockets, timeoutMs);
450
+ });
451
+ upstream.setTimeout(timeoutMs, () => upstream.destroy(new Error("proxy request timed out")));
452
+ upstream.once("error", () => {
453
+ destroyHttpUpstream(tracked);
454
+ finishResponse(outgoing, 502);
455
+ });
456
+ upstream.once("close", () => {
457
+ if (tracked.response === null)
458
+ forgetHttpUpstream(tracked);
459
+ });
460
+ if (downstreamClosed || outgoing.destroyed || closing) {
461
+ destroyHttpUpstream(tracked);
462
+ return;
463
+ }
464
+ if (!accountTransfer(body.byteLength)) {
465
+ upstream.destroy(new Error("proxy transfer limit exceeded"));
466
+ finishResponse(outgoing, 413);
467
+ return;
468
+ }
469
+ upstream.end(body);
470
+ } catch (error) {
471
+ finishResponse(outgoing, proxyStatusFor(error));
472
+ }
473
+ })();
474
+ });
475
+ server.on("connect", (request, client, head) => {
476
+ if (!reserveRequest()) {
477
+ finishSocket(client, 503);
478
+ return;
479
+ }
480
+ let released = false;
481
+ const release = () => {
482
+ if (released)
483
+ return;
484
+ released = true;
485
+ releaseRequest();
486
+ };
487
+ client.once("close", release);
488
+ trackSocket(client, sockets, timeoutMs);
489
+ (async () => {
490
+ try {
491
+ const target = connectAuthority(request.url ?? "");
492
+ const addresses = await resolveTarget(target);
493
+ if (client.destroyed)
494
+ return;
495
+ const upstream = await connectPinned(addresses, targetPort(target), timeoutMs, sockets, connectAddress, () => closing);
496
+ if (client.destroyed || closing) {
497
+ upstream.destroy();
498
+ return;
499
+ }
500
+ client.write(`HTTP/1.1 200 Connection Established\r
501
+ Proxy-Agent: hraness-kb\r
502
+ \r
503
+ `);
504
+ if (head.byteLength > 0) {
505
+ if (!accountTransfer(head.byteLength)) {
506
+ client.destroy();
507
+ upstream.destroy();
508
+ return;
509
+ }
510
+ upstream.write(head);
511
+ }
512
+ client.on("data", (chunk) => {
513
+ if (!accountTransfer(chunk.byteLength)) {
514
+ client.destroy();
515
+ upstream.destroy();
516
+ }
517
+ });
518
+ upstream.on("data", (chunk) => {
519
+ if (!accountTransfer(chunk.byteLength)) {
520
+ client.destroy();
521
+ upstream.destroy();
522
+ }
523
+ });
524
+ client.once("error", () => upstream.destroy());
525
+ upstream.once("error", () => client.destroy());
526
+ client.pipe(upstream);
527
+ upstream.pipe(client);
528
+ } catch (error) {
529
+ finishSocket(client, proxyStatusFor(error));
530
+ }
531
+ })();
532
+ });
533
+ server.on("upgrade", (_request, socket) => finishSocket(socket, 501));
534
+ server.on("clientError", (_error, socket) => finishSocket(socket, 400));
535
+ server.on("connection", (socket) => trackSocket(socket, sockets, timeoutMs));
536
+ await new Promise((resolve, reject) => {
537
+ const onError = (error) => reject(error);
538
+ server.once("error", onError);
539
+ server.listen(0, "127.0.0.1", () => {
540
+ server.off("error", onError);
541
+ resolve();
542
+ });
543
+ });
544
+ const address = server.address();
545
+ if (address === null || typeof address === "string") {
546
+ server.close();
547
+ throw new Error("network proxy did not bind a TCP port");
548
+ }
549
+ let closePromise = null;
550
+ return {
551
+ url: `http://127.0.0.1:${address.port}`,
552
+ port: address.port,
553
+ close: () => {
554
+ if (closePromise !== null)
555
+ return closePromise;
556
+ closing = true;
557
+ for (const upstream of [...httpUpstreams])
558
+ destroyHttpUpstream(upstream);
559
+ for (const socket of sockets)
560
+ socket.destroy();
561
+ closePromise = new Promise((resolveClose) => {
562
+ server.close(() => resolveClose());
563
+ server.closeAllConnections?.();
564
+ server.closeIdleConnections?.();
565
+ });
566
+ return closePromise;
567
+ }
568
+ };
569
+ }
570
+
571
+ export { startNetworkProxy };