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.
Files changed (284) hide show
  1. package/README.md +15 -30
  2. package/README.zh_cn.md +15 -30
  3. package/THIRD_PARTY_NOTICES.md +28 -6
  4. package/backend/acp/packaged-claude-acp.js +13 -0
  5. package/backend/acp/packaged-codex-acp.js +2 -2
  6. package/backend/acp-runtime.js +52 -3
  7. package/backend/acp-transcript.js +174 -9
  8. package/backend/agent-manager.js +1081 -96
  9. package/backend/atomic-json-store.js +37 -0
  10. package/backend/cli-agents.js +9 -7
  11. package/backend/config-manager.js +211 -58
  12. package/backend/control-api.js +2 -1
  13. package/backend/data/LICENSE.agent-browser +201 -0
  14. package/backend/data/runtime-dependency-manifest.json +199 -0
  15. package/backend/data/runtime-dependency-sources.json +5 -0
  16. package/backend/executable-discovery.js +7 -0
  17. package/backend/farming-agent-bootstrap.js +49 -0
  18. package/backend/farming-agent-bootstrap.zh_cn.md +7 -0
  19. package/backend/farming-app-cli.js +168 -65
  20. package/backend/farming-cli.js +57 -0
  21. package/backend/farming-session-store.js +92 -89
  22. package/backend/index-html.js +13 -0
  23. package/backend/main-agent-skills.js +13 -0
  24. package/backend/npm-update-helper.js +43 -6
  25. package/backend/preview-session-manager.js +68 -0
  26. package/backend/provider-adapters.js +54 -5
  27. package/backend/review-session-store.js +32 -25
  28. package/backend/review-state-store.js +13 -14
  29. package/backend/run-history-store.js +13 -16
  30. package/backend/runtime-dependency-manager.js +759 -0
  31. package/backend/runtime-executable-invocation.js +20 -0
  32. package/backend/server-process-identity.js +47 -0
  33. package/backend/server.js +231 -223
  34. package/backend/slash-command-discovery.js +138 -1
  35. package/backend/storage-layout.js +35 -0
  36. package/backend/update-service.js +27 -1
  37. package/backend/workspace-file-router.js +141 -0
  38. package/backend/workspace-file-service.js +19 -0
  39. package/backend/workspace-root-registry.js +6 -4
  40. package/config/farming.install.env.example +4 -0
  41. package/dist/acp/LICENSE.claude-agent-acp +191 -0
  42. package/dist/acp/LICENSE.claude-agent-sdk +1 -0
  43. package/dist/acp/claude-agent-acp-0.59.0.mjs +41606 -0
  44. package/dist/assets/App-BKNUILuL.css +1 -0
  45. package/dist/assets/App-Ex2oVVXO.js +693 -0
  46. package/dist/assets/{FileEditorMarkdownPreview-Da5jguUs.js → FileEditorMarkdownPreview-Cs9cLv8a.js} +5 -5
  47. package/dist/assets/FileEditorPane-CNtuGjQ3.js +2 -0
  48. package/dist/assets/IconGlyphs-BClgag6E.js +1 -0
  49. package/dist/assets/ProjectFilesSection-Cf2BEcAN.js +12 -0
  50. package/dist/assets/ReviewPage-BRuxFaH5.js +3 -0
  51. package/dist/assets/{abnfDiagram-VRR7QNED-nnglrXFi.js → abnfDiagram-VRR7QNED-BY1qwq8A.js} +1 -1
  52. package/dist/assets/api-w2x5QfxB.js +1 -0
  53. package/dist/assets/{arc-CWoVlxJw.js → arc-DgZRAlwj.js} +1 -1
  54. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BEXMaFfY.js → architectureDiagram-ZJ3FMSHR-jFn144od.js} +1 -1
  55. package/dist/assets/{blockDiagram-677ZJIJ3-1mCezSBk.js → blockDiagram-677ZJIJ3-BmRjlV0H.js} +1 -1
  56. package/dist/assets/{c4Diagram-LMCZKHZV-DhTiI6Uv.js → c4Diagram-LMCZKHZV-DUKul6tT.js} +1 -1
  57. package/dist/assets/{workspace-editor-monaco-DLlws5Ig.js → channel-CI9j3QFD.js} +1 -1
  58. package/dist/assets/chunk-2Q5K7J3B-87UnWaoh.js +1 -0
  59. package/dist/assets/{chunk-32BRIVSS-CCdsKd73.js → chunk-32BRIVSS-rGVu23o5.js} +1 -1
  60. package/dist/assets/{chunk-52WLFC77-Gncjk4LF.js → chunk-52WLFC77-CvFDKgbB.js} +1 -1
  61. package/dist/assets/{chunk-5VM5RSS4-Dlw7AXWp.js → chunk-5VM5RSS4-CFgnRW1C.js} +1 -1
  62. package/dist/assets/{chunk-7BUUIJ7U-ZBRhjLOh.js → chunk-7BUUIJ7U-Cp7bqW6s.js} +1 -1
  63. package/dist/assets/{chunk-C7G6YPKG-CiDVUP82.js → chunk-C7G6YPKG-BIS1gjZO.js} +1 -1
  64. package/dist/assets/{chunk-EX3LRPZG-z6gXP2_N.js → chunk-EX3LRPZG-BKG-cR9j.js} +1 -1
  65. package/dist/assets/{chunk-FWX5IMBZ-DMZBccQa.js → chunk-FWX5IMBZ-B8PsTr8a.js} +2 -2
  66. package/dist/assets/{chunk-HOUHSVGY-D8tIeoPk.js → chunk-HOUHSVGY-BHFOAiXT.js} +1 -1
  67. package/dist/assets/{chunk-ICXQ74PX-b6oQ2Q17.js → chunk-ICXQ74PX-CvLE1J9X.js} +1 -1
  68. package/dist/assets/{chunk-JWPE2WC7-DSyBRaRG.js → chunk-JWPE2WC7-D8misks7.js} +1 -1
  69. package/dist/assets/{chunk-MOJQB5TN-DAVH_p2a.js → chunk-MOJQB5TN-D-oNgSNH.js} +1 -1
  70. package/dist/assets/{chunk-OGEWGWER-DxoiMCI-.js → chunk-OGEWGWER-C2N-la9e.js} +1 -1
  71. package/dist/assets/{chunk-PUDLZKDR-NsOWh7HM.js → chunk-PUDLZKDR-BwI3Jz8q.js} +1 -1
  72. package/dist/assets/{chunk-Q4XR5HBZ-Di4H7L21.js → chunk-Q4XR5HBZ-BuqGfsXA.js} +1 -1
  73. package/dist/assets/{chunk-RYQCIY6F-CZA9pvFX.js → chunk-RYQCIY6F-Bq4XZcGt.js} +1 -1
  74. package/dist/assets/{chunk-V7JOEXUC-DttpZg9Y.js → chunk-V7JOEXUC-QZLzdTTJ.js} +1 -1
  75. package/dist/assets/chunk-VAUOI2AC-Cb0-kQ9g.js +1 -0
  76. package/dist/assets/{chunk-VR4S4FIN-D-Fmtd4Z.js → chunk-VR4S4FIN-DcZTpML_.js} +1 -1
  77. package/dist/assets/{chunk-WYO6CB5R-CUxMtfK2.js → chunk-WYO6CB5R-DCU8wuny.js} +2 -2
  78. package/dist/assets/chunk-XXDRQBXY-BZAC1Krv.js +1 -0
  79. package/dist/assets/{chunk-ZGVPDNZ5-CoV2QEl0.js → chunk-ZGVPDNZ5-B7r85QbY.js} +1 -1
  80. package/dist/assets/{chunk-ZIRB5QZD-DncwlQQc.js → chunk-ZIRB5QZD-BgEnDWhQ.js} +1 -1
  81. package/dist/assets/classDiagram-OUVF2IWQ-DcvGOuvi.js +1 -0
  82. package/dist/assets/classDiagram-v2-EOCWNBFH-DDYUSofQ.js +1 -0
  83. package/dist/assets/code-dark-BEZTQwYZ.css +1 -0
  84. package/dist/assets/{cose-bilkent-JH36ORCC-BNIAz0nN.js → cose-bilkent-JH36ORCC-CThXS2qG.js} +1 -1
  85. package/dist/assets/{cssMode-DDWxTPm_.js → cssMode-Bdg5RpcJ.js} +1 -1
  86. package/dist/assets/{cynefinDiagram-TSTJHNR4-0xPI92Lg.js → cynefinDiagram-TSTJHNR4-DxBgzw25.js} +1 -1
  87. package/dist/assets/{dagre-DXlcHgr3.js → dagre-BM-GvGUZ.js} +1 -1
  88. package/dist/assets/{dagre-VKFMJZFB-DfuMG4gU.js → dagre-VKFMJZFB-B7UCRmxR.js} +1 -1
  89. package/dist/assets/{diagram-FQU43EPY-CT00bLZ6.js → diagram-FQU43EPY-CeL2DwKx.js} +1 -1
  90. package/dist/assets/{diagram-G47NLZAW-Xbh7zXUx.js → diagram-G47NLZAW-BEirglCC.js} +1 -1
  91. package/dist/assets/{diagram-NH7WQ7WH-SRDVPfn7.js → diagram-NH7WQ7WH-C6wcI-Rl.js} +1 -1
  92. package/dist/assets/{diagram-OA4YK3LP-CQRli4Np.js → diagram-OA4YK3LP-DpM-XXtJ.js} +1 -1
  93. package/dist/assets/{diagram-WEI45ONY-DMf17WQi.js → diagram-WEI45ONY-BQyaut9k.js} +1 -1
  94. package/dist/assets/{ebnfDiagram-CCIWWBDH-DhpFkgJ5.js → ebnfDiagram-CCIWWBDH-UmAXsUhr.js} +1 -1
  95. package/dist/assets/{erDiagram-Q63AITRT-1IwxWmJo.js → erDiagram-Q63AITRT-CdYLEOch.js} +1 -1
  96. package/dist/assets/file-icons-BOlcJ0ZF.js +1 -0
  97. package/dist/assets/flowDiagram-23GEKE2U-CO-z9-tq.js +1 -0
  98. package/dist/assets/{freemarker2-QBsTERH-.js → freemarker2-DfMDJfnE.js} +1 -1
  99. package/dist/assets/{ganttDiagram-NO4QXBWP-BNCdfyr1.js → ganttDiagram-NO4QXBWP-DgSUNdUB.js} +1 -1
  100. package/dist/assets/{gitGraphDiagram-IHSO6WYX-pbyWZuiX.js → gitGraphDiagram-IHSO6WYX-DjwjtRtF.js} +1 -1
  101. package/dist/assets/{handlebars-CuoxPo5A.js → handlebars-BEdPtzMT.js} +1 -1
  102. package/dist/assets/{html-gX5PEu0Q.js → html-B1fPb1QD.js} +1 -1
  103. package/dist/assets/html2canvas-DsczWxBa.js +5 -0
  104. package/dist/assets/{htmlMode-DIWUbUxd.js → htmlMode-CY2ZjHxH.js} +1 -1
  105. package/dist/assets/{index-CtwKYTiC.js → index-CQNaZc3l.js} +2 -2
  106. package/dist/assets/{infoDiagram-FWYZ7A6U-BcTOgDbe.js → infoDiagram-FWYZ7A6U-Bi9076l2.js} +1 -1
  107. package/dist/assets/{ishikawaDiagram-FXEZZL3T-DCdvGKrn.js → ishikawaDiagram-FXEZZL3T-DaMaNZN7.js} +1 -1
  108. package/dist/assets/{javascript-BYKGIUGs.js → javascript-BV2OsWA0.js} +1 -1
  109. package/dist/assets/{journeyDiagram-5HDEW3XC-BZ-1q_IT.js → journeyDiagram-5HDEW3XC-CfDy69EZ.js} +1 -1
  110. package/dist/assets/{jsonMode-AFmQQP6u.js → jsonMode-zd07aeuX.js} +1 -1
  111. package/dist/assets/{kanban-definition-HUTT4EX6-BIgejCjj.js → kanban-definition-HUTT4EX6-XFvZ722B.js} +1 -1
  112. package/dist/assets/{line-CfptFMmX.js → line-BXK9PKLn.js} +1 -1
  113. package/dist/assets/{linear-Dld3_ERu.js → linear-CULvmq2Z.js} +1 -1
  114. package/dist/assets/{liquid-DBIl-f0z.js → liquid-BParI24X.js} +1 -1
  115. package/dist/assets/{lspLanguageFeatures-DK6tqicV.js → lspLanguageFeatures-BzredkoV.js} +1 -1
  116. package/dist/assets/main-DK9Sy7-c.css +1 -0
  117. package/dist/assets/{map-LZWWoeWa.js → map-C9Qw9eWh.js} +1 -1
  118. package/dist/assets/{mdx-DS-bVtZN.js → mdx-vGzUJN98.js} +1 -1
  119. package/dist/assets/{mermaid.core-gSpX30pV.js → mermaid.core-CzazaIKr.js} +3 -3
  120. package/dist/assets/{mindmap-definition-LN4V7U3C-BKBaI4LW.js → mindmap-definition-LN4V7U3C-Uoff7hZ3.js} +1 -1
  121. package/dist/assets/{monaco.contribution-BXz-lfFB.js → monaco.contribution-BmaeBQEc.js} +2 -2
  122. package/dist/assets/{ordinal-BLrOss5K.js → ordinal-r6pIwRdZ.js} +1 -1
  123. package/dist/assets/{pegDiagram-2B236MQR-sBvDY3VX.js → pegDiagram-2B236MQR-CCBiHQjk.js} +1 -1
  124. package/dist/assets/{pieDiagram-ENE6RG2P-DWlWcu08.js → pieDiagram-ENE6RG2P-D61s7l75.js} +1 -1
  125. package/dist/assets/{python-C93q3xXz.js → python-CUwdLgsI.js} +1 -1
  126. package/dist/assets/{quadrantDiagram-ABIIQ3AL-DZutUd3J.js → quadrantDiagram-ABIIQ3AL-D_CWk2DQ.js} +1 -1
  127. package/dist/assets/{railroadDiagram-RFXS5EU6-DQb3lXGM.js → railroadDiagram-RFXS5EU6-CdlCHtxN.js} +1 -1
  128. package/dist/assets/{razor-CBifNGXO.js → razor-BScebUxU.js} +1 -1
  129. package/dist/assets/{requirementDiagram-TGXJPOKE-DqFGXu2e.js → requirementDiagram-TGXJPOKE-BLpHtkoC.js} +1 -1
  130. package/dist/assets/{sankeyDiagram-HTMAVEWB-d7ECahnO.js → sankeyDiagram-HTMAVEWB-CkhYZN2j.js} +1 -1
  131. package/dist/assets/{sequenceDiagram-DBY2YBRQ-DdsbKqct.js → sequenceDiagram-DBY2YBRQ-8bploKJl.js} +1 -1
  132. package/dist/assets/{sizeCapture-X5ZJPWSS-daTB6OcF.js → sizeCapture-X5ZJPWSS-DtSdkS0d.js} +1 -1
  133. package/dist/assets/{src-WBW1QxJt.js → src-6yeq6WBA.js} +1 -1
  134. package/dist/assets/{stateDiagram-2N3HPSRC-P6iSrgwR.js → stateDiagram-2N3HPSRC-DfmVGpfA.js} +1 -1
  135. package/dist/assets/stateDiagram-v2-6OUMAXLB-B2c9CQ-J.js +1 -0
  136. package/dist/assets/{swimlanes-5IMT3BWC-BGnZJx1M.js → swimlanes-5IMT3BWC-DRLk2zRF.js} +2 -2
  137. package/dist/assets/swimlanesDiagram-G3AALYLV-B7_APh_h.js +8 -0
  138. package/dist/assets/{timeline-definition-FHXFAJF6-rA-r3aUR.js → timeline-definition-FHXFAJF6-Cngfy588.js} +1 -1
  139. package/dist/assets/{tsMode-B0oUTcXQ.js → tsMode-DzUAibXK.js} +1 -1
  140. package/dist/assets/{typescript-KJKfYO7K.js → typescript-D9EOeM7N.js} +1 -1
  141. package/dist/assets/{vennDiagram-L72KCM5P-DiVKUjA9.js → vennDiagram-L72KCM5P-DeJn63Lq.js} +1 -1
  142. package/dist/assets/{wardleyDiagram-EHGQE667-Cfxh7UOn.js → wardleyDiagram-EHGQE667-BH1nzy77.js} +1 -1
  143. package/dist/assets/{workers-BSQRIkqa.js → workers-DAiomYZM.js} +1 -1
  144. package/dist/assets/workspace-editor-model-CSGCl21x.js +1 -0
  145. package/dist/assets/{workspace-editor-monaco-BIpqcWf1.js → workspace-editor-monaco-CJNwbpCy.js} +3 -3
  146. package/dist/assets/workspace-editor-monaco-CO9w8YV0.js +1 -0
  147. package/dist/assets/workspace-view-state-BjKPYp0E.js +7 -0
  148. package/dist/assets/{xml-C8FAcN6j.js → xml-wWl1dhkQ.js} +1 -1
  149. package/dist/assets/{xychartDiagram-FW5EYKEG--hmryQeL.js → xychartDiagram-FW5EYKEG-SkO2prCU.js} +1 -1
  150. package/dist/assets/{yaml-BQ4NrRhG.js → yaml-Dc2WDaOZ.js} +1 -1
  151. package/dist/crt-markdown-renderer.js +46 -46
  152. package/dist/index.html +32 -3
  153. package/extensions/browser/backend/agent-browser-runtime.js +1041 -0
  154. package/extensions/browser/backend/agent-capability.js +55 -0
  155. package/extensions/browser/backend/browser-mcp-server.js +621 -0
  156. package/extensions/browser/backend/browser-resource-manager.js +1309 -0
  157. package/extensions/browser/backend/browser-resource-store.js +190 -0
  158. package/extensions/browser/backend/browser-router.js +133 -0
  159. package/extensions/browser/backend/executable-discovery.js +224 -0
  160. package/extensions/browser/backend/farming-browser-client.js +83 -0
  161. package/extensions/browser/backend/index.js +7 -0
  162. package/extensions/browser/bin/farming-browser +471 -0
  163. package/extensions/browser/frontend/BrowserSidebarPortals.tsx +360 -0
  164. package/extensions/browser/frontend/BrowserViewer.tsx +500 -0
  165. package/extensions/browser/frontend/browser-resource-state.ts +60 -0
  166. package/extensions/browser/frontend/browser.css +567 -0
  167. package/extensions/browser/frontend/types.ts +31 -0
  168. package/extensions/browser/frontend/useBrowserResources.ts +158 -0
  169. package/frontend/session-bridge.js +7 -6
  170. package/frontend/skins/crt/app.js +64 -4
  171. package/index.html +31 -2
  172. package/package.json +27 -18
  173. package/shared/backend-connection-status.d.ts +12 -0
  174. package/shared/backend-connection-status.js +35 -4
  175. package/shared/browser-protocol.js +1 -1
  176. package/backend/agent-activity.js +0 -35
  177. package/dist/assets/App-BrP-ENHg.css +0 -1
  178. package/dist/assets/App-Cm8qTjsc.js +0 -210
  179. package/dist/assets/FileEditorPane-fFqHA4wW.js +0 -2
  180. package/dist/assets/IconGlyphs-C2WFcoJS.js +0 -1
  181. package/dist/assets/ProjectFilesSection-Cyy3Vcn8.js +0 -12
  182. package/dist/assets/ReviewPage-L1vh1Ggs.js +0 -3
  183. package/dist/assets/api-D8nyOEbz.js +0 -1
  184. package/dist/assets/channel-BVWNatUB.js +0 -1
  185. package/dist/assets/chunk-2Q5K7J3B-d5WWh8jo.js +0 -1
  186. package/dist/assets/chunk-VAUOI2AC-UHNr_yhX.js +0 -1
  187. package/dist/assets/chunk-XXDRQBXY-BBXwjhUx.js +0 -1
  188. package/dist/assets/classDiagram-OUVF2IWQ-Db0ib5iE.js +0 -1
  189. package/dist/assets/classDiagram-v2-EOCWNBFH-Bkf69XF9.js +0 -1
  190. package/dist/assets/code-dark-BKtvf0Xc.css +0 -1
  191. package/dist/assets/file-icons-BqiH0rDI.js +0 -1
  192. package/dist/assets/flowDiagram-23GEKE2U-DYFo2xmB.js +0 -1
  193. package/dist/assets/main-XMXgyJJt.css +0 -1
  194. package/dist/assets/stateDiagram-v2-6OUMAXLB-DIYSf_qh.js +0 -1
  195. package/dist/assets/swimlanesDiagram-G3AALYLV-BIkrlKrZ.js +0 -8
  196. package/dist/assets/workspace-editor-model-D0Im34S5.js +0 -1
  197. package/dist/assets/workspace-view-state-BiuEbmRr.js +0 -7
  198. /package/dist/acp/{codex-acp-1.1.4.js → codex-acp-1.1.4.mjs} +0 -0
  199. /package/dist/assets/{abap-Cc5D5h5i.js → abap-CtX5nWyX.js} +0 -0
  200. /package/dist/assets/{apex-BKopwLmx.js → apex-DQJRAHwc.js} +0 -0
  201. /package/dist/assets/{array-HeX70jSN.js → array-BTwzYU0x.js} +0 -0
  202. /package/dist/assets/{azcli-DI-C34WA.js → azcli-9SMrOQ4q.js} +0 -0
  203. /package/dist/assets/{bat-Cbp6u42z.js → bat-CBGLUsw1.js} +0 -0
  204. /package/dist/assets/{bicep-4QA1jTCM.js → bicep-pDx8da1v.js} +0 -0
  205. /package/dist/assets/{cameligo-Dda_adPs.js → cameligo-hZV3vQ6_.js} +0 -0
  206. /package/dist/assets/{chunk-Y2CYZVJY-Di0sYzi1.js → chunk-Y2CYZVJY-BYi0W5ea.js} +0 -0
  207. /package/dist/assets/{clojure-CvY8A83L.js → clojure-BhCwU3gq.js} +0 -0
  208. /package/dist/assets/{coffee-4LX2Sv8A.js → coffee-BVB1PsgH.js} +0 -0
  209. /package/dist/assets/{core-B9MwRjDE.js → core-vPkV8P_m.js} +0 -0
  210. /package/dist/assets/{cpp-D4AzSV28.js → cpp-eTjuiNPq.js} +0 -0
  211. /package/dist/assets/{csharp-C4nWN4Yd.js → csharp-1kP-kKZT.js} +0 -0
  212. /package/dist/assets/{csp-CiEaaS5Z.js → csp-C_iQqIes.js} +0 -0
  213. /package/dist/assets/{css-DXxBRsbk.js → css-CxOs7IfP.js} +0 -0
  214. /package/dist/assets/{cypher-DOMzqyFA.js → cypher-Cd-a9Y82.js} +0 -0
  215. /package/dist/assets/{cytoscape.esm-Cl8iC3D7.js → cytoscape.esm-OLojuicg.js} +0 -0
  216. /package/dist/assets/{dart-BWS7MXt_.js → dart-whhXpexn.js} +0 -0
  217. /package/dist/assets/{defaultLocale-GJwWH1Jr.js → defaultLocale-CPimVhfE.js} +0 -0
  218. /package/dist/assets/{dist-ClAL-Tv5.js → dist-CLPYXg1u.js} +0 -0
  219. /package/dist/assets/{dockerfile-Baf4U3xT.js → dockerfile-DibBi8b6.js} +0 -0
  220. /package/dist/assets/{ecl-CT7udlkQ.js → ecl-DlF1KdoW.js} +0 -0
  221. /package/dist/assets/{editor.api2-B9h6iFPK.js → editor.api2-D74ufkbr.js} +0 -0
  222. /package/dist/assets/{elixir-CIbAZw5l.js → elixir-CHQ8uFT3.js} +0 -0
  223. /package/dist/assets/{flow9-Dqiw45WV.js → flow9-8nUnVKk1.js} +0 -0
  224. /package/dist/assets/{fsharp-E8E2L9jU.js → fsharp-DNZNZyEE.js} +0 -0
  225. /package/dist/assets/{go-CouVSN4D.js → go-_kPJjNc_.js} +0 -0
  226. /package/dist/assets/{graphlib-CbSvZcZU.js → graphlib-CBGF9gA9.js} +0 -0
  227. /package/dist/assets/{graphql-a6h6h5-t.js → graphql-4HCNVt5g.js} +0 -0
  228. /package/dist/assets/{hcl-vso3e6N-.js → hcl-C87ymY0o.js} +0 -0
  229. /package/dist/assets/{ini-SwZZkotR.js → ini-BYrrGNTc.js} +0 -0
  230. /package/dist/assets/{init-BPLPMQ3Y.js → init-8_PCa3LO.js} +0 -0
  231. /package/dist/assets/{java-CtRLC4_3.js → java-B211rJbk.js} +0 -0
  232. /package/dist/assets/{julia-CuJWFKQH.js → julia-DVPsRtBm.js} +0 -0
  233. /package/dist/assets/{katex-C8DPyI6s.js → katex-C2iYad0m.js} +0 -0
  234. /package/dist/assets/{kotlin-B_kBhsre.js → kotlin-BYyNerjp.js} +0 -0
  235. /package/dist/assets/{less-CtrHIfz3.js → less-EC9UblGE.js} +0 -0
  236. /package/dist/assets/{lexon-yVt1pJLu.js → lexon-B1yuUdqW.js} +0 -0
  237. /package/dist/assets/{lua-prOwgJ4U.js → lua-Bo6GFzxT.js} +0 -0
  238. /package/dist/assets/{m3-DhrdTQ84.js → m3-C-Y9cBVI.js} +0 -0
  239. /package/dist/assets/{markdown-DoI5HwSM.js → markdown-BHu-wLbe.js} +0 -0
  240. /package/dist/assets/{mermaid-parser.core-BQtepoLd.js → mermaid-parser.core-IxcGtNYG.js} +0 -0
  241. /package/dist/assets/{mips-Dye5jsBU.js → mips-Dkds26rX.js} +0 -0
  242. /package/dist/assets/{msdax-TkNOyIL8.js → msdax-PU4g-8_g.js} +0 -0
  243. /package/dist/assets/{mysql-CEc-RKZF.js → mysql-fPpuGpS7.js} +0 -0
  244. /package/dist/assets/{objective-c-CqqCsQz0.js → objective-c-Wy1e5JUf.js} +0 -0
  245. /package/dist/assets/{pascal-DO_pj8yg.js → pascal-BIXwvrqU.js} +0 -0
  246. /package/dist/assets/{pascaligo-1cbdZdF3.js → pascaligo-DwR0AtL_.js} +0 -0
  247. /package/dist/assets/{path-YdFzr2W6.js → path-BxQuywGl.js} +0 -0
  248. /package/dist/assets/{perl-B_N3PYfC.js → perl-CGHAl8my.js} +0 -0
  249. /package/dist/assets/{pgsql-9iYk1FQ7.js → pgsql-B91M73E2.js} +0 -0
  250. /package/dist/assets/{php-rO2sgoUM.js → php-C70ttrCx.js} +0 -0
  251. /package/dist/assets/{pla-Cbgv60kq.js → pla-BNrXaRh0.js} +0 -0
  252. /package/dist/assets/{postiats-y6xrhy_M.js → postiats-Dv2IAZR9.js} +0 -0
  253. /package/dist/assets/{powerquery-BaD8gWNE.js → powerquery-CkY5Wq7J.js} +0 -0
  254. /package/dist/assets/{powershell-BMhG4JVe.js → powershell-uGIYLU3C.js} +0 -0
  255. /package/dist/assets/{protobuf-CdOP5kN3.js → protobuf-CavfmJu-.js} +0 -0
  256. /package/dist/assets/{pug-Ck60hNF9.js → pug-Drqnyitx.js} +0 -0
  257. /package/dist/assets/{qrcode-DV-_rjU8.js → qrcode-7uGsTEmi.js} +0 -0
  258. /package/dist/assets/{qsharp-BcaKFfP-.js → qsharp-C7yF-Dti.js} +0 -0
  259. /package/dist/assets/{r-rFNes0iK.js → r-HXLGP0_-.js} +0 -0
  260. /package/dist/assets/{redis-DkpQ3K6G.js → redis-C4y6mf2p.js} +0 -0
  261. /package/dist/assets/{redshift-Di9N5TI3.js → redshift-DdQrFPrv.js} +0 -0
  262. /package/dist/assets/{restructuredtext-By5ZzLt6.js → restructuredtext-CBrf2PNj.js} +0 -0
  263. /package/dist/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
  264. /package/dist/assets/{ruby-fF5zGiLp.js → ruby-CVtMoX2B.js} +0 -0
  265. /package/dist/assets/{rust-C5b3AYY6.js → rust-Ckq4sbMP.js} +0 -0
  266. /package/dist/assets/{sb-CD07UNRs.js → sb-er66OpE3.js} +0 -0
  267. /package/dist/assets/{scala-jtn1ip9G.js → scala-BdWZz-hH.js} +0 -0
  268. /package/dist/assets/{scheme-C4vqMkn_.js → scheme-kTV_s2kP.js} +0 -0
  269. /package/dist/assets/{scss-I4Glxjbz.js → scss-Bymy2VJQ.js} +0 -0
  270. /package/dist/assets/{shell-DubUcuns.js → shell-bwEP1LQ1.js} +0 -0
  271. /package/dist/assets/{solidity-CNy2KSl2.js → solidity-DCddeBgL.js} +0 -0
  272. /package/dist/assets/{sophia-D9_URt84.js → sophia-BMN3AQig.js} +0 -0
  273. /package/dist/assets/{sparql-DviXpm4C.js → sparql-CSXOLjzi.js} +0 -0
  274. /package/dist/assets/{sql-vDRbAZgK.js → sql-COg8ewmK.js} +0 -0
  275. /package/dist/assets/{st-CnpnRqA7.js → st-CIaZtBS_.js} +0 -0
  276. /package/dist/assets/{swift-D_g8Ganz.js → swift-DXiOhUHu.js} +0 -0
  277. /package/dist/assets/{systemverilog-OuZa5h1M.js → systemverilog-BTDE6Dr2.js} +0 -0
  278. /package/dist/assets/{tcl-DBm9ucsV.js → tcl-B0zy9NvV.js} +0 -0
  279. /package/dist/assets/{twig-BRzEqX5Y.js → twig-D594l4Z1.js} +0 -0
  280. /package/dist/assets/{typespec-D4ZgGeX8.js → typespec-XKVjaPqo.js} +0 -0
  281. /package/dist/assets/{useWorkspaceMenuKeyboard-Brws6Ar9.js → useWorkspaceMenuKeyboard-Bpy_s69Z.js} +0 -0
  282. /package/dist/assets/{vb-DcP4s6WS.js → vb-B2wYoKRV.js} +0 -0
  283. /package/dist/assets/{wgsl-CnRJ_QUf.js → wgsl-D0Yf2LbK.js} +0 -0
  284. /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: new Date(this.now()).toISOString(),
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, '&lt;')
98
+ .replace(/>/g, '&gt;');
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