@cc-monitoring/proxy-core 0.2.3 → 0.3.0
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/docs/404.html +1 -1
- package/dist/docs/assets/{app.6_CicOIJ.js → app.Dc46Hlqp.js} +1 -1
- package/dist/docs/assets/chunks/{@localSearchIndexroot.CHyuB7c8.js → @localSearchIndexroot.BN6hB8Jr.js} +1 -1
- package/dist/docs/assets/chunks/{VPLocalSearchBox.ZMWZsg8z.js → VPLocalSearchBox.Snq5jR6J.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BkvhsjzH.js → theme.XLtxGM-l.js} +2 -2
- package/dist/docs/index.html +2 -2
- package/dist/docs/overview.html +2 -2
- package/dist/docs/packages-utils.html +2 -2
- package/dist/docs/proxy-admin/env-and-build.html +2 -2
- package/dist/docs/proxy-admin/index.html +2 -2
- package/dist/docs/proxy-admin/layout-and-menu.html +2 -2
- package/dist/docs/proxy-admin/pages.html +2 -2
- package/dist/docs/proxy-admin/routing-and-auth.html +2 -2
- package/dist/docs/proxy-admin/services-and-mock.html +2 -2
- package/dist/docs/proxy-cli/api-layer.html +2 -2
- package/dist/docs/proxy-cli/app-shell.html +2 -2
- package/dist/docs/proxy-cli/components-common.html +2 -2
- package/dist/docs/proxy-cli/components-config.html +2 -2
- package/dist/docs/proxy-cli/components-requests.html +2 -2
- package/dist/docs/proxy-cli/components-stats.html +2 -2
- package/dist/docs/proxy-cli/components-status.html +2 -2
- package/dist/docs/proxy-cli/data-hooks.html +2 -2
- package/dist/docs/proxy-cli/index.html +2 -2
- package/dist/docs/proxy-cli/utils-and-i18n.html +2 -2
- package/dist/docs/proxy-core/chat-api.html +2 -2
- package/dist/docs/proxy-core/cli.html +2 -2
- package/dist/docs/proxy-core/config.html +2 -2
- package/dist/docs/proxy-core/data-layer.html +2 -2
- package/dist/docs/proxy-core/entry.html +2 -2
- package/dist/docs/proxy-core/index.html +2 -2
- package/dist/docs/proxy-core/pm2.html +2 -2
- package/dist/docs/proxy-core/providers.html +2 -2
- package/dist/docs/proxy-core/proxy.html +2 -2
- package/dist/docs/proxy-core/stats.html +2 -2
- package/dist/docs/proxy-core/tool.html +2 -2
- package/dist/docs/proxy-core/ui-routes.html +2 -2
- package/dist/docs/proxy-core/utilities.html +2 -2
- package/dist/docs/quickstart.html +2 -2
- package/dist/docs/request-flow.html +2 -2
- package/dist/server/__tests__/server.test.js +9 -1
- package/dist/server/__tests__/ui-route.test.js +8 -0
- package/dist/server/ui-route.js +113 -7
- package/dist/ui/assets/{1c-HTPXddJd.js → 1c-Bx3CHH-I.js} +1 -1
- package/dist/ui/assets/{abnf-bINSkl_m.js → abnf-NN4-iXDM.js} +1 -1
- package/dist/ui/assets/{abnfDiagram-VRR7QNED-ClDzCbvw.js → abnfDiagram-VRR7QNED-CXFZgDk7.js} +1 -1
- package/dist/ui/assets/{accesslog-BAnIWzpe.js → accesslog-Bq9x4vSZ.js} +1 -1
- package/dist/ui/assets/{actionscript-DVcm6F_C.js → actionscript-CeaB6gjT.js} +1 -1
- package/dist/ui/assets/{ada-Cxn-QX4j.js → ada-DObFDSC6.js} +1 -1
- package/dist/ui/assets/{angelscript-BAwAPvBQ.js → angelscript-4pIbuWPH.js} +1 -1
- package/dist/ui/assets/{apache-CjjovgUx.js → apache-w0LzP4me.js} +1 -1
- package/dist/ui/assets/{applescript-VUcyXoVh.js → applescript-DXpD6Cc9.js} +1 -1
- package/dist/ui/assets/{arc-3Nou_aku.js → arc-Dtt1H4m8.js} +1 -1
- package/dist/ui/assets/{arcade-Dz2VAk7O.js → arcade-D_zU9Djr.js} +1 -1
- package/dist/ui/assets/{architectureDiagram-ZJ3FMSHR-DhwSsCdN.js → architectureDiagram-ZJ3FMSHR-C0E_eQDM.js} +1 -1
- package/dist/ui/assets/{arduino-B-Ptb1K1.js → arduino-BnmN_xp_.js} +1 -1
- package/dist/ui/assets/{armasm-DU_6qZlW.js → armasm-CH_xJaHf.js} +1 -1
- package/dist/ui/assets/{asciidoc-DYbYiWKT.js → asciidoc-BlvZZ-hO.js} +1 -1
- package/dist/ui/assets/{aspectj-CmdNkqAs.js → aspectj-DP5DdFKQ.js} +1 -1
- package/dist/ui/assets/{autohotkey-if9QUvYo.js → autohotkey-Cy7dy_B1.js} +1 -1
- package/dist/ui/assets/{autoit-DGU3G1s6.js → autoit-BmQwjQdM.js} +1 -1
- package/dist/ui/assets/{avrasm-1zsEtPJ6.js → avrasm-fcPy4w1X.js} +1 -1
- package/dist/ui/assets/{awk-CvmeshSq.js → awk-AlcwGSdk.js} +1 -1
- package/dist/ui/assets/{axapta-CbJGb5cN.js → axapta-NyMN6Mvz.js} +1 -1
- package/dist/ui/assets/{bash-CFXg_i2e.js → bash-Bn3yyTUs.js} +1 -1
- package/dist/ui/assets/{basic-6atic5cT.js → basic-gp9eOjjv.js} +1 -1
- package/dist/ui/assets/{blockDiagram-677ZJIJ3-BKXinDkB.js → blockDiagram-677ZJIJ3-BHAadGGo.js} +1 -1
- package/dist/ui/assets/{bnf-DydAtPHV.js → bnf-DRAiK-_J.js} +1 -1
- package/dist/ui/assets/{brainfuck-CnHx1vO0.js → brainfuck-IYgPavhh.js} +1 -1
- package/dist/ui/assets/{coq-BShzwzh7.js → c-Dht0EgwX.js} +1 -1
- package/dist/ui/assets/{c-like-XpQV9S6j.js → c-like-B7T39dtK.js} +1 -1
- package/dist/ui/assets/{c4Diagram-LMCZKHZV-D5EmWl5d.js → c4Diagram-LMCZKHZV-4cXT8R9f.js} +1 -1
- package/dist/ui/assets/{cal-D-orUuBW.js → cal-DmXm_WLX.js} +1 -1
- package/dist/ui/assets/{capnproto-TdI6NT8O.js → capnproto-Cb-rKd-Z.js} +1 -1
- package/dist/ui/assets/{ceylon-Dji8eqtG.js → ceylon-Bn2aNwEq.js} +1 -1
- package/dist/ui/assets/channel-BgAIihW-.js +1 -0
- package/dist/ui/assets/{chunk-2Q5K7J3B-CaIvwVWl.js → chunk-2Q5K7J3B-B3N_AddF.js} +1 -1
- package/dist/ui/assets/{chunk-32BRIVSS-C7oMpoeu.js → chunk-32BRIVSS-CW7iU3gE.js} +1 -1
- package/dist/ui/assets/{chunk-5VM5RSS4-BQ0aVGl-.js → chunk-5VM5RSS4-M8Ep3dJD.js} +1 -1
- package/dist/ui/assets/{chunk-EX3LRPZG-D-_EqojZ.js → chunk-EX3LRPZG-t2hCXH-v.js} +1 -1
- package/dist/ui/assets/{chunk-JWPE2WC7-B0OLx2rk.js → chunk-JWPE2WC7-rQK8maXo.js} +1 -1
- package/dist/ui/assets/{chunk-MOJQB5TN-Be8IXTUE.js → chunk-MOJQB5TN-BpU7vp3L.js} +1 -1
- package/dist/ui/assets/{chunk-RYQCIY6F-DceCW2mS.js → chunk-RYQCIY6F-CJVNOulR.js} +1 -1
- package/dist/ui/assets/{chunk-V7JOEXUC-Cbhq5qgO.js → chunk-V7JOEXUC-B--maww3.js} +1 -1
- package/dist/ui/assets/{chunk-VR4S4FIN-D6WJuaS1.js → chunk-VR4S4FIN-QJ5u9nSh.js} +1 -1
- package/dist/ui/assets/{chunk-XXDRQBXY-CKrih4pC.js → chunk-XXDRQBXY-BcM9JOXa.js} +1 -1
- package/dist/ui/assets/classDiagram-OUVF2IWQ-0fsnUoNI.js +1 -0
- package/dist/ui/assets/classDiagram-v2-EOCWNBFH-0fsnUoNI.js +1 -0
- package/dist/ui/assets/{clean-0frlVN_A.js → clean-BCfhFoON.js} +1 -1
- package/dist/ui/assets/{clojure-D0q_yQDf.js → clojure-DtnBj8eB.js} +1 -1
- package/dist/ui/assets/{clojure-repl-DgYhEzOe.js → clojure-repl-D8X-urCW.js} +1 -1
- package/dist/ui/assets/{cmake-WF5tF5xU.js → cmake-BH2RXBgt.js} +1 -1
- package/dist/ui/assets/{coffeescript-CyWYgeyv.js → coffeescript-CuoLiQ_d.js} +1 -1
- package/dist/ui/assets/{c-DarYnPBd.js → coq-BYVsAo31.js} +1 -1
- package/dist/ui/assets/{cos-C5i3_TIB.js → cos-t-cDfrf6.js} +1 -1
- package/dist/ui/assets/{cose-bilkent-JH36ORCC-BCwilS6I.js → cose-bilkent-JH36ORCC-CYip_9u7.js} +1 -1
- package/dist/ui/assets/{cpp-F1mRQFyw.js → cpp-Cgv0dQ7g.js} +1 -1
- package/dist/ui/assets/{crmsh-BAkHUu-B.js → crmsh-7pr1Pq_e.js} +1 -1
- package/dist/ui/assets/{crystal-CwUaomfj.js → crystal-BHZFifeV.js} +1 -1
- package/dist/ui/assets/{csharp-B1gHIdjK.js → csharp-CVMfBRGF.js} +1 -1
- package/dist/ui/assets/{csp-CblCTMvV.js → csp-NEBRDuZY.js} +1 -1
- package/dist/ui/assets/{css-CXg7kJV3.js → css-B0fbMkoQ.js} +1 -1
- package/dist/ui/assets/{cynefin-VYW2F7L2-BdgMlR5e.js → cynefin-VYW2F7L2-CycR2u2l.js} +1 -1
- package/dist/ui/assets/{cynefinDiagram-TSTJHNR4-Bozdux8p.js → cynefinDiagram-TSTJHNR4-CvXYb5em.js} +1 -1
- package/dist/ui/assets/{d-Cs5yNCaM.js → d-AzA6F7Ab.js} +1 -1
- package/dist/ui/assets/{dagre-VKFMJZFB-tI_mjNMn.js → dagre-VKFMJZFB-6NuSYMRG.js} +1 -1
- package/dist/ui/assets/{dart-BqgylVkh.js → dart-D0VXSwKi.js} +1 -1
- package/dist/ui/assets/{delphi-CbnfHpIi.js → delphi-B-bkLmCx.js} +1 -1
- package/dist/ui/assets/{diagram-FQU43EPY-DmyL3OJT.js → diagram-FQU43EPY-CNNW_5O7.js} +1 -1
- package/dist/ui/assets/{diagram-G47NLZAW-BEi9GZlZ.js → diagram-G47NLZAW-C60bRph-.js} +1 -1
- package/dist/ui/assets/{diagram-NH7WQ7WH-Bct44Oh1.js → diagram-NH7WQ7WH-CwERNWac.js} +1 -1
- package/dist/ui/assets/{diagram-OA4YK3LP-VWAgZxPK.js → diagram-OA4YK3LP-9XSTmf1Z.js} +1 -1
- package/dist/ui/assets/{diagram-WEI45ONY-CxcHsE6q.js → diagram-WEI45ONY-h8K41_YA.js} +1 -1
- package/dist/ui/assets/{diff-CMJ2hGQW.js → diff-Ch9RlEn8.js} +1 -1
- package/dist/ui/assets/{django-Cq_XIhVZ.js → django-C00rhzcB.js} +1 -1
- package/dist/ui/assets/{dns-CJs3z6-E.js → dns-C5MKVde0.js} +1 -1
- package/dist/ui/assets/{dockerfile-ehs0ogry.js → dockerfile-BpRaVDMB.js} +1 -1
- package/dist/ui/assets/{dos-DXOYESf1.js → dos-CjQOJ_vn.js} +1 -1
- package/dist/ui/assets/{dsconfig-Cs2hs3Bl.js → dsconfig-3zd3UkyN.js} +1 -1
- package/dist/ui/assets/{dts-Dk8yoweb.js → dts-Bb6PtK5T.js} +1 -1
- package/dist/ui/assets/{dust-BMK9e-Ss.js → dust-BEJKezIa.js} +1 -1
- package/dist/ui/assets/{ebnf-DlyBI5m6.js → ebnf-5-TvfsoC.js} +1 -1
- package/dist/ui/assets/{ebnfDiagram-CCIWWBDH-DBp9JHEq.js → ebnfDiagram-CCIWWBDH-CRmIjeas.js} +1 -1
- package/dist/ui/assets/{elixir-BThlsblT.js → elixir-B6gSEyen.js} +1 -1
- package/dist/ui/assets/{elm-DrmZwmcc.js → elm-rR0aqyyf.js} +1 -1
- package/dist/ui/assets/{erDiagram-Q63AITRT-BhK4NKvx.js → erDiagram-Q63AITRT-DS3jYOq8.js} +1 -1
- package/dist/ui/assets/{erb-BQM7j3K9.js → erb-DUu22Jxg.js} +1 -1
- package/dist/ui/assets/{erlang-CuUaWEXv.js → erlang-C0m-R-6W.js} +1 -1
- package/dist/ui/assets/{erlang-repl-DB0uphao.js → erlang-repl-B7O2MhlI.js} +1 -1
- package/dist/ui/assets/{excel-DmsuWUss.js → excel-CIqnqFs1.js} +1 -1
- package/dist/ui/assets/{fix-D-Uzxpz_.js → fix-Ds9tcTzQ.js} +1 -1
- package/dist/ui/assets/{flix-B_7EHWiA.js → flix-ByzuevTB.js} +1 -1
- package/dist/ui/assets/{flowDiagram-23GEKE2U-DtMG1G-h.js → flowDiagram-23GEKE2U-B9F3S_Hl.js} +1 -1
- package/dist/ui/assets/{fortran-Djp5tH9W.js → fortran-DieSVGx5.js} +1 -1
- package/dist/ui/assets/{fsharp-Pj_2ZEa4.js → fsharp-DGP8m6H3.js} +1 -1
- package/dist/ui/assets/{gams-B2i3v2ec.js → gams-BiLGBohb.js} +1 -1
- package/dist/ui/assets/{ganttDiagram-NO4QXBWP-C7M-JA0W.js → ganttDiagram-NO4QXBWP-CHiCWnJt.js} +1 -1
- package/dist/ui/assets/{gauss-DaV8sHLU.js → gauss-Btl1fjJ0.js} +1 -1
- package/dist/ui/assets/{gcode-0Jvrh6HY.js → gcode-DWl2YoL_.js} +1 -1
- package/dist/ui/assets/{gherkin-DoOl28D6.js → gherkin-D42ESIJ2.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-IHSO6WYX-D3HB5mow.js → gitGraphDiagram-IHSO6WYX-0pEKZnJg.js} +1 -1
- package/dist/ui/assets/{glsl-DEbk7rH5.js → glsl-Dx1QhGIM.js} +1 -1
- package/dist/ui/assets/{gml-CBcqXBNV.js → gml-f0noEVTB.js} +1 -1
- package/dist/ui/assets/{go-DqesmqF8.js → go-DXT6c-0g.js} +1 -1
- package/dist/ui/assets/{golo-9U1oO0b5.js → golo-D3NuJxTg.js} +1 -1
- package/dist/ui/assets/{gradle-DZliBXTY.js → gradle-CUsTLc_e.js} +1 -1
- package/dist/ui/assets/{groovy-Dys3GQwv.js → groovy-BIkOcIpQ.js} +1 -1
- package/dist/ui/assets/{haml-CrtAODl1.js → haml-C5jI7YVN.js} +1 -1
- package/dist/ui/assets/{handlebars-CueXuqSj.js → handlebars-UOvbWwDx.js} +1 -1
- package/dist/ui/assets/{haskell-CRungOQt.js → haskell-DuO_Av2n.js} +1 -1
- package/dist/ui/assets/{haxe-aMz7L4gD.js → haxe-C0Cg5Hjh.js} +1 -1
- package/dist/ui/assets/{hsp-Cw75yqGb.js → hsp-CTDajZig.js} +1 -1
- package/dist/ui/assets/{htmlbars-e_mpUZNH.js → htmlbars-_ITuCkOs.js} +1 -1
- package/dist/ui/assets/{http-Di7E_m1o.js → http-Dr26PHfb.js} +1 -1
- package/dist/ui/assets/{hy-CwlvsNj6.js → hy-B-ThgV4T.js} +1 -1
- package/dist/ui/assets/index-C0fxlU9d.css +10 -0
- package/dist/ui/assets/{index-BdSumlU0.js → index-CpQr158p.js} +3 -3
- package/dist/ui/assets/{index-BVM6YOjH.js → index-CwcnuNUs.js} +256 -256
- package/dist/ui/assets/{infoDiagram-FWYZ7A6U-_r-6eAzx.js → infoDiagram-FWYZ7A6U-ClKPOdlx.js} +1 -1
- package/dist/ui/assets/{inform7-dwg1imm1.js → inform7-D7JYYkX9.js} +1 -1
- package/dist/ui/assets/{ini-DEXL1rCA.js → ini-BZwCkFAf.js} +1 -1
- package/dist/ui/assets/{irpf90-Ccs_8Sp-.js → irpf90-omMve0rd.js} +1 -1
- package/dist/ui/assets/{isbl-B9d1nYmL.js → isbl-C4xosfum.js} +1 -1
- package/dist/ui/assets/{ishikawaDiagram-FXEZZL3T-lNCezpU4.js → ishikawaDiagram-FXEZZL3T-PtbNBMgh.js} +1 -1
- package/dist/ui/assets/{java-5fv2smM6.js → java-CWjWDe7Q.js} +1 -1
- package/dist/ui/assets/{javascript-TAWm0NK8.js → javascript-C3dxrGuA.js} +1 -1
- package/dist/ui/assets/{jboss-cli-dM1kcsbR.js → jboss-cli-CJE7m0Y-.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-5HDEW3XC-CBYHux8i.js → journeyDiagram-5HDEW3XC-DgK9V4yg.js} +1 -1
- package/dist/ui/assets/{json-CtCzm6jF.js → json-DFUiNV7v.js} +1 -1
- package/dist/ui/assets/{julia-Co7H3kzk.js → julia-CUTHpx8X.js} +1 -1
- package/dist/ui/assets/{julia-repl-BT1ar5VS.js → julia-repl-_ZaQqdrz.js} +1 -1
- package/dist/ui/assets/{kanban-definition-HUTT4EX6-B8x8eQyo.js → kanban-definition-HUTT4EX6-BFrDfKdv.js} +1 -1
- package/dist/ui/assets/{kotlin-W6mVKIj9.js → kotlin-CrYjQYp8.js} +1 -1
- package/dist/ui/assets/{lasso-DJG4luGz.js → lasso-CAiVg9m2.js} +1 -1
- package/dist/ui/assets/{latex-DbBOxO7u.js → latex-BkxnXIrT.js} +1 -1
- package/dist/ui/assets/{ldif-CVS189JA.js → ldif-CBOqxiN7.js} +1 -1
- package/dist/ui/assets/{leaf-eYNiXTKP.js → leaf-CbBZyOGH.js} +1 -1
- package/dist/ui/assets/{less-DxhvWM95.js → less-Ca6uLPP0.js} +1 -1
- package/dist/ui/assets/{lisp-SC7AwnXC.js → lisp-0Cm7_Bzv.js} +1 -1
- package/dist/ui/assets/{livecodeserver-CR2eHihF.js → livecodeserver-B7ka0vFT.js} +1 -1
- package/dist/ui/assets/{livescript-DBZ-GaVK.js → livescript-CGmWmcLd.js} +1 -1
- package/dist/ui/assets/{llvm-BzURO4tt.js → llvm-DCVMvkrH.js} +1 -1
- package/dist/ui/assets/{lsl-DGoJxEUu.js → lsl-PVrTScpn.js} +1 -1
- package/dist/ui/assets/{lua-qgG9gdiw.js → lua-BTo3z2Ux.js} +1 -1
- package/dist/ui/assets/{makefile-D0kZ6SOe.js → makefile-DJGh9r9C.js} +1 -1
- package/dist/ui/assets/{markdown-DRQWgMxk.js → markdown-BOloL-q7.js} +1 -1
- package/dist/ui/assets/{mathematica-DIBvZyEZ.js → mathematica-CtMMQM2V.js} +1 -1
- package/dist/ui/assets/{matlab-BNg0BR0b.js → matlab-Bfg_AWdR.js} +1 -1
- package/dist/ui/assets/{maxima-Bpga3G1G.js → maxima-B2cdS7BY.js} +1 -1
- package/dist/ui/assets/{mel-CMOJa9Mg.js → mel-BvfPvpYI.js} +1 -1
- package/dist/ui/assets/{mercury-uvrRzupw.js → mercury-B6KgNIai.js} +1 -1
- package/dist/ui/assets/{mindmap-definition-LN4V7U3C-DK8j_8Ap.js → mindmap-definition-LN4V7U3C-BkeXo5If.js} +1 -1
- package/dist/ui/assets/{mipsasm-p1gWpDuw.js → mipsasm-Csras-cD.js} +1 -1
- package/dist/ui/assets/{mizar-DV1ENOOU.js → mizar-C3WDI1-X.js} +1 -1
- package/dist/ui/assets/{mojolicious-tg5XQSro.js → mojolicious-MnD2Whic.js} +1 -1
- package/dist/ui/assets/{monkey-D8mYa31Q.js → monkey-DLvUPYTI.js} +1 -1
- package/dist/ui/assets/{moonscript-1d_rtlyv.js → moonscript-B0XEb3R3.js} +1 -1
- package/dist/ui/assets/{n1ql-C5srRxme.js → n1ql-BMRF54sY.js} +1 -1
- package/dist/ui/assets/{nginx-xS1muaWm.js → nginx-DqUyeHb8.js} +1 -1
- package/dist/ui/assets/{nim-CGGiMQfp.js → nim-3xwg8mOL.js} +1 -1
- package/dist/ui/assets/{nix-C77HsR7m.js → nix-ByTkLdcH.js} +1 -1
- package/dist/ui/assets/{node-repl-CSeYusSM.js → node-repl-sGSuIUFG.js} +1 -1
- package/dist/ui/assets/{nsis-BffC7mSW.js → nsis-Dng5mFSM.js} +1 -1
- package/dist/ui/assets/{objectivec-WpjnmlJB.js → objectivec-DSQ8vb7n.js} +1 -1
- package/dist/ui/assets/{ocaml-CHUkJkjB.js → ocaml-BHOhEgRu.js} +1 -1
- package/dist/ui/assets/{openscad-BvImqFT0.js → openscad-DcUxQ-FQ.js} +1 -1
- package/dist/ui/assets/{oxygene-BMBykbf5.js → oxygene-DNlmqhsR.js} +1 -1
- package/dist/ui/assets/{parser3-lRbuZW0M.js → parser3-DIR98Q6n.js} +1 -1
- package/dist/ui/assets/{pegDiagram-2B236MQR-C4DWmrja.js → pegDiagram-2B236MQR-BrXp_1_9.js} +1 -1
- package/dist/ui/assets/{perl-B0DjfKoO.js → perl-16cY7fyu.js} +1 -1
- package/dist/ui/assets/{pf-Aor6NyYJ.js → pf-KrAq2BW7.js} +1 -1
- package/dist/ui/assets/{pgsql-CVKcjjNR.js → pgsql-BV1Z2bjp.js} +1 -1
- package/dist/ui/assets/{php-CZ7geHZt.js → php-FGjYSZqs.js} +1 -1
- package/dist/ui/assets/{php-template-DiFuW2dF.js → php-template-B1OW86WT.js} +1 -1
- package/dist/ui/assets/{pieDiagram-ENE6RG2P-BO7AmF5j.js → pieDiagram-ENE6RG2P-Bh0bhWSa.js} +1 -1
- package/dist/ui/assets/{plaintext-DcvmOuNc.js → plaintext-Cvn_UgSv.js} +1 -1
- package/dist/ui/assets/{pony-DGPeAcOc.js → pony-CRWnA3MR.js} +1 -1
- package/dist/ui/assets/{powershell-DsrPyJdK.js → powershell-Cwn155MZ.js} +1 -1
- package/dist/ui/assets/{processing-Co2VRXN1.js → processing-BS6-1lrQ.js} +1 -1
- package/dist/ui/assets/{profile-D4PjpLUf.js → profile-C45OLQrv.js} +1 -1
- package/dist/ui/assets/{prolog-DEbkeg9r.js → prolog-C5YafSzY.js} +1 -1
- package/dist/ui/assets/{properties-Cxqcuj1s.js → properties-BULd-Zhg.js} +1 -1
- package/dist/ui/assets/{protobuf-C-zdlrww.js → protobuf-Qf1IuG5l.js} +1 -1
- package/dist/ui/assets/{puppet-BPX3SrOx.js → puppet-93XdQvnn.js} +1 -1
- package/dist/ui/assets/{purebasic-BdQnNJS3.js → purebasic-DlYNScQP.js} +1 -1
- package/dist/ui/assets/{python-D067_Gj8.js → python-D-Do6-F0.js} +1 -1
- package/dist/ui/assets/{python-repl-DCqNVlc0.js → python-repl-DGutP04w.js} +1 -1
- package/dist/ui/assets/{q-kABXYW-8.js → q-lmgfrySy.js} +1 -1
- package/dist/ui/assets/{qml-Iao-Whg7.js → qml-BbjX_mSU.js} +1 -1
- package/dist/ui/assets/{quadrantDiagram-ABIIQ3AL-DetQn-0l.js → quadrantDiagram-ABIIQ3AL-B891FOO8.js} +1 -1
- package/dist/ui/assets/{r-BnwpapnO.js → r--x8NzO9N.js} +1 -1
- package/dist/ui/assets/{railroadDiagram-RFXS5EU6-DFaFKHAH.js → railroadDiagram-RFXS5EU6-R3O-GKNe.js} +1 -1
- package/dist/ui/assets/{reasonml-Cn_Y03uT.js → reasonml-DwHsAflh.js} +1 -1
- package/dist/ui/assets/{requirementDiagram-TGXJPOKE-Dg1LrT-T.js → requirementDiagram-TGXJPOKE-D4l_xEXt.js} +1 -1
- package/dist/ui/assets/{rib-ByAqQn0v.js → rib-DMJNtDwV.js} +1 -1
- package/dist/ui/assets/{roboconf-BJPu6NC5.js → roboconf-BNNGTvHr.js} +1 -1
- package/dist/ui/assets/{routeros-CL3Jm0Zq.js → routeros-BF7SVruD.js} +1 -1
- package/dist/ui/assets/{rsl-DaXy7KaD.js → rsl-1EHeIZqp.js} +1 -1
- package/dist/ui/assets/{ruby-DI6awte_.js → ruby-B-v_BYYp.js} +1 -1
- package/dist/ui/assets/{ruleslanguage-DoTVEZE9.js → ruleslanguage-GFeEMSdK.js} +1 -1
- package/dist/ui/assets/{rust-ULEd75wO.js → rust-ApqGtMhh.js} +1 -1
- package/dist/ui/assets/{sankeyDiagram-HTMAVEWB-DQtFl110.js → sankeyDiagram-HTMAVEWB-Dc-2lhBq.js} +1 -1
- package/dist/ui/assets/{sas-f7ATFuJk.js → sas-YkxX6N9_.js} +1 -1
- package/dist/ui/assets/{scala-Dha1zro2.js → scala-BpHi86GZ.js} +1 -1
- package/dist/ui/assets/{scheme-BcaFHKeF.js → scheme-D889qY8Y.js} +1 -1
- package/dist/ui/assets/{scilab-CIiiyD4z.js → scilab-C3l-bcRx.js} +1 -1
- package/dist/ui/assets/{scss-CkBomL0n.js → scss-D0iGASxm.js} +1 -1
- package/dist/ui/assets/{sequenceDiagram-DBY2YBRQ-DIbNQtCn.js → sequenceDiagram-DBY2YBRQ-DvppgciC.js} +1 -1
- package/dist/ui/assets/{shell-Cxu8Py1z.js → shell-PUnaEPCO.js} +1 -1
- package/dist/ui/assets/{sizeCapture-X5ZJPWSS-Cj7PglQv.js → sizeCapture-X5ZJPWSS-DhvIbxXs.js} +1 -1
- package/dist/ui/assets/{smali-C4EYyWYz.js → smali-Du_dF9hl.js} +1 -1
- package/dist/ui/assets/{smalltalk-BlPTprP4.js → smalltalk-CSr2Gc1v.js} +1 -1
- package/dist/ui/assets/{sml-DEPzhlr4.js → sml-D3zkvzP2.js} +1 -1
- package/dist/ui/assets/{sqf-CRu3asu9.js → sqf-BZQ34qEZ.js} +1 -1
- package/dist/ui/assets/{sql-DK-YfQqg.js → sql-BFKScMsf.js} +1 -1
- package/dist/ui/assets/{sql_more-BuMEOcjj.js → sql_more-C2Lc_Qqq.js} +1 -1
- package/dist/ui/assets/{stan-DMr13kyD.js → stan-CzY8VdxU.js} +1 -1
- package/dist/ui/assets/{stata-Bt6E2zQi.js → stata-9GxyhPpV.js} +1 -1
- package/dist/ui/assets/{stateDiagram-2N3HPSRC-Bk-gF72D.js → stateDiagram-2N3HPSRC-CM63y6f0.js} +1 -1
- package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-OA9dcuA9.js +1 -0
- package/dist/ui/assets/{step21-Bi954ur4.js → step21-BpK_Zw3v.js} +1 -1
- package/dist/ui/assets/{stylus-CQwwMaLm.js → stylus-DQ4k28M4.js} +1 -1
- package/dist/ui/assets/{subunit-BkixDEMH.js → subunit-C6ciOPNF.js} +1 -1
- package/dist/ui/assets/{swift-DS1DWNAE.js → swift-CJfqqSbT.js} +1 -1
- package/dist/ui/assets/{swimlanes-5IMT3BWC-C-AZKO5z.js → swimlanes-5IMT3BWC-DS6_HNF7.js} +2 -2
- package/dist/ui/assets/swimlanesDiagram-G3AALYLV-BLEnJU_R.js +8 -0
- package/dist/ui/assets/{taggerscript-DZHi1hoc.js → taggerscript-VouMmJ_v.js} +1 -1
- package/dist/ui/assets/{tap-CXxSD8bM.js → tap-wXCy18ot.js} +1 -1
- package/dist/ui/assets/{tcl-CdoAAMbn.js → tcl-ewDOmCFX.js} +1 -1
- package/dist/ui/assets/{thrift-DM086HVt.js → thrift-BfFA03rE.js} +1 -1
- package/dist/ui/assets/{timeline-definition-FHXFAJF6-DcrcjFwq.js → timeline-definition-FHXFAJF6-Dz2m6S6x.js} +1 -1
- package/dist/ui/assets/{tp-Y-w3S3ES.js → tp-HGBcpNf9.js} +1 -1
- package/dist/ui/assets/{twig-C5c_nAus.js → twig-nrQZ0pw8.js} +1 -1
- package/dist/ui/assets/{typescript-fc7SMhJu.js → typescript-B7tNYFEQ.js} +1 -1
- package/dist/ui/assets/{vala-BV-GlwLI.js → vala-BIjQtoo6.js} +1 -1
- package/dist/ui/assets/{vbnet-BIqQmnKr.js → vbnet-Bbspccm6.js} +1 -1
- package/dist/ui/assets/{vbscript-h186Lnrz.js → vbscript-C45_QgOy.js} +1 -1
- package/dist/ui/assets/{vbscript-html-DcuP7lTb.js → vbscript-html-DlF2QBc7.js} +1 -1
- package/dist/ui/assets/{vennDiagram-L72KCM5P-Dnh11y2e.js → vennDiagram-L72KCM5P-BPEphYtX.js} +1 -1
- package/dist/ui/assets/{verilog-Cbgb_XnR.js → verilog-BSitd8W0.js} +1 -1
- package/dist/ui/assets/{vhdl-COP7w5IC.js → vhdl-C1bbeiC4.js} +1 -1
- package/dist/ui/assets/{vim-COGwKzgS.js → vim-DchwbT8X.js} +1 -1
- package/dist/ui/assets/{wardleyDiagram-EHGQE667-BeiD8fPz.js → wardleyDiagram-EHGQE667-BttTCoBT.js} +1 -1
- package/dist/ui/assets/{x86asm-BdBqPPf4.js → x86asm-vARxMUvn.js} +1 -1
- package/dist/ui/assets/{xl-BmCQT7Vj.js → xl-dx63vvXo.js} +1 -1
- package/dist/ui/assets/{xml-DM0NxGiJ.js → xml-BUnj27wL.js} +1 -1
- package/dist/ui/assets/{xquery-BUaYHDJM.js → xquery-T7gpnf0e.js} +1 -1
- package/dist/ui/assets/{xychartDiagram-FW5EYKEG-CnwvTrkm.js → xychartDiagram-FW5EYKEG-BfrNhdrG.js} +1 -1
- package/dist/ui/assets/{yaml-DMs8lGDt.js → yaml-xRgQKzeL.js} +1 -1
- package/dist/ui/assets/{zephir-BvjoPdmg.js → zephir-Yu6l5qJj.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/dist/utils/__tests__/knowledge-store.test.js +86 -5
- package/dist/utils/__tests__/pi-browser-tools.test.d.ts +1 -0
- package/dist/utils/__tests__/pi-browser-tools.test.js +30 -0
- package/dist/utils/__tests__/pi-crawl-tools.test.d.ts +1 -0
- package/dist/utils/__tests__/pi-crawl-tools.test.js +315 -0
- package/dist/utils/__tests__/pi-dedup-reconcile.test.d.ts +1 -0
- package/dist/utils/__tests__/pi-dedup-reconcile.test.js +89 -0
- package/dist/utils/__tests__/pi-dedup.test.d.ts +1 -0
- package/dist/utils/__tests__/pi-dedup.test.js +106 -0
- package/dist/utils/__tests__/pi-learn-orchestrator.test.d.ts +1 -0
- package/dist/utils/__tests__/pi-learn-orchestrator.test.js +885 -0
- package/dist/utils/__tests__/pi-runtime-json.test.d.ts +1 -0
- package/dist/utils/__tests__/pi-runtime-json.test.js +11 -0
- package/dist/utils/__tests__/web-crawler.test.d.ts +1 -0
- package/dist/utils/__tests__/web-crawler.test.js +69 -0
- package/dist/utils/knowledge-store.d.ts +2 -0
- package/dist/utils/knowledge-store.js +32 -6
- package/dist/utils/pi-browser-tools.d.ts +2 -0
- package/dist/utils/pi-browser-tools.js +9 -2
- package/dist/utils/pi-crawl-tools.d.ts +62 -0
- package/dist/utils/pi-crawl-tools.js +298 -0
- package/dist/utils/pi-dedup.d.ts +62 -0
- package/dist/utils/pi-dedup.js +218 -0
- package/dist/utils/pi-learn-orchestrator.d.ts +44 -0
- package/dist/utils/pi-learn-orchestrator.js +431 -0
- package/dist/utils/pi-runtime.d.ts +23 -1
- package/dist/utils/pi-runtime.js +255 -68
- package/dist/utils/web-crawler.d.ts +13 -0
- package/dist/utils/web-crawler.js +64 -0
- package/package.json +1 -1
- package/dist/ui/assets/channel-BdcV3Ykv.js +0 -1
- package/dist/ui/assets/classDiagram-OUVF2IWQ-CAZR5rAv.js +0 -1
- package/dist/ui/assets/classDiagram-v2-EOCWNBFH-CAZR5rAv.js +0 -1
- package/dist/ui/assets/index-WPLwx8h5.css +0 -10
- package/dist/ui/assets/stateDiagram-v2-6OUMAXLB-BFJCYsLY.js +0 -1
- package/dist/ui/assets/swimlanesDiagram-G3AALYLV-BZd1Gt4_.js +0 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// pi-coding-agent 为 ESM-only(无 require 导出),jest CJS 无法加载;
|
|
2
|
+
// mock 掉以加载真实 pi-runtime 模块测试纯函数(同 pi-browser-tools.test.ts 的 typebox mock 做法)
|
|
3
|
+
jest.mock('@earendil-works/pi-coding-agent', () => ({ createWriteToolDefinition: () => ({}) }), { virtual: true });
|
|
4
|
+
import { extractJsonObject } from '../pi-runtime.js';
|
|
5
|
+
test('extractJsonObject: 从含 prose/代码围栏文本取出第一个 JSON 对象', () => {
|
|
6
|
+
const raw = 'Here:\n```json\n{"a":1,"b":[2,3]}\n```\ndone';
|
|
7
|
+
expect(extractJsonObject(raw)).toEqual({ a: 1, b: [2, 3] });
|
|
8
|
+
});
|
|
9
|
+
test('extractJsonObject: 无 JSON → throw', () => {
|
|
10
|
+
expect(() => extractJsonObject('no json here')).toThrow(/no json object/);
|
|
11
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { slugFromUrl, slugFromLabel, isSameSite, normalizeCandidate, } from '../web-crawler.js';
|
|
2
|
+
describe('slugFromUrl', () => {
|
|
3
|
+
it('bare host without path', () => {
|
|
4
|
+
expect(slugFromUrl('https://pi.dev')).toBe('pi');
|
|
5
|
+
});
|
|
6
|
+
it('drops generic docs segment', () => {
|
|
7
|
+
expect(slugFromUrl('https://pi.dev/docs/intro')).toBe('pi-intro');
|
|
8
|
+
});
|
|
9
|
+
it('strips www and keeps at most 3 path segments', () => {
|
|
10
|
+
expect(slugFromUrl('https://www.example.com/a/b/c/d')).toBe('example-a-b-c');
|
|
11
|
+
});
|
|
12
|
+
it('ignores query and hash', () => {
|
|
13
|
+
expect(slugFromUrl('https://pi.dev/x?y=1#z')).toBe('pi-x');
|
|
14
|
+
});
|
|
15
|
+
it('throws on invalid url', () => {
|
|
16
|
+
expect(() => slugFromUrl('not a url')).toThrow();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('slugFromLabel', () => {
|
|
20
|
+
it('strips extension from ascii filename', () => {
|
|
21
|
+
expect(slugFromLabel('react-hooks.md')).toBe('react-hooks');
|
|
22
|
+
});
|
|
23
|
+
it('keeps the ascii stem and washes non-ascii chars away', () => {
|
|
24
|
+
expect(slugFromLabel('Pi 介绍.docx')).toBe('pi');
|
|
25
|
+
});
|
|
26
|
+
it('falls back to topic when nothing ascii remains', () => {
|
|
27
|
+
expect(slugFromLabel('介绍.docx')).toBe('topic');
|
|
28
|
+
});
|
|
29
|
+
it('takes basename and cleans spaces case-insensitively', () => {
|
|
30
|
+
expect(slugFromLabel('/tmp/dir/My Note.TXT')).toBe('my-note');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('isSameSite', () => {
|
|
34
|
+
it('subdomain is same site', () => {
|
|
35
|
+
expect(isSameSite('blog.pi.dev', 'pi.dev')).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
it('www is equivalent to bare host', () => {
|
|
38
|
+
expect(isSameSite('www.pi.dev', 'pi.dev')).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('cross domain is not same site', () => {
|
|
41
|
+
expect(isSameSite('other.com', 'pi.dev')).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it('suffix-without-dot is not same site', () => {
|
|
44
|
+
expect(isSameSite('notpi.dev', 'pi.dev')).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('normalizeCandidate', () => {
|
|
48
|
+
const entry = new URL('https://pi.dev/');
|
|
49
|
+
it('passes a normal http(s) link', () => {
|
|
50
|
+
const u = normalizeCandidate('https://pi.dev/a', entry);
|
|
51
|
+
expect(u?.href).toBe('https://pi.dev/a');
|
|
52
|
+
});
|
|
53
|
+
it('resolves relative links against entry', () => {
|
|
54
|
+
const u = normalizeCandidate('/about', entry);
|
|
55
|
+
expect(u?.href).toBe('https://pi.dev/about');
|
|
56
|
+
});
|
|
57
|
+
it('rejects non-http protocols like mailto', () => {
|
|
58
|
+
expect(normalizeCandidate('mailto:a@b.com', entry)).toBeNull();
|
|
59
|
+
});
|
|
60
|
+
it('excludes binary/document extensions', () => {
|
|
61
|
+
expect(normalizeCandidate('https://pi.dev/file.pdf', entry)).toBeNull();
|
|
62
|
+
expect(normalizeCandidate('https://pi.dev/PIC.JPG', entry)).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
it('clears the hash', () => {
|
|
65
|
+
const u = normalizeCandidate('https://pi.dev/a#section', entry);
|
|
66
|
+
expect(u?.href).toBe('https://pi.dev/a');
|
|
67
|
+
expect(u?.hash).toBe('');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -8,6 +8,7 @@ export interface ChunkRef {
|
|
|
8
8
|
id: string;
|
|
9
9
|
title: string;
|
|
10
10
|
summary: string;
|
|
11
|
+
tags: string[];
|
|
11
12
|
}
|
|
12
13
|
export interface TopicDetail {
|
|
13
14
|
title: string;
|
|
@@ -36,6 +37,7 @@ export interface KnowledgeStore {
|
|
|
36
37
|
chunkId: string;
|
|
37
38
|
title: string;
|
|
38
39
|
summary: string;
|
|
40
|
+
tags: string[];
|
|
39
41
|
}): Promise<void>;
|
|
40
42
|
}
|
|
41
43
|
export declare function createKnowledgeStore(root: string): KnowledgeStore;
|
|
@@ -53,7 +53,14 @@ export function createKnowledgeStore(root) {
|
|
|
53
53
|
title: meta.title || topic,
|
|
54
54
|
tags: Array.isArray(meta.tags) ? meta.tags : [],
|
|
55
55
|
sources: [],
|
|
56
|
-
chunks: Array.isArray(meta.chunks)
|
|
56
|
+
chunks: Array.isArray(meta.chunks)
|
|
57
|
+
? meta.chunks.map(c => ({
|
|
58
|
+
id: c.id,
|
|
59
|
+
title: c.title,
|
|
60
|
+
summary: c.summary,
|
|
61
|
+
tags: Array.isArray(c.tags) ? c.tags : [],
|
|
62
|
+
}))
|
|
63
|
+
: [],
|
|
57
64
|
};
|
|
58
65
|
}
|
|
59
66
|
async function upsertTopic(input) {
|
|
@@ -90,7 +97,7 @@ export function createKnowledgeStore(root) {
|
|
|
90
97
|
async function upsertChunkRef(input) {
|
|
91
98
|
assertTopic(input.topic);
|
|
92
99
|
assertChunkId(input.chunkId);
|
|
93
|
-
const { topic, chunkId, title, summary } = input;
|
|
100
|
+
const { topic, chunkId, title, summary, tags } = input;
|
|
94
101
|
const topicPath = path.join(root, topic, 'index.json');
|
|
95
102
|
let topicIdx;
|
|
96
103
|
try {
|
|
@@ -109,9 +116,10 @@ export function createKnowledgeStore(root) {
|
|
|
109
116
|
if (existing) {
|
|
110
117
|
existing.title = title;
|
|
111
118
|
existing.summary = summary;
|
|
119
|
+
existing.tags = tags;
|
|
112
120
|
}
|
|
113
121
|
else {
|
|
114
|
-
topicIdx.chunks.push({ id: chunkId, title, summary });
|
|
122
|
+
topicIdx.chunks.push({ id: chunkId, title, summary, tags });
|
|
115
123
|
}
|
|
116
124
|
await fs.writeFile(topicPath, JSON.stringify(topicIdx, null, 2), 'utf8');
|
|
117
125
|
}
|
|
@@ -120,6 +128,8 @@ export function createKnowledgeStore(root) {
|
|
|
120
128
|
return [];
|
|
121
129
|
const hits = [];
|
|
122
130
|
const q = query.toLowerCase();
|
|
131
|
+
// topic index 本函数作用域缓存,避免每个 chunk 重复读
|
|
132
|
+
const topicIndexCache = new Map();
|
|
123
133
|
let entries;
|
|
124
134
|
try {
|
|
125
135
|
entries = await fs.readdir(root);
|
|
@@ -130,6 +140,14 @@ export function createKnowledgeStore(root) {
|
|
|
130
140
|
for (const topic of entries) {
|
|
131
141
|
if (!TOPIC_RE.test(topic))
|
|
132
142
|
continue;
|
|
143
|
+
if (!topicIndexCache.has(topic)) {
|
|
144
|
+
const idx = await readJsonOr(path.join(root, topic, 'index.json'), null);
|
|
145
|
+
topicIndexCache.set(topic, idx);
|
|
146
|
+
}
|
|
147
|
+
const topicIdx = topicIndexCache.get(topic) ?? null;
|
|
148
|
+
const topicTags = topicIdx && Array.isArray(topicIdx.tags) ? topicIdx.tags : [];
|
|
149
|
+
const chunkRefs = topicIdx && Array.isArray(topicIdx.chunks) ? topicIdx.chunks : [];
|
|
150
|
+
const topicTagHit = topicTags.some(t => t.toLowerCase().includes(q));
|
|
133
151
|
const chunksDir = path.join(root, topic, 'chunks');
|
|
134
152
|
let files;
|
|
135
153
|
try {
|
|
@@ -139,11 +157,19 @@ export function createKnowledgeStore(root) {
|
|
|
139
157
|
continue;
|
|
140
158
|
}
|
|
141
159
|
for (const file of files) {
|
|
142
|
-
|
|
160
|
+
const chunkId = file.replace(/\.md$/, '');
|
|
161
|
+
if (!CHUNK_RE.test(chunkId))
|
|
143
162
|
continue;
|
|
144
163
|
const text = await fs.readFile(path.join(chunksDir, file), 'utf8');
|
|
145
|
-
|
|
146
|
-
|
|
164
|
+
const ref = chunkRefs.find(c => c.id === chunkId);
|
|
165
|
+
const refTags = ref && Array.isArray(ref.tags) ? ref.tags : [];
|
|
166
|
+
const hit = text.toLowerCase().includes(q) ||
|
|
167
|
+
(ref?.title ?? '').toLowerCase().includes(q) ||
|
|
168
|
+
(ref?.summary ?? '').toLowerCase().includes(q) ||
|
|
169
|
+
topicTagHit ||
|
|
170
|
+
refTags.some(t => t.toLowerCase().includes(q));
|
|
171
|
+
if (hit) {
|
|
172
|
+
hits.push({ topic, chunkId, title: parseChunkTitle(text) });
|
|
147
173
|
}
|
|
148
174
|
}
|
|
149
175
|
}
|
|
@@ -7,4 +7,6 @@ export declare function runEgoBrowser(scriptBody: string, opts?: {
|
|
|
7
7
|
timeoutMs?: number;
|
|
8
8
|
signal?: AbortSignal;
|
|
9
9
|
}): Promise<string>;
|
|
10
|
+
/** 过滤 ego-browser 自身打到输出流的横幅行(如升级 notice),保留脚本 cliLog 的真实输出 */
|
|
11
|
+
export declare function stripCliBanners(text: string): string;
|
|
10
12
|
export declare const browserTools: ToolDefinition[];
|
|
@@ -8,7 +8,7 @@ export function runEgoBrowser(scriptBody, opts = {}) {
|
|
|
8
8
|
const timeoutMs = opts.timeoutMs ?? 30000;
|
|
9
9
|
return new Promise((resolve, reject) => {
|
|
10
10
|
// 头部自动注入 task space 复用调用
|
|
11
|
-
const script = `const task = await
|
|
11
|
+
const script = `const task = await taskSpace('knowledge-learn')\n${scriptBody}`;
|
|
12
12
|
const child = spawn('ego-browser', ['nodejs'], {
|
|
13
13
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
14
|
});
|
|
@@ -61,7 +61,7 @@ export function runEgoBrowser(scriptBody, opts = {}) {
|
|
|
61
61
|
return;
|
|
62
62
|
if (code === 0) {
|
|
63
63
|
// 新版 ego cliLog 走 stderr;stdout 非空用 stdout,否则回退 stderr(新旧兼容)
|
|
64
|
-
const out = stdout.trim() ? stdout : stderr;
|
|
64
|
+
const out = stripCliBanners(stdout.trim() ? stdout : stderr);
|
|
65
65
|
finish(null, out);
|
|
66
66
|
}
|
|
67
67
|
else {
|
|
@@ -73,6 +73,13 @@ export function runEgoBrowser(scriptBody, opts = {}) {
|
|
|
73
73
|
child.stdin.end(script, 'utf-8');
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
|
+
/** 过滤 ego-browser 自身打到输出流的横幅行(如升级 notice),保留脚本 cliLog 的真实输出 */
|
|
77
|
+
export function stripCliBanners(text) {
|
|
78
|
+
return text
|
|
79
|
+
.split('\n')
|
|
80
|
+
.filter((line) => !line.startsWith('[ego-browser:notice]'))
|
|
81
|
+
.join('\n');
|
|
82
|
+
}
|
|
76
83
|
// ---------- 工具实现辅助 ----------
|
|
77
84
|
/** 把字符串参数安全注入脚本:JSON.stringify 自动处理引号 / 反斜杠 / 换行 */
|
|
78
85
|
const jsLit = (v) => JSON.stringify(v);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
export interface NavigatedPage {
|
|
3
|
+
title: string;
|
|
4
|
+
text: string;
|
|
5
|
+
links: string[];
|
|
6
|
+
}
|
|
7
|
+
interface PageData extends NavigatedPage {
|
|
8
|
+
url: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CrawlGuardOptions {
|
|
11
|
+
entryUrl: string;
|
|
12
|
+
docsDir: string;
|
|
13
|
+
slug: string;
|
|
14
|
+
maxPages?: number;
|
|
15
|
+
maxDepth?: number;
|
|
16
|
+
/** 可注入的单页抓取(测试用);默认调 runEgoBrowser;signal 用于中断在途抓取 */
|
|
17
|
+
navigate?: (url: string, signal?: AbortSignal) => Promise<NavigatedPage>;
|
|
18
|
+
}
|
|
19
|
+
export interface SaveSourceResult {
|
|
20
|
+
savedCount: number;
|
|
21
|
+
reused: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare class CrawlGuard {
|
|
24
|
+
private readonly opts;
|
|
25
|
+
private readonly maxPages;
|
|
26
|
+
private readonly maxDepth;
|
|
27
|
+
private readonly navigate;
|
|
28
|
+
private entry;
|
|
29
|
+
private readonly seen;
|
|
30
|
+
private readonly depthByUrl;
|
|
31
|
+
private readonly rawCache;
|
|
32
|
+
private readonly savedUrls;
|
|
33
|
+
private _savedCount;
|
|
34
|
+
private currentDepth;
|
|
35
|
+
constructor(opts: CrawlGuardOptions);
|
|
36
|
+
/** 默认单页抓取:跑 ego 脚本,JSON parse 失败硬抛错;signal 透传中断在途进程 */
|
|
37
|
+
private defaultNavigate;
|
|
38
|
+
/** 打开入口页:URL 校验 + 初始状态;signal 用于中断在途抓取 */
|
|
39
|
+
open(signal?: AbortSignal): Promise<PageData>;
|
|
40
|
+
/**
|
|
41
|
+
* 解析候选页的 BFS 最短深度:扫描每个已抓来源页的 links,
|
|
42
|
+
* 凡归一后命中候选页的来源,候选深度 = 来源深度 + 1,取所有命中的最小值。
|
|
43
|
+
* 一个来源都没查到(幻觉 URL)时硬抛错,绝不放行。
|
|
44
|
+
*/
|
|
45
|
+
private resolveDepth;
|
|
46
|
+
/** 访问子页:同站/seen/链接来源/深度/页数硬校验(原因 cross-site/seen/not linked/max-depth/max-pages) */
|
|
47
|
+
visit(rawUrl: string, signal?: AbortSignal): Promise<PageData>;
|
|
48
|
+
/**
|
|
49
|
+
* 保存已抓取页原文到 <slug>/sources/source-NNN.md;必须先 open/visit 过该 URL。
|
|
50
|
+
* 同页重复保存不写新文件,返回已有编号 + reused=true;空正文页判失败。
|
|
51
|
+
*/
|
|
52
|
+
saveSource(rawUrl: string): SaveSourceResult;
|
|
53
|
+
get savedCount(): number;
|
|
54
|
+
/**
|
|
55
|
+
* 抓取结束清理:no-op。
|
|
56
|
+
* 不能调 task.finish()——它会关闭全部 page 并把 space 所有权交还 user,
|
|
57
|
+
* 导致下一次 learn 的 taskSpace() resume 失败;持久 space 与 tab 留给后续复用。
|
|
58
|
+
*/
|
|
59
|
+
close(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
export declare function createCrawlTools(guard: CrawlGuard): ToolDefinition[];
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// crawl 阶段工具:LLM Agent 在 outline 阶段内自己驱动渐进抓取。
|
|
2
|
+
// - CrawlGuard:同站/深度/页数硬校验的有状态抓取器,抓取过的原文存内存缓存,
|
|
3
|
+
// saveSource 落盘到 <slug>/sources/source-NNN.md
|
|
4
|
+
// - createCrawlTools:挂给 pi session 的 4 个工具(crawl_open/crawl_visit/
|
|
5
|
+
// save_source/crawl_finish),错误以文本返回,LLM 可读可恢复
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { Type } from 'typebox';
|
|
9
|
+
import { runEgoBrowser } from './pi-browser-tools.js';
|
|
10
|
+
import { CRAWL_MAX_PAGES, CRAWL_MAX_DEPTH, isSameSite, normalizeCandidate } from './web-crawler.js';
|
|
11
|
+
/** 单页浏览器脚本:task space 取 tab → goto → networkidle 缓冲 → 稳定性轮询 → 提取标题/正文/链接 */
|
|
12
|
+
function buildCrawlScript(url) {
|
|
13
|
+
return `const pages = await task.pages()
|
|
14
|
+
const page = pages.find(p => p.label === 'p1') ?? pages[0] ?? await task.newPage()
|
|
15
|
+
await page.goto(${JSON.stringify(url)}, { waitUntil: 'load', timeout: 30000 })
|
|
16
|
+
try { await page.waitForLoadState('networkidle', { timeout: 5000 }) } catch {}
|
|
17
|
+
// 稳定性:500ms 采一次 innerText.length,连续 2 次相同且 >0,最多 10 次
|
|
18
|
+
await page.evaluate(() => new Promise((resolve) => {
|
|
19
|
+
let last = -1, same = 0, n = 0
|
|
20
|
+
const tick = () => {
|
|
21
|
+
const len = document.body ? document.body.innerText.length : 0
|
|
22
|
+
if (len > 0 && len === last) {
|
|
23
|
+
same += 1
|
|
24
|
+
if (same >= 2) return resolve()
|
|
25
|
+
} else {
|
|
26
|
+
same = 0
|
|
27
|
+
}
|
|
28
|
+
last = len
|
|
29
|
+
if (++n >= 10) return resolve()
|
|
30
|
+
setTimeout(tick, 500)
|
|
31
|
+
}
|
|
32
|
+
tick()
|
|
33
|
+
}))
|
|
34
|
+
const data = await page.evaluate(() => ({
|
|
35
|
+
title: document.title,
|
|
36
|
+
text: document.body ? document.body.innerText : '',
|
|
37
|
+
links: Array.from(document.querySelectorAll('a[href]')).map(a => a.href),
|
|
38
|
+
}))
|
|
39
|
+
cliLog(JSON.stringify(data))
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
export class CrawlGuard {
|
|
43
|
+
constructor(opts) {
|
|
44
|
+
Object.defineProperty(this, "opts", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: opts
|
|
49
|
+
});
|
|
50
|
+
Object.defineProperty(this, "maxPages", {
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
value: void 0
|
|
55
|
+
});
|
|
56
|
+
Object.defineProperty(this, "maxDepth", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: void 0
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, "navigate", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: void 0
|
|
67
|
+
});
|
|
68
|
+
Object.defineProperty(this, "entry", {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: void 0
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(this, "seen", {
|
|
75
|
+
enumerable: true,
|
|
76
|
+
configurable: true,
|
|
77
|
+
writable: true,
|
|
78
|
+
value: new Set()
|
|
79
|
+
});
|
|
80
|
+
Object.defineProperty(this, "depthByUrl", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
configurable: true,
|
|
83
|
+
writable: true,
|
|
84
|
+
value: new Map()
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(this, "rawCache", {
|
|
87
|
+
enumerable: true,
|
|
88
|
+
configurable: true,
|
|
89
|
+
writable: true,
|
|
90
|
+
value: new Map()
|
|
91
|
+
});
|
|
92
|
+
// 已落盘过的 URL(防重复原料)
|
|
93
|
+
Object.defineProperty(this, "savedUrls", {
|
|
94
|
+
enumerable: true,
|
|
95
|
+
configurable: true,
|
|
96
|
+
writable: true,
|
|
97
|
+
value: new Set()
|
|
98
|
+
});
|
|
99
|
+
Object.defineProperty(this, "_savedCount", {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
configurable: true,
|
|
102
|
+
writable: true,
|
|
103
|
+
value: 0
|
|
104
|
+
});
|
|
105
|
+
Object.defineProperty(this, "currentDepth", {
|
|
106
|
+
enumerable: true,
|
|
107
|
+
configurable: true,
|
|
108
|
+
writable: true,
|
|
109
|
+
value: 0
|
|
110
|
+
});
|
|
111
|
+
this.maxPages = opts.maxPages ?? CRAWL_MAX_PAGES;
|
|
112
|
+
this.maxDepth = opts.maxDepth ?? CRAWL_MAX_DEPTH;
|
|
113
|
+
this.navigate = opts.navigate ?? ((url, signal) => this.defaultNavigate(url, signal));
|
|
114
|
+
}
|
|
115
|
+
/** 默认单页抓取:跑 ego 脚本,JSON parse 失败硬抛错;signal 透传中断在途进程 */
|
|
116
|
+
async defaultNavigate(url, signal) {
|
|
117
|
+
const raw = await runEgoBrowser(buildCrawlScript(url), { timeoutMs: 60000, signal });
|
|
118
|
+
let data;
|
|
119
|
+
try {
|
|
120
|
+
data = JSON.parse(raw);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
throw new Error(`crawl: failed to parse result from ${url}`);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
title: typeof data.title === 'string' ? data.title : '',
|
|
127
|
+
text: typeof data.text === 'string' ? data.text : '',
|
|
128
|
+
links: Array.isArray(data.links) ? data.links.filter((l) => typeof l === 'string') : [],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** 打开入口页:URL 校验 + 初始状态;signal 用于中断在途抓取 */
|
|
132
|
+
async open(signal) {
|
|
133
|
+
const entry = new URL(this.opts.entryUrl);
|
|
134
|
+
entry.hash = '';
|
|
135
|
+
this.entry = entry;
|
|
136
|
+
this.seen.add(entry.href);
|
|
137
|
+
this.depthByUrl.set(entry.href, 0);
|
|
138
|
+
this.currentDepth = 0;
|
|
139
|
+
const page = await this.navigate(entry.href, signal);
|
|
140
|
+
this.rawCache.set(entry.href, page);
|
|
141
|
+
return { url: entry.href, ...page };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 解析候选页的 BFS 最短深度:扫描每个已抓来源页的 links,
|
|
145
|
+
* 凡归一后命中候选页的来源,候选深度 = 来源深度 + 1,取所有命中的最小值。
|
|
146
|
+
* 一个来源都没查到(幻觉 URL)时硬抛错,绝不放行。
|
|
147
|
+
*/
|
|
148
|
+
resolveDepth(candidate) {
|
|
149
|
+
let resolved = null;
|
|
150
|
+
for (const [sourceHref, page] of this.rawCache) {
|
|
151
|
+
for (const link of page.links) {
|
|
152
|
+
const linked = normalizeCandidate(link, this.entry);
|
|
153
|
+
if (linked && linked.href === candidate.href) {
|
|
154
|
+
const next = this.depthByUrl.get(sourceHref) + 1;
|
|
155
|
+
if (resolved === null || next < resolved)
|
|
156
|
+
resolved = next;
|
|
157
|
+
break; // 本来源页已命中,不必再看它其余 links
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (resolved === null) {
|
|
162
|
+
throw new Error(`crawl visit reject: ${candidate.href} not linked from any crawled page`);
|
|
163
|
+
}
|
|
164
|
+
return resolved;
|
|
165
|
+
}
|
|
166
|
+
/** 访问子页:同站/seen/链接来源/深度/页数硬校验(原因 cross-site/seen/not linked/max-depth/max-pages) */
|
|
167
|
+
async visit(rawUrl, signal) {
|
|
168
|
+
const candidate = normalizeCandidate(rawUrl, this.entry);
|
|
169
|
+
if (!candidate)
|
|
170
|
+
throw new Error(`crawl visit reject: invalid or binary url: ${rawUrl}`);
|
|
171
|
+
if (!isSameSite(candidate.hostname, this.entry.hostname)) {
|
|
172
|
+
throw new Error(`crawl visit reject cross-site: ${candidate.href}`);
|
|
173
|
+
}
|
|
174
|
+
if (this.seen.has(candidate.href)) {
|
|
175
|
+
throw new Error(`crawl visit reject seen: ${candidate.href}`);
|
|
176
|
+
}
|
|
177
|
+
// BFS 最短深度(候选必须由某个已抓页链接;未链接直接 throw)
|
|
178
|
+
const nextDepth = this.resolveDepth(candidate);
|
|
179
|
+
if (nextDepth > this.maxDepth) {
|
|
180
|
+
throw new Error(`crawl visit reject max-depth: ${candidate.href}`);
|
|
181
|
+
}
|
|
182
|
+
if (this.seen.size + 1 > this.maxPages) {
|
|
183
|
+
throw new Error(`crawl visit reject max-pages: ${candidate.href}`);
|
|
184
|
+
}
|
|
185
|
+
const page = await this.navigate(candidate.href, signal);
|
|
186
|
+
this.seen.add(candidate.href);
|
|
187
|
+
this.depthByUrl.set(candidate.href, nextDepth);
|
|
188
|
+
this.currentDepth = nextDepth;
|
|
189
|
+
this.rawCache.set(candidate.href, page);
|
|
190
|
+
return { url: candidate.href, ...page };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 保存已抓取页原文到 <slug>/sources/source-NNN.md;必须先 open/visit 过该 URL。
|
|
194
|
+
* 同页重复保存不写新文件,返回已有编号 + reused=true;空正文页判失败。
|
|
195
|
+
*/
|
|
196
|
+
saveSource(rawUrl) {
|
|
197
|
+
let key = rawUrl;
|
|
198
|
+
const candidate = normalizeCandidate(rawUrl, this.entry);
|
|
199
|
+
if (candidate)
|
|
200
|
+
key = candidate.href;
|
|
201
|
+
const page = this.rawCache.get(key);
|
|
202
|
+
if (!page)
|
|
203
|
+
throw new Error(`save_source reject: url not crawled: ${rawUrl}`);
|
|
204
|
+
// 已存过:不重复落盘,返回已有编号
|
|
205
|
+
if (this.savedUrls.has(key)) {
|
|
206
|
+
return { savedCount: this._savedCount, reused: true };
|
|
207
|
+
}
|
|
208
|
+
// 对齐旧 crawlSite:trim 后为空的页面判失败,不产生空原料
|
|
209
|
+
if (page.text.trim() === '')
|
|
210
|
+
throw new Error(`save_source reject empty page: ${key}`);
|
|
211
|
+
const sourcesDir = path.join(this.opts.docsDir, this.opts.slug, 'sources');
|
|
212
|
+
fs.mkdirSync(sourcesDir, { recursive: true });
|
|
213
|
+
const name = `source-${String(this._savedCount + 1).padStart(3, '0')}.md`;
|
|
214
|
+
fs.writeFileSync(path.join(sourcesDir, name), `Source: ${key}\n\n${page.text}`, 'utf8');
|
|
215
|
+
this.savedUrls.add(key);
|
|
216
|
+
this._savedCount += 1;
|
|
217
|
+
return { savedCount: this._savedCount, reused: false };
|
|
218
|
+
}
|
|
219
|
+
get savedCount() {
|
|
220
|
+
return this._savedCount;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 抓取结束清理:no-op。
|
|
224
|
+
* 不能调 task.finish()——它会关闭全部 page 并把 space 所有权交还 user,
|
|
225
|
+
* 导致下一次 learn 的 taskSpace() resume 失败;持久 space 与 tab 留给后续复用。
|
|
226
|
+
*/
|
|
227
|
+
async close() { }
|
|
228
|
+
}
|
|
229
|
+
// ============================================================
|
|
230
|
+
// ToolDefinition 构造(写法对齐 pi-runtime,ok/err 返回)
|
|
231
|
+
// ============================================================
|
|
232
|
+
const ok = (text) => ({
|
|
233
|
+
content: [{ type: 'text', text }],
|
|
234
|
+
details: undefined,
|
|
235
|
+
});
|
|
236
|
+
const errAsText = (e) => ok(`error: ${e instanceof Error ? e.message : String(e)}`);
|
|
237
|
+
export function createCrawlTools(guard) {
|
|
238
|
+
return [
|
|
239
|
+
{
|
|
240
|
+
name: 'crawl_open',
|
|
241
|
+
label: 'crawl_open',
|
|
242
|
+
description: '打开本次任务的入口 URL,返回标题、正文与同站链接。抓取第一步调用。',
|
|
243
|
+
parameters: Type.Object({}),
|
|
244
|
+
async execute(_id, _params, signal) {
|
|
245
|
+
try {
|
|
246
|
+
return ok(JSON.stringify(await guard.open(signal)));
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
return errAsText(e);
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'crawl_visit',
|
|
255
|
+
label: 'crawl_visit',
|
|
256
|
+
description: '访问入口同站的子页面(必须是 crawl_open/上一次 crawl_visit 返回链接中的 http(s) 页面)。',
|
|
257
|
+
parameters: Type.Object({ url: Type.String({ description: '要访问的同站完整 URL' }) }),
|
|
258
|
+
async execute(_id, params, signal) {
|
|
259
|
+
try {
|
|
260
|
+
return ok(JSON.stringify(await guard.visit(params.url, signal)));
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
return errAsText(e);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: 'save_source',
|
|
269
|
+
label: 'save_source',
|
|
270
|
+
description: '把当前已抓取页面的原文保存到本主题 sources/ 目录。每打开一页后调用一次;同页重复保存不写新文件。',
|
|
271
|
+
parameters: Type.Object({ url: Type.String({ description: '刚抓取的页面 URL' }) }),
|
|
272
|
+
async execute(_id, params) {
|
|
273
|
+
try {
|
|
274
|
+
// 直接返回 { savedCount, reused }
|
|
275
|
+
return ok(JSON.stringify(guard.saveSource(params.url)));
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
return errAsText(e);
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: 'crawl_finish',
|
|
284
|
+
label: 'crawl_finish',
|
|
285
|
+
description: '结束抓取:清理浏览器并返回已保存页数。无更多有价值链接或到达上限时调用。',
|
|
286
|
+
parameters: Type.Object({}),
|
|
287
|
+
async execute() {
|
|
288
|
+
try {
|
|
289
|
+
await guard.close();
|
|
290
|
+
return ok(JSON.stringify({ finished: true, savedCount: guard.savedCount }));
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
return errAsText(e);
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
];
|
|
298
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export interface TopicSummary {
|
|
2
|
+
slug: string;
|
|
3
|
+
title: string;
|
|
4
|
+
summary: string;
|
|
5
|
+
tags: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function normalizeText(s: string): string;
|
|
8
|
+
export declare function findCandidates(cand: {
|
|
9
|
+
title: string;
|
|
10
|
+
tags: string[];
|
|
11
|
+
}, topics: TopicSummary[]): TopicSummary[];
|
|
12
|
+
export interface OutlineItem {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
oneLine: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ChunkRefLike {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
summary: string;
|
|
21
|
+
tags: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface TopicIndexLike {
|
|
24
|
+
slug: string;
|
|
25
|
+
title: string;
|
|
26
|
+
summary: string;
|
|
27
|
+
tags: string[];
|
|
28
|
+
chunks: ChunkRefLike[];
|
|
29
|
+
}
|
|
30
|
+
export interface ReconcileResult {
|
|
31
|
+
match: boolean;
|
|
32
|
+
targetSlug?: string;
|
|
33
|
+
confidence?: number;
|
|
34
|
+
mergedTitle?: string;
|
|
35
|
+
mergedSummary?: string;
|
|
36
|
+
itemsToAdd?: OutlineItem[];
|
|
37
|
+
}
|
|
38
|
+
export declare const LLM_FULL_SCAN_MAX = 20;
|
|
39
|
+
export declare function readAllTopics(docsDir: string): Promise<TopicSummary[]>;
|
|
40
|
+
export declare function readTopicDetail(docsDir: string, slug: string): Promise<TopicIndexLike | null>;
|
|
41
|
+
export declare function reconcileTopics(opts: {
|
|
42
|
+
port: number;
|
|
43
|
+
docsDir: string;
|
|
44
|
+
candidate: {
|
|
45
|
+
slug: string;
|
|
46
|
+
title: string;
|
|
47
|
+
tags: string[];
|
|
48
|
+
items?: OutlineItem[];
|
|
49
|
+
};
|
|
50
|
+
targets?: Array<TopicSummary & {
|
|
51
|
+
chunks?: ChunkRefLike[];
|
|
52
|
+
}>;
|
|
53
|
+
wantItems: boolean;
|
|
54
|
+
}): Promise<ReconcileResult>;
|
|
55
|
+
export declare function mergeTopics(docsDir: string, srcSlug: string, dstSlug: string): number;
|
|
56
|
+
export declare function readSidecarDetail(docsDir: string, slug: string): Promise<TopicIndexLike | null>;
|
|
57
|
+
export declare function mintSlug(opts: {
|
|
58
|
+
port: number;
|
|
59
|
+
title: string;
|
|
60
|
+
existing: string[];
|
|
61
|
+
}): Promise<string>;
|
|
62
|
+
export declare function assertNoOldDirs(docsDir: string): void;
|