@linzumi/cli 0.0.20-beta → 0.0.23-beta
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 +65 -62
- package/bin/linzumi.js +10 -18
- package/dist/assets/linzumi-logo.svg +1 -0
- package/dist/index.js +9229 -0
- package/package.json +9 -4
- package/src/agentBootstrap.ts +0 -872
- package/src/authCache.ts +0 -157
- package/src/authResolution.ts +0 -77
- package/src/boundedCache.ts +0 -57
- package/src/channelSession.ts +0 -4301
- package/src/channelSessionSupport.ts +0 -308
- package/src/codexAppServer.ts +0 -380
- package/src/codexOutput.ts +0 -846
- package/src/codexRuntimeOptions.ts +0 -80
- package/src/dependencyStatus.ts +0 -198
- package/src/forwardTunnel.ts +0 -859
- package/src/forwardTunnelProtocol.ts +0 -324
- package/src/index.ts +0 -1080
- package/src/json.ts +0 -49
- package/src/kandanQueue.ts +0 -113
- package/src/kandanTls.ts +0 -86
- package/src/localCapabilities.ts +0 -143
- package/src/localCodexMessageState.ts +0 -135
- package/src/localCodexTurnState.ts +0 -108
- package/src/localConfig.ts +0 -99
- package/src/localEditor.ts +0 -1061
- package/src/localEditorRuntime.ts +0 -717
- package/src/localForwarding.ts +0 -523
- package/src/oauth.ts +0 -425
- package/src/pendingKandanMessageQueue.ts +0 -109
- package/src/phoenix.ts +0 -359
- package/src/portForwardApproval.ts +0 -181
- package/src/portForwardWatcher.ts +0 -404
- package/src/protocol.ts +0 -321
- package/src/runner.ts +0 -943
- package/src/runnerConsoleReporter.ts +0 -142
- package/src/runnerLogger.ts +0 -50
- package/src/streamDeltaCoalescing.ts +0 -129
- package/src/streamDeltaQueue.ts +0 -102
package/src/localForwarding.ts
DELETED
|
@@ -1,523 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
- Date: 2026-04-26
|
|
3
|
-
Spec: plans/2026-04-26-local-runner-forwarding-and-editor-plan.md
|
|
4
|
-
Relationship: Implements the local side of bounded HTTP preview forwarding,
|
|
5
|
-
enforcing explicit allowed ports before the runner contacts loopback.
|
|
6
|
-
|
|
7
|
-
- Date: 2026-04-26
|
|
8
|
-
Spec: plans/2026-04-26-local-runner-subdomain-forwarding-epr.md
|
|
9
|
-
Relationship: Implements the runner side of local WebSocket forwarding for
|
|
10
|
-
isolated preview subdomains.
|
|
11
|
-
*/
|
|
12
|
-
import { gzipSync } from "node:zlib";
|
|
13
|
-
import { type JsonObject, type JsonValue, isJsonObject } from "./protocol";
|
|
14
|
-
import type { PhoenixClient } from "./phoenix";
|
|
15
|
-
|
|
16
|
-
const maxForwardBodyBytes = 64 * 1024 * 1024;
|
|
17
|
-
const gzipForwardThresholdBytes = 32 * 1024;
|
|
18
|
-
|
|
19
|
-
export type ForwardHttpRequestControl = {
|
|
20
|
-
readonly type: "forward_http_request";
|
|
21
|
-
readonly requestId: string;
|
|
22
|
-
readonly port: number;
|
|
23
|
-
readonly method: string;
|
|
24
|
-
readonly path: string;
|
|
25
|
-
readonly queryString?: string;
|
|
26
|
-
readonly headers?: JsonValue;
|
|
27
|
-
readonly bodyBase64?: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type ForwardHttpResponsePayload = JsonObject;
|
|
31
|
-
|
|
32
|
-
type RequestBodyDecision =
|
|
33
|
-
| { readonly ok: true; readonly body: Uint8Array | undefined }
|
|
34
|
-
| { readonly ok: false; readonly error: string };
|
|
35
|
-
|
|
36
|
-
export type ForwardWebSocketOpenControl = {
|
|
37
|
-
readonly type: "forward_websocket_open";
|
|
38
|
-
readonly socketId: string;
|
|
39
|
-
readonly port: number;
|
|
40
|
-
readonly path: string;
|
|
41
|
-
readonly queryString?: string;
|
|
42
|
-
readonly headers?: JsonValue;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export type ForwardWebSocketSendControl = {
|
|
46
|
-
readonly type: "forward_websocket_send";
|
|
47
|
-
readonly socketId: string;
|
|
48
|
-
readonly opcode: "text" | "binary" | "ping" | "pong";
|
|
49
|
-
readonly bodyBase64: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type ForwardWebSocketCloseControl = {
|
|
53
|
-
readonly type: "forward_websocket_close";
|
|
54
|
-
readonly socketId: string;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export type ForwardWebSocketControl =
|
|
58
|
-
| ForwardWebSocketOpenControl
|
|
59
|
-
| ForwardWebSocketSendControl
|
|
60
|
-
| ForwardWebSocketCloseControl;
|
|
61
|
-
|
|
62
|
-
export type ForwardWebSocketManager = {
|
|
63
|
-
readonly handle: (control: ForwardWebSocketControl) => void;
|
|
64
|
-
readonly close: () => void;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export async function handleForwardHttpRequest(
|
|
68
|
-
control: ForwardHttpRequestControl,
|
|
69
|
-
allowedPorts: readonly number[],
|
|
70
|
-
): Promise<ForwardHttpResponsePayload> {
|
|
71
|
-
if (!allowedPorts.includes(control.port)) {
|
|
72
|
-
return forwardError(control.requestId, "forward_port_not_allowed");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const bodyDecision = requestBody(control);
|
|
76
|
-
|
|
77
|
-
if (!bodyDecision.ok) {
|
|
78
|
-
return forwardError(control.requestId, bodyDecision.error);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
const request = {
|
|
83
|
-
method: control.method,
|
|
84
|
-
headers: requestHeaders(control.headers),
|
|
85
|
-
...(bodyDecision.body === undefined ? {} : { body: bodyDecision.body }),
|
|
86
|
-
};
|
|
87
|
-
const response = await fetchWithHttpsFallback(
|
|
88
|
-
control.port,
|
|
89
|
-
control.path,
|
|
90
|
-
control.queryString,
|
|
91
|
-
request,
|
|
92
|
-
);
|
|
93
|
-
const upstreamBuffer = Buffer.from(await response.arrayBuffer());
|
|
94
|
-
const patchedBody = patchForwardBody(
|
|
95
|
-
control.path,
|
|
96
|
-
response.headers,
|
|
97
|
-
upstreamBuffer,
|
|
98
|
-
);
|
|
99
|
-
const preparedResponse = prepareResponseBodyForChannel(
|
|
100
|
-
control,
|
|
101
|
-
response.headers,
|
|
102
|
-
patchedBody,
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
if (preparedResponse.body.byteLength > maxForwardBodyBytes) {
|
|
106
|
-
return forwardError(control.requestId, "forward_response_too_large");
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
requestId: control.requestId,
|
|
111
|
-
ok: true,
|
|
112
|
-
status: response.status,
|
|
113
|
-
headers: preparedResponse.headers,
|
|
114
|
-
bodyBase64: preparedResponse.body.toString("base64"),
|
|
115
|
-
};
|
|
116
|
-
} catch (_error) {
|
|
117
|
-
return forwardError(control.requestId, "forward_target_unavailable");
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function isForwardHttpRequestControl(control: {
|
|
122
|
-
readonly type: string;
|
|
123
|
-
}): control is ForwardHttpRequestControl {
|
|
124
|
-
return control.type === "forward_http_request";
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function isForwardWebSocketControl(control: {
|
|
128
|
-
readonly type: string;
|
|
129
|
-
}): control is ForwardWebSocketControl {
|
|
130
|
-
return (
|
|
131
|
-
control.type === "forward_websocket_open" ||
|
|
132
|
-
control.type === "forward_websocket_send" ||
|
|
133
|
-
control.type === "forward_websocket_close"
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function createForwardWebSocketManager(
|
|
138
|
-
kandan: Pick<PhoenixClient, "push">,
|
|
139
|
-
topic: string,
|
|
140
|
-
allowedPorts: () => readonly number[],
|
|
141
|
-
): ForwardWebSocketManager {
|
|
142
|
-
const sockets = new Map<string, WebSocket>();
|
|
143
|
-
|
|
144
|
-
const pushEvent = (payload: JsonObject) =>
|
|
145
|
-
kandan
|
|
146
|
-
.push(topic, "forward:websocket_event", payload)
|
|
147
|
-
.catch(() => undefined);
|
|
148
|
-
|
|
149
|
-
const closeSocket = (socketId: string) => {
|
|
150
|
-
const socket = sockets.get(socketId);
|
|
151
|
-
sockets.delete(socketId);
|
|
152
|
-
socket?.close();
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
handle: (control) => {
|
|
157
|
-
switch (control.type) {
|
|
158
|
-
case "forward_websocket_open": {
|
|
159
|
-
if (!allowedPorts().includes(control.port)) {
|
|
160
|
-
void pushEvent({
|
|
161
|
-
socketId: control.socketId,
|
|
162
|
-
type: "error",
|
|
163
|
-
error: "forward_port_not_allowed",
|
|
164
|
-
});
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
openLocalWebSocket(control, sockets, pushEvent, "ws");
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
case "forward_websocket_send": {
|
|
172
|
-
const socket = sockets.get(control.socketId);
|
|
173
|
-
if (socket === undefined || socket.readyState !== WebSocket.OPEN) {
|
|
174
|
-
void pushEvent({
|
|
175
|
-
socketId: control.socketId,
|
|
176
|
-
type: "error",
|
|
177
|
-
error: "websocket_not_open",
|
|
178
|
-
});
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const body = Buffer.from(control.bodyBase64, "base64");
|
|
183
|
-
switch (control.opcode) {
|
|
184
|
-
case "text":
|
|
185
|
-
socket.send(body.toString());
|
|
186
|
-
return;
|
|
187
|
-
case "binary":
|
|
188
|
-
case "ping":
|
|
189
|
-
case "pong":
|
|
190
|
-
socket.send(body);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
case "forward_websocket_close":
|
|
195
|
-
closeSocket(control.socketId);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
close: () => {
|
|
200
|
-
for (const socketId of sockets.keys()) {
|
|
201
|
-
closeSocket(socketId);
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function openLocalWebSocket(
|
|
208
|
-
control: ForwardWebSocketOpenControl,
|
|
209
|
-
sockets: Map<string, WebSocket>,
|
|
210
|
-
pushEvent: (payload: JsonObject) => Promise<void | JsonValue>,
|
|
211
|
-
scheme: "ws" | "wss",
|
|
212
|
-
): void {
|
|
213
|
-
let opened = false;
|
|
214
|
-
const url = localForwardUrl(
|
|
215
|
-
scheme === "ws" ? "http" : "https",
|
|
216
|
-
control.port,
|
|
217
|
-
control.path,
|
|
218
|
-
control.queryString,
|
|
219
|
-
).replace(/^http/, scheme);
|
|
220
|
-
const protocols = webSocketProtocols(control.headers);
|
|
221
|
-
const websocket =
|
|
222
|
-
protocols === undefined ? new WebSocket(url) : new WebSocket(url, protocols);
|
|
223
|
-
sockets.set(control.socketId, websocket);
|
|
224
|
-
websocket.addEventListener("open", () => {
|
|
225
|
-
opened = true;
|
|
226
|
-
void pushEvent({ socketId: control.socketId, type: "open" });
|
|
227
|
-
});
|
|
228
|
-
websocket.addEventListener("message", (event) => {
|
|
229
|
-
const body =
|
|
230
|
-
typeof event.data === "string"
|
|
231
|
-
? Buffer.from(event.data)
|
|
232
|
-
: Buffer.from(event.data as ArrayBuffer);
|
|
233
|
-
void pushEvent({
|
|
234
|
-
socketId: control.socketId,
|
|
235
|
-
type: "message",
|
|
236
|
-
opcode: typeof event.data === "string" ? "text" : "binary",
|
|
237
|
-
bodyBase64: body.toString("base64"),
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
websocket.addEventListener("close", (event) => {
|
|
241
|
-
sockets.delete(control.socketId);
|
|
242
|
-
void pushEvent({
|
|
243
|
-
socketId: control.socketId,
|
|
244
|
-
type: "close",
|
|
245
|
-
code: event.code,
|
|
246
|
-
reason: event.reason,
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
websocket.addEventListener("error", () => {
|
|
250
|
-
sockets.delete(control.socketId);
|
|
251
|
-
if (!opened && scheme === "ws") {
|
|
252
|
-
openLocalWebSocket(control, sockets, pushEvent, "wss");
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
void pushEvent({
|
|
257
|
-
socketId: control.socketId,
|
|
258
|
-
type: "error",
|
|
259
|
-
error: "websocket_error",
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function webSocketProtocols(headers: JsonValue | undefined): string[] | undefined {
|
|
265
|
-
if (!Array.isArray(headers)) {
|
|
266
|
-
return undefined;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const protocols = headers.flatMap(header => {
|
|
270
|
-
if (
|
|
271
|
-
!isWireHeader(header) ||
|
|
272
|
-
header.name.toLowerCase() !== "sec-websocket-protocol"
|
|
273
|
-
) {
|
|
274
|
-
return [];
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return header.value
|
|
278
|
-
.split(",")
|
|
279
|
-
.map(protocol => protocol.trim())
|
|
280
|
-
.filter(protocol => protocol !== "");
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
return protocols.length === 0 ? undefined : protocols;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function localForwardUrl(
|
|
287
|
-
scheme: "http" | "https",
|
|
288
|
-
port: number,
|
|
289
|
-
path: string,
|
|
290
|
-
queryString: string | undefined,
|
|
291
|
-
): string {
|
|
292
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
293
|
-
const url = new URL(`${scheme}://127.0.0.1:${port}${normalizedPath}`);
|
|
294
|
-
|
|
295
|
-
if (queryString !== undefined && queryString.trim() !== "") {
|
|
296
|
-
url.search = queryString;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return url.toString();
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
async function fetchWithHttpsFallback(
|
|
303
|
-
port: number,
|
|
304
|
-
path: string,
|
|
305
|
-
queryString: string | undefined,
|
|
306
|
-
request: RequestInit,
|
|
307
|
-
): Promise<Response> {
|
|
308
|
-
try {
|
|
309
|
-
return await fetch(
|
|
310
|
-
localForwardUrl("http", port, path, queryString),
|
|
311
|
-
request,
|
|
312
|
-
);
|
|
313
|
-
} catch (httpError) {
|
|
314
|
-
try {
|
|
315
|
-
return await fetch(
|
|
316
|
-
localForwardUrl("https", port, path, queryString),
|
|
317
|
-
request,
|
|
318
|
-
);
|
|
319
|
-
} catch (_httpsError) {
|
|
320
|
-
throw httpError;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function requestBody(
|
|
326
|
-
control: ForwardHttpRequestControl,
|
|
327
|
-
): RequestBodyDecision {
|
|
328
|
-
if (control.method === "GET" || control.method === "HEAD") {
|
|
329
|
-
return { ok: true, body: undefined };
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (control.bodyBase64 === undefined || control.bodyBase64 === "") {
|
|
333
|
-
return { ok: true, body: undefined };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const body = decodeBase64Body(control.bodyBase64);
|
|
337
|
-
|
|
338
|
-
if (body === undefined) {
|
|
339
|
-
return { ok: false, error: "invalid_forward_request" };
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (body.byteLength > maxForwardBodyBytes) {
|
|
343
|
-
return { ok: false, error: "forward_body_too_large" };
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return { ok: true, body };
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function decodeBase64Body(value: string): Uint8Array | undefined {
|
|
350
|
-
const normalized = value.trim();
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
normalized.length % 4 === 1 ||
|
|
354
|
-
!/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)
|
|
355
|
-
) {
|
|
356
|
-
return undefined;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const body = Buffer.from(normalized, "base64");
|
|
360
|
-
const canonicalBody = body.toString("base64").replace(/=+$/, "");
|
|
361
|
-
const canonicalInput = normalized.replace(/=+$/, "");
|
|
362
|
-
|
|
363
|
-
return canonicalBody === canonicalInput ? body : undefined;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function requestHeaders(headers: JsonValue | undefined): Headers {
|
|
367
|
-
const result = new Headers();
|
|
368
|
-
|
|
369
|
-
if (!Array.isArray(headers)) {
|
|
370
|
-
return result;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
for (const header of headers) {
|
|
374
|
-
if (isHeader(header)) {
|
|
375
|
-
result.set(header.name, header.value);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
return result;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function responseHeaders(headers: Headers): JsonValue[] {
|
|
383
|
-
return Array.from(headers.entries())
|
|
384
|
-
.filter(([name]) => !blockedForwardHeaderNames.has(name.toLowerCase()))
|
|
385
|
-
.slice(0, 40)
|
|
386
|
-
.map(([name, value]) => ({ name, value }));
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function patchForwardBody(path: string, headers: Headers, body: Buffer): Buffer {
|
|
390
|
-
if (!codeServerWorkbenchScript(path, headers)) {
|
|
391
|
-
return body;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return Buffer.from(
|
|
395
|
-
body
|
|
396
|
-
.toString("utf8")
|
|
397
|
-
.replace(
|
|
398
|
-
"message:h(3783,null,\"code-server\")",
|
|
399
|
-
"message:h(3783,null,\"Kandan\")",
|
|
400
|
-
),
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function prepareResponseBodyForChannel(
|
|
405
|
-
control: ForwardHttpRequestControl,
|
|
406
|
-
headers: Headers,
|
|
407
|
-
body: Buffer,
|
|
408
|
-
): { readonly body: Buffer; readonly headers: JsonValue[] } {
|
|
409
|
-
const forwardedHeaders = responseHeaders(headers);
|
|
410
|
-
|
|
411
|
-
if (!shouldGzipResponse(control, headers, body)) {
|
|
412
|
-
return { body, headers: forwardedHeaders };
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return {
|
|
416
|
-
body: gzipSync(body),
|
|
417
|
-
headers: [
|
|
418
|
-
...forwardedHeaders,
|
|
419
|
-
{ name: "content-encoding", value: "gzip" },
|
|
420
|
-
{ name: "vary", value: "accept-encoding" },
|
|
421
|
-
],
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function shouldGzipResponse(
|
|
426
|
-
control: ForwardHttpRequestControl,
|
|
427
|
-
headers: Headers,
|
|
428
|
-
body: Buffer,
|
|
429
|
-
): boolean {
|
|
430
|
-
if (
|
|
431
|
-
control.method === "HEAD" ||
|
|
432
|
-
body.byteLength < gzipForwardThresholdBytes ||
|
|
433
|
-
!clientAcceptsGzip(control.headers)
|
|
434
|
-
) {
|
|
435
|
-
return false;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
return compressibleResponse(control.path, headers);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function clientAcceptsGzip(headers: JsonValue | undefined): boolean {
|
|
442
|
-
if (!Array.isArray(headers)) {
|
|
443
|
-
return false;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
return headers.some(
|
|
447
|
-
header =>
|
|
448
|
-
isWireHeader(header) &&
|
|
449
|
-
header.name.toLowerCase() === "accept-encoding" &&
|
|
450
|
-
header.value
|
|
451
|
-
.toLowerCase()
|
|
452
|
-
.split(",")
|
|
453
|
-
.map(value => value.trim())
|
|
454
|
-
.some(value => value === "gzip" || value.startsWith("gzip;")),
|
|
455
|
-
);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function compressibleResponse(path: string, headers: Headers): boolean {
|
|
459
|
-
const contentType = headers.get("content-type")?.toLowerCase() ?? "";
|
|
460
|
-
|
|
461
|
-
if (
|
|
462
|
-
contentType.startsWith("text/") ||
|
|
463
|
-
contentType.includes("javascript") ||
|
|
464
|
-
contentType.includes("json") ||
|
|
465
|
-
contentType.includes("xml")
|
|
466
|
-
) {
|
|
467
|
-
return true;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return /\.(?:css|html|js|json|map|mjs|svg|txt)$/i.test(path);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function codeServerWorkbenchScript(path: string, headers: Headers): boolean {
|
|
474
|
-
return (
|
|
475
|
-
path.endsWith("/out/vs/code/browser/workbench/workbench.js") &&
|
|
476
|
-
(headers.get("content-type") ?? "").toLowerCase().includes("javascript")
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function isHeader(
|
|
481
|
-
value: JsonValue,
|
|
482
|
-
): value is { readonly name: string; readonly value: string } {
|
|
483
|
-
return (
|
|
484
|
-
isWireHeader(value) &&
|
|
485
|
-
!blockedForwardHeaderNames.has(value.name.toLowerCase())
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function isWireHeader(
|
|
490
|
-
value: JsonValue,
|
|
491
|
-
): value is { readonly name: string; readonly value: string } {
|
|
492
|
-
return (
|
|
493
|
-
isJsonObject(value) &&
|
|
494
|
-
typeof value.name === "string" &&
|
|
495
|
-
typeof value.value === "string"
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function forwardError(
|
|
500
|
-
requestId: string,
|
|
501
|
-
error: string,
|
|
502
|
-
): ForwardHttpResponsePayload {
|
|
503
|
-
return {
|
|
504
|
-
requestId,
|
|
505
|
-
ok: false,
|
|
506
|
-
error,
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
const blockedForwardHeaderNames = new Set([
|
|
511
|
-
"accept-encoding",
|
|
512
|
-
"connection",
|
|
513
|
-
"content-encoding",
|
|
514
|
-
"content-length",
|
|
515
|
-
"host",
|
|
516
|
-
"keep-alive",
|
|
517
|
-
"proxy-authenticate",
|
|
518
|
-
"proxy-authorization",
|
|
519
|
-
"te",
|
|
520
|
-
"trailer",
|
|
521
|
-
"transfer-encoding",
|
|
522
|
-
"upgrade",
|
|
523
|
-
]);
|