@pasko70/pibo 3.4.3 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-runtime/routed-session.js +39 -12
- package/dist/agent-runtimes/codex-native/adapter.js +14 -1
- package/dist/agent-runtimes/codex-native/models.js +4 -4
- package/dist/agent-runtimes/codex-native/process.js +32 -13
- package/dist/agent-runtimes/codex-native/provider-usage.js +115 -0
- package/dist/agent-runtimes/pi/adapter.js +1 -0
- package/dist/agent-runtimes/pi/routed-session.js +4 -2
- package/dist/apps/chat/bounded-event-stream.js +98 -0
- package/dist/apps/chat/chat-settings-routes.js +3 -3
- package/dist/apps/chat/data/chat-data-mappers.js +32 -13
- package/dist/apps/chat/data/event-command-service.js +30 -21
- package/dist/apps/chat/data/history-query-service.js +34 -25
- package/dist/apps/chat/data/read-state-service.js +13 -0
- package/dist/apps/chat/data/session-query-service.js +19 -11
- package/dist/apps/chat/data/timeline-query-service.js +19 -7
- package/dist/apps/chat/message-command-dispatcher.js +134 -0
- package/dist/apps/chat/output-compactor.js +9 -0
- package/dist/apps/chat/output-event-policy.js +9 -1
- package/dist/apps/chat/stream.js +21 -3
- package/dist/apps/chat/telemetry-retention-service.js +113 -8
- package/dist/apps/chat/trace-response-cache.js +46 -0
- package/dist/apps/chat/trace-v2.js +12 -6
- package/dist/apps/chat/trace.js +1 -1
- package/dist/apps/chat/web-app.js +608 -281
- package/dist/apps/chat-ui/assets/{dist-DeMKnZR8.js → dist-BG0n7zLd.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-C8GzUMPk.js → dist-D6TjFhAm.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BkUu8WPA.js → dist-D79vyxSX.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-B4YhOxh0.js → dist-DFZ8cwh0.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Cyb4rVa6.js → dist-cOjokPrK.js} +1 -1
- package/dist/apps/chat-ui/assets/index-RMHUTJ62.js +229 -0
- package/dist/apps/chat-ui/assets/{index-BOceJ0jM.css → index-hEkrlRk-.css} +1 -1
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-xacbCyTx.js +44 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/compute/pool/seeds.js +25 -3
- package/dist/core/events.js +4 -0
- package/dist/core/output-render-sequence.js +2 -0
- package/dist/core/provider-capacity.js +33 -0
- package/dist/core/provider-telemetry.js +21 -6
- package/dist/core/runtime-capacity.js +174 -0
- package/dist/core/runtime-telemetry.js +40 -12
- package/dist/core/session-router.js +79 -2
- package/dist/data/async-chat-reads.js +38 -0
- package/dist/data/async-chat-storage.js +91 -0
- package/dist/data/async-telemetry-maintenance.js +9 -0
- package/dist/data/bounded-worker-client.js +256 -0
- package/dist/data/chat-read-projections.js +159 -0
- package/dist/data/chat-read-worker.js +73 -0
- package/dist/data/chat-storage-worker.js +146 -0
- package/dist/data/ingest-service.js +79 -14
- package/dist/data/message-command-store.js +148 -0
- package/dist/data/payload-store.js +92 -11
- package/dist/data/pibo-store.js +10 -8
- package/dist/data/schema.js +16 -2
- package/dist/data/session-store.js +3 -1
- package/dist/data/storage-backup.js +278 -0
- package/dist/data/telemetry-capture.js +188 -0
- package/dist/data/telemetry-command.js +3 -0
- package/dist/data/telemetry-maintenance-worker.js +40 -0
- package/dist/data/telemetry-maintenance.js +110 -0
- package/dist/data/telemetry-retention.js +16 -7
- package/dist/data/telemetry-worker.js +111 -0
- package/dist/data/telemetry-writer.js +150 -83
- package/dist/data/telemetry.js +5 -0
- package/dist/debug/index.js +52 -0
- package/dist/debug/storage-backup.js +33 -0
- package/dist/debug/telemetry-capture.js +66 -0
- package/dist/gateway/cli.js +104 -16
- package/dist/gateway/server.js +2 -0
- package/dist/providers/openai-gpt56.js +11 -6
- package/dist/session-ui/terminalRows.js +55 -13
- package/dist/sessions/pibo-data-store.js +30 -10
- package/dist/shared/debug-features.js +4 -0
- package/dist/shared/model-inference-metrics.js +23 -0
- package/dist/shared/trace-event-projection.js +59 -2
- package/dist/shared/trace-history.js +9 -0
- package/dist/shared/trace-live-reducer.js +1 -0
- package/dist/shared/trace-patch-nodes.js +19 -0
- package/dist/web/channel.js +8 -2
- package/dist/web/http.js +36 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-Dk4mbXAB.js +0 -228
- package/dist/apps/chat-vscode-web/assets/index-0oTGFHni.js +0 -43
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function modelInferenceInputTokens(metrics) {
|
|
2
|
+
const total = tokenCount(metrics?.totalTokens);
|
|
3
|
+
const output = tokenCount(metrics?.outputTokens);
|
|
4
|
+
if (total !== undefined && output !== undefined)
|
|
5
|
+
return Math.max(0, total - output);
|
|
6
|
+
return tokenCount(metrics?.inputTokens);
|
|
7
|
+
}
|
|
8
|
+
export function modelInferenceCachedInputTokens(metrics) {
|
|
9
|
+
const cacheRead = tokenCount(metrics?.cacheReadTokens);
|
|
10
|
+
const cacheWrite = tokenCount(metrics?.cacheWriteTokens);
|
|
11
|
+
if (cacheRead === undefined && cacheWrite === undefined)
|
|
12
|
+
return undefined;
|
|
13
|
+
return (cacheRead ?? 0) + (cacheWrite ?? 0);
|
|
14
|
+
}
|
|
15
|
+
export function modelInferenceUncachedInputTokens(metrics) {
|
|
16
|
+
const input = modelInferenceInputTokens(metrics);
|
|
17
|
+
if (input === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
return Math.max(0, input - (modelInferenceCachedInputTokens(metrics) ?? 0));
|
|
20
|
+
}
|
|
21
|
+
function tokenCount(value) {
|
|
22
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : undefined;
|
|
23
|
+
}
|
|
@@ -6,6 +6,10 @@ import { findLikelyTraceChildSession, isSubagentToolName, } from "./trace-subage
|
|
|
6
6
|
import { qualifiedToolNodeId } from "./trace-tool-identity.js";
|
|
7
7
|
export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent, childByParent, linkedChildByToolCallId, historyCoverage, openTranscriptEventIds, sessionStatus) {
|
|
8
8
|
const payload = storedEvent.payload;
|
|
9
|
+
if (payload.type === "assistant_usage") {
|
|
10
|
+
attachModelInferenceToLatestOutput(nodes, byId, payload, storedEvent);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
9
13
|
if (payload.type === "message_started" && payload.source === "user" && payload.eventId) {
|
|
10
14
|
const persistedUser = flattenTraceNodes([...nodes]).find((node) => node.type === "user.message"
|
|
11
15
|
&& isPersistedHistorySource(node.source)
|
|
@@ -42,6 +46,7 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
|
|
|
42
46
|
}
|
|
43
47
|
if (payload.type === "assistant_message") {
|
|
44
48
|
const node = assistantMessageNodeFromEvent(piboSessionId, payload, storedEvent.createdAt, storedEvent.eventSequence, storedEvent.streamId, storedEvent.streamFrameIndex, storedEvent.traceSource, storedEvent.id);
|
|
49
|
+
applyStoredPayloadRef(node, payload, storedEvent.storedPayloadRef);
|
|
45
50
|
const existing = byId.get(node.id) ?? findMatchingContentNode(byId, node);
|
|
46
51
|
if (existing) {
|
|
47
52
|
mergeAssistantMessageEvent(existing, node);
|
|
@@ -153,8 +158,12 @@ function applyRenderSequenceToHistoryNode(byId, storedEvent, event) {
|
|
|
153
158
|
&& typeof event.toolCallId === "string"
|
|
154
159
|
&& node.toolCallId === event.toolCallId
|
|
155
160
|
&& (!eventId || !node.eventId || node.eventId === eventId)));
|
|
156
|
-
if (target)
|
|
157
|
-
|
|
161
|
+
if (!target)
|
|
162
|
+
return;
|
|
163
|
+
applyRenderSequence(target, storedEvent, event);
|
|
164
|
+
if (event.type === "tool_execution_finished" && event.toolMetrics) {
|
|
165
|
+
target.toolMetrics = event.toolMetrics;
|
|
166
|
+
}
|
|
158
167
|
}
|
|
159
168
|
function applyRenderSequence(node, storedEvent, event) {
|
|
160
169
|
const renderSequence = storedEvent.renderSequence ?? event.renderSequence;
|
|
@@ -171,6 +180,9 @@ function applyRenderSequence(node, storedEvent, event) {
|
|
|
171
180
|
function applyStoredPayloadRef(node, event, storedPayloadRef) {
|
|
172
181
|
if (!storedPayloadRef)
|
|
173
182
|
return;
|
|
183
|
+
if (event.type === "assistant_message" || event.type === "thinking_finished") {
|
|
184
|
+
node.payloadRefs = { ...node.payloadRefs, [storedPayloadRef.payloadKind ?? "output"]: storedPayloadRef };
|
|
185
|
+
}
|
|
174
186
|
if (event.type === "tool_call" || event.type === "tool_execution_started") {
|
|
175
187
|
node.payloadRefs = { ...node.payloadRefs, input: storedPayloadRef };
|
|
176
188
|
}
|
|
@@ -524,6 +536,47 @@ export function latestTraceStreamId(events, initial) {
|
|
|
524
536
|
}
|
|
525
537
|
return latest;
|
|
526
538
|
}
|
|
539
|
+
function attachModelInferenceToLatestOutput(nodes, byId, event, storedEvent) {
|
|
540
|
+
const eventId = event.eventId;
|
|
541
|
+
const candidates = flattenTraceNodes(nodes)
|
|
542
|
+
.filter((node) => node.eventId === eventId && traceNodeStartedBeforeInference(node, storedEvent) && (node.type === "assistant.message"
|
|
543
|
+
|| node.type === "model.reasoning"
|
|
544
|
+
|| node.type === "tool.call"
|
|
545
|
+
|| node.type === "agent.delegation"))
|
|
546
|
+
.sort(compareTraceNodes);
|
|
547
|
+
const target = candidates.at(-1) ?? (eventId ? byId.get(messageTurnNodeId(eventId)) : undefined);
|
|
548
|
+
if (!target)
|
|
549
|
+
return;
|
|
550
|
+
const id = eventId ? `${eventId}:usage:${event.usageIndex ?? 0}` : storedEvent.id;
|
|
551
|
+
const record = {
|
|
552
|
+
id,
|
|
553
|
+
completedAt: storedEvent.createdAt,
|
|
554
|
+
metrics: {
|
|
555
|
+
...(event.inputTokens === undefined ? {} : { inputTokens: event.inputTokens }),
|
|
556
|
+
...(event.outputTokens === undefined ? {} : { outputTokens: event.outputTokens }),
|
|
557
|
+
...(event.cacheReadTokens === undefined ? {} : { cacheReadTokens: event.cacheReadTokens }),
|
|
558
|
+
...(event.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: event.cacheWriteTokens }),
|
|
559
|
+
...(event.reasoningTokens === undefined ? {} : { reasoningTokens: event.reasoningTokens }),
|
|
560
|
+
totalTokens: event.totalTokens,
|
|
561
|
+
...(event.costUsd === undefined ? {} : { costUsd: event.costUsd }),
|
|
562
|
+
},
|
|
563
|
+
};
|
|
564
|
+
target.modelInferences = [...(target.modelInferences ?? []).filter((item) => item.id !== id), record];
|
|
565
|
+
}
|
|
566
|
+
function traceNodeStartedBeforeInference(node, storedEvent) {
|
|
567
|
+
const inferenceRenderSequence = storedEvent.renderSequence;
|
|
568
|
+
const nodeRenderSequence = node.orderKey?.renderSequence;
|
|
569
|
+
if (inferenceRenderSequence !== undefined && nodeRenderSequence !== undefined) {
|
|
570
|
+
return nodeRenderSequence <= inferenceRenderSequence;
|
|
571
|
+
}
|
|
572
|
+
const inferenceSequence = storedEvent.eventSequence ?? storedEvent.streamId;
|
|
573
|
+
const nodeSequence = node.orderKey?.eventSequence ?? node.orderKey?.streamId;
|
|
574
|
+
if (inferenceSequence !== undefined && nodeSequence !== undefined)
|
|
575
|
+
return nodeSequence <= inferenceSequence;
|
|
576
|
+
const inferenceTime = Date.parse(storedEvent.createdAt);
|
|
577
|
+
const nodeTime = Date.parse(node.startedAt ?? node.completedAt ?? "");
|
|
578
|
+
return !Number.isFinite(inferenceTime) || !Number.isFinite(nodeTime) || nodeTime <= inferenceTime;
|
|
579
|
+
}
|
|
527
580
|
// ── event → node helpers ─────────────────────────────────────────
|
|
528
581
|
function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByToolCallId, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex, traceSource, storedInstanceId) {
|
|
529
582
|
const eventId = "eventId" in event && typeof event.eventId === "string" ? event.eventId : undefined;
|
|
@@ -562,6 +615,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
562
615
|
type: "user.message",
|
|
563
616
|
title: "User Message",
|
|
564
617
|
status: isOptimisticUserMessageEvent(event) ? "running" : "done",
|
|
618
|
+
messageDeliveryState: isOptimisticUserMessageEvent(event) ? "sending" : undefined,
|
|
565
619
|
summary: event.text,
|
|
566
620
|
output: event.text,
|
|
567
621
|
};
|
|
@@ -711,6 +765,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
711
765
|
input: { reason: event.reason },
|
|
712
766
|
output: event.result,
|
|
713
767
|
error: event.errorMessage,
|
|
768
|
+
compactionStats: event.compactionStats,
|
|
714
769
|
stableKey: `compaction:${eventInstanceKey}`,
|
|
715
770
|
};
|
|
716
771
|
}
|
|
@@ -768,6 +823,7 @@ function findMatchingContentNode(byId, update) {
|
|
|
768
823
|
return [...byId.values()].find((candidate) => candidate.type === update.type && candidate.stableKey === update.stableKey);
|
|
769
824
|
}
|
|
770
825
|
function mergeAssistantMessageEvent(target, update) {
|
|
826
|
+
target.payloadRefs = { ...target.payloadRefs, ...update.payloadRefs };
|
|
771
827
|
target.status = update.status;
|
|
772
828
|
target.summary = update.summary ?? target.summary;
|
|
773
829
|
target.output = update.output ?? target.output;
|
|
@@ -1163,6 +1219,7 @@ function mergeCompactionEvent(target, update) {
|
|
|
1163
1219
|
target.input = update.input ?? target.input;
|
|
1164
1220
|
target.output = update.output ?? target.output;
|
|
1165
1221
|
target.error = update.error ?? target.error;
|
|
1222
|
+
target.compactionStats = update.compactionStats ?? target.compactionStats;
|
|
1166
1223
|
target.completedAt = update.completedAt ?? target.completedAt;
|
|
1167
1224
|
}
|
|
1168
1225
|
function hasVisibleText(value) {
|
|
@@ -710,6 +710,7 @@ function createUserMessageNode(piboSessionId, entry, entryIndex, timing, fallbac
|
|
|
710
710
|
startedAt: entry.createdAt,
|
|
711
711
|
summary: text,
|
|
712
712
|
output: text,
|
|
713
|
+
payloadRefs: historyContentPayloadRef(entry),
|
|
713
714
|
error: entry.error,
|
|
714
715
|
source,
|
|
715
716
|
stableKey: eventIdentity ?? historyEntryStableKey(entry),
|
|
@@ -902,6 +903,7 @@ function createAssistantMessageNode(input) {
|
|
|
902
903
|
completedAt: input.completedAt,
|
|
903
904
|
summary: input.text,
|
|
904
905
|
output: input.text,
|
|
906
|
+
payloadRefs: historyContentPayloadRef(input.entry),
|
|
905
907
|
error: input.error,
|
|
906
908
|
source,
|
|
907
909
|
stableKey: eventIdentity ? `assistant:${eventIdentity}` : `${historyEntryStableKey(input.entry)}:response:${input.contentPartIndex}`,
|
|
@@ -909,6 +911,13 @@ function createAssistantMessageNode(input) {
|
|
|
909
911
|
children: input.children ?? [],
|
|
910
912
|
};
|
|
911
913
|
}
|
|
914
|
+
/** Preserve a stored full body when the bounded history page contains its preview. */
|
|
915
|
+
function historyContentPayloadRef(entry) {
|
|
916
|
+
const ref = entry.source === "product" ? entry.metadata?.tracePayloadRef : undefined;
|
|
917
|
+
if (!ref || typeof ref !== "object" || Array.isArray(ref) || typeof ref.ref !== "string" || typeof ref.byteLength !== "number" || typeof ref.preview !== "string")
|
|
918
|
+
return undefined;
|
|
919
|
+
return { output: ref };
|
|
920
|
+
}
|
|
912
921
|
function historyMessageParts(entry) {
|
|
913
922
|
return typeof entry.content === "string" ? [{ type: "text", text: entry.content }] : [...entry.content];
|
|
914
923
|
}
|
|
@@ -165,6 +165,7 @@ function makeStored(streamEvent, piboSessionId, type, payload, nextSequence, now
|
|
|
165
165
|
streamId: typeof streamEvent.streamId === "number" ? streamEvent.streamId : undefined,
|
|
166
166
|
streamFrameIndex,
|
|
167
167
|
traceSource: "live",
|
|
168
|
+
storedPayloadRef: streamEvent.storedPayloadRef,
|
|
168
169
|
eventId: typeof positionedPayload.eventId === "string" ? positionedPayload.eventId : undefined,
|
|
169
170
|
type,
|
|
170
171
|
createdAt: streamEventCreatedAt(streamEvent) ?? now(),
|
|
@@ -64,6 +64,7 @@ function traceNodeShallowEqual(left, right) {
|
|
|
64
64
|
left.toolMetrics?.inputTokens === right.toolMetrics?.inputTokens &&
|
|
65
65
|
left.toolMetrics?.outputTokens === right.toolMetrics?.outputTokens &&
|
|
66
66
|
left.toolMetrics?.tokenBasis === right.toolMetrics?.tokenBasis &&
|
|
67
|
+
modelInferenceRecordsEqual(left.modelInferences, right.modelInferences) &&
|
|
67
68
|
left.summary === right.summary &&
|
|
68
69
|
left.input === right.input &&
|
|
69
70
|
left.output === right.output &&
|
|
@@ -73,6 +74,24 @@ function traceNodeShallowEqual(left, right) {
|
|
|
73
74
|
left.stableKey === right.stableKey &&
|
|
74
75
|
traceOrderKeyEqual(left.orderKey, right.orderKey));
|
|
75
76
|
}
|
|
77
|
+
function modelInferenceRecordsEqual(left, right) {
|
|
78
|
+
if (left === right)
|
|
79
|
+
return true;
|
|
80
|
+
if (!left || !right || left.length !== right.length)
|
|
81
|
+
return false;
|
|
82
|
+
return left.every((item, index) => {
|
|
83
|
+
const other = right[index];
|
|
84
|
+
return item.id === other?.id
|
|
85
|
+
&& item.completedAt === other.completedAt
|
|
86
|
+
&& item.metrics.inputTokens === other.metrics.inputTokens
|
|
87
|
+
&& item.metrics.outputTokens === other.metrics.outputTokens
|
|
88
|
+
&& item.metrics.cacheReadTokens === other.metrics.cacheReadTokens
|
|
89
|
+
&& item.metrics.cacheWriteTokens === other.metrics.cacheWriteTokens
|
|
90
|
+
&& item.metrics.reasoningTokens === other.metrics.reasoningTokens
|
|
91
|
+
&& item.metrics.totalTokens === other.metrics.totalTokens
|
|
92
|
+
&& item.metrics.costUsd === other.metrics.costUsd;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
76
95
|
function traceOrderKeyEqual(left, right) {
|
|
77
96
|
if (left === right)
|
|
78
97
|
return true;
|
package/dist/web/channel.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { createServer } from "node:http";
|
|
2
3
|
import { PiboAuthError } from "../auth/types.js";
|
|
3
4
|
import { handleSimpleAgentApiRequest } from "../api/simple-agent-api.js";
|
|
@@ -190,13 +191,15 @@ function createGatewayRuntimeStatuses(channelContext) {
|
|
|
190
191
|
}
|
|
191
192
|
});
|
|
192
193
|
}
|
|
193
|
-
function createGatewayStatusResponse(channelContext, options) {
|
|
194
|
+
function createGatewayStatusResponse(channelContext, options, generation) {
|
|
194
195
|
const mode = gatewayMode(options);
|
|
195
196
|
return responseJson({
|
|
196
197
|
status: "ok",
|
|
197
198
|
mode,
|
|
199
|
+
generation,
|
|
198
200
|
health: { status: "ok", mode },
|
|
199
201
|
runtimeStatuses: createGatewayRuntimeStatuses(channelContext),
|
|
202
|
+
...(channelContext.getRuntimeCapacityStatus ? { runtimeCapacity: channelContext.getRuntimeCapacityStatus() } : {}),
|
|
200
203
|
activeRuns: collectActiveRuns(channelContext),
|
|
201
204
|
});
|
|
202
205
|
}
|
|
@@ -238,6 +241,7 @@ async function waitForServerClose(closePromise, timeoutMs) {
|
|
|
238
241
|
});
|
|
239
242
|
}
|
|
240
243
|
export function createWebHostChannel(options = {}) {
|
|
244
|
+
const generation = randomUUID();
|
|
241
245
|
const host = options.host ?? DEFAULT_WEB_CHANNEL_HOST;
|
|
242
246
|
const port = options.port ?? DEFAULT_WEB_CHANNEL_PORT;
|
|
243
247
|
const shutdownDrainTimeoutMs = options.shutdownDrainTimeoutMs ?? DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS;
|
|
@@ -319,7 +323,7 @@ export function createWebHostChannel(options = {}) {
|
|
|
319
323
|
return;
|
|
320
324
|
}
|
|
321
325
|
if (url.pathname === "/gateway/status") {
|
|
322
|
-
await sendResponse(nodeResponse, createGatewayStatusResponse(requireContext(), options));
|
|
326
|
+
await sendResponse(nodeResponse, createGatewayStatusResponse(requireContext(), options, generation));
|
|
323
327
|
return;
|
|
324
328
|
}
|
|
325
329
|
if (url.pathname.startsWith("/api/auth/")) {
|
|
@@ -398,6 +402,8 @@ export function createWebHostChannel(options = {}) {
|
|
|
398
402
|
return;
|
|
399
403
|
shuttingDown = false;
|
|
400
404
|
context = channelContext;
|
|
405
|
+
for (const app of channelContext.getWebApps())
|
|
406
|
+
await app.initialize?.(createAppContext(channelContext));
|
|
401
407
|
server = createServer((request, response) => {
|
|
402
408
|
void handleRequest(request, response);
|
|
403
409
|
});
|
package/dist/web/http.js
CHANGED
|
@@ -107,11 +107,11 @@ export async function sendWebResponse(response, webResponse, options = {}) {
|
|
|
107
107
|
}
|
|
108
108
|
const reader = webResponse.body.getReader();
|
|
109
109
|
const cancel = () => {
|
|
110
|
-
void reader.cancel();
|
|
110
|
+
void reader.cancel().catch(() => undefined);
|
|
111
111
|
};
|
|
112
112
|
const abort = () => {
|
|
113
113
|
const socket = response.socket;
|
|
114
|
-
void reader.cancel();
|
|
114
|
+
void reader.cancel().catch(() => undefined);
|
|
115
115
|
if (!response.writableEnded)
|
|
116
116
|
response.end();
|
|
117
117
|
socket?.end();
|
|
@@ -125,7 +125,20 @@ export async function sendWebResponse(response, webResponse, options = {}) {
|
|
|
125
125
|
const { done, value } = await reader.read();
|
|
126
126
|
if (done)
|
|
127
127
|
break;
|
|
128
|
-
|
|
128
|
+
const bytes = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
|
|
129
|
+
for (let offset = 0; offset < bytes.length; offset += 64 * 1024) {
|
|
130
|
+
if (response.destroyed || response.writableEnded)
|
|
131
|
+
return;
|
|
132
|
+
if (!response.write(bytes.subarray(offset, offset + 64 * 1024))) {
|
|
133
|
+
const drained = await waitForResponseDrain(response, (webResponse.headers.get("content-type") ?? "").startsWith("text/event-stream") ? 5000 : undefined, options.signal);
|
|
134
|
+
if (!drained) {
|
|
135
|
+
void reader.cancel().catch(() => undefined);
|
|
136
|
+
if (!response.destroyed)
|
|
137
|
+
response.destroy();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
129
142
|
}
|
|
130
143
|
if (!response.writableEnded)
|
|
131
144
|
response.end();
|
|
@@ -206,3 +219,23 @@ function appendVary(existing, value) {
|
|
|
206
219
|
function appendServerTiming(existing, value) {
|
|
207
220
|
return existing ? `${existing}, ${value}` : value;
|
|
208
221
|
}
|
|
222
|
+
/** Honor socket pressure before pulling another application frame. */
|
|
223
|
+
function waitForResponseDrain(response, maximumAgeMs, signal) {
|
|
224
|
+
if (response.destroyed || response.writableEnded || signal?.aborted)
|
|
225
|
+
return Promise.resolve(false);
|
|
226
|
+
return new Promise(resolve => {
|
|
227
|
+
let timer;
|
|
228
|
+
const finish = (ready) => { if (timer)
|
|
229
|
+
clearTimeout(timer); response.off("drain", drain); response.off("close", close); response.off("error", close); signal?.removeEventListener("abort", close); resolve(ready); };
|
|
230
|
+
const drain = () => finish(true);
|
|
231
|
+
const close = () => finish(false);
|
|
232
|
+
response.once("drain", drain);
|
|
233
|
+
response.once("close", close);
|
|
234
|
+
response.once("error", close);
|
|
235
|
+
signal?.addEventListener("abort", close, { once: true });
|
|
236
|
+
if (maximumAgeMs !== undefined) {
|
|
237
|
+
timer = setTimeout(close, maximumAgeMs);
|
|
238
|
+
timer.unref();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pasko70/pibo",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@pasko70/pibo",
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.5.0",
|
|
10
10
|
"workspaces": [
|
|
11
11
|
"packages/workflows"
|
|
12
12
|
],
|