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.
- package/README.md +1 -2
- package/dist/node-server/_cloud_core/src/errors.d.ts +14 -0
- package/dist/node-server/_cloud_core/src/errors.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/errors.js +13 -0
- package/dist/node-server/_cloud_core/src/index.d.ts +19 -0
- package/dist/node-server/_cloud_core/src/index.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/index.js +14 -0
- package/dist/node-server/_cloud_core/src/operations/kill.d.ts +18 -0
- package/dist/node-server/_cloud_core/src/operations/kill.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/operations/kill.js +32 -0
- package/dist/node-server/_cloud_core/src/operations/list.d.ts +25 -0
- package/dist/node-server/_cloud_core/src/operations/list.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/operations/list.js +145 -0
- package/dist/node-server/_cloud_core/src/operations/pause.d.ts +15 -0
- package/dist/node-server/_cloud_core/src/operations/pause.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/operations/pause.js +27 -0
- package/dist/node-server/_cloud_core/src/operations/resume.d.ts +22 -0
- package/dist/node-server/_cloud_core/src/operations/resume.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/operations/resume.js +86 -0
- package/dist/node-server/_cloud_core/src/operations/start.d.ts +67 -0
- package/dist/node-server/_cloud_core/src/operations/start.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/operations/start.js +228 -0
- package/dist/node-server/_cloud_core/src/polling.d.ts +18 -0
- package/dist/node-server/_cloud_core/src/polling.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/polling.js +79 -0
- package/dist/node-server/_cloud_core/src/registry.d.ts +27 -0
- package/dist/node-server/_cloud_core/src/registry.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/registry.js +1 -0
- package/dist/node-server/_cloud_core/src/secrets-filter.d.ts +2 -0
- package/dist/node-server/_cloud_core/src/secrets-filter.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/secrets-filter.js +17 -0
- package/dist/node-server/_cloud_core/src/substrate-factory.d.ts +3 -0
- package/dist/node-server/_cloud_core/src/substrate-factory.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/substrate-factory.js +6 -0
- package/dist/node-server/_cloud_core/src/substrate.d.ts +53 -0
- package/dist/node-server/_cloud_core/src/substrate.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/substrate.js +1 -0
- package/dist/node-server/_cloud_core/src/substrates/e2b.d.ts +3 -0
- package/dist/node-server/_cloud_core/src/substrates/e2b.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/substrates/e2b.js +98 -0
- package/dist/node-server/_cloud_core/src/types.d.ts +94 -0
- package/dist/node-server/_cloud_core/src/types.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/types.js +3 -0
- package/dist/node-server/_cloud_core/tests/fixtures/fake-substrate.d.ts +39 -0
- package/dist/node-server/_cloud_core/tests/fixtures/fake-substrate.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/fixtures/fake-substrate.js +75 -0
- package/dist/node-server/_cloud_core/tests/fixtures/index.d.ts +4 -0
- package/dist/node-server/_cloud_core/tests/fixtures/index.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/fixtures/index.js +2 -0
- package/dist/node-server/_cloud_core/tests/fixtures/mem-registry.d.ts +10 -0
- package/dist/node-server/_cloud_core/tests/fixtures/mem-registry.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/fixtures/mem-registry.js +25 -0
- package/dist/node-server/_cloud_core/tests/kill.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/kill.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/kill.test.js +164 -0
- package/dist/node-server/_cloud_core/tests/list.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/list.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/list.test.js +300 -0
- package/dist/node-server/_cloud_core/tests/pause.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/pause.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/pause.test.js +167 -0
- package/dist/node-server/_cloud_core/tests/resume.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/resume.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/resume.test.js +203 -0
- package/dist/node-server/_cloud_core/tests/secrets-filter.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/secrets-filter.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/secrets-filter.test.js +30 -0
- package/dist/node-server/_cloud_core/tests/start.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/start.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/start.test.js +513 -0
- package/dist/node-server/chrome-launch.d.ts +1 -0
- package/dist/node-server/chrome-launch.js +3 -0
- package/dist/node-server/cloud/dispatch.d.ts +34 -0
- package/dist/node-server/cloud/dispatch.js +79 -0
- package/dist/node-server/cloud/kill.d.ts +12 -0
- package/dist/node-server/cloud/kill.js +11 -0
- package/dist/node-server/cloud/list.d.ts +6 -0
- package/dist/node-server/cloud/list.js +6 -0
- package/dist/node-server/cloud/pause.d.ts +7 -0
- package/dist/node-server/cloud/pause.js +6 -0
- package/dist/node-server/cloud/registry-file.d.ts +13 -0
- package/dist/node-server/cloud/registry-file.js +85 -0
- package/dist/node-server/cloud/resume.d.ts +11 -0
- package/dist/node-server/cloud/resume.js +17 -0
- package/dist/node-server/cloud/start.d.ts +16 -0
- package/dist/node-server/cloud/start.js +49 -0
- package/dist/node-server/cloud-status.d.ts +11 -0
- package/dist/node-server/cloud-status.js +49 -0
- package/dist/node-server/hosted-bootstrap.d.ts +21 -0
- package/dist/node-server/hosted-bootstrap.js +14 -0
- package/dist/node-server/index.js +146 -3
- package/dist/node-server/leader-restart.d.ts +29 -0
- package/dist/node-server/leader-restart.js +146 -0
- package/dist/node-server/runtime-flags.d.ts +1 -0
- package/dist/node-server/runtime-flags.js +6 -0
- package/dist/ui/assets/adobe-BLY_1hN7.js +1 -0
- package/dist/ui/assets/adobe-BZS2Kmci.js +2 -0
- package/dist/ui/assets/agent-bridge-BFRFxJAl.js +1 -0
- package/dist/ui/assets/{agent-message-to-chat-H07zq-Kv.js → agent-message-to-chat-Cd-DQATg.js} +1 -1
- package/dist/ui/assets/anthropic-BbwUhO9H.js +39 -0
- package/dist/ui/assets/{anthropic-BK3Od7MT.js → anthropic-Dg9_3G1u.js} +3 -3
- package/dist/ui/assets/{apps-mZJxOz_q.js → apps-BQuoIsxV.js} +1 -1
- package/dist/ui/assets/azure-openai-CilK3M_Q.js +1 -0
- package/dist/ui/assets/{azure-openai-Dch5aR9j.js → azure-openai-CleJGzBE.js} +1 -1
- package/dist/ui/assets/azure-openai-responses-DMLD0Ano.js +1 -0
- package/dist/ui/assets/azure-openai-responses-_joBD1ru.js +1 -0
- package/dist/ui/assets/{bsh-watchdog-i5GXbvla.js → bsh-watchdog-CpJwB685.js} +1 -1
- package/dist/ui/assets/{cdp-Cv6W51Ld.js → cdp-BXSNdZRM.js} +3 -3
- package/dist/ui/assets/cloud-CCHD45tb.js +1 -0
- package/dist/ui/assets/cloud-DyAca2cv.css +1 -0
- package/dist/ui/assets/cost-command-Bv5UQVUC.js +1 -0
- package/dist/ui/assets/{dist-cK1g1pHF.js → dist-ChvbMSOY.js} +1 -1
- package/dist/ui/assets/{dist-BTGs16AV.js → dist-CltZJzbd.js} +1 -1
- package/dist/ui/assets/{dist-BcBxL8nL.js → dist-cZSF0917.js} +1 -1
- package/dist/ui/assets/{dist-Bgcuvc8h.js → dist-ljtB3O-J.js} +1 -1
- package/dist/ui/assets/{dist-EkY3RL7q.js → dist-tLLu0PBg.js} +1 -1
- package/dist/ui/assets/{es-Crcyr8Z7.js → es-C2wm9v2H.js} +1 -1
- package/dist/ui/assets/follower-sprinkle-bridge-Bg1mCcnG.js +1 -0
- package/dist/ui/assets/{github-DKcpc18I.js → github-BDzC2pCD.js} +1 -1
- package/dist/ui/assets/github-BzwlgA3s.js +2 -0
- package/dist/ui/assets/{google-IZXg3PJn.js → google-BVxsfaV3.js} +1 -1
- package/dist/ui/assets/google-C0Zd1qsl.js +1 -0
- package/dist/ui/assets/google-shared-DcZhZ_Z7.js +11 -0
- package/dist/ui/assets/{google-shared-BaIigNCs.js → google-shared-KyLYJrXa.js} +1 -1
- package/dist/ui/assets/google-vertex-BGpbWEWq.js +1 -0
- package/dist/ui/assets/{google-vertex-DDwmtWoJ.js → google-vertex-BSOalca9.js} +1 -1
- package/dist/ui/assets/{intercepted-oauth-CEgKdcyY.js → intercepted-oauth-D6eAxYca.js} +1 -1
- package/dist/ui/assets/{kernel-worker-dg2bRx7X.js → kernel-worker-D1_EtuZW.js} +1016 -980
- package/dist/ui/assets/{lick-ws-bridge-DgWfdUlv.js → lick-ws-bridge-DXFDOui-.js} +1 -1
- package/dist/ui/assets/{local-llm-DuUGv7wX.js → local-llm-B3qsU55x.js} +1 -1
- package/dist/ui/assets/magick-wasm-CasStH-w.js +1 -0
- package/dist/ui/assets/main-BSfjQXCf.js +1612 -0
- package/dist/ui/assets/{mistral-czd8-m0c.js → mistral-BkhZQqeV.js} +1 -1
- package/dist/ui/assets/{mistral-BS_Gm2MH.js → mistral-D130SR-b.js} +1 -1
- package/dist/ui/assets/modulepreload-polyfill-wMinxHhO.js +1 -0
- package/dist/ui/assets/nuke-command-D_sxu_AA.js +1 -0
- package/dist/ui/assets/{oauth-CJxNn7hC.js → oauth-BocTO4G9.js} +1 -1
- package/dist/ui/assets/oauth-bootstrap-DZdd7KZS.js +2 -0
- package/dist/ui/assets/{oauth-service-BAnfipgD.js → oauth-service-DAWR1CpY.js} +1 -1
- package/dist/ui/assets/oauth-service-y04jhWb_.js +1 -0
- package/dist/ui/assets/offscreen-client-HCLuFFlN.js +1 -0
- package/dist/ui/assets/onboarding-orchestrator-qbEVluIu.js +1 -0
- package/dist/ui/assets/openai-codex-responses-Bmd17Jq9.js +7 -0
- package/dist/ui/assets/openai-codex-responses-CMOclqki.js +7 -0
- package/dist/ui/assets/openai-completions-DmwgEsb4.js +5 -0
- package/dist/ui/assets/openai-responses-BYdEOxUq.js +1 -0
- package/dist/ui/assets/openai-responses-C3bs8sd1.js +1 -0
- package/dist/ui/assets/{openai-responses-shared-DDootAUL.js → openai-responses-shared-AM04ghTC.js} +1 -1
- package/dist/ui/assets/{openai-responses-shared-07lPyxEg.js → openai-responses-shared-Be22tDzQ.js} +1 -1
- package/dist/ui/assets/openrouter-BIFqVYxG.js +1 -0
- package/dist/ui/assets/openrouter-Dc1D0QOS.js +1 -0
- package/dist/ui/assets/panel-rpc-handlers-DDaHHzWR.js +1 -0
- package/dist/ui/assets/{provider-rBD-3FHW.js → provider-CU5ao6kF.js} +2 -2
- package/dist/ui/assets/{provider-BhabAJGA.js → provider-ChUiWhH0.js} +1 -1
- package/dist/ui/assets/provider-store-access-Bliwcbgg.js +1 -0
- package/dist/ui/assets/provider-store-access-iTh_6mMO.js +1 -0
- package/dist/ui/assets/proxied-fetch-8v3TaZYg.js +1 -0
- package/dist/ui/assets/{pyodide-BC4PaR54.js → pyodide-B7x60zRI.js} +2 -2
- package/dist/ui/assets/{remote-terminal-view-CXbFLcuS.js → remote-terminal-view-BREvd00U.js} +1 -1
- package/dist/ui/assets/secret-env-Js2g2sNO.js +1 -0
- package/dist/ui/assets/slicc-editor-Bu07BjW4.js +12 -0
- package/dist/ui/assets/{spawn-qtlw6x6p.js → spawn-CQ5XACZ1.js} +1 -1
- package/dist/ui/assets/{sql-wasm-DeVtn0z8.js → sql-wasm-C-pXJvOw.js} +1 -1
- package/dist/ui/assets/{store-DLeVoNF3.js → store-DQUahHdW.js} +1 -1
- package/dist/ui/assets/tray-leave-runtime-BEBMIYY_.js +1 -0
- package/dist/ui/assets/{upgrade-detection-CKrzeeRx.js → upgrade-detection-T74KTnyb.js} +1 -1
- package/dist/ui/assets/{xai-grok-CNyu04Tg.js → xai-grok-CkPPMxfZ.js} +1 -1
- package/dist/ui/assets/{xai-grok-sanitize-BeI8rdV5.js → xai-grok-sanitize-C7TgWvwD.js} +1 -1
- package/dist/ui/assets/{xai-grok-sanitize-h_8XMTD9.js → xai-grok-sanitize-L5nUbDcW.js} +1 -1
- package/dist/ui/assets/xai-grok-u9MyUN8m.js +1 -0
- package/dist/ui/electron-overlay-entry.js +9 -9
- package/dist/ui/index.html +3 -2
- package/dist/ui/packages/webapp/cloud/index.html +38 -0
- package/dist/ui/packages/webapp/index.html +3 -2
- package/package.json +10 -8
- package/dist/ui/assets/adobe-DpCIcyEb.js +0 -2
- package/dist/ui/assets/adobe-NMBK1vG2.js +0 -1
- package/dist/ui/assets/agent-bridge-D2k69QZ-.js +0 -1
- package/dist/ui/assets/anthropic-ZtfIcLyL.js +0 -39
- package/dist/ui/assets/azure-openai-CN9QA_pv.js +0 -1
- package/dist/ui/assets/azure-openai-responses-Bhr0mNdj.js +0 -1
- package/dist/ui/assets/azure-openai-responses-D400BxTc.js +0 -1
- package/dist/ui/assets/cost-command-BGqK_hbH.js +0 -1
- package/dist/ui/assets/follower-sprinkle-bridge-BayCf20v.js +0 -1
- package/dist/ui/assets/github-DpDjTcq9.js +0 -2
- package/dist/ui/assets/google-B97zVBac.js +0 -1
- package/dist/ui/assets/google-shared-DwzANLbz.js +0 -11
- package/dist/ui/assets/google-vertex-ICKJGS1M.js +0 -1
- package/dist/ui/assets/index-CCGqFzR9.js +0 -1575
- package/dist/ui/assets/magick-wasm-Bu7zx3Ux.js +0 -1
- package/dist/ui/assets/nuke-command-BSkSFZ4H.js +0 -1
- package/dist/ui/assets/oauth-bootstrap-BeZQrd7y.js +0 -2
- package/dist/ui/assets/oauth-service-BX55wTV9.js +0 -1
- package/dist/ui/assets/offscreen-client-DSEhUCxj.js +0 -1
- package/dist/ui/assets/onboarding-orchestrator-DjNti83y.js +0 -1
- package/dist/ui/assets/openai-codex-responses-DJKJH9AX.js +0 -7
- package/dist/ui/assets/openai-codex-responses-jB5RkWeq.js +0 -7
- package/dist/ui/assets/openai-completions-BWWM3S2J.js +0 -5
- package/dist/ui/assets/openai-responses-C_3YpAmj.js +0 -1
- package/dist/ui/assets/openai-responses-tJegTQYT.js +0 -1
- package/dist/ui/assets/panel-rpc-handlers-B4jZTNHO.js +0 -1
- package/dist/ui/assets/provider-store-access-H9Lu5HRL.js +0 -1
- package/dist/ui/assets/provider-store-access-biaB--zG.js +0 -1
- package/dist/ui/assets/proxied-fetch-C6dm2Ugc.js +0 -1
- package/dist/ui/assets/secret-env-s_8cP7Wf.js +0 -1
- package/dist/ui/assets/slicc-editor-pZcWEwTm.js +0 -12
- package/dist/ui/assets/tray-leave-runtime-DqA9PbxV.js +0 -1
- package/dist/ui/assets/xai-grok-CN1nP4TF.js +0 -1
- /package/dist/ui/assets/{active-transport-CfU1UxY6.js → active-transport-Clj9slj7.js} +0 -0
- /package/dist/ui/assets/{active-transport-Dm6CWYVE.js → active-transport-DHcaBd4j.js} +0 -0
- /package/dist/ui/assets/{addon-fit-BDUxDgg1.js → addon-fit-CBG3Hsec.js} +0 -0
- /package/dist/ui/assets/{addon-fit-Dkm5m5XK.js → addon-fit-CUt9lrQO.js} +0 -0
- /package/dist/ui/assets/{azure-ai-foundry-BHLfY_0t.js → azure-ai-foundry-B4QrVklT.js} +0 -0
- /package/dist/ui/assets/{azure-ai-foundry-1pnZpxAJ.js → azure-ai-foundry-CGkSI_4f.js} +0 -0
- /package/dist/ui/assets/{chat-fixture-DOzffHLr.js → chat-fixture-ChGRNFda.js} +0 -0
- /package/dist/ui/assets/{dist-BYoCEWmq.js → dist-9RlRmGag.js} +0 -0
- /package/dist/ui/assets/{dist-BNe7ZIzA.js → dist-CZcDRTma.js} +0 -0
- /package/dist/ui/assets/{dist-BrjVVq8c.js → dist-D0kTaNET.js} +0 -0
- /package/dist/ui/assets/{git-config-CGMfR0WK.js → git-config-DcQbkikl.js} +0 -0
- /package/dist/ui/assets/{github-copilot-headers-CTUP_cqn.js → github-copilot-headers-C5CocKfn.js} +0 -0
- /package/dist/ui/assets/{global-db-B97_fwjD.js → global-db-DL3jdM9H.js} +0 -0
- /package/dist/ui/assets/{hash-B9Dtr1VK.js → hash-iitWbNjK.js} +0 -0
- /package/dist/ui/assets/{headers-AeYLn7L4.js → headers-qDMOQQGF.js} +0 -0
- /package/dist/ui/assets/{lick-manager-proxy-BnnMFbgv.js → lick-manager-proxy-1L0EGIWD.js} +0 -0
- /package/dist/ui/assets/{index-CvlDeETm.css → main-CvlDeETm.css} +0 -0
- /package/dist/ui/assets/{openai-Bf1npfRy.js → openai-BR_xAflc.js} +0 -0
- /package/dist/ui/assets/{page-storage-sync-CqMAh8jG.js → page-storage-sync-sv2ogNKh.js} +0 -0
- /package/dist/ui/assets/{pdfjs-DDDkZHZ4.js → pdfjs-CW51NNE6.js} +0 -0
- /package/dist/ui/assets/{rum-DCU8sePD.js → rum-z5wz4Dnj.js} +0 -0
- /package/dist/ui/assets/{sanitize-unicode-h0KEBmEw.js → sanitize-unicode-B1z_Y4Dq.js} +0 -0
- /package/dist/ui/assets/{sprinkle-bridge-channel-DojIojh7.js → sprinkle-bridge-channel-Bw5UaanD.js} +0 -0
- /package/dist/ui/assets/{sprinkle-bridge-channel-DJC5SGWr.js → sprinkle-bridge-channel-ChD0T07C.js} +0 -0
- /package/dist/ui/assets/{sprinkle-proxy-rF3NSOGq.js → sprinkle-proxy-D6wqmewK.js} +0 -0
- /package/dist/ui/assets/{src-BQRb5emh.js → src-DRmQ1wr-.js} +0 -0
- /package/dist/ui/assets/{xai-grok-errors-Bzn3oNvW.js → xai-grok-errors-BdY43UIn.js} +0 -0
- /package/dist/ui/assets/{xai-grok-errors-WbFsMZum.js → xai-grok-errors-C6VnD_MH.js} +0 -0
- /package/dist/ui/assets/{xai-grok-models-cvcIXmNM.js → xai-grok-models-BndZgIFZ.js} +0 -0
- /package/dist/ui/assets/{xai-grok-models-DMYWtapT.js → xai-grok-models-C9MIa-Aj.js} +0 -0
- /package/dist/ui/assets/{xterm-D8ldLLKi.js → xterm-DLCsxsB1.js} +0 -0
- /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
|
-
[](https://github.com/ai-ecoverse/vibe-coded-badge-action)
|
|
15
15
|
|
|
16
16
|
[](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"}
|