@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.
- package/README.md +11 -35
- package/dist/cli/commands/doc.d.ts +4 -0
- package/dist/cli/commands/doc.js +31 -0
- package/dist/cli/index.js +7 -0
- package/dist/config/index.js +2 -1
- package/dist/config/schema.d.ts +48 -0
- package/dist/config/schema.js +11 -1
- package/dist/parsers/docx-loader.d.ts +5 -0
- package/dist/parsers/docx-loader.js +15 -0
- package/dist/parsers/index.d.ts +3 -0
- package/dist/parsers/index.js +10 -0
- package/dist/parsers/md-loader.d.ts +5 -0
- package/dist/parsers/md-loader.js +13 -0
- package/dist/parsers/registry.d.ts +8 -0
- package/dist/parsers/registry.js +32 -0
- package/dist/parsers/types.d.ts +4 -0
- package/dist/parsers/types.js +1 -0
- package/dist/parsers/xlsx-loader.d.ts +5 -0
- package/dist/parsers/xlsx-loader.js +34 -0
- package/dist/server/__tests__/providers-route.test.js +4 -3
- package/dist/server/__tests__/proxy.test.js +11 -1
- package/dist/server/chat-route.d.ts +3 -0
- package/dist/server/chat-route.js +401 -0
- package/dist/server/claude-settings-route.d.ts +2 -0
- package/dist/server/claude-settings-route.js +76 -0
- package/dist/server/data-push.d.ts +3 -1
- package/dist/server/data-push.js +10 -3
- package/dist/server/docs-route.d.ts +2 -0
- package/dist/server/docs-route.js +63 -0
- package/dist/server/index.js +4 -0
- package/dist/server/mcp-route.d.ts +2 -0
- package/dist/server/mcp-route.js +161 -0
- package/dist/server/proxy.d.ts +4 -1
- package/dist/server/proxy.js +289 -128
- package/dist/server/ui-route.js +123 -46
- package/dist/tools/mcp-bridge.d.ts +13 -0
- package/dist/tools/mcp-bridge.js +36 -0
- package/dist/tools/registry.js +10 -4
- package/dist/ui/assets/{1c-CgVqmGt6.js → 1c-KmWe8RWs.js} +1 -1
- package/dist/ui/assets/{abnf-JPM4b6fj.js → abnf-BKQ6qIuo.js} +1 -1
- package/dist/ui/assets/{abnfDiagram-VRR7QNED-DJkd8PHl.js → abnfDiagram-VRR7QNED-CkRuDPu1.js} +1 -1
- package/dist/ui/assets/{accesslog-CLIMhXWi.js → accesslog-DD9H5Ktc.js} +1 -1
- package/dist/ui/assets/{actionscript-DqniQGld.js → actionscript-CAjf2Qtd.js} +1 -1
- package/dist/ui/assets/{ada-2BFfY7fF.js → ada-BXkXW16O.js} +1 -1
- package/dist/ui/assets/{angelscript-C47qRqcs.js → angelscript-BxT7o0Up.js} +1 -1
- package/dist/ui/assets/{apache-DP-LC5-h.js → apache-C5z_tmVm.js} +1 -1
- package/dist/ui/assets/{applescript-BB0gk3HQ.js → applescript-DdIxl8rr.js} +1 -1
- package/dist/ui/assets/{arc-D8wRCC4D.js → arc-hSdFaqKn.js} +1 -1
- package/dist/ui/assets/{arcade-1R6g1VPm.js → arcade-D5m0roat.js} +1 -1
- package/dist/ui/assets/{architectureDiagram-ZJ3FMSHR-CbHhHV9v.js → architectureDiagram-ZJ3FMSHR-DOSHSqzf.js} +1 -1
- package/dist/ui/assets/{arduino-B8YryK0R.js → arduino-FDap8kq-.js} +1 -1
- package/dist/ui/assets/{armasm-CYdj7mzR.js → armasm-Kzp-Jr1v.js} +1 -1
- package/dist/ui/assets/{asciidoc-bZ11zz6i.js → asciidoc-DbGMW3EE.js} +1 -1
- package/dist/ui/assets/{aspectj-D5WPU-RQ.js → aspectj-CVYMmBCO.js} +1 -1
- package/dist/ui/assets/{autohotkey-70tZvCLL.js → autohotkey-9PmBGTPd.js} +1 -1
- package/dist/ui/assets/{autoit-CX2GaaHP.js → autoit-B4F86jgh.js} +1 -1
- package/dist/ui/assets/{avrasm-C154El0d.js → avrasm-CSzv2eHX.js} +1 -1
- package/dist/ui/assets/{awk-DdXKOeux.js → awk-CjsvYvZE.js} +1 -1
- package/dist/ui/assets/{axapta-CwLwkXST.js → axapta-BVgMoNRY.js} +1 -1
- package/dist/ui/assets/{bash-Uu61S1nB.js → bash-IeBjW3pt.js} +1 -1
- package/dist/ui/assets/{basic-CeiAWoiF.js → basic-BTFJfmV1.js} +1 -1
- package/dist/ui/assets/{blockDiagram-677ZJIJ3-DcvhphUd.js → blockDiagram-677ZJIJ3-DLw3Bbhf.js} +1 -1
- package/dist/ui/assets/{bnf-B5KF61QW.js → bnf-rCkeRYk0.js} +1 -1
- package/dist/ui/assets/{brainfuck-CP0Y7_VW.js → brainfuck-DBHuB3uZ.js} +1 -1
- package/dist/ui/assets/{coq-D-WTlq_d.js → c-C95V0zHL.js} +1 -1
- package/dist/ui/assets/{c-like-DXr53gS6.js → c-like-CK1xGeX_.js} +1 -1
- package/dist/ui/assets/{c4Diagram-LMCZKHZV-Cn017lmJ.js → c4Diagram-LMCZKHZV-cqY51yJw.js} +1 -1
- package/dist/ui/assets/{cal-7ld1j-Wh.js → cal-BxGPpEZK.js} +1 -1
- package/dist/ui/assets/{capnproto-DrddNBCZ.js → capnproto-Bo2fgXF9.js} +1 -1
- package/dist/ui/assets/{ceylon-XPuE6IIL.js → ceylon-C4Bb4pNB.js} +1 -1
- package/dist/ui/assets/channel-D9Y4iN1l.js +1 -0
- package/dist/ui/assets/{chunk-2Q5K7J3B-BoX3IESS.js → chunk-2Q5K7J3B-CGTiTBmo.js} +1 -1
- package/dist/ui/assets/{chunk-32BRIVSS-C300ACzq.js → chunk-32BRIVSS-DWagYBp9.js} +1 -1
- package/dist/ui/assets/{chunk-5VM5RSS4-MtSkFsde.js → chunk-5VM5RSS4-BzSBMKJX.js} +1 -1
- package/dist/ui/assets/{chunk-EX3LRPZG-CEeHrGNV.js → chunk-EX3LRPZG-4-aKwCgZ.js} +1 -1
- package/dist/ui/assets/{chunk-JWPE2WC7-DTRgpK0R.js → chunk-JWPE2WC7-CPC_cBN0.js} +1 -1
- package/dist/ui/assets/{chunk-MOJQB5TN-DiOl0HJx.js → chunk-MOJQB5TN-DrDE4H9w.js} +1 -1
- package/dist/ui/assets/{chunk-RYQCIY6F-CVC3opqY.js → chunk-RYQCIY6F-DkdXe_kU.js} +1 -1
- package/dist/ui/assets/{chunk-V7JOEXUC-yQYlqQCC.js → chunk-V7JOEXUC-Da6EWcQt.js} +1 -1
- package/dist/ui/assets/{chunk-VR4S4FIN-iAA8ZgkK.js → chunk-VR4S4FIN-nP6dstD5.js} +1 -1
- package/dist/ui/assets/{chunk-XXDRQBXY-B303eieF.js → chunk-XXDRQBXY-Bi_rRAPA.js} +1 -1
- package/dist/ui/assets/classDiagram-OUVF2IWQ-C4UVH6RI.js +1 -0
- package/dist/ui/assets/classDiagram-v2-EOCWNBFH-C4UVH6RI.js +1 -0
- package/dist/ui/assets/{clean-MfHaW_uS.js → clean-cc1m9AxK.js} +1 -1
- package/dist/ui/assets/{clojure-9L8TfiWA.js → clojure-DX5V8QA8.js} +1 -1
- package/dist/ui/assets/{clojure-repl-CjsH9sxr.js → clojure-repl-FzjnVNWQ.js} +1 -1
- package/dist/ui/assets/{cmake-D0D1Q30G.js → cmake-COZZyrKs.js} +1 -1
- package/dist/ui/assets/{coffeescript-BI4jWZnB.js → coffeescript-DBsnOmC4.js} +1 -1
- package/dist/ui/assets/{c-BgqCrSpv.js → coq-TwFFolHa.js} +1 -1
- package/dist/ui/assets/{cos-DHax5LIJ.js → cos-Dm5g-IQT.js} +1 -1
- package/dist/ui/assets/{cose-bilkent-JH36ORCC-BzrKmTAZ.js → cose-bilkent-JH36ORCC-B3-weOQF.js} +1 -1
- package/dist/ui/assets/{cpp-M4aMPUas.js → cpp-_RZxQjg7.js} +1 -1
- package/dist/ui/assets/{crmsh-DAAczfR0.js → crmsh-DGPJ11Gt.js} +1 -1
- package/dist/ui/assets/{crystal-7g2CpWm4.js → crystal-BOMSPGge.js} +1 -1
- package/dist/ui/assets/{csharp-DW5GdKKf.js → csharp-DZXiHH72.js} +1 -1
- package/dist/ui/assets/{csp-Bq74fG7-.js → csp-DDSIMUoU.js} +1 -1
- package/dist/ui/assets/{css-DHyeLXff.js → css-Bjye-ZWr.js} +1 -1
- package/dist/ui/assets/{cynefin-VYW2F7L2-DwV1t9iV.js → cynefin-VYW2F7L2-Cj5c4L3y.js} +1 -1
- package/dist/ui/assets/{cynefinDiagram-TSTJHNR4-kBCFRYPz.js → cynefinDiagram-TSTJHNR4-BYsfTeNX.js} +1 -1
- package/dist/ui/assets/{d-yey2lm3v.js → d-22pfurOx.js} +1 -1
- package/dist/ui/assets/{dagre-VKFMJZFB-CErVJ1s0.js → dagre-VKFMJZFB-DiEob-by.js} +1 -1
- package/dist/ui/assets/{dart-BadDxflP.js → dart-Doiu8cDW.js} +1 -1
- package/dist/ui/assets/{delphi-CqzVqtDp.js → delphi-CQ7epp84.js} +1 -1
- package/dist/ui/assets/{diagram-FQU43EPY-C7zAW2DU.js → diagram-FQU43EPY-BOPbQcL4.js} +1 -1
- package/dist/ui/assets/{diagram-G47NLZAW-CbUb6BZI.js → diagram-G47NLZAW-C1UWDJTx.js} +1 -1
- package/dist/ui/assets/{diagram-NH7WQ7WH-C5mgi3dn.js → diagram-NH7WQ7WH-CYsBqW3J.js} +1 -1
- package/dist/ui/assets/{diagram-OA4YK3LP-Cok2YNfS.js → diagram-OA4YK3LP-Dnag0x_3.js} +1 -1
- package/dist/ui/assets/{diagram-WEI45ONY-BODuP2EB.js → diagram-WEI45ONY-Bo4CsDoj.js} +1 -1
- package/dist/ui/assets/{diff-EVigJ-h2.js → diff-Cs6VQQZt.js} +1 -1
- package/dist/ui/assets/{django-BH2EEal6.js → django-Bo3okOrw.js} +1 -1
- package/dist/ui/assets/{dns-BSc8VTm8.js → dns-BpG60eIX.js} +1 -1
- package/dist/ui/assets/{dockerfile-Br_pZjru.js → dockerfile-BWyH8SlV.js} +1 -1
- package/dist/ui/assets/{dos-Bx3ludet.js → dos-DWlNbaB9.js} +1 -1
- package/dist/ui/assets/{dsconfig-DSI-ePne.js → dsconfig-D_8y8YrJ.js} +1 -1
- package/dist/ui/assets/{dts-ClCuE1Xq.js → dts-Cz4SyMYR.js} +1 -1
- package/dist/ui/assets/{dust-CC9Z8iuW.js → dust-Cu4qLkeH.js} +1 -1
- package/dist/ui/assets/{ebnf-DI0w_fbi.js → ebnf-CTVHui3r.js} +1 -1
- package/dist/ui/assets/{ebnfDiagram-CCIWWBDH-CgsnO8Qm.js → ebnfDiagram-CCIWWBDH-epcXQLEj.js} +1 -1
- package/dist/ui/assets/{elixir-hdzZik8p.js → elixir-MAlwXhfU.js} +1 -1
- package/dist/ui/assets/{elm-DJAgqt5I.js → elm-Bdmwbvef.js} +1 -1
- package/dist/ui/assets/{erDiagram-Q63AITRT-K4zJcEKd.js → erDiagram-Q63AITRT-Bb60TqAM.js} +1 -1
- package/dist/ui/assets/{erb-Cq4OQqkl.js → erb-Ce9TpP4g.js} +1 -1
- package/dist/ui/assets/{erlang-CHoXLODu.js → erlang-CN2GEiqk.js} +1 -1
- package/dist/ui/assets/{erlang-repl-Brp70Zip.js → erlang-repl-D8K4O_cp.js} +1 -1
- package/dist/ui/assets/{excel-DjS-QR9P.js → excel-eVZU1r0f.js} +1 -1
- package/dist/ui/assets/{fix-BsO9Oq6H.js → fix-BzKerEv0.js} +1 -1
- package/dist/ui/assets/{flix-2A7CKozm.js → flix-B9CE3skV.js} +1 -1
- package/dist/ui/assets/{flowDiagram-23GEKE2U-Ck4tfxWX.js → flowDiagram-23GEKE2U-DzMLZIyt.js} +1 -1
- package/dist/ui/assets/{fortran-BQu3wORI.js → fortran-B4__3vos.js} +1 -1
- package/dist/ui/assets/{fsharp-D439S6PZ.js → fsharp-BJ3-ARHz.js} +1 -1
- package/dist/ui/assets/{gams-CPRxKfRH.js → gams-Cu43oSYT.js} +1 -1
- package/dist/ui/assets/{ganttDiagram-NO4QXBWP-QTPyXXxa.js → ganttDiagram-NO4QXBWP-D_aAoMRP.js} +1 -1
- package/dist/ui/assets/{gauss-GJFW9Vod.js → gauss-T8vk_TpE.js} +1 -1
- package/dist/ui/assets/{gcode-CF6woDAl.js → gcode-9j3BicpZ.js} +1 -1
- package/dist/ui/assets/{gherkin-Co-YjHfW.js → gherkin-Dyh5KFG-.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-IHSO6WYX-Do9VfPHL.js → gitGraphDiagram-IHSO6WYX-CMxCHC-Z.js} +1 -1
- package/dist/ui/assets/{glsl-D3lldBTJ.js → glsl-DWWjN4ts.js} +1 -1
- package/dist/ui/assets/{gml-DyoCibnw.js → gml-BcYE-D5A.js} +1 -1
- package/dist/ui/assets/{go-BNeAS1jC.js → go-DUnUzOEI.js} +1 -1
- package/dist/ui/assets/{golo-BuXecWAH.js → golo-BDoc1QQL.js} +1 -1
- package/dist/ui/assets/{gradle-BP7ToLe9.js → gradle-B_WRa_bT.js} +1 -1
- package/dist/ui/assets/{groovy-GqWVT09g.js → groovy-CgjqFq1e.js} +1 -1
- package/dist/ui/assets/{haml-SLbvCHsC.js → haml-DCqcg7Ti.js} +1 -1
- package/dist/ui/assets/{handlebars-BnaCJTc5.js → handlebars-DSDUQrnw.js} +1 -1
- package/dist/ui/assets/{haskell-CrX9ftWp.js → haskell-DPVnctVi.js} +1 -1
- package/dist/ui/assets/{haxe-DNoaIxsE.js → haxe-BuCGXBa4.js} +1 -1
- package/dist/ui/assets/{hsp-CLjy3djj.js → hsp-BTKpS_u0.js} +1 -1
- package/dist/ui/assets/{htmlbars-w96Kz5NL.js → htmlbars-Cql3Jvy2.js} +1 -1
- package/dist/ui/assets/{http-VIMXIxM8.js → http-BB8I9jUh.js} +1 -1
- package/dist/ui/assets/{hy-_qE83_aW.js → hy-DG7xkZNz.js} +1 -1
- package/dist/ui/assets/index-CK_lJsTu.css +10 -0
- package/dist/ui/assets/index-CfTIGTiY.js +2677 -0
- package/dist/ui/assets/{index-DpcPfZl3.js → index-DqIiCkSD.js} +3 -3
- package/dist/ui/assets/{infoDiagram-FWYZ7A6U-D9O7SZ2X.js → infoDiagram-FWYZ7A6U-DIzEV-BJ.js} +1 -1
- package/dist/ui/assets/{inform7-CnyW73C1.js → inform7-BWNpVEkA.js} +1 -1
- package/dist/ui/assets/{ini-GFMFAjln.js → ini-BMw_C8EP.js} +1 -1
- package/dist/ui/assets/{irpf90-BPmSYATn.js → irpf90-Cy42ibFZ.js} +1 -1
- package/dist/ui/assets/{isbl-BnStGiBQ.js → isbl-CjFSc0hz.js} +1 -1
- package/dist/ui/assets/{ishikawaDiagram-FXEZZL3T-DgcUDM9X.js → ishikawaDiagram-FXEZZL3T-BZcxp5am.js} +1 -1
- package/dist/ui/assets/{java-CVORcFd8.js → java-BDxe9dp1.js} +1 -1
- package/dist/ui/assets/{javascript-B2pFzODm.js → javascript-CgLkkj7h.js} +1 -1
- package/dist/ui/assets/{jboss-cli-B9cQeKvZ.js → jboss-cli-C8biclrb.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-5HDEW3XC-Bivjw4h7.js → journeyDiagram-5HDEW3XC-BbJu6m6Q.js} +1 -1
- package/dist/ui/assets/{json-BJof8L10.js → json-BjJKhmM6.js} +1 -1
- package/dist/ui/assets/{julia-D_dGrCpZ.js → julia-DvKEpg-R.js} +1 -1
- package/dist/ui/assets/{julia-repl-Dsw0K1B1.js → julia-repl-BDJWZxkU.js} +1 -1
- package/dist/ui/assets/{kanban-definition-HUTT4EX6-Dg7NbKta.js → kanban-definition-HUTT4EX6-DeIVPlbv.js} +1 -1
- package/dist/ui/assets/{kotlin-8oetHRtX.js → kotlin-DCoXcnEb.js} +1 -1
- package/dist/ui/assets/{lasso-e37r_NX1.js → lasso-C7m0QmV2.js} +1 -1
- package/dist/ui/assets/{latex-DAmwzl3n.js → latex-SHnTG9Eq.js} +1 -1
- package/dist/ui/assets/{ldif-CD0mgmt4.js → ldif-_6hUnKPV.js} +1 -1
- package/dist/ui/assets/{leaf-GfPKlp7a.js → leaf-Co9qxlaF.js} +1 -1
- package/dist/ui/assets/{less-PndaZJ6n.js → less-CJRDPwNA.js} +1 -1
- package/dist/ui/assets/{lisp-CNaspu3A.js → lisp-JEHGYbiW.js} +1 -1
- package/dist/ui/assets/{livecodeserver-DEKsXiP3.js → livecodeserver-BSYaUj4k.js} +1 -1
- package/dist/ui/assets/{livescript-DM7jasJn.js → livescript-WQMCQeuz.js} +1 -1
- package/dist/ui/assets/{llvm-D_29j9MV.js → llvm-DnAHqj5I.js} +1 -1
- package/dist/ui/assets/{lsl-DY0WRYhg.js → lsl--k-ghYia.js} +1 -1
- package/dist/ui/assets/{lua-DZ_v4xaE.js → lua-CXb23hbC.js} +1 -1
- package/dist/ui/assets/{makefile-VOjcUGXf.js → makefile-CM7o86yn.js} +1 -1
- package/dist/ui/assets/{markdown-yUEmZWvu.js → markdown-HB3HvChQ.js} +1 -1
- package/dist/ui/assets/{mathematica-BLXYGKkd.js → mathematica-9NTuf22N.js} +1 -1
- package/dist/ui/assets/{matlab-B7SkIpvh.js → matlab-CWv-ik4r.js} +1 -1
- package/dist/ui/assets/{maxima-C7Fw8Vyt.js → maxima-CqYiKCrw.js} +1 -1
- package/dist/ui/assets/{mel-C_Znst2Y.js → mel-BbsRpUm5.js} +1 -1
- package/dist/ui/assets/{mercury-DjUnQNsq.js → mercury-Cz2j4HhU.js} +1 -1
- package/dist/ui/assets/{mindmap-definition-LN4V7U3C-Dp6mEy6L.js → mindmap-definition-LN4V7U3C-WyJS8oye.js} +1 -1
- package/dist/ui/assets/{mipsasm-DaGHhR8c.js → mipsasm-BqWSRbQV.js} +1 -1
- package/dist/ui/assets/{mizar-CZnct-_Z.js → mizar-JLBfQ6vt.js} +1 -1
- package/dist/ui/assets/{mojolicious-BdWFK35j.js → mojolicious-D0LbMmyk.js} +1 -1
- package/dist/ui/assets/{monkey-ta_jgafo.js → monkey-DAGL6-JN.js} +1 -1
- package/dist/ui/assets/{moonscript-B9qovjV6.js → moonscript-DzbwNSKy.js} +1 -1
- package/dist/ui/assets/{n1ql-BciXbxiw.js → n1ql-dkgKq6Yv.js} +1 -1
- package/dist/ui/assets/{nginx-DRCZYbxj.js → nginx-AUoaDPV4.js} +1 -1
- package/dist/ui/assets/{nim-DTMCxPJt.js → nim-CdqYmVrl.js} +1 -1
- package/dist/ui/assets/{nix-D39yZxF9.js → nix-CxmVjwwo.js} +1 -1
- package/dist/ui/assets/{node-repl-vAEXdnYM.js → node-repl-B1Z3zUAh.js} +1 -1
- package/dist/ui/assets/{nsis-CCMP8mq3.js → nsis-CTAuPWhe.js} +1 -1
- package/dist/ui/assets/{objectivec-CZx4kSLP.js → objectivec-CvbKeAPz.js} +1 -1
- package/dist/ui/assets/{ocaml-DBnEo9uR.js → ocaml-DCiyXKOH.js} +1 -1
- package/dist/ui/assets/{openscad-BKc7u4Bl.js → openscad-CPGUwmeR.js} +1 -1
- package/dist/ui/assets/{oxygene-M_Z4lQ6l.js → oxygene-CI_bD1Gw.js} +1 -1
- package/dist/ui/assets/{parser3-DS9UKiKD.js → parser3-C91CVs85.js} +1 -1
- package/dist/ui/assets/{pegDiagram-2B236MQR-BBEXn76M.js → pegDiagram-2B236MQR-yF8AW7GR.js} +1 -1
- package/dist/ui/assets/{perl-D6iEALGE.js → perl-ChxfKl-n.js} +1 -1
- package/dist/ui/assets/{pf-JQL0ClGi.js → pf-CKCHkzIA.js} +1 -1
- package/dist/ui/assets/{pgsql-DEmCURBr.js → pgsql-BwOP20uH.js} +1 -1
- package/dist/ui/assets/{php-DVm-0PY5.js → php-B7Rs2tM5.js} +1 -1
- package/dist/ui/assets/{php-template-DfWbDpnE.js → php-template-Cx-Fh4AR.js} +1 -1
- package/dist/ui/assets/{pieDiagram-ENE6RG2P-D5PZFiDj.js → pieDiagram-ENE6RG2P-BS4hnGNX.js} +1 -1
- package/dist/ui/assets/{plaintext-BahVkGlu.js → plaintext-U7qkdtgS.js} +1 -1
- package/dist/ui/assets/{pony-wVig0caC.js → pony-_fGYgePh.js} +1 -1
- package/dist/ui/assets/{powershell-COjMBUit.js → powershell-CFBTcUce.js} +1 -1
- package/dist/ui/assets/{processing-DnUzvFZx.js → processing-BWHneU0K.js} +1 -1
- package/dist/ui/assets/{profile-mDlbwJeL.js → profile-BJ-HxhzS.js} +1 -1
- package/dist/ui/assets/{prolog-x2hG6DnA.js → prolog-ChHo7n6L.js} +1 -1
- package/dist/ui/assets/{properties-DzZyyivC.js → properties-e_DOYclC.js} +1 -1
- package/dist/ui/assets/{protobuf-a5NwbYD5.js → protobuf-5aGSIv7w.js} +1 -1
- package/dist/ui/assets/{puppet-CCWyPVuC.js → puppet-D2vQatzX.js} +1 -1
- package/dist/ui/assets/{purebasic-CXNdqtD4.js → purebasic-n6krfaeF.js} +1 -1
- package/dist/ui/assets/{python-iSR9YlDJ.js → python-CbIo0XxP.js} +1 -1
- package/dist/ui/assets/{python-repl-CCmKzWru.js → python-repl-BpIq9kyr.js} +1 -1
- package/dist/ui/assets/{q-B30ZmDuD.js → q-jntnvcCx.js} +1 -1
- package/dist/ui/assets/{qml-BGdPdB4X.js → qml-BafQlZNV.js} +1 -1
- package/dist/ui/assets/{quadrantDiagram-ABIIQ3AL-BBF1rAvI.js → quadrantDiagram-ABIIQ3AL-C3Th8mUJ.js} +1 -1
- package/dist/ui/assets/{r-B8ASdWL1.js → r-CHgvQFLK.js} +1 -1
- package/dist/ui/assets/{railroadDiagram-RFXS5EU6-DQFUXGVD.js → railroadDiagram-RFXS5EU6-CMQkbGJl.js} +1 -1
- package/dist/ui/assets/{reasonml-Dw4Bi5B5.js → reasonml-DNwlPD_T.js} +1 -1
- package/dist/ui/assets/{requirementDiagram-TGXJPOKE-DCgrs7-P.js → requirementDiagram-TGXJPOKE-B70_oR7v.js} +1 -1
- package/dist/ui/assets/{rib-CgUGGGZ9.js → rib-CX4e9JSF.js} +1 -1
- package/dist/ui/assets/{roboconf-CCZNQvSP.js → roboconf-ee4qsPGf.js} +1 -1
- package/dist/ui/assets/{routeros-DVT5M6iV.js → routeros-BJSwvu2L.js} +1 -1
- package/dist/ui/assets/{rsl-BY-LhdKq.js → rsl-BiKdMl_F.js} +1 -1
- package/dist/ui/assets/{ruby-CZW4dclw.js → ruby-CAXTU5LY.js} +1 -1
- package/dist/ui/assets/{ruleslanguage-CZdjv1of.js → ruleslanguage-C1kgDGXM.js} +1 -1
- package/dist/ui/assets/{rust-D5Xa_mSD.js → rust-DyEf_GXL.js} +1 -1
- package/dist/ui/assets/{sankeyDiagram-HTMAVEWB-BXeEgUB7.js → sankeyDiagram-HTMAVEWB-CvUfseMg.js} +1 -1
- package/dist/ui/assets/{sas-B_UJ-HCZ.js → sas-Q1rlUxPH.js} +1 -1
- package/dist/ui/assets/{scala-B8wSdZik.js → scala-Dg6KNmge.js} +1 -1
- package/dist/ui/assets/{scheme-DD7U81eh.js → scheme-D9iooUxI.js} +1 -1
- package/dist/ui/assets/{scilab-aJGpbkKs.js → scilab-CztEJXJY.js} +1 -1
- package/dist/ui/assets/{scss-BBqyvWYh.js → scss-BMh-JkdE.js} +1 -1
- package/dist/ui/assets/{sequenceDiagram-DBY2YBRQ-CnDTZN18.js → sequenceDiagram-DBY2YBRQ-DB0c9kf4.js} +1 -1
- package/dist/ui/assets/{shell-CR23jqfz.js → shell-DWo6z0MG.js} +1 -1
- package/dist/ui/assets/{sizeCapture-X5ZJPWSS-joZ477RB.js → sizeCapture-X5ZJPWSS-BcQlgjfD.js} +1 -1
- package/dist/ui/assets/{smali-ZmLyRCrQ.js → smali-Cijeb6pL.js} +1 -1
- package/dist/ui/assets/{smalltalk-CnADYfPr.js → smalltalk-D0lvqa_d.js} +1 -1
- package/dist/ui/assets/{sml-zj1KD04_.js → sml-B-vqWZ3b.js} +1 -1
- package/dist/ui/assets/{sqf-DCCaZ3e3.js → sqf-BJ-1Q583.js} +1 -1
- package/dist/ui/assets/{sql-CHGw2SCR.js → sql-CMGLxEYV.js} +1 -1
- package/dist/ui/assets/{sql_more-DsnvjMwh.js → sql_more-gsRmbNCH.js} +1 -1
- package/dist/ui/assets/{stan-ChxdV8f9.js → stan-D1AlTSpf.js} +1 -1
- package/dist/ui/assets/{stata-CgFw45qg.js → stata-DnEYviI0.js} +1 -1
- package/dist/ui/assets/{stateDiagram-2N3HPSRC-CzLIdKbt.js → stateDiagram-2N3HPSRC-BSRgVsHO.js} +1 -1
- package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-BLdfpGJO.js +1 -0
- package/dist/ui/assets/{step21-DaraQMOk.js → step21-DbQV9sMo.js} +1 -1
- package/dist/ui/assets/{stylus-BZpiJEzN.js → stylus-Cyc7vdUc.js} +1 -1
- package/dist/ui/assets/{subunit-B6Gp0n7h.js → subunit-16ipyMie.js} +1 -1
- package/dist/ui/assets/{swift-DQzoz60p.js → swift-DTNnw4qS.js} +1 -1
- package/dist/ui/assets/{swimlanes-5IMT3BWC-DOMOhwX0.js → swimlanes-5IMT3BWC-DgOdcc4_.js} +2 -2
- package/dist/ui/assets/swimlanesDiagram-G3AALYLV-DRVnkg3M.js +8 -0
- package/dist/ui/assets/{taggerscript-BMLkfiab.js → taggerscript-DM6pvUWE.js} +1 -1
- package/dist/ui/assets/{tap-PUNWTFgu.js → tap-Ch-uyKZq.js} +1 -1
- package/dist/ui/assets/{tcl-BODH5qJa.js → tcl-D0T_V5dA.js} +1 -1
- package/dist/ui/assets/{thrift-BQ-W_YlC.js → thrift-D9xJjYw4.js} +1 -1
- package/dist/ui/assets/{timeline-definition-FHXFAJF6-DQBDeTzH.js → timeline-definition-FHXFAJF6-BBeDMHxb.js} +1 -1
- package/dist/ui/assets/{tp-Dt3WwOGF.js → tp-kUrCGo9X.js} +1 -1
- package/dist/ui/assets/{twig-44RQtw7Z.js → twig-Cgub6rxA.js} +1 -1
- package/dist/ui/assets/{typescript-BsouHXHR.js → typescript-BxNP9Ap9.js} +1 -1
- package/dist/ui/assets/{vala-DqJj6fPT.js → vala-DF9ZZagU.js} +1 -1
- package/dist/ui/assets/{vbnet-CuqWYg5r.js → vbnet-Cf46ypDg.js} +1 -1
- package/dist/ui/assets/{vbscript-w20MQMYP.js → vbscript-DNs-QKLH.js} +1 -1
- package/dist/ui/assets/{vbscript-html-BP0D0WEV.js → vbscript-html-DyS6I5L1.js} +1 -1
- package/dist/ui/assets/{vennDiagram-L72KCM5P-DwNnj3Kc.js → vennDiagram-L72KCM5P-8WpWycE-.js} +1 -1
- package/dist/ui/assets/{verilog-DIpBpJAe.js → verilog-CF5wC4X-.js} +1 -1
- package/dist/ui/assets/{vhdl-D5nCxDbL.js → vhdl-Dm0jpDMn.js} +1 -1
- package/dist/ui/assets/{vim-CPfbBM_x.js → vim-nyO2DRAx.js} +1 -1
- package/dist/ui/assets/{wardleyDiagram-EHGQE667-WxVQHmM7.js → wardleyDiagram-EHGQE667-UeIOK-jv.js} +1 -1
- package/dist/ui/assets/{x86asm-D_t4Z_eq.js → x86asm-BNw2-6GG.js} +1 -1
- package/dist/ui/assets/{xl-DQf5IG_W.js → xl-D-YHxr6V.js} +1 -1
- package/dist/ui/assets/{xml-D-Bn0NMR.js → xml-BijAlrow.js} +1 -1
- package/dist/ui/assets/{xquery-DqushYX1.js → xquery-z0wxWBKz.js} +1 -1
- package/dist/ui/assets/{xychartDiagram-FW5EYKEG-B6D8mgs8.js → xychartDiagram-FW5EYKEG-BNzHLweN.js} +1 -1
- package/dist/ui/assets/{yaml-B2GFZenc.js → yaml-n5TIQArR.js} +1 -1
- package/dist/ui/assets/{zephir-SiNGockR.js → zephir-DrGhH8jG.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/dist/utils/__tests__/providers-db.test.js +2 -1
- package/dist/utils/__tests__/tool-injector.test.js +32 -35
- package/dist/utils/db.d.ts +3 -0
- package/dist/utils/db.js +54 -0
- package/dist/utils/inject-loop.d.ts +52 -1
- package/dist/utils/inject-loop.js +70 -2
- package/dist/utils/mcp-db.d.ts +35 -0
- package/dist/utils/mcp-db.js +79 -0
- package/dist/utils/mcp-manager.d.ts +36 -0
- package/dist/utils/mcp-manager.js +215 -0
- package/dist/utils/openai-converter.d.ts +99 -0
- package/dist/utils/openai-converter.js +293 -0
- package/dist/utils/providers-db.d.ts +2 -0
- package/dist/utils/providers-db.js +19 -7
- package/dist/utils/request-writer.d.ts +20 -0
- package/dist/utils/request-writer.js +184 -4
- package/dist/utils/sse-stream-processor.d.ts +27 -0
- package/dist/utils/sse-stream-processor.js +144 -0
- package/dist/utils/stats-aggregator.js +2 -0
- package/dist/utils/status-history.js +3 -3
- package/dist/utils/tool-injector.d.ts +6 -9
- package/dist/utils/tool-injector.js +13 -14
- package/dist/utils/version.d.ts +6 -0
- package/dist/utils/version.js +11 -1
- package/package.json +14 -12
- package/dist/ui/assets/channel-C5JlZ38H.js +0 -1
- package/dist/ui/assets/classDiagram-OUVF2IWQ-j5FVBKaD.js +0 -1
- package/dist/ui/assets/classDiagram-v2-EOCWNBFH-j5FVBKaD.js +0 -1
- package/dist/ui/assets/index-Baqd6LlK.js +0 -2625
- package/dist/ui/assets/index-fHtNI_e8.css +0 -1
- package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-Bg4GAkB5.js +0 -1
- package/dist/ui/assets/swimlanesDiagram-G3AALYLV-99wEK8yq.js +0 -8
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { Readable } from 'stream';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { loadConfig } from '../config/index.js';
|
|
4
|
+
import { saveRequestDB, extractTokenUsage, loadSessionHistory } from '../utils/request-writer.js';
|
|
5
|
+
import { cleanOldRequestsDB } from '../utils/request-writer.js';
|
|
6
|
+
import { pushNewRequest } from './data-push.js';
|
|
7
|
+
import { initDatabase, getDatabase } from '../utils/db.js';
|
|
8
|
+
import { resolveProxyTarget, getProxyModel, filterForwardHeaders, extractSafeHeaders } from './proxy.js';
|
|
9
|
+
import { injectMcpTools } from '../utils/tool-injector.js';
|
|
10
|
+
import { findInjectedCall, runJsonInjectLoop, runStreamingInjectLoop } from '../utils/inject-loop.js';
|
|
11
|
+
import { findEnabledTool } from '../tools/index.js';
|
|
12
|
+
import { writeSSEEvent, writeTextAsSSE, shouldMock } from '../utils/sse-writer.js';
|
|
13
|
+
import { SSEStreamProcessor } from '../utils/sse-stream-processor.js';
|
|
14
|
+
import { getMachineIdSync, preloadMachineId } from '../utils/machine-id.js';
|
|
15
|
+
export async function setupChatRoute(fastify, proxyConfig) {
|
|
16
|
+
const fullConfig = await loadConfig();
|
|
17
|
+
const maxDays = fullConfig.storage?.maxDays ?? 7;
|
|
18
|
+
initDatabase();
|
|
19
|
+
await preloadMachineId();
|
|
20
|
+
fastify.post('/api/chat', async (request, reply) => {
|
|
21
|
+
const startTime = Date.now();
|
|
22
|
+
const resolved = resolveProxyTarget(proxyConfig);
|
|
23
|
+
// Generate or reuse session ID
|
|
24
|
+
const providedSessionId = request.headers['x-chat-session-id'];
|
|
25
|
+
const sessionId = providedSessionId || randomUUID();
|
|
26
|
+
// Validate request body
|
|
27
|
+
if (!request.body?.model || !Array.isArray(request.body?.messages)) {
|
|
28
|
+
return reply.code(400).send({
|
|
29
|
+
error: 'invalid_request',
|
|
30
|
+
message: 'Request body must contain "model" and "messages" fields'
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// Session resume: if sessionId exists in DB, load history and prepend
|
|
34
|
+
if (providedSessionId) {
|
|
35
|
+
const history = loadSessionHistory(providedSessionId);
|
|
36
|
+
if (history) {
|
|
37
|
+
// Prepend history messages before the new user messages
|
|
38
|
+
request.body.messages = [...history.messages, ...request.body.messages];
|
|
39
|
+
// Carry over system prompt if provided and not in current request
|
|
40
|
+
if (history.system && !request.body.system && !request.body.system_prompt) {
|
|
41
|
+
request.body.system = history.system;
|
|
42
|
+
}
|
|
43
|
+
request.log.info(`[chat] session resumed: ${providedSessionId}, history: ${history.messages.length} messages`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Rewrite model based on tier
|
|
47
|
+
const originalModel = request.body.model;
|
|
48
|
+
const proxyModel = getProxyModel(originalModel, resolved);
|
|
49
|
+
if (proxyModel) {
|
|
50
|
+
request.body.model = proxyModel;
|
|
51
|
+
}
|
|
52
|
+
const forwardedModel = request.body.model;
|
|
53
|
+
// Inject MCP tools only (not probe/curl)
|
|
54
|
+
const injectedNames = injectMcpTools(request.body);
|
|
55
|
+
const injected = injectedNames.length > 0;
|
|
56
|
+
if (injected) {
|
|
57
|
+
request.log.info(`[chat] MCP tools injected: ${injectedNames.join(',')} (${request.id})`);
|
|
58
|
+
}
|
|
59
|
+
// Filter and forward headers
|
|
60
|
+
const forwardHeaders = filterForwardHeaders(extractSafeHeaders(request.headers), resolved.apiKey);
|
|
61
|
+
forwardHeaders['x-chat-session-id'] = sessionId;
|
|
62
|
+
// Extract API keys for recording
|
|
63
|
+
const authHeader = request.headers['authorization'];
|
|
64
|
+
const apiKey = authHeader?.startsWith('Bearer ')
|
|
65
|
+
? authHeader.slice(7)
|
|
66
|
+
: request.headers['x-api-key'];
|
|
67
|
+
const forwardedAuthHeader = forwardHeaders['authorization'];
|
|
68
|
+
const proxyApiKey = forwardHeaders['x-api-key'] ||
|
|
69
|
+
(forwardedAuthHeader?.startsWith('Bearer ') ? forwardedAuthHeader.slice(7) : undefined);
|
|
70
|
+
// Build request data
|
|
71
|
+
const machineId = getMachineIdSync() ?? undefined;
|
|
72
|
+
const requestData = {
|
|
73
|
+
requestId: request.id,
|
|
74
|
+
sessionId,
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
apiKey,
|
|
77
|
+
proxyApiKey,
|
|
78
|
+
baseUrl: resolved.baseUrl,
|
|
79
|
+
model: forwardedModel,
|
|
80
|
+
machineId,
|
|
81
|
+
request: {
|
|
82
|
+
method: request.method,
|
|
83
|
+
path: request.url,
|
|
84
|
+
headers: forwardHeaders,
|
|
85
|
+
body: request.body
|
|
86
|
+
},
|
|
87
|
+
response: {
|
|
88
|
+
statusCode: 0,
|
|
89
|
+
events: []
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
await cleanOldRequestsDB(maxDays);
|
|
93
|
+
// Save initial record + push pending status to frontend
|
|
94
|
+
await saveRequestDB(sessionId, request.id, requestData);
|
|
95
|
+
pushNewRequest({
|
|
96
|
+
requestId: request.id,
|
|
97
|
+
sessionId,
|
|
98
|
+
compositeId: `${sessionId}:${request.id}`,
|
|
99
|
+
method: request.method,
|
|
100
|
+
path: request.url,
|
|
101
|
+
timestamp: requestData.timestamp,
|
|
102
|
+
statusCode: 0,
|
|
103
|
+
responseTime: 0,
|
|
104
|
+
model: forwardedModel,
|
|
105
|
+
machineId,
|
|
106
|
+
baseUrl: resolved.baseUrl
|
|
107
|
+
}, 'pending');
|
|
108
|
+
// Mock interception (PoC)
|
|
109
|
+
if (shouldMock(request.body)) {
|
|
110
|
+
reply.hijack();
|
|
111
|
+
reply.raw.writeHead(200, {
|
|
112
|
+
'content-type': 'text/event-stream',
|
|
113
|
+
'cache-control': 'no-cache',
|
|
114
|
+
'connection': 'keep-alive'
|
|
115
|
+
});
|
|
116
|
+
writeTextAsSSE(reply.raw, '[PoC] 这是 /api/chat 构造的合成响应,未经过真实大模型。', `msg_poc_chat_${request.id}`);
|
|
117
|
+
reply.raw.end();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Build target URL — always target /v1/messages
|
|
121
|
+
const targetUrlStr = `${resolved.baseUrl}${resolved.targetPath}/v1/messages`;
|
|
122
|
+
request.log.info(`[chat] forwarding to: ${targetUrlStr}`);
|
|
123
|
+
// Forward to upstream
|
|
124
|
+
const response = await fetch(targetUrlStr, {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
headers: forwardHeaders,
|
|
127
|
+
body: JSON.stringify(request.body)
|
|
128
|
+
});
|
|
129
|
+
request.log.info(`[chat] response status: ${response.status}`);
|
|
130
|
+
// Extract cloud request ID
|
|
131
|
+
const cloudRequestId = response.headers.get('x-request-id') || request.id;
|
|
132
|
+
requestData.requestId = cloudRequestId;
|
|
133
|
+
requestData.response.statusCode = response.status;
|
|
134
|
+
requestData.response.headers = Object.fromEntries(response.headers);
|
|
135
|
+
// Check response type
|
|
136
|
+
const contentType = response.headers.get('content-type') || '';
|
|
137
|
+
const isSSE = contentType.includes('text/event-stream');
|
|
138
|
+
const isJSON = contentType.includes('application/json');
|
|
139
|
+
// === JSON response (non-streaming) ===
|
|
140
|
+
if (isJSON && !isSSE && response.body) {
|
|
141
|
+
const responseBody = await response.text();
|
|
142
|
+
let jsonBody;
|
|
143
|
+
try {
|
|
144
|
+
jsonBody = JSON.parse(responseBody);
|
|
145
|
+
requestData.response.events.push(jsonBody);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
requestData.response.events.push({ raw: responseBody });
|
|
149
|
+
}
|
|
150
|
+
// JSON inject loop: if MCP tools injected and response has tool_use
|
|
151
|
+
if (injected && jsonBody && jsonBody.stop_reason === 'tool_use') {
|
|
152
|
+
const toolCalls = (jsonBody.content || []).filter((b) => b.type === 'tool_use');
|
|
153
|
+
const hitCall = toolCalls.find((b) => findEnabledTool(b.name));
|
|
154
|
+
if (hitCall) {
|
|
155
|
+
request.log.info(`[chat] json tool hit: ${hitCall.name} (${cloudRequestId})`);
|
|
156
|
+
const loopResult = await runJsonInjectLoop({
|
|
157
|
+
initialBody: jsonBody,
|
|
158
|
+
requestBody: request.body,
|
|
159
|
+
callUpstream: async (b) => {
|
|
160
|
+
const r = await fetch(targetUrlStr, {
|
|
161
|
+
method: 'POST',
|
|
162
|
+
headers: forwardHeaders,
|
|
163
|
+
body: JSON.stringify(b)
|
|
164
|
+
});
|
|
165
|
+
const loopResponseBody = await r.text();
|
|
166
|
+
let loopJson;
|
|
167
|
+
try {
|
|
168
|
+
loopJson = JSON.parse(loopResponseBody);
|
|
169
|
+
}
|
|
170
|
+
catch { /* ignore */ }
|
|
171
|
+
// Record each loop iteration as a separate request
|
|
172
|
+
if (loopJson) {
|
|
173
|
+
const loopRequestId = r.headers.get('x-request-id') || `loop_${request.id}_${Date.now()}`;
|
|
174
|
+
const loopData = {
|
|
175
|
+
requestId: loopRequestId,
|
|
176
|
+
sessionId,
|
|
177
|
+
timestamp: new Date().toISOString(),
|
|
178
|
+
apiKey,
|
|
179
|
+
proxyApiKey,
|
|
180
|
+
baseUrl: resolved.baseUrl,
|
|
181
|
+
model: forwardedModel,
|
|
182
|
+
machineId,
|
|
183
|
+
request: { method: 'POST', path: '/api/chat', headers: forwardHeaders, body: b },
|
|
184
|
+
response: {
|
|
185
|
+
statusCode: r.status,
|
|
186
|
+
headers: Object.fromEntries(r.headers),
|
|
187
|
+
events: [loopJson],
|
|
188
|
+
completedAt: new Date().toISOString()
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
await saveRequestDB(sessionId, loopRequestId, loopData);
|
|
192
|
+
const loopUsage = extractTokenUsage(loopData.response.events);
|
|
193
|
+
pushNewRequest({
|
|
194
|
+
requestId: loopRequestId,
|
|
195
|
+
sessionId,
|
|
196
|
+
compositeId: `${sessionId}:${loopRequestId}`,
|
|
197
|
+
method: 'POST',
|
|
198
|
+
path: '/api/chat',
|
|
199
|
+
timestamp: loopData.timestamp,
|
|
200
|
+
statusCode: r.status,
|
|
201
|
+
responseTime: 0,
|
|
202
|
+
model: forwardedModel,
|
|
203
|
+
machineId,
|
|
204
|
+
baseUrl: resolved.baseUrl,
|
|
205
|
+
usage: loopUsage
|
|
206
|
+
}, 'completed');
|
|
207
|
+
}
|
|
208
|
+
return loopJson || JSON.parse(loopResponseBody);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
request.log.info(`[chat] json loop done: ${loopResult.iterations} iter (${cloudRequestId})`);
|
|
212
|
+
requestData.response.completedAt = new Date().toISOString();
|
|
213
|
+
// Remove pending row if cloudRequestId differs from request.id
|
|
214
|
+
if (cloudRequestId !== request.id) {
|
|
215
|
+
const db = getDatabase();
|
|
216
|
+
db.prepare('DELETE FROM requests WHERE request_id = ? AND session_id = ?').run(request.id, sessionId);
|
|
217
|
+
}
|
|
218
|
+
await saveRequestDB(sessionId, cloudRequestId, requestData);
|
|
219
|
+
const usage = extractTokenUsage(requestData.response.events);
|
|
220
|
+
pushNewRequest({
|
|
221
|
+
requestId: cloudRequestId,
|
|
222
|
+
sessionId,
|
|
223
|
+
compositeId: `${sessionId}:${cloudRequestId}`,
|
|
224
|
+
method: request.method,
|
|
225
|
+
path: request.url,
|
|
226
|
+
timestamp: requestData.timestamp,
|
|
227
|
+
statusCode: response.status,
|
|
228
|
+
responseTime: Date.now() - startTime,
|
|
229
|
+
model: forwardedModel,
|
|
230
|
+
machineId,
|
|
231
|
+
baseUrl: resolved.baseUrl,
|
|
232
|
+
usage,
|
|
233
|
+
pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
|
|
234
|
+
}, 'completed');
|
|
235
|
+
const finalBody = { ...loopResult.finalBody, _sessionId: sessionId };
|
|
236
|
+
reply.code(200).header('x-chat-session-id', sessionId).send(JSON.stringify(finalBody));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Normal JSON passthrough
|
|
241
|
+
requestData.response.completedAt = new Date().toISOString();
|
|
242
|
+
// Remove pending row if cloudRequestId differs from request.id
|
|
243
|
+
if (cloudRequestId !== request.id) {
|
|
244
|
+
const db = getDatabase();
|
|
245
|
+
db.prepare('DELETE FROM requests WHERE request_id = ? AND session_id = ?').run(request.id, sessionId);
|
|
246
|
+
}
|
|
247
|
+
await saveRequestDB(sessionId, cloudRequestId, requestData);
|
|
248
|
+
const usage = extractTokenUsage(requestData.response.events);
|
|
249
|
+
pushNewRequest({
|
|
250
|
+
requestId: cloudRequestId,
|
|
251
|
+
sessionId,
|
|
252
|
+
compositeId: `${sessionId}:${cloudRequestId}`,
|
|
253
|
+
method: request.method,
|
|
254
|
+
path: request.url,
|
|
255
|
+
timestamp: requestData.timestamp,
|
|
256
|
+
statusCode: response.status,
|
|
257
|
+
responseTime: Date.now() - startTime,
|
|
258
|
+
model: forwardedModel,
|
|
259
|
+
machineId,
|
|
260
|
+
baseUrl: resolved.baseUrl,
|
|
261
|
+
usage,
|
|
262
|
+
pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
|
|
263
|
+
}, 'completed');
|
|
264
|
+
// Inject session ID into response for passthrough
|
|
265
|
+
try {
|
|
266
|
+
const passthroughBody = JSON.parse(responseBody);
|
|
267
|
+
passthroughBody._sessionId = sessionId;
|
|
268
|
+
reply.code(response.status);
|
|
269
|
+
reply.header('x-chat-session-id', sessionId);
|
|
270
|
+
reply.send(JSON.stringify(passthroughBody));
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
reply.code(response.status);
|
|
274
|
+
reply.headers(Object.fromEntries(response.headers));
|
|
275
|
+
reply.send(responseBody);
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
// === SSE response (streaming — unified path) ===
|
|
280
|
+
if (isSSE && response.body) {
|
|
281
|
+
reply.hijack();
|
|
282
|
+
reply.raw.writeHead(response.status, { ...Object.fromEntries(response.headers), 'x-chat-session-id': sessionId });
|
|
283
|
+
const processor = new SSEStreamProcessor(reply.raw);
|
|
284
|
+
const nodeStream = Readable.fromWeb(response.body);
|
|
285
|
+
nodeStream.on('data', (chunk) => {
|
|
286
|
+
// SSEStreamProcessor.processChunk 同时转发到 reply.raw + 增量解析
|
|
287
|
+
processor.processChunk(chunk);
|
|
288
|
+
});
|
|
289
|
+
nodeStream.on('end', async () => {
|
|
290
|
+
processor.flushLeftover();
|
|
291
|
+
// 保存原始请求到 DB
|
|
292
|
+
requestData.response.events = processor.events;
|
|
293
|
+
requestData.response.completedAt = new Date().toISOString();
|
|
294
|
+
// Remove pending row if cloudRequestId differs from request.id
|
|
295
|
+
if (cloudRequestId !== request.id) {
|
|
296
|
+
const db = getDatabase();
|
|
297
|
+
db.prepare('DELETE FROM requests WHERE request_id = ? AND session_id = ?').run(request.id, sessionId);
|
|
298
|
+
}
|
|
299
|
+
await saveRequestDB(sessionId, cloudRequestId, requestData);
|
|
300
|
+
const usage = extractTokenUsage(requestData.response.events);
|
|
301
|
+
pushNewRequest({
|
|
302
|
+
requestId: cloudRequestId,
|
|
303
|
+
sessionId,
|
|
304
|
+
compositeId: `${sessionId}:${cloudRequestId}`,
|
|
305
|
+
method: request.method,
|
|
306
|
+
path: request.url,
|
|
307
|
+
timestamp: requestData.timestamp,
|
|
308
|
+
statusCode: response.status,
|
|
309
|
+
responseTime: Date.now() - startTime,
|
|
310
|
+
model: forwardedModel,
|
|
311
|
+
machineId,
|
|
312
|
+
baseUrl: resolved.baseUrl,
|
|
313
|
+
usage,
|
|
314
|
+
pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
|
|
315
|
+
}, 'completed');
|
|
316
|
+
request.log.info(`[chat] stream completed: ${cloudRequestId} ${response.status} (${Date.now() - startTime}ms, ${processor.events.length} events)`);
|
|
317
|
+
// 检测注入的 MCP tool_use → 发 proxy_status 信号 + 跑 streaming inject loop
|
|
318
|
+
if (processor.hasInjectedToolUse()) {
|
|
319
|
+
const injectedCall = findInjectedCall(processor.getParsedUpstream());
|
|
320
|
+
// 通知客户端:proxy 正在代行 tool,同连接上还有续接数据
|
|
321
|
+
writeSSEEvent(reply.raw, { type: 'proxy_status', status: 'tool_executing', tool: injectedCall?.name });
|
|
322
|
+
request.log.info(`[chat] SSE tool hit, entering streaming inject loop: ${cloudRequestId}`);
|
|
323
|
+
await runStreamingInjectLoop({
|
|
324
|
+
initialParsed: processor.getParsedUpstream(),
|
|
325
|
+
body: request.body,
|
|
326
|
+
callUpstream: async (b) => {
|
|
327
|
+
const r = await fetch(targetUrlStr, {
|
|
328
|
+
method: 'POST',
|
|
329
|
+
headers: forwardHeaders,
|
|
330
|
+
body: JSON.stringify(b)
|
|
331
|
+
});
|
|
332
|
+
const loopRequestId = r.headers.get('x-request-id') || `loop_${request.id}_${Date.now()}`;
|
|
333
|
+
return { response: r, requestId: loopRequestId };
|
|
334
|
+
},
|
|
335
|
+
streamAndParse: async (loopResponse) => {
|
|
336
|
+
// 流式转发 loop 迭代的 SSE 到同一 reply.raw
|
|
337
|
+
const loopProcessor = new SSEStreamProcessor(reply.raw);
|
|
338
|
+
const loopStream = Readable.fromWeb(loopResponse.body);
|
|
339
|
+
loopStream.on('data', (chunk) => loopProcessor.processChunk(chunk));
|
|
340
|
+
return new Promise((resolve, reject) => {
|
|
341
|
+
loopStream.on('end', () => {
|
|
342
|
+
loopProcessor.flushLeftover();
|
|
343
|
+
resolve({ processor: loopProcessor, statusCode: loopResponse.status });
|
|
344
|
+
});
|
|
345
|
+
loopStream.on('error', (err) => {
|
|
346
|
+
request.log.error({ err }, `[chat] loop stream error`);
|
|
347
|
+
reject(err);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
},
|
|
351
|
+
sessionId,
|
|
352
|
+
saveIterationDB: async (loopRequestId, loopProcessor, loopStatusCode, loopBody) => {
|
|
353
|
+
const loopData = {
|
|
354
|
+
requestId: loopRequestId,
|
|
355
|
+
sessionId,
|
|
356
|
+
timestamp: new Date().toISOString(),
|
|
357
|
+
apiKey,
|
|
358
|
+
proxyApiKey,
|
|
359
|
+
baseUrl: resolved.baseUrl,
|
|
360
|
+
model: forwardedModel,
|
|
361
|
+
machineId,
|
|
362
|
+
request: { method: 'POST', path: '/api/chat', headers: forwardHeaders, body: loopBody },
|
|
363
|
+
response: {
|
|
364
|
+
statusCode: loopStatusCode,
|
|
365
|
+
events: loopProcessor.events,
|
|
366
|
+
completedAt: new Date().toISOString()
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
await saveRequestDB(sessionId, loopRequestId, loopData);
|
|
370
|
+
const loopUsage = extractTokenUsage(loopProcessor.events);
|
|
371
|
+
pushNewRequest({
|
|
372
|
+
requestId: loopRequestId,
|
|
373
|
+
sessionId,
|
|
374
|
+
compositeId: `${sessionId}:${loopRequestId}`,
|
|
375
|
+
method: 'POST',
|
|
376
|
+
path: '/api/chat',
|
|
377
|
+
timestamp: loopData.timestamp,
|
|
378
|
+
statusCode: loopStatusCode,
|
|
379
|
+
responseTime: 0,
|
|
380
|
+
model: forwardedModel,
|
|
381
|
+
machineId,
|
|
382
|
+
baseUrl: resolved.baseUrl,
|
|
383
|
+
usage: loopUsage
|
|
384
|
+
}, 'completed');
|
|
385
|
+
},
|
|
386
|
+
raw: reply.raw
|
|
387
|
+
});
|
|
388
|
+
request.log.info(`[chat] SSE streaming inject loop done: ${cloudRequestId}`);
|
|
389
|
+
}
|
|
390
|
+
reply.raw.end();
|
|
391
|
+
});
|
|
392
|
+
nodeStream.on('error', (err) => {
|
|
393
|
+
request.log.error({ err }, `[chat] stream error: ${cloudRequestId}`);
|
|
394
|
+
reply.raw.end();
|
|
395
|
+
});
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
// No body — error
|
|
399
|
+
reply.code(response.status).send('');
|
|
400
|
+
});
|
|
401
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { isClaudeInstalled } from '../utils/version.js';
|
|
5
|
+
function getClaudeSettingsPath() {
|
|
6
|
+
return path.join(os.homedir(), '.claude', 'settings.json');
|
|
7
|
+
}
|
|
8
|
+
function buildServerAddress(config) {
|
|
9
|
+
const host = config.server.host === '0.0.0.0' ? 'localhost' : config.server.host;
|
|
10
|
+
return `http://${host}:${config.server.port}`;
|
|
11
|
+
}
|
|
12
|
+
export async function setupClaudeSettingsRoutes(fastify, config) {
|
|
13
|
+
fastify.post('/ui/api/claude-settings/sync', async (request, reply) => {
|
|
14
|
+
// 1. 检测 Claude Code 是否安装
|
|
15
|
+
if (!isClaudeInstalled()) {
|
|
16
|
+
return reply.send({
|
|
17
|
+
success: false,
|
|
18
|
+
message: '未检测到 Claude Code,请安装完以后重试'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
// 2. 检测 settings.json 是否存在
|
|
22
|
+
const settingsPath = getClaudeSettingsPath();
|
|
23
|
+
if (!fs.existsSync(settingsPath)) {
|
|
24
|
+
return reply.send({
|
|
25
|
+
success: false,
|
|
26
|
+
message: '未检测到配置文件,请手动修改'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// 3. 读取 settings.json
|
|
30
|
+
let settings;
|
|
31
|
+
try {
|
|
32
|
+
const raw = fs.readFileSync(settingsPath, 'utf-8');
|
|
33
|
+
settings = JSON.parse(raw);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return reply.code(500).send({
|
|
37
|
+
success: false,
|
|
38
|
+
message: '配置文件格式错误,请手动检查'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// 4. 构建当前 ccm 服务地址
|
|
42
|
+
const serverAddress = buildServerAddress(config);
|
|
43
|
+
const currentBaseUrl = settings?.env?.ANTHROPIC_BASE_URL;
|
|
44
|
+
// 5. 比较
|
|
45
|
+
if (currentBaseUrl === serverAddress) {
|
|
46
|
+
return reply.send({
|
|
47
|
+
success: true,
|
|
48
|
+
message: '已使用 CCM 的配置,无需改动',
|
|
49
|
+
alreadyMatched: true
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
// 6. 备份旧值 + 设置新值
|
|
53
|
+
if (!settings.env) {
|
|
54
|
+
settings.env = {};
|
|
55
|
+
}
|
|
56
|
+
if (currentBaseUrl) {
|
|
57
|
+
settings.env.ANTHROPIC_BASE_URL_CCM_BACKUP = currentBaseUrl;
|
|
58
|
+
}
|
|
59
|
+
settings.env.ANTHROPIC_BASE_URL = serverAddress;
|
|
60
|
+
// 7. 写回 settings.json
|
|
61
|
+
try {
|
|
62
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return reply.code(500).send({
|
|
66
|
+
success: false,
|
|
67
|
+
message: `写入配置文件失败: ${err.message}`
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return reply.send({
|
|
71
|
+
success: true,
|
|
72
|
+
message: '修改成功,请重启 Claude 终端生效',
|
|
73
|
+
alreadyMatched: false
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { WebSocket } from 'ws';
|
|
2
2
|
export declare function addDataClient(ws: WebSocket): void;
|
|
3
3
|
export declare function removeDataClient(ws: WebSocket): void;
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function getDataClientCount(): number;
|
|
5
|
+
export declare function pushNewRequest(summary: any, status?: 'pending' | 'completed'): void;
|
|
5
6
|
export declare function pushStatsNotify(): void;
|
|
7
|
+
export declare function broadcastToDataClients(msg: any): void;
|
package/dist/server/data-push.js
CHANGED
|
@@ -7,12 +7,15 @@ export function addDataClient(ws) {
|
|
|
7
7
|
export function removeDataClient(ws) {
|
|
8
8
|
dataClients.delete(ws);
|
|
9
9
|
}
|
|
10
|
+
export function getDataClientCount() {
|
|
11
|
+
return dataClients.size;
|
|
12
|
+
}
|
|
10
13
|
// Broadcast helpers
|
|
11
|
-
export function pushNewRequest(summary) {
|
|
14
|
+
export function pushNewRequest(summary, status = 'completed') {
|
|
12
15
|
broadcast({
|
|
13
16
|
type: 'request',
|
|
14
|
-
action: 'new',
|
|
15
|
-
data: summary
|
|
17
|
+
action: status === 'pending' ? 'new' : 'update',
|
|
18
|
+
data: { ...summary, status }
|
|
16
19
|
});
|
|
17
20
|
}
|
|
18
21
|
export function pushStatsNotify() {
|
|
@@ -30,3 +33,7 @@ function broadcast(msg) {
|
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
}
|
|
36
|
+
// Expose broadcast for global status timer (ui-route.ts)
|
|
37
|
+
export function broadcastToDataClients(msg) {
|
|
38
|
+
broadcast(msg);
|
|
39
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import multipart from '@fastify/multipart';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { registry } from '../parsers/index.js';
|
|
5
|
+
import { dataDir } from '../utils/paths.js';
|
|
6
|
+
const docsDir = path.join(dataDir, 'docs');
|
|
7
|
+
export async function setupDocsRoutes(fastify) {
|
|
8
|
+
await fastify.register(multipart, { limits: { fileSize: 10 * 1024 * 1024 } });
|
|
9
|
+
fastify.post('/ui/api/docs/upload', async (request, reply) => {
|
|
10
|
+
const data = await request.file();
|
|
11
|
+
if (!data) {
|
|
12
|
+
return reply.code(400).send({ success: false, error: 'No file uploaded' });
|
|
13
|
+
}
|
|
14
|
+
const filename = path.basename(data.filename);
|
|
15
|
+
const buffer = await data.toBuffer();
|
|
16
|
+
// 校验扩展名
|
|
17
|
+
const supported = registry.getSupportedExtensions();
|
|
18
|
+
const dotIndex = filename.lastIndexOf('.');
|
|
19
|
+
if (dotIndex === -1) {
|
|
20
|
+
return reply.code(400).send({
|
|
21
|
+
success: false,
|
|
22
|
+
error: `File has no extension. Supported types: ${supported.join(', ')}`
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const ext = filename.slice(dotIndex).toLowerCase();
|
|
26
|
+
if (!supported.includes(ext)) {
|
|
27
|
+
return reply.code(400).send({
|
|
28
|
+
success: false,
|
|
29
|
+
error: `Unsupported file type: "${ext}". Supported types: ${supported.join(', ')}`
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// 解析文件内容为 md
|
|
33
|
+
let mdContent;
|
|
34
|
+
try {
|
|
35
|
+
mdContent = await registry.parse(buffer, filename);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
request.log.error({ err, filename }, 'File parsing failed');
|
|
39
|
+
return reply.code(500).send({
|
|
40
|
+
success: false,
|
|
41
|
+
error: err.message || 'File parsing failed'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// 确保 docs 目录存在
|
|
45
|
+
if (!fs.existsSync(docsDir)) {
|
|
46
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
// 写入 md 文件
|
|
49
|
+
const mdFilename = filename.slice(0, dotIndex) + '.md';
|
|
50
|
+
const mdPath = path.join(docsDir, mdFilename);
|
|
51
|
+
try {
|
|
52
|
+
fs.writeFileSync(mdPath, mdContent, 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
request.log.error({ err, mdPath }, 'Failed to write md file');
|
|
56
|
+
return reply.code(500).send({
|
|
57
|
+
success: false,
|
|
58
|
+
error: 'Failed to save file'
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return { success: true, path: `docs/${mdFilename}` };
|
|
62
|
+
});
|
|
63
|
+
}
|
package/dist/server/index.js
CHANGED
|
@@ -5,6 +5,8 @@ import { configureDbPath } from '../utils/db.js';
|
|
|
5
5
|
import { getCurrentModuleUrl } from '../utils/esm-dirname.js';
|
|
6
6
|
import { setupProxy } from './proxy.js';
|
|
7
7
|
import { setupUiRoutes } from './ui-route.js';
|
|
8
|
+
import { setupChatRoute } from './chat-route.js';
|
|
9
|
+
import { setupDocsRoutes } from './docs-route.js';
|
|
8
10
|
import { requestIdMiddleware } from './middleware/request-id.js';
|
|
9
11
|
export async function createServer() {
|
|
10
12
|
const config = await loadConfig();
|
|
@@ -19,6 +21,8 @@ export async function createServer() {
|
|
|
19
21
|
fastify.register(requestIdMiddleware);
|
|
20
22
|
await setupUiRoutes(fastify); // UI routes registered BEFORE proxy to avoid being proxied
|
|
21
23
|
await setupProxy(fastify, config.proxy);
|
|
24
|
+
await setupChatRoute(fastify, config.proxy); // MCP-enhanced chat endpoint
|
|
25
|
+
await setupDocsRoutes(fastify);
|
|
22
26
|
return fastify;
|
|
23
27
|
}
|
|
24
28
|
export async function startServer() {
|