@tonyclaw/agent-inspector 3.0.47 → 3.1.0

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.
Files changed (29) hide show
  1. package/.output/backend/nitro.json +1 -1
  2. package/.output/cli.js +1855 -1758
  3. package/.output/server/_ssr/index.mjs +1 -1
  4. package/.output/server/_ssr/{router-BwZEsYfS.mjs → router-CPZp1dgy.mjs} +304 -2
  5. package/.output/server/index.mjs +1 -1
  6. package/.output/ui/assets/{CompareDrawer-V-8lPASC.js → CompareDrawer-CkABWY5h.js} +1 -1
  7. package/.output/ui/assets/{InspectorPet-D4CVnfdf.js → InspectorPet-BvsFl3jv.js} +1 -1
  8. package/.output/ui/assets/{ProxyViewerContainer-CO3ZVHLX.js → ProxyViewerContainer-D6O6tY5h.js} +23 -23
  9. package/.output/ui/assets/{ReplayDialog-CpGyTLtt.js → ReplayDialog-uJnW9FgF.js} +1 -1
  10. package/.output/ui/assets/{RequestAnatomy-BLuJL9dZ.js → RequestAnatomy-CJ1IFwXf.js} +1 -1
  11. package/.output/ui/assets/{ResponseView-BuSC-CH7.js → ResponseView-Da8fKfh6.js} +1 -1
  12. package/.output/ui/assets/{StreamingChunkSequence-DeA-JJKa.js → StreamingChunkSequence-zIZ1fbEj.js} +1 -1
  13. package/.output/ui/assets/{_sessionId-Chp67th6.js → _sessionId-CY9mZjlC.js} +1 -1
  14. package/.output/ui/assets/{_sessionId-8HuHFhjo.js → _sessionId-WYeJ04Ke.js} +1 -1
  15. package/.output/ui/assets/{index-atoQrlb8.js → index-Bzm_5L4l.js} +1 -1
  16. package/.output/ui/assets/{index-nY3E1vxV.js → index-C2AvphEC.js} +2 -2
  17. package/.output/ui/assets/{index-DPmKK_5d.js → index-CwOAPU17.js} +1 -1
  18. package/.output/ui/assets/{index-nl9-dpZn.js → index-DWI-2j8i.js} +1 -1
  19. package/.output/ui/assets/{json-viewer-B9kIzqeX.js → json-viewer-C9EpwpoT.js} +1 -1
  20. package/.output/ui/assets/{jszip.min-B7tfyE-I.js → jszip.min-CEk40IyV.js} +1 -1
  21. package/.output/ui/index.html +1 -1
  22. package/README.md +46 -0
  23. package/package.json +4 -4
  24. package/src/cli/instance.ts +12 -1
  25. package/src/cli/instanceArgs.ts +1 -1
  26. package/src/cli/rustBackendPackage.ts +8 -0
  27. package/src/cli.ts +108 -9
  28. package/src/components/ProxyViewer.tsx +4 -13
  29. package/src/components/proxy-viewer/proxyViewerLogic.ts +9 -0
package/src/cli.ts CHANGED
@@ -4,7 +4,7 @@ import { spawn, type ChildProcess } from "node:child_process";
4
4
  import { createConnection } from "node:net";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { dirname, join, resolve as resolvePath } from "node:path";
7
- import { existsSync } from "node:fs";
7
+ import { appendFileSync, existsSync, mkdirSync } from "node:fs";
8
8
  import type { Readable, Writable } from "node:stream";
9
9
  import packageJson from "../package.json";
10
10
  import {
@@ -40,7 +40,10 @@ import {
40
40
  shouldSuppressServerOutputLine,
41
41
  type ServerOutputFilterContext,
42
42
  } from "./cli/startupOutput.js";
43
- import { resolveRustBackendPackage } from "./cli/rustBackendPackage.js";
43
+ import {
44
+ resolvePreferredBackendImplementation,
45
+ resolveRustBackendPackage,
46
+ } from "./cli/rustBackendPackage.js";
44
47
  import { handleNamedInstanceApi } from "./cli/instanceApi.js";
45
48
  import {
46
49
  closeIdentityProxyGracefully,
@@ -51,6 +54,7 @@ import {
51
54
  type ManagedInstanceControlOptions,
52
55
  } from "./proxy/identityProxy.js";
53
56
  import { findPidsByPort, killPid, openUrlCommand } from "./proxy/platformCommands.js";
57
+ import { resolveDataDir } from "./proxy/dataDir.js";
54
58
 
55
59
  const __filename = fileURLToPath(import.meta.url);
56
60
  const __dirname = dirname(__filename);
@@ -68,6 +72,7 @@ const BRANDED_WINDOWS_RUNTIME_EXE = "agent-inspector.exe";
68
72
  const DEFAULT_CAPTURE_MODE = "simple";
69
73
  const DEFAULT_SHUTDOWN_DEADLINE_MS = 15_000;
70
74
  const SUPERVISOR_SHUTDOWN_GRACE_MS = 2_000;
75
+ const AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV = "AGENT_INSPECTOR_BACKGROUND_SUPERVISOR";
71
76
 
72
77
  type CaptureMode = "simple" | "full";
73
78
 
@@ -96,7 +101,7 @@ Global options:
96
101
  -v, --version Print the package version
97
102
 
98
103
  Start options:
99
- --backend <typescript|rust> Select the backend implementation (default: typescript)
104
+ --backend <typescript|rust> Select the backend implementation (default: rust when native package is available)
100
105
  --backend-only, --headless Start the API, proxy, and MCP runtime without serving the UI
101
106
  --with-ui Serve the independently built UI through the protected ingress
102
107
  --port, -p <port> Public ingress port (default: 9527)
@@ -298,6 +303,30 @@ function pipeServerOutput(child: ChildProcess, context: ServerOutputFilterContex
298
303
  pipeServerOutputStream(child.stderr, process.stderr, context);
299
304
  }
300
305
 
306
+ type BackgroundSupervisorLogs = {
307
+ stdoutPath: string;
308
+ stderrPath: string;
309
+ };
310
+
311
+ function createBackgroundSupervisorLogs(
312
+ env: Readonly<Record<string, string | undefined>>,
313
+ ): BackgroundSupervisorLogs | null {
314
+ try {
315
+ const logsDir = join(resolveDataDir(existsSync, env), "logs");
316
+ mkdirSync(logsDir, { recursive: true });
317
+ const stdoutPath = join(logsDir, "backend.stdout.log");
318
+ const stderrPath = join(logsDir, "backend.stderr.log");
319
+ const header = `\n[${new Date().toISOString()}] Agent Inspector backend process started\n`;
320
+ appendFileSync(stdoutPath, header, { encoding: "utf8" });
321
+ appendFileSync(stderrPath, header, { encoding: "utf8" });
322
+ return { stdoutPath, stderrPath };
323
+ } catch (error) {
324
+ const message = error instanceof Error ? error.message : String(error);
325
+ process.stderr.write(`Failed to open Agent Inspector background logs: ${message}\n`);
326
+ return null;
327
+ }
328
+ }
329
+
301
330
  function pipeServerOutputStream(
302
331
  stream: Readable | null,
303
332
  target: Writable,
@@ -325,6 +354,42 @@ function pipeServerOutputStream(
325
354
  });
326
355
  }
327
356
 
357
+ function appendBackgroundLog(path: string, text: string): void {
358
+ try {
359
+ appendFileSync(path, text, { encoding: "utf8" });
360
+ } catch {
361
+ // Background supervisor output is best-effort: startup must not fail just
362
+ // because a diagnostic log file is temporarily unavailable.
363
+ }
364
+ }
365
+
366
+ function pipeServerOutputStreamToFile(
367
+ stream: Readable | null,
368
+ targetPath: string,
369
+ context: ServerOutputFilterContext,
370
+ ): void {
371
+ if (stream === null) return;
372
+
373
+ let buffered = "";
374
+ stream.on("data", (chunk: unknown) => {
375
+ const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
376
+ const lines = `${buffered}${text}`.split(/\r?\n/);
377
+ buffered = lines.pop() ?? "";
378
+ for (const line of lines) {
379
+ if (!shouldSuppressServerOutputLine(line, context)) {
380
+ appendBackgroundLog(targetPath, `${line}\n`);
381
+ }
382
+ }
383
+ });
384
+ stream.on("end", () => {
385
+ if (buffered.length === 0) return;
386
+ if (!shouldSuppressServerOutputLine(buffered, context)) {
387
+ appendBackgroundLog(targetPath, buffered);
388
+ }
389
+ buffered = "";
390
+ });
391
+ }
392
+
328
393
  type ServerCommand = {
329
394
  command: string;
330
395
  args: string[];
@@ -568,7 +633,9 @@ async function runStart(args: string[]): Promise<void> {
568
633
  const envHost = process.env["NITRO_HOST"] ?? process.env["HOST"];
569
634
  const envMode =
570
635
  process.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? process.env["AGENT_INSPECTOR_MODE"];
571
- const envBackend = parseBackendImplementation(process.env[AGENT_INSPECTOR_BACKEND_ENV]);
636
+ const rawEnvBackend = process.env[AGENT_INSPECTOR_BACKEND_ENV];
637
+ const envBackend =
638
+ rawEnvBackend === undefined ? undefined : parseBackendImplementation(rawEnvBackend);
572
639
  if (envBackend === null) {
573
640
  console.error(
574
641
  `Invalid ${AGENT_INSPECTOR_BACKEND_ENV}. Use typescript or rust; no backend was started.`,
@@ -591,7 +658,8 @@ async function runStart(args: string[]): Promise<void> {
591
658
  let enableIdentityProxy = true;
592
659
  let legacyAliasEnabled = false;
593
660
  let serveUi = process.env["AGENT_INSPECTOR_BACKEND_ONLY"] !== "1";
594
- let backend: BackendImplementation = envBackend;
661
+ let backend: BackendImplementation = envBackend ?? "typescript";
662
+ let backendWasSpecified = envBackend !== undefined;
595
663
 
596
664
  if (envMode !== undefined && envMode !== "") {
597
665
  const parsedMode = parseCaptureMode(envMode);
@@ -651,6 +719,7 @@ async function runStart(args: string[]): Promise<void> {
651
719
  return;
652
720
  }
653
721
  backend = parsedBackend.data;
722
+ backendWasSpecified = true;
654
723
  continue;
655
724
  }
656
725
  switch (arg) {
@@ -681,6 +750,7 @@ async function runStart(args: string[]): Promise<void> {
681
750
  return;
682
751
  }
683
752
  backend = parsedBackend.data;
753
+ backendWasSpecified = true;
684
754
  i++;
685
755
  break;
686
756
  }
@@ -772,6 +842,12 @@ async function runStart(args: string[]): Promise<void> {
772
842
  return;
773
843
  }
774
844
 
845
+ if (!backendWasSpecified) {
846
+ backend =
847
+ managedLaunch?.backend ??
848
+ resolvePreferredBackendImplementation({ rootVersion: packageJson.version });
849
+ }
850
+
775
851
  if (managedLaunch !== null && managedLaunch.backend !== backend) {
776
852
  console.error(
777
853
  `Managed instance backend mismatch: launch identity requires ${managedLaunch.backend}, but ${backend} was selected.`,
@@ -967,7 +1043,11 @@ async function runStart(args: string[]): Promise<void> {
967
1043
  const supervisorProcess = spawn(process.execPath, [cliEntry, ...supervisorArgs], {
968
1044
  stdio: "ignore",
969
1045
  detached: true,
970
- env: { ...process.env, [AGENT_INSPECTOR_CLI_ENTRY_ENV]: cliEntry },
1046
+ env: {
1047
+ ...process.env,
1048
+ [AGENT_INSPECTOR_CLI_ENTRY_ENV]: cliEntry,
1049
+ [AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV]: "1",
1050
+ },
971
1051
  windowsHide: true,
972
1052
  });
973
1053
  supervisorProcess.once("error", (err) => {
@@ -1042,6 +1122,11 @@ async function runStart(args: string[]): Promise<void> {
1042
1122
  if (existsSync(workerDir)) {
1043
1123
  serverEnv["AGENT_INSPECTOR_WORKER_DIR"] = workerDir;
1044
1124
  }
1125
+ const runningInBackgroundSupervisor =
1126
+ process.env[AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV] === "1";
1127
+ const backgroundSupervisorLogs = runningInBackgroundSupervisor
1128
+ ? createBackgroundSupervisorLogs(serverEnv)
1129
+ : null;
1045
1130
  const serverProcess = spawn(serverCommand.command, serverCommand.args, {
1046
1131
  stdio:
1047
1132
  managedLaunch === null
@@ -1057,18 +1142,32 @@ async function runStart(args: string[]): Promise<void> {
1057
1142
  : ["ignore", "pipe", "pipe"],
1058
1143
  detached: background,
1059
1144
  env: serverEnv,
1060
- windowsHide: background,
1145
+ windowsHide: background || runningInBackgroundSupervisor,
1061
1146
  });
1062
1147
  serverProcess.once("error", (err) => {
1063
1148
  console.error(`Failed to start Agent Inspector runtime: ${err.message}`);
1064
1149
  });
1065
1150
  const serverExit = waitForProcessExit(serverProcess);
1066
1151
  if (!background) {
1067
- pipeServerOutput(serverProcess, {
1152
+ const outputContext = {
1068
1153
  enableIdentityProxy,
1069
1154
  publicPort: port,
1070
1155
  upstreamPort,
1071
- });
1156
+ };
1157
+ if (backgroundSupervisorLogs === null) {
1158
+ pipeServerOutput(serverProcess, outputContext);
1159
+ } else {
1160
+ pipeServerOutputStreamToFile(
1161
+ serverProcess.stdout,
1162
+ backgroundSupervisorLogs.stdoutPath,
1163
+ outputContext,
1164
+ );
1165
+ pipeServerOutputStreamToFile(
1166
+ serverProcess.stderr,
1167
+ backgroundSupervisorLogs.stderrPath,
1168
+ outputContext,
1169
+ );
1170
+ }
1072
1171
  }
1073
1172
 
1074
1173
  // Identity proxy — a native Node HTTP server that sits in front of
@@ -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
- <CopyableCommand
2152
- command={`ANTHROPIC_BASE_URL=${connectionEndpoints.proxy} <your-tool>`}
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;