@tonyclaw/agent-inspector 3.1.32 → 4.0.2

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 (373) hide show
  1. package/.output/backend/nitro.json +1 -1
  2. package/.output/cli.js +241 -120
  3. package/.output/server/_ssr/index.mjs +1 -1
  4. package/.output/server/_ssr/{router-oXUk3MBR.mjs → router-BBY_zWRK.mjs} +91 -3
  5. package/.output/server/index.mjs +1 -1
  6. package/.output/ui/assets/{CompareDrawer-BtfPSpkV.js → CompareDrawer-rtNElN-B.js} +1 -1
  7. package/.output/ui/assets/{InspectorPet-D9DPPQ3n.js → InspectorPet-QaAiuF3d.js} +1 -1
  8. package/.output/ui/assets/{ProxyViewerContainer-Buu8vkCK.js → ProxyViewerContainer-6twNZHwl.js} +4 -4
  9. package/.output/ui/assets/{ReplayDialog-BER-QzpD.js → ReplayDialog-B7KlziO2.js} +1 -1
  10. package/.output/ui/assets/{RequestAnatomy-C8Zwxrr7.js → RequestAnatomy-2XVFBrW4.js} +1 -1
  11. package/.output/ui/assets/{ResponseView-DDpKdYk6.js → ResponseView-BULgzLVB.js} +1 -1
  12. package/.output/ui/assets/{StreamingChunkSequence-sMPGG7Sw.js → StreamingChunkSequence-DSkvkbns.js} +1 -1
  13. package/.output/ui/assets/{_sessionId-e4IheHnP.js → _sessionId-D2dmJi3G.js} +1 -1
  14. package/.output/ui/assets/{_sessionId-C5Uv72Fx.js → _sessionId-DcS0s1Mr.js} +1 -1
  15. package/.output/ui/assets/{index-B__qT0Yd.js → index-B0TN1KKO.js} +2 -2
  16. package/.output/ui/assets/{index-Be3bTCzi.js → index-Bva1zHEs.js} +1 -1
  17. package/.output/ui/assets/{index-C9q__p8J.js → index-BzBExOxu.js} +1 -1
  18. package/.output/ui/assets/{index-CtB0deSZ.js → index-k6sRQz55.js} +1 -1
  19. package/.output/ui/assets/{json-viewer-Db0AFp-7.js → json-viewer-CWM4tY-j.js} +1 -1
  20. package/.output/ui/assets/{jszip.min-DkY0Ym_q.js → jszip.min-BkWvRFlo.js} +1 -1
  21. package/.output/ui/index.html +1 -1
  22. package/package.json +8 -13
  23. package/src/assets/IDE/claudecode-color.svg +0 -1
  24. package/src/assets/IDE/codex-color.svg +0 -1
  25. package/src/assets/IDE/opencode.svg +0 -1
  26. package/src/assets/IDE/xiaomimimo.svg +0 -1
  27. package/src/assets/favicon.svg +0 -21
  28. package/src/assets/logos/alibaba.svg +0 -1
  29. package/src/assets/logos/anthropic.svg +0 -1
  30. package/src/assets/logos/claude-code.svg +0 -4
  31. package/src/assets/logos/deepseek.svg +0 -1
  32. package/src/assets/logos/minimax.jpeg +0 -0
  33. package/src/assets/logos/openai.svg +0 -1
  34. package/src/assets/logos/opencode.svg +0 -4
  35. package/src/assets/logos/qwen.webp +0 -0
  36. package/src/assets/logos/zhipuai.svg +0 -219
  37. package/src/backend/router.tsx +0 -36
  38. package/src/backend/routes/$.ts +0 -26
  39. package/src/backend/routes/__root.ts +0 -4
  40. package/src/backend/routes/api/-instances.ts +0 -7
  41. package/src/backend/routes/api/alerts.summary.ts +0 -28
  42. package/src/backend/routes/api/alerts.ts +0 -70
  43. package/src/backend/routes/api/config.paths.ts +0 -14
  44. package/src/backend/routes/api/config.ts +0 -73
  45. package/src/backend/routes/api/ecosystem.packages.$packageId.help.ts +0 -11
  46. package/src/backend/routes/api/ecosystem.packages.$packageId.install.ts +0 -11
  47. package/src/backend/routes/api/ecosystem.packages.$packageId.runner-presets.ts +0 -11
  48. package/src/backend/routes/api/ecosystem.packages.$packageId.upgrade.ts +0 -11
  49. package/src/backend/routes/api/ecosystem.packages.ts +0 -21
  50. package/src/backend/routes/api/ecosystem.recipes.$recipeId.run.ts +0 -11
  51. package/src/backend/routes/api/ecosystem.tasks.$taskId.ts +0 -16
  52. package/src/backend/routes/api/ecosystem.tasks.ts +0 -12
  53. package/src/backend/routes/api/groups.$groupId.evidence.ts +0 -55
  54. package/src/backend/routes/api/groups.$groupId.sessions.ts +0 -50
  55. package/src/backend/routes/api/groups.$groupId.ts +0 -93
  56. package/src/backend/routes/api/groups.ts +0 -79
  57. package/src/backend/routes/api/health.ts +0 -22
  58. package/src/backend/routes/api/instances.$name.connection.ts +0 -12
  59. package/src/backend/routes/api/instances.$name.restart.ts +0 -23
  60. package/src/backend/routes/api/instances.$name.start.ts +0 -18
  61. package/src/backend/routes/api/instances.$name.stop.ts +0 -23
  62. package/src/backend/routes/api/instances.$name.ts +0 -12
  63. package/src/backend/routes/api/instances.ts +0 -16
  64. package/src/backend/routes/api/knowledge.candidates.$candidateId.promote.ts +0 -32
  65. package/src/backend/routes/api/knowledge.candidates.$candidateId.ts +0 -50
  66. package/src/backend/routes/api/knowledge.candidates.ts +0 -10
  67. package/src/backend/routes/api/knowledge.project-context.ts +0 -29
  68. package/src/backend/routes/api/knowledge.search.ts +0 -62
  69. package/src/backend/routes/api/knowledge.sessions.$sessionId.candidates.ts +0 -26
  70. package/src/backend/routes/api/logs.$id.body.ts +0 -58
  71. package/src/backend/routes/api/logs.$id.chunks.ts +0 -37
  72. package/src/backend/routes/api/logs.$id.replay.ts +0 -297
  73. package/src/backend/routes/api/logs.$id.ts +0 -23
  74. package/src/backend/routes/api/logs.import.ts +0 -39
  75. package/src/backend/routes/api/logs.stream.ts +0 -174
  76. package/src/backend/routes/api/logs.ts +0 -214
  77. package/src/backend/routes/api/mcp.ts +0 -25
  78. package/src/backend/routes/api/models.ts +0 -10
  79. package/src/backend/routes/api/pi-agent.ts +0 -29
  80. package/src/backend/routes/api/providers.$providerId.model-metadata.ts +0 -173
  81. package/src/backend/routes/api/providers.$providerId.test.log.ts +0 -342
  82. package/src/backend/routes/api/providers.$providerId.ts +0 -111
  83. package/src/backend/routes/api/providers.export.ts +0 -44
  84. package/src/backend/routes/api/providers.import.ts +0 -81
  85. package/src/backend/routes/api/providers.scan.ts +0 -28
  86. package/src/backend/routes/api/providers.ts +0 -102
  87. package/src/backend/routes/api/runs.$runId.evidence.ts +0 -55
  88. package/src/backend/routes/api/runs.$runId.ts +0 -50
  89. package/src/backend/routes/api/runs.ts +0 -62
  90. package/src/backend/routes/api/runtime.ts +0 -46
  91. package/src/backend/routes/api/sessions.ts +0 -51
  92. package/src/backend/routes/api/storage.ts +0 -56
  93. package/src/backend/routes/livez.ts +0 -13
  94. package/src/backend/routes/metrics.ts +0 -14
  95. package/src/backend/routes/proxy/$.ts +0 -15
  96. package/src/backend/routes/readyz.ts +0 -18
  97. package/src/backend/start.ts +0 -10
  98. package/src/cli/alias.ts +0 -431
  99. package/src/cli/detect-tools.ts +0 -123
  100. package/src/cli/doctor.ts +0 -836
  101. package/src/cli/instance.ts +0 -1071
  102. package/src/cli/instanceApi.ts +0 -213
  103. package/src/cli/instanceArgs.ts +0 -301
  104. package/src/cli/instanceControl.ts +0 -191
  105. package/src/cli/instanceModel.ts +0 -276
  106. package/src/cli/instanceRegistry.ts +0 -731
  107. package/src/cli/networkHints.ts +0 -170
  108. package/src/cli/onboard.ts +0 -1668
  109. package/src/cli/onboardTarget.ts +0 -228
  110. package/src/cli/rustBackendPackage.ts +0 -458
  111. package/src/cli/startupOutput.ts +0 -16
  112. package/src/cli/templates/codex-skill-onboard.ts +0 -320
  113. package/src/cli/templates/command-onboard.ts +0 -25
  114. package/src/cli/templates/skill-onboard.ts +0 -625
  115. package/src/cli.ts +0 -1397
  116. package/src/components/OnboardingBanner.tsx +0 -115
  117. package/src/components/ProxyViewer.tsx +0 -2259
  118. package/src/components/ProxyViewerContainer.tsx +0 -1076
  119. package/src/components/alerts/AlertsDialog.tsx +0 -615
  120. package/src/components/clients/ClientLogo.tsx +0 -175
  121. package/src/components/ecosystem/AgentLabDialog.tsx +0 -947
  122. package/src/components/errors/SafeErrorBoundary.tsx +0 -201
  123. package/src/components/groups/GroupsDialog.tsx +0 -1304
  124. package/src/components/inspector-pet/InspectorPet.tsx +0 -398
  125. package/src/components/inspector-pet/InspectorPetCrab3D.tsx +0 -455
  126. package/src/components/inspector-pet/inspectorPetLogic.ts +0 -272
  127. package/src/components/pi-agent/PiAgentPanel.tsx +0 -446
  128. package/src/components/pi-agent/piAgentChatLogic.ts +0 -273
  129. package/src/components/providers/ImportWizardDialog.tsx +0 -281
  130. package/src/components/providers/ProviderCard.tsx +0 -347
  131. package/src/components/providers/ProviderForm.tsx +0 -1183
  132. package/src/components/providers/ProviderLogo.tsx +0 -122
  133. package/src/components/providers/ProviderTestResultsView.tsx +0 -734
  134. package/src/components/providers/ProvidersPanel.tsx +0 -714
  135. package/src/components/providers/SettingsDialog.tsx +0 -1042
  136. package/src/components/providers/providerFormCapabilities.ts +0 -36
  137. package/src/components/proxy-viewer/AgentTraceSummary.tsx +0 -902
  138. package/src/components/proxy-viewer/AnswerMarkdown.tsx +0 -51
  139. package/src/components/proxy-viewer/ApplicationBar.tsx +0 -123
  140. package/src/components/proxy-viewer/CompareDrawer.tsx +0 -984
  141. package/src/components/proxy-viewer/ConversationGroup.tsx +0 -208
  142. package/src/components/proxy-viewer/ConversationGroupList.tsx +0 -652
  143. package/src/components/proxy-viewer/ConversationHeader.tsx +0 -429
  144. package/src/components/proxy-viewer/LiveConnectionStatus.tsx +0 -186
  145. package/src/components/proxy-viewer/LogEntry.tsx +0 -1419
  146. package/src/components/proxy-viewer/LogEntryHeader.tsx +0 -596
  147. package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -57
  148. package/src/components/proxy-viewer/RawExportWarning.tsx +0 -77
  149. package/src/components/proxy-viewer/ReplayDialog.tsx +0 -636
  150. package/src/components/proxy-viewer/RequestToolsPanel.tsx +0 -299
  151. package/src/components/proxy-viewer/ResponseView.tsx +0 -202
  152. package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +0 -61
  153. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +0 -265
  154. package/src/components/proxy-viewer/ThreadConnector.tsx +0 -165
  155. package/src/components/proxy-viewer/ToolTraceEvents.tsx +0 -64
  156. package/src/components/proxy-viewer/TurnGroup.tsx +0 -383
  157. package/src/components/proxy-viewer/TurnGroupList.tsx +0 -156
  158. package/src/components/proxy-viewer/accessibility.ts +0 -8
  159. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +0 -887
  160. package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +0 -222
  161. package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +0 -737
  162. package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +0 -268
  163. package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +0 -53
  164. package/src/components/proxy-viewer/anatomy/types.ts +0 -41
  165. package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +0 -114
  166. package/src/components/proxy-viewer/bodyHydration.ts +0 -123
  167. package/src/components/proxy-viewer/cacheTrend.ts +0 -50
  168. package/src/components/proxy-viewer/diff/DiffView.tsx +0 -321
  169. package/src/components/proxy-viewer/diff/computeDiff.ts +0 -178
  170. package/src/components/proxy-viewer/diff/index.ts +0 -3
  171. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +0 -194
  172. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +0 -81
  173. package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +0 -21
  174. package/src/components/proxy-viewer/formats/index.tsx +0 -47
  175. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +0 -442
  176. package/src/components/proxy-viewer/index.ts +0 -9
  177. package/src/components/proxy-viewer/lazy.ts +0 -41
  178. package/src/components/proxy-viewer/liveConnectionState.ts +0 -158
  179. package/src/components/proxy-viewer/log-formats/anthropic.ts +0 -191
  180. package/src/components/proxy-viewer/log-formats/index.ts +0 -23
  181. package/src/components/proxy-viewer/log-formats/openai.ts +0 -256
  182. package/src/components/proxy-viewer/log-formats/types.ts +0 -40
  183. package/src/components/proxy-viewer/log-formats/unknown.ts +0 -18
  184. package/src/components/proxy-viewer/logEntryVisibility.ts +0 -73
  185. package/src/components/proxy-viewer/logFocus.ts +0 -54
  186. package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +0 -124
  187. package/src/components/proxy-viewer/proxyViewerLogic.ts +0 -488
  188. package/src/components/proxy-viewer/replayComparison.ts +0 -131
  189. package/src/components/proxy-viewer/requestDiff.ts +0 -277
  190. package/src/components/proxy-viewer/requestTools.ts +0 -220
  191. package/src/components/proxy-viewer/useCopyFeedback.ts +0 -38
  192. package/src/components/proxy-viewer/useKeyboardNavigation.ts +0 -232
  193. package/src/components/proxy-viewer/viewerState.ts +0 -176
  194. package/src/components/ui/badge.tsx +0 -47
  195. package/src/components/ui/button.tsx +0 -47
  196. package/src/components/ui/collapsible.tsx +0 -21
  197. package/src/components/ui/confirm-dialog.tsx +0 -51
  198. package/src/components/ui/crab-logo.tsx +0 -45
  199. package/src/components/ui/crab-variants.tsx +0 -467
  200. package/src/components/ui/dialog.tsx +0 -130
  201. package/src/components/ui/icon-trigger.ts +0 -2
  202. package/src/components/ui/json-viewer-bulk.ts +0 -132
  203. package/src/components/ui/json-viewer.tsx +0 -508
  204. package/src/components/ui/scroll-area.tsx +0 -54
  205. package/src/components/ui/select.tsx +0 -178
  206. package/src/components/ui/separator.tsx +0 -28
  207. package/src/components/ui/tabs.tsx +0 -88
  208. package/src/components/ui/tooltip.tsx +0 -51
  209. package/src/components/ui/transient-toast.tsx +0 -105
  210. package/src/contracts/anthropic.ts +0 -230
  211. package/src/contracts/index.ts +0 -59
  212. package/src/contracts/json.ts +0 -37
  213. package/src/contracts/log.ts +0 -139
  214. package/src/contracts/openai.ts +0 -267
  215. package/src/index.css +0 -11
  216. package/src/knowledge/candidateStore.ts +0 -94
  217. package/src/knowledge/distiller.ts +0 -98
  218. package/src/knowledge/openclawClient.ts +0 -161
  219. package/src/knowledge/openclawGatewayClient.ts +0 -249
  220. package/src/knowledge/openclawMarkdown.ts +0 -146
  221. package/src/knowledge/redactor.ts +0 -80
  222. package/src/knowledge/types.ts +0 -83
  223. package/src/lib/alertContract.ts +0 -76
  224. package/src/lib/apiClient.ts +0 -166
  225. package/src/lib/apiFormat.ts +0 -29
  226. package/src/lib/backendImplementation.ts +0 -16
  227. package/src/lib/basePath.ts +0 -107
  228. package/src/lib/browserRuntimeContract.ts +0 -124
  229. package/src/lib/browserRuntimeResolver.ts +0 -333
  230. package/src/lib/chunkLoadRecovery.ts +0 -83
  231. package/src/lib/claudeCodeStrip.ts +0 -98
  232. package/src/lib/clipboard.ts +0 -67
  233. package/src/lib/codingAgentEndpoints.ts +0 -54
  234. package/src/lib/ecosystemContract.ts +0 -130
  235. package/src/lib/export-logs.ts +0 -287
  236. package/src/lib/groupContract.ts +0 -166
  237. package/src/lib/instanceContract.ts +0 -239
  238. package/src/lib/jsonFile.ts +0 -46
  239. package/src/lib/logImportContract.ts +0 -12
  240. package/src/lib/managedInstance.ts +0 -112
  241. package/src/lib/mask.ts +0 -4
  242. package/src/lib/objectUtils.ts +0 -22
  243. package/src/lib/piAgentContract.ts +0 -102
  244. package/src/lib/providerContract.ts +0 -125
  245. package/src/lib/providerImportContract.ts +0 -27
  246. package/src/lib/providerModelMetadata.ts +0 -458
  247. package/src/lib/providerTestContract.ts +0 -170
  248. package/src/lib/providerTestPrompt.ts +0 -100
  249. package/src/lib/publicBasePathContext.ts +0 -137
  250. package/src/lib/resourceLimits.ts +0 -345
  251. package/src/lib/runContract.ts +0 -164
  252. package/src/lib/runtimeConfig.ts +0 -43
  253. package/src/lib/safeDiagnostic.ts +0 -38
  254. package/src/lib/safeFetch.ts +0 -508
  255. package/src/lib/serverPort.ts +0 -47
  256. package/src/lib/sessionInfoContract.ts +0 -78
  257. package/src/lib/sessionUrl.ts +0 -46
  258. package/src/lib/ssrfGuard.ts +0 -133
  259. package/src/lib/stopReason.ts +0 -130
  260. package/src/lib/themeMode.ts +0 -41
  261. package/src/lib/timeDisplay.ts +0 -22
  262. package/src/lib/toolTrace.ts +0 -290
  263. package/src/lib/uiScale.ts +0 -117
  264. package/src/lib/unknown.ts +0 -27
  265. package/src/lib/upstreamUrl.ts +0 -150
  266. package/src/lib/useAlerts.ts +0 -82
  267. package/src/lib/useGroupEvidence.ts +0 -37
  268. package/src/lib/useGroups.ts +0 -32
  269. package/src/lib/useOnboarding.ts +0 -93
  270. package/src/lib/useProviders.ts +0 -30
  271. package/src/lib/useStripConfig.ts +0 -154
  272. package/src/lib/useThemeMode.ts +0 -40
  273. package/src/lib/useUiScale.ts +0 -52
  274. package/src/lib/utils.ts +0 -78
  275. package/src/mcp/currentContext.ts +0 -65
  276. package/src/mcp/instanceHandlers.ts +0 -151
  277. package/src/mcp/loopback.ts +0 -88
  278. package/src/mcp/mode.ts +0 -75
  279. package/src/mcp/previewExtractor.ts +0 -233
  280. package/src/mcp/server.ts +0 -2274
  281. package/src/mcp/toolHandlers.ts +0 -1533
  282. package/src/proxy/alerts.ts +0 -664
  283. package/src/proxy/chunkStorage.ts +0 -123
  284. package/src/proxy/config.ts +0 -218
  285. package/src/proxy/constants.ts +0 -66
  286. package/src/proxy/dataDir.ts +0 -147
  287. package/src/proxy/ecosystemExecutionConfirmation.ts +0 -114
  288. package/src/proxy/ecosystemExecutionRoute.ts +0 -116
  289. package/src/proxy/ecosystemRegistry.ts +0 -296
  290. package/src/proxy/ecosystemTasks.ts +0 -415
  291. package/src/proxy/evidenceAnalysis.ts +0 -526
  292. package/src/proxy/evidenceExporter.ts +0 -235
  293. package/src/proxy/formats/anthropic/anthropicProvider.ts +0 -75
  294. package/src/proxy/formats/anthropic/handler.ts +0 -75
  295. package/src/proxy/formats/anthropic/index.ts +0 -13
  296. package/src/proxy/formats/anthropic/register.ts +0 -4
  297. package/src/proxy/formats/anthropic/schemas.ts +0 -6
  298. package/src/proxy/formats/anthropic/stream.ts +0 -205
  299. package/src/proxy/formats/handler.ts +0 -46
  300. package/src/proxy/formats/index.ts +0 -13
  301. package/src/proxy/formats/jsonSchema.ts +0 -2
  302. package/src/proxy/formats/openai/alibabaProvider.ts +0 -34
  303. package/src/proxy/formats/openai/codexMetadata.ts +0 -61
  304. package/src/proxy/formats/openai/handler.ts +0 -133
  305. package/src/proxy/formats/openai/index.ts +0 -33
  306. package/src/proxy/formats/openai/provider.ts +0 -50
  307. package/src/proxy/formats/openai/register.ts +0 -4
  308. package/src/proxy/formats/openai/schemas.ts +0 -25
  309. package/src/proxy/formats/openai/stream.ts +0 -600
  310. package/src/proxy/formats/openai/zhipuProvider.ts +0 -31
  311. package/src/proxy/formats/providerAdapter.ts +0 -50
  312. package/src/proxy/formats/providerRegistry.ts +0 -51
  313. package/src/proxy/formats/providers/index.ts +0 -3
  314. package/src/proxy/formats/registry.ts +0 -65
  315. package/src/proxy/groupEvidenceExporter.ts +0 -371
  316. package/src/proxy/groupStore.ts +0 -336
  317. package/src/proxy/handler.ts +0 -729
  318. package/src/proxy/identityProxy.ts +0 -1842
  319. package/src/proxy/jsonlRecovery.ts +0 -554
  320. package/src/proxy/logBodyChunks.ts +0 -91
  321. package/src/proxy/logFinalizer.ts +0 -777
  322. package/src/proxy/logFinalizer.worker.ts +0 -24
  323. package/src/proxy/logImportUpload.ts +0 -128
  324. package/src/proxy/logImporter.ts +0 -813
  325. package/src/proxy/logIndex.ts +0 -663
  326. package/src/proxy/logSearch.ts +0 -122
  327. package/src/proxy/logger.ts +0 -537
  328. package/src/proxy/openaiOrphanToolStrip.ts +0 -142
  329. package/src/proxy/platformCommands.ts +0 -253
  330. package/src/proxy/privateDataPath.ts +0 -419
  331. package/src/proxy/providerImporters.ts +0 -578
  332. package/src/proxy/providerScanStore.ts +0 -87
  333. package/src/proxy/providerSecretStore.ts +0 -216
  334. package/src/proxy/providers.ts +0 -998
  335. package/src/proxy/rawStreamCapture.ts +0 -342
  336. package/src/proxy/runFailures.ts +0 -94
  337. package/src/proxy/runStore.ts +0 -214
  338. package/src/proxy/runtimeAdmission.ts +0 -55
  339. package/src/proxy/runtimeHealth.ts +0 -221
  340. package/src/proxy/runtimeMetrics.ts +0 -149
  341. package/src/proxy/runtimeShutdown.ts +0 -157
  342. package/src/proxy/schemas.ts +0 -112
  343. package/src/proxy/sessionArchive.ts +0 -479
  344. package/src/proxy/sessionInfo.ts +0 -266
  345. package/src/proxy/sessionProcess.ts +0 -194
  346. package/src/proxy/sessionRuntime.ts +0 -121
  347. package/src/proxy/sessionSupervisor.ts +0 -305
  348. package/src/proxy/sessionWorkerEntry.ts +0 -26
  349. package/src/proxy/shutdownCoordinator.ts +0 -90
  350. package/src/proxy/socketTracker.ts +0 -639
  351. package/src/proxy/sqliteLogIndex.ts +0 -733
  352. package/src/proxy/storageLifecycle.ts +0 -432
  353. package/src/proxy/store.ts +0 -1773
  354. package/src/proxy/streamTiming.ts +0 -31
  355. package/src/proxy/toolSchemaWarnings.ts +0 -529
  356. package/src/proxy/uiAssetServer.ts +0 -106
  357. package/src/proxy/upstream.ts +0 -127
  358. package/src/services/alerts.ts +0 -12
  359. package/src/services/groups.ts +0 -119
  360. package/src/services/piAgent.ts +0 -421
  361. package/src/services/piAgentRetrieval.ts +0 -1180
  362. package/src/services/result.ts +0 -17
  363. package/src/services/runs.ts +0 -76
  364. package/src/ui/bootstrap.ts +0 -167
  365. package/src/ui/index.html +0 -38
  366. package/src/ui/logsStreamClient.ts +0 -20
  367. package/src/ui/main.tsx +0 -13
  368. package/src/ui/public/runtime-config.js +0 -2
  369. package/src/ui/router.tsx +0 -13
  370. package/src/ui/routes/__root.tsx +0 -69
  371. package/src/ui/routes/index.tsx +0 -35
  372. package/src/ui/routes/session/$sessionId.tsx +0 -50
  373. package/src/ui/runtimeDiscovery.ts +0 -50
package/src/cli.ts DELETED
@@ -1,1397 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Buffer } from "node:buffer";
3
- import { spawn, type ChildProcess } from "node:child_process";
4
- import { createConnection } from "node:net";
5
- import { fileURLToPath } from "node:url";
6
- import { dirname, join, resolve as resolvePath } from "node:path";
7
- import { appendFileSync, existsSync, mkdirSync } from "node:fs";
8
- import type { Readable, Writable } from "node:stream";
9
- import packageJson from "../package.json";
10
- import {
11
- AGENT_INSPECTOR_BACKEND_ENV,
12
- AGENT_INSPECTOR_BACKEND_HEADER,
13
- BackendImplementationSchema,
14
- parseBackendImplementation,
15
- type BackendImplementation,
16
- } from "./lib/backendImplementation.js";
17
- import {
18
- AGENT_INSPECTOR_BASE_PATH_ENV,
19
- AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV,
20
- appendBasePathToOrigin,
21
- getConfiguredBasePath,
22
- normalizePublicBasePath,
23
- } from "./lib/basePath.js";
24
- import {
25
- AGENT_INSPECTOR_CLI_ENTRY_ENV,
26
- managedInstanceShutdownMessage,
27
- readManagedInstanceLaunch,
28
- type ManagedInstanceLaunch,
29
- } from "./lib/managedInstance.js";
30
- import {
31
- formatAccessHintLines,
32
- isLoopbackHost,
33
- isValidBindHost,
34
- localUrlForPort,
35
- networkUrlsForBindHost,
36
- probeHostForBindHost,
37
- urlForHost,
38
- } from "./cli/networkHints.js";
39
- import {
40
- shouldSuppressServerOutputLine,
41
- type ServerOutputFilterContext,
42
- } from "./cli/startupOutput.js";
43
- import {
44
- resolvePreferredBackendImplementation,
45
- resolveRustBackendPackage,
46
- } from "./cli/rustBackendPackage.js";
47
- import { handleNamedInstanceApi } from "./cli/instanceApi.js";
48
- import {
49
- closeIdentityProxyGracefully,
50
- forceCloseIdentityProxy,
51
- parseUiOriginAllowlist,
52
- startIdentityProxy,
53
- stopIdentityProxyAdmission,
54
- type ManagedInstanceControlOptions,
55
- } from "./proxy/identityProxy.js";
56
- import { findPidsByPort, killPid, openUrlCommand } from "./proxy/platformCommands.js";
57
- import { resolveDataDir } from "./proxy/dataDir.js";
58
-
59
- const __filename = fileURLToPath(import.meta.url);
60
- const __dirname = dirname(__filename);
61
-
62
- // v3 defaults the public entrypoint to 9527. The legacy 9527 alias can be
63
- // enabled explicitly for older AI-tool and MCP configs. The TanStack Start
64
- // runtime stays on a private upstream port and public ports reverse-proxy to
65
- // that same runtime, so logs, sessions, and MCP state remain unified.
66
- const DEFAULT_PORT = 9527;
67
- const LEGACY_ALIAS_PORT = 9527;
68
- const DEFAULT_UPSTREAM_PORT = 9529;
69
- const DEFAULT_BIND_HOST = "127.0.0.1";
70
- const LOCAL_PROBE_TIMEOUT_MS = 5000;
71
- const BRANDED_WINDOWS_RUNTIME_EXE = "agent-inspector.exe";
72
- const DEFAULT_CAPTURE_MODE = "simple";
73
- const DEFAULT_SHUTDOWN_DEADLINE_MS = 15_000;
74
- const SUPERVISOR_SHUTDOWN_GRACE_MS = 2_000;
75
- const TYPESCRIPT_STARTUP_READY_TIMEOUT_MS = 10_000;
76
- const RUST_STARTUP_READY_TIMEOUT_MS = 60_000;
77
- const AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV = "AGENT_INSPECTOR_BACKGROUND_SUPERVISOR";
78
-
79
- type CaptureMode = "simple" | "full";
80
-
81
- process.title = "Agent Inspector";
82
-
83
- /**
84
- * Subcommand router. The legacy one-liner UX (`agent-inspector` with no args,
85
- * or `agent-inspector start`) starts the proxy. `agent-inspector onboard` runs
86
- * the guided skill install. Unknown subcommands fall back to the legacy
87
- * behavior so a stray positional arg doesn't break an old invocation.
88
- */
89
- const subcommand = process.argv[2];
90
-
91
- function printGlobalHelp(): void {
92
- console.log(`agent-inspector ${packageJson.version} - Agent observability and knowledge capture
93
-
94
- Usage:
95
- agent-inspector [start] [options]
96
- agent-inspector instance <start|list|status|connect|stop|restart> ...
97
- agent-inspector alias <list|add|remove> ...
98
- agent-inspector onboard [options]
99
- agent-inspector doctor [options]
100
-
101
- Global options:
102
- -h, --help Show this help without starting a runtime
103
- -v, --version Print the package version
104
-
105
- Start options:
106
- --backend <typescript|rust> Select the backend implementation (default: rust when native package is available)
107
- --backend-only, --headless Start the API, proxy, and MCP runtime without serving the UI
108
- --with-ui Serve the independently built UI through the protected ingress
109
- --port, -p <port> Public ingress port (default: 9527)
110
- --host, -H <host> Bind host (default: 127.0.0.1)
111
- --background Start a hidden supervisor and return after readiness
112
- --no-open Do not open the UI after startup
113
-
114
- Run \`agent-inspector instance --help\`, \`agent-inspector onboard --help\`, or
115
- \`agent-inspector doctor --help\` for command-specific options.`);
116
- }
117
-
118
- switch (subcommand) {
119
- case "--help":
120
- case "-h":
121
- printGlobalHelp();
122
- process.exitCode = 0;
123
- break;
124
- case "--version":
125
- case "-v":
126
- console.log(packageJson.version);
127
- process.exitCode = 0;
128
- break;
129
- case "onboard": {
130
- const { runOnboard } = await import("./cli/onboard.js");
131
- const code = await runOnboard(process.argv.slice(3));
132
- process.exit(code);
133
- }
134
- case "doctor": {
135
- const { runDoctor } = await import("./cli/doctor.js");
136
- const code = await runDoctor(process.argv.slice(3));
137
- process.exit(code);
138
- }
139
- case "alias": {
140
- const { runAlias } = await import("./cli/alias.js");
141
- const code = await runAlias(process.argv.slice(3));
142
- process.exit(code);
143
- }
144
- case "instance": {
145
- const { runInstance } = await import("./cli/instance.js");
146
- const code = await runInstance(process.argv.slice(3));
147
- process.exitCode = code;
148
- break;
149
- }
150
- case undefined:
151
- await runStart(process.argv.slice(2));
152
- break;
153
- case "start":
154
- await runStart(process.argv.slice(3));
155
- break;
156
- default:
157
- if (subcommand.startsWith("-")) {
158
- await runStart(process.argv.slice(2));
159
- } else {
160
- console.error(`agent-inspector: unknown command ${subcommand}`);
161
- console.error("Run `agent-inspector --help` for supported commands.");
162
- process.exitCode = 2;
163
- }
164
- }
165
-
166
- // -----------------------------------------------------------------------------
167
- // Legacy `start` behavior — start the proxy on the configured port. Extracted
168
- // into a function so the router above can keep the top-level flow readable.
169
- // -----------------------------------------------------------------------------
170
- async function isInspectorHealthy(port: number, host?: string, basePath = ""): Promise<boolean> {
171
- const controller = new AbortController();
172
- const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
173
- try {
174
- const response = await fetch(
175
- appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/health", basePath),
176
- {
177
- cache: "no-store",
178
- signal: controller.signal,
179
- },
180
- );
181
- return response.ok;
182
- } catch {
183
- return false;
184
- } finally {
185
- clearTimeout(timeout);
186
- }
187
- }
188
-
189
- async function getRunningCaptureMode(
190
- port: number,
191
- host?: string,
192
- basePath = "",
193
- ): Promise<CaptureMode | null> {
194
- const controller = new AbortController();
195
- const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
196
- try {
197
- const response = await fetch(
198
- appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/config", basePath),
199
- {
200
- cache: "no-store",
201
- signal: controller.signal,
202
- },
203
- );
204
- if (!response.ok) return null;
205
- const raw: unknown = await response.json();
206
- return readCaptureMode(raw);
207
- } catch {
208
- return null;
209
- } finally {
210
- clearTimeout(timeout);
211
- }
212
- }
213
-
214
- async function getRunningBackend(
215
- port: number,
216
- host?: string,
217
- basePath = "",
218
- ): Promise<BackendImplementation | null> {
219
- const controller = new AbortController();
220
- const timeout = setTimeout(() => controller.abort(), LOCAL_PROBE_TIMEOUT_MS);
221
- try {
222
- const response = await fetch(
223
- appendBasePathToOrigin(urlForHost(port, probeHostForBindHost(host)), "/api/health", basePath),
224
- {
225
- cache: "no-store",
226
- signal: controller.signal,
227
- },
228
- );
229
- if (!response.ok) return null;
230
- const header = response.headers.get(AGENT_INSPECTOR_BACKEND_HEADER);
231
- if (header === null) return "typescript";
232
- const parsed = BackendImplementationSchema.safeParse(header.trim().toLowerCase());
233
- return parsed.success ? parsed.data : null;
234
- } catch {
235
- return null;
236
- } finally {
237
- clearTimeout(timeout);
238
- }
239
- }
240
-
241
- function isPortAcceptingConnections(port: number, host?: string): Promise<boolean> {
242
- return new Promise((resolve) => {
243
- const socket = createConnection({ host: probeHostForBindHost(host), port });
244
- const finish = (value: boolean): void => {
245
- socket.removeAllListeners();
246
- socket.destroy();
247
- resolve(value);
248
- };
249
-
250
- socket.setTimeout(LOCAL_PROBE_TIMEOUT_MS);
251
- socket.once("connect", () => finish(true));
252
- socket.once("timeout", () => finish(false));
253
- socket.once("error", () => finish(false));
254
- });
255
- }
256
-
257
- function sleep(ms: number): Promise<void> {
258
- return new Promise((resolve) => {
259
- setTimeout(resolve, ms);
260
- });
261
- }
262
-
263
- function startupReadyTimeoutMs(backend: BackendImplementation): number {
264
- const configured = Number(process.env["AGENT_INSPECTOR_STARTUP_READY_TIMEOUT_MS"] ?? "");
265
- if (Number.isInteger(configured) && configured > 0) return configured;
266
- return backend === "rust" ? RUST_STARTUP_READY_TIMEOUT_MS : TYPESCRIPT_STARTUP_READY_TIMEOUT_MS;
267
- }
268
-
269
- async function waitForInspectorHealthy(
270
- port: number,
271
- timeoutMs: number,
272
- host?: string,
273
- basePath = "",
274
- ): Promise<boolean> {
275
- const start = Date.now();
276
- while (Date.now() - start < timeoutMs) {
277
- if (await isInspectorHealthy(port, host, basePath)) return true;
278
- await sleep(250);
279
- }
280
- return false;
281
- }
282
-
283
- function openBrowser(targetUrl: string): void {
284
- const command = openUrlCommand(targetUrl);
285
- if (command === null) return;
286
- const browserProcess = spawn(command.command, command.args, {
287
- stdio: "ignore",
288
- detached: true,
289
- windowsHide: true,
290
- });
291
- browserProcess.once("error", () => {
292
- // Opening a browser is best-effort. Minimal Linux/container images may not
293
- // include xdg-open, and that should not make the CLI look unhealthy.
294
- });
295
- browserProcess.unref();
296
- }
297
-
298
- function waitForProcessExit(child: ChildProcess): Promise<number> {
299
- return new Promise((resolve) => {
300
- child.once("exit", (code) => {
301
- resolve(code ?? 1);
302
- });
303
- child.once("error", () => {
304
- resolve(1);
305
- });
306
- });
307
- }
308
-
309
- function pipeServerOutput(child: ChildProcess, context: ServerOutputFilterContext): void {
310
- pipeServerOutputStream(child.stdout, process.stdout, context);
311
- pipeServerOutputStream(child.stderr, process.stderr, context);
312
- }
313
-
314
- type BackgroundSupervisorLogs = {
315
- stdoutPath: string;
316
- stderrPath: string;
317
- };
318
-
319
- function createBackgroundSupervisorLogs(
320
- env: Readonly<Record<string, string | undefined>>,
321
- ): BackgroundSupervisorLogs | null {
322
- try {
323
- const logsDir = join(resolveDataDir(existsSync, env), "logs");
324
- mkdirSync(logsDir, { recursive: true });
325
- const stdoutPath = join(logsDir, "backend.stdout.log");
326
- const stderrPath = join(logsDir, "backend.stderr.log");
327
- const header = `\n[${new Date().toISOString()}] Agent Inspector backend process started\n`;
328
- appendFileSync(stdoutPath, header, { encoding: "utf8" });
329
- appendFileSync(stderrPath, header, { encoding: "utf8" });
330
- return { stdoutPath, stderrPath };
331
- } catch (error) {
332
- const message = error instanceof Error ? error.message : String(error);
333
- process.stderr.write(`Failed to open Agent Inspector background logs: ${message}\n`);
334
- return null;
335
- }
336
- }
337
-
338
- function pipeServerOutputStream(
339
- stream: Readable | null,
340
- target: Writable,
341
- context: ServerOutputFilterContext,
342
- ): void {
343
- if (stream === null) return;
344
-
345
- let buffered = "";
346
- stream.on("data", (chunk: unknown) => {
347
- const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
348
- const lines = `${buffered}${text}`.split(/\r?\n/);
349
- buffered = lines.pop() ?? "";
350
- for (const line of lines) {
351
- if (!shouldSuppressServerOutputLine(line, context)) {
352
- target.write(`${line}\n`);
353
- }
354
- }
355
- });
356
- stream.on("end", () => {
357
- if (buffered.length === 0) return;
358
- if (!shouldSuppressServerOutputLine(buffered, context)) {
359
- target.write(buffered);
360
- }
361
- buffered = "";
362
- });
363
- }
364
-
365
- function appendBackgroundLog(path: string, text: string): void {
366
- try {
367
- appendFileSync(path, text, { encoding: "utf8" });
368
- } catch {
369
- // Background supervisor output is best-effort: startup must not fail just
370
- // because a diagnostic log file is temporarily unavailable.
371
- }
372
- }
373
-
374
- function pipeServerOutputStreamToFile(
375
- stream: Readable | null,
376
- targetPath: string,
377
- context: ServerOutputFilterContext,
378
- ): void {
379
- if (stream === null) return;
380
-
381
- let buffered = "";
382
- stream.on("data", (chunk: unknown) => {
383
- const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
384
- const lines = `${buffered}${text}`.split(/\r?\n/);
385
- buffered = lines.pop() ?? "";
386
- for (const line of lines) {
387
- if (!shouldSuppressServerOutputLine(line, context)) {
388
- appendBackgroundLog(targetPath, `${line}\n`);
389
- }
390
- }
391
- });
392
- stream.on("end", () => {
393
- if (buffered.length === 0) return;
394
- if (!shouldSuppressServerOutputLine(buffered, context)) {
395
- appendBackgroundLog(targetPath, buffered);
396
- }
397
- buffered = "";
398
- });
399
- }
400
-
401
- type ServerCommand = {
402
- command: string;
403
- args: string[];
404
- };
405
-
406
- type ServerCommandResolution =
407
- | { kind: "success"; command: ServerCommand }
408
- | { kind: "error"; message: string };
409
-
410
- function resolveServerCommand(
411
- outputDir: string,
412
- serverPath: string,
413
- backend: BackendImplementation,
414
- ): ServerCommandResolution {
415
- if (backend === "rust") {
416
- const resolved = resolveRustBackendPackage({ rootVersion: packageJson.version });
417
- return resolved.kind === "error"
418
- ? {
419
- kind: "error",
420
- message: `Cannot start the Rust backend (${resolved.error.code}): ${resolved.error.message}`,
421
- }
422
- : {
423
- kind: "success",
424
- command: { command: resolved.value.command, args: [...resolved.value.args] },
425
- };
426
- }
427
- const brandedRuntime = join(outputDir, BRANDED_WINDOWS_RUNTIME_EXE);
428
- return {
429
- kind: "success",
430
- command:
431
- process.platform === "win32" && existsSync(brandedRuntime)
432
- ? { command: brandedRuntime, args: [serverPath] }
433
- : { command: process.execPath, args: [serverPath] },
434
- };
435
- }
436
-
437
- function parseCaptureMode(value: string | undefined): CaptureMode | null {
438
- switch (value) {
439
- case undefined:
440
- return null;
441
- case "simple":
442
- return "simple";
443
- case "full":
444
- return "full";
445
- default:
446
- return null;
447
- }
448
- }
449
-
450
- function readCaptureMode(raw: unknown): CaptureMode | null {
451
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
452
- const descriptor = Object.getOwnPropertyDescriptor(raw, "captureMode");
453
- const value: unknown = descriptor?.value;
454
- return typeof value === "string" ? parseCaptureMode(value) : null;
455
- }
456
-
457
- function printAccessHints(
458
- port: number,
459
- host: string | undefined,
460
- basePath: string,
461
- serveUi: boolean,
462
- publicOrigin: string | undefined,
463
- ): void {
464
- const networkUrls = networkUrlsForBindHost(port, host);
465
- for (const line of formatAccessHintLines(port, networkUrls, host, basePath, serveUi)) {
466
- console.log(line);
467
- }
468
- if (publicOrigin !== undefined) {
469
- const publicBaseUrl = appendBasePathToOrigin(publicOrigin, "/", basePath);
470
- if (serveUi) console.log(` Public UI: ${publicBaseUrl}`);
471
- console.log(` Public control Base: ${publicBaseUrl}`);
472
- console.log(` Public agent Base: ${publicBaseUrl}`);
473
- console.log(` Public REST API: ${appendBasePathToOrigin(publicOrigin, "/api", basePath)}`);
474
- console.log(` Public proxy: ${appendBasePathToOrigin(publicOrigin, "/proxy", basePath)}`);
475
- console.log(` Public MCP: ${appendBasePathToOrigin(publicOrigin, "/api/mcp", basePath)}`);
476
- }
477
- if (!isLoopbackHost(host)) {
478
- const proxyTokenConfigured = (process.env["AGENT_INSPECTOR_PROXY_TOKEN"] ?? "") !== "";
479
- const legacyProxyEnabled =
480
- process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1";
481
- if (proxyTokenConfigured) {
482
- console.log(" Remote proxy: dedicated token authentication enabled");
483
- } else if (legacyProxyEnabled) {
484
- console.warn(" Remote proxy: UNAUTHENTICATED compatibility mode enabled");
485
- } else {
486
- console.warn(" Remote proxy: blocked until AGENT_INSPECTOR_PROXY_TOKEN is configured");
487
- }
488
- if (process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] === "1") {
489
- console.warn(" Control plane: REMOTE ACCESS ENABLED by administrator override");
490
- } else {
491
- console.log(" Control plane: local only by default");
492
- if ((process.env["AGENT_INSPECTOR_CONTROL_TOKEN"] ?? "") !== "") {
493
- console.log(" Remote API: Bearer token authentication enabled");
494
- }
495
- }
496
- }
497
- }
498
-
499
- function readPositiveIntegerEnv(name: string): number | undefined {
500
- const raw = process.env[name];
501
- if (raw === undefined || raw.trim().length === 0) return undefined;
502
- const parsed = Number(raw);
503
- if (Number.isSafeInteger(parsed) && parsed > 0) return parsed;
504
- console.warn(`Ignoring invalid ${name}=${raw}; expected a positive integer.`);
505
- return undefined;
506
- }
507
-
508
- function normalizeExplicitPublicOrigin(value: string | undefined): string | null | undefined {
509
- if (value === undefined || value.trim().length === 0) return undefined;
510
- try {
511
- const parsed = new URL(value.trim());
512
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
513
- if (parsed.username.length > 0 || parsed.password.length > 0) return null;
514
- if (parsed.pathname !== "/" || parsed.search.length > 0 || parsed.hash.length > 0) return null;
515
- return parsed.origin;
516
- } catch {
517
- return null;
518
- }
519
- }
520
-
521
- type BackgroundSupervisorOptions = {
522
- backend: BackendImplementation;
523
- port: number;
524
- host: string | undefined;
525
- configDir: string | undefined;
526
- providersJson: string | undefined;
527
- captureMode: CaptureMode;
528
- enableIdentityProxy: boolean;
529
- legacyAliasEnabled: boolean;
530
- serveUi: boolean;
531
- };
532
-
533
- function buildBackgroundSupervisorArgs(options: BackgroundSupervisorOptions): string[] {
534
- const args = [
535
- "--no-open",
536
- "--backend",
537
- options.backend,
538
- "--port",
539
- String(options.port),
540
- "--mode",
541
- options.captureMode,
542
- ];
543
- if (options.host !== undefined) {
544
- args.push("--host", options.host);
545
- }
546
- if (options.configDir !== undefined) {
547
- args.push("--config-dir", options.configDir);
548
- }
549
- if (options.providersJson !== undefined) {
550
- args.push("--providers", options.providersJson);
551
- }
552
- if (!options.enableIdentityProxy) {
553
- args.push("--no-identity-proxy");
554
- }
555
- if (options.legacyAliasEnabled) {
556
- args.push("--legacy-port");
557
- }
558
- if (!options.serveUi) {
559
- args.push("--backend-only");
560
- }
561
- return args;
562
- }
563
-
564
- function pickUpstreamPort(publicPort: number, legacyAliasPort: number | null): number {
565
- const nearbyPort = publicPort + 2 <= 65535 ? publicPort + 2 : publicPort - 2;
566
- const candidates =
567
- publicPort === DEFAULT_PORT
568
- ? [DEFAULT_UPSTREAM_PORT, nearbyPort, LEGACY_ALIAS_PORT + 2]
569
- : [nearbyPort, DEFAULT_UPSTREAM_PORT, LEGACY_ALIAS_PORT + 2];
570
- for (const candidate of candidates) {
571
- if (
572
- Number.isInteger(candidate) &&
573
- candidate > 0 &&
574
- candidate <= 65535 &&
575
- candidate !== publicPort &&
576
- candidate !== legacyAliasPort
577
- ) {
578
- return candidate;
579
- }
580
- }
581
- return DEFAULT_UPSTREAM_PORT;
582
- }
583
-
584
- /** Try to start an identity-proxy ingress. The primary caller treats a
585
- * `null` result as fatal; only an optional compatibility alias may degrade. */
586
- async function tryStartIdentityProxy(
587
- identityPort: number,
588
- upstreamPort: number,
589
- listenHost: string,
590
- upstreamHost: string,
591
- managedInstance: ManagedInstanceControlOptions | undefined,
592
- allowedUiOrigins: readonly string[],
593
- uiDirectory: string | undefined,
594
- publicOrigin: string | undefined,
595
- backend: BackendImplementation,
596
- ): Promise<import("node:http").Server | null> {
597
- try {
598
- const server = await startIdentityProxy({
599
- listenPort: identityPort,
600
- listenHost,
601
- upstreamHost,
602
- upstreamPort,
603
- backendImplementation: backend,
604
- restrictRemoteControl: !isLoopbackHost(listenHost),
605
- allowRemoteControl: process.env["AGENT_INSPECTOR_ALLOW_REMOTE_CONTROL"] === "1",
606
- controlToken: process.env["AGENT_INSPECTOR_CONTROL_TOKEN"],
607
- proxyToken: process.env["AGENT_INSPECTOR_PROXY_TOKEN"],
608
- allowUnauthenticatedRemoteProxy:
609
- process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1",
610
- trustedHosts: (process.env["AGENT_INSPECTOR_TRUSTED_HOSTS"] ?? "")
611
- .split(",")
612
- .map((host) => host.trim())
613
- .filter((host) => host.length > 0),
614
- allowedUiOrigins,
615
- uiDirectory,
616
- publicOrigin,
617
- maxRequestBytes: readPositiveIntegerEnv("AGENT_INSPECTOR_MAX_REQUEST_BYTES"),
618
- requestTimeoutMs: readPositiveIntegerEnv("AGENT_INSPECTOR_REQUEST_TIMEOUT_MS"),
619
- managedInstance,
620
- instanceApiHandler: backend === "rust" ? handleNamedInstanceApi : undefined,
621
- });
622
- return server;
623
- } catch (err) {
624
- const message = err instanceof Error ? err.message : String(err);
625
- console.warn(`[identity-proxy] Could not listen on port ${identityPort}: ${message}.`);
626
- return null;
627
- }
628
- }
629
-
630
- async function runStart(args: string[]): Promise<void> {
631
- const managedLaunchResult = readManagedInstanceLaunch(process.env);
632
- if (managedLaunchResult.kind === "invalid") {
633
- console.error(managedLaunchResult.message);
634
- process.exitCode = 1;
635
- return;
636
- }
637
- const managedLaunch: ManagedInstanceLaunch | null =
638
- managedLaunchResult.kind === "managed" ? managedLaunchResult.launch : null;
639
- const envPort = process.env["AGENT_INSPECTOR_PORT"] ?? process.env["PORT"];
640
- const portDefault = envPort !== undefined ? Number(envPort) : DEFAULT_PORT;
641
- const envHost = process.env["NITRO_HOST"] ?? process.env["HOST"];
642
- const envMode =
643
- process.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? process.env["AGENT_INSPECTOR_MODE"];
644
- const rawEnvBackend = process.env[AGENT_INSPECTOR_BACKEND_ENV];
645
- const envBackend =
646
- rawEnvBackend === undefined ? undefined : parseBackendImplementation(rawEnvBackend);
647
- if (envBackend === null) {
648
- console.error(
649
- `Invalid ${AGENT_INSPECTOR_BACKEND_ENV}. Use typescript or rust; no backend was started.`,
650
- );
651
- process.exitCode = 1;
652
- return;
653
- }
654
-
655
- let port = portDefault;
656
- let host =
657
- envHost !== undefined && envHost.trim().length > 0 ? envHost.trim() : DEFAULT_BIND_HOST;
658
- let open = true;
659
- let openWasSpecified = false;
660
- let background = false;
661
- let forceRestart = false;
662
- let configDir: string | undefined;
663
- let providersJson: string | undefined;
664
- let captureMode: CaptureMode = DEFAULT_CAPTURE_MODE;
665
- let captureModeWasSpecified = false;
666
- let enableIdentityProxy = true;
667
- let legacyAliasEnabled = false;
668
- let serveUi = process.env["AGENT_INSPECTOR_BACKEND_ONLY"] !== "1";
669
- let backend: BackendImplementation = envBackend ?? "typescript";
670
- let backendWasSpecified = envBackend !== undefined;
671
-
672
- if (envMode !== undefined && envMode !== "") {
673
- const parsedMode = parseCaptureMode(envMode);
674
- if (parsedMode === null) {
675
- console.error(`Invalid capture mode: ${envMode}. Use simple or full.`);
676
- process.exitCode = 1;
677
- return;
678
- }
679
- captureMode = parsedMode;
680
- captureModeWasSpecified = true;
681
- }
682
-
683
- if (host !== undefined && !isValidBindHost(host)) {
684
- console.error(`Invalid host: ${host}. Use an IP or hostname without protocol/path.`);
685
- process.exitCode = 1;
686
- return;
687
- }
688
-
689
- for (let i = 0; i < args.length; i++) {
690
- const arg = args[i] ?? "";
691
- if (arg.startsWith("--port=")) {
692
- port = Number(arg.slice(arg.indexOf("=") + 1));
693
- continue;
694
- }
695
- if (arg.startsWith("--host=")) {
696
- const value = arg.slice(arg.indexOf("=") + 1).trim();
697
- if (!isValidBindHost(value)) {
698
- console.error(`Invalid host: ${value}. Use an IP or hostname without protocol/path.`);
699
- process.exitCode = 1;
700
- return;
701
- }
702
- host = value;
703
- continue;
704
- }
705
- if (arg.startsWith("--mode=") || arg.startsWith("--capture-mode=")) {
706
- const value = arg.slice(arg.indexOf("=") + 1);
707
- const parsedMode = parseCaptureMode(value);
708
- if (parsedMode === null) {
709
- console.error(`Invalid capture mode: ${value}. Use simple or full.`);
710
- process.exitCode = 1;
711
- return;
712
- }
713
- captureMode = parsedMode;
714
- captureModeWasSpecified = true;
715
- continue;
716
- }
717
- if (arg.startsWith("--backend=")) {
718
- const parsedBackend = BackendImplementationSchema.safeParse(
719
- arg
720
- .slice(arg.indexOf("=") + 1)
721
- .trim()
722
- .toLowerCase(),
723
- );
724
- if (!parsedBackend.success) {
725
- console.error("Invalid backend implementation. Use typescript or rust.");
726
- process.exitCode = 1;
727
- return;
728
- }
729
- backend = parsedBackend.data;
730
- backendWasSpecified = true;
731
- continue;
732
- }
733
- switch (arg) {
734
- case "--port":
735
- case "-p":
736
- port = Number(args[i + 1]);
737
- i++;
738
- break;
739
- case "--host":
740
- case "-H": {
741
- const value = args[i + 1]?.trim() ?? "";
742
- if (!isValidBindHost(value)) {
743
- console.error(`Invalid host: ${value}. Use an IP or hostname without protocol/path.`);
744
- process.exitCode = 1;
745
- return;
746
- }
747
- host = value;
748
- i++;
749
- break;
750
- }
751
- case "--backend": {
752
- const parsedBackend = BackendImplementationSchema.safeParse(
753
- args[i + 1]?.trim().toLowerCase(),
754
- );
755
- if (!parsedBackend.success) {
756
- console.error("Invalid backend implementation. Use typescript or rust.");
757
- process.exitCode = 1;
758
- return;
759
- }
760
- backend = parsedBackend.data;
761
- backendWasSpecified = true;
762
- i++;
763
- break;
764
- }
765
- case "--no-open":
766
- open = false;
767
- openWasSpecified = true;
768
- break;
769
- case "--open":
770
- open = true;
771
- openWasSpecified = true;
772
- break;
773
- case "--force-restart":
774
- case "--restart":
775
- forceRestart = true;
776
- break;
777
- case "--background":
778
- background = true;
779
- break;
780
- case "--backend-only":
781
- case "--headless":
782
- serveUi = false;
783
- open = false;
784
- break;
785
- case "--with-ui":
786
- serveUi = true;
787
- break;
788
- case "--config-dir":
789
- configDir = args[i + 1];
790
- i++;
791
- break;
792
- case "--providers":
793
- providersJson = args[i + 1];
794
- i++;
795
- break;
796
- case "--mode":
797
- case "--capture-mode": {
798
- const value = args[i + 1];
799
- const parsedMode = parseCaptureMode(value);
800
- if (parsedMode === null) {
801
- console.error(`Invalid capture mode: ${String(value)}. Use simple or full.`);
802
- process.exitCode = 1;
803
- return;
804
- }
805
- captureMode = parsedMode;
806
- captureModeWasSpecified = true;
807
- i++;
808
- break;
809
- }
810
- case "--no-identity-proxy":
811
- enableIdentityProxy = false;
812
- break;
813
- case "--identity-proxy-port": {
814
- const value = args[i + 1];
815
- const parsed = Number(value);
816
- if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
817
- console.error(`Invalid identity-proxy port: ${String(value)}.`);
818
- process.exitCode = 1;
819
- return;
820
- }
821
- port = parsed;
822
- i++;
823
- break;
824
- }
825
- case "--no-legacy-port":
826
- legacyAliasEnabled = false;
827
- break;
828
- case "--legacy-port":
829
- legacyAliasEnabled = true;
830
- break;
831
- case "--help":
832
- case "-h":
833
- printGlobalHelp();
834
- return;
835
- case "--version":
836
- case "-v":
837
- console.log(packageJson.version);
838
- return;
839
- default:
840
- console.error(`agent-inspector start: unknown option ${arg}`);
841
- console.error("Run `agent-inspector --help` for supported options.");
842
- process.exitCode = 2;
843
- return;
844
- }
845
- }
846
-
847
- if (!Number.isInteger(port) || port <= 0 || port > 65535) {
848
- console.error(`Invalid port: ${String(port)}. Use --port <1-65535>.`);
849
- process.exitCode = 1;
850
- return;
851
- }
852
-
853
- if (!backendWasSpecified) {
854
- backend =
855
- managedLaunch?.backend ??
856
- resolvePreferredBackendImplementation({ rootVersion: packageJson.version });
857
- }
858
-
859
- if (managedLaunch !== null && managedLaunch.backend !== backend) {
860
- console.error(
861
- `Managed instance backend mismatch: launch identity requires ${managedLaunch.backend}, but ${backend} was selected.`,
862
- );
863
- process.exitCode = 1;
864
- return;
865
- }
866
-
867
- if (!enableIdentityProxy && process.env["AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS"] !== "1") {
868
- console.error(
869
- "Refusing --no-identity-proxy because it also disables Host, CSRF, credential-scope, and first-hop budget enforcement. Keep the protected ingress enabled or set AGENT_INSPECTOR_ALLOW_UNPROTECTED_INGRESS=1 explicitly for temporary diagnostics.",
870
- );
871
- process.exitCode = 1;
872
- return;
873
- }
874
- if (!serveUi) open = false;
875
- if (!enableIdentityProxy && serveUi) {
876
- console.error(
877
- "The decoupled UI is composed only by the protected ingress. Use --backend-only with --no-identity-proxy, or keep the identity proxy enabled.",
878
- );
879
- process.exitCode = 1;
880
- return;
881
- }
882
- const uiOriginAllowlist = parseUiOriginAllowlist(process.env["AGENT_INSPECTOR_UI_ORIGINS"]);
883
- if (uiOriginAllowlist.rejected.length > 0) {
884
- console.error(
885
- `Invalid AGENT_INSPECTOR_UI_ORIGINS entries: ${uiOriginAllowlist.rejected.join(", ")}. Only exact loopback http(s) origins are accepted; use a same-origin authenticated gateway for remote UI hosting.`,
886
- );
887
- process.exitCode = 1;
888
- return;
889
- }
890
- const publicOrigin = normalizeExplicitPublicOrigin(process.env["AGENT_INSPECTOR_PUBLIC_ORIGIN"]);
891
- if (publicOrigin === null) {
892
- console.error(
893
- "Invalid AGENT_INSPECTOR_PUBLIC_ORIGIN. Use an exact http(s) origin without credentials, path, query, or fragment.",
894
- );
895
- process.exitCode = 1;
896
- return;
897
- }
898
- const uiDirectoryCandidate = join(__dirname, "../.output/ui");
899
- const uiIndexPath = join(uiDirectoryCandidate, "index.html");
900
- const uiDirectory = serveUi && existsSync(uiIndexPath) ? uiDirectoryCandidate : undefined;
901
- if (serveUi && uiDirectory === undefined) {
902
- console.error(
903
- `Agent Inspector UI artifact is missing at ${uiIndexPath}. Run the composed build or use --backend-only.`,
904
- );
905
- process.exitCode = 1;
906
- return;
907
- }
908
-
909
- const outputDir = __dirname;
910
- const serverPath = join(outputDir, "../.output/server/index.mjs");
911
- const serverCommandResolution = resolveServerCommand(outputDir, serverPath, backend);
912
- if (serverCommandResolution.kind === "error") {
913
- console.error(serverCommandResolution.message);
914
- process.exitCode = 1;
915
- return;
916
- }
917
- const serverCommand = serverCommandResolution.command;
918
-
919
- /**
920
- * Check if a port is in use and kill the process using it
921
- */
922
- function killProcessOnPort(targetPort: number): void {
923
- const pids = findPidsByPort(targetPort);
924
- for (const pid of pids) {
925
- console.log(`Killing process ${pid} on port ${targetPort}...`);
926
- killPid(pid);
927
- }
928
- }
929
-
930
- const legacyAliasPort =
931
- enableIdentityProxy && legacyAliasEnabled && port === DEFAULT_PORT ? LEGACY_ALIAS_PORT : null;
932
-
933
- // TanStack Start listens on the private upstream port; identity proxies own
934
- // the public-facing port(s). When the identity proxy is disabled we fall
935
- // back to TanStack listening on the public port directly.
936
- const upstreamPort =
937
- managedLaunch === null
938
- ? enableIdentityProxy
939
- ? pickUpstreamPort(port, legacyAliasPort)
940
- : port
941
- : managedLaunch.upstreamPort;
942
- if (
943
- upstreamPort < 1 ||
944
- upstreamPort > 65_535 ||
945
- (enableIdentityProxy && upstreamPort === port) ||
946
- upstreamPort === legacyAliasPort
947
- ) {
948
- console.error(`Invalid managed upstream port: ${String(upstreamPort)}.`);
949
- process.exitCode = 1;
950
- return;
951
- }
952
- const upstreamHost = enableIdentityProxy ? DEFAULT_BIND_HOST : host;
953
- process.env["PORT"] = String(upstreamPort);
954
-
955
- const configuredBasePath = process.env[AGENT_INSPECTOR_BASE_PATH_ENV];
956
- if (configuredBasePath !== undefined && normalizePublicBasePath(configuredBasePath) === null) {
957
- console.error(
958
- `Invalid ${AGENT_INSPECTOR_BASE_PATH_ENV}. Use / for root or URL-safe path segments such as /inspector or /foo-bar/v1.`,
959
- );
960
- process.exitCode = 1;
961
- return;
962
- }
963
- const basePath = getConfiguredBasePath(process.env);
964
- const bindOrigin = urlForHost(port, host);
965
- const outwardOrigin = publicOrigin ?? bindOrigin;
966
- const surfaceBaseUrl = appendBasePathToOrigin(outwardOrigin, "/", basePath);
967
- const uiUrl = serveUi ? surfaceBaseUrl : null;
968
- const proxyUrl = appendBasePathToOrigin(outwardOrigin, "/proxy", basePath);
969
- const openAiProxyUrl = appendBasePathToOrigin(outwardOrigin, "/proxy/v1", basePath);
970
- const proxyMessagesUrl = appendBasePathToOrigin(outwardOrigin, "/proxy/v1/messages", basePath);
971
- const legacyUrl =
972
- legacyAliasPort === null
973
- ? null
974
- : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/", basePath);
975
- const legacyProxyUrl =
976
- legacyAliasPort === null
977
- ? null
978
- : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/proxy", basePath);
979
- const legacyMcpUrl =
980
- legacyAliasPort === null
981
- ? null
982
- : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/api/mcp", basePath);
983
-
984
- if (!forceRestart && (await isInspectorHealthy(port, host, basePath))) {
985
- const runningBackend = await getRunningBackend(port, host, basePath);
986
- if (runningBackend !== backend) {
987
- console.error(
988
- runningBackend === null
989
- ? `A healthy Agent Inspector is already running at ${surfaceBaseUrl}, but its backend implementation cannot be verified. Use --force-restart to replace it safely.`
990
- : `Agent Inspector at ${surfaceBaseUrl} uses ${runningBackend}, but ${backend} was requested. Use --force-restart to switch implementations.`,
991
- );
992
- process.exitCode = 1;
993
- return;
994
- }
995
- console.log(
996
- `agent-inspector ${serveUi ? "is already running" : "backend is already running"} at ${surfaceBaseUrl}`,
997
- );
998
- printAccessHints(port, host, basePath, serveUi, publicOrigin);
999
- if (captureModeWasSpecified) {
1000
- const runningMode = await getRunningCaptureMode(port, host, basePath);
1001
- if (runningMode !== null && runningMode !== captureMode) {
1002
- console.log(`Existing instance capture mode is ${runningMode}; requested ${captureMode}.`);
1003
- console.log(`Use --force-restart to restart with ${captureMode} mode.`);
1004
- }
1005
- }
1006
- console.log(`Use --force-restart to restart the existing instance.`);
1007
- if (open && openWasSpecified && uiUrl !== null) {
1008
- openBrowser(uiUrl);
1009
- }
1010
- return;
1011
- }
1012
-
1013
- if (!forceRestart && (await isPortAcceptingConnections(port, host))) {
1014
- console.error(`Port ${port} is already in use, but it is not a healthy agent-inspector.`);
1015
- console.error(`Stop that process, choose --port <n>, or re-run with --force-restart.`);
1016
- process.exitCode = 1;
1017
- return;
1018
- }
1019
-
1020
- if (forceRestart) {
1021
- const restartPorts = new Set<number>();
1022
- restartPorts.add(port);
1023
- if (enableIdentityProxy) {
1024
- restartPorts.add(upstreamPort);
1025
- if (legacyAliasPort !== null) restartPorts.add(legacyAliasPort);
1026
- }
1027
- for (const targetPort of restartPorts) {
1028
- killProcessOnPort(targetPort);
1029
- }
1030
- }
1031
-
1032
- if (background && enableIdentityProxy) {
1033
- const cliEntry = process.argv[1];
1034
- if (cliEntry === undefined || cliEntry.length === 0) {
1035
- console.error("Cannot resolve agent-inspector CLI entry for background supervisor.");
1036
- process.exitCode = 1;
1037
- return;
1038
- }
1039
-
1040
- const supervisorArgs = buildBackgroundSupervisorArgs({
1041
- backend,
1042
- port,
1043
- host,
1044
- configDir,
1045
- providersJson,
1046
- captureMode,
1047
- enableIdentityProxy,
1048
- legacyAliasEnabled,
1049
- serveUi,
1050
- });
1051
- const supervisorProcess = spawn(process.execPath, [cliEntry, ...supervisorArgs], {
1052
- stdio: "ignore",
1053
- detached: true,
1054
- env: {
1055
- ...process.env,
1056
- [AGENT_INSPECTOR_CLI_ENTRY_ENV]: cliEntry,
1057
- [AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV]: "1",
1058
- },
1059
- windowsHide: true,
1060
- });
1061
- supervisorProcess.once("error", (err) => {
1062
- console.error(`Failed to start Agent Inspector background supervisor: ${err.message}`);
1063
- });
1064
- supervisorProcess.unref();
1065
-
1066
- if (await waitForInspectorHealthy(port, startupReadyTimeoutMs(backend), host, basePath)) {
1067
- console.log(
1068
- `agent-inspector background ${serveUi ? "server" : "backend"} is ready at ${surfaceBaseUrl}`,
1069
- );
1070
- printAccessHints(port, host, basePath, serveUi, publicOrigin);
1071
- if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
1072
- console.log(` Legacy proxy: ${legacyProxyUrl}`);
1073
- console.log(` Legacy MCP: ${legacyMcpUrl}`);
1074
- }
1075
- console.log(``);
1076
- console.log(`Route AI coding tools through the identity proxy for accurate PID attribution:`);
1077
- console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
1078
- console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
1079
- console.log(` MiMo Code: OPENAI_BASE_URL=${openAiProxyUrl} mimo`);
1080
- if (open && openWasSpecified && uiUrl !== null) {
1081
- openBrowser(uiUrl);
1082
- }
1083
- return;
1084
- }
1085
-
1086
- console.error(`agent-inspector background server did not become ready at ${surfaceBaseUrl}.`);
1087
- process.exitCode = 1;
1088
- return;
1089
- }
1090
-
1091
- // Start the server with the branded Windows runtime when postinstall created it.
1092
- const serverEnv = { ...process.env };
1093
- const currentCliEntry = process.env[AGENT_INSPECTOR_CLI_ENTRY_ENV] ?? process.argv[1];
1094
- if (currentCliEntry !== undefined && currentCliEntry.length > 0) {
1095
- serverEnv[AGENT_INSPECTOR_CLI_ENTRY_ENV] = currentCliEntry;
1096
- }
1097
- if (configDir !== undefined) {
1098
- // Normalize MSYS / Git Bash paths to Windows native form.
1099
- // On Windows, `path.join('/c/Users/foo')` becomes `\c\Users\foo`
1100
- // (leading slash converted to backslash).
1101
- // Child processes spawned by `spawn()` won't follow that style, so
1102
- // rewrite `\c\...` (or any `\x\...` drive) to `C:\...` before
1103
- // handing the path to the proxy server. No-op on already-native
1104
- // Windows paths and on non-Windows platforms.
1105
- let resolvedPath = join(configDir);
1106
- const msysMatch = /^\\([a-z])\\(.*)$/i.exec(resolvedPath);
1107
- if (msysMatch !== null) {
1108
- const drive = (msysMatch[1] ?? "").toUpperCase();
1109
- const rest = msysMatch[2] ?? "";
1110
- resolvedPath = `${drive}:\\${rest}`;
1111
- } else {
1112
- resolvedPath = resolvePath(resolvedPath);
1113
- }
1114
- serverEnv["AGENT_INSPECTOR_CONFIG_DIR"] = resolvedPath;
1115
- }
1116
- if (providersJson !== undefined) {
1117
- serverEnv["AGENT_INSPECTOR_PROVIDERS_JSON"] = providersJson;
1118
- }
1119
- serverEnv["HOST"] = upstreamHost;
1120
- serverEnv["NITRO_HOST"] = upstreamHost;
1121
- serverEnv["PORT"] = String(upstreamPort);
1122
- serverEnv["NITRO_PORT"] = String(upstreamPort);
1123
- if (enableIdentityProxy && basePath.length > 0) {
1124
- serverEnv[AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV] = "1";
1125
- }
1126
- serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
1127
- serverEnv["PROXY_PORT"] = String(upstreamPort);
1128
- serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
1129
- serverEnv[AGENT_INSPECTOR_BACKEND_ENV] = backend;
1130
- serverEnv["AGENT_INSPECTOR_BACKEND_ONLY"] = serveUi ? "0" : "1";
1131
- const workerDir = join(__dirname, "workers");
1132
- if (existsSync(workerDir)) {
1133
- serverEnv["AGENT_INSPECTOR_WORKER_DIR"] = workerDir;
1134
- }
1135
- const runningInBackgroundSupervisor =
1136
- process.env[AGENT_INSPECTOR_BACKGROUND_SUPERVISOR_ENV] === "1";
1137
- const backgroundSupervisorLogs = runningInBackgroundSupervisor
1138
- ? createBackgroundSupervisorLogs(serverEnv)
1139
- : null;
1140
- const shouldPipeBackgroundRuntimeLogs = backgroundSupervisorLogs !== null;
1141
- const serverProcess = spawn(serverCommand.command, serverCommand.args, {
1142
- stdio:
1143
- managedLaunch === null
1144
- ? background && !shouldPipeBackgroundRuntimeLogs
1145
- ? ["ignore", "ignore", "ignore"]
1146
- : ["ignore", "pipe", "pipe"]
1147
- : backend === "typescript"
1148
- ? background && !shouldPipeBackgroundRuntimeLogs
1149
- ? ["ignore", "ignore", "ignore", "ipc"]
1150
- : ["ignore", "pipe", "pipe", "ipc"]
1151
- : background
1152
- ? shouldPipeBackgroundRuntimeLogs
1153
- ? ["ignore", "pipe", "pipe"]
1154
- : ["ignore", "ignore", "ignore"]
1155
- : ["ignore", "pipe", "pipe"],
1156
- detached: background,
1157
- env: serverEnv,
1158
- windowsHide: background || runningInBackgroundSupervisor,
1159
- });
1160
- serverProcess.once("error", (err) => {
1161
- console.error(`Failed to start Agent Inspector runtime: ${err.message}`);
1162
- });
1163
- const serverExit = waitForProcessExit(serverProcess);
1164
- if (!background || backgroundSupervisorLogs !== null) {
1165
- const outputContext = {
1166
- enableIdentityProxy,
1167
- publicPort: port,
1168
- upstreamPort,
1169
- };
1170
- if (backgroundSupervisorLogs === null) {
1171
- pipeServerOutput(serverProcess, outputContext);
1172
- } else {
1173
- pipeServerOutputStreamToFile(
1174
- serverProcess.stdout,
1175
- backgroundSupervisorLogs.stdoutPath,
1176
- outputContext,
1177
- );
1178
- pipeServerOutputStreamToFile(
1179
- serverProcess.stderr,
1180
- backgroundSupervisorLogs.stderrPath,
1181
- outputContext,
1182
- );
1183
- }
1184
- }
1185
-
1186
- // Identity proxy — a native Node HTTP server that sits in front of
1187
- // TanStack Start and looks up each client's PID via the OS connection
1188
- // table, injecting the result as `X-Agent-Inspector-Client-*` headers.
1189
- // This lets AI tools (which can't easily set those headers themselves)
1190
- // still get accurate session attribution. Started only after TanStack
1191
- // Start is healthy, so the upstream is ready by the time clients connect.
1192
- const identityProxies: Array<{
1193
- label: "primary" | "legacy";
1194
- port: number;
1195
- server: import("node:http").Server;
1196
- }> = [];
1197
- let supervisorShutdownPromise: Promise<void> | null = null;
1198
- const requestSupervisorShutdown = (signal: "SIGINT" | "SIGTERM"): void => {
1199
- if (supervisorShutdownPromise !== null) return;
1200
- for (const proxy of identityProxies) stopIdentityProxyAdmission(proxy.server);
1201
- if (managedLaunch !== null && backend === "typescript" && serverProcess.connected) {
1202
- serverProcess.send(managedInstanceShutdownMessage(managedLaunch.controlToken), (error) => {
1203
- if (error !== null && serverProcess.exitCode === null) serverProcess.kill(signal);
1204
- });
1205
- } else {
1206
- serverProcess.kill(signal);
1207
- }
1208
-
1209
- const childDeadline =
1210
- readPositiveIntegerEnv("AGENT_INSPECTOR_SHUTDOWN_TIMEOUT_MS") ?? DEFAULT_SHUTDOWN_DEADLINE_MS;
1211
- let deadline: ReturnType<typeof setTimeout> | null = null;
1212
- let forced = false;
1213
- const graceful = Promise.all([
1214
- serverExit.then(() => undefined),
1215
- ...identityProxies.map(async (proxy) => await closeIdentityProxyGracefully(proxy.server)),
1216
- ]).then(() => undefined);
1217
- const timedOut = new Promise<void>((resolve) => {
1218
- deadline = setTimeout(() => {
1219
- forced = true;
1220
- for (const proxy of identityProxies) forceCloseIdentityProxy(proxy.server);
1221
- if (serverProcess.exitCode === null) serverProcess.kill("SIGKILL");
1222
- resolve();
1223
- }, childDeadline + SUPERVISOR_SHUTDOWN_GRACE_MS);
1224
- });
1225
- supervisorShutdownPromise = Promise.race([graceful, timedOut])
1226
- .finally(() => {
1227
- if (deadline !== null) clearTimeout(deadline);
1228
- })
1229
- .then(() => {
1230
- if (forced) process.exitCode = 1;
1231
- });
1232
- };
1233
- const handleSigint = (): void => requestSupervisorShutdown("SIGINT");
1234
- const handleSigterm = (): void => requestSupervisorShutdown("SIGTERM");
1235
- const currentSupervisorShutdown = (): Promise<void> | null => supervisorShutdownPromise;
1236
- process.on("SIGINT", handleSigint);
1237
- process.on("SIGTERM", handleSigterm);
1238
- const managedInstanceControl: ManagedInstanceControlOptions | undefined =
1239
- managedLaunch === null
1240
- ? undefined
1241
- : {
1242
- name: managedLaunch.name,
1243
- instanceId: managedLaunch.instanceId,
1244
- launchId: managedLaunch.launchId,
1245
- controlToken: managedLaunch.controlToken,
1246
- supervisorPid: process.pid,
1247
- startedAt: managedLaunch.startedAt,
1248
- backend,
1249
- publicPort: port,
1250
- upstreamPort,
1251
- requestShutdown: () => requestSupervisorShutdown("SIGTERM"),
1252
- };
1253
- if (enableIdentityProxy) {
1254
- const primaryProxy = await tryStartIdentityProxy(
1255
- port,
1256
- upstreamPort,
1257
- host,
1258
- upstreamHost,
1259
- managedInstanceControl,
1260
- uiOriginAllowlist.origins,
1261
- uiDirectory,
1262
- publicOrigin,
1263
- backend,
1264
- );
1265
- if (primaryProxy === null) {
1266
- console.error(
1267
- `Agent Inspector could not bind its protected public ingress at ${bindOrigin}; stopping the private backend.`,
1268
- );
1269
- requestSupervisorShutdown("SIGTERM");
1270
- const failedStartupShutdown = currentSupervisorShutdown();
1271
- if (failedStartupShutdown !== null) await failedStartupShutdown;
1272
- process.off("SIGINT", handleSigint);
1273
- process.off("SIGTERM", handleSigterm);
1274
- process.exitCode = 1;
1275
- return;
1276
- }
1277
- identityProxies.push({ label: "primary", port, server: primaryProxy });
1278
- if (legacyAliasPort !== null) {
1279
- const legacyProxy = await tryStartIdentityProxy(
1280
- legacyAliasPort,
1281
- upstreamPort,
1282
- host,
1283
- upstreamHost,
1284
- managedInstanceControl,
1285
- uiOriginAllowlist.origins,
1286
- uiDirectory,
1287
- publicOrigin,
1288
- backend,
1289
- );
1290
- if (legacyProxy !== null) {
1291
- identityProxies.push({ label: "legacy", port: legacyAliasPort, server: legacyProxy });
1292
- }
1293
- }
1294
- }
1295
-
1296
- if (background) {
1297
- serverProcess.unref();
1298
- // Probe the upstream TanStack port directly — the identity proxy is
1299
- // not started yet so the public port is not yet accepting traffic.
1300
- const upstreamProbeBasePath = enableIdentityProxy ? "" : basePath;
1301
- if (
1302
- await waitForInspectorHealthy(
1303
- upstreamPort,
1304
- startupReadyTimeoutMs(backend),
1305
- host,
1306
- upstreamProbeBasePath,
1307
- )
1308
- ) {
1309
- console.log(
1310
- `agent-inspector background ${serveUi ? "server" : "backend"} is ready at ${surfaceBaseUrl}`,
1311
- );
1312
- if (identityProxies.length > 0) {
1313
- console.log(`Identity proxy ready on port ${port} (PID-attributing proxy).`);
1314
- }
1315
- printAccessHints(port, host, basePath, serveUi, publicOrigin);
1316
- if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
1317
- console.log(` Legacy proxy: ${legacyProxyUrl}`);
1318
- console.log(` Legacy MCP: ${legacyMcpUrl}`);
1319
- }
1320
- if (identityProxies.length > 0) {
1321
- console.log(
1322
- ``,
1323
- `Route AI coding tools through the identity proxy for accurate PID attribution:`,
1324
- ` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`,
1325
- ` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`,
1326
- ` MiMo Code: OPENAI_BASE_URL=${openAiProxyUrl} mimo`,
1327
- legacyProxyUrl === null
1328
- ? ` Override the port with --port <n> when needed.`
1329
- : ` Legacy configs using ${legacyProxyUrl} still work.`,
1330
- );
1331
- }
1332
- return;
1333
- }
1334
- console.error(`agent-inspector background server did not become ready at ${surfaceBaseUrl}.`);
1335
- process.exitCode = 1;
1336
- return;
1337
- }
1338
-
1339
- if (!(await waitForInspectorHealthy(port, startupReadyTimeoutMs(backend), host, basePath))) {
1340
- console.error(`Agent Inspector did not become ready at ${surfaceBaseUrl}; stopping startup.`);
1341
- requestSupervisorShutdown("SIGTERM");
1342
- const failedStartupShutdown = currentSupervisorShutdown();
1343
- if (failedStartupShutdown !== null) await failedStartupShutdown;
1344
- process.off("SIGINT", handleSigint);
1345
- process.off("SIGTERM", handleSigterm);
1346
- process.exitCode = 1;
1347
- return;
1348
- }
1349
-
1350
- if (identityProxies.length > 0) {
1351
- console.log(``);
1352
- console.log(`Identity proxy ready at ${bindOrigin} (PID-attributing ingress).`);
1353
- if (legacyAliasPort !== null) {
1354
- const legacyReady = identityProxies.some((proxy) => proxy.label === "legacy");
1355
- if (legacyReady) {
1356
- console.log(` Legacy alias ready at http://localhost:${legacyAliasPort}.`);
1357
- }
1358
- }
1359
- if (upstreamPort !== port) {
1360
- console.log(` Internal runtime port ${upstreamPort} is hidden behind the public proxy.`);
1361
- }
1362
- }
1363
-
1364
- console.log(
1365
- `${serveUi ? "UI and backend" : "Backend control and agent runtime"} running at ${surfaceBaseUrl}`,
1366
- );
1367
- console.log(` Capture mode: ${captureMode}`);
1368
- console.log(` Backend: ${backend}`);
1369
- console.log(` Proxy: ${proxyUrl}`);
1370
- printAccessHints(port, host, basePath, serveUi, publicOrigin);
1371
- if (legacyUrl !== null && legacyProxyUrl !== null && legacyMcpUrl !== null) {
1372
- console.log(` Legacy UI: ${legacyUrl}`);
1373
- console.log(` Legacy proxy: ${legacyProxyUrl}`);
1374
- console.log(` Legacy MCP: ${legacyMcpUrl}`);
1375
- }
1376
- console.log(``);
1377
- console.log(`Route AI coding tools through the proxy:`);
1378
- console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
1379
- console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
1380
- console.log(` MiMo Code: OPENAI_BASE_URL=${openAiProxyUrl} mimo`);
1381
- console.log(` Direct HTTP: curl ${proxyMessagesUrl} -d '{"model":"...","messages":[...]}'`);
1382
- console.log(
1383
- ` Remote/container tools: use the Network proxy URL above when shown; otherwise restart with --host 0.0.0.0 or a reachable IP.`,
1384
- );
1385
- if (open && uiUrl !== null) openBrowser(uiUrl);
1386
-
1387
- process.exitCode = await serverExit;
1388
-
1389
- const activeSupervisorShutdown = currentSupervisorShutdown();
1390
- if (activeSupervisorShutdown !== null) {
1391
- await activeSupervisorShutdown;
1392
- } else {
1393
- await Promise.all(identityProxies.map((proxy) => closeIdentityProxyGracefully(proxy.server)));
1394
- }
1395
- process.off("SIGINT", handleSigint);
1396
- process.off("SIGTERM", handleSigterm);
1397
- }