@cc-monitoring/proxy-core 0.3.1 → 0.4.1
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/dist/cli/__tests__/commands.test.js +20 -1
- package/dist/cli/commands/exec.js +31 -42
- package/dist/cli/commands/repair.d.ts +1 -0
- package/dist/cli/commands/repair.js +15 -0
- package/dist/cli/index.js +30 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/assets/{app.B9A39uFv.js → app.g7N9jkzn.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.IHxQqfJH.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DdhC696L.js → VPLocalSearchBox.BHwI475_.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CfB2tOFi.js → theme.FhAJSNDh.js} +2 -2
- package/dist/docs/assets/{proxy-cli_data-hooks.md.w9Oe1OD3.js → proxy-cli_data-hooks.md.Bz8CVbxO.js} +1 -1
- package/dist/docs/assets/{proxy-core_ui-routes.md.Dyqkeoiv.js → proxy-core_ui-routes.md.B21t1p7R.js} +1 -1
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/index.html +3 -3
- package/dist/docs/overview.html +3 -3
- package/dist/docs/packages-utils.html +3 -3
- package/dist/docs/proxy-admin/env-and-build.html +3 -3
- package/dist/docs/proxy-admin/index.html +3 -3
- package/dist/docs/proxy-admin/layout-and-menu.html +3 -3
- package/dist/docs/proxy-admin/pages.html +3 -3
- package/dist/docs/proxy-admin/routing-and-auth.html +3 -3
- package/dist/docs/proxy-admin/services-and-mock.html +3 -3
- package/dist/docs/proxy-cli/api-layer.html +3 -3
- package/dist/docs/proxy-cli/app-shell.html +3 -3
- package/dist/docs/proxy-cli/components-common.html +3 -3
- package/dist/docs/proxy-cli/components-config.html +3 -3
- package/dist/docs/proxy-cli/components-requests.html +3 -3
- package/dist/docs/proxy-cli/components-stats.html +3 -3
- package/dist/docs/proxy-cli/components-status.html +3 -3
- package/dist/docs/proxy-cli/data-hooks.html +5 -5
- package/dist/docs/proxy-cli/index.html +3 -3
- package/dist/docs/proxy-cli/utils-and-i18n.html +3 -3
- package/dist/docs/proxy-core/chat-api.html +3 -3
- package/dist/docs/proxy-core/cli.html +3 -3
- package/dist/docs/proxy-core/config.html +3 -3
- package/dist/docs/proxy-core/data-layer.html +3 -3
- package/dist/docs/proxy-core/entry.html +3 -3
- package/dist/docs/proxy-core/index.html +3 -3
- package/dist/docs/proxy-core/pm2.html +3 -3
- package/dist/docs/proxy-core/providers.html +3 -3
- package/dist/docs/proxy-core/proxy.html +3 -3
- package/dist/docs/proxy-core/stats.html +3 -3
- package/dist/docs/proxy-core/tool.html +3 -3
- package/dist/docs/proxy-core/ui-routes.html +5 -5
- package/dist/docs/proxy-core/utilities.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/request-flow.html +3 -3
- package/dist/logger/index.d.ts +0 -3
- package/dist/logger/index.js +0 -1
- package/dist/mcp-server/tools.js +8 -9
- package/dist/server/__tests__/data-push.test.js +44 -0
- package/dist/server/__tests__/providers-route.test.js +1 -0
- package/dist/server/__tests__/proxy.test.js +281 -2
- package/dist/server/__tests__/server.test.js +15 -10
- package/dist/server/__tests__/ui-route.test.js +43 -0
- package/dist/server/auth.d.ts +0 -1
- package/dist/server/auth.js +1 -1
- package/dist/server/chat-route.js +48 -162
- package/dist/server/data-push.d.ts +2 -1
- package/dist/server/data-push.js +22 -7
- package/dist/server/index.js +4 -3
- package/dist/server/proxy.js +193 -229
- package/dist/server/ui-route.js +84 -60
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/registry.d.ts +0 -4
- package/dist/tools/registry.js +0 -8
- package/dist/types/config.d.ts +0 -13
- package/dist/types/pm2.d.ts +1 -2
- package/dist/ui/assets/{1c-DAu68mP4.js → 1c-CaPSZBmB.js} +1 -1
- package/dist/ui/assets/{abnf-Cu8RHCHK.js → abnf-DBDOsmne.js} +1 -1
- package/dist/ui/assets/{abnfDiagram-VRR7QNED-BUBToPXF.js → abnfDiagram-VRR7QNED-D8zK__Mj.js} +1 -1
- package/dist/ui/assets/{accesslog-BG-34XOy.js → accesslog-BOLs2Kig.js} +1 -1
- package/dist/ui/assets/{actionscript-iw0HHMGM.js → actionscript-BZYKhS1B.js} +1 -1
- package/dist/ui/assets/{ada-qRqj6VGQ.js → ada-DyacnMpg.js} +1 -1
- package/dist/ui/assets/{angelscript-BeaXyx_P.js → angelscript-CAN6j0H3.js} +1 -1
- package/dist/ui/assets/{apache-CU8d-z9X.js → apache-BE4vouiQ.js} +1 -1
- package/dist/ui/assets/{applescript-d1Wxkqz1.js → applescript-CS88I1yz.js} +1 -1
- package/dist/ui/assets/{arc-BMsPIm3j.js → arc-CTMmdrSV.js} +1 -1
- package/dist/ui/assets/{arcade-CtbgrWTM.js → arcade-B0f0f5SD.js} +1 -1
- package/dist/ui/assets/{architectureDiagram-ZJ3FMSHR-CVjC5TrS.js → architectureDiagram-ZJ3FMSHR-Dtvpnopp.js} +1 -1
- package/dist/ui/assets/{arduino-Bc9L39Hr.js → arduino-Bhm-OSb2.js} +1 -1
- package/dist/ui/assets/{armasm-DuWmpjWR.js → armasm-CEy-mDHc.js} +1 -1
- package/dist/ui/assets/{asciidoc-CDIhFTnx.js → asciidoc-CrZqQBTj.js} +1 -1
- package/dist/ui/assets/{aspectj-SYj-57Es.js → aspectj-B4IlFyyJ.js} +1 -1
- package/dist/ui/assets/{autohotkey-hBi5dq37.js → autohotkey-Cihy-FjA.js} +1 -1
- package/dist/ui/assets/{autoit-BSawXzhg.js → autoit-Dis_JVJq.js} +1 -1
- package/dist/ui/assets/{avrasm-CwYom8Np.js → avrasm-CVhMycHj.js} +1 -1
- package/dist/ui/assets/{awk-BvBRfGo2.js → awk-BCViwLf5.js} +1 -1
- package/dist/ui/assets/{axapta-CqUavA8k.js → axapta-pWB4JVB_.js} +1 -1
- package/dist/ui/assets/{bash-BS_ZnmEu.js → bash-B6Giryub.js} +1 -1
- package/dist/ui/assets/{basic-xbP4IXrN.js → basic-DcrjcHB0.js} +1 -1
- package/dist/ui/assets/{blockDiagram-677ZJIJ3-CmpI3f5p.js → blockDiagram-677ZJIJ3-DfhtgI1r.js} +1 -1
- package/dist/ui/assets/{bnf-BECwu6uW.js → bnf-cpKU0967.js} +1 -1
- package/dist/ui/assets/{brainfuck-QQgu4j4N.js → brainfuck-CFFdk4cM.js} +1 -1
- package/dist/ui/assets/{coq-IDMIFfem.js → c-CW6KPMeI.js} +1 -1
- package/dist/ui/assets/{c-like-DYrFOtXy.js → c-like-CWn59vrg.js} +1 -1
- package/dist/ui/assets/{c4Diagram-LMCZKHZV-B6Hgngz9.js → c4Diagram-LMCZKHZV-D1Oh6k37.js} +1 -1
- package/dist/ui/assets/{cal-CNMrJ6v5.js → cal-8i26_L39.js} +1 -1
- package/dist/ui/assets/{capnproto-DYCKl6oY.js → capnproto-CviwnFBf.js} +1 -1
- package/dist/ui/assets/{ceylon-mb8tRPl0.js → ceylon-CQyc4rWs.js} +1 -1
- package/dist/ui/assets/channel-Da2MvpNG.js +1 -0
- package/dist/ui/assets/{chunk-2Q5K7J3B-LxvQ-G67.js → chunk-2Q5K7J3B-oswPBwIn.js} +1 -1
- package/dist/ui/assets/{chunk-32BRIVSS-DxzKK6Aw.js → chunk-32BRIVSS-B5pU_9b9.js} +1 -1
- package/dist/ui/assets/{chunk-5VM5RSS4-CZEoYJ2W.js → chunk-5VM5RSS4-gvtfMc4w.js} +1 -1
- package/dist/ui/assets/{chunk-EX3LRPZG-DS2PytrN.js → chunk-EX3LRPZG-BkBaDMy4.js} +1 -1
- package/dist/ui/assets/{chunk-JWPE2WC7-UX0Sr1Ie.js → chunk-JWPE2WC7-Ch-C75ar.js} +1 -1
- package/dist/ui/assets/{chunk-MOJQB5TN-YbJgT9t4.js → chunk-MOJQB5TN-BfYhj1jN.js} +1 -1
- package/dist/ui/assets/{chunk-RYQCIY6F-DS2VH_U9.js → chunk-RYQCIY6F-D8izfruP.js} +1 -1
- package/dist/ui/assets/{chunk-V7JOEXUC-CZr0pkI1.js → chunk-V7JOEXUC-CbzHnW90.js} +1 -1
- package/dist/ui/assets/{chunk-VR4S4FIN-50opPAD3.js → chunk-VR4S4FIN-Dr4M5qZA.js} +1 -1
- package/dist/ui/assets/{chunk-XXDRQBXY-DAAX9l7U.js → chunk-XXDRQBXY-QE-R8dRe.js} +1 -1
- package/dist/ui/assets/classDiagram-OUVF2IWQ-mHUiWjcC.js +1 -0
- package/dist/ui/assets/classDiagram-v2-EOCWNBFH-mHUiWjcC.js +1 -0
- package/dist/ui/assets/{clean-C4X7dSkh.js → clean-DZeAtQ7H.js} +1 -1
- package/dist/ui/assets/{clojure-DSg9gUa7.js → clojure-U4ebZ7NQ.js} +1 -1
- package/dist/ui/assets/{clojure-repl-CagRYx7h.js → clojure-repl-B4QSIzUI.js} +1 -1
- package/dist/ui/assets/{cmake-CFdBn53c.js → cmake-UHexZySb.js} +1 -1
- package/dist/ui/assets/{coffeescript-CJZtMOPx.js → coffeescript-Dg56H9Mr.js} +1 -1
- package/dist/ui/assets/{c-BRJEPYra.js → coq-C1g3mm0X.js} +1 -1
- package/dist/ui/assets/{cos-Bx2VJuzG.js → cos-BL4DdyGh.js} +1 -1
- package/dist/ui/assets/{cose-bilkent-JH36ORCC-zWRLsZpZ.js → cose-bilkent-JH36ORCC-BHQslHP3.js} +1 -1
- package/dist/ui/assets/{cpp-CsG6DlC4.js → cpp-keZI-XAd.js} +1 -1
- package/dist/ui/assets/{crmsh-DTy9MnmJ.js → crmsh-DdXYVeeA.js} +1 -1
- package/dist/ui/assets/{crystal-BwgrLrZ1.js → crystal-C4AN0m0I.js} +1 -1
- package/dist/ui/assets/{csharp-B2VeTPAF.js → csharp-COUyKsNR.js} +1 -1
- package/dist/ui/assets/{csp-CTpydNhH.js → csp-DBc-rgSJ.js} +1 -1
- package/dist/ui/assets/{css-B0mMUbpY.js → css-B8m6O_Pw.js} +1 -1
- package/dist/ui/assets/{cynefin-VYW2F7L2-BmjQEj6q.js → cynefin-VYW2F7L2-B3L-WcKn.js} +1 -1
- package/dist/ui/assets/{cynefinDiagram-TSTJHNR4-Dr0Zrke0.js → cynefinDiagram-TSTJHNR4-fzaM4qcZ.js} +1 -1
- package/dist/ui/assets/{d-DjuDZnbI.js → d-dgQN4Rko.js} +1 -1
- package/dist/ui/assets/{dagre-VKFMJZFB-xoTPT2Uu.js → dagre-VKFMJZFB-DJHj16mR.js} +1 -1
- package/dist/ui/assets/{dart-xR3GU9TJ.js → dart-D2Y7LOq-.js} +1 -1
- package/dist/ui/assets/{delphi-bJas5QnG.js → delphi-DaGcSk5A.js} +1 -1
- package/dist/ui/assets/{diagram-FQU43EPY-rL_IU7gU.js → diagram-FQU43EPY-iZEZ1f-R.js} +1 -1
- package/dist/ui/assets/{diagram-G47NLZAW-EcDiCVGj.js → diagram-G47NLZAW-rkQb3r-C.js} +1 -1
- package/dist/ui/assets/{diagram-NH7WQ7WH-CGGXOjev.js → diagram-NH7WQ7WH-B4Fy72tP.js} +1 -1
- package/dist/ui/assets/{diagram-OA4YK3LP-DnE7hDjD.js → diagram-OA4YK3LP-BndmuIBq.js} +1 -1
- package/dist/ui/assets/{diagram-WEI45ONY-CTJH5eD8.js → diagram-WEI45ONY-CwlAiqOe.js} +1 -1
- package/dist/ui/assets/{diff-doJFF3fv.js → diff-prcRV1Y3.js} +1 -1
- package/dist/ui/assets/{django-D3b-owa_.js → django-B1zIoGSx.js} +1 -1
- package/dist/ui/assets/{dns-C42KYV-Z.js → dns-ZxI_aTwc.js} +1 -1
- package/dist/ui/assets/{dockerfile-9KUINc2j.js → dockerfile-Dp_fT6E-.js} +1 -1
- package/dist/ui/assets/{dos-CxnlJ6qp.js → dos-Cuu5c8vv.js} +1 -1
- package/dist/ui/assets/{dsconfig-6CMMCwmY.js → dsconfig-DcGTOhbS.js} +1 -1
- package/dist/ui/assets/{dts-D78rdUIB.js → dts-DCF8VaRk.js} +1 -1
- package/dist/ui/assets/{dust-Dv1uTfJU.js → dust-DeCmZWMK.js} +1 -1
- package/dist/ui/assets/{ebnf-BGbG1cFK.js → ebnf-BHQQcuF_.js} +1 -1
- package/dist/ui/assets/{ebnfDiagram-CCIWWBDH-z2VgRSUV.js → ebnfDiagram-CCIWWBDH-DJ_RuhTm.js} +1 -1
- package/dist/ui/assets/{elixir-ZgAQVY1J.js → elixir-BHmweJ4d.js} +1 -1
- package/dist/ui/assets/{elm-CudiFuNu.js → elm-v76Hd2B3.js} +1 -1
- package/dist/ui/assets/{erDiagram-Q63AITRT-X1TODuRv.js → erDiagram-Q63AITRT-DVZkY5RZ.js} +1 -1
- package/dist/ui/assets/{erb-Bz_NZh6W.js → erb-xvWF5bfx.js} +1 -1
- package/dist/ui/assets/{erlang-DNuj1nb2.js → erlang-CKBMURym.js} +1 -1
- package/dist/ui/assets/{erlang-repl-SP1Gan9W.js → erlang-repl-BjcfXt3Z.js} +1 -1
- package/dist/ui/assets/{excel-0K77uECF.js → excel-Cdrtxhml.js} +1 -1
- package/dist/ui/assets/{fix-CalJq7JD.js → fix-CB-So5So.js} +1 -1
- package/dist/ui/assets/{flix-BlIs11_w.js → flix-VM9AOrq6.js} +1 -1
- package/dist/ui/assets/{flowDiagram-23GEKE2U-DA2dnmc0.js → flowDiagram-23GEKE2U-vyGW67Xm.js} +1 -1
- package/dist/ui/assets/{fortran-ZCobXY2R.js → fortran-CcwJVO8H.js} +1 -1
- package/dist/ui/assets/{fsharp-Be8rwUtc.js → fsharp-DiATpw1u.js} +1 -1
- package/dist/ui/assets/{gams-DUat8frQ.js → gams-Csg9YNPz.js} +1 -1
- package/dist/ui/assets/{ganttDiagram-NO4QXBWP-DdFwaIGZ.js → ganttDiagram-NO4QXBWP-C2TRbwLw.js} +1 -1
- package/dist/ui/assets/{gauss-BB4U88dy.js → gauss-Bc4TC8Wh.js} +1 -1
- package/dist/ui/assets/{gcode-DzeUZuZg.js → gcode-Rig_2OoA.js} +1 -1
- package/dist/ui/assets/{gherkin-B_8vW_BO.js → gherkin-ByBU06QB.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-IHSO6WYX-B-Dn8x6I.js → gitGraphDiagram-IHSO6WYX-CEiqfFIF.js} +1 -1
- package/dist/ui/assets/{glsl-DGHGjPC1.js → glsl-C8D6y2Tn.js} +1 -1
- package/dist/ui/assets/{gml-BYEku8Yh.js → gml-PHpGPHbT.js} +1 -1
- package/dist/ui/assets/{go-D8aSgQMc.js → go-BL_o_3ez.js} +1 -1
- package/dist/ui/assets/{golo-CDjCU_jh.js → golo-CWQ6dTUz.js} +1 -1
- package/dist/ui/assets/{gradle-BeUw7tl_.js → gradle-Y_I2KN2z.js} +1 -1
- package/dist/ui/assets/{groovy-5iCQOtR3.js → groovy-JnIv8p2l.js} +1 -1
- package/dist/ui/assets/{haml-BEdL8FJi.js → haml-B8W_h6mZ.js} +1 -1
- package/dist/ui/assets/{handlebars-BuYQNzoK.js → handlebars-DVm8mJuD.js} +1 -1
- package/dist/ui/assets/{haskell-I4FCBcW0.js → haskell-DlDi2Ty7.js} +1 -1
- package/dist/ui/assets/{haxe-yigyKfFr.js → haxe-CftFNVE-.js} +1 -1
- package/dist/ui/assets/{hsp-ZUk4j7d1.js → hsp-C67xnq9C.js} +1 -1
- package/dist/ui/assets/{htmlbars-QYP3lT3u.js → htmlbars-CmJCm67U.js} +1 -1
- package/dist/ui/assets/{http-BJpD1O71.js → http-b-FPEcHP.js} +1 -1
- package/dist/ui/assets/{hy-CIJ1bP6S.js → hy-BWD3ncpN.js} +1 -1
- package/dist/ui/assets/{index-COAgrx1J.js → index-C7s1zgJb.js} +242 -242
- package/dist/ui/assets/{index-BDXWttnT.js → index-D2UBwfSG.js} +3 -3
- package/dist/ui/assets/index-DlsVXaHT.css +10 -0
- package/dist/ui/assets/{infoDiagram-FWYZ7A6U-fTg9eLNt.js → infoDiagram-FWYZ7A6U-C0Px17IN.js} +1 -1
- package/dist/ui/assets/{inform7-DAsu-lC2.js → inform7-C-OYaMO5.js} +1 -1
- package/dist/ui/assets/{ini-Bhz-zHCg.js → ini-CVyYmykw.js} +1 -1
- package/dist/ui/assets/{irpf90-BunhbwBl.js → irpf90-BcpjWXIz.js} +1 -1
- package/dist/ui/assets/{isbl-q9L6hOhP.js → isbl-ZraBBeEr.js} +1 -1
- package/dist/ui/assets/{ishikawaDiagram-FXEZZL3T-B8G0sdgd.js → ishikawaDiagram-FXEZZL3T-BjmTMgpO.js} +1 -1
- package/dist/ui/assets/{java-Iks5Pkau.js → java-D_mE2DT6.js} +1 -1
- package/dist/ui/assets/{javascript-C4vltXPw.js → javascript-akXESsox.js} +1 -1
- package/dist/ui/assets/{jboss-cli-BQBvd2X2.js → jboss-cli-CFe3gTDt.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-5HDEW3XC-Crbzgrum.js → journeyDiagram-5HDEW3XC-BpgJUvs6.js} +1 -1
- package/dist/ui/assets/{json-DXK0_jUK.js → json-sjN7AzRC.js} +1 -1
- package/dist/ui/assets/{julia-CCYoBiYK.js → julia-BJJYbUMV.js} +1 -1
- package/dist/ui/assets/{julia-repl-y8v2Oorl.js → julia-repl-BwruD8Ix.js} +1 -1
- package/dist/ui/assets/{kanban-definition-HUTT4EX6-C8sCOsBX.js → kanban-definition-HUTT4EX6-DFlE9edA.js} +1 -1
- package/dist/ui/assets/{kotlin-Bnjk7_fw.js → kotlin-CAIQsLCF.js} +1 -1
- package/dist/ui/assets/{lasso-B-al6x63.js → lasso-sU67h-5u.js} +1 -1
- package/dist/ui/assets/{latex-BUmqlb9r.js → latex-CVX-jP88.js} +1 -1
- package/dist/ui/assets/{ldif-D42RWj6Q.js → ldif-DGM9xbFZ.js} +1 -1
- package/dist/ui/assets/{leaf-CrH4BZ0m.js → leaf-AlB_zmwz.js} +1 -1
- package/dist/ui/assets/{less-upmRlQQW.js → less-CKwfEdZQ.js} +1 -1
- package/dist/ui/assets/{lisp-DHYS22SA.js → lisp-CXYLaAPw.js} +1 -1
- package/dist/ui/assets/{livecodeserver-CeQNmFP3.js → livecodeserver-C02fGGTS.js} +1 -1
- package/dist/ui/assets/{livescript-PJWpY1JI.js → livescript-CBMa8QF_.js} +1 -1
- package/dist/ui/assets/{llvm-BKtKWwCt.js → llvm-WXTIohTC.js} +1 -1
- package/dist/ui/assets/{lsl-RWRLRjk7.js → lsl-4obd2U_I.js} +1 -1
- package/dist/ui/assets/{lua-BFUDiPGm.js → lua-DTSy-TjF.js} +1 -1
- package/dist/ui/assets/{makefile-CUDyOz5D.js → makefile-Bf2UEO0i.js} +1 -1
- package/dist/ui/assets/{markdown-zZCLypc6.js → markdown-BgoGmFfJ.js} +1 -1
- package/dist/ui/assets/{mathematica-BHbxKxnq.js → mathematica-CSiA8KQh.js} +1 -1
- package/dist/ui/assets/{matlab-DlGtg--p.js → matlab-DoMvLsGB.js} +1 -1
- package/dist/ui/assets/{maxima-BBBeS3Ra.js → maxima-BFO4dfZO.js} +1 -1
- package/dist/ui/assets/{mel-nh8j7W_6.js → mel-DyBpmDxt.js} +1 -1
- package/dist/ui/assets/{mercury-DyyD-pqr.js → mercury-OL5G5uU6.js} +1 -1
- package/dist/ui/assets/{mindmap-definition-LN4V7U3C-CJlhDHqM.js → mindmap-definition-LN4V7U3C-s-nUZuCs.js} +1 -1
- package/dist/ui/assets/{mipsasm-hiEhRRiq.js → mipsasm-KQwdm39j.js} +1 -1
- package/dist/ui/assets/{mizar-BZa3iAAL.js → mizar-BqndJ30I.js} +1 -1
- package/dist/ui/assets/{mojolicious-BCIm2V_y.js → mojolicious-Bvc1uJd-.js} +1 -1
- package/dist/ui/assets/{monkey-zmK-7N8b.js → monkey-Dl_gLm7-.js} +1 -1
- package/dist/ui/assets/{moonscript-BsS_Mn3D.js → moonscript-BL6R6eAz.js} +1 -1
- package/dist/ui/assets/{n1ql-D7xVgDP0.js → n1ql-C09iOJfQ.js} +1 -1
- package/dist/ui/assets/{nginx-ct2q4dtd.js → nginx-C7W01nBr.js} +1 -1
- package/dist/ui/assets/{nim-RO0UOmW3.js → nim-Dj78szvD.js} +1 -1
- package/dist/ui/assets/{nix-BcRJxPKH.js → nix-BOtGyPmd.js} +1 -1
- package/dist/ui/assets/{node-repl-DgIefwxW.js → node-repl-19nZvZ4A.js} +1 -1
- package/dist/ui/assets/{nsis-DqHs9QhB.js → nsis-ByCl1zIm.js} +1 -1
- package/dist/ui/assets/{objectivec-BBcWqc7Y.js → objectivec-BhdaT_Jr.js} +1 -1
- package/dist/ui/assets/{ocaml-BExfw7D6.js → ocaml-D_NXvSO7.js} +1 -1
- package/dist/ui/assets/{openscad-CHPRELQb.js → openscad-DOhagvnA.js} +1 -1
- package/dist/ui/assets/{oxygene-DpS9uMJW.js → oxygene-5BnkqMRH.js} +1 -1
- package/dist/ui/assets/{parser3-CjJ_QdBp.js → parser3-jSWlxi8m.js} +1 -1
- package/dist/ui/assets/{pegDiagram-2B236MQR-GM70woGy.js → pegDiagram-2B236MQR-CideH-XT.js} +1 -1
- package/dist/ui/assets/{perl-BSNM2Kt8.js → perl-CfsqjZ4b.js} +1 -1
- package/dist/ui/assets/{pf-Z-hKf_88.js → pf-kOewxO29.js} +1 -1
- package/dist/ui/assets/{pgsql-DDgznxBg.js → pgsql-CXm2Xd-Z.js} +1 -1
- package/dist/ui/assets/{php-BzAhxF-l.js → php-CRiA_dH8.js} +1 -1
- package/dist/ui/assets/{php-template-DkgcKHRc.js → php-template-ByMrxqSd.js} +1 -1
- package/dist/ui/assets/{pieDiagram-ENE6RG2P-Bst7mPyz.js → pieDiagram-ENE6RG2P-CJkCdB38.js} +1 -1
- package/dist/ui/assets/{plaintext-BYEppD0R.js → plaintext-BJx0R6A5.js} +1 -1
- package/dist/ui/assets/{pony-BT_kQd1K.js → pony-CozCHtz4.js} +1 -1
- package/dist/ui/assets/{powershell-BfRsuSsf.js → powershell-BT7gMMgn.js} +1 -1
- package/dist/ui/assets/{processing-DpzWe5Iu.js → processing-FOIxn_W_.js} +1 -1
- package/dist/ui/assets/{profile-BCOhS5cM.js → profile-BsoUuD9w.js} +1 -1
- package/dist/ui/assets/{prolog-OxaCCaW8.js → prolog-Bd3gvQ2N.js} +1 -1
- package/dist/ui/assets/{properties-DMFlA2Ca.js → properties-BWGFdny9.js} +1 -1
- package/dist/ui/assets/{protobuf-CRDFkXvZ.js → protobuf-pQSKoOOY.js} +1 -1
- package/dist/ui/assets/{puppet-DnrVyLzx.js → puppet-DnEN_mgS.js} +1 -1
- package/dist/ui/assets/{purebasic-Dht4Hniv.js → purebasic-BqoOGoRw.js} +1 -1
- package/dist/ui/assets/{python-B0aMirWu.js → python-TeVYomkE.js} +1 -1
- package/dist/ui/assets/{python-repl-BlYDBGa7.js → python-repl-zqu5igFS.js} +1 -1
- package/dist/ui/assets/{q-DzrtW9kT.js → q-CZC9Dltd.js} +1 -1
- package/dist/ui/assets/{qml-DnfQsR3i.js → qml-ZNInP45Q.js} +1 -1
- package/dist/ui/assets/{quadrantDiagram-ABIIQ3AL-DNRYurBT.js → quadrantDiagram-ABIIQ3AL-Dd7PwxsF.js} +1 -1
- package/dist/ui/assets/{r-DGI_ZuTD.js → r-B39DAmkC.js} +1 -1
- package/dist/ui/assets/{railroadDiagram-RFXS5EU6-CmbC3Vz4.js → railroadDiagram-RFXS5EU6-DtO5PVFZ.js} +1 -1
- package/dist/ui/assets/{reasonml-3SohMgh2.js → reasonml-B-ZWFJt_.js} +1 -1
- package/dist/ui/assets/{requirementDiagram-TGXJPOKE-8CxrXHco.js → requirementDiagram-TGXJPOKE-DihF4OTj.js} +1 -1
- package/dist/ui/assets/{rib-DfRSd4Qa.js → rib-CnGG0qMF.js} +1 -1
- package/dist/ui/assets/{roboconf-CQczGdGW.js → roboconf-Ewtfps4u.js} +1 -1
- package/dist/ui/assets/{routeros-DCka-T2i.js → routeros-CPcmP1c7.js} +1 -1
- package/dist/ui/assets/{rsl-Dr8MOwMo.js → rsl-NbjWFbPH.js} +1 -1
- package/dist/ui/assets/{ruby-C2zFSkh_.js → ruby-DWDdy5yE.js} +1 -1
- package/dist/ui/assets/{ruleslanguage-C97YsgQq.js → ruleslanguage-Cv_DZe2r.js} +1 -1
- package/dist/ui/assets/{rust-H3h8kd9A.js → rust-NZnsfER5.js} +1 -1
- package/dist/ui/assets/{sankeyDiagram-HTMAVEWB-DBXLWjEq.js → sankeyDiagram-HTMAVEWB-D6i-Mzte.js} +1 -1
- package/dist/ui/assets/{sas-04z0Xu8P.js → sas-C4wv0P6G.js} +1 -1
- package/dist/ui/assets/{scala-DyB9Aoiv.js → scala-DUdTOIgx.js} +1 -1
- package/dist/ui/assets/{scheme-DBj6nxUi.js → scheme-DEdFa14t.js} +1 -1
- package/dist/ui/assets/{scilab-Btj7AD9s.js → scilab-xY51J2bR.js} +1 -1
- package/dist/ui/assets/{scss-BXZJyXt7.js → scss-BEYp1sXc.js} +1 -1
- package/dist/ui/assets/{sequenceDiagram-DBY2YBRQ-DXIkBFKC.js → sequenceDiagram-DBY2YBRQ-njQlqy_J.js} +1 -1
- package/dist/ui/assets/{shell-BsreQR1Z.js → shell-fq6bkhOw.js} +1 -1
- package/dist/ui/assets/{sizeCapture-X5ZJPWSS-ocS9jxAQ.js → sizeCapture-X5ZJPWSS-tLn70-Fb.js} +1 -1
- package/dist/ui/assets/{smali-D9wQw9zm.js → smali-CsSAuSYg.js} +1 -1
- package/dist/ui/assets/{smalltalk-Dku6G_H3.js → smalltalk-DJ7zrd87.js} +1 -1
- package/dist/ui/assets/{sml-BknyU6ya.js → sml-CnDwl0nH.js} +1 -1
- package/dist/ui/assets/{sqf-DRYMaOg8.js → sqf-Cc2SX4Zy.js} +1 -1
- package/dist/ui/assets/{sql-Cw3Ax4KA.js → sql-DUQA_V_4.js} +1 -1
- package/dist/ui/assets/{sql_more-vWdslLE0.js → sql_more-BHoH-8oO.js} +1 -1
- package/dist/ui/assets/{stan-8XH5bDS6.js → stan-D-7F5jH6.js} +1 -1
- package/dist/ui/assets/{stata-4e77wgfe.js → stata-xKD_DnDE.js} +1 -1
- package/dist/ui/assets/{stateDiagram-2N3HPSRC-RMmL9jMG.js → stateDiagram-2N3HPSRC-B4O8weor.js} +1 -1
- package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-CO881_ht.js +1 -0
- package/dist/ui/assets/{step21-ByIeDMY3.js → step21-obc_kzQY.js} +1 -1
- package/dist/ui/assets/{stylus-ZAhJyWsw.js → stylus-CkAUSRd-.js} +1 -1
- package/dist/ui/assets/{subunit-pPW2UGkj.js → subunit-BDv8PGo1.js} +1 -1
- package/dist/ui/assets/{swift-CX3BB5az.js → swift-CHsYyW-p.js} +1 -1
- package/dist/ui/assets/{swimlanes-5IMT3BWC-C2zAuJ56.js → swimlanes-5IMT3BWC-DSr9Ty4A.js} +2 -2
- package/dist/ui/assets/swimlanesDiagram-G3AALYLV-DFMCDs8b.js +8 -0
- package/dist/ui/assets/{taggerscript-BHcjbWIC.js → taggerscript-C_2JQgO9.js} +1 -1
- package/dist/ui/assets/{tap-BOl1lBlS.js → tap-Bi497Z2c.js} +1 -1
- package/dist/ui/assets/{tcl-DZjeOBDh.js → tcl-B1XMvLaD.js} +1 -1
- package/dist/ui/assets/{thrift-BxboWg4x.js → thrift-D9X0Hmmc.js} +1 -1
- package/dist/ui/assets/{timeline-definition-FHXFAJF6-BOxsxyq_.js → timeline-definition-FHXFAJF6-C4_gj50B.js} +1 -1
- package/dist/ui/assets/{tp-BNM6ql3J.js → tp-CJG4Odbx.js} +1 -1
- package/dist/ui/assets/{twig-Dj9ZiXD4.js → twig-flj72_wc.js} +1 -1
- package/dist/ui/assets/{typescript-7MEJlu_6.js → typescript-CWPwVoLd.js} +1 -1
- package/dist/ui/assets/{vala-DNqOAgHJ.js → vala-LuCNWNuU.js} +1 -1
- package/dist/ui/assets/{vbnet-T0mNKVc_.js → vbnet-CGUT6ss0.js} +1 -1
- package/dist/ui/assets/{vbscript-CeuwVMAI.js → vbscript-CNiUBJhn.js} +1 -1
- package/dist/ui/assets/{vbscript-html-BbJSt2w6.js → vbscript-html-CJGgjjbc.js} +1 -1
- package/dist/ui/assets/{vennDiagram-L72KCM5P-BX9mWeiT.js → vennDiagram-L72KCM5P-BxcAUnYo.js} +1 -1
- package/dist/ui/assets/{verilog-DYuhYfkX.js → verilog-C4f9ZBTK.js} +1 -1
- package/dist/ui/assets/{vhdl-Cf9aT-t7.js → vhdl-CyDQIZve.js} +1 -1
- package/dist/ui/assets/{vim-DciP44K1.js → vim-XAUBqa0M.js} +1 -1
- package/dist/ui/assets/{wardleyDiagram-EHGQE667-CDj2rOzu.js → wardleyDiagram-EHGQE667-DlwSH77q.js} +1 -1
- package/dist/ui/assets/{x86asm-E-AN_LXO.js → x86asm-Cp6vHuUx.js} +1 -1
- package/dist/ui/assets/{xl-CxYLJelN.js → xl-BHgDofIM.js} +1 -1
- package/dist/ui/assets/{xml-yz1SRLuU.js → xml-QloVn4s_.js} +1 -1
- package/dist/ui/assets/{xquery-BLzBF3tV.js → xquery-spJZLmyM.js} +1 -1
- package/dist/ui/assets/{xychartDiagram-FW5EYKEG-Czapk9-Z.js → xychartDiagram-FW5EYKEG-BxGVZcnb.js} +1 -1
- package/dist/ui/assets/{yaml-Cv4Y7iN_.js → yaml-CUju4iq0.js} +1 -1
- package/dist/ui/assets/{zephir-KRcdQfEX.js → zephir-ChVpmD0Y.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/dist/utils/__tests__/abort-signals.test.js +67 -0
- package/dist/utils/__tests__/db-maintenance.test.js +55 -0
- package/dist/utils/__tests__/db-migration.test.js +79 -1
- package/dist/utils/__tests__/inject-loop.test.js +1 -53
- package/dist/utils/__tests__/learn-tasks-db.test.js +82 -0
- package/dist/utils/__tests__/openai-converter.test.js +25 -0
- package/dist/utils/__tests__/pi-crawl-tools.test.js +15 -4
- package/dist/utils/__tests__/pi-dedup-reconcile.test.js +11 -9
- package/dist/utils/__tests__/pi-events.test.d.ts +1 -0
- package/dist/utils/__tests__/pi-events.test.js +172 -0
- package/dist/utils/__tests__/pi-learn-orchestrator.test.js +216 -6
- package/dist/utils/__tests__/pi-runtime-json.test.js +38 -3
- package/dist/utils/__tests__/providers-db.test.js +40 -2
- package/dist/utils/__tests__/request-writer.test.js +532 -39
- package/dist/utils/__tests__/sse-protocol.test.d.ts +1 -0
- package/dist/utils/__tests__/sse-protocol.test.js +115 -0
- package/dist/utils/__tests__/stats-aggregator.test.d.ts +1 -0
- package/dist/utils/__tests__/stats-aggregator.test.js +114 -0
- package/dist/utils/__tests__/vault.test.d.ts +1 -0
- package/dist/utils/__tests__/{knowledge-store.test.js → vault.test.js} +16 -16
- package/dist/utils/abort-signals.d.ts +13 -0
- package/dist/utils/abort-signals.js +33 -0
- package/dist/utils/db-maintenance.d.ts +8 -0
- package/dist/utils/db-maintenance.js +18 -0
- package/dist/utils/db.d.ts +2 -0
- package/dist/utils/db.js +86 -0
- package/dist/utils/esm-dirname.d.ts +0 -15
- package/dist/utils/esm-dirname.js +0 -4
- package/dist/utils/inject-loop.d.ts +0 -27
- package/dist/utils/inject-loop.js +2 -56
- package/dist/utils/learn-tasks-db.d.ts +15 -0
- package/dist/utils/learn-tasks-db.js +35 -0
- package/dist/utils/openai-converter.js +88 -78
- package/dist/utils/pi-browser-tools.d.ts +0 -2
- package/dist/utils/pi-browser-tools.js +0 -174
- package/dist/utils/pi-crawl-tools.d.ts +2 -1
- package/dist/utils/pi-crawl-tools.js +16 -11
- package/dist/utils/pi-dedup.d.ts +8 -30
- package/dist/utils/pi-dedup.js +32 -67
- package/dist/utils/pi-events.d.ts +65 -0
- package/dist/utils/pi-events.js +98 -0
- package/dist/utils/pi-learn-orchestrator.d.ts +6 -5
- package/dist/utils/pi-learn-orchestrator.js +71 -106
- package/dist/utils/pi-runtime.d.ts +5 -6
- package/dist/utils/pi-runtime.js +38 -42
- package/dist/utils/pi-sandbox-ops.d.ts +0 -36
- package/dist/utils/pi-sandbox-ops.js +5 -190
- package/dist/utils/providers-db.d.ts +3 -0
- package/dist/utils/providers-db.js +21 -7
- package/dist/utils/request-writer.d.ts +77 -26
- package/dist/utils/request-writer.js +362 -56
- package/dist/utils/sse-protocol.d.ts +34 -0
- package/dist/utils/sse-protocol.js +121 -0
- package/dist/utils/sse-stream-processor.d.ts +4 -9
- package/dist/utils/sse-stream-processor.js +13 -104
- package/dist/utils/sse-writer.d.ts +1 -1
- package/dist/utils/sse-writer.js +3 -2
- package/dist/utils/stats-aggregator.d.ts +1 -1
- package/dist/utils/stats-aggregator.js +44 -24
- package/dist/utils/tool-injector.js +7 -0
- package/dist/utils/vault.d.ts +111 -0
- package/dist/utils/vault.js +434 -0
- package/ecosystem.config.cjs +2 -1
- package/package.json +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B6Qfxe6W.js +0 -1
- package/dist/server/middleware/__tests__/request-id.test.js +0 -28
- package/dist/server/middleware/request-id.d.ts +0 -2
- package/dist/server/middleware/request-id.js +0 -12
- package/dist/types/api.d.ts +0 -20
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.js +0 -4
- package/dist/types/log.d.ts +0 -13
- package/dist/ui/assets/channel-Bc4Td8eZ.js +0 -1
- package/dist/ui/assets/classDiagram-OUVF2IWQ-BicxhZFq.js +0 -1
- package/dist/ui/assets/classDiagram-v2-EOCWNBFH-BicxhZFq.js +0 -1
- package/dist/ui/assets/index-C0fxlU9d.css +0 -10
- package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-QvaEO5U6.js +0 -1
- package/dist/ui/assets/swimlanesDiagram-G3AALYLV-DPgCOY0D.js +0 -8
- package/dist/utils/__tests__/sse-accumulator.test.js +0 -83
- package/dist/utils/knowledge-store.d.ts +0 -51
- package/dist/utils/knowledge-store.js +0 -241
- package/dist/utils/migrate-files-to-db.d.ts +0 -1
- package/dist/utils/migrate-files-to-db.js +0 -138
- package/dist/utils/sse-accumulator.d.ts +0 -9
- package/dist/utils/sse-accumulator.js +0 -59
- /package/dist/docs/assets/{proxy-cli_data-hooks.md.w9Oe1OD3.lean.js → proxy-cli_data-hooks.md.Bz8CVbxO.lean.js} +0 -0
- /package/dist/docs/assets/{proxy-core_ui-routes.md.Dyqkeoiv.lean.js → proxy-core_ui-routes.md.B21t1p7R.lean.js} +0 -0
- /package/dist/server/{middleware/__tests__/request-id.test.d.ts → __tests__/data-push.test.d.ts} +0 -0
- /package/dist/{types/api.js → utils/__tests__/abort-signals.test.d.ts} +0 -0
- /package/dist/{types/log.js → utils/__tests__/db-maintenance.test.d.ts} +0 -0
- /package/dist/utils/__tests__/{knowledge-store.test.d.ts → learn-tasks-db.test.d.ts} +0 -0
- /package/dist/utils/__tests__/{sse-accumulator.test.d.ts → openai-converter.test.d.ts} +0 -0
package/dist/server/proxy.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Readable, finished } from 'stream';
|
|
2
2
|
import { convertClaudeToOpenAIRequest, convertOpenAIToClaudeResponse, createStreamTransformer, buildOpenAIHeaders } from '../utils/openai-converter.js';
|
|
3
3
|
import { loadConfig } from '../config/index.js';
|
|
4
|
-
import {
|
|
5
|
-
import { cleanOldRequestsDB } from '../utils/request-writer.js';
|
|
6
|
-
import { pushNewRequest } from './data-push.js';
|
|
4
|
+
import { createRequestRecorder, isStreamCompleteEvents } from '../utils/request-writer.js';
|
|
5
|
+
import { cleanOldRequestsDB, interruptStaleRequestsOnStartup } from '../utils/request-writer.js';
|
|
6
|
+
import { pushNewRequest, broadcastToDataClients, clearStatsNotify, scheduleStatsNotify } from './data-push.js';
|
|
7
7
|
import { initDatabase, closeDatabase, getDatabase } from '../utils/db.js';
|
|
8
|
+
import { recoverCrashedTasks } from '../utils/learn-tasks-db.js';
|
|
9
|
+
import { getActiveProvider } from '../utils/providers-db.js';
|
|
8
10
|
import { aggregateStats } from '../utils/stats-aggregator.js';
|
|
9
11
|
import { mcpManager } from '../utils/mcp-manager.js';
|
|
10
12
|
import { getMachineIdSync, preloadMachineId } from '../utils/machine-id.js';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
let cachedDbInstance = null;
|
|
13
|
+
import { createEventDecoder } from '../utils/sse-protocol.js';
|
|
14
|
+
import { createClientAbortSignal, createTimeoutSignal } from '../utils/abort-signals.js';
|
|
14
15
|
/**
|
|
15
16
|
* Resolves the proxy target per request:
|
|
16
17
|
* - If a provider with is_default=1 exists in DB, use its base_url + api_key + tier models (partial replacement)
|
|
@@ -36,15 +37,7 @@ export function resolveProxyTarget(proxyConfig) {
|
|
|
36
37
|
baseUrl = configUrl.origin;
|
|
37
38
|
targetPath = configUrl.pathname.replace(/\/$/, '');
|
|
38
39
|
const db = getDatabase();
|
|
39
|
-
|
|
40
|
-
if (db !== cachedDbInstance) {
|
|
41
|
-
activeProviderStmt = null;
|
|
42
|
-
cachedDbInstance = db;
|
|
43
|
-
}
|
|
44
|
-
if (!activeProviderStmt) {
|
|
45
|
-
activeProviderStmt = db.prepare('SELECT base_url, api_key, model, haiku_model, sonnet_model, opus_model, is_openai_compatible, auth_style FROM providers WHERE is_default = 1 LIMIT 1');
|
|
46
|
-
}
|
|
47
|
-
const active = activeProviderStmt.get();
|
|
40
|
+
const active = getActiveProvider(db);
|
|
48
41
|
if (active) {
|
|
49
42
|
// Replace base_url only if provided
|
|
50
43
|
if (active.base_url && active.base_url.trim() !== '') {
|
|
@@ -163,58 +156,6 @@ export function getErrorStatusCode(error) {
|
|
|
163
156
|
}
|
|
164
157
|
return 500;
|
|
165
158
|
}
|
|
166
|
-
/**
|
|
167
|
-
* Push a parsed SSE event into batch/tail buffers.
|
|
168
|
-
* Flushes batch to DB when it reaches EVENT_BATCH_SIZE (10).
|
|
169
|
-
* Only memory held: current batch (≤10 events) + tail (≤5 events).
|
|
170
|
-
*/
|
|
171
|
-
function pushEventToBuffers(event, sessionId, requestId, eventBatch, tailBuffer, eventIndexRef) {
|
|
172
|
-
const entry = { index: eventIndexRef.value, type: event?.type, data: event };
|
|
173
|
-
eventBatch.push(entry);
|
|
174
|
-
// Maintain tail buffer (last 5 events for usage extraction)
|
|
175
|
-
tailBuffer.push(event);
|
|
176
|
-
if (tailBuffer.length > 5) {
|
|
177
|
-
tailBuffer.shift();
|
|
178
|
-
}
|
|
179
|
-
// Flush batch to DB when threshold reached
|
|
180
|
-
if (eventBatch.length >= EVENT_BATCH_SIZE) {
|
|
181
|
-
flushEventBatch(sessionId, requestId, eventBatch, eventIndexRef.value - eventBatch.length + 1);
|
|
182
|
-
eventBatch.length = 0; // Reset batch (memory freed)
|
|
183
|
-
}
|
|
184
|
-
eventIndexRef.value++;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Flush remaining batch + update request completion in DB.
|
|
188
|
-
* Extracts token usage from tail buffer (no need for full events array).
|
|
189
|
-
*/
|
|
190
|
-
async function completeStreamSave(sessionId, requestId, requestData, eventBatch, tailBuffer, eventIndexRef, startTime, statusCode, resolved, forwardedModel, originalModel, machineId, request) {
|
|
191
|
-
// Flush remaining events in batch
|
|
192
|
-
if (eventBatch.length > 0) {
|
|
193
|
-
flushEventBatch(sessionId, requestId, eventBatch, eventIndexRef.value - eventBatch.length);
|
|
194
|
-
eventBatch.length = 0;
|
|
195
|
-
}
|
|
196
|
-
requestData.response.completedAt = new Date().toISOString();
|
|
197
|
-
requestData.response.events = []; // Not needed for streaming, clear to avoid serialization
|
|
198
|
-
await saveRequestDB(sessionId, requestId, requestData);
|
|
199
|
-
const usage = extractTokenUsage(tailBuffer);
|
|
200
|
-
updateRequestTokens(requestId, sessionId, usage);
|
|
201
|
-
pushNewRequest({
|
|
202
|
-
requestId,
|
|
203
|
-
sessionId,
|
|
204
|
-
compositeId: `${sessionId}:${requestId}`,
|
|
205
|
-
method: request.method,
|
|
206
|
-
path: request.url,
|
|
207
|
-
timestamp: requestData.timestamp,
|
|
208
|
-
statusCode,
|
|
209
|
-
responseTime: Date.now() - startTime,
|
|
210
|
-
model: forwardedModel || originalModel,
|
|
211
|
-
machineId,
|
|
212
|
-
userAgent: request.headers['user-agent'],
|
|
213
|
-
baseUrl: resolved.baseUrl,
|
|
214
|
-
usage,
|
|
215
|
-
pendingId: requestId !== request.id ? `${sessionId}:${request.id}` : undefined
|
|
216
|
-
}, 'completed');
|
|
217
|
-
}
|
|
218
159
|
export function getErrorMessage(error) {
|
|
219
160
|
if (error.code === 'ECONNREFUSED') {
|
|
220
161
|
return 'Target server unreachable';
|
|
@@ -227,12 +168,72 @@ export function getErrorMessage(error) {
|
|
|
227
168
|
}
|
|
228
169
|
return 'Proxy error';
|
|
229
170
|
}
|
|
171
|
+
// SSE 流空闲看门狗:超过该时长无上游数据则销毁流,避免挂死产生僵尸行
|
|
172
|
+
const STREAM_IDLE_TIMEOUT_MS = 120000;
|
|
173
|
+
// 测试 seam:CCM_STREAM_IDLE_TIMEOUT_MS 覆盖超时(仅在 attach 时读一次)
|
|
174
|
+
function getIdleTimeoutMs() {
|
|
175
|
+
const envValue = Number(process.env.CCM_STREAM_IDLE_TIMEOUT_MS);
|
|
176
|
+
return Number.isFinite(envValue) && envValue > 0 ? envValue : STREAM_IDLE_TIMEOUT_MS;
|
|
177
|
+
}
|
|
178
|
+
function attachIdleWatchdog(nodeStream) {
|
|
179
|
+
let timer = null;
|
|
180
|
+
const clearTimer = () => {
|
|
181
|
+
if (timer) {
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
timer = null;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const resetTimer = () => {
|
|
187
|
+
clearTimer();
|
|
188
|
+
timer = setTimeout(() => {
|
|
189
|
+
if (!nodeStream.destroyed) {
|
|
190
|
+
nodeStream.destroy(new Error('upstream idle timeout'));
|
|
191
|
+
}
|
|
192
|
+
}, getIdleTimeoutMs());
|
|
193
|
+
};
|
|
194
|
+
nodeStream.on('data', resetTimer);
|
|
195
|
+
nodeStream.on('end', clearTimer);
|
|
196
|
+
nodeStream.on('close', clearTimer);
|
|
197
|
+
nodeStream.on('error', clearTimer);
|
|
198
|
+
resetTimer();
|
|
199
|
+
return {
|
|
200
|
+
finish() {
|
|
201
|
+
clearTimer();
|
|
202
|
+
if (!nodeStream.destroyed) {
|
|
203
|
+
nodeStream.destroy();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
230
208
|
export async function setupProxy(fastify, proxyConfig) {
|
|
231
209
|
// Load full config to get storage.maxDays
|
|
232
210
|
const fullConfig = await loadConfig();
|
|
233
211
|
const maxDays = fullConfig.storage?.maxDays ?? 7;
|
|
234
212
|
// Initialize SQLite database
|
|
235
|
-
initDatabase();
|
|
213
|
+
const db = initDatabase();
|
|
214
|
+
// 回收上次进程遗留的无主 running learn 任务(进程崩溃/被 kill)
|
|
215
|
+
const recoveredTasks = recoverCrashedTasks(db);
|
|
216
|
+
if (recoveredTasks > 0) {
|
|
217
|
+
console.log(`Recovered ${recoveredTasks} crashed learn task(s) from previous run`);
|
|
218
|
+
}
|
|
219
|
+
// 回收上次进程遗留的僵尸 SSE 请求行(流中断/挂死未回调):逐行分流
|
|
220
|
+
const { completed: completedAtStartup, interrupted: interruptedAtStartup } = interruptStaleRequestsOnStartup();
|
|
221
|
+
if (completedAtStartup > 0 || interruptedAtStartup > 0) {
|
|
222
|
+
console.log(`Recovered ${completedAtStartup} completed / ${interruptedAtStartup} interrupted stale request(s)`);
|
|
223
|
+
}
|
|
224
|
+
// 收割周期化:每 60s 扫 >2min 仍 pending 的行
|
|
225
|
+
const staleReaperTimer = setInterval(() => {
|
|
226
|
+
try {
|
|
227
|
+
const { completed, interrupted } = interruptStaleRequestsOnStartup(2 * 60000);
|
|
228
|
+
// 收割到终态行时通知前端重取 stats(trailing 合并)
|
|
229
|
+
if (completed + interrupted > 0) {
|
|
230
|
+
scheduleStatsNotify();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
console.error('Stale request reaping failed:', err);
|
|
235
|
+
}
|
|
236
|
+
}, 60000);
|
|
236
237
|
// Initialize MCP connections (after DB is ready)
|
|
237
238
|
try {
|
|
238
239
|
await mcpManager.init();
|
|
@@ -245,7 +246,8 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
245
246
|
// Start stats aggregation timer (every 5 minutes)
|
|
246
247
|
const statsTimer = setInterval(() => {
|
|
247
248
|
try {
|
|
248
|
-
aggregateStats();
|
|
249
|
+
aggregateStats(maxDays);
|
|
250
|
+
broadcastToDataClients({ type: 'stats', action: 'notify' });
|
|
249
251
|
}
|
|
250
252
|
catch (err) {
|
|
251
253
|
console.error('Stats aggregation failed:', err);
|
|
@@ -262,7 +264,8 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
262
264
|
}, 1800000); // 30 minutes
|
|
263
265
|
// Initial aggregation on startup
|
|
264
266
|
try {
|
|
265
|
-
aggregateStats();
|
|
267
|
+
aggregateStats(maxDays);
|
|
268
|
+
broadcastToDataClients({ type: 'stats', action: 'notify' });
|
|
266
269
|
}
|
|
267
270
|
catch (err) {
|
|
268
271
|
console.error('Initial stats aggregation failed:', err);
|
|
@@ -278,6 +281,8 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
278
281
|
fastify.addHook('onClose', async () => {
|
|
279
282
|
clearInterval(statsTimer);
|
|
280
283
|
clearInterval(cleanupTimer);
|
|
284
|
+
clearInterval(staleReaperTimer);
|
|
285
|
+
clearStatsNotify();
|
|
281
286
|
await mcpManager.close();
|
|
282
287
|
closeDatabase();
|
|
283
288
|
});
|
|
@@ -286,9 +291,12 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
286
291
|
fastify.all('/v1/*', async (request, reply) => {
|
|
287
292
|
const startTime = Date.now();
|
|
288
293
|
const resolved = resolveProxyTarget(proxyConfig);
|
|
294
|
+
// 下游客户端中止:整个请求作用域建一次,两处上游 fetch 复用
|
|
295
|
+
const clientSignal = createClientAbortSignal(request.raw);
|
|
289
296
|
const sessionId = request.headers['x-claude-code-session-id'] || 'unknown-session';
|
|
290
297
|
// Replace model based on tier matching (getProxyModel)
|
|
291
298
|
const originalModel = request.body?.model;
|
|
299
|
+
let rec;
|
|
292
300
|
try {
|
|
293
301
|
const proxyModel = getProxyModel(originalModel, resolved);
|
|
294
302
|
if (proxyModel && request.body && typeof request.body === 'object') {
|
|
@@ -312,9 +320,9 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
312
320
|
(forwardedAuthHeader?.startsWith('Bearer ') ? forwardedAuthHeader.slice(7) : undefined);
|
|
313
321
|
// Log complete request data (Fastify already parsed body)
|
|
314
322
|
const machineId = getMachineIdSync() ?? undefined;
|
|
315
|
-
const
|
|
316
|
-
requestId: request.id, // Will be updated with cloud x-request-id later
|
|
323
|
+
const meta = {
|
|
317
324
|
sessionId,
|
|
325
|
+
localRequestId: request.id, // Will be replaced with cloud x-request-id row on completion
|
|
318
326
|
timestamp: new Date().toISOString(),
|
|
319
327
|
apiKey,
|
|
320
328
|
proxyApiKey,
|
|
@@ -326,34 +334,12 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
326
334
|
path: request.url,
|
|
327
335
|
headers: forwardHeaders,
|
|
328
336
|
body: request.body // Full payload: model, messages, tools, MCP params, etc.
|
|
329
|
-
},
|
|
330
|
-
response: {
|
|
331
|
-
statusCode: 0, // Will be updated after fetch
|
|
332
|
-
events: [] // Only used for JSON responses; streaming uses batch buffer
|
|
333
337
|
}
|
|
334
338
|
};
|
|
335
|
-
|
|
336
|
-
// Tail buffer: keep last 5 events for token usage extraction at stream end
|
|
337
|
-
const eventBatch = [];
|
|
338
|
-
const tailBuffer = [];
|
|
339
|
-
const eventIndexRef = { value: 0 };
|
|
339
|
+
rec = createRequestRecorder(meta);
|
|
340
340
|
request.log.info(`request logged: ${request.id} ${request.method} ${request.url}`);
|
|
341
341
|
// Save initial record + push pending status to frontend
|
|
342
|
-
await
|
|
343
|
-
pushNewRequest({
|
|
344
|
-
requestId: request.id,
|
|
345
|
-
sessionId,
|
|
346
|
-
compositeId: `${sessionId}:${request.id}`,
|
|
347
|
-
method: request.method,
|
|
348
|
-
path: request.url,
|
|
349
|
-
timestamp: requestData.timestamp,
|
|
350
|
-
statusCode: 0,
|
|
351
|
-
responseTime: 0,
|
|
352
|
-
model: forwardedModel,
|
|
353
|
-
machineId,
|
|
354
|
-
userAgent: request.headers['user-agent'],
|
|
355
|
-
baseUrl: resolved.baseUrl
|
|
356
|
-
}, 'pending');
|
|
342
|
+
await rec.pending();
|
|
357
343
|
// --- OpenAI Compatible Mode ---
|
|
358
344
|
if (resolved.is_openai_compatible) {
|
|
359
345
|
const openaiRequest = convertClaudeToOpenAIRequest(request.body, forwardedModel);
|
|
@@ -366,14 +352,11 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
366
352
|
method: request.method,
|
|
367
353
|
headers: openaiHeaders,
|
|
368
354
|
body: JSON.stringify(openaiRequest),
|
|
369
|
-
signal:
|
|
355
|
+
signal: createTimeoutSignal(300000, clientSignal)
|
|
370
356
|
});
|
|
371
357
|
request.log.info(`response status: ${response.status}`);
|
|
372
358
|
// Extract cloud request ID
|
|
373
359
|
const cloudRequestId = response.headers.get('x-request-id') || request.id;
|
|
374
|
-
requestData.requestId = cloudRequestId;
|
|
375
|
-
requestData.response.statusCode = response.status;
|
|
376
|
-
requestData.response.headers = Object.fromEntries(response.headers);
|
|
377
360
|
// Check response type
|
|
378
361
|
const contentType = response.headers.get('content-type') || '';
|
|
379
362
|
const isSSE = contentType.includes('text/event-stream');
|
|
@@ -386,46 +369,30 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
386
369
|
const openaiRes = JSON.parse(responseBody);
|
|
387
370
|
const claudeRes = convertOpenAIToClaudeResponse(openaiRes, forwardedModel || originalModel);
|
|
388
371
|
claudeResponseBody = JSON.stringify(claudeRes);
|
|
389
|
-
|
|
372
|
+
rec.appendEvent(claudeRes);
|
|
390
373
|
}
|
|
391
374
|
catch {
|
|
392
375
|
claudeResponseBody = responseBody;
|
|
393
376
|
try {
|
|
394
|
-
|
|
377
|
+
rec.appendEvent(JSON.parse(responseBody));
|
|
395
378
|
}
|
|
396
379
|
catch {
|
|
397
|
-
|
|
380
|
+
rec.appendEvent({ raw: responseBody });
|
|
398
381
|
}
|
|
399
382
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
deleteResponseEvents(request.id, sessionId);
|
|
403
|
-
deleteRequestRow(request.id, sessionId);
|
|
404
|
-
}
|
|
405
|
-
await saveRequestDB(sessionId, cloudRequestId, requestData);
|
|
406
|
-
const usage = extractTokenUsage(requestData.response.events);
|
|
407
|
-
pushNewRequest({
|
|
408
|
-
requestId: cloudRequestId,
|
|
409
|
-
sessionId,
|
|
410
|
-
compositeId: `${sessionId}:${cloudRequestId}`,
|
|
411
|
-
method: request.method,
|
|
412
|
-
path: request.url,
|
|
413
|
-
timestamp: requestData.timestamp,
|
|
383
|
+
await rec.complete({
|
|
384
|
+
cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
|
|
414
385
|
statusCode: response.status,
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
machineId,
|
|
418
|
-
userAgent: request.headers['user-agent'],
|
|
419
|
-
baseUrl: resolved.baseUrl,
|
|
420
|
-
usage,
|
|
421
|
-
pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
|
|
422
|
-
}, 'completed');
|
|
386
|
+
headers: Object.fromEntries(response.headers)
|
|
387
|
+
});
|
|
423
388
|
const elapsed = Date.now() - startTime;
|
|
424
389
|
request.log.info(`json response (openai): ${cloudRequestId} ${response.status} (${elapsed}ms)`);
|
|
425
390
|
reply.code(response.status).headers({ 'Content-Type': 'application/json' }).send(claudeResponseBody);
|
|
426
391
|
return;
|
|
427
392
|
}
|
|
428
393
|
// Handle SSE streaming response
|
|
394
|
+
// 上游响应头已到达:回写真实 status_code(状态仍 pending,保留 spinner)
|
|
395
|
+
rec.headers({ statusCode: response.status, headers: Object.fromEntries(response.headers) });
|
|
429
396
|
reply.hijack();
|
|
430
397
|
const responseHeaders = Object.fromEntries(response.headers);
|
|
431
398
|
// Replace content-type with Claude SSE format
|
|
@@ -433,55 +400,56 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
433
400
|
reply.raw.writeHead(response.status, responseHeaders);
|
|
434
401
|
if (response.body) {
|
|
435
402
|
const nodeStream = Readable.fromWeb(response.body);
|
|
403
|
+
// 客户端断开:销毁上游流,finished 回调走中断落库
|
|
404
|
+
clientSignal.addEventListener('abort', () => {
|
|
405
|
+
if (!nodeStream.destroyed)
|
|
406
|
+
nodeStream.destroy(new Error('client disconnected'));
|
|
407
|
+
});
|
|
408
|
+
const watchdog = attachIdleWatchdog(nodeStream);
|
|
436
409
|
const transformStream = createStreamTransformer(forwardedModel || originalModel);
|
|
437
|
-
//
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
try {
|
|
446
|
-
const event = JSON.parse(eventData);
|
|
447
|
-
pushEventToBuffers(event, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
|
|
448
|
-
}
|
|
449
|
-
catch {
|
|
450
|
-
// Not valid JSON, skip
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
410
|
+
// 流是否已逻辑终止(finish_reason / [DONE]);err 分支据此改走 complete
|
|
411
|
+
let logicallyEnded = false;
|
|
412
|
+
// 旁路解码器:只做事件入库,不介入转发管道;[DONE] 走 onDone 触发逻辑终止
|
|
413
|
+
const tapDecoder = createEventDecoder((e) => {
|
|
414
|
+
rec.appendEvent(e);
|
|
415
|
+
if (!logicallyEnded && isStreamCompleteEvents([e])) {
|
|
416
|
+
logicallyEnded = true;
|
|
417
|
+
watchdog.finish();
|
|
454
418
|
}
|
|
419
|
+
}, () => {
|
|
420
|
+
if (!logicallyEnded) {
|
|
421
|
+
logicallyEnded = true;
|
|
422
|
+
watchdog.finish();
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
nodeStream.on('data', (chunk) => {
|
|
426
|
+
tapDecoder.write(chunk);
|
|
455
427
|
});
|
|
456
428
|
// Use finished() for reliable stream completion detection
|
|
457
429
|
finished(nodeStream, async (err) => {
|
|
458
|
-
|
|
430
|
+
tapDecoder.end();
|
|
431
|
+
if (err && !logicallyEnded) {
|
|
459
432
|
request.log.error(`openai stream error: ${err.message}`);
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const lastChunk = tailBuffer.find((e) => e?.usage);
|
|
463
|
-
if (lastChunk?.usage) {
|
|
464
|
-
const syntheticEvent = {
|
|
465
|
-
type: 'message_delta',
|
|
466
|
-
usage: {
|
|
467
|
-
output_tokens: lastChunk.usage.completion_tokens || 0
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
pushEventToBuffers(syntheticEvent, sessionId, cloudRequestId, eventBatch, tailBuffer, eventIndexRef);
|
|
471
|
-
}
|
|
472
|
-
if (cloudRequestId !== request.id) {
|
|
473
|
-
deleteResponseEvents(request.id, sessionId);
|
|
474
|
-
deleteRequestRow(request.id, sessionId);
|
|
433
|
+
rec.interrupt({ statusCode: response.status, error: `stream interrupted: ${err.message}` });
|
|
434
|
+
return;
|
|
475
435
|
}
|
|
476
436
|
// Flush remaining batch + complete request
|
|
477
437
|
try {
|
|
478
|
-
await
|
|
438
|
+
await rec.complete({
|
|
439
|
+
cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
|
|
440
|
+
statusCode: response.status,
|
|
441
|
+
headers: Object.fromEntries(response.headers)
|
|
442
|
+
});
|
|
479
443
|
const elapsed = Date.now() - startTime;
|
|
480
|
-
request.log.info(`stream completed (openai): ${cloudRequestId} ${response.status} (${elapsed}ms
|
|
444
|
+
request.log.info(`stream completed (openai): ${cloudRequestId} ${response.status} (${elapsed}ms)`);
|
|
481
445
|
}
|
|
482
446
|
catch (dbErr) {
|
|
483
447
|
request.log.error(`openai stream save failed: ${dbErr.message}`);
|
|
484
448
|
}
|
|
449
|
+
// pipe 在无 'end' 的 destroy 后不会收尾 dest,手动结束客户端连接
|
|
450
|
+
if (reply.raw.writableEnded === false && !reply.raw.destroyed) {
|
|
451
|
+
reply.raw.end();
|
|
452
|
+
}
|
|
485
453
|
});
|
|
486
454
|
// Pipe: OpenAI SSE → Transform → Claude SSE → Client
|
|
487
455
|
nodeStream.pipe(transformStream).pipe(reply.raw);
|
|
@@ -498,15 +466,11 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
498
466
|
method: request.method,
|
|
499
467
|
headers: forwardHeaders,
|
|
500
468
|
body: request.method !== 'GET' ? JSON.stringify(request.body) : undefined,
|
|
501
|
-
signal:
|
|
469
|
+
signal: createTimeoutSignal(300000, clientSignal)
|
|
502
470
|
});
|
|
503
471
|
request.log.info(`response status: ${response.status}`);
|
|
504
472
|
// Extract cloud request ID from response header
|
|
505
473
|
const cloudRequestId = response.headers.get('x-request-id') || request.id;
|
|
506
|
-
requestData.requestId = cloudRequestId;
|
|
507
|
-
// Update response status in requestData
|
|
508
|
-
requestData.response.statusCode = response.status;
|
|
509
|
-
requestData.response.headers = Object.fromEntries(response.headers);
|
|
510
474
|
// Check response type: SSE stream or JSON
|
|
511
475
|
const contentType = response.headers.get('content-type') || '';
|
|
512
476
|
const isSSE = contentType.includes('text/event-stream');
|
|
@@ -517,35 +481,16 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
517
481
|
// Record JSON body as a single event
|
|
518
482
|
try {
|
|
519
483
|
const jsonBody = JSON.parse(responseBody);
|
|
520
|
-
|
|
484
|
+
rec.appendEvent(jsonBody);
|
|
521
485
|
}
|
|
522
486
|
catch {
|
|
523
|
-
|
|
524
|
-
}
|
|
525
|
-
requestData.response.completedAt = new Date().toISOString();
|
|
526
|
-
// Remove pending row if cloudRequestId differs from request.id
|
|
527
|
-
if (cloudRequestId !== request.id) {
|
|
528
|
-
deleteResponseEvents(request.id, sessionId);
|
|
529
|
-
deleteRequestRow(request.id, sessionId);
|
|
487
|
+
rec.appendEvent({ raw: responseBody });
|
|
530
488
|
}
|
|
531
|
-
await
|
|
532
|
-
|
|
533
|
-
pushNewRequest({
|
|
534
|
-
requestId: cloudRequestId,
|
|
535
|
-
sessionId,
|
|
536
|
-
compositeId: `${sessionId}:${cloudRequestId}`,
|
|
537
|
-
method: request.method,
|
|
538
|
-
path: request.url,
|
|
539
|
-
timestamp: requestData.timestamp,
|
|
489
|
+
await rec.complete({
|
|
490
|
+
cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
|
|
540
491
|
statusCode: response.status,
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
machineId,
|
|
544
|
-
userAgent: request.headers['user-agent'],
|
|
545
|
-
baseUrl: resolved.baseUrl,
|
|
546
|
-
usage,
|
|
547
|
-
pendingId: cloudRequestId !== request.id ? `${sessionId}:${request.id}` : undefined
|
|
548
|
-
}, 'completed');
|
|
492
|
+
headers: Object.fromEntries(response.headers)
|
|
493
|
+
});
|
|
549
494
|
const elapsed = Date.now() - startTime;
|
|
550
495
|
request.log.info(`json response: ${cloudRequestId} ${response.status} (${elapsed}ms)`);
|
|
551
496
|
reply.code(response.status);
|
|
@@ -554,6 +499,8 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
554
499
|
return;
|
|
555
500
|
}
|
|
556
501
|
// Hijack reply to take full control of SSE stream
|
|
502
|
+
// 上游响应头已到达:回写真实 status_code(状态仍 pending,保留 spinner)
|
|
503
|
+
rec.headers({ statusCode: response.status, headers: Object.fromEntries(response.headers) });
|
|
557
504
|
reply.hijack();
|
|
558
505
|
// Write response headers (preserve original from Aliyun)
|
|
559
506
|
const responseHeaders = Object.fromEntries(response.headers);
|
|
@@ -562,42 +509,54 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
562
509
|
if (response.body) {
|
|
563
510
|
// Convert Web ReadableStream to Node Readable stream
|
|
564
511
|
const nodeStream = Readable.fromWeb(response.body);
|
|
565
|
-
//
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
512
|
+
// 客户端断开:销毁上游流,finished 回调走中断落库
|
|
513
|
+
clientSignal.addEventListener('abort', () => {
|
|
514
|
+
if (!nodeStream.destroyed)
|
|
515
|
+
nodeStream.destroy(new Error('client disconnected'));
|
|
516
|
+
});
|
|
517
|
+
const watchdog = attachIdleWatchdog(nodeStream);
|
|
518
|
+
// 流是否已逻辑终止(message_stop);err 分支据此改走 complete
|
|
519
|
+
let logicallyEnded = false;
|
|
520
|
+
// 旁路解码器:只做事件入库,不介入转发管道
|
|
521
|
+
const tapDecoder = createEventDecoder((e) => {
|
|
522
|
+
rec.appendEvent(e);
|
|
523
|
+
if (!logicallyEnded && isStreamCompleteEvents([e])) {
|
|
524
|
+
logicallyEnded = true;
|
|
525
|
+
watchdog.finish();
|
|
526
|
+
}
|
|
527
|
+
}, () => {
|
|
528
|
+
if (!logicallyEnded) {
|
|
529
|
+
logicallyEnded = true;
|
|
530
|
+
watchdog.finish();
|
|
582
531
|
}
|
|
583
532
|
});
|
|
533
|
+
nodeStream.on('data', (chunk) => {
|
|
534
|
+
tapDecoder.write(chunk);
|
|
535
|
+
});
|
|
584
536
|
// Save request data when stream completes
|
|
585
537
|
finished(nodeStream, async (err) => {
|
|
586
|
-
|
|
538
|
+
tapDecoder.end();
|
|
539
|
+
if (err && !logicallyEnded) {
|
|
587
540
|
request.log.error(`claude stream error: ${err.message}`);
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
deleteResponseEvents(request.id, sessionId);
|
|
591
|
-
deleteRequestRow(request.id, sessionId);
|
|
541
|
+
rec.interrupt({ statusCode: response.status, error: `stream interrupted: ${err.message}` });
|
|
542
|
+
return;
|
|
592
543
|
}
|
|
593
544
|
try {
|
|
594
|
-
await
|
|
545
|
+
await rec.complete({
|
|
546
|
+
cloudRequestId: cloudRequestId !== request.id ? cloudRequestId : undefined,
|
|
547
|
+
statusCode: response.status,
|
|
548
|
+
headers: Object.fromEntries(response.headers)
|
|
549
|
+
});
|
|
595
550
|
const elapsed = Date.now() - startTime;
|
|
596
|
-
request.log.info(`stream completed: ${cloudRequestId} ${response.status} (${elapsed}ms
|
|
551
|
+
request.log.info(`stream completed: ${cloudRequestId} ${response.status} (${elapsed}ms)`);
|
|
597
552
|
}
|
|
598
553
|
catch (dbErr) {
|
|
599
554
|
request.log.error(`stream save failed: ${dbErr.message}`);
|
|
600
555
|
}
|
|
556
|
+
// pipe 在无 'end' 的 destroy 后不会收尾 dest,手动结束客户端连接
|
|
557
|
+
if (reply.raw.writableEnded === false && !reply.raw.destroyed) {
|
|
558
|
+
reply.raw.end();
|
|
559
|
+
}
|
|
601
560
|
});
|
|
602
561
|
// Pipe: Aliyun -> Claude Code
|
|
603
562
|
nodeStream.pipe(reply.raw);
|
|
@@ -610,22 +569,27 @@ export async function setupProxy(fastify, proxyConfig) {
|
|
|
610
569
|
errorMessage: error.message,
|
|
611
570
|
errorCode: error.code || 'unknown'
|
|
612
571
|
}, 'proxy error');
|
|
613
|
-
// Fetch failed — no cloudRequestId available, use Fastify request.id
|
|
614
|
-
pushNewRequest({
|
|
615
|
-
requestId: request.id,
|
|
616
|
-
sessionId,
|
|
617
|
-
compositeId: `${sessionId}:${request.id}`,
|
|
618
|
-
method: request.method,
|
|
619
|
-
path: request.url,
|
|
620
|
-
timestamp: new Date().toISOString(),
|
|
621
|
-
statusCode: getErrorStatusCode(error),
|
|
622
|
-
responseTime: Date.now() - startTime,
|
|
623
|
-
model: originalModel,
|
|
624
|
-
machineId: getMachineIdSync() ?? undefined,
|
|
625
|
-
userAgent: request.headers['user-agent'],
|
|
626
|
-
baseUrl: resolved.baseUrl
|
|
627
|
-
}, 'completed');
|
|
628
572
|
const statusCode = getErrorStatusCode(error);
|
|
573
|
+
if (rec) {
|
|
574
|
+
rec.fail({ statusCode, error: getErrorMessage(error) });
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
// pending() 之前就抛错(header/meta 准备阶段),无 DB 行可更新,仅推送
|
|
578
|
+
pushNewRequest({
|
|
579
|
+
requestId: request.id,
|
|
580
|
+
sessionId,
|
|
581
|
+
compositeId: `${sessionId}:${request.id}`,
|
|
582
|
+
method: request.method,
|
|
583
|
+
path: request.url,
|
|
584
|
+
timestamp: new Date().toISOString(),
|
|
585
|
+
statusCode,
|
|
586
|
+
responseTime: Date.now() - startTime,
|
|
587
|
+
model: originalModel,
|
|
588
|
+
machineId: getMachineIdSync() ?? undefined,
|
|
589
|
+
userAgent: request.headers['user-agent'],
|
|
590
|
+
baseUrl: resolved.baseUrl
|
|
591
|
+
}, 'completed');
|
|
592
|
+
}
|
|
629
593
|
reply.code(statusCode).send({
|
|
630
594
|
error: getErrorMessage(error),
|
|
631
595
|
requestId: request.id
|