@tonyclaw/agent-inspector 3.0.46 → 3.0.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/backend/nitro.json +1 -1
- package/.output/cli.js +6791 -6006
- package/.output/server/_ssr/index.mjs +1 -1
- package/.output/server/_ssr/{router-D18yUq36.mjs → router-Cj10mv-Z.mjs} +334 -5
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-DAUuIJ6G.js → CompareDrawer-uRiXFOCp.js} +1 -1
- package/.output/ui/assets/{InspectorPet-BRBVjOWI.js → InspectorPet-j-r6xk0V.js} +1 -1
- package/.output/ui/assets/{ProxyViewerContainer-D7Sq0ctc.js → ProxyViewerContainer-DnUrkMJF.js} +23 -23
- package/.output/ui/assets/{ReplayDialog-nDLsjOhs.js → ReplayDialog-B2URyHMv.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-zd4BDgL0.js → RequestAnatomy-CK0M0CJO.js} +1 -1
- package/.output/ui/assets/{ResponseView-AdIXGcSn.js → ResponseView-CYSVA4lT.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-CesTz8He.js → StreamingChunkSequence-CHf54ZUV.js} +1 -1
- package/.output/ui/assets/{_sessionId-QnBomgPD.js → _sessionId-BXULpTNP.js} +1 -1
- package/.output/ui/assets/{_sessionId-BAfzrhSU.js → _sessionId-E4lbj5-I.js} +1 -1
- package/.output/ui/assets/{index-D0rtCN9V.js → index-B4ixI3kG.js} +1 -1
- package/.output/ui/assets/{index-BcsdxBAd.js → index-BJ-71WLq.js} +1 -1
- package/.output/ui/assets/{index-DHpwr08Z.js → index-CW0ab9r3.js} +2 -2
- package/.output/ui/assets/{index-dNuk2dsU.js → index-Ce-lO51c.js} +1 -1
- package/.output/ui/assets/{json-viewer-BvFHglMb.js → json-viewer-C6E8aLaV.js} +1 -1
- package/.output/ui/assets/{jszip.min-B5Z7gNZB.js → jszip.min-DjWdTBbp.js} +1 -1
- package/.output/ui/index.html +1 -1
- package/README.md +53 -0
- package/package.json +17 -3
- package/src/backend/routes/api/-instances.ts +7 -109
- package/src/cli/instance.ts +21 -0
- package/src/cli/instanceApi.ts +213 -0
- package/src/cli/instanceArgs.ts +16 -0
- package/src/cli/instanceControl.ts +10 -0
- package/src/cli/instanceModel.ts +9 -0
- package/src/cli/rustBackendPackage.ts +458 -0
- package/src/cli.ts +171 -14
- package/src/components/ProxyViewer.tsx +4 -13
- package/src/components/proxy-viewer/proxyViewerLogic.ts +9 -0
- package/src/lib/backendImplementation.ts +16 -0
- package/src/lib/instanceContract.ts +3 -0
- package/src/lib/managedInstance.ts +9 -1
- package/src/mcp/instanceHandlers.ts +1 -0
- package/src/proxy/identityProxy.ts +148 -0
- package/src/proxy/sessionArchive.ts +0 -8
- package/src/proxy/sqliteLogIndex.ts +0 -40
package/src/cli.ts
CHANGED
|
@@ -7,6 +7,13 @@ import { dirname, join, resolve as resolvePath } from "node:path";
|
|
|
7
7
|
import { existsSync } from "node:fs";
|
|
8
8
|
import type { Readable, Writable } from "node:stream";
|
|
9
9
|
import packageJson from "../package.json";
|
|
10
|
+
import {
|
|
11
|
+
AGENT_INSPECTOR_BACKEND_ENV,
|
|
12
|
+
AGENT_INSPECTOR_BACKEND_HEADER,
|
|
13
|
+
BackendImplementationSchema,
|
|
14
|
+
parseBackendImplementation,
|
|
15
|
+
type BackendImplementation,
|
|
16
|
+
} from "./lib/backendImplementation.js";
|
|
10
17
|
import {
|
|
11
18
|
AGENT_INSPECTOR_BASE_PATH_ENV,
|
|
12
19
|
AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV,
|
|
@@ -33,6 +40,11 @@ import {
|
|
|
33
40
|
shouldSuppressServerOutputLine,
|
|
34
41
|
type ServerOutputFilterContext,
|
|
35
42
|
} from "./cli/startupOutput.js";
|
|
43
|
+
import {
|
|
44
|
+
resolvePreferredBackendImplementation,
|
|
45
|
+
resolveRustBackendPackage,
|
|
46
|
+
} from "./cli/rustBackendPackage.js";
|
|
47
|
+
import { handleNamedInstanceApi } from "./cli/instanceApi.js";
|
|
36
48
|
import {
|
|
37
49
|
closeIdentityProxyGracefully,
|
|
38
50
|
forceCloseIdentityProxy,
|
|
@@ -87,6 +99,7 @@ Global options:
|
|
|
87
99
|
-v, --version Print the package version
|
|
88
100
|
|
|
89
101
|
Start options:
|
|
102
|
+
--backend <typescript|rust> Select the backend implementation (default: rust when native package is available)
|
|
90
103
|
--backend-only, --headless Start the API, proxy, and MCP runtime without serving the UI
|
|
91
104
|
--with-ui Serve the independently built UI through the protected ingress
|
|
92
105
|
--port, -p <port> Public ingress port (default: 9527)
|
|
@@ -194,6 +207,33 @@ async function getRunningCaptureMode(
|
|
|
194
207
|
}
|
|
195
208
|
}
|
|
196
209
|
|
|
210
|
+
async function getRunningBackend(
|
|
211
|
+
port: number,
|
|
212
|
+
host?: string,
|
|
213
|
+
basePath = "",
|
|
214
|
+
): Promise<BackendImplementation | null> {
|
|
215
|
+
const controller = new AbortController();
|
|
216
|
+
const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
|
|
217
|
+
try {
|
|
218
|
+
const response = await fetch(
|
|
219
|
+
appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/health", basePath),
|
|
220
|
+
{
|
|
221
|
+
cache: "no-store",
|
|
222
|
+
signal: controller.signal,
|
|
223
|
+
},
|
|
224
|
+
);
|
|
225
|
+
if (!response.ok) return null;
|
|
226
|
+
const header = response.headers.get(AGENT_INSPECTOR_BACKEND_HEADER);
|
|
227
|
+
if (header === null) return "typescript";
|
|
228
|
+
const parsed = BackendImplementationSchema.safeParse(header.trim().toLowerCase());
|
|
229
|
+
return parsed.success ? parsed.data : null;
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
} finally {
|
|
233
|
+
clearTimeout(timeout);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
197
237
|
function isPortAcceptingConnections(port: number, host?: string): Promise<boolean> {
|
|
198
238
|
return new Promise((resolve) => {
|
|
199
239
|
const socket = createConnection({ host: probeHostForBindHost(host), port });
|
|
@@ -293,11 +333,35 @@ type ServerCommand = {
|
|
|
293
333
|
args: string[];
|
|
294
334
|
};
|
|
295
335
|
|
|
296
|
-
|
|
336
|
+
type ServerCommandResolution =
|
|
337
|
+
| { kind: "success"; command: ServerCommand }
|
|
338
|
+
| { kind: "error"; message: string };
|
|
339
|
+
|
|
340
|
+
function resolveServerCommand(
|
|
341
|
+
outputDir: string,
|
|
342
|
+
serverPath: string,
|
|
343
|
+
backend: BackendImplementation,
|
|
344
|
+
): ServerCommandResolution {
|
|
345
|
+
if (backend === "rust") {
|
|
346
|
+
const resolved = resolveRustBackendPackage({ rootVersion: packageJson.version });
|
|
347
|
+
return resolved.kind === "error"
|
|
348
|
+
? {
|
|
349
|
+
kind: "error",
|
|
350
|
+
message: `Cannot start the Rust backend (${resolved.error.code}): ${resolved.error.message}`,
|
|
351
|
+
}
|
|
352
|
+
: {
|
|
353
|
+
kind: "success",
|
|
354
|
+
command: { command: resolved.value.command, args: [...resolved.value.args] },
|
|
355
|
+
};
|
|
356
|
+
}
|
|
297
357
|
const brandedRuntime = join(outputDir, BRANDED_WINDOWS_RUNTIME_EXE);
|
|
298
|
-
return
|
|
299
|
-
|
|
300
|
-
|
|
358
|
+
return {
|
|
359
|
+
kind: "success",
|
|
360
|
+
command:
|
|
361
|
+
process.platform === "win32" && existsSync(brandedRuntime)
|
|
362
|
+
? { command: brandedRuntime, args: [serverPath] }
|
|
363
|
+
: { command: process.execPath, args: [serverPath] },
|
|
364
|
+
};
|
|
301
365
|
}
|
|
302
366
|
|
|
303
367
|
function parseCaptureMode(value: string | undefined): CaptureMode | null {
|
|
@@ -385,6 +449,7 @@ function normalizeExplicitPublicOrigin(value: string | undefined): string | null
|
|
|
385
449
|
}
|
|
386
450
|
|
|
387
451
|
type BackgroundSupervisorOptions = {
|
|
452
|
+
backend: BackendImplementation;
|
|
388
453
|
port: number;
|
|
389
454
|
host: string | undefined;
|
|
390
455
|
configDir: string | undefined;
|
|
@@ -396,7 +461,15 @@ type BackgroundSupervisorOptions = {
|
|
|
396
461
|
};
|
|
397
462
|
|
|
398
463
|
function buildBackgroundSupervisorArgs(options: BackgroundSupervisorOptions): string[] {
|
|
399
|
-
const args = [
|
|
464
|
+
const args = [
|
|
465
|
+
"--no-open",
|
|
466
|
+
"--backend",
|
|
467
|
+
options.backend,
|
|
468
|
+
"--port",
|
|
469
|
+
String(options.port),
|
|
470
|
+
"--mode",
|
|
471
|
+
options.captureMode,
|
|
472
|
+
];
|
|
400
473
|
if (options.host !== undefined) {
|
|
401
474
|
args.push("--host", options.host);
|
|
402
475
|
}
|
|
@@ -449,6 +522,7 @@ async function tryStartIdentityProxy(
|
|
|
449
522
|
allowedUiOrigins: readonly string[],
|
|
450
523
|
uiDirectory: string | undefined,
|
|
451
524
|
publicOrigin: string | undefined,
|
|
525
|
+
backend: BackendImplementation,
|
|
452
526
|
): Promise<import("node:http").Server | null> {
|
|
453
527
|
try {
|
|
454
528
|
const server = await startIdentityProxy({
|
|
@@ -456,6 +530,7 @@ async function tryStartIdentityProxy(
|
|
|
456
530
|
listenHost,
|
|
457
531
|
upstreamHost,
|
|
458
532
|
upstreamPort,
|
|
533
|
+
backendImplementation: backend,
|
|
459
534
|
restrictRemoteControl: !isLoopbackHost(listenHost),
|
|
460
535
|
allowRemoteControl: process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] === "1",
|
|
461
536
|
controlToken: process.env["AGENT_INSPECTOR_CONTROL_TOKEN"],
|
|
@@ -472,6 +547,7 @@ async function tryStartIdentityProxy(
|
|
|
472
547
|
maxRequestBytes: readPositiveIntegerEnv("AGENT_INSPECTOR_MAX_REQUEST_BYTES"),
|
|
473
548
|
requestTimeoutMs: readPositiveIntegerEnv("AGENT_INSPECTOR_REQUEST_TIMEOUT_MS"),
|
|
474
549
|
managedInstance,
|
|
550
|
+
instanceApiHandler: backend === "rust" ? handleNamedInstanceApi : undefined,
|
|
475
551
|
});
|
|
476
552
|
return server;
|
|
477
553
|
} catch (err) {
|
|
@@ -495,6 +571,16 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
495
571
|
const envHost = process.env["NITRO_HOST"] ?? process.env["HOST"];
|
|
496
572
|
const envMode =
|
|
497
573
|
process.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? process.env["AGENT_INSPECTOR_MODE"];
|
|
574
|
+
const rawEnvBackend = process.env[AGENT_INSPECTOR_BACKEND_ENV];
|
|
575
|
+
const envBackend =
|
|
576
|
+
rawEnvBackend === undefined ? undefined : parseBackendImplementation(rawEnvBackend);
|
|
577
|
+
if (envBackend === null) {
|
|
578
|
+
console.error(
|
|
579
|
+
`Invalid ${AGENT_INSPECTOR_BACKEND_ENV}. Use typescript or rust; no backend was started.`,
|
|
580
|
+
);
|
|
581
|
+
process.exitCode = 1;
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
498
584
|
|
|
499
585
|
let port = portDefault;
|
|
500
586
|
let host =
|
|
@@ -510,6 +596,8 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
510
596
|
let enableIdentityProxy = true;
|
|
511
597
|
let legacyAliasEnabled = false;
|
|
512
598
|
let serveUi = process.env["AGENT_INSPECTOR_BACKEND_ONLY"] !== "1";
|
|
599
|
+
let backend: BackendImplementation = envBackend ?? "typescript";
|
|
600
|
+
let backendWasSpecified = envBackend !== undefined;
|
|
513
601
|
|
|
514
602
|
if (envMode !== undefined && envMode !== "") {
|
|
515
603
|
const parsedMode = parseCaptureMode(envMode);
|
|
@@ -556,6 +644,22 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
556
644
|
captureModeWasSpecified = true;
|
|
557
645
|
continue;
|
|
558
646
|
}
|
|
647
|
+
if (arg.startsWith("--backend=")) {
|
|
648
|
+
const parsedBackend = BackendImplementationSchema.safeParse(
|
|
649
|
+
arg
|
|
650
|
+
.slice(arg.indexOf("=") + 1)
|
|
651
|
+
.trim()
|
|
652
|
+
.toLowerCase(),
|
|
653
|
+
);
|
|
654
|
+
if (!parsedBackend.success) {
|
|
655
|
+
console.error("Invalid backend implementation. Use typescript or rust.");
|
|
656
|
+
process.exitCode = 1;
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
backend = parsedBackend.data;
|
|
660
|
+
backendWasSpecified = true;
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
559
663
|
switch (arg) {
|
|
560
664
|
case "--port":
|
|
561
665
|
case "-p":
|
|
@@ -574,6 +678,20 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
574
678
|
i++;
|
|
575
679
|
break;
|
|
576
680
|
}
|
|
681
|
+
case "--backend": {
|
|
682
|
+
const parsedBackend = BackendImplementationSchema.safeParse(
|
|
683
|
+
args[i + 1]?.trim().toLowerCase(),
|
|
684
|
+
);
|
|
685
|
+
if (!parsedBackend.success) {
|
|
686
|
+
console.error("Invalid backend implementation. Use typescript or rust.");
|
|
687
|
+
process.exitCode = 1;
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
backend = parsedBackend.data;
|
|
691
|
+
backendWasSpecified = true;
|
|
692
|
+
i++;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
577
695
|
case "--no-open":
|
|
578
696
|
open = false;
|
|
579
697
|
openWasSpecified = true;
|
|
@@ -662,6 +780,20 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
662
780
|
return;
|
|
663
781
|
}
|
|
664
782
|
|
|
783
|
+
if (!backendWasSpecified) {
|
|
784
|
+
backend =
|
|
785
|
+
managedLaunch?.backend ??
|
|
786
|
+
resolvePreferredBackendImplementation({ rootVersion: packageJson.version });
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (managedLaunch !== null && managedLaunch.backend !== backend) {
|
|
790
|
+
console.error(
|
|
791
|
+
`Managed instance backend mismatch: launch identity requires ${managedLaunch.backend}, but ${backend} was selected.`,
|
|
792
|
+
);
|
|
793
|
+
process.exitCode = 1;
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
|
|
665
797
|
if (!enableIdentityProxy && process.env["AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS"] !== "1") {
|
|
666
798
|
console.error(
|
|
667
799
|
"Refusing --no-identity-proxy because it also disables Host, CSRF, credential-scope, and first-hop budget enforcement. Keep the protected ingress enabled or set AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS=1 explicitly for temporary diagnostics.",
|
|
@@ -704,6 +836,16 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
704
836
|
return;
|
|
705
837
|
}
|
|
706
838
|
|
|
839
|
+
const outputDir = __dirname;
|
|
840
|
+
const serverPath = join(outputDir, "../.output/server/index.mjs");
|
|
841
|
+
const serverCommandResolution = resolveServerCommand(outputDir, serverPath, backend);
|
|
842
|
+
if (serverCommandResolution.kind === "error") {
|
|
843
|
+
console.error(serverCommandResolution.message);
|
|
844
|
+
process.exitCode = 1;
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const serverCommand = serverCommandResolution.command;
|
|
848
|
+
|
|
707
849
|
/**
|
|
708
850
|
* Check if a port is in use and kill the process using it
|
|
709
851
|
*/
|
|
@@ -770,6 +912,16 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
770
912
|
: appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/api/mcp", basePath);
|
|
771
913
|
|
|
772
914
|
if (!forceRestart && (await isInspectorHealthy(port, host, basePath))) {
|
|
915
|
+
const runningBackend = await getRunningBackend(port, host, basePath);
|
|
916
|
+
if (runningBackend !== backend) {
|
|
917
|
+
console.error(
|
|
918
|
+
runningBackend === null
|
|
919
|
+
? `A healthy Agent Inspector is already running at ${surfaceBaseUrl}, but its backend implementation cannot be verified. Use --force-restart to replace it safely.`
|
|
920
|
+
: `Agent Inspector at ${surfaceBaseUrl} uses ${runningBackend}, but ${backend} was requested. Use --force-restart to switch implementations.`,
|
|
921
|
+
);
|
|
922
|
+
process.exitCode = 1;
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
773
925
|
console.log(
|
|
774
926
|
`agent-inspector ${serveUi ? "is already running" : "backend is already running"} at ${surfaceBaseUrl}`,
|
|
775
927
|
);
|
|
@@ -816,6 +968,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
816
968
|
}
|
|
817
969
|
|
|
818
970
|
const supervisorArgs = buildBackgroundSupervisorArgs({
|
|
971
|
+
backend,
|
|
819
972
|
port,
|
|
820
973
|
host,
|
|
821
974
|
configDir,
|
|
@@ -861,11 +1014,6 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
861
1014
|
return;
|
|
862
1015
|
}
|
|
863
1016
|
|
|
864
|
-
// Compute server path
|
|
865
|
-
const outputDir = __dirname;
|
|
866
|
-
const serverPath = join(outputDir, "../.output/server/index.mjs");
|
|
867
|
-
const serverCommand = resolveServerCommand(outputDir, serverPath);
|
|
868
|
-
|
|
869
1017
|
// Start the server with the branded Windows runtime when postinstall created it.
|
|
870
1018
|
const serverEnv = { ...process.env };
|
|
871
1019
|
const currentCliEntry = process.env[AGENT_INSPECTOR_CLI_ENTRY_ENV] ?? process.argv[1];
|
|
@@ -902,6 +1050,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
902
1050
|
serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
|
|
903
1051
|
serverEnv["PROXY_PORT"] = String(port);
|
|
904
1052
|
serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
|
|
1053
|
+
serverEnv[AGENT_INSPECTOR_BACKEND_ENV] = backend;
|
|
905
1054
|
serverEnv["AGENT_INSPECTOR_BACKEND_ONLY"] = serveUi ? "0" : "1";
|
|
906
1055
|
const workerDir = join(__dirname, "workers");
|
|
907
1056
|
if (existsSync(workerDir)) {
|
|
@@ -913,9 +1062,13 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
913
1062
|
? background
|
|
914
1063
|
? ["ignore", "ignore", "ignore"]
|
|
915
1064
|
: ["ignore", "pipe", "pipe"]
|
|
916
|
-
:
|
|
917
|
-
?
|
|
918
|
-
|
|
1065
|
+
: backend === "typescript"
|
|
1066
|
+
? background
|
|
1067
|
+
? ["ignore", "ignore", "ignore", "ipc"]
|
|
1068
|
+
: ["ignore", "pipe", "pipe", "ipc"]
|
|
1069
|
+
: background
|
|
1070
|
+
? ["ignore", "ignore", "ignore"]
|
|
1071
|
+
: ["ignore", "pipe", "pipe"],
|
|
919
1072
|
detached: background,
|
|
920
1073
|
env: serverEnv,
|
|
921
1074
|
windowsHide: background,
|
|
@@ -947,7 +1100,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
947
1100
|
const requestSupervisorShutdown = (signal: "SIGINT" | "SIGTERM"): void => {
|
|
948
1101
|
if (supervisorShutdownPromise !== null) return;
|
|
949
1102
|
for (const proxy of identityProxies) stopIdentityProxyAdmission(proxy.server);
|
|
950
|
-
if (managedLaunch !== null && serverProcess.connected) {
|
|
1103
|
+
if (managedLaunch !== null && backend === "typescript" && serverProcess.connected) {
|
|
951
1104
|
serverProcess.send(managedInstanceShutdownMessage(managedLaunch.controlToken), (error) => {
|
|
952
1105
|
if (error !== null && serverProcess.exitCode === null) serverProcess.kill(signal);
|
|
953
1106
|
});
|
|
@@ -994,6 +1147,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
994
1147
|
controlToken: managedLaunch.controlToken,
|
|
995
1148
|
supervisorPid: process.pid,
|
|
996
1149
|
startedAt: managedLaunch.startedAt,
|
|
1150
|
+
backend,
|
|
997
1151
|
publicPort: port,
|
|
998
1152
|
upstreamPort,
|
|
999
1153
|
requestShutdown: () => requestSupervisorShutdown("SIGTERM"),
|
|
@@ -1008,6 +1162,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
1008
1162
|
uiOriginAllowlist.origins,
|
|
1009
1163
|
uiDirectory,
|
|
1010
1164
|
publicOrigin,
|
|
1165
|
+
backend,
|
|
1011
1166
|
);
|
|
1012
1167
|
if (primaryProxy === null) {
|
|
1013
1168
|
console.error(
|
|
@@ -1032,6 +1187,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
1032
1187
|
uiOriginAllowlist.origins,
|
|
1033
1188
|
uiDirectory,
|
|
1034
1189
|
publicOrigin,
|
|
1190
|
+
backend,
|
|
1035
1191
|
);
|
|
1036
1192
|
if (legacyProxy !== null) {
|
|
1037
1193
|
identityProxies.push({ label: "legacy", port: legacyAliasPort, server: legacyProxy });
|
|
@@ -1104,6 +1260,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
1104
1260
|
`${serveUi ? "UI and backend" : "Backend control and agent runtime"} running at ${surfaceBaseUrl}`,
|
|
1105
1261
|
);
|
|
1106
1262
|
console.log(` Capture mode: ${captureMode}`);
|
|
1263
|
+
console.log(` Backend: ${backend}`);
|
|
1107
1264
|
console.log(` Proxy: ${proxyUrl}`);
|
|
1108
1265
|
printAccessHints(port, host, basePath, serveUi, publicOrigin);
|
|
1109
1266
|
if (legacyUrl !== null && legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
@@ -61,6 +61,7 @@ import { useProviders } from "../lib/useProviders";
|
|
|
61
61
|
import { groupLogsByConversation, type ConversationGroupData } from "./proxy-viewer";
|
|
62
62
|
import {
|
|
63
63
|
buildSessionSlateStats,
|
|
64
|
+
buildHomeConnectionCommands,
|
|
64
65
|
COPYABLE_COMMAND_CONTAINER_CLASS_NAME,
|
|
65
66
|
COPYABLE_COMMAND_TEXT_CLASS_NAME,
|
|
66
67
|
exportRequiresConfirmation,
|
|
@@ -2148,19 +2149,9 @@ export function ProxyViewer({
|
|
|
2148
2149
|
{liveEmptyStateCopy.showConnectionCommands && connectionEndpoints !== null && (
|
|
2149
2150
|
<>
|
|
2150
2151
|
<div className="flex flex-col items-center gap-2">
|
|
2151
|
-
|
|
2152
|
-
command={
|
|
2153
|
-
|
|
2154
|
-
<CopyableCommand
|
|
2155
|
-
command={`LLM_BASE_URL=${connectionEndpoints.proxy} opencode`}
|
|
2156
|
-
/>
|
|
2157
|
-
<CopyableCommand
|
|
2158
|
-
command={`OPENAI_BASE_URL=${connectionEndpoints.openAiV1} <your-tool>`}
|
|
2159
|
-
/>
|
|
2160
|
-
<CopyableCommand
|
|
2161
|
-
command={`base_url = "${connectionEndpoints.openAiV1}"`}
|
|
2162
|
-
/>
|
|
2163
|
-
<CopyableCommand command={`MCP_URL=${connectionEndpoints.mcp}`} />
|
|
2152
|
+
{buildHomeConnectionCommands(connectionEndpoints).map((command) => (
|
|
2153
|
+
<CopyableCommand key={command} command={command} />
|
|
2154
|
+
))}
|
|
2164
2155
|
</div>
|
|
2165
2156
|
<p className="mx-auto max-w-xl text-xs leading-relaxed text-muted-foreground">
|
|
2166
2157
|
Container note: if your AI tool runs in a different container or host than
|
|
@@ -24,11 +24,20 @@ export type ConversationLogs = {
|
|
|
24
24
|
logs: CapturedLog[];
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
export type HomeConnectionEndpoints = {
|
|
28
|
+
proxy: string;
|
|
29
|
+
mcp: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
27
32
|
export const COPYABLE_COMMAND_CONTAINER_CLASS_NAME =
|
|
28
33
|
"flex w-full min-w-0 items-center gap-2 rounded-md border border-border/70 bg-muted/35 px-3 py-2 sm:w-auto sm:max-w-full";
|
|
29
34
|
export const COPYABLE_COMMAND_TEXT_CLASS_NAME =
|
|
30
35
|
"m-0 min-w-0 flex-1 overflow-x-auto font-mono text-xs text-foreground sm:text-sm";
|
|
31
36
|
|
|
37
|
+
export function buildHomeConnectionCommands(endpoints: HomeConnectionEndpoints): readonly string[] {
|
|
38
|
+
return [`PROXY_BASE_URL=${endpoints.proxy}`, `MCP_URL=${endpoints.mcp}`];
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
export type SessionSlateStats = {
|
|
33
42
|
status: SessionSlateStatus;
|
|
34
43
|
statusLabel: string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const AGENT_INSPECTOR_BACKEND_ENV = "AGENT_INSPECTOR_BACKEND";
|
|
4
|
+
export const AGENT_INSPECTOR_BACKEND_HEADER = "x-agent-inspector-backend";
|
|
5
|
+
|
|
6
|
+
export const BackendImplementationSchema = z.enum(["typescript", "rust"]);
|
|
7
|
+
|
|
8
|
+
export type BackendImplementation = z.infer<typeof BackendImplementationSchema>;
|
|
9
|
+
|
|
10
|
+
export function parseBackendImplementation(
|
|
11
|
+
value: string | undefined,
|
|
12
|
+
): BackendImplementation | null {
|
|
13
|
+
if (value === undefined) return "typescript";
|
|
14
|
+
const parsed = BackendImplementationSchema.safeParse(value.trim().toLowerCase());
|
|
15
|
+
return parsed.success ? parsed.data : null;
|
|
16
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { BackendImplementationSchema } from "./backendImplementation";
|
|
2
3
|
import { normalizePublicBasePath } from "./basePath";
|
|
3
4
|
|
|
4
5
|
export const INSTANCE_API_SCHEMA_VERSION = 1;
|
|
@@ -43,6 +44,7 @@ export const InstanceStatusOutputSchema = z
|
|
|
43
44
|
upstreamPort: z.number().int().min(1).max(65_535),
|
|
44
45
|
basePath: z.string().refine(isCanonicalPublicBasePath, "Invalid public Base Path"),
|
|
45
46
|
captureMode: z.enum(["simple", "full"]),
|
|
47
|
+
backend: BackendImplementationSchema.default("typescript"),
|
|
46
48
|
uiEnabled: z.boolean(),
|
|
47
49
|
dataDir: z.string().min(1),
|
|
48
50
|
supervisorPid: z.number().int().positive().nullable(),
|
|
@@ -124,6 +126,7 @@ export const InstanceStartRequestSchema = z
|
|
|
124
126
|
)
|
|
125
127
|
.optional(),
|
|
126
128
|
captureMode: z.enum(["simple", "full"]).optional(),
|
|
129
|
+
backend: BackendImplementationSchema.optional(),
|
|
127
130
|
uiEnabled: z.boolean().optional(),
|
|
128
131
|
dataDir: z.string().trim().min(1).max(32_767).optional(),
|
|
129
132
|
})
|
|
@@ -5,6 +5,7 @@ export const MANAGED_INSTANCE_SHUTDOWN_MESSAGE_TYPE = "agent-inspector:managed-s
|
|
|
5
5
|
export const AGENT_INSPECTOR_CLI_ENTRY_ENV = "AGENT_INSPECTOR_CLI_ENTRY";
|
|
6
6
|
|
|
7
7
|
export const MANAGED_INSTANCE_ENV = {
|
|
8
|
+
backend: "AGENT_INSPECTOR_INSTANCE_BACKEND",
|
|
8
9
|
name: "AGENT_INSPECTOR_INSTANCE_NAME",
|
|
9
10
|
instanceId: "AGENT_INSPECTOR_INSTANCE_ID",
|
|
10
11
|
launchId: "AGENT_INSPECTOR_INSTANCE_LAUNCH_ID",
|
|
@@ -16,6 +17,7 @@ export const MANAGED_INSTANCE_ENV = {
|
|
|
16
17
|
type ManagedInstanceEnv = Readonly<Record<string, string | undefined>>;
|
|
17
18
|
|
|
18
19
|
export type ManagedInstanceLaunch = {
|
|
20
|
+
backend: BackendImplementation;
|
|
19
21
|
name: string;
|
|
20
22
|
instanceId: string;
|
|
21
23
|
launchId: string;
|
|
@@ -47,6 +49,7 @@ export function readManagedInstanceLaunch(
|
|
|
47
49
|
env: ManagedInstanceEnv = process.env,
|
|
48
50
|
): ManagedInstanceLaunchResult {
|
|
49
51
|
const values = [
|
|
52
|
+
env[MANAGED_INSTANCE_ENV.backend],
|
|
50
53
|
env[MANAGED_INSTANCE_ENV.name],
|
|
51
54
|
env[MANAGED_INSTANCE_ENV.instanceId],
|
|
52
55
|
env[MANAGED_INSTANCE_ENV.launchId],
|
|
@@ -59,6 +62,9 @@ export function readManagedInstanceLaunch(
|
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
const name = nonEmpty(env[MANAGED_INSTANCE_ENV.name]);
|
|
65
|
+
const backend = parseBackendImplementation(
|
|
66
|
+
nonEmpty(env[MANAGED_INSTANCE_ENV.backend]) ?? undefined,
|
|
67
|
+
);
|
|
62
68
|
const instanceId = nonEmpty(env[MANAGED_INSTANCE_ENV.instanceId]);
|
|
63
69
|
const launchId = nonEmpty(env[MANAGED_INSTANCE_ENV.launchId]);
|
|
64
70
|
const controlToken = nonEmpty(env[MANAGED_INSTANCE_ENV.controlToken]);
|
|
@@ -66,6 +72,7 @@ export function readManagedInstanceLaunch(
|
|
|
66
72
|
const upstreamPort = Number(env[MANAGED_INSTANCE_ENV.upstreamPort]);
|
|
67
73
|
if (
|
|
68
74
|
name === null ||
|
|
75
|
+
backend === null ||
|
|
69
76
|
instanceId === null ||
|
|
70
77
|
launchId === null ||
|
|
71
78
|
controlToken === null ||
|
|
@@ -83,7 +90,7 @@ export function readManagedInstanceLaunch(
|
|
|
83
90
|
}
|
|
84
91
|
return {
|
|
85
92
|
kind: "managed",
|
|
86
|
-
launch: { name, instanceId, launchId, controlToken, startedAt, upstreamPort },
|
|
93
|
+
launch: { backend, name, instanceId, launchId, controlToken, startedAt, upstreamPort },
|
|
87
94
|
};
|
|
88
95
|
}
|
|
89
96
|
|
|
@@ -102,3 +109,4 @@ export function parseManagedInstanceShutdownMessage(
|
|
|
102
109
|
? { type: MANAGED_INSTANCE_SHUTDOWN_MESSAGE_TYPE, controlToken: value["controlToken"] }
|
|
103
110
|
: null;
|
|
104
111
|
}
|
|
112
|
+
import { parseBackendImplementation, type BackendImplementation } from "./backendImplementation";
|