@cc-monitoring/proxy-core 0.1.4 → 0.1.5

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 (318) hide show
  1. package/README.md +11 -35
  2. package/dist/cli/commands/doc.d.ts +4 -0
  3. package/dist/cli/commands/doc.js +31 -0
  4. package/dist/cli/index.js +7 -0
  5. package/dist/config/index.js +2 -1
  6. package/dist/config/schema.d.ts +48 -0
  7. package/dist/config/schema.js +11 -1
  8. package/dist/parsers/docx-loader.d.ts +5 -0
  9. package/dist/parsers/docx-loader.js +15 -0
  10. package/dist/parsers/index.d.ts +3 -0
  11. package/dist/parsers/index.js +10 -0
  12. package/dist/parsers/md-loader.d.ts +5 -0
  13. package/dist/parsers/md-loader.js +13 -0
  14. package/dist/parsers/registry.d.ts +8 -0
  15. package/dist/parsers/registry.js +32 -0
  16. package/dist/parsers/types.d.ts +4 -0
  17. package/dist/parsers/types.js +1 -0
  18. package/dist/parsers/xlsx-loader.d.ts +5 -0
  19. package/dist/parsers/xlsx-loader.js +34 -0
  20. package/dist/server/__tests__/providers-route.test.js +4 -3
  21. package/dist/server/__tests__/proxy.test.js +11 -1
  22. package/dist/server/chat-route.d.ts +3 -0
  23. package/dist/server/chat-route.js +401 -0
  24. package/dist/server/claude-settings-route.d.ts +2 -0
  25. package/dist/server/claude-settings-route.js +76 -0
  26. package/dist/server/data-push.d.ts +3 -1
  27. package/dist/server/data-push.js +10 -3
  28. package/dist/server/docs-route.d.ts +2 -0
  29. package/dist/server/docs-route.js +63 -0
  30. package/dist/server/index.js +4 -0
  31. package/dist/server/mcp-route.d.ts +2 -0
  32. package/dist/server/mcp-route.js +161 -0
  33. package/dist/server/proxy.d.ts +4 -1
  34. package/dist/server/proxy.js +289 -128
  35. package/dist/server/ui-route.js +123 -46
  36. package/dist/tools/mcp-bridge.d.ts +13 -0
  37. package/dist/tools/mcp-bridge.js +36 -0
  38. package/dist/tools/registry.js +10 -4
  39. package/dist/ui/assets/{1c-CgVqmGt6.js → 1c-KmWe8RWs.js} +1 -1
  40. package/dist/ui/assets/{abnf-JPM4b6fj.js → abnf-BKQ6qIuo.js} +1 -1
  41. package/dist/ui/assets/{abnfDiagram-VRR7QNED-DJkd8PHl.js → abnfDiagram-VRR7QNED-CkRuDPu1.js} +1 -1
  42. package/dist/ui/assets/{accesslog-CLIMhXWi.js → accesslog-DD9H5Ktc.js} +1 -1
  43. package/dist/ui/assets/{actionscript-DqniQGld.js → actionscript-CAjf2Qtd.js} +1 -1
  44. package/dist/ui/assets/{ada-2BFfY7fF.js → ada-BXkXW16O.js} +1 -1
  45. package/dist/ui/assets/{angelscript-C47qRqcs.js → angelscript-BxT7o0Up.js} +1 -1
  46. package/dist/ui/assets/{apache-DP-LC5-h.js → apache-C5z_tmVm.js} +1 -1
  47. package/dist/ui/assets/{applescript-BB0gk3HQ.js → applescript-DdIxl8rr.js} +1 -1
  48. package/dist/ui/assets/{arc-D8wRCC4D.js → arc-hSdFaqKn.js} +1 -1
  49. package/dist/ui/assets/{arcade-1R6g1VPm.js → arcade-D5m0roat.js} +1 -1
  50. package/dist/ui/assets/{architectureDiagram-ZJ3FMSHR-CbHhHV9v.js → architectureDiagram-ZJ3FMSHR-DOSHSqzf.js} +1 -1
  51. package/dist/ui/assets/{arduino-B8YryK0R.js → arduino-FDap8kq-.js} +1 -1
  52. package/dist/ui/assets/{armasm-CYdj7mzR.js → armasm-Kzp-Jr1v.js} +1 -1
  53. package/dist/ui/assets/{asciidoc-bZ11zz6i.js → asciidoc-DbGMW3EE.js} +1 -1
  54. package/dist/ui/assets/{aspectj-D5WPU-RQ.js → aspectj-CVYMmBCO.js} +1 -1
  55. package/dist/ui/assets/{autohotkey-70tZvCLL.js → autohotkey-9PmBGTPd.js} +1 -1
  56. package/dist/ui/assets/{autoit-CX2GaaHP.js → autoit-B4F86jgh.js} +1 -1
  57. package/dist/ui/assets/{avrasm-C154El0d.js → avrasm-CSzv2eHX.js} +1 -1
  58. package/dist/ui/assets/{awk-DdXKOeux.js → awk-CjsvYvZE.js} +1 -1
  59. package/dist/ui/assets/{axapta-CwLwkXST.js → axapta-BVgMoNRY.js} +1 -1
  60. package/dist/ui/assets/{bash-Uu61S1nB.js → bash-IeBjW3pt.js} +1 -1
  61. package/dist/ui/assets/{basic-CeiAWoiF.js → basic-BTFJfmV1.js} +1 -1
  62. package/dist/ui/assets/{blockDiagram-677ZJIJ3-DcvhphUd.js → blockDiagram-677ZJIJ3-DLw3Bbhf.js} +1 -1
  63. package/dist/ui/assets/{bnf-B5KF61QW.js → bnf-rCkeRYk0.js} +1 -1
  64. package/dist/ui/assets/{brainfuck-CP0Y7_VW.js → brainfuck-DBHuB3uZ.js} +1 -1
  65. package/dist/ui/assets/{coq-D-WTlq_d.js → c-C95V0zHL.js} +1 -1
  66. package/dist/ui/assets/{c-like-DXr53gS6.js → c-like-CK1xGeX_.js} +1 -1
  67. package/dist/ui/assets/{c4Diagram-LMCZKHZV-Cn017lmJ.js → c4Diagram-LMCZKHZV-cqY51yJw.js} +1 -1
  68. package/dist/ui/assets/{cal-7ld1j-Wh.js → cal-BxGPpEZK.js} +1 -1
  69. package/dist/ui/assets/{capnproto-DrddNBCZ.js → capnproto-Bo2fgXF9.js} +1 -1
  70. package/dist/ui/assets/{ceylon-XPuE6IIL.js → ceylon-C4Bb4pNB.js} +1 -1
  71. package/dist/ui/assets/channel-D9Y4iN1l.js +1 -0
  72. package/dist/ui/assets/{chunk-2Q5K7J3B-BoX3IESS.js → chunk-2Q5K7J3B-CGTiTBmo.js} +1 -1
  73. package/dist/ui/assets/{chunk-32BRIVSS-C300ACzq.js → chunk-32BRIVSS-DWagYBp9.js} +1 -1
  74. package/dist/ui/assets/{chunk-5VM5RSS4-MtSkFsde.js → chunk-5VM5RSS4-BzSBMKJX.js} +1 -1
  75. package/dist/ui/assets/{chunk-EX3LRPZG-CEeHrGNV.js → chunk-EX3LRPZG-4-aKwCgZ.js} +1 -1
  76. package/dist/ui/assets/{chunk-JWPE2WC7-DTRgpK0R.js → chunk-JWPE2WC7-CPC_cBN0.js} +1 -1
  77. package/dist/ui/assets/{chunk-MOJQB5TN-DiOl0HJx.js → chunk-MOJQB5TN-DrDE4H9w.js} +1 -1
  78. package/dist/ui/assets/{chunk-RYQCIY6F-CVC3opqY.js → chunk-RYQCIY6F-DkdXe_kU.js} +1 -1
  79. package/dist/ui/assets/{chunk-V7JOEXUC-yQYlqQCC.js → chunk-V7JOEXUC-Da6EWcQt.js} +1 -1
  80. package/dist/ui/assets/{chunk-VR4S4FIN-iAA8ZgkK.js → chunk-VR4S4FIN-nP6dstD5.js} +1 -1
  81. package/dist/ui/assets/{chunk-XXDRQBXY-B303eieF.js → chunk-XXDRQBXY-Bi_rRAPA.js} +1 -1
  82. package/dist/ui/assets/classDiagram-OUVF2IWQ-C4UVH6RI.js +1 -0
  83. package/dist/ui/assets/classDiagram-v2-EOCWNBFH-C4UVH6RI.js +1 -0
  84. package/dist/ui/assets/{clean-MfHaW_uS.js → clean-cc1m9AxK.js} +1 -1
  85. package/dist/ui/assets/{clojure-9L8TfiWA.js → clojure-DX5V8QA8.js} +1 -1
  86. package/dist/ui/assets/{clojure-repl-CjsH9sxr.js → clojure-repl-FzjnVNWQ.js} +1 -1
  87. package/dist/ui/assets/{cmake-D0D1Q30G.js → cmake-COZZyrKs.js} +1 -1
  88. package/dist/ui/assets/{coffeescript-BI4jWZnB.js → coffeescript-DBsnOmC4.js} +1 -1
  89. package/dist/ui/assets/{c-BgqCrSpv.js → coq-TwFFolHa.js} +1 -1
  90. package/dist/ui/assets/{cos-DHax5LIJ.js → cos-Dm5g-IQT.js} +1 -1
  91. package/dist/ui/assets/{cose-bilkent-JH36ORCC-BzrKmTAZ.js → cose-bilkent-JH36ORCC-B3-weOQF.js} +1 -1
  92. package/dist/ui/assets/{cpp-M4aMPUas.js → cpp-_RZxQjg7.js} +1 -1
  93. package/dist/ui/assets/{crmsh-DAAczfR0.js → crmsh-DGPJ11Gt.js} +1 -1
  94. package/dist/ui/assets/{crystal-7g2CpWm4.js → crystal-BOMSPGge.js} +1 -1
  95. package/dist/ui/assets/{csharp-DW5GdKKf.js → csharp-DZXiHH72.js} +1 -1
  96. package/dist/ui/assets/{csp-Bq74fG7-.js → csp-DDSIMUoU.js} +1 -1
  97. package/dist/ui/assets/{css-DHyeLXff.js → css-Bjye-ZWr.js} +1 -1
  98. package/dist/ui/assets/{cynefin-VYW2F7L2-DwV1t9iV.js → cynefin-VYW2F7L2-Cj5c4L3y.js} +1 -1
  99. package/dist/ui/assets/{cynefinDiagram-TSTJHNR4-kBCFRYPz.js → cynefinDiagram-TSTJHNR4-BYsfTeNX.js} +1 -1
  100. package/dist/ui/assets/{d-yey2lm3v.js → d-22pfurOx.js} +1 -1
  101. package/dist/ui/assets/{dagre-VKFMJZFB-CErVJ1s0.js → dagre-VKFMJZFB-DiEob-by.js} +1 -1
  102. package/dist/ui/assets/{dart-BadDxflP.js → dart-Doiu8cDW.js} +1 -1
  103. package/dist/ui/assets/{delphi-CqzVqtDp.js → delphi-CQ7epp84.js} +1 -1
  104. package/dist/ui/assets/{diagram-FQU43EPY-C7zAW2DU.js → diagram-FQU43EPY-BOPbQcL4.js} +1 -1
  105. package/dist/ui/assets/{diagram-G47NLZAW-CbUb6BZI.js → diagram-G47NLZAW-C1UWDJTx.js} +1 -1
  106. package/dist/ui/assets/{diagram-NH7WQ7WH-C5mgi3dn.js → diagram-NH7WQ7WH-CYsBqW3J.js} +1 -1
  107. package/dist/ui/assets/{diagram-OA4YK3LP-Cok2YNfS.js → diagram-OA4YK3LP-Dnag0x_3.js} +1 -1
  108. package/dist/ui/assets/{diagram-WEI45ONY-BODuP2EB.js → diagram-WEI45ONY-Bo4CsDoj.js} +1 -1
  109. package/dist/ui/assets/{diff-EVigJ-h2.js → diff-Cs6VQQZt.js} +1 -1
  110. package/dist/ui/assets/{django-BH2EEal6.js → django-Bo3okOrw.js} +1 -1
  111. package/dist/ui/assets/{dns-BSc8VTm8.js → dns-BpG60eIX.js} +1 -1
  112. package/dist/ui/assets/{dockerfile-Br_pZjru.js → dockerfile-BWyH8SlV.js} +1 -1
  113. package/dist/ui/assets/{dos-Bx3ludet.js → dos-DWlNbaB9.js} +1 -1
  114. package/dist/ui/assets/{dsconfig-DSI-ePne.js → dsconfig-D_8y8YrJ.js} +1 -1
  115. package/dist/ui/assets/{dts-ClCuE1Xq.js → dts-Cz4SyMYR.js} +1 -1
  116. package/dist/ui/assets/{dust-CC9Z8iuW.js → dust-Cu4qLkeH.js} +1 -1
  117. package/dist/ui/assets/{ebnf-DI0w_fbi.js → ebnf-CTVHui3r.js} +1 -1
  118. package/dist/ui/assets/{ebnfDiagram-CCIWWBDH-CgsnO8Qm.js → ebnfDiagram-CCIWWBDH-epcXQLEj.js} +1 -1
  119. package/dist/ui/assets/{elixir-hdzZik8p.js → elixir-MAlwXhfU.js} +1 -1
  120. package/dist/ui/assets/{elm-DJAgqt5I.js → elm-Bdmwbvef.js} +1 -1
  121. package/dist/ui/assets/{erDiagram-Q63AITRT-K4zJcEKd.js → erDiagram-Q63AITRT-Bb60TqAM.js} +1 -1
  122. package/dist/ui/assets/{erb-Cq4OQqkl.js → erb-Ce9TpP4g.js} +1 -1
  123. package/dist/ui/assets/{erlang-CHoXLODu.js → erlang-CN2GEiqk.js} +1 -1
  124. package/dist/ui/assets/{erlang-repl-Brp70Zip.js → erlang-repl-D8K4O_cp.js} +1 -1
  125. package/dist/ui/assets/{excel-DjS-QR9P.js → excel-eVZU1r0f.js} +1 -1
  126. package/dist/ui/assets/{fix-BsO9Oq6H.js → fix-BzKerEv0.js} +1 -1
  127. package/dist/ui/assets/{flix-2A7CKozm.js → flix-B9CE3skV.js} +1 -1
  128. package/dist/ui/assets/{flowDiagram-23GEKE2U-Ck4tfxWX.js → flowDiagram-23GEKE2U-DzMLZIyt.js} +1 -1
  129. package/dist/ui/assets/{fortran-BQu3wORI.js → fortran-B4__3vos.js} +1 -1
  130. package/dist/ui/assets/{fsharp-D439S6PZ.js → fsharp-BJ3-ARHz.js} +1 -1
  131. package/dist/ui/assets/{gams-CPRxKfRH.js → gams-Cu43oSYT.js} +1 -1
  132. package/dist/ui/assets/{ganttDiagram-NO4QXBWP-QTPyXXxa.js → ganttDiagram-NO4QXBWP-D_aAoMRP.js} +1 -1
  133. package/dist/ui/assets/{gauss-GJFW9Vod.js → gauss-T8vk_TpE.js} +1 -1
  134. package/dist/ui/assets/{gcode-CF6woDAl.js → gcode-9j3BicpZ.js} +1 -1
  135. package/dist/ui/assets/{gherkin-Co-YjHfW.js → gherkin-Dyh5KFG-.js} +1 -1
  136. package/dist/ui/assets/{gitGraphDiagram-IHSO6WYX-Do9VfPHL.js → gitGraphDiagram-IHSO6WYX-CMxCHC-Z.js} +1 -1
  137. package/dist/ui/assets/{glsl-D3lldBTJ.js → glsl-DWWjN4ts.js} +1 -1
  138. package/dist/ui/assets/{gml-DyoCibnw.js → gml-BcYE-D5A.js} +1 -1
  139. package/dist/ui/assets/{go-BNeAS1jC.js → go-DUnUzOEI.js} +1 -1
  140. package/dist/ui/assets/{golo-BuXecWAH.js → golo-BDoc1QQL.js} +1 -1
  141. package/dist/ui/assets/{gradle-BP7ToLe9.js → gradle-B_WRa_bT.js} +1 -1
  142. package/dist/ui/assets/{groovy-GqWVT09g.js → groovy-CgjqFq1e.js} +1 -1
  143. package/dist/ui/assets/{haml-SLbvCHsC.js → haml-DCqcg7Ti.js} +1 -1
  144. package/dist/ui/assets/{handlebars-BnaCJTc5.js → handlebars-DSDUQrnw.js} +1 -1
  145. package/dist/ui/assets/{haskell-CrX9ftWp.js → haskell-DPVnctVi.js} +1 -1
  146. package/dist/ui/assets/{haxe-DNoaIxsE.js → haxe-BuCGXBa4.js} +1 -1
  147. package/dist/ui/assets/{hsp-CLjy3djj.js → hsp-BTKpS_u0.js} +1 -1
  148. package/dist/ui/assets/{htmlbars-w96Kz5NL.js → htmlbars-Cql3Jvy2.js} +1 -1
  149. package/dist/ui/assets/{http-VIMXIxM8.js → http-BB8I9jUh.js} +1 -1
  150. package/dist/ui/assets/{hy-_qE83_aW.js → hy-DG7xkZNz.js} +1 -1
  151. package/dist/ui/assets/index-CK_lJsTu.css +10 -0
  152. package/dist/ui/assets/index-CfTIGTiY.js +2677 -0
  153. package/dist/ui/assets/{index-DpcPfZl3.js → index-DqIiCkSD.js} +3 -3
  154. package/dist/ui/assets/{infoDiagram-FWYZ7A6U-D9O7SZ2X.js → infoDiagram-FWYZ7A6U-DIzEV-BJ.js} +1 -1
  155. package/dist/ui/assets/{inform7-CnyW73C1.js → inform7-BWNpVEkA.js} +1 -1
  156. package/dist/ui/assets/{ini-GFMFAjln.js → ini-BMw_C8EP.js} +1 -1
  157. package/dist/ui/assets/{irpf90-BPmSYATn.js → irpf90-Cy42ibFZ.js} +1 -1
  158. package/dist/ui/assets/{isbl-BnStGiBQ.js → isbl-CjFSc0hz.js} +1 -1
  159. package/dist/ui/assets/{ishikawaDiagram-FXEZZL3T-DgcUDM9X.js → ishikawaDiagram-FXEZZL3T-BZcxp5am.js} +1 -1
  160. package/dist/ui/assets/{java-CVORcFd8.js → java-BDxe9dp1.js} +1 -1
  161. package/dist/ui/assets/{javascript-B2pFzODm.js → javascript-CgLkkj7h.js} +1 -1
  162. package/dist/ui/assets/{jboss-cli-B9cQeKvZ.js → jboss-cli-C8biclrb.js} +1 -1
  163. package/dist/ui/assets/{journeyDiagram-5HDEW3XC-Bivjw4h7.js → journeyDiagram-5HDEW3XC-BbJu6m6Q.js} +1 -1
  164. package/dist/ui/assets/{json-BJof8L10.js → json-BjJKhmM6.js} +1 -1
  165. package/dist/ui/assets/{julia-D_dGrCpZ.js → julia-DvKEpg-R.js} +1 -1
  166. package/dist/ui/assets/{julia-repl-Dsw0K1B1.js → julia-repl-BDJWZxkU.js} +1 -1
  167. package/dist/ui/assets/{kanban-definition-HUTT4EX6-Dg7NbKta.js → kanban-definition-HUTT4EX6-DeIVPlbv.js} +1 -1
  168. package/dist/ui/assets/{kotlin-8oetHRtX.js → kotlin-DCoXcnEb.js} +1 -1
  169. package/dist/ui/assets/{lasso-e37r_NX1.js → lasso-C7m0QmV2.js} +1 -1
  170. package/dist/ui/assets/{latex-DAmwzl3n.js → latex-SHnTG9Eq.js} +1 -1
  171. package/dist/ui/assets/{ldif-CD0mgmt4.js → ldif-_6hUnKPV.js} +1 -1
  172. package/dist/ui/assets/{leaf-GfPKlp7a.js → leaf-Co9qxlaF.js} +1 -1
  173. package/dist/ui/assets/{less-PndaZJ6n.js → less-CJRDPwNA.js} +1 -1
  174. package/dist/ui/assets/{lisp-CNaspu3A.js → lisp-JEHGYbiW.js} +1 -1
  175. package/dist/ui/assets/{livecodeserver-DEKsXiP3.js → livecodeserver-BSYaUj4k.js} +1 -1
  176. package/dist/ui/assets/{livescript-DM7jasJn.js → livescript-WQMCQeuz.js} +1 -1
  177. package/dist/ui/assets/{llvm-D_29j9MV.js → llvm-DnAHqj5I.js} +1 -1
  178. package/dist/ui/assets/{lsl-DY0WRYhg.js → lsl--k-ghYia.js} +1 -1
  179. package/dist/ui/assets/{lua-DZ_v4xaE.js → lua-CXb23hbC.js} +1 -1
  180. package/dist/ui/assets/{makefile-VOjcUGXf.js → makefile-CM7o86yn.js} +1 -1
  181. package/dist/ui/assets/{markdown-yUEmZWvu.js → markdown-HB3HvChQ.js} +1 -1
  182. package/dist/ui/assets/{mathematica-BLXYGKkd.js → mathematica-9NTuf22N.js} +1 -1
  183. package/dist/ui/assets/{matlab-B7SkIpvh.js → matlab-CWv-ik4r.js} +1 -1
  184. package/dist/ui/assets/{maxima-C7Fw8Vyt.js → maxima-CqYiKCrw.js} +1 -1
  185. package/dist/ui/assets/{mel-C_Znst2Y.js → mel-BbsRpUm5.js} +1 -1
  186. package/dist/ui/assets/{mercury-DjUnQNsq.js → mercury-Cz2j4HhU.js} +1 -1
  187. package/dist/ui/assets/{mindmap-definition-LN4V7U3C-Dp6mEy6L.js → mindmap-definition-LN4V7U3C-WyJS8oye.js} +1 -1
  188. package/dist/ui/assets/{mipsasm-DaGHhR8c.js → mipsasm-BqWSRbQV.js} +1 -1
  189. package/dist/ui/assets/{mizar-CZnct-_Z.js → mizar-JLBfQ6vt.js} +1 -1
  190. package/dist/ui/assets/{mojolicious-BdWFK35j.js → mojolicious-D0LbMmyk.js} +1 -1
  191. package/dist/ui/assets/{monkey-ta_jgafo.js → monkey-DAGL6-JN.js} +1 -1
  192. package/dist/ui/assets/{moonscript-B9qovjV6.js → moonscript-DzbwNSKy.js} +1 -1
  193. package/dist/ui/assets/{n1ql-BciXbxiw.js → n1ql-dkgKq6Yv.js} +1 -1
  194. package/dist/ui/assets/{nginx-DRCZYbxj.js → nginx-AUoaDPV4.js} +1 -1
  195. package/dist/ui/assets/{nim-DTMCxPJt.js → nim-CdqYmVrl.js} +1 -1
  196. package/dist/ui/assets/{nix-D39yZxF9.js → nix-CxmVjwwo.js} +1 -1
  197. package/dist/ui/assets/{node-repl-vAEXdnYM.js → node-repl-B1Z3zUAh.js} +1 -1
  198. package/dist/ui/assets/{nsis-CCMP8mq3.js → nsis-CTAuPWhe.js} +1 -1
  199. package/dist/ui/assets/{objectivec-CZx4kSLP.js → objectivec-CvbKeAPz.js} +1 -1
  200. package/dist/ui/assets/{ocaml-DBnEo9uR.js → ocaml-DCiyXKOH.js} +1 -1
  201. package/dist/ui/assets/{openscad-BKc7u4Bl.js → openscad-CPGUwmeR.js} +1 -1
  202. package/dist/ui/assets/{oxygene-M_Z4lQ6l.js → oxygene-CI_bD1Gw.js} +1 -1
  203. package/dist/ui/assets/{parser3-DS9UKiKD.js → parser3-C91CVs85.js} +1 -1
  204. package/dist/ui/assets/{pegDiagram-2B236MQR-BBEXn76M.js → pegDiagram-2B236MQR-yF8AW7GR.js} +1 -1
  205. package/dist/ui/assets/{perl-D6iEALGE.js → perl-ChxfKl-n.js} +1 -1
  206. package/dist/ui/assets/{pf-JQL0ClGi.js → pf-CKCHkzIA.js} +1 -1
  207. package/dist/ui/assets/{pgsql-DEmCURBr.js → pgsql-BwOP20uH.js} +1 -1
  208. package/dist/ui/assets/{php-DVm-0PY5.js → php-B7Rs2tM5.js} +1 -1
  209. package/dist/ui/assets/{php-template-DfWbDpnE.js → php-template-Cx-Fh4AR.js} +1 -1
  210. package/dist/ui/assets/{pieDiagram-ENE6RG2P-D5PZFiDj.js → pieDiagram-ENE6RG2P-BS4hnGNX.js} +1 -1
  211. package/dist/ui/assets/{plaintext-BahVkGlu.js → plaintext-U7qkdtgS.js} +1 -1
  212. package/dist/ui/assets/{pony-wVig0caC.js → pony-_fGYgePh.js} +1 -1
  213. package/dist/ui/assets/{powershell-COjMBUit.js → powershell-CFBTcUce.js} +1 -1
  214. package/dist/ui/assets/{processing-DnUzvFZx.js → processing-BWHneU0K.js} +1 -1
  215. package/dist/ui/assets/{profile-mDlbwJeL.js → profile-BJ-HxhzS.js} +1 -1
  216. package/dist/ui/assets/{prolog-x2hG6DnA.js → prolog-ChHo7n6L.js} +1 -1
  217. package/dist/ui/assets/{properties-DzZyyivC.js → properties-e_DOYclC.js} +1 -1
  218. package/dist/ui/assets/{protobuf-a5NwbYD5.js → protobuf-5aGSIv7w.js} +1 -1
  219. package/dist/ui/assets/{puppet-CCWyPVuC.js → puppet-D2vQatzX.js} +1 -1
  220. package/dist/ui/assets/{purebasic-CXNdqtD4.js → purebasic-n6krfaeF.js} +1 -1
  221. package/dist/ui/assets/{python-iSR9YlDJ.js → python-CbIo0XxP.js} +1 -1
  222. package/dist/ui/assets/{python-repl-CCmKzWru.js → python-repl-BpIq9kyr.js} +1 -1
  223. package/dist/ui/assets/{q-B30ZmDuD.js → q-jntnvcCx.js} +1 -1
  224. package/dist/ui/assets/{qml-BGdPdB4X.js → qml-BafQlZNV.js} +1 -1
  225. package/dist/ui/assets/{quadrantDiagram-ABIIQ3AL-BBF1rAvI.js → quadrantDiagram-ABIIQ3AL-C3Th8mUJ.js} +1 -1
  226. package/dist/ui/assets/{r-B8ASdWL1.js → r-CHgvQFLK.js} +1 -1
  227. package/dist/ui/assets/{railroadDiagram-RFXS5EU6-DQFUXGVD.js → railroadDiagram-RFXS5EU6-CMQkbGJl.js} +1 -1
  228. package/dist/ui/assets/{reasonml-Dw4Bi5B5.js → reasonml-DNwlPD_T.js} +1 -1
  229. package/dist/ui/assets/{requirementDiagram-TGXJPOKE-DCgrs7-P.js → requirementDiagram-TGXJPOKE-B70_oR7v.js} +1 -1
  230. package/dist/ui/assets/{rib-CgUGGGZ9.js → rib-CX4e9JSF.js} +1 -1
  231. package/dist/ui/assets/{roboconf-CCZNQvSP.js → roboconf-ee4qsPGf.js} +1 -1
  232. package/dist/ui/assets/{routeros-DVT5M6iV.js → routeros-BJSwvu2L.js} +1 -1
  233. package/dist/ui/assets/{rsl-BY-LhdKq.js → rsl-BiKdMl_F.js} +1 -1
  234. package/dist/ui/assets/{ruby-CZW4dclw.js → ruby-CAXTU5LY.js} +1 -1
  235. package/dist/ui/assets/{ruleslanguage-CZdjv1of.js → ruleslanguage-C1kgDGXM.js} +1 -1
  236. package/dist/ui/assets/{rust-D5Xa_mSD.js → rust-DyEf_GXL.js} +1 -1
  237. package/dist/ui/assets/{sankeyDiagram-HTMAVEWB-BXeEgUB7.js → sankeyDiagram-HTMAVEWB-CvUfseMg.js} +1 -1
  238. package/dist/ui/assets/{sas-B_UJ-HCZ.js → sas-Q1rlUxPH.js} +1 -1
  239. package/dist/ui/assets/{scala-B8wSdZik.js → scala-Dg6KNmge.js} +1 -1
  240. package/dist/ui/assets/{scheme-DD7U81eh.js → scheme-D9iooUxI.js} +1 -1
  241. package/dist/ui/assets/{scilab-aJGpbkKs.js → scilab-CztEJXJY.js} +1 -1
  242. package/dist/ui/assets/{scss-BBqyvWYh.js → scss-BMh-JkdE.js} +1 -1
  243. package/dist/ui/assets/{sequenceDiagram-DBY2YBRQ-CnDTZN18.js → sequenceDiagram-DBY2YBRQ-DB0c9kf4.js} +1 -1
  244. package/dist/ui/assets/{shell-CR23jqfz.js → shell-DWo6z0MG.js} +1 -1
  245. package/dist/ui/assets/{sizeCapture-X5ZJPWSS-joZ477RB.js → sizeCapture-X5ZJPWSS-BcQlgjfD.js} +1 -1
  246. package/dist/ui/assets/{smali-ZmLyRCrQ.js → smali-Cijeb6pL.js} +1 -1
  247. package/dist/ui/assets/{smalltalk-CnADYfPr.js → smalltalk-D0lvqa_d.js} +1 -1
  248. package/dist/ui/assets/{sml-zj1KD04_.js → sml-B-vqWZ3b.js} +1 -1
  249. package/dist/ui/assets/{sqf-DCCaZ3e3.js → sqf-BJ-1Q583.js} +1 -1
  250. package/dist/ui/assets/{sql-CHGw2SCR.js → sql-CMGLxEYV.js} +1 -1
  251. package/dist/ui/assets/{sql_more-DsnvjMwh.js → sql_more-gsRmbNCH.js} +1 -1
  252. package/dist/ui/assets/{stan-ChxdV8f9.js → stan-D1AlTSpf.js} +1 -1
  253. package/dist/ui/assets/{stata-CgFw45qg.js → stata-DnEYviI0.js} +1 -1
  254. package/dist/ui/assets/{stateDiagram-2N3HPSRC-CzLIdKbt.js → stateDiagram-2N3HPSRC-BSRgVsHO.js} +1 -1
  255. package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-BLdfpGJO.js +1 -0
  256. package/dist/ui/assets/{step21-DaraQMOk.js → step21-DbQV9sMo.js} +1 -1
  257. package/dist/ui/assets/{stylus-BZpiJEzN.js → stylus-Cyc7vdUc.js} +1 -1
  258. package/dist/ui/assets/{subunit-B6Gp0n7h.js → subunit-16ipyMie.js} +1 -1
  259. package/dist/ui/assets/{swift-DQzoz60p.js → swift-DTNnw4qS.js} +1 -1
  260. package/dist/ui/assets/{swimlanes-5IMT3BWC-DOMOhwX0.js → swimlanes-5IMT3BWC-DgOdcc4_.js} +2 -2
  261. package/dist/ui/assets/swimlanesDiagram-G3AALYLV-DRVnkg3M.js +8 -0
  262. package/dist/ui/assets/{taggerscript-BMLkfiab.js → taggerscript-DM6pvUWE.js} +1 -1
  263. package/dist/ui/assets/{tap-PUNWTFgu.js → tap-Ch-uyKZq.js} +1 -1
  264. package/dist/ui/assets/{tcl-BODH5qJa.js → tcl-D0T_V5dA.js} +1 -1
  265. package/dist/ui/assets/{thrift-BQ-W_YlC.js → thrift-D9xJjYw4.js} +1 -1
  266. package/dist/ui/assets/{timeline-definition-FHXFAJF6-DQBDeTzH.js → timeline-definition-FHXFAJF6-BBeDMHxb.js} +1 -1
  267. package/dist/ui/assets/{tp-Dt3WwOGF.js → tp-kUrCGo9X.js} +1 -1
  268. package/dist/ui/assets/{twig-44RQtw7Z.js → twig-Cgub6rxA.js} +1 -1
  269. package/dist/ui/assets/{typescript-BsouHXHR.js → typescript-BxNP9Ap9.js} +1 -1
  270. package/dist/ui/assets/{vala-DqJj6fPT.js → vala-DF9ZZagU.js} +1 -1
  271. package/dist/ui/assets/{vbnet-CuqWYg5r.js → vbnet-Cf46ypDg.js} +1 -1
  272. package/dist/ui/assets/{vbscript-w20MQMYP.js → vbscript-DNs-QKLH.js} +1 -1
  273. package/dist/ui/assets/{vbscript-html-BP0D0WEV.js → vbscript-html-DyS6I5L1.js} +1 -1
  274. package/dist/ui/assets/{vennDiagram-L72KCM5P-DwNnj3Kc.js → vennDiagram-L72KCM5P-8WpWycE-.js} +1 -1
  275. package/dist/ui/assets/{verilog-DIpBpJAe.js → verilog-CF5wC4X-.js} +1 -1
  276. package/dist/ui/assets/{vhdl-D5nCxDbL.js → vhdl-Dm0jpDMn.js} +1 -1
  277. package/dist/ui/assets/{vim-CPfbBM_x.js → vim-nyO2DRAx.js} +1 -1
  278. package/dist/ui/assets/{wardleyDiagram-EHGQE667-WxVQHmM7.js → wardleyDiagram-EHGQE667-UeIOK-jv.js} +1 -1
  279. package/dist/ui/assets/{x86asm-D_t4Z_eq.js → x86asm-BNw2-6GG.js} +1 -1
  280. package/dist/ui/assets/{xl-DQf5IG_W.js → xl-D-YHxr6V.js} +1 -1
  281. package/dist/ui/assets/{xml-D-Bn0NMR.js → xml-BijAlrow.js} +1 -1
  282. package/dist/ui/assets/{xquery-DqushYX1.js → xquery-z0wxWBKz.js} +1 -1
  283. package/dist/ui/assets/{xychartDiagram-FW5EYKEG-B6D8mgs8.js → xychartDiagram-FW5EYKEG-BNzHLweN.js} +1 -1
  284. package/dist/ui/assets/{yaml-B2GFZenc.js → yaml-n5TIQArR.js} +1 -1
  285. package/dist/ui/assets/{zephir-SiNGockR.js → zephir-DrGhH8jG.js} +1 -1
  286. package/dist/ui/index.html +2 -2
  287. package/dist/utils/__tests__/providers-db.test.js +2 -1
  288. package/dist/utils/__tests__/tool-injector.test.js +32 -35
  289. package/dist/utils/db.d.ts +3 -0
  290. package/dist/utils/db.js +54 -0
  291. package/dist/utils/inject-loop.d.ts +52 -1
  292. package/dist/utils/inject-loop.js +70 -2
  293. package/dist/utils/mcp-db.d.ts +35 -0
  294. package/dist/utils/mcp-db.js +79 -0
  295. package/dist/utils/mcp-manager.d.ts +36 -0
  296. package/dist/utils/mcp-manager.js +215 -0
  297. package/dist/utils/openai-converter.d.ts +99 -0
  298. package/dist/utils/openai-converter.js +293 -0
  299. package/dist/utils/providers-db.d.ts +2 -0
  300. package/dist/utils/providers-db.js +19 -7
  301. package/dist/utils/request-writer.d.ts +20 -0
  302. package/dist/utils/request-writer.js +184 -4
  303. package/dist/utils/sse-stream-processor.d.ts +27 -0
  304. package/dist/utils/sse-stream-processor.js +144 -0
  305. package/dist/utils/stats-aggregator.js +2 -0
  306. package/dist/utils/status-history.js +3 -3
  307. package/dist/utils/tool-injector.d.ts +6 -9
  308. package/dist/utils/tool-injector.js +13 -14
  309. package/dist/utils/version.d.ts +6 -0
  310. package/dist/utils/version.js +11 -1
  311. package/package.json +14 -12
  312. package/dist/ui/assets/channel-C5JlZ38H.js +0 -1
  313. package/dist/ui/assets/classDiagram-OUVF2IWQ-j5FVBKaD.js +0 -1
  314. package/dist/ui/assets/classDiagram-v2-EOCWNBFH-j5FVBKaD.js +0 -1
  315. package/dist/ui/assets/index-Baqd6LlK.js +0 -2625
  316. package/dist/ui/assets/index-fHtNI_e8.css +0 -1
  317. package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-Bg4GAkB5.js +0 -1
  318. package/dist/ui/assets/swimlanesDiagram-G3AALYLV-99wEK8yq.js +0 -8
@@ -1,22 +1,23 @@
1
- import { Readable } from 'stream';
1
+ import { Readable, finished } from 'stream';
2
+ import { convertClaudeToOpenAIRequest, convertOpenAIToClaudeResponse, createStreamTransformer, buildOpenAIHeaders } from '../utils/openai-converter.js';
2
3
  import { loadConfig } from '../config/index.js';
3
- import { saveRequestDB, extractTokenUsage } from '../utils/request-writer.js';
4
+ import { saveRequestDB, extractTokenUsage, flushEventBatch, deleteResponseEvents, deleteRequestRow, updateRequestTokens, EVENT_BATCH_SIZE } from '../utils/request-writer.js';
4
5
  import { cleanOldRequestsDB } from '../utils/request-writer.js';
5
6
  import { pushNewRequest } from './data-push.js';
6
7
  import { initDatabase, closeDatabase, getDatabase } from '../utils/db.js';
7
8
  import { aggregateStats } from '../utils/stats-aggregator.js';
8
- import { writeTextAsSSE, shouldMock } from '../utils/sse-writer.js';
9
- import { injectTools } from '../utils/tool-injector.js';
10
- import { parseSSEText, parseUpstreamEvents, findInjectedCall, runInjectLoop } from '../utils/inject-loop.js';
11
- import { hasEnabledTools } from '../tools/index.js';
9
+ import { mcpManager } from '../utils/mcp-manager.js';
12
10
  import { getMachineIdSync, preloadMachineId } from '../utils/machine-id.js';
11
+ // Cached prepared statement for active provider lookup (avoids per-request allocation)
12
+ let activeProviderStmt = null;
13
+ let cachedDbInstance = null;
13
14
  /**
14
15
  * Resolves the proxy target per request:
15
16
  * - If a provider with is_default=1 exists in DB, use its base_url + api_key + tier models (partial replacement)
16
17
  * - Otherwise fall back to config.json proxy.target + proxy.apiKey
17
18
  *
18
19
  * Called per request for hot-swap support (no server restart needed on activation change).
19
- * better-sqlite3 is synchronous and ~us-level for a single-row SELECT; no caching needed.
20
+ * better-sqlite3 is synchronous and ~us-level for a single-row SELECT; cached statement avoids repeated allocation.
20
21
  */
21
22
  export function resolveProxyTarget(proxyConfig) {
22
23
  // Default to config values
@@ -27,13 +28,22 @@ export function resolveProxyTarget(proxyConfig) {
27
28
  let haiku_model;
28
29
  let sonnet_model;
29
30
  let opus_model;
31
+ let is_openai_compatible;
30
32
  try {
31
33
  // Parse config target as base
32
34
  const configUrl = new URL(proxyConfig.target);
33
35
  baseUrl = configUrl.origin;
34
36
  targetPath = configUrl.pathname.replace(/\/$/, '');
35
37
  const db = getDatabase();
36
- const active = db.prepare('SELECT base_url, api_key, model, haiku_model, sonnet_model, opus_model FROM providers WHERE is_default = 1 LIMIT 1').get();
38
+ // Invalidate cached stmt if DB instance changed (happens in tests with fresh DB per test)
39
+ if (db !== cachedDbInstance) {
40
+ activeProviderStmt = null;
41
+ cachedDbInstance = db;
42
+ }
43
+ if (!activeProviderStmt) {
44
+ activeProviderStmt = db.prepare('SELECT base_url, api_key, model, haiku_model, sonnet_model, opus_model, is_openai_compatible FROM providers WHERE is_default = 1 LIMIT 1');
45
+ }
46
+ const active = activeProviderStmt.get();
37
47
  if (active) {
38
48
  // Replace base_url only if provided
39
49
  if (active.base_url && active.base_url.trim() !== '') {
@@ -54,6 +64,8 @@ export function resolveProxyTarget(proxyConfig) {
54
64
  sonnet_model = active.sonnet_model;
55
65
  if (active.opus_model && active.opus_model.trim() !== '')
56
66
  opus_model = active.opus_model;
67
+ // Populate is_openai_compatible
68
+ is_openai_compatible = !!active.is_openai_compatible;
57
69
  }
58
70
  }
59
71
  catch {
@@ -62,7 +74,7 @@ export function resolveProxyTarget(proxyConfig) {
62
74
  baseUrl = configUrl.origin;
63
75
  targetPath = configUrl.pathname.replace(/\/$/, '');
64
76
  }
65
- return { baseUrl, targetPath, apiKey, model, haiku_model, sonnet_model, opus_model };
77
+ return { baseUrl, targetPath, apiKey, model, haiku_model, sonnet_model, opus_model, is_openai_compatible };
66
78
  }
67
79
  /**
68
80
  * Selects the proxy model based on the original request model and provider tier config.
@@ -87,7 +99,7 @@ export function getProxyModel(originalModel, tierModels) {
87
99
  return tierModels.model || undefined;
88
100
  }
89
101
  // Extract safe headers (avoid circular reference)
90
- function extractSafeHeaders(headers) {
102
+ export function extractSafeHeaders(headers) {
91
103
  const safeHeaders = {};
92
104
  for (const [key, value] of Object.entries(headers)) {
93
105
  if (typeof value === 'string' || typeof value === 'number') {
@@ -100,23 +112,29 @@ function extractSafeHeaders(headers) {
100
112
  return safeHeaders;
101
113
  }
102
114
  // Filter headers for forwarding (remove problematic ones, replace API key if configured)
103
- function filterForwardHeaders(headers, proxyApiKey) {
115
+ export function filterForwardHeaders(headers, proxyApiKey) {
104
116
  const filtered = { ...headers };
105
117
  // Remove headers that cause issues
106
118
  delete filtered.host;
107
119
  delete filtered['content-length'];
108
120
  delete filtered['x-req-key'];
109
- // Replace API key if proxy key is configured
121
+ // Inject or replace API key when proxy key is available
110
122
  if (proxyApiKey) {
111
- // Only replace existing headers, don't add new ones
112
- if ('x-api-key' in filtered) {
123
+ const hasApiKey = 'x-api-key' in filtered;
124
+ const hasAuth = 'authorization' in filtered || 'Authorization' in filtered;
125
+ if (hasApiKey) {
126
+ // Replace existing x-api-key
113
127
  filtered['x-api-key'] = proxyApiKey;
114
128
  }
115
- if ('authorization' in filtered || 'Authorization' in filtered) {
116
- // Use the same case as the original header
129
+ else if (hasAuth) {
130
+ // Replace existing authorization
117
131
  const key = 'authorization' in filtered ? 'authorization' : 'Authorization';
118
132
  filtered[key] = `Bearer ${proxyApiKey}`;
119
133
  }
134
+ else {
135
+ // No auth header in request → inject x-api-key from provider
136
+ filtered['x-api-key'] = proxyApiKey;
137
+ }
120
138
  }
121
139
  return filtered;
122
140
  }
@@ -129,6 +147,56 @@ export function getErrorStatusCode(error) {
129
147
  }
130
148
  return 500;
131
149
  }
150
+ /**
151
+ * Push a parsed SSE event into batch/tail buffers.
152
+ * Flushes batch to DB when it reaches EVENT_BATCH_SIZE (10).
153
+ * Only memory held: current batch (≤10 events) + tail (≤5 events).
154
+ */
155
+ function pushEventToBuffers(event, sessionId, requestId, eventBatch, tailBuffer, eventIndexRef) {
156
+ const entry = { index: eventIndexRef.value, type: event?.type, data: event };
157
+ eventBatch.push(entry);
158
+ // Maintain tail buffer (last 5 events for usage extraction)
159
+ tailBuffer.push(event);
160
+ if (tailBuffer.length > 5) {
161
+ tailBuffer.shift();
162
+ }
163
+ // Flush batch to DB when threshold reached
164
+ if (eventBatch.length >= EVENT_BATCH_SIZE) {
165
+ flushEventBatch(sessionId, requestId, eventBatch, eventIndexRef.value - eventBatch.length + 1);
166
+ eventBatch.length = 0; // Reset batch (memory freed)
167
+ }
168
+ eventIndexRef.value++;
169
+ }
170
+ /**
171
+ * Flush remaining batch + update request completion in DB.
172
+ * Extracts token usage from tail buffer (no need for full events array).
173
+ */
174
+ async function completeStreamSave(sessionId, requestId, requestData, eventBatch, tailBuffer, eventIndexRef, startTime, statusCode, resolved, forwardedModel, originalModel, machineId, request) {
175
+ // Flush remaining events in batch
176
+ if (eventBatch.length > 0) {
177
+ flushEventBatch(sessionId, requestId, eventBatch, eventIndexRef.value - eventBatch.length);
178
+ eventBatch.length = 0;
179
+ }
180
+ requestData.response.completedAt = new Date().toISOString();
181
+ requestData.response.events = []; // Not needed for streaming, clear to avoid serialization
182
+ await saveRequestDB(sessionId, requestId, requestData);
183
+ const usage = extractTokenUsage(tailBuffer);
184
+ updateRequestTokens(requestId, sessionId, usage);
185
+ pushNewRequest({
186
+ requestId,
187
+ sessionId,
188
+ compositeId: `${sessionId}:${requestId}`,
189
+ method: request.method,
190
+ path: request.url,
191
+ timestamp: requestData.timestamp,
192
+ statusCode,
193
+ responseTime: Date.now() - startTime,
194
+ model: forwardedModel || originalModel,
195
+ machineId,
196
+ baseUrl: resolved.baseUrl,
197
+ usage
198
+ }, 'completed');
199
+ }
132
200
  export function getErrorMessage(error) {
133
201
  if (error.code === 'ECONNREFUSED') {
134
202
  return 'Target server unreachable';
@@ -147,6 +215,13 @@ export async function setupProxy(fastify, proxyConfig) {
147
215
  const maxDays = fullConfig.storage?.maxDays ?? 7;
148
216
  // Initialize SQLite database
149
217
  initDatabase();
218
+ // Initialize MCP connections (after DB is ready)
219
+ try {
220
+ await mcpManager.init();
221
+ }
222
+ catch (err) {
223
+ console.error('MCP manager init failed:', err);
224
+ }
150
225
  // Preload machine ID
151
226
  await preloadMachineId();
152
227
  // Start stats aggregation timer (every 5 minutes)
@@ -158,6 +233,15 @@ export async function setupProxy(fastify, proxyConfig) {
158
233
  console.error('Stats aggregation failed:', err);
159
234
  }
160
235
  }, 300000); // 5 minutes
236
+ // Periodic cleanup timer (every 30 minutes, not per-request)
237
+ const cleanupTimer = setInterval(() => {
238
+ try {
239
+ cleanOldRequestsDB(maxDays);
240
+ }
241
+ catch (err) {
242
+ console.error('Cleanup failed:', err);
243
+ }
244
+ }, 1800000); // 30 minutes
161
245
  // Initial aggregation on startup
162
246
  try {
163
247
  aggregateStats();
@@ -165,9 +249,18 @@ export async function setupProxy(fastify, proxyConfig) {
165
249
  catch (err) {
166
250
  console.error('Initial stats aggregation failed:', err);
167
251
  }
168
- // Cleanup database and timer on server close
252
+ // Initial cleanup on startup
253
+ try {
254
+ cleanOldRequestsDB(maxDays);
255
+ }
256
+ catch (err) {
257
+ console.error('Initial cleanup failed:', err);
258
+ }
259
+ // Cleanup database, MCP connections, and timer on server close
169
260
  fastify.addHook('onClose', async () => {
170
261
  clearInterval(statsTimer);
262
+ clearInterval(cleanupTimer);
263
+ await mcpManager.close();
171
264
  closeDatabase();
172
265
  });
173
266
  // Application-layer forwarding (Controller + Fetch + SSE Pipe)
@@ -218,38 +311,166 @@ export async function setupProxy(fastify, proxyConfig) {
218
311
  },
219
312
  response: {
220
313
  statusCode: 0, // Will be updated after fetch
221
- events: [] // SSE events will be recorded here
314
+ events: [] // Only used for JSON responses; streaming uses batch buffer
222
315
  }
223
316
  };
224
- await cleanOldRequestsDB(maxDays);
317
+ // SSE event batch buffer: flush to DB every ~10 events to avoid in-memory accumulation
318
+ // Tail buffer: keep last 5 events for token usage extraction at stream end
319
+ const eventBatch = [];
320
+ const tailBuffer = [];
321
+ const eventIndexRef = { value: 0 };
225
322
  request.log.info(`request logged: ${request.id} ${request.method} ${request.url}`);
226
- // [PoC] 魔法词拦截:最新 user message 含 __MOCK__ 时,不转发上游,
227
- // 直接构造合成 SSE 流返回,验证客户端能否将其识别为正常回答。
228
- if (shouldMock(request.body)) {
229
- reply.hijack();
230
- reply.raw.writeHead(200, {
231
- 'content-type': 'text/event-stream',
232
- 'cache-control': 'no-cache',
233
- 'connection': 'keep-alive'
323
+ // Save initial record + push pending status to frontend
324
+ await saveRequestDB(sessionId, request.id, requestData);
325
+ pushNewRequest({
326
+ requestId: request.id,
327
+ sessionId,
328
+ compositeId: `${sessionId}:${request.id}`,
329
+ method: request.method,
330
+ path: request.url,
331
+ timestamp: requestData.timestamp,
332
+ statusCode: 0,
333
+ responseTime: 0,
334
+ model: forwardedModel,
335
+ machineId,
336
+ baseUrl: resolved.baseUrl
337
+ }, 'pending');
338
+ // --- OpenAI Compatible Mode ---
339
+ if (resolved.is_openai_compatible) {
340
+ const openaiRequest = convertClaudeToOpenAIRequest(request.body, forwardedModel);
341
+ const openaiHeaders = buildOpenAIHeaders(resolved.apiKey || '');
342
+ // Build target URL: /chat/completions for OpenAI format
343
+ const targetUrlStr = `${resolved.baseUrl}${resolved.targetPath}/chat/completions`;
344
+ request.log.info(`forwarding (openai mode) to: ${targetUrlStr}`);
345
+ // Forward converted request to OpenAI-compatible API
346
+ const response = await fetch(targetUrlStr, {
347
+ method: request.method,
348
+ headers: openaiHeaders,
349
+ body: JSON.stringify(openaiRequest)
234
350
  });
235
- writeTextAsSSE(reply.raw, '[PoC] 这是 proxy 构造的合成响应,未经过真实大模型。', `msg_poc_${request.id}`);
236
- reply.raw.end();
237
- request.log.info(`[PoC] mock intercepted: ${request.id}`);
351
+ request.log.info(`response status: ${response.status}`);
352
+ // Extract cloud request ID
353
+ const cloudRequestId = response.headers.get('x-request-id') || request.id;
354
+ requestData.requestId = cloudRequestId;
355
+ requestData.response.statusCode = response.status;
356
+ requestData.response.headers = Object.fromEntries(response.headers);
357
+ // Check response type
358
+ const contentType = response.headers.get('content-type') || '';
359
+ const isSSE = contentType.includes('text/event-stream');
360
+ const isJSON = contentType.includes('application/json');
361
+ // Handle non-streaming JSON response
362
+ if (isJSON && !isSSE && response.body) {
363
+ const responseBody = await response.text();
364
+ let claudeResponseBody;
365
+ try {
366
+ const openaiRes = JSON.parse(responseBody);
367
+ const claudeRes = convertOpenAIToClaudeResponse(openaiRes, forwardedModel || originalModel);
368
+ claudeResponseBody = JSON.stringify(claudeRes);
369
+ requestData.response.events.push(claudeRes);
370
+ }
371
+ catch {
372
+ claudeResponseBody = responseBody;
373
+ try {
374
+ requestData.response.events.push(JSON.parse(responseBody));
375
+ }
376
+ catch {
377
+ requestData.response.events.push({ raw: responseBody });
378
+ }
379
+ }
380
+ requestData.response.completedAt = new Date().toISOString();
381
+ if (cloudRequestId !== request.id) {
382
+ deleteResponseEvents(request.id, sessionId);
383
+ deleteRequestRow(request.id, sessionId);
384
+ }
385
+ await saveRequestDB(sessionId, cloudRequestId, requestData);
386
+ const usage = extractTokenUsage(requestData.response.events);
387
+ pushNewRequest({
388
+ requestId: cloudRequestId,
389
+ sessionId,
390
+ compositeId: `${sessionId}:${cloudRequestId}`,
391
+ method: request.method,
392
+ path: request.url,
393
+ timestamp: requestData.timestamp,
394
+ statusCode: response.status,
395
+ responseTime: Date.now() - startTime,
396
+ model: forwardedModel,
397
+ machineId,
398
+ baseUrl: resolved.baseUrl,
399
+ usage,
400
+ pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
401
+ }, 'completed');
402
+ const elapsed = Date.now() - startTime;
403
+ request.log.info(`json response (openai): ${cloudRequestId} ${response.status} (${elapsed}ms)`);
404
+ reply.code(response.status).headers({ 'Content-Type': 'application/json' }).send(claudeResponseBody);
405
+ return;
406
+ }
407
+ // Handle SSE streaming response
408
+ reply.hijack();
409
+ const responseHeaders = Object.fromEntries(response.headers);
410
+ // Replace content-type with Claude SSE format
411
+ responseHeaders['content-type'] = 'text/event-stream';
412
+ reply.raw.writeHead(response.status, responseHeaders);
413
+ if (response.body) {
414
+ const nodeStream = Readable.fromWeb(response.body);
415
+ const transformStream = createStreamTransformer(forwardedModel || originalModel);
416
+ // Record SSE events via batch buffer (memory: ≤10 batch + ≤5 tail)
417
+ nodeStream.on('data', (chunk) => {
418
+ const text = chunk.toString();
419
+ const lines = text.split('\n');
420
+ for (const line of lines) {
421
+ if (line.startsWith('data:')) {
422
+ const eventData = line.slice(5).trim();
423
+ if (eventData && eventData !== '[DONE]') {
424
+ try {
425
+ const event = JSON.parse(eventData);
426
+ pushEventToBuffers(event, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
427
+ }
428
+ catch {
429
+ // Not valid JSON, skip
430
+ }
431
+ }
432
+ }
433
+ }
434
+ });
435
+ // Use finished() for reliable stream completion detection
436
+ finished(nodeStream, async (err) => {
437
+ if (err) {
438
+ request.log.error(`openai stream error: ${err.message}`);
439
+ }
440
+ // Convert OpenAI stream usage to Claude format for extractTokenUsage compatibility
441
+ const lastChunk = tailBuffer.find((e) => e?.usage);
442
+ if (lastChunk?.usage) {
443
+ const syntheticEvent = {
444
+ type: 'message_delta',
445
+ usage: {
446
+ output_tokens: lastChunk.usage.completion_tokens || 0
447
+ }
448
+ };
449
+ pushEventToBuffers(syntheticEvent, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
450
+ }
451
+ if (cloudRequestId !== request.id) {
452
+ deleteResponseEvents(request.id, sessionId);
453
+ deleteRequestRow(request.id, sessionId);
454
+ }
455
+ // Flush remaining batch + complete request
456
+ try {
457
+ await completeStreamSave(sessionId, cloudRequestId, requestData, eventBatch, tailBuffer, eventIndexRef, startTime, response.status, resolved, forwardedModel, originalModel, machineId, request);
458
+ const elapsed = Date.now() - startTime;
459
+ request.log.info(`stream completed (openai): ${cloudRequestId} ${response.status} (${elapsed}ms, ${eventIndexRef.value} events)`);
460
+ }
461
+ catch (dbErr) {
462
+ request.log.error(`openai stream save failed: ${dbErr.message}`);
463
+ }
464
+ });
465
+ // Pipe: OpenAI SSE → Transform → Claude SSE → Client
466
+ nodeStream.pipe(transformStream).pipe(reply.raw);
467
+ }
238
468
  return;
239
469
  }
470
+ // --- Claude Mode (existing direct forwarding) ---
240
471
  // Build target URL correctly
241
472
  // request.url = '/v1/messages' -> should become '/apps/anthropic/v1/messages'
242
473
  const targetUrlStr = `${resolved.baseUrl}${resolved.targetPath}${request.url}`;
243
- // Tool 注入:存在启用 tool 时,向主对话请求的 tools 末尾注入自定义 tool。
244
- // 无启用 tool 时完全不进此分支,走下方原 pipe 路径(零影响)。
245
- let injectedNames = [];
246
- if (hasEnabledTools()) {
247
- injectedNames = injectTools(request.body);
248
- if (injectedNames.length) {
249
- request.log.info(`[inject] tools injected: ${injectedNames.join(',')} (${request.id})`);
250
- }
251
- }
252
- const injected = injectedNames.length > 0;
253
474
  request.log.info(`forwarding to: ${targetUrlStr}`);
254
475
  // Forward request to Aliyun with filtered headers
255
476
  const response = await fetch(targetUrlStr, {
@@ -277,10 +498,14 @@ export async function setupProxy(fastify, proxyConfig) {
277
498
  requestData.response.events.push(jsonBody);
278
499
  }
279
500
  catch {
280
- // Not valid JSON, record as raw text
281
501
  requestData.response.events.push({ raw: responseBody });
282
502
  }
283
503
  requestData.response.completedAt = new Date().toISOString();
504
+ // Remove pending row if cloudRequestId differs from request.id
505
+ if (cloudRequestId !== request.id) {
506
+ deleteResponseEvents(request.id, sessionId);
507
+ deleteRequestRow(request.id, sessionId);
508
+ }
284
509
  await saveRequestDB(sessionId, cloudRequestId, requestData);
285
510
  const usage = extractTokenUsage(requestData.response.events);
286
511
  pushNewRequest({
@@ -295,74 +520,16 @@ export async function setupProxy(fastify, proxyConfig) {
295
520
  model: forwardedModel,
296
521
  machineId,
297
522
  baseUrl: resolved.baseUrl,
298
- usage
299
- });
523
+ usage,
524
+ pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
525
+ }, 'completed');
300
526
  const elapsed = Date.now() - startTime;
301
527
  request.log.info(`json response: ${cloudRequestId} ${response.status} (${elapsed}ms)`);
302
- // Send response to client
303
528
  reply.code(response.status);
304
529
  reply.headers(Object.fromEntries(response.headers));
305
530
  reply.send(responseBody);
306
531
  return;
307
532
  }
308
- // 条件缓冲:注入生效且为 SSE 时,收完整流检测是否命中已启用 tool。
309
- // 命中 -> 内部循环 -> 合成 SSE 回写;未命中 -> 原样回放缓冲字节。
310
- // 未注入时完全不进此分支,走下方原有 pipe 路径(零影响)。
311
- if (injected && isSSE && response.body) {
312
- const fullText = await response.text();
313
- const events = parseSSEText(fullText);
314
- requestData.response.events = events;
315
- const parsed = parseUpstreamEvents(events);
316
- const hitInjected = findInjectedCall(parsed) && parsed.stopReason === 'tool_use';
317
- reply.hijack();
318
- if (!hitInjected) {
319
- // 未命中:原样回放上游缓冲字节,客户端无感知
320
- reply.raw.writeHead(response.status, Object.fromEntries(response.headers));
321
- reply.raw.end(fullText);
322
- }
323
- else {
324
- // 命中:进入内部循环,代行「执行 tool -> 追加对话 -> 再调上游」
325
- request.log.info(`[inject] tool hit, entering loop: ${cloudRequestId}`);
326
- const loop = await runInjectLoop({
327
- events,
328
- body: request.body,
329
- callUpstream: async (b) => {
330
- const r = await fetch(targetUrlStr, {
331
- method: request.method,
332
- headers: forwardHeaders,
333
- body: JSON.stringify(b)
334
- });
335
- return parseSSEText(await r.text());
336
- }
337
- });
338
- reply.raw.writeHead(200, {
339
- 'content-type': 'text/event-stream',
340
- 'cache-control': 'no-cache',
341
- 'connection': 'keep-alive'
342
- });
343
- writeTextAsSSE(reply.raw, loop.text, `msg_inject_${cloudRequestId}`);
344
- reply.raw.end();
345
- request.log.info(`[inject] loop done: ${cloudRequestId} (${loop.iterations} iter, limit=${loop.stoppedByLimit})`);
346
- }
347
- requestData.response.completedAt = new Date().toISOString();
348
- await saveRequestDB(sessionId, cloudRequestId, requestData);
349
- const usage = extractTokenUsage(requestData.response.events);
350
- pushNewRequest({
351
- requestId: cloudRequestId,
352
- sessionId,
353
- compositeId: `${sessionId}:${cloudRequestId}`,
354
- method: request.method,
355
- path: request.url,
356
- timestamp: requestData.timestamp,
357
- statusCode: response.status,
358
- responseTime: Date.now() - startTime,
359
- model: forwardedModel,
360
- machineId,
361
- baseUrl: resolved.baseUrl,
362
- usage
363
- });
364
- return;
365
- }
366
533
  // Hijack reply to take full control of SSE stream
367
534
  reply.hijack();
368
535
  // Write response headers (preserve original from Aliyun)
@@ -372,20 +539,17 @@ export async function setupProxy(fastify, proxyConfig) {
372
539
  if (response.body) {
373
540
  // Convert Web ReadableStream to Node Readable stream
374
541
  const nodeStream = Readable.fromWeb(response.body);
375
- // Record SSE events while forwarding
542
+ // Record SSE events via batch buffer (memory: ≤10 batch + ≤5 tail)
376
543
  nodeStream.on('data', (chunk) => {
377
544
  const text = chunk.toString();
378
545
  const lines = text.split('\n');
379
546
  for (const line of lines) {
380
- // Handle both standard SSE and Aliyun format
381
- // Standard: data: {...}
382
- // Aliyun: event:xxx\ndata:{...}
383
547
  if (line.startsWith('data:')) {
384
- const eventData = line.slice(5).trim(); // Remove 'data:' prefix
548
+ const eventData = line.slice(5).trim();
385
549
  if (eventData && eventData !== '[DONE]') {
386
550
  try {
387
551
  const event = JSON.parse(eventData);
388
- requestData.response.events.push(event);
552
+ pushEventToBuffers(event, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
389
553
  }
390
554
  catch {
391
555
  // Not valid JSON, skip
@@ -395,25 +559,22 @@ export async function setupProxy(fastify, proxyConfig) {
395
559
  }
396
560
  });
397
561
  // Save request data when stream completes
398
- nodeStream.on('end', async () => {
399
- const elapsed = Date.now() - startTime;
400
- requestData.response.completedAt = new Date().toISOString();
401
- await saveRequestDB(sessionId, cloudRequestId, requestData);
402
- const usage = extractTokenUsage(requestData.response.events);
403
- pushNewRequest({
404
- requestId: cloudRequestId,
405
- sessionId,
406
- compositeId: `${sessionId}:${cloudRequestId}`,
407
- method: request.method,
408
- path: request.url,
409
- timestamp: requestData.timestamp,
410
- statusCode: response.status,
411
- responseTime: elapsed,
412
- model: forwardedModel,
413
- machineId,
414
- usage
415
- });
416
- request.log.info(`stream completed: ${cloudRequestId} ${response.status} (${elapsed}ms, ${requestData.response.events.length} events)`);
562
+ finished(nodeStream, async (err) => {
563
+ if (err) {
564
+ request.log.error(`claude stream error: ${err.message}`);
565
+ }
566
+ if (cloudRequestId !== request.id) {
567
+ deleteResponseEvents(request.id, sessionId);
568
+ deleteRequestRow(request.id, sessionId);
569
+ }
570
+ try {
571
+ await completeStreamSave(sessionId, cloudRequestId, requestData, eventBatch, tailBuffer, eventIndexRef, startTime, response.status, resolved, forwardedModel, originalModel, machineId, request);
572
+ const elapsed = Date.now() - startTime;
573
+ request.log.info(`stream completed: ${cloudRequestId} ${response.status} (${elapsed}ms, ${eventIndexRef.value} events)`);
574
+ }
575
+ catch (dbErr) {
576
+ request.log.error(`stream save failed: ${dbErr.message}`);
577
+ }
417
578
  });
418
579
  // Pipe: Aliyun -> Claude Code
419
580
  nodeStream.pipe(reply.raw);
@@ -439,7 +600,7 @@ export async function setupProxy(fastify, proxyConfig) {
439
600
  model: originalModel,
440
601
  machineId: getMachineIdSync() ?? undefined,
441
602
  baseUrl: resolved.baseUrl
442
- });
603
+ }, 'completed');
443
604
  const statusCode = getErrorStatusCode(error);
444
605
  reply.code(statusCode).send({
445
606
  error: getErrorMessage(error),