@tonyclaw/llm-inspector 1.6.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 (286) hide show
  1. package/.output/nitro.json +17 -0
  2. package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
  3. package/.output/public/assets/index-B3RwBPLW.css +1 -0
  4. package/.output/public/assets/index-s4lwsWvq.js +97 -0
  5. package/.output/public/assets/main-Cp8AM0Pa.js +17 -0
  6. package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
  7. package/.output/public/assets/qwen-CONDcHqt.png +0 -0
  8. package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
  9. package/.output/server/_chunks/ssr-renderer.mjs +17 -0
  10. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
  11. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
  12. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +1 -0
  13. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +1 -0
  14. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +1 -0
  15. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
  16. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
  17. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
  18. package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
  19. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
  20. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
  21. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
  22. package/.output/server/_libs/ajv-formats.mjs +330 -0
  23. package/.output/server/_libs/ajv.mjs +11444 -0
  24. package/.output/server/_libs/aria-hidden.mjs +122 -0
  25. package/.output/server/_libs/atomically.mjs +152 -0
  26. package/.output/server/_libs/bail.mjs +8 -0
  27. package/.output/server/_libs/character-entities.mjs +2130 -0
  28. package/.output/server/_libs/class-variance-authority.mjs +44 -0
  29. package/.output/server/_libs/clsx.mjs +16 -0
  30. package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
  31. package/.output/server/_libs/conf.mjs +635 -0
  32. package/.output/server/_libs/cookie-es.mjs +58 -0
  33. package/.output/server/_libs/core-util-is.mjs +75 -0
  34. package/.output/server/_libs/croner.mjs +1 -0
  35. package/.output/server/_libs/crossws.mjs +1 -0
  36. package/.output/server/_libs/debounce-fn.mjs +69 -0
  37. package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
  38. package/.output/server/_libs/detect-node-es.mjs +1 -0
  39. package/.output/server/_libs/devlop.mjs +8 -0
  40. package/.output/server/_libs/dot-prop.mjs +265 -0
  41. package/.output/server/_libs/env-paths.mjs +57 -0
  42. package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
  43. package/.output/server/_libs/extend.mjs +97 -0
  44. package/.output/server/_libs/fast-deep-equal.mjs +38 -0
  45. package/.output/server/_libs/fast-uri.mjs +812 -0
  46. package/.output/server/_libs/floating-ui__core.mjs +725 -0
  47. package/.output/server/_libs/floating-ui__dom.mjs +622 -0
  48. package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
  49. package/.output/server/_libs/floating-ui__utils.mjs +320 -0
  50. package/.output/server/_libs/get-nonce.mjs +9 -0
  51. package/.output/server/_libs/h3-v2.mjs +276 -0
  52. package/.output/server/_libs/h3.mjs +400 -0
  53. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
  54. package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
  55. package/.output/server/_libs/hookable.mjs +1 -0
  56. package/.output/server/_libs/html-url-attributes.mjs +26 -0
  57. package/.output/server/_libs/immediate.mjs +74 -0
  58. package/.output/server/_libs/inherits.mjs +50 -0
  59. package/.output/server/_libs/inline-style-parser.mjs +142 -0
  60. package/.output/server/_libs/is-plain-obj.mjs +10 -0
  61. package/.output/server/_libs/isarray.mjs +14 -0
  62. package/.output/server/_libs/isbot.mjs +20 -0
  63. package/.output/server/_libs/json-schema-traverse.mjs +180 -0
  64. package/.output/server/_libs/jszip.mjs +3049 -0
  65. package/.output/server/_libs/lie.mjs +273 -0
  66. package/.output/server/_libs/lucide-react.mjs +368 -0
  67. package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
  68. package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
  69. package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
  70. package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
  71. package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
  72. package/.output/server/_libs/micromark-factory-label.mjs +63 -0
  73. package/.output/server/_libs/micromark-factory-space.mjs +24 -0
  74. package/.output/server/_libs/micromark-factory-title.mjs +65 -0
  75. package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
  76. package/.output/server/_libs/micromark-util-character.mjs +44 -0
  77. package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
  78. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
  79. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
  80. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
  81. package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
  82. package/.output/server/_libs/micromark-util-encode.mjs +1 -0
  83. package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
  84. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
  85. package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
  86. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
  87. package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
  88. package/.output/server/_libs/micromark.mjs +906 -0
  89. package/.output/server/_libs/mimic-function.mjs +47 -0
  90. package/.output/server/_libs/ohash.mjs +1 -0
  91. package/.output/server/_libs/pako.mjs +4223 -0
  92. package/.output/server/_libs/process-nextick-args.mjs +48 -0
  93. package/.output/server/_libs/property-information.mjs +1209 -0
  94. package/.output/server/_libs/radix-ui.mjs +1 -0
  95. package/.output/server/_libs/radix-ui__number.mjs +6 -0
  96. package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
  97. package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
  98. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
  99. package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
  100. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
  101. package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
  102. package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
  103. package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
  104. package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
  105. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
  106. package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
  107. package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
  108. package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
  109. package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
  110. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
  111. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
  112. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
  113. package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
  114. package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
  115. package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
  116. package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
  117. package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
  118. package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
  119. package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
  120. package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
  121. package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
  122. package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
  123. package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
  124. package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
  125. package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
  126. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
  127. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
  128. package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
  129. package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
  130. package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
  131. package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
  132. package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
  133. package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
  134. package/.output/server/_libs/radix-ui__react-toast.mjs +1 -0
  135. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
  136. package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
  137. package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
  138. package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
  139. package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
  140. package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
  141. package/.output/server/_libs/react-dom.mjs +9935 -0
  142. package/.output/server/_libs/react-markdown.mjs +147 -0
  143. package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
  144. package/.output/server/_libs/react-remove-scroll.mjs +328 -0
  145. package/.output/server/_libs/react-style-singleton.mjs +69 -0
  146. package/.output/server/_libs/react.mjs +515 -0
  147. package/.output/server/_libs/readable-stream.mjs +1518 -0
  148. package/.output/server/_libs/remark-parse.mjs +19 -0
  149. package/.output/server/_libs/remark-rehype.mjs +21 -0
  150. package/.output/server/_libs/rou3.mjs +8 -0
  151. package/.output/server/_libs/safe-buffer.mjs +64 -0
  152. package/.output/server/_libs/semver.mjs +1984 -0
  153. package/.output/server/_libs/seroval-plugins.mjs +58 -0
  154. package/.output/server/_libs/seroval.mjs +1765 -0
  155. package/.output/server/_libs/setimmediate.mjs +1 -0
  156. package/.output/server/_libs/space-separated-tokens.mjs +6 -0
  157. package/.output/server/_libs/srvx.mjs +334 -0
  158. package/.output/server/_libs/stubborn-fs.mjs +91 -0
  159. package/.output/server/_libs/stubborn-utils.mjs +66 -0
  160. package/.output/server/_libs/style-to-js.mjs +72 -0
  161. package/.output/server/_libs/style-to-object.mjs +38 -0
  162. package/.output/server/_libs/tailwind-merge.mjs +3010 -0
  163. package/.output/server/_libs/tanstack__history.mjs +217 -0
  164. package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
  165. package/.output/server/_libs/tanstack__react-store.mjs +1 -0
  166. package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
  167. package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
  168. package/.output/server/_libs/tanstack__store.mjs +1 -0
  169. package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
  170. package/.output/server/_libs/tiny-invariant.mjs +12 -0
  171. package/.output/server/_libs/tiny-warning.mjs +5 -0
  172. package/.output/server/_libs/trim-lines.mjs +41 -0
  173. package/.output/server/_libs/trough.mjs +85 -0
  174. package/.output/server/_libs/tslib.mjs +576 -0
  175. package/.output/server/_libs/ufo.mjs +54 -0
  176. package/.output/server/_libs/uint8array-extras.mjs +69 -0
  177. package/.output/server/_libs/unctx.mjs +1 -0
  178. package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
  179. package/.output/server/_libs/unified.mjs +661 -0
  180. package/.output/server/_libs/unist-util-is.mjs +100 -0
  181. package/.output/server/_libs/unist-util-position.mjs +27 -0
  182. package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
  183. package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
  184. package/.output/server/_libs/unist-util-visit.mjs +24 -0
  185. package/.output/server/_libs/unstorage.mjs +1 -0
  186. package/.output/server/_libs/use-callback-ref.mjs +66 -0
  187. package/.output/server/_libs/use-sidecar.mjs +106 -0
  188. package/.output/server/_libs/use-sync-external-store.mjs +1 -0
  189. package/.output/server/_libs/util-deprecate.mjs +12 -0
  190. package/.output/server/_libs/vfile-message.mjs +138 -0
  191. package/.output/server/_libs/vfile.mjs +467 -0
  192. package/.output/server/_libs/when-exit.mjs +53 -0
  193. package/.output/server/_libs/zod.mjs +4460 -0
  194. package/.output/server/_ssr/index-ByCLZu7J.mjs +3061 -0
  195. package/.output/server/_ssr/index.mjs +1176 -0
  196. package/.output/server/_ssr/router-Bq_mxeNz.mjs +2872 -0
  197. package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  198. package/.output/server/_tanstack-start-manifest_v-C4E0e9my.mjs +4 -0
  199. package/.output/server/index.mjs +393 -0
  200. package/README.md +196 -0
  201. package/package.json +91 -0
  202. package/src/assets/logos/alibaba.svg +1 -0
  203. package/src/assets/logos/anthropic.svg +1 -0
  204. package/src/assets/logos/deepseek.svg +1 -0
  205. package/src/assets/logos/minimax.jpeg +0 -0
  206. package/src/assets/logos/openai.svg +1 -0
  207. package/src/assets/logos/qwen.png +0 -0
  208. package/src/assets/logos/zhipuai.svg +219 -0
  209. package/src/cli.ts +68 -0
  210. package/src/components/ProxyViewer.tsx +325 -0
  211. package/src/components/ProxyViewerContainer.tsx +211 -0
  212. package/src/components/providers/ProviderCard.tsx +186 -0
  213. package/src/components/providers/ProviderForm.tsx +259 -0
  214. package/src/components/providers/ProviderLogo.tsx +111 -0
  215. package/src/components/providers/ProvidersPanel.tsx +259 -0
  216. package/src/components/providers/SettingsDialog.tsx +39 -0
  217. package/src/components/proxy-viewer/ConversationGroup.tsx +68 -0
  218. package/src/components/proxy-viewer/ConversationHeader.tsx +141 -0
  219. package/src/components/proxy-viewer/LogEntry.tsx +225 -0
  220. package/src/components/proxy-viewer/LogEntryHeader.tsx +250 -0
  221. package/src/components/proxy-viewer/ReplayDialog.tsx +208 -0
  222. package/src/components/proxy-viewer/ResponseView.tsx +161 -0
  223. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +171 -0
  224. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +139 -0
  225. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +64 -0
  226. package/src/components/proxy-viewer/formats/index.tsx +24 -0
  227. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +80 -0
  228. package/src/components/proxy-viewer/index.ts +8 -0
  229. package/src/components/ui/badge.tsx +47 -0
  230. package/src/components/ui/button.tsx +47 -0
  231. package/src/components/ui/collapsible.tsx +21 -0
  232. package/src/components/ui/dialog.tsx +129 -0
  233. package/src/components/ui/json-viewer.tsx +464 -0
  234. package/src/components/ui/scroll-area.tsx +54 -0
  235. package/src/components/ui/select.tsx +178 -0
  236. package/src/components/ui/separator.tsx +28 -0
  237. package/src/components/ui/tabs.tsx +88 -0
  238. package/src/components/ui/tooltip.tsx +51 -0
  239. package/src/index.css +11 -0
  240. package/src/lib/export-logs.ts +51 -0
  241. package/src/lib/utils.ts +22 -0
  242. package/src/proxy/chunkStorage.ts +118 -0
  243. package/src/proxy/constants.ts +36 -0
  244. package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
  245. package/src/proxy/formats/anthropic/handler.ts +74 -0
  246. package/src/proxy/formats/anthropic/index.ts +14 -0
  247. package/src/proxy/formats/anthropic/register.ts +4 -0
  248. package/src/proxy/formats/anthropic/schemas.ts +217 -0
  249. package/src/proxy/formats/anthropic/stream.ts +167 -0
  250. package/src/proxy/formats/handler.ts +46 -0
  251. package/src/proxy/formats/index.ts +12 -0
  252. package/src/proxy/formats/jsonSchema.ts +24 -0
  253. package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
  254. package/src/proxy/formats/openai/handler.ts +70 -0
  255. package/src/proxy/formats/openai/index.ts +25 -0
  256. package/src/proxy/formats/openai/provider.ts +50 -0
  257. package/src/proxy/formats/openai/register.ts +4 -0
  258. package/src/proxy/formats/openai/schemas.ts +150 -0
  259. package/src/proxy/formats/openai/stream.ts +153 -0
  260. package/src/proxy/formats/protocol.ts +50 -0
  261. package/src/proxy/formats/providerRegistry.ts +51 -0
  262. package/src/proxy/formats/providers/index.ts +3 -0
  263. package/src/proxy/formats/registry.ts +61 -0
  264. package/src/proxy/handler.ts +389 -0
  265. package/src/proxy/logIndex.ts +187 -0
  266. package/src/proxy/logger.ts +99 -0
  267. package/src/proxy/providers.ts +234 -0
  268. package/src/proxy/schemas.ts +160 -0
  269. package/src/proxy/socketTracker.ts +158 -0
  270. package/src/proxy/store.ts +386 -0
  271. package/src/router.tsx +16 -0
  272. package/src/routes/__root.tsx +38 -0
  273. package/src/routes/api/config.paths.ts +14 -0
  274. package/src/routes/api/health.ts +11 -0
  275. package/src/routes/api/logs.$id.chunks.ts +36 -0
  276. package/src/routes/api/logs.$id.replay.ts +262 -0
  277. package/src/routes/api/logs.$id.ts +22 -0
  278. package/src/routes/api/logs.stream.ts +64 -0
  279. package/src/routes/api/logs.ts +30 -0
  280. package/src/routes/api/models.ts +10 -0
  281. package/src/routes/api/providers.$providerId.ts +45 -0
  282. package/src/routes/api/providers.ts +37 -0
  283. package/src/routes/api/sessions.ts +10 -0
  284. package/src/routes/index.tsx +6 -0
  285. package/src/routes/proxy/$.ts +15 -0
  286. package/styles/globals.css +121 -0
@@ -0,0 +1,3061 @@
1
+ import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
+ import { C as CapturedLogSchema, a as parseRequest, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-Bq_mxeNz.mjs";
3
+ import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
4
+ import { J as JSZip } from "../_libs/jszip.mjs";
5
+ import { c as clsx } from "../_libs/clsx.mjs";
6
+ import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
+ import { c as cva } from "../_libs/class-variance-authority.mjs";
8
+ import { R as Root, T as Trigger$1, C as Content, a as Close, b as Title, P as Portal$1, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
9
+ import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
10
+ import { D as Download, L as LayoutGrid, a as List, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, X, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, f as ChevronRight, g as Clock, M as MessageSquare, Z as Zap, h as LoaderCircle, W as Wrench, G as Globe, U as User, F as FileTerminal, i as Radio, E as ExternalLink, j as EyeOff, k as Eye, l as RotateCw, m as Pencil, T as Trash2, n as TriangleAlert, o as Minus, p as CircleCheckBig, q as CircleX, r as CircleStop, s as ChevronsUp, t as ChevronsDown, u as Terminal, B as Brain } from "../_libs/lucide-react.mjs";
11
+ import { M as Markdown } from "../_libs/react-markdown.mjs";
12
+ import { a as array, s as string, u as union, o as object, l as literal, b as boolean, n as number } from "../_libs/zod.mjs";
13
+ import { R as Root2$1, L as List$1, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
14
+ import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
15
+ import { P as Provider, R as Root3, T as Trigger$3, a as Portal$2, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
16
+ import { R as Root$1 } from "../_libs/radix-ui__react-separator.mjs";
17
+ import { R as Root$2, C as CollapsibleTrigger$1, a as CollapsibleContent$1 } from "../_libs/radix-ui__react-collapsible.mjs";
18
+ import { R as Root$3, V as Viewport$1, C as Corner, S as ScrollAreaScrollbar, a as ScrollAreaThumb } from "../_libs/radix-ui__react-scroll-area.mjs";
19
+ import "../_libs/tanstack__react-router.mjs";
20
+ import "../_libs/tiny-warning.mjs";
21
+ import "../_libs/tanstack__router-core.mjs";
22
+ import "../_libs/cookie-es.mjs";
23
+ import "../_libs/tanstack__history.mjs";
24
+ import "../_libs/tiny-invariant.mjs";
25
+ import "../_libs/seroval.mjs";
26
+ import "../_libs/seroval-plugins.mjs";
27
+ import "node:stream/web";
28
+ import "node:stream";
29
+ import "../_libs/react-dom.mjs";
30
+ import "../_libs/isbot.mjs";
31
+ import "node:fs";
32
+ import "node:path";
33
+ import "node:fs/promises";
34
+ import "../_libs/conf.mjs";
35
+ import "node:util";
36
+ import "node:process";
37
+ import "node:crypto";
38
+ import "node:assert";
39
+ import "../_libs/dot-prop.mjs";
40
+ import "../_libs/env-paths.mjs";
41
+ import "node:os";
42
+ import "../_libs/atomically.mjs";
43
+ import "../_libs/stubborn-fs.mjs";
44
+ import "../_libs/stubborn-utils.mjs";
45
+ import "../_libs/when-exit.mjs";
46
+ import "../_libs/ajv.mjs";
47
+ import "../_libs/fast-deep-equal.mjs";
48
+ import "../_libs/json-schema-traverse.mjs";
49
+ import "../_libs/fast-uri.mjs";
50
+ import "../_libs/ajv-formats.mjs";
51
+ import "../_libs/debounce-fn.mjs";
52
+ import "../_libs/mimic-function.mjs";
53
+ import "../_libs/semver.mjs";
54
+ import "../_libs/uint8array-extras.mjs";
55
+ import "crypto";
56
+ import "node:child_process";
57
+ import "../_libs/tanstack__virtual-core.mjs";
58
+ import "../_libs/readable-stream.mjs";
59
+ import "stream";
60
+ import "../_libs/process-nextick-args.mjs";
61
+ import "../_libs/isarray.mjs";
62
+ import "events";
63
+ import "../_libs/safe-buffer.mjs";
64
+ import "buffer";
65
+ import "../_libs/core-util-is.mjs";
66
+ import "../_libs/inherits.mjs";
67
+ import "util";
68
+ import "../_libs/util-deprecate.mjs";
69
+ import "node:string_decoder";
70
+ import "../_libs/lie.mjs";
71
+ import "../_libs/immediate.mjs";
72
+ import "../_libs/pako.mjs";
73
+ import "../_libs/radix-ui__primitive.mjs";
74
+ import "../_libs/radix-ui__react-compose-refs.mjs";
75
+ import "../_libs/radix-ui__react-context.mjs";
76
+ import "../_libs/radix-ui__react-id.mjs";
77
+ import "../_libs/@radix-ui/react-use-layout-effect+[...].mjs";
78
+ import "../_libs/@radix-ui/react-use-controllable-state+[...].mjs";
79
+ import "../_libs/@radix-ui/react-dismissable-layer+[...].mjs";
80
+ import "../_libs/radix-ui__react-primitive.mjs";
81
+ import "../_libs/@radix-ui/react-use-callback-ref+[...].mjs";
82
+ import "../_libs/@radix-ui/react-use-escape-keydown+[...].mjs";
83
+ import "../_libs/radix-ui__react-focus-scope.mjs";
84
+ import "../_libs/radix-ui__react-portal.mjs";
85
+ import "../_libs/radix-ui__react-presence.mjs";
86
+ import "../_libs/radix-ui__react-focus-guards.mjs";
87
+ import "../_libs/react-remove-scroll.mjs";
88
+ import "../_libs/tslib.mjs";
89
+ import "../_libs/react-remove-scroll-bar.mjs";
90
+ import "../_libs/react-style-singleton.mjs";
91
+ import "../_libs/get-nonce.mjs";
92
+ import "../_libs/use-sidecar.mjs";
93
+ import "../_libs/use-callback-ref.mjs";
94
+ import "../_libs/aria-hidden.mjs";
95
+ import "../_libs/radix-ui__number.mjs";
96
+ import "../_libs/radix-ui__react-collection.mjs";
97
+ import "../_libs/radix-ui__react-direction.mjs";
98
+ import "../_libs/radix-ui__react-popper.mjs";
99
+ import "../_libs/floating-ui__react-dom.mjs";
100
+ import "../_libs/floating-ui__dom.mjs";
101
+ import "../_libs/floating-ui__core.mjs";
102
+ import "../_libs/floating-ui__utils.mjs";
103
+ import "../_libs/radix-ui__react-arrow.mjs";
104
+ import "../_libs/radix-ui__react-use-size.mjs";
105
+ import "../_libs/radix-ui__react-use-previous.mjs";
106
+ import "../_libs/@radix-ui/react-visually-hidden+[...].mjs";
107
+ import "../_libs/devlop.mjs";
108
+ import "../_libs/unified.mjs";
109
+ import "../_libs/bail.mjs";
110
+ import "../_libs/extend.mjs";
111
+ import "../_libs/is-plain-obj.mjs";
112
+ import "../_libs/trough.mjs";
113
+ import "../_libs/vfile.mjs";
114
+ import "../_libs/vfile-message.mjs";
115
+ import "../_libs/unist-util-stringify-position.mjs";
116
+ import "node:url";
117
+ import "../_libs/remark-parse.mjs";
118
+ import "../_libs/mdast-util-from-markdown.mjs";
119
+ import "../_libs/micromark-util-decode-numeric-character-reference+[...].mjs";
120
+ import "../_libs/micromark-util-decode-string.mjs";
121
+ import "../_libs/decode-named-character-reference+[...].mjs";
122
+ import "../_libs/character-entities.mjs";
123
+ import "../_libs/micromark-util-normalize-identifier+[...].mjs";
124
+ import "../_libs/micromark.mjs";
125
+ import "../_libs/micromark-util-combine-extensions+[...].mjs";
126
+ import "../_libs/micromark-util-chunked.mjs";
127
+ import "../_libs/micromark-factory-space.mjs";
128
+ import "../_libs/micromark-util-character.mjs";
129
+ import "../_libs/micromark-core-commonmark.mjs";
130
+ import "../_libs/micromark-util-classify-character+[...].mjs";
131
+ import "../_libs/micromark-util-resolve-all.mjs";
132
+ import "../_libs/micromark-util-subtokenize.mjs";
133
+ import "../_libs/micromark-factory-destination.mjs";
134
+ import "../_libs/micromark-factory-label.mjs";
135
+ import "../_libs/micromark-factory-title.mjs";
136
+ import "../_libs/micromark-factory-whitespace.mjs";
137
+ import "../_libs/micromark-util-html-tag-name.mjs";
138
+ import "../_libs/mdast-util-to-string.mjs";
139
+ import "../_libs/remark-rehype.mjs";
140
+ import "../_libs/mdast-util-to-hast.mjs";
141
+ import "../_libs/ungap__structured-clone.mjs";
142
+ import "../_libs/micromark-util-sanitize-uri.mjs";
143
+ import "../_libs/unist-util-position.mjs";
144
+ import "../_libs/trim-lines.mjs";
145
+ import "../_libs/unist-util-visit.mjs";
146
+ import "../_libs/unist-util-visit-parents.mjs";
147
+ import "../_libs/unist-util-is.mjs";
148
+ import "../_libs/hast-util-to-jsx-runtime.mjs";
149
+ import "../_libs/comma-separated-tokens.mjs";
150
+ import "../_libs/property-information.mjs";
151
+ import "../_libs/space-separated-tokens.mjs";
152
+ import "../_libs/style-to-js.mjs";
153
+ import "../_libs/style-to-object.mjs";
154
+ import "../_libs/inline-style-parser.mjs";
155
+ import "../_libs/hast-util-whitespace.mjs";
156
+ import "../_libs/estree-util-is-identifier-name.mjs";
157
+ import "../_libs/html-url-attributes.mjs";
158
+ import "../_libs/radix-ui__react-roving-focus.mjs";
159
+ async function fetchStreamingChunks(logId) {
160
+ try {
161
+ const response = await fetch(`/api/logs/${logId}/chunks`);
162
+ if (!response.ok) return null;
163
+ const data = await response.json();
164
+ return JSON.stringify(data, null, 2);
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
169
+ async function exportLogsAsZip(logs) {
170
+ const zip = new JSZip();
171
+ for (const log of logs) {
172
+ if (log.rawRequestBody !== null) {
173
+ zip.file(`#${log.id}.Request.json`, log.rawRequestBody);
174
+ }
175
+ if (log.responseText !== null) {
176
+ zip.file(`#${log.id}.Response.json`, log.responseText);
177
+ }
178
+ }
179
+ const streamingLogs = logs.filter((log) => log.streaming);
180
+ await Promise.allSettled(
181
+ streamingLogs.map(async (log) => {
182
+ const chunks = await fetchStreamingChunks(log.id);
183
+ if (chunks !== null) {
184
+ zip.file(`#${log.id}.SSE.Response.json`, chunks);
185
+ }
186
+ })
187
+ );
188
+ const blob = await zip.generateAsync({ type: "blob" });
189
+ const url = URL.createObjectURL(blob);
190
+ const anchor = document.createElement("a");
191
+ anchor.href = url;
192
+ anchor.download = `llm-inspector-export-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.zip`;
193
+ document.body.appendChild(anchor);
194
+ anchor.click();
195
+ document.body.removeChild(anchor);
196
+ URL.revokeObjectURL(url);
197
+ }
198
+ function cn(...inputs) {
199
+ return twMerge(clsx(inputs));
200
+ }
201
+ function formatTokens(count) {
202
+ if (count >= 1048576) return (count / 1048576).toFixed(1).replace(/\.0$/, "") + "M";
203
+ if (count >= 1024) return (count / 1024).toFixed(1).replace(/\.0$/, "") + "K";
204
+ return count.toString();
205
+ }
206
+ function getStatusCategory(status) {
207
+ if (status === null) return "pending";
208
+ if (status >= 200 && status < 300) return "success";
209
+ if (status >= 400 && status < 500) return "client_error";
210
+ if (status >= 500) return "server_error";
211
+ return "pending";
212
+ }
213
+ function formatTimestamp(iso) {
214
+ const date = new Date(iso);
215
+ return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
216
+ }
217
+ function ConversationHeader({
218
+ conversationId,
219
+ startTime,
220
+ endTime,
221
+ totalCalls,
222
+ totalInputTokens,
223
+ totalOutputTokens,
224
+ expanded,
225
+ onToggle
226
+ }) {
227
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
228
+ "div",
229
+ {
230
+ role: "button",
231
+ tabIndex: 0,
232
+ className: cn(
233
+ "flex items-center gap-3 px-3 py-2 cursor-pointer transition-colors",
234
+ "hover:bg-muted/50",
235
+ "select-none",
236
+ "border border-border rounded-lg mb-2 bg-muted/30"
237
+ ),
238
+ onClick: onToggle,
239
+ onKeyDown: (e) => {
240
+ if (e.key === "Enter" || e.key === " ") {
241
+ e.preventDefault();
242
+ onToggle();
243
+ }
244
+ },
245
+ children: [
246
+ expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 text-muted-foreground shrink-0" }),
247
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
248
+ "span",
249
+ {
250
+ className: "text-purple-400/90 font-mono text-xs font-semibold shrink-0",
251
+ title: conversationId,
252
+ children: conversationId.length > 24 ? conversationId.slice(0, 12) + "…" + conversationId.slice(-12) : conversationId
253
+ }
254
+ ),
255
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
256
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
257
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
258
+ formatTimestamp(startTime),
259
+ " - ",
260
+ formatTimestamp(endTime)
261
+ ] })
262
+ ] }),
263
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
264
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3" }),
265
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
266
+ totalCalls,
267
+ " call",
268
+ totalCalls !== 1 ? "s" : ""
269
+ ] })
270
+ ] }),
271
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
272
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
273
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
274
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-400", children: formatTokens(totalInputTokens) }),
275
+ " / ",
276
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400", children: formatTokens(totalOutputTokens) })
277
+ ] })
278
+ ] }),
279
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" })
280
+ ]
281
+ }
282
+ );
283
+ }
284
+ function getConversationId(log) {
285
+ if (log.sessionId !== null && log.sessionId !== "") {
286
+ return log.sessionId;
287
+ }
288
+ const pid = log.clientPid !== null ? `PID:${log.clientPid}` : "unknown";
289
+ const folder = log.clientProjectFolder !== null ? log.clientProjectFolder : "no-folder";
290
+ return `${pid}|${folder}`;
291
+ }
292
+ function groupLogsByConversation(logs) {
293
+ const groups = /* @__PURE__ */ new Map();
294
+ for (const log of logs) {
295
+ const convId = getConversationId(log);
296
+ const existing = groups.get(convId);
297
+ if (existing !== void 0) {
298
+ existing.push(log);
299
+ } else {
300
+ groups.set(convId, [log]);
301
+ }
302
+ }
303
+ const result = [];
304
+ for (const [conversationId, groupLogs] of groups) {
305
+ const sorted = [...groupLogs].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
306
+ result.push({
307
+ id: conversationId,
308
+ conversationId,
309
+ logs: sorted
310
+ });
311
+ }
312
+ result.sort((a, b) => (a.logs[0]?.timestamp ?? "").localeCompare(b.logs[0]?.timestamp ?? ""));
313
+ return result;
314
+ }
315
+ const buttonVariants = cva(
316
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
317
+ {
318
+ variants: {
319
+ variant: {
320
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
321
+ destructive: "bg-destructive text-white hover:bg-destructive/90",
322
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
323
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
324
+ ghost: "hover:bg-accent hover:text-accent-foreground",
325
+ link: "text-primary underline-offset-4 hover:underline"
326
+ },
327
+ size: {
328
+ default: "h-9 px-4 py-2",
329
+ sm: "h-8 rounded-md px-3 text-xs",
330
+ lg: "h-10 rounded-md px-8",
331
+ icon: "size-9"
332
+ }
333
+ },
334
+ defaultVariants: {
335
+ variant: "default",
336
+ size: "default"
337
+ }
338
+ }
339
+ );
340
+ function Button({
341
+ className,
342
+ variant,
343
+ size,
344
+ ...props
345
+ }) {
346
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
347
+ "button",
348
+ {
349
+ "data-slot": "button",
350
+ className: cn(buttonVariants({ variant, size, className })),
351
+ ...props
352
+ }
353
+ );
354
+ }
355
+ function TooltipProvider({
356
+ delayDuration = 0,
357
+ ...props
358
+ }) {
359
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
360
+ Provider,
361
+ {
362
+ "data-slot": "tooltip-provider",
363
+ delayDuration,
364
+ ...props
365
+ }
366
+ );
367
+ }
368
+ function Tooltip({ ...props }) {
369
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Root3, { "data-slot": "tooltip", ...props });
370
+ }
371
+ function TooltipTrigger({ ...props }) {
372
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$3, { "data-slot": "tooltip-trigger", ...props });
373
+ }
374
+ function TooltipContent({
375
+ className,
376
+ sideOffset = 0,
377
+ children,
378
+ ...props
379
+ }) {
380
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$2, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
381
+ Content2$1,
382
+ {
383
+ "data-slot": "tooltip-content",
384
+ sideOffset,
385
+ className: cn(
386
+ "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
387
+ className
388
+ ),
389
+ ...props,
390
+ children: [
391
+ children,
392
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Arrow2, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
393
+ ]
394
+ }
395
+ ) });
396
+ }
397
+ function classifyValue(value) {
398
+ if (value === null) return "null";
399
+ if (Array.isArray(value)) return "array";
400
+ switch (typeof value) {
401
+ case "string":
402
+ return "string";
403
+ case "number":
404
+ return "number";
405
+ case "boolean":
406
+ return "boolean";
407
+ case "object":
408
+ return "object";
409
+ case "bigint":
410
+ case "symbol":
411
+ case "undefined":
412
+ case "function":
413
+ return "object";
414
+ }
415
+ }
416
+ function isExpandable(value) {
417
+ return value !== null && (Array.isArray(value) || typeof value === "object");
418
+ }
419
+ function getPropertyValue(obj, key) {
420
+ const descriptor = Object.getOwnPropertyDescriptor(obj, key);
421
+ if (descriptor === void 0) return void 0;
422
+ return descriptor.value;
423
+ }
424
+ function getEntries(value) {
425
+ if (Array.isArray(value)) {
426
+ return value.map((item, index) => [String(index), item]);
427
+ }
428
+ if (typeof value === "object" && value !== null) {
429
+ return Object.keys(value).map((key) => {
430
+ const raw = getPropertyValue(value, key);
431
+ return [key, safeJsonValue(raw)];
432
+ });
433
+ }
434
+ return [];
435
+ }
436
+ function getItemCount(value) {
437
+ if (Array.isArray(value)) return value.length;
438
+ if (typeof value === "object" && value !== null) return Object.keys(value).length;
439
+ return 0;
440
+ }
441
+ const STRING_TRUNCATE_LIMIT = 120;
442
+ function StringValue({ text }) {
443
+ const [expanded, setExpanded] = reactExports.useState(false);
444
+ const isLong = text.length > STRING_TRUNCATE_LIMIT;
445
+ if (!isLong) {
446
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
447
+ '"',
448
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) }),
449
+ '"'
450
+ ] });
451
+ }
452
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
453
+ '"',
454
+ expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(
455
+ "span",
456
+ {
457
+ className: "cursor-pointer prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300",
458
+ onClick: (e) => {
459
+ e.stopPropagation();
460
+ setExpanded(false);
461
+ },
462
+ onKeyDown: (e) => {
463
+ if (e.key === "Enter" || e.key === " ") {
464
+ e.stopPropagation();
465
+ setExpanded(false);
466
+ }
467
+ },
468
+ role: "button",
469
+ tabIndex: 0,
470
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text })
471
+ }
472
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { delayDuration: 300, children: [
473
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
474
+ TooltipTrigger,
475
+ {
476
+ onClick: (e) => {
477
+ e.stopPropagation();
478
+ setExpanded(true);
479
+ },
480
+ className: "text-left cursor-pointer",
481
+ children: [
482
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: text.slice(0, STRING_TRUNCATE_LIMIT) }),
483
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400/50", children: "…" })
484
+ ]
485
+ }
486
+ ),
487
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
488
+ TooltipContent,
489
+ {
490
+ side: "bottom",
491
+ className: "max-w-md text-xs p-2 break-words whitespace-pre-wrap",
492
+ children: [
493
+ text.slice(0, 500),
494
+ text.length > 500 ? "…" : ""
495
+ ]
496
+ }
497
+ )
498
+ ] }),
499
+ '"'
500
+ ] });
501
+ }
502
+ function PrimitiveValue({ value }) {
503
+ if (value === null) {
504
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-rose-400 italic", children: "null" });
505
+ }
506
+ switch (typeof value) {
507
+ case "string":
508
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(StringValue, { text: value });
509
+ case "number":
510
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400", children: value });
511
+ case "boolean":
512
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-400", children: value ? "true" : "false" });
513
+ case "object":
514
+ case "bigint":
515
+ case "symbol":
516
+ case "undefined":
517
+ case "function":
518
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: JSON.stringify(value) });
519
+ }
520
+ }
521
+ function CopyValueButton({ value }) {
522
+ const [copied, setCopied] = reactExports.useState(false);
523
+ function handleCopy(e) {
524
+ e.stopPropagation();
525
+ void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
526
+ setCopied(true);
527
+ setTimeout(() => {
528
+ setCopied(false);
529
+ }, 2e3);
530
+ });
531
+ }
532
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
533
+ "button",
534
+ {
535
+ type: "button",
536
+ onClick: handleCopy,
537
+ className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors",
538
+ title: "Copy JSON",
539
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
540
+ }
541
+ );
542
+ }
543
+ function CopyButton$1({ value }) {
544
+ const [copied, setCopied] = reactExports.useState(false);
545
+ function handleCopy(e) {
546
+ e.stopPropagation();
547
+ void window.navigator.clipboard.writeText(JSON.stringify(value, null, 2)).then(() => {
548
+ setCopied(true);
549
+ setTimeout(() => {
550
+ setCopied(false);
551
+ }, 2e3);
552
+ });
553
+ }
554
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
555
+ "button",
556
+ {
557
+ type: "button",
558
+ onClick: handleCopy,
559
+ className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
560
+ title: "Copy to clipboard",
561
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3 text-muted-foreground" })
562
+ }
563
+ );
564
+ }
565
+ function hasExpandableDescendant(value) {
566
+ if (!isExpandable(value)) return false;
567
+ return getEntries(value).some(([, v]) => isExpandable(v));
568
+ }
569
+ function ExpandCollapseButton({
570
+ allExpanded,
571
+ onClick
572
+ }) {
573
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
574
+ "button",
575
+ {
576
+ type: "button",
577
+ onClick,
578
+ className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
579
+ title: allExpanded ? "Collapse all" : "Expand all",
580
+ children: allExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsUp, { className: "size-3.5 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5 text-muted-foreground" })
581
+ }
582
+ );
583
+ }
584
+ function JsonNode({
585
+ name,
586
+ value,
587
+ level,
588
+ defaultExpandDepth,
589
+ isArrayItem
590
+ }) {
591
+ const [expanded, setExpanded] = reactExports.useState(level < defaultExpandDepth);
592
+ const [childResetKey, setChildResetKey] = reactExports.useState(0);
593
+ const [childDepthOverride, setChildDepthOverride] = reactExports.useState(null);
594
+ const [allExpanded, setAllExpanded] = reactExports.useState(false);
595
+ const expandable = isExpandable(value);
596
+ const dataType = classifyValue(value);
597
+ const openBracket = dataType === "array" ? "[" : "{";
598
+ const closeBracket = dataType === "array" ? "]" : "}";
599
+ function handleExpandAll(e) {
600
+ e.stopPropagation();
601
+ if (allExpanded) {
602
+ setExpanded(false);
603
+ setChildDepthOverride(0);
604
+ setChildResetKey((k) => k + 1);
605
+ setAllExpanded(false);
606
+ } else {
607
+ setExpanded(true);
608
+ setChildDepthOverride(Number.POSITIVE_INFINITY);
609
+ setChildResetKey((k) => k + 1);
610
+ setAllExpanded(true);
611
+ }
612
+ }
613
+ const effectiveChildDepth = childDepthOverride ?? defaultExpandDepth;
614
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn(level > 0 && "border-l border-border/50 ml-2"), children: [
615
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
616
+ "div",
617
+ {
618
+ className: cn(
619
+ "flex items-start gap-1 py-0.5 px-1 -ml-1 rounded-sm group/row",
620
+ expandable && "cursor-pointer hover:bg-muted/50"
621
+ ),
622
+ onClick: expandable ? () => {
623
+ if (expanded) {
624
+ setAllExpanded(false);
625
+ }
626
+ setExpanded(!expanded);
627
+ } : void 0,
628
+ onKeyDown: expandable ? (e) => {
629
+ if (e.key === "Enter" || e.key === " ") {
630
+ e.preventDefault();
631
+ setExpanded(!expanded);
632
+ }
633
+ } : void 0,
634
+ role: expandable ? "button" : void 0,
635
+ tabIndex: expandable ? 0 : void 0,
636
+ children: [
637
+ expandable ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 h-5 flex items-center justify-center shrink-0", children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 shrink-0" }),
638
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("shrink-0", isArrayItem ? "text-muted-foreground" : "text-cyan-400"), children: isArrayItem ? name : `"${name}"` }),
639
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground shrink-0", children: expandable ? "" : ":" }),
640
+ expandable ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
641
+ openBracket,
642
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground/60 text-xs", children: [
643
+ " ",
644
+ getItemCount(value),
645
+ " ",
646
+ getItemCount(value) === 1 ? "item" : "items",
647
+ " ",
648
+ closeBracket
649
+ ] })
650
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value }) }),
651
+ expandable && hasExpandableDescendant(value) && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandCollapseButton, { allExpanded, onClick: handleExpandAll }),
652
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CopyButton$1, { value })
653
+ ]
654
+ }
655
+ ),
656
+ expandable && expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-4", children: [
657
+ getEntries(value).map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
658
+ JsonNode,
659
+ {
660
+ name: key,
661
+ value: childValue,
662
+ level: level + 1,
663
+ defaultExpandDepth: effectiveChildDepth,
664
+ isArrayItem: dataType === "array"
665
+ },
666
+ key
667
+ )),
668
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground py-0.5 px-1", children: closeBracket })
669
+ ] }, childResetKey)
670
+ ] });
671
+ }
672
+ function JsonViewer({
673
+ data,
674
+ defaultExpandDepth = 2,
675
+ className,
676
+ showCopy = false
677
+ }) {
678
+ const expandable = isExpandable(data);
679
+ if (!expandable) {
680
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("font-mono text-xs leading-relaxed", className), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
681
+ /* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value: data }),
682
+ showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data })
683
+ ] }) }) });
684
+ }
685
+ const dataType = classifyValue(data);
686
+ const isArray = dataType === "array";
687
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("font-mono text-xs leading-relaxed", className), children: [
688
+ showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data }),
689
+ getEntries(data).map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
690
+ JsonNode,
691
+ {
692
+ name: key,
693
+ value: childValue,
694
+ level: 0,
695
+ defaultExpandDepth,
696
+ isArrayItem: isArray
697
+ },
698
+ key
699
+ ))
700
+ ] }) });
701
+ }
702
+ function JsonViewerFromString({
703
+ text,
704
+ defaultExpandDepth = 2,
705
+ className
706
+ }) {
707
+ try {
708
+ const parsed = JSON.parse(text);
709
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
710
+ JsonViewer,
711
+ {
712
+ data: safeJsonValue(parsed),
713
+ defaultExpandDepth,
714
+ className
715
+ }
716
+ );
717
+ } catch {
718
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: cn("font-mono text-xs whitespace-pre-wrap break-words", className), children: text });
719
+ }
720
+ }
721
+ function safeJsonValue(value) {
722
+ if (value === null || value === void 0) return null;
723
+ switch (typeof value) {
724
+ case "string":
725
+ return value;
726
+ case "number":
727
+ return value;
728
+ case "boolean":
729
+ return value;
730
+ case "object": {
731
+ if (Array.isArray(value)) {
732
+ return value.map((item) => safeJsonValue(item));
733
+ }
734
+ const result = {};
735
+ for (const key of Object.keys(value)) {
736
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
737
+ result[key] = safeJsonValue(descriptor?.value);
738
+ }
739
+ return result;
740
+ }
741
+ case "bigint":
742
+ case "symbol":
743
+ case "function":
744
+ case "undefined":
745
+ return String(value);
746
+ }
747
+ return null;
748
+ }
749
+ function Tabs({
750
+ className,
751
+ orientation = "horizontal",
752
+ ...props
753
+ }) {
754
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
755
+ Root2$1,
756
+ {
757
+ "data-slot": "tabs",
758
+ "data-orientation": orientation,
759
+ orientation,
760
+ className: cn("group/tabs flex gap-2 data-[orientation=horizontal]:flex-col", className),
761
+ ...props
762
+ }
763
+ );
764
+ }
765
+ const tabsListVariants = cva(
766
+ "rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
767
+ {
768
+ variants: {
769
+ variant: {
770
+ default: "bg-muted",
771
+ line: "gap-1 bg-transparent"
772
+ }
773
+ },
774
+ defaultVariants: {
775
+ variant: "default"
776
+ }
777
+ }
778
+ );
779
+ function TabsList({
780
+ className,
781
+ variant = "default",
782
+ ...props
783
+ }) {
784
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
785
+ List$1,
786
+ {
787
+ "data-slot": "tabs-list",
788
+ "data-variant": variant,
789
+ className: cn(tabsListVariants({ variant }), className),
790
+ ...props
791
+ }
792
+ );
793
+ }
794
+ function TabsTrigger({
795
+ className,
796
+ ...props
797
+ }) {
798
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
799
+ Trigger$2,
800
+ {
801
+ "data-slot": "tabs-trigger",
802
+ className: cn(
803
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
804
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
805
+ "data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 data-[state=active]:text-foreground",
806
+ "after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
807
+ className
808
+ ),
809
+ ...props
810
+ }
811
+ );
812
+ }
813
+ function TabsContent({
814
+ className,
815
+ ...props
816
+ }) {
817
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
818
+ Content$1,
819
+ {
820
+ "data-slot": "tabs-content",
821
+ className: cn("flex-1 outline-none", className),
822
+ ...props
823
+ }
824
+ );
825
+ }
826
+ const badgeVariants = cva(
827
+ "inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
828
+ {
829
+ variants: {
830
+ variant: {
831
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
832
+ secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
833
+ destructive: "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
834
+ outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
835
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
836
+ link: "text-primary underline-offset-4 [a&]:hover:underline"
837
+ }
838
+ },
839
+ defaultVariants: {
840
+ variant: "default"
841
+ }
842
+ }
843
+ );
844
+ function Badge({
845
+ className,
846
+ variant = "default",
847
+ asChild = false,
848
+ ...props
849
+ }) {
850
+ const Comp = asChild ? Slot : "span";
851
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
852
+ Comp,
853
+ {
854
+ "data-slot": "badge",
855
+ "data-variant": variant,
856
+ className: cn(badgeVariants({ variant }), className),
857
+ ...props
858
+ }
859
+ );
860
+ }
861
+ const AnthropicLogoSvg = "data:image/svg+xml,%3csvg%20height='2500'%20viewBox='0%206.603%201192.672%201193.397'%20width='2500'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='m233.96%20800.215%20234.684-131.678%203.947-11.436-3.947-6.363h-11.436l-39.221-2.416-134.094-3.624-116.296-4.832-112.67-6.04-28.35-6.04-26.577-35.035%202.738-17.477%2023.84-16.027%2034.147%202.98%2075.463%205.155%20113.235%207.812%2082.147%204.832%20121.692%2012.644h19.329l2.738-7.812-6.604-4.832-5.154-4.832-117.182-79.41-126.845-83.92-66.443-48.321-35.92-24.484-18.12-22.953-7.813-50.093%2032.618-35.92%2043.812%202.98%2011.195%202.98%2044.375%2034.147%2094.792%2073.37%20123.786%2091.167%2018.12%2015.06%207.249-5.154.886-3.624-8.135-13.61-67.329-121.692-71.838-123.785-31.974-51.302-8.456-30.765c-2.98-12.645-5.154-23.275-5.154-36.242l37.127-50.416%2020.537-6.604%2049.53%206.604%2020.86%2018.121%2030.765%2070.39%2049.852%20110.818%2077.315%20150.684%2022.631%2044.698%2012.08%2041.396%204.51%2012.645h7.813v-7.248l6.362-84.886%2011.759-104.215%2011.436-134.094%203.946-37.772%2018.685-45.262%2037.127-24.482%2028.994%2013.852%2023.839%2034.148-3.303%2022.067-14.174%2092.134-27.785%20144.323-18.121%2096.644h10.55l12.08-12.08%2048.887-64.913%2082.147-102.685%2036.242-40.752%2042.282-45.02%2027.14-21.423h51.303l37.772%2056.135-16.913%2057.986-52.832%2067.007-43.812%2056.779-62.82%2084.563-39.22%2067.651%203.623%205.396%209.343-.886%20141.906-30.201%2076.671-13.852%2091.49-15.705%2041.396%2019.329%204.51%2019.65-16.269%2040.189-97.852%2024.16-114.764%2022.954-170.9%2040.43-2.093%201.53%202.416%202.98%2076.993%207.248%2032.94%201.771h80.617l150.12%2011.195%2039.222%2025.933%2023.517%2031.732-3.946%2024.16-60.403%2030.766-81.503-19.33-190.228-45.26-65.235-16.27h-9.02v5.397l54.362%2053.154%2099.624%2089.96%20124.752%20115.973%206.362%2028.671-16.027%2022.63-16.912-2.415-109.611-82.47-42.282-37.127-95.758-80.618h-6.363v8.456l22.067%2032.296%20116.537%20175.167%206.04%2053.719-8.456%2017.476-30.201%2010.55-33.181-6.04-68.215-95.758-70.39-107.84-56.778-96.644-6.926%203.947-33.503%20360.886-15.705%2018.443-36.243%2013.852-30.201-22.953-16.027-37.127%2016.027-73.37%2019.329-95.758%2015.704-76.107%2014.175-94.55%208.456-31.41-.563-2.094-6.927.886-71.275%2097.852-108.402%20146.497-85.772%2091.812-20.537%208.134-35.597-18.443%203.301-32.94%2019.893-29.315%20118.712-151.007%2071.597-93.583%2046.228-54.04-.322-7.813h-2.738l-315.302%20204.725-56.135%207.248-24.16-22.63%202.98-37.128%2011.435-12.08%2094.792-65.236-.322.323z'%20fill='%23d97757'/%3e%3c/svg%3e";
862
+ const OpenAILogoSvg = "data:image/svg+xml,%3csvg%20height='2500'%20viewBox='-1%20-.1%20949.1%20959.8'%20width='2474'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='m925.8%20456.3c10.4%2023.2%2017%2048%2019.7%2073.3%202.6%2025.3%201.3%2050.9-4.1%2075.8-5.3%2024.9-14.5%2048.8-27.3%2070.8-8.4%2014.7-18.3%2028.5-29.7%2041.2-11.3%2012.6-23.9%2024-37.6%2034-13.8%2010-28.5%2018.4-44.1%2025.3-15.5%206.8-31.7%2012-48.3%2015.4-7.8%2024.2-19.4%2047.1-34.4%2067.7-14.9%2020.6-33%2038.7-53.6%2053.6-20.6%2015-43.4%2026.6-67.6%2034.4-24.2%207.9-49.5%2011.8-75%2011.8-16.9.1-33.9-1.7-50.5-5.1-16.5-3.5-32.7-8.8-48.2-15.7s-30.2-15.5-43.9-25.5c-13.6-10-26.2-21.5-37.4-34.2-25%205.4-50.6%206.7-75.9%204.1-25.3-2.7-50.1-9.3-73.4-19.7-23.2-10.3-44.7-24.3-63.6-41.4s-35-37.1-47.7-59.1c-8.5-14.7-15.5-30.2-20.8-46.3s-8.8-32.7-10.6-49.6c-1.8-16.8-1.7-33.8.1-50.7%201.8-16.8%205.5-33.4%2010.8-49.5-17-18.9-31-40.4-41.4-63.6-10.3-23.3-17-48-19.6-73.3-2.7-25.3-1.3-50.9%204-75.8s14.5-48.8%2027.3-70.8c8.4-14.7%2018.3-28.6%2029.6-41.2s24-24%2037.7-34%2028.5-18.5%2044-25.3c15.6-6.9%2031.8-12%2048.4-15.4%207.8-24.3%2019.4-47.1%2034.3-67.7%2015-20.6%2033.1-38.7%2053.7-53.7%2020.6-14.9%2043.4-26.5%2067.6-34.4%2024.2-7.8%2049.5-11.8%2075-11.7%2016.9-.1%2033.9%201.6%2050.5%205.1s32.8%208.7%2048.3%2015.6c15.5%207%2030.2%2015.5%2043.9%2025.5%2013.7%2010.1%2026.3%2021.5%2037.5%2034.2%2024.9-5.3%2050.5-6.6%2075.8-4s50%209.3%2073.3%2019.6c23.2%2010.4%2044.7%2024.3%2063.6%2041.4%2018.9%2017%2035%2036.9%2047.7%2059%208.5%2014.6%2015.5%2030.1%2020.8%2046.3%205.3%2016.1%208.9%2032.7%2010.6%2049.6%201.8%2016.9%201.8%2033.9-.1%2050.8-1.8%2016.9-5.5%2033.5-10.8%2049.6%2017.1%2018.9%2031%2040.3%2041.4%2063.6zm-333.2%20426.9c21.8-9%2041.6-22.3%2058.3-39s30-36.5%2039-58.4c9-21.8%2013.7-45.2%2013.7-68.8v-223q-.1-.3-.2-.7-.1-.3-.3-.6-.2-.3-.5-.5-.3-.3-.6-.4l-80.7-46.6v269.4c0%202.7-.4%205.5-1.1%208.1-.7%202.7-1.7%205.2-3.1%207.6s-3%204.6-5%206.5a32.1%2032.1%200%200%201%20-6.5%205l-191.1%20110.3c-1.6%201-4.3%202.4-5.7%203.2%207.9%206.7%2016.5%2012.6%2025.5%2017.8%209.1%205.2%2018.5%209.6%2028.3%2013.2%209.8%203.5%2019.9%206.2%2030.1%208%2010.3%201.8%2020.7%202.7%2031.1%202.7%2023.6%200%2047-4.7%2068.8-13.8zm-455.1-151.4c11.9%2020.5%2027.6%2038.3%2046.3%2052.7%2018.8%2014.4%2040.1%2024.9%2062.9%2031s46.6%207.7%2070%204.6%2045.9-10.7%2066.4-22.5l193.2-111.5.5-.5q.2-.2.3-.6.2-.3.3-.6v-94l-233.2%20134.9c-2.4%201.4-4.9%202.4-7.5%203.2-2.7.7-5.4%201-8.2%201-2.7%200-5.4-.3-8.1-1-2.6-.8-5.2-1.8-7.6-3.2l-191.1-110.4c-1.7-1-4.2-2.5-5.6-3.4-1.8%2010.3-2.7%2020.7-2.7%2031.1s1%2020.8%202.8%2031.1c1.8%2010.2%204.6%2020.3%208.1%2030.1%203.6%209.8%208%2019.2%2013.2%2028.2zm-50.2-417c-11.8%2020.5-19.4%2043.1-22.5%2066.5s-1.5%2047.1%204.6%2070c6.1%2022.8%2016.6%2044.1%2031%2062.9%2014.4%2018.7%2032.3%2034.4%2052.7%2046.2l193.1%20111.6q.3.1.7.2h.7q.4%200%20.7-.2.3-.1.6-.3l81-46.8-233.2-134.6c-2.3-1.4-4.5-3.1-6.5-5a32.1%2032.1%200%200%201%20-5-6.5c-1.3-2.4-2.4-4.9-3.1-7.6-.7-2.6-1.1-5.3-1-8.1v-227.1c-9.8%203.6-19.3%208-28.3%2013.2-9%205.3-17.5%2011.3-25.5%2018-7.9%206.7-15.3%2014.1-22%2022.1-6.7%207.9-12.6%2016.5-17.8%2025.5zm663.3%20154.4c2.4%201.4%204.6%203%206.6%205%201.9%201.9%203.6%204.1%205%206.5%201.3%202.4%202.4%205%203.1%207.6.6%202.7%201%205.4.9%208.2v227.1c32.1-11.8%2060.1-32.5%2080.8-59.7%2020.8-27.2%2033.3-59.7%2036.2-93.7s-3.9-68.2-19.7-98.5-39.9-55.5-69.5-72.5l-193.1-111.6q-.3-.1-.7-.2h-.7q-.3.1-.7.2-.3.1-.6.3l-80.6%2046.6%20233.2%20134.7zm80.5-121h-.1v.1zm-.1-.1c5.8-33.6%201.9-68.2-11.3-99.7-13.1-31.5-35-58.6-63-78.2-28-19.5-61-30.7-95.1-32.2-34.2-1.4-68%206.9-97.6%2023.9l-193.1%20111.5q-.3.2-.5.5l-.4.6q-.1.3-.2.7-.1.3-.1.7v93.2l233.2-134.7c2.4-1.4%205-2.4%207.6-3.2%202.7-.7%205.4-1%208.1-1%202.8%200%205.5.3%208.2%201%202.6.8%205.1%201.8%207.5%203.2l191.1%20110.4c1.7%201%204.2%202.4%205.6%203.3zm-505.3-103.2c0-2.7.4-5.4%201.1-8.1.7-2.6%201.7-5.2%203.1-7.6%201.4-2.3%203-4.5%205-6.5%201.9-1.9%204.1-3.6%206.5-4.9l191.1-110.3c1.8-1.1%204.3-2.5%205.7-3.2-26.2-21.9-58.2-35.9-92.1-40.2-33.9-4.4-68.3%201-99.2%2015.5-31%2014.5-57.2%2037.6-75.5%2066.4-18.3%2028.9-28%2062.3-28%2096.5v223q.1.4.2.7.1.3.3.6.2.3.5.6.2.2.6.4l80.7%2046.6zm43.8%20294.7%20103.9%2060%20103.9-60v-119.9l-103.8-60-103.9%2060z'/%3e%3c/svg%3e";
863
+ const DeepSeekLogoSvg = "data:image/svg+xml,%3csvg%20fill='none'%20height='1320'%20viewBox='3.771%206.973%2023.993%2017.652'%20width='2500'%20xmlns='http://www.w3.org/2000/svg'%3e%3cpath%20d='m27.501%208.469c-.252-.123-.36.111-.508.23-.05.04-.093.09-.135.135-.368.395-.797.652-1.358.621-.821-.045-1.521.213-2.14.842-.132-.776-.57-1.238-1.235-1.535-.349-.155-.701-.309-.944-.645-.171-.238-.217-.504-.303-.765-.054-.159-.108-.32-.29-.348-.197-.031-.274.135-.352.273-.31.567-.43%201.192-.419%201.825.028%201.421.628%202.554%201.82%203.36.136.093.17.186.128.321-.081.278-.178.547-.264.824-.054.178-.135.217-.324.14a5.448%205.448%200%200%201%20-1.719-1.169c-.848-.82-1.614-1.726-2.57-2.435-.225-.166-.449-.32-.681-.467-.976-.95.128-1.729.383-1.82.267-.096.093-.428-.77-.424s-1.653.293-2.659.677a2.782%202.782%200%200%201%20-.46.135%209.554%209.554%200%200%200%20-2.853-.1c-1.866.21-3.356%201.092-4.452%202.6-1.315%201.81-1.625%203.87-1.246%206.018.399%202.261%201.552%204.136%203.326%205.601%201.837%201.518%203.955%202.262%206.37%202.12%201.466-.085%203.1-.282%204.942-1.842.465.23.952.322%201.762.392.623.059%201.223-.031%201.687-.127.728-.154.677-.828.414-.953-2.132-.994-1.665-.59-2.09-.916%201.084-1.285%202.717-2.619%203.356-6.94.05-.343.007-.558%200-.837-.004-.168.034-.235.228-.254a4.084%204.084%200%200%200%201.529-.47c1.382-.757%201.938-1.997%202.07-3.485.02-.227-.004-.463-.243-.582zm-12.041%2013.391c-2.067-1.627-3.07-2.162-3.483-2.138-.387.021-.318.465-.233.754.089.285.205.482.368.732.113.166.19.414-.112.598-.666.414-1.823-.139-1.878-.166-1.347-.793-2.473-1.842-3.267-3.276-.765-1.38-1.21-2.861-1.284-4.441-.02-.383.093-.518.472-.586a4.692%204.692%200%200%201%201.514-.04c2.109.31%203.905%201.255%205.41%202.749.86.853%201.51%201.871%202.18%202.865.711%201.057%201.478%202.063%202.454%202.887.343.289.619.51.881.672-.792.088-2.117.107-3.022-.61zm.99-6.38a.304.304%200%201%201%20.609%200c0%20.17-.136.304-.306.304a.3.3%200%200%201%20-.303-.305zm3.077%201.581c-.197.08-.394.15-.584.159a1.246%201.246%200%200%201%20-.79-.252c-.27-.227-.463-.354-.546-.752a1.752%201.752%200%200%201%20.016-.582c.07-.324-.008-.531-.235-.72-.187-.155-.422-.196-.682-.196a.551.551%200%200%201%20-.252-.078c-.108-.055-.197-.19-.112-.356.027-.053.159-.183.19-.207.352-.201.758-.135%201.134.016.349.142.611.404.99.773.388.448.457.573.678.906.174.264.333.534.441.842.066.192-.02.35-.248.448z'%20fill='%234d6bfe'/%3e%3c/svg%3e";
864
+ const MiniMaxLogoSvg = "/assets/minimax-BPMzvuL-.jpeg";
865
+ const AlibabaLogoSvg = "/assets/alibaba-TTwafVwX.svg";
866
+ const QwenLogoSvg = "/assets/qwen-CONDcHqt.png";
867
+ const ZhipuAILogoSvg = "/assets/zhipuai-BPNAnxo-.svg";
868
+ const PROVIDER_MAP = {
869
+ "claude-": "anthropic",
870
+ "gpt-": "openai",
871
+ "o1-": "openai",
872
+ "o3-": "openai",
873
+ "deepseek-": "deepseek",
874
+ MiniMax: "minimax",
875
+ qwen: "qwen",
876
+ "glm-": "zhipuai"
877
+ };
878
+ function detectProvider(model) {
879
+ if (model === null) return "unknown";
880
+ for (const [prefix, provider] of Object.entries(PROVIDER_MAP)) {
881
+ if (model.startsWith(prefix)) {
882
+ return provider;
883
+ }
884
+ }
885
+ return "unknown";
886
+ }
887
+ const sizeStyle = { width: 24, height: 24, objectFit: "contain" };
888
+ const AnthropicLogo = React.memo(
889
+ ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: AnthropicLogoSvg, alt: "Anthropic", className, style: sizeStyle })
890
+ );
891
+ const OpenAILogo = React.memo(
892
+ ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: OpenAILogoSvg, alt: "OpenAI", className, style: sizeStyle })
893
+ );
894
+ const DeepSeekLogo = React.memo(
895
+ ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: DeepSeekLogoSvg, alt: "DeepSeek", className, style: sizeStyle })
896
+ );
897
+ const MiniMaxLogo = React.memo(
898
+ ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: MiniMaxLogoSvg, alt: "MiniMax", className, style: sizeStyle })
899
+ );
900
+ const AlibabaLogo = React.memo(
901
+ ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: AlibabaLogoSvg, alt: "Alibaba", className, style: sizeStyle })
902
+ );
903
+ const QwenLogo = React.memo(
904
+ ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: QwenLogoSvg, alt: "Qwen", className, style: sizeStyle })
905
+ );
906
+ const ZhipuAILogo = React.memo(
907
+ ({ className }) => /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: ZhipuAILogoSvg, alt: "ZhipuAI", className, style: sizeStyle })
908
+ );
909
+ function ProviderLogo({
910
+ provider,
911
+ className
912
+ }) {
913
+ switch (provider) {
914
+ case "anthropic":
915
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(AnthropicLogo, { className });
916
+ case "openai":
917
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(OpenAILogo, { className });
918
+ case "deepseek":
919
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(DeepSeekLogo, { className });
920
+ case "minimax":
921
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(MiniMaxLogo, { className });
922
+ case "alibaba":
923
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(AlibabaLogo, { className });
924
+ case "qwen":
925
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(QwenLogo, { className });
926
+ case "zhipuai":
927
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ZhipuAILogo, { className });
928
+ case "unknown":
929
+ return null;
930
+ }
931
+ }
932
+ const API_FORMAT_LABELS = {
933
+ anthropic: "Anthropic",
934
+ openai: "OpenAI",
935
+ unknown: "Unknown"
936
+ };
937
+ function formatElapsed(ms) {
938
+ if (ms < 1e3) return `${ms}ms`;
939
+ return `${(ms / 1e3).toFixed(1)}s`;
940
+ }
941
+ const STATUS_BADGE_CLASSES = {
942
+ success: "bg-emerald-500/15 text-emerald-400 border-emerald-500/25",
943
+ client_error: "bg-amber-500/15 text-amber-400 border-amber-500/25",
944
+ server_error: "",
945
+ pending: "bg-muted text-muted-foreground border-border"
946
+ };
947
+ function LogEntryHeader({
948
+ log,
949
+ parsedRequest,
950
+ expanded,
951
+ onToggle
952
+ }) {
953
+ const statusCategory = getStatusCategory(log.responseStatus);
954
+ const hasTokens = log.inputTokens !== null || log.outputTokens !== null;
955
+ const messageCount = parsedRequest !== null ? parsedRequest.messages.length : null;
956
+ const toolCount = parsedRequest !== null && parsedRequest.tools !== void 0 && parsedRequest.tools.length > 0 ? parsedRequest.tools.length : null;
957
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
958
+ "div",
959
+ {
960
+ role: "button",
961
+ tabIndex: 0,
962
+ className: cn(
963
+ "flex items-center gap-2.5 px-3 py-2 cursor-pointer transition-colors",
964
+ "hover:bg-muted/50",
965
+ "select-none"
966
+ ),
967
+ onClick: onToggle,
968
+ onKeyDown: (e) => {
969
+ if (e.key === "Enter" || e.key === " ") {
970
+ e.preventDefault();
971
+ onToggle();
972
+ }
973
+ },
974
+ children: [
975
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400/80 font-mono text-xs font-semibold tabular-nums shrink-0", children: [
976
+ "#",
977
+ log.id
978
+ ] }),
979
+ log.model !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
980
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderLogo, { provider: detectProvider(log.model), className: "size-4 shrink-0" }),
981
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "secondary", className: "text-[10px] px-1.5 py-0 h-5 font-mono", children: log.model })
982
+ ] }),
983
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
984
+ Badge,
985
+ {
986
+ variant: "outline",
987
+ className: cn(
988
+ "text-[10px] px-1.5 py-0 h-5 font-mono",
989
+ log.apiFormat === "openai" && "border-blue-500/40 text-blue-400",
990
+ log.apiFormat === "anthropic" && "border-orange-500/40 text-orange-400",
991
+ log.apiFormat === "unknown" && "border-muted text-muted-foreground"
992
+ ),
993
+ children: API_FORMAT_LABELS[log.apiFormat] ?? log.apiFormat
994
+ }
995
+ ),
996
+ statusCategory === "server_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "destructive", className: "text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums", children: log.responseStatus }) : statusCategory === "pending" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
997
+ Badge,
998
+ {
999
+ variant: "outline",
1000
+ className: cn(
1001
+ "text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums",
1002
+ STATUS_BADGE_CLASSES[statusCategory]
1003
+ ),
1004
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" })
1005
+ }
1006
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
1007
+ Badge,
1008
+ {
1009
+ variant: "outline",
1010
+ className: cn(
1011
+ "text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums",
1012
+ STATUS_BADGE_CLASSES[statusCategory]
1013
+ ),
1014
+ children: log.responseStatus
1015
+ }
1016
+ ),
1017
+ log.elapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
1018
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
1019
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed(log.elapsedMs) })
1020
+ ] }),
1021
+ hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
1022
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
1023
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
1024
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: log.inputTokens !== null ? "text-blue-400" : "text-muted-foreground", children: [
1025
+ "IN ",
1026
+ log.inputTokens !== null ? formatTokens(log.inputTokens) : "—"
1027
+ ] }),
1028
+ " / ",
1029
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1030
+ "span",
1031
+ {
1032
+ className: log.outputTokens !== null ? "text-amber-400" : "text-muted-foreground",
1033
+ children: [
1034
+ "OUT ",
1035
+ log.outputTokens !== null ? formatTokens(log.outputTokens) : "—"
1036
+ ]
1037
+ }
1038
+ )
1039
+ ] })
1040
+ ] }),
1041
+ log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1 text-xs shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
1042
+ "Cache +",
1043
+ formatTokens(log.cacheCreationInputTokens)
1044
+ ] }) }),
1045
+ log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-center gap-1 text-xs shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
1046
+ "Cache ~",
1047
+ formatTokens(log.cacheReadInputTokens)
1048
+ ] }) }),
1049
+ messageCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
1050
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3" }),
1051
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: messageCount })
1052
+ ] }),
1053
+ toolCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
1054
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
1055
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: toolCount })
1056
+ ] }),
1057
+ log.origin !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1058
+ "span",
1059
+ {
1060
+ className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0",
1061
+ title: `Origin: ${log.origin}`,
1062
+ children: [
1063
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "size-3" }),
1064
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums truncate max-w-[120px]", title: log.origin, children: log.origin })
1065
+ ]
1066
+ }
1067
+ ),
1068
+ log.userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1069
+ "span",
1070
+ {
1071
+ className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0",
1072
+ title: `User-Agent: ${log.userAgent}`,
1073
+ children: [
1074
+ /* @__PURE__ */ jsxRuntimeExports.jsx(User, { className: "size-3" }),
1075
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums truncate max-w-[150px]", title: log.userAgent, children: log.userAgent })
1076
+ ]
1077
+ }
1078
+ ),
1079
+ (log.clientPid !== null || log.clientProjectFolder !== null) && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1080
+ "span",
1081
+ {
1082
+ className: "flex items-center gap-1 text-purple-400/80 text-xs shrink-0",
1083
+ title: log.clientCwd !== null ? `PID: ${log.clientPid ?? "?"} | CWD: ${log.clientCwd}` : `PID: ${log.clientPid ?? "?"}`,
1084
+ children: [
1085
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FileTerminal, { className: "size-3" }),
1086
+ log.clientProjectFolder !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: log.clientProjectFolder }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
1087
+ "PID ",
1088
+ log.clientPid
1089
+ ] })
1090
+ ]
1091
+ }
1092
+ ),
1093
+ log.streaming && /* @__PURE__ */ jsxRuntimeExports.jsx(Radio, { className: "size-3 text-muted-foreground/60 shrink-0" }),
1094
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" }),
1095
+ expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 text-muted-foreground shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 text-muted-foreground shrink-0" })
1096
+ ]
1097
+ }
1098
+ );
1099
+ }
1100
+ function Dialog({
1101
+ ...props
1102
+ }) {
1103
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Root, { "data-slot": "dialog", ...props });
1104
+ }
1105
+ function DialogTrigger({
1106
+ ...props
1107
+ }) {
1108
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$1, { "data-slot": "dialog-trigger", ...props });
1109
+ }
1110
+ function DialogPortal({
1111
+ ...props
1112
+ }) {
1113
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$1, { "data-slot": "dialog-portal", ...props });
1114
+ }
1115
+ function DialogOverlay({
1116
+ className,
1117
+ ...props
1118
+ }) {
1119
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1120
+ Overlay,
1121
+ {
1122
+ "data-slot": "dialog-overlay",
1123
+ className: cn(
1124
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
1125
+ className
1126
+ ),
1127
+ ...props
1128
+ }
1129
+ );
1130
+ }
1131
+ function DialogContent({
1132
+ className,
1133
+ children,
1134
+ ...props
1135
+ }) {
1136
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogPortal, { children: [
1137
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogOverlay, {}),
1138
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1139
+ Content,
1140
+ {
1141
+ "data-slot": "dialog-content",
1142
+ className: cn(
1143
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg",
1144
+ className
1145
+ ),
1146
+ ...props,
1147
+ children: [
1148
+ children,
1149
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [
1150
+ /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-4" }),
1151
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Close" })
1152
+ ] })
1153
+ ]
1154
+ }
1155
+ )
1156
+ ] });
1157
+ }
1158
+ function DialogHeader({ className, ...props }) {
1159
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1160
+ "div",
1161
+ {
1162
+ "data-slot": "dialog-header",
1163
+ className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className),
1164
+ ...props
1165
+ }
1166
+ );
1167
+ }
1168
+ function DialogTitle({
1169
+ className,
1170
+ ...props
1171
+ }) {
1172
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1173
+ Title,
1174
+ {
1175
+ "data-slot": "dialog-title",
1176
+ className: cn("text-lg font-semibold leading-none tracking-tight", className),
1177
+ ...props
1178
+ }
1179
+ );
1180
+ }
1181
+ function Separator({
1182
+ className,
1183
+ orientation = "horizontal",
1184
+ decorative = true,
1185
+ ...props
1186
+ }) {
1187
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1188
+ Root$1,
1189
+ {
1190
+ "data-slot": "separator",
1191
+ decorative,
1192
+ orientation,
1193
+ className: cn(
1194
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
1195
+ className
1196
+ ),
1197
+ ...props
1198
+ }
1199
+ );
1200
+ }
1201
+ function Collapsible({
1202
+ ...props
1203
+ }) {
1204
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Root$2, { "data-slot": "collapsible", ...props });
1205
+ }
1206
+ function CollapsibleTrigger({
1207
+ ...props
1208
+ }) {
1209
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleTrigger$1, { "data-slot": "collapsible-trigger", ...props });
1210
+ }
1211
+ function CollapsibleContent({
1212
+ ...props
1213
+ }) {
1214
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent$1, { "data-slot": "collapsible-content", ...props });
1215
+ }
1216
+ function ScrollArea({
1217
+ className,
1218
+ children,
1219
+ ...props
1220
+ }) {
1221
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1222
+ Root$3,
1223
+ {
1224
+ "data-slot": "scroll-area",
1225
+ className: cn("relative", className),
1226
+ ...props,
1227
+ children: [
1228
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1229
+ Viewport$1,
1230
+ {
1231
+ "data-slot": "scroll-area-viewport",
1232
+ className: "focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1",
1233
+ children
1234
+ }
1235
+ ),
1236
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollBar, {}),
1237
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Corner, {})
1238
+ ]
1239
+ }
1240
+ );
1241
+ }
1242
+ function ScrollBar({
1243
+ className,
1244
+ orientation = "vertical",
1245
+ ...props
1246
+ }) {
1247
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1248
+ ScrollAreaScrollbar,
1249
+ {
1250
+ "data-slot": "scroll-area-scrollbar",
1251
+ orientation,
1252
+ className: cn(
1253
+ "flex touch-none p-px transition-colors select-none",
1254
+ orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent",
1255
+ orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent",
1256
+ className
1257
+ ),
1258
+ ...props,
1259
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1260
+ ScrollAreaThumb,
1261
+ {
1262
+ "data-slot": "scroll-area-thumb",
1263
+ className: "bg-border relative flex-1 rounded-full"
1264
+ }
1265
+ )
1266
+ }
1267
+ );
1268
+ }
1269
+ function assertNever(_value) {
1270
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {});
1271
+ }
1272
+ function SystemReminderBlock({ text }) {
1273
+ const [open, setOpen] = reactExports.useState(false);
1274
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Collapsible, { open, onOpenChange: setOpen, children: [
1275
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(CollapsibleTrigger, { className: "flex items-center gap-1.5 py-0.5 cursor-pointer hover:opacity-80 transition-opacity group", children: [
1276
+ open ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }),
1277
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground text-xs italic select-none opacity-60", children: "[system-reminder]" })
1278
+ ] }),
1279
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-4 pt-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) }) }) })
1280
+ ] });
1281
+ }
1282
+ function TextBlock({ text }) {
1283
+ if (text.includes("<system-reminder>")) {
1284
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SystemReminderBlock, { text });
1285
+ }
1286
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) });
1287
+ }
1288
+ function ThinkingBlock({ thinking }) {
1289
+ const [open, setOpen] = reactExports.useState(false);
1290
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-l-2 border-purple-500/40 my-1", children: [
1291
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(CollapsibleTrigger, { className: "flex items-center gap-1.5 px-3 py-1 w-full text-left cursor-pointer hover:bg-purple-500/5 transition-colors rounded-r-sm group", children: [
1292
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5 text-purple-400 shrink-0" }),
1293
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-medium text-purple-400", children: "Thinking" }),
1294
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1295
+ Badge,
1296
+ {
1297
+ variant: "ghost",
1298
+ className: "text-[10px] text-muted-foreground px-1.5 py-0 h-4 font-mono",
1299
+ children: [
1300
+ thinking.length.toLocaleString(),
1301
+ " chars"
1302
+ ]
1303
+ }
1304
+ ),
1305
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
1306
+ open ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" })
1307
+ ] }),
1308
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "max-h-[60vh]", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono leading-relaxed", children: thinking }) }) }) })
1309
+ ] }) });
1310
+ }
1311
+ function ToolUseBlock({
1312
+ name,
1313
+ input
1314
+ }) {
1315
+ const [open, setOpen] = reactExports.useState(false);
1316
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-l-2 border-blue-500/40 my-1", children: [
1317
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(CollapsibleTrigger, { className: "flex items-center gap-1.5 px-3 py-1 w-full text-left cursor-pointer hover:bg-blue-500/5 transition-colors rounded-r-sm group", children: [
1318
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { className: "size-3.5 text-blue-400 shrink-0" }),
1319
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "text-[10px] font-mono px-1.5 py-0 h-4", children: name }),
1320
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
1321
+ open ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" })
1322
+ ] }),
1323
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "max-h-[60vh]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewer, { data: safeJsonValue(input), defaultExpandDepth: 2 }) }) }) })
1324
+ ] }) });
1325
+ }
1326
+ function ResponseContentBlockRenderer({
1327
+ block
1328
+ }) {
1329
+ switch (block.type) {
1330
+ case "text":
1331
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TextBlock, { text: block.text });
1332
+ case "thinking":
1333
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ThinkingBlock, { thinking: block.thinking });
1334
+ case "tool_use":
1335
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ToolUseBlock, { name: block.name, input: block.input });
1336
+ default:
1337
+ return assertNever();
1338
+ }
1339
+ }
1340
+ function StructuredResponseViewAnthropic({
1341
+ response
1342
+ }) {
1343
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
1344
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
1345
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "secondary", className: "text-[10px] px-1.5 py-0 h-5 font-mono", children: response.model }),
1346
+ response.stop_reason !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1347
+ Badge,
1348
+ {
1349
+ variant: "outline",
1350
+ className: "text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1",
1351
+ children: [
1352
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleStop, { className: "size-2.5" }),
1353
+ response.stop_reason
1354
+ ]
1355
+ }
1356
+ ),
1357
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs", children: [
1358
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3" }),
1359
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
1360
+ formatTokens(response.usage.input_tokens),
1361
+ " in /",
1362
+ " ",
1363
+ formatTokens(response.usage.output_tokens),
1364
+ " out"
1365
+ ] }),
1366
+ response.usage.cache_creation_input_tokens !== void 0 && response.usage.cache_creation_input_tokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
1367
+ "Cache +",
1368
+ formatTokens(response.usage.cache_creation_input_tokens)
1369
+ ] }),
1370
+ response.usage.cache_read_input_tokens !== void 0 && response.usage.cache_read_input_tokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
1371
+ "Cache ~",
1372
+ formatTokens(response.usage.cache_read_input_tokens)
1373
+ ] })
1374
+ ] })
1375
+ ] }),
1376
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Separator, { className: "opacity-50" }),
1377
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1378
+ response.content.map((block, i) => /* @__PURE__ */ jsxRuntimeExports.jsx(ResponseContentBlockRenderer, { block }, i)),
1379
+ response.content.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "Empty response content" })
1380
+ ] })
1381
+ ] });
1382
+ }
1383
+ function OpenAIResponseView({ response }) {
1384
+ const choice = response.choices[0];
1385
+ const message = choice?.message;
1386
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3", children: [
1387
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
1388
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "secondary", className: "text-[10px] px-1.5 py-0 h-5 font-mono", children: response.model }),
1389
+ choice?.finish_reason !== null && choice?.finish_reason !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1390
+ Badge,
1391
+ {
1392
+ variant: "outline",
1393
+ className: "text-[10px] px-1.5 py-0 h-5 font-mono flex items-center gap-1",
1394
+ children: [
1395
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleStop, { className: "size-2.5" }),
1396
+ choice.finish_reason
1397
+ ]
1398
+ }
1399
+ ),
1400
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs", children: [
1401
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3" }),
1402
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
1403
+ formatTokens(response.usage.prompt_tokens),
1404
+ " in /",
1405
+ " ",
1406
+ formatTokens(response.usage.completion_tokens),
1407
+ " out"
1408
+ ] })
1409
+ ] })
1410
+ ] }),
1411
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Separator, { className: "opacity-50" }),
1412
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1413
+ message?.reasoning_content !== null && message?.reasoning_content !== void 0 && message.reasoning_content.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-purple-500/30 rounded-md p-3 bg-purple-500/5", children: [
1414
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-purple-400 font-mono mb-1", children: "thinking" }),
1415
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-purple-200 whitespace-pre-wrap", children: message.reasoning_content })
1416
+ ] }),
1417
+ message?.content !== null && message?.content !== void 0 && message.content.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: message.content }) }),
1418
+ message?.function_call !== null && message?.function_call !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-blue-500/30 rounded-md p-3 bg-blue-500/5", children: [
1419
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-blue-400 font-mono mb-1", children: "function_call" }),
1420
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono text-xs", children: [
1421
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-300", children: message.function_call.name }),
1422
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
1423
+ "(",
1424
+ message.function_call.arguments,
1425
+ ")"
1426
+ ] })
1427
+ ] })
1428
+ ] }),
1429
+ (message?.content === null || message?.content === void 0 || message.content.length === 0) && (message?.reasoning_content === null || message?.reasoning_content === void 0 || message.reasoning_content.length === 0) && (message?.function_call === null || message?.function_call === void 0) && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "Empty response content" })
1430
+ ] })
1431
+ ] });
1432
+ }
1433
+ function isOpenAIResponse(resp) {
1434
+ if (typeof resp !== "object" || resp === null) return false;
1435
+ const desc = Object.getOwnPropertyDescriptor(resp, "object");
1436
+ return desc?.value === "chat.completion";
1437
+ }
1438
+ function formatViewFor(apiFormat, response) {
1439
+ if (isOpenAIResponse(response)) {
1440
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(OpenAIResponseView, { response });
1441
+ }
1442
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(StructuredResponseViewAnthropic, { response });
1443
+ }
1444
+ function getStatusClasses(category) {
1445
+ switch (category) {
1446
+ case "success":
1447
+ return "text-emerald-400";
1448
+ case "client_error":
1449
+ return "text-amber-400";
1450
+ case "server_error":
1451
+ return "text-red-400";
1452
+ case "pending":
1453
+ return "text-muted-foreground";
1454
+ }
1455
+ }
1456
+ function parseResponse(text, apiFormat) {
1457
+ try {
1458
+ const json = JSON.parse(text);
1459
+ if (apiFormat === "openai") {
1460
+ const result2 = parseOpenAIResponse(text);
1461
+ if (result2) return result2;
1462
+ }
1463
+ const result = InspectorResponseSchema.safeParse(json);
1464
+ if (result.success) return result.data;
1465
+ return null;
1466
+ } catch {
1467
+ return null;
1468
+ }
1469
+ }
1470
+ function StatusIndicator({ status }) {
1471
+ const category = getStatusCategory(status);
1472
+ const classes = getStatusClasses(category);
1473
+ if (status === null) {
1474
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground italic", children: "pending" });
1475
+ }
1476
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn("flex items-center gap-1 text-xs font-mono font-semibold", classes), children: [
1477
+ category === "server_error" && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3" }),
1478
+ status
1479
+ ] });
1480
+ }
1481
+ function ErrorResponseView({ text }) {
1482
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md border border-red-500/30 bg-red-500/5 p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs text-red-300 whitespace-pre-wrap font-mono leading-relaxed overflow-auto max-h-[60vh]", children: text }) });
1483
+ }
1484
+ function MarkdownFallbackView({ text }) {
1485
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "prose prose-sm dark:prose-invert max-w-none [&_pre]:bg-muted [&_pre]:text-foreground [&_code]:text-[0.8em] [&_p]:my-1 [&_ul]:my-1 [&_ol]:my-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) });
1486
+ }
1487
+ function ResponseView({
1488
+ responseText,
1489
+ responseStatus,
1490
+ streaming,
1491
+ inputTokens,
1492
+ outputTokens,
1493
+ cacheCreationInputTokens,
1494
+ cacheReadInputTokens,
1495
+ apiFormat
1496
+ }) {
1497
+ if (responseText === null) {
1498
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 py-3", children: [
1499
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
1500
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground italic", children: "No response" })
1501
+ ] });
1502
+ }
1503
+ const isError = responseStatus !== null && responseStatus >= 400;
1504
+ if (isError) {
1505
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1506
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
1507
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorResponseView, { text: responseText })
1508
+ ] });
1509
+ }
1510
+ const parsed = parseResponse(responseText, apiFormat);
1511
+ if (parsed !== null) {
1512
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1513
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
1514
+ formatViewFor(apiFormat, parsed)
1515
+ ] });
1516
+ }
1517
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
1518
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1519
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
1520
+ streaming && (inputTokens !== null || outputTokens !== null) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs", children: [
1521
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3" }),
1522
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
1523
+ inputTokens !== null ? formatTokens(inputTokens) : "—",
1524
+ " in /",
1525
+ " ",
1526
+ outputTokens !== null ? formatTokens(outputTokens) : "—",
1527
+ " out"
1528
+ ] }),
1529
+ cacheCreationInputTokens !== null && cacheCreationInputTokens !== void 0 && cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
1530
+ "Cache +",
1531
+ formatTokens(cacheCreationInputTokens)
1532
+ ] }),
1533
+ cacheReadInputTokens !== null && cacheReadInputTokens !== void 0 && cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
1534
+ "Cache ~",
1535
+ formatTokens(cacheReadInputTokens)
1536
+ ] })
1537
+ ] })
1538
+ ] }),
1539
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MarkdownFallbackView, { text: responseText })
1540
+ ] });
1541
+ }
1542
+ const ReplayResultSchema = object({
1543
+ success: boolean(),
1544
+ error: string().optional(),
1545
+ responseStatus: number().optional(),
1546
+ responseText: string().optional(),
1547
+ inputTokens: number().optional(),
1548
+ outputTokens: number().optional(),
1549
+ elapsedMs: number().optional(),
1550
+ streaming: boolean().optional()
1551
+ });
1552
+ function ReplayDialog({ log, open, onOpenChange }) {
1553
+ const [modifiedBody, setModifiedBody] = reactExports.useState(() => {
1554
+ return log.rawRequestBody ?? "{}";
1555
+ });
1556
+ const [replayResult, setReplayResult] = reactExports.useState(null);
1557
+ const [loading, setLoading] = reactExports.useState(false);
1558
+ const [error, setError] = reactExports.useState(null);
1559
+ async function handleReplay() {
1560
+ setLoading(true);
1561
+ setError(null);
1562
+ setReplayResult(null);
1563
+ try {
1564
+ const res = await fetch(`/api/logs/${log.id}/replay`, {
1565
+ method: "POST",
1566
+ headers: { "Content-Type": "application/json" },
1567
+ body: JSON.stringify({ modifiedBody })
1568
+ });
1569
+ const json = await res.json();
1570
+ const parsed = ReplayResultSchema.safeParse(json);
1571
+ if (!parsed.success) {
1572
+ setError("Invalid response from server");
1573
+ setLoading(false);
1574
+ return;
1575
+ }
1576
+ const data = parsed.data;
1577
+ setReplayResult(data);
1578
+ if (!data.success) {
1579
+ setError(data.error ?? "Replay failed");
1580
+ }
1581
+ } catch (err) {
1582
+ setError(err instanceof Error ? err.message : "Network error");
1583
+ } finally {
1584
+ setLoading(false);
1585
+ }
1586
+ }
1587
+ function handleClose() {
1588
+ setReplayResult(null);
1589
+ setError(null);
1590
+ onOpenChange(false);
1591
+ }
1592
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-4xl max-h-[85vh] overflow-auto", children: [
1593
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
1594
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { className: "size-4" }),
1595
+ "Replay Request #",
1596
+ log.id
1597
+ ] }) }),
1598
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "modified", children: [
1599
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
1600
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "modified", children: "Modified Request" }),
1601
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "original", children: "Original Response" }),
1602
+ replayResult && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "replay", children: "Replay Response" })
1603
+ ] }),
1604
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsContent, { value: "modified", className: "space-y-4", children: [
1605
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1606
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-sm font-medium mb-2 block", children: "Request Body (JSON)" }),
1607
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1608
+ "textarea",
1609
+ {
1610
+ className: "w-full h-64 p-3 font-mono text-xs bg-muted rounded-md border border-input resize-none focus:outline-none focus:ring-2 focus:ring-ring",
1611
+ value: modifiedBody,
1612
+ onChange: (e) => setModifiedBody(e.target.value),
1613
+ spellCheck: false
1614
+ }
1615
+ )
1616
+ ] }),
1617
+ error !== null && error !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm text-destructive bg-destructive/10 px-3 py-2 rounded-md", children: error }),
1618
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1619
+ Button,
1620
+ {
1621
+ onClick: () => {
1622
+ void handleReplay();
1623
+ },
1624
+ disabled: loading,
1625
+ children: loading ? "Replaying..." : "Replay"
1626
+ }
1627
+ ) }),
1628
+ replayResult && replayResult.success && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "parsed", children: [
1629
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
1630
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Parsed Response" }),
1631
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" })
1632
+ ] }),
1633
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1634
+ ResponseView,
1635
+ {
1636
+ responseText: replayResult.responseText ?? null,
1637
+ responseStatus: replayResult.responseStatus ?? null,
1638
+ streaming: replayResult.streaming ?? false,
1639
+ inputTokens: replayResult.inputTokens ?? null,
1640
+ outputTokens: replayResult.outputTokens ?? null,
1641
+ apiFormat: log.apiFormat
1642
+ }
1643
+ ) }),
1644
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap bg-muted p-3 rounded-md max-h-96 overflow-auto", children: replayResult.responseText ?? "No response" }) })
1645
+ ] })
1646
+ ] }),
1647
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "original", children: log.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "parsed", children: [
1648
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
1649
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Parsed Response" }),
1650
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" })
1651
+ ] }),
1652
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1653
+ ResponseView,
1654
+ {
1655
+ responseText: log.responseText,
1656
+ responseStatus: log.responseStatus,
1657
+ streaming: log.streaming,
1658
+ inputTokens: log.inputTokens,
1659
+ outputTokens: log.outputTokens,
1660
+ cacheCreationInputTokens: log.cacheCreationInputTokens,
1661
+ cacheReadInputTokens: log.cacheReadInputTokens,
1662
+ apiFormat: log.apiFormat
1663
+ }
1664
+ ) }),
1665
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap bg-muted p-3 rounded-md max-h-96 overflow-auto", children: log.responseText }) })
1666
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground italic", children: "No original response" }) }),
1667
+ replayResult && replayResult.success && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "replay", children: replayResult.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "parsed", children: [
1668
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
1669
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Parsed Response" }),
1670
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" })
1671
+ ] }),
1672
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1673
+ ResponseView,
1674
+ {
1675
+ responseText: replayResult.responseText ?? null,
1676
+ responseStatus: replayResult.responseStatus ?? null,
1677
+ streaming: replayResult.streaming ?? false,
1678
+ inputTokens: replayResult.inputTokens ?? null,
1679
+ outputTokens: replayResult.outputTokens ?? null,
1680
+ apiFormat: log.apiFormat
1681
+ }
1682
+ ) }),
1683
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap bg-muted p-3 rounded-md max-h-96 overflow-auto", children: replayResult.responseText }) })
1684
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground italic", children: "No replay response" }) })
1685
+ ] })
1686
+ ] }) });
1687
+ }
1688
+ function StreamingChunkSequence({
1689
+ logId,
1690
+ truncated
1691
+ }) {
1692
+ const [containerExpanded, setContainerExpanded] = reactExports.useState(false);
1693
+ const [chunkState, setChunkState] = reactExports.useState({ status: "idle" });
1694
+ const [expandedIndices, setExpandedIndices] = reactExports.useState(/* @__PURE__ */ new Set());
1695
+ reactExports.useEffect(() => {
1696
+ if (!containerExpanded || chunkState.status !== "idle") return;
1697
+ setChunkState({ status: "loading" });
1698
+ fetch(`/api/logs/${logId}/chunks`).then((res) => {
1699
+ if (!res.ok) {
1700
+ return Promise.reject(new Error("Chunks not found"));
1701
+ }
1702
+ return res.json();
1703
+ }).then((data) => {
1704
+ setChunkState({ status: "success", chunks: data.chunks });
1705
+ }).catch(() => {
1706
+ setChunkState({ status: "error", message: "Chunk data unavailable" });
1707
+ });
1708
+ }, [containerExpanded, logId, chunkState.status]);
1709
+ const groups = reactExports.useMemo(() => {
1710
+ if (chunkState.status !== "success") return [];
1711
+ const map = /* @__PURE__ */ new Map();
1712
+ for (const chunk of chunkState.chunks) {
1713
+ const existing = map.get(chunk.index);
1714
+ if (existing) {
1715
+ existing.push(chunk);
1716
+ } else {
1717
+ map.set(chunk.index, [chunk]);
1718
+ }
1719
+ }
1720
+ return Array.from(map.entries()).map(([index, chunks]) => ({ index, chunks })).sort((a, b) => a.index - b.index);
1721
+ }, [chunkState]);
1722
+ const toggleIndex = (index) => {
1723
+ setExpandedIndices((prev) => {
1724
+ const next = new Set(prev);
1725
+ if (next.has(index)) {
1726
+ next.delete(index);
1727
+ } else {
1728
+ next.add(index);
1729
+ }
1730
+ return next;
1731
+ });
1732
+ };
1733
+ function renderBody() {
1734
+ if (chunkState.status === "idle" || chunkState.status === "loading") {
1735
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 py-2 text-xs text-muted-foreground", children: [
1736
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin" }),
1737
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Loading chunks..." })
1738
+ ] });
1739
+ }
1740
+ if (chunkState.status === "error") {
1741
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-2 text-xs text-muted-foreground italic", children: chunkState.message });
1742
+ }
1743
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-2 space-y-1", children: [
1744
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-[10px] text-muted-foreground font-mono mb-2", children: [
1745
+ groups.length,
1746
+ " index group",
1747
+ groups.length !== 1 ? "s" : "",
1748
+ " available"
1749
+ ] }),
1750
+ groups.map((group) => {
1751
+ const isExpanded = expandedIndices.has(group.index);
1752
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-border bg-background", children: [
1753
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1754
+ "button",
1755
+ {
1756
+ type: "button",
1757
+ className: "flex items-center gap-2 w-full px-2 py-1.5 text-left hover:bg-muted/50 transition-colors cursor-pointer",
1758
+ onClick: () => toggleIndex(group.index),
1759
+ children: [
1760
+ isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }),
1761
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
1762
+ "[",
1763
+ group.index,
1764
+ "] ",
1765
+ group.chunks[0]?.type ?? ""
1766
+ ] }),
1767
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
1768
+ group.chunks.length,
1769
+ " chunk",
1770
+ group.chunks.length !== 1 ? "s" : ""
1771
+ ] })
1772
+ ]
1773
+ }
1774
+ ),
1775
+ isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 pb-2 space-y-1", children: group.chunks.map((chunk) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-border bg-muted/20 p-2", children: [
1776
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
1777
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-muted-foreground font-mono", children: [
1778
+ "+",
1779
+ chunk.timestamp,
1780
+ "ms"
1781
+ ] }),
1782
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground font-mono", children: chunk.type })
1783
+ ] }),
1784
+ /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewer, { data: chunk, defaultExpandDepth: 1, showCopy: true })
1785
+ ] }, chunk.index)) })
1786
+ ] }, group.index);
1787
+ })
1788
+ ] });
1789
+ }
1790
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
1791
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1792
+ "button",
1793
+ {
1794
+ type: "button",
1795
+ className: "flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
1796
+ onClick: () => setContainerExpanded((v) => !v),
1797
+ children: [
1798
+ containerExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3" }),
1799
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Raw SSE Events" }),
1800
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Badge, { variant: "outline", className: "text-[9px] px-1 py-0 h-4 font-mono ml-1", children: [
1801
+ logId,
1802
+ truncated === true ? "+" : ""
1803
+ ] })
1804
+ ]
1805
+ }
1806
+ ),
1807
+ containerExpanded === true ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md border border-border bg-muted/20 overflow-auto max-h-64", children: renderBody() }) : null
1808
+ ] });
1809
+ }
1810
+ function CopyButton({
1811
+ text,
1812
+ label,
1813
+ copied,
1814
+ onCopy
1815
+ }) {
1816
+ if (text === null) return null;
1817
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
1818
+ "button",
1819
+ {
1820
+ type: "button",
1821
+ onClick: onCopy,
1822
+ className: "flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors px-2 py-1 rounded hover:bg-muted",
1823
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1824
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }),
1825
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-green-500", children: "Copied!" })
1826
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1827
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" }),
1828
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: label })
1829
+ ] })
1830
+ }
1831
+ );
1832
+ }
1833
+ function LogEntry({ log, viewMode = "simple" }) {
1834
+ const [expanded, setExpanded] = reactExports.useState(false);
1835
+ const [requestCopied, setRequestCopied] = reactExports.useState(false);
1836
+ const [responseCopied, setResponseCopied] = reactExports.useState(false);
1837
+ const [replayOpen, setReplayOpen] = reactExports.useState(false);
1838
+ const parsedRequest = reactExports.useMemo(() => parseRequest(log.rawRequestBody), [log.rawRequestBody]);
1839
+ function handleCopyRequest(e) {
1840
+ e.stopPropagation();
1841
+ if (log.rawRequestBody === null) return;
1842
+ void window.navigator.clipboard.writeText(log.rawRequestBody).then(() => {
1843
+ setRequestCopied(true);
1844
+ setTimeout(() => setRequestCopied(false), 2e3);
1845
+ });
1846
+ }
1847
+ function handleCopyResponse(e) {
1848
+ e.stopPropagation();
1849
+ if (log.responseText === null) return;
1850
+ void window.navigator.clipboard.writeText(log.responseText).then(() => {
1851
+ setResponseCopied(true);
1852
+ setTimeout(() => setResponseCopied(false), 2e3);
1853
+ });
1854
+ }
1855
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1856
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("border border-border rounded-lg mb-3 overflow-hidden"), children: [
1857
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1858
+ LogEntryHeader,
1859
+ {
1860
+ log,
1861
+ parsedRequest,
1862
+ expanded,
1863
+ onToggle: () => setExpanded(!expanded)
1864
+ }
1865
+ ),
1866
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { onClick: (e) => e.stopPropagation(), onKeyDown: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "request", children: [
1867
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { className: "mx-4 mt-2", children: [
1868
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw-headers", children: "Raw Headers" }),
1869
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "headers", children: "Headers" }),
1870
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "request", children: "Request" }),
1871
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" }),
1872
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Parsed Response" })
1873
+ ] }),
1874
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3", children: [
1875
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mb-2", children: [
1876
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
1877
+ Button,
1878
+ {
1879
+ variant: "outline",
1880
+ size: "sm",
1881
+ className: "h-7 text-xs",
1882
+ onClick: (e) => {
1883
+ e.stopPropagation();
1884
+ setReplayOpen(true);
1885
+ },
1886
+ children: [
1887
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { className: "size-3 mr-1" }),
1888
+ "Replay"
1889
+ ]
1890
+ }
1891
+ ),
1892
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1893
+ CopyButton,
1894
+ {
1895
+ text: log.rawRequestBody,
1896
+ label: "Copy Request",
1897
+ copied: requestCopied,
1898
+ onCopy: handleCopyRequest
1899
+ }
1900
+ )
1901
+ ] }),
1902
+ log.rawRequestBody !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: log.rawRequestBody, defaultExpandDepth: 1 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" })
1903
+ ] }) }),
1904
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3", children: log.headers && Object.keys(log.headers).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.headers).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1905
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
1906
+ key,
1907
+ ":"
1908
+ ] }),
1909
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
1910
+ ] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No headers captured" }) }) }),
1911
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3", children: log.rawHeaders && Object.keys(log.rawHeaders).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.rawHeaders).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1912
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
1913
+ key,
1914
+ ":"
1915
+ ] }),
1916
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
1917
+ ] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No raw headers captured" }) }) }),
1918
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 space-y-3", children: [
1919
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1920
+ CopyButton,
1921
+ {
1922
+ text: log.responseText,
1923
+ label: "Copy Response",
1924
+ copied: responseCopied,
1925
+ onCopy: handleCopyResponse
1926
+ }
1927
+ ) }),
1928
+ log.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewerFromString, { text: log.responseText, defaultExpandDepth: 1 }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No response" }),
1929
+ log.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(
1930
+ StreamingChunkSequence,
1931
+ {
1932
+ logId: log.id,
1933
+ truncated: log.streamingChunksPath !== null
1934
+ }
1935
+ )
1936
+ ] }) }),
1937
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
1938
+ ResponseView,
1939
+ {
1940
+ responseText: log.responseText,
1941
+ responseStatus: log.responseStatus,
1942
+ streaming: log.streaming,
1943
+ inputTokens: log.inputTokens,
1944
+ outputTokens: log.outputTokens,
1945
+ cacheCreationInputTokens: log.cacheCreationInputTokens,
1946
+ cacheReadInputTokens: log.cacheReadInputTokens,
1947
+ apiFormat: log.apiFormat
1948
+ }
1949
+ ) }) })
1950
+ ] }) })
1951
+ ] }),
1952
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ReplayDialog, { log, open: replayOpen, onOpenChange: setReplayOpen })
1953
+ ] });
1954
+ }
1955
+ function computeStats(logs) {
1956
+ let totalInput = 0;
1957
+ let totalOutput = 0;
1958
+ for (const log of logs) {
1959
+ if (log.inputTokens !== null) totalInput += log.inputTokens;
1960
+ if (log.outputTokens !== null) totalOutput += log.outputTokens;
1961
+ }
1962
+ return { totalInputTokens: totalInput, totalOutputTokens: totalOutput };
1963
+ }
1964
+ function ConversationGroup({
1965
+ group,
1966
+ viewMode = "simple"
1967
+ }) {
1968
+ const [expanded, setExpanded] = reactExports.useState(false);
1969
+ const stats = computeStats(group.logs);
1970
+ const startTime = group.logs[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
1971
+ const endTime = group.logs[group.logs.length - 1]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
1972
+ const displayId = group.conversationId.startsWith("PID:") || group.conversationId.includes("|") ? group.conversationId : group.conversationId.length > 24 ? group.conversationId.slice(0, 12) + "…" + group.conversationId.slice(-12) : group.conversationId;
1973
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4", children: [
1974
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
1975
+ ConversationHeader,
1976
+ {
1977
+ conversationId: displayId,
1978
+ startTime,
1979
+ endTime,
1980
+ totalCalls: group.logs.length,
1981
+ totalInputTokens: stats.totalInputTokens,
1982
+ totalOutputTokens: stats.totalOutputTokens,
1983
+ expanded,
1984
+ onToggle: () => setExpanded(!expanded)
1985
+ }
1986
+ ),
1987
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-4 border-l-2 border-muted ml-3", children: group.logs.map((log) => /* @__PURE__ */ jsxRuntimeExports.jsx(LogEntry, { log, viewMode }, log.id)) })
1988
+ ] });
1989
+ }
1990
+ function Select({
1991
+ ...props
1992
+ }) {
1993
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Root2, { "data-slot": "select", ...props });
1994
+ }
1995
+ function SelectValue({
1996
+ ...props
1997
+ }) {
1998
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Value, { "data-slot": "select-value", ...props });
1999
+ }
2000
+ function SelectTrigger({
2001
+ className,
2002
+ size = "default",
2003
+ children,
2004
+ ...props
2005
+ }) {
2006
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2007
+ Trigger,
2008
+ {
2009
+ "data-slot": "select-trigger",
2010
+ "data-size": size,
2011
+ className: cn(
2012
+ "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
2013
+ className
2014
+ ),
2015
+ ...props,
2016
+ children: [
2017
+ children,
2018
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 opacity-50" }) })
2019
+ ]
2020
+ }
2021
+ );
2022
+ }
2023
+ function SelectContent({
2024
+ className,
2025
+ children,
2026
+ position = "popper",
2027
+ align = "center",
2028
+ ...props
2029
+ }) {
2030
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2031
+ Content2,
2032
+ {
2033
+ "data-slot": "select-content",
2034
+ className: cn(
2035
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
2036
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
2037
+ className
2038
+ ),
2039
+ position,
2040
+ align,
2041
+ ...props,
2042
+ children: [
2043
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollUpButton, {}),
2044
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2045
+ Viewport,
2046
+ {
2047
+ className: cn(
2048
+ "p-1",
2049
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
2050
+ ),
2051
+ children
2052
+ }
2053
+ ),
2054
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollDownButton, {})
2055
+ ]
2056
+ }
2057
+ ) });
2058
+ }
2059
+ function SelectItem({
2060
+ className,
2061
+ children,
2062
+ ...props
2063
+ }) {
2064
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2065
+ Item,
2066
+ {
2067
+ "data-slot": "select-item",
2068
+ className: cn(
2069
+ "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
2070
+ className
2071
+ ),
2072
+ ...props,
2073
+ children: [
2074
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ItemIndicator, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4" }) }) }),
2075
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ItemText, { children })
2076
+ ]
2077
+ }
2078
+ );
2079
+ }
2080
+ function SelectScrollUpButton({
2081
+ className,
2082
+ ...props
2083
+ }) {
2084
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2085
+ ScrollUpButton,
2086
+ {
2087
+ "data-slot": "select-scroll-up-button",
2088
+ className: cn("flex cursor-default items-center justify-center py-1", className),
2089
+ ...props,
2090
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-4" })
2091
+ }
2092
+ );
2093
+ }
2094
+ function SelectScrollDownButton({
2095
+ className,
2096
+ ...props
2097
+ }) {
2098
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2099
+ ScrollDownButton,
2100
+ {
2101
+ "data-slot": "select-scroll-down-button",
2102
+ className: cn("flex cursor-default items-center justify-center py-1", className),
2103
+ ...props,
2104
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
2105
+ }
2106
+ );
2107
+ }
2108
+ const KNOWN_PROVIDER_DOCS = {
2109
+ deepseek: "https://api-docs.deepseek.com/zh-cn/"
2110
+ };
2111
+ function maskApiKey(apiKey) {
2112
+ if (apiKey.length <= 8) return "••••••••";
2113
+ return apiKey.slice(0, 4) + "••••••••" + apiKey.slice(-4);
2114
+ }
2115
+ function hasSuccessField(result) {
2116
+ return Object.prototype.hasOwnProperty.call(result, "success");
2117
+ }
2118
+ function TestStatus({ result }) {
2119
+ if (!hasSuccessField(result)) {
2120
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
2121
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "size-3" }),
2122
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Not configured" })
2123
+ ] });
2124
+ }
2125
+ if (result.success) {
2126
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-green-600", children: [
2127
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3" }),
2128
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Connected" })
2129
+ ] });
2130
+ }
2131
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-red-600", title: result.error, children: [
2132
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3" }),
2133
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: result.error })
2134
+ ] });
2135
+ }
2136
+ function ProviderCard({
2137
+ provider,
2138
+ testResults,
2139
+ isTesting,
2140
+ onEdit,
2141
+ onDelete,
2142
+ onTest
2143
+ }) {
2144
+ const [showApiKey, setShowApiKey] = reactExports.useState(false);
2145
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border rounded-lg p-4 flex flex-col gap-3 bg-card", children: [
2146
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2147
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2 min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium truncate", children: provider.model !== void 0 && provider.model !== "" ? `${provider.model} (${provider.name})` : provider.name }) }),
2148
+ Object.entries(KNOWN_PROVIDER_DOCS).map(
2149
+ ([keyword, url]) => provider.name.toLowerCase().includes(keyword) ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
2150
+ "a",
2151
+ {
2152
+ href: url,
2153
+ target: "_blank",
2154
+ rel: "noopener noreferrer",
2155
+ className: "text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1 text-xs",
2156
+ title: "View API documentation",
2157
+ children: [
2158
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "size-3" }),
2159
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Docs" })
2160
+ ]
2161
+ },
2162
+ keyword
2163
+ ) : null
2164
+ )
2165
+ ] }),
2166
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
2167
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-xs text-muted-foreground bg-muted px-2 py-1 rounded flex-1 truncate", children: showApiKey ? provider.apiKey : maskApiKey(provider.apiKey) }),
2168
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2169
+ "button",
2170
+ {
2171
+ type: "button",
2172
+ onClick: () => setShowApiKey((s) => !s),
2173
+ className: "text-muted-foreground hover:text-foreground transition-colors p-1",
2174
+ "aria-label": showApiKey ? "Hide API key" : "Show API key",
2175
+ children: showApiKey ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-4" })
2176
+ }
2177
+ )
2178
+ ] }),
2179
+ provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2180
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground min-w-0 flex-1", children: [
2181
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Anthropic:" }),
2182
+ " ",
2183
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: provider.anthropicBaseUrl })
2184
+ ] }),
2185
+ testResults && /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: testResults.anthropic.nonStreaming })
2186
+ ] }),
2187
+ provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2188
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground min-w-0 flex-1", children: [
2189
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "OpenAI:" }),
2190
+ " ",
2191
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: provider.openaiBaseUrl })
2192
+ ] }),
2193
+ testResults && /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: testResults.openai.nonStreaming })
2194
+ ] }),
2195
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-1 border-t", children: [
2196
+ onTest !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
2197
+ Button,
2198
+ {
2199
+ variant: "outline",
2200
+ size: "sm",
2201
+ onClick: () => onTest(provider.id),
2202
+ className: "text-xs h-7 gap-1",
2203
+ disabled: isTesting ?? false,
2204
+ children: [
2205
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCw, { className: `size-3 ${isTesting ?? false ? "animate-spin" : ""}` }),
2206
+ isTesting ?? false ? "Testing..." : "Test"
2207
+ ]
2208
+ }
2209
+ ),
2210
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
2211
+ Button,
2212
+ {
2213
+ variant: "outline",
2214
+ size: "sm",
2215
+ onClick: () => onEdit(provider),
2216
+ className: "text-xs h-7 gap-1",
2217
+ children: [
2218
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
2219
+ "Edit"
2220
+ ]
2221
+ }
2222
+ ),
2223
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
2224
+ Button,
2225
+ {
2226
+ variant: "outline",
2227
+ size: "sm",
2228
+ onClick: () => onDelete(provider.id),
2229
+ className: "text-xs h-7 gap-1 text-destructive hover:text-destructive",
2230
+ children: [
2231
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3" }),
2232
+ "Delete"
2233
+ ]
2234
+ }
2235
+ )
2236
+ ] })
2237
+ ] });
2238
+ }
2239
+ const KNOWN_PROVIDER_PRESETS = {
2240
+ deepseek: {
2241
+ format: "openai",
2242
+ baseUrl: "https://api.deepseek.com"
2243
+ },
2244
+ minimax: {
2245
+ format: "anthropic",
2246
+ baseUrl: "https://api.minimaxi.com/anthropic"
2247
+ },
2248
+ alibaba: {
2249
+ format: "openai",
2250
+ baseUrl: "https://dashscope.aliyuncs.com/compatible-mode"
2251
+ }
2252
+ };
2253
+ const MINIMAX_MODELS = [
2254
+ "MiniMax M2.7",
2255
+ "MiniMax M2.7-highspeed",
2256
+ "MiniMax M2.5",
2257
+ "MiniMax M2.5-highspeed",
2258
+ "MiniMax M2.1",
2259
+ "MiniMax M2.1-highspeed",
2260
+ "MiniMax M2"
2261
+ ];
2262
+ const ALIBABA_MODELS = ["glm-5", "glm-5.1", "qwen3.6-plus", "qwen3.7-max"];
2263
+ function ProviderForm({ provider, onSubmit, onCancel }) {
2264
+ const [name, setName] = reactExports.useState(provider?.name ?? "Provider Name");
2265
+ const [apiKey, setApiKey] = reactExports.useState(provider?.apiKey ?? "");
2266
+ const [model, setModel] = reactExports.useState(provider?.model ?? "");
2267
+ const [format, setFormat] = reactExports.useState(provider?.format ?? "anthropic");
2268
+ const [baseUrl, setBaseUrl] = reactExports.useState(provider?.baseUrl ?? "");
2269
+ const [errors, setErrors] = reactExports.useState({});
2270
+ const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
2271
+ const [manualBaseUrlOverride, setManualBaseUrlOverride] = reactExports.useState(false);
2272
+ const isMiniMax = name.toLowerCase().includes("minimax");
2273
+ const isAlibaba = name.toLowerCase().includes("alibaba");
2274
+ reactExports.useEffect(() => {
2275
+ if (provider) {
2276
+ setName(provider.name);
2277
+ setApiKey(provider.apiKey);
2278
+ setModel(provider.model ?? "");
2279
+ setFormat(provider.format ?? "anthropic");
2280
+ setBaseUrl(provider.baseUrl ?? "");
2281
+ setManualBaseUrlOverride(false);
2282
+ }
2283
+ }, [provider]);
2284
+ reactExports.useEffect(() => {
2285
+ const lowerName = name.toLowerCase();
2286
+ for (const [keyword, preset] of Object.entries(KNOWN_PROVIDER_PRESETS)) {
2287
+ if (lowerName.includes(keyword)) {
2288
+ if (!manualBaseUrlOverride) {
2289
+ setFormat(preset.format);
2290
+ setBaseUrl(preset.baseUrl);
2291
+ }
2292
+ if (keyword === "minimax" && !model) {
2293
+ setModel(MINIMAX_MODELS[0] ?? "");
2294
+ }
2295
+ if (keyword === "alibaba" && !model) {
2296
+ setModel(ALIBABA_MODELS[0] ?? "");
2297
+ }
2298
+ break;
2299
+ }
2300
+ }
2301
+ }, [name]);
2302
+ function validate() {
2303
+ const newErrors = {};
2304
+ if (!name.trim()) {
2305
+ newErrors.name = "Name is required";
2306
+ }
2307
+ if (!apiKey.trim()) {
2308
+ newErrors.apiKey = "API key is required";
2309
+ }
2310
+ if (!model.trim()) {
2311
+ newErrors.model = "Model is required";
2312
+ }
2313
+ if (!baseUrl.trim()) {
2314
+ newErrors.baseUrl = "Base URL is required";
2315
+ } else if (!isValidUrl(baseUrl.trim())) {
2316
+ newErrors.baseUrl = "Invalid URL format";
2317
+ }
2318
+ setErrors(newErrors);
2319
+ return Object.keys(newErrors).length === 0;
2320
+ }
2321
+ function isValidUrl(str) {
2322
+ try {
2323
+ new URL(str);
2324
+ return true;
2325
+ } catch {
2326
+ return false;
2327
+ }
2328
+ }
2329
+ function handleSubmit(e) {
2330
+ e.preventDefault();
2331
+ if (!validate()) return;
2332
+ setIsSubmitting(true);
2333
+ try {
2334
+ onSubmit({
2335
+ name: name.trim(),
2336
+ apiKey: apiKey.trim(),
2337
+ model: model.trim() || void 0,
2338
+ format,
2339
+ baseUrl: baseUrl.trim() || void 0
2340
+ });
2341
+ } finally {
2342
+ setIsSubmitting(false);
2343
+ }
2344
+ }
2345
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
2346
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
2347
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-name", className: "text-sm font-medium", children: [
2348
+ "Name ",
2349
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
2350
+ ] }),
2351
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2352
+ "input",
2353
+ {
2354
+ id: "provider-name",
2355
+ type: "text",
2356
+ value: name,
2357
+ onChange: (e) => setName(e.target.value),
2358
+ placeholder: "Model Name",
2359
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
2360
+ }
2361
+ ),
2362
+ errors.name !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.name })
2363
+ ] }),
2364
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
2365
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-apikey", className: "text-sm font-medium", children: [
2366
+ "API Key ",
2367
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
2368
+ ] }),
2369
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2370
+ "input",
2371
+ {
2372
+ id: "provider-apikey",
2373
+ type: "password",
2374
+ value: apiKey,
2375
+ onChange: (e) => setApiKey(e.target.value),
2376
+ placeholder: "sk-ant-...",
2377
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
2378
+ }
2379
+ ),
2380
+ errors.apiKey !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.apiKey })
2381
+ ] }),
2382
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
2383
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-model", className: "text-sm font-medium", children: [
2384
+ "Model ",
2385
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
2386
+ ] }),
2387
+ isMiniMax || isAlibaba ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2388
+ "select",
2389
+ {
2390
+ id: "provider-model",
2391
+ value: model,
2392
+ onChange: (e) => setModel(e.target.value),
2393
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
2394
+ children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: m, children: m }, m))
2395
+ }
2396
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
2397
+ "input",
2398
+ {
2399
+ id: "provider-model",
2400
+ type: "text",
2401
+ value: model,
2402
+ onChange: (e) => setModel(e.target.value),
2403
+ placeholder: "",
2404
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
2405
+ }
2406
+ ),
2407
+ errors.model !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.model })
2408
+ ] }),
2409
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
2410
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-format", className: "text-sm font-medium", children: [
2411
+ "API Format ",
2412
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
2413
+ ] }),
2414
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
2415
+ "select",
2416
+ {
2417
+ id: "provider-format",
2418
+ value: format,
2419
+ onChange: (e) => setFormat(e.target.value === "anthropic" ? "anthropic" : "openai"),
2420
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
2421
+ children: [
2422
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "anthropic", children: "Anthropic (/v1/messages)" }),
2423
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "openai", children: "OpenAI (/v1/chat/completions)" })
2424
+ ]
2425
+ }
2426
+ ),
2427
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: format === "anthropic" ? "Use Anthropic format for /v1/messages endpoint" : "Use OpenAI format for /v1/chat/completions endpoint" })
2428
+ ] }),
2429
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
2430
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-base-url", className: "text-sm font-medium", children: [
2431
+ "Base URL ",
2432
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
2433
+ ] }),
2434
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2435
+ "input",
2436
+ {
2437
+ id: "provider-base-url",
2438
+ type: "text",
2439
+ value: baseUrl,
2440
+ onChange: (e) => {
2441
+ setManualBaseUrlOverride(true);
2442
+ setBaseUrl(e.target.value);
2443
+ },
2444
+ placeholder: format === "anthropic" ? "https://api.anthropic.com" : "https://api.openai.com/v1",
2445
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
2446
+ }
2447
+ ),
2448
+ errors.baseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.baseUrl }),
2449
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Base URL for the provider API." })
2450
+ ] }),
2451
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 justify-end pt-2", children: [
2452
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: isSubmitting, children: "Cancel" }),
2453
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? "Saving..." : provider ? "Update Provider" : "Add Provider" })
2454
+ ] })
2455
+ ] });
2456
+ }
2457
+ function ProvidersPanel() {
2458
+ const [providers, setProviders] = reactExports.useState([]);
2459
+ const [isLoading, setIsLoading] = reactExports.useState(true);
2460
+ const [showForm, setShowForm] = reactExports.useState(false);
2461
+ const [editingProvider, setEditingProvider] = reactExports.useState();
2462
+ const [error, setError] = reactExports.useState(null);
2463
+ const [testResults, setTestResults] = reactExports.useState({});
2464
+ const [testingProviders, setTestingProviders] = reactExports.useState(/* @__PURE__ */ new Set());
2465
+ const [configPath, setConfigPath] = reactExports.useState(null);
2466
+ const [configPathCopied, setConfigPathCopied] = reactExports.useState(false);
2467
+ const fetchProviders = reactExports.useCallback(async () => {
2468
+ try {
2469
+ const providersRes = await fetch("/api/providers");
2470
+ const providersData = await providersRes.json();
2471
+ setProviders(providersData);
2472
+ } catch {
2473
+ setError("Failed to load providers");
2474
+ } finally {
2475
+ setIsLoading(false);
2476
+ }
2477
+ }, []);
2478
+ reactExports.useEffect(() => {
2479
+ void fetchProviders();
2480
+ void (async () => {
2481
+ try {
2482
+ const res = await fetch("/api/config/paths");
2483
+ if (res.ok) {
2484
+ const data = await res.json();
2485
+ setConfigPath(data.providerConfig);
2486
+ }
2487
+ } catch {
2488
+ }
2489
+ })();
2490
+ }, [fetchProviders]);
2491
+ const runTest = reactExports.useCallback(async (providerId) => {
2492
+ setTestingProviders((prev) => new Set(prev).add(providerId));
2493
+ try {
2494
+ const res = await fetch(`/api/providers/${providerId}/test`, { method: "POST" });
2495
+ if (res.ok) {
2496
+ const results = await res.json();
2497
+ setTestResults((prev) => ({ ...prev, [providerId]: results }));
2498
+ }
2499
+ } finally {
2500
+ setTestingProviders((prev) => {
2501
+ const next = new Set(prev);
2502
+ next.delete(providerId);
2503
+ return next;
2504
+ });
2505
+ }
2506
+ }, []);
2507
+ function handleAddProvider(data) {
2508
+ void (async () => {
2509
+ const res = await fetch("/api/providers", {
2510
+ method: "POST",
2511
+ headers: { "Content-Type": "application/json" },
2512
+ body: JSON.stringify(data)
2513
+ });
2514
+ if (!res.ok) {
2515
+ const err = await res.json();
2516
+ setError(err.error ?? "Failed to add provider");
2517
+ return;
2518
+ }
2519
+ const newProvider = await res.json();
2520
+ await fetchProviders();
2521
+ setShowForm(false);
2522
+ await runTest(newProvider.id);
2523
+ })();
2524
+ }
2525
+ function handleUpdateProvider(data) {
2526
+ if (!editingProvider) return;
2527
+ void (async () => {
2528
+ const res = await fetch(`/api/providers/${editingProvider.id}`, {
2529
+ method: "PUT",
2530
+ headers: { "Content-Type": "application/json" },
2531
+ body: JSON.stringify(data)
2532
+ });
2533
+ if (!res.ok) {
2534
+ const err = await res.json();
2535
+ setError(err.error ?? "Failed to update provider");
2536
+ return;
2537
+ }
2538
+ const updated = await res.json();
2539
+ await fetchProviders();
2540
+ setEditingProvider(void 0);
2541
+ await runTest(updated.id);
2542
+ })();
2543
+ }
2544
+ function handleDeleteProvider(providerId) {
2545
+ if (!window.confirm("Are you sure you want to delete this provider?")) return;
2546
+ void (async () => {
2547
+ const res = await fetch(`/api/providers/${providerId}`, {
2548
+ method: "DELETE"
2549
+ });
2550
+ if (!res.ok) {
2551
+ const err = await res.json();
2552
+ setError(err.error ?? "Failed to delete provider");
2553
+ return;
2554
+ }
2555
+ await fetchProviders();
2556
+ })();
2557
+ }
2558
+ if (isLoading) {
2559
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "Loading providers..." }) });
2560
+ }
2561
+ if (showForm || editingProvider) {
2562
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
2563
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium", children: editingProvider ? "Edit Provider" : "Add New Provider" }) }),
2564
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2565
+ ProviderForm,
2566
+ {
2567
+ provider: editingProvider,
2568
+ onSubmit: editingProvider ? handleUpdateProvider : handleAddProvider,
2569
+ onCancel: () => {
2570
+ setShowForm(false);
2571
+ setEditingProvider(void 0);
2572
+ }
2573
+ }
2574
+ )
2575
+ ] });
2576
+ }
2577
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
2578
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
2579
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium", children: "Providers" }),
2580
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowForm(true), size: "sm", className: "gap-1", children: [
2581
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
2582
+ "Add Provider"
2583
+ ] })
2584
+ ] }),
2585
+ configPath !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground bg-muted/30 rounded-md px-3 py-2", children: [
2586
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0", children: "Config:" }),
2587
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono truncate", title: configPath, children: configPath }),
2588
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2589
+ "button",
2590
+ {
2591
+ type: "button",
2592
+ onClick: () => {
2593
+ void window.navigator.clipboard.writeText(configPath).then(() => {
2594
+ setConfigPathCopied(true);
2595
+ setTimeout(() => setConfigPathCopied(false), 2e3);
2596
+ });
2597
+ },
2598
+ className: "shrink-0 ml-auto text-muted-foreground hover:text-foreground transition-colors",
2599
+ title: "Copy path",
2600
+ children: configPathCopied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
2601
+ }
2602
+ )
2603
+ ] }),
2604
+ error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-sm text-destructive bg-destructive/10 rounded-md px-3 py-2", children: [
2605
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "size-4 shrink-0" }),
2606
+ error
2607
+ ] }),
2608
+ providers.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-12 space-y-3", children: [
2609
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "No providers configured yet." }),
2610
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowForm(true), variant: "outline", size: "sm", children: [
2611
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
2612
+ "Add Your First Provider"
2613
+ ] })
2614
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: providers.map((provider) => /* @__PURE__ */ jsxRuntimeExports.jsx(
2615
+ ProviderCard,
2616
+ {
2617
+ provider,
2618
+ testResults: testResults[provider.id],
2619
+ isTesting: testingProviders.has(provider.id),
2620
+ onEdit: (p) => setEditingProvider(p),
2621
+ onDelete: handleDeleteProvider,
2622
+ onTest: (id) => {
2623
+ void runTest(id);
2624
+ }
2625
+ },
2626
+ provider.id
2627
+ )) })
2628
+ ] });
2629
+ }
2630
+ function SettingsDialog() {
2631
+ const [open, setOpen] = reactExports.useState(false);
2632
+ const [activeTab, setActiveTab] = reactExports.useState("providers");
2633
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Dialog, { open, onOpenChange: setOpen, children: [
2634
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "ghost", size: "icon", className: "size-8", children: [
2635
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { className: "size-4" }),
2636
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Settings" })
2637
+ ] }) }),
2638
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-2xl max-h-[80vh] overflow-hidden flex flex-col", children: [
2639
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { children: "Settings" }) }),
2640
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "flex-1 overflow-hidden", children: [
2641
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsList, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "providers", children: "Providers" }) }),
2642
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-4 overflow-y-auto flex-1", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "providers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProvidersPanel, {}) }) })
2643
+ ] })
2644
+ ] })
2645
+ ] });
2646
+ }
2647
+ function truncateSessionId(id) {
2648
+ if (id.length <= 30) return id;
2649
+ return id.slice(0, 12) + "…" + id.slice(-12);
2650
+ }
2651
+ function computeTokenSummary(logs) {
2652
+ let totalIn = 0;
2653
+ let totalOut = 0;
2654
+ for (const log of logs) {
2655
+ if (log.inputTokens !== null) totalIn += log.inputTokens;
2656
+ if (log.outputTokens !== null) totalOut += log.outputTokens;
2657
+ }
2658
+ return { totalIn, totalOut };
2659
+ }
2660
+ function CopyableCommand({ command }) {
2661
+ const [copied, setCopied] = reactExports.useState(false);
2662
+ const handleCopy = reactExports.useCallback(() => {
2663
+ void window.navigator.clipboard.writeText(command).then(() => {
2664
+ setCopied(true);
2665
+ setTimeout(() => setCopied(false), 2e3);
2666
+ });
2667
+ }, [command]);
2668
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center gap-2 bg-muted rounded-md px-3 py-2", children: [
2669
+ /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-blue-500 font-mono text-sm m-0", children: command }),
2670
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2671
+ "button",
2672
+ {
2673
+ type: "button",
2674
+ onClick: handleCopy,
2675
+ className: "text-muted-foreground hover:text-foreground transition-colors shrink-0 cursor-pointer",
2676
+ "aria-label": "Copy command",
2677
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2678
+ "svg",
2679
+ {
2680
+ width: "16",
2681
+ height: "16",
2682
+ viewBox: "0 0 24 24",
2683
+ fill: "none",
2684
+ stroke: "currentColor",
2685
+ strokeWidth: "2",
2686
+ strokeLinecap: "round",
2687
+ strokeLinejoin: "round",
2688
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M20 6 9 17l-5-5" })
2689
+ }
2690
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
2691
+ "svg",
2692
+ {
2693
+ width: "16",
2694
+ height: "16",
2695
+ viewBox: "0 0 24 24",
2696
+ fill: "none",
2697
+ stroke: "currentColor",
2698
+ strokeWidth: "2",
2699
+ strokeLinecap: "round",
2700
+ strokeLinejoin: "round",
2701
+ children: [
2702
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
2703
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
2704
+ ]
2705
+ }
2706
+ )
2707
+ }
2708
+ )
2709
+ ] });
2710
+ }
2711
+ function ProxyViewer({
2712
+ logs,
2713
+ sessions,
2714
+ models,
2715
+ selectedSession,
2716
+ selectedModel,
2717
+ onSessionChange,
2718
+ onModelChange,
2719
+ onClearAll,
2720
+ viewMode,
2721
+ onViewModeChange
2722
+ }) {
2723
+ const { totalIn, totalOut } = computeTokenSummary(logs);
2724
+ const [groupedView, setGroupedView] = reactExports.useState(true);
2725
+ const [exporting, setExporting] = reactExports.useState(false);
2726
+ const handleExport = reactExports.useCallback(async () => {
2727
+ setExporting(true);
2728
+ try {
2729
+ await exportLogsAsZip(logs);
2730
+ } finally {
2731
+ setExporting(false);
2732
+ }
2733
+ }, [logs]);
2734
+ const parentRef = reactExports.useRef(null);
2735
+ const groups = reactExports.useMemo(() => groupLogsByConversation(logs), [logs]);
2736
+ const renderGroups = logs.length > 0 && groupedView && !(groups.length === 1 && groups[0]?.logs.length === logs.length);
2737
+ const rowVirtualizer = useVirtualizer({
2738
+ count: renderGroups ? groups.length : logs.length,
2739
+ getScrollElement: () => parentRef.current,
2740
+ estimateSize: () => 150,
2741
+ measureElement: typeof window !== "undefined" ? (element) => element.getBoundingClientRect().height : void 0,
2742
+ overscan: 5
2743
+ });
2744
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1200px] mx-auto flex flex-col h-screen", style: { maxHeight: "100vh" }, children: [
2745
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 mb-4 px-6 pt-6", children: [
2746
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-lg font-bold flex-1", children: "LLM Proxy Inspector" }),
2747
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border border-border rounded-md overflow-hidden", children: [
2748
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2749
+ "button",
2750
+ {
2751
+ type: "button",
2752
+ onClick: () => onViewModeChange("simple"),
2753
+ className: `px-2 py-1 cursor-pointer transition-colors text-xs ${viewMode === "simple" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
2754
+ children: "Simple"
2755
+ }
2756
+ ),
2757
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2758
+ "button",
2759
+ {
2760
+ type: "button",
2761
+ onClick: () => onViewModeChange("full"),
2762
+ className: `px-2 py-1 cursor-pointer transition-colors text-xs ${viewMode === "full" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
2763
+ children: "Full"
2764
+ }
2765
+ )
2766
+ ] }),
2767
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}),
2768
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
2769
+ logs.length,
2770
+ " request",
2771
+ logs.length !== 1 ? "s" : "",
2772
+ totalIn > 0 || totalOut > 0 ? ` · ${totalIn.toLocaleString()} in / ${totalOut.toLocaleString()} out` : ""
2773
+ ] }),
2774
+ logs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
2775
+ "button",
2776
+ {
2777
+ type: "button",
2778
+ onClick: () => {
2779
+ void handleExport();
2780
+ },
2781
+ disabled: exporting,
2782
+ className: "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",
2783
+ title: "Export all logs as JSON ZIP",
2784
+ children: exporting ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2785
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3" }),
2786
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export All" })
2787
+ ] })
2788
+ }
2789
+ ),
2790
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2791
+ "button",
2792
+ {
2793
+ type: "button",
2794
+ onClick: onClearAll,
2795
+ className: "text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer",
2796
+ title: "Clear all logs",
2797
+ children: "Clear All"
2798
+ }
2799
+ )
2800
+ ] }),
2801
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3 px-6 mb-4", children: [
2802
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
2803
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[400px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
2804
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
2805
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All sessions" }),
2806
+ sessions.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: s, children: truncateSessionId(s) }, s))
2807
+ ] })
2808
+ ] }),
2809
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedModel, onValueChange: onModelChange, children: [
2810
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All models" }) }),
2811
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
2812
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All models" }),
2813
+ models.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: m, children: m }, m))
2814
+ ] })
2815
+ ] }),
2816
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border border-border rounded-md overflow-hidden", children: [
2817
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2818
+ "button",
2819
+ {
2820
+ type: "button",
2821
+ onClick: () => setGroupedView(true),
2822
+ className: `px-2 py-1.5 cursor-pointer transition-colors ${groupedView ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
2823
+ title: "Grouped view",
2824
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(LayoutGrid, { className: "size-4" })
2825
+ }
2826
+ ),
2827
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2828
+ "button",
2829
+ {
2830
+ type: "button",
2831
+ onClick: () => setGroupedView(false),
2832
+ className: `px-2 py-1.5 cursor-pointer transition-colors ${!groupedView ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
2833
+ title: "Flat view",
2834
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { className: "size-4" })
2835
+ }
2836
+ )
2837
+ ] })
2838
+ ] }),
2839
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 px-6 pb-6", children: logs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
2840
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No requests captured yet." }),
2841
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs", children: "Route AI coding tools through the proxy:" }),
2842
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "LLM_BASE_URL=http://localhost:25947/proxy <your-tool>" })
2843
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: parentRef, className: "overflow-y-auto h-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2844
+ "div",
2845
+ {
2846
+ style: {
2847
+ height: `${rowVirtualizer.getTotalSize()}px`,
2848
+ width: "100%",
2849
+ position: "relative"
2850
+ },
2851
+ children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
2852
+ if (renderGroups) {
2853
+ const group = groups[virtualRow.index];
2854
+ if (group === void 0) return null;
2855
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2856
+ "div",
2857
+ {
2858
+ "data-index": virtualRow.index,
2859
+ ref: rowVirtualizer.measureElement,
2860
+ style: {
2861
+ position: "absolute",
2862
+ top: 0,
2863
+ left: 0,
2864
+ width: "100%",
2865
+ transform: `translateY(${virtualRow.start}px)`
2866
+ },
2867
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ConversationGroup, { group, viewMode })
2868
+ },
2869
+ group.id
2870
+ );
2871
+ } else {
2872
+ const log = logs[virtualRow.index];
2873
+ if (log === void 0) return null;
2874
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
2875
+ "div",
2876
+ {
2877
+ "data-index": virtualRow.index,
2878
+ ref: rowVirtualizer.measureElement,
2879
+ style: {
2880
+ position: "absolute",
2881
+ top: 0,
2882
+ left: 0,
2883
+ width: "100%",
2884
+ transform: `translateY(${virtualRow.start}px)`
2885
+ },
2886
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(LogEntry, { log, viewMode })
2887
+ },
2888
+ log.id
2889
+ );
2890
+ }
2891
+ })
2892
+ }
2893
+ ) }) })
2894
+ ] });
2895
+ }
2896
+ object({
2897
+ logs: array(CapturedLogSchema),
2898
+ total: number(),
2899
+ offset: number(),
2900
+ limit: number()
2901
+ });
2902
+ const SSEUpdateSchema = union([
2903
+ object({
2904
+ type: literal("init"),
2905
+ logs: array(CapturedLogSchema)
2906
+ }),
2907
+ object({
2908
+ type: literal("update"),
2909
+ log: CapturedLogSchema
2910
+ })
2911
+ ]);
2912
+ function extractSessions(logs) {
2913
+ const set = /* @__PURE__ */ new Set();
2914
+ for (const l of logs) {
2915
+ if (l.sessionId !== null && l.sessionId !== "") set.add(l.sessionId);
2916
+ }
2917
+ return [...set];
2918
+ }
2919
+ function extractModels(logs) {
2920
+ const set = /* @__PURE__ */ new Set();
2921
+ for (const l of logs) {
2922
+ if (l.model !== null && l.model !== "") set.add(l.model);
2923
+ }
2924
+ return [...set];
2925
+ }
2926
+ function ProxyViewerContainer() {
2927
+ const [logs, setLogs] = reactExports.useState([]);
2928
+ const [sessions, setSessions] = reactExports.useState([]);
2929
+ const [models, setModels] = reactExports.useState([]);
2930
+ const [selectedSession, setSelectedSession] = reactExports.useState("__all__");
2931
+ const [selectedModel, setSelectedModel] = reactExports.useState("__all__");
2932
+ const [viewMode, setViewMode] = reactExports.useState("simple");
2933
+ const [error, setError] = reactExports.useState(null);
2934
+ const eventSourceRef = reactExports.useRef(null);
2935
+ const fetchSessionsAndModels = reactExports.useCallback(async () => {
2936
+ try {
2937
+ const [sessionsRes, modelsRes] = await Promise.all([
2938
+ fetch("/api/sessions"),
2939
+ fetch("/api/models")
2940
+ ]);
2941
+ if (sessionsRes.ok && modelsRes.ok) {
2942
+ const sessionsJson = await sessionsRes.json();
2943
+ const modelsJson = await modelsRes.json();
2944
+ const sessionsResult = array(string()).safeParse(sessionsJson);
2945
+ const modelsResult = array(string()).safeParse(modelsJson);
2946
+ if (sessionsResult.success) setSessions(sessionsResult.data);
2947
+ if (modelsResult.success) setModels(modelsResult.data);
2948
+ }
2949
+ } catch {
2950
+ }
2951
+ }, []);
2952
+ const connectSSE = reactExports.useCallback(() => {
2953
+ if (eventSourceRef.current) {
2954
+ eventSourceRef.current.close();
2955
+ }
2956
+ const params = new URLSearchParams();
2957
+ if (selectedSession !== "__all__") params.set("sessionId", selectedSession);
2958
+ if (selectedModel !== "__all__") params.set("model", selectedModel);
2959
+ const es = new EventSource(`/api/logs/stream?${params}`);
2960
+ eventSourceRef.current = es;
2961
+ es.onmessage = (event) => {
2962
+ try {
2963
+ const rawData = String(event.data);
2964
+ const parsed = JSON.parse(rawData);
2965
+ const updateResult = SSEUpdateSchema.safeParse(parsed);
2966
+ if (!updateResult.success) {
2967
+ setError("Failed to parse SSE data");
2968
+ return;
2969
+ }
2970
+ const update = updateResult.data;
2971
+ if (update.type === "init") {
2972
+ setLogs(update.logs);
2973
+ setSessions(extractSessions(update.logs));
2974
+ setModels(extractModels(update.logs));
2975
+ setError(null);
2976
+ } else if (update.type === "update") {
2977
+ setLogs((prev) => {
2978
+ const sessionMatch = selectedSession === "__all__" || update.log.sessionId === selectedSession;
2979
+ const modelMatch = selectedModel === "__all__" || update.log.model === selectedModel;
2980
+ if (!sessionMatch || !modelMatch) return prev;
2981
+ const existsIndex = prev.findIndex((l) => l.id === update.log.id);
2982
+ if (existsIndex >= 0) {
2983
+ return prev.map((l) => l.id === update.log.id ? update.log : l);
2984
+ }
2985
+ return [...prev, update.log];
2986
+ });
2987
+ setSessions((prev) => {
2988
+ const sessionId = update.log.sessionId;
2989
+ if (sessionId !== null && sessionId !== void 0 && sessionId !== "") {
2990
+ return prev.includes(sessionId) ? prev : [...prev, sessionId];
2991
+ }
2992
+ return prev;
2993
+ });
2994
+ setModels((prev) => {
2995
+ const model = update.log.model;
2996
+ if (model !== null && model !== void 0 && model !== "") {
2997
+ return prev.includes(model) ? prev : [...prev, model];
2998
+ }
2999
+ return prev;
3000
+ });
3001
+ }
3002
+ } catch {
3003
+ setError("Failed to parse SSE data");
3004
+ }
3005
+ };
3006
+ es.onerror = () => {
3007
+ setError("SSE connection lost, reconnecting...");
3008
+ es.close();
3009
+ setTimeout(connectSSE, 3e3);
3010
+ };
3011
+ void fetchSessionsAndModels();
3012
+ }, [selectedSession, selectedModel, fetchSessionsAndModels]);
3013
+ reactExports.useEffect(() => {
3014
+ connectSSE();
3015
+ return () => {
3016
+ if (eventSourceRef.current) {
3017
+ eventSourceRef.current.close();
3018
+ eventSourceRef.current = null;
3019
+ }
3020
+ };
3021
+ }, [connectSSE]);
3022
+ const handleClearAll = reactExports.useCallback(() => {
3023
+ void (async () => {
3024
+ try {
3025
+ const res = await fetch("/api/logs", { method: "DELETE" });
3026
+ if (!res.ok) {
3027
+ setError("Failed to clear logs");
3028
+ return;
3029
+ }
3030
+ setLogs([]);
3031
+ setSessions([]);
3032
+ setModels([]);
3033
+ setError(null);
3034
+ } catch (err) {
3035
+ setError(err instanceof Error ? err.message : "Unknown error clearing logs");
3036
+ }
3037
+ })();
3038
+ }, []);
3039
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3040
+ error !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed top-4 right-4 bg-destructive text-destructive-foreground px-4 py-2 rounded-md text-sm z-50", children: error }),
3041
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3042
+ ProxyViewer,
3043
+ {
3044
+ logs,
3045
+ sessions,
3046
+ models,
3047
+ selectedSession,
3048
+ selectedModel,
3049
+ onSessionChange: setSelectedSession,
3050
+ onModelChange: setSelectedModel,
3051
+ onClearAll: handleClearAll,
3052
+ viewMode,
3053
+ onViewModeChange: setViewMode
3054
+ }
3055
+ )
3056
+ ] });
3057
+ }
3058
+ const SplitComponent = ProxyViewerContainer;
3059
+ export {
3060
+ SplitComponent as component
3061
+ };