sliccy 3.26.0 → 3.28.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/dist/node-server/_cloud_core/src/cone-config/index.d.ts +77 -0
- package/dist/node-server/_cloud_core/src/cone-config/index.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/src/cone-config/index.js +209 -0
- package/dist/node-server/_cloud_core/src/index.d.ts +1 -0
- package/dist/node-server/_cloud_core/src/index.d.ts.map +1 -1
- package/dist/node-server/_cloud_core/src/index.js +6 -0
- package/dist/node-server/_cloud_core/src/operations/resume.d.ts +13 -0
- package/dist/node-server/_cloud_core/src/operations/resume.d.ts.map +1 -1
- package/dist/node-server/_cloud_core/src/operations/resume.js +117 -3
- package/dist/node-server/_cloud_core/src/operations/start.d.ts +3 -0
- package/dist/node-server/_cloud_core/src/operations/start.d.ts.map +1 -1
- package/dist/node-server/_cloud_core/src/operations/start.js +8 -0
- package/dist/node-server/_cloud_core/src/types.d.ts +7 -0
- package/dist/node-server/_cloud_core/src/types.d.ts.map +1 -1
- package/dist/node-server/_cloud_core/tests/cone-config/cone-config.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/cone-config/cone-config.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/cone-config/cone-config.test.js +119 -0
- package/dist/node-server/_cloud_core/tests/resume-cone-config.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/resume-cone-config.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/resume-cone-config.test.js +57 -0
- package/dist/node-server/_cloud_core/tests/start-cone-config.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/start-cone-config.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/start-cone-config.test.js +65 -0
- package/dist/node-server/hosted-bootstrap.d.ts +9 -13
- package/dist/node-server/hosted-bootstrap.js +51 -8
- package/dist/node-server/index.js +2 -0
- package/dist/node-server/secrets-reload-endpoint.d.ts +7 -0
- package/dist/node-server/secrets-reload-endpoint.js +7 -0
- package/dist/ui/assets/{adobe-CqrEgLNi.js → adobe-BsI2QHM-.js} +1 -1
- package/dist/ui/assets/adobe-CMOt6RBV.js +2 -0
- package/dist/ui/assets/{agent-bridge-DLoHpCQV.js → agent-bridge-hW415QAE.js} +1 -1
- package/dist/ui/assets/{agent-message-to-chat-DAB_RuwN.js → agent-message-to-chat-DxvgRMh2.js} +1 -1
- package/dist/ui/assets/{anthropic-M0Z3vEYG.js → anthropic-6xJJ2YZO.js} +1 -1
- package/dist/ui/assets/{anthropic-BlLRyvML.js → anthropic-BC6K6cdY.js} +2 -2
- package/dist/ui/assets/{apps-BEDw2CRe.js → apps-Dr9FIzjl.js} +1 -1
- package/dist/ui/assets/azure-openai-BMUF5f0d.js +1 -0
- package/dist/ui/assets/{azure-openai-k9R7HvYf.js → azure-openai-BaAPMfG9.js} +1 -1
- package/dist/ui/assets/{azure-openai-responses-DDHZ47hL.js → azure-openai-responses-CG2x6ghy.js} +1 -1
- package/dist/ui/assets/azure-openai-responses-DJv1qDQU.js +1 -0
- package/dist/ui/assets/{biome_wasm-CTxVnl7G.js → biome_wasm-BzCoRiez.js} +1 -1
- package/dist/ui/assets/{bsh-watchdog-CkHKF91q.js → bsh-watchdog-BhyyrsLG.js} +1 -1
- package/dist/ui/assets/{cdp-BwSaINyX.js → cdp-CVhzXRUr.js} +4 -4
- package/dist/ui/assets/{cdp-ws-page-bridge-BZg_arql.js → cdp-ws-page-bridge-DIQyPbnc.js} +1 -1
- package/dist/ui/assets/cloud-DhBwfVPK.js +1 -0
- package/dist/ui/assets/cloud-DwpQLDy4.css +1 -0
- package/dist/ui/assets/{common-BXJu90Rc.js → common-CuKB31Bx.js} +1 -1
- package/dist/ui/assets/connect-surface-CCGvblVO.js +103 -0
- package/dist/ui/assets/cost-command-7TPmiftF.js +1 -0
- package/dist/ui/assets/{es-48UZpaVV.js → es-Cv6f-0s7.js} +1 -1
- package/dist/ui/assets/{follower-sprinkle-bridge-jm6CXbz1.js → follower-sprinkle-bridge-DEAKinMn.js} +1 -1
- package/dist/ui/assets/github-8u3THsb9.js +1 -0
- package/dist/ui/assets/github-IUCTxlu3.js +2 -0
- package/dist/ui/assets/github-copilot-BpjWdMXh.js +2 -0
- package/dist/ui/assets/{github-copilot-C2tBzQ4u.js → github-copilot-DgFjN6KZ.js} +1 -1
- package/dist/ui/assets/google-CE3pVXMY.js +1 -0
- package/dist/ui/assets/{google-BZiIgeFw.js → google-fooyUc87.js} +1 -1
- package/dist/ui/assets/{google-shared-BcE4P_yG.js → google-shared-C_9EKNrW.js} +1 -1
- package/dist/ui/assets/{google-shared--uY0bz2s.js → google-shared-CvX1HGJK.js} +1 -1
- package/dist/ui/assets/{google-vertex-D9lRdxr2.js → google-vertex-FAi9wG7t.js} +1 -1
- package/dist/ui/assets/{google-vertex-CwZ7zLwx.js → google-vertex-Wf1pkd9h.js} +1 -1
- package/dist/ui/assets/hosted-config-apply-BuSIcpqX.js +1 -0
- package/dist/ui/assets/{intercepted-oauth-D2d-qzF2.js → intercepted-oauth-onIAQjXH.js} +1 -1
- package/dist/ui/assets/{kernel-worker-qa_GIQ_O.js → kernel-worker-DrqmmlLF.js} +1185 -1062
- package/dist/ui/assets/{lick-ws-bridge-lwDDVM1H.js → lick-ws-bridge-DEwdAGsC.js} +1 -1
- package/dist/ui/assets/{local-llm-s5TSv3VA.js → local-llm-Q03t5xci.js} +2 -2
- package/dist/ui/assets/{magick-wasm-Dg17fb8P.js → magick-wasm-BA_z-_am.js} +1 -1
- package/dist/ui/assets/{main-pholQCCK.js → main-BlxLnse7.js} +188 -188
- package/dist/ui/assets/main-cherry-D44F4Vlh.js +1 -0
- package/dist/ui/assets/{mistral-8auDf7Jd.js → mistral-DEjW9qqh.js} +2 -2
- package/dist/ui/assets/{mistral-DWhJFHHy.js → mistral-yWGuKrq4.js} +1 -1
- package/dist/ui/assets/{nodejs-BMz0dS6L.js → nodejs-CfVKCb8p.js} +1 -1
- package/dist/ui/assets/{nuke-command-Ct7N3bt9.js → nuke-command-BHFb7iYV.js} +1 -1
- package/dist/ui/assets/{oauth-B_aSN2_9.js → oauth-DaANiDrq.js} +1 -1
- package/dist/ui/assets/oauth-bootstrap-DdTo_8kc.js +2 -0
- package/dist/ui/assets/{oauth-service-CSONhiAr.js → oauth-service-CGlrsCXI.js} +1 -1
- package/dist/ui/assets/{oauth-service-CWG5aTag.js → oauth-service-Ct9lZUcR.js} +1 -1
- package/dist/ui/assets/offscreen-client-DQeDUS4X.js +1 -0
- package/dist/ui/assets/{onboarding-orchestrator-sLJH00ba.js → onboarding-orchestrator-BN98n0DZ.js} +1 -1
- package/dist/ui/assets/{openai-codex-kcOw2Vdc.js → openai-codex-DCZS82Tv.js} +1 -1
- package/dist/ui/assets/openai-codex-H4Rpya6U.js +1 -0
- package/dist/ui/assets/{openai-codex-responses-OSkMGGNT.js → openai-codex-responses-BOKYXvIU.js} +5 -5
- package/dist/ui/assets/{openai-codex-responses-DUtA00zk.js → openai-codex-responses-D8oxdjNx.js} +1 -1
- package/dist/ui/assets/openai-completions-zAlP8W8O.js +5 -0
- package/dist/ui/assets/openai-responses-1AuAFhF4.js +1 -0
- package/dist/ui/assets/{openai-responses-D4FZMY-V.js → openai-responses-Bm1d6S7j.js} +1 -1
- package/dist/ui/assets/{openai-responses-shared-brUOwaY9.js → openai-responses-shared-CyGENinm.js} +1 -1
- package/dist/ui/assets/{openai-responses-shared-D1uRqOzX.js → openai-responses-shared-DtjQSz4-.js} +4 -4
- package/dist/ui/assets/{openrouter-yGOsBjDk.js → openrouter-BjNEb_vt.js} +1 -1
- package/dist/ui/assets/openrouter-Cy7d-Hse.js +1 -0
- package/dist/ui/assets/panel-rpc-handlers-BIBd7qwX.js +1 -0
- package/dist/ui/assets/{provider-B2-C4Uc0.js → provider-B4kcdYSm.js} +2 -2
- package/dist/ui/assets/{provider-B-Z77g0j.js → provider-D62HUJ72.js} +1 -1
- package/dist/ui/assets/provider-store-access-DUMZRNG6.js +1 -0
- package/dist/ui/assets/provider-store-access-XwHcqQAV.js +1 -0
- package/dist/ui/assets/{proxied-fetch-fouZMPr0.js → proxied-fetch-Ku3mKDDw.js} +1 -1
- package/dist/ui/assets/{pyodide-D0Y6NsDy.js → pyodide-BzzXGKXZ.js} +2 -2
- package/dist/ui/assets/{remote-terminal-view-B5-dZXNa.js → remote-terminal-view-Aip8SZl4.js} +1 -1
- package/dist/ui/assets/{secret-env-BTjJvpLw.js → secret-env-BXrNc3Yc.js} +1 -1
- package/dist/ui/assets/{slicc-editor-BjOPBIPG.js → slicc-editor-DxNnkMN4.js} +1 -1
- package/dist/ui/assets/{spawn-8eiTHKfU.js → spawn-NJPpLzuU.js} +1 -1
- package/dist/ui/assets/{sql-wasm-B1Tsjops.js → sql-wasm-BatnsmsR.js} +1 -1
- package/dist/ui/assets/{store-DRudQTL3.js → store-Cu-dZ-Qd.js} +1 -1
- package/dist/ui/assets/{tray-leave-runtime-Cs2vcG7Q.js → tray-leave-runtime-B3OyxolX.js} +1 -1
- package/dist/ui/assets/{typescript-CpCsC2U4.js → typescript-DAY1Zzuh.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-B_PXlklw.js → upgrade-detection-BSSXcDy5.js} +1 -1
- package/dist/ui/assets/{web-oHZ28cEx.js → web-CJrS_pPN.js} +1 -1
- package/dist/ui/assets/{ws-subscribers-BKg2braa.js → ws-subscribers-B8hdAhNQ.js} +1 -1
- package/dist/ui/assets/{xai-grok-CrDfS2o4.js → xai-grok-CKHIBDys.js} +1 -1
- package/dist/ui/assets/{xai-grok-BIGY0Sih.js → xai-grok-Cil1zzx5.js} +1 -1
- package/dist/ui/electron-overlay-entry.js +3 -3
- package/dist/ui/index.html +1 -1
- package/dist/ui/packages/webapp/cloud/index.html +44 -6
- package/dist/ui/packages/webapp/index.html +1 -1
- package/package.json +5 -3
- package/dist/ui/assets/adobe-ccKB80po.js +0 -2
- package/dist/ui/assets/azure-openai-9hADAfm8.js +0 -1
- package/dist/ui/assets/azure-openai-responses-BylSyLnY.js +0 -1
- package/dist/ui/assets/cloud-CZM-12lk.js +0 -1
- package/dist/ui/assets/cloud-CyzxzXN0.css +0 -1
- package/dist/ui/assets/cost-command-BoMWdldA.js +0 -1
- package/dist/ui/assets/github-BJKlBN9d.js +0 -2
- package/dist/ui/assets/github-C4AFIIt2.js +0 -1
- package/dist/ui/assets/github-copilot-BgQsh-to.js +0 -2
- package/dist/ui/assets/google-CnX2AAb4.js +0 -1
- package/dist/ui/assets/oauth-bootstrap-g4iVUmBv.js +0 -2
- package/dist/ui/assets/offscreen-client-VoSRhHbL.js +0 -1
- package/dist/ui/assets/openai-codex-vn0_7U9V.js +0 -1
- package/dist/ui/assets/openai-completions-XCsW7xai.js +0 -5
- package/dist/ui/assets/openai-responses-DYkL5h7J.js +0 -1
- package/dist/ui/assets/openrouter-B3noN2UV.js +0 -1
- package/dist/ui/assets/panel-rpc-handlers-HU3Mwr0v.js +0 -1
- package/dist/ui/assets/provider-store-access-BSELjH0e.js +0 -1
- package/dist/ui/assets/provider-store-access-hpnqQa7b.js +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export interface OAuthAccount {
|
|
2
|
+
providerId: string;
|
|
3
|
+
kind: 'oauth';
|
|
4
|
+
accessToken: string;
|
|
5
|
+
refreshToken?: string;
|
|
6
|
+
tokenExpiresAt?: number;
|
|
7
|
+
userName?: string;
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ApiKeyAccount {
|
|
11
|
+
providerId: string;
|
|
12
|
+
kind: 'apikey';
|
|
13
|
+
apiKey: string;
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
deployment?: string;
|
|
16
|
+
apiVersion?: string;
|
|
17
|
+
}
|
|
18
|
+
export type Account = OAuthAccount | ApiKeyAccount;
|
|
19
|
+
export interface SecretEntry {
|
|
20
|
+
name: string;
|
|
21
|
+
value: string;
|
|
22
|
+
domains: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface ConeConfig {
|
|
25
|
+
model: string;
|
|
26
|
+
accounts: Account[];
|
|
27
|
+
secrets: SecretEntry[];
|
|
28
|
+
}
|
|
29
|
+
export interface ConeConfigDelta {
|
|
30
|
+
model?: string;
|
|
31
|
+
upsert?: {
|
|
32
|
+
accounts?: Account[];
|
|
33
|
+
secrets?: SecretEntry[];
|
|
34
|
+
};
|
|
35
|
+
delete?: {
|
|
36
|
+
providerIds?: string[];
|
|
37
|
+
secretNames?: string[];
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface ConeConfigIndex {
|
|
41
|
+
model: string;
|
|
42
|
+
accountProviderIds: string[];
|
|
43
|
+
accountMeta: Array<{
|
|
44
|
+
providerId: string;
|
|
45
|
+
kind: Account['kind'];
|
|
46
|
+
tokenExpiresAt?: number;
|
|
47
|
+
}>;
|
|
48
|
+
secretNames: string[];
|
|
49
|
+
}
|
|
50
|
+
/** Max serialized bundle size (bytes) accepted as a preboot env payload. */
|
|
51
|
+
export declare const MAX_CONE_CONFIG_BYTES: number;
|
|
52
|
+
export declare function validateConeConfig(input: unknown): ConeConfig;
|
|
53
|
+
/**
|
|
54
|
+
* Validate an untrusted resume delta (the worker receives it as `unknown`).
|
|
55
|
+
* Nested accounts/secrets go through the same validators as a full bundle, so a
|
|
56
|
+
* malformed or newline-injecting entry is rejected at the boundary with a clear
|
|
57
|
+
* message rather than blowing up later inside mergeConeConfig.
|
|
58
|
+
*/
|
|
59
|
+
export declare function validateConeConfigDelta(input: unknown): ConeConfigDelta;
|
|
60
|
+
export declare function mergeConeConfig(base: ConeConfig, delta: ConeConfigDelta): ConeConfig;
|
|
61
|
+
/**
|
|
62
|
+
* Serialize flat secrets to the `NAME=value` / `NAME_DOMAINS=a,b` line format
|
|
63
|
+
* that node-server's EnvSecretStore reads. Values are written verbatim (no
|
|
64
|
+
* escaping — matching the existing parser), so secret names must be env-var
|
|
65
|
+
* identifiers and values/domains must be single-line (no newlines, and values
|
|
66
|
+
* must not break `NAME=value` parsing). Callers sanitize/validate inputs.
|
|
67
|
+
*/
|
|
68
|
+
export declare function serializeSecretsEnv(secrets: SecretEntry[]): string;
|
|
69
|
+
export declare function bundleToFiles(cfg: ConeConfig): {
|
|
70
|
+
coneConfigJson: string;
|
|
71
|
+
secretsEnv: string;
|
|
72
|
+
};
|
|
73
|
+
export declare function bundleIndex(cfg: ConeConfig): ConeConfigIndex;
|
|
74
|
+
/** Portable base64 of a UTF-8 string (worker/browser/node all have btoa+TextEncoder). */
|
|
75
|
+
export declare function encodeBundleEnv(json: string): string;
|
|
76
|
+
export declare function decodeBundleEnv(b64: string): string;
|
|
77
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cone-config/index.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AACD,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG,aAAa,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAA;KAAE,CAAC;IAC3D,MAAM,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAC7D;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,WAAW,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3F,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAMhD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAS7D;AA6DD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAiDvE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CAYpF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAOlE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAM7F;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,eAAe,CAW5D;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQpD;AACD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAInD"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// Side-effect-free shared contract for cloud-cone configuration.
|
|
2
|
+
// MUST NOT import e2b, node:*, or any runtime substrate — it is imported
|
|
3
|
+
// by the browser webapp via the @slicc/cloud-core/cone-config subpath.
|
|
4
|
+
/** Max serialized bundle size (bytes) accepted as a preboot env payload. */
|
|
5
|
+
export const MAX_CONE_CONFIG_BYTES = 256 * 1024;
|
|
6
|
+
function isStr(v) {
|
|
7
|
+
return typeof v === 'string';
|
|
8
|
+
}
|
|
9
|
+
export function validateConeConfig(input) {
|
|
10
|
+
if (!input || typeof input !== 'object')
|
|
11
|
+
throw new Error('cone-config: not an object');
|
|
12
|
+
const cfg = input;
|
|
13
|
+
if (!isStr(cfg.model))
|
|
14
|
+
throw new Error('cone-config: model must be a string');
|
|
15
|
+
if (!Array.isArray(cfg.accounts))
|
|
16
|
+
throw new Error('cone-config: accounts must be an array');
|
|
17
|
+
if (!Array.isArray(cfg.secrets))
|
|
18
|
+
throw new Error('cone-config: secrets must be an array');
|
|
19
|
+
const accounts = cfg.accounts.map((a) => validateAccount(a));
|
|
20
|
+
const secrets = cfg.secrets.map((s) => validateSecret(s));
|
|
21
|
+
return { model: cfg.model, accounts, secrets };
|
|
22
|
+
}
|
|
23
|
+
function validateAccount(a) {
|
|
24
|
+
if (!a || typeof a !== 'object')
|
|
25
|
+
throw new Error('cone-config: account not an object');
|
|
26
|
+
const acc = a;
|
|
27
|
+
if (!isStr(acc.providerId))
|
|
28
|
+
throw new Error('cone-config: account.providerId required');
|
|
29
|
+
if (!isStr(acc.kind))
|
|
30
|
+
throw new Error('cone-config: account.kind required');
|
|
31
|
+
if (acc.kind === 'oauth') {
|
|
32
|
+
if (!isStr(acc.accessToken))
|
|
33
|
+
throw new Error('cone-config: oauth account requires accessToken');
|
|
34
|
+
return {
|
|
35
|
+
providerId: acc.providerId,
|
|
36
|
+
kind: 'oauth',
|
|
37
|
+
accessToken: acc.accessToken,
|
|
38
|
+
...(isStr(acc.refreshToken) ? { refreshToken: acc.refreshToken } : {}),
|
|
39
|
+
...(typeof acc.tokenExpiresAt === 'number' ? { tokenExpiresAt: acc.tokenExpiresAt } : {}),
|
|
40
|
+
...(isStr(acc.userName) ? { userName: acc.userName } : {}),
|
|
41
|
+
...(isStr(acc.baseUrl) ? { baseUrl: acc.baseUrl } : {}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (acc.kind === 'apikey') {
|
|
45
|
+
if (!isStr(acc.apiKey))
|
|
46
|
+
throw new Error('cone-config: apikey account requires apiKey');
|
|
47
|
+
return {
|
|
48
|
+
providerId: acc.providerId,
|
|
49
|
+
kind: 'apikey',
|
|
50
|
+
apiKey: acc.apiKey,
|
|
51
|
+
...(isStr(acc.baseUrl) ? { baseUrl: acc.baseUrl } : {}),
|
|
52
|
+
...(isStr(acc.deployment) ? { deployment: acc.deployment } : {}),
|
|
53
|
+
...(isStr(acc.apiVersion) ? { apiVersion: acc.apiVersion } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`cone-config: account.kind must be 'oauth' | 'apikey'`);
|
|
57
|
+
}
|
|
58
|
+
// secrets.env is line-based (NAME=value / NAME_DOMAINS=a,b) with no escaping,
|
|
59
|
+
// so a newline/CR in a name or value would inject phantom lines on round-trip.
|
|
60
|
+
// Names must be env-var identifiers; values and domains must be single-line.
|
|
61
|
+
const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
62
|
+
function hasNewline(v) {
|
|
63
|
+
return /[\r\n]/.test(v);
|
|
64
|
+
}
|
|
65
|
+
function validateSecret(s) {
|
|
66
|
+
if (!s || typeof s !== 'object')
|
|
67
|
+
throw new Error('cone-config: secret not an object');
|
|
68
|
+
const sec = s;
|
|
69
|
+
if (!isStr(sec.name))
|
|
70
|
+
throw new Error('cone-config: secret.name required');
|
|
71
|
+
if (!ENV_NAME_RE.test(sec.name)) {
|
|
72
|
+
throw new Error('cone-config: secret.name must be an env-var identifier ([A-Za-z_][A-Za-z0-9_]*)');
|
|
73
|
+
}
|
|
74
|
+
if (!isStr(sec.value))
|
|
75
|
+
throw new Error('cone-config: secret.value required');
|
|
76
|
+
if (hasNewline(sec.value))
|
|
77
|
+
throw new Error('cone-config: secret.value must be single-line');
|
|
78
|
+
if (!Array.isArray(sec.domains) || !sec.domains.every(isStr)) {
|
|
79
|
+
throw new Error('cone-config: secret.domains must be string[]');
|
|
80
|
+
}
|
|
81
|
+
if (sec.domains.some((d) => hasNewline(d) || d.includes(','))) {
|
|
82
|
+
throw new Error('cone-config: secret.domains entries must be single-line and comma-free');
|
|
83
|
+
}
|
|
84
|
+
return { name: sec.name, value: sec.value, domains: sec.domains };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validate an untrusted resume delta (the worker receives it as `unknown`).
|
|
88
|
+
* Nested accounts/secrets go through the same validators as a full bundle, so a
|
|
89
|
+
* malformed or newline-injecting entry is rejected at the boundary with a clear
|
|
90
|
+
* message rather than blowing up later inside mergeConeConfig.
|
|
91
|
+
*/
|
|
92
|
+
export function validateConeConfigDelta(input) {
|
|
93
|
+
if (!input || typeof input !== 'object')
|
|
94
|
+
throw new Error('cone-config: delta not an object');
|
|
95
|
+
const d = input;
|
|
96
|
+
const out = {};
|
|
97
|
+
if (d.model !== undefined) {
|
|
98
|
+
if (!isStr(d.model))
|
|
99
|
+
throw new Error('cone-config: delta.model must be a string');
|
|
100
|
+
out.model = d.model;
|
|
101
|
+
}
|
|
102
|
+
if (d.upsert !== undefined) {
|
|
103
|
+
if (!d.upsert || typeof d.upsert !== 'object') {
|
|
104
|
+
throw new Error('cone-config: delta.upsert must be an object');
|
|
105
|
+
}
|
|
106
|
+
const up = d.upsert;
|
|
107
|
+
const upsert = {};
|
|
108
|
+
if (up.accounts !== undefined) {
|
|
109
|
+
if (!Array.isArray(up.accounts)) {
|
|
110
|
+
throw new Error('cone-config: delta.upsert.accounts must be an array');
|
|
111
|
+
}
|
|
112
|
+
upsert.accounts = up.accounts.map((a) => validateAccount(a));
|
|
113
|
+
}
|
|
114
|
+
if (up.secrets !== undefined) {
|
|
115
|
+
if (!Array.isArray(up.secrets)) {
|
|
116
|
+
throw new Error('cone-config: delta.upsert.secrets must be an array');
|
|
117
|
+
}
|
|
118
|
+
upsert.secrets = up.secrets.map((s) => validateSecret(s));
|
|
119
|
+
}
|
|
120
|
+
out.upsert = upsert;
|
|
121
|
+
}
|
|
122
|
+
if (d.delete !== undefined) {
|
|
123
|
+
if (!d.delete || typeof d.delete !== 'object') {
|
|
124
|
+
throw new Error('cone-config: delta.delete must be an object');
|
|
125
|
+
}
|
|
126
|
+
const del = d.delete;
|
|
127
|
+
const deletion = {};
|
|
128
|
+
if (del.providerIds !== undefined) {
|
|
129
|
+
if (!Array.isArray(del.providerIds) || !del.providerIds.every(isStr)) {
|
|
130
|
+
throw new Error('cone-config: delta.delete.providerIds must be string[]');
|
|
131
|
+
}
|
|
132
|
+
deletion.providerIds = del.providerIds;
|
|
133
|
+
}
|
|
134
|
+
if (del.secretNames !== undefined) {
|
|
135
|
+
if (!Array.isArray(del.secretNames) || !del.secretNames.every(isStr)) {
|
|
136
|
+
throw new Error('cone-config: delta.delete.secretNames must be string[]');
|
|
137
|
+
}
|
|
138
|
+
deletion.secretNames = del.secretNames;
|
|
139
|
+
}
|
|
140
|
+
out.delete = deletion;
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
export function mergeConeConfig(base, delta) {
|
|
145
|
+
const accounts = new Map(base.accounts.map((a) => [a.providerId, a]));
|
|
146
|
+
for (const a of delta.upsert?.accounts ?? [])
|
|
147
|
+
accounts.set(a.providerId, a);
|
|
148
|
+
for (const id of delta.delete?.providerIds ?? [])
|
|
149
|
+
accounts.delete(id);
|
|
150
|
+
const secrets = new Map(base.secrets.map((s) => [s.name, s]));
|
|
151
|
+
for (const s of delta.upsert?.secrets ?? [])
|
|
152
|
+
secrets.set(s.name, s);
|
|
153
|
+
for (const n of delta.delete?.secretNames ?? [])
|
|
154
|
+
secrets.delete(n);
|
|
155
|
+
return {
|
|
156
|
+
model: delta.model ?? base.model,
|
|
157
|
+
accounts: [...accounts.values()],
|
|
158
|
+
secrets: [...secrets.values()],
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Serialize flat secrets to the `NAME=value` / `NAME_DOMAINS=a,b` line format
|
|
163
|
+
* that node-server's EnvSecretStore reads. Values are written verbatim (no
|
|
164
|
+
* escaping — matching the existing parser), so secret names must be env-var
|
|
165
|
+
* identifiers and values/domains must be single-line (no newlines, and values
|
|
166
|
+
* must not break `NAME=value` parsing). Callers sanitize/validate inputs.
|
|
167
|
+
*/
|
|
168
|
+
export function serializeSecretsEnv(secrets) {
|
|
169
|
+
const lines = [];
|
|
170
|
+
for (const s of secrets) {
|
|
171
|
+
lines.push(`${s.name}=${s.value}`);
|
|
172
|
+
lines.push(`${s.name}_DOMAINS=${s.domains.join(',')}`);
|
|
173
|
+
}
|
|
174
|
+
return lines.length ? lines.join('\n') + '\n' : '';
|
|
175
|
+
}
|
|
176
|
+
export function bundleToFiles(cfg) {
|
|
177
|
+
return {
|
|
178
|
+
// Secrets are excluded here and serialized separately into secretsEnv.
|
|
179
|
+
coneConfigJson: JSON.stringify({ model: cfg.model, accounts: cfg.accounts }),
|
|
180
|
+
secretsEnv: serializeSecretsEnv(cfg.secrets),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
export function bundleIndex(cfg) {
|
|
184
|
+
return {
|
|
185
|
+
model: cfg.model,
|
|
186
|
+
accountProviderIds: cfg.accounts.map((a) => a.providerId),
|
|
187
|
+
accountMeta: cfg.accounts.map((a) => ({
|
|
188
|
+
providerId: a.providerId,
|
|
189
|
+
kind: a.kind,
|
|
190
|
+
tokenExpiresAt: a.kind === 'oauth' ? a.tokenExpiresAt : undefined,
|
|
191
|
+
})),
|
|
192
|
+
secretNames: cfg.secrets.map((s) => s.name),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/** Portable base64 of a UTF-8 string (worker/browser/node all have btoa+TextEncoder). */
|
|
196
|
+
export function encodeBundleEnv(json) {
|
|
197
|
+
const bytes = new TextEncoder().encode(json);
|
|
198
|
+
let bin = '';
|
|
199
|
+
const CHUNK = 0x8000;
|
|
200
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
201
|
+
bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
202
|
+
}
|
|
203
|
+
return btoa(bin);
|
|
204
|
+
}
|
|
205
|
+
export function decodeBundleEnv(b64) {
|
|
206
|
+
const bin = atob(b64);
|
|
207
|
+
const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
208
|
+
return new TextDecoder().decode(bytes);
|
|
209
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAC/D,cAAc,cAAc,CAAC;AAC7B,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,cAAc,YAAY,CAAC"}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// Public re-exports for the @slicc/cloud-core package.
|
|
2
2
|
// Populated by subsequent tasks as we move code in.
|
|
3
|
+
// Re-export the side-effect-free cone-config contract from the root too, so
|
|
4
|
+
// Node/worker consumers (which go through node-server's inline-workspaces
|
|
5
|
+
// packaging that only rewrites the bare '@slicc/cloud-core' specifier) can
|
|
6
|
+
// import it from the root. The browser webapp must still import the
|
|
7
|
+
// './cone-config' subpath to avoid pulling e2b into its bundle.
|
|
8
|
+
export * from './cone-config/index.js';
|
|
3
9
|
export * from './errors.js';
|
|
4
10
|
export { killCone } from './operations/kill.js';
|
|
5
11
|
export { listCones } from './operations/list.js';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ConeConfigDelta, type ConeConfigIndex } from '../cone-config/index.js';
|
|
1
2
|
import type { Registry } from '../registry.js';
|
|
2
3
|
import type { SandboxSubstrate } from '../substrate.js';
|
|
3
4
|
import type { ResumeResult } from '../types.js';
|
|
@@ -12,11 +13,23 @@ export interface ResumeConeOpts {
|
|
|
12
13
|
* and BEFORE the leader-restart kick. Both CLI (cloud/resume.ts) and worker
|
|
13
14
|
* (cloud-sessions-do.ts) pass this unconditionally now to inject a fresh IMS bearer. */
|
|
14
15
|
refreshSecretsContents?: string;
|
|
16
|
+
/** Optional: merge delta into existing cone-config.json + secrets.env. */
|
|
17
|
+
coneConfigDelta?: ConeConfigDelta;
|
|
15
18
|
pollIntervalMs?: number;
|
|
16
19
|
pollTimeoutMs?: number;
|
|
17
20
|
/** Skip the state check (NOT_FOUND + ALREADY_RUNNING). Used by the worker
|
|
18
21
|
* after doing atomic cap-check + state-flip under DO lock. */
|
|
19
22
|
skipStateCheck?: boolean;
|
|
20
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Merge `delta` over the existing files; returns new file contents + names index.
|
|
26
|
+
* When `coneConfigJson` is null (a pre-feature cone), synthesizes a degenerate
|
|
27
|
+
* base from secrets.env.
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyConeConfigDelta(coneConfigJson: string | null, secretsEnv: string, delta: ConeConfigDelta): {
|
|
30
|
+
coneConfigJson: string;
|
|
31
|
+
secretsEnv: string;
|
|
32
|
+
index: ConeConfigIndex;
|
|
33
|
+
};
|
|
21
34
|
export declare function resumeCone(deps: ResumeConeDeps, opts: ResumeConeOpts): Promise<ResumeResult>;
|
|
22
35
|
//# sourceMappingURL=resume.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/operations/resume.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/operations/resume.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,eAAe,EAIrB,MAAM,yBAAyB,CAAC;AAGjC,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,0EAA0E;IAC1E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;kEAC8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAsCD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,eAAe,GACrB;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,eAAe,CAAA;CAAE,CA2BxE;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,YAAY,CAAC,CA0GvB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bundleIndex, bundleToFiles, mergeConeConfig, validateConeConfig, } from '../cone-config/index.js';
|
|
1
2
|
import { CloudError } from '../errors.js';
|
|
2
3
|
import { pollForRefreshedStatus } from '../polling.js';
|
|
3
4
|
// curl writes its body to /dev/null and prints the HTTP status code on stdout.
|
|
@@ -5,6 +6,66 @@ import { pollForRefreshedStatus } from '../polling.js';
|
|
|
5
6
|
// can distinguish "node-server not up yet" from "200/503 response received").
|
|
6
7
|
// Therefore: no `|| true`; we parse status from stdout AND check exitCode.
|
|
7
8
|
const KICK_CMD = 'curl -sS -X POST http://localhost:5710/api/leader-restart -o /dev/null -w "%{http_code}"';
|
|
9
|
+
const DEFAULT_MODEL = 'adobe:claude-opus-4-6';
|
|
10
|
+
function parseSecretsEnv(text) {
|
|
11
|
+
const domains = new Map();
|
|
12
|
+
for (const l of text.split('\n')) {
|
|
13
|
+
const eq = l.indexOf('=');
|
|
14
|
+
if (eq < 0)
|
|
15
|
+
continue;
|
|
16
|
+
const key = l.slice(0, eq);
|
|
17
|
+
if (key.endsWith('_DOMAINS')) {
|
|
18
|
+
domains.set(key.slice(0, -'_DOMAINS'.length), l
|
|
19
|
+
.slice(eq + 1)
|
|
20
|
+
.split(',')
|
|
21
|
+
.filter(Boolean));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const l of text.split('\n')) {
|
|
26
|
+
const eq = l.indexOf('=');
|
|
27
|
+
if (eq < 0)
|
|
28
|
+
continue;
|
|
29
|
+
const name = l.slice(0, eq);
|
|
30
|
+
if (name.endsWith('_DOMAINS'))
|
|
31
|
+
continue;
|
|
32
|
+
out.push({ name, value: l.slice(eq + 1), domains: domains.get(name) ?? [] });
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Merge `delta` over the existing files; returns new file contents + names index.
|
|
38
|
+
* When `coneConfigJson` is null (a pre-feature cone), synthesizes a degenerate
|
|
39
|
+
* base from secrets.env.
|
|
40
|
+
*/
|
|
41
|
+
export function applyConeConfigDelta(coneConfigJson, secretsEnv, delta) {
|
|
42
|
+
let base;
|
|
43
|
+
if (coneConfigJson) {
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(coneConfigJson);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
// Distinguish a corrupt on-disk file from invalid data, so the surfaced
|
|
50
|
+
// error points at the right cause instead of an opaque SyntaxError.
|
|
51
|
+
throw new Error(`cone-config: corrupt /slicc/cone-config.json: ${err instanceof Error ? err.message : String(err)}`);
|
|
52
|
+
}
|
|
53
|
+
base = validateConeConfig({
|
|
54
|
+
model: parsed.model ?? DEFAULT_MODEL,
|
|
55
|
+
accounts: parsed.accounts ?? [],
|
|
56
|
+
secrets: parseSecretsEnv(secretsEnv),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
base = { model: DEFAULT_MODEL, accounts: [], secrets: parseSecretsEnv(secretsEnv) };
|
|
61
|
+
}
|
|
62
|
+
// Validate the MERGED result, not just the base: delta upserts arrive as
|
|
63
|
+
// unknown from the worker and are otherwise unvalidated, so this is where a
|
|
64
|
+
// newline-injecting secret value or bad name gets rejected before serialization.
|
|
65
|
+
const merged = validateConeConfig(mergeConeConfig(base, delta));
|
|
66
|
+
const files = bundleToFiles(merged);
|
|
67
|
+
return { ...files, index: bundleIndex(merged) };
|
|
68
|
+
}
|
|
8
69
|
export async function resumeCone(deps, opts) {
|
|
9
70
|
// skipStateCheck is used by the worker after doing atomic cap-check + state-flip
|
|
10
71
|
// under DO lock. CLI still wants the NOT_FOUND + ALREADY_RUNNING checks.
|
|
@@ -30,9 +91,35 @@ export async function resumeCone(deps, opts) {
|
|
|
30
91
|
const baselineUpdatedAt = entry.lastJoinUpdatedAt;
|
|
31
92
|
const baselineTrayId = entry.trayId;
|
|
32
93
|
const handle = await deps.substrate.connect(entry.sandboxId);
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
if (opts.
|
|
94
|
+
let resumeIndex;
|
|
95
|
+
// New: coneConfigDelta takes precedence — read existing files, merge, write both.
|
|
96
|
+
if (opts.coneConfigDelta) {
|
|
97
|
+
let existingConeConfig = null;
|
|
98
|
+
try {
|
|
99
|
+
existingConeConfig = await handle.readFile('/slicc/cone-config.json');
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
existingConeConfig = null;
|
|
103
|
+
}
|
|
104
|
+
let existingSecretsEnv = '';
|
|
105
|
+
try {
|
|
106
|
+
existingSecretsEnv = await handle.readFile('/slicc/secrets.env');
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
existingSecretsEnv = '';
|
|
110
|
+
}
|
|
111
|
+
const applied = applyConeConfigDelta(existingConeConfig, existingSecretsEnv, opts.coneConfigDelta);
|
|
112
|
+
await handle.writeFile('/slicc/secrets.env', applied.secretsEnv);
|
|
113
|
+
await handle.writeFile('/slicc/cone-config.json', applied.coneConfigJson);
|
|
114
|
+
// Reload the fetch-proxy masking so changed flat secrets take effect. Must
|
|
115
|
+
// succeed before the leader Page.reload — a silent failure here would leave
|
|
116
|
+
// the running cone with stale flat secrets while reporting resume success.
|
|
117
|
+
await reloadSecretsProxyUntilReady(handle);
|
|
118
|
+
resumeIndex = applied.index;
|
|
119
|
+
}
|
|
120
|
+
else if (opts.refreshSecretsContents !== undefined) {
|
|
121
|
+
// Legacy path: both CLI and worker pass this now to inject a fresh IMS bearer.
|
|
122
|
+
// Write to /slicc/secrets.env BEFORE the kick loop.
|
|
36
123
|
await handle.writeFile('/slicc/secrets.env', opts.refreshSecretsContents);
|
|
37
124
|
}
|
|
38
125
|
// Kick the leader to recover from a possible onReconnectGaveUp state.
|
|
@@ -67,6 +154,7 @@ export async function resumeCone(deps, opts) {
|
|
|
67
154
|
joinUrl: refreshed.joinUrl,
|
|
68
155
|
trayRebuilt,
|
|
69
156
|
...(versionMismatch ? { versionMismatch } : {}),
|
|
157
|
+
coneConfigIndex: resumeIndex,
|
|
70
158
|
};
|
|
71
159
|
}
|
|
72
160
|
async function kickLeaderUntilReady(handle) {
|
|
@@ -84,3 +172,29 @@ async function kickLeaderUntilReady(handle) {
|
|
|
84
172
|
}
|
|
85
173
|
return false;
|
|
86
174
|
}
|
|
175
|
+
const RELOAD_CMD = 'curl -sS -X POST http://localhost:5710/api/secrets/reload -o /dev/null -w "%{http_code}"';
|
|
176
|
+
// Reload the node-server secret proxy, retrying the cold-start (503/connect)
|
|
177
|
+
// window like the leader kick. Throws if it never succeeds — a stale fetch-proxy
|
|
178
|
+
// would silently serve old flat secrets.
|
|
179
|
+
async function reloadSecretsProxyUntilReady(handle) {
|
|
180
|
+
// Track why the last attempt didn't succeed so the exhaustion error is
|
|
181
|
+
// actionable (HTTP 503 cold-start vs. a connection-level curl failure).
|
|
182
|
+
let lastError = 'no attempt made';
|
|
183
|
+
for (let i = 0; i < 5; i++) {
|
|
184
|
+
const result = await handle.run(RELOAD_CMD);
|
|
185
|
+
if (result.exitCode === 0) {
|
|
186
|
+
const status = result.stdout.trim();
|
|
187
|
+
if (status === '200')
|
|
188
|
+
return;
|
|
189
|
+
if (status !== '503') {
|
|
190
|
+
throw new CloudError('INTERNAL', `/api/secrets/reload returned unexpected status ${status}`);
|
|
191
|
+
}
|
|
192
|
+
lastError = `HTTP ${status}`;
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
lastError = `curl exit ${result.exitCode}: ${result.stderr.trim()}`;
|
|
196
|
+
}
|
|
197
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
198
|
+
}
|
|
199
|
+
throw new CloudError('INTERNAL', `Failed to reload secrets proxy after 5 retries (changed secrets may be stale; last: ${lastError})`);
|
|
200
|
+
}
|
|
@@ -27,6 +27,9 @@ export interface StartConeOpts {
|
|
|
27
27
|
/** Optional reservation ID from reserveSlot(); if provided, updates that
|
|
28
28
|
* placeholder entry instead of appending a new one. */
|
|
29
29
|
reservationId?: string;
|
|
30
|
+
/** Optional cone config JSON; injected as SLICC_CONE_CONFIG_B64 env and
|
|
31
|
+
* written to /slicc/cone-config.json after create. */
|
|
32
|
+
coneConfigJson?: string;
|
|
30
33
|
}
|
|
31
34
|
export interface StartConeDeps {
|
|
32
35
|
substrate: SandboxSubstrate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/operations/start.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../../src/operations/start.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,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;IACvB;0DACsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;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,CAyJ9F"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeBundleEnv } from '../cone-config/index.js';
|
|
1
2
|
import { CloudError } from '../errors.js';
|
|
2
3
|
import { pollCloudStatus } from '../polling.js';
|
|
3
4
|
import { filterSecretsEnv } from '../secrets-filter.js';
|
|
@@ -98,6 +99,10 @@ export async function startCone(deps, opts) {
|
|
|
98
99
|
autoPauseOnCap: opts.autoPauseOnCap ?? true,
|
|
99
100
|
envVars: {
|
|
100
101
|
SLICC_TRAY_WORKER_BASE_URL: opts.workerBaseUrl,
|
|
102
|
+
SLICC_SECRETS_ENV_B64: encodeBundleEnv(safeSecrets),
|
|
103
|
+
...(opts.coneConfigJson
|
|
104
|
+
? { SLICC_CONE_CONFIG_B64: encodeBundleEnv(opts.coneConfigJson) }
|
|
105
|
+
: {}),
|
|
101
106
|
...(opts.envs ?? {}),
|
|
102
107
|
},
|
|
103
108
|
metadata: {
|
|
@@ -166,6 +171,9 @@ export async function startCone(deps, opts) {
|
|
|
166
171
|
// because the page-side bootstrap polls /api/hosted-bootstrap after a 5s
|
|
167
172
|
// delay, by which time this writeFile has landed.
|
|
168
173
|
await handle.writeFile('/slicc/secrets.env', safeSecrets);
|
|
174
|
+
if (opts.coneConfigJson) {
|
|
175
|
+
await handle.writeFile('/slicc/cone-config.json', opts.coneConfigJson);
|
|
176
|
+
}
|
|
169
177
|
let status;
|
|
170
178
|
try {
|
|
171
179
|
status = await pollCloudStatus(handle, {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConeConfigIndex } from './cone-config/index.js';
|
|
1
2
|
/**
|
|
2
3
|
* Registry entry for a cloud-hosted cone (sandbox). Renamed from
|
|
3
4
|
* CloudSessionEntry in node-server — the canonical name going forward is
|
|
@@ -47,6 +48,11 @@ export interface ConeEntry {
|
|
|
47
48
|
* filtering and by CLI for tracking.
|
|
48
49
|
*/
|
|
49
50
|
metadata?: Record<string, string>;
|
|
51
|
+
/**
|
|
52
|
+
* Names-only index of the cone's config bundle (model, account provider IDs,
|
|
53
|
+
* secret names). Persisted on the registry; NO secret values stored.
|
|
54
|
+
*/
|
|
55
|
+
coneConfigIndex?: ConeConfigIndex;
|
|
50
56
|
}
|
|
51
57
|
/**
|
|
52
58
|
* The shape persisted to `/tmp/slicc-join.json` inside the sandbox. Read by
|
|
@@ -80,6 +86,7 @@ export interface ResumeResult {
|
|
|
80
86
|
running: string;
|
|
81
87
|
local: string;
|
|
82
88
|
};
|
|
89
|
+
coneConfigIndex?: ConeConfigIndex;
|
|
83
90
|
}
|
|
84
91
|
/**
|
|
85
92
|
* Summary of a sandbox returned by substrate.list(). Minimal shape with ID,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IAClD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IAClD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cone-config.test.d.ts","sourceRoot":"","sources":["../../../tests/cone-config/cone-config.test.ts"],"names":[],"mappings":""}
|