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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets-filter.test.d.ts","sourceRoot":"","sources":["../../tests/secrets-filter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { filterSecretsEnv } from '../src/secrets-filter.js';
|
|
3
|
+
describe('filterSecretsEnv', () => {
|
|
4
|
+
it('strips E2B_API_KEY with various whitespace shapes', () => {
|
|
5
|
+
const input = [
|
|
6
|
+
'ANTHROPIC_API_KEY=sk-test',
|
|
7
|
+
'E2B_API_KEY=plain',
|
|
8
|
+
' E2B_API_KEY=leading-space',
|
|
9
|
+
'E2B_API_KEY =extra-space-before-eq',
|
|
10
|
+
'E2B_API_KEY_DOMAINS=e2b.dev',
|
|
11
|
+
'\tE2B_API_KEY=tab-prefixed',
|
|
12
|
+
].join('\n');
|
|
13
|
+
const out = filterSecretsEnv(input);
|
|
14
|
+
expect(out).toContain('ANTHROPIC_API_KEY=sk-test');
|
|
15
|
+
// All four E2B_API_KEY variants must be stripped:
|
|
16
|
+
expect(out).not.toContain('E2B_API_KEY=plain');
|
|
17
|
+
expect(out).not.toContain('E2B_API_KEY=leading-space');
|
|
18
|
+
expect(out).not.toContain('E2B_API_KEY=extra-space-before-eq');
|
|
19
|
+
expect(out).not.toContain('E2B_API_KEY_DOMAINS=e2b.dev');
|
|
20
|
+
expect(out).not.toContain('E2B_API_KEY=tab-prefixed');
|
|
21
|
+
});
|
|
22
|
+
it('preserves comments and empty lines', () => {
|
|
23
|
+
const input = '# important comment\n\nANTHROPIC_API_KEY=x\nE2B_API_KEY=strip\n';
|
|
24
|
+
const out = filterSecretsEnv(input);
|
|
25
|
+
expect(out).toContain('# important comment');
|
|
26
|
+
expect(out.split('\n').some((l) => l === '')).toBe(true);
|
|
27
|
+
expect(out).toContain('ANTHROPIC_API_KEY=x');
|
|
28
|
+
expect(out).not.toContain('E2B_API_KEY=strip');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.test.d.ts","sourceRoot":"","sources":["../../tests/start.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { startCone, reserveSlot } from '../src/operations/start.js';
|
|
3
|
+
import { MemRegistry, makeFakeHandle, makeFakeSubstrate } from './fixtures/index.js';
|
|
4
|
+
// Specialized for startCone: handle stores files per-create, tracks kill state for list result.
|
|
5
|
+
function makeStartTestSubstrate(opts) {
|
|
6
|
+
const files = new Map();
|
|
7
|
+
let killed = false;
|
|
8
|
+
return {
|
|
9
|
+
id: 'e2b',
|
|
10
|
+
async create(_opts) {
|
|
11
|
+
const sandboxId = 'sbx-fake';
|
|
12
|
+
files.set('/tmp/slicc-join.json', opts.joinJson);
|
|
13
|
+
const handle = makeFakeHandle({ sandboxId });
|
|
14
|
+
return {
|
|
15
|
+
sandboxId: handle.sandboxId,
|
|
16
|
+
substrate: handle.substrate,
|
|
17
|
+
pause: handle.pause,
|
|
18
|
+
kill: async () => {
|
|
19
|
+
killed = true;
|
|
20
|
+
},
|
|
21
|
+
getInfo: handle.getInfo,
|
|
22
|
+
writeFile: handle.writeFile,
|
|
23
|
+
readFile: async (path) => {
|
|
24
|
+
const content = files.get(path);
|
|
25
|
+
if (!content)
|
|
26
|
+
throw new Error(`ENOENT ${path}`);
|
|
27
|
+
return content;
|
|
28
|
+
},
|
|
29
|
+
run: handle.run,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
async connect() {
|
|
33
|
+
throw new Error('not used in startCone tests');
|
|
34
|
+
},
|
|
35
|
+
async list(_opts) {
|
|
36
|
+
if (killed)
|
|
37
|
+
return [];
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
sandboxId: 'sbx-fake',
|
|
41
|
+
state: 'running',
|
|
42
|
+
metadata: {},
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
},
|
|
46
|
+
async extendTimeout(_sandboxId, _ttlMs) {
|
|
47
|
+
// No-op for tests.
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
describe('reserveSlot', () => {
|
|
52
|
+
it('throws CAP_EXCEEDED when paused cap is at limit', async () => {
|
|
53
|
+
const registry = new MemRegistry();
|
|
54
|
+
// Seed CONE_CAP_PAUSED=2 paused cones in both registry and substrate.
|
|
55
|
+
// listCones reconciles them. No userId filter so all cones count.
|
|
56
|
+
for (let i = 0; i < 2; i++) {
|
|
57
|
+
await registry.append({
|
|
58
|
+
sandboxId: `s${i}`,
|
|
59
|
+
substrate: 'e2b',
|
|
60
|
+
createdAt: '',
|
|
61
|
+
lastSeen: '',
|
|
62
|
+
state: 'paused',
|
|
63
|
+
joinUrl: 'https://w',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const substrate = makeFakeSubstrate({
|
|
67
|
+
listResult: [
|
|
68
|
+
{ sandboxId: 's0', state: 'paused', metadata: {} },
|
|
69
|
+
{ sandboxId: 's1', state: 'paused', metadata: {} },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
await expect(reserveSlot({ substrate, registry }, {
|
|
73
|
+
// No userId — all cones count
|
|
74
|
+
metadata: {},
|
|
75
|
+
sliccVersion: 'test',
|
|
76
|
+
env: { CONE_CAP_RUNNING: '5', CONE_CAP_PAUSED: '2' },
|
|
77
|
+
})).rejects.toMatchObject({ code: 'CAP_EXCEEDED' });
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('startCone', () => {
|
|
81
|
+
it('creates a sandbox, polls join.json, appends placeholder then updates registry, returns StartResult', async () => {
|
|
82
|
+
// Use a recent timestamp that will pass the minUpdatedAt check (5s skew margin)
|
|
83
|
+
const updatedAt = new Date().toISOString();
|
|
84
|
+
const substrate = makeStartTestSubstrate({
|
|
85
|
+
joinJson: JSON.stringify({
|
|
86
|
+
joinUrl: 'https://w/join/x',
|
|
87
|
+
trayId: 't-1',
|
|
88
|
+
updatedAt,
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
const registry = new MemRegistry();
|
|
92
|
+
const result = await startCone({ substrate, registry }, {
|
|
93
|
+
envContents: 'ANTHROPIC_API_KEY=sk-x\nE2B_API_KEY=should-be-stripped',
|
|
94
|
+
workerBaseUrl: 'https://w',
|
|
95
|
+
sliccVersion: 'test',
|
|
96
|
+
name: 'smoke',
|
|
97
|
+
});
|
|
98
|
+
expect(result.joinUrl).toBe('https://w/join/x');
|
|
99
|
+
expect(result.name).toBe('smoke');
|
|
100
|
+
expect(result.sandboxId).toBe('sbx-fake');
|
|
101
|
+
// After completion, should have exactly one entry with the full data (placeholder updated)
|
|
102
|
+
const entries = await registry.list();
|
|
103
|
+
expect(entries).toHaveLength(1);
|
|
104
|
+
expect(entries[0]?.name).toBe('smoke');
|
|
105
|
+
expect(entries[0]?.state).toBe('running');
|
|
106
|
+
expect(entries[0]?.lastJoinUpdatedAt).toBe(updatedAt);
|
|
107
|
+
expect(entries[0]?.trayId).toBe('t-1');
|
|
108
|
+
expect(entries[0]?.substrate).toBe('e2b');
|
|
109
|
+
expect(entries[0]?.joinUrl).toBe('https://w/join/x');
|
|
110
|
+
});
|
|
111
|
+
it('throws SANDBOX_NOT_READY when pollCloudStatus times out and cleans up placeholder', async () => {
|
|
112
|
+
const substrate = makeStartTestSubstrate({ joinJson: '{}' }); // no joinUrl → poll never returns
|
|
113
|
+
const registry = new MemRegistry();
|
|
114
|
+
await expect(startCone({ substrate, registry }, {
|
|
115
|
+
envContents: '',
|
|
116
|
+
workerBaseUrl: 'https://w',
|
|
117
|
+
sliccVersion: 'test',
|
|
118
|
+
pollTimeoutMs: 200,
|
|
119
|
+
pollIntervalMs: 50,
|
|
120
|
+
})).rejects.toMatchObject({ name: 'CloudError', code: 'SANDBOX_NOT_READY' });
|
|
121
|
+
// Registry should have no entries (placeholder was removed during cleanup).
|
|
122
|
+
expect(await registry.list()).toHaveLength(0);
|
|
123
|
+
// Substrate should have no sandboxes (killed during cleanup).
|
|
124
|
+
expect(await substrate.list()).toHaveLength(0);
|
|
125
|
+
});
|
|
126
|
+
it('includes stderr tail in error message when poll fails', async () => {
|
|
127
|
+
// Create a substrate that seeds stderr but no join.json
|
|
128
|
+
const files = new Map();
|
|
129
|
+
files.set('/tmp/slicc-stderr.log', 'Error: Failed to launch\n cause: missing deps\nStack trace...');
|
|
130
|
+
const substrate = {
|
|
131
|
+
id: 'e2b',
|
|
132
|
+
async create(_opts) {
|
|
133
|
+
const handle = makeFakeHandle({ sandboxId: 'sbx-err' });
|
|
134
|
+
return {
|
|
135
|
+
sandboxId: handle.sandboxId,
|
|
136
|
+
substrate: handle.substrate,
|
|
137
|
+
pause: handle.pause,
|
|
138
|
+
kill: handle.kill,
|
|
139
|
+
getInfo: handle.getInfo,
|
|
140
|
+
writeFile: handle.writeFile,
|
|
141
|
+
readFile: async (path) => {
|
|
142
|
+
const content = files.get(path);
|
|
143
|
+
if (!content)
|
|
144
|
+
throw new Error(`ENOENT ${path}`);
|
|
145
|
+
return content;
|
|
146
|
+
},
|
|
147
|
+
run: handle.run,
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
async connect() {
|
|
151
|
+
throw new Error('not used');
|
|
152
|
+
},
|
|
153
|
+
async list(_opts) {
|
|
154
|
+
return [];
|
|
155
|
+
},
|
|
156
|
+
async extendTimeout(_sandboxId, _ttlMs) {
|
|
157
|
+
// No-op for tests.
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const registry = new MemRegistry();
|
|
161
|
+
await expect(startCone({ substrate, registry }, {
|
|
162
|
+
envContents: '',
|
|
163
|
+
workerBaseUrl: 'https://w',
|
|
164
|
+
sliccVersion: 'test',
|
|
165
|
+
pollTimeoutMs: 200,
|
|
166
|
+
pollIntervalMs: 50,
|
|
167
|
+
})).rejects.toThrow(/missing deps/);
|
|
168
|
+
});
|
|
169
|
+
it('appends placeholder entry to registry BEFORE pollCloudStatus runs', async () => {
|
|
170
|
+
// This test protects against the orphan-recovery race: concurrent /list-cones
|
|
171
|
+
// calls should see the new sandbox in the registry (pass 1) during the slow
|
|
172
|
+
// poll window, NOT go through orphan recovery (pass 2).
|
|
173
|
+
const updatedAt = new Date().toISOString();
|
|
174
|
+
let pollStarted = false;
|
|
175
|
+
let entryPresentAtPollStart = false;
|
|
176
|
+
const registry = new MemRegistry();
|
|
177
|
+
// Create a custom substrate that captures the registry state when readFile
|
|
178
|
+
// is first called (which happens inside pollCloudStatus).
|
|
179
|
+
const substrate = {
|
|
180
|
+
id: 'e2b',
|
|
181
|
+
async create(_opts) {
|
|
182
|
+
const handle = makeFakeHandle({ sandboxId: 'sbx-race' });
|
|
183
|
+
return {
|
|
184
|
+
sandboxId: handle.sandboxId,
|
|
185
|
+
substrate: handle.substrate,
|
|
186
|
+
pause: handle.pause,
|
|
187
|
+
kill: handle.kill,
|
|
188
|
+
getInfo: handle.getInfo,
|
|
189
|
+
writeFile: handle.writeFile,
|
|
190
|
+
readFile: async (path) => {
|
|
191
|
+
if (path === '/tmp/slicc-join.json' && !pollStarted) {
|
|
192
|
+
pollStarted = true;
|
|
193
|
+
// Check if the registry already has an entry for this sandbox
|
|
194
|
+
const entries = await registry.list();
|
|
195
|
+
entryPresentAtPollStart = entries.some((e) => e.sandboxId === 'sbx-race');
|
|
196
|
+
}
|
|
197
|
+
if (path === '/tmp/slicc-join.json') {
|
|
198
|
+
return JSON.stringify({
|
|
199
|
+
joinUrl: 'https://w/join/race',
|
|
200
|
+
trayId: 't-race',
|
|
201
|
+
updatedAt,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
throw new Error(`ENOENT ${path}`);
|
|
205
|
+
},
|
|
206
|
+
run: handle.run,
|
|
207
|
+
};
|
|
208
|
+
},
|
|
209
|
+
async connect() {
|
|
210
|
+
throw new Error('not used');
|
|
211
|
+
},
|
|
212
|
+
async list(_opts) {
|
|
213
|
+
return [];
|
|
214
|
+
},
|
|
215
|
+
async extendTimeout(_sandboxId, _ttlMs) {
|
|
216
|
+
// No-op for tests.
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
await startCone({ substrate, registry }, {
|
|
220
|
+
envContents: '',
|
|
221
|
+
workerBaseUrl: 'https://w',
|
|
222
|
+
sliccVersion: 'test',
|
|
223
|
+
});
|
|
224
|
+
// The placeholder should have been present in the registry BEFORE pollCloudStatus
|
|
225
|
+
// started reading the file.
|
|
226
|
+
expect(entryPresentAtPollStart).toBe(true);
|
|
227
|
+
// After completion, verify the entry was updated with the real joinUrl
|
|
228
|
+
const entries = await registry.list();
|
|
229
|
+
expect(entries).toHaveLength(1);
|
|
230
|
+
expect(entries[0]?.sandboxId).toBe('sbx-race');
|
|
231
|
+
expect(entries[0]?.joinUrl).toBe('https://w/join/race');
|
|
232
|
+
});
|
|
233
|
+
it('rejects stale /tmp/slicc-join.json from template snapshot and waits for fresh one', async () => {
|
|
234
|
+
// Simulate the template-baked stale file → fresh file transition.
|
|
235
|
+
// First read returns stale (updatedAt 1 hour ago), second returns fresh (updatedAt now).
|
|
236
|
+
const staleUpdatedAt = new Date(Date.now() - 3600_000).toISOString();
|
|
237
|
+
const freshUpdatedAt = new Date().toISOString();
|
|
238
|
+
const files = new Map();
|
|
239
|
+
let readCount = 0;
|
|
240
|
+
files.set('/tmp/slicc-join.json', JSON.stringify({
|
|
241
|
+
joinUrl: 'https://w/join/stale',
|
|
242
|
+
trayId: 't-stale',
|
|
243
|
+
updatedAt: staleUpdatedAt,
|
|
244
|
+
}));
|
|
245
|
+
const substrate = {
|
|
246
|
+
id: 'e2b',
|
|
247
|
+
async create(_opts) {
|
|
248
|
+
const handle = makeFakeHandle({ sandboxId: 'sbx-stale' });
|
|
249
|
+
return {
|
|
250
|
+
sandboxId: handle.sandboxId,
|
|
251
|
+
substrate: handle.substrate,
|
|
252
|
+
pause: handle.pause,
|
|
253
|
+
kill: handle.kill,
|
|
254
|
+
getInfo: handle.getInfo,
|
|
255
|
+
writeFile: handle.writeFile,
|
|
256
|
+
readFile: async (path) => {
|
|
257
|
+
if (path === '/tmp/slicc-join.json') {
|
|
258
|
+
readCount++;
|
|
259
|
+
if (readCount === 1) {
|
|
260
|
+
// First read: stale template-baked file
|
|
261
|
+
return JSON.stringify({
|
|
262
|
+
joinUrl: 'https://w/join/stale',
|
|
263
|
+
trayId: 't-stale',
|
|
264
|
+
updatedAt: staleUpdatedAt,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// Second+ read: fresh file written by node-server
|
|
269
|
+
return JSON.stringify({
|
|
270
|
+
joinUrl: 'https://w/join/fresh',
|
|
271
|
+
trayId: 't-fresh',
|
|
272
|
+
updatedAt: freshUpdatedAt,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const content = files.get(path);
|
|
277
|
+
if (!content)
|
|
278
|
+
throw new Error(`ENOENT ${path}`);
|
|
279
|
+
return content;
|
|
280
|
+
},
|
|
281
|
+
run: handle.run,
|
|
282
|
+
};
|
|
283
|
+
},
|
|
284
|
+
async connect() {
|
|
285
|
+
throw new Error('not used');
|
|
286
|
+
},
|
|
287
|
+
async list(_opts) {
|
|
288
|
+
return [];
|
|
289
|
+
},
|
|
290
|
+
async extendTimeout(_sandboxId, _ttlMs) {
|
|
291
|
+
// No-op for tests.
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
const registry = new MemRegistry();
|
|
295
|
+
const result = await startCone({ substrate, registry }, {
|
|
296
|
+
envContents: '',
|
|
297
|
+
workerBaseUrl: 'https://w',
|
|
298
|
+
sliccVersion: 'test',
|
|
299
|
+
pollTimeoutMs: 2000,
|
|
300
|
+
pollIntervalMs: 50,
|
|
301
|
+
});
|
|
302
|
+
// Should have waited past the stale read and returned the fresh one
|
|
303
|
+
expect(result.joinUrl).toBe('https://w/join/fresh');
|
|
304
|
+
expect(readCount).toBeGreaterThan(1); // Proves the stale read was rejected
|
|
305
|
+
// After update, should have exactly one entry with the fresh data
|
|
306
|
+
const entries = await registry.list();
|
|
307
|
+
expect(entries).toHaveLength(1);
|
|
308
|
+
expect(entries[0]?.trayId).toBe('t-fresh');
|
|
309
|
+
expect(entries[0]?.lastJoinUpdatedAt).toBe(freshUpdatedAt);
|
|
310
|
+
expect(entries[0]?.joinUrl).toBe('https://w/join/fresh');
|
|
311
|
+
});
|
|
312
|
+
it('reserveSlot appends a placeholder entry that counts toward cap', async () => {
|
|
313
|
+
// Use makeFakeSubstrate with empty listResult so reserveSlot doesn't see
|
|
314
|
+
// phantom sandboxes from the substrate
|
|
315
|
+
const substrate = makeFakeSubstrate({ listResult: [] });
|
|
316
|
+
const registry = new MemRegistry();
|
|
317
|
+
const { reservationId } = await reserveSlot({ substrate, registry }, {
|
|
318
|
+
userId: 'u1',
|
|
319
|
+
name: 'reserved',
|
|
320
|
+
metadata: { userId: 'u1' },
|
|
321
|
+
sliccVersion: 'test',
|
|
322
|
+
env: { CONE_CAP_RUNNING: '1', CONE_CAP_PAUSED: '5' },
|
|
323
|
+
});
|
|
324
|
+
expect(reservationId).toMatch(/^pending-/);
|
|
325
|
+
// Verify the placeholder was appended
|
|
326
|
+
const entries = await registry.list();
|
|
327
|
+
expect(entries).toHaveLength(1);
|
|
328
|
+
expect(entries[0]?.sandboxId).toBe(reservationId);
|
|
329
|
+
expect(entries[0]?.name).toBe('reserved');
|
|
330
|
+
expect(entries[0]?.state).toBe('reserved');
|
|
331
|
+
expect(entries[0]?.joinUrl).toBe('');
|
|
332
|
+
// Verify a second reservation hits CAP_EXCEEDED
|
|
333
|
+
await expect(reserveSlot({ substrate, registry }, {
|
|
334
|
+
userId: 'u1',
|
|
335
|
+
name: 'second',
|
|
336
|
+
metadata: { userId: 'u1' },
|
|
337
|
+
sliccVersion: 'test',
|
|
338
|
+
env: { CONE_CAP_RUNNING: '1', CONE_CAP_PAUSED: '5' },
|
|
339
|
+
})).rejects.toMatchObject({ name: 'CloudError', code: 'CAP_EXCEEDED' });
|
|
340
|
+
});
|
|
341
|
+
it('reserveSlot throws NAME_TAKEN when name conflicts with existing entry', async () => {
|
|
342
|
+
// Pre-populate substrate with the existing sandbox so listCones sees it as live
|
|
343
|
+
const substrate = makeFakeSubstrate({
|
|
344
|
+
listResult: [
|
|
345
|
+
{
|
|
346
|
+
sandboxId: 'sbx-existing',
|
|
347
|
+
name: 'existing',
|
|
348
|
+
state: 'running',
|
|
349
|
+
metadata: { userId: 'u1' },
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
const registry = new MemRegistry();
|
|
354
|
+
// Pre-populate with an existing entry
|
|
355
|
+
await registry.append({
|
|
356
|
+
substrate: 'e2b',
|
|
357
|
+
sandboxId: 'sbx-existing',
|
|
358
|
+
name: 'existing',
|
|
359
|
+
createdAt: new Date().toISOString(),
|
|
360
|
+
lastSeen: new Date().toISOString(),
|
|
361
|
+
state: 'running',
|
|
362
|
+
joinUrl: 'https://w/join/existing',
|
|
363
|
+
metadata: { userId: 'u1' },
|
|
364
|
+
});
|
|
365
|
+
await expect(reserveSlot({ substrate, registry }, {
|
|
366
|
+
userId: 'u1',
|
|
367
|
+
name: 'existing',
|
|
368
|
+
metadata: { userId: 'u1' },
|
|
369
|
+
sliccVersion: 'test',
|
|
370
|
+
env: { CONE_CAP_RUNNING: '5', CONE_CAP_PAUSED: '5' },
|
|
371
|
+
})).rejects.toMatchObject({ name: 'CloudError', code: 'NAME_TAKEN' });
|
|
372
|
+
});
|
|
373
|
+
it('startCone with reservationId updates the placeholder entry', async () => {
|
|
374
|
+
const updatedAt = new Date().toISOString();
|
|
375
|
+
// For startCone we can use makeStartTestSubstrate since we're actually creating
|
|
376
|
+
const substrate = makeStartTestSubstrate({
|
|
377
|
+
joinJson: JSON.stringify({
|
|
378
|
+
joinUrl: 'https://w/join/real',
|
|
379
|
+
trayId: 't-real',
|
|
380
|
+
updatedAt,
|
|
381
|
+
}),
|
|
382
|
+
});
|
|
383
|
+
const registry = new MemRegistry();
|
|
384
|
+
// First reserve a slot — use a clean substrate for this phase
|
|
385
|
+
const reserveSubstrate = makeFakeSubstrate({ listResult: [] });
|
|
386
|
+
const { reservationId } = await reserveSlot({ substrate: reserveSubstrate, registry }, {
|
|
387
|
+
userId: 'u1',
|
|
388
|
+
name: 'reserved',
|
|
389
|
+
metadata: { userId: 'u1' },
|
|
390
|
+
sliccVersion: 'test',
|
|
391
|
+
env: { CONE_CAP_RUNNING: '5', CONE_CAP_PAUSED: '5' },
|
|
392
|
+
});
|
|
393
|
+
// Verify placeholder exists
|
|
394
|
+
let entries = await registry.list();
|
|
395
|
+
expect(entries).toHaveLength(1);
|
|
396
|
+
expect(entries[0]?.sandboxId).toBe(reservationId);
|
|
397
|
+
// Now call startCone with the reservation (use the real substrate for create)
|
|
398
|
+
const result = await startCone({ substrate, registry }, {
|
|
399
|
+
reservationId,
|
|
400
|
+
envContents: '',
|
|
401
|
+
workerBaseUrl: 'https://w',
|
|
402
|
+
sliccVersion: 'test',
|
|
403
|
+
name: 'reserved',
|
|
404
|
+
metadata: { userId: 'u1' },
|
|
405
|
+
});
|
|
406
|
+
expect(result.sandboxId).toBe('sbx-fake');
|
|
407
|
+
expect(result.joinUrl).toBe('https://w/join/real');
|
|
408
|
+
// Verify the placeholder was replaced with the real entry
|
|
409
|
+
entries = await registry.list();
|
|
410
|
+
expect(entries).toHaveLength(1);
|
|
411
|
+
expect(entries[0]?.sandboxId).toBe('sbx-fake');
|
|
412
|
+
expect(entries[0]?.name).toBe('reserved');
|
|
413
|
+
expect(entries[0]?.joinUrl).toBe('https://w/join/real');
|
|
414
|
+
});
|
|
415
|
+
it('removes reservation when substrate.create fails', async () => {
|
|
416
|
+
const registry = new MemRegistry();
|
|
417
|
+
// Seed a reservation in the registry (as if reserveSlot was called):
|
|
418
|
+
const reservationId = 'pending-test-uuid';
|
|
419
|
+
await registry.append({
|
|
420
|
+
sandboxId: reservationId,
|
|
421
|
+
substrate: 'e2b',
|
|
422
|
+
createdAt: '',
|
|
423
|
+
lastSeen: '',
|
|
424
|
+
state: 'reserved',
|
|
425
|
+
joinUrl: '',
|
|
426
|
+
});
|
|
427
|
+
// Substrate that fails on create
|
|
428
|
+
const substrate = {
|
|
429
|
+
id: 'e2b',
|
|
430
|
+
async create() {
|
|
431
|
+
throw new Error('e2b boom');
|
|
432
|
+
},
|
|
433
|
+
async connect() {
|
|
434
|
+
throw new Error('not used');
|
|
435
|
+
},
|
|
436
|
+
async list() {
|
|
437
|
+
return [];
|
|
438
|
+
},
|
|
439
|
+
async extendTimeout() { },
|
|
440
|
+
};
|
|
441
|
+
await expect(startCone({ substrate, registry }, {
|
|
442
|
+
reservationId,
|
|
443
|
+
envContents: '',
|
|
444
|
+
workerBaseUrl: 'https://w',
|
|
445
|
+
sliccVersion: 'test',
|
|
446
|
+
})).rejects.toThrow('e2b boom');
|
|
447
|
+
expect(registry.entries.find((e) => e.sandboxId === reservationId)).toBeUndefined();
|
|
448
|
+
});
|
|
449
|
+
it('removes real entry when pollCloudStatus fails after sandbox swap', async () => {
|
|
450
|
+
const registry = new MemRegistry();
|
|
451
|
+
const reservationId = 'pending-test-uuid-2';
|
|
452
|
+
await registry.append({
|
|
453
|
+
sandboxId: reservationId,
|
|
454
|
+
substrate: 'e2b',
|
|
455
|
+
createdAt: '',
|
|
456
|
+
lastSeen: '',
|
|
457
|
+
state: 'reserved',
|
|
458
|
+
joinUrl: '',
|
|
459
|
+
});
|
|
460
|
+
let killCalled = false;
|
|
461
|
+
const realSandboxId = 'sbx-created';
|
|
462
|
+
// Substrate creates successfully but readFile never returns joinUrl (poll fails)
|
|
463
|
+
const substrate = {
|
|
464
|
+
id: 'e2b',
|
|
465
|
+
async create() {
|
|
466
|
+
const handle = makeFakeHandle({ sandboxId: realSandboxId });
|
|
467
|
+
return {
|
|
468
|
+
sandboxId: realSandboxId,
|
|
469
|
+
substrate: handle.substrate,
|
|
470
|
+
pause: handle.pause,
|
|
471
|
+
kill: async () => {
|
|
472
|
+
killCalled = true;
|
|
473
|
+
},
|
|
474
|
+
getInfo: handle.getInfo,
|
|
475
|
+
writeFile: handle.writeFile,
|
|
476
|
+
readFile: async (path) => {
|
|
477
|
+
if (path === '/tmp/slicc-join.json') {
|
|
478
|
+
// Return stale data that fails minUpdatedAt check
|
|
479
|
+
return JSON.stringify({
|
|
480
|
+
joinUrl: 'https://w/join/stale',
|
|
481
|
+
trayId: 't-stale',
|
|
482
|
+
updatedAt: '2020-01-01T00:00:00.000Z',
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
if (path === '/tmp/slicc-stderr.log')
|
|
486
|
+
return 'no logs';
|
|
487
|
+
throw new Error(`ENOENT ${path}`);
|
|
488
|
+
},
|
|
489
|
+
run: handle.run,
|
|
490
|
+
};
|
|
491
|
+
},
|
|
492
|
+
async connect() {
|
|
493
|
+
throw new Error('not used');
|
|
494
|
+
},
|
|
495
|
+
async list() {
|
|
496
|
+
return [];
|
|
497
|
+
},
|
|
498
|
+
async extendTimeout() { },
|
|
499
|
+
};
|
|
500
|
+
await expect(startCone({ substrate, registry }, {
|
|
501
|
+
reservationId,
|
|
502
|
+
envContents: '',
|
|
503
|
+
workerBaseUrl: 'https://w',
|
|
504
|
+
sliccVersion: 'test',
|
|
505
|
+
pollTimeoutMs: 200,
|
|
506
|
+
pollIntervalMs: 50,
|
|
507
|
+
})).rejects.toMatchObject({ code: 'SANDBOX_NOT_READY' });
|
|
508
|
+
// Assert: reservation was removed, real entry was removed, sandbox was killed
|
|
509
|
+
expect(registry.entries.find((e) => e.sandboxId === reservationId)).toBeUndefined();
|
|
510
|
+
expect(registry.entries.find((e) => e.sandboxId === realSandboxId)).toBeUndefined();
|
|
511
|
+
expect(killCalled).toBe(true);
|
|
512
|
+
});
|
|
513
|
+
});
|
|
@@ -120,6 +120,9 @@ export function buildChromeLaunchArgs(options) {
|
|
|
120
120
|
args.push(`--disable-extensions-except=${options.profile.extensionPath}`);
|
|
121
121
|
args.push(`--load-extension=${options.profile.extensionPath}`);
|
|
122
122
|
}
|
|
123
|
+
if (options.hosted) {
|
|
124
|
+
args.push('--headless=new', '--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu', '--font-render-hinting=none');
|
|
125
|
+
}
|
|
123
126
|
args.push(options.launchUrl);
|
|
124
127
|
return args;
|
|
125
128
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SubstrateId } from '../_cloud_core/src/index.js';
|
|
2
|
+
export type ParsedCloudArgs = {
|
|
3
|
+
subcommand: 'start';
|
|
4
|
+
args: {
|
|
5
|
+
substrate: SubstrateId;
|
|
6
|
+
name?: string;
|
|
7
|
+
envFile?: string;
|
|
8
|
+
};
|
|
9
|
+
} | {
|
|
10
|
+
subcommand: 'list';
|
|
11
|
+
args: {
|
|
12
|
+
substrate: SubstrateId;
|
|
13
|
+
};
|
|
14
|
+
} | {
|
|
15
|
+
subcommand: 'pause';
|
|
16
|
+
args: {
|
|
17
|
+
substrate: SubstrateId;
|
|
18
|
+
query: string;
|
|
19
|
+
};
|
|
20
|
+
} | {
|
|
21
|
+
subcommand: 'resume';
|
|
22
|
+
args: {
|
|
23
|
+
substrate: SubstrateId;
|
|
24
|
+
query: string;
|
|
25
|
+
envFile?: string;
|
|
26
|
+
};
|
|
27
|
+
} | {
|
|
28
|
+
subcommand: 'kill';
|
|
29
|
+
args: {
|
|
30
|
+
substrate: SubstrateId;
|
|
31
|
+
query: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export declare function parseCloudArgs(argv: string[]): ParsedCloudArgs | null;
|