agentbox-app 0.1.12 → 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 (370) 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/acp.mjs +296 -0
  9. package/main/agent-homes.mjs +158 -0
  10. package/main/agent-sessions.mjs +311 -260
  11. package/main/agent-updates.mjs +9 -22
  12. package/main/agents.mjs +179 -466
  13. package/main/analytics.mjs +42 -2
  14. package/main/answer-modes.mjs +103 -37
  15. package/main/approval-prompt-core.mjs +197 -0
  16. package/main/approval-prompt-server.mjs +4 -138
  17. package/main/approvals.mjs +101 -297
  18. package/main/artifact-path.mjs +47 -18
  19. package/main/claude-bin.mjs +45 -311
  20. package/main/claude-input.mjs +57 -7
  21. package/main/claude-launch-env.mjs +54 -0
  22. package/main/claude-models.mjs +39 -7
  23. package/main/claude-plan.mjs +64 -16
  24. package/main/claude-session-open.mjs +28 -0
  25. package/main/claude-usage-process.mjs +40 -0
  26. package/main/claude-usage.mjs +94 -59
  27. package/main/cloud/account.mjs +179 -0
  28. package/main/cloud/aws.mjs +127 -0
  29. package/main/cloud/browser-return.mjs +58 -0
  30. package/main/cloud/clients.mjs +15 -0
  31. package/main/cloud/codex-cloud.mjs +53 -0
  32. package/main/cloud/config.mjs +107 -0
  33. package/main/cloud/docs.mjs +219 -0
  34. package/main/cloud/exec.mjs +27 -0
  35. package/main/cloud/host.mjs +165 -0
  36. package/main/cloud/kit.mjs +80 -0
  37. package/main/cloud/logins.mjs +543 -0
  38. package/main/cloud/managed.mjs +115 -0
  39. package/main/cloud/marker.mjs +104 -0
  40. package/main/cloud/org.mjs +53 -0
  41. package/main/cloud/reuse.mjs +208 -0
  42. package/main/cloud/run-cli.mjs +177 -0
  43. package/main/cloud/run.mjs +1934 -0
  44. package/main/cloud/usage.mjs +76 -0
  45. package/main/cloud-permission.mjs +51 -0
  46. package/main/codex-account.mjs +78 -0
  47. package/main/codex-app-server.mjs +276 -409
  48. package/main/codex-approvals.mjs +148 -433
  49. package/main/codex-bin.mjs +51 -249
  50. package/main/codex-models.mjs +20 -2
  51. package/main/codex-session.mjs +246 -506
  52. package/main/codex-threads-worker.mjs +12 -0
  53. package/main/codex-threads.mjs +112 -11
  54. package/main/codex-usage-file.mjs +5 -3
  55. package/main/codex-usage.mjs +56 -7
  56. package/main/codex-watch.mjs +83 -5
  57. package/main/codex.mjs +95 -363
  58. package/main/config.mjs +37 -5
  59. package/main/corner-tag.mjs +810 -0
  60. package/main/crash-report.mjs +26 -3
  61. package/main/dev-window.mjs +1 -1
  62. package/main/engine-bin.mjs +545 -0
  63. package/main/engine-setup.mjs +25 -24
  64. package/main/feedback.mjs +14 -31
  65. package/main/folder-pictures.mjs +2 -2
  66. package/main/freeze-recorder-thread.mjs +86 -0
  67. package/main/freeze-recorder.mjs +146 -0
  68. package/main/fresh-user.mjs +20 -9
  69. package/main/git-change-offthread.mjs +17 -3
  70. package/main/git-change-thread.mjs +5 -2
  71. package/main/harnesses/claude-stream.mjs +208 -0
  72. package/main/harnesses/claude.mjs +105 -0
  73. package/main/harnesses/codex.mjs +72 -0
  74. package/main/harnesses/common.mjs +78 -0
  75. package/main/harnesses/instructions-file.mjs +116 -0
  76. package/main/harnesses/opencode.mjs +234 -0
  77. package/main/harnesses.mjs +65 -0
  78. package/main/importable-threads.mjs +96 -0
  79. package/main/ipc.mjs +520 -135
  80. package/main/leftovers.mjs +117 -25
  81. package/main/liquid-glass.mjs +26 -0
  82. package/main/live-replies.mjs +10 -4
  83. package/main/local-app-frames.mjs +82 -0
  84. package/main/main-went-red.mjs +330 -0
  85. package/main/main.mjs +248 -37
  86. package/main/memory-gate-history.mjs +85 -20
  87. package/main/memory-gate-server.mjs +129 -11
  88. package/main/memory-gate.mjs +62 -8
  89. package/main/memory-log.mjs +124 -0
  90. package/main/menu.mjs +4 -2
  91. package/main/message-priority.mjs +2 -2
  92. package/main/mic.mjs +38 -0
  93. package/main/native/liquid-glass/build/liquid-glass.node +0 -0
  94. package/main/new-chats-watch.mjs +110 -0
  95. package/main/new-user-window.mjs +25 -0
  96. package/main/notify.mjs +64 -5
  97. package/main/opencode-server.mjs +271 -0
  98. package/main/opencode.mjs +287 -0
  99. package/main/phone-alerts.mjs +117 -0
  100. package/main/phone-inbox.mjs +625 -0
  101. package/main/phone-link.mjs +571 -0
  102. package/main/phone-uploads.mjs +476 -0
  103. package/main/picture-shape.mjs +114 -0
  104. package/main/quit-sequence.mjs +143 -0
  105. package/main/refresh-agent-models.mjs +15 -2
  106. package/main/relay-address.mjs +28 -0
  107. package/main/relay-client.mjs +314 -0
  108. package/main/repeats.mjs +10 -4
  109. package/main/row-label.mjs +18 -47
  110. package/main/run-change.mjs +13 -0
  111. package/main/run-ledger.mjs +135 -0
  112. package/main/runs-on.mjs +32 -35
  113. package/main/save-attachments.mjs +41 -0
  114. package/main/serve.mjs +155 -9
  115. package/main/settings.mjs +364 -50
  116. package/main/shell-path.mjs +234 -0
  117. package/main/ship-conflict-brief.mjs +119 -0
  118. package/main/ship-queue.mjs +134 -20
  119. package/main/sign-in-files.mjs +3 -7
  120. package/main/source-updater.mjs +45 -11
  121. package/main/stage-attachment.mjs +68 -0
  122. package/main/stall-watch.mjs +50 -0
  123. package/main/start-hold.mjs +73 -0
  124. package/main/store/home.mjs +87 -232
  125. package/main/store/paths.mjs +11 -0
  126. package/main/store/teammates-files.mjs +99 -0
  127. package/main/store/work-items.mjs +231 -13
  128. package/main/store.mjs +227 -53
  129. package/main/supervisor.mjs +3865 -699
  130. package/main/task-commands.mjs +13 -17
  131. package/main/task-folders-offthread.mjs +6 -1
  132. package/main/task-folders.mjs +897 -105
  133. package/main/task-terminals.mjs +147 -41
  134. package/main/team/index.mjs +114 -10
  135. package/main/team/memory-cloud.mjs +183 -1
  136. package/main/team/room-hours.mjs +48 -0
  137. package/main/team/room-setup.mjs +31 -0
  138. package/main/team/rooms-memory.mjs +454 -0
  139. package/main/team/rooms-shape.mjs +42 -0
  140. package/main/team/rooms-supabase.mjs +324 -0
  141. package/main/team/rooms.mjs +575 -0
  142. package/main/team/session.mjs +96 -26
  143. package/main/team/supabase-backend.mjs +180 -0
  144. package/main/team/sync.mjs +109 -1
  145. package/main/teammates.mjs +580 -0
  146. package/main/terminal-host.mjs +143 -0
  147. package/main/terminal-hosts.mjs +261 -0
  148. package/main/transcript-search.mjs +80 -0
  149. package/main/updater.mjs +71 -9
  150. package/main/webview-guard.mjs +45 -0
  151. package/main/worktree-include.mjs +365 -0
  152. package/mcp/core/documents.mjs +3 -1
  153. package/mcp/core/repeats.mjs +88 -0
  154. package/mcp/core/teammates.mjs +222 -0
  155. package/mcp/core/work.mjs +76 -4
  156. package/mcp/stdio.mjs +24 -11
  157. package/mcp/tools.mjs +116 -8
  158. package/package.json +30 -5
  159. package/renderer/dist/assets/abnfDiagram-YUKMZFIV-CoWB6YUK.js +1 -0
  160. package/renderer/dist/assets/arc-Ch0NrCul.js +1 -0
  161. package/renderer/dist/assets/architectureDiagram-47P4ROYG-CXz9M5tQ.js +36 -0
  162. package/renderer/dist/assets/blockDiagram-E7TT5TSB-DchGUuHH.js +140 -0
  163. package/renderer/dist/assets/c4Diagram-CRA5TL53-Cip-zNyr.js +38 -0
  164. package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
  165. package/renderer/dist/assets/chunk-2Q5K7J3B-BoXid_2r.js +1 -0
  166. package/renderer/dist/assets/chunk-5VM5RSS4-Cpez1rRz.js +15 -0
  167. package/renderer/dist/assets/chunk-7TKQ45FW-DKA84YkE.js +1 -0
  168. package/renderer/dist/assets/chunk-AW2ZBBNX-Dg-SumPG.js +88 -0
  169. package/renderer/dist/assets/chunk-FVRAUYC3-06TCvPm1.js +2 -0
  170. package/renderer/dist/assets/chunk-HTAEGDNF-CMEBy1Td.js +1 -0
  171. package/renderer/dist/assets/chunk-JWPE2WC7-BJ2ObmV-.js +1 -0
  172. package/renderer/dist/assets/chunk-KQW6MTUR-CGWOYXUE.js +1 -0
  173. package/renderer/dist/assets/chunk-XXDRQBXY-CiZp07XC.js +1 -0
  174. package/renderer/dist/assets/classDiagram-v2-K4WV4PDN-DJNIX719.js +217 -0
  175. package/renderer/dist/assets/cornerTag-CPgvu-q3.js +1 -0
  176. package/renderer/dist/assets/cornerTag-oMfEjzCO.css +1 -0
  177. package/renderer/dist/assets/cose-bilkent-JH36ORCC-CbkyZOUH.js +1 -0
  178. package/renderer/dist/assets/cynefin-EF2NZ3EQ-78iOroZO.js +141 -0
  179. package/renderer/dist/assets/cynefinDiagram-3GCD6N5R-GcCopMs7.js +62 -0
  180. package/renderer/dist/assets/cytoscape.esm-C5JX3QLV.js +331 -0
  181. package/renderer/dist/assets/dagre-W4DXFKR2-DHLlXLkz.js +4 -0
  182. package/renderer/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  183. package/renderer/dist/assets/diagram-2UJZ2QOL-DuZSUHcH.js +200 -0
  184. package/renderer/dist/assets/diagram-OVF4WLC6-Ckxu3cqH.js +24 -0
  185. package/renderer/dist/assets/diagram-PFPMY2P6-CsTw9Ycn.js +30 -0
  186. package/renderer/dist/assets/diagram-UMYRVEAY-CTXd8qVg.js +24 -0
  187. package/renderer/dist/assets/diagram-YEKJPTXX-CJs1T_9m.js +41 -0
  188. package/renderer/dist/assets/diagram-ZIFT7M5P-CvjI23mf.js +3 -0
  189. package/renderer/dist/assets/ebnfDiagram-VR2GEFS7-d_IeAzOJ.js +1 -0
  190. package/renderer/dist/assets/elk-IJKZMXRS-zo9KFP3N.js +27 -0
  191. package/renderer/dist/assets/erDiagram-O2IAPWRE-DqfYf5Dt.js +99 -0
  192. package/renderer/dist/assets/flowDiagram-OXPTDLAJ-DAWsMLmo.js +213 -0
  193. package/renderer/dist/assets/ganttDiagram-R7TSEDQI-4ml_bhwR.js +292 -0
  194. package/renderer/dist/assets/gitGraphDiagram-XJZIOB7I-B_WcqSId.js +106 -0
  195. package/renderer/dist/assets/graph-DGbsTEsI.js +1 -0
  196. package/renderer/dist/assets/infoDiagram-5W2HQ5XZ-CI-jYqzc.js +2 -0
  197. package/renderer/dist/assets/init-Gi6I4Gst.js +1 -0
  198. package/renderer/dist/assets/ishikawaDiagram-K3B6WC7H-B56VWRlV.js +70 -0
  199. package/renderer/dist/assets/journeyDiagram-COXZFDF6-D1-p09Lr.js +139 -0
  200. package/renderer/dist/assets/kanban-definition-P3RFRI5V-Cv3w7JoN.js +89 -0
  201. package/renderer/dist/assets/katex-C5jXJg4s.js +257 -0
  202. package/renderer/dist/assets/linear-C35V2Spi.js +1 -0
  203. package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
  204. package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
  205. package/renderer/dist/assets/mermaid.core-BzGT5Vo9.js +314 -0
  206. package/renderer/dist/assets/mindmap-definition-LZFPQGKD-BZUfuymC.js +96 -0
  207. package/renderer/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  208. package/renderer/dist/assets/pegDiagram-BGZESJAR-DFh9XNGe.js +1 -0
  209. package/renderer/dist/assets/pieDiagram-CAPJLFHJ-C65tPVsy.js +39 -0
  210. package/renderer/dist/assets/product-name-CSNW6d7s.js +40 -0
  211. package/renderer/dist/assets/quadrantDiagram-GDMTTRAM-Cushj_nk.js +7 -0
  212. package/renderer/dist/assets/railroadDiagram-SM67HX2B-DZGebGx5.js +1 -0
  213. package/renderer/dist/assets/requirementDiagram-X7JNWC4A-DpAB0MNo.js +84 -0
  214. package/renderer/dist/assets/sankeyDiagram-UM26HJRW-D-29rHra.js +40 -0
  215. package/renderer/dist/assets/sequenceDiagram-ZO4K6R2Y-FfOQAZol.js +169 -0
  216. package/renderer/dist/assets/sizeCapture-INFHLROL-C_n6fDBY.js +1 -0
  217. package/renderer/dist/assets/stateDiagram-v2-TBUQTH76-BZjenzJG.js +291 -0
  218. package/renderer/dist/assets/swimlanes-N4OXWK64-DxaMU9vX.js +1 -0
  219. package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
  220. package/renderer/dist/assets/timeline-definition-YOQAKGHF-DTg5HJKz.js +120 -0
  221. package/renderer/dist/assets/usecaseDiagram-VIAY4XPW-Bc3MZDsl.js +370 -0
  222. package/renderer/dist/assets/vennDiagram-BLWOH2XV-GIVGk9Id.js +34 -0
  223. package/renderer/dist/assets/wardleyDiagram-YMQ3BMBF-NmJnbQOg.js +78 -0
  224. package/renderer/dist/assets/xychartDiagram-TAQBALBS-p-kfGfnW.js +7 -0
  225. package/renderer/dist/corner-tag.html +13 -0
  226. package/renderer/dist/emulator/audio-rules.mjs +74 -0
  227. package/renderer/dist/emulator/data/compression/README.md +11 -0
  228. package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
  229. package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
  230. package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
  231. package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
  232. package/renderer/dist/emulator/data/cores/README.md +22 -0
  233. package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
  234. package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
  235. package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
  236. package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
  237. package/renderer/dist/emulator/data/cores/package.json +65 -0
  238. package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
  239. package/renderer/dist/emulator/data/emulator.css +1614 -0
  240. package/renderer/dist/emulator/data/loader.js +170 -0
  241. package/renderer/dist/emulator/data/localization/README.md +63 -0
  242. package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
  243. package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
  244. package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
  245. package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
  246. package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
  247. package/renderer/dist/emulator/data/localization/en-US.json +339 -0
  248. package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
  249. package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
  250. package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
  251. package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
  252. package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
  253. package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
  254. package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
  255. package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
  256. package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
  257. package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
  258. package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
  259. package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
  260. package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
  261. package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
  262. package/renderer/dist/emulator/data/src/GameManager.js +464 -0
  263. package/renderer/dist/emulator/data/src/compression.js +142 -0
  264. package/renderer/dist/emulator/data/src/emulator.js +6158 -0
  265. package/renderer/dist/emulator/data/src/gamepad.js +167 -0
  266. package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
  267. package/renderer/dist/emulator/data/src/shaders.js +236 -0
  268. package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
  269. package/renderer/dist/emulator/data/src/storage.js +125 -0
  270. package/renderer/dist/emulator/data/version.json +5 -0
  271. package/renderer/dist/emulator/host.js +297 -0
  272. package/renderer/dist/emulator/index.html +28 -0
  273. package/renderer/dist/emulator/provenance.json +6 -0
  274. package/renderer/dist/index.html +16 -3
  275. package/scripts/after-install.mjs +30 -8
  276. package/scripts/approval-server.sh +24 -5
  277. package/scripts/before-publish.mjs +12 -10
  278. package/scripts/cloud-shim.pl +57 -0
  279. package/shared/account-names.d.mts +6 -0
  280. package/shared/account-names.mjs +15 -0
  281. package/shared/agent-hours.mjs +136 -0
  282. package/shared/agent-import.mjs +62 -34
  283. package/shared/agents.d.mts +1 -0
  284. package/shared/agents.mjs +126 -522
  285. package/shared/analytics-events.mjs +12 -0
  286. package/shared/approval-card.mjs +36 -0
  287. package/shared/bridge-map.mjs +45 -1
  288. package/shared/claude-code-shape.mjs +6 -0
  289. package/shared/claude-commands.generated.mjs +6 -4
  290. package/shared/claude-models.generated.mjs +6 -4
  291. package/shared/claude-usage.d.mts +1 -0
  292. package/shared/claude-usage.mjs +42 -1
  293. package/shared/cloud-hand-back.d.mts +7 -0
  294. package/shared/cloud-hand-back.mjs +20 -0
  295. package/shared/cloud-kit.d.mts +219 -0
  296. package/shared/cloud-kit.mjs +341 -0
  297. package/shared/cloud-login-name.mjs +4 -0
  298. package/shared/cloud-start.mjs +17 -0
  299. package/shared/cloud-versions.mjs +23 -0
  300. package/shared/codex-import.mjs +2 -2
  301. package/shared/codex-modes.d.mts +22 -9
  302. package/shared/codex-modes.mjs +67 -11
  303. package/shared/codex-usage.mjs +108 -2
  304. package/shared/corner-tag.d.mts +36 -0
  305. package/shared/corner-tag.mjs +396 -0
  306. package/shared/dead-run-trace.mjs +19 -2
  307. package/shared/engines.mjs +76 -300
  308. package/shared/feedback.d.mts +7 -0
  309. package/shared/feedback.mjs +25 -0
  310. package/shared/first-run-practice.mjs +17 -24
  311. package/shared/harness-definitions.mjs +111 -0
  312. package/shared/instruction-defaults.json +2 -2
  313. package/shared/key-caps.d.mts +14 -0
  314. package/shared/key-caps.mjs +68 -0
  315. package/shared/message-edits.d.mts +7 -0
  316. package/shared/message-edits.mjs +35 -0
  317. package/shared/move-thread.d.mts +24 -0
  318. package/shared/move-thread.mjs +112 -0
  319. package/shared/named-threads.mjs +72 -0
  320. package/shared/not-shipped.d.mts +22 -0
  321. package/shared/not-shipped.mjs +89 -0
  322. package/shared/notify-rules.d.mts +31 -0
  323. package/shared/notify-rules.mjs +55 -8
  324. package/shared/phone-files.mjs +28 -0
  325. package/shared/phone-settings.mjs +118 -0
  326. package/shared/phone-snapshot.d.mts +16 -0
  327. package/shared/phone-snapshot.mjs +348 -0
  328. package/shared/picture-formats.d.mts +1 -0
  329. package/shared/picture-formats.mjs +13 -0
  330. package/shared/provider-commands.mjs +10 -2
  331. package/shared/rank.d.mts +4 -1
  332. package/shared/rank.mjs +82 -19
  333. package/shared/relay-address.mjs +6 -0
  334. package/shared/relay-crypto.mjs +165 -0
  335. package/shared/repeats.d.mts +8 -0
  336. package/shared/repeats.mjs +33 -2
  337. package/shared/room-cards.mjs +167 -0
  338. package/shared/room-links.d.mts +4 -0
  339. package/shared/room-links.mjs +21 -0
  340. package/shared/runs-on.d.mts +3 -13
  341. package/shared/runs-on.mjs +31 -52
  342. package/shared/shipped-note.d.mts +4 -0
  343. package/shared/shipped-note.mjs +6 -0
  344. package/shared/shortcuts.mjs +24 -10
  345. package/shared/spawn-trouble.d.mts +4 -0
  346. package/shared/spawn-trouble.mjs +484 -30
  347. package/shared/team-rules.mjs +9 -2
  348. package/shared/teammates.d.mts +19 -0
  349. package/shared/teammates.mjs +756 -0
  350. package/shared/terminal-focus.d.mts +1 -0
  351. package/shared/terminal-focus.mjs +13 -0
  352. package/shared/terminal-input.d.mts +1 -0
  353. package/shared/terminal-input.mjs +30 -0
  354. package/shared/terminal-keys.d.mts +1 -0
  355. package/shared/terminal-keys.mjs +16 -0
  356. package/shared/terminal-size.d.mts +4 -0
  357. package/shared/terminal-size.mjs +29 -0
  358. package/shared/thread-cards.mjs +4 -1
  359. package/shared/usage.d.mts +12 -0
  360. package/shared/usage.mjs +56 -3
  361. package/shared/websites.d.mts +15 -0
  362. package/shared/websites.mjs +61 -0
  363. package/shared/where-it-runs-from.d.mts +43 -0
  364. package/shared/where-it-runs-from.mjs +155 -0
  365. package/shared/work-items.mjs +150 -29
  366. package/shared/work-lines.mjs +93 -245
  367. package/shared/xterm-invalid-range.d.mts +2 -0
  368. package/shared/xterm-invalid-range.mjs +58 -0
  369. package/renderer/dist/assets/index-ChKsQkRw.js +0 -1169
  370. package/renderer/dist/assets/index-ClBXnq3B.css +0 -32
@@ -0,0 +1,219 @@
1
+ // THE PROJECT'S DOCUMENTS FOLDER, FOR A CLOUD RUN.
2
+ //
3
+ // A worker on this Mac reads the plans and notes in the project's documents
4
+ // folder and writes its designs and reports there, where the app draws them.
5
+ // A cloud run gets a copy at the same path: everything small, and this
6
+ // thread's own designs folder, newest first, as much as fits. What it writes
7
+ // there comes back while it works and once more at the end
8
+ // (main/cloud/run.mjs), and is laid in here. Never a deletion, never over a
9
+ // file someone changed on this Mac while the run was away, and never over one
10
+ // the run did not take with it: those are kept, and the run's copy goes beside.
11
+ import fs from 'node:fs';
12
+ import os from 'node:os';
13
+ import path from 'node:path';
14
+ import crypto from 'node:crypto';
15
+ import { run } from './exec.mjs';
16
+
17
+ // The folders that hold pictures by the gigabyte. Only this thread's own
18
+ // designs go up from them; the rest of the folder is small.
19
+ const HEAVY = new Set(['designs', 'attachments']);
20
+ export const DOCS_LIMITS = {
21
+ file: 256 * 1024,
22
+ ownFile: 25 * 1024 * 1024,
23
+ own: 50 * 1024 * 1024,
24
+ total: 100 * 1024 * 1024,
25
+ files: 5000,
26
+ };
27
+
28
+ const inside = (p, root) => p === root || p.startsWith(root + path.sep);
29
+
30
+ /** The files, by path inside `dir`, that go up with a cloud run for `item`. */
31
+ export function docsToSend(dir, { item = null, limits = DOCS_LIMITS } = {}) {
32
+ const own = item && /^[A-Za-z0-9_-]+$/.test(item) ? `designs/${item}` : null;
33
+ const picked = [];
34
+ let total = 0;
35
+ let ownTotal = 0;
36
+ const walk = (rel) => {
37
+ let entries = [];
38
+ try { entries = fs.readdirSync(path.join(dir, rel), { withFileTypes: true }); } catch { return; }
39
+ entries.sort((a, b) => a.name.localeCompare(b.name));
40
+ for (const e of entries) {
41
+ if (picked.length >= limits.files) return;
42
+ if (e.name.startsWith('.')) continue;
43
+ const r = rel ? `${rel}/${e.name}` : e.name;
44
+ if (/[\n\r]/.test(r)) continue;
45
+ if (e.isDirectory()) {
46
+ if (!rel && HEAVY.has(e.name)) { if (own && e.name === 'designs') walkOwn(); continue; }
47
+ walk(r);
48
+ continue;
49
+ }
50
+ if (!e.isFile()) continue;
51
+ let size;
52
+ try { size = fs.statSync(path.join(dir, r)).size; } catch { continue; }
53
+ if (size > limits.file || total + size > limits.total) continue;
54
+ picked.push(r);
55
+ total += size;
56
+ }
57
+ };
58
+ // Newest first: a thread whose designs do not all fit takes its latest
59
+ // work, and the rest stays on this Mac.
60
+ const walkOwn = () => {
61
+ const found = [];
62
+ const stack = [own];
63
+ while (stack.length) {
64
+ const rel = stack.shift();
65
+ let entries = [];
66
+ try { entries = fs.readdirSync(path.join(dir, rel), { withFileTypes: true }); } catch { continue; }
67
+ for (const e of entries) {
68
+ if (e.name.startsWith('.')) continue;
69
+ const r = `${rel}/${e.name}`;
70
+ if (/[\n\r]/.test(r)) continue;
71
+ if (e.isDirectory()) { stack.push(r); continue; }
72
+ if (!e.isFile()) continue;
73
+ let st;
74
+ try { st = fs.statSync(path.join(dir, r)); } catch { continue; }
75
+ if (st.size <= limits.ownFile) found.push({ r, size: st.size, at: st.mtimeMs });
76
+ }
77
+ }
78
+ found.sort((a, b) => b.at - a.at || a.r.localeCompare(b.r));
79
+ for (const f of found) {
80
+ if (picked.length >= limits.files) return;
81
+ if (ownTotal + f.size > limits.own) continue;
82
+ picked.push(f.r);
83
+ ownTotal += f.size;
84
+ }
85
+ };
86
+ walk('');
87
+ return picked;
88
+ }
89
+
90
+ /** Those files as one gzipped tar, paths inside `dir`. */
91
+ export function tarDocs(dir, files) {
92
+ return run('tar', ['-czf', '-', '-C', dir, '--null', '-T', '-'], { input: files.join('\0') + '\0', encoding: null, env: { ...process.env, COPYFILE_DISABLE: '1' } });
93
+ }
94
+
95
+ const stampOf = (st) => `${st.size}:${Math.round(st.mtimeMs)}`;
96
+ const hashOf = (body) => crypto.createHash('sha256').update(body).digest('hex');
97
+
98
+ // `notes.md` -> `notes (cloud).md`, then `notes (cloud 2).md`, and so on.
99
+ function besideName(file) {
100
+ const ext = path.extname(file);
101
+ const base = file.slice(0, file.length - ext.length);
102
+ for (let i = 1; ; i += 1) {
103
+ const p = `${base} (cloud${i > 1 ? ` ${i}` : ''})${ext}`;
104
+ if (!fs.existsSync(p)) return p;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Lay an archive of documents the run wrote into `dir`.
110
+ *
111
+ * `sentAt` is when the run left with the folder; `laid` is what earlier calls
112
+ * for the same run wrote, path -> { stamp, hash }, and is updated in place. A
113
+ * version this run already laid is never laid again: the run sends a file
114
+ * until the Mac confirms it, so the same one can arrive twice. Otherwise a file
115
+ * is written over only if it is missing here, unchanged here since the run
116
+ * left, or is still exactly what this run itself laid. Anything else is a
117
+ * change made on this Mac, and the run's copy goes beside it.
118
+ *
119
+ * `sent` is every path the run took with it. A file here that is not one of
120
+ * them was never the run's to change, however old it is, so its copy goes
121
+ * beside it too. A run recorded before the list was kept passes none.
122
+ */
123
+ export async function layDocs({ tgz, dir, sentAt = 0, laid = {}, sent = null }) {
124
+ const took = sent ? new Set(sent) : null;
125
+ const out = { written: [], beside: [], moved: [] };
126
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'agentbox-docs-'));
127
+ try {
128
+ // From a file, not a pipe: tar may stop reading at the archive's end
129
+ // marker, and a pipe it stopped reading fails the write (EPIPE) on a busy Mac.
130
+ const file = path.join(tmp, '.agentbox-docs.tgz');
131
+ fs.writeFileSync(file, tgz);
132
+ const into = path.join(tmp, 'docs');
133
+ fs.mkdirSync(into);
134
+ await run('tar', ['-xzf', file, '-C', into]);
135
+ fs.mkdirSync(dir, { recursive: true });
136
+ const root = fs.realpathSync(dir);
137
+ const files = [];
138
+ const walk = (rel) => {
139
+ for (const e of fs.readdirSync(path.join(into, rel), { withFileTypes: true })) {
140
+ const r = rel ? `${rel}/${e.name}` : e.name;
141
+ if (e.isDirectory()) walk(r);
142
+ else if (e.isFile()) files.push(r);
143
+ }
144
+ };
145
+ walk('');
146
+ // The nearest folder that exists, after links, must be this one or in
147
+ // it: a link inside the folder never carries a file out of it.
148
+ const safeParent = (file) => {
149
+ let parent = path.dirname(file);
150
+ while (!fs.existsSync(parent)) parent = path.dirname(parent);
151
+ if (!inside(fs.realpathSync(parent), root)) return false;
152
+ fs.mkdirSync(path.dirname(file), { recursive: true });
153
+ return inside(fs.realpathSync(path.dirname(file)), root);
154
+ };
155
+ for (const rel of files) {
156
+ const asIs = path.join(root, rel);
157
+ let target = asIs;
158
+ const body = fs.readFileSync(path.join(into, rel));
159
+ const hash = hashOf(body);
160
+ if (laid[rel]?.hash === hash) continue;
161
+ // A file whose folder here is a link out of it is not dropped: it lands
162
+ // in a folder of its own, and the run's work is all still home.
163
+ if (!safeParent(target)) {
164
+ target = path.join(root, 'from-the-cloud', rel);
165
+ if (!safeParent(target)) throw new Error(`nowhere safe to put ${rel}`);
166
+ out.moved.push(path.relative(root, target));
167
+ }
168
+ let here = null;
169
+ try { here = fs.lstatSync(target); } catch { /* new */ }
170
+ // A link or a folder where the file goes is left as it is, and the
171
+ // run's copy goes beside it.
172
+ if (here && !here.isFile()) {
173
+ target = besideName(target);
174
+ out.beside.push(path.relative(root, target));
175
+ here = null;
176
+ }
177
+ if (here) {
178
+ // The same bytes already here: nothing to write. It becomes the run's
179
+ // only if the run took it or wrote it before, never a file left here.
180
+ if (here.size === body.length && fs.readFileSync(target).equals(body)) {
181
+ const owned = !took || took.has(rel) || !!laid[rel]?.stamp;
182
+ laid[rel] = { stamp: owned ? stampOf(here) : null, hash };
183
+ continue;
184
+ }
185
+ const mine = laid[rel]?.stamp === stampOf(here);
186
+ // Whole milliseconds on both sides: `sentAt` is a Date.now() and counts
187
+ // them, an mtime does not, so a file saved 0.7 ms before the run left
188
+ // read as newer than it and the run's own edit went beside a file
189
+ // nobody had touched.
190
+ const untouched = mine || (Math.floor(here.mtimeMs) <= sentAt && (!took || took.has(rel)));
191
+ if (!untouched) {
192
+ target = besideName(target);
193
+ out.beside.push(path.relative(root, target));
194
+ }
195
+ }
196
+ // A fresh name, made only if nothing is there: a link left at a name
197
+ // this could guess would otherwise be written through.
198
+ const part = path.join(path.dirname(target), `.agentbox-${crypto.randomUUID()}.part`);
199
+ fs.writeFileSync(part, body, { flag: 'wx' });
200
+ fs.renameSync(part, target);
201
+ if (target === asIs) out.written.push(rel);
202
+ laid[rel] = { stamp: target === asIs ? stampOf(fs.statSync(target)) : laid[rel]?.stamp ?? null, hash };
203
+ }
204
+ } finally {
205
+ fs.rmSync(tmp, { recursive: true, force: true });
206
+ }
207
+ return out;
208
+ }
209
+
210
+ /** The documents folder a run may take, or null: it must exist and must not hold, or sit in, the task folder. */
211
+ export function docsFolderFor(docs, cwd) {
212
+ if (!docs || !path.isAbsolute(docs)) return null;
213
+ let real;
214
+ let realCwd;
215
+ try { real = fs.realpathSync(docs); realCwd = fs.realpathSync(cwd); } catch { return null; }
216
+ if (!fs.statSync(real).isDirectory()) return null;
217
+ if (inside(real, realCwd) || inside(realCwd, real)) return null;
218
+ return docs;
219
+ }
@@ -0,0 +1,27 @@
1
+ // A command run without holding up this process, which every cloud run's relay
2
+ // shares (./host.mjs): one run's git or tar must not stop the others.
3
+ import { spawn } from 'node:child_process';
4
+
5
+ // Resolves to stdout (a string, or a Buffer when `encoding` is null); rejects,
6
+ // as execFileSync throws, with "Command failed: <command>" and its stderr.
7
+ export function run(cmd, args, { cwd, env = process.env, input = null, encoding = 'utf8' } = {}) {
8
+ return new Promise((resolve, reject) => {
9
+ let child;
10
+ try { child = spawn(cmd, args, { cwd, env, stdio: ['pipe', 'pipe', 'pipe'] }); } catch (err) { reject(err); return; }
11
+ const out = [];
12
+ const err = [];
13
+ child.stdout.on('data', (d) => out.push(d));
14
+ child.stderr.on('data', (d) => err.push(d));
15
+ child.on('error', reject);
16
+ child.on('close', (code, signal) => {
17
+ const stdout = Buffer.concat(out);
18
+ if (code === 0) { resolve(encoding ? stdout.toString(encoding) : stdout); return; }
19
+ const stderr = Buffer.concat(err).toString('utf8');
20
+ const e = new Error(`Command failed: ${cmd} ${args.join(' ')}\n${stderr}`);
21
+ Object.assign(e, { status: code, signal, stderr });
22
+ reject(e);
23
+ });
24
+ child.stdin.on('error', () => {});
25
+ child.stdin.end(input ?? undefined);
26
+ });
27
+ }
@@ -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
+ }