@tonyclaw/agent-inspector 2.1.24 → 3.0.1

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 (37) hide show
  1. package/.output/cli.js +85 -30
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-DsHSCHSx.js → CompareDrawer-BgcAk1te.js} +1 -1
  4. package/.output/public/assets/{ProxyViewerContainer-GI3xErl5.js → ProxyViewerContainer-7b_uwvT7.js} +6 -6
  5. package/.output/public/assets/{ReplayDialog-D0ixqStX.js → ReplayDialog-7Pvhjbjk.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-BG5mb14k.js → RequestAnatomy-BX-UKYQg.js} +1 -1
  7. package/.output/public/assets/{ResponseView-Bgns6xum.js → ResponseView-zFxGX8xH.js} +2 -2
  8. package/.output/public/assets/{StreamingChunkSequence-BeqKXAS6.js → StreamingChunkSequence-DzXzVtBF.js} +1 -1
  9. package/.output/public/assets/{_sessionId-Nme0kuTf.js → _sessionId-CmsUOEIS.js} +1 -1
  10. package/.output/public/assets/{index-hbDdeKtc.js → index-DXR7RB48.js} +1 -1
  11. package/.output/public/assets/index-MfTE52Ty.js +1 -0
  12. package/.output/public/assets/{json-viewer-CWxF2eLV.js → json-viewer-CiMN_9sG.js} +1 -1
  13. package/.output/public/assets/{main-BDfcCp_1.js → main-Ba9x64uY.js} +2 -2
  14. package/.output/server/{_sessionId-DOHtZHvJ.mjs → _sessionId-B8GZtC4P.mjs} +2 -2
  15. package/.output/server/_ssr/{CompareDrawer-HMfALocK.mjs → CompareDrawer-SBDtZDAp.mjs} +2 -2
  16. package/.output/server/_ssr/{ProxyViewerContainer-DgnyaJPd.mjs → ProxyViewerContainer-B__2O0zh.mjs} +12 -12
  17. package/.output/server/_ssr/{ReplayDialog-DyIQdJHE.mjs → ReplayDialog-D0u1VHz_.mjs} +3 -3
  18. package/.output/server/_ssr/{RequestAnatomy-8v4e8mYT.mjs → RequestAnatomy-ClvQ3RFh.mjs} +2 -2
  19. package/.output/server/_ssr/{ResponseView-D17WnkXv.mjs → ResponseView-B4UAvUym.mjs} +2 -2
  20. package/.output/server/_ssr/{StreamingChunkSequence-CALVBf-R.mjs → StreamingChunkSequence-D_aiGGzz.mjs} +2 -2
  21. package/.output/server/_ssr/{index-D8EDemF-.mjs → index-DuWacKr6.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-BgrtJGqX.mjs → json-viewer-CmNMVinH.mjs} +2 -2
  24. package/.output/server/_ssr/{router-D3qLyERI.mjs → router-BP46kVrE.mjs} +30 -5
  25. package/.output/server/{_tanstack-start-manifest_v-6qZpokPP.mjs → _tanstack-start-manifest_v-DltuTRfm.mjs} +1 -1
  26. package/.output/server/index.mjs +61 -61
  27. package/README.md +30 -8
  28. package/package.json +1 -1
  29. package/src/cli/doctor.ts +1 -1
  30. package/src/cli/onboard.ts +15 -9
  31. package/src/cli.ts +96 -34
  32. package/src/components/ProxyViewer.tsx +2 -2
  33. package/src/components/providers/SettingsDialog.tsx +2 -2
  34. package/src/lib/serverPort.ts +9 -3
  35. package/src/proxy/sessionProcess.ts +22 -1
  36. package/src/proxy/socketTracker.ts +1 -1
  37. package/.output/public/assets/index-CUKpd9jb.js +0 -1
@@ -38,114 +38,114 @@ const assets = {
38
38
  "/assets/alibaba-TTwafVwX.svg": {
39
39
  "type": "image/svg+xml",
40
40
  "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
41
- "mtime": "2026-07-06T23:47:30.045Z",
41
+ "mtime": "2026-07-07T00:47:56.086Z",
42
42
  "size": 5915,
43
43
  "path": "../public/assets/alibaba-TTwafVwX.svg"
44
44
  },
45
- "/assets/index-CUKpd9jb.js": {
45
+ "/assets/CompareDrawer-BgcAk1te.js": {
46
46
  "type": "text/javascript; charset=utf-8",
47
- "etag": '"74-S5coP0OJAkI32YF22K1A7pIhhaQ"',
48
- "mtime": "2026-07-06T23:47:30.045Z",
49
- "size": 116,
50
- "path": "../public/assets/index-CUKpd9jb.js"
47
+ "etag": '"4bbf-gA9nBXsloTEIMhmM3ql/4qNmhOs"',
48
+ "mtime": "2026-07-07T00:47:56.086Z",
49
+ "size": 19391,
50
+ "path": "../public/assets/CompareDrawer-BgcAk1te.js"
51
51
  },
52
- "/assets/json-viewer-CWxF2eLV.js": {
52
+ "/assets/index-DXR7RB48.js": {
53
53
  "type": "text/javascript; charset=utf-8",
54
- "etag": '"1ae6-W5gyZM3eWBF1xbeKMyl4smbFfEM"',
55
- "mtime": "2026-07-06T23:47:30.047Z",
56
- "size": 6886,
57
- "path": "../public/assets/json-viewer-CWxF2eLV.js"
54
+ "etag": '"1cd2c-3SEnnZb993yst8Dzg9UbKuoON3A"',
55
+ "mtime": "2026-07-07T00:47:56.086Z",
56
+ "size": 118060,
57
+ "path": "../public/assets/index-DXR7RB48.js"
58
58
  },
59
- "/assets/CompareDrawer-DsHSCHSx.js": {
59
+ "/assets/json-viewer-CiMN_9sG.js": {
60
60
  "type": "text/javascript; charset=utf-8",
61
- "etag": '"4bbf-nlyV9TgErKS/2wxRBsjCrr8IQBo"',
62
- "mtime": "2026-07-06T23:47:30.047Z",
63
- "size": 19391,
64
- "path": "../public/assets/CompareDrawer-DsHSCHSx.js"
61
+ "etag": '"1ae6-uMTpz3tfSXntmUuk5MzHyqBxhWI"',
62
+ "mtime": "2026-07-07T00:47:56.086Z",
63
+ "size": 6886,
64
+ "path": "../public/assets/json-viewer-CiMN_9sG.js"
65
65
  },
66
66
  "/assets/index-Tq5s9NV0.css": {
67
67
  "type": "text/css; charset=utf-8",
68
68
  "etag": '"22408-MQKT02eqQudVEj/DozyiEJoTl6E"',
69
- "mtime": "2026-07-06T23:47:30.045Z",
69
+ "mtime": "2026-07-07T00:47:56.086Z",
70
70
  "size": 140296,
71
71
  "path": "../public/assets/index-Tq5s9NV0.css"
72
72
  },
73
73
  "/assets/minimax-BPMzvuL-.jpeg": {
74
74
  "type": "image/jpeg",
75
75
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
76
- "mtime": "2026-07-06T23:47:30.042Z",
76
+ "mtime": "2026-07-07T00:47:56.084Z",
77
77
  "size": 6918,
78
78
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
79
79
  },
80
- "/assets/RequestAnatomy-BG5mb14k.js": {
80
+ "/assets/index-MfTE52Ty.js": {
81
81
  "type": "text/javascript; charset=utf-8",
82
- "etag": '"43f2-5+JmeO4mEgbfXx4qZ0z/5j/kGC0"',
83
- "mtime": "2026-07-06T23:47:30.047Z",
84
- "size": 17394,
85
- "path": "../public/assets/RequestAnatomy-BG5mb14k.js"
82
+ "etag": '"74-xI8cgjppS3YpgGFVqBj/+DUVe/4"',
83
+ "mtime": "2026-07-07T00:47:56.086Z",
84
+ "size": 116,
85
+ "path": "../public/assets/index-MfTE52Ty.js"
86
+ },
87
+ "/assets/ReplayDialog-7Pvhjbjk.js": {
88
+ "type": "text/javascript; charset=utf-8",
89
+ "etag": '"31d6-WfysEtvfuOApCBk5olHqKk8rMfU"',
90
+ "mtime": "2026-07-07T00:47:56.086Z",
91
+ "size": 12758,
92
+ "path": "../public/assets/ReplayDialog-7Pvhjbjk.js"
86
93
  },
87
- "/assets/StreamingChunkSequence-BeqKXAS6.js": {
94
+ "/assets/main-Ba9x64uY.js": {
88
95
  "type": "text/javascript; charset=utf-8",
89
- "etag": '"dd6-+UuLiAyXfMce8K34YDVdE2+W+dQ"',
90
- "mtime": "2026-07-06T23:47:30.047Z",
96
+ "etag": '"512fd-xrDcMUYYndBJtS06eXJ9QM25lz4"',
97
+ "mtime": "2026-07-07T00:47:56.086Z",
98
+ "size": 332541,
99
+ "path": "../public/assets/main-Ba9x64uY.js"
100
+ },
101
+ "/assets/StreamingChunkSequence-DzXzVtBF.js": {
102
+ "type": "text/javascript; charset=utf-8",
103
+ "etag": '"dd6-FSNaNigsd1X+gyqALfVxeG2s4fo"',
104
+ "mtime": "2026-07-07T00:47:56.086Z",
91
105
  "size": 3542,
92
- "path": "../public/assets/StreamingChunkSequence-BeqKXAS6.js"
106
+ "path": "../public/assets/StreamingChunkSequence-DzXzVtBF.js"
93
107
  },
94
108
  "/assets/zhipuai-BPNAnxo-.svg": {
95
109
  "type": "image/svg+xml",
96
110
  "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
97
- "mtime": "2026-07-06T23:47:30.045Z",
111
+ "mtime": "2026-07-07T00:47:56.086Z",
98
112
  "size": 11256,
99
113
  "path": "../public/assets/zhipuai-BPNAnxo-.svg"
100
114
  },
101
- "/assets/main-BDfcCp_1.js": {
102
- "type": "text/javascript; charset=utf-8",
103
- "etag": '"512fd-wNI+QxyzwhA/0QqJbbcpb3MMxmg"',
104
- "mtime": "2026-07-06T23:47:30.047Z",
105
- "size": 332541,
106
- "path": "../public/assets/main-BDfcCp_1.js"
107
- },
108
- "/assets/ReplayDialog-D0ixqStX.js": {
115
+ "/assets/RequestAnatomy-BX-UKYQg.js": {
109
116
  "type": "text/javascript; charset=utf-8",
110
- "etag": '"31d6-kYb7AI9zYbXu6Y118c89CIIKsQk"',
111
- "mtime": "2026-07-06T23:47:30.047Z",
112
- "size": 12758,
113
- "path": "../public/assets/ReplayDialog-D0ixqStX.js"
117
+ "etag": '"43f2-Zq3l43VsakNoYmpaf2yidbFacQg"',
118
+ "mtime": "2026-07-07T00:47:56.086Z",
119
+ "size": 17394,
120
+ "path": "../public/assets/RequestAnatomy-BX-UKYQg.js"
114
121
  },
115
- "/assets/ResponseView-Bgns6xum.js": {
122
+ "/assets/ResponseView-zFxGX8xH.js": {
116
123
  "type": "text/javascript; charset=utf-8",
117
- "etag": '"47fa-9tCNwF7F78L1zRC3UidiQKud7L8"',
118
- "mtime": "2026-07-06T23:47:30.047Z",
124
+ "etag": '"47fa-7HB1lxdpkTuo7BEzpS+JAr6zRjM"',
125
+ "mtime": "2026-07-07T00:47:56.086Z",
119
126
  "size": 18426,
120
- "path": "../public/assets/ResponseView-Bgns6xum.js"
127
+ "path": "../public/assets/ResponseView-zFxGX8xH.js"
121
128
  },
122
- "/assets/_sessionId-Nme0kuTf.js": {
129
+ "/assets/_sessionId-CmsUOEIS.js": {
123
130
  "type": "text/javascript; charset=utf-8",
124
- "etag": '"36c-gpP2xqLaqXgb4GOWSbhVDrqjv3g"',
125
- "mtime": "2026-07-06T23:47:30.045Z",
131
+ "etag": '"36c-oBFmeA36vW57pay3tX7/Tm7ZnGI"',
132
+ "mtime": "2026-07-07T00:47:56.086Z",
126
133
  "size": 876,
127
- "path": "../public/assets/_sessionId-Nme0kuTf.js"
134
+ "path": "../public/assets/_sessionId-CmsUOEIS.js"
128
135
  },
129
136
  "/assets/qwen-CONDcHqt.png": {
130
137
  "type": "image/png",
131
138
  "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
132
- "mtime": "2026-07-06T23:47:30.045Z",
139
+ "mtime": "2026-07-07T00:47:56.086Z",
133
140
  "size": 357059,
134
141
  "path": "../public/assets/qwen-CONDcHqt.png"
135
142
  },
136
- "/assets/index-hbDdeKtc.js": {
137
- "type": "text/javascript; charset=utf-8",
138
- "etag": '"1cd2c-o4cSL+ZBVABZPPb9xrCnibEnQQQ"',
139
- "mtime": "2026-07-06T23:47:30.047Z",
140
- "size": 118060,
141
- "path": "../public/assets/index-hbDdeKtc.js"
142
- },
143
- "/assets/ProxyViewerContainer-GI3xErl5.js": {
143
+ "/assets/ProxyViewerContainer-7b_uwvT7.js": {
144
144
  "type": "text/javascript; charset=utf-8",
145
- "etag": '"aef16-1fUWo2IWu+2OgSdsAPCGVYmzUNU"',
146
- "mtime": "2026-07-06T23:47:30.047Z",
147
- "size": 716566,
148
- "path": "../public/assets/ProxyViewerContainer-GI3xErl5.js"
145
+ "etag": '"aef11-9vtbsodrImAt3S4nrgSZgSQRdnc"',
146
+ "mtime": "2026-07-07T00:47:56.086Z",
147
+ "size": 716561,
148
+ "path": "../public/assets/ProxyViewerContainer-7b_uwvT7.js"
149
149
  }
150
150
  };
151
151
  function readAsset(id) {
package/README.md CHANGED
@@ -62,13 +62,13 @@ The onboarding command installs guided setup skills for local agents:
62
62
 
63
63
  - Claude Code: `~/.claude/skills/agent-inspector-onboard/SKILL.md` plus a slash command.
64
64
  - Codex: `~/.codex/skills/agent-inspector-onboard/SKILL.md`, focused on connecting
65
- `http://localhost:25947/api/mcp` through Codex `mcp_servers.agent-inspector`.
65
+ `http://localhost:9527/api/mcp` through Codex `mcp_servers.agent-inspector`.
66
66
  - OpenCode: run `agent-inspector onboard --opencode-only` to merge
67
67
  `mcp.agent-inspector` into `~/.config/opencode/opencode.json` or `opencode.jsonc`, then verify
68
68
  with `opencode mcp list`.
69
69
  - MiMo Code: run `agent-inspector onboard --mimo-only` to merge `mcp.agent-inspector` into
70
70
  `~/.config/mimocode/mimocode.jsonc` or `mimocode.json`, then route MiMo through the proxy with
71
- `OPENAI_BASE_URL=http://localhost:25947/proxy`.
71
+ `OPENAI_BASE_URL=http://localhost:9527/proxy`.
72
72
 
73
73
  During npm global install, Agent Inspector also makes a best-effort onboarding skill install for
74
74
  detected local agents. If `~/.claude` exists, it installs the Claude Code skill and slash command.
@@ -123,20 +123,39 @@ agent-inspector doctor --chrome-extension
123
123
  Then point an AI coding tool at the proxy:
124
124
 
125
125
  ```bash
126
- ANTHROPIC_BASE_URL=http://localhost:25947/proxy <tool>
126
+ ANTHROPIC_BASE_URL=http://localhost:9527/proxy <tool>
127
127
  ```
128
128
 
129
- The web UI runs at http://localhost:25947. The public proxy endpoint is also
130
- `http://localhost:25947/proxy`; internally Agent Inspector may run the app on a private helper port,
129
+ The web UI runs at http://localhost:9527. The public proxy endpoint is also
130
+ `http://localhost:9527/proxy`; internally Agent Inspector may run the app on a private helper port,
131
131
  but AI tools should use the public URL printed by the CLI.
132
132
 
133
+ For v3 compatibility, the packaged CLI also keeps `http://localhost:25947` open as a legacy alias
134
+ when it starts on the default port. Existing AI tool and MCP configs that still point at `25947`
135
+ continue to work for now, but new configs should use `9527`.
136
+
137
+ ## v3.0 Port Migration
138
+
139
+ Agent Inspector v3.0 changes the default public port from `25947` to `9527`. The packaged CLI keeps
140
+ `25947` as a legacy alias when you run the default `agent-inspector` command, so existing AI coding
141
+ tool and MCP configs do not need to be edited immediately.
142
+
143
+ Use these endpoints for new configs:
144
+
145
+ - Web UI: `http://localhost:9527`
146
+ - Proxy: `http://localhost:9527/proxy`
147
+ - OpenAI-compatible `/v1` proxy: `http://localhost:9527/proxy/v1`
148
+ - MCP: `http://localhost:9527/api/mcp`
149
+
150
+ If you intentionally want only the new port, start with `agent-inspector --no-legacy-port`.
151
+
133
152
  For Codex-style OpenAI Responses clients, configure the proxy as an OpenAI-compatible `/v1` base:
134
153
 
135
154
  ```toml
136
155
  model_provider = "agent-inspector"
137
156
 
138
157
  [model_providers."agent-inspector"]
139
- base_url = "http://127.0.0.1:25947/proxy/v1"
158
+ base_url = "http://127.0.0.1:9527/proxy/v1"
140
159
  wire_api = "responses"
141
160
  ```
142
161
 
@@ -226,7 +245,7 @@ upstream URL failed.
226
245
  Agent Inspector exposes MCP at:
227
246
 
228
247
  ```text
229
- http://localhost:25947/api/mcp
248
+ http://localhost:9527/api/mcp
230
249
  ```
231
250
 
232
251
  Coding agents can connect to this MCP surface directly over Streamable HTTP, or through the
@@ -244,10 +263,13 @@ The MCP HTTP surface is also documented as OpenAPI YAML in
244
263
  [`docs/mcp-openapi.yaml`](docs/mcp-openapi.yaml).
245
264
 
246
265
  Use only one transport for the same Inspector instance. If the MCP client supports Streamable HTTP,
247
- configure `http://localhost:25947/api/mcp` directly. If it only supports stdio, configure
266
+ configure `http://localhost:9527/api/mcp` directly. If it only supports stdio, configure
248
267
  `agent-inspector-mcp stdio`; that command is only a bridge to `/api/mcp`, not a second Inspector
249
268
  server.
250
269
 
270
+ Legacy MCP configs that still use `http://localhost:25947/api/mcp` are accepted by the default v3
271
+ CLI alias during the compatibility window.
272
+
251
273
  Coding agents can use the MCP surface in three ways:
252
274
 
253
275
  - Tools: perform actions such as listing logs, reading bounded log details, testing providers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/agent-inspector",
3
- "version": "2.1.24",
3
+ "version": "3.0.1",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
package/src/cli/doctor.ts CHANGED
@@ -3,7 +3,7 @@ import { isAbsolute, join, resolve } from "node:path";
3
3
  import { createConnection } from "node:net";
4
4
  import { migrateLegacyDataDirs, resolveDataDir } from "../proxy/dataDir";
5
5
 
6
- const DEFAULT_PORT = 25947;
6
+ const DEFAULT_PORT = 9527;
7
7
  const PROBE_TIMEOUT_MS = 2000;
8
8
  const EXTENSION_REQUIRED_FILES = [
9
9
  "manifest.json",
@@ -35,8 +35,9 @@ import { renderSkillOnboard, REQUIRED_PHASE_HEADINGS } from "./templates/skill-o
35
35
  const __filename = fileURLToPath(import.meta.url);
36
36
  const __dirname = dirname(__filename);
37
37
 
38
- const DEFAULT_PORT = 25947;
38
+ const DEFAULT_PORT = 9527;
39
39
  const DEFAULT_MCP_URL = `http://localhost:${DEFAULT_PORT}/api/mcp`;
40
+ const LEGACY_MCP_URL = "http://localhost:25947/api/mcp";
40
41
  const SKILL_DIR_NAME = "agent-inspector-onboard";
41
42
  const SKILL_FILE_NAME = "SKILL.md";
42
43
  // Windows reserves `:` for NTFS alternate data streams, so a file named
@@ -630,6 +631,10 @@ function stringArrayEquals(value: unknown, expected: readonly string[]): boolean
630
631
  return value.every((item, index) => item === expected[index]);
631
632
  }
632
633
 
634
+ function knownMcpEndpoints(endpoint: string): readonly string[] {
635
+ return endpoint === DEFAULT_MCP_URL ? [DEFAULT_MCP_URL, LEGACY_MCP_URL] : [endpoint];
636
+ }
637
+
633
638
  function isAgentLocalEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
634
639
  if (!isObject(value)) {
635
640
  return false;
@@ -641,16 +646,14 @@ function isAgentLocalEntry(value: unknown, endpoint: string, ignoreEnabled: bool
641
646
  }
642
647
 
643
648
  const env = value["environment"];
644
- const usesUrlArg = stringArrayEquals(value["command"], [
645
- "agent-inspector-mcp",
646
- "stdio",
647
- "--url",
648
- endpoint,
649
- ]);
649
+ const endpoints = knownMcpEndpoints(endpoint);
650
+ const usesUrlArg = endpoints.some((candidate) =>
651
+ stringArrayEquals(value["command"], ["agent-inspector-mcp", "stdio", "--url", candidate]),
652
+ );
650
653
  const usesEnv =
651
654
  stringArrayEquals(value["command"], ["agent-inspector-mcp", "stdio"]) &&
652
655
  isObject(env) &&
653
- env["AGENT_INSPECTOR_MCP_URL"] === endpoint;
656
+ endpoints.some((candidate) => env["AGENT_INSPECTOR_MCP_URL"] === candidate);
654
657
 
655
658
  return value["type"] === "local" && (usesUrlArg || usesEnv);
656
659
  }
@@ -665,7 +668,10 @@ function isAgentRemoteEntry(value: unknown, endpoint: string, ignoreEnabled: boo
665
668
  return false;
666
669
  }
667
670
 
668
- return value["type"] === "remote" && value["url"] === endpoint;
671
+ return (
672
+ value["type"] === "remote" &&
673
+ knownMcpEndpoints(endpoint).some((candidate) => value["url"] === candidate)
674
+ );
669
675
  }
670
676
 
671
677
  function isKnownAgentEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
package/src/cli.ts CHANGED
@@ -23,14 +23,13 @@ import { startIdentityProxy } from "./proxy/identityProxy.js";
23
23
  const __filename = fileURLToPath(import.meta.url);
24
24
  const __dirname = dirname(__filename);
25
25
 
26
- // External-facing port stays at 25947 so existing tooling / AI clients
27
- // (Claude Code, OpenCode, MiMo Code, etc.) keep working with the
28
- // unchanged `ANTHROPIC_BASE_URL=http://localhost:25947/proxy`. The
29
- // identity-injecting proxy listens on that port; the TanStack Start
30
- // upstream is moved to a private port (25949 by default) and reverse-
31
- // proxied through.
32
- const DEFAULT_PORT = 25947;
33
- const DEFAULT_UPSTREAM_PORT = 25949;
26
+ // v3 defaults the public entrypoint to 9527 while keeping 25947 as a
27
+ // compatibility alias for older AI-tool and MCP configs. The TanStack Start
28
+ // runtime stays on a private upstream port and both public ports reverse-proxy
29
+ // to that same runtime, so logs, sessions, and MCP state remain unified.
30
+ const DEFAULT_PORT = 9527;
31
+ const LEGACY_ALIAS_PORT = 25947;
32
+ const DEFAULT_UPSTREAM_PORT = 9529;
34
33
  const LOCAL_PROBE_TIMEOUT_MS = 2000;
35
34
  const BRANDED_WINDOWS_RUNTIME_EXE = "agent-inspector.exe";
36
35
  const DEFAULT_CAPTURE_MODE = "simple";
@@ -159,6 +158,10 @@ function openBrowser(targetUrl: string): void {
159
158
  detached: true,
160
159
  windowsHide: true,
161
160
  });
161
+ browserProcess.once("error", () => {
162
+ // Opening a browser is best-effort. Minimal Linux/container images may not
163
+ // include xdg-open, and that should not make the CLI look unhealthy.
164
+ });
162
165
  browserProcess.unref();
163
166
  }
164
167
 
@@ -244,6 +247,26 @@ function printAccessHints(port: number, host?: string): void {
244
247
  }
245
248
  }
246
249
 
250
+ function pickUpstreamPort(publicPort: number, legacyAliasPort: number | null): number {
251
+ const candidates = [
252
+ DEFAULT_UPSTREAM_PORT,
253
+ publicPort + 2 <= 65535 ? publicPort + 2 : publicPort - 2,
254
+ LEGACY_ALIAS_PORT + 2,
255
+ ];
256
+ for (const candidate of candidates) {
257
+ if (
258
+ Number.isInteger(candidate) &&
259
+ candidate > 0 &&
260
+ candidate <= 65535 &&
261
+ candidate !== publicPort &&
262
+ candidate !== legacyAliasPort
263
+ ) {
264
+ return candidate;
265
+ }
266
+ }
267
+ return DEFAULT_UPSTREAM_PORT;
268
+ }
269
+
247
270
  /** Try to start the identity proxy. Returns `null` (and prints a warning)
248
271
  * if the listen port is already in use by another process — e.g. when
249
272
  * restarting a previous instance that didn't shut down cleanly. The
@@ -273,7 +296,7 @@ async function tryStartIdentityProxy(
273
296
  }
274
297
 
275
298
  async function runStart(args: string[]): Promise<void> {
276
- const envPort = process.env["PORT"];
299
+ const envPort = process.env["AGENT_INSPECTOR_PORT"] ?? process.env["PORT"];
277
300
  const portDefault = envPort !== undefined ? Number(envPort) : DEFAULT_PORT;
278
301
  const envHost = process.env["NITRO_HOST"] ?? process.env["HOST"];
279
302
  const envMode =
@@ -290,10 +313,7 @@ async function runStart(args: string[]): Promise<void> {
290
313
  let captureMode = DEFAULT_CAPTURE_MODE;
291
314
  let captureModeWasSpecified = false;
292
315
  let enableIdentityProxy = true;
293
- let identityProxyPort =
294
- process.env["AGENT_INSPECTOR_PORT"] !== undefined
295
- ? Number(process.env["AGENT_INSPECTOR_PORT"])
296
- : DEFAULT_PORT;
316
+ let legacyAliasEnabled = true;
297
317
 
298
318
  if (envMode !== undefined && envMode !== "") {
299
319
  const parsedMode = parseCaptureMode(envMode);
@@ -402,10 +422,13 @@ async function runStart(args: string[]): Promise<void> {
402
422
  process.exitCode = 1;
403
423
  return;
404
424
  }
405
- identityProxyPort = parsed;
425
+ port = parsed;
406
426
  i++;
407
427
  break;
408
428
  }
429
+ case "--no-legacy-port":
430
+ legacyAliasEnabled = false;
431
+ break;
409
432
  default:
410
433
  break;
411
434
  }
@@ -489,11 +512,13 @@ async function runStart(args: string[]): Promise<void> {
489
512
  }
490
513
  }
491
514
 
492
- // TanStack Start listens on the private upstream port; the identity
493
- // proxy owns the public-facing `port`. When the identity proxy is
494
- // disabled (or not yet running) we fall back to TanStack listening on
495
- // the public port directly.
496
- const upstreamPort = enableIdentityProxy ? DEFAULT_UPSTREAM_PORT : port;
515
+ const legacyAliasPort =
516
+ enableIdentityProxy && legacyAliasEnabled && port === DEFAULT_PORT ? LEGACY_ALIAS_PORT : null;
517
+
518
+ // TanStack Start listens on the private upstream port; identity proxies own
519
+ // the public-facing port(s). When the identity proxy is disabled we fall
520
+ // back to TanStack listening on the public port directly.
521
+ const upstreamPort = enableIdentityProxy ? pickUpstreamPort(port, legacyAliasPort) : port;
497
522
  process.env["PORT"] = String(upstreamPort);
498
523
 
499
524
  const url = urlForHost(port, host);
@@ -526,8 +551,8 @@ async function runStart(args: string[]): Promise<void> {
526
551
  const restartPorts = new Set<number>();
527
552
  restartPorts.add(port);
528
553
  if (enableIdentityProxy) {
529
- restartPorts.add(identityProxyPort);
530
554
  restartPorts.add(upstreamPort);
555
+ if (legacyAliasPort !== null) restartPorts.add(legacyAliasPort);
531
556
  }
532
557
  for (const targetPort of restartPorts) {
533
558
  killProcessOnPort(targetPort);
@@ -538,6 +563,11 @@ async function runStart(args: string[]): Promise<void> {
538
563
  console.log(` Capture mode: ${captureMode}`);
539
564
  console.log(` Proxy: ${url}/proxy`);
540
565
  printAccessHints(port, host);
566
+ if (legacyAliasPort !== null) {
567
+ console.log(` Legacy UI: http://localhost:${legacyAliasPort}`);
568
+ console.log(` Legacy proxy: http://localhost:${legacyAliasPort}/proxy`);
569
+ console.log(` Legacy MCP: http://localhost:${legacyAliasPort}/api/mcp`);
570
+ }
541
571
  console.log(``);
542
572
  console.log(`Route AI coding tools through the proxy:`);
543
573
  console.log(` Claude Code: ANTHROPIC_BASE_URL=${url}/proxy claude`);
@@ -594,6 +624,8 @@ async function runStart(args: string[]): Promise<void> {
594
624
  serverEnv["HOST"] = host;
595
625
  serverEnv["NITRO_HOST"] = host;
596
626
  }
627
+ serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
628
+ serverEnv["PROXY_PORT"] = String(port);
597
629
  serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
598
630
  const serverProcess = spawn(serverCommand.command, serverCommand.args, {
599
631
  stdio: background ? ["ignore", "ignore", "ignore"] : ["ignore", "pipe", "pipe"],
@@ -601,6 +633,10 @@ async function runStart(args: string[]): Promise<void> {
601
633
  env: serverEnv,
602
634
  windowsHide: background,
603
635
  });
636
+ serverProcess.once("error", (err) => {
637
+ console.error(`Failed to start Agent Inspector runtime: ${err.message}`);
638
+ });
639
+ const serverExit = waitForProcessExit(serverProcess);
604
640
  if (!background) {
605
641
  pipeServerOutput(serverProcess, {
606
642
  enableIdentityProxy,
@@ -615,9 +651,23 @@ async function runStart(args: string[]): Promise<void> {
615
651
  // This lets AI tools (which can't easily set those headers themselves)
616
652
  // still get accurate session attribution. Started only after TanStack
617
653
  // Start is healthy, so the upstream is ready by the time clients connect.
618
- const identityProxy = enableIdentityProxy
619
- ? await tryStartIdentityProxy(identityProxyPort, upstreamPort, host)
620
- : null;
654
+ const identityProxies: Array<{
655
+ label: "primary" | "legacy";
656
+ port: number;
657
+ server: import("node:http").Server;
658
+ }> = [];
659
+ if (enableIdentityProxy) {
660
+ const primaryProxy = await tryStartIdentityProxy(port, upstreamPort, host);
661
+ if (primaryProxy !== null) {
662
+ identityProxies.push({ label: "primary", port, server: primaryProxy });
663
+ }
664
+ if (legacyAliasPort !== null) {
665
+ const legacyProxy = await tryStartIdentityProxy(legacyAliasPort, upstreamPort, host);
666
+ if (legacyProxy !== null) {
667
+ identityProxies.push({ label: "legacy", port: legacyAliasPort, server: legacyProxy });
668
+ }
669
+ }
670
+ }
621
671
 
622
672
  if (background) {
623
673
  serverProcess.unref();
@@ -625,18 +675,24 @@ async function runStart(args: string[]): Promise<void> {
625
675
  // not started yet so the public port is not yet accepting traffic.
626
676
  if (await waitForInspectorHealthy(upstreamPort, 5000, host)) {
627
677
  console.log(`agent-inspector background server is ready at ${url}`);
628
- if (identityProxy !== null) {
629
- console.log(`Identity proxy ready on port ${identityProxyPort} (PID-attributing proxy).`);
678
+ if (identityProxies.length > 0) {
679
+ console.log(`Identity proxy ready on port ${port} (PID-attributing proxy).`);
630
680
  }
631
681
  printAccessHints(port, host);
632
- if (identityProxy !== null) {
682
+ if (legacyAliasPort !== null) {
683
+ console.log(` Legacy proxy: http://localhost:${legacyAliasPort}/proxy`);
684
+ console.log(` Legacy MCP: http://localhost:${legacyAliasPort}/api/mcp`);
685
+ }
686
+ if (identityProxies.length > 0) {
633
687
  console.log(
634
688
  ``,
635
689
  `Route AI coding tools through the identity proxy for accurate PID attribution:`,
636
- ` Claude Code: ANTHROPIC_BASE_URL=http://localhost:${identityProxyPort}/proxy claude`,
637
- ` OpenCode: LLM_BASE_URL=http://localhost:${identityProxyPort}/proxy opencode`,
638
- ` MiMo Code: OPENAI_BASE_URL=http://localhost:${identityProxyPort}/proxy mimo`,
639
- ` (Direct ${port} still works; it falls back to OS-scan heuristic.)`,
690
+ ` Claude Code: ANTHROPIC_BASE_URL=http://localhost:${port}/proxy claude`,
691
+ ` OpenCode: LLM_BASE_URL=http://localhost:${port}/proxy opencode`,
692
+ ` MiMo Code: OPENAI_BASE_URL=http://localhost:${port}/proxy mimo`,
693
+ legacyAliasPort === null
694
+ ? ` Override the port with --port <n> when needed.`
695
+ : ` Legacy configs using http://localhost:${legacyAliasPort}/proxy still work.`,
640
696
  );
641
697
  }
642
698
  return;
@@ -646,17 +702,23 @@ async function runStart(args: string[]): Promise<void> {
646
702
  return;
647
703
  }
648
704
 
649
- if (identityProxy !== null) {
705
+ if (identityProxies.length > 0) {
650
706
  console.log(``);
651
707
  console.log(`Identity proxy ready at ${url} (PID-attributing public entrypoint).`);
708
+ if (legacyAliasPort !== null) {
709
+ const legacyReady = identityProxies.some((proxy) => proxy.label === "legacy");
710
+ if (legacyReady) {
711
+ console.log(` Legacy alias ready at http://localhost:${legacyAliasPort}.`);
712
+ }
713
+ }
652
714
  if (upstreamPort !== port) {
653
715
  console.log(` Internal runtime port ${upstreamPort} is hidden behind the public proxy.`);
654
716
  }
655
717
  }
656
718
 
657
- process.exitCode = await waitForProcessExit(serverProcess);
719
+ process.exitCode = await serverExit;
658
720
 
659
- if (identityProxy !== null) {
660
- await new Promise<void>((resolve) => identityProxy.close(() => resolve()));
721
+ for (const proxy of identityProxies) {
722
+ await new Promise<void>((resolve) => proxy.server.close(() => resolve()));
661
723
  }
662
724
  }
@@ -2143,8 +2143,8 @@ export function ProxyViewer({
2143
2143
  <div className="bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.045),0_18px_52px_rgba(0,0,0,0.18)] mx-auto max-w-2xl space-y-4 rounded-[8px] px-4 py-8">
2144
2144
  <p className="text-sm">No requests captured yet.</p>
2145
2145
  <div className="flex flex-col items-center gap-2">
2146
- <CopyableCommand command="ANTHROPIC_BASE_URL=http://localhost:25947/proxy <your-tool>" />
2147
- <CopyableCommand command="OPENAI_BASE_URL=http://localhost:25947/proxy <your-tool>" />
2146
+ <CopyableCommand command="ANTHROPIC_BASE_URL=http://localhost:9527/proxy <your-tool>" />
2147
+ <CopyableCommand command="OPENAI_BASE_URL=http://localhost:9527/proxy <your-tool>" />
2148
2148
  </div>
2149
2149
  <p className="mx-auto max-w-xl text-xs leading-relaxed text-muted-foreground">
2150
2150
  Container note: if your AI tool runs in a different container or host than
@@ -303,7 +303,7 @@ function CopyableSetupValue({
303
303
  function OnboardingSettingsTab(): JSX.Element {
304
304
  const [copiedId, setCopiedId] = useState<string | null>(null);
305
305
  const origin = useMemo(() => {
306
- if (typeof window === "undefined") return "http://localhost:25947";
306
+ if (typeof window === "undefined") return "http://localhost:9527";
307
307
  return window.location.origin;
308
308
  }, []);
309
309
  const values = useMemo(
@@ -454,7 +454,7 @@ function OnboardingSettingsTab(): JSX.Element {
454
454
  function McpSettingsTab(): JSX.Element {
455
455
  const [copiedId, setCopiedId] = useState<string | null>(null);
456
456
  const origin = useMemo(() => {
457
- if (typeof window === "undefined") return "http://localhost:25947";
457
+ if (typeof window === "undefined") return "http://localhost:9527";
458
458
  return window.location.origin;
459
459
  }, []);
460
460
  const endpoint = `${origin}/api/mcp`;
@@ -7,9 +7,10 @@
7
7
  * Initialization order:
8
8
  * 1. `setCurrentPort(port)` — explicit override (used by tests, or any
9
9
  * code that has direct knowledge of the bound port).
10
- * 2. `process.env.PORT` — fallback. The CLI (`src/cli.ts`) sets this
11
- * before spawning the server process, so in normal operation this
12
- * branch is taken and no explicit `setCurrentPort` call is needed.
10
+ * 2. `process.env.AGENT_INSPECTOR_PUBLIC_PORT` — the public identity-proxy
11
+ * port. The CLI sets this when the runtime is hidden behind a private
12
+ * upstream port.
13
+ * 3. `process.env.PORT` — fallback for direct dev/server runs.
13
14
  *
14
15
  * `getCurrentPort()` throws if neither source is available.
15
16
  */
@@ -25,6 +26,11 @@ export function setCurrentPort(port: number): void {
25
26
 
26
27
  export function getCurrentPort(): number {
27
28
  if (overridePort !== null) return overridePort;
29
+ const publicPort = process.env["AGENT_INSPECTOR_PUBLIC_PORT"];
30
+ if (publicPort !== undefined && publicPort !== "") {
31
+ const n = Number(publicPort);
32
+ if (Number.isInteger(n) && n > 0 && n <= 65535) return n;
33
+ }
28
34
  const envPort = process.env["PORT"];
29
35
  if (envPort !== undefined && envPort !== "") {
30
36
  const n = Number(envPort);