agentbox-app 0.1.13 → 0.1.16

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 (278) hide show
  1. package/README.md +24 -9
  2. package/bin/agentbox.mjs +23 -1
  3. package/briefs/message-rules.md +36 -8
  4. package/briefs/worker.md +46 -23
  5. package/main/account-discovery.mjs +122 -30
  6. package/main/account-tooling.mjs +29 -1
  7. package/main/account-usage.mjs +46 -0
  8. package/main/agent-sessions.mjs +282 -268
  9. package/main/agents.mjs +179 -466
  10. package/main/analytics.mjs +12 -2
  11. package/main/answer-modes.mjs +103 -37
  12. package/main/approval-prompt-core.mjs +197 -0
  13. package/main/approval-prompt-server.mjs +4 -138
  14. package/main/approvals.mjs +101 -297
  15. package/main/artifact-path.mjs +47 -18
  16. package/main/claude-input.mjs +48 -4
  17. package/main/claude-plan.mjs +48 -0
  18. package/main/claude-session-open.mjs +28 -0
  19. package/main/claude-usage-process.mjs +40 -0
  20. package/main/claude-usage.mjs +18 -40
  21. package/main/cloud/account.mjs +179 -0
  22. package/main/cloud/aws.mjs +127 -0
  23. package/main/cloud/browser-return.mjs +58 -0
  24. package/main/cloud/clients.mjs +15 -0
  25. package/main/cloud/codex-cloud.mjs +53 -0
  26. package/main/cloud/config.mjs +107 -0
  27. package/main/cloud/docs.mjs +219 -0
  28. package/main/cloud/exec.mjs +27 -0
  29. package/main/cloud/host.mjs +165 -0
  30. package/main/cloud/kit.mjs +80 -0
  31. package/main/cloud/logins.mjs +543 -0
  32. package/main/cloud/managed.mjs +115 -0
  33. package/main/cloud/marker.mjs +104 -0
  34. package/main/cloud/org.mjs +53 -0
  35. package/main/cloud/reuse.mjs +208 -0
  36. package/main/cloud/run-cli.mjs +177 -0
  37. package/main/cloud/run.mjs +1934 -0
  38. package/main/cloud/usage.mjs +76 -0
  39. package/main/cloud-permission.mjs +51 -0
  40. package/main/codex-account.mjs +78 -0
  41. package/main/codex-app-server.mjs +276 -409
  42. package/main/codex-approvals.mjs +148 -433
  43. package/main/codex-models.mjs +20 -2
  44. package/main/codex-session.mjs +212 -507
  45. package/main/codex-threads-worker.mjs +12 -0
  46. package/main/codex-threads.mjs +71 -3
  47. package/main/codex-usage-file.mjs +5 -3
  48. package/main/codex-usage.mjs +56 -7
  49. package/main/codex-watch.mjs +17 -3
  50. package/main/codex.mjs +95 -363
  51. package/main/config.mjs +11 -1
  52. package/main/corner-tag.mjs +340 -19
  53. package/main/dev-window.mjs +1 -1
  54. package/main/engine-bin.mjs +232 -282
  55. package/main/engine-setup.mjs +21 -9
  56. package/main/feedback.mjs +3 -25
  57. package/main/folder-pictures.mjs +2 -2
  58. package/main/freeze-recorder-thread.mjs +86 -0
  59. package/main/freeze-recorder.mjs +146 -0
  60. package/main/fresh-user.mjs +20 -9
  61. package/main/git-change-offthread.mjs +17 -3
  62. package/main/git-change-thread.mjs +5 -2
  63. package/main/harnesses/claude.mjs +32 -11
  64. package/main/harnesses/codex.mjs +9 -2
  65. package/main/importable-threads.mjs +64 -6
  66. package/main/ipc.mjs +362 -111
  67. package/main/leftovers.mjs +117 -25
  68. package/main/liquid-glass.mjs +26 -0
  69. package/main/live-replies.mjs +10 -4
  70. package/main/main-went-red.mjs +330 -0
  71. package/main/main.mjs +199 -84
  72. package/main/memory-gate-history.mjs +36 -10
  73. package/main/memory-gate-server.mjs +62 -0
  74. package/main/memory-gate.mjs +14 -1
  75. package/main/menu.mjs +4 -2
  76. package/main/message-priority.mjs +2 -2
  77. package/main/mic.mjs +38 -0
  78. package/main/native/liquid-glass/build/liquid-glass.node +0 -0
  79. package/main/new-chats-watch.mjs +110 -0
  80. package/main/new-user-window.mjs +25 -0
  81. package/main/notify.mjs +24 -8
  82. package/main/phone-alerts.mjs +117 -0
  83. package/main/phone-inbox.mjs +625 -0
  84. package/main/phone-link.mjs +29 -9
  85. package/main/phone-uploads.mjs +476 -0
  86. package/main/picture-shape.mjs +114 -0
  87. package/main/quit-sequence.mjs +143 -0
  88. package/main/refresh-agent-models.mjs +15 -2
  89. package/main/relay-client.mjs +69 -1
  90. package/main/repeats.mjs +10 -4
  91. package/main/row-label.mjs +12 -41
  92. package/main/run-change.mjs +13 -0
  93. package/main/run-ledger.mjs +135 -0
  94. package/main/save-attachments.mjs +41 -0
  95. package/main/serve.mjs +45 -3
  96. package/main/settings.mjs +220 -44
  97. package/main/shell-path.mjs +185 -37
  98. package/main/ship-conflict-brief.mjs +119 -0
  99. package/main/ship-queue.mjs +115 -19
  100. package/main/source-updater.mjs +18 -10
  101. package/main/stage-attachment.mjs +68 -0
  102. package/main/stall-watch.mjs +50 -0
  103. package/main/store/home.mjs +87 -232
  104. package/main/store/paths.mjs +11 -0
  105. package/main/store/teammates-files.mjs +99 -0
  106. package/main/store/work-items.mjs +231 -13
  107. package/main/store.mjs +226 -52
  108. package/main/supervisor.mjs +3103 -341
  109. package/main/task-folders.mjs +430 -30
  110. package/main/task-terminals.mjs +110 -34
  111. package/main/team/index.mjs +114 -10
  112. package/main/team/memory-cloud.mjs +183 -1
  113. package/main/team/room-hours.mjs +48 -0
  114. package/main/team/room-setup.mjs +31 -0
  115. package/main/team/rooms-memory.mjs +454 -0
  116. package/main/team/rooms-shape.mjs +42 -0
  117. package/main/team/rooms-supabase.mjs +324 -0
  118. package/main/team/rooms.mjs +575 -0
  119. package/main/team/session.mjs +96 -26
  120. package/main/team/supabase-backend.mjs +180 -0
  121. package/main/team/sync.mjs +109 -1
  122. package/main/teammates.mjs +580 -0
  123. package/main/terminal-host.mjs +143 -0
  124. package/main/terminal-hosts.mjs +261 -0
  125. package/main/transcript-search.mjs +80 -0
  126. package/main/webview-guard.mjs +45 -0
  127. package/main/worktree-include.mjs +90 -219
  128. package/mcp/core/documents.mjs +3 -1
  129. package/mcp/core/repeats.mjs +88 -0
  130. package/mcp/core/teammates.mjs +222 -0
  131. package/mcp/core/work.mjs +76 -4
  132. package/mcp/stdio.mjs +24 -11
  133. package/mcp/tools.mjs +116 -8
  134. package/package.json +21 -5
  135. package/renderer/dist/assets/{abnfDiagram-YUKMZFIV-DPPuMWt-.js → abnfDiagram-YUKMZFIV-CoWB6YUK.js} +1 -1
  136. package/renderer/dist/assets/{arc-HmFfdseH.js → arc-Ch0NrCul.js} +1 -1
  137. package/renderer/dist/assets/{architectureDiagram-47P4ROYG-DxJ3zHrH.js → architectureDiagram-47P4ROYG-CXz9M5tQ.js} +1 -1
  138. package/renderer/dist/assets/{blockDiagram-E7TT5TSB-CbTXzOqQ.js → blockDiagram-E7TT5TSB-DchGUuHH.js} +1 -1
  139. package/renderer/dist/assets/{c4Diagram-CRA5TL53-X5ItFyLn.js → c4Diagram-CRA5TL53-Cip-zNyr.js} +1 -1
  140. package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
  141. package/renderer/dist/assets/{chunk-2Q5K7J3B-DC12Ho_L.js → chunk-2Q5K7J3B-BoXid_2r.js} +1 -1
  142. package/renderer/dist/assets/{chunk-5VM5RSS4-ClVtfPd0.js → chunk-5VM5RSS4-Cpez1rRz.js} +1 -1
  143. package/renderer/dist/assets/{chunk-7TKQ45FW-DgcOm0HT.js → chunk-7TKQ45FW-DKA84YkE.js} +1 -1
  144. package/renderer/dist/assets/{chunk-AW2ZBBNX-C01s4yoF.js → chunk-AW2ZBBNX-Dg-SumPG.js} +1 -1
  145. package/renderer/dist/assets/{chunk-FVRAUYC3-Bj0zxhR4.js → chunk-FVRAUYC3-06TCvPm1.js} +2 -2
  146. package/renderer/dist/assets/{chunk-HTAEGDNF-DWDeEhLU.js → chunk-HTAEGDNF-CMEBy1Td.js} +1 -1
  147. package/renderer/dist/assets/{chunk-JWPE2WC7-Dsqwv3eW.js → chunk-JWPE2WC7-BJ2ObmV-.js} +1 -1
  148. package/renderer/dist/assets/{chunk-KQW6MTUR-CE86ANL2.js → chunk-KQW6MTUR-CGWOYXUE.js} +1 -1
  149. package/renderer/dist/assets/{chunk-XXDRQBXY-CoNTASfs.js → chunk-XXDRQBXY-CiZp07XC.js} +1 -1
  150. package/renderer/dist/assets/{classDiagram-v2-K4WV4PDN-2wprLqtw.js → classDiagram-v2-K4WV4PDN-DJNIX719.js} +1 -1
  151. package/renderer/dist/assets/cornerTag-CPgvu-q3.js +1 -0
  152. package/renderer/dist/assets/cornerTag-oMfEjzCO.css +1 -0
  153. package/renderer/dist/assets/{cose-bilkent-JH36ORCC-enqMiYbn.js → cose-bilkent-JH36ORCC-CbkyZOUH.js} +1 -1
  154. package/renderer/dist/assets/{cynefin-EF2NZ3EQ-DHngLxvQ.js → cynefin-EF2NZ3EQ-78iOroZO.js} +1 -1
  155. package/renderer/dist/assets/{cynefinDiagram-3GCD6N5R-C0rZZIl3.js → cynefinDiagram-3GCD6N5R-GcCopMs7.js} +1 -1
  156. package/renderer/dist/assets/{dagre-W4DXFKR2-DQnaEWO7.js → dagre-W4DXFKR2-DHLlXLkz.js} +1 -1
  157. package/renderer/dist/assets/{diagram-2UJZ2QOL-CS8AlGPx.js → diagram-2UJZ2QOL-DuZSUHcH.js} +1 -1
  158. package/renderer/dist/assets/{diagram-OVF4WLC6-yiDMHIkY.js → diagram-OVF4WLC6-Ckxu3cqH.js} +1 -1
  159. package/renderer/dist/assets/{diagram-PFPMY2P6-XbQKzbBA.js → diagram-PFPMY2P6-CsTw9Ycn.js} +1 -1
  160. package/renderer/dist/assets/{diagram-UMYRVEAY-dhVvlfl_.js → diagram-UMYRVEAY-CTXd8qVg.js} +1 -1
  161. package/renderer/dist/assets/{diagram-YEKJPTXX-DZHe8SCf.js → diagram-YEKJPTXX-CJs1T_9m.js} +1 -1
  162. package/renderer/dist/assets/{diagram-ZIFT7M5P-B5h6aXVR.js → diagram-ZIFT7M5P-CvjI23mf.js} +1 -1
  163. package/renderer/dist/assets/{ebnfDiagram-VR2GEFS7-PR9ScDco.js → ebnfDiagram-VR2GEFS7-d_IeAzOJ.js} +1 -1
  164. package/renderer/dist/assets/{elk-IJKZMXRS-Cg-yQXPd.js → elk-IJKZMXRS-zo9KFP3N.js} +1 -1
  165. package/renderer/dist/assets/{erDiagram-O2IAPWRE-omOrJMxu.js → erDiagram-O2IAPWRE-DqfYf5Dt.js} +1 -1
  166. package/renderer/dist/assets/{flowDiagram-OXPTDLAJ-aOX2hzvT.js → flowDiagram-OXPTDLAJ-DAWsMLmo.js} +1 -1
  167. package/renderer/dist/assets/{ganttDiagram-R7TSEDQI-B1H3pWyI.js → ganttDiagram-R7TSEDQI-4ml_bhwR.js} +1 -1
  168. package/renderer/dist/assets/{gitGraphDiagram-XJZIOB7I-Cvw304HC.js → gitGraphDiagram-XJZIOB7I-B_WcqSId.js} +1 -1
  169. package/renderer/dist/assets/{infoDiagram-5W2HQ5XZ-_ZXcNViH.js → infoDiagram-5W2HQ5XZ-CI-jYqzc.js} +1 -1
  170. package/renderer/dist/assets/{ishikawaDiagram-K3B6WC7H-C7Mx_qQk.js → ishikawaDiagram-K3B6WC7H-B56VWRlV.js} +1 -1
  171. package/renderer/dist/assets/{journeyDiagram-COXZFDF6-fr62QoAt.js → journeyDiagram-COXZFDF6-D1-p09Lr.js} +1 -1
  172. package/renderer/dist/assets/{kanban-definition-P3RFRI5V-J8zsabCe.js → kanban-definition-P3RFRI5V-Cv3w7JoN.js} +1 -1
  173. package/renderer/dist/assets/{linear-BmNdEtIi.js → linear-C35V2Spi.js} +1 -1
  174. package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
  175. package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
  176. package/renderer/dist/assets/{mermaid.core-CnYzvFkz.js → mermaid.core-BzGT5Vo9.js} +4 -4
  177. package/renderer/dist/assets/{mindmap-definition-LZFPQGKD-Bfcxc7PM.js → mindmap-definition-LZFPQGKD-BZUfuymC.js} +1 -1
  178. package/renderer/dist/assets/{pegDiagram-BGZESJAR-CwUJOq6X.js → pegDiagram-BGZESJAR-DFh9XNGe.js} +1 -1
  179. package/renderer/dist/assets/{pieDiagram-CAPJLFHJ-BrwMmqfC.js → pieDiagram-CAPJLFHJ-C65tPVsy.js} +1 -1
  180. package/renderer/dist/assets/{product-name-CTKLYNWY.js → product-name-CSNW6d7s.js} +1 -1
  181. package/renderer/dist/assets/{quadrantDiagram-GDMTTRAM-BN4Stoj-.js → quadrantDiagram-GDMTTRAM-Cushj_nk.js} +1 -1
  182. package/renderer/dist/assets/{railroadDiagram-SM67HX2B-CPZ5bbeR.js → railroadDiagram-SM67HX2B-DZGebGx5.js} +1 -1
  183. package/renderer/dist/assets/{requirementDiagram-X7JNWC4A-CgRnimVe.js → requirementDiagram-X7JNWC4A-DpAB0MNo.js} +1 -1
  184. package/renderer/dist/assets/{sankeyDiagram-UM26HJRW-Bt2rej__.js → sankeyDiagram-UM26HJRW-D-29rHra.js} +1 -1
  185. package/renderer/dist/assets/{sequenceDiagram-ZO4K6R2Y-Dd5nKe9G.js → sequenceDiagram-ZO4K6R2Y-FfOQAZol.js} +1 -1
  186. package/renderer/dist/assets/{sizeCapture-INFHLROL-usyNyXyh.js → sizeCapture-INFHLROL-C_n6fDBY.js} +1 -1
  187. package/renderer/dist/assets/{stateDiagram-v2-TBUQTH76-CRzUBb0u.js → stateDiagram-v2-TBUQTH76-BZjenzJG.js} +1 -1
  188. package/renderer/dist/assets/{swimlanes-N4OXWK64-C1GJvMS0.js → swimlanes-N4OXWK64-DxaMU9vX.js} +1 -1
  189. package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
  190. package/renderer/dist/assets/{timeline-definition-YOQAKGHF-caeyPlcc.js → timeline-definition-YOQAKGHF-DTg5HJKz.js} +1 -1
  191. package/renderer/dist/assets/{usecaseDiagram-VIAY4XPW-BN_SV6PC.js → usecaseDiagram-VIAY4XPW-Bc3MZDsl.js} +1 -1
  192. package/renderer/dist/assets/{vennDiagram-BLWOH2XV-BLGuaUX-.js → vennDiagram-BLWOH2XV-GIVGk9Id.js} +1 -1
  193. package/renderer/dist/assets/{wardleyDiagram-YMQ3BMBF-Da0cJeNc.js → wardleyDiagram-YMQ3BMBF-NmJnbQOg.js} +1 -1
  194. package/renderer/dist/assets/{xychartDiagram-TAQBALBS-cGfUffMk.js → xychartDiagram-TAQBALBS-p-kfGfnW.js} +1 -1
  195. package/renderer/dist/corner-tag.html +3 -3
  196. package/renderer/dist/index.html +3 -3
  197. package/scripts/after-install.mjs +30 -8
  198. package/scripts/cloud-shim.pl +57 -0
  199. package/shared/account-names.d.mts +6 -0
  200. package/shared/account-names.mjs +15 -0
  201. package/shared/agent-hours.mjs +136 -0
  202. package/shared/agent-import.mjs +60 -34
  203. package/shared/agents.mjs +126 -522
  204. package/shared/analytics-events.mjs +12 -0
  205. package/shared/approval-card.mjs +36 -0
  206. package/shared/bridge-map.mjs +27 -1
  207. package/shared/claude-code-shape.mjs +6 -0
  208. package/shared/claude-usage.d.mts +1 -0
  209. package/shared/claude-usage.mjs +42 -1
  210. package/shared/cloud-hand-back.d.mts +7 -0
  211. package/shared/cloud-hand-back.mjs +20 -0
  212. package/shared/cloud-kit.d.mts +219 -0
  213. package/shared/cloud-kit.mjs +341 -0
  214. package/shared/cloud-login-name.mjs +4 -0
  215. package/shared/cloud-start.mjs +17 -0
  216. package/shared/cloud-versions.mjs +23 -0
  217. package/shared/codex-import.mjs +2 -2
  218. package/shared/codex-modes.d.mts +22 -9
  219. package/shared/codex-modes.mjs +67 -11
  220. package/shared/codex-usage.mjs +108 -2
  221. package/shared/corner-tag.d.mts +23 -1
  222. package/shared/corner-tag.mjs +284 -29
  223. package/shared/dead-run-trace.mjs +19 -2
  224. package/shared/engines.mjs +70 -309
  225. package/shared/feedback.d.mts +7 -0
  226. package/shared/feedback.mjs +25 -0
  227. package/shared/first-run-practice.mjs +17 -24
  228. package/shared/harness-definitions.mjs +8 -0
  229. package/shared/instruction-defaults.json +2 -2
  230. package/shared/key-caps.d.mts +14 -0
  231. package/shared/key-caps.mjs +68 -0
  232. package/shared/message-edits.d.mts +7 -0
  233. package/shared/message-edits.mjs +35 -0
  234. package/shared/move-thread.d.mts +24 -0
  235. package/shared/move-thread.mjs +112 -0
  236. package/shared/named-threads.mjs +72 -0
  237. package/shared/not-shipped.d.mts +22 -0
  238. package/shared/not-shipped.mjs +89 -0
  239. package/shared/notify-rules.d.mts +6 -0
  240. package/shared/notify-rules.mjs +36 -8
  241. package/shared/phone-files.mjs +28 -0
  242. package/shared/phone-settings.mjs +118 -0
  243. package/shared/phone-snapshot.d.mts +16 -0
  244. package/shared/phone-snapshot.mjs +348 -0
  245. package/shared/picture-formats.d.mts +1 -0
  246. package/shared/picture-formats.mjs +13 -0
  247. package/shared/rank.d.mts +2 -1
  248. package/shared/rank.mjs +32 -19
  249. package/shared/repeats.d.mts +8 -0
  250. package/shared/repeats.mjs +33 -2
  251. package/shared/room-cards.mjs +167 -0
  252. package/shared/room-links.d.mts +4 -0
  253. package/shared/room-links.mjs +21 -0
  254. package/shared/runs-on.d.mts +3 -3
  255. package/shared/runs-on.mjs +8 -5
  256. package/shared/shipped-note.d.mts +4 -0
  257. package/shared/shipped-note.mjs +6 -0
  258. package/shared/shortcuts.mjs +14 -6
  259. package/shared/spawn-trouble.d.mts +4 -0
  260. package/shared/spawn-trouble.mjs +443 -40
  261. package/shared/team-rules.mjs +9 -2
  262. package/shared/teammates.d.mts +19 -0
  263. package/shared/teammates.mjs +756 -0
  264. package/shared/terminal-focus.d.mts +1 -0
  265. package/shared/terminal-focus.mjs +13 -0
  266. package/shared/thread-cards.mjs +4 -1
  267. package/shared/usage.d.mts +9 -0
  268. package/shared/usage.mjs +49 -3
  269. package/shared/websites.d.mts +15 -0
  270. package/shared/websites.mjs +61 -0
  271. package/shared/work-items.mjs +127 -30
  272. package/shared/work-lines.mjs +92 -244
  273. package/renderer/dist/assets/channel-BlT2g9Sz.js +0 -1
  274. package/renderer/dist/assets/cornerTag-BLrxiDE5.css +0 -1
  275. package/renderer/dist/assets/cornerTag-BOT6u3Tn.js +0 -1
  276. package/renderer/dist/assets/main-ChNdnUvt.js +0 -1132
  277. package/renderer/dist/assets/main-nj0QR5kg.css +0 -32
  278. package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cy4Av2_p.js +0 -8
@@ -0,0 +1,165 @@
1
+ // ONE PROCESS ON THIS MAC FOR EVERY CLOUD RUN'S RELAY (main/cloud/run.mjs).
2
+ // The supervisor holds, for each run, a stand-in of a few megabytes
3
+ // (scripts/cloud-shim.pl) that carries the run's input, output, signals and
4
+ // exit code here and back, so to the supervisor a cloud run is still one
5
+ // process.
6
+ //
7
+ // Lines over a socket only this user can reach. The stand-in sends a hello
8
+ // ({ argv, env, cwd, pid }), then `i <b64>` input, `I` end of input,
9
+ // `k <signal>`; this answers `o <b64>` output, `e <b64>` errors, `x <code>`.
10
+ // A stand-in that goes without saying (killed outright) is a run whose app
11
+ // has gone: it is left going in the cloud, as on a quit, and brought home
12
+ // later. With no run for a minute this process ends.
13
+ //
14
+ // ELECTRON_RUN_AS_NODE=1 <electron> main/cloud/host.mjs <socket>
15
+ import fs from 'node:fs';
16
+ import os from 'node:os';
17
+ import net from 'node:net';
18
+ import path from 'node:path';
19
+ import crypto from 'node:crypto';
20
+ import { spawnSync } from 'node:child_process';
21
+ import { EventEmitter } from 'node:events';
22
+ import { PassThrough } from 'node:stream';
23
+ import { fileURLToPath } from 'node:url';
24
+ import { nameSlug } from '../../shared/product-name.mjs';
25
+
26
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
27
+ // The files a host is made of. A host started by an older app serves only the
28
+ // stand-ins of that app: a newer one starts its own beside it.
29
+ const MADE_OF = ['host.mjs', 'run.mjs', 'run-cli.mjs', 'docs.mjs', 'exec.mjs', 'marker.mjs', 'aws.mjs', '../approval-prompt-core.mjs', '../../mcp/stdio.mjs', '../../mcp/tools.mjs'];
30
+ const TRANSPORT_FAULT = 75;
31
+ // The exit of a host that found one of this app's already serving its socket.
32
+ export const HOST_ALREADY_THERE = 3;
33
+ let hosts = 0;
34
+
35
+ // Whether this perl has every module the stand-in uses: some Linux perls ship
36
+ // without JSON::PP.
37
+ export function perlRunsTheShim(perl) {
38
+ try {
39
+ return spawnSync(perl, ['-MIO::Socket::UNIX', '-MIO::Select', '-MMIME::Base64', '-MJSON::PP', '-MCwd', '-MPOSIX', '-e1'], { stdio: 'ignore', timeout: 10_000 }).status === 0;
40
+ } catch { return false; }
41
+ }
42
+
43
+ export function hostStamp() {
44
+ const h = crypto.createHash('sha1');
45
+ for (const f of MADE_OF) { try { h.update(fs.readFileSync(path.join(HERE, f))); } catch { h.update(f); } }
46
+ return h.digest('hex').slice(0, 12);
47
+ }
48
+
49
+ // Short enough for a socket's path (104 bytes on macOS), and this user's own.
50
+ export function hostSocketPath({ stamp = hostStamp(), dir = os.tmpdir(), uid = process.getuid?.() ?? 0 } = {}) {
51
+ return path.join(dir, `${nameSlug}-cloud-${uid}-${stamp}.sock`);
52
+ }
53
+
54
+ /**
55
+ * Serves runs on `sockPath`; `runOne(opts)` is one run, resolving to its exit
56
+ * code. `started` says 'serving', or 'taken' when a host already answers there.
57
+ */
58
+ export function startCloudHost({ sockPath, runOne, idleMs = 60_000, onIdle = () => {}, log = () => {} }) {
59
+ let live = 0;
60
+ let idleTimer = null;
61
+ const idle = () => {
62
+ clearTimeout(idleTimer);
63
+ if (live === 0) { idleTimer = setTimeout(() => { if (live === 0) onIdle(); }, idleMs); idleTimer.unref?.(); }
64
+ };
65
+ const server = net.createServer((sock) => {
66
+ let buf = '';
67
+ let started = false;
68
+ let done = false;
69
+ const stdin = new PassThrough();
70
+ const stdout = new PassThrough();
71
+ const stderr = new PassThrough();
72
+ const signals = new EventEmitter();
73
+ const send = (line) => { if (!sock.destroyed) return sock.write(line + '\n'); return true; };
74
+ // What the run prints goes out as it comes, never faster than the
75
+ // stand-in takes it.
76
+ for (const [stream, tag] of [[stdout, 'o'], [stderr, 'e']]) {
77
+ stream.on('data', (b) => { if (send(`${tag} ${b.toString('base64')}`) === false && !sock.destroyed) { stream.pause(); sock.once('drain', () => stream.resume()); } });
78
+ }
79
+ const finish = (code) => {
80
+ if (done) return;
81
+ done = true;
82
+ let open = 2;
83
+ const both = () => { open -= 1; if (open === 0) { send(`x ${Number.isInteger(code) ? code : TRANSPORT_FAULT}`); sock.end(); live -= 1; idle(); } };
84
+ stdout.once('end', both); stderr.once('end', both);
85
+ stdout.end(); stderr.end();
86
+ stdout.resume(); stderr.resume();
87
+ };
88
+ sock.setEncoding('utf8');
89
+ sock.on('data', (d) => {
90
+ buf += d;
91
+ let n;
92
+ while ((n = buf.indexOf('\n')) >= 0) {
93
+ const line = buf.slice(0, n);
94
+ buf = buf.slice(n + 1);
95
+ if (!started) {
96
+ let hello = null;
97
+ try { hello = JSON.parse(line); } catch { /* not a hello */ }
98
+ if (!Array.isArray(hello?.argv) || typeof hello.cwd !== 'string') { send(`e ${Buffer.from('agentbox cloud: the stand-in sent no run\n').toString('base64')}`); send(`x ${TRANSPORT_FAULT}`); sock.end(); return; }
99
+ started = true;
100
+ live += 1;
101
+ clearTimeout(idleTimer);
102
+ Promise.resolve()
103
+ .then(() => runOne({ args: hello.argv, env: hello.env || {}, cwd: hello.cwd, stdin, stdout, stderr, signals, pid: Number(hello.pid) || undefined, watchParent: false }))
104
+ .then((code) => finish(code), (err) => { stderr.write(`agentbox cloud: stopped: ${err?.message || err}\n`); finish(TRANSPORT_FAULT); });
105
+ continue;
106
+ }
107
+ if (line.startsWith('i ')) stdin.write(Buffer.from(line.slice(2), 'base64'));
108
+ else if (line === 'I') stdin.end();
109
+ else if (line.startsWith('k ') && /^SIG(TERM|INT|HUP)$/.test(line.slice(2))) signals.emit(line.slice(2));
110
+ }
111
+ });
112
+ // Gone without a word: its app is gone, so the run carries on in the
113
+ // cloud and is brought home when the app next opens.
114
+ // Its output then goes nowhere, so a run held waiting for room is let go.
115
+ sock.on('close', () => { stdout.resume(); stderr.resume(); if (started && !done) signals.emit('SIGHUP'); });
116
+ sock.on('error', () => {});
117
+ });
118
+ server.on('error', (err) => log(`cloud host: ${err.message}`));
119
+ // The socket file this host made. Ending, it takes away that one only: a
120
+ // newer host may have put its own at the same path. Node deletes the path a
121
+ // server listened on as it closes, so it listens on a name of its own and
122
+ // moves it into place.
123
+ let mine = null;
124
+ const own = `${sockPath}.${process.pid}${(hosts += 1)}`;
125
+ const ours = () => { try { return mine !== null && fs.statSync(sockPath).ino === mine; } catch { return false; } };
126
+ const started = new Promise((resolve) => {
127
+ const probe = net.connect(sockPath);
128
+ probe.once('connect', () => { probe.destroy(); resolve('taken'); });
129
+ probe.once('error', () => {
130
+ try { fs.unlinkSync(own); } catch { /* not there */ }
131
+ server.listen(own, () => {
132
+ try { fs.chmodSync(own, 0o600); fs.renameSync(own, sockPath); mine = fs.statSync(sockPath).ino; } catch (err) { log(`cloud host: ${err.message}`); }
133
+ idle();
134
+ resolve('serving');
135
+ });
136
+ });
137
+ });
138
+ return {
139
+ server,
140
+ started,
141
+ live: () => live,
142
+ close: () => started.then(() => new Promise((resolve) => {
143
+ clearTimeout(idleTimer);
144
+ if (ours()) { try { fs.unlinkSync(sockPath); } catch { /* gone */ } }
145
+ if (server.listening) server.close(() => resolve()); else resolve();
146
+ })),
147
+ };
148
+ }
149
+
150
+ // Started as a program by the supervisor (Supervisor#_cloudHost).
151
+ if (path.basename(process.argv[1] || '') === 'host.mjs') {
152
+ const sockPath = process.argv[2];
153
+ const { cloudRunFromArgs, dropAwsSignIns } = await import('./run-cli.mjs');
154
+ dropAwsSignIns();
155
+ const host = startCloudHost({
156
+ sockPath,
157
+ runOne: cloudRunFromArgs,
158
+ onIdle: () => host.close().then(() => process.exit(0)),
159
+ log: (m) => process.stderr.write(`${m}\n`),
160
+ });
161
+ if (await host.started === 'taken') process.exit(HOST_ALREADY_THERE);
162
+ // A run's stray error is that run's, not every run's.
163
+ process.on('uncaughtException', (err) => process.stderr.write(`cloud host: ${err?.stack || err}\n`));
164
+ process.on('unhandledRejection', (err) => process.stderr.write(`cloud host: ${err?.stack || err}\n`));
165
+ }
@@ -0,0 +1,80 @@
1
+ // THE COMPUTER'S SIDE OF A KIT (shared/cloud-kit.mjs): where its copy is kept
2
+ // here, and the marker that makes a run the cloud server started from it this
3
+ // thread's own, so the import path joins it or brings it home
4
+ // (main/cloud/run.mjs `joining`, Supervisor#adoptCloudRun).
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { PassThrough } from 'node:stream';
8
+ import { cloudSessionId, APPROVALS_SERVER } from '../../shared/cloud-kit.mjs';
9
+ import { serveApprovals } from '../approval-prompt-core.mjs';
10
+
11
+ const safe = (s) => String(s).replace(/[^A-Za-z0-9_-]/g, '_');
12
+
13
+ /** Where the last kit written for a thread is kept on this computer. */
14
+ export const kitCopyPath = (userDir, product, item) => path.join(userDir, 'cloud-kits', `${safe(product)}--${safe(item)}.json`);
15
+ /** Where the reply's place in the prompt waits for the kit writer, read once. */
16
+ export const kitFramePath = (userDir, product, item) => path.join(userDir, 'cloud-kits', `${safe(product)}--${safe(item)}.frame.json`);
17
+
18
+ export function readKitCopy(file) {
19
+ try {
20
+ const kit = JSON.parse(fs.readFileSync(file, 'utf8'));
21
+ return kit && typeof kit === 'object' && kit.marker && typeof kit.cwd === 'string' ? kit : null;
22
+ } catch { return null; }
23
+ }
24
+
25
+ // None of its events have been read here (`cursor`). Left `detached`, as one
26
+ // the app left going: the import path asks the sandbox which it is now, and
27
+ // numbers this computer's input on from what the sandbox says it took
28
+ // (`inSeq`), however many replies the server sent. Its input is closed when
29
+ // joined (`unsent`), so the turn it is on ends and its work comes home; her
30
+ // next reply is then an ordinary run here.
31
+ export function markerForCloudRun(kit, runId) {
32
+ return {
33
+ ...kit.marker,
34
+ runId,
35
+ sessionId: cloudSessionId(kit.item, runId),
36
+ offline: true,
37
+ state: 'detached', pid: null, cursor: 0, conns: {},
38
+ unsent: [{ action: 'stdin', end: true }],
39
+ };
40
+ }
41
+
42
+ // PERMISSION REQUESTS WITH THE COMPUTER AWAY (cloud/sandbox/server.mjs
43
+ // `offline.hold`). A run that asks permission names the app's approvals
44
+ // server on its command line; one in a mode that never asks does not need it
45
+ // held.
46
+ export function asksPermission(argv) {
47
+ const args = (Array.isArray(argv) ? argv : []).map(String);
48
+ if (args.includes('--dangerously-skip-permissions')) return false;
49
+ const at = args.indexOf('--permission-prompt-tool');
50
+ if (at < 0 || !String(args[at + 1] ?? '').includes(`__${APPROVALS_SERVER}__`)) return false;
51
+ const mode = args.indexOf('--permission-mode');
52
+ return !(mode >= 0 && args[mode + 1] === 'bypassPermissions');
53
+ }
54
+
55
+ // The approvals server's own answers to a run opening it, read off the server
56
+ // itself, so a run the computer never spoke to opens it in the cloud.
57
+ export async function approvalsHandshakes({ timeoutMs = 5_000 } = {}) {
58
+ const input = new PassThrough();
59
+ const output = new PassThrough();
60
+ const server = serveApprovals({ env: {}, input, output, warn: () => {} });
61
+ const answers = new Map();
62
+ let buf = '';
63
+ const done = new Promise((resolve, reject) => {
64
+ const timer = setTimeout(() => reject(new Error('the approvals server did not answer its handshake')), timeoutMs);
65
+ output.on('data', (d) => {
66
+ buf += d;
67
+ let i;
68
+ while ((i = buf.indexOf('\n')) >= 0) {
69
+ const msg = JSON.parse(buf.slice(0, i));
70
+ buf = buf.slice(i + 1);
71
+ answers.set(msg.id, msg.result);
72
+ if (answers.size === 2) { clearTimeout(timer); resolve(); }
73
+ }
74
+ });
75
+ });
76
+ input.write(`${JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05' } })}\n`);
77
+ input.write(`${JSON.stringify({ jsonrpc: '2.0', id: 2, method: 'tools/list' })}\n`);
78
+ try { await done; } finally { server.close(); }
79
+ return { initialize: answers.get(1), 'tools/list': answers.get(2) };
80
+ }