@tonyclaw/agent-inspector 2.0.17 → 2.0.19
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/.output/cli.js +149 -40
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-DDg7Cs4N.js → CompareDrawer-D-90PIPw.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-CxRDWacg.js +114 -0
- package/.output/public/assets/ReplayDialog-DzlLYoTO.js +1 -0
- package/.output/public/assets/{RequestAnatomy-DR8OICOJ.js → RequestAnatomy-0hbyf6oa.js} +1 -1
- package/.output/public/assets/ResponseView-CKe0tNqr.js +1 -0
- package/.output/public/assets/StreamingChunkSequence-enhfdeNh.js +1 -0
- package/.output/public/assets/_sessionId-CJ-SusES.js +1 -0
- package/.output/public/assets/index-BbFsmQRC.js +1 -0
- package/.output/public/assets/index-BfBgrkgU.css +1 -0
- package/.output/public/assets/{main-bwZlEXw2.js → main-o9-hg8g1.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +141 -118
- package/.output/server/_libs/tanstack__react-virtual.mjs +13 -2
- package/.output/server/_libs/tanstack__virtual-core.mjs +24 -1
- package/.output/server/{_sessionId-BEuJJhqT.mjs → _sessionId-F8lm6PIb.mjs} +3 -2
- package/.output/server/_ssr/{CompareDrawer-yTO93GMz.mjs → CompareDrawer-Cf1yfwfd.mjs} +4 -3
- package/.output/server/_ssr/{ProxyViewerContainer-C8qCkHGB.mjs → ProxyViewerContainer-Bmu9867L.mjs} +349 -74
- package/.output/server/_ssr/{ReplayDialog-DRF9PG7Z.mjs → ReplayDialog-DxZfLBOe.mjs} +199 -17
- package/.output/server/_ssr/{RequestAnatomy-DBcuV_jV.mjs → RequestAnatomy-CbdERGZd.mjs} +4 -3
- package/.output/server/_ssr/{ResponseView-JJm78HZT.mjs → ResponseView-CvLd6qxI.mjs} +4 -3
- package/.output/server/_ssr/{StreamingChunkSequence-DyGKjBYx.mjs → StreamingChunkSequence-B_lkRB6o.mjs} +4 -3
- package/.output/server/_ssr/{index-T7JG28t6.mjs → index-ejrSItfz.mjs} +3 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-BJob8RN3.mjs → router-SbCnbDc2.mjs} +896 -240
- package/.output/server/{_tanstack-start-manifest_v-CuiFi3XC.mjs → _tanstack-start-manifest_v-yx8ftqWz.mjs} +1 -1
- package/.output/server/index.mjs +60 -60
- package/README.md +26 -109
- package/package.json +2 -1
- package/src/cli/doctor.ts +160 -8
- package/src/cli/templates/skill-onboard.ts +2 -20
- package/src/components/OnboardingBanner.tsx +13 -13
- package/src/components/ProxyViewer.tsx +55 -38
- package/src/components/ProxyViewerContainer.tsx +5 -0
- package/src/components/providers/SettingsDialog.tsx +113 -2
- package/src/components/proxy-viewer/ConversationGroup.tsx +20 -3
- package/src/components/proxy-viewer/ConversationGroupList.tsx +144 -0
- package/src/components/proxy-viewer/ConversationHeader.tsx +0 -2
- package/src/components/proxy-viewer/ReplayDialog.tsx +200 -13
- package/src/components/proxy-viewer/index.ts +1 -1
- package/src/knowledge/openclawClient.ts +0 -8
- package/src/knowledge/types.ts +0 -2
- package/src/lib/export-logs.ts +72 -5
- package/src/lib/providerTestContract.ts +0 -1
- package/src/lib/useProviders.ts +1 -1
- package/src/mcp/server.ts +14 -6
- package/src/mcp/toolHandlers.ts +21 -4
- package/src/proxy/chunkStorage.ts +2 -4
- package/src/proxy/dataDir.ts +37 -15
- package/src/proxy/formats/anthropic/index.ts +0 -1
- package/src/proxy/formats/anthropic/schemas.ts +0 -2
- package/src/proxy/handler.ts +41 -29
- package/src/proxy/logFinalizer.ts +93 -11
- package/src/proxy/logger.ts +35 -10
- package/src/proxy/providers.ts +12 -5
- package/src/proxy/rawStreamCapture.ts +85 -0
- package/src/proxy/schemas.ts +1 -51
- package/src/proxy/sessionRuntime.ts +0 -9
- package/src/proxy/socketTracker.ts +15 -8
- package/src/proxy/store.ts +414 -13
- package/src/routes/api/logs.$id.replay.ts +66 -1
- package/src/routes/api/logs.stream.ts +11 -3
- package/src/routes/api/logs.ts +51 -17
- package/src/routes/api/providers.$providerId.ts +10 -3
- package/src/routes/api/providers.ts +6 -3
- package/.output/public/assets/ProxyViewerContainer-DbbK1y7N.js +0 -114
- package/.output/public/assets/ReplayDialog-C1zWNkoz.js +0 -1
- package/.output/public/assets/ResponseView-Dh9iSj6h.js +0 -1
- package/.output/public/assets/StreamingChunkSequence-B_xElyH3.js +0 -1
- package/.output/public/assets/_sessionId-gv1a_NNu.js +0 -1
- package/.output/public/assets/index-D_nZj9Vt.css +0 -1
- package/.output/public/assets/index-u-LwDaeH.js +0 -1
- package/src/components/ui/json-expansion-button.tsx +0 -56
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { c as createRouter, a as createRootRoute, b as createFileRoute, l as lazyRouteComponent, O as Outlet, H as HeadContent, S as Scripts } from "../_libs/tanstack__react-router.mjs";
|
|
2
2
|
import { j as jsxRuntimeExports } from "../_libs/react.mjs";
|
|
3
3
|
import { S as SWRConfig } from "../_libs/swr.mjs";
|
|
4
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync } from "node:fs";
|
|
5
|
-
import fs, { mkdir, appendFile, readFile,
|
|
4
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync, readdirSync, statSync, createReadStream, cpSync, rmSync } from "node:fs";
|
|
5
|
+
import fs, { mkdir, appendFile, readFile, writeFile, readdir, open, stat, unlink } from "node:fs/promises";
|
|
6
|
+
import { createInterface } from "node:readline";
|
|
6
7
|
import { Buffer } from "node:buffer";
|
|
7
8
|
import path, { join, isAbsolute, dirname } from "node:path";
|
|
8
9
|
import { execFile, exec, spawn } from "node:child_process";
|
|
@@ -11,6 +12,7 @@ import { C as Conf } from "../_libs/conf.mjs";
|
|
|
11
12
|
import { randomUUID } from "crypto";
|
|
12
13
|
import { promisify } from "node:util";
|
|
13
14
|
import { Worker } from "node:worker_threads";
|
|
15
|
+
import { randomUUID as randomUUID$1 } from "node:crypto";
|
|
14
16
|
import { M as McpServer, W as WebStandardStreamableHTTPServerTransport } from "../_libs/modelcontextprotocol__server.mjs";
|
|
15
17
|
import { homedir } from "node:os";
|
|
16
18
|
import { d as object, b as string, a as array, _ as _enum, u as union, n as number, c as boolean, e as unknown, l as literal, r as record, g as discriminatedUnion, h as _null, k as lazy } from "../_libs/zod.mjs";
|
|
@@ -31,7 +33,6 @@ import "../_libs/isbot.mjs";
|
|
|
31
33
|
import "../_libs/use-sync-external-store.mjs";
|
|
32
34
|
import "../_libs/dequal.mjs";
|
|
33
35
|
import "node:process";
|
|
34
|
-
import "node:crypto";
|
|
35
36
|
import "node:assert";
|
|
36
37
|
import "../_libs/dot-prop.mjs";
|
|
37
38
|
import "../_libs/env-paths.mjs";
|
|
@@ -49,7 +50,7 @@ import "../_libs/mimic-function.mjs";
|
|
|
49
50
|
import "../_libs/semver.mjs";
|
|
50
51
|
import "../_libs/uint8array-extras.mjs";
|
|
51
52
|
const faviconSvg = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2064%2064'%20role='img'%20aria-label='Agent%20Inspector'%3e%3crect%20width='64'%20height='64'%20rx='14'%20fill='%23111827'%20/%3e%3cg%20fill='none'%20stroke='%23f59e0b'%20stroke-width='4.2'%20stroke-linecap='round'%20stroke-linejoin='round'%3e%3cpath%20fill='%23f59e0b'%20d='M15%2036c0-11%207-18%2017-18s17%207%2017%2018c0%208-7%2013-17%2013s-17-5-17-13z'%20/%3e%3cpath%20d='M16%2031c-6-5-12-3-12%204%200%205%206%206%2011%202'%20/%3e%3cpath%20d='M48%2031c6-5%2012-3%2012%204%200%205-6%206-11%202'%20/%3e%3cpath%20d='M27%2019l-3-7'%20/%3e%3cpath%20d='M37%2019l3-7'%20/%3e%3cpath%20d='M19%2045l-6%209'%20/%3e%3cpath%20d='M27%2048l-3%209'%20/%3e%3cpath%20d='M37%2048l3%209'%20/%3e%3cpath%20d='M45%2045l6%209'%20/%3e%3c/g%3e%3cpath%20d='M14%2047l9-8%208%209c-5%203.5-12%203-17-1z'%20fill='%232f6b3f'%20opacity='.95'%20/%3e%3cpath%20d='M18%2046l5-5%205%206c-3%201.6-7%201.4-10-1z'%20fill='%236fb36f'%20opacity='.95'%20/%3e%3cpath%20d='M23%2041v10'%20fill='none'%20stroke='%23d6b45f'%20stroke-width='1.6'%20stroke-linecap='round'%20/%3e%3ccircle%20cx='31'%20cy='48'%20r='1.8'%20fill='%23c2412d'%20/%3e%3cpath%20d='M24%2044l13-11%2014%2013c-8%205.8-18%205.3-27-2z'%20fill='%232f6b3f'%20opacity='.97'%20/%3e%3cpath%20d='M30%2043l7-7%209%209c-5%202.7-11%202.5-16-2z'%20fill='%236fb36f'%20opacity='.95'%20/%3e%3cpath%20d='M37%2036v14'%20fill='none'%20stroke='%23d6b45f'%20stroke-width='2'%20stroke-linecap='round'%20/%3e%3cpath%20d='M30%2043c5-2%2011-1.5%2016%202'%20fill='none'%20stroke='%239fca78'%20stroke-width='1.8'%20stroke-linecap='round'%20/%3e%3ccircle%20cx='51'%20cy='46'%20r='2'%20fill='%23c2412d'%20/%3e%3cpath%20d='M40%2050l8-8%207%208c-4.2%203.2-10%203-15%200z'%20fill='%232f6b3f'%20opacity='.95'%20/%3e%3cpath%20d='M43%2049l5-5%204.5%205.5c-3%201.5-6%201.2-9.5-.5z'%20fill='%236fb36f'%20opacity='.95'%20/%3e%3cpath%20d='M48%2044v9'%20fill='none'%20stroke='%23d6b45f'%20stroke-width='1.5'%20stroke-linecap='round'%20/%3e%3ccircle%20cx='55'%20cy='50'%20r='1.7'%20fill='%23c2412d'%20/%3e%3ccircle%20cx='24'%20cy='11'%20r='3.2'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='40'%20cy='11'%20r='3.2'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='25'%20cy='34'%20r='2.1'%20fill='%23111827'%20/%3e%3ccircle%20cx='39'%20cy='34'%20r='2.1'%20fill='%23111827'%20/%3e%3c/svg%3e";
|
|
52
|
-
const appCss = "/assets/index-
|
|
53
|
+
const appCss = "/assets/index-BfBgrkgU.css";
|
|
53
54
|
const Route$s = createRootRoute({
|
|
54
55
|
head: () => ({
|
|
55
56
|
meta: [
|
|
@@ -92,7 +93,7 @@ function RootDocument({ children }) {
|
|
|
92
93
|
] })
|
|
93
94
|
] });
|
|
94
95
|
}
|
|
95
|
-
const $$splitComponentImporter$1 = () => import("./index-
|
|
96
|
+
const $$splitComponentImporter$1 = () => import("./index-ejrSItfz.mjs");
|
|
96
97
|
const Route$r = createFileRoute("/")({
|
|
97
98
|
component: lazyRouteComponent($$splitComponentImporter$1, "component")
|
|
98
99
|
});
|
|
@@ -135,7 +136,7 @@ function decodeSessionIdFromPath(encoded) {
|
|
|
135
136
|
function getSessionPath(sessionId) {
|
|
136
137
|
return `/session/${encodeSessionIdForPath(sessionId)}`;
|
|
137
138
|
}
|
|
138
|
-
const $$splitComponentImporter = () => import("../_sessionId-
|
|
139
|
+
const $$splitComponentImporter = () => import("../_sessionId-F8lm6PIb.mjs");
|
|
139
140
|
const Route$q = createFileRoute("/session/$sessionId")({
|
|
140
141
|
component: lazyRouteComponent($$splitComponentImporter, "component"),
|
|
141
142
|
parseParams: (params) => ({
|
|
@@ -146,16 +147,19 @@ const Route$q = createFileRoute("/session/$sessionId")({
|
|
|
146
147
|
})
|
|
147
148
|
});
|
|
148
149
|
const CURRENT_DATA_DIR_NAME = ".agent-inspector";
|
|
149
|
-
const
|
|
150
|
+
const LEGACY_DATA_DIR_NAMES = [".llm-inspector", ".llm-agent"];
|
|
150
151
|
function getDataDir() {
|
|
151
|
-
|
|
152
|
+
migrateLegacyDataDirs();
|
|
153
|
+
return resolveDataDir();
|
|
152
154
|
}
|
|
153
155
|
function hasDataDirState(pathExists, dir) {
|
|
154
156
|
return pathExists(join(dir, "providers.json")) || pathExists(join(dir, "config.json")) || pathExists(join(dir, "logs")) || pathExists(join(dir, "chunks"));
|
|
155
157
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
function defaultBaseDir(env = process.env, platform = process.platform) {
|
|
159
|
+
return platform === "win32" ? env["USERPROFILE"] ?? env["APPDATA"] ?? "C:\\" : env["HOME"] ?? "/tmp";
|
|
160
|
+
}
|
|
161
|
+
function resolveDataDir(_pathExists, env = process.env, platform = process.platform) {
|
|
162
|
+
const base = defaultBaseDir(env, platform);
|
|
159
163
|
const dirEnv = env["AGENT_INSPECTOR_DATA_DIR"];
|
|
160
164
|
if (dirEnv !== void 0 && dirEnv !== "") {
|
|
161
165
|
return isAbsolute(dirEnv) ? dirEnv : join(base, dirEnv);
|
|
@@ -164,14 +168,31 @@ function resolveDataDir(pathExists, env = process.env, platform = process.platfo
|
|
|
164
168
|
if (legacyDirEnv !== void 0 && legacyDirEnv !== "") {
|
|
165
169
|
return isAbsolute(legacyDirEnv) ? legacyDirEnv : join(base, legacyDirEnv);
|
|
166
170
|
}
|
|
167
|
-
|
|
168
|
-
const legacyDir = join(base, LEGACY_DATA_DIR_NAME);
|
|
169
|
-
return !hasDataDirState(pathExists, currentDir) && hasDataDirState(pathExists, legacyDir) ? legacyDir : currentDir;
|
|
171
|
+
return join(base, CURRENT_DATA_DIR_NAME);
|
|
170
172
|
}
|
|
171
173
|
function hasExplicitDataDir() {
|
|
172
174
|
return process.env["AGENT_INSPECTOR_DATA_DIR"] !== void 0 && process.env["AGENT_INSPECTOR_DATA_DIR"] !== "" || process.env["AGENT_INSPECTOR_CONFIG_DIR"] !== void 0 && process.env["AGENT_INSPECTOR_CONFIG_DIR"] !== "";
|
|
173
175
|
}
|
|
176
|
+
function migrateLegacyDataDirs() {
|
|
177
|
+
if (hasExplicitDataDir()) return;
|
|
178
|
+
const base = defaultBaseDir();
|
|
179
|
+
const currentDir = join(base, CURRENT_DATA_DIR_NAME);
|
|
180
|
+
if (hasDataDirState(existsSync, currentDir)) return;
|
|
181
|
+
for (const legacyName of LEGACY_DATA_DIR_NAMES) {
|
|
182
|
+
const legacyDir = join(base, legacyName);
|
|
183
|
+
if (!existsSync(legacyDir)) continue;
|
|
184
|
+
try {
|
|
185
|
+
cpSync(legacyDir, currentDir, { recursive: true, force: true });
|
|
186
|
+
rmSync(legacyDir, { recursive: true, force: true });
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
174
191
|
const RETENTION_DAYS = Number(process.env["LOG_RETENTION_DAYS"] ?? "7");
|
|
192
|
+
function writeStderrFallback(message, error) {
|
|
193
|
+
process.stderr.write(`${message} ${String(error)}
|
|
194
|
+
`);
|
|
195
|
+
}
|
|
175
196
|
function resolveLogDir() {
|
|
176
197
|
const logDirEnv = process.env["LOG_DIR"];
|
|
177
198
|
if (logDirEnv !== void 0 && logDirEnv !== "") {
|
|
@@ -212,7 +233,7 @@ async function initLogger() {
|
|
|
212
233
|
}
|
|
213
234
|
}
|
|
214
235
|
} catch (err) {
|
|
215
|
-
|
|
236
|
+
writeStderrFallback("[logger] Failed to initialize log directory:", err);
|
|
216
237
|
}
|
|
217
238
|
}
|
|
218
239
|
let loggerInitialized = false;
|
|
@@ -229,7 +250,7 @@ async function writeAppLog(message) {
|
|
|
229
250
|
await appendFile(logPath, `[${timestamp}] ${message}
|
|
230
251
|
`, "utf-8");
|
|
231
252
|
} catch (err) {
|
|
232
|
-
|
|
253
|
+
writeStderrFallback(`[logger] Failed to write to ${logPath}:`, err);
|
|
233
254
|
}
|
|
234
255
|
}
|
|
235
256
|
const logger = {
|
|
@@ -253,12 +274,27 @@ const logger = {
|
|
|
253
274
|
const MAX_BUFFER_SIZE = 1e3;
|
|
254
275
|
let writeBuffer = [];
|
|
255
276
|
let writeQueue = Promise.resolve();
|
|
277
|
+
let logWriteLock = Promise.resolve();
|
|
278
|
+
async function runWithLogWriteLock(fn) {
|
|
279
|
+
const previousLock = logWriteLock;
|
|
280
|
+
let releaseLock2 = () => {
|
|
281
|
+
};
|
|
282
|
+
logWriteLock = new Promise((resolve) => {
|
|
283
|
+
releaseLock2 = resolve;
|
|
284
|
+
});
|
|
285
|
+
await previousLock;
|
|
286
|
+
try {
|
|
287
|
+
return await fn();
|
|
288
|
+
} finally {
|
|
289
|
+
releaseLock2();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
256
292
|
function drainBuffer() {
|
|
257
293
|
const toWrite = writeBuffer.join("");
|
|
258
294
|
writeBuffer = [];
|
|
259
295
|
return toWrite;
|
|
260
296
|
}
|
|
261
|
-
async function
|
|
297
|
+
async function flushWriteBufferUnlocked() {
|
|
262
298
|
let toWrite;
|
|
263
299
|
{
|
|
264
300
|
toWrite = drainBuffer();
|
|
@@ -270,24 +306,31 @@ async function flushWriteBuffer() {
|
|
|
270
306
|
await appendFile(filePath, toWrite, "utf-8");
|
|
271
307
|
} catch (err) {
|
|
272
308
|
writeBuffer.unshift(toWrite);
|
|
273
|
-
|
|
309
|
+
writeStderrFallback("[logger] Failed to flush write buffer, re-queued:", err);
|
|
274
310
|
}
|
|
275
311
|
}
|
|
276
312
|
function appendLogEntry(entry) {
|
|
277
313
|
const line = JSON.stringify(entry) + "\n";
|
|
278
314
|
writeBuffer.push(line);
|
|
279
315
|
if (writeBuffer.length >= MAX_BUFFER_SIZE) {
|
|
280
|
-
writeQueue = writeQueue.then(() =>
|
|
316
|
+
writeQueue = writeQueue.then(() => runWithLogWriteLock(flushWriteBufferUnlocked));
|
|
281
317
|
} else if (writeBuffer.length === 1) {
|
|
282
|
-
writeQueue = writeQueue.then(() =>
|
|
318
|
+
writeQueue = writeQueue.then(() => runWithLogWriteLock(flushWriteBufferUnlocked));
|
|
283
319
|
}
|
|
284
320
|
}
|
|
285
321
|
async function flushLogBuffer() {
|
|
286
322
|
await writeQueue;
|
|
287
323
|
if (writeBuffer.length > 0) {
|
|
288
|
-
await
|
|
324
|
+
await runWithLogWriteLock(flushWriteBufferUnlocked);
|
|
289
325
|
}
|
|
290
326
|
}
|
|
327
|
+
async function runWithFlushedLogWriteLock(fn) {
|
|
328
|
+
await flushLogBuffer();
|
|
329
|
+
return await runWithLogWriteLock(async () => {
|
|
330
|
+
await flushWriteBufferUnlocked();
|
|
331
|
+
return await fn();
|
|
332
|
+
});
|
|
333
|
+
}
|
|
291
334
|
process.on("exit", () => {
|
|
292
335
|
if (writeBuffer.length > 0) {
|
|
293
336
|
const toWrite = drainBuffer();
|
|
@@ -304,127 +347,6 @@ for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
|
304
347
|
void flushLogBuffer().then(() => process.exit(0));
|
|
305
348
|
});
|
|
306
349
|
}
|
|
307
|
-
const INDEX_VERSION = 1;
|
|
308
|
-
const INDEX_FILE = "logs.idx";
|
|
309
|
-
function getIndexPath() {
|
|
310
|
-
return join(resolveLogDir(), INDEX_FILE);
|
|
311
|
-
}
|
|
312
|
-
let cachedIndex = null;
|
|
313
|
-
function createEmptyIndex() {
|
|
314
|
-
return {
|
|
315
|
-
version: INDEX_VERSION,
|
|
316
|
-
entries: {},
|
|
317
|
-
maxId: 0
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
function isLogIndex(obj) {
|
|
321
|
-
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return false;
|
|
322
|
-
const versionDesc = Object.getOwnPropertyDescriptor(obj, "version");
|
|
323
|
-
const entriesDesc = Object.getOwnPropertyDescriptor(obj, "entries");
|
|
324
|
-
const maxIdDesc = Object.getOwnPropertyDescriptor(obj, "maxId");
|
|
325
|
-
return versionDesc !== void 0 && typeof versionDesc.value === "number" && entriesDesc !== void 0 && typeof entriesDesc.value === "object" && entriesDesc.value !== null && maxIdDesc !== void 0 && typeof maxIdDesc.value === "number";
|
|
326
|
-
}
|
|
327
|
-
async function loadIndex() {
|
|
328
|
-
if (cachedIndex !== null) return cachedIndex;
|
|
329
|
-
const indexPath = getIndexPath();
|
|
330
|
-
if (!existsSync(indexPath)) {
|
|
331
|
-
cachedIndex = createEmptyIndex();
|
|
332
|
-
return cachedIndex;
|
|
333
|
-
}
|
|
334
|
-
try {
|
|
335
|
-
const content = await readFile(indexPath, "utf-8");
|
|
336
|
-
const parsed = JSON.parse(content);
|
|
337
|
-
if (isLogIndex(parsed)) {
|
|
338
|
-
cachedIndex = parsed;
|
|
339
|
-
} else {
|
|
340
|
-
cachedIndex = createEmptyIndex();
|
|
341
|
-
}
|
|
342
|
-
return cachedIndex;
|
|
343
|
-
} catch (err) {
|
|
344
|
-
logger.error("[logIndex] Failed to load index:", String(err));
|
|
345
|
-
cachedIndex = createEmptyIndex();
|
|
346
|
-
return cachedIndex;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
async function saveIndex(index) {
|
|
350
|
-
const indexPath = getIndexPath();
|
|
351
|
-
const dir = dirname(indexPath);
|
|
352
|
-
try {
|
|
353
|
-
await mkdir(dir, { recursive: true });
|
|
354
|
-
} catch {
|
|
355
|
-
}
|
|
356
|
-
try {
|
|
357
|
-
await writeFile(indexPath, JSON.stringify(index), "utf-8");
|
|
358
|
-
} catch (err) {
|
|
359
|
-
logger.error("[logIndex] Failed to save index:", String(err));
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
async function addToIndex(id, file, byteOffset, byteLength) {
|
|
363
|
-
const index = await loadIndex();
|
|
364
|
-
index.entries[id] = { id, file, byteOffset, byteLength };
|
|
365
|
-
if (id > index.maxId) {
|
|
366
|
-
index.maxId = id;
|
|
367
|
-
}
|
|
368
|
-
scheduleIndexFlush();
|
|
369
|
-
}
|
|
370
|
-
let indexFlushScheduled = false;
|
|
371
|
-
async function flushIndexAsync() {
|
|
372
|
-
indexFlushScheduled = false;
|
|
373
|
-
const index = await loadIndex();
|
|
374
|
-
await saveIndex(index);
|
|
375
|
-
}
|
|
376
|
-
function scheduleIndexFlush() {
|
|
377
|
-
if (indexFlushScheduled) return;
|
|
378
|
-
indexFlushScheduled = true;
|
|
379
|
-
setTimeout(() => {
|
|
380
|
-
void flushIndexAsync();
|
|
381
|
-
}, 1e3);
|
|
382
|
-
}
|
|
383
|
-
async function findInIndex(id) {
|
|
384
|
-
const index = await loadIndex();
|
|
385
|
-
return index.entries[id] ?? null;
|
|
386
|
-
}
|
|
387
|
-
let idGenerationPromise = null;
|
|
388
|
-
let releaseLock = null;
|
|
389
|
-
async function acquireLock() {
|
|
390
|
-
if (releaseLock === null) {
|
|
391
|
-
idGenerationPromise = new Promise((resolve) => {
|
|
392
|
-
releaseLock = resolve;
|
|
393
|
-
});
|
|
394
|
-
} else {
|
|
395
|
-
await idGenerationPromise;
|
|
396
|
-
idGenerationPromise = new Promise((resolve) => {
|
|
397
|
-
releaseLock = resolve;
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
function releaseLockFn() {
|
|
402
|
-
if (releaseLock) {
|
|
403
|
-
const resolve = releaseLock;
|
|
404
|
-
releaseLock = null;
|
|
405
|
-
idGenerationPromise = null;
|
|
406
|
-
resolve();
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
async function getNextLogId() {
|
|
410
|
-
await acquireLock();
|
|
411
|
-
try {
|
|
412
|
-
const index = await loadIndex();
|
|
413
|
-
const nextId = index.maxId + 1;
|
|
414
|
-
index.maxId = nextId;
|
|
415
|
-
cachedIndex = index;
|
|
416
|
-
return nextId;
|
|
417
|
-
} finally {
|
|
418
|
-
releaseLockFn();
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
function getCurrentLogFile() {
|
|
422
|
-
const now = /* @__PURE__ */ new Date();
|
|
423
|
-
const yyyy = now.getUTCFullYear();
|
|
424
|
-
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
425
|
-
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
426
|
-
return `${yyyy}-${mm}-${dd}.jsonl`;
|
|
427
|
-
}
|
|
428
350
|
const JsonValueSchema = lazy(
|
|
429
351
|
() => union([
|
|
430
352
|
string(),
|
|
@@ -438,14 +360,6 @@ const JsonValueSchema = lazy(
|
|
|
438
360
|
function trustAsJsonValue(value) {
|
|
439
361
|
return value;
|
|
440
362
|
}
|
|
441
|
-
function isPlainRecord(val) {
|
|
442
|
-
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
443
|
-
}
|
|
444
|
-
function safeGetOwnProperty(obj, key) {
|
|
445
|
-
if (obj === null || typeof obj !== "object" || Array.isArray(obj)) return void 0;
|
|
446
|
-
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
447
|
-
return desc?.value;
|
|
448
|
-
}
|
|
449
363
|
const CacheControl = object({
|
|
450
364
|
type: string(),
|
|
451
365
|
ttl: string().optional(),
|
|
@@ -807,6 +721,7 @@ const CapturedLogSchema = object({
|
|
|
807
721
|
headers: record(string(), string()).optional(),
|
|
808
722
|
apiFormat: _enum(["anthropic", "openai", "unknown"]).default("unknown"),
|
|
809
723
|
isTest: boolean().optional().default(false),
|
|
724
|
+
replayOfLogId: number().nullable().optional(),
|
|
810
725
|
providerName: string().nullable().optional(),
|
|
811
726
|
clientPort: number().nullable().optional(),
|
|
812
727
|
clientPid: number().nullable().optional(),
|
|
@@ -897,7 +812,7 @@ function writeChunks(logId, chunks, truncated) {
|
|
|
897
812
|
try {
|
|
898
813
|
renameSync(tempPath, targetPath);
|
|
899
814
|
} catch (err) {
|
|
900
|
-
|
|
815
|
+
logger.warn("[chunkStorage] Rename failed, falling back to copy+delete:", String(err));
|
|
901
816
|
try {
|
|
902
817
|
copyFileSync(tempPath, targetPath);
|
|
903
818
|
unlinkSync(tempPath);
|
|
@@ -921,6 +836,193 @@ function readChunks(path2) {
|
|
|
921
836
|
return null;
|
|
922
837
|
}
|
|
923
838
|
}
|
|
839
|
+
const INDEX_VERSION = 1;
|
|
840
|
+
const INDEX_FILE = "logs.idx";
|
|
841
|
+
function getIndexPath() {
|
|
842
|
+
return join(resolveLogDir(), INDEX_FILE);
|
|
843
|
+
}
|
|
844
|
+
let cachedIndex = null;
|
|
845
|
+
function createEmptyIndex() {
|
|
846
|
+
return {
|
|
847
|
+
version: INDEX_VERSION,
|
|
848
|
+
entries: {},
|
|
849
|
+
maxId: 0
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
function isLogIndex(obj) {
|
|
853
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return false;
|
|
854
|
+
const versionDesc = Object.getOwnPropertyDescriptor(obj, "version");
|
|
855
|
+
const entriesDesc = Object.getOwnPropertyDescriptor(obj, "entries");
|
|
856
|
+
const maxIdDesc = Object.getOwnPropertyDescriptor(obj, "maxId");
|
|
857
|
+
return versionDesc !== void 0 && typeof versionDesc.value === "number" && entriesDesc !== void 0 && typeof entriesDesc.value === "object" && entriesDesc.value !== null && maxIdDesc !== void 0 && typeof maxIdDesc.value === "number";
|
|
858
|
+
}
|
|
859
|
+
async function loadIndex() {
|
|
860
|
+
if (cachedIndex !== null) return cachedIndex;
|
|
861
|
+
const indexPath = getIndexPath();
|
|
862
|
+
if (!existsSync(indexPath)) {
|
|
863
|
+
cachedIndex = createEmptyIndex();
|
|
864
|
+
return cachedIndex;
|
|
865
|
+
}
|
|
866
|
+
try {
|
|
867
|
+
const content = await readFile(indexPath, "utf-8");
|
|
868
|
+
const parsed = JSON.parse(content);
|
|
869
|
+
if (isLogIndex(parsed)) {
|
|
870
|
+
cachedIndex = parsed;
|
|
871
|
+
} else {
|
|
872
|
+
cachedIndex = createEmptyIndex();
|
|
873
|
+
}
|
|
874
|
+
return cachedIndex;
|
|
875
|
+
} catch (err) {
|
|
876
|
+
logger.error("[logIndex] Failed to load index:", String(err));
|
|
877
|
+
cachedIndex = createEmptyIndex();
|
|
878
|
+
return cachedIndex;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
async function saveIndex(index) {
|
|
882
|
+
const indexPath = getIndexPath();
|
|
883
|
+
const dir = dirname(indexPath);
|
|
884
|
+
try {
|
|
885
|
+
await mkdir(dir, { recursive: true });
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
888
|
+
try {
|
|
889
|
+
await writeFile(indexPath, JSON.stringify(index), "utf-8");
|
|
890
|
+
} catch (err) {
|
|
891
|
+
logger.error("[logIndex] Failed to save index:", String(err));
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
async function addToIndex(id, file, byteOffset, byteLength) {
|
|
895
|
+
const index = await loadIndex();
|
|
896
|
+
index.entries[id] = { id, file, byteOffset, byteLength };
|
|
897
|
+
if (id > index.maxId) {
|
|
898
|
+
index.maxId = id;
|
|
899
|
+
}
|
|
900
|
+
scheduleIndexFlush();
|
|
901
|
+
}
|
|
902
|
+
let indexFlushScheduled = false;
|
|
903
|
+
let indexFlushTimeout = null;
|
|
904
|
+
async function flushIndexAsync() {
|
|
905
|
+
indexFlushScheduled = false;
|
|
906
|
+
indexFlushTimeout = null;
|
|
907
|
+
const index = await loadIndex();
|
|
908
|
+
await saveIndex(index);
|
|
909
|
+
}
|
|
910
|
+
function scheduleIndexFlush() {
|
|
911
|
+
if (indexFlushScheduled) return;
|
|
912
|
+
indexFlushScheduled = true;
|
|
913
|
+
indexFlushTimeout = setTimeout(() => {
|
|
914
|
+
void flushIndexAsync();
|
|
915
|
+
}, 1e3);
|
|
916
|
+
}
|
|
917
|
+
async function flushIndex() {
|
|
918
|
+
if (indexFlushTimeout !== null) {
|
|
919
|
+
clearTimeout(indexFlushTimeout);
|
|
920
|
+
indexFlushTimeout = null;
|
|
921
|
+
}
|
|
922
|
+
indexFlushScheduled = false;
|
|
923
|
+
const index = await loadIndex();
|
|
924
|
+
await saveIndex(index);
|
|
925
|
+
}
|
|
926
|
+
async function findInIndex(id) {
|
|
927
|
+
const index = await loadIndex();
|
|
928
|
+
return index.entries[id] ?? null;
|
|
929
|
+
}
|
|
930
|
+
async function indexFile(filePath, file) {
|
|
931
|
+
const entries = {};
|
|
932
|
+
let maxId = 0;
|
|
933
|
+
let byteOffset = 0;
|
|
934
|
+
const fileStream = createInterface({
|
|
935
|
+
input: createReadStream(filePath),
|
|
936
|
+
crlfDelay: Infinity
|
|
937
|
+
});
|
|
938
|
+
for await (const line of fileStream) {
|
|
939
|
+
const lineBytes = Buffer.byteLength(line, "utf-8") + 1;
|
|
940
|
+
if (line.trim() !== "") {
|
|
941
|
+
try {
|
|
942
|
+
const entry = JSON.parse(line);
|
|
943
|
+
if (typeof entry === "object" && entry !== null && !Array.isArray(entry)) {
|
|
944
|
+
const idDesc = Object.getOwnPropertyDescriptor(entry, "id");
|
|
945
|
+
if (idDesc !== void 0 && typeof idDesc.value === "number") {
|
|
946
|
+
const entryId = idDesc.value;
|
|
947
|
+
maxId = Math.max(maxId, entryId);
|
|
948
|
+
entries[entryId] = {
|
|
949
|
+
id: entryId,
|
|
950
|
+
file,
|
|
951
|
+
byteOffset,
|
|
952
|
+
byteLength: lineBytes
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
} catch {
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
byteOffset += lineBytes;
|
|
960
|
+
}
|
|
961
|
+
return { entries, maxId };
|
|
962
|
+
}
|
|
963
|
+
async function rebuildIndex() {
|
|
964
|
+
const logDir = resolveLogDir();
|
|
965
|
+
const newIndex = createEmptyIndex();
|
|
966
|
+
if (!existsSync(logDir)) {
|
|
967
|
+
cachedIndex = newIndex;
|
|
968
|
+
await saveIndex(newIndex);
|
|
969
|
+
return newIndex;
|
|
970
|
+
}
|
|
971
|
+
const files = (await readdir(logDir)).filter((f) => f.endsWith(".jsonl")).sort();
|
|
972
|
+
const CONCURRENCY = 4;
|
|
973
|
+
for (let i = 0; i < files.length; i += CONCURRENCY) {
|
|
974
|
+
const batch = files.slice(i, i + CONCURRENCY);
|
|
975
|
+
const results = await Promise.all(batch.map((file) => indexFile(join(logDir, file), file)));
|
|
976
|
+
for (const result of results) {
|
|
977
|
+
Object.assign(newIndex.entries, result.entries);
|
|
978
|
+
newIndex.maxId = Math.max(newIndex.maxId, result.maxId);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
cachedIndex = newIndex;
|
|
982
|
+
await saveIndex(newIndex);
|
|
983
|
+
return newIndex;
|
|
984
|
+
}
|
|
985
|
+
let idGenerationPromise = null;
|
|
986
|
+
let releaseLock = null;
|
|
987
|
+
async function acquireLock() {
|
|
988
|
+
if (releaseLock === null) {
|
|
989
|
+
idGenerationPromise = new Promise((resolve) => {
|
|
990
|
+
releaseLock = resolve;
|
|
991
|
+
});
|
|
992
|
+
} else {
|
|
993
|
+
await idGenerationPromise;
|
|
994
|
+
idGenerationPromise = new Promise((resolve) => {
|
|
995
|
+
releaseLock = resolve;
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function releaseLockFn() {
|
|
1000
|
+
if (releaseLock) {
|
|
1001
|
+
const resolve = releaseLock;
|
|
1002
|
+
releaseLock = null;
|
|
1003
|
+
idGenerationPromise = null;
|
|
1004
|
+
resolve();
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
async function getNextLogId() {
|
|
1008
|
+
await acquireLock();
|
|
1009
|
+
try {
|
|
1010
|
+
const index = await loadIndex();
|
|
1011
|
+
const nextId = index.maxId + 1;
|
|
1012
|
+
index.maxId = nextId;
|
|
1013
|
+
cachedIndex = index;
|
|
1014
|
+
return nextId;
|
|
1015
|
+
} finally {
|
|
1016
|
+
releaseLockFn();
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
function getCurrentLogFile() {
|
|
1020
|
+
const now = /* @__PURE__ */ new Date();
|
|
1021
|
+
const yyyy = now.getUTCFullYear();
|
|
1022
|
+
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
1023
|
+
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
1024
|
+
return `${yyyy}-${mm}-${dd}.jsonl`;
|
|
1025
|
+
}
|
|
924
1026
|
const IDLE_TIMEOUT_MS = Number(process.env["SESSION_PROCESS_IDLE_MS"]) || 5 * 60 * 1e3;
|
|
925
1027
|
const MAX_RESTARTS = 3;
|
|
926
1028
|
const _processes = /* @__PURE__ */ new Map();
|
|
@@ -1259,9 +1361,11 @@ async function addTestLogEntry(entry) {
|
|
|
1259
1361
|
sessionId: session.id,
|
|
1260
1362
|
streamingChunksPath
|
|
1261
1363
|
};
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1364
|
+
await runWithLogWriteLock(async () => {
|
|
1365
|
+
const logFile = getCurrentLogFile();
|
|
1366
|
+
appendLogEntry(log);
|
|
1367
|
+
await addToIndex(id, logFile, -1, -1);
|
|
1368
|
+
});
|
|
1265
1369
|
addToCache(log);
|
|
1266
1370
|
observeSessionLog(log, session.source);
|
|
1267
1371
|
emitLogUpdate(log);
|
|
@@ -1304,9 +1408,11 @@ async function createLog(method, path2, requestBody, headers, clientInfo, rawHea
|
|
|
1304
1408
|
clientProjectFolder: clientInfo?.projectFolder ?? null,
|
|
1305
1409
|
streamingChunksPath: null
|
|
1306
1410
|
};
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1411
|
+
await runWithLogWriteLock(async () => {
|
|
1412
|
+
const logFile = getCurrentLogFile();
|
|
1413
|
+
appendLogEntry(log);
|
|
1414
|
+
await addToIndex(id, logFile, -1, -1);
|
|
1415
|
+
});
|
|
1310
1416
|
addToCache(log);
|
|
1311
1417
|
markSessionStarted(log, session.source);
|
|
1312
1418
|
emitLogUpdate(log);
|
|
@@ -1388,11 +1494,81 @@ async function getLogById(id) {
|
|
|
1388
1494
|
}
|
|
1389
1495
|
function getFilteredLogs(sessionId, model) {
|
|
1390
1496
|
return [...memoryCache.values()].filter((l) => {
|
|
1391
|
-
|
|
1392
|
-
if (model !== void 0 && l.model !== model) return false;
|
|
1393
|
-
return true;
|
|
1497
|
+
return matchesLogFilters(l, sessionId, model);
|
|
1394
1498
|
});
|
|
1395
1499
|
}
|
|
1500
|
+
function matchesLogFilters(log, sessionId, model) {
|
|
1501
|
+
if (sessionId !== void 0 && getLogSessionId(log) !== sessionId) return false;
|
|
1502
|
+
if (model !== void 0 && log.model !== model) return false;
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1505
|
+
function requestedWindowFitsMemory(filteredMemoryCount, offset, limit) {
|
|
1506
|
+
return offset + limit <= filteredMemoryCount;
|
|
1507
|
+
}
|
|
1508
|
+
function paginateLogs(logs, offset, limit) {
|
|
1509
|
+
return logs.slice(offset, offset + limit);
|
|
1510
|
+
}
|
|
1511
|
+
async function readPersistedLogsById() {
|
|
1512
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1513
|
+
const logDir = resolveLogDir();
|
|
1514
|
+
if (!existsSync(logDir)) return byId;
|
|
1515
|
+
try {
|
|
1516
|
+
const entries = await readdir(logDir);
|
|
1517
|
+
const files = entries.filter((file) => file.endsWith(".jsonl")).sort();
|
|
1518
|
+
for (const file of files) {
|
|
1519
|
+
await readPersistedLogFile(join(logDir, file), byId);
|
|
1520
|
+
}
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
logger.error("[store] Failed to list persisted logs:", String(err));
|
|
1523
|
+
}
|
|
1524
|
+
for (const log of memoryCache.values()) {
|
|
1525
|
+
byId.set(log.id, log);
|
|
1526
|
+
}
|
|
1527
|
+
return byId;
|
|
1528
|
+
}
|
|
1529
|
+
async function readPersistedLogFile(filePath, byId) {
|
|
1530
|
+
if (!existsSync(filePath)) return;
|
|
1531
|
+
try {
|
|
1532
|
+
const fileStream = createInterface({
|
|
1533
|
+
input: createReadStream(filePath),
|
|
1534
|
+
crlfDelay: Infinity
|
|
1535
|
+
});
|
|
1536
|
+
for await (const line of fileStream) {
|
|
1537
|
+
if (line.trim() === "") continue;
|
|
1538
|
+
try {
|
|
1539
|
+
const parsed = JSON.parse(line);
|
|
1540
|
+
const result = CapturedLogSchema.safeParse(parsed);
|
|
1541
|
+
if (result.success) {
|
|
1542
|
+
const log = normalizeLogSession(result.data);
|
|
1543
|
+
byId.set(log.id, log);
|
|
1544
|
+
observeSessionLog(log);
|
|
1545
|
+
}
|
|
1546
|
+
} catch {
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
} catch (err) {
|
|
1550
|
+
logger.error("[store] Failed to read persisted log file:", filePath, String(err));
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
async function listLogsPage(options) {
|
|
1554
|
+
const memoryLogs = getFilteredLogs(options.sessionId, options.model);
|
|
1555
|
+
if (requestedWindowFitsMemory(memoryLogs.length, options.offset, options.limit)) {
|
|
1556
|
+
return {
|
|
1557
|
+
logs: paginateLogs(memoryLogs, options.offset, options.limit),
|
|
1558
|
+
total: memoryLogs.length,
|
|
1559
|
+
offset: options.offset,
|
|
1560
|
+
limit: options.limit
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
const persistedById = await readPersistedLogsById();
|
|
1564
|
+
const persistedLogs = [...persistedById.values()].filter((log) => matchesLogFilters(log, options.sessionId, options.model)).sort((left, right) => left.id - right.id);
|
|
1565
|
+
return {
|
|
1566
|
+
logs: paginateLogs(persistedLogs, options.offset, options.limit),
|
|
1567
|
+
total: persistedLogs.length,
|
|
1568
|
+
offset: options.offset,
|
|
1569
|
+
limit: options.limit
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1396
1572
|
function getSessions() {
|
|
1397
1573
|
return getSessionIds();
|
|
1398
1574
|
}
|
|
@@ -1403,12 +1579,216 @@ function getModels() {
|
|
|
1403
1579
|
}
|
|
1404
1580
|
return [...set];
|
|
1405
1581
|
}
|
|
1582
|
+
function getReplayLogsForSource(sourceLogId) {
|
|
1583
|
+
return [...memoryCache.values()].filter((log) => log.replayOfLogId === sourceLogId);
|
|
1584
|
+
}
|
|
1585
|
+
function collectDirectoryStats(dir, shouldCount) {
|
|
1586
|
+
if (!existsSync(dir)) return { fileCount: 0, bytes: 0 };
|
|
1587
|
+
let fileCount = 0;
|
|
1588
|
+
let bytes = 0;
|
|
1589
|
+
const visit = (currentDir, depth) => {
|
|
1590
|
+
if (depth > 8) return;
|
|
1591
|
+
let entries;
|
|
1592
|
+
try {
|
|
1593
|
+
entries = readdirSync(currentDir);
|
|
1594
|
+
} catch {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
for (const entry of entries) {
|
|
1598
|
+
const path2 = join(currentDir, entry);
|
|
1599
|
+
try {
|
|
1600
|
+
const stats = statSync(path2);
|
|
1601
|
+
if (stats.isDirectory()) {
|
|
1602
|
+
visit(path2, depth + 1);
|
|
1603
|
+
} else if (shouldCount(entry)) {
|
|
1604
|
+
fileCount += 1;
|
|
1605
|
+
bytes += stats.size;
|
|
1606
|
+
}
|
|
1607
|
+
} catch {
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
visit(dir, 0);
|
|
1612
|
+
return { fileCount, bytes };
|
|
1613
|
+
}
|
|
1614
|
+
function getLogStorageStats() {
|
|
1615
|
+
const logDir = resolveLogDir();
|
|
1616
|
+
const chunkDir = getChunksDir();
|
|
1617
|
+
const logStats = collectDirectoryStats(logDir, (fileName) => fileName.endsWith(".jsonl"));
|
|
1618
|
+
const chunkStats = collectDirectoryStats(chunkDir, (fileName) => fileName.endsWith(".json"));
|
|
1619
|
+
return {
|
|
1620
|
+
memoryCount: memoryCache.size,
|
|
1621
|
+
logDir,
|
|
1622
|
+
logFileCount: logStats.fileCount,
|
|
1623
|
+
logBytes: logStats.bytes,
|
|
1624
|
+
chunkDir,
|
|
1625
|
+
chunkFileCount: chunkStats.fileCount,
|
|
1626
|
+
chunkBytes: chunkStats.bytes
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1406
1629
|
function clearAllLogs() {
|
|
1407
1630
|
const count = memoryCache.size;
|
|
1408
1631
|
memoryCache.clear();
|
|
1409
1632
|
clearSessionRegistry();
|
|
1410
1633
|
return { cleared: count };
|
|
1411
1634
|
}
|
|
1635
|
+
async function deleteMatchingFiles(dir, shouldDelete) {
|
|
1636
|
+
if (!existsSync(dir)) return 0;
|
|
1637
|
+
let deleted = 0;
|
|
1638
|
+
let entries;
|
|
1639
|
+
try {
|
|
1640
|
+
entries = await readdir(dir);
|
|
1641
|
+
} catch (err) {
|
|
1642
|
+
logger.warn("[store] Failed to list log storage directory:", String(err));
|
|
1643
|
+
return deleted;
|
|
1644
|
+
}
|
|
1645
|
+
for (const entry of entries) {
|
|
1646
|
+
if (!shouldDelete(entry)) continue;
|
|
1647
|
+
try {
|
|
1648
|
+
await unlink(join(dir, entry));
|
|
1649
|
+
deleted += 1;
|
|
1650
|
+
} catch (err) {
|
|
1651
|
+
logger.warn("[store] Failed to delete log storage file:", entry, String(err));
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return deleted;
|
|
1655
|
+
}
|
|
1656
|
+
async function clearPersistedLogStorage() {
|
|
1657
|
+
return await runWithFlushedLogWriteLock(async () => {
|
|
1658
|
+
await flushIndex();
|
|
1659
|
+
const result = clearAllLogs();
|
|
1660
|
+
const [logFilesDeleted, chunkFilesDeleted] = await Promise.all([
|
|
1661
|
+
deleteMatchingFiles(
|
|
1662
|
+
resolveLogDir(),
|
|
1663
|
+
(fileName) => fileName.endsWith(".jsonl") || fileName === "logs.idx"
|
|
1664
|
+
),
|
|
1665
|
+
deleteMatchingFiles(
|
|
1666
|
+
getChunksDir(),
|
|
1667
|
+
(fileName) => fileName.endsWith(".json") || fileName.startsWith(".") && fileName.endsWith(".tmp")
|
|
1668
|
+
)
|
|
1669
|
+
]);
|
|
1670
|
+
await rebuildIndex();
|
|
1671
|
+
return {
|
|
1672
|
+
cleared: result.cleared,
|
|
1673
|
+
logFilesDeleted,
|
|
1674
|
+
chunkFilesDeleted
|
|
1675
|
+
};
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
function readLogIdFromLine(line) {
|
|
1679
|
+
if (line.trim() === "") return null;
|
|
1680
|
+
try {
|
|
1681
|
+
const parsed = JSON.parse(line);
|
|
1682
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
1683
|
+
const idDesc = Object.getOwnPropertyDescriptor(parsed, "id");
|
|
1684
|
+
if (idDesc === void 0 || typeof idDesc.value !== "number") return null;
|
|
1685
|
+
return idDesc.value;
|
|
1686
|
+
} catch {
|
|
1687
|
+
return null;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
async function rewriteLogFileWithoutIds(filePath, ids) {
|
|
1691
|
+
let content;
|
|
1692
|
+
try {
|
|
1693
|
+
content = await readFile(filePath, "utf-8");
|
|
1694
|
+
} catch (err) {
|
|
1695
|
+
logger.warn("[store] Failed to read log file for deletion:", filePath, String(err));
|
|
1696
|
+
return { rewritten: false, removedIds: /* @__PURE__ */ new Set() };
|
|
1697
|
+
}
|
|
1698
|
+
const lines = content.split("\n");
|
|
1699
|
+
const keptLines = [];
|
|
1700
|
+
const removedIds = /* @__PURE__ */ new Set();
|
|
1701
|
+
for (const line of lines) {
|
|
1702
|
+
if (line === "") continue;
|
|
1703
|
+
const id = readLogIdFromLine(line);
|
|
1704
|
+
if (id !== null && ids.has(id)) {
|
|
1705
|
+
removedIds.add(id);
|
|
1706
|
+
} else {
|
|
1707
|
+
keptLines.push(line);
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
if (removedIds.size === 0) return { rewritten: false, removedIds };
|
|
1711
|
+
try {
|
|
1712
|
+
if (keptLines.length === 0) {
|
|
1713
|
+
await unlink(filePath);
|
|
1714
|
+
} else {
|
|
1715
|
+
await writeFile(filePath, `${keptLines.join("\n")}
|
|
1716
|
+
`, "utf-8");
|
|
1717
|
+
}
|
|
1718
|
+
} catch (err) {
|
|
1719
|
+
logger.warn("[store] Failed to rewrite log file after deletion:", filePath, String(err));
|
|
1720
|
+
return { rewritten: false, removedIds: /* @__PURE__ */ new Set() };
|
|
1721
|
+
}
|
|
1722
|
+
return { rewritten: true, removedIds };
|
|
1723
|
+
}
|
|
1724
|
+
async function rewriteLogFilesWithoutIds(ids) {
|
|
1725
|
+
const logDir = resolveLogDir();
|
|
1726
|
+
if (!existsSync(logDir)) return { logFilesRewritten: 0, removedIds: /* @__PURE__ */ new Set() };
|
|
1727
|
+
let entries;
|
|
1728
|
+
try {
|
|
1729
|
+
entries = await readdir(logDir);
|
|
1730
|
+
} catch (err) {
|
|
1731
|
+
logger.warn("[store] Failed to list log directory for deletion:", String(err));
|
|
1732
|
+
return { logFilesRewritten: 0, removedIds: /* @__PURE__ */ new Set() };
|
|
1733
|
+
}
|
|
1734
|
+
let logFilesRewritten = 0;
|
|
1735
|
+
const removedIds = /* @__PURE__ */ new Set();
|
|
1736
|
+
for (const entry of entries) {
|
|
1737
|
+
if (!entry.endsWith(".jsonl")) continue;
|
|
1738
|
+
const result = await rewriteLogFileWithoutIds(join(logDir, entry), ids);
|
|
1739
|
+
if (result.rewritten) logFilesRewritten += 1;
|
|
1740
|
+
for (const removedId of result.removedIds) {
|
|
1741
|
+
removedIds.add(removedId);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
return { logFilesRewritten, removedIds };
|
|
1745
|
+
}
|
|
1746
|
+
async function deleteChunkFilesByIds(ids) {
|
|
1747
|
+
const chunkDir = getChunksDir();
|
|
1748
|
+
if (!existsSync(chunkDir)) return 0;
|
|
1749
|
+
let deleted = 0;
|
|
1750
|
+
for (const id of ids) {
|
|
1751
|
+
const candidates2 = [`${String(id)}.json`, `.${String(id)}.tmp`];
|
|
1752
|
+
for (const candidate of candidates2) {
|
|
1753
|
+
try {
|
|
1754
|
+
await unlink(join(chunkDir, candidate));
|
|
1755
|
+
deleted += 1;
|
|
1756
|
+
} catch {
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return deleted;
|
|
1761
|
+
}
|
|
1762
|
+
async function clearPersistedLogsByIds(ids) {
|
|
1763
|
+
const uniqueIds = new Set(ids);
|
|
1764
|
+
if (uniqueIds.size === 0) {
|
|
1765
|
+
return { cleared: 0, logFilesRewritten: 0, chunkFilesDeleted: 0 };
|
|
1766
|
+
}
|
|
1767
|
+
return await runWithFlushedLogWriteLock(async () => {
|
|
1768
|
+
await flushIndex();
|
|
1769
|
+
const memoryRemovedIds = /* @__PURE__ */ new Set();
|
|
1770
|
+
for (const id of uniqueIds) {
|
|
1771
|
+
if (memoryCache.has(id)) memoryRemovedIds.add(id);
|
|
1772
|
+
}
|
|
1773
|
+
const result = clearLogsByIds([...uniqueIds]);
|
|
1774
|
+
const [rewriteResult, chunkFilesDeleted] = await Promise.all([
|
|
1775
|
+
rewriteLogFilesWithoutIds(uniqueIds),
|
|
1776
|
+
deleteChunkFilesByIds(uniqueIds)
|
|
1777
|
+
]);
|
|
1778
|
+
await rebuildIndex();
|
|
1779
|
+
const clearedIds = new Set(rewriteResult.removedIds);
|
|
1780
|
+
if (result.cleared > 0) {
|
|
1781
|
+
for (const id of memoryRemovedIds) {
|
|
1782
|
+
clearedIds.add(id);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
cleared: clearedIds.size,
|
|
1787
|
+
logFilesRewritten: rewriteResult.logFilesRewritten,
|
|
1788
|
+
chunkFilesDeleted
|
|
1789
|
+
};
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1412
1792
|
function clearLogsByIds(ids) {
|
|
1413
1793
|
const unique2 = /* @__PURE__ */ new Set();
|
|
1414
1794
|
for (const id of ids) {
|
|
@@ -2047,6 +2427,10 @@ const alibabaProvider = {
|
|
|
2047
2427
|
}
|
|
2048
2428
|
};
|
|
2049
2429
|
registry.register(alibabaProvider);
|
|
2430
|
+
function maskApiKey(apiKey) {
|
|
2431
|
+
if (apiKey.length <= 8) return "••••••••";
|
|
2432
|
+
return apiKey.slice(0, 4) + "••••••••" + apiKey.slice(-4);
|
|
2433
|
+
}
|
|
2050
2434
|
const ProviderModelMetadataSchema = object({
|
|
2051
2435
|
model: string().min(1),
|
|
2052
2436
|
contextWindow: number().int().positive().optional(),
|
|
@@ -2613,6 +2997,15 @@ function getProvider(id) {
|
|
|
2613
2997
|
const providers = getProviders();
|
|
2614
2998
|
return providers.find((p) => p.id === id);
|
|
2615
2999
|
}
|
|
3000
|
+
function redactProvider(provider) {
|
|
3001
|
+
return {
|
|
3002
|
+
...provider,
|
|
3003
|
+
apiKey: maskApiKey(provider.apiKey)
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
function redactProviders(providers) {
|
|
3007
|
+
return providers.map(redactProvider);
|
|
3008
|
+
}
|
|
2616
3009
|
function normalizeApiKey(apiKey) {
|
|
2617
3010
|
return apiKey.replace(/^Bearer\s+/i, "").trim();
|
|
2618
3011
|
}
|
|
@@ -2779,10 +3172,6 @@ function importProviders(json) {
|
|
|
2779
3172
|
}
|
|
2780
3173
|
return { imported, errors };
|
|
2781
3174
|
}
|
|
2782
|
-
function maskApiKey(apiKey) {
|
|
2783
|
-
if (apiKey.length <= 8) return "••••••••";
|
|
2784
|
-
return apiKey.slice(0, 4) + "••••••••" + apiKey.slice(-4);
|
|
2785
|
-
}
|
|
2786
3175
|
function getModelUsageName(model, providerName) {
|
|
2787
3176
|
if (providerName !== void 0 && providerName !== "" && providerName.toLowerCase().includes("minimax")) {
|
|
2788
3177
|
return model.replace(/ /g, "-");
|
|
@@ -2824,6 +3213,10 @@ const execFileAsync = promisify(execFile);
|
|
|
2824
3213
|
const cache = /* @__PURE__ */ new Map();
|
|
2825
3214
|
const CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
2826
3215
|
const MAX_CACHE_SIZE = 200;
|
|
3216
|
+
const RemotePortSchema = number().int().min(1).max(65535);
|
|
3217
|
+
const SocketSchema = object({
|
|
3218
|
+
remotePort: RemotePortSchema.nullish()
|
|
3219
|
+
}).passthrough();
|
|
2827
3220
|
const inflight = /* @__PURE__ */ new Map();
|
|
2828
3221
|
function evictCacheIfNeeded() {
|
|
2829
3222
|
while (cache.size > MAX_CACHE_SIZE) {
|
|
@@ -2845,10 +3238,13 @@ function setCache(port, info) {
|
|
|
2845
3238
|
evictCacheIfNeeded();
|
|
2846
3239
|
}
|
|
2847
3240
|
function extractRemotePort(request) {
|
|
2848
|
-
const
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
return null;
|
|
3241
|
+
const descriptor = Object.getOwnPropertyDescriptor(request, "socket");
|
|
3242
|
+
if (descriptor === void 0 || descriptor.get !== void 0) return null;
|
|
3243
|
+
const parsed = SocketSchema.safeParse(descriptor.value);
|
|
3244
|
+
if (!parsed.success) return null;
|
|
3245
|
+
const remotePort = parsed.data.remotePort;
|
|
3246
|
+
if (remotePort === void 0 || remotePort === null) return null;
|
|
3247
|
+
return remotePort;
|
|
2852
3248
|
}
|
|
2853
3249
|
async function lookupClientInfo(port) {
|
|
2854
3250
|
const platform = process.platform;
|
|
@@ -3123,6 +3519,14 @@ function toPersistedConfig(value) {
|
|
|
3123
3519
|
timeDisplayFormat: value.timeDisplayFormat
|
|
3124
3520
|
};
|
|
3125
3521
|
}
|
|
3522
|
+
function isPlainRecord(val) {
|
|
3523
|
+
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
3524
|
+
}
|
|
3525
|
+
function safeGetOwnProperty(obj, key) {
|
|
3526
|
+
if (obj === null || typeof obj !== "object" || Array.isArray(obj)) return void 0;
|
|
3527
|
+
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
3528
|
+
return desc?.value;
|
|
3529
|
+
}
|
|
3126
3530
|
const BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";
|
|
3127
3531
|
function isClaudeCodeBillingHeaderBlock(text) {
|
|
3128
3532
|
const trimmed = text.trimStart();
|
|
@@ -3288,14 +3692,14 @@ function persistStreamingChunks(log) {
|
|
|
3288
3692
|
log.streamingChunksPath = chunkPath;
|
|
3289
3693
|
}
|
|
3290
3694
|
}
|
|
3291
|
-
function finalizeWithError(job, log, fallbackStatus, fallbackResponseText, err) {
|
|
3695
|
+
function finalizeWithError(job, log, fallbackStatus, fallbackResponseText, err, cleanupRawStreamPath = null) {
|
|
3292
3696
|
const message = errorMessage$2(err);
|
|
3293
3697
|
logger.error(`[logFinalizer] Failed to finalize log #${log.id}:`, message);
|
|
3294
3698
|
log.responseStatus = log.responseStatus ?? fallbackStatus;
|
|
3295
3699
|
log.responseText = log.responseText ?? fallbackResponseText;
|
|
3296
3700
|
log.elapsedMs = job.elapsedMs;
|
|
3297
3701
|
log.error = message;
|
|
3298
|
-
return { log, upstreamUrl: job.upstreamUrl, error: message };
|
|
3702
|
+
return { log, upstreamUrl: job.upstreamUrl, error: message, cleanupRawStreamPath };
|
|
3299
3703
|
}
|
|
3300
3704
|
function finalizeNonStreaming(job, log) {
|
|
3301
3705
|
const formatHandler = formatForPath(log.path);
|
|
@@ -3316,36 +3720,90 @@ function finalizeNonStreaming(job, log) {
|
|
|
3316
3720
|
return finalizeWithError(job, log, job.responseStatus, job.responseBody, err);
|
|
3317
3721
|
}
|
|
3318
3722
|
}
|
|
3723
|
+
function cleanupPathForRawStream(rawStream) {
|
|
3724
|
+
if (typeof rawStream === "string") return null;
|
|
3725
|
+
switch (rawStream.type) {
|
|
3726
|
+
case "memory":
|
|
3727
|
+
return null;
|
|
3728
|
+
case "file":
|
|
3729
|
+
return rawStream.path;
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
function readRawStream(rawStream) {
|
|
3733
|
+
if (typeof rawStream === "string") {
|
|
3734
|
+
return { rawStreamText: rawStream, cleanupRawStreamPath: null };
|
|
3735
|
+
}
|
|
3736
|
+
switch (rawStream.type) {
|
|
3737
|
+
case "memory":
|
|
3738
|
+
return { rawStreamText: rawStream.rawStream, cleanupRawStreamPath: null };
|
|
3739
|
+
case "file":
|
|
3740
|
+
return {
|
|
3741
|
+
rawStreamText: readFileSync(rawStream.path, "utf-8"),
|
|
3742
|
+
cleanupRawStreamPath: rawStream.path
|
|
3743
|
+
};
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3319
3746
|
function finalizeStreaming(job, log) {
|
|
3320
3747
|
const formatHandler = formatForPath(log.path);
|
|
3321
3748
|
if (formatHandler === null) {
|
|
3322
|
-
return finalizeWithError(
|
|
3749
|
+
return finalizeWithError(
|
|
3750
|
+
job,
|
|
3751
|
+
log,
|
|
3752
|
+
job.responseStatus,
|
|
3753
|
+
"Streaming response unavailable",
|
|
3754
|
+
"Unsupported format",
|
|
3755
|
+
cleanupPathForRawStream(job.rawStream)
|
|
3756
|
+
);
|
|
3323
3757
|
}
|
|
3758
|
+
let rawStreamText = "";
|
|
3759
|
+
let cleanupRawStreamPath = null;
|
|
3324
3760
|
try {
|
|
3761
|
+
const raw = readRawStream(job.rawStream);
|
|
3762
|
+
rawStreamText = raw.rawStreamText;
|
|
3763
|
+
cleanupRawStreamPath = raw.cleanupRawStreamPath;
|
|
3325
3764
|
log.elapsedMs = job.elapsedMs;
|
|
3326
3765
|
log.responseStatus = job.responseStatus;
|
|
3327
3766
|
log.responseText = formatHandler.extractStream(
|
|
3328
|
-
|
|
3767
|
+
rawStreamText,
|
|
3329
3768
|
log,
|
|
3330
3769
|
log.model ?? void 0,
|
|
3331
3770
|
job.collectStreamingChunks
|
|
3332
3771
|
);
|
|
3333
3772
|
persistStreamingChunks(log);
|
|
3334
|
-
return { log, upstreamUrl: job.upstreamUrl, error: null };
|
|
3773
|
+
return { log, upstreamUrl: job.upstreamUrl, error: null, cleanupRawStreamPath };
|
|
3335
3774
|
} catch (err) {
|
|
3336
|
-
return finalizeWithError(
|
|
3775
|
+
return finalizeWithError(
|
|
3776
|
+
job,
|
|
3777
|
+
log,
|
|
3778
|
+
job.responseStatus,
|
|
3779
|
+
rawStreamText === "" ? "Streaming response unavailable" : rawStreamText,
|
|
3780
|
+
err,
|
|
3781
|
+
cleanupRawStreamPath ?? cleanupPathForRawStream(job.rawStream)
|
|
3782
|
+
);
|
|
3337
3783
|
}
|
|
3338
3784
|
}
|
|
3339
3785
|
function finalizeStreamAbort(job, log) {
|
|
3340
3786
|
const formatHandler = formatForPath(log.path);
|
|
3341
3787
|
if (formatHandler === null && job.hasChunks) {
|
|
3342
|
-
return finalizeWithError(
|
|
3788
|
+
return finalizeWithError(
|
|
3789
|
+
job,
|
|
3790
|
+
log,
|
|
3791
|
+
499,
|
|
3792
|
+
"Client aborted",
|
|
3793
|
+
"Unsupported format",
|
|
3794
|
+
cleanupPathForRawStream(job.rawStream)
|
|
3795
|
+
);
|
|
3343
3796
|
}
|
|
3797
|
+
let rawStreamText = "";
|
|
3798
|
+
let cleanupRawStreamPath = null;
|
|
3344
3799
|
try {
|
|
3345
3800
|
log.elapsedMs = job.elapsedMs;
|
|
3346
3801
|
if (job.hasChunks && formatHandler !== null) {
|
|
3802
|
+
const raw = readRawStream(job.rawStream);
|
|
3803
|
+
rawStreamText = raw.rawStreamText;
|
|
3804
|
+
cleanupRawStreamPath = raw.cleanupRawStreamPath;
|
|
3347
3805
|
log.responseText = formatHandler.extractStream(
|
|
3348
|
-
|
|
3806
|
+
rawStreamText,
|
|
3349
3807
|
log,
|
|
3350
3808
|
log.model ?? void 0,
|
|
3351
3809
|
job.collectStreamingChunks
|
|
@@ -3354,9 +3812,16 @@ function finalizeStreamAbort(job, log) {
|
|
|
3354
3812
|
} else {
|
|
3355
3813
|
log.responseText = "Client aborted";
|
|
3356
3814
|
}
|
|
3357
|
-
return { log, upstreamUrl: job.upstreamUrl, error: "Client aborted" };
|
|
3815
|
+
return { log, upstreamUrl: job.upstreamUrl, error: "Client aborted", cleanupRawStreamPath };
|
|
3358
3816
|
} catch (err) {
|
|
3359
|
-
return finalizeWithError(
|
|
3817
|
+
return finalizeWithError(
|
|
3818
|
+
job,
|
|
3819
|
+
log,
|
|
3820
|
+
499,
|
|
3821
|
+
rawStreamText === "" ? "Client aborted" : rawStreamText,
|
|
3822
|
+
err,
|
|
3823
|
+
cleanupRawStreamPath ?? cleanupPathForRawStream(job.rawStream)
|
|
3824
|
+
);
|
|
3360
3825
|
}
|
|
3361
3826
|
}
|
|
3362
3827
|
function buildFinalizeLogResult(job) {
|
|
@@ -3373,6 +3838,17 @@ function buildFinalizeLogResult(job) {
|
|
|
3373
3838
|
function commitFinalizeLogResult(result) {
|
|
3374
3839
|
appendLogEntry({ ...buildFileLogEntry(result.log, result.upstreamUrl), error: result.error });
|
|
3375
3840
|
finalizeLogUpdate(result.log);
|
|
3841
|
+
if (result.cleanupRawStreamPath !== void 0 && result.cleanupRawStreamPath !== null) {
|
|
3842
|
+
try {
|
|
3843
|
+
rmSync(result.cleanupRawStreamPath, { force: true });
|
|
3844
|
+
} catch (err) {
|
|
3845
|
+
logger.warn(
|
|
3846
|
+
"[logFinalizer] Failed to remove raw stream temp file:",
|
|
3847
|
+
result.cleanupRawStreamPath,
|
|
3848
|
+
errorMessage$2(err)
|
|
3849
|
+
);
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3376
3852
|
}
|
|
3377
3853
|
const RUNTIME = (() => {
|
|
3378
3854
|
if (process.env["FINALIZER_USE_WORKER"] === "0") return "inline";
|
|
@@ -3495,6 +3971,63 @@ function enqueueFinalizeLogJob(job) {
|
|
|
3495
3971
|
() => executeFinalizeLogJob(job)
|
|
3496
3972
|
);
|
|
3497
3973
|
}
|
|
3974
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
|
3975
|
+
function resolveMemoryLimitBytes() {
|
|
3976
|
+
const raw = process.env["AGENT_INSPECTOR_STREAM_MEMORY_LIMIT_BYTES"];
|
|
3977
|
+
if (raw === void 0 || raw === "") return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
3978
|
+
const parsed = Number(raw);
|
|
3979
|
+
if (!Number.isInteger(parsed) || parsed < 0) return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
3980
|
+
return parsed;
|
|
3981
|
+
}
|
|
3982
|
+
function getRawStreamDir() {
|
|
3983
|
+
return join(getDataDir(), "raw-streams");
|
|
3984
|
+
}
|
|
3985
|
+
function createRawStreamPath() {
|
|
3986
|
+
return join(getRawStreamDir(), `${Date.now()}-${randomUUID$1()}.stream.tmp`);
|
|
3987
|
+
}
|
|
3988
|
+
class RawStreamCapture {
|
|
3989
|
+
memoryLimitBytes;
|
|
3990
|
+
chunks = [];
|
|
3991
|
+
byteLength = 0;
|
|
3992
|
+
filePath = null;
|
|
3993
|
+
sawChunk = false;
|
|
3994
|
+
constructor(memoryLimitBytes = resolveMemoryLimitBytes()) {
|
|
3995
|
+
this.memoryLimitBytes = memoryLimitBytes;
|
|
3996
|
+
}
|
|
3997
|
+
get hasChunks() {
|
|
3998
|
+
return this.sawChunk;
|
|
3999
|
+
}
|
|
4000
|
+
async append(text) {
|
|
4001
|
+
if (text === "") return;
|
|
4002
|
+
this.sawChunk = true;
|
|
4003
|
+
const nextBytes = Buffer.byteLength(text, "utf-8");
|
|
4004
|
+
if (this.filePath === null && this.byteLength + nextBytes <= this.memoryLimitBytes) {
|
|
4005
|
+
this.chunks.push(text);
|
|
4006
|
+
this.byteLength += nextBytes;
|
|
4007
|
+
return;
|
|
4008
|
+
}
|
|
4009
|
+
if (this.filePath === null) {
|
|
4010
|
+
await this.spillToFile();
|
|
4011
|
+
}
|
|
4012
|
+
if (this.filePath !== null) {
|
|
4013
|
+
await appendFile(this.filePath, text, "utf-8");
|
|
4014
|
+
this.byteLength += nextBytes;
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
snapshot() {
|
|
4018
|
+
if (this.filePath !== null) {
|
|
4019
|
+
return { type: "file", path: this.filePath };
|
|
4020
|
+
}
|
|
4021
|
+
return { type: "memory", rawStream: this.chunks.join("") };
|
|
4022
|
+
}
|
|
4023
|
+
async spillToFile() {
|
|
4024
|
+
const path2 = createRawStreamPath();
|
|
4025
|
+
await mkdir(getRawStreamDir(), { recursive: true });
|
|
4026
|
+
await writeFile(path2, this.chunks.join(""), "utf-8");
|
|
4027
|
+
this.filePath = path2;
|
|
4028
|
+
this.chunks = [];
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
3498
4031
|
function describeApiRoute(apiPath) {
|
|
3499
4032
|
const endpointPath = apiPath.split("?")[0] ?? "";
|
|
3500
4033
|
const isChatCompletions = endpointPath === PATH_CHAT_COMPLETIONS || endpointPath === PATH_V1_CHAT_COMPLETIONS;
|
|
@@ -3603,25 +4136,47 @@ function handleNonStreamingResponse(upstreamRes, responseBody, startTime, upstre
|
|
|
3603
4136
|
function handleStreamingResponse(upstreamRes, req, startTime, upstreamUrl, log, collectStreamingChunks) {
|
|
3604
4137
|
log.streaming = true;
|
|
3605
4138
|
log.responseStatus = upstreamRes.status;
|
|
3606
|
-
const
|
|
4139
|
+
const capture = new RawStreamCapture();
|
|
3607
4140
|
const decoder = new TextDecoder();
|
|
4141
|
+
let finalizationScheduled = false;
|
|
4142
|
+
const scheduleStreamingFinalization = (type) => {
|
|
4143
|
+
if (finalizationScheduled) return;
|
|
4144
|
+
finalizationScheduled = true;
|
|
4145
|
+
const elapsedMs = Date.now() - startTime;
|
|
4146
|
+
const rawStream = capture.snapshot();
|
|
4147
|
+
switch (type) {
|
|
4148
|
+
case "streaming":
|
|
4149
|
+
scheduleLogFinalization({
|
|
4150
|
+
type: "streaming",
|
|
4151
|
+
log,
|
|
4152
|
+
upstreamUrl,
|
|
4153
|
+
elapsedMs,
|
|
4154
|
+
responseStatus: upstreamRes.status,
|
|
4155
|
+
rawStream,
|
|
4156
|
+
collectStreamingChunks
|
|
4157
|
+
});
|
|
4158
|
+
return;
|
|
4159
|
+
case "stream-abort":
|
|
4160
|
+
scheduleLogFinalization({
|
|
4161
|
+
type: "stream-abort",
|
|
4162
|
+
log,
|
|
4163
|
+
upstreamUrl,
|
|
4164
|
+
elapsedMs,
|
|
4165
|
+
rawStream,
|
|
4166
|
+
hasChunks: capture.hasChunks,
|
|
4167
|
+
collectStreamingChunks
|
|
4168
|
+
});
|
|
4169
|
+
return;
|
|
4170
|
+
}
|
|
4171
|
+
};
|
|
3608
4172
|
const transform = new TransformStream({
|
|
3609
|
-
transform(chunk, controller) {
|
|
4173
|
+
async transform(chunk, controller) {
|
|
3610
4174
|
controller.enqueue(chunk);
|
|
3611
|
-
|
|
4175
|
+
await capture.append(decoder.decode(chunk, { stream: true }));
|
|
3612
4176
|
},
|
|
3613
|
-
flush() {
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
scheduleLogFinalization({
|
|
3617
|
-
type: "streaming",
|
|
3618
|
-
log,
|
|
3619
|
-
upstreamUrl,
|
|
3620
|
-
elapsedMs,
|
|
3621
|
-
responseStatus: upstreamRes.status,
|
|
3622
|
-
rawStream: full,
|
|
3623
|
-
collectStreamingChunks
|
|
3624
|
-
});
|
|
4177
|
+
async flush() {
|
|
4178
|
+
await capture.append(decoder.decode());
|
|
4179
|
+
scheduleStreamingFinalization("streaming");
|
|
3625
4180
|
}
|
|
3626
4181
|
});
|
|
3627
4182
|
if (upstreamRes.body === null) {
|
|
@@ -3631,18 +4186,7 @@ function handleStreamingResponse(upstreamRes, req, startTime, upstreamUrl, log,
|
|
|
3631
4186
|
req.signal?.addEventListener("abort", () => {
|
|
3632
4187
|
if (log.responseText === null) {
|
|
3633
4188
|
logger.info(`[handler] Streaming client aborted: ${log.method} ${log.path}`);
|
|
3634
|
-
|
|
3635
|
-
const full = chunks.join("");
|
|
3636
|
-
const hasChunks = chunks.length > 0;
|
|
3637
|
-
scheduleLogFinalization({
|
|
3638
|
-
type: "stream-abort",
|
|
3639
|
-
log,
|
|
3640
|
-
upstreamUrl,
|
|
3641
|
-
elapsedMs,
|
|
3642
|
-
rawStream: full,
|
|
3643
|
-
hasChunks,
|
|
3644
|
-
collectStreamingChunks
|
|
3645
|
-
});
|
|
4189
|
+
scheduleStreamingFinalization("stream-abort");
|
|
3646
4190
|
}
|
|
3647
4191
|
});
|
|
3648
4192
|
const responseHeaders = new Headers(upstreamRes.headers);
|
|
@@ -3797,8 +4341,11 @@ const ProviderInputSchema = object({
|
|
|
3797
4341
|
const Route$n = createFileRoute("/api/providers")({
|
|
3798
4342
|
server: {
|
|
3799
4343
|
handlers: {
|
|
3800
|
-
GET: () => {
|
|
3801
|
-
|
|
4344
|
+
GET: ({ request }) => {
|
|
4345
|
+
const url = new URL(request.url);
|
|
4346
|
+
const includeSecrets = url.searchParams.get("includeSecrets") === "1";
|
|
4347
|
+
const providers = getProviders();
|
|
4348
|
+
return Response.json(includeSecrets ? providers : redactProviders(providers));
|
|
3802
4349
|
},
|
|
3803
4350
|
POST: async ({ request }) => {
|
|
3804
4351
|
const parsed = ProviderInputSchema.safeParse(await request.json());
|
|
@@ -4073,13 +4620,18 @@ async function listModelsImpl(callApi2) {
|
|
|
4073
4620
|
if (!res.ok) return toolError(`GET /api/models returned ${res.status}`);
|
|
4074
4621
|
return textJson(await res.json());
|
|
4075
4622
|
}
|
|
4076
|
-
|
|
4077
|
-
|
|
4623
|
+
function includeSecretsQuery(includeSecrets) {
|
|
4624
|
+
return includeSecrets === true ? "?includeSecrets=1" : "";
|
|
4625
|
+
}
|
|
4626
|
+
async function listProvidersImpl(callApi2, args = {}) {
|
|
4627
|
+
const res = await callApi2(`/api/providers${includeSecretsQuery(args.includeSecrets)}`);
|
|
4078
4628
|
if (!res.ok) return toolError(`GET /api/providers returned ${res.status}`);
|
|
4079
4629
|
return textJson(await res.json());
|
|
4080
4630
|
}
|
|
4081
|
-
async function getProviderImpl(callApi2, id) {
|
|
4082
|
-
const res = await callApi2(
|
|
4631
|
+
async function getProviderImpl(callApi2, id, args = {}) {
|
|
4632
|
+
const res = await callApi2(
|
|
4633
|
+
`/api/providers/${encodeURIComponent(id)}${includeSecretsQuery(args.includeSecrets)}`
|
|
4634
|
+
);
|
|
4083
4635
|
if (!res.ok) return toolError(`GET /api/providers/${id} returned ${res.status}`);
|
|
4084
4636
|
return textJson(await res.json());
|
|
4085
4637
|
}
|
|
@@ -4302,21 +4854,24 @@ function registerTools(server) {
|
|
|
4302
4854
|
"inspector_list_providers",
|
|
4303
4855
|
{
|
|
4304
4856
|
title: "List configured LLM providers",
|
|
4305
|
-
description: "Returns the
|
|
4306
|
-
inputSchema: object({
|
|
4857
|
+
description: "Returns the configured ProviderConfig array with apiKey redacted by default. Set includeSecrets=true only when the user explicitly needs plaintext local provider keys; this exposes secrets from <dataDir>/providers.json.",
|
|
4858
|
+
inputSchema: object({
|
|
4859
|
+
includeSecrets: boolean().optional().describe("Return plaintext provider API keys. Exposes local secrets when true.")
|
|
4860
|
+
})
|
|
4307
4861
|
},
|
|
4308
|
-
() => safeCall(() => listProvidersImpl(callApi))
|
|
4862
|
+
(args) => safeCall(() => listProvidersImpl(callApi, args))
|
|
4309
4863
|
);
|
|
4310
4864
|
server.registerTool(
|
|
4311
4865
|
"inspector_get_provider",
|
|
4312
4866
|
{
|
|
4313
4867
|
title: "Get a single provider by id",
|
|
4314
|
-
description: "Returns
|
|
4868
|
+
description: "Returns one ProviderConfig with apiKey redacted by default. Set includeSecrets=true only when the user explicitly needs the plaintext local provider key.",
|
|
4315
4869
|
inputSchema: object({
|
|
4316
|
-
id: string().describe("The provider id.")
|
|
4870
|
+
id: string().describe("The provider id."),
|
|
4871
|
+
includeSecrets: boolean().optional().describe("Return the plaintext provider API key. Exposes a local secret when true.")
|
|
4317
4872
|
})
|
|
4318
4873
|
},
|
|
4319
|
-
({ id }) => safeCall(() => getProviderImpl(callApi, id))
|
|
4874
|
+
({ id, includeSecrets }) => safeCall(() => getProviderImpl(callApi, id, { includeSecrets }))
|
|
4320
4875
|
);
|
|
4321
4876
|
server.registerTool(
|
|
4322
4877
|
"inspector_replay_log",
|
|
@@ -4452,23 +5007,40 @@ const Route$l = createFileRoute("/api/mcp")({
|
|
|
4452
5007
|
const DeleteBodySchema = object({
|
|
4453
5008
|
ids: array(number().int().positive()).optional()
|
|
4454
5009
|
}).optional();
|
|
5010
|
+
function parseNonNegativeInt(value, fallback) {
|
|
5011
|
+
if (value === null) return fallback;
|
|
5012
|
+
const parsed = Number(value);
|
|
5013
|
+
if (!Number.isInteger(parsed) || parsed < 0) return fallback;
|
|
5014
|
+
return parsed;
|
|
5015
|
+
}
|
|
5016
|
+
function parsePositiveInt(value, fallback) {
|
|
5017
|
+
if (value === null) return fallback;
|
|
5018
|
+
const parsed = Number(value);
|
|
5019
|
+
if (!Number.isInteger(parsed) || parsed < 1) return fallback;
|
|
5020
|
+
return parsed;
|
|
5021
|
+
}
|
|
4455
5022
|
const Route$k = createFileRoute("/api/logs")({
|
|
4456
5023
|
server: {
|
|
4457
5024
|
handlers: {
|
|
4458
|
-
GET: ({ request }) => {
|
|
5025
|
+
GET: async ({ request }) => {
|
|
4459
5026
|
const url = new URL(request.url);
|
|
5027
|
+
if (url.searchParams.get("stats") === "1") {
|
|
5028
|
+
return Response.json(getLogStorageStats());
|
|
5029
|
+
}
|
|
5030
|
+
const replayOf = url.searchParams.get("replayOf");
|
|
5031
|
+
if (replayOf !== null) {
|
|
5032
|
+
const id = Number(replayOf);
|
|
5033
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
5034
|
+
return Response.json({ error: "Invalid replayOf log ID" }, { status: 400 });
|
|
5035
|
+
}
|
|
5036
|
+
const logs = getReplayLogsForSource(id);
|
|
5037
|
+
return Response.json({ logs, total: logs.length, offset: 0, limit: logs.length });
|
|
5038
|
+
}
|
|
4460
5039
|
const sessionId = url.searchParams.get("sessionId") ?? void 0;
|
|
4461
5040
|
const model = url.searchParams.get("model") ?? void 0;
|
|
4462
|
-
const offset =
|
|
4463
|
-
const limit =
|
|
4464
|
-
|
|
4465
|
-
const paginatedLogs = allLogs.slice(offset, offset + limit);
|
|
4466
|
-
return Response.json({
|
|
4467
|
-
logs: paginatedLogs,
|
|
4468
|
-
total: allLogs.length,
|
|
4469
|
-
offset,
|
|
4470
|
-
limit
|
|
4471
|
-
});
|
|
5041
|
+
const offset = parseNonNegativeInt(url.searchParams.get("offset"), 0);
|
|
5042
|
+
const limit = parsePositiveInt(url.searchParams.get("limit"), 50);
|
|
5043
|
+
return Response.json(await listLogsPage({ sessionId, model, offset, limit }));
|
|
4472
5044
|
},
|
|
4473
5045
|
DELETE: async ({ request }) => {
|
|
4474
5046
|
let body = void 0;
|
|
@@ -4489,11 +5061,21 @@ const Route$k = createFileRoute("/api/logs")({
|
|
|
4489
5061
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
4490
5062
|
}
|
|
4491
5063
|
if (body?.ids !== void 0 && body.ids.length > 0) {
|
|
4492
|
-
const result2 =
|
|
4493
|
-
return Response.json({
|
|
5064
|
+
const result2 = await clearPersistedLogsByIds(body.ids);
|
|
5065
|
+
return Response.json({
|
|
5066
|
+
success: true,
|
|
5067
|
+
cleared: result2.cleared,
|
|
5068
|
+
logFilesRewritten: result2.logFilesRewritten,
|
|
5069
|
+
chunkFilesDeleted: result2.chunkFilesDeleted
|
|
5070
|
+
});
|
|
4494
5071
|
}
|
|
4495
|
-
const result =
|
|
4496
|
-
return Response.json({
|
|
5072
|
+
const result = await clearPersistedLogStorage();
|
|
5073
|
+
return Response.json({
|
|
5074
|
+
success: true,
|
|
5075
|
+
cleared: result.cleared,
|
|
5076
|
+
logFilesDeleted: result.logFilesDeleted,
|
|
5077
|
+
chunkFilesDeleted: result.chunkFilesDeleted
|
|
5078
|
+
});
|
|
4497
5079
|
}
|
|
4498
5080
|
}
|
|
4499
5081
|
}
|
|
@@ -5015,13 +5597,15 @@ const ProviderUpdateSchema = object({
|
|
|
5015
5597
|
const Route$e = createFileRoute("/api/providers/$providerId")({
|
|
5016
5598
|
server: {
|
|
5017
5599
|
handlers: {
|
|
5018
|
-
GET: ({ params }) => {
|
|
5600
|
+
GET: ({ params, request }) => {
|
|
5601
|
+
const url = new URL(request.url);
|
|
5602
|
+
const includeSecrets = url.searchParams.get("includeSecrets") === "1";
|
|
5019
5603
|
const providers = getProviders();
|
|
5020
5604
|
const provider = providers.find((p) => p.id === params.providerId);
|
|
5021
5605
|
if (!provider) {
|
|
5022
5606
|
return Response.json({ error: "Provider not found" }, { status: 404 });
|
|
5023
5607
|
}
|
|
5024
|
-
return Response.json(provider);
|
|
5608
|
+
return Response.json(includeSecrets ? provider : redactProvider(provider));
|
|
5025
5609
|
},
|
|
5026
5610
|
PUT: async ({ params, request }) => {
|
|
5027
5611
|
const parsed = ProviderUpdateSchema.safeParse(await request.json());
|
|
@@ -5044,6 +5628,7 @@ const Route$e = createFileRoute("/api/providers/$providerId")({
|
|
|
5044
5628
|
}
|
|
5045
5629
|
}
|
|
5046
5630
|
});
|
|
5631
|
+
const INITIAL_STREAM_LOG_LIMIT = 500;
|
|
5047
5632
|
const Route$d = createFileRoute("/api/logs/stream")({
|
|
5048
5633
|
server: {
|
|
5049
5634
|
handlers: {
|
|
@@ -5085,9 +5670,15 @@ const Route$d = createFileRoute("/api/logs/stream")({
|
|
|
5085
5670
|
}, 3e4);
|
|
5086
5671
|
request.signal.addEventListener("abort", cleanup, { once: true });
|
|
5087
5672
|
const stream = new ReadableStream({
|
|
5088
|
-
start(controller) {
|
|
5673
|
+
async start(controller) {
|
|
5089
5674
|
controllerRef = controller;
|
|
5090
|
-
const
|
|
5675
|
+
const result = await listLogsPage({
|
|
5676
|
+
sessionId,
|
|
5677
|
+
model,
|
|
5678
|
+
offset: 0,
|
|
5679
|
+
limit: INITIAL_STREAM_LOG_LIMIT
|
|
5680
|
+
});
|
|
5681
|
+
const logs = result.logs;
|
|
5091
5682
|
const initData = `data: ${JSON.stringify({ type: "init", logs })}
|
|
5092
5683
|
|
|
5093
5684
|
`;
|
|
@@ -6501,6 +7092,37 @@ const Route$5 = createFileRoute("/api/logs/$id/replay")({
|
|
|
6501
7092
|
const mockLog = { ...log };
|
|
6502
7093
|
const responseText = formatHandler.extractStream(fullResponse, mockLog, model, false);
|
|
6503
7094
|
const tokens = formatHandler.extractTokens(responseText);
|
|
7095
|
+
const savedReplayLog = await addTestLogEntry({
|
|
7096
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7097
|
+
method: log.method,
|
|
7098
|
+
path: log.path,
|
|
7099
|
+
model,
|
|
7100
|
+
sessionId: log.sessionId,
|
|
7101
|
+
rawRequestBody: modifiedBody,
|
|
7102
|
+
responseStatus: upstreamRes.status,
|
|
7103
|
+
responseText,
|
|
7104
|
+
inputTokens: tokens.inputTokens ?? null,
|
|
7105
|
+
outputTokens: tokens.outputTokens ?? null,
|
|
7106
|
+
cacheCreationInputTokens: tokens.cacheCreationInputTokens ?? null,
|
|
7107
|
+
cacheReadInputTokens: tokens.cacheReadInputTokens ?? null,
|
|
7108
|
+
elapsedMs,
|
|
7109
|
+
streaming: true,
|
|
7110
|
+
userAgent: log.userAgent,
|
|
7111
|
+
origin: log.origin,
|
|
7112
|
+
rawHeaders: log.rawHeaders,
|
|
7113
|
+
headers: log.headers,
|
|
7114
|
+
apiFormat: log.apiFormat,
|
|
7115
|
+
isTest: true,
|
|
7116
|
+
replayOfLogId: log.id,
|
|
7117
|
+
providerName: log.providerName,
|
|
7118
|
+
clientPort: log.clientPort,
|
|
7119
|
+
clientPid: log.clientPid,
|
|
7120
|
+
clientCwd: log.clientCwd,
|
|
7121
|
+
clientProjectFolder: log.clientProjectFolder,
|
|
7122
|
+
streamingChunks: void 0,
|
|
7123
|
+
streamingChunksPath: null,
|
|
7124
|
+
error: null
|
|
7125
|
+
});
|
|
6504
7126
|
return Response.json({
|
|
6505
7127
|
success: true,
|
|
6506
7128
|
responseStatus: upstreamRes.status,
|
|
@@ -6508,11 +7130,43 @@ const Route$5 = createFileRoute("/api/logs/$id/replay")({
|
|
|
6508
7130
|
inputTokens: tokens.inputTokens ?? void 0,
|
|
6509
7131
|
outputTokens: tokens.outputTokens ?? void 0,
|
|
6510
7132
|
elapsedMs,
|
|
6511
|
-
streaming: true
|
|
7133
|
+
streaming: true,
|
|
7134
|
+
replayLogId: savedReplayLog.id
|
|
6512
7135
|
});
|
|
6513
7136
|
} else {
|
|
6514
7137
|
const responseText = await upstreamRes.text();
|
|
6515
7138
|
const tokens = formatHandler.extractTokens(responseText);
|
|
7139
|
+
const savedReplayLog = await addTestLogEntry({
|
|
7140
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7141
|
+
method: log.method,
|
|
7142
|
+
path: log.path,
|
|
7143
|
+
model,
|
|
7144
|
+
sessionId: log.sessionId,
|
|
7145
|
+
rawRequestBody: modifiedBody,
|
|
7146
|
+
responseStatus: upstreamRes.status,
|
|
7147
|
+
responseText,
|
|
7148
|
+
inputTokens: tokens.inputTokens ?? null,
|
|
7149
|
+
outputTokens: tokens.outputTokens ?? null,
|
|
7150
|
+
cacheCreationInputTokens: tokens.cacheCreationInputTokens ?? null,
|
|
7151
|
+
cacheReadInputTokens: tokens.cacheReadInputTokens ?? null,
|
|
7152
|
+
elapsedMs,
|
|
7153
|
+
streaming: false,
|
|
7154
|
+
userAgent: log.userAgent,
|
|
7155
|
+
origin: log.origin,
|
|
7156
|
+
rawHeaders: log.rawHeaders,
|
|
7157
|
+
headers: log.headers,
|
|
7158
|
+
apiFormat: log.apiFormat,
|
|
7159
|
+
isTest: true,
|
|
7160
|
+
replayOfLogId: log.id,
|
|
7161
|
+
providerName: log.providerName,
|
|
7162
|
+
clientPort: log.clientPort,
|
|
7163
|
+
clientPid: log.clientPid,
|
|
7164
|
+
clientCwd: log.clientCwd,
|
|
7165
|
+
clientProjectFolder: log.clientProjectFolder,
|
|
7166
|
+
streamingChunks: void 0,
|
|
7167
|
+
streamingChunksPath: null,
|
|
7168
|
+
error: null
|
|
7169
|
+
});
|
|
6516
7170
|
return Response.json({
|
|
6517
7171
|
success: true,
|
|
6518
7172
|
responseStatus: upstreamRes.status,
|
|
@@ -6520,7 +7174,8 @@ const Route$5 = createFileRoute("/api/logs/$id/replay")({
|
|
|
6520
7174
|
inputTokens: tokens.inputTokens ?? void 0,
|
|
6521
7175
|
outputTokens: tokens.outputTokens ?? void 0,
|
|
6522
7176
|
elapsedMs,
|
|
6523
|
-
streaming: false
|
|
7177
|
+
streaming: false,
|
|
7178
|
+
replayLogId: savedReplayLog.id
|
|
6524
7179
|
});
|
|
6525
7180
|
}
|
|
6526
7181
|
}
|
|
@@ -7204,8 +7859,9 @@ export {
|
|
|
7204
7859
|
ProviderTestResultsSchema as j,
|
|
7205
7860
|
createFailedProviderTestResults as k,
|
|
7206
7861
|
resolveProviderContextWindow as l,
|
|
7207
|
-
|
|
7208
|
-
|
|
7862
|
+
maskApiKey as m,
|
|
7863
|
+
isPlainRecord as n,
|
|
7864
|
+
router as o,
|
|
7209
7865
|
parseOpenAIResponse as p,
|
|
7210
7866
|
requestFormatForPath as r,
|
|
7211
7867
|
safeGetOwnProperty as s
|