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,79 @@
|
|
|
1
|
+
const VALID_SUBCOMMANDS = ['start', 'list', 'pause', 'resume', 'kill'];
|
|
2
|
+
export function parseCloudArgs(argv) {
|
|
3
|
+
if (argv.includes('--hosted') && argv.includes('--cloud')) {
|
|
4
|
+
throw new Error('--cloud and --hosted are mutually exclusive');
|
|
5
|
+
}
|
|
6
|
+
const cloudIdx = argv.indexOf('--cloud');
|
|
7
|
+
if (cloudIdx === -1)
|
|
8
|
+
return null;
|
|
9
|
+
const sub = argv[cloudIdx + 1];
|
|
10
|
+
if (!sub || !VALID_SUBCOMMANDS.includes(sub)) {
|
|
11
|
+
throw new Error(`unknown subcommand: ${sub ?? '(none)'} (expected one of: ${VALID_SUBCOMMANDS.join(', ')})`);
|
|
12
|
+
}
|
|
13
|
+
const rest = argv.slice(cloudIdx + 2);
|
|
14
|
+
const baseArgs = {
|
|
15
|
+
substrate: 'e2b',
|
|
16
|
+
};
|
|
17
|
+
let i = 0;
|
|
18
|
+
while (i < rest.length) {
|
|
19
|
+
const a = rest[i];
|
|
20
|
+
if (a === '--name') {
|
|
21
|
+
baseArgs.name = rest[++i];
|
|
22
|
+
}
|
|
23
|
+
else if (a === '--env-file') {
|
|
24
|
+
baseArgs.envFile = rest[++i];
|
|
25
|
+
}
|
|
26
|
+
else if (a === '--substrate') {
|
|
27
|
+
const v = rest[++i];
|
|
28
|
+
if (v !== 'e2b')
|
|
29
|
+
throw new Error(`unsupported substrate: ${v} (MVP only supports 'e2b')`);
|
|
30
|
+
baseArgs.substrate = v;
|
|
31
|
+
}
|
|
32
|
+
else if (!a.startsWith('--') &&
|
|
33
|
+
!baseArgs.query &&
|
|
34
|
+
(sub === 'pause' || sub === 'resume' || sub === 'kill')) {
|
|
35
|
+
baseArgs.query = a;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
throw new Error(`unrecognized arg: ${a}`);
|
|
39
|
+
}
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
// Build discriminated union based on subcommand
|
|
43
|
+
switch (sub) {
|
|
44
|
+
case 'start':
|
|
45
|
+
return {
|
|
46
|
+
subcommand: 'start',
|
|
47
|
+
args: {
|
|
48
|
+
substrate: baseArgs.substrate,
|
|
49
|
+
name: baseArgs.name,
|
|
50
|
+
envFile: baseArgs.envFile,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
case 'list':
|
|
54
|
+
return { subcommand: 'list', args: { substrate: baseArgs.substrate } };
|
|
55
|
+
case 'pause':
|
|
56
|
+
case 'kill':
|
|
57
|
+
if (!baseArgs.query) {
|
|
58
|
+
throw new Error(`${sub} requires a query argument (sandbox ID or name)`);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
subcommand: sub,
|
|
62
|
+
args: { substrate: baseArgs.substrate, query: baseArgs.query },
|
|
63
|
+
};
|
|
64
|
+
case 'resume':
|
|
65
|
+
if (!baseArgs.query) {
|
|
66
|
+
throw new Error(`${sub} requires a query argument (sandbox ID or name)`);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
subcommand: 'resume',
|
|
70
|
+
args: {
|
|
71
|
+
substrate: baseArgs.substrate,
|
|
72
|
+
query: baseArgs.query,
|
|
73
|
+
envFile: baseArgs.envFile,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`unknown subcommand: ${sub}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type SandboxSubstrate } from '../_cloud_core/src/index.js';
|
|
2
|
+
export interface RunKillOpts {
|
|
3
|
+
substrate: SandboxSubstrate;
|
|
4
|
+
registryPath: string;
|
|
5
|
+
query: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Thin wrapper over killCone from @slicc/cloud-core.
|
|
9
|
+
* Adapts the cloud-core interface to the node-server's file-backed registry.
|
|
10
|
+
* Returns void for backward compatibility with existing callers.
|
|
11
|
+
*/
|
|
12
|
+
export declare function runKill(opts: RunKillOpts): Promise<void>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { killCone } from '../_cloud_core/src/index.js';
|
|
2
|
+
import { FileRegistry } from './registry-file.js';
|
|
3
|
+
/**
|
|
4
|
+
* Thin wrapper over killCone from @slicc/cloud-core.
|
|
5
|
+
* Adapts the cloud-core interface to the node-server's file-backed registry.
|
|
6
|
+
* Returns void for backward compatibility with existing callers.
|
|
7
|
+
*/
|
|
8
|
+
export async function runKill(opts) {
|
|
9
|
+
const registry = new FileRegistry(opts.registryPath);
|
|
10
|
+
await killCone({ substrate: opts.substrate, registry }, opts.query);
|
|
11
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { listCones } from '../_cloud_core/src/index.js';
|
|
2
|
+
import { FileRegistry } from './registry-file.js';
|
|
3
|
+
export async function runList(opts) {
|
|
4
|
+
const registry = new FileRegistry(opts.registryPath);
|
|
5
|
+
return listCones({ substrate: opts.substrate, registry });
|
|
6
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { pauseCone } from '../_cloud_core/src/index.js';
|
|
2
|
+
import { FileRegistry } from './registry-file.js';
|
|
3
|
+
export async function runPause(opts) {
|
|
4
|
+
const registry = new FileRegistry(opts.registryPath);
|
|
5
|
+
return pauseCone({ substrate: opts.substrate, registry }, opts.query);
|
|
6
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ConeEntry, Registry } from '../_cloud_core/src/index.js';
|
|
2
|
+
export declare class FileRegistry implements Registry {
|
|
3
|
+
private readonly filePath;
|
|
4
|
+
constructor(filePath: string);
|
|
5
|
+
static defaultPath(): string;
|
|
6
|
+
list(): Promise<ConeEntry[]>;
|
|
7
|
+
append(entry: ConeEntry): Promise<void>;
|
|
8
|
+
update(sandboxId: string, patch: Partial<ConeEntry>): Promise<void>;
|
|
9
|
+
remove(sandboxId: string): Promise<void>;
|
|
10
|
+
findByNameOrId(query: string): Promise<ConeEntry | null>;
|
|
11
|
+
private read;
|
|
12
|
+
private write;
|
|
13
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
function isConeEntry(x) {
|
|
4
|
+
if (typeof x !== 'object' || x === null)
|
|
5
|
+
return false;
|
|
6
|
+
const e = x;
|
|
7
|
+
return (typeof e.substrate === 'string' &&
|
|
8
|
+
typeof e.sandboxId === 'string' &&
|
|
9
|
+
typeof e.createdAt === 'string' &&
|
|
10
|
+
typeof e.joinUrl === 'string' &&
|
|
11
|
+
typeof e.lastSeen === 'string' &&
|
|
12
|
+
typeof e.state === 'string' &&
|
|
13
|
+
(e.state === 'running' || e.state === 'paused' || e.state === 'dead' || e.state === 'reserved'));
|
|
14
|
+
}
|
|
15
|
+
export class FileRegistry {
|
|
16
|
+
filePath;
|
|
17
|
+
constructor(filePath) {
|
|
18
|
+
this.filePath = filePath;
|
|
19
|
+
}
|
|
20
|
+
static defaultPath() {
|
|
21
|
+
const home = process.env['HOME'] ?? process.env['USERPROFILE'] ?? '.';
|
|
22
|
+
return path.join(home, '.slicc', 'cloud-sessions.json');
|
|
23
|
+
}
|
|
24
|
+
async list() {
|
|
25
|
+
const data = await this.read();
|
|
26
|
+
return data.sessions;
|
|
27
|
+
}
|
|
28
|
+
async append(entry) {
|
|
29
|
+
const data = await this.read();
|
|
30
|
+
// UPSERT semantics: filter out existing sandboxId, then push the new entry
|
|
31
|
+
data.sessions = data.sessions.filter((s) => s.sandboxId !== entry.sandboxId);
|
|
32
|
+
data.sessions.push(entry);
|
|
33
|
+
await this.write(data);
|
|
34
|
+
}
|
|
35
|
+
async update(sandboxId, patch) {
|
|
36
|
+
const data = await this.read();
|
|
37
|
+
const idx = data.sessions.findIndex((s) => s.sandboxId === sandboxId);
|
|
38
|
+
if (idx === -1) {
|
|
39
|
+
throw new Error(`entry not found: ${sandboxId}`);
|
|
40
|
+
}
|
|
41
|
+
data.sessions[idx] = { ...data.sessions[idx], ...patch, sandboxId };
|
|
42
|
+
await this.write(data);
|
|
43
|
+
}
|
|
44
|
+
async remove(sandboxId) {
|
|
45
|
+
const data = await this.read();
|
|
46
|
+
data.sessions = data.sessions.filter((s) => s.sandboxId !== sandboxId);
|
|
47
|
+
await this.write(data);
|
|
48
|
+
}
|
|
49
|
+
async findByNameOrId(query) {
|
|
50
|
+
const data = await this.read();
|
|
51
|
+
return (data.sessions.find((s) => s.sandboxId === query) ??
|
|
52
|
+
data.sessions.find((s) => s.name === query) ??
|
|
53
|
+
null);
|
|
54
|
+
}
|
|
55
|
+
async read() {
|
|
56
|
+
let raw;
|
|
57
|
+
try {
|
|
58
|
+
raw = JSON.parse(await fs.readFile(this.filePath, 'utf-8'));
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err.code === 'ENOENT')
|
|
62
|
+
return { sessions: [] };
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
if (typeof raw !== 'object' ||
|
|
66
|
+
raw === null ||
|
|
67
|
+
!Array.isArray(raw.sessions)) {
|
|
68
|
+
console.warn('cloud-sessions.json is malformed; treating as empty', this.filePath);
|
|
69
|
+
return { sessions: [] };
|
|
70
|
+
}
|
|
71
|
+
const candidates = raw.sessions;
|
|
72
|
+
const sessions = [];
|
|
73
|
+
for (const c of candidates) {
|
|
74
|
+
if (isConeEntry(c))
|
|
75
|
+
sessions.push(c);
|
|
76
|
+
else
|
|
77
|
+
console.warn('skipping malformed cloud-sessions entry', c);
|
|
78
|
+
}
|
|
79
|
+
return { sessions };
|
|
80
|
+
}
|
|
81
|
+
async write(data) {
|
|
82
|
+
await fs.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
83
|
+
await fs.writeFile(this.filePath, JSON.stringify(data, null, 2));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ResumeResult, type SandboxSubstrate } from '../_cloud_core/src/index.js';
|
|
2
|
+
export interface RunResumeOpts {
|
|
3
|
+
substrate: SandboxSubstrate;
|
|
4
|
+
envFilePath: string;
|
|
5
|
+
registryPath: string;
|
|
6
|
+
query: string;
|
|
7
|
+
localSliccVersion: string;
|
|
8
|
+
pollIntervalMs?: number;
|
|
9
|
+
pollTimeoutMs?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function runResume(opts: RunResumeOpts): Promise<ResumeResult>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { resumeCone } from '../_cloud_core/src/index.js';
|
|
3
|
+
import { FileRegistry } from './registry-file.js';
|
|
4
|
+
export async function runResume(opts) {
|
|
5
|
+
// Refresh on resume so cones paused for >24h pick up a freshly-issued token
|
|
6
|
+
// from the local secrets.env. This was a pre-existing CLI gap — resume would
|
|
7
|
+
// succeed but Adobe LLM calls would 401 on expired tokens.
|
|
8
|
+
const envContents = await fs.readFile(opts.envFilePath, 'utf-8');
|
|
9
|
+
const registry = new FileRegistry(opts.registryPath);
|
|
10
|
+
return resumeCone({ substrate: opts.substrate, registry }, {
|
|
11
|
+
query: opts.query,
|
|
12
|
+
localSliccVersion: opts.localSliccVersion,
|
|
13
|
+
refreshSecretsContents: envContents,
|
|
14
|
+
pollIntervalMs: opts.pollIntervalMs,
|
|
15
|
+
pollTimeoutMs: opts.pollTimeoutMs,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type StartResult, type SandboxHandle } from '../_cloud_core/src/index.js';
|
|
2
|
+
import type { SandboxSubstrate } from '../_cloud_core/src/index.js';
|
|
3
|
+
export interface RunStartOpts {
|
|
4
|
+
substrate: SandboxSubstrate;
|
|
5
|
+
envFilePath: string;
|
|
6
|
+
registryPath: string;
|
|
7
|
+
workerBaseUrl: string;
|
|
8
|
+
sliccVersion: string;
|
|
9
|
+
template?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
pollIntervalMs?: number;
|
|
12
|
+
pollTimeoutMs?: number;
|
|
13
|
+
/** Test-only hook: invoked after substrate.create but before pollCloudStatus. */
|
|
14
|
+
onAfterCreate?: (handle: SandboxHandle) => Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare function runStart(opts: RunStartOpts): Promise<StartResult>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { startCone } from '../_cloud_core/src/index.js';
|
|
3
|
+
import { FileRegistry } from './registry-file.js';
|
|
4
|
+
/**
|
|
5
|
+
* Extract ADOBE_IMS_TOKEN (+ DOMAINS) from an env-file body so they can be
|
|
6
|
+
* injected as sandbox env vars at Sandbox.create. start.sh writes them to
|
|
7
|
+
* /slicc/secrets.env BEFORE node-server boots, eliminating the historical 5s
|
|
8
|
+
* race window where the page-side bootstrap fetch found no secrets file.
|
|
9
|
+
*/
|
|
10
|
+
function extractAdobeBootstrap(envContents) {
|
|
11
|
+
const envs = {};
|
|
12
|
+
for (const line of envContents.split('\n')) {
|
|
13
|
+
const m = line.match(/^\s*(ADOBE_IMS_TOKEN(?:_DOMAINS)?)\s*=\s*(.*)$/);
|
|
14
|
+
if (m)
|
|
15
|
+
envs[m[1]] = m[2].trim();
|
|
16
|
+
}
|
|
17
|
+
return envs;
|
|
18
|
+
}
|
|
19
|
+
export async function runStart(opts) {
|
|
20
|
+
const envContents = await fs.readFile(opts.envFilePath, 'utf-8');
|
|
21
|
+
const adobeBootstrap = extractAdobeBootstrap(envContents);
|
|
22
|
+
const registry = new FileRegistry(opts.registryPath);
|
|
23
|
+
// If we have a test hook, wrap the substrate to inject it.
|
|
24
|
+
let substrate = opts.substrate;
|
|
25
|
+
if (opts.onAfterCreate) {
|
|
26
|
+
const originalCreate = substrate.create.bind(substrate);
|
|
27
|
+
substrate = {
|
|
28
|
+
...substrate,
|
|
29
|
+
create: async (createOpts) => {
|
|
30
|
+
const handle = await originalCreate(createOpts);
|
|
31
|
+
await opts.onAfterCreate(handle);
|
|
32
|
+
return handle;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return startCone({ substrate, registry }, {
|
|
37
|
+
envContents,
|
|
38
|
+
envs: adobeBootstrap,
|
|
39
|
+
workerBaseUrl: opts.workerBaseUrl,
|
|
40
|
+
template: opts.template,
|
|
41
|
+
name: opts.name,
|
|
42
|
+
sliccVersion: opts.sliccVersion,
|
|
43
|
+
pollTimeoutMs: opts.pollTimeoutMs,
|
|
44
|
+
pollIntervalMs: opts.pollIntervalMs,
|
|
45
|
+
metadata: {
|
|
46
|
+
createdBy: process.env['USER'] ?? 'unknown',
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Express, type Request, type Response, type NextFunction } from 'express';
|
|
2
|
+
export interface CloudStatusEndpointOptions {
|
|
3
|
+
joinFilePath: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Reject non-loopback requests. The sandbox is a private execution boundary,
|
|
7
|
+
* but defense in depth: someone might wire a port-forward and we want this
|
|
8
|
+
* endpoint to be unreachable from the outside.
|
|
9
|
+
*/
|
|
10
|
+
export declare function requireLoopback(req: Request, res: Response, next: NextFunction): void;
|
|
11
|
+
export declare function registerCloudStatusEndpoint(app: Express, options: CloudStatusEndpointOptions): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
function isCloudStatusPayload(x) {
|
|
4
|
+
if (typeof x !== 'object' || x === null)
|
|
5
|
+
return false;
|
|
6
|
+
const p = x;
|
|
7
|
+
if (typeof p.joinUrl !== 'string' || p.joinUrl.length === 0)
|
|
8
|
+
return false;
|
|
9
|
+
// Optional fields: validate type if present, but don't require.
|
|
10
|
+
for (const key of ['trayId', 'controllerUrl', 'webhookUrl', 'runtime', 'sliccVersion']) {
|
|
11
|
+
if (key in p && typeof p[key] !== 'string')
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Reject non-loopback requests. The sandbox is a private execution boundary,
|
|
18
|
+
* but defense in depth: someone might wire a port-forward and we want this
|
|
19
|
+
* endpoint to be unreachable from the outside.
|
|
20
|
+
*/
|
|
21
|
+
export function requireLoopback(req, res, next) {
|
|
22
|
+
const addr = req.socket.remoteAddress ?? '';
|
|
23
|
+
const isLoopback = addr === '127.0.0.1' || addr === '::1' || addr === '::ffff:127.0.0.1' || addr === 'localhost';
|
|
24
|
+
if (!isLoopback) {
|
|
25
|
+
res.status(403).json({ error: 'localhost only' });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
next();
|
|
29
|
+
}
|
|
30
|
+
export function registerCloudStatusEndpoint(app, options) {
|
|
31
|
+
app.post('/api/cloud-status', requireLoopback, express.json(), async (req, res) => {
|
|
32
|
+
if (!isCloudStatusPayload(req.body)) {
|
|
33
|
+
res.status(400).json({ error: 'invalid cloud-status payload' });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const body = req.body;
|
|
37
|
+
const payload = {
|
|
38
|
+
joinUrl: body.joinUrl,
|
|
39
|
+
trayId: body.trayId ?? null,
|
|
40
|
+
controllerUrl: body.controllerUrl ?? null,
|
|
41
|
+
webhookUrl: body.webhookUrl ?? null,
|
|
42
|
+
runtime: body.runtime ?? null,
|
|
43
|
+
sliccVersion: body.sliccVersion ?? null,
|
|
44
|
+
updatedAt: new Date().toISOString(),
|
|
45
|
+
};
|
|
46
|
+
await fs.writeFile(options.joinFilePath, JSON.stringify(payload, null, 2));
|
|
47
|
+
res.json({ ok: true });
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback-only endpoint that hands the hosted-leader page any pre-acquired
|
|
3
|
+
* provider credentials that were uploaded with secrets.env.
|
|
4
|
+
*
|
|
5
|
+
* Currently exposes ADOBE_IMS_TOKEN so the laptop can paste in an IMS bearer
|
|
6
|
+
* once (avoiding OAuth, which can't complete in headless chromium). The
|
|
7
|
+
* webapp's hosted-leader boot calls saveOAuthAccount with the returned token
|
|
8
|
+
* before any LLM call goes out.
|
|
9
|
+
*
|
|
10
|
+
* Same loopback discipline as /api/cloud-status and /api/leader-restart:
|
|
11
|
+
* chromium-on-localhost is the only thing that can hit it; the laptop-side
|
|
12
|
+
* orchestrator talks to the leader through the tray-worker tunnel, not http.
|
|
13
|
+
*/
|
|
14
|
+
import type { Express } from 'express';
|
|
15
|
+
import type { SecretStore } from './secrets/types.js';
|
|
16
|
+
export interface HostedBootstrapPayload {
|
|
17
|
+
adobeImsToken?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function registerHostedBootstrapEndpoint(app: Express, options: {
|
|
20
|
+
secretStore: SecretStore;
|
|
21
|
+
}): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { requireLoopback } from './cloud-status.js';
|
|
2
|
+
export function registerHostedBootstrapEndpoint(app, options) {
|
|
3
|
+
app.get('/api/hosted-bootstrap', requireLoopback, (_req, res) => {
|
|
4
|
+
const payload = {};
|
|
5
|
+
// EnvSecretStore.get(name) requires both the value AND a <name>_DOMAINS
|
|
6
|
+
// entry. The user's secrets.env must contain BOTH:
|
|
7
|
+
// ADOBE_IMS_TOKEN=<bearer>
|
|
8
|
+
// ADOBE_IMS_TOKEN_DOMAINS=adobe-llm-proxy.paolo-moz.workers.dev
|
|
9
|
+
const token = options.secretStore.get('ADOBE_IMS_TOKEN');
|
|
10
|
+
if (token?.value)
|
|
11
|
+
payload.adobeImsToken = token.value;
|
|
12
|
+
res.json(payload);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -22,10 +22,30 @@ import { SecretProxyManager } from './secrets/proxy-manager.js';
|
|
|
22
22
|
import { OauthSecretStore } from './secrets/oauth-secret-store.js';
|
|
23
23
|
import { handleDaSignAndForward, handleS3SignAndForward } from './secrets/sign-and-forward.js';
|
|
24
24
|
import { readOrCreateSessionId } from './secrets/session-id-file.js';
|
|
25
|
+
import { registerCloudStatusEndpoint } from './cloud-status.js';
|
|
26
|
+
import { registerHostedBootstrapEndpoint } from './hosted-bootstrap.js';
|
|
27
|
+
import { createHttpCdp, registerLeaderRestartEndpoint } from './leader-restart.js';
|
|
25
28
|
import { FETCH_PROXY_SKIP_HEADERS } from './fetch-proxy-headers.js';
|
|
26
29
|
import { buildLocalApiDescriptor, sliccLinksMiddleware } from './links-middleware.js';
|
|
30
|
+
import { parseCloudArgs } from './cloud/dispatch.js';
|
|
31
|
+
import { createSubstrate } from './_cloud_core/src/index.js';
|
|
32
|
+
import { FileRegistry } from './cloud/registry-file.js';
|
|
33
|
+
import { runStart } from './cloud/start.js';
|
|
34
|
+
import { runList } from './cloud/list.js';
|
|
35
|
+
import { runPause } from './cloud/pause.js';
|
|
36
|
+
import { runResume } from './cloud/resume.js';
|
|
37
|
+
import { runKill } from './cloud/kill.js';
|
|
38
|
+
import { promises as fsPromises } from 'node:fs';
|
|
27
39
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
|
28
40
|
const PROJECT_ROOT = resolve(__dirname, '..', '..');
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Cloud dispatcher — must run BEFORE any other boot logic
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
const _parsedCloudArgs = parseCloudArgs(process.argv.slice(2));
|
|
45
|
+
if (_parsedCloudArgs) {
|
|
46
|
+
await runCloudSubcommand(_parsedCloudArgs);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
29
49
|
const RUNTIME_FLAGS = parseCliRuntimeFlags(process.argv.slice(2));
|
|
30
50
|
// Version command — exit immediately, no side effects
|
|
31
51
|
if (RUNTIME_FLAGS.version) {
|
|
@@ -420,6 +440,11 @@ async function main() {
|
|
|
420
440
|
join: RUNTIME_FLAGS.join,
|
|
421
441
|
joinUrl: RUNTIME_FLAGS.joinUrl,
|
|
422
442
|
});
|
|
443
|
+
// Append runtime parameter for hosted mode
|
|
444
|
+
if (RUNTIME_FLAGS.hosted) {
|
|
445
|
+
const sep = browserLaunchUrl.includes('?') ? '&' : '?';
|
|
446
|
+
browserLaunchUrl += `${sep}runtime=hosted-leader`;
|
|
447
|
+
}
|
|
423
448
|
// Append optional prompt parameter
|
|
424
449
|
if (RUNTIME_FLAGS.prompt) {
|
|
425
450
|
const sep = browserLaunchUrl.includes('?') ? '&' : '?';
|
|
@@ -433,12 +458,17 @@ async function main() {
|
|
|
433
458
|
}
|
|
434
459
|
const chromeProfile = (() => {
|
|
435
460
|
try {
|
|
436
|
-
|
|
461
|
+
const resolved = resolveChromeLaunchProfile({
|
|
437
462
|
projectRoot: PROJECT_ROOT,
|
|
438
463
|
tmpDir: process.env['TMPDIR'] ?? '/tmp',
|
|
439
464
|
profile: RUNTIME_FLAGS.profile,
|
|
440
465
|
servePort: SERVE_PORT,
|
|
441
466
|
});
|
|
467
|
+
// Override user data dir in hosted mode to use persistent profile
|
|
468
|
+
if (RUNTIME_FLAGS.hosted) {
|
|
469
|
+
resolved.userDataDir = process.env['CHROME_USER_DATA_DIR'] ?? '/data/profile';
|
|
470
|
+
}
|
|
471
|
+
return resolved;
|
|
442
472
|
}
|
|
443
473
|
catch (error) {
|
|
444
474
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -471,6 +501,7 @@ async function main() {
|
|
|
471
501
|
cdpPort: REQUESTED_CDP_PORT,
|
|
472
502
|
launchUrl: browserLaunchUrl,
|
|
473
503
|
profile: chromeProfile,
|
|
504
|
+
hosted: RUNTIME_FLAGS.hosted,
|
|
474
505
|
});
|
|
475
506
|
// Profile directories are reused across runs (both the dev
|
|
476
507
|
// `/tmp/browser-coding-agent-chrome` profile and the persistent
|
|
@@ -678,7 +709,10 @@ async function main() {
|
|
|
678
709
|
}));
|
|
679
710
|
app.get('/api/runtime-config', (_req, res) => {
|
|
680
711
|
res.json({
|
|
681
|
-
trayWorkerBaseUrl:
|
|
712
|
+
trayWorkerBaseUrl:
|
|
713
|
+
// Hosted mode source: env var injected at sandbox-create time.
|
|
714
|
+
(RUNTIME_FLAGS.hosted ? process.env['SLICC_TRAY_WORKER_BASE_URL']?.trim() : null) ??
|
|
715
|
+
RUNTIME_FLAGS.leadWorkerBaseUrl ??
|
|
682
716
|
(process.env['WORKER_BASE_URL']?.trim() || null) ??
|
|
683
717
|
(DEV_MODE
|
|
684
718
|
? 'https://slicc-tray-hub-staging.minivelos.workers.dev'
|
|
@@ -992,6 +1026,14 @@ async function main() {
|
|
|
992
1026
|
await secretProxy.reload();
|
|
993
1027
|
res.status(204).end();
|
|
994
1028
|
});
|
|
1029
|
+
// Cloud status endpoint (hosted-only) — writes join info to /tmp/slicc-join.json
|
|
1030
|
+
// Register BEFORE Chromium launches. The webapp's first action after
|
|
1031
|
+
// ?runtime=hosted-leader boot is to mint a tray and POST /api/cloud-status.
|
|
1032
|
+
// If the route doesn't exist yet, the post 404s and the CLI poll times out.
|
|
1033
|
+
if (RUNTIME_FLAGS.hosted) {
|
|
1034
|
+
registerCloudStatusEndpoint(app, { joinFilePath: '/tmp/slicc-join.json' });
|
|
1035
|
+
registerHostedBootstrapEndpoint(app, { secretStore });
|
|
1036
|
+
}
|
|
995
1037
|
// Fetch proxy — forwards cross-origin requests from the browser to bypass CORS.
|
|
996
1038
|
// Used by just-bash's curl which calls the browser's fetch() API.
|
|
997
1039
|
// Note: express.json() may have already parsed the body, so we check req.body first.
|
|
@@ -1304,7 +1346,7 @@ async function main() {
|
|
|
1304
1346
|
});
|
|
1305
1347
|
// Create the HTTP server BEFORE Vite so we can register our upgrade handler first
|
|
1306
1348
|
const server = createServer(app);
|
|
1307
|
-
if (DEV_MODE) {
|
|
1349
|
+
if (DEV_MODE && !RUNTIME_FLAGS.hosted) {
|
|
1308
1350
|
// Dev mode: use Vite's dev server as middleware for HMR
|
|
1309
1351
|
const { createServer: createViteServer } = await import('vite');
|
|
1310
1352
|
const webappIndexHtml = resolve(process.cwd(), 'packages/webapp/index.html');
|
|
@@ -1730,6 +1772,14 @@ async function main() {
|
|
|
1730
1772
|
console.log(`[cdp-proxy] Pre-connected: CDP available at ${cdpUrl}`);
|
|
1731
1773
|
await ensureChromeConnection(cdpUrl);
|
|
1732
1774
|
console.log('[cdp-proxy] Chrome WebSocket ready (pre-warmed)');
|
|
1775
|
+
// Register leader-restart endpoint now that CDP is ready (hosted mode only)
|
|
1776
|
+
if (RUNTIME_FLAGS.hosted) {
|
|
1777
|
+
registerLeaderRestartEndpoint(app, {
|
|
1778
|
+
cdp: createHttpCdp(CDP_PORT),
|
|
1779
|
+
localUrlPrefix: `http://localhost:${SERVE_PORT}/`,
|
|
1780
|
+
});
|
|
1781
|
+
console.log('[hosted] /api/leader-restart endpoint registered');
|
|
1782
|
+
}
|
|
1733
1783
|
}
|
|
1734
1784
|
catch (err) {
|
|
1735
1785
|
console.log('[cdp-proxy] Pre-connect failed (will retry on first client):', err);
|
|
@@ -1762,6 +1812,99 @@ async function main() {
|
|
|
1762
1812
|
}
|
|
1763
1813
|
});
|
|
1764
1814
|
}
|
|
1815
|
+
// ---------------------------------------------------------------------------
|
|
1816
|
+
// Cloud dispatcher helpers
|
|
1817
|
+
// ---------------------------------------------------------------------------
|
|
1818
|
+
async function readSecretsEnvKey(name) {
|
|
1819
|
+
try {
|
|
1820
|
+
const home = process.env['HOME'] ?? process.env['USERPROFILE'] ?? '.';
|
|
1821
|
+
const file = process.env['SLICC_SECRETS_FILE'] ?? join(home, '.slicc', 'secrets.env');
|
|
1822
|
+
const contents = await fsPromises.readFile(file, 'utf-8');
|
|
1823
|
+
for (const line of contents.split('\n')) {
|
|
1824
|
+
const m = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
1825
|
+
if (m && m[1] === name)
|
|
1826
|
+
return m[2].trim();
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
catch {
|
|
1830
|
+
/* file missing → undefined */
|
|
1831
|
+
}
|
|
1832
|
+
return undefined;
|
|
1833
|
+
}
|
|
1834
|
+
function defaultSecretsPath() {
|
|
1835
|
+
const home = process.env['HOME'] ?? process.env['USERPROFILE'] ?? '.';
|
|
1836
|
+
return join(home, '.slicc', 'secrets.env');
|
|
1837
|
+
}
|
|
1838
|
+
function readPackageVersion() {
|
|
1839
|
+
try {
|
|
1840
|
+
const pkgPath = join(PROJECT_ROOT, 'package.json');
|
|
1841
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
1842
|
+
return pkg.version;
|
|
1843
|
+
}
|
|
1844
|
+
catch {
|
|
1845
|
+
return 'unknown';
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
async function runCloudSubcommand(parsed) {
|
|
1849
|
+
const apiKey = process.env['E2B_API_KEY'] ?? (await readSecretsEnvKey('E2B_API_KEY'));
|
|
1850
|
+
if (!apiKey) {
|
|
1851
|
+
console.error('E2B_API_KEY not set. Add it to ~/.slicc/secrets.env (with E2B_API_KEY_DOMAINS=e2b.dev) ' +
|
|
1852
|
+
'or export it.');
|
|
1853
|
+
process.exit(2);
|
|
1854
|
+
}
|
|
1855
|
+
const substrate = createSubstrate(parsed.args.substrate, { apiKey });
|
|
1856
|
+
const registryPath = FileRegistry.defaultPath();
|
|
1857
|
+
const localSliccVersion = readPackageVersion();
|
|
1858
|
+
switch (parsed.subcommand) {
|
|
1859
|
+
case 'start': {
|
|
1860
|
+
const result = await runStart({
|
|
1861
|
+
substrate,
|
|
1862
|
+
envFilePath: parsed.args.envFile ?? defaultSecretsPath(),
|
|
1863
|
+
registryPath,
|
|
1864
|
+
workerBaseUrl: process.env['SLICC_TRAY_WORKER_BASE_URL']?.trim() || 'https://www.sliccy.ai',
|
|
1865
|
+
sliccVersion: localSliccVersion,
|
|
1866
|
+
name: parsed.args.name,
|
|
1867
|
+
});
|
|
1868
|
+
console.log(`Sandbox ${result.sandboxId} ready.`);
|
|
1869
|
+
console.log(`Open: ${result.joinUrl}`);
|
|
1870
|
+
console.log('Attach from iOS, desktop SLICC, or any browser tab.');
|
|
1871
|
+
break;
|
|
1872
|
+
}
|
|
1873
|
+
case 'list': {
|
|
1874
|
+
const entries = await runList({ substrate, registryPath });
|
|
1875
|
+
for (const e of entries) {
|
|
1876
|
+
console.log(`${e.substrate}\t${e.sandboxId}\t${e.name ?? '-'}\t${e.state}\t${e.joinUrl}`);
|
|
1877
|
+
}
|
|
1878
|
+
break;
|
|
1879
|
+
}
|
|
1880
|
+
case 'pause':
|
|
1881
|
+
await runPause({ substrate, registryPath, query: parsed.args.query });
|
|
1882
|
+
console.log('Paused.');
|
|
1883
|
+
break;
|
|
1884
|
+
case 'resume': {
|
|
1885
|
+
const result = await runResume({
|
|
1886
|
+
substrate,
|
|
1887
|
+
envFilePath: parsed.args.envFile ?? defaultSecretsPath(),
|
|
1888
|
+
registryPath,
|
|
1889
|
+
query: parsed.args.query,
|
|
1890
|
+
localSliccVersion,
|
|
1891
|
+
});
|
|
1892
|
+
if (result.versionMismatch) {
|
|
1893
|
+
console.warn(`Warning: running sandbox is sliccVersion=${result.versionMismatch.running}, ` +
|
|
1894
|
+
`local CLI is ${result.versionMismatch.local}. Proceeding anyway.`);
|
|
1895
|
+
}
|
|
1896
|
+
if (result.trayRebuilt) {
|
|
1897
|
+
console.warn('Tray was rebuilt; existing followers must re-attach to the new join URL.');
|
|
1898
|
+
}
|
|
1899
|
+
console.log(`Resumed. Open: ${result.joinUrl}`);
|
|
1900
|
+
break;
|
|
1901
|
+
}
|
|
1902
|
+
case 'kill':
|
|
1903
|
+
await runKill({ substrate, registryPath, query: parsed.args.query });
|
|
1904
|
+
console.log('Killed.');
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1765
1908
|
main().catch((err) => {
|
|
1766
1909
|
console.error('Fatal error:', err);
|
|
1767
1910
|
const errorData = err instanceof Error
|