@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/logger/index.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import pino from 'pino';
|
|
2
2
|
import type { LogConfig } from '../types/config.js';
|
|
3
|
-
declare const LEVEL_MAP: Record<string, number>;
|
|
4
|
-
declare const NUM_TO_LEVEL: Record<number, string>;
|
|
5
3
|
export declare function createLogger(config: LogConfig): pino.Logger;
|
|
6
4
|
export declare function readLogs(filePath: string, options: {
|
|
7
5
|
limit?: number;
|
|
8
6
|
level?: string;
|
|
9
7
|
grep?: string;
|
|
10
8
|
}): Promise<string[]>;
|
|
11
|
-
export { LEVEL_MAP, NUM_TO_LEVEL };
|
package/dist/logger/index.js
CHANGED
package/dist/mcp-server/tools.js
CHANGED
|
@@ -2,8 +2,8 @@ import fs from 'fs';
|
|
|
2
2
|
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { getDatabase } from '../utils/db.js';
|
|
4
4
|
import { listRequestDB } from '../utils/request-writer.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { getActiveProvider } from '../utils/providers-db.js';
|
|
6
|
+
import { vault } from '../utils/vault.js';
|
|
7
7
|
function ok(text) {
|
|
8
8
|
return { content: [{ type: 'text', text }] };
|
|
9
9
|
}
|
|
@@ -18,8 +18,7 @@ async function handleGetStatus(_args, ctx) {
|
|
|
18
18
|
const countRow = db.prepare('SELECT COUNT(*) AS n FROM requests').get();
|
|
19
19
|
let defaultProvider = null;
|
|
20
20
|
try {
|
|
21
|
-
const
|
|
22
|
-
const def = providers.find(p => p.default);
|
|
21
|
+
const def = getActiveProvider(db);
|
|
23
22
|
if (def) {
|
|
24
23
|
defaultProvider = { name: def.name, model: def.model, base_url: def.base_url };
|
|
25
24
|
}
|
|
@@ -192,10 +191,10 @@ const HANDLERS = {
|
|
|
192
191
|
list_requests: handleListRequests,
|
|
193
192
|
get_token_stats: handleGetTokenStats,
|
|
194
193
|
get_logs: handleGetLogs,
|
|
195
|
-
list_topics: async () => ok(JSON.stringify(await
|
|
194
|
+
list_topics: async () => ok(JSON.stringify(await vault.listTopics(), null, 2)),
|
|
196
195
|
get_topic: async (args) => {
|
|
197
196
|
try {
|
|
198
|
-
return ok(JSON.stringify(await
|
|
197
|
+
return ok(JSON.stringify(await vault.getTopic(args.topic), null, 2));
|
|
199
198
|
}
|
|
200
199
|
catch (e) {
|
|
201
200
|
const msg = e instanceof Error ? e.message : String(e);
|
|
@@ -206,7 +205,7 @@ const HANDLERS = {
|
|
|
206
205
|
}
|
|
207
206
|
},
|
|
208
207
|
query_knowledge: async (args) => {
|
|
209
|
-
const hits = await
|
|
208
|
+
const hits = await vault.queryKnowledge(args.query ?? '', args.limit);
|
|
210
209
|
if (hits.length === 0) {
|
|
211
210
|
return ok('No knowledge chunks matched. Call list_topics to browse available topics, or refine your query.');
|
|
212
211
|
}
|
|
@@ -214,10 +213,10 @@ const HANDLERS = {
|
|
|
214
213
|
hits: hits.map(h => ({ topic: h.topic, chunkId: h.chunkId, title: h.title, body: h.body })),
|
|
215
214
|
}, null, 2));
|
|
216
215
|
},
|
|
217
|
-
search_chunks: async (args) => ok(JSON.stringify(await
|
|
216
|
+
search_chunks: async (args) => ok(JSON.stringify(await vault.searchChunks(args.query ?? ''), null, 2)),
|
|
218
217
|
read_chunk: async (args) => {
|
|
219
218
|
try {
|
|
220
|
-
const body = await
|
|
219
|
+
const body = await vault.readChunk(args.topic, args.chunk_id);
|
|
221
220
|
return ok(body);
|
|
222
221
|
}
|
|
223
222
|
catch (e) {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
import { addDataClient, removeDataClient, pushNewRequest } from '../data-push';
|
|
3
|
+
// 统计 fake client 收到的各类广播
|
|
4
|
+
function messagesOf(send) {
|
|
5
|
+
return send.mock.calls.map((call) => JSON.parse(call[0]));
|
|
6
|
+
}
|
|
7
|
+
describe('data-push stats notify debounce', () => {
|
|
8
|
+
let send;
|
|
9
|
+
const fakeWs = {
|
|
10
|
+
readyState: WebSocket.OPEN,
|
|
11
|
+
send: jest.fn()
|
|
12
|
+
};
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.useFakeTimers();
|
|
15
|
+
send = fakeWs.send;
|
|
16
|
+
send.mockClear();
|
|
17
|
+
addDataClient(fakeWs);
|
|
18
|
+
});
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
removeDataClient(fakeWs);
|
|
21
|
+
jest.clearAllTimers();
|
|
22
|
+
jest.useRealTimers();
|
|
23
|
+
});
|
|
24
|
+
it('trailing:重复 completed push 会重置延迟窗口,只在最后一次后 1.5s 广播一次', () => {
|
|
25
|
+
// t=0 第一次 completed push
|
|
26
|
+
pushNewRequest({ id: 1 }, 'completed');
|
|
27
|
+
jest.advanceTimersByTime(1000);
|
|
28
|
+
// t=1000 第二次 completed push,trailing 重置:窗口从第二次重新起算
|
|
29
|
+
pushNewRequest({ id: 2 }, 'completed');
|
|
30
|
+
// 距第二次 1499ms(总 2499ms)仍无 stats 广播;若没有重置,第一次的 timer 在 t=1500 就会触发
|
|
31
|
+
jest.advanceTimersByTime(1499);
|
|
32
|
+
expect(messagesOf(send).filter((m) => m.type === 'stats')).toHaveLength(0);
|
|
33
|
+
// 总 2500ms(距第二次 1500ms)恰好广播一次
|
|
34
|
+
jest.advanceTimersByTime(1);
|
|
35
|
+
const statsMessages = messagesOf(send).filter((m) => m.type === 'stats');
|
|
36
|
+
expect(statsMessages).toHaveLength(1);
|
|
37
|
+
expect(statsMessages[0]).toEqual({ type: 'stats', action: 'notify' });
|
|
38
|
+
});
|
|
39
|
+
it('pending push 不产生 stats 广播', () => {
|
|
40
|
+
pushNewRequest({ id: 1 }, 'pending');
|
|
41
|
+
jest.advanceTimersByTime(5000);
|
|
42
|
+
expect(messagesOf(send).filter((m) => m.type === 'stats')).toHaveLength(0);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -49,6 +49,7 @@ jest.mock('@utils/db', () => {
|
|
|
49
49
|
provider TEXT NOT NULL,
|
|
50
50
|
is_default INTEGER NOT NULL DEFAULT 0,
|
|
51
51
|
is_openai_compatible INTEGER NOT NULL DEFAULT 0,
|
|
52
|
+
auth_style TEXT NOT NULL DEFAULT 'bearer',
|
|
52
53
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
53
54
|
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
54
55
|
)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { getErrorStatusCode, getErrorMessage, resolveProxyTarget, filterForwardHeaders } from '../proxy';
|
|
1
|
+
import { getErrorStatusCode, getErrorMessage, resolveProxyTarget, filterForwardHeaders, setupProxy } from '../proxy';
|
|
2
|
+
import Fastify from 'fastify';
|
|
3
|
+
import http from 'node:http';
|
|
2
4
|
import Database from 'better-sqlite3';
|
|
3
5
|
jest.mock('@utils/db', () => ({
|
|
4
6
|
__esModule: true,
|
|
@@ -6,7 +8,24 @@ jest.mock('@utils/db', () => ({
|
|
|
6
8
|
initDatabase: jest.fn(),
|
|
7
9
|
closeDatabase: jest.fn()
|
|
8
10
|
}));
|
|
9
|
-
|
|
11
|
+
jest.mock('@config/index', () => ({
|
|
12
|
+
loadConfig: jest.fn().mockResolvedValue({ storage: { maxDays: 7 } })
|
|
13
|
+
}));
|
|
14
|
+
jest.mock('../../utils/mcp-manager', () => ({
|
|
15
|
+
mcpManager: {
|
|
16
|
+
init: jest.fn().mockResolvedValue(undefined),
|
|
17
|
+
close: jest.fn().mockResolvedValue(undefined)
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
jest.mock('../../utils/machine-id', () => ({
|
|
21
|
+
getMachineId: jest.fn().mockResolvedValue('test-machine-id'),
|
|
22
|
+
getMachineIdSync: jest.fn().mockReturnValue('test-machine-id'),
|
|
23
|
+
preloadMachineId: jest.fn().mockResolvedValue(undefined)
|
|
24
|
+
}));
|
|
25
|
+
jest.mock('../../utils/stats-aggregator', () => ({
|
|
26
|
+
aggregateStats: jest.fn()
|
|
27
|
+
}));
|
|
28
|
+
const { getDatabase, initDatabase } = jest.requireMock('@utils/db');
|
|
10
29
|
function makeError(code, message) {
|
|
11
30
|
const error = new Error(message);
|
|
12
31
|
error.code = code;
|
|
@@ -135,3 +154,263 @@ describe('filterForwardHeaders with authStyle', () => {
|
|
|
135
154
|
expect(out['x-api-key']).toBe('inbound-key');
|
|
136
155
|
});
|
|
137
156
|
});
|
|
157
|
+
// --- G2: 逻辑终止识别 + 收割接入(setupProxy 集成测试)---
|
|
158
|
+
// 构造集成测试用内存库:仅建 proxy.ts 运行路径触及的表
|
|
159
|
+
function createIntegrationDb() {
|
|
160
|
+
const db = new Database(':memory:');
|
|
161
|
+
db.exec(`
|
|
162
|
+
CREATE TABLE requests (
|
|
163
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
164
|
+
request_id TEXT UNIQUE NOT NULL,
|
|
165
|
+
session_id TEXT NOT NULL,
|
|
166
|
+
timestamp TEXT NOT NULL,
|
|
167
|
+
method TEXT NOT NULL,
|
|
168
|
+
path TEXT NOT NULL,
|
|
169
|
+
status_code INTEGER NOT NULL,
|
|
170
|
+
response_time INTEGER NOT NULL,
|
|
171
|
+
api_key TEXT,
|
|
172
|
+
proxy_api_key TEXT,
|
|
173
|
+
base_url TEXT,
|
|
174
|
+
model TEXT,
|
|
175
|
+
machine_id TEXT,
|
|
176
|
+
user_agent TEXT,
|
|
177
|
+
input_tokens INTEGER,
|
|
178
|
+
output_tokens INTEGER,
|
|
179
|
+
cache_read_tokens INTEGER,
|
|
180
|
+
cache_creation_tokens INTEGER,
|
|
181
|
+
request_headers TEXT,
|
|
182
|
+
request_body TEXT,
|
|
183
|
+
response_headers TEXT,
|
|
184
|
+
response_events TEXT,
|
|
185
|
+
completed_at TEXT,
|
|
186
|
+
closed_at TEXT,
|
|
187
|
+
error TEXT,
|
|
188
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
189
|
+
);
|
|
190
|
+
CREATE TABLE response_events (
|
|
191
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
192
|
+
request_id TEXT NOT NULL,
|
|
193
|
+
session_id TEXT NOT NULL,
|
|
194
|
+
event_index INTEGER NOT NULL,
|
|
195
|
+
event_type TEXT,
|
|
196
|
+
event_data TEXT NOT NULL
|
|
197
|
+
);
|
|
198
|
+
CREATE TABLE learn_tasks (
|
|
199
|
+
task_id TEXT PRIMARY KEY,
|
|
200
|
+
slug TEXT NOT NULL,
|
|
201
|
+
source_type TEXT NOT NULL,
|
|
202
|
+
label TEXT NOT NULL,
|
|
203
|
+
status TEXT NOT NULL,
|
|
204
|
+
error TEXT,
|
|
205
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
206
|
+
ended_at TEXT
|
|
207
|
+
);
|
|
208
|
+
CREATE TABLE providers (
|
|
209
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
210
|
+
name TEXT UNIQUE NOT NULL,
|
|
211
|
+
base_url TEXT,
|
|
212
|
+
api_key TEXT,
|
|
213
|
+
model TEXT,
|
|
214
|
+
haiku_model TEXT,
|
|
215
|
+
sonnet_model TEXT,
|
|
216
|
+
opus_model TEXT,
|
|
217
|
+
subagent_model TEXT,
|
|
218
|
+
provider TEXT NOT NULL,
|
|
219
|
+
is_default INTEGER NOT NULL DEFAULT 0,
|
|
220
|
+
is_openai_compatible INTEGER NOT NULL DEFAULT 0,
|
|
221
|
+
auth_style TEXT NOT NULL DEFAULT 'bearer',
|
|
222
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
223
|
+
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
224
|
+
);
|
|
225
|
+
`);
|
|
226
|
+
return db;
|
|
227
|
+
}
|
|
228
|
+
function sseFrame(event, data) {
|
|
229
|
+
const body = typeof data === 'string' ? data : JSON.stringify(data);
|
|
230
|
+
return event !== null ? `event: ${event}\ndata: ${body}\n\n` : `data: ${body}\n\n`;
|
|
231
|
+
}
|
|
232
|
+
const completeClaudeFrames = [
|
|
233
|
+
sseFrame('message_start', { type: 'message_start', usage: { input_tokens: 10, cache_read_input_tokens: 2 } }),
|
|
234
|
+
sseFrame('ping', { type: 'ping' }),
|
|
235
|
+
sseFrame('message_delta', { type: 'message_delta', usage: { output_tokens: 4 } }),
|
|
236
|
+
sseFrame('message_stop', { type: 'message_stop' })
|
|
237
|
+
];
|
|
238
|
+
const incompleteClaudeFrames = [
|
|
239
|
+
sseFrame('message_start', { type: 'message_start', usage: { input_tokens: 3 } }),
|
|
240
|
+
sseFrame('content_block_start', { type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } }),
|
|
241
|
+
sseFrame('content_block_delta', { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'hel' } })
|
|
242
|
+
];
|
|
243
|
+
// 上游 Web 流:逐帧异步入队;endAfter=false 时入队后保持悬挂(不 close)。
|
|
244
|
+
// singleChunk=true 时所有帧合并为一次 enqueue(模拟真实 TCP 合包,如
|
|
245
|
+
// finish_reason chunk 与紧随的 [DONE] 通常在同一缓冲到达)。
|
|
246
|
+
function createUpstreamWebStream(frames, endAfter = false, singleChunk = false) {
|
|
247
|
+
let controller;
|
|
248
|
+
const webStream = new ReadableStream({
|
|
249
|
+
start(c) { controller = c; }
|
|
250
|
+
});
|
|
251
|
+
void (async () => {
|
|
252
|
+
try {
|
|
253
|
+
if (singleChunk) {
|
|
254
|
+
controller.enqueue(Buffer.from(frames.join(''), 'utf-8'));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
for (const frame of frames) {
|
|
258
|
+
controller.enqueue(Buffer.from(frame, 'utf-8'));
|
|
259
|
+
await new Promise(r => setTimeout(r, 3));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (endAfter)
|
|
263
|
+
controller.close();
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// nodeStream.destroy 取消 web 流后 controller 已关闭,忽略后续入队
|
|
267
|
+
}
|
|
268
|
+
})();
|
|
269
|
+
return webStream;
|
|
270
|
+
}
|
|
271
|
+
async function waitFor(predicate, { timeout = 3000, interval = 20 } = {}) {
|
|
272
|
+
const start = Date.now();
|
|
273
|
+
while (Date.now() - start < timeout) {
|
|
274
|
+
const value = predicate();
|
|
275
|
+
if (value !== undefined)
|
|
276
|
+
return value;
|
|
277
|
+
await new Promise(r => setTimeout(r, interval));
|
|
278
|
+
}
|
|
279
|
+
throw new Error('waitFor timed out');
|
|
280
|
+
}
|
|
281
|
+
function mockUpstreamFetch(webStream, requestId) {
|
|
282
|
+
return jest.spyOn(globalThis, 'fetch').mockImplementation(async () => new Response(webStream, {
|
|
283
|
+
status: 200,
|
|
284
|
+
headers: { 'content-type': 'text/event-stream', 'x-request-id': requestId }
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
const proxyConfig = {
|
|
288
|
+
target: 'https://upstream.example.com/apps/anthropic',
|
|
289
|
+
changeOrigin: true
|
|
290
|
+
};
|
|
291
|
+
const requestPayload = JSON.stringify({
|
|
292
|
+
model: 'claude-sonnet-4-20250514',
|
|
293
|
+
max_tokens: 16,
|
|
294
|
+
messages: [{ role: 'user', content: 'hi' }]
|
|
295
|
+
});
|
|
296
|
+
describe('setupProxy SSE lifecycle (G2)', () => {
|
|
297
|
+
let db;
|
|
298
|
+
let fastify;
|
|
299
|
+
beforeEach(() => {
|
|
300
|
+
jest.clearAllMocks();
|
|
301
|
+
db = createIntegrationDb();
|
|
302
|
+
getDatabase.mockReturnValue(db);
|
|
303
|
+
initDatabase.mockReturnValue(db);
|
|
304
|
+
fastify = Fastify();
|
|
305
|
+
});
|
|
306
|
+
afterEach(async () => {
|
|
307
|
+
delete process.env.CCM_STREAM_IDLE_TIMEOUT_MS;
|
|
308
|
+
await fastify.close();
|
|
309
|
+
db.close();
|
|
310
|
+
});
|
|
311
|
+
test('message_stop 已发出后上游悬挂:行落 completed_at,不标中断,客户端不悬挂', async () => {
|
|
312
|
+
await setupProxy(fastify, proxyConfig);
|
|
313
|
+
mockUpstreamFetch(createUpstreamWebStream(completeClaudeFrames), 'cloud-req-1');
|
|
314
|
+
const response = await fastify.inject({
|
|
315
|
+
method: 'POST',
|
|
316
|
+
url: '/v1/messages',
|
|
317
|
+
headers: { 'content-type': 'application/json' },
|
|
318
|
+
payload: requestPayload
|
|
319
|
+
});
|
|
320
|
+
expect(response.statusCode).toBe(200);
|
|
321
|
+
expect(response.payload).toContain('message_stop');
|
|
322
|
+
const row = db.prepare('SELECT * FROM requests WHERE request_id = ?').get('cloud-req-1');
|
|
323
|
+
expect(row).toBeDefined();
|
|
324
|
+
expect(row.completed_at).not.toBeNull();
|
|
325
|
+
expect(row.closed_at).toBeNull();
|
|
326
|
+
expect(row.error).toBeNull();
|
|
327
|
+
});
|
|
328
|
+
test('无 message_stop 的 idle 超时仍标中断(closed_at + error)', async () => {
|
|
329
|
+
// 测试专用时长覆盖:真实定时器,只缩短看门狗超时
|
|
330
|
+
process.env.CCM_STREAM_IDLE_TIMEOUT_MS = '80';
|
|
331
|
+
await setupProxy(fastify, proxyConfig);
|
|
332
|
+
mockUpstreamFetch(createUpstreamWebStream(incompleteClaudeFrames), 'cloud-req-2');
|
|
333
|
+
// 走真实 TCP:中断分支 pipe 不结束客户端(保持既有语义),客户端自行判超时
|
|
334
|
+
await fastify.listen({ port: 0, host: '127.0.0.1' });
|
|
335
|
+
const port = fastify.server.address().port;
|
|
336
|
+
const clientReq = http.request({
|
|
337
|
+
port,
|
|
338
|
+
host: '127.0.0.1',
|
|
339
|
+
method: 'POST',
|
|
340
|
+
path: '/v1/messages',
|
|
341
|
+
headers: { 'content-type': 'application/json', 'content-length': Buffer.byteLength(requestPayload) }
|
|
342
|
+
});
|
|
343
|
+
clientReq.on('error', () => { });
|
|
344
|
+
clientReq.end(requestPayload);
|
|
345
|
+
// 客户端收到数据后看门狗触发,不再有数据;保底 1s 后自毁连接
|
|
346
|
+
const clientTimer = setTimeout(() => clientReq.destroy(), 1000);
|
|
347
|
+
clientReq.on('close', () => clearTimeout(clientTimer));
|
|
348
|
+
const row = await waitFor(() => {
|
|
349
|
+
const r = db.prepare('SELECT * FROM requests LIMIT 1').get();
|
|
350
|
+
return r && r.closed_at ? r : undefined;
|
|
351
|
+
});
|
|
352
|
+
expect(row.completed_at).toBeNull();
|
|
353
|
+
expect(row.closed_at).not.toBeNull();
|
|
354
|
+
expect(row.error).toContain('idle timeout');
|
|
355
|
+
expect(row.status_code).toBe(200);
|
|
356
|
+
});
|
|
357
|
+
test('OpenAI 流 finish_reason + [DONE] 后上游悬挂:行落 completed_at', async () => {
|
|
358
|
+
db.prepare(`
|
|
359
|
+
INSERT INTO providers (name, base_url, api_key, model, provider, is_default, is_openai_compatible, auth_style)
|
|
360
|
+
VALUES ('openai-test', 'https://openai.example.com/v1', 'k', 'gpt-4o', 'openai', 1, 1, 'bearer')
|
|
361
|
+
`).run();
|
|
362
|
+
await setupProxy(fastify, proxyConfig);
|
|
363
|
+
const openaiFrames = [
|
|
364
|
+
sseFrame(null, { id: 'chat-1', choices: [{ delta: { role: 'assistant' }, finish_reason: null }] }),
|
|
365
|
+
sseFrame(null, { id: 'chat-1', choices: [{ delta: { content: 'hi' }, finish_reason: null }] }),
|
|
366
|
+
sseFrame(null, { id: 'chat-1', choices: [{ delta: {}, finish_reason: 'stop' }], usage: { prompt_tokens: 5, completion_tokens: 7 } }),
|
|
367
|
+
sseFrame(null, '[DONE]')
|
|
368
|
+
];
|
|
369
|
+
const fetchSpy = mockUpstreamFetch(createUpstreamWebStream(openaiFrames, false, true), 'cloud-openai-1');
|
|
370
|
+
const response = await fastify.inject({
|
|
371
|
+
method: 'POST',
|
|
372
|
+
url: '/v1/messages',
|
|
373
|
+
headers: { 'content-type': 'application/json' },
|
|
374
|
+
payload: requestPayload
|
|
375
|
+
});
|
|
376
|
+
expect(response.statusCode).toBe(200);
|
|
377
|
+
expect(response.payload).toContain('message_stop');
|
|
378
|
+
expect(fetchSpy.mock.calls[0][0]).toBe('https://openai.example.com/v1/chat/completions');
|
|
379
|
+
const row = db.prepare('SELECT * FROM requests WHERE request_id = ?').get('cloud-openai-1');
|
|
380
|
+
expect(row).toBeDefined();
|
|
381
|
+
expect(row.completed_at).not.toBeNull();
|
|
382
|
+
expect(row.closed_at).toBeNull();
|
|
383
|
+
expect(row.error).toBeNull();
|
|
384
|
+
});
|
|
385
|
+
test('message_stop 到达后客户端断开:内容已完整交付,仍走 complete', async () => {
|
|
386
|
+
await setupProxy(fastify, proxyConfig);
|
|
387
|
+
mockUpstreamFetch(createUpstreamWebStream(completeClaudeFrames), 'cloud-req-3');
|
|
388
|
+
await fastify.listen({ port: 0, host: '127.0.0.1' });
|
|
389
|
+
const port = fastify.server.address().port;
|
|
390
|
+
await new Promise((resolve, reject) => {
|
|
391
|
+
const clientReq = http.request({
|
|
392
|
+
port,
|
|
393
|
+
host: '127.0.0.1',
|
|
394
|
+
method: 'POST',
|
|
395
|
+
path: '/v1/messages',
|
|
396
|
+
headers: { 'content-type': 'application/json', 'content-length': Buffer.byteLength(requestPayload) }
|
|
397
|
+
});
|
|
398
|
+
clientReq.on('error', () => { });
|
|
399
|
+
clientReq.on('response', (res) => {
|
|
400
|
+
res.on('data', (chunk) => {
|
|
401
|
+
if (chunk.toString().includes('message_stop'))
|
|
402
|
+
clientReq.destroy();
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
clientReq.on('close', () => resolve());
|
|
406
|
+
setTimeout(() => reject(new Error('client did not close')), 3000).unref();
|
|
407
|
+
clientReq.end(requestPayload);
|
|
408
|
+
});
|
|
409
|
+
const row = await waitFor(() => {
|
|
410
|
+
const r = db.prepare('SELECT * FROM requests WHERE request_id = ?').get('cloud-req-3');
|
|
411
|
+
return r && r.completed_at ? r : undefined;
|
|
412
|
+
});
|
|
413
|
+
expect(row.closed_at).toBeNull();
|
|
414
|
+
expect(row.error).toBeNull();
|
|
415
|
+
});
|
|
416
|
+
});
|
|
@@ -105,22 +105,27 @@ describe('createServer', () => {
|
|
|
105
105
|
expect(response).toBeDefined();
|
|
106
106
|
expect(typeof response.statusCode).toBe('number');
|
|
107
107
|
});
|
|
108
|
-
it('
|
|
108
|
+
it('uses x-request-id header value as request id when header present', async () => {
|
|
109
109
|
server = await createServer();
|
|
110
|
+
server.get('/request-id-echo', async (request) => ({ id: request.id }));
|
|
110
111
|
const response = await server.inject({
|
|
111
112
|
method: 'GET',
|
|
112
|
-
url: '/
|
|
113
|
-
headers: { 'x-request-id': '
|
|
113
|
+
url: '/request-id-echo',
|
|
114
|
+
headers: { 'x-request-id': 'my-trace-123' }
|
|
114
115
|
});
|
|
115
|
-
expect(response).
|
|
116
|
+
expect(JSON.parse(response.body).id).toBe('my-trace-123');
|
|
116
117
|
});
|
|
117
|
-
it('
|
|
118
|
+
it('generates non-default nanoid request id without header, unique per request', async () => {
|
|
118
119
|
server = await createServer();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
server.get('/request-id-echo', async (request) => ({ id: request.id }));
|
|
121
|
+
const response1 = await server.inject({ method: 'GET', url: '/request-id-echo' });
|
|
122
|
+
const response2 = await server.inject({ method: 'GET', url: '/request-id-echo' });
|
|
123
|
+
const id1 = JSON.parse(response1.body).id;
|
|
124
|
+
const id2 = JSON.parse(response2.body).id;
|
|
125
|
+
expect(id1).not.toMatch(/^req-\d+$/);
|
|
126
|
+
expect(id2).not.toMatch(/^req-\d+$/);
|
|
127
|
+
expect(id1).toHaveLength(16);
|
|
128
|
+
expect(id1).not.toBe(id2);
|
|
124
129
|
});
|
|
125
130
|
it('server closes cleanly', async () => {
|
|
126
131
|
server = await createServer();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Fastify from 'fastify';
|
|
2
|
+
import * as statsAggregator from '@utils/stats-aggregator';
|
|
2
3
|
import { setupUiRoutes } from '../ui-route';
|
|
3
4
|
// Mock PM2 utils
|
|
4
5
|
jest.mock('@utils/pm2', () => ({
|
|
@@ -372,4 +373,46 @@ describe('setupUiRoutes', () => {
|
|
|
372
373
|
expect(body.error).toContain('Disk write failed');
|
|
373
374
|
});
|
|
374
375
|
});
|
|
376
|
+
describe('GET /ui/api/stats', () => {
|
|
377
|
+
let aggregateSpy;
|
|
378
|
+
let getStatsSpy;
|
|
379
|
+
beforeEach(() => {
|
|
380
|
+
aggregateSpy = jest.spyOn(statsAggregator, 'aggregateStats').mockImplementation(() => { });
|
|
381
|
+
getStatsSpy = jest.spyOn(statsAggregator, 'getStatsData').mockReturnValue({ hourly: {}, lastUpdated: null });
|
|
382
|
+
});
|
|
383
|
+
afterEach(() => {
|
|
384
|
+
aggregateSpy.mockRestore();
|
|
385
|
+
getStatsSpy.mockRestore();
|
|
386
|
+
});
|
|
387
|
+
it('读前触发 aggregateStats 并返回 200', async () => {
|
|
388
|
+
const response = await server.inject({ method: 'GET', url: '/ui/api/stats' });
|
|
389
|
+
expect(aggregateSpy).toHaveBeenCalledTimes(1);
|
|
390
|
+
expect(aggregateSpy).toHaveBeenCalledWith(7);
|
|
391
|
+
expect(getStatsSpy).toHaveBeenCalledTimes(1);
|
|
392
|
+
// 必须先聚合再读取
|
|
393
|
+
expect(aggregateSpy.mock.invocationCallOrder[0]).toBeLessThan(getStatsSpy.mock.invocationCallOrder[0]);
|
|
394
|
+
expect(response.statusCode).toBe(200);
|
|
395
|
+
expect(JSON.parse(response.body)).toEqual({ hourly: {}, lastUpdated: null });
|
|
396
|
+
});
|
|
397
|
+
it('聚合失败只记录不阻断读取,仍返回 200', async () => {
|
|
398
|
+
aggregateSpy.mockImplementation(() => { throw new Error('agg boom'); });
|
|
399
|
+
// inject 请求的 request.log 来自 server.log.child(),替换为可观测的 mock
|
|
400
|
+
const logError = jest.fn();
|
|
401
|
+
jest.spyOn(server.log, 'child').mockReturnValue({
|
|
402
|
+
level: 'info',
|
|
403
|
+
fatal: jest.fn(),
|
|
404
|
+
error: logError,
|
|
405
|
+
warn: jest.fn(),
|
|
406
|
+
info: jest.fn(),
|
|
407
|
+
debug: jest.fn(),
|
|
408
|
+
trace: jest.fn(),
|
|
409
|
+
child: jest.fn()
|
|
410
|
+
});
|
|
411
|
+
const response = await server.inject({ method: 'GET', url: '/ui/api/stats' });
|
|
412
|
+
expect(response.statusCode).toBe(200);
|
|
413
|
+
expect(getStatsSpy).toHaveBeenCalledTimes(1);
|
|
414
|
+
// 聚合异常必须记录日志
|
|
415
|
+
expect(logError).toHaveBeenCalledTimes(1);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
375
418
|
});
|
package/dist/server/auth.d.ts
CHANGED
package/dist/server/auth.js
CHANGED