@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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createSSEDecoder, createEventDecoder, encodeSSEFrame } from '../sse-protocol.js';
|
|
2
|
+
// 收集分发帧的辅助
|
|
3
|
+
function collectFrames() {
|
|
4
|
+
const frames = [];
|
|
5
|
+
const decoder = createSSEDecoder((frame) => frames.push(frame));
|
|
6
|
+
return { frames, decoder };
|
|
7
|
+
}
|
|
8
|
+
describe('sse-protocol', () => {
|
|
9
|
+
// 样例 1:单帧含 event: + data:
|
|
10
|
+
it('1. 单帧含 event 和 data', () => {
|
|
11
|
+
const { frames, decoder } = collectFrames();
|
|
12
|
+
decoder.write('event: foo\ndata: {"a":1}\n\n');
|
|
13
|
+
expect(frames).toEqual([
|
|
14
|
+
{ event: 'foo', data: ['{"a":1}'], id: undefined }
|
|
15
|
+
]);
|
|
16
|
+
});
|
|
17
|
+
// 样例 2:仅 data:(event 为 undefined)
|
|
18
|
+
it('2. 仅 data,event 缺省为 undefined', () => {
|
|
19
|
+
const { frames, decoder } = collectFrames();
|
|
20
|
+
decoder.write('data: {"b":2}\n\n');
|
|
21
|
+
expect(frames).toEqual([
|
|
22
|
+
{ event: undefined, data: ['{"b":2}'], id: undefined }
|
|
23
|
+
]);
|
|
24
|
+
});
|
|
25
|
+
// 样例 3:一个 data 行切成 3 个 chunk(残帧缓冲)
|
|
26
|
+
it('3. data 行跨 3 个 chunk 恰好得到一帧', () => {
|
|
27
|
+
const { frames, decoder } = collectFrames();
|
|
28
|
+
decoder.write('dat');
|
|
29
|
+
expect(frames).toHaveLength(0);
|
|
30
|
+
decoder.write('a: {"x');
|
|
31
|
+
expect(frames).toHaveLength(0);
|
|
32
|
+
decoder.write('":1}\n\n');
|
|
33
|
+
expect(frames).toEqual([
|
|
34
|
+
{ event: undefined, data: ['{"x":1}'], id: undefined }
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
// 样例 4:CRLF 行尾 + 空行分帧
|
|
38
|
+
it('4. CRLF 行尾与空行分帧', () => {
|
|
39
|
+
const { frames, decoder } = collectFrames();
|
|
40
|
+
decoder.write('event: ping\r\ndata: {"y":2}\r\n\r\n');
|
|
41
|
+
expect(frames).toEqual([
|
|
42
|
+
{ event: 'ping', data: ['{"y":2}'], id: undefined }
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
// 样例 5:同一帧内多行 data:,数组保留两行,消费 join('\n') 正确
|
|
46
|
+
it('5. 多行 data 逐行保留且 join 正确', () => {
|
|
47
|
+
const { frames, decoder } = collectFrames();
|
|
48
|
+
decoder.write('data: line1\ndata: line2\n\n');
|
|
49
|
+
expect(frames).toHaveLength(1);
|
|
50
|
+
expect(frames[0].data).toEqual(['line1', 'line2']);
|
|
51
|
+
expect(frames[0].data.join('\n')).toBe('line1\nline2');
|
|
52
|
+
});
|
|
53
|
+
// 样例 6:注释行不产生帧
|
|
54
|
+
it('6. 注释行被忽略不产生帧', () => {
|
|
55
|
+
const { frames, decoder } = collectFrames();
|
|
56
|
+
decoder.write(': ping\n\n');
|
|
57
|
+
expect(frames).toHaveLength(0);
|
|
58
|
+
// 注释与真实帧混合时仅真实帧分发
|
|
59
|
+
decoder.write(': keep-alive\ndata: {"r":1}\n\n');
|
|
60
|
+
expect(frames).toEqual([
|
|
61
|
+
{ event: undefined, data: ['{"r":1}'], id: undefined }
|
|
62
|
+
]);
|
|
63
|
+
});
|
|
64
|
+
// 样例 7:data: [DONE] → onDone 触发、onEvent 不触发
|
|
65
|
+
it('7. [DONE] 仅触发 onDone', () => {
|
|
66
|
+
const events = [];
|
|
67
|
+
let doneCount = 0;
|
|
68
|
+
const decoder = createEventDecoder((e) => events.push(e), () => { doneCount++; });
|
|
69
|
+
decoder.write('data: [DONE]\n\n');
|
|
70
|
+
expect(events).toHaveLength(0);
|
|
71
|
+
expect(doneCount).toBe(1);
|
|
72
|
+
});
|
|
73
|
+
// 样例 8:非 JSON data → onEvent 不触发、不抛错
|
|
74
|
+
it('8. 非 JSON data 静默跳过', () => {
|
|
75
|
+
const events = [];
|
|
76
|
+
const decoder = createEventDecoder((e) => events.push(e));
|
|
77
|
+
expect(() => {
|
|
78
|
+
decoder.write('data: not-json\n\n');
|
|
79
|
+
}).not.toThrow();
|
|
80
|
+
expect(events).toHaveLength(0);
|
|
81
|
+
});
|
|
82
|
+
// 样例 9:末尾帧无尾空行,write 不触发,end() 后恰好一帧
|
|
83
|
+
it('9. end() 输出末尾无空行的残留帧', () => {
|
|
84
|
+
const { frames, decoder } = collectFrames();
|
|
85
|
+
decoder.write('data: {"z":9}');
|
|
86
|
+
expect(frames).toHaveLength(0);
|
|
87
|
+
decoder.end();
|
|
88
|
+
expect(frames).toEqual([
|
|
89
|
+
{ event: undefined, data: ['{"z":9}'], id: undefined }
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
// 样例 10:id: 行保留在帧 id 字段
|
|
93
|
+
it('10. id 字段保留在帧上', () => {
|
|
94
|
+
const { frames, decoder } = collectFrames();
|
|
95
|
+
decoder.write('id: e1\ndata: {"i":1}\n\n');
|
|
96
|
+
expect(frames).toEqual([
|
|
97
|
+
{ event: undefined, data: ['{"i":1}'], id: 'e1' }
|
|
98
|
+
]);
|
|
99
|
+
});
|
|
100
|
+
// 样例 11:encodeSSEFrame → createSSEDecoder 往返一致(有/无 event)
|
|
101
|
+
it('11. encode/decode 往返帧内容一致(有 event 和无 event)', () => {
|
|
102
|
+
const withEvent = encodeSSEFrame({ event: 'foo', data: '{"x":1}' });
|
|
103
|
+
const d1 = collectFrames();
|
|
104
|
+
d1.decoder.write(withEvent);
|
|
105
|
+
expect(d1.frames).toEqual([
|
|
106
|
+
{ event: 'foo', data: ['{"x":1}'], id: undefined }
|
|
107
|
+
]);
|
|
108
|
+
const withoutEvent = encodeSSEFrame({ data: '{"x":2}' });
|
|
109
|
+
const d2 = collectFrames();
|
|
110
|
+
d2.decoder.write(withoutEvent);
|
|
111
|
+
expect(d2.frames).toEqual([
|
|
112
|
+
{ event: undefined, data: ['{"x":2}'], id: undefined }
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from 'fs';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { configureDbPath, initDatabase, closeDatabase, getDatabase } from '../db.js';
|
|
5
|
+
import { aggregateStats, getStatsData, getTodayStats } from '../stats-aggregator.js';
|
|
6
|
+
let counter = 0;
|
|
7
|
+
// 直接往 requests 表插行,绕过 request-writer,聚焦聚合口径
|
|
8
|
+
function insertRequest(opts) {
|
|
9
|
+
counter += 1;
|
|
10
|
+
getDatabase()
|
|
11
|
+
.prepare(`
|
|
12
|
+
INSERT INTO requests (
|
|
13
|
+
request_id, session_id, timestamp, method, path, status_code, response_time,
|
|
14
|
+
model, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, closed_at
|
|
15
|
+
) VALUES (?, ?, ?, 'POST', '/v1/messages', 200, 100, ?, ?, ?, ?, ?, ?)
|
|
16
|
+
`)
|
|
17
|
+
.run(`req_${counter}`, `sess_${counter}`, opts.timestamp, opts.model ?? null, opts.inputTokens ?? null, opts.outputTokens ?? null, opts.cacheReadTokens ?? null, opts.cacheCreationTokens ?? null, opts.closedAt ?? null);
|
|
18
|
+
}
|
|
19
|
+
function hourKeyOf(iso) {
|
|
20
|
+
const d = new Date(iso);
|
|
21
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')}-${String(d.getUTCHours()).padStart(2, '0')}`;
|
|
22
|
+
}
|
|
23
|
+
describe('stats-aggregator', () => {
|
|
24
|
+
let tempDir;
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
tempDir = mkdtempSync(path.join(tmpdir(), 'ccm-stats-'));
|
|
27
|
+
configureDbPath(path.join(tempDir, 'test.db'));
|
|
28
|
+
initDatabase();
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
closeDatabase();
|
|
32
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
33
|
+
});
|
|
34
|
+
it('P0: requests 源行删除后重算,冻结的 hourly 快照同步消失', () => {
|
|
35
|
+
const ts = new Date(Date.now() - 2 * 3600000).toISOString();
|
|
36
|
+
const key = hourKeyOf(ts);
|
|
37
|
+
insertRequest({ timestamp: ts, inputTokens: 100, outputTokens: 20 });
|
|
38
|
+
aggregateStats();
|
|
39
|
+
let stats = getStatsData();
|
|
40
|
+
expect(stats.hourly[key]).toBeTruthy();
|
|
41
|
+
expect(stats.hourly[key].input_tokens).toBe(100);
|
|
42
|
+
expect(stats.hourly[key].request_count).toBe(1);
|
|
43
|
+
// 模拟 cleanOldRequestsDB 把源行删掉
|
|
44
|
+
getDatabase().prepare('DELETE FROM requests').run();
|
|
45
|
+
aggregateStats();
|
|
46
|
+
stats = getStatsData();
|
|
47
|
+
expect(stats.hourly[key]).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
it('P2: closed_at 非空的中断行仍计入 aggregateStats 与 getTodayStats', () => {
|
|
50
|
+
const ts = new Date().toISOString();
|
|
51
|
+
const key = hourKeyOf(ts);
|
|
52
|
+
insertRequest({
|
|
53
|
+
timestamp: ts,
|
|
54
|
+
inputTokens: 100,
|
|
55
|
+
outputTokens: 50,
|
|
56
|
+
cacheReadTokens: 10,
|
|
57
|
+
cacheCreationTokens: 3,
|
|
58
|
+
closedAt: new Date().toISOString(),
|
|
59
|
+
model: 'claude-interrupted'
|
|
60
|
+
});
|
|
61
|
+
aggregateStats();
|
|
62
|
+
const entry = getStatsData().hourly[key];
|
|
63
|
+
expect(entry).toBeTruthy();
|
|
64
|
+
expect(entry.input_tokens).toBe(100);
|
|
65
|
+
expect(entry.output_tokens).toBe(50);
|
|
66
|
+
expect(entry.cache_read_tokens).toBe(10);
|
|
67
|
+
expect(entry.cache_creation_tokens).toBe(3);
|
|
68
|
+
expect(entry.request_count).toBe(1);
|
|
69
|
+
const since = new Date(Date.now() - 3600000).toISOString();
|
|
70
|
+
const today = getTodayStats(since);
|
|
71
|
+
expect(today.input_tokens).toBe(100);
|
|
72
|
+
expect(today.output_tokens).toBe(50);
|
|
73
|
+
expect(today.cache_read_tokens).toBe(10);
|
|
74
|
+
expect(today.request_count).toBe(1);
|
|
75
|
+
expect(today.models['claude-interrupted']).toBe(1);
|
|
76
|
+
});
|
|
77
|
+
it('P1: aggregateStats(1) 只保留 1 天窗口,无参仍为 30 天窗口', () => {
|
|
78
|
+
const nowIso = new Date().toISOString();
|
|
79
|
+
const oldIso = new Date(Date.now() - 2 * 86400000).toISOString();
|
|
80
|
+
const nowKey = hourKeyOf(nowIso);
|
|
81
|
+
const oldKey = hourKeyOf(oldIso);
|
|
82
|
+
insertRequest({ timestamp: nowIso, inputTokens: 1 });
|
|
83
|
+
insertRequest({ timestamp: oldIso, inputTokens: 2 });
|
|
84
|
+
aggregateStats(1);
|
|
85
|
+
let stats = getStatsData();
|
|
86
|
+
expect(stats.hourly[nowKey]).toBeTruthy();
|
|
87
|
+
expect(stats.hourly[oldKey]).toBeUndefined();
|
|
88
|
+
aggregateStats();
|
|
89
|
+
stats = getStatsData();
|
|
90
|
+
expect(stats.hourly[nowKey]).toBeTruthy();
|
|
91
|
+
expect(stats.hourly[oldKey]).toBeTruthy();
|
|
92
|
+
});
|
|
93
|
+
describe('P1b: aggregateStats 的 windowDays 分支(截断与回退)', () => {
|
|
94
|
+
// 31 天前落在 30 天窗口外、40 天窗口内:保留它意味着窗口被放宽到 40,
|
|
95
|
+
// 删除它才是「截断/回退为 30」;2 天前始终在窗口内。
|
|
96
|
+
it.each([
|
|
97
|
+
[40, '超过 30 截断为 30'],
|
|
98
|
+
[0, '0 回退默认 30'],
|
|
99
|
+
[-3, '负数回退默认 30'],
|
|
100
|
+
[1.5, '非整数回退默认 30']
|
|
101
|
+
])('maxDays = %s(%s):2 天前保留,31 天前删除', (maxDays, _desc) => {
|
|
102
|
+
const recentIso = new Date(Date.now() - 2 * 86400000).toISOString();
|
|
103
|
+
const oldIso = new Date(Date.now() - 31 * 86400000).toISOString();
|
|
104
|
+
const recentKey = hourKeyOf(recentIso);
|
|
105
|
+
const oldKey = hourKeyOf(oldIso);
|
|
106
|
+
insertRequest({ timestamp: recentIso, inputTokens: 2 });
|
|
107
|
+
insertRequest({ timestamp: oldIso, inputTokens: 31 });
|
|
108
|
+
aggregateStats(maxDays);
|
|
109
|
+
const stats = getStatsData();
|
|
110
|
+
expect(stats.hourly[recentKey]).toBeTruthy();
|
|
111
|
+
expect(stats.hourly[oldKey]).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import {
|
|
4
|
+
import { createVault } from '../vault.js';
|
|
5
5
|
async function makeTmpDocs() {
|
|
6
6
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kb-test-'));
|
|
7
7
|
// 根 index.json
|
|
@@ -22,10 +22,10 @@ async function makeTmpDocs() {
|
|
|
22
22
|
await fs.writeFile(path.join(dir, 'react-hooks', 'chunks', 'chunk-001.md'), '# 状态管理:useState\n\nuseState 用于组件状态。', 'utf8');
|
|
23
23
|
return dir;
|
|
24
24
|
}
|
|
25
|
-
describe('
|
|
25
|
+
describe('vault', () => {
|
|
26
26
|
it('listTopics 解析根 index.json', async () => {
|
|
27
27
|
const root = await makeTmpDocs();
|
|
28
|
-
const store =
|
|
28
|
+
const store = createVault(root);
|
|
29
29
|
const topics = await store.listTopics();
|
|
30
30
|
expect(topics).toEqual([
|
|
31
31
|
{ slug: 'react-hooks', title: 'React Hooks 参考', summary: 'React hooks 用法', tags: ['react', 'hooks'] },
|
|
@@ -33,12 +33,12 @@ describe('knowledge-store', () => {
|
|
|
33
33
|
});
|
|
34
34
|
it('listTopics 根 index.json 不存在返回空数组', async () => {
|
|
35
35
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kb-empty-'));
|
|
36
|
-
const store =
|
|
36
|
+
const store = createVault(dir);
|
|
37
37
|
expect(await store.listTopics()).toEqual([]);
|
|
38
38
|
});
|
|
39
39
|
it('getTopic 返回元数据 + 片段清单', async () => {
|
|
40
40
|
const root = await makeTmpDocs();
|
|
41
|
-
const store =
|
|
41
|
+
const store = createVault(root);
|
|
42
42
|
const detail = await store.getTopic('react-hooks');
|
|
43
43
|
expect(detail.title).toBe('React Hooks 参考');
|
|
44
44
|
expect(detail.tags).toEqual(['react', 'hooks']);
|
|
@@ -46,25 +46,25 @@ describe('knowledge-store', () => {
|
|
|
46
46
|
});
|
|
47
47
|
it('readChunk 返回单片正文', async () => {
|
|
48
48
|
const root = await makeTmpDocs();
|
|
49
|
-
const store =
|
|
49
|
+
const store = createVault(root);
|
|
50
50
|
const text = await store.readChunk('react-hooks', 'chunk-001');
|
|
51
51
|
expect(text).toContain('useState 用于组件状态');
|
|
52
52
|
});
|
|
53
53
|
it('readChunk 拒绝路径穿越', async () => {
|
|
54
54
|
const root = await makeTmpDocs();
|
|
55
|
-
const store =
|
|
55
|
+
const store = createVault(root);
|
|
56
56
|
await expect(store.readChunk('..', 'chunk-001')).rejects.toThrow();
|
|
57
57
|
await expect(store.readChunk('react-hooks', '../chunk-001')).rejects.toThrow();
|
|
58
58
|
});
|
|
59
59
|
it('searchChunks 关键词命中返回清单', async () => {
|
|
60
60
|
const root = await makeTmpDocs();
|
|
61
|
-
const store =
|
|
61
|
+
const store = createVault(root);
|
|
62
62
|
const hits = await store.searchChunks('useState');
|
|
63
63
|
expect(hits).toEqual([{ topic: 'react-hooks', chunkId: 'chunk-001', title: '状态管理:useState' }]);
|
|
64
64
|
});
|
|
65
65
|
it('upsertTopic 两次不同 slug → listTopics 返回 2 条,不覆盖', async () => {
|
|
66
66
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kb-upsert-'));
|
|
67
|
-
const store =
|
|
67
|
+
const store = createVault(dir);
|
|
68
68
|
await store.upsertTopic({ slug: 'topic-a', title: 'A', summary: 'sa', tags: [] });
|
|
69
69
|
await store.upsertTopic({ slug: 'topic-b', title: 'B', summary: 'sb', tags: ['x'] });
|
|
70
70
|
const topics = await store.listTopics();
|
|
@@ -73,7 +73,7 @@ describe('knowledge-store', () => {
|
|
|
73
73
|
});
|
|
74
74
|
it('upsertTopic 同 slug 再调更新 summary,chunks 数组不变,listTopics 仍 1 条', async () => {
|
|
75
75
|
const root = await makeTmpDocs();
|
|
76
|
-
const store =
|
|
76
|
+
const store = createVault(root);
|
|
77
77
|
// 先登记一个额外 chunk ref
|
|
78
78
|
await store.upsertChunkRef({ topic: 'react-hooks', chunkId: 'chunk-002', title: 'T2', summary: 'S2', tags: [] });
|
|
79
79
|
await store.upsertTopic({
|
|
@@ -92,7 +92,7 @@ describe('knowledge-store', () => {
|
|
|
92
92
|
});
|
|
93
93
|
it('upsertChunkRef 同 topic 两次不同 chunk_id → getTopic 返回 2 chunks', async () => {
|
|
94
94
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kb-chunk-'));
|
|
95
|
-
const store =
|
|
95
|
+
const store = createVault(dir);
|
|
96
96
|
await store.upsertTopic({ slug: 't1', title: 'T1', summary: 's', tags: [] });
|
|
97
97
|
await store.upsertChunkRef({ topic: 't1', chunkId: 'chunk-001', title: 'c1', summary: 's1', tags: [] });
|
|
98
98
|
await store.upsertChunkRef({ topic: 't1', chunkId: 'chunk-002', title: 'c2', summary: 's2', tags: [] });
|
|
@@ -102,12 +102,12 @@ describe('knowledge-store', () => {
|
|
|
102
102
|
});
|
|
103
103
|
it('upsertChunkRef topic 不存在 → rejects', async () => {
|
|
104
104
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kb-no-topic-'));
|
|
105
|
-
const store =
|
|
105
|
+
const store = createVault(dir);
|
|
106
106
|
await expect(store.upsertChunkRef({ topic: 'ghost', chunkId: 'chunk-001', title: 'c', summary: 's', tags: [] })).rejects.toThrow(/topic not found: ghost/);
|
|
107
107
|
});
|
|
108
108
|
it('tags: upsertChunkRef 落盘 tags,重复调用覆写 tags', async () => {
|
|
109
109
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kb-tags-'));
|
|
110
|
-
const store =
|
|
110
|
+
const store = createVault(dir);
|
|
111
111
|
await store.upsertTopic({ slug: 't1', title: 'T1', summary: 's', tags: [] });
|
|
112
112
|
await store.upsertChunkRef({ topic: 't1', chunkId: 'chunk-001', title: 'c1', summary: 's1', tags: ['redis', '缓存'] });
|
|
113
113
|
// 落盘验证
|
|
@@ -128,7 +128,7 @@ describe('knowledge-store', () => {
|
|
|
128
128
|
slug: 'old', title: 'Old', summary: 'so', tags: [],
|
|
129
129
|
chunks: [{ id: 'chunk-001', title: 'oc', summary: 'os' }],
|
|
130
130
|
}), 'utf8');
|
|
131
|
-
const store =
|
|
131
|
+
const store = createVault(dir);
|
|
132
132
|
const detail = await store.getTopic('old');
|
|
133
133
|
expect(detail.chunks[0].tags).toEqual([]);
|
|
134
134
|
});
|
|
@@ -144,7 +144,7 @@ describe('knowledge-store', () => {
|
|
|
144
144
|
}), 'utf8');
|
|
145
145
|
await fs.writeFile(path.join(dir, 'redis', 'chunks', 'chunk-001.md'), '# 缓存详解\n\n正文讲缓存。', 'utf8');
|
|
146
146
|
await fs.writeFile(path.join(dir, 'redis', 'chunks', 'chunk-002.md'), '# 故障转移\n\n正文讲哨兵。', 'utf8');
|
|
147
|
-
const store =
|
|
147
|
+
const store = createVault(dir);
|
|
148
148
|
// body
|
|
149
149
|
expect(await store.searchChunks('缓存')).toEqual([
|
|
150
150
|
{ topic: 'redis', chunkId: 'chunk-001', title: '缓存详解' },
|
|
@@ -177,7 +177,7 @@ describe('knowledge-store', () => {
|
|
|
177
177
|
chunks: [{ id: 'chunk-001', title: '缓存详解', summary: '缓存摘要' }],
|
|
178
178
|
}), 'utf8');
|
|
179
179
|
await fs.writeFile(path.join(dir, 'redis', 'chunks', 'chunk-001.md'), '# 缓存详解\n\n正文讲缓存。', 'utf8');
|
|
180
|
-
const store =
|
|
180
|
+
const store = createVault(dir);
|
|
181
181
|
expect(await store.searchChunks('缓存')).toEqual([
|
|
182
182
|
{ topic: 'redis', chunkId: 'chunk-001', title: '缓存详解' },
|
|
183
183
|
]);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* 监听 Node IncomingMessage,客户端在响应结束前中止连接时 abort 返回的 signal。
|
|
4
|
+
* close 时以 req.aborted 区分:true=客户端侧中止;正常响应结束后的 close 为 false。
|
|
5
|
+
*/
|
|
6
|
+
export declare function createClientAbortSignal(req: IncomingMessage): AbortSignal;
|
|
7
|
+
/** 合成:固定超时 + 外部取消,任意触发即 abort */
|
|
8
|
+
export declare function createTimeoutSignal(ms: number, external?: AbortSignal): AbortSignal;
|
|
9
|
+
/**
|
|
10
|
+
* 把 source signal 接到 abortFn:source 已 abort 立即调,否则将来 abort 时调一次。
|
|
11
|
+
* abortFn 异步拒绝不外泄(由调用方保证幂等)。
|
|
12
|
+
*/
|
|
13
|
+
export declare function bindAbort(source: AbortSignal, abortFn: () => Promise<void>): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 监听 Node IncomingMessage,客户端在响应结束前中止连接时 abort 返回的 signal。
|
|
3
|
+
* close 时以 req.aborted 区分:true=客户端侧中止;正常响应结束后的 close 为 false。
|
|
4
|
+
*/
|
|
5
|
+
export function createClientAbortSignal(req) {
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
// 传入时已 aborted(或已 close 过):立即 abort,不再依赖 close 事件
|
|
8
|
+
if (req.aborted) {
|
|
9
|
+
controller.abort();
|
|
10
|
+
return controller.signal;
|
|
11
|
+
}
|
|
12
|
+
req.on('close', () => {
|
|
13
|
+
if (req.aborted)
|
|
14
|
+
controller.abort();
|
|
15
|
+
});
|
|
16
|
+
return controller.signal;
|
|
17
|
+
}
|
|
18
|
+
/** 合成:固定超时 + 外部取消,任意触发即 abort */
|
|
19
|
+
export function createTimeoutSignal(ms, external) {
|
|
20
|
+
return AbortSignal.any([AbortSignal.timeout(ms), ...(external ? [external] : [])]);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 把 source signal 接到 abortFn:source 已 abort 立即调,否则将来 abort 时调一次。
|
|
24
|
+
* abortFn 异步拒绝不外泄(由调用方保证幂等)。
|
|
25
|
+
*/
|
|
26
|
+
export function bindAbort(source, abortFn) {
|
|
27
|
+
if (source.aborted) {
|
|
28
|
+
abortFn().catch(() => { });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
source.addEventListener('abort', () => abortFn().catch(() => { }), { once: true });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// 回收空闲页。auto_vacuum 不是 INCREMENTAL 时,pragma 修改只对新建库生效,
|
|
2
|
+
// 已有库必须先 VACUUM 全库重写才能让 pragma 生效;之后例行调用走
|
|
3
|
+
// incremental_vacuum,只还页不重写,代价低。
|
|
4
|
+
export function vacuumDatabase(db) {
|
|
5
|
+
const autoVacuumBefore = db.pragma('auto_vacuum', { simple: true }) ?? 0;
|
|
6
|
+
const pagesBefore = db.pragma('page_count', { simple: true });
|
|
7
|
+
const pageSize = db.pragma('page_size', { simple: true });
|
|
8
|
+
if (autoVacuumBefore !== 2) {
|
|
9
|
+
db.pragma('auto_vacuum = INCREMENTAL');
|
|
10
|
+
// 首次:全库重写,pragma 在重写后生效
|
|
11
|
+
db.exec('VACUUM');
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
db.pragma('incremental_vacuum');
|
|
15
|
+
}
|
|
16
|
+
const pagesAfter = db.pragma('page_count', { simple: true });
|
|
17
|
+
return { autoVacuumBefore, pagesBefore, pagesAfter, pageSize };
|
|
18
|
+
}
|
package/dist/utils/db.d.ts
CHANGED
|
@@ -10,5 +10,7 @@ export declare function applySchemaV7(db: Database.Database): void;
|
|
|
10
10
|
export declare function applySchemaV8(db: Database.Database): void;
|
|
11
11
|
export declare function applySchemaV9(db: Database.Database): void;
|
|
12
12
|
export declare function applySchemaV10(db: Database.Database): void;
|
|
13
|
+
export declare function applySchemaV11(db: Database.Database): void;
|
|
14
|
+
export declare function applySchemaV12(db: Database.Database): void;
|
|
13
15
|
export declare function closeDatabase(): void;
|
|
14
16
|
export declare function getDatabase(): Database.Database;
|
package/dist/utils/db.js
CHANGED
|
@@ -72,6 +72,14 @@ export function initDatabase() {
|
|
|
72
72
|
applySchemaV10(db);
|
|
73
73
|
db.prepare('INSERT OR REPLACE INTO schema_version (version) VALUES (10)').run();
|
|
74
74
|
}
|
|
75
|
+
if (currentVersion < 11) {
|
|
76
|
+
applySchemaV11(db);
|
|
77
|
+
db.prepare('INSERT OR REPLACE INTO schema_version (version) VALUES (11)').run();
|
|
78
|
+
}
|
|
79
|
+
if (currentVersion < 12) {
|
|
80
|
+
applySchemaV12(db);
|
|
81
|
+
db.prepare('INSERT OR REPLACE INTO schema_version (version) VALUES (12)').run();
|
|
82
|
+
}
|
|
75
83
|
return db;
|
|
76
84
|
}
|
|
77
85
|
export function applySchemaV2(db) {
|
|
@@ -280,6 +288,84 @@ export function applySchemaV10(db) {
|
|
|
280
288
|
WHERE user_agent IS NULL AND request_headers IS NOT NULL
|
|
281
289
|
`).run();
|
|
282
290
|
}
|
|
291
|
+
export function applySchemaV11(db) {
|
|
292
|
+
// learn 流水线状态机落库:文件操作前插 running,结束转 completed/failed/aborted;
|
|
293
|
+
// 服务启动时无主 running 回收为 crashed。partial unique index 硬守同 slug 并发运行。
|
|
294
|
+
db.exec(`
|
|
295
|
+
CREATE TABLE IF NOT EXISTS learn_tasks (
|
|
296
|
+
task_id TEXT PRIMARY KEY,
|
|
297
|
+
slug TEXT NOT NULL,
|
|
298
|
+
source_type TEXT NOT NULL,
|
|
299
|
+
label TEXT NOT NULL,
|
|
300
|
+
status TEXT NOT NULL,
|
|
301
|
+
error TEXT,
|
|
302
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
303
|
+
ended_at TEXT
|
|
304
|
+
)
|
|
305
|
+
`);
|
|
306
|
+
db.exec(`
|
|
307
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_learn_tasks_running_slug
|
|
308
|
+
ON learn_tasks(slug) WHERE status = 'running'
|
|
309
|
+
`);
|
|
310
|
+
}
|
|
311
|
+
export function applySchemaV12(db) {
|
|
312
|
+
// response_events 历史上存在 (request_id, session_id, event_index) 三元组重复行
|
|
313
|
+
// (request_id 计数器重启复用所致)和 requests 表无对应行的孤儿事件;清理后加
|
|
314
|
+
// 唯一索引硬守。生产库约 800 万行:先一次性把待删行号收集进临时表,再按主键
|
|
315
|
+
// 分批删除(每批 5000 行、独立事务),避免单个巨型事务长时间锁库。
|
|
316
|
+
//
|
|
317
|
+
// 注意:不能用 `GROUP BY ... HAVING COUNT(*) > 1 AND id != MIN(id)` 直接选行——
|
|
318
|
+
// SQLite 裸列 id 在该计划下取 MIN(id) 所在行,HAVING 恒假,实测删除 0 行。
|
|
319
|
+
const batchSize = 5000;
|
|
320
|
+
db.exec('DROP TABLE IF EXISTS temp.ids_to_purge');
|
|
321
|
+
db.exec('CREATE TEMP TABLE ids_to_purge (id INTEGER PRIMARY KEY)');
|
|
322
|
+
// a) 去重:每组 (request_id, session_id, event_index) 保留 MIN(id),其余入临时表
|
|
323
|
+
db.prepare(`
|
|
324
|
+
INSERT INTO ids_to_purge
|
|
325
|
+
SELECT id FROM response_events
|
|
326
|
+
WHERE id NOT IN (
|
|
327
|
+
SELECT MIN(id) FROM response_events
|
|
328
|
+
GROUP BY request_id, session_id, event_index
|
|
329
|
+
)
|
|
330
|
+
`).run();
|
|
331
|
+
purgeResponseEventsBatched(db, batchSize);
|
|
332
|
+
// b) 孤儿事件:requests 表中无对应 request_id
|
|
333
|
+
db.prepare(`
|
|
334
|
+
INSERT INTO ids_to_purge
|
|
335
|
+
SELECT re.id FROM response_events re
|
|
336
|
+
WHERE NOT EXISTS (
|
|
337
|
+
SELECT 1 FROM requests r WHERE r.request_id = re.request_id
|
|
338
|
+
)
|
|
339
|
+
`).run();
|
|
340
|
+
purgeResponseEventsBatched(db, batchSize);
|
|
341
|
+
db.exec('DROP TABLE IF EXISTS temp.ids_to_purge');
|
|
342
|
+
// c) 唯一索引(IF NOT EXISTS,幂等)
|
|
343
|
+
db.exec(`
|
|
344
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uq_response_events
|
|
345
|
+
ON response_events(request_id, session_id, event_index)
|
|
346
|
+
`);
|
|
347
|
+
}
|
|
348
|
+
// 按临时表 ids_to_purge 收集的行号分批删除 response_events;每批同时剪除临时表中
|
|
349
|
+
// 已处理的行号,整批包在一个事务里,循环直到没有可删行。
|
|
350
|
+
function purgeResponseEventsBatched(db, batchSize) {
|
|
351
|
+
const deleteEvents = db.prepare(`
|
|
352
|
+
DELETE FROM response_events
|
|
353
|
+
WHERE id IN (SELECT id FROM ids_to_purge ORDER BY id LIMIT ?)
|
|
354
|
+
`);
|
|
355
|
+
const pruneTemp = db.prepare(`
|
|
356
|
+
DELETE FROM ids_to_purge
|
|
357
|
+
WHERE id IN (SELECT id FROM ids_to_purge ORDER BY id LIMIT ?)
|
|
358
|
+
`);
|
|
359
|
+
const runBatch = db.transaction(() => {
|
|
360
|
+
const info = deleteEvents.run(batchSize);
|
|
361
|
+
pruneTemp.run(batchSize);
|
|
362
|
+
return info.changes;
|
|
363
|
+
});
|
|
364
|
+
while (true) {
|
|
365
|
+
if (runBatch() === 0)
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
283
369
|
export function closeDatabase() {
|
|
284
370
|
if (db) {
|
|
285
371
|
db.close();
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESM-compatible __dirname for this module.
|
|
3
|
-
*
|
|
4
|
-
* In ESM runtime, __dirname is undefined. This module computes its own
|
|
5
|
-
* directory path using import.meta.url, which can then be used to derive
|
|
6
|
-
* other paths relative to known locations.
|
|
7
|
-
*
|
|
8
|
-
* In CJS test environments (Jest), import.meta is not available, so this
|
|
9
|
-
* module should be mocked to return a suitable path.
|
|
10
|
-
*/
|
|
11
|
-
declare const thisDirname: string;
|
|
12
1
|
/**
|
|
13
2
|
* Get the directory of a source file relative to this utility's location.
|
|
14
3
|
*
|
|
@@ -22,10 +11,6 @@ declare const thisDirname: string;
|
|
|
22
11
|
* Example: getCallerDirname('../server') => packages/core/src/server
|
|
23
12
|
*/
|
|
24
13
|
export declare function getCallerDirname(relativePath: string): string;
|
|
25
|
-
/**
|
|
26
|
-
* Direct export of this module's __dirname for simple relative path derivation.
|
|
27
|
-
*/
|
|
28
|
-
export { thisDirname as utilsDirname };
|
|
29
14
|
/**
|
|
30
15
|
* ESM-compatible accessor for the entry module's URL.
|
|
31
16
|
*
|
|
@@ -34,10 +34,6 @@ const thisDirname = (() => {
|
|
|
34
34
|
export function getCallerDirname(relativePath) {
|
|
35
35
|
return path.resolve(thisDirname, relativePath);
|
|
36
36
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Direct export of this module's __dirname for simple relative path derivation.
|
|
39
|
-
*/
|
|
40
|
-
export { thisDirname as utilsDirname };
|
|
41
37
|
/**
|
|
42
38
|
* ESM-compatible accessor for the entry module's URL.
|
|
43
39
|
*
|
|
@@ -18,39 +18,13 @@ export interface ParsedUpstream {
|
|
|
18
18
|
stopReason: string;
|
|
19
19
|
usage: any;
|
|
20
20
|
}
|
|
21
|
-
/** 解析上游 SSE 全文 -> 事件数组(收集 data: 行的 JSON) */
|
|
22
|
-
export declare function parseSSEText(text: string): any[];
|
|
23
21
|
/** 从事件数组重建有序内容块 + stop_reason + usage */
|
|
24
22
|
export declare function parseUpstreamEvents(events: any[]): ParsedUpstream;
|
|
25
23
|
/** 找出命中的、已启用的注入 tool 调用块 */
|
|
26
24
|
export declare function findInjectedCall(parsed: ParsedUpstream): ParsedBlock | undefined;
|
|
27
|
-
export interface InjectLoopResult {
|
|
28
|
-
text: string;
|
|
29
|
-
iterations: number;
|
|
30
|
-
stoppedByLimit: boolean;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* SSE 内部循环:命中已启用 tool -> 执行 exec -> 追加对话 -> 再调上游 -> 直到 end_turn。
|
|
34
|
-
* opts.body 会被原地追加 messages。
|
|
35
|
-
*
|
|
36
|
-
* @param opts.events 首次上游响应事件(已确认命中注入 tool)
|
|
37
|
-
* @param opts.callUpstream 用给定 body 再次调上游,返回事件数组
|
|
38
|
-
* @param opts.maxIterations 死循环护栏(tool 描述可能诱导模型反复调用)
|
|
39
|
-
*/
|
|
40
|
-
export declare function runInjectLoop(opts: {
|
|
41
|
-
events: any[];
|
|
42
|
-
body: any;
|
|
43
|
-
callUpstream: (body: any) => Promise<any[]>;
|
|
44
|
-
maxIterations?: number;
|
|
45
|
-
}): Promise<InjectLoopResult>;
|
|
46
25
|
/**
|
|
47
26
|
* SSE 流式注入循环:命中已启用 tool → 执行 exec → 追加对话 → 再调上游 → 新 SSE 流式转发到同一 reply.raw。
|
|
48
27
|
* 每次迭代作为独立 DB 记录。客户端实时看到续接 SSE。
|
|
49
|
-
*
|
|
50
|
-
* 与 runInjectLoop 的区别:
|
|
51
|
-
* - 不缓冲上游响应,直接流式转发
|
|
52
|
-
* - 不返回合成 text,客户端已实时收到原始 SSE
|
|
53
|
-
* - 每次迭代用 SSEStreamProcessor 边转发边解析
|
|
54
28
|
*/
|
|
55
29
|
export interface StreamingInjectLoopResult {
|
|
56
30
|
iterations: number;
|
|
@@ -79,7 +53,6 @@ export interface JsonInjectLoopResult {
|
|
|
79
53
|
}
|
|
80
54
|
/**
|
|
81
55
|
* JSON 内部循环:非流式请求中命中 MCP tool -> 执行 exec -> 追加对话 -> 再调上游 -> 直到 end_turn。
|
|
82
|
-
* 与 SSE 版 runInjectLoop 对称,但处理 parsed JSON response body 而非 SSE event 数组。
|
|
83
56
|
*
|
|
84
57
|
* @param opts.initialBody 首次上游 JSON 响应(已确认命中 MCP tool)
|
|
85
58
|
* @param opts.requestBody 请求体(messages 会被原地追加)
|