@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.
- package/.output/{nitro.json → backend/nitro.json} +3 -3
- package/.output/backend-public/assets/index-D4LX-sw-.js +11 -0
- package/.output/cli.js +2326 -516
- 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-D18yUq36.mjs} +19770 -19447
- 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-DAUuIJ6G.js} +1 -1
- package/.output/{public/assets/InspectorPet-Cpn6hv4P.js → ui/assets/InspectorPet-BRBVjOWI.js} +1 -1
- package/.output/ui/assets/ProxyViewerContainer-D7Sq0ctc.js +59 -0
- package/.output/{public/assets/ReplayDialog-B3EKVdJk.js → ui/assets/ReplayDialog-nDLsjOhs.js} +1 -1
- package/.output/{public/assets/RequestAnatomy-COOQjyUC.js → ui/assets/RequestAnatomy-zd4BDgL0.js} +1 -1
- package/.output/ui/assets/ResponseView-AdIXGcSn.js +2 -0
- package/.output/ui/assets/StreamingChunkSequence-CesTz8He.js +1 -0
- package/.output/{public/assets/_sessionId-zbgYl7ZJ.js → ui/assets/_sessionId-BAfzrhSU.js} +1 -1
- package/.output/{public/assets/_sessionId-BJlM3nmt.js → ui/assets/_sessionId-QnBomgPD.js} +1 -1
- package/.output/ui/assets/favicon-DYEkHSNl.svg +21 -0
- package/.output/{public/assets/index-BIK4bPTv.js → ui/assets/index-BcsdxBAd.js} +1 -1
- package/.output/ui/assets/index-BvXp42al.css +1 -0
- package/.output/{public/assets/index-D2Qw6Ri2.js → ui/assets/index-D0rtCN9V.js} +1 -1
- package/.output/ui/assets/index-DHpwr08Z.js +73 -0
- package/.output/{public/assets/index-xF_NY8Yb.js → ui/assets/index-dNuk2dsU.js} +5 -5
- package/.output/ui/assets/json-viewer-BvFHglMb.js +1 -0
- package/.output/{public/assets/jszip.min-B_rfAUCe.js → ui/assets/jszip.min-B5Z7gNZB.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 +175 -43
- package/docs/instance-control.openapi.yaml +59 -4
- package/package.json +25 -5
- 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/{routes → backend/routes}/api/-instances.ts +3 -3
- 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 +26 -13
- package/src/cli/instanceArgs.ts +30 -11
- package/src/cli/instanceControl.ts +0 -2
- package/src/cli/instanceModel.ts +9 -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/templates/codex-skill-onboard.ts +44 -14
- package/src/cli/templates/skill-onboard.ts +64 -28
- package/src/cli.ts +184 -61
- 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/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 +27 -11
- 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 +1 -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 +419 -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 +78 -14
- package/src/proxy/sqliteLogIndex.ts +65 -2
- 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/.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
package/.output/cli.js
CHANGED
|
@@ -18,6 +18,15 @@ var __export = (target, all) => {
|
|
|
18
18
|
function trimSlashes(value) {
|
|
19
19
|
return value.replace(/^\/+/u, "").replace(/\/+$/u, "");
|
|
20
20
|
}
|
|
21
|
+
function normalizePublicBasePath(value) {
|
|
22
|
+
const trimmed = value?.trim() ?? "";
|
|
23
|
+
if (trimmed.length === 0 || trimmed === "/") return "";
|
|
24
|
+
if (!trimmed.startsWith("/") || trimmed.startsWith("//")) return null;
|
|
25
|
+
const normalized = normalizeBasePath(trimmed);
|
|
26
|
+
if (!SAFE_PUBLIC_BASE_PATH_PATTERN.test(normalized)) return null;
|
|
27
|
+
if (normalized.split("/").some((segment) => segment === "." || segment === "..")) return null;
|
|
28
|
+
return normalized;
|
|
29
|
+
}
|
|
21
30
|
function normalizeBasePath(value) {
|
|
22
31
|
const trimmed = value?.trim();
|
|
23
32
|
if (trimmed === void 0 || trimmed.length === 0 || trimmed === "/") return "";
|
|
@@ -34,14 +43,10 @@ function readProcessBasePath() {
|
|
|
34
43
|
return process.env[AGENT_INSPECTOR_BASE_PATH_ENV];
|
|
35
44
|
}
|
|
36
45
|
function getConfiguredBasePath(env) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
return normalizeBasePath(
|
|
43
|
-
readProcessBasePath() ?? readDocumentBasePath() ?? import.meta.env?.BASE_URL ?? DEFAULT_AGENT_INSPECTOR_BASE_PATH
|
|
44
|
-
);
|
|
46
|
+
const configured = env !== void 0 ? env[AGENT_INSPECTOR_BASE_PATH_ENV] ?? DEFAULT_AGENT_INSPECTOR_BASE_PATH : readProcessBasePath() ?? readDocumentBasePath() ?? import.meta.env?.BASE_URL ?? DEFAULT_AGENT_INSPECTOR_BASE_PATH;
|
|
47
|
+
const normalized = normalizePublicBasePath(configured);
|
|
48
|
+
if (normalized !== null) return normalized;
|
|
49
|
+
return normalizePublicBasePath(DEFAULT_AGENT_INSPECTOR_BASE_PATH) ?? "";
|
|
45
50
|
}
|
|
46
51
|
function withBasePath(path2, basePath = getConfiguredBasePath()) {
|
|
47
52
|
const normalized = normalizeBasePath(basePath);
|
|
@@ -67,7 +72,7 @@ function stripBasePathFromRequestPath(rawPath, basePath) {
|
|
|
67
72
|
const url2 = new URL(rawPath, "http://localhost");
|
|
68
73
|
return `${stripBasePath(url2.pathname, basePath)}${url2.search}`;
|
|
69
74
|
}
|
|
70
|
-
var AGENT_INSPECTOR_BASE_PATH_ENV, AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV, AGENT_INSPECTOR_BASE_PATH_META_NAME, AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER, AGENT_INSPECTOR_PUBLIC_PORT_HEADER, DEFAULT_AGENT_INSPECTOR_BASE_PATH;
|
|
75
|
+
var AGENT_INSPECTOR_BASE_PATH_ENV, AGENT_INSPECTOR_INTERNAL_STRIPPED_BASE_PATH_ENV, AGENT_INSPECTOR_BASE_PATH_META_NAME, AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER, AGENT_INSPECTOR_PUBLIC_PORT_HEADER, AGENT_INSPECTOR_PUBLIC_ORIGIN_HEADER, DEFAULT_AGENT_INSPECTOR_BASE_PATH, SAFE_PUBLIC_BASE_PATH_PATTERN;
|
|
71
76
|
var init_basePath = __esm({
|
|
72
77
|
"src/lib/basePath.ts"() {
|
|
73
78
|
"use strict";
|
|
@@ -76,7 +81,9 @@ var init_basePath = __esm({
|
|
|
76
81
|
AGENT_INSPECTOR_BASE_PATH_META_NAME = "agent-inspector-base-path";
|
|
77
82
|
AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER = "x-agent-inspector-public-base-path";
|
|
78
83
|
AGENT_INSPECTOR_PUBLIC_PORT_HEADER = "x-agent-inspector-public-port";
|
|
84
|
+
AGENT_INSPECTOR_PUBLIC_ORIGIN_HEADER = "x-agent-inspector-public-origin";
|
|
79
85
|
DEFAULT_AGENT_INSPECTOR_BASE_PATH = "/inspector";
|
|
86
|
+
SAFE_PUBLIC_BASE_PATH_PATTERN = /^\/[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*$/u;
|
|
80
87
|
}
|
|
81
88
|
});
|
|
82
89
|
|
|
@@ -139,6 +146,7 @@ var init_managedInstance = __esm({
|
|
|
139
146
|
|
|
140
147
|
// src/cli/networkHints.ts
|
|
141
148
|
import { networkInterfaces } from "node:os";
|
|
149
|
+
import { isIP } from "node:net";
|
|
142
150
|
function localUrlForPort(port) {
|
|
143
151
|
return `http://localhost:${String(port)}`;
|
|
144
152
|
}
|
|
@@ -153,7 +161,9 @@ function isWildcardHost(host) {
|
|
|
153
161
|
function isLoopbackHost(host) {
|
|
154
162
|
const trimmed = trimHost(host);
|
|
155
163
|
if (trimmed === void 0) return false;
|
|
156
|
-
|
|
164
|
+
const normalized = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1).toLowerCase() : trimmed.toLowerCase();
|
|
165
|
+
if (normalized === "localhost" || normalized === "::1") return true;
|
|
166
|
+
return isIP(normalized) === 4 && normalized.split(".", 1)[0] === "127";
|
|
157
167
|
}
|
|
158
168
|
function bracketIpv6(host) {
|
|
159
169
|
if (!host.includes(":")) return host;
|
|
@@ -215,20 +225,25 @@ function networkUrlsForBindHost(port, host, interfaces = networkInterfaces()) {
|
|
|
215
225
|
function accessUrl(origin, path2, basePath) {
|
|
216
226
|
return appendBasePathToOrigin(origin, path2, basePath);
|
|
217
227
|
}
|
|
218
|
-
function
|
|
228
|
+
function appendSurfaceLines(lines, label, origin, basePath, uiEnabled) {
|
|
229
|
+
const baseUrl = accessUrl(origin, "/", basePath);
|
|
230
|
+
if (uiEnabled) lines.push(` ${label} UI: ${baseUrl}`);
|
|
231
|
+
lines.push(` ${label} control Base: ${baseUrl}`);
|
|
232
|
+
lines.push(` ${label} agent Base: ${baseUrl}`);
|
|
233
|
+
lines.push(` ${label} REST API: ${accessUrl(origin, "/api", basePath)}`);
|
|
234
|
+
lines.push(` ${label} proxy: ${accessUrl(origin, "/proxy", basePath)}`);
|
|
235
|
+
lines.push(` ${label} MCP: ${accessUrl(origin, "/api/mcp", basePath)}`);
|
|
236
|
+
}
|
|
237
|
+
function formatAccessHintLines(port, networkUrls, host = void 0, basePath = "", uiEnabled = true) {
|
|
219
238
|
const normalizedBasePath = normalizeBasePath(basePath);
|
|
220
239
|
const localUrl = localUrlForPort(port);
|
|
221
240
|
const hostLabel = isWildcardHost(host) ? "all interfaces (0.0.0.0)" : trimHost(host) ?? "";
|
|
222
241
|
const lines = ["Access URLs:", ` Bind host: ${hostLabel}`];
|
|
223
242
|
if (isLoopbackHost(host) || isWildcardHost(host)) {
|
|
224
|
-
lines
|
|
225
|
-
lines.push(` Local proxy: ${accessUrl(localUrl, "/proxy", normalizedBasePath)}`);
|
|
226
|
-
lines.push(` Local MCP: ${accessUrl(localUrl, "/api/mcp", normalizedBasePath)}`);
|
|
243
|
+
appendSurfaceLines(lines, "Local", localUrl, normalizedBasePath, uiEnabled);
|
|
227
244
|
} else {
|
|
228
245
|
const boundUrl = urlForHost(port, host);
|
|
229
|
-
lines
|
|
230
|
-
lines.push(` Bound proxy: ${accessUrl(boundUrl, "/proxy", normalizedBasePath)}`);
|
|
231
|
-
lines.push(` Bound MCP: ${accessUrl(boundUrl, "/api/mcp", normalizedBasePath)}`);
|
|
246
|
+
appendSurfaceLines(lines, "Bound", boundUrl, normalizedBasePath, uiEnabled);
|
|
232
247
|
lines.push(" Localhost: may not work because the server is bound to a specific host");
|
|
233
248
|
}
|
|
234
249
|
const firstNetworkUrl = networkUrls[0];
|
|
@@ -237,8 +252,7 @@ function formatAccessHintLines(port, networkUrls, host = void 0, basePath = "")
|
|
|
237
252
|
} else if (firstNetworkUrl === void 0) {
|
|
238
253
|
lines.push(" Network: no non-loopback IPv4 address detected");
|
|
239
254
|
} else {
|
|
240
|
-
lines
|
|
241
|
-
lines.push(` Network proxy: ${accessUrl(firstNetworkUrl, "/proxy", normalizedBasePath)}`);
|
|
255
|
+
appendSurfaceLines(lines, "Network", firstNetworkUrl, normalizedBasePath, uiEnabled);
|
|
242
256
|
const moreUrls = networkUrls.slice(1);
|
|
243
257
|
if (moreUrls.length > 0) {
|
|
244
258
|
lines.push(` Other IPs: ${moreUrls.join(", ")}`);
|
|
@@ -15337,10 +15351,103 @@ var init_zod = __esm({
|
|
|
15337
15351
|
}
|
|
15338
15352
|
});
|
|
15339
15353
|
|
|
15354
|
+
// src/lib/browserRuntimeContract.ts
|
|
15355
|
+
function parseAbsoluteUrl(value) {
|
|
15356
|
+
try {
|
|
15357
|
+
return new URL(value);
|
|
15358
|
+
} catch {
|
|
15359
|
+
return null;
|
|
15360
|
+
}
|
|
15361
|
+
}
|
|
15362
|
+
function hasUnsafePathSegment(pathname) {
|
|
15363
|
+
return pathname.split("/").some((segment) => segment === "." || segment === ".." || /%(?:2e|2f|5c)/iu.test(segment));
|
|
15364
|
+
}
|
|
15365
|
+
function hasControlCharacter(value) {
|
|
15366
|
+
return [...value].some((character) => {
|
|
15367
|
+
const code = character.charCodeAt(0);
|
|
15368
|
+
return code < 32 || code === 127;
|
|
15369
|
+
});
|
|
15370
|
+
}
|
|
15371
|
+
function normalizeBrowserRuntimeBasePath(value) {
|
|
15372
|
+
const trimmed = value.trim();
|
|
15373
|
+
if (trimmed.length === 0) return null;
|
|
15374
|
+
const normalized = normalizePublicBasePath(trimmed);
|
|
15375
|
+
if (normalized === null) return null;
|
|
15376
|
+
return normalized.length === 0 ? "/" : normalized;
|
|
15377
|
+
}
|
|
15378
|
+
function normalizeBrowserRuntimeBaseUrl(value) {
|
|
15379
|
+
if (value.trim() !== value || value.includes("\\") || hasControlCharacter(value) || hasUnsafePathSegment(value)) {
|
|
15380
|
+
return null;
|
|
15381
|
+
}
|
|
15382
|
+
const parsed = parseAbsoluteUrl(value);
|
|
15383
|
+
if (parsed === null) return null;
|
|
15384
|
+
switch (parsed.protocol) {
|
|
15385
|
+
case "http:":
|
|
15386
|
+
case "https:":
|
|
15387
|
+
break;
|
|
15388
|
+
default:
|
|
15389
|
+
return null;
|
|
15390
|
+
}
|
|
15391
|
+
if (parsed.username.length > 0 || parsed.password.length > 0 || parsed.search.length > 0 || parsed.hash.length > 0 || hasUnsafePathSegment(parsed.pathname)) {
|
|
15392
|
+
return null;
|
|
15393
|
+
}
|
|
15394
|
+
const basePath = normalizeBrowserRuntimeBasePath(parsed.pathname);
|
|
15395
|
+
if (basePath === null) return null;
|
|
15396
|
+
return basePath === "/" ? parsed.origin : `${parsed.origin}${basePath}`;
|
|
15397
|
+
}
|
|
15398
|
+
var BROWSER_RUNTIME_SCHEMA_VERSION, BROWSER_RUNTIME_JSON_PLACEHOLDER, BrowserRuntimeBasePathSchema, BrowserRuntimeBaseUrlSchema, BrowserRuntimeCapabilitiesSchema, BrowserRuntimeDeploymentSchema;
|
|
15399
|
+
var init_browserRuntimeContract = __esm({
|
|
15400
|
+
"src/lib/browserRuntimeContract.ts"() {
|
|
15401
|
+
"use strict";
|
|
15402
|
+
init_zod();
|
|
15403
|
+
init_basePath();
|
|
15404
|
+
BROWSER_RUNTIME_SCHEMA_VERSION = 1;
|
|
15405
|
+
BROWSER_RUNTIME_JSON_PLACEHOLDER = "__AGENT_INSPECTOR_RUNTIME_JSON__";
|
|
15406
|
+
BrowserRuntimeBasePathSchema = external_exports.string().min(1).max(2048).refine(
|
|
15407
|
+
(value) => normalizeBrowserRuntimeBasePath(value) !== null,
|
|
15408
|
+
"UI Base Path must be an absolute path without query, hash, backslash, or traversal"
|
|
15409
|
+
).transform((value) => normalizeBrowserRuntimeBasePath(value) ?? value);
|
|
15410
|
+
BrowserRuntimeBaseUrlSchema = external_exports.string().min(1).max(2048).refine(
|
|
15411
|
+
(value) => normalizeBrowserRuntimeBaseUrl(value) !== null,
|
|
15412
|
+
"Runtime Base URL must be an HTTP(S) URL without credentials, query, hash, or traversal"
|
|
15413
|
+
).transform((value) => normalizeBrowserRuntimeBaseUrl(value) ?? value);
|
|
15414
|
+
BrowserRuntimeCapabilitiesSchema = external_exports.object({
|
|
15415
|
+
rest: external_exports.boolean(),
|
|
15416
|
+
sse: external_exports.boolean(),
|
|
15417
|
+
proxy: external_exports.boolean(),
|
|
15418
|
+
mcp: external_exports.boolean()
|
|
15419
|
+
}).strict();
|
|
15420
|
+
BrowserRuntimeDeploymentSchema = external_exports.object({
|
|
15421
|
+
schemaVersion: external_exports.literal(BROWSER_RUNTIME_SCHEMA_VERSION),
|
|
15422
|
+
deploymentMode: external_exports.enum(["composed", "backend-only", "separated"]),
|
|
15423
|
+
ui: external_exports.object({
|
|
15424
|
+
basePath: BrowserRuntimeBasePathSchema
|
|
15425
|
+
}).strict(),
|
|
15426
|
+
control: external_exports.object({
|
|
15427
|
+
baseUrl: BrowserRuntimeBaseUrlSchema
|
|
15428
|
+
}).strict(),
|
|
15429
|
+
agent: external_exports.object({
|
|
15430
|
+
baseUrl: BrowserRuntimeBaseUrlSchema
|
|
15431
|
+
}).strict(),
|
|
15432
|
+
capabilities: BrowserRuntimeCapabilitiesSchema
|
|
15433
|
+
}).strict();
|
|
15434
|
+
}
|
|
15435
|
+
});
|
|
15436
|
+
|
|
15340
15437
|
// src/proxy/privateDataPath.ts
|
|
15341
15438
|
import { spawnSync } from "node:child_process";
|
|
15342
|
-
import {
|
|
15343
|
-
|
|
15439
|
+
import {
|
|
15440
|
+
closeSync,
|
|
15441
|
+
constants,
|
|
15442
|
+
fchmodSync,
|
|
15443
|
+
fstatSync,
|
|
15444
|
+
lstatSync,
|
|
15445
|
+
mkdirSync,
|
|
15446
|
+
openSync,
|
|
15447
|
+
readdirSync,
|
|
15448
|
+
realpathSync
|
|
15449
|
+
} from "node:fs";
|
|
15450
|
+
import { join, parse as parse3, resolve, sep } from "node:path";
|
|
15344
15451
|
function windowsSystemPath(executable) {
|
|
15345
15452
|
const systemRoot = process.env["SystemRoot"] ?? "C:\\Windows";
|
|
15346
15453
|
return `${systemRoot}\\System32\\${executable}`;
|
|
@@ -15369,9 +15476,7 @@ function resolveWindowsUserSid(run) {
|
|
|
15369
15476
|
function windowsBroadGroupRemovalArgs() {
|
|
15370
15477
|
return ["/remove:g", "*S-1-1-0", "*S-1-5-11", "*S-1-5-32-545"];
|
|
15371
15478
|
}
|
|
15372
|
-
function
|
|
15373
|
-
const sid = resolveWindowsUserSid(run);
|
|
15374
|
-
if (sid === null) return false;
|
|
15479
|
+
function applyWindowsAclWithSid(path2, kind, sid, run) {
|
|
15375
15480
|
const access = kind === "directory" ? `(OI)(CI)F` : "F";
|
|
15376
15481
|
const result = run("icacls.exe", [
|
|
15377
15482
|
path2,
|
|
@@ -15383,112 +15488,244 @@ function applyWindowsUserOnlyAcl(path2, kind, run = runWindowsCommand) {
|
|
|
15383
15488
|
]);
|
|
15384
15489
|
return result.status === 0;
|
|
15385
15490
|
}
|
|
15386
|
-
function
|
|
15491
|
+
function applyWindowsUserOnlyAcl(path2, kind, run = runWindowsCommand) {
|
|
15387
15492
|
const sid = resolveWindowsUserSid(run);
|
|
15388
|
-
|
|
15389
|
-
const result = run("icacls.exe", [
|
|
15390
|
-
root,
|
|
15391
|
-
"/inheritance:r",
|
|
15392
|
-
"/grant:r",
|
|
15393
|
-
`*${sid}:(OI)(CI)F`,
|
|
15394
|
-
`*${sid}:F`,
|
|
15395
|
-
...windowsBroadGroupRemovalArgs(),
|
|
15396
|
-
"/T",
|
|
15397
|
-
"/C",
|
|
15398
|
-
"/Q"
|
|
15399
|
-
]);
|
|
15400
|
-
return result.status === 0;
|
|
15493
|
+
return sid !== null && applyWindowsAclWithSid(path2, kind, sid, run);
|
|
15401
15494
|
}
|
|
15402
|
-
function
|
|
15495
|
+
function comparablePath(path2) {
|
|
15496
|
+
let normalized = path2;
|
|
15403
15497
|
if (process.platform === "win32") {
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
} catch {
|
|
15410
|
-
return false;
|
|
15498
|
+
if (normalized.startsWith("\\\\?\\UNC\\")) {
|
|
15499
|
+
normalized = `\\\\${normalized.slice(8)}`;
|
|
15500
|
+
} else if (normalized.startsWith("\\\\?\\")) {
|
|
15501
|
+
normalized = normalized.slice(4);
|
|
15502
|
+
}
|
|
15411
15503
|
}
|
|
15504
|
+
const absolute = resolve(normalized);
|
|
15505
|
+
return process.platform === "win32" ? absolute.toLowerCase() : absolute;
|
|
15412
15506
|
}
|
|
15413
|
-
function
|
|
15414
|
-
|
|
15415
|
-
if (securedPaths.has(cacheKey)) return true;
|
|
15416
|
-
if (!applyPrivatePermissionsSync(path2, kind)) return false;
|
|
15417
|
-
securedPaths.add(cacheKey);
|
|
15418
|
-
return true;
|
|
15507
|
+
function pathIdentity(stats) {
|
|
15508
|
+
return `${String(stats.dev)}:${String(stats.ino)}:${String(stats.birthtimeMs)}`;
|
|
15419
15509
|
}
|
|
15420
|
-
function
|
|
15510
|
+
function readPathEntry(path2) {
|
|
15511
|
+
let stats;
|
|
15421
15512
|
try {
|
|
15422
|
-
|
|
15513
|
+
const observed = lstatSync(path2);
|
|
15514
|
+
if (observed === void 0) return { status: "missing" };
|
|
15515
|
+
stats = observed;
|
|
15423
15516
|
} catch {
|
|
15424
|
-
return
|
|
15517
|
+
return { status: "missing" };
|
|
15425
15518
|
}
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
let
|
|
15519
|
+
if (stats.isSymbolicLink()) return { status: "unsafe" };
|
|
15520
|
+
const kind = stats.isDirectory() ? "directory" : stats.isFile() ? "file" : null;
|
|
15521
|
+
if (kind === null) return { status: "unsafe" };
|
|
15522
|
+
let canonical;
|
|
15430
15523
|
try {
|
|
15431
|
-
|
|
15432
|
-
identity = `${String(stats.dev)}:${String(stats.ino)}:${String(stats.birthtimeMs)}`;
|
|
15524
|
+
canonical = realpathSync.native(path2);
|
|
15433
15525
|
} catch {
|
|
15434
|
-
return
|
|
15526
|
+
return { status: "unsafe" };
|
|
15435
15527
|
}
|
|
15436
|
-
if (
|
|
15437
|
-
|
|
15438
|
-
|
|
15439
|
-
|
|
15528
|
+
if (comparablePath(canonical) !== comparablePath(path2)) return { status: "unsafe" };
|
|
15529
|
+
return {
|
|
15530
|
+
status: "ok",
|
|
15531
|
+
entry: {
|
|
15532
|
+
path: path2,
|
|
15533
|
+
kind,
|
|
15534
|
+
identity: pathIdentity(stats),
|
|
15535
|
+
mode: stats.mode & 511
|
|
15536
|
+
}
|
|
15537
|
+
};
|
|
15538
|
+
}
|
|
15539
|
+
function snapshotPathChain(path2) {
|
|
15540
|
+
const absolutePath = resolve(path2);
|
|
15541
|
+
const root = parse3(absolutePath).root;
|
|
15542
|
+
const suffix = absolutePath.slice(root.length);
|
|
15543
|
+
const parts = suffix === "" ? [] : suffix.split(sep).filter((part) => part !== "");
|
|
15544
|
+
const entries = [];
|
|
15545
|
+
let current = root;
|
|
15546
|
+
const rootRead = readPathEntry(root);
|
|
15547
|
+
if (rootRead.status !== "ok" || rootRead.entry.kind !== "directory") return null;
|
|
15548
|
+
entries.push(rootRead.entry);
|
|
15549
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
15550
|
+
const part = parts[index];
|
|
15551
|
+
if (part === void 0) return null;
|
|
15552
|
+
current = join(current, part);
|
|
15553
|
+
const read = readPathEntry(current);
|
|
15554
|
+
if (read.status === "unsafe") return null;
|
|
15555
|
+
if (read.status === "missing") {
|
|
15556
|
+
return {
|
|
15557
|
+
absolutePath,
|
|
15558
|
+
complete: false,
|
|
15559
|
+
entries,
|
|
15560
|
+
missingParts: parts.slice(index)
|
|
15561
|
+
};
|
|
15562
|
+
}
|
|
15563
|
+
if (index < parts.length - 1 && read.entry.kind !== "directory") return null;
|
|
15564
|
+
entries.push(read.entry);
|
|
15440
15565
|
}
|
|
15441
|
-
|
|
15442
|
-
securedFileIdentities.set(path2, identity);
|
|
15443
|
-
return true;
|
|
15566
|
+
return { absolutePath, complete: true, entries, missingParts: [] };
|
|
15444
15567
|
}
|
|
15445
|
-
function
|
|
15446
|
-
|
|
15447
|
-
|
|
15568
|
+
function chainFingerprint(snapshot) {
|
|
15569
|
+
return snapshot.entries.map((entry) => `${entry.path}:${entry.identity}`).join("|");
|
|
15570
|
+
}
|
|
15571
|
+
function snapshotsRetainPrefix(before, after) {
|
|
15572
|
+
if (after.entries.length < before.entries.length) return false;
|
|
15573
|
+
return before.entries.every((entry, index) => {
|
|
15574
|
+
const next = after.entries[index];
|
|
15575
|
+
return next !== void 0 && next.path === entry.path && next.identity === entry.identity;
|
|
15576
|
+
});
|
|
15577
|
+
}
|
|
15578
|
+
function completeSnapshotForKind(path2, kind) {
|
|
15579
|
+
const snapshot = snapshotPathChain(path2);
|
|
15580
|
+
if (snapshot === null || !snapshot.complete) return null;
|
|
15581
|
+
const leaf = snapshot.entries.at(-1);
|
|
15582
|
+
return leaf !== void 0 && leaf.kind === kind ? snapshot : null;
|
|
15583
|
+
}
|
|
15584
|
+
function applyPosixPermissions(path2, kind, expectedIdentity2) {
|
|
15585
|
+
let descriptor = null;
|
|
15448
15586
|
try {
|
|
15449
|
-
|
|
15587
|
+
const directoryFlag = kind === "directory" ? constants.O_DIRECTORY : 0;
|
|
15588
|
+
descriptor = openSync(path2, constants.O_RDONLY | constants.O_NOFOLLOW | directoryFlag);
|
|
15589
|
+
const before = fstatSync(descriptor);
|
|
15590
|
+
if (pathIdentity(before) !== expectedIdentity2) return false;
|
|
15591
|
+
fchmodSync(descriptor, kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
15592
|
+
const after = fstatSync(descriptor);
|
|
15593
|
+
return pathIdentity(after) === expectedIdentity2 && (after.mode & 511) === (kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
15450
15594
|
} catch {
|
|
15451
15595
|
return false;
|
|
15596
|
+
} finally {
|
|
15597
|
+
if (descriptor !== null) {
|
|
15598
|
+
try {
|
|
15599
|
+
closeSync(descriptor);
|
|
15600
|
+
} catch {
|
|
15601
|
+
}
|
|
15602
|
+
}
|
|
15452
15603
|
}
|
|
15453
|
-
|
|
15454
|
-
|
|
15455
|
-
if (process.platform === "win32")
|
|
15456
|
-
|
|
15457
|
-
|
|
15458
|
-
|
|
15604
|
+
}
|
|
15605
|
+
function permissionsArePrivate(entry) {
|
|
15606
|
+
if (process.platform === "win32") return true;
|
|
15607
|
+
return entry.mode === (entry.kind === "directory" ? PRIVATE_DIRECTORY_MODE : PRIVATE_FILE_MODE);
|
|
15608
|
+
}
|
|
15609
|
+
function secureExistingPath(path2, kind, forcePermissions = false) {
|
|
15610
|
+
const before = completeSnapshotForKind(path2, kind);
|
|
15611
|
+
if (before === null) return false;
|
|
15612
|
+
const leaf = before.entries.at(-1);
|
|
15613
|
+
if (leaf === void 0) return false;
|
|
15614
|
+
const cacheKey = `${kind}:${before.absolutePath}`;
|
|
15615
|
+
const beforeFingerprint = chainFingerprint(before);
|
|
15616
|
+
if (!forcePermissions && securedPathFingerprints.get(cacheKey) === beforeFingerprint && permissionsArePrivate(leaf)) {
|
|
15617
|
+
const cachedCheck = completeSnapshotForKind(path2, kind);
|
|
15618
|
+
return cachedCheck !== null && chainFingerprint(cachedCheck) === beforeFingerprint;
|
|
15619
|
+
}
|
|
15620
|
+
const applied = process.platform === "win32" ? applyWindowsUserOnlyAcl(path2, kind) : applyPosixPermissions(path2, kind, leaf.identity);
|
|
15621
|
+
if (!applied) return false;
|
|
15622
|
+
const after = completeSnapshotForKind(path2, kind);
|
|
15623
|
+
if (after === null || !snapshotsRetainPrefix(before, after)) return false;
|
|
15624
|
+
const afterLeaf = after.entries.at(-1);
|
|
15625
|
+
if (afterLeaf === void 0 || afterLeaf.identity !== leaf.identity) return false;
|
|
15626
|
+
if (!permissionsArePrivate(afterLeaf)) return false;
|
|
15627
|
+
securedPathFingerprints.set(cacheKey, chainFingerprint(after));
|
|
15628
|
+
return true;
|
|
15629
|
+
}
|
|
15630
|
+
function ensurePrivateDirectorySync(path2) {
|
|
15631
|
+
const before = snapshotPathChain(path2);
|
|
15632
|
+
if (before === null) return false;
|
|
15633
|
+
let current = before.entries.at(-1)?.path;
|
|
15634
|
+
if (current === void 0) return false;
|
|
15635
|
+
for (const part of before.missingParts) {
|
|
15636
|
+
const next = join(current, part);
|
|
15637
|
+
try {
|
|
15638
|
+
mkdirSync(next, { mode: PRIVATE_DIRECTORY_MODE });
|
|
15639
|
+
} catch {
|
|
15640
|
+
const raced = readPathEntry(next);
|
|
15641
|
+
if (raced.status !== "ok" || raced.entry.kind !== "directory") return false;
|
|
15642
|
+
}
|
|
15643
|
+
if (!secureExistingPath(next, "directory", true)) return false;
|
|
15644
|
+
current = next;
|
|
15459
15645
|
}
|
|
15460
|
-
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15646
|
+
const complete = completeSnapshotForKind(path2, "directory");
|
|
15647
|
+
if (complete === null || !snapshotsRetainPrefix(before, complete)) return false;
|
|
15648
|
+
return secureExistingPath(path2, "directory");
|
|
15649
|
+
}
|
|
15650
|
+
function securePrivateFileSync(path2, forceAcl = false) {
|
|
15651
|
+
return secureExistingPath(path2, "file", forceAcl);
|
|
15652
|
+
}
|
|
15653
|
+
function collectSafeTree(root) {
|
|
15654
|
+
const collected = [];
|
|
15655
|
+
const visit = (path2) => {
|
|
15656
|
+
const snapshot = snapshotPathChain(path2);
|
|
15657
|
+
if (snapshot === null || !snapshot.complete) return false;
|
|
15658
|
+
const leaf = snapshot.entries.at(-1);
|
|
15659
|
+
if (leaf === void 0) return false;
|
|
15660
|
+
const fingerprint = chainFingerprint(snapshot);
|
|
15661
|
+
collected.push({ path: snapshot.absolutePath, kind: leaf.kind, fingerprint });
|
|
15662
|
+
if (leaf.kind === "file") return true;
|
|
15663
|
+
let entries;
|
|
15664
|
+
try {
|
|
15665
|
+
entries = readdirSync(path2);
|
|
15666
|
+
} catch {
|
|
15667
|
+
return false;
|
|
15668
|
+
}
|
|
15669
|
+
const afterRead = completeSnapshotForKind(path2, "directory");
|
|
15670
|
+
if (afterRead === null || chainFingerprint(afterRead) !== fingerprint) return false;
|
|
15671
|
+
for (const entry of entries) {
|
|
15672
|
+
if (!visit(join(path2, entry))) return false;
|
|
15673
|
+
}
|
|
15674
|
+
return true;
|
|
15675
|
+
};
|
|
15676
|
+
return visit(root) ? collected : null;
|
|
15677
|
+
}
|
|
15678
|
+
function treeFingerprintsMatch(expected, observed) {
|
|
15679
|
+
if (observed === null || observed.length !== expected.length) return false;
|
|
15680
|
+
return expected.every((entry, index) => {
|
|
15681
|
+
const next = observed[index];
|
|
15682
|
+
return next !== void 0 && next.path === entry.path && next.kind === entry.kind && next.fingerprint === entry.fingerprint;
|
|
15683
|
+
});
|
|
15684
|
+
}
|
|
15685
|
+
function isPathTreeFreeOfIndirectionSync(root) {
|
|
15686
|
+
return collectSafeTree(root) !== null;
|
|
15687
|
+
}
|
|
15688
|
+
function applyWindowsUserOnlyTreeAcl(root, run = runWindowsCommand) {
|
|
15689
|
+
const entries = collectSafeTree(root);
|
|
15690
|
+
if (entries === null) return false;
|
|
15691
|
+
const sid = resolveWindowsUserSid(run);
|
|
15692
|
+
if (sid === null) return false;
|
|
15693
|
+
for (const entry of entries) {
|
|
15694
|
+
const before = completeSnapshotForKind(entry.path, entry.kind);
|
|
15695
|
+
if (before === null || chainFingerprint(before) !== entry.fingerprint) return false;
|
|
15696
|
+
if (!applyWindowsAclWithSid(entry.path, entry.kind, sid, run)) return false;
|
|
15697
|
+
const after = completeSnapshotForKind(entry.path, entry.kind);
|
|
15698
|
+
if (after === null || chainFingerprint(after) !== entry.fingerprint) return false;
|
|
15699
|
+
securedPathFingerprints.set(`${entry.kind}:${entry.path}`, entry.fingerprint);
|
|
15466
15700
|
}
|
|
15467
|
-
|
|
15701
|
+
return treeFingerprintsMatch(entries, collectSafeTree(root));
|
|
15702
|
+
}
|
|
15703
|
+
function securePrivateTreeSync(root) {
|
|
15704
|
+
const entries = collectSafeTree(root);
|
|
15705
|
+
if (entries === null) return false;
|
|
15706
|
+
if (process.platform === "win32") return applyWindowsUserOnlyTreeAcl(root);
|
|
15468
15707
|
for (const entry of entries) {
|
|
15469
|
-
|
|
15470
|
-
if (!securePrivateTreeSync(childPath)) secured = false;
|
|
15708
|
+
if (!secureExistingPath(entry.path, entry.kind, true)) return false;
|
|
15471
15709
|
}
|
|
15472
|
-
return
|
|
15710
|
+
return treeFingerprintsMatch(entries, collectSafeTree(root));
|
|
15473
15711
|
}
|
|
15474
|
-
var PRIVATE_DIRECTORY_MODE, PRIVATE_FILE_MODE,
|
|
15712
|
+
var PRIVATE_DIRECTORY_MODE, PRIVATE_FILE_MODE, securedPathFingerprints, cachedWindowsSid;
|
|
15475
15713
|
var init_privateDataPath = __esm({
|
|
15476
15714
|
"src/proxy/privateDataPath.ts"() {
|
|
15477
15715
|
"use strict";
|
|
15478
15716
|
PRIVATE_DIRECTORY_MODE = 448;
|
|
15479
15717
|
PRIVATE_FILE_MODE = 384;
|
|
15480
|
-
|
|
15481
|
-
securedFileIdentities = /* @__PURE__ */ new Map();
|
|
15718
|
+
securedPathFingerprints = /* @__PURE__ */ new Map();
|
|
15482
15719
|
cachedWindowsSid = null;
|
|
15483
15720
|
}
|
|
15484
15721
|
});
|
|
15485
15722
|
|
|
15486
15723
|
// src/proxy/dataDir.ts
|
|
15487
|
-
import { cpSync, existsSync as
|
|
15724
|
+
import { cpSync, existsSync, readdirSync as readdirSync2, renameSync, rmSync } from "node:fs";
|
|
15488
15725
|
import { isAbsolute, join as join2 } from "node:path";
|
|
15489
15726
|
function getDataDir() {
|
|
15490
15727
|
migrateLegacyDataDirs();
|
|
15491
|
-
return resolveDataDir(
|
|
15728
|
+
return resolveDataDir(existsSync);
|
|
15492
15729
|
}
|
|
15493
15730
|
function hasDataDirState(pathExists, dir) {
|
|
15494
15731
|
return pathExists(join2(dir, "providers.json")) || pathExists(join2(dir, "config.json")) || pathExists(join2(dir, "logs")) || pathExists(join2(dir, "chunks"));
|
|
@@ -15515,16 +15752,42 @@ function migrateLegacyDataDirs() {
|
|
|
15515
15752
|
if (hasExplicitDataDir()) return;
|
|
15516
15753
|
const base = defaultBaseDir();
|
|
15517
15754
|
const currentDir = join2(base, CURRENT_DATA_DIR_NAME);
|
|
15518
|
-
if (hasDataDirState(
|
|
15755
|
+
if (hasDataDirState(existsSync, currentDir)) return;
|
|
15519
15756
|
for (const legacyName of LEGACY_DATA_DIR_NAMES) {
|
|
15520
15757
|
const legacyDir = join2(base, legacyName);
|
|
15521
|
-
if (!
|
|
15758
|
+
if (!existsSync(legacyDir)) continue;
|
|
15759
|
+
const stagingDir = join2(
|
|
15760
|
+
base,
|
|
15761
|
+
`${CURRENT_DATA_DIR_NAME}.migration-${String(process.pid)}-${String(Date.now())}`
|
|
15762
|
+
);
|
|
15522
15763
|
try {
|
|
15523
|
-
if (!
|
|
15524
|
-
|
|
15525
|
-
|
|
15764
|
+
if (!isPathTreeFreeOfIndirectionSync(legacyDir)) return;
|
|
15765
|
+
if (existsSync(currentDir)) {
|
|
15766
|
+
if (readdirSync2(currentDir).length > 0) return;
|
|
15767
|
+
rmSync(currentDir, { recursive: true, force: false });
|
|
15768
|
+
}
|
|
15769
|
+
if (!ensurePrivateDirectorySync(stagingDir)) return;
|
|
15770
|
+
for (const entry of readdirSync2(legacyDir)) {
|
|
15771
|
+
cpSync(join2(legacyDir, entry), join2(stagingDir, entry), {
|
|
15772
|
+
recursive: true,
|
|
15773
|
+
force: false,
|
|
15774
|
+
errorOnExist: true
|
|
15775
|
+
});
|
|
15776
|
+
}
|
|
15777
|
+
if (!isPathTreeFreeOfIndirectionSync(legacyDir)) {
|
|
15778
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
15779
|
+
return;
|
|
15780
|
+
}
|
|
15781
|
+
if (!securePrivateTreeSync(stagingDir)) {
|
|
15782
|
+
rmSync(stagingDir, { recursive: true, force: true });
|
|
15783
|
+
return;
|
|
15784
|
+
}
|
|
15785
|
+
renameSync(stagingDir, currentDir);
|
|
15526
15786
|
rmSync(legacyDir, { recursive: true, force: true });
|
|
15787
|
+
return;
|
|
15527
15788
|
} catch {
|
|
15789
|
+
if (existsSync(stagingDir)) rmSync(stagingDir, { recursive: true, force: true });
|
|
15790
|
+
return;
|
|
15528
15791
|
}
|
|
15529
15792
|
}
|
|
15530
15793
|
}
|
|
@@ -15539,9 +15802,9 @@ var init_dataDir = __esm({
|
|
|
15539
15802
|
});
|
|
15540
15803
|
|
|
15541
15804
|
// src/proxy/platformCommands.ts
|
|
15542
|
-
import { statSync
|
|
15805
|
+
import { statSync } from "node:fs";
|
|
15543
15806
|
import { execFileSync } from "node:child_process";
|
|
15544
|
-
import { delimiter, isAbsolute as isAbsolute2, join as
|
|
15807
|
+
import { delimiter, isAbsolute as isAbsolute2, join as join4 } from "node:path";
|
|
15545
15808
|
function pathEntries(envPath) {
|
|
15546
15809
|
if (envPath === void 0 || envPath.length === 0) return [];
|
|
15547
15810
|
return envPath.split(delimiter).filter((entry) => entry.length > 0);
|
|
@@ -15554,10 +15817,10 @@ function windowsExecutableExtensions(envPathExt) {
|
|
|
15554
15817
|
}
|
|
15555
15818
|
function isExecutableCandidate(path2, platform) {
|
|
15556
15819
|
try {
|
|
15557
|
-
const
|
|
15558
|
-
if (!
|
|
15820
|
+
const stat4 = statSync(path2);
|
|
15821
|
+
if (!stat4.isFile()) return false;
|
|
15559
15822
|
if (platform === "win32" || process.platform === "win32") return true;
|
|
15560
|
-
return (
|
|
15823
|
+
return (stat4.mode & 73) !== 0;
|
|
15561
15824
|
} catch {
|
|
15562
15825
|
return false;
|
|
15563
15826
|
}
|
|
@@ -15565,14 +15828,14 @@ function isExecutableCandidate(path2, platform) {
|
|
|
15565
15828
|
function executableCandidates(bin, platform, env) {
|
|
15566
15829
|
if (isAbsolute2(bin)) return [bin];
|
|
15567
15830
|
const entries = pathEntries(env["PATH"] ?? env["Path"] ?? env["path"]);
|
|
15568
|
-
if (platform !== "win32") return entries.map((entry) =>
|
|
15831
|
+
if (platform !== "win32") return entries.map((entry) => join4(entry, bin));
|
|
15569
15832
|
const hasExtension = /\.[^\\/]+$/.test(bin);
|
|
15570
15833
|
const extensions = hasExtension ? [""] : windowsExecutableExtensions(env["PATHEXT"]);
|
|
15571
15834
|
const candidates = [];
|
|
15572
15835
|
for (const entry of entries) {
|
|
15573
15836
|
for (const extension of extensions) {
|
|
15574
|
-
candidates.push(
|
|
15575
|
-
candidates.push(
|
|
15837
|
+
candidates.push(join4(entry, `${bin}${extension.toLowerCase()}`));
|
|
15838
|
+
candidates.push(join4(entry, `${bin}${extension.toUpperCase()}`));
|
|
15576
15839
|
}
|
|
15577
15840
|
}
|
|
15578
15841
|
return candidates;
|
|
@@ -15693,7 +15956,7 @@ var init_platformCommands = __esm({
|
|
|
15693
15956
|
|
|
15694
15957
|
// src/cli/detect-tools.ts
|
|
15695
15958
|
import { existsSync as existsSync3 } from "node:fs";
|
|
15696
|
-
import { join as
|
|
15959
|
+
import { join as join5 } from "node:path";
|
|
15697
15960
|
import { homedir } from "node:os";
|
|
15698
15961
|
function which(bin) {
|
|
15699
15962
|
return resolveExecutableOnPath(bin);
|
|
@@ -15702,31 +15965,31 @@ function tryDir(path2) {
|
|
|
15702
15965
|
return existsSync3(path2) ? path2 : null;
|
|
15703
15966
|
}
|
|
15704
15967
|
function detectClaudeCode() {
|
|
15705
|
-
const configDir = tryDir(
|
|
15968
|
+
const configDir = tryDir(join5(homedir(), ".claude"));
|
|
15706
15969
|
const bin = which("claude");
|
|
15707
15970
|
if (configDir === null && bin === null) return { found: false };
|
|
15708
15971
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
15709
15972
|
}
|
|
15710
15973
|
function detectOpenCode() {
|
|
15711
|
-
const configDir = tryDir(
|
|
15974
|
+
const configDir = tryDir(join5(homedir(), ".config", "opencode"));
|
|
15712
15975
|
const bin = which("opencode");
|
|
15713
15976
|
if (configDir === null && bin === null) return { found: false };
|
|
15714
15977
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
15715
15978
|
}
|
|
15716
15979
|
function detectMiMo() {
|
|
15717
|
-
const configDir = tryDir(
|
|
15980
|
+
const configDir = tryDir(join5(homedir(), ".config", "mimocode")) ?? tryDir(join5(homedir(), ".mimocode"));
|
|
15718
15981
|
const bin = which("mimo");
|
|
15719
15982
|
if (configDir === null && bin === null) return { found: false };
|
|
15720
15983
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
15721
15984
|
}
|
|
15722
15985
|
function detectCursor() {
|
|
15723
|
-
const configDir = tryDir(
|
|
15986
|
+
const configDir = tryDir(join5(homedir(), ".cursor"));
|
|
15724
15987
|
const bin = which("cursor");
|
|
15725
15988
|
if (configDir === null && bin === null) return { found: false };
|
|
15726
15989
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
15727
15990
|
}
|
|
15728
15991
|
function detectCody() {
|
|
15729
|
-
const configDir = tryDir(
|
|
15992
|
+
const configDir = tryDir(join5(homedir(), ".config", "cody"));
|
|
15730
15993
|
const bin = which("cody");
|
|
15731
15994
|
if (configDir === null && bin === null) return { found: false };
|
|
15732
15995
|
return { found: true, path: configDir ?? bin ?? "" };
|
|
@@ -15786,7 +16049,7 @@ var init_runtimeConfig = __esm({
|
|
|
15786
16049
|
});
|
|
15787
16050
|
|
|
15788
16051
|
// src/cli/instanceModel.ts
|
|
15789
|
-
import { randomBytes, randomUUID } from "node:crypto";
|
|
16052
|
+
import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
|
|
15790
16053
|
import { isAbsolute as isAbsolute3 } from "node:path";
|
|
15791
16054
|
function instanceRegistrySuccess(value) {
|
|
15792
16055
|
return { kind: "success", value };
|
|
@@ -15803,19 +16066,13 @@ function validateInstanceName(rawName) {
|
|
|
15803
16066
|
) : instanceRegistrySuccess(normalized);
|
|
15804
16067
|
}
|
|
15805
16068
|
function normalizeInstanceBasePath(rawBasePath) {
|
|
15806
|
-
const
|
|
15807
|
-
|
|
15808
|
-
const hasControlCharacter = [...trimmed].some((character) => {
|
|
15809
|
-
const codePoint = character.codePointAt(0) ?? 0;
|
|
15810
|
-
return codePoint < 32 || codePoint === 127;
|
|
15811
|
-
});
|
|
15812
|
-
if (trimmed.length > 0 && (!trimmed.startsWith("/") || trimmed.includes("?") || trimmed.includes("#") || trimmed.includes("\\") || hasControlCharacter)) {
|
|
16069
|
+
const normalized = normalizePublicBasePath(rawBasePath);
|
|
16070
|
+
if (normalized === null) {
|
|
15813
16071
|
return instanceRegistryError(
|
|
15814
16072
|
"record_invalid",
|
|
15815
|
-
"Instance Base Path must
|
|
16073
|
+
"Instance Base Path must use URL-safe path segments without traversal, encoded separators, or markup characters."
|
|
15816
16074
|
);
|
|
15817
16075
|
}
|
|
15818
|
-
const normalized = normalizeBasePath(trimmed);
|
|
15819
16076
|
const reservedControlRoot = MANAGED_INSTANCE_CONTROL_PATH.replace(/\/instance$/u, "");
|
|
15820
16077
|
if (normalized === reservedControlRoot || normalized.startsWith(`${reservedControlRoot}/`)) {
|
|
15821
16078
|
return instanceRegistryError(
|
|
@@ -15827,7 +16084,7 @@ function normalizeInstanceBasePath(rawBasePath) {
|
|
|
15827
16084
|
}
|
|
15828
16085
|
function createLaunch(factory, now) {
|
|
15829
16086
|
return {
|
|
15830
|
-
launchId: factory.createLaunchId?.() ??
|
|
16087
|
+
launchId: factory.createLaunchId?.() ?? randomUUID2(),
|
|
15831
16088
|
controlToken: factory.createControlToken?.() ?? randomBytes(32).toString("base64url"),
|
|
15832
16089
|
startedAt: now.toISOString()
|
|
15833
16090
|
};
|
|
@@ -15842,7 +16099,7 @@ function createInstanceRecord(input, factory = {}) {
|
|
|
15842
16099
|
const candidate = {
|
|
15843
16100
|
schemaVersion: INSTANCE_RECORD_SCHEMA_VERSION,
|
|
15844
16101
|
name: name.value,
|
|
15845
|
-
instanceId: factory.createInstanceId?.() ??
|
|
16102
|
+
instanceId: factory.createInstanceId?.() ?? randomUUID2(),
|
|
15846
16103
|
createdAt: timestamp,
|
|
15847
16104
|
updatedAt: timestamp,
|
|
15848
16105
|
host: input.host.trim(),
|
|
@@ -15850,6 +16107,7 @@ function createInstanceRecord(input, factory = {}) {
|
|
|
15850
16107
|
upstreamPort: input.upstreamPort,
|
|
15851
16108
|
basePath: basePath.value,
|
|
15852
16109
|
captureMode: input.captureMode,
|
|
16110
|
+
uiEnabled: input.uiEnabled ?? true,
|
|
15853
16111
|
dataDir: input.dataDir,
|
|
15854
16112
|
launch: createLaunch(factory, now)
|
|
15855
16113
|
};
|
|
@@ -15911,6 +16169,7 @@ var init_instanceModel = __esm({
|
|
|
15911
16169
|
upstreamPort: PortSchema,
|
|
15912
16170
|
basePath: NormalizedBasePathSchema,
|
|
15913
16171
|
captureMode: CaptureModeSchema,
|
|
16172
|
+
uiEnabled: external_exports.boolean().default(true),
|
|
15914
16173
|
dataDir: external_exports.string().min(1).refine((path2) => isAbsolute3(path2)),
|
|
15915
16174
|
launch: InstanceLaunchSchema
|
|
15916
16175
|
}).strict().superRefine((record2, context) => {
|
|
@@ -15926,23 +16185,23 @@ var init_instanceModel = __esm({
|
|
|
15926
16185
|
});
|
|
15927
16186
|
|
|
15928
16187
|
// src/cli/instanceRegistry.ts
|
|
15929
|
-
import { randomUUID as
|
|
16188
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
15930
16189
|
import {
|
|
15931
|
-
closeSync,
|
|
16190
|
+
closeSync as closeSync3,
|
|
15932
16191
|
existsSync as existsSync4,
|
|
15933
16192
|
mkdirSync as mkdirSync2,
|
|
15934
|
-
openSync,
|
|
16193
|
+
openSync as openSync3,
|
|
15935
16194
|
readFileSync,
|
|
15936
|
-
readdirSync as
|
|
15937
|
-
realpathSync,
|
|
15938
|
-
renameSync,
|
|
16195
|
+
readdirSync as readdirSync3,
|
|
16196
|
+
realpathSync as realpathSync2,
|
|
16197
|
+
renameSync as renameSync2,
|
|
15939
16198
|
rmSync as rmSync2,
|
|
15940
|
-
statSync as
|
|
16199
|
+
statSync as statSync2,
|
|
15941
16200
|
writeFileSync
|
|
15942
16201
|
} from "node:fs";
|
|
15943
16202
|
import { createServer } from "node:net";
|
|
15944
16203
|
import { homedir as homedir2 } from "node:os";
|
|
15945
|
-
import { join as
|
|
16204
|
+
import { join as join6, posix, win32 } from "node:path";
|
|
15946
16205
|
function getInstancesRoot(env = process.env, platform = process.platform) {
|
|
15947
16206
|
const pathApi = platform === "win32" ? win32 : posix;
|
|
15948
16207
|
const explicitRoot = env[AGENT_INSPECTOR_INSTANCES_DIR_ENV];
|
|
@@ -15965,13 +16224,13 @@ function getInstancesRoot(env = process.env, platform = process.platform) {
|
|
|
15965
16224
|
function getInstancePaths(root, rawName) {
|
|
15966
16225
|
const name = validateInstanceName(rawName);
|
|
15967
16226
|
if (name.kind === "error") return name;
|
|
15968
|
-
const instanceDir =
|
|
16227
|
+
const instanceDir = join6(root, name.value);
|
|
15969
16228
|
return instanceRegistrySuccess({
|
|
15970
16229
|
root,
|
|
15971
16230
|
instanceDir,
|
|
15972
|
-
dataDir:
|
|
15973
|
-
recordPath:
|
|
15974
|
-
lockPath:
|
|
16231
|
+
dataDir: join6(instanceDir, "data"),
|
|
16232
|
+
recordPath: join6(instanceDir, INSTANCE_RECORD_FILE_NAME),
|
|
16233
|
+
lockPath: join6(instanceDir, INSTANCE_LOCK_DIRECTORY_NAME)
|
|
15975
16234
|
});
|
|
15976
16235
|
}
|
|
15977
16236
|
function ensureInstancesRoot(root = getInstancesRoot()) {
|
|
@@ -16011,7 +16270,7 @@ function listInstanceRecords(root) {
|
|
|
16011
16270
|
if (!existsSync4(root)) return instanceRegistrySuccess([]);
|
|
16012
16271
|
let entries;
|
|
16013
16272
|
try {
|
|
16014
|
-
entries =
|
|
16273
|
+
entries = readdirSync3(root, { withFileTypes: true });
|
|
16015
16274
|
} catch {
|
|
16016
16275
|
return instanceRegistryError("registry_io", `Cannot read the instance registry at ${root}.`);
|
|
16017
16276
|
}
|
|
@@ -16019,7 +16278,7 @@ function listInstanceRecords(root) {
|
|
|
16019
16278
|
const names = /* @__PURE__ */ new Set();
|
|
16020
16279
|
for (const entry of entries) {
|
|
16021
16280
|
if (!entry.isDirectory()) continue;
|
|
16022
|
-
const recordPath =
|
|
16281
|
+
const recordPath = join6(root, entry.name, INSTANCE_RECORD_FILE_NAME);
|
|
16023
16282
|
if (!existsSync4(recordPath)) continue;
|
|
16024
16283
|
const record2 = readRecordFile(recordPath);
|
|
16025
16284
|
if (record2.kind === "error") return record2;
|
|
@@ -16066,16 +16325,16 @@ function writeInstanceRecord(root, record2) {
|
|
|
16066
16325
|
`Cannot create or secure the data directory for instance ${validated.data.name}.`
|
|
16067
16326
|
);
|
|
16068
16327
|
}
|
|
16069
|
-
const temporaryPath =
|
|
16328
|
+
const temporaryPath = join6(
|
|
16070
16329
|
paths.value.instanceDir,
|
|
16071
|
-
`.${INSTANCE_RECORD_FILE_NAME}.${String(process.pid)}.${
|
|
16330
|
+
`.${INSTANCE_RECORD_FILE_NAME}.${String(process.pid)}.${randomUUID3()}.tmp`
|
|
16072
16331
|
);
|
|
16073
16332
|
let descriptor = null;
|
|
16074
16333
|
try {
|
|
16075
|
-
descriptor =
|
|
16334
|
+
descriptor = openSync3(temporaryPath, "wx", PRIVATE_FILE_MODE);
|
|
16076
16335
|
writeFileSync(descriptor, `${JSON.stringify(validated.data, null, 2)}
|
|
16077
16336
|
`, "utf8");
|
|
16078
|
-
|
|
16337
|
+
closeSync3(descriptor);
|
|
16079
16338
|
descriptor = null;
|
|
16080
16339
|
if (!securePrivateFileSync(temporaryPath)) {
|
|
16081
16340
|
cleanupFile(temporaryPath);
|
|
@@ -16084,12 +16343,12 @@ function writeInstanceRecord(root, record2) {
|
|
|
16084
16343
|
`Cannot secure the saved record for instance ${validated.data.name}.`
|
|
16085
16344
|
);
|
|
16086
16345
|
}
|
|
16087
|
-
|
|
16346
|
+
renameSync2(temporaryPath, paths.value.recordPath);
|
|
16088
16347
|
return instanceRegistrySuccess(validated.data);
|
|
16089
16348
|
} catch {
|
|
16090
16349
|
if (descriptor !== null) {
|
|
16091
16350
|
try {
|
|
16092
|
-
|
|
16351
|
+
closeSync3(descriptor);
|
|
16093
16352
|
} catch {
|
|
16094
16353
|
}
|
|
16095
16354
|
}
|
|
@@ -16116,7 +16375,7 @@ function defaultIsProcessAlive(pid) {
|
|
|
16116
16375
|
}
|
|
16117
16376
|
function readLockOwner(lockPath) {
|
|
16118
16377
|
try {
|
|
16119
|
-
const raw = JSON.parse(readFileSync(
|
|
16378
|
+
const raw = JSON.parse(readFileSync(join6(lockPath, LOCK_OWNER_FILE_NAME), "utf8"));
|
|
16120
16379
|
const parsed = InstanceLockOwnerSchema.safeParse(raw);
|
|
16121
16380
|
return parsed.success ? parsed.data : null;
|
|
16122
16381
|
} catch {
|
|
@@ -16126,7 +16385,7 @@ function readLockOwner(lockPath) {
|
|
|
16126
16385
|
function lockCreatedAt(lockPath, owner) {
|
|
16127
16386
|
if (owner !== null) return Date.parse(owner.createdAt);
|
|
16128
16387
|
try {
|
|
16129
|
-
return
|
|
16388
|
+
return statSync2(lockPath).mtimeMs;
|
|
16130
16389
|
} catch {
|
|
16131
16390
|
return null;
|
|
16132
16391
|
}
|
|
@@ -16136,9 +16395,9 @@ function recoverStaleLock(lockPath, staleAfterMs, now, isProcessAlive) {
|
|
|
16136
16395
|
const createdAt = lockCreatedAt(lockPath, owner);
|
|
16137
16396
|
if (createdAt === null || now - createdAt < staleAfterMs) return false;
|
|
16138
16397
|
if (owner !== null && isProcessAlive(owner.pid)) return false;
|
|
16139
|
-
const recoveredPath = `${lockPath}.stale-${
|
|
16398
|
+
const recoveredPath = `${lockPath}.stale-${randomUUID3()}`;
|
|
16140
16399
|
try {
|
|
16141
|
-
|
|
16400
|
+
renameSync2(lockPath, recoveredPath);
|
|
16142
16401
|
rmSync2(recoveredPath, { recursive: true, force: true });
|
|
16143
16402
|
return true;
|
|
16144
16403
|
} catch {
|
|
@@ -16169,7 +16428,7 @@ async function acquireDirectoryLock(lockPath, options) {
|
|
|
16169
16428
|
const now = options.now ?? Date.now;
|
|
16170
16429
|
const sleep3 = options.sleep ?? defaultSleep;
|
|
16171
16430
|
const isProcessAlive = options.isProcessAlive ?? defaultIsProcessAlive;
|
|
16172
|
-
const createToken = options.createToken ??
|
|
16431
|
+
const createToken = options.createToken ?? randomUUID3;
|
|
16173
16432
|
const startedAt = now();
|
|
16174
16433
|
while (now() - startedAt <= timeoutMs) {
|
|
16175
16434
|
const token = createToken();
|
|
@@ -16195,7 +16454,7 @@ async function acquireDirectoryLock(lockPath, options) {
|
|
|
16195
16454
|
token,
|
|
16196
16455
|
createdAt: new Date(now()).toISOString()
|
|
16197
16456
|
};
|
|
16198
|
-
const ownerPath =
|
|
16457
|
+
const ownerPath = join6(lockPath, LOCK_OWNER_FILE_NAME);
|
|
16199
16458
|
writeFileSync(ownerPath, `${JSON.stringify(owner)}
|
|
16200
16459
|
`, {
|
|
16201
16460
|
encoding: "utf8",
|
|
@@ -16225,7 +16484,7 @@ async function acquireDirectoryLock(lockPath, options) {
|
|
|
16225
16484
|
}
|
|
16226
16485
|
async function acquireRegistryLock(root, options = {}) {
|
|
16227
16486
|
const ensured = ensureInstancesRoot(root);
|
|
16228
|
-
return ensured.kind === "error" ? ensured : acquireDirectoryLock(
|
|
16487
|
+
return ensured.kind === "error" ? ensured : acquireDirectoryLock(join6(root, ROOT_LOCK_DIRECTORY_NAME), options);
|
|
16229
16488
|
}
|
|
16230
16489
|
async function acquireInstanceLock(root, rawName, options = {}) {
|
|
16231
16490
|
const paths = getInstancePaths(root, rawName);
|
|
@@ -16268,7 +16527,7 @@ function canonicalizeDataDirectory(rawPath, options = {}) {
|
|
|
16268
16527
|
const resolvedPath = resolveRealPathWithMissingTail(
|
|
16269
16528
|
absolutePath,
|
|
16270
16529
|
pathApi,
|
|
16271
|
-
options.realpath ??
|
|
16530
|
+
options.realpath ?? realpathSync2.native
|
|
16272
16531
|
);
|
|
16273
16532
|
const normalizedPath = pathApi.normalize(resolvedPath);
|
|
16274
16533
|
const root = pathApi.parse(normalizedPath).root;
|
|
@@ -16419,18 +16678,21 @@ var init_instanceRegistry = __esm({
|
|
|
16419
16678
|
});
|
|
16420
16679
|
|
|
16421
16680
|
// src/cli/onboardTarget.ts
|
|
16422
|
-
function
|
|
16681
|
+
function targetFromSurfaces(kind, instanceName, mcpName, port, controlBaseUrl, agentBaseUrl, uiUrl) {
|
|
16423
16682
|
return {
|
|
16424
16683
|
kind,
|
|
16425
16684
|
instanceName,
|
|
16426
16685
|
mcpName,
|
|
16427
16686
|
port,
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16687
|
+
uiUrl,
|
|
16688
|
+
controlBaseUrl,
|
|
16689
|
+
agentBaseUrl,
|
|
16690
|
+
baseUrl: controlBaseUrl,
|
|
16691
|
+
apiUrl: `${controlBaseUrl}/api`,
|
|
16692
|
+
healthUrl: `${controlBaseUrl}/api/health`,
|
|
16693
|
+
anthropicProxyUrl: `${agentBaseUrl}/proxy`,
|
|
16694
|
+
openAiProxyUrl: `${agentBaseUrl}/proxy/v1`,
|
|
16695
|
+
mcpUrl: `${agentBaseUrl}/api/mcp`
|
|
16434
16696
|
};
|
|
16435
16697
|
}
|
|
16436
16698
|
function validateMcpName(rawName) {
|
|
@@ -16438,29 +16700,35 @@ function validateMcpName(rawName) {
|
|
|
16438
16700
|
return name === rawName && MCP_NAME_PATTERN.test(name) ? name : null;
|
|
16439
16701
|
}
|
|
16440
16702
|
function normalizeExplicitBaseUrl(rawBaseUrl) {
|
|
16441
|
-
const
|
|
16442
|
-
if (
|
|
16703
|
+
const baseUrl = normalizeBrowserRuntimeBaseUrl(rawBaseUrl);
|
|
16704
|
+
if (baseUrl === null) return null;
|
|
16705
|
+
try {
|
|
16706
|
+
const parsed = new URL(baseUrl);
|
|
16707
|
+
const defaultPort = parsed.protocol === "https:" ? 443 : 80;
|
|
16708
|
+
const port = parsed.port === "" ? defaultPort : Number(parsed.port);
|
|
16709
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535 ? { baseUrl, port } : null;
|
|
16710
|
+
} catch {
|
|
16443
16711
|
return null;
|
|
16444
16712
|
}
|
|
16713
|
+
}
|
|
16714
|
+
function normalizePublicOrigin(rawOrigin) {
|
|
16715
|
+
if (rawOrigin.trim() !== rawOrigin) return null;
|
|
16445
16716
|
try {
|
|
16446
|
-
const parsed = new URL(
|
|
16447
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.
|
|
16717
|
+
const parsed = new URL(rawOrigin);
|
|
16718
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username.length > 0 || parsed.password.length > 0 || parsed.pathname !== "/" || parsed.search.length > 0 || parsed.hash.length > 0) {
|
|
16448
16719
|
return null;
|
|
16449
16720
|
}
|
|
16450
|
-
|
|
16451
|
-
const baseUrl = `${parsed.origin}${pathname === "" ? "" : pathname}`;
|
|
16452
|
-
const defaultPort = parsed.protocol === "https:" ? 443 : 80;
|
|
16453
|
-
const port = parsed.port === "" ? defaultPort : Number(parsed.port);
|
|
16454
|
-
return Number.isInteger(port) && port >= 1 && port <= 65535 ? { baseUrl, port } : null;
|
|
16721
|
+
return parsed.origin;
|
|
16455
16722
|
} catch {
|
|
16456
16723
|
return null;
|
|
16457
16724
|
}
|
|
16458
16725
|
}
|
|
16459
16726
|
function resolveOnboardTarget(options, env = process.env) {
|
|
16460
|
-
|
|
16727
|
+
const hasExplicitSurface = options.baseUrl !== null || options.controlBaseUrl !== null && options.controlBaseUrl !== void 0 || options.agentBaseUrl !== null && options.agentBaseUrl !== void 0 || options.uiUrl !== void 0;
|
|
16728
|
+
if (options.instanceName !== null && hasExplicitSurface) {
|
|
16461
16729
|
return {
|
|
16462
16730
|
ok: false,
|
|
16463
|
-
message: "--instance and --base-url are mutually exclusive"
|
|
16731
|
+
message: "--instance and explicit --base-url/--control-base-url/--agent-base-url/--ui-url options are mutually exclusive"
|
|
16464
16732
|
};
|
|
16465
16733
|
}
|
|
16466
16734
|
const explicitMcpName = options.mcpName === null ? null : validateMcpName(options.mcpName);
|
|
@@ -16470,22 +16738,29 @@ function resolveOnboardTarget(options, env = process.env) {
|
|
|
16470
16738
|
message: "--mcp-name must be 1-128 letters, digits, dots, hyphens, or underscores and start with a letter or digit"
|
|
16471
16739
|
};
|
|
16472
16740
|
}
|
|
16473
|
-
if (
|
|
16474
|
-
const
|
|
16475
|
-
|
|
16741
|
+
if (hasExplicitSurface) {
|
|
16742
|
+
const sharedRaw = options.baseUrl;
|
|
16743
|
+
const controlRaw = options.controlBaseUrl ?? sharedRaw ?? options.agentBaseUrl;
|
|
16744
|
+
const agentRaw = options.agentBaseUrl ?? sharedRaw ?? options.controlBaseUrl;
|
|
16745
|
+
const control = controlRaw === null || controlRaw === void 0 ? null : normalizeExplicitBaseUrl(controlRaw);
|
|
16746
|
+
const agent = agentRaw === null || agentRaw === void 0 ? null : normalizeExplicitBaseUrl(agentRaw);
|
|
16747
|
+
const ui = options.uiUrl === void 0 || options.uiUrl === null ? null : normalizeExplicitBaseUrl(options.uiUrl);
|
|
16748
|
+
if (control === null || agent === null || options.uiUrl !== void 0 && options.uiUrl !== null && ui === null) {
|
|
16476
16749
|
return {
|
|
16477
16750
|
ok: false,
|
|
16478
|
-
message: "
|
|
16751
|
+
message: "Explicit UI, control, and agent Base URLs must be credential-free HTTP(S) URLs without a query or fragment"
|
|
16479
16752
|
};
|
|
16480
16753
|
}
|
|
16481
16754
|
return {
|
|
16482
16755
|
ok: true,
|
|
16483
|
-
target:
|
|
16756
|
+
target: targetFromSurfaces(
|
|
16484
16757
|
"base-url",
|
|
16485
16758
|
null,
|
|
16486
16759
|
explicitMcpName ?? "agent-inspector",
|
|
16487
|
-
|
|
16488
|
-
|
|
16760
|
+
control.port,
|
|
16761
|
+
control.baseUrl,
|
|
16762
|
+
agent.baseUrl,
|
|
16763
|
+
ui?.baseUrl ?? null
|
|
16489
16764
|
)
|
|
16490
16765
|
};
|
|
16491
16766
|
}
|
|
@@ -16508,36 +16783,50 @@ function resolveOnboardTarget(options, env = process.env) {
|
|
|
16508
16783
|
const baseUrl2 = appendBasePathToOrigin(origin, "/", record2.value.basePath);
|
|
16509
16784
|
return {
|
|
16510
16785
|
ok: true,
|
|
16511
|
-
target:
|
|
16786
|
+
target: targetFromSurfaces(
|
|
16512
16787
|
"instance",
|
|
16513
16788
|
record2.value.name,
|
|
16514
16789
|
explicitMcpName ?? `agent-inspector-${record2.value.name}`,
|
|
16515
16790
|
record2.value.publicPort,
|
|
16516
|
-
baseUrl2
|
|
16791
|
+
baseUrl2,
|
|
16792
|
+
baseUrl2,
|
|
16793
|
+
record2.value.uiEnabled ? baseUrl2 : null
|
|
16517
16794
|
)
|
|
16518
16795
|
};
|
|
16519
16796
|
}
|
|
16520
|
-
const
|
|
16797
|
+
const configuredPublicOrigin = env[AGENT_INSPECTOR_PUBLIC_ORIGIN_ENV];
|
|
16798
|
+
const publicOrigin = configuredPublicOrigin === void 0 ? DEFAULT_ORIGIN : normalizePublicOrigin(configuredPublicOrigin);
|
|
16799
|
+
if (publicOrigin === null) {
|
|
16800
|
+
return {
|
|
16801
|
+
ok: false,
|
|
16802
|
+
message: "AGENT_INSPECTOR_PUBLIC_ORIGIN must be an exact credential-free HTTP(S) origin without a path, query, or fragment"
|
|
16803
|
+
};
|
|
16804
|
+
}
|
|
16805
|
+
const baseUrl = appendBasePathToOrigin(publicOrigin, "/", getConfiguredBasePath(env));
|
|
16521
16806
|
return {
|
|
16522
16807
|
ok: true,
|
|
16523
|
-
target:
|
|
16808
|
+
target: targetFromSurfaces(
|
|
16524
16809
|
"default",
|
|
16525
16810
|
null,
|
|
16526
16811
|
explicitMcpName ?? "agent-inspector",
|
|
16527
16812
|
DEFAULT_PORT,
|
|
16528
|
-
baseUrl
|
|
16813
|
+
baseUrl,
|
|
16814
|
+
baseUrl,
|
|
16815
|
+
env["AGENT_INSPECTOR_BACKEND_ONLY"] === "1" ? null : baseUrl
|
|
16529
16816
|
)
|
|
16530
16817
|
};
|
|
16531
16818
|
}
|
|
16532
|
-
var DEFAULT_PORT, DEFAULT_ORIGIN, MCP_NAME_PATTERN;
|
|
16819
|
+
var DEFAULT_PORT, DEFAULT_ORIGIN, AGENT_INSPECTOR_PUBLIC_ORIGIN_ENV, MCP_NAME_PATTERN;
|
|
16533
16820
|
var init_onboardTarget = __esm({
|
|
16534
16821
|
"src/cli/onboardTarget.ts"() {
|
|
16535
16822
|
"use strict";
|
|
16536
16823
|
init_basePath();
|
|
16824
|
+
init_browserRuntimeContract();
|
|
16537
16825
|
init_instanceRegistry();
|
|
16538
16826
|
init_networkHints();
|
|
16539
16827
|
DEFAULT_PORT = 9527;
|
|
16540
16828
|
DEFAULT_ORIGIN = `http://localhost:${String(DEFAULT_PORT)}`;
|
|
16829
|
+
AGENT_INSPECTOR_PUBLIC_ORIGIN_ENV = "AGENT_INSPECTOR_PUBLIC_ORIGIN";
|
|
16541
16830
|
MCP_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
16542
16831
|
}
|
|
16543
16832
|
});
|
|
@@ -16564,22 +16853,28 @@ var init_command_onboard = __esm({
|
|
|
16564
16853
|
|
|
16565
16854
|
// src/cli/templates/codex-skill-onboard.ts
|
|
16566
16855
|
function renderCodexSkillOnboard(ctx) {
|
|
16567
|
-
const { version: version2
|
|
16856
|
+
const { version: version2 } = ctx;
|
|
16857
|
+
const controlBaseUrl = ctx.controlBaseUrl ?? ctx.baseUrl;
|
|
16858
|
+
const agentBaseUrl = ctx.agentBaseUrl ?? ctx.baseUrl;
|
|
16859
|
+
const uiUrl = ctx.uiUrl === void 0 ? ctx.baseUrl : ctx.uiUrl;
|
|
16568
16860
|
const mcpName = ctx.mcpName ?? "agent-inspector";
|
|
16569
16861
|
const targetKind = ctx.targetKind ?? "default";
|
|
16570
16862
|
const targetArguments = ctx.onboardTargetArguments ?? "";
|
|
16571
16863
|
const instanceName = ctx.instanceName ?? null;
|
|
16572
|
-
const healthUrl = `${
|
|
16573
|
-
const
|
|
16574
|
-
const
|
|
16575
|
-
const
|
|
16576
|
-
const
|
|
16864
|
+
const healthUrl = `${controlBaseUrl}/api/health`;
|
|
16865
|
+
const runtimeUrl = `${controlBaseUrl}/api/runtime`;
|
|
16866
|
+
const configUrl = `${controlBaseUrl}/api/config`;
|
|
16867
|
+
const anthropicProxyUrl = `${agentBaseUrl}/proxy`;
|
|
16868
|
+
const openAiProxyUrl = `${agentBaseUrl}/proxy/v1`;
|
|
16869
|
+
const mcpUrl = `${agentBaseUrl}/api/mcp`;
|
|
16577
16870
|
const mcpTomlKey = /^[A-Za-z0-9_-]+$/u.test(mcpName) ? mcpName : JSON.stringify(mcpName);
|
|
16578
16871
|
const statusCommand = `agent-inspector onboard --status${targetArguments}`;
|
|
16579
16872
|
const refreshCommand = `agent-inspector onboard --force${targetArguments}`;
|
|
16580
|
-
const startCommand = targetKind === "instance" && instanceName !== null ? `agent-inspector instance start ${instanceName}` : targetKind === "base-url" ? `# Explicit target ${
|
|
16873
|
+
const startCommand = targetKind === "instance" && instanceName !== null ? `agent-inspector instance start ${instanceName}` : targetKind === "base-url" ? `# Explicit control target ${controlBaseUrl}: do not start a replacement local runtime.` : "agent-inspector --mode simple --background --no-open";
|
|
16581
16874
|
const fullModeCommand = targetKind === "instance" && instanceName !== null ? `agent-inspector instance restart ${instanceName} --mode full` : targetKind === "base-url" ? "ask the target operator to enable Full capture mode" : "agent-inspector --mode full --force-restart";
|
|
16582
|
-
const startExplanation = targetKind === "base-url" ? `The selected Inspector target
|
|
16875
|
+
const startExplanation = targetKind === "base-url" ? `The selected Inspector target uses control Base URL \`${controlBaseUrl}\` and agent Base URL \`${agentBaseUrl}\`. Do not start the default local runtime as a substitute; verify the selected target and ask its operator for help if it is unavailable.` : `Start or reuse the selected Agent Inspector backend before wiring MCP. The default command composes a UI, while backend-only named instances retain proxy, REST, health, and MCP without UI files. Prefer the installed binary so Windows users get the branded runtime in Task Manager. Use \`${fullModeCommand}\` only when the user needs raw headers, raw response, or SSE chunks.`;
|
|
16876
|
+
const selectedUi = uiUrl === null ? "not supplied (do not infer one from a backend URL)" : `\`${uiUrl}\``;
|
|
16877
|
+
const providerTestUiGuidance = uiUrl === null ? `No UI URL was supplied. Do not open \`${controlBaseUrl}\` or \`${agentBaseUrl}\` as a page; use the MCP Provider Test tools and log/session resources, or ask the operator for a standalone UI URL.` : `The operator supplied the explicit composed or standalone UI URL \`${uiUrl}\`. Use that URL for browser steps; do not substitute the control or agent Base URL.`;
|
|
16583
16878
|
return `---
|
|
16584
16879
|
name: agent-inspector-onboard
|
|
16585
16880
|
description: Guide Codex users through connecting Agent Inspector v${version2} as a local MCP server.
|
|
@@ -16592,7 +16887,13 @@ metadata:
|
|
|
16592
16887
|
|
|
16593
16888
|
Use this skill when the user wants to set up Agent Inspector inside Codex, connect the Agent Inspector MCP server, inspect captured model traffic from Codex, or turn Inspector sessions into reviewable knowledge candidates.
|
|
16594
16889
|
|
|
16595
|
-
|
|
16890
|
+
The selected deployment has these explicit surfaces:
|
|
16891
|
+
|
|
16892
|
+
- UI URL: ${selectedUi}
|
|
16893
|
+
- Control Base URL: \`${controlBaseUrl}\`
|
|
16894
|
+
- Agent Base URL: \`${agentBaseUrl}\`
|
|
16895
|
+
|
|
16896
|
+
Composed mode may use one URL for all three, but backend-only mode and separately hosted UI deployments do not require UI and backend to share a process, origin, or port. The selected MCP endpoint is:
|
|
16596
16897
|
|
|
16597
16898
|
\`\`\`text
|
|
16598
16899
|
${mcpUrl}
|
|
@@ -16610,6 +16911,8 @@ Do not configure both transports in the same MCP client, or the client may show
|
|
|
16610
16911
|
|
|
16611
16912
|
Keep this setup local and explicit. Agent Inspector MCP tools can read captured requests and provider configuration, so only wire it into trusted local Codex environments.
|
|
16612
16913
|
|
|
16914
|
+
The backend publishes token-free runtime discovery at \`${runtimeUrl}\`. Treat \`backend-only\` as healthy when REST, proxy, and MCP capabilities are present.
|
|
16915
|
+
|
|
16613
16916
|
Agent Inspector starts in **Simple mode** by default. Keep Simple mode for everyday capture: requests, responses, tools, timing, and token usage stay available without retaining heavier raw headers, raw response, or detailed SSE chunk artifacts. Use **Full mode** only when the user is debugging provider headers, raw response payloads, or SSE stream details.
|
|
16614
16917
|
|
|
16615
16918
|
---
|
|
@@ -16652,12 +16955,14 @@ ${startExplanation}
|
|
|
16652
16955
|
\`\`\`powershell
|
|
16653
16956
|
${startCommand}
|
|
16654
16957
|
Invoke-RestMethod -Uri "${healthUrl}" -TimeoutSec 3
|
|
16958
|
+
Invoke-RestMethod -Uri "${runtimeUrl}" -TimeoutSec 3 | Select-Object deploymentMode, capabilities
|
|
16655
16959
|
Invoke-RestMethod -Uri "${configUrl}" -TimeoutSec 3 | Select-Object captureMode
|
|
16656
16960
|
\`\`\`
|
|
16657
16961
|
|
|
16658
16962
|
\`\`\`bash
|
|
16659
16963
|
${startCommand}
|
|
16660
16964
|
curl -fsS "${healthUrl}"
|
|
16965
|
+
curl -fsS "${runtimeUrl}"
|
|
16661
16966
|
curl -fsS "${configUrl}" | grep -o '"captureMode":"[^"]*"'
|
|
16662
16967
|
\`\`\`
|
|
16663
16968
|
|
|
@@ -16701,7 +17006,7 @@ When editing:
|
|
|
16701
17006
|
- Keep existing \`mcp_servers.*\`, \`projects.*\`, profiles, sandbox, approval, and model settings intact.
|
|
16702
17007
|
- Remove any duplicate Inspector MCP entry that points to the same \`/api/mcp\` endpoint or runs the same \`agent-inspector-mcp stdio\` bridge under another name.
|
|
16703
17008
|
- Do not write provider API keys into Codex config.
|
|
16704
|
-
- Do not expose
|
|
17009
|
+
- Do not expose a raw Inspector listener to untrusted networks. For remote use, require the operator's authenticated same-origin gateway and keep control credentials in environment variables rather than generated config.
|
|
16705
17010
|
|
|
16706
17011
|
---
|
|
16707
17012
|
|
|
@@ -16782,14 +17087,18 @@ Inspector, \`localhost\` points at the tool's own environment. Replace \`localho
|
|
|
16782
17087
|
address reachable from that environment, then verify the network/firewall path before debugging
|
|
16783
17088
|
provider credentials.
|
|
16784
17089
|
|
|
16785
|
-
For a Provider Test memory probe,
|
|
17090
|
+
For a Provider Test memory probe, first read \`${runtimeUrl}\`. ${providerTestUiGuidance}
|
|
17091
|
+
|
|
17092
|
+
When an explicit UI URL is available:
|
|
16786
17093
|
|
|
16787
|
-
1. Open
|
|
17094
|
+
1. Open the supplied UI URL.
|
|
16788
17095
|
2. Go to Settings -> Providers.
|
|
16789
17096
|
3. Add or edit a Provider.
|
|
16790
17097
|
4. Run Test.
|
|
16791
17098
|
5. Check the \`provider-test\` session.
|
|
16792
17099
|
|
|
17100
|
+
If discovery reports \`backend-only\`, do not fail onboarding. A separately hosted UI may still exist only when its URL was explicitly supplied; otherwise use the MCP Provider Test tools and log/session resources. Proxy, REST, health, and MCP remain fully usable without UI files.
|
|
17101
|
+
|
|
16793
17102
|
Then ask Codex:
|
|
16794
17103
|
|
|
16795
17104
|
\`\`\`text
|
|
@@ -16823,35 +17132,43 @@ var init_codex_skill_onboard = __esm({
|
|
|
16823
17132
|
|
|
16824
17133
|
// src/cli/templates/skill-onboard.ts
|
|
16825
17134
|
function renderSkillOnboard(ctx) {
|
|
16826
|
-
const { version: version2, port,
|
|
17135
|
+
const { version: version2, port, detectedSummary } = ctx;
|
|
17136
|
+
const controlBaseUrl = ctx.controlBaseUrl ?? ctx.baseUrl;
|
|
17137
|
+
const agentBaseUrl = ctx.agentBaseUrl ?? ctx.baseUrl;
|
|
17138
|
+
const uiUrl = ctx.uiUrl === void 0 ? ctx.baseUrl : ctx.uiUrl;
|
|
16827
17139
|
const mcpName = ctx.mcpName ?? "agent-inspector";
|
|
16828
17140
|
const targetKind = ctx.targetKind ?? "default";
|
|
16829
17141
|
const targetArguments = ctx.onboardTargetArguments ?? "";
|
|
16830
17142
|
const instanceName = ctx.instanceName ?? null;
|
|
16831
|
-
const healthUrl = `${
|
|
16832
|
-
const
|
|
16833
|
-
const
|
|
16834
|
-
const
|
|
16835
|
-
const
|
|
16836
|
-
const
|
|
17143
|
+
const healthUrl = `${controlBaseUrl}/api/health`;
|
|
17144
|
+
const runtimeUrl = `${controlBaseUrl}/api/runtime`;
|
|
17145
|
+
const configUrl = `${controlBaseUrl}/api/config`;
|
|
17146
|
+
const anthropicProxyUrl = `${agentBaseUrl}/proxy`;
|
|
17147
|
+
const openAiProxyUrl = `${agentBaseUrl}/proxy/v1`;
|
|
17148
|
+
const mcpUrl = `${agentBaseUrl}/api/mcp`;
|
|
17149
|
+
const logsUrl = `${controlBaseUrl}/api/logs`;
|
|
17150
|
+
const providersUrl = `${controlBaseUrl}/api/providers`;
|
|
16837
17151
|
const statusCommand = `agent-inspector onboard --status${targetArguments}`;
|
|
16838
17152
|
const refreshCommand = `agent-inspector onboard --force${targetArguments}`;
|
|
16839
|
-
const startCommand = targetKind === "instance" && instanceName !== null ? `agent-inspector instance start ${instanceName}` : targetKind === "base-url" ? `# Explicit target ${
|
|
17153
|
+
const startCommand = targetKind === "instance" && instanceName !== null ? `agent-inspector instance start ${instanceName}` : targetKind === "base-url" ? `# Explicit control target ${controlBaseUrl}: do not start a replacement local runtime.` : "agent-inspector --mode simple --background --no-open";
|
|
16840
17154
|
const fullModeCommand = targetKind === "instance" && instanceName !== null ? `agent-inspector instance restart ${instanceName} --mode full` : targetKind === "base-url" ? "ask the target operator to enable Full capture mode" : "agent-inspector --mode full --force-restart";
|
|
16841
17155
|
const bashStartSnippet = targetKind === "base-url" ? startCommand : `${startCommand} > /tmp/agent-inspector.log 2>&1`;
|
|
16842
17156
|
const windowsStartArguments = targetKind === "instance" && instanceName !== null ? `instance start ${instanceName}` : "--mode simple --background --no-open";
|
|
16843
17157
|
const powershellStartSnippet = targetKind === "base-url" ? startCommand : `$found = Get-Command agent-inspector -ErrorAction SilentlyContinue
|
|
16844
17158
|
if (-not $found) { throw 'agent-inspector is not on PATH' }
|
|
16845
17159
|
& $found.Source ${windowsStartArguments}`;
|
|
16846
|
-
const startExplanation = targetKind === "base-url" ? `The selected Inspector target
|
|
16847
|
-
const stopSnippet = targetKind === "instance" && instanceName !== null ? `agent-inspector instance stop ${instanceName}` : targetKind === "base-url" ? `# Ask the operator of ${
|
|
17160
|
+
const startExplanation = targetKind === "base-url" ? `The selected Inspector target uses control Base URL \`${controlBaseUrl}\` and agent Base URL \`${agentBaseUrl}\`. Do not launch a default local runtime as a substitute; verify the selected target and ask its operator for help if it is unavailable.` : `Start or reuse the selected Inspector backend. Its public control and agent endpoints preserve the selected Base Path; a Web UI may be composed at the same URL, hosted separately, or intentionally absent in backend-only mode. Use \`${fullModeCommand}\` only when the user needs raw headers, raw response, or SSE chunks.`;
|
|
17161
|
+
const stopSnippet = targetKind === "instance" && instanceName !== null ? `agent-inspector instance stop ${instanceName}` : targetKind === "base-url" ? `# Ask the operator of ${controlBaseUrl} to stop the selected runtime.` : `# Unix / macOS
|
|
16848
17162
|
lsof -ti:${String(port)} | xargs -r kill -9
|
|
16849
17163
|
|
|
16850
17164
|
# Windows PowerShell
|
|
16851
17165
|
Get-NetTCPConnection -LocalPort ${String(port)} | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force }`;
|
|
17166
|
+
const providerScopeGuidance = targetKind === "default" ? "The unmanaged default target owns the local Agent Inspector data directory used by the file checks below." : `The selected target is not the unmanaged default data directory. Do not inspect or edit local ~/.agent-inspector Provider files as a substitute. Query \`${providersUrl}\`, use the selected target's MCP Provider tools with explicit approval, or ask its operator to configure Providers. Skip local-file commands below.`;
|
|
17167
|
+
const selectedUi = uiUrl === null ? "not supplied (do not infer one from a backend URL)" : `\`${uiUrl}\``;
|
|
17168
|
+
const uiTour = uiUrl === null ? "not supplied; continue with MCP/REST, and ask the operator for a standalone UI URL only if the user needs one" : `\`${uiUrl}\` \u2014 use this explicit composed or standalone browser URL; do not replace it with the control or agent Base URL`;
|
|
16852
17169
|
return `---
|
|
16853
17170
|
name: agent-inspector-onboard
|
|
16854
|
-
description: Guided setup for Agent Inspector v${version2}: start the
|
|
17171
|
+
description: Guided setup for Agent Inspector v${version2}: start or select the backend, wire your AI coding tool, and capture your first request with or without a Web UI.
|
|
16855
17172
|
metadata:
|
|
16856
17173
|
author: agent-inspector
|
|
16857
17174
|
version: ${version2}
|
|
@@ -16859,12 +17176,16 @@ metadata:
|
|
|
16859
17176
|
|
|
16860
17177
|
# Agent Inspector onboard
|
|
16861
17178
|
|
|
16862
|
-
Guide the user from "I just installed Agent Inspector" to "
|
|
17179
|
+
Guide the user from "I just installed Agent Inspector" to "my coding agent can use the proxy, REST API, and MCP surface". The backend works in composed and headless deployments; treat the Web UI as an optional presentation surface.
|
|
16863
17180
|
|
|
16864
17181
|
Environment detected by the installer:
|
|
16865
17182
|
${detectedSummary || " (no known AI tool detected \u2014 the user can still use the generic curl example in Phase 4)"}
|
|
16866
17183
|
|
|
16867
|
-
Selected Inspector
|
|
17184
|
+
Selected Inspector surfaces:
|
|
17185
|
+
- UI URL: ${selectedUi}
|
|
17186
|
+
- Control Base URL: \`${controlBaseUrl}\`
|
|
17187
|
+
- Agent Base URL: \`${agentBaseUrl}\`
|
|
17188
|
+
- MCP key: \`${mcpName}\`
|
|
16868
17189
|
|
|
16869
17190
|
> **PAUSE protocol.** Every \`**PAUSE**\` marker in this skill is a real stop.
|
|
16870
17191
|
> Use the \`AskUserQuestion\` tool to actually wait for the user before
|
|
@@ -16892,9 +17213,11 @@ ${statusCommand}
|
|
|
16892
17213
|
|
|
16893
17214
|
**DO:** Then probe the three pieces of state this skill touches. Use targeted checks \u2014 do **not** read large JSON files into the conversation.
|
|
16894
17215
|
|
|
17216
|
+
**DO:** ${providerScopeGuidance}
|
|
17217
|
+
|
|
16895
17218
|
\`\`\`bash
|
|
16896
|
-
# 1. Is the
|
|
16897
|
-
curl -fsS "${healthUrl}" 2>/dev/null && echo "
|
|
17219
|
+
# 1. Is the backend already up?
|
|
17220
|
+
curl -fsS "${healthUrl}" 2>/dev/null && echo "BACKEND: up" || echo "BACKEND: down"
|
|
16898
17221
|
|
|
16899
17222
|
# 2. Does the provider config have a real provider key?
|
|
16900
17223
|
agent_inspector_data_dir() {
|
|
@@ -16960,12 +17283,12 @@ $dataDir = Resolve-AgentInspectorDataDir
|
|
|
16960
17283
|
$providers = Join-Path $dataDir 'providers.json'
|
|
16961
17284
|
$legacyConfig = Join-Path $dataDir 'config.json'
|
|
16962
17285
|
|
|
16963
|
-
# 1. Is the
|
|
17286
|
+
# 1. Is the backend already up?
|
|
16964
17287
|
try {
|
|
16965
17288
|
$null = Invoke-RestMethod -Uri "${healthUrl}" -TimeoutSec 2 -ErrorAction Stop
|
|
16966
|
-
Write-Host '
|
|
17289
|
+
Write-Host 'BACKEND: up'
|
|
16967
17290
|
} catch {
|
|
16968
|
-
Write-Host '
|
|
17291
|
+
Write-Host 'BACKEND: down'
|
|
16969
17292
|
}
|
|
16970
17293
|
|
|
16971
17294
|
# 2. Does the config have a real provider key?
|
|
@@ -17035,7 +17358,7 @@ node -e "const fs=require('node:fs'),p=require('node:path');const names=process.
|
|
|
17035
17358
|
\`\`\`
|
|
17036
17359
|
## Welcome to Agent Inspector!
|
|
17037
17360
|
|
|
17038
|
-
Agent Inspector is an agent observability and knowledge-capture platform for AI coding tools.
|
|
17361
|
+
Agent Inspector is an agent observability and knowledge-capture platform for AI coding tools. Its backend owns the transparent model proxy, REST/SSE APIs, capture storage, and MCP server. The default CLI composes a Web UI at the same public Base URL, while \`--backend-only\` keeps every Coding Agent surface available without serving UI files.
|
|
17039
17362
|
|
|
17040
17363
|
Agent Inspector starts in **Simple mode** by default. Simple mode is the right habit for everyday use: it keeps requests, responses, tools, timing, and token usage while avoiding heavier raw headers, raw response, and detailed SSE chunk artifacts. Use **Full mode** only when the user is debugging provider headers, raw response payloads, or SSE stream details.
|
|
17041
17364
|
|
|
@@ -17055,6 +17378,8 @@ Ready? Let's start with the provider.
|
|
|
17055
17378
|
|
|
17056
17379
|
## Phase 2: Provider setup
|
|
17057
17380
|
|
|
17381
|
+
**DO:** ${providerScopeGuidance}
|
|
17382
|
+
|
|
17058
17383
|
**EXPLAIN:** "A 'provider' is an upstream LLM endpoint \u2014 Anthropic, OpenAI, MiniMax, etc. agent-inspector routes each request to the right upstream based on the model name. You need at least one provider configured for the proxy to forward traffic."
|
|
17059
17384
|
|
|
17060
17385
|
**DO:** First, re-check whether \`<dataDir>/providers.json\` already has a real key (Phase 0 may have raced with a manual edit). Use the same data-dir resolution and \`grep -qE '"apiKey":"sk-'\` (bash) or \`Select-String\` (PowerShell) check from Phase 0. If a real key is present, skip the rest of this phase.
|
|
@@ -17155,7 +17480,7 @@ $json | Set-Content -Path $file -Encoding UTF8
|
|
|
17155
17480
|
|
|
17156
17481
|
**EXPLAIN:** "${startExplanation}"
|
|
17157
17482
|
|
|
17158
|
-
**DO:** Skip this phase entirely if the Phase 0 health check already reported \`
|
|
17483
|
+
**DO:** Skip this phase entirely if the Phase 0 health check already reported \`BACKEND: up\` and the user opted to skip done phases.
|
|
17159
17484
|
|
|
17160
17485
|
**DO:** Otherwise, use the selected target command below. An explicit wrapper Base URL is connection-only and must not cause a replacement local runtime to start.
|
|
17161
17486
|
|
|
@@ -17168,7 +17493,7 @@ ${bashStartSnippet}
|
|
|
17168
17493
|
${powershellStartSnippet}
|
|
17169
17494
|
\`\`\`
|
|
17170
17495
|
|
|
17171
|
-
Then wait for the
|
|
17496
|
+
Then wait for the selected backend health URL to be ready:
|
|
17172
17497
|
|
|
17173
17498
|
\`\`\`bash
|
|
17174
17499
|
for i in $(seq 1 20); do
|
|
@@ -17177,12 +17502,18 @@ for i in $(seq 1 20); do
|
|
|
17177
17502
|
done
|
|
17178
17503
|
\`\`\`
|
|
17179
17504
|
|
|
17180
|
-
**DO:** Hit the health endpoint to confirm the
|
|
17505
|
+
**DO:** Hit the health endpoint to confirm the backend is alive:
|
|
17181
17506
|
|
|
17182
17507
|
\`\`\`bash
|
|
17183
17508
|
curl -sS "${healthUrl}"
|
|
17184
17509
|
\`\`\`
|
|
17185
17510
|
|
|
17511
|
+
**DO:** Read the token-free runtime discovery document. Report \`deploymentMode\` and capabilities; do not treat \`backend-only\` as a failure or infer that a UI is present.
|
|
17512
|
+
|
|
17513
|
+
\`\`\`bash
|
|
17514
|
+
curl -sS "${runtimeUrl}"
|
|
17515
|
+
\`\`\`
|
|
17516
|
+
|
|
17186
17517
|
**DO:** Read \`/api/config\` and tell the user which capture mode is active. If deeper capture is required, offer \`${fullModeCommand}\`.
|
|
17187
17518
|
|
|
17188
17519
|
\`\`\`bash
|
|
@@ -17322,10 +17653,11 @@ done
|
|
|
17322
17653
|
|
|
17323
17654
|
## Phase 6: Tour & wrap
|
|
17324
17655
|
|
|
17325
|
-
**EXPLAIN:** "Everything's working. Here's the cheat sheet for the
|
|
17656
|
+
**EXPLAIN:** "Everything's working. Here's the cheat sheet for the backend surfaces and the optional Web UI:"
|
|
17326
17657
|
|
|
17327
|
-
- **
|
|
17328
|
-
- **
|
|
17658
|
+
- **Runtime discovery**: \`${runtimeUrl}\` \u2014 token-free deployment mode, UI Base Path, control/agent Base URLs, and capabilities.
|
|
17659
|
+
- **Web UI**: ${uiTour}.
|
|
17660
|
+
- **MCP server**: \`${mcpUrl}\` \u2014 connect from the coding agent to query logs, replay, and test providers; it does not require a Web UI process.
|
|
17329
17661
|
- **REST API**: \`/api/logs\`, \`/api/sessions\`, \`/api/providers\` \u2014 for scripting and shell-based inspection.
|
|
17330
17662
|
- **Stop the proxy**:
|
|
17331
17663
|
|
|
@@ -17336,7 +17668,7 @@ ${stopSnippet}
|
|
|
17336
17668
|
- **Re-run onboard**: \`${refreshCommand}\` refreshes this skill.
|
|
17337
17669
|
- **Check onboard files**: \`${statusCommand}\` shows whether generated skills are
|
|
17338
17670
|
current, outdated, missing, newer, or custom.
|
|
17339
|
-
- **Full docs**: see the project README
|
|
17671
|
+
- **Full docs**: see the project README; do not depend on a UI footer being available.
|
|
17340
17672
|
|
|
17341
17673
|
> **PAUSE** \u2014 use \`AskUserQuestion\` with header \`All set?\` and options \`["All set, I'm done", "Wait, I want to revisit a phase"]\`. Wait for the answer.
|
|
17342
17674
|
|
|
@@ -17372,11 +17704,11 @@ import {
|
|
|
17372
17704
|
existsSync as existsSync5,
|
|
17373
17705
|
readFileSync as readFileSync2,
|
|
17374
17706
|
unlinkSync,
|
|
17375
|
-
readdirSync as
|
|
17707
|
+
readdirSync as readdirSync4,
|
|
17376
17708
|
rmdirSync
|
|
17377
17709
|
} from "node:fs";
|
|
17378
17710
|
import { homedir as homedir3 } from "node:os";
|
|
17379
|
-
import { dirname
|
|
17711
|
+
import { dirname, join as join7 } from "node:path";
|
|
17380
17712
|
import { fileURLToPath } from "node:url";
|
|
17381
17713
|
function actionForStatus(label, state) {
|
|
17382
17714
|
switch (state) {
|
|
@@ -17422,6 +17754,9 @@ function parseFlags(argv) {
|
|
|
17422
17754
|
mimoMcpUrl: null,
|
|
17423
17755
|
instanceName: null,
|
|
17424
17756
|
baseUrl: null,
|
|
17757
|
+
controlBaseUrl: null,
|
|
17758
|
+
agentBaseUrl: null,
|
|
17759
|
+
uiUrl: void 0,
|
|
17425
17760
|
mcpName: null,
|
|
17426
17761
|
uninstall: false,
|
|
17427
17762
|
status: false,
|
|
@@ -17572,6 +17907,39 @@ function parseFlags(argv) {
|
|
|
17572
17907
|
i++;
|
|
17573
17908
|
break;
|
|
17574
17909
|
}
|
|
17910
|
+
case "--control-base-url": {
|
|
17911
|
+
const next = argv[i + 1];
|
|
17912
|
+
if (next === void 0 || next.trim().length === 0) {
|
|
17913
|
+
process.stderr.write("agent-inspector onboard: --control-base-url requires a URL\n");
|
|
17914
|
+
process.exit(2);
|
|
17915
|
+
}
|
|
17916
|
+
flags.controlBaseUrl = next;
|
|
17917
|
+
i++;
|
|
17918
|
+
break;
|
|
17919
|
+
}
|
|
17920
|
+
case "--agent-base-url": {
|
|
17921
|
+
const next = argv[i + 1];
|
|
17922
|
+
if (next === void 0 || next.trim().length === 0) {
|
|
17923
|
+
process.stderr.write("agent-inspector onboard: --agent-base-url requires a URL\n");
|
|
17924
|
+
process.exit(2);
|
|
17925
|
+
}
|
|
17926
|
+
flags.agentBaseUrl = next;
|
|
17927
|
+
i++;
|
|
17928
|
+
break;
|
|
17929
|
+
}
|
|
17930
|
+
case "--ui-url": {
|
|
17931
|
+
const next = argv[i + 1];
|
|
17932
|
+
if (next === void 0 || next.trim().length === 0) {
|
|
17933
|
+
process.stderr.write("agent-inspector onboard: --ui-url requires a URL\n");
|
|
17934
|
+
process.exit(2);
|
|
17935
|
+
}
|
|
17936
|
+
flags.uiUrl = next;
|
|
17937
|
+
i++;
|
|
17938
|
+
break;
|
|
17939
|
+
}
|
|
17940
|
+
case "--no-ui":
|
|
17941
|
+
flags.uiUrl = null;
|
|
17942
|
+
break;
|
|
17575
17943
|
case "--mcp-name": {
|
|
17576
17944
|
const next = argv[i + 1];
|
|
17577
17945
|
if (next === void 0 || next.trim().length === 0) {
|
|
@@ -17652,7 +18020,11 @@ Options:
|
|
|
17652
18020
|
MiMo Code MCP transport to write (default: local)
|
|
17653
18021
|
--mimo-mcp-url <url> Agent Inspector MCP endpoint for MiMo Code
|
|
17654
18022
|
--instance <name> Target a registered named Inspector instance
|
|
17655
|
-
--base-url <url> Target an explicit credential-free
|
|
18023
|
+
--base-url <url> Target an explicit credential-free backend control/agent Base URL
|
|
18024
|
+
--control-base-url <url> Control API Base URL for a separated deployment
|
|
18025
|
+
--agent-base-url <url> Proxy and MCP Base URL for Coding Agents
|
|
18026
|
+
--ui-url <url> Browser UI URL for a separated deployment
|
|
18027
|
+
--no-ui Declare that the explicit target has no browser UI
|
|
17656
18028
|
--mcp-name <name> MCP config key (named default: agent-inspector-<instance>)
|
|
17657
18029
|
--uninstall Remove generated files whose version matches this package
|
|
17658
18030
|
--status Show installed onboarding file versions and suggested action
|
|
@@ -17668,15 +18040,15 @@ Exit codes:
|
|
|
17668
18040
|
`);
|
|
17669
18041
|
}
|
|
17670
18042
|
function resolveTargets(flags) {
|
|
17671
|
-
const claudeRoot = flags.skillDir ??
|
|
17672
|
-
const claudeSkillDir =
|
|
17673
|
-
const claudeCommandsDir =
|
|
17674
|
-
const codexRoot = process.env["CODEX_HOME"] ??
|
|
17675
|
-
const codexSkillsDir = flags.codexSkillDir ??
|
|
18043
|
+
const claudeRoot = flags.skillDir ?? join7(homedir3(), ".claude");
|
|
18044
|
+
const claudeSkillDir = join7(claudeRoot, "skills", SKILL_DIR_NAME);
|
|
18045
|
+
const claudeCommandsDir = join7(claudeRoot, "commands");
|
|
18046
|
+
const codexRoot = process.env["CODEX_HOME"] ?? join7(homedir3(), ".codex");
|
|
18047
|
+
const codexSkillsDir = flags.codexSkillDir ?? join7(codexRoot, "skills");
|
|
17676
18048
|
return {
|
|
17677
|
-
claudeSkillFile:
|
|
17678
|
-
claudeCommandFile:
|
|
17679
|
-
codexSkillFile:
|
|
18049
|
+
claudeSkillFile: join7(claudeSkillDir, SKILL_FILE_NAME),
|
|
18050
|
+
claudeCommandFile: join7(claudeCommandsDir, COMMAND_FILE_NAME),
|
|
18051
|
+
codexSkillFile: join7(codexSkillsDir, SKILL_DIR_NAME, SKILL_FILE_NAME)
|
|
17680
18052
|
};
|
|
17681
18053
|
}
|
|
17682
18054
|
function isObject2(value) {
|
|
@@ -17695,32 +18067,32 @@ function resolveOpenCodeConfigPath(flags) {
|
|
|
17695
18067
|
if (flags.opencodeConfig !== null) {
|
|
17696
18068
|
return flags.opencodeConfig;
|
|
17697
18069
|
}
|
|
17698
|
-
const configDir =
|
|
17699
|
-
const jsoncPath =
|
|
18070
|
+
const configDir = join7(homedir3(), ".config", "opencode");
|
|
18071
|
+
const jsoncPath = join7(configDir, "opencode.jsonc");
|
|
17700
18072
|
if (existsSync5(jsoncPath)) {
|
|
17701
18073
|
return jsoncPath;
|
|
17702
18074
|
}
|
|
17703
|
-
return
|
|
18075
|
+
return join7(configDir, "opencode.json");
|
|
17704
18076
|
}
|
|
17705
18077
|
function resolveMiMoConfigPath(flags) {
|
|
17706
18078
|
if (flags.mimoConfig !== null) {
|
|
17707
18079
|
return flags.mimoConfig;
|
|
17708
18080
|
}
|
|
17709
|
-
const configDir =
|
|
17710
|
-
const jsoncPath =
|
|
18081
|
+
const configDir = join7(homedir3(), ".config", "mimocode");
|
|
18082
|
+
const jsoncPath = join7(configDir, "mimocode.jsonc");
|
|
17711
18083
|
if (existsSync5(jsoncPath)) {
|
|
17712
18084
|
return jsoncPath;
|
|
17713
18085
|
}
|
|
17714
|
-
const jsonPath =
|
|
18086
|
+
const jsonPath = join7(configDir, "mimocode.json");
|
|
17715
18087
|
if (existsSync5(jsonPath)) {
|
|
17716
18088
|
return jsonPath;
|
|
17717
18089
|
}
|
|
17718
|
-
const legacyDir =
|
|
17719
|
-
const legacyJsoncPath =
|
|
18090
|
+
const legacyDir = join7(homedir3(), ".mimocode");
|
|
18091
|
+
const legacyJsoncPath = join7(legacyDir, "config.jsonc");
|
|
17720
18092
|
if (existsSync5(legacyJsoncPath)) {
|
|
17721
18093
|
return legacyJsoncPath;
|
|
17722
18094
|
}
|
|
17723
|
-
const legacyJsonPath =
|
|
18095
|
+
const legacyJsonPath = join7(legacyDir, "config.json");
|
|
17724
18096
|
if (existsSync5(legacyJsonPath)) {
|
|
17725
18097
|
return legacyJsonPath;
|
|
17726
18098
|
}
|
|
@@ -17731,7 +18103,18 @@ function onboardSelectionArguments(flags, target) {
|
|
|
17731
18103
|
if (target.kind === "instance" && target.instanceName !== null) {
|
|
17732
18104
|
args.push("--instance", target.instanceName);
|
|
17733
18105
|
} else if (target.kind === "base-url") {
|
|
17734
|
-
|
|
18106
|
+
const usedSharedBaseUrl = flags.baseUrl !== null && flags.controlBaseUrl === null && flags.agentBaseUrl === null && flags.uiUrl === void 0;
|
|
18107
|
+
if (usedSharedBaseUrl) {
|
|
18108
|
+
args.push("--base-url", target.baseUrl);
|
|
18109
|
+
} else {
|
|
18110
|
+
args.push("--control-base-url", target.controlBaseUrl);
|
|
18111
|
+
args.push("--agent-base-url", target.agentBaseUrl);
|
|
18112
|
+
if (target.uiUrl === null) {
|
|
18113
|
+
args.push("--no-ui");
|
|
18114
|
+
} else {
|
|
18115
|
+
args.push("--ui-url", target.uiUrl);
|
|
18116
|
+
}
|
|
18117
|
+
}
|
|
17735
18118
|
}
|
|
17736
18119
|
if (flags.mcpName !== null) args.push("--mcp-name", target.mcpName);
|
|
17737
18120
|
return args.length === 0 ? "" : ` ${args.join(" ")}`;
|
|
@@ -18114,8 +18497,8 @@ function buildDetectedSummary() {
|
|
|
18114
18497
|
}
|
|
18115
18498
|
function readPackageVersion() {
|
|
18116
18499
|
const packageJsonPaths = [
|
|
18117
|
-
|
|
18118
|
-
|
|
18500
|
+
join7(__dirname, "..", "package.json"),
|
|
18501
|
+
join7(__dirname, "..", "..", "package.json")
|
|
18119
18502
|
];
|
|
18120
18503
|
try {
|
|
18121
18504
|
for (const packageJsonPath of packageJsonPaths) {
|
|
@@ -18289,6 +18672,9 @@ function buildPlannedFiles(flags, targets, version2, onboardTarget) {
|
|
|
18289
18672
|
version: version2,
|
|
18290
18673
|
port: onboardTarget.port,
|
|
18291
18674
|
baseUrl: onboardTarget.baseUrl,
|
|
18675
|
+
controlBaseUrl: onboardTarget.controlBaseUrl,
|
|
18676
|
+
agentBaseUrl: onboardTarget.agentBaseUrl,
|
|
18677
|
+
uiUrl: onboardTarget.uiUrl,
|
|
18292
18678
|
mcpName: onboardTarget.mcpName,
|
|
18293
18679
|
targetKind: onboardTarget.kind,
|
|
18294
18680
|
instanceName: onboardTarget.instanceName,
|
|
@@ -18300,6 +18686,9 @@ function buildPlannedFiles(flags, targets, version2, onboardTarget) {
|
|
|
18300
18686
|
version: version2,
|
|
18301
18687
|
port: onboardTarget.port,
|
|
18302
18688
|
baseUrl: onboardTarget.baseUrl,
|
|
18689
|
+
controlBaseUrl: onboardTarget.controlBaseUrl,
|
|
18690
|
+
agentBaseUrl: onboardTarget.agentBaseUrl,
|
|
18691
|
+
uiUrl: onboardTarget.uiUrl,
|
|
18303
18692
|
mcpName: onboardTarget.mcpName,
|
|
18304
18693
|
targetKind: onboardTarget.kind,
|
|
18305
18694
|
instanceName: onboardTarget.instanceName,
|
|
@@ -18345,7 +18734,7 @@ function shouldUninstall(file2, currentVersion) {
|
|
|
18345
18734
|
}
|
|
18346
18735
|
function removeEmptyDirectory(path2) {
|
|
18347
18736
|
try {
|
|
18348
|
-
if (
|
|
18737
|
+
if (readdirSync4(path2).length === 0) {
|
|
18349
18738
|
rmdirSync(path2);
|
|
18350
18739
|
}
|
|
18351
18740
|
} catch {
|
|
@@ -18393,13 +18782,13 @@ No files were removed.
|
|
|
18393
18782
|
try {
|
|
18394
18783
|
for (const file2 of filesToRemove) {
|
|
18395
18784
|
unlinkSync(file2.path);
|
|
18396
|
-
removeEmptyDirectory(
|
|
18785
|
+
removeEmptyDirectory(dirname(file2.path));
|
|
18397
18786
|
process.stdout.write(`Removed ${file2.label}: ${file2.path}
|
|
18398
18787
|
`);
|
|
18399
18788
|
}
|
|
18400
18789
|
for (const plan of agentPlans) {
|
|
18401
18790
|
if (plan.shouldRemove && plan.body !== null) {
|
|
18402
|
-
mkdirSync3(
|
|
18791
|
+
mkdirSync3(dirname(plan.status.path), { recursive: true });
|
|
18403
18792
|
writeFileSync2(plan.status.path, plan.body, "utf8");
|
|
18404
18793
|
process.stdout.write(`Removed ${plan.status.label} entry from: ${plan.status.path}
|
|
18405
18794
|
`);
|
|
@@ -18516,6 +18905,9 @@ function runOnboardSync(argv) {
|
|
|
18516
18905
|
const resolvedTarget = resolveOnboardTarget({
|
|
18517
18906
|
instanceName: flags.instanceName,
|
|
18518
18907
|
baseUrl: flags.baseUrl,
|
|
18908
|
+
controlBaseUrl: flags.controlBaseUrl,
|
|
18909
|
+
agentBaseUrl: flags.agentBaseUrl,
|
|
18910
|
+
uiUrl: flags.uiUrl,
|
|
18519
18911
|
mcpName: flags.mcpName
|
|
18520
18912
|
});
|
|
18521
18913
|
if (!resolvedTarget.ok) {
|
|
@@ -18636,12 +19028,12 @@ No files were written.
|
|
|
18636
19028
|
}
|
|
18637
19029
|
try {
|
|
18638
19030
|
for (const file2 of filesToWrite) {
|
|
18639
|
-
mkdirSync3(
|
|
19031
|
+
mkdirSync3(dirname(file2.path), { recursive: true });
|
|
18640
19032
|
writeFileSync2(file2.path, file2.body, "utf8");
|
|
18641
19033
|
}
|
|
18642
19034
|
for (const plan of agentPlansToWrite) {
|
|
18643
19035
|
if (plan.body !== null) {
|
|
18644
|
-
mkdirSync3(
|
|
19036
|
+
mkdirSync3(dirname(plan.status.path), { recursive: true });
|
|
18645
19037
|
writeFileSync2(plan.status.path, plan.body, "utf8");
|
|
18646
19038
|
}
|
|
18647
19039
|
}
|
|
@@ -18701,7 +19093,7 @@ var init_onboard = __esm({
|
|
|
18701
19093
|
init_codex_skill_onboard();
|
|
18702
19094
|
init_skill_onboard();
|
|
18703
19095
|
__filename = fileURLToPath(import.meta.url);
|
|
18704
|
-
__dirname =
|
|
19096
|
+
__dirname = dirname(__filename);
|
|
18705
19097
|
DEFAULT_PORT2 = 9527;
|
|
18706
19098
|
DEFAULT_ORIGIN2 = `http://localhost:${DEFAULT_PORT2}`;
|
|
18707
19099
|
DEFAULT_MCP_URL = appendBasePathToOrigin(
|
|
@@ -18730,8 +19122,8 @@ __export(doctor_exports, {
|
|
|
18730
19122
|
parseDoctorArgs: () => parseDoctorArgs,
|
|
18731
19123
|
runDoctor: () => runDoctor
|
|
18732
19124
|
});
|
|
18733
|
-
import { existsSync as existsSync6, readFileSync as readFileSync3, readdirSync as
|
|
18734
|
-
import { isAbsolute as isAbsolute4, join as
|
|
19125
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
|
|
19126
|
+
import { isAbsolute as isAbsolute4, join as join8, resolve as resolve4 } from "node:path";
|
|
18735
19127
|
import { createConnection } from "node:net";
|
|
18736
19128
|
function check2(name, severity, message, hint = "") {
|
|
18737
19129
|
return {
|
|
@@ -18834,7 +19226,7 @@ function providerCountFromJson(raw) {
|
|
|
18834
19226
|
}
|
|
18835
19227
|
}
|
|
18836
19228
|
function resolveMaybeRelative(base, value) {
|
|
18837
|
-
return isAbsolute4(value) ? value :
|
|
19229
|
+
return isAbsolute4(value) ? value : resolve4(base, value);
|
|
18838
19230
|
}
|
|
18839
19231
|
function resolveDefaultDataDir(env, platform, exists) {
|
|
18840
19232
|
return resolveDataDir(exists, env, platform);
|
|
@@ -18846,10 +19238,10 @@ function providerConfigCandidates(options, deps) {
|
|
|
18846
19238
|
}
|
|
18847
19239
|
if (options.configDir !== null) {
|
|
18848
19240
|
const dir = resolveMaybeRelative(deps.cwd, options.configDir);
|
|
18849
|
-
return [
|
|
19241
|
+
return [join8(dir, "providers.json"), join8(dir, "config.json")];
|
|
18850
19242
|
}
|
|
18851
19243
|
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
18852
|
-
return [
|
|
19244
|
+
return [join8(dataDir, "providers.json"), join8(dataDir, "config.json")];
|
|
18853
19245
|
}
|
|
18854
19246
|
function checkProviderConfig(options, deps) {
|
|
18855
19247
|
const providersJson = options.providersJson ?? deps.env["AGENT_INSPECTOR_PROVIDERS_JSON"] ?? null;
|
|
@@ -18895,58 +19287,71 @@ function checkProviderConfig(options, deps) {
|
|
|
18895
19287
|
"Provider config",
|
|
18896
19288
|
"warn",
|
|
18897
19289
|
`Provider config exists but no providers were found: ${candidate}`,
|
|
18898
|
-
"Add a provider
|
|
19290
|
+
"Add a provider through the control API/MCP, or use Settings when a UI is available."
|
|
18899
19291
|
);
|
|
18900
19292
|
}
|
|
18901
19293
|
return check2(
|
|
18902
19294
|
"Provider config",
|
|
18903
19295
|
"warn",
|
|
18904
19296
|
"No provider config was found.",
|
|
18905
|
-
"Add a provider
|
|
19297
|
+
"Add a provider through the control API/MCP, use Settings when a UI is available, or pass --providers for this check."
|
|
18906
19298
|
);
|
|
18907
19299
|
}
|
|
19300
|
+
function publicOriginForDoctor(port, env) {
|
|
19301
|
+
const configured = env["AGENT_INSPECTOR_PUBLIC_ORIGIN"];
|
|
19302
|
+
if (configured !== void 0 && configured.trim() === configured) {
|
|
19303
|
+
try {
|
|
19304
|
+
const parsed = new URL(configured);
|
|
19305
|
+
if ((parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username.length === 0 && parsed.password.length === 0 && parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0) {
|
|
19306
|
+
return parsed.origin;
|
|
19307
|
+
}
|
|
19308
|
+
} catch {
|
|
19309
|
+
}
|
|
19310
|
+
}
|
|
19311
|
+
return `http://localhost:${String(port)}`;
|
|
19312
|
+
}
|
|
18908
19313
|
async function checkProxyHealth(port, deps) {
|
|
18909
19314
|
const basePath = getConfiguredBasePath(deps.env);
|
|
18910
19315
|
const health = await deps.fetchHealth(port, basePath);
|
|
18911
|
-
const url2 =
|
|
19316
|
+
const url2 = publicOriginForDoctor(port, deps.env);
|
|
18912
19317
|
const proxyUrl = appendBasePathToOrigin(url2, "/proxy", basePath);
|
|
18913
19318
|
const healthPath = appendBasePathToOrigin("", "/api/health", basePath);
|
|
18914
19319
|
if (health.ok) {
|
|
18915
19320
|
return check2(
|
|
18916
|
-
"
|
|
19321
|
+
"Backend health",
|
|
18917
19322
|
"pass",
|
|
18918
|
-
`
|
|
18919
|
-
`
|
|
19323
|
+
`Agent Inspector backend is healthy at ${appendBasePathToOrigin(url2, "/", basePath)}.`,
|
|
19324
|
+
`Agent proxy URL: ${proxyUrl}. UI availability is not part of this backend check.`
|
|
18920
19325
|
);
|
|
18921
19326
|
}
|
|
18922
19327
|
if (await deps.isPortOpen(port)) {
|
|
18923
19328
|
const suffix = health.status === null ? "" : ` (HTTP ${health.status})`;
|
|
18924
19329
|
return check2(
|
|
18925
|
-
"
|
|
19330
|
+
"Backend health",
|
|
18926
19331
|
"fail",
|
|
18927
19332
|
`Port ${port} is accepting connections, but ${healthPath} is not healthy${suffix}.`,
|
|
18928
19333
|
"Stop that process, choose --port <n>, or start agent-inspector with --force-restart."
|
|
18929
19334
|
);
|
|
18930
19335
|
}
|
|
18931
19336
|
return check2(
|
|
18932
|
-
"
|
|
19337
|
+
"Backend health",
|
|
18933
19338
|
"warn",
|
|
18934
19339
|
`No process is listening on port ${port}.`,
|
|
18935
19340
|
"Start with `agent-inspector`, `agent-inspector --background`, or `bun run dev`."
|
|
18936
19341
|
);
|
|
18937
19342
|
}
|
|
18938
19343
|
function findProjectRoot(startDir, deps) {
|
|
18939
|
-
let current =
|
|
19344
|
+
let current = resolve4(startDir);
|
|
18940
19345
|
for (let i = 0; i < 6; i++) {
|
|
18941
|
-
if (deps.exists(
|
|
18942
|
-
const next =
|
|
19346
|
+
if (deps.exists(join8(current, "package.json"))) return current;
|
|
19347
|
+
const next = resolve4(current, "..");
|
|
18943
19348
|
if (next === current) return startDir;
|
|
18944
19349
|
current = next;
|
|
18945
19350
|
}
|
|
18946
19351
|
return startDir;
|
|
18947
19352
|
}
|
|
18948
19353
|
function checkPackage(rootDir, deps) {
|
|
18949
|
-
const packagePath =
|
|
19354
|
+
const packagePath = join8(rootDir, "package.json");
|
|
18950
19355
|
const text = deps.readText(packagePath);
|
|
18951
19356
|
if (text === null) {
|
|
18952
19357
|
return check2("Package metadata", "warn", "package.json was not found near the CLI bundle.");
|
|
@@ -18971,7 +19376,7 @@ function countFilesInDir(dir, deps) {
|
|
|
18971
19376
|
if (files === null) return null;
|
|
18972
19377
|
let count = 0;
|
|
18973
19378
|
for (const file2 of files) {
|
|
18974
|
-
const fileStat = deps.stat(
|
|
19379
|
+
const fileStat = deps.stat(join8(dir, file2));
|
|
18975
19380
|
if (fileStat !== null && !fileStat.isDirectory) count += 1;
|
|
18976
19381
|
}
|
|
18977
19382
|
return count;
|
|
@@ -18991,24 +19396,24 @@ function dirSize(dir, deps, depth = 0) {
|
|
|
18991
19396
|
if (files === null) return null;
|
|
18992
19397
|
let total = 0;
|
|
18993
19398
|
for (const file2 of files) {
|
|
18994
|
-
const childSize = dirSize(
|
|
19399
|
+
const childSize = dirSize(join8(dir, file2), deps, depth + 1);
|
|
18995
19400
|
if (childSize !== null) total += childSize;
|
|
18996
19401
|
}
|
|
18997
19402
|
return total;
|
|
18998
19403
|
}
|
|
18999
19404
|
function verboseChecks(options, deps) {
|
|
19000
19405
|
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
19001
|
-
const logsDir =
|
|
19002
|
-
const chunksDir =
|
|
19406
|
+
const logsDir = join8(dataDir, "logs");
|
|
19407
|
+
const chunksDir = join8(dataDir, "chunks");
|
|
19003
19408
|
const providerCandidates = providerConfigCandidates(options, deps);
|
|
19004
19409
|
const captureMode = deps.env["AGENT_INSPECTOR_MODE"] ?? deps.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? "simple";
|
|
19005
19410
|
const logCount = countFilesInDir(logsDir, deps);
|
|
19006
19411
|
const logSize = dirSize(logsDir, deps);
|
|
19007
19412
|
const chunkSize = dirSize(chunksDir, deps);
|
|
19008
19413
|
const homeDir = deps.platform === "win32" ? deps.env["USERPROFILE"] ?? deps.env["APPDATA"] ?? null : deps.env["HOME"] ?? null;
|
|
19009
|
-
const claudeSkill = homeDir === null ? null :
|
|
19010
|
-
const claudeCommand = homeDir === null ? null :
|
|
19011
|
-
const codexSkill = homeDir === null ? null :
|
|
19414
|
+
const claudeSkill = homeDir === null ? null : join8(homeDir, ".claude", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
19415
|
+
const claudeCommand = homeDir === null ? null : join8(homeDir, ".claude", "commands", "agent-inspector-onboard.md");
|
|
19416
|
+
const codexSkill = homeDir === null ? null : join8(homeDir, ".codex", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
19012
19417
|
const hasClaudeOnboarding = claudeSkill !== null && claudeCommand !== null && deps.exists(claudeSkill) && deps.exists(claudeCommand);
|
|
19013
19418
|
const hasCodexOnboarding = codexSkill !== null && deps.exists(codexSkill);
|
|
19014
19419
|
return [
|
|
@@ -19040,8 +19445,8 @@ function verboseChecks(options, deps) {
|
|
|
19040
19445
|
];
|
|
19041
19446
|
}
|
|
19042
19447
|
function checkExtensionSource(rootDir, deps) {
|
|
19043
|
-
const extensionDir =
|
|
19044
|
-
const missing = EXTENSION_REQUIRED_FILES.filter((file2) => !deps.exists(
|
|
19448
|
+
const extensionDir = join8(rootDir, "extensions", "chrome");
|
|
19449
|
+
const missing = EXTENSION_REQUIRED_FILES.filter((file2) => !deps.exists(join8(extensionDir, file2)));
|
|
19045
19450
|
if (missing.length === 0) {
|
|
19046
19451
|
return check2(
|
|
19047
19452
|
"Chrome extension source",
|
|
@@ -19057,7 +19462,7 @@ function checkExtensionSource(rootDir, deps) {
|
|
|
19057
19462
|
);
|
|
19058
19463
|
}
|
|
19059
19464
|
function extensionManifestVersion(rootDir, deps) {
|
|
19060
|
-
const manifestText = deps.readText(
|
|
19465
|
+
const manifestText = deps.readText(join8(rootDir, "extensions", "chrome", "manifest.json"));
|
|
19061
19466
|
if (manifestText === null) return null;
|
|
19062
19467
|
try {
|
|
19063
19468
|
const manifest = JSON.parse(manifestText);
|
|
@@ -19069,7 +19474,7 @@ function extensionManifestVersion(rootDir, deps) {
|
|
|
19069
19474
|
}
|
|
19070
19475
|
}
|
|
19071
19476
|
function checkExtensionPackage(rootDir, deps) {
|
|
19072
|
-
const distDir =
|
|
19477
|
+
const distDir = join8(rootDir, "dist", "chrome-extension");
|
|
19073
19478
|
const files = deps.listDir(distDir);
|
|
19074
19479
|
if (files === null) {
|
|
19075
19480
|
return check2(
|
|
@@ -19169,7 +19574,8 @@ function doctorHelp() {
|
|
|
19169
19574
|
"Usage:",
|
|
19170
19575
|
" agent-inspector doctor [--port <port>] [--config-dir <dir>] [--providers <json>] [--fix] [--json] [--verbose] [--chrome-extension]",
|
|
19171
19576
|
"",
|
|
19172
|
-
"Checks local
|
|
19577
|
+
"Checks local backend health, provider config presence, and package metadata.",
|
|
19578
|
+
"The backend check probes the local health endpoint and does not require a composed Web UI.",
|
|
19173
19579
|
"Pass --fix to run safe local repairs before checks, including legacy data-dir migration.",
|
|
19174
19580
|
"Pass --json to emit a machine-readable report for CI, scripts, and Jenkins.",
|
|
19175
19581
|
"Pass --verbose to include local paths, capture mode, and log storage checks.",
|
|
@@ -19218,14 +19624,14 @@ function readText(path2) {
|
|
|
19218
19624
|
}
|
|
19219
19625
|
function listDir(path2) {
|
|
19220
19626
|
try {
|
|
19221
|
-
return
|
|
19627
|
+
return readdirSync5(path2);
|
|
19222
19628
|
} catch {
|
|
19223
19629
|
return null;
|
|
19224
19630
|
}
|
|
19225
19631
|
}
|
|
19226
|
-
function
|
|
19632
|
+
function stat3(path2) {
|
|
19227
19633
|
try {
|
|
19228
|
-
const fileStat =
|
|
19634
|
+
const fileStat = statSync3(path2);
|
|
19229
19635
|
return { isDirectory: fileStat.isDirectory(), size: fileStat.size };
|
|
19230
19636
|
} catch {
|
|
19231
19637
|
return null;
|
|
@@ -19239,7 +19645,7 @@ function createDoctorDeps() {
|
|
|
19239
19645
|
exists: existsSync6,
|
|
19240
19646
|
readText,
|
|
19241
19647
|
listDir,
|
|
19242
|
-
stat:
|
|
19648
|
+
stat: stat3,
|
|
19243
19649
|
fetchHealth,
|
|
19244
19650
|
isPortOpen
|
|
19245
19651
|
};
|
|
@@ -19298,11 +19704,7 @@ function parsePort2(raw) {
|
|
|
19298
19704
|
return port;
|
|
19299
19705
|
}
|
|
19300
19706
|
function normalizeAliasPath(raw) {
|
|
19301
|
-
|
|
19302
|
-
if (!trimmed.startsWith("/") || trimmed.includes("?") || trimmed.includes("#") || trimmed.includes("\\")) {
|
|
19303
|
-
return null;
|
|
19304
|
-
}
|
|
19305
|
-
return normalizeBasePath(trimmed);
|
|
19707
|
+
return normalizePublicBasePath(raw);
|
|
19306
19708
|
}
|
|
19307
19709
|
function isValidHost(host) {
|
|
19308
19710
|
const trimmed = host.trim();
|
|
@@ -19627,6 +20029,10 @@ var init_alias = __esm({
|
|
|
19627
20029
|
});
|
|
19628
20030
|
|
|
19629
20031
|
// src/lib/instanceContract.ts
|
|
20032
|
+
function isCanonicalPublicBasePath(value) {
|
|
20033
|
+
const normalized = normalizePublicBasePath(value);
|
|
20034
|
+
return normalized !== null && (normalized.length === 0 ? "/" : normalized) === value;
|
|
20035
|
+
}
|
|
19630
20036
|
function isRetryableInstanceError(code) {
|
|
19631
20037
|
switch (code) {
|
|
19632
20038
|
case "launch_failed":
|
|
@@ -19653,11 +20059,12 @@ function isRetryableInstanceError(code) {
|
|
|
19653
20059
|
return false;
|
|
19654
20060
|
}
|
|
19655
20061
|
}
|
|
19656
|
-
var INSTANCE_API_SCHEMA_VERSION, InstanceStateSchema, InstanceStateReasonSchema, InstanceUrlsSchema, InstanceStatusOutputSchema, InstanceConnectionOutputSchema, InstanceStartRequestSchema, InstanceEmptyRequestSchema, InstanceOperationSchema, InstanceOperationOutcomeSchema, InstanceOperationOutputSchema, InstanceListOutputSchema, InstancePublicErrorCodeSchema, InstancePublicErrorSchema, InstanceErrorOutputSchema;
|
|
20062
|
+
var INSTANCE_API_SCHEMA_VERSION, InstanceStateSchema, InstanceStateReasonSchema, InstanceUrlsSchema, InstanceStatusOutputSchema, InstanceConnectionSurfacesSchema, InstanceConnectionOutputSchema, InstanceStartRequestSchema, InstanceEmptyRequestSchema, InstanceOperationSchema, InstanceOperationOutcomeSchema, InstanceOperationOutputSchema, InstanceListOutputSchema, InstancePublicErrorCodeSchema, InstancePublicErrorSchema, InstanceErrorOutputSchema;
|
|
19657
20063
|
var init_instanceContract = __esm({
|
|
19658
20064
|
"src/lib/instanceContract.ts"() {
|
|
19659
20065
|
"use strict";
|
|
19660
20066
|
init_zod();
|
|
20067
|
+
init_basePath();
|
|
19661
20068
|
INSTANCE_API_SCHEMA_VERSION = 1;
|
|
19662
20069
|
InstanceStateSchema = external_exports.enum(["running", "stopped", "conflict"]);
|
|
19663
20070
|
InstanceStateReasonSchema = external_exports.enum(["identity_mismatch", "port_conflict"]);
|
|
@@ -19685,15 +20092,27 @@ var init_instanceContract = __esm({
|
|
|
19685
20092
|
host: external_exports.string().min(1),
|
|
19686
20093
|
publicPort: external_exports.number().int().min(1).max(65535),
|
|
19687
20094
|
upstreamPort: external_exports.number().int().min(1).max(65535),
|
|
19688
|
-
basePath: external_exports.string().
|
|
20095
|
+
basePath: external_exports.string().refine(isCanonicalPublicBasePath, "Invalid public Base Path"),
|
|
19689
20096
|
captureMode: external_exports.enum(["simple", "full"]),
|
|
20097
|
+
uiEnabled: external_exports.boolean(),
|
|
19690
20098
|
dataDir: external_exports.string().min(1),
|
|
19691
20099
|
supervisorPid: external_exports.number().int().positive().nullable(),
|
|
19692
20100
|
urls: InstanceUrlsSchema
|
|
19693
20101
|
}).strict();
|
|
20102
|
+
InstanceConnectionSurfacesSchema = external_exports.object({
|
|
20103
|
+
ui: external_exports.object({
|
|
20104
|
+
enabled: external_exports.boolean(),
|
|
20105
|
+
baseUrl: external_exports.url().nullable()
|
|
20106
|
+
}).strict(),
|
|
20107
|
+
control: external_exports.object({ baseUrl: external_exports.url() }).strict(),
|
|
20108
|
+
agent: external_exports.object({ baseUrl: external_exports.url() }).strict()
|
|
20109
|
+
}).strict();
|
|
19694
20110
|
InstanceConnectionOutputSchema = external_exports.object({
|
|
19695
20111
|
schemaVersion: external_exports.literal(INSTANCE_API_SCHEMA_VERSION),
|
|
20112
|
+
uiEnabled: external_exports.boolean(),
|
|
19696
20113
|
instance: InstanceStatusOutputSchema,
|
|
20114
|
+
surfaces: InstanceConnectionSurfacesSchema,
|
|
20115
|
+
/** @deprecated Use surfaces.control.baseUrl or surfaces.agent.baseUrl. */
|
|
19697
20116
|
baseUrl: external_exports.url(),
|
|
19698
20117
|
endpoints: InstanceUrlsSchema,
|
|
19699
20118
|
environment: external_exports.object({
|
|
@@ -19716,14 +20135,15 @@ var init_instanceContract = __esm({
|
|
|
19716
20135
|
InstanceStartRequestSchema = external_exports.object({
|
|
19717
20136
|
port: external_exports.number().int().min(1).max(65535).optional(),
|
|
19718
20137
|
basePath: external_exports.string().min(1).max(2048).refine(
|
|
19719
|
-
(value) =>
|
|
19720
|
-
"Base Path must
|
|
20138
|
+
(value) => normalizePublicBasePath(value) !== null,
|
|
20139
|
+
"Base Path must use URL-safe path segments without traversal, encoded separators, or markup characters"
|
|
19721
20140
|
).optional(),
|
|
19722
20141
|
host: external_exports.string().min(1).max(253).refine(
|
|
19723
20142
|
(value) => value.trim() === value && !value.includes("://") && !value.includes("/") && !value.includes("\\") && !/\s/u.test(value),
|
|
19724
20143
|
"Host must be a hostname or IP address without protocol, path, or whitespace"
|
|
19725
20144
|
).optional(),
|
|
19726
20145
|
captureMode: external_exports.enum(["simple", "full"]).optional(),
|
|
20146
|
+
uiEnabled: external_exports.boolean().optional(),
|
|
19727
20147
|
dataDir: external_exports.string().trim().min(1).max(32767).optional()
|
|
19728
20148
|
}).strict();
|
|
19729
20149
|
InstanceEmptyRequestSchema = external_exports.object({}).strict();
|
|
@@ -19790,10 +20210,8 @@ function parseMode(value) {
|
|
|
19790
20210
|
return value === "simple" || value === "full" ? value : null;
|
|
19791
20211
|
}
|
|
19792
20212
|
function parseBasePath(value) {
|
|
19793
|
-
if (value === void 0
|
|
19794
|
-
|
|
19795
|
-
}
|
|
19796
|
-
return normalizeBasePath(value);
|
|
20213
|
+
if (value === void 0) return null;
|
|
20214
|
+
return normalizePublicBasePath(value);
|
|
19797
20215
|
}
|
|
19798
20216
|
function optionWithEquals(arg) {
|
|
19799
20217
|
if (!arg.startsWith("--") || !arg.includes("=")) return null;
|
|
@@ -19807,6 +20225,7 @@ function emptyOverrides() {
|
|
|
19807
20225
|
host: null,
|
|
19808
20226
|
captureMode: null,
|
|
19809
20227
|
dataDir: null,
|
|
20228
|
+
uiEnabled: null,
|
|
19810
20229
|
open: false
|
|
19811
20230
|
};
|
|
19812
20231
|
}
|
|
@@ -19880,6 +20299,24 @@ function parseInstanceArgs(argv) {
|
|
|
19880
20299
|
case "--open":
|
|
19881
20300
|
options.overrides.open = true;
|
|
19882
20301
|
break;
|
|
20302
|
+
case "--backend-only":
|
|
20303
|
+
if (options.overrides.uiEnabled === true) {
|
|
20304
|
+
return {
|
|
20305
|
+
kind: "error",
|
|
20306
|
+
message: "instance: --backend-only and --with-ui are mutually exclusive"
|
|
20307
|
+
};
|
|
20308
|
+
}
|
|
20309
|
+
options.overrides.uiEnabled = false;
|
|
20310
|
+
break;
|
|
20311
|
+
case "--with-ui":
|
|
20312
|
+
if (options.overrides.uiEnabled === false) {
|
|
20313
|
+
return {
|
|
20314
|
+
kind: "error",
|
|
20315
|
+
message: "instance: --backend-only and --with-ui are mutually exclusive"
|
|
20316
|
+
};
|
|
20317
|
+
}
|
|
20318
|
+
options.overrides.uiEnabled = true;
|
|
20319
|
+
break;
|
|
19883
20320
|
case "--port":
|
|
19884
20321
|
case "-p":
|
|
19885
20322
|
case "--base-path":
|
|
@@ -19906,7 +20343,7 @@ function parseInstanceArgs(argv) {
|
|
|
19906
20343
|
if (options.positional.length !== 1) {
|
|
19907
20344
|
return { kind: "error", message: "instance: list does not accept an instance name" };
|
|
19908
20345
|
}
|
|
19909
|
-
if (options.overrides.port !== null || options.overrides.basePath !== null || options.overrides.host !== null || options.overrides.captureMode !== null || options.overrides.dataDir !== null || options.overrides.open) {
|
|
20346
|
+
if (options.overrides.port !== null || options.overrides.basePath !== null || options.overrides.host !== null || options.overrides.captureMode !== null || options.overrides.dataDir !== null || options.overrides.uiEnabled !== null || options.overrides.open) {
|
|
19910
20347
|
return { kind: "error", message: "instance: list accepts only --json" };
|
|
19911
20348
|
}
|
|
19912
20349
|
return { kind: "ok", action: { kind: "list", json: options.json } };
|
|
@@ -19916,7 +20353,7 @@ function parseInstanceArgs(argv) {
|
|
|
19916
20353
|
if (name === void 0 || options.positional.length !== 2) {
|
|
19917
20354
|
return { kind: "error", message: `instance: ${command} requires exactly one instance name` };
|
|
19918
20355
|
}
|
|
19919
|
-
if (options.overrides.port !== null || options.overrides.basePath !== null || options.overrides.host !== null || options.overrides.captureMode !== null || options.overrides.dataDir !== null || options.overrides.open) {
|
|
20356
|
+
if (options.overrides.port !== null || options.overrides.basePath !== null || options.overrides.host !== null || options.overrides.captureMode !== null || options.overrides.dataDir !== null || options.overrides.uiEnabled !== null || options.overrides.open) {
|
|
19920
20357
|
return {
|
|
19921
20358
|
kind: "error",
|
|
19922
20359
|
message: `instance: ${command} accepts only an instance name and --json`
|
|
@@ -19932,6 +20369,9 @@ function parseInstanceArgs(argv) {
|
|
|
19932
20369
|
if (name === void 0 || options.positional.length !== 2) {
|
|
19933
20370
|
return { kind: "error", message: `instance: ${command} requires exactly one instance name` };
|
|
19934
20371
|
}
|
|
20372
|
+
if (options.overrides.uiEnabled === false && options.overrides.open) {
|
|
20373
|
+
return { kind: "error", message: "instance: --open cannot be used with --backend-only" };
|
|
20374
|
+
}
|
|
19935
20375
|
return {
|
|
19936
20376
|
kind: "ok",
|
|
19937
20377
|
action: { kind: command, name, overrides: options.overrides, json: options.json }
|
|
@@ -19962,6 +20402,8 @@ Start/restart options:
|
|
|
19962
20402
|
--host, -H <host> Bind host (default: 127.0.0.1)
|
|
19963
20403
|
--mode <simple|full> Capture mode (default: simple)
|
|
19964
20404
|
--data-dir <path> Isolated data/config directory
|
|
20405
|
+
--backend-only Persist a headless backend without serving the UI
|
|
20406
|
+
--with-ui Persist composed UI serving (the default for new instances)
|
|
19965
20407
|
--open Open the UI after readiness
|
|
19966
20408
|
--json Emit machine-readable output
|
|
19967
20409
|
--help, -h Show this help
|
|
@@ -20093,9 +20535,9 @@ var init_instanceControl = __esm({
|
|
|
20093
20535
|
// src/cli/instance.ts
|
|
20094
20536
|
var instance_exports = {};
|
|
20095
20537
|
__export(instance_exports, {
|
|
20538
|
+
buildInstanceLaunchArgs: () => buildInstanceLaunchArgs,
|
|
20096
20539
|
getNamedInstance: () => getNamedInstance,
|
|
20097
20540
|
getNamedInstanceConnection: () => getNamedInstanceConnection,
|
|
20098
|
-
instanceLifecycleForTests: () => instanceLifecycleForTests,
|
|
20099
20541
|
listNamedInstances: () => listNamedInstances,
|
|
20100
20542
|
restartNamedInstance: () => restartNamedInstance,
|
|
20101
20543
|
runInstance: () => runInstance,
|
|
@@ -20152,6 +20594,7 @@ function statusOutput(record2, live, checkedAt = /* @__PURE__ */ new Date()) {
|
|
|
20152
20594
|
upstreamPort: record2.upstreamPort,
|
|
20153
20595
|
basePath: displayBasePath2(record2.basePath),
|
|
20154
20596
|
captureMode: record2.captureMode,
|
|
20597
|
+
uiEnabled: record2.uiEnabled,
|
|
20155
20598
|
dataDir: record2.dataDir,
|
|
20156
20599
|
supervisorPid: live.supervisorPid,
|
|
20157
20600
|
urls: recordUrls(record2)
|
|
@@ -20169,7 +20612,16 @@ function connectionOutput(instance) {
|
|
|
20169
20612
|
const serverName = `agent-inspector-${instance.name}`;
|
|
20170
20613
|
return {
|
|
20171
20614
|
schemaVersion: INSTANCE_API_SCHEMA_VERSION,
|
|
20615
|
+
uiEnabled: instance.uiEnabled,
|
|
20172
20616
|
instance,
|
|
20617
|
+
surfaces: {
|
|
20618
|
+
ui: {
|
|
20619
|
+
enabled: instance.uiEnabled,
|
|
20620
|
+
baseUrl: instance.uiEnabled ? instance.urls.ui : null
|
|
20621
|
+
},
|
|
20622
|
+
control: { baseUrl: instance.urls.ui },
|
|
20623
|
+
agent: { baseUrl: instance.urls.ui }
|
|
20624
|
+
},
|
|
20173
20625
|
baseUrl: instance.urls.ui,
|
|
20174
20626
|
endpoints: instance.urls,
|
|
20175
20627
|
environment: {
|
|
@@ -20235,7 +20687,7 @@ function defaultOpenUrl(targetUrl) {
|
|
|
20235
20687
|
child.once("error", () => void 0);
|
|
20236
20688
|
child.unref();
|
|
20237
20689
|
}
|
|
20238
|
-
function
|
|
20690
|
+
function buildInstanceLaunchArgs(record2, open2) {
|
|
20239
20691
|
const args = [
|
|
20240
20692
|
"--background",
|
|
20241
20693
|
"--no-open",
|
|
@@ -20246,11 +20698,12 @@ function launchArgs(record2, open) {
|
|
|
20246
20698
|
"--mode",
|
|
20247
20699
|
record2.captureMode
|
|
20248
20700
|
];
|
|
20249
|
-
if (
|
|
20701
|
+
if (!record2.uiEnabled) args.push("--backend-only");
|
|
20702
|
+
if (open2 && record2.uiEnabled) args.push("--open");
|
|
20250
20703
|
return args;
|
|
20251
20704
|
}
|
|
20252
|
-
async function defaultLaunch(record2,
|
|
20253
|
-
const child = spawn(process.execPath, [cliEntry, ...
|
|
20705
|
+
async function defaultLaunch(record2, open2, cliEntry) {
|
|
20706
|
+
const child = spawn(process.execPath, [cliEntry, ...buildInstanceLaunchArgs(record2, open2)], {
|
|
20254
20707
|
stdio: "ignore",
|
|
20255
20708
|
windowsHide: true,
|
|
20256
20709
|
env: {
|
|
@@ -20268,12 +20721,12 @@ async function defaultLaunch(record2, open, cliEntry) {
|
|
|
20268
20721
|
});
|
|
20269
20722
|
return await waitForProcess(child) === 0;
|
|
20270
20723
|
}
|
|
20271
|
-
async function launchRecord(record2,
|
|
20724
|
+
async function launchRecord(record2, open2, deps) {
|
|
20272
20725
|
const cliEntry = deps.cliEntry ?? process.env[AGENT_INSPECTOR_CLI_ENTRY_ENV] ?? process.argv[1];
|
|
20273
20726
|
if (deps.launch === void 0 && (cliEntry === void 0 || cliEntry.length === 0)) {
|
|
20274
20727
|
return failure("launch_unavailable", "Cannot resolve the packaged Agent Inspector CLI entry.");
|
|
20275
20728
|
}
|
|
20276
|
-
const launched = deps.launch === void 0 ? await defaultLaunch(record2,
|
|
20729
|
+
const launched = deps.launch === void 0 ? await defaultLaunch(record2, open2, cliEntry ?? "") : await deps.launch(record2, open2);
|
|
20277
20730
|
if (!launched) {
|
|
20278
20731
|
return failure(
|
|
20279
20732
|
"launch_failed",
|
|
@@ -20379,6 +20832,7 @@ function overridesDiffer(record2, action) {
|
|
|
20379
20832
|
if (overrides.basePath !== null && overrides.basePath !== record2.basePath) return true;
|
|
20380
20833
|
if (overrides.host !== null && overrides.host !== record2.host) return true;
|
|
20381
20834
|
if (overrides.captureMode !== null && overrides.captureMode !== record2.captureMode) return true;
|
|
20835
|
+
if (overrides.uiEnabled !== null && overrides.uiEnabled !== record2.uiEnabled) return true;
|
|
20382
20836
|
if (overrides.dataDir !== null) {
|
|
20383
20837
|
const requested = canonicalizeDataDirectory(overrides.dataDir);
|
|
20384
20838
|
const saved = canonicalizeDataDirectory(record2.dataDir);
|
|
@@ -20396,6 +20850,7 @@ function updatedRecord(record2, action, pair, dataDir) {
|
|
|
20396
20850
|
upstreamPort: pair.upstreamPort,
|
|
20397
20851
|
basePath: action.overrides.basePath ?? record2.basePath,
|
|
20398
20852
|
captureMode: action.overrides.captureMode ?? record2.captureMode,
|
|
20853
|
+
uiEnabled: action.overrides.uiEnabled ?? record2.uiEnabled,
|
|
20399
20854
|
dataDir
|
|
20400
20855
|
};
|
|
20401
20856
|
const parsed = InstanceRecordSchema.safeParse(candidate);
|
|
@@ -20446,6 +20901,7 @@ async function createNewRecord(root, action, deps) {
|
|
|
20446
20901
|
upstreamPort: pairResult.value.upstreamPort,
|
|
20447
20902
|
basePath: action.overrides.basePath ?? getConfiguredBasePath(process.env),
|
|
20448
20903
|
captureMode: action.overrides.captureMode ?? DEFAULT_INSTANCE_CAPTURE_MODE,
|
|
20904
|
+
uiEnabled: action.overrides.uiEnabled ?? true,
|
|
20449
20905
|
dataDir: dataDirResult.value
|
|
20450
20906
|
});
|
|
20451
20907
|
return created.kind === "error" ? fromRegistry(created) : saveRecord(root, created.value);
|
|
@@ -20486,7 +20942,9 @@ async function startAction(root, action, deps) {
|
|
|
20486
20942
|
}
|
|
20487
20943
|
const live = await classifyRecord(record2, deps);
|
|
20488
20944
|
if (live.state === "running") {
|
|
20489
|
-
if (action.overrides.open
|
|
20945
|
+
if (action.overrides.open && record2.uiEnabled) {
|
|
20946
|
+
(deps.openUrl ?? defaultOpenUrl)(recordUrls(record2).ui);
|
|
20947
|
+
}
|
|
20490
20948
|
return success2(operationOutput("start", "already_running", statusOutput(record2, live)));
|
|
20491
20949
|
}
|
|
20492
20950
|
if (live.state === "conflict") {
|
|
@@ -20616,9 +21074,10 @@ async function listAction(root, deps) {
|
|
|
20616
21074
|
function requestActionOverrides(input) {
|
|
20617
21075
|
return {
|
|
20618
21076
|
port: input.port ?? null,
|
|
20619
|
-
basePath: input.basePath === void 0 ? null :
|
|
21077
|
+
basePath: input.basePath === void 0 ? null : normalizePublicBasePath(input.basePath),
|
|
20620
21078
|
host: input.host ?? null,
|
|
20621
21079
|
captureMode: input.captureMode ?? null,
|
|
21080
|
+
uiEnabled: input.uiEnabled ?? null,
|
|
20622
21081
|
dataDir: input.dataDir ?? null,
|
|
20623
21082
|
open: false
|
|
20624
21083
|
};
|
|
@@ -20668,8 +21127,10 @@ async function listNamedInstances(deps = {}) {
|
|
|
20668
21127
|
function formatStatus(output) {
|
|
20669
21128
|
const pid = output.supervisorPid === null ? "-" : String(output.supervisorPid);
|
|
20670
21129
|
const reason = output.stateReason === null ? "-" : output.stateReason;
|
|
21130
|
+
const ui = output.uiEnabled ? output.urls.ui : `disabled (configured URL: ${output.urls.ui})`;
|
|
20671
21131
|
return `Instance ${output.name}: ${output.state}
|
|
20672
|
-
UI: ${
|
|
21132
|
+
UI: ${ui}
|
|
21133
|
+
UI enabled: ${output.uiEnabled ? "yes" : "no"}
|
|
20673
21134
|
Proxy: ${output.urls.proxy}
|
|
20674
21135
|
OpenAI /v1: ${output.urls.openai}
|
|
20675
21136
|
MCP: ${output.urls.mcp}
|
|
@@ -20810,7 +21271,7 @@ ${instanceHelp()}`);
|
|
|
20810
21271
|
}
|
|
20811
21272
|
}
|
|
20812
21273
|
}
|
|
20813
|
-
var DEFAULT_INSTANCE_HOST, DEFAULT_INSTANCE_CAPTURE_MODE, INSTANCE_READY_TIMEOUT_MS, INSTANCE_STOP_TIMEOUT_MS, INSTANCE_POLL_INTERVAL_MS, processIo2
|
|
21274
|
+
var DEFAULT_INSTANCE_HOST, DEFAULT_INSTANCE_CAPTURE_MODE, INSTANCE_READY_TIMEOUT_MS, INSTANCE_STOP_TIMEOUT_MS, INSTANCE_POLL_INTERVAL_MS, processIo2;
|
|
20814
21275
|
var init_instance = __esm({
|
|
20815
21276
|
"src/cli/instance.ts"() {
|
|
20816
21277
|
"use strict";
|
|
@@ -20832,26 +21293,21 @@ var init_instance = __esm({
|
|
|
20832
21293
|
stdout: (text) => process.stdout.write(text),
|
|
20833
21294
|
stderr: (text) => process.stderr.write(text)
|
|
20834
21295
|
};
|
|
20835
|
-
instanceLifecycleForTests = {
|
|
20836
|
-
classifyRecord,
|
|
20837
|
-
launchArgs,
|
|
20838
|
-
statusOutput
|
|
20839
|
-
};
|
|
20840
21296
|
}
|
|
20841
21297
|
});
|
|
20842
21298
|
|
|
20843
21299
|
// src/cli.ts
|
|
20844
|
-
import { Buffer as
|
|
21300
|
+
import { Buffer as Buffer5 } from "node:buffer";
|
|
20845
21301
|
import { spawn as spawn2 } from "node:child_process";
|
|
20846
21302
|
import { createConnection as createConnection2 } from "node:net";
|
|
20847
21303
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
20848
|
-
import { dirname as
|
|
21304
|
+
import { dirname as dirname2, join as join9, resolve as resolvePath2 } from "node:path";
|
|
20849
21305
|
import { existsSync as existsSync7 } from "node:fs";
|
|
20850
21306
|
|
|
20851
21307
|
// package.json
|
|
20852
21308
|
var package_default = {
|
|
20853
21309
|
name: "@tonyclaw/agent-inspector",
|
|
20854
|
-
version: "3.0.
|
|
21310
|
+
version: "3.0.46",
|
|
20855
21311
|
type: "module",
|
|
20856
21312
|
description: "Agent observability and knowledge capture layer for AI coding tools.",
|
|
20857
21313
|
license: "MIT",
|
|
@@ -20895,7 +21351,8 @@ var package_default = {
|
|
|
20895
21351
|
"!src/**/*.test.ts",
|
|
20896
21352
|
"!src/**/*.stories.tsx",
|
|
20897
21353
|
"!src/**/__fixtures__",
|
|
20898
|
-
"!src/routeTree.gen.ts",
|
|
21354
|
+
"!src/ui/routeTree.gen.ts",
|
|
21355
|
+
"!src/backend/routeTree.gen.ts",
|
|
20899
21356
|
"scripts/setup-windows-runtime.mjs",
|
|
20900
21357
|
"scripts/setup-agent-skills.mjs",
|
|
20901
21358
|
"scripts/generate-theme-scales.mjs",
|
|
@@ -20905,21 +21362,37 @@ var package_default = {
|
|
|
20905
21362
|
"!.output/*.exe"
|
|
20906
21363
|
],
|
|
20907
21364
|
scripts: {
|
|
20908
|
-
dev: "
|
|
21365
|
+
dev: "node scripts/dev-decoupled.mjs",
|
|
21366
|
+
"dev:ui": "vite dev --config vite.ui.config.ts",
|
|
21367
|
+
"dev:backend": "node scripts/dev-backend.mjs",
|
|
20909
21368
|
"test:smoke": "bun scripts/run-test-suite.mjs smoke",
|
|
20910
21369
|
"test:fast": "bun scripts/run-test-suite.mjs smoke",
|
|
20911
21370
|
"test:unit": "bun scripts/run-test-suite.mjs unit",
|
|
20912
21371
|
"test:integration": "bun scripts/run-test-suite.mjs integration",
|
|
20913
21372
|
"test:changed": "bun scripts/run-test-suite.mjs changed",
|
|
20914
21373
|
"test:all": "bun scripts/run-test-suite.mjs all",
|
|
21374
|
+
"test:compatibility-tooling": "node --test scripts/compatibility/*.test.mjs",
|
|
20915
21375
|
start: "node .output/cli.js",
|
|
20916
|
-
|
|
21376
|
+
"start:backend": "node .output/cli.js --backend-only --no-open",
|
|
21377
|
+
"preview:ui": "vite preview --config vite.ui.config.ts",
|
|
21378
|
+
"clean:output": "node scripts/clean-output.mjs",
|
|
21379
|
+
build: "bun clean:output && bun build:backend && bun build:ui && bun smoke:ui-preview && bun build:workers && bun build:cli && bun build:mcp && bun check:bundle && bun check:boundaries",
|
|
21380
|
+
"build:backend": "vite build --config vite.backend.config.ts",
|
|
21381
|
+
"build:ui": "vite build --config vite.ui.config.ts",
|
|
20917
21382
|
"build:workers": "npx esbuild src/proxy/logFinalizer.worker.ts src/proxy/sessionWorkerEntry.ts --bundle --platform=node --target=node22 --format=esm --outdir=.output/workers --external:better-sqlite3",
|
|
20918
21383
|
"smoke:workers": "node scripts/smoke-production-workers.mjs",
|
|
20919
21384
|
"smoke:packed-workers": "node scripts/smoke-packed-workers.mjs",
|
|
20920
21385
|
"smoke:instances": "node scripts/smoke-windows-instances.mjs",
|
|
20921
21386
|
"smoke:coding-agent-instances": "node scripts/smoke-coding-agent-instances.mjs",
|
|
21387
|
+
"smoke:ui-preview": "node scripts/smoke-decoupled-ui-preview.mjs",
|
|
20922
21388
|
"check:bundle": "node scripts/check-client-bundle.mjs",
|
|
21389
|
+
"check:boundaries": "node scripts/check-runtime-boundaries.mjs",
|
|
21390
|
+
"compatibility:bdd": "node scripts/lint-bdd-catalog.mjs",
|
|
21391
|
+
"compatibility:compare": "node scripts/compatibility/cli.mjs compare",
|
|
21392
|
+
"compatibility:drift": "node scripts/check-backend-compatibility.mjs",
|
|
21393
|
+
"compatibility:fixtures": "bun scripts/validate-compatibility-fixtures.ts",
|
|
21394
|
+
"compatibility:freeze": "node scripts/freeze-backend-baseline.mjs",
|
|
21395
|
+
"compatibility:run": "node scripts/compatibility/cli.mjs run",
|
|
20923
21396
|
"build:cli": "npx esbuild src/cli.ts --bundle --platform=node --target=node22 --format=esm --outfile=.output/cli.js",
|
|
20924
21397
|
"build:mcp": "npx esbuild packages/agent-inspector-mcp/src/cli.ts --bundle --platform=node --target=node22 --format=esm --outfile=packages/agent-inspector-mcp/dist/cli.js",
|
|
20925
21398
|
"themes:generate": "node scripts/generate-theme-scales.mjs",
|
|
@@ -20934,7 +21407,7 @@ var package_default = {
|
|
|
20934
21407
|
"extension:release": "node scripts/release-chrome-extension.mjs",
|
|
20935
21408
|
release: "node scripts/release.mjs",
|
|
20936
21409
|
knip: "knip",
|
|
20937
|
-
check: "bun scripts/run-static-checks.mjs --format",
|
|
21410
|
+
check: "bun scripts/run-static-checks.mjs --format && bun check:boundaries && bun compatibility:bdd",
|
|
20938
21411
|
prepare: "husky",
|
|
20939
21412
|
ladle: "ladle serve",
|
|
20940
21413
|
"ladle:build": "ladle build"
|
|
@@ -20973,6 +21446,7 @@ var package_default = {
|
|
|
20973
21446
|
"@ladle/react": "^5.1.1",
|
|
20974
21447
|
"@radix-ui/colors": "^3.0.0",
|
|
20975
21448
|
"@tailwindcss/vite": "^4.3.2",
|
|
21449
|
+
"@tanstack/router-plugin": "^1.168.20",
|
|
20976
21450
|
"@types/bun": "^1.3.14",
|
|
20977
21451
|
"@types/react": "^19.2.17",
|
|
20978
21452
|
"@types/react-dom": "^19.2.3",
|
|
@@ -20980,6 +21454,8 @@ var package_default = {
|
|
|
20980
21454
|
"@typescript-eslint/eslint-plugin": "^8.64.0",
|
|
20981
21455
|
"@typescript-eslint/parser": "^8.64.0",
|
|
20982
21456
|
"@vitejs/plugin-react": "^5.2.0",
|
|
21457
|
+
ajv: "8.20.0",
|
|
21458
|
+
"ajv-formats": "3.0.1",
|
|
20983
21459
|
"colorjs.io": "^0.7.0",
|
|
20984
21460
|
esbuild: "^0.28.1",
|
|
20985
21461
|
eslint: "^9.39.5",
|
|
@@ -21023,53 +21499,887 @@ function shouldSuppressServerOutputLine(line, context) {
|
|
|
21023
21499
|
}
|
|
21024
21500
|
|
|
21025
21501
|
// src/proxy/identityProxy.ts
|
|
21502
|
+
init_browserRuntimeContract();
|
|
21026
21503
|
init_basePath();
|
|
21027
21504
|
init_managedInstance();
|
|
21028
21505
|
import http from "node:http";
|
|
21029
|
-
import { Buffer as
|
|
21030
|
-
import { createHash, timingSafeEqual } from "node:crypto";
|
|
21031
|
-
import { isIP } from "node:net";
|
|
21506
|
+
import { Buffer as Buffer4 } from "node:buffer";
|
|
21507
|
+
import { createHash as createHash2, timingSafeEqual } from "node:crypto";
|
|
21508
|
+
import { isIP as isIP2 } from "node:net";
|
|
21032
21509
|
|
|
21033
21510
|
// src/proxy/socketTracker.ts
|
|
21034
21511
|
init_zod();
|
|
21035
21512
|
import { execFile } from "node:child_process";
|
|
21036
|
-
import { readFile } from "node:fs/promises";
|
|
21513
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
21037
21514
|
import { promisify } from "node:util";
|
|
21038
21515
|
|
|
21039
21516
|
// src/proxy/logger.ts
|
|
21040
21517
|
init_dataDir();
|
|
21041
21518
|
init_privateDataPath();
|
|
21042
|
-
import { Buffer as
|
|
21043
|
-
import {
|
|
21044
|
-
import { appendFile, readdir, rename, stat, unlink } from "node:fs/promises";
|
|
21519
|
+
import { Buffer as Buffer3 } from "node:buffer";
|
|
21520
|
+
import { closeSync as closeSync2, constants as constants3, fsyncSync, openSync as openSync2, writeSync } from "node:fs";
|
|
21521
|
+
import { appendFile, readdir as readdir2, rename, stat as stat2, unlink as unlink2 } from "node:fs/promises";
|
|
21045
21522
|
import path from "node:path";
|
|
21046
|
-
|
|
21047
|
-
|
|
21048
|
-
|
|
21049
|
-
|
|
21050
|
-
|
|
21051
|
-
|
|
21052
|
-
|
|
21053
|
-
|
|
21054
|
-
|
|
21055
|
-
|
|
21056
|
-
|
|
21057
|
-
|
|
21058
|
-
|
|
21059
|
-
|
|
21060
|
-
|
|
21061
|
-
|
|
21062
|
-
|
|
21063
|
-
|
|
21064
|
-
|
|
21065
|
-
|
|
21066
|
-
|
|
21067
|
-
|
|
21068
|
-
|
|
21069
|
-
|
|
21070
|
-
|
|
21071
|
-
|
|
21072
|
-
|
|
21523
|
+
|
|
21524
|
+
// src/proxy/jsonlRecovery.ts
|
|
21525
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
21526
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
21527
|
+
import { constants as constants2, existsSync as existsSync2, lstatSync as lstatSync2 } from "node:fs";
|
|
21528
|
+
import { link, lstat, open, readFile, readdir, stat, unlink } from "node:fs/promises";
|
|
21529
|
+
import { basename, join as join3, resolve as resolve2 } from "node:path";
|
|
21530
|
+
|
|
21531
|
+
// src/proxy/schemas.ts
|
|
21532
|
+
init_zod();
|
|
21533
|
+
|
|
21534
|
+
// src/contracts/json.ts
|
|
21535
|
+
init_zod();
|
|
21536
|
+
var JsonValueSchema = external_exports.lazy(
|
|
21537
|
+
() => external_exports.union([
|
|
21538
|
+
external_exports.string(),
|
|
21539
|
+
external_exports.number(),
|
|
21540
|
+
external_exports.boolean(),
|
|
21541
|
+
external_exports.null(),
|
|
21542
|
+
external_exports.array(JsonValueSchema),
|
|
21543
|
+
external_exports.record(external_exports.string(), JsonValueSchema)
|
|
21544
|
+
])
|
|
21545
|
+
);
|
|
21546
|
+
|
|
21547
|
+
// src/contracts/log.ts
|
|
21548
|
+
init_zod();
|
|
21549
|
+
var LogIdSchema = external_exports.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
21550
|
+
var CaptureIncompleteReasonSchema = external_exports.enum([
|
|
21551
|
+
"capacity-exceeded",
|
|
21552
|
+
"pipeline-closed",
|
|
21553
|
+
"write-failed",
|
|
21554
|
+
"finalize-failed",
|
|
21555
|
+
"upstream-stream-error",
|
|
21556
|
+
"shutdown"
|
|
21557
|
+
]);
|
|
21558
|
+
var StreamingChunkSchema = external_exports.object({
|
|
21559
|
+
index: external_exports.number(),
|
|
21560
|
+
timestamp: external_exports.number(),
|
|
21561
|
+
type: external_exports.string(),
|
|
21562
|
+
data: JsonValueSchema
|
|
21563
|
+
});
|
|
21564
|
+
var StreamingChunksArraySchema = external_exports.object({
|
|
21565
|
+
chunks: external_exports.array(StreamingChunkSchema),
|
|
21566
|
+
truncated: external_exports.boolean().optional().default(false)
|
|
21567
|
+
});
|
|
21568
|
+
var LogBodyPartSchema = external_exports.enum(["request", "response"]);
|
|
21569
|
+
var LogBodyChunkSchema = external_exports.object({
|
|
21570
|
+
logId: LogIdSchema,
|
|
21571
|
+
part: LogBodyPartSchema,
|
|
21572
|
+
text: external_exports.string(),
|
|
21573
|
+
offset: external_exports.number().int().nonnegative(),
|
|
21574
|
+
limit: external_exports.number().int().positive(),
|
|
21575
|
+
totalBytes: external_exports.number().int().nonnegative(),
|
|
21576
|
+
textBytes: external_exports.number().int().nonnegative(),
|
|
21577
|
+
nextOffset: external_exports.number().int().nonnegative().nullable(),
|
|
21578
|
+
hasMore: external_exports.boolean(),
|
|
21579
|
+
contentMode: external_exports.enum(["empty", "partial", "full"])
|
|
21580
|
+
});
|
|
21581
|
+
var CapturedLogSchema = external_exports.object({
|
|
21582
|
+
id: LogIdSchema,
|
|
21583
|
+
timestamp: external_exports.string(),
|
|
21584
|
+
method: external_exports.string(),
|
|
21585
|
+
path: external_exports.string(),
|
|
21586
|
+
model: external_exports.string().nullable(),
|
|
21587
|
+
sessionId: external_exports.string().nullable(),
|
|
21588
|
+
rawRequestBody: external_exports.string().nullable(),
|
|
21589
|
+
responseStatus: external_exports.number().nullable(),
|
|
21590
|
+
responseText: external_exports.string().nullable(),
|
|
21591
|
+
inputTokens: external_exports.number().nullable(),
|
|
21592
|
+
outputTokens: external_exports.number().nullable(),
|
|
21593
|
+
cacheCreationInputTokens: external_exports.number().nullable(),
|
|
21594
|
+
cacheReadInputTokens: external_exports.number().nullable(),
|
|
21595
|
+
elapsedMs: external_exports.number().nullable(),
|
|
21596
|
+
firstChunkMs: external_exports.number().nullable().optional(),
|
|
21597
|
+
totalStreamMs: external_exports.number().nullable().optional(),
|
|
21598
|
+
tokensPerSecond: external_exports.number().nullable().optional(),
|
|
21599
|
+
streaming: external_exports.boolean(),
|
|
21600
|
+
userAgent: external_exports.string().nullable(),
|
|
21601
|
+
origin: external_exports.string().nullable(),
|
|
21602
|
+
rawHeaders: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
21603
|
+
/** Headers sent to the upstream LLM */
|
|
21604
|
+
headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
21605
|
+
apiFormat: external_exports.enum(["anthropic", "openai", "unknown"]).default("unknown"),
|
|
21606
|
+
isTest: external_exports.boolean().optional().default(false),
|
|
21607
|
+
replayOfLogId: LogIdSchema.nullable().optional(),
|
|
21608
|
+
providerName: external_exports.string().nullable().optional(),
|
|
21609
|
+
clientPort: external_exports.number().nullable().optional(),
|
|
21610
|
+
clientPid: external_exports.number().nullable().optional(),
|
|
21611
|
+
clientCwd: external_exports.string().nullable().optional(),
|
|
21612
|
+
clientProjectFolder: external_exports.string().nullable().optional(),
|
|
21613
|
+
streamingChunks: StreamingChunksArraySchema.optional(),
|
|
21614
|
+
streamingChunksPath: external_exports.string().nullable().optional(),
|
|
21615
|
+
rawRequestBodyBytes: external_exports.number().int().nonnegative().nullable().optional(),
|
|
21616
|
+
responseTextBytes: external_exports.number().int().nonnegative().nullable().optional(),
|
|
21617
|
+
bodyContentMode: external_exports.enum(["full", "compact", "truncated"]).optional(),
|
|
21618
|
+
captureIncomplete: external_exports.boolean().optional(),
|
|
21619
|
+
droppedChunks: external_exports.number().int().nonnegative().optional(),
|
|
21620
|
+
droppedBytes: external_exports.number().int().nonnegative().optional(),
|
|
21621
|
+
captureIncompleteReason: CaptureIncompleteReasonSchema.nullable().optional(),
|
|
21622
|
+
warnings: external_exports.array(external_exports.string()).optional(),
|
|
21623
|
+
/** Error message from streaming response (e.g., SSE error event) */
|
|
21624
|
+
error: external_exports.string().nullable().optional()
|
|
21625
|
+
});
|
|
21626
|
+
|
|
21627
|
+
// src/contracts/anthropic.ts
|
|
21628
|
+
init_zod();
|
|
21629
|
+
var CacheControl = external_exports.object({
|
|
21630
|
+
type: external_exports.string(),
|
|
21631
|
+
ttl: external_exports.string().optional(),
|
|
21632
|
+
scope: external_exports.string().optional()
|
|
21633
|
+
});
|
|
21634
|
+
var TextContentBlock = external_exports.object({
|
|
21635
|
+
type: external_exports.literal("text"),
|
|
21636
|
+
text: external_exports.string(),
|
|
21637
|
+
cache_control: CacheControl.optional()
|
|
21638
|
+
});
|
|
21639
|
+
var ThinkingContentBlock = external_exports.object({
|
|
21640
|
+
type: external_exports.literal("thinking"),
|
|
21641
|
+
thinking: external_exports.string(),
|
|
21642
|
+
signature: external_exports.string().optional()
|
|
21643
|
+
});
|
|
21644
|
+
var ThinkContentBlock = external_exports.object({
|
|
21645
|
+
type: external_exports.literal("think"),
|
|
21646
|
+
thinking: external_exports.string(),
|
|
21647
|
+
signature: external_exports.string().optional()
|
|
21648
|
+
});
|
|
21649
|
+
var ImageSourceBlock = external_exports.object({
|
|
21650
|
+
type: external_exports.literal("base64"),
|
|
21651
|
+
media_type: external_exports.string(),
|
|
21652
|
+
data: external_exports.string()
|
|
21653
|
+
});
|
|
21654
|
+
var ImageContentBlock = external_exports.object({
|
|
21655
|
+
type: external_exports.literal("image"),
|
|
21656
|
+
source: ImageSourceBlock
|
|
21657
|
+
});
|
|
21658
|
+
var ToolUseContentBlock = external_exports.object({
|
|
21659
|
+
type: external_exports.literal("tool_use"),
|
|
21660
|
+
id: external_exports.string(),
|
|
21661
|
+
name: external_exports.string(),
|
|
21662
|
+
input: external_exports.record(external_exports.string(), JsonValueSchema)
|
|
21663
|
+
});
|
|
21664
|
+
var ToolResultContentItem = external_exports.discriminatedUnion("type", [TextContentBlock, ImageContentBlock]);
|
|
21665
|
+
var ToolResultContentBlock = external_exports.object({
|
|
21666
|
+
type: external_exports.literal("tool_result"),
|
|
21667
|
+
tool_use_id: external_exports.string().optional(),
|
|
21668
|
+
content: external_exports.union([external_exports.string(), external_exports.array(ToolResultContentItem)]),
|
|
21669
|
+
is_error: external_exports.boolean().optional()
|
|
21670
|
+
});
|
|
21671
|
+
var ContentBlock = external_exports.discriminatedUnion("type", [
|
|
21672
|
+
TextContentBlock,
|
|
21673
|
+
ThinkingContentBlock,
|
|
21674
|
+
ImageContentBlock,
|
|
21675
|
+
ToolUseContentBlock,
|
|
21676
|
+
ToolResultContentBlock
|
|
21677
|
+
]);
|
|
21678
|
+
var MessageContent = external_exports.union([external_exports.string(), external_exports.array(ContentBlock)]);
|
|
21679
|
+
var Message = external_exports.object({
|
|
21680
|
+
role: external_exports.enum(["user", "assistant"]),
|
|
21681
|
+
content: MessageContent
|
|
21682
|
+
});
|
|
21683
|
+
var SystemBlock = external_exports.object({
|
|
21684
|
+
type: external_exports.literal("text"),
|
|
21685
|
+
text: external_exports.string(),
|
|
21686
|
+
cache_control: CacheControl.optional()
|
|
21687
|
+
});
|
|
21688
|
+
var InputSchema = external_exports.object({
|
|
21689
|
+
type: external_exports.string(),
|
|
21690
|
+
properties: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), JsonValueSchema)).optional(),
|
|
21691
|
+
required: external_exports.array(external_exports.string()).optional(),
|
|
21692
|
+
additionalProperties: external_exports.boolean().optional(),
|
|
21693
|
+
$schema: external_exports.string().optional()
|
|
21694
|
+
});
|
|
21695
|
+
var ToolDefinition = external_exports.object({
|
|
21696
|
+
name: external_exports.string(),
|
|
21697
|
+
description: external_exports.string().optional(),
|
|
21698
|
+
input_schema: InputSchema.optional(),
|
|
21699
|
+
cache_control: CacheControl.optional()
|
|
21700
|
+
});
|
|
21701
|
+
var ThinkingConfig = external_exports.discriminatedUnion("type", [
|
|
21702
|
+
external_exports.object({ type: external_exports.literal("enabled"), budget_tokens: external_exports.number() }),
|
|
21703
|
+
external_exports.object({ type: external_exports.literal("disabled") }),
|
|
21704
|
+
external_exports.object({ type: external_exports.literal("adaptive") })
|
|
21705
|
+
]);
|
|
21706
|
+
var AnthropicRequestSchema = external_exports.object({
|
|
21707
|
+
model: external_exports.string(),
|
|
21708
|
+
messages: external_exports.array(Message),
|
|
21709
|
+
system: external_exports.array(SystemBlock).optional(),
|
|
21710
|
+
tools: external_exports.array(ToolDefinition).optional(),
|
|
21711
|
+
max_tokens: external_exports.number().optional(),
|
|
21712
|
+
temperature: external_exports.number().optional(),
|
|
21713
|
+
stream: external_exports.boolean().optional(),
|
|
21714
|
+
thinking: ThinkingConfig.optional(),
|
|
21715
|
+
metadata: external_exports.object({
|
|
21716
|
+
user_id: external_exports.string().optional()
|
|
21717
|
+
}).optional()
|
|
21718
|
+
});
|
|
21719
|
+
var ResponseContentBlock = external_exports.discriminatedUnion("type", [
|
|
21720
|
+
TextContentBlock,
|
|
21721
|
+
ThinkingContentBlock,
|
|
21722
|
+
ThinkContentBlock,
|
|
21723
|
+
ToolUseContentBlock
|
|
21724
|
+
]);
|
|
21725
|
+
var ResponseUsageSchema = external_exports.object({
|
|
21726
|
+
input_tokens: external_exports.number().nullable().optional(),
|
|
21727
|
+
output_tokens: external_exports.number().nullable().optional(),
|
|
21728
|
+
cache_creation_input_tokens: external_exports.number().nullable().optional(),
|
|
21729
|
+
cache_read_input_tokens: external_exports.number().nullable().optional()
|
|
21730
|
+
}).passthrough();
|
|
21731
|
+
var AnthropicResponseSchema = external_exports.object({
|
|
21732
|
+
id: external_exports.string(),
|
|
21733
|
+
type: external_exports.literal("message"),
|
|
21734
|
+
model: external_exports.string(),
|
|
21735
|
+
role: external_exports.literal("assistant"),
|
|
21736
|
+
content: external_exports.array(ResponseContentBlock),
|
|
21737
|
+
stop_reason: external_exports.string().nullable(),
|
|
21738
|
+
stop_sequence: external_exports.string().nullable().optional(),
|
|
21739
|
+
usage: ResponseUsageSchema
|
|
21740
|
+
}).passthrough();
|
|
21741
|
+
var SseMessageStartEvent = external_exports.object({
|
|
21742
|
+
type: external_exports.literal("message_start"),
|
|
21743
|
+
message: external_exports.object({
|
|
21744
|
+
id: external_exports.string(),
|
|
21745
|
+
type: external_exports.literal("message"),
|
|
21746
|
+
model: external_exports.string(),
|
|
21747
|
+
role: external_exports.literal("assistant"),
|
|
21748
|
+
content: external_exports.array(ResponseContentBlock),
|
|
21749
|
+
stop_reason: external_exports.null(),
|
|
21750
|
+
stop_sequence: external_exports.null(),
|
|
21751
|
+
usage: external_exports.object({
|
|
21752
|
+
input_tokens: external_exports.number(),
|
|
21753
|
+
cache_creation_input_tokens: external_exports.number().optional(),
|
|
21754
|
+
cache_read_input_tokens: external_exports.number().optional()
|
|
21755
|
+
}).passthrough()
|
|
21756
|
+
})
|
|
21757
|
+
});
|
|
21758
|
+
var SseContentBlockStartEvent = external_exports.object({
|
|
21759
|
+
type: external_exports.literal("content_block_start"),
|
|
21760
|
+
index: external_exports.number(),
|
|
21761
|
+
content_block: ResponseContentBlock
|
|
21762
|
+
});
|
|
21763
|
+
var SseDeltaBlock = external_exports.discriminatedUnion("type", [
|
|
21764
|
+
external_exports.object({ type: external_exports.literal("text_delta"), text: external_exports.string() }),
|
|
21765
|
+
external_exports.object({ type: external_exports.literal("input_json_delta"), partial_json: external_exports.string() }),
|
|
21766
|
+
external_exports.object({ type: external_exports.literal("thinking_delta"), thinking: external_exports.string() }),
|
|
21767
|
+
external_exports.object({ type: external_exports.literal("signature_delta"), signature: external_exports.string() })
|
|
21768
|
+
]);
|
|
21769
|
+
var SseContentBlockDeltaEvent = external_exports.object({
|
|
21770
|
+
type: external_exports.literal("content_block_delta"),
|
|
21771
|
+
index: external_exports.number(),
|
|
21772
|
+
delta: SseDeltaBlock
|
|
21773
|
+
});
|
|
21774
|
+
var SseContentBlockStopEvent = external_exports.object({
|
|
21775
|
+
type: external_exports.literal("content_block_stop"),
|
|
21776
|
+
index: external_exports.number()
|
|
21777
|
+
});
|
|
21778
|
+
var SseMessageDeltaEvent = external_exports.object({
|
|
21779
|
+
type: external_exports.literal("message_delta"),
|
|
21780
|
+
delta: external_exports.object({
|
|
21781
|
+
stop_reason: external_exports.string().nullable(),
|
|
21782
|
+
stop_sequence: external_exports.string().nullable().optional()
|
|
21783
|
+
}),
|
|
21784
|
+
usage: external_exports.object({
|
|
21785
|
+
output_tokens: external_exports.number(),
|
|
21786
|
+
input_tokens: external_exports.number().optional(),
|
|
21787
|
+
cache_creation_input_tokens: external_exports.number().optional(),
|
|
21788
|
+
cache_read_input_tokens: external_exports.number().optional()
|
|
21789
|
+
}).passthrough()
|
|
21790
|
+
});
|
|
21791
|
+
var SseMessageStopEvent = external_exports.object({
|
|
21792
|
+
type: external_exports.literal("message_stop")
|
|
21793
|
+
});
|
|
21794
|
+
var SsePingEvent = external_exports.object({
|
|
21795
|
+
type: external_exports.literal("ping")
|
|
21796
|
+
});
|
|
21797
|
+
var SseErrorEvent = external_exports.object({
|
|
21798
|
+
type: external_exports.literal("error"),
|
|
21799
|
+
error: external_exports.object({
|
|
21800
|
+
type: external_exports.string(),
|
|
21801
|
+
message: external_exports.string()
|
|
21802
|
+
})
|
|
21803
|
+
});
|
|
21804
|
+
var SseEventSchema = external_exports.discriminatedUnion("type", [
|
|
21805
|
+
SseMessageStartEvent,
|
|
21806
|
+
SseContentBlockStartEvent,
|
|
21807
|
+
SseContentBlockDeltaEvent,
|
|
21808
|
+
SseContentBlockStopEvent,
|
|
21809
|
+
SseMessageDeltaEvent,
|
|
21810
|
+
SseMessageStopEvent,
|
|
21811
|
+
SsePingEvent,
|
|
21812
|
+
SseErrorEvent
|
|
21813
|
+
]);
|
|
21814
|
+
|
|
21815
|
+
// src/contracts/openai.ts
|
|
21816
|
+
init_zod();
|
|
21817
|
+
var OpenAIMessageContent = external_exports.union([
|
|
21818
|
+
external_exports.string(),
|
|
21819
|
+
external_exports.array(
|
|
21820
|
+
external_exports.discriminatedUnion("type", [
|
|
21821
|
+
external_exports.object({ type: external_exports.literal("text"), text: external_exports.string() }),
|
|
21822
|
+
external_exports.object({
|
|
21823
|
+
type: external_exports.literal("image_url"),
|
|
21824
|
+
image_url: external_exports.object({ url: external_exports.string(), detail: external_exports.string().optional() })
|
|
21825
|
+
})
|
|
21826
|
+
])
|
|
21827
|
+
)
|
|
21828
|
+
]);
|
|
21829
|
+
var OpenAIMessage = external_exports.object({
|
|
21830
|
+
role: external_exports.enum(["system", "user", "assistant", "tool"]),
|
|
21831
|
+
content: OpenAIMessageContent,
|
|
21832
|
+
name: external_exports.string().optional(),
|
|
21833
|
+
reasoning_content: external_exports.string().optional(),
|
|
21834
|
+
thinking: external_exports.string().optional(),
|
|
21835
|
+
think: external_exports.string().optional()
|
|
21836
|
+
});
|
|
21837
|
+
var OpenAIFunctionCall = external_exports.object({
|
|
21838
|
+
name: external_exports.string(),
|
|
21839
|
+
arguments: external_exports.string()
|
|
21840
|
+
});
|
|
21841
|
+
var OpenAIToolCallSchema = external_exports.object({
|
|
21842
|
+
index: external_exports.number().optional(),
|
|
21843
|
+
id: external_exports.string().optional(),
|
|
21844
|
+
type: external_exports.literal("function").optional(),
|
|
21845
|
+
function: external_exports.object({
|
|
21846
|
+
name: external_exports.string().optional(),
|
|
21847
|
+
arguments: external_exports.string().optional()
|
|
21848
|
+
})
|
|
21849
|
+
});
|
|
21850
|
+
var OpenAIMessageWithFunctionCall = OpenAIMessage.extend({
|
|
21851
|
+
content: external_exports.union([external_exports.string(), external_exports.array(external_exports.object({ type: external_exports.literal("text"), text: external_exports.string() }))]).optional(),
|
|
21852
|
+
function_call: OpenAIFunctionCall.optional(),
|
|
21853
|
+
tool_calls: external_exports.array(OpenAIToolCallSchema).optional()
|
|
21854
|
+
});
|
|
21855
|
+
var OpenAIToolDefinition = external_exports.object({
|
|
21856
|
+
type: external_exports.literal("function"),
|
|
21857
|
+
function: external_exports.object({
|
|
21858
|
+
name: external_exports.string(),
|
|
21859
|
+
description: external_exports.string().optional(),
|
|
21860
|
+
parameters: external_exports.record(external_exports.string(), JsonValueSchema)
|
|
21861
|
+
})
|
|
21862
|
+
});
|
|
21863
|
+
var OpenAIRequestSchema = external_exports.object({
|
|
21864
|
+
model: external_exports.string(),
|
|
21865
|
+
messages: external_exports.array(OpenAIMessage),
|
|
21866
|
+
temperature: external_exports.number().optional(),
|
|
21867
|
+
max_tokens: external_exports.number().optional(),
|
|
21868
|
+
stream: external_exports.boolean().optional(),
|
|
21869
|
+
tools: external_exports.array(OpenAIToolDefinition).optional(),
|
|
21870
|
+
tool_choice: external_exports.union([
|
|
21871
|
+
external_exports.enum(["auto", "none", "required"]),
|
|
21872
|
+
external_exports.object({ type: external_exports.literal("auto") }),
|
|
21873
|
+
external_exports.object({ type: external_exports.literal("none") }),
|
|
21874
|
+
external_exports.object({ type: external_exports.literal("function"), function: external_exports.object({ name: external_exports.string() }) })
|
|
21875
|
+
]).optional(),
|
|
21876
|
+
user: external_exports.string().optional()
|
|
21877
|
+
});
|
|
21878
|
+
var OpenAIResponsesToolDefinition = external_exports.object({
|
|
21879
|
+
type: external_exports.string(),
|
|
21880
|
+
name: external_exports.string().optional(),
|
|
21881
|
+
description: external_exports.string().optional(),
|
|
21882
|
+
parameters: JsonValueSchema.optional(),
|
|
21883
|
+
function: external_exports.object({
|
|
21884
|
+
name: external_exports.string().optional(),
|
|
21885
|
+
description: external_exports.string().optional(),
|
|
21886
|
+
parameters: JsonValueSchema.optional()
|
|
21887
|
+
}).optional()
|
|
21888
|
+
}).passthrough();
|
|
21889
|
+
var OpenAIResponsesRequestSchema = external_exports.object({
|
|
21890
|
+
model: external_exports.string(),
|
|
21891
|
+
input: external_exports.unknown().optional(),
|
|
21892
|
+
instructions: external_exports.union([external_exports.string(), external_exports.null()]).optional(),
|
|
21893
|
+
stream: external_exports.boolean().optional(),
|
|
21894
|
+
tools: external_exports.array(OpenAIResponsesToolDefinition).optional(),
|
|
21895
|
+
tool_choice: external_exports.unknown().optional(),
|
|
21896
|
+
max_output_tokens: external_exports.number().optional(),
|
|
21897
|
+
temperature: external_exports.number().optional(),
|
|
21898
|
+
user: external_exports.string().optional(),
|
|
21899
|
+
metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
21900
|
+
}).passthrough();
|
|
21901
|
+
var OpenAIChoiceDelta = external_exports.object({
|
|
21902
|
+
role: external_exports.enum(["assistant"]).optional(),
|
|
21903
|
+
content: external_exports.string().nullable().optional(),
|
|
21904
|
+
reasoning_content: external_exports.string().nullable().optional(),
|
|
21905
|
+
thinking: external_exports.string().nullable().optional(),
|
|
21906
|
+
think: external_exports.string().nullable().optional(),
|
|
21907
|
+
function_call: external_exports.object({ name: external_exports.string().optional(), arguments: external_exports.string().optional() }).nullable().optional(),
|
|
21908
|
+
tool_calls: external_exports.array(
|
|
21909
|
+
external_exports.object({
|
|
21910
|
+
index: external_exports.number(),
|
|
21911
|
+
id: external_exports.string().optional(),
|
|
21912
|
+
type: external_exports.literal("function").optional(),
|
|
21913
|
+
function: external_exports.object({
|
|
21914
|
+
name: external_exports.string().optional(),
|
|
21915
|
+
arguments: external_exports.string().optional()
|
|
21916
|
+
})
|
|
21917
|
+
})
|
|
21918
|
+
).nullable().optional()
|
|
21919
|
+
});
|
|
21920
|
+
var OpenAIChoice = external_exports.object({
|
|
21921
|
+
index: external_exports.number(),
|
|
21922
|
+
message: external_exports.object({
|
|
21923
|
+
role: external_exports.enum(["assistant"]),
|
|
21924
|
+
content: external_exports.string().nullable(),
|
|
21925
|
+
reasoning_content: external_exports.string().optional(),
|
|
21926
|
+
thinking: external_exports.string().optional(),
|
|
21927
|
+
think: external_exports.string().optional(),
|
|
21928
|
+
function_call: external_exports.object({ name: external_exports.string(), arguments: external_exports.string() }).nullable().optional(),
|
|
21929
|
+
tool_calls: external_exports.array(OpenAIToolCallSchema).optional()
|
|
21930
|
+
}).optional(),
|
|
21931
|
+
delta: OpenAIChoiceDelta.optional(),
|
|
21932
|
+
finish_reason: external_exports.string().nullable()
|
|
21933
|
+
});
|
|
21934
|
+
var OpenAIResponseSchema = external_exports.object({
|
|
21935
|
+
id: external_exports.string(),
|
|
21936
|
+
object: external_exports.literal("chat.completion"),
|
|
21937
|
+
created: external_exports.number(),
|
|
21938
|
+
model: external_exports.string(),
|
|
21939
|
+
choices: external_exports.array(OpenAIChoice),
|
|
21940
|
+
usage: external_exports.object({
|
|
21941
|
+
prompt_tokens: external_exports.number().nullable().optional(),
|
|
21942
|
+
completion_tokens: external_exports.number().nullable().optional(),
|
|
21943
|
+
total_tokens: external_exports.number().nullable().optional()
|
|
21944
|
+
}).passthrough(),
|
|
21945
|
+
service_tier: external_exports.string().nullable().optional(),
|
|
21946
|
+
system_fingerprint: external_exports.string().nullable().optional()
|
|
21947
|
+
}).passthrough();
|
|
21948
|
+
var OpenAISSERawChunkSchema = external_exports.object({
|
|
21949
|
+
id: external_exports.string(),
|
|
21950
|
+
object: external_exports.literal("chat.completion.chunk"),
|
|
21951
|
+
created: external_exports.number(),
|
|
21952
|
+
model: external_exports.string(),
|
|
21953
|
+
choices: external_exports.array(
|
|
21954
|
+
external_exports.object({
|
|
21955
|
+
index: external_exports.number(),
|
|
21956
|
+
delta: OpenAIChoiceDelta,
|
|
21957
|
+
finish_reason: external_exports.string().nullable().optional()
|
|
21958
|
+
})
|
|
21959
|
+
),
|
|
21960
|
+
usage: external_exports.object({
|
|
21961
|
+
prompt_tokens: external_exports.number().nullable().optional(),
|
|
21962
|
+
completion_tokens: external_exports.number().nullable().optional(),
|
|
21963
|
+
total_tokens: external_exports.number().nullable().optional()
|
|
21964
|
+
}).passthrough().nullable().optional()
|
|
21965
|
+
}).passthrough();
|
|
21966
|
+
var OpenAIResponsesUsageSchema = external_exports.object({
|
|
21967
|
+
input_tokens: external_exports.number().nullable().optional(),
|
|
21968
|
+
output_tokens: external_exports.number().nullable().optional(),
|
|
21969
|
+
total_tokens: external_exports.number().nullable().optional(),
|
|
21970
|
+
input_tokens_details: external_exports.object({
|
|
21971
|
+
cached_tokens: external_exports.number().nullable().optional()
|
|
21972
|
+
}).passthrough().nullable().optional(),
|
|
21973
|
+
output_tokens_details: external_exports.object({
|
|
21974
|
+
reasoning_tokens: external_exports.number().nullable().optional()
|
|
21975
|
+
}).passthrough().nullable().optional()
|
|
21976
|
+
}).passthrough();
|
|
21977
|
+
var OpenAIResponsesOutputItemSchema = external_exports.object({
|
|
21978
|
+
type: external_exports.string()
|
|
21979
|
+
}).passthrough();
|
|
21980
|
+
var OpenAIResponsesResponseSchema = external_exports.object({
|
|
21981
|
+
id: external_exports.string(),
|
|
21982
|
+
object: external_exports.literal("response").optional(),
|
|
21983
|
+
created_at: external_exports.number().optional(),
|
|
21984
|
+
status: external_exports.string().nullable().optional(),
|
|
21985
|
+
model: external_exports.string().optional(),
|
|
21986
|
+
output: external_exports.array(OpenAIResponsesOutputItemSchema).optional(),
|
|
21987
|
+
output_text: external_exports.string().optional(),
|
|
21988
|
+
usage: OpenAIResponsesUsageSchema.nullable().optional(),
|
|
21989
|
+
error: external_exports.unknown().optional()
|
|
21990
|
+
}).passthrough();
|
|
21991
|
+
var OpenAIResponsesSSEventSchema = external_exports.object({
|
|
21992
|
+
type: external_exports.string()
|
|
21993
|
+
}).passthrough();
|
|
21994
|
+
|
|
21995
|
+
// src/proxy/schemas.ts
|
|
21996
|
+
var RequestModelSchema = external_exports.object({
|
|
21997
|
+
model: external_exports.string()
|
|
21998
|
+
});
|
|
21999
|
+
var LooseRequestSchema = external_exports.object({
|
|
22000
|
+
model: external_exports.string().optional(),
|
|
22001
|
+
metadata: external_exports.object({ user_id: external_exports.string().optional() }).passthrough().optional()
|
|
22002
|
+
});
|
|
22003
|
+
|
|
22004
|
+
// src/proxy/jsonlRecovery.ts
|
|
22005
|
+
init_privateDataPath();
|
|
22006
|
+
var READ_CHUNK_BYTES = 64 * 1024;
|
|
22007
|
+
var QUARANTINE_DIRECTORY = "quarantine";
|
|
22008
|
+
var activeContext = null;
|
|
22009
|
+
function emptyHealth() {
|
|
22010
|
+
return {
|
|
22011
|
+
level: "ok",
|
|
22012
|
+
diagnostic: null,
|
|
22013
|
+
repairedFiles: 0,
|
|
22014
|
+
delimiterRepairs: 0,
|
|
22015
|
+
quarantinedTails: 0
|
|
22016
|
+
};
|
|
22017
|
+
}
|
|
22018
|
+
function contextFor(logDir) {
|
|
22019
|
+
const root = resolve2(logDir);
|
|
22020
|
+
if (activeContext === null || activeContext.root !== root) {
|
|
22021
|
+
activeContext = {
|
|
22022
|
+
root,
|
|
22023
|
+
health: emptyHealth(),
|
|
22024
|
+
startup: null,
|
|
22025
|
+
startupComplete: false,
|
|
22026
|
+
safeFiles: /* @__PURE__ */ new Map()
|
|
22027
|
+
};
|
|
22028
|
+
}
|
|
22029
|
+
return activeContext;
|
|
22030
|
+
}
|
|
22031
|
+
function copyHealth(health) {
|
|
22032
|
+
return { ...health };
|
|
22033
|
+
}
|
|
22034
|
+
function hashBytes(value) {
|
|
22035
|
+
return createHash("sha256").update(value).digest("hex");
|
|
22036
|
+
}
|
|
22037
|
+
function signatureFromStats(stats) {
|
|
22038
|
+
return {
|
|
22039
|
+
size: stats.size,
|
|
22040
|
+
modifiedAt: stats.mtimeMs,
|
|
22041
|
+
changedAt: stats.ctimeMs,
|
|
22042
|
+
device: stats.dev,
|
|
22043
|
+
inode: stats.ino
|
|
22044
|
+
};
|
|
22045
|
+
}
|
|
22046
|
+
function signaturesMatch(left, right) {
|
|
22047
|
+
return left.size === right.size && left.modifiedAt === right.modifiedAt && left.changedAt === right.changedAt && left.device === right.device && left.inode === right.inode;
|
|
22048
|
+
}
|
|
22049
|
+
function validateRecord(bytes) {
|
|
22050
|
+
const withoutCarriageReturn = bytes.length > 0 && bytes[bytes.length - 1] === 13 ? bytes.subarray(0, -1) : bytes;
|
|
22051
|
+
const text = withoutCarriageReturn.toString("utf-8");
|
|
22052
|
+
if (text.trim() === "") return { valid: true };
|
|
22053
|
+
let parsed;
|
|
22054
|
+
try {
|
|
22055
|
+
parsed = JSON.parse(text);
|
|
22056
|
+
} catch {
|
|
22057
|
+
return { valid: false, reason: "invalid-json" };
|
|
22058
|
+
}
|
|
22059
|
+
return CapturedLogSchema.safeParse(parsed).success ? { valid: true } : { valid: false, reason: "invalid-log-schema" };
|
|
22060
|
+
}
|
|
22061
|
+
async function scanFile(handle, size) {
|
|
22062
|
+
const readBuffer = Buffer2.alloc(READ_CHUNK_BYTES);
|
|
22063
|
+
let position = 0;
|
|
22064
|
+
let pending = Buffer2.alloc(0);
|
|
22065
|
+
let pendingOffset = 0;
|
|
22066
|
+
let line = 1;
|
|
22067
|
+
while (position < size) {
|
|
22068
|
+
const length = Math.min(READ_CHUNK_BYTES, size - position);
|
|
22069
|
+
let bytesRead;
|
|
22070
|
+
try {
|
|
22071
|
+
const result = await handle.read(readBuffer, 0, length, position);
|
|
22072
|
+
bytesRead = result.bytesRead;
|
|
22073
|
+
} catch (error51) {
|
|
22074
|
+
return { status: "failed", reason: `read failed: ${String(error51)}` };
|
|
22075
|
+
}
|
|
22076
|
+
if (bytesRead === 0) {
|
|
22077
|
+
return { status: "failed", reason: "captured-log file changed while being scanned" };
|
|
22078
|
+
}
|
|
22079
|
+
const chunk = readBuffer.subarray(0, bytesRead);
|
|
22080
|
+
const combined = pending.length === 0 ? Buffer2.from(chunk) : Buffer2.concat([pending, chunk]);
|
|
22081
|
+
let consumed = 0;
|
|
22082
|
+
let newline = combined.indexOf(10, consumed);
|
|
22083
|
+
while (newline !== -1) {
|
|
22084
|
+
const record2 = combined.subarray(consumed, newline);
|
|
22085
|
+
const validation = validateRecord(record2);
|
|
22086
|
+
if (!validation.valid) {
|
|
22087
|
+
return {
|
|
22088
|
+
status: "interior-corruption",
|
|
22089
|
+
line,
|
|
22090
|
+
byteOffset: pendingOffset + consumed,
|
|
22091
|
+
recordHash: hashBytes(record2),
|
|
22092
|
+
reason: validation.reason
|
|
22093
|
+
};
|
|
22094
|
+
}
|
|
22095
|
+
consumed = newline + 1;
|
|
22096
|
+
line += 1;
|
|
22097
|
+
newline = combined.indexOf(10, consumed);
|
|
22098
|
+
}
|
|
22099
|
+
pending = Buffer2.from(combined.subarray(consumed));
|
|
22100
|
+
pendingOffset += consumed;
|
|
22101
|
+
position += bytesRead;
|
|
22102
|
+
}
|
|
22103
|
+
return { status: "complete", pending, pendingOffset, nextLine: line };
|
|
22104
|
+
}
|
|
22105
|
+
async function syncDirectory(directory) {
|
|
22106
|
+
let handle = null;
|
|
22107
|
+
try {
|
|
22108
|
+
handle = await open(directory, "r");
|
|
22109
|
+
await handle.sync();
|
|
22110
|
+
} catch {
|
|
22111
|
+
} finally {
|
|
22112
|
+
await handle?.close().catch(() => void 0);
|
|
22113
|
+
}
|
|
22114
|
+
}
|
|
22115
|
+
async function removeTemporaryFile(filePath) {
|
|
22116
|
+
await unlink(filePath).catch(() => void 0);
|
|
22117
|
+
}
|
|
22118
|
+
function readOwnProperty(value, name) {
|
|
22119
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
22120
|
+
return Object.getOwnPropertyDescriptor(value, name)?.value;
|
|
22121
|
+
}
|
|
22122
|
+
async function inspectExistingQuarantine(filePath, expected) {
|
|
22123
|
+
if (!existsSync2(filePath)) return "missing";
|
|
22124
|
+
try {
|
|
22125
|
+
const parsed = JSON.parse(await readFile(filePath, "utf-8"));
|
|
22126
|
+
return readOwnProperty(parsed, "schemaVersion") === expected.schemaVersion && readOwnProperty(parsed, "reason") === expected.reason && readOwnProperty(parsed, "sourceFile") === expected.sourceFile && readOwnProperty(parsed, "sourcePathHash") === expected.sourcePathHash && readOwnProperty(parsed, "tailOffset") === expected.tailOffset && readOwnProperty(parsed, "tailBytes") === expected.tailBytes && readOwnProperty(parsed, "tailSha256") === expected.tailSha256 && readOwnProperty(parsed, "tailBase64") === expected.tailBase64 ? "matching" : "conflicting";
|
|
22127
|
+
} catch {
|
|
22128
|
+
return "conflicting";
|
|
22129
|
+
}
|
|
22130
|
+
}
|
|
22131
|
+
async function quarantineTail(context, sourceFile, tail, tailOffset) {
|
|
22132
|
+
const quarantineDirectory = join3(context.root, QUARANTINE_DIRECTORY);
|
|
22133
|
+
if (!ensurePrivateDirectorySync(quarantineDirectory)) return null;
|
|
22134
|
+
const tailSha256 = hashBytes(tail);
|
|
22135
|
+
const sourceFileName = basename(sourceFile);
|
|
22136
|
+
const destination = join3(quarantineDirectory, `${sourceFileName}.tail-${tailSha256}.json`);
|
|
22137
|
+
const temporary = join3(
|
|
22138
|
+
quarantineDirectory,
|
|
22139
|
+
`.${sourceFileName}.${String(process.pid)}.${randomUUID()}.tmp`
|
|
22140
|
+
);
|
|
22141
|
+
const record2 = {
|
|
22142
|
+
schemaVersion: 1,
|
|
22143
|
+
reason: "invalid-final-jsonl-record",
|
|
22144
|
+
sourceFile: sourceFileName,
|
|
22145
|
+
sourcePathHash: hashBytes(Buffer2.from(resolve2(sourceFile), "utf-8")),
|
|
22146
|
+
recoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22147
|
+
tailOffset,
|
|
22148
|
+
tailBytes: tail.length,
|
|
22149
|
+
tailSha256,
|
|
22150
|
+
tailBase64: tail.toString("base64")
|
|
22151
|
+
};
|
|
22152
|
+
const existing = await inspectExistingQuarantine(destination, record2);
|
|
22153
|
+
if (existing === "matching") {
|
|
22154
|
+
return securePrivateFileSync(destination) ? destination : null;
|
|
22155
|
+
}
|
|
22156
|
+
if (existing === "conflicting") return null;
|
|
22157
|
+
let handle = null;
|
|
22158
|
+
try {
|
|
22159
|
+
handle = await open(temporary, "wx", PRIVATE_FILE_MODE);
|
|
22160
|
+
await handle.writeFile(`${JSON.stringify(record2)}
|
|
22161
|
+
`, "utf-8");
|
|
22162
|
+
await handle.sync();
|
|
22163
|
+
await handle.close();
|
|
22164
|
+
handle = null;
|
|
22165
|
+
if (!securePrivateFileSync(temporary)) {
|
|
22166
|
+
await removeTemporaryFile(temporary);
|
|
22167
|
+
return null;
|
|
22168
|
+
}
|
|
22169
|
+
await link(temporary, destination);
|
|
22170
|
+
await unlink(temporary);
|
|
22171
|
+
if (!securePrivateFileSync(destination)) return null;
|
|
22172
|
+
await syncDirectory(quarantineDirectory);
|
|
22173
|
+
return destination;
|
|
22174
|
+
} catch {
|
|
22175
|
+
await handle?.close().catch(() => void 0);
|
|
22176
|
+
await removeTemporaryFile(temporary);
|
|
22177
|
+
const racedExisting = await inspectExistingQuarantine(destination, record2);
|
|
22178
|
+
return racedExisting === "matching" && securePrivateFileSync(destination) ? destination : null;
|
|
22179
|
+
}
|
|
22180
|
+
}
|
|
22181
|
+
async function recoverFile(context, filePath) {
|
|
22182
|
+
if (!existsSync2(filePath)) {
|
|
22183
|
+
context.safeFiles.delete(filePath);
|
|
22184
|
+
return {
|
|
22185
|
+
status: "clean",
|
|
22186
|
+
signature: { size: 0, modifiedAt: 0, changedAt: 0, device: 0, inode: 0 }
|
|
22187
|
+
};
|
|
22188
|
+
}
|
|
22189
|
+
let fileStats;
|
|
22190
|
+
try {
|
|
22191
|
+
fileStats = await lstat(filePath);
|
|
22192
|
+
} catch (error51) {
|
|
22193
|
+
return { status: "failed", reason: `stat failed: ${String(error51)}` };
|
|
22194
|
+
}
|
|
22195
|
+
if (fileStats.isSymbolicLink() || !fileStats.isFile()) {
|
|
22196
|
+
return { status: "failed", reason: "captured-log path is not a regular private file" };
|
|
22197
|
+
}
|
|
22198
|
+
const initialSignature = signatureFromStats(fileStats);
|
|
22199
|
+
const safeSignature = context.safeFiles.get(filePath);
|
|
22200
|
+
if (safeSignature !== void 0 && signaturesMatch(safeSignature, initialSignature)) {
|
|
22201
|
+
return { status: "clean", signature: initialSignature };
|
|
22202
|
+
}
|
|
22203
|
+
let handle = null;
|
|
22204
|
+
try {
|
|
22205
|
+
handle = await open(filePath, constants2.O_RDWR | constants2.O_NOFOLLOW);
|
|
22206
|
+
const openedStats = await handle.stat();
|
|
22207
|
+
if (!openedStats.isFile()) {
|
|
22208
|
+
return { status: "failed", reason: "captured-log handle is not a regular file" };
|
|
22209
|
+
}
|
|
22210
|
+
const openedSignature = signatureFromStats(openedStats);
|
|
22211
|
+
if (!signaturesMatch(initialSignature, openedSignature)) {
|
|
22212
|
+
return {
|
|
22213
|
+
status: "failed",
|
|
22214
|
+
reason: "captured-log path changed between metadata validation and open"
|
|
22215
|
+
};
|
|
22216
|
+
}
|
|
22217
|
+
const scan = await scanFile(handle, openedStats.size);
|
|
22218
|
+
const scannedSignature = signatureFromStats(await handle.stat());
|
|
22219
|
+
if (!signaturesMatch(openedSignature, scannedSignature)) {
|
|
22220
|
+
return {
|
|
22221
|
+
status: "failed",
|
|
22222
|
+
reason: "captured-log file changed while recovery was scanning it"
|
|
22223
|
+
};
|
|
22224
|
+
}
|
|
22225
|
+
switch (scan.status) {
|
|
22226
|
+
case "failed":
|
|
22227
|
+
return scan;
|
|
22228
|
+
case "interior-corruption":
|
|
22229
|
+
return scan;
|
|
22230
|
+
case "complete": {
|
|
22231
|
+
if (scan.pending.length === 0) {
|
|
22232
|
+
return { status: "clean", signature: signatureFromStats(await handle.stat()) };
|
|
22233
|
+
}
|
|
22234
|
+
const validation = validateRecord(scan.pending);
|
|
22235
|
+
if (validation.valid) {
|
|
22236
|
+
const write = await handle.write(Buffer2.from("\n", "utf-8"), 0, 1, openedStats.size);
|
|
22237
|
+
if (write.bytesWritten !== 1) {
|
|
22238
|
+
return { status: "failed", reason: "captured-log delimiter append was incomplete" };
|
|
22239
|
+
}
|
|
22240
|
+
await handle.sync();
|
|
22241
|
+
return {
|
|
22242
|
+
status: "delimiter-appended",
|
|
22243
|
+
signature: signatureFromStats(await handle.stat())
|
|
22244
|
+
};
|
|
22245
|
+
}
|
|
22246
|
+
const quarantinePath = await quarantineTail(
|
|
22247
|
+
context,
|
|
22248
|
+
filePath,
|
|
22249
|
+
scan.pending,
|
|
22250
|
+
scan.pendingOffset
|
|
22251
|
+
);
|
|
22252
|
+
if (quarantinePath === null) {
|
|
22253
|
+
return { status: "failed", reason: "invalid tail could not be privately quarantined" };
|
|
22254
|
+
}
|
|
22255
|
+
const beforeTruncate = signatureFromStats(await handle.stat());
|
|
22256
|
+
if (!signaturesMatch(openedSignature, beforeTruncate)) {
|
|
22257
|
+
return {
|
|
22258
|
+
status: "failed",
|
|
22259
|
+
reason: "captured-log file changed before invalid-tail truncation"
|
|
22260
|
+
};
|
|
22261
|
+
}
|
|
22262
|
+
await handle.truncate(scan.pendingOffset);
|
|
22263
|
+
await handle.sync();
|
|
22264
|
+
return {
|
|
22265
|
+
status: "tail-quarantined",
|
|
22266
|
+
signature: signatureFromStats(await handle.stat())
|
|
22267
|
+
};
|
|
22268
|
+
}
|
|
22269
|
+
}
|
|
22270
|
+
return { status: "failed", reason: "unsupported captured-log scan state" };
|
|
22271
|
+
} catch (error51) {
|
|
22272
|
+
return { status: "failed", reason: String(error51) };
|
|
22273
|
+
} finally {
|
|
22274
|
+
await handle?.close().catch(() => void 0);
|
|
22275
|
+
}
|
|
22276
|
+
}
|
|
22277
|
+
function applyFileResult(context, filePath, result) {
|
|
22278
|
+
const file2 = basename(filePath);
|
|
22279
|
+
switch (result.status) {
|
|
22280
|
+
case "clean":
|
|
22281
|
+
context.safeFiles.set(filePath, result.signature);
|
|
22282
|
+
return;
|
|
22283
|
+
case "delimiter-appended":
|
|
22284
|
+
context.safeFiles.set(filePath, result.signature);
|
|
22285
|
+
context.health.delimiterRepairs += 1;
|
|
22286
|
+
context.health.repairedFiles += 1;
|
|
22287
|
+
return;
|
|
22288
|
+
case "tail-quarantined":
|
|
22289
|
+
context.safeFiles.set(filePath, result.signature);
|
|
22290
|
+
context.health.quarantinedTails += 1;
|
|
22291
|
+
context.health.repairedFiles += 1;
|
|
22292
|
+
if (context.health.level !== "unavailable") {
|
|
22293
|
+
context.health.level = "degraded";
|
|
22294
|
+
context.health.diagnostic = `Recovered and quarantined an invalid JSONL tail in ${file2}`;
|
|
22295
|
+
}
|
|
22296
|
+
return;
|
|
22297
|
+
case "interior-corruption":
|
|
22298
|
+
context.safeFiles.delete(filePath);
|
|
22299
|
+
context.health.level = "unavailable";
|
|
22300
|
+
context.health.diagnostic = `JSONL corruption in ${file2} at line ${String(result.line)} (offset ${String(result.byteOffset)}, ${result.reason}, sha256 ${result.recordHash})`;
|
|
22301
|
+
return;
|
|
22302
|
+
case "failed":
|
|
22303
|
+
context.safeFiles.delete(filePath);
|
|
22304
|
+
context.health.level = "unavailable";
|
|
22305
|
+
context.health.diagnostic = `JSONL recovery failed for ${file2}: ${result.reason}`;
|
|
22306
|
+
}
|
|
22307
|
+
}
|
|
22308
|
+
async function runStartupRecovery(context) {
|
|
22309
|
+
if (!ensurePrivateDirectorySync(context.root)) {
|
|
22310
|
+
context.health.level = "unavailable";
|
|
22311
|
+
context.health.diagnostic = "Captured-log directory could not be made private";
|
|
22312
|
+
return copyHealth(context.health);
|
|
22313
|
+
}
|
|
22314
|
+
let entries;
|
|
22315
|
+
try {
|
|
22316
|
+
entries = await readdir(context.root);
|
|
22317
|
+
} catch (error51) {
|
|
22318
|
+
context.health.level = "unavailable";
|
|
22319
|
+
context.health.diagnostic = `Captured-log directory could not be listed: ${String(error51)}`;
|
|
22320
|
+
return copyHealth(context.health);
|
|
22321
|
+
}
|
|
22322
|
+
const files = entries.filter((entry) => entry.endsWith(".jsonl")).sort();
|
|
22323
|
+
for (const file2 of files) {
|
|
22324
|
+
const filePath = join3(context.root, file2);
|
|
22325
|
+
const result = await recoverFile(context, filePath);
|
|
22326
|
+
applyFileResult(context, filePath, result);
|
|
22327
|
+
if (context.health.level === "unavailable") break;
|
|
22328
|
+
}
|
|
22329
|
+
return copyHealth(context.health);
|
|
22330
|
+
}
|
|
22331
|
+
async function recoverCapturedLogStorage(logDir) {
|
|
22332
|
+
const context = contextFor(logDir);
|
|
22333
|
+
if (context.startup === null) {
|
|
22334
|
+
context.startup = runStartupRecovery(context).then((health) => {
|
|
22335
|
+
context.startupComplete = true;
|
|
22336
|
+
return health;
|
|
22337
|
+
});
|
|
22338
|
+
}
|
|
22339
|
+
return await context.startup;
|
|
22340
|
+
}
|
|
22341
|
+
function canAppendCapturedLogFileOnExit(logDir, filePath) {
|
|
22342
|
+
const context = contextFor(logDir);
|
|
22343
|
+
if (!context.startupComplete || context.health.level === "unavailable") return false;
|
|
22344
|
+
const trusted = context.safeFiles.get(filePath);
|
|
22345
|
+
if (trusted === void 0) return false;
|
|
22346
|
+
try {
|
|
22347
|
+
const observed = lstatSync2(filePath);
|
|
22348
|
+
if (observed === void 0 || observed.isSymbolicLink() || !observed.isFile()) return false;
|
|
22349
|
+
return signaturesMatch(trusted, signatureFromStats(observed));
|
|
22350
|
+
} catch {
|
|
22351
|
+
return false;
|
|
22352
|
+
}
|
|
22353
|
+
}
|
|
22354
|
+
|
|
22355
|
+
// src/proxy/logger.ts
|
|
22356
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
22357
|
+
var DEFAULT_RETENTION_DAYS = 7;
|
|
22358
|
+
var DEFAULT_APP_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
22359
|
+
var DEFAULT_APP_LOG_MAX_FILES = 5;
|
|
22360
|
+
function writeStderrFallback(message, error51) {
|
|
22361
|
+
process.stderr.write(`${message} ${String(error51)}
|
|
22362
|
+
`);
|
|
22363
|
+
}
|
|
22364
|
+
function resolveLogDir() {
|
|
22365
|
+
const logDirEnv = process.env["LOG_DIR"];
|
|
22366
|
+
if (logDirEnv !== void 0 && logDirEnv !== "") {
|
|
22367
|
+
return path.isAbsolute(logDirEnv) ? logDirEnv : path.join(getDataDir(), logDirEnv);
|
|
22368
|
+
}
|
|
22369
|
+
return path.join(getDataDir(), "logs");
|
|
22370
|
+
}
|
|
22371
|
+
function getLogFilePath() {
|
|
22372
|
+
const date5 = /* @__PURE__ */ new Date();
|
|
22373
|
+
const yyyy = date5.getUTCFullYear();
|
|
22374
|
+
const mm = String(date5.getUTCMonth() + 1).padStart(2, "0");
|
|
22375
|
+
const dd = String(date5.getUTCDate()).padStart(2, "0");
|
|
22376
|
+
return path.join(resolveLogDir(), `${yyyy}-${mm}-${dd}.jsonl`);
|
|
22377
|
+
}
|
|
22378
|
+
function getInspectorLogPath() {
|
|
22379
|
+
const logFileEnv = process.env["AGENT_INSPECTOR_LOG_FILE"];
|
|
22380
|
+
if (logFileEnv !== void 0 && logFileEnv !== "") {
|
|
22381
|
+
return logFileEnv;
|
|
22382
|
+
}
|
|
21073
22383
|
return path.join(getDataDir(), "logs", "inspector.log");
|
|
21074
22384
|
}
|
|
21075
22385
|
function readBoundedInteger(name, fallback, minimum, maximum) {
|
|
@@ -21109,14 +22419,14 @@ function getRotatedGeneration(entry, baseName) {
|
|
|
21109
22419
|
async function listApplicationLogFiles(logPath) {
|
|
21110
22420
|
const directory = path.dirname(logPath);
|
|
21111
22421
|
const baseName = path.basename(logPath);
|
|
21112
|
-
const entries = await
|
|
22422
|
+
const entries = await readdir2(directory);
|
|
21113
22423
|
const files = [];
|
|
21114
22424
|
for (const entry of entries) {
|
|
21115
22425
|
const generation = entry === baseName ? 0 : getRotatedGeneration(entry, baseName);
|
|
21116
22426
|
if (generation === null) continue;
|
|
21117
22427
|
const filePath = path.join(directory, entry);
|
|
21118
22428
|
try {
|
|
21119
|
-
const fileStat = await
|
|
22429
|
+
const fileStat = await stat2(filePath);
|
|
21120
22430
|
if (!fileStat.isFile()) continue;
|
|
21121
22431
|
files.push({ generation, modifiedAt: fileStat.mtimeMs, path: filePath });
|
|
21122
22432
|
} catch {
|
|
@@ -21126,12 +22436,12 @@ async function listApplicationLogFiles(logPath) {
|
|
|
21126
22436
|
}
|
|
21127
22437
|
async function removeApplicationLog(filePath) {
|
|
21128
22438
|
try {
|
|
21129
|
-
await
|
|
22439
|
+
await stat2(filePath);
|
|
21130
22440
|
} catch {
|
|
21131
22441
|
return true;
|
|
21132
22442
|
}
|
|
21133
22443
|
try {
|
|
21134
|
-
await
|
|
22444
|
+
await unlink2(filePath);
|
|
21135
22445
|
return true;
|
|
21136
22446
|
} catch {
|
|
21137
22447
|
return false;
|
|
@@ -21164,7 +22474,7 @@ async function maintainApplicationLogs(logPath, policy) {
|
|
|
21164
22474
|
}
|
|
21165
22475
|
async function moveApplicationLog(source, destination) {
|
|
21166
22476
|
try {
|
|
21167
|
-
await
|
|
22477
|
+
await stat2(source);
|
|
21168
22478
|
} catch {
|
|
21169
22479
|
return true;
|
|
21170
22480
|
}
|
|
@@ -21197,7 +22507,7 @@ async function rotateApplicationLog(logPath, maxFiles) {
|
|
|
21197
22507
|
}
|
|
21198
22508
|
async function rotateApplicationLogIfNeeded(logPath, incomingBytes, policy) {
|
|
21199
22509
|
try {
|
|
21200
|
-
const fileStat = await
|
|
22510
|
+
const fileStat = await stat2(logPath);
|
|
21201
22511
|
if (fileStat.size > 0 && fileStat.size + incomingBytes > policy.maxBytes) {
|
|
21202
22512
|
return await rotateApplicationLog(logPath, policy.maxFiles);
|
|
21203
22513
|
}
|
|
@@ -21216,16 +22526,24 @@ async function initLogger() {
|
|
|
21216
22526
|
} else if (!securePrivateTreeSync(dir)) {
|
|
21217
22527
|
writeStderrFallback("[logger] Failed to secure existing log files:", dir);
|
|
21218
22528
|
} else {
|
|
21219
|
-
const
|
|
21220
|
-
|
|
21221
|
-
|
|
21222
|
-
|
|
21223
|
-
|
|
21224
|
-
|
|
21225
|
-
|
|
21226
|
-
|
|
22529
|
+
const recovery = await recoverCapturedLogStorage(dir);
|
|
22530
|
+
if (recovery.level === "unavailable") {
|
|
22531
|
+
writeStderrFallback(
|
|
22532
|
+
"[logger] Captured-log recovery blocked readiness:",
|
|
22533
|
+
recovery.diagnostic ?? "unknown recovery failure"
|
|
22534
|
+
);
|
|
22535
|
+
} else {
|
|
22536
|
+
const entries = await readdir2(dir);
|
|
22537
|
+
for (const entry of entries) {
|
|
22538
|
+
if (!entry.endsWith(".jsonl")) continue;
|
|
22539
|
+
const fullPath = path.join(dir, entry);
|
|
22540
|
+
try {
|
|
22541
|
+
const s = await stat2(fullPath);
|
|
22542
|
+
if (s.mtimeMs < cutoff) {
|
|
22543
|
+
await unlink2(fullPath);
|
|
22544
|
+
}
|
|
22545
|
+
} catch {
|
|
21227
22546
|
}
|
|
21228
|
-
} catch {
|
|
21229
22547
|
}
|
|
21230
22548
|
}
|
|
21231
22549
|
}
|
|
@@ -21252,7 +22570,7 @@ async function appendApplicationLogLine(logPath, line, policy) {
|
|
|
21252
22570
|
return { ok: false, reason: "application log directory is not private" };
|
|
21253
22571
|
}
|
|
21254
22572
|
try {
|
|
21255
|
-
if (!await rotateApplicationLogIfNeeded(logPath,
|
|
22573
|
+
if (!await rotateApplicationLogIfNeeded(logPath, Buffer3.byteLength(line, "utf-8"), policy)) {
|
|
21256
22574
|
return { ok: false, reason: "application log rotation failed" };
|
|
21257
22575
|
}
|
|
21258
22576
|
await appendFile(logPath, line, { encoding: "utf-8", mode: PRIVATE_FILE_MODE });
|
|
@@ -21309,7 +22627,24 @@ process.on("exit", () => {
|
|
|
21309
22627
|
try {
|
|
21310
22628
|
const filePath = getLogFilePath();
|
|
21311
22629
|
if (!ensurePrivateDirectorySync(path.dirname(filePath))) return;
|
|
21312
|
-
|
|
22630
|
+
if (!canAppendCapturedLogFileOnExit(resolveLogDir(), filePath)) return;
|
|
22631
|
+
const descriptor = openSync2(
|
|
22632
|
+
filePath,
|
|
22633
|
+
constants3.O_WRONLY | constants3.O_APPEND | constants3.O_NOFOLLOW,
|
|
22634
|
+
PRIVATE_FILE_MODE
|
|
22635
|
+
);
|
|
22636
|
+
try {
|
|
22637
|
+
const payload = Buffer3.from(toWrite, "utf-8");
|
|
22638
|
+
let written = 0;
|
|
22639
|
+
while (written < payload.length) {
|
|
22640
|
+
const writeCount = writeSync(descriptor, payload, written, payload.length - written);
|
|
22641
|
+
if (writeCount === 0) return;
|
|
22642
|
+
written += writeCount;
|
|
22643
|
+
}
|
|
22644
|
+
fsyncSync(descriptor);
|
|
22645
|
+
} finally {
|
|
22646
|
+
closeSync2(descriptor);
|
|
22647
|
+
}
|
|
21313
22648
|
void securePrivateFileSync(filePath);
|
|
21314
22649
|
} catch {
|
|
21315
22650
|
}
|
|
@@ -21323,15 +22658,15 @@ var RemotePortSchema = external_exports.number().int().min(1).max(65535);
|
|
|
21323
22658
|
var SocketSchema = external_exports.object({
|
|
21324
22659
|
remotePort: RemotePortSchema.nullish()
|
|
21325
22660
|
}).passthrough();
|
|
21326
|
-
function parseCommandLine(commandLine,
|
|
22661
|
+
function parseCommandLine(commandLine, sep3) {
|
|
21327
22662
|
if (!commandLine) return { cwd: null, projectFolder: null };
|
|
21328
22663
|
const exeMatch = commandLine.match(/^"([^"]+)"/) || commandLine.match(/^([^\s]+)/);
|
|
21329
22664
|
if (exeMatch?.[1] !== void 0) {
|
|
21330
22665
|
const exePath = exeMatch[1];
|
|
21331
|
-
const lastSep = exePath.lastIndexOf(
|
|
22666
|
+
const lastSep = exePath.lastIndexOf(sep3);
|
|
21332
22667
|
if (lastSep > 0) {
|
|
21333
22668
|
const exeDir = exePath.substring(0, lastSep);
|
|
21334
|
-
return { cwd: exeDir, projectFolder: exeDir.split(
|
|
22669
|
+
return { cwd: exeDir, projectFolder: exeDir.split(sep3).pop() ?? null };
|
|
21335
22670
|
}
|
|
21336
22671
|
}
|
|
21337
22672
|
return { cwd: null, projectFolder: null };
|
|
@@ -21357,7 +22692,7 @@ async function lookupProcessInfo(pid) {
|
|
|
21357
22692
|
if (commandLine.length > 0) return parseCommandLine(commandLine, "\\");
|
|
21358
22693
|
} else {
|
|
21359
22694
|
try {
|
|
21360
|
-
const raw = await
|
|
22695
|
+
const raw = await readFile2(`/proc/${String(pid)}/cmdline`, "utf8");
|
|
21361
22696
|
const commandLine = raw.replace(/\0/g, " ").trim();
|
|
21362
22697
|
if (commandLine) {
|
|
21363
22698
|
return parseCommandLine(commandLine, "/");
|
|
@@ -21473,6 +22808,88 @@ async function lookupPidByLocalPort(clientPort) {
|
|
|
21473
22808
|
}
|
|
21474
22809
|
}
|
|
21475
22810
|
|
|
22811
|
+
// src/proxy/uiAssetServer.ts
|
|
22812
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
22813
|
+
import { extname, relative, resolve as resolve3, sep as sep2 } from "node:path";
|
|
22814
|
+
var CONTENT_TYPES = {
|
|
22815
|
+
".css": "text/css; charset=utf-8",
|
|
22816
|
+
".html": "text/html; charset=utf-8",
|
|
22817
|
+
".ico": "image/x-icon",
|
|
22818
|
+
".jpeg": "image/jpeg",
|
|
22819
|
+
".jpg": "image/jpeg",
|
|
22820
|
+
".js": "text/javascript; charset=utf-8",
|
|
22821
|
+
".json": "application/json; charset=utf-8",
|
|
22822
|
+
".map": "application/json; charset=utf-8",
|
|
22823
|
+
".png": "image/png",
|
|
22824
|
+
".svg": "image/svg+xml",
|
|
22825
|
+
".webp": "image/webp",
|
|
22826
|
+
".woff": "font/woff",
|
|
22827
|
+
".woff2": "font/woff2"
|
|
22828
|
+
};
|
|
22829
|
+
function safeDecodedPath(pathname) {
|
|
22830
|
+
let decoded;
|
|
22831
|
+
try {
|
|
22832
|
+
decoded = decodeURIComponent(pathname);
|
|
22833
|
+
} catch {
|
|
22834
|
+
return null;
|
|
22835
|
+
}
|
|
22836
|
+
if (decoded.includes("\\") || decoded.includes("\0")) return null;
|
|
22837
|
+
if (decoded.split("/").some((segment) => segment === "." || segment === "..")) return null;
|
|
22838
|
+
return decoded;
|
|
22839
|
+
}
|
|
22840
|
+
function isInsideDirectory(path2, directory) {
|
|
22841
|
+
const child = relative(directory, path2);
|
|
22842
|
+
return child === "" || !child.startsWith("..") && !child.startsWith(sep2);
|
|
22843
|
+
}
|
|
22844
|
+
function assetPath(uiDirectory, pathname) {
|
|
22845
|
+
const decoded = safeDecodedPath(pathname);
|
|
22846
|
+
if (decoded === null) return null;
|
|
22847
|
+
const target = resolve3(uiDirectory, `.${decoded}`);
|
|
22848
|
+
return isInsideDirectory(target, resolve3(uiDirectory)) ? target : null;
|
|
22849
|
+
}
|
|
22850
|
+
async function readAsset(path2) {
|
|
22851
|
+
try {
|
|
22852
|
+
return await readFile3(path2);
|
|
22853
|
+
} catch {
|
|
22854
|
+
return null;
|
|
22855
|
+
}
|
|
22856
|
+
}
|
|
22857
|
+
function contentTypeFor(path2) {
|
|
22858
|
+
return CONTENT_TYPES[extname(path2).toLowerCase()] ?? "application/octet-stream";
|
|
22859
|
+
}
|
|
22860
|
+
function isBackendNamespace(pathname) {
|
|
22861
|
+
return pathname === "/api" || pathname.startsWith("/api/") || pathname === "/proxy" || pathname.startsWith("/proxy/") || pathname === "/livez" || pathname === "/readyz";
|
|
22862
|
+
}
|
|
22863
|
+
function shouldUseDocumentFallback(pathname) {
|
|
22864
|
+
if (pathname === "/" || pathname.length === 0) return true;
|
|
22865
|
+
const lastSegment = pathname.split("/").at(-1) ?? "";
|
|
22866
|
+
return !lastSegment.includes(".");
|
|
22867
|
+
}
|
|
22868
|
+
async function loadUiAsset(uiDirectory, pathname) {
|
|
22869
|
+
if (isBackendNamespace(pathname)) return null;
|
|
22870
|
+
const requestedPath = assetPath(uiDirectory, pathname);
|
|
22871
|
+
if (requestedPath === null) return null;
|
|
22872
|
+
const requestedBody = await readAsset(requestedPath);
|
|
22873
|
+
if (requestedBody !== null) {
|
|
22874
|
+
return {
|
|
22875
|
+
body: requestedBody,
|
|
22876
|
+
cacheControl: pathname.startsWith("/assets/") ? "public, max-age=31536000, immutable" : "no-cache",
|
|
22877
|
+
contentType: contentTypeFor(requestedPath),
|
|
22878
|
+
document: extname(requestedPath).toLowerCase() === ".html"
|
|
22879
|
+
};
|
|
22880
|
+
}
|
|
22881
|
+
if (!shouldUseDocumentFallback(pathname)) return null;
|
|
22882
|
+
const indexPath = resolve3(uiDirectory, "index.html");
|
|
22883
|
+
const indexBody = await readAsset(indexPath);
|
|
22884
|
+
if (indexBody === null) return null;
|
|
22885
|
+
return {
|
|
22886
|
+
body: indexBody,
|
|
22887
|
+
cacheControl: "no-store",
|
|
22888
|
+
contentType: CONTENT_TYPES[".html"] ?? "text/html; charset=utf-8",
|
|
22889
|
+
document: true
|
|
22890
|
+
};
|
|
22891
|
+
}
|
|
22892
|
+
|
|
21476
22893
|
// src/proxy/identityProxy.ts
|
|
21477
22894
|
var IDENTITY_HEADERS = {
|
|
21478
22895
|
pid: "x-agent-inspector-client-pid",
|
|
@@ -21489,26 +22906,19 @@ var ALIAS_CONTROL_MAX_REQUEST_BYTES = 8 * 1024;
|
|
|
21489
22906
|
var IDENTITY_PROXY_CONTROL_NAMESPACE = "/.agent-inspector";
|
|
21490
22907
|
var IDENTITY_PROXY_ALIAS_CONTROL_PATH = `${IDENTITY_PROXY_CONTROL_NAMESPACE}/base-path-aliases`;
|
|
21491
22908
|
function normalizedAliasCandidate(value) {
|
|
21492
|
-
const normalized =
|
|
21493
|
-
if (normalized
|
|
21494
|
-
return {
|
|
21495
|
-
ok: false,
|
|
21496
|
-
code: "invalid",
|
|
21497
|
-
error: "Base Path aliases must be non-root paths such as /inspector."
|
|
21498
|
-
};
|
|
21499
|
-
}
|
|
21500
|
-
if (!/^\/[A-Za-z0-9._~-]+(?:\/[A-Za-z0-9._~-]+)*$/u.test(normalized)) {
|
|
22909
|
+
const normalized = normalizePublicBasePath(value);
|
|
22910
|
+
if (normalized === null) {
|
|
21501
22911
|
return {
|
|
21502
22912
|
ok: false,
|
|
21503
22913
|
code: "invalid",
|
|
21504
22914
|
error: "Base Path aliases may contain only URL-safe path segments."
|
|
21505
22915
|
};
|
|
21506
22916
|
}
|
|
21507
|
-
if (normalized.
|
|
22917
|
+
if (normalized.length === 0) {
|
|
21508
22918
|
return {
|
|
21509
22919
|
ok: false,
|
|
21510
22920
|
code: "invalid",
|
|
21511
|
-
error: "Base Path aliases
|
|
22921
|
+
error: "Base Path aliases must be non-root paths such as /inspector."
|
|
21512
22922
|
};
|
|
21513
22923
|
}
|
|
21514
22924
|
if (normalized === IDENTITY_PROXY_CONTROL_NAMESPACE || normalized.startsWith(`${IDENTITY_PROXY_CONTROL_NAMESPACE}/`)) {
|
|
@@ -21537,7 +22947,7 @@ function basePathsOverlap(left, right) {
|
|
|
21537
22947
|
return pathMatchesBasePath(left, right) || pathMatchesBasePath(right, left);
|
|
21538
22948
|
}
|
|
21539
22949
|
function createBasePathAliasRegistry(canonicalBasePath, initialAliases = []) {
|
|
21540
|
-
const canonical =
|
|
22950
|
+
const canonical = normalizePublicBasePath(canonicalBasePath) ?? "";
|
|
21541
22951
|
const aliases = [];
|
|
21542
22952
|
const add = (value) => {
|
|
21543
22953
|
const candidate = normalizedAliasCandidate(value);
|
|
@@ -21596,7 +23006,7 @@ function createBasePathAliasRegistry(canonicalBasePath, initialAliases = []) {
|
|
|
21596
23006
|
aliases.splice(index, 1);
|
|
21597
23007
|
return { ok: true, basePath: candidate.basePath };
|
|
21598
23008
|
};
|
|
21599
|
-
const
|
|
23009
|
+
const resolve5 = (rawPath) => {
|
|
21600
23010
|
const requestUrl = new URL(rawPath, "http://localhost");
|
|
21601
23011
|
const alias = aliases.find((candidate) => pathMatchesBasePath(requestUrl.pathname, candidate));
|
|
21602
23012
|
const effectiveBasePath = alias ?? canonical;
|
|
@@ -21611,7 +23021,7 @@ function createBasePathAliasRegistry(canonicalBasePath, initialAliases = []) {
|
|
|
21611
23021
|
add,
|
|
21612
23022
|
list: () => [...aliases],
|
|
21613
23023
|
remove,
|
|
21614
|
-
resolve:
|
|
23024
|
+
resolve: resolve5
|
|
21615
23025
|
};
|
|
21616
23026
|
for (const alias of initialAliases) add(alias);
|
|
21617
23027
|
return registry2;
|
|
@@ -21664,8 +23074,9 @@ function htmlAttributeEscape(value) {
|
|
|
21664
23074
|
}
|
|
21665
23075
|
function rewriteRootRelativePublicPath(value, effectiveBasePath, sourceBasePath) {
|
|
21666
23076
|
if (!value.startsWith("/") || value.startsWith("//")) return value;
|
|
21667
|
-
const normalizedEffective =
|
|
21668
|
-
const normalizedSource =
|
|
23077
|
+
const normalizedEffective = normalizePublicBasePath(effectiveBasePath);
|
|
23078
|
+
const normalizedSource = normalizePublicBasePath(sourceBasePath);
|
|
23079
|
+
if (normalizedEffective === null || normalizedSource === null) return value;
|
|
21669
23080
|
const suffixIndex = value.search(/[?#]/u);
|
|
21670
23081
|
const pathname = suffixIndex === -1 ? value : value.slice(0, suffixIndex);
|
|
21671
23082
|
const suffix = suffixIndex === -1 ? "" : value.slice(suffixIndex);
|
|
@@ -21677,14 +23088,19 @@ function rewriteRootRelativePublicPath(value, effectiveBasePath, sourceBasePath)
|
|
|
21677
23088
|
const trailingSlash = pathname.endsWith("/") && !rewritten.endsWith("/") ? "/" : "";
|
|
21678
23089
|
return `${rewritten}${trailingSlash}${suffix}`;
|
|
21679
23090
|
}
|
|
21680
|
-
function rewriteQuotedAssetPaths(source, effectiveBasePath, sourceBasePath) {
|
|
23091
|
+
function rewriteQuotedAssetPaths(source, effectiveBasePath, sourceBasePath, escape) {
|
|
21681
23092
|
return source.replace(
|
|
21682
23093
|
/(["'])(\/(?:[^"'/]+\/)*assets\/)/gu,
|
|
21683
|
-
(_match, quote, assetPrefix) => `${quote}${
|
|
23094
|
+
(_match, quote, assetPrefix) => `${quote}${escape(
|
|
23095
|
+
rewriteRootRelativePublicPath(assetPrefix, effectiveBasePath, sourceBasePath),
|
|
23096
|
+
quote
|
|
23097
|
+
)}`
|
|
21684
23098
|
);
|
|
21685
23099
|
}
|
|
21686
23100
|
function rewriteHtmlForBasePath(html, basePath, sourceBasePath = "") {
|
|
21687
|
-
const normalized =
|
|
23101
|
+
const normalized = normalizePublicBasePath(basePath);
|
|
23102
|
+
const normalizedSource = normalizePublicBasePath(sourceBasePath);
|
|
23103
|
+
if (normalized === null || normalizedSource === null) return html;
|
|
21688
23104
|
const escaped = htmlAttributeEscape(normalized);
|
|
21689
23105
|
const meta3 = `<meta name="${AGENT_INSPECTOR_BASE_PATH_META_NAME}" content="${escaped}">`;
|
|
21690
23106
|
const metaPattern = new RegExp(
|
|
@@ -21700,29 +23116,112 @@ function rewriteHtmlForBasePath(html, basePath, sourceBasePath = "") {
|
|
|
21700
23116
|
const withMeta = replacedMeta ? withReplacedMeta : withReplacedMeta.replace(/<head(\s[^>]*)?>/iu, (match) => `${match}${meta3}`);
|
|
21701
23117
|
const withRewrittenAttributes = withMeta.replace(
|
|
21702
23118
|
/\b(href|src|action)=(["'])(\/(?!\/)[^"']*)\2/giu,
|
|
21703
|
-
(_match, attribute, quote, path2) => `${attribute}=${quote}${
|
|
23119
|
+
(_match, attribute, quote, path2) => `${attribute}=${quote}${htmlAttributeEscape(
|
|
23120
|
+
rewriteRootRelativePublicPath(path2, normalized, normalizedSource)
|
|
23121
|
+
)}${quote}`
|
|
23122
|
+
);
|
|
23123
|
+
return rewriteQuotedAssetPaths(
|
|
23124
|
+
withRewrittenAttributes,
|
|
23125
|
+
normalized,
|
|
23126
|
+
normalizedSource,
|
|
23127
|
+
(value) => htmlAttributeEscape(value)
|
|
21704
23128
|
);
|
|
21705
|
-
return rewriteQuotedAssetPaths(withRewrittenAttributes, normalized, sourceBasePath);
|
|
21706
23129
|
}
|
|
21707
|
-
function javaScriptStringEscape(value) {
|
|
21708
|
-
|
|
23130
|
+
function javaScriptStringEscape(value, quote) {
|
|
23131
|
+
const escaped = value.replace(/\\/gu, "\\\\").replace(/\r/gu, "\\r").replace(/\n/gu, "\\n").replace(/\u2028/gu, "\\u2028").replace(/\u2029/gu, "\\u2029").replace(/&/gu, "\\u0026").replace(/</gu, "\\u003c").replace(/>/gu, "\\u003e");
|
|
23132
|
+
return quote === "'" ? escaped.replace(/'/gu, "\\'") : escaped.replace(/"/gu, '\\"');
|
|
21709
23133
|
}
|
|
21710
23134
|
function rewriteJavaScriptForBasePath(source, basePath, sourceBasePath = "") {
|
|
21711
|
-
const normalized =
|
|
21712
|
-
const
|
|
23135
|
+
const normalized = normalizePublicBasePath(basePath);
|
|
23136
|
+
const normalizedSource = normalizePublicBasePath(sourceBasePath);
|
|
23137
|
+
if (normalized === null || normalizedSource === null) return source;
|
|
23138
|
+
const escaped = javaScriptStringEscape(normalized, '"');
|
|
21713
23139
|
const withRouterBasePath = source.replace(
|
|
21714
23140
|
/\bbasepath:\s*(["'])[^"']*\1/gu,
|
|
21715
23141
|
`basepath:"${escaped}"`
|
|
21716
23142
|
);
|
|
21717
23143
|
const withDynamicPaths = withRouterBasePath.replace(
|
|
21718
23144
|
/return(["'])(\/(?!\/)[^"']*)\1\+([A-Za-z_$][\w$]*)/gu,
|
|
21719
|
-
(_match, quote, path2, variable) =>
|
|
21720
|
-
path2,
|
|
21721
|
-
|
|
21722
|
-
|
|
21723
|
-
|
|
23145
|
+
(_match, quote, path2, variable) => {
|
|
23146
|
+
const rewritten = rewriteRootRelativePublicPath(path2, normalized, normalizedSource);
|
|
23147
|
+
return `return${quote}${javaScriptStringEscape(rewritten, quote)}${quote}+${variable}`;
|
|
23148
|
+
}
|
|
23149
|
+
);
|
|
23150
|
+
return rewriteQuotedAssetPaths(
|
|
23151
|
+
withDynamicPaths,
|
|
23152
|
+
normalized,
|
|
23153
|
+
normalizedSource,
|
|
23154
|
+
javaScriptStringEscape
|
|
21724
23155
|
);
|
|
21725
|
-
|
|
23156
|
+
}
|
|
23157
|
+
function publicOriginForRequest(req, options) {
|
|
23158
|
+
if (options.publicOrigin !== void 0) {
|
|
23159
|
+
try {
|
|
23160
|
+
return new URL(options.publicOrigin).origin;
|
|
23161
|
+
} catch {
|
|
23162
|
+
}
|
|
23163
|
+
}
|
|
23164
|
+
return `http://${req.headers.host ?? "localhost"}`;
|
|
23165
|
+
}
|
|
23166
|
+
function browserRuntimeForRequest(req, options, basePath) {
|
|
23167
|
+
const origin = publicOriginForRequest(req, options);
|
|
23168
|
+
const normalized = normalizePublicBasePath(basePath) ?? "";
|
|
23169
|
+
const baseUrl = normalized.length === 0 ? origin : `${origin}${normalized}`;
|
|
23170
|
+
return {
|
|
23171
|
+
schemaVersion: BROWSER_RUNTIME_SCHEMA_VERSION,
|
|
23172
|
+
deploymentMode: "composed",
|
|
23173
|
+
ui: { basePath: normalized.length === 0 ? "/" : normalized },
|
|
23174
|
+
control: { baseUrl },
|
|
23175
|
+
agent: { baseUrl },
|
|
23176
|
+
capabilities: {
|
|
23177
|
+
rest: true,
|
|
23178
|
+
sse: true,
|
|
23179
|
+
proxy: true,
|
|
23180
|
+
mcp: true
|
|
23181
|
+
}
|
|
23182
|
+
};
|
|
23183
|
+
}
|
|
23184
|
+
function safeInlineJson(value) {
|
|
23185
|
+
return JSON.stringify(value).replace(/&/gu, "\\u0026").replace(/</gu, "\\u003c").replace(/>/gu, "\\u003e").replace(/\u2028/gu, "\\u2028").replace(/\u2029/gu, "\\u2029");
|
|
23186
|
+
}
|
|
23187
|
+
function injectBrowserRuntime(html, runtime) {
|
|
23188
|
+
const runtimeJson = safeInlineJson(runtime);
|
|
23189
|
+
if (html.includes(BROWSER_RUNTIME_JSON_PLACEHOLDER)) {
|
|
23190
|
+
return html.replaceAll(BROWSER_RUNTIME_JSON_PLACEHOLDER, runtimeJson);
|
|
23191
|
+
}
|
|
23192
|
+
const bootstrap = `<script>window.__AGENT_INSPECTOR_RUNTIME__=${runtimeJson};</script>`;
|
|
23193
|
+
return html.replace(/<head(\s[^>]*)?>/iu, (head) => `${head}${bootstrap}`);
|
|
23194
|
+
}
|
|
23195
|
+
async function tryServeUiAsset(req, res, options, pathname, basePath, sourceBasePath) {
|
|
23196
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
23197
|
+
if (method !== "GET" && method !== "HEAD" || options.uiDirectory === void 0) return false;
|
|
23198
|
+
const asset = await loadUiAsset(options.uiDirectory, pathname);
|
|
23199
|
+
if (asset === null) return false;
|
|
23200
|
+
let body = asset.body;
|
|
23201
|
+
if (asset.document) {
|
|
23202
|
+
const rewritten = rewriteHtmlForBasePath(body.toString("utf8"), basePath, sourceBasePath);
|
|
23203
|
+
body = Buffer4.from(
|
|
23204
|
+
injectBrowserRuntime(rewritten, browserRuntimeForRequest(req, options, basePath)),
|
|
23205
|
+
"utf8"
|
|
23206
|
+
);
|
|
23207
|
+
} else if (basePath.length > 0 && asset.contentType.includes("javascript")) {
|
|
23208
|
+
body = Buffer4.from(
|
|
23209
|
+
rewriteJavaScriptForBasePath(body.toString("utf8"), basePath, sourceBasePath),
|
|
23210
|
+
"utf8"
|
|
23211
|
+
);
|
|
23212
|
+
}
|
|
23213
|
+
const headers = {
|
|
23214
|
+
...BASE_SECURITY_HEADERS,
|
|
23215
|
+
"cache-control": asset.cacheControl,
|
|
23216
|
+
"content-length": body.byteLength,
|
|
23217
|
+
"content-type": asset.contentType
|
|
23218
|
+
};
|
|
23219
|
+
if (asset.document) headers["content-security-policy"] = DOCUMENT_CONTENT_SECURITY_POLICY;
|
|
23220
|
+
res.writeHead(200, headers);
|
|
23221
|
+
if (method === "HEAD") res.end();
|
|
23222
|
+
else res.end(body);
|
|
23223
|
+
req.resume();
|
|
23224
|
+
return true;
|
|
21726
23225
|
}
|
|
21727
23226
|
function isLoopbackPeer(address) {
|
|
21728
23227
|
if (address === void 0) return false;
|
|
@@ -21736,14 +23235,14 @@ function bearerToken(authorization) {
|
|
|
21736
23235
|
}
|
|
21737
23236
|
function tokensMatch(received, expected) {
|
|
21738
23237
|
if (received === null || expected === void 0 || expected === "") return false;
|
|
21739
|
-
const receivedBuffer =
|
|
21740
|
-
const expectedBuffer =
|
|
23238
|
+
const receivedBuffer = Buffer4.from(received);
|
|
23239
|
+
const expectedBuffer = Buffer4.from(expected);
|
|
21741
23240
|
return receivedBuffer.byteLength === expectedBuffer.byteLength && timingSafeEqual(receivedBuffer, expectedBuffer);
|
|
21742
23241
|
}
|
|
21743
23242
|
function managedInstanceTokensMatch(received, expected) {
|
|
21744
23243
|
if (received === void 0 || received.length === 0 || expected.length === 0) return false;
|
|
21745
|
-
const receivedDigest =
|
|
21746
|
-
const expectedDigest =
|
|
23244
|
+
const receivedDigest = createHash2("sha256").update(received).digest();
|
|
23245
|
+
const expectedDigest = createHash2("sha256").update(expected).digest();
|
|
21747
23246
|
return timingSafeEqual(receivedDigest, expectedDigest);
|
|
21748
23247
|
}
|
|
21749
23248
|
function firstHeader(value) {
|
|
@@ -21759,13 +23258,42 @@ function normalizedHostname(value) {
|
|
|
21759
23258
|
}
|
|
21760
23259
|
}
|
|
21761
23260
|
function isLoopbackHostname(hostname3) {
|
|
21762
|
-
|
|
23261
|
+
if (hostname3 === "localhost" || hostname3 === "::1") return true;
|
|
23262
|
+
if (isIP2(hostname3) !== 4) return false;
|
|
23263
|
+
return hostname3.split(".", 1)[0] === "127";
|
|
23264
|
+
}
|
|
23265
|
+
function parseUrl(value) {
|
|
23266
|
+
try {
|
|
23267
|
+
return new URL(value);
|
|
23268
|
+
} catch {
|
|
23269
|
+
return null;
|
|
23270
|
+
}
|
|
23271
|
+
}
|
|
23272
|
+
function parseUiOriginAllowlist(value) {
|
|
23273
|
+
const origins = [];
|
|
23274
|
+
const rejected = [];
|
|
23275
|
+
for (const entry of (value ?? "").split(",")) {
|
|
23276
|
+
const candidate = entry.trim();
|
|
23277
|
+
if (candidate.length === 0) continue;
|
|
23278
|
+
const parsed = parseUrl(candidate);
|
|
23279
|
+
const valid = parsed !== null && (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username.length === 0 && parsed.password.length === 0 && parsed.pathname === "/" && parsed.search.length === 0 && parsed.hash.length === 0 && isLoopbackHostname(parsed.hostname.toLowerCase().replace(/^\[|\]$/gu, ""));
|
|
23280
|
+
if (!valid || parsed === null) {
|
|
23281
|
+
rejected.push(candidate);
|
|
23282
|
+
continue;
|
|
23283
|
+
}
|
|
23284
|
+
if (!origins.includes(parsed.origin)) origins.push(parsed.origin);
|
|
23285
|
+
}
|
|
23286
|
+
return { origins, rejected };
|
|
23287
|
+
}
|
|
23288
|
+
function normalizeTrustedPublicOrigin(value) {
|
|
23289
|
+
if (value === void 0) return void 0;
|
|
23290
|
+
return normalizedRequestOrigin(value.trim());
|
|
21763
23291
|
}
|
|
21764
23292
|
function isTrustedIdentityProxyHost(host, options) {
|
|
21765
23293
|
if (host === void 0) return false;
|
|
21766
23294
|
const hostname3 = normalizedHostname(host);
|
|
21767
23295
|
if (hostname3 === null) return false;
|
|
21768
|
-
if (isLoopbackHostname(hostname3) ||
|
|
23296
|
+
if (isLoopbackHostname(hostname3) || isIP2(hostname3) !== 0) return true;
|
|
21769
23297
|
const listenHostname = normalizedHostname(options.listenHost);
|
|
21770
23298
|
if (listenHostname !== null && hostname3 === listenHostname) return true;
|
|
21771
23299
|
return (options.trustedHosts ?? []).some(
|
|
@@ -21776,6 +23304,83 @@ function isSafeMethod(method) {
|
|
|
21776
23304
|
const normalized = (method ?? "GET").toUpperCase();
|
|
21777
23305
|
return normalized === "GET" || normalized === "HEAD" || normalized === "OPTIONS";
|
|
21778
23306
|
}
|
|
23307
|
+
var UI_CORS_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
|
|
23308
|
+
var UI_CORS_HEADERS = ["authorization", "content-type", "last-event-id", CSRF_HEADER];
|
|
23309
|
+
function normalizedRequestOrigin(origin) {
|
|
23310
|
+
try {
|
|
23311
|
+
const parsed = new URL(origin);
|
|
23312
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
23313
|
+
if (parsed.username.length > 0 || parsed.password.length > 0) return null;
|
|
23314
|
+
if (parsed.pathname !== "/" || parsed.search.length > 0 || parsed.hash.length > 0) return null;
|
|
23315
|
+
return parsed.origin;
|
|
23316
|
+
} catch {
|
|
23317
|
+
return null;
|
|
23318
|
+
}
|
|
23319
|
+
}
|
|
23320
|
+
function isSameRequestOrigin(origin, host, publicOrigin) {
|
|
23321
|
+
const parsedOrigin = normalizedRequestOrigin(origin);
|
|
23322
|
+
if (parsedOrigin === null) return false;
|
|
23323
|
+
const expected = normalizedRequestOrigin(publicOrigin ?? `http://${host ?? "localhost"}`);
|
|
23324
|
+
return expected !== null && parsedOrigin === expected;
|
|
23325
|
+
}
|
|
23326
|
+
function matchedUiOrigin(origin, allowedUiOrigins) {
|
|
23327
|
+
if (origin === void 0) return null;
|
|
23328
|
+
return (allowedUiOrigins ?? []).find((candidate) => candidate === origin) ?? null;
|
|
23329
|
+
}
|
|
23330
|
+
function isUiControlPath(pathname) {
|
|
23331
|
+
if (pathname === "/livez" || pathname === "/readyz") return true;
|
|
23332
|
+
if (pathname === "/api/mcp") return false;
|
|
23333
|
+
return pathname === "/api" || pathname.startsWith("/api/");
|
|
23334
|
+
}
|
|
23335
|
+
function isModelProxyPath(pathname) {
|
|
23336
|
+
return pathname === "/proxy" || pathname.startsWith("/proxy/");
|
|
23337
|
+
}
|
|
23338
|
+
function uiCorsHeaders(origin) {
|
|
23339
|
+
if (origin === null) return {};
|
|
23340
|
+
return {
|
|
23341
|
+
"access-control-allow-origin": origin,
|
|
23342
|
+
"access-control-expose-headers": "Content-Disposition",
|
|
23343
|
+
vary: "Origin"
|
|
23344
|
+
};
|
|
23345
|
+
}
|
|
23346
|
+
function appendVaryHeader(existing, name) {
|
|
23347
|
+
const values = (Array.isArray(existing) ? existing.join(",") : existing ?? "").split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
23348
|
+
if (!values.some((value) => value.toLowerCase() === name.toLowerCase())) values.push(name);
|
|
23349
|
+
return values.join(", ");
|
|
23350
|
+
}
|
|
23351
|
+
function requestedCorsHeaders(value) {
|
|
23352
|
+
return (value ?? "").split(",").map((header) => header.trim().toLowerCase()).filter((header) => header.length > 0);
|
|
23353
|
+
}
|
|
23354
|
+
function handleUiCorsPreflight(req, res, origin) {
|
|
23355
|
+
const requestedMethod = firstHeader(req.headers["access-control-request-method"])?.toUpperCase();
|
|
23356
|
+
const requestedHeaders = requestedCorsHeaders(
|
|
23357
|
+
firstHeader(req.headers["access-control-request-headers"])
|
|
23358
|
+
);
|
|
23359
|
+
const methodAllowed = requestedMethod !== void 0 && UI_CORS_METHODS.some((method) => method === requestedMethod);
|
|
23360
|
+
const headersAllowed = requestedHeaders.every(
|
|
23361
|
+
(header) => UI_CORS_HEADERS.some((allowedHeader) => allowedHeader === header)
|
|
23362
|
+
);
|
|
23363
|
+
if (!methodAllowed || !headersAllowed) {
|
|
23364
|
+
res.writeHead(403, {
|
|
23365
|
+
"content-type": "application/json; charset=utf-8",
|
|
23366
|
+
"cache-control": "no-store",
|
|
23367
|
+
...uiCorsHeaders(origin)
|
|
23368
|
+
});
|
|
23369
|
+
res.end(JSON.stringify({ error: "CORS preflight is not allowed for this request." }));
|
|
23370
|
+
req.resume();
|
|
23371
|
+
return;
|
|
23372
|
+
}
|
|
23373
|
+
res.writeHead(204, {
|
|
23374
|
+
"cache-control": "no-store",
|
|
23375
|
+
"access-control-allow-origin": origin,
|
|
23376
|
+
"access-control-allow-methods": UI_CORS_METHODS.join(", "),
|
|
23377
|
+
"access-control-allow-headers": UI_CORS_HEADERS.join(", "),
|
|
23378
|
+
"access-control-max-age": "600",
|
|
23379
|
+
vary: "Origin, Access-Control-Request-Method, Access-Control-Request-Headers"
|
|
23380
|
+
});
|
|
23381
|
+
res.end();
|
|
23382
|
+
req.resume();
|
|
23383
|
+
}
|
|
21779
23384
|
function isPublicHealthPath(pathname) {
|
|
21780
23385
|
return pathname === "/livez" || pathname === "/readyz" || pathname === "/api/health";
|
|
21781
23386
|
}
|
|
@@ -21797,16 +23402,21 @@ function isBrowserMutationAllowed(input) {
|
|
|
21797
23402
|
new URL(input.path, "http://localhost").pathname,
|
|
21798
23403
|
input.basePath ?? getConfiguredBasePath(process.env)
|
|
21799
23404
|
);
|
|
21800
|
-
if (pathname === "/proxy" || pathname.startsWith("/proxy/")) return true;
|
|
21801
23405
|
const fetchSite = input.secFetchSite?.toLowerCase();
|
|
21802
23406
|
const browserRequest = input.origin !== void 0 || fetchSite !== void 0;
|
|
21803
23407
|
if (!browserRequest) return true;
|
|
21804
|
-
if (
|
|
23408
|
+
if (isModelProxyPath(pathname)) {
|
|
23409
|
+
if (fetchSite !== void 0 && fetchSite !== "same-origin") return false;
|
|
23410
|
+
if (input.origin !== void 0 && !isSameRequestOrigin(input.origin, input.host, input.publicOrigin)) {
|
|
23411
|
+
return false;
|
|
23412
|
+
}
|
|
23413
|
+
return input.csrfToken === "1";
|
|
23414
|
+
}
|
|
23415
|
+
const allowedOrigin = matchedUiOrigin(input.origin, input.allowedUiOrigins);
|
|
23416
|
+
if (fetchSite !== void 0 && fetchSite !== "same-origin" && allowedOrigin === null)
|
|
23417
|
+
return false;
|
|
21805
23418
|
if (input.origin !== void 0) {
|
|
21806
|
-
|
|
21807
|
-
const originHost = new URL(input.origin).host.toLowerCase();
|
|
21808
|
-
if (originHost !== input.host?.toLowerCase()) return false;
|
|
21809
|
-
} catch {
|
|
23419
|
+
if (!isSameRequestOrigin(input.origin, input.host, input.publicOrigin) && allowedOrigin === null) {
|
|
21810
23420
|
return false;
|
|
21811
23421
|
}
|
|
21812
23422
|
}
|
|
@@ -21845,14 +23455,14 @@ function readAliasControlBody(req) {
|
|
|
21845
23455
|
error: `Alias control body exceeds ${String(ALIAS_CONTROL_MAX_REQUEST_BYTES)} bytes`
|
|
21846
23456
|
});
|
|
21847
23457
|
}
|
|
21848
|
-
return new Promise((
|
|
23458
|
+
return new Promise((resolve5) => {
|
|
21849
23459
|
const chunks = [];
|
|
21850
23460
|
let receivedBytes = 0;
|
|
21851
23461
|
let settled = false;
|
|
21852
23462
|
const finish = (result) => {
|
|
21853
23463
|
if (settled) return;
|
|
21854
23464
|
settled = true;
|
|
21855
|
-
|
|
23465
|
+
resolve5(result);
|
|
21856
23466
|
};
|
|
21857
23467
|
req.on("data", (chunk) => {
|
|
21858
23468
|
if (settled) return;
|
|
@@ -21869,7 +23479,7 @@ function readAliasControlBody(req) {
|
|
|
21869
23479
|
chunks.push(buffer);
|
|
21870
23480
|
});
|
|
21871
23481
|
req.on("end", () => {
|
|
21872
|
-
finish({ ok: true, body:
|
|
23482
|
+
finish({ ok: true, body: Buffer4.concat(chunks).toString("utf8") });
|
|
21873
23483
|
});
|
|
21874
23484
|
req.on("aborted", () => {
|
|
21875
23485
|
finish({ ok: false, status: 400, error: "Client aborted alias control request" });
|
|
@@ -22040,7 +23650,28 @@ function isIdentityProxyControlPath(pathname) {
|
|
|
22040
23650
|
return pathname === IDENTITY_PROXY_CONTROL_NAMESPACE || pathname.startsWith(`${IDENTITY_PROXY_CONTROL_NAMESPACE}/`);
|
|
22041
23651
|
}
|
|
22042
23652
|
function startIdentityProxy(options) {
|
|
22043
|
-
|
|
23653
|
+
const allowedUiOrigins = parseUiOriginAllowlist(options.allowedUiOrigins?.join(","));
|
|
23654
|
+
if (allowedUiOrigins.rejected.length > 0) {
|
|
23655
|
+
return Promise.reject(
|
|
23656
|
+
new Error(
|
|
23657
|
+
`Unsafe UI origins are not accepted: ${allowedUiOrigins.rejected.join(", ")}. Only exact loopback HTTP(S) origins are supported.`
|
|
23658
|
+
)
|
|
23659
|
+
);
|
|
23660
|
+
}
|
|
23661
|
+
const publicOrigin = normalizeTrustedPublicOrigin(options.publicOrigin);
|
|
23662
|
+
if (publicOrigin === null) {
|
|
23663
|
+
return Promise.reject(
|
|
23664
|
+
new Error(
|
|
23665
|
+
"publicOrigin must be an exact HTTP(S) origin without credentials, path, or query."
|
|
23666
|
+
)
|
|
23667
|
+
);
|
|
23668
|
+
}
|
|
23669
|
+
const effectiveOptions = {
|
|
23670
|
+
...options,
|
|
23671
|
+
allowedUiOrigins: allowedUiOrigins.origins,
|
|
23672
|
+
publicOrigin
|
|
23673
|
+
};
|
|
23674
|
+
return new Promise((resolve5, reject) => {
|
|
22044
23675
|
const server = http.createServer();
|
|
22045
23676
|
const aliasRegistry = options.aliasRegistry ?? createBasePathAliasRegistry(getConfiguredBasePath(process.env));
|
|
22046
23677
|
const runtimeState = {
|
|
@@ -22079,7 +23710,7 @@ function startIdentityProxy(options) {
|
|
|
22079
23710
|
};
|
|
22080
23711
|
res.once("finish", release);
|
|
22081
23712
|
res.once("close", release);
|
|
22082
|
-
void handleRequest(req, res,
|
|
23713
|
+
void handleRequest(req, res, effectiveOptions, aliasRegistry).catch((err) => {
|
|
22083
23714
|
const message = err instanceof Error ? err.message : String(err);
|
|
22084
23715
|
if (!res.headersSent) {
|
|
22085
23716
|
res.writeHead(502, { "content-type": "text/plain" });
|
|
@@ -22090,7 +23721,7 @@ function startIdentityProxy(options) {
|
|
|
22090
23721
|
server.once("error", reject);
|
|
22091
23722
|
server.listen(options.listenPort, options.listenHost, () => {
|
|
22092
23723
|
server.removeListener("error", reject);
|
|
22093
|
-
|
|
23724
|
+
resolve5(server);
|
|
22094
23725
|
});
|
|
22095
23726
|
});
|
|
22096
23727
|
}
|
|
@@ -22101,18 +23732,18 @@ function stopIdentityProxyAdmission(server) {
|
|
|
22101
23732
|
async function waitForIdentityProxyDrain(server) {
|
|
22102
23733
|
const state = identityProxyRuntimeStates.get(server);
|
|
22103
23734
|
if (state === void 0 || state.activeRequests === 0) return;
|
|
22104
|
-
await new Promise((
|
|
22105
|
-
state.drainWaiters.add(
|
|
23735
|
+
await new Promise((resolve5) => {
|
|
23736
|
+
state.drainWaiters.add(resolve5);
|
|
22106
23737
|
});
|
|
22107
23738
|
}
|
|
22108
23739
|
async function closeIdentityProxyGracefully(server) {
|
|
22109
23740
|
stopIdentityProxyAdmission(server);
|
|
22110
|
-
const closed = new Promise((
|
|
23741
|
+
const closed = new Promise((resolve5) => {
|
|
22111
23742
|
if (!server.listening) {
|
|
22112
|
-
|
|
23743
|
+
resolve5();
|
|
22113
23744
|
return;
|
|
22114
23745
|
}
|
|
22115
|
-
server.close(() =>
|
|
23746
|
+
server.close(() => resolve5());
|
|
22116
23747
|
});
|
|
22117
23748
|
await Promise.all([closed, waitForIdentityProxyDrain(server)]);
|
|
22118
23749
|
}
|
|
@@ -22148,6 +23779,48 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22148
23779
|
const resolution = resolveBasePathAliasRequest(aliasRegistry, req.url ?? "/");
|
|
22149
23780
|
const basePath = resolution.effectiveBasePath;
|
|
22150
23781
|
const upstreamPath = resolution.upstreamPath;
|
|
23782
|
+
const upstreamPathname = new URL(upstreamPath, "http://localhost").pathname;
|
|
23783
|
+
const requestOrigin = req.headers.origin;
|
|
23784
|
+
const requestPublicOrigin = publicOriginForRequest(req, options);
|
|
23785
|
+
const crossOriginRequest = requestOrigin !== void 0 && !isSameRequestOrigin(requestOrigin, req.headers.host, requestPublicOrigin);
|
|
23786
|
+
const allowedUiOrigin = crossOriginRequest ? matchedUiOrigin(requestOrigin, options.allowedUiOrigins) : null;
|
|
23787
|
+
if (crossOriginRequest && isModelProxyPath(upstreamPathname)) {
|
|
23788
|
+
res.writeHead(403, {
|
|
23789
|
+
"content-type": "application/json; charset=utf-8",
|
|
23790
|
+
"cache-control": "no-store"
|
|
23791
|
+
});
|
|
23792
|
+
res.end(
|
|
23793
|
+
JSON.stringify({ error: "Cross-origin browser access to the model proxy is forbidden." })
|
|
23794
|
+
);
|
|
23795
|
+
req.resume();
|
|
23796
|
+
return;
|
|
23797
|
+
}
|
|
23798
|
+
if (crossOriginRequest && upstreamPathname === "/api/mcp") {
|
|
23799
|
+
res.writeHead(403, {
|
|
23800
|
+
"content-type": "application/json; charset=utf-8",
|
|
23801
|
+
"cache-control": "no-store"
|
|
23802
|
+
});
|
|
23803
|
+
res.end(JSON.stringify({ error: "Browser CORS is not enabled for the MCP endpoint." }));
|
|
23804
|
+
req.resume();
|
|
23805
|
+
return;
|
|
23806
|
+
}
|
|
23807
|
+
if (crossOriginRequest && isUiControlPath(upstreamPathname)) {
|
|
23808
|
+
if (allowedUiOrigin === null) {
|
|
23809
|
+
res.writeHead(403, {
|
|
23810
|
+
"content-type": "application/json; charset=utf-8",
|
|
23811
|
+
"cache-control": "no-store"
|
|
23812
|
+
});
|
|
23813
|
+
res.end(
|
|
23814
|
+
JSON.stringify({ error: "UI origin is not allowed by this Agent Inspector runtime." })
|
|
23815
|
+
);
|
|
23816
|
+
req.resume();
|
|
23817
|
+
return;
|
|
23818
|
+
}
|
|
23819
|
+
if ((req.method ?? "GET").toUpperCase() === "OPTIONS") {
|
|
23820
|
+
handleUiCorsPreflight(req, res, allowedUiOrigin);
|
|
23821
|
+
return;
|
|
23822
|
+
}
|
|
23823
|
+
}
|
|
22151
23824
|
if (basePath.length > 0 && requestUrl.pathname.startsWith("/assets/")) {
|
|
22152
23825
|
res.writeHead(307, {
|
|
22153
23826
|
location: `${basePath}${requestUrl.pathname}${requestUrl.search}`,
|
|
@@ -22164,11 +23837,14 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22164
23837
|
host: req.headers.host,
|
|
22165
23838
|
origin: req.headers.origin,
|
|
22166
23839
|
secFetchSite: firstHeader(req.headers["sec-fetch-site"]),
|
|
22167
|
-
csrfToken: firstHeader(req.headers[CSRF_HEADER])
|
|
23840
|
+
csrfToken: firstHeader(req.headers[CSRF_HEADER]),
|
|
23841
|
+
allowedUiOrigins: options.allowedUiOrigins,
|
|
23842
|
+
publicOrigin: requestPublicOrigin
|
|
22168
23843
|
})) {
|
|
22169
23844
|
res.writeHead(403, {
|
|
22170
23845
|
"content-type": "application/json; charset=utf-8",
|
|
22171
|
-
"cache-control": "no-store"
|
|
23846
|
+
"cache-control": "no-store",
|
|
23847
|
+
...uiCorsHeaders(allowedUiOrigin)
|
|
22172
23848
|
});
|
|
22173
23849
|
res.end(JSON.stringify({ error: "Browser mutation requires same-origin CSRF proof" }));
|
|
22174
23850
|
req.resume();
|
|
@@ -22185,7 +23861,8 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22185
23861
|
})) {
|
|
22186
23862
|
res.writeHead(403, {
|
|
22187
23863
|
"content-type": "application/json; charset=utf-8",
|
|
22188
|
-
"cache-control": "no-store"
|
|
23864
|
+
"cache-control": "no-store",
|
|
23865
|
+
...uiCorsHeaders(allowedUiOrigin)
|
|
22189
23866
|
});
|
|
22190
23867
|
res.end(
|
|
22191
23868
|
JSON.stringify({
|
|
@@ -22195,6 +23872,16 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22195
23872
|
req.resume();
|
|
22196
23873
|
return;
|
|
22197
23874
|
}
|
|
23875
|
+
if (await tryServeUiAsset(
|
|
23876
|
+
req,
|
|
23877
|
+
res,
|
|
23878
|
+
options,
|
|
23879
|
+
upstreamPathname,
|
|
23880
|
+
basePath,
|
|
23881
|
+
aliasRegistry.canonicalBasePath
|
|
23882
|
+
)) {
|
|
23883
|
+
return;
|
|
23884
|
+
}
|
|
22198
23885
|
const remotePort = req.socket.remotePort;
|
|
22199
23886
|
let identity = null;
|
|
22200
23887
|
if (remotePort !== void 0 && remotePort > 0) {
|
|
@@ -22212,7 +23899,8 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22212
23899
|
if (contentLength !== null && (!Number.isFinite(contentLength) || contentLength < 0)) {
|
|
22213
23900
|
res.writeHead(400, {
|
|
22214
23901
|
"content-type": "application/json; charset=utf-8",
|
|
22215
|
-
"cache-control": "no-store"
|
|
23902
|
+
"cache-control": "no-store",
|
|
23903
|
+
...uiCorsHeaders(allowedUiOrigin)
|
|
22216
23904
|
});
|
|
22217
23905
|
res.end(JSON.stringify({ error: "Invalid Content-Length header" }));
|
|
22218
23906
|
req.resume();
|
|
@@ -22221,7 +23909,8 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22221
23909
|
if (contentLength !== null && contentLength > maxRequestBytes) {
|
|
22222
23910
|
res.writeHead(413, {
|
|
22223
23911
|
"content-type": "application/json; charset=utf-8",
|
|
22224
|
-
"cache-control": "no-store"
|
|
23912
|
+
"cache-control": "no-store",
|
|
23913
|
+
...uiCorsHeaders(allowedUiOrigin)
|
|
22225
23914
|
});
|
|
22226
23915
|
res.end(JSON.stringify({ error: `Request body exceeds ${String(maxRequestBytes)} bytes` }));
|
|
22227
23916
|
req.resume();
|
|
@@ -22234,10 +23923,12 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22234
23923
|
if (basePath.length > 0 && (lowerName === "accept-encoding" || lowerName === "range" || lowerName === "if-range"))
|
|
22235
23924
|
continue;
|
|
22236
23925
|
if (lowerName.startsWith(IDENTITY_HEADER_PREFIX)) continue;
|
|
23926
|
+
if (lowerName === "authorization" && !isModelProxyPath(upstreamPathname)) continue;
|
|
22237
23927
|
if (value === void 0) continue;
|
|
22238
23928
|
upstreamHeaders[name] = value;
|
|
22239
23929
|
}
|
|
22240
23930
|
upstreamHeaders[AGENT_INSPECTOR_PUBLIC_BASE_PATH_HEADER] = basePath.length === 0 ? "/" : basePath;
|
|
23931
|
+
upstreamHeaders[AGENT_INSPECTOR_PUBLIC_ORIGIN_HEADER] = requestPublicOrigin;
|
|
22241
23932
|
const publicPort = req.socket.localPort;
|
|
22242
23933
|
if (publicPort !== void 0 && publicPort > 0 && publicPort <= 65535) {
|
|
22243
23934
|
upstreamHeaders[AGENT_INSPECTOR_PUBLIC_PORT_HEADER] = String(publicPort);
|
|
@@ -22311,6 +24002,11 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22311
24002
|
for (const [name, value] of Object.entries(BASE_SECURITY_HEADERS)) {
|
|
22312
24003
|
outHeaders[name] = value;
|
|
22313
24004
|
}
|
|
24005
|
+
if (allowedUiOrigin !== null && isUiControlPath(upstreamPathname)) {
|
|
24006
|
+
outHeaders["access-control-allow-origin"] = allowedUiOrigin;
|
|
24007
|
+
outHeaders["access-control-expose-headers"] = "Content-Disposition";
|
|
24008
|
+
outHeaders["vary"] = appendVaryHeader(outHeaders["vary"], "Origin");
|
|
24009
|
+
}
|
|
22314
24010
|
if (contentType?.toLowerCase().includes("text/html") === true) {
|
|
22315
24011
|
outHeaders["content-security-policy"] = DOCUMENT_CONTENT_SECURITY_POLICY;
|
|
22316
24012
|
}
|
|
@@ -22322,7 +24018,7 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22322
24018
|
chunks.push(toBuffer(chunk));
|
|
22323
24019
|
});
|
|
22324
24020
|
proxyRes.on("end", () => {
|
|
22325
|
-
const body =
|
|
24021
|
+
const body = Buffer4.concat(chunks).toString("utf8");
|
|
22326
24022
|
res.write(
|
|
22327
24023
|
shouldRewriteHtml ? rewriteHtmlForBasePath(body, basePath, aliasRegistry.canonicalBasePath) : rewriteJavaScriptForBasePath(body, basePath, aliasRegistry.canonicalBasePath)
|
|
22328
24024
|
);
|
|
@@ -22397,18 +24093,18 @@ async function handleRequest(req, res, options, aliasRegistry) {
|
|
|
22397
24093
|
});
|
|
22398
24094
|
}
|
|
22399
24095
|
function toBuffer(chunk) {
|
|
22400
|
-
if (
|
|
22401
|
-
if (typeof chunk === "string") return
|
|
22402
|
-
if (chunk instanceof ArrayBuffer) return
|
|
24096
|
+
if (Buffer4.isBuffer(chunk)) return chunk;
|
|
24097
|
+
if (typeof chunk === "string") return Buffer4.from(chunk);
|
|
24098
|
+
if (chunk instanceof ArrayBuffer) return Buffer4.from(chunk);
|
|
22403
24099
|
if (chunk instanceof Uint8Array)
|
|
22404
|
-
return
|
|
22405
|
-
return
|
|
24100
|
+
return Buffer4.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
24101
|
+
return Buffer4.alloc(0);
|
|
22406
24102
|
}
|
|
22407
24103
|
|
|
22408
24104
|
// src/cli.ts
|
|
22409
24105
|
init_platformCommands();
|
|
22410
24106
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
22411
|
-
var __dirname2 =
|
|
24107
|
+
var __dirname2 = dirname2(__filename2);
|
|
22412
24108
|
var DEFAULT_PORT4 = 9527;
|
|
22413
24109
|
var LEGACY_ALIAS_PORT = 9527;
|
|
22414
24110
|
var DEFAULT_UPSTREAM_PORT = 9529;
|
|
@@ -22434,6 +24130,14 @@ Global options:
|
|
|
22434
24130
|
-h, --help Show this help without starting a runtime
|
|
22435
24131
|
-v, --version Print the package version
|
|
22436
24132
|
|
|
24133
|
+
Start options:
|
|
24134
|
+
--backend-only, --headless Start the API, proxy, and MCP runtime without serving the UI
|
|
24135
|
+
--with-ui Serve the independently built UI through the protected ingress
|
|
24136
|
+
--port, -p <port> Public ingress port (default: 9527)
|
|
24137
|
+
--host, -H <host> Bind host (default: 127.0.0.1)
|
|
24138
|
+
--background Start a hidden supervisor and return after readiness
|
|
24139
|
+
--no-open Do not open the UI after startup
|
|
24140
|
+
|
|
22437
24141
|
Run \`agent-inspector instance --help\`, \`agent-inspector onboard --help\`, or
|
|
22438
24142
|
\`agent-inspector doctor --help\` for command-specific options.`);
|
|
22439
24143
|
}
|
|
@@ -22523,12 +24227,12 @@ async function getRunningCaptureMode(port, host, basePath = "") {
|
|
|
22523
24227
|
}
|
|
22524
24228
|
}
|
|
22525
24229
|
function isPortAcceptingConnections(port, host) {
|
|
22526
|
-
return new Promise((
|
|
24230
|
+
return new Promise((resolve5) => {
|
|
22527
24231
|
const socket = createConnection2({ host: probeHostForBindHost(host), port });
|
|
22528
24232
|
const finish = (value) => {
|
|
22529
24233
|
socket.removeAllListeners();
|
|
22530
24234
|
socket.destroy();
|
|
22531
|
-
|
|
24235
|
+
resolve5(value);
|
|
22532
24236
|
};
|
|
22533
24237
|
socket.setTimeout(LOCAL_PROBE_TIMEOUT_MS);
|
|
22534
24238
|
socket.once("connect", () => finish(true));
|
|
@@ -22537,8 +24241,8 @@ function isPortAcceptingConnections(port, host) {
|
|
|
22537
24241
|
});
|
|
22538
24242
|
}
|
|
22539
24243
|
function sleep2(ms) {
|
|
22540
|
-
return new Promise((
|
|
22541
|
-
setTimeout(
|
|
24244
|
+
return new Promise((resolve5) => {
|
|
24245
|
+
setTimeout(resolve5, ms);
|
|
22542
24246
|
});
|
|
22543
24247
|
}
|
|
22544
24248
|
async function waitForInspectorHealthy(port, timeoutMs, host, basePath = "") {
|
|
@@ -22562,12 +24266,12 @@ function openBrowser(targetUrl) {
|
|
|
22562
24266
|
browserProcess.unref();
|
|
22563
24267
|
}
|
|
22564
24268
|
function waitForProcessExit(child) {
|
|
22565
|
-
return new Promise((
|
|
24269
|
+
return new Promise((resolve5) => {
|
|
22566
24270
|
child.once("exit", (code) => {
|
|
22567
|
-
|
|
24271
|
+
resolve5(code ?? 1);
|
|
22568
24272
|
});
|
|
22569
24273
|
child.once("error", () => {
|
|
22570
|
-
|
|
24274
|
+
resolve5(1);
|
|
22571
24275
|
});
|
|
22572
24276
|
});
|
|
22573
24277
|
}
|
|
@@ -22579,7 +24283,7 @@ function pipeServerOutputStream(stream, target, context) {
|
|
|
22579
24283
|
if (stream === null) return;
|
|
22580
24284
|
let buffered = "";
|
|
22581
24285
|
stream.on("data", (chunk) => {
|
|
22582
|
-
const text =
|
|
24286
|
+
const text = Buffer5.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
|
|
22583
24287
|
const lines = `${buffered}${text}`.split(/\r?\n/);
|
|
22584
24288
|
buffered = lines.pop() ?? "";
|
|
22585
24289
|
for (const line of lines) {
|
|
@@ -22598,7 +24302,7 @@ function pipeServerOutputStream(stream, target, context) {
|
|
|
22598
24302
|
});
|
|
22599
24303
|
}
|
|
22600
24304
|
function resolveServerCommand(outputDir, serverPath) {
|
|
22601
|
-
const brandedRuntime =
|
|
24305
|
+
const brandedRuntime = join9(outputDir, BRANDED_WINDOWS_RUNTIME_EXE);
|
|
22602
24306
|
return process.platform === "win32" && existsSync7(brandedRuntime) ? { command: brandedRuntime, args: [serverPath] } : { command: process.execPath, args: [serverPath] };
|
|
22603
24307
|
}
|
|
22604
24308
|
function parseCaptureMode(value) {
|
|
@@ -22619,11 +24323,20 @@ function readCaptureMode(raw) {
|
|
|
22619
24323
|
const value = descriptor?.value;
|
|
22620
24324
|
return typeof value === "string" ? parseCaptureMode(value) : null;
|
|
22621
24325
|
}
|
|
22622
|
-
function printAccessHints(port, host, basePath
|
|
24326
|
+
function printAccessHints(port, host, basePath, serveUi, publicOrigin) {
|
|
22623
24327
|
const networkUrls = networkUrlsForBindHost(port, host);
|
|
22624
|
-
for (const line of formatAccessHintLines(port, networkUrls, host, basePath)) {
|
|
24328
|
+
for (const line of formatAccessHintLines(port, networkUrls, host, basePath, serveUi)) {
|
|
22625
24329
|
console.log(line);
|
|
22626
24330
|
}
|
|
24331
|
+
if (publicOrigin !== void 0) {
|
|
24332
|
+
const publicBaseUrl = appendBasePathToOrigin(publicOrigin, "/", basePath);
|
|
24333
|
+
if (serveUi) console.log(` Public UI: ${publicBaseUrl}`);
|
|
24334
|
+
console.log(` Public control Base: ${publicBaseUrl}`);
|
|
24335
|
+
console.log(` Public agent Base: ${publicBaseUrl}`);
|
|
24336
|
+
console.log(` Public REST API: ${appendBasePathToOrigin(publicOrigin, "/api", basePath)}`);
|
|
24337
|
+
console.log(` Public proxy: ${appendBasePathToOrigin(publicOrigin, "/proxy", basePath)}`);
|
|
24338
|
+
console.log(` Public MCP: ${appendBasePathToOrigin(publicOrigin, "/api/mcp", basePath)}`);
|
|
24339
|
+
}
|
|
22627
24340
|
if (!isLoopbackHost(host)) {
|
|
22628
24341
|
const proxyTokenConfigured = (process.env["AGENT_INSPECTOR_PROXY_TOKEN"] ?? "") !== "";
|
|
22629
24342
|
const legacyProxyEnabled = process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1";
|
|
@@ -22652,6 +24365,18 @@ function readPositiveIntegerEnv(name) {
|
|
|
22652
24365
|
console.warn(`Ignoring invalid ${name}=${raw}; expected a positive integer.`);
|
|
22653
24366
|
return void 0;
|
|
22654
24367
|
}
|
|
24368
|
+
function normalizeExplicitPublicOrigin(value) {
|
|
24369
|
+
if (value === void 0 || value.trim().length === 0) return void 0;
|
|
24370
|
+
try {
|
|
24371
|
+
const parsed = new URL(value.trim());
|
|
24372
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
24373
|
+
if (parsed.username.length > 0 || parsed.password.length > 0) return null;
|
|
24374
|
+
if (parsed.pathname !== "/" || parsed.search.length > 0 || parsed.hash.length > 0) return null;
|
|
24375
|
+
return parsed.origin;
|
|
24376
|
+
} catch {
|
|
24377
|
+
return null;
|
|
24378
|
+
}
|
|
24379
|
+
}
|
|
22655
24380
|
function buildBackgroundSupervisorArgs(options) {
|
|
22656
24381
|
const args = ["--no-open", "--port", String(options.port), "--mode", options.captureMode];
|
|
22657
24382
|
if (options.host !== void 0) {
|
|
@@ -22669,6 +24394,9 @@ function buildBackgroundSupervisorArgs(options) {
|
|
|
22669
24394
|
if (options.legacyAliasEnabled) {
|
|
22670
24395
|
args.push("--legacy-port");
|
|
22671
24396
|
}
|
|
24397
|
+
if (!options.serveUi) {
|
|
24398
|
+
args.push("--backend-only");
|
|
24399
|
+
}
|
|
22672
24400
|
return args;
|
|
22673
24401
|
}
|
|
22674
24402
|
function pickUpstreamPort(publicPort, legacyAliasPort) {
|
|
@@ -22681,7 +24409,7 @@ function pickUpstreamPort(publicPort, legacyAliasPort) {
|
|
|
22681
24409
|
}
|
|
22682
24410
|
return DEFAULT_UPSTREAM_PORT;
|
|
22683
24411
|
}
|
|
22684
|
-
async function tryStartIdentityProxy(identityPort, upstreamPort, listenHost, upstreamHost, managedInstance) {
|
|
24412
|
+
async function tryStartIdentityProxy(identityPort, upstreamPort, listenHost, upstreamHost, managedInstance, allowedUiOrigins, uiDirectory, publicOrigin) {
|
|
22685
24413
|
try {
|
|
22686
24414
|
const server = await startIdentityProxy({
|
|
22687
24415
|
listenPort: identityPort,
|
|
@@ -22694,6 +24422,9 @@ async function tryStartIdentityProxy(identityPort, upstreamPort, listenHost, ups
|
|
|
22694
24422
|
proxyToken: process.env["AGENT_INSPECTOR_PROXY_TOKEN"],
|
|
22695
24423
|
allowUnauthenticatedRemoteProxy: process.env["AGENT_INSPECTOR_ALLOW_UNAUTHENTICATED_REMOTE_PROXY"] === "1",
|
|
22696
24424
|
trustedHosts: (process.env["AGENT_INSPECTOR_TRUSTED_HOSTS"] ?? "").split(",").map((host) => host.trim()).filter((host) => host.length > 0),
|
|
24425
|
+
allowedUiOrigins,
|
|
24426
|
+
uiDirectory,
|
|
24427
|
+
publicOrigin,
|
|
22697
24428
|
maxRequestBytes: readPositiveIntegerEnv("AGENT_INSPECTOR_MAX_REQUEST_BYTES"),
|
|
22698
24429
|
requestTimeoutMs: readPositiveIntegerEnv("AGENT_INSPECTOR_REQUEST_TIMEOUT_MS"),
|
|
22699
24430
|
managedInstance
|
|
@@ -22701,9 +24432,7 @@ async function tryStartIdentityProxy(identityPort, upstreamPort, listenHost, ups
|
|
|
22701
24432
|
return server;
|
|
22702
24433
|
} catch (err) {
|
|
22703
24434
|
const message = err instanceof Error ? err.message : String(err);
|
|
22704
|
-
console.warn(
|
|
22705
|
-
`[identity-proxy] Could not listen on port ${identityPort}: ${message}. Falling back to OS-scan heuristic on port ${upstreamPort}. Pass --identity-proxy-port <n> to choose another port, or --no-identity-proxy to disable.`
|
|
22706
|
-
);
|
|
24435
|
+
console.warn(`[identity-proxy] Could not listen on port ${identityPort}: ${message}.`);
|
|
22707
24436
|
return null;
|
|
22708
24437
|
}
|
|
22709
24438
|
}
|
|
@@ -22721,7 +24450,7 @@ async function runStart(args) {
|
|
|
22721
24450
|
const envMode = process.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? process.env["AGENT_INSPECTOR_MODE"];
|
|
22722
24451
|
let port = portDefault;
|
|
22723
24452
|
let host = envHost !== void 0 && envHost.trim().length > 0 ? envHost.trim() : DEFAULT_BIND_HOST;
|
|
22724
|
-
let
|
|
24453
|
+
let open2 = true;
|
|
22725
24454
|
let openWasSpecified = false;
|
|
22726
24455
|
let background = false;
|
|
22727
24456
|
let forceRestart = false;
|
|
@@ -22731,6 +24460,7 @@ async function runStart(args) {
|
|
|
22731
24460
|
let captureModeWasSpecified = false;
|
|
22732
24461
|
let enableIdentityProxy = true;
|
|
22733
24462
|
let legacyAliasEnabled = false;
|
|
24463
|
+
let serveUi = process.env["AGENT_INSPECTOR_BACKEND_ONLY"] !== "1";
|
|
22734
24464
|
if (envMode !== void 0 && envMode !== "") {
|
|
22735
24465
|
const parsedMode = parseCaptureMode(envMode);
|
|
22736
24466
|
if (parsedMode === null) {
|
|
@@ -22793,11 +24523,11 @@ async function runStart(args) {
|
|
|
22793
24523
|
break;
|
|
22794
24524
|
}
|
|
22795
24525
|
case "--no-open":
|
|
22796
|
-
|
|
24526
|
+
open2 = false;
|
|
22797
24527
|
openWasSpecified = true;
|
|
22798
24528
|
break;
|
|
22799
24529
|
case "--open":
|
|
22800
|
-
|
|
24530
|
+
open2 = true;
|
|
22801
24531
|
openWasSpecified = true;
|
|
22802
24532
|
break;
|
|
22803
24533
|
case "--force-restart":
|
|
@@ -22807,6 +24537,14 @@ async function runStart(args) {
|
|
|
22807
24537
|
case "--background":
|
|
22808
24538
|
background = true;
|
|
22809
24539
|
break;
|
|
24540
|
+
case "--backend-only":
|
|
24541
|
+
case "--headless":
|
|
24542
|
+
serveUi = false;
|
|
24543
|
+
open2 = false;
|
|
24544
|
+
break;
|
|
24545
|
+
case "--with-ui":
|
|
24546
|
+
serveUi = true;
|
|
24547
|
+
break;
|
|
22810
24548
|
case "--config-dir":
|
|
22811
24549
|
configDir = args[i + 1];
|
|
22812
24550
|
i++;
|
|
@@ -22877,6 +24615,40 @@ async function runStart(args) {
|
|
|
22877
24615
|
process.exitCode = 1;
|
|
22878
24616
|
return;
|
|
22879
24617
|
}
|
|
24618
|
+
if (!serveUi) open2 = false;
|
|
24619
|
+
if (!enableIdentityProxy && serveUi) {
|
|
24620
|
+
console.error(
|
|
24621
|
+
"The decoupled UI is composed only by the protected ingress. Use --backend-only with --no-identity-proxy, or keep the identity proxy enabled."
|
|
24622
|
+
);
|
|
24623
|
+
process.exitCode = 1;
|
|
24624
|
+
return;
|
|
24625
|
+
}
|
|
24626
|
+
const uiOriginAllowlist = parseUiOriginAllowlist(process.env["AGENT_INSPECTOR_UI_ORIGINS"]);
|
|
24627
|
+
if (uiOriginAllowlist.rejected.length > 0) {
|
|
24628
|
+
console.error(
|
|
24629
|
+
`Invalid AGENT_INSPECTOR_UI_ORIGINS entries: ${uiOriginAllowlist.rejected.join(", ")}. Only exact loopback http(s) origins are accepted; use a same-origin authenticated gateway for remote UI hosting.`
|
|
24630
|
+
);
|
|
24631
|
+
process.exitCode = 1;
|
|
24632
|
+
return;
|
|
24633
|
+
}
|
|
24634
|
+
const publicOrigin = normalizeExplicitPublicOrigin(process.env["AGENT_INSPECTOR_PUBLIC_ORIGIN"]);
|
|
24635
|
+
if (publicOrigin === null) {
|
|
24636
|
+
console.error(
|
|
24637
|
+
"Invalid AGENT_INSPECTOR_PUBLIC_ORIGIN. Use an exact http(s) origin without credentials, path, query, or fragment."
|
|
24638
|
+
);
|
|
24639
|
+
process.exitCode = 1;
|
|
24640
|
+
return;
|
|
24641
|
+
}
|
|
24642
|
+
const uiDirectoryCandidate = join9(__dirname2, "../.output/ui");
|
|
24643
|
+
const uiIndexPath = join9(uiDirectoryCandidate, "index.html");
|
|
24644
|
+
const uiDirectory = serveUi && existsSync7(uiIndexPath) ? uiDirectoryCandidate : void 0;
|
|
24645
|
+
if (serveUi && uiDirectory === void 0) {
|
|
24646
|
+
console.error(
|
|
24647
|
+
`Agent Inspector UI artifact is missing at ${uiIndexPath}. Run the composed build or use --backend-only.`
|
|
24648
|
+
);
|
|
24649
|
+
process.exitCode = 1;
|
|
24650
|
+
return;
|
|
24651
|
+
}
|
|
22880
24652
|
function killProcessOnPort(targetPort) {
|
|
22881
24653
|
const pids = findPidsByPort(targetPort);
|
|
22882
24654
|
for (const pid of pids) {
|
|
@@ -22893,17 +24665,30 @@ async function runStart(args) {
|
|
|
22893
24665
|
}
|
|
22894
24666
|
const upstreamHost = enableIdentityProxy ? DEFAULT_BIND_HOST : host;
|
|
22895
24667
|
process.env["PORT"] = String(upstreamPort);
|
|
24668
|
+
const configuredBasePath = process.env[AGENT_INSPECTOR_BASE_PATH_ENV];
|
|
24669
|
+
if (configuredBasePath !== void 0 && normalizePublicBasePath(configuredBasePath) === null) {
|
|
24670
|
+
console.error(
|
|
24671
|
+
`Invalid ${AGENT_INSPECTOR_BASE_PATH_ENV}. Use / for root or URL-safe path segments such as /inspector or /foo-bar/v1.`
|
|
24672
|
+
);
|
|
24673
|
+
process.exitCode = 1;
|
|
24674
|
+
return;
|
|
24675
|
+
}
|
|
22896
24676
|
const basePath = getConfiguredBasePath(process.env);
|
|
22897
|
-
const
|
|
22898
|
-
const
|
|
22899
|
-
const
|
|
22900
|
-
const
|
|
24677
|
+
const bindOrigin = urlForHost(port, host);
|
|
24678
|
+
const outwardOrigin = publicOrigin ?? bindOrigin;
|
|
24679
|
+
const surfaceBaseUrl = appendBasePathToOrigin(outwardOrigin, "/", basePath);
|
|
24680
|
+
const uiUrl = serveUi ? surfaceBaseUrl : null;
|
|
24681
|
+
const proxyUrl = appendBasePathToOrigin(outwardOrigin, "/proxy", basePath);
|
|
24682
|
+
const openAiProxyUrl = appendBasePathToOrigin(outwardOrigin, "/proxy/v1", basePath);
|
|
24683
|
+
const proxyMessagesUrl = appendBasePathToOrigin(outwardOrigin, "/proxy/v1/messages", basePath);
|
|
22901
24684
|
const legacyUrl = legacyAliasPort === null ? null : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/", basePath);
|
|
22902
24685
|
const legacyProxyUrl = legacyAliasPort === null ? null : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/proxy", basePath);
|
|
22903
24686
|
const legacyMcpUrl = legacyAliasPort === null ? null : appendBasePathToOrigin(localUrlForPort(legacyAliasPort), "/api/mcp", basePath);
|
|
22904
24687
|
if (!forceRestart && await isInspectorHealthy(port, host, basePath)) {
|
|
22905
|
-
console.log(
|
|
22906
|
-
|
|
24688
|
+
console.log(
|
|
24689
|
+
`agent-inspector ${serveUi ? "is already running" : "backend is already running"} at ${surfaceBaseUrl}`
|
|
24690
|
+
);
|
|
24691
|
+
printAccessHints(port, host, basePath, serveUi, publicOrigin);
|
|
22907
24692
|
if (captureModeWasSpecified) {
|
|
22908
24693
|
const runningMode = await getRunningCaptureMode(port, host, basePath);
|
|
22909
24694
|
if (runningMode !== null && runningMode !== captureMode) {
|
|
@@ -22912,7 +24697,7 @@ async function runStart(args) {
|
|
|
22912
24697
|
}
|
|
22913
24698
|
}
|
|
22914
24699
|
console.log(`Use --force-restart to restart the existing instance.`);
|
|
22915
|
-
if (
|
|
24700
|
+
if (open2 && openWasSpecified && uiUrl !== null) {
|
|
22916
24701
|
openBrowser(uiUrl);
|
|
22917
24702
|
}
|
|
22918
24703
|
return;
|
|
@@ -22948,7 +24733,8 @@ async function runStart(args) {
|
|
|
22948
24733
|
providersJson,
|
|
22949
24734
|
captureMode,
|
|
22950
24735
|
enableIdentityProxy,
|
|
22951
|
-
legacyAliasEnabled
|
|
24736
|
+
legacyAliasEnabled,
|
|
24737
|
+
serveUi
|
|
22952
24738
|
});
|
|
22953
24739
|
const supervisorProcess = spawn2(process.execPath, [cliEntry, ...supervisorArgs], {
|
|
22954
24740
|
stdio: "ignore",
|
|
@@ -22961,8 +24747,10 @@ async function runStart(args) {
|
|
|
22961
24747
|
});
|
|
22962
24748
|
supervisorProcess.unref();
|
|
22963
24749
|
if (await waitForInspectorHealthy(port, 1e4, host, basePath)) {
|
|
22964
|
-
console.log(
|
|
22965
|
-
|
|
24750
|
+
console.log(
|
|
24751
|
+
`agent-inspector background ${serveUi ? "server" : "backend"} is ready at ${surfaceBaseUrl}`
|
|
24752
|
+
);
|
|
24753
|
+
printAccessHints(port, host, basePath, serveUi, publicOrigin);
|
|
22966
24754
|
if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
22967
24755
|
console.log(` Legacy proxy: ${legacyProxyUrl}`);
|
|
22968
24756
|
console.log(` Legacy MCP: ${legacyMcpUrl}`);
|
|
@@ -22971,46 +24759,18 @@ async function runStart(args) {
|
|
|
22971
24759
|
console.log(`Route AI coding tools through the identity proxy for accurate PID attribution:`);
|
|
22972
24760
|
console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
|
|
22973
24761
|
console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
|
|
22974
|
-
console.log(` MiMo Code: OPENAI_BASE_URL=${
|
|
22975
|
-
if (
|
|
24762
|
+
console.log(` MiMo Code: OPENAI_BASE_URL=${openAiProxyUrl} mimo`);
|
|
24763
|
+
if (open2 && openWasSpecified && uiUrl !== null) {
|
|
22976
24764
|
openBrowser(uiUrl);
|
|
22977
24765
|
}
|
|
22978
24766
|
return;
|
|
22979
24767
|
}
|
|
22980
|
-
console.error(`agent-inspector background server did not become ready at ${
|
|
24768
|
+
console.error(`agent-inspector background server did not become ready at ${surfaceBaseUrl}.`);
|
|
22981
24769
|
process.exitCode = 1;
|
|
22982
24770
|
return;
|
|
22983
24771
|
}
|
|
22984
|
-
console.log(`Server running at ${uiUrl}`);
|
|
22985
|
-
console.log(` Capture mode: ${captureMode}`);
|
|
22986
|
-
console.log(` Proxy: ${proxyUrl}`);
|
|
22987
|
-
printAccessHints(port, host, basePath);
|
|
22988
|
-
if (legacyUrl !== null && legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
22989
|
-
console.log(` Legacy UI: ${legacyUrl}`);
|
|
22990
|
-
console.log(` Legacy proxy: ${legacyProxyUrl}`);
|
|
22991
|
-
console.log(` Legacy MCP: ${legacyMcpUrl}`);
|
|
22992
|
-
}
|
|
22993
|
-
console.log(``);
|
|
22994
|
-
console.log(`Route AI coding tools through the proxy:`);
|
|
22995
|
-
console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
|
|
22996
|
-
console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
|
|
22997
|
-
console.log(` MiMo Code: OPENAI_BASE_URL=${proxyUrl} mimo`);
|
|
22998
|
-
console.log(` Direct HTTP: curl ${proxyMessagesUrl} -d '{"model":"...","messages":[...]}'`);
|
|
22999
|
-
console.log(
|
|
23000
|
-
` Remote/container tools: use the Network proxy URL above when shown; otherwise restart with --host 0.0.0.0 or a reachable IP.`
|
|
23001
|
-
);
|
|
23002
|
-
console.log(``);
|
|
23003
|
-
console.log(`Routing environment variables:`);
|
|
23004
|
-
console.log(` ROUTES JSON map of model prefix -> upstream URL`);
|
|
23005
|
-
console.log(` DEFAULT_UPSTREAM Fallback upstream for unmatched models`);
|
|
23006
|
-
console.log(
|
|
23007
|
-
` Example: ROUTES='{"claude-":"https://api.anthropic.com","MiniMax":"https://api.minimaxi.com/anthropic"}'`
|
|
23008
|
-
);
|
|
23009
|
-
if (open) {
|
|
23010
|
-
openBrowser(uiUrl);
|
|
23011
|
-
}
|
|
23012
24772
|
const outputDir = __dirname2;
|
|
23013
|
-
const serverPath =
|
|
24773
|
+
const serverPath = join9(outputDir, "../.output/server/index.mjs");
|
|
23014
24774
|
const serverCommand = resolveServerCommand(outputDir, serverPath);
|
|
23015
24775
|
const serverEnv = { ...process.env };
|
|
23016
24776
|
const currentCliEntry = process.env[AGENT_INSPECTOR_CLI_ENTRY_ENV] ?? process.argv[1];
|
|
@@ -23018,7 +24778,7 @@ async function runStart(args) {
|
|
|
23018
24778
|
serverEnv[AGENT_INSPECTOR_CLI_ENTRY_ENV] = currentCliEntry;
|
|
23019
24779
|
}
|
|
23020
24780
|
if (configDir !== void 0) {
|
|
23021
|
-
let resolvedPath =
|
|
24781
|
+
let resolvedPath = join9(configDir);
|
|
23022
24782
|
const msysMatch = /^\\([a-z])\\(.*)$/i.exec(resolvedPath);
|
|
23023
24783
|
if (msysMatch !== null) {
|
|
23024
24784
|
const drive = (msysMatch[1] ?? "").toUpperCase();
|
|
@@ -23040,7 +24800,8 @@ async function runStart(args) {
|
|
|
23040
24800
|
serverEnv["AGENT_INSPECTOR_PUBLIC_PORT"] = String(port);
|
|
23041
24801
|
serverEnv["PROXY_PORT"] = String(port);
|
|
23042
24802
|
serverEnv["AGENT_INSPECTOR_CAPTURE_MODE"] = captureMode;
|
|
23043
|
-
|
|
24803
|
+
serverEnv["AGENT_INSPECTOR_BACKEND_ONLY"] = serveUi ? "0" : "1";
|
|
24804
|
+
const workerDir = join9(__dirname2, "workers");
|
|
23044
24805
|
if (existsSync7(workerDir)) {
|
|
23045
24806
|
serverEnv["AGENT_INSPECTOR_WORKER_DIR"] = workerDir;
|
|
23046
24807
|
}
|
|
@@ -23080,12 +24841,12 @@ async function runStart(args) {
|
|
|
23080
24841
|
serverExit.then(() => void 0),
|
|
23081
24842
|
...identityProxies.map(async (proxy) => await closeIdentityProxyGracefully(proxy.server))
|
|
23082
24843
|
]).then(() => void 0);
|
|
23083
|
-
const timedOut = new Promise((
|
|
24844
|
+
const timedOut = new Promise((resolve5) => {
|
|
23084
24845
|
deadline = setTimeout(() => {
|
|
23085
24846
|
forced = true;
|
|
23086
24847
|
for (const proxy of identityProxies) forceCloseIdentityProxy(proxy.server);
|
|
23087
24848
|
if (serverProcess.exitCode === null) serverProcess.kill("SIGKILL");
|
|
23088
|
-
|
|
24849
|
+
resolve5();
|
|
23089
24850
|
}, childDeadline + SUPERVISOR_SHUTDOWN_GRACE_MS);
|
|
23090
24851
|
});
|
|
23091
24852
|
supervisorShutdownPromise = Promise.race([graceful, timedOut]).finally(() => {
|
|
@@ -23116,18 +24877,34 @@ async function runStart(args) {
|
|
|
23116
24877
|
upstreamPort,
|
|
23117
24878
|
host,
|
|
23118
24879
|
upstreamHost,
|
|
23119
|
-
managedInstanceControl
|
|
24880
|
+
managedInstanceControl,
|
|
24881
|
+
uiOriginAllowlist.origins,
|
|
24882
|
+
uiDirectory,
|
|
24883
|
+
publicOrigin
|
|
23120
24884
|
);
|
|
23121
|
-
if (primaryProxy
|
|
23122
|
-
|
|
24885
|
+
if (primaryProxy === null) {
|
|
24886
|
+
console.error(
|
|
24887
|
+
`Agent Inspector could not bind its protected public ingress at ${bindOrigin}; stopping the private backend.`
|
|
24888
|
+
);
|
|
24889
|
+
requestSupervisorShutdown("SIGTERM");
|
|
24890
|
+
const failedStartupShutdown = currentSupervisorShutdown();
|
|
24891
|
+
if (failedStartupShutdown !== null) await failedStartupShutdown;
|
|
24892
|
+
process.off("SIGINT", handleSigint);
|
|
24893
|
+
process.off("SIGTERM", handleSigterm);
|
|
24894
|
+
process.exitCode = 1;
|
|
24895
|
+
return;
|
|
23123
24896
|
}
|
|
24897
|
+
identityProxies.push({ label: "primary", port, server: primaryProxy });
|
|
23124
24898
|
if (legacyAliasPort !== null) {
|
|
23125
24899
|
const legacyProxy = await tryStartIdentityProxy(
|
|
23126
24900
|
legacyAliasPort,
|
|
23127
24901
|
upstreamPort,
|
|
23128
24902
|
host,
|
|
23129
24903
|
upstreamHost,
|
|
23130
|
-
managedInstanceControl
|
|
24904
|
+
managedInstanceControl,
|
|
24905
|
+
uiOriginAllowlist.origins,
|
|
24906
|
+
uiDirectory,
|
|
24907
|
+
publicOrigin
|
|
23131
24908
|
);
|
|
23132
24909
|
if (legacyProxy !== null) {
|
|
23133
24910
|
identityProxies.push({ label: "legacy", port: legacyAliasPort, server: legacyProxy });
|
|
@@ -23138,11 +24915,13 @@ async function runStart(args) {
|
|
|
23138
24915
|
serverProcess.unref();
|
|
23139
24916
|
const upstreamProbeBasePath = enableIdentityProxy ? "" : basePath;
|
|
23140
24917
|
if (await waitForInspectorHealthy(upstreamPort, 5e3, host, upstreamProbeBasePath)) {
|
|
23141
|
-
console.log(
|
|
24918
|
+
console.log(
|
|
24919
|
+
`agent-inspector background ${serveUi ? "server" : "backend"} is ready at ${surfaceBaseUrl}`
|
|
24920
|
+
);
|
|
23142
24921
|
if (identityProxies.length > 0) {
|
|
23143
24922
|
console.log(`Identity proxy ready on port ${port} (PID-attributing proxy).`);
|
|
23144
24923
|
}
|
|
23145
|
-
printAccessHints(port, host, basePath);
|
|
24924
|
+
printAccessHints(port, host, basePath, serveUi, publicOrigin);
|
|
23146
24925
|
if (legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
23147
24926
|
console.log(` Legacy proxy: ${legacyProxyUrl}`);
|
|
23148
24927
|
console.log(` Legacy MCP: ${legacyMcpUrl}`);
|
|
@@ -23153,19 +24932,29 @@ async function runStart(args) {
|
|
|
23153
24932
|
`Route AI coding tools through the identity proxy for accurate PID attribution:`,
|
|
23154
24933
|
` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`,
|
|
23155
24934
|
` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`,
|
|
23156
|
-
` MiMo Code: OPENAI_BASE_URL=${
|
|
24935
|
+
` MiMo Code: OPENAI_BASE_URL=${openAiProxyUrl} mimo`,
|
|
23157
24936
|
legacyProxyUrl === null ? ` Override the port with --port <n> when needed.` : ` Legacy configs using ${legacyProxyUrl} still work.`
|
|
23158
24937
|
);
|
|
23159
24938
|
}
|
|
23160
24939
|
return;
|
|
23161
24940
|
}
|
|
23162
|
-
console.error(`agent-inspector background server did not become ready at ${
|
|
24941
|
+
console.error(`agent-inspector background server did not become ready at ${surfaceBaseUrl}.`);
|
|
24942
|
+
process.exitCode = 1;
|
|
24943
|
+
return;
|
|
24944
|
+
}
|
|
24945
|
+
if (!await waitForInspectorHealthy(port, 1e4, host, basePath)) {
|
|
24946
|
+
console.error(`Agent Inspector did not become ready at ${surfaceBaseUrl}; stopping startup.`);
|
|
24947
|
+
requestSupervisorShutdown("SIGTERM");
|
|
24948
|
+
const failedStartupShutdown = currentSupervisorShutdown();
|
|
24949
|
+
if (failedStartupShutdown !== null) await failedStartupShutdown;
|
|
24950
|
+
process.off("SIGINT", handleSigint);
|
|
24951
|
+
process.off("SIGTERM", handleSigterm);
|
|
23163
24952
|
process.exitCode = 1;
|
|
23164
24953
|
return;
|
|
23165
24954
|
}
|
|
23166
24955
|
if (identityProxies.length > 0) {
|
|
23167
24956
|
console.log(``);
|
|
23168
|
-
console.log(`Identity proxy ready at ${
|
|
24957
|
+
console.log(`Identity proxy ready at ${bindOrigin} (PID-attributing ingress).`);
|
|
23169
24958
|
if (legacyAliasPort !== null) {
|
|
23170
24959
|
const legacyReady = identityProxies.some((proxy) => proxy.label === "legacy");
|
|
23171
24960
|
if (legacyReady) {
|
|
@@ -23176,6 +24965,27 @@ async function runStart(args) {
|
|
|
23176
24965
|
console.log(` Internal runtime port ${upstreamPort} is hidden behind the public proxy.`);
|
|
23177
24966
|
}
|
|
23178
24967
|
}
|
|
24968
|
+
console.log(
|
|
24969
|
+
`${serveUi ? "UI and backend" : "Backend control and agent runtime"} running at ${surfaceBaseUrl}`
|
|
24970
|
+
);
|
|
24971
|
+
console.log(` Capture mode: ${captureMode}`);
|
|
24972
|
+
console.log(` Proxy: ${proxyUrl}`);
|
|
24973
|
+
printAccessHints(port, host, basePath, serveUi, publicOrigin);
|
|
24974
|
+
if (legacyUrl !== null && legacyProxyUrl !== null && legacyMcpUrl !== null) {
|
|
24975
|
+
console.log(` Legacy UI: ${legacyUrl}`);
|
|
24976
|
+
console.log(` Legacy proxy: ${legacyProxyUrl}`);
|
|
24977
|
+
console.log(` Legacy MCP: ${legacyMcpUrl}`);
|
|
24978
|
+
}
|
|
24979
|
+
console.log(``);
|
|
24980
|
+
console.log(`Route AI coding tools through the proxy:`);
|
|
24981
|
+
console.log(` Claude Code: ANTHROPIC_BASE_URL=${proxyUrl} claude`);
|
|
24982
|
+
console.log(` OpenCode: LLM_BASE_URL=${proxyUrl} opencode`);
|
|
24983
|
+
console.log(` MiMo Code: OPENAI_BASE_URL=${openAiProxyUrl} mimo`);
|
|
24984
|
+
console.log(` Direct HTTP: curl ${proxyMessagesUrl} -d '{"model":"...","messages":[...]}'`);
|
|
24985
|
+
console.log(
|
|
24986
|
+
` Remote/container tools: use the Network proxy URL above when shown; otherwise restart with --host 0.0.0.0 or a reachable IP.`
|
|
24987
|
+
);
|
|
24988
|
+
if (open2 && uiUrl !== null) openBrowser(uiUrl);
|
|
23179
24989
|
process.exitCode = await serverExit;
|
|
23180
24990
|
const activeSupervisorShutdown = currentSupervisorShutdown();
|
|
23181
24991
|
if (activeSupervisorShutdown !== null) {
|