@tonyclaw/agent-inspector 3.0.44 → 3.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/cli.js +20743 -16945
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-DQHjxFO6.js → CompareDrawer-B3_TLTJh.js} +1 -1
- package/.output/public/assets/{InspectorPet-D1A4505B.js → InspectorPet-Cpn6hv4P.js} +1 -1
- package/.output/public/assets/{ProxyViewerContainer-BhpIgfBi.js → ProxyViewerContainer-BEAhuGOJ.js} +27 -27
- package/.output/public/assets/{ReplayDialog-C5vmbecv.js → ReplayDialog-B3EKVdJk.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CPGNB8lk.js → RequestAnatomy-COOQjyUC.js} +1 -1
- package/.output/public/assets/{ResponseView-DUfrajCm.js → ResponseView-DBCjkwye.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-nbeCQqOz.js → StreamingChunkSequence-CuXhDgMo.js} +1 -1
- package/.output/public/assets/{_sessionId-mw3aA8Bp.js → _sessionId-BJlM3nmt.js} +1 -1
- package/.output/public/assets/{_sessionId-CDhRgzf6.js → _sessionId-zbgYl7ZJ.js} +1 -1
- package/.output/public/assets/{index-B2eoeT-Z.js → index-BIK4bPTv.js} +1 -1
- package/.output/public/assets/{index-C6qZwYlR.js → index-BZo_8koU.js} +15 -15
- package/.output/public/assets/{index-CX7_rf7o.js → index-D2Qw6Ri2.js} +1 -1
- package/.output/public/assets/{index-BWjeqxbk.css → index-ZWschkCh.css} +1 -1
- package/.output/public/assets/{index-BfRhTvOd.js → index-xF_NY8Yb.js} +5 -5
- package/.output/public/assets/{json-viewer-BLmF1sOZ.js → json-viewer-B-Ao6pwW.js} +1 -1
- package/.output/public/assets/{jszip.min-C9rCFoU3.js → jszip.min-B_rfAUCe.js} +1 -1
- package/.output/server/_libs/modelcontextprotocol__core.mjs +25 -25
- package/.output/server/_libs/modelcontextprotocol__server.mjs +2 -2
- package/.output/server/_libs/zod.mjs +6 -6
- package/.output/server/{_sessionId-U54BAqdp.mjs → _sessionId-CJ99gZFV.mjs} +3 -1
- package/.output/server/{_sessionId-E_Dmopc-.mjs → _sessionId-DEjScBpK.mjs} +5 -3
- package/.output/server/_ssr/{CompareDrawer-CksatPed.mjs → CompareDrawer-DtOIMeQB.mjs} +4 -2
- package/.output/server/_ssr/{InspectorPet-DNc4GQ7z.mjs → InspectorPet-CXc4oCBQ.mjs} +3 -1
- package/.output/server/_ssr/{ProxyViewerContainer-B02Ey0p_.mjs → ProxyViewerContainer-DVPlwfkt.mjs} +52 -27
- package/.output/server/_ssr/{ReplayDialog-DdMvHJF1.mjs → ReplayDialog-BQEA95qC.mjs} +6 -4
- package/.output/server/_ssr/{RequestAnatomy--vh2Z8__.mjs → RequestAnatomy-BMyd8B7Q.mjs} +4 -2
- package/.output/server/_ssr/{ResponseView-Dhxc-cKC.mjs → ResponseView-qrTaEIM8.mjs} +4 -2
- package/.output/server/_ssr/{StreamingChunkSequence-BG0EQmaD.mjs → StreamingChunkSequence-BFTL8mIy.mjs} +5 -3
- package/.output/server/_ssr/{index-lcfulIXa.mjs → index-Bcj1Hcy4.mjs} +4 -2
- package/.output/server/_ssr/{index-D0At9nfd.mjs → index-DW-M7XUG.mjs} +3 -1
- package/.output/server/_ssr/index.mjs +4 -3
- package/.output/server/_ssr/{json-viewer-My1J1_5R.mjs → json-viewer-DiI0Yjsn.mjs} +4 -2
- package/.output/server/_ssr/publicBasePathContext-5264L3rX.mjs +148 -0
- package/.output/server/_ssr/{router-DWcUFPR3.mjs → router-BJ_ltLim.mjs} +2432 -475
- package/.output/server/_ssr/start-C6MjPjxp.mjs +57 -0
- package/.output/server/_tanstack-start-manifest_v-C6pXXX9R.mjs +4 -0
- package/.output/server/index.mjs +92 -92
- package/.output/workers/logFinalizer.worker.js +4 -0
- package/.output/workers/sessionWorkerEntry.js +4 -0
- package/README.md +221 -25
- package/docs/instance-control.openapi.yaml +306 -0
- package/package.json +4 -1
- package/src/cli/alias.ts +440 -0
- package/src/cli/instance.ts +1037 -0
- package/src/cli/instanceArgs.ts +266 -0
- package/src/cli/instanceControl.ts +183 -0
- package/src/cli/instanceModel.ts +275 -0
- package/src/cli/instanceRegistry.ts +740 -0
- package/src/cli/onboard.ts +123 -35
- package/src/cli/onboardTarget.ts +179 -0
- package/src/cli/templates/codex-skill-onboard.ts +76 -28
- package/src/cli/templates/skill-onboard.ts +93 -59
- package/src/cli.ts +154 -16
- package/src/components/ProxyViewer.tsx +13 -5
- package/src/components/providers/SettingsDialog.tsx +13 -8
- package/src/lib/basePath.ts +8 -2
- package/src/lib/codingAgentEndpoints.ts +16 -0
- package/src/lib/instanceContract.ts +220 -0
- package/src/lib/managedInstance.ts +104 -0
- package/src/lib/publicBasePathContext.ts +107 -0
- package/src/mcp/instanceHandlers.ts +149 -0
- package/src/mcp/loopback.ts +16 -5
- package/src/mcp/mode.ts +25 -0
- package/src/mcp/server.ts +258 -18
- package/src/mcp/toolHandlers.ts +2 -2
- package/src/proxy/identityProxy.ts +655 -36
- package/src/proxy/runtimeShutdown.ts +73 -1
- package/src/proxy/sessionInfo.ts +5 -4
- package/src/routes/api/-instances.ts +109 -0
- package/src/routes/api/instances.$name.connection.ts +12 -0
- package/src/routes/api/instances.$name.restart.ts +23 -0
- package/src/routes/api/instances.$name.start.ts +18 -0
- package/src/routes/api/instances.$name.stop.ts +23 -0
- package/src/routes/api/instances.$name.ts +12 -0
- package/src/routes/api/instances.ts +16 -0
- package/src/start.ts +10 -0
- package/.output/server/_ssr/start-HYkvq4Ni.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-B2chR8ef.mjs +0 -4
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { c as createRouter, a as createRootRoute, b as createFileRoute, l as lazyRouteComponent, O as Outlet, H as HeadContent, S as Scripts } from "../_libs/tanstack__react-router.mjs";
|
|
2
|
+
import { h as getRouterBasePath, r as runWithRequestPublicBasePath, a as getPublicBasePath, w as withBasePath, s as stripBasePath, g as getConfiguredBasePath, n as normalizeBasePath, e as appendBasePathToOrigin, A as AGENT_INSPECTOR_BASE_PATH_META_NAME, f as AGENT_INSPECTOR_BASE_PATH_ENV, b as getPublicPort, c as AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER, d as AGENT_INSPECTOR_PUBLIC_PORT_HEADER } from "./publicBasePathContext-5264L3rX.mjs";
|
|
2
3
|
import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
|
|
3
4
|
import { S as SWRConfig } from "../_libs/swr.mjs";
|
|
4
5
|
import { c as cva } from "../_libs/class-variance-authority.mjs";
|
|
@@ -6,8 +7,8 @@ import { c as clsx } from "../_libs/clsx.mjs";
|
|
|
6
7
|
import { t as twMerge } from "../_libs/tailwind-merge.mjs";
|
|
7
8
|
import { randomUUID, timingSafeEqual, createHash, createDecipheriv, randomBytes, createCipheriv } from "node:crypto";
|
|
8
9
|
import fs, { readdir, open, readFile, unlink, writeFile, appendFile, stat, rename, rm, statfs } from "node:fs/promises";
|
|
9
|
-
import path, { basename, join, dirname, isAbsolute, resolve, relative } from "node:path";
|
|
10
|
-
import { existsSync, readFileSync, writeFileSync, unlinkSync, renameSync, copyFileSync, createReadStream, mkdirSync, statSync, readdirSync, rmSync, chmodSync, cpSync, lstatSync, appendFileSync, createWriteStream } from "node:fs";
|
|
10
|
+
import path, { basename, join, dirname, isAbsolute, resolve, win32, posix, relative } from "node:path";
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync, renameSync, copyFileSync, createReadStream, mkdirSync, statSync, readdirSync, rmSync, chmodSync, cpSync, lstatSync, appendFileSync, createWriteStream, realpathSync, openSync, closeSync } from "node:fs";
|
|
11
12
|
import { spawn, execFile, spawnSync } from "node:child_process";
|
|
12
13
|
import { Buffer } from "node:buffer";
|
|
13
14
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -17,12 +18,12 @@ import { C as Conf } from "../_libs/conf.mjs";
|
|
|
17
18
|
import { randomUUID as randomUUID$1 } from "crypto";
|
|
18
19
|
import { promisify } from "node:util";
|
|
19
20
|
import { lookup } from "node:dns/promises";
|
|
20
|
-
import { isIP } from "node:net";
|
|
21
|
+
import { isIP, createServer } from "node:net";
|
|
21
22
|
import assert from "node:assert/strict";
|
|
22
23
|
import { M as McpServer, W as WebStandardStreamableHTTPServerTransport, R as ResourceTemplate } from "../_libs/modelcontextprotocol__server.mjs";
|
|
23
24
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
24
25
|
import { homedir } from "node:os";
|
|
25
|
-
import { o as object, c as string,
|
|
26
|
+
import { o as object, c as string, f as array, n as number, r as record, g as _enum, u as union, j as unknown, a as literal, e as boolean, k as discriminatedUnion, _ as _null, b as lazy, q as url, d as datetime } from "../_libs/zod.mjs";
|
|
26
27
|
import "../_libs/tanstack__router-core.mjs";
|
|
27
28
|
import "../_libs/cookie-es.mjs";
|
|
28
29
|
import "../_libs/seroval.mjs";
|
|
@@ -35,6 +36,7 @@ import "util";
|
|
|
35
36
|
import "async_hooks";
|
|
36
37
|
import "stream";
|
|
37
38
|
import "../_libs/isbot.mjs";
|
|
39
|
+
import "node:async_hooks";
|
|
38
40
|
import "../_libs/use-sync-external-store.mjs";
|
|
39
41
|
import "../_libs/dequal.mjs";
|
|
40
42
|
import "node:process";
|
|
@@ -70,52 +72,6 @@ import "../_libs/lie.mjs";
|
|
|
70
72
|
import "../_libs/immediate.mjs";
|
|
71
73
|
import "../_libs/setimmediate.mjs";
|
|
72
74
|
import "../_libs/pako.mjs";
|
|
73
|
-
const AGENT_INSPECTOR_BASE_PATH_ENV = "AGENT_INSPECTOR_BASE_PATH";
|
|
74
|
-
const AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV = "AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH";
|
|
75
|
-
const AGENT_INSPECTOR_BASE_PATH_META_NAME = "agent-inspector-base-path";
|
|
76
|
-
const DEFAULT_AGENT_INSPECTOR_BASE_PATH = "/inspector";
|
|
77
|
-
function trimSlashes(value) {
|
|
78
|
-
return value.replace(/^\/+/u, "").replace(/\/+$/u, "");
|
|
79
|
-
}
|
|
80
|
-
function normalizeBasePath(value) {
|
|
81
|
-
const trimmed = value?.trim();
|
|
82
|
-
if (trimmed === void 0 || trimmed.length === 0 || trimmed === "/") return "";
|
|
83
|
-
const path2 = trimSlashes(trimmed);
|
|
84
|
-
if (path2.length === 0) return "";
|
|
85
|
-
return `/${path2}`;
|
|
86
|
-
}
|
|
87
|
-
function readDocumentBasePath() {
|
|
88
|
-
if (typeof document === "undefined") return void 0;
|
|
89
|
-
return document.querySelector(`meta[name="${AGENT_INSPECTOR_BASE_PATH_META_NAME}"]`)?.getAttribute("content") ?? void 0;
|
|
90
|
-
}
|
|
91
|
-
function readProcessBasePath() {
|
|
92
|
-
if (typeof process === "undefined") return void 0;
|
|
93
|
-
return process.env[AGENT_INSPECTOR_BASE_PATH_ENV];
|
|
94
|
-
}
|
|
95
|
-
function getConfiguredBasePath(env) {
|
|
96
|
-
return normalizeBasePath(
|
|
97
|
-
env?.[AGENT_INSPECTOR_BASE_PATH_ENV] ?? readProcessBasePath() ?? readDocumentBasePath() ?? "/" ?? DEFAULT_AGENT_INSPECTOR_BASE_PATH
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
function getRouterBasePath(env) {
|
|
101
|
-
if (env?.[AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV] === "1") return "";
|
|
102
|
-
return getConfiguredBasePath(env);
|
|
103
|
-
}
|
|
104
|
-
function withBasePath(path2, basePath = getConfiguredBasePath()) {
|
|
105
|
-
const normalized = normalizeBasePath(basePath);
|
|
106
|
-
if (/^[a-z][a-z0-9+.-]*:/iu.test(path2)) return path2;
|
|
107
|
-
if (normalized.length === 0) return path2;
|
|
108
|
-
if (path2.length === 0 || path2 === "/") return normalized;
|
|
109
|
-
if (path2.startsWith(`${normalized}/`) || path2 === normalized) return path2;
|
|
110
|
-
return path2.startsWith("/") ? `${normalized}${path2}` : `${normalized}/${path2}`;
|
|
111
|
-
}
|
|
112
|
-
function stripBasePath(pathname, basePath = getConfiguredBasePath()) {
|
|
113
|
-
const normalized = normalizeBasePath(basePath);
|
|
114
|
-
if (normalized.length === 0) return pathname;
|
|
115
|
-
if (pathname === normalized) return "/";
|
|
116
|
-
if (pathname.startsWith(`${normalized}/`)) return pathname.slice(normalized.length);
|
|
117
|
-
return pathname;
|
|
118
|
-
}
|
|
119
75
|
const faviconSvg = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20role='img'%20aria-label='Agent%20Inspector'%3e%3cg%20fill='none'%20stroke='%23f59e0b'%20stroke-width='1.5'%20stroke-linecap='round'%20stroke-linejoin='round'%20%3e%3cpath%20d='M5%2013%20C5%209%208%207%2012%207%20C16%207%2019%209%2019%2013%20C19%2016%2016%2018%2012%2018%20C8%2018%205%2016%205%2013%20Z'%20/%3e%3cpath%20d='M5%2011%20C3.5%209.5%201.5%2010%202%2012.5%20C2.5%2014%204%2013.5%205%2012.5'%20/%3e%3cpath%20d='M19%2011%20C20.5%209.5%2022.5%2010%2022%2012.5%20C21.5%2014%2020%2013.5%2019%2012.5'%20/%3e%3cline%20x1='10'%20y1='7'%20x2='9.5'%20y2='5'%20/%3e%3cline%20x1='14'%20y1='7'%20x2='14.5'%20y2='5'%20/%3e%3cline%20x1='6.5'%20y1='16'%20x2='4.5'%20y2='19.5'%20/%3e%3cline%20x1='9'%20y1='17.5'%20x2='8'%20y2='20.5'%20/%3e%3cline%20x1='15'%20y1='17.5'%20x2='16'%20y2='20.5'%20/%3e%3cline%20x1='17.5'%20y1='16'%20x2='19.5'%20y2='19.5'%20/%3e%3c/g%3e%3ccircle%20cx='9.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='14.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3c/svg%3e";
|
|
120
76
|
function restoreSelection(selection, ranges) {
|
|
121
77
|
if (selection === null) return;
|
|
@@ -339,8 +295,8 @@ function SafeErrorFallback({
|
|
|
339
295
|
}) {
|
|
340
296
|
const [copied, setCopied] = reactExports.useState(false);
|
|
341
297
|
const handleCopy = reactExports.useCallback(() => {
|
|
342
|
-
void copyTextToClipboard(reference).then((
|
|
343
|
-
if (!
|
|
298
|
+
void copyTextToClipboard(reference).then((success2) => {
|
|
299
|
+
if (!success2) return;
|
|
344
300
|
setCopied(true);
|
|
345
301
|
});
|
|
346
302
|
}, [reference]);
|
|
@@ -447,7 +403,7 @@ function SafeRouteError({
|
|
|
447
403
|
}
|
|
448
404
|
);
|
|
449
405
|
}
|
|
450
|
-
const appCss = "/assets/index-
|
|
406
|
+
const appCss = "/assets/index-ZWschkCh.css";
|
|
451
407
|
const CHUNK_LOAD_RELOAD_AT_KEY = "agent-inspector.chunkLoadReloadAt";
|
|
452
408
|
const CHUNK_LOAD_RELOAD_WINDOW_MS = 15e3;
|
|
453
409
|
const CHUNK_LOAD_FAILURE_PATTERNS = [
|
|
@@ -700,7 +656,7 @@ const uiScaleBootstrapScript = `
|
|
|
700
656
|
}
|
|
701
657
|
})();
|
|
702
658
|
`;
|
|
703
|
-
const Route$
|
|
659
|
+
const Route$U = createRootRoute({
|
|
704
660
|
head: () => ({
|
|
705
661
|
meta: [
|
|
706
662
|
{ charSet: "utf-8" },
|
|
@@ -3002,11 +2958,11 @@ function destroyAllSessionProcesses() {
|
|
|
3002
2958
|
sp.destroy();
|
|
3003
2959
|
}
|
|
3004
2960
|
}
|
|
3005
|
-
function isRecord(value) {
|
|
2961
|
+
function isRecord$2(value) {
|
|
3006
2962
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3007
2963
|
}
|
|
3008
2964
|
function readProperty$3(value, name2) {
|
|
3009
|
-
if (!isRecord(value)) return void 0;
|
|
2965
|
+
if (!isRecord$2(value)) return void 0;
|
|
3010
2966
|
const descriptor = Object.getOwnPropertyDescriptor(value, name2);
|
|
3011
2967
|
return descriptor === void 0 ? void 0 : descriptor.value;
|
|
3012
2968
|
}
|
|
@@ -4015,13 +3971,13 @@ function buildAbsoluteUrl(baseUrl, path2) {
|
|
|
4015
3971
|
}
|
|
4016
3972
|
function buildApiPath(sessionId) {
|
|
4017
3973
|
return buildRelativePath(
|
|
4018
|
-
withBasePath("/api/sessions",
|
|
3974
|
+
withBasePath("/api/sessions", getPublicBasePath()),
|
|
4019
3975
|
new URLSearchParams({ sessionId })
|
|
4020
3976
|
);
|
|
4021
3977
|
}
|
|
4022
3978
|
function buildCompactLogsPath(sessionId) {
|
|
4023
3979
|
return buildRelativePath(
|
|
4024
|
-
withBasePath("/api/logs",
|
|
3980
|
+
withBasePath("/api/logs", getPublicBasePath()),
|
|
4025
3981
|
new URLSearchParams({ compact: "1", sessionId })
|
|
4026
3982
|
);
|
|
4027
3983
|
}
|
|
@@ -4100,7 +4056,7 @@ function buildSessionInfo(input) {
|
|
|
4100
4056
|
const queuedTasks = input.snapshot?.queuedTasks ?? 0;
|
|
4101
4057
|
const runningTasks = input.snapshot?.runningTasks ?? 0;
|
|
4102
4058
|
const lastTaskError = normalizeString(input.snapshot?.lastTaskError);
|
|
4103
|
-
const inspectorPath = getSessionPath(input.sessionId,
|
|
4059
|
+
const inspectorPath = getSessionPath(input.sessionId, getPublicBasePath());
|
|
4104
4060
|
const apiPath = buildApiPath(input.sessionId);
|
|
4105
4061
|
const compactLogsPath = buildCompactLogsPath(input.sessionId);
|
|
4106
4062
|
const latestLogs = summaries.slice(0, latestLogLimit).map((summary, index) => ({
|
|
@@ -5838,7 +5794,7 @@ function readStringArray(value, key) {
|
|
|
5838
5794
|
}
|
|
5839
5795
|
function readObject(value, key) {
|
|
5840
5796
|
const property = readProperty$3(value, key);
|
|
5841
|
-
return isRecord(property) ? property : null;
|
|
5797
|
+
return isRecord$2(property) ? property : null;
|
|
5842
5798
|
}
|
|
5843
5799
|
function normalizeExpectedType(typeName) {
|
|
5844
5800
|
switch (typeName) {
|
|
@@ -5883,7 +5839,7 @@ function actualType(value) {
|
|
|
5883
5839
|
case "function":
|
|
5884
5840
|
case "object":
|
|
5885
5841
|
case "symbol":
|
|
5886
|
-
return isRecord(value) ? "object" : "unknown";
|
|
5842
|
+
return isRecord$2(value) ? "object" : "unknown";
|
|
5887
5843
|
}
|
|
5888
5844
|
return "unknown";
|
|
5889
5845
|
}
|
|
@@ -5900,7 +5856,7 @@ function matchesExpectedType(value, expected) {
|
|
|
5900
5856
|
case "number":
|
|
5901
5857
|
return typeof value === "number" && Number.isFinite(value);
|
|
5902
5858
|
case "object":
|
|
5903
|
-
return isRecord(value);
|
|
5859
|
+
return isRecord$2(value);
|
|
5904
5860
|
case "string":
|
|
5905
5861
|
return typeof value === "string";
|
|
5906
5862
|
}
|
|
@@ -6005,7 +5961,7 @@ function validateEnum(schema, value, toolName, path2, warnings) {
|
|
|
6005
5961
|
}
|
|
6006
5962
|
}
|
|
6007
5963
|
function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
6008
|
-
if (!isRecord(schema)) return;
|
|
5964
|
+
if (!isRecord$2(schema)) return;
|
|
6009
5965
|
const expectedTypes = schemaTypes(schema);
|
|
6010
5966
|
if (expectedTypes.length > 0 && !matchesAnyExpectedType(value, expectedTypes)) {
|
|
6011
5967
|
addTypeWarning(warnings, toolName, path2, expectedTypes, value);
|
|
@@ -6013,7 +5969,7 @@ function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
|
6013
5969
|
}
|
|
6014
5970
|
validateEnum(schema, value, toolName, path2, warnings);
|
|
6015
5971
|
if (schemaAllowsObjectValidation(schema, expectedTypes)) {
|
|
6016
|
-
if (isRecord(value)) {
|
|
5972
|
+
if (isRecord$2(value)) {
|
|
6017
5973
|
validateObjectSchema(schema, value, toolName, path2, warnings);
|
|
6018
5974
|
} else if (expectedTypes.length === 0) {
|
|
6019
5975
|
warnings.push(
|
|
@@ -6034,7 +5990,7 @@ function validateValueAgainstSchema(schema, value, toolName, path2, warnings) {
|
|
|
6034
5990
|
function extractAnthropicToolSchemas(request) {
|
|
6035
5991
|
const tools = /* @__PURE__ */ new Map();
|
|
6036
5992
|
for (const tool of readArray(request, "tools")) {
|
|
6037
|
-
if (!isRecord(tool)) continue;
|
|
5993
|
+
if (!isRecord$2(tool)) continue;
|
|
6038
5994
|
const name2 = readString(tool, "name");
|
|
6039
5995
|
if (name2 === null || name2 === "") continue;
|
|
6040
5996
|
tools.set(name2, {
|
|
@@ -6848,7 +6804,7 @@ async function readinessResponse() {
|
|
|
6848
6804
|
headers: { "cache-control": "no-store" }
|
|
6849
6805
|
});
|
|
6850
6806
|
}
|
|
6851
|
-
const Route$
|
|
6807
|
+
const Route$T = createFileRoute("/readyz")({
|
|
6852
6808
|
server: {
|
|
6853
6809
|
handlers: {
|
|
6854
6810
|
GET: readinessResponse
|
|
@@ -6858,23 +6814,23 @@ const Route$N = createFileRoute("/readyz")({
|
|
|
6858
6814
|
function livenessResponse() {
|
|
6859
6815
|
return Response.json({ status: "ok" }, { headers: { "cache-control": "no-store" } });
|
|
6860
6816
|
}
|
|
6861
|
-
const Route$
|
|
6817
|
+
const Route$S = createFileRoute("/livez")({
|
|
6862
6818
|
server: {
|
|
6863
6819
|
handlers: {
|
|
6864
6820
|
GET: livenessResponse
|
|
6865
6821
|
}
|
|
6866
6822
|
}
|
|
6867
6823
|
});
|
|
6868
|
-
const $$splitErrorComponentImporter$1 = () => import("./index-
|
|
6869
|
-
const $$splitComponentImporter$1 = () => import("./index-
|
|
6870
|
-
const Route$
|
|
6824
|
+
const $$splitErrorComponentImporter$1 = () => import("./index-DW-M7XUG.mjs");
|
|
6825
|
+
const $$splitComponentImporter$1 = () => import("./index-Bcj1Hcy4.mjs");
|
|
6826
|
+
const Route$R = createFileRoute("/")({
|
|
6871
6827
|
ssr: false,
|
|
6872
6828
|
component: lazyRouteComponent($$splitComponentImporter$1, "component"),
|
|
6873
6829
|
errorComponent: lazyRouteComponent($$splitErrorComponentImporter$1, "errorComponent")
|
|
6874
6830
|
});
|
|
6875
|
-
const $$splitErrorComponentImporter = () => import("../_sessionId-
|
|
6876
|
-
const $$splitComponentImporter = () => import("../_sessionId-
|
|
6877
|
-
const Route$
|
|
6831
|
+
const $$splitErrorComponentImporter = () => import("../_sessionId-CJ99gZFV.mjs");
|
|
6832
|
+
const $$splitComponentImporter = () => import("../_sessionId-DEjScBpK.mjs");
|
|
6833
|
+
const Route$Q = createFileRoute("/session/$sessionId")({
|
|
6878
6834
|
ssr: false,
|
|
6879
6835
|
component: lazyRouteComponent($$splitComponentImporter, "component"),
|
|
6880
6836
|
errorComponent: lazyRouteComponent($$splitErrorComponentImporter, "errorComponent"),
|
|
@@ -7302,9 +7258,9 @@ function providerEntryMatches(provider, entry) {
|
|
|
7302
7258
|
return providerName.includes(normalized) || normalized.includes(providerName);
|
|
7303
7259
|
});
|
|
7304
7260
|
if (nameMatches) return true;
|
|
7305
|
-
const normalizedProviderUrls = providerBaseUrls(provider).map((
|
|
7306
|
-
const baseUrlMatches = entry.baseUrls.some((
|
|
7307
|
-
const normalized = normalizeUrl(
|
|
7261
|
+
const normalizedProviderUrls = providerBaseUrls(provider).map((url2) => normalizeUrl(url2)).filter((url2) => url2 !== null);
|
|
7262
|
+
const baseUrlMatches = entry.baseUrls.some((url2) => {
|
|
7263
|
+
const normalized = normalizeUrl(url2);
|
|
7308
7264
|
return normalized !== null && normalizedProviderUrls.includes(normalized);
|
|
7309
7265
|
});
|
|
7310
7266
|
if (baseUrlMatches) return true;
|
|
@@ -8260,8 +8216,8 @@ function isZhipuProvider(provider) {
|
|
|
8260
8216
|
return normalizedName.includes(normalizedAlias) || normalizedAlias.includes(normalizedName);
|
|
8261
8217
|
});
|
|
8262
8218
|
if (nameMatches) return true;
|
|
8263
|
-
return providerRoutingUrls(provider).some((
|
|
8264
|
-
const normalizedUrl =
|
|
8219
|
+
return providerRoutingUrls(provider).some((url2) => {
|
|
8220
|
+
const normalizedUrl = url2.toLowerCase().replace(/\/+$/, "");
|
|
8265
8221
|
return ZHIPU_BASE_URL_MARKERS.some((marker) => normalizedUrl.includes(marker));
|
|
8266
8222
|
});
|
|
8267
8223
|
}
|
|
@@ -9405,9 +9361,9 @@ function describeApiRoute(apiPath) {
|
|
|
9405
9361
|
const normalizedPath = isOpenAI && !apiPath.startsWith("/v1/") ? `/v1${apiPath}` : apiPath;
|
|
9406
9362
|
return { apiPath, endpointPath, isChatCompletions, isResponses, isOpenAI, normalizedPath };
|
|
9407
9363
|
}
|
|
9408
|
-
function getProxyApiPath(
|
|
9409
|
-
const pathname = stripBasePath(
|
|
9410
|
-
return pathname.replace(/^\/proxy/, "") +
|
|
9364
|
+
function getProxyApiPath(url2) {
|
|
9365
|
+
const pathname = stripBasePath(url2.pathname, getConfiguredBasePath(process.env));
|
|
9366
|
+
return pathname.replace(/^\/proxy/, "") + url2.search;
|
|
9411
9367
|
}
|
|
9412
9368
|
function selectUpstreamBase(route, provider) {
|
|
9413
9369
|
if (provider === null) {
|
|
@@ -9472,8 +9428,8 @@ function buildProxyHeaders(originalHeaders, captureRawHeaders) {
|
|
|
9472
9428
|
}
|
|
9473
9429
|
return { headers, rawHeaders };
|
|
9474
9430
|
}
|
|
9475
|
-
function parseRequestPath(req,
|
|
9476
|
-
const route = describeApiRoute(getProxyApiPath(
|
|
9431
|
+
function parseRequestPath(req, url2) {
|
|
9432
|
+
const route = describeApiRoute(getProxyApiPath(url2));
|
|
9477
9433
|
const isPost = req.method === "POST";
|
|
9478
9434
|
const isChatCompletions = isPost && route.isChatCompletions;
|
|
9479
9435
|
const isMessages = isPost && (route.endpointPath === PATH_V1_MESSAGES$1 || route.isChatCompletions);
|
|
@@ -9641,8 +9597,8 @@ function handleStreamingResponse(upstreamRes, req, startTime, upstreamUrl, log,
|
|
|
9641
9597
|
});
|
|
9642
9598
|
}
|
|
9643
9599
|
async function handleAdmittedProxy(req) {
|
|
9644
|
-
const
|
|
9645
|
-
const parsed = parseRequestPath(req,
|
|
9600
|
+
const url2 = new URL(req.url);
|
|
9601
|
+
const parsed = parseRequestPath(req, url2);
|
|
9646
9602
|
const bodyRead = await readBodyWithLimit(req, MAX_REQUEST_BODY_BYTES);
|
|
9647
9603
|
if (!bodyRead.ok) {
|
|
9648
9604
|
logger.warn(`[handler] Rejected oversized request body: ${req.method} ${parsed.apiPath}`);
|
|
@@ -9818,7 +9774,7 @@ async function handleProxy(req) {
|
|
|
9818
9774
|
return await Promise.reject(error);
|
|
9819
9775
|
}
|
|
9820
9776
|
}
|
|
9821
|
-
const Route$
|
|
9777
|
+
const Route$P = createFileRoute("/proxy/$")({
|
|
9822
9778
|
server: {
|
|
9823
9779
|
handlers: {
|
|
9824
9780
|
GET: ({ request }) => handleProxy(request),
|
|
@@ -9833,18 +9789,18 @@ const Route$J = createFileRoute("/proxy/$")({
|
|
|
9833
9789
|
function parseBooleanFlag(value) {
|
|
9834
9790
|
return value === "1" || value === "true";
|
|
9835
9791
|
}
|
|
9836
|
-
const Route$
|
|
9792
|
+
const Route$O = createFileRoute("/api/sessions")({
|
|
9837
9793
|
server: {
|
|
9838
9794
|
handlers: {
|
|
9839
9795
|
GET: async ({ request }) => {
|
|
9840
|
-
const
|
|
9841
|
-
const sessionId =
|
|
9796
|
+
const url2 = new URL(request.url);
|
|
9797
|
+
const sessionId = url2.searchParams.get("sessionId");
|
|
9842
9798
|
if (sessionId !== null) {
|
|
9843
9799
|
if (sessionId.length === 0) {
|
|
9844
9800
|
return Response.json({ error: "sessionId is required" }, { status: 400 });
|
|
9845
9801
|
}
|
|
9846
9802
|
const limitResult = parseBoundedIntegerParameter({
|
|
9847
|
-
value:
|
|
9803
|
+
value: url2.searchParams.get("limit"),
|
|
9848
9804
|
parameter: "limit",
|
|
9849
9805
|
fallback: SESSION_INFO_DEFAULT_LATEST_LOG_LIMIT,
|
|
9850
9806
|
minimum: 1,
|
|
@@ -9853,8 +9809,8 @@ const Route$I = createFileRoute("/api/sessions")({
|
|
|
9853
9809
|
});
|
|
9854
9810
|
if (!limitResult.ok) return limitResult.response;
|
|
9855
9811
|
const info = await getSessionInfo(sessionId, {
|
|
9856
|
-
baseUrl:
|
|
9857
|
-
includeHistory: parseBooleanFlag(
|
|
9812
|
+
baseUrl: url2.origin,
|
|
9813
|
+
includeHistory: parseBooleanFlag(url2.searchParams.get("includeHistory")),
|
|
9858
9814
|
latestLogLimit: limitResult.value
|
|
9859
9815
|
});
|
|
9860
9816
|
if (info === null) {
|
|
@@ -9862,7 +9818,7 @@ const Route$I = createFileRoute("/api/sessions")({
|
|
|
9862
9818
|
}
|
|
9863
9819
|
return Response.json(info);
|
|
9864
9820
|
}
|
|
9865
|
-
const details =
|
|
9821
|
+
const details = url2.searchParams.get("details");
|
|
9866
9822
|
if (details === "true" || details === "1") {
|
|
9867
9823
|
return Response.json(getSessionSnapshots());
|
|
9868
9824
|
}
|
|
@@ -11226,7 +11182,7 @@ function failureFromRun(run) {
|
|
|
11226
11182
|
};
|
|
11227
11183
|
}
|
|
11228
11184
|
function listRecentFailures(limit) {
|
|
11229
|
-
const failures = listRuns().map(failureFromRun).filter((
|
|
11185
|
+
const failures = listRuns().map(failureFromRun).filter((failure2) => failure2 !== null).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
11230
11186
|
const clampedLimit = clampRecentFailuresLimit(limit);
|
|
11231
11187
|
return {
|
|
11232
11188
|
failures: failures.slice(0, clampedLimit),
|
|
@@ -11282,14 +11238,14 @@ async function readJsonBody$6(request) {
|
|
|
11282
11238
|
return { ok: false };
|
|
11283
11239
|
}
|
|
11284
11240
|
}
|
|
11285
|
-
const Route$
|
|
11241
|
+
const Route$N = createFileRoute("/api/runs")({
|
|
11286
11242
|
server: {
|
|
11287
11243
|
handlers: {
|
|
11288
11244
|
GET: ({ request }) => {
|
|
11289
|
-
const
|
|
11290
|
-
if (
|
|
11245
|
+
const url2 = new URL(request.url);
|
|
11246
|
+
if (url2.searchParams.get("failures") === "1") {
|
|
11291
11247
|
const limitResult = parseBoundedIntegerParameter({
|
|
11292
|
-
value:
|
|
11248
|
+
value: url2.searchParams.get("limit"),
|
|
11293
11249
|
parameter: "limit",
|
|
11294
11250
|
fallback: RECENT_FAILURES_DEFAULT_LIMIT,
|
|
11295
11251
|
minimum: 1,
|
|
@@ -11334,7 +11290,7 @@ const ProviderInputSchema = object({
|
|
|
11334
11290
|
modelMetadata: ProviderModelMetadataListSchema.optional(),
|
|
11335
11291
|
source: _enum(["company", "personal"]).optional()
|
|
11336
11292
|
});
|
|
11337
|
-
const Route$
|
|
11293
|
+
const Route$M = createFileRoute("/api/providers")({
|
|
11338
11294
|
server: {
|
|
11339
11295
|
handlers: {
|
|
11340
11296
|
GET: () => {
|
|
@@ -11874,8 +11830,8 @@ function warningObservation(log, warning) {
|
|
|
11874
11830
|
}
|
|
11875
11831
|
function addLogObservations(map, logs, slowResponseThresholdSeconds) {
|
|
11876
11832
|
for (const log of logs) {
|
|
11877
|
-
const
|
|
11878
|
-
if (
|
|
11833
|
+
const failure2 = requestFailureObservation(log);
|
|
11834
|
+
if (failure2 !== null) addObservation(map, failure2);
|
|
11879
11835
|
const slow = slowResponseObservation(log, slowResponseThresholdSeconds);
|
|
11880
11836
|
if (slow !== null) addObservation(map, slow);
|
|
11881
11837
|
for (const warning of log.warnings ?? []) {
|
|
@@ -11883,8 +11839,8 @@ function addLogObservations(map, logs, slowResponseThresholdSeconds) {
|
|
|
11883
11839
|
}
|
|
11884
11840
|
}
|
|
11885
11841
|
}
|
|
11886
|
-
function alertSeverityFromFailure(
|
|
11887
|
-
switch (
|
|
11842
|
+
function alertSeverityFromFailure(failure2) {
|
|
11843
|
+
switch (failure2.classification.severity) {
|
|
11888
11844
|
case "high":
|
|
11889
11845
|
return "critical";
|
|
11890
11846
|
case "medium":
|
|
@@ -11894,47 +11850,47 @@ function alertSeverityFromFailure(failure) {
|
|
|
11894
11850
|
return "notice";
|
|
11895
11851
|
}
|
|
11896
11852
|
}
|
|
11897
|
-
function providerFromRun(
|
|
11898
|
-
const provider =
|
|
11853
|
+
function providerFromRun(failure2) {
|
|
11854
|
+
const provider = failure2.run.metadata["provider"] ?? failure2.run.metadata["providerName"];
|
|
11899
11855
|
return typeof provider === "string" && provider.length > 0 ? provider : null;
|
|
11900
11856
|
}
|
|
11901
|
-
function modelFromRun(
|
|
11902
|
-
const model =
|
|
11857
|
+
function modelFromRun(failure2) {
|
|
11858
|
+
const model = failure2.run.metadata["model"] ?? failure2.run.metadata["modelName"];
|
|
11903
11859
|
return typeof model === "string" && model.length > 0 ? model : null;
|
|
11904
11860
|
}
|
|
11905
|
-
function runFailureObservation(
|
|
11906
|
-
const groupId =
|
|
11907
|
-
const logEvidenceLinks =
|
|
11861
|
+
function runFailureObservation(failure2) {
|
|
11862
|
+
const groupId = failure2.run.groupId;
|
|
11863
|
+
const logEvidenceLinks = failure2.classification.evidenceLogIds.map(logEvidence$1);
|
|
11908
11864
|
const links = mergeEvidence(
|
|
11909
|
-
baseEvidence(null,
|
|
11865
|
+
baseEvidence(null, failure2.sessionId, failure2.run.id, groupId),
|
|
11910
11866
|
logEvidenceLinks
|
|
11911
11867
|
);
|
|
11912
11868
|
return {
|
|
11913
|
-
fingerprint: fingerprint(["run",
|
|
11914
|
-
severity: alertSeverityFromFailure(
|
|
11869
|
+
fingerprint: fingerprint(["run", failure2.run.id, failure2.classification.category]),
|
|
11870
|
+
severity: alertSeverityFromFailure(failure2),
|
|
11915
11871
|
category: "run-failure",
|
|
11916
11872
|
source: "run",
|
|
11917
|
-
title: `Run ${
|
|
11918
|
-
message:
|
|
11919
|
-
observedAt:
|
|
11920
|
-
logId:
|
|
11921
|
-
sessionId:
|
|
11922
|
-
runId:
|
|
11873
|
+
title: `Run ${failure2.run.status}`,
|
|
11874
|
+
message: failure2.classification.summary,
|
|
11875
|
+
observedAt: failure2.updatedAt,
|
|
11876
|
+
logId: failure2.classification.evidenceLogIds[0] ?? null,
|
|
11877
|
+
sessionId: failure2.sessionId,
|
|
11878
|
+
runId: failure2.run.id,
|
|
11923
11879
|
groupId,
|
|
11924
|
-
provider: providerFromRun(
|
|
11925
|
-
model: modelFromRun(
|
|
11880
|
+
provider: providerFromRun(failure2),
|
|
11881
|
+
model: modelFromRun(failure2),
|
|
11926
11882
|
apiFormat: null,
|
|
11927
11883
|
evidence: links,
|
|
11928
11884
|
metadata: {
|
|
11929
|
-
runStatus:
|
|
11930
|
-
failureCategory:
|
|
11931
|
-
evidenceMarkdownPath:
|
|
11885
|
+
runStatus: failure2.run.status,
|
|
11886
|
+
failureCategory: failure2.classification.category,
|
|
11887
|
+
evidenceMarkdownPath: failure2.evidenceMarkdownPath
|
|
11932
11888
|
}
|
|
11933
11889
|
};
|
|
11934
11890
|
}
|
|
11935
11891
|
function addRunFailureObservations(map, recentFailures) {
|
|
11936
|
-
for (const
|
|
11937
|
-
addObservation(map, runFailureObservation(
|
|
11892
|
+
for (const failure2 of recentFailures) {
|
|
11893
|
+
addObservation(map, runFailureObservation(failure2));
|
|
11938
11894
|
}
|
|
11939
11895
|
}
|
|
11940
11896
|
function memberProvider(member) {
|
|
@@ -12950,14 +12906,14 @@ const MAX_CONTEXT_CHARS = 18e3;
|
|
|
12950
12906
|
const MAX_HISTORY_CHARS = 4e3;
|
|
12951
12907
|
const PI_AGENT_MAX_OUTPUT_TOKENS = 900;
|
|
12952
12908
|
const DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
12953
|
-
function nonEmpty(value) {
|
|
12909
|
+
function nonEmpty$1(value) {
|
|
12954
12910
|
return value !== void 0 && value.trim() !== "" ? value.trim() : void 0;
|
|
12955
12911
|
}
|
|
12956
12912
|
function firstModel(provider) {
|
|
12957
|
-
const explicitModel = nonEmpty(provider.model);
|
|
12913
|
+
const explicitModel = nonEmpty$1(provider.model);
|
|
12958
12914
|
if (explicitModel !== void 0) return explicitModel;
|
|
12959
12915
|
for (const model of provider.models) {
|
|
12960
|
-
const trimmed = nonEmpty(model);
|
|
12916
|
+
const trimmed = nonEmpty$1(model);
|
|
12961
12917
|
if (trimmed !== void 0) return trimmed;
|
|
12962
12918
|
}
|
|
12963
12919
|
return void 0;
|
|
@@ -12978,7 +12934,7 @@ function selectProvider(request) {
|
|
|
12978
12934
|
for (const provider of providerCandidates(request)) {
|
|
12979
12935
|
const modelName = request.model ?? firstModel(provider);
|
|
12980
12936
|
if (modelName === void 0) continue;
|
|
12981
|
-
const responsesBaseUrl = nonEmpty(provider.openaiResponsesBaseUrl);
|
|
12937
|
+
const responsesBaseUrl = nonEmpty$1(provider.openaiResponsesBaseUrl);
|
|
12982
12938
|
if (responsesBaseUrl !== void 0) {
|
|
12983
12939
|
return {
|
|
12984
12940
|
provider,
|
|
@@ -12989,7 +12945,7 @@ function selectProvider(request) {
|
|
|
12989
12945
|
upstreamPath: "/v1/responses"
|
|
12990
12946
|
};
|
|
12991
12947
|
}
|
|
12992
|
-
const openaiBaseUrl = nonEmpty(provider.openaiBaseUrl);
|
|
12948
|
+
const openaiBaseUrl = nonEmpty$1(provider.openaiBaseUrl);
|
|
12993
12949
|
if (openaiBaseUrl !== void 0) {
|
|
12994
12950
|
return {
|
|
12995
12951
|
provider,
|
|
@@ -13000,7 +12956,7 @@ function selectProvider(request) {
|
|
|
13000
12956
|
upstreamPath: "/v1/chat/completions"
|
|
13001
12957
|
};
|
|
13002
12958
|
}
|
|
13003
|
-
const anthropicBaseUrl = nonEmpty(provider.anthropicBaseUrl);
|
|
12959
|
+
const anthropicBaseUrl = nonEmpty$1(provider.anthropicBaseUrl);
|
|
13004
12960
|
if (anthropicBaseUrl !== void 0) {
|
|
13005
12961
|
return {
|
|
13006
12962
|
provider,
|
|
@@ -13131,14 +13087,14 @@ function stripUrlSuffix(value, suffix) {
|
|
|
13131
13087
|
}
|
|
13132
13088
|
function sdkBaseUrlFromFinalUrl(finalUrl, protocol) {
|
|
13133
13089
|
try {
|
|
13134
|
-
const
|
|
13135
|
-
const pathname =
|
|
13090
|
+
const url2 = new URL(finalUrl);
|
|
13091
|
+
const pathname = url2.pathname.replace(/\/+$/, "");
|
|
13136
13092
|
const suffixes = protocol === "openaiResponses" ? ["/responses"] : protocol === "openaiChat" ? ["/chat/completions"] : ["/v1/messages", "/messages"];
|
|
13137
13093
|
for (const suffix of suffixes) {
|
|
13138
13094
|
const nextPath = stripUrlSuffix(pathname, suffix);
|
|
13139
13095
|
if (nextPath !== null) {
|
|
13140
|
-
|
|
13141
|
-
return
|
|
13096
|
+
url2.pathname = nextPath;
|
|
13097
|
+
return url2.toString().replace(/\/+$/, "");
|
|
13142
13098
|
}
|
|
13143
13099
|
}
|
|
13144
13100
|
} catch {
|
|
@@ -13278,7 +13234,7 @@ async function runPiAgent(request) {
|
|
|
13278
13234
|
queryStats: retrieval.queryStats
|
|
13279
13235
|
};
|
|
13280
13236
|
}
|
|
13281
|
-
const Route$
|
|
13237
|
+
const Route$L = createFileRoute("/api/pi-agent")({
|
|
13282
13238
|
server: {
|
|
13283
13239
|
handlers: {
|
|
13284
13240
|
POST: async ({ request }) => {
|
|
@@ -13301,7 +13257,7 @@ const Route$F = createFileRoute("/api/pi-agent")({
|
|
|
13301
13257
|
}
|
|
13302
13258
|
}
|
|
13303
13259
|
});
|
|
13304
|
-
const Route$
|
|
13260
|
+
const Route$K = createFileRoute("/api/models")({
|
|
13305
13261
|
server: {
|
|
13306
13262
|
handlers: {
|
|
13307
13263
|
GET: () => Response.json(getModels())
|
|
@@ -13309,33 +13265,158 @@ const Route$E = createFileRoute("/api/models")({
|
|
|
13309
13265
|
}
|
|
13310
13266
|
});
|
|
13311
13267
|
const name = "@tonyclaw/agent-inspector";
|
|
13312
|
-
const version = "3.0.
|
|
13268
|
+
const version = "3.0.45";
|
|
13313
13269
|
const packageJson = {
|
|
13314
13270
|
name,
|
|
13315
13271
|
version
|
|
13316
13272
|
};
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
|
|
13327
|
-
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13337
|
-
|
|
13338
|
-
|
|
13273
|
+
const INSTANCE_API_SCHEMA_VERSION = 1;
|
|
13274
|
+
const InstanceStateSchema = _enum(["running", "stopped", "conflict"]);
|
|
13275
|
+
const InstanceStateReasonSchema = _enum(["identity_mismatch", "port_conflict"]);
|
|
13276
|
+
const InstanceUrlsSchema = object({
|
|
13277
|
+
ui: url(),
|
|
13278
|
+
api: url(),
|
|
13279
|
+
health: url(),
|
|
13280
|
+
proxy: url(),
|
|
13281
|
+
openai: url(),
|
|
13282
|
+
mcp: url()
|
|
13283
|
+
}).strict();
|
|
13284
|
+
const InstanceStatusOutputSchema = object({
|
|
13285
|
+
schemaVersion: literal(INSTANCE_API_SCHEMA_VERSION),
|
|
13286
|
+
name: string().min(1),
|
|
13287
|
+
instanceId: string().min(1),
|
|
13288
|
+
state: InstanceStateSchema,
|
|
13289
|
+
stateReason: InstanceStateReasonSchema.nullable(),
|
|
13290
|
+
checkedAt: datetime(),
|
|
13291
|
+
createdAt: datetime(),
|
|
13292
|
+
updatedAt: datetime(),
|
|
13293
|
+
launch: object({
|
|
13294
|
+
launchId: string().min(1),
|
|
13295
|
+
startedAt: datetime()
|
|
13296
|
+
}).strict(),
|
|
13297
|
+
host: string().min(1),
|
|
13298
|
+
publicPort: number().int().min(1).max(65535),
|
|
13299
|
+
upstreamPort: number().int().min(1).max(65535),
|
|
13300
|
+
basePath: string().startsWith("/"),
|
|
13301
|
+
captureMode: _enum(["simple", "full"]),
|
|
13302
|
+
dataDir: string().min(1),
|
|
13303
|
+
supervisorPid: number().int().positive().nullable(),
|
|
13304
|
+
urls: InstanceUrlsSchema
|
|
13305
|
+
}).strict();
|
|
13306
|
+
const InstanceConnectionOutputSchema = object({
|
|
13307
|
+
schemaVersion: literal(INSTANCE_API_SCHEMA_VERSION),
|
|
13308
|
+
instance: InstanceStatusOutputSchema,
|
|
13309
|
+
baseUrl: url(),
|
|
13310
|
+
endpoints: InstanceUrlsSchema,
|
|
13311
|
+
environment: object({
|
|
13312
|
+
ANTHROPIC_BASE_URL: url(),
|
|
13313
|
+
LLM_BASE_URL: url(),
|
|
13314
|
+
OPENAI_BASE_URL: url(),
|
|
13315
|
+
AGENT_INSPECTOR_MCP_URL: url()
|
|
13316
|
+
}).strict(),
|
|
13317
|
+
mcp: object({
|
|
13318
|
+
serverName: string().min(1),
|
|
13319
|
+
transport: literal("streamable-http"),
|
|
13320
|
+
url: url()
|
|
13321
|
+
}).strict(),
|
|
13322
|
+
codex: object({
|
|
13323
|
+
providerName: string().min(1),
|
|
13324
|
+
baseUrl: url(),
|
|
13325
|
+
wireApi: literal("responses")
|
|
13326
|
+
}).strict()
|
|
13327
|
+
}).strict();
|
|
13328
|
+
const InstanceStartRequestSchema = object({
|
|
13329
|
+
port: number().int().min(1).max(65535).optional(),
|
|
13330
|
+
basePath: string().min(1).max(2048).refine(
|
|
13331
|
+
(value) => value.startsWith("/") && !value.includes("?") && !value.includes("#") && !value.includes("\\"),
|
|
13332
|
+
"Base Path must be an absolute URL path without query, hash, or backslash"
|
|
13333
|
+
).optional(),
|
|
13334
|
+
host: string().min(1).max(253).refine(
|
|
13335
|
+
(value) => value.trim() === value && !value.includes("://") && !value.includes("/") && !value.includes("\\") && !/\s/u.test(value),
|
|
13336
|
+
"Host must be a hostname or IP address without protocol, path, or whitespace"
|
|
13337
|
+
).optional(),
|
|
13338
|
+
captureMode: _enum(["simple", "full"]).optional(),
|
|
13339
|
+
dataDir: string().trim().min(1).max(32767).optional()
|
|
13340
|
+
}).strict();
|
|
13341
|
+
const InstanceEmptyRequestSchema = object({}).strict();
|
|
13342
|
+
const InstanceOperationSchema = _enum(["start", "stop", "restart"]);
|
|
13343
|
+
const InstanceOperationOutcomeSchema = _enum([
|
|
13344
|
+
"created",
|
|
13345
|
+
"started",
|
|
13346
|
+
"already_running",
|
|
13347
|
+
"stopped",
|
|
13348
|
+
"already_stopped",
|
|
13349
|
+
"restarted"
|
|
13350
|
+
]);
|
|
13351
|
+
const InstanceOperationOutputSchema = object({
|
|
13352
|
+
schemaVersion: literal(INSTANCE_API_SCHEMA_VERSION),
|
|
13353
|
+
operation: InstanceOperationSchema,
|
|
13354
|
+
outcome: InstanceOperationOutcomeSchema,
|
|
13355
|
+
instance: InstanceStatusOutputSchema
|
|
13356
|
+
}).strict();
|
|
13357
|
+
const InstanceListOutputSchema = object({
|
|
13358
|
+
schemaVersion: literal(INSTANCE_API_SCHEMA_VERSION),
|
|
13359
|
+
instances: array(InstanceStatusOutputSchema)
|
|
13360
|
+
}).strict();
|
|
13361
|
+
const InstancePublicErrorCodeSchema = _enum([
|
|
13362
|
+
"argument_invalid",
|
|
13363
|
+
"config_mismatch",
|
|
13364
|
+
"data_dir_conflict",
|
|
13365
|
+
"data_dir_invalid",
|
|
13366
|
+
"identity_mismatch",
|
|
13367
|
+
"instance_not_found",
|
|
13368
|
+
"launch_failed",
|
|
13369
|
+
"launch_unavailable",
|
|
13370
|
+
"lock_not_owned",
|
|
13371
|
+
"lock_timeout",
|
|
13372
|
+
"name_invalid",
|
|
13373
|
+
"port_conflict",
|
|
13374
|
+
"port_exhausted",
|
|
13375
|
+
"port_invalid",
|
|
13376
|
+
"record_invalid",
|
|
13377
|
+
"registry_io",
|
|
13378
|
+
"restart_failed_degraded",
|
|
13379
|
+
"restart_failed_rolled_back",
|
|
13380
|
+
"self_lifecycle_unsupported",
|
|
13381
|
+
"shutdown_failed"
|
|
13382
|
+
]);
|
|
13383
|
+
const InstancePublicErrorSchema = object({
|
|
13384
|
+
code: InstancePublicErrorCodeSchema,
|
|
13385
|
+
message: string().min(1),
|
|
13386
|
+
retryable: boolean()
|
|
13387
|
+
}).strict();
|
|
13388
|
+
const InstanceErrorOutputSchema = object({
|
|
13389
|
+
schemaVersion: literal(INSTANCE_API_SCHEMA_VERSION),
|
|
13390
|
+
error: InstancePublicErrorSchema
|
|
13391
|
+
}).strict();
|
|
13392
|
+
function isRetryableInstanceError(code) {
|
|
13393
|
+
switch (code) {
|
|
13394
|
+
case "launch_failed":
|
|
13395
|
+
case "launch_unavailable":
|
|
13396
|
+
case "lock_timeout":
|
|
13397
|
+
case "port_exhausted":
|
|
13398
|
+
case "registry_io":
|
|
13399
|
+
case "restart_failed_degraded":
|
|
13400
|
+
case "restart_failed_rolled_back":
|
|
13401
|
+
case "shutdown_failed":
|
|
13402
|
+
return true;
|
|
13403
|
+
case "argument_invalid":
|
|
13404
|
+
case "config_mismatch":
|
|
13405
|
+
case "data_dir_conflict":
|
|
13406
|
+
case "data_dir_invalid":
|
|
13407
|
+
case "identity_mismatch":
|
|
13408
|
+
case "instance_not_found":
|
|
13409
|
+
case "lock_not_owned":
|
|
13410
|
+
case "name_invalid":
|
|
13411
|
+
case "port_conflict":
|
|
13412
|
+
case "port_invalid":
|
|
13413
|
+
case "record_invalid":
|
|
13414
|
+
case "self_lifecycle_unsupported":
|
|
13415
|
+
return false;
|
|
13416
|
+
}
|
|
13417
|
+
}
|
|
13418
|
+
function instanceErrorOutput(error) {
|
|
13419
|
+
return { schemaVersion: INSTANCE_API_SCHEMA_VERSION, error };
|
|
13339
13420
|
}
|
|
13340
13421
|
function envString(name2) {
|
|
13341
13422
|
const value = process.env[name2];
|
|
@@ -13370,6 +13451,7 @@ function setCurrentContext(patch) {
|
|
|
13370
13451
|
};
|
|
13371
13452
|
return currentContext;
|
|
13372
13453
|
}
|
|
13454
|
+
const MCP_INSTANCE_WRITES_ENV = "AGENT_INSPECTOR_MCP_INSTANCE_WRITES";
|
|
13373
13455
|
function parseFlag(value) {
|
|
13374
13456
|
if (value === void 0) return null;
|
|
13375
13457
|
switch (value.trim().toLowerCase()) {
|
|
@@ -13393,6 +13475,20 @@ function isMcpWriteEnabled() {
|
|
|
13393
13475
|
const writesFlag = parseFlag(process.env["AGENT_INSPECTOR_MCP_WRITES"]);
|
|
13394
13476
|
return writesFlag === true;
|
|
13395
13477
|
}
|
|
13478
|
+
function isMcpInstanceWriteEnabled() {
|
|
13479
|
+
return process.env[MCP_INSTANCE_WRITES_ENV] === "1";
|
|
13480
|
+
}
|
|
13481
|
+
function getMcpInstanceModeInfo() {
|
|
13482
|
+
const writesEnabled = isMcpInstanceWriteEnabled();
|
|
13483
|
+
return {
|
|
13484
|
+
writeMode: writesEnabled ? "enabled" : "readonly",
|
|
13485
|
+
writesEnabled,
|
|
13486
|
+
env: {
|
|
13487
|
+
AGENT_INSPECTOR_MCP_INSTANCE_WRITES: process.env[MCP_INSTANCE_WRITES_ENV] ?? null
|
|
13488
|
+
},
|
|
13489
|
+
note: writesEnabled ? "Named-instance lifecycle tools passed the instance-specific opt-in. General MCP writes must also be enabled." : "Named-instance lifecycle tools require AGENT_INSPECTOR_MCP_INSTANCE_WRITES=1 in addition to general MCP writes."
|
|
13490
|
+
};
|
|
13491
|
+
}
|
|
13396
13492
|
function getMcpModeInfo() {
|
|
13397
13493
|
const writesEnabled = isMcpWriteEnabled();
|
|
13398
13494
|
return {
|
|
@@ -13405,6 +13501,95 @@ function getMcpModeInfo() {
|
|
|
13405
13501
|
note: writesEnabled ? "MCP write/action tools are enabled explicitly by AGENT_INSPECTOR_MCP_WRITES=1." : "MCP is readonly by default. Read tools/resources still work; set AGENT_INSPECTOR_MCP_WRITES=1 to enable mutations."
|
|
13406
13502
|
};
|
|
13407
13503
|
}
|
|
13504
|
+
const INSTANCE_LIFECYCLE_TIMEOUT_MS = 6e4;
|
|
13505
|
+
const InstanceToolNameSchema = string().trim().min(1).max(64);
|
|
13506
|
+
const InstanceLifecycleToolInputSchema = InstanceStartRequestSchema.extend({
|
|
13507
|
+
name: InstanceToolNameSchema
|
|
13508
|
+
});
|
|
13509
|
+
const InstanceNameToolInputSchema = object({
|
|
13510
|
+
name: InstanceToolNameSchema
|
|
13511
|
+
}).strict();
|
|
13512
|
+
function textJson$1(data) {
|
|
13513
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
13514
|
+
}
|
|
13515
|
+
function toolError$1(message) {
|
|
13516
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
13517
|
+
}
|
|
13518
|
+
async function responseJson$1(response) {
|
|
13519
|
+
try {
|
|
13520
|
+
const value = await response.json();
|
|
13521
|
+
return value;
|
|
13522
|
+
} catch {
|
|
13523
|
+
return null;
|
|
13524
|
+
}
|
|
13525
|
+
}
|
|
13526
|
+
async function validatedInstanceResponse(response, schema, operation) {
|
|
13527
|
+
const value = await responseJson$1(response);
|
|
13528
|
+
if (!response.ok) {
|
|
13529
|
+
const error = InstanceErrorOutputSchema.safeParse(value);
|
|
13530
|
+
return error.success ? { ...textJson$1(error.data), isError: true } : toolError$1(
|
|
13531
|
+
`${operation} returned HTTP ${String(response.status)} with an invalid error body`
|
|
13532
|
+
);
|
|
13533
|
+
}
|
|
13534
|
+
const parsed = schema.safeParse(value);
|
|
13535
|
+
return parsed.success ? textJson$1(parsed.data) : toolError$1(`${operation} returned an invalid public instance document`);
|
|
13536
|
+
}
|
|
13537
|
+
function instancePath(name2, suffix = "") {
|
|
13538
|
+
return `/api/instances/${encodeURIComponent(name2)}${suffix}`;
|
|
13539
|
+
}
|
|
13540
|
+
function jsonPost(body) {
|
|
13541
|
+
return {
|
|
13542
|
+
method: "POST",
|
|
13543
|
+
headers: {
|
|
13544
|
+
accept: "application/json",
|
|
13545
|
+
"content-type": "application/json"
|
|
13546
|
+
},
|
|
13547
|
+
body: JSON.stringify(body),
|
|
13548
|
+
timeoutMs: INSTANCE_LIFECYCLE_TIMEOUT_MS
|
|
13549
|
+
};
|
|
13550
|
+
}
|
|
13551
|
+
function lifecycleRequest(input) {
|
|
13552
|
+
return {
|
|
13553
|
+
port: input.port,
|
|
13554
|
+
basePath: input.basePath,
|
|
13555
|
+
host: input.host,
|
|
13556
|
+
captureMode: input.captureMode,
|
|
13557
|
+
dataDir: input.dataDir
|
|
13558
|
+
};
|
|
13559
|
+
}
|
|
13560
|
+
async function listInstancesImpl(callApi2) {
|
|
13561
|
+
const response = await callApi2("/api/instances", {
|
|
13562
|
+
headers: { accept: "application/json" }
|
|
13563
|
+
});
|
|
13564
|
+
return validatedInstanceResponse(response, InstanceListOutputSchema, "GET /api/instances");
|
|
13565
|
+
}
|
|
13566
|
+
async function getInstanceImpl(callApi2, name2) {
|
|
13567
|
+
const path2 = instancePath(name2);
|
|
13568
|
+
const response = await callApi2(path2, { headers: { accept: "application/json" } });
|
|
13569
|
+
return validatedInstanceResponse(response, InstanceStatusOutputSchema, `GET ${path2}`);
|
|
13570
|
+
}
|
|
13571
|
+
async function getInstanceConnectionImpl(callApi2, name2) {
|
|
13572
|
+
const path2 = instancePath(name2, "/connection");
|
|
13573
|
+
const response = await callApi2(path2, { headers: { accept: "application/json" } });
|
|
13574
|
+
return validatedInstanceResponse(response, InstanceConnectionOutputSchema, `GET ${path2}`);
|
|
13575
|
+
}
|
|
13576
|
+
async function startInstanceImpl(callApi2, input) {
|
|
13577
|
+
const path2 = instancePath(input.name, "/start");
|
|
13578
|
+
const request = lifecycleRequest(input);
|
|
13579
|
+
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(request)));
|
|
13580
|
+
return validatedInstanceResponse(response, InstanceOperationOutputSchema, `POST ${path2}`);
|
|
13581
|
+
}
|
|
13582
|
+
async function stopInstanceImpl(callApi2, name2) {
|
|
13583
|
+
const path2 = instancePath(name2, "/stop");
|
|
13584
|
+
const response = await callApi2(path2, jsonPost(InstanceEmptyRequestSchema.parse({})));
|
|
13585
|
+
return validatedInstanceResponse(response, InstanceOperationOutputSchema, `POST ${path2}`);
|
|
13586
|
+
}
|
|
13587
|
+
async function restartInstanceImpl(callApi2, input) {
|
|
13588
|
+
const path2 = instancePath(input.name, "/restart");
|
|
13589
|
+
const request = lifecycleRequest(input);
|
|
13590
|
+
const response = await callApi2(path2, jsonPost(InstanceStartRequestSchema.parse(request)));
|
|
13591
|
+
return validatedInstanceResponse(response, InstanceOperationOutputSchema, `POST ${path2}`);
|
|
13592
|
+
}
|
|
13408
13593
|
function groupEvidenceDir(groupId) {
|
|
13409
13594
|
return join(getDataDir(), "evidence", "groups", groupId);
|
|
13410
13595
|
}
|
|
@@ -13774,9 +13959,16 @@ async function callApi(path2, options = {}) {
|
|
|
13774
13959
|
if (!path2.startsWith("/")) {
|
|
13775
13960
|
throw new Error(`callApi: path must start with '/', got: ${path2}`);
|
|
13776
13961
|
}
|
|
13777
|
-
const port =
|
|
13778
|
-
const
|
|
13962
|
+
const port = getPublicPort();
|
|
13963
|
+
const publicBasePath = getPublicBasePath();
|
|
13964
|
+
const url2 = `http://127.0.0.1:${port}${withBasePath(path2, publicBasePath)}`;
|
|
13779
13965
|
const { timeoutMs = DEFAULT_TIMEOUT_MS, signal: userSignal, ...rest } = options;
|
|
13966
|
+
const headers = new Headers(rest.headers);
|
|
13967
|
+
headers.set(
|
|
13968
|
+
AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER,
|
|
13969
|
+
publicBasePath.length === 0 ? "/" : publicBasePath
|
|
13970
|
+
);
|
|
13971
|
+
headers.set(AGENT_INSPECTOR_PUBLIC_PORT_HEADER, String(port));
|
|
13780
13972
|
const controller = new AbortController();
|
|
13781
13973
|
const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
13782
13974
|
if (userSignal !== void 0 && userSignal !== null) {
|
|
@@ -13787,7 +13979,7 @@ async function callApi(path2, options = {}) {
|
|
|
13787
13979
|
}
|
|
13788
13980
|
}
|
|
13789
13981
|
try {
|
|
13790
|
-
return await fetch(
|
|
13982
|
+
return await fetch(url2, { ...rest, headers, signal: controller.signal });
|
|
13791
13983
|
} catch (err) {
|
|
13792
13984
|
if (err instanceof Error && err.name === "AbortError") {
|
|
13793
13985
|
throw new LoopbackTimeoutError(path2, timeoutMs);
|
|
@@ -13995,7 +14187,7 @@ function redactedProvidersResult(value) {
|
|
|
13995
14187
|
return textJson(toRedactedProviderDtos(parsed.data));
|
|
13996
14188
|
}
|
|
13997
14189
|
function inspectorBaseUrl() {
|
|
13998
|
-
return `http://127.0.0.1:${String(
|
|
14190
|
+
return `http://127.0.0.1:${String(getPublicPort())}`;
|
|
13999
14191
|
}
|
|
14000
14192
|
function hasBodyText(value, byteLength) {
|
|
14001
14193
|
if (value !== null && value.length > 0) return true;
|
|
@@ -14527,8 +14719,8 @@ async function diagnoseCaptureImpl(callApi2, args = {}) {
|
|
|
14527
14719
|
runtimePayload = JSON.parse(runtimeText);
|
|
14528
14720
|
} catch {
|
|
14529
14721
|
}
|
|
14530
|
-
const runtimeRecord = isRecord(runtimePayload) ? runtimePayload : {};
|
|
14531
|
-
const configRecord = isRecord(runtimeRecord["config"]) ? runtimeRecord["config"] : {};
|
|
14722
|
+
const runtimeRecord = isRecord$2(runtimePayload) ? runtimePayload : {};
|
|
14723
|
+
const configRecord = isRecord$2(runtimeRecord["config"]) ? runtimeRecord["config"] : {};
|
|
14532
14724
|
const captureMode = stringField(configRecord, "captureMode");
|
|
14533
14725
|
let log = null;
|
|
14534
14726
|
if (args.id !== void 0) {
|
|
@@ -14850,11 +15042,11 @@ function collectProviderDiagnosticIssues(value, path2, issues) {
|
|
|
14850
15042
|
);
|
|
14851
15043
|
return;
|
|
14852
15044
|
}
|
|
14853
|
-
if (!isRecord(value)) return;
|
|
14854
|
-
const
|
|
15045
|
+
if (!isRecord$2(value)) return;
|
|
15046
|
+
const success2 = value["success"];
|
|
14855
15047
|
const status = firstNumberField(value, ["status", "responseStatus", "statusCode"]);
|
|
14856
15048
|
const error = firstStringField(value, ["error", "message", "errorMessage"]);
|
|
14857
|
-
if (
|
|
15049
|
+
if (success2 === false || error !== null || status !== null && status >= 400) {
|
|
14858
15050
|
issues.push({
|
|
14859
15051
|
path: path2,
|
|
14860
15052
|
status,
|
|
@@ -15055,14 +15247,10 @@ async function getServer() {
|
|
|
15055
15247
|
return initPromise;
|
|
15056
15248
|
}
|
|
15057
15249
|
async function handleMcpRequest(request) {
|
|
15058
|
-
|
|
15059
|
-
const
|
|
15060
|
-
|
|
15061
|
-
|
|
15062
|
-
} catch {
|
|
15063
|
-
}
|
|
15064
|
-
const { transport } = await getServer();
|
|
15065
|
-
return transport.handleRequest(request);
|
|
15250
|
+
return runWithRequestPublicBasePath(request, async () => {
|
|
15251
|
+
const { transport } = await getServer();
|
|
15252
|
+
return transport.handleRequest(request);
|
|
15253
|
+
});
|
|
15066
15254
|
}
|
|
15067
15255
|
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.
|
|
15068
15256
|
|
|
@@ -15084,6 +15272,22 @@ Returns: array of "thick summary" objects, each containing:
|
|
|
15084
15272
|
|
|
15085
15273
|
Either preview field is null when the body is unknown format, unparseable, or the relevant content is non-text (e.g., image-only).`;
|
|
15086
15274
|
const PROVIDER_WRITE_WARNING = "⚠ This tool mutates provider configuration. Confirm with the user before invoking.";
|
|
15275
|
+
const INSTANCE_LIFECYCLE_WRITE_WARNING = "This tool changes a local Agent Inspector process. Obtain explicit user approval before invoking it.";
|
|
15276
|
+
const INSTANCE_READ_TOOL_NAMES = [
|
|
15277
|
+
"inspector_list_instances",
|
|
15278
|
+
"inspector_get_instance",
|
|
15279
|
+
"inspector_get_instance_connection"
|
|
15280
|
+
];
|
|
15281
|
+
const INSTANCE_WRITE_TOOL_NAMES = [
|
|
15282
|
+
"inspector_start_instance",
|
|
15283
|
+
"inspector_stop_instance",
|
|
15284
|
+
"inspector_restart_instance"
|
|
15285
|
+
];
|
|
15286
|
+
const INSTANCE_RESOURCE_URIS = [
|
|
15287
|
+
"inspector://instances",
|
|
15288
|
+
"inspector://instances/{name}",
|
|
15289
|
+
"inspector://instances/{name}/connection"
|
|
15290
|
+
];
|
|
15087
15291
|
const AddProviderInputSchema = object({
|
|
15088
15292
|
name: string().min(1).describe("Provider display name."),
|
|
15089
15293
|
apiKey: string().min(1).describe("Plaintext provider API key."),
|
|
@@ -15132,9 +15336,9 @@ function jsonResource(uri, data) {
|
|
|
15132
15336
|
return textResource(uri, "application/json", JSON.stringify(data, null, 2));
|
|
15133
15337
|
}
|
|
15134
15338
|
function mcpEndpointUrl() {
|
|
15135
|
-
return `http://localhost:${String(
|
|
15339
|
+
return `http://localhost:${String(getPublicPort())}${withBasePath(
|
|
15136
15340
|
"/api/mcp",
|
|
15137
|
-
|
|
15341
|
+
getPublicBasePath()
|
|
15138
15342
|
)}`;
|
|
15139
15343
|
}
|
|
15140
15344
|
function mcpHealth() {
|
|
@@ -15146,7 +15350,7 @@ function mcpHealth() {
|
|
|
15146
15350
|
},
|
|
15147
15351
|
transport: {
|
|
15148
15352
|
primary: "streamable-http",
|
|
15149
|
-
endpoint:
|
|
15353
|
+
endpoint: mcpEndpointUrl(),
|
|
15150
15354
|
stateless: true
|
|
15151
15355
|
},
|
|
15152
15356
|
mode: getMcpModeInfo(),
|
|
@@ -15158,6 +15362,7 @@ function mcpHealth() {
|
|
|
15158
15362
|
};
|
|
15159
15363
|
}
|
|
15160
15364
|
const WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
15365
|
+
...INSTANCE_WRITE_TOOL_NAMES,
|
|
15161
15366
|
"inspector_create_run",
|
|
15162
15367
|
"inspector_update_run",
|
|
15163
15368
|
"inspector_export_evidence",
|
|
@@ -15181,42 +15386,59 @@ const WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
15181
15386
|
function mcpCapabilities() {
|
|
15182
15387
|
const readTools = TOOL_NAMES.filter((name2) => !WRITE_TOOL_NAMES.has(name2));
|
|
15183
15388
|
const writeTools = TOOL_NAMES.filter((name2) => WRITE_TOOL_NAMES.has(name2));
|
|
15389
|
+
const generalWriteMode = getMcpModeInfo();
|
|
15390
|
+
const instanceWriteMode = getMcpInstanceModeInfo();
|
|
15184
15391
|
return {
|
|
15185
15392
|
serverInfo: {
|
|
15186
15393
|
name: "agent-inspector",
|
|
15187
15394
|
version: MCP_SERVER_VERSION
|
|
15188
15395
|
},
|
|
15189
|
-
mode:
|
|
15396
|
+
mode: generalWriteMode,
|
|
15190
15397
|
tools: {
|
|
15191
15398
|
read: readTools,
|
|
15192
15399
|
writeOrAction: writeTools
|
|
15193
15400
|
},
|
|
15194
15401
|
resources: RESOURCE_NAMES,
|
|
15195
|
-
prompts: PROMPT_NAMES
|
|
15402
|
+
prompts: PROMPT_NAMES,
|
|
15403
|
+
instanceControl: {
|
|
15404
|
+
tools: {
|
|
15405
|
+
read: INSTANCE_READ_TOOL_NAMES,
|
|
15406
|
+
writeOrAction: INSTANCE_WRITE_TOOL_NAMES
|
|
15407
|
+
},
|
|
15408
|
+
resources: INSTANCE_RESOURCE_URIS,
|
|
15409
|
+
writeGates: {
|
|
15410
|
+
general: generalWriteMode,
|
|
15411
|
+
instance: instanceWriteMode,
|
|
15412
|
+
lifecycleWritesEnabled: generalWriteMode.writesEnabled && instanceWriteMode.writesEnabled
|
|
15413
|
+
},
|
|
15414
|
+
limitations: {
|
|
15415
|
+
selfStopOrRestart: "The serving managed instance cannot stop or restart itself over REST or MCP. Use the CLI or a sibling controller instance."
|
|
15416
|
+
}
|
|
15417
|
+
}
|
|
15196
15418
|
};
|
|
15197
15419
|
}
|
|
15198
15420
|
function mcpConfig() {
|
|
15199
|
-
const
|
|
15421
|
+
const endpoint2 = mcpEndpointUrl();
|
|
15200
15422
|
return {
|
|
15201
15423
|
streamableHttp: {
|
|
15202
|
-
endpoint,
|
|
15424
|
+
endpoint: endpoint2,
|
|
15203
15425
|
config: {
|
|
15204
15426
|
mcpServers: {
|
|
15205
15427
|
"agent-inspector": {
|
|
15206
15428
|
type: "streamableHttp",
|
|
15207
|
-
url:
|
|
15429
|
+
url: endpoint2
|
|
15208
15430
|
}
|
|
15209
15431
|
}
|
|
15210
15432
|
}
|
|
15211
15433
|
},
|
|
15212
15434
|
stdioBridge: {
|
|
15213
15435
|
command: "agent-inspector-mcp",
|
|
15214
|
-
args: ["stdio", "--url",
|
|
15436
|
+
args: ["stdio", "--url", endpoint2],
|
|
15215
15437
|
config: {
|
|
15216
15438
|
mcpServers: {
|
|
15217
15439
|
"agent-inspector": {
|
|
15218
15440
|
command: "agent-inspector-mcp",
|
|
15219
|
-
args: ["stdio", "--url",
|
|
15441
|
+
args: ["stdio", "--url", endpoint2]
|
|
15220
15442
|
}
|
|
15221
15443
|
}
|
|
15222
15444
|
}
|
|
@@ -15225,7 +15447,7 @@ function mcpConfig() {
|
|
|
15225
15447
|
mcp: {
|
|
15226
15448
|
"agent-inspector": {
|
|
15227
15449
|
type: "local",
|
|
15228
|
-
command: ["agent-inspector-mcp", "stdio", "--url",
|
|
15450
|
+
command: ["agent-inspector-mcp", "stdio", "--url", endpoint2],
|
|
15229
15451
|
enabled: true
|
|
15230
15452
|
}
|
|
15231
15453
|
}
|
|
@@ -15234,7 +15456,7 @@ function mcpConfig() {
|
|
|
15234
15456
|
mcp: {
|
|
15235
15457
|
"agent-inspector": {
|
|
15236
15458
|
type: "local",
|
|
15237
|
-
command: ["agent-inspector-mcp", "stdio", "--url",
|
|
15459
|
+
command: ["agent-inspector-mcp", "stdio", "--url", endpoint2],
|
|
15238
15460
|
enabled: true
|
|
15239
15461
|
}
|
|
15240
15462
|
}
|
|
@@ -15267,6 +15489,32 @@ function safeWriteCall(toolName, fn) {
|
|
|
15267
15489
|
return fn();
|
|
15268
15490
|
});
|
|
15269
15491
|
}
|
|
15492
|
+
function disabledInstanceWriteTool(toolName) {
|
|
15493
|
+
return {
|
|
15494
|
+
content: [
|
|
15495
|
+
{
|
|
15496
|
+
type: "text",
|
|
15497
|
+
text: JSON.stringify(
|
|
15498
|
+
{
|
|
15499
|
+
error: "mcp-instance-write-disabled",
|
|
15500
|
+
tool: toolName,
|
|
15501
|
+
generalMode: getMcpModeInfo(),
|
|
15502
|
+
instanceMode: getMcpInstanceModeInfo()
|
|
15503
|
+
},
|
|
15504
|
+
null,
|
|
15505
|
+
2
|
|
15506
|
+
)
|
|
15507
|
+
}
|
|
15508
|
+
],
|
|
15509
|
+
isError: true
|
|
15510
|
+
};
|
|
15511
|
+
}
|
|
15512
|
+
function safeInstanceWriteCall(toolName, fn) {
|
|
15513
|
+
return safeWriteCall(
|
|
15514
|
+
toolName,
|
|
15515
|
+
() => isMcpInstanceWriteEnabled() ? fn() : disabledInstanceWriteTool(toolName)
|
|
15516
|
+
);
|
|
15517
|
+
}
|
|
15270
15518
|
function variableString(variables, name2) {
|
|
15271
15519
|
const value = variables[name2];
|
|
15272
15520
|
if (Array.isArray(value)) return value[0] ?? null;
|
|
@@ -15291,6 +15539,32 @@ function readServiceJsonResource(uri, result) {
|
|
|
15291
15539
|
if (!result.ok) return textResource(uri, "text/plain", result.message);
|
|
15292
15540
|
return jsonResource(uri, result.value);
|
|
15293
15541
|
}
|
|
15542
|
+
async function readInstanceToolResource(uri, resultPromise) {
|
|
15543
|
+
const result = await resultPromise;
|
|
15544
|
+
const text = result.content[0]?.text ?? "";
|
|
15545
|
+
return textResource(uri, result.isError === true ? "text/plain" : "application/json", text);
|
|
15546
|
+
}
|
|
15547
|
+
async function listInstanceTemplateResources(connection) {
|
|
15548
|
+
try {
|
|
15549
|
+
const response = await callApi("/api/instances", {
|
|
15550
|
+
headers: { accept: "application/json" }
|
|
15551
|
+
});
|
|
15552
|
+
if (!response.ok) return listResult([]);
|
|
15553
|
+
const parsed = InstanceListOutputSchema.safeParse(await response.json());
|
|
15554
|
+
if (!parsed.success) return listResult([]);
|
|
15555
|
+
return listResult(
|
|
15556
|
+
parsed.data.instances.map((instance) => ({
|
|
15557
|
+
uri: connection ? `inspector://instances/${encodeURIComponent(instance.name)}/connection` : `inspector://instances/${encodeURIComponent(instance.name)}`,
|
|
15558
|
+
name: connection ? `instance-connection:${instance.name}` : `instance:${instance.name}`,
|
|
15559
|
+
title: connection ? `Agent Inspector connection for ${instance.name}` : `Agent Inspector instance ${instance.name}`,
|
|
15560
|
+
mimeType: "application/json",
|
|
15561
|
+
description: `${instance.state} at ${instance.urls.ui}`
|
|
15562
|
+
}))
|
|
15563
|
+
);
|
|
15564
|
+
} catch {
|
|
15565
|
+
return listResult([]);
|
|
15566
|
+
}
|
|
15567
|
+
}
|
|
15294
15568
|
async function listSessionResources() {
|
|
15295
15569
|
try {
|
|
15296
15570
|
const response = await callApi("/api/sessions");
|
|
@@ -15467,6 +15741,60 @@ function userPrompt(text) {
|
|
|
15467
15741
|
};
|
|
15468
15742
|
}
|
|
15469
15743
|
function registerTools(server) {
|
|
15744
|
+
server.registerTool(
|
|
15745
|
+
"inspector_list_instances",
|
|
15746
|
+
{
|
|
15747
|
+
title: "List named Agent Inspector instances",
|
|
15748
|
+
description: "Returns the versioned, token-free REST instance list with live state, saved configuration, and public URLs. Available in readonly mode.",
|
|
15749
|
+
inputSchema: object({}).strict()
|
|
15750
|
+
},
|
|
15751
|
+
() => safeCall(() => listInstancesImpl(callApi))
|
|
15752
|
+
);
|
|
15753
|
+
server.registerTool(
|
|
15754
|
+
"inspector_get_instance",
|
|
15755
|
+
{
|
|
15756
|
+
title: "Get a named Agent Inspector instance",
|
|
15757
|
+
description: "Returns one versioned, token-free named-instance status document from the REST control plane. Available in readonly mode.",
|
|
15758
|
+
inputSchema: InstanceNameToolInputSchema
|
|
15759
|
+
},
|
|
15760
|
+
({ name: name2 }) => safeCall(() => getInstanceImpl(callApi, name2))
|
|
15761
|
+
);
|
|
15762
|
+
server.registerTool(
|
|
15763
|
+
"inspector_get_instance_connection",
|
|
15764
|
+
{
|
|
15765
|
+
title: "Get coding-agent connection settings for an instance",
|
|
15766
|
+
description: "Returns the REST connection descriptor for one named instance, including protocol-correct proxy, OpenAI /v1, MCP, health, environment, and Codex values. It never includes credentials.",
|
|
15767
|
+
inputSchema: InstanceNameToolInputSchema
|
|
15768
|
+
},
|
|
15769
|
+
({ name: name2 }) => safeCall(() => getInstanceConnectionImpl(callApi, name2))
|
|
15770
|
+
);
|
|
15771
|
+
server.registerTool(
|
|
15772
|
+
"inspector_start_instance",
|
|
15773
|
+
{
|
|
15774
|
+
title: "Start or create a named Agent Inspector instance",
|
|
15775
|
+
description: `${INSTANCE_LIFECYCLE_WRITE_WARNING} Starts, creates, or idempotently reuses a named sibling instance through the REST lifecycle API. Requires both AGENT_INSPECTOR_MCP_WRITES=1 and AGENT_INSPECTOR_MCP_INSTANCE_WRITES=1.`,
|
|
15776
|
+
inputSchema: InstanceLifecycleToolInputSchema
|
|
15777
|
+
},
|
|
15778
|
+
(args) => safeInstanceWriteCall("inspector_start_instance", () => startInstanceImpl(callApi, args))
|
|
15779
|
+
);
|
|
15780
|
+
server.registerTool(
|
|
15781
|
+
"inspector_stop_instance",
|
|
15782
|
+
{
|
|
15783
|
+
title: "Stop a named Agent Inspector instance",
|
|
15784
|
+
description: `${INSTANCE_LIFECYCLE_WRITE_WARNING} Gracefully stops a sibling instance after authenticated ownership checks. The instance serving this MCP connection cannot stop itself. Requires both MCP write gates.`,
|
|
15785
|
+
inputSchema: InstanceNameToolInputSchema
|
|
15786
|
+
},
|
|
15787
|
+
({ name: name2 }) => safeInstanceWriteCall("inspector_stop_instance", () => stopInstanceImpl(callApi, name2))
|
|
15788
|
+
);
|
|
15789
|
+
server.registerTool(
|
|
15790
|
+
"inspector_restart_instance",
|
|
15791
|
+
{
|
|
15792
|
+
title: "Restart a named Agent Inspector instance",
|
|
15793
|
+
description: `${INSTANCE_LIFECYCLE_WRITE_WARNING} Validates and restarts a sibling instance through the bounded REST lifecycle path. The instance serving this MCP connection cannot restart itself. Requires both MCP write gates.`,
|
|
15794
|
+
inputSchema: InstanceLifecycleToolInputSchema
|
|
15795
|
+
},
|
|
15796
|
+
(args) => safeInstanceWriteCall("inspector_restart_instance", () => restartInstanceImpl(callApi, args))
|
|
15797
|
+
);
|
|
15470
15798
|
server.registerTool(
|
|
15471
15799
|
"inspector_list_logs",
|
|
15472
15800
|
{
|
|
@@ -16012,6 +16340,46 @@ PATCH-style update: only the fields you supply are changed. Use models to update
|
|
|
16012
16340
|
);
|
|
16013
16341
|
}
|
|
16014
16342
|
function registerResources(server) {
|
|
16343
|
+
server.registerResource(
|
|
16344
|
+
"inspector_instances",
|
|
16345
|
+
"inspector://instances",
|
|
16346
|
+
{
|
|
16347
|
+
title: "Named Agent Inspector instances",
|
|
16348
|
+
description: "Versioned, token-free named-instance list backed by GET /api/instances.",
|
|
16349
|
+
mimeType: "application/json"
|
|
16350
|
+
},
|
|
16351
|
+
(uri) => readInstanceToolResource(uri, listInstancesImpl(callApi))
|
|
16352
|
+
);
|
|
16353
|
+
server.registerResource(
|
|
16354
|
+
"inspector_instance",
|
|
16355
|
+
new ResourceTemplate("inspector://instances/{name}", {
|
|
16356
|
+
list: async () => await listInstanceTemplateResources(false)
|
|
16357
|
+
}),
|
|
16358
|
+
{
|
|
16359
|
+
title: "Named Agent Inspector instance",
|
|
16360
|
+
description: "Versioned, token-free status for one named instance backed by GET /api/instances/{name}.",
|
|
16361
|
+
mimeType: "application/json"
|
|
16362
|
+
},
|
|
16363
|
+
(uri, variables) => {
|
|
16364
|
+
const name2 = variableString(variables, "name");
|
|
16365
|
+
return name2 === null ? textResource(uri, "text/plain", "Missing instance name") : readInstanceToolResource(uri, getInstanceImpl(callApi, name2));
|
|
16366
|
+
}
|
|
16367
|
+
);
|
|
16368
|
+
server.registerResource(
|
|
16369
|
+
"inspector_instance_connection",
|
|
16370
|
+
new ResourceTemplate("inspector://instances/{name}/connection", {
|
|
16371
|
+
list: async () => await listInstanceTemplateResources(true)
|
|
16372
|
+
}),
|
|
16373
|
+
{
|
|
16374
|
+
title: "Named instance coding-agent connection",
|
|
16375
|
+
description: "Token-free connection descriptor backed by GET /api/instances/{name}/connection.",
|
|
16376
|
+
mimeType: "application/json"
|
|
16377
|
+
},
|
|
16378
|
+
(uri, variables) => {
|
|
16379
|
+
const name2 = variableString(variables, "name");
|
|
16380
|
+
return name2 === null ? textResource(uri, "text/plain", "Missing instance name") : readInstanceToolResource(uri, getInstanceConnectionImpl(callApi, name2));
|
|
16381
|
+
}
|
|
16382
|
+
);
|
|
16015
16383
|
server.registerResource(
|
|
16016
16384
|
"inspector_mcp_health",
|
|
16017
16385
|
"inspector://mcp/health",
|
|
@@ -16456,6 +16824,8 @@ Return:
|
|
|
16456
16824
|
);
|
|
16457
16825
|
}
|
|
16458
16826
|
const TOOL_NAMES = [
|
|
16827
|
+
...INSTANCE_READ_TOOL_NAMES,
|
|
16828
|
+
...INSTANCE_WRITE_TOOL_NAMES,
|
|
16459
16829
|
"inspector_list_logs",
|
|
16460
16830
|
"inspector_search_logs",
|
|
16461
16831
|
"inspector_get_log",
|
|
@@ -16503,6 +16873,9 @@ const TOOL_NAMES = [
|
|
|
16503
16873
|
"inspector_get_project_context"
|
|
16504
16874
|
];
|
|
16505
16875
|
const RESOURCE_NAMES = [
|
|
16876
|
+
"inspector_instances",
|
|
16877
|
+
"inspector_instance",
|
|
16878
|
+
"inspector_instance_connection",
|
|
16506
16879
|
"inspector_mcp_health",
|
|
16507
16880
|
"inspector_mcp_capabilities",
|
|
16508
16881
|
"inspector_mcp_config",
|
|
@@ -16533,7 +16906,7 @@ const PROMPT_NAMES = [
|
|
|
16533
16906
|
"inspector_generate_group_report",
|
|
16534
16907
|
"inspector_extract_repro_steps"
|
|
16535
16908
|
];
|
|
16536
|
-
const Route$
|
|
16909
|
+
const Route$J = createFileRoute("/api/mcp")({
|
|
16537
16910
|
server: {
|
|
16538
16911
|
handlers: {
|
|
16539
16912
|
// The SDK may issue either POST, GET, or DELETE. TanStack Start only
|
|
@@ -16646,15 +17019,15 @@ function parseOptionalPositiveInt(value) {
|
|
|
16646
17019
|
if (!Number.isInteger(parsed) || parsed < 1) return { ok: false };
|
|
16647
17020
|
return { ok: true, value: parsed };
|
|
16648
17021
|
}
|
|
16649
|
-
const Route$
|
|
17022
|
+
const Route$I = createFileRoute("/api/logs")({
|
|
16650
17023
|
server: {
|
|
16651
17024
|
handlers: {
|
|
16652
17025
|
GET: async ({ request }) => {
|
|
16653
|
-
const
|
|
16654
|
-
if (
|
|
17026
|
+
const url2 = new URL(request.url);
|
|
17027
|
+
if (url2.searchParams.get("stats") === "1") {
|
|
16655
17028
|
return Response.json(getLogStorageStats());
|
|
16656
17029
|
}
|
|
16657
|
-
const replayOf =
|
|
17030
|
+
const replayOf = url2.searchParams.get("replayOf");
|
|
16658
17031
|
if (replayOf !== null) {
|
|
16659
17032
|
const id = Number(replayOf);
|
|
16660
17033
|
if (!Number.isInteger(id) || id <= 0) {
|
|
@@ -16663,15 +17036,15 @@ const Route$C = createFileRoute("/api/logs")({
|
|
|
16663
17036
|
const logs = getReplayLogsForSource(id);
|
|
16664
17037
|
return Response.json({ logs, total: logs.length, offset: 0, limit: logs.length });
|
|
16665
17038
|
}
|
|
16666
|
-
const sessionId =
|
|
16667
|
-
const model =
|
|
16668
|
-
const clientPidResult = parseOptionalPositiveInt(
|
|
17039
|
+
const sessionId = url2.searchParams.get("sessionId") ?? void 0;
|
|
17040
|
+
const model = url2.searchParams.get("model") ?? void 0;
|
|
17041
|
+
const clientPidResult = parseOptionalPositiveInt(url2.searchParams.get("clientPid"));
|
|
16669
17042
|
if (!clientPidResult.ok) {
|
|
16670
17043
|
return Response.json({ error: "Invalid clientPid" }, { status: 400 });
|
|
16671
17044
|
}
|
|
16672
17045
|
const clientPid = clientPidResult.value ?? void 0;
|
|
16673
|
-
const query =
|
|
16674
|
-
const offset = parseNonNegativeInt(
|
|
17046
|
+
const query = url2.searchParams.get("q") ?? url2.searchParams.get("search");
|
|
17047
|
+
const offset = parseNonNegativeInt(url2.searchParams.get("offset"), 0);
|
|
16675
17048
|
if (query !== null) {
|
|
16676
17049
|
if (query.length > MAX_SEARCH_QUERY_CHARS) {
|
|
16677
17050
|
return resourceLimitResponse({
|
|
@@ -16684,7 +17057,7 @@ const Route$C = createFileRoute("/api/logs")({
|
|
|
16684
17057
|
});
|
|
16685
17058
|
}
|
|
16686
17059
|
const limitResult2 = parseBoundedIntegerParameter({
|
|
16687
|
-
value:
|
|
17060
|
+
value: url2.searchParams.get("limit"),
|
|
16688
17061
|
parameter: "limit",
|
|
16689
17062
|
fallback: LOG_SEARCH_DEFAULT_LIMIT,
|
|
16690
17063
|
minimum: 1,
|
|
@@ -16693,7 +17066,7 @@ const Route$C = createFileRoute("/api/logs")({
|
|
|
16693
17066
|
});
|
|
16694
17067
|
if (!limitResult2.ok) return limitResult2.response;
|
|
16695
17068
|
const scanLimitResult = parseBoundedIntegerParameter({
|
|
16696
|
-
value:
|
|
17069
|
+
value: url2.searchParams.get("scanLimit"),
|
|
16697
17070
|
parameter: "scanLimit",
|
|
16698
17071
|
fallback: LOG_SEARCH_DEFAULT_SCAN_LIMIT,
|
|
16699
17072
|
minimum: 1,
|
|
@@ -16714,7 +17087,7 @@ const Route$C = createFileRoute("/api/logs")({
|
|
|
16714
17087
|
);
|
|
16715
17088
|
}
|
|
16716
17089
|
const limitResult = parseBoundedIntegerParameter({
|
|
16717
|
-
value:
|
|
17090
|
+
value: url2.searchParams.get("limit"),
|
|
16718
17091
|
parameter: "limit",
|
|
16719
17092
|
fallback: 50,
|
|
16720
17093
|
minimum: 1,
|
|
@@ -16723,10 +17096,10 @@ const Route$C = createFileRoute("/api/logs")({
|
|
|
16723
17096
|
});
|
|
16724
17097
|
if (!limitResult.ok) return limitResult.response;
|
|
16725
17098
|
const limit = limitResult.value;
|
|
16726
|
-
const includeBodies =
|
|
16727
|
-
if (
|
|
16728
|
-
const beforeLogId = parseOptionalPositiveInt(
|
|
16729
|
-
const afterLogId = parseOptionalPositiveInt(
|
|
17099
|
+
const includeBodies = url2.searchParams.get("compact") !== "1";
|
|
17100
|
+
if (url2.searchParams.get("cursor") === "1" || url2.searchParams.get("cursor") === "true") {
|
|
17101
|
+
const beforeLogId = parseOptionalPositiveInt(url2.searchParams.get("beforeLogId"));
|
|
17102
|
+
const afterLogId = parseOptionalPositiveInt(url2.searchParams.get("afterLogId"));
|
|
16730
17103
|
if (!beforeLogId.ok || !afterLogId.ok) {
|
|
16731
17104
|
return Response.json({ error: "Invalid cursor log ID" }, { status: 400 });
|
|
16732
17105
|
}
|
|
@@ -16736,7 +17109,7 @@ const Route$C = createFileRoute("/api/logs")({
|
|
|
16736
17109
|
{ status: 400 }
|
|
16737
17110
|
);
|
|
16738
17111
|
}
|
|
16739
|
-
const anchor =
|
|
17112
|
+
const anchor = url2.searchParams.get("anchor") === "oldest" ? "oldest" : "newest";
|
|
16740
17113
|
return Response.json(
|
|
16741
17114
|
await listLogsCursorPage({
|
|
16742
17115
|
sessionId,
|
|
@@ -16803,94 +17176,1549 @@ const Route$C = createFileRoute("/api/logs")({
|
|
|
16803
17176
|
}
|
|
16804
17177
|
}
|
|
16805
17178
|
});
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
|
|
16822
|
-
await dependencies.drainTraffic();
|
|
16823
|
-
if (forced) return { forced: true, reason: "deadline" };
|
|
16824
|
-
await dependencies.drainFinalizers();
|
|
16825
|
-
if (forced) return { forced: true, reason: "deadline" };
|
|
16826
|
-
await dependencies.flushPersistence();
|
|
16827
|
-
if (forced) return { forced: true, reason: "deadline" };
|
|
16828
|
-
await dependencies.closeResources();
|
|
16829
|
-
return { forced: false, reason: "completed" };
|
|
16830
|
-
} catch {
|
|
16831
|
-
forceCloseOnce();
|
|
16832
|
-
return { forced: true, reason: "error" };
|
|
16833
|
-
}
|
|
16834
|
-
}
|
|
16835
|
-
const timedOut = new Promise((resolve2) => {
|
|
16836
|
-
deadline = setTimeout(
|
|
16837
|
-
() => {
|
|
16838
|
-
forced = true;
|
|
16839
|
-
forceCloseOnce();
|
|
16840
|
-
resolve2({ forced: true, reason: "deadline" });
|
|
16841
|
-
},
|
|
16842
|
-
Math.max(1, deadlineMs)
|
|
16843
|
-
);
|
|
16844
|
-
});
|
|
16845
|
-
shutdownPromise = Promise.race([gracefulShutdown(), timedOut]).finally(() => {
|
|
16846
|
-
if (deadline !== null) clearTimeout(deadline);
|
|
16847
|
-
});
|
|
16848
|
-
return await shutdownPromise;
|
|
16849
|
-
}
|
|
16850
|
-
return { shutdown };
|
|
17179
|
+
const MANAGED_INSTANCE_CONTROL_PATH = "/.agent-inspector/instance";
|
|
17180
|
+
const MANAGED_INSTANCE_SHUTDOWN_PATH = `${MANAGED_INSTANCE_CONTROL_PATH}/shutdown`;
|
|
17181
|
+
const MANAGED_INSTANCE_TOKEN_HEADER = "x-agent-inspector-instance-token";
|
|
17182
|
+
const MANAGED_INSTANCE_SHUTDOWN_MESSAGE_TYPE = "agent-inspector:managed-shutdown";
|
|
17183
|
+
const AGENT_INSPECTOR_CLI_ENTRY_ENV = "AGENT_INSPECTOR_CLI_ENTRY";
|
|
17184
|
+
const MANAGED_INSTANCE_ENV = {
|
|
17185
|
+
name: "AGENT_INSPECTOR_INSTANCE_NAME",
|
|
17186
|
+
instanceId: "AGENT_INSPECTOR_INSTANCE_ID",
|
|
17187
|
+
launchId: "AGENT_INSPECTOR_INSTANCE_LAUNCH_ID",
|
|
17188
|
+
controlToken: "AGENT_INSPECTOR_INSTANCE_CONTROL_TOKEN",
|
|
17189
|
+
startedAt: "AGENT_INSPECTOR_INSTANCE_STARTED_AT",
|
|
17190
|
+
upstreamPort: "AGENT_INSPECTOR_UPSTREAM_PORT"
|
|
17191
|
+
};
|
|
17192
|
+
function nonEmpty(value) {
|
|
17193
|
+
const trimmed = value?.trim();
|
|
17194
|
+
return trimmed === void 0 || trimmed.length === 0 ? null : trimmed;
|
|
16851
17195
|
}
|
|
16852
|
-
function
|
|
16853
|
-
|
|
16854
|
-
|
|
16855
|
-
|
|
16856
|
-
|
|
16857
|
-
|
|
17196
|
+
function isRecord$1(value) {
|
|
17197
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17198
|
+
}
|
|
17199
|
+
function readManagedInstanceLaunch(env = process.env) {
|
|
17200
|
+
const values = [
|
|
17201
|
+
env[MANAGED_INSTANCE_ENV.name],
|
|
17202
|
+
env[MANAGED_INSTANCE_ENV.instanceId],
|
|
17203
|
+
env[MANAGED_INSTANCE_ENV.launchId],
|
|
17204
|
+
env[MANAGED_INSTANCE_ENV.controlToken],
|
|
17205
|
+
env[MANAGED_INSTANCE_ENV.startedAt],
|
|
17206
|
+
env[MANAGED_INSTANCE_ENV.upstreamPort]
|
|
17207
|
+
];
|
|
17208
|
+
if (values.every((value) => value === void 0 || value.trim().length === 0)) {
|
|
17209
|
+
return { kind: "unmanaged" };
|
|
17210
|
+
}
|
|
17211
|
+
const name2 = nonEmpty(env[MANAGED_INSTANCE_ENV.name]);
|
|
17212
|
+
const instanceId = nonEmpty(env[MANAGED_INSTANCE_ENV.instanceId]);
|
|
17213
|
+
const launchId = nonEmpty(env[MANAGED_INSTANCE_ENV.launchId]);
|
|
17214
|
+
const controlToken = nonEmpty(env[MANAGED_INSTANCE_ENV.controlToken]);
|
|
17215
|
+
const startedAt = nonEmpty(env[MANAGED_INSTANCE_ENV.startedAt]);
|
|
17216
|
+
const upstreamPort = Number(env[MANAGED_INSTANCE_ENV.upstreamPort]);
|
|
17217
|
+
if (name2 === null || instanceId === null || launchId === null || controlToken === null || controlToken.length < 32 || startedAt === null || !Number.isFinite(Date.parse(startedAt)) || !Number.isInteger(upstreamPort) || upstreamPort < 1 || upstreamPort > 65535) {
|
|
17218
|
+
return {
|
|
17219
|
+
kind: "invalid",
|
|
17220
|
+
message: "Managed instance launch environment is incomplete or invalid."
|
|
17221
|
+
};
|
|
17222
|
+
}
|
|
17223
|
+
return {
|
|
17224
|
+
kind: "managed",
|
|
17225
|
+
launch: { name: name2, instanceId, launchId, controlToken, startedAt, upstreamPort }
|
|
16858
17226
|
};
|
|
16859
17227
|
}
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
if (raw === void 0 || raw === "") return DEFAULT_SHUTDOWN_DEADLINE_MS;
|
|
16864
|
-
const parsed = Number(raw);
|
|
16865
|
-
return Number.isSafeInteger(parsed) && parsed > 0 && parsed <= 3e5 ? parsed : DEFAULT_SHUTDOWN_DEADLINE_MS;
|
|
17228
|
+
function parseManagedInstanceShutdownMessage(value) {
|
|
17229
|
+
if (!isRecord$1(value)) return null;
|
|
17230
|
+
return value["type"] === MANAGED_INSTANCE_SHUTDOWN_MESSAGE_TYPE && typeof value["controlToken"] === "string" ? { type: MANAGED_INSTANCE_SHUTDOWN_MESSAGE_TYPE, controlToken: value["controlToken"] } : null;
|
|
16866
17231
|
}
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
16870
|
-
await flushToolSchemaWarningAnalysis();
|
|
17232
|
+
function platformCommand(command, args, platform = process.platform) {
|
|
17233
|
+
if (platform !== "win32") return { command, args: [...args] };
|
|
17234
|
+
return { command: "cmd.exe", args: ["/d", "/s", "/c", command, ...args] };
|
|
16871
17235
|
}
|
|
16872
|
-
|
|
16873
|
-
|
|
16874
|
-
await flushSessionArchiveWrites();
|
|
16875
|
-
await flushIndex();
|
|
16876
|
-
await flushApplicationLog();
|
|
17236
|
+
function npmCommand(platform = process.platform) {
|
|
17237
|
+
return platform === "win32" ? "npm.cmd" : "npm";
|
|
16877
17238
|
}
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
destroyAllSessionProcesses();
|
|
16881
|
-
closeSqliteLogIndex();
|
|
16882
|
-
await flushApplicationLog();
|
|
17239
|
+
function npmSpawnCommand(args, platform = process.platform) {
|
|
17240
|
+
return platformCommand(npmCommand(platform), args, platform);
|
|
16883
17241
|
}
|
|
16884
|
-
function
|
|
16885
|
-
|
|
16886
|
-
|
|
16887
|
-
|
|
17242
|
+
function openUrlCommand(targetUrl, platform = process.platform) {
|
|
17243
|
+
switch (platform) {
|
|
17244
|
+
case "darwin":
|
|
17245
|
+
return { command: "open", args: [targetUrl] };
|
|
17246
|
+
case "linux":
|
|
17247
|
+
case "freebsd":
|
|
17248
|
+
case "openbsd":
|
|
17249
|
+
case "netbsd":
|
|
17250
|
+
return { command: "xdg-open", args: [targetUrl] };
|
|
17251
|
+
case "win32":
|
|
17252
|
+
return { command: "cmd.exe", args: ["/d", "/s", "/c", "start", "", targetUrl] };
|
|
17253
|
+
case "aix":
|
|
17254
|
+
case "android":
|
|
17255
|
+
case "cygwin":
|
|
17256
|
+
case "haiku":
|
|
17257
|
+
case "sunos":
|
|
17258
|
+
return null;
|
|
17259
|
+
}
|
|
17260
|
+
return null;
|
|
16888
17261
|
}
|
|
16889
|
-
|
|
16890
|
-
{
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
17262
|
+
function localUrlForPort(port) {
|
|
17263
|
+
return `http://localhost:${String(port)}`;
|
|
17264
|
+
}
|
|
17265
|
+
function trimHost(host) {
|
|
17266
|
+
const trimmed = host?.trim();
|
|
17267
|
+
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
17268
|
+
}
|
|
17269
|
+
function isWildcardHost(host) {
|
|
17270
|
+
const trimmed = trimHost(host);
|
|
17271
|
+
return trimmed === void 0 || trimmed === "0.0.0.0" || trimmed === "::" || trimmed === "[::]";
|
|
17272
|
+
}
|
|
17273
|
+
function bracketIpv6(host) {
|
|
17274
|
+
if (!host.includes(":")) return host;
|
|
17275
|
+
if (host.startsWith("[") && host.endsWith("]")) return host;
|
|
17276
|
+
return `[${host}]`;
|
|
17277
|
+
}
|
|
17278
|
+
function urlForHost(port, host) {
|
|
17279
|
+
const trimmed = trimHost(host);
|
|
17280
|
+
if (trimmed === void 0 || isWildcardHost(trimmed)) return localUrlForPort(port);
|
|
17281
|
+
return `http://${bracketIpv6(trimmed)}:${String(port)}`;
|
|
17282
|
+
}
|
|
17283
|
+
function probeHostForBindHost(host) {
|
|
17284
|
+
const trimmed = trimHost(host);
|
|
17285
|
+
if (trimmed === void 0 || isWildcardHost(trimmed)) return "127.0.0.1";
|
|
17286
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) return trimmed.slice(1, -1);
|
|
17287
|
+
return trimmed;
|
|
17288
|
+
}
|
|
17289
|
+
const DEFAULT_CONTROL_TIMEOUT_MS = 2e3;
|
|
17290
|
+
function isRecord(value) {
|
|
17291
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17292
|
+
}
|
|
17293
|
+
function parseIdentity(value) {
|
|
17294
|
+
if (!isRecord(value)) return null;
|
|
17295
|
+
const schemaVersion = value["schemaVersion"];
|
|
17296
|
+
const instanceId = value["instanceId"];
|
|
17297
|
+
const launchId = value["launchId"];
|
|
17298
|
+
const supervisorPid = value["supervisorPid"];
|
|
17299
|
+
const startedAt = value["startedAt"];
|
|
17300
|
+
const publicPort = value["publicPort"];
|
|
17301
|
+
const upstreamPort = value["upstreamPort"];
|
|
17302
|
+
if (schemaVersion !== 1 || typeof instanceId !== "string" || instanceId.length === 0 || typeof launchId !== "string" || launchId.length === 0 || typeof supervisorPid !== "number" || !Number.isInteger(supervisorPid) || supervisorPid < 1 || typeof startedAt !== "string" || !Number.isFinite(Date.parse(startedAt)) || typeof publicPort !== "number" || !Number.isInteger(publicPort) || publicPort < 1 || publicPort > 65535 || typeof upstreamPort !== "number" || !Number.isInteger(upstreamPort) || upstreamPort < 1 || upstreamPort > 65535) {
|
|
17303
|
+
return null;
|
|
17304
|
+
}
|
|
17305
|
+
return {
|
|
17306
|
+
schemaVersion: 1,
|
|
17307
|
+
instanceId,
|
|
17308
|
+
launchId,
|
|
17309
|
+
supervisorPid,
|
|
17310
|
+
startedAt,
|
|
17311
|
+
publicPort,
|
|
17312
|
+
upstreamPort
|
|
17313
|
+
};
|
|
17314
|
+
}
|
|
17315
|
+
function hostForUrl(host) {
|
|
17316
|
+
if (host.startsWith("[") && host.endsWith("]")) return host;
|
|
17317
|
+
return host.includes(":") ? `[${host}]` : host;
|
|
17318
|
+
}
|
|
17319
|
+
function endpoint(identity, path2) {
|
|
17320
|
+
return `http://${hostForUrl(identity.host)}:${String(identity.publicPort)}${path2}`;
|
|
17321
|
+
}
|
|
17322
|
+
function requestInit(identity, controller, method) {
|
|
17323
|
+
return {
|
|
17324
|
+
method,
|
|
17325
|
+
headers: {
|
|
17326
|
+
accept: "application/json",
|
|
17327
|
+
[MANAGED_INSTANCE_TOKEN_HEADER]: identity.controlToken
|
|
17328
|
+
},
|
|
17329
|
+
cache: "no-store",
|
|
17330
|
+
signal: controller.signal
|
|
17331
|
+
};
|
|
17332
|
+
}
|
|
17333
|
+
async function responseJson(response) {
|
|
17334
|
+
try {
|
|
17335
|
+
const value = await response.json();
|
|
17336
|
+
return value;
|
|
17337
|
+
} catch {
|
|
17338
|
+
return null;
|
|
17339
|
+
}
|
|
17340
|
+
}
|
|
17341
|
+
async function probeInstanceControl(expected, fetchImpl = fetch, timeoutMs = DEFAULT_CONTROL_TIMEOUT_MS) {
|
|
17342
|
+
const controller = new AbortController();
|
|
17343
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
17344
|
+
try {
|
|
17345
|
+
const response = await fetchImpl(
|
|
17346
|
+
endpoint(expected, MANAGED_INSTANCE_CONTROL_PATH),
|
|
17347
|
+
requestInit(expected, controller, "GET")
|
|
17348
|
+
);
|
|
17349
|
+
if (!response.ok) return { kind: "identity-mismatch" };
|
|
17350
|
+
const identity = parseIdentity(await responseJson(response));
|
|
17351
|
+
if (identity === null) return { kind: "invalid-response" };
|
|
17352
|
+
if (identity.instanceId !== expected.instanceId || identity.launchId !== expected.launchId || identity.publicPort !== expected.publicPort) {
|
|
17353
|
+
return { kind: "identity-mismatch" };
|
|
17354
|
+
}
|
|
17355
|
+
return { kind: "running", identity };
|
|
17356
|
+
} catch (error) {
|
|
17357
|
+
return {
|
|
17358
|
+
kind: "unreachable",
|
|
17359
|
+
timedOut: controller.signal.aborted || error instanceof Error && error.name === "AbortError"
|
|
17360
|
+
};
|
|
17361
|
+
} finally {
|
|
17362
|
+
clearTimeout(timeout);
|
|
17363
|
+
}
|
|
17364
|
+
}
|
|
17365
|
+
async function requestInstanceShutdown(expected, fetchImpl = fetch, timeoutMs = DEFAULT_CONTROL_TIMEOUT_MS) {
|
|
17366
|
+
const controller = new AbortController();
|
|
17367
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
17368
|
+
try {
|
|
17369
|
+
const response = await fetchImpl(
|
|
17370
|
+
endpoint(expected, MANAGED_INSTANCE_SHUTDOWN_PATH),
|
|
17371
|
+
requestInit(expected, controller, "POST")
|
|
17372
|
+
);
|
|
17373
|
+
if (response.status === 202) return { kind: "accepted" };
|
|
17374
|
+
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
|
17375
|
+
return { kind: "identity-mismatch" };
|
|
17376
|
+
}
|
|
17377
|
+
return { kind: "rejected" };
|
|
17378
|
+
} catch (error) {
|
|
17379
|
+
return {
|
|
17380
|
+
kind: "unreachable",
|
|
17381
|
+
timedOut: controller.signal.aborted || error instanceof Error && error.name === "AbortError"
|
|
17382
|
+
};
|
|
17383
|
+
} finally {
|
|
17384
|
+
clearTimeout(timeout);
|
|
17385
|
+
}
|
|
17386
|
+
}
|
|
17387
|
+
const INSTANCE_RECORD_SCHEMA_VERSION = 1;
|
|
17388
|
+
const MAX_INSTANCE_NAME_LENGTH = 64;
|
|
17389
|
+
const WINDOWS_DEVICE_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/u;
|
|
17390
|
+
const INSTANCE_NAME_CHARACTERS = /^[a-z0-9._-]+$/u;
|
|
17391
|
+
function instanceRegistrySuccess(value) {
|
|
17392
|
+
return { kind: "success", value };
|
|
17393
|
+
}
|
|
17394
|
+
function instanceRegistryError(code, message) {
|
|
17395
|
+
return { kind: "error", error: { code, message } };
|
|
17396
|
+
}
|
|
17397
|
+
function validateInstanceName(rawName) {
|
|
17398
|
+
const normalized = rawName.toLowerCase();
|
|
17399
|
+
const invalid = rawName.length === 0 || rawName !== rawName.trim() || normalized.length > MAX_INSTANCE_NAME_LENGTH || normalized === "." || normalized === ".." || normalized === "default" || normalized.endsWith(".") || normalized.endsWith(" ") || !INSTANCE_NAME_CHARACTERS.test(normalized) || WINDOWS_DEVICE_NAME.test(normalized);
|
|
17400
|
+
return invalid ? instanceRegistryError(
|
|
17401
|
+
"name_invalid",
|
|
17402
|
+
`Invalid instance name ${JSON.stringify(rawName)}. Use 1-${String(MAX_INSTANCE_NAME_LENGTH)} letters, digits, dots, hyphens, or underscores; "default" and Windows device names are reserved.`
|
|
17403
|
+
) : instanceRegistrySuccess(normalized);
|
|
17404
|
+
}
|
|
17405
|
+
function normalizeInstanceBasePath(rawBasePath) {
|
|
17406
|
+
const value = rawBasePath ?? "";
|
|
17407
|
+
const trimmed = value.trim();
|
|
17408
|
+
const hasControlCharacter = [...trimmed].some((character) => {
|
|
17409
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
17410
|
+
return codePoint < 32 || codePoint === 127;
|
|
17411
|
+
});
|
|
17412
|
+
if (trimmed.length > 0 && (!trimmed.startsWith("/") || trimmed.includes("?") || trimmed.includes("#") || trimmed.includes("\\") || hasControlCharacter)) {
|
|
17413
|
+
return instanceRegistryError(
|
|
17414
|
+
"record_invalid",
|
|
17415
|
+
"Instance Base Path must be an absolute URL path without a query, hash, backslash, or control character."
|
|
17416
|
+
);
|
|
17417
|
+
}
|
|
17418
|
+
const normalized = normalizeBasePath(trimmed);
|
|
17419
|
+
const reservedControlRoot = MANAGED_INSTANCE_CONTROL_PATH.replace(/\/instance$/u, "");
|
|
17420
|
+
if (normalized === reservedControlRoot || normalized.startsWith(`${reservedControlRoot}/`)) {
|
|
17421
|
+
return instanceRegistryError(
|
|
17422
|
+
"record_invalid",
|
|
17423
|
+
`Instance Base Path ${normalized} collides with the reserved Agent Inspector control namespace.`
|
|
17424
|
+
);
|
|
17425
|
+
}
|
|
17426
|
+
return instanceRegistrySuccess(normalized);
|
|
17427
|
+
}
|
|
17428
|
+
const NormalizedInstanceNameSchema = string().refine((name2) => {
|
|
17429
|
+
const result = validateInstanceName(name2);
|
|
17430
|
+
return result.kind === "success" && result.value === name2;
|
|
17431
|
+
});
|
|
17432
|
+
const NormalizedBasePathSchema = string().refine((basePath) => {
|
|
17433
|
+
const result = normalizeInstanceBasePath(basePath);
|
|
17434
|
+
return result.kind === "success" && result.value === basePath;
|
|
17435
|
+
});
|
|
17436
|
+
const PortSchema = number().int().min(1).max(65535);
|
|
17437
|
+
const TimestampSchema = string().datetime();
|
|
17438
|
+
const IdentifierSchema = string().min(8).max(128);
|
|
17439
|
+
const InstanceLaunchSchema = object({
|
|
17440
|
+
launchId: IdentifierSchema,
|
|
17441
|
+
controlToken: string().min(32).max(256),
|
|
17442
|
+
startedAt: TimestampSchema
|
|
17443
|
+
}).strict();
|
|
17444
|
+
const InstanceRecordSchema = object({
|
|
17445
|
+
schemaVersion: literal(INSTANCE_RECORD_SCHEMA_VERSION),
|
|
17446
|
+
name: NormalizedInstanceNameSchema,
|
|
17447
|
+
instanceId: IdentifierSchema,
|
|
17448
|
+
createdAt: TimestampSchema,
|
|
17449
|
+
updatedAt: TimestampSchema,
|
|
17450
|
+
host: string().trim().min(1).max(255),
|
|
17451
|
+
publicPort: PortSchema,
|
|
17452
|
+
upstreamPort: PortSchema,
|
|
17453
|
+
basePath: NormalizedBasePathSchema,
|
|
17454
|
+
captureMode: CaptureModeSchema,
|
|
17455
|
+
dataDir: string().min(1).refine((path2) => isAbsolute(path2)),
|
|
17456
|
+
launch: InstanceLaunchSchema
|
|
17457
|
+
}).strict().superRefine((record2, context) => {
|
|
17458
|
+
if (record2.publicPort === record2.upstreamPort) {
|
|
17459
|
+
context.addIssue({
|
|
17460
|
+
code: "custom",
|
|
17461
|
+
message: "Public and upstream ports must be different.",
|
|
17462
|
+
path: ["upstreamPort"]
|
|
17463
|
+
});
|
|
17464
|
+
}
|
|
17465
|
+
});
|
|
17466
|
+
function createLaunch(factory, now) {
|
|
17467
|
+
return {
|
|
17468
|
+
launchId: factory.createLaunchId?.() ?? randomUUID(),
|
|
17469
|
+
controlToken: factory.createControlToken?.() ?? randomBytes(32).toString("base64url"),
|
|
17470
|
+
startedAt: now.toISOString()
|
|
17471
|
+
};
|
|
17472
|
+
}
|
|
17473
|
+
function createInstanceRecord(input, factory = {}) {
|
|
17474
|
+
const name2 = validateInstanceName(input.name);
|
|
17475
|
+
if (name2.kind === "error") return name2;
|
|
17476
|
+
const basePath = normalizeInstanceBasePath(input.basePath);
|
|
17477
|
+
if (basePath.kind === "error") return basePath;
|
|
17478
|
+
const now = factory.now?.() ?? /* @__PURE__ */ new Date();
|
|
17479
|
+
const timestamp = now.toISOString();
|
|
17480
|
+
const candidate = {
|
|
17481
|
+
schemaVersion: INSTANCE_RECORD_SCHEMA_VERSION,
|
|
17482
|
+
name: name2.value,
|
|
17483
|
+
instanceId: factory.createInstanceId?.() ?? randomUUID(),
|
|
17484
|
+
createdAt: timestamp,
|
|
17485
|
+
updatedAt: timestamp,
|
|
17486
|
+
host: input.host.trim(),
|
|
17487
|
+
publicPort: input.publicPort,
|
|
17488
|
+
upstreamPort: input.upstreamPort,
|
|
17489
|
+
basePath: basePath.value,
|
|
17490
|
+
captureMode: input.captureMode,
|
|
17491
|
+
dataDir: input.dataDir,
|
|
17492
|
+
launch: createLaunch(factory, now)
|
|
17493
|
+
};
|
|
17494
|
+
const parsed = InstanceRecordSchema.safeParse(candidate);
|
|
17495
|
+
return parsed.success ? instanceRegistrySuccess(parsed.data) : instanceRegistryError(
|
|
17496
|
+
"record_invalid",
|
|
17497
|
+
`Cannot create instance record: ${parsed.error.issues[0]?.message ?? "invalid saved configuration"}`
|
|
17498
|
+
);
|
|
17499
|
+
}
|
|
17500
|
+
function rotateInstanceLaunch(record2, factory = {}) {
|
|
17501
|
+
const now = factory.now?.() ?? /* @__PURE__ */ new Date();
|
|
17502
|
+
const candidate = {
|
|
17503
|
+
...record2,
|
|
17504
|
+
updatedAt: now.toISOString(),
|
|
17505
|
+
launch: createLaunch(factory, now)
|
|
17506
|
+
};
|
|
17507
|
+
const parsed = InstanceRecordSchema.safeParse(candidate);
|
|
17508
|
+
return parsed.success ? instanceRegistrySuccess(parsed.data) : instanceRegistryError(
|
|
17509
|
+
"record_invalid",
|
|
17510
|
+
`Cannot rotate instance launch: ${parsed.error.issues[0]?.message ?? "invalid saved configuration"}`
|
|
17511
|
+
);
|
|
17512
|
+
}
|
|
17513
|
+
const AGENT_INSPECTOR_INSTANCES_DIR_ENV = "AGENT_INSPECTOR_INSTANCES_DIR";
|
|
17514
|
+
const DEFAULT_NAMED_INSTANCE_PUBLIC_PORT = 9530;
|
|
17515
|
+
const NAMED_INSTANCE_PORT_STEP = 3;
|
|
17516
|
+
const NAMED_INSTANCE_UPSTREAM_OFFSET = 2;
|
|
17517
|
+
const DEFAULT_INSTANCE_LOCK_TIMEOUT_MS = 1e4;
|
|
17518
|
+
const DEFAULT_INSTANCE_LOCK_STALE_MS = 3e4;
|
|
17519
|
+
const INSTANCE_RECORD_FILE_NAME = "instance.json";
|
|
17520
|
+
const ROOT_LOCK_DIRECTORY_NAME = ".allocation.lock";
|
|
17521
|
+
const INSTANCE_LOCK_DIRECTORY_NAME = ".mutation.lock";
|
|
17522
|
+
const LOCK_OWNER_FILE_NAME = "owner.json";
|
|
17523
|
+
const DEFAULT_LOCK_RETRY_MS = 50;
|
|
17524
|
+
function getInstancesRoot(env = process.env, platform = process.platform) {
|
|
17525
|
+
const pathApi = platform === "win32" ? win32 : posix;
|
|
17526
|
+
const explicitRoot = env[AGENT_INSPECTOR_INSTANCES_DIR_ENV];
|
|
17527
|
+
if (explicitRoot !== void 0 && explicitRoot.trim().length > 0) {
|
|
17528
|
+
return pathApi.isAbsolute(explicitRoot) ? explicitRoot : pathApi.resolve(explicitRoot);
|
|
17529
|
+
}
|
|
17530
|
+
if (platform === "win32") {
|
|
17531
|
+
const localAppData = env["LOCALAPPDATA"];
|
|
17532
|
+
if (localAppData !== void 0 && localAppData.trim().length > 0) {
|
|
17533
|
+
return win32.join(localAppData, "agent-inspector", "instances");
|
|
17534
|
+
}
|
|
17535
|
+
const userProfile = env["USERPROFILE"];
|
|
17536
|
+
const fallback = userProfile !== void 0 && userProfile.trim().length > 0 ? userProfile : homedir();
|
|
17537
|
+
return win32.join(fallback, ".agent-inspector", "instances");
|
|
17538
|
+
}
|
|
17539
|
+
const home = env["HOME"];
|
|
17540
|
+
const base = home !== void 0 && home.trim().length > 0 ? home : homedir();
|
|
17541
|
+
return posix.join(base, ".agent-inspector", "instances");
|
|
17542
|
+
}
|
|
17543
|
+
function getInstancePaths(root, rawName) {
|
|
17544
|
+
const name2 = validateInstanceName(rawName);
|
|
17545
|
+
if (name2.kind === "error") return name2;
|
|
17546
|
+
const instanceDir = join(root, name2.value);
|
|
17547
|
+
return instanceRegistrySuccess({
|
|
17548
|
+
root,
|
|
17549
|
+
instanceDir,
|
|
17550
|
+
dataDir: join(instanceDir, "data"),
|
|
17551
|
+
recordPath: join(instanceDir, INSTANCE_RECORD_FILE_NAME),
|
|
17552
|
+
lockPath: join(instanceDir, INSTANCE_LOCK_DIRECTORY_NAME)
|
|
17553
|
+
});
|
|
17554
|
+
}
|
|
17555
|
+
function ensureInstancesRoot(root = getInstancesRoot()) {
|
|
17556
|
+
return ensurePrivateDirectorySync(root) ? instanceRegistrySuccess(root) : instanceRegistryError(
|
|
17557
|
+
"registry_io",
|
|
17558
|
+
`Cannot create or secure the private instance registry at ${root}.`
|
|
17559
|
+
);
|
|
17560
|
+
}
|
|
17561
|
+
function nodeErrorCode(error) {
|
|
17562
|
+
if (typeof error !== "object" || error === null) return null;
|
|
17563
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, "code");
|
|
17564
|
+
return typeof descriptor?.value === "string" ? descriptor.value : null;
|
|
17565
|
+
}
|
|
17566
|
+
function readRecordFile(recordPath) {
|
|
17567
|
+
let parsedJson;
|
|
17568
|
+
try {
|
|
17569
|
+
parsedJson = JSON.parse(readFileSync(recordPath, "utf8"));
|
|
17570
|
+
} catch {
|
|
17571
|
+
return instanceRegistryError(
|
|
17572
|
+
"record_invalid",
|
|
17573
|
+
`Instance record ${recordPath} is unreadable or is not valid JSON.`
|
|
17574
|
+
);
|
|
17575
|
+
}
|
|
17576
|
+
const parsed = InstanceRecordSchema.safeParse(parsedJson);
|
|
17577
|
+
return parsed.success ? instanceRegistrySuccess(parsed.data) : instanceRegistryError(
|
|
17578
|
+
"record_invalid",
|
|
17579
|
+
`Instance record ${recordPath} does not match schema version 1: ${parsed.error.issues[0]?.message ?? "invalid record"}.`
|
|
17580
|
+
);
|
|
17581
|
+
}
|
|
17582
|
+
function readInstanceRecord(root, rawName) {
|
|
17583
|
+
const paths = getInstancePaths(root, rawName);
|
|
17584
|
+
if (paths.kind === "error") return paths;
|
|
17585
|
+
if (!existsSync(paths.value.recordPath)) return instanceRegistrySuccess(null);
|
|
17586
|
+
return readRecordFile(paths.value.recordPath);
|
|
17587
|
+
}
|
|
17588
|
+
function listInstanceRecords(root) {
|
|
17589
|
+
if (!existsSync(root)) return instanceRegistrySuccess([]);
|
|
17590
|
+
let entries;
|
|
17591
|
+
try {
|
|
17592
|
+
entries = readdirSync(root, { withFileTypes: true });
|
|
17593
|
+
} catch {
|
|
17594
|
+
return instanceRegistryError("registry_io", `Cannot read the instance registry at ${root}.`);
|
|
17595
|
+
}
|
|
17596
|
+
const records = [];
|
|
17597
|
+
const names = /* @__PURE__ */ new Set();
|
|
17598
|
+
for (const entry of entries) {
|
|
17599
|
+
if (!entry.isDirectory()) continue;
|
|
17600
|
+
const recordPath = join(root, entry.name, INSTANCE_RECORD_FILE_NAME);
|
|
17601
|
+
if (!existsSync(recordPath)) continue;
|
|
17602
|
+
const record2 = readRecordFile(recordPath);
|
|
17603
|
+
if (record2.kind === "error") return record2;
|
|
17604
|
+
const expectedName = validateInstanceName(entry.name);
|
|
17605
|
+
if (expectedName.kind === "error" || record2.value.name !== expectedName.value || names.has(record2.value.name)) {
|
|
17606
|
+
return instanceRegistryError(
|
|
17607
|
+
"record_invalid",
|
|
17608
|
+
`Instance directory ${entry.name} does not uniquely match its saved normalized name.`
|
|
17609
|
+
);
|
|
17610
|
+
}
|
|
17611
|
+
names.add(record2.value.name);
|
|
17612
|
+
records.push(record2.value);
|
|
17613
|
+
}
|
|
17614
|
+
records.sort((left, right) => left.name.localeCompare(right.name));
|
|
17615
|
+
return instanceRegistrySuccess(records);
|
|
17616
|
+
}
|
|
17617
|
+
function cleanupFile(path2) {
|
|
17618
|
+
try {
|
|
17619
|
+
rmSync(path2, { force: true });
|
|
17620
|
+
} catch {
|
|
17621
|
+
}
|
|
17622
|
+
}
|
|
17623
|
+
function writeInstanceRecord(root, record2) {
|
|
17624
|
+
const validated = InstanceRecordSchema.safeParse(record2);
|
|
17625
|
+
if (!validated.success) {
|
|
17626
|
+
return instanceRegistryError(
|
|
17627
|
+
"record_invalid",
|
|
17628
|
+
`Cannot save instance record: ${validated.error.issues[0]?.message ?? "invalid record"}.`
|
|
17629
|
+
);
|
|
17630
|
+
}
|
|
17631
|
+
const paths = getInstancePaths(root, validated.data.name);
|
|
17632
|
+
if (paths.kind === "error") return paths;
|
|
17633
|
+
const rootResult = ensureInstancesRoot(root);
|
|
17634
|
+
if (rootResult.kind === "error") return rootResult;
|
|
17635
|
+
if (!ensurePrivateDirectorySync(paths.value.instanceDir)) {
|
|
17636
|
+
return instanceRegistryError(
|
|
17637
|
+
"registry_io",
|
|
17638
|
+
`Cannot create or secure the private directory for instance ${validated.data.name}.`
|
|
17639
|
+
);
|
|
17640
|
+
}
|
|
17641
|
+
if (!ensurePrivateDirectorySync(validated.data.dataDir)) {
|
|
17642
|
+
return instanceRegistryError(
|
|
17643
|
+
"registry_io",
|
|
17644
|
+
`Cannot create or secure the data directory for instance ${validated.data.name}.`
|
|
17645
|
+
);
|
|
17646
|
+
}
|
|
17647
|
+
const temporaryPath = join(
|
|
17648
|
+
paths.value.instanceDir,
|
|
17649
|
+
`.${INSTANCE_RECORD_FILE_NAME}.${String(process.pid)}.${randomUUID()}.tmp`
|
|
17650
|
+
);
|
|
17651
|
+
let descriptor = null;
|
|
17652
|
+
try {
|
|
17653
|
+
descriptor = openSync(temporaryPath, "wx", PRIVATE_FILE_MODE);
|
|
17654
|
+
writeFileSync(descriptor, `${JSON.stringify(validated.data, null, 2)}
|
|
17655
|
+
`, "utf8");
|
|
17656
|
+
closeSync(descriptor);
|
|
17657
|
+
descriptor = null;
|
|
17658
|
+
if (!securePrivateFileSync(temporaryPath)) {
|
|
17659
|
+
cleanupFile(temporaryPath);
|
|
17660
|
+
return instanceRegistryError(
|
|
17661
|
+
"registry_io",
|
|
17662
|
+
`Cannot secure the saved record for instance ${validated.data.name}.`
|
|
17663
|
+
);
|
|
17664
|
+
}
|
|
17665
|
+
renameSync(temporaryPath, paths.value.recordPath);
|
|
17666
|
+
return instanceRegistrySuccess(validated.data);
|
|
17667
|
+
} catch {
|
|
17668
|
+
if (descriptor !== null) {
|
|
17669
|
+
try {
|
|
17670
|
+
closeSync(descriptor);
|
|
17671
|
+
} catch {
|
|
17672
|
+
}
|
|
17673
|
+
}
|
|
17674
|
+
cleanupFile(temporaryPath);
|
|
17675
|
+
return instanceRegistryError(
|
|
17676
|
+
"registry_io",
|
|
17677
|
+
`Cannot atomically save the record for instance ${validated.data.name}.`
|
|
17678
|
+
);
|
|
17679
|
+
}
|
|
17680
|
+
}
|
|
17681
|
+
const InstanceLockOwnerSchema = object({
|
|
17682
|
+
schemaVersion: literal(1),
|
|
17683
|
+
pid: number().int().positive(),
|
|
17684
|
+
token: string().min(8),
|
|
17685
|
+
createdAt: string().datetime()
|
|
17686
|
+
}).strict();
|
|
17687
|
+
function defaultSleep(milliseconds) {
|
|
17688
|
+
return new Promise((resolveSleep) => {
|
|
17689
|
+
setTimeout(resolveSleep, milliseconds);
|
|
17690
|
+
});
|
|
17691
|
+
}
|
|
17692
|
+
function defaultIsProcessAlive(pid) {
|
|
17693
|
+
if (pid === process.pid) return true;
|
|
17694
|
+
try {
|
|
17695
|
+
process.kill(pid, 0);
|
|
17696
|
+
return true;
|
|
17697
|
+
} catch (error) {
|
|
17698
|
+
return nodeErrorCode(error) !== "ESRCH";
|
|
17699
|
+
}
|
|
17700
|
+
}
|
|
17701
|
+
function readLockOwner(lockPath) {
|
|
17702
|
+
try {
|
|
17703
|
+
const raw = JSON.parse(readFileSync(join(lockPath, LOCK_OWNER_FILE_NAME), "utf8"));
|
|
17704
|
+
const parsed = InstanceLockOwnerSchema.safeParse(raw);
|
|
17705
|
+
return parsed.success ? parsed.data : null;
|
|
17706
|
+
} catch {
|
|
17707
|
+
return null;
|
|
17708
|
+
}
|
|
17709
|
+
}
|
|
17710
|
+
function lockCreatedAt(lockPath, owner) {
|
|
17711
|
+
if (owner !== null) return Date.parse(owner.createdAt);
|
|
17712
|
+
try {
|
|
17713
|
+
return statSync(lockPath).mtimeMs;
|
|
17714
|
+
} catch {
|
|
17715
|
+
return null;
|
|
17716
|
+
}
|
|
17717
|
+
}
|
|
17718
|
+
function recoverStaleLock(lockPath, staleAfterMs, now, isProcessAlive) {
|
|
17719
|
+
const owner = readLockOwner(lockPath);
|
|
17720
|
+
const createdAt = lockCreatedAt(lockPath, owner);
|
|
17721
|
+
if (createdAt === null || now - createdAt < staleAfterMs) return false;
|
|
17722
|
+
if (owner !== null && isProcessAlive(owner.pid)) return false;
|
|
17723
|
+
const recoveredPath = `${lockPath}.stale-${randomUUID()}`;
|
|
17724
|
+
try {
|
|
17725
|
+
renameSync(lockPath, recoveredPath);
|
|
17726
|
+
rmSync(recoveredPath, { recursive: true, force: true });
|
|
17727
|
+
return true;
|
|
17728
|
+
} catch {
|
|
17729
|
+
return false;
|
|
17730
|
+
}
|
|
17731
|
+
}
|
|
17732
|
+
function releaseDirectoryLock(lockPath, token) {
|
|
17733
|
+
if (!existsSync(lockPath)) return instanceRegistrySuccess(void 0);
|
|
17734
|
+
const owner = readLockOwner(lockPath);
|
|
17735
|
+
if (owner === null || owner.token !== token) {
|
|
17736
|
+
return instanceRegistryError(
|
|
17737
|
+
"lock_not_owned",
|
|
17738
|
+
`Lifecycle lock ${lockPath} is no longer owned by this command.`
|
|
17739
|
+
);
|
|
17740
|
+
}
|
|
17741
|
+
try {
|
|
17742
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
17743
|
+
return instanceRegistrySuccess(void 0);
|
|
17744
|
+
} catch {
|
|
17745
|
+
return instanceRegistryError("registry_io", `Cannot release lifecycle lock ${lockPath}.`);
|
|
17746
|
+
}
|
|
17747
|
+
}
|
|
17748
|
+
async function acquireDirectoryLock(lockPath, options) {
|
|
17749
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_INSTANCE_LOCK_TIMEOUT_MS;
|
|
17750
|
+
const retryDelayMs = options.retryDelayMs ?? DEFAULT_LOCK_RETRY_MS;
|
|
17751
|
+
const staleAfterMs = options.staleAfterMs ?? DEFAULT_INSTANCE_LOCK_STALE_MS;
|
|
17752
|
+
const pid = options.pid ?? process.pid;
|
|
17753
|
+
const now = options.now ?? Date.now;
|
|
17754
|
+
const sleep2 = options.sleep ?? defaultSleep;
|
|
17755
|
+
const isProcessAlive = options.isProcessAlive ?? defaultIsProcessAlive;
|
|
17756
|
+
const createToken = options.createToken ?? randomUUID;
|
|
17757
|
+
const startedAt = now();
|
|
17758
|
+
while (now() - startedAt <= timeoutMs) {
|
|
17759
|
+
const token = createToken();
|
|
17760
|
+
try {
|
|
17761
|
+
mkdirSync(lockPath, { mode: 448 });
|
|
17762
|
+
} catch (error) {
|
|
17763
|
+
if (nodeErrorCode(error) !== "EEXIST") {
|
|
17764
|
+
return instanceRegistryError("registry_io", `Cannot acquire lifecycle lock ${lockPath}.`);
|
|
17765
|
+
}
|
|
17766
|
+
if (recoverStaleLock(lockPath, staleAfterMs, now(), isProcessAlive)) continue;
|
|
17767
|
+
if (now() - startedAt >= timeoutMs) break;
|
|
17768
|
+
await sleep2(retryDelayMs);
|
|
17769
|
+
continue;
|
|
17770
|
+
}
|
|
17771
|
+
try {
|
|
17772
|
+
if (!ensurePrivateDirectorySync(lockPath)) {
|
|
17773
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
17774
|
+
return instanceRegistryError("registry_io", `Cannot secure lifecycle lock ${lockPath}.`);
|
|
17775
|
+
}
|
|
17776
|
+
const owner = {
|
|
17777
|
+
schemaVersion: 1,
|
|
17778
|
+
pid,
|
|
17779
|
+
token,
|
|
17780
|
+
createdAt: new Date(now()).toISOString()
|
|
17781
|
+
};
|
|
17782
|
+
const ownerPath = join(lockPath, LOCK_OWNER_FILE_NAME);
|
|
17783
|
+
writeFileSync(ownerPath, `${JSON.stringify(owner)}
|
|
17784
|
+
`, {
|
|
17785
|
+
encoding: "utf8",
|
|
17786
|
+
flag: "wx",
|
|
17787
|
+
mode: PRIVATE_FILE_MODE
|
|
17788
|
+
});
|
|
17789
|
+
if (!securePrivateFileSync(ownerPath)) {
|
|
17790
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
17791
|
+
return instanceRegistryError("registry_io", `Cannot secure lifecycle lock ${lockPath}.`);
|
|
17792
|
+
}
|
|
17793
|
+
return instanceRegistrySuccess({
|
|
17794
|
+
path: lockPath,
|
|
17795
|
+
release: () => releaseDirectoryLock(lockPath, token)
|
|
17796
|
+
});
|
|
17797
|
+
} catch {
|
|
17798
|
+
try {
|
|
17799
|
+
rmSync(lockPath, { recursive: true, force: true });
|
|
17800
|
+
} catch {
|
|
17801
|
+
}
|
|
17802
|
+
return instanceRegistryError("registry_io", `Cannot acquire lifecycle lock ${lockPath}.`);
|
|
17803
|
+
}
|
|
17804
|
+
}
|
|
17805
|
+
return instanceRegistryError(
|
|
17806
|
+
"lock_timeout",
|
|
17807
|
+
`Timed out waiting for lifecycle lock ${lockPath}; retry after the other command finishes.`
|
|
17808
|
+
);
|
|
17809
|
+
}
|
|
17810
|
+
async function acquireRegistryLock(root, options = {}) {
|
|
17811
|
+
const ensured = ensureInstancesRoot(root);
|
|
17812
|
+
return ensured.kind === "error" ? ensured : acquireDirectoryLock(join(root, ROOT_LOCK_DIRECTORY_NAME), options);
|
|
17813
|
+
}
|
|
17814
|
+
async function acquireInstanceLock(root, rawName, options = {}) {
|
|
17815
|
+
const paths = getInstancePaths(root, rawName);
|
|
17816
|
+
if (paths.kind === "error") return paths;
|
|
17817
|
+
const ensured = ensureInstancesRoot(root);
|
|
17818
|
+
if (ensured.kind === "error") return ensured;
|
|
17819
|
+
if (!ensurePrivateDirectorySync(paths.value.instanceDir)) {
|
|
17820
|
+
return instanceRegistryError(
|
|
17821
|
+
"registry_io",
|
|
17822
|
+
`Cannot create or secure the private directory for instance ${rawName}.`
|
|
17823
|
+
);
|
|
17824
|
+
}
|
|
17825
|
+
return acquireDirectoryLock(paths.value.lockPath, options);
|
|
17826
|
+
}
|
|
17827
|
+
function pathApiForPlatform(platform) {
|
|
17828
|
+
return platform === "win32" ? win32 : posix;
|
|
17829
|
+
}
|
|
17830
|
+
function resolveRealPathWithMissingTail(absolutePath, pathApi, realpath) {
|
|
17831
|
+
let current = absolutePath;
|
|
17832
|
+
const missingSegments = [];
|
|
17833
|
+
while (true) {
|
|
17834
|
+
try {
|
|
17835
|
+
return pathApi.join(realpath(current), ...missingSegments.reverse());
|
|
17836
|
+
} catch {
|
|
17837
|
+
const parent = pathApi.dirname(current);
|
|
17838
|
+
if (parent === current) return absolutePath;
|
|
17839
|
+
missingSegments.push(pathApi.basename(current));
|
|
17840
|
+
current = parent;
|
|
17841
|
+
}
|
|
17842
|
+
}
|
|
17843
|
+
}
|
|
17844
|
+
function canonicalizeDataDirectory(rawPath, options = {}) {
|
|
17845
|
+
if (rawPath.trim().length === 0 || rawPath.includes("\0")) {
|
|
17846
|
+
return instanceRegistryError("data_dir_invalid", "Instance data directory cannot be empty.");
|
|
17847
|
+
}
|
|
17848
|
+
const platform = options.platform ?? process.platform;
|
|
17849
|
+
const pathApi = pathApiForPlatform(platform);
|
|
17850
|
+
const cwd = options.cwd ?? process.cwd();
|
|
17851
|
+
const absolutePath = pathApi.resolve(cwd, rawPath);
|
|
17852
|
+
const resolvedPath = resolveRealPathWithMissingTail(
|
|
17853
|
+
absolutePath,
|
|
17854
|
+
pathApi,
|
|
17855
|
+
options.realpath ?? realpathSync.native
|
|
17856
|
+
);
|
|
17857
|
+
const normalizedPath = pathApi.normalize(resolvedPath);
|
|
17858
|
+
const root = pathApi.parse(normalizedPath).root;
|
|
17859
|
+
const withoutTrailingSeparator = normalizedPath === root ? normalizedPath : normalizedPath.replace(/[\\/]+$/u, "");
|
|
17860
|
+
return instanceRegistrySuccess({
|
|
17861
|
+
path: withoutTrailingSeparator,
|
|
17862
|
+
comparisonKey: platform === "win32" ? withoutTrailingSeparator.toLowerCase() : withoutTrailingSeparator
|
|
17863
|
+
});
|
|
17864
|
+
}
|
|
17865
|
+
function validateDataDirectoryAssignment(rawPath, records, excludeName, options = {}) {
|
|
17866
|
+
const candidate = canonicalizeDataDirectory(rawPath, options);
|
|
17867
|
+
if (candidate.kind === "error") return candidate;
|
|
17868
|
+
const normalizedExcludedName = excludeName?.toLowerCase();
|
|
17869
|
+
for (const record2 of records) {
|
|
17870
|
+
if (record2.name === normalizedExcludedName) continue;
|
|
17871
|
+
const registered = canonicalizeDataDirectory(record2.dataDir, options);
|
|
17872
|
+
if (registered.kind === "error") {
|
|
17873
|
+
return instanceRegistryError(
|
|
17874
|
+
"data_dir_conflict",
|
|
17875
|
+
`Cannot safely compare the data directory registered to instance ${record2.name}.`
|
|
17876
|
+
);
|
|
17877
|
+
}
|
|
17878
|
+
if (registered.value.comparisonKey === candidate.value.comparisonKey) {
|
|
17879
|
+
return instanceRegistryError(
|
|
17880
|
+
"data_dir_conflict",
|
|
17881
|
+
`Data directory ${candidate.value.path} is already assigned to instance ${record2.name}; named instances cannot share data.`
|
|
17882
|
+
);
|
|
17883
|
+
}
|
|
17884
|
+
}
|
|
17885
|
+
return candidate;
|
|
17886
|
+
}
|
|
17887
|
+
function validPort(port) {
|
|
17888
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535;
|
|
17889
|
+
}
|
|
17890
|
+
function registeredPortOwner(pair, records, excludeName) {
|
|
17891
|
+
const excluded = excludeName?.toLowerCase();
|
|
17892
|
+
for (const record2 of records) {
|
|
17893
|
+
if (record2.name === excluded) continue;
|
|
17894
|
+
const occupied = /* @__PURE__ */ new Set([record2.publicPort, record2.upstreamPort]);
|
|
17895
|
+
if (occupied.has(pair.publicPort) || occupied.has(pair.upstreamPort)) return record2.name;
|
|
17896
|
+
}
|
|
17897
|
+
return null;
|
|
17898
|
+
}
|
|
17899
|
+
function isPortAvailableByBinding(port, host) {
|
|
17900
|
+
return new Promise((resolveAvailability) => {
|
|
17901
|
+
const server = createServer();
|
|
17902
|
+
let settled = false;
|
|
17903
|
+
const finish = (available) => {
|
|
17904
|
+
if (settled) return;
|
|
17905
|
+
settled = true;
|
|
17906
|
+
server.removeAllListeners();
|
|
17907
|
+
resolveAvailability(available);
|
|
17908
|
+
};
|
|
17909
|
+
server.once("error", () => finish(false));
|
|
17910
|
+
server.once("listening", () => {
|
|
17911
|
+
server.close((error) => finish(error === void 0));
|
|
17912
|
+
});
|
|
17913
|
+
server.unref();
|
|
17914
|
+
try {
|
|
17915
|
+
server.listen({ port, host, exclusive: true });
|
|
17916
|
+
} catch {
|
|
17917
|
+
finish(false);
|
|
17918
|
+
}
|
|
17919
|
+
});
|
|
17920
|
+
}
|
|
17921
|
+
async function pairIsAvailable(pair, publicHost, check) {
|
|
17922
|
+
if (!await check(pair.publicPort, publicHost)) {
|
|
17923
|
+
return { available: false, conflictingPort: pair.publicPort };
|
|
17924
|
+
}
|
|
17925
|
+
if (!await check(pair.upstreamPort, "127.0.0.1")) {
|
|
17926
|
+
return { available: false, conflictingPort: pair.upstreamPort };
|
|
17927
|
+
}
|
|
17928
|
+
return { available: true, conflictingPort: null };
|
|
17929
|
+
}
|
|
17930
|
+
async function planInstancePorts(options) {
|
|
17931
|
+
const check = options.isPortAvailable ?? isPortAvailableByBinding;
|
|
17932
|
+
const publicHost = options.publicHost ?? "127.0.0.1";
|
|
17933
|
+
if (options.requestedPublicPort === void 0 && options.requestedUpstreamPort !== void 0) {
|
|
17934
|
+
return instanceRegistryError(
|
|
17935
|
+
"port_invalid",
|
|
17936
|
+
"An explicit upstream port requires an explicit public port."
|
|
17937
|
+
);
|
|
17938
|
+
}
|
|
17939
|
+
if (options.requestedPublicPort !== void 0) {
|
|
17940
|
+
const pair = {
|
|
17941
|
+
publicPort: options.requestedPublicPort,
|
|
17942
|
+
upstreamPort: options.requestedUpstreamPort ?? options.requestedPublicPort + NAMED_INSTANCE_UPSTREAM_OFFSET
|
|
17943
|
+
};
|
|
17944
|
+
if (!validPort(pair.publicPort) || !validPort(pair.upstreamPort) || pair.publicPort === pair.upstreamPort) {
|
|
17945
|
+
return instanceRegistryError(
|
|
17946
|
+
"port_invalid",
|
|
17947
|
+
"Public and upstream ports must be different integers from 1 to 65535."
|
|
17948
|
+
);
|
|
17949
|
+
}
|
|
17950
|
+
const owner = registeredPortOwner(pair, options.records, options.excludeName);
|
|
17951
|
+
if (owner !== null) {
|
|
17952
|
+
return instanceRegistryError(
|
|
17953
|
+
"port_conflict",
|
|
17954
|
+
`Requested port pair ${String(pair.publicPort)}/${String(pair.upstreamPort)} overlaps instance ${owner}.`
|
|
17955
|
+
);
|
|
17956
|
+
}
|
|
17957
|
+
const availability = await pairIsAvailable(pair, publicHost, check);
|
|
17958
|
+
return availability.available ? instanceRegistrySuccess(pair) : instanceRegistryError(
|
|
17959
|
+
"port_conflict",
|
|
17960
|
+
`Port ${String(availability.conflictingPort)} already has a listener; no process was terminated.`
|
|
17961
|
+
);
|
|
17962
|
+
}
|
|
17963
|
+
for (let publicPort = DEFAULT_NAMED_INSTANCE_PUBLIC_PORT; publicPort + NAMED_INSTANCE_UPSTREAM_OFFSET <= 65535; publicPort += NAMED_INSTANCE_PORT_STEP) {
|
|
17964
|
+
const pair = {
|
|
17965
|
+
publicPort,
|
|
17966
|
+
upstreamPort: publicPort + NAMED_INSTANCE_UPSTREAM_OFFSET
|
|
17967
|
+
};
|
|
17968
|
+
if (registeredPortOwner(pair, options.records, options.excludeName) !== null) continue;
|
|
17969
|
+
const availability = await pairIsAvailable(pair, publicHost, check);
|
|
17970
|
+
if (availability.available) return instanceRegistrySuccess(pair);
|
|
17971
|
+
}
|
|
17972
|
+
return instanceRegistryError(
|
|
17973
|
+
"port_exhausted",
|
|
17974
|
+
`No available named-instance port pair was found from ${String(DEFAULT_NAMED_INSTANCE_PUBLIC_PORT)} through 65535.`
|
|
17975
|
+
);
|
|
17976
|
+
}
|
|
17977
|
+
const DEFAULT_INSTANCE_HOST = "127.0.0.1";
|
|
17978
|
+
const DEFAULT_INSTANCE_CAPTURE_MODE = "simple";
|
|
17979
|
+
const INSTANCE_READY_TIMEOUT_MS = 15e3;
|
|
17980
|
+
const INSTANCE_STOP_TIMEOUT_MS = 2e4;
|
|
17981
|
+
const INSTANCE_POLL_INTERVAL_MS = 200;
|
|
17982
|
+
function success(value) {
|
|
17983
|
+
return { kind: "success", value };
|
|
17984
|
+
}
|
|
17985
|
+
function failure(code, message) {
|
|
17986
|
+
return {
|
|
17987
|
+
kind: "error",
|
|
17988
|
+
error: { code, message, retryable: isRetryableInstanceError(code) }
|
|
17989
|
+
};
|
|
17990
|
+
}
|
|
17991
|
+
function fromRegistry(result) {
|
|
17992
|
+
return result.kind === "success" ? success(result.value) : failure(result.error.code, result.error.message);
|
|
17993
|
+
}
|
|
17994
|
+
function sleep(milliseconds) {
|
|
17995
|
+
return new Promise((resolveSleep) => setTimeout(resolveSleep, milliseconds));
|
|
17996
|
+
}
|
|
17997
|
+
function displayBasePath(basePath) {
|
|
17998
|
+
return basePath.length === 0 ? "/" : basePath;
|
|
17999
|
+
}
|
|
18000
|
+
function recordUrls(record2) {
|
|
18001
|
+
const origin = urlForHost(record2.publicPort, record2.host);
|
|
18002
|
+
return {
|
|
18003
|
+
ui: appendBasePathToOrigin(origin, "/", record2.basePath),
|
|
18004
|
+
api: appendBasePathToOrigin(origin, "/api", record2.basePath),
|
|
18005
|
+
health: appendBasePathToOrigin(origin, "/api/health", record2.basePath),
|
|
18006
|
+
proxy: appendBasePathToOrigin(origin, "/proxy", record2.basePath),
|
|
18007
|
+
openai: appendBasePathToOrigin(origin, "/proxy/v1", record2.basePath),
|
|
18008
|
+
mcp: appendBasePathToOrigin(origin, "/api/mcp", record2.basePath)
|
|
18009
|
+
};
|
|
18010
|
+
}
|
|
18011
|
+
function statusOutput(record2, live, checkedAt = /* @__PURE__ */ new Date()) {
|
|
18012
|
+
return {
|
|
18013
|
+
schemaVersion: INSTANCE_API_SCHEMA_VERSION,
|
|
18014
|
+
name: record2.name,
|
|
18015
|
+
instanceId: record2.instanceId,
|
|
18016
|
+
state: live.state,
|
|
18017
|
+
stateReason: live.state === "conflict" ? live.code : null,
|
|
18018
|
+
checkedAt: checkedAt.toISOString(),
|
|
18019
|
+
createdAt: record2.createdAt,
|
|
18020
|
+
updatedAt: record2.updatedAt,
|
|
18021
|
+
launch: {
|
|
18022
|
+
launchId: record2.launch.launchId,
|
|
18023
|
+
startedAt: record2.launch.startedAt
|
|
18024
|
+
},
|
|
18025
|
+
host: record2.host,
|
|
18026
|
+
publicPort: record2.publicPort,
|
|
18027
|
+
upstreamPort: record2.upstreamPort,
|
|
18028
|
+
basePath: displayBasePath(record2.basePath),
|
|
18029
|
+
captureMode: record2.captureMode,
|
|
18030
|
+
dataDir: record2.dataDir,
|
|
18031
|
+
supervisorPid: live.supervisorPid,
|
|
18032
|
+
urls: recordUrls(record2)
|
|
18033
|
+
};
|
|
18034
|
+
}
|
|
18035
|
+
function operationOutput(operation, outcome, instance) {
|
|
18036
|
+
return {
|
|
18037
|
+
schemaVersion: INSTANCE_API_SCHEMA_VERSION,
|
|
18038
|
+
operation,
|
|
18039
|
+
outcome,
|
|
18040
|
+
instance
|
|
18041
|
+
};
|
|
18042
|
+
}
|
|
18043
|
+
function connectionOutput(instance) {
|
|
18044
|
+
const serverName = `agent-inspector-${instance.name}`;
|
|
18045
|
+
return {
|
|
18046
|
+
schemaVersion: INSTANCE_API_SCHEMA_VERSION,
|
|
18047
|
+
instance,
|
|
18048
|
+
baseUrl: instance.urls.ui,
|
|
18049
|
+
endpoints: instance.urls,
|
|
18050
|
+
environment: {
|
|
18051
|
+
ANTHROPIC_BASE_URL: instance.urls.proxy,
|
|
18052
|
+
LLM_BASE_URL: instance.urls.proxy,
|
|
18053
|
+
OPENAI_BASE_URL: instance.urls.openai,
|
|
18054
|
+
AGENT_INSPECTOR_MCP_URL: instance.urls.mcp
|
|
18055
|
+
},
|
|
18056
|
+
mcp: {
|
|
18057
|
+
serverName,
|
|
18058
|
+
transport: "streamable-http",
|
|
18059
|
+
url: instance.urls.mcp
|
|
18060
|
+
},
|
|
18061
|
+
codex: {
|
|
18062
|
+
providerName: serverName,
|
|
18063
|
+
baseUrl: instance.urls.openai,
|
|
18064
|
+
wireApi: "responses"
|
|
18065
|
+
}
|
|
18066
|
+
};
|
|
18067
|
+
}
|
|
18068
|
+
function expectedIdentity(record2) {
|
|
18069
|
+
return {
|
|
18070
|
+
instanceId: record2.instanceId,
|
|
18071
|
+
launchId: record2.launch.launchId,
|
|
18072
|
+
controlToken: record2.launch.controlToken,
|
|
18073
|
+
host: probeHostForBindHost(record2.host),
|
|
18074
|
+
publicPort: record2.publicPort
|
|
18075
|
+
};
|
|
18076
|
+
}
|
|
18077
|
+
async function classifyRecord(record2, deps) {
|
|
18078
|
+
const probe = await probeInstanceControl(expectedIdentity(record2), deps.fetchImpl);
|
|
18079
|
+
if (probe.kind === "running") {
|
|
18080
|
+
return { state: "running", supervisorPid: probe.identity.supervisorPid };
|
|
18081
|
+
}
|
|
18082
|
+
if (probe.kind === "identity-mismatch" || probe.kind === "invalid-response") {
|
|
18083
|
+
return { state: "conflict", supervisorPid: null, code: "identity_mismatch" };
|
|
18084
|
+
}
|
|
18085
|
+
const check = deps.isPortAvailable ?? isPortAvailableByBinding;
|
|
18086
|
+
const publicAvailable = await check(record2.publicPort, record2.host);
|
|
18087
|
+
const upstreamAvailable = await check(record2.upstreamPort, DEFAULT_INSTANCE_HOST);
|
|
18088
|
+
return publicAvailable && upstreamAvailable ? { state: "stopped", supervisorPid: null } : { state: "conflict", supervisorPid: null, code: "port_conflict" };
|
|
18089
|
+
}
|
|
18090
|
+
function waitForProcess(child) {
|
|
18091
|
+
return new Promise((resolveExit) => {
|
|
18092
|
+
let settled = false;
|
|
18093
|
+
const finish = (code) => {
|
|
18094
|
+
if (settled) return;
|
|
18095
|
+
settled = true;
|
|
18096
|
+
resolveExit(code);
|
|
18097
|
+
};
|
|
18098
|
+
child.once("exit", (code) => finish(code ?? 1));
|
|
18099
|
+
child.once("error", () => finish(1));
|
|
18100
|
+
});
|
|
18101
|
+
}
|
|
18102
|
+
function defaultOpenUrl(targetUrl) {
|
|
18103
|
+
const command = openUrlCommand(targetUrl);
|
|
18104
|
+
if (command === null) return;
|
|
18105
|
+
const child = spawn(command.command, command.args, {
|
|
18106
|
+
stdio: "ignore",
|
|
18107
|
+
detached: true,
|
|
18108
|
+
windowsHide: true
|
|
18109
|
+
});
|
|
18110
|
+
child.once("error", () => void 0);
|
|
18111
|
+
child.unref();
|
|
18112
|
+
}
|
|
18113
|
+
function launchArgs(record2, open2) {
|
|
18114
|
+
const args = [
|
|
18115
|
+
"--background",
|
|
18116
|
+
"--no-open",
|
|
18117
|
+
"--port",
|
|
18118
|
+
String(record2.publicPort),
|
|
18119
|
+
"--host",
|
|
18120
|
+
record2.host,
|
|
18121
|
+
"--mode",
|
|
18122
|
+
record2.captureMode
|
|
18123
|
+
];
|
|
18124
|
+
if (open2) args.push("--open");
|
|
18125
|
+
return args;
|
|
18126
|
+
}
|
|
18127
|
+
async function defaultLaunch(record2, open2, cliEntry) {
|
|
18128
|
+
const child = spawn(process.execPath, [cliEntry, ...launchArgs(record2, open2)], {
|
|
18129
|
+
stdio: "ignore",
|
|
18130
|
+
windowsHide: true,
|
|
18131
|
+
env: {
|
|
18132
|
+
...process.env,
|
|
18133
|
+
[AGENT_INSPECTOR_CLI_ENTRY_ENV]: cliEntry,
|
|
18134
|
+
AGENT_INSPECTOR_DATA_DIR: record2.dataDir,
|
|
18135
|
+
[AGENT_INSPECTOR_BASE_PATH_ENV]: displayBasePath(record2.basePath),
|
|
18136
|
+
[MANAGED_INSTANCE_ENV.name]: record2.name,
|
|
18137
|
+
[MANAGED_INSTANCE_ENV.instanceId]: record2.instanceId,
|
|
18138
|
+
[MANAGED_INSTANCE_ENV.launchId]: record2.launch.launchId,
|
|
18139
|
+
[MANAGED_INSTANCE_ENV.controlToken]: record2.launch.controlToken,
|
|
18140
|
+
[MANAGED_INSTANCE_ENV.startedAt]: record2.launch.startedAt,
|
|
18141
|
+
[MANAGED_INSTANCE_ENV.upstreamPort]: String(record2.upstreamPort)
|
|
18142
|
+
}
|
|
18143
|
+
});
|
|
18144
|
+
return await waitForProcess(child) === 0;
|
|
18145
|
+
}
|
|
18146
|
+
async function launchRecord(record2, open2, deps) {
|
|
18147
|
+
const cliEntry = deps.cliEntry ?? process.env[AGENT_INSPECTOR_CLI_ENTRY_ENV] ?? process.argv[1];
|
|
18148
|
+
if (deps.launch === void 0 && (cliEntry === void 0 || cliEntry.length === 0)) {
|
|
18149
|
+
return failure("launch_unavailable", "Cannot resolve the packaged Agent Inspector CLI entry.");
|
|
18150
|
+
}
|
|
18151
|
+
const launched = deps.launch === void 0 ? await defaultLaunch(record2, open2, cliEntry ?? "") : await deps.launch(record2, open2);
|
|
18152
|
+
if (!launched) {
|
|
18153
|
+
return failure(
|
|
18154
|
+
"launch_failed",
|
|
18155
|
+
`Instance ${record2.name} did not become ready; its saved configuration is retained for retry.`
|
|
18156
|
+
);
|
|
18157
|
+
}
|
|
18158
|
+
const wait = deps.sleep ?? sleep;
|
|
18159
|
+
const deadline = Date.now() + (deps.readyTimeoutMs ?? INSTANCE_READY_TIMEOUT_MS);
|
|
18160
|
+
while (Date.now() <= deadline) {
|
|
18161
|
+
const live = await classifyRecord(record2, deps);
|
|
18162
|
+
if (live.state === "running") return success(live);
|
|
18163
|
+
if (live.state === "conflict") {
|
|
18164
|
+
return failure(
|
|
18165
|
+
live.code,
|
|
18166
|
+
`Instance ${record2.name} launch identity does not own its saved port pair; no process was terminated.`
|
|
18167
|
+
);
|
|
18168
|
+
}
|
|
18169
|
+
await wait(INSTANCE_POLL_INTERVAL_MS);
|
|
18170
|
+
}
|
|
18171
|
+
return failure(
|
|
18172
|
+
"launch_failed",
|
|
18173
|
+
`Instance ${record2.name} launch timed out; its saved configuration is retained for retry.`
|
|
18174
|
+
);
|
|
18175
|
+
}
|
|
18176
|
+
async function waitForPortsToClose(record2, deps) {
|
|
18177
|
+
const check = deps.isPortAvailable ?? isPortAvailableByBinding;
|
|
18178
|
+
const wait = deps.sleep ?? sleep;
|
|
18179
|
+
const deadline = Date.now() + (deps.stopTimeoutMs ?? INSTANCE_STOP_TIMEOUT_MS);
|
|
18180
|
+
while (Date.now() <= deadline) {
|
|
18181
|
+
const publicAvailable = await check(record2.publicPort, record2.host);
|
|
18182
|
+
const upstreamAvailable = await check(record2.upstreamPort, DEFAULT_INSTANCE_HOST);
|
|
18183
|
+
if (publicAvailable && upstreamAvailable) return true;
|
|
18184
|
+
await wait(INSTANCE_POLL_INTERVAL_MS);
|
|
18185
|
+
}
|
|
18186
|
+
return false;
|
|
18187
|
+
}
|
|
18188
|
+
async function stopRecord(record2, deps) {
|
|
18189
|
+
const live = await classifyRecord(record2, deps);
|
|
18190
|
+
if (live.state === "stopped") return success({ live, outcome: "already_stopped" });
|
|
18191
|
+
if (live.state === "conflict") {
|
|
18192
|
+
return failure(
|
|
18193
|
+
live.code,
|
|
18194
|
+
`Instance ${record2.name} does not own its saved port pair; refusing to stop another process.`
|
|
18195
|
+
);
|
|
18196
|
+
}
|
|
18197
|
+
const requested = await requestInstanceShutdown(expectedIdentity(record2), deps.fetchImpl);
|
|
18198
|
+
if (requested.kind === "identity-mismatch") {
|
|
18199
|
+
return failure(
|
|
18200
|
+
"identity_mismatch",
|
|
18201
|
+
`Instance ${record2.name} changed identity before shutdown; no process was terminated.`
|
|
18202
|
+
);
|
|
18203
|
+
}
|
|
18204
|
+
if (requested.kind !== "accepted") {
|
|
18205
|
+
return failure("shutdown_failed", `Instance ${record2.name} rejected the shutdown request.`);
|
|
18206
|
+
}
|
|
18207
|
+
return await waitForPortsToClose(record2, deps) ? success({
|
|
18208
|
+
live: { state: "stopped", supervisorPid: null },
|
|
18209
|
+
outcome: "stopped"
|
|
18210
|
+
}) : failure(
|
|
18211
|
+
"shutdown_failed",
|
|
18212
|
+
`Instance ${record2.name} did not stop before the shutdown deadline.`
|
|
18213
|
+
);
|
|
18214
|
+
}
|
|
18215
|
+
async function acquireLocks(root, name2, includeRegistry) {
|
|
18216
|
+
const registryResult = includeRegistry ? await acquireRegistryLock(root) : null;
|
|
18217
|
+
if (registryResult !== null && registryResult.kind === "error") {
|
|
18218
|
+
return fromRegistry(registryResult);
|
|
18219
|
+
}
|
|
18220
|
+
const registry2 = registryResult?.kind === "success" ? registryResult.value : null;
|
|
18221
|
+
const instanceResult = await acquireInstanceLock(root, name2);
|
|
18222
|
+
if (instanceResult.kind === "error") {
|
|
18223
|
+
registry2?.release();
|
|
18224
|
+
return fromRegistry(instanceResult);
|
|
18225
|
+
}
|
|
18226
|
+
return success({ registry: registry2, instance: instanceResult.value });
|
|
18227
|
+
}
|
|
18228
|
+
function releaseLocks(locks) {
|
|
18229
|
+
const instanceReleased = locks.instance.release();
|
|
18230
|
+
const registryReleased = locks.registry?.release() ?? instanceRegistrySuccess(void 0);
|
|
18231
|
+
if (instanceReleased.kind === "error") return fromRegistry(instanceReleased);
|
|
18232
|
+
return fromRegistry(registryReleased);
|
|
18233
|
+
}
|
|
18234
|
+
async function withLocks(root, name2, includeRegistry, operation) {
|
|
18235
|
+
const locks = await acquireLocks(root, name2, includeRegistry);
|
|
18236
|
+
if (locks.kind === "error") return locks;
|
|
18237
|
+
let result;
|
|
18238
|
+
try {
|
|
18239
|
+
result = await operation();
|
|
18240
|
+
} catch {
|
|
18241
|
+
result = failure("registry_io", "The named-instance lifecycle operation failed.");
|
|
18242
|
+
}
|
|
18243
|
+
const released = releaseLocks(locks.value);
|
|
18244
|
+
return result.kind === "success" && released.kind === "error" ? released : result;
|
|
18245
|
+
}
|
|
18246
|
+
function readRequiredRecord(root, name2) {
|
|
18247
|
+
const read = readInstanceRecord(root, name2);
|
|
18248
|
+
if (read.kind === "error") return fromRegistry(read);
|
|
18249
|
+
return read.value === null ? failure("instance_not_found", `Named instance ${name2} does not exist.`) : success(read.value);
|
|
18250
|
+
}
|
|
18251
|
+
function overridesDiffer(record2, action) {
|
|
18252
|
+
const { overrides } = action;
|
|
18253
|
+
if (overrides.port !== null && overrides.port !== record2.publicPort) return true;
|
|
18254
|
+
if (overrides.basePath !== null && overrides.basePath !== record2.basePath) return true;
|
|
18255
|
+
if (overrides.host !== null && overrides.host !== record2.host) return true;
|
|
18256
|
+
if (overrides.captureMode !== null && overrides.captureMode !== record2.captureMode) return true;
|
|
18257
|
+
if (overrides.dataDir !== null) {
|
|
18258
|
+
const requested = canonicalizeDataDirectory(overrides.dataDir);
|
|
18259
|
+
const saved = canonicalizeDataDirectory(record2.dataDir);
|
|
18260
|
+
if (requested.kind === "error" || saved.kind === "error" || requested.value.comparisonKey !== saved.value.comparisonKey) {
|
|
18261
|
+
return true;
|
|
18262
|
+
}
|
|
18263
|
+
}
|
|
18264
|
+
return false;
|
|
18265
|
+
}
|
|
18266
|
+
function updatedRecord(record2, action, pair, dataDir2) {
|
|
18267
|
+
const candidate = {
|
|
18268
|
+
...record2,
|
|
18269
|
+
host: action.overrides.host ?? record2.host,
|
|
18270
|
+
publicPort: pair.publicPort,
|
|
18271
|
+
upstreamPort: pair.upstreamPort,
|
|
18272
|
+
basePath: action.overrides.basePath ?? record2.basePath,
|
|
18273
|
+
captureMode: action.overrides.captureMode ?? record2.captureMode,
|
|
18274
|
+
dataDir: dataDir2
|
|
18275
|
+
};
|
|
18276
|
+
const parsed = InstanceRecordSchema.safeParse(candidate);
|
|
18277
|
+
if (!parsed.success) {
|
|
18278
|
+
return failure(
|
|
18279
|
+
"record_invalid",
|
|
18280
|
+
`Cannot update instance ${record2.name}: ${parsed.error.issues[0]?.message ?? "invalid configuration"}.`
|
|
18281
|
+
);
|
|
18282
|
+
}
|
|
18283
|
+
return fromRegistry(rotateInstanceLaunch(parsed.data));
|
|
18284
|
+
}
|
|
18285
|
+
function saveRecord(root, record2) {
|
|
18286
|
+
return fromRegistry(writeInstanceRecord(root, record2));
|
|
18287
|
+
}
|
|
18288
|
+
function validateInstanceDataDirectory(rawPath, records, excludeName) {
|
|
18289
|
+
const assigned = validateDataDirectoryAssignment(rawPath, records, excludeName);
|
|
18290
|
+
if (assigned.kind === "error") return fromRegistry(assigned);
|
|
18291
|
+
const defaultDirectory = canonicalizeDataDirectory(resolveDataDir());
|
|
18292
|
+
if (defaultDirectory.kind === "error") return fromRegistry(defaultDirectory);
|
|
18293
|
+
if (assigned.value.comparisonKey === defaultDirectory.value.comparisonKey) {
|
|
18294
|
+
return failure(
|
|
18295
|
+
"data_dir_conflict",
|
|
18296
|
+
`Data directory ${assigned.value.path} belongs to the unmanaged default instance; named instances cannot share it.`
|
|
18297
|
+
);
|
|
18298
|
+
}
|
|
18299
|
+
return success(assigned.value.path);
|
|
18300
|
+
}
|
|
18301
|
+
async function createNewRecord(root, action, deps) {
|
|
18302
|
+
const recordsResult = listInstanceRecords(root);
|
|
18303
|
+
if (recordsResult.kind === "error") return fromRegistry(recordsResult);
|
|
18304
|
+
const pathsResult = getInstancePaths(root, action.name);
|
|
18305
|
+
if (pathsResult.kind === "error") return fromRegistry(pathsResult);
|
|
18306
|
+
const host = action.overrides.host ?? DEFAULT_INSTANCE_HOST;
|
|
18307
|
+
const pairResult = await planInstancePorts({
|
|
18308
|
+
records: recordsResult.value,
|
|
18309
|
+
requestedPublicPort: action.overrides.port ?? void 0,
|
|
18310
|
+
publicHost: host,
|
|
18311
|
+
isPortAvailable: deps.isPortAvailable
|
|
18312
|
+
});
|
|
18313
|
+
if (pairResult.kind === "error") return fromRegistry(pairResult);
|
|
18314
|
+
const requestedDataDir = action.overrides.dataDir ?? pathsResult.value.dataDir;
|
|
18315
|
+
const dataDirResult = validateInstanceDataDirectory(requestedDataDir, recordsResult.value);
|
|
18316
|
+
if (dataDirResult.kind === "error") return dataDirResult;
|
|
18317
|
+
const created = createInstanceRecord({
|
|
18318
|
+
name: action.name,
|
|
18319
|
+
host,
|
|
18320
|
+
publicPort: pairResult.value.publicPort,
|
|
18321
|
+
upstreamPort: pairResult.value.upstreamPort,
|
|
18322
|
+
basePath: action.overrides.basePath ?? getConfiguredBasePath(process.env),
|
|
18323
|
+
captureMode: action.overrides.captureMode ?? DEFAULT_INSTANCE_CAPTURE_MODE,
|
|
18324
|
+
dataDir: dataDirResult.value
|
|
18325
|
+
});
|
|
18326
|
+
return created.kind === "error" ? fromRegistry(created) : saveRecord(root, created.value);
|
|
18327
|
+
}
|
|
18328
|
+
async function prepareStoppedStart(root, record2, deps) {
|
|
18329
|
+
const records = listInstanceRecords(root);
|
|
18330
|
+
if (records.kind === "error") return fromRegistry(records);
|
|
18331
|
+
const pair = await planInstancePorts({
|
|
18332
|
+
records: records.value,
|
|
18333
|
+
requestedPublicPort: record2.publicPort,
|
|
18334
|
+
requestedUpstreamPort: record2.upstreamPort,
|
|
18335
|
+
excludeName: record2.name,
|
|
18336
|
+
publicHost: record2.host,
|
|
18337
|
+
isPortAvailable: deps.isPortAvailable
|
|
18338
|
+
});
|
|
18339
|
+
if (pair.kind === "error") return fromRegistry(pair);
|
|
18340
|
+
const dataDir2 = validateInstanceDataDirectory(record2.dataDir, records.value, record2.name);
|
|
18341
|
+
if (dataDir2.kind === "error") return dataDir2;
|
|
18342
|
+
const rotated = rotateInstanceLaunch({ ...record2, dataDir: dataDir2.value });
|
|
18343
|
+
return rotated.kind === "error" ? fromRegistry(rotated) : saveRecord(root, rotated.value);
|
|
18344
|
+
}
|
|
18345
|
+
async function startAction(root, action, deps) {
|
|
18346
|
+
return withLocks(root, action.name, true, async () => {
|
|
18347
|
+
const read = readInstanceRecord(root, action.name);
|
|
18348
|
+
if (read.kind === "error") return fromRegistry(read);
|
|
18349
|
+
if (read.value === null) {
|
|
18350
|
+
const created = await createNewRecord(root, action, deps);
|
|
18351
|
+
if (created.kind === "error") return created;
|
|
18352
|
+
const launched2 = await launchRecord(created.value, action.overrides.open, deps);
|
|
18353
|
+
return launched2.kind === "error" ? launched2 : success(operationOutput("start", "created", statusOutput(created.value, launched2.value)));
|
|
18354
|
+
}
|
|
18355
|
+
const record2 = read.value;
|
|
18356
|
+
if (overridesDiffer(record2, action)) {
|
|
18357
|
+
return failure(
|
|
18358
|
+
"config_mismatch",
|
|
18359
|
+
`Instance ${record2.name} already has saved configuration; use "agent-inspector instance restart ${record2.name}" with overrides to change it.`
|
|
18360
|
+
);
|
|
18361
|
+
}
|
|
18362
|
+
const live = await classifyRecord(record2, deps);
|
|
18363
|
+
if (live.state === "running") {
|
|
18364
|
+
if (action.overrides.open) (deps.openUrl ?? defaultOpenUrl)(recordUrls(record2).ui);
|
|
18365
|
+
return success(operationOutput("start", "already_running", statusOutput(record2, live)));
|
|
18366
|
+
}
|
|
18367
|
+
if (live.state === "conflict") {
|
|
18368
|
+
return failure(
|
|
18369
|
+
live.code,
|
|
18370
|
+
`Instance ${record2.name} does not own its saved port pair; no process was terminated.`
|
|
18371
|
+
);
|
|
18372
|
+
}
|
|
18373
|
+
const prepared = await prepareStoppedStart(root, record2, deps);
|
|
18374
|
+
if (prepared.kind === "error") return prepared;
|
|
18375
|
+
const launched = await launchRecord(prepared.value, action.overrides.open, deps);
|
|
18376
|
+
return launched.kind === "error" ? launched : success(operationOutput("start", "started", statusOutput(prepared.value, launched.value)));
|
|
18377
|
+
});
|
|
18378
|
+
}
|
|
18379
|
+
async function planRestartRecord(root, record2, action, wasRunning, deps) {
|
|
18380
|
+
const records = listInstanceRecords(root);
|
|
18381
|
+
if (records.kind === "error") return fromRegistry(records);
|
|
18382
|
+
const desiredPublicPort = action.overrides.port ?? record2.publicPort;
|
|
18383
|
+
const desiredUpstreamPort = desiredPublicPort + 2;
|
|
18384
|
+
const sameLivePair = wasRunning && desiredPublicPort === record2.publicPort && desiredUpstreamPort === record2.upstreamPort;
|
|
18385
|
+
let pair;
|
|
18386
|
+
if (sameLivePair) {
|
|
18387
|
+
pair = { publicPort: record2.publicPort, upstreamPort: record2.upstreamPort };
|
|
18388
|
+
} else {
|
|
18389
|
+
const planned = await planInstancePorts({
|
|
18390
|
+
records: records.value,
|
|
18391
|
+
requestedPublicPort: desiredPublicPort,
|
|
18392
|
+
requestedUpstreamPort: desiredUpstreamPort,
|
|
18393
|
+
excludeName: record2.name,
|
|
18394
|
+
publicHost: action.overrides.host ?? record2.host,
|
|
18395
|
+
isPortAvailable: deps.isPortAvailable
|
|
18396
|
+
});
|
|
18397
|
+
if (planned.kind === "error") return fromRegistry(planned);
|
|
18398
|
+
pair = planned.value;
|
|
18399
|
+
}
|
|
18400
|
+
const requestedDataDir = action.overrides.dataDir ?? record2.dataDir;
|
|
18401
|
+
const dataDir2 = validateInstanceDataDirectory(requestedDataDir, records.value, record2.name);
|
|
18402
|
+
if (dataDir2.kind === "error") return dataDir2;
|
|
18403
|
+
return updatedRecord(record2, action, pair, dataDir2.value);
|
|
18404
|
+
}
|
|
18405
|
+
async function rollbackRestart(root, previous, deps) {
|
|
18406
|
+
const rotated = rotateInstanceLaunch(previous);
|
|
18407
|
+
if (rotated.kind === "error") return false;
|
|
18408
|
+
const saved = saveRecord(root, rotated.value);
|
|
18409
|
+
if (saved.kind === "error") return false;
|
|
18410
|
+
const launched = await launchRecord(saved.value, false, deps);
|
|
18411
|
+
return launched.kind === "success";
|
|
18412
|
+
}
|
|
18413
|
+
async function restartFailureAfterStop(root, previous, deps) {
|
|
18414
|
+
const rolledBack = await rollbackRestart(root, previous, deps);
|
|
18415
|
+
return rolledBack ? failure(
|
|
18416
|
+
"restart_failed_rolled_back",
|
|
18417
|
+
`Instance ${previous.name} replacement failed; the previous configuration was restored with a fresh launch.`
|
|
18418
|
+
) : failure(
|
|
18419
|
+
"restart_failed_degraded",
|
|
18420
|
+
`Instance ${previous.name} replacement and rollback both failed; its saved configuration remains recoverable for a later start.`
|
|
18421
|
+
);
|
|
18422
|
+
}
|
|
18423
|
+
async function restartAction(root, action, deps) {
|
|
18424
|
+
return withLocks(root, action.name, true, async () => {
|
|
18425
|
+
const required = readRequiredRecord(root, action.name);
|
|
18426
|
+
if (required.kind === "error") return required;
|
|
18427
|
+
const current = required.value;
|
|
18428
|
+
const live = await classifyRecord(current, deps);
|
|
18429
|
+
if (live.state === "conflict") {
|
|
18430
|
+
return failure(
|
|
18431
|
+
live.code,
|
|
18432
|
+
`Instance ${current.name} does not own its saved port pair; refusing to replace another process.`
|
|
18433
|
+
);
|
|
18434
|
+
}
|
|
18435
|
+
const planned = await planRestartRecord(root, current, action, live.state === "running", deps);
|
|
18436
|
+
if (planned.kind === "error") return planned;
|
|
18437
|
+
if (live.state === "running") {
|
|
18438
|
+
const stopped = await stopRecord(current, deps);
|
|
18439
|
+
if (stopped.kind === "error") return stopped;
|
|
18440
|
+
if (planned.value.publicPort === current.publicPort && planned.value.upstreamPort === current.upstreamPort) {
|
|
18441
|
+
const check = deps.isPortAvailable ?? isPortAvailableByBinding;
|
|
18442
|
+
if (!await check(current.publicPort, current.host) || !await check(current.upstreamPort, DEFAULT_INSTANCE_HOST)) {
|
|
18443
|
+
return failure(
|
|
18444
|
+
"port_conflict",
|
|
18445
|
+
`Instance ${current.name} ports did not become available after shutdown.`
|
|
18446
|
+
);
|
|
18447
|
+
}
|
|
18448
|
+
}
|
|
18449
|
+
}
|
|
18450
|
+
const saved = saveRecord(root, planned.value);
|
|
18451
|
+
if (saved.kind === "error") {
|
|
18452
|
+
return live.state === "running" ? restartFailureAfterStop(root, current, deps) : saved;
|
|
18453
|
+
}
|
|
18454
|
+
const launched = await launchRecord(saved.value, action.overrides.open, deps);
|
|
18455
|
+
if (launched.kind === "error") {
|
|
18456
|
+
return live.state === "running" ? restartFailureAfterStop(root, current, deps) : launched;
|
|
18457
|
+
}
|
|
18458
|
+
return success(
|
|
18459
|
+
operationOutput("restart", "restarted", statusOutput(saved.value, launched.value))
|
|
18460
|
+
);
|
|
18461
|
+
});
|
|
18462
|
+
}
|
|
18463
|
+
async function stopAction(root, action, deps) {
|
|
18464
|
+
return withLocks(root, action.name, false, async () => {
|
|
18465
|
+
const required = readRequiredRecord(root, action.name);
|
|
18466
|
+
if (required.kind === "error") return required;
|
|
18467
|
+
const stopped = await stopRecord(required.value, deps);
|
|
18468
|
+
return stopped.kind === "error" ? stopped : success(
|
|
18469
|
+
operationOutput(
|
|
18470
|
+
"stop",
|
|
18471
|
+
stopped.value.outcome,
|
|
18472
|
+
statusOutput(required.value, stopped.value.live)
|
|
18473
|
+
)
|
|
18474
|
+
);
|
|
18475
|
+
});
|
|
18476
|
+
}
|
|
18477
|
+
async function statusAction(root, name2, deps) {
|
|
18478
|
+
const required = readRequiredRecord(root, name2);
|
|
18479
|
+
if (required.kind === "error") return required;
|
|
18480
|
+
const live = await classifyRecord(required.value, deps);
|
|
18481
|
+
return success(statusOutput(required.value, live));
|
|
18482
|
+
}
|
|
18483
|
+
async function listAction(root, deps) {
|
|
18484
|
+
const records = listInstanceRecords(root);
|
|
18485
|
+
if (records.kind === "error") return fromRegistry(records);
|
|
18486
|
+
const outputs = await Promise.all(
|
|
18487
|
+
records.value.map(async (record2) => statusOutput(record2, await classifyRecord(record2, deps)))
|
|
18488
|
+
);
|
|
18489
|
+
return success(outputs);
|
|
18490
|
+
}
|
|
18491
|
+
function requestActionOverrides(input) {
|
|
18492
|
+
return {
|
|
18493
|
+
port: input.port ?? null,
|
|
18494
|
+
basePath: input.basePath === void 0 ? null : normalizeBasePath(input.basePath),
|
|
18495
|
+
host: input.host ?? null,
|
|
18496
|
+
captureMode: input.captureMode ?? null,
|
|
18497
|
+
dataDir: input.dataDir ?? null,
|
|
18498
|
+
open: false
|
|
18499
|
+
};
|
|
18500
|
+
}
|
|
18501
|
+
function lifecycleRoot(deps) {
|
|
18502
|
+
return resolve(deps.root ?? getInstancesRoot());
|
|
18503
|
+
}
|
|
18504
|
+
function normalizedServiceName(rawName) {
|
|
18505
|
+
const validated = validateInstanceName(rawName);
|
|
18506
|
+
return validated.kind === "error" ? fromRegistry(validated) : success(validated.value);
|
|
18507
|
+
}
|
|
18508
|
+
async function startNamedInstance(rawName, input = {}, deps = {}) {
|
|
18509
|
+
const name2 = normalizedServiceName(rawName);
|
|
18510
|
+
if (name2.kind === "error") return name2;
|
|
18511
|
+
return startAction(
|
|
18512
|
+
lifecycleRoot(deps),
|
|
18513
|
+
{ kind: "start", name: name2.value, overrides: requestActionOverrides(input), json: true },
|
|
18514
|
+
deps
|
|
18515
|
+
);
|
|
18516
|
+
}
|
|
18517
|
+
async function restartNamedInstance(rawName, input = {}, deps = {}) {
|
|
18518
|
+
const name2 = normalizedServiceName(rawName);
|
|
18519
|
+
if (name2.kind === "error") return name2;
|
|
18520
|
+
return restartAction(
|
|
18521
|
+
lifecycleRoot(deps),
|
|
18522
|
+
{ kind: "restart", name: name2.value, overrides: requestActionOverrides(input), json: true },
|
|
18523
|
+
deps
|
|
18524
|
+
);
|
|
18525
|
+
}
|
|
18526
|
+
async function stopNamedInstance(rawName, deps = {}) {
|
|
18527
|
+
const name2 = normalizedServiceName(rawName);
|
|
18528
|
+
if (name2.kind === "error") return name2;
|
|
18529
|
+
return stopAction(lifecycleRoot(deps), { kind: "stop", name: name2.value, json: true }, deps);
|
|
18530
|
+
}
|
|
18531
|
+
async function getNamedInstance(rawName, deps = {}) {
|
|
18532
|
+
const name2 = normalizedServiceName(rawName);
|
|
18533
|
+
if (name2.kind === "error") return name2;
|
|
18534
|
+
return statusAction(lifecycleRoot(deps), name2.value, deps);
|
|
18535
|
+
}
|
|
18536
|
+
async function getNamedInstanceConnection(rawName, deps = {}) {
|
|
18537
|
+
const instance = await getNamedInstance(rawName, deps);
|
|
18538
|
+
return instance.kind === "error" ? instance : success(connectionOutput(instance.value));
|
|
18539
|
+
}
|
|
18540
|
+
async function listNamedInstances(deps = {}) {
|
|
18541
|
+
return listAction(lifecycleRoot(deps), deps);
|
|
18542
|
+
}
|
|
18543
|
+
const NO_STORE_HEADERS$1 = { "cache-control": "no-store" };
|
|
18544
|
+
function errorStatus(code) {
|
|
18545
|
+
switch (code) {
|
|
18546
|
+
case "argument_invalid":
|
|
18547
|
+
case "data_dir_invalid":
|
|
18548
|
+
case "name_invalid":
|
|
18549
|
+
case "port_invalid":
|
|
18550
|
+
case "record_invalid":
|
|
18551
|
+
return 400;
|
|
18552
|
+
case "instance_not_found":
|
|
18553
|
+
return 404;
|
|
18554
|
+
case "config_mismatch":
|
|
18555
|
+
case "data_dir_conflict":
|
|
18556
|
+
case "identity_mismatch":
|
|
18557
|
+
case "port_conflict":
|
|
18558
|
+
case "self_lifecycle_unsupported":
|
|
18559
|
+
return 409;
|
|
18560
|
+
case "lock_not_owned":
|
|
18561
|
+
case "lock_timeout":
|
|
18562
|
+
return 423;
|
|
18563
|
+
case "launch_failed":
|
|
18564
|
+
case "launch_unavailable":
|
|
18565
|
+
case "port_exhausted":
|
|
18566
|
+
case "registry_io":
|
|
18567
|
+
case "restart_failed_degraded":
|
|
18568
|
+
case "restart_failed_rolled_back":
|
|
18569
|
+
case "shutdown_failed":
|
|
18570
|
+
return 503;
|
|
18571
|
+
}
|
|
18572
|
+
}
|
|
18573
|
+
function instanceErrorResponse(error) {
|
|
18574
|
+
return Response.json(instanceErrorOutput(error), {
|
|
18575
|
+
status: errorStatus(error.code),
|
|
18576
|
+
headers: NO_STORE_HEADERS$1
|
|
18577
|
+
});
|
|
18578
|
+
}
|
|
18579
|
+
function instanceResultResponse(result, successStatus = 200) {
|
|
18580
|
+
return result.kind === "error" ? instanceErrorResponse(result.error) : Response.json(result.value, { status: successStatus, headers: NO_STORE_HEADERS$1 });
|
|
18581
|
+
}
|
|
18582
|
+
async function parseInstanceBody(request, schema) {
|
|
18583
|
+
let value;
|
|
18584
|
+
try {
|
|
18585
|
+
const text = await request.text();
|
|
18586
|
+
value = text.trim().length === 0 ? {} : JSON.parse(text);
|
|
18587
|
+
} catch {
|
|
18588
|
+
return {
|
|
18589
|
+
ok: false,
|
|
18590
|
+
response: instanceErrorResponse({
|
|
18591
|
+
code: "argument_invalid",
|
|
18592
|
+
message: "Request body must be valid JSON.",
|
|
18593
|
+
retryable: false
|
|
18594
|
+
})
|
|
18595
|
+
};
|
|
18596
|
+
}
|
|
18597
|
+
const parsed = schema.safeParse(value);
|
|
18598
|
+
if (!parsed.success) {
|
|
18599
|
+
return {
|
|
18600
|
+
ok: false,
|
|
18601
|
+
response: instanceErrorResponse({
|
|
18602
|
+
code: "argument_invalid",
|
|
18603
|
+
message: `Invalid instance request: ${parsed.error.issues[0]?.message ?? "invalid body"}`,
|
|
18604
|
+
retryable: false
|
|
18605
|
+
})
|
|
18606
|
+
};
|
|
18607
|
+
}
|
|
18608
|
+
return { ok: true, value: parsed.data };
|
|
18609
|
+
}
|
|
18610
|
+
function selfLifecycleError(rawName) {
|
|
18611
|
+
const current = readManagedInstanceLaunch(process.env);
|
|
18612
|
+
return current.kind === "managed" && current.launch.name.toLowerCase() === rawName.toLowerCase() ? {
|
|
18613
|
+
code: "self_lifecycle_unsupported",
|
|
18614
|
+
message: "A managed runtime cannot stop or restart itself through its own REST/MCP transport; use the CLI or a different controller instance.",
|
|
18615
|
+
retryable: false
|
|
18616
|
+
} : null;
|
|
18617
|
+
}
|
|
18618
|
+
function instanceListResponse(instances) {
|
|
18619
|
+
return Response.json(
|
|
18620
|
+
{ schemaVersion: INSTANCE_API_SCHEMA_VERSION, instances },
|
|
18621
|
+
{ headers: NO_STORE_HEADERS$1 }
|
|
18622
|
+
);
|
|
18623
|
+
}
|
|
18624
|
+
const Route$H = createFileRoute("/api/instances")({
|
|
18625
|
+
server: {
|
|
18626
|
+
handlers: {
|
|
18627
|
+
GET: async () => {
|
|
18628
|
+
const result = await listNamedInstances();
|
|
18629
|
+
return result.kind === "error" ? instanceResultResponse(result) : instanceListResponse(result.value);
|
|
18630
|
+
}
|
|
18631
|
+
}
|
|
18632
|
+
}
|
|
18633
|
+
});
|
|
18634
|
+
function createShutdownCoordinator(dependencies, deadlineMs) {
|
|
18635
|
+
let shutdownPromise = null;
|
|
18636
|
+
async function shutdown(_reason = "manual") {
|
|
18637
|
+
if (shutdownPromise !== null) return await shutdownPromise;
|
|
18638
|
+
dependencies.markShuttingDown();
|
|
18639
|
+
dependencies.stopAdmission();
|
|
18640
|
+
let forced = false;
|
|
18641
|
+
let forceClosed = false;
|
|
18642
|
+
let deadline = null;
|
|
18643
|
+
function forceCloseOnce() {
|
|
18644
|
+
if (forceClosed) return;
|
|
18645
|
+
forceClosed = true;
|
|
18646
|
+
dependencies.forceClose();
|
|
18647
|
+
}
|
|
18648
|
+
async function gracefulShutdown() {
|
|
18649
|
+
try {
|
|
18650
|
+
await dependencies.drainTraffic();
|
|
18651
|
+
if (forced) return { forced: true, reason: "deadline" };
|
|
18652
|
+
await dependencies.drainFinalizers();
|
|
18653
|
+
if (forced) return { forced: true, reason: "deadline" };
|
|
18654
|
+
await dependencies.flushPersistence();
|
|
18655
|
+
if (forced) return { forced: true, reason: "deadline" };
|
|
18656
|
+
await dependencies.closeResources();
|
|
18657
|
+
return { forced: false, reason: "completed" };
|
|
18658
|
+
} catch {
|
|
18659
|
+
forceCloseOnce();
|
|
18660
|
+
return { forced: true, reason: "error" };
|
|
18661
|
+
}
|
|
18662
|
+
}
|
|
18663
|
+
const timedOut = new Promise((resolve2) => {
|
|
18664
|
+
deadline = setTimeout(
|
|
18665
|
+
() => {
|
|
18666
|
+
forced = true;
|
|
18667
|
+
forceCloseOnce();
|
|
18668
|
+
resolve2({ forced: true, reason: "deadline" });
|
|
18669
|
+
},
|
|
18670
|
+
Math.max(1, deadlineMs)
|
|
18671
|
+
);
|
|
18672
|
+
});
|
|
18673
|
+
shutdownPromise = Promise.race([gracefulShutdown(), timedOut]).finally(() => {
|
|
18674
|
+
if (deadline !== null) clearTimeout(deadline);
|
|
18675
|
+
});
|
|
18676
|
+
return await shutdownPromise;
|
|
18677
|
+
}
|
|
18678
|
+
return { shutdown };
|
|
18679
|
+
}
|
|
18680
|
+
function createShutdownSignalHandler(coordinator, onComplete) {
|
|
18681
|
+
let handled = false;
|
|
18682
|
+
return (signal) => {
|
|
18683
|
+
if (handled) return;
|
|
18684
|
+
handled = true;
|
|
18685
|
+
void coordinator.shutdown(signal).then(onComplete);
|
|
18686
|
+
};
|
|
18687
|
+
}
|
|
18688
|
+
const DEFAULT_SHUTDOWN_DEADLINE_MS = 15e3;
|
|
18689
|
+
function shutdownDeadlineMs() {
|
|
18690
|
+
const raw = process.env["AGENT_INSPECTOR_SHUTDOWN_TIMEOUT_MS"];
|
|
18691
|
+
if (raw === void 0 || raw === "") return DEFAULT_SHUTDOWN_DEADLINE_MS;
|
|
18692
|
+
const parsed = Number(raw);
|
|
18693
|
+
return Number.isSafeInteger(parsed) && parsed > 0 && parsed <= 3e5 ? parsed : DEFAULT_SHUTDOWN_DEADLINE_MS;
|
|
18694
|
+
}
|
|
18695
|
+
async function drainFinalizers() {
|
|
18696
|
+
await flushAllRawStreamCaptures();
|
|
18697
|
+
await flushSessionTasks();
|
|
18698
|
+
await flushToolSchemaWarningAnalysis();
|
|
18699
|
+
}
|
|
18700
|
+
async function flushPersistence() {
|
|
18701
|
+
await flushLogBuffer();
|
|
18702
|
+
await flushSessionArchiveWrites();
|
|
18703
|
+
await flushIndex();
|
|
18704
|
+
await flushApplicationLog();
|
|
18705
|
+
}
|
|
18706
|
+
async function closeResources() {
|
|
18707
|
+
await closeLogFinalizerWorkers();
|
|
18708
|
+
destroyAllSessionProcesses();
|
|
18709
|
+
closeSqliteLogIndex();
|
|
18710
|
+
await flushApplicationLog();
|
|
18711
|
+
}
|
|
18712
|
+
function forceCloseResources() {
|
|
18713
|
+
forceCloseLogFinalizerWorkers();
|
|
18714
|
+
destroyAllSessionProcesses();
|
|
18715
|
+
closeSqliteLogIndex();
|
|
18716
|
+
}
|
|
18717
|
+
const runtimeShutdownCoordinator = createShutdownCoordinator(
|
|
18718
|
+
{
|
|
18719
|
+
markShuttingDown: () => setRuntimeShuttingDown(true),
|
|
18720
|
+
stopAdmission: stopRuntimeAdmission,
|
|
18721
|
+
drainTraffic: waitForRuntimeTrafficDrain,
|
|
16894
18722
|
drainFinalizers,
|
|
16895
18723
|
flushPersistence,
|
|
16896
18724
|
closeResources,
|
|
@@ -16899,7 +18727,50 @@ const runtimeShutdownCoordinator = createShutdownCoordinator(
|
|
|
16899
18727
|
shutdownDeadlineMs()
|
|
16900
18728
|
);
|
|
16901
18729
|
let signalHandlersInstalled = false;
|
|
18730
|
+
let managedInstanceHandlersInstalled = false;
|
|
18731
|
+
function managedControlTokensMatch(received, expected) {
|
|
18732
|
+
if (received.length === 0 || expected.length === 0) return false;
|
|
18733
|
+
const receivedDigest = createHash("sha256").update(received).digest();
|
|
18734
|
+
const expectedDigest = createHash("sha256").update(expected).digest();
|
|
18735
|
+
return timingSafeEqual(receivedDigest, expectedDigest);
|
|
18736
|
+
}
|
|
18737
|
+
function createManagedRuntimeShutdownController(options) {
|
|
18738
|
+
let shutdownRequested = false;
|
|
18739
|
+
const requestShutdown = () => {
|
|
18740
|
+
if (shutdownRequested) return;
|
|
18741
|
+
shutdownRequested = true;
|
|
18742
|
+
void options.shutdown().then(
|
|
18743
|
+
(result) => options.exit(result.forced ? 1 : 0),
|
|
18744
|
+
() => options.exit(1)
|
|
18745
|
+
);
|
|
18746
|
+
};
|
|
18747
|
+
return {
|
|
18748
|
+
handleDisconnect: requestShutdown,
|
|
18749
|
+
handleMessage: (message) => {
|
|
18750
|
+
const parsed = parseManagedInstanceShutdownMessage(message);
|
|
18751
|
+
if (parsed === null || !managedControlTokensMatch(parsed.controlToken, options.controlToken)) {
|
|
18752
|
+
return;
|
|
18753
|
+
}
|
|
18754
|
+
requestShutdown();
|
|
18755
|
+
}
|
|
18756
|
+
};
|
|
18757
|
+
}
|
|
18758
|
+
function installRuntimeManagedInstanceShutdownHandlers() {
|
|
18759
|
+
if (managedInstanceHandlersInstalled) return;
|
|
18760
|
+
const launchResult = readManagedInstanceLaunch(process.env);
|
|
18761
|
+
if (launchResult.kind !== "managed") return;
|
|
18762
|
+
managedInstanceHandlersInstalled = true;
|
|
18763
|
+
const controller = createManagedRuntimeShutdownController({
|
|
18764
|
+
controlToken: launchResult.launch.controlToken,
|
|
18765
|
+
shutdown: async () => await runtimeShutdownCoordinator.shutdown("manual"),
|
|
18766
|
+
exit: (code) => process.exit(code)
|
|
18767
|
+
});
|
|
18768
|
+
process.on("message", controller.handleMessage);
|
|
18769
|
+
process.once("disconnect", controller.handleDisconnect);
|
|
18770
|
+
if (process.connected === false) process.nextTick(controller.handleDisconnect);
|
|
18771
|
+
}
|
|
16902
18772
|
function installRuntimeShutdownSignalHandlers() {
|
|
18773
|
+
installRuntimeManagedInstanceShutdownHandlers();
|
|
16903
18774
|
if (signalHandlersInstalled) return;
|
|
16904
18775
|
signalHandlersInstalled = true;
|
|
16905
18776
|
const handleSignal = createShutdownSignalHandler(runtimeShutdownCoordinator, (result) => {
|
|
@@ -16910,7 +18781,7 @@ function installRuntimeShutdownSignalHandlers() {
|
|
|
16910
18781
|
}
|
|
16911
18782
|
installRuntimeShutdownSignalHandlers();
|
|
16912
18783
|
logger.debug("Health endpoint loaded");
|
|
16913
|
-
const Route$
|
|
18784
|
+
const Route$G = createFileRoute("/api/health")({
|
|
16914
18785
|
server: {
|
|
16915
18786
|
handlers: {
|
|
16916
18787
|
GET: async () => {
|
|
@@ -16951,7 +18822,7 @@ function readDeleteEvidenceFlag$1(request) {
|
|
|
16951
18822
|
return { ok: false };
|
|
16952
18823
|
}
|
|
16953
18824
|
}
|
|
16954
|
-
const Route$
|
|
18825
|
+
const Route$F = createFileRoute("/api/groups")({
|
|
16955
18826
|
server: {
|
|
16956
18827
|
handlers: {
|
|
16957
18828
|
GET: () => Response.json(listInspectorGroups()),
|
|
@@ -16995,7 +18866,7 @@ const RuntimeConfigPatchSchema = object({
|
|
|
16995
18866
|
}).strict().refine((v) => Object.keys(v).length > 0, {
|
|
16996
18867
|
message: "At least one field must be provided"
|
|
16997
18868
|
});
|
|
16998
|
-
const Route$
|
|
18869
|
+
const Route$E = createFileRoute("/api/config")({
|
|
16999
18870
|
server: {
|
|
17000
18871
|
handlers: {
|
|
17001
18872
|
GET: () => {
|
|
@@ -17090,23 +18961,23 @@ function listInspectorAlerts(options) {
|
|
|
17090
18961
|
function getInspectorAlertSummary(options) {
|
|
17091
18962
|
return getAlertSummary(options);
|
|
17092
18963
|
}
|
|
17093
|
-
function parseFilters(
|
|
17094
|
-
const severityResult = AlertSeveritySchema.safeParse(
|
|
17095
|
-
const categoryResult = AlertCategorySchema.safeParse(
|
|
17096
|
-
const sourceResult = AlertSourceSchema.safeParse(
|
|
18964
|
+
function parseFilters(url2) {
|
|
18965
|
+
const severityResult = AlertSeveritySchema.safeParse(url2.searchParams.get("severity"));
|
|
18966
|
+
const categoryResult = AlertCategorySchema.safeParse(url2.searchParams.get("category"));
|
|
18967
|
+
const sourceResult = AlertSourceSchema.safeParse(url2.searchParams.get("source"));
|
|
17097
18968
|
return {
|
|
17098
18969
|
severity: severityResult.success ? severityResult.data : void 0,
|
|
17099
18970
|
category: categoryResult.success ? categoryResult.data : void 0,
|
|
17100
18971
|
source: sourceResult.success ? sourceResult.data : void 0
|
|
17101
18972
|
};
|
|
17102
18973
|
}
|
|
17103
|
-
const Route$
|
|
18974
|
+
const Route$D = createFileRoute("/api/alerts")({
|
|
17104
18975
|
server: {
|
|
17105
18976
|
handlers: {
|
|
17106
18977
|
GET: async ({ request }) => {
|
|
17107
|
-
const
|
|
18978
|
+
const url2 = new URL(request.url);
|
|
17108
18979
|
const limitResult = parseBoundedIntegerParameter({
|
|
17109
|
-
value:
|
|
18980
|
+
value: url2.searchParams.get("limit"),
|
|
17110
18981
|
parameter: "limit",
|
|
17111
18982
|
fallback: DEFAULT_ALERT_LIMIT,
|
|
17112
18983
|
minimum: 1,
|
|
@@ -17115,7 +18986,7 @@ const Route$y = createFileRoute("/api/alerts")({
|
|
|
17115
18986
|
});
|
|
17116
18987
|
if (!limitResult.ok) return limitResult.response;
|
|
17117
18988
|
const scanLimitResult = parseBoundedIntegerParameter({
|
|
17118
|
-
value:
|
|
18989
|
+
value: url2.searchParams.get("scanLimit"),
|
|
17119
18990
|
parameter: "scanLimit",
|
|
17120
18991
|
fallback: DEFAULT_ALERT_SCAN_LIMIT,
|
|
17121
18992
|
minimum: 1,
|
|
@@ -17125,7 +18996,7 @@ const Route$y = createFileRoute("/api/alerts")({
|
|
|
17125
18996
|
if (!scanLimitResult.ok) return scanLimitResult.response;
|
|
17126
18997
|
return Response.json(
|
|
17127
18998
|
await listInspectorAlerts({
|
|
17128
|
-
...parseFilters(
|
|
18999
|
+
...parseFilters(url2),
|
|
17129
19000
|
limit: limitResult.value,
|
|
17130
19001
|
scanLimit: scanLimitResult.value
|
|
17131
19002
|
})
|
|
@@ -17143,7 +19014,7 @@ async function readJsonBody$4(request) {
|
|
|
17143
19014
|
return { ok: false };
|
|
17144
19015
|
}
|
|
17145
19016
|
}
|
|
17146
|
-
const Route$
|
|
19017
|
+
const Route$C = createFileRoute("/api/runs/$runId")({
|
|
17147
19018
|
server: {
|
|
17148
19019
|
handlers: {
|
|
17149
19020
|
GET: ({ params }) => {
|
|
@@ -17228,9 +19099,9 @@ const KNOWN_PROVIDER_NAMES = {
|
|
|
17228
19099
|
lingyiwanwu: "LingyiWanwu",
|
|
17229
19100
|
stepfun: "StepFun"
|
|
17230
19101
|
};
|
|
17231
|
-
function deriveNameFromUrl(
|
|
19102
|
+
function deriveNameFromUrl(url2) {
|
|
17232
19103
|
try {
|
|
17233
|
-
const hostname = new URL(
|
|
19104
|
+
const hostname = new URL(url2).hostname;
|
|
17234
19105
|
const parts = hostname.split(".");
|
|
17235
19106
|
const domain = parts.length >= 2 ? parts[parts.length - 2] ?? hostname : hostname;
|
|
17236
19107
|
const domainLower = domain.toLowerCase();
|
|
@@ -17599,8 +19470,8 @@ function scanExternalProviders() {
|
|
|
17599
19470
|
}
|
|
17600
19471
|
const allProviders = [...claudeProviders, ...opencodeProviders, ...mimoProviders];
|
|
17601
19472
|
const filteredProviders = allProviders.filter((p) => {
|
|
17602
|
-
const
|
|
17603
|
-
return !/(?:localhost|127\.0\.0\.1)/.test(
|
|
19473
|
+
const url2 = p.anthropicBaseUrl || p.openaiBaseUrl || p.openaiResponsesBaseUrl;
|
|
19474
|
+
return !/(?:localhost|127\.0\.0\.1)/.test(url2);
|
|
17604
19475
|
});
|
|
17605
19476
|
let existing = [];
|
|
17606
19477
|
try {
|
|
@@ -17676,7 +19547,7 @@ function consumeProviderScan(scanId, selectionIds, now = Date.now()) {
|
|
|
17676
19547
|
}
|
|
17677
19548
|
return selected;
|
|
17678
19549
|
}
|
|
17679
|
-
const Route$
|
|
19550
|
+
const Route$B = createFileRoute("/api/providers/scan")({
|
|
17680
19551
|
server: {
|
|
17681
19552
|
handlers: {
|
|
17682
19553
|
POST: () => {
|
|
@@ -17722,7 +19593,7 @@ const ProviderScanImportRequestSchema = object({
|
|
|
17722
19593
|
selectionIds: array(string().uuid()).min(1).max(100)
|
|
17723
19594
|
});
|
|
17724
19595
|
const ProviderFileImportSchema = union([string(), object({ providers: unknown() })]);
|
|
17725
|
-
const Route$
|
|
19596
|
+
const Route$A = createFileRoute("/api/providers/import")({
|
|
17726
19597
|
server: {
|
|
17727
19598
|
handlers: {
|
|
17728
19599
|
POST: async ({ request }) => {
|
|
@@ -17794,7 +19665,7 @@ function exportResponse(json, filename) {
|
|
|
17794
19665
|
}
|
|
17795
19666
|
});
|
|
17796
19667
|
}
|
|
17797
|
-
const Route$
|
|
19668
|
+
const Route$z = createFileRoute("/api/providers/export")({
|
|
17798
19669
|
server: {
|
|
17799
19670
|
handlers: {
|
|
17800
19671
|
GET: () => {
|
|
@@ -17837,7 +19708,7 @@ const ProviderUpdateSchema = object({
|
|
|
17837
19708
|
modelMetadata: ProviderModelMetadataListSchema.optional(),
|
|
17838
19709
|
source: _enum(["company", "personal"]).optional()
|
|
17839
19710
|
});
|
|
17840
|
-
const Route$
|
|
19711
|
+
const Route$y = createFileRoute("/api/providers/$providerId")({
|
|
17841
19712
|
server: {
|
|
17842
19713
|
handlers: {
|
|
17843
19714
|
GET: ({ params }) => {
|
|
@@ -17905,8 +19776,8 @@ const Route$t = createFileRoute("/api/providers/$providerId")({
|
|
|
17905
19776
|
const INITIAL_STREAM_LOG_LIMIT = 100;
|
|
17906
19777
|
const UPDATE_COALESCE_MS = 50;
|
|
17907
19778
|
const sseClientLimiter = createCapacityLimiter(MAX_SSE_CLIENTS);
|
|
17908
|
-
function streamIncludesBodies(
|
|
17909
|
-
return
|
|
19779
|
+
function streamIncludesBodies(url2) {
|
|
19780
|
+
return url2.searchParams.get("compact") === "0";
|
|
17910
19781
|
}
|
|
17911
19782
|
function parseLastEventLogId(value) {
|
|
17912
19783
|
if (value === null || !/^\d+$/.test(value)) return void 0;
|
|
@@ -17933,10 +19804,10 @@ function createLogsStreamResponse(request, clientLimiter = sseClientLimiter) {
|
|
|
17933
19804
|
retryAfterSeconds: 1
|
|
17934
19805
|
});
|
|
17935
19806
|
}
|
|
17936
|
-
const
|
|
17937
|
-
const sessionId =
|
|
17938
|
-
const model =
|
|
17939
|
-
const includeBodies = streamIncludesBodies(
|
|
19807
|
+
const url2 = new URL(request.url);
|
|
19808
|
+
const sessionId = url2.searchParams.get("sessionId") ?? void 0;
|
|
19809
|
+
const model = url2.searchParams.get("model") ?? void 0;
|
|
19810
|
+
const includeBodies = streamIncludesBodies(url2);
|
|
17940
19811
|
const lastEventLogId = parseLastEventLogId(request.headers.get("last-event-id"));
|
|
17941
19812
|
let controllerRef = null;
|
|
17942
19813
|
let cleanedUp = false;
|
|
@@ -18024,7 +19895,7 @@ function createLogsStreamResponse(request, clientLimiter = sseClientLimiter) {
|
|
|
18024
19895
|
}
|
|
18025
19896
|
});
|
|
18026
19897
|
}
|
|
18027
|
-
const Route$
|
|
19898
|
+
const Route$x = createFileRoute("/api/logs/stream")({
|
|
18028
19899
|
server: {
|
|
18029
19900
|
handlers: {
|
|
18030
19901
|
GET: ({ request }) => createLogsStreamResponse(request)
|
|
@@ -18714,7 +20585,7 @@ async function readBoundedLogImportUpload(request, options = {}) {
|
|
|
18714
20585
|
data: new Uint8Array(await upload.arrayBuffer())
|
|
18715
20586
|
};
|
|
18716
20587
|
}
|
|
18717
|
-
const Route$
|
|
20588
|
+
const Route$w = createFileRoute("/api/logs/import")({
|
|
18718
20589
|
server: {
|
|
18719
20590
|
handlers: {
|
|
18720
20591
|
POST: async ({ request }) => {
|
|
@@ -18745,7 +20616,7 @@ const Route$r = createFileRoute("/api/logs/import")({
|
|
|
18745
20616
|
}
|
|
18746
20617
|
}
|
|
18747
20618
|
});
|
|
18748
|
-
const Route$
|
|
20619
|
+
const Route$v = createFileRoute("/api/logs/$id")({
|
|
18749
20620
|
server: {
|
|
18750
20621
|
handlers: {
|
|
18751
20622
|
GET: async ({ params }) => {
|
|
@@ -19128,20 +20999,20 @@ const OpenClawWriteResponseSchema = object({
|
|
|
19128
20999
|
memoryId: string().optional()
|
|
19129
21000
|
});
|
|
19130
21001
|
function getLegacyEndpoint() {
|
|
19131
|
-
const
|
|
19132
|
-
if (
|
|
19133
|
-
return
|
|
21002
|
+
const endpoint2 = process.env["OPENCLAW_MEMORY_URL"] ?? process.env["OPENCLAW_API_URL"];
|
|
21003
|
+
if (endpoint2 === void 0 || endpoint2.trim() === "") return null;
|
|
21004
|
+
return endpoint2.trim();
|
|
19134
21005
|
}
|
|
19135
|
-
function buildMemoryUrl(
|
|
19136
|
-
return
|
|
21006
|
+
function buildMemoryUrl(endpoint2) {
|
|
21007
|
+
return endpoint2.endsWith("/memories") ? endpoint2 : `${endpoint2.replace(/\/$/, "")}/memories`;
|
|
19137
21008
|
}
|
|
19138
|
-
function buildSearchUrl(
|
|
19139
|
-
const
|
|
19140
|
-
|
|
21009
|
+
function buildSearchUrl(endpoint2, query, project) {
|
|
21010
|
+
const url2 = new URL(`${endpoint2.replace(/\/$/, "")}/search`);
|
|
21011
|
+
url2.searchParams.set("q", query);
|
|
19141
21012
|
if (project !== void 0 && project !== "") {
|
|
19142
|
-
|
|
21013
|
+
url2.searchParams.set("project", project);
|
|
19143
21014
|
}
|
|
19144
|
-
return
|
|
21015
|
+
return url2.toString();
|
|
19145
21016
|
}
|
|
19146
21017
|
function authHeaders() {
|
|
19147
21018
|
const headers = new Headers({ "content-type": "application/json" });
|
|
@@ -19172,15 +21043,15 @@ async function promoteToOpenClaw(candidate) {
|
|
|
19172
21043
|
}
|
|
19173
21044
|
return { success: false, error: result.error };
|
|
19174
21045
|
}
|
|
19175
|
-
const
|
|
19176
|
-
if (
|
|
21046
|
+
const endpoint2 = getLegacyEndpoint();
|
|
21047
|
+
if (endpoint2 === null) {
|
|
19177
21048
|
return {
|
|
19178
21049
|
success: false,
|
|
19179
21050
|
error: "OpenClaw memory backend is not configured. Set OPENCLAW_WORKSPACE_DIR for file-backed memory or OPENCLAW_MEMORY_URL for a legacy HTTP backend."
|
|
19180
21051
|
};
|
|
19181
21052
|
}
|
|
19182
21053
|
const fetchResult = await safeFetch(
|
|
19183
|
-
buildMemoryUrl(
|
|
21054
|
+
buildMemoryUrl(endpoint2),
|
|
19184
21055
|
{
|
|
19185
21056
|
method: "POST",
|
|
19186
21057
|
headers: authHeaders(),
|
|
@@ -19204,8 +21075,8 @@ async function searchOpenClaw(query, project) {
|
|
|
19204
21075
|
if (isOpenClawGatewayConfigured()) {
|
|
19205
21076
|
return await searchOpenClawGateway(query, project);
|
|
19206
21077
|
}
|
|
19207
|
-
const
|
|
19208
|
-
if (
|
|
21078
|
+
const endpoint2 = getLegacyEndpoint();
|
|
21079
|
+
if (endpoint2 === null) {
|
|
19209
21080
|
const workspaceHint = isOpenClawFileBridgeConfigured() ? " OPENCLAW_WORKSPACE_DIR is configured for writes, but search requires OPENCLAW_GATEWAY_URL." : "";
|
|
19210
21081
|
return {
|
|
19211
21082
|
results: [],
|
|
@@ -19213,7 +21084,7 @@ async function searchOpenClaw(query, project) {
|
|
|
19213
21084
|
};
|
|
19214
21085
|
}
|
|
19215
21086
|
const fetchResult = await safeFetch(
|
|
19216
|
-
buildSearchUrl(
|
|
21087
|
+
buildSearchUrl(endpoint2, query, project),
|
|
19217
21088
|
{
|
|
19218
21089
|
method: "GET",
|
|
19219
21090
|
headers: authHeaders()
|
|
@@ -19255,13 +21126,13 @@ function bodySearchQuery(value) {
|
|
|
19255
21126
|
const query = descriptor.value;
|
|
19256
21127
|
return typeof query === "string" ? query : null;
|
|
19257
21128
|
}
|
|
19258
|
-
const Route$
|
|
21129
|
+
const Route$u = createFileRoute("/api/knowledge/search")({
|
|
19259
21130
|
server: {
|
|
19260
21131
|
handlers: {
|
|
19261
21132
|
GET: async ({ request }) => {
|
|
19262
|
-
const
|
|
19263
|
-
const query =
|
|
19264
|
-
const project =
|
|
21133
|
+
const url2 = new URL(request.url);
|
|
21134
|
+
const query = url2.searchParams.get("q") ?? "";
|
|
21135
|
+
const project = url2.searchParams.get("project") ?? void 0;
|
|
19265
21136
|
if (query === "") {
|
|
19266
21137
|
return Response.json({ error: "Missing q search parameter" }, { status: 400 });
|
|
19267
21138
|
}
|
|
@@ -19288,12 +21159,12 @@ const Route$p = createFileRoute("/api/knowledge/search")({
|
|
|
19288
21159
|
}
|
|
19289
21160
|
}
|
|
19290
21161
|
});
|
|
19291
|
-
const Route$
|
|
21162
|
+
const Route$t = createFileRoute("/api/knowledge/project-context")({
|
|
19292
21163
|
server: {
|
|
19293
21164
|
handlers: {
|
|
19294
21165
|
GET: async ({ request }) => {
|
|
19295
|
-
const
|
|
19296
|
-
const project =
|
|
21166
|
+
const url2 = new URL(request.url);
|
|
21167
|
+
const project = url2.searchParams.get("project") ?? "";
|
|
19297
21168
|
if (project === "") {
|
|
19298
21169
|
return Response.json({ error: "Missing project search parameter" }, { status: 400 });
|
|
19299
21170
|
}
|
|
@@ -19313,13 +21184,20 @@ const Route$o = createFileRoute("/api/knowledge/project-context")({
|
|
|
19313
21184
|
}
|
|
19314
21185
|
}
|
|
19315
21186
|
});
|
|
19316
|
-
const Route$
|
|
21187
|
+
const Route$s = createFileRoute("/api/knowledge/candidates")({
|
|
19317
21188
|
server: {
|
|
19318
21189
|
handlers: {
|
|
19319
21190
|
GET: () => Response.json({ candidates: listCandidates() })
|
|
19320
21191
|
}
|
|
19321
21192
|
}
|
|
19322
21193
|
});
|
|
21194
|
+
const Route$r = createFileRoute("/api/instances/$name")({
|
|
21195
|
+
server: {
|
|
21196
|
+
handlers: {
|
|
21197
|
+
GET: async ({ params }) => instanceResultResponse(await getNamedInstance(params.name))
|
|
21198
|
+
}
|
|
21199
|
+
}
|
|
21200
|
+
});
|
|
19323
21201
|
async function readJsonBody$3(request) {
|
|
19324
21202
|
try {
|
|
19325
21203
|
const raw = await request.text();
|
|
@@ -19348,7 +21226,7 @@ function readDeleteEvidenceFlag(request) {
|
|
|
19348
21226
|
return { ok: false };
|
|
19349
21227
|
}
|
|
19350
21228
|
}
|
|
19351
|
-
const Route$
|
|
21229
|
+
const Route$q = createFileRoute("/api/groups/$groupId")({
|
|
19352
21230
|
server: {
|
|
19353
21231
|
handlers: {
|
|
19354
21232
|
GET: ({ params }) => {
|
|
@@ -19395,16 +21273,6 @@ const Route$m = createFileRoute("/api/groups/$groupId")({
|
|
|
19395
21273
|
}
|
|
19396
21274
|
}
|
|
19397
21275
|
});
|
|
19398
|
-
function platformCommand(command, args, platform = process.platform) {
|
|
19399
|
-
if (platform !== "win32") return { command, args: [...args] };
|
|
19400
|
-
return { command: "cmd.exe", args: ["/d", "/s", "/c", command, ...args] };
|
|
19401
|
-
}
|
|
19402
|
-
function npmCommand(platform = process.platform) {
|
|
19403
|
-
return platform === "win32" ? "npm.cmd" : "npm";
|
|
19404
|
-
}
|
|
19405
|
-
function npmSpawnCommand(args, platform = process.platform) {
|
|
19406
|
-
return platformCommand(npmCommand(platform), args, platform);
|
|
19407
|
-
}
|
|
19408
21276
|
const ECOSYSTEM_PACKAGES = [
|
|
19409
21277
|
{
|
|
19410
21278
|
id: "inspector",
|
|
@@ -19886,7 +21754,7 @@ ${error.message}
|
|
|
19886
21754
|
});
|
|
19887
21755
|
return task;
|
|
19888
21756
|
}
|
|
19889
|
-
const Route$
|
|
21757
|
+
const Route$p = createFileRoute("/api/ecosystem/tasks")({
|
|
19890
21758
|
server: {
|
|
19891
21759
|
handlers: {
|
|
19892
21760
|
GET: () => {
|
|
@@ -19895,7 +21763,7 @@ const Route$l = createFileRoute("/api/ecosystem/tasks")({
|
|
|
19895
21763
|
}
|
|
19896
21764
|
}
|
|
19897
21765
|
});
|
|
19898
|
-
const Route$
|
|
21766
|
+
const Route$o = createFileRoute("/api/ecosystem/packages")({
|
|
19899
21767
|
server: {
|
|
19900
21768
|
handlers: {
|
|
19901
21769
|
GET: async () => {
|
|
@@ -19909,7 +21777,7 @@ const Route$k = createFileRoute("/api/ecosystem/packages")({
|
|
|
19909
21777
|
}
|
|
19910
21778
|
}
|
|
19911
21779
|
});
|
|
19912
|
-
const Route$
|
|
21780
|
+
const Route$n = createFileRoute("/api/config/paths")({
|
|
19913
21781
|
server: {
|
|
19914
21782
|
handlers: {
|
|
19915
21783
|
GET: () => {
|
|
@@ -19920,13 +21788,13 @@ const Route$j = createFileRoute("/api/config/paths")({
|
|
|
19920
21788
|
}
|
|
19921
21789
|
}
|
|
19922
21790
|
});
|
|
19923
|
-
const Route$
|
|
21791
|
+
const Route$m = createFileRoute("/api/alerts/summary")({
|
|
19924
21792
|
server: {
|
|
19925
21793
|
handlers: {
|
|
19926
21794
|
GET: async ({ request }) => {
|
|
19927
|
-
const
|
|
21795
|
+
const url2 = new URL(request.url);
|
|
19928
21796
|
const scanLimitResult = parseBoundedIntegerParameter({
|
|
19929
|
-
value:
|
|
21797
|
+
value: url2.searchParams.get("scanLimit"),
|
|
19930
21798
|
parameter: "scanLimit",
|
|
19931
21799
|
fallback: DEFAULT_ALERT_SCAN_LIMIT,
|
|
19932
21800
|
minimum: 1,
|
|
@@ -19952,7 +21820,7 @@ async function readJsonBody$2(request) {
|
|
|
19952
21820
|
return { ok: false };
|
|
19953
21821
|
}
|
|
19954
21822
|
}
|
|
19955
|
-
const Route$
|
|
21823
|
+
const Route$l = createFileRoute("/api/runs/$runId/evidence")({
|
|
19956
21824
|
server: {
|
|
19957
21825
|
handlers: {
|
|
19958
21826
|
GET: ({ params }) => {
|
|
@@ -20699,7 +22567,7 @@ async function testStreamingEndpoint(baseUrl, provider, path2, model, protocol)
|
|
|
20699
22567
|
clearTimeout(timeoutId);
|
|
20700
22568
|
}
|
|
20701
22569
|
}
|
|
20702
|
-
const Route$
|
|
22570
|
+
const Route$k = createFileRoute("/api/providers/$providerId/test")({
|
|
20703
22571
|
server: {
|
|
20704
22572
|
handlers: {
|
|
20705
22573
|
POST: async ({ params }) => {
|
|
@@ -20882,7 +22750,7 @@ async function readRegistryJson(response) {
|
|
|
20882
22750
|
return null;
|
|
20883
22751
|
}
|
|
20884
22752
|
}
|
|
20885
|
-
const Route$
|
|
22753
|
+
const Route$j = createFileRoute("/api/providers/$providerId/model-metadata")({
|
|
20886
22754
|
server: {
|
|
20887
22755
|
handlers: {
|
|
20888
22756
|
POST: async ({ params, request }) => {
|
|
@@ -20997,7 +22865,7 @@ const Route$f = createFileRoute("/api/providers/$providerId/model-metadata")({
|
|
|
20997
22865
|
const ReplayRequestSchema = object({
|
|
20998
22866
|
modifiedBody: string()
|
|
20999
22867
|
});
|
|
21000
|
-
const Route$
|
|
22868
|
+
const Route$i = createFileRoute("/api/logs/$id/replay")({
|
|
21001
22869
|
server: {
|
|
21002
22870
|
handlers: {
|
|
21003
22871
|
POST: async ({ params, request }) => {
|
|
@@ -21209,7 +23077,7 @@ const Route$e = createFileRoute("/api/logs/$id/replay")({
|
|
|
21209
23077
|
}
|
|
21210
23078
|
}
|
|
21211
23079
|
});
|
|
21212
|
-
const Route$
|
|
23080
|
+
const Route$h = createFileRoute("/api/logs/$id/chunks")({
|
|
21213
23081
|
server: {
|
|
21214
23082
|
handlers: {
|
|
21215
23083
|
GET: async ({ params }) => {
|
|
@@ -21316,7 +23184,7 @@ function parseIntegerParam(value, fallback) {
|
|
|
21316
23184
|
if (!Number.isInteger(parsed)) return fallback;
|
|
21317
23185
|
return parsed;
|
|
21318
23186
|
}
|
|
21319
|
-
const Route$
|
|
23187
|
+
const Route$g = createFileRoute("/api/logs/$id/body")({
|
|
21320
23188
|
server: {
|
|
21321
23189
|
handlers: {
|
|
21322
23190
|
GET: async ({ params, request }) => {
|
|
@@ -21324,13 +23192,13 @@ const Route$c = createFileRoute("/api/logs/$id/body")({
|
|
|
21324
23192
|
if (!Number.isInteger(id) || id <= 0) {
|
|
21325
23193
|
return Response.json({ error: "Invalid log ID" }, { status: 400 });
|
|
21326
23194
|
}
|
|
21327
|
-
const
|
|
21328
|
-
const partResult = LogBodyPartSchema.safeParse(
|
|
23195
|
+
const url2 = new URL(request.url);
|
|
23196
|
+
const partResult = LogBodyPartSchema.safeParse(url2.searchParams.get("part"));
|
|
21329
23197
|
if (!partResult.success) {
|
|
21330
23198
|
return Response.json({ error: "Invalid body part" }, { status: 400 });
|
|
21331
23199
|
}
|
|
21332
23200
|
const limitResult = parseBoundedIntegerParameter({
|
|
21333
|
-
value:
|
|
23201
|
+
value: url2.searchParams.get("limit"),
|
|
21334
23202
|
parameter: "limit",
|
|
21335
23203
|
fallback: DEFAULT_LOG_BODY_CHUNK_BYTES,
|
|
21336
23204
|
minimum: 1,
|
|
@@ -21346,7 +23214,7 @@ const Route$c = createFileRoute("/api/logs/$id/body")({
|
|
|
21346
23214
|
createLogBodyChunk({
|
|
21347
23215
|
log,
|
|
21348
23216
|
part: partResult.data,
|
|
21349
|
-
offset: parseIntegerParam(
|
|
23217
|
+
offset: parseIntegerParam(url2.searchParams.get("offset"), 0),
|
|
21350
23218
|
limit: limitResult.value
|
|
21351
23219
|
})
|
|
21352
23220
|
);
|
|
@@ -21363,7 +23231,7 @@ const CandidateUpdateSchema = object({
|
|
|
21363
23231
|
object({
|
|
21364
23232
|
candidate: KnowledgeCandidateSchema
|
|
21365
23233
|
});
|
|
21366
|
-
const Route$
|
|
23234
|
+
const Route$f = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
21367
23235
|
server: {
|
|
21368
23236
|
handlers: {
|
|
21369
23237
|
PATCH: async ({ params, request }) => {
|
|
@@ -21391,6 +23259,52 @@ const Route$b = createFileRoute("/api/knowledge/candidates/$candidateId")({
|
|
|
21391
23259
|
}
|
|
21392
23260
|
}
|
|
21393
23261
|
});
|
|
23262
|
+
const Route$e = createFileRoute("/api/instances/$name/stop")({
|
|
23263
|
+
server: {
|
|
23264
|
+
handlers: {
|
|
23265
|
+
POST: async ({ params, request }) => {
|
|
23266
|
+
const body = await parseInstanceBody(request, InstanceEmptyRequestSchema);
|
|
23267
|
+
if (!body.ok) return body.response;
|
|
23268
|
+
const selfError = selfLifecycleError(params.name);
|
|
23269
|
+
if (selfError !== null) return instanceErrorResponse(selfError);
|
|
23270
|
+
return instanceResultResponse(await stopNamedInstance(params.name));
|
|
23271
|
+
}
|
|
23272
|
+
}
|
|
23273
|
+
}
|
|
23274
|
+
});
|
|
23275
|
+
const Route$d = createFileRoute("/api/instances/$name/start")({
|
|
23276
|
+
server: {
|
|
23277
|
+
handlers: {
|
|
23278
|
+
POST: async ({ params, request }) => {
|
|
23279
|
+
const body = await parseInstanceBody(request, InstanceStartRequestSchema);
|
|
23280
|
+
if (!body.ok) return body.response;
|
|
23281
|
+
const result = await startNamedInstance(params.name, body.value);
|
|
23282
|
+
const status = result.kind === "success" && result.value.outcome === "created" ? 201 : 200;
|
|
23283
|
+
return instanceResultResponse(result, status);
|
|
23284
|
+
}
|
|
23285
|
+
}
|
|
23286
|
+
}
|
|
23287
|
+
});
|
|
23288
|
+
const Route$c = createFileRoute("/api/instances/$name/restart")({
|
|
23289
|
+
server: {
|
|
23290
|
+
handlers: {
|
|
23291
|
+
POST: async ({ params, request }) => {
|
|
23292
|
+
const body = await parseInstanceBody(request, InstanceStartRequestSchema);
|
|
23293
|
+
if (!body.ok) return body.response;
|
|
23294
|
+
const selfError = selfLifecycleError(params.name);
|
|
23295
|
+
if (selfError !== null) return instanceErrorResponse(selfError);
|
|
23296
|
+
return instanceResultResponse(await restartNamedInstance(params.name, body.value));
|
|
23297
|
+
}
|
|
23298
|
+
}
|
|
23299
|
+
}
|
|
23300
|
+
});
|
|
23301
|
+
const Route$b = createFileRoute("/api/instances/$name/connection")({
|
|
23302
|
+
server: {
|
|
23303
|
+
handlers: {
|
|
23304
|
+
GET: async ({ params }) => instanceResultResponse(await getNamedInstanceConnection(params.name))
|
|
23305
|
+
}
|
|
23306
|
+
}
|
|
23307
|
+
});
|
|
21394
23308
|
async function readJsonBody$1(request) {
|
|
21395
23309
|
try {
|
|
21396
23310
|
const raw = await request.text();
|
|
@@ -22271,201 +24185,231 @@ const Route = createFileRoute("/api/ecosystem/packages/$packageId/help")({
|
|
|
22271
24185
|
}
|
|
22272
24186
|
}
|
|
22273
24187
|
});
|
|
22274
|
-
const ReadyzRoute = Route$
|
|
24188
|
+
const ReadyzRoute = Route$T.update({
|
|
22275
24189
|
id: "/readyz",
|
|
22276
24190
|
path: "/readyz",
|
|
22277
|
-
getParentRoute: () => Route$
|
|
24191
|
+
getParentRoute: () => Route$U
|
|
22278
24192
|
});
|
|
22279
|
-
const LivezRoute = Route$
|
|
24193
|
+
const LivezRoute = Route$S.update({
|
|
22280
24194
|
id: "/livez",
|
|
22281
24195
|
path: "/livez",
|
|
22282
|
-
getParentRoute: () => Route$
|
|
24196
|
+
getParentRoute: () => Route$U
|
|
22283
24197
|
});
|
|
22284
|
-
const IndexRoute = Route$
|
|
24198
|
+
const IndexRoute = Route$R.update({
|
|
22285
24199
|
id: "/",
|
|
22286
24200
|
path: "/",
|
|
22287
|
-
getParentRoute: () => Route$
|
|
24201
|
+
getParentRoute: () => Route$U
|
|
22288
24202
|
});
|
|
22289
|
-
const SessionSessionIdRoute = Route$
|
|
24203
|
+
const SessionSessionIdRoute = Route$Q.update({
|
|
22290
24204
|
id: "/session/$sessionId",
|
|
22291
24205
|
path: "/session/$sessionId",
|
|
22292
|
-
getParentRoute: () => Route$
|
|
24206
|
+
getParentRoute: () => Route$U
|
|
22293
24207
|
});
|
|
22294
|
-
const ProxySplatRoute = Route$
|
|
24208
|
+
const ProxySplatRoute = Route$P.update({
|
|
22295
24209
|
id: "/proxy/$",
|
|
22296
24210
|
path: "/proxy/$",
|
|
22297
|
-
getParentRoute: () => Route$
|
|
24211
|
+
getParentRoute: () => Route$U
|
|
22298
24212
|
});
|
|
22299
|
-
const ApiSessionsRoute = Route$
|
|
24213
|
+
const ApiSessionsRoute = Route$O.update({
|
|
22300
24214
|
id: "/api/sessions",
|
|
22301
24215
|
path: "/api/sessions",
|
|
22302
|
-
getParentRoute: () => Route$
|
|
24216
|
+
getParentRoute: () => Route$U
|
|
22303
24217
|
});
|
|
22304
|
-
const ApiRunsRoute = Route$
|
|
24218
|
+
const ApiRunsRoute = Route$N.update({
|
|
22305
24219
|
id: "/api/runs",
|
|
22306
24220
|
path: "/api/runs",
|
|
22307
|
-
getParentRoute: () => Route$
|
|
24221
|
+
getParentRoute: () => Route$U
|
|
22308
24222
|
});
|
|
22309
|
-
const ApiProvidersRoute = Route$
|
|
24223
|
+
const ApiProvidersRoute = Route$M.update({
|
|
22310
24224
|
id: "/api/providers",
|
|
22311
24225
|
path: "/api/providers",
|
|
22312
|
-
getParentRoute: () => Route$
|
|
24226
|
+
getParentRoute: () => Route$U
|
|
22313
24227
|
});
|
|
22314
|
-
const ApiPiAgentRoute = Route$
|
|
24228
|
+
const ApiPiAgentRoute = Route$L.update({
|
|
22315
24229
|
id: "/api/pi-agent",
|
|
22316
24230
|
path: "/api/pi-agent",
|
|
22317
|
-
getParentRoute: () => Route$
|
|
24231
|
+
getParentRoute: () => Route$U
|
|
22318
24232
|
});
|
|
22319
|
-
const ApiModelsRoute = Route$
|
|
24233
|
+
const ApiModelsRoute = Route$K.update({
|
|
22320
24234
|
id: "/api/models",
|
|
22321
24235
|
path: "/api/models",
|
|
22322
|
-
getParentRoute: () => Route$
|
|
24236
|
+
getParentRoute: () => Route$U
|
|
22323
24237
|
});
|
|
22324
|
-
const ApiMcpRoute = Route$
|
|
24238
|
+
const ApiMcpRoute = Route$J.update({
|
|
22325
24239
|
id: "/api/mcp",
|
|
22326
24240
|
path: "/api/mcp",
|
|
22327
|
-
getParentRoute: () => Route$
|
|
24241
|
+
getParentRoute: () => Route$U
|
|
22328
24242
|
});
|
|
22329
|
-
const ApiLogsRoute = Route$
|
|
24243
|
+
const ApiLogsRoute = Route$I.update({
|
|
22330
24244
|
id: "/api/logs",
|
|
22331
24245
|
path: "/api/logs",
|
|
22332
|
-
getParentRoute: () => Route$
|
|
24246
|
+
getParentRoute: () => Route$U
|
|
24247
|
+
});
|
|
24248
|
+
const ApiInstancesRoute = Route$H.update({
|
|
24249
|
+
id: "/api/instances",
|
|
24250
|
+
path: "/api/instances",
|
|
24251
|
+
getParentRoute: () => Route$U
|
|
22333
24252
|
});
|
|
22334
|
-
const ApiHealthRoute = Route$
|
|
24253
|
+
const ApiHealthRoute = Route$G.update({
|
|
22335
24254
|
id: "/api/health",
|
|
22336
24255
|
path: "/api/health",
|
|
22337
|
-
getParentRoute: () => Route$
|
|
24256
|
+
getParentRoute: () => Route$U
|
|
22338
24257
|
});
|
|
22339
|
-
const ApiGroupsRoute = Route$
|
|
24258
|
+
const ApiGroupsRoute = Route$F.update({
|
|
22340
24259
|
id: "/api/groups",
|
|
22341
24260
|
path: "/api/groups",
|
|
22342
|
-
getParentRoute: () => Route$
|
|
24261
|
+
getParentRoute: () => Route$U
|
|
22343
24262
|
});
|
|
22344
|
-
const ApiConfigRoute = Route$
|
|
24263
|
+
const ApiConfigRoute = Route$E.update({
|
|
22345
24264
|
id: "/api/config",
|
|
22346
24265
|
path: "/api/config",
|
|
22347
|
-
getParentRoute: () => Route$
|
|
24266
|
+
getParentRoute: () => Route$U
|
|
22348
24267
|
});
|
|
22349
|
-
const ApiAlertsRoute = Route$
|
|
24268
|
+
const ApiAlertsRoute = Route$D.update({
|
|
22350
24269
|
id: "/api/alerts",
|
|
22351
24270
|
path: "/api/alerts",
|
|
22352
|
-
getParentRoute: () => Route$
|
|
24271
|
+
getParentRoute: () => Route$U
|
|
22353
24272
|
});
|
|
22354
|
-
const ApiRunsRunIdRoute = Route$
|
|
24273
|
+
const ApiRunsRunIdRoute = Route$C.update({
|
|
22355
24274
|
id: "/$runId",
|
|
22356
24275
|
path: "/$runId",
|
|
22357
24276
|
getParentRoute: () => ApiRunsRoute
|
|
22358
24277
|
});
|
|
22359
|
-
const ApiProvidersScanRoute = Route$
|
|
24278
|
+
const ApiProvidersScanRoute = Route$B.update({
|
|
22360
24279
|
id: "/scan",
|
|
22361
24280
|
path: "/scan",
|
|
22362
24281
|
getParentRoute: () => ApiProvidersRoute
|
|
22363
24282
|
});
|
|
22364
|
-
const ApiProvidersImportRoute = Route$
|
|
24283
|
+
const ApiProvidersImportRoute = Route$A.update({
|
|
22365
24284
|
id: "/import",
|
|
22366
24285
|
path: "/import",
|
|
22367
24286
|
getParentRoute: () => ApiProvidersRoute
|
|
22368
24287
|
});
|
|
22369
|
-
const ApiProvidersExportRoute = Route$
|
|
24288
|
+
const ApiProvidersExportRoute = Route$z.update({
|
|
22370
24289
|
id: "/export",
|
|
22371
24290
|
path: "/export",
|
|
22372
24291
|
getParentRoute: () => ApiProvidersRoute
|
|
22373
24292
|
});
|
|
22374
|
-
const ApiProvidersProviderIdRoute = Route$
|
|
24293
|
+
const ApiProvidersProviderIdRoute = Route$y.update({
|
|
22375
24294
|
id: "/$providerId",
|
|
22376
24295
|
path: "/$providerId",
|
|
22377
24296
|
getParentRoute: () => ApiProvidersRoute
|
|
22378
24297
|
});
|
|
22379
|
-
const ApiLogsStreamRoute = Route$
|
|
24298
|
+
const ApiLogsStreamRoute = Route$x.update({
|
|
22380
24299
|
id: "/stream",
|
|
22381
24300
|
path: "/stream",
|
|
22382
24301
|
getParentRoute: () => ApiLogsRoute
|
|
22383
24302
|
});
|
|
22384
|
-
const ApiLogsImportRoute = Route$
|
|
24303
|
+
const ApiLogsImportRoute = Route$w.update({
|
|
22385
24304
|
id: "/import",
|
|
22386
24305
|
path: "/import",
|
|
22387
24306
|
getParentRoute: () => ApiLogsRoute
|
|
22388
24307
|
});
|
|
22389
|
-
const ApiLogsIdRoute = Route$
|
|
24308
|
+
const ApiLogsIdRoute = Route$v.update({
|
|
22390
24309
|
id: "/$id",
|
|
22391
24310
|
path: "/$id",
|
|
22392
24311
|
getParentRoute: () => ApiLogsRoute
|
|
22393
24312
|
});
|
|
22394
|
-
const ApiKnowledgeSearchRoute = Route$
|
|
24313
|
+
const ApiKnowledgeSearchRoute = Route$u.update({
|
|
22395
24314
|
id: "/api/knowledge/search",
|
|
22396
24315
|
path: "/api/knowledge/search",
|
|
22397
|
-
getParentRoute: () => Route$
|
|
24316
|
+
getParentRoute: () => Route$U
|
|
22398
24317
|
});
|
|
22399
|
-
const ApiKnowledgeProjectContextRoute = Route$
|
|
24318
|
+
const ApiKnowledgeProjectContextRoute = Route$t.update({
|
|
22400
24319
|
id: "/api/knowledge/project-context",
|
|
22401
24320
|
path: "/api/knowledge/project-context",
|
|
22402
|
-
getParentRoute: () => Route$
|
|
24321
|
+
getParentRoute: () => Route$U
|
|
22403
24322
|
});
|
|
22404
|
-
const ApiKnowledgeCandidatesRoute = Route$
|
|
24323
|
+
const ApiKnowledgeCandidatesRoute = Route$s.update({
|
|
22405
24324
|
id: "/api/knowledge/candidates",
|
|
22406
24325
|
path: "/api/knowledge/candidates",
|
|
22407
|
-
getParentRoute: () => Route$
|
|
24326
|
+
getParentRoute: () => Route$U
|
|
22408
24327
|
});
|
|
22409
|
-
const
|
|
24328
|
+
const ApiInstancesNameRoute = Route$r.update({
|
|
24329
|
+
id: "/$name",
|
|
24330
|
+
path: "/$name",
|
|
24331
|
+
getParentRoute: () => ApiInstancesRoute
|
|
24332
|
+
});
|
|
24333
|
+
const ApiGroupsGroupIdRoute = Route$q.update({
|
|
22410
24334
|
id: "/$groupId",
|
|
22411
24335
|
path: "/$groupId",
|
|
22412
24336
|
getParentRoute: () => ApiGroupsRoute
|
|
22413
24337
|
});
|
|
22414
|
-
const ApiEcosystemTasksRoute = Route$
|
|
24338
|
+
const ApiEcosystemTasksRoute = Route$p.update({
|
|
22415
24339
|
id: "/api/ecosystem/tasks",
|
|
22416
24340
|
path: "/api/ecosystem/tasks",
|
|
22417
|
-
getParentRoute: () => Route$
|
|
24341
|
+
getParentRoute: () => Route$U
|
|
22418
24342
|
});
|
|
22419
|
-
const ApiEcosystemPackagesRoute = Route$
|
|
24343
|
+
const ApiEcosystemPackagesRoute = Route$o.update({
|
|
22420
24344
|
id: "/api/ecosystem/packages",
|
|
22421
24345
|
path: "/api/ecosystem/packages",
|
|
22422
|
-
getParentRoute: () => Route$
|
|
24346
|
+
getParentRoute: () => Route$U
|
|
22423
24347
|
});
|
|
22424
|
-
const ApiConfigPathsRoute = Route$
|
|
24348
|
+
const ApiConfigPathsRoute = Route$n.update({
|
|
22425
24349
|
id: "/paths",
|
|
22426
24350
|
path: "/paths",
|
|
22427
24351
|
getParentRoute: () => ApiConfigRoute
|
|
22428
24352
|
});
|
|
22429
|
-
const ApiAlertsSummaryRoute = Route$
|
|
24353
|
+
const ApiAlertsSummaryRoute = Route$m.update({
|
|
22430
24354
|
id: "/summary",
|
|
22431
24355
|
path: "/summary",
|
|
22432
24356
|
getParentRoute: () => ApiAlertsRoute
|
|
22433
24357
|
});
|
|
22434
|
-
const ApiRunsRunIdEvidenceRoute = Route$
|
|
24358
|
+
const ApiRunsRunIdEvidenceRoute = Route$l.update({
|
|
22435
24359
|
id: "/evidence",
|
|
22436
24360
|
path: "/evidence",
|
|
22437
24361
|
getParentRoute: () => ApiRunsRunIdRoute
|
|
22438
24362
|
});
|
|
22439
|
-
const ApiProvidersProviderIdTestRoute = Route$
|
|
24363
|
+
const ApiProvidersProviderIdTestRoute = Route$k.update({
|
|
22440
24364
|
id: "/test",
|
|
22441
24365
|
path: "/test",
|
|
22442
24366
|
getParentRoute: () => ApiProvidersProviderIdRoute
|
|
22443
24367
|
});
|
|
22444
|
-
const ApiProvidersProviderIdModelMetadataRoute = Route$
|
|
24368
|
+
const ApiProvidersProviderIdModelMetadataRoute = Route$j.update({
|
|
22445
24369
|
id: "/model-metadata",
|
|
22446
24370
|
path: "/model-metadata",
|
|
22447
24371
|
getParentRoute: () => ApiProvidersProviderIdRoute
|
|
22448
24372
|
});
|
|
22449
|
-
const ApiLogsIdReplayRoute = Route$
|
|
24373
|
+
const ApiLogsIdReplayRoute = Route$i.update({
|
|
22450
24374
|
id: "/replay",
|
|
22451
24375
|
path: "/replay",
|
|
22452
24376
|
getParentRoute: () => ApiLogsIdRoute
|
|
22453
24377
|
});
|
|
22454
|
-
const ApiLogsIdChunksRoute = Route$
|
|
24378
|
+
const ApiLogsIdChunksRoute = Route$h.update({
|
|
22455
24379
|
id: "/chunks",
|
|
22456
24380
|
path: "/chunks",
|
|
22457
24381
|
getParentRoute: () => ApiLogsIdRoute
|
|
22458
24382
|
});
|
|
22459
|
-
const ApiLogsIdBodyRoute = Route$
|
|
24383
|
+
const ApiLogsIdBodyRoute = Route$g.update({
|
|
22460
24384
|
id: "/body",
|
|
22461
24385
|
path: "/body",
|
|
22462
24386
|
getParentRoute: () => ApiLogsIdRoute
|
|
22463
24387
|
});
|
|
22464
|
-
const ApiKnowledgeCandidatesCandidateIdRoute = Route$
|
|
24388
|
+
const ApiKnowledgeCandidatesCandidateIdRoute = Route$f.update({
|
|
22465
24389
|
id: "/$candidateId",
|
|
22466
24390
|
path: "/$candidateId",
|
|
22467
24391
|
getParentRoute: () => ApiKnowledgeCandidatesRoute
|
|
22468
24392
|
});
|
|
24393
|
+
const ApiInstancesNameStopRoute = Route$e.update({
|
|
24394
|
+
id: "/stop",
|
|
24395
|
+
path: "/stop",
|
|
24396
|
+
getParentRoute: () => ApiInstancesNameRoute
|
|
24397
|
+
});
|
|
24398
|
+
const ApiInstancesNameStartRoute = Route$d.update({
|
|
24399
|
+
id: "/start",
|
|
24400
|
+
path: "/start",
|
|
24401
|
+
getParentRoute: () => ApiInstancesNameRoute
|
|
24402
|
+
});
|
|
24403
|
+
const ApiInstancesNameRestartRoute = Route$c.update({
|
|
24404
|
+
id: "/restart",
|
|
24405
|
+
path: "/restart",
|
|
24406
|
+
getParentRoute: () => ApiInstancesNameRoute
|
|
24407
|
+
});
|
|
24408
|
+
const ApiInstancesNameConnectionRoute = Route$b.update({
|
|
24409
|
+
id: "/connection",
|
|
24410
|
+
path: "/connection",
|
|
24411
|
+
getParentRoute: () => ApiInstancesNameRoute
|
|
24412
|
+
});
|
|
22469
24413
|
const ApiGroupsGroupIdSessionsRoute = Route$a.update({
|
|
22470
24414
|
id: "/sessions",
|
|
22471
24415
|
path: "/sessions",
|
|
@@ -22489,7 +24433,7 @@ const ApiProvidersProviderIdTestLogRoute = Route$7.update({
|
|
|
22489
24433
|
const ApiKnowledgeSessionsSessionIdCandidatesRoute = Route$6.update({
|
|
22490
24434
|
id: "/api/knowledge/sessions/$sessionId/candidates",
|
|
22491
24435
|
path: "/api/knowledge/sessions/$sessionId/candidates",
|
|
22492
|
-
getParentRoute: () => Route$
|
|
24436
|
+
getParentRoute: () => Route$U
|
|
22493
24437
|
});
|
|
22494
24438
|
const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route$5.update({
|
|
22495
24439
|
id: "/promote",
|
|
@@ -22499,7 +24443,7 @@ const ApiKnowledgeCandidatesCandidateIdPromoteRoute = Route$5.update({
|
|
|
22499
24443
|
const ApiEcosystemRecipesRecipeIdRunRoute = Route$4.update({
|
|
22500
24444
|
id: "/api/ecosystem/recipes/$recipeId/run",
|
|
22501
24445
|
path: "/api/ecosystem/recipes/$recipeId/run",
|
|
22502
|
-
getParentRoute: () => Route$
|
|
24446
|
+
getParentRoute: () => Route$U
|
|
22503
24447
|
});
|
|
22504
24448
|
const ApiEcosystemPackagesPackageIdUpgradeRoute = Route$3.update({
|
|
22505
24449
|
id: "/$packageId/upgrade",
|
|
@@ -22544,6 +24488,19 @@ const ApiGroupsRouteChildren = {
|
|
|
22544
24488
|
const ApiGroupsRouteWithChildren = ApiGroupsRoute._addFileChildren(
|
|
22545
24489
|
ApiGroupsRouteChildren
|
|
22546
24490
|
);
|
|
24491
|
+
const ApiInstancesNameRouteChildren = {
|
|
24492
|
+
ApiInstancesNameConnectionRoute,
|
|
24493
|
+
ApiInstancesNameRestartRoute,
|
|
24494
|
+
ApiInstancesNameStartRoute,
|
|
24495
|
+
ApiInstancesNameStopRoute
|
|
24496
|
+
};
|
|
24497
|
+
const ApiInstancesNameRouteWithChildren = ApiInstancesNameRoute._addFileChildren(ApiInstancesNameRouteChildren);
|
|
24498
|
+
const ApiInstancesRouteChildren = {
|
|
24499
|
+
ApiInstancesNameRoute: ApiInstancesNameRouteWithChildren
|
|
24500
|
+
};
|
|
24501
|
+
const ApiInstancesRouteWithChildren = ApiInstancesRoute._addFileChildren(
|
|
24502
|
+
ApiInstancesRouteChildren
|
|
24503
|
+
);
|
|
22547
24504
|
const ApiLogsIdRouteChildren = {
|
|
22548
24505
|
ApiLogsIdBodyRoute,
|
|
22549
24506
|
ApiLogsIdChunksRoute,
|
|
@@ -22621,6 +24578,7 @@ const rootRouteChildren = {
|
|
|
22621
24578
|
ApiConfigRoute: ApiConfigRouteWithChildren,
|
|
22622
24579
|
ApiGroupsRoute: ApiGroupsRouteWithChildren,
|
|
22623
24580
|
ApiHealthRoute,
|
|
24581
|
+
ApiInstancesRoute: ApiInstancesRouteWithChildren,
|
|
22624
24582
|
ApiLogsRoute: ApiLogsRouteWithChildren,
|
|
22625
24583
|
ApiMcpRoute,
|
|
22626
24584
|
ApiModelsRoute,
|
|
@@ -22638,7 +24596,7 @@ const rootRouteChildren = {
|
|
|
22638
24596
|
ApiEcosystemRecipesRecipeIdRunRoute,
|
|
22639
24597
|
ApiKnowledgeSessionsSessionIdCandidatesRoute
|
|
22640
24598
|
};
|
|
22641
|
-
const routeTree = Route$
|
|
24599
|
+
const routeTree = Route$U._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
22642
24600
|
function defaultRouterBasePath() {
|
|
22643
24601
|
return getRouterBasePath(typeof process === "undefined" ? void 0 : process.env);
|
|
22644
24602
|
}
|
|
@@ -22670,54 +24628,53 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
|
|
|
22670
24628
|
getRouter
|
|
22671
24629
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
22672
24630
|
export {
|
|
22673
|
-
|
|
24631
|
+
formatContextWindowInput as $,
|
|
22674
24632
|
AnthropicResponseSchema$1 as A,
|
|
22675
24633
|
Button as B,
|
|
22676
24634
|
CapturedLogSchema as C,
|
|
22677
24635
|
DEFAULT_CAPTURE_MODE as D,
|
|
22678
|
-
|
|
22679
|
-
|
|
22680
|
-
|
|
22681
|
-
|
|
22682
|
-
|
|
22683
|
-
|
|
24636
|
+
stripClaudeCodeBillingHeader as E,
|
|
24637
|
+
LazyFeatureBoundary as F,
|
|
24638
|
+
AlertSummarySchema as G,
|
|
24639
|
+
AlertListResponseSchema as H,
|
|
24640
|
+
GroupEvidenceReadResponseSchema as I,
|
|
24641
|
+
InspectorGroupsListResponseSchema as J,
|
|
22684
24642
|
KnowledgeCandidateSchema as K,
|
|
22685
24643
|
LogBodyChunkSchema as L,
|
|
22686
|
-
|
|
22687
|
-
|
|
24644
|
+
DeleteInspectorGroupsResponseSchema as M,
|
|
24645
|
+
GroupEvidenceExportResultSchema as N,
|
|
22688
24646
|
OpenAIRequestSchema as O,
|
|
22689
|
-
|
|
22690
|
-
|
|
22691
|
-
Route$
|
|
24647
|
+
DeleteInspectorGroupResponseSchema as P,
|
|
24648
|
+
ProviderScanResponseSchema as Q,
|
|
24649
|
+
Route$Q as R,
|
|
22692
24650
|
SafeRouteError as S,
|
|
22693
24651
|
THEME_MODE_STORAGE_KEY as T,
|
|
22694
24652
|
UI_SCALE_STORAGE_KEY as U,
|
|
22695
|
-
|
|
22696
|
-
|
|
22697
|
-
|
|
22698
|
-
|
|
22699
|
-
|
|
22700
|
-
|
|
24653
|
+
providerHasContextMetadata as V,
|
|
24654
|
+
findProviderModelMetadata as W,
|
|
24655
|
+
formatContextWindowTokens as X,
|
|
24656
|
+
PATH_V1_MESSAGES as Y,
|
|
24657
|
+
PATH_V1_CHAT_COMPLETIONS as Z,
|
|
24658
|
+
PATH_V1_RESPONSES as _,
|
|
22701
24659
|
DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS as a,
|
|
22702
|
-
|
|
22703
|
-
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
|
|
22707
|
-
|
|
22708
|
-
|
|
22709
|
-
|
|
22710
|
-
|
|
22711
|
-
|
|
22712
|
-
|
|
22713
|
-
|
|
22714
|
-
|
|
22715
|
-
|
|
22716
|
-
|
|
22717
|
-
|
|
22718
|
-
|
|
22719
|
-
|
|
22720
|
-
router as ai,
|
|
24660
|
+
parseContextWindowTokensInput as a0,
|
|
24661
|
+
previewUpstreamUrl as a1,
|
|
24662
|
+
createPendingProviderTestResults as a2,
|
|
24663
|
+
ProviderTestResultsSchema as a3,
|
|
24664
|
+
createFailedProviderTestResults as a4,
|
|
24665
|
+
MAX_SLOW_RESPONSE_THRESHOLD_SECONDS as a5,
|
|
24666
|
+
MAX_PROVIDER_TEST_TIMEOUT_SECONDS as a6,
|
|
24667
|
+
TimeDisplayFormatSchema as a7,
|
|
24668
|
+
EcosystemPackagesResponseSchema as a8,
|
|
24669
|
+
EcosystemTaskResponseSchema as a9,
|
|
24670
|
+
EcosystemTasksResponseSchema as aa,
|
|
24671
|
+
EcosystemExecutionConfirmationResponseSchema as ab,
|
|
24672
|
+
CrabLogo as ac,
|
|
24673
|
+
resolveProviderContextWindow as ad,
|
|
24674
|
+
isPlainRecord as ae,
|
|
24675
|
+
formatUiScale as af,
|
|
24676
|
+
StreamingChunkSchema$1 as ag,
|
|
24677
|
+
router as ah,
|
|
22721
24678
|
DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS as b,
|
|
22722
24679
|
DEFAULT_TIME_DISPLAY_FORMAT as c,
|
|
22723
24680
|
RuntimeConfigSchema as d,
|
|
@@ -22725,7 +24682,7 @@ export {
|
|
|
22725
24682
|
persistThemeMode as f,
|
|
22726
24683
|
readStoredUiScalePreference as g,
|
|
22727
24684
|
persistUiScalePreference as h,
|
|
22728
|
-
isRecord as i,
|
|
24685
|
+
isRecord$2 as i,
|
|
22729
24686
|
applyUiScalePreference as j,
|
|
22730
24687
|
normalizeUiScalePreference as k,
|
|
22731
24688
|
RedactedProviderDtoSchema as l,
|
|
@@ -22739,8 +24696,8 @@ export {
|
|
|
22739
24696
|
parseOpenAIResponsesResponse as t,
|
|
22740
24697
|
OpenAIResponsesRequestSchema as u,
|
|
22741
24698
|
apiFormatForPath as v,
|
|
22742
|
-
|
|
22743
|
-
|
|
22744
|
-
|
|
22745
|
-
|
|
24699
|
+
getSessionPath as w,
|
|
24700
|
+
formatTokens as x,
|
|
24701
|
+
copyTextToClipboard as y,
|
|
24702
|
+
getStatusCategory as z
|
|
22746
24703
|
};
|