@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.
Files changed (58) hide show
  1. package/.output/cli.js +249 -84
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-BXQWabQJ.js → CompareDrawer-BjQ2HH8j.js} +1 -1
  4. package/.output/public/assets/{InspectorPet-CpQEXKcr.js → InspectorPet-DDScgJ7_.js} +1 -1
  5. package/.output/public/assets/ProxyViewerContainer-C3iHhtkc.js +126 -0
  6. package/.output/public/assets/{ReplayDialog-CnYt4UKE.js → ReplayDialog-BujXVTMJ.js} +1 -1
  7. package/.output/public/assets/{RequestAnatomy-BI25al3m.js → RequestAnatomy-iK2nOuCP.js} +1 -1
  8. package/.output/public/assets/{ResponseView-D3y-I9nb.js → ResponseView-iAuOJxem.js} +1 -1
  9. package/.output/public/assets/{StreamingChunkSequence-n90dYzPp.js → StreamingChunkSequence-DiZ21icS.js} +1 -1
  10. package/.output/public/assets/{_sessionId-DLbOYm3h.js → _sessionId-CLTlZx8L.js} +1 -1
  11. package/.output/public/assets/{_sessionId-CogrNhCS.js → _sessionId-D3WwDIit.js} +1 -1
  12. package/.output/public/assets/{index-I5zN_KrA.js → index--ND7WRCU.js} +1 -1
  13. package/.output/public/assets/{index-DXGp27k9.js → index-DGuDxV-I.js} +1 -1
  14. package/.output/public/assets/index-fXi7tXyH.js +76 -0
  15. package/.output/public/assets/{index-BWWA1Rf6.js → index-nN3UPNM0.js} +6 -6
  16. package/.output/public/assets/{json-viewer-BoB2BfHg.js → json-viewer-CzcwNVJa.js} +1 -1
  17. package/.output/public/assets/{jszip.min-CAXkxQd6.js → jszip.min-DAbOCDPI.js} +1 -1
  18. package/.output/server/{_sessionId-_hORwhWH.mjs → _sessionId-DSetUDiv.mjs} +1 -1
  19. package/.output/server/{_sessionId-DScqlEFC.mjs → _sessionId-DtRKr8Jt.mjs} +2 -2
  20. package/.output/server/_ssr/{CompareDrawer-BtGm5i_L.mjs → CompareDrawer-Cns26sqx.mjs} +2 -2
  21. package/.output/server/_ssr/{InspectorPet-BnN6lVqM.mjs → InspectorPet-CyWBvBuh.mjs} +1 -1
  22. package/.output/server/_ssr/{ProxyViewerContainer-aduEJ_Xy.mjs → ProxyViewerContainer-CimBv5WL.mjs} +58 -23
  23. package/.output/server/_ssr/{ReplayDialog-BSGZ-TFs.mjs → ReplayDialog-DrM897xL.mjs} +3 -3
  24. package/.output/server/_ssr/{RequestAnatomy-DN_eSnDN.mjs → RequestAnatomy-BRgezegq.mjs} +2 -2
  25. package/.output/server/_ssr/{ResponseView-Dk9Td_HM.mjs → ResponseView-D7u7r-lA.mjs} +2 -2
  26. package/.output/server/_ssr/{StreamingChunkSequence-DthH8Lx4.mjs → StreamingChunkSequence-DM26ZaVC.mjs} +2 -2
  27. package/.output/server/_ssr/{index-CqeZgFFF.mjs → index-BeWedHzY.mjs} +2 -2
  28. package/.output/server/_ssr/{index-DvVHL942.mjs → index-C284XGkZ.mjs} +1 -1
  29. package/.output/server/_ssr/index.mjs +2 -2
  30. package/.output/server/_ssr/{json-viewer-DLKA2jUq.mjs → json-viewer-CD_GbR7V.mjs} +2 -2
  31. package/.output/server/_ssr/{router-CRKEDn6n.mjs → router-DxPlMfLZ.mjs} +125 -58
  32. package/.output/server/{_tanstack-start-manifest_v-pQO9kTmV.mjs → _tanstack-start-manifest_v-82w9tFI-.mjs} +1 -1
  33. package/.output/server/index.mjs +84 -84
  34. package/.output/workers/logFinalizer.worker.js +6 -1
  35. package/.output/workers/sessionWorkerEntry.js +6 -1
  36. package/README.md +42 -10
  37. package/package.json +1 -1
  38. package/src/cli/doctor.ts +17 -10
  39. package/src/cli/networkHints.ts +15 -8
  40. package/src/cli/onboard.ts +10 -3
  41. package/src/cli.ts +90 -56
  42. package/src/components/ProxyViewer.tsx +11 -2
  43. package/src/components/ProxyViewerContainer.tsx +2 -1
  44. package/src/components/pi-agent/PiAgentPanel.tsx +2 -1
  45. package/src/components/providers/SettingsDialog.tsx +24 -7
  46. package/src/lib/apiClient.ts +9 -2
  47. package/src/lib/basePath.ts +83 -0
  48. package/src/lib/sessionUrl.ts +4 -2
  49. package/src/mcp/loopback.ts +2 -1
  50. package/src/mcp/server.ts +6 -2
  51. package/src/proxy/identityProxy.ts +98 -6
  52. package/src/proxy/privateDataPath.ts +15 -1
  53. package/src/proxy/sessionInfo.ts +10 -3
  54. package/src/proxy/upstream.ts +3 -1
  55. package/src/router.tsx +7 -1
  56. package/src/routes/__root.tsx +9 -3
  57. package/.output/public/assets/ProxyViewerContainer-8pNJsCJ5.js +0 -126
  58. package/.output/public/assets/index-Ca0grOxW.js +0 -76
package/.output/cli.js CHANGED
@@ -14,12 +14,76 @@ var __export = (target, all) => {
14
14
  __defProp(target, name, { get: all[name], enumerable: true });
15
15
  };
16
16
 
17
+ // src/lib/basePath.ts
18
+ function trimSlashes(value) {
19
+ return value.replace(/^\/+/u, "").replace(/\/+$/u, "");
20
+ }
21
+ function normalizeBasePath(value) {
22
+ const trimmed = value?.trim();
23
+ if (trimmed === void 0 || trimmed.length === 0 || trimmed === "/") return "";
24
+ const path2 = trimSlashes(trimmed);
25
+ if (path2.length === 0) return "";
26
+ return `/${path2}`;
27
+ }
28
+ function readDocumentBasePath() {
29
+ if (typeof document === "undefined") return void 0;
30
+ return document.querySelector(`meta[name="${AGENT_INSPECTOR_BASE_PATH_META_NAME}"]`)?.getAttribute("content") ?? void 0;
31
+ }
32
+ function readProcessBasePath() {
33
+ if (typeof process === "undefined") return void 0;
34
+ return process.env[AGENT_INSPECTOR_BASE_PATH_ENV];
35
+ }
36
+ function getConfiguredBasePath(env) {
37
+ return normalizeBasePath(
38
+ env?.[AGENT_INSPECTOR_BASE_PATH_ENV] ?? readProcessBasePath() ?? readDocumentBasePath() ?? import.meta.env?.BASE_URL ?? DEFAULT_AGENT_INSPECTOR_BASE_PATH
39
+ );
40
+ }
41
+ function withBasePath(path2, basePath = getConfiguredBasePath()) {
42
+ const normalized = normalizeBasePath(basePath);
43
+ if (/^[a-z][a-z0-9+.-]*:/iu.test(path2)) return path2;
44
+ if (normalized.length === 0) return path2;
45
+ if (path2.length === 0 || path2 === "/") return normalized;
46
+ if (path2.startsWith(`${normalized}/`) || path2 === normalized) return path2;
47
+ return path2.startsWith("/") ? `${normalized}${path2}` : `${normalized}/${path2}`;
48
+ }
49
+ function stripBasePath(pathname, basePath = getConfiguredBasePath()) {
50
+ const normalized = normalizeBasePath(basePath);
51
+ if (normalized.length === 0) return pathname;
52
+ if (pathname === normalized) return "/";
53
+ if (pathname.startsWith(`${normalized}/`)) return pathname.slice(normalized.length);
54
+ return pathname;
55
+ }
56
+ function appendBasePathToOrigin(origin, path2, basePath) {
57
+ const normalized = normalizeBasePath(basePath);
58
+ if (normalized.length === 0 && path2 === "/") return origin;
59
+ return `${origin}${withBasePath(path2, normalized)}`;
60
+ }
61
+ function stripBasePathFromRequestPath(rawPath, basePath) {
62
+ const url2 = new URL(rawPath, "http://localhost");
63
+ return `${stripBasePath(url2.pathname, basePath)}${url2.search}`;
64
+ }
65
+ var AGENT_INSPECTOR_BASE_PATH_ENV, AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV, AGENT_INSPECTOR_BASE_PATH_META_NAME, DEFAULT_AGENT_INSPECTOR_BASE_PATH;
66
+ var init_basePath = __esm({
67
+ "src/lib/basePath.ts"() {
68
+ "use strict";
69
+ AGENT_INSPECTOR_BASE_PATH_ENV = "AGENT_INSPECTOR_BASE_PATH";
70
+ AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV = "AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH";
71
+ AGENT_INSPECTOR_BASE_PATH_META_NAME = "agent-inspector-base-path";
72
+ DEFAULT_AGENT_INSPECTOR_BASE_PATH = "/inspector";
73
+ }
74
+ });
75
+
17
76
  // src/proxy/privateDataPath.ts
18
77
  import { spawnSync } from "node:child_process";
19
78
  import { chmodSync, existsSync, lstatSync, mkdirSync, readdirSync, statSync } from "node:fs";
20
79
  import { dirname, join } from "node:path";
80
+ function windowsSystemPath(executable) {
81
+ const systemRoot = process.env["SystemRoot"] ?? "C:\\Windows";
82
+ return `${systemRoot}\\System32\\${executable}`;
83
+ }
21
84
  function runWindowsCommand(executable, args) {
22
- const result = spawnSync(executable, [...args], {
85
+ const resolved = process.platform === "win32" && /^(whoami|icacls)\.exe$/iu.test(executable) ? windowsSystemPath(executable) : executable;
86
+ const result = spawnSync(resolved, [...args], {
23
87
  encoding: "utf8",
24
88
  shell: false,
25
89
  windowsHide: true
@@ -1265,6 +1329,11 @@ function actionForStatus(label, state) {
1265
1329
  }
1266
1330
  }
1267
1331
  function parseFlags(argv) {
1332
+ const defaultMcpUrl = appendBasePathToOrigin(
1333
+ DEFAULT_ORIGIN,
1334
+ "/api/mcp",
1335
+ getConfiguredBasePath(process.env)
1336
+ );
1268
1337
  const flags = {
1269
1338
  force: false,
1270
1339
  dryRun: false,
@@ -1276,12 +1345,12 @@ function parseFlags(argv) {
1276
1345
  opencodeOnly: false,
1277
1346
  opencodeConfig: null,
1278
1347
  opencodeTransport: "local",
1279
- opencodeMcpUrl: DEFAULT_MCP_URL,
1348
+ opencodeMcpUrl: defaultMcpUrl,
1280
1349
  mimo: false,
1281
1350
  mimoOnly: false,
1282
1351
  mimoConfig: null,
1283
1352
  mimoTransport: "local",
1284
- mimoMcpUrl: DEFAULT_MCP_URL,
1353
+ mimoMcpUrl: defaultMcpUrl,
1285
1354
  uninstall: false,
1286
1355
  status: false,
1287
1356
  json: false,
@@ -2473,10 +2542,11 @@ Next steps:
2473
2542
  }
2474
2543
  return 0;
2475
2544
  }
2476
- var __filename, __dirname, DEFAULT_PORT, DEFAULT_MCP_URL, LEGACY_MCP_URL, SKILL_DIR_NAME, SKILL_FILE_NAME, COMMAND_FILE_NAME, GENERATED_VERSION_PATTERN, FRONTMATTER_PATTERN, SEMVER_PATTERN;
2545
+ var __filename, __dirname, DEFAULT_PORT, DEFAULT_ORIGIN, DEFAULT_MCP_URL, LEGACY_MCP_URL, SKILL_DIR_NAME, SKILL_FILE_NAME, COMMAND_FILE_NAME, GENERATED_VERSION_PATTERN, FRONTMATTER_PATTERN, SEMVER_PATTERN;
2477
2546
  var init_onboard = __esm({
2478
2547
  "src/cli/onboard.ts"() {
2479
2548
  "use strict";
2549
+ init_basePath();
2480
2550
  init_detect_tools();
2481
2551
  init_command_onboard();
2482
2552
  init_codex_skill_onboard();
@@ -2484,7 +2554,8 @@ var init_onboard = __esm({
2484
2554
  __filename = fileURLToPath(import.meta.url);
2485
2555
  __dirname = dirname2(__filename);
2486
2556
  DEFAULT_PORT = 9527;
2487
- DEFAULT_MCP_URL = `http://localhost:${DEFAULT_PORT}/api/mcp`;
2557
+ DEFAULT_ORIGIN = `http://localhost:${DEFAULT_PORT}`;
2558
+ DEFAULT_MCP_URL = `${DEFAULT_ORIGIN}/api/mcp`;
2488
2559
  LEGACY_MCP_URL = "http://localhost:9527/api/mcp";
2489
2560
  SKILL_DIR_NAME = "agent-inspector-onboard";
2490
2561
  SKILL_FILE_NAME = "SKILL.md";
@@ -2682,14 +2753,17 @@ function checkProviderConfig(options, deps) {
2682
2753
  );
2683
2754
  }
2684
2755
  async function checkProxyHealth(port, deps) {
2685
- const health = await deps.fetchHealth(port);
2756
+ const basePath = getConfiguredBasePath(deps.env);
2757
+ const health = await deps.fetchHealth(port, basePath);
2686
2758
  const url2 = `http://localhost:${port}`;
2759
+ const proxyUrl = appendBasePathToOrigin(url2, "/proxy", basePath);
2760
+ const healthPath = appendBasePathToOrigin("", "/api/health", basePath);
2687
2761
  if (health.ok) {
2688
2762
  return check2(
2689
2763
  "Proxy health",
2690
2764
  "pass",
2691
- `agent-inspector is healthy at ${url2}.`,
2692
- `Proxy URL: ${url2}/proxy`
2765
+ `agent-inspector is healthy at ${appendBasePathToOrigin(url2, "/", basePath)}.`,
2766
+ `Proxy URL: ${proxyUrl}`
2693
2767
  );
2694
2768
  }
2695
2769
  if (await deps.isPortOpen(port)) {
@@ -2697,7 +2771,7 @@ async function checkProxyHealth(port, deps) {
2697
2771
  return check2(
2698
2772
  "Proxy health",
2699
2773
  "fail",
2700
- `Port ${port} is accepting connections, but /api/health is not healthy${suffix}.`,
2774
+ `Port ${port} is accepting connections, but ${healthPath} is not healthy${suffix}.`,
2701
2775
  "Stop that process, choose --port <n>, or start agent-inspector with --force-restart."
2702
2776
  );
2703
2777
  }
@@ -2950,14 +3024,17 @@ function doctorHelp() {
2950
3024
  ""
2951
3025
  ].join("\n");
2952
3026
  }
2953
- async function fetchHealth(port) {
3027
+ async function fetchHealth(port, basePath) {
2954
3028
  const controller = new AbortController();
2955
3029
  const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
2956
3030
  try {
2957
- const response = await fetch(`http://127.0.0.1:${port}/api/health`, {
2958
- cache: "no-store",
2959
- signal: controller.signal
2960
- });
3031
+ const response = await fetch(
3032
+ appendBasePathToOrigin(`http://127.0.0.1:${port}`, "/api/health", basePath),
3033
+ {
3034
+ cache: "no-store",
3035
+ signal: controller.signal
3036
+ }
3037
+ );
2961
3038
  return { ok: response.ok, status: response.status };
2962
3039
  } catch {
2963
3040
  return { ok: false, status: null };
@@ -3036,6 +3113,7 @@ var DEFAULT_PORT2, PROBE_TIMEOUT_MS, EXTENSION_REQUIRED_FILES;
3036
3113
  var init_doctor = __esm({
3037
3114
  "src/cli/doctor.ts"() {
3038
3115
  "use strict";
3116
+ init_basePath();
3039
3117
  init_dataDir();
3040
3118
  DEFAULT_PORT2 = 9527;
3041
3119
  PROBE_TIMEOUT_MS = 2e3;
@@ -3055,6 +3133,7 @@ var init_doctor = __esm({
3055
3133
  });
3056
3134
 
3057
3135
  // src/cli.ts
3136
+ init_basePath();
3058
3137
  import { Buffer as Buffer4 } from "node:buffer";
3059
3138
  import { spawn } from "node:child_process";
3060
3139
  import { createConnection as createConnection2 } from "node:net";
@@ -3063,6 +3142,7 @@ import { dirname as dirname3, join as join7, resolve as resolvePath } from "node
3063
3142
  import { existsSync as existsSync6 } from "node:fs";
3064
3143
 
3065
3144
  // src/cli/networkHints.ts
3145
+ init_basePath();
3066
3146
  import { networkInterfaces } from "node:os";
3067
3147
  function localUrlForPort(port) {
3068
3148
  return `http://localhost:${String(port)}`;
@@ -3137,19 +3217,23 @@ function networkUrlsForBindHost(port, host, interfaces = networkInterfaces()) {
3137
3217
  if (isWildcardHost(host)) return collectNetworkUrls(port, interfaces);
3138
3218
  return [urlForHost(port, host)];
3139
3219
  }
3140
- function formatAccessHintLines(port, networkUrls, host = void 0) {
3220
+ function accessUrl(origin, path2, basePath) {
3221
+ return appendBasePathToOrigin(origin, path2, basePath);
3222
+ }
3223
+ function formatAccessHintLines(port, networkUrls, host = void 0, basePath = "") {
3224
+ const normalizedBasePath = normalizeBasePath(basePath);
3141
3225
  const localUrl = localUrlForPort(port);
3142
3226
  const hostLabel = isWildcardHost(host) ? "all interfaces (0.0.0.0)" : trimHost(host) ?? "";
3143
3227
  const lines = ["Access URLs:", ` Bind host: ${hostLabel}`];
3144
3228
  if (isLoopbackHost(host) || isWildcardHost(host)) {
3145
- lines.push(` Local UI: ${localUrl}`);
3146
- lines.push(` Local proxy: ${localUrl}/proxy`);
3147
- lines.push(` Local MCP: ${localUrl}/api/mcp`);
3229
+ lines.push(` Local UI: ${accessUrl(localUrl, "/", normalizedBasePath)}`);
3230
+ lines.push(` Local proxy: ${accessUrl(localUrl, "/proxy", normalizedBasePath)}`);
3231
+ lines.push(` Local MCP: ${accessUrl(localUrl, "/api/mcp", normalizedBasePath)}`);
3148
3232
  } else {
3149
3233
  const boundUrl = urlForHost(port, host);
3150
- lines.push(` Bound UI: ${boundUrl}`);
3151
- lines.push(` Bound proxy: ${boundUrl}/proxy`);
3152
- lines.push(` Bound MCP: ${boundUrl}/api/mcp`);
3234
+ lines.push(` Bound UI: ${accessUrl(boundUrl, "/", normalizedBasePath)}`);
3235
+ lines.push(` Bound proxy: ${accessUrl(boundUrl, "/proxy", normalizedBasePath)}`);
3236
+ lines.push(` Bound MCP: ${accessUrl(boundUrl, "/api/mcp", normalizedBasePath)}`);
3153
3237
  lines.push(" Localhost: may not work because the server is bound to a specific host");
3154
3238
  }
3155
3239
  const firstNetworkUrl = networkUrls[0];
@@ -3158,8 +3242,8 @@ function formatAccessHintLines(port, networkUrls, host = void 0) {
3158
3242
  } else if (firstNetworkUrl === void 0) {
3159
3243
  lines.push(" Network: no non-loopback IPv4 address detected");
3160
3244
  } else {
3161
- lines.push(` Network UI: ${firstNetworkUrl}`);
3162
- lines.push(` Network proxy: ${firstNetworkUrl}/proxy`);
3245
+ lines.push(` Network UI: ${accessUrl(firstNetworkUrl, "/", normalizedBasePath)}`);
3246
+ lines.push(` Network proxy: ${accessUrl(firstNetworkUrl, "/proxy", normalizedBasePath)}`);
3163
3247
  const moreUrls = networkUrls.slice(1);
3164
3248
  if (moreUrls.length > 0) {
3165
3249
  lines.push(` Other IPs: ${moreUrls.join(", ")}`);
@@ -3180,6 +3264,7 @@ function shouldSuppressServerOutputLine(line, context) {
3180
3264
  }
3181
3265
 
3182
3266
  // src/proxy/identityProxy.ts
3267
+ init_basePath();
3183
3268
  import http from "node:http";
3184
3269
  import { Buffer as Buffer3 } from "node:buffer";
3185
3270
  import { timingSafeEqual } from "node:crypto";
@@ -18193,6 +18278,30 @@ var DOCUMENT_CONTENT_SECURITY_POLICY = [
18193
18278
  "style-src 'self' 'unsafe-inline'",
18194
18279
  "worker-src 'self' blob:"
18195
18280
  ].join("; ");
18281
+ function htmlAttributeEscape(value) {
18282
+ return value.replace(/&/gu, "&amp;").replace(/"/gu, "&quot;").replace(/</gu, "&lt;").replace(/>/gu, "&gt;");
18283
+ }
18284
+ function rewriteHtmlForBasePath(html, basePath) {
18285
+ const normalized = normalizeBasePath(basePath);
18286
+ if (normalized.length === 0) return html;
18287
+ const escaped = htmlAttributeEscape(normalized);
18288
+ const meta3 = `<meta name="${AGENT_INSPECTOR_BASE_PATH_META_NAME}" content="${escaped}">`;
18289
+ const withMeta = html.includes(`name="${AGENT_INSPECTOR_BASE_PATH_META_NAME}"`) ? html : html.replace(/<head(\s[^>]*)?>/iu, (match) => `${match}${meta3}`);
18290
+ const escapedBase = normalized.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
18291
+ return withMeta.replace(
18292
+ new RegExp(`\\b(href|src|action)=("|')/(?!/|${escapedBase.slice(1)}(?:/|$))`, "giu"),
18293
+ (_match, attr, quote) => `${attr}=${quote}${normalized}/`
18294
+ );
18295
+ }
18296
+ function javaScriptStringEscape(value) {
18297
+ return value.replace(/\\/gu, "\\\\").replace(/"/gu, '\\"');
18298
+ }
18299
+ function rewriteJavaScriptForBasePath(source, basePath) {
18300
+ const normalized = normalizeBasePath(basePath);
18301
+ if (normalized.length === 0) return source;
18302
+ const escaped = javaScriptStringEscape(normalized);
18303
+ return source.replace(/\bbasepath:\s*""/gu, `basepath:"${escaped}"`).replace(/return"\/"\+([A-Za-z_$][\w$]*)/gu, `return"${escaped}/"+$1`).replace(/(["'])\/assets\//gu, `$1${normalized}/assets/`);
18304
+ }
18196
18305
  function isLoopbackPeer(address) {
18197
18306
  if (address === void 0) return false;
18198
18307
  const normalized = address.toLowerCase();
@@ -18240,7 +18349,8 @@ function isSafeMethod(method) {
18240
18349
  return normalized === "GET" || normalized === "HEAD" || normalized === "OPTIONS";
18241
18350
  }
18242
18351
  function isPublicHealthPath(pathname) {
18243
- return pathname === "/livez" || pathname === "/readyz" || pathname === "/api/health";
18352
+ const path2 = stripBasePath(pathname, getConfiguredBasePath(process.env));
18353
+ return path2 === "/livez" || path2 === "/readyz" || path2 === "/api/health";
18244
18354
  }
18245
18355
  function configuredPositiveInteger(value, fallback) {
18246
18356
  return value !== void 0 && Number.isSafeInteger(value) && value > 0 ? value : fallback;
@@ -18253,7 +18363,10 @@ function declaredContentLength(value) {
18253
18363
  }
18254
18364
  function isBrowserMutationAllowed(input) {
18255
18365
  if (isSafeMethod(input.method)) return true;
18256
- const pathname = new URL(input.path, "http://localhost").pathname;
18366
+ const pathname = stripBasePath(
18367
+ new URL(input.path, "http://localhost").pathname,
18368
+ getConfiguredBasePath(process.env)
18369
+ );
18257
18370
  if (pathname === "/proxy" || pathname.startsWith("/proxy/")) return true;
18258
18371
  const fetchSite = input.secFetchSite?.toLowerCase();
18259
18372
  const browserRequest = input.origin !== void 0 || fetchSite !== void 0;
@@ -18273,7 +18386,10 @@ function isIdentityProxyRequestAllowed(input) {
18273
18386
  if (input.options.restrictRemoteControl !== true) return true;
18274
18387
  if (input.options.allowRemoteControl === true) return true;
18275
18388
  if (isLoopbackPeer(input.remoteAddress)) return true;
18276
- const pathname = new URL(input.path, "http://localhost").pathname;
18389
+ const pathname = stripBasePath(
18390
+ new URL(input.path, "http://localhost").pathname,
18391
+ getConfiguredBasePath(process.env)
18392
+ );
18277
18393
  if (pathname === "/proxy" || pathname.startsWith("/proxy/")) {
18278
18394
  if (input.options.allowUnauthenticatedRemoteProxy === true) return true;
18279
18395
  const proxyCredential = bearerToken(input.proxyAuthorization) ?? input.proxyTokenHeader ?? null;
@@ -18364,6 +18480,9 @@ function forceCloseIdentityProxy(server) {
18364
18480
  server.close();
18365
18481
  }
18366
18482
  async function handleRequest(req, res, options) {
18483
+ const basePath = getConfiguredBasePath(process.env);
18484
+ const upstreamPath = stripBasePathFromRequestPath(req.url ?? "/", basePath);
18485
+ const requestUrl = new URL(req.url ?? "/", "http://localhost");
18367
18486
  if (!isTrustedIdentityProxyHost(req.headers.host, options)) {
18368
18487
  res.writeHead(400, {
18369
18488
  "content-type": "application/json; charset=utf-8",
@@ -18373,6 +18492,15 @@ async function handleRequest(req, res, options) {
18373
18492
  req.resume();
18374
18493
  return;
18375
18494
  }
18495
+ if (basePath.length > 0 && requestUrl.pathname.startsWith("/assets/")) {
18496
+ res.writeHead(307, {
18497
+ location: `${basePath}${requestUrl.pathname}${requestUrl.search}`,
18498
+ "cache-control": "no-store"
18499
+ });
18500
+ res.end();
18501
+ req.resume();
18502
+ return;
18503
+ }
18376
18504
  if (!isBrowserMutationAllowed({
18377
18505
  path: req.url ?? "/",
18378
18506
  method: req.method,
@@ -18445,6 +18573,7 @@ async function handleRequest(req, res, options) {
18445
18573
  for (const [name, value] of Object.entries(req.headers)) {
18446
18574
  const lowerName = name.toLowerCase();
18447
18575
  if (REQUEST_HOP_BY_HOP.has(lowerName)) continue;
18576
+ if (basePath.length > 0 && lowerName === "accept-encoding") continue;
18448
18577
  if (lowerName.startsWith(IDENTITY_HEADER_PREFIX)) continue;
18449
18578
  if (value === void 0) continue;
18450
18579
  upstreamHeaders[name] = value;
@@ -18470,7 +18599,7 @@ async function handleRequest(req, res, options) {
18470
18599
  {
18471
18600
  hostname: options.upstreamHost,
18472
18601
  port: options.upstreamPort,
18473
- path: req.url ?? "/",
18602
+ path: upstreamPath,
18474
18603
  method: req.method,
18475
18604
  headers: upstreamHeaders
18476
18605
  },
@@ -18480,19 +18609,40 @@ async function handleRequest(req, res, options) {
18480
18609
  return;
18481
18610
  }
18482
18611
  const outHeaders = {};
18612
+ const contentType = firstHeader(proxyRes.headers["content-type"]);
18613
+ const shouldRewriteHtml = basePath.length > 0 && contentType?.toLowerCase().includes("text/html") === true;
18614
+ const shouldRewriteJavaScript = basePath.length > 0 && (contentType?.toLowerCase().includes("javascript") === true || upstreamPath.split("?", 1)[0]?.endsWith(".js") === true);
18615
+ const shouldRewriteBody = shouldRewriteHtml || shouldRewriteJavaScript;
18483
18616
  for (const [name, value] of Object.entries(proxyRes.headers)) {
18484
- if (RESPONSE_HOP_BY_HOP.has(name.toLowerCase())) continue;
18617
+ const lowerName = name.toLowerCase();
18618
+ if (RESPONSE_HOP_BY_HOP.has(lowerName)) continue;
18619
+ if (shouldRewriteBody && lowerName === "content-length") continue;
18485
18620
  if (value === void 0) continue;
18486
18621
  outHeaders[name] = value;
18487
18622
  }
18488
18623
  for (const [name, value] of Object.entries(BASE_SECURITY_HEADERS)) {
18489
18624
  outHeaders[name] = value;
18490
18625
  }
18491
- const contentType = firstHeader(proxyRes.headers["content-type"]);
18492
18626
  if (contentType?.toLowerCase().includes("text/html") === true) {
18493
18627
  outHeaders["content-security-policy"] = DOCUMENT_CONTENT_SECURITY_POLICY;
18494
18628
  }
18495
18629
  res.writeHead(proxyRes.statusCode ?? 502, outHeaders);
18630
+ if (shouldRewriteBody) {
18631
+ const chunks = [];
18632
+ proxyRes.on("data", (chunk) => {
18633
+ chunks.push(toBuffer(chunk));
18634
+ });
18635
+ proxyRes.on("end", () => {
18636
+ const body = Buffer3.concat(chunks).toString("utf8");
18637
+ res.end(
18638
+ shouldRewriteHtml ? rewriteHtmlForBasePath(body, basePath) : rewriteJavaScriptForBasePath(body, basePath)
18639
+ );
18640
+ });
18641
+ proxyRes.on("error", () => {
18642
+ if (!res.writableEnded) res.destroy();
18643
+ });
18644
+ return;
18645
+ }
18496
18646
  proxyRes.pipe(res);
18497
18647
  }
18498
18648
  );
@@ -18591,14 +18741,17 @@ if (subcommand === "doctor") {
18591
18741
  process.exit(code);
18592
18742
  }
18593
18743
  await runStart(process.argv.slice(2));
18594
- async function isInspectorHealthy(port, host) {
18744
+ async function isInspectorHealthy(port, host, basePath = "") {
18595
18745
  const controller = new AbortController();
18596
18746
  const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
18597
18747
  try {
18598
- const response = await fetch(`${urlForHost(port, probeHostForBindHost(host))}/api/health`, {
18599
- cache: "no-store",
18600
- signal: controller.signal
18601
- });
18748
+ const response = await fetch(
18749
+ appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/health", basePath),
18750
+ {
18751
+ cache: "no-store",
18752
+ signal: controller.signal
18753
+ }
18754
+ );
18602
18755
  return response.ok;
18603
18756
  } catch {
18604
18757
  return false;
@@ -18606,14 +18759,17 @@ async function isInspectorHealthy(port, host) {
18606
18759
  clearTimeout(timeout);
18607
18760
  }
18608
18761
  }
18609
- async function getRunningCaptureMode(port, host) {
18762
+ async function getRunningCaptureMode(port, host, basePath = "") {
18610
18763
  const controller = new AbortController();
18611
18764
  const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
18612
18765
  try {
18613
- const response = await fetch(`${urlForHost(port, probeHostForBindHost(host))}/api/config`, {
18614
- cache: "no-store",
18615
- signal: controller.signal
18616
- });
18766
+ const response = await fetch(
18767
+ appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/config", basePath),
18768
+ {
18769
+ cache: "no-store",
18770
+ signal: controller.signal
18771
+ }
18772
+ );
18617
18773
  if (!response.ok) return null;
18618
18774
  const raw = await response.json();
18619
18775
  return readCaptureMode(raw);
@@ -18642,10 +18798,10 @@ function sleep(ms) {
18642
18798
  setTimeout(resolve2, ms);
18643
18799
  });
18644
18800
  }
18645
- async function waitForInspectorHealthy(port, timeoutMs, host) {
18801
+ async function waitForInspectorHealthy(port, timeoutMs, host, basePath = "") {
18646
18802
  const start = Date.now();
18647
18803
  while (Date.now() - start < timeoutMs) {
18648
- if (await isInspectorHealthy(port, host)) return true;
18804
+ if (await isInspectorHealthy(port, host, basePath)) return true;
18649
18805
  await sleep(250);
18650
18806
  }
18651
18807
  return false;
@@ -18720,9 +18876,9 @@ function readCaptureMode(raw) {
18720
18876
  const value = descriptor?.value;
18721
18877
  return typeof value === "string" ? parseCaptureMode(value) : null;
18722
18878
  }
18723
- function printAccessHints(port, host) {
18879
+ function printAccessHints(port, host, basePath = "") {
18724
18880
  const networkUrls = networkUrlsForBindHost(port, host);
18725
- for (const line of formatAccessHintLines(port, networkUrls, host)) {
18881
+ for (const line of formatAccessHintLines(port, networkUrls, host, basePath)) {
18726
18882
  console.log(line);
18727
18883
  }
18728
18884
  if (!isLoopbackHost(host)) {
@@ -18966,12 +19122,19 @@ async function runStart(args) {
18966
19122
  const upstreamPort = enableIdentityProxy ? pickUpstreamPort(port, legacyAliasPort) : port;
18967
19123
  const upstreamHost = enableIdentityProxy ? DEFAULT_BIND_HOST : host;
18968
19124
  process.env["PORT"] = String(upstreamPort);
19125
+ const basePath = getConfiguredBasePath(process.env);
18969
19126
  const url2 = urlForHost(port, host);
18970
- if (!forceRestart && await isInspectorHealthy(port, host)) {
18971
- console.log(`agent-inspector is already running at ${url2}`);
18972
- printAccessHints(port, host);
19127
+ const uiUrl = appendBasePathToOrigin(url2, "/", basePath);
19128
+ const proxyUrl = appendBasePathToOrigin(url2, "/proxy", basePath);
19129
+ const proxyMessagesUrl = appendBasePathToOrigin(url2, "/proxy/v1/messages", basePath);
19130
+ const legacyUrl = legacyAliasPort === null ? null : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/", basePath);
19131
+ const legacyProxyUrl = legacyAliasPort === null ? null : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/proxy", basePath);
19132
+ const legacyMcpUrl = legacyAliasPort === null ? null : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/api/mcp", basePath);
19133
+ if (!forceRestart && await isInspectorHealthy(port, host, basePath)) {
19134
+ console.log(`agent-inspector is already running at ${uiUrl}`);
19135
+ printAccessHints(port, host, basePath);
18973
19136
  if (captureModeWasSpecified) {
18974
- const runningMode = await getRunningCaptureMode(port, host);
19137
+ const runningMode = await getRunningCaptureMode(port, host, basePath);
18975
19138
  if (runningMode !== null && runningMode !== captureMode) {
18976
19139
  console.log(`Existing instance capture mode is ${runningMode}; requested ${captureMode}.`);
18977
19140
  console.log(`Use --force-restart to restart with ${captureMode} mode.`);
@@ -18979,7 +19142,7 @@ async function runStart(args) {
18979
19142
  }
18980
19143
  console.log(`Use --force-restart to restart the existing instance.`);
18981
19144
  if (open && openWasSpecified) {
18982
- openBrowser(url2);
19145
+ openBrowser(uiUrl);
18983
19146
  }
18984
19147
  return;
18985
19148
  }
@@ -19026,44 +19189,42 @@ async function runStart(args) {
19026
19189
  console.error(`Failed to start Agent Inspector background supervisor: ${err.message}`);
19027
19190
  });
19028
19191
  supervisorProcess.unref();
19029
- if (await waitForInspectorHealthy(port, 1e4, host)) {
19030
- console.log(`agent-inspector background server is ready at ${url2}`);
19031
- printAccessHints(port, host);
19032
- if (legacyAliasPort !== null) {
19033
- console.log(` Legacy proxy: http://localhost:${legacyAliasPort}/proxy`);
19034
- console.log(` Legacy MCP: http://localhost:${legacyAliasPort}/api/mcp`);
19192
+ if (await waitForInspectorHealthy(port, 1e4, host, basePath)) {
19193
+ console.log(`agent-inspector background server is ready at ${uiUrl}`);
19194
+ printAccessHints(port, host, basePath);
19195
+ if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
19196
+ console.log(` Legacy proxy: ${legacyProxyUrl}`);
19197
+ console.log(` Legacy MCP: ${legacyMcpUrl}`);
19035
19198
  }
19036
19199
  console.log(``);
19037
19200
  console.log(`Route AI coding tools through the identity proxy for accurate PID attribution:`);
19038
- console.log(` Claude Code: ANTHROPIC_BASE_URL=http://localhost:${port}/proxy claude`);
19039
- console.log(` OpenCode: LLM_BASE_URL=http://localhost:${port}/proxy opencode`);
19040
- console.log(` MiMo Code: OPENAI_BASE_URL=http://localhost:${port}/proxy mimo`);
19201
+ console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
19202
+ console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
19203
+ console.log(` MiMo Code: OPENAI_BASE_URL=${proxyUrl} mimo`);
19041
19204
  if (open && openWasSpecified) {
19042
- openBrowser(url2);
19205
+ openBrowser(uiUrl);
19043
19206
  }
19044
19207
  return;
19045
19208
  }
19046
- console.error(`agent-inspector background server did not become ready at ${url2}.`);
19209
+ console.error(`agent-inspector background server did not become ready at ${uiUrl}.`);
19047
19210
  process.exitCode = 1;
19048
19211
  return;
19049
19212
  }
19050
- console.log(`Server running at ${url2}`);
19213
+ console.log(`Server running at ${uiUrl}`);
19051
19214
  console.log(` Capture mode: ${captureMode}`);
19052
- console.log(` Proxy: ${url2}/proxy`);
19053
- printAccessHints(port, host);
19054
- if (legacyAliasPort !== null) {
19055
- console.log(` Legacy UI: http://localhost:${legacyAliasPort}`);
19056
- console.log(` Legacy proxy: http://localhost:${legacyAliasPort}/proxy`);
19057
- console.log(` Legacy MCP: http://localhost:${legacyAliasPort}/api/mcp`);
19215
+ console.log(` Proxy: ${proxyUrl}`);
19216
+ printAccessHints(port, host, basePath);
19217
+ if (legacyUrl !== null && legacyProxyUrl !== null && legacyMcpUrl !== null) {
19218
+ console.log(` Legacy UI: ${legacyUrl}`);
19219
+ console.log(` Legacy proxy: ${legacyProxyUrl}`);
19220
+ console.log(` Legacy MCP: ${legacyMcpUrl}`);
19058
19221
  }
19059
19222
  console.log(``);
19060
19223
  console.log(`Route AI coding tools through the proxy:`);
19061
- console.log(` Claude Code: ANTHROPIC_BASE_URL=${url2}/proxy claude`);
19062
- console.log(` OpenCode: LLM_BASE_URL=${url2}/proxy opencode`);
19063
- console.log(` MiMo Code: OPENAI_BASE_URL=${url2}/proxy mimo`);
19064
- console.log(
19065
- ` Direct HTTP: curl ${url2}/proxy/v1/messages -d '{"model":"...","messages":[...]}'`
19066
- );
19224
+ console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
19225
+ console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
19226
+ console.log(` MiMo Code: OPENAI_BASE_URL=${proxyUrl} mimo`);
19227
+ console.log(` Direct HTTP: curl ${proxyMessagesUrl} -d '{"model":"...","messages":[...]}'`);
19067
19228
  console.log(
19068
19229
  ` Remote/container tools: use the Network proxy URL above when shown; otherwise restart with --host 0.0.0.0 or a reachable IP.`
19069
19230
  );
@@ -19075,7 +19236,7 @@ async function runStart(args) {
19075
19236
  ` Example: ROUTES='{"claude-":"https://api.anthropic.com","MiniMax":"https://api.minimaxi.com/anthropic"}'`
19076
19237
  );
19077
19238
  if (open) {
19078
- openBrowser(url2);
19239
+ openBrowser(uiUrl);
19079
19240
  }
19080
19241
  const outputDir = __dirname2;
19081
19242
  const serverPath = join7(outputDir, "../.output/server/index.mjs");
@@ -19098,6 +19259,9 @@ async function runStart(args) {
19098
19259
  }
19099
19260
  serverEnv["HOST"] = upstreamHost;
19100
19261
  serverEnv["NITRO_HOST"] = upstreamHost;
19262
+ if (enableIdentityProxy && basePath.length > 0) {
19263
+ serverEnv[AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV] = "1";
19264
+ }
19101
19265
  serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
19102
19266
  serverEnv["PROXY_PORT"] = String(port);
19103
19267
  serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
@@ -19173,29 +19337,30 @@ async function runStart(args) {
19173
19337
  }
19174
19338
  if (background) {
19175
19339
  serverProcess.unref();
19176
- if (await waitForInspectorHealthy(upstreamPort, 5e3, host)) {
19177
- console.log(`agent-inspector background server is ready at ${url2}`);
19340
+ const upstreamProbeBasePath = enableIdentityProxy ? "" : basePath;
19341
+ if (await waitForInspectorHealthy(upstreamPort, 5e3, host, upstreamProbeBasePath)) {
19342
+ console.log(`agent-inspector background server is ready at ${uiUrl}`);
19178
19343
  if (identityProxies.length > 0) {
19179
19344
  console.log(`Identity proxy ready on port ${port} (PID-attributing proxy).`);
19180
19345
  }
19181
- printAccessHints(port, host);
19182
- if (legacyAliasPort !== null) {
19183
- console.log(` Legacy proxy: http://localhost:${legacyAliasPort}/proxy`);
19184
- console.log(` Legacy MCP: http://localhost:${legacyAliasPort}/api/mcp`);
19346
+ printAccessHints(port, host, basePath);
19347
+ if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
19348
+ console.log(` Legacy proxy: ${legacyProxyUrl}`);
19349
+ console.log(` Legacy MCP: ${legacyMcpUrl}`);
19185
19350
  }
19186
19351
  if (identityProxies.length > 0) {
19187
19352
  console.log(
19188
19353
  ``,
19189
19354
  `Route AI coding tools through the identity proxy for accurate PID attribution:`,
19190
- ` Claude Code: ANTHROPIC_BASE_URL=http://localhost:${port}/proxy claude`,
19191
- ` OpenCode: LLM_BASE_URL=http://localhost:${port}/proxy opencode`,
19192
- ` MiMo Code: OPENAI_BASE_URL=http://localhost:${port}/proxy mimo`,
19193
- legacyAliasPort === null ? ` Override the port with --port <n> when needed.` : ` Legacy configs using http://localhost:${legacyAliasPort}/proxy still work.`
19355
+ ` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`,
19356
+ ` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`,
19357
+ ` MiMo Code: OPENAI_BASE_URL=${proxyUrl} mimo`,
19358
+ legacyProxyUrl === null ? ` Override the port with --port <n> when needed.` : ` Legacy configs using ${legacyProxyUrl} still work.`
19194
19359
  );
19195
19360
  }
19196
19361
  return;
19197
19362
  }
19198
- console.error(`agent-inspector background server did not become ready at ${url2}.`);
19363
+ console.error(`agent-inspector background server did not become ready at ${uiUrl}.`);
19199
19364
  process.exitCode = 1;
19200
19365
  return;
19201
19366
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "date": "2026-07-14T03:53:34.395Z",
2
+ "date": "2026-07-14T08:10:19.568Z",
3
3
  "preset": "node-server",
4
4
  "framework": {
5
5
  "name": "nitro",