sliccy 3.5.4 → 3.6.0

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 (240) hide show
  1. package/README.md +1 -2
  2. package/dist/node-server/_cloud_core/src/errors.d.ts +14 -0
  3. package/dist/node-server/_cloud_core/src/errors.d.ts.map +1 -0
  4. package/dist/node-server/_cloud_core/src/errors.js +13 -0
  5. package/dist/node-server/_cloud_core/src/index.d.ts +19 -0
  6. package/dist/node-server/_cloud_core/src/index.d.ts.map +1 -0
  7. package/dist/node-server/_cloud_core/src/index.js +14 -0
  8. package/dist/node-server/_cloud_core/src/operations/kill.d.ts +18 -0
  9. package/dist/node-server/_cloud_core/src/operations/kill.d.ts.map +1 -0
  10. package/dist/node-server/_cloud_core/src/operations/kill.js +32 -0
  11. package/dist/node-server/_cloud_core/src/operations/list.d.ts +25 -0
  12. package/dist/node-server/_cloud_core/src/operations/list.d.ts.map +1 -0
  13. package/dist/node-server/_cloud_core/src/operations/list.js +145 -0
  14. package/dist/node-server/_cloud_core/src/operations/pause.d.ts +15 -0
  15. package/dist/node-server/_cloud_core/src/operations/pause.d.ts.map +1 -0
  16. package/dist/node-server/_cloud_core/src/operations/pause.js +27 -0
  17. package/dist/node-server/_cloud_core/src/operations/resume.d.ts +22 -0
  18. package/dist/node-server/_cloud_core/src/operations/resume.d.ts.map +1 -0
  19. package/dist/node-server/_cloud_core/src/operations/resume.js +86 -0
  20. package/dist/node-server/_cloud_core/src/operations/start.d.ts +67 -0
  21. package/dist/node-server/_cloud_core/src/operations/start.d.ts.map +1 -0
  22. package/dist/node-server/_cloud_core/src/operations/start.js +228 -0
  23. package/dist/node-server/_cloud_core/src/polling.d.ts +18 -0
  24. package/dist/node-server/_cloud_core/src/polling.d.ts.map +1 -0
  25. package/dist/node-server/_cloud_core/src/polling.js +79 -0
  26. package/dist/node-server/_cloud_core/src/registry.d.ts +27 -0
  27. package/dist/node-server/_cloud_core/src/registry.d.ts.map +1 -0
  28. package/dist/node-server/_cloud_core/src/registry.js +1 -0
  29. package/dist/node-server/_cloud_core/src/secrets-filter.d.ts +2 -0
  30. package/dist/node-server/_cloud_core/src/secrets-filter.d.ts.map +1 -0
  31. package/dist/node-server/_cloud_core/src/secrets-filter.js +17 -0
  32. package/dist/node-server/_cloud_core/src/substrate-factory.d.ts +3 -0
  33. package/dist/node-server/_cloud_core/src/substrate-factory.d.ts.map +1 -0
  34. package/dist/node-server/_cloud_core/src/substrate-factory.js +6 -0
  35. package/dist/node-server/_cloud_core/src/substrate.d.ts +53 -0
  36. package/dist/node-server/_cloud_core/src/substrate.d.ts.map +1 -0
  37. package/dist/node-server/_cloud_core/src/substrate.js +1 -0
  38. package/dist/node-server/_cloud_core/src/substrates/e2b.d.ts +3 -0
  39. package/dist/node-server/_cloud_core/src/substrates/e2b.d.ts.map +1 -0
  40. package/dist/node-server/_cloud_core/src/substrates/e2b.js +98 -0
  41. package/dist/node-server/_cloud_core/src/types.d.ts +94 -0
  42. package/dist/node-server/_cloud_core/src/types.d.ts.map +1 -0
  43. package/dist/node-server/_cloud_core/src/types.js +3 -0
  44. package/dist/node-server/_cloud_core/tests/fixtures/fake-substrate.d.ts +39 -0
  45. package/dist/node-server/_cloud_core/tests/fixtures/fake-substrate.d.ts.map +1 -0
  46. package/dist/node-server/_cloud_core/tests/fixtures/fake-substrate.js +75 -0
  47. package/dist/node-server/_cloud_core/tests/fixtures/index.d.ts +4 -0
  48. package/dist/node-server/_cloud_core/tests/fixtures/index.d.ts.map +1 -0
  49. package/dist/node-server/_cloud_core/tests/fixtures/index.js +2 -0
  50. package/dist/node-server/_cloud_core/tests/fixtures/mem-registry.d.ts +10 -0
  51. package/dist/node-server/_cloud_core/tests/fixtures/mem-registry.d.ts.map +1 -0
  52. package/dist/node-server/_cloud_core/tests/fixtures/mem-registry.js +25 -0
  53. package/dist/node-server/_cloud_core/tests/kill.test.d.ts +2 -0
  54. package/dist/node-server/_cloud_core/tests/kill.test.d.ts.map +1 -0
  55. package/dist/node-server/_cloud_core/tests/kill.test.js +164 -0
  56. package/dist/node-server/_cloud_core/tests/list.test.d.ts +2 -0
  57. package/dist/node-server/_cloud_core/tests/list.test.d.ts.map +1 -0
  58. package/dist/node-server/_cloud_core/tests/list.test.js +300 -0
  59. package/dist/node-server/_cloud_core/tests/pause.test.d.ts +2 -0
  60. package/dist/node-server/_cloud_core/tests/pause.test.d.ts.map +1 -0
  61. package/dist/node-server/_cloud_core/tests/pause.test.js +167 -0
  62. package/dist/node-server/_cloud_core/tests/resume.test.d.ts +2 -0
  63. package/dist/node-server/_cloud_core/tests/resume.test.d.ts.map +1 -0
  64. package/dist/node-server/_cloud_core/tests/resume.test.js +203 -0
  65. package/dist/node-server/_cloud_core/tests/secrets-filter.test.d.ts +2 -0
  66. package/dist/node-server/_cloud_core/tests/secrets-filter.test.d.ts.map +1 -0
  67. package/dist/node-server/_cloud_core/tests/secrets-filter.test.js +30 -0
  68. package/dist/node-server/_cloud_core/tests/start.test.d.ts +2 -0
  69. package/dist/node-server/_cloud_core/tests/start.test.d.ts.map +1 -0
  70. package/dist/node-server/_cloud_core/tests/start.test.js +513 -0
  71. package/dist/node-server/chrome-launch.d.ts +1 -0
  72. package/dist/node-server/chrome-launch.js +3 -0
  73. package/dist/node-server/cloud/dispatch.d.ts +34 -0
  74. package/dist/node-server/cloud/dispatch.js +79 -0
  75. package/dist/node-server/cloud/kill.d.ts +12 -0
  76. package/dist/node-server/cloud/kill.js +11 -0
  77. package/dist/node-server/cloud/list.d.ts +6 -0
  78. package/dist/node-server/cloud/list.js +6 -0
  79. package/dist/node-server/cloud/pause.d.ts +7 -0
  80. package/dist/node-server/cloud/pause.js +6 -0
  81. package/dist/node-server/cloud/registry-file.d.ts +13 -0
  82. package/dist/node-server/cloud/registry-file.js +85 -0
  83. package/dist/node-server/cloud/resume.d.ts +11 -0
  84. package/dist/node-server/cloud/resume.js +17 -0
  85. package/dist/node-server/cloud/start.d.ts +16 -0
  86. package/dist/node-server/cloud/start.js +49 -0
  87. package/dist/node-server/cloud-status.d.ts +11 -0
  88. package/dist/node-server/cloud-status.js +49 -0
  89. package/dist/node-server/hosted-bootstrap.d.ts +21 -0
  90. package/dist/node-server/hosted-bootstrap.js +14 -0
  91. package/dist/node-server/index.js +146 -3
  92. package/dist/node-server/leader-restart.d.ts +29 -0
  93. package/dist/node-server/leader-restart.js +146 -0
  94. package/dist/node-server/runtime-flags.d.ts +1 -0
  95. package/dist/node-server/runtime-flags.js +6 -0
  96. package/dist/ui/assets/adobe-BLY_1hN7.js +1 -0
  97. package/dist/ui/assets/adobe-BZS2Kmci.js +2 -0
  98. package/dist/ui/assets/agent-bridge-BFRFxJAl.js +1 -0
  99. package/dist/ui/assets/{agent-message-to-chat-H07zq-Kv.js → agent-message-to-chat-Cd-DQATg.js} +1 -1
  100. package/dist/ui/assets/anthropic-BbwUhO9H.js +39 -0
  101. package/dist/ui/assets/{anthropic-BK3Od7MT.js → anthropic-Dg9_3G1u.js} +3 -3
  102. package/dist/ui/assets/{apps-mZJxOz_q.js → apps-BQuoIsxV.js} +1 -1
  103. package/dist/ui/assets/azure-openai-CilK3M_Q.js +1 -0
  104. package/dist/ui/assets/{azure-openai-Dch5aR9j.js → azure-openai-CleJGzBE.js} +1 -1
  105. package/dist/ui/assets/azure-openai-responses-DMLD0Ano.js +1 -0
  106. package/dist/ui/assets/azure-openai-responses-_joBD1ru.js +1 -0
  107. package/dist/ui/assets/{bsh-watchdog-i5GXbvla.js → bsh-watchdog-CpJwB685.js} +1 -1
  108. package/dist/ui/assets/{cdp-Cv6W51Ld.js → cdp-BXSNdZRM.js} +3 -3
  109. package/dist/ui/assets/cloud-CCHD45tb.js +1 -0
  110. package/dist/ui/assets/cloud-DyAca2cv.css +1 -0
  111. package/dist/ui/assets/cost-command-Bv5UQVUC.js +1 -0
  112. package/dist/ui/assets/{dist-cK1g1pHF.js → dist-ChvbMSOY.js} +1 -1
  113. package/dist/ui/assets/{dist-BTGs16AV.js → dist-CltZJzbd.js} +1 -1
  114. package/dist/ui/assets/{dist-BcBxL8nL.js → dist-cZSF0917.js} +1 -1
  115. package/dist/ui/assets/{dist-Bgcuvc8h.js → dist-ljtB3O-J.js} +1 -1
  116. package/dist/ui/assets/{dist-EkY3RL7q.js → dist-tLLu0PBg.js} +1 -1
  117. package/dist/ui/assets/{es-Crcyr8Z7.js → es-C2wm9v2H.js} +1 -1
  118. package/dist/ui/assets/follower-sprinkle-bridge-Bg1mCcnG.js +1 -0
  119. package/dist/ui/assets/{github-DKcpc18I.js → github-BDzC2pCD.js} +1 -1
  120. package/dist/ui/assets/github-BzwlgA3s.js +2 -0
  121. package/dist/ui/assets/{google-IZXg3PJn.js → google-BVxsfaV3.js} +1 -1
  122. package/dist/ui/assets/google-C0Zd1qsl.js +1 -0
  123. package/dist/ui/assets/google-shared-DcZhZ_Z7.js +11 -0
  124. package/dist/ui/assets/{google-shared-BaIigNCs.js → google-shared-KyLYJrXa.js} +1 -1
  125. package/dist/ui/assets/google-vertex-BGpbWEWq.js +1 -0
  126. package/dist/ui/assets/{google-vertex-DDwmtWoJ.js → google-vertex-BSOalca9.js} +1 -1
  127. package/dist/ui/assets/{intercepted-oauth-CEgKdcyY.js → intercepted-oauth-D6eAxYca.js} +1 -1
  128. package/dist/ui/assets/{kernel-worker-dg2bRx7X.js → kernel-worker-D1_EtuZW.js} +1016 -980
  129. package/dist/ui/assets/{lick-ws-bridge-DgWfdUlv.js → lick-ws-bridge-DXFDOui-.js} +1 -1
  130. package/dist/ui/assets/{local-llm-DuUGv7wX.js → local-llm-B3qsU55x.js} +1 -1
  131. package/dist/ui/assets/magick-wasm-CasStH-w.js +1 -0
  132. package/dist/ui/assets/main-BSfjQXCf.js +1612 -0
  133. package/dist/ui/assets/{mistral-czd8-m0c.js → mistral-BkhZQqeV.js} +1 -1
  134. package/dist/ui/assets/{mistral-BS_Gm2MH.js → mistral-D130SR-b.js} +1 -1
  135. package/dist/ui/assets/modulepreload-polyfill-wMinxHhO.js +1 -0
  136. package/dist/ui/assets/nuke-command-D_sxu_AA.js +1 -0
  137. package/dist/ui/assets/{oauth-CJxNn7hC.js → oauth-BocTO4G9.js} +1 -1
  138. package/dist/ui/assets/oauth-bootstrap-DZdd7KZS.js +2 -0
  139. package/dist/ui/assets/{oauth-service-BAnfipgD.js → oauth-service-DAWR1CpY.js} +1 -1
  140. package/dist/ui/assets/oauth-service-y04jhWb_.js +1 -0
  141. package/dist/ui/assets/offscreen-client-HCLuFFlN.js +1 -0
  142. package/dist/ui/assets/onboarding-orchestrator-qbEVluIu.js +1 -0
  143. package/dist/ui/assets/openai-codex-responses-Bmd17Jq9.js +7 -0
  144. package/dist/ui/assets/openai-codex-responses-CMOclqki.js +7 -0
  145. package/dist/ui/assets/openai-completions-DmwgEsb4.js +5 -0
  146. package/dist/ui/assets/openai-responses-BYdEOxUq.js +1 -0
  147. package/dist/ui/assets/openai-responses-C3bs8sd1.js +1 -0
  148. package/dist/ui/assets/{openai-responses-shared-DDootAUL.js → openai-responses-shared-AM04ghTC.js} +1 -1
  149. package/dist/ui/assets/{openai-responses-shared-07lPyxEg.js → openai-responses-shared-Be22tDzQ.js} +1 -1
  150. package/dist/ui/assets/openrouter-BIFqVYxG.js +1 -0
  151. package/dist/ui/assets/openrouter-Dc1D0QOS.js +1 -0
  152. package/dist/ui/assets/panel-rpc-handlers-DDaHHzWR.js +1 -0
  153. package/dist/ui/assets/{provider-rBD-3FHW.js → provider-CU5ao6kF.js} +2 -2
  154. package/dist/ui/assets/{provider-BhabAJGA.js → provider-ChUiWhH0.js} +1 -1
  155. package/dist/ui/assets/provider-store-access-Bliwcbgg.js +1 -0
  156. package/dist/ui/assets/provider-store-access-iTh_6mMO.js +1 -0
  157. package/dist/ui/assets/proxied-fetch-8v3TaZYg.js +1 -0
  158. package/dist/ui/assets/{pyodide-BC4PaR54.js → pyodide-B7x60zRI.js} +2 -2
  159. package/dist/ui/assets/{remote-terminal-view-CXbFLcuS.js → remote-terminal-view-BREvd00U.js} +1 -1
  160. package/dist/ui/assets/secret-env-Js2g2sNO.js +1 -0
  161. package/dist/ui/assets/slicc-editor-Bu07BjW4.js +12 -0
  162. package/dist/ui/assets/{spawn-qtlw6x6p.js → spawn-CQ5XACZ1.js} +1 -1
  163. package/dist/ui/assets/{sql-wasm-DeVtn0z8.js → sql-wasm-C-pXJvOw.js} +1 -1
  164. package/dist/ui/assets/{store-DLeVoNF3.js → store-DQUahHdW.js} +1 -1
  165. package/dist/ui/assets/tray-leave-runtime-BEBMIYY_.js +1 -0
  166. package/dist/ui/assets/{upgrade-detection-CKrzeeRx.js → upgrade-detection-T74KTnyb.js} +1 -1
  167. package/dist/ui/assets/{xai-grok-CNyu04Tg.js → xai-grok-CkPPMxfZ.js} +1 -1
  168. package/dist/ui/assets/{xai-grok-sanitize-BeI8rdV5.js → xai-grok-sanitize-C7TgWvwD.js} +1 -1
  169. package/dist/ui/assets/{xai-grok-sanitize-h_8XMTD9.js → xai-grok-sanitize-L5nUbDcW.js} +1 -1
  170. package/dist/ui/assets/xai-grok-u9MyUN8m.js +1 -0
  171. package/dist/ui/electron-overlay-entry.js +9 -9
  172. package/dist/ui/index.html +3 -2
  173. package/dist/ui/packages/webapp/cloud/index.html +38 -0
  174. package/dist/ui/packages/webapp/index.html +3 -2
  175. package/package.json +10 -8
  176. package/dist/ui/assets/adobe-DpCIcyEb.js +0 -2
  177. package/dist/ui/assets/adobe-NMBK1vG2.js +0 -1
  178. package/dist/ui/assets/agent-bridge-D2k69QZ-.js +0 -1
  179. package/dist/ui/assets/anthropic-ZtfIcLyL.js +0 -39
  180. package/dist/ui/assets/azure-openai-CN9QA_pv.js +0 -1
  181. package/dist/ui/assets/azure-openai-responses-Bhr0mNdj.js +0 -1
  182. package/dist/ui/assets/azure-openai-responses-D400BxTc.js +0 -1
  183. package/dist/ui/assets/cost-command-BGqK_hbH.js +0 -1
  184. package/dist/ui/assets/follower-sprinkle-bridge-BayCf20v.js +0 -1
  185. package/dist/ui/assets/github-DpDjTcq9.js +0 -2
  186. package/dist/ui/assets/google-B97zVBac.js +0 -1
  187. package/dist/ui/assets/google-shared-DwzANLbz.js +0 -11
  188. package/dist/ui/assets/google-vertex-ICKJGS1M.js +0 -1
  189. package/dist/ui/assets/index-CCGqFzR9.js +0 -1575
  190. package/dist/ui/assets/magick-wasm-Bu7zx3Ux.js +0 -1
  191. package/dist/ui/assets/nuke-command-BSkSFZ4H.js +0 -1
  192. package/dist/ui/assets/oauth-bootstrap-BeZQrd7y.js +0 -2
  193. package/dist/ui/assets/oauth-service-BX55wTV9.js +0 -1
  194. package/dist/ui/assets/offscreen-client-DSEhUCxj.js +0 -1
  195. package/dist/ui/assets/onboarding-orchestrator-DjNti83y.js +0 -1
  196. package/dist/ui/assets/openai-codex-responses-DJKJH9AX.js +0 -7
  197. package/dist/ui/assets/openai-codex-responses-jB5RkWeq.js +0 -7
  198. package/dist/ui/assets/openai-completions-BWWM3S2J.js +0 -5
  199. package/dist/ui/assets/openai-responses-C_3YpAmj.js +0 -1
  200. package/dist/ui/assets/openai-responses-tJegTQYT.js +0 -1
  201. package/dist/ui/assets/panel-rpc-handlers-B4jZTNHO.js +0 -1
  202. package/dist/ui/assets/provider-store-access-H9Lu5HRL.js +0 -1
  203. package/dist/ui/assets/provider-store-access-biaB--zG.js +0 -1
  204. package/dist/ui/assets/proxied-fetch-C6dm2Ugc.js +0 -1
  205. package/dist/ui/assets/secret-env-s_8cP7Wf.js +0 -1
  206. package/dist/ui/assets/slicc-editor-pZcWEwTm.js +0 -12
  207. package/dist/ui/assets/tray-leave-runtime-DqA9PbxV.js +0 -1
  208. package/dist/ui/assets/xai-grok-CN1nP4TF.js +0 -1
  209. /package/dist/ui/assets/{active-transport-CfU1UxY6.js → active-transport-Clj9slj7.js} +0 -0
  210. /package/dist/ui/assets/{active-transport-Dm6CWYVE.js → active-transport-DHcaBd4j.js} +0 -0
  211. /package/dist/ui/assets/{addon-fit-BDUxDgg1.js → addon-fit-CBG3Hsec.js} +0 -0
  212. /package/dist/ui/assets/{addon-fit-Dkm5m5XK.js → addon-fit-CUt9lrQO.js} +0 -0
  213. /package/dist/ui/assets/{azure-ai-foundry-BHLfY_0t.js → azure-ai-foundry-B4QrVklT.js} +0 -0
  214. /package/dist/ui/assets/{azure-ai-foundry-1pnZpxAJ.js → azure-ai-foundry-CGkSI_4f.js} +0 -0
  215. /package/dist/ui/assets/{chat-fixture-DOzffHLr.js → chat-fixture-ChGRNFda.js} +0 -0
  216. /package/dist/ui/assets/{dist-BYoCEWmq.js → dist-9RlRmGag.js} +0 -0
  217. /package/dist/ui/assets/{dist-BNe7ZIzA.js → dist-CZcDRTma.js} +0 -0
  218. /package/dist/ui/assets/{dist-BrjVVq8c.js → dist-D0kTaNET.js} +0 -0
  219. /package/dist/ui/assets/{git-config-CGMfR0WK.js → git-config-DcQbkikl.js} +0 -0
  220. /package/dist/ui/assets/{github-copilot-headers-CTUP_cqn.js → github-copilot-headers-C5CocKfn.js} +0 -0
  221. /package/dist/ui/assets/{global-db-B97_fwjD.js → global-db-DL3jdM9H.js} +0 -0
  222. /package/dist/ui/assets/{hash-B9Dtr1VK.js → hash-iitWbNjK.js} +0 -0
  223. /package/dist/ui/assets/{headers-AeYLn7L4.js → headers-qDMOQQGF.js} +0 -0
  224. /package/dist/ui/assets/{lick-manager-proxy-BnnMFbgv.js → lick-manager-proxy-1L0EGIWD.js} +0 -0
  225. /package/dist/ui/assets/{index-CvlDeETm.css → main-CvlDeETm.css} +0 -0
  226. /package/dist/ui/assets/{openai-Bf1npfRy.js → openai-BR_xAflc.js} +0 -0
  227. /package/dist/ui/assets/{page-storage-sync-CqMAh8jG.js → page-storage-sync-sv2ogNKh.js} +0 -0
  228. /package/dist/ui/assets/{pdfjs-DDDkZHZ4.js → pdfjs-CW51NNE6.js} +0 -0
  229. /package/dist/ui/assets/{rum-DCU8sePD.js → rum-z5wz4Dnj.js} +0 -0
  230. /package/dist/ui/assets/{sanitize-unicode-h0KEBmEw.js → sanitize-unicode-B1z_Y4Dq.js} +0 -0
  231. /package/dist/ui/assets/{sprinkle-bridge-channel-DojIojh7.js → sprinkle-bridge-channel-Bw5UaanD.js} +0 -0
  232. /package/dist/ui/assets/{sprinkle-bridge-channel-DJC5SGWr.js → sprinkle-bridge-channel-ChD0T07C.js} +0 -0
  233. /package/dist/ui/assets/{sprinkle-proxy-rF3NSOGq.js → sprinkle-proxy-D6wqmewK.js} +0 -0
  234. /package/dist/ui/assets/{src-BQRb5emh.js → src-DRmQ1wr-.js} +0 -0
  235. /package/dist/ui/assets/{xai-grok-errors-Bzn3oNvW.js → xai-grok-errors-BdY43UIn.js} +0 -0
  236. /package/dist/ui/assets/{xai-grok-errors-WbFsMZum.js → xai-grok-errors-C6VnD_MH.js} +0 -0
  237. /package/dist/ui/assets/{xai-grok-models-cvcIXmNM.js → xai-grok-models-BndZgIFZ.js} +0 -0
  238. /package/dist/ui/assets/{xai-grok-models-DMYWtapT.js → xai-grok-models-C9MIa-Aj.js} +0 -0
  239. /package/dist/ui/assets/{xterm-D8ldLLKi.js → xterm-DLCsxsB1.js} +0 -0
  240. /package/dist/ui/assets/{xterm-DhusGoAm.js → xterm-VndicwFp.js} +0 -0
package/README.md CHANGED
@@ -11,7 +11,7 @@ If this scares, confuses, or excites you, keep reading.
11
11
 
12
12
  # slicc — Self-Licking Ice Cream Cone
13
13
 
14
- [![74% Vibe_Coded](https://img.shields.io/badge/74%25-Vibe_Coded-ff69b4?style=for-the-badge&logo=claude&logoColor=white)](https://github.com/ai-ecoverse/vibe-coded-badge-action)
14
+ [![75% Vibe_Coded](https://img.shields.io/badge/75%25-Vibe_Coded-ff69b4?style=for-the-badge&logo=claude&logoColor=white)](https://github.com/ai-ecoverse/vibe-coded-badge-action)
15
15
 
16
16
  [![npm](https://img.shields.io/npm/v/sliccy)](https://www.npmjs.com/package/sliccy)
17
17
 
@@ -125,7 +125,6 @@ SLICC can mirror itself across multiple browsers, even on other machines:
125
125
  1. **First browser:** click your avatar in the top-right header and choose **Enable multi-browser sync**. A dialog opens with the sync URL (already copied to your clipboard) and step-by-step instructions. The same dialog has a **Reset URL** button if you want to invalidate the link and disconnect connected browsers. (You can also type `host` in the built-in terminal to print the URL.)
126
126
  2. **Second browser:** open the account dialog, click **Connect to another browser**, and paste the URL. The "How do I get the sync URL?" hint inside the dialog walks through the same steps.
127
127
  3. **Leaving the tray:** click the avatar on either browser to open the popover — the tray section now has a **Stop multi-browser sync** (leader) or **Disconnect from leader** (follower) action. From the terminal, `host leave` does the same thing; `host leave --leader <worker-url>` leaves the current role and becomes a leader on that worker.
128
- 4. **Re-enabling after Stop:** once you've stopped multi-browser sync, the avatar popover keeps an **Enable multi-browser sync** entry so you can restart the leader without reloading the extension or dropping into the shell. Clicking it spins up a new leader on the default tray worker; reopen the popover after a moment to copy the fresh sync URL.
129
128
 
130
129
  Both browsers must run the same SLICC version. Once connected, the sessions stay in sync in real time.
131
130
 
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Stable, machine-readable error codes used across cloud operations.
3
+ * Workers' HTTP handlers translate these to HTTP statuses (Plan D maps the
4
+ * codes to 403/404/409/503/500 etc.); CLI commands print them. Adding a new
5
+ * code is fine; renaming/removing one is a breaking change to consumers.
6
+ */
7
+ export type CloudErrorCode = 'CAP_EXCEEDED' | 'NOT_FOUND' | 'NAME_TAKEN' | 'ALREADY_PAUSED' | 'ALREADY_RUNNING' | 'LEADER_NOT_READY' | 'SANDBOX_NOT_READY' | 'CDP_NOT_READY' | 'CDP_ERROR' | 'DO_UNREACHABLE' | 'UPSTREAM_UNAVAILABLE' | 'INTERNAL';
8
+ export declare class CloudError extends Error {
9
+ readonly code: CloudErrorCode;
10
+ readonly details?: Record<string, unknown> | undefined;
11
+ constructor(code: CloudErrorCode, message: string, details?: Record<string, unknown> | undefined);
12
+ }
13
+ export declare function isCloudError(err: unknown): err is CloudError;
14
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GACtB,cAAc,GACd,WAAW,GACX,YAAY,GACZ,gBAAgB,GAChB,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,GACnB,eAAe,GACf,WAAW,GACX,gBAAgB,GAChB,sBAAsB,GACtB,UAAU,CAAC;AAEf,qBAAa,UAAW,SAAQ,KAAK;aAEjB,IAAI,EAAE,cAAc;aAEpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFjC,IAAI,EAAE,cAAc,EACpC,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAKpD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,UAAU,CAE5D"}
@@ -0,0 +1,13 @@
1
+ export class CloudError extends Error {
2
+ code;
3
+ details;
4
+ constructor(code, message, details) {
5
+ super(message);
6
+ this.code = code;
7
+ this.details = details;
8
+ this.name = 'CloudError';
9
+ }
10
+ }
11
+ export function isCloudError(err) {
12
+ return err instanceof CloudError;
13
+ }
@@ -0,0 +1,19 @@
1
+ export * from './types.js';
2
+ export * from './errors.js';
3
+ export * from './substrate.js';
4
+ export * from './substrate-factory.js';
5
+ export * from './polling.js';
6
+ export { createE2bSubstrate } from './substrates/e2b.js';
7
+ export { filterSecretsEnv } from './secrets-filter.js';
8
+ export type { Registry } from './registry.js';
9
+ export { startCone, reserveSlot } from './operations/start.js';
10
+ export type { StartConeOpts, StartConeDeps, ReserveSlotOpts } from './operations/start.js';
11
+ export { listCones } from './operations/list.js';
12
+ export type { ListConesDeps, ListConesOpts } from './operations/list.js';
13
+ export { pauseCone } from './operations/pause.js';
14
+ export type { PauseConeDeps } from './operations/pause.js';
15
+ export { resumeCone } from './operations/resume.js';
16
+ export type { ResumeConeOpts, ResumeConeDeps } from './operations/resume.js';
17
+ export { killCone } from './operations/kill.js';
18
+ export type { KillConeDeps, KillConeResult } from './operations/kill.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,14 @@
1
+ // Public re-exports for the @slicc/cloud-core package.
2
+ // Populated by subsequent tasks as we move code in.
3
+ export * from './types.js';
4
+ export * from './errors.js';
5
+ export * from './substrate.js';
6
+ export * from './substrate-factory.js';
7
+ export * from './polling.js';
8
+ export { createE2bSubstrate } from './substrates/e2b.js';
9
+ export { filterSecretsEnv } from './secrets-filter.js';
10
+ export { startCone, reserveSlot } from './operations/start.js';
11
+ export { listCones } from './operations/list.js';
12
+ export { pauseCone } from './operations/pause.js';
13
+ export { resumeCone } from './operations/resume.js';
14
+ export { killCone } from './operations/kill.js';
@@ -0,0 +1,18 @@
1
+ import type { Registry } from '../registry.js';
2
+ import type { SandboxSubstrate } from '../substrate.js';
3
+ export interface KillConeDeps {
4
+ substrate: SandboxSubstrate;
5
+ registry: Registry;
6
+ }
7
+ export interface KillConeResult {
8
+ sandboxId: string;
9
+ alreadyDead: boolean;
10
+ }
11
+ /**
12
+ * Kill a cloud cone and remove it from the registry.
13
+ * Discriminates between "not found" substrate errors (sandbox is already gone,
14
+ * safe to clean up registry) and other errors (network issues, auth failures, etc.)
15
+ * which are re-thrown to surface the problem without silently leaking credits.
16
+ */
17
+ export declare function killCone(deps: KillConeDeps, query: string): Promise<KillConeResult>;
18
+ //# sourceMappingURL=kill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill.d.ts","sourceRoot":"","sources":["../../../src/operations/kill.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA0BzF"}
@@ -0,0 +1,32 @@
1
+ import { CloudError } from '../errors.js';
2
+ /**
3
+ * Kill a cloud cone and remove it from the registry.
4
+ * Discriminates between "not found" substrate errors (sandbox is already gone,
5
+ * safe to clean up registry) and other errors (network issues, auth failures, etc.)
6
+ * which are re-thrown to surface the problem without silently leaking credits.
7
+ */
8
+ export async function killCone(deps, query) {
9
+ const entry = await deps.registry.findByNameOrId(query);
10
+ if (!entry)
11
+ throw new CloudError('NOT_FOUND', `cloud session not found: ${query}`);
12
+ let alreadyDead = false;
13
+ try {
14
+ const handle = await deps.substrate.connect(entry.sandboxId);
15
+ await handle.kill();
16
+ }
17
+ catch (err) {
18
+ const msg = err instanceof Error ? err.message : String(err);
19
+ // Only proceed with registry cleanup if substrate reports "not found".
20
+ // Other errors (timeouts, auth failures) might leave a sandbox running;
21
+ // surface them so the user doesn't silently leak credits.
22
+ const notFound = /not found|unknown sandbox|404|does not exist/i.test(msg);
23
+ if (!notFound) {
24
+ throw new CloudError('INTERNAL', `substrate.kill failed (sandbox ${entry.sandboxId}): ${msg}. ` +
25
+ `Registry entry NOT removed — verify sandbox state manually.`);
26
+ }
27
+ // else: substrate doesn't know about it; registry cleanup proceeds below.
28
+ alreadyDead = true;
29
+ }
30
+ await deps.registry.remove(entry.sandboxId);
31
+ return { sandboxId: entry.sandboxId, alreadyDead };
32
+ }
@@ -0,0 +1,25 @@
1
+ import type { Registry } from '../registry.js';
2
+ import type { SandboxSubstrate } from '../substrate.js';
3
+ import type { ConeEntry } from '../types.js';
4
+ export interface ListConesDeps {
5
+ substrate: SandboxSubstrate;
6
+ registry: Registry;
7
+ }
8
+ export interface ListConesOpts {
9
+ /**
10
+ * Restrict substrate.list to sandboxes whose metadata matches.
11
+ * Worker passes { userId } to scope per-user. CLI passes nothing
12
+ * (sees every sandbox in the team account).
13
+ */
14
+ metadata?: Record<string, string>;
15
+ }
16
+ /**
17
+ * List cones reconciling registry against substrate.
18
+ * - Substrate is source of truth for state (running/paused).
19
+ * - Registry entries missing from substrate → marked 'dead'.
20
+ * - Substrate sandboxes not in registry → rebuilt and appended (orphan recovery).
21
+ *
22
+ * Reconciliation writes are persisted to registry (state flips, entry adds).
23
+ */
24
+ export declare function listCones(deps: ListConesDeps, opts?: ListConesOpts): Promise<ConeEntry[]>;
25
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/operations/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,aAAa,EACnB,IAAI,GAAE,aAAkB,GACvB,OAAO,CAAC,SAAS,EAAE,CAAC,CA8ItB"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * List cones reconciling registry against substrate.
3
+ * - Substrate is source of truth for state (running/paused).
4
+ * - Registry entries missing from substrate → marked 'dead'.
5
+ * - Substrate sandboxes not in registry → rebuilt and appended (orphan recovery).
6
+ *
7
+ * Reconciliation writes are persisted to registry (state flips, entry adds).
8
+ */
9
+ export async function listCones(deps, opts = {}) {
10
+ const registryEntries = await deps.registry.list();
11
+ // Pass metadata filter to substrate.list for server-side filtering
12
+ const live = await deps.substrate.list(opts.metadata ? { metadata: opts.metadata } : undefined);
13
+ const liveById = new Map(live.map((s) => [s.sandboxId, s]));
14
+ // Helper to check if an entry matches the metadata filter
15
+ const matchesFilter = (entry) => {
16
+ if (!opts.metadata)
17
+ return true;
18
+ if (!entry.metadata)
19
+ return false;
20
+ for (const [k, v] of Object.entries(opts.metadata)) {
21
+ if (entry.metadata[k] !== v)
22
+ return false;
23
+ }
24
+ return true;
25
+ };
26
+ // Pass 1: walk registry; reconcile against live.
27
+ // Reconciliation runs for EVERY registry entry regardless of metadata filter
28
+ // — otherwise zombie entries (e.g. legacy entries without userId metadata)
29
+ // never get marked dead and accumulate forever in cap math. The metadata
30
+ // filter is applied to the RETURN value only, so callers still see their
31
+ // per-user view.
32
+ const reconciled = [];
33
+ for (const entry of registryEntries) {
34
+ // Reserved entries: check for stale reservations (TTL: 10 min) and reclaim.
35
+ // Stale reservations are from crashed operations that never completed or rolled back.
36
+ if (entry.state === 'reserved') {
37
+ const STALE_MS = 10 * 60 * 1000;
38
+ // Reclaim if stale OR if reservedAt is missing (legacy/malformed entry
39
+ // from before commit e9011ba6 — could otherwise wedge cap forever).
40
+ const isStale = !entry.reservedAt || Date.now() - new Date(entry.reservedAt).getTime() > STALE_MS;
41
+ if (isStale) {
42
+ await deps.registry.remove(entry.sandboxId);
43
+ console.warn('[cloud-core] reclaimed stale reservation', {
44
+ sandboxId: entry.sandboxId,
45
+ reservedAt: entry.reservedAt ?? '(missing)',
46
+ });
47
+ continue;
48
+ }
49
+ // Active reservation — preserve as-is (in-flight operation holding a cap slot)
50
+ reconciled.push(entry);
51
+ continue;
52
+ }
53
+ const liveEntry = liveById.get(entry.sandboxId);
54
+ if (!liveEntry) {
55
+ // Substrate doesn't know about it — mark dead unless it's a placeholder.
56
+ // The 'pending-' prefix is a sentinel for "no real sandbox yet" (paired
57
+ // with state:'reserved' by reserveSlot before substrate.create).
58
+ if (entry.sandboxId.startsWith('pending-')) {
59
+ // Reservation placeholder — no real sandbox yet. Keep it alive.
60
+ reconciled.push(entry);
61
+ }
62
+ else {
63
+ // Real sandbox is missing — mark dead
64
+ if (entry.state !== 'dead') {
65
+ await deps.registry.update(entry.sandboxId, { state: 'dead' });
66
+ }
67
+ reconciled.push({ ...entry, state: 'dead' });
68
+ }
69
+ continue;
70
+ }
71
+ if (entry.state !== liveEntry.state) {
72
+ await deps.registry.update(entry.sandboxId, { state: liveEntry.state });
73
+ }
74
+ reconciled.push({ ...entry, state: liveEntry.state });
75
+ liveById.delete(entry.sandboxId);
76
+ }
77
+ // Pass 2: any substrate entries not in registry → recover.
78
+ for (const summary of liveById.values()) {
79
+ const now = new Date().toISOString();
80
+ // Try to read the real joinUrl from the sandbox. Orphans created before this
81
+ // fix won't have it in metadata, but we can read /tmp/slicc-join.json directly.
82
+ let joinUrl = summary.metadata?.['joinUrl'] ?? '';
83
+ let trayId = summary.metadata?.['trayId'];
84
+ let lastJoinUpdatedAt = summary.metadata?.['lastJoinUpdatedAt'];
85
+ // Recover joinUrl from /tmp/slicc-join.json ONLY when sandbox is running.
86
+ // Calling substrate.connect() on a paused sandbox would RESUME it, which:
87
+ // (a) burns sandbox runtime silently
88
+ // (b) destabilizes the leader chromium mid-list
89
+ // Paused orphans surface with joinUrl='' — UI hides the Open button.
90
+ // User explicitly resumes via /api/cloud/resume which writes a fresh joinUrl.
91
+ if (!joinUrl && summary.state === 'running') {
92
+ try {
93
+ const handle = await deps.substrate.connect(summary.sandboxId);
94
+ const joinData = await handle.readFile('/tmp/slicc-join.json');
95
+ const parsed = JSON.parse(joinData);
96
+ joinUrl = parsed.joinUrl ?? '';
97
+ trayId = trayId ?? parsed.trayId;
98
+ lastJoinUpdatedAt = lastJoinUpdatedAt ?? parsed.updatedAt;
99
+ }
100
+ catch (err) {
101
+ // File not readable (sandbox is transitioning, or file doesn't exist).
102
+ // Leave joinUrl empty — UI will handle gracefully.
103
+ const msg = err instanceof Error ? err.message : String(err);
104
+ console.warn('[cloud-core] orphan recovery readFile failed', {
105
+ sandboxId: summary.sandboxId,
106
+ err: msg,
107
+ });
108
+ }
109
+ }
110
+ const recovered = {
111
+ sandboxId: summary.sandboxId,
112
+ substrate: 'e2b',
113
+ name: summary.metadata?.['name'] ?? summary.name,
114
+ createdAt: summary.metadata?.['createdAt'] ?? now,
115
+ joinUrl,
116
+ lastSeen: now,
117
+ state: summary.state,
118
+ trayId,
119
+ lastJoinUpdatedAt,
120
+ metadata: summary.metadata,
121
+ };
122
+ await deps.registry.append(recovered);
123
+ reconciled.push(recovered);
124
+ }
125
+ // Refresh the timeout of every running cone so active users keep their cones
126
+ // alive past the 1h default. Failures are non-fatal — a sandbox that
127
+ // disappears between substrate.list() and extendTimeout() will be discovered
128
+ // dead on the NEXT list call.
129
+ const DEFAULT_TTL_MS = 60 * 60 * 1000;
130
+ await Promise.all(reconciled
131
+ .filter((c) => c.state === 'running')
132
+ .map(async (c) => {
133
+ try {
134
+ await deps.substrate.extendTimeout(c.sandboxId, DEFAULT_TTL_MS);
135
+ }
136
+ catch (err) {
137
+ // Sandbox may have died between list and extendTimeout; ignore.
138
+ const msg = err instanceof Error ? err.message : String(err);
139
+ console.warn('[cloud-core] extendTimeout failed', { sandboxId: c.sandboxId, err: msg });
140
+ }
141
+ }));
142
+ // Apply the metadata filter to the RETURN value only — reconciliation
143
+ // above ran on all entries to keep zombies from accumulating.
144
+ return reconciled.filter(matchesFilter);
145
+ }
@@ -0,0 +1,15 @@
1
+ import type { Registry } from '../registry.js';
2
+ import type { SandboxSubstrate } from '../substrate.js';
3
+ export interface PauseConeDeps {
4
+ substrate: SandboxSubstrate;
5
+ registry: Registry;
6
+ }
7
+ /**
8
+ * Pause a running cloud cone. Fails if the cone is not found or already paused.
9
+ * Updates registry state to 'paused' and lastSeen timestamp.
10
+ *
11
+ * Note: trayId and lastJoinUpdatedAt are deliberately NOT overwritten during pause,
12
+ * as they are baseline values for the next resume operation to detect tray rebuilds.
13
+ */
14
+ export declare function pauseCone(deps: PauseConeDeps, query: string): Promise<void>;
15
+ //# sourceMappingURL=pause.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pause.d.ts","sourceRoot":"","sources":["../../../src/operations/pause.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBjF"}
@@ -0,0 +1,27 @@
1
+ import { CloudError } from '../errors.js';
2
+ /**
3
+ * Pause a running cloud cone. Fails if the cone is not found or already paused.
4
+ * Updates registry state to 'paused' and lastSeen timestamp.
5
+ *
6
+ * Note: trayId and lastJoinUpdatedAt are deliberately NOT overwritten during pause,
7
+ * as they are baseline values for the next resume operation to detect tray rebuilds.
8
+ */
9
+ export async function pauseCone(deps, query) {
10
+ const entry = await deps.registry.findByNameOrId(query);
11
+ if (!entry)
12
+ throw new CloudError('NOT_FOUND', `cloud session not found: ${query}`);
13
+ if (entry.state === 'reserved') {
14
+ throw new CloudError('ALREADY_RUNNING', `cloud session is being started/resumed: ${query}`);
15
+ }
16
+ if (entry.state === 'paused') {
17
+ throw new CloudError('ALREADY_PAUSED', `cloud session is already paused: ${query}`);
18
+ }
19
+ const handle = await deps.substrate.connect(entry.sandboxId);
20
+ await handle.pause();
21
+ // Update ONLY state + lastSeen. trayId and lastJoinUpdatedAt are baseline
22
+ // values for the next resume — preserving them is load-bearing.
23
+ await deps.registry.update(entry.sandboxId, {
24
+ state: 'paused',
25
+ lastSeen: new Date().toISOString(),
26
+ });
27
+ }
@@ -0,0 +1,22 @@
1
+ import type { Registry } from '../registry.js';
2
+ import type { SandboxSubstrate } from '../substrate.js';
3
+ import type { ResumeResult } from '../types.js';
4
+ export interface ResumeConeDeps {
5
+ substrate: SandboxSubstrate;
6
+ registry: Registry;
7
+ }
8
+ export interface ResumeConeOpts {
9
+ query: string;
10
+ localSliccVersion: string;
11
+ /** Optional: if provided, written to /slicc/secrets.env AFTER substrate.connect
12
+ * and BEFORE the leader-restart kick. Both CLI (cloud/resume.ts) and worker
13
+ * (cloud-sessions-do.ts) pass this unconditionally now to inject a fresh IMS bearer. */
14
+ refreshSecretsContents?: string;
15
+ pollIntervalMs?: number;
16
+ pollTimeoutMs?: number;
17
+ /** Skip the state check (NOT_FOUND + ALREADY_RUNNING). Used by the worker
18
+ * after doing atomic cap-check + state-flip under DO lock. */
19
+ skipStateCheck?: boolean;
20
+ }
21
+ export declare function resumeCone(deps: ResumeConeDeps, opts: ResumeConeOpts): Promise<ResumeResult>;
22
+ //# sourceMappingURL=resume.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/operations/resume.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;4FAEwF;IACxF,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;kEAC8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AASD,wBAAsB,UAAU,CAC9B,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,YAAY,CAAC,CA6EvB"}
@@ -0,0 +1,86 @@
1
+ import { CloudError } from '../errors.js';
2
+ import { pollForRefreshedStatus } from '../polling.js';
3
+ // curl writes its body to /dev/null and prints the HTTP status code on stdout.
4
+ // We DO want curl to return non-zero on connection errors (so the retry loop
5
+ // can distinguish "node-server not up yet" from "200/503 response received").
6
+ // Therefore: no `|| true`; we parse status from stdout AND check exitCode.
7
+ const KICK_CMD = 'curl -sS -X POST http://localhost:5710/api/leader-restart -o /dev/null -w "%{http_code}"';
8
+ export async function resumeCone(deps, opts) {
9
+ // skipStateCheck is used by the worker after doing atomic cap-check + state-flip
10
+ // under DO lock. CLI still wants the NOT_FOUND + ALREADY_RUNNING checks.
11
+ if (!opts.skipStateCheck) {
12
+ const entry = await deps.registry.findByNameOrId(opts.query);
13
+ if (!entry)
14
+ throw new CloudError('NOT_FOUND', `cloud session not found: ${opts.query}`);
15
+ // NEW: ALREADY_RUNNING check (additive — existing CLI resume didn't have this).
16
+ // Accept both 'running' and 'reserved' as "already in flight".
17
+ if (entry.state === 'running' || entry.state === 'reserved') {
18
+ throw new CloudError('ALREADY_RUNNING', `cloud session is already running: ${opts.query}`);
19
+ }
20
+ }
21
+ // After state checks, fetch the entry again for baseline data (the worker's
22
+ // precheck already validated it exists).
23
+ const entry = await deps.registry.findByNameOrId(opts.query);
24
+ if (!entry)
25
+ throw new CloudError('NOT_FOUND', `cloud session not found: ${opts.query}`);
26
+ // Baseline from the registry — `startCone` stored these at create, and
27
+ // `pauseCone` preserves them across pause. Resume requires a strictly
28
+ // newer `updatedAt` than `entry.lastJoinUpdatedAt`, so we only declare
29
+ // success once the kick has produced a fresh refresh.
30
+ const baselineUpdatedAt = entry.lastJoinUpdatedAt;
31
+ const baselineTrayId = entry.trayId;
32
+ const handle = await deps.substrate.connect(entry.sandboxId);
33
+ // refreshSecretsContents: both CLI and worker pass this now to inject a fresh
34
+ // IMS bearer. Write to /slicc/secrets.env BEFORE the kick loop.
35
+ if (opts.refreshSecretsContents !== undefined) {
36
+ await handle.writeFile('/slicc/secrets.env', opts.refreshSecretsContents);
37
+ }
38
+ // Kick the leader to recover from a possible onReconnectGaveUp state.
39
+ // 5×1s retry covers the CDP-cold-start race after a long pause.
40
+ // Success = curl exited 0 AND status is 200. 503 means the SLICC page
41
+ // target isn't ready yet — retry. Any other status is a hard error.
42
+ const kicked = await kickLeaderUntilReady(handle);
43
+ if (!kicked) {
44
+ throw new CloudError('LEADER_NOT_READY', 'Failed to kick leader after 5 retries (sandbox may not be healthy)');
45
+ }
46
+ const refreshed = await pollForRefreshedStatus(handle, baselineUpdatedAt, {
47
+ timeoutMs: opts.pollTimeoutMs ?? 60_000,
48
+ intervalMs: opts.pollIntervalMs ?? 500,
49
+ });
50
+ // Tray rebuilt iff we had a baseline AND the new trayId is different.
51
+ // (No baseline → can't tell, default to false so we don't spuriously
52
+ // warn on a freshly-created sandbox where the registry was wiped.)
53
+ const trayRebuilt = Boolean(baselineTrayId && refreshed.trayId && baselineTrayId !== refreshed.trayId);
54
+ const versionMismatch = refreshed.sliccVersion && refreshed.sliccVersion !== opts.localSliccVersion
55
+ ? { running: refreshed.sliccVersion, local: opts.localSliccVersion }
56
+ : undefined;
57
+ // Write the new baseline back — same fields startCone populated.
58
+ await deps.registry.update(entry.sandboxId, {
59
+ joinUrl: refreshed.joinUrl,
60
+ lastSeen: new Date().toISOString(),
61
+ state: 'running',
62
+ trayId: refreshed.trayId,
63
+ lastJoinUpdatedAt: refreshed.updatedAt,
64
+ });
65
+ return {
66
+ sandboxId: entry.sandboxId,
67
+ joinUrl: refreshed.joinUrl,
68
+ trayRebuilt,
69
+ ...(versionMismatch ? { versionMismatch } : {}),
70
+ };
71
+ }
72
+ async function kickLeaderUntilReady(handle) {
73
+ for (let i = 0; i < 5; i++) {
74
+ const result = await handle.run(KICK_CMD);
75
+ if (result.exitCode === 0) {
76
+ const status = result.stdout.trim();
77
+ if (status === '200')
78
+ return true;
79
+ if (status !== '503') {
80
+ throw new CloudError('LEADER_NOT_READY', `/api/leader-restart returned unexpected status ${status}`);
81
+ }
82
+ }
83
+ await new Promise((r) => setTimeout(r, 1000));
84
+ }
85
+ return false;
86
+ }
@@ -0,0 +1,67 @@
1
+ import type { Registry } from '../registry.js';
2
+ import type { SandboxSubstrate } from '../substrate.js';
3
+ import type { ConeEntry, StartResult } from '../types.js';
4
+ export interface StartConeOpts {
5
+ /** Full secrets.env content (caller reads from disk in CLI; constructs in
6
+ * worker). Will be filtered with filterSecretsEnv before upload. */
7
+ envContents: string;
8
+ /** Tray worker base URL injected into the sandbox env. */
9
+ workerBaseUrl: string;
10
+ /** Substrate template ID (default 'slicc'). */
11
+ template?: string;
12
+ /** Optional user-supplied name; goes into substrate.metadata.name. */
13
+ name?: string;
14
+ /** SLICC version recorded on the registry entry. */
15
+ sliccVersion: string;
16
+ /** Additional metadata tagged on the sandbox (e.g., { userId, email } in
17
+ * worker context). Merged on top of the sandbox metadata. */
18
+ metadata?: Record<string, string>;
19
+ /** Extra envs passed at substrate.create. start.sh writes /slicc/secrets.env
20
+ * from these BEFORE node-server boots (no race). Plan B task. */
21
+ envs?: Record<string, string>;
22
+ /** Poll budget for waiting on /tmp/slicc-join.json. */
23
+ pollTimeoutMs?: number;
24
+ pollIntervalMs?: number;
25
+ /** Default true. */
26
+ autoPauseOnCap?: boolean;
27
+ /** Optional reservation ID from reserveSlot(); if provided, updates that
28
+ * placeholder entry instead of appending a new one. */
29
+ reservationId?: string;
30
+ }
31
+ export interface StartConeDeps {
32
+ substrate: SandboxSubstrate;
33
+ registry: Registry;
34
+ }
35
+ export interface ReserveSlotOpts {
36
+ /** User ID for filtering (worker use) or undefined (CLI use). */
37
+ userId?: string;
38
+ /** Optional name; checked for conflicts. */
39
+ name?: string;
40
+ /** Metadata to store on the reservation entry. */
41
+ metadata?: Record<string, string>;
42
+ /** SLICC version recorded on the registry entry. */
43
+ sliccVersion: string;
44
+ /** Environment for cap checking. */
45
+ env?: {
46
+ CONE_CAP_RUNNING: string;
47
+ CONE_CAP_PAUSED: string;
48
+ };
49
+ /** Pre-reconciled cone list (from listCones). If provided, skips the slow
50
+ * reconciliation call inside reserveSlot, making it fast enough to fit under
51
+ * blockConcurrencyWhile. Worker callers MUST pass this to avoid holding the
52
+ * DO lock through substrate.list(). */
53
+ reconciledCones?: ConeEntry[];
54
+ }
55
+ /**
56
+ * Reserve a slot in the registry atomically under DO lock, BEFORE substrate.create.
57
+ * Returns a synthetic reservationId (pending-<uuid>) that counts toward the cap.
58
+ * Throws CloudError('CAP_EXCEEDED' | 'NAME_TAKEN') on conflict.
59
+ *
60
+ * Callers MUST wrap this in blockConcurrencyWhile so two concurrent calls
61
+ * serialize and the second sees the first's placeholder.
62
+ */
63
+ export declare function reserveSlot(deps: StartConeDeps, opts: ReserveSlotOpts): Promise<{
64
+ reservationId: string;
65
+ }>;
66
+ export declare function startCone(deps: StartConeDeps, opts: StartConeOpts): Promise<StartResult>;
67
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/operations/start.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAiB,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,aAAa;IAC5B;wEACoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB;iEAC6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;qEACiE;IACjE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,uDAAuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;2DACuD;IACvD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,GAAG,CAAC,EAAE;QACJ,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF;;;2CAGuC;IACvC,eAAe,CAAC,EAAE,SAAS,EAAE,CAAC;CAC/B;AA6BD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,aAAa,EACnB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAwDpC;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAkJ9F"}