@tonyclaw/agent-inspector 3.0.45 → 3.0.46

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 (338) hide show
  1. package/.output/{nitro.json → backend/nitro.json} +3 -3
  2. package/.output/backend-public/assets/index-D4LX-sw-.js +11 -0
  3. package/.output/cli.js +2326 -516
  4. package/.output/server/_libs/extend.mjs +4 -8
  5. package/.output/server/_libs/jszip.mjs +9 -35
  6. package/.output/server/_libs/react-dom.mjs +2 -3
  7. package/.output/server/_libs/react.mjs +1 -27
  8. package/.output/server/_libs/tanstack__react-router.mjs +1 -329
  9. package/.output/server/_libs/tanstack__router-core.mjs +42 -100
  10. package/.output/server/_libs/use-sync-external-store.mjs +1 -64
  11. package/.output/server/_libs/ws.mjs +1 -1
  12. package/.output/server/_ssr/index.mjs +4 -4
  13. package/.output/server/_ssr/{publicBasePathContext-5264L3rX.mjs → publicBasePathContext-IH2ilOfu.mjs} +52 -24
  14. package/.output/server/_ssr/{router-BJ_ltLim.mjs → router-D18yUq36.mjs} +19770 -19447
  15. package/.output/server/_ssr/{start-C6MjPjxp.mjs → start-COQu_AgD.mjs} +1 -1
  16. package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +4 -0
  17. package/.output/server/index.mjs +5 -145
  18. package/.output/{public/assets/CompareDrawer-B3_TLTJh.js → ui/assets/CompareDrawer-DAUuIJ6G.js} +1 -1
  19. package/.output/{public/assets/InspectorPet-Cpn6hv4P.js → ui/assets/InspectorPet-BRBVjOWI.js} +1 -1
  20. package/.output/ui/assets/ProxyViewerContainer-D7Sq0ctc.js +59 -0
  21. package/.output/{public/assets/ReplayDialog-B3EKVdJk.js → ui/assets/ReplayDialog-nDLsjOhs.js} +1 -1
  22. package/.output/{public/assets/RequestAnatomy-COOQjyUC.js → ui/assets/RequestAnatomy-zd4BDgL0.js} +1 -1
  23. package/.output/ui/assets/ResponseView-AdIXGcSn.js +2 -0
  24. package/.output/ui/assets/StreamingChunkSequence-CesTz8He.js +1 -0
  25. package/.output/{public/assets/_sessionId-zbgYl7ZJ.js → ui/assets/_sessionId-BAfzrhSU.js} +1 -1
  26. package/.output/{public/assets/_sessionId-BJlM3nmt.js → ui/assets/_sessionId-QnBomgPD.js} +1 -1
  27. package/.output/ui/assets/favicon-DYEkHSNl.svg +21 -0
  28. package/.output/{public/assets/index-BIK4bPTv.js → ui/assets/index-BcsdxBAd.js} +1 -1
  29. package/.output/ui/assets/index-BvXp42al.css +1 -0
  30. package/.output/{public/assets/index-D2Qw6Ri2.js → ui/assets/index-D0rtCN9V.js} +1 -1
  31. package/.output/ui/assets/index-DHpwr08Z.js +73 -0
  32. package/.output/{public/assets/index-xF_NY8Yb.js → ui/assets/index-dNuk2dsU.js} +5 -5
  33. package/.output/ui/assets/json-viewer-BvFHglMb.js +1 -0
  34. package/.output/{public/assets/jszip.min-B_rfAUCe.js → ui/assets/jszip.min-B5Z7gNZB.js} +1 -1
  35. package/.output/ui/index.html +229 -0
  36. package/.output/ui/runtime-config.js +2 -0
  37. package/.output/workers/logFinalizer.worker.js +979 -414
  38. package/.output/workers/sessionWorkerEntry.js +979 -414
  39. package/README.md +175 -43
  40. package/docs/instance-control.openapi.yaml +59 -4
  41. package/package.json +25 -5
  42. package/scripts/generate-theme-scales.mjs +3 -3
  43. package/src/{router.tsx → backend/router.tsx} +3 -15
  44. package/src/backend/routes/$.ts +26 -0
  45. package/src/backend/routes/__root.ts +4 -0
  46. package/src/{routes → backend/routes}/api/-instances.ts +3 -3
  47. package/src/{routes → backend/routes}/api/alerts.summary.ts +3 -3
  48. package/src/{routes → backend/routes}/api/alerts.ts +4 -4
  49. package/src/{routes → backend/routes}/api/config.paths.ts +1 -1
  50. package/src/{routes → backend/routes}/api/config.ts +11 -2
  51. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.help.ts +1 -1
  52. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.install.ts +1 -1
  53. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.runner-presets.ts +1 -1
  54. package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.upgrade.ts +1 -1
  55. package/src/{routes → backend/routes}/api/ecosystem.packages.ts +5 -1
  56. package/src/{routes → backend/routes}/api/ecosystem.recipes.$recipeId.run.ts +1 -1
  57. package/src/{routes → backend/routes}/api/ecosystem.tasks.$taskId.ts +1 -1
  58. package/src/{routes → backend/routes}/api/ecosystem.tasks.ts +1 -1
  59. package/src/{routes → backend/routes}/api/groups.$groupId.evidence.ts +2 -2
  60. package/src/{routes → backend/routes}/api/groups.$groupId.sessions.ts +2 -2
  61. package/src/{routes → backend/routes}/api/groups.$groupId.ts +2 -2
  62. package/src/{routes → backend/routes}/api/groups.ts +2 -2
  63. package/src/{routes → backend/routes}/api/health.ts +3 -3
  64. package/src/{routes → backend/routes}/api/instances.$name.connection.ts +1 -1
  65. package/src/{routes → backend/routes}/api/instances.$name.restart.ts +2 -2
  66. package/src/{routes → backend/routes}/api/instances.$name.start.ts +2 -2
  67. package/src/{routes → backend/routes}/api/instances.$name.stop.ts +2 -2
  68. package/src/{routes → backend/routes}/api/instances.$name.ts +1 -1
  69. package/src/{routes → backend/routes}/api/instances.ts +1 -1
  70. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.promote.ts +2 -2
  71. package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.ts +2 -2
  72. package/src/{routes → backend/routes}/api/knowledge.candidates.ts +1 -1
  73. package/src/{routes → backend/routes}/api/knowledge.project-context.ts +2 -2
  74. package/src/{routes → backend/routes}/api/knowledge.search.ts +2 -2
  75. package/src/{routes → backend/routes}/api/knowledge.sessions.$sessionId.candidates.ts +3 -3
  76. package/src/{routes → backend/routes}/api/logs.$id.body.ts +6 -6
  77. package/src/{routes → backend/routes}/api/logs.$id.chunks.ts +5 -4
  78. package/src/{routes → backend/routes}/api/logs.$id.replay.ts +12 -11
  79. package/src/{routes → backend/routes}/api/logs.$id.ts +4 -3
  80. package/src/{routes → backend/routes}/api/logs.import.ts +3 -3
  81. package/src/{routes → backend/routes}/api/logs.stream.ts +11 -7
  82. package/src/{routes → backend/routes}/api/logs.ts +17 -9
  83. package/src/{routes → backend/routes}/api/mcp.ts +1 -1
  84. package/src/{routes → backend/routes}/api/models.ts +1 -1
  85. package/src/{routes → backend/routes}/api/pi-agent.ts +2 -2
  86. package/src/{routes → backend/routes}/api/providers.$providerId.model-metadata.ts +5 -5
  87. package/src/{routes → backend/routes}/api/providers.$providerId.test.log.ts +7 -7
  88. package/src/{routes → backend/routes}/api/providers.$providerId.ts +4 -4
  89. package/src/{routes → backend/routes}/api/providers.export.ts +1 -1
  90. package/src/{routes → backend/routes}/api/providers.import.ts +3 -3
  91. package/src/{routes → backend/routes}/api/providers.scan.ts +2 -2
  92. package/src/{routes → backend/routes}/api/providers.ts +9 -4
  93. package/src/{routes → backend/routes}/api/runs.$runId.evidence.ts +2 -2
  94. package/src/{routes → backend/routes}/api/runs.$runId.ts +2 -2
  95. package/src/{routes → backend/routes}/api/runs.ts +4 -4
  96. package/src/backend/routes/api/runtime.ts +46 -0
  97. package/src/{routes → backend/routes}/api/sessions.ts +3 -3
  98. package/src/{routes → backend/routes}/proxy/$.ts +1 -1
  99. package/src/{routes → backend/routes}/readyz.ts +1 -1
  100. package/src/{start.ts → backend/start.ts} +1 -1
  101. package/src/cli/alias.ts +2 -11
  102. package/src/cli/doctor.ts +32 -9
  103. package/src/cli/instance.ts +26 -13
  104. package/src/cli/instanceArgs.ts +30 -11
  105. package/src/cli/instanceControl.ts +0 -2
  106. package/src/cli/instanceModel.ts +9 -17
  107. package/src/cli/instanceRegistry.ts +0 -9
  108. package/src/cli/networkHints.ts +27 -14
  109. package/src/cli/onboard.ts +69 -2
  110. package/src/cli/onboardTarget.ts +89 -40
  111. package/src/cli/templates/codex-skill-onboard.ts +44 -14
  112. package/src/cli/templates/skill-onboard.ts +64 -28
  113. package/src/cli.ts +184 -61
  114. package/src/components/ProxyViewer.tsx +11 -9
  115. package/src/components/ProxyViewerContainer.tsx +12 -11
  116. package/src/components/pi-agent/PiAgentPanel.tsx +2 -2
  117. package/src/components/providers/ProviderCard.tsx +1 -1
  118. package/src/components/providers/ProviderTestResultsView.tsx +1 -1
  119. package/src/components/providers/SettingsDialog.tsx +159 -144
  120. package/src/components/proxy-viewer/ApplicationBar.tsx +2 -1
  121. package/src/components/proxy-viewer/LogEntry.tsx +1 -1
  122. package/src/components/proxy-viewer/log-formats/index.ts +1 -1
  123. package/src/contracts/index.ts +5 -0
  124. package/src/contracts/log.ts +33 -3
  125. package/src/knowledge/types.ts +3 -2
  126. package/src/lib/alertContract.ts +2 -2
  127. package/src/lib/apiClient.ts +30 -7
  128. package/src/lib/apiFormat.ts +29 -0
  129. package/src/lib/basePath.ts +29 -11
  130. package/src/lib/browserRuntimeContract.ts +124 -0
  131. package/src/lib/browserRuntimeResolver.ts +244 -0
  132. package/src/{proxy → lib}/claudeCodeStrip.ts +6 -7
  133. package/src/lib/codingAgentEndpoints.ts +38 -0
  134. package/src/lib/instanceContract.ts +27 -11
  135. package/src/lib/piAgentContract.ts +3 -2
  136. package/src/lib/publicBasePathContext.ts +40 -10
  137. package/src/lib/resourceLimits.ts +2 -0
  138. package/src/lib/runContract.ts +3 -3
  139. package/src/lib/sessionInfoContract.ts +3 -2
  140. package/src/mcp/instanceHandlers.ts +1 -0
  141. package/src/mcp/server.ts +19 -24
  142. package/src/proxy/config.ts +25 -30
  143. package/src/proxy/dataDir.ts +43 -7
  144. package/src/proxy/ecosystemTasks.ts +161 -70
  145. package/src/proxy/formats/registry.ts +6 -21
  146. package/src/proxy/handler.ts +129 -29
  147. package/src/proxy/identityProxy.ts +419 -39
  148. package/src/proxy/jsonlRecovery.ts +554 -0
  149. package/src/proxy/logFinalizer.ts +25 -10
  150. package/src/proxy/logImporter.ts +2 -1
  151. package/src/proxy/logIndex.ts +191 -95
  152. package/src/proxy/logger.ts +77 -14
  153. package/src/proxy/platformCommands.ts +41 -0
  154. package/src/proxy/privateDataPath.ts +310 -88
  155. package/src/proxy/rawStreamCapture.ts +194 -13
  156. package/src/proxy/runtimeAdmission.ts +5 -2
  157. package/src/proxy/runtimeHealth.ts +14 -1
  158. package/src/proxy/runtimeShutdown.ts +11 -1
  159. package/src/proxy/schemas.ts +7 -1
  160. package/src/proxy/sessionArchive.ts +78 -14
  161. package/src/proxy/sqliteLogIndex.ts +65 -2
  162. package/src/proxy/store.ts +141 -34
  163. package/src/proxy/uiAssetServer.ts +106 -0
  164. package/src/proxy/upstream.ts +29 -2
  165. package/src/ui/bootstrap.ts +167 -0
  166. package/src/ui/index.html +38 -0
  167. package/src/ui/logsStreamClient.ts +20 -0
  168. package/src/ui/main.tsx +13 -0
  169. package/src/ui/public/runtime-config.js +2 -0
  170. package/src/ui/router.tsx +13 -0
  171. package/src/ui/routes/__root.tsx +69 -0
  172. package/src/{routes → ui/routes}/index.tsx +2 -3
  173. package/src/{routes → ui/routes}/session/$sessionId.tsx +5 -11
  174. package/src/ui/runtimeDiscovery.ts +50 -0
  175. package/styles/globals.css +1 -0
  176. package/.output/public/assets/ProxyViewerContainer-BEAhuGOJ.js +0 -126
  177. package/.output/public/assets/ResponseView-DBCjkwye.js +0 -2
  178. package/.output/public/assets/StreamingChunkSequence-CuXhDgMo.js +0 -1
  179. package/.output/public/assets/index-BZo_8koU.js +0 -70
  180. package/.output/public/assets/index-ZWschkCh.css +0 -1
  181. package/.output/public/assets/json-viewer-B-Ao6pwW.js +0 -1
  182. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +0 -1
  183. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +0 -344
  184. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +0 -2
  185. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +0 -2
  186. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +0 -2
  187. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +0 -11
  188. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +0 -69
  189. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +0 -1
  190. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +0 -28
  191. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +0 -6
  192. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +0 -34
  193. package/.output/server/_libs/aria-hidden.mjs +0 -122
  194. package/.output/server/_libs/bail.mjs +0 -8
  195. package/.output/server/_libs/character-entities.mjs +0 -2130
  196. package/.output/server/_libs/class-variance-authority.mjs +0 -44
  197. package/.output/server/_libs/clsx.mjs +0 -16
  198. package/.output/server/_libs/comma-separated-tokens.mjs +0 -10
  199. package/.output/server/_libs/decode-named-character-reference+[...].mjs +0 -8
  200. package/.output/server/_libs/dequal.mjs +0 -27
  201. package/.output/server/_libs/detect-node-es.mjs +0 -1
  202. package/.output/server/_libs/devlop.mjs +0 -8
  203. package/.output/server/_libs/diff.mjs +0 -320
  204. package/.output/server/_libs/estree-util-is-identifier-name.mjs +0 -11
  205. package/.output/server/_libs/floating-ui__core.mjs +0 -725
  206. package/.output/server/_libs/floating-ui__dom.mjs +0 -622
  207. package/.output/server/_libs/floating-ui__react-dom.mjs +0 -292
  208. package/.output/server/_libs/floating-ui__utils.mjs +0 -320
  209. package/.output/server/_libs/get-nonce.mjs +0 -9
  210. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
  211. package/.output/server/_libs/hast-util-whitespace.mjs +0 -10
  212. package/.output/server/_libs/html-url-attributes.mjs +0 -26
  213. package/.output/server/_libs/inline-style-parser.mjs +0 -142
  214. package/.output/server/_libs/is-plain-obj.mjs +0 -10
  215. package/.output/server/_libs/lucide-react.mjs +0 -1027
  216. package/.output/server/_libs/mdast-util-from-markdown.mjs +0 -717
  217. package/.output/server/_libs/mdast-util-to-hast.mjs +0 -710
  218. package/.output/server/_libs/mdast-util-to-string.mjs +0 -38
  219. package/.output/server/_libs/micromark-core-commonmark.mjs +0 -2259
  220. package/.output/server/_libs/micromark-factory-destination.mjs +0 -94
  221. package/.output/server/_libs/micromark-factory-label.mjs +0 -63
  222. package/.output/server/_libs/micromark-factory-space.mjs +0 -24
  223. package/.output/server/_libs/micromark-factory-title.mjs +0 -65
  224. package/.output/server/_libs/micromark-factory-whitespace.mjs +0 -22
  225. package/.output/server/_libs/micromark-util-character.mjs +0 -44
  226. package/.output/server/_libs/micromark-util-chunked.mjs +0 -36
  227. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +0 -12
  228. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
  229. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
  230. package/.output/server/_libs/micromark-util-decode-string.mjs +0 -21
  231. package/.output/server/_libs/micromark-util-encode.mjs +0 -1
  232. package/.output/server/_libs/micromark-util-html-tag-name.mjs +0 -69
  233. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
  234. package/.output/server/_libs/micromark-util-resolve-all.mjs +0 -15
  235. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +0 -41
  236. package/.output/server/_libs/micromark-util-subtokenize.mjs +0 -346
  237. package/.output/server/_libs/micromark.mjs +0 -906
  238. package/.output/server/_libs/property-information.mjs +0 -1209
  239. package/.output/server/_libs/radix-ui.mjs +0 -1
  240. package/.output/server/_libs/radix-ui__number.mjs +0 -6
  241. package/.output/server/_libs/radix-ui__primitive.mjs +0 -11
  242. package/.output/server/_libs/radix-ui__react-accordion.mjs +0 -1
  243. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +0 -1
  244. package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
  245. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +0 -1
  246. package/.output/server/_libs/radix-ui__react-avatar.mjs +0 -1
  247. package/.output/server/_libs/radix-ui__react-checkbox.mjs +0 -1
  248. package/.output/server/_libs/radix-ui__react-collapsible.mjs +0 -144
  249. package/.output/server/_libs/radix-ui__react-collection.mjs +0 -69
  250. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +0 -38
  251. package/.output/server/_libs/radix-ui__react-context-menu.mjs +0 -1
  252. package/.output/server/_libs/radix-ui__react-context.mjs +0 -64
  253. package/.output/server/_libs/radix-ui__react-dialog.mjs +0 -280
  254. package/.output/server/_libs/radix-ui__react-direction.mjs +0 -9
  255. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +0 -1
  256. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +0 -39
  257. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +0 -210
  258. package/.output/server/_libs/radix-ui__react-form.mjs +0 -1
  259. package/.output/server/_libs/radix-ui__react-hover-card.mjs +0 -1
  260. package/.output/server/_libs/radix-ui__react-id.mjs +0 -14
  261. package/.output/server/_libs/radix-ui__react-label.mjs +0 -1
  262. package/.output/server/_libs/radix-ui__react-menu.mjs +0 -1
  263. package/.output/server/_libs/radix-ui__react-menubar.mjs +0 -1
  264. package/.output/server/_libs/radix-ui__react-popover.mjs +0 -1
  265. package/.output/server/_libs/radix-ui__react-popper.mjs +0 -340
  266. package/.output/server/_libs/radix-ui__react-portal.mjs +0 -16
  267. package/.output/server/_libs/radix-ui__react-presence.mjs +0 -173
  268. package/.output/server/_libs/radix-ui__react-primitive.mjs +0 -42
  269. package/.output/server/_libs/radix-ui__react-progress.mjs +0 -1
  270. package/.output/server/_libs/radix-ui__react-radio-group.mjs +0 -1
  271. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +0 -235
  272. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +0 -731
  273. package/.output/server/_libs/radix-ui__react-select.mjs +0 -1207
  274. package/.output/server/_libs/radix-ui__react-separator.mjs +0 -28
  275. package/.output/server/_libs/radix-ui__react-slider.mjs +0 -1
  276. package/.output/server/_libs/radix-ui__react-slot.mjs +0 -132
  277. package/.output/server/_libs/radix-ui__react-switch.mjs +0 -1
  278. package/.output/server/_libs/radix-ui__react-tabs.mjs +0 -194
  279. package/.output/server/_libs/radix-ui__react-toast.mjs +0 -2
  280. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +0 -1
  281. package/.output/server/_libs/radix-ui__react-toggle.mjs +0 -1
  282. package/.output/server/_libs/radix-ui__react-toolbar.mjs +0 -1
  283. package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -498
  284. package/.output/server/_libs/radix-ui__react-use-previous.mjs +0 -14
  285. package/.output/server/_libs/radix-ui__react-use-size.mjs +0 -39
  286. package/.output/server/_libs/react-markdown.mjs +0 -235
  287. package/.output/server/_libs/react-remove-scroll-bar.mjs +0 -82
  288. package/.output/server/_libs/react-remove-scroll.mjs +0 -328
  289. package/.output/server/_libs/react-style-singleton.mjs +0 -69
  290. package/.output/server/_libs/remark-parse.mjs +0 -19
  291. package/.output/server/_libs/remark-rehype.mjs +0 -21
  292. package/.output/server/_libs/space-separated-tokens.mjs +0 -6
  293. package/.output/server/_libs/style-to-js.mjs +0 -72
  294. package/.output/server/_libs/style-to-object.mjs +0 -38
  295. package/.output/server/_libs/swr.mjs +0 -947
  296. package/.output/server/_libs/tailwind-merge.mjs +0 -3255
  297. package/.output/server/_libs/tanstack__react-virtual.mjs +0 -130
  298. package/.output/server/_libs/tanstack__virtual-core.mjs +0 -1319
  299. package/.output/server/_libs/three.mjs +0 -26016
  300. package/.output/server/_libs/trim-lines.mjs +0 -41
  301. package/.output/server/_libs/trough.mjs +0 -85
  302. package/.output/server/_libs/tslib.mjs +0 -1
  303. package/.output/server/_libs/ungap__structured-clone.mjs +0 -212
  304. package/.output/server/_libs/unified.mjs +0 -661
  305. package/.output/server/_libs/unist-util-is.mjs +0 -100
  306. package/.output/server/_libs/unist-util-position.mjs +0 -27
  307. package/.output/server/_libs/unist-util-stringify-position.mjs +0 -27
  308. package/.output/server/_libs/unist-util-visit-parents.mjs +0 -82
  309. package/.output/server/_libs/unist-util-visit.mjs +0 -24
  310. package/.output/server/_libs/use-callback-ref.mjs +0 -66
  311. package/.output/server/_libs/use-sidecar.mjs +0 -106
  312. package/.output/server/_libs/vfile-message.mjs +0 -138
  313. package/.output/server/_libs/vfile.mjs +0 -467
  314. package/.output/server/_sessionId-CJ99gZFV.mjs +0 -84
  315. package/.output/server/_sessionId-DEjScBpK.mjs +0 -146
  316. package/.output/server/_ssr/CompareDrawer-DtOIMeQB.mjs +0 -1060
  317. package/.output/server/_ssr/InspectorPet-CXc4oCBQ.mjs +0 -938
  318. package/.output/server/_ssr/ProxyViewerContainer-DVPlwfkt.mjs +0 -17632
  319. package/.output/server/_ssr/ReplayDialog-BQEA95qC.mjs +0 -725
  320. package/.output/server/_ssr/RequestAnatomy-BMyd8B7Q.mjs +0 -837
  321. package/.output/server/_ssr/ResponseView-qrTaEIM8.mjs +0 -757
  322. package/.output/server/_ssr/StreamingChunkSequence-BFTL8mIy.mjs +0 -274
  323. package/.output/server/_ssr/index-Bcj1Hcy4.mjs +0 -135
  324. package/.output/server/_ssr/index-DW-M7XUG.mjs +0 -84
  325. package/.output/server/_ssr/json-viewer-DiI0Yjsn.mjs +0 -483
  326. package/.output/server/_tanstack-start-manifest_v-C6pXXX9R.mjs +0 -4
  327. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  328. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  329. package/.output/server/node_modules/tslib/package.json +0 -47
  330. package/.output/server/node_modules/tslib/tslib.js +0 -484
  331. package/.output/server/package.json +0 -9
  332. package/src/routes/__root.tsx +0 -170
  333. /package/.output/{public → ui}/assets/agent-inspector-58K1_MsC.ico +0 -0
  334. /package/.output/{public → ui}/assets/alibaba-TTwafVwX.svg +0 -0
  335. /package/.output/{public → ui}/assets/minimax-BPMzvuL-.jpeg +0 -0
  336. /package/.output/{public → ui}/assets/qwen-mMn3f5ul.webp +0 -0
  337. /package/.output/{public → ui}/assets/zhipuai-BPNAnxo-.svg +0 -0
  338. /package/src/{routes → backend/routes}/livez.ts +0 -0
@@ -1,17 +1,17 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { basename } from "node:path";
3
- import { getProvider, getModelUsageName } from "../../proxy/providers";
4
- import { addTestLogEntry } from "../../proxy/store";
5
- import { getConfig } from "../../proxy/config";
6
- import { buildProviderTestSessionId } from "../../proxy/sessionSupervisor";
7
- import { buildProviderTestRequestBody } from "../../lib/providerTestPrompt";
3
+ import { getProvider, getModelUsageName } from "../../../proxy/providers";
4
+ import { addTestLogEntry } from "../../../proxy/store";
5
+ import { getConfig } from "../../../proxy/config";
6
+ import { buildProviderTestSessionId } from "../../../proxy/sessionSupervisor";
7
+ import { buildProviderTestRequestBody } from "../../../lib/providerTestPrompt";
8
8
  import {
9
9
  ProviderTestResultsSchema,
10
10
  type ProviderModelTestResults,
11
11
  type ProviderTestResult as TestResult,
12
12
  type ProviderTestState,
13
- } from "../../lib/providerTestContract";
14
- import { computeTokensPerSecond, firstChunkMsFromChunks } from "../../proxy/streamTiming";
13
+ } from "../../../lib/providerTestContract";
14
+ import { computeTokensPerSecond, firstChunkMsFromChunks } from "../../../proxy/streamTiming";
15
15
 
16
16
  function hasSuccessField(result: ProviderTestState): result is TestResult {
17
17
  return Object.prototype.hasOwnProperty.call(result, "success");
@@ -5,15 +5,15 @@ import {
5
5
  updateProvider,
6
6
  deleteProvider,
7
7
  redactProvider,
8
- } from "../../proxy/providers";
8
+ } from "../../../proxy/providers";
9
9
  import {
10
10
  getProviderModelLimitViolation,
11
11
  ProviderModelMetadataListSchema,
12
12
  ProviderModelNameSchema,
13
13
  ProviderModelsSchema,
14
- } from "../../lib/providerContract";
15
- import { evaluateUpstreamUrl } from "../../lib/ssrfGuard";
16
- import { resourceLimitResponse } from "../../lib/resourceLimits";
14
+ } from "../../../lib/providerContract";
15
+ import { evaluateUpstreamUrl } from "../../../lib/ssrfGuard";
16
+ import { resourceLimitResponse } from "../../../lib/resourceLimits";
17
17
 
18
18
  const ALLOW_LOOPBACK = process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1";
19
19
 
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { z } from "zod";
3
- import { exportProviders, exportProvidersWithKeys } from "../../proxy/providers";
3
+ import { exportProviders, exportProvidersWithKeys } from "../../../proxy/providers";
4
4
 
5
5
  const SecretExportRequestSchema = z.object({
6
6
  confirmation: z.literal("EXPORT_PROVIDER_KEYS"),
@@ -1,9 +1,9 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { z } from "zod";
3
3
  import { randomUUID } from "node:crypto";
4
- import { ProviderScanImportRequestSchema } from "../../lib/providerImportContract";
5
- import { consumeProviderScan } from "../../proxy/providerScanStore";
6
- import { importProviders } from "../../proxy/providers";
4
+ import { ProviderScanImportRequestSchema } from "../../../lib/providerImportContract";
5
+ import { consumeProviderScan } from "../../../proxy/providerScanStore";
6
+ import { importProviders } from "../../../proxy/providers";
7
7
 
8
8
  const ProviderFileImportSchema = z.union([z.string(), z.object({ providers: z.unknown() })]);
9
9
 
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { scanExternalProviders } from "../../proxy/providerImporters";
3
- import { createProviderScan } from "../../proxy/providerScanStore";
2
+ import { scanExternalProviders } from "../../../proxy/providerImporters";
3
+ import { createProviderScan } from "../../../proxy/providerScanStore";
4
4
 
5
5
  export const Route = createFileRoute("/api/providers/scan")({
6
6
  server: {
@@ -1,14 +1,19 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
2
  import { z } from "zod";
3
- import { getProviders, addProvider, redactProvider, redactProviders } from "../../proxy/providers";
3
+ import {
4
+ getProviders,
5
+ addProvider,
6
+ redactProvider,
7
+ redactProviders,
8
+ } from "../../../proxy/providers";
4
9
  import {
5
10
  getProviderModelLimitViolation,
6
11
  ProviderModelMetadataListSchema,
7
12
  ProviderModelNameSchema,
8
13
  ProviderModelsSchema,
9
- } from "../../lib/providerContract";
10
- import { evaluateUpstreamUrl } from "../../lib/ssrfGuard";
11
- import { resourceLimitResponse } from "../../lib/resourceLimits";
14
+ } from "../../../lib/providerContract";
15
+ import { evaluateUpstreamUrl } from "../../../lib/ssrfGuard";
16
+ import { resourceLimitResponse } from "../../../lib/resourceLimits";
12
17
 
13
18
  const ALLOW_LOOPBACK = process.env["AGENT_INSPECTOR_ALLOW_LOOPBACK"] === "1";
14
19
 
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { EvidenceExportOptionsSchema } from "../../lib/runContract";
3
- import { exportInspectorRunEvidence, readInspectorRunEvidence } from "../../services/runs";
2
+ import { EvidenceExportOptionsSchema } from "../../../lib/runContract";
3
+ import { exportInspectorRunEvidence, readInspectorRunEvidence } from "../../../services/runs";
4
4
 
5
5
  type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
6
6
 
@@ -1,6 +1,6 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { UpdateInspectorRunInputSchema } from "../../lib/runContract";
3
- import { getInspectorRun, updateInspectorRun } from "../../services/runs";
2
+ import { UpdateInspectorRunInputSchema } from "../../../lib/runContract";
3
+ import { getInspectorRun, updateInspectorRun } from "../../../services/runs";
4
4
 
5
5
  type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
6
6
 
@@ -1,13 +1,13 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { CreateInspectorRunInputSchema } from "../../lib/runContract";
2
+ import { CreateInspectorRunInputSchema } from "../../../lib/runContract";
3
3
  import {
4
4
  RECENT_FAILURES_DEFAULT_LIMIT,
5
5
  createInspectorRun,
6
6
  listInspectorRecentFailures,
7
7
  listInspectorRuns,
8
- } from "../../services/runs";
9
- import { RECENT_FAILURES_MAX_LIMIT } from "../../proxy/runFailures";
10
- import { parseBoundedIntegerParameter } from "../../lib/resourceLimits";
8
+ } from "../../../services/runs";
9
+ import { RECENT_FAILURES_MAX_LIMIT } from "../../../proxy/runFailures";
10
+ import { parseBoundedIntegerParameter } from "../../../lib/resourceLimits";
11
11
 
12
12
  type JsonBodyResult = { ok: true; value: unknown } | { ok: false };
13
13
 
@@ -0,0 +1,46 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { appendBasePathToOrigin } from "../../../lib/basePath";
3
+ import {
4
+ BROWSER_RUNTIME_SCHEMA_VERSION,
5
+ type BrowserRuntimeDeployment,
6
+ } from "../../../lib/browserRuntimeContract";
7
+ import { getPublicBasePath, getPublicOrigin } from "../../../lib/publicBasePathContext";
8
+
9
+ type RuntimeEnvironment = Readonly<Record<string, string | undefined>>;
10
+
11
+ export const AGENT_INSPECTOR_BACKEND_ONLY_ENV = "AGENT_INSPECTOR_BACKEND_ONLY";
12
+
13
+ export function runtimeDiscoveryDocument(
14
+ env: RuntimeEnvironment = process.env,
15
+ ): BrowserRuntimeDeployment {
16
+ const basePath = getPublicBasePath(env);
17
+ const baseUrl = appendBasePathToOrigin(getPublicOrigin(), "/", basePath);
18
+
19
+ return {
20
+ schemaVersion: BROWSER_RUNTIME_SCHEMA_VERSION,
21
+ deploymentMode: env[AGENT_INSPECTOR_BACKEND_ONLY_ENV] === "1" ? "backend-only" : "composed",
22
+ ui: { basePath: basePath.length === 0 ? "/" : basePath },
23
+ control: { baseUrl },
24
+ agent: { baseUrl },
25
+ capabilities: {
26
+ rest: true,
27
+ sse: true,
28
+ proxy: true,
29
+ mcp: true,
30
+ },
31
+ };
32
+ }
33
+
34
+ export function runtimeDiscoveryResponse(env: RuntimeEnvironment = process.env): Response {
35
+ return Response.json(runtimeDiscoveryDocument(env), {
36
+ headers: { "cache-control": "no-store" },
37
+ });
38
+ }
39
+
40
+ export const Route = createFileRoute("/api/runtime")({
41
+ server: {
42
+ handlers: {
43
+ GET: () => runtimeDiscoveryResponse(),
44
+ },
45
+ },
46
+ });
@@ -1,10 +1,10 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { getSessionInfo, getSessionSnapshots, listSessions } from "../../proxy/store";
2
+ import { getSessionInfo, getSessionSnapshots, listSessions } from "../../../proxy/store";
3
3
  import {
4
4
  SESSION_INFO_DEFAULT_LATEST_LOG_LIMIT,
5
5
  SESSION_INFO_MAX_LATEST_LOG_LIMIT,
6
- } from "../../proxy/sessionInfo";
7
- import { parseBoundedIntegerParameter } from "../../lib/resourceLimits";
6
+ } from "../../../proxy/sessionInfo";
7
+ import { parseBoundedIntegerParameter } from "../../../lib/resourceLimits";
8
8
 
9
9
  function parseBooleanFlag(value: string | null): boolean {
10
10
  return value === "1" || value === "true";
@@ -1,5 +1,5 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { handleProxy } from "../../proxy/handler";
2
+ import { handleProxy } from "../../../proxy/handler";
3
3
 
4
4
  export const Route = createFileRoute("/proxy/$")({
5
5
  server: {
@@ -1,5 +1,5 @@
1
1
  import { createFileRoute } from "@tanstack/react-router";
2
- import { getRuntimeReadiness } from "../proxy/runtimeHealth";
2
+ import { getRuntimeReadiness } from "../../proxy/runtimeHealth";
3
3
 
4
4
  export async function readinessResponse(): Promise<Response> {
5
5
  const readiness = await getRuntimeReadiness();
@@ -1,7 +1,7 @@
1
1
  import { createMiddleware, createStart } from "@tanstack/react-start";
2
2
 
3
3
  const publicBasePathMiddleware = createMiddleware().server(async ({ next, request }) => {
4
- const { runWithRequestPublicBasePath } = await import("./lib/publicBasePathContext");
4
+ const { runWithRequestPublicBasePath } = await import("../lib/publicBasePathContext");
5
5
  return runWithRequestPublicBasePath(request, () => next());
6
6
  });
7
7
 
package/src/cli/alias.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { normalizeBasePath } from "../lib/basePath";
1
+ import { normalizeBasePath, normalizePublicBasePath } from "../lib/basePath";
2
2
 
3
3
  const DEFAULT_ALIAS_HOST = "localhost";
4
4
  const DEFAULT_ALIAS_PORT = 9527;
@@ -67,16 +67,7 @@ function parsePort(raw: string | undefined): number | null {
67
67
  }
68
68
 
69
69
  function normalizeAliasPath(raw: string): string | null {
70
- const trimmed = raw.trim();
71
- if (
72
- !trimmed.startsWith("/") ||
73
- trimmed.includes("?") ||
74
- trimmed.includes("#") ||
75
- trimmed.includes("\\")
76
- ) {
77
- return null;
78
- }
79
- return normalizeBasePath(trimmed);
70
+ return normalizePublicBasePath(raw);
80
71
  }
81
72
 
82
73
  function isValidHost(host: string): boolean {
package/src/cli/doctor.ts CHANGED
@@ -249,7 +249,7 @@ function checkProviderConfig(options: DoctorOptions, deps: DoctorDeps): DoctorCh
249
249
  "Provider config",
250
250
  "warn",
251
251
  `Provider config exists but no providers were found: ${candidate}`,
252
- "Add a provider in Settings or import provider settings.",
252
+ "Add a provider through the control API/MCP, or use Settings when a UI is available.",
253
253
  );
254
254
  }
255
255
 
@@ -257,29 +257,51 @@ function checkProviderConfig(options: DoctorOptions, deps: DoctorDeps): DoctorCh
257
257
  "Provider config",
258
258
  "warn",
259
259
  "No provider config was found.",
260
- "Add a provider in the web UI, import providers, or pass --providers for this check.",
260
+ "Add a provider through the control API/MCP, use Settings when a UI is available, or pass --providers for this check.",
261
261
  );
262
262
  }
263
263
 
264
+ function publicOriginForDoctor(port: number, env: DoctorDeps["env"]): string {
265
+ const configured = env["AGENT_INSPECTOR_PUBLIC_ORIGIN"];
266
+ if (configured !== undefined && configured.trim() === configured) {
267
+ try {
268
+ const parsed = new URL(configured);
269
+ if (
270
+ (parsed.protocol === "http:" || parsed.protocol === "https:") &&
271
+ parsed.username.length === 0 &&
272
+ parsed.password.length === 0 &&
273
+ parsed.pathname === "/" &&
274
+ parsed.search.length === 0 &&
275
+ parsed.hash.length === 0
276
+ ) {
277
+ return parsed.origin;
278
+ }
279
+ } catch {
280
+ // Startup reports invalid public-origin configuration; doctor keeps a safe local hint.
281
+ }
282
+ }
283
+ return `http://localhost:${String(port)}`;
284
+ }
285
+
264
286
  async function checkProxyHealth(port: number, deps: DoctorDeps): Promise<DoctorCheck> {
265
287
  const basePath = getConfiguredBasePath(deps.env);
266
288
  const health = await deps.fetchHealth(port, basePath);
267
- const url = `http://localhost:${port}`;
289
+ const url = publicOriginForDoctor(port, deps.env);
268
290
  const proxyUrl = appendBasePathToOrigin(url, "/proxy", basePath);
269
291
  const healthPath = appendBasePathToOrigin("", "/api/health", basePath);
270
292
  if (health.ok) {
271
293
  return check(
272
- "Proxy health",
294
+ "Backend health",
273
295
  "pass",
274
- `agent-inspector is healthy at ${appendBasePathToOrigin(url, "/", basePath)}.`,
275
- `Proxy URL: ${proxyUrl}`,
296
+ `Agent Inspector backend is healthy at ${appendBasePathToOrigin(url, "/", basePath)}.`,
297
+ `Agent proxy URL: ${proxyUrl}. UI availability is not part of this backend check.`,
276
298
  );
277
299
  }
278
300
 
279
301
  if (await deps.isPortOpen(port)) {
280
302
  const suffix = health.status === null ? "" : ` (HTTP ${health.status})`;
281
303
  return check(
282
- "Proxy health",
304
+ "Backend health",
283
305
  "fail",
284
306
  `Port ${port} is accepting connections, but ${healthPath} is not healthy${suffix}.`,
285
307
  "Stop that process, choose --port <n>, or start agent-inspector with --force-restart.",
@@ -287,7 +309,7 @@ async function checkProxyHealth(port: number, deps: DoctorDeps): Promise<DoctorC
287
309
  }
288
310
 
289
311
  return check(
290
- "Proxy health",
312
+ "Backend health",
291
313
  "warn",
292
314
  `No process is listening on port ${port}.`,
293
315
  "Start with `agent-inspector`, `agent-inspector --background`, or `bun run dev`.",
@@ -578,7 +600,8 @@ export function doctorHelp(): string {
578
600
  "Usage:",
579
601
  " agent-inspector doctor [--port <port>] [--config-dir <dir>] [--providers <json>] [--fix] [--json] [--verbose] [--chrome-extension]",
580
602
  "",
581
- "Checks local proxy health, provider config presence, and package metadata.",
603
+ "Checks local backend health, provider config presence, and package metadata.",
604
+ "The backend check probes the local health endpoint and does not require a composed Web UI.",
582
605
  "Pass --fix to run safe local repairs before checks, including legacy data-dir migration.",
583
606
  "Pass --json to emit a machine-readable report for CI, scripts, and Jenkins.",
584
607
  "Pass --verbose to include local paths, capture mode, and log storage checks.",
@@ -4,7 +4,7 @@ import {
4
4
  AGENT_INSPECTOR_BASE_PATH_ENV,
5
5
  appendBasePathToOrigin,
6
6
  getConfiguredBasePath,
7
- normalizeBasePath,
7
+ normalizePublicBasePath,
8
8
  } from "../lib/basePath";
9
9
  import {
10
10
  type InstanceConnectionOutput,
@@ -171,6 +171,7 @@ function statusOutput(
171
171
  upstreamPort: record.upstreamPort,
172
172
  basePath: displayBasePath(record.basePath),
173
173
  captureMode: record.captureMode,
174
+ uiEnabled: record.uiEnabled,
174
175
  dataDir: record.dataDir,
175
176
  supervisorPid: live.supervisorPid,
176
177
  urls: recordUrls(record),
@@ -194,7 +195,16 @@ function connectionOutput(instance: InstanceStatusOutput): InstanceConnectionOut
194
195
  const serverName = `agent-inspector-${instance.name}`;
195
196
  return {
196
197
  schemaVersion: INSTANCE_API_SCHEMA_VERSION,
198
+ uiEnabled: instance.uiEnabled,
197
199
  instance,
200
+ surfaces: {
201
+ ui: {
202
+ enabled: instance.uiEnabled,
203
+ baseUrl: instance.uiEnabled ? instance.urls.ui : null,
204
+ },
205
+ control: { baseUrl: instance.urls.ui },
206
+ agent: { baseUrl: instance.urls.ui },
207
+ },
198
208
  baseUrl: instance.urls.ui,
199
209
  endpoints: instance.urls,
200
210
  environment: {
@@ -271,7 +281,7 @@ function defaultOpenUrl(targetUrl: string): void {
271
281
  child.unref();
272
282
  }
273
283
 
274
- function launchArgs(record: InstanceRecord, open: boolean): string[] {
284
+ export function buildInstanceLaunchArgs(record: InstanceRecord, open: boolean): string[] {
275
285
  const args = [
276
286
  "--background",
277
287
  "--no-open",
@@ -282,7 +292,8 @@ function launchArgs(record: InstanceRecord, open: boolean): string[] {
282
292
  "--mode",
283
293
  record.captureMode,
284
294
  ];
285
- if (open) args.push("--open");
295
+ if (!record.uiEnabled) args.push("--backend-only");
296
+ if (open && record.uiEnabled) args.push("--open");
286
297
  return args;
287
298
  }
288
299
 
@@ -291,7 +302,7 @@ async function defaultLaunch(
291
302
  open: boolean,
292
303
  cliEntry: string,
293
304
  ): Promise<boolean> {
294
- const child = spawn(process.execPath, [cliEntry, ...launchArgs(record, open)], {
305
+ const child = spawn(process.execPath, [cliEntry, ...buildInstanceLaunchArgs(record, open)], {
295
306
  stdio: "ignore",
296
307
  windowsHide: true,
297
308
  env: {
@@ -468,6 +479,7 @@ function overridesDiffer(
468
479
  if (overrides.basePath !== null && overrides.basePath !== record.basePath) return true;
469
480
  if (overrides.host !== null && overrides.host !== record.host) return true;
470
481
  if (overrides.captureMode !== null && overrides.captureMode !== record.captureMode) return true;
482
+ if (overrides.uiEnabled !== null && overrides.uiEnabled !== record.uiEnabled) return true;
471
483
  if (overrides.dataDir !== null) {
472
484
  const requested = canonicalizeDataDirectory(overrides.dataDir);
473
485
  const saved = canonicalizeDataDirectory(record.dataDir);
@@ -495,6 +507,7 @@ function updatedRecord(
495
507
  upstreamPort: pair.upstreamPort,
496
508
  basePath: action.overrides.basePath ?? record.basePath,
497
509
  captureMode: action.overrides.captureMode ?? record.captureMode,
510
+ uiEnabled: action.overrides.uiEnabled ?? record.uiEnabled,
498
511
  dataDir,
499
512
  };
500
513
  const parsed = InstanceRecordSchema.safeParse(candidate);
@@ -556,6 +569,7 @@ async function createNewRecord(
556
569
  upstreamPort: pairResult.value.upstreamPort,
557
570
  basePath: action.overrides.basePath ?? getConfiguredBasePath(process.env),
558
571
  captureMode: action.overrides.captureMode ?? DEFAULT_INSTANCE_CAPTURE_MODE,
572
+ uiEnabled: action.overrides.uiEnabled ?? true,
559
573
  dataDir: dataDirResult.value,
560
574
  });
561
575
  return created.kind === "error" ? fromRegistry(created) : saveRecord(root, created.value);
@@ -609,7 +623,9 @@ async function startAction(
609
623
  }
610
624
  const live = await classifyRecord(record, deps);
611
625
  if (live.state === "running") {
612
- if (action.overrides.open) (deps.openUrl ?? defaultOpenUrl)(recordUrls(record).ui);
626
+ if (action.overrides.open && record.uiEnabled) {
627
+ (deps.openUrl ?? defaultOpenUrl)(recordUrls(record).ui);
628
+ }
613
629
  return success(operationOutput("start", "already_running", statusOutput(record, live)));
614
630
  }
615
631
  if (live.state === "conflict") {
@@ -793,9 +809,10 @@ function requestActionOverrides(
793
809
  ): Extract<InstanceAction, { kind: "start" }>["overrides"] {
794
810
  return {
795
811
  port: input.port ?? null,
796
- basePath: input.basePath === undefined ? null : normalizeBasePath(input.basePath),
812
+ basePath: input.basePath === undefined ? null : normalizePublicBasePath(input.basePath),
797
813
  host: input.host ?? null,
798
814
  captureMode: input.captureMode ?? null,
815
+ uiEnabled: input.uiEnabled ?? null,
799
816
  dataDir: input.dataDir ?? null,
800
817
  open: false,
801
818
  };
@@ -873,8 +890,10 @@ export async function listNamedInstances(
873
890
  function formatStatus(output: InstanceStatusOutput): string {
874
891
  const pid = output.supervisorPid === null ? "-" : String(output.supervisorPid);
875
892
  const reason = output.stateReason === null ? "-" : output.stateReason;
893
+ const ui = output.uiEnabled ? output.urls.ui : `disabled (configured URL: ${output.urls.ui})`;
876
894
  return `Instance ${output.name}: ${output.state}
877
- UI: ${output.urls.ui}
895
+ UI: ${ui}
896
+ UI enabled: ${output.uiEnabled ? "yes" : "no"}
878
897
  Proxy: ${output.urls.proxy}
879
898
  OpenAI /v1: ${output.urls.openai}
880
899
  MCP: ${output.urls.mcp}
@@ -1029,9 +1048,3 @@ export async function runInstance(
1029
1048
  }
1030
1049
  }
1031
1050
  }
1032
-
1033
- export const instanceLifecycleForTests = {
1034
- classifyRecord,
1035
- launchArgs,
1036
- statusOutput,
1037
- };
@@ -1,4 +1,4 @@
1
- import { normalizeBasePath } from "../lib/basePath";
1
+ import { normalizePublicBasePath } from "../lib/basePath";
2
2
  import { isValidBindHost } from "./networkHints";
3
3
 
4
4
  export type InstanceCaptureMode = "simple" | "full";
@@ -9,6 +9,7 @@ export type InstanceStartOverrides = {
9
9
  host: string | null;
10
10
  captureMode: InstanceCaptureMode | null;
11
11
  dataDir: string | null;
12
+ uiEnabled: boolean | null;
12
13
  open: boolean;
13
14
  };
14
15
 
@@ -36,16 +37,8 @@ function parseMode(value: string | undefined): InstanceCaptureMode | null {
36
37
  }
37
38
 
38
39
  function parseBasePath(value: string | undefined): string | null {
39
- if (
40
- value === undefined ||
41
- !value.startsWith("/") ||
42
- value.includes("?") ||
43
- value.includes("#") ||
44
- value.includes("\\")
45
- ) {
46
- return null;
47
- }
48
- return normalizeBasePath(value);
40
+ if (value === undefined) return null;
41
+ return normalizePublicBasePath(value);
49
42
  }
50
43
 
51
44
  function optionWithEquals(arg: string): { name: string; value: string } | null {
@@ -61,6 +54,7 @@ function emptyOverrides(): InstanceStartOverrides {
61
54
  host: null,
62
55
  captureMode: null,
63
56
  dataDir: null,
57
+ uiEnabled: null,
64
58
  open: false,
65
59
  };
66
60
  }
@@ -148,6 +142,24 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
148
142
  case "--open":
149
143
  options.overrides.open = true;
150
144
  break;
145
+ case "--backend-only":
146
+ if (options.overrides.uiEnabled === true) {
147
+ return {
148
+ kind: "error",
149
+ message: "instance: --backend-only and --with-ui are mutually exclusive",
150
+ };
151
+ }
152
+ options.overrides.uiEnabled = false;
153
+ break;
154
+ case "--with-ui":
155
+ if (options.overrides.uiEnabled === false) {
156
+ return {
157
+ kind: "error",
158
+ message: "instance: --backend-only and --with-ui are mutually exclusive",
159
+ };
160
+ }
161
+ options.overrides.uiEnabled = true;
162
+ break;
151
163
  case "--port":
152
164
  case "-p":
153
165
  case "--base-path":
@@ -181,6 +193,7 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
181
193
  options.overrides.host !== null ||
182
194
  options.overrides.captureMode !== null ||
183
195
  options.overrides.dataDir !== null ||
196
+ options.overrides.uiEnabled !== null ||
184
197
  options.overrides.open
185
198
  ) {
186
199
  return { kind: "error", message: "instance: list accepts only --json" };
@@ -203,6 +216,7 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
203
216
  options.overrides.host !== null ||
204
217
  options.overrides.captureMode !== null ||
205
218
  options.overrides.dataDir !== null ||
219
+ options.overrides.uiEnabled !== null ||
206
220
  options.overrides.open
207
221
  ) {
208
222
  return {
@@ -220,6 +234,9 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
220
234
  if (name === undefined || options.positional.length !== 2) {
221
235
  return { kind: "error", message: `instance: ${command} requires exactly one instance name` };
222
236
  }
237
+ if (options.overrides.uiEnabled === false && options.overrides.open) {
238
+ return { kind: "error", message: "instance: --open cannot be used with --backend-only" };
239
+ }
223
240
  return {
224
241
  kind: "ok",
225
242
  action: { kind: command, name, overrides: options.overrides, json: options.json },
@@ -251,6 +268,8 @@ Start/restart options:
251
268
  --host, -H <host> Bind host (default: 127.0.0.1)
252
269
  --mode <simple|full> Capture mode (default: simple)
253
270
  --data-dir <path> Isolated data/config directory
271
+ --backend-only Persist a headless backend without serving the UI
272
+ --with-ui Persist composed UI serving (the default for new instances)
254
273
  --open Open the UI after readiness
255
274
  --json Emit machine-readable output
256
275
  --help, -h Show this help
@@ -179,5 +179,3 @@ export async function requestInstanceShutdown(
179
179
  clearTimeout(timeout);
180
180
  }
181
181
  }
182
-
183
- export const instanceControlForTests = { parseIdentity };
@@ -1,7 +1,7 @@
1
1
  import { randomBytes, randomUUID } from "node:crypto";
2
2
  import { isAbsolute } from "node:path";
3
3
  import { z } from "zod";
4
- import { normalizeBasePath } from "../lib/basePath";
4
+ import { normalizePublicBasePath } from "../lib/basePath";
5
5
  import { MANAGED_INSTANCE_CONTROL_PATH } from "../lib/managedInstance";
6
6
  import { CaptureModeSchema, type CaptureMode } from "../lib/runtimeConfig";
7
7
 
@@ -73,26 +73,13 @@ export function validateInstanceName(rawName: string): InstanceRegistryResult<st
73
73
  export function normalizeInstanceBasePath(
74
74
  rawBasePath: string | undefined,
75
75
  ): InstanceRegistryResult<string> {
76
- const value = rawBasePath ?? "";
77
- const trimmed = value.trim();
78
- const hasControlCharacter = [...trimmed].some((character) => {
79
- const codePoint = character.codePointAt(0) ?? 0;
80
- return codePoint < 32 || codePoint === 127;
81
- });
82
- if (
83
- trimmed.length > 0 &&
84
- (!trimmed.startsWith("/") ||
85
- trimmed.includes("?") ||
86
- trimmed.includes("#") ||
87
- trimmed.includes("\\") ||
88
- hasControlCharacter)
89
- ) {
76
+ const normalized = normalizePublicBasePath(rawBasePath);
77
+ if (normalized === null) {
90
78
  return instanceRegistryError(
91
79
  "record_invalid",
92
- "Instance Base Path must be an absolute URL path without a query, hash, backslash, or control character.",
80
+ "Instance Base Path must use URL-safe path segments without traversal, encoded separators, or markup characters.",
93
81
  );
94
82
  }
95
- const normalized = normalizeBasePath(trimmed);
96
83
  const reservedControlRoot = MANAGED_INSTANCE_CONTROL_PATH.replace(/\/instance$/u, "");
97
84
  if (normalized === reservedControlRoot || normalized.startsWith(`${reservedControlRoot}/`)) {
98
85
  return instanceRegistryError(
@@ -137,6 +124,7 @@ export const InstanceRecordSchema = z
137
124
  upstreamPort: PortSchema,
138
125
  basePath: NormalizedBasePathSchema,
139
126
  captureMode: CaptureModeSchema,
127
+ uiEnabled: z.boolean().default(true),
140
128
  dataDir: z
141
129
  .string()
142
130
  .min(1)
@@ -168,6 +156,7 @@ export type InstanceRecordOutput = {
168
156
  upstreamPort: number;
169
157
  basePath: string;
170
158
  captureMode: CaptureMode;
159
+ uiEnabled: boolean;
171
160
  dataDir: string;
172
161
  launch: {
173
162
  launchId: string;
@@ -182,6 +171,7 @@ export type CreateInstanceRecordInput = {
182
171
  upstreamPort: number;
183
172
  basePath?: string;
184
173
  captureMode: CaptureMode;
174
+ uiEnabled?: boolean;
185
175
  dataDir: string;
186
176
  };
187
177
 
@@ -222,6 +212,7 @@ export function createInstanceRecord(
222
212
  upstreamPort: input.upstreamPort,
223
213
  basePath: basePath.value,
224
214
  captureMode: input.captureMode,
215
+ uiEnabled: input.uiEnabled ?? true,
225
216
  dataDir: input.dataDir,
226
217
  launch: createLaunch(factory, now),
227
218
  };
@@ -266,6 +257,7 @@ export function toInstanceRecordOutput(record: InstanceRecord): InstanceRecordOu
266
257
  upstreamPort: record.upstreamPort,
267
258
  basePath: record.basePath,
268
259
  captureMode: record.captureMode,
260
+ uiEnabled: record.uiEnabled,
269
261
  dataDir: record.dataDir,
270
262
  launch: {
271
263
  launchId: record.launch.launchId,