@vacbo/opencode-anthropic-fix 0.1.7 → 0.1.9
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/README.md +88 -88
- package/dist/opencode-anthropic-auth-cli.mjs +804 -507
- package/dist/opencode-anthropic-auth-plugin.js +4751 -4109
- package/package.json +67 -59
- package/src/__tests__/billing-edge-cases.test.ts +59 -59
- package/src/__tests__/bun-proxy.parallel.test.ts +388 -382
- package/src/__tests__/cc-comparison.test.ts +87 -87
- package/src/__tests__/cc-credentials.test.ts +254 -250
- package/src/__tests__/cch-drift-checker.test.ts +51 -51
- package/src/__tests__/cch-native-style.test.ts +56 -56
- package/src/__tests__/debug-gating.test.ts +42 -42
- package/src/__tests__/decomposition-smoke.test.ts +68 -68
- package/src/__tests__/fingerprint-regression.test.ts +575 -566
- package/src/__tests__/helpers/conversation-history.smoke.test.ts +271 -271
- package/src/__tests__/helpers/conversation-history.ts +119 -119
- package/src/__tests__/helpers/deferred.smoke.test.ts +103 -103
- package/src/__tests__/helpers/deferred.ts +69 -69
- package/src/__tests__/helpers/in-memory-storage.smoke.test.ts +155 -155
- package/src/__tests__/helpers/in-memory-storage.ts +88 -88
- package/src/__tests__/helpers/mock-bun-proxy.smoke.test.ts +68 -68
- package/src/__tests__/helpers/mock-bun-proxy.ts +189 -189
- package/src/__tests__/helpers/plugin-fetch-harness.smoke.test.ts +273 -273
- package/src/__tests__/helpers/plugin-fetch-harness.ts +288 -288
- package/src/__tests__/helpers/sse.smoke.test.ts +236 -236
- package/src/__tests__/helpers/sse.ts +209 -209
- package/src/__tests__/index.parallel.test.ts +605 -595
- package/src/__tests__/sanitization-regex.test.ts +112 -112
- package/src/__tests__/state-bounds.test.ts +90 -90
- package/src/account-identity.test.ts +197 -192
- package/src/account-identity.ts +69 -67
- package/src/account-state.test.ts +86 -86
- package/src/account-state.ts +25 -25
- package/src/accounts/matching.test.ts +335 -0
- package/src/accounts/matching.ts +167 -0
- package/src/accounts/persistence.test.ts +345 -0
- package/src/accounts/persistence.ts +432 -0
- package/src/accounts/repair.test.ts +276 -0
- package/src/accounts/repair.ts +407 -0
- package/src/accounts.dedup.test.ts +621 -621
- package/src/accounts.test.ts +933 -929
- package/src/accounts.ts +633 -989
- package/src/backoff.test.ts +345 -345
- package/src/backoff.ts +219 -219
- package/src/betas.ts +124 -124
- package/src/bun-fetch.test.ts +345 -342
- package/src/bun-fetch.ts +424 -424
- package/src/bun-proxy.test.ts +25 -25
- package/src/bun-proxy.ts +209 -209
- package/src/cc-credentials.ts +111 -111
- package/src/circuit-breaker.test.ts +184 -184
- package/src/circuit-breaker.ts +169 -169
- package/src/cli/commands/auth.ts +963 -0
- package/src/cli/commands/config.ts +547 -0
- package/src/cli/formatting.test.ts +406 -0
- package/src/cli/formatting.ts +219 -0
- package/src/cli.ts +255 -2022
- package/src/commands/handlers/betas.ts +100 -0
- package/src/commands/handlers/config.ts +99 -0
- package/src/commands/handlers/files.ts +375 -0
- package/src/commands/oauth-flow.ts +181 -166
- package/src/commands/prompts.ts +61 -61
- package/src/commands/router.test.ts +421 -0
- package/src/commands/router.ts +143 -635
- package/src/config.test.ts +482 -482
- package/src/config.ts +412 -404
- package/src/constants.ts +48 -48
- package/src/drift/cch-constants.ts +95 -95
- package/src/env.ts +111 -105
- package/src/headers/billing.ts +33 -33
- package/src/headers/builder.ts +130 -130
- package/src/headers/cch.ts +75 -75
- package/src/headers/stainless.ts +25 -25
- package/src/headers/user-agent.ts +23 -23
- package/src/index.ts +436 -828
- package/src/models.ts +27 -27
- package/src/oauth.test.ts +102 -102
- package/src/oauth.ts +178 -178
- package/src/parent-pid-watcher.test.ts +148 -148
- package/src/parent-pid-watcher.ts +69 -69
- package/src/plugin-helpers.ts +82 -82
- package/src/refresh-helpers.ts +145 -139
- package/src/refresh-lock.test.ts +94 -94
- package/src/refresh-lock.ts +93 -93
- package/src/request/body.history.test.ts +579 -571
- package/src/request/body.ts +255 -255
- package/src/request/metadata.ts +65 -65
- package/src/request/retry.test.ts +156 -156
- package/src/request/retry.ts +67 -67
- package/src/request/url.ts +21 -21
- package/src/request-orchestration-helpers.ts +648 -0
- package/src/response/index.ts +5 -5
- package/src/response/mcp.ts +58 -58
- package/src/response/streaming.test.ts +313 -311
- package/src/response/streaming.ts +412 -410
- package/src/rotation.test.ts +304 -301
- package/src/rotation.ts +205 -205
- package/src/storage.test.ts +547 -547
- package/src/storage.ts +315 -291
- package/src/system-prompt/builder.ts +38 -38
- package/src/system-prompt/index.ts +5 -5
- package/src/system-prompt/normalize.ts +60 -60
- package/src/system-prompt/sanitize.ts +30 -30
- package/src/thinking.ts +21 -20
- package/src/token-refresh.test.ts +265 -265
- package/src/token-refresh.ts +219 -214
- package/src/types.ts +30 -30
- package/dist/bun-proxy.mjs +0 -291
package/src/bun-proxy.test.ts
CHANGED
|
@@ -3,35 +3,35 @@ import { describe, expect, it, vi } from "vitest";
|
|
|
3
3
|
import { createProxyRequestHandler } from "./bun-proxy.js";
|
|
4
4
|
|
|
5
5
|
function makeProxyRequest(headers?: HeadersInit): Request {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const requestHeaders = new Headers(headers);
|
|
7
|
+
requestHeaders.set("x-proxy-url", "https://api.anthropic.com/v1/messages");
|
|
8
|
+
requestHeaders.set("content-type", "application/json");
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
return new Request("http://127.0.0.1/proxy", {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: requestHeaders,
|
|
13
|
+
body: JSON.stringify({ ok: true }),
|
|
14
|
+
});
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
describe("createProxyRequestHandler", () => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
it("forwards retry requests with keepalive disabled to the upstream fetch", async () => {
|
|
19
|
+
const upstreamFetch = vi.fn(async (_input, init?: RequestInit) => {
|
|
20
|
+
expect(init?.keepalive).toBe(false);
|
|
21
|
+
const forwardedHeaders = init?.headers instanceof Headers ? init.headers : new Headers(init?.headers);
|
|
22
|
+
expect(forwardedHeaders.get("connection")).toBe("close");
|
|
23
|
+
expect(forwardedHeaders.get("x-proxy-disable-keepalive")).toBeNull();
|
|
24
|
+
return new Response("ok", { status: 200 });
|
|
25
|
+
});
|
|
26
|
+
const handler = createProxyRequestHandler({
|
|
27
|
+
fetchImpl: upstreamFetch as typeof fetch,
|
|
28
|
+
allowHosts: ["api.anthropic.com"],
|
|
29
|
+
requestTimeoutMs: 50,
|
|
30
|
+
});
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const response = await handler(makeProxyRequest({ "x-proxy-disable-keepalive": "true" }));
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
await expect(response.text()).resolves.toBe("ok");
|
|
35
|
+
expect(upstreamFetch).toHaveBeenCalledTimes(1);
|
|
36
|
+
});
|
|
37
37
|
});
|
package/src/bun-proxy.ts
CHANGED
|
@@ -12,300 +12,300 @@ const PROXY_DISABLE_KEEPALIVE_HEADER = "x-proxy-disable-keepalive";
|
|
|
12
12
|
const DEBUG_ENABLED = process.env.OPENCODE_ANTHROPIC_DEBUG === "1";
|
|
13
13
|
|
|
14
14
|
interface ProxyRequestHandlerOptions {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
fetchImpl: typeof fetch;
|
|
16
|
+
allowHosts?: string[];
|
|
17
|
+
requestTimeoutMs?: number;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface ProxyProcessRuntimeOptions {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
argv?: string[];
|
|
22
|
+
exit?: (code?: number) => void;
|
|
23
|
+
parentWatcherFactory?: ParentWatcherFactory;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
interface ParentWatcher {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
start(): void;
|
|
28
|
+
stop(): void;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
interface ParentWatcherFactoryOptions {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
parentPid: number;
|
|
33
|
+
onParentExit: (exitCode?: number) => void;
|
|
34
|
+
pollIntervalMs?: number;
|
|
35
|
+
exitCode?: number;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
type ParentWatcherFactory = (options: ParentWatcherFactoryOptions) => ParentWatcher;
|
|
39
39
|
|
|
40
40
|
type RequestInitWithDuplex = RequestInit & {
|
|
41
|
-
|
|
41
|
+
duplex?: "half";
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
interface AbortContext {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
timeoutSignal: AbortSignal;
|
|
46
|
+
cancelTimeout(): void;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function isMainModule(argv: string[] = process.argv): boolean {
|
|
50
|
-
|
|
50
|
+
return Boolean(argv[1]) && resolve(argv[1]) === fileURLToPath(import.meta.url);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
function parseInteger(value: string | undefined): number | null {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const parsed = Number.parseInt(value ?? "", 10);
|
|
55
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function parseParentPid(argv: string[]): number | null {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const inlineValue = argv
|
|
60
|
+
.map((argument) => argument.match(/^--parent-pid=(\d+)$/)?.[1] ?? null)
|
|
61
|
+
.find((value) => value !== null);
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
if (inlineValue) {
|
|
64
|
+
return parseInteger(inlineValue);
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const flagIndex = argv.indexOf("--parent-pid");
|
|
68
|
+
return flagIndex >= 0 ? parseInteger(argv[flagIndex + 1]) : null;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function createNoopWatcher(): ParentWatcher {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
return {
|
|
73
|
+
start(): void {},
|
|
74
|
+
stop(): void {},
|
|
75
|
+
};
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function createDefaultParentWatcherFactory(): ParentWatcherFactory {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
return ({ parentPid, onParentExit, pollIntervalMs, exitCode }): ParentWatcher =>
|
|
80
|
+
new ParentPidWatcher({
|
|
81
|
+
parentPid,
|
|
82
|
+
pollIntervalMs,
|
|
83
|
+
onParentGone: () => {
|
|
84
|
+
onParentExit(exitCode);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function sanitizeForwardHeaders(source: Headers, forceFreshConnection = false): Headers {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
const headers = new Headers(source);
|
|
91
|
+
[PROXY_DISABLE_KEEPALIVE_HEADER, "x-proxy-url", "host", "connection", "content-length"].forEach((headerName) => {
|
|
92
|
+
headers.delete(headerName);
|
|
93
|
+
});
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
if (forceFreshConnection) {
|
|
96
|
+
headers.set("connection", "close");
|
|
97
|
+
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
return headers;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function copyResponseHeaders(source: Headers): Headers {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
const headers = new Headers(source);
|
|
104
|
+
["transfer-encoding", "content-encoding"].forEach((headerName) => {
|
|
105
|
+
headers.delete(headerName);
|
|
106
|
+
});
|
|
107
|
+
return headers;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function resolveTargetUrl(req: Request, allowedHosts: ReadonlySet<string>): URL | Response {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (!targetUrl) {
|
|
114
|
-
return new Response("Missing x-proxy-url", { status: 400 });
|
|
115
|
-
}
|
|
111
|
+
const targetUrl = req.headers.get("x-proxy-url");
|
|
116
112
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (allowedHosts.size > 0 && !allowedHosts.has(parsedUrl.hostname)) {
|
|
120
|
-
return new Response(`Host not allowed: ${parsedUrl.hostname}`, { status: 403 });
|
|
113
|
+
if (!targetUrl) {
|
|
114
|
+
return new Response("Missing x-proxy-url", { status: 400 });
|
|
121
115
|
}
|
|
122
116
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
117
|
+
try {
|
|
118
|
+
const parsedUrl = new URL(targetUrl);
|
|
119
|
+
if (allowedHosts.size > 0 && !allowedHosts.has(parsedUrl.hostname)) {
|
|
120
|
+
return new Response(`Host not allowed: ${parsedUrl.hostname}`, { status: 403 });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return parsedUrl;
|
|
124
|
+
} catch {
|
|
125
|
+
return new Response("Invalid x-proxy-url", { status: 400 });
|
|
126
|
+
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
function createAbortContext(requestTimeoutMs: number): AbortContext {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
130
|
+
const timeoutController = new AbortController();
|
|
131
|
+
const timer = setTimeout(() => {
|
|
132
|
+
timeoutController.abort(new DOMException("Upstream request timed out", "TimeoutError"));
|
|
133
|
+
}, requestTimeoutMs);
|
|
134
|
+
|
|
135
|
+
timer.unref?.();
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
timeoutSignal: timeoutController.signal,
|
|
139
|
+
cancelTimeout(): void {
|
|
140
|
+
clearTimeout(timer);
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
function isAbortError(error: unknown): boolean {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
return error instanceof DOMException
|
|
147
|
+
? error.name === "AbortError" || error.name === "TimeoutError"
|
|
148
|
+
: error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
function isTimeoutAbort(signal: AbortSignal): boolean {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
const reason = signal.reason;
|
|
153
|
+
return reason instanceof DOMException
|
|
154
|
+
? reason.name === "TimeoutError"
|
|
155
|
+
: reason instanceof Error && reason.name === "TimeoutError";
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
function createAbortResponse(req: Request, timeoutSignal: AbortSignal): Response {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
return req.signal.aborted
|
|
160
|
+
? new Response("Client disconnected", { status: 499 })
|
|
161
|
+
: isTimeoutAbort(timeoutSignal)
|
|
162
|
+
? new Response("Upstream request timed out", { status: 504 })
|
|
163
|
+
: new Response("Upstream request aborted", { status: 499 });
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
async function createUpstreamInit(req: Request, signal: AbortSignal): Promise<RequestInitWithDuplex> {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
167
|
+
const method = req.method || "GET";
|
|
168
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
169
|
+
const bodyText = hasBody ? await req.text() : "";
|
|
170
|
+
const forceFreshConnection = req.headers.get(PROXY_DISABLE_KEEPALIVE_HEADER) === "true";
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
method,
|
|
174
|
+
headers: sanitizeForwardHeaders(req.headers, forceFreshConnection),
|
|
175
|
+
signal,
|
|
176
|
+
...(forceFreshConnection ? { keepalive: false } : {}),
|
|
177
|
+
...(hasBody && bodyText.length > 0 ? { body: bodyText } : {}),
|
|
178
|
+
};
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
function logRequest(targetUrl: URL, req: Request): void {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
182
|
+
if (!DEBUG_ENABLED) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const logHeaders = Object.fromEntries(
|
|
187
|
+
[...sanitizeForwardHeaders(req.headers).entries()].map(([key, value]) => [
|
|
188
|
+
key,
|
|
189
|
+
key === "authorization" ? "Bearer ***" : value,
|
|
190
|
+
]),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
console.error("\n[bun-proxy] === FORWARDED REQUEST ===");
|
|
194
|
+
console.error(`[bun-proxy] ${req.method} ${targetUrl.toString()}`);
|
|
195
|
+
console.error(`[bun-proxy] Headers: ${JSON.stringify(logHeaders, null, 2)}`);
|
|
196
|
+
console.error("[bun-proxy] =========================\n");
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
export function createProxyRequestHandler(options: ProxyRequestHandlerOptions): (req: Request) => Promise<Response> {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
200
|
+
const allowedHosts = new Set(options.allowHosts ?? DEFAULT_ALLOWED_HOSTS);
|
|
201
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
202
|
+
|
|
203
|
+
return async function handleProxyRequest(req: Request): Promise<Response> {
|
|
204
|
+
if (new URL(req.url).pathname === HEALTH_PATH) {
|
|
205
|
+
return new Response("ok");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const targetUrl = resolveTargetUrl(req, allowedHosts);
|
|
209
|
+
if (targetUrl instanceof Response) {
|
|
210
|
+
return targetUrl;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const abortContext = createAbortContext(requestTimeoutMs);
|
|
214
|
+
const upstreamSignal = AbortSignal.any([req.signal, abortContext.timeoutSignal]);
|
|
215
|
+
const upstreamInit = await createUpstreamInit(req, upstreamSignal);
|
|
216
|
+
logRequest(targetUrl, req);
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const upstreamResponse = await options.fetchImpl(targetUrl.toString(), upstreamInit);
|
|
220
|
+
return new Response(upstreamResponse.body, {
|
|
221
|
+
status: upstreamResponse.status,
|
|
222
|
+
statusText: upstreamResponse.statusText,
|
|
223
|
+
headers: copyResponseHeaders(upstreamResponse.headers),
|
|
224
|
+
});
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (upstreamSignal.aborted && isAbortError(error)) {
|
|
227
|
+
return createAbortResponse(req, abortContext.timeoutSignal);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
231
|
+
return new Response(message, { status: 502 });
|
|
232
|
+
} finally {
|
|
233
|
+
abortContext.cancelTimeout();
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
207
237
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
238
|
+
export function createProxyProcessRuntime(options: ProxyProcessRuntimeOptions = {}): ParentWatcher {
|
|
239
|
+
const argv = options.argv ?? process.argv;
|
|
240
|
+
const parentPid = parseParentPid(argv);
|
|
241
|
+
if (!parentPid) {
|
|
242
|
+
return createNoopWatcher();
|
|
211
243
|
}
|
|
212
244
|
|
|
213
|
-
const
|
|
214
|
-
const
|
|
215
|
-
const upstreamInit = await createUpstreamInit(req, upstreamSignal);
|
|
216
|
-
logRequest(targetUrl, req);
|
|
245
|
+
const exit = options.exit ?? process.exit;
|
|
246
|
+
const parentWatcherFactory = options.parentWatcherFactory ?? createDefaultParentWatcherFactory();
|
|
217
247
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
if (upstreamSignal.aborted && isAbortError(error)) {
|
|
227
|
-
return createAbortResponse(req, abortContext.timeoutSignal);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
231
|
-
return new Response(message, { status: 502 });
|
|
232
|
-
} finally {
|
|
233
|
-
abortContext.cancelTimeout();
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export function createProxyProcessRuntime(options: ProxyProcessRuntimeOptions = {}): ParentWatcher {
|
|
239
|
-
const argv = options.argv ?? process.argv;
|
|
240
|
-
const parentPid = parseParentPid(argv);
|
|
241
|
-
if (!parentPid) {
|
|
242
|
-
return createNoopWatcher();
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const exit = options.exit ?? process.exit;
|
|
246
|
-
const parentWatcherFactory = options.parentWatcherFactory ?? createDefaultParentWatcherFactory();
|
|
247
|
-
|
|
248
|
-
return parentWatcherFactory({
|
|
249
|
-
parentPid,
|
|
250
|
-
pollIntervalMs: DEFAULT_PARENT_POLL_INTERVAL_MS,
|
|
251
|
-
exitCode: DEFAULT_PARENT_EXIT_CODE,
|
|
252
|
-
onParentExit: (exitCode) => {
|
|
253
|
-
exit(exitCode ?? DEFAULT_PARENT_EXIT_CODE);
|
|
254
|
-
},
|
|
255
|
-
});
|
|
248
|
+
return parentWatcherFactory({
|
|
249
|
+
parentPid,
|
|
250
|
+
pollIntervalMs: DEFAULT_PARENT_POLL_INTERVAL_MS,
|
|
251
|
+
exitCode: DEFAULT_PARENT_EXIT_CODE,
|
|
252
|
+
onParentExit: (exitCode) => {
|
|
253
|
+
exit(exitCode ?? DEFAULT_PARENT_EXIT_CODE);
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
function assertBunRuntime(): typeof Bun {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
if (typeof Bun === "undefined") {
|
|
260
|
+
throw new Error("bun-proxy.ts must be executed with Bun.");
|
|
261
|
+
}
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
return Bun;
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
async function runProxyProcess(): Promise<void> {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const lifecycle = {
|
|
279
|
-
closed: false,
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
const shutdown = (exitCode = 0): void => {
|
|
283
|
-
if (lifecycle.closed) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
267
|
+
const bun = assertBunRuntime();
|
|
268
|
+
const watcher = createProxyProcessRuntime();
|
|
269
|
+
const server = bun.serve({
|
|
270
|
+
port: 0,
|
|
271
|
+
fetch: createProxyRequestHandler({
|
|
272
|
+
fetchImpl: fetch,
|
|
273
|
+
allowHosts: DEFAULT_ALLOWED_HOSTS,
|
|
274
|
+
requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
|
|
275
|
+
}),
|
|
276
|
+
});
|
|
286
277
|
|
|
287
|
-
lifecycle
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
process.exit(exitCode);
|
|
291
|
-
};
|
|
278
|
+
const lifecycle = {
|
|
279
|
+
closed: false,
|
|
280
|
+
};
|
|
292
281
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
const shutdown = (exitCode = 0): void => {
|
|
283
|
+
if (lifecycle.closed) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
296
286
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
287
|
+
lifecycle.closed = true;
|
|
288
|
+
watcher.stop();
|
|
289
|
+
server.stop(true);
|
|
290
|
+
process.exit(exitCode);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
process.on("SIGTERM", () => {
|
|
294
|
+
shutdown(0);
|
|
295
|
+
});
|
|
300
296
|
|
|
301
|
-
|
|
302
|
-
|
|
297
|
+
process.on("SIGINT", () => {
|
|
298
|
+
shutdown(0);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
watcher.start();
|
|
302
|
+
process.stdout.write(`BUN_PROXY_PORT=${server.port}\n`);
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
if (isMainModule()) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
306
|
+
void runProxyProcess().catch((error) => {
|
|
307
|
+
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
308
|
+
process.stderr.write(`${message}\n`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
});
|
|
311
311
|
}
|