@tonyclaw/agent-inspector 2.0.0
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/cli.js +1611 -0
- package/.output/nitro.json +17 -0
- package/.output/public/assets/CompareDrawer-CU5ZrWcL.js +1 -0
- package/.output/public/assets/ProxyViewerContainer-pEBqVp1d.js +101 -0
- package/.output/public/assets/ReplayDialog-F58yNg5j.js +1 -0
- package/.output/public/assets/RequestAnatomy-C9lT0qE_.js +1 -0
- package/.output/public/assets/ResponseView-DHJq6bnz.js +1 -0
- package/.output/public/assets/StreamingChunkSequence-BTgfpFUT.js +1 -0
- package/.output/public/assets/_sessionId-DsNRbnNm.js +1 -0
- package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
- package/.output/public/assets/index-CpWG2hFn.css +1 -0
- package/.output/public/assets/index-DmBV8Gve.js +1 -0
- package/.output/public/assets/json-viewer-CZVYLR8j.js +14 -0
- package/.output/public/assets/main-DHs7FBK3.js +18 -0
- package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
- package/.output/public/assets/qwen-CONDcHqt.png +0 -0
- package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
- package/.output/server/_chunks/ssr-renderer.mjs +22 -0
- package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
- package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +2 -0
- package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +2 -0
- package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +2 -0
- package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
- package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
- package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
- package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
- package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
- package/.output/server/_libs/ajv-formats.mjs +330 -0
- package/.output/server/_libs/ajv.mjs +11444 -0
- package/.output/server/_libs/aria-hidden.mjs +122 -0
- package/.output/server/_libs/atomically.mjs +152 -0
- package/.output/server/_libs/bail.mjs +8 -0
- package/.output/server/_libs/cfworker__json-schema.mjs +1 -0
- package/.output/server/_libs/character-entities.mjs +2130 -0
- package/.output/server/_libs/class-variance-authority.mjs +44 -0
- package/.output/server/_libs/clsx.mjs +16 -0
- package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
- package/.output/server/_libs/conf.mjs +635 -0
- package/.output/server/_libs/cookie-es.mjs +58 -0
- package/.output/server/_libs/core-util-is.mjs +75 -0
- package/.output/server/_libs/croner.mjs +1 -0
- package/.output/server/_libs/crossws.mjs +1 -0
- package/.output/server/_libs/debounce-fn.mjs +69 -0
- package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
- package/.output/server/_libs/dequal.mjs +27 -0
- package/.output/server/_libs/detect-node-es.mjs +1 -0
- package/.output/server/_libs/devlop.mjs +8 -0
- package/.output/server/_libs/diff.mjs +320 -0
- package/.output/server/_libs/dot-prop.mjs +265 -0
- package/.output/server/_libs/env-paths.mjs +57 -0
- package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
- package/.output/server/_libs/extend.mjs +97 -0
- package/.output/server/_libs/fast-deep-equal.mjs +38 -0
- package/.output/server/_libs/fast-uri.mjs +812 -0
- package/.output/server/_libs/floating-ui__core.mjs +725 -0
- package/.output/server/_libs/floating-ui__dom.mjs +622 -0
- package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
- package/.output/server/_libs/floating-ui__utils.mjs +320 -0
- package/.output/server/_libs/get-nonce.mjs +9 -0
- package/.output/server/_libs/h3-v2.mjs +276 -0
- package/.output/server/_libs/h3.mjs +408 -0
- package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
- package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
- package/.output/server/_libs/hookable.mjs +1 -0
- package/.output/server/_libs/html-url-attributes.mjs +26 -0
- package/.output/server/_libs/immediate.mjs +74 -0
- package/.output/server/_libs/inherits.mjs +50 -0
- package/.output/server/_libs/inline-style-parser.mjs +142 -0
- package/.output/server/_libs/is-plain-obj.mjs +10 -0
- package/.output/server/_libs/isarray.mjs +14 -0
- package/.output/server/_libs/isbot.mjs +20 -0
- package/.output/server/_libs/json-schema-traverse.mjs +180 -0
- package/.output/server/_libs/jszip.mjs +3051 -0
- package/.output/server/_libs/lie.mjs +273 -0
- package/.output/server/_libs/lucide-react.mjs +492 -0
- package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
- package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
- package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
- package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
- package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
- package/.output/server/_libs/micromark-factory-label.mjs +63 -0
- package/.output/server/_libs/micromark-factory-space.mjs +24 -0
- package/.output/server/_libs/micromark-factory-title.mjs +65 -0
- package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
- package/.output/server/_libs/micromark-util-character.mjs +44 -0
- package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
- package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
- package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
- package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
- package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
- package/.output/server/_libs/micromark-util-encode.mjs +1 -0
- package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
- package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
- package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
- package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
- package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
- package/.output/server/_libs/micromark.mjs +906 -0
- package/.output/server/_libs/mimic-function.mjs +47 -0
- package/.output/server/_libs/modelcontextprotocol__server.mjs +9738 -0
- package/.output/server/_libs/ocache.mjs +1 -0
- package/.output/server/_libs/ohash.mjs +1 -0
- package/.output/server/_libs/pako.mjs +4223 -0
- package/.output/server/_libs/process-nextick-args.mjs +48 -0
- package/.output/server/_libs/property-information.mjs +1209 -0
- package/.output/server/_libs/radix-ui.mjs +1 -0
- package/.output/server/_libs/radix-ui__number.mjs +6 -0
- package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
- package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
- package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
- package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
- package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
- package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
- package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
- package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
- package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
- package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
- package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
- package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
- package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
- package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
- package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
- package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
- package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
- package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
- package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
- package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
- package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
- package/.output/server/_libs/radix-ui__react-toast.mjs +2 -0
- package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
- package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
- package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
- package/.output/server/_libs/react-dom.mjs +10781 -0
- package/.output/server/_libs/react-markdown.mjs +147 -0
- package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
- package/.output/server/_libs/react-remove-scroll.mjs +328 -0
- package/.output/server/_libs/react-style-singleton.mjs +69 -0
- package/.output/server/_libs/react.mjs +515 -0
- package/.output/server/_libs/readable-stream.mjs +1518 -0
- package/.output/server/_libs/remark-parse.mjs +19 -0
- package/.output/server/_libs/remark-rehype.mjs +21 -0
- package/.output/server/_libs/rou3.mjs +14 -0
- package/.output/server/_libs/safe-buffer.mjs +64 -0
- package/.output/server/_libs/semver.mjs +1938 -0
- package/.output/server/_libs/seroval-plugins.mjs +58 -0
- package/.output/server/_libs/seroval.mjs +1765 -0
- package/.output/server/_libs/setimmediate.mjs +152 -0
- package/.output/server/_libs/space-separated-tokens.mjs +6 -0
- package/.output/server/_libs/srvx.mjs +1029 -0
- package/.output/server/_libs/stubborn-fs.mjs +91 -0
- package/.output/server/_libs/stubborn-utils.mjs +66 -0
- package/.output/server/_libs/style-to-js.mjs +72 -0
- package/.output/server/_libs/style-to-object.mjs +38 -0
- package/.output/server/_libs/swr.mjs +939 -0
- package/.output/server/_libs/tailwind-merge.mjs +3010 -0
- package/.output/server/_libs/tanstack__history.mjs +217 -0
- package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
- package/.output/server/_libs/tanstack__react-store.mjs +1 -0
- package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
- package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
- package/.output/server/_libs/tanstack__store.mjs +1 -0
- package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
- package/.output/server/_libs/tiny-invariant.mjs +12 -0
- package/.output/server/_libs/tiny-warning.mjs +5 -0
- package/.output/server/_libs/trim-lines.mjs +41 -0
- package/.output/server/_libs/trough.mjs +85 -0
- package/.output/server/_libs/tslib.mjs +1 -0
- package/.output/server/_libs/ufo.mjs +54 -0
- package/.output/server/_libs/uint8array-extras.mjs +69 -0
- package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
- package/.output/server/_libs/unified.mjs +661 -0
- package/.output/server/_libs/unist-util-is.mjs +100 -0
- package/.output/server/_libs/unist-util-position.mjs +27 -0
- package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
- package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
- package/.output/server/_libs/unist-util-visit.mjs +24 -0
- package/.output/server/_libs/unstorage.mjs +1 -0
- package/.output/server/_libs/use-callback-ref.mjs +66 -0
- package/.output/server/_libs/use-sidecar.mjs +106 -0
- package/.output/server/_libs/use-sync-external-store.mjs +64 -0
- package/.output/server/_libs/util-deprecate.mjs +12 -0
- package/.output/server/_libs/vfile-message.mjs +138 -0
- package/.output/server/_libs/vfile.mjs +467 -0
- package/.output/server/_libs/when-exit.mjs +53 -0
- package/.output/server/_libs/zod.mjs +4524 -0
- package/.output/server/_sessionId-wMLPvC5g.mjs +123 -0
- package/.output/server/_ssr/CompareDrawer-BU4V0uVf.mjs +1041 -0
- package/.output/server/_ssr/ProxyViewerContainer-BnRwFEnn.mjs +5972 -0
- package/.output/server/_ssr/ReplayDialog-C7dn9pd_.mjs +322 -0
- package/.output/server/_ssr/RequestAnatomy-C1rWpe9-.mjs +353 -0
- package/.output/server/_ssr/ResponseView-hGpPaYsf.mjs +602 -0
- package/.output/server/_ssr/StreamingChunkSequence-BRWI1r_G.mjs +302 -0
- package/.output/server/_ssr/index-BKURLVPz.mjs +118 -0
- package/.output/server/_ssr/index.mjs +1184 -0
- package/.output/server/_ssr/json-viewer-BBd2DtQP.mjs +515 -0
- package/.output/server/_ssr/router-BcZ0D6AB.mjs +6317 -0
- package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
- package/.output/server/_tanstack-start-manifest_v-1y8ZVxRI.mjs +4 -0
- package/.output/server/index.mjs +436 -0
- package/.output/server/node_modules/tslib/modules/index.js +70 -0
- package/.output/server/node_modules/tslib/modules/package.json +3 -0
- package/.output/server/node_modules/tslib/package.json +47 -0
- package/.output/server/node_modules/tslib/tslib.js +484 -0
- package/.output/server/package.json +9 -0
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/package.json +110 -0
- package/src/assets/favicon.svg +31 -0
- package/src/assets/logos/alibaba.svg +1 -0
- package/src/assets/logos/anthropic.svg +1 -0
- package/src/assets/logos/claude-code.svg +4 -0
- package/src/assets/logos/deepseek.svg +1 -0
- package/src/assets/logos/mcp.png +0 -0
- package/src/assets/logos/minimax.jpeg +0 -0
- package/src/assets/logos/openai.svg +1 -0
- package/src/assets/logos/opencode.svg +4 -0
- package/src/assets/logos/qwen.png +0 -0
- package/src/assets/logos/zhipuai.svg +219 -0
- package/src/cli/detect-tools.ts +147 -0
- package/src/cli/doctor.ts +521 -0
- package/src/cli/onboard.ts +224 -0
- package/src/cli/templates/command-onboard.ts +17 -0
- package/src/cli/templates/skill-onboard.ts +547 -0
- package/src/cli.ts +345 -0
- package/src/components/OnboardingBanner.tsx +67 -0
- package/src/components/ProxyViewer.tsx +545 -0
- package/src/components/ProxyViewerContainer.tsx +363 -0
- package/src/components/providers/ImportWizardDialog.tsx +349 -0
- package/src/components/providers/ProviderCard.tsx +474 -0
- package/src/components/providers/ProviderForm.tsx +494 -0
- package/src/components/providers/ProviderLogo.tsx +117 -0
- package/src/components/providers/ProvidersPanel.tsx +619 -0
- package/src/components/providers/SettingsDialog.tsx +202 -0
- package/src/components/proxy-viewer/CompareDrawer.tsx +893 -0
- package/src/components/proxy-viewer/ConversationGroup.tsx +107 -0
- package/src/components/proxy-viewer/ConversationHeader.tsx +300 -0
- package/src/components/proxy-viewer/LogEntry.tsx +543 -0
- package/src/components/proxy-viewer/LogEntryHeader.tsx +501 -0
- package/src/components/proxy-viewer/ReplayDialog.tsx +218 -0
- package/src/components/proxy-viewer/ResponseView.tsx +171 -0
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +188 -0
- package/src/components/proxy-viewer/ThreadConnector.tsx +136 -0
- package/src/components/proxy-viewer/TurnGroup.tsx +337 -0
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +98 -0
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +196 -0
- package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +53 -0
- package/src/components/proxy-viewer/anatomy/types.ts +39 -0
- package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +114 -0
- package/src/components/proxy-viewer/cacheTrend.ts +50 -0
- package/src/components/proxy-viewer/diff/DiffView.tsx +321 -0
- package/src/components/proxy-viewer/diff/computeDiff.ts +178 -0
- package/src/components/proxy-viewer/diff/index.ts +3 -0
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +157 -0
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +66 -0
- package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +21 -0
- package/src/components/proxy-viewer/formats/index.tsx +33 -0
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +170 -0
- package/src/components/proxy-viewer/index.ts +9 -0
- package/src/components/proxy-viewer/lazy.ts +37 -0
- package/src/components/proxy-viewer/log-formats/anthropic.ts +194 -0
- package/src/components/proxy-viewer/log-formats/index.ts +23 -0
- package/src/components/proxy-viewer/log-formats/openai.ts +167 -0
- package/src/components/proxy-viewer/log-formats/types.ts +40 -0
- package/src/components/proxy-viewer/log-formats/unknown.ts +18 -0
- package/src/components/proxy-viewer/logEntryVisibility.ts +39 -0
- package/src/components/proxy-viewer/requestDiff.ts +277 -0
- package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +190 -0
- package/src/components/proxy-viewer/viewerState.ts +66 -0
- package/src/components/ui/badge.tsx +47 -0
- package/src/components/ui/button.tsx +47 -0
- package/src/components/ui/collapsible.tsx +21 -0
- package/src/components/ui/confirm-dialog.tsx +51 -0
- package/src/components/ui/crab-logo.tsx +95 -0
- package/src/components/ui/crab-variants.tsx +467 -0
- package/src/components/ui/dialog.tsx +129 -0
- package/src/components/ui/json-expansion-button.tsx +56 -0
- package/src/components/ui/json-viewer-bulk.ts +97 -0
- package/src/components/ui/json-viewer.tsx +494 -0
- package/src/components/ui/mcp-logo.tsx +20 -0
- package/src/components/ui/scroll-area.tsx +54 -0
- package/src/components/ui/select.tsx +178 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/tabs.tsx +88 -0
- package/src/components/ui/tooltip.tsx +51 -0
- package/src/index.css +11 -0
- package/src/knowledge/candidateStore.ts +63 -0
- package/src/knowledge/distiller.ts +98 -0
- package/src/knowledge/openclawClient.ts +118 -0
- package/src/knowledge/redactor.ts +80 -0
- package/src/knowledge/types.ts +84 -0
- package/src/lib/apiClient.ts +49 -0
- package/src/lib/export-logs.ts +51 -0
- package/src/lib/mask.ts +4 -0
- package/src/lib/objectUtils.ts +22 -0
- package/src/lib/providerContract.ts +26 -0
- package/src/lib/providerTestContract.ts +107 -0
- package/src/lib/runtimeConfig.ts +25 -0
- package/src/lib/serverPort.ts +41 -0
- package/src/lib/sessionUrl.ts +44 -0
- package/src/lib/stopReason.ts +58 -0
- package/src/lib/useOnboarding.ts +80 -0
- package/src/lib/useProviders.ts +30 -0
- package/src/lib/useStripConfig.ts +108 -0
- package/src/lib/utils.ts +21 -0
- package/src/mcp/loopback.ts +76 -0
- package/src/mcp/previewExtractor.ts +166 -0
- package/src/mcp/server.ts +396 -0
- package/src/mcp/toolHandlers.ts +341 -0
- package/src/proxy/chunkStorage.ts +112 -0
- package/src/proxy/claudeCodeStrip.ts +99 -0
- package/src/proxy/config.ts +172 -0
- package/src/proxy/constants.ts +47 -0
- package/src/proxy/dataDir.ts +86 -0
- package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
- package/src/proxy/formats/anthropic/handler.ts +71 -0
- package/src/proxy/formats/anthropic/index.ts +14 -0
- package/src/proxy/formats/anthropic/register.ts +4 -0
- package/src/proxy/formats/anthropic/schemas.ts +237 -0
- package/src/proxy/formats/anthropic/stream.ts +205 -0
- package/src/proxy/formats/handler.ts +46 -0
- package/src/proxy/formats/index.ts +12 -0
- package/src/proxy/formats/jsonSchema.ts +36 -0
- package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
- package/src/proxy/formats/openai/handler.ts +96 -0
- package/src/proxy/formats/openai/index.ts +25 -0
- package/src/proxy/formats/openai/provider.ts +50 -0
- package/src/proxy/formats/openai/register.ts +4 -0
- package/src/proxy/formats/openai/schemas.ts +187 -0
- package/src/proxy/formats/openai/stream.ts +206 -0
- package/src/proxy/formats/protocol.ts +50 -0
- package/src/proxy/formats/providerRegistry.ts +51 -0
- package/src/proxy/formats/providers/index.ts +3 -0
- package/src/proxy/formats/registry.ts +66 -0
- package/src/proxy/handler.ts +334 -0
- package/src/proxy/logFinalizer.ts +305 -0
- package/src/proxy/logFinalizer.worker.ts +24 -0
- package/src/proxy/logIndex.ts +268 -0
- package/src/proxy/logger.ts +179 -0
- package/src/proxy/openaiOrphanToolStrip.ts +142 -0
- package/src/proxy/providerImporters.ts +491 -0
- package/src/proxy/providers.ts +613 -0
- package/src/proxy/schemas.ts +209 -0
- package/src/proxy/sessionProcess.ts +140 -0
- package/src/proxy/sessionRuntime.ts +85 -0
- package/src/proxy/sessionSupervisor.ts +283 -0
- package/src/proxy/sessionWorkerEntry.ts +26 -0
- package/src/proxy/socketTracker.ts +255 -0
- package/src/proxy/store.ts +412 -0
- package/src/proxy/upstream.ts +90 -0
- package/src/router.tsx +16 -0
- package/src/routes/__root.tsx +45 -0
- package/src/routes/api/config.paths.ts +14 -0
- package/src/routes/api/config.ts +53 -0
- package/src/routes/api/health.ts +15 -0
- package/src/routes/api/knowledge.candidates.$candidateId.promote.ts +32 -0
- package/src/routes/api/knowledge.candidates.ts +10 -0
- package/src/routes/api/knowledge.project-context.ts +18 -0
- package/src/routes/api/knowledge.search.ts +31 -0
- package/src/routes/api/knowledge.sessions.$sessionId.candidates.ts +16 -0
- package/src/routes/api/logs.$id.chunks.ts +36 -0
- package/src/routes/api/logs.$id.replay.ts +191 -0
- package/src/routes/api/logs.$id.ts +22 -0
- package/src/routes/api/logs.stream.ts +74 -0
- package/src/routes/api/logs.ts +59 -0
- package/src/routes/api/mcp.ts +25 -0
- package/src/routes/api/models.ts +10 -0
- package/src/routes/api/providers.$providerId.test.log.ts +293 -0
- package/src/routes/api/providers.$providerId.ts +50 -0
- package/src/routes/api/providers.export.ts +26 -0
- package/src/routes/api/providers.import.ts +47 -0
- package/src/routes/api/providers.scan.ts +23 -0
- package/src/routes/api/providers.ts +45 -0
- package/src/routes/api/sessions.ts +17 -0
- package/src/routes/index.tsx +6 -0
- package/src/routes/proxy/$.ts +15 -0
- package/src/routes/session/$sessionId.tsx +23 -0
- package/styles/globals.css +188 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { type JSX, useCallback, useEffect, useMemo, useRef, useState, Suspense } from "react";
|
|
2
|
+
import { ArrowLeft, Check, Copy, Download, Plus } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import type { CapturedLog } from "../proxy/schemas";
|
|
5
|
+
import { exportLogsAsZip } from "../lib/export-logs";
|
|
6
|
+
import { formatTokens } from "../lib/utils";
|
|
7
|
+
import packageJson from "../../package.json";
|
|
8
|
+
import { ConversationGroup, groupLogsByConversation } from "./proxy-viewer";
|
|
9
|
+
|
|
10
|
+
import { CrabLogo } from "./ui/crab-logo";
|
|
11
|
+
import { crabVariants } from "./ui/crab-variants";
|
|
12
|
+
import { McpLogo } from "./ui/mcp-logo";
|
|
13
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
|
14
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
|
|
15
|
+
import { SettingsDialog } from "./providers/SettingsDialog";
|
|
16
|
+
import { computeCacheTrends } from "./proxy-viewer/cacheTrend";
|
|
17
|
+
import { LazyCompareDrawer } from "./proxy-viewer/lazy";
|
|
18
|
+
import { buildValidPredecessors } from "./proxy-viewer/viewerState";
|
|
19
|
+
import { useKeyboardNavigation } from "./proxy-viewer/useKeyboardNavigation";
|
|
20
|
+
|
|
21
|
+
function truncateSessionId(id: string): string {
|
|
22
|
+
if (id.length <= 30) return id;
|
|
23
|
+
return id.slice(0, 12) + "…" + id.slice(-12);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function computeTokenSummary(logs: CapturedLog[]): { totalIn: number; totalOut: number } {
|
|
27
|
+
let totalIn = 0;
|
|
28
|
+
let totalOut = 0;
|
|
29
|
+
for (const log of logs) {
|
|
30
|
+
if (log.inputTokens !== null) totalIn += log.inputTokens;
|
|
31
|
+
if (log.outputTokens !== null) totalOut += log.outputTokens;
|
|
32
|
+
}
|
|
33
|
+
return { totalIn, totalOut };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function formatTimeRange(logs: CapturedLog[]): string | null {
|
|
37
|
+
const first = logs[0];
|
|
38
|
+
const last = logs[logs.length - 1];
|
|
39
|
+
if (first === undefined || last === undefined) return null;
|
|
40
|
+
const format = (iso: string): string =>
|
|
41
|
+
new Date(iso).toLocaleTimeString([], {
|
|
42
|
+
hour: "2-digit",
|
|
43
|
+
minute: "2-digit",
|
|
44
|
+
second: "2-digit",
|
|
45
|
+
});
|
|
46
|
+
return `${format(first.timestamp)} - ${format(last.timestamp)}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getFirstUserAgent(logs: CapturedLog[]): string | null {
|
|
50
|
+
for (const log of logs) {
|
|
51
|
+
if (log.userAgent !== null && log.userAgent !== undefined && log.userAgent !== "") {
|
|
52
|
+
return log.userAgent;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function CopyableCommand({ command }: { command: string }): JSX.Element {
|
|
59
|
+
const [copied, setCopied] = useState(false);
|
|
60
|
+
|
|
61
|
+
const handleCopy = useCallback(() => {
|
|
62
|
+
void window.navigator.clipboard.writeText(command).then(() => {
|
|
63
|
+
setCopied(true);
|
|
64
|
+
setTimeout(() => setCopied(false), 2000);
|
|
65
|
+
});
|
|
66
|
+
}, [command]);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="inline-flex items-center gap-2 bg-muted rounded-md px-3 py-2">
|
|
70
|
+
<pre className="text-blue-500 font-mono text-sm m-0">{command}</pre>
|
|
71
|
+
<button
|
|
72
|
+
type="button"
|
|
73
|
+
onClick={handleCopy}
|
|
74
|
+
className="text-muted-foreground hover:text-foreground transition-colors shrink-0 cursor-pointer"
|
|
75
|
+
aria-label="Copy command"
|
|
76
|
+
>
|
|
77
|
+
{copied ? (
|
|
78
|
+
<svg
|
|
79
|
+
width="16"
|
|
80
|
+
height="16"
|
|
81
|
+
viewBox="0 0 24 24"
|
|
82
|
+
fill="none"
|
|
83
|
+
stroke="currentColor"
|
|
84
|
+
strokeWidth="2"
|
|
85
|
+
strokeLinecap="round"
|
|
86
|
+
strokeLinejoin="round"
|
|
87
|
+
>
|
|
88
|
+
<path d="M20 6 9 17l-5-5" />
|
|
89
|
+
</svg>
|
|
90
|
+
) : (
|
|
91
|
+
<svg
|
|
92
|
+
width="16"
|
|
93
|
+
height="16"
|
|
94
|
+
viewBox="0 0 24 24"
|
|
95
|
+
fill="none"
|
|
96
|
+
stroke="currentColor"
|
|
97
|
+
strokeWidth="2"
|
|
98
|
+
strokeLinecap="round"
|
|
99
|
+
strokeLinejoin="round"
|
|
100
|
+
>
|
|
101
|
+
<rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
|
|
102
|
+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
|
|
103
|
+
</svg>
|
|
104
|
+
)}
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function McpReadyBadge(): JSX.Element {
|
|
111
|
+
return (
|
|
112
|
+
<TooltipProvider>
|
|
113
|
+
<Tooltip>
|
|
114
|
+
<TooltipTrigger asChild>
|
|
115
|
+
<span className="inline-flex h-7 items-center gap-2 rounded-md border border-cyan-400/30 bg-cyan-500/10 px-2.5 font-mono text-[11px] font-medium text-cyan-300 shadow-[0_0_16px_rgba(34,211,238,0.08)]">
|
|
116
|
+
<span className="size-1.5 rounded-full bg-emerald-300 shadow-[0_0_8px_rgba(110,231,183,0.8)]" />
|
|
117
|
+
MCP Ready
|
|
118
|
+
<span className="hidden text-cyan-200/70 sm:inline">/api/mcp</span>
|
|
119
|
+
</span>
|
|
120
|
+
</TooltipTrigger>
|
|
121
|
+
<TooltipContent sideOffset={8} className="max-w-[320px] text-left leading-relaxed">
|
|
122
|
+
Coding agents can inspect logs, replay requests, test providers, and debug sessions
|
|
123
|
+
through MCP at /api/mcp.
|
|
124
|
+
</TooltipContent>
|
|
125
|
+
</Tooltip>
|
|
126
|
+
</TooltipProvider>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function SessionContextBar({
|
|
131
|
+
sessionId,
|
|
132
|
+
logs,
|
|
133
|
+
totalIn,
|
|
134
|
+
totalOut,
|
|
135
|
+
}: {
|
|
136
|
+
sessionId: string;
|
|
137
|
+
logs: CapturedLog[];
|
|
138
|
+
totalIn: number;
|
|
139
|
+
totalOut: number;
|
|
140
|
+
}): JSX.Element {
|
|
141
|
+
const [copied, setCopied] = useState(false);
|
|
142
|
+
const timeRange = useMemo(() => formatTimeRange(logs), [logs]);
|
|
143
|
+
const userAgent = useMemo(() => getFirstUserAgent(logs), [logs]);
|
|
144
|
+
|
|
145
|
+
const handleCopyLink = useCallback(() => {
|
|
146
|
+
void window.navigator.clipboard.writeText(window.location.href).then(() => {
|
|
147
|
+
setCopied(true);
|
|
148
|
+
setTimeout(() => setCopied(false), 2000);
|
|
149
|
+
});
|
|
150
|
+
}, []);
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<div className="mb-4 flex items-center gap-3 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs">
|
|
154
|
+
<a
|
|
155
|
+
href="/"
|
|
156
|
+
className="inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
157
|
+
aria-label="Back to all sessions"
|
|
158
|
+
title="Back to all sessions"
|
|
159
|
+
>
|
|
160
|
+
<ArrowLeft className="size-3.5" />
|
|
161
|
+
</a>
|
|
162
|
+
<div className="min-w-0 flex-1">
|
|
163
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
164
|
+
<span className="font-mono font-semibold text-purple-400/90 truncate" title={sessionId}>
|
|
165
|
+
{truncateSessionId(sessionId)}
|
|
166
|
+
</span>
|
|
167
|
+
{userAgent !== null && (
|
|
168
|
+
<span
|
|
169
|
+
className="font-mono text-muted-foreground truncate max-w-[220px]"
|
|
170
|
+
title={userAgent}
|
|
171
|
+
>
|
|
172
|
+
{userAgent}
|
|
173
|
+
</span>
|
|
174
|
+
)}
|
|
175
|
+
</div>
|
|
176
|
+
<div className="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground">
|
|
177
|
+
<span>
|
|
178
|
+
{logs.length} request{logs.length !== 1 ? "s" : ""}
|
|
179
|
+
</span>
|
|
180
|
+
{timeRange !== null && <span>{timeRange}</span>}
|
|
181
|
+
{(totalIn > 0 || totalOut > 0) && (
|
|
182
|
+
<span className="font-mono">
|
|
183
|
+
{formatTokens(totalIn)} in / {formatTokens(totalOut)} out
|
|
184
|
+
</span>
|
|
185
|
+
)}
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
<button
|
|
189
|
+
type="button"
|
|
190
|
+
onClick={handleCopyLink}
|
|
191
|
+
className="inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
192
|
+
aria-label={copied ? "Copied session link" : "Copy session link"}
|
|
193
|
+
title={copied ? "Copied session link" : "Copy session link"}
|
|
194
|
+
>
|
|
195
|
+
{copied ? <Check className="size-3.5" /> : <Copy className="size-3.5" />}
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export type ProxyViewerProps = {
|
|
202
|
+
logs: CapturedLog[];
|
|
203
|
+
sessions: string[];
|
|
204
|
+
models: string[];
|
|
205
|
+
selectedSession: string;
|
|
206
|
+
selectedModel: string;
|
|
207
|
+
onSessionChange: (session: string) => void;
|
|
208
|
+
onModelChange: (model: string) => void;
|
|
209
|
+
onClearAll: () => void;
|
|
210
|
+
/** Clear only the logs whose ids are passed. Called by the per-group
|
|
211
|
+
* Clear button on each conversation header. */
|
|
212
|
+
onClearGroup: (ids: number[]) => void;
|
|
213
|
+
viewMode: "simple" | "full";
|
|
214
|
+
onViewModeChange: (mode: "simple" | "full") => void;
|
|
215
|
+
/** Live strip-Claude-Code-billing-header flag, sourced once at the container. */
|
|
216
|
+
strip: boolean;
|
|
217
|
+
/** Slow-response threshold in seconds. `0` disables the warning indicator. */
|
|
218
|
+
slowResponseThresholdSeconds: number;
|
|
219
|
+
/** Hide the session filter dropdown. Used on `/session/$id` routes where
|
|
220
|
+
* the session is already pinned by the URL and the dropdown would just
|
|
221
|
+
* fight the URL state. */
|
|
222
|
+
hideSessionFilter?: boolean;
|
|
223
|
+
/** Session id pinned by a `/session/$id` route. Enables session-page chrome. */
|
|
224
|
+
pinnedSessionId?: string;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export function ProxyViewer({
|
|
228
|
+
logs,
|
|
229
|
+
sessions,
|
|
230
|
+
models,
|
|
231
|
+
selectedSession,
|
|
232
|
+
selectedModel,
|
|
233
|
+
onSessionChange,
|
|
234
|
+
onModelChange,
|
|
235
|
+
onClearAll,
|
|
236
|
+
onClearGroup,
|
|
237
|
+
viewMode,
|
|
238
|
+
onViewModeChange,
|
|
239
|
+
strip,
|
|
240
|
+
slowResponseThresholdSeconds,
|
|
241
|
+
hideSessionFilter = false,
|
|
242
|
+
pinnedSessionId,
|
|
243
|
+
}: ProxyViewerProps): JSX.Element {
|
|
244
|
+
const { totalIn, totalOut } = useMemo(() => computeTokenSummary(logs), [logs]);
|
|
245
|
+
const [exporting, setExporting] = useState(false);
|
|
246
|
+
const [comparePair, setComparePair] = useState<[CapturedLog, CapturedLog] | null>(null);
|
|
247
|
+
const [crabEntrancePhase, setCrabEntrancePhase] = useState<"hidden" | "playing" | "done">(
|
|
248
|
+
"hidden",
|
|
249
|
+
);
|
|
250
|
+
const logListRef = useRef<HTMLDivElement>(null);
|
|
251
|
+
const logListWrapperRef = useRef<HTMLDivElement>(null);
|
|
252
|
+
useKeyboardNavigation(logListRef, logListWrapperRef);
|
|
253
|
+
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
const perCrabDuration = 400;
|
|
256
|
+
const startDelay = 50;
|
|
257
|
+
const playingDone = startDelay + crabVariants.length * perCrabDuration;
|
|
258
|
+
const t1 = setTimeout(() => setCrabEntrancePhase("playing"), startDelay);
|
|
259
|
+
const t2 = setTimeout(() => setCrabEntrancePhase("done"), playingDone);
|
|
260
|
+
return () => {
|
|
261
|
+
clearTimeout(t1);
|
|
262
|
+
clearTimeout(t2);
|
|
263
|
+
};
|
|
264
|
+
}, []);
|
|
265
|
+
|
|
266
|
+
useEffect(() => {
|
|
267
|
+
if (pinnedSessionId === undefined) {
|
|
268
|
+
document.title = "Agent Inspector";
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const requestLabel = logs.length === 1 ? "1 req" : `${logs.length} req`;
|
|
272
|
+
document.title = `${truncateSessionId(pinnedSessionId)} - ${requestLabel} - Agent Inspector`;
|
|
273
|
+
}, [logs.length, pinnedSessionId]);
|
|
274
|
+
|
|
275
|
+
const handleExport = useCallback(async () => {
|
|
276
|
+
setExporting(true);
|
|
277
|
+
try {
|
|
278
|
+
await exportLogsAsZip(logs);
|
|
279
|
+
} finally {
|
|
280
|
+
setExporting(false);
|
|
281
|
+
}
|
|
282
|
+
}, [logs]);
|
|
283
|
+
// Close the compare drawer when the user changes the session or model
|
|
284
|
+
// filter, since the predecessor relationship may no longer be meaningful.
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
setComparePair(null);
|
|
287
|
+
}, [selectedSession, selectedModel]);
|
|
288
|
+
|
|
289
|
+
const closeCompare = useCallback(() => {
|
|
290
|
+
setComparePair(null);
|
|
291
|
+
}, []);
|
|
292
|
+
|
|
293
|
+
const groups = useMemo(() => groupLogsByConversation(logs), [logs]);
|
|
294
|
+
const cacheTrends = useMemo(() => computeCacheTrends(groups), [groups]);
|
|
295
|
+
const comparisonPredecessors = useMemo(() => buildValidPredecessors(groups), [groups]);
|
|
296
|
+
const handleCompareWithPrevious = useCallback(
|
|
297
|
+
(log: CapturedLog) => {
|
|
298
|
+
const predecessor = comparisonPredecessors.get(log.id);
|
|
299
|
+
if (predecessor !== undefined) setComparePair([predecessor, log]);
|
|
300
|
+
},
|
|
301
|
+
[comparisonPredecessors],
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<div className="max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6">
|
|
306
|
+
{/* Sticky chrome — brand row, optional pinned-session context, and the
|
|
307
|
+
filter/control bar stay visible while the user scrolls through the
|
|
308
|
+
log list. `z-30` sits above the per-conversation sticky header
|
|
309
|
+
(`z-10`) but below modal-level overlays (dialogs, dropdowns,
|
|
310
|
+
tooltips — `z-50`). Background is fully opaque so logs don't bleed
|
|
311
|
+
through. */}
|
|
312
|
+
<div className="sticky top-0 z-30 bg-background pt-6">
|
|
313
|
+
{/* Brand row */}
|
|
314
|
+
<div className="grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-8">
|
|
315
|
+
<div />
|
|
316
|
+
<h1 className="flex min-w-0 flex-col items-center gap-2 text-center">
|
|
317
|
+
<span className="flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap">
|
|
318
|
+
{/* Crab family — hover to animate together */}
|
|
319
|
+
<span
|
|
320
|
+
className="flex shrink-0 items-end gap-1 group cursor-default"
|
|
321
|
+
aria-hidden="true"
|
|
322
|
+
>
|
|
323
|
+
<CrabLogo className="size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" />
|
|
324
|
+
<span className="hidden items-end gap-0.5 sm:flex">
|
|
325
|
+
{crabVariants.map((Crab, i) => {
|
|
326
|
+
const color = [
|
|
327
|
+
"text-amber-500",
|
|
328
|
+
"text-rose-500",
|
|
329
|
+
"text-sky-500",
|
|
330
|
+
"text-emerald-500",
|
|
331
|
+
"text-violet-500",
|
|
332
|
+
"text-orange-500",
|
|
333
|
+
"text-cyan-500",
|
|
334
|
+
"text-pink-500",
|
|
335
|
+
"text-lime-500",
|
|
336
|
+
"text-blue-500",
|
|
337
|
+
"text-yellow-500",
|
|
338
|
+
"text-fuchsia-500",
|
|
339
|
+
][i];
|
|
340
|
+
const entranceClass =
|
|
341
|
+
crabEntrancePhase === "hidden"
|
|
342
|
+
? "opacity-0 scale-0"
|
|
343
|
+
: crabEntrancePhase === "playing"
|
|
344
|
+
? "animate-crab-piano-pop"
|
|
345
|
+
: "";
|
|
346
|
+
return (
|
|
347
|
+
<Crab
|
|
348
|
+
key={i}
|
|
349
|
+
className={`size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`}
|
|
350
|
+
style={{
|
|
351
|
+
transitionDelay: `${i * 50}ms`,
|
|
352
|
+
...(crabEntrancePhase === "playing"
|
|
353
|
+
? { animationDelay: `${i * 400}ms` }
|
|
354
|
+
: {}),
|
|
355
|
+
}}
|
|
356
|
+
/>
|
|
357
|
+
);
|
|
358
|
+
})}
|
|
359
|
+
</span>
|
|
360
|
+
</span>
|
|
361
|
+
<span className="flex min-w-0 items-baseline gap-2 pl-1">
|
|
362
|
+
<span className="truncate text-lg font-bold">Agent Inspector</span>
|
|
363
|
+
<span className="shrink-0 font-mono text-xs font-semibold text-muted-foreground">
|
|
364
|
+
v{packageJson.version}
|
|
365
|
+
</span>
|
|
366
|
+
</span>
|
|
367
|
+
<Plus className="size-4 shrink-0 text-muted-foreground/70" aria-hidden="true" />
|
|
368
|
+
<McpLogo className="size-10 shrink-0" />
|
|
369
|
+
</span>
|
|
370
|
+
<McpReadyBadge />
|
|
371
|
+
</h1>
|
|
372
|
+
<div className="justify-self-end">
|
|
373
|
+
<SettingsDialog />
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
{pinnedSessionId !== undefined && (
|
|
378
|
+
<SessionContextBar
|
|
379
|
+
sessionId={pinnedSessionId}
|
|
380
|
+
logs={logs}
|
|
381
|
+
totalIn={totalIn}
|
|
382
|
+
totalOut={totalOut}
|
|
383
|
+
/>
|
|
384
|
+
)}
|
|
385
|
+
|
|
386
|
+
{/* Controls + Filters */}
|
|
387
|
+
<div className="flex items-center gap-3 mb-4">
|
|
388
|
+
{!hideSessionFilter && (
|
|
389
|
+
<Select value={selectedSession} onValueChange={onSessionChange}>
|
|
390
|
+
<SelectTrigger className="flex-1 max-w-[350px] text-xs">
|
|
391
|
+
<SelectValue placeholder="All sessions" />
|
|
392
|
+
</SelectTrigger>
|
|
393
|
+
<SelectContent>
|
|
394
|
+
<SelectItem value="__all__">All sessions</SelectItem>
|
|
395
|
+
{sessions.map((s) => (
|
|
396
|
+
<SelectItem key={s} value={s}>
|
|
397
|
+
{truncateSessionId(s)}
|
|
398
|
+
</SelectItem>
|
|
399
|
+
))}
|
|
400
|
+
</SelectContent>
|
|
401
|
+
</Select>
|
|
402
|
+
)}
|
|
403
|
+
<Select value={selectedModel} onValueChange={onModelChange}>
|
|
404
|
+
<SelectTrigger className="flex-1 max-w-[250px] text-xs">
|
|
405
|
+
<SelectValue placeholder="All models" />
|
|
406
|
+
</SelectTrigger>
|
|
407
|
+
<SelectContent>
|
|
408
|
+
<SelectItem value="__all__">All models</SelectItem>
|
|
409
|
+
{models.map((m) => (
|
|
410
|
+
<SelectItem key={m} value={m}>
|
|
411
|
+
{m}
|
|
412
|
+
</SelectItem>
|
|
413
|
+
))}
|
|
414
|
+
</SelectContent>
|
|
415
|
+
</Select>
|
|
416
|
+
<div className="flex items-center border border-border rounded-md overflow-hidden">
|
|
417
|
+
<button
|
|
418
|
+
type="button"
|
|
419
|
+
onClick={() => onViewModeChange("simple")}
|
|
420
|
+
className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
|
|
421
|
+
viewMode === "simple"
|
|
422
|
+
? "bg-muted text-foreground"
|
|
423
|
+
: "text-muted-foreground hover:bg-muted/50"
|
|
424
|
+
}`}
|
|
425
|
+
>
|
|
426
|
+
Simple
|
|
427
|
+
</button>
|
|
428
|
+
<button
|
|
429
|
+
type="button"
|
|
430
|
+
onClick={() => onViewModeChange("full")}
|
|
431
|
+
className={`h-8 px-3 cursor-pointer transition-colors text-xs ${
|
|
432
|
+
viewMode === "full"
|
|
433
|
+
? "bg-muted text-foreground"
|
|
434
|
+
: "text-muted-foreground hover:bg-muted/50"
|
|
435
|
+
}`}
|
|
436
|
+
>
|
|
437
|
+
Full
|
|
438
|
+
</button>
|
|
439
|
+
</div>
|
|
440
|
+
<div className="flex-1" />
|
|
441
|
+
<span className="text-muted-foreground text-xs font-mono">
|
|
442
|
+
{logs.length} request{logs.length !== 1 ? "s" : ""}
|
|
443
|
+
{totalIn > 0 || totalOut > 0
|
|
444
|
+
? ` · ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out`
|
|
445
|
+
: ""}
|
|
446
|
+
</span>
|
|
447
|
+
{logs.length > 0 && (
|
|
448
|
+
<button
|
|
449
|
+
type="button"
|
|
450
|
+
onClick={() => {
|
|
451
|
+
void handleExport();
|
|
452
|
+
}}
|
|
453
|
+
disabled={exporting}
|
|
454
|
+
className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5 rounded-md hover:bg-muted"
|
|
455
|
+
title="Export all logs as JSON ZIP"
|
|
456
|
+
>
|
|
457
|
+
{exporting ? (
|
|
458
|
+
<span>Exporting...</span>
|
|
459
|
+
) : (
|
|
460
|
+
<>
|
|
461
|
+
<Download className="size-3.5" />
|
|
462
|
+
<span>Export</span>
|
|
463
|
+
</>
|
|
464
|
+
)}
|
|
465
|
+
</button>
|
|
466
|
+
)}
|
|
467
|
+
<button
|
|
468
|
+
type="button"
|
|
469
|
+
onClick={onClearAll}
|
|
470
|
+
className="h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-md hover:bg-muted"
|
|
471
|
+
title="Clear all logs"
|
|
472
|
+
>
|
|
473
|
+
Clear
|
|
474
|
+
</button>
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
{/* Log list */}
|
|
479
|
+
<div>
|
|
480
|
+
{logs.length === 0 ? (
|
|
481
|
+
selectedSession !== "__all__" ? (
|
|
482
|
+
<div className="text-center text-muted-foreground py-16 space-y-4">
|
|
483
|
+
<p className="text-sm font-medium">Session not found</p>
|
|
484
|
+
<p className="text-xs font-mono bg-muted px-3 py-1 rounded inline-block max-w-[500px] break-all">
|
|
485
|
+
{truncateSessionId(selectedSession)}
|
|
486
|
+
</p>
|
|
487
|
+
<p className="text-xs">
|
|
488
|
+
This session may have been cleared or never existed.{" "}
|
|
489
|
+
{hideSessionFilter ? (
|
|
490
|
+
<a href="/" className="underline hover:text-foreground transition-colors">
|
|
491
|
+
Back to all sessions
|
|
492
|
+
</a>
|
|
493
|
+
) : (
|
|
494
|
+
<button
|
|
495
|
+
type="button"
|
|
496
|
+
onClick={() => onSessionChange("__all__")}
|
|
497
|
+
className="underline hover:text-foreground transition-colors"
|
|
498
|
+
>
|
|
499
|
+
Show all sessions
|
|
500
|
+
</button>
|
|
501
|
+
)}
|
|
502
|
+
</p>
|
|
503
|
+
</div>
|
|
504
|
+
) : (
|
|
505
|
+
<div className="text-center text-muted-foreground py-16 space-y-4">
|
|
506
|
+
<p className="text-sm">No requests captured yet.</p>
|
|
507
|
+
<p className="text-xs">Route AI coding tools through the proxy:</p>
|
|
508
|
+
<CopyableCommand command="LLM_BASE_URL=http://localhost:25947/proxy <your-tool>" />
|
|
509
|
+
</div>
|
|
510
|
+
)
|
|
511
|
+
) : (
|
|
512
|
+
<div
|
|
513
|
+
ref={logListWrapperRef}
|
|
514
|
+
tabIndex={0}
|
|
515
|
+
className="flex flex-col gap-2 focus:outline-none"
|
|
516
|
+
>
|
|
517
|
+
<div ref={logListRef}>
|
|
518
|
+
{groups.map((group) => (
|
|
519
|
+
<ConversationGroup
|
|
520
|
+
key={group.id}
|
|
521
|
+
group={group}
|
|
522
|
+
viewMode={viewMode}
|
|
523
|
+
strip={strip}
|
|
524
|
+
slowResponseThresholdSeconds={slowResponseThresholdSeconds}
|
|
525
|
+
cacheTrends={cacheTrends}
|
|
526
|
+
onCompareWithPrevious={handleCompareWithPrevious}
|
|
527
|
+
comparisonPredecessors={comparisonPredecessors}
|
|
528
|
+
onClearGroup={onClearGroup}
|
|
529
|
+
standalone={groups.length === 1}
|
|
530
|
+
/>
|
|
531
|
+
))}
|
|
532
|
+
</div>
|
|
533
|
+
</div>
|
|
534
|
+
)}
|
|
535
|
+
</div>
|
|
536
|
+
|
|
537
|
+
{/* Compare drawer — sibling of the log list, not a route change. */}
|
|
538
|
+
{comparePair !== null && (
|
|
539
|
+
<Suspense fallback={null}>
|
|
540
|
+
<LazyCompareDrawer left={comparePair[0]} right={comparePair[1]} onClose={closeCompare} />
|
|
541
|
+
</Suspense>
|
|
542
|
+
)}
|
|
543
|
+
</div>
|
|
544
|
+
);
|
|
545
|
+
}
|