@tonyclaw/agent-inspector 3.0.38 → 3.0.39
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 +249 -84
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-BXQWabQJ.js → CompareDrawer-BjQ2HH8j.js} +1 -1
- package/.output/public/assets/{InspectorPet-CpQEXKcr.js → InspectorPet-DDScgJ7_.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-C3iHhtkc.js +126 -0
- package/.output/public/assets/{ReplayDialog-CnYt4UKE.js → ReplayDialog-BujXVTMJ.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-BI25al3m.js → RequestAnatomy-iK2nOuCP.js} +1 -1
- package/.output/public/assets/{ResponseView-D3y-I9nb.js → ResponseView-iAuOJxem.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-n90dYzPp.js → StreamingChunkSequence-DiZ21icS.js} +1 -1
- package/.output/public/assets/{_sessionId-DLbOYm3h.js → _sessionId-CLTlZx8L.js} +1 -1
- package/.output/public/assets/{_sessionId-CogrNhCS.js → _sessionId-D3WwDIit.js} +1 -1
- package/.output/public/assets/{index-I5zN_KrA.js → index--ND7WRCU.js} +1 -1
- package/.output/public/assets/{index-DXGp27k9.js → index-DGuDxV-I.js} +1 -1
- package/.output/public/assets/index-fXi7tXyH.js +76 -0
- package/.output/public/assets/{index-BWWA1Rf6.js → index-nN3UPNM0.js} +6 -6
- package/.output/public/assets/{json-viewer-BoB2BfHg.js → json-viewer-CzcwNVJa.js} +1 -1
- package/.output/public/assets/{jszip.min-CAXkxQd6.js → jszip.min-DAbOCDPI.js} +1 -1
- package/.output/server/{_sessionId-_hORwhWH.mjs → _sessionId-DSetUDiv.mjs} +1 -1
- package/.output/server/{_sessionId-DScqlEFC.mjs → _sessionId-DtRKr8Jt.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-BtGm5i_L.mjs → CompareDrawer-Cns26sqx.mjs} +2 -2
- package/.output/server/_ssr/{InspectorPet-BnN6lVqM.mjs → InspectorPet-CyWBvBuh.mjs} +1 -1
- package/.output/server/_ssr/{ProxyViewerContainer-aduEJ_Xy.mjs → ProxyViewerContainer-CimBv5WL.mjs} +58 -23
- package/.output/server/_ssr/{ReplayDialog-BSGZ-TFs.mjs → ReplayDialog-DrM897xL.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-DN_eSnDN.mjs → RequestAnatomy-BRgezegq.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-Dk9Td_HM.mjs → ResponseView-D7u7r-lA.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-DthH8Lx4.mjs → StreamingChunkSequence-DM26ZaVC.mjs} +2 -2
- package/.output/server/_ssr/{index-CqeZgFFF.mjs → index-BeWedHzY.mjs} +2 -2
- package/.output/server/_ssr/{index-DvVHL942.mjs → index-C284XGkZ.mjs} +1 -1
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-DLKA2jUq.mjs → json-viewer-CD_GbR7V.mjs} +2 -2
- package/.output/server/_ssr/{router-CRKEDn6n.mjs → router-DxPlMfLZ.mjs} +125 -58
- package/.output/server/{_tanstack-start-manifest_v-pQO9kTmV.mjs → _tanstack-start-manifest_v-82w9tFI-.mjs} +1 -1
- package/.output/server/index.mjs +84 -84
- package/.output/workers/logFinalizer.worker.js +6 -1
- package/.output/workers/sessionWorkerEntry.js +6 -1
- package/README.md +42 -10
- package/package.json +1 -1
- package/src/cli/doctor.ts +17 -10
- package/src/cli/networkHints.ts +15 -8
- package/src/cli/onboard.ts +10 -3
- package/src/cli.ts +90 -56
- package/src/components/ProxyViewer.tsx +11 -2
- package/src/components/ProxyViewerContainer.tsx +2 -1
- package/src/components/pi-agent/PiAgentPanel.tsx +2 -1
- package/src/components/providers/SettingsDialog.tsx +24 -7
- package/src/lib/apiClient.ts +9 -2
- package/src/lib/basePath.ts +83 -0
- package/src/lib/sessionUrl.ts +4 -2
- package/src/mcp/loopback.ts +2 -1
- package/src/mcp/server.ts +6 -2
- package/src/proxy/identityProxy.ts +98 -6
- package/src/proxy/privateDataPath.ts +15 -1
- package/src/proxy/sessionInfo.ts +10 -3
- package/src/proxy/upstream.ts +3 -1
- package/src/router.tsx +7 -1
- package/src/routes/__root.tsx +9 -3
- package/.output/public/assets/ProxyViewerContainer-8pNJsCJ5.js +0 -126
- package/.output/public/assets/index-Ca0grOxW.js +0 -76
|
@@ -40,8 +40,13 @@ var PRIVATE_FILE_MODE = 384;
|
|
|
40
40
|
var securedPaths = /* @__PURE__ */ new Set();
|
|
41
41
|
var securedFileIdentities = /* @__PURE__ */ new Map();
|
|
42
42
|
var cachedWindowsSid = null;
|
|
43
|
+
function windowsSystemPath(executable) {
|
|
44
|
+
const systemRoot = process.env["SystemRoot"] ?? "C:\\Windows";
|
|
45
|
+
return `${systemRoot}\\System32\\${executable}`;
|
|
46
|
+
}
|
|
43
47
|
function runWindowsCommand(executable, args) {
|
|
44
|
-
const
|
|
48
|
+
const resolved = process.platform === "win32" && /^(whoami|icacls)\.exe$/iu.test(executable) ? windowsSystemPath(executable) : executable;
|
|
49
|
+
const result = spawnSync(resolved, [...args], {
|
|
45
50
|
encoding: "utf8",
|
|
46
51
|
shell: false,
|
|
47
52
|
windowsHide: true
|
package/README.md
CHANGED
|
@@ -147,12 +147,44 @@ agent-inspector doctor --chrome-extension
|
|
|
147
147
|
Then point an AI coding tool at the proxy:
|
|
148
148
|
|
|
149
149
|
```bash
|
|
150
|
-
ANTHROPIC_BASE_URL=http://localhost:9527/proxy <tool>
|
|
150
|
+
ANTHROPIC_BASE_URL=http://localhost:9527/inspector/proxy <tool>
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
-
The web UI runs at http://localhost:9527. The public proxy endpoint is
|
|
154
|
-
`http://localhost:9527/proxy`; internally Agent Inspector may run the app on a private
|
|
155
|
-
but AI tools should use the public URL printed by the CLI.
|
|
153
|
+
The web UI runs at http://localhost:9527/inspector by default. The public proxy endpoint is
|
|
154
|
+
`http://localhost:9527/inspector/proxy`; internally Agent Inspector may run the app on a private
|
|
155
|
+
helper port, but AI tools should use the public URL printed by the CLI.
|
|
156
|
+
|
|
157
|
+
### Base path
|
|
158
|
+
|
|
159
|
+
Agent Inspector defaults to the `/inspector` public base path:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
agent-inspector
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
This generates prefixed public URLs for the UI, assets, REST API, MCP, sessions, and model proxy:
|
|
166
|
+
|
|
167
|
+
- Web UI: `http://localhost:9527/inspector`
|
|
168
|
+
- Assets: `http://localhost:9527/inspector/assets/...`
|
|
169
|
+
- REST API: `http://localhost:9527/inspector/api/...`
|
|
170
|
+
- MCP: `http://localhost:9527/inspector/api/mcp`
|
|
171
|
+
- Sessions: `http://localhost:9527/inspector/session/...`
|
|
172
|
+
- Proxy: `http://localhost:9527/inspector/proxy`
|
|
173
|
+
|
|
174
|
+
AI tools should use the prefixed proxy URL:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
ANTHROPIC_BASE_URL=http://localhost:9527/inspector/proxy <tool>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
To run at the domain root instead, explicitly set the base path to `/`:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
AGENT_INSPECTOR_BASE_PATH=/ agent-inspector
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Reverse proxies should preserve the `/inspector` prefix when forwarding public requests to Agent
|
|
187
|
+
Inspector.
|
|
156
188
|
|
|
157
189
|
The legacy `http://localhost:9527` port is closed by default. If you need a temporary alias for
|
|
158
190
|
older AI tool or MCP configs, start with `agent-inspector --legacy-port`.
|
|
@@ -163,12 +195,12 @@ Agent Inspector v3.0 changes the default public port from `9527` to `9527`. The
|
|
|
163
195
|
opens only `9527` by default. Existing AI coding tool and MCP configs that still point at `9527`
|
|
164
196
|
should be moved to `9527`, or you can temporarily start with `agent-inspector --legacy-port`.
|
|
165
197
|
|
|
166
|
-
Use these endpoints for new configs:
|
|
198
|
+
Use these default endpoints for new configs:
|
|
167
199
|
|
|
168
|
-
- Web UI: `http://localhost:9527`
|
|
169
|
-
- Proxy: `http://localhost:9527/proxy`
|
|
170
|
-
- OpenAI-compatible `/v1` proxy: `http://localhost:9527/proxy/v1`
|
|
171
|
-
- MCP: `http://localhost:9527/api/mcp`
|
|
200
|
+
- Web UI: `http://localhost:9527/inspector`
|
|
201
|
+
- Proxy: `http://localhost:9527/inspector/proxy`
|
|
202
|
+
- OpenAI-compatible `/v1` proxy: `http://localhost:9527/inspector/proxy/v1`
|
|
203
|
+
- MCP: `http://localhost:9527/inspector/api/mcp`
|
|
172
204
|
|
|
173
205
|
If you intentionally need the old compatibility port, start with `agent-inspector --legacy-port`.
|
|
174
206
|
|
|
@@ -178,7 +210,7 @@ For Codex-style OpenAI Responses clients, configure the proxy as an OpenAI-compa
|
|
|
178
210
|
model_provider = "agent-inspector"
|
|
179
211
|
|
|
180
212
|
[model_providers."agent-inspector"]
|
|
181
|
-
base_url = "http://127.0.0.1:9527/proxy/v1"
|
|
213
|
+
base_url = "http://127.0.0.1:9527/inspector/proxy/v1"
|
|
182
214
|
wire_api = "responses"
|
|
183
215
|
```
|
|
184
216
|
|
package/package.json
CHANGED
package/src/cli/doctor.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, resolve } from "node:path";
|
|
3
3
|
import { createConnection } from "node:net";
|
|
4
|
+
import { appendBasePathToOrigin, getConfiguredBasePath } from "../lib/basePath";
|
|
4
5
|
import { migrateLegacyDataDirs, resolveDataDir } from "../proxy/dataDir";
|
|
5
6
|
|
|
6
7
|
const DEFAULT_PORT = 9527;
|
|
@@ -55,7 +56,7 @@ export type DoctorDeps = {
|
|
|
55
56
|
exists: (path: string) => boolean;
|
|
56
57
|
listDir: (path: string) => readonly string[] | null;
|
|
57
58
|
stat: (path: string) => { isDirectory: boolean; size: number } | null;
|
|
58
|
-
fetchHealth: (port: number) => Promise<HealthProbe>;
|
|
59
|
+
fetchHealth: (port: number, basePath: string) => Promise<HealthProbe>;
|
|
59
60
|
isPortOpen: (port: number) => Promise<boolean>;
|
|
60
61
|
};
|
|
61
62
|
|
|
@@ -261,14 +262,17 @@ function checkProviderConfig(options: DoctorOptions, deps: DoctorDeps): DoctorCh
|
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
async function checkProxyHealth(port: number, deps: DoctorDeps): Promise<DoctorCheck> {
|
|
264
|
-
const
|
|
265
|
+
const basePath = getConfiguredBasePath(deps.env);
|
|
266
|
+
const health = await deps.fetchHealth(port, basePath);
|
|
265
267
|
const url = `http://localhost:${port}`;
|
|
268
|
+
const proxyUrl = appendBasePathToOrigin(url, "/proxy", basePath);
|
|
269
|
+
const healthPath = appendBasePathToOrigin("", "/api/health", basePath);
|
|
266
270
|
if (health.ok) {
|
|
267
271
|
return check(
|
|
268
272
|
"Proxy health",
|
|
269
273
|
"pass",
|
|
270
|
-
`agent-inspector is healthy at ${url}.`,
|
|
271
|
-
`Proxy URL: ${
|
|
274
|
+
`agent-inspector is healthy at ${appendBasePathToOrigin(url, "/", basePath)}.`,
|
|
275
|
+
`Proxy URL: ${proxyUrl}`,
|
|
272
276
|
);
|
|
273
277
|
}
|
|
274
278
|
|
|
@@ -277,7 +281,7 @@ async function checkProxyHealth(port: number, deps: DoctorDeps): Promise<DoctorC
|
|
|
277
281
|
return check(
|
|
278
282
|
"Proxy health",
|
|
279
283
|
"fail",
|
|
280
|
-
`Port ${port} is accepting connections, but
|
|
284
|
+
`Port ${port} is accepting connections, but ${healthPath} is not healthy${suffix}.`,
|
|
281
285
|
"Stop that process, choose --port <n>, or start agent-inspector with --force-restart.",
|
|
282
286
|
);
|
|
283
287
|
}
|
|
@@ -583,14 +587,17 @@ export function doctorHelp(): string {
|
|
|
583
587
|
].join("\n");
|
|
584
588
|
}
|
|
585
589
|
|
|
586
|
-
async function fetchHealth(port: number): Promise<HealthProbe> {
|
|
590
|
+
async function fetchHealth(port: number, basePath: string): Promise<HealthProbe> {
|
|
587
591
|
const controller = new AbortController();
|
|
588
592
|
const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
589
593
|
try {
|
|
590
|
-
const response = await fetch(
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
+
const response = await fetch(
|
|
595
|
+
appendBasePathToOrigin(`http://127.0.0.1:${port}`, "/api/health", basePath),
|
|
596
|
+
{
|
|
597
|
+
cache: "no-store",
|
|
598
|
+
signal: controller.signal,
|
|
599
|
+
},
|
|
600
|
+
);
|
|
594
601
|
return { ok: response.ok, status: response.status };
|
|
595
602
|
} catch {
|
|
596
603
|
return { ok: false, status: null };
|
package/src/cli/networkHints.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { networkInterfaces } from "node:os";
|
|
2
|
+
import { appendBasePathToOrigin, normalizeBasePath } from "../lib/basePath";
|
|
2
3
|
|
|
3
4
|
export type NetworkInterfaceMap = ReturnType<typeof networkInterfaces>;
|
|
4
5
|
|
|
@@ -107,24 +108,30 @@ export function networkUrlsForBindHost(
|
|
|
107
108
|
return [urlForHost(port, host)];
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
function accessUrl(origin: string, path: string, basePath: string): string {
|
|
112
|
+
return appendBasePathToOrigin(origin, path, basePath);
|
|
113
|
+
}
|
|
114
|
+
|
|
110
115
|
export function formatAccessHintLines(
|
|
111
116
|
port: number,
|
|
112
117
|
networkUrls: readonly string[],
|
|
113
118
|
host: BindHost = undefined,
|
|
119
|
+
basePath = "",
|
|
114
120
|
): string[] {
|
|
121
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
115
122
|
const localUrl = localUrlForPort(port);
|
|
116
123
|
const hostLabel = isWildcardHost(host) ? "all interfaces (0.0.0.0)" : (trimHost(host) ?? "");
|
|
117
124
|
const lines = ["Access URLs:", ` Bind host: ${hostLabel}`];
|
|
118
125
|
|
|
119
126
|
if (isLoopbackHost(host) || isWildcardHost(host)) {
|
|
120
|
-
lines.push(` Local UI: ${localUrl}`);
|
|
121
|
-
lines.push(` Local proxy: ${localUrl
|
|
122
|
-
lines.push(` Local MCP: ${localUrl
|
|
127
|
+
lines.push(` Local UI: ${accessUrl(localUrl, "/", normalizedBasePath)}`);
|
|
128
|
+
lines.push(` Local proxy: ${accessUrl(localUrl, "/proxy", normalizedBasePath)}`);
|
|
129
|
+
lines.push(` Local MCP: ${accessUrl(localUrl, "/api/mcp", normalizedBasePath)}`);
|
|
123
130
|
} else {
|
|
124
131
|
const boundUrl = urlForHost(port, host);
|
|
125
|
-
lines.push(` Bound UI: ${boundUrl}`);
|
|
126
|
-
lines.push(` Bound proxy: ${boundUrl
|
|
127
|
-
lines.push(` Bound MCP: ${boundUrl
|
|
132
|
+
lines.push(` Bound UI: ${accessUrl(boundUrl, "/", normalizedBasePath)}`);
|
|
133
|
+
lines.push(` Bound proxy: ${accessUrl(boundUrl, "/proxy", normalizedBasePath)}`);
|
|
134
|
+
lines.push(` Bound MCP: ${accessUrl(boundUrl, "/api/mcp", normalizedBasePath)}`);
|
|
128
135
|
lines.push(" Localhost: may not work because the server is bound to a specific host");
|
|
129
136
|
}
|
|
130
137
|
|
|
@@ -134,8 +141,8 @@ export function formatAccessHintLines(
|
|
|
134
141
|
} else if (firstNetworkUrl === undefined) {
|
|
135
142
|
lines.push(" Network: no non-loopback IPv4 address detected");
|
|
136
143
|
} else {
|
|
137
|
-
lines.push(` Network UI: ${firstNetworkUrl}`);
|
|
138
|
-
lines.push(` Network proxy: ${firstNetworkUrl
|
|
144
|
+
lines.push(` Network UI: ${accessUrl(firstNetworkUrl, "/", normalizedBasePath)}`);
|
|
145
|
+
lines.push(` Network proxy: ${accessUrl(firstNetworkUrl, "/proxy", normalizedBasePath)}`);
|
|
139
146
|
const moreUrls = networkUrls.slice(1);
|
|
140
147
|
if (moreUrls.length > 0) {
|
|
141
148
|
lines.push(` Other IPs: ${moreUrls.join(", ")}`);
|
package/src/cli/onboard.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { homedir } from "node:os";
|
|
|
24
24
|
import { dirname, join } from "node:path";
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
26
|
|
|
27
|
+
import { appendBasePathToOrigin, getConfiguredBasePath } from "../lib/basePath.js";
|
|
27
28
|
import { detectAll, detectFirst } from "./detect-tools.js";
|
|
28
29
|
import { renderCommandOnboard } from "./templates/command-onboard.js";
|
|
29
30
|
import {
|
|
@@ -36,7 +37,8 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
36
37
|
const __dirname = dirname(__filename);
|
|
37
38
|
|
|
38
39
|
const DEFAULT_PORT = 9527;
|
|
39
|
-
const
|
|
40
|
+
const DEFAULT_ORIGIN = `http://localhost:${DEFAULT_PORT}`;
|
|
41
|
+
const DEFAULT_MCP_URL = `${DEFAULT_ORIGIN}/api/mcp`;
|
|
40
42
|
const LEGACY_MCP_URL = "http://localhost:9527/api/mcp";
|
|
41
43
|
const SKILL_DIR_NAME = "agent-inspector-onboard";
|
|
42
44
|
const SKILL_FILE_NAME = "SKILL.md";
|
|
@@ -181,6 +183,11 @@ function actionForStatus(label: string, state: GeneratedFileState): string {
|
|
|
181
183
|
}
|
|
182
184
|
|
|
183
185
|
function parseFlags(argv: readonly string[]): OnboardFlags {
|
|
186
|
+
const defaultMcpUrl = appendBasePathToOrigin(
|
|
187
|
+
DEFAULT_ORIGIN,
|
|
188
|
+
"/api/mcp",
|
|
189
|
+
getConfiguredBasePath(process.env),
|
|
190
|
+
);
|
|
184
191
|
const flags: OnboardFlags = {
|
|
185
192
|
force: false,
|
|
186
193
|
dryRun: false,
|
|
@@ -192,12 +199,12 @@ function parseFlags(argv: readonly string[]): OnboardFlags {
|
|
|
192
199
|
opencodeOnly: false,
|
|
193
200
|
opencodeConfig: null,
|
|
194
201
|
opencodeTransport: "local",
|
|
195
|
-
opencodeMcpUrl:
|
|
202
|
+
opencodeMcpUrl: defaultMcpUrl,
|
|
196
203
|
mimo: false,
|
|
197
204
|
mimoOnly: false,
|
|
198
205
|
mimoConfig: null,
|
|
199
206
|
mimoTransport: "local",
|
|
200
|
-
mimoMcpUrl:
|
|
207
|
+
mimoMcpUrl: defaultMcpUrl,
|
|
201
208
|
uninstall: false,
|
|
202
209
|
status: false,
|
|
203
210
|
json: false,
|
package/src/cli.ts
CHANGED
|
@@ -6,6 +6,11 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
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
|
+
import {
|
|
10
|
+
AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV,
|
|
11
|
+
appendBasePathToOrigin,
|
|
12
|
+
getConfiguredBasePath,
|
|
13
|
+
} from "./lib/basePath.js";
|
|
9
14
|
import {
|
|
10
15
|
formatAccessHintLines,
|
|
11
16
|
isLoopbackHost,
|
|
@@ -74,14 +79,17 @@ await runStart(process.argv.slice(2));
|
|
|
74
79
|
// Legacy `start` behavior — start the proxy on the configured port. Extracted
|
|
75
80
|
// into a function so the router above can keep the top-level flow readable.
|
|
76
81
|
// -----------------------------------------------------------------------------
|
|
77
|
-
async function isInspectorHealthy(port: number, host?: string): Promise<boolean> {
|
|
82
|
+
async function isInspectorHealthy(port: number, host?: string, basePath = ""): Promise<boolean> {
|
|
78
83
|
const controller = new AbortController();
|
|
79
84
|
const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
|
|
80
85
|
try {
|
|
81
|
-
const response = await fetch(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
const response = await fetch(
|
|
87
|
+
appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/health", basePath),
|
|
88
|
+
{
|
|
89
|
+
cache: "no-store",
|
|
90
|
+
signal: controller.signal,
|
|
91
|
+
},
|
|
92
|
+
);
|
|
85
93
|
return response.ok;
|
|
86
94
|
} catch {
|
|
87
95
|
return false;
|
|
@@ -90,14 +98,21 @@ async function isInspectorHealthy(port: number, host?: string): Promise<boolean>
|
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
async function getRunningCaptureMode(
|
|
101
|
+
async function getRunningCaptureMode(
|
|
102
|
+
port: number,
|
|
103
|
+
host?: string,
|
|
104
|
+
basePath = "",
|
|
105
|
+
): Promise<CaptureMode | null> {
|
|
94
106
|
const controller = new AbortController();
|
|
95
107
|
const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
|
|
96
108
|
try {
|
|
97
|
-
const response = await fetch(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
const response = await fetch(
|
|
110
|
+
appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/config", basePath),
|
|
111
|
+
{
|
|
112
|
+
cache: "no-store",
|
|
113
|
+
signal: controller.signal,
|
|
114
|
+
},
|
|
115
|
+
);
|
|
101
116
|
if (!response.ok) return null;
|
|
102
117
|
const raw: unknown = await response.json();
|
|
103
118
|
return readCaptureMode(raw);
|
|
@@ -134,10 +149,11 @@ async function waitForInspectorHealthy(
|
|
|
134
149
|
port: number,
|
|
135
150
|
timeoutMs: number,
|
|
136
151
|
host?: string,
|
|
152
|
+
basePath = "",
|
|
137
153
|
): Promise<boolean> {
|
|
138
154
|
const start = Date.now();
|
|
139
155
|
while (Date.now() - start < timeoutMs) {
|
|
140
|
-
if (await isInspectorHealthy(port, host)) return true;
|
|
156
|
+
if (await isInspectorHealthy(port, host, basePath)) return true;
|
|
141
157
|
await sleep(250);
|
|
142
158
|
}
|
|
143
159
|
return false;
|
|
@@ -233,9 +249,9 @@ function readCaptureMode(raw: unknown): CaptureMode | null {
|
|
|
233
249
|
return typeof value === "string" ? parseCaptureMode(value) : null;
|
|
234
250
|
}
|
|
235
251
|
|
|
236
|
-
function printAccessHints(port: number, host?: string): void {
|
|
252
|
+
function printAccessHints(port: number, host?: string, basePath = ""): void {
|
|
237
253
|
const networkUrls = networkUrlsForBindHost(port, host);
|
|
238
|
-
for (const line of formatAccessHintLines(port, networkUrls, host)) {
|
|
254
|
+
for (const line of formatAccessHintLines(port, networkUrls, host, basePath)) {
|
|
239
255
|
console.log(line);
|
|
240
256
|
}
|
|
241
257
|
if (!isLoopbackHost(host)) {
|
|
@@ -539,13 +555,29 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
539
555
|
const upstreamHost = enableIdentityProxy ? DEFAULT_BIND_HOST : host;
|
|
540
556
|
process.env["PORT"] = String(upstreamPort);
|
|
541
557
|
|
|
558
|
+
const basePath = getConfiguredBasePath(process.env);
|
|
542
559
|
const url = urlForHost(port, host);
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
560
|
+
const uiUrl = appendBasePathToOrigin(url, "/", basePath);
|
|
561
|
+
const proxyUrl = appendBasePathToOrigin(url, "/proxy", basePath);
|
|
562
|
+
const proxyMessagesUrl = appendBasePathToOrigin(url, "/proxy/v1/messages", basePath);
|
|
563
|
+
const legacyUrl =
|
|
564
|
+
legacyAliasPort === null
|
|
565
|
+
? null
|
|
566
|
+
: appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/", basePath);
|
|
567
|
+
const legacyProxyUrl =
|
|
568
|
+
legacyAliasPort === null
|
|
569
|
+
? null
|
|
570
|
+
: appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/proxy", basePath);
|
|
571
|
+
const legacyMcpUrl =
|
|
572
|
+
legacyAliasPort === null
|
|
573
|
+
? null
|
|
574
|
+
: appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/api/mcp", basePath);
|
|
575
|
+
|
|
576
|
+
if (!forceRestart && (await isInspectorHealthy(port, host, basePath))) {
|
|
577
|
+
console.log(`agent-inspector is already running at ${uiUrl}`);
|
|
578
|
+
printAccessHints(port, host, basePath);
|
|
547
579
|
if (captureModeWasSpecified) {
|
|
548
|
-
const runningMode = await getRunningCaptureMode(port, host);
|
|
580
|
+
const runningMode = await getRunningCaptureMode(port, host, basePath);
|
|
549
581
|
if (runningMode !== null && runningMode !== captureMode) {
|
|
550
582
|
console.log(`Existing instance capture mode is ${runningMode}; requested ${captureMode}.`);
|
|
551
583
|
console.log(`Use --force-restart to restart with ${captureMode} mode.`);
|
|
@@ -553,7 +585,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
553
585
|
}
|
|
554
586
|
console.log(`Use --force-restart to restart the existing instance.`);
|
|
555
587
|
if (open && openWasSpecified) {
|
|
556
|
-
openBrowser(
|
|
588
|
+
openBrowser(uiUrl);
|
|
557
589
|
}
|
|
558
590
|
return;
|
|
559
591
|
}
|
|
@@ -605,46 +637,44 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
605
637
|
});
|
|
606
638
|
supervisorProcess.unref();
|
|
607
639
|
|
|
608
|
-
if (await waitForInspectorHealthy(port, 10000, host)) {
|
|
609
|
-
console.log(`agent-inspector background server is ready at ${
|
|
610
|
-
printAccessHints(port, host);
|
|
611
|
-
if (
|
|
612
|
-
console.log(` Legacy proxy:
|
|
613
|
-
console.log(` Legacy MCP:
|
|
640
|
+
if (await waitForInspectorHealthy(port, 10000, host, basePath)) {
|
|
641
|
+
console.log(`agent-inspector background server is ready at ${uiUrl}`);
|
|
642
|
+
printAccessHints(port, host, basePath);
|
|
643
|
+
if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
644
|
+
console.log(` Legacy proxy: ${legacyProxyUrl}`);
|
|
645
|
+
console.log(` Legacy MCP: ${legacyMcpUrl}`);
|
|
614
646
|
}
|
|
615
647
|
console.log(``);
|
|
616
648
|
console.log(`Route AI coding tools through the identity proxy for accurate PID attribution:`);
|
|
617
|
-
console.log(` Claude Code: ANTHROPIC_BASE_URL
|
|
618
|
-
console.log(` OpenCode: LLM_BASE_URL
|
|
619
|
-
console.log(` MiMo Code: OPENAI_BASE_URL
|
|
649
|
+
console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
|
|
650
|
+
console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
|
|
651
|
+
console.log(` MiMo Code: OPENAI_BASE_URL=${proxyUrl} mimo`);
|
|
620
652
|
if (open && openWasSpecified) {
|
|
621
|
-
openBrowser(
|
|
653
|
+
openBrowser(uiUrl);
|
|
622
654
|
}
|
|
623
655
|
return;
|
|
624
656
|
}
|
|
625
657
|
|
|
626
|
-
console.error(`agent-inspector background server did not become ready at ${
|
|
658
|
+
console.error(`agent-inspector background server did not become ready at ${uiUrl}.`);
|
|
627
659
|
process.exitCode = 1;
|
|
628
660
|
return;
|
|
629
661
|
}
|
|
630
662
|
|
|
631
|
-
console.log(`Server running at ${
|
|
663
|
+
console.log(`Server running at ${uiUrl}`);
|
|
632
664
|
console.log(` Capture mode: ${captureMode}`);
|
|
633
|
-
console.log(` Proxy: ${
|
|
634
|
-
printAccessHints(port, host);
|
|
635
|
-
if (
|
|
636
|
-
console.log(` Legacy UI:
|
|
637
|
-
console.log(` Legacy proxy:
|
|
638
|
-
console.log(` Legacy MCP:
|
|
665
|
+
console.log(` Proxy: ${proxyUrl}`);
|
|
666
|
+
printAccessHints(port, host, basePath);
|
|
667
|
+
if (legacyUrl !== null && legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
668
|
+
console.log(` Legacy UI: ${legacyUrl}`);
|
|
669
|
+
console.log(` Legacy proxy: ${legacyProxyUrl}`);
|
|
670
|
+
console.log(` Legacy MCP: ${legacyMcpUrl}`);
|
|
639
671
|
}
|
|
640
672
|
console.log(``);
|
|
641
673
|
console.log(`Route AI coding tools through the proxy:`);
|
|
642
|
-
console.log(` Claude Code: ANTHROPIC_BASE_URL=${
|
|
643
|
-
console.log(` OpenCode: LLM_BASE_URL=${
|
|
644
|
-
console.log(` MiMo Code: OPENAI_BASE_URL=${
|
|
645
|
-
console.log(
|
|
646
|
-
` Direct HTTP: curl ${url}/proxy/v1/messages -d '{"model":"...","messages":[...]}'`,
|
|
647
|
-
);
|
|
674
|
+
console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
|
|
675
|
+
console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
|
|
676
|
+
console.log(` MiMo Code: OPENAI_BASE_URL=${proxyUrl} mimo`);
|
|
677
|
+
console.log(` Direct HTTP: curl ${proxyMessagesUrl} -d '{"model":"...","messages":[...]}'`);
|
|
648
678
|
console.log(
|
|
649
679
|
` Remote/container tools: use the Network proxy URL above when shown; otherwise restart with --host 0.0.0.0 or a reachable IP.`,
|
|
650
680
|
);
|
|
@@ -657,7 +687,7 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
657
687
|
);
|
|
658
688
|
|
|
659
689
|
if (open) {
|
|
660
|
-
openBrowser(
|
|
690
|
+
openBrowser(uiUrl);
|
|
661
691
|
}
|
|
662
692
|
|
|
663
693
|
// Compute server path
|
|
@@ -691,6 +721,9 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
691
721
|
}
|
|
692
722
|
serverEnv["HOST"] = upstreamHost;
|
|
693
723
|
serverEnv["NITRO_HOST"] = upstreamHost;
|
|
724
|
+
if (enableIdentityProxy && basePath.length > 0) {
|
|
725
|
+
serverEnv[AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV] = "1";
|
|
726
|
+
}
|
|
694
727
|
serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
|
|
695
728
|
serverEnv["PROXY_PORT"] = String(port);
|
|
696
729
|
serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
|
|
@@ -784,31 +817,32 @@ async function runStart(args: string[]): Promise<void> {
|
|
|
784
817
|
serverProcess.unref();
|
|
785
818
|
// Probe the upstream TanStack port directly — the identity proxy is
|
|
786
819
|
// not started yet so the public port is not yet accepting traffic.
|
|
787
|
-
|
|
788
|
-
|
|
820
|
+
const upstreamProbeBasePath = enableIdentityProxy ? "" : basePath;
|
|
821
|
+
if (await waitForInspectorHealthy(upstreamPort, 5000, host, upstreamProbeBasePath)) {
|
|
822
|
+
console.log(`agent-inspector background server is ready at ${uiUrl}`);
|
|
789
823
|
if (identityProxies.length > 0) {
|
|
790
824
|
console.log(`Identity proxy ready on port ${port} (PID-attributing proxy).`);
|
|
791
825
|
}
|
|
792
|
-
printAccessHints(port, host);
|
|
793
|
-
if (
|
|
794
|
-
console.log(` Legacy proxy:
|
|
795
|
-
console.log(` Legacy MCP:
|
|
826
|
+
printAccessHints(port, host, basePath);
|
|
827
|
+
if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
828
|
+
console.log(` Legacy proxy: ${legacyProxyUrl}`);
|
|
829
|
+
console.log(` Legacy MCP: ${legacyMcpUrl}`);
|
|
796
830
|
}
|
|
797
831
|
if (identityProxies.length > 0) {
|
|
798
832
|
console.log(
|
|
799
833
|
``,
|
|
800
834
|
`Route AI coding tools through the identity proxy for accurate PID attribution:`,
|
|
801
|
-
` Claude Code: ANTHROPIC_BASE_URL
|
|
802
|
-
` OpenCode: LLM_BASE_URL
|
|
803
|
-
` MiMo Code: OPENAI_BASE_URL
|
|
804
|
-
|
|
835
|
+
` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`,
|
|
836
|
+
` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`,
|
|
837
|
+
` MiMo Code: OPENAI_BASE_URL=${proxyUrl} mimo`,
|
|
838
|
+
legacyProxyUrl === null
|
|
805
839
|
? ` Override the port with --port <n> when needed.`
|
|
806
|
-
: ` Legacy configs using
|
|
840
|
+
: ` Legacy configs using ${legacyProxyUrl} still work.`,
|
|
807
841
|
);
|
|
808
842
|
}
|
|
809
843
|
return;
|
|
810
844
|
}
|
|
811
|
-
console.error(`agent-inspector background server did not become ready at ${
|
|
845
|
+
console.error(`agent-inspector background server did not become ready at ${uiUrl}.`);
|
|
812
846
|
process.exitCode = 1;
|
|
813
847
|
return;
|
|
814
848
|
}
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
|
|
43
43
|
import type { CapturedLog } from "../contracts";
|
|
44
44
|
import { exportLogsAsZip, type ExportMode } from "../lib/export-logs";
|
|
45
|
+
import { withBasePath } from "../lib/basePath";
|
|
45
46
|
import type { ImportLogsResponse } from "../lib/logImportContract";
|
|
46
47
|
import { copyTextToClipboard } from "../lib/clipboard";
|
|
47
48
|
import type { InspectorGroupEvidence, InspectorGroupMember } from "../lib/groupContract";
|
|
@@ -1340,6 +1341,10 @@ export function ProxyViewer({
|
|
|
1340
1341
|
const exportAbortRef = useRef<AbortController | null>(null);
|
|
1341
1342
|
const exportRequestIdRef = useRef(0);
|
|
1342
1343
|
useKeyboardNavigation(logListRef, logListWrapperRef, { pageWide: true });
|
|
1344
|
+
const proxyEndpoint = useMemo(() => {
|
|
1345
|
+
const origin = typeof window === "undefined" ? "http://localhost:9527" : window.location.origin;
|
|
1346
|
+
return `${origin}${withBasePath("/proxy")}`;
|
|
1347
|
+
}, []);
|
|
1343
1348
|
|
|
1344
1349
|
const cancelTransientTasks = useCallback(() => {
|
|
1345
1350
|
exportRequestIdRef.current += 1;
|
|
@@ -2026,8 +2031,12 @@ export function ProxyViewer({
|
|
|
2026
2031
|
{liveEmptyStateCopy.showConnectionCommands && (
|
|
2027
2032
|
<>
|
|
2028
2033
|
<div className="flex flex-col items-center gap-2">
|
|
2029
|
-
<CopyableCommand
|
|
2030
|
-
|
|
2034
|
+
<CopyableCommand
|
|
2035
|
+
command={`ANTHROPIC_BASE_URL=${proxyEndpoint} <your-tool>`}
|
|
2036
|
+
/>
|
|
2037
|
+
<CopyableCommand
|
|
2038
|
+
command={`OPENAI_BASE_URL=${proxyEndpoint} <your-tool>`}
|
|
2039
|
+
/>
|
|
2031
2040
|
</div>
|
|
2032
2041
|
<p className="mx-auto max-w-xl text-xs leading-relaxed text-muted-foreground">
|
|
2033
2042
|
Container note: if your AI tool runs in a different container or host than
|
|
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useRef, useMemo, useTransition, type
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { CapturedLogSchema, type CapturedLog } from "../contracts";
|
|
4
4
|
import { fetchJson, fetchJsonWithTimeout } from "../lib/apiClient";
|
|
5
|
+
import { withBasePath } from "../lib/basePath";
|
|
5
6
|
import {
|
|
6
7
|
DeleteInspectorGroupsResponseSchema,
|
|
7
8
|
InspectorGroupsListResponseSchema,
|
|
@@ -139,7 +140,7 @@ function buildLogsStreamUrl(sessionId: string | undefined): string {
|
|
|
139
140
|
params.set("sessionId", sessionId);
|
|
140
141
|
}
|
|
141
142
|
const query = params.toString();
|
|
142
|
-
return query.length > 0 ? `/api/logs/stream?${query}` : "/api/logs/stream";
|
|
143
|
+
return withBasePath(query.length > 0 ? `/api/logs/stream?${query}` : "/api/logs/stream");
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
function buildSessionLogsPageUrl(
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type PiAgentResponseView,
|
|
14
14
|
} from "./piAgentChatLogic";
|
|
15
15
|
import { useProviders } from "../../lib/useProviders";
|
|
16
|
+
import { withBasePath } from "../../lib/basePath";
|
|
16
17
|
import { dispatchLogFocusRequest } from "../proxy-viewer/logFocus";
|
|
17
18
|
import { AnswerMarkdown } from "../proxy-viewer/AnswerMarkdown";
|
|
18
19
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
|
@@ -273,7 +274,7 @@ export function PiAgentPanel({
|
|
|
273
274
|
logLimit: 16,
|
|
274
275
|
};
|
|
275
276
|
|
|
276
|
-
void fetch("/api/pi-agent", {
|
|
277
|
+
void fetch(withBasePath("/api/pi-agent"), {
|
|
277
278
|
method: "POST",
|
|
278
279
|
headers: {
|
|
279
280
|
"content-type": "application/json",
|