@tonyclaw/agent-inspector 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (400) hide show
  1. package/.output/cli.js +1611 -0
  2. package/.output/nitro.json +17 -0
  3. package/.output/public/assets/CompareDrawer-CU5ZrWcL.js +1 -0
  4. package/.output/public/assets/ProxyViewerContainer-pEBqVp1d.js +101 -0
  5. package/.output/public/assets/ReplayDialog-F58yNg5j.js +1 -0
  6. package/.output/public/assets/RequestAnatomy-C9lT0qE_.js +1 -0
  7. package/.output/public/assets/ResponseView-DHJq6bnz.js +1 -0
  8. package/.output/public/assets/StreamingChunkSequence-BTgfpFUT.js +1 -0
  9. package/.output/public/assets/_sessionId-DsNRbnNm.js +1 -0
  10. package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
  11. package/.output/public/assets/index-CpWG2hFn.css +1 -0
  12. package/.output/public/assets/index-DmBV8Gve.js +1 -0
  13. package/.output/public/assets/json-viewer-CZVYLR8j.js +14 -0
  14. package/.output/public/assets/main-DHs7FBK3.js +18 -0
  15. package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
  16. package/.output/public/assets/qwen-CONDcHqt.png +0 -0
  17. package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
  18. package/.output/server/_chunks/ssr-renderer.mjs +22 -0
  19. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
  20. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
  21. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +2 -0
  22. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +2 -0
  23. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +2 -0
  24. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
  25. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
  26. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
  27. package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
  28. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
  29. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
  30. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
  31. package/.output/server/_libs/ajv-formats.mjs +330 -0
  32. package/.output/server/_libs/ajv.mjs +11444 -0
  33. package/.output/server/_libs/aria-hidden.mjs +122 -0
  34. package/.output/server/_libs/atomically.mjs +152 -0
  35. package/.output/server/_libs/bail.mjs +8 -0
  36. package/.output/server/_libs/cfworker__json-schema.mjs +1 -0
  37. package/.output/server/_libs/character-entities.mjs +2130 -0
  38. package/.output/server/_libs/class-variance-authority.mjs +44 -0
  39. package/.output/server/_libs/clsx.mjs +16 -0
  40. package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
  41. package/.output/server/_libs/conf.mjs +635 -0
  42. package/.output/server/_libs/cookie-es.mjs +58 -0
  43. package/.output/server/_libs/core-util-is.mjs +75 -0
  44. package/.output/server/_libs/croner.mjs +1 -0
  45. package/.output/server/_libs/crossws.mjs +1 -0
  46. package/.output/server/_libs/debounce-fn.mjs +69 -0
  47. package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
  48. package/.output/server/_libs/dequal.mjs +27 -0
  49. package/.output/server/_libs/detect-node-es.mjs +1 -0
  50. package/.output/server/_libs/devlop.mjs +8 -0
  51. package/.output/server/_libs/diff.mjs +320 -0
  52. package/.output/server/_libs/dot-prop.mjs +265 -0
  53. package/.output/server/_libs/env-paths.mjs +57 -0
  54. package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
  55. package/.output/server/_libs/extend.mjs +97 -0
  56. package/.output/server/_libs/fast-deep-equal.mjs +38 -0
  57. package/.output/server/_libs/fast-uri.mjs +812 -0
  58. package/.output/server/_libs/floating-ui__core.mjs +725 -0
  59. package/.output/server/_libs/floating-ui__dom.mjs +622 -0
  60. package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
  61. package/.output/server/_libs/floating-ui__utils.mjs +320 -0
  62. package/.output/server/_libs/get-nonce.mjs +9 -0
  63. package/.output/server/_libs/h3-v2.mjs +276 -0
  64. package/.output/server/_libs/h3.mjs +408 -0
  65. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
  66. package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
  67. package/.output/server/_libs/hookable.mjs +1 -0
  68. package/.output/server/_libs/html-url-attributes.mjs +26 -0
  69. package/.output/server/_libs/immediate.mjs +74 -0
  70. package/.output/server/_libs/inherits.mjs +50 -0
  71. package/.output/server/_libs/inline-style-parser.mjs +142 -0
  72. package/.output/server/_libs/is-plain-obj.mjs +10 -0
  73. package/.output/server/_libs/isarray.mjs +14 -0
  74. package/.output/server/_libs/isbot.mjs +20 -0
  75. package/.output/server/_libs/json-schema-traverse.mjs +180 -0
  76. package/.output/server/_libs/jszip.mjs +3051 -0
  77. package/.output/server/_libs/lie.mjs +273 -0
  78. package/.output/server/_libs/lucide-react.mjs +492 -0
  79. package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
  80. package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
  81. package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
  82. package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
  83. package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
  84. package/.output/server/_libs/micromark-factory-label.mjs +63 -0
  85. package/.output/server/_libs/micromark-factory-space.mjs +24 -0
  86. package/.output/server/_libs/micromark-factory-title.mjs +65 -0
  87. package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
  88. package/.output/server/_libs/micromark-util-character.mjs +44 -0
  89. package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
  90. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
  91. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
  92. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
  93. package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
  94. package/.output/server/_libs/micromark-util-encode.mjs +1 -0
  95. package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
  96. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
  97. package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
  98. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
  99. package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
  100. package/.output/server/_libs/micromark.mjs +906 -0
  101. package/.output/server/_libs/mimic-function.mjs +47 -0
  102. package/.output/server/_libs/modelcontextprotocol__server.mjs +9738 -0
  103. package/.output/server/_libs/ocache.mjs +1 -0
  104. package/.output/server/_libs/ohash.mjs +1 -0
  105. package/.output/server/_libs/pako.mjs +4223 -0
  106. package/.output/server/_libs/process-nextick-args.mjs +48 -0
  107. package/.output/server/_libs/property-information.mjs +1209 -0
  108. package/.output/server/_libs/radix-ui.mjs +1 -0
  109. package/.output/server/_libs/radix-ui__number.mjs +6 -0
  110. package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
  111. package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
  112. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
  113. package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
  114. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
  115. package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
  116. package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
  117. package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
  118. package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
  119. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
  120. package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
  121. package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
  122. package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
  123. package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
  124. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
  125. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
  126. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
  127. package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
  128. package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
  129. package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
  130. package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
  131. package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
  132. package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
  133. package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
  134. package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
  135. package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
  136. package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
  137. package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
  138. package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
  139. package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
  140. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
  141. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
  142. package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
  143. package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
  144. package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
  145. package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
  146. package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
  147. package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
  148. package/.output/server/_libs/radix-ui__react-toast.mjs +2 -0
  149. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
  150. package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
  151. package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
  152. package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
  153. package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
  154. package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
  155. package/.output/server/_libs/react-dom.mjs +10781 -0
  156. package/.output/server/_libs/react-markdown.mjs +147 -0
  157. package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
  158. package/.output/server/_libs/react-remove-scroll.mjs +328 -0
  159. package/.output/server/_libs/react-style-singleton.mjs +69 -0
  160. package/.output/server/_libs/react.mjs +515 -0
  161. package/.output/server/_libs/readable-stream.mjs +1518 -0
  162. package/.output/server/_libs/remark-parse.mjs +19 -0
  163. package/.output/server/_libs/remark-rehype.mjs +21 -0
  164. package/.output/server/_libs/rou3.mjs +14 -0
  165. package/.output/server/_libs/safe-buffer.mjs +64 -0
  166. package/.output/server/_libs/semver.mjs +1938 -0
  167. package/.output/server/_libs/seroval-plugins.mjs +58 -0
  168. package/.output/server/_libs/seroval.mjs +1765 -0
  169. package/.output/server/_libs/setimmediate.mjs +152 -0
  170. package/.output/server/_libs/space-separated-tokens.mjs +6 -0
  171. package/.output/server/_libs/srvx.mjs +1029 -0
  172. package/.output/server/_libs/stubborn-fs.mjs +91 -0
  173. package/.output/server/_libs/stubborn-utils.mjs +66 -0
  174. package/.output/server/_libs/style-to-js.mjs +72 -0
  175. package/.output/server/_libs/style-to-object.mjs +38 -0
  176. package/.output/server/_libs/swr.mjs +939 -0
  177. package/.output/server/_libs/tailwind-merge.mjs +3010 -0
  178. package/.output/server/_libs/tanstack__history.mjs +217 -0
  179. package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
  180. package/.output/server/_libs/tanstack__react-store.mjs +1 -0
  181. package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
  182. package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
  183. package/.output/server/_libs/tanstack__store.mjs +1 -0
  184. package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
  185. package/.output/server/_libs/tiny-invariant.mjs +12 -0
  186. package/.output/server/_libs/tiny-warning.mjs +5 -0
  187. package/.output/server/_libs/trim-lines.mjs +41 -0
  188. package/.output/server/_libs/trough.mjs +85 -0
  189. package/.output/server/_libs/tslib.mjs +1 -0
  190. package/.output/server/_libs/ufo.mjs +54 -0
  191. package/.output/server/_libs/uint8array-extras.mjs +69 -0
  192. package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
  193. package/.output/server/_libs/unified.mjs +661 -0
  194. package/.output/server/_libs/unist-util-is.mjs +100 -0
  195. package/.output/server/_libs/unist-util-position.mjs +27 -0
  196. package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
  197. package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
  198. package/.output/server/_libs/unist-util-visit.mjs +24 -0
  199. package/.output/server/_libs/unstorage.mjs +1 -0
  200. package/.output/server/_libs/use-callback-ref.mjs +66 -0
  201. package/.output/server/_libs/use-sidecar.mjs +106 -0
  202. package/.output/server/_libs/use-sync-external-store.mjs +64 -0
  203. package/.output/server/_libs/util-deprecate.mjs +12 -0
  204. package/.output/server/_libs/vfile-message.mjs +138 -0
  205. package/.output/server/_libs/vfile.mjs +467 -0
  206. package/.output/server/_libs/when-exit.mjs +53 -0
  207. package/.output/server/_libs/zod.mjs +4524 -0
  208. package/.output/server/_sessionId-wMLPvC5g.mjs +123 -0
  209. package/.output/server/_ssr/CompareDrawer-BU4V0uVf.mjs +1041 -0
  210. package/.output/server/_ssr/ProxyViewerContainer-BnRwFEnn.mjs +5972 -0
  211. package/.output/server/_ssr/ReplayDialog-C7dn9pd_.mjs +322 -0
  212. package/.output/server/_ssr/RequestAnatomy-C1rWpe9-.mjs +353 -0
  213. package/.output/server/_ssr/ResponseView-hGpPaYsf.mjs +602 -0
  214. package/.output/server/_ssr/StreamingChunkSequence-BRWI1r_G.mjs +302 -0
  215. package/.output/server/_ssr/index-BKURLVPz.mjs +118 -0
  216. package/.output/server/_ssr/index.mjs +1184 -0
  217. package/.output/server/_ssr/json-viewer-BBd2DtQP.mjs +515 -0
  218. package/.output/server/_ssr/router-BcZ0D6AB.mjs +6317 -0
  219. package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  220. package/.output/server/_tanstack-start-manifest_v-1y8ZVxRI.mjs +4 -0
  221. package/.output/server/index.mjs +436 -0
  222. package/.output/server/node_modules/tslib/modules/index.js +70 -0
  223. package/.output/server/node_modules/tslib/modules/package.json +3 -0
  224. package/.output/server/node_modules/tslib/package.json +47 -0
  225. package/.output/server/node_modules/tslib/tslib.js +484 -0
  226. package/.output/server/package.json +9 -0
  227. package/LICENSE +21 -0
  228. package/README.md +52 -0
  229. package/package.json +110 -0
  230. package/src/assets/favicon.svg +31 -0
  231. package/src/assets/logos/alibaba.svg +1 -0
  232. package/src/assets/logos/anthropic.svg +1 -0
  233. package/src/assets/logos/claude-code.svg +4 -0
  234. package/src/assets/logos/deepseek.svg +1 -0
  235. package/src/assets/logos/mcp.png +0 -0
  236. package/src/assets/logos/minimax.jpeg +0 -0
  237. package/src/assets/logos/openai.svg +1 -0
  238. package/src/assets/logos/opencode.svg +4 -0
  239. package/src/assets/logos/qwen.png +0 -0
  240. package/src/assets/logos/zhipuai.svg +219 -0
  241. package/src/cli/detect-tools.ts +147 -0
  242. package/src/cli/doctor.ts +521 -0
  243. package/src/cli/onboard.ts +224 -0
  244. package/src/cli/templates/command-onboard.ts +17 -0
  245. package/src/cli/templates/skill-onboard.ts +547 -0
  246. package/src/cli.ts +345 -0
  247. package/src/components/OnboardingBanner.tsx +67 -0
  248. package/src/components/ProxyViewer.tsx +545 -0
  249. package/src/components/ProxyViewerContainer.tsx +363 -0
  250. package/src/components/providers/ImportWizardDialog.tsx +349 -0
  251. package/src/components/providers/ProviderCard.tsx +474 -0
  252. package/src/components/providers/ProviderForm.tsx +494 -0
  253. package/src/components/providers/ProviderLogo.tsx +117 -0
  254. package/src/components/providers/ProvidersPanel.tsx +619 -0
  255. package/src/components/providers/SettingsDialog.tsx +202 -0
  256. package/src/components/proxy-viewer/CompareDrawer.tsx +893 -0
  257. package/src/components/proxy-viewer/ConversationGroup.tsx +107 -0
  258. package/src/components/proxy-viewer/ConversationHeader.tsx +300 -0
  259. package/src/components/proxy-viewer/LogEntry.tsx +543 -0
  260. package/src/components/proxy-viewer/LogEntryHeader.tsx +501 -0
  261. package/src/components/proxy-viewer/ReplayDialog.tsx +218 -0
  262. package/src/components/proxy-viewer/ResponseView.tsx +171 -0
  263. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +188 -0
  264. package/src/components/proxy-viewer/ThreadConnector.tsx +136 -0
  265. package/src/components/proxy-viewer/TurnGroup.tsx +337 -0
  266. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +98 -0
  267. package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +196 -0
  268. package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +53 -0
  269. package/src/components/proxy-viewer/anatomy/types.ts +39 -0
  270. package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +114 -0
  271. package/src/components/proxy-viewer/cacheTrend.ts +50 -0
  272. package/src/components/proxy-viewer/diff/DiffView.tsx +321 -0
  273. package/src/components/proxy-viewer/diff/computeDiff.ts +178 -0
  274. package/src/components/proxy-viewer/diff/index.ts +3 -0
  275. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +157 -0
  276. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +66 -0
  277. package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +21 -0
  278. package/src/components/proxy-viewer/formats/index.tsx +33 -0
  279. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +170 -0
  280. package/src/components/proxy-viewer/index.ts +9 -0
  281. package/src/components/proxy-viewer/lazy.ts +37 -0
  282. package/src/components/proxy-viewer/log-formats/anthropic.ts +194 -0
  283. package/src/components/proxy-viewer/log-formats/index.ts +23 -0
  284. package/src/components/proxy-viewer/log-formats/openai.ts +167 -0
  285. package/src/components/proxy-viewer/log-formats/types.ts +40 -0
  286. package/src/components/proxy-viewer/log-formats/unknown.ts +18 -0
  287. package/src/components/proxy-viewer/logEntryVisibility.ts +39 -0
  288. package/src/components/proxy-viewer/requestDiff.ts +277 -0
  289. package/src/components/proxy-viewer/useCopyFeedback.ts +36 -0
  290. package/src/components/proxy-viewer/useKeyboardNavigation.ts +190 -0
  291. package/src/components/proxy-viewer/viewerState.ts +66 -0
  292. package/src/components/ui/badge.tsx +47 -0
  293. package/src/components/ui/button.tsx +47 -0
  294. package/src/components/ui/collapsible.tsx +21 -0
  295. package/src/components/ui/confirm-dialog.tsx +51 -0
  296. package/src/components/ui/crab-logo.tsx +95 -0
  297. package/src/components/ui/crab-variants.tsx +467 -0
  298. package/src/components/ui/dialog.tsx +129 -0
  299. package/src/components/ui/json-expansion-button.tsx +56 -0
  300. package/src/components/ui/json-viewer-bulk.ts +97 -0
  301. package/src/components/ui/json-viewer.tsx +494 -0
  302. package/src/components/ui/mcp-logo.tsx +20 -0
  303. package/src/components/ui/scroll-area.tsx +54 -0
  304. package/src/components/ui/select.tsx +178 -0
  305. package/src/components/ui/separator.tsx +28 -0
  306. package/src/components/ui/tabs.tsx +88 -0
  307. package/src/components/ui/tooltip.tsx +51 -0
  308. package/src/index.css +11 -0
  309. package/src/knowledge/candidateStore.ts +63 -0
  310. package/src/knowledge/distiller.ts +98 -0
  311. package/src/knowledge/openclawClient.ts +118 -0
  312. package/src/knowledge/redactor.ts +80 -0
  313. package/src/knowledge/types.ts +84 -0
  314. package/src/lib/apiClient.ts +49 -0
  315. package/src/lib/export-logs.ts +51 -0
  316. package/src/lib/mask.ts +4 -0
  317. package/src/lib/objectUtils.ts +22 -0
  318. package/src/lib/providerContract.ts +26 -0
  319. package/src/lib/providerTestContract.ts +107 -0
  320. package/src/lib/runtimeConfig.ts +25 -0
  321. package/src/lib/serverPort.ts +41 -0
  322. package/src/lib/sessionUrl.ts +44 -0
  323. package/src/lib/stopReason.ts +58 -0
  324. package/src/lib/useOnboarding.ts +80 -0
  325. package/src/lib/useProviders.ts +30 -0
  326. package/src/lib/useStripConfig.ts +108 -0
  327. package/src/lib/utils.ts +21 -0
  328. package/src/mcp/loopback.ts +76 -0
  329. package/src/mcp/previewExtractor.ts +166 -0
  330. package/src/mcp/server.ts +396 -0
  331. package/src/mcp/toolHandlers.ts +341 -0
  332. package/src/proxy/chunkStorage.ts +112 -0
  333. package/src/proxy/claudeCodeStrip.ts +99 -0
  334. package/src/proxy/config.ts +172 -0
  335. package/src/proxy/constants.ts +47 -0
  336. package/src/proxy/dataDir.ts +86 -0
  337. package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
  338. package/src/proxy/formats/anthropic/handler.ts +71 -0
  339. package/src/proxy/formats/anthropic/index.ts +14 -0
  340. package/src/proxy/formats/anthropic/register.ts +4 -0
  341. package/src/proxy/formats/anthropic/schemas.ts +237 -0
  342. package/src/proxy/formats/anthropic/stream.ts +205 -0
  343. package/src/proxy/formats/handler.ts +46 -0
  344. package/src/proxy/formats/index.ts +12 -0
  345. package/src/proxy/formats/jsonSchema.ts +36 -0
  346. package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
  347. package/src/proxy/formats/openai/handler.ts +96 -0
  348. package/src/proxy/formats/openai/index.ts +25 -0
  349. package/src/proxy/formats/openai/provider.ts +50 -0
  350. package/src/proxy/formats/openai/register.ts +4 -0
  351. package/src/proxy/formats/openai/schemas.ts +187 -0
  352. package/src/proxy/formats/openai/stream.ts +206 -0
  353. package/src/proxy/formats/protocol.ts +50 -0
  354. package/src/proxy/formats/providerRegistry.ts +51 -0
  355. package/src/proxy/formats/providers/index.ts +3 -0
  356. package/src/proxy/formats/registry.ts +66 -0
  357. package/src/proxy/handler.ts +334 -0
  358. package/src/proxy/logFinalizer.ts +305 -0
  359. package/src/proxy/logFinalizer.worker.ts +24 -0
  360. package/src/proxy/logIndex.ts +268 -0
  361. package/src/proxy/logger.ts +179 -0
  362. package/src/proxy/openaiOrphanToolStrip.ts +142 -0
  363. package/src/proxy/providerImporters.ts +491 -0
  364. package/src/proxy/providers.ts +613 -0
  365. package/src/proxy/schemas.ts +209 -0
  366. package/src/proxy/sessionProcess.ts +140 -0
  367. package/src/proxy/sessionRuntime.ts +85 -0
  368. package/src/proxy/sessionSupervisor.ts +283 -0
  369. package/src/proxy/sessionWorkerEntry.ts +26 -0
  370. package/src/proxy/socketTracker.ts +255 -0
  371. package/src/proxy/store.ts +412 -0
  372. package/src/proxy/upstream.ts +90 -0
  373. package/src/router.tsx +16 -0
  374. package/src/routes/__root.tsx +45 -0
  375. package/src/routes/api/config.paths.ts +14 -0
  376. package/src/routes/api/config.ts +53 -0
  377. package/src/routes/api/health.ts +15 -0
  378. package/src/routes/api/knowledge.candidates.$candidateId.promote.ts +32 -0
  379. package/src/routes/api/knowledge.candidates.ts +10 -0
  380. package/src/routes/api/knowledge.project-context.ts +18 -0
  381. package/src/routes/api/knowledge.search.ts +31 -0
  382. package/src/routes/api/knowledge.sessions.$sessionId.candidates.ts +16 -0
  383. package/src/routes/api/logs.$id.chunks.ts +36 -0
  384. package/src/routes/api/logs.$id.replay.ts +191 -0
  385. package/src/routes/api/logs.$id.ts +22 -0
  386. package/src/routes/api/logs.stream.ts +74 -0
  387. package/src/routes/api/logs.ts +59 -0
  388. package/src/routes/api/mcp.ts +25 -0
  389. package/src/routes/api/models.ts +10 -0
  390. package/src/routes/api/providers.$providerId.test.log.ts +293 -0
  391. package/src/routes/api/providers.$providerId.ts +50 -0
  392. package/src/routes/api/providers.export.ts +26 -0
  393. package/src/routes/api/providers.import.ts +47 -0
  394. package/src/routes/api/providers.scan.ts +23 -0
  395. package/src/routes/api/providers.ts +45 -0
  396. package/src/routes/api/sessions.ts +17 -0
  397. package/src/routes/index.tsx +6 -0
  398. package/src/routes/proxy/$.ts +15 -0
  399. package/src/routes/session/$sessionId.tsx +23 -0
  400. package/styles/globals.css +188 -0
@@ -0,0 +1,494 @@
1
+ import { type JSX, useState, useEffect, useRef } from "react";
2
+ import { Button } from "../ui/button";
3
+ import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
4
+ import { Eye, EyeOff, Copy, Check, ChevronDown } from "lucide-react";
5
+ import type { ProviderConfig } from "../../proxy/providers";
6
+ import { maskApiKey } from "../../lib/mask";
7
+
8
+ // Known provider presets - maps provider name keywords to their API URLs
9
+ const KNOWN_PROVIDER_PRESETS: Record<
10
+ string,
11
+ { format: "anthropic" | "openai"; baseUrl: string; apiDocsUrl?: string }
12
+ > = {
13
+ deepseek: {
14
+ format: "openai",
15
+ baseUrl: "https://api.deepseek.com",
16
+ },
17
+ minimax: {
18
+ format: "anthropic",
19
+ baseUrl: "https://api.minimaxi.com/anthropic",
20
+ apiDocsUrl: "https://platform.minimaxi.com/docs/api-reference/api-overview",
21
+ },
22
+ alibaba: {
23
+ format: "openai",
24
+ baseUrl: "https://dashscope.aliyuncs.com/compatible-mode",
25
+ },
26
+ };
27
+
28
+ // MiniMax model options
29
+ const MINIMAX_MODELS = [
30
+ "MiniMax M3",
31
+ "MiniMax M2.7",
32
+ "MiniMax M2.7-highspeed",
33
+ "MiniMax M2.5",
34
+ "MiniMax M2.5-highspeed",
35
+ "MiniMax M2.1",
36
+ "MiniMax M2.1-highspeed",
37
+ "MiniMax M2",
38
+ ];
39
+
40
+ // Alibaba model options
41
+ const ALIBABA_MODELS = ["glm-5", "glm-5.1", "qwen3.6-plus", "qwen3.7-max"];
42
+
43
+ type ProviderFormProps = {
44
+ provider?: ProviderConfig;
45
+ onSubmit: (data: {
46
+ name: string;
47
+ apiKey: string;
48
+ models: string[];
49
+ anthropicBaseUrl?: string;
50
+ openaiBaseUrl?: string;
51
+ apiDocsUrl?: string;
52
+ source?: "company" | "personal";
53
+ }) => void;
54
+ onCancel: () => void;
55
+ };
56
+
57
+ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps): JSX.Element {
58
+ const [name, setName] = useState(provider?.name ?? "");
59
+ const [apiKey, setApiKey] = useState(provider?.apiKey ?? "");
60
+ const [showApiKey, setShowApiKey] = useState(false);
61
+ const [copied, setCopied] = useState(false);
62
+ const initialModels = provider?.models;
63
+ const [models, setModels] = useState<string[]>(
64
+ initialModels !== undefined && initialModels.length > 0 ? initialModels : [""],
65
+ );
66
+ const [activeTab, setActiveTab] = useState<"anthropic" | "openai">("anthropic");
67
+ const [anthropicBaseUrl, setAnthropicBaseUrl] = useState(provider?.anthropicBaseUrl ?? "");
68
+ const [openaiBaseUrl, setOpenaiBaseUrl] = useState(provider?.openaiBaseUrl ?? "");
69
+ const [apiDocsUrl, setApiDocsUrl] = useState(provider?.apiDocsUrl ?? "");
70
+ const [source, setSource] = useState<"company" | "personal" | undefined>(provider?.source);
71
+ const [errors, setErrors] = useState<Record<string, string>>({});
72
+ const [isSubmitting, setIsSubmitting] = useState(false);
73
+ const [openModelDropdown, setOpenModelDropdown] = useState<number | null>(null);
74
+ const modelRowRefs = useRef<(HTMLDivElement | null)[]>([]);
75
+
76
+ // Close model dropdown when clicking outside
77
+ useEffect(() => {
78
+ if (openModelDropdown === null) return;
79
+ const index = openModelDropdown;
80
+ function handleClick(e: MouseEvent) {
81
+ if (!(e.target instanceof Node)) return;
82
+ const ref = modelRowRefs.current[index];
83
+ if (ref !== null && ref !== undefined && !ref.contains(e.target)) {
84
+ setOpenModelDropdown(null);
85
+ }
86
+ }
87
+ document.addEventListener("mousedown", handleClick);
88
+ return () => document.removeEventListener("mousedown", handleClick);
89
+ }, [openModelDropdown]);
90
+
91
+ // Track if URL fields have been manually edited (to avoid overriding user edits)
92
+ const [manualAnthropicUrlOverride, setManualAnthropicUrlOverride] = useState(false);
93
+ const [manualOpenaiUrlOverride, setManualOpenaiUrlOverride] = useState(false);
94
+
95
+ // Check if MiniMax is detected
96
+ const isMiniMax = name.toLowerCase().includes("minimax");
97
+ // Check if Alibaba is detected
98
+ const isAlibaba = name.toLowerCase().includes("alibaba");
99
+
100
+ function handleCopy() {
101
+ navigator.clipboard.writeText(apiKey).catch(() => {});
102
+ setCopied(true);
103
+ setTimeout(() => setCopied(false), 2000);
104
+ }
105
+
106
+ useEffect(() => {
107
+ if (provider) {
108
+ setName(provider.name);
109
+ setApiKey(provider.apiKey);
110
+ setModels((provider.models?.length ?? 0) > 0 ? provider.models : [""]);
111
+ setAnthropicBaseUrl(provider.anthropicBaseUrl ?? "");
112
+ setOpenaiBaseUrl(provider.openaiBaseUrl ?? "");
113
+ setApiDocsUrl(provider.apiDocsUrl ?? "");
114
+ setSource(provider.source);
115
+ setManualAnthropicUrlOverride(false);
116
+ setManualOpenaiUrlOverride(false);
117
+ }
118
+ }, [provider]);
119
+
120
+ // Detect known provider presets and auto-fill URLs
121
+ useEffect(() => {
122
+ const lowerName = name.toLowerCase();
123
+ for (const [keyword, preset] of Object.entries(KNOWN_PROVIDER_PRESETS)) {
124
+ if (lowerName.includes(keyword)) {
125
+ if (preset.format === "anthropic" && !manualAnthropicUrlOverride) {
126
+ setAnthropicBaseUrl(preset.baseUrl);
127
+ } else if (preset.format === "openai" && !manualOpenaiUrlOverride) {
128
+ setOpenaiBaseUrl(preset.baseUrl);
129
+ }
130
+ if (preset.apiDocsUrl !== undefined && !apiDocsUrl) {
131
+ setApiDocsUrl(preset.apiDocsUrl);
132
+ }
133
+ // For MiniMax, auto-select the first model if not already set
134
+ if (keyword === "minimax" && models.length === 1 && models[0] === "") {
135
+ setModels([MINIMAX_MODELS[0] ?? ""]);
136
+ }
137
+ // For Alibaba, auto-select the first model if not already set
138
+ if (keyword === "alibaba" && models.length === 1 && models[0] === "") {
139
+ setModels([ALIBABA_MODELS[0] ?? ""]);
140
+ }
141
+ break;
142
+ }
143
+ }
144
+ }, [name]);
145
+
146
+ function validate(): boolean {
147
+ const newErrors: Record<string, string> = {};
148
+ if (!name.trim()) {
149
+ newErrors.name = "Name is required";
150
+ }
151
+ if (!apiKey.trim()) {
152
+ newErrors.apiKey = "API key is required";
153
+ }
154
+ if (models.length === 0 || models.every((m) => !m.trim())) {
155
+ newErrors.models = "At least one model is required";
156
+ }
157
+ if (anthropicBaseUrl.trim() && !isValidUrl(anthropicBaseUrl.trim())) {
158
+ newErrors.anthropicBaseUrl = "Invalid URL format";
159
+ }
160
+ if (openaiBaseUrl.trim() && !isValidUrl(openaiBaseUrl.trim())) {
161
+ newErrors.openaiBaseUrl = "Invalid URL format";
162
+ }
163
+ if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim()) {
164
+ newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
165
+ }
166
+ setErrors(newErrors);
167
+ return Object.keys(newErrors).length === 0;
168
+ }
169
+
170
+ function isValidUrl(str: string): boolean {
171
+ try {
172
+ new URL(str);
173
+ return true;
174
+ } catch {
175
+ return false;
176
+ }
177
+ }
178
+
179
+ function handleSubmit(e: React.FormEvent): void {
180
+ e.preventDefault();
181
+ if (!validate()) return;
182
+ setIsSubmitting(true);
183
+ try {
184
+ onSubmit({
185
+ name: name.trim(),
186
+ apiKey: apiKey.trim(),
187
+ models: models.map((m) => m.trim()).filter((m) => m !== ""),
188
+ anthropicBaseUrl: anthropicBaseUrl.trim() || undefined,
189
+ openaiBaseUrl: openaiBaseUrl.trim() || undefined,
190
+ apiDocsUrl: apiDocsUrl.trim() || undefined,
191
+ source,
192
+ });
193
+ } finally {
194
+ setIsSubmitting(false);
195
+ }
196
+ }
197
+
198
+ return (
199
+ <form onSubmit={handleSubmit} className="space-y-4">
200
+ <div className="space-y-2">
201
+ <label htmlFor="provider-name" className="text-sm font-medium">
202
+ Name <span className="text-destructive">*</span>
203
+ </label>
204
+ <input
205
+ id="provider-name"
206
+ type="text"
207
+ value={name}
208
+ onChange={(e) => setName(e.target.value)}
209
+ placeholder="Provider Name"
210
+ 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"
211
+ />
212
+ {errors.name !== undefined && <p className="text-xs text-destructive">{errors.name}</p>}
213
+ </div>
214
+
215
+ <div className="space-y-2">
216
+ <label htmlFor="provider-source" className="text-sm font-medium">
217
+ Token Source
218
+ </label>
219
+ <select
220
+ id="provider-source"
221
+ value={source ?? ""}
222
+ onChange={(e) =>
223
+ setSource(
224
+ e.target.value === "company" || e.target.value === "personal"
225
+ ? e.target.value
226
+ : undefined,
227
+ )
228
+ }
229
+ 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"
230
+ >
231
+ <option value="">—</option>
232
+ <option value="personal">个人 (Personal)</option>
233
+ <option value="company">公司 (Company)</option>
234
+ </select>
235
+ <p className="text-xs text-muted-foreground">
236
+ Label whether this API key is company-provided or personal.
237
+ </p>
238
+ </div>
239
+
240
+ <div className="space-y-2">
241
+ <label htmlFor="provider-apikey" className="text-sm font-medium">
242
+ API Key <span className="text-destructive">*</span>
243
+ </label>
244
+ <div className="flex items-center gap-2">
245
+ <input
246
+ id="provider-apikey"
247
+ type="text"
248
+ value={showApiKey || apiKey.length === 0 ? apiKey : maskApiKey(apiKey)}
249
+ onChange={(e) => setApiKey(e.target.value)}
250
+ onFocus={() => {
251
+ if (!showApiKey && apiKey.length > 0) setShowApiKey(true);
252
+ }}
253
+ placeholder="sk-ant-..."
254
+ readOnly={!showApiKey && apiKey.length > 0}
255
+ className="flex-1 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"
256
+ />
257
+ <button
258
+ type="button"
259
+ onClick={() => setShowApiKey((s) => !s)}
260
+ className="text-muted-foreground hover:text-foreground transition-colors p-1 shrink-0"
261
+ aria-label={showApiKey ? "Hide API key" : "Show API key"}
262
+ >
263
+ {showApiKey ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
264
+ </button>
265
+ <button
266
+ type="button"
267
+ onClick={handleCopy}
268
+ className="text-muted-foreground hover:text-foreground transition-colors p-1 shrink-0"
269
+ aria-label="Copy API key"
270
+ >
271
+ {copied ? <Check className="size-4 text-green-500" /> : <Copy className="size-4" />}
272
+ </button>
273
+ </div>
274
+ {errors.apiKey !== undefined && <p className="text-xs text-destructive">{errors.apiKey}</p>}
275
+ </div>
276
+
277
+ <div className="space-y-2">
278
+ <label className="text-sm font-medium">
279
+ Models <span className="text-destructive">*</span>
280
+ </label>
281
+ {models.map((m, i) => (
282
+ <div
283
+ key={i}
284
+ ref={(el) => {
285
+ modelRowRefs.current[i] = el;
286
+ }}
287
+ className="flex items-center gap-2"
288
+ >
289
+ <div className="relative flex-1">
290
+ <input
291
+ type="text"
292
+ value={m}
293
+ onChange={(e) => {
294
+ setModels((prev) => {
295
+ const next = [...prev];
296
+ next[i] = e.target.value;
297
+ return next;
298
+ });
299
+ }}
300
+ placeholder={isMiniMax || isAlibaba ? "Type or select a model..." : "Model name"}
301
+ className="w-full rounded-md border border-input bg-background px-4 py-3 pr-8 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"
302
+ />
303
+ {(isMiniMax || isAlibaba) && (
304
+ <>
305
+ <button
306
+ type="button"
307
+ onClick={() => setOpenModelDropdown(openModelDropdown === i ? null : i)}
308
+ className="absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors p-1 z-10"
309
+ aria-label="Show model suggestions"
310
+ >
311
+ <ChevronDown className="size-4" />
312
+ </button>
313
+ {openModelDropdown === i && (
314
+ <div className="absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto">
315
+ {(isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => (
316
+ <button
317
+ key={opt}
318
+ type="button"
319
+ onClick={() => {
320
+ setModels((prev) => {
321
+ const next = [...prev];
322
+ next[i] = opt;
323
+ return next;
324
+ });
325
+ setOpenModelDropdown(null);
326
+ }}
327
+ className="w-full text-left px-3 py-2 text-sm hover:bg-muted transition-colors"
328
+ >
329
+ {opt}
330
+ </button>
331
+ ))}
332
+ </div>
333
+ )}
334
+ </>
335
+ )}
336
+ </div>
337
+ {models.length > 1 && (
338
+ <button
339
+ type="button"
340
+ onClick={() => setModels((prev) => prev.filter((_, idx) => idx !== i))}
341
+ className="text-muted-foreground hover:text-destructive transition-colors p-1 shrink-0"
342
+ aria-label="Remove model"
343
+ >
344
+ <svg
345
+ xmlns="http://www.w3.org/2000/svg"
346
+ width="16"
347
+ height="16"
348
+ viewBox="0 0 24 24"
349
+ fill="none"
350
+ stroke="currentColor"
351
+ strokeWidth="2"
352
+ strokeLinecap="round"
353
+ strokeLinejoin="round"
354
+ >
355
+ <path d="M3 6h18" />
356
+ <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
357
+ <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
358
+ </svg>
359
+ </button>
360
+ )}
361
+ </div>
362
+ ))}
363
+ <button
364
+ type="button"
365
+ onClick={() => setModels((prev) => [...prev, ""])}
366
+ className="text-xs text-primary hover:underline flex items-center gap-1"
367
+ >
368
+ + Add Model
369
+ </button>
370
+ {errors.models !== undefined && <p className="text-xs text-destructive">{errors.models}</p>}
371
+ </div>
372
+
373
+ <div className="space-y-2">
374
+ <div className="flex gap-1 border-b border-border">
375
+ <TooltipProvider>
376
+ <Tooltip>
377
+ <TooltipTrigger asChild>
378
+ <button
379
+ type="button"
380
+ onClick={() => setActiveTab("anthropic")}
381
+ className={`px-3 py-2 text-sm font-medium border-b-2 transition-colors ${
382
+ activeTab === "anthropic"
383
+ ? "border-primary text-primary"
384
+ : "border-transparent text-muted-foreground hover:text-foreground"
385
+ }`}
386
+ >
387
+ Anthropic Format
388
+ </button>
389
+ </TooltipTrigger>
390
+ <TooltipContent>Anthropic Messages API protocol</TooltipContent>
391
+ </Tooltip>
392
+ </TooltipProvider>
393
+ <TooltipProvider>
394
+ <Tooltip>
395
+ <TooltipTrigger asChild>
396
+ <button
397
+ type="button"
398
+ onClick={() => setActiveTab("openai")}
399
+ className={`px-3 py-2 text-sm font-medium border-b-2 transition-colors ${
400
+ activeTab === "openai"
401
+ ? "border-primary text-primary"
402
+ : "border-transparent text-muted-foreground hover:text-foreground"
403
+ }`}
404
+ >
405
+ OpenAI Format
406
+ </button>
407
+ </TooltipTrigger>
408
+ <TooltipContent>OpenAI Chat Completions API protocol</TooltipContent>
409
+ </Tooltip>
410
+ </TooltipProvider>
411
+ </div>
412
+ {errors.format !== undefined && <p className="text-xs text-destructive">{errors.format}</p>}
413
+ </div>
414
+
415
+ {activeTab === "anthropic" && (
416
+ <div className="space-y-2">
417
+ <label htmlFor="provider-anthropic-base-url" className="text-sm font-medium">
418
+ Anthropic Base URL
419
+ </label>
420
+ <input
421
+ id="provider-anthropic-base-url"
422
+ type="text"
423
+ value={anthropicBaseUrl}
424
+ onChange={(e) => {
425
+ setManualAnthropicUrlOverride(true);
426
+ setAnthropicBaseUrl(e.target.value);
427
+ }}
428
+ placeholder="https://api.anthropic.com"
429
+ 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"
430
+ />
431
+ {errors.anthropicBaseUrl !== undefined && (
432
+ <p className="text-xs text-destructive">{errors.anthropicBaseUrl}</p>
433
+ )}
434
+ <p className="text-xs text-muted-foreground">
435
+ Anthropic-compatible endpoint URL. Leave empty if this provider does not support
436
+ Anthropic format.
437
+ </p>
438
+ </div>
439
+ )}
440
+
441
+ {activeTab === "openai" && (
442
+ <div className="space-y-2">
443
+ <label htmlFor="provider-openai-base-url" className="text-sm font-medium">
444
+ OpenAI Base URL
445
+ </label>
446
+ <input
447
+ id="provider-openai-base-url"
448
+ type="text"
449
+ value={openaiBaseUrl}
450
+ onChange={(e) => {
451
+ setManualOpenaiUrlOverride(true);
452
+ setOpenaiBaseUrl(e.target.value);
453
+ }}
454
+ placeholder="https://api.openai.com/v1"
455
+ 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"
456
+ />
457
+ {errors.openaiBaseUrl !== undefined && (
458
+ <p className="text-xs text-destructive">{errors.openaiBaseUrl}</p>
459
+ )}
460
+ <p className="text-xs text-muted-foreground">
461
+ OpenAI-compatible endpoint URL. Leave empty if this provider does not support OpenAI
462
+ format.
463
+ </p>
464
+ </div>
465
+ )}
466
+
467
+ <div className="space-y-2">
468
+ <label htmlFor="provider-api-docs-url" className="text-sm font-medium">
469
+ API Docs URL
470
+ </label>
471
+ <input
472
+ id="provider-api-docs-url"
473
+ type="text"
474
+ value={apiDocsUrl}
475
+ onChange={(e) => setApiDocsUrl(e.target.value)}
476
+ placeholder="https://api.example.com/docs"
477
+ 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"
478
+ />
479
+ <p className="text-xs text-muted-foreground">
480
+ Optional API documentation URL. If not set, uses known provider docs.
481
+ </p>
482
+ </div>
483
+
484
+ <div className="sticky bottom-0 bg-card border-t flex gap-2 justify-end pt-2 pb-2">
485
+ <Button type="button" variant="outline" onClick={onCancel} disabled={isSubmitting}>
486
+ Cancel
487
+ </Button>
488
+ <Button type="submit" disabled={isSubmitting}>
489
+ {isSubmitting ? "Saving..." : provider ? "Update Provider" : "Add Provider"}
490
+ </Button>
491
+ </div>
492
+ </form>
493
+ );
494
+ }
@@ -0,0 +1,117 @@
1
+ import type { JSX } from "react";
2
+ import React from "react";
3
+ import AnthropicLogoSvg from "../../assets/logos/anthropic.svg";
4
+ import OpenAILogoSvg from "../../assets/logos/openai.svg";
5
+ import DeepSeekLogoSvg from "../../assets/logos/deepseek.svg";
6
+ import MiniMaxLogoSvg from "../../assets/logos/minimax.jpeg";
7
+ import AlibabaLogoSvg from "../../assets/logos/alibaba.svg";
8
+ import QwenLogoSvg from "../../assets/logos/qwen.png";
9
+ import ZhipuAILogoSvg from "../../assets/logos/zhipuai.svg";
10
+
11
+ export type Provider =
12
+ | "anthropic"
13
+ | "openai"
14
+ | "deepseek"
15
+ | "minimax"
16
+ | "alibaba"
17
+ | "qwen"
18
+ | "zhipuai"
19
+ | "unknown";
20
+
21
+ const PROVIDER_MAP: Record<string, Provider> = {
22
+ "claude-": "anthropic",
23
+ "gpt-": "openai",
24
+ "o1-": "openai",
25
+ "o3-": "openai",
26
+ "deepseek-": "deepseek",
27
+ MiniMax: "minimax",
28
+ qwen: "qwen",
29
+ "glm-": "zhipuai",
30
+ };
31
+
32
+ export function detectProvider(model: string | null): Provider {
33
+ if (model === null) return "unknown";
34
+ const modelLower = model.toLowerCase();
35
+ for (const [prefix, provider] of Object.entries(PROVIDER_MAP)) {
36
+ if (modelLower.startsWith(prefix.toLowerCase())) {
37
+ return provider;
38
+ }
39
+ }
40
+ return "unknown";
41
+ }
42
+
43
+ const sizeStyle = { width: 24, height: 24, objectFit: "contain" as const };
44
+
45
+ const AnthropicLogo = React.memo(
46
+ ({ className }: { className?: string }): JSX.Element => (
47
+ <img src={AnthropicLogoSvg} alt="Anthropic" className={className} style={sizeStyle} />
48
+ ),
49
+ );
50
+
51
+ const OpenAILogo = React.memo(
52
+ ({ className }: { className?: string }): JSX.Element => (
53
+ <img
54
+ src={OpenAILogoSvg}
55
+ alt="OpenAI"
56
+ className={className}
57
+ style={{ ...sizeStyle, background: "white", borderRadius: "4px" }}
58
+ />
59
+ ),
60
+ );
61
+
62
+ const DeepSeekLogo = React.memo(
63
+ ({ className }: { className?: string }): JSX.Element => (
64
+ <img src={DeepSeekLogoSvg} alt="DeepSeek" className={className} style={sizeStyle} />
65
+ ),
66
+ );
67
+
68
+ const MiniMaxLogo = React.memo(
69
+ ({ className }: { className?: string }): JSX.Element => (
70
+ <img src={MiniMaxLogoSvg} alt="MiniMax" className={className} style={sizeStyle} />
71
+ ),
72
+ );
73
+
74
+ const AlibabaLogo = React.memo(
75
+ ({ className }: { className?: string }): JSX.Element => (
76
+ <img src={AlibabaLogoSvg} alt="Alibaba" className={className} style={sizeStyle} />
77
+ ),
78
+ );
79
+
80
+ const QwenLogo = React.memo(
81
+ ({ className }: { className?: string }): JSX.Element => (
82
+ <img src={QwenLogoSvg} alt="Qwen" className={className} style={sizeStyle} />
83
+ ),
84
+ );
85
+
86
+ const ZhipuAILogo = React.memo(
87
+ ({ className }: { className?: string }): JSX.Element => (
88
+ <img src={ZhipuAILogoSvg} alt="ZhipuAI" className={className} style={sizeStyle} />
89
+ ),
90
+ );
91
+
92
+ export function ProviderLogo({
93
+ provider,
94
+ className,
95
+ }: {
96
+ provider: Provider;
97
+ className?: string;
98
+ }): JSX.Element | null {
99
+ switch (provider) {
100
+ case "anthropic":
101
+ return <AnthropicLogo className={className} />;
102
+ case "openai":
103
+ return <OpenAILogo className={className} />;
104
+ case "deepseek":
105
+ return <DeepSeekLogo className={className} />;
106
+ case "minimax":
107
+ return <MiniMaxLogo className={className} />;
108
+ case "alibaba":
109
+ return <AlibabaLogo className={className} />;
110
+ case "qwen":
111
+ return <QwenLogo className={className} />;
112
+ case "zhipuai":
113
+ return <ZhipuAILogo className={className} />;
114
+ case "unknown":
115
+ return null;
116
+ }
117
+ }