create-harness-vibe-coding 0.8.19 → 0.9.3
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/CHANGELOG.md +49 -0
- package/README-CN.md +4 -2
- package/README.md +23 -4
- package/package.json +17 -2
- package/src/generator.js +19 -6
- package/src/index.js +531 -38
- package/src/prompts.js +4 -4
- package/src/ui/dist/assets/AgentsRoute-BGGs90wH.js +7 -0
- package/src/ui/dist/assets/Assistant-Bold-gm-uSS1B.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-Medium-DrcxCXg3.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-Regular-DVxZuzxb.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-SemiBold-SCI4bEL9.woff2 +0 -0
- package/src/ui/dist/assets/Combination-DhBLEmof.js +42 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/src/ui/dist/assets/RolesRoute-DnodCSz0.js +2 -0
- package/src/ui/dist/assets/RuntimePicker-CucD8TGn.js +7 -0
- package/src/ui/dist/assets/SettingsRoute-DquMWjac.js +2 -0
- package/src/ui/dist/assets/TaskList-Bxc0Uktx.js +7 -0
- package/src/ui/dist/assets/TerminalDrawer-CDusYltc.js +36 -0
- package/src/ui/dist/assets/WorkflowRoute-BN8yHmUs.css +1 -0
- package/src/ui/dist/assets/WorkflowRoute-D-0wVDpY.js +334 -0
- package/src/ui/dist/assets/abnfDiagram-VRR7QNED-zkRKkaxO.js +2 -0
- package/src/ui/dist/assets/apl-B4CMkyY2.js +2 -0
- package/src/ui/dist/assets/ar-SA-G6X2FPQ2-BiHaArx5.js +11 -0
- package/src/ui/dist/assets/arc-CBnLtOFO.js +2 -0
- package/src/ui/dist/assets/architectureDiagram-ZJ3FMSHR-DWXRXsh1.js +37 -0
- package/src/ui/dist/assets/asciiarmor-Df11BRmG.js +2 -0
- package/src/ui/dist/assets/asn1-EdZsLKOL.js +2 -0
- package/src/ui/dist/assets/asterisk-B-8jnY81.js +2 -0
- package/src/ui/dist/assets/az-AZ-76LH7QW2-CiT05voO.js +2 -0
- package/src/ui/dist/assets/bg-BG-XCXSNQG7-L-UM9tNf.js +6 -0
- package/src/ui/dist/assets/blockDiagram-677ZJIJ3-BIyh3Bry.js +133 -0
- package/src/ui/dist/assets/bn-BD-2XOGV67Q-NdgbA0bC.js +6 -0
- package/src/ui/dist/assets/brainfuck-C4LP7Hcl.js +2 -0
- package/src/ui/dist/assets/c4Diagram-LMCZKHZV-CA54vWWM.js +11 -0
- package/src/ui/dist/assets/ca-ES-6MX7JW3Y-BKZZsQ5J.js +9 -0
- package/src/ui/dist/assets/channel-Da_-zcAu.js +2 -0
- package/src/ui/dist/assets/chunk-2Q5K7J3B-DE4cxr1v.js +2 -0
- package/src/ui/dist/assets/chunk-32BRIVSS-i7FwbEU9.js +2 -0
- package/src/ui/dist/assets/chunk-5VM5RSS4-BhIcOYSu.js +16 -0
- package/src/ui/dist/assets/chunk-EX3LRPZG-Ci9zZRIs.js +232 -0
- package/src/ui/dist/assets/chunk-JWPE2WC7-BFdkcz-G.js +2 -0
- package/src/ui/dist/assets/chunk-MOJQB5TN-Dzmapl_S.js +89 -0
- package/src/ui/dist/assets/chunk-RYQCIY6F-RTmp7FOy.js +2 -0
- package/src/ui/dist/assets/chunk-V7JOEXUC-DInU3v3D.js +207 -0
- package/src/ui/dist/assets/chunk-VR4S4FIN-BHoyGBQO.js +2 -0
- package/src/ui/dist/assets/chunk-XXDRQBXY-DfeLZNZx.js +2 -0
- package/src/ui/dist/assets/classDiagram-OUVF2IWQ-FKbUS0bu.js +2 -0
- package/src/ui/dist/assets/classDiagram-v2-EOCWNBFH-FKbUS0bu.js +2 -0
- package/src/ui/dist/assets/clike-B9uivgTg.js +2 -0
- package/src/ui/dist/assets/clojure-BMjYHr_A.js +2 -0
- package/src/ui/dist/assets/cmake-BQqOBYOt.js +2 -0
- package/src/ui/dist/assets/cobol-CWcv1MsR.js +2 -0
- package/src/ui/dist/assets/coffeescript-S37ZYGWr.js +2 -0
- package/src/ui/dist/assets/commonlisp-DBKNyK5s.js +2 -0
- package/src/ui/dist/assets/cose-bilkent-JH36ORCC-_5PUJ615.js +2 -0
- package/src/ui/dist/assets/crystal-SjHAIU92.js +2 -0
- package/src/ui/dist/assets/cs-CZ-2BRQDIVT-DnkfnhDU.js +12 -0
- package/src/ui/dist/assets/css-BnMrqG3P.js +2 -0
- package/src/ui/dist/assets/cynefin-VYW2F7L2-CmkcFv-b.js +179 -0
- package/src/ui/dist/assets/cynefinDiagram-TSTJHNR4-D72VZn8H.js +63 -0
- package/src/ui/dist/assets/cypher-C_CwsFkJ.js +2 -0
- package/src/ui/dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
- package/src/ui/dist/assets/d-pRatUO7H.js +2 -0
- package/src/ui/dist/assets/da-DK-5WZEPLOC-C54tQUgd.js +6 -0
- package/src/ui/dist/assets/dagre-VKFMJZFB-0BsAkrmo.js +5 -0
- package/src/ui/dist/assets/de-DE-XR44H4JA-DGfZlR10.js +9 -0
- package/src/ui/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/src/ui/dist/assets/diagram-FQU43EPY-C1ZJDOca.js +4 -0
- package/src/ui/dist/assets/diagram-G47NLZAW-D3pr3FSP.js +25 -0
- package/src/ui/dist/assets/diagram-NH7WQ7WH-Bwry0NV-.js +25 -0
- package/src/ui/dist/assets/diagram-OA4YK3LP-L6g09cmV.js +31 -0
- package/src/ui/dist/assets/diagram-WEI45ONY-Bh1puYVX.js +42 -0
- package/src/ui/dist/assets/diff-DbItnlRl.js +2 -0
- package/src/ui/dist/assets/directory-open-01563666-DWU9wJ6I.js +2 -0
- package/src/ui/dist/assets/directory-open-4ed118d0-CunoC1EB.js +2 -0
- package/src/ui/dist/assets/dockerfile-BKs6k2Af.js +2 -0
- package/src/ui/dist/assets/dtd-DF_7sFjM.js +2 -0
- package/src/ui/dist/assets/dylan-DwRh75JA.js +2 -0
- package/src/ui/dist/assets/ebnf-CDyGwa7X.js +2 -0
- package/src/ui/dist/assets/ebnfDiagram-CCIWWBDH-BC_mBRR5.js +2 -0
- package/src/ui/dist/assets/ecl-Cabwm37j.js +2 -0
- package/src/ui/dist/assets/eiffel-CnydiIhH.js +2 -0
- package/src/ui/dist/assets/el-GR-BZB4AONW-DsnwWweL.js +11 -0
- package/src/ui/dist/assets/elm-vLlmbW-K.js +2 -0
- package/src/ui/dist/assets/erDiagram-Q63AITRT-Bkxam36l.js +86 -0
- package/src/ui/dist/assets/erlang-BNw1qcRV.js +2 -0
- package/src/ui/dist/assets/es-ES-U4NZUMDT-CRxBcQgC.js +10 -0
- package/src/ui/dist/assets/eu-ES-A7QVB2H4-CTd_E7YR.js +12 -0
- package/src/ui/dist/assets/excalidraw-default-bMWIgNCy.svg +1 -0
- package/src/ui/dist/assets/fa-IR-HGAKTJCU-DO56k7eG.js +9 -0
- package/src/ui/dist/assets/factor-kuTfRLto.js +2 -0
- package/src/ui/dist/assets/fcl-Kvtd6kyn.js +2 -0
- package/src/ui/dist/assets/fi-FI-Z5N7JZ37-DEDCF9WB.js +7 -0
- package/src/ui/dist/assets/file-open-002ab408-DIuFHtCF.js +2 -0
- package/src/ui/dist/assets/file-open-7c801643-684qeFg4.js +2 -0
- package/src/ui/dist/assets/file-save-3189631c-C1wFhQhH.js +2 -0
- package/src/ui/dist/assets/file-save-745eba88-Bb9F9Kg7.js +2 -0
- package/src/ui/dist/assets/flowDiagram-23GEKE2U-C3SAEao7.js +157 -0
- package/src/ui/dist/assets/forth-Ffai-XNe.js +2 -0
- package/src/ui/dist/assets/fortran-DYz_wnZ1.js +2 -0
- package/src/ui/dist/assets/fr-FR-RHASNOE6-DE2nPyDt.js +10 -0
- package/src/ui/dist/assets/ganttDiagram-NO4QXBWP-U-J_Xi-_.js +293 -0
- package/src/ui/dist/assets/gas-Bneqetm1.js +2 -0
- package/src/ui/dist/assets/gherkin-heZmZLOM.js +2 -0
- package/src/ui/dist/assets/gitGraphDiagram-IHSO6WYX-BADVbD-a.js +107 -0
- package/src/ui/dist/assets/gl-ES-HMX3MZ6V-mCqVgQHC.js +11 -0
- package/src/ui/dist/assets/graph-C9eacEi8.js +2 -0
- package/src/ui/dist/assets/groovy-D9Dt4D0W.js +2 -0
- package/src/ui/dist/assets/haskell-Cw1EW3IL.js +2 -0
- package/src/ui/dist/assets/haxe-H-WmDvRZ.js +2 -0
- package/src/ui/dist/assets/he-IL-6SHJWFNN-KnaLrCzw.js +11 -0
- package/src/ui/dist/assets/hi-IN-IWLTKZ5I-wuqiaaWt.js +5 -0
- package/src/ui/dist/assets/http-DBlCnlav.js +2 -0
- package/src/ui/dist/assets/hu-HU-A5ZG7DT2-DLg_zm22.js +8 -0
- package/src/ui/dist/assets/id-ID-SAP4L64H-MB2mXqcR.js +11 -0
- package/src/ui/dist/assets/idl-BEugSyMb.js +2 -0
- package/src/ui/dist/assets/image-blob-reduce.esm-HwCm7IZ0.js +8 -0
- package/src/ui/dist/assets/index-39SJtrt0.js +2 -0
- package/src/ui/dist/assets/index-BQ0LIrW-.js +2 -0
- package/src/ui/dist/assets/index-BcAUh2Nm.css +1 -0
- package/src/ui/dist/assets/index-BcThOK-i.js +331 -0
- package/src/ui/dist/assets/index-BnxV3k_S.js +3 -0
- package/src/ui/dist/assets/index-C7MZBHbT.js +117 -0
- package/src/ui/dist/assets/index-CCvKI2rM.js +2 -0
- package/src/ui/dist/assets/index-CZvmcCab.js +8 -0
- package/src/ui/dist/assets/index-Cp3hLKNl.js +2 -0
- package/src/ui/dist/assets/index-D7ayFxcx.js +2 -0
- package/src/ui/dist/assets/index-DAYxSQGU.js +2 -0
- package/src/ui/dist/assets/index-DD-HeMV0.css +1 -0
- package/src/ui/dist/assets/index-DPMxd3Qn.js +2 -0
- package/src/ui/dist/assets/index-DSe2jx3k.js +4 -0
- package/src/ui/dist/assets/index-DUirCkgW.js +2 -0
- package/src/ui/dist/assets/index-DaKPQ2hN.js +159 -0
- package/src/ui/dist/assets/index-DzpuR0Jg.js +2 -0
- package/src/ui/dist/assets/index-POndwsCI.js +2 -0
- package/src/ui/dist/assets/index-R8NM9I_9.js +2 -0
- package/src/ui/dist/assets/index-TCDuDhBj.js +2 -0
- package/src/ui/dist/assets/index-TckzASdM.js +2 -0
- package/src/ui/dist/assets/index-Yi5ytFFr.js +2 -0
- package/src/ui/dist/assets/infoDiagram-FWYZ7A6U-CiGBlFqH.js +3 -0
- package/src/ui/dist/assets/init-Gi6I4Gst.js +2 -0
- package/src/ui/dist/assets/ishikawaDiagram-FXEZZL3T-B1aheRbz.js +71 -0
- package/src/ui/dist/assets/it-IT-JPQ66NNP-DT3HSU7X.js +12 -0
- package/src/ui/dist/assets/ja-JP-DBVTYXUO-CsfJjjWt.js +9 -0
- package/src/ui/dist/assets/javascript-iXu5QeM3.js +2 -0
- package/src/ui/dist/assets/journeyDiagram-5HDEW3XC-DtnJPxub.js +140 -0
- package/src/ui/dist/assets/julia-DuME0IfC.js +2 -0
- package/src/ui/dist/assets/kaa-6HZHGXH3-DQz-1-Gn.js +2 -0
- package/src/ui/dist/assets/kab-KAB-ZGHBKWFO-CaH-LSoK.js +9 -0
- package/src/ui/dist/assets/kanban-definition-HUTT4EX6-CZatSm3e.js +90 -0
- package/src/ui/dist/assets/katex-C5jXJg4s.js +258 -0
- package/src/ui/dist/assets/kk-KZ-P5N5QNE5-BMXmcm55.js +2 -0
- package/src/ui/dist/assets/km-KH-HSX4SM5Z-ZeoJNBpN.js +12 -0
- package/src/ui/dist/assets/ko-KR-MTYHY66A-BeaLRKph.js +10 -0
- package/src/ui/dist/assets/ku-TR-6OUDTVRD-B_28KrkR.js +10 -0
- package/src/ui/dist/assets/layout-DEXfKzaS.js +2 -0
- package/src/ui/dist/assets/linear-CpEDnQRC.js +2 -0
- package/src/ui/dist/assets/livescript-BwQOo05w.js +2 -0
- package/src/ui/dist/assets/lt-LT-XHIRWOB4-BxDcnUhp.js +4 -0
- package/src/ui/dist/assets/lua-VAEuO923.js +2 -0
- package/src/ui/dist/assets/lv-LV-5QDEKY6T-DpKDl1_y.js +8 -0
- package/src/ui/dist/assets/map-Czzmt4hB.js +2 -0
- package/src/ui/dist/assets/mathematica-DTrFuWx2.js +2 -0
- package/src/ui/dist/assets/mbox-CNhZ1qSd.js +2 -0
- package/src/ui/dist/assets/mermaidLoader-CBO31t0J.js +305 -0
- package/src/ui/dist/assets/mermaidLoader-CEK31ho9.css +1 -0
- package/src/ui/dist/assets/mindmap-definition-LN4V7U3C-CQ0TccEr.js +97 -0
- package/src/ui/dist/assets/mirc-CjQqDB4T.js +2 -0
- package/src/ui/dist/assets/mllike-CXdrOF99.js +2 -0
- package/src/ui/dist/assets/modelica-Dc1JOy9r.js +2 -0
- package/src/ui/dist/assets/mr-IN-CRQNXWMA-BKCZT58p.js +14 -0
- package/src/ui/dist/assets/mscgen-BA5vi2Kp.js +2 -0
- package/src/ui/dist/assets/mumps-BT43cFF4.js +2 -0
- package/src/ui/dist/assets/my-MM-5M5IBNSE-D34k0G9T.js +2 -0
- package/src/ui/dist/assets/nb-NO-T6EIAALU-DXr_KW8J.js +11 -0
- package/src/ui/dist/assets/nginx-DdIZxoE0.js +2 -0
- package/src/ui/dist/assets/nl-NL-IS3SIHDZ-C1L0aVDR.js +9 -0
- package/src/ui/dist/assets/nn-NO-6E72VCQL-CLWVSxbZ.js +9 -0
- package/src/ui/dist/assets/nsis-LdVXkNf5.js +2 -0
- package/src/ui/dist/assets/ntriples-BfvgReVJ.js +2 -0
- package/src/ui/dist/assets/oc-FR-POXYY2M6-nf8_J3eR.js +9 -0
- package/src/ui/dist/assets/octave-Ck1zUtKM.js +2 -0
- package/src/ui/dist/assets/ordinal-Cboi1Yqb.js +2 -0
- package/src/ui/dist/assets/oz-BzwKVEFT.js +2 -0
- package/src/ui/dist/assets/pa-IN-N4M65BXN-CAMJ-xd6.js +5 -0
- package/src/ui/dist/assets/pascal--L3eBynH.js +2 -0
- package/src/ui/dist/assets/pegDiagram-2B236MQR-Bj2RcmRd.js +2 -0
- package/src/ui/dist/assets/percentages-BXMCSKIN-s9yWFdr6.js +160 -0
- package/src/ui/dist/assets/perl-CdXCOZ3F.js +2 -0
- package/src/ui/dist/assets/pica-BGv_AhAB.js +8 -0
- package/src/ui/dist/assets/pieDiagram-ENE6RG2P-obpzz8dC.js +40 -0
- package/src/ui/dist/assets/pig-CevX1Tat.js +2 -0
- package/src/ui/dist/assets/pl-PL-T2D74RX3-DKJ5H85v.js +10 -0
- package/src/ui/dist/assets/powershell-CFHJl5sT.js +2 -0
- package/src/ui/dist/assets/properties-C78fOPTZ.js +2 -0
- package/src/ui/dist/assets/protobuf-ChK-085T.js +2 -0
- package/src/ui/dist/assets/{proxy-D0vUbds5.js → proxy-BSmwef4H.js} +2 -2
- package/src/ui/dist/assets/pt-BR-5N22H2LF-B5kow-kN.js +10 -0
- package/src/ui/dist/assets/pt-PT-UZXXM6DQ-B2G34fso.js +10 -0
- package/src/ui/dist/assets/pug-DeIclll2.js +2 -0
- package/src/ui/dist/assets/puppet-DMA9R1ak.js +2 -0
- package/src/ui/dist/assets/python-BuPzkPfP.js +2 -0
- package/src/ui/dist/assets/q-pXgVlZs6.js +2 -0
- package/src/ui/dist/assets/quadrantDiagram-ABIIQ3AL-Cl-T7NkJ.js +8 -0
- package/src/ui/dist/assets/r-B6wPVr8A.js +2 -0
- package/src/ui/dist/assets/railroadDiagram-RFXS5EU6-DMq82RHg.js +2 -0
- package/src/ui/dist/assets/{refresh-cw-JNmExijd.js → refresh-cw-CbFd7guV.js} +2 -2
- package/src/ui/dist/assets/requirementDiagram-TGXJPOKE-tGInMDgU.js +85 -0
- package/src/ui/dist/assets/ro-RO-JPDTUUEW-BwLdZW91.js +12 -0
- package/src/ui/dist/assets/roundRect-0PYZxl1G.js +2 -0
- package/src/ui/dist/assets/rpm-CTu-6PCP.js +2 -0
- package/src/ui/dist/assets/ru-RU-B4JR7IUQ-DpdAgIJc.js +10 -0
- package/src/ui/dist/assets/ruby-B2Rjki9n.js +2 -0
- package/src/ui/dist/assets/runtimeBrand-BoKPimsz.js +2 -0
- package/src/ui/dist/assets/sankeyDiagram-HTMAVEWB-C7X-ET4m.js +41 -0
- package/src/ui/dist/assets/sas-B4kiWyti.js +2 -0
- package/src/ui/dist/assets/save-D1CXHwBw.js +12 -0
- package/src/ui/dist/assets/scheme-C41bIUwD.js +2 -0
- package/src/ui/dist/assets/sequenceDiagram-DBY2YBRQ-Bn24i_nY.js +163 -0
- package/src/ui/dist/assets/settings-2-DtSNAAcU.js +22 -0
- package/src/ui/dist/assets/shell-CjFT_Tl9.js +2 -0
- package/src/ui/dist/assets/shield-check-BVbJ8T_N.js +7 -0
- package/src/ui/dist/assets/si-LK-N5RQ5JYF-naJaUy5u.js +2 -0
- package/src/ui/dist/assets/sieve-C3Gn_uJK.js +2 -0
- package/src/ui/dist/assets/simple-mode-GW_nhZxv.js +2 -0
- package/src/ui/dist/assets/sizeCapture-X5ZJPWSS-DAQHfm82.js +2 -0
- package/src/ui/dist/assets/sk-SK-C5VTKIMK-1NPz1J_d.js +7 -0
- package/src/ui/dist/assets/sl-SI-NN7IZMDC-D8RfmNQw.js +7 -0
- package/src/ui/dist/assets/smalltalk-CnHTOXQT.js +2 -0
- package/src/ui/dist/assets/solr-DehyRSwq.js +2 -0
- package/src/ui/dist/assets/sparql-DkYu6x3z.js +2 -0
- package/src/ui/dist/assets/spreadsheet-BCZA_wO0.js +2 -0
- package/src/ui/dist/assets/sql-D0XecflT.js +2 -0
- package/src/ui/dist/assets/stateDiagram-2N3HPSRC-Ck48D1ck.js +2 -0
- package/src/ui/dist/assets/stateDiagram-v2-6OUMAXLB-CdfRCJAJ.js +2 -0
- package/src/ui/dist/assets/stex-C3f8Ysf7.js +2 -0
- package/src/ui/dist/assets/style-C3QI7VPf.css +1 -0
- package/src/ui/dist/assets/stylus-B533Al4x.js +2 -0
- package/src/ui/dist/assets/subset-shared.chunk-eAtLHmbi.js +85 -0
- package/src/ui/dist/assets/subset-worker.chunk-DeHCTnTk.js +2 -0
- package/src/ui/dist/assets/sv-SE-XGPEYMSR-BeOYCU-8.js +11 -0
- package/src/ui/dist/assets/swift-BzpIVaGY.js +2 -0
- package/src/ui/dist/assets/swimlanes-5IMT3BWC-DOy22UjP.js +3 -0
- package/src/ui/dist/assets/swimlanesDiagram-G3AALYLV-sicPbVOg.js +9 -0
- package/src/ui/dist/assets/ta-IN-2NMHFXQM-CtS68WDd.js +10 -0
- package/src/ui/dist/assets/tcl-DVfN8rqt.js +2 -0
- package/src/ui/dist/assets/{terminal-BBQvnOAK.js → terminal-DEby5Y_P.js} +2 -2
- package/src/ui/dist/assets/terminalControl-BkFeDVXo.js +134 -0
- package/src/ui/dist/assets/textile-CnDTJFAw.js +2 -0
- package/src/ui/dist/assets/th-TH-HPSO5L25-DCHY9-Co.js +3 -0
- package/src/ui/dist/assets/tiddlywiki-DO-Gjzrf.js +2 -0
- package/src/ui/dist/assets/tiki-DGYXhP31.js +2 -0
- package/src/ui/dist/assets/timeline-definition-FHXFAJF6-DVkG72qO.js +121 -0
- package/src/ui/dist/assets/toml-Bm5Em-hy.js +2 -0
- package/src/ui/dist/assets/tr-TR-DEFEU3FU-y5-vjTv8.js +8 -0
- package/src/ui/dist/assets/troff-wAsdV37c.js +2 -0
- package/src/ui/dist/assets/ttcn-CfJYG6tj.js +2 -0
- package/src/ui/dist/assets/ttcn-cfg-B9xdYoR4.js +2 -0
- package/src/ui/dist/assets/turtle-B1tBg_DP.js +2 -0
- package/src/ui/dist/assets/uk-UA-QMV73CPH-BRbR5It6.js +7 -0
- package/src/ui/dist/assets/useReducedMotion-D07XP52Q.js +12 -0
- package/src/ui/dist/assets/vb-CmGdzxic.js +2 -0
- package/src/ui/dist/assets/vbscript-BuJXcnF6.js +2 -0
- package/src/ui/dist/assets/velocity-D8B20fx6.js +2 -0
- package/src/ui/dist/assets/vennDiagram-L72KCM5P-DwMwBlf-.js +35 -0
- package/src/ui/dist/assets/verilog-C6RDOZhf.js +2 -0
- package/src/ui/dist/assets/vhdl-lSbBsy5d.js +2 -0
- package/src/ui/dist/assets/vi-VN-M7AON7JQ-s6wSBv2s.js +6 -0
- package/src/ui/dist/assets/wardleyDiagram-EHGQE667-CNuuvzEg.js +79 -0
- package/src/ui/dist/assets/webidl-ZXfAyPTL.js +2 -0
- package/src/ui/dist/assets/{x-DoIOGAJz.js → x-B84wq78k.js} +2 -2
- package/src/ui/dist/assets/xquery-DzFWVndE.js +2 -0
- package/src/ui/dist/assets/xychartDiagram-FW5EYKEG-dUpZxmn6.js +8 -0
- package/src/ui/dist/assets/yacas-BJ4BC0dw.js +2 -0
- package/src/ui/dist/assets/z80-Hz9HOZM7.js +2 -0
- package/src/ui/dist/assets/zh-CN-LNUGB5OW-Cu_-POQN.js +11 -0
- package/src/ui/dist/assets/zh-HK-E62DVLB3-BCx4W4qZ.js +2 -0
- package/src/ui/dist/assets/zh-TW-RAJ6MFWO-02wx_Mrj.js +10 -0
- package/src/ui/dist/icons/excalidraw.svg +1 -0
- package/src/ui/dist/icons/markdown.svg +1 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/package.json +10 -0
- package/src/ui/playwright.config.ts +36 -0
- package/src/ui/pnpm-workspace.yaml +2 -0
- package/src/ui/public/icons/excalidraw.svg +1 -0
- package/src/ui/public/icons/markdown.svg +1 -0
- package/src/ui/tsconfig.json +2 -1
- package/src/ui/vite.config.ts +1 -0
- package/src/wf-ui-server/__tests__/a2a-store.test.mjs +432 -15
- package/src/wf-ui-server/__tests__/action-registry-loader.test.mjs +156 -0
- package/src/wf-ui-server/__tests__/claude-stream-json.test.mjs +397 -0
- package/src/wf-ui-server/__tests__/codex-appserver.test.mjs +392 -0
- package/src/wf-ui-server/__tests__/component-node-api.test.mjs +881 -0
- package/src/wf-ui-server/__tests__/component-node-store.test.mjs +401 -0
- package/src/wf-ui-server/__tests__/control-plane-acceptance.test.mjs +2027 -0
- package/src/wf-ui-server/__tests__/control-plane-cli-smoke.test.mjs +1014 -0
- package/src/wf-ui-server/__tests__/dispatch-lifecycle.test.mjs +330 -0
- package/src/wf-ui-server/__tests__/effort-compatibility-red-092.test.mjs +151 -0
- package/src/wf-ui-server/__tests__/graph-layout-agent-tree.test.mjs +254 -0
- package/src/wf-ui-server/__tests__/graph-layout-tidy.test.mjs +260 -0
- package/src/wf-ui-server/__tests__/graph-layout.test.mjs +84 -0
- package/src/wf-ui-server/__tests__/node-config-api.test.mjs +384 -0
- package/src/wf-ui-server/__tests__/opencode-server.test.mjs +526 -0
- package/src/wf-ui-server/__tests__/peer-capsule.test.mjs +2 -2
- package/src/wf-ui-server/__tests__/pty-adapter.test.mjs +71 -1
- package/src/wf-ui-server/__tests__/runtime-config.test.mjs +3 -3
- package/src/wf-ui-server/__tests__/runtime-detector.test.mjs +21 -0
- package/src/wf-ui-server/__tests__/runtime-review.test.mjs +828 -0
- package/src/wf-ui-server/__tests__/server.integration.test.mjs +1463 -11
- package/src/wf-ui-server/__tests__/session-cleanup.test.mjs +159 -0
- package/src/wf-ui-server/__tests__/session-registry.test.mjs +52 -3
- package/src/wf-ui-server/__tests__/settings.test.mjs +12 -2
- package/src/wf-ui-server/__tests__/spawn-gate.test.mjs +78 -0
- package/src/wf-ui-server/__tests__/task-group-parser.test.mjs +120 -0
- package/src/wf-ui-server/__tests__/task-group-tag.test.mjs +264 -0
- package/src/wf-ui-server/__tests__/task-parser.test.mjs +49 -3
- package/src/wf-ui-server/__tests__/terminal-context-hardening.test.mjs +122 -0
- package/src/wf-ui-server/__tests__/terminal-context.test.mjs +164 -0
- package/src/wf-ui-server/__tests__/terminal-store.test.mjs +4 -4
- package/src/wf-ui-server/__tests__/wf-browser-api.test.mjs +599 -0
- package/src/wf-ui-server/__tests__/wf-browser-store.test.mjs +184 -0
- package/src/wf-ui-server/__tests__/workflow-action-registry.test.mjs +282 -0
- package/src/wf-ui-server/__tests__/workflow-agent-communication.test.mjs +411 -0
- package/src/wf-ui-server/__tests__/workflow-agent-context.test.mjs +323 -0
- package/src/wf-ui-server/__tests__/workflow-agent-cooperation-permissions.test.mjs +318 -0
- package/src/wf-ui-server/__tests__/workflow-agent-messages-structured.test.mjs +458 -0
- package/src/wf-ui-server/__tests__/workflow-agent-routing.test.mjs +434 -0
- package/src/wf-ui-server/__tests__/workflow-agent-runtime.test.mjs +324 -0
- package/src/wf-ui-server/__tests__/workflow-api-integration.test.mjs +263 -0
- package/src/wf-ui-server/__tests__/workflow-api-phase3.test.mjs +1213 -0
- package/src/wf-ui-server/__tests__/workflow-claude-materialize.test.mjs +209 -0
- package/src/wf-ui-server/__tests__/workflow-cli-registry-manuals.test.mjs +173 -0
- package/src/wf-ui-server/__tests__/workflow-cli-surface.test.mjs +176 -0
- package/src/wf-ui-server/__tests__/workflow-codex-resume-id.test.mjs +556 -0
- package/src/wf-ui-server/__tests__/workflow-cooperation-enforcement.test.mjs +553 -0
- package/src/wf-ui-server/__tests__/workflow-depth-resume-dock.test.mjs +846 -0
- package/src/wf-ui-server/__tests__/workflow-display-node.test.mjs +150 -0
- package/src/wf-ui-server/__tests__/workflow-edge-update.test.mjs +296 -0
- package/src/wf-ui-server/__tests__/workflow-file-cache-lock-watch.test.mjs +380 -0
- package/src/wf-ui-server/__tests__/workflow-file-formats.test.mjs +994 -0
- package/src/wf-ui-server/__tests__/workflow-file-node-actions.test.mjs +294 -0
- package/src/wf-ui-server/__tests__/workflow-goal-actions.test.mjs +357 -0
- package/src/wf-ui-server/__tests__/workflow-goal-task-autoclose.test.mjs +256 -0
- package/src/wf-ui-server/__tests__/workflow-graceful-stop.test.mjs +108 -0
- package/src/wf-ui-server/__tests__/workflow-graph-undo.test.mjs +524 -0
- package/src/wf-ui-server/__tests__/workflow-layout.test.mjs +732 -0
- package/src/wf-ui-server/__tests__/workflow-magnetic-topology.test.mjs +222 -0
- package/src/wf-ui-server/__tests__/workflow-markdown-lock.test.mjs +359 -0
- package/src/wf-ui-server/__tests__/workflow-mcp-hub.test.mjs +174 -0
- package/src/wf-ui-server/__tests__/workflow-node-manual-schema.test.mjs +241 -0
- package/src/wf-ui-server/__tests__/workflow-node-runtime.test.mjs +1991 -0
- package/src/wf-ui-server/__tests__/workflow-node-store-utils.test.mjs +125 -0
- package/src/wf-ui-server/__tests__/workflow-registry-drift.test.mjs +296 -0
- package/src/wf-ui-server/__tests__/workflow-resource-handle-four-side.test.mjs +217 -0
- package/src/wf-ui-server/__tests__/workflow-role-profile.test.mjs +234 -0
- package/src/wf-ui-server/__tests__/workflow-set-model.test.mjs +261 -0
- package/src/wf-ui-server/__tests__/workflow-skill-group-context-enabled.test.mjs +131 -0
- package/src/wf-ui-server/__tests__/workflow-skill-group-enable-disable.test.mjs +186 -0
- package/src/wf-ui-server/__tests__/workflow-skills-hub.test.mjs +125 -0
- package/src/wf-ui-server/__tests__/workflow-state-reconcile.test.mjs +139 -0
- package/src/wf-ui-server/__tests__/workflow-subagent-strategy-matrix.test.mjs +529 -0
- package/src/wf-ui-server/__tests__/workflow-team-flow.test.mjs +278 -0
- package/src/wf-ui-server/__tests__/workflow-terminal-control-live.test.mjs +294 -0
- package/src/wf-ui-server/__tests__/workflow-terminal-control.test.mjs +631 -0
- package/src/wf-ui-server/__tests__/workflow-timer-wakeup.test.mjs +318 -0
- package/src/wf-ui-server/__tests__/workflow-unified-resume.test.mjs +537 -0
- package/src/wf-ui-server/__tests__/workspace-api-hardening.test.mjs +187 -0
- package/src/wf-ui-server/__tests__/workspace-api.test.mjs +382 -0
- package/src/wf-ui-server/__tests__/workspace-reveal.test.mjs +138 -0
- package/src/wf-ui-server/__tests__/workspace-store-hardening.test.mjs +255 -0
- package/src/wf-ui-server/__tests__/workspace-store.test.mjs +208 -0
- package/src/wf-ui-server/__tests__/ws-events.integration.test.mjs +52 -25
- package/src/wf-ui-server/__tests__/ws-terminal.integration.test.mjs +12 -6
- package/src/wf-ui-server/__tests__/ws-wf-browser.integration.test.mjs +488 -0
- package/src/wf-ui-server/a2a-store.mjs +3007 -89
- package/src/wf-ui-server/action-registry.mjs +277 -0
- package/src/wf-ui-server/bridge-store.mjs +276 -0
- package/src/wf-ui-server/chat-driver.mjs +220 -0
- package/src/wf-ui-server/chat-store.mjs +109 -0
- package/src/wf-ui-server/claude-transcript-materializer.mjs +235 -0
- package/src/wf-ui-server/codex-update-prompt.mjs +53 -0
- package/src/wf-ui-server/component-node-store.mjs +689 -0
- package/src/wf-ui-server/composition-api.mjs +436 -0
- package/src/wf-ui-server/control-plane-token.mjs +17 -0
- package/src/wf-ui-server/dispatch-contract.mjs +150 -0
- package/src/wf-ui-server/drivers/claude-stream-json.mjs +371 -0
- package/src/wf-ui-server/drivers/codex-appserver.mjs +513 -0
- package/src/wf-ui-server/drivers/opencode-server.mjs +486 -0
- package/src/wf-ui-server/excalidraw-svg.mjs +141 -0
- package/src/wf-ui-server/file-format-adapters.mjs +511 -0
- package/src/wf-ui-server/file-watcher.mjs +215 -0
- package/src/wf-ui-server/graph-layout.mjs +778 -0
- package/src/wf-ui-server/harness-env.mjs +46 -0
- package/src/wf-ui-server/json-store-utils.mjs +48 -0
- package/src/wf-ui-server/model-capability.mjs +773 -0
- package/src/wf-ui-server/node-config-store.mjs +233 -0
- package/src/wf-ui-server/node-store-utils.mjs +88 -0
- package/src/wf-ui-server/peer-capsule.mjs +56 -1
- package/src/wf-ui-server/pty-adapter.mjs +513 -11
- package/src/wf-ui-server/reveal-foreground.mjs +69 -0
- package/src/wf-ui-server/runtime-detector.mjs +52 -6
- package/src/wf-ui-server/server.mjs +5613 -403
- package/src/wf-ui-server/session-cleanup.mjs +363 -0
- package/src/wf-ui-server/session-registry.mjs +179 -3
- package/src/wf-ui-server/settings.mjs +25 -0
- package/src/wf-ui-server/spawn-gate.mjs +46 -0
- package/src/wf-ui-server/task-parser.mjs +88 -3
- package/src/wf-ui-server/terminal-store.mjs +225 -11
- package/src/wf-ui-server/timer-wakeup-scheduler.mjs +226 -0
- package/src/wf-ui-server/wf-browser-store.mjs +733 -0
- package/src/wf-ui-server/workflow-agent-context.mjs +1731 -0
- package/src/wf-ui-server/workflow-capability-node-store.mjs +676 -0
- package/src/wf-ui-server/workflow-event-node-store.mjs +623 -0
- package/src/wf-ui-server/workflow-goal-node-store.mjs +328 -0
- package/src/wf-ui-server/workflow-graph-store.mjs +623 -0
- package/src/wf-ui-server/workflow-mcp-hub.mjs +286 -0
- package/src/wf-ui-server/workflow-node-runtime.mjs +1767 -0
- package/src/wf-ui-server/workflow-node-settings-store.mjs +339 -0
- package/src/wf-ui-server/workflow-node-types/agent-node.mjs +920 -0
- package/src/wf-ui-server/workflow-node-types/display-node.mjs +151 -0
- package/src/wf-ui-server/workflow-node-types/excalidraw-node.mjs +145 -0
- package/src/wf-ui-server/workflow-node-types/file-node.mjs +484 -0
- package/src/wf-ui-server/workflow-node-types/github-trigger-node.mjs +207 -0
- package/src/wf-ui-server/workflow-node-types/goal-node.mjs +491 -0
- package/src/wf-ui-server/workflow-node-types/markdown-node.mjs +225 -0
- package/src/wf-ui-server/workflow-node-types/mcp-connector-node.mjs +71 -0
- package/src/wf-ui-server/workflow-node-types/role-profile-store.mjs +191 -0
- package/src/wf-ui-server/workflow-node-types/skill-group-node.mjs +98 -0
- package/src/wf-ui-server/workflow-node-types/timer-node.mjs +607 -0
- package/src/wf-ui-server/workflow-ontology.mjs +681 -0
- package/src/wf-ui-server/workflow-operation-store.mjs +115 -0
- package/src/wf-ui-server/workflow-skills-hub.mjs +366 -0
- package/src/wf-ui-server/workflow-skills-market.mjs +723 -0
- package/src/wf-ui-server/workspace-store.mjs +951 -0
- package/src/wf-ui-server/ws-chat.mjs +299 -0
- package/src/wf-ui-server/ws-events.mjs +45 -20
- package/src/wf-ui-server/ws-terminal.mjs +165 -22
- package/src/wf-ui-server/ws-wf-browser.mjs +419 -0
- package/templates/common/.claude/agents/review-manager.md +33 -29
- package/templates/common/.claude/commands/wf-browser.md +1 -1
- package/templates/common/.claude/commands/wf-help.md +5 -3
- package/templates/common/.claude/commands/wf-init.md +44 -0
- package/templates/common/.claude/commands/wf-learn.md +34 -8
- package/templates/common/.claude/commands/wf-max.md +19 -9
- package/templates/common/.claude/commands/wf-review.md +3 -3
- package/templates/common/.claude/commands/wf-search.md +55 -0
- package/templates/common/.claude/commands/wf-task-archive.md +5 -4
- package/templates/common/.claude/commands/wf-task-list.md +6 -3
- package/templates/common/.claude/commands/wf-task-record.md +10 -2
- package/templates/common/.claude/commands/wf-ui.md +12 -5
- package/templates/common/.claude/commands/wf-update.md +4 -0
- package/templates/common/.claude/commands/wf.md +12 -1
- package/templates/common/.claude/rules/ecc/common.md +10 -1
- package/templates/common/.claude/skills/wf/SKILL.md +36 -0
- package/templates/common/.claude/skills/wf-browser/SKILL.md +427 -122
- package/templates/common/.claude/skills/wf-browser/references/BROWSER_CONTROL_PATTERNS.md +43 -0
- package/templates/common/.claude/skills/wf-init/SKILL.md +33 -0
- package/templates/common/.claude/skills/wf-learn/SKILL.md +106 -26
- package/templates/common/.claude/skills/wf-max/SKILL.md +45 -11
- package/templates/common/.claude/skills/wf-review/SKILL.md +26 -54
- package/templates/common/.claude/skills/wf-search/SKILL.md +122 -0
- package/templates/common/.claude/skills/wf-search/references/evidence-contract.md +77 -0
- package/templates/common/.claude/skills/wf-search/references/policies/compare.md +38 -0
- package/templates/common/.claude/skills/wf-search/references/policies/fact.md +39 -0
- package/templates/common/.claude/skills/wf-search/references/policies/troubleshoot.md +39 -0
- package/templates/common/.claude/skills/wf-search/references/policies/verify.md +44 -0
- package/templates/common/.claude/skills/wf-search/references/source-routing.md +40 -0
- package/templates/common/.claude/skills/wf-search/references/tool-adapters.md +38 -0
- package/templates/common/.claude/skills/wf-ui/SKILL.md +146 -12
- package/templates/common/.claude/skills/wf-update/SKILL.md +5 -1
- package/templates/common/.harness-version +188 -85
- package/templates/common/.opencode/agents/review-manager.md +31 -27
- package/templates/common/.opencode/commands/wf-browser.md +1 -1
- package/templates/common/.opencode/commands/wf-help.md +5 -3
- package/templates/common/.opencode/commands/wf-init.md +44 -0
- package/templates/common/.opencode/commands/wf-learn.md +34 -8
- package/templates/common/.opencode/commands/wf-max.md +19 -9
- package/templates/common/.opencode/commands/wf-review.md +3 -3
- package/templates/common/.opencode/commands/wf-search.md +55 -0
- package/templates/common/.opencode/commands/wf-task-archive.md +5 -7
- package/templates/common/.opencode/commands/wf-task-list.md +6 -6
- package/templates/common/.opencode/commands/wf-task-record.md +10 -5
- package/templates/common/.opencode/commands/wf-ui.md +12 -5
- package/templates/common/.opencode/commands/wf-update.md +4 -0
- package/templates/common/.opencode/commands/wf.md +12 -1
- package/templates/common/CLAUDE.md +61 -3
- package/templates/common/Harness/MEMORY.md +8 -5
- package/templates/common/Harness/README.md +14 -10
- package/templates/common/Harness/a2a/action-registry.json +4397 -0
- package/templates/common/Harness/a2a/role-graph.json +16 -3
- package/templates/common/Harness/a2a/skills/terminal-control.json +45 -1
- package/templates/common/Harness/a2a/skills/wf-ui-map.json +50 -0
- package/templates/common/Harness/a2a/skills/workflow-agent-node.json +143 -0
- package/templates/common/Harness/a2a/skills/workflow-context.json +95 -0
- package/templates/common/Harness/a2a/skills/workflow-diagram-node.json +60 -0
- package/templates/common/Harness/a2a/skills/workflow-display-node.json +69 -0
- package/templates/common/Harness/a2a/skills/workflow-file-node.json +92 -0
- package/templates/common/Harness/a2a/skills/workflow-goal-node.json +76 -0
- package/templates/common/Harness/a2a/skills/workflow-markdown-node.json +79 -0
- package/templates/common/Harness/a2a/skills/workflow-mcp-connector-node.json +59 -0
- package/templates/common/Harness/a2a/skills/workflow-node-actions.json +105 -0
- package/templates/common/Harness/a2a/skills/workflow-node-map.json +71 -0
- package/templates/common/Harness/a2a/skills/workflow-ontology.json +54 -0
- package/templates/common/Harness/a2a/skills/workflow-resource-node.json +68 -0
- package/templates/common/Harness/a2a/skills/workflow-skill-group-node.json +56 -0
- package/templates/common/Harness/a2a/skills/workflow-timer-node.json +81 -0
- package/templates/common/Harness/a2a/workflows.json +33 -0
- package/templates/common/Harness/ownership.manifest.json +263 -2
- package/templates/common/Harness/project/architecture.md +499 -0
- package/templates/common/Harness/research/README.md +20 -0
- package/templates/common/Harness/scripts/README.md +70 -6
- package/templates/common/Harness/scripts/a2a-terminal.mjs +12 -4
- package/templates/common/Harness/scripts/context-budget.mjs +7 -3
- package/templates/common/Harness/scripts/lib/memory-context.mjs +400 -0
- package/templates/common/Harness/scripts/lib/path-scope.mjs +77 -0
- package/templates/common/Harness/scripts/lib/research-policy.mjs +99 -0
- package/templates/common/Harness/scripts/lib/wf-search.mjs +189 -0
- package/templates/common/Harness/scripts/memory-context.mjs +63 -0
- package/templates/common/Harness/scripts/research-policy.mjs +27 -0
- package/templates/common/Harness/scripts/task-context.mjs +547 -0
- package/templates/common/Harness/scripts/task-group-index.mjs +242 -0
- package/templates/common/Harness/scripts/task-state.mjs +659 -95
- package/templates/common/Harness/scripts/validate-harness.mjs +70 -28
- package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +24 -0
- package/templates/common/Harness/scripts/wf-remove.mjs +10 -0
- package/templates/common/Harness/scripts/wf-search.mjs +10 -0
- package/templates/common/Harness/scripts/wf-ui-control.mjs +3144 -0
- package/templates/common/Harness/scripts/wf-update-check.mjs +274 -51
- package/templates/common/Harness/scripts/wf-update-runner.mjs +53 -16
- package/templates/common/Harness/settings.json +10 -0
- package/templates/common/Harness/specs/guides/SETUP.md +4 -3
- package/templates/common/Harness/specs/protocols/HARNESS_BRIDGE.md +429 -48
- package/templates/common/Harness/specs/protocols/MEMORY_PROTOCOL.md +24 -0
- package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +14 -11
- package/templates/common/Harness/specs/runtime/command-surface.json +28 -0
- package/templates/common/Harness/specs/runtime/context-loading.md +6 -0
- package/templates/common/Harness/specs/runtime/dispatch.md +9 -1
- package/templates/common/Harness/specs/runtime/subagents.md +20 -5
- package/templates/common/Harness/specs/workflows/WF-AUTO-SPARK.md +19 -0
- package/templates/common/Harness/specs/workflows/WF-AUTO.md +21 -0
- package/templates/common/Harness/specs/workflows/WF-KERNEL.md +4 -2
- package/templates/common/Harness/specs/workflows/WF-MAX.md +39 -12
- package/templates/common/Harness/specs/workflows/WF-STATE.md +62 -14
- package/templates/common/Harness/specs/workflows/WF.md +32 -0
- package/templates/common/Harness/tasks/INDEX.json +7 -0
- package/templates/common/Harness/tasks/INDEX.md +12 -0
- package/templates/common/Harness/tasks/_template/PLAN.md +6 -0
- package/templates/common/Harness/tasks/_template/PROGRESS.md +5 -0
- package/templates/common/Harness/tasks/_template/STATE.json +22 -0
- package/templates/common/Harness/templates/TEST_PLAN.template.md +5 -4
- package/templates/common/Harness/templates/UI_CONTRACT.template.md +7 -5
- package/templates/common/Harness/wf-browser/README.md +69 -0
- package/templates/common/README.md +17 -0
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js +0 -17
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js.map +0 -1
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js +0 -7
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js.map +0 -1
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js +0 -12
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js.map +0 -1
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js +0 -50
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js.map +0 -1
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js +0 -74
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js.map +0 -1
- package/src/ui/dist/assets/WorkflowRoute-BnuhLJ6X.css +0 -1
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js +0 -39
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js.map +0 -1
- package/src/ui/dist/assets/index-BOVYWntB.js +0 -117
- package/src/ui/dist/assets/index-BOVYWntB.js.map +0 -1
- package/src/ui/dist/assets/index-DaUObq0H.css +0 -1
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js +0 -7
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js.map +0 -1
- package/src/ui/dist/assets/plus-QuTJyoT3.js +0 -7
- package/src/ui/dist/assets/plus-QuTJyoT3.js.map +0 -1
- package/src/ui/dist/assets/proxy-D0vUbds5.js.map +0 -1
- package/src/ui/dist/assets/refresh-cw-JNmExijd.js.map +0 -1
- package/src/ui/dist/assets/terminal-BBQvnOAK.js.map +0 -1
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js +0 -7
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js.map +0 -1
- package/src/ui/dist/assets/x-DoIOGAJz.js.map +0 -1
- package/src/ui/pnpm-lock.yaml +0 -1582
- package/src/ui/src/App.tsx +0 -86
- package/src/ui/src/api.ts +0 -93
- package/src/ui/src/components/AgentsRoute.tsx +0 -503
- package/src/ui/src/components/Footer.tsx +0 -69
- package/src/ui/src/components/Header.tsx +0 -175
- package/src/ui/src/components/LoadingView.tsx +0 -22
- package/src/ui/src/components/RolesRoute.tsx +0 -169
- package/src/ui/src/components/SettingsRoute.tsx +0 -166
- package/src/ui/src/components/TaskList.tsx +0 -388
- package/src/ui/src/components/TerminalDrawer.tsx +0 -611
- package/src/ui/src/components/WorkflowRoute.tsx +0 -670
- package/src/ui/src/hooks/useReducedMotion.ts +0 -17
- package/src/ui/src/hooks/useServerConnection.ts +0 -114
- package/src/ui/src/index.css +0 -281
- package/src/ui/src/main.tsx +0 -11
- package/src/ui/src/types.ts +0 -108
- /package/src/ui/dist/assets/{TerminalDrawer-6GBZ9nXN.css → terminalControl-6GBZ9nXN.css} +0 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
// file-format-adapters.mjs
|
|
2
|
+
//
|
|
3
|
+
// Stateless parsing adapters for Excel / PDF / ZIP workspace files
|
|
4
|
+
// (task-upgrade-file-node AC-5..AC-7, PLAN D4).
|
|
5
|
+
//
|
|
6
|
+
// Contract:
|
|
7
|
+
// - Pure functions: no module state, no caching; every call re-reads the
|
|
8
|
+
// file. File access is async (fs/promises) and zip payloads are streamed —
|
|
9
|
+
// never the whole archive in memory.
|
|
10
|
+
// - Error contract: corrupt/unreadable files throw ComponentNodeError with
|
|
11
|
+
// a machine-readable code (XLSX_PARSE / PDF_PARSE / ZIP_PARSE /
|
|
12
|
+
// ZIP_ENTRY_NOT_TEXT / ZIP_SLIP / ZIP_ENTRY_NOT_FOUND / ZIP_INVALID_NAME /
|
|
13
|
+
// FILE_TOO_LARGE / FILE_EXISTS / PAYLOAD_TOO_LARGE) and a 4xx
|
|
14
|
+
// statusCode, matching the ComponentNodeError precedent
|
|
15
|
+
// (markdown_locked, 409). Message text is never asserted by tests — only
|
|
16
|
+
// err.code and err.statusCode.
|
|
17
|
+
// - Parser libraries are imported dynamically so loading this module stays
|
|
18
|
+
// cheap and unpdf's pdfjs bundle is only fetched on first PDF call.
|
|
19
|
+
|
|
20
|
+
import fs from 'node:fs/promises'
|
|
21
|
+
import { createWriteStream, existsSync, rmSync } from 'node:fs'
|
|
22
|
+
import path from 'node:path'
|
|
23
|
+
import { ComponentNodeError } from './component-node-store.mjs'
|
|
24
|
+
|
|
25
|
+
// Text-like extensions allowed for readZipEntry previews. Everything else is
|
|
26
|
+
// refused with ZIP_ENTRY_NOT_TEXT (binary previews are out of scope).
|
|
27
|
+
const TEXT_ENTRY_EXTENSIONS = new Set([
|
|
28
|
+
'.txt', '.md', '.json', '.js', '.ts', '.css', '.html', '.csv', '.log', '.yaml', '.yml',
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
// W4F hardening limits (task-upgrade-file-node security review):
|
|
32
|
+
// - MAX_FORMAT_FILE_BYTES: files larger than this are refused before any
|
|
33
|
+
// xlsx/pdf parser is touched (M3; injectable per call for tests).
|
|
34
|
+
// - MAX_ZIP_PREVIEW_BYTES: hard ceiling for a single readZipEntry preview —
|
|
35
|
+
// maxBytes is clamped to this, and entries whose central-directory size
|
|
36
|
+
// exceeds it are refused without opening a read stream (H1).
|
|
37
|
+
// - MAX_ZIP_ENTRIES: readZipEntries stops scanning after this many entries
|
|
38
|
+
// and reports truncated:true (M2).
|
|
39
|
+
// - MAX_EXTRACT_ENTRY_BYTES: extractZipEntry refuses entries whose
|
|
40
|
+
// uncompressed size exceeds this before writing any byte (M1).
|
|
41
|
+
export const MAX_FORMAT_FILE_BYTES = 100 * 1024 * 1024
|
|
42
|
+
export const MAX_ZIP_PREVIEW_BYTES = 1 * 1024 * 1024
|
|
43
|
+
export const MAX_ZIP_ENTRIES = 10000
|
|
44
|
+
export const MAX_EXTRACT_ENTRY_BYTES = 64 * 1024 * 1024
|
|
45
|
+
|
|
46
|
+
// H1: hard-clamp a caller-supplied byte budget. Non-finite numbers
|
|
47
|
+
// (Infinity, 1e999, NaN) fall back to the 64KB default; finite numbers are
|
|
48
|
+
// capped at MAX_ZIP_PREVIEW_BYTES so no caller can raise the preview budget
|
|
49
|
+
// beyond 1MB (Math.max(1, Math.floor(n) || 65536) failed to clamp those).
|
|
50
|
+
function clampPreviewBytes(value) {
|
|
51
|
+
const n = Number(value)
|
|
52
|
+
if (!Number.isFinite(n)) return 65536
|
|
53
|
+
return Math.min(Math.max(1, Math.floor(n)), MAX_ZIP_PREVIEW_BYTES)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// W5F-R1: hard-clamp a caller-supplied format-gate budget (xlsx/pdf size
|
|
57
|
+
// gate). Same finite-ness logic as clampPreviewBytes, but the ceiling is
|
|
58
|
+
// MAX_FORMAT_FILE_BYTES (100MB), not the 1MB preview cap: an absurd budget
|
|
59
|
+
// (Infinity, 1e999, NaN, 'abc') falls back to the 100MB cap so it can never
|
|
60
|
+
// disable the size gate, and no caller can raise the gate beyond 100MB.
|
|
61
|
+
export function clampFormatMaxBytes(value) {
|
|
62
|
+
const n = Number(value)
|
|
63
|
+
if (!Number.isFinite(n)) return MAX_FORMAT_FILE_BYTES
|
|
64
|
+
return Math.min(Math.max(1, Math.floor(n)), MAX_FORMAT_FILE_BYTES)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// M3: refuse oversized xlsx/pdf files before any parser library runs. The
|
|
68
|
+
// limit is injectable (per-call maxBytes) so tests can exercise the rejection
|
|
69
|
+
// without a 100MB fixture; production callers default to MAX_FORMAT_FILE_BYTES.
|
|
70
|
+
async function assertParseFileSizeWithin(filePath, maxBytes) {
|
|
71
|
+
const stat = await fs.stat(filePath)
|
|
72
|
+
if (stat.size > maxBytes) {
|
|
73
|
+
throw new ComponentNodeError(
|
|
74
|
+
`File exceeds the format parsing size limit: ${stat.size} bytes`,
|
|
75
|
+
{ statusCode: 400, code: 'FILE_TOO_LARGE' },
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseError(message, code) {
|
|
81
|
+
return new ComponentNodeError(message, { statusCode: 400, code })
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Excel (.xlsx) ──────────────────────────────────────────────────────────
|
|
85
|
+
// read-excel-file reads the whole workbook into memory for sheet discovery;
|
|
86
|
+
// that is accepted for xlsx (PLAN D4). readXlsxSheet still pages the parsed
|
|
87
|
+
// rows instead of returning the whole sheet.
|
|
88
|
+
|
|
89
|
+
export async function readXlsxSheets(filePath, { maxBytes = MAX_FORMAT_FILE_BYTES } = {}) {
|
|
90
|
+
try {
|
|
91
|
+
// W5F-R1: the gate budget goes through clampFormatMaxBytes so an absurd
|
|
92
|
+
// caller value (1e999 / 'abc') can never fail-open the size gate.
|
|
93
|
+
await assertParseFileSizeWithin(filePath, clampFormatMaxBytes(maxBytes))
|
|
94
|
+
const { default: readXlsxFile } = await import('read-excel-file/node')
|
|
95
|
+
const sheets = await readXlsxFile(filePath)
|
|
96
|
+
return { sheets: sheets.map((sheet) => String(sheet.sheet)) }
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (error instanceof ComponentNodeError) throw error
|
|
99
|
+
throw parseError(`Failed to parse Excel file: ${error.message}`, 'XLSX_PARSE')
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Reads one sheet (name or 1-based index; default first sheet). The returned
|
|
104
|
+
// `rows` array keeps the column header as its first row followed by the data
|
|
105
|
+
// rows of the requested page, so a caller can split headers = rows[0],
|
|
106
|
+
// data = rows.slice(1). Pagination slices the data rows only.
|
|
107
|
+
export async function readXlsxSheet(filePath, { sheet, page = 1, pageSize = 100, maxBytes = MAX_FORMAT_FILE_BYTES } = {}) {
|
|
108
|
+
try {
|
|
109
|
+
await assertParseFileSizeWithin(filePath, clampFormatMaxBytes(maxBytes))
|
|
110
|
+
const { readSheet } = await import('read-excel-file/node')
|
|
111
|
+
const sheetRef = sheet === undefined || sheet === null ? 1 : sheet
|
|
112
|
+
const rows = await readSheet(filePath, sheetRef)
|
|
113
|
+
const parsedPage = Math.max(1, Math.floor(Number(page) || 1))
|
|
114
|
+
const parsedPageSize = Math.max(1, Math.floor(Number(pageSize) || 100))
|
|
115
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
116
|
+
return { sheet: sheetRef, rows: [], totalPages: 0, page: parsedPage, pageSize: parsedPageSize }
|
|
117
|
+
}
|
|
118
|
+
const header = rows[0]
|
|
119
|
+
const dataRows = rows.slice(1)
|
|
120
|
+
const totalPages = Math.ceil(dataRows.length / parsedPageSize)
|
|
121
|
+
const start = (parsedPage - 1) * parsedPageSize
|
|
122
|
+
return {
|
|
123
|
+
sheet: sheetRef,
|
|
124
|
+
rows: [header, ...dataRows.slice(start, start + parsedPageSize)],
|
|
125
|
+
totalPages,
|
|
126
|
+
page: parsedPage,
|
|
127
|
+
pageSize: parsedPageSize,
|
|
128
|
+
}
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (error instanceof ComponentNodeError) throw error
|
|
131
|
+
throw parseError(`Failed to parse Excel file: ${error.message}`, 'XLSX_PARSE')
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── PDF ────────────────────────────────────────────────────────────────────
|
|
136
|
+
// unpdf requires a Uint8Array; the file is read whole (pdfjs needs the full
|
|
137
|
+
// document to build its xref). Rendering/text extraction runs with
|
|
138
|
+
// isEvalSupported:false — no JS evaluation is ever enabled (AC-6 baseline).
|
|
139
|
+
|
|
140
|
+
export async function readPdfInfo(filePath, { maxBytes = MAX_FORMAT_FILE_BYTES } = {}) {
|
|
141
|
+
try {
|
|
142
|
+
await assertParseFileSizeWithin(filePath, clampFormatMaxBytes(maxBytes))
|
|
143
|
+
const { getDocumentProxy, getMeta } = await import('unpdf')
|
|
144
|
+
const bytes = await fs.readFile(filePath)
|
|
145
|
+
const proxy = await getDocumentProxy(new Uint8Array(bytes), { isEvalSupported: false })
|
|
146
|
+
const { info } = await getMeta(proxy)
|
|
147
|
+
const meta = {}
|
|
148
|
+
for (const [key, value] of Object.entries(info || {})) {
|
|
149
|
+
if (value === null || value === undefined) continue
|
|
150
|
+
meta[key.charAt(0).toLowerCase() + key.slice(1)] = value
|
|
151
|
+
}
|
|
152
|
+
return { pages: proxy.numPages, meta }
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (error instanceof ComponentNodeError) throw error
|
|
155
|
+
throw parseError(`Failed to parse PDF file: ${error.message}`, 'PDF_PARSE')
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Extracts ONLY the requested page range as concatenated text (M3). The
|
|
160
|
+
// document proxy stays open and pdf.getPage(n).getTextContent() is called per
|
|
161
|
+
// page in the range — extractText is never used, so a huge document's text is
|
|
162
|
+
// never materialized just to answer a one-page read.
|
|
163
|
+
export const PDF_PAGE_COUNT_CAP = 100
|
|
164
|
+
export async function readPdfPageText(filePath, { page = 1, pageCount = 1, maxBytes = MAX_FORMAT_FILE_BYTES } = {}) {
|
|
165
|
+
try {
|
|
166
|
+
await assertParseFileSizeWithin(filePath, clampFormatMaxBytes(maxBytes))
|
|
167
|
+
const { getDocumentProxy } = await import('unpdf')
|
|
168
|
+
const bytes = await fs.readFile(filePath)
|
|
169
|
+
const proxy = await getDocumentProxy(new Uint8Array(bytes), { isEvalSupported: false })
|
|
170
|
+
const start = Math.min(Math.max(1, Math.floor(Number(page) || 1)), proxy.numPages)
|
|
171
|
+
// W5F-R1: the requested page range is capped — 1e9 can no longer force
|
|
172
|
+
// whole-document extraction; the response echoes the clamped count.
|
|
173
|
+
const count = Math.min(Math.max(1, Math.floor(Number(pageCount) || 1)), PDF_PAGE_COUNT_CAP)
|
|
174
|
+
const end = Math.min(proxy.numPages, start + count - 1)
|
|
175
|
+
const parts = []
|
|
176
|
+
for (let pageNumber = start; pageNumber <= end; pageNumber += 1) {
|
|
177
|
+
const pageProxy = await proxy.getPage(pageNumber)
|
|
178
|
+
const content = await pageProxy.getTextContent()
|
|
179
|
+
parts.push(content.items.map((item) => (item && typeof item.str === 'string' ? item.str : '')).join(' '))
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
page: start,
|
|
183
|
+
pageCount: count,
|
|
184
|
+
text: parts.join('\n'),
|
|
185
|
+
isEvalSupported: false,
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error instanceof ComponentNodeError) throw error
|
|
189
|
+
throw parseError(`Failed to parse PDF file: ${error.message}`, 'PDF_PARSE')
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── ZIP ────────────────────────────────────────────────────────────────────
|
|
194
|
+
// yauzl lazyEntries + decodeStrings:false: the central directory is walked
|
|
195
|
+
// entry by entry — no whole-archive read. Entry names are decoded UTF-8 when
|
|
196
|
+
// the general-purpose bit 11 (fileNameIsUTF8) is set, otherwise GBK
|
|
197
|
+
// (iconv-lite) so legacy Chinese-named archives are readable.
|
|
198
|
+
|
|
199
|
+
async function openZip(filePath, options) {
|
|
200
|
+
const { default: yauzl } = await import('yauzl')
|
|
201
|
+
return new Promise((resolve, reject) => {
|
|
202
|
+
// validateEntrySizes defaults to FALSE here: yauzl 2.10.0's
|
|
203
|
+
// AssertByteCountStream swallows the over-limit callback error on Node 24
|
|
204
|
+
// and hangs forever on forged uncompressedSize entries. Size enforcement
|
|
205
|
+
// is instead owned by our own declared-value pre-checks + stream byte
|
|
206
|
+
// counters (stronger, and they reject instead of hanging).
|
|
207
|
+
yauzl.open(filePath, { lazyEntries: true, decodeStrings: false, autoClose: false, validateEntrySizes: false, ...options }, (err, zipfile) => {
|
|
208
|
+
if (err) reject(err)
|
|
209
|
+
else resolve(zipfile)
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function isUtf8Name(entry) {
|
|
215
|
+
// yauzl does not expose a fileNameIsUTF8 property; the UTF-8 flag is
|
|
216
|
+
// general-purpose bit 11 of the entry's central-directory bit flag.
|
|
217
|
+
return (entry.generalPurposeBitFlag & 0x800) !== 0
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function decodeEntryName(entry, iconv) {
|
|
221
|
+
const nameBuffer = Buffer.isBuffer(entry.fileName)
|
|
222
|
+
? entry.fileName
|
|
223
|
+
: Buffer.from(String(entry.fileName || ''), 'utf8')
|
|
224
|
+
if (isUtf8Name(entry)) return nameBuffer.toString('utf8')
|
|
225
|
+
return iconv.decode(nameBuffer, 'gbk')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function closeZip(zipfile) {
|
|
229
|
+
try {
|
|
230
|
+
zipfile.close()
|
|
231
|
+
} catch {
|
|
232
|
+
// Already closed or failed to open; nothing else to clean up.
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Scan (lazily) for the entry whose decoded name equals targetName.
|
|
237
|
+
function findZipEntry(zipfile, targetName, iconv) {
|
|
238
|
+
return new Promise((resolve, reject) => {
|
|
239
|
+
zipfile.on('entry', (entry) => {
|
|
240
|
+
const name = decodeEntryName(entry, iconv)
|
|
241
|
+
if (name === targetName) return resolve(entry)
|
|
242
|
+
zipfile.readEntry()
|
|
243
|
+
})
|
|
244
|
+
zipfile.on('end', () => reject(
|
|
245
|
+
new ComponentNodeError(`ZIP entry not found: ${targetName}`, {
|
|
246
|
+
statusCode: 404,
|
|
247
|
+
code: 'ZIP_ENTRY_NOT_FOUND',
|
|
248
|
+
}),
|
|
249
|
+
))
|
|
250
|
+
zipfile.on('error', reject)
|
|
251
|
+
zipfile.readEntry()
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export async function readZipEntries(filePath, { maxEntries = MAX_ZIP_ENTRIES } = {}) {
|
|
256
|
+
// M2: the scan budget is caller-injectable for tests but can never be raised
|
|
257
|
+
// above MAX_ZIP_ENTRIES — a huge archive stops being scanned once the cap is
|
|
258
|
+
// hit and the result reports truncated:true instead of a full listing.
|
|
259
|
+
const limit = Math.min(Math.max(1, Math.floor(Number(maxEntries) || MAX_ZIP_ENTRIES)), MAX_ZIP_ENTRIES)
|
|
260
|
+
let zipfile = null
|
|
261
|
+
try {
|
|
262
|
+
const { default: iconv } = await import('iconv-lite')
|
|
263
|
+
zipfile = await openZip(filePath)
|
|
264
|
+
const { entries, truncated } = await new Promise((resolve, reject) => {
|
|
265
|
+
const list = []
|
|
266
|
+
let truncated = false
|
|
267
|
+
zipfile.on('entry', (entry) => {
|
|
268
|
+
if (list.length >= limit) {
|
|
269
|
+
// Cap reached: stop driving the scan and report the truncated list.
|
|
270
|
+
truncated = true
|
|
271
|
+
return resolve({ entries: list, truncated })
|
|
272
|
+
}
|
|
273
|
+
const name = decodeEntryName(entry, iconv)
|
|
274
|
+
list.push({
|
|
275
|
+
name,
|
|
276
|
+
size: entry.uncompressedSize,
|
|
277
|
+
compressedSize: entry.compressedSize,
|
|
278
|
+
directory: name.endsWith('/') || ((entry.externalFileAttributes >>> 16) & 0x10) !== 0,
|
|
279
|
+
isUtf8: isUtf8Name(entry),
|
|
280
|
+
})
|
|
281
|
+
zipfile.readEntry()
|
|
282
|
+
})
|
|
283
|
+
zipfile.on('end', () => resolve({ entries: list, truncated }))
|
|
284
|
+
zipfile.on('error', reject)
|
|
285
|
+
zipfile.readEntry()
|
|
286
|
+
})
|
|
287
|
+
return { entries, truncated, total: truncated ? null : entries.length }
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (error instanceof ComponentNodeError) throw error
|
|
290
|
+
throw parseError(`Failed to parse ZIP archive: ${error.message}`, 'ZIP_PARSE')
|
|
291
|
+
} finally {
|
|
292
|
+
if (zipfile) closeZip(zipfile)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Stream one entry's content with a maxBytes cap; once the cap is reached the
|
|
297
|
+
// stream is destroyed and the remaining payload is never read.
|
|
298
|
+
function streamEntry(zipfile, entry, maxBytes) {
|
|
299
|
+
return new Promise((resolve, reject) => {
|
|
300
|
+
zipfile.openReadStream(entry, (err, stream) => {
|
|
301
|
+
if (err) return reject(err)
|
|
302
|
+
const chunks = []
|
|
303
|
+
let bytes = 0
|
|
304
|
+
let truncated = false
|
|
305
|
+
let settled = false
|
|
306
|
+
const settle = (error, value) => {
|
|
307
|
+
if (settled) return
|
|
308
|
+
settled = true
|
|
309
|
+
if (error) reject(error)
|
|
310
|
+
else resolve(value)
|
|
311
|
+
}
|
|
312
|
+
stream.on('data', (chunk) => {
|
|
313
|
+
if (settled) return
|
|
314
|
+
const remaining = maxBytes - bytes
|
|
315
|
+
if (remaining <= 0) {
|
|
316
|
+
truncated = true
|
|
317
|
+
stream.destroy()
|
|
318
|
+
settle(null, { text: Buffer.concat(chunks).toString('utf8'), truncated, bytes })
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
const slice = chunk.length > remaining ? chunk.subarray(0, remaining) : chunk
|
|
322
|
+
chunks.push(slice)
|
|
323
|
+
bytes += slice.length
|
|
324
|
+
if (bytes >= maxBytes) {
|
|
325
|
+
// yauzl's destroy() on the inflate-filter stream never emits
|
|
326
|
+
// 'close', so settle right here; the settle guard absorbs any late
|
|
327
|
+
// 'error'/'end' that destroy may still trigger.
|
|
328
|
+
truncated = true
|
|
329
|
+
stream.destroy()
|
|
330
|
+
settle(null, { text: Buffer.concat(chunks).toString('utf8'), truncated, bytes })
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
stream.on('end', () => settle(null, { text: Buffer.concat(chunks).toString('utf8'), truncated, bytes }))
|
|
334
|
+
stream.on('error', settle)
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export async function readZipEntryText(filePath, entryName, { maxBytes = 65536 } = {}) {
|
|
340
|
+
const name = String(entryName || '')
|
|
341
|
+
const extension = path.extname(name).toLowerCase()
|
|
342
|
+
if (!TEXT_ENTRY_EXTENSIONS.has(extension)) {
|
|
343
|
+
throw new ComponentNodeError(
|
|
344
|
+
`ZIP entry is not a text file (extension ${extension || 'none'} is not supported for preview): ${name}`,
|
|
345
|
+
{ statusCode: 400, code: 'ZIP_ENTRY_NOT_TEXT' },
|
|
346
|
+
)
|
|
347
|
+
}
|
|
348
|
+
// H1: the caller-visible budget is the clamped limit; the response echoes it
|
|
349
|
+
// as maxBytes so callers can verify the clamp took effect (1e999 / 1e15 /
|
|
350
|
+
// Infinity can never raise the effective budget above 1MB).
|
|
351
|
+
const limit = clampPreviewBytes(maxBytes)
|
|
352
|
+
let zipfile = null
|
|
353
|
+
try {
|
|
354
|
+
const { default: iconv } = await import('iconv-lite')
|
|
355
|
+
zipfile = await openZip(filePath)
|
|
356
|
+
const entry = await findZipEntry(zipfile, name, iconv)
|
|
357
|
+
// H1: an entry whose central-directory size already exceeds the hard cap
|
|
358
|
+
// can never fit the preview budget — return the truncation marker without
|
|
359
|
+
// opening (or decompressing) the read stream at all. Entries up to the
|
|
360
|
+
// cap are streamed and destroyed at `limit`, so memory stays bounded
|
|
361
|
+
// either way.
|
|
362
|
+
if (entry.uncompressedSize > MAX_ZIP_PREVIEW_BYTES) {
|
|
363
|
+
return { entryName: name, text: '', truncated: true, bytes: 0, maxBytes: limit }
|
|
364
|
+
}
|
|
365
|
+
const result = await streamEntry(zipfile, entry, limit)
|
|
366
|
+
return { entryName: name, text: result.text, truncated: result.truncated, bytes: result.bytes, maxBytes: limit }
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (error instanceof ComponentNodeError) throw error
|
|
369
|
+
throw parseError(`Failed to parse ZIP archive: ${error.message}`, 'ZIP_PARSE')
|
|
370
|
+
} finally {
|
|
371
|
+
if (zipfile) closeZip(zipfile)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Zip Slip guard: entry names with ../ segments, backslash ../ segments, or
|
|
376
|
+
// absolute paths must be rejected before anything is written. destDir is
|
|
377
|
+
// caller-validated to stay inside the workspace (file-node action resolves it
|
|
378
|
+
// through the workspace boundary); the target filename is basename-only so a
|
|
379
|
+
// nested entry lands flat inside destDir.
|
|
380
|
+
function assertSafeEntryName(entryName) {
|
|
381
|
+
const name = String(entryName || '')
|
|
382
|
+
if (!name) return name
|
|
383
|
+
// M1: NTFS Alternate Data Streams are rejected wherever the ':' appears —
|
|
384
|
+
// before any path.basename() on win32 (which treats ':' as a separator) can
|
|
385
|
+
// hide or re-target the payload.
|
|
386
|
+
if (name.includes(':')) {
|
|
387
|
+
throw new ComponentNodeError(
|
|
388
|
+
`Zip entry name with a colon (NTFS ADS) is not allowed: ${name}`,
|
|
389
|
+
{ statusCode: 400, code: 'ZIP_INVALID_NAME' },
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
const normalized = name.replace(/\\/g, '/')
|
|
393
|
+
const isAbsolute = path.isAbsolute(name) || /^[a-zA-Z]:/.test(name) || normalized.startsWith('/')
|
|
394
|
+
const hasTraversal = normalized.split('/').includes('..')
|
|
395
|
+
if (isAbsolute || hasTraversal) {
|
|
396
|
+
throw new ComponentNodeError(
|
|
397
|
+
`Zip Slip traversal rejected: entry name must stay inside the archive (${name})`,
|
|
398
|
+
{ statusCode: 400, code: 'ZIP_SLIP' },
|
|
399
|
+
)
|
|
400
|
+
}
|
|
401
|
+
return name
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export async function extractZipEntry(filePath, entryName, destDir, { maxBytes = MAX_EXTRACT_ENTRY_BYTES } = {}) {
|
|
405
|
+
const name = assertSafeEntryName(entryName)
|
|
406
|
+
// W5F-R2: the effective extract budget is the caller-injectable limit,
|
|
407
|
+
// cap-the-capped at MAX_EXTRACT_ENTRY_BYTES — an absurd budget (1e999, NaN)
|
|
408
|
+
// falls back to the 64MB hard cap and can never disable the gate.
|
|
409
|
+
const limit = Math.min(
|
|
410
|
+
Math.max(1, Math.floor(Number(maxBytes) || MAX_EXTRACT_ENTRY_BYTES)),
|
|
411
|
+
MAX_EXTRACT_ENTRY_BYTES,
|
|
412
|
+
)
|
|
413
|
+
let zipfile = null
|
|
414
|
+
let outPath = null
|
|
415
|
+
let writeStarted = false
|
|
416
|
+
let writeStream = null
|
|
417
|
+
try {
|
|
418
|
+
const { default: iconv } = await import('iconv-lite')
|
|
419
|
+
await fs.mkdir(destDir, { recursive: true })
|
|
420
|
+
// W5F-R2: yauzl's validateEntrySizes (AssertByteCountStream) swallows the
|
|
421
|
+
// over-limit callback error on Node 24 and hangs forever when an entry's
|
|
422
|
+
// declared uncompressed size lies below its real payload. Size enforcement
|
|
423
|
+
// is fully covered by our own declared-size precheck plus the streamed
|
|
424
|
+
// byte count below, so yauzl's check is redundant here — disable it.
|
|
425
|
+
zipfile = await openZip(filePath, { validateEntrySizes: false })
|
|
426
|
+
const entry = await findZipEntry(zipfile, name, iconv)
|
|
427
|
+
outPath = path.join(destDir, path.basename(name))
|
|
428
|
+
// M1: extraction never silently overwrites an existing target
|
|
429
|
+
// (requireMissing semantics) — the caller must pick another destDir.
|
|
430
|
+
if (existsSync(outPath)) {
|
|
431
|
+
throw new ComponentNodeError(
|
|
432
|
+
`Extract target already exists, refusing overwrite: ${path.basename(name)}`,
|
|
433
|
+
{ statusCode: 409, code: 'FILE_EXISTS' },
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
// M1: an entry whose central-directory uncompressed size already exceeds
|
|
437
|
+
// the effective cap is rejected BEFORE any byte is written — a lying size
|
|
438
|
+
// field cannot force an oversized disk write (quick-fail precheck; the
|
|
439
|
+
// streamed byte count below is the authoritative enforcement).
|
|
440
|
+
if (entry.uncompressedSize > limit) {
|
|
441
|
+
throw new ComponentNodeError(
|
|
442
|
+
`ZIP entry exceeds the extract size limit: ${name}`,
|
|
443
|
+
{ statusCode: 413, code: 'PAYLOAD_TOO_LARGE' },
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
const bytes = await new Promise((resolve, reject) => {
|
|
447
|
+
zipfile.openReadStream(entry, (err, stream) => {
|
|
448
|
+
if (err) return reject(err)
|
|
449
|
+
writeStarted = true
|
|
450
|
+
const out = createWriteStream(outPath)
|
|
451
|
+
writeStream = out
|
|
452
|
+
let written = 0
|
|
453
|
+
let failed = false
|
|
454
|
+
const fail = (error) => {
|
|
455
|
+
if (failed) return
|
|
456
|
+
failed = true
|
|
457
|
+
stream.destroy()
|
|
458
|
+
out.destroy()
|
|
459
|
+
reject(error)
|
|
460
|
+
}
|
|
461
|
+
// W5F-R2: the declared central-directory size can lie, so the written
|
|
462
|
+
// bytes are counted as they land; the write is destroyed and refused
|
|
463
|
+
// with 413 as soon as the effective cap is crossed.
|
|
464
|
+
stream.on('data', (chunk) => {
|
|
465
|
+
if (failed) return
|
|
466
|
+
written += chunk.length
|
|
467
|
+
if (written > limit) {
|
|
468
|
+
fail(new ComponentNodeError(
|
|
469
|
+
`ZIP entry exceeds the extract byte limit: ${name}`,
|
|
470
|
+
{ statusCode: 413, code: 'PAYLOAD_TOO_LARGE' },
|
|
471
|
+
))
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
stream.on('error', fail)
|
|
475
|
+
out.on('error', fail)
|
|
476
|
+
out.on('finish', () => resolve(written))
|
|
477
|
+
stream.pipe(out)
|
|
478
|
+
})
|
|
479
|
+
})
|
|
480
|
+
return { path: outPath, bytes }
|
|
481
|
+
} catch (error) {
|
|
482
|
+
// W5F-R2: a failed extraction (byte-limit reached, stream error) must not
|
|
483
|
+
// leave a half-written target on disk. Only runs once the write stream
|
|
484
|
+
// actually opened — the FILE_EXISTS / declared-size precheck paths are
|
|
485
|
+
// untouched, so a pre-existing target is never deleted.
|
|
486
|
+
if (writeStarted && outPath) {
|
|
487
|
+
// W5F-R2: Windows cannot unlink a file whose handle is still open, and
|
|
488
|
+
// the destroyed write stream closes its fd asynchronously — wait for the
|
|
489
|
+
// stream's 'close' (bounded) before removing the half-written target so
|
|
490
|
+
// cleanup is deterministic instead of racing the fd close.
|
|
491
|
+
if (writeStream && !writeStream.closed) {
|
|
492
|
+
await Promise.race([
|
|
493
|
+
new Promise((resolve) => writeStream.once('close', resolve)),
|
|
494
|
+
new Promise((resolve) => setTimeout(resolve, 500)),
|
|
495
|
+
])
|
|
496
|
+
}
|
|
497
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
498
|
+
try {
|
|
499
|
+
rmSync(outPath, { force: true })
|
|
500
|
+
break
|
|
501
|
+
} catch {
|
|
502
|
+
await new Promise((resolve) => setTimeout(resolve, 5))
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (error instanceof ComponentNodeError) throw error
|
|
507
|
+
throw parseError(`Failed to parse ZIP archive: ${error.message}`, 'ZIP_PARSE')
|
|
508
|
+
} finally {
|
|
509
|
+
if (zipfile) closeZip(zipfile)
|
|
510
|
+
}
|
|
511
|
+
}
|