@earendil-works/pi-ai 0.80.2 → 0.80.5
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 +1 -1
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +13 -4
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +14 -18
- package/dist/api/azure-openai-responses.js.map +1 -1
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.js +21 -9
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/google-generative-ai.d.ts.map +1 -1
- package/dist/api/google-generative-ai.js +4 -2
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-vertex.d.ts.map +1 -1
- package/dist/api/google-vertex.js +4 -2
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +1 -1
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +62 -42
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +12 -5
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/dist/api/openai-responses-shared.js +153 -176
- package/dist/api/openai-responses-shared.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +6 -16
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/openrouter-images.d.ts.map +1 -1
- package/dist/api/openrouter-images.js +2 -1
- package/dist/api/openrouter-images.js.map +1 -1
- package/dist/api/simple-options.d.ts +3 -2
- package/dist/api/simple-options.d.ts.map +1 -1
- package/dist/api/simple-options.js +11 -2
- package/dist/api/simple-options.js.map +1 -1
- package/dist/api/transform-messages.d.ts.map +1 -1
- package/dist/api/transform-messages.js +4 -1
- package/dist/api/transform-messages.js.map +1 -1
- package/dist/image-models.generated.d.ts +34 -19
- package/dist/image-models.generated.d.ts.map +1 -1
- package/dist/image-models.generated.js +60 -45
- package/dist/image-models.generated.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/models.generated.d.ts +1754 -498
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.models.d.ts +157 -0
- package/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.models.js +166 -12
- package/dist/providers/amazon-bedrock.models.js.map +1 -1
- package/dist/providers/anthropic.models.d.ts +6 -190
- package/dist/providers/anthropic.models.d.ts.map +1 -1
- package/dist/providers/anthropic.models.js +21 -207
- package/dist/providers/anthropic.models.js.map +1 -1
- package/dist/providers/azure-openai-responses.models.d.ts +84 -0
- package/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.models.js +72 -0
- package/dist/providers/azure-openai-responses.models.js.map +1 -1
- package/dist/providers/cerebras.models.d.ts +21 -0
- package/dist/providers/cerebras.models.d.ts.map +1 -1
- package/dist/providers/cerebras.models.js +18 -0
- package/dist/providers/cerebras.models.js.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.d.ts +21 -0
- package/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
- package/dist/providers/cloudflare-ai-gateway.models.js +18 -0
- package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
- package/dist/providers/fireworks.models.d.ts +28 -0
- package/dist/providers/fireworks.models.d.ts.map +1 -1
- package/dist/providers/fireworks.models.js +21 -2
- package/dist/providers/fireworks.models.js.map +1 -1
- package/dist/providers/github-copilot.models.d.ts +82 -0
- package/dist/providers/github-copilot.models.d.ts.map +1 -1
- package/dist/providers/github-copilot.models.js +62 -5
- package/dist/providers/github-copilot.models.js.map +1 -1
- package/dist/providers/groq.models.d.ts.map +1 -1
- package/dist/providers/groq.models.js +1 -1
- package/dist/providers/groq.models.js.map +1 -1
- package/dist/providers/huggingface.models.d.ts +100 -0
- package/dist/providers/huggingface.models.d.ts.map +1 -1
- package/dist/providers/huggingface.models.js +90 -0
- package/dist/providers/huggingface.models.js.map +1 -1
- package/dist/providers/minimax-cn.models.d.ts.map +1 -1
- package/dist/providers/minimax-cn.models.js +4 -4
- package/dist/providers/minimax-cn.models.js.map +1 -1
- package/dist/providers/minimax.models.d.ts.map +1 -1
- package/dist/providers/minimax.models.js +4 -4
- package/dist/providers/minimax.models.js.map +1 -1
- package/dist/providers/mistral.models.d.ts +1 -1
- package/dist/providers/mistral.models.d.ts.map +1 -1
- package/dist/providers/mistral.models.js +4 -4
- package/dist/providers/mistral.models.js.map +1 -1
- package/dist/providers/nvidia.models.d.ts +29 -1
- package/dist/providers/nvidia.models.d.ts.map +1 -1
- package/dist/providers/nvidia.models.js +23 -4
- package/dist/providers/nvidia.models.js.map +1 -1
- package/dist/providers/openai-codex.models.d.ts +63 -0
- package/dist/providers/openai-codex.models.d.ts.map +1 -1
- package/dist/providers/openai-codex.models.js +54 -0
- package/dist/providers/openai-codex.models.js.map +1 -1
- package/dist/providers/openai.models.d.ts +84 -0
- package/dist/providers/openai.models.d.ts.map +1 -1
- package/dist/providers/openai.models.js +72 -0
- package/dist/providers/openai.models.js.map +1 -1
- package/dist/providers/opencode-go.models.d.ts.map +1 -1
- package/dist/providers/opencode-go.models.js +6 -6
- package/dist/providers/opencode-go.models.js.map +1 -1
- package/dist/providers/opencode.models.d.ts +132 -0
- package/dist/providers/opencode.models.d.ts.map +1 -1
- package/dist/providers/opencode.models.js +111 -2
- package/dist/providers/opencode.models.js.map +1 -1
- package/dist/providers/openrouter.models.d.ts +358 -55
- package/dist/providers/openrouter.models.d.ts.map +1 -1
- package/dist/providers/openrouter.models.js +395 -138
- package/dist/providers/openrouter.models.js.map +1 -1
- package/dist/providers/together.models.d.ts +31 -0
- package/dist/providers/together.models.d.ts.map +1 -1
- package/dist/providers/together.models.js +21 -2
- package/dist/providers/together.models.js.map +1 -1
- package/dist/providers/vercel-ai-gateway.models.d.ts +446 -36
- package/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
- package/dist/providers/vercel-ai-gateway.models.js +477 -80
- package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/dist/providers/xai.models.d.ts +22 -0
- package/dist/providers/xai.models.d.ts.map +1 -1
- package/dist/providers/xai.models.js +18 -0
- package/dist/providers/xai.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts +0 -42
- package/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-ams.models.js +9 -45
- package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts +0 -42
- package/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-cn.models.js +9 -45
- package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts +0 -42
- package/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
- package/dist/providers/xiaomi-token-plan-sgp.models.js +9 -45
- package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
- package/dist/providers/xiaomi.models.d.ts.map +1 -1
- package/dist/providers/xiaomi.models.js +15 -15
- package/dist/providers/xiaomi.models.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/error-body.d.ts +25 -0
- package/dist/utils/error-body.d.ts.map +1 -0
- package/dist/utils/error-body.js +109 -0
- package/dist/utils/error-body.js.map +1 -0
- package/dist/utils/estimate.d.ts +17 -0
- package/dist/utils/estimate.d.ts.map +1 -0
- package/dist/utils/estimate.js +95 -0
- package/dist/utils/estimate.js.map +1 -0
- package/dist/utils/oauth/device-code.d.ts +2 -0
- package/dist/utils/oauth/device-code.d.ts.map +1 -1
- package/dist/utils/oauth/device-code.js +15 -2
- package/dist/utils/oauth/device-code.js.map +1 -1
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/dist/utils/oauth/github-copilot.js +3 -2
- package/dist/utils/oauth/github-copilot.js.map +1 -1
- package/dist/utils/overflow.d.ts +1 -0
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +3 -0
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/retry.d.ts +12 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +90 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +9 -21
- package/dist/utils/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
if (typeof
|
|
3
|
-
return
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
1
|
+
function loadNodeOs() {
|
|
2
|
+
if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
|
|
3
|
+
return null;
|
|
6
4
|
}
|
|
7
|
-
return
|
|
8
|
-
};
|
|
9
|
-
// NEVER convert to top-level runtime imports - breaks browser/Vite builds
|
|
10
|
-
let _os = null;
|
|
11
|
-
const dynamicImport = (specifier) => import(__rewriteRelativeImportExtension(specifier));
|
|
12
|
-
const NODE_OS_SPECIFIER = "node:" + "os";
|
|
13
|
-
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
|
14
|
-
dynamicImport(NODE_OS_SPECIFIER).then((m) => {
|
|
15
|
-
_os = m;
|
|
16
|
-
});
|
|
5
|
+
return process.getBuiltinModule?.("node:os") ?? null;
|
|
17
6
|
}
|
|
7
|
+
// NEVER convert to top-level runtime imports - breaks browser/Vite builds
|
|
8
|
+
const _os = loadNodeOs();
|
|
18
9
|
import { clampThinkingLevel } from "../models.js";
|
|
19
10
|
import { registerSessionResourceCleanup } from "../session-resources.js";
|
|
20
11
|
import { combineAbortSignals } from "../utils/abort-signals.js";
|
|
21
12
|
import { appendAssistantMessageDiagnostic, createAssistantMessageDiagnostic, formatThrownValue, } from "../utils/diagnostics.js";
|
|
13
|
+
import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
22
14
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
23
15
|
import { headersToRecord } from "../utils/headers.js";
|
|
24
16
|
import { resolveHttpProxyUrlForTarget } from "../utils/node-http-proxy.js";
|
|
@@ -33,10 +25,10 @@ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
|
33
25
|
const DEFAULT_MAX_RETRIES = 0;
|
|
34
26
|
const BASE_DELAY_MS = 1000;
|
|
35
27
|
const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;
|
|
36
|
-
// Keep a bounded pre-header timeout so zero-event Codex SSE stalls fail instead of
|
|
37
|
-
// leaving callers stuck on "Working..." indefinitely. See #4945.
|
|
38
|
-
const DEFAULT_SSE_HEADER_TIMEOUT_MS = 20_000;
|
|
39
28
|
const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15_000;
|
|
29
|
+
// The Codex backend accepts zstd-compressed request bodies on the SSE responses
|
|
30
|
+
// endpoint (the same endpoint the official Codex client compresses against).
|
|
31
|
+
const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
|
|
40
32
|
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
41
33
|
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
42
34
|
const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
|
|
@@ -110,18 +102,29 @@ function normalizeTimeoutMs(value) {
|
|
|
110
102
|
}
|
|
111
103
|
return Math.floor(value);
|
|
112
104
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
105
|
+
function loadNodeZlib() {
|
|
106
|
+
if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return process.getBuiltinModule?.("node:zlib") ?? null;
|
|
110
|
+
}
|
|
111
|
+
// Returns the zstd-compressed body bytes, or null when compression is
|
|
112
|
+
// unavailable (browser/Vite builds). Callers fall back to sending the
|
|
113
|
+
// uncompressed JSON when this returns null.
|
|
114
|
+
function compressRequestBodyZstd(bodyJson) {
|
|
115
|
+
const zlib = loadNodeZlib();
|
|
116
|
+
if (!zlib || typeof zlib.zstdCompressSync !== "function") {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const compressed = zlib.zstdCompressSync(bodyJson, {
|
|
121
|
+
params: { [zlib.constants.ZSTD_c_compressionLevel]: REQUEST_COMPRESSION_ZSTD_LEVEL },
|
|
122
|
+
});
|
|
123
|
+
return new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
125
128
|
}
|
|
126
129
|
// ============================================================================
|
|
127
130
|
// Main Stream Function
|
|
@@ -161,7 +164,7 @@ export const stream = (model, context, options) => {
|
|
|
161
164
|
const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, options?.sessionId);
|
|
162
165
|
const websocketHeaders = buildWebSocketHeaders(model.headers, options?.headers, accountId, apiKey, websocketRequestId);
|
|
163
166
|
const bodyJson = JSON.stringify(body);
|
|
164
|
-
const
|
|
167
|
+
const httpTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);
|
|
165
168
|
const websocketConnectTimeoutMs = normalizeTimeoutMs(options?.websocketConnectTimeoutMs);
|
|
166
169
|
const transport = options?.transport || "auto";
|
|
167
170
|
const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(options?.sessionId);
|
|
@@ -176,7 +179,7 @@ export const stream = (model, context, options) => {
|
|
|
176
179
|
try {
|
|
177
180
|
await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
|
|
178
181
|
websocketStarted = true;
|
|
179
|
-
},
|
|
182
|
+
}, httpTimeoutMs, websocketConnectTimeoutMs, options);
|
|
180
183
|
if (options?.signal?.aborted) {
|
|
181
184
|
throw new Error("Request was aborted");
|
|
182
185
|
}
|
|
@@ -214,6 +217,14 @@ export const stream = (model, context, options) => {
|
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
}
|
|
220
|
+
// Compress the request body once for the SSE path. The Codex backend
|
|
221
|
+
// decodes Content-Encoding: zstd; the WebSocket transport above sends the
|
|
222
|
+
// uncompressed JSON frame, matching the official Codex client.
|
|
223
|
+
const compressedBody = compressRequestBodyZstd(bodyJson);
|
|
224
|
+
if (compressedBody) {
|
|
225
|
+
sseHeaders.set("content-encoding", "zstd");
|
|
226
|
+
}
|
|
227
|
+
const sseBody = compressedBody ?? bodyJson;
|
|
217
228
|
// Fetch with retry logic for rate limits and transient errors
|
|
218
229
|
let response;
|
|
219
230
|
let lastError;
|
|
@@ -223,23 +234,24 @@ export const stream = (model, context, options) => {
|
|
|
223
234
|
throw new Error("Request was aborted");
|
|
224
235
|
}
|
|
225
236
|
try {
|
|
226
|
-
const
|
|
227
|
-
const combinedSignal = combineAbortSignals([options?.signal,
|
|
237
|
+
const headerTimeoutSignal = httpTimeoutMs !== undefined && httpTimeoutMs > 0 ? AbortSignal.timeout(httpTimeoutMs) : undefined;
|
|
238
|
+
const combinedSignal = combineAbortSignals([options?.signal, headerTimeoutSignal]);
|
|
228
239
|
try {
|
|
229
240
|
response = await fetch(resolveCodexUrl(model.baseUrl), {
|
|
230
241
|
method: "POST",
|
|
231
242
|
headers: sseHeaders,
|
|
232
|
-
body:
|
|
243
|
+
body: sseBody,
|
|
233
244
|
signal: combinedSignal.signal,
|
|
234
245
|
});
|
|
235
246
|
}
|
|
236
247
|
catch (error) {
|
|
237
|
-
|
|
238
|
-
|
|
248
|
+
if (headerTimeoutSignal?.aborted && !options?.signal?.aborted) {
|
|
249
|
+
throw new Error(`Codex SSE response headers timed out after ${httpTimeoutMs}ms`);
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
239
252
|
}
|
|
240
253
|
finally {
|
|
241
254
|
combinedSignal.cleanup();
|
|
242
|
-
headerTimeout.clear();
|
|
243
255
|
}
|
|
244
256
|
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
245
257
|
if (response.ok) {
|
|
@@ -300,7 +312,7 @@ export const stream = (model, context, options) => {
|
|
|
300
312
|
delete block.partialJson;
|
|
301
313
|
}
|
|
302
314
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
303
|
-
output.errorMessage =
|
|
315
|
+
output.errorMessage = formatProviderError(normalizeProviderError(error));
|
|
304
316
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
305
317
|
stream.end();
|
|
306
318
|
}
|
|
@@ -312,7 +324,7 @@ export const streamSimple = (model, context, options) => {
|
|
|
312
324
|
if (!apiKey) {
|
|
313
325
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
314
326
|
}
|
|
315
|
-
const base = buildBaseOptions(model, options, apiKey);
|
|
327
|
+
const base = buildBaseOptions(model, context, options, apiKey);
|
|
316
328
|
const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
|
|
317
329
|
const reasoningEffort = clampedReasoning === "off" ? undefined : clampedReasoning;
|
|
318
330
|
return stream(model, context, {
|
|
@@ -553,6 +565,7 @@ async function* parseSSE(response, signal) {
|
|
|
553
565
|
// ============================================================================
|
|
554
566
|
const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
|
|
555
567
|
const SESSION_WEBSOCKET_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
568
|
+
const SESSION_WEBSOCKET_MAX_AGE_MS = 55 * 60 * 1000;
|
|
556
569
|
const websocketSessionCache = new Map();
|
|
557
570
|
const websocketDebugStats = new Map();
|
|
558
571
|
const websocketSseFallbackSessions = new Set();
|
|
@@ -677,6 +690,9 @@ function isWebSocketReusable(socket) {
|
|
|
677
690
|
// If readyState is unavailable, assume the runtime keeps it open/reusable.
|
|
678
691
|
return readyState === undefined || readyState === 1;
|
|
679
692
|
}
|
|
693
|
+
function isWebSocketSessionExpired(entry) {
|
|
694
|
+
return Date.now() - entry.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
|
|
695
|
+
}
|
|
680
696
|
function closeWebSocketSilently(socket, code = 1000, reason = "done") {
|
|
681
697
|
try {
|
|
682
698
|
socket.close(code, reason);
|
|
@@ -777,7 +793,11 @@ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutM
|
|
|
777
793
|
clearTimeout(cached.idleTimer);
|
|
778
794
|
cached.idleTimer = undefined;
|
|
779
795
|
}
|
|
780
|
-
if (!cached.busy &&
|
|
796
|
+
if (!cached.busy && isWebSocketSessionExpired(cached)) {
|
|
797
|
+
closeWebSocketSilently(cached.socket, 1000, "connection_age_limit");
|
|
798
|
+
websocketSessionCache.delete(sessionId);
|
|
799
|
+
}
|
|
800
|
+
else if (!cached.busy && isWebSocketReusable(cached.socket)) {
|
|
781
801
|
cached.busy = true;
|
|
782
802
|
return {
|
|
783
803
|
socket: cached.socket,
|
|
@@ -810,7 +830,7 @@ async function acquireWebSocket(url, headers, sessionId, signal, connectTimeoutM
|
|
|
810
830
|
}
|
|
811
831
|
}
|
|
812
832
|
const socket = await connectWebSocket(url, headers, signal, connectTimeoutMs, env);
|
|
813
|
-
const entry = { socket, busy: true };
|
|
833
|
+
const entry = { socket, busy: true, createdAt: Date.now() };
|
|
814
834
|
websocketSessionCache.set(sessionId, entry);
|
|
815
835
|
return {
|
|
816
836
|
socket,
|