@tonyclaw/agent-inspector 3.0.45 → 3.0.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/{nitro.json → backend/nitro.json} +3 -3
- package/.output/backend-public/assets/index-D4LX-sw-.js +11 -0
- package/.output/cli.js +8018 -5451
- package/.output/server/_libs/extend.mjs +4 -8
- package/.output/server/_libs/jszip.mjs +9 -35
- package/.output/server/_libs/react-dom.mjs +2 -3
- package/.output/server/_libs/react.mjs +1 -27
- package/.output/server/_libs/tanstack__react-router.mjs +1 -329
- package/.output/server/_libs/tanstack__router-core.mjs +42 -100
- package/.output/server/_libs/use-sync-external-store.mjs +1 -64
- package/.output/server/_libs/ws.mjs +1 -1
- package/.output/server/_ssr/index.mjs +4 -4
- package/.output/server/_ssr/{publicBasePathContext-5264L3rX.mjs → publicBasePathContext-IH2ilOfu.mjs} +52 -24
- package/.output/server/_ssr/{router-BJ_ltLim.mjs → router-BwZEsYfS.mjs} +19794 -19444
- package/.output/server/_ssr/{start-C6MjPjxp.mjs → start-COQu_AgD.mjs} +1 -1
- package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +4 -0
- package/.output/server/index.mjs +5 -145
- package/.output/{public/assets/CompareDrawer-B3_TLTJh.js → ui/assets/CompareDrawer-V-8lPASC.js} +1 -1
- package/.output/{public/assets/InspectorPet-Cpn6hv4P.js → ui/assets/InspectorPet-D4CVnfdf.js} +1 -1
- package/.output/ui/assets/ProxyViewerContainer-CO3ZVHLX.js +59 -0
- package/.output/{public/assets/ReplayDialog-B3EKVdJk.js → ui/assets/ReplayDialog-CpGyTLtt.js} +1 -1
- package/.output/{public/assets/RequestAnatomy-COOQjyUC.js → ui/assets/RequestAnatomy-BLuJL9dZ.js} +1 -1
- package/.output/ui/assets/ResponseView-BuSC-CH7.js +2 -0
- package/.output/ui/assets/StreamingChunkSequence-DeA-JJKa.js +1 -0
- package/.output/{public/assets/_sessionId-zbgYl7ZJ.js → ui/assets/_sessionId-8HuHFhjo.js} +1 -1
- package/.output/{public/assets/_sessionId-BJlM3nmt.js → ui/assets/_sessionId-Chp67th6.js} +1 -1
- package/.output/ui/assets/favicon-DYEkHSNl.svg +21 -0
- package/.output/ui/assets/index-BvXp42al.css +1 -0
- package/.output/{public/assets/index-xF_NY8Yb.js → ui/assets/index-DPmKK_5d.js} +5 -5
- package/.output/{public/assets/index-BIK4bPTv.js → ui/assets/index-atoQrlb8.js} +1 -1
- package/.output/ui/assets/index-nY3E1vxV.js +73 -0
- package/.output/{public/assets/index-D2Qw6Ri2.js → ui/assets/index-nl9-dpZn.js} +1 -1
- package/.output/ui/assets/json-viewer-B9kIzqeX.js +1 -0
- package/.output/{public/assets/jszip.min-B_rfAUCe.js → ui/assets/jszip.min-B7tfyE-I.js} +1 -1
- package/.output/ui/index.html +229 -0
- package/.output/ui/runtime-config.js +2 -0
- package/.output/workers/logFinalizer.worker.js +979 -414
- package/.output/workers/sessionWorkerEntry.js +979 -414
- package/README.md +182 -43
- package/docs/instance-control.openapi.yaml +59 -4
- package/package.json +40 -6
- package/scripts/generate-theme-scales.mjs +3 -3
- package/src/{router.tsx → backend/router.tsx} +3 -15
- package/src/backend/routes/$.ts +26 -0
- package/src/backend/routes/__root.ts +4 -0
- package/src/backend/routes/api/-instances.ts +7 -0
- package/src/{routes → backend/routes}/api/alerts.summary.ts +3 -3
- package/src/{routes → backend/routes}/api/alerts.ts +4 -4
- package/src/{routes → backend/routes}/api/config.paths.ts +1 -1
- package/src/{routes → backend/routes}/api/config.ts +11 -2
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.help.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.install.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.runner-presets.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.upgrade.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.ts +5 -1
- package/src/{routes → backend/routes}/api/ecosystem.recipes.$recipeId.run.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.tasks.$taskId.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.tasks.ts +1 -1
- package/src/{routes → backend/routes}/api/groups.$groupId.evidence.ts +2 -2
- package/src/{routes → backend/routes}/api/groups.$groupId.sessions.ts +2 -2
- package/src/{routes → backend/routes}/api/groups.$groupId.ts +2 -2
- package/src/{routes → backend/routes}/api/groups.ts +2 -2
- package/src/{routes → backend/routes}/api/health.ts +3 -3
- package/src/{routes → backend/routes}/api/instances.$name.connection.ts +1 -1
- package/src/{routes → backend/routes}/api/instances.$name.restart.ts +2 -2
- package/src/{routes → backend/routes}/api/instances.$name.start.ts +2 -2
- package/src/{routes → backend/routes}/api/instances.$name.stop.ts +2 -2
- package/src/{routes → backend/routes}/api/instances.$name.ts +1 -1
- package/src/{routes → backend/routes}/api/instances.ts +1 -1
- package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.promote.ts +2 -2
- package/src/{routes → backend/routes}/api/knowledge.candidates.$candidateId.ts +2 -2
- package/src/{routes → backend/routes}/api/knowledge.candidates.ts +1 -1
- package/src/{routes → backend/routes}/api/knowledge.project-context.ts +2 -2
- package/src/{routes → backend/routes}/api/knowledge.search.ts +2 -2
- package/src/{routes → backend/routes}/api/knowledge.sessions.$sessionId.candidates.ts +3 -3
- package/src/{routes → backend/routes}/api/logs.$id.body.ts +6 -6
- package/src/{routes → backend/routes}/api/logs.$id.chunks.ts +5 -4
- package/src/{routes → backend/routes}/api/logs.$id.replay.ts +12 -11
- package/src/{routes → backend/routes}/api/logs.$id.ts +4 -3
- package/src/{routes → backend/routes}/api/logs.import.ts +3 -3
- package/src/{routes → backend/routes}/api/logs.stream.ts +11 -7
- package/src/{routes → backend/routes}/api/logs.ts +17 -9
- package/src/{routes → backend/routes}/api/mcp.ts +1 -1
- package/src/{routes → backend/routes}/api/models.ts +1 -1
- package/src/{routes → backend/routes}/api/pi-agent.ts +2 -2
- package/src/{routes → backend/routes}/api/providers.$providerId.model-metadata.ts +5 -5
- package/src/{routes → backend/routes}/api/providers.$providerId.test.log.ts +7 -7
- package/src/{routes → backend/routes}/api/providers.$providerId.ts +4 -4
- package/src/{routes → backend/routes}/api/providers.export.ts +1 -1
- package/src/{routes → backend/routes}/api/providers.import.ts +3 -3
- package/src/{routes → backend/routes}/api/providers.scan.ts +2 -2
- package/src/{routes → backend/routes}/api/providers.ts +9 -4
- package/src/{routes → backend/routes}/api/runs.$runId.evidence.ts +2 -2
- package/src/{routes → backend/routes}/api/runs.$runId.ts +2 -2
- package/src/{routes → backend/routes}/api/runs.ts +4 -4
- package/src/backend/routes/api/runtime.ts +46 -0
- package/src/{routes → backend/routes}/api/sessions.ts +3 -3
- package/src/{routes → backend/routes}/proxy/$.ts +1 -1
- package/src/{routes → backend/routes}/readyz.ts +1 -1
- package/src/{start.ts → backend/start.ts} +1 -1
- package/src/cli/alias.ts +2 -11
- package/src/cli/doctor.ts +32 -9
- package/src/cli/instance.ts +36 -13
- package/src/cli/instanceApi.ts +213 -0
- package/src/cli/instanceArgs.ts +46 -11
- package/src/cli/instanceControl.ts +10 -2
- package/src/cli/instanceModel.ts +18 -17
- package/src/cli/instanceRegistry.ts +0 -9
- package/src/cli/networkHints.ts +27 -14
- package/src/cli/onboard.ts +69 -2
- package/src/cli/onboardTarget.ts +89 -40
- package/src/cli/rustBackendPackage.ts +450 -0
- package/src/cli/templates/codex-skill-onboard.ts +44 -14
- package/src/cli/templates/skill-onboard.ts +64 -28
- package/src/cli.ts +341 -75
- package/src/components/ProxyViewer.tsx +11 -9
- package/src/components/ProxyViewerContainer.tsx +12 -11
- package/src/components/pi-agent/PiAgentPanel.tsx +2 -2
- package/src/components/providers/ProviderCard.tsx +1 -1
- package/src/components/providers/ProviderTestResultsView.tsx +1 -1
- package/src/components/providers/SettingsDialog.tsx +159 -144
- package/src/components/proxy-viewer/ApplicationBar.tsx +2 -1
- package/src/components/proxy-viewer/LogEntry.tsx +1 -1
- package/src/components/proxy-viewer/log-formats/index.ts +1 -1
- package/src/contracts/index.ts +5 -0
- package/src/contracts/log.ts +33 -3
- package/src/knowledge/types.ts +3 -2
- package/src/lib/alertContract.ts +2 -2
- package/src/lib/apiClient.ts +30 -7
- package/src/lib/apiFormat.ts +29 -0
- package/src/lib/backendImplementation.ts +16 -0
- package/src/lib/basePath.ts +29 -11
- package/src/lib/browserRuntimeContract.ts +124 -0
- package/src/lib/browserRuntimeResolver.ts +244 -0
- package/src/{proxy → lib}/claudeCodeStrip.ts +6 -7
- package/src/lib/codingAgentEndpoints.ts +38 -0
- package/src/lib/instanceContract.ts +30 -11
- package/src/lib/managedInstance.ts +9 -1
- package/src/lib/piAgentContract.ts +3 -2
- package/src/lib/publicBasePathContext.ts +40 -10
- package/src/lib/resourceLimits.ts +2 -0
- package/src/lib/runContract.ts +3 -3
- package/src/lib/sessionInfoContract.ts +3 -2
- package/src/mcp/instanceHandlers.ts +2 -0
- package/src/mcp/server.ts +19 -24
- package/src/proxy/config.ts +25 -30
- package/src/proxy/dataDir.ts +43 -7
- package/src/proxy/ecosystemTasks.ts +161 -70
- package/src/proxy/formats/registry.ts +6 -21
- package/src/proxy/handler.ts +129 -29
- package/src/proxy/identityProxy.ts +567 -39
- package/src/proxy/jsonlRecovery.ts +554 -0
- package/src/proxy/logFinalizer.ts +25 -10
- package/src/proxy/logImporter.ts +2 -1
- package/src/proxy/logIndex.ts +191 -95
- package/src/proxy/logger.ts +77 -14
- package/src/proxy/platformCommands.ts +41 -0
- package/src/proxy/privateDataPath.ts +310 -88
- package/src/proxy/rawStreamCapture.ts +194 -13
- package/src/proxy/runtimeAdmission.ts +5 -2
- package/src/proxy/runtimeHealth.ts +14 -1
- package/src/proxy/runtimeShutdown.ts +11 -1
- package/src/proxy/schemas.ts +7 -1
- package/src/proxy/sessionArchive.ts +70 -14
- package/src/proxy/sqliteLogIndex.ts +65 -42
- package/src/proxy/store.ts +141 -34
- package/src/proxy/uiAssetServer.ts +106 -0
- package/src/proxy/upstream.ts +29 -2
- package/src/ui/bootstrap.ts +167 -0
- package/src/ui/index.html +38 -0
- package/src/ui/logsStreamClient.ts +20 -0
- package/src/ui/main.tsx +13 -0
- package/src/ui/public/runtime-config.js +2 -0
- package/src/ui/router.tsx +13 -0
- package/src/ui/routes/__root.tsx +69 -0
- package/src/{routes → ui/routes}/index.tsx +2 -3
- package/src/{routes → ui/routes}/session/$sessionId.tsx +5 -11
- package/src/ui/runtimeDiscovery.ts +50 -0
- package/styles/globals.css +1 -0
- package/.output/public/assets/ProxyViewerContainer-BEAhuGOJ.js +0 -126
- package/.output/public/assets/ResponseView-DBCjkwye.js +0 -2
- package/.output/public/assets/StreamingChunkSequence-CuXhDgMo.js +0 -1
- package/.output/public/assets/index-BZo_8koU.js +0 -70
- package/.output/public/assets/index-ZWschkCh.css +0 -1
- package/.output/public/assets/json-viewer-B-Ao6pwW.js +0 -1
- package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +0 -1
- package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +0 -344
- package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +0 -2
- package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +0 -2
- package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +0 -2
- package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +0 -11
- package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +0 -69
- package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +0 -1
- package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +0 -28
- package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +0 -6
- package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +0 -34
- package/.output/server/_libs/aria-hidden.mjs +0 -122
- package/.output/server/_libs/bail.mjs +0 -8
- package/.output/server/_libs/character-entities.mjs +0 -2130
- package/.output/server/_libs/class-variance-authority.mjs +0 -44
- package/.output/server/_libs/clsx.mjs +0 -16
- package/.output/server/_libs/comma-separated-tokens.mjs +0 -10
- package/.output/server/_libs/decode-named-character-reference+[...].mjs +0 -8
- package/.output/server/_libs/dequal.mjs +0 -27
- package/.output/server/_libs/detect-node-es.mjs +0 -1
- package/.output/server/_libs/devlop.mjs +0 -8
- package/.output/server/_libs/diff.mjs +0 -320
- package/.output/server/_libs/estree-util-is-identifier-name.mjs +0 -11
- package/.output/server/_libs/floating-ui__core.mjs +0 -725
- package/.output/server/_libs/floating-ui__dom.mjs +0 -622
- package/.output/server/_libs/floating-ui__react-dom.mjs +0 -292
- package/.output/server/_libs/floating-ui__utils.mjs +0 -320
- package/.output/server/_libs/get-nonce.mjs +0 -9
- package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +0 -388
- package/.output/server/_libs/hast-util-whitespace.mjs +0 -10
- package/.output/server/_libs/html-url-attributes.mjs +0 -26
- package/.output/server/_libs/inline-style-parser.mjs +0 -142
- package/.output/server/_libs/is-plain-obj.mjs +0 -10
- package/.output/server/_libs/lucide-react.mjs +0 -1027
- package/.output/server/_libs/mdast-util-from-markdown.mjs +0 -717
- package/.output/server/_libs/mdast-util-to-hast.mjs +0 -710
- package/.output/server/_libs/mdast-util-to-string.mjs +0 -38
- package/.output/server/_libs/micromark-core-commonmark.mjs +0 -2259
- package/.output/server/_libs/micromark-factory-destination.mjs +0 -94
- package/.output/server/_libs/micromark-factory-label.mjs +0 -63
- package/.output/server/_libs/micromark-factory-space.mjs +0 -24
- package/.output/server/_libs/micromark-factory-title.mjs +0 -65
- package/.output/server/_libs/micromark-factory-whitespace.mjs +0 -22
- package/.output/server/_libs/micromark-util-character.mjs +0 -44
- package/.output/server/_libs/micromark-util-chunked.mjs +0 -36
- package/.output/server/_libs/micromark-util-classify-character+[...].mjs +0 -12
- package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +0 -41
- package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +0 -19
- package/.output/server/_libs/micromark-util-decode-string.mjs +0 -21
- package/.output/server/_libs/micromark-util-encode.mjs +0 -1
- package/.output/server/_libs/micromark-util-html-tag-name.mjs +0 -69
- package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +0 -6
- package/.output/server/_libs/micromark-util-resolve-all.mjs +0 -15
- package/.output/server/_libs/micromark-util-sanitize-uri.mjs +0 -41
- package/.output/server/_libs/micromark-util-subtokenize.mjs +0 -346
- package/.output/server/_libs/micromark.mjs +0 -906
- package/.output/server/_libs/property-information.mjs +0 -1209
- package/.output/server/_libs/radix-ui.mjs +0 -1
- package/.output/server/_libs/radix-ui__number.mjs +0 -6
- package/.output/server/_libs/radix-ui__primitive.mjs +0 -11
- package/.output/server/_libs/radix-ui__react-accordion.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
- package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-avatar.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-checkbox.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-collapsible.mjs +0 -144
- package/.output/server/_libs/radix-ui__react-collection.mjs +0 -69
- package/.output/server/_libs/radix-ui__react-compose-refs.mjs +0 -38
- package/.output/server/_libs/radix-ui__react-context-menu.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-context.mjs +0 -64
- package/.output/server/_libs/radix-ui__react-dialog.mjs +0 -280
- package/.output/server/_libs/radix-ui__react-direction.mjs +0 -9
- package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-focus-guards.mjs +0 -39
- package/.output/server/_libs/radix-ui__react-focus-scope.mjs +0 -210
- package/.output/server/_libs/radix-ui__react-form.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-hover-card.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-id.mjs +0 -14
- package/.output/server/_libs/radix-ui__react-label.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-menu.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-menubar.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-popover.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-popper.mjs +0 -340
- package/.output/server/_libs/radix-ui__react-portal.mjs +0 -16
- package/.output/server/_libs/radix-ui__react-presence.mjs +0 -173
- package/.output/server/_libs/radix-ui__react-primitive.mjs +0 -42
- package/.output/server/_libs/radix-ui__react-progress.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-radio-group.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-roving-focus.mjs +0 -235
- package/.output/server/_libs/radix-ui__react-scroll-area.mjs +0 -731
- package/.output/server/_libs/radix-ui__react-select.mjs +0 -1207
- package/.output/server/_libs/radix-ui__react-separator.mjs +0 -28
- package/.output/server/_libs/radix-ui__react-slider.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-slot.mjs +0 -132
- package/.output/server/_libs/radix-ui__react-switch.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-tabs.mjs +0 -194
- package/.output/server/_libs/radix-ui__react-toast.mjs +0 -2
- package/.output/server/_libs/radix-ui__react-toggle-group.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-toggle.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-toolbar.mjs +0 -1
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -498
- package/.output/server/_libs/radix-ui__react-use-previous.mjs +0 -14
- package/.output/server/_libs/radix-ui__react-use-size.mjs +0 -39
- package/.output/server/_libs/react-markdown.mjs +0 -235
- package/.output/server/_libs/react-remove-scroll-bar.mjs +0 -82
- package/.output/server/_libs/react-remove-scroll.mjs +0 -328
- package/.output/server/_libs/react-style-singleton.mjs +0 -69
- package/.output/server/_libs/remark-parse.mjs +0 -19
- package/.output/server/_libs/remark-rehype.mjs +0 -21
- package/.output/server/_libs/space-separated-tokens.mjs +0 -6
- package/.output/server/_libs/style-to-js.mjs +0 -72
- package/.output/server/_libs/style-to-object.mjs +0 -38
- package/.output/server/_libs/swr.mjs +0 -947
- package/.output/server/_libs/tailwind-merge.mjs +0 -3255
- package/.output/server/_libs/tanstack__react-virtual.mjs +0 -130
- package/.output/server/_libs/tanstack__virtual-core.mjs +0 -1319
- package/.output/server/_libs/three.mjs +0 -26016
- package/.output/server/_libs/trim-lines.mjs +0 -41
- package/.output/server/_libs/trough.mjs +0 -85
- package/.output/server/_libs/tslib.mjs +0 -1
- package/.output/server/_libs/ungap__structured-clone.mjs +0 -212
- package/.output/server/_libs/unified.mjs +0 -661
- package/.output/server/_libs/unist-util-is.mjs +0 -100
- package/.output/server/_libs/unist-util-position.mjs +0 -27
- package/.output/server/_libs/unist-util-stringify-position.mjs +0 -27
- package/.output/server/_libs/unist-util-visit-parents.mjs +0 -82
- package/.output/server/_libs/unist-util-visit.mjs +0 -24
- package/.output/server/_libs/use-callback-ref.mjs +0 -66
- package/.output/server/_libs/use-sidecar.mjs +0 -106
- package/.output/server/_libs/vfile-message.mjs +0 -138
- package/.output/server/_libs/vfile.mjs +0 -467
- package/.output/server/_sessionId-CJ99gZFV.mjs +0 -84
- package/.output/server/_sessionId-DEjScBpK.mjs +0 -146
- package/.output/server/_ssr/CompareDrawer-DtOIMeQB.mjs +0 -1060
- package/.output/server/_ssr/InspectorPet-CXc4oCBQ.mjs +0 -938
- package/.output/server/_ssr/ProxyViewerContainer-DVPlwfkt.mjs +0 -17632
- package/.output/server/_ssr/ReplayDialog-BQEA95qC.mjs +0 -725
- package/.output/server/_ssr/RequestAnatomy-BMyd8B7Q.mjs +0 -837
- package/.output/server/_ssr/ResponseView-qrTaEIM8.mjs +0 -757
- package/.output/server/_ssr/StreamingChunkSequence-BFTL8mIy.mjs +0 -274
- package/.output/server/_ssr/index-Bcj1Hcy4.mjs +0 -135
- package/.output/server/_ssr/index-DW-M7XUG.mjs +0 -84
- package/.output/server/_ssr/json-viewer-DiI0Yjsn.mjs +0 -483
- package/.output/server/_tanstack-start-manifest_v-C6pXXX9R.mjs +0 -4
- package/.output/server/node_modules/tslib/modules/index.js +0 -70
- package/.output/server/node_modules/tslib/modules/package.json +0 -3
- package/.output/server/node_modules/tslib/package.json +0 -47
- package/.output/server/node_modules/tslib/tslib.js +0 -484
- package/.output/server/package.json +0 -9
- package/src/routes/__root.tsx +0 -170
- package/src/routes/api/-instances.ts +0 -109
- /package/.output/{public → ui}/assets/agent-inspector-58K1_MsC.ico +0 -0
- /package/.output/{public → ui}/assets/alibaba-TTwafVwX.svg +0 -0
- /package/.output/{public → ui}/assets/minimax-BPMzvuL-.jpeg +0 -0
- /package/.output/{public → ui}/assets/qwen-mMn3f5ul.webp +0 -0
- /package/.output/{public → ui}/assets/zhipuai-BPNAnxo-.svg +0 -0
- /package/src/{routes → backend/routes}/livez.ts +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { type z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
getNamedInstance,
|
|
4
|
+
getNamedInstanceConnection,
|
|
5
|
+
type InstanceLifecycleError,
|
|
6
|
+
type InstanceLifecycleResult,
|
|
7
|
+
listNamedInstances,
|
|
8
|
+
restartNamedInstance,
|
|
9
|
+
startNamedInstance,
|
|
10
|
+
stopNamedInstance,
|
|
11
|
+
} from "./instance";
|
|
12
|
+
import {
|
|
13
|
+
type InstancePublicErrorCode,
|
|
14
|
+
INSTANCE_API_SCHEMA_VERSION,
|
|
15
|
+
InstanceEmptyRequestSchema,
|
|
16
|
+
InstanceStartRequestSchema,
|
|
17
|
+
instanceErrorOutput,
|
|
18
|
+
} from "../lib/instanceContract";
|
|
19
|
+
import { readManagedInstanceLaunch } from "../lib/managedInstance";
|
|
20
|
+
|
|
21
|
+
const NO_STORE_HEADERS = { "cache-control": "no-store" };
|
|
22
|
+
|
|
23
|
+
type ParsedBody<T> = { ok: true; value: T } | { ok: false; response: Response };
|
|
24
|
+
|
|
25
|
+
function errorStatus(code: InstancePublicErrorCode): number {
|
|
26
|
+
switch (code) {
|
|
27
|
+
case "argument_invalid":
|
|
28
|
+
case "data_dir_invalid":
|
|
29
|
+
case "name_invalid":
|
|
30
|
+
case "port_invalid":
|
|
31
|
+
case "record_invalid":
|
|
32
|
+
return 400;
|
|
33
|
+
case "instance_not_found":
|
|
34
|
+
return 404;
|
|
35
|
+
case "config_mismatch":
|
|
36
|
+
case "data_dir_conflict":
|
|
37
|
+
case "identity_mismatch":
|
|
38
|
+
case "port_conflict":
|
|
39
|
+
case "self_lifecycle_unsupported":
|
|
40
|
+
return 409;
|
|
41
|
+
case "lock_not_owned":
|
|
42
|
+
case "lock_timeout":
|
|
43
|
+
return 423;
|
|
44
|
+
case "launch_failed":
|
|
45
|
+
case "launch_unavailable":
|
|
46
|
+
case "port_exhausted":
|
|
47
|
+
case "registry_io":
|
|
48
|
+
case "restart_failed_degraded":
|
|
49
|
+
case "restart_failed_rolled_back":
|
|
50
|
+
case "shutdown_failed":
|
|
51
|
+
return 503;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function instanceErrorResponse(error: InstanceLifecycleError): Response {
|
|
56
|
+
return Response.json(instanceErrorOutput(error), {
|
|
57
|
+
status: errorStatus(error.code),
|
|
58
|
+
headers: NO_STORE_HEADERS,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function instanceResultResponse<T>(
|
|
63
|
+
result: InstanceLifecycleResult<T>,
|
|
64
|
+
successStatus = 200,
|
|
65
|
+
): Response {
|
|
66
|
+
return result.kind === "error"
|
|
67
|
+
? instanceErrorResponse(result.error)
|
|
68
|
+
: Response.json(result.value, { status: successStatus, headers: NO_STORE_HEADERS });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function parseInstanceBody<T extends z.ZodType>(
|
|
72
|
+
request: Request,
|
|
73
|
+
schema: T,
|
|
74
|
+
): Promise<ParsedBody<z.output<T>>> {
|
|
75
|
+
let value: unknown;
|
|
76
|
+
try {
|
|
77
|
+
const text = await request.text();
|
|
78
|
+
value = text.trim().length === 0 ? {} : JSON.parse(text);
|
|
79
|
+
} catch {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
response: instanceErrorResponse({
|
|
83
|
+
code: "argument_invalid",
|
|
84
|
+
message: "Request body must be valid JSON.",
|
|
85
|
+
retryable: false,
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const parsed = schema.safeParse(value);
|
|
90
|
+
if (!parsed.success) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
response: instanceErrorResponse({
|
|
94
|
+
code: "argument_invalid",
|
|
95
|
+
message: `Invalid instance request: ${parsed.error.issues[0]?.message ?? "invalid body"}`,
|
|
96
|
+
retryable: false,
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { ok: true, value: parsed.data };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function selfLifecycleError(rawName: string): InstanceLifecycleError | null {
|
|
104
|
+
const current = readManagedInstanceLaunch(process.env);
|
|
105
|
+
return current.kind === "managed" && current.launch.name.toLowerCase() === rawName.toLowerCase()
|
|
106
|
+
? {
|
|
107
|
+
code: "self_lifecycle_unsupported",
|
|
108
|
+
message:
|
|
109
|
+
"A managed runtime cannot stop or restart itself through its own REST/MCP transport; use the CLI or a different controller instance.",
|
|
110
|
+
retryable: false,
|
|
111
|
+
}
|
|
112
|
+
: null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function instanceListResponse(instances: readonly unknown[]): Response {
|
|
116
|
+
return Response.json(
|
|
117
|
+
{ schemaVersion: INSTANCE_API_SCHEMA_VERSION, instances },
|
|
118
|
+
{ headers: NO_STORE_HEADERS },
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function methodNotAllowed(allow: string): Response {
|
|
123
|
+
return Response.json(
|
|
124
|
+
{ error: "Method not allowed" },
|
|
125
|
+
{ status: 405, headers: { ...NO_STORE_HEADERS, allow } },
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function endpointNotFound(): Response {
|
|
130
|
+
return Response.json(
|
|
131
|
+
{ error: "Instance endpoint not found" },
|
|
132
|
+
{ status: 404, headers: NO_STORE_HEADERS },
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function decodedName(value: string): string | null {
|
|
137
|
+
try {
|
|
138
|
+
return decodeURIComponent(value);
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Framework-neutral named-instance API owned by the Node supervisor plane.
|
|
146
|
+
*
|
|
147
|
+
* The protected ingress uses this when a Rust backend is selected, keeping process lifecycle and
|
|
148
|
+
* OS-specific background behavior in the existing CLI instead of reimplementing it in Rust.
|
|
149
|
+
*/
|
|
150
|
+
export async function handleNamedInstanceApi(request: Request): Promise<Response> {
|
|
151
|
+
const url = new URL(request.url);
|
|
152
|
+
const segments = url.pathname.split("/").filter((segment) => segment.length > 0);
|
|
153
|
+
if (segments.length === 2 && segments[0] === "api" && segments[1] === "instances") {
|
|
154
|
+
if (request.method !== "GET") return methodNotAllowed("GET");
|
|
155
|
+
const result = await listNamedInstances();
|
|
156
|
+
return result.kind === "error"
|
|
157
|
+
? instanceResultResponse(result)
|
|
158
|
+
: instanceListResponse(result.value);
|
|
159
|
+
}
|
|
160
|
+
if (segments.length < 3 || segments[0] !== "api" || segments[1] !== "instances") {
|
|
161
|
+
return endpointNotFound();
|
|
162
|
+
}
|
|
163
|
+
const name = decodedName(segments[2] ?? "");
|
|
164
|
+
if (name === null) {
|
|
165
|
+
return instanceErrorResponse({
|
|
166
|
+
code: "name_invalid",
|
|
167
|
+
message: "Instance name is not valid URL encoding.",
|
|
168
|
+
retryable: false,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (segments.length === 3) {
|
|
172
|
+
return request.method === "GET"
|
|
173
|
+
? instanceResultResponse(await getNamedInstance(name))
|
|
174
|
+
: methodNotAllowed("GET");
|
|
175
|
+
}
|
|
176
|
+
if (segments.length !== 4) return endpointNotFound();
|
|
177
|
+
const action = segments[3];
|
|
178
|
+
if (action === undefined) return endpointNotFound();
|
|
179
|
+
switch (action) {
|
|
180
|
+
case "connection":
|
|
181
|
+
return request.method === "GET"
|
|
182
|
+
? instanceResultResponse(await getNamedInstanceConnection(name))
|
|
183
|
+
: methodNotAllowed("GET");
|
|
184
|
+
case "start": {
|
|
185
|
+
if (request.method !== "POST") return methodNotAllowed("POST");
|
|
186
|
+
const body = await parseInstanceBody(request, InstanceStartRequestSchema);
|
|
187
|
+
if (!body.ok) return body.response;
|
|
188
|
+
const result = await startNamedInstance(name, body.value);
|
|
189
|
+
const status = result.kind === "success" && result.value.outcome === "created" ? 201 : 200;
|
|
190
|
+
return instanceResultResponse(result, status);
|
|
191
|
+
}
|
|
192
|
+
case "stop": {
|
|
193
|
+
if (request.method !== "POST") return methodNotAllowed("POST");
|
|
194
|
+
const body = await parseInstanceBody(request, InstanceEmptyRequestSchema);
|
|
195
|
+
if (!body.ok) return body.response;
|
|
196
|
+
const selfError = selfLifecycleError(name);
|
|
197
|
+
return selfError === null
|
|
198
|
+
? instanceResultResponse(await stopNamedInstance(name))
|
|
199
|
+
: instanceErrorResponse(selfError);
|
|
200
|
+
}
|
|
201
|
+
case "restart": {
|
|
202
|
+
if (request.method !== "POST") return methodNotAllowed("POST");
|
|
203
|
+
const body = await parseInstanceBody(request, InstanceStartRequestSchema);
|
|
204
|
+
if (!body.ok) return body.response;
|
|
205
|
+
const selfError = selfLifecycleError(name);
|
|
206
|
+
return selfError === null
|
|
207
|
+
? instanceResultResponse(await restartNamedInstance(name, body.value))
|
|
208
|
+
: instanceErrorResponse(selfError);
|
|
209
|
+
}
|
|
210
|
+
default:
|
|
211
|
+
return endpointNotFound();
|
|
212
|
+
}
|
|
213
|
+
}
|
package/src/cli/instanceArgs.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizePublicBasePath } from "../lib/basePath";
|
|
2
|
+
import {
|
|
3
|
+
BackendImplementationSchema,
|
|
4
|
+
type BackendImplementation,
|
|
5
|
+
} from "../lib/backendImplementation";
|
|
2
6
|
import { isValidBindHost } from "./networkHints";
|
|
3
7
|
|
|
4
8
|
export type InstanceCaptureMode = "simple" | "full";
|
|
5
9
|
|
|
6
10
|
export type InstanceStartOverrides = {
|
|
11
|
+
backend: BackendImplementation | null;
|
|
7
12
|
port: number | null;
|
|
8
13
|
basePath: string | null;
|
|
9
14
|
host: string | null;
|
|
10
15
|
captureMode: InstanceCaptureMode | null;
|
|
11
16
|
dataDir: string | null;
|
|
17
|
+
uiEnabled: boolean | null;
|
|
12
18
|
open: boolean;
|
|
13
19
|
};
|
|
14
20
|
|
|
@@ -36,16 +42,8 @@ function parseMode(value: string | undefined): InstanceCaptureMode | null {
|
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
function parseBasePath(value: string | undefined): string | null {
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
!value.startsWith("/") ||
|
|
42
|
-
value.includes("?") ||
|
|
43
|
-
value.includes("#") ||
|
|
44
|
-
value.includes("\\")
|
|
45
|
-
) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
return normalizeBasePath(value);
|
|
45
|
+
if (value === undefined) return null;
|
|
46
|
+
return normalizePublicBasePath(value);
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
function optionWithEquals(arg: string): { name: string; value: string } | null {
|
|
@@ -56,11 +54,13 @@ function optionWithEquals(arg: string): { name: string; value: string } | null {
|
|
|
56
54
|
|
|
57
55
|
function emptyOverrides(): InstanceStartOverrides {
|
|
58
56
|
return {
|
|
57
|
+
backend: null,
|
|
59
58
|
port: null,
|
|
60
59
|
basePath: null,
|
|
61
60
|
host: null,
|
|
62
61
|
captureMode: null,
|
|
63
62
|
dataDir: null,
|
|
63
|
+
uiEnabled: null,
|
|
64
64
|
open: false,
|
|
65
65
|
};
|
|
66
66
|
}
|
|
@@ -78,6 +78,12 @@ function setValueOption(
|
|
|
78
78
|
value: string | undefined,
|
|
79
79
|
): string | null {
|
|
80
80
|
switch (name) {
|
|
81
|
+
case "--backend": {
|
|
82
|
+
const backend = BackendImplementationSchema.safeParse(value?.trim().toLowerCase());
|
|
83
|
+
if (!backend.success) return "instance: --backend must be typescript or rust";
|
|
84
|
+
options.overrides.backend = backend.data;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
81
87
|
case "--port":
|
|
82
88
|
case "-p": {
|
|
83
89
|
const port = parsePort(value);
|
|
@@ -148,8 +154,27 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
|
|
|
148
154
|
case "--open":
|
|
149
155
|
options.overrides.open = true;
|
|
150
156
|
break;
|
|
157
|
+
case "--backend-only":
|
|
158
|
+
if (options.overrides.uiEnabled === true) {
|
|
159
|
+
return {
|
|
160
|
+
kind: "error",
|
|
161
|
+
message: "instance: --backend-only and --with-ui are mutually exclusive",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
options.overrides.uiEnabled = false;
|
|
165
|
+
break;
|
|
166
|
+
case "--with-ui":
|
|
167
|
+
if (options.overrides.uiEnabled === false) {
|
|
168
|
+
return {
|
|
169
|
+
kind: "error",
|
|
170
|
+
message: "instance: --backend-only and --with-ui are mutually exclusive",
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
options.overrides.uiEnabled = true;
|
|
174
|
+
break;
|
|
151
175
|
case "--port":
|
|
152
176
|
case "-p":
|
|
177
|
+
case "--backend":
|
|
153
178
|
case "--base-path":
|
|
154
179
|
case "--host":
|
|
155
180
|
case "-H":
|
|
@@ -176,11 +201,13 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
|
|
|
176
201
|
return { kind: "error", message: "instance: list does not accept an instance name" };
|
|
177
202
|
}
|
|
178
203
|
if (
|
|
204
|
+
options.overrides.backend !== null ||
|
|
179
205
|
options.overrides.port !== null ||
|
|
180
206
|
options.overrides.basePath !== null ||
|
|
181
207
|
options.overrides.host !== null ||
|
|
182
208
|
options.overrides.captureMode !== null ||
|
|
183
209
|
options.overrides.dataDir !== null ||
|
|
210
|
+
options.overrides.uiEnabled !== null ||
|
|
184
211
|
options.overrides.open
|
|
185
212
|
) {
|
|
186
213
|
return { kind: "error", message: "instance: list accepts only --json" };
|
|
@@ -198,11 +225,13 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
|
|
|
198
225
|
return { kind: "error", message: `instance: ${command} requires exactly one instance name` };
|
|
199
226
|
}
|
|
200
227
|
if (
|
|
228
|
+
options.overrides.backend !== null ||
|
|
201
229
|
options.overrides.port !== null ||
|
|
202
230
|
options.overrides.basePath !== null ||
|
|
203
231
|
options.overrides.host !== null ||
|
|
204
232
|
options.overrides.captureMode !== null ||
|
|
205
233
|
options.overrides.dataDir !== null ||
|
|
234
|
+
options.overrides.uiEnabled !== null ||
|
|
206
235
|
options.overrides.open
|
|
207
236
|
) {
|
|
208
237
|
return {
|
|
@@ -220,6 +249,9 @@ export function parseInstanceArgs(argv: readonly string[]): InstanceArgsResult {
|
|
|
220
249
|
if (name === undefined || options.positional.length !== 2) {
|
|
221
250
|
return { kind: "error", message: `instance: ${command} requires exactly one instance name` };
|
|
222
251
|
}
|
|
252
|
+
if (options.overrides.uiEnabled === false && options.overrides.open) {
|
|
253
|
+
return { kind: "error", message: "instance: --open cannot be used with --backend-only" };
|
|
254
|
+
}
|
|
223
255
|
return {
|
|
224
256
|
kind: "ok",
|
|
225
257
|
action: { kind: command, name, overrides: options.overrides, json: options.json },
|
|
@@ -246,11 +278,14 @@ Usage:
|
|
|
246
278
|
agent-inspector instance restart <name> [options]
|
|
247
279
|
|
|
248
280
|
Start/restart options:
|
|
281
|
+
--backend <typescript|rust> Persist the backend implementation (default: typescript)
|
|
249
282
|
--port, -p <port> Public ingress port (auto-allocated on first start)
|
|
250
283
|
--base-path <path> Public Base Path (default: /inspector; use / for root)
|
|
251
284
|
--host, -H <host> Bind host (default: 127.0.0.1)
|
|
252
285
|
--mode <simple|full> Capture mode (default: simple)
|
|
253
286
|
--data-dir <path> Isolated data/config directory
|
|
287
|
+
--backend-only Persist a headless backend without serving the UI
|
|
288
|
+
--with-ui Persist composed UI serving (the default for new instances)
|
|
254
289
|
--open Open the UI after readiness
|
|
255
290
|
--json Emit machine-readable output
|
|
256
291
|
--help, -h Show this help
|
|
@@ -3,12 +3,17 @@ import {
|
|
|
3
3
|
MANAGED_INSTANCE_SHUTDOWN_PATH,
|
|
4
4
|
MANAGED_INSTANCE_TOKEN_HEADER,
|
|
5
5
|
} from "../lib/managedInstance";
|
|
6
|
+
import {
|
|
7
|
+
BackendImplementationSchema,
|
|
8
|
+
type BackendImplementation,
|
|
9
|
+
} from "../lib/backendImplementation";
|
|
6
10
|
|
|
7
11
|
const DEFAULT_CONTROL_TIMEOUT_MS = 2_000;
|
|
8
12
|
|
|
9
13
|
export type InstanceControlFetch = (input: string, init?: RequestInit) => Promise<Response>;
|
|
10
14
|
|
|
11
15
|
export type ExpectedInstanceControlIdentity = {
|
|
16
|
+
backend?: BackendImplementation;
|
|
12
17
|
instanceId: string;
|
|
13
18
|
launchId: string;
|
|
14
19
|
controlToken: string;
|
|
@@ -18,6 +23,7 @@ export type ExpectedInstanceControlIdentity = {
|
|
|
18
23
|
|
|
19
24
|
export type InstanceControlIdentity = {
|
|
20
25
|
schemaVersion: 1;
|
|
26
|
+
backend: BackendImplementation;
|
|
21
27
|
instanceId: string;
|
|
22
28
|
launchId: string;
|
|
23
29
|
supervisorPid: number;
|
|
@@ -45,6 +51,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
45
51
|
function parseIdentity(value: unknown): InstanceControlIdentity | null {
|
|
46
52
|
if (!isRecord(value)) return null;
|
|
47
53
|
const schemaVersion = value["schemaVersion"];
|
|
54
|
+
const backend = BackendImplementationSchema.safeParse(value["backend"] ?? "typescript");
|
|
48
55
|
const instanceId = value["instanceId"];
|
|
49
56
|
const launchId = value["launchId"];
|
|
50
57
|
const supervisorPid = value["supervisorPid"];
|
|
@@ -53,6 +60,7 @@ function parseIdentity(value: unknown): InstanceControlIdentity | null {
|
|
|
53
60
|
const upstreamPort = value["upstreamPort"];
|
|
54
61
|
if (
|
|
55
62
|
schemaVersion !== 1 ||
|
|
63
|
+
!backend.success ||
|
|
56
64
|
typeof instanceId !== "string" ||
|
|
57
65
|
instanceId.length === 0 ||
|
|
58
66
|
typeof launchId !== "string" ||
|
|
@@ -75,6 +83,7 @@ function parseIdentity(value: unknown): InstanceControlIdentity | null {
|
|
|
75
83
|
}
|
|
76
84
|
return {
|
|
77
85
|
schemaVersion: 1,
|
|
86
|
+
backend: backend.data,
|
|
78
87
|
instanceId,
|
|
79
88
|
launchId,
|
|
80
89
|
supervisorPid,
|
|
@@ -136,6 +145,7 @@ export async function probeInstanceControl(
|
|
|
136
145
|
if (
|
|
137
146
|
identity.instanceId !== expected.instanceId ||
|
|
138
147
|
identity.launchId !== expected.launchId ||
|
|
148
|
+
identity.backend !== (expected.backend ?? "typescript") ||
|
|
139
149
|
identity.publicPort !== expected.publicPort
|
|
140
150
|
) {
|
|
141
151
|
return { kind: "identity-mismatch" };
|
|
@@ -179,5 +189,3 @@ export async function requestInstanceShutdown(
|
|
|
179
189
|
clearTimeout(timeout);
|
|
180
190
|
}
|
|
181
191
|
}
|
|
182
|
-
|
|
183
|
-
export const instanceControlForTests = { parseIdentity };
|
package/src/cli/instanceModel.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { isAbsolute } from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
BackendImplementationSchema,
|
|
6
|
+
type BackendImplementation,
|
|
7
|
+
} from "../lib/backendImplementation";
|
|
8
|
+
import { normalizePublicBasePath } from "../lib/basePath";
|
|
5
9
|
import { MANAGED_INSTANCE_CONTROL_PATH } from "../lib/managedInstance";
|
|
6
10
|
import { CaptureModeSchema, type CaptureMode } from "../lib/runtimeConfig";
|
|
7
11
|
|
|
@@ -73,26 +77,13 @@ export function validateInstanceName(rawName: string): InstanceRegistryResult<st
|
|
|
73
77
|
export function normalizeInstanceBasePath(
|
|
74
78
|
rawBasePath: string | undefined,
|
|
75
79
|
): InstanceRegistryResult<string> {
|
|
76
|
-
const
|
|
77
|
-
|
|
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
|
-
) {
|
|
80
|
+
const normalized = normalizePublicBasePath(rawBasePath);
|
|
81
|
+
if (normalized === null) {
|
|
90
82
|
return instanceRegistryError(
|
|
91
83
|
"record_invalid",
|
|
92
|
-
"Instance Base Path must
|
|
84
|
+
"Instance Base Path must use URL-safe path segments without traversal, encoded separators, or markup characters.",
|
|
93
85
|
);
|
|
94
86
|
}
|
|
95
|
-
const normalized = normalizeBasePath(trimmed);
|
|
96
87
|
const reservedControlRoot = MANAGED_INSTANCE_CONTROL_PATH.replace(/\/instance$/u, "");
|
|
97
88
|
if (normalized === reservedControlRoot || normalized.startsWith(`${reservedControlRoot}/`)) {
|
|
98
89
|
return instanceRegistryError(
|
|
@@ -137,6 +128,8 @@ export const InstanceRecordSchema = z
|
|
|
137
128
|
upstreamPort: PortSchema,
|
|
138
129
|
basePath: NormalizedBasePathSchema,
|
|
139
130
|
captureMode: CaptureModeSchema,
|
|
131
|
+
backend: BackendImplementationSchema.default("typescript"),
|
|
132
|
+
uiEnabled: z.boolean().default(true),
|
|
140
133
|
dataDir: z
|
|
141
134
|
.string()
|
|
142
135
|
.min(1)
|
|
@@ -168,6 +161,8 @@ export type InstanceRecordOutput = {
|
|
|
168
161
|
upstreamPort: number;
|
|
169
162
|
basePath: string;
|
|
170
163
|
captureMode: CaptureMode;
|
|
164
|
+
backend: BackendImplementation;
|
|
165
|
+
uiEnabled: boolean;
|
|
171
166
|
dataDir: string;
|
|
172
167
|
launch: {
|
|
173
168
|
launchId: string;
|
|
@@ -182,6 +177,8 @@ export type CreateInstanceRecordInput = {
|
|
|
182
177
|
upstreamPort: number;
|
|
183
178
|
basePath?: string;
|
|
184
179
|
captureMode: CaptureMode;
|
|
180
|
+
backend?: BackendImplementation;
|
|
181
|
+
uiEnabled?: boolean;
|
|
185
182
|
dataDir: string;
|
|
186
183
|
};
|
|
187
184
|
|
|
@@ -222,6 +219,8 @@ export function createInstanceRecord(
|
|
|
222
219
|
upstreamPort: input.upstreamPort,
|
|
223
220
|
basePath: basePath.value,
|
|
224
221
|
captureMode: input.captureMode,
|
|
222
|
+
backend: input.backend ?? "typescript",
|
|
223
|
+
uiEnabled: input.uiEnabled ?? true,
|
|
225
224
|
dataDir: input.dataDir,
|
|
226
225
|
launch: createLaunch(factory, now),
|
|
227
226
|
};
|
|
@@ -266,6 +265,8 @@ export function toInstanceRecordOutput(record: InstanceRecord): InstanceRecordOu
|
|
|
266
265
|
upstreamPort: record.upstreamPort,
|
|
267
266
|
basePath: record.basePath,
|
|
268
267
|
captureMode: record.captureMode,
|
|
268
|
+
backend: record.backend,
|
|
269
|
+
uiEnabled: record.uiEnabled,
|
|
269
270
|
dataDir: record.dataDir,
|
|
270
271
|
launch: {
|
|
271
272
|
launchId: record.launch.launchId,
|
|
@@ -500,15 +500,6 @@ export function withRegistryLock<T>(
|
|
|
500
500
|
return runWithLock(acquireRegistryLock(root, options), operation);
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
-
export function withInstanceLock<T>(
|
|
504
|
-
root: string,
|
|
505
|
-
rawName: string,
|
|
506
|
-
operation: LockedOperation<T>,
|
|
507
|
-
options: InstanceLockOptions = {},
|
|
508
|
-
): Promise<InstanceRegistryResult<T>> {
|
|
509
|
-
return runWithLock(acquireInstanceLock(root, rawName, options), operation);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
503
|
type PathApi = typeof win32;
|
|
513
504
|
|
|
514
505
|
export type CanonicalDataDirectory = {
|
package/src/cli/networkHints.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { networkInterfaces } from "node:os";
|
|
2
|
+
import { isIP } from "node:net";
|
|
2
3
|
import { appendBasePathToOrigin, normalizeBasePath } from "../lib/basePath";
|
|
3
4
|
|
|
4
5
|
export type NetworkInterfaceMap = ReturnType<typeof networkInterfaces>;
|
|
@@ -22,12 +23,12 @@ export function isWildcardHost(host: BindHost): boolean {
|
|
|
22
23
|
export function isLoopbackHost(host: BindHost): boolean {
|
|
23
24
|
const trimmed = trimHost(host);
|
|
24
25
|
if (trimmed === undefined) return false;
|
|
25
|
-
|
|
26
|
-
trimmed
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
);
|
|
26
|
+
const normalized =
|
|
27
|
+
trimmed.startsWith("[") && trimmed.endsWith("]")
|
|
28
|
+
? trimmed.slice(1, -1).toLowerCase()
|
|
29
|
+
: trimmed.toLowerCase();
|
|
30
|
+
if (normalized === "localhost" || normalized === "::1") return true;
|
|
31
|
+
return isIP(normalized) === 4 && normalized.split(".", 1)[0] === "127";
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
function bracketIpv6(host: string): string {
|
|
@@ -112,11 +113,28 @@ function accessUrl(origin: string, path: string, basePath: string): string {
|
|
|
112
113
|
return appendBasePathToOrigin(origin, path, basePath);
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
function appendSurfaceLines(
|
|
117
|
+
lines: string[],
|
|
118
|
+
label: "Local" | "Bound" | "Network",
|
|
119
|
+
origin: string,
|
|
120
|
+
basePath: string,
|
|
121
|
+
uiEnabled: boolean,
|
|
122
|
+
): void {
|
|
123
|
+
const baseUrl = accessUrl(origin, "/", basePath);
|
|
124
|
+
if (uiEnabled) lines.push(` ${label} UI: ${baseUrl}`);
|
|
125
|
+
lines.push(` ${label} control Base: ${baseUrl}`);
|
|
126
|
+
lines.push(` ${label} agent Base: ${baseUrl}`);
|
|
127
|
+
lines.push(` ${label} REST API: ${accessUrl(origin, "/api", basePath)}`);
|
|
128
|
+
lines.push(` ${label} proxy: ${accessUrl(origin, "/proxy", basePath)}`);
|
|
129
|
+
lines.push(` ${label} MCP: ${accessUrl(origin, "/api/mcp", basePath)}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
115
132
|
export function formatAccessHintLines(
|
|
116
133
|
port: number,
|
|
117
134
|
networkUrls: readonly string[],
|
|
118
135
|
host: BindHost = undefined,
|
|
119
136
|
basePath = "",
|
|
137
|
+
uiEnabled = true,
|
|
120
138
|
): string[] {
|
|
121
139
|
const normalizedBasePath = normalizeBasePath(basePath);
|
|
122
140
|
const localUrl = localUrlForPort(port);
|
|
@@ -124,14 +142,10 @@ export function formatAccessHintLines(
|
|
|
124
142
|
const lines = ["Access URLs:", ` Bind host: ${hostLabel}`];
|
|
125
143
|
|
|
126
144
|
if (isLoopbackHost(host) || isWildcardHost(host)) {
|
|
127
|
-
lines
|
|
128
|
-
lines.push(` Local proxy: ${accessUrl(localUrl, "/proxy", normalizedBasePath)}`);
|
|
129
|
-
lines.push(` Local MCP: ${accessUrl(localUrl, "/api/mcp", normalizedBasePath)}`);
|
|
145
|
+
appendSurfaceLines(lines, "Local", localUrl, normalizedBasePath, uiEnabled);
|
|
130
146
|
} else {
|
|
131
147
|
const boundUrl = urlForHost(port, host);
|
|
132
|
-
lines
|
|
133
|
-
lines.push(` Bound proxy: ${accessUrl(boundUrl, "/proxy", normalizedBasePath)}`);
|
|
134
|
-
lines.push(` Bound MCP: ${accessUrl(boundUrl, "/api/mcp", normalizedBasePath)}`);
|
|
148
|
+
appendSurfaceLines(lines, "Bound", boundUrl, normalizedBasePath, uiEnabled);
|
|
135
149
|
lines.push(" Localhost: may not work because the server is bound to a specific host");
|
|
136
150
|
}
|
|
137
151
|
|
|
@@ -141,8 +155,7 @@ export function formatAccessHintLines(
|
|
|
141
155
|
} else if (firstNetworkUrl === undefined) {
|
|
142
156
|
lines.push(" Network: no non-loopback IPv4 address detected");
|
|
143
157
|
} else {
|
|
144
|
-
lines
|
|
145
|
-
lines.push(` Network proxy: ${accessUrl(firstNetworkUrl, "/proxy", normalizedBasePath)}`);
|
|
158
|
+
appendSurfaceLines(lines, "Network", firstNetworkUrl, normalizedBasePath, uiEnabled);
|
|
146
159
|
const moreUrls = networkUrls.slice(1);
|
|
147
160
|
if (moreUrls.length > 0) {
|
|
148
161
|
lines.push(` Other IPs: ${moreUrls.join(", ")}`);
|