farming-code 2.2.27 → 2.2.29
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/README.md +15 -30
- package/README.zh_cn.md +15 -30
- package/THIRD_PARTY_NOTICES.md +28 -6
- package/backend/acp/packaged-claude-acp.js +13 -0
- package/backend/acp/packaged-codex-acp.js +2 -2
- package/backend/acp-runtime.js +52 -3
- package/backend/acp-transcript.js +174 -9
- package/backend/agent-manager.js +1081 -96
- package/backend/atomic-json-store.js +37 -0
- package/backend/cli-agents.js +9 -7
- package/backend/config-manager.js +211 -58
- package/backend/control-api.js +2 -1
- package/backend/data/LICENSE.agent-browser +201 -0
- package/backend/data/runtime-dependency-manifest.json +199 -0
- package/backend/data/runtime-dependency-sources.json +5 -0
- package/backend/executable-discovery.js +7 -0
- package/backend/farming-agent-bootstrap.js +49 -0
- package/backend/farming-agent-bootstrap.zh_cn.md +7 -0
- package/backend/farming-app-cli.js +168 -65
- package/backend/farming-cli.js +57 -0
- package/backend/farming-session-store.js +92 -89
- package/backend/index-html.js +13 -0
- package/backend/main-agent-skills.js +13 -0
- package/backend/npm-update-helper.js +43 -6
- package/backend/preview-session-manager.js +68 -0
- package/backend/provider-adapters.js +54 -5
- package/backend/review-session-store.js +32 -25
- package/backend/review-state-store.js +13 -14
- package/backend/run-history-store.js +13 -16
- package/backend/runtime-dependency-manager.js +759 -0
- package/backend/runtime-executable-invocation.js +20 -0
- package/backend/server-process-identity.js +47 -0
- package/backend/server.js +231 -223
- package/backend/slash-command-discovery.js +138 -1
- package/backend/storage-layout.js +35 -0
- package/backend/update-service.js +27 -1
- package/backend/workspace-file-router.js +141 -0
- package/backend/workspace-file-service.js +19 -0
- package/backend/workspace-root-registry.js +6 -4
- package/config/farming.install.env.example +4 -0
- package/dist/acp/LICENSE.claude-agent-acp +191 -0
- package/dist/acp/LICENSE.claude-agent-sdk +1 -0
- package/dist/acp/claude-agent-acp-0.59.0.mjs +41606 -0
- package/dist/assets/App-BKNUILuL.css +1 -0
- package/dist/assets/App-Ex2oVVXO.js +693 -0
- package/dist/assets/{FileEditorMarkdownPreview-Da5jguUs.js → FileEditorMarkdownPreview-Cs9cLv8a.js} +5 -5
- package/dist/assets/FileEditorPane-CNtuGjQ3.js +2 -0
- package/dist/assets/IconGlyphs-BClgag6E.js +1 -0
- package/dist/assets/ProjectFilesSection-Cf2BEcAN.js +12 -0
- package/dist/assets/ReviewPage-BRuxFaH5.js +3 -0
- package/dist/assets/{abnfDiagram-VRR7QNED-nnglrXFi.js → abnfDiagram-VRR7QNED-BY1qwq8A.js} +1 -1
- package/dist/assets/api-w2x5QfxB.js +1 -0
- package/dist/assets/{arc-CWoVlxJw.js → arc-DgZRAlwj.js} +1 -1
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-BEXMaFfY.js → architectureDiagram-ZJ3FMSHR-jFn144od.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-1mCezSBk.js → blockDiagram-677ZJIJ3-BmRjlV0H.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-DhTiI6Uv.js → c4Diagram-LMCZKHZV-DUKul6tT.js} +1 -1
- package/dist/assets/{workspace-editor-monaco-DLlws5Ig.js → channel-CI9j3QFD.js} +1 -1
- package/dist/assets/chunk-2Q5K7J3B-87UnWaoh.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-CCdsKd73.js → chunk-32BRIVSS-rGVu23o5.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-Gncjk4LF.js → chunk-52WLFC77-CvFDKgbB.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-Dlw7AXWp.js → chunk-5VM5RSS4-CFgnRW1C.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-ZBRhjLOh.js → chunk-7BUUIJ7U-Cp7bqW6s.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-CiDVUP82.js → chunk-C7G6YPKG-BIS1gjZO.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-z6gXP2_N.js → chunk-EX3LRPZG-BKG-cR9j.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-DMZBccQa.js → chunk-FWX5IMBZ-B8PsTr8a.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-D8tIeoPk.js → chunk-HOUHSVGY-BHFOAiXT.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-b6oQ2Q17.js → chunk-ICXQ74PX-CvLE1J9X.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DSyBRaRG.js → chunk-JWPE2WC7-D8misks7.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-DAVH_p2a.js → chunk-MOJQB5TN-D-oNgSNH.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-DxoiMCI-.js → chunk-OGEWGWER-C2N-la9e.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-NsOWh7HM.js → chunk-PUDLZKDR-BwI3Jz8q.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-Di4H7L21.js → chunk-Q4XR5HBZ-BuqGfsXA.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-CZA9pvFX.js → chunk-RYQCIY6F-Bq4XZcGt.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DttpZg9Y.js → chunk-V7JOEXUC-QZLzdTTJ.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-Cb0-kQ9g.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-D-Fmtd4Z.js → chunk-VR4S4FIN-DcZTpML_.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-CUxMtfK2.js → chunk-WYO6CB5R-DCU8wuny.js} +2 -2
- package/dist/assets/chunk-XXDRQBXY-BZAC1Krv.js +1 -0
- package/dist/assets/{chunk-ZGVPDNZ5-CoV2QEl0.js → chunk-ZGVPDNZ5-B7r85QbY.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-DncwlQQc.js → chunk-ZIRB5QZD-BgEnDWhQ.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DcvGOuvi.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-DDYUSofQ.js +1 -0
- package/dist/assets/code-dark-BEZTQwYZ.css +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-BNIAz0nN.js → cose-bilkent-JH36ORCC-CThXS2qG.js} +1 -1
- package/dist/assets/{cssMode-DDWxTPm_.js → cssMode-Bdg5RpcJ.js} +1 -1
- package/dist/assets/{cynefinDiagram-TSTJHNR4-0xPI92Lg.js → cynefinDiagram-TSTJHNR4-DxBgzw25.js} +1 -1
- package/dist/assets/{dagre-DXlcHgr3.js → dagre-BM-GvGUZ.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-DfuMG4gU.js → dagre-VKFMJZFB-B7UCRmxR.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-CT00bLZ6.js → diagram-FQU43EPY-CeL2DwKx.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-Xbh7zXUx.js → diagram-G47NLZAW-BEirglCC.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-SRDVPfn7.js → diagram-NH7WQ7WH-C6wcI-Rl.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-CQRli4Np.js → diagram-OA4YK3LP-DpM-XXtJ.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-DMf17WQi.js → diagram-WEI45ONY-BQyaut9k.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-DhpFkgJ5.js → ebnfDiagram-CCIWWBDH-UmAXsUhr.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-1IwxWmJo.js → erDiagram-Q63AITRT-CdYLEOch.js} +1 -1
- package/dist/assets/file-icons-BOlcJ0ZF.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-CO-z9-tq.js +1 -0
- package/dist/assets/{freemarker2-QBsTERH-.js → freemarker2-DfMDJfnE.js} +1 -1
- package/dist/assets/{ganttDiagram-NO4QXBWP-BNCdfyr1.js → ganttDiagram-NO4QXBWP-DgSUNdUB.js} +1 -1
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-pbyWZuiX.js → gitGraphDiagram-IHSO6WYX-DjwjtRtF.js} +1 -1
- package/dist/assets/{handlebars-CuoxPo5A.js → handlebars-BEdPtzMT.js} +1 -1
- package/dist/assets/{html-gX5PEu0Q.js → html-B1fPb1QD.js} +1 -1
- package/dist/assets/html2canvas-DsczWxBa.js +5 -0
- package/dist/assets/{htmlMode-DIWUbUxd.js → htmlMode-CY2ZjHxH.js} +1 -1
- package/dist/assets/{index-CtwKYTiC.js → index-CQNaZc3l.js} +2 -2
- package/dist/assets/{infoDiagram-FWYZ7A6U-BcTOgDbe.js → infoDiagram-FWYZ7A6U-Bi9076l2.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-DCdvGKrn.js → ishikawaDiagram-FXEZZL3T-DaMaNZN7.js} +1 -1
- package/dist/assets/{javascript-BYKGIUGs.js → javascript-BV2OsWA0.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-BZ-1q_IT.js → journeyDiagram-5HDEW3XC-CfDy69EZ.js} +1 -1
- package/dist/assets/{jsonMode-AFmQQP6u.js → jsonMode-zd07aeuX.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-BIgejCjj.js → kanban-definition-HUTT4EX6-XFvZ722B.js} +1 -1
- package/dist/assets/{line-CfptFMmX.js → line-BXK9PKLn.js} +1 -1
- package/dist/assets/{linear-Dld3_ERu.js → linear-CULvmq2Z.js} +1 -1
- package/dist/assets/{liquid-DBIl-f0z.js → liquid-BParI24X.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-DK6tqicV.js → lspLanguageFeatures-BzredkoV.js} +1 -1
- package/dist/assets/main-DK9Sy7-c.css +1 -0
- package/dist/assets/{map-LZWWoeWa.js → map-C9Qw9eWh.js} +1 -1
- package/dist/assets/{mdx-DS-bVtZN.js → mdx-vGzUJN98.js} +1 -1
- package/dist/assets/{mermaid.core-gSpX30pV.js → mermaid.core-CzazaIKr.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-BKBaI4LW.js → mindmap-definition-LN4V7U3C-Uoff7hZ3.js} +1 -1
- package/dist/assets/{monaco.contribution-BXz-lfFB.js → monaco.contribution-BmaeBQEc.js} +2 -2
- package/dist/assets/{ordinal-BLrOss5K.js → ordinal-r6pIwRdZ.js} +1 -1
- package/dist/assets/{pegDiagram-2B236MQR-sBvDY3VX.js → pegDiagram-2B236MQR-CCBiHQjk.js} +1 -1
- package/dist/assets/{pieDiagram-ENE6RG2P-DWlWcu08.js → pieDiagram-ENE6RG2P-D61s7l75.js} +1 -1
- package/dist/assets/{python-C93q3xXz.js → python-CUwdLgsI.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-DZutUd3J.js → quadrantDiagram-ABIIQ3AL-D_CWk2DQ.js} +1 -1
- package/dist/assets/{railroadDiagram-RFXS5EU6-DQb3lXGM.js → railroadDiagram-RFXS5EU6-CdlCHtxN.js} +1 -1
- package/dist/assets/{razor-CBifNGXO.js → razor-BScebUxU.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-DqFGXu2e.js → requirementDiagram-TGXJPOKE-BLpHtkoC.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-d7ECahnO.js → sankeyDiagram-HTMAVEWB-CkhYZN2j.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-DdsbKqct.js → sequenceDiagram-DBY2YBRQ-8bploKJl.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-daTB6OcF.js → sizeCapture-X5ZJPWSS-DtSdkS0d.js} +1 -1
- package/dist/assets/{src-WBW1QxJt.js → src-6yeq6WBA.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-P6iSrgwR.js → stateDiagram-2N3HPSRC-DfmVGpfA.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-B2c9CQ-J.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-BGnZJx1M.js → swimlanes-5IMT3BWC-DRLk2zRF.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-B7_APh_h.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-rA-r3aUR.js → timeline-definition-FHXFAJF6-Cngfy588.js} +1 -1
- package/dist/assets/{tsMode-B0oUTcXQ.js → tsMode-DzUAibXK.js} +1 -1
- package/dist/assets/{typescript-KJKfYO7K.js → typescript-D9EOeM7N.js} +1 -1
- package/dist/assets/{vennDiagram-L72KCM5P-DiVKUjA9.js → vennDiagram-L72KCM5P-DeJn63Lq.js} +1 -1
- package/dist/assets/{wardleyDiagram-EHGQE667-Cfxh7UOn.js → wardleyDiagram-EHGQE667-BH1nzy77.js} +1 -1
- package/dist/assets/{workers-BSQRIkqa.js → workers-DAiomYZM.js} +1 -1
- package/dist/assets/workspace-editor-model-CSGCl21x.js +1 -0
- package/dist/assets/{workspace-editor-monaco-BIpqcWf1.js → workspace-editor-monaco-CJNwbpCy.js} +3 -3
- package/dist/assets/workspace-editor-monaco-CO9w8YV0.js +1 -0
- package/dist/assets/workspace-view-state-BjKPYp0E.js +7 -0
- package/dist/assets/{xml-C8FAcN6j.js → xml-wWl1dhkQ.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG--hmryQeL.js → xychartDiagram-FW5EYKEG-SkO2prCU.js} +1 -1
- package/dist/assets/{yaml-BQ4NrRhG.js → yaml-Dc2WDaOZ.js} +1 -1
- package/dist/crt-markdown-renderer.js +46 -46
- package/dist/index.html +32 -3
- package/extensions/browser/backend/agent-browser-runtime.js +1041 -0
- package/extensions/browser/backend/agent-capability.js +55 -0
- package/extensions/browser/backend/browser-mcp-server.js +621 -0
- package/extensions/browser/backend/browser-resource-manager.js +1309 -0
- package/extensions/browser/backend/browser-resource-store.js +190 -0
- package/extensions/browser/backend/browser-router.js +133 -0
- package/extensions/browser/backend/executable-discovery.js +224 -0
- package/extensions/browser/backend/farming-browser-client.js +83 -0
- package/extensions/browser/backend/index.js +7 -0
- package/extensions/browser/bin/farming-browser +471 -0
- package/extensions/browser/frontend/BrowserSidebarPortals.tsx +360 -0
- package/extensions/browser/frontend/BrowserViewer.tsx +500 -0
- package/extensions/browser/frontend/browser-resource-state.ts +60 -0
- package/extensions/browser/frontend/browser.css +567 -0
- package/extensions/browser/frontend/types.ts +31 -0
- package/extensions/browser/frontend/useBrowserResources.ts +158 -0
- package/frontend/session-bridge.js +7 -6
- package/frontend/skins/crt/app.js +64 -4
- package/index.html +31 -2
- package/package.json +27 -18
- package/shared/backend-connection-status.d.ts +12 -0
- package/shared/backend-connection-status.js +35 -4
- package/shared/browser-protocol.js +1 -1
- package/backend/agent-activity.js +0 -35
- package/dist/assets/App-BrP-ENHg.css +0 -1
- package/dist/assets/App-Cm8qTjsc.js +0 -210
- package/dist/assets/FileEditorPane-fFqHA4wW.js +0 -2
- package/dist/assets/IconGlyphs-C2WFcoJS.js +0 -1
- package/dist/assets/ProjectFilesSection-Cyy3Vcn8.js +0 -12
- package/dist/assets/ReviewPage-L1vh1Ggs.js +0 -3
- package/dist/assets/api-D8nyOEbz.js +0 -1
- package/dist/assets/channel-BVWNatUB.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-d5WWh8jo.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-UHNr_yhX.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-BBXwjhUx.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Db0ib5iE.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-Bkf69XF9.js +0 -1
- package/dist/assets/code-dark-BKtvf0Xc.css +0 -1
- package/dist/assets/file-icons-BqiH0rDI.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DYFo2xmB.js +0 -1
- package/dist/assets/main-XMXgyJJt.css +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-DIYSf_qh.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-BIkrlKrZ.js +0 -8
- package/dist/assets/workspace-editor-model-D0Im34S5.js +0 -1
- package/dist/assets/workspace-view-state-BiuEbmRr.js +0 -7
- /package/dist/acp/{codex-acp-1.1.4.js → codex-acp-1.1.4.mjs} +0 -0
- /package/dist/assets/{abap-Cc5D5h5i.js → abap-CtX5nWyX.js} +0 -0
- /package/dist/assets/{apex-BKopwLmx.js → apex-DQJRAHwc.js} +0 -0
- /package/dist/assets/{array-HeX70jSN.js → array-BTwzYU0x.js} +0 -0
- /package/dist/assets/{azcli-DI-C34WA.js → azcli-9SMrOQ4q.js} +0 -0
- /package/dist/assets/{bat-Cbp6u42z.js → bat-CBGLUsw1.js} +0 -0
- /package/dist/assets/{bicep-4QA1jTCM.js → bicep-pDx8da1v.js} +0 -0
- /package/dist/assets/{cameligo-Dda_adPs.js → cameligo-hZV3vQ6_.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-Di0sYzi1.js → chunk-Y2CYZVJY-BYi0W5ea.js} +0 -0
- /package/dist/assets/{clojure-CvY8A83L.js → clojure-BhCwU3gq.js} +0 -0
- /package/dist/assets/{coffee-4LX2Sv8A.js → coffee-BVB1PsgH.js} +0 -0
- /package/dist/assets/{core-B9MwRjDE.js → core-vPkV8P_m.js} +0 -0
- /package/dist/assets/{cpp-D4AzSV28.js → cpp-eTjuiNPq.js} +0 -0
- /package/dist/assets/{csharp-C4nWN4Yd.js → csharp-1kP-kKZT.js} +0 -0
- /package/dist/assets/{csp-CiEaaS5Z.js → csp-C_iQqIes.js} +0 -0
- /package/dist/assets/{css-DXxBRsbk.js → css-CxOs7IfP.js} +0 -0
- /package/dist/assets/{cypher-DOMzqyFA.js → cypher-Cd-a9Y82.js} +0 -0
- /package/dist/assets/{cytoscape.esm-Cl8iC3D7.js → cytoscape.esm-OLojuicg.js} +0 -0
- /package/dist/assets/{dart-BWS7MXt_.js → dart-whhXpexn.js} +0 -0
- /package/dist/assets/{defaultLocale-GJwWH1Jr.js → defaultLocale-CPimVhfE.js} +0 -0
- /package/dist/assets/{dist-ClAL-Tv5.js → dist-CLPYXg1u.js} +0 -0
- /package/dist/assets/{dockerfile-Baf4U3xT.js → dockerfile-DibBi8b6.js} +0 -0
- /package/dist/assets/{ecl-CT7udlkQ.js → ecl-DlF1KdoW.js} +0 -0
- /package/dist/assets/{editor.api2-B9h6iFPK.js → editor.api2-D74ufkbr.js} +0 -0
- /package/dist/assets/{elixir-CIbAZw5l.js → elixir-CHQ8uFT3.js} +0 -0
- /package/dist/assets/{flow9-Dqiw45WV.js → flow9-8nUnVKk1.js} +0 -0
- /package/dist/assets/{fsharp-E8E2L9jU.js → fsharp-DNZNZyEE.js} +0 -0
- /package/dist/assets/{go-CouVSN4D.js → go-_kPJjNc_.js} +0 -0
- /package/dist/assets/{graphlib-CbSvZcZU.js → graphlib-CBGF9gA9.js} +0 -0
- /package/dist/assets/{graphql-a6h6h5-t.js → graphql-4HCNVt5g.js} +0 -0
- /package/dist/assets/{hcl-vso3e6N-.js → hcl-C87ymY0o.js} +0 -0
- /package/dist/assets/{ini-SwZZkotR.js → ini-BYrrGNTc.js} +0 -0
- /package/dist/assets/{init-BPLPMQ3Y.js → init-8_PCa3LO.js} +0 -0
- /package/dist/assets/{java-CtRLC4_3.js → java-B211rJbk.js} +0 -0
- /package/dist/assets/{julia-CuJWFKQH.js → julia-DVPsRtBm.js} +0 -0
- /package/dist/assets/{katex-C8DPyI6s.js → katex-C2iYad0m.js} +0 -0
- /package/dist/assets/{kotlin-B_kBhsre.js → kotlin-BYyNerjp.js} +0 -0
- /package/dist/assets/{less-CtrHIfz3.js → less-EC9UblGE.js} +0 -0
- /package/dist/assets/{lexon-yVt1pJLu.js → lexon-B1yuUdqW.js} +0 -0
- /package/dist/assets/{lua-prOwgJ4U.js → lua-Bo6GFzxT.js} +0 -0
- /package/dist/assets/{m3-DhrdTQ84.js → m3-C-Y9cBVI.js} +0 -0
- /package/dist/assets/{markdown-DoI5HwSM.js → markdown-BHu-wLbe.js} +0 -0
- /package/dist/assets/{mermaid-parser.core-BQtepoLd.js → mermaid-parser.core-IxcGtNYG.js} +0 -0
- /package/dist/assets/{mips-Dye5jsBU.js → mips-Dkds26rX.js} +0 -0
- /package/dist/assets/{msdax-TkNOyIL8.js → msdax-PU4g-8_g.js} +0 -0
- /package/dist/assets/{mysql-CEc-RKZF.js → mysql-fPpuGpS7.js} +0 -0
- /package/dist/assets/{objective-c-CqqCsQz0.js → objective-c-Wy1e5JUf.js} +0 -0
- /package/dist/assets/{pascal-DO_pj8yg.js → pascal-BIXwvrqU.js} +0 -0
- /package/dist/assets/{pascaligo-1cbdZdF3.js → pascaligo-DwR0AtL_.js} +0 -0
- /package/dist/assets/{path-YdFzr2W6.js → path-BxQuywGl.js} +0 -0
- /package/dist/assets/{perl-B_N3PYfC.js → perl-CGHAl8my.js} +0 -0
- /package/dist/assets/{pgsql-9iYk1FQ7.js → pgsql-B91M73E2.js} +0 -0
- /package/dist/assets/{php-rO2sgoUM.js → php-C70ttrCx.js} +0 -0
- /package/dist/assets/{pla-Cbgv60kq.js → pla-BNrXaRh0.js} +0 -0
- /package/dist/assets/{postiats-y6xrhy_M.js → postiats-Dv2IAZR9.js} +0 -0
- /package/dist/assets/{powerquery-BaD8gWNE.js → powerquery-CkY5Wq7J.js} +0 -0
- /package/dist/assets/{powershell-BMhG4JVe.js → powershell-uGIYLU3C.js} +0 -0
- /package/dist/assets/{protobuf-CdOP5kN3.js → protobuf-CavfmJu-.js} +0 -0
- /package/dist/assets/{pug-Ck60hNF9.js → pug-Drqnyitx.js} +0 -0
- /package/dist/assets/{qrcode-DV-_rjU8.js → qrcode-7uGsTEmi.js} +0 -0
- /package/dist/assets/{qsharp-BcaKFfP-.js → qsharp-C7yF-Dti.js} +0 -0
- /package/dist/assets/{r-rFNes0iK.js → r-HXLGP0_-.js} +0 -0
- /package/dist/assets/{redis-DkpQ3K6G.js → redis-C4y6mf2p.js} +0 -0
- /package/dist/assets/{redshift-Di9N5TI3.js → redshift-DdQrFPrv.js} +0 -0
- /package/dist/assets/{restructuredtext-By5ZzLt6.js → restructuredtext-CBrf2PNj.js} +0 -0
- /package/dist/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
- /package/dist/assets/{ruby-fF5zGiLp.js → ruby-CVtMoX2B.js} +0 -0
- /package/dist/assets/{rust-C5b3AYY6.js → rust-Ckq4sbMP.js} +0 -0
- /package/dist/assets/{sb-CD07UNRs.js → sb-er66OpE3.js} +0 -0
- /package/dist/assets/{scala-jtn1ip9G.js → scala-BdWZz-hH.js} +0 -0
- /package/dist/assets/{scheme-C4vqMkn_.js → scheme-kTV_s2kP.js} +0 -0
- /package/dist/assets/{scss-I4Glxjbz.js → scss-Bymy2VJQ.js} +0 -0
- /package/dist/assets/{shell-DubUcuns.js → shell-bwEP1LQ1.js} +0 -0
- /package/dist/assets/{solidity-CNy2KSl2.js → solidity-DCddeBgL.js} +0 -0
- /package/dist/assets/{sophia-D9_URt84.js → sophia-BMN3AQig.js} +0 -0
- /package/dist/assets/{sparql-DviXpm4C.js → sparql-CSXOLjzi.js} +0 -0
- /package/dist/assets/{sql-vDRbAZgK.js → sql-COg8ewmK.js} +0 -0
- /package/dist/assets/{st-CnpnRqA7.js → st-CIaZtBS_.js} +0 -0
- /package/dist/assets/{swift-D_g8Ganz.js → swift-DXiOhUHu.js} +0 -0
- /package/dist/assets/{systemverilog-OuZa5h1M.js → systemverilog-BTDE6Dr2.js} +0 -0
- /package/dist/assets/{tcl-DBm9ucsV.js → tcl-B0zy9NvV.js} +0 -0
- /package/dist/assets/{twig-BRzEqX5Y.js → twig-D594l4Z1.js} +0 -0
- /package/dist/assets/{typespec-D4ZgGeX8.js → typespec-XKVjaPqo.js} +0 -0
- /package/dist/assets/{useWorkspaceMenuKeyboard-Brws6Ar9.js → useWorkspaceMenuKeyboard-Bpy_s69Z.js} +0 -0
- /package/dist/assets/{vb-DcP4s6WS.js → vb-B2wYoKRV.js} +0 -0
- /package/dist/assets/{wgsl-CnRJ_QUf.js → wgsl-D0Yf2LbK.js} +0 -0
- /package/dist/assets/{workspace-working-copy-D8-s_Sgh.js → workspace-working-copy-C3FSR3N6.js} +0 -0
|
@@ -159,6 +159,7 @@ function discoverClaudeSkillCommands(commands, skillsDir, sourceLabel) {
|
|
|
159
159
|
label: commandLabel(entry.name),
|
|
160
160
|
description: `Claude skill from ${sourceLabel}`,
|
|
161
161
|
source: 'skill',
|
|
162
|
+
scope: sourceLabel === 'home' ? 'Personal' : commandLabel(sourceLabel),
|
|
162
163
|
});
|
|
163
164
|
});
|
|
164
165
|
}
|
|
@@ -180,10 +181,100 @@ function discoverClaudeCustomCommands(commands, commandsDir, sourceLabel) {
|
|
|
180
181
|
label: commandLabel(name),
|
|
181
182
|
description: `Claude custom command from ${sourceLabel}`,
|
|
182
183
|
source: 'custom',
|
|
184
|
+
scope: sourceLabel === 'home' ? 'Personal' : commandLabel(sourceLabel),
|
|
183
185
|
});
|
|
184
186
|
});
|
|
185
187
|
}
|
|
186
188
|
|
|
189
|
+
function discoverClaudePluginComponents(commands, pluginPath, pluginName) {
|
|
190
|
+
discoverSkillFiles(path.join(pluginPath, 'skills')).forEach(skillFile => {
|
|
191
|
+
const skillName = path.basename(path.dirname(skillFile));
|
|
192
|
+
const metadata = parseSkillFrontMatter(skillFile);
|
|
193
|
+
addCommand(commands, {
|
|
194
|
+
command: `/${pluginName}:${skillName}`,
|
|
195
|
+
label: commandLabel(metadata.name || skillName),
|
|
196
|
+
description: metadata.description || `Claude skill from ${pluginName}`,
|
|
197
|
+
source: 'skill',
|
|
198
|
+
scope: 'Plugin',
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
let commandEntries = [];
|
|
203
|
+
try {
|
|
204
|
+
commandEntries = fs.readdirSync(path.join(pluginPath, 'commands'), { withFileTypes: true });
|
|
205
|
+
} catch {
|
|
206
|
+
// Plugins do not have to provide commands.
|
|
207
|
+
}
|
|
208
|
+
commandEntries.forEach(entry => {
|
|
209
|
+
if (!entry.isFile() || !entry.name.endsWith('.md')) return;
|
|
210
|
+
const commandName = entry.name.slice(0, -3);
|
|
211
|
+
if (!SAFE_COMMAND_NAME.test(commandName)) return;
|
|
212
|
+
addCommand(commands, {
|
|
213
|
+
command: `/${pluginName}:${commandName}`,
|
|
214
|
+
label: commandLabel(commandName),
|
|
215
|
+
description: `Claude command from ${pluginName}`,
|
|
216
|
+
source: 'custom',
|
|
217
|
+
scope: 'Plugin',
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function readBoundedJson(filePath, maxBytes = 1024 * 1024) {
|
|
223
|
+
try {
|
|
224
|
+
const stats = fs.statSync(filePath);
|
|
225
|
+
if (!stats.isFile() || stats.size > maxBytes) return null;
|
|
226
|
+
const value = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
227
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function addClaudePlugin(commands, pluginName, pluginPath, scope = 'Plugin') {
|
|
234
|
+
if (!SAFE_COMMAND_NAME.test(pluginName)) return;
|
|
235
|
+
const manifest = readBoundedJson(path.join(pluginPath, '.claude-plugin', 'plugin.json'));
|
|
236
|
+
addCommand(commands, {
|
|
237
|
+
command: `plugin:${pluginName}`,
|
|
238
|
+
label: String(manifest?.name || commandLabel(pluginName)).slice(0, 100),
|
|
239
|
+
description: String(manifest?.description || 'Claude Code plugin').slice(0, 500),
|
|
240
|
+
source: 'plugin',
|
|
241
|
+
scope,
|
|
242
|
+
});
|
|
243
|
+
discoverClaudePluginComponents(commands, pluginPath, pluginName);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function discoverClaudeInstalledPlugins(commands, homePath) {
|
|
247
|
+
const pluginsRoot = path.join(homePath, 'plugins');
|
|
248
|
+
const installed = readBoundedJson(path.join(pluginsRoot, 'installed_plugins.json'));
|
|
249
|
+
Object.entries(installed?.plugins || {}).slice(0, MAX_DISCOVERED_SKILLS).forEach(([key, rawEntries]) => {
|
|
250
|
+
const pluginName = key.split('@')[0];
|
|
251
|
+
const entries = Array.isArray(rawEntries) ? rawEntries : [];
|
|
252
|
+
const installedEntry = entries.find(entry => (
|
|
253
|
+
entry && typeof entry === 'object' && typeof entry.installPath === 'string'
|
|
254
|
+
));
|
|
255
|
+
if (!installedEntry) return;
|
|
256
|
+
addClaudePlugin(
|
|
257
|
+
commands,
|
|
258
|
+
pluginName,
|
|
259
|
+
installedEntry.installPath,
|
|
260
|
+
String(installedEntry.scope || 'Plugin'),
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
let entries = [];
|
|
265
|
+
try {
|
|
266
|
+
entries = fs.readdirSync(pluginsRoot, { withFileTypes: true });
|
|
267
|
+
} catch {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
entries.forEach(entry => {
|
|
271
|
+
if (!entry.isDirectory() || !SAFE_COMMAND_NAME.test(entry.name)) return;
|
|
272
|
+
const pluginPath = path.join(pluginsRoot, entry.name);
|
|
273
|
+
if (!fs.existsSync(path.join(pluginPath, '.claude-plugin', 'plugin.json'))) return;
|
|
274
|
+
addClaudePlugin(commands, entry.name, pluginPath);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
187
278
|
function discoverClaudeSlashCommands({ homeDir = os.homedir(), workspace } = {}) {
|
|
188
279
|
const commands = [];
|
|
189
280
|
const normalizedWorkspace = normalizeWorkspace(workspace, homeDir);
|
|
@@ -249,15 +340,26 @@ function discoverPluginSkillFiles(root, depth = 0, skillFiles = []) {
|
|
|
249
340
|
|
|
250
341
|
function pluginNameForSkillFile(skillFile, pluginsCacheDir) {
|
|
251
342
|
const parts = path.relative(pluginsCacheDir, skillFile).split(path.sep);
|
|
252
|
-
if (parts[0] === 'openai-curated' && parts[1]) return parts[1];
|
|
343
|
+
if ((parts[0] === 'openai-curated' || parts[0] === 'openai-curated-remote') && parts[1]) return parts[1];
|
|
253
344
|
if ((parts[0] === 'openai-primary-runtime' || parts[0] === 'openai-bundled') && parts[1]) return parts[1];
|
|
254
345
|
return parts[0] || '';
|
|
255
346
|
}
|
|
256
347
|
|
|
257
348
|
function discoverCodexPluginSkills(commands, homeDir) {
|
|
258
349
|
const pluginsCacheDir = path.join(homeDir, '.codex', 'plugins', 'cache');
|
|
350
|
+
discoverCodexPluginSkillsAt(commands, pluginsCacheDir);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function discoverCodexPluginSkillsAt(commands, pluginsCacheDir) {
|
|
259
354
|
discoverPluginSkillFiles(pluginsCacheDir).forEach(skillFile => {
|
|
260
355
|
const pluginName = pluginNameForSkillFile(skillFile, pluginsCacheDir);
|
|
356
|
+
addCommand(commands, {
|
|
357
|
+
command: `plugin:${pluginName}`,
|
|
358
|
+
label: commandLabel(pluginName),
|
|
359
|
+
description: 'Codex plugin',
|
|
360
|
+
source: 'plugin',
|
|
361
|
+
scope: 'Plugin',
|
|
362
|
+
});
|
|
261
363
|
addSkillMention(commands, skillFile, {
|
|
262
364
|
mentionPrefix: pluginName,
|
|
263
365
|
fallbackName: path.basename(path.dirname(skillFile)),
|
|
@@ -278,6 +380,40 @@ function discoverCodexSkillMentions({ homeDir = os.homedir(), workspace } = {})
|
|
|
278
380
|
return commands.slice(0, MAX_DISCOVERED_SKILLS);
|
|
279
381
|
}
|
|
280
382
|
|
|
383
|
+
function discoverAgentExtensions({
|
|
384
|
+
provider,
|
|
385
|
+
providerHomePath,
|
|
386
|
+
workspace,
|
|
387
|
+
homeDir = os.homedir(),
|
|
388
|
+
} = {}) {
|
|
389
|
+
const normalizedProvider = normalizeProvider(provider);
|
|
390
|
+
const homePath = normalizeWorkspace(providerHomePath);
|
|
391
|
+
const normalizedHomeDir = normalizeWorkspace(homeDir) || os.homedir();
|
|
392
|
+
if (!normalizedProvider || !homePath) return [];
|
|
393
|
+
|
|
394
|
+
const commands = [];
|
|
395
|
+
if (normalizedProvider === 'claude') {
|
|
396
|
+
const normalizedWorkspace = normalizeWorkspace(workspace);
|
|
397
|
+
if (normalizedWorkspace) {
|
|
398
|
+
discoverClaudeSkillCommands(commands, path.join(normalizedWorkspace, '.claude', 'skills'), 'workspace');
|
|
399
|
+
discoverClaudeCustomCommands(commands, path.join(normalizedWorkspace, '.claude', 'commands'), 'workspace');
|
|
400
|
+
}
|
|
401
|
+
discoverClaudeSkillCommands(commands, path.join(homePath, 'skills'), 'home');
|
|
402
|
+
discoverClaudeCustomCommands(commands, path.join(homePath, 'commands'), 'home');
|
|
403
|
+
discoverClaudeInstalledPlugins(commands, homePath);
|
|
404
|
+
return commands.slice(0, MAX_DISCOVERED_SKILLS);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
workspaceSkillRoots(workspace, normalizedHomeDir)
|
|
408
|
+
.forEach(root => discoverDirectCodexSkills(commands, root, 'Repo'));
|
|
409
|
+
discoverDirectCodexSkills(commands, path.join(normalizedHomeDir, '.agents', 'skills'), 'Personal');
|
|
410
|
+
discoverDirectCodexSkills(commands, path.join(homePath, 'skills'), 'Personal');
|
|
411
|
+
discoverDirectCodexSkills(commands, path.join(homePath, 'skills', '.system'), 'System');
|
|
412
|
+
discoverDirectCodexSkills(commands, path.join('/etc', 'codex', 'skills'), 'Admin');
|
|
413
|
+
discoverCodexPluginSkillsAt(commands, path.join(homePath, 'plugins', 'cache'));
|
|
414
|
+
return commands.slice(0, MAX_DISCOVERED_SKILLS);
|
|
415
|
+
}
|
|
416
|
+
|
|
281
417
|
function discoverSlashCommands({ provider, homeDir = os.homedir(), workspace } = {}) {
|
|
282
418
|
const normalizedProvider = normalizeProvider(provider);
|
|
283
419
|
if (normalizedProvider === 'codex') {
|
|
@@ -290,6 +426,7 @@ function discoverSlashCommands({ provider, homeDir = os.homedir(), workspace } =
|
|
|
290
426
|
}
|
|
291
427
|
|
|
292
428
|
module.exports = {
|
|
429
|
+
discoverAgentExtensions,
|
|
293
430
|
discoverSlashCommands,
|
|
294
431
|
discoverClaudeSlashCommands,
|
|
295
432
|
discoverCodexSkillMentions,
|
|
@@ -49,6 +49,34 @@ function usageHistoryCacheFile(configDir) {
|
|
|
49
49
|
return path.join(historyDir(configDir), 'usage-history-v2.sqlite3');
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function browserResourcesDir(configDir) {
|
|
53
|
+
return path.join(configDir, 'browsers');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function browserResourcesFile(configDir) {
|
|
57
|
+
return path.join(browserResourcesDir(configDir), 'resources.json');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function browserProfileDir(configDir, browserId) {
|
|
61
|
+
return path.join(browserResourcesDir(configDir), browserId, 'profile');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function runtimeDependenciesDir(configDir) {
|
|
65
|
+
return path.join(configDir, 'runtimes');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function runtimeDependenciesActiveFile(configDir) {
|
|
69
|
+
return path.join(runtimeDependenciesDir(configDir), 'active.json');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function runtimeDependenciesLockDir(configDir) {
|
|
73
|
+
return path.join(runtimeDependenciesDir(configDir), '.prepare.lock');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function farmingAgentBootstrapFile(configDir) {
|
|
77
|
+
return path.join(configDir, 'farming-agent-bootstrap.zh_cn.md');
|
|
78
|
+
}
|
|
79
|
+
|
|
52
80
|
function sessionIndexFile(configDir) {
|
|
53
81
|
return path.join(sessionsDir(configDir), 'index.json');
|
|
54
82
|
}
|
|
@@ -124,6 +152,10 @@ function farmingNetTrustFile(configDir) {
|
|
|
124
152
|
module.exports = {
|
|
125
153
|
acpCheckpointsDir,
|
|
126
154
|
agentStateFile,
|
|
155
|
+
browserProfileDir,
|
|
156
|
+
browserResourcesDir,
|
|
157
|
+
browserResourcesFile,
|
|
158
|
+
farmingAgentBootstrapFile,
|
|
127
159
|
farmingNetInstancesFile,
|
|
128
160
|
farmingNetServerLogFile,
|
|
129
161
|
farmingNetServerStateFile,
|
|
@@ -132,6 +164,9 @@ module.exports = {
|
|
|
132
164
|
farmingNetTrustFile,
|
|
133
165
|
farmingConfigDir,
|
|
134
166
|
historyDir,
|
|
167
|
+
runtimeDependenciesActiveFile,
|
|
168
|
+
runtimeDependenciesDir,
|
|
169
|
+
runtimeDependenciesLockDir,
|
|
135
170
|
nativePtyControllerGenerationFile,
|
|
136
171
|
nativePtyControllerGenerationLockDir,
|
|
137
172
|
nativePtyHostLogFile,
|
|
@@ -7,6 +7,7 @@ const os = require('os');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { Transform } = require('stream');
|
|
9
9
|
const { pipeline } = require('stream/promises');
|
|
10
|
+
const { readServerProcessIdentity } = require('./server-process-identity');
|
|
10
11
|
const storageLayout = require('./storage-layout');
|
|
11
12
|
|
|
12
13
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
@@ -789,6 +790,15 @@ class FarmingUpdateService {
|
|
|
789
790
|
this.getNpmGlobalRoot = options.getNpmGlobalRoot
|
|
790
791
|
|| ((npmCommand, npmPrefix) => readNpmGlobalRoot(npmCommand, npmPrefix, this.execFile));
|
|
791
792
|
this.spawn = options.spawn || childProcess.spawn;
|
|
793
|
+
this.prepareRuntimeDependencies = options.prepareRuntimeDependencies || (async (releaseDir) => {
|
|
794
|
+
if (process.env.NODE_ENV === 'test') return;
|
|
795
|
+
const managerPath = path.join(releaseDir, 'backend', 'runtime-dependency-manager.js');
|
|
796
|
+
const { prepareRuntimeDependencies } = require(managerPath);
|
|
797
|
+
await prepareRuntimeDependencies({
|
|
798
|
+
configDir: this.configDir,
|
|
799
|
+
env: { ...process.env, FARMING_CONFIG_DIR: this.configDir },
|
|
800
|
+
});
|
|
801
|
+
});
|
|
792
802
|
this.latestCache = null;
|
|
793
803
|
this.npmCache = null;
|
|
794
804
|
this.installState = { phase: 'idle' };
|
|
@@ -1300,6 +1310,10 @@ class FarmingUpdateService {
|
|
|
1300
1310
|
}
|
|
1301
1311
|
|
|
1302
1312
|
const nodePath = process.env.FARMING_NODE_BIN || process.execPath;
|
|
1313
|
+
const serverProcessIdentity = await readServerProcessIdentity(process.pid);
|
|
1314
|
+
if (!serverProcessIdentity) {
|
|
1315
|
+
throw new Error('Running Farming server process identity could not be verified');
|
|
1316
|
+
}
|
|
1303
1317
|
let helperPath;
|
|
1304
1318
|
let payload;
|
|
1305
1319
|
let env;
|
|
@@ -1343,6 +1357,7 @@ class FarmingUpdateService {
|
|
|
1343
1357
|
stagingPrefix,
|
|
1344
1358
|
stagingPackageRoot,
|
|
1345
1359
|
serverPid: process.pid,
|
|
1360
|
+
serverProcessIdentity,
|
|
1346
1361
|
configDir: this.configDir,
|
|
1347
1362
|
port: process.env.FARMING_PORT || process.env.PORT || '6694',
|
|
1348
1363
|
basePath: process.env.FARMING_BASE_PATH || '/farming',
|
|
@@ -1483,6 +1498,16 @@ class FarmingUpdateService {
|
|
|
1483
1498
|
}
|
|
1484
1499
|
const logPath = this.installState.logPath || path.join(this.configDir, 'farming-update.log');
|
|
1485
1500
|
const targetMethod = String(releaseMetadata.updateMethod || releaseMetadata.type || this.installMethod);
|
|
1501
|
+
this.persistInstallState({
|
|
1502
|
+
...this.installState,
|
|
1503
|
+
phase: 'preparing-runtimes',
|
|
1504
|
+
targetMethod,
|
|
1505
|
+
releaseDir,
|
|
1506
|
+
installer,
|
|
1507
|
+
logPath,
|
|
1508
|
+
});
|
|
1509
|
+
await this.prepareRuntimeDependencies(releaseDir);
|
|
1510
|
+
const runtimePreparedAt = new Date(this.now()).toISOString();
|
|
1486
1511
|
this.persistInstallState({
|
|
1487
1512
|
...this.installState,
|
|
1488
1513
|
phase: 'ready-to-restart',
|
|
@@ -1490,7 +1515,8 @@ class FarmingUpdateService {
|
|
|
1490
1515
|
releaseDir,
|
|
1491
1516
|
installer,
|
|
1492
1517
|
logPath,
|
|
1493
|
-
preparedAt:
|
|
1518
|
+
preparedAt: runtimePreparedAt,
|
|
1519
|
+
runtimePreparedAt,
|
|
1494
1520
|
});
|
|
1495
1521
|
}
|
|
1496
1522
|
}
|
|
@@ -4,6 +4,7 @@ const os = require('os');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { inspectGitWorktree } = require('./git-worktree-info');
|
|
6
6
|
const { WorkspaceFileError } = require('./workspace-file-service');
|
|
7
|
+
const { PreviewSessionManager } = require('./preview-session-manager');
|
|
7
8
|
const {
|
|
8
9
|
GLOBAL_WORKSPACE_FILES_AGENT_ID,
|
|
9
10
|
GLOBAL_WORKSPACE_FILES_ROOT,
|
|
@@ -57,6 +58,53 @@ function isSameOrDescendantPath(root, target) {
|
|
|
57
58
|
return relative === '' || Boolean(relative) && !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
function previewAuthorizedRootForTarget(allowedRoots, target) {
|
|
62
|
+
return allowedRoots
|
|
63
|
+
.filter(root => isSameOrDescendantPath(root, target))
|
|
64
|
+
.sort((left, right) => right.length - left.length)[0] || '';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizePreviewAssetPath(value) {
|
|
68
|
+
const normalized = path.posix.normalize(String(value || '').replace(/\\/g, '/').replace(/^\/+/, ''));
|
|
69
|
+
if (!normalized || normalized === '.') return '';
|
|
70
|
+
if (normalized === '..' || normalized.startsWith('../') || path.posix.isAbsolute(normalized)) {
|
|
71
|
+
throw new WorkspaceFileError('preview path must stay inside the authorized root', 403);
|
|
72
|
+
}
|
|
73
|
+
return normalized;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function previewContentSecurityPolicy() {
|
|
77
|
+
return [
|
|
78
|
+
"default-src 'none'",
|
|
79
|
+
"script-src 'none'",
|
|
80
|
+
"style-src 'self' 'unsafe-inline'",
|
|
81
|
+
"img-src 'self' data:",
|
|
82
|
+
"font-src 'self' data:",
|
|
83
|
+
"media-src 'self' data:",
|
|
84
|
+
"connect-src 'none'",
|
|
85
|
+
"frame-src 'none'",
|
|
86
|
+
"object-src 'none'",
|
|
87
|
+
"base-uri 'none'",
|
|
88
|
+
"form-action 'none'",
|
|
89
|
+
"frame-ancestors 'self'",
|
|
90
|
+
].join('; ');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function escapeHtmlAttribute(value) {
|
|
94
|
+
return String(value || '')
|
|
95
|
+
.replace(/&/g, '&')
|
|
96
|
+
.replace(/"/g, '"')
|
|
97
|
+
.replace(/</g, '<')
|
|
98
|
+
.replace(/>/g, '>');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function rewritePreviewHtmlRootReferences(source, rootUrl) {
|
|
102
|
+
const safeRootUrl = escapeHtmlAttribute(rootUrl);
|
|
103
|
+
return String(source || '')
|
|
104
|
+
.replace(/(\b(?:href|src|poster)\s*=\s*["'])\/(?!\/)/gi, `$1${safeRootUrl}`)
|
|
105
|
+
.replace(/(url\(\s*["']?)\/(?!\/)/gi, `$1${safeRootUrl}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
60
108
|
function globalUserPathToAbsolute(userPath = '') {
|
|
61
109
|
const normalized = String(userPath || '')
|
|
62
110
|
.trim()
|
|
@@ -222,6 +270,7 @@ function readOptionsForAgent(agentManager, agentId) {
|
|
|
222
270
|
function createWorkspaceFileRouter(agentManager, fileService, options = {}) {
|
|
223
271
|
const router = express.Router();
|
|
224
272
|
const rootRegistry = options.rootRegistry || workspaceRootRegistryFor(agentManager);
|
|
273
|
+
const previewSessions = options.previewSessionManager || new PreviewSessionManager();
|
|
225
274
|
|
|
226
275
|
router.use(express.json({ limit: '3mb' }));
|
|
227
276
|
|
|
@@ -287,6 +336,98 @@ function createWorkspaceFileRouter(agentManager, fileService, options = {}) {
|
|
|
287
336
|
}
|
|
288
337
|
});
|
|
289
338
|
|
|
339
|
+
router.post('/previews', async (req, res) => {
|
|
340
|
+
try {
|
|
341
|
+
const body = req.body || {};
|
|
342
|
+
const rootRef = workspaceRef(body);
|
|
343
|
+
let entryPath = String(body.path || '').trim();
|
|
344
|
+
if (!/\.html?$/i.test(entryPath)) {
|
|
345
|
+
throw new WorkspaceFileError('HTML preview requires an .html or .htm file', 415);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let authorizedRoot;
|
|
349
|
+
if (isGlobalWorkspaceFilesAgentId(rootRef)) {
|
|
350
|
+
if (body.exact === true) {
|
|
351
|
+
entryPath = assertExactExternalFileReadable(entryPath);
|
|
352
|
+
authorizedRoot = path.dirname(realPathIfPresent(globalUserPathToAbsolute(entryPath)));
|
|
353
|
+
if (authorizedRoot === path.parse(authorizedRoot).root) {
|
|
354
|
+
throw new WorkspaceFileError('external HTML preview directory cannot be the filesystem root', 403);
|
|
355
|
+
}
|
|
356
|
+
} else {
|
|
357
|
+
const authorization = assertGlobalWorkspacePathAllowed(agentManager, entryPath);
|
|
358
|
+
authorizedRoot = previewAuthorizedRootForTarget(authorization.allowedRoots, realPathIfPresent(authorization.target));
|
|
359
|
+
if (!authorizedRoot) throw new WorkspaceFileError('global file path is outside allowed workspaces', 403);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const { root, rootId } = resolveRequestRoot(body);
|
|
364
|
+
await fileService.readFile(root, entryPath, { allowedExternalRoots: [] });
|
|
365
|
+
const session = previewSessions.createStatic({
|
|
366
|
+
rootId,
|
|
367
|
+
workspaceRoot: root,
|
|
368
|
+
authorizedRoot: realPathIfPresent(authorizedRoot || root),
|
|
369
|
+
entryPath,
|
|
370
|
+
baseDirectory: path.posix.dirname(entryPath) === '.' ? '' : path.posix.dirname(entryPath),
|
|
371
|
+
});
|
|
372
|
+
res.status(201).json({
|
|
373
|
+
preview: {
|
|
374
|
+
id: session.id,
|
|
375
|
+
kind: session.kind,
|
|
376
|
+
expiresAt: session.expiresAt,
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
} catch (error) {
|
|
380
|
+
sendWorkspaceFileError(res, error);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
router.get('/previews/:sessionId/:scope/*', async (req, res) => {
|
|
385
|
+
try {
|
|
386
|
+
const session = previewSessions.get(req.params.sessionId);
|
|
387
|
+
if (!session) throw new WorkspaceFileError('preview session not found or expired', 404);
|
|
388
|
+
const scope = req.params.scope;
|
|
389
|
+
if (scope !== 'base' && scope !== 'root') {
|
|
390
|
+
throw new WorkspaceFileError('preview scope is invalid', 400);
|
|
391
|
+
}
|
|
392
|
+
const assetPath = normalizePreviewAssetPath(req.params[0] || '');
|
|
393
|
+
const resourcePath = scope === 'base'
|
|
394
|
+
? path.posix.join(session.baseDirectory, assetPath)
|
|
395
|
+
: assetPath;
|
|
396
|
+
if (!resourcePath) throw new WorkspaceFileError('preview resource path is required', 400);
|
|
397
|
+
|
|
398
|
+
const absoluteTarget = realPathIfPresent(path.resolve(session.workspaceRoot, resourcePath));
|
|
399
|
+
if (!isSameOrDescendantPath(session.authorizedRoot, absoluteTarget)) {
|
|
400
|
+
throw new WorkspaceFileError('preview path must stay inside the authorized root', 403);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const file = await fileService.readResourceFile(session.workspaceRoot, resourcePath, {
|
|
404
|
+
allowedExternalRoots: [],
|
|
405
|
+
});
|
|
406
|
+
const isHtml = /\.html?$/i.test(file.path);
|
|
407
|
+
const body = isHtml
|
|
408
|
+
? Buffer.from(rewritePreviewHtmlRootReferences(
|
|
409
|
+
file.buffer.toString('utf8'),
|
|
410
|
+
`${req.baseUrl}/previews/${encodeURIComponent(session.id)}/root/`,
|
|
411
|
+
))
|
|
412
|
+
: file.buffer;
|
|
413
|
+
res
|
|
414
|
+
.status(200)
|
|
415
|
+
.type(path.extname(file.path) || 'application/octet-stream')
|
|
416
|
+
.set('Cache-Control', 'no-store')
|
|
417
|
+
.set('X-Content-Type-Options', 'nosniff')
|
|
418
|
+
.set('Content-Length', String(body.length));
|
|
419
|
+
if (isHtml) res.set('Content-Security-Policy', previewContentSecurityPolicy());
|
|
420
|
+
res.send(body);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
sendWorkspaceFileError(res, error);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
router.delete('/previews/:sessionId', (req, res) => {
|
|
427
|
+
const deleted = previewSessions.delete(req.params.sessionId);
|
|
428
|
+
res.status(deleted ? 204 : 404).end();
|
|
429
|
+
});
|
|
430
|
+
|
|
290
431
|
router.put('/file', async (req, res) => {
|
|
291
432
|
try {
|
|
292
433
|
const body = req.body || {};
|
|
@@ -2199,6 +2199,25 @@ class WorkspaceFileService {
|
|
|
2199
2199
|
};
|
|
2200
2200
|
}
|
|
2201
2201
|
|
|
2202
|
+
async readResourceFile(workspaceRoot, userPath, options = {}) {
|
|
2203
|
+
const { target, relativePath } = await this.resolvePath(workspaceRoot, userPath, options);
|
|
2204
|
+
const stat = await fsp.stat(target);
|
|
2205
|
+
if (!stat.isFile()) {
|
|
2206
|
+
throw new WorkspaceFileError('path must be a file', 400);
|
|
2207
|
+
}
|
|
2208
|
+
if (stat.size > this.maxPreviewFileSize) {
|
|
2209
|
+
throw new WorkspaceFileError('file is too large to preview', 413, { size: stat.size });
|
|
2210
|
+
}
|
|
2211
|
+
const buffer = await fsp.readFile(target);
|
|
2212
|
+
return {
|
|
2213
|
+
path: relativePath,
|
|
2214
|
+
buffer,
|
|
2215
|
+
size: stat.size,
|
|
2216
|
+
mtimeMs: stat.mtimeMs,
|
|
2217
|
+
sha1: sha1(buffer),
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2202
2221
|
async blameCapability(workspaceRoot, userPath, options = {}) {
|
|
2203
2222
|
const { root, target, relativePath, actualRelativePath, external } = await this.resolvePath(workspaceRoot, userPath, options);
|
|
2204
2223
|
const capability = (available, reason = '') => ({
|
|
@@ -27,6 +27,7 @@ function canonicalWorkspacePath(value) {
|
|
|
27
27
|
function rootIdForPath(value) {
|
|
28
28
|
const canonicalPath = canonicalWorkspacePath(value);
|
|
29
29
|
if (!canonicalPath) return '';
|
|
30
|
+
if (canonicalPath === GLOBAL_WORKSPACE_FILES_ROOT) return GLOBAL_WORKSPACE_ROOT_ID;
|
|
30
31
|
let hash = 0xcbf29ce484222325n;
|
|
31
32
|
for (const byte of Buffer.from(canonicalPath, 'utf8')) {
|
|
32
33
|
hash ^= BigInt(byte);
|
|
@@ -77,15 +78,16 @@ class WorkspaceRootRegistry {
|
|
|
77
78
|
if (current && current.canonicalPath !== canonicalPath) {
|
|
78
79
|
throw new WorkspaceFileError('workspace root identity collision', 409);
|
|
79
80
|
}
|
|
81
|
+
const isGlobalRoot = rootId === GLOBAL_WORKSPACE_ROOT_ID;
|
|
80
82
|
const root = Object.freeze({
|
|
81
83
|
rootId,
|
|
82
|
-
kind: options.kind || current?.kind || 'directory',
|
|
84
|
+
kind: isGlobalRoot ? 'global' : (options.kind || current?.kind || 'directory'),
|
|
83
85
|
canonicalPath,
|
|
84
86
|
repositoryId: options.repositoryId || current?.repositoryId || '',
|
|
85
87
|
accessPolicy: Object.freeze({
|
|
86
|
-
readOnly: options.accessPolicy?.readOnly === true,
|
|
87
|
-
watch: options.accessPolicy?.watch !== false,
|
|
88
|
-
externalReads: options.accessPolicy?.externalReads !== false,
|
|
88
|
+
readOnly: isGlobalRoot || options.accessPolicy?.readOnly === true,
|
|
89
|
+
watch: isGlobalRoot ? false : options.accessPolicy?.watch !== false,
|
|
90
|
+
externalReads: isGlobalRoot ? false : options.accessPolicy?.externalReads !== false,
|
|
89
91
|
}),
|
|
90
92
|
});
|
|
91
93
|
this.roots.set(rootId, root);
|
|
@@ -13,5 +13,9 @@ FARMING_BASE_PATH=/farming
|
|
|
13
13
|
|
|
14
14
|
# 应用内升级源在 Web Settings 中配置,并保存到 ~/.farming/settings.json。
|
|
15
15
|
|
|
16
|
+
# Farming 随发行包提供默认公共 npm 镜像候选。
|
|
17
|
+
# 可设置为其他 HTTPS Registry Origin,或设置为 off 关闭镜像候选。
|
|
18
|
+
# FARMING_RUNTIME_NPM_MIRROR=off
|
|
19
|
+
|
|
16
20
|
# 可选:按 cgroup / 系统内存自动计算 Node heap;设置为 0 表示使用 Node 默认值。
|
|
17
21
|
# FARMING_NODE_MAX_OLD_SPACE_SIZE=auto
|