@tonyclaw/agent-inspector 3.0.44 → 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 +22707 -17099
- package/.output/server/_libs/extend.mjs +4 -8
- package/.output/server/_libs/jszip.mjs +9 -35
- package/.output/server/_libs/modelcontextprotocol__core.mjs +25 -25
- package/.output/server/_libs/modelcontextprotocol__server.mjs +2 -2
- 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/_libs/zod.mjs +6 -6
- package/.output/server/_ssr/index.mjs +5 -4
- package/.output/server/_ssr/publicBasePathContext-IH2ilOfu.mjs +176 -0
- package/.output/server/_ssr/{router-DWcUFPR3.mjs → router-D18yUq36.mjs} +7877 -5597
- package/.output/server/_ssr/start-COQu_AgD.mjs +57 -0
- package/.output/server/_tanstack-start-manifest_v-DnbdNeun.mjs +4 -0
- package/.output/server/index.mjs +5 -145
- package/.output/{public/assets/CompareDrawer-DQHjxFO6.js → ui/assets/CompareDrawer-DAUuIJ6G.js} +1 -1
- package/.output/{public/assets/InspectorPet-D1A4505B.js → ui/assets/InspectorPet-BRBVjOWI.js} +1 -1
- package/.output/ui/assets/ProxyViewerContainer-D7Sq0ctc.js +59 -0
- package/.output/{public/assets/ReplayDialog-C5vmbecv.js → ui/assets/ReplayDialog-nDLsjOhs.js} +1 -1
- package/.output/{public/assets/RequestAnatomy-CPGNB8lk.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-CDhRgzf6.js → ui/assets/_sessionId-BAfzrhSU.js} +1 -1
- package/.output/{public/assets/_sessionId-mw3aA8Bp.js → ui/assets/_sessionId-QnBomgPD.js} +1 -1
- package/.output/ui/assets/favicon-DYEkHSNl.svg +21 -0
- package/.output/{public/assets/index-B2eoeT-Z.js → ui/assets/index-BcsdxBAd.js} +1 -1
- package/.output/ui/assets/index-BvXp42al.css +1 -0
- package/.output/{public/assets/index-CX7_rf7o.js → ui/assets/index-D0rtCN9V.js} +1 -1
- package/.output/ui/assets/index-DHpwr08Z.js +73 -0
- package/.output/{public/assets/index-BfRhTvOd.js → ui/assets/index-dNuk2dsU.js} +1 -1
- package/.output/ui/assets/json-viewer-BvFHglMb.js +1 -0
- package/.output/{public/assets/jszip.min-C9rCFoU3.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 +983 -414
- package/.output/workers/sessionWorkerEntry.js +983 -414
- package/README.md +363 -35
- package/docs/instance-control.openapi.yaml +361 -0
- package/package.json +28 -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/backend/routes/api/-instances.ts +109 -0
- package/src/{routes → backend/routes}/api/alerts.summary.ts +3 -3
- package/src/{routes → backend/routes}/api/alerts.ts +4 -4
- package/src/{routes → backend/routes}/api/config.paths.ts +1 -1
- package/src/{routes → backend/routes}/api/config.ts +11 -2
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.help.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.install.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.runner-presets.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.$packageId.upgrade.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.packages.ts +5 -1
- package/src/{routes → backend/routes}/api/ecosystem.recipes.$recipeId.run.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.tasks.$taskId.ts +1 -1
- package/src/{routes → backend/routes}/api/ecosystem.tasks.ts +1 -1
- package/src/{routes → backend/routes}/api/groups.$groupId.evidence.ts +2 -2
- package/src/{routes → backend/routes}/api/groups.$groupId.sessions.ts +2 -2
- package/src/{routes → backend/routes}/api/groups.$groupId.ts +2 -2
- package/src/{routes → backend/routes}/api/groups.ts +2 -2
- package/src/{routes → backend/routes}/api/health.ts +3 -3
- package/src/backend/routes/api/instances.$name.connection.ts +12 -0
- package/src/backend/routes/api/instances.$name.restart.ts +23 -0
- package/src/backend/routes/api/instances.$name.start.ts +18 -0
- package/src/backend/routes/api/instances.$name.stop.ts +23 -0
- package/src/backend/routes/api/instances.$name.ts +12 -0
- package/src/backend/routes/api/instances.ts +16 -0
- 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/backend/start.ts +10 -0
- package/src/cli/alias.ts +431 -0
- package/src/cli/doctor.ts +32 -9
- package/src/cli/instance.ts +1050 -0
- package/src/cli/instanceArgs.ts +285 -0
- package/src/cli/instanceControl.ts +181 -0
- package/src/cli/instanceModel.ts +267 -0
- package/src/cli/instanceRegistry.ts +731 -0
- package/src/cli/networkHints.ts +27 -14
- package/src/cli/onboard.ts +190 -35
- package/src/cli/onboardTarget.ts +228 -0
- package/src/cli/templates/codex-skill-onboard.ts +109 -31
- package/src/cli/templates/skill-onboard.ts +140 -70
- package/src/cli.ts +338 -77
- package/src/components/ProxyViewer.tsx +20 -10
- 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 +160 -140
- 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 +31 -7
- 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 +54 -0
- package/src/lib/instanceContract.ts +236 -0
- package/src/lib/managedInstance.ts +104 -0
- package/src/lib/piAgentContract.ts +3 -2
- package/src/lib/publicBasePathContext.ts +137 -0
- 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 +150 -0
- package/src/mcp/loopback.ts +16 -5
- package/src/mcp/mode.ts +25 -0
- package/src/mcp/server.ts +274 -39
- package/src/mcp/toolHandlers.ts +2 -2
- 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 +1049 -50
- 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 +84 -2
- package/src/proxy/schemas.ts +7 -1
- package/src/proxy/sessionArchive.ts +78 -14
- package/src/proxy/sessionInfo.ts +5 -4
- 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-BhpIgfBi.js +0 -126
- package/.output/public/assets/ResponseView-DUfrajCm.js +0 -2
- package/.output/public/assets/StreamingChunkSequence-nbeCQqOz.js +0 -1
- package/.output/public/assets/index-BWjeqxbk.css +0 -1
- package/.output/public/assets/index-C6qZwYlR.js +0 -70
- package/.output/public/assets/json-viewer-BLmF1sOZ.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-E_Dmopc-.mjs +0 -144
- package/.output/server/_sessionId-U54BAqdp.mjs +0 -82
- package/.output/server/_ssr/CompareDrawer-CksatPed.mjs +0 -1058
- package/.output/server/_ssr/InspectorPet-DNc4GQ7z.mjs +0 -936
- package/.output/server/_ssr/ProxyViewerContainer-B02Ey0p_.mjs +0 -17607
- package/.output/server/_ssr/ReplayDialog-DdMvHJF1.mjs +0 -723
- package/.output/server/_ssr/RequestAnatomy--vh2Z8__.mjs +0 -835
- package/.output/server/_ssr/ResponseView-Dhxc-cKC.mjs +0 -755
- package/.output/server/_ssr/StreamingChunkSequence-BG0EQmaD.mjs +0 -272
- package/.output/server/_ssr/index-D0At9nfd.mjs +0 -82
- package/.output/server/_ssr/index-lcfulIXa.mjs +0 -133
- package/.output/server/_ssr/json-viewer-My1J1_5R.mjs +0 -481
- package/.output/server/_ssr/start-HYkvq4Ni.mjs +0 -4
- package/.output/server/_tanstack-start-manifest_v-B2chR8ef.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
|
@@ -12,8 +12,28 @@ import {
|
|
|
12
12
|
|
|
13
13
|
const DEFAULT_MEMORY_LIMIT_BYTES = 1024 * 1024;
|
|
14
14
|
const FILE_WRITE_BATCH_BYTES = 64 * 1024;
|
|
15
|
+
const DEFAULT_QUEUE_MAX_MESSAGES = 256;
|
|
16
|
+
const DEFAULT_QUEUE_MAX_BYTES = 8 * 1024 * 1024;
|
|
17
|
+
const DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES = 4096;
|
|
18
|
+
const DEFAULT_GLOBAL_QUEUE_MAX_BYTES = 64 * 1024 * 1024;
|
|
15
19
|
const activeCaptures = new Set<RawStreamCapture>();
|
|
16
20
|
|
|
21
|
+
export type RawStreamCaptureGapReason = "capacity-exceeded" | "pipeline-closed" | "write-failed";
|
|
22
|
+
|
|
23
|
+
export type RawStreamCaptureStatus = {
|
|
24
|
+
incomplete: boolean;
|
|
25
|
+
droppedChunks: number;
|
|
26
|
+
droppedBytes: number;
|
|
27
|
+
reason: RawStreamCaptureGapReason | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type RawStreamCaptureOptions = {
|
|
31
|
+
memoryLimitBytes?: number;
|
|
32
|
+
queueMaxMessages?: number;
|
|
33
|
+
queueMaxBytes?: number;
|
|
34
|
+
globalBudget?: RawStreamObservationBudget;
|
|
35
|
+
};
|
|
36
|
+
|
|
17
37
|
export type RawStreamSource =
|
|
18
38
|
| {
|
|
19
39
|
type: "memory";
|
|
@@ -32,6 +52,62 @@ function resolveMemoryLimitBytes(): number {
|
|
|
32
52
|
return parsed;
|
|
33
53
|
}
|
|
34
54
|
|
|
55
|
+
function resolvePositiveIntegerEnv(name: string, fallback: number): number {
|
|
56
|
+
const raw = process.env[name];
|
|
57
|
+
if (raw === undefined || raw === "") return fallback;
|
|
58
|
+
const parsed = Number(raw);
|
|
59
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) return fallback;
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function defaultQueueMaxMessages(): number {
|
|
64
|
+
return resolvePositiveIntegerEnv(
|
|
65
|
+
"AGENT_INSPECTOR_OBSERVATION_QUEUE_MESSAGES",
|
|
66
|
+
DEFAULT_QUEUE_MAX_MESSAGES,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function defaultQueueMaxBytes(): number {
|
|
71
|
+
return resolvePositiveIntegerEnv(
|
|
72
|
+
"AGENT_INSPECTOR_OBSERVATION_QUEUE_BYTES",
|
|
73
|
+
DEFAULT_QUEUE_MAX_BYTES,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class RawStreamObservationBudget {
|
|
78
|
+
private messages = 0;
|
|
79
|
+
private bytes = 0;
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
private readonly maxMessages: number,
|
|
83
|
+
private readonly maxBytes: number,
|
|
84
|
+
) {}
|
|
85
|
+
|
|
86
|
+
tryAcquire(bytes: number): (() => void) | null {
|
|
87
|
+
if (this.messages + 1 > this.maxMessages || this.bytes + bytes > this.maxBytes) return null;
|
|
88
|
+
this.messages += 1;
|
|
89
|
+
this.bytes += bytes;
|
|
90
|
+
let released = false;
|
|
91
|
+
return () => {
|
|
92
|
+
if (released) return;
|
|
93
|
+
released = true;
|
|
94
|
+
this.messages -= 1;
|
|
95
|
+
this.bytes -= bytes;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const globalObservationBudget = new RawStreamObservationBudget(
|
|
101
|
+
resolvePositiveIntegerEnv(
|
|
102
|
+
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_MESSAGES",
|
|
103
|
+
DEFAULT_GLOBAL_QUEUE_MAX_MESSAGES,
|
|
104
|
+
),
|
|
105
|
+
resolvePositiveIntegerEnv(
|
|
106
|
+
"AGENT_INSPECTOR_OBSERVATION_GLOBAL_QUEUE_BYTES",
|
|
107
|
+
DEFAULT_GLOBAL_QUEUE_MAX_BYTES,
|
|
108
|
+
),
|
|
109
|
+
);
|
|
110
|
+
|
|
35
111
|
function getRawStreamDir(): string {
|
|
36
112
|
return join(getDataDir(), "raw-streams");
|
|
37
113
|
}
|
|
@@ -42,6 +118,9 @@ function createRawStreamPath(): string {
|
|
|
42
118
|
|
|
43
119
|
export class RawStreamCapture {
|
|
44
120
|
private readonly memoryLimitBytes: number;
|
|
121
|
+
private readonly queueMaxMessages: number;
|
|
122
|
+
private readonly queueMaxBytes: number;
|
|
123
|
+
private readonly globalBudget: RawStreamObservationBudget;
|
|
45
124
|
private chunks: string[] = [];
|
|
46
125
|
private byteLength = 0;
|
|
47
126
|
private filePath: string | null = null;
|
|
@@ -49,11 +128,22 @@ export class RawStreamCapture {
|
|
|
49
128
|
private pendingFileChunks: string[] = [];
|
|
50
129
|
private pendingFileBytes = 0;
|
|
51
130
|
private operation = Promise.resolve();
|
|
131
|
+
private accepting = true;
|
|
52
132
|
private closed = false;
|
|
53
133
|
private sawChunk = false;
|
|
134
|
+
private queuedMessages = 0;
|
|
135
|
+
private queuedBytes = 0;
|
|
136
|
+
private incomplete = false;
|
|
137
|
+
private storageFailed = false;
|
|
138
|
+
private droppedChunks = 0;
|
|
139
|
+
private droppedBytes = 0;
|
|
140
|
+
private gapReason: RawStreamCaptureGapReason | null = null;
|
|
54
141
|
|
|
55
|
-
constructor(
|
|
56
|
-
this.memoryLimitBytes = memoryLimitBytes;
|
|
142
|
+
constructor(options: RawStreamCaptureOptions = {}) {
|
|
143
|
+
this.memoryLimitBytes = options.memoryLimitBytes ?? resolveMemoryLimitBytes();
|
|
144
|
+
this.queueMaxMessages = options.queueMaxMessages ?? defaultQueueMaxMessages();
|
|
145
|
+
this.queueMaxBytes = options.queueMaxBytes ?? defaultQueueMaxBytes();
|
|
146
|
+
this.globalBudget = options.globalBudget ?? globalObservationBudget;
|
|
57
147
|
activeCaptures.add(this);
|
|
58
148
|
}
|
|
59
149
|
|
|
@@ -61,6 +151,21 @@ export class RawStreamCapture {
|
|
|
61
151
|
return this.sawChunk;
|
|
62
152
|
}
|
|
63
153
|
|
|
154
|
+
get status(): RawStreamCaptureStatus {
|
|
155
|
+
return {
|
|
156
|
+
incomplete: this.incomplete,
|
|
157
|
+
droppedChunks: this.droppedChunks,
|
|
158
|
+
droppedBytes: this.droppedBytes,
|
|
159
|
+
reason: this.gapReason,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
recordDroppedObservation(reason: RawStreamCaptureGapReason, observedBytes: number): void {
|
|
164
|
+
const safeObservedBytes =
|
|
165
|
+
Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
166
|
+
this.recordGap(reason, safeObservedBytes, safeObservedBytes > 0);
|
|
167
|
+
}
|
|
168
|
+
|
|
64
169
|
async append(text: string): Promise<void> {
|
|
65
170
|
if (text === "") return;
|
|
66
171
|
const next = this.operation.then(() => this.appendInternal(text));
|
|
@@ -68,7 +173,63 @@ export class RawStreamCapture {
|
|
|
68
173
|
return next;
|
|
69
174
|
}
|
|
70
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Submit an observation without waiting for file I/O. Once capacity is exceeded, capture remains
|
|
178
|
+
* incomplete and all later chunks are counted as dropped so the persisted value is an honest
|
|
179
|
+
* prefix rather than a misleading stream with an unmarked hole.
|
|
180
|
+
*/
|
|
181
|
+
tryAppend(text: string, observedBytes: number): boolean {
|
|
182
|
+
const safeObservedBytes =
|
|
183
|
+
Number.isSafeInteger(observedBytes) && observedBytes >= 0 ? observedBytes : 0;
|
|
184
|
+
const queuedBytes = Math.max(safeObservedBytes, Buffer.byteLength(text, "utf-8"));
|
|
185
|
+
|
|
186
|
+
if (!this.accepting || this.closed) {
|
|
187
|
+
this.recordGap("pipeline-closed", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (this.incomplete) {
|
|
191
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, safeObservedBytes > 0 || text !== "");
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
if (text === "") return true;
|
|
195
|
+
if (
|
|
196
|
+
this.queuedMessages + 1 > this.queueMaxMessages ||
|
|
197
|
+
this.queuedBytes + queuedBytes > this.queueMaxBytes
|
|
198
|
+
) {
|
|
199
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const releaseGlobal = this.globalBudget.tryAcquire(queuedBytes);
|
|
204
|
+
if (releaseGlobal === null) {
|
|
205
|
+
this.recordGap("capacity-exceeded", safeObservedBytes, true);
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this.queuedMessages += 1;
|
|
210
|
+
this.queuedBytes += queuedBytes;
|
|
211
|
+
this.operation = this.operation
|
|
212
|
+
.then(async () => {
|
|
213
|
+
if (this.storageFailed) {
|
|
214
|
+
this.recordGap("write-failed", safeObservedBytes, true);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
await this.appendInternal(text);
|
|
218
|
+
})
|
|
219
|
+
.catch(() => {
|
|
220
|
+
this.storageFailed = true;
|
|
221
|
+
this.recordGap("write-failed", safeObservedBytes, true);
|
|
222
|
+
})
|
|
223
|
+
.finally(() => {
|
|
224
|
+
this.queuedMessages -= 1;
|
|
225
|
+
this.queuedBytes -= queuedBytes;
|
|
226
|
+
releaseGlobal();
|
|
227
|
+
});
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
71
231
|
async finalize(): Promise<RawStreamSource> {
|
|
232
|
+
this.accepting = false;
|
|
72
233
|
const next = this.operation.then(() => this.finalizeInternal());
|
|
73
234
|
this.operation = next.then(
|
|
74
235
|
() => undefined,
|
|
@@ -100,14 +261,29 @@ export class RawStreamCapture {
|
|
|
100
261
|
}
|
|
101
262
|
}
|
|
102
263
|
|
|
264
|
+
private recordGap(
|
|
265
|
+
reason: RawStreamCaptureGapReason,
|
|
266
|
+
observedBytes: number,
|
|
267
|
+
countChunk: boolean,
|
|
268
|
+
): void {
|
|
269
|
+
this.incomplete = true;
|
|
270
|
+
this.gapReason ??= reason;
|
|
271
|
+
if (countChunk) this.droppedChunks += 1;
|
|
272
|
+
this.droppedBytes += observedBytes;
|
|
273
|
+
}
|
|
274
|
+
|
|
103
275
|
private async finalizeInternal(): Promise<RawStreamSource> {
|
|
104
276
|
if (!this.closed) {
|
|
105
277
|
this.closed = true;
|
|
106
278
|
await this.flushFileBuffer();
|
|
107
279
|
if (this.fileHandle !== null) {
|
|
108
|
-
|
|
109
|
-
await this.fileHandle.close();
|
|
280
|
+
const handle = this.fileHandle;
|
|
110
281
|
this.fileHandle = null;
|
|
282
|
+
try {
|
|
283
|
+
await handle.sync();
|
|
284
|
+
} finally {
|
|
285
|
+
await handle.close();
|
|
286
|
+
}
|
|
111
287
|
}
|
|
112
288
|
}
|
|
113
289
|
if (this.filePath !== null) {
|
|
@@ -120,16 +296,21 @@ export class RawStreamCapture {
|
|
|
120
296
|
const path = createRawStreamPath();
|
|
121
297
|
assert(ensurePrivateDirectorySync(getRawStreamDir()), "Could not secure raw-stream directory");
|
|
122
298
|
const handle = await open(path, "wx", PRIVATE_FILE_MODE);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
await handle.
|
|
127
|
-
|
|
128
|
-
|
|
299
|
+
let committed = false;
|
|
300
|
+
try {
|
|
301
|
+
const memoryText = this.chunks.join("");
|
|
302
|
+
if (memoryText.length > 0) await handle.write(memoryText, undefined, "utf-8");
|
|
303
|
+
assert(securePrivateFileSync(path), "Could not secure raw-stream file");
|
|
304
|
+
this.filePath = path;
|
|
305
|
+
this.fileHandle = handle;
|
|
306
|
+
this.chunks = [];
|
|
307
|
+
committed = true;
|
|
308
|
+
} finally {
|
|
309
|
+
if (!committed) {
|
|
310
|
+
await handle.close().catch(() => undefined);
|
|
311
|
+
await unlink(path).catch(() => undefined);
|
|
312
|
+
}
|
|
129
313
|
}
|
|
130
|
-
this.filePath = path;
|
|
131
|
-
this.fileHandle = handle;
|
|
132
|
-
this.chunks = [];
|
|
133
314
|
}
|
|
134
315
|
|
|
135
316
|
private async flushFileBuffer(): Promise<void> {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { MAX_ACTIVE_PROXY_REQUESTS } from "../lib/resourceLimits";
|
|
2
|
+
|
|
1
3
|
export type RuntimeRequestLease = {
|
|
2
4
|
release: () => void;
|
|
3
5
|
};
|
|
@@ -13,7 +15,7 @@ function notifyDrained(): void {
|
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export function acquireRuntimeRequest(): RuntimeRequestLease | null {
|
|
16
|
-
if (!acceptingRequests) return null;
|
|
18
|
+
if (!acceptingRequests || activeRequests >= MAX_ACTIVE_PROXY_REQUESTS) return null;
|
|
17
19
|
activeRequests += 1;
|
|
18
20
|
let released = false;
|
|
19
21
|
return {
|
|
@@ -41,8 +43,9 @@ export async function waitForRuntimeTrafficDrain(): Promise<void> {
|
|
|
41
43
|
export function getRuntimeAdmissionState(): {
|
|
42
44
|
acceptingRequests: boolean;
|
|
43
45
|
activeRequests: number;
|
|
46
|
+
capacity: number;
|
|
44
47
|
} {
|
|
45
|
-
return { acceptingRequests, activeRequests };
|
|
48
|
+
return { acceptingRequests, activeRequests, capacity: MAX_ACTIVE_PROXY_REQUESTS };
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
export function _resetRuntimeAdmissionForTests(): void {
|
|
@@ -5,6 +5,9 @@ import { getDataDir } from "./dataDir";
|
|
|
5
5
|
import { getFinalizerRuntimeHealth } from "./logFinalizer";
|
|
6
6
|
import { ensurePrivateDirectorySync, PRIVATE_FILE_MODE } from "./privateDataPath";
|
|
7
7
|
import { isSqliteLogIndexReady } from "./sqliteLogIndex";
|
|
8
|
+
import { recoverCapturedLogStorage } from "./jsonlRecovery";
|
|
9
|
+
import { resolveLogDir } from "./logger";
|
|
10
|
+
import { loadIndex } from "./logIndex";
|
|
8
11
|
|
|
9
12
|
const DEFAULT_CACHE_MS = 2_000;
|
|
10
13
|
const DEFAULT_MIN_DISK_HEADROOM_BYTES = 256 * 1024 * 1024;
|
|
@@ -118,7 +121,17 @@ function checkQueue(): Promise<ReadinessCheckLevel> {
|
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
async function checkIndex(): Promise<ReadinessCheckLevel> {
|
|
121
|
-
|
|
124
|
+
const recovery = await recoverCapturedLogStorage(resolveLogDir());
|
|
125
|
+
if (recovery.level === "unavailable") return "unavailable";
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
await loadIndex();
|
|
129
|
+
} catch {
|
|
130
|
+
return "unavailable";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const sqliteReady = await isSqliteLogIndexReady();
|
|
134
|
+
return recovery.level === "degraded" || !sqliteReady ? "degraded" : "ok";
|
|
122
135
|
}
|
|
123
136
|
|
|
124
137
|
function checkShutdown(): Promise<ReadinessCheckLevel> {
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
parseManagedInstanceShutdownMessage,
|
|
4
|
+
readManagedInstanceLaunch,
|
|
5
|
+
} from "../lib/managedInstance";
|
|
1
6
|
import { closeLogFinalizerWorkers, forceCloseLogFinalizerWorkers } from "./logFinalizer";
|
|
2
7
|
import { flushIndex } from "./logIndex";
|
|
3
8
|
import { flushApplicationLog, flushLogBuffer } from "./logger";
|
|
4
9
|
import { flushAllRawStreamCaptures } from "./rawStreamCapture";
|
|
5
10
|
import { setRuntimeShuttingDown } from "./runtimeHealth";
|
|
6
11
|
import { stopRuntimeAdmission, waitForRuntimeTrafficDrain } from "./runtimeAdmission";
|
|
7
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
createShutdownCoordinator,
|
|
14
|
+
createShutdownSignalHandler,
|
|
15
|
+
type ShutdownResult,
|
|
16
|
+
} from "./shutdownCoordinator";
|
|
8
17
|
import { destroyAllSessionProcesses } from "./sessionProcess";
|
|
18
|
+
import {
|
|
19
|
+
beginEcosystemTaskShutdown,
|
|
20
|
+
forceCloseEcosystemTasks,
|
|
21
|
+
waitForEcosystemTasksToClose,
|
|
22
|
+
} from "./ecosystemTasks";
|
|
9
23
|
import { flushSessionTasks } from "./sessionRuntime";
|
|
10
24
|
import { closeSqliteLogIndex } from "./sqliteLogIndex";
|
|
11
25
|
import { flushSessionArchiveWrites } from "./store";
|
|
@@ -36,6 +50,7 @@ async function flushPersistence(): Promise<void> {
|
|
|
36
50
|
}
|
|
37
51
|
|
|
38
52
|
async function closeResources(): Promise<void> {
|
|
53
|
+
await waitForEcosystemTasksToClose();
|
|
39
54
|
await closeLogFinalizerWorkers();
|
|
40
55
|
destroyAllSessionProcesses();
|
|
41
56
|
closeSqliteLogIndex();
|
|
@@ -43,6 +58,7 @@ async function closeResources(): Promise<void> {
|
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
function forceCloseResources(): void {
|
|
61
|
+
forceCloseEcosystemTasks();
|
|
46
62
|
forceCloseLogFinalizerWorkers();
|
|
47
63
|
destroyAllSessionProcesses();
|
|
48
64
|
closeSqliteLogIndex();
|
|
@@ -50,7 +66,10 @@ function forceCloseResources(): void {
|
|
|
50
66
|
|
|
51
67
|
const runtimeShutdownCoordinator = createShutdownCoordinator(
|
|
52
68
|
{
|
|
53
|
-
markShuttingDown: () =>
|
|
69
|
+
markShuttingDown: () => {
|
|
70
|
+
setRuntimeShuttingDown(true);
|
|
71
|
+
beginEcosystemTaskShutdown();
|
|
72
|
+
},
|
|
54
73
|
stopAdmission: stopRuntimeAdmission,
|
|
55
74
|
drainTraffic: waitForRuntimeTrafficDrain,
|
|
56
75
|
drainFinalizers,
|
|
@@ -62,9 +81,72 @@ const runtimeShutdownCoordinator = createShutdownCoordinator(
|
|
|
62
81
|
);
|
|
63
82
|
|
|
64
83
|
let signalHandlersInstalled = false;
|
|
84
|
+
let managedInstanceHandlersInstalled = false;
|
|
85
|
+
|
|
86
|
+
export type ManagedRuntimeShutdownController = {
|
|
87
|
+
handleDisconnect: () => void;
|
|
88
|
+
handleMessage: (message: unknown) => void;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
type ManagedRuntimeShutdownControllerOptions = {
|
|
92
|
+
controlToken: string;
|
|
93
|
+
shutdown: () => Promise<ShutdownResult>;
|
|
94
|
+
exit: (code: number) => void;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
function managedControlTokensMatch(received: string, expected: string): boolean {
|
|
98
|
+
if (received.length === 0 || expected.length === 0) return false;
|
|
99
|
+
const receivedDigest = createHash("sha256").update(received).digest();
|
|
100
|
+
const expectedDigest = createHash("sha256").update(expected).digest();
|
|
101
|
+
return timingSafeEqual(receivedDigest, expectedDigest);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function createManagedRuntimeShutdownController(
|
|
105
|
+
options: ManagedRuntimeShutdownControllerOptions,
|
|
106
|
+
): ManagedRuntimeShutdownController {
|
|
107
|
+
let shutdownRequested = false;
|
|
108
|
+
const requestShutdown = (): void => {
|
|
109
|
+
if (shutdownRequested) return;
|
|
110
|
+
shutdownRequested = true;
|
|
111
|
+
void options.shutdown().then(
|
|
112
|
+
(result) => options.exit(result.forced ? 1 : 0),
|
|
113
|
+
() => options.exit(1),
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
handleDisconnect: requestShutdown,
|
|
118
|
+
handleMessage: (message) => {
|
|
119
|
+
const parsed = parseManagedInstanceShutdownMessage(message);
|
|
120
|
+
if (
|
|
121
|
+
parsed === null ||
|
|
122
|
+
!managedControlTokensMatch(parsed.controlToken, options.controlToken)
|
|
123
|
+
) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
requestShutdown();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Install lifecycle cleanup for the private supervisor/runtime IPC channel. */
|
|
132
|
+
export function installRuntimeManagedInstanceShutdownHandlers(): void {
|
|
133
|
+
if (managedInstanceHandlersInstalled) return;
|
|
134
|
+
const launchResult = readManagedInstanceLaunch(process.env);
|
|
135
|
+
if (launchResult.kind !== "managed") return;
|
|
136
|
+
managedInstanceHandlersInstalled = true;
|
|
137
|
+
const controller = createManagedRuntimeShutdownController({
|
|
138
|
+
controlToken: launchResult.launch.controlToken,
|
|
139
|
+
shutdown: async () => await runtimeShutdownCoordinator.shutdown("manual"),
|
|
140
|
+
exit: (code) => process.exit(code),
|
|
141
|
+
});
|
|
142
|
+
process.on("message", controller.handleMessage);
|
|
143
|
+
process.once("disconnect", controller.handleDisconnect);
|
|
144
|
+
if (process.connected === false) process.nextTick(controller.handleDisconnect);
|
|
145
|
+
}
|
|
65
146
|
|
|
66
147
|
/** Install once in the server runtime; the CLI supervisor owns its own public-ingress signals. */
|
|
67
148
|
export function installRuntimeShutdownSignalHandlers(): void {
|
|
149
|
+
installRuntimeManagedInstanceShutdownHandlers();
|
|
68
150
|
if (signalHandlersInstalled) return;
|
|
69
151
|
signalHandlersInstalled = true;
|
|
70
152
|
const handleSignal = createShutdownSignalHandler(runtimeShutdownCoordinator, (result) => {
|
package/src/proxy/schemas.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
export {
|
|
3
|
+
export {
|
|
4
|
+
CaptureIncompleteReasonSchema,
|
|
5
|
+
CapturedLogSchema,
|
|
6
|
+
JsonValueSchema,
|
|
7
|
+
StreamingChunkSchema,
|
|
8
|
+
} from "../contracts";
|
|
4
9
|
export type {
|
|
5
10
|
CapturedLog,
|
|
6
11
|
ApiFormat,
|
|
12
|
+
CaptureIncompleteReason,
|
|
7
13
|
JsonValue,
|
|
8
14
|
StreamingChunk,
|
|
9
15
|
TokenUsage,
|
|
@@ -40,6 +40,7 @@ ON CONFLICT(id) DO UPDATE SET
|
|
|
40
40
|
`;
|
|
41
41
|
|
|
42
42
|
let didLogUnavailable = false;
|
|
43
|
+
let cachedArchiveMax: { root: string; maxId: number } | null = null;
|
|
43
44
|
|
|
44
45
|
export function getSessionArchiveRoot(): string {
|
|
45
46
|
return join(resolveLogDir(), SESSION_ARCHIVE_DIR);
|
|
@@ -236,23 +237,49 @@ async function openSessionArchiveDb(filePath: string, create: boolean): Promise<
|
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
239
|
|
|
239
|
-
export async function
|
|
240
|
-
const
|
|
241
|
-
|
|
240
|
+
export async function archiveSessionLogs(logs: readonly CapturedLog[]): Promise<number> {
|
|
241
|
+
const bySession = new Map<string, CapturedLog[]>();
|
|
242
|
+
for (const log of logs) {
|
|
243
|
+
const sessionId = log.sessionId;
|
|
244
|
+
if (sessionId === null || sessionId.trim().length === 0) continue;
|
|
245
|
+
const existing = bySession.get(sessionId);
|
|
246
|
+
if (existing === undefined) bySession.set(sessionId, [log]);
|
|
247
|
+
else existing.push(log);
|
|
248
|
+
}
|
|
242
249
|
|
|
243
|
-
|
|
244
|
-
|
|
250
|
+
let archivedCount = 0;
|
|
251
|
+
for (const [sessionId, sessionLogs] of bySession) {
|
|
252
|
+
const db = await openSessionArchiveDb(getSessionArchivePath(sessionId), true);
|
|
253
|
+
if (db === null || !execSql(db, "BEGIN IMMEDIATE")) {
|
|
254
|
+
if (db !== null) closeDatabase(db);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
245
257
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
258
|
+
let committed = false;
|
|
259
|
+
try {
|
|
260
|
+
const updatedAt = new Date().toISOString();
|
|
261
|
+
const archived = sessionLogs.every((log) =>
|
|
262
|
+
runPrepared(db, UPSERT_SQL, [log.id, sessionId, JSON.stringify(log), updatedAt]),
|
|
263
|
+
);
|
|
264
|
+
if (archived && execSql(db, "COMMIT")) {
|
|
265
|
+
committed = true;
|
|
266
|
+
archivedCount += sessionLogs.length;
|
|
267
|
+
if (cachedArchiveMax?.root === getSessionArchiveRoot()) {
|
|
268
|
+
for (const log of sessionLogs) {
|
|
269
|
+
cachedArchiveMax.maxId = Math.max(cachedArchiveMax.maxId, log.id);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
} finally {
|
|
274
|
+
if (!committed) void execSql(db, "ROLLBACK");
|
|
275
|
+
closeDatabase(db);
|
|
276
|
+
}
|
|
255
277
|
}
|
|
278
|
+
return archivedCount;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export async function archiveSessionLog(log: CapturedLog): Promise<boolean> {
|
|
282
|
+
return (await archiveSessionLogs([log])) === 1;
|
|
256
283
|
}
|
|
257
284
|
|
|
258
285
|
function readString(row: unknown, name: string): string | null {
|
|
@@ -343,6 +370,36 @@ async function listArchiveDatabasePaths(): Promise<string[]> {
|
|
|
343
370
|
}
|
|
344
371
|
}
|
|
345
372
|
|
|
373
|
+
/**
|
|
374
|
+
* Return the highest ID retained by an authoritative session archive. The global SQLite index is a
|
|
375
|
+
* rebuildable projection and must never influence ID allocation, while archive IDs must remain
|
|
376
|
+
* reserved even after their corresponding global JSONL records have aged out.
|
|
377
|
+
*/
|
|
378
|
+
export async function getSessionArchiveMaxLogId(): Promise<number> {
|
|
379
|
+
const root = getSessionArchiveRoot();
|
|
380
|
+
if (cachedArchiveMax?.root === root) return cachedArchiveMax.maxId;
|
|
381
|
+
|
|
382
|
+
let maxId = 0;
|
|
383
|
+
const archivePaths = await listArchiveDatabasePaths();
|
|
384
|
+
for (const archivePath of archivePaths) {
|
|
385
|
+
const db = await openSessionArchiveDb(archivePath, false);
|
|
386
|
+
if (db === null) continue;
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const rows = allPrepared(db, "SELECT MAX(id) AS max_id FROM session_logs");
|
|
390
|
+
const archiveMax = readNumber(rows[0], "max_id");
|
|
391
|
+
if (archiveMax !== null && Number.isSafeInteger(archiveMax) && archiveMax >= 0) {
|
|
392
|
+
maxId = Math.max(maxId, archiveMax);
|
|
393
|
+
}
|
|
394
|
+
} finally {
|
|
395
|
+
closeDatabase(db);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
cachedArchiveMax = { root, maxId };
|
|
400
|
+
return maxId;
|
|
401
|
+
}
|
|
402
|
+
|
|
346
403
|
export async function findArchivedLogById(id: number): Promise<CapturedLog | null> {
|
|
347
404
|
const archivePaths = await listArchiveDatabasePaths();
|
|
348
405
|
for (const archivePath of archivePaths) {
|
|
@@ -405,6 +462,8 @@ export async function deleteArchivedLogsByIds(ids: ReadonlySet<number>): Promise
|
|
|
405
462
|
}
|
|
406
463
|
}
|
|
407
464
|
|
|
465
|
+
if (removedIds.size > 0) cachedArchiveMax = null;
|
|
466
|
+
|
|
408
467
|
return removedIds;
|
|
409
468
|
}
|
|
410
469
|
|
|
@@ -415,9 +474,14 @@ export async function clearSessionArchives(): Promise<number> {
|
|
|
415
474
|
const archivePaths = await listArchiveDatabasePaths();
|
|
416
475
|
try {
|
|
417
476
|
await rm(root, { recursive: true, force: true });
|
|
477
|
+
cachedArchiveMax = null;
|
|
418
478
|
return archivePaths.length;
|
|
419
479
|
} catch (err) {
|
|
420
480
|
logger.warn("[sessionArchive] Failed to clear session archives:", String(err));
|
|
421
481
|
return 0;
|
|
422
482
|
}
|
|
423
483
|
}
|
|
484
|
+
|
|
485
|
+
export function _resetSessionArchiveCacheForTests(): void {
|
|
486
|
+
cachedArchiveMax = null;
|
|
487
|
+
}
|
package/src/proxy/sessionInfo.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SessionInfo, SessionLogSummary, SessionTokenUsage } from "../lib/sessionInfoContract";
|
|
2
|
-
import {
|
|
2
|
+
import { withBasePath } from "../lib/basePath";
|
|
3
|
+
import { getPublicBasePath } from "../lib/publicBasePathContext";
|
|
3
4
|
import { getSessionPath } from "../lib/sessionUrl";
|
|
4
5
|
import type { CapturedLog } from "./schemas";
|
|
5
6
|
import type { SessionSnapshot } from "./sessionSupervisor";
|
|
@@ -106,14 +107,14 @@ function buildAbsoluteUrl(baseUrl: string, path: string): string {
|
|
|
106
107
|
|
|
107
108
|
function buildApiPath(sessionId: string): string {
|
|
108
109
|
return buildRelativePath(
|
|
109
|
-
withBasePath("/api/sessions",
|
|
110
|
+
withBasePath("/api/sessions", getPublicBasePath()),
|
|
110
111
|
new URLSearchParams({ sessionId }),
|
|
111
112
|
);
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
function buildCompactLogsPath(sessionId: string): string {
|
|
115
116
|
return buildRelativePath(
|
|
116
|
-
withBasePath("/api/logs",
|
|
117
|
+
withBasePath("/api/logs", getPublicBasePath()),
|
|
117
118
|
new URLSearchParams({ compact: "1", sessionId }),
|
|
118
119
|
);
|
|
119
120
|
}
|
|
@@ -211,7 +212,7 @@ export function buildSessionInfo(input: BuildSessionInfoInput): SessionInfo {
|
|
|
211
212
|
const queuedTasks = input.snapshot?.queuedTasks ?? 0;
|
|
212
213
|
const runningTasks = input.snapshot?.runningTasks ?? 0;
|
|
213
214
|
const lastTaskError = normalizeString(input.snapshot?.lastTaskError);
|
|
214
|
-
const inspectorPath = getSessionPath(input.sessionId,
|
|
215
|
+
const inspectorPath = getSessionPath(input.sessionId, getPublicBasePath());
|
|
215
216
|
const apiPath = buildApiPath(input.sessionId);
|
|
216
217
|
const compactLogsPath = buildCompactLogsPath(input.sessionId);
|
|
217
218
|
const latestLogs = summaries.slice(0, latestLogLimit).map((summary, index) => ({
|