@tonyclaw/agent-inspector 3.1.20 → 3.1.22
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/backend/nitro.json +1 -1
- package/.output/cli.js +137 -20
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-DpXchCXm.mjs → router-DKFaWvAr.mjs} +2371 -1171
- package/.output/server/_tanstack-start-manifest_v-ClTaMkEj.mjs +4 -0
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-4i1QHaQf.js → CompareDrawer-pPcsyvc6.js} +1 -1
- package/.output/ui/assets/{InspectorPet-Cw6VJLr7.js → InspectorPet-DR3JEqze.js} +1 -1
- package/.output/ui/assets/ProxyViewerContainer-DOyYRnb-.js +60 -0
- package/.output/ui/assets/{ReplayDialog-DTs575iW.js → ReplayDialog-Z-1-gUDm.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-Cl6Es3gv.js → RequestAnatomy-BsnZjE5G.js} +1 -1
- package/.output/ui/assets/{ResponseView-DNCCLkzG.js → ResponseView-HY64_ZY2.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-DKaxRL9U.js → StreamingChunkSequence-CfQUFbTA.js} +1 -1
- package/.output/ui/assets/{_sessionId-rkR3WgRq.js → _sessionId-Dq97lD86.js} +1 -1
- package/.output/ui/assets/{_sessionId-C-YjWz50.js → _sessionId-_F9wwa3a.js} +1 -1
- package/.output/ui/assets/index-B-Qod-aA.css +1 -0
- package/.output/ui/assets/{index-DxiecWHw.js → index-B2yLwOEu.js} +1 -1
- package/.output/ui/assets/{index-D0d-V67x.js → index-CN-gZDqV.js} +1 -1
- package/.output/ui/assets/{index-DYbzZTDM.js → index-DOAGzrq2.js} +1 -1
- package/.output/ui/assets/{index-B48RD1-5.js → index-DYnqmBBx.js} +2 -2
- package/.output/ui/assets/{json-viewer-CjAwZhHA.js → json-viewer-B2SQN0Lg.js} +1 -1
- package/.output/ui/assets/{jszip.min-DEGEN1HP.js → jszip.min-Dpks67u2.js} +1 -1
- package/.output/ui/index.html +2 -2
- package/.output/workers/logFinalizer.worker.js +258 -0
- package/.output/workers/sessionWorkerEntry.js +258 -0
- package/package.json +13 -6
- package/src/backend/routes/api/logs.$id.replay.ts +6 -1
- package/src/backend/routes/api/logs.stream.ts +7 -0
- package/src/backend/routes/api/providers.$providerId.model-metadata.ts +15 -4
- package/src/backend/routes/api/storage.ts +56 -0
- package/src/backend/routes/metrics.ts +14 -0
- package/src/cli/doctor.ts +135 -2
- package/src/cli.ts +7 -4
- package/src/components/ProxyViewerContainer.tsx +41 -24
- package/src/components/providers/SettingsDialog.tsx +261 -70
- package/src/components/proxy-viewer/LogEntry.tsx +19 -5
- package/src/components/proxy-viewer/bodyHydration.ts +67 -0
- package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +78 -0
- package/src/knowledge/openclawClient.ts +11 -2
- package/src/knowledge/openclawGatewayClient.ts +6 -1
- package/src/lib/resourceLimits.ts +191 -0
- package/src/lib/safeFetch.ts +428 -20
- package/src/proxy/ecosystemTasks.ts +20 -0
- package/src/proxy/handler.ts +17 -4
- package/src/proxy/identityProxy.ts +36 -16
- package/src/proxy/logIndex.ts +38 -6
- package/src/proxy/logger.ts +4 -0
- package/src/proxy/rawStreamCapture.ts +16 -1
- package/src/proxy/runtimeHealth.ts +4 -2
- package/src/proxy/runtimeMetrics.ts +149 -0
- package/src/proxy/sessionRuntime.ts +38 -0
- package/src/proxy/sqliteLogIndex.ts +51 -0
- package/src/proxy/storageLifecycle.ts +432 -0
- package/src/proxy/store.ts +62 -12
- package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +0 -4
- package/.output/ui/assets/ProxyViewerContainer-BST0B7lY.js +0 -60
- package/.output/ui/assets/index-IBEDJoV_.css +0 -1
|
@@ -2,19 +2,21 @@ import { c as createRouter, a as createRootRoute, b as createFileRoute } from ".
|
|
|
2
2
|
import { h as getRouterBasePath, r as runWithRequestPublicBasePath, g as getPublicBasePath, b as appendBasePathToOrigin, c as getPublicOrigin, w as withBasePath, s as stripBasePath, a as getConfiguredBasePath, n as normalizePublicBasePath, f as AGENT_INSPECTOR_BASE_PATH_ENV, d as getPublicPort, A as AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER, e as AGENT_INSPECTOR_PUBLIC_PORT_HEADER } from "./publicBasePathContext-IH2ilOfu.mjs";
|
|
3
3
|
import { randomUUID, timingSafeEqual, createHash, createDecipheriv, randomBytes, createCipheriv } from "node:crypto";
|
|
4
4
|
import fs, { readdir, open, readFile, unlink, appendFile, writeFile, rename, stat, lstat, link, rm, statfs } from "node:fs/promises";
|
|
5
|
-
import path, {
|
|
6
|
-
import { existsSync, readFileSync, writeFileSync, renameSync,
|
|
5
|
+
import path, { dirname, join, basename, relative, resolve, isAbsolute, win32, posix, parse, sep } from "node:path";
|
|
6
|
+
import { unlinkSync, existsSync, readFileSync, writeFileSync, renameSync, readdirSync, lstatSync, rmSync, cpSync, statSync, createReadStream, mkdirSync, copyFileSync, constants, realpathSync, openSync, fstatSync, fchmodSync, closeSync, writeSync, fsyncSync, createWriteStream } from "node:fs";
|
|
7
7
|
import { spawn, execFileSync, execFile, spawnSync } from "node:child_process";
|
|
8
8
|
import { Buffer } from "node:buffer";
|
|
9
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
10
|
import { Worker } from "node:worker_threads";
|
|
11
11
|
import { createInterface } from "node:readline";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
12
13
|
import { C as Conf } from "../_libs/conf.mjs";
|
|
13
14
|
import { randomUUID as randomUUID$1 } from "crypto";
|
|
14
15
|
import { promisify } from "node:util";
|
|
15
16
|
import { lookup } from "node:dns/promises";
|
|
17
|
+
import { request as request$1 } from "node:http";
|
|
18
|
+
import { request } from "node:https";
|
|
16
19
|
import { isIP, createServer } from "node:net";
|
|
17
|
-
import assert from "node:assert/strict";
|
|
18
20
|
import { M as McpServer, W as WebStandardStreamableHTTPServerTransport, R as ResourceTemplate } from "../_libs/modelcontextprotocol__server.mjs";
|
|
19
21
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
20
22
|
import { homedir } from "node:os";
|
|
@@ -67,7 +69,7 @@ import "../_libs/lie.mjs";
|
|
|
67
69
|
import "../_libs/immediate.mjs";
|
|
68
70
|
import "../_libs/setimmediate.mjs";
|
|
69
71
|
import "../_libs/pako.mjs";
|
|
70
|
-
const Route$
|
|
72
|
+
const Route$W = createRootRoute();
|
|
71
73
|
const PRIVATE_DIRECTORY_MODE = 448;
|
|
72
74
|
const PRIVATE_FILE_MODE = 384;
|
|
73
75
|
const securedPathFingerprints = /* @__PURE__ */ new Map();
|
|
@@ -1300,8 +1302,8 @@ function canAppendCapturedLogFileOnExit(logDir, filePath) {
|
|
|
1300
1302
|
return false;
|
|
1301
1303
|
}
|
|
1302
1304
|
}
|
|
1303
|
-
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
1304
|
-
const DEFAULT_RETENTION_DAYS = 7;
|
|
1305
|
+
const DAY_MS$1 = 24 * 60 * 60 * 1e3;
|
|
1306
|
+
const DEFAULT_RETENTION_DAYS$1 = 7;
|
|
1305
1307
|
const DEFAULT_APP_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
1306
1308
|
const DEFAULT_APP_LOG_MAX_FILES = 5;
|
|
1307
1309
|
function writeStderrFallback(message, error) {
|
|
@@ -1329,6 +1331,9 @@ function getInspectorLogPath() {
|
|
|
1329
1331
|
}
|
|
1330
1332
|
return path.join(getDataDir(), "logs", "inspector.log");
|
|
1331
1333
|
}
|
|
1334
|
+
function resolveApplicationLogPath() {
|
|
1335
|
+
return getInspectorLogPath();
|
|
1336
|
+
}
|
|
1332
1337
|
function readBoundedInteger(name2, fallback, minimum, maximum) {
|
|
1333
1338
|
const raw = process.env[name2];
|
|
1334
1339
|
if (raw === void 0 || raw === "") return fallback;
|
|
@@ -1337,7 +1342,7 @@ function readBoundedInteger(name2, fallback, minimum, maximum) {
|
|
|
1337
1342
|
return parsed;
|
|
1338
1343
|
}
|
|
1339
1344
|
function getApplicationLogPolicy() {
|
|
1340
|
-
const retentionDays = readBoundedInteger("LOG_RETENTION_DAYS", DEFAULT_RETENTION_DAYS, 1, 3650);
|
|
1345
|
+
const retentionDays = readBoundedInteger("LOG_RETENTION_DAYS", DEFAULT_RETENTION_DAYS$1, 1, 3650);
|
|
1341
1346
|
return {
|
|
1342
1347
|
maxBytes: readBoundedInteger(
|
|
1343
1348
|
"AGENT_INSPECTOR_APP_LOG_MAX_BYTES",
|
|
@@ -1351,7 +1356,7 @@ function getApplicationLogPolicy() {
|
|
|
1351
1356
|
1,
|
|
1352
1357
|
100
|
|
1353
1358
|
),
|
|
1354
|
-
retentionMs: retentionDays * DAY_MS
|
|
1359
|
+
retentionMs: retentionDays * DAY_MS$1
|
|
1355
1360
|
};
|
|
1356
1361
|
}
|
|
1357
1362
|
function getRotatedGeneration(entry, baseName) {
|
|
@@ -3667,6 +3672,143 @@ function readNumber$1(row, name2) {
|
|
|
3667
3672
|
const value = readProperty$2(row, name2);
|
|
3668
3673
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
3669
3674
|
}
|
|
3675
|
+
function readString$2(row, name2) {
|
|
3676
|
+
const value = readProperty$2(row, name2);
|
|
3677
|
+
return typeof value === "string" ? value : null;
|
|
3678
|
+
}
|
|
3679
|
+
function readBoolean(row, name2) {
|
|
3680
|
+
return readNumber$1(row, name2) === 1;
|
|
3681
|
+
}
|
|
3682
|
+
function readApiFormat(row) {
|
|
3683
|
+
const value = readString$2(row, "api_format");
|
|
3684
|
+
switch (value) {
|
|
3685
|
+
case "anthropic":
|
|
3686
|
+
return "anthropic";
|
|
3687
|
+
case "openai":
|
|
3688
|
+
return "openai";
|
|
3689
|
+
case "unknown":
|
|
3690
|
+
case null:
|
|
3691
|
+
return "unknown";
|
|
3692
|
+
default:
|
|
3693
|
+
return "unknown";
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
function readCaptureIncompleteReason(row) {
|
|
3697
|
+
const value = readString$2(row, "capture_incomplete_reason");
|
|
3698
|
+
switch (value) {
|
|
3699
|
+
case "capacity-exceeded":
|
|
3700
|
+
case "pipeline-closed":
|
|
3701
|
+
case "write-failed":
|
|
3702
|
+
case "finalize-failed":
|
|
3703
|
+
case "upstream-stream-error":
|
|
3704
|
+
case "shutdown":
|
|
3705
|
+
return value;
|
|
3706
|
+
case null:
|
|
3707
|
+
default:
|
|
3708
|
+
return null;
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
function readStopReason(row) {
|
|
3712
|
+
const value = readString$2(row, "stop_reason");
|
|
3713
|
+
switch (value) {
|
|
3714
|
+
case "end_turn":
|
|
3715
|
+
case "tool_use":
|
|
3716
|
+
case "stop":
|
|
3717
|
+
case "length":
|
|
3718
|
+
return value;
|
|
3719
|
+
case null:
|
|
3720
|
+
default:
|
|
3721
|
+
return null;
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
function readWarnings(row) {
|
|
3725
|
+
const value = readString$2(row, "warnings_json");
|
|
3726
|
+
if (value === null) return void 0;
|
|
3727
|
+
try {
|
|
3728
|
+
const parsed = JSON.parse(value);
|
|
3729
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
3730
|
+
const warnings = [];
|
|
3731
|
+
for (const item of parsed) {
|
|
3732
|
+
if (typeof item !== "string") return void 0;
|
|
3733
|
+
warnings.push(item);
|
|
3734
|
+
}
|
|
3735
|
+
return warnings;
|
|
3736
|
+
} catch {
|
|
3737
|
+
return void 0;
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
function readToolTraceEvents(row) {
|
|
3741
|
+
const value = readString$2(row, "tool_trace_events_json");
|
|
3742
|
+
if (value === null) return void 0;
|
|
3743
|
+
try {
|
|
3744
|
+
const parsed = JSON.parse(value);
|
|
3745
|
+
const result = ToolTraceEventSchema.array().safeParse(parsed);
|
|
3746
|
+
return result.success ? result.data : void 0;
|
|
3747
|
+
} catch {
|
|
3748
|
+
return void 0;
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
function readRequiredNumber(row, name2) {
|
|
3752
|
+
const value = readNumber$1(row, name2);
|
|
3753
|
+
return value === null || !Number.isInteger(value) ? null : value;
|
|
3754
|
+
}
|
|
3755
|
+
function summaryFromRow(row, id) {
|
|
3756
|
+
return {
|
|
3757
|
+
id,
|
|
3758
|
+
timestamp: readString$2(row, "timestamp") ?? "",
|
|
3759
|
+
method: readString$2(row, "method") ?? "",
|
|
3760
|
+
path: readString$2(row, "path") ?? "",
|
|
3761
|
+
model: readString$2(row, "model"),
|
|
3762
|
+
sessionId: readString$2(row, "session_id"),
|
|
3763
|
+
responseStatus: readNumber$1(row, "response_status"),
|
|
3764
|
+
inputTokens: readNumber$1(row, "input_tokens"),
|
|
3765
|
+
outputTokens: readNumber$1(row, "output_tokens"),
|
|
3766
|
+
cacheCreationInputTokens: readNumber$1(row, "cache_creation_input_tokens"),
|
|
3767
|
+
cacheReadInputTokens: readNumber$1(row, "cache_read_input_tokens"),
|
|
3768
|
+
elapsedMs: readNumber$1(row, "elapsed_ms"),
|
|
3769
|
+
firstChunkMs: readNumber$1(row, "first_chunk_ms"),
|
|
3770
|
+
totalStreamMs: readNumber$1(row, "total_stream_ms"),
|
|
3771
|
+
tokensPerSecond: readNumber$1(row, "tokens_per_second"),
|
|
3772
|
+
streaming: readBoolean(row, "streaming"),
|
|
3773
|
+
userAgent: readString$2(row, "user_agent"),
|
|
3774
|
+
origin: readString$2(row, "origin"),
|
|
3775
|
+
apiFormat: readApiFormat(row),
|
|
3776
|
+
isTest: readBoolean(row, "is_test"),
|
|
3777
|
+
replayOfLogId: readNumber$1(row, "replay_of_log_id"),
|
|
3778
|
+
providerName: readString$2(row, "provider_name"),
|
|
3779
|
+
clientPort: readNumber$1(row, "client_port"),
|
|
3780
|
+
clientPid: readNumber$1(row, "client_pid"),
|
|
3781
|
+
clientCwd: readString$2(row, "client_cwd"),
|
|
3782
|
+
clientProjectFolder: readString$2(row, "client_project_folder"),
|
|
3783
|
+
streamingChunksPath: readString$2(row, "streaming_chunks_path"),
|
|
3784
|
+
rawRequestBodyBytes: readNumber$1(row, "raw_request_body_bytes"),
|
|
3785
|
+
responseTextBytes: readNumber$1(row, "response_text_bytes"),
|
|
3786
|
+
captureIncomplete: readBoolean(row, "capture_incomplete"),
|
|
3787
|
+
droppedChunks: readNumber$1(row, "dropped_chunks") ?? 0,
|
|
3788
|
+
droppedBytes: readNumber$1(row, "dropped_bytes") ?? 0,
|
|
3789
|
+
captureIncompleteReason: readCaptureIncompleteReason(row),
|
|
3790
|
+
warnings: readWarnings(row),
|
|
3791
|
+
stopReason: readStopReason(row),
|
|
3792
|
+
toolTraceEvents: readToolTraceEvents(row),
|
|
3793
|
+
error: readString$2(row, "error")
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3796
|
+
function entryFromRow(row) {
|
|
3797
|
+
const id = readRequiredNumber(row, "id");
|
|
3798
|
+
const file = readString$2(row, "file");
|
|
3799
|
+
const byteOffset = readRequiredNumber(row, "byte_offset");
|
|
3800
|
+
const byteLength = readRequiredNumber(row, "byte_length");
|
|
3801
|
+
if (id === null || file === null || byteOffset === null || byteLength === null) return null;
|
|
3802
|
+
return {
|
|
3803
|
+
id,
|
|
3804
|
+
file,
|
|
3805
|
+
byteOffset,
|
|
3806
|
+
byteLength,
|
|
3807
|
+
sessionId: readString$2(row, "session_id"),
|
|
3808
|
+
model: readString$2(row, "model"),
|
|
3809
|
+
summary: summaryFromRow(row, id)
|
|
3810
|
+
};
|
|
3811
|
+
}
|
|
3670
3812
|
async function getSqliteLogIndexMaxId() {
|
|
3671
3813
|
const state = await getSqliteState();
|
|
3672
3814
|
if (state.status !== "ready") return null;
|
|
@@ -3679,6 +3821,44 @@ async function upsertSqliteLogIndexEntry(entry) {
|
|
|
3679
3821
|
if (state.status !== "ready") return false;
|
|
3680
3822
|
return runPrepared$1(state.db, UPSERT_SQL$1, entryParams(entry));
|
|
3681
3823
|
}
|
|
3824
|
+
function sqliteCursorWhereClause(filters) {
|
|
3825
|
+
const clauses = [];
|
|
3826
|
+
const params = [];
|
|
3827
|
+
if (filters.sessionId !== void 0) {
|
|
3828
|
+
clauses.push("session_id = ?");
|
|
3829
|
+
params.push(filters.sessionId);
|
|
3830
|
+
}
|
|
3831
|
+
if (filters.model !== void 0) {
|
|
3832
|
+
clauses.push("model = ?");
|
|
3833
|
+
params.push(filters.model);
|
|
3834
|
+
}
|
|
3835
|
+
if (filters.clientPid !== void 0) {
|
|
3836
|
+
clauses.push("client_pid = ?");
|
|
3837
|
+
params.push(filters.clientPid);
|
|
3838
|
+
}
|
|
3839
|
+
return {
|
|
3840
|
+
sql: clauses.length === 0 ? "" : ` WHERE ${clauses.join(" AND ")}`,
|
|
3841
|
+
params
|
|
3842
|
+
};
|
|
3843
|
+
}
|
|
3844
|
+
async function listSqliteLogCursorEntries(filters) {
|
|
3845
|
+
const state = await getSqliteState();
|
|
3846
|
+
if (state.status !== "ready") return null;
|
|
3847
|
+
const where = sqliteCursorWhereClause(filters);
|
|
3848
|
+
const rows = allPrepared$1(
|
|
3849
|
+
state.db,
|
|
3850
|
+
`SELECT * FROM log_index${where.sql} ORDER BY id ASC`,
|
|
3851
|
+
where.params
|
|
3852
|
+
);
|
|
3853
|
+
if (rows === null) return null;
|
|
3854
|
+
const entries = [];
|
|
3855
|
+
for (const row of rows) {
|
|
3856
|
+
const entry = entryFromRow(row);
|
|
3857
|
+
if (entry === null || entry.summary === void 0) return null;
|
|
3858
|
+
entries.push(entry);
|
|
3859
|
+
}
|
|
3860
|
+
return entries;
|
|
3861
|
+
}
|
|
3682
3862
|
async function syncSqliteLogIndexEntries(entries) {
|
|
3683
3863
|
const state = await getSqliteState();
|
|
3684
3864
|
if (state.status !== "ready") return false;
|
|
@@ -4177,8 +4357,33 @@ function createEmptyIndex() {
|
|
|
4177
4357
|
maxId: 0
|
|
4178
4358
|
};
|
|
4179
4359
|
}
|
|
4360
|
+
function createIndexFromEntries(entries) {
|
|
4361
|
+
const index = createEmptyIndex();
|
|
4362
|
+
for (const entry of entries) {
|
|
4363
|
+
index.entries[entry.id] = entry;
|
|
4364
|
+
index.maxId = Math.max(index.maxId, entry.id);
|
|
4365
|
+
}
|
|
4366
|
+
return index;
|
|
4367
|
+
}
|
|
4368
|
+
function sqliteProjectionReferencesExistingLedgers(entries) {
|
|
4369
|
+
const logDir = resolveLogDir();
|
|
4370
|
+
for (const entry of entries) {
|
|
4371
|
+
if (!entry.file.endsWith(".jsonl")) return false;
|
|
4372
|
+
if (!existsSync(join(logDir, entry.file))) return false;
|
|
4373
|
+
}
|
|
4374
|
+
return true;
|
|
4375
|
+
}
|
|
4180
4376
|
async function loadIndex() {
|
|
4181
4377
|
if (cachedIndex !== null) return cachedIndex;
|
|
4378
|
+
const sqliteEntries = await listSqliteLogCursorEntries({});
|
|
4379
|
+
if (sqliteEntries !== null) {
|
|
4380
|
+
if (sqliteProjectionReferencesExistingLedgers(sqliteEntries)) {
|
|
4381
|
+
cachedIndex = createIndexFromEntries(sqliteEntries);
|
|
4382
|
+
allocatedMaxId = Math.max(allocatedMaxId, cachedIndex.maxId);
|
|
4383
|
+
return cachedIndex;
|
|
4384
|
+
}
|
|
4385
|
+
logger.warn("[logIndex] SQLite projection references missing ledgers; rebuilding from JSONL");
|
|
4386
|
+
}
|
|
4182
4387
|
return await rebuildIndex();
|
|
4183
4388
|
}
|
|
4184
4389
|
async function saveIndex(index) {
|
|
@@ -4215,8 +4420,10 @@ async function addToIndex(id, file, byteOffset, byteLength, metadata) {
|
|
|
4215
4420
|
if (id > index.maxId) {
|
|
4216
4421
|
index.maxId = id;
|
|
4217
4422
|
}
|
|
4218
|
-
|
|
4219
|
-
|
|
4423
|
+
const sqliteUpdated = await upsertSqliteLogIndexEntry(entry);
|
|
4424
|
+
if (!sqliteUpdated) {
|
|
4425
|
+
scheduleIndexFlush();
|
|
4426
|
+
}
|
|
4220
4427
|
}
|
|
4221
4428
|
async function updateLogIndexEntryMetadata(log) {
|
|
4222
4429
|
const index = await loadIndex();
|
|
@@ -4227,14 +4434,15 @@ async function updateLogIndexEntryMetadata(log) {
|
|
|
4227
4434
|
...createLogIndexEntryMetadata(log)
|
|
4228
4435
|
};
|
|
4229
4436
|
index.entries[log.id] = updated;
|
|
4230
|
-
await upsertSqliteLogIndexEntry(updated);
|
|
4231
|
-
scheduleIndexFlush();
|
|
4437
|
+
const sqliteUpdated = await upsertSqliteLogIndexEntry(updated);
|
|
4438
|
+
if (!sqliteUpdated) scheduleIndexFlush();
|
|
4232
4439
|
}
|
|
4233
4440
|
let indexFlushScheduled = false;
|
|
4234
4441
|
let indexFlushTimeout = null;
|
|
4235
4442
|
async function flushIndexAsync() {
|
|
4236
4443
|
indexFlushScheduled = false;
|
|
4237
4444
|
indexFlushTimeout = null;
|
|
4445
|
+
if (await listSqliteLogCursorEntries({}) !== null) return;
|
|
4238
4446
|
const index = await loadIndex();
|
|
4239
4447
|
await saveIndex(index);
|
|
4240
4448
|
}
|
|
@@ -4251,6 +4459,7 @@ async function flushIndex() {
|
|
|
4251
4459
|
indexFlushTimeout = null;
|
|
4252
4460
|
}
|
|
4253
4461
|
indexFlushScheduled = false;
|
|
4462
|
+
if (await listSqliteLogCursorEntries({}) !== null) return;
|
|
4254
4463
|
const index = await loadIndex();
|
|
4255
4464
|
await saveIndex(index);
|
|
4256
4465
|
}
|
|
@@ -5001,135 +5210,917 @@ function getSessionIds() {
|
|
|
5001
5210
|
function getSessionSnapshots() {
|
|
5002
5211
|
return [...sessions$1.values()].map((session) => ({ ...session }));
|
|
5003
5212
|
}
|
|
5004
|
-
const
|
|
5005
|
-
const
|
|
5006
|
-
const
|
|
5007
|
-
const
|
|
5008
|
-
|
|
5009
|
-
const
|
|
5010
|
-
const
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
})
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
}
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
}
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
}
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5213
|
+
const MAX_REST_LOG_PAGE_SIZE = 200;
|
|
5214
|
+
const MAX_SEARCH_QUERY_CHARS = 512;
|
|
5215
|
+
const MAX_SEARCH_PROJECT_CHARS = 256;
|
|
5216
|
+
const MAX_PROVIDER_MODELS = 64;
|
|
5217
|
+
const MAX_PROVIDER_MODEL_NAME_CHARS = 256;
|
|
5218
|
+
const MAX_PROVIDER_MODEL_METADATA = 64;
|
|
5219
|
+
const MAX_SSE_CLIENTS = 32;
|
|
5220
|
+
const MAX_ACTIVE_PROVIDER_TESTS = 2;
|
|
5221
|
+
const MAX_PROVIDER_TEST_CONCURRENCY = 4;
|
|
5222
|
+
const MAX_ACTIVE_PROXY_REQUESTS = 16;
|
|
5223
|
+
const positiveInteger = number().int().positive();
|
|
5224
|
+
const RuntimeBudgetPolicySchema = object({
|
|
5225
|
+
proxy: object({
|
|
5226
|
+
requestBytes: positiveInteger,
|
|
5227
|
+
responseBytes: positiveInteger,
|
|
5228
|
+
headerBytes: positiveInteger,
|
|
5229
|
+
deadlineMs: positiveInteger,
|
|
5230
|
+
idleTimeoutMs: positiveInteger,
|
|
5231
|
+
activeRequests: positiveInteger
|
|
5232
|
+
}),
|
|
5233
|
+
control: object({
|
|
5234
|
+
requestBytes: positiveInteger,
|
|
5235
|
+
responseBytes: positiveInteger,
|
|
5236
|
+
headerBytes: positiveInteger,
|
|
5237
|
+
deadlineMs: positiveInteger,
|
|
5238
|
+
idleTimeoutMs: positiveInteger,
|
|
5239
|
+
activeRequests: positiveInteger
|
|
5240
|
+
}),
|
|
5241
|
+
upstream: object({
|
|
5242
|
+
fetchResponseBytes: positiveInteger,
|
|
5243
|
+
fetchDeadlineMs: positiveInteger,
|
|
5244
|
+
replayResponseBytes: positiveInteger,
|
|
5245
|
+
replayDeadlineMs: positiveInteger,
|
|
5246
|
+
metadataResponseBytes: positiveInteger,
|
|
5247
|
+
metadataDeadlineMs: positiveInteger,
|
|
5248
|
+
connectTimeoutMs: positiveInteger,
|
|
5249
|
+
idleTimeoutMs: positiveInteger
|
|
5250
|
+
}),
|
|
5251
|
+
pagination: object({
|
|
5252
|
+
restPageSize: positiveInteger,
|
|
5253
|
+
searchQueryChars: positiveInteger,
|
|
5254
|
+
searchProjectChars: positiveInteger
|
|
5255
|
+
}),
|
|
5256
|
+
sse: object({
|
|
5257
|
+
clients: positiveInteger,
|
|
5258
|
+
initLogs: positiveInteger,
|
|
5259
|
+
heartbeatMs: positiveInteger
|
|
5260
|
+
}),
|
|
5261
|
+
imports: object({
|
|
5262
|
+
archiveBytes: positiveInteger,
|
|
5263
|
+
entries: positiveInteger,
|
|
5264
|
+
entryBytes: positiveInteger,
|
|
5265
|
+
uncompressedBytes: positiveInteger,
|
|
5266
|
+
expansionRatio: positiveInteger
|
|
5267
|
+
}),
|
|
5268
|
+
fanOut: object({
|
|
5269
|
+
providerModels: positiveInteger,
|
|
5270
|
+
providerModelMetadata: positiveInteger,
|
|
5271
|
+
providerTestConcurrency: positiveInteger,
|
|
5272
|
+
activeProviderTests: positiveInteger
|
|
5273
|
+
}),
|
|
5274
|
+
queues: object({
|
|
5275
|
+
proxyRequests: positiveInteger,
|
|
5276
|
+
finalizerJobs: positiveInteger,
|
|
5277
|
+
rawStreamMessages: positiveInteger,
|
|
5278
|
+
rawStreamBytes: positiveInteger,
|
|
5279
|
+
backgroundWorkers: positiveInteger
|
|
5280
|
+
})
|
|
5281
|
+
}).superRefine((policy, context) => {
|
|
5282
|
+
if (policy.control.requestBytes > policy.proxy.requestBytes) {
|
|
5283
|
+
context.addIssue({
|
|
5284
|
+
code: "custom",
|
|
5285
|
+
path: ["control", "requestBytes"],
|
|
5286
|
+
message: "control request budget must not exceed proxy request budget"
|
|
5287
|
+
});
|
|
5072
5288
|
}
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
index.entriesById.delete(id);
|
|
5080
|
-
}
|
|
5081
|
-
index.ids = index.ids.filter((id) => !ids.has(id));
|
|
5289
|
+
if (policy.control.responseBytes > policy.proxy.responseBytes) {
|
|
5290
|
+
context.addIssue({
|
|
5291
|
+
code: "custom",
|
|
5292
|
+
path: ["control", "responseBytes"],
|
|
5293
|
+
message: "control response budget must not exceed proxy response budget"
|
|
5294
|
+
});
|
|
5082
5295
|
}
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
const oldestKey = memoryCache.keys().next().value;
|
|
5090
|
-
if (oldestKey === void 0) break;
|
|
5091
|
-
memoryCache.delete(oldestKey);
|
|
5296
|
+
if (policy.control.activeRequests > policy.proxy.activeRequests) {
|
|
5297
|
+
context.addIssue({
|
|
5298
|
+
code: "custom",
|
|
5299
|
+
path: ["control", "activeRequests"],
|
|
5300
|
+
message: "control active request budget must not exceed proxy active request budget"
|
|
5301
|
+
});
|
|
5092
5302
|
}
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
const droppedChunks = log.droppedChunks ?? 0;
|
|
5100
|
-
const droppedBytes = log.droppedBytes ?? 0;
|
|
5101
|
-
const captureIncompleteReason = log.captureIncompleteReason ?? null;
|
|
5102
|
-
const stopReason = log.stopReason ?? extractStopReason(log);
|
|
5103
|
-
if (sessionId === log.sessionId && apiFormat === log.apiFormat && log.captureIncomplete !== void 0 && log.droppedChunks !== void 0 && log.droppedBytes !== void 0 && log.captureIncompleteReason !== void 0 && log.stopReason !== void 0) {
|
|
5104
|
-
return log;
|
|
5303
|
+
if (policy.upstream.fetchResponseBytes > policy.proxy.responseBytes) {
|
|
5304
|
+
context.addIssue({
|
|
5305
|
+
code: "custom",
|
|
5306
|
+
path: ["upstream", "fetchResponseBytes"],
|
|
5307
|
+
message: "upstream fetch response budget must not exceed proxy response budget"
|
|
5308
|
+
});
|
|
5105
5309
|
}
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
droppedBytes,
|
|
5113
|
-
captureIncompleteReason,
|
|
5114
|
-
stopReason
|
|
5115
|
-
};
|
|
5116
|
-
}
|
|
5117
|
-
function addToCache(log) {
|
|
5118
|
-
const cachedLog = normalizeLog(log);
|
|
5119
|
-
if (memoryCache.has(cachedLog.id)) {
|
|
5120
|
-
memoryCache.delete(cachedLog.id);
|
|
5310
|
+
if (policy.upstream.replayResponseBytes > policy.proxy.responseBytes) {
|
|
5311
|
+
context.addIssue({
|
|
5312
|
+
code: "custom",
|
|
5313
|
+
path: ["upstream", "replayResponseBytes"],
|
|
5314
|
+
message: "upstream replay response budget must not exceed proxy response budget"
|
|
5315
|
+
});
|
|
5121
5316
|
}
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5317
|
+
if (policy.upstream.metadataResponseBytes > policy.control.responseBytes) {
|
|
5318
|
+
context.addIssue({
|
|
5319
|
+
code: "custom",
|
|
5320
|
+
path: ["upstream", "metadataResponseBytes"],
|
|
5321
|
+
message: "upstream metadata response budget must not exceed control response budget"
|
|
5322
|
+
});
|
|
5323
|
+
}
|
|
5324
|
+
if (policy.upstream.metadataDeadlineMs > policy.control.deadlineMs) {
|
|
5325
|
+
context.addIssue({
|
|
5326
|
+
code: "custom",
|
|
5327
|
+
path: ["upstream", "metadataDeadlineMs"],
|
|
5328
|
+
message: "upstream metadata deadline must not exceed control deadline"
|
|
5329
|
+
});
|
|
5330
|
+
}
|
|
5331
|
+
if (policy.queues.proxyRequests < policy.proxy.activeRequests) {
|
|
5332
|
+
context.addIssue({
|
|
5333
|
+
code: "custom",
|
|
5334
|
+
path: ["queues", "proxyRequests"],
|
|
5335
|
+
message: "proxy request queue budget must cover active proxy requests"
|
|
5336
|
+
});
|
|
5337
|
+
}
|
|
5338
|
+
});
|
|
5339
|
+
const DEFAULT_RUNTIME_BUDGET_POLICY = RuntimeBudgetPolicySchema.parse({
|
|
5340
|
+
proxy: {
|
|
5341
|
+
requestBytes: 64 * 1024 * 1024,
|
|
5342
|
+
responseBytes: 256 * 1024 * 1024,
|
|
5343
|
+
headerBytes: 64 * 1024,
|
|
5344
|
+
deadlineMs: 12e4,
|
|
5345
|
+
idleTimeoutMs: 3e4,
|
|
5346
|
+
activeRequests: MAX_ACTIVE_PROXY_REQUESTS
|
|
5347
|
+
},
|
|
5348
|
+
control: {
|
|
5349
|
+
requestBytes: 8 * 1024 * 1024,
|
|
5350
|
+
responseBytes: 64 * 1024 * 1024,
|
|
5351
|
+
headerBytes: 32 * 1024,
|
|
5352
|
+
deadlineMs: 3e4,
|
|
5353
|
+
idleTimeoutMs: 1e4,
|
|
5354
|
+
activeRequests: 8
|
|
5355
|
+
},
|
|
5356
|
+
upstream: {
|
|
5357
|
+
fetchResponseBytes: 256 * 1024 * 1024,
|
|
5358
|
+
fetchDeadlineMs: 12e4,
|
|
5359
|
+
replayResponseBytes: 256 * 1024 * 1024,
|
|
5360
|
+
replayDeadlineMs: 12e4,
|
|
5361
|
+
metadataResponseBytes: 10 * 1024 * 1024,
|
|
5362
|
+
metadataDeadlineMs: 3e4,
|
|
5363
|
+
connectTimeoutMs: 1e4,
|
|
5364
|
+
idleTimeoutMs: 3e4
|
|
5365
|
+
},
|
|
5366
|
+
pagination: {
|
|
5367
|
+
restPageSize: MAX_REST_LOG_PAGE_SIZE,
|
|
5368
|
+
searchQueryChars: MAX_SEARCH_QUERY_CHARS,
|
|
5369
|
+
searchProjectChars: MAX_SEARCH_PROJECT_CHARS
|
|
5370
|
+
},
|
|
5371
|
+
sse: {
|
|
5372
|
+
clients: MAX_SSE_CLIENTS,
|
|
5373
|
+
initLogs: 500,
|
|
5374
|
+
heartbeatMs: 15e3
|
|
5375
|
+
},
|
|
5376
|
+
imports: {
|
|
5377
|
+
archiveBytes: 64 * 1024 * 1024,
|
|
5378
|
+
entries: 5e3,
|
|
5379
|
+
entryBytes: 32 * 1024 * 1024,
|
|
5380
|
+
uncompressedBytes: 256 * 1024 * 1024,
|
|
5381
|
+
expansionRatio: 20
|
|
5382
|
+
},
|
|
5383
|
+
fanOut: {
|
|
5384
|
+
providerModels: MAX_PROVIDER_MODELS,
|
|
5385
|
+
providerModelMetadata: MAX_PROVIDER_MODEL_METADATA,
|
|
5386
|
+
providerTestConcurrency: MAX_PROVIDER_TEST_CONCURRENCY,
|
|
5387
|
+
activeProviderTests: MAX_ACTIVE_PROVIDER_TESTS
|
|
5388
|
+
},
|
|
5389
|
+
queues: {
|
|
5390
|
+
proxyRequests: MAX_ACTIVE_PROXY_REQUESTS,
|
|
5391
|
+
finalizerJobs: 1e3,
|
|
5392
|
+
rawStreamMessages: 16384,
|
|
5393
|
+
rawStreamBytes: 64 * 1024 * 1024,
|
|
5394
|
+
backgroundWorkers: 4
|
|
5395
|
+
}
|
|
5396
|
+
});
|
|
5397
|
+
const ResourceLimitErrorCodeSchema = _enum([
|
|
5398
|
+
"invalid-page-request",
|
|
5399
|
+
"search-limit-exceeded",
|
|
5400
|
+
"provider-model-limit-exceeded",
|
|
5401
|
+
"sse-capacity-exceeded",
|
|
5402
|
+
"provider-test-capacity-exceeded",
|
|
5403
|
+
"proxy-capacity-exceeded"
|
|
5404
|
+
]);
|
|
5405
|
+
const ResourceLimitStatusSchema = union([
|
|
5406
|
+
literal(400),
|
|
5407
|
+
literal(413),
|
|
5408
|
+
literal(429),
|
|
5409
|
+
literal(503)
|
|
5410
|
+
]);
|
|
5411
|
+
const ResourceLimitErrorSchema = object({
|
|
5412
|
+
error: string().min(1).max(300),
|
|
5413
|
+
code: ResourceLimitErrorCodeSchema,
|
|
5414
|
+
status: ResourceLimitStatusSchema,
|
|
5415
|
+
resource: string().min(1).max(80),
|
|
5416
|
+
limit: number().int().positive(),
|
|
5417
|
+
retryable: boolean()
|
|
5418
|
+
});
|
|
5419
|
+
function createResourceLimitError(input) {
|
|
5420
|
+
return ResourceLimitErrorSchema.parse(input);
|
|
5421
|
+
}
|
|
5422
|
+
function resourceLimitResponse(input) {
|
|
5423
|
+
const body = createResourceLimitError(input);
|
|
5424
|
+
const headers = new Headers({ "cache-control": "no-store" });
|
|
5425
|
+
if (input.retryAfterSeconds !== void 0) {
|
|
5426
|
+
headers.set("retry-after", String(input.retryAfterSeconds));
|
|
5427
|
+
}
|
|
5428
|
+
return Response.json(body, { status: input.status, headers });
|
|
5429
|
+
}
|
|
5430
|
+
function parseBoundedIntegerParameter(input) {
|
|
5431
|
+
if (input.value === null) return { ok: true, value: input.fallback };
|
|
5432
|
+
const parsed = Number(input.value);
|
|
5433
|
+
if (!Number.isSafeInteger(parsed) || parsed < input.minimum || parsed > input.maximum) {
|
|
5434
|
+
return {
|
|
5435
|
+
ok: false,
|
|
5436
|
+
response: resourceLimitResponse({
|
|
5437
|
+
error: `Query parameter "${input.parameter}" must be an integer between ${String(input.minimum)} and ${String(input.maximum)}.`,
|
|
5438
|
+
code: "invalid-page-request",
|
|
5439
|
+
status: 400,
|
|
5440
|
+
resource: input.resource,
|
|
5441
|
+
limit: input.maximum,
|
|
5442
|
+
retryable: false
|
|
5443
|
+
})
|
|
5444
|
+
};
|
|
5445
|
+
}
|
|
5446
|
+
return { ok: true, value: parsed };
|
|
5447
|
+
}
|
|
5448
|
+
function createCapacityLimiter(maxActive) {
|
|
5449
|
+
const capacity = Number.isSafeInteger(maxActive) && maxActive > 0 ? maxActive : 1;
|
|
5450
|
+
let active = 0;
|
|
5451
|
+
return {
|
|
5452
|
+
capacity,
|
|
5453
|
+
activeCount: () => active,
|
|
5454
|
+
tryAcquire: () => {
|
|
5455
|
+
if (active >= capacity) return null;
|
|
5456
|
+
active += 1;
|
|
5457
|
+
let released = false;
|
|
5458
|
+
return {
|
|
5459
|
+
release: () => {
|
|
5460
|
+
if (released) return;
|
|
5461
|
+
released = true;
|
|
5462
|
+
active = Math.max(0, active - 1);
|
|
5463
|
+
}
|
|
5464
|
+
};
|
|
5465
|
+
}
|
|
5466
|
+
};
|
|
5467
|
+
}
|
|
5468
|
+
async function runWithConcurrencyLimit(tasks2, maxConcurrent) {
|
|
5469
|
+
if (tasks2.length === 0) return;
|
|
5470
|
+
const concurrency = Number.isSafeInteger(maxConcurrent) && maxConcurrent > 0 ? maxConcurrent : 1;
|
|
5471
|
+
const workerCount = Math.min(concurrency, tasks2.length);
|
|
5472
|
+
let nextIndex = 0;
|
|
5473
|
+
const runWorker = async () => {
|
|
5474
|
+
while (nextIndex < tasks2.length) {
|
|
5475
|
+
const index = nextIndex;
|
|
5476
|
+
nextIndex += 1;
|
|
5477
|
+
const task = tasks2[index];
|
|
5478
|
+
if (task !== void 0) await task();
|
|
5479
|
+
}
|
|
5480
|
+
};
|
|
5481
|
+
await Promise.all(Array.from({ length: workerCount }, runWorker));
|
|
5482
|
+
}
|
|
5483
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
|
5484
|
+
const FILE_WRITE_BATCH_BYTES = 64 * 1024;
|
|
5485
|
+
const DEFAULT_QUEUE_MAX_MESSAGES = 256;
|
|
5486
|
+
const DEFAULT_QUEUE_MAX_BYTES = 8 * 1024 * 1024;
|
|
5487
|
+
const DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
|
|
5488
|
+
const DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
|
|
5489
|
+
const activeCaptures = /* @__PURE__ */ new Set();
|
|
5490
|
+
function resolveMemoryLimitBytes() {
|
|
5491
|
+
const raw = process.env["AGENT_INSPECTOR_STREAM_MEMORY_LIMIT_BYTES"];
|
|
5492
|
+
if (raw === void 0 || raw === "") return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
5493
|
+
const parsed = Number(raw);
|
|
5494
|
+
if (!Number.isInteger(parsed) || parsed < 0) return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
5495
|
+
return parsed;
|
|
5496
|
+
}
|
|
5497
|
+
function resolvePositiveIntegerEnv(name2, fallback) {
|
|
5498
|
+
const raw = process.env[name2];
|
|
5499
|
+
if (raw === void 0 || raw === "") return fallback;
|
|
5500
|
+
const parsed = Number(raw);
|
|
5501
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
|
|
5502
|
+
return parsed;
|
|
5503
|
+
}
|
|
5504
|
+
function defaultQueueMaxMessages() {
|
|
5505
|
+
return resolvePositiveIntegerEnv(
|
|
5506
|
+
"AGENT_INSPECTOR_OBSERVATION_QUEUE_MESSAGES",
|
|
5507
|
+
DEFAULT_QUEUE_MAX_MESSAGES
|
|
5508
|
+
);
|
|
5509
|
+
}
|
|
5510
|
+
function defaultQueueMaxBytes() {
|
|
5511
|
+
return resolvePositiveIntegerEnv(
|
|
5512
|
+
"AGENT_INSPECTOR_OBSERVATION_QUEUE_BYTES",
|
|
5513
|
+
DEFAULT_QUEUE_MAX_BYTES
|
|
5514
|
+
);
|
|
5515
|
+
}
|
|
5516
|
+
class RawStreamObservationBudget {
|
|
5517
|
+
constructor(maxMessages, maxBytes) {
|
|
5518
|
+
this.maxMessages = maxMessages;
|
|
5519
|
+
this.maxBytes = maxBytes;
|
|
5520
|
+
}
|
|
5521
|
+
maxMessages;
|
|
5522
|
+
maxBytes;
|
|
5523
|
+
messages = 0;
|
|
5524
|
+
bytes = 0;
|
|
5525
|
+
tryAcquire(bytes) {
|
|
5526
|
+
if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
|
|
5527
|
+
this.messages += 1;
|
|
5528
|
+
this.bytes += bytes;
|
|
5529
|
+
let released = false;
|
|
5530
|
+
return () => {
|
|
5531
|
+
if (released) return;
|
|
5532
|
+
released = true;
|
|
5533
|
+
this.messages -= 1;
|
|
5534
|
+
this.bytes -= bytes;
|
|
5535
|
+
};
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
const globalObservationBudget = new RawStreamObservationBudget(
|
|
5539
|
+
resolvePositiveIntegerEnv(
|
|
5540
|
+
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_MESSAGES",
|
|
5541
|
+
DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES
|
|
5542
|
+
),
|
|
5543
|
+
resolvePositiveIntegerEnv(
|
|
5544
|
+
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
|
|
5545
|
+
DEFAULT_GLOBAL_QUEUE_MAX_BYTES
|
|
5546
|
+
)
|
|
5547
|
+
);
|
|
5548
|
+
function getRawStreamDir() {
|
|
5549
|
+
return join(getDataDir(), "raw-streams");
|
|
5550
|
+
}
|
|
5551
|
+
function createRawStreamPath() {
|
|
5552
|
+
return join(getRawStreamDir(), `${Date.now()}-${randomUUID()}.stream.tmp`);
|
|
5553
|
+
}
|
|
5554
|
+
class RawStreamCapture {
|
|
5555
|
+
memoryLimitBytes;
|
|
5556
|
+
queueMaxMessages;
|
|
5557
|
+
queueMaxBytes;
|
|
5558
|
+
globalBudget;
|
|
5559
|
+
chunks = [];
|
|
5560
|
+
byteLength = 0;
|
|
5561
|
+
filePath = null;
|
|
5562
|
+
fileHandle = null;
|
|
5563
|
+
pendingFileChunks = [];
|
|
5564
|
+
pendingFileBytes = 0;
|
|
5565
|
+
operation = Promise.resolve();
|
|
5566
|
+
accepting = true;
|
|
5567
|
+
closed = false;
|
|
5568
|
+
sawChunk = false;
|
|
5569
|
+
queuedMessages = 0;
|
|
5570
|
+
queuedBytes = 0;
|
|
5571
|
+
incomplete = false;
|
|
5572
|
+
storageFailed = false;
|
|
5573
|
+
droppedChunks = 0;
|
|
5574
|
+
droppedBytes = 0;
|
|
5575
|
+
gapReason = null;
|
|
5576
|
+
constructor(options = {}) {
|
|
5577
|
+
this.memoryLimitBytes = options.memoryLimitBytes ?? resolveMemoryLimitBytes();
|
|
5578
|
+
this.queueMaxMessages = options.queueMaxMessages ?? defaultQueueMaxMessages();
|
|
5579
|
+
this.queueMaxBytes = options.queueMaxBytes ?? defaultQueueMaxBytes();
|
|
5580
|
+
this.globalBudget = options.globalBudget ?? globalObservationBudget;
|
|
5581
|
+
activeCaptures.add(this);
|
|
5582
|
+
}
|
|
5583
|
+
get hasChunks() {
|
|
5584
|
+
return this.sawChunk;
|
|
5585
|
+
}
|
|
5586
|
+
get status() {
|
|
5587
|
+
return {
|
|
5588
|
+
incomplete: this.incomplete,
|
|
5589
|
+
droppedChunks: this.droppedChunks,
|
|
5590
|
+
droppedBytes: this.droppedBytes,
|
|
5591
|
+
reason: this.gapReason
|
|
5592
|
+
};
|
|
5593
|
+
}
|
|
5594
|
+
recordDroppedObservation(reason, observedBytes) {
|
|
5595
|
+
const safeObservedBytes = Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
5596
|
+
this.recordGap(reason, safeObservedBytes, safeObservedBytes > 0);
|
|
5597
|
+
}
|
|
5598
|
+
async append(text) {
|
|
5599
|
+
if (text === "") return;
|
|
5600
|
+
const next = this.operation.then(() => this.appendInternal(text));
|
|
5601
|
+
this.operation = next.catch(() => void 0);
|
|
5602
|
+
return next;
|
|
5603
|
+
}
|
|
5604
|
+
/**
|
|
5605
|
+
* Submit an observation without waiting for file I/O. Once capacity is exceeded, capture remains
|
|
5606
|
+
* incomplete and all later chunks are counted as dropped so the persisted value is an honest
|
|
5607
|
+
* prefix rather than a misleading stream with an unmarked hole.
|
|
5608
|
+
*/
|
|
5609
|
+
tryAppend(text, observedBytes) {
|
|
5610
|
+
const safeObservedBytes = Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
5611
|
+
const queuedBytes = Math.max(safeObservedBytes, Buffer.byteLength(text, "utf-8"));
|
|
5612
|
+
if (!this.accepting || this.closed) {
|
|
5613
|
+
this.recordGap("pipeline-closed", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
5614
|
+
return false;
|
|
5615
|
+
}
|
|
5616
|
+
if (this.incomplete) {
|
|
5617
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
5618
|
+
return false;
|
|
5619
|
+
}
|
|
5620
|
+
if (text === "") return true;
|
|
5621
|
+
if (this.queuedMessages + 1 > this.queueMaxMessages || this.queuedBytes + queuedBytes > this.queueMaxBytes) {
|
|
5622
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
5623
|
+
return false;
|
|
5624
|
+
}
|
|
5625
|
+
const releaseGlobal = this.globalBudget.tryAcquire(queuedBytes);
|
|
5626
|
+
if (releaseGlobal === null) {
|
|
5627
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
5628
|
+
return false;
|
|
5629
|
+
}
|
|
5630
|
+
this.queuedMessages += 1;
|
|
5631
|
+
this.queuedBytes += queuedBytes;
|
|
5632
|
+
this.operation = this.operation.then(async () => {
|
|
5633
|
+
if (this.storageFailed) {
|
|
5634
|
+
this.recordGap("write-failed", safeObservedBytes, true);
|
|
5635
|
+
return;
|
|
5636
|
+
}
|
|
5637
|
+
await this.appendInternal(text);
|
|
5638
|
+
}).catch(() => {
|
|
5639
|
+
this.storageFailed = true;
|
|
5640
|
+
this.recordGap("write-failed", safeObservedBytes, true);
|
|
5641
|
+
}).finally(() => {
|
|
5642
|
+
this.queuedMessages -= 1;
|
|
5643
|
+
this.queuedBytes -= queuedBytes;
|
|
5644
|
+
releaseGlobal();
|
|
5645
|
+
});
|
|
5646
|
+
return true;
|
|
5647
|
+
}
|
|
5648
|
+
async finalize() {
|
|
5649
|
+
this.accepting = false;
|
|
5650
|
+
const next = this.operation.then(() => this.finalizeInternal());
|
|
5651
|
+
this.operation = next.then(
|
|
5652
|
+
() => void 0,
|
|
5653
|
+
() => void 0
|
|
5654
|
+
);
|
|
5655
|
+
return next.finally(() => activeCaptures.delete(this));
|
|
5656
|
+
}
|
|
5657
|
+
async appendInternal(text) {
|
|
5658
|
+
if (this.closed) return;
|
|
5659
|
+
this.sawChunk = true;
|
|
5660
|
+
const nextBytes = Buffer.byteLength(text, "utf-8");
|
|
5661
|
+
if (this.filePath === null && this.byteLength + nextBytes <= this.memoryLimitBytes) {
|
|
5662
|
+
this.chunks.push(text);
|
|
5663
|
+
this.byteLength += nextBytes;
|
|
5664
|
+
return;
|
|
5665
|
+
}
|
|
5666
|
+
if (this.filePath === null) {
|
|
5667
|
+
await this.spillToFile();
|
|
5668
|
+
}
|
|
5669
|
+
if (this.filePath !== null) {
|
|
5670
|
+
this.pendingFileChunks.push(text);
|
|
5671
|
+
this.pendingFileBytes += nextBytes;
|
|
5672
|
+
this.byteLength += nextBytes;
|
|
5673
|
+
if (this.pendingFileBytes >= FILE_WRITE_BATCH_BYTES) await this.flushFileBuffer();
|
|
5674
|
+
}
|
|
5675
|
+
}
|
|
5676
|
+
recordGap(reason, observedBytes, countChunk) {
|
|
5677
|
+
this.incomplete = true;
|
|
5678
|
+
this.gapReason ??= reason;
|
|
5679
|
+
if (countChunk) this.droppedChunks += 1;
|
|
5680
|
+
this.droppedBytes += observedBytes;
|
|
5681
|
+
}
|
|
5682
|
+
async finalizeInternal() {
|
|
5683
|
+
if (!this.closed) {
|
|
5684
|
+
this.closed = true;
|
|
5685
|
+
await this.flushFileBuffer();
|
|
5686
|
+
if (this.fileHandle !== null) {
|
|
5687
|
+
const handle = this.fileHandle;
|
|
5688
|
+
this.fileHandle = null;
|
|
5689
|
+
try {
|
|
5690
|
+
await handle.sync();
|
|
5691
|
+
} finally {
|
|
5692
|
+
await handle.close();
|
|
5693
|
+
}
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
if (this.filePath !== null) {
|
|
5697
|
+
return { type: "file", path: this.filePath };
|
|
5698
|
+
}
|
|
5699
|
+
return { type: "memory", rawStream: this.chunks.join("") };
|
|
5700
|
+
}
|
|
5701
|
+
async spillToFile() {
|
|
5702
|
+
const path2 = createRawStreamPath();
|
|
5703
|
+
assert(ensurePrivateDirectorySync(getRawStreamDir()), "Could not secure raw-stream directory");
|
|
5704
|
+
const handle = await open(path2, "wx", PRIVATE_FILE_MODE);
|
|
5705
|
+
let committed = false;
|
|
5706
|
+
try {
|
|
5707
|
+
const memoryText = this.chunks.join("");
|
|
5708
|
+
if (memoryText.length > 0) await handle.write(memoryText, void 0, "utf-8");
|
|
5709
|
+
assert(securePrivateFileSync(path2), "Could not secure raw-stream file");
|
|
5710
|
+
this.filePath = path2;
|
|
5711
|
+
this.fileHandle = handle;
|
|
5712
|
+
this.chunks = [];
|
|
5713
|
+
committed = true;
|
|
5714
|
+
} finally {
|
|
5715
|
+
if (!committed) {
|
|
5716
|
+
await handle.close().catch(() => void 0);
|
|
5717
|
+
await unlink(path2).catch(() => void 0);
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
}
|
|
5721
|
+
async flushFileBuffer() {
|
|
5722
|
+
if (this.fileHandle === null || this.pendingFileChunks.length === 0) return;
|
|
5723
|
+
const text = this.pendingFileChunks.join("");
|
|
5724
|
+
this.pendingFileChunks = [];
|
|
5725
|
+
this.pendingFileBytes = 0;
|
|
5726
|
+
await this.fileHandle.write(text, void 0, "utf-8");
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
async function flushAllRawStreamCaptures() {
|
|
5730
|
+
await Promise.all([...activeCaptures].map((capture) => capture.finalize()));
|
|
5731
|
+
}
|
|
5732
|
+
const MAX_INVENTORY_DEPTH = 8;
|
|
5733
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
5734
|
+
const DEFAULT_RETENTION_DAYS = 7;
|
|
5735
|
+
const DEFAULT_MAX_TOTAL_BYTES = 2 * 1024 * 1024 * 1024;
|
|
5736
|
+
let lastStorageLifecycleGcResult = null;
|
|
5737
|
+
function emptyStats() {
|
|
5738
|
+
return {
|
|
5739
|
+
fileCount: 0,
|
|
5740
|
+
bytes: 0,
|
|
5741
|
+
oldestModifiedMs: null,
|
|
5742
|
+
newestModifiedMs: null
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5745
|
+
function recordFile(stats, bytes, modifiedMs) {
|
|
5746
|
+
stats.fileCount += 1;
|
|
5747
|
+
stats.bytes += bytes;
|
|
5748
|
+
stats.oldestModifiedMs = stats.oldestModifiedMs === null ? modifiedMs : Math.min(stats.oldestModifiedMs, modifiedMs);
|
|
5749
|
+
stats.newestModifiedMs = stats.newestModifiedMs === null ? modifiedMs : Math.max(stats.newestModifiedMs, modifiedMs);
|
|
5750
|
+
}
|
|
5751
|
+
function isoFromMs(value) {
|
|
5752
|
+
return value === null ? null : new Date(value).toISOString();
|
|
5753
|
+
}
|
|
5754
|
+
function isPathInsideRoot(root, path2) {
|
|
5755
|
+
const relativePath = relative(resolve(root), resolve(path2));
|
|
5756
|
+
return relativePath.length === 0 || !relativePath.startsWith("..") && !relativePath.startsWith("/") && !relativePath.startsWith("\\");
|
|
5757
|
+
}
|
|
5758
|
+
function collectRuleFiles(rule) {
|
|
5759
|
+
const files = [];
|
|
5760
|
+
if (!existsSync(rule.root)) return files;
|
|
5761
|
+
const visit = (currentDir, depth) => {
|
|
5762
|
+
if (depth > MAX_INVENTORY_DEPTH) return;
|
|
5763
|
+
let entries;
|
|
5764
|
+
try {
|
|
5765
|
+
entries = readdirSync(currentDir);
|
|
5766
|
+
} catch {
|
|
5767
|
+
return;
|
|
5768
|
+
}
|
|
5769
|
+
for (const entry of entries) {
|
|
5770
|
+
const path2 = join(currentDir, entry);
|
|
5771
|
+
if (!isPathInsideRoot(rule.root, path2)) continue;
|
|
5772
|
+
try {
|
|
5773
|
+
const fileStats = lstatSync(path2);
|
|
5774
|
+
if (fileStats.isDirectory()) {
|
|
5775
|
+
visit(path2, depth + 1);
|
|
5776
|
+
continue;
|
|
5777
|
+
}
|
|
5778
|
+
if (!fileStats.isFile() || !rule.shouldCount(entry, path2)) continue;
|
|
5779
|
+
files.push({ rule, path: path2, bytes: fileStats.size, modifiedMs: fileStats.mtimeMs });
|
|
5780
|
+
} catch {
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5784
|
+
visit(rule.root, 0);
|
|
5785
|
+
return files;
|
|
5786
|
+
}
|
|
5787
|
+
function collectRuleStats(rule) {
|
|
5788
|
+
const stats = emptyStats();
|
|
5789
|
+
for (const file of collectRuleFiles(rule)) {
|
|
5790
|
+
recordFile(stats, file.bytes, file.modifiedMs);
|
|
5791
|
+
}
|
|
5792
|
+
return stats;
|
|
5793
|
+
}
|
|
5794
|
+
function isAutoDeletableCleanupBehavior(behavior) {
|
|
5795
|
+
switch (behavior) {
|
|
5796
|
+
case "retention-gc":
|
|
5797
|
+
case "orphan-reclaim":
|
|
5798
|
+
return true;
|
|
5799
|
+
case "rotation":
|
|
5800
|
+
case "manual-owner-delete":
|
|
5801
|
+
return false;
|
|
5802
|
+
}
|
|
5803
|
+
}
|
|
5804
|
+
function summarizeRule(rule) {
|
|
5805
|
+
const stats = collectRuleStats(rule);
|
|
5806
|
+
return {
|
|
5807
|
+
kind: rule.kind,
|
|
5808
|
+
root: rule.root,
|
|
5809
|
+
sensitivity: rule.sensitivity,
|
|
5810
|
+
cleanupBehavior: rule.cleanupBehavior,
|
|
5811
|
+
fileCount: stats.fileCount,
|
|
5812
|
+
bytes: stats.bytes,
|
|
5813
|
+
oldestModifiedAt: isoFromMs(stats.oldestModifiedMs),
|
|
5814
|
+
newestModifiedAt: isoFromMs(stats.newestModifiedMs)
|
|
5815
|
+
};
|
|
5816
|
+
}
|
|
5817
|
+
function isSessionArchiveFile(fileName) {
|
|
5818
|
+
return fileName === "session.sqlite" || fileName.startsWith("session.sqlite-");
|
|
5819
|
+
}
|
|
5820
|
+
function isSqliteLogIndexFile(fileName) {
|
|
5821
|
+
return fileName === "inspector.sqlite" || fileName.startsWith("inspector.sqlite-");
|
|
5822
|
+
}
|
|
5823
|
+
function isBackupFile(fileName) {
|
|
5824
|
+
return fileName.endsWith(".bak") || fileName.endsWith(".backup");
|
|
5825
|
+
}
|
|
5826
|
+
function applicationLogMatcher(logPath) {
|
|
5827
|
+
const directory = dirname(logPath);
|
|
5828
|
+
const baseName = basename(logPath);
|
|
5829
|
+
return (fileName, path2) => {
|
|
5830
|
+
if (dirname(path2) !== directory) return false;
|
|
5831
|
+
if (fileName === baseName) return true;
|
|
5832
|
+
return fileName.startsWith(`${baseName}.`) && /^\d+$/.test(fileName.slice(baseName.length + 1));
|
|
5833
|
+
};
|
|
5834
|
+
}
|
|
5835
|
+
function parsePositiveIntegerEnv(name2, fallback) {
|
|
5836
|
+
const value = process.env[name2];
|
|
5837
|
+
if (value === void 0) return fallback;
|
|
5838
|
+
const parsed = Number.parseInt(value, 10);
|
|
5839
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
|
|
5840
|
+
return parsed;
|
|
5841
|
+
}
|
|
5842
|
+
function resolveStorageLifecycleQuotaPolicy() {
|
|
5843
|
+
const retentionDays = parsePositiveIntegerEnv(
|
|
5844
|
+
"AGENT_INSPECTOR_STORAGE_RETENTION_DAYS",
|
|
5845
|
+
parsePositiveIntegerEnv("LOG_RETENTION_DAYS", DEFAULT_RETENTION_DAYS)
|
|
5846
|
+
);
|
|
5847
|
+
return {
|
|
5848
|
+
maxAgeMs: retentionDays * DAY_MS,
|
|
5849
|
+
maxTotalBytes: parsePositiveIntegerEnv(
|
|
5850
|
+
"AGENT_INSPECTOR_STORAGE_MAX_BYTES",
|
|
5851
|
+
DEFAULT_MAX_TOTAL_BYTES
|
|
5852
|
+
)
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5855
|
+
function getStorageLifecycleInventoryRules() {
|
|
5856
|
+
const applicationLogPath = resolveApplicationLogPath();
|
|
5857
|
+
return [
|
|
5858
|
+
{
|
|
5859
|
+
kind: "captured-log-jsonl",
|
|
5860
|
+
root: resolveLogDir(),
|
|
5861
|
+
sensitivity: "captured-data",
|
|
5862
|
+
cleanupBehavior: "retention-gc",
|
|
5863
|
+
shouldCount: (fileName) => fileName.endsWith(".jsonl")
|
|
5864
|
+
},
|
|
5865
|
+
{
|
|
5866
|
+
kind: "sqlite-log-index",
|
|
5867
|
+
root: resolveLogDir(),
|
|
5868
|
+
sensitivity: "captured-data",
|
|
5869
|
+
cleanupBehavior: "orphan-reclaim",
|
|
5870
|
+
shouldCount: isSqliteLogIndexFile
|
|
5871
|
+
},
|
|
5872
|
+
{
|
|
5873
|
+
kind: "session-archive",
|
|
5874
|
+
root: getSessionArchiveRoot(),
|
|
5875
|
+
sensitivity: "captured-data",
|
|
5876
|
+
cleanupBehavior: "retention-gc",
|
|
5877
|
+
shouldCount: isSessionArchiveFile
|
|
5878
|
+
},
|
|
5879
|
+
{
|
|
5880
|
+
kind: "streaming-chunk",
|
|
5881
|
+
root: getChunksDir(),
|
|
5882
|
+
sensitivity: "captured-data",
|
|
5883
|
+
cleanupBehavior: "retention-gc",
|
|
5884
|
+
shouldCount: (fileName) => fileName.endsWith(".json")
|
|
5885
|
+
},
|
|
5886
|
+
{
|
|
5887
|
+
kind: "raw-stream-temp",
|
|
5888
|
+
root: getRawStreamDir(),
|
|
5889
|
+
sensitivity: "captured-data",
|
|
5890
|
+
cleanupBehavior: "orphan-reclaim",
|
|
5891
|
+
shouldCount: () => true
|
|
5892
|
+
},
|
|
5893
|
+
{
|
|
5894
|
+
kind: "application-log",
|
|
5895
|
+
root: dirname(applicationLogPath),
|
|
5896
|
+
sensitivity: "operational",
|
|
5897
|
+
cleanupBehavior: "rotation",
|
|
5898
|
+
shouldCount: applicationLogMatcher(applicationLogPath)
|
|
5899
|
+
},
|
|
5900
|
+
{
|
|
5901
|
+
kind: "evidence",
|
|
5902
|
+
root: join(getDataDir(), "evidence"),
|
|
5903
|
+
sensitivity: "captured-data",
|
|
5904
|
+
cleanupBehavior: "manual-owner-delete",
|
|
5905
|
+
shouldCount: () => true
|
|
5906
|
+
},
|
|
5907
|
+
{
|
|
5908
|
+
kind: "backup",
|
|
5909
|
+
root: getDataDir(),
|
|
5910
|
+
sensitivity: "secret-adjacent",
|
|
5911
|
+
cleanupBehavior: "orphan-reclaim",
|
|
5912
|
+
shouldCount: isBackupFile
|
|
5913
|
+
}
|
|
5914
|
+
];
|
|
5915
|
+
}
|
|
5916
|
+
function getStorageLifecycleInventory(rules = getStorageLifecycleInventoryRules()) {
|
|
5917
|
+
const artifacts = rules.map(summarizeRule);
|
|
5918
|
+
return {
|
|
5919
|
+
checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5920
|
+
totalFiles: artifacts.reduce((sum, artifact) => sum + artifact.fileCount, 0),
|
|
5921
|
+
totalBytes: artifacts.reduce((sum, artifact) => sum + artifact.bytes, 0),
|
|
5922
|
+
artifacts
|
|
5923
|
+
};
|
|
5924
|
+
}
|
|
5925
|
+
function collectLifecycleFiles(rules = getStorageLifecycleInventoryRules()) {
|
|
5926
|
+
return rules.flatMap((rule) => collectRuleFiles(rule));
|
|
5927
|
+
}
|
|
5928
|
+
function selectGcCandidates(files, policy, nowMs) {
|
|
5929
|
+
const autoDeletableFiles = files.filter(
|
|
5930
|
+
(file) => isAutoDeletableCleanupBehavior(file.rule.cleanupBehavior)
|
|
5931
|
+
);
|
|
5932
|
+
const expiredFiles = autoDeletableFiles.filter(
|
|
5933
|
+
(file) => nowMs - file.modifiedMs > policy.maxAgeMs
|
|
5934
|
+
);
|
|
5935
|
+
const expiredPaths = new Set(expiredFiles.map((file) => file.path));
|
|
5936
|
+
const candidates2 = [...expiredFiles];
|
|
5937
|
+
let totalBytes = files.reduce((sum, file) => sum + file.bytes, 0) - expiredFiles.reduce((sum, file) => sum + file.bytes, 0);
|
|
5938
|
+
if (totalBytes <= policy.maxTotalBytes) return candidates2;
|
|
5939
|
+
const quotaCandidates = autoDeletableFiles.filter((file) => !expiredPaths.has(file.path)).sort((a, b) => a.modifiedMs - b.modifiedMs);
|
|
5940
|
+
for (const file of quotaCandidates) {
|
|
5941
|
+
if (totalBytes <= policy.maxTotalBytes) break;
|
|
5942
|
+
candidates2.push(file);
|
|
5943
|
+
totalBytes -= file.bytes;
|
|
5944
|
+
}
|
|
5945
|
+
return candidates2;
|
|
5946
|
+
}
|
|
5947
|
+
function deleteLifecycleFile(file) {
|
|
5948
|
+
if (!isPathInsideRoot(file.rule.root, file.path)) {
|
|
5949
|
+
return { kind: file.rule.kind, path: file.path, message: "path outside lifecycle root" };
|
|
5950
|
+
}
|
|
5951
|
+
try {
|
|
5952
|
+
unlinkSync(file.path);
|
|
5953
|
+
return null;
|
|
5954
|
+
} catch (err) {
|
|
5955
|
+
if (err instanceof Error && err.message.includes("ENOENT")) return null;
|
|
5956
|
+
return { kind: file.rule.kind, path: file.path, message: String(err) };
|
|
5957
|
+
}
|
|
5958
|
+
}
|
|
5959
|
+
function runStorageLifecycleGc(policy = resolveStorageLifecycleQuotaPolicy(), rules = getStorageLifecycleInventoryRules(), nowMs = Date.now()) {
|
|
5960
|
+
const before = getStorageLifecycleInventory(rules);
|
|
5961
|
+
const files = collectLifecycleFiles(rules);
|
|
5962
|
+
const candidates2 = selectGcCandidates(files, policy, nowMs);
|
|
5963
|
+
const errors = [];
|
|
5964
|
+
let filesDeleted = 0;
|
|
5965
|
+
let bytesDeleted = 0;
|
|
5966
|
+
for (const file of candidates2) {
|
|
5967
|
+
const error = deleteLifecycleFile(file);
|
|
5968
|
+
if (error !== null) {
|
|
5969
|
+
errors.push(error);
|
|
5970
|
+
continue;
|
|
5971
|
+
}
|
|
5972
|
+
filesDeleted += 1;
|
|
5973
|
+
bytesDeleted += file.bytes;
|
|
5974
|
+
}
|
|
5975
|
+
const after = getStorageLifecycleInventory(rules);
|
|
5976
|
+
const autoDeletableBytes = after.artifacts.filter((artifact) => isAutoDeletableCleanupBehavior(artifact.cleanupBehavior)).reduce((sum, artifact) => sum + artifact.bytes, 0);
|
|
5977
|
+
const result = {
|
|
5978
|
+
checkedAt: new Date(nowMs).toISOString(),
|
|
5979
|
+
policy,
|
|
5980
|
+
before,
|
|
5981
|
+
after,
|
|
5982
|
+
filesScanned: files.length,
|
|
5983
|
+
bytesScanned: files.reduce((sum, file) => sum + file.bytes, 0),
|
|
5984
|
+
filesDeleted,
|
|
5985
|
+
bytesDeleted,
|
|
5986
|
+
reclaimableBytes: autoDeletableBytes,
|
|
5987
|
+
errors
|
|
5988
|
+
};
|
|
5989
|
+
lastStorageLifecycleGcResult = result;
|
|
5990
|
+
return result;
|
|
5991
|
+
}
|
|
5992
|
+
function getLastStorageLifecycleGcResult() {
|
|
5993
|
+
return lastStorageLifecycleGcResult;
|
|
5994
|
+
}
|
|
5995
|
+
const MAX_MEMORY_CACHE = 100;
|
|
5996
|
+
const memoryCache = /* @__PURE__ */ new Map();
|
|
5997
|
+
const MAX_CURSOR_PAGE_INDEXES = 16;
|
|
5998
|
+
const logCursorPageIndexes = /* @__PURE__ */ new Map();
|
|
5999
|
+
let sessionArchiveQueue = Promise.resolve();
|
|
6000
|
+
const SESSION_ARCHIVE_BATCH_SIZE = 128;
|
|
6001
|
+
const SESSION_ARCHIVE_FLUSH_DELAY_MS = 10;
|
|
6002
|
+
let sessionArchiveBuffer = [];
|
|
6003
|
+
let sessionArchiveFlushTimeout = null;
|
|
6004
|
+
function drainSessionArchiveBuffer() {
|
|
6005
|
+
const logs = sessionArchiveBuffer;
|
|
6006
|
+
sessionArchiveBuffer = [];
|
|
6007
|
+
return logs;
|
|
6008
|
+
}
|
|
6009
|
+
function queueSessionArchiveFlush() {
|
|
6010
|
+
const logs = drainSessionArchiveBuffer();
|
|
6011
|
+
if (logs.length === 0) return;
|
|
6012
|
+
sessionArchiveQueue = sessionArchiveQueue.catch(() => void 0).then(async () => {
|
|
6013
|
+
await archiveSessionLogs(logs);
|
|
6014
|
+
}).catch((err) => {
|
|
6015
|
+
logger.warn("[store] Failed to archive session logs:", String(err));
|
|
6016
|
+
});
|
|
6017
|
+
}
|
|
6018
|
+
function cancelSessionArchiveFlushTimer() {
|
|
6019
|
+
if (sessionArchiveFlushTimeout === null) return;
|
|
6020
|
+
clearTimeout(sessionArchiveFlushTimeout);
|
|
6021
|
+
sessionArchiveFlushTimeout = null;
|
|
6022
|
+
}
|
|
6023
|
+
function scheduleSessionArchiveFlush() {
|
|
6024
|
+
if (sessionArchiveFlushTimeout !== null) return;
|
|
6025
|
+
sessionArchiveFlushTimeout = setTimeout(() => {
|
|
6026
|
+
sessionArchiveFlushTimeout = null;
|
|
6027
|
+
queueSessionArchiveFlush();
|
|
6028
|
+
}, SESSION_ARCHIVE_FLUSH_DELAY_MS);
|
|
6029
|
+
}
|
|
6030
|
+
function cursorPageIndexKey(options) {
|
|
6031
|
+
return JSON.stringify({
|
|
6032
|
+
sessionId: options.sessionId ?? null,
|
|
6033
|
+
model: options.model ?? null,
|
|
6034
|
+
clientPid: options.clientPid ?? null
|
|
6035
|
+
});
|
|
6036
|
+
}
|
|
6037
|
+
function matchesCursorIndexFilters(log, index) {
|
|
6038
|
+
if (index.sessionId !== void 0 && getLogSessionId(log) !== index.sessionId) return false;
|
|
6039
|
+
if (index.model !== void 0 && log.model !== index.model) return false;
|
|
6040
|
+
if (index.clientPid !== void 0 && log.clientPid !== index.clientPid) return false;
|
|
6041
|
+
return true;
|
|
6042
|
+
}
|
|
6043
|
+
function insertSortedLogId(ids, id) {
|
|
6044
|
+
if (ids.includes(id)) return;
|
|
6045
|
+
const insertionIndex = ids.findIndex((candidate) => candidate > id);
|
|
6046
|
+
if (insertionIndex === -1) {
|
|
6047
|
+
ids.push(id);
|
|
6048
|
+
return;
|
|
6049
|
+
}
|
|
6050
|
+
ids.splice(insertionIndex, 0, id);
|
|
6051
|
+
}
|
|
6052
|
+
function upsertLogCursorPageIndexes(log) {
|
|
6053
|
+
for (const index of logCursorPageIndexes.values()) {
|
|
6054
|
+
if (!matchesCursorIndexFilters(log, index)) {
|
|
6055
|
+
if (index.logsById.delete(log.id)) {
|
|
6056
|
+
index.ids = index.ids.filter((id) => id !== log.id);
|
|
6057
|
+
}
|
|
6058
|
+
index.entriesById.delete(log.id);
|
|
6059
|
+
continue;
|
|
6060
|
+
}
|
|
6061
|
+
index.logsById.set(log.id, log);
|
|
6062
|
+
insertSortedLogId(index.ids, log.id);
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
function removeLogsFromCursorPageIndexes(ids) {
|
|
6066
|
+
if (ids.size === 0) return;
|
|
6067
|
+
for (const index of logCursorPageIndexes.values()) {
|
|
6068
|
+
for (const id of ids) {
|
|
6069
|
+
index.logsById.delete(id);
|
|
6070
|
+
index.entriesById.delete(id);
|
|
6071
|
+
}
|
|
6072
|
+
index.ids = index.ids.filter((id) => !ids.has(id));
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
function clearLogCursorPageIndexes() {
|
|
6076
|
+
logCursorPageIndexes.clear();
|
|
6077
|
+
}
|
|
6078
|
+
function evictOldestIfNeeded() {
|
|
6079
|
+
while (memoryCache.size > MAX_MEMORY_CACHE) {
|
|
6080
|
+
const oldestKey = memoryCache.keys().next().value;
|
|
6081
|
+
if (oldestKey === void 0) break;
|
|
6082
|
+
memoryCache.delete(oldestKey);
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
6085
|
+
function normalizeLog(log) {
|
|
6086
|
+
const sessionId = getLogSessionId(log);
|
|
6087
|
+
const pathFormat = apiFormatForPath(log.path);
|
|
6088
|
+
const apiFormat = pathFormat === "unknown" ? log.apiFormat : pathFormat;
|
|
6089
|
+
const captureIncomplete = log.captureIncomplete ?? false;
|
|
6090
|
+
const droppedChunks = log.droppedChunks ?? 0;
|
|
6091
|
+
const droppedBytes = log.droppedBytes ?? 0;
|
|
6092
|
+
const captureIncompleteReason = log.captureIncompleteReason ?? null;
|
|
6093
|
+
const stopReason = log.stopReason ?? extractStopReason(log);
|
|
6094
|
+
if (sessionId === log.sessionId && apiFormat === log.apiFormat && log.captureIncomplete !== void 0 && log.droppedChunks !== void 0 && log.droppedBytes !== void 0 && log.captureIncompleteReason !== void 0 && log.stopReason !== void 0) {
|
|
6095
|
+
return log;
|
|
6096
|
+
}
|
|
6097
|
+
return {
|
|
6098
|
+
...log,
|
|
6099
|
+
sessionId,
|
|
6100
|
+
apiFormat,
|
|
6101
|
+
captureIncomplete,
|
|
6102
|
+
droppedChunks,
|
|
6103
|
+
droppedBytes,
|
|
6104
|
+
captureIncompleteReason,
|
|
6105
|
+
stopReason
|
|
6106
|
+
};
|
|
6107
|
+
}
|
|
6108
|
+
function addToCache(log) {
|
|
6109
|
+
const cachedLog = normalizeLog(log);
|
|
6110
|
+
if (memoryCache.has(cachedLog.id)) {
|
|
6111
|
+
memoryCache.delete(cachedLog.id);
|
|
6112
|
+
}
|
|
6113
|
+
memoryCache.set(cachedLog.id, cachedLog);
|
|
6114
|
+
upsertLogCursorPageIndexes(cachedLog);
|
|
6115
|
+
evictOldestIfNeeded();
|
|
6116
|
+
}
|
|
6117
|
+
function archiveLogSnapshot(log) {
|
|
6118
|
+
sessionArchiveBuffer.push(normalizeLog(log));
|
|
6119
|
+
if (sessionArchiveBuffer.length >= SESSION_ARCHIVE_BATCH_SIZE) {
|
|
6120
|
+
cancelSessionArchiveFlushTimer();
|
|
6121
|
+
queueSessionArchiveFlush();
|
|
6122
|
+
} else if (sessionArchiveBuffer.length === 1) {
|
|
6123
|
+
scheduleSessionArchiveFlush();
|
|
5133
6124
|
}
|
|
5134
6125
|
}
|
|
5135
6126
|
async function flushSessionArchiveWrites() {
|
|
@@ -5559,17 +6550,42 @@ async function listLogsPage(options) {
|
|
|
5559
6550
|
limit: options.limit
|
|
5560
6551
|
};
|
|
5561
6552
|
}
|
|
5562
|
-
function
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
6553
|
+
function lowerBoundLogId(ids, target) {
|
|
6554
|
+
let low = 0;
|
|
6555
|
+
let high = ids.length;
|
|
6556
|
+
while (low < high) {
|
|
6557
|
+
const middle = Math.floor((low + high) / 2);
|
|
6558
|
+
const id = ids[middle];
|
|
6559
|
+
if (id === void 0 || id >= target) {
|
|
6560
|
+
high = middle;
|
|
6561
|
+
continue;
|
|
6562
|
+
}
|
|
6563
|
+
low = middle + 1;
|
|
6564
|
+
}
|
|
6565
|
+
return low;
|
|
6566
|
+
}
|
|
6567
|
+
function upperBoundLogId(ids, target) {
|
|
6568
|
+
let low = 0;
|
|
6569
|
+
let high = ids.length;
|
|
6570
|
+
while (low < high) {
|
|
6571
|
+
const middle = Math.floor((low + high) / 2);
|
|
6572
|
+
const id = ids[middle];
|
|
6573
|
+
if (id === void 0 || id > target) {
|
|
6574
|
+
high = middle;
|
|
6575
|
+
continue;
|
|
6576
|
+
}
|
|
6577
|
+
low = middle + 1;
|
|
6578
|
+
}
|
|
6579
|
+
return low;
|
|
5566
6580
|
}
|
|
5567
6581
|
function selectCursorPageLogs(ids, options) {
|
|
5568
|
-
const
|
|
6582
|
+
const windowStart = options.afterLogId === void 0 ? 0 : upperBoundLogId(ids, options.afterLogId);
|
|
6583
|
+
const windowEnd = options.beforeLogId === void 0 ? ids.length : lowerBoundLogId(ids, options.beforeLogId);
|
|
6584
|
+
if (windowStart >= windowEnd) return [];
|
|
5569
6585
|
if (options.afterLogId !== void 0 || options.anchor === "oldest") {
|
|
5570
|
-
return
|
|
6586
|
+
return ids.slice(windowStart, Math.min(windowEnd, windowStart + options.limit));
|
|
5571
6587
|
}
|
|
5572
|
-
return
|
|
6588
|
+
return ids.slice(Math.max(windowStart, windowEnd - options.limit), windowEnd);
|
|
5573
6589
|
}
|
|
5574
6590
|
function rememberLogCursorPageIndex(index) {
|
|
5575
6591
|
if (logCursorPageIndexes.has(index.key)) {
|
|
@@ -5601,11 +6617,18 @@ function matchesLogIndexEntryFilters(entry, sessionId, model, clientPid) {
|
|
|
5601
6617
|
return true;
|
|
5602
6618
|
}
|
|
5603
6619
|
async function buildLogCursorPageIndexFromLogIndex(options, allowRebuild) {
|
|
5604
|
-
|
|
5605
|
-
|
|
6620
|
+
let entries = await listSqliteLogCursorEntries(options);
|
|
6621
|
+
if (entries === null) {
|
|
6622
|
+
if (options.clientPid !== void 0) return null;
|
|
6623
|
+
entries = await listFilteredIndexEntries(options);
|
|
6624
|
+
}
|
|
5606
6625
|
if (entries.length === 0 && allowRebuild) {
|
|
5607
6626
|
await rebuildIndex();
|
|
5608
|
-
entries = await
|
|
6627
|
+
entries = await listSqliteLogCursorEntries(options);
|
|
6628
|
+
if (entries === null) {
|
|
6629
|
+
if (options.clientPid !== void 0) return null;
|
|
6630
|
+
entries = await listFilteredIndexEntries(options);
|
|
6631
|
+
}
|
|
5609
6632
|
}
|
|
5610
6633
|
if (entries.some((entry) => !hasLogIndexFilterMetadata(entry))) {
|
|
5611
6634
|
if (!allowRebuild) return null;
|
|
@@ -6420,9 +7443,9 @@ function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
|
6420
7443
|
}
|
|
6421
7444
|
}
|
|
6422
7445
|
}
|
|
6423
|
-
function extractAnthropicToolSchemas(
|
|
7446
|
+
function extractAnthropicToolSchemas(request2) {
|
|
6424
7447
|
const tools = /* @__PURE__ */ new Map();
|
|
6425
|
-
for (const tool of readArray(
|
|
7448
|
+
for (const tool of readArray(request2, "tools")) {
|
|
6426
7449
|
if (!isRecord$2(tool)) continue;
|
|
6427
7450
|
const name2 = readString(tool, "name");
|
|
6428
7451
|
if (name2 === null || name2 === "") continue;
|
|
@@ -6433,9 +7456,9 @@ function extractAnthropicToolSchemas(request) {
|
|
|
6433
7456
|
}
|
|
6434
7457
|
return tools;
|
|
6435
7458
|
}
|
|
6436
|
-
function extractOpenAIToolSchemas(
|
|
7459
|
+
function extractOpenAIToolSchemas(request2) {
|
|
6437
7460
|
const tools = /* @__PURE__ */ new Map();
|
|
6438
|
-
for (const tool of readArray(
|
|
7461
|
+
for (const tool of readArray(request2, "tools")) {
|
|
6439
7462
|
const functionDefinition = readObject(tool, "function");
|
|
6440
7463
|
const name2 = functionDefinition === null ? readString(tool, "name") : readString(functionDefinition, "name") ?? readString(tool, "name");
|
|
6441
7464
|
if (name2 === null || name2 === "") continue;
|
|
@@ -6454,16 +7477,16 @@ function mergeToolSchemas(primary, secondary) {
|
|
|
6454
7477
|
}
|
|
6455
7478
|
return merged;
|
|
6456
7479
|
}
|
|
6457
|
-
function extractToolSchemas(
|
|
7480
|
+
function extractToolSchemas(request2, apiFormat) {
|
|
6458
7481
|
switch (apiFormat) {
|
|
6459
7482
|
case "anthropic":
|
|
6460
|
-
return extractAnthropicToolSchemas(
|
|
7483
|
+
return extractAnthropicToolSchemas(request2);
|
|
6461
7484
|
case "openai":
|
|
6462
|
-
return extractOpenAIToolSchemas(
|
|
7485
|
+
return extractOpenAIToolSchemas(request2);
|
|
6463
7486
|
case "unknown":
|
|
6464
7487
|
return mergeToolSchemas(
|
|
6465
|
-
extractOpenAIToolSchemas(
|
|
6466
|
-
extractAnthropicToolSchemas(
|
|
7488
|
+
extractOpenAIToolSchemas(request2),
|
|
7489
|
+
extractAnthropicToolSchemas(request2)
|
|
6467
7490
|
);
|
|
6468
7491
|
}
|
|
6469
7492
|
}
|
|
@@ -6570,10 +7593,10 @@ function uniqueWarnings(warnings) {
|
|
|
6570
7593
|
return unique2;
|
|
6571
7594
|
}
|
|
6572
7595
|
function analyzeToolSchemaWarnings(input) {
|
|
6573
|
-
const
|
|
7596
|
+
const request2 = parseJson$1(input.rawRequestBody);
|
|
6574
7597
|
const response = parseJson$1(input.responseText);
|
|
6575
|
-
if (!
|
|
6576
|
-
const toolSchemas = extractToolSchemas(
|
|
7598
|
+
if (!request2.ok || !response.ok) return [];
|
|
7599
|
+
const toolSchemas = extractToolSchemas(request2.value, input.apiFormat);
|
|
6577
7600
|
const toolCalls = extractToolCalls(response.value, input.apiFormat);
|
|
6578
7601
|
if (toolCalls.length === 0) return [];
|
|
6579
7602
|
const warnings = [];
|
|
@@ -7180,6 +8203,84 @@ async function closeLogFinalizerWorkers() {
|
|
|
7180
8203
|
function forceCloseLogFinalizerWorkers() {
|
|
7181
8204
|
for (const worker of detachLogFinalizerWorkers()) void worker.terminate();
|
|
7182
8205
|
}
|
|
8206
|
+
const UNASSIGNED_SESSION_QUEUE = "__unassigned__";
|
|
8207
|
+
const queues = /* @__PURE__ */ new Map();
|
|
8208
|
+
let queuedTaskCount = 0;
|
|
8209
|
+
class SessionTaskQueueRejectedError extends Error {
|
|
8210
|
+
constructor(limit) {
|
|
8211
|
+
super(`Session task queue is full; limit=${String(limit)}`);
|
|
8212
|
+
this.name = "SessionTaskQueueRejectedError";
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
8215
|
+
function queueKeyForSession(sessionId) {
|
|
8216
|
+
return sessionId ?? UNASSIGNED_SESSION_QUEUE;
|
|
8217
|
+
}
|
|
8218
|
+
function resolveSessionTaskQueueLimit() {
|
|
8219
|
+
const configured = Number(process.env["SESSION_TASK_QUEUE_LIMIT"]);
|
|
8220
|
+
if (Number.isSafeInteger(configured) && configured > 0) return configured;
|
|
8221
|
+
return DEFAULT_RUNTIME_BUDGET_POLICY.queues.finalizerJobs;
|
|
8222
|
+
}
|
|
8223
|
+
function errorMessage$1(err) {
|
|
8224
|
+
return err instanceof Error ? err.message : String(err);
|
|
8225
|
+
}
|
|
8226
|
+
async function runSessionTask(sessionId, taskName, task) {
|
|
8227
|
+
markSessionTaskStarted(sessionId);
|
|
8228
|
+
try {
|
|
8229
|
+
const result = await task();
|
|
8230
|
+
markSessionTaskFinished(sessionId, null);
|
|
8231
|
+
return result;
|
|
8232
|
+
} catch (err) {
|
|
8233
|
+
const message = errorMessage$1(err);
|
|
8234
|
+
logger.error(`[sessionRuntime] ${taskName} failed for session ${sessionId ?? "unassigned"}`);
|
|
8235
|
+
markSessionTaskFinished(sessionId, message);
|
|
8236
|
+
return Promise.reject(err);
|
|
8237
|
+
}
|
|
8238
|
+
}
|
|
8239
|
+
function enqueueSessionTask(sessionId, taskName, task) {
|
|
8240
|
+
const queueLimit = resolveSessionTaskQueueLimit();
|
|
8241
|
+
if (queuedTaskCount >= queueLimit) {
|
|
8242
|
+
logger.warn(
|
|
8243
|
+
`[sessionRuntime] Rejecting ${taskName} for session ${sessionId ?? "unassigned"}: queue full (${String(queuedTaskCount)}/${String(queueLimit)})`
|
|
8244
|
+
);
|
|
8245
|
+
return Promise.reject(new SessionTaskQueueRejectedError(queueLimit));
|
|
8246
|
+
}
|
|
8247
|
+
const queueKey = queueKeyForSession(sessionId);
|
|
8248
|
+
const previous = queues.get(queueKey) ?? Promise.resolve();
|
|
8249
|
+
queuedTaskCount += 1;
|
|
8250
|
+
markSessionTaskQueued(sessionId);
|
|
8251
|
+
const current = previous.catch(() => void 0).then(() => runSessionTask(sessionId, taskName, task));
|
|
8252
|
+
const settled = current.then(
|
|
8253
|
+
() => void 0,
|
|
8254
|
+
() => void 0
|
|
8255
|
+
);
|
|
8256
|
+
queues.set(queueKey, settled);
|
|
8257
|
+
void settled.then(() => {
|
|
8258
|
+
queuedTaskCount = Math.max(0, queuedTaskCount - 1);
|
|
8259
|
+
if (queues.get(queueKey) === settled) {
|
|
8260
|
+
queues.delete(queueKey);
|
|
8261
|
+
}
|
|
8262
|
+
});
|
|
8263
|
+
return current;
|
|
8264
|
+
}
|
|
8265
|
+
function enqueueFinalizeLogJob(job) {
|
|
8266
|
+
return enqueueSessionTask(
|
|
8267
|
+
getLogSessionId(job.log),
|
|
8268
|
+
"finalize-log",
|
|
8269
|
+
() => executeFinalizeLogJob(job)
|
|
8270
|
+
);
|
|
8271
|
+
}
|
|
8272
|
+
async function flushSessionTasks() {
|
|
8273
|
+
while (queues.size > 0) {
|
|
8274
|
+
await Promise.all([...queues.values()]);
|
|
8275
|
+
}
|
|
8276
|
+
}
|
|
8277
|
+
function getSessionRuntimeQueueHealth() {
|
|
8278
|
+
return {
|
|
8279
|
+
activeQueues: queues.size,
|
|
8280
|
+
queuedTasks: queuedTaskCount,
|
|
8281
|
+
queueLimit: resolveSessionTaskQueueLimit()
|
|
8282
|
+
};
|
|
8283
|
+
}
|
|
7183
8284
|
const DEFAULT_CACHE_MS = 2e3;
|
|
7184
8285
|
const DEFAULT_MIN_DISK_HEADROOM_BYTES = 256 * 1024 * 1024;
|
|
7185
8286
|
const DEFAULT_MAX_FINALIZER_QUEUE = 256;
|
|
@@ -7218,6 +8319,7 @@ async function checkDiskHeadroom() {
|
|
|
7218
8319
|
}
|
|
7219
8320
|
function finalizerChecks() {
|
|
7220
8321
|
const health = getFinalizerRuntimeHealth();
|
|
8322
|
+
const sessionQueue = getSessionRuntimeQueueHealth();
|
|
7221
8323
|
const runtimeArtifactMissing = health.runtime !== "inline" && !health.artifactAvailable;
|
|
7222
8324
|
const initializedWorkerMissing = health.runtime === "worker" && health.activeWorkers !== null && health.activeWorkers < health.configuredWorkers;
|
|
7223
8325
|
const workers = runtimeArtifactMissing || initializedWorkerMissing ? "unavailable" : health.runtime === "inline" ? "degraded" : "ok";
|
|
@@ -7226,7 +8328,7 @@ function finalizerChecks() {
|
|
|
7226
8328
|
DEFAULT_MAX_FINALIZER_QUEUE
|
|
7227
8329
|
);
|
|
7228
8330
|
const warningThreshold = Math.max(1, Math.floor(queueLimit * 0.8));
|
|
7229
|
-
const queue = health.pendingJobs >= queueLimit ? "unavailable" : health.pendingJobs >= warningThreshold ? "degraded" : "ok";
|
|
8331
|
+
const queue = health.pendingJobs + sessionQueue.queuedTasks >= queueLimit ? "unavailable" : health.pendingJobs + sessionQueue.queuedTasks >= warningThreshold ? "degraded" : "ok";
|
|
7230
8332
|
return { workers, queue };
|
|
7231
8333
|
}
|
|
7232
8334
|
function checkWorkers() {
|
|
@@ -7308,151 +8410,313 @@ function setRuntimeShuttingDown(shuttingDown) {
|
|
|
7308
8410
|
runtimeShuttingDown = shuttingDown;
|
|
7309
8411
|
readinessService.invalidate();
|
|
7310
8412
|
}
|
|
7311
|
-
async function readinessResponse() {
|
|
7312
|
-
const readiness = await getRuntimeReadiness();
|
|
7313
|
-
return Response.json(readiness, {
|
|
7314
|
-
status: readiness.status === "unavailable" ? 503 : 200,
|
|
7315
|
-
headers: { "cache-control": "no-store" }
|
|
8413
|
+
async function readinessResponse() {
|
|
8414
|
+
const readiness = await getRuntimeReadiness();
|
|
8415
|
+
return Response.json(readiness, {
|
|
8416
|
+
status: readiness.status === "unavailable" ? 503 : 200,
|
|
8417
|
+
headers: { "cache-control": "no-store" }
|
|
8418
|
+
});
|
|
8419
|
+
}
|
|
8420
|
+
const Route$V = createFileRoute("/readyz")({
|
|
8421
|
+
server: {
|
|
8422
|
+
handlers: {
|
|
8423
|
+
GET: readinessResponse
|
|
8424
|
+
}
|
|
8425
|
+
}
|
|
8426
|
+
});
|
|
8427
|
+
const INITIAL_STREAM_LOG_LIMIT = 100;
|
|
8428
|
+
const UPDATE_COALESCE_MS = 50;
|
|
8429
|
+
const sseClientLimiter = createCapacityLimiter(MAX_SSE_CLIENTS);
|
|
8430
|
+
function getLogsStreamMetrics() {
|
|
8431
|
+
return {
|
|
8432
|
+
activeClients: sseClientLimiter.activeCount(),
|
|
8433
|
+
capacity: sseClientLimiter.capacity
|
|
8434
|
+
};
|
|
8435
|
+
}
|
|
8436
|
+
function streamIncludesBodies(url2) {
|
|
8437
|
+
return url2.searchParams.get("compact") === "0";
|
|
8438
|
+
}
|
|
8439
|
+
function parseLastEventLogId(value) {
|
|
8440
|
+
if (value === null) return void 0;
|
|
8441
|
+
return parseLogId(value) ?? void 0;
|
|
8442
|
+
}
|
|
8443
|
+
function encodeSse(value, id) {
|
|
8444
|
+
const idLine = id === void 0 ? "" : `id: ${String(id)}
|
|
8445
|
+
`;
|
|
8446
|
+
return new TextEncoder().encode(`${idLine}data: ${JSON.stringify(value)}
|
|
8447
|
+
|
|
8448
|
+
`);
|
|
8449
|
+
}
|
|
8450
|
+
function createLogsStreamResponse(request2, clientLimiter = sseClientLimiter) {
|
|
8451
|
+
const lastEventIdHeader = request2.headers.get("last-event-id");
|
|
8452
|
+
const lastEventLogId = parseLastEventLogId(lastEventIdHeader);
|
|
8453
|
+
if (lastEventIdHeader !== null && lastEventLogId === void 0) {
|
|
8454
|
+
return Response.json({ error: "Invalid Last-Event-ID" }, { status: 400 });
|
|
8455
|
+
}
|
|
8456
|
+
const lease = clientLimiter.tryAcquire();
|
|
8457
|
+
if (lease === null) {
|
|
8458
|
+
return resourceLimitResponse({
|
|
8459
|
+
error: `SSE client capacity reached. At most ${String(clientLimiter.capacity)} live clients are allowed; retry after a connection closes.`,
|
|
8460
|
+
code: "sse-capacity-exceeded",
|
|
8461
|
+
status: 503,
|
|
8462
|
+
resource: "sse-clients",
|
|
8463
|
+
limit: clientLimiter.capacity,
|
|
8464
|
+
retryable: true,
|
|
8465
|
+
retryAfterSeconds: 1
|
|
8466
|
+
});
|
|
8467
|
+
}
|
|
8468
|
+
const url2 = new URL(request2.url);
|
|
8469
|
+
const sessionId = url2.searchParams.get("sessionId") ?? void 0;
|
|
8470
|
+
const model = url2.searchParams.get("model") ?? void 0;
|
|
8471
|
+
const includeBodies = streamIncludesBodies(url2);
|
|
8472
|
+
let controllerRef = null;
|
|
8473
|
+
let cleanedUp = false;
|
|
8474
|
+
let updateTimer = null;
|
|
8475
|
+
const pendingUpdates = /* @__PURE__ */ new Map();
|
|
8476
|
+
const flushPendingUpdates = () => {
|
|
8477
|
+
updateTimer = null;
|
|
8478
|
+
const controller = controllerRef;
|
|
8479
|
+
if (controller === null) return;
|
|
8480
|
+
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
8481
|
+
controller.close();
|
|
8482
|
+
cleanup();
|
|
8483
|
+
return;
|
|
8484
|
+
}
|
|
8485
|
+
for (const log of pendingUpdates.values()) {
|
|
8486
|
+
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
8487
|
+
controller.close();
|
|
8488
|
+
cleanup();
|
|
8489
|
+
return;
|
|
8490
|
+
}
|
|
8491
|
+
const payloadLog = includeBodies ? log : compactLogForList(log);
|
|
8492
|
+
controller.enqueue(encodeSse({ type: "update", log: payloadLog }, log.id));
|
|
8493
|
+
}
|
|
8494
|
+
pendingUpdates.clear();
|
|
8495
|
+
};
|
|
8496
|
+
const unsubscribe = onLogUpdate((log) => {
|
|
8497
|
+
if (controllerRef === null) return;
|
|
8498
|
+
if (sessionId !== void 0 && getLogSessionId(log) !== sessionId) return;
|
|
8499
|
+
if (model !== void 0 && log.model !== model) return;
|
|
8500
|
+
pendingUpdates.set(log.id, log);
|
|
8501
|
+
if (updateTimer === null) updateTimer = setTimeout(flushPendingUpdates, UPDATE_COALESCE_MS);
|
|
7316
8502
|
});
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
8503
|
+
const cleanup = () => {
|
|
8504
|
+
if (cleanedUp) return;
|
|
8505
|
+
cleanedUp = true;
|
|
8506
|
+
clearInterval(heartbeat);
|
|
8507
|
+
if (updateTimer !== null) clearTimeout(updateTimer);
|
|
8508
|
+
updateTimer = null;
|
|
8509
|
+
pendingUpdates.clear();
|
|
8510
|
+
unsubscribe();
|
|
8511
|
+
lease.release();
|
|
8512
|
+
controllerRef = null;
|
|
8513
|
+
request2.signal.removeEventListener("abort", cleanup);
|
|
8514
|
+
};
|
|
8515
|
+
const heartbeat = setInterval(() => {
|
|
8516
|
+
if (controllerRef !== null) {
|
|
8517
|
+
try {
|
|
8518
|
+
controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
|
|
8519
|
+
} catch {
|
|
8520
|
+
cleanup();
|
|
8521
|
+
}
|
|
7322
8522
|
}
|
|
7323
|
-
}
|
|
7324
|
-
});
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
8523
|
+
}, 3e4);
|
|
8524
|
+
request2.signal.addEventListener("abort", cleanup, { once: true });
|
|
8525
|
+
const stream = new ReadableStream({
|
|
8526
|
+
async start(controller) {
|
|
8527
|
+
controllerRef = controller;
|
|
8528
|
+
try {
|
|
8529
|
+
const result = await listLogsCursorPage({
|
|
8530
|
+
sessionId,
|
|
8531
|
+
model,
|
|
8532
|
+
limit: INITIAL_STREAM_LOG_LIMIT,
|
|
8533
|
+
includeBodies,
|
|
8534
|
+
afterLogId: lastEventLogId,
|
|
8535
|
+
anchor: lastEventLogId === void 0 ? "newest" : void 0
|
|
8536
|
+
});
|
|
8537
|
+
if (cleanedUp) return;
|
|
8538
|
+
const logs = result.logs;
|
|
8539
|
+
const newestLogId = logs.at(-1)?.id;
|
|
8540
|
+
controller.enqueue(encodeSse({ type: "init", logs }, newestLogId));
|
|
8541
|
+
} catch (error) {
|
|
8542
|
+
cleanup();
|
|
8543
|
+
controller.error(error);
|
|
8544
|
+
}
|
|
8545
|
+
},
|
|
8546
|
+
cancel() {
|
|
8547
|
+
cleanup();
|
|
8548
|
+
}
|
|
8549
|
+
});
|
|
8550
|
+
return new Response(stream, {
|
|
8551
|
+
headers: {
|
|
8552
|
+
"Content-Type": "text/event-stream",
|
|
8553
|
+
"Cache-Control": "no-cache",
|
|
8554
|
+
Connection: "keep-alive",
|
|
8555
|
+
"X-Accel-Buffering": "no"
|
|
7332
8556
|
}
|
|
7333
|
-
}
|
|
7334
|
-
});
|
|
7335
|
-
const BACKEND_NOT_FOUND_BODY = {
|
|
7336
|
-
error: "not_found",
|
|
7337
|
-
message: "Agent Inspector backend does not serve the web UI."
|
|
7338
|
-
};
|
|
7339
|
-
function backendNotFoundResponse() {
|
|
7340
|
-
return Response.json(BACKEND_NOT_FOUND_BODY, {
|
|
7341
|
-
status: 404,
|
|
7342
|
-
headers: { "cache-control": "no-store" }
|
|
7343
8557
|
});
|
|
7344
8558
|
}
|
|
7345
|
-
const Route$
|
|
8559
|
+
const Route$U = createFileRoute("/api/logs/stream")({
|
|
7346
8560
|
server: {
|
|
7347
8561
|
handlers: {
|
|
7348
|
-
GET:
|
|
7349
|
-
POST: backendNotFoundResponse,
|
|
7350
|
-
PUT: backendNotFoundResponse,
|
|
7351
|
-
PATCH: backendNotFoundResponse,
|
|
7352
|
-
DELETE: backendNotFoundResponse,
|
|
7353
|
-
OPTIONS: backendNotFoundResponse
|
|
8562
|
+
GET: ({ request: request2 }) => createLogsStreamResponse(request2)
|
|
7354
8563
|
}
|
|
7355
8564
|
}
|
|
7356
8565
|
});
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
8566
|
+
let acceptingRequests = true;
|
|
8567
|
+
let activeRequests = 0;
|
|
8568
|
+
const drainWaiters = /* @__PURE__ */ new Set();
|
|
8569
|
+
function notifyDrained() {
|
|
8570
|
+
if (activeRequests !== 0) return;
|
|
8571
|
+
for (const resolve2 of drainWaiters) resolve2();
|
|
8572
|
+
drainWaiters.clear();
|
|
7360
8573
|
}
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
"search-limit-exceeded",
|
|
7374
|
-
"provider-model-limit-exceeded",
|
|
7375
|
-
"sse-capacity-exceeded",
|
|
7376
|
-
"provider-test-capacity-exceeded",
|
|
7377
|
-
"proxy-capacity-exceeded"
|
|
7378
|
-
]);
|
|
7379
|
-
const ResourceLimitStatusSchema = union([
|
|
7380
|
-
literal(400),
|
|
7381
|
-
literal(413),
|
|
7382
|
-
literal(429),
|
|
7383
|
-
literal(503)
|
|
7384
|
-
]);
|
|
7385
|
-
const ResourceLimitErrorSchema = object({
|
|
7386
|
-
error: string().min(1).max(300),
|
|
7387
|
-
code: ResourceLimitErrorCodeSchema,
|
|
7388
|
-
status: ResourceLimitStatusSchema,
|
|
7389
|
-
resource: string().min(1).max(80),
|
|
7390
|
-
limit: number().int().positive(),
|
|
7391
|
-
retryable: boolean()
|
|
7392
|
-
});
|
|
7393
|
-
function createResourceLimitError(input) {
|
|
7394
|
-
return ResourceLimitErrorSchema.parse(input);
|
|
8574
|
+
function acquireRuntimeRequest() {
|
|
8575
|
+
if (!acceptingRequests || activeRequests >= MAX_ACTIVE_PROXY_REQUESTS) return null;
|
|
8576
|
+
activeRequests += 1;
|
|
8577
|
+
let released = false;
|
|
8578
|
+
return {
|
|
8579
|
+
release() {
|
|
8580
|
+
if (released) return;
|
|
8581
|
+
released = true;
|
|
8582
|
+
activeRequests = Math.max(0, activeRequests - 1);
|
|
8583
|
+
notifyDrained();
|
|
8584
|
+
}
|
|
8585
|
+
};
|
|
7395
8586
|
}
|
|
7396
|
-
function
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
if (input.retryAfterSeconds !== void 0) {
|
|
7400
|
-
headers.set("retry-after", String(input.retryAfterSeconds));
|
|
7401
|
-
}
|
|
7402
|
-
return Response.json(body, { status: input.status, headers });
|
|
8587
|
+
function stopRuntimeAdmission() {
|
|
8588
|
+
acceptingRequests = false;
|
|
8589
|
+
notifyDrained();
|
|
7403
8590
|
}
|
|
7404
|
-
function
|
|
7405
|
-
if (
|
|
7406
|
-
|
|
7407
|
-
|
|
8591
|
+
async function waitForRuntimeTrafficDrain() {
|
|
8592
|
+
if (activeRequests === 0) return;
|
|
8593
|
+
await new Promise((resolve2) => {
|
|
8594
|
+
drainWaiters.add(resolve2);
|
|
8595
|
+
});
|
|
8596
|
+
}
|
|
8597
|
+
function getRuntimeAdmissionState() {
|
|
8598
|
+
return { acceptingRequests, activeRequests, capacity: MAX_ACTIVE_PROXY_REQUESTS };
|
|
8599
|
+
}
|
|
8600
|
+
const PERFORMANCE_METRICS_SCHEMA = "agent-inspector.performance-metrics/v1";
|
|
8601
|
+
const OPERATIONAL_METRICS_SCHEMA = "agent-inspector.operational-metrics/v1";
|
|
8602
|
+
async function getTruncationMetrics() {
|
|
8603
|
+
const entries = await listSqliteLogCursorEntries({});
|
|
8604
|
+
if (entries === null) {
|
|
7408
8605
|
return {
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
status: 400,
|
|
7414
|
-
resource: input.resource,
|
|
7415
|
-
limit: input.maximum,
|
|
7416
|
-
retryable: false
|
|
7417
|
-
})
|
|
8606
|
+
captureIncompleteLogs: 0,
|
|
8607
|
+
droppedChunks: 0,
|
|
8608
|
+
droppedBytes: 0,
|
|
8609
|
+
source: "unavailable"
|
|
7418
8610
|
};
|
|
7419
8611
|
}
|
|
7420
|
-
return {
|
|
8612
|
+
return {
|
|
8613
|
+
captureIncompleteLogs: entries.filter((entry) => entry.summary?.captureIncomplete === true).length,
|
|
8614
|
+
droppedChunks: entries.reduce((sum, entry) => sum + (entry.summary?.droppedChunks ?? 0), 0),
|
|
8615
|
+
droppedBytes: entries.reduce((sum, entry) => sum + (entry.summary?.droppedBytes ?? 0), 0),
|
|
8616
|
+
source: "sqlite"
|
|
8617
|
+
};
|
|
7421
8618
|
}
|
|
7422
|
-
function
|
|
7423
|
-
const
|
|
7424
|
-
|
|
8619
|
+
async function getSafeOperationalMetrics(sse) {
|
|
8620
|
+
const [readiness, truncation] = await Promise.all([
|
|
8621
|
+
getRuntimeReadiness(),
|
|
8622
|
+
getTruncationMetrics()
|
|
8623
|
+
]);
|
|
8624
|
+
const admission = getRuntimeAdmissionState();
|
|
8625
|
+
const workers = getFinalizerRuntimeHealth();
|
|
8626
|
+
const sessionQueue = getSessionRuntimeQueueHealth();
|
|
8627
|
+
const storageInventory = getStorageLifecycleInventory();
|
|
8628
|
+
const lastGc = getLastStorageLifecycleGcResult();
|
|
8629
|
+
const reclaimableBytes = storageInventory.artifacts.filter(
|
|
8630
|
+
(artifact) => artifact.cleanupBehavior === "retention-gc" || artifact.cleanupBehavior === "orphan-reclaim"
|
|
8631
|
+
).reduce((sum, artifact) => sum + artifact.bytes, 0);
|
|
8632
|
+
const queueDepth = workers.pendingJobs + sessionQueue.queuedTasks;
|
|
7425
8633
|
return {
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
8634
|
+
schema: PERFORMANCE_METRICS_SCHEMA,
|
|
8635
|
+
activeUpstream: admission.activeRequests,
|
|
8636
|
+
queueDepth,
|
|
8637
|
+
queueBytes: 0,
|
|
8638
|
+
incompleteCaptures: truncation.captureIncompleteLogs,
|
|
8639
|
+
finalizerLagMs: 0,
|
|
8640
|
+
persistenceLagMs: 0,
|
|
8641
|
+
activeLeases: sse.activeClients,
|
|
8642
|
+
runtime: {
|
|
8643
|
+
schema: OPERATIONAL_METRICS_SCHEMA,
|
|
8644
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8645
|
+
readiness,
|
|
8646
|
+
requests: {
|
|
8647
|
+
activeProxyRequests: admission.activeRequests,
|
|
8648
|
+
proxyCapacity: admission.capacity,
|
|
8649
|
+
acceptingRequests: admission.acceptingRequests
|
|
8650
|
+
},
|
|
8651
|
+
limits: DEFAULT_RUNTIME_BUDGET_POLICY,
|
|
8652
|
+
queues: {
|
|
8653
|
+
finalizerPendingJobs: workers.pendingJobs,
|
|
8654
|
+
sessionQueuedTasks: sessionQueue.queuedTasks,
|
|
8655
|
+
sessionActiveQueues: sessionQueue.activeQueues,
|
|
8656
|
+
sessionQueueLimit: sessionQueue.queueLimit
|
|
8657
|
+
},
|
|
8658
|
+
workers,
|
|
8659
|
+
storage: {
|
|
8660
|
+
totalFiles: storageInventory.totalFiles,
|
|
8661
|
+
totalBytes: storageInventory.totalBytes,
|
|
8662
|
+
reclaimableBytes,
|
|
8663
|
+
lastGc: lastGc === null ? null : {
|
|
8664
|
+
checkedAt: lastGc.checkedAt,
|
|
8665
|
+
filesDeleted: lastGc.filesDeleted,
|
|
8666
|
+
bytesDeleted: lastGc.bytesDeleted,
|
|
8667
|
+
errors: lastGc.errors.length
|
|
7437
8668
|
}
|
|
7438
|
-
}
|
|
8669
|
+
},
|
|
8670
|
+
sse,
|
|
8671
|
+
truncation
|
|
8672
|
+
}
|
|
8673
|
+
};
|
|
8674
|
+
}
|
|
8675
|
+
const Route$T = createFileRoute("/metrics")({
|
|
8676
|
+
server: {
|
|
8677
|
+
handlers: {
|
|
8678
|
+
GET: async () => {
|
|
8679
|
+
const metrics = await getSafeOperationalMetrics(getLogsStreamMetrics());
|
|
8680
|
+
return Response.json(metrics, { headers: { "cache-control": "no-store" } });
|
|
8681
|
+
}
|
|
8682
|
+
}
|
|
8683
|
+
}
|
|
8684
|
+
});
|
|
8685
|
+
function livenessResponse() {
|
|
8686
|
+
return Response.json({ status: "ok" }, { headers: { "cache-control": "no-store" } });
|
|
8687
|
+
}
|
|
8688
|
+
const Route$S = createFileRoute("/livez")({
|
|
8689
|
+
server: {
|
|
8690
|
+
handlers: {
|
|
8691
|
+
GET: livenessResponse
|
|
7439
8692
|
}
|
|
7440
|
-
}
|
|
8693
|
+
}
|
|
8694
|
+
});
|
|
8695
|
+
const BACKEND_NOT_FOUND_BODY = {
|
|
8696
|
+
error: "not_found",
|
|
8697
|
+
message: "Agent Inspector backend does not serve the web UI."
|
|
8698
|
+
};
|
|
8699
|
+
function backendNotFoundResponse() {
|
|
8700
|
+
return Response.json(BACKEND_NOT_FOUND_BODY, {
|
|
8701
|
+
status: 404,
|
|
8702
|
+
headers: { "cache-control": "no-store" }
|
|
8703
|
+
});
|
|
7441
8704
|
}
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
const task = tasks2[index];
|
|
7452
|
-
if (task !== void 0) await task();
|
|
8705
|
+
const Route$R = createFileRoute("/$")({
|
|
8706
|
+
server: {
|
|
8707
|
+
handlers: {
|
|
8708
|
+
GET: backendNotFoundResponse,
|
|
8709
|
+
POST: backendNotFoundResponse,
|
|
8710
|
+
PUT: backendNotFoundResponse,
|
|
8711
|
+
PATCH: backendNotFoundResponse,
|
|
8712
|
+
DELETE: backendNotFoundResponse,
|
|
8713
|
+
OPTIONS: backendNotFoundResponse
|
|
7453
8714
|
}
|
|
7454
|
-
}
|
|
7455
|
-
|
|
8715
|
+
}
|
|
8716
|
+
});
|
|
8717
|
+
function maskApiKey(apiKey) {
|
|
8718
|
+
if (apiKey.length <= 8) return "••••••••";
|
|
8719
|
+
return apiKey.slice(0, 4) + "••••••••" + apiKey.slice(-4);
|
|
7456
8720
|
}
|
|
7457
8721
|
function httpUrl() {
|
|
7458
8722
|
return string().refine((val) => {
|
|
@@ -8796,16 +10060,16 @@ function setCache(port, info) {
|
|
|
8796
10060
|
cache.set(port, { ...info, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
8797
10061
|
evictCacheIfNeeded();
|
|
8798
10062
|
}
|
|
8799
|
-
function extractRemotePort(
|
|
10063
|
+
function extractRemotePort(request2) {
|
|
8800
10064
|
const candidateSockets = [];
|
|
8801
|
-
const requestAsUnknown =
|
|
10065
|
+
const requestAsUnknown = request2;
|
|
8802
10066
|
const ctx = pick(requestAsUnknown, "context");
|
|
8803
10067
|
const nitro = pick(ctx, "nitro");
|
|
8804
10068
|
const node = pick(nitro, "node");
|
|
8805
10069
|
const innerReq = pick(node, "req");
|
|
8806
10070
|
const sock = pick(innerReq, "socket") ?? pick(innerReq, "connection");
|
|
8807
10071
|
if (sock !== void 0) candidateSockets.push(sock);
|
|
8808
|
-
const descriptor = Object.getOwnPropertyDescriptor(
|
|
10072
|
+
const descriptor = Object.getOwnPropertyDescriptor(request2, "socket");
|
|
8809
10073
|
if (descriptor !== void 0 && descriptor.get === void 0) {
|
|
8810
10074
|
candidateSockets.push(descriptor.value);
|
|
8811
10075
|
}
|
|
@@ -9009,12 +10273,12 @@ function readIntHeader(headers, name2) {
|
|
|
9009
10273
|
if (!Number.isInteger(parsed) || parsed < 0) return null;
|
|
9010
10274
|
return parsed;
|
|
9011
10275
|
}
|
|
9012
|
-
function readClientHeaders(
|
|
9013
|
-
const pid = readIntHeader(
|
|
10276
|
+
function readClientHeaders(request2) {
|
|
10277
|
+
const pid = readIntHeader(request2.headers, CLIENT_HEADERS.pid);
|
|
9014
10278
|
if (pid === null || pid === 0) return null;
|
|
9015
|
-
const port = readIntHeader(
|
|
9016
|
-
const cwd =
|
|
9017
|
-
const projectFolder =
|
|
10279
|
+
const port = readIntHeader(request2.headers, CLIENT_HEADERS.port);
|
|
10280
|
+
const cwd = request2.headers.get(CLIENT_HEADERS.cwd);
|
|
10281
|
+
const projectFolder = request2.headers.get(CLIENT_HEADERS.projectFolder);
|
|
9018
10282
|
return {
|
|
9019
10283
|
pid,
|
|
9020
10284
|
port: port !== null && port > 0 ? port : null,
|
|
@@ -9022,10 +10286,10 @@ function readClientHeaders(request) {
|
|
|
9022
10286
|
projectFolder: projectFolder !== null && projectFolder !== "" ? projectFolder : null
|
|
9023
10287
|
};
|
|
9024
10288
|
}
|
|
9025
|
-
async function getClientInfo(
|
|
9026
|
-
const fromHeaders = readClientHeaders(
|
|
10289
|
+
async function getClientInfo(request2) {
|
|
10290
|
+
const fromHeaders = readClientHeaders(request2);
|
|
9027
10291
|
if (fromHeaders !== null) return fromHeaders;
|
|
9028
|
-
const remotePort = extractRemotePort(
|
|
10292
|
+
const remotePort = extractRemotePort(request2);
|
|
9029
10293
|
if (remotePort !== null) {
|
|
9030
10294
|
const cachedPort = getFromCache(remotePort);
|
|
9031
10295
|
if (cachedPort !== null) return cachedPort;
|
|
@@ -9332,585 +10596,569 @@ function isLoopbackHost(hostname) {
|
|
|
9332
10596
|
if (LOOPBACK_HOSTNAMES.has(lower2)) return true;
|
|
9333
10597
|
if (lower2 === "::1") return true;
|
|
9334
10598
|
if (lower2.startsWith("::ffff:127.")) return true;
|
|
9335
|
-
if (lower2.startsWith("::ffff:7f00:")) return true;
|
|
9336
|
-
return /^127\./.test(lower2);
|
|
9337
|
-
}
|
|
9338
|
-
function mappedIpv4Address(hostname) {
|
|
9339
|
-
const lower2 = hostname.toLowerCase();
|
|
9340
|
-
if (!lower2.startsWith("::ffff:")) return null;
|
|
9341
|
-
const suffix = lower2.slice("::ffff:".length);
|
|
9342
|
-
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(suffix)) return suffix;
|
|
9343
|
-
const match = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(suffix);
|
|
9344
|
-
if (match === null) return null;
|
|
9345
|
-
const highText = match[1];
|
|
9346
|
-
const lowText = match[2];
|
|
9347
|
-
if (highText === void 0 || lowText === void 0) return null;
|
|
9348
|
-
const high = Number.parseInt(highText, 16);
|
|
9349
|
-
const low = Number.parseInt(lowText, 16);
|
|
9350
|
-
return `${String(high >> 8)}.${String(high & 255)}.${String(low >> 8)}.${String(low & 255)}`;
|
|
9351
|
-
}
|
|
9352
|
-
function isInternalHost(hostname) {
|
|
9353
|
-
if (LOOPBACK_HOSTNAMES.has(hostname.toLowerCase())) return true;
|
|
9354
|
-
const mappedIpv4 = mappedIpv4Address(hostname);
|
|
9355
|
-
if (mappedIpv4 !== null) return isInternalHost(mappedIpv4);
|
|
9356
|
-
const ipv4Match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
|
|
9357
|
-
if (ipv4Match !== null) {
|
|
9358
|
-
const [, a, b] = ipv4Match;
|
|
9359
|
-
if (a === void 0 || b === void 0) return false;
|
|
9360
|
-
const first = Number.parseInt(a, 10);
|
|
9361
|
-
const second = Number.parseInt(b, 10);
|
|
9362
|
-
if (first === 127) return true;
|
|
9363
|
-
if (first === 10) return true;
|
|
9364
|
-
if (first === 172 && second >= 16 && second <= 31) return true;
|
|
9365
|
-
if (first === 192 && second === 168) return true;
|
|
9366
|
-
if (first === 169 && second === 254) return true;
|
|
9367
|
-
if (first === 0) return true;
|
|
9368
|
-
if (first === 100 && second >= 64 && second <= 127) return true;
|
|
9369
|
-
if (first >= 224) return true;
|
|
9370
|
-
if (first === 198 && (second === 18 || second === 19)) return true;
|
|
9371
|
-
return false;
|
|
9372
|
-
}
|
|
9373
|
-
const lower2 = hostname.toLowerCase();
|
|
9374
|
-
if (lower2 === "::") return true;
|
|
9375
|
-
if (lower2 === "::1") return true;
|
|
9376
|
-
if (/^fe[89ab]/.test(lower2)) return true;
|
|
9377
|
-
if (lower2.startsWith("fc") || lower2.startsWith("fd")) return true;
|
|
9378
|
-
if (lower2.startsWith("ff")) return true;
|
|
9379
|
-
return false;
|
|
9380
|
-
}
|
|
9381
|
-
function evaluateUpstreamUrl(rawUrl, options = {}) {
|
|
9382
|
-
if (options.bypass === true) return { allow: true };
|
|
9383
|
-
let parsed;
|
|
9384
|
-
try {
|
|
9385
|
-
parsed = new URL(rawUrl);
|
|
9386
|
-
} catch {
|
|
9387
|
-
return { allow: false, reason: "Not a valid URL" };
|
|
9388
|
-
}
|
|
9389
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
9390
|
-
return { allow: false, reason: `Unsupported scheme "${parsed.protocol}"` };
|
|
9391
|
-
}
|
|
9392
|
-
if (options.allowLoopback === true && parsed.hostname === "localhost") {
|
|
9393
|
-
return { allow: true };
|
|
9394
|
-
}
|
|
9395
|
-
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
9396
|
-
if (isInternalHost(hostname)) {
|
|
9397
|
-
if (options.allowLoopback === true) {
|
|
9398
|
-
if (isLoopbackHost(hostname)) return { allow: true };
|
|
9399
|
-
}
|
|
9400
|
-
return {
|
|
9401
|
-
allow: false,
|
|
9402
|
-
reason: `Refusing to proxy to internal address "${hostname}"`
|
|
9403
|
-
};
|
|
9404
|
-
}
|
|
9405
|
-
return { allow: true };
|
|
9406
|
-
}
|
|
9407
|
-
async function defaultResolver(hostname) {
|
|
9408
|
-
return lookup(hostname, { all: true, verbatim: true });
|
|
9409
|
-
}
|
|
9410
|
-
function isLoopbackAddress(hostname) {
|
|
9411
|
-
const lower2 = hostname.toLowerCase();
|
|
9412
|
-
return lower2 === "::1" || lower2.startsWith("::ffff:127.") || lower2.startsWith("::ffff:7f00:") || /^127\./.test(lower2);
|
|
9413
|
-
}
|
|
9414
|
-
async function evaluateResolvedUpstreamUrl(rawUrl, options = {}) {
|
|
9415
|
-
const initial = evaluateUpstreamUrl(rawUrl, options);
|
|
9416
|
-
if (!initial.allow || options.bypass === true) return initial;
|
|
9417
|
-
const parsed = new URL(rawUrl);
|
|
9418
|
-
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
9419
|
-
if (isIP(hostname) !== 0 || hostname.toLowerCase() === "localhost") return initial;
|
|
9420
|
-
let addresses;
|
|
9421
|
-
try {
|
|
9422
|
-
addresses = await (options.resolver ?? defaultResolver)(hostname);
|
|
9423
|
-
} catch (error) {
|
|
9424
|
-
return {
|
|
9425
|
-
allow: false,
|
|
9426
|
-
reason: `Could not safely resolve upstream host "${hostname}": ${String(error)}`
|
|
9427
|
-
};
|
|
9428
|
-
}
|
|
9429
|
-
if (addresses.length === 0) {
|
|
9430
|
-
return { allow: false, reason: `Upstream host "${hostname}" resolved to no addresses` };
|
|
9431
|
-
}
|
|
9432
|
-
for (const resolved of addresses) {
|
|
9433
|
-
if (!isInternalHost(resolved.address)) continue;
|
|
9434
|
-
if (options.allowLoopback === true && isLoopbackAddress(resolved.address)) continue;
|
|
9435
|
-
return {
|
|
9436
|
-
allow: false,
|
|
9437
|
-
reason: `Refusing upstream host "${hostname}" because it resolves to internal address "${resolved.address}"`
|
|
9438
|
-
};
|
|
9439
|
-
}
|
|
9440
|
-
return { allow: true };
|
|
9441
|
-
}
|
|
9442
|
-
function isRedirectStatus(status) {
|
|
9443
|
-
return status >= 300 && status < 400;
|
|
9444
|
-
}
|
|
9445
|
-
async function safeFetch(rawUrl, init = {}, options = {}) {
|
|
9446
|
-
const decision = await evaluateResolvedUpstreamUrl(rawUrl, options);
|
|
9447
|
-
if (!decision.allow) {
|
|
9448
|
-
return { ok: false, kind: "network-policy", reason: decision.reason };
|
|
9449
|
-
}
|
|
9450
|
-
const response = await fetch(rawUrl, { ...init, redirect: "manual" });
|
|
9451
|
-
if (!isRedirectStatus(response.status)) return { ok: true, response };
|
|
9452
|
-
if (response.body !== null) {
|
|
9453
|
-
await response.body.cancel().catch(() => void 0);
|
|
9454
|
-
}
|
|
9455
|
-
const location = response.headers.get("location");
|
|
9456
|
-
return {
|
|
9457
|
-
ok: false,
|
|
9458
|
-
kind: "redirect",
|
|
9459
|
-
reason: location === null ? `Upstream returned blocked redirect HTTP ${String(response.status)}` : `Upstream redirect to "${location}" is blocked; configure the final API URL directly`
|
|
9460
|
-
};
|
|
9461
|
-
}
|
|
9462
|
-
const BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";
|
|
9463
|
-
function isClaudeCodeBillingHeaderBlock(text) {
|
|
9464
|
-
const trimmed = text.trimStart();
|
|
9465
|
-
return trimmed.toLowerCase().startsWith(BILLING_HEADER_PREFIX);
|
|
9466
|
-
}
|
|
9467
|
-
function isObjectWithSystem(value) {
|
|
9468
|
-
if (!isPlainRecord(value)) return false;
|
|
9469
|
-
return Object.prototype.hasOwnProperty.call(value, "system");
|
|
9470
|
-
}
|
|
9471
|
-
function isBillingHeaderTextBlock(block) {
|
|
9472
|
-
if (!isPlainRecord(block)) return false;
|
|
9473
|
-
const typeVal = safeGetOwnProperty(block, "type");
|
|
9474
|
-
const textVal = safeGetOwnProperty(block, "text");
|
|
9475
|
-
if (typeof typeVal !== "string" || typeVal !== "text") return false;
|
|
9476
|
-
if (typeof textVal !== "string") return false;
|
|
9477
|
-
return isClaudeCodeBillingHeaderBlock(textVal);
|
|
9478
|
-
}
|
|
9479
|
-
function stripClaudeCodeBillingHeader(rawBody) {
|
|
9480
|
-
let parsed;
|
|
9481
|
-
try {
|
|
9482
|
-
parsed = JSON.parse(rawBody);
|
|
9483
|
-
} catch {
|
|
9484
|
-
return { body: rawBody, removed: 0 };
|
|
9485
|
-
}
|
|
9486
|
-
if (!isObjectWithSystem(parsed)) {
|
|
9487
|
-
return { body: rawBody, removed: 0 };
|
|
9488
|
-
}
|
|
9489
|
-
const system = parsed.system;
|
|
9490
|
-
if (!Array.isArray(system) || system.length === 0) {
|
|
9491
|
-
return { body: rawBody, removed: 0 };
|
|
9492
|
-
}
|
|
9493
|
-
const kept = [];
|
|
9494
|
-
let removed = 0;
|
|
9495
|
-
for (const block of system) {
|
|
9496
|
-
if (isBillingHeaderTextBlock(block)) {
|
|
9497
|
-
removed += 1;
|
|
9498
|
-
continue;
|
|
9499
|
-
}
|
|
9500
|
-
kept.push(block);
|
|
9501
|
-
}
|
|
9502
|
-
if (removed === 0) {
|
|
9503
|
-
return { body: rawBody, removed: 0 };
|
|
9504
|
-
}
|
|
9505
|
-
if (kept.length === 0) {
|
|
9506
|
-
delete parsed.system;
|
|
9507
|
-
} else {
|
|
9508
|
-
parsed.system = kept;
|
|
9509
|
-
}
|
|
9510
|
-
return { body: JSON.stringify(parsed), removed };
|
|
9511
|
-
}
|
|
9512
|
-
const ROLE_ASSISTANT = "assistant";
|
|
9513
|
-
const ROLE_TOOL = "tool";
|
|
9514
|
-
function isObjectWithMessages(value) {
|
|
9515
|
-
if (!isPlainRecord(value)) return false;
|
|
9516
|
-
if (!Object.prototype.hasOwnProperty.call(value, "messages")) return false;
|
|
9517
|
-
const messages = safeGetOwnProperty(value, "messages");
|
|
9518
|
-
return Array.isArray(messages);
|
|
9519
|
-
}
|
|
9520
|
-
function collectAssistantToolCallIds(message, into) {
|
|
9521
|
-
if (!isPlainRecord(message)) return;
|
|
9522
|
-
const toolCalls = safeGetOwnProperty(message, "tool_calls");
|
|
9523
|
-
if (!Array.isArray(toolCalls)) return;
|
|
9524
|
-
for (const tc of toolCalls) {
|
|
9525
|
-
if (!isPlainRecord(tc)) continue;
|
|
9526
|
-
const id = safeGetOwnProperty(tc, "id");
|
|
9527
|
-
if (typeof id === "string" && id.length > 0) {
|
|
9528
|
-
into.add(id);
|
|
9529
|
-
}
|
|
9530
|
-
}
|
|
9531
|
-
}
|
|
9532
|
-
function findOrphanToolMessageIndices(messages) {
|
|
9533
|
-
const knownToolCallIds = /* @__PURE__ */ new Set();
|
|
9534
|
-
const indices = [];
|
|
9535
|
-
const orphanIds = [];
|
|
9536
|
-
for (let i = 0; i < messages.length; i++) {
|
|
9537
|
-
const msg = messages[i];
|
|
9538
|
-
if (!isPlainRecord(msg)) continue;
|
|
9539
|
-
const role = safeGetOwnProperty(msg, "role");
|
|
9540
|
-
if (role === ROLE_ASSISTANT) {
|
|
9541
|
-
collectAssistantToolCallIds(msg, knownToolCallIds);
|
|
9542
|
-
continue;
|
|
9543
|
-
}
|
|
9544
|
-
if (role === ROLE_TOOL) {
|
|
9545
|
-
const id = safeGetOwnProperty(msg, "tool_call_id");
|
|
9546
|
-
if (typeof id !== "string" || id.length === 0) {
|
|
9547
|
-
indices.push(i);
|
|
9548
|
-
orphanIds.push(null);
|
|
9549
|
-
continue;
|
|
9550
|
-
}
|
|
9551
|
-
if (!knownToolCallIds.has(id)) {
|
|
9552
|
-
indices.push(i);
|
|
9553
|
-
orphanIds.push(id);
|
|
9554
|
-
}
|
|
9555
|
-
}
|
|
10599
|
+
if (lower2.startsWith("::ffff:7f00:")) return true;
|
|
10600
|
+
return /^127\./.test(lower2);
|
|
10601
|
+
}
|
|
10602
|
+
function mappedIpv4Address(hostname) {
|
|
10603
|
+
const lower2 = hostname.toLowerCase();
|
|
10604
|
+
if (!lower2.startsWith("::ffff:")) return null;
|
|
10605
|
+
const suffix = lower2.slice("::ffff:".length);
|
|
10606
|
+
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(suffix)) return suffix;
|
|
10607
|
+
const match = /^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(suffix);
|
|
10608
|
+
if (match === null) return null;
|
|
10609
|
+
const highText = match[1];
|
|
10610
|
+
const lowText = match[2];
|
|
10611
|
+
if (highText === void 0 || lowText === void 0) return null;
|
|
10612
|
+
const high = Number.parseInt(highText, 16);
|
|
10613
|
+
const low = Number.parseInt(lowText, 16);
|
|
10614
|
+
return `${String(high >> 8)}.${String(high & 255)}.${String(low >> 8)}.${String(low & 255)}`;
|
|
10615
|
+
}
|
|
10616
|
+
function isInternalHost(hostname) {
|
|
10617
|
+
if (LOOPBACK_HOSTNAMES.has(hostname.toLowerCase())) return true;
|
|
10618
|
+
const mappedIpv4 = mappedIpv4Address(hostname);
|
|
10619
|
+
if (mappedIpv4 !== null) return isInternalHost(mappedIpv4);
|
|
10620
|
+
const ipv4Match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
|
|
10621
|
+
if (ipv4Match !== null) {
|
|
10622
|
+
const [, a, b] = ipv4Match;
|
|
10623
|
+
if (a === void 0 || b === void 0) return false;
|
|
10624
|
+
const first = Number.parseInt(a, 10);
|
|
10625
|
+
const second = Number.parseInt(b, 10);
|
|
10626
|
+
if (first === 127) return true;
|
|
10627
|
+
if (first === 10) return true;
|
|
10628
|
+
if (first === 172 && second >= 16 && second <= 31) return true;
|
|
10629
|
+
if (first === 192 && second === 168) return true;
|
|
10630
|
+
if (first === 169 && second === 254) return true;
|
|
10631
|
+
if (first === 0) return true;
|
|
10632
|
+
if (first === 100 && second >= 64 && second <= 127) return true;
|
|
10633
|
+
if (first >= 224) return true;
|
|
10634
|
+
if (first === 198 && (second === 18 || second === 19)) return true;
|
|
10635
|
+
return false;
|
|
9556
10636
|
}
|
|
9557
|
-
|
|
10637
|
+
const lower2 = hostname.toLowerCase();
|
|
10638
|
+
if (lower2 === "::") return true;
|
|
10639
|
+
if (lower2 === "::1") return true;
|
|
10640
|
+
if (/^fe[89ab]/.test(lower2)) return true;
|
|
10641
|
+
if (lower2.startsWith("fc") || lower2.startsWith("fd")) return true;
|
|
10642
|
+
if (lower2.startsWith("ff")) return true;
|
|
10643
|
+
return false;
|
|
9558
10644
|
}
|
|
9559
|
-
function
|
|
10645
|
+
function evaluateUpstreamUrl(rawUrl, options = {}) {
|
|
10646
|
+
if (options.bypass === true) return { allow: true };
|
|
9560
10647
|
let parsed;
|
|
9561
10648
|
try {
|
|
9562
|
-
parsed =
|
|
10649
|
+
parsed = new URL(rawUrl);
|
|
9563
10650
|
} catch {
|
|
9564
|
-
return {
|
|
10651
|
+
return { allow: false, reason: "Not a valid URL" };
|
|
9565
10652
|
}
|
|
9566
|
-
if (
|
|
9567
|
-
return {
|
|
10653
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
10654
|
+
return { allow: false, reason: `Unsupported scheme "${parsed.protocol}"` };
|
|
9568
10655
|
}
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
if (indices.length === 0) {
|
|
9572
|
-
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
10656
|
+
if (options.allowLoopback === true && parsed.hostname === "localhost") {
|
|
10657
|
+
return { allow: true };
|
|
9573
10658
|
}
|
|
9574
|
-
const
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
10659
|
+
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
10660
|
+
if (isInternalHost(hostname)) {
|
|
10661
|
+
if (options.allowLoopback === true) {
|
|
10662
|
+
if (isLoopbackHost(hostname)) return { allow: true };
|
|
10663
|
+
}
|
|
10664
|
+
return {
|
|
10665
|
+
allow: false,
|
|
10666
|
+
reason: `Refusing to proxy to internal address "${hostname}"`
|
|
10667
|
+
};
|
|
9578
10668
|
}
|
|
9579
|
-
|
|
9580
|
-
return { body: JSON.stringify(parsed), removed: indices.length, orphanIds };
|
|
10669
|
+
return { allow: true };
|
|
9581
10670
|
}
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
function queueKeyForSession(sessionId) {
|
|
9585
|
-
return sessionId ?? UNASSIGNED_SESSION_QUEUE;
|
|
10671
|
+
async function defaultResolver(hostname) {
|
|
10672
|
+
return lookup(hostname, { all: true, verbatim: true });
|
|
9586
10673
|
}
|
|
9587
|
-
function
|
|
9588
|
-
|
|
10674
|
+
function isLoopbackAddress(hostname) {
|
|
10675
|
+
const lower2 = hostname.toLowerCase();
|
|
10676
|
+
return lower2 === "::1" || lower2.startsWith("::ffff:127.") || lower2.startsWith("::ffff:7f00:") || /^127\./.test(lower2);
|
|
9589
10677
|
}
|
|
9590
|
-
async function
|
|
9591
|
-
|
|
10678
|
+
async function evaluateResolvedUpstreamUrlForFetch(rawUrl, options) {
|
|
10679
|
+
const initial = evaluateUpstreamUrl(rawUrl, options);
|
|
10680
|
+
if (!initial.allow) return initial;
|
|
10681
|
+
const parsed = new URL(rawUrl);
|
|
10682
|
+
const hostname = parsed.hostname.replace(/^\[|]$/g, "");
|
|
10683
|
+
if (options.bypass === true) return { allow: true, hostname, addresses: [] };
|
|
10684
|
+
const ipFamily = isIP(hostname);
|
|
10685
|
+
if (ipFamily !== 0)
|
|
10686
|
+
return { allow: true, hostname, addresses: [{ address: hostname, family: ipFamily }] };
|
|
10687
|
+
let addresses;
|
|
9592
10688
|
try {
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
return
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
markSessionTaskFinished(sessionId, message);
|
|
9600
|
-
return Promise.reject(err);
|
|
10689
|
+
addresses = await (options.resolver ?? defaultResolver)(hostname);
|
|
10690
|
+
} catch (error) {
|
|
10691
|
+
return {
|
|
10692
|
+
allow: false,
|
|
10693
|
+
reason: `Could not safely resolve upstream host "${hostname}": ${String(error)}`
|
|
10694
|
+
};
|
|
9601
10695
|
}
|
|
10696
|
+
if (addresses.length === 0) {
|
|
10697
|
+
return { allow: false, reason: `Upstream host "${hostname}" resolved to no addresses` };
|
|
10698
|
+
}
|
|
10699
|
+
for (const resolved of addresses) {
|
|
10700
|
+
if (!isInternalHost(resolved.address)) continue;
|
|
10701
|
+
if (options.allowLoopback === true && isLoopbackAddress(resolved.address)) continue;
|
|
10702
|
+
return {
|
|
10703
|
+
allow: false,
|
|
10704
|
+
reason: `Refusing upstream host "${hostname}" because it resolves to internal address "${resolved.address}"`
|
|
10705
|
+
};
|
|
10706
|
+
}
|
|
10707
|
+
return { allow: true, hostname, addresses };
|
|
9602
10708
|
}
|
|
9603
|
-
function
|
|
9604
|
-
|
|
9605
|
-
const previous = queues.get(queueKey) ?? Promise.resolve();
|
|
9606
|
-
markSessionTaskQueued(sessionId);
|
|
9607
|
-
const current = previous.catch(() => void 0).then(() => runSessionTask(sessionId, taskName, task));
|
|
9608
|
-
const settled = current.then(
|
|
9609
|
-
() => void 0,
|
|
9610
|
-
() => void 0
|
|
9611
|
-
);
|
|
9612
|
-
queues.set(queueKey, settled);
|
|
9613
|
-
void settled.then(() => {
|
|
9614
|
-
if (queues.get(queueKey) === settled) {
|
|
9615
|
-
queues.delete(queueKey);
|
|
9616
|
-
}
|
|
9617
|
-
});
|
|
9618
|
-
return current;
|
|
9619
|
-
}
|
|
9620
|
-
function enqueueFinalizeLogJob(job) {
|
|
9621
|
-
return enqueueSessionTask(
|
|
9622
|
-
getLogSessionId(job.log),
|
|
9623
|
-
"finalize-log",
|
|
9624
|
-
() => executeFinalizeLogJob(job)
|
|
9625
|
-
);
|
|
10709
|
+
function isRedirectStatus(status) {
|
|
10710
|
+
return status >= 300 && status < 400;
|
|
9626
10711
|
}
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
await Promise.all([...queues.values()]);
|
|
9630
|
-
}
|
|
10712
|
+
function formatByteLimit(limit) {
|
|
10713
|
+
return `${String(limit)} bytes`;
|
|
9631
10714
|
}
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
const
|
|
9635
|
-
|
|
9636
|
-
const DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
|
|
9637
|
-
const DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
|
|
9638
|
-
const activeCaptures = /* @__PURE__ */ new Set();
|
|
9639
|
-
function resolveMemoryLimitBytes() {
|
|
9640
|
-
const raw = process.env["AGENT_INSPECTOR_STREAM_MEMORY_LIMIT_BYTES"];
|
|
9641
|
-
if (raw === void 0 || raw === "") return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
9642
|
-
const parsed = Number(raw);
|
|
9643
|
-
if (!Number.isInteger(parsed) || parsed < 0) return DEFAULT_MEMORY_LIMIT_BYTES;
|
|
10715
|
+
function parseContentLength(value) {
|
|
10716
|
+
if (value === null) return null;
|
|
10717
|
+
const parsed = Number(value);
|
|
10718
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) return null;
|
|
9644
10719
|
return parsed;
|
|
9645
10720
|
}
|
|
9646
|
-
function
|
|
9647
|
-
const
|
|
9648
|
-
if (
|
|
9649
|
-
|
|
9650
|
-
if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
|
|
9651
|
-
return parsed;
|
|
10721
|
+
function isPinnedDnsEnabled(options) {
|
|
10722
|
+
const disabledForTest = typeof process !== "undefined" && process.env["NODE_ENV"] === "test" && process.env["AGENT_INSPECTOR_DISABLE_DNS_PINNING_FOR_TESTS"] === "1";
|
|
10723
|
+
if (disabledForTest) return false;
|
|
10724
|
+
return options.pinDns !== false && options.bypass !== true;
|
|
9652
10725
|
}
|
|
9653
|
-
function
|
|
9654
|
-
return
|
|
9655
|
-
"AGENT_INSPECTOR_OBSERVATION_QUEUE_MESSAGES",
|
|
9656
|
-
DEFAULT_QUEUE_MAX_MESSAGES
|
|
9657
|
-
);
|
|
10726
|
+
function choosePinnedAddress(addresses) {
|
|
10727
|
+
return addresses[0] ?? null;
|
|
9658
10728
|
}
|
|
9659
|
-
function
|
|
9660
|
-
return
|
|
9661
|
-
"AGENT_INSPECTOR_OBSERVATION_QUEUE_BYTES",
|
|
9662
|
-
DEFAULT_QUEUE_MAX_BYTES
|
|
9663
|
-
);
|
|
10729
|
+
function hostHeaderValue(parsed) {
|
|
10730
|
+
return parsed.port.length === 0 ? parsed.host : `${parsed.hostname}:${parsed.port}`;
|
|
9664
10731
|
}
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
}
|
|
9670
|
-
maxMessages;
|
|
9671
|
-
maxBytes;
|
|
9672
|
-
messages = 0;
|
|
9673
|
-
bytes = 0;
|
|
9674
|
-
tryAcquire(bytes) {
|
|
9675
|
-
if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
|
|
9676
|
-
this.messages += 1;
|
|
9677
|
-
this.bytes += bytes;
|
|
9678
|
-
let released = false;
|
|
9679
|
-
return () => {
|
|
9680
|
-
if (released) return;
|
|
9681
|
-
released = true;
|
|
9682
|
-
this.messages -= 1;
|
|
9683
|
-
this.bytes -= bytes;
|
|
9684
|
-
};
|
|
10732
|
+
function headersToRecord$1(headers, parsed) {
|
|
10733
|
+
const normalized = new Headers(headers);
|
|
10734
|
+
if (!normalized.has("host")) {
|
|
10735
|
+
normalized.set("host", hostHeaderValue(parsed));
|
|
9685
10736
|
}
|
|
10737
|
+
const result = {};
|
|
10738
|
+
normalized.forEach((value, key) => {
|
|
10739
|
+
result[key] = value;
|
|
10740
|
+
});
|
|
10741
|
+
return result;
|
|
9686
10742
|
}
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
),
|
|
9692
|
-
resolvePositiveIntegerEnv(
|
|
9693
|
-
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
|
|
9694
|
-
DEFAULT_GLOBAL_QUEUE_MAX_BYTES
|
|
9695
|
-
)
|
|
9696
|
-
);
|
|
9697
|
-
function getRawStreamDir() {
|
|
9698
|
-
return join(getDataDir(), "raw-streams");
|
|
9699
|
-
}
|
|
9700
|
-
function createRawStreamPath() {
|
|
9701
|
-
return join(getRawStreamDir(), `${Date.now()}-${randomUUID()}.stream.tmp`);
|
|
9702
|
-
}
|
|
9703
|
-
class RawStreamCapture {
|
|
9704
|
-
memoryLimitBytes;
|
|
9705
|
-
queueMaxMessages;
|
|
9706
|
-
queueMaxBytes;
|
|
9707
|
-
globalBudget;
|
|
9708
|
-
chunks = [];
|
|
9709
|
-
byteLength = 0;
|
|
9710
|
-
filePath = null;
|
|
9711
|
-
fileHandle = null;
|
|
9712
|
-
pendingFileChunks = [];
|
|
9713
|
-
pendingFileBytes = 0;
|
|
9714
|
-
operation = Promise.resolve();
|
|
9715
|
-
accepting = true;
|
|
9716
|
-
closed = false;
|
|
9717
|
-
sawChunk = false;
|
|
9718
|
-
queuedMessages = 0;
|
|
9719
|
-
queuedBytes = 0;
|
|
9720
|
-
incomplete = false;
|
|
9721
|
-
storageFailed = false;
|
|
9722
|
-
droppedChunks = 0;
|
|
9723
|
-
droppedBytes = 0;
|
|
9724
|
-
gapReason = null;
|
|
9725
|
-
constructor(options = {}) {
|
|
9726
|
-
this.memoryLimitBytes = options.memoryLimitBytes ?? resolveMemoryLimitBytes();
|
|
9727
|
-
this.queueMaxMessages = options.queueMaxMessages ?? defaultQueueMaxMessages();
|
|
9728
|
-
this.queueMaxBytes = options.queueMaxBytes ?? defaultQueueMaxBytes();
|
|
9729
|
-
this.globalBudget = options.globalBudget ?? globalObservationBudget;
|
|
9730
|
-
activeCaptures.add(this);
|
|
9731
|
-
}
|
|
9732
|
-
get hasChunks() {
|
|
9733
|
-
return this.sawChunk;
|
|
10743
|
+
async function requestBodyToUint8Array(body) {
|
|
10744
|
+
if (body === void 0 || body === null) return { ok: true, body: null, contentType: null };
|
|
10745
|
+
if (typeof body === "string") {
|
|
10746
|
+
return { ok: true, body: new TextEncoder().encode(body), contentType: null };
|
|
9734
10747
|
}
|
|
9735
|
-
|
|
10748
|
+
if (body instanceof URLSearchParams) {
|
|
9736
10749
|
return {
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
reason: this.gapReason
|
|
10750
|
+
ok: true,
|
|
10751
|
+
body: new TextEncoder().encode(body.toString()),
|
|
10752
|
+
contentType: "application/x-www-form-urlencoded;charset=UTF-8"
|
|
9741
10753
|
};
|
|
9742
10754
|
}
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
10755
|
+
if (body instanceof Uint8Array) return { ok: true, body, contentType: null };
|
|
10756
|
+
if (body instanceof ArrayBuffer) {
|
|
10757
|
+
return { ok: true, body: new Uint8Array(body), contentType: null };
|
|
9746
10758
|
}
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
const next = this.operation.then(() => this.appendInternal(text));
|
|
9750
|
-
this.operation = next.catch(() => void 0);
|
|
9751
|
-
return next;
|
|
10759
|
+
if (body instanceof Blob) {
|
|
10760
|
+
return { ok: true, body: new Uint8Array(await body.arrayBuffer()), contentType: body.type };
|
|
9752
10761
|
}
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
return false;
|
|
9764
|
-
}
|
|
9765
|
-
if (this.incomplete) {
|
|
9766
|
-
this.recordGap("capacity-exceeded", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
9767
|
-
return false;
|
|
9768
|
-
}
|
|
9769
|
-
if (text === "") return true;
|
|
9770
|
-
if (this.queuedMessages + 1 > this.queueMaxMessages || this.queuedBytes + queuedBytes > this.queueMaxBytes) {
|
|
9771
|
-
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
9772
|
-
return false;
|
|
10762
|
+
return {
|
|
10763
|
+
ok: false,
|
|
10764
|
+
reason: "Pinned upstream fetch only supports buffered request bodies"
|
|
10765
|
+
};
|
|
10766
|
+
}
|
|
10767
|
+
function pinnedLookup(pinnedAddress) {
|
|
10768
|
+
return (_hostname, options, callback) => {
|
|
10769
|
+
if (options.all === true) {
|
|
10770
|
+
callback(null, [pinnedAddress]);
|
|
10771
|
+
return;
|
|
9773
10772
|
}
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
10773
|
+
callback(null, pinnedAddress.address, pinnedAddress.family);
|
|
10774
|
+
};
|
|
10775
|
+
}
|
|
10776
|
+
function requestOptionsForPinnedFetch(parsed, init, pinnedAddress, contentType) {
|
|
10777
|
+
const headers = headersToRecord$1(init.headers, parsed);
|
|
10778
|
+
if (contentType !== null && !new Headers(headers).has("content-type")) {
|
|
10779
|
+
headers["content-type"] = contentType;
|
|
10780
|
+
}
|
|
10781
|
+
return {
|
|
10782
|
+
protocol: parsed.protocol,
|
|
10783
|
+
hostname: parsed.hostname,
|
|
10784
|
+
port: parsed.port,
|
|
10785
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
10786
|
+
method: init.method ?? "GET",
|
|
10787
|
+
headers,
|
|
10788
|
+
lookup: pinnedLookup(pinnedAddress)
|
|
10789
|
+
};
|
|
10790
|
+
}
|
|
10791
|
+
function streamFromIncomingMessage(incoming, maxBytes) {
|
|
10792
|
+
let seenBytes = 0;
|
|
10793
|
+
return new ReadableStream({
|
|
10794
|
+
start(controller) {
|
|
10795
|
+
incoming.on("data", (chunk) => {
|
|
10796
|
+
seenBytes += chunk.byteLength;
|
|
10797
|
+
if (maxBytes !== void 0 && seenBytes > maxBytes) {
|
|
10798
|
+
incoming.destroy(
|
|
10799
|
+
new Error(
|
|
10800
|
+
`Upstream response exceeded configured limit of ${formatByteLimit(maxBytes)}`
|
|
10801
|
+
)
|
|
10802
|
+
);
|
|
10803
|
+
controller.error(
|
|
10804
|
+
new Error(
|
|
10805
|
+
`Upstream response exceeded configured limit of ${formatByteLimit(maxBytes)}`
|
|
10806
|
+
)
|
|
10807
|
+
);
|
|
10808
|
+
return;
|
|
10809
|
+
}
|
|
10810
|
+
controller.enqueue(chunk);
|
|
10811
|
+
});
|
|
10812
|
+
incoming.on("end", () => {
|
|
10813
|
+
controller.close();
|
|
10814
|
+
});
|
|
10815
|
+
incoming.on("error", (error) => {
|
|
10816
|
+
controller.error(error);
|
|
10817
|
+
});
|
|
10818
|
+
},
|
|
10819
|
+
cancel() {
|
|
10820
|
+
incoming.destroy();
|
|
9778
10821
|
}
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
10822
|
+
});
|
|
10823
|
+
}
|
|
10824
|
+
async function cancelBody(response) {
|
|
10825
|
+
if (response.body === null) return;
|
|
10826
|
+
await response.body.cancel().catch(() => void 0);
|
|
10827
|
+
}
|
|
10828
|
+
function responseWithByteLimit(response, maxBytes) {
|
|
10829
|
+
if (response.body === null) return response;
|
|
10830
|
+
let seenBytes = 0;
|
|
10831
|
+
const reader = response.body.getReader();
|
|
10832
|
+
const body = new ReadableStream({
|
|
10833
|
+
async pull(controller) {
|
|
10834
|
+
const chunk = await reader.read();
|
|
10835
|
+
if (chunk.done) {
|
|
10836
|
+
controller.close();
|
|
9784
10837
|
return;
|
|
9785
10838
|
}
|
|
9786
|
-
|
|
9787
|
-
|
|
9788
|
-
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
10839
|
+
seenBytes += chunk.value.byteLength;
|
|
10840
|
+
if (seenBytes > maxBytes) {
|
|
10841
|
+
void reader.cancel().catch(() => void 0);
|
|
10842
|
+
controller.error(
|
|
10843
|
+
new Error(`Upstream response exceeded configured limit of ${formatByteLimit(maxBytes)}`)
|
|
10844
|
+
);
|
|
10845
|
+
return;
|
|
10846
|
+
}
|
|
10847
|
+
controller.enqueue(chunk.value);
|
|
10848
|
+
},
|
|
10849
|
+
cancel(reason) {
|
|
10850
|
+
return reader.cancel(reason);
|
|
10851
|
+
}
|
|
10852
|
+
});
|
|
10853
|
+
return new Response(body, {
|
|
10854
|
+
status: response.status,
|
|
10855
|
+
statusText: response.statusText,
|
|
10856
|
+
headers: response.headers
|
|
10857
|
+
});
|
|
10858
|
+
}
|
|
10859
|
+
function shouldUseBudgetedSignal(init, deadlineMs) {
|
|
10860
|
+
return deadlineMs !== void 0 || init.signal !== void 0;
|
|
10861
|
+
}
|
|
10862
|
+
async function pinnedNodeFetch(rawUrl, init, options, decision) {
|
|
10863
|
+
const parsed = new URL(rawUrl);
|
|
10864
|
+
const pinnedAddress = choosePinnedAddress(decision.addresses);
|
|
10865
|
+
if (pinnedAddress === null) {
|
|
10866
|
+
return {
|
|
10867
|
+
ok: false,
|
|
10868
|
+
kind: "network-policy",
|
|
10869
|
+
reason: `Could not pin a resolved upstream address for "${decision.hostname}"`
|
|
10870
|
+
};
|
|
9796
10871
|
}
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
10872
|
+
const encodedBody = await requestBodyToUint8Array(init.body);
|
|
10873
|
+
if (!encodedBody.ok) {
|
|
10874
|
+
return { ok: false, kind: "resource-limit", reason: encodedBody.reason };
|
|
10875
|
+
}
|
|
10876
|
+
return await new Promise((resolve2, reject) => {
|
|
10877
|
+
const linkedSignal = init.signal ?? void 0;
|
|
10878
|
+
const transport = parsed.protocol === "https:" ? request : request$1;
|
|
10879
|
+
const requestOptions = requestOptionsForPinnedFetch(
|
|
10880
|
+
parsed,
|
|
10881
|
+
init,
|
|
10882
|
+
pinnedAddress,
|
|
10883
|
+
encodedBody.contentType
|
|
9803
10884
|
);
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
10885
|
+
const request$2 = transport(requestOptions, (incoming) => {
|
|
10886
|
+
const status = incoming.statusCode ?? 0;
|
|
10887
|
+
if (isRedirectStatus(status)) {
|
|
10888
|
+
incoming.resume();
|
|
10889
|
+
const location = incoming.headers.location;
|
|
10890
|
+
resolve2({
|
|
10891
|
+
ok: false,
|
|
10892
|
+
kind: "redirect",
|
|
10893
|
+
reason: location === void 0 ? `Upstream returned blocked redirect HTTP ${String(status)}` : `Upstream redirect to "${location}" is blocked; configure the final API URL directly`
|
|
10894
|
+
});
|
|
10895
|
+
return;
|
|
10896
|
+
}
|
|
10897
|
+
const maxBytes = options.responseBytes;
|
|
10898
|
+
const headers = new Headers();
|
|
10899
|
+
for (const [key, value] of Object.entries(incoming.headers)) {
|
|
10900
|
+
if (Array.isArray(value)) {
|
|
10901
|
+
headers.set(key, value.join(", "));
|
|
10902
|
+
continue;
|
|
10903
|
+
}
|
|
10904
|
+
if (value !== void 0) headers.set(key, value);
|
|
10905
|
+
}
|
|
10906
|
+
if (maxBytes !== void 0) {
|
|
10907
|
+
const declaredBytes = parseContentLength(headers.get("content-length"));
|
|
10908
|
+
if (declaredBytes !== null && declaredBytes > maxBytes) {
|
|
10909
|
+
incoming.destroy();
|
|
10910
|
+
resolve2({
|
|
10911
|
+
ok: false,
|
|
10912
|
+
kind: "resource-limit",
|
|
10913
|
+
reason: `Upstream declared ${String(declaredBytes)} response bytes, exceeding configured limit of ${formatByteLimit(maxBytes)}`
|
|
10914
|
+
});
|
|
10915
|
+
return;
|
|
10916
|
+
}
|
|
10917
|
+
}
|
|
10918
|
+
resolve2({
|
|
10919
|
+
ok: true,
|
|
10920
|
+
response: new Response(streamFromIncomingMessage(incoming, maxBytes), {
|
|
10921
|
+
status,
|
|
10922
|
+
statusText: incoming.statusMessage,
|
|
10923
|
+
headers
|
|
10924
|
+
})
|
|
10925
|
+
});
|
|
10926
|
+
});
|
|
10927
|
+
let timedOut = false;
|
|
10928
|
+
let timeout;
|
|
10929
|
+
const finishAbort = () => {
|
|
10930
|
+
request$2.destroy(new DOMException("Safe fetch aborted", "AbortError"));
|
|
10931
|
+
};
|
|
10932
|
+
if (linkedSignal !== void 0) {
|
|
10933
|
+
if (linkedSignal.aborted) {
|
|
10934
|
+
finishAbort();
|
|
10935
|
+
} else {
|
|
10936
|
+
linkedSignal.addEventListener("abort", finishAbort, { once: true });
|
|
10937
|
+
}
|
|
9814
10938
|
}
|
|
9815
|
-
if (
|
|
9816
|
-
|
|
10939
|
+
if (options.deadlineMs !== void 0) {
|
|
10940
|
+
timeout = setTimeout(() => {
|
|
10941
|
+
timedOut = true;
|
|
10942
|
+
request$2.destroy(new DOMException("Safe fetch deadline exceeded", "TimeoutError"));
|
|
10943
|
+
}, options.deadlineMs);
|
|
9817
10944
|
}
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
10945
|
+
request$2.on("error", (error) => {
|
|
10946
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
10947
|
+
if (linkedSignal !== void 0) {
|
|
10948
|
+
linkedSignal.removeEventListener("abort", finishAbort);
|
|
10949
|
+
}
|
|
10950
|
+
if (timedOut) {
|
|
10951
|
+
resolve2({
|
|
10952
|
+
ok: false,
|
|
10953
|
+
kind: "timeout",
|
|
10954
|
+
reason: `Upstream request exceeded configured deadline of ${String(options.deadlineMs)} ms`
|
|
10955
|
+
});
|
|
10956
|
+
return;
|
|
10957
|
+
}
|
|
10958
|
+
reject(error);
|
|
10959
|
+
});
|
|
10960
|
+
request$2.on("close", () => {
|
|
10961
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
10962
|
+
if (linkedSignal !== void 0) {
|
|
10963
|
+
linkedSignal.removeEventListener("abort", finishAbort);
|
|
10964
|
+
}
|
|
10965
|
+
});
|
|
10966
|
+
if (encodedBody.body !== null) request$2.write(encodedBody.body);
|
|
10967
|
+
request$2.end();
|
|
10968
|
+
});
|
|
10969
|
+
}
|
|
10970
|
+
async function safeFetch(rawUrl, init = {}, options = {}) {
|
|
10971
|
+
const resolvedDecision = await evaluateResolvedUpstreamUrlForFetch(rawUrl, options);
|
|
10972
|
+
if (!resolvedDecision.allow) {
|
|
10973
|
+
return { ok: false, kind: "network-policy", reason: resolvedDecision.reason };
|
|
10974
|
+
}
|
|
10975
|
+
if (isPinnedDnsEnabled(options)) {
|
|
10976
|
+
return await pinnedNodeFetch(rawUrl, init, options, resolvedDecision);
|
|
10977
|
+
}
|
|
10978
|
+
const deadlineMs = options.deadlineMs;
|
|
10979
|
+
const useBudgetedSignal = shouldUseBudgetedSignal(init, deadlineMs);
|
|
10980
|
+
const controller = useBudgetedSignal ? new AbortController() : void 0;
|
|
10981
|
+
let timedOut = false;
|
|
10982
|
+
let timeout;
|
|
10983
|
+
const linkedSignal = init.signal ?? void 0;
|
|
10984
|
+
const forwardAbort = () => {
|
|
10985
|
+
controller?.abort(linkedSignal?.reason);
|
|
10986
|
+
};
|
|
10987
|
+
if (controller !== void 0 && linkedSignal !== void 0) {
|
|
10988
|
+
if (linkedSignal.aborted) {
|
|
10989
|
+
forwardAbort();
|
|
10990
|
+
} else {
|
|
10991
|
+
linkedSignal.addEventListener("abort", forwardAbort, { once: true });
|
|
9823
10992
|
}
|
|
9824
10993
|
}
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
10994
|
+
if (controller !== void 0 && deadlineMs !== void 0) {
|
|
10995
|
+
timeout = setTimeout(() => {
|
|
10996
|
+
timedOut = true;
|
|
10997
|
+
controller.abort(new DOMException("Safe fetch deadline exceeded", "TimeoutError"));
|
|
10998
|
+
}, deadlineMs);
|
|
9830
10999
|
}
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
11000
|
+
try {
|
|
11001
|
+
const response = await fetch(rawUrl, {
|
|
11002
|
+
...init,
|
|
11003
|
+
redirect: "manual",
|
|
11004
|
+
signal: controller?.signal ?? init.signal
|
|
11005
|
+
});
|
|
11006
|
+
if (!isRedirectStatus(response.status)) {
|
|
11007
|
+
const maxBytes = options.responseBytes;
|
|
11008
|
+
if (maxBytes === void 0) return { ok: true, response };
|
|
11009
|
+
const declaredBytes = parseContentLength(response.headers.get("content-length"));
|
|
11010
|
+
if (declaredBytes !== null && declaredBytes > maxBytes) {
|
|
11011
|
+
await cancelBody(response);
|
|
11012
|
+
return {
|
|
11013
|
+
ok: false,
|
|
11014
|
+
kind: "resource-limit",
|
|
11015
|
+
reason: `Upstream declared ${String(declaredBytes)} response bytes, exceeding configured limit of ${formatByteLimit(maxBytes)}`
|
|
11016
|
+
};
|
|
9843
11017
|
}
|
|
11018
|
+
return { ok: true, response: responseWithByteLimit(response, maxBytes) };
|
|
9844
11019
|
}
|
|
9845
|
-
|
|
9846
|
-
|
|
11020
|
+
await cancelBody(response);
|
|
11021
|
+
const location = response.headers.get("location");
|
|
11022
|
+
return {
|
|
11023
|
+
ok: false,
|
|
11024
|
+
kind: "redirect",
|
|
11025
|
+
reason: location === null ? `Upstream returned blocked redirect HTTP ${String(response.status)}` : `Upstream redirect to "${location}" is blocked; configure the final API URL directly`
|
|
11026
|
+
};
|
|
11027
|
+
} catch (error) {
|
|
11028
|
+
if (timedOut) {
|
|
11029
|
+
return {
|
|
11030
|
+
ok: false,
|
|
11031
|
+
kind: "timeout",
|
|
11032
|
+
reason: deadlineMs === void 0 ? "Upstream request exceeded its configured deadline" : `Upstream request exceeded configured deadline of ${String(deadlineMs)} ms`
|
|
11033
|
+
};
|
|
11034
|
+
}
|
|
11035
|
+
return Promise.reject(error);
|
|
11036
|
+
} finally {
|
|
11037
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
11038
|
+
if (controller !== void 0 && linkedSignal !== void 0) {
|
|
11039
|
+
linkedSignal.removeEventListener("abort", forwardAbort);
|
|
9847
11040
|
}
|
|
9848
|
-
return { type: "memory", rawStream: this.chunks.join("") };
|
|
9849
11041
|
}
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
11042
|
+
}
|
|
11043
|
+
const BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";
|
|
11044
|
+
function isClaudeCodeBillingHeaderBlock(text) {
|
|
11045
|
+
const trimmed = text.trimStart();
|
|
11046
|
+
return trimmed.toLowerCase().startsWith(BILLING_HEADER_PREFIX);
|
|
11047
|
+
}
|
|
11048
|
+
function isObjectWithSystem(value) {
|
|
11049
|
+
if (!isPlainRecord(value)) return false;
|
|
11050
|
+
return Object.prototype.hasOwnProperty.call(value, "system");
|
|
11051
|
+
}
|
|
11052
|
+
function isBillingHeaderTextBlock(block) {
|
|
11053
|
+
if (!isPlainRecord(block)) return false;
|
|
11054
|
+
const typeVal = safeGetOwnProperty(block, "type");
|
|
11055
|
+
const textVal = safeGetOwnProperty(block, "text");
|
|
11056
|
+
if (typeof typeVal !== "string" || typeVal !== "text") return false;
|
|
11057
|
+
if (typeof textVal !== "string") return false;
|
|
11058
|
+
return isClaudeCodeBillingHeaderBlock(textVal);
|
|
11059
|
+
}
|
|
11060
|
+
function stripClaudeCodeBillingHeader(rawBody) {
|
|
11061
|
+
let parsed;
|
|
11062
|
+
try {
|
|
11063
|
+
parsed = JSON.parse(rawBody);
|
|
11064
|
+
} catch {
|
|
11065
|
+
return { body: rawBody, removed: 0 };
|
|
11066
|
+
}
|
|
11067
|
+
if (!isObjectWithSystem(parsed)) {
|
|
11068
|
+
return { body: rawBody, removed: 0 };
|
|
11069
|
+
}
|
|
11070
|
+
const system = parsed.system;
|
|
11071
|
+
if (!Array.isArray(system) || system.length === 0) {
|
|
11072
|
+
return { body: rawBody, removed: 0 };
|
|
11073
|
+
}
|
|
11074
|
+
const kept = [];
|
|
11075
|
+
let removed = 0;
|
|
11076
|
+
for (const block of system) {
|
|
11077
|
+
if (isBillingHeaderTextBlock(block)) {
|
|
11078
|
+
removed += 1;
|
|
11079
|
+
continue;
|
|
9868
11080
|
}
|
|
11081
|
+
kept.push(block);
|
|
9869
11082
|
}
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
const text = this.pendingFileChunks.join("");
|
|
9873
|
-
this.pendingFileChunks = [];
|
|
9874
|
-
this.pendingFileBytes = 0;
|
|
9875
|
-
await this.fileHandle.write(text, void 0, "utf-8");
|
|
11083
|
+
if (removed === 0) {
|
|
11084
|
+
return { body: rawBody, removed: 0 };
|
|
9876
11085
|
}
|
|
11086
|
+
if (kept.length === 0) {
|
|
11087
|
+
delete parsed.system;
|
|
11088
|
+
} else {
|
|
11089
|
+
parsed.system = kept;
|
|
11090
|
+
}
|
|
11091
|
+
return { body: JSON.stringify(parsed), removed };
|
|
9877
11092
|
}
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
const
|
|
9884
|
-
|
|
9885
|
-
if (activeRequests !== 0) return;
|
|
9886
|
-
for (const resolve2 of drainWaiters) resolve2();
|
|
9887
|
-
drainWaiters.clear();
|
|
11093
|
+
const ROLE_ASSISTANT = "assistant";
|
|
11094
|
+
const ROLE_TOOL = "tool";
|
|
11095
|
+
function isObjectWithMessages(value) {
|
|
11096
|
+
if (!isPlainRecord(value)) return false;
|
|
11097
|
+
if (!Object.prototype.hasOwnProperty.call(value, "messages")) return false;
|
|
11098
|
+
const messages = safeGetOwnProperty(value, "messages");
|
|
11099
|
+
return Array.isArray(messages);
|
|
9888
11100
|
}
|
|
9889
|
-
function
|
|
9890
|
-
if (!
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
notifyDrained();
|
|
11101
|
+
function collectAssistantToolCallIds(message, into) {
|
|
11102
|
+
if (!isPlainRecord(message)) return;
|
|
11103
|
+
const toolCalls = safeGetOwnProperty(message, "tool_calls");
|
|
11104
|
+
if (!Array.isArray(toolCalls)) return;
|
|
11105
|
+
for (const tc of toolCalls) {
|
|
11106
|
+
if (!isPlainRecord(tc)) continue;
|
|
11107
|
+
const id = safeGetOwnProperty(tc, "id");
|
|
11108
|
+
if (typeof id === "string" && id.length > 0) {
|
|
11109
|
+
into.add(id);
|
|
9899
11110
|
}
|
|
9900
|
-
}
|
|
9901
|
-
}
|
|
9902
|
-
function stopRuntimeAdmission() {
|
|
9903
|
-
acceptingRequests = false;
|
|
9904
|
-
notifyDrained();
|
|
11111
|
+
}
|
|
9905
11112
|
}
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
11113
|
+
function findOrphanToolMessageIndices(messages) {
|
|
11114
|
+
const knownToolCallIds = /* @__PURE__ */ new Set();
|
|
11115
|
+
const indices = [];
|
|
11116
|
+
const orphanIds = [];
|
|
11117
|
+
for (let i = 0; i < messages.length; i++) {
|
|
11118
|
+
const msg = messages[i];
|
|
11119
|
+
if (!isPlainRecord(msg)) continue;
|
|
11120
|
+
const role = safeGetOwnProperty(msg, "role");
|
|
11121
|
+
if (role === ROLE_ASSISTANT) {
|
|
11122
|
+
collectAssistantToolCallIds(msg, knownToolCallIds);
|
|
11123
|
+
continue;
|
|
11124
|
+
}
|
|
11125
|
+
if (role === ROLE_TOOL) {
|
|
11126
|
+
const id = safeGetOwnProperty(msg, "tool_call_id");
|
|
11127
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
11128
|
+
indices.push(i);
|
|
11129
|
+
orphanIds.push(null);
|
|
11130
|
+
continue;
|
|
11131
|
+
}
|
|
11132
|
+
if (!knownToolCallIds.has(id)) {
|
|
11133
|
+
indices.push(i);
|
|
11134
|
+
orphanIds.push(id);
|
|
11135
|
+
}
|
|
11136
|
+
}
|
|
11137
|
+
}
|
|
11138
|
+
return { indices, orphanIds };
|
|
9911
11139
|
}
|
|
9912
|
-
function
|
|
9913
|
-
|
|
11140
|
+
function stripOpenAIOrphanToolMessages(rawBody) {
|
|
11141
|
+
let parsed;
|
|
11142
|
+
try {
|
|
11143
|
+
parsed = JSON.parse(rawBody);
|
|
11144
|
+
} catch {
|
|
11145
|
+
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
11146
|
+
}
|
|
11147
|
+
if (!isObjectWithMessages(parsed)) {
|
|
11148
|
+
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
11149
|
+
}
|
|
11150
|
+
const messages = parsed.messages;
|
|
11151
|
+
const { indices, orphanIds } = findOrphanToolMessageIndices(messages);
|
|
11152
|
+
if (indices.length === 0) {
|
|
11153
|
+
return { body: rawBody, removed: 0, orphanIds: [] };
|
|
11154
|
+
}
|
|
11155
|
+
const dropSet = new Set(indices);
|
|
11156
|
+
const kept = [];
|
|
11157
|
+
for (let i = 0; i < messages.length; i++) {
|
|
11158
|
+
if (!dropSet.has(i)) kept.push(messages[i]);
|
|
11159
|
+
}
|
|
11160
|
+
parsed.messages = kept;
|
|
11161
|
+
return { body: JSON.stringify(parsed), removed: indices.length, orphanIds };
|
|
9914
11162
|
}
|
|
9915
11163
|
const PATH_V1_CHAT_COMPLETIONS = "/v1/chat/completions";
|
|
9916
11164
|
function buildUpstreamUrl(upstreamBase, normalizedPath) {
|
|
@@ -10400,10 +11648,14 @@ async function handleAdmittedProxy(req) {
|
|
|
10400
11648
|
body: bodyToForward,
|
|
10401
11649
|
signal: req.signal
|
|
10402
11650
|
},
|
|
10403
|
-
{
|
|
11651
|
+
{
|
|
11652
|
+
allowLoopback: ALLOW_LOOPBACK$2,
|
|
11653
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.fetchDeadlineMs,
|
|
11654
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.fetchResponseBytes
|
|
11655
|
+
}
|
|
10404
11656
|
);
|
|
10405
11657
|
if (!fetchResult.ok) {
|
|
10406
|
-
const status = fetchResult.kind === "network-policy" ? STATUS_FORBIDDEN : STATUS_BAD_GATEWAY;
|
|
11658
|
+
const status = fetchResult.kind === "network-policy" ? STATUS_FORBIDDEN : fetchResult.kind === "resource-limit" ? STATUS_PAYLOAD_TOO_LARGE : STATUS_BAD_GATEWAY;
|
|
10407
11659
|
logger.warn(`[handler] Safe fetch blocked ${upstreamUrl}: ${fetchResult.reason}`);
|
|
10408
11660
|
log.elapsedMs = Date.now() - startTime;
|
|
10409
11661
|
log.responseStatus = status;
|
|
@@ -10411,7 +11663,7 @@ async function handleAdmittedProxy(req) {
|
|
|
10411
11663
|
appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: fetchResult.reason });
|
|
10412
11664
|
await persistFinalizedLogUpdate(log);
|
|
10413
11665
|
return new Response(
|
|
10414
|
-
fetchResult.kind === "network-policy" ? "Forbidden: upstream URL is not permitted" : "Proxy error: upstream redirects are not permitted",
|
|
11666
|
+
fetchResult.kind === "network-policy" ? "Forbidden: upstream URL is not permitted" : fetchResult.kind === "resource-limit" ? "Proxy error: upstream response exceeded configured limits" : fetchResult.kind === "timeout" ? "Proxy error: upstream request timed out" : "Proxy error: upstream redirects are not permitted",
|
|
10415
11667
|
{ status }
|
|
10416
11668
|
);
|
|
10417
11669
|
}
|
|
@@ -10513,23 +11765,62 @@ async function handleProxy(req) {
|
|
|
10513
11765
|
const Route$Q = createFileRoute("/proxy/$")({
|
|
10514
11766
|
server: {
|
|
10515
11767
|
handlers: {
|
|
10516
|
-
GET: ({ request }) => handleProxy(
|
|
10517
|
-
POST: ({ request }) => handleProxy(
|
|
10518
|
-
PUT: ({ request }) => handleProxy(
|
|
10519
|
-
DELETE: ({ request }) => handleProxy(
|
|
10520
|
-
PATCH: ({ request }) => handleProxy(
|
|
10521
|
-
OPTIONS: ({ request }) => handleProxy(
|
|
11768
|
+
GET: ({ request: request2 }) => handleProxy(request2),
|
|
11769
|
+
POST: ({ request: request2 }) => handleProxy(request2),
|
|
11770
|
+
PUT: ({ request: request2 }) => handleProxy(request2),
|
|
11771
|
+
DELETE: ({ request: request2 }) => handleProxy(request2),
|
|
11772
|
+
PATCH: ({ request: request2 }) => handleProxy(request2),
|
|
11773
|
+
OPTIONS: ({ request: request2 }) => handleProxy(request2)
|
|
11774
|
+
}
|
|
11775
|
+
}
|
|
11776
|
+
});
|
|
11777
|
+
function summarizeGcResult(result) {
|
|
11778
|
+
if (result === null) return null;
|
|
11779
|
+
return {
|
|
11780
|
+
checkedAt: result.checkedAt,
|
|
11781
|
+
filesDeleted: result.filesDeleted,
|
|
11782
|
+
bytesDeleted: result.bytesDeleted,
|
|
11783
|
+
errors: result.errors.length,
|
|
11784
|
+
beforeTotalBytes: result.before.totalBytes,
|
|
11785
|
+
afterTotalBytes: result.after.totalBytes
|
|
11786
|
+
};
|
|
11787
|
+
}
|
|
11788
|
+
function getStorageResponse() {
|
|
11789
|
+
const logStats = getLogStorageStats();
|
|
11790
|
+
const inventory = getStorageLifecycleInventory();
|
|
11791
|
+
const policy = resolveStorageLifecycleQuotaPolicy();
|
|
11792
|
+
const reclaimableBytes = inventory.artifacts.filter(
|
|
11793
|
+
(artifact) => artifact.cleanupBehavior === "retention-gc" || artifact.cleanupBehavior === "orphan-reclaim"
|
|
11794
|
+
).reduce((sum, artifact) => sum + artifact.bytes, 0);
|
|
11795
|
+
return {
|
|
11796
|
+
memoryCount: logStats.memoryCount,
|
|
11797
|
+
inventory,
|
|
11798
|
+
policy,
|
|
11799
|
+
reclaimableBytes,
|
|
11800
|
+
lastGc: summarizeGcResult(getLastStorageLifecycleGcResult())
|
|
11801
|
+
};
|
|
11802
|
+
}
|
|
11803
|
+
const Route$P = createFileRoute("/api/storage")({
|
|
11804
|
+
server: {
|
|
11805
|
+
handlers: {
|
|
11806
|
+
GET: () => {
|
|
11807
|
+
return Response.json(getStorageResponse(), { headers: { "cache-control": "no-store" } });
|
|
11808
|
+
},
|
|
11809
|
+
POST: () => {
|
|
11810
|
+
runStorageLifecycleGc();
|
|
11811
|
+
return Response.json(getStorageResponse(), { headers: { "cache-control": "no-store" } });
|
|
11812
|
+
}
|
|
10522
11813
|
}
|
|
10523
11814
|
}
|
|
10524
11815
|
});
|
|
10525
11816
|
function parseBooleanFlag(value) {
|
|
10526
11817
|
return value === "1" || value === "true";
|
|
10527
11818
|
}
|
|
10528
|
-
const Route$
|
|
11819
|
+
const Route$O = createFileRoute("/api/sessions")({
|
|
10529
11820
|
server: {
|
|
10530
11821
|
handlers: {
|
|
10531
|
-
GET: async ({ request }) => {
|
|
10532
|
-
const url2 = new URL(
|
|
11822
|
+
GET: async ({ request: request2 }) => {
|
|
11823
|
+
const url2 = new URL(request2.url);
|
|
10533
11824
|
const sessionId = url2.searchParams.get("sessionId");
|
|
10534
11825
|
if (sessionId !== null) {
|
|
10535
11826
|
if (sessionId.length === 0) {
|
|
@@ -10658,7 +11949,7 @@ function runtimeDiscoveryResponse(env = process.env) {
|
|
|
10658
11949
|
headers: { "cache-control": "no-store" }
|
|
10659
11950
|
});
|
|
10660
11951
|
}
|
|
10661
|
-
const Route$
|
|
11952
|
+
const Route$N = createFileRoute("/api/runtime")({
|
|
10662
11953
|
server: {
|
|
10663
11954
|
handlers: {
|
|
10664
11955
|
GET: () => runtimeDiscoveryResponse()
|
|
@@ -12067,20 +13358,20 @@ function readInspectorRunEvidence(runId) {
|
|
|
12067
13358
|
}
|
|
12068
13359
|
return serviceOk({ run, evidence: run.evidence, markdown });
|
|
12069
13360
|
}
|
|
12070
|
-
async function readJsonBody$6(
|
|
13361
|
+
async function readJsonBody$6(request2) {
|
|
12071
13362
|
try {
|
|
12072
|
-
const raw = await
|
|
13363
|
+
const raw = await request2.text();
|
|
12073
13364
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
12074
13365
|
return { ok: true, value: JSON.parse(raw) };
|
|
12075
13366
|
} catch {
|
|
12076
13367
|
return { ok: false };
|
|
12077
13368
|
}
|
|
12078
13369
|
}
|
|
12079
|
-
const Route$
|
|
13370
|
+
const Route$M = createFileRoute("/api/runs")({
|
|
12080
13371
|
server: {
|
|
12081
13372
|
handlers: {
|
|
12082
|
-
GET: ({ request }) => {
|
|
12083
|
-
const url2 = new URL(
|
|
13373
|
+
GET: ({ request: request2 }) => {
|
|
13374
|
+
const url2 = new URL(request2.url);
|
|
12084
13375
|
if (url2.searchParams.get("failures") === "1") {
|
|
12085
13376
|
const limitResult = parseBoundedIntegerParameter({
|
|
12086
13377
|
value: url2.searchParams.get("limit"),
|
|
@@ -12095,8 +13386,8 @@ const Route$N = createFileRoute("/api/runs")({
|
|
|
12095
13386
|
}
|
|
12096
13387
|
return Response.json(listInspectorRuns());
|
|
12097
13388
|
},
|
|
12098
|
-
POST: async ({ request }) => {
|
|
12099
|
-
const body = await readJsonBody$6(
|
|
13389
|
+
POST: async ({ request: request2 }) => {
|
|
13390
|
+
const body = await readJsonBody$6(request2);
|
|
12100
13391
|
if (!body.ok) {
|
|
12101
13392
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
12102
13393
|
}
|
|
@@ -12128,16 +13419,16 @@ const ProviderInputSchema = object({
|
|
|
12128
13419
|
modelMetadata: ProviderModelMetadataListSchema.optional(),
|
|
12129
13420
|
source: _enum(["company", "personal"]).optional()
|
|
12130
13421
|
});
|
|
12131
|
-
const Route$
|
|
13422
|
+
const Route$L = createFileRoute("/api/providers")({
|
|
12132
13423
|
server: {
|
|
12133
13424
|
handlers: {
|
|
12134
13425
|
GET: () => {
|
|
12135
13426
|
return Response.json(redactProviders(getProviders()));
|
|
12136
13427
|
},
|
|
12137
|
-
POST: async ({ request }) => {
|
|
13428
|
+
POST: async ({ request: request2 }) => {
|
|
12138
13429
|
let body;
|
|
12139
13430
|
try {
|
|
12140
|
-
body = await
|
|
13431
|
+
body = await request2.json();
|
|
12141
13432
|
} catch {
|
|
12142
13433
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
12143
13434
|
}
|
|
@@ -13756,8 +15047,8 @@ function firstModel(provider) {
|
|
|
13756
15047
|
}
|
|
13757
15048
|
return void 0;
|
|
13758
15049
|
}
|
|
13759
|
-
function providerCandidates(
|
|
13760
|
-
if (
|
|
15050
|
+
function providerCandidates(request2) {
|
|
15051
|
+
if (request2.providerId === void 0) {
|
|
13761
15052
|
return getProviders().toSorted((left, right) => {
|
|
13762
15053
|
const leftCompanyRank = left.source === "company" ? 0 : 1;
|
|
13763
15054
|
const rightCompanyRank = right.source === "company" ? 0 : 1;
|
|
@@ -13765,12 +15056,12 @@ function providerCandidates(request) {
|
|
|
13765
15056
|
return left.name.localeCompare(right.name);
|
|
13766
15057
|
});
|
|
13767
15058
|
}
|
|
13768
|
-
const provider = getProvider(
|
|
15059
|
+
const provider = getProvider(request2.providerId);
|
|
13769
15060
|
return provider === void 0 ? [] : [provider];
|
|
13770
15061
|
}
|
|
13771
|
-
function selectProvider(
|
|
13772
|
-
for (const provider of providerCandidates(
|
|
13773
|
-
const modelName =
|
|
15062
|
+
function selectProvider(request2) {
|
|
15063
|
+
for (const provider of providerCandidates(request2)) {
|
|
15064
|
+
const modelName = request2.model ?? firstModel(provider);
|
|
13774
15065
|
if (modelName === void 0) continue;
|
|
13775
15066
|
const responsesBaseUrl = nonEmpty$1(provider.openaiResponsesBaseUrl);
|
|
13776
15067
|
if (responsesBaseUrl !== void 0) {
|
|
@@ -13813,8 +15104,8 @@ function truncateContext(value, maxLength) {
|
|
|
13813
15104
|
return `${value.slice(0, maxLength)}
|
|
13814
15105
|
...[truncated ${String(value.length - maxLength)} chars]`;
|
|
13815
15106
|
}
|
|
13816
|
-
function lastUserQuestion(
|
|
13817
|
-
const history =
|
|
15107
|
+
function lastUserQuestion(request2) {
|
|
15108
|
+
const history = request2.history ?? [];
|
|
13818
15109
|
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
13819
15110
|
const message = history[index];
|
|
13820
15111
|
if (message !== void 0 && message.role === "user") return message.content;
|
|
@@ -13835,13 +15126,13 @@ function emptySummary() {
|
|
|
13835
15126
|
sourceCounts: { log: 0, knowledge: 0, alert: 0, run: 0, group: 0, provider: 0 }
|
|
13836
15127
|
};
|
|
13837
15128
|
}
|
|
13838
|
-
async function retrieveInspectorContext(
|
|
15129
|
+
async function retrieveInspectorContext(request2) {
|
|
13839
15130
|
try {
|
|
13840
15131
|
return await retrievePiAgentEvidence({
|
|
13841
|
-
question:
|
|
13842
|
-
sessionId:
|
|
13843
|
-
recentUserQuestion: lastUserQuestion(
|
|
13844
|
-
limits:
|
|
15132
|
+
question: request2.question,
|
|
15133
|
+
sessionId: request2.sessionId,
|
|
15134
|
+
recentUserQuestion: lastUserQuestion(request2),
|
|
15135
|
+
limits: request2.logLimit === void 0 ? void 0 : { maxEvidence: request2.logLimit }
|
|
13845
15136
|
});
|
|
13846
15137
|
} catch (err) {
|
|
13847
15138
|
return {
|
|
@@ -13991,11 +15282,11 @@ function lastAssistantMessage(messages) {
|
|
|
13991
15282
|
}
|
|
13992
15283
|
return null;
|
|
13993
15284
|
}
|
|
13994
|
-
function boundedHistory(
|
|
13995
|
-
const history = (
|
|
15285
|
+
function boundedHistory(request2) {
|
|
15286
|
+
const history = (request2.history ?? []).slice(-12).map((message) => `${message.role}: ${message.content.slice(0, MAX_HISTORY_CHARS)}`);
|
|
13996
15287
|
return history.length > 0 ? history.join("\n\n") : "No prior conversation in this request.";
|
|
13997
15288
|
}
|
|
13998
|
-
function buildContext(
|
|
15289
|
+
function buildContext(request2, retrieval) {
|
|
13999
15290
|
return {
|
|
14000
15291
|
systemPrompt: buildSystemPrompt(),
|
|
14001
15292
|
messages: [
|
|
@@ -14003,16 +15294,16 @@ function buildContext(request, retrieval) {
|
|
|
14003
15294
|
role: "user",
|
|
14004
15295
|
content: [
|
|
14005
15296
|
"Bounded conversation history:",
|
|
14006
|
-
boundedHistory(
|
|
15297
|
+
boundedHistory(request2),
|
|
14007
15298
|
"",
|
|
14008
|
-
buildUserPrompt(
|
|
15299
|
+
buildUserPrompt(request2.question, retrieval)
|
|
14009
15300
|
].join("\n"),
|
|
14010
15301
|
timestamp: Date.now()
|
|
14011
15302
|
}
|
|
14012
15303
|
]
|
|
14013
15304
|
};
|
|
14014
15305
|
}
|
|
14015
|
-
async function askPi(selected,
|
|
15306
|
+
async function askPi(selected, request2, retrieval) {
|
|
14016
15307
|
const { Agent } = await import(
|
|
14017
15308
|
/* @vite-ignore */
|
|
14018
15309
|
"../_libs/mariozechner__pi-agent-core.mjs"
|
|
@@ -14028,7 +15319,7 @@ async function askPi(selected, request, retrieval) {
|
|
|
14028
15319
|
sessionId: selected.provider.id,
|
|
14029
15320
|
maxRetryDelayMs: 1e4
|
|
14030
15321
|
});
|
|
14031
|
-
const context = buildContext(
|
|
15322
|
+
const context = buildContext(request2, retrieval);
|
|
14032
15323
|
await agent.prompt(context.messages);
|
|
14033
15324
|
const assistantMessage = lastAssistantMessage(agent.state.messages);
|
|
14034
15325
|
if (assistantMessage === null) {
|
|
@@ -14038,9 +15329,9 @@ async function askPi(selected, request, retrieval) {
|
|
|
14038
15329
|
if (answer !== "") return stripReasoningTags(answer);
|
|
14039
15330
|
return assistantMessage.errorMessage ?? "Inspector Assistant received an empty provider response.";
|
|
14040
15331
|
}
|
|
14041
|
-
async function runPiAgent(
|
|
14042
|
-
const retrieval = await retrieveInspectorContext(
|
|
14043
|
-
const selected = selectProvider(
|
|
15332
|
+
async function runPiAgent(request2) {
|
|
15333
|
+
const retrieval = await retrieveInspectorContext(request2);
|
|
15334
|
+
const selected = selectProvider(request2);
|
|
14044
15335
|
if (selected === null) {
|
|
14045
15336
|
return {
|
|
14046
15337
|
answer: "Inspector Assistant needs at least one Provider with OpenAI Responses, OpenAI Chat, or Anthropic Messages configured.",
|
|
@@ -14056,7 +15347,7 @@ async function runPiAgent(request) {
|
|
|
14056
15347
|
}
|
|
14057
15348
|
let answer;
|
|
14058
15349
|
try {
|
|
14059
|
-
answer = await askPi(selected,
|
|
15350
|
+
answer = await askPi(selected, request2, retrieval);
|
|
14060
15351
|
} catch (err) {
|
|
14061
15352
|
answer = `Inspector Assistant provider request failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
14062
15353
|
}
|
|
@@ -14072,13 +15363,13 @@ async function runPiAgent(request) {
|
|
|
14072
15363
|
queryStats: retrieval.queryStats
|
|
14073
15364
|
};
|
|
14074
15365
|
}
|
|
14075
|
-
const Route$
|
|
15366
|
+
const Route$K = createFileRoute("/api/pi-agent")({
|
|
14076
15367
|
server: {
|
|
14077
15368
|
handlers: {
|
|
14078
|
-
POST: async ({ request }) => {
|
|
15369
|
+
POST: async ({ request: request2 }) => {
|
|
14079
15370
|
let body;
|
|
14080
15371
|
try {
|
|
14081
|
-
body = await
|
|
15372
|
+
body = await request2.json();
|
|
14082
15373
|
} catch {
|
|
14083
15374
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
14084
15375
|
}
|
|
@@ -14095,7 +15386,7 @@ const Route$L = createFileRoute("/api/pi-agent")({
|
|
|
14095
15386
|
}
|
|
14096
15387
|
}
|
|
14097
15388
|
});
|
|
14098
|
-
const Route$
|
|
15389
|
+
const Route$J = createFileRoute("/api/models")({
|
|
14099
15390
|
server: {
|
|
14100
15391
|
handlers: {
|
|
14101
15392
|
GET: () => Response.json(getModels())
|
|
@@ -14103,7 +15394,7 @@ const Route$K = createFileRoute("/api/models")({
|
|
|
14103
15394
|
}
|
|
14104
15395
|
});
|
|
14105
15396
|
const name = "@tonyclaw/agent-inspector";
|
|
14106
|
-
const version = "3.1.
|
|
15397
|
+
const version = "3.1.22";
|
|
14107
15398
|
const packageJson = {
|
|
14108
15399
|
name,
|
|
14109
15400
|
version
|
|
@@ -14440,8 +15731,8 @@ async function getInstanceConnectionImpl(callApi2, name2) {
|
|
|
14440
15731
|
}
|
|
14441
15732
|
async function startInstanceImpl(callApi2, input) {
|
|
14442
15733
|
const path2 = instancePath(input.name, "/start");
|
|
14443
|
-
const
|
|
14444
|
-
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(
|
|
15734
|
+
const request2 = lifecycleRequest(input);
|
|
15735
|
+
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(request2)));
|
|
14445
15736
|
return validatedInstanceResponse(response, InstanceOperationOutputSchema, `POST ${path2}`);
|
|
14446
15737
|
}
|
|
14447
15738
|
async function stopInstanceImpl(callApi2, name2) {
|
|
@@ -14451,8 +15742,8 @@ async function stopInstanceImpl(callApi2, name2) {
|
|
|
14451
15742
|
}
|
|
14452
15743
|
async function restartInstanceImpl(callApi2, input) {
|
|
14453
15744
|
const path2 = instancePath(input.name, "/restart");
|
|
14454
|
-
const
|
|
14455
|
-
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(
|
|
15745
|
+
const request2 = lifecycleRequest(input);
|
|
15746
|
+
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(request2)));
|
|
14456
15747
|
return validatedInstanceResponse(response, InstanceOperationOutputSchema, `POST ${path2}`);
|
|
14457
15748
|
}
|
|
14458
15749
|
function groupEvidenceDir(groupId) {
|
|
@@ -16111,10 +17402,10 @@ async function getServer() {
|
|
|
16111
17402
|
}
|
|
16112
17403
|
return initPromise;
|
|
16113
17404
|
}
|
|
16114
|
-
async function handleMcpRequest(
|
|
16115
|
-
return runWithRequestPublicBasePath(
|
|
17405
|
+
async function handleMcpRequest(request2) {
|
|
17406
|
+
return runWithRequestPublicBasePath(request2, async () => {
|
|
16116
17407
|
const { transport } = await getServer();
|
|
16117
|
-
return transport.handleRequest(
|
|
17408
|
+
return transport.handleRequest(request2);
|
|
16118
17409
|
});
|
|
16119
17410
|
}
|
|
16120
17411
|
const TOOL_LIST_LOGS_DESC = `List recent captured agent/model proxy logs in reverse-chronological order with parsed previews. Useful for "what did I just send?" discovery.
|
|
@@ -17764,15 +19055,15 @@ const PROMPT_NAMES = [
|
|
|
17764
19055
|
"inspector_generate_group_report",
|
|
17765
19056
|
"inspector_extract_repro_steps"
|
|
17766
19057
|
];
|
|
17767
|
-
const Route$
|
|
19058
|
+
const Route$I = createFileRoute("/api/mcp")({
|
|
17768
19059
|
server: {
|
|
17769
19060
|
handlers: {
|
|
17770
19061
|
// The SDK may issue either POST, GET, or DELETE. TanStack Start only
|
|
17771
19062
|
// requires us to declare the methods we accept; routing by method is
|
|
17772
19063
|
// handled inside the transport.
|
|
17773
|
-
POST: ({ request }) => handleMcpRequest(
|
|
17774
|
-
GET: ({ request }) => handleMcpRequest(
|
|
17775
|
-
DELETE: ({ request }) => handleMcpRequest(
|
|
19064
|
+
POST: ({ request: request2 }) => handleMcpRequest(request2),
|
|
19065
|
+
GET: ({ request: request2 }) => handleMcpRequest(request2),
|
|
19066
|
+
DELETE: ({ request: request2 }) => handleMcpRequest(request2)
|
|
17776
19067
|
}
|
|
17777
19068
|
}
|
|
17778
19069
|
});
|
|
@@ -17882,11 +19173,11 @@ function parseOptionalLogId(value) {
|
|
|
17882
19173
|
const parsed = parseLogId(value);
|
|
17883
19174
|
return parsed === null ? { ok: false } : { ok: true, value: parsed };
|
|
17884
19175
|
}
|
|
17885
|
-
const Route$
|
|
19176
|
+
const Route$H = createFileRoute("/api/logs")({
|
|
17886
19177
|
server: {
|
|
17887
19178
|
handlers: {
|
|
17888
|
-
GET: async ({ request }) => {
|
|
17889
|
-
const url2 = new URL(
|
|
19179
|
+
GET: async ({ request: request2 }) => {
|
|
19180
|
+
const url2 = new URL(request2.url);
|
|
17890
19181
|
if (url2.searchParams.get("stats") === "1") {
|
|
17891
19182
|
return Response.json(getLogStorageStats());
|
|
17892
19183
|
}
|
|
@@ -17990,10 +19281,10 @@ const Route$I = createFileRoute("/api/logs")({
|
|
|
17990
19281
|
await listLogsPage({ sessionId, model, clientPid, offset, limit, includeBodies })
|
|
17991
19282
|
);
|
|
17992
19283
|
},
|
|
17993
|
-
DELETE: async ({ request }) => {
|
|
19284
|
+
DELETE: async ({ request: request2 }) => {
|
|
17994
19285
|
let body = void 0;
|
|
17995
19286
|
try {
|
|
17996
|
-
const raw = await
|
|
19287
|
+
const raw = await request2.text();
|
|
17997
19288
|
if (raw !== "") {
|
|
17998
19289
|
const parsed = JSON.parse(raw);
|
|
17999
19290
|
const result2 = DeleteLogsBodySchema.safeParse(parsed);
|
|
@@ -19800,10 +21091,10 @@ function instanceErrorResponse(error) {
|
|
|
19800
21091
|
function instanceResultResponse(result, successStatus = 200) {
|
|
19801
21092
|
return result.kind === "error" ? instanceErrorResponse(result.error) : Response.json(result.value, { status: successStatus, headers: NO_STORE_HEADERS$1 });
|
|
19802
21093
|
}
|
|
19803
|
-
async function parseInstanceBody(
|
|
21094
|
+
async function parseInstanceBody(request2, schema) {
|
|
19804
21095
|
let value;
|
|
19805
21096
|
try {
|
|
19806
|
-
const text = await
|
|
21097
|
+
const text = await request2.text();
|
|
19807
21098
|
value = text.trim().length === 0 ? {} : JSON.parse(text);
|
|
19808
21099
|
} catch {
|
|
19809
21100
|
return {
|
|
@@ -19842,7 +21133,7 @@ function instanceListResponse(instances) {
|
|
|
19842
21133
|
{ headers: NO_STORE_HEADERS$1 }
|
|
19843
21134
|
);
|
|
19844
21135
|
}
|
|
19845
|
-
const Route$
|
|
21136
|
+
const Route$G = createFileRoute("/api/instances")({
|
|
19846
21137
|
server: {
|
|
19847
21138
|
handlers: {
|
|
19848
21139
|
GET: async () => {
|
|
@@ -20152,6 +21443,11 @@ const tasks = /* @__PURE__ */ new Map();
|
|
|
20152
21443
|
const activeTasks = /* @__PURE__ */ new Map();
|
|
20153
21444
|
let acceptingTasks = true;
|
|
20154
21445
|
let shutdownForceTimer = null;
|
|
21446
|
+
function activeTaskLimit() {
|
|
21447
|
+
const configured = Number(process.env["ECOSYSTEM_TASK_ACTIVE_LIMIT"]);
|
|
21448
|
+
if (Number.isSafeInteger(configured) && configured > 0) return configured;
|
|
21449
|
+
return DEFAULT_RUNTIME_BUDGET_POLICY.queues.backgroundWorkers;
|
|
21450
|
+
}
|
|
20155
21451
|
function nowIso() {
|
|
20156
21452
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
20157
21453
|
}
|
|
@@ -20346,6 +21642,7 @@ function listEcosystemTasks() {
|
|
|
20346
21642
|
}
|
|
20347
21643
|
function startEcosystemTask(packageId, action) {
|
|
20348
21644
|
if (!acceptingTasks) return null;
|
|
21645
|
+
if (activeTasks.size >= activeTaskLimit()) return null;
|
|
20349
21646
|
if (action === "recipe") return null;
|
|
20350
21647
|
const definition = action === "runner-presets" ? findEcosystemPackage("eval-harness") : findEcosystemPackage(packageId);
|
|
20351
21648
|
if (definition === null) return null;
|
|
@@ -20372,6 +21669,7 @@ function startEcosystemTask(packageId, action) {
|
|
|
20372
21669
|
}
|
|
20373
21670
|
function startEcosystemRecipeTask(recipeId) {
|
|
20374
21671
|
if (!acceptingTasks) return null;
|
|
21672
|
+
if (activeTasks.size >= activeTaskLimit()) return null;
|
|
20375
21673
|
const recipe = findEcosystemRecipe(recipeId);
|
|
20376
21674
|
if (recipe === null) return null;
|
|
20377
21675
|
if (!recipe.runnable) return null;
|
|
@@ -20534,7 +21832,7 @@ function installRuntimeShutdownSignalHandlers() {
|
|
|
20534
21832
|
}
|
|
20535
21833
|
installRuntimeShutdownSignalHandlers();
|
|
20536
21834
|
logger.debug("Health endpoint loaded");
|
|
20537
|
-
const Route$
|
|
21835
|
+
const Route$F = createFileRoute("/api/health")({
|
|
20538
21836
|
server: {
|
|
20539
21837
|
handlers: {
|
|
20540
21838
|
GET: async () => {
|
|
@@ -20547,18 +21845,18 @@ const Route$G = createFileRoute("/api/health")({
|
|
|
20547
21845
|
}
|
|
20548
21846
|
}
|
|
20549
21847
|
});
|
|
20550
|
-
async function readJsonBody$5(
|
|
21848
|
+
async function readJsonBody$5(request2) {
|
|
20551
21849
|
try {
|
|
20552
|
-
const raw = await
|
|
21850
|
+
const raw = await request2.text();
|
|
20553
21851
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
20554
21852
|
return { ok: true, value: JSON.parse(raw) };
|
|
20555
21853
|
} catch {
|
|
20556
21854
|
return { ok: false };
|
|
20557
21855
|
}
|
|
20558
21856
|
}
|
|
20559
|
-
function readDeleteEvidenceFlag$1(
|
|
21857
|
+
function readDeleteEvidenceFlag$1(request2) {
|
|
20560
21858
|
try {
|
|
20561
|
-
const value = new URL(
|
|
21859
|
+
const value = new URL(request2.url).searchParams.get("deleteEvidence");
|
|
20562
21860
|
switch (value) {
|
|
20563
21861
|
case null:
|
|
20564
21862
|
return { ok: true, value: void 0 };
|
|
@@ -20575,12 +21873,12 @@ function readDeleteEvidenceFlag$1(request) {
|
|
|
20575
21873
|
return { ok: false };
|
|
20576
21874
|
}
|
|
20577
21875
|
}
|
|
20578
|
-
const Route$
|
|
21876
|
+
const Route$E = createFileRoute("/api/groups")({
|
|
20579
21877
|
server: {
|
|
20580
21878
|
handlers: {
|
|
20581
21879
|
GET: () => Response.json(listInspectorGroups()),
|
|
20582
|
-
POST: async ({ request }) => {
|
|
20583
|
-
const body = await readJsonBody$5(
|
|
21880
|
+
POST: async ({ request: request2 }) => {
|
|
21881
|
+
const body = await readJsonBody$5(request2);
|
|
20584
21882
|
if (!body.ok) {
|
|
20585
21883
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
20586
21884
|
}
|
|
@@ -20593,8 +21891,8 @@ const Route$F = createFileRoute("/api/groups")({
|
|
|
20593
21891
|
}
|
|
20594
21892
|
return Response.json(createInspectorGroup(parsed.data), { status: 201 });
|
|
20595
21893
|
},
|
|
20596
|
-
DELETE: ({ request }) => {
|
|
20597
|
-
const deleteEvidence = readDeleteEvidenceFlag$1(
|
|
21894
|
+
DELETE: ({ request: request2 }) => {
|
|
21895
|
+
const deleteEvidence = readDeleteEvidenceFlag$1(request2);
|
|
20598
21896
|
if (!deleteEvidence.ok) {
|
|
20599
21897
|
return Response.json(
|
|
20600
21898
|
{ error: "deleteEvidence must be one of true, false, 1, or 0" },
|
|
@@ -20619,16 +21917,16 @@ const RuntimeConfigPatchSchema = object({
|
|
|
20619
21917
|
}).strict().refine((v) => Object.keys(v).length > 0, {
|
|
20620
21918
|
message: "At least one field must be provided"
|
|
20621
21919
|
});
|
|
20622
|
-
const Route$
|
|
21920
|
+
const Route$D = createFileRoute("/api/config")({
|
|
20623
21921
|
server: {
|
|
20624
21922
|
handlers: {
|
|
20625
21923
|
GET: () => {
|
|
20626
21924
|
return Response.json(getConfig());
|
|
20627
21925
|
},
|
|
20628
|
-
PATCH: async ({ request }) => {
|
|
21926
|
+
PATCH: async ({ request: request2 }) => {
|
|
20629
21927
|
let body;
|
|
20630
21928
|
try {
|
|
20631
|
-
body = await
|
|
21929
|
+
body = await request2.json();
|
|
20632
21930
|
} catch {
|
|
20633
21931
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
20634
21932
|
}
|
|
@@ -20733,11 +22031,11 @@ function parseFilters(url2) {
|
|
|
20733
22031
|
source: sourceResult.success ? sourceResult.data : void 0
|
|
20734
22032
|
};
|
|
20735
22033
|
}
|
|
20736
|
-
const Route$
|
|
22034
|
+
const Route$C = createFileRoute("/api/alerts")({
|
|
20737
22035
|
server: {
|
|
20738
22036
|
handlers: {
|
|
20739
|
-
GET: async ({ request }) => {
|
|
20740
|
-
const url2 = new URL(
|
|
22037
|
+
GET: async ({ request: request2 }) => {
|
|
22038
|
+
const url2 = new URL(request2.url);
|
|
20741
22039
|
const limitResult = parseBoundedIntegerParameter({
|
|
20742
22040
|
value: url2.searchParams.get("limit"),
|
|
20743
22041
|
parameter: "limit",
|
|
@@ -20767,16 +22065,16 @@ const Route$D = createFileRoute("/api/alerts")({
|
|
|
20767
22065
|
}
|
|
20768
22066
|
}
|
|
20769
22067
|
});
|
|
20770
|
-
async function readJsonBody$4(
|
|
22068
|
+
async function readJsonBody$4(request2) {
|
|
20771
22069
|
try {
|
|
20772
|
-
const raw = await
|
|
22070
|
+
const raw = await request2.text();
|
|
20773
22071
|
if (raw.trim().length === 0) return { ok: true, value: {} };
|
|
20774
22072
|
return { ok: true, value: JSON.parse(raw) };
|
|
20775
22073
|
} catch {
|
|
20776
22074
|
return { ok: false };
|
|
20777
22075
|
}
|
|
20778
22076
|
}
|
|
20779
|
-
const Route$
|
|
22077
|
+
const Route$B = createFileRoute("/api/runs/$runId")({
|
|
20780
22078
|
server: {
|
|
20781
22079
|
handlers: {
|
|
20782
22080
|
GET: ({ params }) => {
|
|
@@ -20786,8 +22084,8 @@ const Route$C = createFileRoute("/api/runs/$runId")({
|
|
|
20786
22084
|
}
|
|
20787
22085
|
return Response.json(run.value);
|
|
20788
22086
|
},
|
|
20789
|
-
PATCH: async ({ params, request }) => {
|
|
20790
|
-
const body = await readJsonBody$4(
|
|
22087
|
+
PATCH: async ({ params, request: request2 }) => {
|
|
22088
|
+
const body = await readJsonBody$4(request2);
|
|
20791
22089
|
if (!body.ok) {
|
|
20792
22090
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
20793
22091
|
}
|
|
@@ -21309,7 +22607,7 @@ function consumeProviderScan(scanId, selectionIds, now = Date.now()) {
|
|
|
21309
22607
|
}
|
|
21310
22608
|
return selected;
|
|
21311
22609
|
}
|
|
21312
|
-
const Route$
|
|
22610
|
+
const Route$A = createFileRoute("/api/providers/scan")({
|
|
21313
22611
|
server: {
|
|
21314
22612
|
handlers: {
|
|
21315
22613
|
POST: () => {
|
|
@@ -21355,12 +22653,12 @@ const ProviderScanImportRequestSchema = object({
|
|
|
21355
22653
|
selectionIds: array(string().uuid()).min(1).max(100)
|
|
21356
22654
|
});
|
|
21357
22655
|
const ProviderFileImportSchema = union([string(), object({ providers: unknown() })]);
|
|
21358
|
-
const Route$
|
|
22656
|
+
const Route$z = createFileRoute("/api/providers/import")({
|
|
21359
22657
|
server: {
|
|
21360
22658
|
handlers: {
|
|
21361
|
-
POST: async ({ request }) => {
|
|
22659
|
+
POST: async ({ request: request2 }) => {
|
|
21362
22660
|
try {
|
|
21363
|
-
const rawBody = await
|
|
22661
|
+
const rawBody = await request2.text();
|
|
21364
22662
|
let jsonContent;
|
|
21365
22663
|
try {
|
|
21366
22664
|
const parsedBody = JSON.parse(rawBody);
|
|
@@ -21427,7 +22725,7 @@ function exportResponse(json, filename) {
|
|
|
21427
22725
|
}
|
|
21428
22726
|
});
|
|
21429
22727
|
}
|
|
21430
|
-
const Route$
|
|
22728
|
+
const Route$y = createFileRoute("/api/providers/export")({
|
|
21431
22729
|
server: {
|
|
21432
22730
|
handlers: {
|
|
21433
22731
|
GET: () => {
|
|
@@ -21436,8 +22734,8 @@ const Route$z = createFileRoute("/api/providers/export")({
|
|
|
21436
22734
|
`agent-inspector-providers-safe-${Date.now()}.json`
|
|
21437
22735
|
);
|
|
21438
22736
|
},
|
|
21439
|
-
POST: async ({ request }) => {
|
|
21440
|
-
const body = await
|
|
22737
|
+
POST: async ({ request: request2 }) => {
|
|
22738
|
+
const body = await request2.json().catch(() => null);
|
|
21441
22739
|
const parsed = SecretExportRequestSchema.safeParse(body);
|
|
21442
22740
|
if (!parsed.success) {
|
|
21443
22741
|
return Response.json(
|
|
@@ -21470,7 +22768,7 @@ const ProviderUpdateSchema = object({
|
|
|
21470
22768
|
modelMetadata: ProviderModelMetadataListSchema.optional(),
|
|
21471
22769
|
source: _enum(["company", "personal"]).optional()
|
|
21472
22770
|
});
|
|
21473
|
-
const Route$
|
|
22771
|
+
const Route$x = createFileRoute("/api/providers/$providerId")({
|
|
21474
22772
|
server: {
|
|
21475
22773
|
handlers: {
|
|
21476
22774
|
GET: ({ params }) => {
|
|
@@ -21481,10 +22779,10 @@ const Route$y = createFileRoute("/api/providers/$providerId")({
|
|
|
21481
22779
|
}
|
|
21482
22780
|
return Response.json(redactProvider(provider));
|
|
21483
22781
|
},
|
|
21484
|
-
PUT: async ({ params, request }) => {
|
|
22782
|
+
PUT: async ({ params, request: request2 }) => {
|
|
21485
22783
|
let body;
|
|
21486
22784
|
try {
|
|
21487
|
-
body = await
|
|
22785
|
+
body = await request2.json();
|
|
21488
22786
|
} catch {
|
|
21489
22787
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
21490
22788
|
}
|
|
@@ -21535,139 +22833,6 @@ const Route$y = createFileRoute("/api/providers/$providerId")({
|
|
|
21535
22833
|
}
|
|
21536
22834
|
}
|
|
21537
22835
|
});
|
|
21538
|
-
const INITIAL_STREAM_LOG_LIMIT = 100;
|
|
21539
|
-
const UPDATE_COALESCE_MS = 50;
|
|
21540
|
-
const sseClientLimiter = createCapacityLimiter(MAX_SSE_CLIENTS);
|
|
21541
|
-
function streamIncludesBodies(url2) {
|
|
21542
|
-
return url2.searchParams.get("compact") === "0";
|
|
21543
|
-
}
|
|
21544
|
-
function parseLastEventLogId(value) {
|
|
21545
|
-
if (value === null) return void 0;
|
|
21546
|
-
return parseLogId(value) ?? void 0;
|
|
21547
|
-
}
|
|
21548
|
-
function encodeSse(value, id) {
|
|
21549
|
-
const idLine = id === void 0 ? "" : `id: ${String(id)}
|
|
21550
|
-
`;
|
|
21551
|
-
return new TextEncoder().encode(`${idLine}data: ${JSON.stringify(value)}
|
|
21552
|
-
|
|
21553
|
-
`);
|
|
21554
|
-
}
|
|
21555
|
-
function createLogsStreamResponse(request, clientLimiter = sseClientLimiter) {
|
|
21556
|
-
const lastEventIdHeader = request.headers.get("last-event-id");
|
|
21557
|
-
const lastEventLogId = parseLastEventLogId(lastEventIdHeader);
|
|
21558
|
-
if (lastEventIdHeader !== null && lastEventLogId === void 0) {
|
|
21559
|
-
return Response.json({ error: "Invalid Last-Event-ID" }, { status: 400 });
|
|
21560
|
-
}
|
|
21561
|
-
const lease = clientLimiter.tryAcquire();
|
|
21562
|
-
if (lease === null) {
|
|
21563
|
-
return resourceLimitResponse({
|
|
21564
|
-
error: `SSE client capacity reached. At most ${String(clientLimiter.capacity)} live clients are allowed; retry after a connection closes.`,
|
|
21565
|
-
code: "sse-capacity-exceeded",
|
|
21566
|
-
status: 503,
|
|
21567
|
-
resource: "sse-clients",
|
|
21568
|
-
limit: clientLimiter.capacity,
|
|
21569
|
-
retryable: true,
|
|
21570
|
-
retryAfterSeconds: 1
|
|
21571
|
-
});
|
|
21572
|
-
}
|
|
21573
|
-
const url2 = new URL(request.url);
|
|
21574
|
-
const sessionId = url2.searchParams.get("sessionId") ?? void 0;
|
|
21575
|
-
const model = url2.searchParams.get("model") ?? void 0;
|
|
21576
|
-
const includeBodies = streamIncludesBodies(url2);
|
|
21577
|
-
let controllerRef = null;
|
|
21578
|
-
let cleanedUp = false;
|
|
21579
|
-
let updateTimer = null;
|
|
21580
|
-
const pendingUpdates = /* @__PURE__ */ new Map();
|
|
21581
|
-
const flushPendingUpdates = () => {
|
|
21582
|
-
updateTimer = null;
|
|
21583
|
-
const controller = controllerRef;
|
|
21584
|
-
if (controller === null) return;
|
|
21585
|
-
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
21586
|
-
controller.close();
|
|
21587
|
-
cleanup();
|
|
21588
|
-
return;
|
|
21589
|
-
}
|
|
21590
|
-
for (const log of pendingUpdates.values()) {
|
|
21591
|
-
if (controller.desiredSize !== null && controller.desiredSize <= -16) {
|
|
21592
|
-
controller.close();
|
|
21593
|
-
cleanup();
|
|
21594
|
-
return;
|
|
21595
|
-
}
|
|
21596
|
-
const payloadLog = includeBodies ? log : compactLogForList(log);
|
|
21597
|
-
controller.enqueue(encodeSse({ type: "update", log: payloadLog }, log.id));
|
|
21598
|
-
}
|
|
21599
|
-
pendingUpdates.clear();
|
|
21600
|
-
};
|
|
21601
|
-
const unsubscribe = onLogUpdate((log) => {
|
|
21602
|
-
if (controllerRef === null) return;
|
|
21603
|
-
if (sessionId !== void 0 && getLogSessionId(log) !== sessionId) return;
|
|
21604
|
-
if (model !== void 0 && log.model !== model) return;
|
|
21605
|
-
pendingUpdates.set(log.id, log);
|
|
21606
|
-
if (updateTimer === null) updateTimer = setTimeout(flushPendingUpdates, UPDATE_COALESCE_MS);
|
|
21607
|
-
});
|
|
21608
|
-
const cleanup = () => {
|
|
21609
|
-
if (cleanedUp) return;
|
|
21610
|
-
cleanedUp = true;
|
|
21611
|
-
clearInterval(heartbeat);
|
|
21612
|
-
if (updateTimer !== null) clearTimeout(updateTimer);
|
|
21613
|
-
updateTimer = null;
|
|
21614
|
-
pendingUpdates.clear();
|
|
21615
|
-
unsubscribe();
|
|
21616
|
-
lease.release();
|
|
21617
|
-
controllerRef = null;
|
|
21618
|
-
request.signal.removeEventListener("abort", cleanup);
|
|
21619
|
-
};
|
|
21620
|
-
const heartbeat = setInterval(() => {
|
|
21621
|
-
if (controllerRef !== null) {
|
|
21622
|
-
try {
|
|
21623
|
-
controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
|
|
21624
|
-
} catch {
|
|
21625
|
-
cleanup();
|
|
21626
|
-
}
|
|
21627
|
-
}
|
|
21628
|
-
}, 3e4);
|
|
21629
|
-
request.signal.addEventListener("abort", cleanup, { once: true });
|
|
21630
|
-
const stream = new ReadableStream({
|
|
21631
|
-
async start(controller) {
|
|
21632
|
-
controllerRef = controller;
|
|
21633
|
-
try {
|
|
21634
|
-
const result = await listLogsCursorPage({
|
|
21635
|
-
sessionId,
|
|
21636
|
-
model,
|
|
21637
|
-
limit: INITIAL_STREAM_LOG_LIMIT,
|
|
21638
|
-
includeBodies,
|
|
21639
|
-
afterLogId: lastEventLogId,
|
|
21640
|
-
anchor: lastEventLogId === void 0 ? "newest" : void 0
|
|
21641
|
-
});
|
|
21642
|
-
if (cleanedUp) return;
|
|
21643
|
-
const logs = result.logs;
|
|
21644
|
-
const newestLogId = logs.at(-1)?.id;
|
|
21645
|
-
controller.enqueue(encodeSse({ type: "init", logs }, newestLogId));
|
|
21646
|
-
} catch (error) {
|
|
21647
|
-
cleanup();
|
|
21648
|
-
controller.error(error);
|
|
21649
|
-
}
|
|
21650
|
-
},
|
|
21651
|
-
cancel() {
|
|
21652
|
-
cleanup();
|
|
21653
|
-
}
|
|
21654
|
-
});
|
|
21655
|
-
return new Response(stream, {
|
|
21656
|
-
headers: {
|
|
21657
|
-
"Content-Type": "text/event-stream",
|
|
21658
|
-
"Cache-Control": "no-cache",
|
|
21659
|
-
Connection: "keep-alive",
|
|
21660
|
-
"X-Accel-Buffering": "no"
|
|
21661
|
-
}
|
|
21662
|
-
});
|
|
21663
|
-
}
|
|
21664
|
-
const Route$x = createFileRoute("/api/logs/stream")({
|
|
21665
|
-
server: {
|
|
21666
|
-
handlers: {
|
|
21667
|
-
GET: ({ request }) => createLogsStreamResponse(request)
|
|
21668
|
-
}
|
|
21669
|
-
}
|
|
21670
|
-
});
|
|
21671
22836
|
const DEFAULT_LOG_IMPORT_LIMITS = {
|
|
21672
22837
|
maxCompressedBytes: 200 * 1024 * 1024,
|
|
21673
22838
|
maxEntries: 1e4,
|
|
@@ -22269,8 +23434,8 @@ function declaredContentLength(value) {
|
|
|
22269
23434
|
const parsed = Number.parseInt(value, 10);
|
|
22270
23435
|
return Number.isSafeInteger(parsed) ? parsed : "invalid";
|
|
22271
23436
|
}
|
|
22272
|
-
async function readBoundedBody(
|
|
22273
|
-
const declaredBytes = declaredContentLength(
|
|
23437
|
+
async function readBoundedBody(request2, maxBytes) {
|
|
23438
|
+
const declaredBytes = declaredContentLength(request2.headers.get("content-length"));
|
|
22274
23439
|
if (declaredBytes === "invalid") {
|
|
22275
23440
|
return { ok: false, status: 400, message: "Invalid Content-Length header" };
|
|
22276
23441
|
}
|
|
@@ -22281,7 +23446,7 @@ async function readBoundedBody(request, maxBytes) {
|
|
|
22281
23446
|
message: `Import request exceeds maximum size of ${String(maxBytes)} bytes.`
|
|
22282
23447
|
};
|
|
22283
23448
|
}
|
|
22284
|
-
const body =
|
|
23449
|
+
const body = request2.body;
|
|
22285
23450
|
if (body === null) {
|
|
22286
23451
|
return { ok: false, status: 400, message: "Expected multipart form data" };
|
|
22287
23452
|
}
|
|
@@ -22317,14 +23482,14 @@ async function readBoundedBody(request, maxBytes) {
|
|
|
22317
23482
|
function isUploadedFile(value) {
|
|
22318
23483
|
return value instanceof File;
|
|
22319
23484
|
}
|
|
22320
|
-
async function readBoundedLogImportUpload(
|
|
22321
|
-
const contentType =
|
|
23485
|
+
async function readBoundedLogImportUpload(request2, options = {}) {
|
|
23486
|
+
const contentType = request2.headers.get("content-type");
|
|
22322
23487
|
if (contentType === null || !contentType.toLowerCase().startsWith("multipart/form-data")) {
|
|
22323
23488
|
return { ok: false, status: 400, message: "Expected multipart form data" };
|
|
22324
23489
|
}
|
|
22325
23490
|
const maxFileBytes = options.maxFileBytes ?? DEFAULT_LOG_IMPORT_LIMITS.maxCompressedBytes;
|
|
22326
23491
|
const maxRequestBytes = options.maxRequestBytes ?? MAX_LOG_IMPORT_REQUEST_BYTES;
|
|
22327
|
-
const body = await readBoundedBody(
|
|
23492
|
+
const body = await readBoundedBody(request2, maxRequestBytes);
|
|
22328
23493
|
if (!body.ok) return body;
|
|
22329
23494
|
let formData;
|
|
22330
23495
|
try {
|
|
@@ -22354,8 +23519,8 @@ async function readBoundedLogImportUpload(request, options = {}) {
|
|
|
22354
23519
|
const Route$w = createFileRoute("/api/logs/import")({
|
|
22355
23520
|
server: {
|
|
22356
23521
|
handlers: {
|
|
22357
|
-
POST: async ({ request }) => {
|
|
22358
|
-
const upload = await readBoundedLogImportUpload(
|
|
23522
|
+
POST: async ({ request: request2 }) => {
|
|
23523
|
+
const upload = await readBoundedLogImportUpload(request2);
|
|
22359
23524
|
if (!upload.ok) {
|
|
22360
23525
|
return Response.json({ error: upload.message }, { status: upload.status });
|
|
22361
23526
|
}
|
|
@@ -22620,7 +23785,11 @@ async function searchOpenClawGateway(query, project) {
|
|
|
22620
23785
|
sessionKey: getOpenClawSessionKey()
|
|
22621
23786
|
})
|
|
22622
23787
|
},
|
|
22623
|
-
{
|
|
23788
|
+
{
|
|
23789
|
+
bypass: true,
|
|
23790
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
23791
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
23792
|
+
}
|
|
22624
23793
|
);
|
|
22625
23794
|
if (!fetchResult.ok) return { results: [], warning: fetchResult.reason };
|
|
22626
23795
|
const response = fetchResult.response;
|
|
@@ -22823,7 +23992,11 @@ async function promoteToOpenClaw(candidate) {
|
|
|
22823
23992
|
headers: authHeaders(),
|
|
22824
23993
|
body: JSON.stringify(buildOpenClawPayload(candidate))
|
|
22825
23994
|
},
|
|
22826
|
-
{
|
|
23995
|
+
{
|
|
23996
|
+
bypass: true,
|
|
23997
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
23998
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
23999
|
+
}
|
|
22827
24000
|
);
|
|
22828
24001
|
if (!fetchResult.ok) return { success: false, error: fetchResult.reason };
|
|
22829
24002
|
const response = fetchResult.response;
|
|
@@ -22855,7 +24028,11 @@ async function searchOpenClaw(query, project) {
|
|
|
22855
24028
|
method: "GET",
|
|
22856
24029
|
headers: authHeaders()
|
|
22857
24030
|
},
|
|
22858
|
-
{
|
|
24031
|
+
{
|
|
24032
|
+
bypass: true,
|
|
24033
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
24034
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
24035
|
+
}
|
|
22859
24036
|
);
|
|
22860
24037
|
if (!fetchResult.ok) return { results: [], warning: fetchResult.reason };
|
|
22861
24038
|
const response = fetchResult.response;
|
|
@@ -22895,8 +24072,8 @@ function bodySearchQuery(value) {
|
|
|
22895
24072
|
const Route$u = createFileRoute("/api/knowledge/search")({
|
|
22896
24073
|
server: {
|
|
22897
24074
|
handlers: {
|
|
22898
|
-
GET: async ({ request }) => {
|
|
22899
|
-
const url2 = new URL(
|
|
24075
|
+
GET: async ({ request: request2 }) => {
|
|
24076
|
+
const url2 = new URL(request2.url);
|
|
22900
24077
|
const query = url2.searchParams.get("q") ?? "";
|
|
22901
24078
|
const project = url2.searchParams.get("project") ?? void 0;
|
|
22902
24079
|
if (query === "") {
|
|
@@ -22905,10 +24082,10 @@ const Route$u = createFileRoute("/api/knowledge/search")({
|
|
|
22905
24082
|
if (query.length > MAX_SEARCH_QUERY_CHARS) return searchLimitResponse();
|
|
22906
24083
|
return Response.json(await searchOpenClaw(query, project));
|
|
22907
24084
|
},
|
|
22908
|
-
POST: async ({ request }) => {
|
|
24085
|
+
POST: async ({ request: request2 }) => {
|
|
22909
24086
|
let body;
|
|
22910
24087
|
try {
|
|
22911
|
-
body = await
|
|
24088
|
+
body = await request2.json();
|
|
22912
24089
|
} catch {
|
|
22913
24090
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
22914
24091
|
}
|
|
@@ -22928,8 +24105,8 @@ const Route$u = createFileRoute("/api/knowledge/search")({
|
|
|
22928
24105
|
const Route$t = createFileRoute("/api/knowledge/project-context")({
|
|
22929
24106
|
server: {
|
|
22930
24107
|
handlers: {
|
|
22931
|
-
GET: async ({ request }) => {
|
|
22932
|
-
const url2 = new URL(
|
|
24108
|
+
GET: async ({ request: request2 }) => {
|
|
24109
|
+
const url2 = new URL(request2.url);
|
|
22933
24110
|
const project = url2.searchParams.get("project") ?? "";
|
|
22934
24111
|
if (project === "") {
|
|
22935
24112
|
return Response.json({ error: "Missing project search parameter" }, { status: 400 });
|
|
@@ -22964,18 +24141,18 @@ const Route$r = createFileRoute("/api/instances/$name")({
|
|
|
22964
24141
|
}
|
|
22965
24142
|
}
|
|
22966
24143
|
});
|
|
22967
|
-
async function readJsonBody$3(
|
|
24144
|
+
async function readJsonBody$3(request2) {
|
|
22968
24145
|
try {
|
|
22969
|
-
const raw = await
|
|
24146
|
+
const raw = await request2.text();
|
|
22970
24147
|
if (raw.trim().length === 0) return { ok: true, value: {} };
|
|
22971
24148
|
return { ok: true, value: JSON.parse(raw) };
|
|
22972
24149
|
} catch {
|
|
22973
24150
|
return { ok: false };
|
|
22974
24151
|
}
|
|
22975
24152
|
}
|
|
22976
|
-
function readDeleteEvidenceFlag(
|
|
24153
|
+
function readDeleteEvidenceFlag(request2) {
|
|
22977
24154
|
try {
|
|
22978
|
-
const value = new URL(
|
|
24155
|
+
const value = new URL(request2.url).searchParams.get("deleteEvidence");
|
|
22979
24156
|
switch (value) {
|
|
22980
24157
|
case null:
|
|
22981
24158
|
return { ok: true, value: void 0 };
|
|
@@ -23002,8 +24179,8 @@ const Route$q = createFileRoute("/api/groups/$groupId")({
|
|
|
23002
24179
|
}
|
|
23003
24180
|
return Response.json(group.value);
|
|
23004
24181
|
},
|
|
23005
|
-
PATCH: async ({ params, request }) => {
|
|
23006
|
-
const body = await readJsonBody$3(
|
|
24182
|
+
PATCH: async ({ params, request: request2 }) => {
|
|
24183
|
+
const body = await readJsonBody$3(request2);
|
|
23007
24184
|
if (!body.ok) {
|
|
23008
24185
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
23009
24186
|
}
|
|
@@ -23020,8 +24197,8 @@ const Route$q = createFileRoute("/api/groups/$groupId")({
|
|
|
23020
24197
|
}
|
|
23021
24198
|
return Response.json(updated.value);
|
|
23022
24199
|
},
|
|
23023
|
-
DELETE: ({ params, request }) => {
|
|
23024
|
-
const deleteEvidence = readDeleteEvidenceFlag(
|
|
24200
|
+
DELETE: ({ params, request: request2 }) => {
|
|
24201
|
+
const deleteEvidence = readDeleteEvidenceFlag(request2);
|
|
23025
24202
|
if (!deleteEvidence.ok) {
|
|
23026
24203
|
return Response.json(
|
|
23027
24204
|
{ error: "deleteEvidence must be one of true, false, 1, or 0" },
|
|
@@ -23076,8 +24253,8 @@ const Route$n = createFileRoute("/api/config/paths")({
|
|
|
23076
24253
|
const Route$m = createFileRoute("/api/alerts/summary")({
|
|
23077
24254
|
server: {
|
|
23078
24255
|
handlers: {
|
|
23079
|
-
GET: async ({ request }) => {
|
|
23080
|
-
const url2 = new URL(
|
|
24256
|
+
GET: async ({ request: request2 }) => {
|
|
24257
|
+
const url2 = new URL(request2.url);
|
|
23081
24258
|
const scanLimitResult = parseBoundedIntegerParameter({
|
|
23082
24259
|
value: url2.searchParams.get("scanLimit"),
|
|
23083
24260
|
parameter: "scanLimit",
|
|
@@ -23096,9 +24273,9 @@ const Route$m = createFileRoute("/api/alerts/summary")({
|
|
|
23096
24273
|
}
|
|
23097
24274
|
}
|
|
23098
24275
|
});
|
|
23099
|
-
async function readJsonBody$2(
|
|
24276
|
+
async function readJsonBody$2(request2) {
|
|
23100
24277
|
try {
|
|
23101
|
-
const raw = await
|
|
24278
|
+
const raw = await request2.text();
|
|
23102
24279
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
23103
24280
|
return { ok: true, value: JSON.parse(raw) };
|
|
23104
24281
|
} catch {
|
|
@@ -23115,8 +24292,8 @@ const Route$l = createFileRoute("/api/runs/$runId/evidence")({
|
|
|
23115
24292
|
}
|
|
23116
24293
|
return Response.json(evidence.value);
|
|
23117
24294
|
},
|
|
23118
|
-
POST: async ({ params, request }) => {
|
|
23119
|
-
const body = await readJsonBody$2(
|
|
24295
|
+
POST: async ({ params, request: request2 }) => {
|
|
24296
|
+
const body = await readJsonBody$2(request2);
|
|
23120
24297
|
if (!body.ok) {
|
|
23121
24298
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
23122
24299
|
}
|
|
@@ -23130,7 +24307,7 @@ const Route$l = createFileRoute("/api/runs/$runId/evidence")({
|
|
|
23130
24307
|
const exported = await exportInspectorRunEvidence(
|
|
23131
24308
|
params.runId,
|
|
23132
24309
|
parsed.data,
|
|
23133
|
-
new URL(
|
|
24310
|
+
new URL(request2.url).origin
|
|
23134
24311
|
);
|
|
23135
24312
|
if (!exported.ok) {
|
|
23136
24313
|
return Response.json({ error: exported.message }, { status: exported.status });
|
|
@@ -24038,12 +25215,12 @@ async function readRegistryJson(response) {
|
|
|
24038
25215
|
const Route$j = createFileRoute("/api/providers/$providerId/model-metadata")({
|
|
24039
25216
|
server: {
|
|
24040
25217
|
handlers: {
|
|
24041
|
-
POST: async ({ params, request }) => {
|
|
25218
|
+
POST: async ({ params, request: request2 }) => {
|
|
24042
25219
|
const provider = getProvider(params.providerId);
|
|
24043
25220
|
if (provider === void 0) {
|
|
24044
25221
|
return Response.json({ error: "Provider not found" }, { status: 404 });
|
|
24045
25222
|
}
|
|
24046
|
-
const parsed = RefreshModelMetadataInputSchema.safeParse(await
|
|
25223
|
+
const parsed = RefreshModelMetadataInputSchema.safeParse(await request2.json());
|
|
24047
25224
|
if (!parsed.success) {
|
|
24048
25225
|
return Response.json({ error: parsed.error.message }, { status: 400 });
|
|
24049
25226
|
}
|
|
@@ -24077,9 +25254,16 @@ const Route$j = createFileRoute("/api/providers/$providerId/model-metadata")({
|
|
|
24077
25254
|
}
|
|
24078
25255
|
let response;
|
|
24079
25256
|
try {
|
|
24080
|
-
const fetchResult = await safeFetch(
|
|
24081
|
-
|
|
24082
|
-
|
|
25257
|
+
const fetchResult = await safeFetch(
|
|
25258
|
+
sourceUrl,
|
|
25259
|
+
{
|
|
25260
|
+
headers: { Accept: "application/json" }
|
|
25261
|
+
},
|
|
25262
|
+
{
|
|
25263
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataDeadlineMs,
|
|
25264
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.metadataResponseBytes
|
|
25265
|
+
}
|
|
25266
|
+
);
|
|
24083
25267
|
if (!fetchResult.ok) {
|
|
24084
25268
|
return Response.json(
|
|
24085
25269
|
{ error: `Model metadata URL blocked: ${fetchResult.reason}` },
|
|
@@ -24153,14 +25337,14 @@ const ReplayRequestSchema = object({
|
|
|
24153
25337
|
const Route$i = createFileRoute("/api/logs/$id/replay")({
|
|
24154
25338
|
server: {
|
|
24155
25339
|
handlers: {
|
|
24156
|
-
POST: async ({ params, request }) => {
|
|
25340
|
+
POST: async ({ params, request: request2 }) => {
|
|
24157
25341
|
const id = parseLogId(params.id);
|
|
24158
25342
|
if (id === null) {
|
|
24159
25343
|
return Response.json({ error: "Invalid log ID" }, { status: 400 });
|
|
24160
25344
|
}
|
|
24161
25345
|
let replayRequest;
|
|
24162
25346
|
try {
|
|
24163
|
-
const raw = await
|
|
25347
|
+
const raw = await request2.json();
|
|
24164
25348
|
const parsed = ReplayRequestSchema.safeParse(raw);
|
|
24165
25349
|
if (!parsed.success) {
|
|
24166
25350
|
return Response.json({ error: "Invalid request body" }, { status: 400 });
|
|
@@ -24218,7 +25402,11 @@ const Route$i = createFileRoute("/api/logs/$id/replay")({
|
|
|
24218
25402
|
headers,
|
|
24219
25403
|
body: modifiedBody
|
|
24220
25404
|
},
|
|
24221
|
-
{
|
|
25405
|
+
{
|
|
25406
|
+
allowLoopback: process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1",
|
|
25407
|
+
deadlineMs: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.replayDeadlineMs,
|
|
25408
|
+
responseBytes: DEFAULT_RUNTIME_BUDGET_POLICY.upstream.replayResponseBytes
|
|
25409
|
+
}
|
|
24222
25410
|
);
|
|
24223
25411
|
if (!fetchResult.ok) {
|
|
24224
25412
|
return Response.json({
|
|
@@ -24472,12 +25660,12 @@ function parseIntegerParam(value, fallback) {
|
|
|
24472
25660
|
const Route$g = createFileRoute("/api/logs/$id/body")({
|
|
24473
25661
|
server: {
|
|
24474
25662
|
handlers: {
|
|
24475
|
-
GET: async ({ params, request }) => {
|
|
25663
|
+
GET: async ({ params, request: request2 }) => {
|
|
24476
25664
|
const id = parseLogId(params.id);
|
|
24477
25665
|
if (id === null) {
|
|
24478
25666
|
return Response.json({ error: "Invalid log ID" }, { status: 400 });
|
|
24479
25667
|
}
|
|
24480
|
-
const url2 = new URL(
|
|
25668
|
+
const url2 = new URL(request2.url);
|
|
24481
25669
|
const partResult = LogBodyPartSchema.safeParse(url2.searchParams.get("part"));
|
|
24482
25670
|
if (!partResult.success) {
|
|
24483
25671
|
return Response.json({ error: "Invalid body part" }, { status: 400 });
|
|
@@ -24519,7 +25707,7 @@ object({
|
|
|
24519
25707
|
const Route$f = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
24520
25708
|
server: {
|
|
24521
25709
|
handlers: {
|
|
24522
|
-
PATCH: async ({ params, request }) => {
|
|
25710
|
+
PATCH: async ({ params, request: request2 }) => {
|
|
24523
25711
|
const existing = getCandidate(params.candidateId);
|
|
24524
25712
|
if (existing === null) {
|
|
24525
25713
|
return Response.json({ error: "Knowledge candidate not found" }, { status: 404 });
|
|
@@ -24530,7 +25718,7 @@ const Route$f = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
|
24530
25718
|
{ status: 409 }
|
|
24531
25719
|
);
|
|
24532
25720
|
}
|
|
24533
|
-
const raw = await
|
|
25721
|
+
const raw = await request2.json().catch(() => null);
|
|
24534
25722
|
const parsed = CandidateUpdateSchema.safeParse(raw);
|
|
24535
25723
|
if (!parsed.success) {
|
|
24536
25724
|
return Response.json({ error: parsed.error.message }, { status: 400 });
|
|
@@ -24547,8 +25735,8 @@ const Route$f = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
|
24547
25735
|
const Route$e = createFileRoute("/api/instances/$name/stop")({
|
|
24548
25736
|
server: {
|
|
24549
25737
|
handlers: {
|
|
24550
|
-
POST: async ({ params, request }) => {
|
|
24551
|
-
const body = await parseInstanceBody(
|
|
25738
|
+
POST: async ({ params, request: request2 }) => {
|
|
25739
|
+
const body = await parseInstanceBody(request2, InstanceEmptyRequestSchema);
|
|
24552
25740
|
if (!body.ok) return body.response;
|
|
24553
25741
|
const selfError = selfLifecycleError(params.name);
|
|
24554
25742
|
if (selfError !== null) return instanceErrorResponse(selfError);
|
|
@@ -24560,8 +25748,8 @@ const Route$e = createFileRoute("/api/instances/$name/stop")({
|
|
|
24560
25748
|
const Route$d = createFileRoute("/api/instances/$name/start")({
|
|
24561
25749
|
server: {
|
|
24562
25750
|
handlers: {
|
|
24563
|
-
POST: async ({ params, request }) => {
|
|
24564
|
-
const body = await parseInstanceBody(
|
|
25751
|
+
POST: async ({ params, request: request2 }) => {
|
|
25752
|
+
const body = await parseInstanceBody(request2, InstanceStartRequestSchema);
|
|
24565
25753
|
if (!body.ok) return body.response;
|
|
24566
25754
|
const result = await startNamedInstance(params.name, body.value);
|
|
24567
25755
|
const status = result.kind === "success" && result.value.outcome === "created" ? 201 : 200;
|
|
@@ -24573,8 +25761,8 @@ const Route$d = createFileRoute("/api/instances/$name/start")({
|
|
|
24573
25761
|
const Route$c = createFileRoute("/api/instances/$name/restart")({
|
|
24574
25762
|
server: {
|
|
24575
25763
|
handlers: {
|
|
24576
|
-
POST: async ({ params, request }) => {
|
|
24577
|
-
const body = await parseInstanceBody(
|
|
25764
|
+
POST: async ({ params, request: request2 }) => {
|
|
25765
|
+
const body = await parseInstanceBody(request2, InstanceStartRequestSchema);
|
|
24578
25766
|
if (!body.ok) return body.response;
|
|
24579
25767
|
const selfError = selfLifecycleError(params.name);
|
|
24580
25768
|
if (selfError !== null) return instanceErrorResponse(selfError);
|
|
@@ -24590,9 +25778,9 @@ const Route$b = createFileRoute("/api/instances/$name/connection")({
|
|
|
24590
25778
|
}
|
|
24591
25779
|
}
|
|
24592
25780
|
});
|
|
24593
|
-
async function readJsonBody$1(
|
|
25781
|
+
async function readJsonBody$1(request2) {
|
|
24594
25782
|
try {
|
|
24595
|
-
const raw = await
|
|
25783
|
+
const raw = await request2.text();
|
|
24596
25784
|
if (raw.trim().length === 0) return { ok: true, value: {} };
|
|
24597
25785
|
return { ok: true, value: JSON.parse(raw) };
|
|
24598
25786
|
} catch {
|
|
@@ -24609,8 +25797,8 @@ const Route$a = createFileRoute("/api/groups/$groupId/sessions")({
|
|
|
24609
25797
|
}
|
|
24610
25798
|
return Response.json(sessions2.value);
|
|
24611
25799
|
},
|
|
24612
|
-
POST: async ({ params, request }) => {
|
|
24613
|
-
const body = await readJsonBody$1(
|
|
25800
|
+
POST: async ({ params, request: request2 }) => {
|
|
25801
|
+
const body = await readJsonBody$1(request2);
|
|
24614
25802
|
if (!body.ok) {
|
|
24615
25803
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
24616
25804
|
}
|
|
@@ -24630,9 +25818,9 @@ const Route$a = createFileRoute("/api/groups/$groupId/sessions")({
|
|
|
24630
25818
|
}
|
|
24631
25819
|
}
|
|
24632
25820
|
});
|
|
24633
|
-
async function readJsonBody(
|
|
25821
|
+
async function readJsonBody(request2) {
|
|
24634
25822
|
try {
|
|
24635
|
-
const raw = await
|
|
25823
|
+
const raw = await request2.text();
|
|
24636
25824
|
if (raw.trim().length === 0) return { ok: true, value: void 0 };
|
|
24637
25825
|
return { ok: true, value: JSON.parse(raw) };
|
|
24638
25826
|
} catch {
|
|
@@ -24649,8 +25837,8 @@ const Route$9 = createFileRoute("/api/groups/$groupId/evidence")({
|
|
|
24649
25837
|
}
|
|
24650
25838
|
return Response.json(evidence.value);
|
|
24651
25839
|
},
|
|
24652
|
-
POST: async ({ params, request }) => {
|
|
24653
|
-
const body = await readJsonBody(
|
|
25840
|
+
POST: async ({ params, request: request2 }) => {
|
|
25841
|
+
const body = await readJsonBody(request2);
|
|
24654
25842
|
if (!body.ok) {
|
|
24655
25843
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
24656
25844
|
}
|
|
@@ -24664,7 +25852,7 @@ const Route$9 = createFileRoute("/api/groups/$groupId/evidence")({
|
|
|
24664
25852
|
const exported = await exportInspectorGroupEvidence(
|
|
24665
25853
|
params.groupId,
|
|
24666
25854
|
parsed.data,
|
|
24667
|
-
new URL(
|
|
25855
|
+
new URL(request2.url).origin
|
|
24668
25856
|
);
|
|
24669
25857
|
if (!exported.ok) {
|
|
24670
25858
|
return Response.json({ error: exported.message }, { status: exported.status });
|
|
@@ -25004,14 +26192,14 @@ async function logModelResults(displayName, providerName, providerSessionId, ant
|
|
|
25004
26192
|
const Route$7 = createFileRoute("/api/providers/$providerId/test/log")({
|
|
25005
26193
|
server: {
|
|
25006
26194
|
handlers: {
|
|
25007
|
-
POST: async ({ params, request }) => {
|
|
26195
|
+
POST: async ({ params, request: request2 }) => {
|
|
25008
26196
|
const provider = getProvider(params.providerId);
|
|
25009
26197
|
if (!provider) {
|
|
25010
26198
|
return Response.json({ error: "Provider not found" }, { status: 404 });
|
|
25011
26199
|
}
|
|
25012
26200
|
let body;
|
|
25013
26201
|
try {
|
|
25014
|
-
body = await
|
|
26202
|
+
body = await request2.json();
|
|
25015
26203
|
} catch {
|
|
25016
26204
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
25017
26205
|
}
|
|
@@ -25329,12 +26517,12 @@ const NO_STORE_HEADERS = { "Cache-Control": "no-store" };
|
|
|
25329
26517
|
function noStoreJson(body, status = 200) {
|
|
25330
26518
|
return Response.json(body, { status, headers: NO_STORE_HEADERS });
|
|
25331
26519
|
}
|
|
25332
|
-
async function readExecutionRequest(
|
|
25333
|
-
const body = await
|
|
26520
|
+
async function readExecutionRequest(request2) {
|
|
26521
|
+
const body = await request2.json().catch(() => null);
|
|
25334
26522
|
return EcosystemExecutionRequestSchema.safeParse(body);
|
|
25335
26523
|
}
|
|
25336
|
-
async function handleEcosystemPackageExecution(
|
|
25337
|
-
const parsed = await readExecutionRequest(
|
|
26524
|
+
async function handleEcosystemPackageExecution(request2, packageId, action, startTask = startEcosystemTask) {
|
|
26525
|
+
const parsed = await readExecutionRequest(request2);
|
|
25338
26526
|
if (!parsed.success) {
|
|
25339
26527
|
auditEcosystemExecution("confirmation_rejected", action, null, "invalid_request");
|
|
25340
26528
|
return noStoreJson({ error: "Explicit local command confirmation is required" }, 400);
|
|
@@ -25366,9 +26554,9 @@ async function handleEcosystemPackageExecution(request, packageId, action, start
|
|
|
25366
26554
|
auditEcosystemExecution("execution_started", action, plan.targetId, "none");
|
|
25367
26555
|
return noStoreJson({ task });
|
|
25368
26556
|
}
|
|
25369
|
-
async function handleEcosystemRecipeExecution(
|
|
26557
|
+
async function handleEcosystemRecipeExecution(request2, recipeId, startTask = startEcosystemRecipeTask) {
|
|
25370
26558
|
const action = "recipe";
|
|
25371
|
-
const parsed = await readExecutionRequest(
|
|
26559
|
+
const parsed = await readExecutionRequest(request2);
|
|
25372
26560
|
if (!parsed.success) {
|
|
25373
26561
|
auditEcosystemExecution("confirmation_rejected", action, null, "invalid_request");
|
|
25374
26562
|
return noStoreJson({ error: "Explicit local command confirmation is required" }, 400);
|
|
@@ -25403,149 +26591,159 @@ async function handleEcosystemRecipeExecution(request, recipeId, startTask = sta
|
|
|
25403
26591
|
const Route$4 = createFileRoute("/api/ecosystem/recipes/$recipeId/run")({
|
|
25404
26592
|
server: {
|
|
25405
26593
|
handlers: {
|
|
25406
|
-
POST: ({ params, request }) => handleEcosystemRecipeExecution(
|
|
26594
|
+
POST: ({ params, request: request2 }) => handleEcosystemRecipeExecution(request2, params.recipeId)
|
|
25407
26595
|
}
|
|
25408
26596
|
}
|
|
25409
26597
|
});
|
|
25410
26598
|
const Route$3 = createFileRoute("/api/ecosystem/packages/$packageId/upgrade")({
|
|
25411
26599
|
server: {
|
|
25412
26600
|
handlers: {
|
|
25413
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26601
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "upgrade")
|
|
25414
26602
|
}
|
|
25415
26603
|
}
|
|
25416
26604
|
});
|
|
25417
26605
|
const Route$2 = createFileRoute("/api/ecosystem/packages/$packageId/runner-presets")({
|
|
25418
26606
|
server: {
|
|
25419
26607
|
handlers: {
|
|
25420
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26608
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "runner-presets")
|
|
25421
26609
|
}
|
|
25422
26610
|
}
|
|
25423
26611
|
});
|
|
25424
26612
|
const Route$1 = createFileRoute("/api/ecosystem/packages/$packageId/install")({
|
|
25425
26613
|
server: {
|
|
25426
26614
|
handlers: {
|
|
25427
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26615
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "install")
|
|
25428
26616
|
}
|
|
25429
26617
|
}
|
|
25430
26618
|
});
|
|
25431
26619
|
const Route = createFileRoute("/api/ecosystem/packages/$packageId/help")({
|
|
25432
26620
|
server: {
|
|
25433
26621
|
handlers: {
|
|
25434
|
-
POST: ({ params, request }) => handleEcosystemPackageExecution(
|
|
26622
|
+
POST: ({ params, request: request2 }) => handleEcosystemPackageExecution(request2, params.packageId, "help")
|
|
25435
26623
|
}
|
|
25436
26624
|
}
|
|
25437
26625
|
});
|
|
25438
|
-
const ReadyzRoute = Route$
|
|
26626
|
+
const ReadyzRoute = Route$V.update({
|
|
25439
26627
|
id: "/readyz",
|
|
25440
26628
|
path: "/readyz",
|
|
25441
|
-
getParentRoute: () => Route$
|
|
26629
|
+
getParentRoute: () => Route$W
|
|
26630
|
+
});
|
|
26631
|
+
const MetricsRoute = Route$T.update({
|
|
26632
|
+
id: "/metrics",
|
|
26633
|
+
path: "/metrics",
|
|
26634
|
+
getParentRoute: () => Route$W
|
|
25442
26635
|
});
|
|
25443
26636
|
const LivezRoute = Route$S.update({
|
|
25444
26637
|
id: "/livez",
|
|
25445
26638
|
path: "/livez",
|
|
25446
|
-
getParentRoute: () => Route$
|
|
26639
|
+
getParentRoute: () => Route$W
|
|
25447
26640
|
});
|
|
25448
26641
|
const SplatRoute = Route$R.update({
|
|
25449
26642
|
id: "/$",
|
|
25450
26643
|
path: "/$",
|
|
25451
|
-
getParentRoute: () => Route$
|
|
26644
|
+
getParentRoute: () => Route$W
|
|
25452
26645
|
});
|
|
25453
26646
|
const ProxySplatRoute = Route$Q.update({
|
|
25454
26647
|
id: "/proxy/$",
|
|
25455
26648
|
path: "/proxy/$",
|
|
25456
|
-
getParentRoute: () => Route$
|
|
26649
|
+
getParentRoute: () => Route$W
|
|
26650
|
+
});
|
|
26651
|
+
const ApiStorageRoute = Route$P.update({
|
|
26652
|
+
id: "/api/storage",
|
|
26653
|
+
path: "/api/storage",
|
|
26654
|
+
getParentRoute: () => Route$W
|
|
25457
26655
|
});
|
|
25458
|
-
const ApiSessionsRoute = Route$
|
|
26656
|
+
const ApiSessionsRoute = Route$O.update({
|
|
25459
26657
|
id: "/api/sessions",
|
|
25460
26658
|
path: "/api/sessions",
|
|
25461
|
-
getParentRoute: () => Route$
|
|
26659
|
+
getParentRoute: () => Route$W
|
|
25462
26660
|
});
|
|
25463
|
-
const ApiRuntimeRoute = Route$
|
|
26661
|
+
const ApiRuntimeRoute = Route$N.update({
|
|
25464
26662
|
id: "/api/runtime",
|
|
25465
26663
|
path: "/api/runtime",
|
|
25466
|
-
getParentRoute: () => Route$
|
|
26664
|
+
getParentRoute: () => Route$W
|
|
25467
26665
|
});
|
|
25468
|
-
const ApiRunsRoute = Route$
|
|
26666
|
+
const ApiRunsRoute = Route$M.update({
|
|
25469
26667
|
id: "/api/runs",
|
|
25470
26668
|
path: "/api/runs",
|
|
25471
|
-
getParentRoute: () => Route$
|
|
26669
|
+
getParentRoute: () => Route$W
|
|
25472
26670
|
});
|
|
25473
|
-
const ApiProvidersRoute = Route$
|
|
26671
|
+
const ApiProvidersRoute = Route$L.update({
|
|
25474
26672
|
id: "/api/providers",
|
|
25475
26673
|
path: "/api/providers",
|
|
25476
|
-
getParentRoute: () => Route$
|
|
26674
|
+
getParentRoute: () => Route$W
|
|
25477
26675
|
});
|
|
25478
|
-
const ApiPiAgentRoute = Route$
|
|
26676
|
+
const ApiPiAgentRoute = Route$K.update({
|
|
25479
26677
|
id: "/api/pi-agent",
|
|
25480
26678
|
path: "/api/pi-agent",
|
|
25481
|
-
getParentRoute: () => Route$
|
|
26679
|
+
getParentRoute: () => Route$W
|
|
25482
26680
|
});
|
|
25483
|
-
const ApiModelsRoute = Route$
|
|
26681
|
+
const ApiModelsRoute = Route$J.update({
|
|
25484
26682
|
id: "/api/models",
|
|
25485
26683
|
path: "/api/models",
|
|
25486
|
-
getParentRoute: () => Route$
|
|
26684
|
+
getParentRoute: () => Route$W
|
|
25487
26685
|
});
|
|
25488
|
-
const ApiMcpRoute = Route$
|
|
26686
|
+
const ApiMcpRoute = Route$I.update({
|
|
25489
26687
|
id: "/api/mcp",
|
|
25490
26688
|
path: "/api/mcp",
|
|
25491
|
-
getParentRoute: () => Route$
|
|
26689
|
+
getParentRoute: () => Route$W
|
|
25492
26690
|
});
|
|
25493
|
-
const ApiLogsRoute = Route$
|
|
26691
|
+
const ApiLogsRoute = Route$H.update({
|
|
25494
26692
|
id: "/api/logs",
|
|
25495
26693
|
path: "/api/logs",
|
|
25496
|
-
getParentRoute: () => Route$
|
|
26694
|
+
getParentRoute: () => Route$W
|
|
25497
26695
|
});
|
|
25498
|
-
const ApiInstancesRoute = Route$
|
|
26696
|
+
const ApiInstancesRoute = Route$G.update({
|
|
25499
26697
|
id: "/api/instances",
|
|
25500
26698
|
path: "/api/instances",
|
|
25501
|
-
getParentRoute: () => Route$
|
|
26699
|
+
getParentRoute: () => Route$W
|
|
25502
26700
|
});
|
|
25503
|
-
const ApiHealthRoute = Route$
|
|
26701
|
+
const ApiHealthRoute = Route$F.update({
|
|
25504
26702
|
id: "/api/health",
|
|
25505
26703
|
path: "/api/health",
|
|
25506
|
-
getParentRoute: () => Route$
|
|
26704
|
+
getParentRoute: () => Route$W
|
|
25507
26705
|
});
|
|
25508
|
-
const ApiGroupsRoute = Route$
|
|
26706
|
+
const ApiGroupsRoute = Route$E.update({
|
|
25509
26707
|
id: "/api/groups",
|
|
25510
26708
|
path: "/api/groups",
|
|
25511
|
-
getParentRoute: () => Route$
|
|
26709
|
+
getParentRoute: () => Route$W
|
|
25512
26710
|
});
|
|
25513
|
-
const ApiConfigRoute = Route$
|
|
26711
|
+
const ApiConfigRoute = Route$D.update({
|
|
25514
26712
|
id: "/api/config",
|
|
25515
26713
|
path: "/api/config",
|
|
25516
|
-
getParentRoute: () => Route$
|
|
26714
|
+
getParentRoute: () => Route$W
|
|
25517
26715
|
});
|
|
25518
|
-
const ApiAlertsRoute = Route$
|
|
26716
|
+
const ApiAlertsRoute = Route$C.update({
|
|
25519
26717
|
id: "/api/alerts",
|
|
25520
26718
|
path: "/api/alerts",
|
|
25521
|
-
getParentRoute: () => Route$
|
|
26719
|
+
getParentRoute: () => Route$W
|
|
25522
26720
|
});
|
|
25523
|
-
const ApiRunsRunIdRoute = Route$
|
|
26721
|
+
const ApiRunsRunIdRoute = Route$B.update({
|
|
25524
26722
|
id: "/$runId",
|
|
25525
26723
|
path: "/$runId",
|
|
25526
26724
|
getParentRoute: () => ApiRunsRoute
|
|
25527
26725
|
});
|
|
25528
|
-
const ApiProvidersScanRoute = Route$
|
|
26726
|
+
const ApiProvidersScanRoute = Route$A.update({
|
|
25529
26727
|
id: "/scan",
|
|
25530
26728
|
path: "/scan",
|
|
25531
26729
|
getParentRoute: () => ApiProvidersRoute
|
|
25532
26730
|
});
|
|
25533
|
-
const ApiProvidersImportRoute = Route$
|
|
26731
|
+
const ApiProvidersImportRoute = Route$z.update({
|
|
25534
26732
|
id: "/import",
|
|
25535
26733
|
path: "/import",
|
|
25536
26734
|
getParentRoute: () => ApiProvidersRoute
|
|
25537
26735
|
});
|
|
25538
|
-
const ApiProvidersExportRoute = Route$
|
|
26736
|
+
const ApiProvidersExportRoute = Route$y.update({
|
|
25539
26737
|
id: "/export",
|
|
25540
26738
|
path: "/export",
|
|
25541
26739
|
getParentRoute: () => ApiProvidersRoute
|
|
25542
26740
|
});
|
|
25543
|
-
const ApiProvidersProviderIdRoute = Route$
|
|
26741
|
+
const ApiProvidersProviderIdRoute = Route$x.update({
|
|
25544
26742
|
id: "/$providerId",
|
|
25545
26743
|
path: "/$providerId",
|
|
25546
26744
|
getParentRoute: () => ApiProvidersRoute
|
|
25547
26745
|
});
|
|
25548
|
-
const ApiLogsStreamRoute = Route$
|
|
26746
|
+
const ApiLogsStreamRoute = Route$U.update({
|
|
25549
26747
|
id: "/stream",
|
|
25550
26748
|
path: "/stream",
|
|
25551
26749
|
getParentRoute: () => ApiLogsRoute
|
|
@@ -25563,17 +26761,17 @@ const ApiLogsIdRoute = Route$v.update({
|
|
|
25563
26761
|
const ApiKnowledgeSearchRoute = Route$u.update({
|
|
25564
26762
|
id: "/api/knowledge/search",
|
|
25565
26763
|
path: "/api/knowledge/search",
|
|
25566
|
-
getParentRoute: () => Route$
|
|
26764
|
+
getParentRoute: () => Route$W
|
|
25567
26765
|
});
|
|
25568
26766
|
const ApiKnowledgeProjectContextRoute = Route$t.update({
|
|
25569
26767
|
id: "/api/knowledge/project-context",
|
|
25570
26768
|
path: "/api/knowledge/project-context",
|
|
25571
|
-
getParentRoute: () => Route$
|
|
26769
|
+
getParentRoute: () => Route$W
|
|
25572
26770
|
});
|
|
25573
26771
|
const ApiKnowledgeCandidatesRoute = Route$s.update({
|
|
25574
26772
|
id: "/api/knowledge/candidates",
|
|
25575
26773
|
path: "/api/knowledge/candidates",
|
|
25576
|
-
getParentRoute: () => Route$
|
|
26774
|
+
getParentRoute: () => Route$W
|
|
25577
26775
|
});
|
|
25578
26776
|
const ApiInstancesNameRoute = Route$r.update({
|
|
25579
26777
|
id: "/$name",
|
|
@@ -25588,12 +26786,12 @@ const ApiGroupsGroupIdRoute = Route$q.update({
|
|
|
25588
26786
|
const ApiEcosystemTasksRoute = Route$p.update({
|
|
25589
26787
|
id: "/api/ecosystem/tasks",
|
|
25590
26788
|
path: "/api/ecosystem/tasks",
|
|
25591
|
-
getParentRoute: () => Route$
|
|
26789
|
+
getParentRoute: () => Route$W
|
|
25592
26790
|
});
|
|
25593
26791
|
const ApiEcosystemPackagesRoute = Route$o.update({
|
|
25594
26792
|
id: "/api/ecosystem/packages",
|
|
25595
26793
|
path: "/api/ecosystem/packages",
|
|
25596
|
-
getParentRoute: () => Route$
|
|
26794
|
+
getParentRoute: () => Route$W
|
|
25597
26795
|
});
|
|
25598
26796
|
const ApiConfigPathsRoute = Route$n.update({
|
|
25599
26797
|
id: "/paths",
|
|
@@ -25683,7 +26881,7 @@ const ApiProvidersProviderIdTestLogRoute = Route$7.update({
|
|
|
25683
26881
|
const ApiKnowledgeSessionsSessionIdCandidatesRoute = Route$6.update({
|
|
25684
26882
|
id: "/api/knowledge/sessions/$sessionId/candidates",
|
|
25685
26883
|
path: "/api/knowledge/sessions/$sessionId/candidates",
|
|
25686
|
-
getParentRoute: () => Route$
|
|
26884
|
+
getParentRoute: () => Route$W
|
|
25687
26885
|
});
|
|
25688
26886
|
const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route$5.update({
|
|
25689
26887
|
id: "/promote",
|
|
@@ -25693,7 +26891,7 @@ const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route$5.update({
|
|
|
25693
26891
|
const ApiEcosystemRecipesRecipeIdRunRoute = Route$4.update({
|
|
25694
26892
|
id: "/api/ecosystem/recipes/$recipeId/run",
|
|
25695
26893
|
path: "/api/ecosystem/recipes/$recipeId/run",
|
|
25696
|
-
getParentRoute: () => Route$
|
|
26894
|
+
getParentRoute: () => Route$W
|
|
25697
26895
|
});
|
|
25698
26896
|
const ApiEcosystemPackagesPackageIdUpgradeRoute = Route$3.update({
|
|
25699
26897
|
id: "/$packageId/upgrade",
|
|
@@ -25823,6 +27021,7 @@ const ApiKnowledgeCandidatesRouteWithChildren = ApiKnowledgeCandidatesRoute._add
|
|
|
25823
27021
|
const rootRouteChildren = {
|
|
25824
27022
|
SplatRoute,
|
|
25825
27023
|
LivezRoute,
|
|
27024
|
+
MetricsRoute,
|
|
25826
27025
|
ReadyzRoute,
|
|
25827
27026
|
ApiAlertsRoute: ApiAlertsRouteWithChildren,
|
|
25828
27027
|
ApiConfigRoute: ApiConfigRouteWithChildren,
|
|
@@ -25837,6 +27036,7 @@ const rootRouteChildren = {
|
|
|
25837
27036
|
ApiRunsRoute: ApiRunsRouteWithChildren,
|
|
25838
27037
|
ApiRuntimeRoute,
|
|
25839
27038
|
ApiSessionsRoute,
|
|
27039
|
+
ApiStorageRoute,
|
|
25840
27040
|
ProxySplatRoute,
|
|
25841
27041
|
ApiEcosystemPackagesRoute: ApiEcosystemPackagesRouteWithChildren,
|
|
25842
27042
|
ApiEcosystemTasksRoute: ApiEcosystemTasksRouteWithChildren,
|
|
@@ -25846,7 +27046,7 @@ const rootRouteChildren = {
|
|
|
25846
27046
|
ApiEcosystemRecipesRecipeIdRunRoute,
|
|
25847
27047
|
ApiKnowledgeSessionsSessionIdCandidatesRoute
|
|
25848
27048
|
};
|
|
25849
|
-
const routeTree = Route$
|
|
27049
|
+
const routeTree = Route$W._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
25850
27050
|
function defaultRouterBasePath() {
|
|
25851
27051
|
return getRouterBasePath(typeof process === "undefined" ? void 0 : process.env);
|
|
25852
27052
|
}
|