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
package/backend/server.js
CHANGED
|
@@ -42,14 +42,17 @@ const { createControlRouter } = require('./control-api');
|
|
|
42
42
|
const { WorkspaceFileService, WorkspaceFileError } = require('./workspace-file-service');
|
|
43
43
|
const { createWorkspaceFileRouter, resolveWorkspaceRoot } = require('./workspace-file-router');
|
|
44
44
|
const { WorkspaceRootRegistry, rootIdForPath } = require('./workspace-root-registry');
|
|
45
|
+
const {
|
|
46
|
+
BrowserResourceManager,
|
|
47
|
+
createBrowserRouter,
|
|
48
|
+
} = require('../extensions/browser/backend');
|
|
45
49
|
const { UsageMonitor } = require('./usage-monitor');
|
|
46
50
|
const { CodexContextWindowReader } = require('./codex-context-window');
|
|
47
51
|
const { DEFAULT_MAX_TURNS: DEFAULT_CODEX_TRANSCRIPT_MAX_TURNS, readCodexTranscript } = require('./codex-transcript');
|
|
48
52
|
const { AsyncCache } = require('./async-cache');
|
|
49
53
|
const { getMainAgentSkillsCatalog } = require('./main-agent-skills');
|
|
50
|
-
const { discoverSlashCommands } = require('./slash-command-discovery');
|
|
54
|
+
const { discoverAgentExtensions, discoverSlashCommands } = require('./slash-command-discovery');
|
|
51
55
|
const { FarmingUpdateService } = require('./update-service');
|
|
52
|
-
const { isRestartBlockingAgent } = require('./agent-activity');
|
|
53
56
|
const { inputPartsFromMessage } = require('./input-parts');
|
|
54
57
|
const { cleanupTerminalRuntime } = require('./terminal-runtime-cleanup');
|
|
55
58
|
const { QrShareTicketStore, SHARE_TICKET_TTL_MS } = require('./qr-share-tickets');
|
|
@@ -61,11 +64,13 @@ const { ReviewSessionStore } = require('./review-session-store');
|
|
|
61
64
|
const { ReviewSessionService } = require('./review-session-service');
|
|
62
65
|
const { createReviewSessionRouter } = require('./review-session-router');
|
|
63
66
|
const {
|
|
67
|
+
applyIndexHtmlAppearance,
|
|
64
68
|
normalizeBasePath,
|
|
65
69
|
routePath,
|
|
66
70
|
rewriteIndexHtmlForBasePath,
|
|
67
71
|
appendIndexHtmlAssetToken,
|
|
68
72
|
} = require('./index-html');
|
|
73
|
+
const { decodeAcpTranscriptMedia } = require('./acp-transcript');
|
|
69
74
|
const {
|
|
70
75
|
coalesceSessionStream,
|
|
71
76
|
deliverSessionStreamToClients,
|
|
@@ -129,6 +134,15 @@ async function requireAgentRecoveryForHttp(res) {
|
|
|
129
134
|
const themeManager = new ThemeManager({ configDir: configManager.farmingDir });
|
|
130
135
|
const workspaceFileService = new WorkspaceFileService();
|
|
131
136
|
const workspaceRootRegistry = new WorkspaceRootRegistry(agentManager);
|
|
137
|
+
const browserResourceManager = new BrowserResourceManager({
|
|
138
|
+
configDir: configManager.farmingDir,
|
|
139
|
+
isEnabled: () => configManager.getSettings().browserExtensionEnabled === true,
|
|
140
|
+
getBrowserSettings: () => configManager.getSettings(),
|
|
141
|
+
});
|
|
142
|
+
const browserRuntimeRecoveryPromise = browserResourceManager.init().catch(error => {
|
|
143
|
+
console.warn('Failed to recover Browser runtimes:', error?.message || error);
|
|
144
|
+
return null;
|
|
145
|
+
});
|
|
132
146
|
const updateService = new FarmingUpdateService({
|
|
133
147
|
rootDir: path.join(__dirname, '..'),
|
|
134
148
|
configDir: configManager.farmingDir,
|
|
@@ -246,6 +260,22 @@ function getAvailableAgentsForRequest() {
|
|
|
246
260
|
supported: true,
|
|
247
261
|
interactive: true,
|
|
248
262
|
},
|
|
263
|
+
{
|
|
264
|
+
name: 'opencode',
|
|
265
|
+
command: 'opencode',
|
|
266
|
+
description: 'OpenCode - AI coding assistant',
|
|
267
|
+
category: 'coding',
|
|
268
|
+
supported: true,
|
|
269
|
+
interactive: true,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: 'qoder',
|
|
273
|
+
command: 'qodercli',
|
|
274
|
+
description: 'Qoder - AI coding assistant',
|
|
275
|
+
category: 'coding',
|
|
276
|
+
supported: true,
|
|
277
|
+
interactive: true,
|
|
278
|
+
},
|
|
249
279
|
{
|
|
250
280
|
name: 'bash',
|
|
251
281
|
command: 'bash',
|
|
@@ -527,6 +557,7 @@ app.use(BASE_PATH || '/', express.static(staticAppDir, { index: false }));
|
|
|
527
557
|
app.use(routePath(BASE_PATH, '/api/files'), createWorkspaceFileRouter(agentManager, workspaceFileService, {
|
|
528
558
|
rootRegistry: workspaceRootRegistry,
|
|
529
559
|
}));
|
|
560
|
+
app.use(routePath(BASE_PATH, '/api/browsers'), createBrowserRouter(browserResourceManager, workspaceRootRegistry));
|
|
530
561
|
|
|
531
562
|
app.use(routePath(BASE_PATH, '/api/review-sessions'), createReviewSessionRouter(reviewSessionService));
|
|
532
563
|
app.use(routePath(BASE_PATH, '/api/reviews'), createReviewDiffRouter(reviewDiffService, reviewSessionService));
|
|
@@ -564,7 +595,10 @@ app.get([
|
|
|
564
595
|
}
|
|
565
596
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
566
597
|
res.setHeader('Cache-Control', 'no-cache');
|
|
567
|
-
const rewrittenHtml =
|
|
598
|
+
const rewrittenHtml = applyIndexHtmlAppearance(
|
|
599
|
+
rewriteIndexHtmlForBasePath(html, BASE_PATH),
|
|
600
|
+
configManager.getSettings().appearance
|
|
601
|
+
);
|
|
568
602
|
const requestUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
569
603
|
const requestToken = requestUrl.searchParams.has('token') ? tokenAuth.extractToken(req) : '';
|
|
570
604
|
const htmlWithAssetToken = authEnabled && requestToken && tokenAuth.verify(requestToken)
|
|
@@ -603,6 +637,40 @@ app.get(routePath(BASE_PATH, '/api/skills'), (_req, res) => {
|
|
|
603
637
|
res.json({ skills: getMainAgentSkillsCatalog() });
|
|
604
638
|
});
|
|
605
639
|
|
|
640
|
+
app.get(routePath(BASE_PATH, '/api/agent-extensions'), (_req, res) => {
|
|
641
|
+
const agents = getAvailableAgentsForRequest()
|
|
642
|
+
.filter(agent => agent.category === 'coding')
|
|
643
|
+
.map(agent => {
|
|
644
|
+
const provider = String(agent.name || agent.command || '').trim().toLowerCase();
|
|
645
|
+
const configuredHomes = configManager.getAgentHomes(provider);
|
|
646
|
+
const homes = configuredHomes.length > 0
|
|
647
|
+
? configuredHomes
|
|
648
|
+
: [{ id: 'default', path: '' }];
|
|
649
|
+
return {
|
|
650
|
+
id: provider,
|
|
651
|
+
name: agent.name,
|
|
652
|
+
description: agent.description || '',
|
|
653
|
+
discoverySupported: provider === 'codex' || provider === 'claude',
|
|
654
|
+
homes: homes.map(home => ({
|
|
655
|
+
id: home.id,
|
|
656
|
+
extensions: discoverAgentExtensions({
|
|
657
|
+
provider,
|
|
658
|
+
providerHomePath: home.path,
|
|
659
|
+
}).map(extension => ({
|
|
660
|
+
id: extension.command,
|
|
661
|
+
command: extension.command,
|
|
662
|
+
name: extension.label,
|
|
663
|
+
description: extension.description,
|
|
664
|
+
kind: extension.source === 'custom' ? 'command' : extension.source || 'command',
|
|
665
|
+
scope: extension.scope || '',
|
|
666
|
+
})),
|
|
667
|
+
})),
|
|
668
|
+
};
|
|
669
|
+
});
|
|
670
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
671
|
+
res.json({ agents });
|
|
672
|
+
});
|
|
673
|
+
|
|
606
674
|
app.get(routePath(BASE_PATH, '/api/slash-commands'), (req, res) => {
|
|
607
675
|
const provider = typeof req.query.provider === 'string' ? req.query.provider : '';
|
|
608
676
|
const workspace = typeof req.query.workspace === 'string' ? req.query.workspace : '';
|
|
@@ -805,26 +873,10 @@ app.post(routePath(BASE_PATH, '/api/codex/context-windows'), express.json(), asy
|
|
|
805
873
|
}
|
|
806
874
|
});
|
|
807
875
|
|
|
808
|
-
function blockingUpdateAgents() {
|
|
809
|
-
return agentManager.getState().agents
|
|
810
|
-
.filter(isRestartBlockingAgent)
|
|
811
|
-
.map(agent => ({
|
|
812
|
-
id: agent.id,
|
|
813
|
-
command: agent.command,
|
|
814
|
-
task: agent.task || agent.customTitle || agent.sessionTitle || '',
|
|
815
|
-
cwd: agent.cwd,
|
|
816
|
-
}));
|
|
817
|
-
}
|
|
818
|
-
|
|
819
876
|
app.get(routePath(BASE_PATH, '/api/update'), async (req, res) => {
|
|
820
877
|
try {
|
|
821
878
|
const update = await updateService.check({ force: req.query.force === '1' });
|
|
822
|
-
res.json({
|
|
823
|
-
update: {
|
|
824
|
-
...update,
|
|
825
|
-
blockingAgents: blockingUpdateAgents(),
|
|
826
|
-
},
|
|
827
|
-
});
|
|
879
|
+
res.json({ update });
|
|
828
880
|
} catch (error) {
|
|
829
881
|
res.status(502).json({ error: error.message || 'Failed to check for updates' });
|
|
830
882
|
}
|
|
@@ -835,31 +887,16 @@ app.post(routePath(BASE_PATH, '/api/update/install'), express.json(), async (req
|
|
|
835
887
|
const state = await updateService.startInstall({
|
|
836
888
|
assetName: req.body && typeof req.body.assetName === 'string' ? req.body.assetName : '',
|
|
837
889
|
});
|
|
838
|
-
res.status(202).json({
|
|
839
|
-
update: {
|
|
840
|
-
state,
|
|
841
|
-
blockingAgents: blockingUpdateAgents(),
|
|
842
|
-
},
|
|
843
|
-
});
|
|
890
|
+
res.status(202).json({ update: { state } });
|
|
844
891
|
} catch (error) {
|
|
845
892
|
res.status(500).json({ error: error.message || 'Failed to start update' });
|
|
846
893
|
}
|
|
847
894
|
});
|
|
848
895
|
|
|
849
896
|
app.post(routePath(BASE_PATH, '/api/update/restart'), express.json(), async (req, res) => {
|
|
850
|
-
const blockers = blockingUpdateAgents();
|
|
851
|
-
const force = req.body && req.body.force === true;
|
|
852
|
-
if (blockers.length > 0 && !force) {
|
|
853
|
-
res.status(409).json({
|
|
854
|
-
error: 'Cannot restart for update while non-recoverable project agents are running',
|
|
855
|
-
blockingAgents: blockers,
|
|
856
|
-
});
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
897
|
try {
|
|
861
898
|
const state = await updateService.applyPreparedUpdate();
|
|
862
|
-
res.status(202).json({ update: { state
|
|
899
|
+
res.status(202).json({ update: { state } });
|
|
863
900
|
} catch (error) {
|
|
864
901
|
res.status(500).json({ error: error.message || 'Failed to restart for update' });
|
|
865
902
|
}
|
|
@@ -1142,8 +1179,17 @@ app.get(routePath(BASE_PATH, '/api/agents/:agentId/acp-transcript'), async (req,
|
|
|
1142
1179
|
? Math.min(MAX_CODEX_TRANSCRIPT_TURNS, Math.max(20, requestedMaxTurns))
|
|
1143
1180
|
: DEFAULT_CODEX_TRANSCRIPT_MAX_TURNS;
|
|
1144
1181
|
const requestedRevision = Number.parseInt(String(req.query.sinceRevision || ''), 10);
|
|
1182
|
+
const externalMedia = req.query.media === 'external-v1';
|
|
1145
1183
|
res.json({ transcript: agentManager.getAcpTranscript(req.params.agentId, {
|
|
1146
1184
|
maxTurns,
|
|
1185
|
+
...(externalMedia
|
|
1186
|
+
? {
|
|
1187
|
+
mediaPathPrefix: routePath(
|
|
1188
|
+
BASE_PATH,
|
|
1189
|
+
`/api/agents/${encodeURIComponent(req.params.agentId)}/acp-media`
|
|
1190
|
+
),
|
|
1191
|
+
}
|
|
1192
|
+
: {}),
|
|
1147
1193
|
...(Number.isFinite(requestedRevision) && requestedRevision >= 0
|
|
1148
1194
|
? { sinceRevision: requestedRevision }
|
|
1149
1195
|
: {}),
|
|
@@ -1154,6 +1200,32 @@ app.get(routePath(BASE_PATH, '/api/agents/:agentId/acp-transcript'), async (req,
|
|
|
1154
1200
|
}
|
|
1155
1201
|
});
|
|
1156
1202
|
|
|
1203
|
+
app.get(routePath(BASE_PATH, '/api/agents/:agentId/acp-media/:entryId/:mediaId'), (req, res) => {
|
|
1204
|
+
try {
|
|
1205
|
+
const media = agentManager.getAcpTranscriptMedia(
|
|
1206
|
+
req.params.agentId,
|
|
1207
|
+
req.params.entryId,
|
|
1208
|
+
req.params.mediaId
|
|
1209
|
+
);
|
|
1210
|
+
const decoded = decodeAcpTranscriptMedia(media);
|
|
1211
|
+
if (!decoded) {
|
|
1212
|
+
res.status(415).json({ error: 'unsupported ACP transcript media' });
|
|
1213
|
+
return;
|
|
1214
|
+
}
|
|
1215
|
+
res.setHeader('Cache-Control', 'private, max-age=31536000, immutable');
|
|
1216
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
1217
|
+
res.type(decoded.mimeType).send(decoded.content);
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
const message = error && error.message ? error.message : 'Failed to read ACP transcript media';
|
|
1220
|
+
const status = message === 'Agent not found'
|
|
1221
|
+
|| message === 'ACP transcript entry not found'
|
|
1222
|
+
|| message === 'ACP transcript media not found'
|
|
1223
|
+
? 404
|
|
1224
|
+
: 409;
|
|
1225
|
+
res.status(status).json({ error: message });
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1157
1229
|
app.get(routePath(BASE_PATH, '/api/agents/:agentId/acp-tool-details/:toolCallId'), async (req, res) => {
|
|
1158
1230
|
try {
|
|
1159
1231
|
res.json(agentManager.getAcpToolDetail(req.params.agentId, req.params.toolCallId));
|
|
@@ -1554,7 +1626,13 @@ app.post(routePath(BASE_PATH, '/api/agents/:agentId/fork'), express.json(), asyn
|
|
|
1554
1626
|
res.status(400).json({ error: 'Unsupported Fork target runtime' });
|
|
1555
1627
|
return;
|
|
1556
1628
|
}
|
|
1629
|
+
const requestId = typeof req.body?.requestId === 'string' ? req.body.requestId.trim() : '';
|
|
1630
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/.test(requestId)) {
|
|
1631
|
+
res.status(400).json({ error: 'Fork requires a valid requestId' });
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1557
1634
|
const result = await agentManager.forkAgent(req.params.agentId, mode, {
|
|
1635
|
+
requestId,
|
|
1558
1636
|
...(targetRuntime ? { targetRuntime } : {}),
|
|
1559
1637
|
...(Number.isSafeInteger(req.body?.expectedRevision)
|
|
1560
1638
|
? { expectedRevision: req.body.expectedRevision }
|
|
@@ -1562,7 +1640,7 @@ app.post(routePath(BASE_PATH, '/api/agents/:agentId/fork'), express.json(), asyn
|
|
|
1562
1640
|
});
|
|
1563
1641
|
if (result.error) {
|
|
1564
1642
|
const status = result.error === 'Agent not found' ? 404 : 400;
|
|
1565
|
-
res.status(status).json(
|
|
1643
|
+
res.status(status).json(result);
|
|
1566
1644
|
return;
|
|
1567
1645
|
}
|
|
1568
1646
|
|
|
@@ -1571,32 +1649,12 @@ app.post(routePath(BASE_PATH, '/api/agents/:agentId/fork'), express.json(), asyn
|
|
|
1571
1649
|
result.projectWorkspaces = membership.projectWorkspaces;
|
|
1572
1650
|
result.pinnedProjectWorkspaces = membership.pinnedProjectWorkspaces;
|
|
1573
1651
|
} catch (error) {
|
|
1574
|
-
let rollbackError = '';
|
|
1575
|
-
try {
|
|
1576
|
-
if (mode === 'new-worktree') {
|
|
1577
|
-
const rollback = await agentManager.deleteForkWorktreeProject(result.workspace, { force: true });
|
|
1578
|
-
if (!rollback?.deleted || rollback.cleanupError) {
|
|
1579
|
-
rollbackError = rollback?.error || rollback?.cleanupError || 'Failed to delete the fork worktree';
|
|
1580
|
-
}
|
|
1581
|
-
} else {
|
|
1582
|
-
const rollback = await agentManager.archiveAgent(result.agentId, {
|
|
1583
|
-
reason: 'project-mount-failed',
|
|
1584
|
-
recordHistory: false,
|
|
1585
|
-
requireEngineExit: true,
|
|
1586
|
-
scheduleProviderArchive: false,
|
|
1587
|
-
});
|
|
1588
|
-
if (rollback?.error) rollbackError = rollback.error;
|
|
1589
|
-
}
|
|
1590
|
-
} catch (cleanupError) {
|
|
1591
|
-
rollbackError = cleanupError?.message || String(cleanupError);
|
|
1592
|
-
}
|
|
1593
1652
|
broadcastState();
|
|
1594
1653
|
const mountError = error.message || 'Failed to create Project';
|
|
1595
1654
|
res.status(500).json({
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
...(rollbackError ? { rollbackError } : {}),
|
|
1655
|
+
...result,
|
|
1656
|
+
error: `${mountError}. Retry the same Fork request to reconcile Project membership.`,
|
|
1657
|
+
retryable: true,
|
|
1600
1658
|
});
|
|
1601
1659
|
return;
|
|
1602
1660
|
}
|
|
@@ -1676,31 +1734,26 @@ app.patch(routePath(BASE_PATH, '/api/projects/name'), express.json(), (req, res)
|
|
|
1676
1734
|
});
|
|
1677
1735
|
|
|
1678
1736
|
app.post(routePath(BASE_PATH, '/api/projects/create-worktree'), express.json(), async (req, res) => {
|
|
1679
|
-
|
|
1737
|
+
const requestId = typeof req.body?.requestId === 'string' ? req.body.requestId.trim() : '';
|
|
1738
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/.test(requestId)) {
|
|
1739
|
+
res.status(400).json({ error: 'Project worktree creation requires a valid requestId' });
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1680
1742
|
try {
|
|
1681
1743
|
const root = resolveProjectActionRoot(req.body?.rootId);
|
|
1682
|
-
created = await agentManager.createPermanentWorktree(root.canonicalPath);
|
|
1683
|
-
const membership = configManager.mountProjectWorkspace(created.workspace);
|
|
1744
|
+
const created = await agentManager.createPermanentWorktree(root.canonicalPath, { requestId });
|
|
1684
1745
|
broadcastState();
|
|
1685
1746
|
res.status(201).json({
|
|
1686
|
-
|
|
1687
|
-
branch: created.branch,
|
|
1688
|
-
projectWorkspaces: membership.projectWorkspaces,
|
|
1689
|
-
pinnedProjectWorkspaces: membership.pinnedProjectWorkspaces,
|
|
1747
|
+
...created,
|
|
1690
1748
|
});
|
|
1691
1749
|
} catch (error) {
|
|
1692
|
-
let rollbackError = '';
|
|
1693
|
-
if (created) {
|
|
1694
|
-
const rollback = await agentManager.rollbackPermanentWorktree(created);
|
|
1695
|
-
if (rollback?.rolledBack === false) rollbackError = rollback.error || 'Unknown rollback failure';
|
|
1696
|
-
}
|
|
1697
1750
|
const status = error instanceof WorkspaceFileError ? error.statusCode : 400;
|
|
1698
|
-
const
|
|
1751
|
+
const operation = configManager.getProjectOperation?.(requestId);
|
|
1699
1752
|
res.status(status).json({
|
|
1700
|
-
error:
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
...(
|
|
1753
|
+
error: error.message || 'Failed to create permanent worktree',
|
|
1754
|
+
requestId,
|
|
1755
|
+
...(operation?.state === 'pending' ? { retryable: true } : {}),
|
|
1756
|
+
...(['unknown', 'blocked'].includes(operation?.state) ? { uncertain: true } : {}),
|
|
1704
1757
|
});
|
|
1705
1758
|
}
|
|
1706
1759
|
});
|
|
@@ -1709,45 +1762,26 @@ app.post(routePath(BASE_PATH, '/api/projects/delete-worktree'), express.json(),
|
|
|
1709
1762
|
if (!await requireAgentRecoveryForHttp(res)) return;
|
|
1710
1763
|
const body = req.body || {};
|
|
1711
1764
|
const workspace = typeof body.workspace === 'string' ? body.workspace : '';
|
|
1712
|
-
const
|
|
1713
|
-
if (
|
|
1714
|
-
|
|
1715
|
-
res.status(409).json({
|
|
1716
|
-
...inspected,
|
|
1717
|
-
error: 'Worktree has uncommitted or untracked files',
|
|
1718
|
-
});
|
|
1719
|
-
return;
|
|
1720
|
-
}
|
|
1721
|
-
const status = inspected.error === 'Workspace not found' || inspected.error === 'Workspace is required' ? 404 : 400;
|
|
1722
|
-
res.status(status).json(inspected);
|
|
1765
|
+
const requestId = typeof body.requestId === 'string' ? body.requestId.trim() : '';
|
|
1766
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/.test(requestId)) {
|
|
1767
|
+
res.status(400).json({ error: 'Project worktree deletion requires a valid requestId' });
|
|
1723
1768
|
return;
|
|
1724
1769
|
}
|
|
1725
1770
|
|
|
1726
|
-
const result = await agentManager.deleteForkWorktreeProject(
|
|
1771
|
+
const result = await agentManager.deleteForkWorktreeProject(workspace, {
|
|
1772
|
+
force: body.force === true,
|
|
1773
|
+
requestId,
|
|
1774
|
+
});
|
|
1727
1775
|
if (result.error) {
|
|
1728
1776
|
broadcastState();
|
|
1729
|
-
const status = result.requiresForce
|
|
1777
|
+
const status = result.requiresForce
|
|
1778
|
+
? 409
|
|
1779
|
+
: (result.error === 'Workspace not found' || result.error === 'Workspace is required' ? 404 : 400);
|
|
1730
1780
|
res.status(status).json(result);
|
|
1731
1781
|
return;
|
|
1732
1782
|
}
|
|
1733
|
-
|
|
1734
|
-
let membership;
|
|
1735
|
-
try {
|
|
1736
|
-
membership = configManager.removeProjectWorkspace(inspected.workspace);
|
|
1737
|
-
} catch (error) {
|
|
1738
|
-
broadcastState();
|
|
1739
|
-
res.status(500).json({
|
|
1740
|
-
...result,
|
|
1741
|
-
error: `Worktree was deleted, but Project membership cleanup failed: ${error.message || error}`,
|
|
1742
|
-
});
|
|
1743
|
-
return;
|
|
1744
|
-
}
|
|
1745
1783
|
broadcastState();
|
|
1746
|
-
res.json(
|
|
1747
|
-
...result,
|
|
1748
|
-
projectWorkspaces: membership.projectWorkspaces,
|
|
1749
|
-
pinnedProjectWorkspaces: membership.pinnedProjectWorkspaces,
|
|
1750
|
-
});
|
|
1784
|
+
res.json(result);
|
|
1751
1785
|
});
|
|
1752
1786
|
|
|
1753
1787
|
app.post(routePath(BASE_PATH, '/api/codex/sessions/:sessionId/resume'), express.json(), async (req, res) => {
|
|
@@ -2232,12 +2266,64 @@ async function autoResumeMainPageAgentSessions() {
|
|
|
2232
2266
|
}
|
|
2233
2267
|
}
|
|
2234
2268
|
|
|
2235
|
-
app.post(routePath(BASE_PATH, '/api/settings'), express.json(), (req, res) => {
|
|
2269
|
+
app.post(routePath(BASE_PATH, '/api/settings'), express.json(), async (req, res) => {
|
|
2236
2270
|
const settingsPatch = { ...(req.body || {}) };
|
|
2237
2271
|
delete settingsPatch.mainPageSessionKeys;
|
|
2238
2272
|
delete settingsPatch.projectWorkspaces;
|
|
2239
2273
|
delete settingsPatch.pinnedProjectWorkspaces;
|
|
2274
|
+
const browserConfigurationKeys = [
|
|
2275
|
+
'browserSource',
|
|
2276
|
+
'browserExecutablePath',
|
|
2277
|
+
'browserExternalCdpUrl',
|
|
2278
|
+
];
|
|
2279
|
+
const changesBrowserExtension = Object.prototype.hasOwnProperty.call(settingsPatch, 'browserExtensionEnabled');
|
|
2280
|
+
const changesBrowserConfiguration = browserConfigurationKeys.some(key =>
|
|
2281
|
+
Object.prototype.hasOwnProperty.call(settingsPatch, key)
|
|
2282
|
+
);
|
|
2283
|
+
const currentSettings = configManager.getSettings();
|
|
2284
|
+
const browserExtensionEnabled = settingsPatch.browserExtensionEnabled === true;
|
|
2285
|
+
const desiredBrowserEnabled = changesBrowserExtension
|
|
2286
|
+
? browserExtensionEnabled
|
|
2287
|
+
: currentSettings.browserExtensionEnabled === true;
|
|
2288
|
+
if ((changesBrowserExtension && browserExtensionEnabled) || changesBrowserConfiguration) {
|
|
2289
|
+
const desiredSelection = browserResourceManager.browserSelection({
|
|
2290
|
+
...currentSettings,
|
|
2291
|
+
...settingsPatch,
|
|
2292
|
+
});
|
|
2293
|
+
const probe = await browserResourceManager.probeCapability(desiredSelection);
|
|
2294
|
+
if (
|
|
2295
|
+
(changesBrowserConfiguration || desiredBrowserEnabled)
|
|
2296
|
+
&& (!probe.runtimeCapability || probe.runtimeCapability.error)
|
|
2297
|
+
) {
|
|
2298
|
+
res.status(400).json({
|
|
2299
|
+
error: probe.runtimeCapability?.error
|
|
2300
|
+
|| 'Install a Chromium-based browser or configure a loopback external CDP endpoint',
|
|
2301
|
+
code: 'BROWSER_EXECUTABLE_NOT_FOUND',
|
|
2302
|
+
});
|
|
2303
|
+
return;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
if (
|
|
2307
|
+
currentSettings.browserExtensionEnabled === true
|
|
2308
|
+
&& (
|
|
2309
|
+
(changesBrowserExtension && !browserExtensionEnabled)
|
|
2310
|
+
|| changesBrowserConfiguration
|
|
2311
|
+
)
|
|
2312
|
+
) {
|
|
2313
|
+
try {
|
|
2314
|
+
await browserResourceManager.stopAll();
|
|
2315
|
+
} catch (error) {
|
|
2316
|
+
res.status(Number(error?.status) || 500).json({
|
|
2317
|
+
error: error?.message || 'Browser extension could not be disabled',
|
|
2318
|
+
code: error?.code || 'BROWSER_DISABLE_FAILED',
|
|
2319
|
+
});
|
|
2320
|
+
return;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2240
2323
|
configManager.updateSettings(settingsPatch);
|
|
2324
|
+
if (changesBrowserExtension || changesBrowserConfiguration) {
|
|
2325
|
+
await browserResourceManager.refreshCapability();
|
|
2326
|
+
}
|
|
2241
2327
|
agentSessionsCache.invalidate();
|
|
2242
2328
|
res.json({
|
|
2243
2329
|
success: true,
|
|
@@ -2299,6 +2385,20 @@ app.get(routePath(BASE_PATH, '/api/themes/:themeId'), (req, res) => {
|
|
|
2299
2385
|
|
|
2300
2386
|
wss.on('connection', (ws, req) => {
|
|
2301
2387
|
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
|
2388
|
+
const viewerPrefix = routePath(BASE_PATH, '/api/browsers/');
|
|
2389
|
+
if (url.pathname.startsWith(viewerPrefix) && url.pathname.endsWith('/viewer')) {
|
|
2390
|
+
if (authEnabled && !tokenAuth.verifyWebSocket(req)) {
|
|
2391
|
+
ws.close(4001, 'Authentication required');
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
const encodedId = url.pathname.slice(viewerPrefix.length, -'/viewer'.length);
|
|
2395
|
+
try {
|
|
2396
|
+
browserResourceManager.attachViewer(decodeURIComponent(encodedId), ws);
|
|
2397
|
+
} catch (error) {
|
|
2398
|
+
ws.close(4004, error?.message || 'Browser resource not found');
|
|
2399
|
+
}
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2302
2402
|
if (url.pathname !== WS_PATH) {
|
|
2303
2403
|
ws.close(1008, 'Invalid path');
|
|
2304
2404
|
return;
|
|
@@ -2442,9 +2542,9 @@ async function sendInputMessage(ws, data) {
|
|
|
2442
2542
|
|
|
2443
2543
|
async function sendComposerInputMessage(ws, data) {
|
|
2444
2544
|
const targetAgentId = resolveInputTargetAgentId(ws, data);
|
|
2445
|
-
const requestId = typeof data.requestId === 'string' ? data.requestId : '';
|
|
2545
|
+
const requestId = typeof data.requestId === 'string' ? data.requestId.trim() : '';
|
|
2446
2546
|
const responseAgentId = targetAgentId || (typeof data.agentId === 'string' ? data.agentId : '');
|
|
2447
|
-
const respond = (accepted, message = '') => {
|
|
2547
|
+
const respond = (accepted, message = '', uncertain = false) => {
|
|
2448
2548
|
if (!requestId || !responseAgentId) return;
|
|
2449
2549
|
ws.send(JSON.stringify({
|
|
2450
2550
|
type: 'composer-input-result',
|
|
@@ -2452,9 +2552,22 @@ async function sendComposerInputMessage(ws, data) {
|
|
|
2452
2552
|
agentId: responseAgentId,
|
|
2453
2553
|
accepted,
|
|
2454
2554
|
...(message ? { message } : {}),
|
|
2555
|
+
...(uncertain ? { uncertain: true } : {}),
|
|
2455
2556
|
}));
|
|
2456
2557
|
};
|
|
2457
2558
|
const message = typeof data.message === 'string' ? data.message : '';
|
|
2559
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/.test(requestId)) {
|
|
2560
|
+
if (responseAgentId) {
|
|
2561
|
+
ws.send(JSON.stringify({
|
|
2562
|
+
type: 'composer-input-result',
|
|
2563
|
+
requestId: requestId || 'invalid-request',
|
|
2564
|
+
agentId: responseAgentId,
|
|
2565
|
+
accepted: false,
|
|
2566
|
+
message: 'Structured Composer input requires a valid requestId',
|
|
2567
|
+
}));
|
|
2568
|
+
}
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2458
2571
|
const content = [];
|
|
2459
2572
|
if (message.trim()) content.push({ type: 'text', text: message });
|
|
2460
2573
|
const attachmentsRoot = path.resolve(imageAttachmentsDir());
|
|
@@ -2496,11 +2609,11 @@ async function sendComposerInputMessage(ws, data) {
|
|
|
2496
2609
|
return;
|
|
2497
2610
|
}
|
|
2498
2611
|
try {
|
|
2499
|
-
await agentManager.sendComposerMessage(targetAgentId, content);
|
|
2612
|
+
await agentManager.sendComposerMessage(targetAgentId, content, { requestId });
|
|
2500
2613
|
respond(true);
|
|
2501
2614
|
} catch (error) {
|
|
2502
2615
|
const message = error && error.message ? error.message : 'Failed to send Composer message';
|
|
2503
|
-
if (requestId) respond(false, message);
|
|
2616
|
+
if (requestId) respond(false, message, error?.uncertain === true);
|
|
2504
2617
|
else ws.send(JSON.stringify({ type: 'error', message }));
|
|
2505
2618
|
}
|
|
2506
2619
|
}
|
|
@@ -2834,7 +2947,6 @@ const pendingAgentUpdates = new Map();
|
|
|
2834
2947
|
const pendingSessionStreams = new Map();
|
|
2835
2948
|
let sessionStreamBroadcastTimer = null;
|
|
2836
2949
|
let lastSessionStreamBroadcastAt = 0;
|
|
2837
|
-
let shutdownStarted = false;
|
|
2838
2950
|
|
|
2839
2951
|
function broadcastState() {
|
|
2840
2952
|
lastStateBroadcastAt = Date.now();
|
|
@@ -3049,105 +3161,6 @@ function scheduleSessionStreamBroadcast(stream) {
|
|
|
3049
3161
|
}
|
|
3050
3162
|
}
|
|
3051
3163
|
|
|
3052
|
-
function clearBroadcastTimers() {
|
|
3053
|
-
if (stateBroadcastTimer) {
|
|
3054
|
-
clearTimeout(stateBroadcastTimer);
|
|
3055
|
-
stateBroadcastTimer = null;
|
|
3056
|
-
}
|
|
3057
|
-
for (const entry of pendingPreviewBroadcasts.values()) {
|
|
3058
|
-
if (entry && entry.timer) {
|
|
3059
|
-
clearTimeout(entry.timer);
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
pendingPreviewBroadcasts.clear();
|
|
3063
|
-
for (const entry of pendingAgentActivityBroadcasts.values()) {
|
|
3064
|
-
if (entry && entry.timer) clearTimeout(entry.timer);
|
|
3065
|
-
}
|
|
3066
|
-
pendingAgentActivityBroadcasts.clear();
|
|
3067
|
-
for (const entry of pendingAgentUpdates.values()) {
|
|
3068
|
-
if (entry && entry.timer) clearTimeout(entry.timer);
|
|
3069
|
-
}
|
|
3070
|
-
pendingAgentUpdates.clear();
|
|
3071
|
-
if (sessionStreamBroadcastTimer) {
|
|
3072
|
-
clearTimeout(sessionStreamBroadcastTimer);
|
|
3073
|
-
sessionStreamBroadcastTimer = null;
|
|
3074
|
-
}
|
|
3075
|
-
pendingSessionStreams.clear();
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
function closeHttpServer() {
|
|
3079
|
-
return new Promise(resolve => {
|
|
3080
|
-
if (!server.listening) {
|
|
3081
|
-
resolve();
|
|
3082
|
-
return;
|
|
3083
|
-
}
|
|
3084
|
-
server.close(error => {
|
|
3085
|
-
if (error && error.code !== 'ERR_SERVER_NOT_RUNNING') {
|
|
3086
|
-
console.warn('Failed to close HTTP server:', error.message || error);
|
|
3087
|
-
}
|
|
3088
|
-
resolve();
|
|
3089
|
-
});
|
|
3090
|
-
});
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
|
-
function closeWebSocketServer() {
|
|
3094
|
-
for (const client of wss.clients) {
|
|
3095
|
-
clearWorkspaceFileWatch(client);
|
|
3096
|
-
try {
|
|
3097
|
-
client.close(1001, 'Farming server shutting down');
|
|
3098
|
-
} catch {
|
|
3099
|
-
// ignore shutdown races
|
|
3100
|
-
}
|
|
3101
|
-
}
|
|
3102
|
-
return new Promise(resolve => {
|
|
3103
|
-
wss.close(() => resolve());
|
|
3104
|
-
setTimeout(resolve, 250).unref?.();
|
|
3105
|
-
});
|
|
3106
|
-
}
|
|
3107
|
-
|
|
3108
|
-
async function shutdownServer(options = {}) {
|
|
3109
|
-
if (shutdownStarted) return;
|
|
3110
|
-
shutdownStarted = true;
|
|
3111
|
-
const preserveTerminalHost = options.preserveTerminalHost !== undefined
|
|
3112
|
-
? options.preserveTerminalHost === true
|
|
3113
|
-
: process.env.FARMING_NATIVE_PTY_HOST_PERSIST !== '0';
|
|
3114
|
-
|
|
3115
|
-
try {
|
|
3116
|
-
await agentManager.dispose({ preserveTerminalHost });
|
|
3117
|
-
} catch (error) {
|
|
3118
|
-
const teardownFrozen = agentManager.disposing === true;
|
|
3119
|
-
shutdownStarted = false;
|
|
3120
|
-
console.error('Farming shutdown blocked because Agent runtime cleanup could not be verified:', error);
|
|
3121
|
-
if (teardownFrozen) {
|
|
3122
|
-
clearBroadcastTimers();
|
|
3123
|
-
tokenAuth.cleanup();
|
|
3124
|
-
await Promise.allSettled([
|
|
3125
|
-
closeWebSocketServer(),
|
|
3126
|
-
workspaceFileService.dispose(),
|
|
3127
|
-
closeHttpServer(),
|
|
3128
|
-
]);
|
|
3129
|
-
}
|
|
3130
|
-
if (options.exit === true) process.exitCode = 1;
|
|
3131
|
-
return {
|
|
3132
|
-
error: error?.message || 'Agent runtime cleanup could not be verified',
|
|
3133
|
-
...(teardownFrozen ? { frozen: true } : {}),
|
|
3134
|
-
};
|
|
3135
|
-
}
|
|
3136
|
-
|
|
3137
|
-
clearBroadcastTimers();
|
|
3138
|
-
tokenAuth.cleanup();
|
|
3139
|
-
await Promise.allSettled([
|
|
3140
|
-
closeWebSocketServer(),
|
|
3141
|
-
workspaceFileService.dispose(),
|
|
3142
|
-
closeHttpServer(),
|
|
3143
|
-
]);
|
|
3144
|
-
|
|
3145
|
-
if (options.exit === true) {
|
|
3146
|
-
process.exit(options.exitCode || 0);
|
|
3147
|
-
}
|
|
3148
|
-
return { stopped: true };
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
3164
|
agentManager.onSessionStream((stream) => {
|
|
3152
3165
|
scheduleSessionStreamBroadcast(stream);
|
|
3153
3166
|
});
|
|
@@ -3197,8 +3210,10 @@ function startServer() {
|
|
|
3197
3210
|
if (serverStarted) return server;
|
|
3198
3211
|
serverStarted = true;
|
|
3199
3212
|
|
|
3200
|
-
void
|
|
3201
|
-
|
|
3213
|
+
void Promise.all([
|
|
3214
|
+
runTerminalRuntimeStartupCleanup(),
|
|
3215
|
+
browserRuntimeRecoveryPromise,
|
|
3216
|
+
]).finally(() => {
|
|
3202
3217
|
server.listen(PORT, () => {
|
|
3203
3218
|
const token = tokenAuth.getToken();
|
|
3204
3219
|
const localIPs = getLocalIPs();
|
|
@@ -3228,13 +3243,6 @@ function startServer() {
|
|
|
3228
3243
|
});
|
|
3229
3244
|
});
|
|
3230
3245
|
|
|
3231
|
-
process.on('SIGINT', () => {
|
|
3232
|
-
void shutdownServer({ exit: true });
|
|
3233
|
-
});
|
|
3234
|
-
process.on('SIGTERM', () => {
|
|
3235
|
-
void shutdownServer({ exit: true });
|
|
3236
|
-
});
|
|
3237
|
-
|
|
3238
3246
|
return server;
|
|
3239
3247
|
}
|
|
3240
3248
|
|
|
@@ -3247,6 +3255,7 @@ module.exports = {
|
|
|
3247
3255
|
server,
|
|
3248
3256
|
wss,
|
|
3249
3257
|
agentManager,
|
|
3258
|
+
browserResourceManager,
|
|
3250
3259
|
workspaceFileService,
|
|
3251
3260
|
handleMessage,
|
|
3252
3261
|
resolveCliBinDir,
|
|
@@ -3254,6 +3263,5 @@ module.exports = {
|
|
|
3254
3263
|
rewriteIndexHtmlForBasePath,
|
|
3255
3264
|
appendIndexHtmlAssetToken,
|
|
3256
3265
|
startServer,
|
|
3257
|
-
shutdownServer,
|
|
3258
3266
|
runTerminalRuntimeStartupCleanup,
|
|
3259
3267
|
};
|