farming-code 2.2.29 → 2.2.32

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 (385) hide show
  1. package/README.md +25 -66
  2. package/README.zh_cn.md +15 -66
  3. package/THIRD_PARTY_NOTICES.md +7 -0
  4. package/backend/acp/{client-services.js → client-services.cjs} +132 -134
  5. package/backend/acp/packaged-claude-acp.cjs +37 -0
  6. package/backend/acp/packaged-codex-acp.cjs +37 -0
  7. package/backend/acp/patch-decisions.cjs +182 -0
  8. package/backend/acp/permission-security.cjs +131 -0
  9. package/backend/{acp-checkpoint-store.js → acp-checkpoint-store.cjs} +90 -59
  10. package/backend/{acp-runtime.js → acp-runtime.cjs} +1219 -1125
  11. package/backend/acp-session-state.cjs +622 -0
  12. package/backend/acp-transcript.cjs +525 -0
  13. package/backend/agent-env.cjs +257 -0
  14. package/backend/agent-extension-discovery.cjs +452 -0
  15. package/backend/agent-extension-inventory.cjs +116 -0
  16. package/backend/agent-json-stream.cjs +283 -0
  17. package/backend/agent-lifecycle-journal.cjs +185 -0
  18. package/backend/{agent-manager.js → agent-manager.cjs} +3116 -4275
  19. package/backend/agent-order-transaction.cjs +85 -0
  20. package/backend/agent-order.cjs +159 -0
  21. package/backend/agent-provider-session.cjs +98 -0
  22. package/backend/agent-runtime-binding.cjs +179 -0
  23. package/backend/agent-session-history.cjs +1022 -0
  24. package/backend/agent-session-inventory.cjs +291 -0
  25. package/backend/async-cache.cjs +104 -0
  26. package/backend/atomic-json-store.cjs +67 -0
  27. package/backend/auth.cjs +261 -0
  28. package/backend/authoritative-inventory-cache.cjs +383 -0
  29. package/backend/business-health.cjs +68 -0
  30. package/backend/chat-runtime.cjs +50 -0
  31. package/backend/claude-settings.cjs +132 -0
  32. package/backend/cli-agents.cjs +435 -0
  33. package/backend/codex-context-window.cjs +196 -0
  34. package/backend/codex-models.cjs +228 -0
  35. package/backend/codex-session-archive.cjs +82 -0
  36. package/backend/codex-session-history.cjs +590 -0
  37. package/backend/{codex-terminal-profile.js → codex-terminal-profile.cjs} +149 -155
  38. package/backend/codex-transcript-sanitizer.cjs +130 -0
  39. package/backend/codex-transcript.cjs +2107 -0
  40. package/backend/command-runner-child.cjs +105 -0
  41. package/backend/config-manager.cjs +1263 -0
  42. package/backend/control-api.cjs +439 -0
  43. package/backend/data/LICENSE.cua +21 -0
  44. package/backend/executable-discovery.cjs +204 -0
  45. package/backend/farming-agent-bootstrap.cjs +81 -0
  46. package/backend/farming-agent-bootstrap.zh_cn.md +6 -2
  47. package/backend/{farming-app-cli.js → farming-app-cli.cjs} +441 -493
  48. package/backend/farming-cli.cjs +414 -0
  49. package/backend/farming-net-pass.cjs +319 -0
  50. package/backend/farming-net-registry.cjs +123 -0
  51. package/backend/farming-net-server.cjs +309 -0
  52. package/backend/farming-session-store.cjs +651 -0
  53. package/backend/git-worktree-info.cjs +181 -0
  54. package/backend/haiku-token.cjs +828 -0
  55. package/backend/index-html.cjs +96 -0
  56. package/backend/input-parts.cjs +68 -0
  57. package/backend/input-routing.cjs +40 -0
  58. package/backend/{local-session-engine.js → local-session-engine.cjs} +276 -282
  59. package/backend/main-agent-skills.cjs +309 -0
  60. package/backend/main-page-session.cjs +132 -0
  61. package/backend/native-pty-controller-generation.cjs +139 -0
  62. package/backend/{native-pty-host-client.js → native-pty-host-client.cjs} +338 -346
  63. package/backend/native-pty-host-identity.cjs +125 -0
  64. package/backend/native-pty-host-path.cjs +68 -0
  65. package/backend/{native-pty-host.js → native-pty-host.cjs} +480 -569
  66. package/backend/native-session-engine.cjs +296 -0
  67. package/backend/network.cjs +57 -0
  68. package/backend/npm-update-helper.cjs +399 -0
  69. package/backend/packaged-node-pty.cjs +120 -0
  70. package/backend/preview-session-manager.cjs +89 -0
  71. package/backend/provider-adapters.cjs +492 -0
  72. package/backend/provider-home-configuration.cjs +211 -0
  73. package/backend/provider-session-id.cjs +79 -0
  74. package/backend/provider-session-service.cjs +333 -0
  75. package/backend/qr-share-tickets.cjs +119 -0
  76. package/backend/resource-broadcast-protocol.cjs +50 -0
  77. package/backend/review-diff-router.cjs +202 -0
  78. package/backend/review-diff-service.cjs +1267 -0
  79. package/backend/review-session-router.cjs +93 -0
  80. package/backend/review-session-service.cjs +470 -0
  81. package/backend/review-session-store.cjs +161 -0
  82. package/backend/review-state-router.cjs +132 -0
  83. package/backend/review-state-store.cjs +299 -0
  84. package/backend/run-history-store.cjs +97 -0
  85. package/backend/runtime-dependency-manager.cjs +893 -0
  86. package/backend/runtime-executable-invocation.cjs +37 -0
  87. package/backend/runtime-observation.cjs +96 -0
  88. package/backend/server-process-identity.cjs +70 -0
  89. package/backend/server.cjs +3356 -0
  90. package/backend/session-engine-bridge.cjs +144 -0
  91. package/backend/session-engine-router.cjs +105 -0
  92. package/backend/session-engine.cjs +64 -0
  93. package/backend/session-stream-protocol.cjs +181 -0
  94. package/backend/shell-busy-integration.cjs +442 -0
  95. package/backend/slash-command-discovery.cjs +423 -0
  96. package/backend/storage-layout.cjs +263 -0
  97. package/backend/system-monitor.cjs +96 -0
  98. package/backend/terminal-attach-checkpoint.cjs +73 -0
  99. package/backend/terminal-exit-quiescence.cjs +57 -0
  100. package/backend/{terminal-reducer-flow-control.js → terminal-reducer-flow-control.cjs} +45 -22
  101. package/backend/terminal-runtime-cleanup.cjs +99 -0
  102. package/backend/{terminal-screen-state.js → terminal-screen-state.cjs} +100 -108
  103. package/backend/{terminal-screen-worker-pool.js → terminal-screen-worker-pool.cjs} +89 -79
  104. package/backend/{terminal-screen-worker-thread.js → terminal-screen-worker-thread.cjs} +64 -65
  105. package/backend/terminal-screen-worker.cjs +352 -0
  106. package/backend/terminal-state-serialization.cjs +147 -0
  107. package/backend/{terminal-status.js → terminal-status.cjs} +137 -187
  108. package/backend/theme-manager.cjs +185 -0
  109. package/backend/update-service.cjs +675 -0
  110. package/backend/usage-forecast.cjs +95 -0
  111. package/backend/{usage-history-client.js → usage-history-client.cjs} +144 -94
  112. package/backend/usage-history-smoke.cjs +85 -0
  113. package/backend/usage-history-worker.cjs +60 -0
  114. package/backend/{usage-monitor.js → usage-monitor.cjs} +363 -390
  115. package/backend/workspace-directory.cjs +239 -0
  116. package/backend/workspace-discovery.cjs +271 -0
  117. package/backend/{workspace-file-router.js → workspace-file-router.cjs} +213 -282
  118. package/backend/{workspace-file-service.js → workspace-file-service.cjs} +1139 -1318
  119. package/backend/workspace-root-registry.cjs +192 -0
  120. package/bin/farming +1 -1
  121. package/config/farming.deploy.env.example +1 -1
  122. package/config/farming.install.env.example +1 -1
  123. package/dist/acp/codex-acp-1.1.4.mjs +405 -8
  124. package/dist/assets/App-BCkdpWB6.css +1 -0
  125. package/dist/assets/App-CRjKpaGp.js +697 -0
  126. package/dist/assets/CodeSelect-Ci8sKZPU.js +1 -0
  127. package/dist/assets/CodeSelect-T2eNzrOc.css +1 -0
  128. package/dist/assets/{FileEditorMarkdownPreview-Cs9cLv8a.js → FileEditorMarkdownPreview-V06QHlAg.js} +55 -55
  129. package/dist/assets/FileEditorPane-BX-Wjg9U.js +2 -0
  130. package/dist/assets/IconGlyphs-YhV7tqd4.js +1 -0
  131. package/dist/assets/ProjectFilesSection-BDT2_r0o.js +12 -0
  132. package/dist/assets/ReviewPage-BqAs5sG1.js +3 -0
  133. package/dist/assets/code-dark-B7eM_9G9.css +1 -0
  134. package/dist/assets/{cssMode-Bdg5RpcJ.js → cssMode-ADdAlxTO.js} +1 -1
  135. package/dist/assets/{editor.api2-D74ufkbr.js → editor.api2-BFLAr0YF.js} +162 -162
  136. package/dist/assets/file-icons-BpeqWkIB.js +1 -0
  137. package/dist/assets/{freemarker2-DfMDJfnE.js → freemarker2-C90q5jrp.js} +1 -1
  138. package/dist/assets/{handlebars-BEdPtzMT.js → handlebars-BwUQcEDq.js} +1 -1
  139. package/dist/assets/{html-B1fPb1QD.js → html-1s3uPKfh.js} +1 -1
  140. package/dist/assets/{htmlMode-CY2ZjHxH.js → htmlMode-xtKD99de.js} +1 -1
  141. package/dist/assets/{index-CQNaZc3l.js → index-C8MchCem.js} +3 -3
  142. package/dist/assets/{javascript-BV2OsWA0.js → javascript-Ph7ijyny.js} +1 -1
  143. package/dist/assets/{jsonMode-zd07aeuX.js → jsonMode-D-jGAnFH.js} +1 -1
  144. package/dist/assets/linkComputer-CbGyI_qx.js +1 -0
  145. package/dist/assets/{liquid-BParI24X.js → liquid-0IamE_cI.js} +1 -1
  146. package/dist/assets/{lspLanguageFeatures-BzredkoV.js → lspLanguageFeatures-BxnQunWF.js} +1 -1
  147. package/dist/assets/main-CLKXa6hf.css +1 -0
  148. package/dist/assets/{mdx-vGzUJN98.js → mdx-DdNmfivn.js} +1 -1
  149. package/dist/assets/{monaco.contribution-BmaeBQEc.js → monaco.contribution-B43jsUs1.js} +2 -2
  150. package/dist/assets/{python-CUwdLgsI.js → python-IAt4X5TS.js} +1 -1
  151. package/dist/assets/{razor-BScebUxU.js → razor-DRkFYW0S.js} +1 -1
  152. package/dist/assets/review-C8-oPKXk.css +1 -0
  153. package/dist/assets/{tsMode-DzUAibXK.js → tsMode-_d_4U2gf.js} +1 -1
  154. package/dist/assets/{typescript-D9EOeM7N.js → typescript-C7zcMq9l.js} +1 -1
  155. package/dist/assets/{workers-DAiomYZM.js → workers-D0nOyJzf.js} +1 -1
  156. package/dist/assets/workspace-editor-model-DND_Y-Bj.js +1 -0
  157. package/dist/assets/workspace-editor-monaco-BPWhgcIB.js +4 -0
  158. package/dist/assets/workspace-editor-monaco-N641qPjX.js +1 -0
  159. package/dist/assets/workspace-view-state-k1bTlJ_Z.js +13 -0
  160. package/dist/assets/{xml-wWl1dhkQ.js → xml-BR-HvxT8.js} +1 -1
  161. package/dist/assets/{yaml-Dc2WDaOZ.js → yaml-YtFQwh4q.js} +1 -1
  162. package/dist/index.html +2 -1
  163. package/extensions/browser/backend/agent-browser-runtime.cjs +1040 -0
  164. package/extensions/browser/backend/agent-capability.cjs +85 -0
  165. package/extensions/browser/backend/browser-mcp-server.cjs +655 -0
  166. package/extensions/browser/backend/browser-resource-manager.cjs +1486 -0
  167. package/extensions/browser/backend/browser-resource-store.cjs +228 -0
  168. package/extensions/browser/backend/browser-router.cjs +203 -0
  169. package/extensions/browser/backend/executable-discovery.cjs +248 -0
  170. package/extensions/browser/backend/farming-browser-client.cjs +116 -0
  171. package/extensions/browser/backend/index.cjs +32 -0
  172. package/extensions/browser/backend/managed-chromium-installer.cjs +833 -0
  173. package/extensions/browser/bin/farming-browser +16 -7
  174. package/extensions/computer/backend/agent-capability.cjs +72 -0
  175. package/extensions/computer/backend/computer-constants.cjs +59 -0
  176. package/extensions/computer/backend/computer-mcp-server.cjs +182 -0
  177. package/extensions/computer/backend/computer-resource-manager.cjs +1219 -0
  178. package/extensions/computer/backend/computer-resource-store.cjs +190 -0
  179. package/extensions/computer/backend/computer-router.cjs +257 -0
  180. package/extensions/computer/backend/cua-tools.json +3067 -0
  181. package/extensions/computer/backend/farming-computer-client.cjs +84 -0
  182. package/extensions/computer/backend/index.cjs +35 -0
  183. package/extensions/computer/backend/isolated-browser-provider.cjs +157 -0
  184. package/extensions/computer/bin/farming-computer +131 -0
  185. package/frontend/farming-net/app.js +255 -262
  186. package/frontend/ghostty-loader.js +24 -20
  187. package/frontend/reading-anchor.js +210 -193
  188. package/frontend/runtime-paths.js +24 -26
  189. package/frontend/session-bridge.js +90 -111
  190. package/frontend/session-modal-bridge.js +184 -302
  191. package/frontend/skin-bridge.js +20 -23
  192. package/frontend/skins/crt/app.js +7686 -7901
  193. package/frontend/skins/crt/effects.js +7 -6
  194. package/frontend/skins/crt/index.html +6 -6
  195. package/frontend/skins/crt/styles/markdown.css +4 -4
  196. package/frontend/terminal-bridge.js +174 -181
  197. package/frontend/terminal-replay.js +320 -345
  198. package/frontend/theme-loader.js +55 -71
  199. package/index.html +1 -0
  200. package/package.json +64 -37
  201. package/shared/backend-connection-status.js +38 -69
  202. package/shared/browser-protocol.js +235 -110
  203. package/shared/websocket-liveness.js +40 -0
  204. package/backend/acp/packaged-claude-acp.js +0 -13
  205. package/backend/acp/packaged-codex-acp.js +0 -13
  206. package/backend/acp/patch-decisions.js +0 -153
  207. package/backend/acp/permission-security.js +0 -108
  208. package/backend/acp-session-state.js +0 -644
  209. package/backend/acp-transcript.js +0 -512
  210. package/backend/agent-env.js +0 -250
  211. package/backend/agent-json-stream.js +0 -268
  212. package/backend/agent-lifecycle-journal.js +0 -133
  213. package/backend/agent-memory-report.js +0 -583
  214. package/backend/agent-order.js +0 -166
  215. package/backend/agent-provider-session.js +0 -76
  216. package/backend/agent-runtime-binding.js +0 -174
  217. package/backend/agent-session-history.js +0 -953
  218. package/backend/async-cache.js +0 -94
  219. package/backend/atomic-json-store.js +0 -37
  220. package/backend/auth.js +0 -251
  221. package/backend/bundle-update-helper.js +0 -113
  222. package/backend/chat-runtime.js +0 -28
  223. package/backend/claude-settings.js +0 -121
  224. package/backend/cli-agents.js +0 -486
  225. package/backend/codex-context-window.js +0 -189
  226. package/backend/codex-models.js +0 -226
  227. package/backend/codex-rollout-follower.js +0 -326
  228. package/backend/codex-session-archive.js +0 -65
  229. package/backend/codex-session-history.js +0 -647
  230. package/backend/codex-transcript-sanitizer.js +0 -115
  231. package/backend/codex-transcript.js +0 -2564
  232. package/backend/command-runner-child.js +0 -67
  233. package/backend/config-manager.js +0 -1133
  234. package/backend/control-api.js +0 -455
  235. package/backend/executable-discovery.js +0 -216
  236. package/backend/farming-agent-bootstrap.js +0 -49
  237. package/backend/farming-cli.js +0 -457
  238. package/backend/farming-net-pass.js +0 -285
  239. package/backend/farming-net-registry.js +0 -112
  240. package/backend/farming-net-server.js +0 -298
  241. package/backend/farming-session-store.js +0 -677
  242. package/backend/git-worktree-info.js +0 -181
  243. package/backend/haiku-token.js +0 -772
  244. package/backend/index-html.js +0 -62
  245. package/backend/input-parts.js +0 -54
  246. package/backend/input-routing.js +0 -24
  247. package/backend/json-cli-runtime.js +0 -275
  248. package/backend/main-agent-skills.js +0 -305
  249. package/backend/main-page-session.js +0 -122
  250. package/backend/native-pty-controller-generation.js +0 -106
  251. package/backend/native-pty-host-identity.js +0 -86
  252. package/backend/native-pty-host-path.js +0 -42
  253. package/backend/native-session-engine.js +0 -268
  254. package/backend/network.js +0 -23
  255. package/backend/npm-update-helper.js +0 -374
  256. package/backend/packaged-node-pty.js +0 -99
  257. package/backend/preview-session-manager.js +0 -68
  258. package/backend/provider-adapters.js +0 -399
  259. package/backend/provider-session-id.js +0 -47
  260. package/backend/provider-session-service.js +0 -345
  261. package/backend/qr-share-tickets.js +0 -100
  262. package/backend/review-diff-router.js +0 -180
  263. package/backend/review-diff-service.js +0 -1270
  264. package/backend/review-session-router.js +0 -74
  265. package/backend/review-session-service.js +0 -446
  266. package/backend/review-session-store.js +0 -151
  267. package/backend/review-state-router.js +0 -116
  268. package/backend/review-state-store.js +0 -286
  269. package/backend/run-history-store.js +0 -67
  270. package/backend/runtime-dependency-manager.js +0 -759
  271. package/backend/runtime-executable-invocation.js +0 -20
  272. package/backend/runtime-observation.js +0 -81
  273. package/backend/server-process-identity.js +0 -47
  274. package/backend/server.js +0 -3267
  275. package/backend/session-engine-bridge.js +0 -129
  276. package/backend/session-engine-router.js +0 -66
  277. package/backend/session-engine.js +0 -39
  278. package/backend/session-stream-protocol.js +0 -185
  279. package/backend/shell-busy-integration.js +0 -472
  280. package/backend/slash-command-discovery.js +0 -433
  281. package/backend/storage-layout.js +0 -190
  282. package/backend/system-monitor.js +0 -77
  283. package/backend/terminal-attach-checkpoint.js +0 -74
  284. package/backend/terminal-exit-quiescence.js +0 -39
  285. package/backend/terminal-runtime-cleanup.js +0 -80
  286. package/backend/terminal-screen-worker.js +0 -337
  287. package/backend/terminal-state-serialization.js +0 -127
  288. package/backend/theme-manager.js +0 -163
  289. package/backend/update-service.js +0 -1545
  290. package/backend/usage-forecast.js +0 -86
  291. package/backend/usage-history-scanner.ts +0 -2718
  292. package/backend/usage-history-smoke.js +0 -64
  293. package/backend/usage-history-worker.js +0 -34
  294. package/backend/workspace-directory.js +0 -232
  295. package/backend/workspace-discovery.js +0 -311
  296. package/backend/workspace-root-registry.js +0 -166
  297. package/dist/assets/App-BKNUILuL.css +0 -1
  298. package/dist/assets/App-Ex2oVVXO.js +0 -693
  299. package/dist/assets/FileEditorPane-CNtuGjQ3.js +0 -2
  300. package/dist/assets/IconGlyphs-BClgag6E.js +0 -1
  301. package/dist/assets/ProjectFilesSection-Cf2BEcAN.js +0 -12
  302. package/dist/assets/ReviewPage-BRuxFaH5.js +0 -3
  303. package/dist/assets/code-dark-BEZTQwYZ.css +0 -1
  304. package/dist/assets/file-icons-BOlcJ0ZF.js +0 -1
  305. package/dist/assets/main-DK9Sy7-c.css +0 -1
  306. package/dist/assets/review-BcnwQtJv.css +0 -1
  307. package/dist/assets/workspace-editor-model-CSGCl21x.js +0 -1
  308. package/dist/assets/workspace-editor-monaco-CJNwbpCy.js +0 -4
  309. package/dist/assets/workspace-editor-monaco-CO9w8YV0.js +0 -1
  310. package/dist/assets/workspace-view-state-BjKPYp0E.js +0 -7
  311. package/extensions/browser/backend/agent-browser-runtime.js +0 -1041
  312. package/extensions/browser/backend/agent-capability.js +0 -55
  313. package/extensions/browser/backend/browser-mcp-server.js +0 -621
  314. package/extensions/browser/backend/browser-resource-manager.js +0 -1309
  315. package/extensions/browser/backend/browser-resource-store.js +0 -190
  316. package/extensions/browser/backend/browser-router.js +0 -133
  317. package/extensions/browser/backend/executable-discovery.js +0 -224
  318. package/extensions/browser/backend/farming-browser-client.js +0 -83
  319. package/extensions/browser/backend/index.js +0 -7
  320. package/extensions/browser/frontend/BrowserSidebarPortals.tsx +0 -360
  321. package/extensions/browser/frontend/BrowserViewer.tsx +0 -500
  322. package/extensions/browser/frontend/browser-resource-state.ts +0 -60
  323. package/extensions/browser/frontend/browser.css +0 -567
  324. package/extensions/browser/frontend/types.ts +0 -31
  325. package/extensions/browser/frontend/useBrowserResources.ts +0 -158
  326. package/frontend/vendor/ghostty-web/index.d.ts +0 -1842
  327. package/shared/backend-connection-status.d.ts +0 -37
  328. package/shared/browser-protocol.d.ts +0 -5
  329. /package/dist/assets/{api-w2x5QfxB.js → api-ByCV9MEr.js} +0 -0
  330. /package/dist/assets/{core-vPkV8P_m.js → core-Czwdx12k.js} +0 -0
  331. /package/dist/assets/{fsharp-DNZNZyEE.js → fsharp-BjPpkoJe.js} +0 -0
  332. /package/dist/assets/{go-_kPJjNc_.js → go-BkIDmSUP.js} +0 -0
  333. /package/dist/assets/{graphql-4HCNVt5g.js → graphql-CCJTpqi1.js} +0 -0
  334. /package/dist/assets/{hcl-C87ymY0o.js → hcl-s4z8lUFg.js} +0 -0
  335. /package/dist/assets/{ini-BYrrGNTc.js → ini-CyUW1I7Z.js} +0 -0
  336. /package/dist/assets/{java-B211rJbk.js → java-CFCcGRiM.js} +0 -0
  337. /package/dist/assets/{julia-DVPsRtBm.js → julia-DQyH_EYb.js} +0 -0
  338. /package/dist/assets/{kotlin-BYyNerjp.js → kotlin-B8hA5O51.js} +0 -0
  339. /package/dist/assets/{less-EC9UblGE.js → less-DaVdaas0.js} +0 -0
  340. /package/dist/assets/{lexon-B1yuUdqW.js → lexon-DLBhPd3y.js} +0 -0
  341. /package/dist/assets/{lua-Bo6GFzxT.js → lua-gB1c_cif.js} +0 -0
  342. /package/dist/assets/{m3-C-Y9cBVI.js → m3-C2T3z3gB.js} +0 -0
  343. /package/dist/assets/{markdown-BHu-wLbe.js → markdown-CKq-Euj1.js} +0 -0
  344. /package/dist/assets/{mips-Dkds26rX.js → mips-Bcs5qIDX.js} +0 -0
  345. /package/dist/assets/{msdax-PU4g-8_g.js → msdax-CBP48FHF.js} +0 -0
  346. /package/dist/assets/{mysql-fPpuGpS7.js → mysql-c7tiPcFj.js} +0 -0
  347. /package/dist/assets/{objective-c-Wy1e5JUf.js → objective-c-D4BZkeEV.js} +0 -0
  348. /package/dist/assets/{pascal-BIXwvrqU.js → pascal-CJDZg0MN.js} +0 -0
  349. /package/dist/assets/{pascaligo-DwR0AtL_.js → pascaligo-Bygi8Ynl.js} +0 -0
  350. /package/dist/assets/{perl-CGHAl8my.js → perl-aS8bkf7E.js} +0 -0
  351. /package/dist/assets/{pgsql-B91M73E2.js → pgsql-CzNAYFYl.js} +0 -0
  352. /package/dist/assets/{php-C70ttrCx.js → php-DOtoEFTT.js} +0 -0
  353. /package/dist/assets/{pla-BNrXaRh0.js → pla-DugRnao1.js} +0 -0
  354. /package/dist/assets/{postiats-Dv2IAZR9.js → postiats-DsXwusKh.js} +0 -0
  355. /package/dist/assets/{powerquery-CkY5Wq7J.js → powerquery-DL3o72oD.js} +0 -0
  356. /package/dist/assets/{powershell-uGIYLU3C.js → powershell-C1qHekys.js} +0 -0
  357. /package/dist/assets/{protobuf-CavfmJu-.js → protobuf-DHgsc-cH.js} +0 -0
  358. /package/dist/assets/{pug-Drqnyitx.js → pug-FZXlE_Iu.js} +0 -0
  359. /package/dist/assets/{qrcode-7uGsTEmi.js → qrcode-DJDTdjL7.js} +0 -0
  360. /package/dist/assets/{qsharp-C7yF-Dti.js → qsharp-bLyhI3DT.js} +0 -0
  361. /package/dist/assets/{r-HXLGP0_-.js → r-DjFGXbAa.js} +0 -0
  362. /package/dist/assets/{redis-C4y6mf2p.js → redis-DSoZ_V6u.js} +0 -0
  363. /package/dist/assets/{redshift-DdQrFPrv.js → redshift-CzkdCsrg.js} +0 -0
  364. /package/dist/assets/{restructuredtext-CBrf2PNj.js → restructuredtext-B_XPrGCQ.js} +0 -0
  365. /package/dist/assets/{ruby-CVtMoX2B.js → ruby-Cry2RneJ.js} +0 -0
  366. /package/dist/assets/{rust-Ckq4sbMP.js → rust-CWzdbFq1.js} +0 -0
  367. /package/dist/assets/{sb-er66OpE3.js → sb-DmofU59h.js} +0 -0
  368. /package/dist/assets/{scala-BdWZz-hH.js → scala-BUC2l-vN.js} +0 -0
  369. /package/dist/assets/{scheme-kTV_s2kP.js → scheme-CNIB_72f.js} +0 -0
  370. /package/dist/assets/{scss-Bymy2VJQ.js → scss-DcL4-NbX.js} +0 -0
  371. /package/dist/assets/{shell-bwEP1LQ1.js → shell-CgAGvSSA.js} +0 -0
  372. /package/dist/assets/{solidity-DCddeBgL.js → solidity-DSkt6O_P.js} +0 -0
  373. /package/dist/assets/{sophia-BMN3AQig.js → sophia--5ytcjDN.js} +0 -0
  374. /package/dist/assets/{sparql-CSXOLjzi.js → sparql-C1Srr7cD.js} +0 -0
  375. /package/dist/assets/{sql-COg8ewmK.js → sql-2k_O5fj7.js} +0 -0
  376. /package/dist/assets/{st-CIaZtBS_.js → st-DCCCPqfz.js} +0 -0
  377. /package/dist/assets/{swift-DXiOhUHu.js → swift-Dd8mAfOt.js} +0 -0
  378. /package/dist/assets/{systemverilog-BTDE6Dr2.js → systemverilog-B0-aZYXu.js} +0 -0
  379. /package/dist/assets/{tcl-B0zy9NvV.js → tcl-CZAZwPVX.js} +0 -0
  380. /package/dist/assets/{twig-D594l4Z1.js → twig-D-Ag0tdS.js} +0 -0
  381. /package/dist/assets/{typespec-XKVjaPqo.js → typespec-B7kTFp0Z.js} +0 -0
  382. /package/dist/assets/{useWorkspaceMenuKeyboard-Bpy_s69Z.js → useWorkspaceMenuKeyboard-uYZpDxhU.js} +0 -0
  383. /package/dist/assets/{vb-B2wYoKRV.js → vb-20w1JPum.js} +0 -0
  384. /package/dist/assets/{wgsl-D0Yf2LbK.js → wgsl-CLJfqFt0.js} +0 -0
  385. /package/dist/assets/{workspace-working-copy-C3FSR3N6.js → workspace-working-copy-Cfz07cOd.js} +0 -0
@@ -0,0 +1,1219 @@
1
+ // Generated by scripts/build-backend-runtime.ts. Do not edit.
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var computer_resource_manager_exports = {};
31
+ __export(computer_resource_manager_exports, {
32
+ ComputerResourceManager: () => ComputerResourceManager,
33
+ exactImageRef: () => exactImageRef
34
+ });
35
+ module.exports = __toCommonJS(computer_resource_manager_exports);
36
+ var storageLayout = __toESM(require("../../../backend/storage-layout.cjs"));
37
+ var import_computer_constants = require("./computer-constants.cjs");
38
+ var import_computer_resource_store = require("./computer-resource-store.cjs");
39
+ const { EventEmitter } = require("events");
40
+ const { execFile } = require("child_process");
41
+ const { promisify } = require("util");
42
+ const crypto = require("crypto");
43
+ const fs = require("fs");
44
+ const http = require("http");
45
+ const path = require("path");
46
+ const WebSocket = require("ws");
47
+ const execFileAsync = promisify(execFile);
48
+ const INACTIVE_AGENT_STATUSES = /* @__PURE__ */ new Set(["dead", "error", "exited", "stopped"]);
49
+ const CUA_TOOL_MANIFEST = require("./cua-tools.json");
50
+ const SUPPORTED_UPSTREAM_TOOLS = new Set(
51
+ (CUA_TOOL_MANIFEST.tools || []).map((tool) => String(tool.upstreamName || "").trim()).filter(Boolean)
52
+ );
53
+ const READ_ONLY_TOOLS = new Set(
54
+ (CUA_TOOL_MANIFEST.tools || []).filter((tool) => tool.annotations?.readOnlyHint === true).map((tool) => String(tool.upstreamName || "").trim()).filter(Boolean)
55
+ );
56
+ const STATE_OBSERVATION_TOOLS = /* @__PURE__ */ new Set([
57
+ "get_accessibility_tree",
58
+ "get_browser_state",
59
+ "get_desktop_state",
60
+ "get_window_state"
61
+ ]);
62
+ const SCREENSHOT_TOOLS = /* @__PURE__ */ new Set([
63
+ "get_browser_state",
64
+ "get_desktop_state",
65
+ "get_window_state",
66
+ "zoom"
67
+ ]);
68
+ const DRIVER_CALL_TIMEOUT_MS = 45e3;
69
+ const DOCKER_TIMEOUT_MS = 9e4;
70
+ const START_TIMEOUT_MS = 45e3;
71
+ const COMPUTER_BROWSER_CDP_PORT = "9223/tcp";
72
+ const COMPUTER_BROWSER_MOUNT = "/opt/farming/chromium";
73
+ const COMPUTER_BROWSER_RELAY_SCRIPT = [
74
+ "import select,socket",
75
+ "listener=socket.socket()",
76
+ "listener.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)",
77
+ 'listener.bind(("0.0.0.0",9223))',
78
+ "listener.listen(32)",
79
+ "while True:",
80
+ " client,_=listener.accept()",
81
+ ' upstream=socket.create_connection(("127.0.0.1",9222),timeout=5)',
82
+ " sockets=[client,upstream]",
83
+ " while sockets:",
84
+ " readable,_,failed=select.select(sockets,[],sockets,30)",
85
+ " if failed: break",
86
+ " if not readable: continue",
87
+ " for source in readable:",
88
+ " data=source.recv(65536)",
89
+ " if not data: sockets=[]; break",
90
+ " (upstream if source is client else client).sendall(data)",
91
+ " client.close()",
92
+ " upstream.close()"
93
+ ].join("\n");
94
+ function recordValue(value) {
95
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
96
+ }
97
+ function pathInside(root, candidate) {
98
+ const relative = path.relative(path.resolve(root), path.resolve(candidate));
99
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
100
+ }
101
+ function computerError(message, status, code, extra = {}) {
102
+ return Object.assign(new Error(message), { status, code, ...extra });
103
+ }
104
+ function randomPassword() {
105
+ return crypto.randomBytes(9).toString("base64url").slice(0, 12);
106
+ }
107
+ function exactImageRef(value) {
108
+ const requested = String(value || "").trim() || import_computer_constants.COMPUTER_IMAGE;
109
+ if (!requested.endsWith(`@${import_computer_constants.COMPUTER_IMAGE_INDEX_DIGEST}`)) {
110
+ throw computerError(
111
+ `Computer image must be pinned to ${import_computer_constants.COMPUTER_IMAGE_INDEX_DIGEST}`,
112
+ 400,
113
+ "COMPUTER_IMAGE_NOT_PINNED"
114
+ );
115
+ }
116
+ return requested;
117
+ }
118
+ function safeNamePart(value) {
119
+ return value.toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 42);
120
+ }
121
+ function parsePort(inspect) {
122
+ return parsePublishedPort(inspect, "6901/tcp");
123
+ }
124
+ function parsePublishedPort(inspect, containerPort) {
125
+ const networkSettings = recordValue(inspect.NetworkSettings);
126
+ const ports = recordValue(networkSettings.Ports);
127
+ const mappings = ports[containerPort];
128
+ if (!Array.isArray(mappings) || mappings.length !== 1) return 0;
129
+ const mapping = recordValue(mappings[0]);
130
+ if (mapping.HostIp !== "127.0.0.1") return 0;
131
+ const port = Number(mapping.HostPort);
132
+ return Number.isSafeInteger(port) && port > 0 && port <= 65535 ? port : 0;
133
+ }
134
+ function waitForHttpPath(port, requestPath, failureMessage, failureCode, timeoutMs = START_TIMEOUT_MS) {
135
+ const deadline = Date.now() + timeoutMs;
136
+ return new Promise((resolve, reject) => {
137
+ const attempt = () => {
138
+ if (Date.now() >= deadline) {
139
+ reject(computerError(failureMessage, 504, failureCode));
140
+ return;
141
+ }
142
+ let successfulResponse = false;
143
+ let attemptFinished = false;
144
+ const retry = () => {
145
+ if (attemptFinished) return;
146
+ attemptFinished = true;
147
+ setTimeout(attempt, 250);
148
+ };
149
+ const request = http.get({
150
+ hostname: "127.0.0.1",
151
+ port,
152
+ path: requestPath,
153
+ timeout: 2e3,
154
+ agent: false,
155
+ headers: { Connection: "close" }
156
+ }, (response) => {
157
+ response.resume();
158
+ if (Number(response.statusCode) >= 200 && Number(response.statusCode) < 500) {
159
+ successfulResponse = true;
160
+ return;
161
+ }
162
+ });
163
+ request.on("timeout", () => request.destroy());
164
+ request.on("error", () => {
165
+ if (!successfulResponse) retry();
166
+ });
167
+ request.on("close", () => {
168
+ if (attemptFinished) return;
169
+ attemptFinished = true;
170
+ if (successfulResponse) resolve();
171
+ else setTimeout(attempt, 250);
172
+ });
173
+ };
174
+ attempt();
175
+ });
176
+ }
177
+ function waitForHttp(port, timeoutMs = START_TIMEOUT_MS) {
178
+ return waitForHttpPath(
179
+ port,
180
+ "/vnc.html",
181
+ "Computer Viewer did not become ready in time",
182
+ "COMPUTER_VIEWER_TIMEOUT",
183
+ timeoutMs
184
+ );
185
+ }
186
+ function waitForCdp(port, timeoutMs = START_TIMEOUT_MS) {
187
+ return waitForHttpPath(
188
+ port,
189
+ "/json/version",
190
+ "Computer Chromium did not expose DevTools in time",
191
+ "COMPUTER_BROWSER_CDP_TIMEOUT",
192
+ timeoutMs
193
+ );
194
+ }
195
+ class ComputerResourceManager extends EventEmitter {
196
+ configDir;
197
+ isEnabled;
198
+ getSettings;
199
+ store;
200
+ docker;
201
+ configFingerprint;
202
+ operations = /* @__PURE__ */ new Map();
203
+ stopAdmissions = /* @__PURE__ */ new Map();
204
+ controlAdmissions = /* @__PURE__ */ new Map();
205
+ viewerSockets = /* @__PURE__ */ new Map();
206
+ browserLeases = /* @__PURE__ */ new Map();
207
+ capabilityCache = null;
208
+ preparePromise = null;
209
+ constructor(options) {
210
+ super();
211
+ this.configDir = options.configDir;
212
+ this.isEnabled = options.isEnabled || (() => false);
213
+ this.getSettings = options.getSettings || (() => ({}));
214
+ this.store = new import_computer_resource_store.ComputerResourceStore(options.configDir);
215
+ this.configFingerprint = crypto.createHash("sha256").update(options.configDir).digest("hex").slice(0, 12);
216
+ this.docker = options.dockerRunner || (async (args, runOptions = {}) => {
217
+ const result = await execFileAsync("docker", args, {
218
+ encoding: "utf8",
219
+ timeout: runOptions.timeoutMs || DOCKER_TIMEOUT_MS,
220
+ maxBuffer: runOptions.maxBuffer || 20 * 1024 * 1024
221
+ });
222
+ return {
223
+ stdout: String(result.stdout || ""),
224
+ stderr: String(result.stderr || "")
225
+ };
226
+ });
227
+ }
228
+ async init() {
229
+ this.store.init();
230
+ for (const resource of this.store.list()) {
231
+ if (!resource.containerId) {
232
+ if (resource.status !== "stopped") {
233
+ this.patch(resource.id, {
234
+ status: "failed",
235
+ error: "Owned Computer container identity is missing",
236
+ viewerPort: 0,
237
+ sessionId: ""
238
+ });
239
+ }
240
+ continue;
241
+ }
242
+ try {
243
+ const inspect = await this.inspectOwnedContainer(resource);
244
+ const running = recordValue(inspect.State).Running === true;
245
+ if (!running) {
246
+ this.patch(resource.id, {
247
+ status: "stopped",
248
+ viewerPort: 0,
249
+ sessionId: "",
250
+ error: ""
251
+ });
252
+ continue;
253
+ }
254
+ const viewerPort = parsePort(inspect);
255
+ if (!viewerPort) throw new Error("Computer container has no loopback Viewer port");
256
+ await this.ensureDriver(resource, resource.sessionId || this.sessionId(resource));
257
+ this.patch(resource.id, {
258
+ status: "running",
259
+ viewerPort,
260
+ sessionId: resource.sessionId || this.sessionId(resource),
261
+ error: ""
262
+ });
263
+ } catch (caught) {
264
+ const error = caught;
265
+ this.patch(resource.id, {
266
+ status: "failed",
267
+ viewerPort: 0,
268
+ sessionId: "",
269
+ error: error.message || "Computer recovery failed"
270
+ });
271
+ }
272
+ }
273
+ }
274
+ requireEnabled() {
275
+ if (!this.isEnabled()) {
276
+ throw computerError("Computer plugin is disabled", 409, "COMPUTER_DISABLED");
277
+ }
278
+ }
279
+ imageRef() {
280
+ return exactImageRef(this.getSettings().computerImage);
281
+ }
282
+ compatibilityMode() {
283
+ return this.getSettings().computerCompatibilityMode === true;
284
+ }
285
+ list() {
286
+ return this.store.snapshot();
287
+ }
288
+ get(id) {
289
+ const resource = this.store.get(id);
290
+ if (!resource) throw computerError("Computer Resource was not found", 404, "COMPUTER_NOT_FOUND");
291
+ return (0, import_computer_resource_store.publicResource)(resource, this.store.revision);
292
+ }
293
+ privateResource(id) {
294
+ const resource = this.store.get(id);
295
+ if (!resource) throw computerError("Computer Resource was not found", 404, "COMPUTER_NOT_FOUND");
296
+ return resource;
297
+ }
298
+ create(input) {
299
+ this.requireEnabled();
300
+ const existing = this.store.list().find(
301
+ (resource2) => resource2.ownerAgentId === input.ownerAgentId
302
+ );
303
+ if (existing) return (0, import_computer_resource_store.publicResource)(existing, this.store.revision);
304
+ const resource = this.store.create(input);
305
+ this.emitResource(resource);
306
+ return (0, import_computer_resource_store.publicResource)(resource, this.store.revision);
307
+ }
308
+ async acquireBrowser(input) {
309
+ this.requireEnabled();
310
+ const executablePath = this.browserExecutableInContainer(input.executablePath);
311
+ this.ensureBrowserCacheTraversal(input.executablePath);
312
+ const resource = this.create({
313
+ ownerAgentId: input.ownerAgentId,
314
+ projectRootId: input.projectRootId,
315
+ workspace: input.workspace,
316
+ name: "Desktop"
317
+ });
318
+ if (resource.projectRootId !== input.projectRootId || resource.workspace !== input.workspace) {
319
+ throw computerError(
320
+ "The Agent Computer belongs to a different Project workspace",
321
+ 409,
322
+ "COMPUTER_BROWSER_OWNER_MISMATCH"
323
+ );
324
+ }
325
+ await this.ensureBrowserCacheMount(resource.id);
326
+ await this.start(resource.id);
327
+ const result = await this.enqueue(resource.id, async () => {
328
+ const current = this.privateResource(resource.id);
329
+ if (current.status !== "running" || !current.containerId) {
330
+ throw computerError(
331
+ "Computer stopped before Chromium could start",
332
+ 409,
333
+ "COMPUTER_NOT_RUNNING"
334
+ );
335
+ }
336
+ const inspect = await this.inspectOwnedContainer(current);
337
+ const cdpPort = parsePublishedPort(inspect, COMPUTER_BROWSER_CDP_PORT);
338
+ if (!cdpPort) {
339
+ throw computerError(
340
+ "Computer container has no loopback DevTools port",
341
+ 409,
342
+ "COMPUTER_BROWSER_PORT_MISSING"
343
+ );
344
+ }
345
+ try {
346
+ await waitForCdp(cdpPort, 500);
347
+ } catch {
348
+ const displayReady = await this.waitForContainer(current.containerId, [
349
+ "test",
350
+ "-S",
351
+ "/tmp/.X11-unix/X1"
352
+ ]);
353
+ if (!displayReady) {
354
+ throw computerError(
355
+ "Computer desktop did not become ready for Chromium",
356
+ 504,
357
+ "COMPUTER_BROWSER_DISPLAY_TIMEOUT"
358
+ );
359
+ }
360
+ let chromiumReady = await this.waitForContainer(current.containerId, [
361
+ "python3",
362
+ "-c",
363
+ 'import urllib.request; urllib.request.urlopen("http://127.0.0.1:9222/json/version", timeout=1)'
364
+ ], 500);
365
+ if (!chromiumReady) {
366
+ await this.docker([
367
+ "exec",
368
+ "-d",
369
+ "-u",
370
+ import_computer_constants.COMPUTER_USER,
371
+ "-e",
372
+ "HOME=/home/cua",
373
+ "-e",
374
+ "DISPLAY=:1",
375
+ current.containerId,
376
+ executablePath,
377
+ "--no-sandbox",
378
+ "--remote-debugging-port=9222",
379
+ "--user-data-dir=/home/cua/.farming-browser",
380
+ "--no-first-run",
381
+ "--no-default-browser-check",
382
+ "about:blank"
383
+ ], { timeoutMs: 1e4 });
384
+ chromiumReady = await this.waitForContainer(current.containerId, [
385
+ "python3",
386
+ "-c",
387
+ 'import urllib.request; urllib.request.urlopen("http://127.0.0.1:9222/json/version", timeout=1)'
388
+ ]);
389
+ }
390
+ if (!chromiumReady) {
391
+ throw computerError(
392
+ "Chromium did not expose its internal DevTools endpoint",
393
+ 504,
394
+ "COMPUTER_BROWSER_CHROMIUM_TIMEOUT"
395
+ );
396
+ }
397
+ await this.docker([
398
+ "exec",
399
+ "-d",
400
+ current.containerId,
401
+ "python3",
402
+ "-c",
403
+ COMPUTER_BROWSER_RELAY_SCRIPT
404
+ ], { timeoutMs: 1e4 });
405
+ await waitForCdp(cdpPort);
406
+ }
407
+ this.browserLeases.set(
408
+ resource.id,
409
+ (this.browserLeases.get(resource.id) || 0) + 1
410
+ );
411
+ return { cdpUrl: `http://127.0.0.1:${cdpPort}`, leaseKey: resource.id };
412
+ });
413
+ return result;
414
+ }
415
+ releaseBrowser(leaseKey) {
416
+ const current = this.browserLeases.get(leaseKey) || 0;
417
+ if (current <= 1) this.browserLeases.delete(leaseKey);
418
+ else this.browserLeases.set(leaseKey, current - 1);
419
+ return Promise.resolve();
420
+ }
421
+ async verifyBrowserExecutable(executablePath) {
422
+ const containerPath = this.browserExecutableInContainer(executablePath);
423
+ this.ensureBrowserCacheTraversal(executablePath);
424
+ const result = await this.docker([
425
+ "run",
426
+ "--rm",
427
+ "--label",
428
+ "farming.dev/kind=computer-browser-probe",
429
+ "--label",
430
+ `farming.dev/config=${this.configFingerprint}`,
431
+ ...this.compatibilityMode() ? ["--security-opt", "seccomp=unconfined"] : [],
432
+ "--user",
433
+ import_computer_constants.COMPUTER_USER,
434
+ "-e",
435
+ "HOME=/home/cua",
436
+ "--entrypoint",
437
+ containerPath,
438
+ "-v",
439
+ `${this.browserCacheRoot()}:${COMPUTER_BROWSER_MOUNT}:ro`,
440
+ this.imageRef(),
441
+ "--no-sandbox",
442
+ "--version"
443
+ ], { timeoutMs: 3e4 });
444
+ const version = result.stdout.trim() || result.stderr.trim();
445
+ if (!/(?:Google Chrome|Chromium)/i.test(version)) {
446
+ throw computerError(
447
+ `Computer Chromium verification returned an unexpected version: ${version}`,
448
+ 409,
449
+ "COMPUTER_BROWSER_VERSION_MISMATCH"
450
+ );
451
+ }
452
+ return version;
453
+ }
454
+ rename(id, name) {
455
+ const normalized = String(name || "").trim().slice(0, 120);
456
+ if (!normalized) throw computerError("Computer name is required", 400, "COMPUTER_NAME_REQUIRED");
457
+ return this.patch(id, { name: normalized });
458
+ }
459
+ snapshot() {
460
+ return this.stateSnapshot();
461
+ }
462
+ stateSnapshot() {
463
+ return this.store.snapshot();
464
+ }
465
+ async capability(refresh = false) {
466
+ if (this.capabilityCache && !refresh) {
467
+ return { ...this.capabilityCache, enabled: this.isEnabled() };
468
+ }
469
+ const probe = await this.probeSettings(this.getSettings());
470
+ this.capabilityCache = {
471
+ available: probe.dockerAvailable && probe.imageReady,
472
+ enabled: this.isEnabled(),
473
+ ...probe,
474
+ imageDigest: import_computer_constants.COMPUTER_IMAGE_INDEX_DIGEST,
475
+ driverVersion: import_computer_constants.COMPUTER_DRIVER_VERSION,
476
+ compatibilityMode: this.compatibilityMode()
477
+ };
478
+ return this.capabilityCache;
479
+ }
480
+ async probeSettings(settings) {
481
+ const image = exactImageRef(settings.computerImage);
482
+ let dockerAvailable = false;
483
+ let imageReady = false;
484
+ let error = "";
485
+ try {
486
+ await this.docker(["version", "--format", "{{.Server.Version}}"], { timeoutMs: 8e3 });
487
+ dockerAvailable = true;
488
+ await this.docker(["image", "inspect", image, "--format", "{{.Id}}"], { timeoutMs: 8e3 });
489
+ imageReady = true;
490
+ } catch (caught) {
491
+ error = caught.message || String(caught);
492
+ }
493
+ return { dockerAvailable, imageReady, image, error };
494
+ }
495
+ prepare() {
496
+ if (this.preparePromise) return this.preparePromise;
497
+ this.preparePromise = (async () => {
498
+ await this.docker(["version", "--format", "{{.Server.Version}}"], { timeoutMs: 8e3 });
499
+ await this.docker(["pull", this.imageRef()], { timeoutMs: 20 * 6e4, maxBuffer: 64 * 1024 * 1024 });
500
+ const probeName = `farming-computer-probe-${this.configFingerprint}-${crypto.randomBytes(3).toString("hex")}`;
501
+ const args = [
502
+ "run",
503
+ "--rm",
504
+ "--name",
505
+ probeName,
506
+ "--label",
507
+ "farming.dev/kind=computer-probe",
508
+ "--label",
509
+ `farming.dev/config=${this.configFingerprint}`,
510
+ ...this.compatibilityMode() ? ["--security-opt", "seccomp=unconfined"] : [],
511
+ "--entrypoint",
512
+ "/bin/sh",
513
+ this.imageRef(),
514
+ "-lc",
515
+ `su -s /bin/sh -c 'HOME=/home/cua ${import_computer_constants.COMPUTER_DRIVER_BIN} --version' ${import_computer_constants.COMPUTER_USER}`
516
+ ];
517
+ try {
518
+ const result = await this.docker(args, { timeoutMs: 6e4 });
519
+ if (!result.stdout.includes(import_computer_constants.COMPUTER_DRIVER_VERSION)) {
520
+ throw computerError(
521
+ `Computer image contains an unexpected Cua Driver version: ${result.stdout.trim()}`,
522
+ 409,
523
+ "COMPUTER_DRIVER_VERSION_MISMATCH"
524
+ );
525
+ }
526
+ } catch (caught) {
527
+ const message = caught.message || String(caught);
528
+ const compatibilityRequired = /operation not permitted|pthread_create|clone3/i.test(
529
+ `${message}
530
+ ${caught.stderr || ""}`
531
+ );
532
+ throw computerError(
533
+ compatibilityRequired ? "This Docker Engine requires explicit Computer compatibility mode" : `Computer runtime probe failed: ${message}`,
534
+ 409,
535
+ compatibilityRequired ? "COMPUTER_COMPATIBILITY_REQUIRED" : "COMPUTER_PROBE_FAILED",
536
+ { compatibilityRequired }
537
+ );
538
+ }
539
+ this.capabilityCache = null;
540
+ return this.capability(true);
541
+ })().finally(() => {
542
+ this.preparePromise = null;
543
+ });
544
+ return this.preparePromise;
545
+ }
546
+ start(id) {
547
+ this.requireEnabled();
548
+ this.assertTransitionAdmissionOpen(id);
549
+ return this.enqueue(id, async () => {
550
+ this.assertTransitionAdmissionOpen(id);
551
+ let resource = this.privateResource(id);
552
+ if (resource.status === "running") return (0, import_computer_resource_store.publicResource)(resource, this.store.revision);
553
+ const generation = resource.generation + 1;
554
+ const password = resource.vncPassword || randomPassword();
555
+ const containerName = resource.containerName || this.containerName(resource);
556
+ resource = this.patch(id, {
557
+ status: "starting",
558
+ generation,
559
+ controlOwner: "agent",
560
+ controlEpoch: resource.controlEpoch + 1,
561
+ needsObserve: false,
562
+ vncPassword: password,
563
+ containerName,
564
+ viewerPort: 0,
565
+ sessionId: "",
566
+ error: ""
567
+ }, true);
568
+ try {
569
+ let containerId = resource.containerId;
570
+ if (containerId) {
571
+ await this.inspectOwnedContainer(resource);
572
+ } else {
573
+ const labels = this.containerLabels(resource);
574
+ const create = await this.docker([
575
+ "create",
576
+ "--name",
577
+ containerName,
578
+ ...labels.flatMap(([key, value]) => ["--label", `${key}=${value}`]),
579
+ "--cpus",
580
+ import_computer_constants.COMPUTER_CONTAINER_CPUS,
581
+ "--memory",
582
+ import_computer_constants.COMPUTER_CONTAINER_MEMORY,
583
+ "--shm-size",
584
+ import_computer_constants.COMPUTER_CONTAINER_SHM_SIZE,
585
+ "--pids-limit",
586
+ import_computer_constants.COMPUTER_CONTAINER_PIDS,
587
+ "--add-host",
588
+ "host.docker.internal:host-gateway",
589
+ "-p",
590
+ "127.0.0.1::6901",
591
+ "-p",
592
+ "127.0.0.1::9223",
593
+ "-v",
594
+ `${this.browserCacheRoot()}:${COMPUTER_BROWSER_MOUNT}:ro`,
595
+ "-e",
596
+ `VNC_PW=${password}`,
597
+ ...this.compatibilityMode() ? ["--security-opt", "seccomp=unconfined"] : [],
598
+ this.imageRef()
599
+ ]);
600
+ containerId = create.stdout.trim();
601
+ if (!/^[a-f0-9]{12,64}$/i.test(containerId)) {
602
+ throw new Error("Docker did not return an exact Computer container identity");
603
+ }
604
+ resource = this.patch(id, { containerId }, true);
605
+ await this.inspectOwnedContainer(resource);
606
+ }
607
+ await this.docker(["start", containerId]);
608
+ const inspect = await this.inspectOwnedContainer(this.privateResource(id));
609
+ const viewerPort = parsePort(inspect);
610
+ if (!viewerPort) throw new Error("Docker did not publish the Computer Viewer on loopback");
611
+ await waitForHttp(viewerPort);
612
+ const sessionId = this.sessionId(this.privateResource(id));
613
+ await this.ensureDriver(this.privateResource(id), sessionId);
614
+ return this.patch(id, {
615
+ status: "running",
616
+ viewerPort,
617
+ sessionId,
618
+ error: ""
619
+ });
620
+ } catch (caught) {
621
+ const error = caught;
622
+ this.patch(id, {
623
+ status: "failed",
624
+ viewerPort: 0,
625
+ sessionId: "",
626
+ error: error.message || "Computer start failed"
627
+ });
628
+ throw error;
629
+ }
630
+ });
631
+ }
632
+ stop(id, internal = false) {
633
+ if (!internal) this.requireEnabled();
634
+ if (!internal && (this.browserLeases.get(id) || 0) > 0) {
635
+ throw computerError(
636
+ "Stop the Agent Browsers using this Computer first",
637
+ 409,
638
+ "COMPUTER_IN_USE_BY_BROWSER"
639
+ );
640
+ }
641
+ this.holdStopAdmission(id);
642
+ return this.enqueue(id, async () => {
643
+ const resource = this.privateResource(id);
644
+ if (!resource.containerId || resource.status === "stopped") {
645
+ return this.patch(id, {
646
+ status: "stopped",
647
+ viewerPort: 0,
648
+ sessionId: "",
649
+ controlOwner: "agent",
650
+ needsObserve: false,
651
+ error: ""
652
+ });
653
+ }
654
+ this.patch(id, { status: "stopping" });
655
+ this.closeViewers(id, 4e3, "Computer stopped");
656
+ try {
657
+ if (resource.sessionId) {
658
+ await this.driverCall(resource, "end_session", { session: resource.sessionId }).catch(() => null);
659
+ }
660
+ await this.inspectOwnedContainer(resource);
661
+ await this.docker(["stop", "--time", "10", resource.containerId], { timeoutMs: 3e4 });
662
+ return this.patch(id, {
663
+ status: "stopped",
664
+ viewerPort: 0,
665
+ sessionId: "",
666
+ controlOwner: "agent",
667
+ controlEpoch: resource.controlEpoch + 1,
668
+ needsObserve: false,
669
+ error: ""
670
+ });
671
+ } catch (caught) {
672
+ const error = caught;
673
+ this.patch(id, { status: "failed", error: error.message || "Computer stop failed" });
674
+ throw error;
675
+ }
676
+ }).finally(() => this.releaseStopAdmission(id));
677
+ }
678
+ async delete(id, internal = false) {
679
+ if (!internal) this.requireEnabled();
680
+ if (!internal && (this.browserLeases.get(id) || 0) > 0) {
681
+ throw computerError(
682
+ "Delete the Agent Browsers using this Computer first",
683
+ 409,
684
+ "COMPUTER_IN_USE_BY_BROWSER"
685
+ );
686
+ }
687
+ this.holdStopAdmission(id);
688
+ try {
689
+ await this.stop(id, true);
690
+ return await this.enqueue(id, async () => {
691
+ const resource = this.privateResource(id);
692
+ if (resource.containerId) {
693
+ await this.inspectOwnedContainer(resource);
694
+ await this.docker(["rm", resource.containerId], { timeoutMs: 3e4 });
695
+ }
696
+ this.closeViewers(id, 4e3, "Computer deleted");
697
+ this.browserLeases.delete(id);
698
+ this.store.remove(id);
699
+ this.emit("deleted", { id, collectionRevision: this.store.revision });
700
+ return { id, collectionRevision: this.store.revision };
701
+ });
702
+ } finally {
703
+ this.releaseStopAdmission(id);
704
+ }
705
+ }
706
+ takeControl(id, owner) {
707
+ this.requireEnabled();
708
+ const resource = this.privateResource(id);
709
+ if (resource.status !== "running") {
710
+ throw computerError("Computer is not running", 409, "COMPUTER_NOT_RUNNING");
711
+ }
712
+ if (owner === resource.controlOwner) {
713
+ return Promise.resolve((0, import_computer_resource_store.publicResource)(resource, this.store.revision));
714
+ }
715
+ if (this.isStopping(id)) {
716
+ throw computerError("Computer is stopping", 409, "COMPUTER_STOPPING");
717
+ }
718
+ if (this.isControlChanging(id)) {
719
+ throw computerError("Computer control is changing", 409, "COMPUTER_CONTROL_CHANGING");
720
+ }
721
+ this.holdControlAdmission(id);
722
+ return this.enqueue(id, async () => {
723
+ const current = this.privateResource(id);
724
+ if (current.status !== "running") {
725
+ throw computerError("Computer is not running", 409, "COMPUTER_NOT_RUNNING");
726
+ }
727
+ if (owner === current.controlOwner) {
728
+ return (0, import_computer_resource_store.publicResource)(current, this.store.revision);
729
+ }
730
+ this.closeViewers(id, 4002, "Computer control changed");
731
+ return this.patch(id, {
732
+ controlOwner: owner,
733
+ controlEpoch: current.controlEpoch + 1,
734
+ needsObserve: owner === "agent"
735
+ });
736
+ }).finally(() => this.releaseControlAdmission(id));
737
+ }
738
+ callTool(id, tool, input, caller = "agent") {
739
+ this.requireEnabled();
740
+ if (!SUPPORTED_UPSTREAM_TOOLS.has(tool)) {
741
+ throw computerError(`Computer tool is not supported: ${tool}`, 400, "COMPUTER_TOOL_NOT_SUPPORTED");
742
+ }
743
+ const resource = this.privateResource(id);
744
+ if (this.isStopping(id)) {
745
+ throw computerError("Computer is stopping", 409, "COMPUTER_STOPPING");
746
+ }
747
+ if (this.isControlChanging(id)) {
748
+ throw computerError("Computer control is changing", 409, "COMPUTER_CONTROL_CHANGING");
749
+ }
750
+ if (resource.status !== "running") {
751
+ throw computerError("Computer is not running", 409, "COMPUTER_NOT_RUNNING");
752
+ }
753
+ if (resource.controlOwner !== caller) {
754
+ throw computerError(
755
+ caller === "agent" ? "A human currently controls this Computer" : "The Agent currently controls this Computer",
756
+ 409,
757
+ "COMPUTER_CONTROL_OWNER_MISMATCH"
758
+ );
759
+ }
760
+ if (caller === "agent" && resource.needsObserve && !READ_ONLY_TOOLS.has(tool)) {
761
+ throw computerError(
762
+ "Observe the Computer after human control before sending another action",
763
+ 409,
764
+ "COMPUTER_OBSERVE_REQUIRED"
765
+ );
766
+ }
767
+ const admittedGeneration = resource.generation;
768
+ const admittedEpoch = resource.controlEpoch;
769
+ return this.enqueue(id, async () => {
770
+ const current = this.privateResource(id);
771
+ if (current.generation !== admittedGeneration || current.controlEpoch !== admittedEpoch || current.status !== "running" || current.controlOwner !== caller) {
772
+ throw computerError("Computer ownership changed before this action ran", 409, "COMPUTER_STALE_ADMISSION");
773
+ }
774
+ const args = { ...input };
775
+ if ("session" in args || this.toolAcceptsSession(tool)) args.session = current.sessionId;
776
+ if (tool === "start_session") {
777
+ return { content: [{ type: "text", text: `Farming manages session ${current.sessionId}` }] };
778
+ }
779
+ if (tool === "end_session") {
780
+ return { content: [{ type: "text", text: "Farming keeps this Computer session alive until Stop." }] };
781
+ }
782
+ const screenshotPath = SCREENSHOT_TOOLS.has(tool) ? `/tmp/farming-${current.id}-${crypto.randomBytes(5).toString("hex")}.png` : "";
783
+ let result;
784
+ try {
785
+ result = await this.driverCall(current, tool, args, screenshotPath);
786
+ } catch (caught) {
787
+ const error = caught;
788
+ if (error.killed || error.signal || /timed out/i.test(error.message || "")) {
789
+ this.patch(id, { needsObserve: true, error: `Uncertain ${tool} outcome; observe before retrying` });
790
+ throw computerError(
791
+ `Computer action ${tool} timed out with an uncertain outcome; observe before retrying`,
792
+ 504,
793
+ "COMPUTER_ACTION_UNCERTAIN",
794
+ { uncertain: true }
795
+ );
796
+ }
797
+ throw error;
798
+ }
799
+ const latest = this.privateResource(id);
800
+ if (caller === "agent" && STATE_OBSERVATION_TOOLS.has(tool) && latest.needsObserve) {
801
+ this.patch(id, { needsObserve: false, error: "" });
802
+ }
803
+ return result;
804
+ });
805
+ }
806
+ async reconcileAgentLifecycle(agentStates) {
807
+ const agents = new Map(agentStates.map((agent) => [String(agent.id || ""), agent]));
808
+ for (const resource of this.store.list()) {
809
+ const owner = agents.get(resource.ownerAgentId);
810
+ if (!owner) {
811
+ await this.delete(resource.id, true);
812
+ continue;
813
+ }
814
+ const operation = String(owner.lifecycleOperation?.type || "");
815
+ const preservesRuntime = ["permission-restart", "runtime-switch"].includes(operation);
816
+ const stopped = owner.archived === true || !preservesRuntime && INACTIVE_AGENT_STATUSES.has(String(owner.status || ""));
817
+ if (stopped && resource.status !== "stopped") {
818
+ await this.stop(resource.id, true);
819
+ }
820
+ }
821
+ }
822
+ async stopAll() {
823
+ for (const resource of this.store.list()) {
824
+ if (resource.status !== "stopped" || resource.containerId) {
825
+ await this.stop(resource.id, true);
826
+ }
827
+ }
828
+ }
829
+ async resetAllContainers() {
830
+ for (const candidate of this.store.list()) {
831
+ this.holdStopAdmission(candidate.id);
832
+ try {
833
+ await this.stop(candidate.id, true);
834
+ await this.enqueue(candidate.id, async () => {
835
+ const resource = this.privateResource(candidate.id);
836
+ if (resource.containerId) {
837
+ await this.inspectOwnedContainer(resource);
838
+ await this.docker(["rm", resource.containerId], { timeoutMs: 3e4 });
839
+ }
840
+ this.patch(resource.id, {
841
+ containerId: "",
842
+ containerName: "",
843
+ vncPassword: "",
844
+ viewerPort: 0,
845
+ sessionId: "",
846
+ status: "stopped",
847
+ error: ""
848
+ });
849
+ });
850
+ } finally {
851
+ this.releaseStopAdmission(candidate.id);
852
+ }
853
+ }
854
+ }
855
+ viewerConfig(id) {
856
+ const resource = this.privateResource(id);
857
+ if (resource.status !== "running" || !resource.viewerPort) {
858
+ throw computerError("Computer Viewer is not running", 409, "COMPUTER_NOT_RUNNING");
859
+ }
860
+ return {
861
+ host: "127.0.0.1",
862
+ port: resource.viewerPort,
863
+ password: resource.vncPassword,
864
+ viewOnly: resource.controlOwner !== "human",
865
+ generation: resource.generation,
866
+ controlEpoch: resource.controlEpoch
867
+ };
868
+ }
869
+ attachViewer(id, client) {
870
+ const config = this.viewerConfig(id);
871
+ const upstream = new WebSocket(`ws://127.0.0.1:${config.port}/websockify`);
872
+ const sockets = this.viewerSockets.get(id) || /* @__PURE__ */ new Set();
873
+ sockets.add(client);
874
+ this.viewerSockets.set(id, sockets);
875
+ const close = () => {
876
+ sockets.delete(client);
877
+ if (sockets.size === 0) this.viewerSockets.delete(id);
878
+ if (upstream.readyState === WebSocket.OPEN || upstream.readyState === WebSocket.CONNECTING) {
879
+ upstream.close();
880
+ }
881
+ };
882
+ upstream.on("message", (data, isBinary) => {
883
+ if (client.readyState === WebSocket.OPEN) client.send(data, { binary: isBinary });
884
+ });
885
+ client.on("message", (data, isBinary) => {
886
+ const latest = this.privateResource(id);
887
+ if (latest.generation !== config.generation || latest.controlEpoch !== config.controlEpoch) {
888
+ client.close(4003, "Computer Viewer is stale");
889
+ return;
890
+ }
891
+ if (upstream.readyState === WebSocket.OPEN) upstream.send(data, { binary: isBinary });
892
+ });
893
+ upstream.on("close", () => client.close(4e3, "Computer Viewer closed"));
894
+ upstream.on("error", () => client.close(4005, "Computer Viewer failed"));
895
+ client.on("close", close);
896
+ client.on("error", close);
897
+ }
898
+ closeViewers(id, code, reason) {
899
+ const sockets = this.viewerSockets.get(id);
900
+ if (!sockets) return;
901
+ this.viewerSockets.delete(id);
902
+ for (const socket of sockets) socket.close(code, reason);
903
+ }
904
+ containerName(resource) {
905
+ return safeNamePart(`farming-computer-${this.configFingerprint}-${resource.id.slice(-12)}`);
906
+ }
907
+ containerLabels(resource) {
908
+ return [
909
+ ["farming.dev/kind", "computer"],
910
+ ["farming.dev/config", this.configFingerprint],
911
+ ["farming.dev/resource", resource.id],
912
+ ["farming.dev/owner-agent", resource.ownerAgentId],
913
+ ["farming.dev/image-digest", import_computer_constants.COMPUTER_IMAGE_INDEX_DIGEST]
914
+ ];
915
+ }
916
+ browserCacheRoot() {
917
+ const root = storageLayout.managedChromiumRootDir(this.configDir);
918
+ fs.mkdirSync(root, { recursive: true, mode: 448 });
919
+ return root;
920
+ }
921
+ hasBrowserRuntimeWiring(inspect) {
922
+ const mounts = Array.isArray(inspect.Mounts) ? inspect.Mounts : [];
923
+ return mounts.some(
924
+ (mount) => recordValue(mount).Destination === COMPUTER_BROWSER_MOUNT && recordValue(mount).RW === false
925
+ ) && Boolean(recordValue(recordValue(inspect.HostConfig).PortBindings)[COMPUTER_BROWSER_CDP_PORT]);
926
+ }
927
+ async ensureBrowserCacheMount(id) {
928
+ const resource = this.privateResource(id);
929
+ if (!resource.containerId) return;
930
+ const inspect = await this.inspectOwnedContainer(resource);
931
+ if (this.hasBrowserRuntimeWiring(inspect)) return;
932
+ await this.resetContainer(id);
933
+ }
934
+ async resetContainer(id) {
935
+ await this.stop(id, true);
936
+ this.holdStopAdmission(id);
937
+ try {
938
+ await this.enqueue(id, async () => {
939
+ const resource = this.privateResource(id);
940
+ if (resource.containerId) {
941
+ await this.inspectOwnedContainer(resource);
942
+ await this.docker(["rm", resource.containerId], { timeoutMs: 3e4 });
943
+ }
944
+ this.patch(resource.id, {
945
+ containerId: "",
946
+ containerName: "",
947
+ vncPassword: "",
948
+ viewerPort: 0,
949
+ sessionId: "",
950
+ status: "stopped",
951
+ error: ""
952
+ });
953
+ });
954
+ } finally {
955
+ this.releaseStopAdmission(id);
956
+ }
957
+ }
958
+ browserExecutableInContainer(executablePath) {
959
+ const root = this.browserCacheRoot();
960
+ const resolved = path.resolve(String(executablePath || "").trim());
961
+ const relative = path.relative(root, resolved);
962
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
963
+ throw computerError(
964
+ "Computer Chromium must come from Farming managed runtime storage",
965
+ 400,
966
+ "COMPUTER_BROWSER_EXECUTABLE_INVALID"
967
+ );
968
+ }
969
+ return `${COMPUTER_BROWSER_MOUNT}/${relative.split(path.sep).join("/")}`;
970
+ }
971
+ ensureBrowserCacheTraversal(executablePath) {
972
+ const root = path.resolve(this.browserCacheRoot());
973
+ let current = path.dirname(path.resolve(String(executablePath || "").trim()));
974
+ while (current === root || pathInside(root, current)) {
975
+ const mode = fs.statSync(current).mode & 511;
976
+ if ((mode & 9) !== 9) fs.chmodSync(current, mode | 9);
977
+ if (current === root) return;
978
+ current = path.dirname(current);
979
+ }
980
+ throw computerError(
981
+ "Computer Chromium must come from Farming managed runtime storage",
982
+ 400,
983
+ "COMPUTER_BROWSER_EXECUTABLE_INVALID"
984
+ );
985
+ }
986
+ async waitForContainer(containerId, command, timeoutMs = START_TIMEOUT_MS) {
987
+ const deadline = Date.now() + timeoutMs;
988
+ while (Date.now() < deadline) {
989
+ try {
990
+ await this.docker(["exec", containerId, ...command], { timeoutMs: 3e3 });
991
+ return true;
992
+ } catch {
993
+ await new Promise((resolve) => setTimeout(resolve, 250));
994
+ }
995
+ }
996
+ return false;
997
+ }
998
+ async inspectOwnedContainer(resource) {
999
+ if (!resource.containerId) {
1000
+ throw computerError("Computer container identity is missing", 409, "COMPUTER_CONTAINER_IDENTITY_MISSING");
1001
+ }
1002
+ const result = await this.docker(["inspect", resource.containerId], { timeoutMs: 1e4 });
1003
+ const parsed = JSON.parse(result.stdout);
1004
+ const inspect = Array.isArray(parsed) ? recordValue(parsed[0]) : {};
1005
+ if (!inspect.Id || String(inspect.Id) !== resource.containerId) {
1006
+ throw computerError("Docker returned a different Computer container identity", 409, "COMPUTER_CONTAINER_MISMATCH");
1007
+ }
1008
+ const labels = recordValue(recordValue(inspect.Config).Labels);
1009
+ for (const [key, value] of this.containerLabels(resource)) {
1010
+ if (labels[key] !== value) {
1011
+ throw computerError(`Computer container ownership label mismatch: ${key}`, 409, "COMPUTER_CONTAINER_OWNER_MISMATCH");
1012
+ }
1013
+ }
1014
+ return inspect;
1015
+ }
1016
+ sessionId(resource) {
1017
+ return `${resource.id}-g${resource.generation}`;
1018
+ }
1019
+ async ensureDriver(resource, sessionId) {
1020
+ await this.docker([
1021
+ "exec",
1022
+ "-u",
1023
+ import_computer_constants.COMPUTER_USER,
1024
+ "-e",
1025
+ "HOME=/home/cua",
1026
+ resource.containerId,
1027
+ import_computer_constants.COMPUTER_DRIVER_BIN,
1028
+ "telemetry",
1029
+ "disable"
1030
+ ], { timeoutMs: 1e4 }).catch(() => null);
1031
+ await this.docker([
1032
+ "exec",
1033
+ "-d",
1034
+ "-u",
1035
+ import_computer_constants.COMPUTER_USER,
1036
+ "-e",
1037
+ "HOME=/home/cua",
1038
+ "-e",
1039
+ "DISPLAY=:1",
1040
+ resource.containerId,
1041
+ import_computer_constants.COMPUTER_DRIVER_BIN,
1042
+ "serve",
1043
+ "--dangerously-bypass-approvals"
1044
+ ], { timeoutMs: 1e4 }).catch(() => null);
1045
+ const deadline = Date.now() + START_TIMEOUT_MS;
1046
+ let lastError = null;
1047
+ while (Date.now() < deadline) {
1048
+ try {
1049
+ const version = await this.docker([
1050
+ "exec",
1051
+ "-u",
1052
+ import_computer_constants.COMPUTER_USER,
1053
+ "-e",
1054
+ "HOME=/home/cua",
1055
+ "-e",
1056
+ "DISPLAY=:1",
1057
+ resource.containerId,
1058
+ import_computer_constants.COMPUTER_DRIVER_BIN,
1059
+ "--version"
1060
+ ], { timeoutMs: 5e3 });
1061
+ if (!version.stdout.includes(import_computer_constants.COMPUTER_DRIVER_VERSION)) {
1062
+ throw new Error(`Expected Cua Driver ${import_computer_constants.COMPUTER_DRIVER_VERSION}, got ${version.stdout.trim()}`);
1063
+ }
1064
+ await this.driverCall(resource, "start_session", {
1065
+ session: sessionId,
1066
+ capture_scope: "desktop"
1067
+ });
1068
+ return;
1069
+ } catch (caught) {
1070
+ lastError = caught;
1071
+ await new Promise((resolve) => setTimeout(resolve, 300));
1072
+ }
1073
+ }
1074
+ throw lastError || new Error("Cua Driver did not become ready");
1075
+ }
1076
+ async driverCall(resource, tool, input, screenshotPath = "") {
1077
+ const args = [
1078
+ "exec",
1079
+ "-u",
1080
+ import_computer_constants.COMPUTER_USER,
1081
+ "-e",
1082
+ "HOME=/home/cua",
1083
+ "-e",
1084
+ "DISPLAY=:1",
1085
+ resource.containerId,
1086
+ import_computer_constants.COMPUTER_DRIVER_BIN,
1087
+ "call",
1088
+ tool,
1089
+ JSON.stringify(input),
1090
+ ...screenshotPath ? ["--screenshot-out-file", screenshotPath] : []
1091
+ ];
1092
+ const result = await this.docker(args, {
1093
+ timeoutMs: DRIVER_CALL_TIMEOUT_MS,
1094
+ maxBuffer: 32 * 1024 * 1024
1095
+ });
1096
+ let structuredContent;
1097
+ const text = result.stdout.trim();
1098
+ try {
1099
+ structuredContent = JSON.parse(text);
1100
+ } catch {
1101
+ structuredContent = void 0;
1102
+ }
1103
+ const content = [{
1104
+ type: "text",
1105
+ text: text || result.stderr.trim() || `${tool} completed`
1106
+ }];
1107
+ if (screenshotPath) {
1108
+ try {
1109
+ await this.docker([
1110
+ "exec",
1111
+ "-u",
1112
+ import_computer_constants.COMPUTER_USER,
1113
+ resource.containerId,
1114
+ "test",
1115
+ "-s",
1116
+ screenshotPath
1117
+ ], { timeoutMs: 5e3 });
1118
+ const image = await this.docker([
1119
+ "exec",
1120
+ "-u",
1121
+ import_computer_constants.COMPUTER_USER,
1122
+ resource.containerId,
1123
+ "base64",
1124
+ "-w",
1125
+ "0",
1126
+ screenshotPath
1127
+ ], { timeoutMs: 1e4, maxBuffer: 24 * 1024 * 1024 });
1128
+ if (image.stdout.trim()) content.push({
1129
+ type: "image",
1130
+ data: image.stdout.trim(),
1131
+ mimeType: "image/png"
1132
+ });
1133
+ } catch {
1134
+ } finally {
1135
+ await this.docker([
1136
+ "exec",
1137
+ "-u",
1138
+ import_computer_constants.COMPUTER_USER,
1139
+ resource.containerId,
1140
+ "rm",
1141
+ "-f",
1142
+ screenshotPath
1143
+ ], { timeoutMs: 5e3 }).catch(() => null);
1144
+ }
1145
+ }
1146
+ return {
1147
+ content,
1148
+ ...structuredContent === void 0 ? {} : { structuredContent }
1149
+ };
1150
+ }
1151
+ toolAcceptsSession(tool) {
1152
+ return ![
1153
+ "check_for_update",
1154
+ "check_permissions",
1155
+ "get_config",
1156
+ "health_report",
1157
+ "install_ffmpeg",
1158
+ "list_apps",
1159
+ "list_windows",
1160
+ "set_config",
1161
+ "start_recording",
1162
+ "stop_recording",
1163
+ "get_recording_state",
1164
+ "replay_trajectory"
1165
+ ].includes(tool);
1166
+ }
1167
+ holdStopAdmission(id) {
1168
+ this.stopAdmissions.set(id, (this.stopAdmissions.get(id) || 0) + 1);
1169
+ }
1170
+ releaseStopAdmission(id) {
1171
+ const next = (this.stopAdmissions.get(id) || 1) - 1;
1172
+ if (next <= 0) this.stopAdmissions.delete(id);
1173
+ else this.stopAdmissions.set(id, next);
1174
+ }
1175
+ isStopping(id) {
1176
+ return (this.stopAdmissions.get(id) || 0) > 0;
1177
+ }
1178
+ holdControlAdmission(id) {
1179
+ this.controlAdmissions.set(id, (this.controlAdmissions.get(id) || 0) + 1);
1180
+ }
1181
+ releaseControlAdmission(id) {
1182
+ const next = (this.controlAdmissions.get(id) || 1) - 1;
1183
+ if (next <= 0) this.controlAdmissions.delete(id);
1184
+ else this.controlAdmissions.set(id, next);
1185
+ }
1186
+ isControlChanging(id) {
1187
+ return (this.controlAdmissions.get(id) || 0) > 0;
1188
+ }
1189
+ assertTransitionAdmissionOpen(id) {
1190
+ if (this.isStopping(id)) {
1191
+ throw computerError("Computer is stopping", 409, "COMPUTER_STOPPING");
1192
+ }
1193
+ if (this.isControlChanging(id)) {
1194
+ throw computerError("Computer control is changing", 409, "COMPUTER_CONTROL_CHANGING");
1195
+ }
1196
+ }
1197
+ enqueue(id, action) {
1198
+ const previous = this.operations.get(id) || Promise.resolve();
1199
+ const next = previous.catch(() => null).then(action);
1200
+ this.operations.set(id, next);
1201
+ return next.finally(() => {
1202
+ if (this.operations.get(id) === next) this.operations.delete(id);
1203
+ });
1204
+ }
1205
+ patch(id, patch, privateResult = false) {
1206
+ const resource = this.store.patch(id, patch);
1207
+ this.emitResource(resource);
1208
+ return privateResult ? resource : (0, import_computer_resource_store.publicResource)(resource, this.store.revision);
1209
+ }
1210
+ emitResource(resource) {
1211
+ this.emit("resource", (0, import_computer_resource_store.publicResource)(resource, this.store.revision));
1212
+ this.emit("resources", this.store.snapshot());
1213
+ }
1214
+ }
1215
+ // Annotate the CommonJS export names for ESM import in node:
1216
+ 0 && (module.exports = {
1217
+ ComputerResourceManager,
1218
+ exactImageRef
1219
+ });