@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { logger, resolveLogDir } from "./logger";
|
|
4
|
-
import type { ApiFormat } from "./schemas";
|
|
4
|
+
import type { ApiFormat, CaptureIncompleteReason } from "./schemas";
|
|
5
5
|
import type { LogIndexEntry, LogIndexSummary } from "./logIndex";
|
|
6
6
|
import { ensurePrivateDirectorySync, securePrivateFileSync } from "./privateDataPath";
|
|
7
7
|
|
|
@@ -47,6 +47,10 @@ CREATE TABLE IF NOT EXISTS log_index (
|
|
|
47
47
|
streaming_chunks_path TEXT,
|
|
48
48
|
raw_request_body_bytes INTEGER,
|
|
49
49
|
response_text_bytes INTEGER,
|
|
50
|
+
capture_incomplete INTEGER NOT NULL DEFAULT 0,
|
|
51
|
+
dropped_chunks INTEGER NOT NULL DEFAULT 0,
|
|
52
|
+
dropped_bytes INTEGER NOT NULL DEFAULT 0,
|
|
53
|
+
capture_incomplete_reason TEXT,
|
|
50
54
|
warnings_json TEXT,
|
|
51
55
|
error TEXT
|
|
52
56
|
);
|
|
@@ -89,10 +93,14 @@ INSERT INTO log_index (
|
|
|
89
93
|
streaming_chunks_path,
|
|
90
94
|
raw_request_body_bytes,
|
|
91
95
|
response_text_bytes,
|
|
96
|
+
capture_incomplete,
|
|
97
|
+
dropped_chunks,
|
|
98
|
+
dropped_bytes,
|
|
99
|
+
capture_incomplete_reason,
|
|
92
100
|
warnings_json,
|
|
93
101
|
error
|
|
94
102
|
) VALUES (
|
|
95
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
103
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
96
104
|
)
|
|
97
105
|
ON CONFLICT(id) DO UPDATE SET
|
|
98
106
|
file = excluded.file,
|
|
@@ -126,6 +134,10 @@ ON CONFLICT(id) DO UPDATE SET
|
|
|
126
134
|
streaming_chunks_path = excluded.streaming_chunks_path,
|
|
127
135
|
raw_request_body_bytes = excluded.raw_request_body_bytes,
|
|
128
136
|
response_text_bytes = excluded.response_text_bytes,
|
|
137
|
+
capture_incomplete = excluded.capture_incomplete,
|
|
138
|
+
dropped_chunks = excluded.dropped_chunks,
|
|
139
|
+
dropped_bytes = excluded.dropped_bytes,
|
|
140
|
+
capture_incomplete_reason = excluded.capture_incomplete_reason,
|
|
129
141
|
warnings_json = excluded.warnings_json,
|
|
130
142
|
error = excluded.error
|
|
131
143
|
`;
|
|
@@ -215,6 +227,30 @@ function execSql(db: unknown, sql: string): boolean {
|
|
|
215
227
|
return result !== undefined;
|
|
216
228
|
}
|
|
217
229
|
|
|
230
|
+
const REQUIRED_SCHEMA_COLUMNS = [
|
|
231
|
+
{ name: "capture_incomplete", definition: "INTEGER NOT NULL DEFAULT 0" },
|
|
232
|
+
{ name: "dropped_chunks", definition: "INTEGER NOT NULL DEFAULT 0" },
|
|
233
|
+
{ name: "dropped_bytes", definition: "INTEGER NOT NULL DEFAULT 0" },
|
|
234
|
+
{ name: "capture_incomplete_reason", definition: "TEXT" },
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
function ensureSchemaColumns(db: unknown): boolean {
|
|
238
|
+
const rows = allPrepared(db, "PRAGMA table_info(log_index)");
|
|
239
|
+
if (rows === null) return false;
|
|
240
|
+
const existing = new Set<string>();
|
|
241
|
+
for (const row of rows) {
|
|
242
|
+
const name = readProperty(row, "name");
|
|
243
|
+
if (typeof name === "string") existing.add(name);
|
|
244
|
+
}
|
|
245
|
+
for (const column of REQUIRED_SCHEMA_COLUMNS) {
|
|
246
|
+
if (existing.has(column.name)) continue;
|
|
247
|
+
if (!execSql(db, `ALTER TABLE log_index ADD COLUMN ${column.name} ${column.definition}`)) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
218
254
|
function sqliteConstructorFromModule(
|
|
219
255
|
moduleValue: unknown,
|
|
220
256
|
exportName: "default" | "Database",
|
|
@@ -316,6 +352,9 @@ async function initializeSqliteState(): Promise<SqliteState> {
|
|
|
316
352
|
if (!execSql(db, CREATE_SCHEMA_SQL)) {
|
|
317
353
|
return { status: "unavailable", path, reason: "SQLite schema initialization failed" };
|
|
318
354
|
}
|
|
355
|
+
if (!ensureSchemaColumns(db)) {
|
|
356
|
+
return { status: "unavailable", path, reason: "SQLite schema migration failed" };
|
|
357
|
+
}
|
|
319
358
|
activeDb = db;
|
|
320
359
|
return { status: "ready", db, path };
|
|
321
360
|
} catch (err) {
|
|
@@ -394,6 +433,10 @@ function entryParams(entry: LogIndexEntry): readonly unknown[] {
|
|
|
394
433
|
summary?.streamingChunksPath ?? null,
|
|
395
434
|
summary?.rawRequestBodyBytes ?? null,
|
|
396
435
|
summary?.responseTextBytes ?? null,
|
|
436
|
+
boolToInt(summary?.captureIncomplete ?? false),
|
|
437
|
+
summary?.droppedChunks ?? 0,
|
|
438
|
+
summary?.droppedBytes ?? 0,
|
|
439
|
+
summary?.captureIncompleteReason ?? null,
|
|
397
440
|
warningJson(summary?.warnings),
|
|
398
441
|
summary?.error ?? null,
|
|
399
442
|
];
|
|
@@ -428,6 +471,22 @@ function readApiFormat(row: unknown): ApiFormat {
|
|
|
428
471
|
}
|
|
429
472
|
}
|
|
430
473
|
|
|
474
|
+
function readCaptureIncompleteReason(row: unknown): CaptureIncompleteReason | null {
|
|
475
|
+
const value = readString(row, "capture_incomplete_reason");
|
|
476
|
+
switch (value) {
|
|
477
|
+
case "capacity-exceeded":
|
|
478
|
+
case "pipeline-closed":
|
|
479
|
+
case "write-failed":
|
|
480
|
+
case "finalize-failed":
|
|
481
|
+
case "upstream-stream-error":
|
|
482
|
+
case "shutdown":
|
|
483
|
+
return value;
|
|
484
|
+
case null:
|
|
485
|
+
default:
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
431
490
|
function readWarnings(row: unknown): string[] | undefined {
|
|
432
491
|
const value = readString(row, "warnings_json");
|
|
433
492
|
if (value === null) return undefined;
|
|
@@ -481,6 +540,10 @@ function summaryFromRow(row: unknown, id: number): LogIndexSummary {
|
|
|
481
540
|
streamingChunksPath: readString(row, "streaming_chunks_path"),
|
|
482
541
|
rawRequestBodyBytes: readNumber(row, "raw_request_body_bytes"),
|
|
483
542
|
responseTextBytes: readNumber(row, "response_text_bytes"),
|
|
543
|
+
captureIncomplete: readBoolean(row, "capture_incomplete"),
|
|
544
|
+
droppedChunks: readNumber(row, "dropped_chunks") ?? 0,
|
|
545
|
+
droppedBytes: readNumber(row, "dropped_bytes") ?? 0,
|
|
546
|
+
captureIncompleteReason: readCaptureIncompleteReason(row),
|
|
484
547
|
warnings: readWarnings(row),
|
|
485
548
|
error: readString(row, "error"),
|
|
486
549
|
};
|
package/src/proxy/store.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
listFilteredIndexEntries,
|
|
30
30
|
listIndexEntries,
|
|
31
31
|
rebuildIndex,
|
|
32
|
+
updateLogIndexEntryMetadata,
|
|
32
33
|
type LogIndexEntry,
|
|
33
34
|
type LogIndexSummary,
|
|
34
35
|
} from "./logIndex";
|
|
@@ -42,7 +43,7 @@ import { CapturedLogSchema } from "./schemas";
|
|
|
42
43
|
import { apiFormatForPath } from "./formats/registry";
|
|
43
44
|
import type { ImportedLogDraft } from "./logImporter";
|
|
44
45
|
import {
|
|
45
|
-
|
|
46
|
+
archiveSessionLogs,
|
|
46
47
|
clearSessionArchives,
|
|
47
48
|
deleteArchivedLogsByIds,
|
|
48
49
|
findArchivedLogById,
|
|
@@ -55,6 +56,7 @@ import {
|
|
|
55
56
|
PRIVATE_FILE_MODE,
|
|
56
57
|
securePrivateFileSync,
|
|
57
58
|
} from "./privateDataPath";
|
|
59
|
+
import { recoverCapturedLogStorage } from "./jsonlRecovery";
|
|
58
60
|
import {
|
|
59
61
|
clearSessionRegistry,
|
|
60
62
|
getLogSessionId,
|
|
@@ -162,6 +164,43 @@ type LogCursorPageIndex = {
|
|
|
162
164
|
const logCursorPageIndexes: Map<string, LogCursorPageIndex> = new Map();
|
|
163
165
|
let logCursorPageIndexBuildCount = 0;
|
|
164
166
|
let sessionArchiveQueue: Promise<void> = Promise.resolve();
|
|
167
|
+
const SESSION_ARCHIVE_BATCH_SIZE = 128;
|
|
168
|
+
const SESSION_ARCHIVE_FLUSH_DELAY_MS = 10;
|
|
169
|
+
let sessionArchiveBuffer: CapturedLog[] = [];
|
|
170
|
+
let sessionArchiveFlushTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
171
|
+
|
|
172
|
+
function drainSessionArchiveBuffer(): CapturedLog[] {
|
|
173
|
+
const logs = sessionArchiveBuffer;
|
|
174
|
+
sessionArchiveBuffer = [];
|
|
175
|
+
return logs;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function queueSessionArchiveFlush(): void {
|
|
179
|
+
const logs = drainSessionArchiveBuffer();
|
|
180
|
+
if (logs.length === 0) return;
|
|
181
|
+
sessionArchiveQueue = sessionArchiveQueue
|
|
182
|
+
.catch(() => undefined)
|
|
183
|
+
.then(async () => {
|
|
184
|
+
await archiveSessionLogs(logs);
|
|
185
|
+
})
|
|
186
|
+
.catch((err: unknown) => {
|
|
187
|
+
logger.warn("[store] Failed to archive session logs:", String(err));
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function cancelSessionArchiveFlushTimer(): void {
|
|
192
|
+
if (sessionArchiveFlushTimeout === null) return;
|
|
193
|
+
clearTimeout(sessionArchiveFlushTimeout);
|
|
194
|
+
sessionArchiveFlushTimeout = null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function scheduleSessionArchiveFlush(): void {
|
|
198
|
+
if (sessionArchiveFlushTimeout !== null) return;
|
|
199
|
+
sessionArchiveFlushTimeout = setTimeout(() => {
|
|
200
|
+
sessionArchiveFlushTimeout = null;
|
|
201
|
+
queueSessionArchiveFlush();
|
|
202
|
+
}, SESSION_ARCHIVE_FLUSH_DELAY_MS);
|
|
203
|
+
}
|
|
165
204
|
|
|
166
205
|
function cursorPageIndexKey(
|
|
167
206
|
options: Pick<ListLogsCursorPageOptions, "sessionId" | "model" | "clientPid">,
|
|
@@ -233,8 +272,29 @@ function normalizeLog(log: CapturedLog): CapturedLog {
|
|
|
233
272
|
const sessionId = getLogSessionId(log);
|
|
234
273
|
const pathFormat = apiFormatForPath(log.path);
|
|
235
274
|
const apiFormat = pathFormat === "unknown" ? log.apiFormat : pathFormat;
|
|
236
|
-
|
|
237
|
-
|
|
275
|
+
const captureIncomplete = log.captureIncomplete ?? false;
|
|
276
|
+
const droppedChunks = log.droppedChunks ?? 0;
|
|
277
|
+
const droppedBytes = log.droppedBytes ?? 0;
|
|
278
|
+
const captureIncompleteReason = log.captureIncompleteReason ?? null;
|
|
279
|
+
if (
|
|
280
|
+
sessionId === log.sessionId &&
|
|
281
|
+
apiFormat === log.apiFormat &&
|
|
282
|
+
log.captureIncomplete !== undefined &&
|
|
283
|
+
log.droppedChunks !== undefined &&
|
|
284
|
+
log.droppedBytes !== undefined &&
|
|
285
|
+
log.captureIncompleteReason !== undefined
|
|
286
|
+
) {
|
|
287
|
+
return log;
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
...log,
|
|
291
|
+
sessionId,
|
|
292
|
+
apiFormat,
|
|
293
|
+
captureIncomplete,
|
|
294
|
+
droppedChunks,
|
|
295
|
+
droppedBytes,
|
|
296
|
+
captureIncompleteReason,
|
|
297
|
+
};
|
|
238
298
|
}
|
|
239
299
|
|
|
240
300
|
function addToCache(log: CapturedLog): void {
|
|
@@ -249,19 +309,20 @@ function addToCache(log: CapturedLog): void {
|
|
|
249
309
|
}
|
|
250
310
|
|
|
251
311
|
function archiveLogSnapshot(log: CapturedLog): void {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
logger.warn("[store] Failed to archive session log:", String(err));
|
|
260
|
-
});
|
|
312
|
+
sessionArchiveBuffer.push(normalizeLog(log));
|
|
313
|
+
if (sessionArchiveBuffer.length >= SESSION_ARCHIVE_BATCH_SIZE) {
|
|
314
|
+
cancelSessionArchiveFlushTimer();
|
|
315
|
+
queueSessionArchiveFlush();
|
|
316
|
+
} else if (sessionArchiveBuffer.length === 1) {
|
|
317
|
+
scheduleSessionArchiveFlush();
|
|
318
|
+
}
|
|
261
319
|
}
|
|
262
320
|
|
|
263
321
|
export async function flushSessionArchiveWrites(): Promise<void> {
|
|
322
|
+
cancelSessionArchiveFlushTimer();
|
|
323
|
+
if (sessionArchiveBuffer.length > 0) queueSessionArchiveFlush();
|
|
264
324
|
await sessionArchiveQueue;
|
|
325
|
+
if (sessionArchiveBuffer.length > 0) await flushSessionArchiveWrites();
|
|
265
326
|
}
|
|
266
327
|
|
|
267
328
|
function removeFromCache(id: number): void {
|
|
@@ -317,6 +378,10 @@ function compactLogFromIndexSummary(summary: LogIndexSummary): CapturedLog {
|
|
|
317
378
|
rawRequestBodyBytes: summary.rawRequestBodyBytes,
|
|
318
379
|
responseTextBytes: summary.responseTextBytes,
|
|
319
380
|
bodyContentMode: "compact",
|
|
381
|
+
captureIncomplete: summary.captureIncomplete,
|
|
382
|
+
droppedChunks: summary.droppedChunks,
|
|
383
|
+
droppedBytes: summary.droppedBytes,
|
|
384
|
+
captureIncompleteReason: summary.captureIncompleteReason,
|
|
320
385
|
warnings: summary.warnings,
|
|
321
386
|
error: summary.error,
|
|
322
387
|
});
|
|
@@ -441,6 +506,10 @@ export async function createLog(
|
|
|
441
506
|
clientCwd: clientInfo?.cwd ?? null,
|
|
442
507
|
clientProjectFolder: clientInfo?.projectFolder ?? null,
|
|
443
508
|
streamingChunksPath: null,
|
|
509
|
+
captureIncomplete: false,
|
|
510
|
+
droppedChunks: 0,
|
|
511
|
+
droppedBytes: 0,
|
|
512
|
+
captureIncompleteReason: null,
|
|
444
513
|
});
|
|
445
514
|
|
|
446
515
|
// Write to disk and update index
|
|
@@ -466,6 +535,12 @@ export function finalizeLogUpdate(log: CapturedLog): void {
|
|
|
466
535
|
emitLogUpdate(log);
|
|
467
536
|
}
|
|
468
537
|
|
|
538
|
+
/** Persist the terminal cache/session state and refresh both JSON and SQLite index summaries. */
|
|
539
|
+
export async function persistFinalizedLogUpdate(log: CapturedLog): Promise<void> {
|
|
540
|
+
finalizeLogUpdate(log);
|
|
541
|
+
await updateLogIndexEntryMetadata(log);
|
|
542
|
+
}
|
|
543
|
+
|
|
469
544
|
async function getArchivedLogByIdForStore(id: number): Promise<CapturedLog | null> {
|
|
470
545
|
await flushSessionArchiveWrites();
|
|
471
546
|
const archivedLog = await findArchivedLogById(id);
|
|
@@ -543,24 +618,31 @@ async function scanLogFileForId(filePath: string, id: number): Promise<CapturedL
|
|
|
543
618
|
crlfDelay: Infinity,
|
|
544
619
|
});
|
|
545
620
|
|
|
621
|
+
let lineNumber = 0;
|
|
546
622
|
for await (const line of fileStream) {
|
|
623
|
+
lineNumber += 1;
|
|
547
624
|
if (line.trim() === "") continue;
|
|
625
|
+
let parsed: unknown;
|
|
548
626
|
try {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
627
|
+
parsed = JSON.parse(line);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
return await Promise.reject(
|
|
630
|
+
new Error(
|
|
631
|
+
`Malformed JSONL record in ${filePath} at line ${String(lineNumber)}: ${String(error)}`,
|
|
632
|
+
),
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
const result = CapturedLogSchema.safeParse(parsed);
|
|
636
|
+
if (!result.success) {
|
|
637
|
+
return await Promise.reject(
|
|
638
|
+
new Error(`Invalid captured-log record in ${filePath} at line ${String(lineNumber)}`),
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
if (result.data.id === id) {
|
|
642
|
+
lastMatch = normalizeLog(result.data);
|
|
643
|
+
if (result.data.responseStatus !== null) {
|
|
644
|
+
return lastMatch;
|
|
561
645
|
}
|
|
562
|
-
} catch {
|
|
563
|
-
// Skip malformed lines
|
|
564
646
|
}
|
|
565
647
|
}
|
|
566
648
|
|
|
@@ -597,6 +679,13 @@ async function visitPersistedLogs(visitor: PersistedLogVisitor): Promise<void> {
|
|
|
597
679
|
const logDir = resolveLogDir();
|
|
598
680
|
if (!existsSync(logDir)) return;
|
|
599
681
|
|
|
682
|
+
const recovery = await recoverCapturedLogStorage(logDir);
|
|
683
|
+
if (recovery.level === "unavailable") {
|
|
684
|
+
return await Promise.reject(
|
|
685
|
+
new Error(recovery.diagnostic ?? "Captured-log recovery blocked persisted-log reads"),
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
600
689
|
try {
|
|
601
690
|
const entries = await readdirCallback(logDir);
|
|
602
691
|
const files = entries.filter((file) => file.endsWith(".jsonl")).sort();
|
|
@@ -605,6 +694,9 @@ async function visitPersistedLogs(visitor: PersistedLogVisitor): Promise<void> {
|
|
|
605
694
|
}
|
|
606
695
|
} catch (err) {
|
|
607
696
|
logger.error("[store] Failed to list persisted logs:", String(err));
|
|
697
|
+
return await Promise.reject(
|
|
698
|
+
err instanceof Error ? err : new Error(`Failed to list persisted logs: ${String(err)}`),
|
|
699
|
+
);
|
|
608
700
|
}
|
|
609
701
|
}
|
|
610
702
|
|
|
@@ -620,21 +712,36 @@ async function visitPersistedLogFile(
|
|
|
620
712
|
crlfDelay: Infinity,
|
|
621
713
|
});
|
|
622
714
|
|
|
715
|
+
let lineNumber = 0;
|
|
623
716
|
for await (const line of fileStream) {
|
|
717
|
+
lineNumber += 1;
|
|
624
718
|
if (line.trim() === "") continue;
|
|
719
|
+
let parsed: unknown;
|
|
625
720
|
try {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
// Skip malformed lines.
|
|
721
|
+
parsed = JSON.parse(line);
|
|
722
|
+
} catch (error) {
|
|
723
|
+
return await Promise.reject(
|
|
724
|
+
new Error(
|
|
725
|
+
`Malformed JSONL record in ${filePath} at line ${String(lineNumber)}: ${String(error)}`,
|
|
726
|
+
),
|
|
727
|
+
);
|
|
634
728
|
}
|
|
729
|
+
const result = CapturedLogSchema.safeParse(parsed);
|
|
730
|
+
if (!result.success) {
|
|
731
|
+
return await Promise.reject(
|
|
732
|
+
new Error(`Invalid captured-log record in ${filePath} at line ${String(lineNumber)}`),
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
const log = normalizeLog(result.data);
|
|
736
|
+
visitor(log);
|
|
635
737
|
}
|
|
636
738
|
} catch (err) {
|
|
637
739
|
logger.error("[store] Failed to read persisted log file:", filePath, String(err));
|
|
740
|
+
return await Promise.reject(
|
|
741
|
+
err instanceof Error
|
|
742
|
+
? err
|
|
743
|
+
: new Error(`Failed to read persisted log file ${filePath}: ${String(err)}`),
|
|
744
|
+
);
|
|
638
745
|
}
|
|
639
746
|
}
|
|
640
747
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { extname, relative, resolve, sep } from "node:path";
|
|
4
|
+
|
|
5
|
+
const CONTENT_TYPES: Readonly<Record<string, string>> = {
|
|
6
|
+
".css": "text/css; charset=utf-8",
|
|
7
|
+
".html": "text/html; charset=utf-8",
|
|
8
|
+
".ico": "image/x-icon",
|
|
9
|
+
".jpeg": "image/jpeg",
|
|
10
|
+
".jpg": "image/jpeg",
|
|
11
|
+
".js": "text/javascript; charset=utf-8",
|
|
12
|
+
".json": "application/json; charset=utf-8",
|
|
13
|
+
".map": "application/json; charset=utf-8",
|
|
14
|
+
".png": "image/png",
|
|
15
|
+
".svg": "image/svg+xml",
|
|
16
|
+
".webp": "image/webp",
|
|
17
|
+
".woff": "font/woff",
|
|
18
|
+
".woff2": "font/woff2",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type UiAsset = {
|
|
22
|
+
body: Buffer;
|
|
23
|
+
cacheControl: string;
|
|
24
|
+
contentType: string;
|
|
25
|
+
document: boolean;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function safeDecodedPath(pathname: string): string | null {
|
|
29
|
+
let decoded: string;
|
|
30
|
+
try {
|
|
31
|
+
decoded = decodeURIComponent(pathname);
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (decoded.includes("\\") || decoded.includes("\0")) return null;
|
|
36
|
+
if (decoded.split("/").some((segment) => segment === "." || segment === "..")) return null;
|
|
37
|
+
return decoded;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isInsideDirectory(path: string, directory: string): boolean {
|
|
41
|
+
const child = relative(directory, path);
|
|
42
|
+
return child === "" || (!child.startsWith("..") && !child.startsWith(sep));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function assetPath(uiDirectory: string, pathname: string): string | null {
|
|
46
|
+
const decoded = safeDecodedPath(pathname);
|
|
47
|
+
if (decoded === null) return null;
|
|
48
|
+
const target = resolve(uiDirectory, `.${decoded}`);
|
|
49
|
+
return isInsideDirectory(target, resolve(uiDirectory)) ? target : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function readAsset(path: string): Promise<Buffer | null> {
|
|
53
|
+
try {
|
|
54
|
+
return await readFile(path);
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function contentTypeFor(path: string): string {
|
|
61
|
+
return CONTENT_TYPES[extname(path).toLowerCase()] ?? "application/octet-stream";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isBackendNamespace(pathname: string): boolean {
|
|
65
|
+
return (
|
|
66
|
+
pathname === "/api" ||
|
|
67
|
+
pathname.startsWith("/api/") ||
|
|
68
|
+
pathname === "/proxy" ||
|
|
69
|
+
pathname.startsWith("/proxy/") ||
|
|
70
|
+
pathname === "/livez" ||
|
|
71
|
+
pathname === "/readyz"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function shouldUseDocumentFallback(pathname: string): boolean {
|
|
76
|
+
if (pathname === "/" || pathname.length === 0) return true;
|
|
77
|
+
const lastSegment = pathname.split("/").at(-1) ?? "";
|
|
78
|
+
return !lastSegment.includes(".");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function loadUiAsset(uiDirectory: string, pathname: string): Promise<UiAsset | null> {
|
|
82
|
+
if (isBackendNamespace(pathname)) return null;
|
|
83
|
+
const requestedPath = assetPath(uiDirectory, pathname);
|
|
84
|
+
if (requestedPath === null) return null;
|
|
85
|
+
const requestedBody = await readAsset(requestedPath);
|
|
86
|
+
if (requestedBody !== null) {
|
|
87
|
+
return {
|
|
88
|
+
body: requestedBody,
|
|
89
|
+
cacheControl: pathname.startsWith("/assets/")
|
|
90
|
+
? "public, max-age=31536000, immutable"
|
|
91
|
+
: "no-cache",
|
|
92
|
+
contentType: contentTypeFor(requestedPath),
|
|
93
|
+
document: extname(requestedPath).toLowerCase() === ".html",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (!shouldUseDocumentFallback(pathname)) return null;
|
|
97
|
+
const indexPath = resolve(uiDirectory, "index.html");
|
|
98
|
+
const indexBody = await readAsset(indexPath);
|
|
99
|
+
if (indexBody === null) return null;
|
|
100
|
+
return {
|
|
101
|
+
body: indexBody,
|
|
102
|
+
cacheControl: "no-store",
|
|
103
|
+
contentType: CONTENT_TYPES[".html"] ?? "text/html; charset=utf-8",
|
|
104
|
+
document: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
package/src/proxy/upstream.ts
CHANGED
|
@@ -55,8 +55,9 @@ export function selectUpstreamBase(route: ApiRoute, provider: ProviderConfig | n
|
|
|
55
55
|
if (formatSpecificUrl !== undefined && formatSpecificUrl !== "") {
|
|
56
56
|
return formatSpecificUrl;
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const legacyBaseUrl = selectLegacyBaseUrl(route, provider);
|
|
59
|
+
if (legacyBaseUrl !== undefined) {
|
|
60
|
+
return legacyBaseUrl;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// Legacy records may only contain format. Keep this fallback for compatibility,
|
|
@@ -68,6 +69,22 @@ function nonEmptyUrl(value: string | undefined): string | undefined {
|
|
|
68
69
|
return value !== undefined && value !== "" ? value : undefined;
|
|
69
70
|
}
|
|
70
71
|
|
|
72
|
+
function hasFormatSpecificUrlFields(provider: ProviderConfig): boolean {
|
|
73
|
+
return (
|
|
74
|
+
provider.anthropicBaseUrl !== undefined ||
|
|
75
|
+
provider.openaiBaseUrl !== undefined ||
|
|
76
|
+
provider.openaiResponsesBaseUrl !== undefined
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function selectLegacyBaseUrl(route: ApiRoute, provider: ProviderConfig): string | undefined {
|
|
81
|
+
if (hasFormatSpecificUrlFields(provider)) return undefined;
|
|
82
|
+
const baseUrl = nonEmptyUrl(provider.baseUrl);
|
|
83
|
+
if (baseUrl === undefined) return undefined;
|
|
84
|
+
if (route.isOpenAI) return provider.format === "openai" ? baseUrl : undefined;
|
|
85
|
+
return provider.format === "anthropic" ? baseUrl : undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
71
88
|
function selectFormatSpecificUrl(route: ApiRoute, provider: ProviderConfig): string | undefined {
|
|
72
89
|
if (!route.isOpenAI) return provider.anthropicBaseUrl;
|
|
73
90
|
if (route.isResponses) {
|
|
@@ -76,6 +93,16 @@ function selectFormatSpecificUrl(route: ApiRoute, provider: ProviderConfig): str
|
|
|
76
93
|
return provider.openaiBaseUrl;
|
|
77
94
|
}
|
|
78
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Whether a model-matched Provider explicitly supports the protocol selected by the request path.
|
|
98
|
+
* Empty current-schema fields are deliberate opt-outs. Only a legacy record with no protocol URL
|
|
99
|
+
* fields may use baseUrl, and then only for the protocol declared by provider.format.
|
|
100
|
+
*/
|
|
101
|
+
export function providerSupportsApiRoute(route: ApiRoute, provider: ProviderConfig): boolean {
|
|
102
|
+
if (nonEmptyUrl(selectFormatSpecificUrl(route, provider)) !== undefined) return true;
|
|
103
|
+
return selectLegacyBaseUrl(route, provider) !== undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
79
106
|
export function setUpstreamHost(headers: Headers, upstreamBase: string): void {
|
|
80
107
|
try {
|
|
81
108
|
headers.set(HEADER_HOST, new URL(upstreamBase).host);
|