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,951 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export const USER_FILES_MAX_COUNT = 64;
|
|
6
|
+
export const USER_FILES_MAX_FILE_BYTES = 10 * 1024 * 1024;
|
|
7
|
+
export const USER_FILES_MAX_TOTAL_BYTES = 25 * 1024 * 1024;
|
|
8
|
+
export const WORKSPACE_TEXT_PREVIEW_MAX_BYTES = 64 * 1024;
|
|
9
|
+
|
|
10
|
+
const WORKSPACE_MIME = {
|
|
11
|
+
'.html': 'text/html; charset=utf-8',
|
|
12
|
+
'.css': 'text/css; charset=utf-8',
|
|
13
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
14
|
+
'.mjs': 'text/javascript; charset=utf-8',
|
|
15
|
+
'.jsx': 'text/javascript; charset=utf-8',
|
|
16
|
+
'.ts': 'text/typescript; charset=utf-8',
|
|
17
|
+
'.tsx': 'text/typescript; charset=utf-8',
|
|
18
|
+
'.json': 'application/json',
|
|
19
|
+
'.md': 'text/markdown; charset=utf-8',
|
|
20
|
+
'.markdown': 'text/markdown; charset=utf-8',
|
|
21
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
22
|
+
'.csv': 'text/csv; charset=utf-8',
|
|
23
|
+
'.yaml': 'application/yaml',
|
|
24
|
+
'.yml': 'application/yaml',
|
|
25
|
+
'.log': 'text/plain; charset=utf-8',
|
|
26
|
+
'.png': 'image/png',
|
|
27
|
+
'.jpg': 'image/jpeg',
|
|
28
|
+
'.jpeg': 'image/jpeg',
|
|
29
|
+
'.gif': 'image/gif',
|
|
30
|
+
'.webp': 'image/webp',
|
|
31
|
+
'.svg': 'image/svg+xml',
|
|
32
|
+
'.pdf': 'application/pdf',
|
|
33
|
+
'.mp4': 'video/mp4',
|
|
34
|
+
'.webm': 'video/webm',
|
|
35
|
+
'.mov': 'video/quicktime',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const TEXT_EXTENSIONS = new Set([
|
|
39
|
+
'.css',
|
|
40
|
+
'.csv',
|
|
41
|
+
'.html',
|
|
42
|
+
'.js',
|
|
43
|
+
'.json',
|
|
44
|
+
'.jsx',
|
|
45
|
+
'.log',
|
|
46
|
+
'.markdown',
|
|
47
|
+
'.md',
|
|
48
|
+
'.mjs',
|
|
49
|
+
'.ts',
|
|
50
|
+
'.tsx',
|
|
51
|
+
'.txt',
|
|
52
|
+
'.yaml',
|
|
53
|
+
'.yml',
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
export class WorkspaceStoreError extends Error {
|
|
57
|
+
constructor(message, { statusCode = 400, code = 'BAD_REQUEST' } = {}) {
|
|
58
|
+
super(message);
|
|
59
|
+
this.name = 'Error';
|
|
60
|
+
this.statusCode = statusCode;
|
|
61
|
+
this.code = code;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function nowIso() {
|
|
66
|
+
return new Date().toISOString();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function canonicalRoot(projectRoot) {
|
|
70
|
+
if (!projectRoot) throw new WorkspaceStoreError('Project root is required');
|
|
71
|
+
return path.resolve(projectRoot);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isInsideOrSame(root, target) {
|
|
75
|
+
const relative = path.relative(root, target);
|
|
76
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeRel(value) {
|
|
80
|
+
return String(value || '').replace(/\\/g, '/').replace(/^\/+/, '');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function realPathIfExists(absolutePath) {
|
|
84
|
+
try {
|
|
85
|
+
return fs.realpathSync.native(absolutePath);
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function canonicalExistingRoot(projectRoot) {
|
|
92
|
+
const root = canonicalRoot(projectRoot);
|
|
93
|
+
return realPathIfExists(root) || root;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isWindowsReservedName(segment) {
|
|
97
|
+
return /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i.test(segment);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function validatePathSegment(segment) {
|
|
101
|
+
if (!segment || segment === '.' || segment === '..') return;
|
|
102
|
+
if (segment.endsWith('.') || segment.endsWith(' ')) {
|
|
103
|
+
throw new WorkspaceStoreError(`Invalid filename segment with trailing dot or space: ${segment}`);
|
|
104
|
+
}
|
|
105
|
+
if (isWindowsReservedName(segment)) {
|
|
106
|
+
throw new WorkspaceStoreError(`Reserved Windows filename segment is not allowed: ${segment}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function validateWorkspaceRelPath(relPath) {
|
|
111
|
+
for (const segment of normalizeRel(relPath).split('/').filter(Boolean)) {
|
|
112
|
+
validatePathSegment(segment);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function toWorkspaceRel(root, absolutePath) {
|
|
117
|
+
const rel = path.relative(root, absolutePath).replace(/\\/g, '/');
|
|
118
|
+
return rel === '' ? '' : rel;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function resolveWorkspacePath(projectRoot, rawPath = '') {
|
|
122
|
+
const root = canonicalRoot(projectRoot);
|
|
123
|
+
const text = String(rawPath || '');
|
|
124
|
+
if (text.includes('\0')) {
|
|
125
|
+
throw new WorkspaceStoreError('Path traversal detected: null bytes are not allowed');
|
|
126
|
+
}
|
|
127
|
+
const candidate = path.isAbsolute(text) ? path.resolve(text) : path.resolve(root, text || '.');
|
|
128
|
+
if (!isInsideOrSame(root, candidate)) {
|
|
129
|
+
throw new WorkspaceStoreError('Path traversal detected: resolved path escapes outside workspace');
|
|
130
|
+
}
|
|
131
|
+
const relPath = toWorkspaceRel(root, candidate);
|
|
132
|
+
validateWorkspaceRelPath(relPath);
|
|
133
|
+
return {
|
|
134
|
+
root,
|
|
135
|
+
absolutePath: candidate,
|
|
136
|
+
path: relPath,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function assertExistingPathInsideWorkspace(projectRoot, absolutePath) {
|
|
141
|
+
const root = canonicalRoot(projectRoot);
|
|
142
|
+
const realRoot = canonicalExistingRoot(projectRoot);
|
|
143
|
+
const realTarget = realPathIfExists(absolutePath);
|
|
144
|
+
if (!realTarget) return;
|
|
145
|
+
if (!isInsideOrSame(realRoot, realTarget)) {
|
|
146
|
+
throw new WorkspaceStoreError('Path symlink/junction escape detected outside workspace');
|
|
147
|
+
}
|
|
148
|
+
if (!isInsideOrSame(root, path.resolve(absolutePath))) {
|
|
149
|
+
throw new WorkspaceStoreError('Path traversal detected: resolved path escapes outside workspace');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function nearestExistingAncestor(absolutePath) {
|
|
154
|
+
let current = path.resolve(absolutePath);
|
|
155
|
+
while (!fs.existsSync(current)) {
|
|
156
|
+
const parent = path.dirname(current);
|
|
157
|
+
if (parent === current) return null;
|
|
158
|
+
current = parent;
|
|
159
|
+
}
|
|
160
|
+
return current;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function assertParentInsideWorkspace(projectRoot, absolutePath) {
|
|
164
|
+
const ancestor = nearestExistingAncestor(path.dirname(absolutePath));
|
|
165
|
+
if (ancestor) assertExistingPathInsideWorkspace(projectRoot, ancestor);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function lstatExisting(absolutePath) {
|
|
169
|
+
return fs.lstatSync(absolutePath);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function statExistingInside(projectRoot, absolutePath) {
|
|
173
|
+
assertExistingPathInsideWorkspace(projectRoot, absolutePath);
|
|
174
|
+
return fs.statSync(absolutePath);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function etagForStat(stat) {
|
|
178
|
+
return `"${stat.size.toString(16)}-${Math.trunc(stat.mtimeMs).toString(16)}"`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ── Preview read cache (task-upgrade-file-node AC-1) ──
|
|
182
|
+
// In-memory cache for getWorkspaceMeta / readWorkspaceTextPreview results. Key
|
|
183
|
+
// = kind + canonical root + workspace-relative path (+ offset/limit for text).
|
|
184
|
+
// An entry is a hit only while the file's stat key (size + truncated mtimeMs)
|
|
185
|
+
// is unchanged and the entry is younger than the TTL, so a stat change is an
|
|
186
|
+
// automatic miss that re-reads the disk; explicit invalidation drops both the
|
|
187
|
+
// meta and text entries for the path. The Map is bounded (~200 entries, LRU by
|
|
188
|
+
// insertion age when full).
|
|
189
|
+
export const PREVIEW_CACHE_TTL_MS = 5000;
|
|
190
|
+
const PREVIEW_CACHE_MAX_ENTRIES = 200;
|
|
191
|
+
const previewCache = new Map(); // key -> { statKey, cachedAt, value }
|
|
192
|
+
|
|
193
|
+
function previewCacheKey(kind, root, relPath) {
|
|
194
|
+
return `${kind}\u0000${root}\u0000${relPath}`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function previewStatKey(stat) {
|
|
198
|
+
return stat ? `${stat.size}:${Math.trunc(stat.mtimeMs)}` : 'missing';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function cachedPreviewValue(key, statKey) {
|
|
202
|
+
const entry = previewCache.get(key);
|
|
203
|
+
if (!entry) return null;
|
|
204
|
+
if (entry.statKey !== statKey || entry.cachedAt + PREVIEW_CACHE_TTL_MS <= Date.now()) {
|
|
205
|
+
previewCache.delete(key);
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
return entry.value;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function setPreviewCache(key, statKey, value) {
|
|
212
|
+
previewCache.set(key, { statKey, cachedAt: Date.now(), value });
|
|
213
|
+
if (previewCache.size > PREVIEW_CACHE_MAX_ENTRIES) {
|
|
214
|
+
let oldestKey = null;
|
|
215
|
+
let oldestAt = Infinity;
|
|
216
|
+
for (const [entryKey, entry] of previewCache) {
|
|
217
|
+
if (entry.cachedAt < oldestAt) {
|
|
218
|
+
oldestAt = entry.cachedAt;
|
|
219
|
+
oldestKey = entryKey;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (oldestKey !== null) previewCache.delete(oldestKey);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Return the cached preview value for a workspace path, or undefined on miss.
|
|
228
|
+
* The stat is re-checked like every internal read, so a changed file (or an
|
|
229
|
+
* expired TTL entry) reports as a miss instead of serving stale content.
|
|
230
|
+
*/
|
|
231
|
+
export function getPreviewCached(projectRoot, rawPath = '') {
|
|
232
|
+
const resolved = resolveWorkspacePath(projectRoot, rawPath);
|
|
233
|
+
const stat = fs.existsSync(resolved.absolutePath) ? fs.statSync(resolved.absolutePath) : null;
|
|
234
|
+
const statKey = previewStatKey(stat);
|
|
235
|
+
const metaKey = previewCacheKey('meta', resolved.root, resolved.path);
|
|
236
|
+
const meta = cachedPreviewValue(metaKey, statKey);
|
|
237
|
+
if (meta) return meta;
|
|
238
|
+
const textBase = previewCacheKey('text', resolved.root, resolved.path);
|
|
239
|
+
for (const [key, entry] of previewCache) {
|
|
240
|
+
if (key.startsWith(`${textBase}\u0000`) && entry.statKey === statKey) {
|
|
241
|
+
if (entry.cachedAt + PREVIEW_CACHE_TTL_MS <= Date.now()) {
|
|
242
|
+
previewCache.delete(key);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
return entry.value;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Drop every cached entry (meta and text) for a workspace path so the next
|
|
253
|
+
* read re-reads the disk. Unresolvable paths are a no-op.
|
|
254
|
+
*/
|
|
255
|
+
export function invalidateFileCache(projectRoot, rawPath = '') {
|
|
256
|
+
let resolved;
|
|
257
|
+
try {
|
|
258
|
+
resolved = resolveWorkspacePath(projectRoot, rawPath);
|
|
259
|
+
} catch {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const prefixes = [
|
|
263
|
+
previewCacheKey('meta', resolved.root, resolved.path),
|
|
264
|
+
previewCacheKey('text', resolved.root, resolved.path),
|
|
265
|
+
];
|
|
266
|
+
for (const key of [...previewCache.keys()]) {
|
|
267
|
+
if (prefixes.some(prefix => key === prefix || key.startsWith(`${prefix}\u0000`))) {
|
|
268
|
+
previewCache.delete(key);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function workspaceMimeForPath(filePath) {
|
|
274
|
+
return WORKSPACE_MIME[path.extname(String(filePath || '')).toLowerCase()] || 'application/octet-stream';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function mimeEssence(mime) {
|
|
278
|
+
return String(mime || '').split(';')[0].trim().toLowerCase();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function isTextLikeMime(mime, filePath) {
|
|
282
|
+
const essence = mimeEssence(mime);
|
|
283
|
+
if (essence.startsWith('text/')) return true;
|
|
284
|
+
if (['application/json', 'application/xml', 'application/yaml', 'application/x-yaml'].includes(essence)) return true;
|
|
285
|
+
return TEXT_EXTENSIONS.has(path.extname(String(filePath || '')).toLowerCase());
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function previewKindFor(type, mime, filePath) {
|
|
289
|
+
if (type === 'missing') return 'missing';
|
|
290
|
+
if (type !== 'file') return 'none';
|
|
291
|
+
const essence = mimeEssence(mime);
|
|
292
|
+
if (isTextLikeMime(mime, filePath)) return 'text';
|
|
293
|
+
if (essence.startsWith('image/')) return 'image';
|
|
294
|
+
if (essence === 'application/pdf') return 'pdf';
|
|
295
|
+
if (essence.startsWith('video/')) return 'video';
|
|
296
|
+
return 'none';
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function typeForStat(stat) {
|
|
300
|
+
if (stat.isFile()) return 'file';
|
|
301
|
+
if (stat.isDirectory()) return 'directory';
|
|
302
|
+
return 'other';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function workspaceMetaFromStat(resolved, stat, absolutePath) {
|
|
306
|
+
const type = typeForStat(stat);
|
|
307
|
+
const mime = type === 'file'
|
|
308
|
+
? workspaceMimeForPath(absolutePath)
|
|
309
|
+
: (type === 'directory' ? 'inode/directory' : 'application/octet-stream');
|
|
310
|
+
return {
|
|
311
|
+
ok: true,
|
|
312
|
+
path: resolved.path,
|
|
313
|
+
name: path.basename(resolved.path || resolved.root),
|
|
314
|
+
type,
|
|
315
|
+
exists: true,
|
|
316
|
+
size: stat.size,
|
|
317
|
+
mtime: stat.mtime.toISOString(),
|
|
318
|
+
mime,
|
|
319
|
+
etag: etagForStat(stat),
|
|
320
|
+
previewKind: previewKindFor(type, mime, absolutePath),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function getWorkspaceMeta(projectRoot, rawPath = '') {
|
|
325
|
+
const resolved = resolveWorkspacePath(projectRoot, rawPath);
|
|
326
|
+
const key = previewCacheKey('meta', resolved.root, resolved.path);
|
|
327
|
+
const stat = fs.existsSync(resolved.absolutePath)
|
|
328
|
+
? statExistingInside(projectRoot, resolved.absolutePath)
|
|
329
|
+
: null;
|
|
330
|
+
const statKey = previewStatKey(stat);
|
|
331
|
+
const cached = cachedPreviewValue(key, statKey);
|
|
332
|
+
if (cached) return cached;
|
|
333
|
+
let meta;
|
|
334
|
+
if (!stat) {
|
|
335
|
+
assertParentInsideWorkspace(projectRoot, resolved.absolutePath);
|
|
336
|
+
meta = {
|
|
337
|
+
ok: true,
|
|
338
|
+
path: resolved.path,
|
|
339
|
+
name: path.basename(resolved.path || resolved.root),
|
|
340
|
+
type: 'missing',
|
|
341
|
+
exists: false,
|
|
342
|
+
size: 0,
|
|
343
|
+
mtime: null,
|
|
344
|
+
mime: '',
|
|
345
|
+
etag: null,
|
|
346
|
+
previewKind: 'missing',
|
|
347
|
+
};
|
|
348
|
+
} else {
|
|
349
|
+
meta = workspaceMetaFromStat(resolved, stat, resolved.absolutePath);
|
|
350
|
+
}
|
|
351
|
+
setPreviewCache(key, statKey, meta);
|
|
352
|
+
return meta;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function getWorkspaceFileInfo(projectRoot, rawPath = '') {
|
|
356
|
+
const resolved = requireExisting(projectRoot, rawPath);
|
|
357
|
+
const absolutePath = fs.realpathSync.native(resolved.absolutePath);
|
|
358
|
+
const stat = fs.statSync(absolutePath);
|
|
359
|
+
if (!stat.isFile()) {
|
|
360
|
+
throw new WorkspaceStoreError(`Path is not a file: ${resolved.path}`, {
|
|
361
|
+
statusCode: 400,
|
|
362
|
+
code: 'BAD_REQUEST',
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
...workspaceMetaFromStat(resolved, stat, absolutePath),
|
|
367
|
+
absolutePath,
|
|
368
|
+
lastModified: stat.mtime.toUTCString(),
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function boundedInteger(value, fallback, max = Number.MAX_SAFE_INTEGER) {
|
|
373
|
+
if (value === undefined || value === null || value === '') return fallback;
|
|
374
|
+
const parsed = Number(value);
|
|
375
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
376
|
+
throw new WorkspaceStoreError('Text preview offset and limit must be non-negative numbers');
|
|
377
|
+
}
|
|
378
|
+
return Math.min(Math.floor(parsed), max);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function bufferLooksBinary(buffer) {
|
|
382
|
+
if (!buffer || buffer.length === 0) return false;
|
|
383
|
+
return buffer.includes(0);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function readWorkspaceTextPreview(projectRoot, options = {}) {
|
|
387
|
+
const info = getWorkspaceFileInfo(projectRoot, options.path || '');
|
|
388
|
+
if (!isTextLikeMime(info.mime, info.absolutePath)) {
|
|
389
|
+
throw new WorkspaceStoreError(`Text preview is not supported for ${info.mime || 'binary content'}`, {
|
|
390
|
+
statusCode: 415,
|
|
391
|
+
code: 'UNSUPPORTED_MEDIA_TYPE',
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
const offset = boundedInteger(options.offset, 0);
|
|
395
|
+
const limit = boundedInteger(options.limit, WORKSPACE_TEXT_PREVIEW_MAX_BYTES, WORKSPACE_TEXT_PREVIEW_MAX_BYTES);
|
|
396
|
+
// Cache the (path, offset, limit) slice against the file's stat key so
|
|
397
|
+
// repeated identical previews skip the disk read (AC-1). The 64KB text
|
|
398
|
+
// preview cap is unchanged — the cached value IS the bounded slice.
|
|
399
|
+
const resolved = resolveWorkspacePath(projectRoot, options.path || '');
|
|
400
|
+
const key = `${previewCacheKey('text', resolved.root, info.path)}\u0000${offset}\u0000${limit}`;
|
|
401
|
+
const statKey = previewStatKey({ size: info.size, mtimeMs: new Date(info.mtime).getTime() });
|
|
402
|
+
const cached = cachedPreviewValue(key, statKey);
|
|
403
|
+
if (cached) return cached;
|
|
404
|
+
const start = Math.min(offset, info.size);
|
|
405
|
+
const readLength = Math.min(limit, Math.max(info.size - start, 0));
|
|
406
|
+
const buffer = Buffer.alloc(readLength);
|
|
407
|
+
let bytesRead = 0;
|
|
408
|
+
const fd = fs.openSync(info.absolutePath, 'r');
|
|
409
|
+
try {
|
|
410
|
+
bytesRead = readLength > 0 ? fs.readSync(fd, buffer, 0, readLength, start) : 0;
|
|
411
|
+
} finally {
|
|
412
|
+
fs.closeSync(fd);
|
|
413
|
+
}
|
|
414
|
+
const body = buffer.subarray(0, bytesRead);
|
|
415
|
+
if (bufferLooksBinary(body)) {
|
|
416
|
+
throw new WorkspaceStoreError('Binary content is not supported for text preview', {
|
|
417
|
+
statusCode: 415,
|
|
418
|
+
code: 'UNSUPPORTED_MEDIA_TYPE',
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
const result = {
|
|
422
|
+
ok: true,
|
|
423
|
+
path: info.path,
|
|
424
|
+
text: body.toString('utf8'),
|
|
425
|
+
bytesRead,
|
|
426
|
+
truncated: start + bytesRead < info.size,
|
|
427
|
+
encoding: 'utf-8',
|
|
428
|
+
};
|
|
429
|
+
setPreviewCache(key, statKey, result);
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function requireExisting(projectRoot, rawPath) {
|
|
434
|
+
const resolved = resolveWorkspacePath(projectRoot, rawPath);
|
|
435
|
+
if (!fs.existsSync(resolved.absolutePath)) {
|
|
436
|
+
throw new WorkspaceStoreError(`Path not found: ${resolved.path || '.'}`, {
|
|
437
|
+
statusCode: 404,
|
|
438
|
+
code: 'NOT_FOUND',
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
assertExistingPathInsideWorkspace(projectRoot, resolved.absolutePath);
|
|
442
|
+
return resolved;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function requireMissing(projectRoot, rawPath) {
|
|
446
|
+
const resolved = resolveWorkspacePath(projectRoot, rawPath);
|
|
447
|
+
if (fs.existsSync(resolved.absolutePath)) {
|
|
448
|
+
throw new WorkspaceStoreError(`Path already exists; refusing overwrite: ${resolved.path}`, {
|
|
449
|
+
statusCode: 409,
|
|
450
|
+
code: 'CONFLICT',
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
assertParentInsideWorkspace(projectRoot, resolved.absolutePath);
|
|
454
|
+
return resolved;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function ensureParent(absolutePath) {
|
|
458
|
+
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function readFileBase64(filePath) {
|
|
462
|
+
return fs.readFileSync(filePath).toString('base64');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function decodeContent(contentBase64 = '') {
|
|
466
|
+
const compact = String(contentBase64 ?? '').replace(/\s+/g, '');
|
|
467
|
+
if (!compact) return Buffer.alloc(0);
|
|
468
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(compact) || compact.length % 4 === 1) {
|
|
469
|
+
throw new WorkspaceStoreError('Invalid malformed base64 content');
|
|
470
|
+
}
|
|
471
|
+
const buffer = Buffer.from(compact, 'base64');
|
|
472
|
+
const normalizedInput = compact.replace(/=+$/g, '');
|
|
473
|
+
const normalizedOutput = buffer.toString('base64').replace(/=+$/g, '');
|
|
474
|
+
if (normalizedInput !== normalizedOutput) {
|
|
475
|
+
throw new WorkspaceStoreError('Invalid malformed base64 content');
|
|
476
|
+
}
|
|
477
|
+
return buffer;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function readJsonl(filePath) {
|
|
481
|
+
if (!fs.existsSync(filePath)) return [];
|
|
482
|
+
return fs.readFileSync(filePath, 'utf8')
|
|
483
|
+
.split(/\r?\n/)
|
|
484
|
+
.filter(Boolean)
|
|
485
|
+
.map((line) => JSON.parse(line));
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function appendJsonl(filePath, row) {
|
|
489
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
490
|
+
fs.appendFileSync(filePath, `${JSON.stringify(row)}\n`, 'utf8');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const revisionState = new Map();
|
|
494
|
+
|
|
495
|
+
export function workspaceOpsLogPath(projectRoot) {
|
|
496
|
+
return path.join(canonicalRoot(projectRoot), 'Harness', 'a2a', 'workspace-ops.jsonl');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function nextRevision(projectRoot) {
|
|
500
|
+
const logPath = workspaceOpsLogPath(projectRoot);
|
|
501
|
+
const rows = readJsonl(logPath);
|
|
502
|
+
const diskMax = rows.reduce((max, row) => Math.max(max, Number(row.revision || 0)), 0);
|
|
503
|
+
const current = Math.max(Number(revisionState.get(logPath) || 0), diskMax);
|
|
504
|
+
const next = current + 1;
|
|
505
|
+
revisionState.set(logPath, next);
|
|
506
|
+
return next;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function trashRoot(projectRoot, opId) {
|
|
510
|
+
return path.join(canonicalRoot(projectRoot), 'Harness', '.trash', 'workspace-ops', opId);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function changedEntries(...items) {
|
|
514
|
+
return items.filter(Boolean).map((item) => normalizeRel(item));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function hasDirectoryChildren(dirPath) {
|
|
518
|
+
try {
|
|
519
|
+
return fs.readdirSync(dirPath).length > 0;
|
|
520
|
+
} catch {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export function listWorkspaceTree(projectRoot, options = {}) {
|
|
526
|
+
const resolved = requireExisting(projectRoot, options.path || '');
|
|
527
|
+
const stat = statExistingInside(projectRoot, resolved.absolutePath);
|
|
528
|
+
if (!stat.isDirectory()) {
|
|
529
|
+
throw new WorkspaceStoreError(`Path is not a directory: ${resolved.path}`, {
|
|
530
|
+
statusCode: 400,
|
|
531
|
+
code: 'BAD_REQUEST',
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
const entries = fs.readdirSync(resolved.absolutePath, { withFileTypes: true })
|
|
535
|
+
.map((entry) => {
|
|
536
|
+
const absolutePath = path.join(resolved.absolutePath, entry.name);
|
|
537
|
+
const entryStat = entry.isSymbolicLink()
|
|
538
|
+
? lstatExisting(absolutePath)
|
|
539
|
+
: statExistingInside(projectRoot, absolutePath);
|
|
540
|
+
const isDirectory = !entry.isSymbolicLink() && entryStat.isDirectory();
|
|
541
|
+
return {
|
|
542
|
+
name: entry.name,
|
|
543
|
+
path: toWorkspaceRel(resolved.root, absolutePath),
|
|
544
|
+
type: isDirectory ? 'directory' : 'file',
|
|
545
|
+
size: entryStat.size,
|
|
546
|
+
mtime: entryStat.mtime.toISOString(),
|
|
547
|
+
hasChildren: isDirectory ? hasDirectoryChildren(absolutePath) : false,
|
|
548
|
+
};
|
|
549
|
+
})
|
|
550
|
+
.sort((a, b) => {
|
|
551
|
+
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
|
|
552
|
+
return a.name.localeCompare(b.name);
|
|
553
|
+
});
|
|
554
|
+
return {
|
|
555
|
+
root: resolved.root,
|
|
556
|
+
path: resolved.path,
|
|
557
|
+
absolutePath: resolved.absolutePath,
|
|
558
|
+
entries,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function removePath(absolutePath) {
|
|
563
|
+
fs.rmSync(absolutePath, { recursive: true, force: true });
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function writeLogRow(projectRoot, row) {
|
|
567
|
+
appendJsonl(workspaceOpsLogPath(projectRoot), row);
|
|
568
|
+
return row;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export function applyWorkspaceOperation(projectRoot, operation = {}) {
|
|
572
|
+
const op = String(operation.op || '').trim();
|
|
573
|
+
const opId = crypto.randomUUID();
|
|
574
|
+
const revision = nextRevision(projectRoot);
|
|
575
|
+
const createdAt = nowIso();
|
|
576
|
+
let result;
|
|
577
|
+
|
|
578
|
+
if (op === 'create-file') {
|
|
579
|
+
const target = requireMissing(projectRoot, operation.target);
|
|
580
|
+
const content = decodeContent(operation.contentBase64);
|
|
581
|
+
ensureParent(target.absolutePath);
|
|
582
|
+
fs.writeFileSync(target.absolutePath, content);
|
|
583
|
+
result = {
|
|
584
|
+
source: null,
|
|
585
|
+
target: target.path,
|
|
586
|
+
inverse: { type: 'remove', target: target.path },
|
|
587
|
+
entriesChanged: changedEntries(target.path),
|
|
588
|
+
};
|
|
589
|
+
} else if (op === 'create-folder') {
|
|
590
|
+
const target = requireMissing(projectRoot, operation.target);
|
|
591
|
+
ensureParent(target.absolutePath);
|
|
592
|
+
fs.mkdirSync(target.absolutePath, { recursive: false });
|
|
593
|
+
result = {
|
|
594
|
+
source: null,
|
|
595
|
+
target: target.path,
|
|
596
|
+
inverse: { type: 'remove', target: target.path },
|
|
597
|
+
entriesChanged: changedEntries(target.path),
|
|
598
|
+
};
|
|
599
|
+
} else if (op === 'write') {
|
|
600
|
+
const target = requireExisting(projectRoot, operation.target);
|
|
601
|
+
const content = decodeContent(operation.contentBase64);
|
|
602
|
+
if (!statExistingInside(projectRoot, target.absolutePath).isFile()) {
|
|
603
|
+
throw new WorkspaceStoreError(`Path is not a file: ${target.path}`);
|
|
604
|
+
}
|
|
605
|
+
const previousContentBase64 = readFileBase64(target.absolutePath);
|
|
606
|
+
fs.writeFileSync(target.absolutePath, content);
|
|
607
|
+
result = {
|
|
608
|
+
source: null,
|
|
609
|
+
target: target.path,
|
|
610
|
+
inverse: { type: 'write', target: target.path, contentBase64: previousContentBase64 },
|
|
611
|
+
entriesChanged: changedEntries(target.path),
|
|
612
|
+
};
|
|
613
|
+
} else if (op === 'rename' || op === 'move') {
|
|
614
|
+
const source = requireExisting(projectRoot, operation.source);
|
|
615
|
+
const target = requireMissing(projectRoot, operation.target);
|
|
616
|
+
ensureParent(target.absolutePath);
|
|
617
|
+
fs.renameSync(source.absolutePath, target.absolutePath);
|
|
618
|
+
result = {
|
|
619
|
+
source: source.path,
|
|
620
|
+
target: target.path,
|
|
621
|
+
inverse: { type: 'rename', source: target.path, target: source.path },
|
|
622
|
+
entriesChanged: changedEntries(source.path, target.path),
|
|
623
|
+
};
|
|
624
|
+
} else if (op === 'copy') {
|
|
625
|
+
const source = requireExisting(projectRoot, operation.source);
|
|
626
|
+
const target = requireMissing(projectRoot, operation.target);
|
|
627
|
+
ensureParent(target.absolutePath);
|
|
628
|
+
fs.cpSync(source.absolutePath, target.absolutePath, { recursive: true, errorOnExist: true });
|
|
629
|
+
result = {
|
|
630
|
+
source: source.path,
|
|
631
|
+
target: target.path,
|
|
632
|
+
inverse: { type: 'remove', target: target.path },
|
|
633
|
+
entriesChanged: changedEntries(target.path),
|
|
634
|
+
};
|
|
635
|
+
} else if (op === 'delete') {
|
|
636
|
+
const source = requireExisting(projectRoot, operation.source);
|
|
637
|
+
const targetTrashRoot = trashRoot(projectRoot, opId);
|
|
638
|
+
fs.mkdirSync(targetTrashRoot, { recursive: true });
|
|
639
|
+
const trashPath = path.join(targetTrashRoot, path.basename(source.absolutePath));
|
|
640
|
+
fs.renameSync(source.absolutePath, trashPath);
|
|
641
|
+
result = {
|
|
642
|
+
source: source.path,
|
|
643
|
+
target: null,
|
|
644
|
+
inverse: { type: 'restore-trash', source: trashPath, target: source.path },
|
|
645
|
+
trashPath,
|
|
646
|
+
strategy: 'rename-to-trash',
|
|
647
|
+
entriesChanged: changedEntries(source.path),
|
|
648
|
+
};
|
|
649
|
+
} else {
|
|
650
|
+
throw new WorkspaceStoreError(`Unsupported workspace operation: ${op}`);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const row = writeLogRow(projectRoot, {
|
|
654
|
+
opId,
|
|
655
|
+
revision,
|
|
656
|
+
op,
|
|
657
|
+
source: result.source,
|
|
658
|
+
target: result.target,
|
|
659
|
+
inverse: result.inverse,
|
|
660
|
+
entriesChanged: result.entriesChanged || [],
|
|
661
|
+
undoable: true,
|
|
662
|
+
createdAt,
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
return {
|
|
666
|
+
ok: true,
|
|
667
|
+
opId,
|
|
668
|
+
revision,
|
|
669
|
+
op,
|
|
670
|
+
undoable: true,
|
|
671
|
+
entriesChanged: result.entriesChanged || [],
|
|
672
|
+
...(result.trashPath ? { trashPath: result.trashPath } : {}),
|
|
673
|
+
...(result.strategy ? { strategy: result.strategy } : {}),
|
|
674
|
+
logRow: row,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function undoneOpIds(rows) {
|
|
679
|
+
return new Set(rows.filter((row) => row.op === 'undo' && row.undoneOpId).map((row) => row.undoneOpId));
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function rowChangedEntries(row) {
|
|
683
|
+
const values = Array.isArray(row.entriesChanged) ? [...row.entriesChanged] : [];
|
|
684
|
+
for (const key of ['source', 'target']) {
|
|
685
|
+
if (row[key]) values.push(row[key]);
|
|
686
|
+
}
|
|
687
|
+
const inverse = row.inverse || {};
|
|
688
|
+
for (const key of ['source', 'target']) {
|
|
689
|
+
if (inverse[key] && !path.isAbsolute(String(inverse[key]))) values.push(inverse[key]);
|
|
690
|
+
}
|
|
691
|
+
return values.map(item => normalizeRel(item)).filter(Boolean);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function pathsOverlap(a, b) {
|
|
695
|
+
const left = normalizeRel(a);
|
|
696
|
+
const right = normalizeRel(b);
|
|
697
|
+
return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function hasLaterOverlappingOperation(row, rows, undone) {
|
|
701
|
+
const rowEntries = rowChangedEntries(row);
|
|
702
|
+
if (rowEntries.length === 0) return false;
|
|
703
|
+
return rows.some((candidate) => {
|
|
704
|
+
if (!candidate.undoable || candidate.op === 'undo' || undone.has(candidate.opId)) return false;
|
|
705
|
+
if (Number(candidate.revision || 0) <= Number(row.revision || 0)) return false;
|
|
706
|
+
return rowChangedEntries(candidate).some(candidateEntry =>
|
|
707
|
+
rowEntries.some(rowEntry => pathsOverlap(candidateEntry, rowEntry))
|
|
708
|
+
);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function resolveUndoRow(projectRoot, opId = 'latest') {
|
|
713
|
+
const rows = readJsonl(workspaceOpsLogPath(projectRoot));
|
|
714
|
+
const undone = undoneOpIds(rows);
|
|
715
|
+
if (opId && opId !== 'latest') {
|
|
716
|
+
const row = rows.find((candidate) => candidate.opId === opId && candidate.undoable);
|
|
717
|
+
if (!row || undone.has(row.opId)) {
|
|
718
|
+
throw new WorkspaceStoreError(`Undoable operation not found: ${opId}`, {
|
|
719
|
+
statusCode: 404,
|
|
720
|
+
code: 'NOT_FOUND',
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
if (hasLaterOverlappingOperation(row, rows, undone)) {
|
|
724
|
+
throw new WorkspaceStoreError(`Stale undo conflict: newer operation affects ${row.opId}`, {
|
|
725
|
+
statusCode: 409,
|
|
726
|
+
code: 'CONFLICT',
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
return row;
|
|
730
|
+
}
|
|
731
|
+
const row = [...rows].reverse().find((candidate) => candidate.undoable && candidate.op !== 'undo' && !undone.has(candidate.opId));
|
|
732
|
+
if (!row) {
|
|
733
|
+
throw new WorkspaceStoreError('No undoable workspace operation found', {
|
|
734
|
+
statusCode: 404,
|
|
735
|
+
code: 'NOT_FOUND',
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
return row;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function applyInverse(projectRoot, row) {
|
|
742
|
+
const inverse = row.inverse || {};
|
|
743
|
+
if (inverse.type === 'remove') {
|
|
744
|
+
const target = requireExisting(projectRoot, inverse.target);
|
|
745
|
+
removePath(target.absolutePath);
|
|
746
|
+
return changedEntries(target.path);
|
|
747
|
+
}
|
|
748
|
+
if (inverse.type === 'write') {
|
|
749
|
+
const target = resolveWorkspacePath(projectRoot, inverse.target);
|
|
750
|
+
ensureParent(target.absolutePath);
|
|
751
|
+
fs.writeFileSync(target.absolutePath, decodeContent(inverse.contentBase64));
|
|
752
|
+
return changedEntries(target.path);
|
|
753
|
+
}
|
|
754
|
+
if (inverse.type === 'rename') {
|
|
755
|
+
const source = requireExisting(projectRoot, inverse.source);
|
|
756
|
+
const target = requireMissing(projectRoot, inverse.target);
|
|
757
|
+
ensureParent(target.absolutePath);
|
|
758
|
+
fs.renameSync(source.absolutePath, target.absolutePath);
|
|
759
|
+
return changedEntries(source.path, target.path);
|
|
760
|
+
}
|
|
761
|
+
if (inverse.type === 'restore-trash') {
|
|
762
|
+
const target = requireMissing(projectRoot, inverse.target);
|
|
763
|
+
const source = path.resolve(String(inverse.source || ''));
|
|
764
|
+
const expectedTrashRoot = trashRoot(projectRoot, row.opId);
|
|
765
|
+
if (!isInsideOrSame(expectedTrashRoot, source)) {
|
|
766
|
+
throw new WorkspaceStoreError(`Corrupt trash source outside operation trash root: ${source}`);
|
|
767
|
+
}
|
|
768
|
+
if (!fs.existsSync(source)) {
|
|
769
|
+
throw new WorkspaceStoreError(`Trash path not found: ${source}`, {
|
|
770
|
+
statusCode: 404,
|
|
771
|
+
code: 'NOT_FOUND',
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
assertExistingPathInsideWorkspace(projectRoot, source);
|
|
775
|
+
ensureParent(target.absolutePath);
|
|
776
|
+
fs.renameSync(source, target.absolutePath);
|
|
777
|
+
return changedEntries(target.path);
|
|
778
|
+
}
|
|
779
|
+
throw new WorkspaceStoreError(`Unsupported undo operation: ${inverse.type}`);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
export function undoWorkspaceOperation(projectRoot, options = {}) {
|
|
783
|
+
const row = resolveUndoRow(projectRoot, options.opId || 'latest');
|
|
784
|
+
const entriesChanged = applyInverse(projectRoot, row);
|
|
785
|
+
const undoOpId = crypto.randomUUID();
|
|
786
|
+
const revision = nextRevision(projectRoot);
|
|
787
|
+
writeLogRow(projectRoot, {
|
|
788
|
+
opId: undoOpId,
|
|
789
|
+
revision,
|
|
790
|
+
op: 'undo',
|
|
791
|
+
undoneOpId: row.opId,
|
|
792
|
+
undoable: false,
|
|
793
|
+
createdAt: nowIso(),
|
|
794
|
+
});
|
|
795
|
+
return {
|
|
796
|
+
ok: true,
|
|
797
|
+
opId: undoOpId,
|
|
798
|
+
revision,
|
|
799
|
+
undoneOpId: row.opId,
|
|
800
|
+
entriesChanged,
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// ── "Open in OS file manager" reveal (task-wf-ui-terminal-explorer-ux AC-009) ──
|
|
805
|
+
// Pure plan builder: validates containment + existence, returns the spawn plan.
|
|
806
|
+
// The HTTP route executes the plan; tests assert the plan without spawning.
|
|
807
|
+
export function planRevealWorkspacePath(projectRoot, rawPath = '') {
|
|
808
|
+
const resolved = resolveWorkspacePath(projectRoot, rawPath);
|
|
809
|
+
if (!fs.existsSync(resolved.absolutePath)) {
|
|
810
|
+
throw new WorkspaceStoreError(`Path does not exist on disk: ${resolved.path}`, {
|
|
811
|
+
statusCode: 404,
|
|
812
|
+
code: 'NOT_FOUND',
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
const isDirectory = fs.statSync(resolved.absolutePath).isDirectory();
|
|
816
|
+
const platform = process.platform;
|
|
817
|
+
let command;
|
|
818
|
+
let args;
|
|
819
|
+
if (platform === 'win32') {
|
|
820
|
+
command = 'explorer.exe';
|
|
821
|
+
// Directories open in place; files open with the item pre-selected.
|
|
822
|
+
// explorer.exe takes the flag and path joined in one argument.
|
|
823
|
+
args = isDirectory ? [resolved.absolutePath] : [`/select,${resolved.absolutePath}`];
|
|
824
|
+
} else if (platform === 'darwin') {
|
|
825
|
+
command = 'open';
|
|
826
|
+
args = isDirectory ? [resolved.absolutePath] : ['-R', resolved.absolutePath];
|
|
827
|
+
} else {
|
|
828
|
+
command = 'xdg-open';
|
|
829
|
+
args = [isDirectory ? resolved.absolutePath : path.dirname(resolved.absolutePath)];
|
|
830
|
+
}
|
|
831
|
+
return {
|
|
832
|
+
path: resolved.path,
|
|
833
|
+
absolutePath: resolved.absolutePath,
|
|
834
|
+
isDirectory,
|
|
835
|
+
platform,
|
|
836
|
+
command,
|
|
837
|
+
args,
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function safeFileName(value, index) {
|
|
842
|
+
const raw = String(value || `user-file-${index + 1}`);
|
|
843
|
+
const basename = raw.split(/[\\/]+/).pop() || `user-file-${index + 1}`;
|
|
844
|
+
validatePathSegment(basename);
|
|
845
|
+
const cleaned = basename
|
|
846
|
+
.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_')
|
|
847
|
+
.replace(/^\.+/, '')
|
|
848
|
+
.trim()
|
|
849
|
+
.slice(0, 160);
|
|
850
|
+
const result = cleaned || `user-file-${index + 1}`;
|
|
851
|
+
validatePathSegment(result);
|
|
852
|
+
return result;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function uniquePath(directory, filename) {
|
|
856
|
+
const ext = path.extname(filename);
|
|
857
|
+
const stem = path.basename(filename, ext) || 'user-file';
|
|
858
|
+
let candidate = path.join(directory, filename);
|
|
859
|
+
for (let index = 2; fs.existsSync(candidate); index += 1) {
|
|
860
|
+
candidate = path.join(directory, `${stem}-${index}${ext}`);
|
|
861
|
+
}
|
|
862
|
+
return candidate;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function categoryFor(file = {}) {
|
|
866
|
+
const hint = String(file.categoryHint || '').trim().toLowerCase();
|
|
867
|
+
if (hint === 'context') return 'context';
|
|
868
|
+
const mime = String(file.mime || file.type || '').trim().toLowerCase();
|
|
869
|
+
const ext = path.extname(String(file.name || '')).toLowerCase();
|
|
870
|
+
if (mime.startsWith('image/') || ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'].includes(ext)) return 'images';
|
|
871
|
+
if (mime === 'application/pdf' || ext === '.pdf') return 'pdf';
|
|
872
|
+
if (mime.includes('word') || mime.includes('document') || ['.doc', '.docx', '.rtf', '.odt'].includes(ext)) return 'documents';
|
|
873
|
+
if (mime.includes('zip') || mime.includes('tar') || mime.includes('rar') || ['.zip', '.tar', '.gz', '.tgz', '.rar', '.7z'].includes(ext)) return 'archives';
|
|
874
|
+
if (mime.startsWith('text/') || ['.txt', '.md', '.markdown', '.csv', '.json', '.yaml', '.yml', '.log'].includes(ext)) return 'text';
|
|
875
|
+
return 'other';
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
export function storeUserFiles(projectRoot, payload = {}) {
|
|
879
|
+
const root = canonicalRoot(projectRoot);
|
|
880
|
+
const files = Array.isArray(payload.files) ? payload.files : [];
|
|
881
|
+
if (files.length > USER_FILES_MAX_COUNT) {
|
|
882
|
+
throw new WorkspaceStoreError(`User file count limit exceeded: ${files.length} > ${USER_FILES_MAX_COUNT}`, {
|
|
883
|
+
statusCode: 413,
|
|
884
|
+
code: 'PAYLOAD_TOO_LARGE',
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
let totalBytes = 0;
|
|
888
|
+
const stored = files.map((file, index) => {
|
|
889
|
+
const category = categoryFor(file);
|
|
890
|
+
const name = safeFileName(file?.name, index);
|
|
891
|
+
const content = decodeContent(file?.contentBase64);
|
|
892
|
+
totalBytes += content.length;
|
|
893
|
+
if (content.length > USER_FILES_MAX_FILE_BYTES) {
|
|
894
|
+
throw new WorkspaceStoreError(`User file too large: ${name}`, {
|
|
895
|
+
statusCode: 413,
|
|
896
|
+
code: 'PAYLOAD_TOO_LARGE',
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
if (totalBytes > USER_FILES_MAX_TOTAL_BYTES) {
|
|
900
|
+
throw new WorkspaceStoreError('User files aggregate size limit exceeded', {
|
|
901
|
+
statusCode: 413,
|
|
902
|
+
code: 'PAYLOAD_TOO_LARGE',
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
const dir = path.join(root, 'Harness', 'user-files', category);
|
|
906
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
907
|
+
const absolutePath = uniquePath(dir, name);
|
|
908
|
+
fs.writeFileSync(absolutePath, content);
|
|
909
|
+
const relPath = toWorkspaceRel(root, absolutePath);
|
|
910
|
+
return {
|
|
911
|
+
name: path.basename(absolutePath),
|
|
912
|
+
category,
|
|
913
|
+
path: relPath,
|
|
914
|
+
mime: String(file?.mime || file?.type || ''),
|
|
915
|
+
size: content.length,
|
|
916
|
+
absolutePath,
|
|
917
|
+
terminalTag: `@file(${relPath})`,
|
|
918
|
+
};
|
|
919
|
+
});
|
|
920
|
+
return { ok: true, source: String(payload.source || ''), files: stored };
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function tagFor(kind, relPath) {
|
|
924
|
+
return kind === 'workspace-folder' ? `@folder(${relPath})` : `@file(${relPath})`;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
function terminalItemText(projectRoot, item, shiftKey) {
|
|
928
|
+
const resolved = requireExisting(projectRoot, item?.path || '');
|
|
929
|
+
const stat = statExistingInside(projectRoot, resolved.absolutePath);
|
|
930
|
+
const kind = String(item?.kind || '').trim();
|
|
931
|
+
if (kind === 'workspace-file' && !stat.isFile()) {
|
|
932
|
+
throw new WorkspaceStoreError(`Path is not a file: ${resolved.path}`);
|
|
933
|
+
}
|
|
934
|
+
if (kind === 'workspace-folder' && !stat.isDirectory()) {
|
|
935
|
+
throw new WorkspaceStoreError(`Path is not a folder: ${resolved.path}`);
|
|
936
|
+
}
|
|
937
|
+
const absolute = shiftKey || item?.format === 'absolute-path';
|
|
938
|
+
if (absolute) return resolved.absolutePath;
|
|
939
|
+
if (item?.format === 'relative-path') return resolved.path;
|
|
940
|
+
return tagFor(kind, resolved.path);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
export function buildTerminalContextInput(projectRoot, payload = {}) {
|
|
944
|
+
const items = Array.isArray(payload.items) ? payload.items : [];
|
|
945
|
+
const shiftKey = Boolean(payload.shiftKey);
|
|
946
|
+
const terminalInput = items.map((item) => terminalItemText(projectRoot, item, shiftKey)).join(' ');
|
|
947
|
+
return {
|
|
948
|
+
ok: true,
|
|
949
|
+
terminalInput,
|
|
950
|
+
};
|
|
951
|
+
}
|