@openhoo/hoopilot 0.8.3 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +234 -67
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +203 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +203 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -276,6 +276,25 @@ function completionStreamFromChatStream(chatStream) {
|
|
|
276
276
|
}
|
|
277
277
|
});
|
|
278
278
|
}
|
|
279
|
+
function completionSseTextFromChatSseText(text) {
|
|
280
|
+
const chunks = [];
|
|
281
|
+
let sawTerminalEvent = false;
|
|
282
|
+
const enqueue = (data) => {
|
|
283
|
+
chunks.push(encodeDataSse(data));
|
|
284
|
+
};
|
|
285
|
+
const markTerminal = () => {
|
|
286
|
+
sawTerminalEvent = true;
|
|
287
|
+
};
|
|
288
|
+
for (const block of text.split(/\r?\n\r?\n/)) {
|
|
289
|
+
if (block.trim()) {
|
|
290
|
+
processCompletionSseBlock(block, enqueue, markTerminal);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!sawTerminalEvent) {
|
|
294
|
+
enqueue("[DONE]");
|
|
295
|
+
}
|
|
296
|
+
return chunks.join("");
|
|
297
|
+
}
|
|
279
298
|
function normalizeModelsResponse(upstream) {
|
|
280
299
|
const record = asRecord(upstream);
|
|
281
300
|
const data = Array.isArray(record.data) ? record.data : Array.isArray(upstream) ? upstream : [];
|
|
@@ -994,16 +1013,7 @@ function responsesStreamToAnthropicStream(stream, options) {
|
|
|
994
1013
|
const decoder = new TextDecoder();
|
|
995
1014
|
const encoder = new TextEncoder();
|
|
996
1015
|
let buffer = "";
|
|
997
|
-
const state =
|
|
998
|
-
blocks: /* @__PURE__ */ new Map(),
|
|
999
|
-
completed: false,
|
|
1000
|
-
messageId: options.messageId ?? `msg_${randomId2()}`,
|
|
1001
|
-
model: options.model,
|
|
1002
|
-
nextBlockIndex: 0,
|
|
1003
|
-
sawToolUse: false,
|
|
1004
|
-
started: false,
|
|
1005
|
-
usage: anthropicUsage(void 0)
|
|
1006
|
-
};
|
|
1016
|
+
const state = createAnthropicStreamState(options);
|
|
1007
1017
|
return new ReadableStream({
|
|
1008
1018
|
async start(controller) {
|
|
1009
1019
|
const enqueue = (event, data) => {
|
|
@@ -1039,6 +1049,20 @@ function responsesStreamToAnthropicStream(stream, options) {
|
|
|
1039
1049
|
}
|
|
1040
1050
|
});
|
|
1041
1051
|
}
|
|
1052
|
+
function responsesSseTextToAnthropicSseText(text, options) {
|
|
1053
|
+
const chunks = [];
|
|
1054
|
+
const state = createAnthropicStreamState(options);
|
|
1055
|
+
const enqueue = (event, data) => {
|
|
1056
|
+
chunks.push(encodeSse2(event, data));
|
|
1057
|
+
};
|
|
1058
|
+
for (const block of text.split(/\r?\n\r?\n/)) {
|
|
1059
|
+
if (block.trim()) {
|
|
1060
|
+
processResponsesSseBlock(block, state, enqueue);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
finishAnthropicStream(state, enqueue);
|
|
1064
|
+
return chunks.join("");
|
|
1065
|
+
}
|
|
1042
1066
|
function estimateAnthropicMessageTokens(request) {
|
|
1043
1067
|
const chars = estimatedTextSize(request.system) + estimatedTextSize(request.messages) + estimatedTextSize(request.tools) + estimatedTextSize(request.tool_choice) + estimatedTextSize(request.thinking);
|
|
1044
1068
|
const messageCount = Array.isArray(request.messages) ? request.messages.length : 1;
|
|
@@ -1049,6 +1073,18 @@ function estimateAnthropicMessageTokens(request) {
|
|
|
1049
1073
|
total_tokens: inputTokens
|
|
1050
1074
|
};
|
|
1051
1075
|
}
|
|
1076
|
+
function createAnthropicStreamState(options) {
|
|
1077
|
+
return {
|
|
1078
|
+
blocks: /* @__PURE__ */ new Map(),
|
|
1079
|
+
completed: false,
|
|
1080
|
+
messageId: options.messageId ?? `msg_${randomId2()}`,
|
|
1081
|
+
model: options.model,
|
|
1082
|
+
nextBlockIndex: 0,
|
|
1083
|
+
sawToolUse: false,
|
|
1084
|
+
started: false,
|
|
1085
|
+
usage: anthropicUsage(void 0)
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1052
1088
|
function anthropicMessagesToResponsesInput(messages) {
|
|
1053
1089
|
if (!Array.isArray(messages)) {
|
|
1054
1090
|
throw new AnthropicCompatibilityError("Anthropic Messages requests require messages[].");
|
|
@@ -2466,6 +2502,20 @@ function observeResponseUsage(response, fallbackModel, onUsage, signal) {
|
|
|
2466
2502
|
statusText: response.statusText
|
|
2467
2503
|
});
|
|
2468
2504
|
}
|
|
2505
|
+
function recordResponseTextUsage(text, isSse, fallbackModel, onUsage) {
|
|
2506
|
+
const accumulator = createUsageAccumulator(fallbackModel, onUsage);
|
|
2507
|
+
if (isSse) {
|
|
2508
|
+
for (const line of text.split(/\r?\n/)) {
|
|
2509
|
+
considerSseLine(line, accumulator.consider);
|
|
2510
|
+
}
|
|
2511
|
+
} else {
|
|
2512
|
+
const parsed = safeParse(text);
|
|
2513
|
+
if (parsed !== void 0) {
|
|
2514
|
+
accumulator.consider(parsed);
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
accumulator.finish();
|
|
2518
|
+
}
|
|
2469
2519
|
async function consumeUsage(stream, isSse, fallbackModel, onUsage, signal) {
|
|
2470
2520
|
const reader = stream.getReader();
|
|
2471
2521
|
const onAbort = () => {
|
|
@@ -2479,22 +2529,10 @@ async function consumeUsage(stream, isSse, fallbackModel, onUsage, signal) {
|
|
|
2479
2529
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2480
2530
|
}
|
|
2481
2531
|
const decoder = new TextDecoder();
|
|
2482
|
-
|
|
2483
|
-
let usage;
|
|
2532
|
+
const accumulator = createUsageAccumulator(fallbackModel, onUsage);
|
|
2484
2533
|
let buffer = "";
|
|
2485
2534
|
let bufferedBytes = 0;
|
|
2486
2535
|
let overflowed = false;
|
|
2487
|
-
const consider = (payload) => {
|
|
2488
|
-
const record = asRecord(payload);
|
|
2489
|
-
const found = extractTokenUsage(record.usage) ?? extractTokenUsage(asRecord(record.response).usage);
|
|
2490
|
-
if (found) {
|
|
2491
|
-
usage = found;
|
|
2492
|
-
}
|
|
2493
|
-
const candidateModel = modelText(record.model) || modelText(asRecord(record.response).model);
|
|
2494
|
-
if (candidateModel) {
|
|
2495
|
-
model = candidateModel;
|
|
2496
|
-
}
|
|
2497
|
-
};
|
|
2498
2536
|
try {
|
|
2499
2537
|
while (true) {
|
|
2500
2538
|
const result = await reader.read();
|
|
@@ -2507,7 +2545,7 @@ async function consumeUsage(stream, isSse, fallbackModel, onUsage, signal) {
|
|
|
2507
2545
|
const lines = buffer.split(/\r?\n/);
|
|
2508
2546
|
buffer = lines.pop() ?? "";
|
|
2509
2547
|
for (const line of lines) {
|
|
2510
|
-
considerSseLine(line, consider);
|
|
2548
|
+
considerSseLine(line, accumulator.consider);
|
|
2511
2549
|
}
|
|
2512
2550
|
if (buffer.length > USAGE_BUFFER_LIMIT_BYTES) {
|
|
2513
2551
|
buffer = "";
|
|
@@ -2525,21 +2563,41 @@ async function consumeUsage(stream, isSse, fallbackModel, onUsage, signal) {
|
|
|
2525
2563
|
const finalBuffer = buffer + decoder.decode();
|
|
2526
2564
|
if (isSse) {
|
|
2527
2565
|
if (finalBuffer) {
|
|
2528
|
-
considerSseLine(finalBuffer, consider);
|
|
2566
|
+
considerSseLine(finalBuffer, accumulator.consider);
|
|
2529
2567
|
}
|
|
2530
2568
|
} else if (!overflowed && finalBuffer) {
|
|
2531
2569
|
const parsed = safeParse(finalBuffer);
|
|
2532
2570
|
if (parsed !== void 0) {
|
|
2533
|
-
consider(parsed);
|
|
2571
|
+
accumulator.consider(parsed);
|
|
2534
2572
|
}
|
|
2535
2573
|
}
|
|
2536
2574
|
} finally {
|
|
2537
2575
|
signal?.removeEventListener("abort", onAbort);
|
|
2538
2576
|
reader.releaseLock();
|
|
2539
2577
|
}
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2578
|
+
accumulator.finish();
|
|
2579
|
+
}
|
|
2580
|
+
function createUsageAccumulator(fallbackModel, onUsage) {
|
|
2581
|
+
let model = fallbackModel;
|
|
2582
|
+
let usage;
|
|
2583
|
+
return {
|
|
2584
|
+
consider(payload) {
|
|
2585
|
+
const record = asRecord(payload);
|
|
2586
|
+
const found = extractTokenUsage(record.usage) ?? extractTokenUsage(asRecord(record.response).usage);
|
|
2587
|
+
if (found) {
|
|
2588
|
+
usage = found;
|
|
2589
|
+
}
|
|
2590
|
+
const candidateModel = modelText(record.model) || modelText(asRecord(record.response).model);
|
|
2591
|
+
if (candidateModel) {
|
|
2592
|
+
model = candidateModel;
|
|
2593
|
+
}
|
|
2594
|
+
},
|
|
2595
|
+
finish() {
|
|
2596
|
+
if (usage) {
|
|
2597
|
+
onUsage(model, usage);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2543
2601
|
}
|
|
2544
2602
|
function considerSseLine(line, consider) {
|
|
2545
2603
|
const trimmed = line.trim();
|
|
@@ -2586,6 +2644,10 @@ function formatNumber(value) {
|
|
|
2586
2644
|
return Number.isInteger(value) ? value.toString() : String(value);
|
|
2587
2645
|
}
|
|
2588
2646
|
|
|
2647
|
+
// src/version.ts
|
|
2648
|
+
var BAKED_VERSION = typeof HOOPILOT_VERSION !== "undefined" ? HOOPILOT_VERSION : void 0;
|
|
2649
|
+
var IS_STANDALONE_BINARY = BAKED_VERSION !== void 0;
|
|
2650
|
+
|
|
2589
2651
|
// src/server.ts
|
|
2590
2652
|
var DEFAULT_HOST = "127.0.0.1";
|
|
2591
2653
|
var DEFAULT_PORT = 4141;
|
|
@@ -2609,6 +2671,8 @@ function createHoopilotHandler(options = {}) {
|
|
|
2609
2671
|
const metrics = options.metrics ?? new MetricsRegistry();
|
|
2610
2672
|
const readUsage = createUsageReader(client, metrics);
|
|
2611
2673
|
const recordTokens = (model, usage) => metrics.recordTokens(model, usage);
|
|
2674
|
+
const streamingProxyMode = resolveStreamingProxyMode(options);
|
|
2675
|
+
const bufferProxyBodies = shouldBufferProxyBodies(streamingProxyMode);
|
|
2612
2676
|
return async (request) => {
|
|
2613
2677
|
const startedAt = performance.now();
|
|
2614
2678
|
const url = new URL(request.url);
|
|
@@ -2628,7 +2692,9 @@ function createHoopilotHandler(options = {}) {
|
|
|
2628
2692
|
metrics,
|
|
2629
2693
|
requestId,
|
|
2630
2694
|
route,
|
|
2631
|
-
startedAt
|
|
2695
|
+
startedAt,
|
|
2696
|
+
closeConnection: bufferProxyBodies,
|
|
2697
|
+
trackStreamingBody: !bufferProxyBodies
|
|
2632
2698
|
});
|
|
2633
2699
|
const browserOrigin = forbiddenBrowserOrigin(request, apiKey);
|
|
2634
2700
|
if (browserOrigin) {
|
|
@@ -2663,7 +2729,14 @@ function createHoopilotHandler(options = {}) {
|
|
|
2663
2729
|
}
|
|
2664
2730
|
if (request.method === "POST" && apiPath === "/v1/messages") {
|
|
2665
2731
|
return finish(
|
|
2666
|
-
await handleAnthropicMessages(
|
|
2732
|
+
await handleAnthropicMessages(
|
|
2733
|
+
client,
|
|
2734
|
+
metrics,
|
|
2735
|
+
recordTokens,
|
|
2736
|
+
request,
|
|
2737
|
+
requestLogger,
|
|
2738
|
+
bufferProxyBodies
|
|
2739
|
+
)
|
|
2667
2740
|
);
|
|
2668
2741
|
}
|
|
2669
2742
|
if (request.method === "POST" && apiPath === "/v1/messages/count_tokens") {
|
|
@@ -2671,16 +2744,39 @@ function createHoopilotHandler(options = {}) {
|
|
|
2671
2744
|
}
|
|
2672
2745
|
if (request.method === "POST" && apiPath === "/v1/chat/completions") {
|
|
2673
2746
|
return finish(
|
|
2674
|
-
await handleChatCompletions(
|
|
2747
|
+
await handleChatCompletions(
|
|
2748
|
+
client,
|
|
2749
|
+
metrics,
|
|
2750
|
+
recordTokens,
|
|
2751
|
+
request,
|
|
2752
|
+
requestLogger,
|
|
2753
|
+
bufferProxyBodies
|
|
2754
|
+
)
|
|
2675
2755
|
);
|
|
2676
2756
|
}
|
|
2677
2757
|
if (request.method === "POST" && apiPath === "/v1/completions") {
|
|
2678
2758
|
return finish(
|
|
2679
|
-
await handleCompletions(
|
|
2759
|
+
await handleCompletions(
|
|
2760
|
+
client,
|
|
2761
|
+
metrics,
|
|
2762
|
+
recordTokens,
|
|
2763
|
+
request,
|
|
2764
|
+
requestLogger,
|
|
2765
|
+
bufferProxyBodies
|
|
2766
|
+
)
|
|
2680
2767
|
);
|
|
2681
2768
|
}
|
|
2682
2769
|
if (request.method === "POST" && apiPath === "/v1/responses") {
|
|
2683
|
-
return finish(
|
|
2770
|
+
return finish(
|
|
2771
|
+
await handleResponses(
|
|
2772
|
+
client,
|
|
2773
|
+
metrics,
|
|
2774
|
+
recordTokens,
|
|
2775
|
+
request,
|
|
2776
|
+
requestLogger,
|
|
2777
|
+
bufferProxyBodies
|
|
2778
|
+
)
|
|
2779
|
+
);
|
|
2684
2780
|
}
|
|
2685
2781
|
return finish(jsonError(404, "not_found", `No route for ${request.method} ${url.pathname}.`));
|
|
2686
2782
|
} catch (error) {
|
|
@@ -2745,7 +2841,7 @@ function startHoopilotServer(options = {}) {
|
|
|
2745
2841
|
url: `http://${urlHost(host)}:${server.port}`
|
|
2746
2842
|
};
|
|
2747
2843
|
}
|
|
2748
|
-
async function handleAnthropicMessages(client, metrics, recordTokens, request, logger) {
|
|
2844
|
+
async function handleAnthropicMessages(client, metrics, recordTokens, request, logger, bufferProxyBodies) {
|
|
2749
2845
|
const anthropicRequest = await readJson(request);
|
|
2750
2846
|
const responsesRequest = anthropicMessagesToResponsesRequest(anthropicRequest);
|
|
2751
2847
|
const upstream = await client.responses(JSON.stringify(responsesRequest), request.signal);
|
|
@@ -2756,6 +2852,13 @@ async function handleAnthropicMessages(client, metrics, recordTokens, request, l
|
|
|
2756
2852
|
logUpstreamSuccess(logger, "/responses", upstream.status);
|
|
2757
2853
|
const model = normalizeRequestedModel(responsesRequest.model);
|
|
2758
2854
|
if (isStreamingResponse(upstream) && upstream.body) {
|
|
2855
|
+
if (bufferProxyBodies) {
|
|
2856
|
+
const text = await upstream.text();
|
|
2857
|
+
recordResponseTextUsage(text, true, model, recordTokens);
|
|
2858
|
+
return proxyResponse(
|
|
2859
|
+
responseFromText(upstream, responsesSseTextToAnthropicSseText(text, { model }))
|
|
2860
|
+
);
|
|
2861
|
+
}
|
|
2759
2862
|
const observed = observeResponseUsage(upstream, model, recordTokens, request.signal);
|
|
2760
2863
|
if (!observed.body) {
|
|
2761
2864
|
return proxyResponse(observed);
|
|
@@ -2799,7 +2902,7 @@ async function handleModels(client, metrics, signal, logger) {
|
|
|
2799
2902
|
logUpstreamSuccess(logger, "/models", upstream.status);
|
|
2800
2903
|
return jsonResponse(normalizeModelsResponse(await upstream.json()));
|
|
2801
2904
|
}
|
|
2802
|
-
async function handleChatCompletions(client, metrics, recordTokens, request, logger) {
|
|
2905
|
+
async function handleChatCompletions(client, metrics, recordTokens, request, logger, bufferProxyBodies) {
|
|
2803
2906
|
const chatRequest = normalizeChatCompletionRequest(await readJson(request));
|
|
2804
2907
|
const upstream = await client.chatCompletions(chatRequest, request.signal);
|
|
2805
2908
|
metrics.recordUpstream("/chat/completions", upstream.ok);
|
|
@@ -2808,9 +2911,17 @@ async function handleChatCompletions(client, metrics, recordTokens, request, log
|
|
|
2808
2911
|
}
|
|
2809
2912
|
logUpstreamSuccess(logger, "/chat/completions", upstream.status);
|
|
2810
2913
|
const model = normalizeRequestedModel(chatRequest.model);
|
|
2811
|
-
return proxyResponse(
|
|
2914
|
+
return proxyResponse(
|
|
2915
|
+
await responseWithObservedUsage(
|
|
2916
|
+
upstream,
|
|
2917
|
+
model,
|
|
2918
|
+
recordTokens,
|
|
2919
|
+
request.signal,
|
|
2920
|
+
bufferProxyBodies
|
|
2921
|
+
)
|
|
2922
|
+
);
|
|
2812
2923
|
}
|
|
2813
|
-
async function handleCompletions(client, metrics, recordTokens, request, logger) {
|
|
2924
|
+
async function handleCompletions(client, metrics, recordTokens, request, logger, bufferProxyBodies) {
|
|
2814
2925
|
const body = await readJson(request);
|
|
2815
2926
|
const upstream = await client.chatCompletions(
|
|
2816
2927
|
completionsRequestToChatCompletion(body),
|
|
@@ -2823,6 +2934,12 @@ async function handleCompletions(client, metrics, recordTokens, request, logger)
|
|
|
2823
2934
|
logUpstreamSuccess(logger, "/chat/completions", upstream.status);
|
|
2824
2935
|
const model = normalizeRequestedModel(body.model);
|
|
2825
2936
|
if (isStreamingResponse(upstream) && upstream.body) {
|
|
2937
|
+
if (bufferProxyBodies) {
|
|
2938
|
+
const upstreamText = await upstream.text();
|
|
2939
|
+
recordResponseTextUsage(upstreamText, true, model, recordTokens);
|
|
2940
|
+
const text = completionSseTextFromChatSseText(upstreamText);
|
|
2941
|
+
return proxyResponse(responseFromText(upstream, text));
|
|
2942
|
+
}
|
|
2826
2943
|
return proxyResponse(
|
|
2827
2944
|
observeResponseUsage(
|
|
2828
2945
|
new Response(completionStreamFromChatStream(upstream.body), {
|
|
@@ -2844,7 +2961,7 @@ async function handleCompletions(client, metrics, recordTokens, request, logger)
|
|
|
2844
2961
|
}
|
|
2845
2962
|
return jsonResponse(chatCompletionToCompletion(completion));
|
|
2846
2963
|
}
|
|
2847
|
-
async function handleResponses(client, metrics, recordTokens, request, logger) {
|
|
2964
|
+
async function handleResponses(client, metrics, recordTokens, request, logger, bufferProxyBodies) {
|
|
2848
2965
|
const body = await readJsonText(request);
|
|
2849
2966
|
const upstream = await client.responses(body, request.signal);
|
|
2850
2967
|
metrics.recordUpstream("/responses", upstream.ok);
|
|
@@ -2853,7 +2970,31 @@ async function handleResponses(client, metrics, recordTokens, request, logger) {
|
|
|
2853
2970
|
}
|
|
2854
2971
|
logUpstreamSuccess(logger, "/responses", upstream.status);
|
|
2855
2972
|
const model = normalizeRequestedModel(asRecord(safeParseJson(body)).model);
|
|
2856
|
-
return proxyResponse(
|
|
2973
|
+
return proxyResponse(
|
|
2974
|
+
await responseWithObservedUsage(
|
|
2975
|
+
upstream,
|
|
2976
|
+
model,
|
|
2977
|
+
recordTokens,
|
|
2978
|
+
request.signal,
|
|
2979
|
+
bufferProxyBodies
|
|
2980
|
+
)
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
async function responseWithObservedUsage(response, fallbackModel, recordTokens, signal, bufferBody) {
|
|
2984
|
+
const isSse = isStreamingResponse(response);
|
|
2985
|
+
if (bufferBody && response.body) {
|
|
2986
|
+
const text = await response.text();
|
|
2987
|
+
recordResponseTextUsage(text, isSse, fallbackModel, recordTokens);
|
|
2988
|
+
return responseFromText(response, text);
|
|
2989
|
+
}
|
|
2990
|
+
return observeResponseUsage(response, fallbackModel, recordTokens, signal);
|
|
2991
|
+
}
|
|
2992
|
+
function responseFromText(source, text) {
|
|
2993
|
+
return new Response(text, {
|
|
2994
|
+
headers: source.headers,
|
|
2995
|
+
status: source.status,
|
|
2996
|
+
statusText: source.statusText
|
|
2997
|
+
});
|
|
2857
2998
|
}
|
|
2858
2999
|
async function proxyError(upstream, logger) {
|
|
2859
3000
|
const text = await upstream.text();
|
|
@@ -3045,8 +3186,24 @@ function serverLogger(options) {
|
|
|
3045
3186
|
}
|
|
3046
3187
|
return noopLogger;
|
|
3047
3188
|
}
|
|
3189
|
+
function resolveStreamingProxyMode(options) {
|
|
3190
|
+
const value = options.streamingProxyMode ?? envValue(options.env?.HOOPILOT_STREAM_MODE) ?? envValue(options.env?.HOOPILOT_STREAMING_PROXY_MODE) ?? "auto";
|
|
3191
|
+
if (value === "auto" || value === "buffer" || value === "live") {
|
|
3192
|
+
return value;
|
|
3193
|
+
}
|
|
3194
|
+
throw new Error(`Invalid stream mode: ${value}. Expected auto, live, or buffer.`);
|
|
3195
|
+
}
|
|
3196
|
+
function shouldBufferProxyBodies(mode) {
|
|
3197
|
+
if (mode === "buffer") {
|
|
3198
|
+
return true;
|
|
3199
|
+
}
|
|
3200
|
+
if (mode === "live") {
|
|
3201
|
+
return false;
|
|
3202
|
+
}
|
|
3203
|
+
return process.platform === "win32" && IS_STANDALONE_BINARY;
|
|
3204
|
+
}
|
|
3048
3205
|
function finishResponse(response, options) {
|
|
3049
|
-
const withRequestId = responseWithRequestId(response, options.requestId);
|
|
3206
|
+
const withRequestId = responseWithRequestId(response, options.requestId, options.closeConnection);
|
|
3050
3207
|
const stream = isStreamingResponse(withRequestId);
|
|
3051
3208
|
const status = withRequestId.status;
|
|
3052
3209
|
const complete = () => {
|
|
@@ -3054,7 +3211,7 @@ function finishResponse(response, options) {
|
|
|
3054
3211
|
options.metrics.observe({ durationMs, method: options.method, route: options.route, status });
|
|
3055
3212
|
logRequestCompleted(options.logger, status, stream, durationMs);
|
|
3056
3213
|
};
|
|
3057
|
-
if (stream && withRequestId.body) {
|
|
3214
|
+
if (stream && withRequestId.body && options.trackStreamingBody) {
|
|
3058
3215
|
return new Response(trackStreamCompletion(withRequestId.body, complete), {
|
|
3059
3216
|
headers: withRequestId.headers,
|
|
3060
3217
|
status,
|
|
@@ -3064,9 +3221,12 @@ function finishResponse(response, options) {
|
|
|
3064
3221
|
complete();
|
|
3065
3222
|
return withRequestId;
|
|
3066
3223
|
}
|
|
3067
|
-
function responseWithRequestId(response, requestId) {
|
|
3224
|
+
function responseWithRequestId(response, requestId, closeConnection) {
|
|
3068
3225
|
const headers = new Headers(response.headers);
|
|
3069
3226
|
headers.set("x-request-id", requestId);
|
|
3227
|
+
if (closeConnection) {
|
|
3228
|
+
headers.set("connection", "close");
|
|
3229
|
+
}
|
|
3070
3230
|
return new Response(response.body, {
|
|
3071
3231
|
headers,
|
|
3072
3232
|
status: response.status,
|