@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.
Files changed (400) hide show
  1. package/.output/cli.js +1611 -0
  2. package/.output/nitro.json +17 -0
  3. package/.output/public/assets/CompareDrawer-CU5ZrWcL.js +1 -0
  4. package/.output/public/assets/ProxyViewerContainer-pEBqVp1d.js +101 -0
  5. package/.output/public/assets/ReplayDialog-F58yNg5j.js +1 -0
  6. package/.output/public/assets/RequestAnatomy-C9lT0qE_.js +1 -0
  7. package/.output/public/assets/ResponseView-DHJq6bnz.js +1 -0
  8. package/.output/public/assets/StreamingChunkSequence-BTgfpFUT.js +1 -0
  9. package/.output/public/assets/_sessionId-DsNRbnNm.js +1 -0
  10. package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
  11. package/.output/public/assets/index-CpWG2hFn.css +1 -0
  12. package/.output/public/assets/index-DmBV8Gve.js +1 -0
  13. package/.output/public/assets/json-viewer-CZVYLR8j.js +14 -0
  14. package/.output/public/assets/main-DHs7FBK3.js +18 -0
  15. package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
  16. package/.output/public/assets/qwen-CONDcHqt.png +0 -0
  17. package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
  18. package/.output/server/_chunks/ssr-renderer.mjs +22 -0
  19. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
  20. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
  21. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +2 -0
  22. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +2 -0
  23. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +2 -0
  24. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
  25. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
  26. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
  27. package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
  28. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
  29. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
  30. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
  31. package/.output/server/_libs/ajv-formats.mjs +330 -0
  32. package/.output/server/_libs/ajv.mjs +11444 -0
  33. package/.output/server/_libs/aria-hidden.mjs +122 -0
  34. package/.output/server/_libs/atomically.mjs +152 -0
  35. package/.output/server/_libs/bail.mjs +8 -0
  36. package/.output/server/_libs/cfworker__json-schema.mjs +1 -0
  37. package/.output/server/_libs/character-entities.mjs +2130 -0
  38. package/.output/server/_libs/class-variance-authority.mjs +44 -0
  39. package/.output/server/_libs/clsx.mjs +16 -0
  40. package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
  41. package/.output/server/_libs/conf.mjs +635 -0
  42. package/.output/server/_libs/cookie-es.mjs +58 -0
  43. package/.output/server/_libs/core-util-is.mjs +75 -0
  44. package/.output/server/_libs/croner.mjs +1 -0
  45. package/.output/server/_libs/crossws.mjs +1 -0
  46. package/.output/server/_libs/debounce-fn.mjs +69 -0
  47. package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
  48. package/.output/server/_libs/dequal.mjs +27 -0
  49. package/.output/server/_libs/detect-node-es.mjs +1 -0
  50. package/.output/server/_libs/devlop.mjs +8 -0
  51. package/.output/server/_libs/diff.mjs +320 -0
  52. package/.output/server/_libs/dot-prop.mjs +265 -0
  53. package/.output/server/_libs/env-paths.mjs +57 -0
  54. package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
  55. package/.output/server/_libs/extend.mjs +97 -0
  56. package/.output/server/_libs/fast-deep-equal.mjs +38 -0
  57. package/.output/server/_libs/fast-uri.mjs +812 -0
  58. package/.output/server/_libs/floating-ui__core.mjs +725 -0
  59. package/.output/server/_libs/floating-ui__dom.mjs +622 -0
  60. package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
  61. package/.output/server/_libs/floating-ui__utils.mjs +320 -0
  62. package/.output/server/_libs/get-nonce.mjs +9 -0
  63. package/.output/server/_libs/h3-v2.mjs +276 -0
  64. package/.output/server/_libs/h3.mjs +408 -0
  65. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
  66. package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
  67. package/.output/server/_libs/hookable.mjs +1 -0
  68. package/.output/server/_libs/html-url-attributes.mjs +26 -0
  69. package/.output/server/_libs/immediate.mjs +74 -0
  70. package/.output/server/_libs/inherits.mjs +50 -0
  71. package/.output/server/_libs/inline-style-parser.mjs +142 -0
  72. package/.output/server/_libs/is-plain-obj.mjs +10 -0
  73. package/.output/server/_libs/isarray.mjs +14 -0
  74. package/.output/server/_libs/isbot.mjs +20 -0
  75. package/.output/server/_libs/json-schema-traverse.mjs +180 -0
  76. package/.output/server/_libs/jszip.mjs +3051 -0
  77. package/.output/server/_libs/lie.mjs +273 -0
  78. package/.output/server/_libs/lucide-react.mjs +492 -0
  79. package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
  80. package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
  81. package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
  82. package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
  83. package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
  84. package/.output/server/_libs/micromark-factory-label.mjs +63 -0
  85. package/.output/server/_libs/micromark-factory-space.mjs +24 -0
  86. package/.output/server/_libs/micromark-factory-title.mjs +65 -0
  87. package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
  88. package/.output/server/_libs/micromark-util-character.mjs +44 -0
  89. package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
  90. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
  91. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
  92. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
  93. package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
  94. package/.output/server/_libs/micromark-util-encode.mjs +1 -0
  95. package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
  96. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
  97. package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
  98. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
  99. package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
  100. package/.output/server/_libs/micromark.mjs +906 -0
  101. package/.output/server/_libs/mimic-function.mjs +47 -0
  102. package/.output/server/_libs/modelcontextprotocol__server.mjs +9738 -0
  103. package/.output/server/_libs/ocache.mjs +1 -0
  104. package/.output/server/_libs/ohash.mjs +1 -0
  105. package/.output/server/_libs/pako.mjs +4223 -0
  106. package/.output/server/_libs/process-nextick-args.mjs +48 -0
  107. package/.output/server/_libs/property-information.mjs +1209 -0
  108. package/.output/server/_libs/radix-ui.mjs +1 -0
  109. package/.output/server/_libs/radix-ui__number.mjs +6 -0
  110. package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
  111. package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
  112. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
  113. package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
  114. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
  115. package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
  116. package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
  117. package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
  118. package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
  119. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
  120. package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
  121. package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
  122. package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
  123. package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
  124. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
  125. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
  126. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
  127. package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
  128. package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
  129. package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
  130. package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
  131. package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
  132. package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
  133. package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
  134. package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
  135. package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
  136. package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
  137. package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
  138. package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
  139. package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
  140. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
  141. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
  142. package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
  143. package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
  144. package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
  145. package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
  146. package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
  147. package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
  148. package/.output/server/_libs/radix-ui__react-toast.mjs +2 -0
  149. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
  150. package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
  151. package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
  152. package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
  153. package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
  154. package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
  155. package/.output/server/_libs/react-dom.mjs +10781 -0
  156. package/.output/server/_libs/react-markdown.mjs +147 -0
  157. package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
  158. package/.output/server/_libs/react-remove-scroll.mjs +328 -0
  159. package/.output/server/_libs/react-style-singleton.mjs +69 -0
  160. package/.output/server/_libs/react.mjs +515 -0
  161. package/.output/server/_libs/readable-stream.mjs +1518 -0
  162. package/.output/server/_libs/remark-parse.mjs +19 -0
  163. package/.output/server/_libs/remark-rehype.mjs +21 -0
  164. package/.output/server/_libs/rou3.mjs +14 -0
  165. package/.output/server/_libs/safe-buffer.mjs +64 -0
  166. package/.output/server/_libs/semver.mjs +1938 -0
  167. package/.output/server/_libs/seroval-plugins.mjs +58 -0
  168. package/.output/server/_libs/seroval.mjs +1765 -0
  169. package/.output/server/_libs/setimmediate.mjs +152 -0
  170. package/.output/server/_libs/space-separated-tokens.mjs +6 -0
  171. package/.output/server/_libs/srvx.mjs +1029 -0
  172. package/.output/server/_libs/stubborn-fs.mjs +91 -0
  173. package/.output/server/_libs/stubborn-utils.mjs +66 -0
  174. package/.output/server/_libs/style-to-js.mjs +72 -0
  175. package/.output/server/_libs/style-to-object.mjs +38 -0
  176. package/.output/server/_libs/swr.mjs +939 -0
  177. package/.output/server/_libs/tailwind-merge.mjs +3010 -0
  178. package/.output/server/_libs/tanstack__history.mjs +217 -0
  179. package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
  180. package/.output/server/_libs/tanstack__react-store.mjs +1 -0
  181. package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
  182. package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
  183. package/.output/server/_libs/tanstack__store.mjs +1 -0
  184. package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
  185. package/.output/server/_libs/tiny-invariant.mjs +12 -0
  186. package/.output/server/_libs/tiny-warning.mjs +5 -0
  187. package/.output/server/_libs/trim-lines.mjs +41 -0
  188. package/.output/server/_libs/trough.mjs +85 -0
  189. package/.output/server/_libs/tslib.mjs +1 -0
  190. package/.output/server/_libs/ufo.mjs +54 -0
  191. package/.output/server/_libs/uint8array-extras.mjs +69 -0
  192. package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
  193. package/.output/server/_libs/unified.mjs +661 -0
  194. package/.output/server/_libs/unist-util-is.mjs +100 -0
  195. package/.output/server/_libs/unist-util-position.mjs +27 -0
  196. package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
  197. package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
  198. package/.output/server/_libs/unist-util-visit.mjs +24 -0
  199. package/.output/server/_libs/unstorage.mjs +1 -0
  200. package/.output/server/_libs/use-callback-ref.mjs +66 -0
  201. package/.output/server/_libs/use-sidecar.mjs +106 -0
  202. package/.output/server/_libs/use-sync-external-store.mjs +64 -0
  203. package/.output/server/_libs/util-deprecate.mjs +12 -0
  204. package/.output/server/_libs/vfile-message.mjs +138 -0
  205. package/.output/server/_libs/vfile.mjs +467 -0
  206. package/.output/server/_libs/when-exit.mjs +53 -0
  207. package/.output/server/_libs/zod.mjs +4524 -0
  208. package/.output/server/_sessionId-wMLPvC5g.mjs +123 -0
  209. package/.output/server/_ssr/CompareDrawer-BU4V0uVf.mjs +1041 -0
  210. package/.output/server/_ssr/ProxyViewerContainer-BnRwFEnn.mjs +5972 -0
  211. package/.output/server/_ssr/ReplayDialog-C7dn9pd_.mjs +322 -0
  212. package/.output/server/_ssr/RequestAnatomy-C1rWpe9-.mjs +353 -0
  213. package/.output/server/_ssr/ResponseView-hGpPaYsf.mjs +602 -0
  214. package/.output/server/_ssr/StreamingChunkSequence-BRWI1r_G.mjs +302 -0
  215. package/.output/server/_ssr/index-BKURLVPz.mjs +118 -0
  216. package/.output/server/_ssr/index.mjs +1184 -0
  217. package/.output/server/_ssr/json-viewer-BBd2DtQP.mjs +515 -0
  218. package/.output/server/_ssr/router-BcZ0D6AB.mjs +6317 -0
  219. package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  220. package/.output/server/_tanstack-start-manifest_v-1y8ZVxRI.mjs +4 -0
  221. package/.output/server/index.mjs +436 -0
  222. package/.output/server/node_modules/tslib/modules/index.js +70 -0
  223. package/.output/server/node_modules/tslib/modules/package.json +3 -0
  224. package/.output/server/node_modules/tslib/package.json +47 -0
  225. package/.output/server/node_modules/tslib/tslib.js +484 -0
  226. package/.output/server/package.json +9 -0
  227. package/LICENSE +21 -0
  228. package/README.md +52 -0
  229. package/package.json +110 -0
  230. package/src/assets/favicon.svg +31 -0
  231. package/src/assets/logos/alibaba.svg +1 -0
  232. package/src/assets/logos/anthropic.svg +1 -0
  233. package/src/assets/logos/claude-code.svg +4 -0
  234. package/src/assets/logos/deepseek.svg +1 -0
  235. package/src/assets/logos/mcp.png +0 -0
  236. package/src/assets/logos/minimax.jpeg +0 -0
  237. package/src/assets/logos/openai.svg +1 -0
  238. package/src/assets/logos/opencode.svg +4 -0
  239. package/src/assets/logos/qwen.png +0 -0
  240. package/src/assets/logos/zhipuai.svg +219 -0
  241. package/src/cli/detect-tools.ts +147 -0
  242. package/src/cli/doctor.ts +521 -0
  243. package/src/cli/onboard.ts +224 -0
  244. package/src/cli/templates/command-onboard.ts +17 -0
  245. package/src/cli/templates/skill-onboard.ts +547 -0
  246. package/src/cli.ts +345 -0
  247. package/src/components/OnboardingBanner.tsx +67 -0
  248. package/src/components/ProxyViewer.tsx +545 -0
  249. package/src/components/ProxyViewerContainer.tsx +363 -0
  250. package/src/components/providers/ImportWizardDialog.tsx +349 -0
  251. package/src/components/providers/ProviderCard.tsx +474 -0
  252. package/src/components/providers/ProviderForm.tsx +494 -0
  253. package/src/components/providers/ProviderLogo.tsx +117 -0
  254. package/src/components/providers/ProvidersPanel.tsx +619 -0
  255. package/src/components/providers/SettingsDialog.tsx +202 -0
  256. package/src/components/proxy-viewer/CompareDrawer.tsx +893 -0
  257. package/src/components/proxy-viewer/ConversationGroup.tsx +107 -0
  258. package/src/components/proxy-viewer/ConversationHeader.tsx +300 -0
  259. package/src/components/proxy-viewer/LogEntry.tsx +543 -0
  260. package/src/components/proxy-viewer/LogEntryHeader.tsx +501 -0
  261. package/src/components/proxy-viewer/ReplayDialog.tsx +218 -0
  262. package/src/components/proxy-viewer/ResponseView.tsx +171 -0
  263. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +188 -0
  264. package/src/components/proxy-viewer/ThreadConnector.tsx +136 -0
  265. package/src/components/proxy-viewer/TurnGroup.tsx +337 -0
  266. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +98 -0
  267. package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +196 -0
  268. package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +53 -0
  269. package/src/components/proxy-viewer/anatomy/types.ts +39 -0
  270. package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +114 -0
  271. package/src/components/proxy-viewer/cacheTrend.ts +50 -0
  272. package/src/components/proxy-viewer/diff/DiffView.tsx +321 -0
  273. package/src/components/proxy-viewer/diff/computeDiff.ts +178 -0
  274. package/src/components/proxy-viewer/diff/index.ts +3 -0
  275. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +157 -0
  276. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +66 -0
  277. package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +21 -0
  278. package/src/components/proxy-viewer/formats/index.tsx +33 -0
  279. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +170 -0
  280. package/src/components/proxy-viewer/index.ts +9 -0
  281. package/src/components/proxy-viewer/lazy.ts +37 -0
  282. package/src/components/proxy-viewer/log-formats/anthropic.ts +194 -0
  283. package/src/components/proxy-viewer/log-formats/index.ts +23 -0
  284. package/src/components/proxy-viewer/log-formats/openai.ts +167 -0
  285. package/src/components/proxy-viewer/log-formats/types.ts +40 -0
  286. package/src/components/proxy-viewer/log-formats/unknown.ts +18 -0
  287. package/src/components/proxy-viewer/logEntryVisibility.ts +39 -0
  288. package/src/components/proxy-viewer/requestDiff.ts +277 -0
  289. package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
  290. package/src/components/proxy-viewer/useKeyboardNavigation.ts +190 -0
  291. package/src/components/proxy-viewer/viewerState.ts +66 -0
  292. package/src/components/ui/badge.tsx +47 -0
  293. package/src/components/ui/button.tsx +47 -0
  294. package/src/components/ui/collapsible.tsx +21 -0
  295. package/src/components/ui/confirm-dialog.tsx +51 -0
  296. package/src/components/ui/crab-logo.tsx +95 -0
  297. package/src/components/ui/crab-variants.tsx +467 -0
  298. package/src/components/ui/dialog.tsx +129 -0
  299. package/src/components/ui/json-expansion-button.tsx +56 -0
  300. package/src/components/ui/json-viewer-bulk.ts +97 -0
  301. package/src/components/ui/json-viewer.tsx +494 -0
  302. package/src/components/ui/mcp-logo.tsx +20 -0
  303. package/src/components/ui/scroll-area.tsx +54 -0
  304. package/src/components/ui/select.tsx +178 -0
  305. package/src/components/ui/separator.tsx +28 -0
  306. package/src/components/ui/tabs.tsx +88 -0
  307. package/src/components/ui/tooltip.tsx +51 -0
  308. package/src/index.css +11 -0
  309. package/src/knowledge/candidateStore.ts +63 -0
  310. package/src/knowledge/distiller.ts +98 -0
  311. package/src/knowledge/openclawClient.ts +118 -0
  312. package/src/knowledge/redactor.ts +80 -0
  313. package/src/knowledge/types.ts +84 -0
  314. package/src/lib/apiClient.ts +49 -0
  315. package/src/lib/export-logs.ts +51 -0
  316. package/src/lib/mask.ts +4 -0
  317. package/src/lib/objectUtils.ts +22 -0
  318. package/src/lib/providerContract.ts +26 -0
  319. package/src/lib/providerTestContract.ts +107 -0
  320. package/src/lib/runtimeConfig.ts +25 -0
  321. package/src/lib/serverPort.ts +41 -0
  322. package/src/lib/sessionUrl.ts +44 -0
  323. package/src/lib/stopReason.ts +58 -0
  324. package/src/lib/useOnboarding.ts +80 -0
  325. package/src/lib/useProviders.ts +30 -0
  326. package/src/lib/useStripConfig.ts +108 -0
  327. package/src/lib/utils.ts +21 -0
  328. package/src/mcp/loopback.ts +76 -0
  329. package/src/mcp/previewExtractor.ts +166 -0
  330. package/src/mcp/server.ts +396 -0
  331. package/src/mcp/toolHandlers.ts +341 -0
  332. package/src/proxy/chunkStorage.ts +112 -0
  333. package/src/proxy/claudeCodeStrip.ts +99 -0
  334. package/src/proxy/config.ts +172 -0
  335. package/src/proxy/constants.ts +47 -0
  336. package/src/proxy/dataDir.ts +86 -0
  337. package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
  338. package/src/proxy/formats/anthropic/handler.ts +71 -0
  339. package/src/proxy/formats/anthropic/index.ts +14 -0
  340. package/src/proxy/formats/anthropic/register.ts +4 -0
  341. package/src/proxy/formats/anthropic/schemas.ts +237 -0
  342. package/src/proxy/formats/anthropic/stream.ts +205 -0
  343. package/src/proxy/formats/handler.ts +46 -0
  344. package/src/proxy/formats/index.ts +12 -0
  345. package/src/proxy/formats/jsonSchema.ts +36 -0
  346. package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
  347. package/src/proxy/formats/openai/handler.ts +96 -0
  348. package/src/proxy/formats/openai/index.ts +25 -0
  349. package/src/proxy/formats/openai/provider.ts +50 -0
  350. package/src/proxy/formats/openai/register.ts +4 -0
  351. package/src/proxy/formats/openai/schemas.ts +187 -0
  352. package/src/proxy/formats/openai/stream.ts +206 -0
  353. package/src/proxy/formats/protocol.ts +50 -0
  354. package/src/proxy/formats/providerRegistry.ts +51 -0
  355. package/src/proxy/formats/providers/index.ts +3 -0
  356. package/src/proxy/formats/registry.ts +66 -0
  357. package/src/proxy/handler.ts +334 -0
  358. package/src/proxy/logFinalizer.ts +305 -0
  359. package/src/proxy/logFinalizer.worker.ts +24 -0
  360. package/src/proxy/logIndex.ts +268 -0
  361. package/src/proxy/logger.ts +179 -0
  362. package/src/proxy/openaiOrphanToolStrip.ts +142 -0
  363. package/src/proxy/providerImporters.ts +491 -0
  364. package/src/proxy/providers.ts +613 -0
  365. package/src/proxy/schemas.ts +209 -0
  366. package/src/proxy/sessionProcess.ts +140 -0
  367. package/src/proxy/sessionRuntime.ts +85 -0
  368. package/src/proxy/sessionSupervisor.ts +283 -0
  369. package/src/proxy/sessionWorkerEntry.ts +26 -0
  370. package/src/proxy/socketTracker.ts +255 -0
  371. package/src/proxy/store.ts +412 -0
  372. package/src/proxy/upstream.ts +90 -0
  373. package/src/router.tsx +16 -0
  374. package/src/routes/__root.tsx +45 -0
  375. package/src/routes/api/config.paths.ts +14 -0
  376. package/src/routes/api/config.ts +53 -0
  377. package/src/routes/api/health.ts +15 -0
  378. package/src/routes/api/knowledge.candidates.$candidateId.promote.ts +32 -0
  379. package/src/routes/api/knowledge.candidates.ts +10 -0
  380. package/src/routes/api/knowledge.project-context.ts +18 -0
  381. package/src/routes/api/knowledge.search.ts +31 -0
  382. package/src/routes/api/knowledge.sessions.$sessionId.candidates.ts +16 -0
  383. package/src/routes/api/logs.$id.chunks.ts +36 -0
  384. package/src/routes/api/logs.$id.replay.ts +191 -0
  385. package/src/routes/api/logs.$id.ts +22 -0
  386. package/src/routes/api/logs.stream.ts +74 -0
  387. package/src/routes/api/logs.ts +59 -0
  388. package/src/routes/api/mcp.ts +25 -0
  389. package/src/routes/api/models.ts +10 -0
  390. package/src/routes/api/providers.$providerId.test.log.ts +293 -0
  391. package/src/routes/api/providers.$providerId.ts +50 -0
  392. package/src/routes/api/providers.export.ts +26 -0
  393. package/src/routes/api/providers.import.ts +47 -0
  394. package/src/routes/api/providers.scan.ts +23 -0
  395. package/src/routes/api/providers.ts +45 -0
  396. package/src/routes/api/sessions.ts +17 -0
  397. package/src/routes/index.tsx +6 -0
  398. package/src/routes/proxy/$.ts +15 -0
  399. package/src/routes/session/$sessionId.tsx +23 -0
  400. package/styles/globals.css +188 -0
@@ -0,0 +1,90 @@
1
+ import type { ProviderConfig } from "./providers";
2
+ import {
3
+ AUTH_HEADER_X_API_KEY,
4
+ DEFAULT_OPENAI_UPSTREAM,
5
+ DEFAULT_UPSTREAM,
6
+ HEADER_AUTHORIZATION,
7
+ HEADER_HOST,
8
+ HEADER_X_API_KEY,
9
+ PATH_CHAT_COMPLETIONS,
10
+ PATH_V1_CHAT_COMPLETIONS,
11
+ } from "./constants";
12
+
13
+ export type ApiRoute = {
14
+ apiPath: string;
15
+ endpointPath: string;
16
+ isChatCompletions: boolean;
17
+ normalizedPath: string;
18
+ };
19
+
20
+ /**
21
+ * Describes the protocol implied by the client-facing path.
22
+ *
23
+ * The path is the source of truth for request/response format. ProviderConfig.format
24
+ * is only a fallback for old provider records that do not have a format-specific URL.
25
+ */
26
+ export function describeApiRoute(apiPath: string): ApiRoute {
27
+ const endpointPath = apiPath.split("?")[0] ?? "";
28
+ const isChatCompletions =
29
+ endpointPath === PATH_CHAT_COMPLETIONS || endpointPath === PATH_V1_CHAT_COMPLETIONS;
30
+ const normalizedPath =
31
+ isChatCompletions && !apiPath.startsWith("/v1/") ? `/v1${apiPath}` : apiPath;
32
+
33
+ return { apiPath, endpointPath, isChatCompletions, normalizedPath };
34
+ }
35
+
36
+ export function getProxyApiPath(url: URL): string {
37
+ return url.pathname.replace(/^\/proxy/, "") + url.search;
38
+ }
39
+
40
+ export function selectUpstreamBase(route: ApiRoute, provider: ProviderConfig | null): string {
41
+ if (provider === null) {
42
+ return route.isChatCompletions ? DEFAULT_OPENAI_UPSTREAM : DEFAULT_UPSTREAM;
43
+ }
44
+
45
+ const formatSpecificUrl = route.isChatCompletions
46
+ ? provider.openaiBaseUrl
47
+ : provider.anthropicBaseUrl;
48
+ if (formatSpecificUrl !== undefined && formatSpecificUrl !== "") {
49
+ return formatSpecificUrl;
50
+ }
51
+ if (provider.baseUrl !== undefined && provider.baseUrl !== "") {
52
+ return provider.baseUrl;
53
+ }
54
+
55
+ // Legacy records may only contain format. Keep this fallback for compatibility,
56
+ // but never use it to infer the response parser; that decision comes from the path.
57
+ return provider.format === "openai" ? DEFAULT_OPENAI_UPSTREAM : DEFAULT_UPSTREAM;
58
+ }
59
+
60
+ export function buildUpstreamUrl(upstreamBase: string, normalizedPath: string): string {
61
+ const base = upstreamBase.endsWith("/") ? upstreamBase.slice(0, -1) : upstreamBase;
62
+
63
+ // Many OpenAI-compatible base URLs already include /v1. Avoid producing /v1/v1.
64
+ if (base.endsWith("/v1") && normalizedPath.startsWith("/v1/")) {
65
+ return base + normalizedPath.slice(3);
66
+ }
67
+ return base + normalizedPath;
68
+ }
69
+
70
+ export function setUpstreamHost(headers: Headers, upstreamBase: string): void {
71
+ try {
72
+ headers.set(HEADER_HOST, new URL(upstreamBase).host);
73
+ } catch {
74
+ // Invalid provider URLs are rejected by the UI. Retain the historical default
75
+ // for imported or manually edited config that bypassed that validation.
76
+ headers.set(HEADER_HOST, "api.anthropic.com");
77
+ }
78
+ }
79
+
80
+ export function injectProviderAuth(headers: Headers, provider: ProviderConfig | null): void {
81
+ if (provider === null) return;
82
+
83
+ const apiKey = provider.apiKey.replace(/^Bearer\s+/i, "").trim();
84
+ if (provider.authHeader === AUTH_HEADER_X_API_KEY) {
85
+ headers.set(HEADER_X_API_KEY, apiKey);
86
+ headers.delete(HEADER_AUTHORIZATION);
87
+ return;
88
+ }
89
+ headers.set(HEADER_AUTHORIZATION, `Bearer ${apiKey}`);
90
+ }
package/src/router.tsx ADDED
@@ -0,0 +1,16 @@
1
+ import { createRouter } from "@tanstack/react-router";
2
+ import { routeTree } from "./routeTree.gen";
3
+
4
+ export function getRouter() {
5
+ const router = createRouter({
6
+ routeTree,
7
+ scrollRestoration: false,
8
+ });
9
+ return router;
10
+ }
11
+
12
+ declare module "@tanstack/react-router" {
13
+ interface Register {
14
+ router: ReturnType<typeof getRouter>;
15
+ }
16
+ }
@@ -0,0 +1,45 @@
1
+ /// <reference types="vite/client" />
2
+ import { Outlet, createRootRoute, HeadContent, Scripts } from "@tanstack/react-router";
3
+ import type { ReactNode } from "react";
4
+ import { SWRConfig } from "swr";
5
+ import faviconSvg from "../assets/favicon.svg?url";
6
+ import appCss from "../index.css?url";
7
+
8
+ export const Route = createRootRoute({
9
+ head: () => ({
10
+ meta: [
11
+ { charSet: "utf-8" },
12
+ { name: "viewport", content: "width=device-width, initial-scale=1" },
13
+ { title: "Agent Inspector" },
14
+ ],
15
+ links: [
16
+ { rel: "stylesheet", href: appCss },
17
+ { rel: "icon", type: "image/svg+xml", href: faviconSvg },
18
+ ],
19
+ }),
20
+ component: RootComponent,
21
+ });
22
+
23
+ function RootComponent() {
24
+ return (
25
+ <RootDocument>
26
+ <Outlet />
27
+ </RootDocument>
28
+ );
29
+ }
30
+
31
+ function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
32
+ return (
33
+ <html lang="en" className="dark">
34
+ <head>
35
+ <HeadContent />
36
+ </head>
37
+ <body>
38
+ <SWRConfig value={{ revalidateOnFocus: false, revalidateIfStale: false }}>
39
+ {children}
40
+ </SWRConfig>
41
+ <Scripts />
42
+ </body>
43
+ </html>
44
+ );
45
+ }
@@ -0,0 +1,14 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { store } from "../../proxy/providers";
3
+
4
+ export const Route = createFileRoute("/api/config/paths")({
5
+ server: {
6
+ handlers: {
7
+ GET: () => {
8
+ return Response.json({
9
+ providerConfig: store.path,
10
+ });
11
+ },
12
+ },
13
+ },
14
+ });
@@ -0,0 +1,53 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { z } from "zod";
3
+ import { MAX_SLOW_RESPONSE_THRESHOLD_SECONDS } from "../../lib/runtimeConfig";
4
+ import { getConfig, setConfig, RuntimeConfigSchema } from "../../proxy/config";
5
+
6
+ // Partial schema for PATCH: at least one known field must be present.
7
+ const RuntimeConfigPatchSchema = z
8
+ .object({
9
+ stripClaudeCodeBillingHeader: z.boolean().optional(),
10
+ hasSeenOnboarding: z.boolean().optional(),
11
+ slowResponseThresholdSeconds: z
12
+ .number()
13
+ .int()
14
+ .min(0)
15
+ .max(MAX_SLOW_RESPONSE_THRESHOLD_SECONDS)
16
+ .optional(),
17
+ })
18
+ .strict()
19
+ .refine((v) => Object.keys(v).length > 0, {
20
+ message: "At least one field must be provided",
21
+ });
22
+
23
+ export const Route = createFileRoute("/api/config")({
24
+ server: {
25
+ handlers: {
26
+ GET: () => {
27
+ return Response.json(getConfig());
28
+ },
29
+ PATCH: async ({ request }: { request: Request }) => {
30
+ let body: unknown;
31
+ try {
32
+ body = await request.json();
33
+ } catch {
34
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
35
+ }
36
+
37
+ const result = RuntimeConfigPatchSchema.safeParse(body);
38
+ if (!result.success) {
39
+ return Response.json(
40
+ {
41
+ error: "Invalid config payload",
42
+ issues: result.error.issues,
43
+ },
44
+ { status: 400 },
45
+ );
46
+ }
47
+
48
+ const updated = setConfig(result.data);
49
+ return Response.json(updated satisfies z.infer<typeof RuntimeConfigSchema>);
50
+ },
51
+ },
52
+ },
53
+ });
@@ -0,0 +1,15 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { logger } from "../../proxy/logger";
3
+
4
+ // Trigger logger auto-initialization when this module loads
5
+ logger.debug("Health endpoint loaded");
6
+
7
+ export const Route = createFileRoute("/api/health")({
8
+ server: {
9
+ handlers: {
10
+ GET: () => {
11
+ return Response.json({ status: "ok" });
12
+ },
13
+ },
14
+ },
15
+ });
@@ -0,0 +1,32 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import {
3
+ getCandidate,
4
+ markCandidateFailed,
5
+ markCandidatePromoted,
6
+ } from "../../knowledge/candidateStore";
7
+ import { promoteToOpenClaw } from "../../knowledge/openclawClient";
8
+
9
+ export const Route = createFileRoute("/api/knowledge/candidates/$candidateId/promote")({
10
+ server: {
11
+ handlers: {
12
+ POST: async ({ params }: { params: { candidateId: string } }) => {
13
+ const candidate = getCandidate(params.candidateId);
14
+ if (candidate === null) {
15
+ return Response.json({ error: "Knowledge candidate not found" }, { status: 404 });
16
+ }
17
+
18
+ const result = await promoteToOpenClaw(candidate);
19
+ if (!result.success) {
20
+ const updated = markCandidateFailed(candidate.id, result.error);
21
+ return Response.json(
22
+ { error: result.error, candidate: updated ?? candidate },
23
+ { status: 400 },
24
+ );
25
+ }
26
+
27
+ const updated = markCandidatePromoted(candidate.id, result.memoryId);
28
+ return Response.json({ candidate: updated ?? candidate });
29
+ },
30
+ },
31
+ },
32
+ });
@@ -0,0 +1,10 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { listCandidates } from "../../knowledge/candidateStore";
3
+
4
+ export const Route = createFileRoute("/api/knowledge/candidates")({
5
+ server: {
6
+ handlers: {
7
+ GET: () => Response.json({ candidates: listCandidates() }),
8
+ },
9
+ },
10
+ });
@@ -0,0 +1,18 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { searchOpenClaw } from "../../knowledge/openclawClient";
3
+
4
+ export const Route = createFileRoute("/api/knowledge/project-context")({
5
+ server: {
6
+ handlers: {
7
+ GET: async ({ request }: { request: Request }) => {
8
+ const url = new URL(request.url);
9
+ const project = url.searchParams.get("project") ?? "";
10
+ if (project === "") {
11
+ return Response.json({ error: "Missing project search parameter" }, { status: 400 });
12
+ }
13
+ const response = await searchOpenClaw(`project context ${project}`, project);
14
+ return Response.json({ project, ...response });
15
+ },
16
+ },
17
+ },
18
+ });
@@ -0,0 +1,31 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { z } from "zod";
3
+ import { searchOpenClaw } from "../../knowledge/openclawClient";
4
+
5
+ const SearchBodySchema = z.object({
6
+ query: z.string().min(1),
7
+ project: z.string().optional(),
8
+ });
9
+
10
+ export const Route = createFileRoute("/api/knowledge/search")({
11
+ server: {
12
+ handlers: {
13
+ GET: async ({ request }: { request: Request }) => {
14
+ const url = new URL(request.url);
15
+ const query = url.searchParams.get("q") ?? "";
16
+ const project = url.searchParams.get("project") ?? undefined;
17
+ if (query === "") {
18
+ return Response.json({ error: "Missing q search parameter" }, { status: 400 });
19
+ }
20
+ return Response.json(await searchOpenClaw(query, project));
21
+ },
22
+ POST: async ({ request }: { request: Request }) => {
23
+ const parsed = SearchBodySchema.safeParse(await request.json());
24
+ if (!parsed.success) {
25
+ return Response.json({ error: parsed.error.message }, { status: 400 });
26
+ }
27
+ return Response.json(await searchOpenClaw(parsed.data.query, parsed.data.project));
28
+ },
29
+ },
30
+ },
31
+ });
@@ -0,0 +1,16 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { getFilteredLogs } from "../../proxy/store";
3
+ import { distillSessionCandidates } from "../../knowledge/distiller";
4
+ import { saveCandidates } from "../../knowledge/candidateStore";
5
+
6
+ export const Route = createFileRoute("/api/knowledge/sessions/$sessionId/candidates")({
7
+ server: {
8
+ handlers: {
9
+ POST: ({ params }: { params: { sessionId: string } }) => {
10
+ const logs = getFilteredLogs(params.sessionId);
11
+ const candidates = saveCandidates(distillSessionCandidates(params.sessionId, logs));
12
+ return Response.json({ candidates });
13
+ },
14
+ },
15
+ },
16
+ });
@@ -0,0 +1,36 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { getLogById } from "../../proxy/store";
3
+ import { readChunks } from "../../proxy/chunkStorage";
4
+
5
+ export const Route = createFileRoute("/api/logs/$id/chunks")({
6
+ server: {
7
+ handlers: {
8
+ GET: async ({ params }: { params: { id: string } }) => {
9
+ const id = Number(params.id);
10
+ if (isNaN(id)) {
11
+ return Response.json({ error: "Invalid log ID" }, { status: 400 });
12
+ }
13
+
14
+ const log = await getLogById(id);
15
+ if (log === null) {
16
+ return Response.json({ error: "Log not found" }, { status: 404 });
17
+ }
18
+
19
+ // Try disk file first (persistent chunks from real proxy streaming)
20
+ if (log.streamingChunksPath !== null && log.streamingChunksPath !== undefined) {
21
+ const chunksData = readChunks(log.streamingChunksPath);
22
+ if (chunksData !== null) {
23
+ return Response.json(chunksData);
24
+ }
25
+ }
26
+
27
+ // Fall back to in-memory streamingChunks (e.g., from provider test)
28
+ if (log.streamingChunks !== undefined && log.streamingChunks.chunks.length > 0) {
29
+ return Response.json(log.streamingChunks);
30
+ }
31
+
32
+ return Response.json({ error: "Chunks not found" }, { status: 404 });
33
+ },
34
+ },
35
+ },
36
+ });
@@ -0,0 +1,191 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { z } from "zod";
3
+ import { getLogById } from "../../proxy/store";
4
+ import { findProviderByModel } from "../../proxy/providers";
5
+ import { registry } from "../../proxy/formats";
6
+ import { formatForPath } from "../../proxy/formats";
7
+ import { extractModelFromBody } from "../../proxy/schemas";
8
+ import {
9
+ PROXY_IDENTITY,
10
+ PRESERVE_HEADERS,
11
+ HEADER_CONTENT_TYPE,
12
+ HEADER_USER_AGENT,
13
+ HEADER_X_PROXY_IDENTITY,
14
+ CONTENT_TYPE_EVENT_STREAM,
15
+ STATUS_FORBIDDEN,
16
+ } from "../../proxy/constants";
17
+ import {
18
+ buildUpstreamUrl,
19
+ describeApiRoute,
20
+ injectProviderAuth,
21
+ selectUpstreamBase,
22
+ setUpstreamHost,
23
+ } from "../../proxy/upstream";
24
+
25
+ type ReplayRequest = {
26
+ modifiedBody: string;
27
+ };
28
+
29
+ const ReplayRequestSchema = z.object({
30
+ modifiedBody: z.string(),
31
+ });
32
+
33
+ type ReplayResponse = {
34
+ success: boolean;
35
+ error?: string;
36
+ responseStatus?: number;
37
+ responseText?: string;
38
+ inputTokens?: number;
39
+ outputTokens?: number;
40
+ elapsedMs?: number;
41
+ streaming?: boolean;
42
+ };
43
+
44
+ export const Route = createFileRoute("/api/logs/$id/replay")({
45
+ server: {
46
+ handlers: {
47
+ POST: async ({ params, request }: { params: { id: string }; request: Request }) => {
48
+ const id = Number(params.id);
49
+ if (isNaN(id)) {
50
+ return Response.json({ error: "Invalid log ID" }, { status: 400 });
51
+ }
52
+
53
+ let replayRequest: ReplayRequest;
54
+ try {
55
+ const raw: unknown = await request.json();
56
+ const parsed = ReplayRequestSchema.safeParse(raw);
57
+ if (!parsed.success) {
58
+ return Response.json({ error: "Invalid request body" }, { status: 400 });
59
+ }
60
+ replayRequest = parsed.data;
61
+ } catch {
62
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
63
+ }
64
+
65
+ const { modifiedBody } = replayRequest;
66
+
67
+ // Get original log for context
68
+ const log = await getLogById(id);
69
+ if (log === null) {
70
+ return Response.json({ error: "Log not found" }, { status: 404 });
71
+ }
72
+
73
+ // Extract model from modified body
74
+ const model = extractModelFromBody(modifiedBody);
75
+ if (model === null) {
76
+ return Response.json(
77
+ { error: "Could not extract model from request body" },
78
+ { status: 400 },
79
+ );
80
+ }
81
+
82
+ // Find provider
83
+ const matchedProviderConfig = findProviderByModel(model);
84
+ const provider = registry.findProvider(model);
85
+ if (provider === null) {
86
+ return Response.json(
87
+ { error: "No provider found for model" },
88
+ { status: STATUS_FORBIDDEN },
89
+ );
90
+ }
91
+
92
+ // Determine API path from original log
93
+ const route = describeApiRoute(log.path);
94
+
95
+ // Get format handler
96
+ const formatHandler = formatForPath(route.apiPath);
97
+ if (formatHandler === null) {
98
+ return Response.json({ error: "Unsupported path" }, { status: STATUS_FORBIDDEN });
99
+ }
100
+
101
+ // Build upstream URL
102
+ const upstreamBase = selectUpstreamBase(route, matchedProviderConfig);
103
+ const upstreamUrl = buildUpstreamUrl(upstreamBase, route.normalizedPath);
104
+
105
+ // Build headers
106
+ const headers = new Headers();
107
+ headers.set(HEADER_USER_AGENT, PROXY_IDENTITY);
108
+ headers.set(HEADER_X_PROXY_IDENTITY, PROXY_IDENTITY);
109
+ headers.set(HEADER_CONTENT_TYPE, "application/json");
110
+
111
+ for (const name of PRESERVE_HEADERS) {
112
+ const value = log.rawHeaders?.[name.toLowerCase()];
113
+ if (value !== undefined) {
114
+ headers.set(name, value);
115
+ }
116
+ }
117
+ setUpstreamHost(headers, upstreamBase);
118
+ injectProviderAuth(headers, matchedProviderConfig);
119
+
120
+ const startTime = Date.now();
121
+
122
+ // Send request
123
+ let upstreamRes: Response;
124
+ try {
125
+ upstreamRes = await fetch(upstreamUrl, {
126
+ method: "POST",
127
+ headers,
128
+ body: modifiedBody,
129
+ });
130
+ } catch (err) {
131
+ return Response.json({
132
+ success: false,
133
+ error: String(err),
134
+ } satisfies ReplayResponse);
135
+ }
136
+
137
+ const elapsedMs = Date.now() - startTime;
138
+ const isStream =
139
+ upstreamRes.headers.get(HEADER_CONTENT_TYPE)?.includes(CONTENT_TYPE_EVENT_STREAM) ??
140
+ false;
141
+
142
+ if (isStream) {
143
+ // Accumulate streaming response
144
+ const chunks: string[] = [];
145
+ const decoder = new TextDecoder();
146
+ const reader = upstreamRes.body?.getReader();
147
+ if (reader) {
148
+ try {
149
+ while (true) {
150
+ const { done, value } = await reader.read();
151
+ if (done) break;
152
+ chunks.push(decoder.decode(value, { stream: true }));
153
+ }
154
+ } catch {
155
+ // Stream read error
156
+ }
157
+ }
158
+ const fullResponse = chunks.join("");
159
+
160
+ // Create a mock log for streaming extraction
161
+ const mockLog: typeof log = { ...log };
162
+ const responseText = formatHandler.extractStream(fullResponse, mockLog, model, false);
163
+ const tokens = formatHandler.extractTokens(responseText);
164
+
165
+ return Response.json({
166
+ success: true,
167
+ responseStatus: upstreamRes.status,
168
+ responseText,
169
+ inputTokens: tokens.inputTokens ?? undefined,
170
+ outputTokens: tokens.outputTokens ?? undefined,
171
+ elapsedMs,
172
+ streaming: true,
173
+ } satisfies ReplayResponse);
174
+ } else {
175
+ const responseText = await upstreamRes.text();
176
+ const tokens = formatHandler.extractTokens(responseText);
177
+
178
+ return Response.json({
179
+ success: true,
180
+ responseStatus: upstreamRes.status,
181
+ responseText,
182
+ inputTokens: tokens.inputTokens ?? undefined,
183
+ outputTokens: tokens.outputTokens ?? undefined,
184
+ elapsedMs,
185
+ streaming: false,
186
+ } satisfies ReplayResponse);
187
+ }
188
+ },
189
+ },
190
+ },
191
+ });
@@ -0,0 +1,22 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { getLogById } from "../../proxy/store";
3
+
4
+ export const Route = createFileRoute("/api/logs/$id")({
5
+ server: {
6
+ handlers: {
7
+ GET: async ({ params }: { params: { id: string } }) => {
8
+ const id = Number(params.id);
9
+ if (isNaN(id)) {
10
+ return Response.json({ error: "Invalid ID" }, { status: 400 });
11
+ }
12
+
13
+ const log = await getLogById(id);
14
+ if (log === null) {
15
+ return Response.json({ error: "Log not found" }, { status: 404 });
16
+ }
17
+
18
+ return Response.json(log);
19
+ },
20
+ },
21
+ },
22
+ });
@@ -0,0 +1,74 @@
1
+ import { createFileRoute } from "@tanstack/react-router";
2
+ import { onLogUpdate, getFilteredLogs, getLogSessionId } from "../../proxy/store";
3
+ import { type CapturedLog } from "../../proxy/schemas";
4
+
5
+ export const Route = createFileRoute("/api/logs/stream")({
6
+ server: {
7
+ handlers: {
8
+ GET: ({ request }: { request: Request }) => {
9
+ const url = new URL(request.url);
10
+ const sessionId = url.searchParams.get("sessionId") ?? undefined;
11
+ const model = url.searchParams.get("model") ?? undefined;
12
+
13
+ let controllerRef: ReadableStreamDefaultController<Uint8Array> | null = null;
14
+ let cleanedUp = false;
15
+
16
+ const unsubscribe = onLogUpdate((log: CapturedLog) => {
17
+ if (controllerRef === null) return;
18
+ // Filter by session/model if specified
19
+ if (sessionId !== undefined && getLogSessionId(log) !== sessionId) return;
20
+ if (model !== undefined && log.model !== model) return;
21
+ try {
22
+ const data = `data: ${JSON.stringify({ type: "update", log })}\n\n`;
23
+ controllerRef.enqueue(new TextEncoder().encode(data));
24
+ } catch {
25
+ cleanup();
26
+ }
27
+ });
28
+
29
+ const cleanup = (): void => {
30
+ if (cleanedUp) return;
31
+ cleanedUp = true;
32
+ clearInterval(heartbeat);
33
+ unsubscribe();
34
+ controllerRef = null;
35
+ request.signal.removeEventListener("abort", cleanup);
36
+ };
37
+
38
+ // Send heartbeat comment every 30s to keep connection alive
39
+ const heartbeat = setInterval(() => {
40
+ if (controllerRef !== null) {
41
+ try {
42
+ controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
43
+ } catch {
44
+ cleanup();
45
+ }
46
+ }
47
+ }, 30000);
48
+
49
+ request.signal.addEventListener("abort", cleanup, { once: true });
50
+
51
+ const stream = new ReadableStream<Uint8Array>({
52
+ start(controller) {
53
+ controllerRef = controller;
54
+ // Send initial batch of logs immediately
55
+ const logs = getFilteredLogs(sessionId, model);
56
+ const initData = `data: ${JSON.stringify({ type: "init", logs })}\n\n`;
57
+ controller.enqueue(new TextEncoder().encode(initData));
58
+ },
59
+ cancel() {
60
+ cleanup();
61
+ },
62
+ });
63
+
64
+ return new Response(stream, {
65
+ headers: {
66
+ "Content-Type": "text/event-stream",
67
+ "Cache-Control": "no-cache",
68
+ Connection: "keep-alive",
69
+ },
70
+ });
71
+ },
72
+ },
73
+ },
74
+ });