@tonyclaw/agent-inspector 3.0.26 → 3.0.28
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 +80 -11
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-SZRUDQc9.js → CompareDrawer-TghZr5t1.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-BJvwOrSi.js → ProxyViewerContainer-fShBAZjj.js} +201 -201
- package/.output/public/assets/{ReplayDialog-DgfmqiM3.js → ReplayDialog-CZMApaF4.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CS6c7Qfn.js → RequestAnatomy-DNWccR7O.js} +1 -1
- package/.output/public/assets/{ResponseView-C0Ri_5gm.js → ResponseView-d25MdFhY.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-7dYcHWyZ.js → StreamingChunkSequence-DIFZEnWe.js} +1 -1
- package/.output/public/assets/{_sessionId-D-V84cgw.js → _sessionId-DRfvuE2k.js} +1 -1
- package/.output/public/assets/{index-DgFWCxOs.js → index-B3aAuqWq.js} +1 -1
- package/.output/public/assets/index-BsxAGqMA.css +1 -0
- package/.output/public/assets/{index-DrVJsks2.js → index-D4HkS7Ct.js} +1 -1
- package/.output/public/assets/{index-CSIL7-MK.js → index-DzARL2Fi.js} +3 -3
- package/.output/public/assets/{json-viewer-C9Ji3gYN.js → json-viewer-wuJSKaed.js} +1 -1
- package/.output/server/_libs/lucide-react.mjs +39 -39
- package/.output/server/{_sessionId-BQ6YQOO1.mjs → _sessionId-BJTNiP_O.mjs} +4 -2
- package/.output/server/_ssr/{CompareDrawer-CqNzIeMR.mjs → CompareDrawer-BMRH3N97.mjs} +5 -3
- package/.output/server/_ssr/{ProxyViewerContainer-CMC7Vsbw.mjs → ProxyViewerContainer-AFwcHAIR.mjs} +330 -223
- package/.output/server/_ssr/{ReplayDialog-D9p_LaTr.mjs → ReplayDialog-CbWvRHiN.mjs} +6 -4
- package/.output/server/_ssr/{RequestAnatomy-B4ge3qNZ.mjs → RequestAnatomy-C4kcPyqN.mjs} +4 -2
- package/.output/server/_ssr/{ResponseView-BCqMKApI.mjs → ResponseView-D8VYt-gZ.mjs} +5 -3
- package/.output/server/_ssr/{StreamingChunkSequence-BA4zTLE4.mjs → StreamingChunkSequence-BUUo6vU0.mjs} +4 -2
- package/.output/server/_ssr/{index-CzxhDjlq.mjs → index-DTwMEEXO.mjs} +4 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-B3qV6iEJ.mjs → json-viewer-BqF1pSQD.mjs} +5 -3
- package/.output/server/_ssr/{router-CZe_R31M.mjs → router-C8BOns7y.mjs} +839 -368
- package/.output/server/{_tanstack-start-manifest_v-B50VTLrH.mjs → _tanstack-start-manifest_v-BBf2nRhb.mjs} +1 -1
- package/.output/server/index.mjs +70 -70
- package/README.md +8 -0
- package/package.json +4 -5
- package/src/cli.ts +43 -9
- package/src/components/ProxyViewer.tsx +57 -1
- package/src/components/clients/ClientLogo.tsx +43 -4
- package/src/components/inspector-pet/InspectorPet.tsx +0 -7
- package/src/components/providers/ProviderCard.tsx +3 -36
- package/src/components/providers/ProviderForm.tsx +13 -12
- package/src/components/providers/ProvidersPanel.tsx +2 -2
- package/src/components/providers/SettingsDialog.tsx +3 -3
- package/src/components/proxy-viewer/ConversationGroup.tsx +1 -1
- package/src/components/proxy-viewer/ConversationGroupList.tsx +233 -152
- package/src/components/proxy-viewer/ConversationHeader.tsx +29 -13
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -3
- package/src/contracts/anthropic.ts +0 -5
- package/src/contracts/index.ts +0 -4
- package/src/contracts/log.ts +0 -1
- package/src/contracts/openai.ts +0 -3
- package/src/knowledge/openclawClient.ts +22 -9
- package/src/knowledge/openclawGatewayClient.ts +20 -13
- package/src/lib/alertContract.ts +0 -3
- package/src/lib/ecosystemContract.ts +0 -3
- package/src/lib/export-logs.ts +1 -4
- package/src/lib/jsonFile.ts +46 -0
- package/src/lib/safeFetch.ts +100 -0
- package/src/lib/ssrfGuard.ts +133 -0
- package/src/lib/stopReason.ts +1 -4
- package/src/lib/unknown.ts +27 -0
- package/src/lib/useProviders.ts +1 -1
- package/src/mcp/mode.ts +3 -9
- package/src/mcp/server.ts +5 -14
- package/src/mcp/toolHandlers.ts +45 -54
- package/src/proxy/constants.ts +17 -0
- package/src/proxy/evidenceAnalysis.ts +116 -117
- package/src/proxy/formats/anthropic/handler.ts +5 -1
- package/src/proxy/formats/anthropic/schemas.ts +1 -7
- package/src/proxy/formats/index.ts +1 -1
- package/src/proxy/formats/openai/schemas.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -3
- package/src/proxy/groupEvidenceExporter.ts +4 -8
- package/src/proxy/groupStore.ts +3 -10
- package/src/proxy/handler.ts +91 -12
- package/src/proxy/identityProxy.ts +74 -1
- package/src/proxy/logImporter.ts +82 -12
- package/src/proxy/logIndex.ts +1 -4
- package/src/proxy/providerSecretStore.ts +177 -0
- package/src/proxy/providers.ts +84 -11
- package/src/proxy/runFailures.ts +4 -10
- package/src/proxy/runStore.ts +4 -11
- package/src/proxy/schemas.ts +1 -8
- package/src/proxy/sqliteLogIndex.ts +0 -16
- package/src/proxy/store.ts +1 -5
- package/src/proxy/toolSchemaWarnings.ts +1 -9
- package/src/routes/api/logs.$id.replay.ts +17 -5
- package/src/routes/api/providers.$providerId.model-metadata.ts +11 -1
- package/src/routes/api/providers.$providerId.ts +18 -5
- package/src/routes/api/providers.ts +18 -7
- package/.output/public/assets/index-CmeF5XXS.css +0 -1
|
@@ -1,17 +1,19 @@
|
|
|
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 { r as reactExports, 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, createReadStream, readdirSync, statSync, rmSync, cpSync, createWriteStream } from "node:fs";
|
|
4
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync, createReadStream, readdirSync, statSync, rmSync, cpSync, chmodSync, createWriteStream } from "node:fs";
|
|
5
5
|
import fs, { mkdir, appendFile, writeFile, readdir, open, readFile, stat, unlink, rm, rename } from "node:fs/promises";
|
|
6
6
|
import { createInterface } from "node:readline";
|
|
7
7
|
import { Buffer } from "node:buffer";
|
|
8
8
|
import path, { basename, join, dirname, isAbsolute, resolve, relative } from "node:path";
|
|
9
|
-
import { randomUUID, createHash } from "node:crypto";
|
|
9
|
+
import { randomUUID, timingSafeEqual, createHash, createDecipheriv, randomBytes, createCipheriv } from "node:crypto";
|
|
10
10
|
import { spawn, execFile } from "node:child_process";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
import { C as Conf } from "../_libs/conf.mjs";
|
|
13
13
|
import { randomUUID as randomUUID$1 } from "crypto";
|
|
14
14
|
import { promisify } from "node:util";
|
|
15
|
+
import { lookup } from "node:dns/promises";
|
|
16
|
+
import { isIP } from "node:net";
|
|
15
17
|
import { Worker } from "node:worker_threads";
|
|
16
18
|
import { M as McpServer, W as WebStandardStreamableHTTPServerTransport, R as ResourceTemplate } from "../_libs/modelcontextprotocol__server.mjs";
|
|
17
19
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
@@ -64,7 +66,7 @@ import "../_libs/immediate.mjs";
|
|
|
64
66
|
import "../_libs/setimmediate.mjs";
|
|
65
67
|
import "../_libs/pako.mjs";
|
|
66
68
|
const faviconSvg = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20role='img'%20aria-label='Agent%20Inspector'%3e%3cg%20fill='none'%20stroke='%23f59e0b'%20stroke-width='1.5'%20stroke-linecap='round'%20stroke-linejoin='round'%20%3e%3cpath%20d='M5%2013%20C5%209%208%207%2012%207%20C16%207%2019%209%2019%2013%20C19%2016%2016%2018%2012%2018%20C8%2018%205%2016%205%2013%20Z'%20/%3e%3cpath%20d='M5%2011%20C3.5%209.5%201.5%2010%202%2012.5%20C2.5%2014%204%2013.5%205%2012.5'%20/%3e%3cpath%20d='M19%2011%20C20.5%209.5%2022.5%2010%2022%2012.5%20C21.5%2014%2020%2013.5%2019%2012.5'%20/%3e%3cline%20x1='10'%20y1='7'%20x2='9.5'%20y2='5'%20/%3e%3cline%20x1='14'%20y1='7'%20x2='14.5'%20y2='5'%20/%3e%3cline%20x1='6.5'%20y1='16'%20x2='4.5'%20y2='19.5'%20/%3e%3cline%20x1='9'%20y1='17.5'%20x2='8'%20y2='20.5'%20/%3e%3cline%20x1='15'%20y1='17.5'%20x2='16'%20y2='20.5'%20/%3e%3cline%20x1='17.5'%20y1='16'%20x2='19.5'%20y2='19.5'%20/%3e%3c/g%3e%3ccircle%20cx='9.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='14.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3c/svg%3e";
|
|
67
|
-
const appCss = "/assets/index-
|
|
69
|
+
const appCss = "/assets/index-BsxAGqMA.css";
|
|
68
70
|
const CHUNK_LOAD_RELOAD_AT_KEY = "agent-inspector.chunkLoadReloadAt";
|
|
69
71
|
const CHUNK_LOAD_RELOAD_WINDOW_MS = 15e3;
|
|
70
72
|
const CHUNK_LOAD_FAILURE_PATTERNS = [
|
|
@@ -370,7 +372,7 @@ function RootDocument({ children }) {
|
|
|
370
372
|
] })
|
|
371
373
|
] });
|
|
372
374
|
}
|
|
373
|
-
const $$splitComponentImporter$1 = () => import("./index-
|
|
375
|
+
const $$splitComponentImporter$1 = () => import("./index-DTwMEEXO.mjs");
|
|
374
376
|
const Route$L = createFileRoute("/")({
|
|
375
377
|
ssr: false,
|
|
376
378
|
component: lazyRouteComponent($$splitComponentImporter$1, "component")
|
|
@@ -414,7 +416,7 @@ function decodeSessionIdFromPath(encoded) {
|
|
|
414
416
|
function getSessionPath(sessionId) {
|
|
415
417
|
return `/session/${encodeSessionIdForPath(sessionId)}`;
|
|
416
418
|
}
|
|
417
|
-
const $$splitComponentImporter = () => import("../_sessionId-
|
|
419
|
+
const $$splitComponentImporter = () => import("../_sessionId-BJTNiP_O.mjs");
|
|
418
420
|
const Route$K = createFileRoute("/session/$sessionId")({
|
|
419
421
|
ssr: false,
|
|
420
422
|
component: lazyRouteComponent($$splitComponentImporter, "component"),
|
|
@@ -1192,6 +1194,18 @@ function readChunks(path2) {
|
|
|
1192
1194
|
return null;
|
|
1193
1195
|
}
|
|
1194
1196
|
}
|
|
1197
|
+
function isRecord(value) {
|
|
1198
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1199
|
+
}
|
|
1200
|
+
function readProperty$5(value, name2) {
|
|
1201
|
+
if (!isRecord(value)) return void 0;
|
|
1202
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, name2);
|
|
1203
|
+
return descriptor === void 0 ? void 0 : descriptor.value;
|
|
1204
|
+
}
|
|
1205
|
+
function textByteLength(value) {
|
|
1206
|
+
if (value === null) return null;
|
|
1207
|
+
return new TextEncoder().encode(value).byteLength;
|
|
1208
|
+
}
|
|
1195
1209
|
const SQLITE_INDEX_FILE = "inspector.sqlite";
|
|
1196
1210
|
const SQLITE_PACKAGE_NAME$1 = "better-sqlite3";
|
|
1197
1211
|
const BUN_SQLITE_MODULE_NAME$1 = "bun:sqlite";
|
|
@@ -1328,7 +1342,7 @@ function objectFromUnknown$1(value) {
|
|
|
1328
1342
|
if (typeof value === "object" || typeof value === "function") return value;
|
|
1329
1343
|
return null;
|
|
1330
1344
|
}
|
|
1331
|
-
function readProperty$
|
|
1345
|
+
function readProperty$4(value, name2) {
|
|
1332
1346
|
let current = objectFromUnknown$1(value);
|
|
1333
1347
|
while (current !== null) {
|
|
1334
1348
|
const desc = Object.getOwnPropertyDescriptor(current, name2);
|
|
@@ -1342,7 +1356,7 @@ function readProperty$5(value, name2) {
|
|
|
1342
1356
|
return void 0;
|
|
1343
1357
|
}
|
|
1344
1358
|
function readFunction$1(value, name2) {
|
|
1345
|
-
const property = readProperty$
|
|
1359
|
+
const property = readProperty$4(value, name2);
|
|
1346
1360
|
return typeof property === "function" ? property : null;
|
|
1347
1361
|
}
|
|
1348
1362
|
function callMethod$1(target, methodName, args) {
|
|
@@ -1384,7 +1398,7 @@ function execSql$1(db, sql) {
|
|
|
1384
1398
|
return result !== void 0;
|
|
1385
1399
|
}
|
|
1386
1400
|
function sqliteConstructorFromModule$1(moduleValue, exportName) {
|
|
1387
|
-
const exportedValue = readProperty$
|
|
1401
|
+
const exportedValue = readProperty$4(moduleValue, exportName);
|
|
1388
1402
|
if (typeof exportedValue === "function") {
|
|
1389
1403
|
return (path2) => Reflect.construct(exportedValue, [path2]);
|
|
1390
1404
|
}
|
|
@@ -1527,11 +1541,11 @@ function entryParams(entry) {
|
|
|
1527
1541
|
];
|
|
1528
1542
|
}
|
|
1529
1543
|
function readNumber$1(row, name2) {
|
|
1530
|
-
const value = readProperty$
|
|
1544
|
+
const value = readProperty$4(row, name2);
|
|
1531
1545
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1532
1546
|
}
|
|
1533
1547
|
function readString$2(row, name2) {
|
|
1534
|
-
const value = readProperty$
|
|
1548
|
+
const value = readProperty$4(row, name2);
|
|
1535
1549
|
return typeof value === "string" ? value : null;
|
|
1536
1550
|
}
|
|
1537
1551
|
function readBoolean(row, name2) {
|
|
@@ -1716,9 +1730,6 @@ function getIndexPath() {
|
|
|
1716
1730
|
}
|
|
1717
1731
|
let cachedIndex = null;
|
|
1718
1732
|
let sqliteBackfillScheduledMaxId = 0;
|
|
1719
|
-
function textByteLength$2(value) {
|
|
1720
|
-
return value === null ? null : Buffer.byteLength(value, "utf8");
|
|
1721
|
-
}
|
|
1722
1733
|
function createLogIndexEntryMetadata(log) {
|
|
1723
1734
|
return {
|
|
1724
1735
|
sessionId: log.sessionId,
|
|
@@ -1751,8 +1762,8 @@ function createLogIndexEntryMetadata(log) {
|
|
|
1751
1762
|
clientCwd: log.clientCwd ?? null,
|
|
1752
1763
|
clientProjectFolder: log.clientProjectFolder ?? null,
|
|
1753
1764
|
streamingChunksPath: log.streamingChunksPath ?? null,
|
|
1754
|
-
rawRequestBodyBytes: log.rawRequestBodyBytes ?? textByteLength
|
|
1755
|
-
responseTextBytes: log.responseTextBytes ?? textByteLength
|
|
1765
|
+
rawRequestBodyBytes: log.rawRequestBodyBytes ?? textByteLength(log.rawRequestBody),
|
|
1766
|
+
responseTextBytes: log.responseTextBytes ?? textByteLength(log.responseText),
|
|
1756
1767
|
warnings: log.warnings,
|
|
1757
1768
|
error: log.error ?? null
|
|
1758
1769
|
}
|
|
@@ -1932,7 +1943,7 @@ function readStringArrayField(entry, field) {
|
|
|
1932
1943
|
function readTextByteLength(entry, bytesField, textField) {
|
|
1933
1944
|
const explicitBytes = readNumberOrNullField(entry, bytesField);
|
|
1934
1945
|
if (explicitBytes !== null && explicitBytes >= 0) return explicitBytes;
|
|
1935
|
-
return textByteLength
|
|
1946
|
+
return textByteLength(readStringOrNullField(entry, textField));
|
|
1936
1947
|
}
|
|
1937
1948
|
function readLogIndexSummary(entry, id) {
|
|
1938
1949
|
return {
|
|
@@ -2284,9 +2295,18 @@ const HEADER_CONTENT_ENCODING = "content-encoding";
|
|
|
2284
2295
|
const HEADER_CONTENT_LENGTH = "content-length";
|
|
2285
2296
|
const HEADER_HOST = "host";
|
|
2286
2297
|
const AUTH_HEADER_X_API_KEY = "x-api-key";
|
|
2298
|
+
const SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
2299
|
+
HEADER_AUTHORIZATION,
|
|
2300
|
+
HEADER_X_API_KEY,
|
|
2301
|
+
"cookie",
|
|
2302
|
+
"set-cookie",
|
|
2303
|
+
"proxy-authorization"
|
|
2304
|
+
]);
|
|
2287
2305
|
const CONTENT_TYPE_EVENT_STREAM = "text/event-stream";
|
|
2288
2306
|
const STATUS_FORBIDDEN = 403;
|
|
2289
2307
|
const STATUS_BAD_GATEWAY = 502;
|
|
2308
|
+
const STATUS_PAYLOAD_TOO_LARGE = 413;
|
|
2309
|
+
const MAX_REQUEST_BODY_BYTES = 50 * 1024 * 1024;
|
|
2290
2310
|
const PRESERVE_HEADERS = /* @__PURE__ */ new Set([HEADER_CONTENT_TYPE]);
|
|
2291
2311
|
class FormatRegistryImpl {
|
|
2292
2312
|
handlers = /* @__PURE__ */ new Map();
|
|
@@ -2383,7 +2403,7 @@ function objectFromUnknown(value) {
|
|
|
2383
2403
|
if (typeof value === "object" || typeof value === "function") return value;
|
|
2384
2404
|
return null;
|
|
2385
2405
|
}
|
|
2386
|
-
function readProperty$
|
|
2406
|
+
function readProperty$3(value, name2) {
|
|
2387
2407
|
let current = objectFromUnknown(value);
|
|
2388
2408
|
while (current !== null) {
|
|
2389
2409
|
const desc = Object.getOwnPropertyDescriptor(current, name2);
|
|
@@ -2397,7 +2417,7 @@ function readProperty$4(value, name2) {
|
|
|
2397
2417
|
return void 0;
|
|
2398
2418
|
}
|
|
2399
2419
|
function readFunction(value, name2) {
|
|
2400
|
-
const property = readProperty$
|
|
2420
|
+
const property = readProperty$3(value, name2);
|
|
2401
2421
|
return typeof property === "function" ? property : null;
|
|
2402
2422
|
}
|
|
2403
2423
|
function callMethod(target, methodName, args) {
|
|
@@ -2435,7 +2455,7 @@ function closeDatabase(db) {
|
|
|
2435
2455
|
callMethod(db, "close", []);
|
|
2436
2456
|
}
|
|
2437
2457
|
function sqliteConstructorFromModule(moduleValue, exportName) {
|
|
2438
|
-
const exportedValue = readProperty$
|
|
2458
|
+
const exportedValue = readProperty$3(moduleValue, exportName);
|
|
2439
2459
|
if (typeof exportedValue === "function") {
|
|
2440
2460
|
return (path2) => Reflect.construct(exportedValue, [path2]);
|
|
2441
2461
|
}
|
|
@@ -2526,11 +2546,11 @@ async function archiveSessionLog(log) {
|
|
|
2526
2546
|
}
|
|
2527
2547
|
}
|
|
2528
2548
|
function readString$1(row, name2) {
|
|
2529
|
-
const value = readProperty$
|
|
2549
|
+
const value = readProperty$3(row, name2);
|
|
2530
2550
|
return typeof value === "string" ? value : null;
|
|
2531
2551
|
}
|
|
2532
2552
|
function readNumber(row, name2) {
|
|
2533
|
-
const value = readProperty$
|
|
2553
|
+
const value = readProperty$3(row, name2);
|
|
2534
2554
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
2535
2555
|
}
|
|
2536
2556
|
function logFromJson(value) {
|
|
@@ -3110,10 +3130,6 @@ function removeFromCache(id) {
|
|
|
3110
3130
|
memoryCache.delete(id);
|
|
3111
3131
|
removeLogsFromCursorPageIndexes(/* @__PURE__ */ new Set([id]));
|
|
3112
3132
|
}
|
|
3113
|
-
function textByteLength$1(value) {
|
|
3114
|
-
if (value === null) return null;
|
|
3115
|
-
return Buffer.byteLength(value, "utf8");
|
|
3116
|
-
}
|
|
3117
3133
|
function compactLogForList(log) {
|
|
3118
3134
|
return {
|
|
3119
3135
|
...log,
|
|
@@ -3122,8 +3138,8 @@ function compactLogForList(log) {
|
|
|
3122
3138
|
rawHeaders: void 0,
|
|
3123
3139
|
headers: void 0,
|
|
3124
3140
|
streamingChunks: void 0,
|
|
3125
|
-
rawRequestBodyBytes: log.rawRequestBodyBytes ?? textByteLength
|
|
3126
|
-
responseTextBytes: log.responseTextBytes ?? textByteLength
|
|
3141
|
+
rawRequestBodyBytes: log.rawRequestBodyBytes ?? textByteLength(log.rawRequestBody),
|
|
3142
|
+
responseTextBytes: log.responseTextBytes ?? textByteLength(log.responseText),
|
|
3127
3143
|
bodyContentMode: "compact"
|
|
3128
3144
|
};
|
|
3129
3145
|
}
|
|
@@ -4275,7 +4291,7 @@ const AnthropicFormatHandler = {
|
|
|
4275
4291
|
if (result.success) {
|
|
4276
4292
|
return {
|
|
4277
4293
|
model: result.data.model,
|
|
4278
|
-
sessionId: result.data.metadata?.user_id ?? headers?.get("x-session-affinity") ?? null
|
|
4294
|
+
sessionId: result.data.metadata?.user_id ?? headers?.get("x-agent-inspector-session-id") ?? headers?.get("x-session-affinity") ?? null
|
|
4279
4295
|
};
|
|
4280
4296
|
}
|
|
4281
4297
|
return null;
|
|
@@ -4390,24 +4406,24 @@ const anthropicProvider = {
|
|
|
4390
4406
|
};
|
|
4391
4407
|
registry.register(anthropicProvider);
|
|
4392
4408
|
const MAX_CHUNKS = 1e3;
|
|
4393
|
-
function readProperty$
|
|
4409
|
+
function readProperty$2(value, key) {
|
|
4394
4410
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
4395
4411
|
return Object.getOwnPropertyDescriptor(value, key)?.value;
|
|
4396
4412
|
}
|
|
4397
4413
|
function readStringProperty$2(value, key) {
|
|
4398
|
-
const property = readProperty$
|
|
4414
|
+
const property = readProperty$2(value, key);
|
|
4399
4415
|
return typeof property === "string" ? property : null;
|
|
4400
4416
|
}
|
|
4401
4417
|
function readNumberProperty(value, key) {
|
|
4402
|
-
const property = readProperty$
|
|
4418
|
+
const property = readProperty$2(value, key);
|
|
4403
4419
|
return typeof property === "number" && Number.isFinite(property) ? property : null;
|
|
4404
4420
|
}
|
|
4405
4421
|
function readObjectProperty(value, key) {
|
|
4406
|
-
const property = readProperty$
|
|
4422
|
+
const property = readProperty$2(value, key);
|
|
4407
4423
|
return property !== null && typeof property === "object" && !Array.isArray(property) ? property : null;
|
|
4408
4424
|
}
|
|
4409
4425
|
function readArrayProperty$1(value, key) {
|
|
4410
|
-
const property = readProperty$
|
|
4426
|
+
const property = readProperty$2(value, key);
|
|
4411
4427
|
return Array.isArray(property) ? property : [];
|
|
4412
4428
|
}
|
|
4413
4429
|
function parseSseEvents(raw) {
|
|
@@ -4599,7 +4615,7 @@ function extractChatCompletionsStream(events, log, fallbackModel, collectChunks)
|
|
|
4599
4615
|
}
|
|
4600
4616
|
function parseResponsesResponse(value) {
|
|
4601
4617
|
const fromResponseProperty = OpenAIResponsesResponseSchema.safeParse(
|
|
4602
|
-
readProperty$
|
|
4618
|
+
readProperty$2(value, "response")
|
|
4603
4619
|
);
|
|
4604
4620
|
if (fromResponseProperty.success) return fromResponseProperty.data;
|
|
4605
4621
|
const direct = OpenAIResponsesResponseSchema.safeParse(value);
|
|
@@ -5416,6 +5432,162 @@ function withBuiltinProviderModelMetadata(provider, updatedAt) {
|
|
|
5416
5432
|
modelMetadata: result.modelMetadata
|
|
5417
5433
|
};
|
|
5418
5434
|
}
|
|
5435
|
+
function readJsonFile(path2, schema) {
|
|
5436
|
+
let raw;
|
|
5437
|
+
try {
|
|
5438
|
+
raw = readFileSync(path2, "utf8");
|
|
5439
|
+
} catch (error) {
|
|
5440
|
+
if (isEnoent(error)) return { ok: false, reason: "missing" };
|
|
5441
|
+
return { ok: false, reason: "io-error" };
|
|
5442
|
+
}
|
|
5443
|
+
let parsed;
|
|
5444
|
+
try {
|
|
5445
|
+
parsed = JSON.parse(raw);
|
|
5446
|
+
} catch {
|
|
5447
|
+
return { ok: false, reason: "parse-error" };
|
|
5448
|
+
}
|
|
5449
|
+
const result = schema.safeParse(parsed);
|
|
5450
|
+
if (!result.success) return { ok: false, reason: "schema-mismatch" };
|
|
5451
|
+
return { ok: true, data: result.data };
|
|
5452
|
+
}
|
|
5453
|
+
function isEnoent(error) {
|
|
5454
|
+
return readProperty$5(error, "code") === "ENOENT";
|
|
5455
|
+
}
|
|
5456
|
+
const SECRET_REFERENCE_PREFIX = "secret:provider:";
|
|
5457
|
+
const SECRET_FILE_VERSION = 1;
|
|
5458
|
+
const KEY_BYTES = 32;
|
|
5459
|
+
const IV_BYTES = 12;
|
|
5460
|
+
const AUTH_TAG_BYTES = 16;
|
|
5461
|
+
const SecretFileSchema = object({
|
|
5462
|
+
version: literal(SECRET_FILE_VERSION),
|
|
5463
|
+
secrets: record(string(), string())
|
|
5464
|
+
});
|
|
5465
|
+
function emptySecretFile() {
|
|
5466
|
+
return { version: SECRET_FILE_VERSION, secrets: {} };
|
|
5467
|
+
}
|
|
5468
|
+
function readSecretFile(path2) {
|
|
5469
|
+
if (!existsSync(path2)) return emptySecretFile();
|
|
5470
|
+
const result = readJsonFile(path2, SecretFileSchema);
|
|
5471
|
+
if (result.ok) return result.data;
|
|
5472
|
+
logger.warn(`[provider-secrets] Ignoring invalid secret file ${path2} (${result.reason})`);
|
|
5473
|
+
return emptySecretFile();
|
|
5474
|
+
}
|
|
5475
|
+
function writePrivateFile(path2, content) {
|
|
5476
|
+
const temporaryPath = `${path2}.${String(process.pid)}.tmp`;
|
|
5477
|
+
try {
|
|
5478
|
+
writeFileSync(temporaryPath, content, { encoding: "utf8", mode: 384 });
|
|
5479
|
+
chmodSync(temporaryPath, 384);
|
|
5480
|
+
renameSync(temporaryPath, path2);
|
|
5481
|
+
chmodSync(path2, 384);
|
|
5482
|
+
return true;
|
|
5483
|
+
} catch (error) {
|
|
5484
|
+
rmSync(temporaryPath, { force: true });
|
|
5485
|
+
logger.error(`[provider-secrets] Could not write ${path2}: ${String(error)}`);
|
|
5486
|
+
return false;
|
|
5487
|
+
}
|
|
5488
|
+
}
|
|
5489
|
+
function readKey(path2) {
|
|
5490
|
+
try {
|
|
5491
|
+
if (!existsSync(path2)) return null;
|
|
5492
|
+
const key = Buffer.from(readFileSync(path2, "utf8").trim(), "base64");
|
|
5493
|
+
if (key.byteLength === KEY_BYTES) return key;
|
|
5494
|
+
logger.error(`[provider-secrets] Invalid encryption key at ${path2}`);
|
|
5495
|
+
return null;
|
|
5496
|
+
} catch (error) {
|
|
5497
|
+
logger.error(`[provider-secrets] Could not read encryption key: ${String(error)}`);
|
|
5498
|
+
return null;
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
function readOrCreateKey(keyPath, secretsPath) {
|
|
5502
|
+
const existingKey = readKey(keyPath);
|
|
5503
|
+
if (existingKey !== null) return existingKey;
|
|
5504
|
+
if (existsSync(keyPath)) return null;
|
|
5505
|
+
if (existsSync(secretsPath)) {
|
|
5506
|
+
logger.error(
|
|
5507
|
+
`[provider-secrets] Refusing to replace missing key while encrypted secrets exist at ${secretsPath}`
|
|
5508
|
+
);
|
|
5509
|
+
return null;
|
|
5510
|
+
}
|
|
5511
|
+
const key = randomBytes(KEY_BYTES);
|
|
5512
|
+
if (!writePrivateFile(keyPath, key.toString("base64"))) return null;
|
|
5513
|
+
return key;
|
|
5514
|
+
}
|
|
5515
|
+
function encrypt(secret, key) {
|
|
5516
|
+
try {
|
|
5517
|
+
const iv = randomBytes(IV_BYTES);
|
|
5518
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
5519
|
+
const ciphertext = Buffer.concat([cipher.update(secret, "utf8"), cipher.final()]);
|
|
5520
|
+
const tag = cipher.getAuthTag();
|
|
5521
|
+
return Buffer.concat([iv, tag, ciphertext]).toString("base64");
|
|
5522
|
+
} catch (error) {
|
|
5523
|
+
logger.error(`[provider-secrets] Encryption failed: ${String(error)}`);
|
|
5524
|
+
return null;
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5527
|
+
function decrypt(payload, key) {
|
|
5528
|
+
try {
|
|
5529
|
+
const packed = Buffer.from(payload, "base64");
|
|
5530
|
+
if (packed.byteLength <= IV_BYTES + AUTH_TAG_BYTES) return null;
|
|
5531
|
+
const iv = packed.subarray(0, IV_BYTES);
|
|
5532
|
+
const tag = packed.subarray(IV_BYTES, IV_BYTES + AUTH_TAG_BYTES);
|
|
5533
|
+
const ciphertext = packed.subarray(IV_BYTES + AUTH_TAG_BYTES);
|
|
5534
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv);
|
|
5535
|
+
decipher.setAuthTag(tag);
|
|
5536
|
+
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
|
|
5537
|
+
} catch {
|
|
5538
|
+
return null;
|
|
5539
|
+
}
|
|
5540
|
+
}
|
|
5541
|
+
function providerIdFromReference(reference) {
|
|
5542
|
+
if (!reference.startsWith(SECRET_REFERENCE_PREFIX)) return null;
|
|
5543
|
+
const providerId = reference.slice(SECRET_REFERENCE_PREFIX.length);
|
|
5544
|
+
return providerId === "" ? null : providerId;
|
|
5545
|
+
}
|
|
5546
|
+
function createProviderSecretStore(directory) {
|
|
5547
|
+
mkdirSync(directory, { recursive: true });
|
|
5548
|
+
const keyPath = join(directory, "provider-secrets.key");
|
|
5549
|
+
const secretsPath = join(directory, "provider-secrets.json");
|
|
5550
|
+
function writeSecrets(data) {
|
|
5551
|
+
return writePrivateFile(secretsPath, JSON.stringify(data, null, 2));
|
|
5552
|
+
}
|
|
5553
|
+
return {
|
|
5554
|
+
isReference: (value) => providerIdFromReference(value) !== null,
|
|
5555
|
+
write: (providerId, secret) => {
|
|
5556
|
+
const key = readOrCreateKey(keyPath, secretsPath);
|
|
5557
|
+
if (key === null) return null;
|
|
5558
|
+
const encrypted = encrypt(secret, key);
|
|
5559
|
+
if (encrypted === null) return null;
|
|
5560
|
+
const data = readSecretFile(secretsPath);
|
|
5561
|
+
const next = {
|
|
5562
|
+
...data,
|
|
5563
|
+
secrets: { ...data.secrets, [providerId]: encrypted }
|
|
5564
|
+
};
|
|
5565
|
+
if (!writeSecrets(next)) return null;
|
|
5566
|
+
return `${SECRET_REFERENCE_PREFIX}${providerId}`;
|
|
5567
|
+
},
|
|
5568
|
+
read: (reference) => {
|
|
5569
|
+
const providerId = providerIdFromReference(reference);
|
|
5570
|
+
if (providerId === null) return null;
|
|
5571
|
+
const key = readKey(keyPath);
|
|
5572
|
+
if (key === null) return null;
|
|
5573
|
+
const encrypted = readSecretFile(secretsPath).secrets[providerId];
|
|
5574
|
+
if (encrypted === void 0) return null;
|
|
5575
|
+
return decrypt(encrypted, key);
|
|
5576
|
+
},
|
|
5577
|
+
delete: (providerId) => {
|
|
5578
|
+
const data = readSecretFile(secretsPath);
|
|
5579
|
+
if (data.secrets[providerId] === void 0) return;
|
|
5580
|
+
const nextSecrets = { ...data.secrets };
|
|
5581
|
+
delete nextSecrets[providerId];
|
|
5582
|
+
void writeSecrets({ ...data, secrets: nextSecrets });
|
|
5583
|
+
}
|
|
5584
|
+
};
|
|
5585
|
+
}
|
|
5586
|
+
function secretValuesEqual(left, right) {
|
|
5587
|
+
const leftBuffer = Buffer.from(left);
|
|
5588
|
+
const rightBuffer = Buffer.from(right);
|
|
5589
|
+
return leftBuffer.byteLength === rightBuffer.byteLength && timingSafeEqual(leftBuffer, rightBuffer);
|
|
5590
|
+
}
|
|
5419
5591
|
object({
|
|
5420
5592
|
providers: array(ProviderConfigSchema)
|
|
5421
5593
|
});
|
|
@@ -5562,6 +5734,60 @@ const store = new Conf({
|
|
|
5562
5734
|
},
|
|
5563
5735
|
...configPath !== void 0 ? { path: configPath } : {}
|
|
5564
5736
|
});
|
|
5737
|
+
const providerSecretStore = createProviderSecretStore(dirname(store.path));
|
|
5738
|
+
function providerWithResolvedSecret(provider) {
|
|
5739
|
+
if (!providerSecretStore.isReference(provider.apiKey)) return provider;
|
|
5740
|
+
const apiKey = providerSecretStore.read(provider.apiKey);
|
|
5741
|
+
if (apiKey !== null) return { ...provider, apiKey };
|
|
5742
|
+
logger.error(`[providers] Secret is missing or unreadable for provider "${provider.name}"`);
|
|
5743
|
+
return null;
|
|
5744
|
+
}
|
|
5745
|
+
function persistProviders(providers, removedProviderIds = /* @__PURE__ */ new Set()) {
|
|
5746
|
+
const existingReferences = /* @__PURE__ */ new Map();
|
|
5747
|
+
const unresolvedRecords = /* @__PURE__ */ new Map();
|
|
5748
|
+
for (const candidate of store.get("providers", [])) {
|
|
5749
|
+
const parsed = ProviderConfigSchema.safeParse(candidate);
|
|
5750
|
+
if (parsed.success && providerSecretStore.isReference(parsed.data.apiKey)) {
|
|
5751
|
+
existingReferences.set(parsed.data.id, parsed.data.apiKey);
|
|
5752
|
+
if (providerSecretStore.read(parsed.data.apiKey) === null) {
|
|
5753
|
+
unresolvedRecords.set(parsed.data.id, parsed.data);
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
const persisted = providers.map((provider) => {
|
|
5758
|
+
const existingReference = existingReferences.get(provider.id);
|
|
5759
|
+
if (existingReference !== void 0) {
|
|
5760
|
+
const existingSecret = providerSecretStore.read(existingReference);
|
|
5761
|
+
if (existingSecret !== null && secretValuesEqual(existingSecret, provider.apiKey)) {
|
|
5762
|
+
return { ...provider, apiKey: existingReference };
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
const reference = providerSecretStore.write(provider.id, provider.apiKey);
|
|
5766
|
+
if (reference !== null) return { ...provider, apiKey: reference };
|
|
5767
|
+
logger.error(
|
|
5768
|
+
`[providers] SecretStore unavailable; provider "${provider.name}" remains in the legacy storage format`
|
|
5769
|
+
);
|
|
5770
|
+
return provider;
|
|
5771
|
+
});
|
|
5772
|
+
const persistedIds = new Set(persisted.map((provider) => provider.id));
|
|
5773
|
+
for (const [id, provider] of unresolvedRecords) {
|
|
5774
|
+
if (!persistedIds.has(id) && !removedProviderIds.has(id)) persisted.push(provider);
|
|
5775
|
+
}
|
|
5776
|
+
store.set("providers", persisted);
|
|
5777
|
+
}
|
|
5778
|
+
function migratePlaintextProviderSecrets() {
|
|
5779
|
+
const raw = store.get("providers", []);
|
|
5780
|
+
let changed = false;
|
|
5781
|
+
const migrated = raw.map((candidate) => {
|
|
5782
|
+
const parsed = ProviderConfigSchema.safeParse(candidate);
|
|
5783
|
+
if (!parsed.success || providerSecretStore.isReference(parsed.data.apiKey)) return candidate;
|
|
5784
|
+
const reference = providerSecretStore.write(parsed.data.id, parsed.data.apiKey);
|
|
5785
|
+
if (reference === null) return candidate;
|
|
5786
|
+
changed = true;
|
|
5787
|
+
return { ...parsed.data, apiKey: reference };
|
|
5788
|
+
});
|
|
5789
|
+
if (changed) store.set("providers", migrated);
|
|
5790
|
+
}
|
|
5565
5791
|
migrateFromDataDirConfig(store, dataDir);
|
|
5566
5792
|
migrateFromLegacyConfLocation(store);
|
|
5567
5793
|
function migrateProvider(p) {
|
|
@@ -5663,13 +5889,15 @@ if (providersJson !== void 0) {
|
|
|
5663
5889
|
} catch {
|
|
5664
5890
|
}
|
|
5665
5891
|
}
|
|
5892
|
+
migratePlaintextProviderSecrets();
|
|
5666
5893
|
function getProviders() {
|
|
5667
5894
|
const raw = store.get("providers", []);
|
|
5668
5895
|
const valid = [];
|
|
5669
5896
|
for (const candidate of raw) {
|
|
5670
5897
|
const entry = ProviderConfigSchema.safeParse(candidate);
|
|
5671
5898
|
if (entry.success) {
|
|
5672
|
-
|
|
5899
|
+
const resolved = providerWithResolvedSecret(entry.data);
|
|
5900
|
+
if (resolved !== null) valid.push(resolved);
|
|
5673
5901
|
continue;
|
|
5674
5902
|
}
|
|
5675
5903
|
let candidateName = "<unknown>";
|
|
@@ -5690,7 +5918,7 @@ function getProvider(id) {
|
|
|
5690
5918
|
function redactProvider(provider) {
|
|
5691
5919
|
return {
|
|
5692
5920
|
...provider,
|
|
5693
|
-
apiKey:
|
|
5921
|
+
apiKey: "configured"
|
|
5694
5922
|
};
|
|
5695
5923
|
}
|
|
5696
5924
|
function redactProviders(providers) {
|
|
@@ -5707,7 +5935,7 @@ function addProvider(name2, apiKey, format, baseUrl, models, authHeader, apiDocs
|
|
|
5707
5935
|
const resolvedOpenaiResponsesUrl = openaiResponsesBaseUrl ?? "";
|
|
5708
5936
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5709
5937
|
const existing = providers.find(
|
|
5710
|
-
(p) => p.apiKey
|
|
5938
|
+
(p) => secretValuesEqual(p.apiKey, normalizedKey) && (p.anthropicBaseUrl ?? "") === (resolvedAnthropicUrl ?? "") && (p.openaiBaseUrl ?? "") === (resolvedOpenaiUrl ?? "") && (p.openaiResponsesBaseUrl ?? "") === (resolvedOpenaiResponsesUrl ?? "")
|
|
5711
5939
|
);
|
|
5712
5940
|
if (existing) {
|
|
5713
5941
|
const existingIndex = providers.findIndex((p) => p.id === existing.id);
|
|
@@ -5724,7 +5952,7 @@ function addProvider(name2, apiKey, format, baseUrl, models, authHeader, apiDocs
|
|
|
5724
5952
|
if (existingIndex >= 0) {
|
|
5725
5953
|
providers[existingIndex] = enriched;
|
|
5726
5954
|
}
|
|
5727
|
-
|
|
5955
|
+
persistProviders(providers);
|
|
5728
5956
|
return enriched;
|
|
5729
5957
|
}
|
|
5730
5958
|
const newProvider = withBuiltinProviderModelMetadata(
|
|
@@ -5749,7 +5977,7 @@ function addProvider(name2, apiKey, format, baseUrl, models, authHeader, apiDocs
|
|
|
5749
5977
|
now
|
|
5750
5978
|
);
|
|
5751
5979
|
providers.push(newProvider);
|
|
5752
|
-
|
|
5980
|
+
persistProviders(providers);
|
|
5753
5981
|
return newProvider;
|
|
5754
5982
|
}
|
|
5755
5983
|
function updateProvider(id, updates) {
|
|
@@ -5781,14 +6009,15 @@ function updateProvider(id, updates) {
|
|
|
5781
6009
|
);
|
|
5782
6010
|
const index = providers.findIndex((p) => p.id === id);
|
|
5783
6011
|
providers[index] = updated;
|
|
5784
|
-
|
|
6012
|
+
persistProviders(providers);
|
|
5785
6013
|
return updated;
|
|
5786
6014
|
}
|
|
5787
6015
|
function deleteProvider(id) {
|
|
5788
6016
|
const providers = getProviders();
|
|
5789
6017
|
const filtered = providers.filter((p) => p.id !== id);
|
|
5790
6018
|
if (filtered.length === providers.length) return false;
|
|
5791
|
-
|
|
6019
|
+
persistProviders(filtered, /* @__PURE__ */ new Set([id]));
|
|
6020
|
+
providerSecretStore.delete(id);
|
|
5792
6021
|
return true;
|
|
5793
6022
|
}
|
|
5794
6023
|
function exportProviders() {
|
|
@@ -5855,7 +6084,7 @@ function importProviders(json) {
|
|
|
5855
6084
|
imported++;
|
|
5856
6085
|
}
|
|
5857
6086
|
if (newProviders.length > 0) {
|
|
5858
|
-
|
|
6087
|
+
persistProviders([...existingProviders, ...newProviders]);
|
|
5859
6088
|
}
|
|
5860
6089
|
} catch (err) {
|
|
5861
6090
|
return {
|
|
@@ -6486,6 +6715,145 @@ function toPersistedConfig(value) {
|
|
|
6486
6715
|
timeDisplayFormat: value.timeDisplayFormat
|
|
6487
6716
|
};
|
|
6488
6717
|
}
|
|
6718
|
+
const LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
6719
|
+
"localhost",
|
|
6720
|
+
"ip6-localhost",
|
|
6721
|
+
"ip6-loopback",
|
|
6722
|
+
"[::1]",
|
|
6723
|
+
"[::ffff:127.0.0.1]"
|
|
6724
|
+
]);
|
|
6725
|
+
function isLoopbackHost(hostname) {
|
|
6726
|
+
const lower2 = hostname.toLowerCase().replace(/^\[|]$/g, "");
|
|
6727
|
+
if (LOOPBACK_HOSTNAMES.has(lower2)) return true;
|
|
6728
|
+
if (lower2 === "::1") return true;
|
|
6729
|
+
if (lower2.startsWith("::ffff:127.")) return true;
|
|
6730
|
+
if (lower2.startsWith("::ffff:7f00:")) return true;
|
|
6731
|
+
return /^127\./.test(lower2);
|
|
6732
|
+
}
|
|
6733
|
+
function mappedIpv4Address(hostname) {
|
|
6734
|
+
const lower2 = hostname.toLowerCase();
|
|
6735
|
+
if (!lower2.startsWith("::ffff:")) return null;
|
|
6736
|
+
const suffix = lower2.slice("::ffff:".length);
|
|
6737
|
+
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(suffix)) return suffix;
|
|
6738
|
+
const match = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(suffix);
|
|
6739
|
+
if (match === null) return null;
|
|
6740
|
+
const highText = match[1];
|
|
6741
|
+
const lowText = match[2];
|
|
6742
|
+
if (highText === void 0 || lowText === void 0) return null;
|
|
6743
|
+
const high = Number.parseInt(highText, 16);
|
|
6744
|
+
const low = Number.parseInt(lowText, 16);
|
|
6745
|
+
return `${String(high >> 8)}.${String(high & 255)}.${String(low >> 8)}.${String(low & 255)}`;
|
|
6746
|
+
}
|
|
6747
|
+
function isInternalHost(hostname) {
|
|
6748
|
+
if (LOOPBACK_HOSTNAMES.has(hostname.toLowerCase())) return true;
|
|
6749
|
+
const mappedIpv4 = mappedIpv4Address(hostname);
|
|
6750
|
+
if (mappedIpv4 !== null) return isInternalHost(mappedIpv4);
|
|
6751
|
+
const ipv4Match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
|
|
6752
|
+
if (ipv4Match !== null) {
|
|
6753
|
+
const [, a, b] = ipv4Match;
|
|
6754
|
+
if (a === void 0 || b === void 0) return false;
|
|
6755
|
+
const first = Number.parseInt(a, 10);
|
|
6756
|
+
const second = Number.parseInt(b, 10);
|
|
6757
|
+
if (first === 127) return true;
|
|
6758
|
+
if (first === 10) return true;
|
|
6759
|
+
if (first === 172 && second >= 16 && second <= 31) return true;
|
|
6760
|
+
if (first === 192 && second === 168) return true;
|
|
6761
|
+
if (first === 169 && second === 254) return true;
|
|
6762
|
+
if (first === 0) return true;
|
|
6763
|
+
if (first === 100 && second >= 64 && second <= 127) return true;
|
|
6764
|
+
if (first >= 224) return true;
|
|
6765
|
+
if (first === 198 && (second === 18 || second === 19)) return true;
|
|
6766
|
+
return false;
|
|
6767
|
+
}
|
|
6768
|
+
const lower2 = hostname.toLowerCase();
|
|
6769
|
+
if (lower2 === "::") return true;
|
|
6770
|
+
if (lower2 === "::1") return true;
|
|
6771
|
+
if (/^fe[89ab]/.test(lower2)) return true;
|
|
6772
|
+
if (lower2.startsWith("fc") || lower2.startsWith("fd")) return true;
|
|
6773
|
+
if (lower2.startsWith("ff")) return true;
|
|
6774
|
+
return false;
|
|
6775
|
+
}
|
|
6776
|
+
function evaluateUpstreamUrl(rawUrl, options = {}) {
|
|
6777
|
+
if (options.bypass === true) return { allow: true };
|
|
6778
|
+
let parsed;
|
|
6779
|
+
try {
|
|
6780
|
+
parsed = new URL(rawUrl);
|
|
6781
|
+
} catch {
|
|
6782
|
+
return { allow: false, reason: "Not a valid URL" };
|
|
6783
|
+
}
|
|
6784
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
6785
|
+
return { allow: false, reason: `Unsupported scheme "${parsed.protocol}"` };
|
|
6786
|
+
}
|
|
6787
|
+
if (options.allowLoopback === true && parsed.hostname === "localhost") {
|
|
6788
|
+
return { allow: true };
|
|
6789
|
+
}
|
|
6790
|
+
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
6791
|
+
if (isInternalHost(hostname)) {
|
|
6792
|
+
if (options.allowLoopback === true) {
|
|
6793
|
+
if (isLoopbackHost(hostname)) return { allow: true };
|
|
6794
|
+
}
|
|
6795
|
+
return {
|
|
6796
|
+
allow: false,
|
|
6797
|
+
reason: `Refusing to proxy to internal address "${hostname}"`
|
|
6798
|
+
};
|
|
6799
|
+
}
|
|
6800
|
+
return { allow: true };
|
|
6801
|
+
}
|
|
6802
|
+
async function defaultResolver(hostname) {
|
|
6803
|
+
return lookup(hostname, { all: true, verbatim: true });
|
|
6804
|
+
}
|
|
6805
|
+
function isLoopbackAddress(hostname) {
|
|
6806
|
+
const lower2 = hostname.toLowerCase();
|
|
6807
|
+
return lower2 === "::1" || lower2.startsWith("::ffff:127.") || lower2.startsWith("::ffff:7f00:") || /^127\./.test(lower2);
|
|
6808
|
+
}
|
|
6809
|
+
async function evaluateResolvedUpstreamUrl(rawUrl, options = {}) {
|
|
6810
|
+
const initial = evaluateUpstreamUrl(rawUrl, options);
|
|
6811
|
+
if (!initial.allow || options.bypass === true) return initial;
|
|
6812
|
+
const parsed = new URL(rawUrl);
|
|
6813
|
+
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
6814
|
+
if (isIP(hostname) !== 0 || hostname.toLowerCase() === "localhost") return initial;
|
|
6815
|
+
let addresses;
|
|
6816
|
+
try {
|
|
6817
|
+
addresses = await (options.resolver ?? defaultResolver)(hostname);
|
|
6818
|
+
} catch (error) {
|
|
6819
|
+
return {
|
|
6820
|
+
allow: false,
|
|
6821
|
+
reason: `Could not safely resolve upstream host "${hostname}": ${String(error)}`
|
|
6822
|
+
};
|
|
6823
|
+
}
|
|
6824
|
+
if (addresses.length === 0) {
|
|
6825
|
+
return { allow: false, reason: `Upstream host "${hostname}" resolved to no addresses` };
|
|
6826
|
+
}
|
|
6827
|
+
for (const resolved of addresses) {
|
|
6828
|
+
if (!isInternalHost(resolved.address)) continue;
|
|
6829
|
+
if (options.allowLoopback === true && isLoopbackAddress(resolved.address)) continue;
|
|
6830
|
+
return {
|
|
6831
|
+
allow: false,
|
|
6832
|
+
reason: `Refusing upstream host "${hostname}" because it resolves to internal address "${resolved.address}"`
|
|
6833
|
+
};
|
|
6834
|
+
}
|
|
6835
|
+
return { allow: true };
|
|
6836
|
+
}
|
|
6837
|
+
function isRedirectStatus(status) {
|
|
6838
|
+
return status >= 300 && status < 400;
|
|
6839
|
+
}
|
|
6840
|
+
async function safeFetch(rawUrl, init = {}, options = {}) {
|
|
6841
|
+
const decision = await evaluateResolvedUpstreamUrl(rawUrl, options);
|
|
6842
|
+
if (!decision.allow) {
|
|
6843
|
+
return { ok: false, kind: "network-policy", reason: decision.reason };
|
|
6844
|
+
}
|
|
6845
|
+
const response = await fetch(rawUrl, { ...init, redirect: "manual" });
|
|
6846
|
+
if (!isRedirectStatus(response.status)) return { ok: true, response };
|
|
6847
|
+
if (response.body !== null) {
|
|
6848
|
+
await response.body.cancel().catch(() => void 0);
|
|
6849
|
+
}
|
|
6850
|
+
const location = response.headers.get("location");
|
|
6851
|
+
return {
|
|
6852
|
+
ok: false,
|
|
6853
|
+
kind: "redirect",
|
|
6854
|
+
reason: location === null ? `Upstream returned blocked redirect HTTP ${String(response.status)}` : `Upstream redirect to "${location}" is blocked; configure the final API URL directly`
|
|
6855
|
+
};
|
|
6856
|
+
}
|
|
6489
6857
|
const BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";
|
|
6490
6858
|
function isClaudeCodeBillingHeaderBlock(text) {
|
|
6491
6859
|
const trimmed = text.trimStart();
|
|
@@ -6631,30 +6999,23 @@ function parseJson$1(raw) {
|
|
|
6631
6999
|
return { ok: false };
|
|
6632
7000
|
}
|
|
6633
7001
|
}
|
|
6634
|
-
function isRecord$2(value) {
|
|
6635
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6636
|
-
}
|
|
6637
|
-
function readProperty$2(value, key) {
|
|
6638
|
-
if (!isRecord$2(value)) return void 0;
|
|
6639
|
-
return Object.getOwnPropertyDescriptor(value, key)?.value;
|
|
6640
|
-
}
|
|
6641
7002
|
function hasOwnProperty(value, key) {
|
|
6642
7003
|
return Object.getOwnPropertyDescriptor(value, key) !== void 0;
|
|
6643
7004
|
}
|
|
6644
7005
|
function readString(value, key) {
|
|
6645
|
-
const property = readProperty$
|
|
7006
|
+
const property = readProperty$5(value, key);
|
|
6646
7007
|
return typeof property === "string" ? property : null;
|
|
6647
7008
|
}
|
|
6648
7009
|
function readArray(value, key) {
|
|
6649
|
-
const property = readProperty$
|
|
7010
|
+
const property = readProperty$5(value, key);
|
|
6650
7011
|
return Array.isArray(property) ? property : [];
|
|
6651
7012
|
}
|
|
6652
7013
|
function readStringArray(value, key) {
|
|
6653
7014
|
return readArray(value, key).filter((item) => typeof item === "string");
|
|
6654
7015
|
}
|
|
6655
7016
|
function readObject(value, key) {
|
|
6656
|
-
const property = readProperty$
|
|
6657
|
-
return isRecord
|
|
7017
|
+
const property = readProperty$5(value, key);
|
|
7018
|
+
return isRecord(property) ? property : null;
|
|
6658
7019
|
}
|
|
6659
7020
|
function normalizeExpectedType(typeName) {
|
|
6660
7021
|
switch (typeName) {
|
|
@@ -6671,7 +7032,7 @@ function normalizeExpectedType(typeName) {
|
|
|
6671
7032
|
}
|
|
6672
7033
|
}
|
|
6673
7034
|
function schemaTypes(schema) {
|
|
6674
|
-
const typeValue = readProperty$
|
|
7035
|
+
const typeValue = readProperty$5(schema, "type");
|
|
6675
7036
|
const rawTypes = Array.isArray(typeValue) ? typeValue : [typeValue];
|
|
6676
7037
|
const types = [];
|
|
6677
7038
|
for (const rawType of rawTypes) {
|
|
@@ -6699,7 +7060,7 @@ function actualType(value) {
|
|
|
6699
7060
|
case "function":
|
|
6700
7061
|
case "object":
|
|
6701
7062
|
case "symbol":
|
|
6702
|
-
return isRecord
|
|
7063
|
+
return isRecord(value) ? "object" : "unknown";
|
|
6703
7064
|
}
|
|
6704
7065
|
return "unknown";
|
|
6705
7066
|
}
|
|
@@ -6716,7 +7077,7 @@ function matchesExpectedType(value, expected) {
|
|
|
6716
7077
|
case "number":
|
|
6717
7078
|
return typeof value === "number" && Number.isFinite(value);
|
|
6718
7079
|
case "object":
|
|
6719
|
-
return isRecord
|
|
7080
|
+
return isRecord(value);
|
|
6720
7081
|
case "string":
|
|
6721
7082
|
return typeof value === "string";
|
|
6722
7083
|
}
|
|
@@ -6761,7 +7122,7 @@ function schemaAllowsObjectValidation(schema, expectedTypes) {
|
|
|
6761
7122
|
return expectedTypes.includes("object");
|
|
6762
7123
|
}
|
|
6763
7124
|
function schemaAllowsArrayValidation(schema, expectedTypes) {
|
|
6764
|
-
if (expectedTypes.length === 0) return readProperty$
|
|
7125
|
+
if (expectedTypes.length === 0) return readProperty$5(schema, "items") !== void 0;
|
|
6765
7126
|
return expectedTypes.includes("array");
|
|
6766
7127
|
}
|
|
6767
7128
|
function addTypeWarning(warnings, toolName, path2, expectedTypes, value) {
|
|
@@ -6794,7 +7155,7 @@ function validateObjectSchema(schema, value, toolName, path2, warnings) {
|
|
|
6794
7155
|
}
|
|
6795
7156
|
}
|
|
6796
7157
|
function validateArraySchema(schema, value, toolName, path2, warnings) {
|
|
6797
|
-
const itemsSchema = readProperty$
|
|
7158
|
+
const itemsSchema = readProperty$5(schema, "items");
|
|
6798
7159
|
if (itemsSchema === void 0) return;
|
|
6799
7160
|
let index = 0;
|
|
6800
7161
|
for (const item of value) {
|
|
@@ -6803,7 +7164,7 @@ function validateArraySchema(schema, value, toolName, path2, warnings) {
|
|
|
6803
7164
|
}
|
|
6804
7165
|
}
|
|
6805
7166
|
function validateEnum(schema, value, toolName, path2, warnings) {
|
|
6806
|
-
const enumValue = readProperty$
|
|
7167
|
+
const enumValue = readProperty$5(schema, "enum");
|
|
6807
7168
|
if (Array.isArray(enumValue) && enumValue.length > 0 && !enumMatches(value, enumValue)) {
|
|
6808
7169
|
warnings.push(
|
|
6809
7170
|
`Tool call "${toolName}" ${pathLabel(path2)} expected one of ${formatJsonValue(
|
|
@@ -6821,7 +7182,7 @@ function validateEnum(schema, value, toolName, path2, warnings) {
|
|
|
6821
7182
|
}
|
|
6822
7183
|
}
|
|
6823
7184
|
function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
6824
|
-
if (!isRecord
|
|
7185
|
+
if (!isRecord(schema)) return;
|
|
6825
7186
|
const expectedTypes = schemaTypes(schema);
|
|
6826
7187
|
if (expectedTypes.length > 0 && !matchesAnyExpectedType(value, expectedTypes)) {
|
|
6827
7188
|
addTypeWarning(warnings, toolName, path2, expectedTypes, value);
|
|
@@ -6829,7 +7190,7 @@ function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
|
6829
7190
|
}
|
|
6830
7191
|
validateEnum(schema, value, toolName, path2, warnings);
|
|
6831
7192
|
if (schemaAllowsObjectValidation(schema, expectedTypes)) {
|
|
6832
|
-
if (isRecord
|
|
7193
|
+
if (isRecord(value)) {
|
|
6833
7194
|
validateObjectSchema(schema, value, toolName, path2, warnings);
|
|
6834
7195
|
} else if (expectedTypes.length === 0) {
|
|
6835
7196
|
warnings.push(
|
|
@@ -6850,7 +7211,7 @@ function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
|
6850
7211
|
function extractAnthropicToolSchemas(request) {
|
|
6851
7212
|
const tools = /* @__PURE__ */ new Map();
|
|
6852
7213
|
for (const tool of readArray(request, "tools")) {
|
|
6853
|
-
if (!isRecord
|
|
7214
|
+
if (!isRecord(tool)) continue;
|
|
6854
7215
|
const name2 = readString(tool, "name");
|
|
6855
7216
|
if (name2 === null || name2 === "") continue;
|
|
6856
7217
|
tools.set(name2, {
|
|
@@ -6920,7 +7281,7 @@ function extractOpenAIToolCalls(response) {
|
|
|
6920
7281
|
for (const call of readArray(message, "tool_calls")) {
|
|
6921
7282
|
const functionCall = readObject(call, "function");
|
|
6922
7283
|
const name2 = functionCall === null ? null : readString(functionCall, "name");
|
|
6923
|
-
const parsedArguments = functionCall === null ? { value: {}, issue: "function block is missing" } : parseOpenAIArguments(readProperty$
|
|
7284
|
+
const parsedArguments = functionCall === null ? { value: {}, issue: "function block is missing" } : parseOpenAIArguments(readProperty$5(functionCall, "arguments"));
|
|
6924
7285
|
calls.push({
|
|
6925
7286
|
name: name2,
|
|
6926
7287
|
arguments: parsedArguments.value,
|
|
@@ -6931,7 +7292,7 @@ function extractOpenAIToolCalls(response) {
|
|
|
6931
7292
|
}
|
|
6932
7293
|
const legacyFunctionCall = readObject(message, "function_call");
|
|
6933
7294
|
if (legacyFunctionCall !== null) {
|
|
6934
|
-
const parsedArguments = parseOpenAIArguments(readProperty$
|
|
7295
|
+
const parsedArguments = parseOpenAIArguments(readProperty$5(legacyFunctionCall, "arguments"));
|
|
6935
7296
|
calls.push({
|
|
6936
7297
|
name: readString(legacyFunctionCall, "name"),
|
|
6937
7298
|
arguments: parsedArguments.value,
|
|
@@ -6947,7 +7308,7 @@ function extractOpenAIToolCalls(response) {
|
|
|
6947
7308
|
outputIndex++;
|
|
6948
7309
|
continue;
|
|
6949
7310
|
}
|
|
6950
|
-
const parsedArguments = parseOpenAIArguments(readProperty$
|
|
7311
|
+
const parsedArguments = parseOpenAIArguments(readProperty$5(item, "arguments"));
|
|
6951
7312
|
calls.push({
|
|
6952
7313
|
name: readString(item, "name"),
|
|
6953
7314
|
arguments: parsedArguments.value,
|
|
@@ -6968,7 +7329,7 @@ function extractAnthropicToolCalls(response) {
|
|
|
6968
7329
|
}
|
|
6969
7330
|
calls.push({
|
|
6970
7331
|
name: readString(block, "name"),
|
|
6971
|
-
arguments: readProperty$
|
|
7332
|
+
arguments: readProperty$5(block, "input") ?? {},
|
|
6972
7333
|
argumentIssue: null,
|
|
6973
7334
|
location: `content[${contentIndex}]`
|
|
6974
7335
|
});
|
|
@@ -7751,12 +8112,14 @@ function injectProviderAuth(headers, provider) {
|
|
|
7751
8112
|
}
|
|
7752
8113
|
headers.set(HEADER_AUTHORIZATION, `Bearer ${apiKey}`);
|
|
7753
8114
|
}
|
|
8115
|
+
const ALLOW_LOOPBACK$2 = process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1";
|
|
7754
8116
|
function buildProxyHeaders(originalHeaders, captureRawHeaders) {
|
|
7755
8117
|
const rawHeaders = captureRawHeaders ? {} : void 0;
|
|
7756
8118
|
const headers = new Headers();
|
|
7757
8119
|
if (rawHeaders !== void 0) {
|
|
7758
8120
|
originalHeaders.forEach((value, key) => {
|
|
7759
|
-
|
|
8121
|
+
const lowerKey = key.toLowerCase();
|
|
8122
|
+
rawHeaders[lowerKey] = SENSITIVE_HEADERS.has(lowerKey) ? maskApiKey(value) : value;
|
|
7760
8123
|
});
|
|
7761
8124
|
}
|
|
7762
8125
|
headers.set(HEADER_USER_AGENT, PROXY_IDENTITY);
|
|
@@ -7784,6 +8147,37 @@ function parseRequestPath(req, url) {
|
|
|
7784
8147
|
function errorMessage(err) {
|
|
7785
8148
|
return err instanceof Error ? err.message : String(err);
|
|
7786
8149
|
}
|
|
8150
|
+
async function readBodyWithLimit(req, limitBytes) {
|
|
8151
|
+
if (req.body === null || req.method === "GET" || req.method === "HEAD") {
|
|
8152
|
+
return { ok: true, body: null };
|
|
8153
|
+
}
|
|
8154
|
+
const contentLengthHeader = req.headers.get(HEADER_CONTENT_LENGTH);
|
|
8155
|
+
if (contentLengthHeader !== null) {
|
|
8156
|
+
const declared = Number.parseInt(contentLengthHeader, 10);
|
|
8157
|
+
if (Number.isFinite(declared) && declared > limitBytes) {
|
|
8158
|
+
return { ok: false, reason: "too-large" };
|
|
8159
|
+
}
|
|
8160
|
+
}
|
|
8161
|
+
const reader = req.body.getReader();
|
|
8162
|
+
const decoder = new TextDecoder();
|
|
8163
|
+
const chunks = [];
|
|
8164
|
+
let totalBytes = 0;
|
|
8165
|
+
try {
|
|
8166
|
+
for (; ; ) {
|
|
8167
|
+
const { done, value } = await reader.read();
|
|
8168
|
+
if (done) break;
|
|
8169
|
+
totalBytes += value.byteLength;
|
|
8170
|
+
if (totalBytes > limitBytes) {
|
|
8171
|
+
return { ok: false, reason: "too-large" };
|
|
8172
|
+
}
|
|
8173
|
+
chunks.push(decoder.decode(value, { stream: true }));
|
|
8174
|
+
}
|
|
8175
|
+
chunks.push(decoder.decode());
|
|
8176
|
+
return { ok: true, body: chunks.join("") };
|
|
8177
|
+
} finally {
|
|
8178
|
+
reader.releaseLock();
|
|
8179
|
+
}
|
|
8180
|
+
}
|
|
7787
8181
|
function scheduleLogFinalization(job) {
|
|
7788
8182
|
void enqueueFinalizeLogJob(job).catch((err) => {
|
|
7789
8183
|
logger.error(
|
|
@@ -7891,10 +8285,12 @@ function handleStreamingResponse(upstreamRes, req, startTime, upstreamUrl, log,
|
|
|
7891
8285
|
async function handleProxy(req) {
|
|
7892
8286
|
const url = new URL(req.url);
|
|
7893
8287
|
const parsed = parseRequestPath(req, url);
|
|
7894
|
-
|
|
7895
|
-
if (
|
|
7896
|
-
|
|
8288
|
+
const bodyRead = await readBodyWithLimit(req, MAX_REQUEST_BODY_BYTES);
|
|
8289
|
+
if (!bodyRead.ok) {
|
|
8290
|
+
logger.warn(`[handler] Rejected oversized request body: ${req.method} ${parsed.apiPath}`);
|
|
8291
|
+
return new Response("Request body too large", { status: STATUS_PAYLOAD_TOO_LARGE });
|
|
7897
8292
|
}
|
|
8293
|
+
const requestBody = bodyRead.body;
|
|
7898
8294
|
const config = getConfig();
|
|
7899
8295
|
let bodyToForward = requestBody;
|
|
7900
8296
|
if (config.stripClaudeCodeBillingHeader && requestBody !== null && parsed.isMessages) {
|
|
@@ -7941,7 +8337,8 @@ async function handleProxy(req) {
|
|
|
7941
8337
|
const upstreamHeadersObj = captureFullDetails ? {} : void 0;
|
|
7942
8338
|
if (upstreamHeadersObj !== void 0) {
|
|
7943
8339
|
upstreamHeaders.forEach((value, key) => {
|
|
7944
|
-
|
|
8340
|
+
const lowerKey = key.toLowerCase();
|
|
8341
|
+
upstreamHeadersObj[lowerKey] = SENSITIVE_HEADERS.has(lowerKey) ? maskApiKey(value) : value;
|
|
7945
8342
|
});
|
|
7946
8343
|
}
|
|
7947
8344
|
const log = await createLog(
|
|
@@ -7959,12 +8356,30 @@ async function handleProxy(req) {
|
|
|
7959
8356
|
);
|
|
7960
8357
|
let upstreamRes;
|
|
7961
8358
|
try {
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
8359
|
+
const fetchResult = await safeFetch(
|
|
8360
|
+
upstreamUrl,
|
|
8361
|
+
{
|
|
8362
|
+
method: req.method,
|
|
8363
|
+
headers: upstreamHeaders,
|
|
8364
|
+
body: bodyToForward,
|
|
8365
|
+
signal: req.signal
|
|
8366
|
+
},
|
|
8367
|
+
{ allowLoopback: ALLOW_LOOPBACK$2 }
|
|
8368
|
+
);
|
|
8369
|
+
if (!fetchResult.ok) {
|
|
8370
|
+
const status = fetchResult.kind === "network-policy" ? STATUS_FORBIDDEN : STATUS_BAD_GATEWAY;
|
|
8371
|
+
logger.warn(`[handler] Safe fetch blocked ${upstreamUrl}: ${fetchResult.reason}`);
|
|
8372
|
+
log.elapsedMs = Date.now() - startTime;
|
|
8373
|
+
log.responseStatus = status;
|
|
8374
|
+
log.responseText = fetchResult.reason;
|
|
8375
|
+
appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: fetchResult.reason });
|
|
8376
|
+
finalizeLogUpdate(log);
|
|
8377
|
+
return new Response(
|
|
8378
|
+
fetchResult.kind === "network-policy" ? "Forbidden: upstream URL is not permitted" : "Proxy error: upstream redirects are not permitted",
|
|
8379
|
+
{ status }
|
|
8380
|
+
);
|
|
8381
|
+
}
|
|
8382
|
+
upstreamRes = fetchResult.response;
|
|
7968
8383
|
} catch (err) {
|
|
7969
8384
|
log.elapsedMs = Date.now() - startTime;
|
|
7970
8385
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -8290,85 +8705,99 @@ function hasStreamingMismatch(session) {
|
|
|
8290
8705
|
}
|
|
8291
8706
|
return false;
|
|
8292
8707
|
}
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
|
|
8365
|
-
|
|
8366
|
-
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8708
|
+
const FAILURE_CATEGORY_INFO = {
|
|
8709
|
+
none: {
|
|
8710
|
+
summary: "Session completed without captured errors.",
|
|
8711
|
+
hints: ["Keep the evidence pack as the success proof for CI or release notes."],
|
|
8712
|
+
severity: "none",
|
|
8713
|
+
outcome: "success"
|
|
8714
|
+
},
|
|
8715
|
+
active: {
|
|
8716
|
+
summary: "Session still has active requests or running tasks.",
|
|
8717
|
+
hints: ["Wait for the session to settle, then export evidence again."],
|
|
8718
|
+
severity: "low",
|
|
8719
|
+
outcome: "active"
|
|
8720
|
+
},
|
|
8721
|
+
"provider-timeout": {
|
|
8722
|
+
summary: "Provider call appears to have timed out.",
|
|
8723
|
+
hints: [
|
|
8724
|
+
"Increase the provider test timeout for slow models.",
|
|
8725
|
+
"Compare streaming and non-streaming probes for the same model."
|
|
8726
|
+
],
|
|
8727
|
+
severity: "high",
|
|
8728
|
+
outcome: "failure"
|
|
8729
|
+
},
|
|
8730
|
+
"provider-http-error": {
|
|
8731
|
+
summary: "Provider returned an HTTP error.",
|
|
8732
|
+
hints: ["Open the cited log ids and inspect upstream status, response body, and provider URL."],
|
|
8733
|
+
severity: "high",
|
|
8734
|
+
outcome: "failure"
|
|
8735
|
+
},
|
|
8736
|
+
"provider-auth-error": {
|
|
8737
|
+
summary: "Provider authentication or API key configuration appears invalid.",
|
|
8738
|
+
hints: ["Verify the provider API key, auth header mode, and configured base URL."],
|
|
8739
|
+
severity: "high",
|
|
8740
|
+
outcome: "failure"
|
|
8741
|
+
},
|
|
8742
|
+
"provider-rate-limit": {
|
|
8743
|
+
summary: "Provider rejected the request because of rate limit or quota pressure.",
|
|
8744
|
+
hints: ["Retry after provider quota recovers or switch to a less constrained provider/model."],
|
|
8745
|
+
severity: "high",
|
|
8746
|
+
outcome: "failure"
|
|
8747
|
+
},
|
|
8748
|
+
"network-or-container": {
|
|
8749
|
+
summary: "Network connectivity looks suspicious; check container boundaries and localhost use.",
|
|
8750
|
+
hints: [
|
|
8751
|
+
"If the coding agent and Inspector are in different containers, replace localhost with a reachable host.",
|
|
8752
|
+
"Check DNS, proxy, firewall, and container network routes."
|
|
8753
|
+
],
|
|
8754
|
+
severity: "high",
|
|
8755
|
+
outcome: "failure"
|
|
8756
|
+
},
|
|
8757
|
+
"streaming-mismatch": {
|
|
8758
|
+
summary: "Streaming and non-streaming behavior differ for the same provider/model.",
|
|
8759
|
+
hints: [
|
|
8760
|
+
"Compare the streaming and non-streaming captured logs side by side.",
|
|
8761
|
+
"Check whether the provider's OpenAI-compatible endpoint handles stream=false differently."
|
|
8762
|
+
],
|
|
8763
|
+
severity: "high",
|
|
8764
|
+
outcome: "failure"
|
|
8765
|
+
},
|
|
8766
|
+
"context-pressure": {
|
|
8767
|
+
summary: "The failure looks related to context window or token pressure.",
|
|
8768
|
+
hints: [
|
|
8769
|
+
"Reduce prompt/tool schema/history size or configure the model context window metadata."
|
|
8770
|
+
],
|
|
8771
|
+
severity: "medium",
|
|
8772
|
+
outcome: "failure"
|
|
8773
|
+
},
|
|
8774
|
+
"runtime-task-error": {
|
|
8775
|
+
summary: "Inspector session runtime reported a task error.",
|
|
8776
|
+
hints: ["Inspect the session runtime error and retry after clearing stuck session tasks."],
|
|
8777
|
+
severity: "high",
|
|
8778
|
+
outcome: "failure"
|
|
8779
|
+
},
|
|
8780
|
+
"model-output-error": {
|
|
8781
|
+
summary: "The model output or upstream payload shape appears invalid.",
|
|
8782
|
+
hints: ["Inspect the raw response and schema parsing details for the cited log ids."],
|
|
8783
|
+
severity: "medium",
|
|
8784
|
+
outcome: "failure"
|
|
8785
|
+
},
|
|
8786
|
+
"unknown-failure": {
|
|
8787
|
+
summary: "Inspector captured errors, but the root cause is not yet classified.",
|
|
8788
|
+
hints: ["Open the cited logs and inspect error/status/latency fields to refine the diagnosis."],
|
|
8789
|
+
severity: "medium",
|
|
8790
|
+
outcome: "failure"
|
|
8791
|
+
},
|
|
8792
|
+
"no-session-data": {
|
|
8793
|
+
summary: "No Inspector session data was available for this run.",
|
|
8794
|
+
hints: [
|
|
8795
|
+
"Confirm the run's sessionId matches captured traffic and that history scanning is enabled when needed."
|
|
8796
|
+
],
|
|
8797
|
+
severity: "medium",
|
|
8798
|
+
outcome: "unknown"
|
|
8370
8799
|
}
|
|
8371
|
-
}
|
|
8800
|
+
};
|
|
8372
8801
|
function classifyCategory(session, candidates2) {
|
|
8373
8802
|
if (session.status === "active") return "active";
|
|
8374
8803
|
if (session.errorCount === 0 && session.failedRequests === 0 && session.lastTaskError === null) {
|
|
@@ -8407,46 +8836,16 @@ function classifyCategory(session, candidates2) {
|
|
|
8407
8836
|
return "unknown-failure";
|
|
8408
8837
|
}
|
|
8409
8838
|
function severityFor(category) {
|
|
8410
|
-
|
|
8411
|
-
case "none":
|
|
8412
|
-
return "none";
|
|
8413
|
-
case "active":
|
|
8414
|
-
return "low";
|
|
8415
|
-
case "provider-timeout":
|
|
8416
|
-
case "provider-http-error":
|
|
8417
|
-
case "provider-auth-error":
|
|
8418
|
-
case "provider-rate-limit":
|
|
8419
|
-
case "network-or-container":
|
|
8420
|
-
case "streaming-mismatch":
|
|
8421
|
-
case "runtime-task-error":
|
|
8422
|
-
return "high";
|
|
8423
|
-
case "context-pressure":
|
|
8424
|
-
case "model-output-error":
|
|
8425
|
-
case "unknown-failure":
|
|
8426
|
-
case "no-session-data":
|
|
8427
|
-
return "medium";
|
|
8428
|
-
}
|
|
8839
|
+
return FAILURE_CATEGORY_INFO[category].severity;
|
|
8429
8840
|
}
|
|
8430
8841
|
function outcomeFor$1(category) {
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
case "provider-timeout":
|
|
8439
|
-
case "provider-http-error":
|
|
8440
|
-
case "provider-auth-error":
|
|
8441
|
-
case "provider-rate-limit":
|
|
8442
|
-
case "network-or-container":
|
|
8443
|
-
case "streaming-mismatch":
|
|
8444
|
-
case "context-pressure":
|
|
8445
|
-
case "runtime-task-error":
|
|
8446
|
-
case "model-output-error":
|
|
8447
|
-
case "unknown-failure":
|
|
8448
|
-
return "failure";
|
|
8449
|
-
}
|
|
8842
|
+
return FAILURE_CATEGORY_INFO[category].outcome;
|
|
8843
|
+
}
|
|
8844
|
+
function categorySummary(category) {
|
|
8845
|
+
return FAILURE_CATEGORY_INFO[category].summary;
|
|
8846
|
+
}
|
|
8847
|
+
function categoryHints(category) {
|
|
8848
|
+
return FAILURE_CATEGORY_INFO[category].hints;
|
|
8450
8849
|
}
|
|
8451
8850
|
function classifyRunFailure(run, session) {
|
|
8452
8851
|
if (session === null) {
|
|
@@ -8827,15 +9226,8 @@ function nullableText(value) {
|
|
|
8827
9226
|
return trimmed.length > 0 ? trimmed : null;
|
|
8828
9227
|
}
|
|
8829
9228
|
function readGroups() {
|
|
8830
|
-
const
|
|
8831
|
-
|
|
8832
|
-
try {
|
|
8833
|
-
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
8834
|
-
const result = GroupsFileSchema.safeParse(parsed);
|
|
8835
|
-
return result.success ? result.data.groups : [];
|
|
8836
|
-
} catch {
|
|
8837
|
-
return [];
|
|
8838
|
-
}
|
|
9229
|
+
const result = readJsonFile(groupsFilePath(), GroupsFileSchema);
|
|
9230
|
+
return result.ok ? result.data.groups : [];
|
|
8839
9231
|
}
|
|
8840
9232
|
function writeGroups(groups) {
|
|
8841
9233
|
const filePath = groupsFilePath();
|
|
@@ -9087,15 +9479,8 @@ function syncRunGroupMember(run, createIfMissing) {
|
|
|
9087
9479
|
});
|
|
9088
9480
|
}
|
|
9089
9481
|
function readRuns() {
|
|
9090
|
-
const
|
|
9091
|
-
|
|
9092
|
-
try {
|
|
9093
|
-
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
9094
|
-
const result = RunsFileSchema.safeParse(parsed);
|
|
9095
|
-
return result.success ? result.data.runs : [];
|
|
9096
|
-
} catch {
|
|
9097
|
-
return [];
|
|
9098
|
-
}
|
|
9482
|
+
const result = readJsonFile(runsFilePath(), RunsFileSchema);
|
|
9483
|
+
return result.ok ? result.data.runs : [];
|
|
9099
9484
|
}
|
|
9100
9485
|
function writeRuns(runs) {
|
|
9101
9486
|
const filePath = runsFilePath();
|
|
@@ -9363,14 +9748,9 @@ function clampRecentFailuresLimit(value) {
|
|
|
9363
9748
|
}
|
|
9364
9749
|
function readEvidenceDocument(run) {
|
|
9365
9750
|
const path2 = run.evidence?.jsonPath;
|
|
9366
|
-
if (path2 === void 0
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
const result = EvidenceExportResultSchema.safeParse(parsed);
|
|
9370
|
-
return result.success ? result.data : null;
|
|
9371
|
-
} catch {
|
|
9372
|
-
return null;
|
|
9373
|
-
}
|
|
9751
|
+
if (path2 === void 0) return null;
|
|
9752
|
+
const result = readJsonFile(path2, EvidenceExportResultSchema);
|
|
9753
|
+
return result.ok ? result.data : null;
|
|
9374
9754
|
}
|
|
9375
9755
|
function syntheticFailedClassification(run) {
|
|
9376
9756
|
return {
|
|
@@ -9506,6 +9886,7 @@ const Route$H = createFileRoute("/api/runs")({
|
|
|
9506
9886
|
}
|
|
9507
9887
|
}
|
|
9508
9888
|
});
|
|
9889
|
+
const ALLOW_LOOPBACK$1 = process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1";
|
|
9509
9890
|
const ProviderInputSchema = object({
|
|
9510
9891
|
name: string().min(1, "Name is required"),
|
|
9511
9892
|
apiKey: string().min(1, "API key is required"),
|
|
@@ -9524,17 +9905,25 @@ const ProviderInputSchema = object({
|
|
|
9524
9905
|
const Route$G = createFileRoute("/api/providers")({
|
|
9525
9906
|
server: {
|
|
9526
9907
|
handlers: {
|
|
9527
|
-
GET: (
|
|
9528
|
-
|
|
9529
|
-
const includeSecrets = url.searchParams.get("includeSecrets") === "1";
|
|
9530
|
-
const providers = getProviders();
|
|
9531
|
-
return Response.json(includeSecrets ? providers : redactProviders(providers));
|
|
9908
|
+
GET: () => {
|
|
9909
|
+
return Response.json(redactProviders(getProviders()));
|
|
9532
9910
|
},
|
|
9533
9911
|
POST: async ({ request }) => {
|
|
9534
9912
|
const parsed = ProviderInputSchema.safeParse(await request.json());
|
|
9535
9913
|
if (!parsed.success) {
|
|
9536
9914
|
return Response.json({ error: parsed.error.message }, { status: 400 });
|
|
9537
9915
|
}
|
|
9916
|
+
for (const candidate of [
|
|
9917
|
+
parsed.data.anthropicBaseUrl,
|
|
9918
|
+
parsed.data.openaiBaseUrl,
|
|
9919
|
+
parsed.data.openaiResponsesBaseUrl
|
|
9920
|
+
]) {
|
|
9921
|
+
if (candidate === void 0 || candidate === "") continue;
|
|
9922
|
+
const decision = evaluateUpstreamUrl(candidate, { allowLoopback: ALLOW_LOOPBACK$1 });
|
|
9923
|
+
if (!decision.allow) {
|
|
9924
|
+
return Response.json({ error: decision.reason }, { status: 400 });
|
|
9925
|
+
}
|
|
9926
|
+
}
|
|
9538
9927
|
const newProvider = addProvider(
|
|
9539
9928
|
parsed.data.name,
|
|
9540
9929
|
parsed.data.apiKey,
|
|
@@ -9551,7 +9940,7 @@ const Route$G = createFileRoute("/api/providers")({
|
|
|
9551
9940
|
parsed.data.modelMetadata,
|
|
9552
9941
|
parsed.data.openaiResponsesBaseUrl
|
|
9553
9942
|
);
|
|
9554
|
-
return Response.json(newProvider, { status: 201 });
|
|
9943
|
+
return Response.json(redactProvider(newProvider), { status: 201 });
|
|
9555
9944
|
}
|
|
9556
9945
|
}
|
|
9557
9946
|
}
|
|
@@ -9571,9 +9960,6 @@ const PiAgentResponseSchema = object({
|
|
|
9571
9960
|
protocol: _enum(["openaiResponses", "openaiChat", "anthropic"]),
|
|
9572
9961
|
logCount: number().int().nonnegative()
|
|
9573
9962
|
});
|
|
9574
|
-
function isRecord$1(value) {
|
|
9575
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9576
|
-
}
|
|
9577
9963
|
function normalizeAnthropicStopReason(value) {
|
|
9578
9964
|
switch (value) {
|
|
9579
9965
|
case "end_turn":
|
|
@@ -9612,20 +9998,20 @@ function extractStopReason(log) {
|
|
|
9612
9998
|
if (typeof json === "string") {
|
|
9613
9999
|
json = JSON.parse(json);
|
|
9614
10000
|
}
|
|
9615
|
-
if (!isRecord
|
|
10001
|
+
if (!isRecord(json)) return null;
|
|
9616
10002
|
if (typeof json.stop_reason === "string") {
|
|
9617
10003
|
return normalizeAnthropicStopReason(json.stop_reason);
|
|
9618
10004
|
}
|
|
9619
10005
|
if (Array.isArray(json.choices)) {
|
|
9620
10006
|
for (const choice of json.choices) {
|
|
9621
|
-
if (!isRecord
|
|
10007
|
+
if (!isRecord(choice) || typeof choice.finish_reason !== "string") continue;
|
|
9622
10008
|
const reason = normalizeOpenAIFinishReason(choice.finish_reason);
|
|
9623
10009
|
if (reason !== null) return reason;
|
|
9624
10010
|
}
|
|
9625
10011
|
}
|
|
9626
10012
|
if (Array.isArray(json.output)) {
|
|
9627
10013
|
for (const item of json.output) {
|
|
9628
|
-
if (isRecord
|
|
10014
|
+
if (isRecord(item) && item.type === "function_call") {
|
|
9629
10015
|
return "tool_use";
|
|
9630
10016
|
}
|
|
9631
10017
|
}
|
|
@@ -9633,7 +10019,7 @@ function extractStopReason(log) {
|
|
|
9633
10019
|
return "stop";
|
|
9634
10020
|
}
|
|
9635
10021
|
}
|
|
9636
|
-
if (json.status === "incomplete" && isRecord
|
|
10022
|
+
if (json.status === "incomplete" && isRecord(json.incomplete_details)) {
|
|
9637
10023
|
const reason = json.incomplete_details.reason;
|
|
9638
10024
|
if (typeof reason === "string" && isResponsesLengthLimitReason(reason)) {
|
|
9639
10025
|
return "length";
|
|
@@ -9980,7 +10366,7 @@ const Route$E = createFileRoute("/api/models")({
|
|
|
9980
10366
|
}
|
|
9981
10367
|
});
|
|
9982
10368
|
const name = "@tonyclaw/agent-inspector";
|
|
9983
|
-
const version = "3.0.
|
|
10369
|
+
const version = "3.0.28";
|
|
9984
10370
|
const packageJson = {
|
|
9985
10371
|
name,
|
|
9986
10372
|
version
|
|
@@ -10062,8 +10448,7 @@ function isMcpWriteEnabled() {
|
|
|
10062
10448
|
const readonlyFlag = parseFlag(process.env["AGENT_INSPECTOR_MCP_READONLY"]);
|
|
10063
10449
|
if (readonlyFlag === true) return false;
|
|
10064
10450
|
const writesFlag = parseFlag(process.env["AGENT_INSPECTOR_MCP_WRITES"]);
|
|
10065
|
-
|
|
10066
|
-
return true;
|
|
10451
|
+
return writesFlag === true;
|
|
10067
10452
|
}
|
|
10068
10453
|
function getMcpModeInfo() {
|
|
10069
10454
|
const writesEnabled = isMcpWriteEnabled();
|
|
@@ -10074,7 +10459,7 @@ function getMcpModeInfo() {
|
|
|
10074
10459
|
AGENT_INSPECTOR_MCP_READONLY: process.env["AGENT_INSPECTOR_MCP_READONLY"] ?? null,
|
|
10075
10460
|
AGENT_INSPECTOR_MCP_WRITES: process.env["AGENT_INSPECTOR_MCP_WRITES"] ?? null
|
|
10076
10461
|
},
|
|
10077
|
-
note: writesEnabled ? "MCP write/action tools are enabled
|
|
10462
|
+
note: writesEnabled ? "MCP write/action tools are enabled explicitly by AGENT_INSPECTOR_MCP_WRITES=1." : "MCP is readonly by default. Read tools/resources still work; set AGENT_INSPECTOR_MCP_WRITES=1 to enable mutations."
|
|
10078
10463
|
};
|
|
10079
10464
|
}
|
|
10080
10465
|
function groupEvidenceDir(groupId) {
|
|
@@ -10356,14 +10741,9 @@ function readGroupEvidenceMarkdown(group) {
|
|
|
10356
10741
|
}
|
|
10357
10742
|
function readGroupEvidenceDocument(group) {
|
|
10358
10743
|
const path2 = group.evidence?.jsonPath;
|
|
10359
|
-
if (path2 === void 0
|
|
10360
|
-
|
|
10361
|
-
|
|
10362
|
-
const parsed = GroupEvidenceExportResultSchema.safeParse(raw);
|
|
10363
|
-
return parsed.success ? parsed.data : null;
|
|
10364
|
-
} catch {
|
|
10365
|
-
return null;
|
|
10366
|
-
}
|
|
10744
|
+
if (path2 === void 0) return null;
|
|
10745
|
+
const result = readJsonFile(path2, GroupEvidenceExportResultSchema);
|
|
10746
|
+
return result.ok ? result.data : null;
|
|
10367
10747
|
}
|
|
10368
10748
|
function listInspectorGroups() {
|
|
10369
10749
|
return { groups: listGroups() };
|
|
@@ -10646,10 +11026,6 @@ function toolError(message) {
|
|
|
10646
11026
|
function inspectorBaseUrl() {
|
|
10647
11027
|
return `http://127.0.0.1:${String(getCurrentPort())}`;
|
|
10648
11028
|
}
|
|
10649
|
-
function textByteLength(value) {
|
|
10650
|
-
if (value === null) return null;
|
|
10651
|
-
return Buffer.byteLength(value, "utf8");
|
|
10652
|
-
}
|
|
10653
11029
|
function hasBodyText(value, byteLength) {
|
|
10654
11030
|
if (value !== null && value.length > 0) return true;
|
|
10655
11031
|
return byteLength !== void 0 && byteLength !== null && byteLength > 0;
|
|
@@ -10780,14 +11156,29 @@ function processSummary(clientPid, logs, total, limit) {
|
|
|
10780
11156
|
}
|
|
10781
11157
|
async function readCompactLogsPage(callApi2, params) {
|
|
10782
11158
|
const path2 = `/api/logs?${params.toString()}`;
|
|
10783
|
-
const
|
|
10784
|
-
if (!
|
|
10785
|
-
const rawBody = await res.json();
|
|
10786
|
-
const parsed = LogsListResponseSchema.safeParse(rawBody);
|
|
10787
|
-
if (!parsed.success) return toolError("GET /api/logs returned an unparseable logs shape");
|
|
11159
|
+
const parsed = await fetchApiJson(callApi2, path2, LogsListResponseSchema);
|
|
11160
|
+
if (!parsed.ok) return parsed.error;
|
|
10788
11161
|
const logs = parsed.data.logs ?? [];
|
|
10789
11162
|
return { logs, total: parsed.data.total ?? logs.length };
|
|
10790
11163
|
}
|
|
11164
|
+
async function fetchApiJson(callApi2, path2, schema, init) {
|
|
11165
|
+
const res = await callApi2(path2, init);
|
|
11166
|
+
if (!res.ok) {
|
|
11167
|
+
return { ok: false, error: toolError(`${methodFor(init)} ${path2} returned ${res.status}`) };
|
|
11168
|
+
}
|
|
11169
|
+
const rawBody = await res.json();
|
|
11170
|
+
const parsed = schema.safeParse(rawBody);
|
|
11171
|
+
if (!parsed.success) {
|
|
11172
|
+
return {
|
|
11173
|
+
ok: false,
|
|
11174
|
+
error: toolError(`${methodFor(init)} ${path2} returned an unparseable shape`)
|
|
11175
|
+
};
|
|
11176
|
+
}
|
|
11177
|
+
return { ok: true, data: parsed.data };
|
|
11178
|
+
}
|
|
11179
|
+
function methodFor(init) {
|
|
11180
|
+
return (init?.method ?? "GET").toUpperCase();
|
|
11181
|
+
}
|
|
10791
11182
|
function isToolResult(value) {
|
|
10792
11183
|
return typeof value === "object" && value !== null && Object.getOwnPropertyDescriptor(value, "content") !== void 0;
|
|
10793
11184
|
}
|
|
@@ -10847,13 +11238,8 @@ async function searchLogsImpl(callApi2, args) {
|
|
|
10847
11238
|
});
|
|
10848
11239
|
}
|
|
10849
11240
|
async function getLogImpl(callApi2, id) {
|
|
10850
|
-
const
|
|
10851
|
-
if (!
|
|
10852
|
-
const rawBody = await res.json();
|
|
10853
|
-
const parsed = CapturedLogSchema.safeParse(rawBody);
|
|
10854
|
-
if (!parsed.success) {
|
|
10855
|
-
return toolError(`GET /api/logs/${id} returned an unparseable CapturedLog`);
|
|
10856
|
-
}
|
|
11241
|
+
const parsed = await fetchApiJson(callApi2, `/api/logs/${id}`, CapturedLogSchema);
|
|
11242
|
+
if (!parsed.ok) return parsed.error;
|
|
10857
11243
|
return textJson(buildBoundedLogDetail(parsed.data));
|
|
10858
11244
|
}
|
|
10859
11245
|
async function getLogChunksImpl(callApi2, id) {
|
|
@@ -10875,11 +11261,8 @@ async function getSessionImpl(callApi2, args) {
|
|
|
10875
11261
|
params.set("includeHistory", "1");
|
|
10876
11262
|
}
|
|
10877
11263
|
const path2 = `/api/sessions?${params.toString()}`;
|
|
10878
|
-
const
|
|
10879
|
-
if (!
|
|
10880
|
-
const rawBody = await res.json();
|
|
10881
|
-
const parsed = SessionInfoSchema.safeParse(rawBody);
|
|
10882
|
-
if (!parsed.success) return toolError("GET /api/sessions returned an unparseable session info");
|
|
11264
|
+
const parsed = await fetchApiJson(callApi2, path2, SessionInfoSchema);
|
|
11265
|
+
if (!parsed.ok) return parsed.error;
|
|
10883
11266
|
return textJson(parsed.data);
|
|
10884
11267
|
}
|
|
10885
11268
|
function createRunImpl(args) {
|
|
@@ -11089,13 +11472,8 @@ async function getProcessTimelineImpl(callApi2, args) {
|
|
|
11089
11472
|
});
|
|
11090
11473
|
}
|
|
11091
11474
|
async function getLogHeadersImpl(callApi2, id) {
|
|
11092
|
-
const
|
|
11093
|
-
if (!
|
|
11094
|
-
const rawBody = await res.json();
|
|
11095
|
-
const parsed = CapturedLogSchema.safeParse(rawBody);
|
|
11096
|
-
if (!parsed.success) {
|
|
11097
|
-
return toolError(`GET /api/logs/${id} returned an unparseable CapturedLog`);
|
|
11098
|
-
}
|
|
11475
|
+
const parsed = await fetchApiJson(callApi2, `/api/logs/${id}`, CapturedLogSchema);
|
|
11476
|
+
if (!parsed.ok) return parsed.error;
|
|
11099
11477
|
const log = parsed.data;
|
|
11100
11478
|
return textJson({
|
|
11101
11479
|
id: log.id,
|
|
@@ -11346,18 +11724,13 @@ async function listModelsImpl(callApi2) {
|
|
|
11346
11724
|
if (!res.ok) return toolError(`GET /api/models returned ${res.status}`);
|
|
11347
11725
|
return textJson(await res.json());
|
|
11348
11726
|
}
|
|
11349
|
-
function
|
|
11350
|
-
|
|
11351
|
-
}
|
|
11352
|
-
async function listProvidersImpl(callApi2, args = {}) {
|
|
11353
|
-
const res = await callApi2(`/api/providers${includeSecretsQuery(args.includeSecrets)}`);
|
|
11727
|
+
async function listProvidersImpl(callApi2) {
|
|
11728
|
+
const res = await callApi2("/api/providers");
|
|
11354
11729
|
if (!res.ok) return toolError(`GET /api/providers returned ${res.status}`);
|
|
11355
11730
|
return textJson(await res.json());
|
|
11356
11731
|
}
|
|
11357
|
-
async function getProviderImpl(callApi2, id
|
|
11358
|
-
const res = await callApi2(
|
|
11359
|
-
`/api/providers/${encodeURIComponent(id)}${includeSecretsQuery(args.includeSecrets)}`
|
|
11360
|
-
);
|
|
11732
|
+
async function getProviderImpl(callApi2, id) {
|
|
11733
|
+
const res = await callApi2(`/api/providers/${encodeURIComponent(id)}`);
|
|
11361
11734
|
if (!res.ok) return toolError(`GET /api/providers/${id} returned ${res.status}`);
|
|
11362
11735
|
return textJson(await res.json());
|
|
11363
11736
|
}
|
|
@@ -11417,9 +11790,6 @@ async function testProviderImpl(callApi2, id) {
|
|
|
11417
11790
|
if (!res.ok) return toolError(`POST /api/providers/${id}/test returned ${res.status}`);
|
|
11418
11791
|
return textJson(await res.json());
|
|
11419
11792
|
}
|
|
11420
|
-
function isRecord(value) {
|
|
11421
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11422
|
-
}
|
|
11423
11793
|
function stringField(record2, key) {
|
|
11424
11794
|
const value = record2[key];
|
|
11425
11795
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
@@ -12442,24 +12812,21 @@ function registerTools(server) {
|
|
|
12442
12812
|
"inspector_list_providers",
|
|
12443
12813
|
{
|
|
12444
12814
|
title: "List configured LLM providers",
|
|
12445
|
-
description: "Returns the configured ProviderConfig array with
|
|
12446
|
-
inputSchema: object({
|
|
12447
|
-
includeSecrets: boolean().optional().describe("Return plaintext provider API keys. Exposes local secrets when true.")
|
|
12448
|
-
})
|
|
12815
|
+
description: "Returns the configured ProviderConfig array with write-only API keys represented as configured.",
|
|
12816
|
+
inputSchema: object({})
|
|
12449
12817
|
},
|
|
12450
|
-
(
|
|
12818
|
+
() => safeCall(() => listProvidersImpl(callApi))
|
|
12451
12819
|
);
|
|
12452
12820
|
server.registerTool(
|
|
12453
12821
|
"inspector_get_provider",
|
|
12454
12822
|
{
|
|
12455
12823
|
title: "Get a single provider by id",
|
|
12456
|
-
description: "Returns one ProviderConfig
|
|
12824
|
+
description: "Returns one ProviderConfig. API keys are write-only and represented as configured.",
|
|
12457
12825
|
inputSchema: object({
|
|
12458
|
-
id: string().describe("The provider id.")
|
|
12459
|
-
includeSecrets: boolean().optional().describe("Return the plaintext provider API key. Exposes a local secret when true.")
|
|
12826
|
+
id: string().describe("The provider id.")
|
|
12460
12827
|
})
|
|
12461
12828
|
},
|
|
12462
|
-
({ id
|
|
12829
|
+
({ id }) => safeCall(() => getProviderImpl(callApi, id))
|
|
12463
12830
|
);
|
|
12464
12831
|
server.registerTool(
|
|
12465
12832
|
"inspector_replay_log",
|
|
@@ -14601,6 +14968,7 @@ const Route$u = createFileRoute("/api/providers/export")({
|
|
|
14601
14968
|
}
|
|
14602
14969
|
}
|
|
14603
14970
|
});
|
|
14971
|
+
const ALLOW_LOOPBACK = process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1";
|
|
14604
14972
|
const ProviderUpdateSchema = object({
|
|
14605
14973
|
name: string().min(1, "Name is required").optional(),
|
|
14606
14974
|
apiKey: string().min(1, "API key is required").optional(),
|
|
@@ -14620,26 +14988,36 @@ const ProviderUpdateSchema = object({
|
|
|
14620
14988
|
const Route$t = createFileRoute("/api/providers/$providerId")({
|
|
14621
14989
|
server: {
|
|
14622
14990
|
handlers: {
|
|
14623
|
-
GET: ({ params
|
|
14624
|
-
const url = new URL(request.url);
|
|
14625
|
-
const includeSecrets = url.searchParams.get("includeSecrets") === "1";
|
|
14991
|
+
GET: ({ params }) => {
|
|
14626
14992
|
const providers = getProviders();
|
|
14627
14993
|
const provider = providers.find((p) => p.id === params.providerId);
|
|
14628
14994
|
if (!provider) {
|
|
14629
14995
|
return Response.json({ error: "Provider not found" }, { status: 404 });
|
|
14630
14996
|
}
|
|
14631
|
-
return Response.json(
|
|
14997
|
+
return Response.json(redactProvider(provider));
|
|
14632
14998
|
},
|
|
14633
14999
|
PUT: async ({ params, request }) => {
|
|
14634
15000
|
const parsed = ProviderUpdateSchema.safeParse(await request.json());
|
|
14635
15001
|
if (!parsed.success) {
|
|
14636
15002
|
return Response.json({ error: parsed.error.message }, { status: 400 });
|
|
14637
15003
|
}
|
|
15004
|
+
for (const candidate of [
|
|
15005
|
+
parsed.data.baseUrl,
|
|
15006
|
+
parsed.data.anthropicBaseUrl,
|
|
15007
|
+
parsed.data.openaiBaseUrl,
|
|
15008
|
+
parsed.data.openaiResponsesBaseUrl
|
|
15009
|
+
]) {
|
|
15010
|
+
if (candidate === void 0 || candidate === "") continue;
|
|
15011
|
+
const decision = evaluateUpstreamUrl(candidate, { allowLoopback: ALLOW_LOOPBACK });
|
|
15012
|
+
if (!decision.allow) {
|
|
15013
|
+
return Response.json({ error: decision.reason }, { status: 400 });
|
|
15014
|
+
}
|
|
15015
|
+
}
|
|
14638
15016
|
const updated = updateProvider(params.providerId, parsed.data);
|
|
14639
15017
|
if (!updated) {
|
|
14640
15018
|
return Response.json({ error: "Provider not found" }, { status: 404 });
|
|
14641
15019
|
}
|
|
14642
|
-
return Response.json(updated);
|
|
15020
|
+
return Response.json(redactProvider(updated));
|
|
14643
15021
|
},
|
|
14644
15022
|
DELETE: ({ params }) => {
|
|
14645
15023
|
const deleted = deleteProvider(params.providerId);
|
|
@@ -14725,6 +15103,8 @@ const Route$s = createFileRoute("/api/logs/stream")({
|
|
|
14725
15103
|
}
|
|
14726
15104
|
}
|
|
14727
15105
|
});
|
|
15106
|
+
const MAX_ZIP_COMPRESSED_BYTES = 200 * 1024 * 1024;
|
|
15107
|
+
const MAX_ZIP_DECOMPRESSED_BYTES = 500 * 1024 * 1024;
|
|
14728
15108
|
const ManifestSchema = object({
|
|
14729
15109
|
version: string().optional(),
|
|
14730
15110
|
exportedAt: string().optional(),
|
|
@@ -14925,12 +15305,15 @@ function legacyDraftFromParts(input) {
|
|
|
14925
15305
|
error: null
|
|
14926
15306
|
});
|
|
14927
15307
|
}
|
|
14928
|
-
async function readZipText(zip, path2) {
|
|
15308
|
+
async function readZipText(zip, path2, remaining) {
|
|
14929
15309
|
const file = zip.file(path2);
|
|
14930
|
-
|
|
15310
|
+
if (file === null) return null;
|
|
15311
|
+
const text = await file.async("text");
|
|
15312
|
+
remaining.bytes -= Buffer.byteLength(text, "utf8");
|
|
15313
|
+
return text;
|
|
14931
15314
|
}
|
|
14932
|
-
async function readManifest(zip) {
|
|
14933
|
-
const text = await readZipText(zip, "manifest.json");
|
|
15315
|
+
async function readManifest(zip, remaining) {
|
|
15316
|
+
const text = await readZipText(zip, "manifest.json", remaining);
|
|
14934
15317
|
const parsed = ManifestSchema.safeParse(parseJson(text));
|
|
14935
15318
|
return parsed.success ? parsed.data : null;
|
|
14936
15319
|
}
|
|
@@ -14976,10 +15359,19 @@ function validateManifestVersion(manifest) {
|
|
|
14976
15359
|
}
|
|
14977
15360
|
return { ok: true, warning: null };
|
|
14978
15361
|
}
|
|
15362
|
+
function bombError(warnings = []) {
|
|
15363
|
+
return {
|
|
15364
|
+
ok: false,
|
|
15365
|
+
message: `Archive decompresses to more than ${MAX_ZIP_DECOMPRESSED_BYTES} bytes.`,
|
|
15366
|
+
warnings
|
|
15367
|
+
};
|
|
15368
|
+
}
|
|
14979
15369
|
async function parseZipLogImport(data) {
|
|
14980
15370
|
const warnings = [];
|
|
14981
15371
|
const zip = await JSZip.loadAsync(data);
|
|
14982
|
-
const
|
|
15372
|
+
const remaining = { bytes: MAX_ZIP_DECOMPRESSED_BYTES };
|
|
15373
|
+
const manifest = await readManifest(zip, remaining);
|
|
15374
|
+
if (remaining.bytes < 0) return bombError();
|
|
14983
15375
|
const versionCheck = validateManifestVersion(manifest);
|
|
14984
15376
|
if (!versionCheck.ok) {
|
|
14985
15377
|
return {
|
|
@@ -14995,15 +15387,21 @@ async function parseZipLogImport(data) {
|
|
|
14995
15387
|
if (manifest?.redacted === true) {
|
|
14996
15388
|
warnings.push("Imported redacted export. Secrets and some replay details may be unavailable.");
|
|
14997
15389
|
}
|
|
14998
|
-
const metadataText2 = await readZipText(zip, "logs.json");
|
|
15390
|
+
const metadataText2 = await readZipText(zip, "logs.json", remaining);
|
|
15391
|
+
if (remaining.bytes < 0) return bombError(warnings);
|
|
14999
15392
|
const metadataLogs = metadataText2 === null ? null : parseJsonLogs(metadataText2);
|
|
15000
15393
|
const logs = [];
|
|
15001
15394
|
let skipped = 0;
|
|
15002
15395
|
if (metadataLogs !== null) {
|
|
15003
15396
|
for (const log of metadataLogs) {
|
|
15004
|
-
const requestText = await readZipText(zip, `#${String(log.id)}.Request.json
|
|
15005
|
-
const responseText = await readZipText(zip, `#${String(log.id)}.Response.json
|
|
15006
|
-
const streamingText = await readZipText(
|
|
15397
|
+
const requestText = await readZipText(zip, `#${String(log.id)}.Request.json`, remaining);
|
|
15398
|
+
const responseText = await readZipText(zip, `#${String(log.id)}.Response.json`, remaining);
|
|
15399
|
+
const streamingText = await readZipText(
|
|
15400
|
+
zip,
|
|
15401
|
+
`#${String(log.id)}.SSE.Response.json`,
|
|
15402
|
+
remaining
|
|
15403
|
+
);
|
|
15404
|
+
if (remaining.bytes < 0) return bombError(warnings);
|
|
15007
15405
|
logs.push(
|
|
15008
15406
|
draftFromCapturedLog(
|
|
15009
15407
|
log,
|
|
@@ -15016,9 +15414,14 @@ async function parseZipLogImport(data) {
|
|
|
15016
15414
|
return { ok: true, logs, skipped, warnings };
|
|
15017
15415
|
}
|
|
15018
15416
|
for (const originalId of manifest?.logIds ?? []) {
|
|
15019
|
-
const requestText = await readZipText(zip, `#${String(originalId)}.Request.json
|
|
15020
|
-
const responseText = await readZipText(zip, `#${String(originalId)}.Response.json
|
|
15021
|
-
const streamingText = await readZipText(
|
|
15417
|
+
const requestText = await readZipText(zip, `#${String(originalId)}.Request.json`, remaining);
|
|
15418
|
+
const responseText = await readZipText(zip, `#${String(originalId)}.Response.json`, remaining);
|
|
15419
|
+
const streamingText = await readZipText(
|
|
15420
|
+
zip,
|
|
15421
|
+
`#${String(originalId)}.SSE.Response.json`,
|
|
15422
|
+
remaining
|
|
15423
|
+
);
|
|
15424
|
+
if (remaining.bytes < 0) return bombError(warnings);
|
|
15022
15425
|
if (requestText === null && responseText === null && streamingText === null) {
|
|
15023
15426
|
skipped += 1;
|
|
15024
15427
|
continue;
|
|
@@ -15073,8 +15476,22 @@ function parseTextLogImport(text) {
|
|
|
15073
15476
|
async function parseImportedLogFile(input) {
|
|
15074
15477
|
try {
|
|
15075
15478
|
if (looksLikeZip(input.data, input.fileName)) {
|
|
15479
|
+
if (input.data.byteLength > MAX_ZIP_COMPRESSED_BYTES) {
|
|
15480
|
+
return {
|
|
15481
|
+
ok: false,
|
|
15482
|
+
message: `Archive exceeds maximum size of ${MAX_ZIP_COMPRESSED_BYTES} bytes.`,
|
|
15483
|
+
warnings: []
|
|
15484
|
+
};
|
|
15485
|
+
}
|
|
15076
15486
|
return await parseZipLogImport(input.data);
|
|
15077
15487
|
}
|
|
15488
|
+
if (input.data.byteLength > MAX_ZIP_COMPRESSED_BYTES) {
|
|
15489
|
+
return {
|
|
15490
|
+
ok: false,
|
|
15491
|
+
message: `Import file exceeds maximum size of ${MAX_ZIP_COMPRESSED_BYTES} bytes.`,
|
|
15492
|
+
warnings: []
|
|
15493
|
+
};
|
|
15494
|
+
}
|
|
15078
15495
|
return parseTextLogImport(new TextDecoder("utf-8").decode(input.data));
|
|
15079
15496
|
} catch (err) {
|
|
15080
15497
|
return {
|
|
@@ -15352,19 +15769,25 @@ async function searchOpenClawGateway(query, project) {
|
|
|
15352
15769
|
warning: "OpenClaw Gateway is not configured. Set OPENCLAW_GATEWAY_URL."
|
|
15353
15770
|
};
|
|
15354
15771
|
}
|
|
15355
|
-
const
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
|
|
15367
|
-
|
|
15772
|
+
const fetchResult = await safeFetch(
|
|
15773
|
+
buildToolInvokeUrl(gatewayUrl),
|
|
15774
|
+
{
|
|
15775
|
+
method: "POST",
|
|
15776
|
+
headers: gatewayHeaders(),
|
|
15777
|
+
body: JSON.stringify({
|
|
15778
|
+
tool: "memory_search",
|
|
15779
|
+
args: {
|
|
15780
|
+
query: buildSearchQuery(query, project),
|
|
15781
|
+
maxResults: 10,
|
|
15782
|
+
corpus: "all"
|
|
15783
|
+
},
|
|
15784
|
+
sessionKey: getOpenClawSessionKey()
|
|
15785
|
+
})
|
|
15786
|
+
},
|
|
15787
|
+
{ bypass: true }
|
|
15788
|
+
);
|
|
15789
|
+
if (!fetchResult.ok) return { results: [], warning: fetchResult.reason };
|
|
15790
|
+
const response = fetchResult.response;
|
|
15368
15791
|
const raw = await response.json().catch(() => null);
|
|
15369
15792
|
if (!response.ok) {
|
|
15370
15793
|
return {
|
|
@@ -15619,11 +16042,17 @@ async function promoteToOpenClaw(candidate) {
|
|
|
15619
16042
|
error: "OpenClaw memory backend is not configured. Set OPENCLAW_WORKSPACE_DIR for file-backed memory or OPENCLAW_MEMORY_URL for a legacy HTTP backend."
|
|
15620
16043
|
};
|
|
15621
16044
|
}
|
|
15622
|
-
const
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
16045
|
+
const fetchResult = await safeFetch(
|
|
16046
|
+
buildMemoryUrl(endpoint),
|
|
16047
|
+
{
|
|
16048
|
+
method: "POST",
|
|
16049
|
+
headers: authHeaders(),
|
|
16050
|
+
body: JSON.stringify(buildOpenClawPayload(candidate))
|
|
16051
|
+
},
|
|
16052
|
+
{ bypass: true }
|
|
16053
|
+
);
|
|
16054
|
+
if (!fetchResult.ok) return { success: false, error: fetchResult.reason };
|
|
16055
|
+
const response = fetchResult.response;
|
|
15627
16056
|
if (!response.ok) {
|
|
15628
16057
|
return { success: false, error: `OpenClaw memory write failed with ${response.status}` };
|
|
15629
16058
|
}
|
|
@@ -15646,10 +16075,16 @@ async function searchOpenClaw(query, project) {
|
|
|
15646
16075
|
warning: `OpenClaw search backend is not configured. Set OPENCLAW_GATEWAY_URL or OPENCLAW_MEMORY_URL.${workspaceHint}`
|
|
15647
16076
|
};
|
|
15648
16077
|
}
|
|
15649
|
-
const
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
16078
|
+
const fetchResult = await safeFetch(
|
|
16079
|
+
buildSearchUrl(endpoint, query, project),
|
|
16080
|
+
{
|
|
16081
|
+
method: "GET",
|
|
16082
|
+
headers: authHeaders()
|
|
16083
|
+
},
|
|
16084
|
+
{ bypass: true }
|
|
16085
|
+
);
|
|
16086
|
+
if (!fetchResult.ok) return { results: [], warning: fetchResult.reason };
|
|
16087
|
+
const response = fetchResult.response;
|
|
15653
16088
|
if (!response.ok) {
|
|
15654
16089
|
return { results: [], warning: `OpenClaw search failed with ${response.status}` };
|
|
15655
16090
|
}
|
|
@@ -16770,12 +17205,20 @@ async function testEndpoint(baseUrl, provider, path2, model, protocol) {
|
|
|
16770
17205
|
const timeoutId = setTimeout(() => controller.abort(), getProviderTestTimeoutMs());
|
|
16771
17206
|
try {
|
|
16772
17207
|
requestUrl = buildUpstreamUrl(baseUrl, path2);
|
|
16773
|
-
const
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
17208
|
+
const fetchResult = await safeFetch(
|
|
17209
|
+
requestUrl,
|
|
17210
|
+
{
|
|
17211
|
+
method: "POST",
|
|
17212
|
+
headers: requestHeaders,
|
|
17213
|
+
body,
|
|
17214
|
+
signal: controller.signal
|
|
17215
|
+
},
|
|
17216
|
+
{ allowLoopback: process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1" }
|
|
17217
|
+
);
|
|
17218
|
+
if (!fetchResult.ok) {
|
|
17219
|
+
return createErrorResult(fetchResult.reason, Date.now() - startTime, false);
|
|
17220
|
+
}
|
|
17221
|
+
const response = fetchResult.response;
|
|
16779
17222
|
const latencyMs = Date.now() - startTime;
|
|
16780
17223
|
if (!response.ok) {
|
|
16781
17224
|
const responseText2 = await readResponseText(response);
|
|
@@ -16894,12 +17337,20 @@ async function testStreamingEndpoint(baseUrl, provider, path2, model, protocol)
|
|
|
16894
17337
|
const timeoutId = setTimeout(() => controller.abort(), getProviderTestTimeoutMs());
|
|
16895
17338
|
try {
|
|
16896
17339
|
requestUrl = buildUpstreamUrl(baseUrl, path2);
|
|
16897
|
-
const
|
|
16898
|
-
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
17340
|
+
const fetchResult = await safeFetch(
|
|
17341
|
+
requestUrl,
|
|
17342
|
+
{
|
|
17343
|
+
method: "POST",
|
|
17344
|
+
headers: requestHeaders,
|
|
17345
|
+
body,
|
|
17346
|
+
signal: controller.signal
|
|
17347
|
+
},
|
|
17348
|
+
{ allowLoopback: process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1" }
|
|
17349
|
+
);
|
|
17350
|
+
if (!fetchResult.ok) {
|
|
17351
|
+
return createErrorResult(fetchResult.reason, Date.now() - startTime, true);
|
|
17352
|
+
}
|
|
17353
|
+
const response = fetchResult.response;
|
|
16903
17354
|
const latencyMs = Date.now() - startTime;
|
|
16904
17355
|
if (!response.ok) {
|
|
16905
17356
|
const responseText = await readResponseText(response);
|
|
@@ -17281,7 +17732,16 @@ const Route$f = createFileRoute("/api/providers/$providerId/model-metadata")({
|
|
|
17281
17732
|
}
|
|
17282
17733
|
let response;
|
|
17283
17734
|
try {
|
|
17284
|
-
|
|
17735
|
+
const fetchResult = await safeFetch(sourceUrl, {
|
|
17736
|
+
headers: { Accept: "application/json" }
|
|
17737
|
+
});
|
|
17738
|
+
if (!fetchResult.ok) {
|
|
17739
|
+
return Response.json(
|
|
17740
|
+
{ error: `Model metadata URL blocked: ${fetchResult.reason}` },
|
|
17741
|
+
{ status: 400 }
|
|
17742
|
+
);
|
|
17743
|
+
}
|
|
17744
|
+
response = fetchResult.response;
|
|
17285
17745
|
} catch (err) {
|
|
17286
17746
|
return Response.json(
|
|
17287
17747
|
{ error: `Failed to fetch model metadata: ${String(err)}` },
|
|
@@ -17393,11 +17853,22 @@ const Route$e = createFileRoute("/api/logs/$id/replay")({
|
|
|
17393
17853
|
const startTime = Date.now();
|
|
17394
17854
|
let upstreamRes;
|
|
17395
17855
|
try {
|
|
17396
|
-
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
|
|
17400
|
-
|
|
17856
|
+
const fetchResult = await safeFetch(
|
|
17857
|
+
upstreamUrl,
|
|
17858
|
+
{
|
|
17859
|
+
method: "POST",
|
|
17860
|
+
headers,
|
|
17861
|
+
body: modifiedBody
|
|
17862
|
+
},
|
|
17863
|
+
{ allowLoopback: process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1" }
|
|
17864
|
+
);
|
|
17865
|
+
if (!fetchResult.ok) {
|
|
17866
|
+
return Response.json({
|
|
17867
|
+
success: false,
|
|
17868
|
+
error: fetchResult.reason
|
|
17869
|
+
});
|
|
17870
|
+
}
|
|
17871
|
+
upstreamRes = fetchResult.response;
|
|
17401
17872
|
} catch (err) {
|
|
17402
17873
|
return Response.json({
|
|
17403
17874
|
success: false,
|
|
@@ -18778,13 +19249,13 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
|
|
|
18778
19249
|
export {
|
|
18779
19250
|
PiAgentResponseSchema as $,
|
|
18780
19251
|
AnthropicResponseSchema$1 as A,
|
|
18781
|
-
|
|
19252
|
+
DeleteInspectorGroupsResponseSchema as B,
|
|
18782
19253
|
CapturedLogSchema as C,
|
|
18783
19254
|
DEFAULT_CAPTURE_MODE as D,
|
|
18784
|
-
|
|
18785
|
-
|
|
19255
|
+
GroupEvidenceExportResultSchema as E,
|
|
19256
|
+
DeleteInspectorGroupResponseSchema as F,
|
|
18786
19257
|
GroupEvidenceReadResponseSchema as G,
|
|
18787
|
-
|
|
19258
|
+
providerHasContextMetadata as H,
|
|
18788
19259
|
InspectorGroupsListResponseSchema as I,
|
|
18789
19260
|
findProviderModelMetadata as J,
|
|
18790
19261
|
KnowledgeCandidateSchema as K,
|
|
@@ -18817,22 +19288,22 @@ export {
|
|
|
18817
19288
|
persistThemeMode as f,
|
|
18818
19289
|
readStoredUiScalePreference as g,
|
|
18819
19290
|
persistUiScalePreference as h,
|
|
18820
|
-
|
|
18821
|
-
|
|
18822
|
-
|
|
18823
|
-
|
|
18824
|
-
|
|
19291
|
+
isRecord as i,
|
|
19292
|
+
applyUiScalePreference as j,
|
|
19293
|
+
normalizeUiScalePreference as k,
|
|
19294
|
+
AnthropicRequestSchema as l,
|
|
19295
|
+
parseOpenAIResponse as m,
|
|
18825
19296
|
normalizeThemeMode as n,
|
|
18826
|
-
|
|
19297
|
+
parseOpenAIResponsesResponse as o,
|
|
18827
19298
|
packageJson as p,
|
|
18828
|
-
|
|
19299
|
+
OpenAIResponsesRequestSchema as q,
|
|
18829
19300
|
readStoredThemeMode as r,
|
|
18830
19301
|
safeGetOwnProperty as s,
|
|
18831
|
-
|
|
18832
|
-
|
|
18833
|
-
|
|
18834
|
-
|
|
18835
|
-
|
|
18836
|
-
|
|
18837
|
-
|
|
19302
|
+
apiFormatForPath as t,
|
|
19303
|
+
getSessionPath as u,
|
|
19304
|
+
stripClaudeCodeBillingHeader as v,
|
|
19305
|
+
isTurnBoundary as w,
|
|
19306
|
+
extractStopReason as x,
|
|
19307
|
+
AlertSummarySchema as y,
|
|
19308
|
+
AlertListResponseSchema as z
|
|
18838
19309
|
};
|