sliccy 4.26.0 → 4.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/node-server/_cloud_core/src/cone-config/index.d.ts +21 -0
  2. package/dist/node-server/_cloud_core/src/cone-config/index.d.ts.map +1 -1
  3. package/dist/node-server/_cloud_core/src/cone-config/index.js +91 -40
  4. package/dist/node-server/_cloud_core/src/index.d.ts +1 -1
  5. package/dist/node-server/_cloud_core/src/index.d.ts.map +1 -1
  6. package/dist/node-server/_cloud_core/src/index.js +1 -1
  7. package/dist/node-server/_cloud_core/src/substrates/e2b.d.ts +9 -0
  8. package/dist/node-server/_cloud_core/src/substrates/e2b.d.ts.map +1 -1
  9. package/dist/node-server/_cloud_core/src/substrates/e2b.js +14 -2
  10. package/dist/node-server/_cloud_core/tests/cone-config/cone-config.test.js +34 -1
  11. package/dist/node-server/_cloud_core/tests/e2b-substrate.test.d.ts +2 -0
  12. package/dist/node-server/_cloud_core/tests/e2b-substrate.test.d.ts.map +1 -0
  13. package/dist/node-server/_cloud_core/tests/e2b-substrate.test.js +18 -0
  14. package/dist/node-server/cloud/dispatch.d.ts +1 -0
  15. package/dist/node-server/cloud/dispatch.js +47 -29
  16. package/dist/node-server/cloud/start.d.ts +14 -0
  17. package/dist/node-server/cloud/start.js +15 -1
  18. package/dist/node-server/hosted-bootstrap.d.ts +2 -1
  19. package/dist/node-server/hosted-bootstrap.js +17 -1
  20. package/dist/node-server/index.js +1 -0
  21. package/dist/ui/assets/{account-store-C_cA3i6g.js → account-store-CtbDaKNT.js} +2 -2
  22. package/dist/ui/assets/{account-store-QleYDKnG.js → account-store-hW-z9pMn.js} +2 -2
  23. package/dist/ui/assets/{adobe-_MN845Fw.js → adobe-Dwwy6Bwz.js} +1 -1
  24. package/dist/ui/assets/{adobe-Buv0zDrc.js → adobe-e8BjbvOH.js} +1 -1
  25. package/dist/ui/assets/{agent-message-to-chat-BISoOCaW.js → agent-message-to-chat-M_C7Wn3B.js} +1 -1
  26. package/dist/ui/assets/{apps-C28amp1i.js → apps-CHnfdOOD.js} +1 -1
  27. package/dist/ui/assets/{azure-openai-CGn8k87V.js → azure-openai-BFiad0DN.js} +1 -1
  28. package/dist/ui/assets/{azure-openai-BUhRZLl1.js → azure-openai-DtXC0Grf.js} +1 -1
  29. package/dist/ui/assets/{bsh-watchdog-DQkgs5b2.js → bsh-watchdog-kim_Mcz1.js} +1 -1
  30. package/dist/ui/assets/{connect-surface-CsN-fRzW.js → connect-surface-CVcCUQYW.js} +1 -1
  31. package/dist/ui/assets/dip-DvwhAcEx.js +1 -0
  32. package/dist/ui/assets/{dist-Q6Ijgx42.js → dist-CT085FLs.js} +1 -1
  33. package/dist/ui/assets/{dist-Ud2wprp1.js → dist-DCg8Qlqz.js} +1 -1
  34. package/dist/ui/assets/{es-ykMvrvgd.js → es-CA3562iO.js} +1 -1
  35. package/dist/ui/assets/{fs-DoZH1t2b.js → fs-DN_AGaO-.js} +2 -2
  36. package/dist/ui/assets/{fs-BYPY106y.js → fs-DwXCCL3T.js} +1 -1
  37. package/dist/ui/assets/{github-CeXLHUEm.js → github-9cZGng-I.js} +1 -1
  38. package/dist/ui/assets/{github-C9PTf7-p.js → github-BVsj9inT.js} +2 -2
  39. package/dist/ui/assets/{github-copilot-BfaeGhAx.js → github-copilot-CJaORkJL.js} +1 -1
  40. package/dist/ui/assets/{github-copilot--a7qwxPo.js → github-copilot-CuElbWkp.js} +1 -1
  41. package/dist/ui/assets/{hear-CHDQfguT.js → hear-QFVMe7Z0.js} +1 -1
  42. package/dist/ui/assets/{kernel-worker-JEGL1H9y.js → kernel-worker-BklfHGpd.js} +20 -20
  43. package/dist/ui/assets/{kokoro-engine-kv1GOKX7.js → kokoro-engine-Dcc_f5OM.js} +1 -1
  44. package/dist/ui/assets/{lick-ws-bridge-CYeKRx-9.js → lick-ws-bridge-DbIMcfT_.js} +1 -1
  45. package/dist/ui/assets/{local-llm-BD11kv77.js → local-llm-0sAJUnfR.js} +1 -1
  46. package/dist/ui/assets/{main-CE5zQ7n8.js → main-Bl07XOVq.js} +3 -3
  47. package/dist/ui/assets/{mount-B2wqIfs8.js → mount-B_InVB1Y.js} +2 -2
  48. package/dist/ui/assets/{mount-D8Dafwe1.js → mount-D-a4sEx2.js} +1 -1
  49. package/dist/ui/assets/{new-session-CoPL13pP.js → new-session-DS1zKNso.js} +2 -2
  50. package/dist/ui/assets/{oauth-bootstrap-Bok5Qb_f.js → oauth-bootstrap-CLs4o9ky.js} +2 -2
  51. package/dist/ui/assets/{oauth-service-DMG4VUb6.js → oauth-service-DCMoV7Jn.js} +1 -1
  52. package/dist/ui/assets/{openai-codex-9Wve47FT.js → openai-codex-BctpB8uS.js} +1 -1
  53. package/dist/ui/assets/{openai-codex-2S-Cwbop.js → openai-codex-Dp2IHpw_.js} +1 -1
  54. package/dist/ui/assets/{panel-rpc-handlers-CpmURTlO.js → panel-rpc-handlers-LfzQkir_.js} +1 -1
  55. package/dist/ui/assets/{provider-CMmXdeLg.js → provider-Bh_LaCHL.js} +1 -1
  56. package/dist/ui/assets/{provider-BZXGxqbT.js → provider-Ty6nsV_a.js} +2 -2
  57. package/dist/ui/assets/provider-store-access-Bjl5NS5O.js +1 -0
  58. package/dist/ui/assets/provider-store-access-C5WIH2RS.js +1 -0
  59. package/dist/ui/assets/{providers-B9f6qRKl.js → providers-BEiCiyOf.js} +1 -1
  60. package/dist/ui/assets/{quick-llm-BtUr0Fuw.js → quick-llm-BM6D3Nys.js} +1 -1
  61. package/dist/ui/assets/session-freezer-B2DfwVUW.js +1 -0
  62. package/dist/ui/assets/setup-sudo-nQ-GU7N7.js +1 -0
  63. package/dist/ui/assets/{speak-CSf0rHKy.js → speak-Dp15RzjV.js} +1 -1
  64. package/dist/ui/assets/{sprinkle-manager-CStXC4E3.js → sprinkle-manager-DJWJoTL6.js} +1 -1
  65. package/dist/ui/assets/{store-CcQt0JbT.js → store-ZS8VuVmV.js} +1 -1
  66. package/dist/ui/assets/{sudo-DFtdMkBt.js → sudo-BGNr-3wB.js} +1 -1
  67. package/dist/ui/assets/{transformers-env-CL1k8Gl_.js → transformers-env-xv5_l1lC.js} +1 -1
  68. package/dist/ui/assets/{tray-leave-runtime-qp-hFu3c.js → tray-leave-runtime-BgzxCClu.js} +1 -1
  69. package/dist/ui/assets/{upgrade-detection-Dtv8Fgtu.js → upgrade-detection-CfbX1zC8.js} +1 -1
  70. package/dist/ui/assets/{wc-attach-D35Mcmik.js → wc-attach-Vl5Qfynh.js} +2 -2
  71. package/dist/ui/assets/{wc-detached-ecJY40A0.js → wc-detached-84I42poA.js} +1 -1
  72. package/dist/ui/assets/{wc-extension-B91PNRXx.js → wc-extension-09HRswTF.js} +2 -2
  73. package/dist/ui/assets/{wc-live-C_FXEKPs.js → wc-live-CW8ZLVuJ.js} +5 -5
  74. package/dist/ui/assets/{wc-nav-CQvxC63l.js → wc-nav-C_SpKWs4.js} +2 -2
  75. package/dist/ui/assets/{wc-onboarding-DmXUKxBT.js → wc-onboarding-Co9pE9QF.js} +2 -2
  76. package/dist/ui/assets/{wc-placeholder-B_loirHI.js → wc-placeholder-BB9EjkgM.js} +3 -3
  77. package/dist/ui/assets/{wc-settings-D_hfd5HZ.js → wc-settings-DJrLDnF7.js} +2 -2
  78. package/dist/ui/assets/{wc-shell-Hz6gftb-.js → wc-shell-B7vycEEc.js} +5 -5
  79. package/dist/ui/assets/{wc-sprinkles-CwjG2x5f.js → wc-sprinkles-CiwNv2V_.js} +2 -2
  80. package/dist/ui/assets/{wc-tray-C8EBociY.js → wc-tray-DANo0oag.js} +3 -3
  81. package/dist/ui/assets/{xai-grok-Bcs5PCv3.js → xai-grok-CS2SFUMH.js} +1 -1
  82. package/dist/ui/assets/{xai-grok-DZtMI_Qj.js → xai-grok-Dj7RmfHt.js} +1 -1
  83. package/dist/ui/index.html +2 -2
  84. package/dist/ui/packages/webapp/index.html +2 -2
  85. package/package.json +1 -1
  86. package/dist/ui/assets/dip-D8nk4YNm.js +0 -1
  87. package/dist/ui/assets/provider-store-access-BtyEXL2H.js +0 -1
  88. package/dist/ui/assets/provider-store-access-CW7DcuPJ.js +0 -1
  89. package/dist/ui/assets/session-freezer-DroVbUe6.js +0 -1
  90. package/dist/ui/assets/setup-sudo-DFOwnLKI.js +0 -1
@@ -74,4 +74,25 @@ export declare function bundleIndex(cfg: ConeConfig): ConeConfigIndex;
74
74
  /** Portable base64 of a UTF-8 string (worker/browser/node all have btoa+TextEncoder). */
75
75
  export declare function encodeBundleEnv(json: string): string;
76
76
  export declare function decodeBundleEnv(b64: string): string;
77
+ /**
78
+ * Best-effort expiry (epoch ms) of an Adobe IMS access token, for stamping
79
+ * onto a synthesized Adobe `OAuthAccount.tokenExpiresAt`.
80
+ *
81
+ * Why it matters: a window-less kernel-worker cone must not treat a still-valid
82
+ * token as expired. The webapp's `getValidAccessToken` (providers/adobe.ts)
83
+ * returns the token only while `tokenExpiresAt` is in the future; otherwise it
84
+ * attempts a silent renewal that ALWAYS returns null in a worker (no `window`)
85
+ * and then throws "Adobe session expired". Without an expiry the account
86
+ * defaults to `tokenExpiresAt ?? 0`, so the very first turn fails. Every site
87
+ * that synthesizes an Adobe oauth account from a bare IMS bearer — node-server's
88
+ * legacy-token branch and the worker's back-compat + resume paths — must stamp
89
+ * this or reintroduce that failure mode.
90
+ *
91
+ * IMS access tokens are JWTs whose payload carries `created_at` + `expires_in`
92
+ * (both epoch ms). Returns `created_at + expires_in`, or `undefined` for opaque
93
+ * / unparseable tokens (callers then leave `tokenExpiresAt` unset — prior
94
+ * behavior). Side-effect- and dependency-free (`atob`, not `node:Buffer`) so it
95
+ * is safe to call from the CF Worker and the browser as well as node-server.
96
+ */
97
+ export declare function imsTokenExpiry(token: string): number | undefined;
77
98
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
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;AAyGD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAWvE;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;AAWD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAiBhE"}
@@ -83,6 +83,48 @@ function validateSecret(s) {
83
83
  }
84
84
  return { name: sec.name, value: sec.value, domains: sec.domains };
85
85
  }
86
+ /** Validate the `upsert` arm of a resume delta (accounts/secrets reuse the full-bundle validators). */
87
+ function validateDeltaUpsert(input) {
88
+ if (!input || typeof input !== 'object') {
89
+ throw new Error('cone-config: delta.upsert must be an object');
90
+ }
91
+ const up = input;
92
+ const upsert = {};
93
+ if (up.accounts !== undefined) {
94
+ if (!Array.isArray(up.accounts)) {
95
+ throw new Error('cone-config: delta.upsert.accounts must be an array');
96
+ }
97
+ upsert.accounts = up.accounts.map((a) => validateAccount(a));
98
+ }
99
+ if (up.secrets !== undefined) {
100
+ if (!Array.isArray(up.secrets)) {
101
+ throw new Error('cone-config: delta.upsert.secrets must be an array');
102
+ }
103
+ upsert.secrets = up.secrets.map((s) => validateSecret(s));
104
+ }
105
+ return upsert;
106
+ }
107
+ /** Validate the `delete` arm of a resume delta (provider-id / secret-name string lists). */
108
+ function validateDeltaDelete(input) {
109
+ if (!input || typeof input !== 'object') {
110
+ throw new Error('cone-config: delta.delete must be an object');
111
+ }
112
+ const del = input;
113
+ const deletion = {};
114
+ if (del.providerIds !== undefined) {
115
+ if (!Array.isArray(del.providerIds) || !del.providerIds.every(isStr)) {
116
+ throw new Error('cone-config: delta.delete.providerIds must be string[]');
117
+ }
118
+ deletion.providerIds = del.providerIds;
119
+ }
120
+ if (del.secretNames !== undefined) {
121
+ if (!Array.isArray(del.secretNames) || !del.secretNames.every(isStr)) {
122
+ throw new Error('cone-config: delta.delete.secretNames must be string[]');
123
+ }
124
+ deletion.secretNames = del.secretNames;
125
+ }
126
+ return deletion;
127
+ }
86
128
  /**
87
129
  * Validate an untrusted resume delta (the worker receives it as `unknown`).
88
130
  * Nested accounts/secrets go through the same validators as a full bundle, so a
@@ -99,46 +141,10 @@ export function validateConeConfigDelta(input) {
99
141
  throw new Error('cone-config: delta.model must be a string');
100
142
  out.model = d.model;
101
143
  }
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
- }
144
+ if (d.upsert !== undefined)
145
+ out.upsert = validateDeltaUpsert(d.upsert);
146
+ if (d.delete !== undefined)
147
+ out.delete = validateDeltaDelete(d.delete);
142
148
  return out;
143
149
  }
144
150
  export function mergeConeConfig(base, delta) {
@@ -207,3 +213,48 @@ export function decodeBundleEnv(b64) {
207
213
  const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
208
214
  return new TextDecoder().decode(bytes);
209
215
  }
216
+ /** Decode a base64url segment (JWT alphabet) to a UTF-8 string. */
217
+ function decodeBase64Url(seg) {
218
+ // base64url → base64; atob (forgiving-base64) tolerates the missing padding.
219
+ const b64 = seg.replace(/-/g, '+').replace(/_/g, '/');
220
+ const bin = atob(b64);
221
+ const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
222
+ return new TextDecoder().decode(bytes);
223
+ }
224
+ /**
225
+ * Best-effort expiry (epoch ms) of an Adobe IMS access token, for stamping
226
+ * onto a synthesized Adobe `OAuthAccount.tokenExpiresAt`.
227
+ *
228
+ * Why it matters: a window-less kernel-worker cone must not treat a still-valid
229
+ * token as expired. The webapp's `getValidAccessToken` (providers/adobe.ts)
230
+ * returns the token only while `tokenExpiresAt` is in the future; otherwise it
231
+ * attempts a silent renewal that ALWAYS returns null in a worker (no `window`)
232
+ * and then throws "Adobe session expired". Without an expiry the account
233
+ * defaults to `tokenExpiresAt ?? 0`, so the very first turn fails. Every site
234
+ * that synthesizes an Adobe oauth account from a bare IMS bearer — node-server's
235
+ * legacy-token branch and the worker's back-compat + resume paths — must stamp
236
+ * this or reintroduce that failure mode.
237
+ *
238
+ * IMS access tokens are JWTs whose payload carries `created_at` + `expires_in`
239
+ * (both epoch ms). Returns `created_at + expires_in`, or `undefined` for opaque
240
+ * / unparseable tokens (callers then leave `tokenExpiresAt` unset — prior
241
+ * behavior). Side-effect- and dependency-free (`atob`, not `node:Buffer`) so it
242
+ * is safe to call from the CF Worker and the browser as well as node-server.
243
+ */
244
+ export function imsTokenExpiry(token) {
245
+ const parts = token.split('.');
246
+ if (parts.length !== 3)
247
+ return undefined;
248
+ try {
249
+ const payload = JSON.parse(decodeBase64Url(parts[1]));
250
+ const created = Number(payload.created_at);
251
+ const ttl = Number(payload.expires_in);
252
+ if (Number.isFinite(created) && created > 0 && Number.isFinite(ttl) && ttl > 0) {
253
+ return created + ttl;
254
+ }
255
+ }
256
+ catch {
257
+ // opaque / malformed token — leave expiry unset
258
+ }
259
+ return undefined;
260
+ }
@@ -15,6 +15,6 @@ export type { Registry } from './registry.js';
15
15
  export { filterSecretsEnv } from './secrets-filter.js';
16
16
  export * from './substrate.js';
17
17
  export * from './substrate-factory.js';
18
- export { createE2bSubstrate } from './substrates/e2b.js';
18
+ export { createE2bSubstrate, isSliccTemplate } from './substrates/e2b.js';
19
19
  export * from './types.js';
20
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
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
+ {"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,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1E,cAAc,YAAY,CAAC"}
@@ -16,5 +16,5 @@ export * from './polling.js';
16
16
  export { filterSecretsEnv } from './secrets-filter.js';
17
17
  export * from './substrate.js';
18
18
  export * from './substrate-factory.js';
19
- export { createE2bSubstrate } from './substrates/e2b.js';
19
+ export { createE2bSubstrate, isSliccTemplate } from './substrates/e2b.js';
20
20
  export * from './types.js';
@@ -1,3 +1,12 @@
1
1
  import type { SandboxSubstrate, SubstrateConfig } from '../substrate.js';
2
+ /**
3
+ * Does an e2b template alias belong to SLICC? `Sandbox.list` returns every
4
+ * sandbox on the team, so we filter by template alias (`info.name`) to ignore
5
+ * unrelated sandboxes. We match the `slicc` PREFIX, not exact `slicc`, so cones
6
+ * booted from an isolated test alias (e.g. `slicc-test`, built via
7
+ * SLICC_E2B_TEMPLATE_NAME) still appear in `--cloud list` instead of showing as
8
+ * `dead`. The undefined guard keeps a nameless sandbox from throwing.
9
+ */
10
+ export declare function isSliccTemplate(name: string | undefined): boolean;
2
11
  export declare function createE2bSubstrate(cfg: SubstrateConfig): SandboxSubstrate;
3
12
  //# sourceMappingURL=e2b.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"e2b.d.ts","sourceRoot":"","sources":["../../../src/substrates/e2b.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAKV,gBAAgB,EAChB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAKzB,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,GAAG,gBAAgB,CA6DzE"}
1
+ {"version":3,"file":"e2b.d.ts","sourceRoot":"","sources":["../../../src/substrates/e2b.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAKV,gBAAgB,EAChB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAKzB;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEjE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,GAAG,gBAAgB,CA8DzE"}
@@ -1,5 +1,16 @@
1
1
  import { Sandbox } from 'e2b';
2
2
  const DEFAULT_TTL_MS = 60 * 60 * 1000; // 1 hour
3
+ /**
4
+ * Does an e2b template alias belong to SLICC? `Sandbox.list` returns every
5
+ * sandbox on the team, so we filter by template alias (`info.name`) to ignore
6
+ * unrelated sandboxes. We match the `slicc` PREFIX, not exact `slicc`, so cones
7
+ * booted from an isolated test alias (e.g. `slicc-test`, built via
8
+ * SLICC_E2B_TEMPLATE_NAME) still appear in `--cloud list` instead of showing as
9
+ * `dead`. The undefined guard keeps a nameless sandbox from throwing.
10
+ */
11
+ export function isSliccTemplate(name) {
12
+ return name?.startsWith('slicc') ?? false;
13
+ }
3
14
  export function createE2bSubstrate(cfg) {
4
15
  // Capture apiKey locally to pass explicitly to every SDK call (worker-safe).
5
16
  const apiKey = cfg.apiKey;
@@ -40,8 +51,9 @@ export function createE2bSubstrate(cfg) {
40
51
  const page = await paginator.nextItems();
41
52
  for (const info of page) {
42
53
  // The SDK's `templateId` is the immutable hash (e.g. cjd0k6foq…);
43
- // the alias 'slicc' is on `info.name`. Filter on the alias.
44
- if (info.name === 'slicc') {
54
+ // the alias (`slicc`, `slicc-test`, …) is on `info.name`. Filter on
55
+ // the alias prefix so isolated test-template cones list too.
56
+ if (isSliccTemplate(info.name)) {
45
57
  items.push({
46
58
  sandboxId: info.sandboxId,
47
59
  // Sandbox name (user-supplied `--name`) lives in metadata.
@@ -1,5 +1,18 @@
1
1
  import { describe, expect, it } from 'vitest';
2
- import { bundleIndex, bundleToFiles, decodeBundleEnv, encodeBundleEnv, MAX_CONE_CONFIG_BYTES, mergeConeConfig, serializeSecretsEnv, validateConeConfig, validateConeConfigDelta, } from '../../src/cone-config/index.js';
2
+ import { bundleIndex, bundleToFiles, decodeBundleEnv, encodeBundleEnv, imsTokenExpiry, MAX_CONE_CONFIG_BYTES, mergeConeConfig, serializeSecretsEnv, validateConeConfig, validateConeConfigDelta, } from '../../src/cone-config/index.js';
3
+ /**
4
+ * Build a fake Adobe IMS access token (JWT) with the given timing claims,
5
+ * using the portable base64url encoding that `imsTokenExpiry` decodes via
6
+ * `atob` (no node:Buffer — this helper runs in the CF Worker too).
7
+ */
8
+ function fakeImsJwt(createdAt, expiresIn) {
9
+ const b64url = (o) => btoa(JSON.stringify(o)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
10
+ return [
11
+ b64url({ alg: 'RS256', typ: 'JWT' }),
12
+ b64url({ created_at: String(createdAt), expires_in: String(expiresIn), type: 'access_token' }),
13
+ 'sig',
14
+ ].join('.');
15
+ }
3
16
  const base = {
4
17
  model: 'anthropic:claude-opus-4-6',
5
18
  accounts: [
@@ -117,3 +130,23 @@ describe('base64 env round-trip', () => {
117
130
  expect(MAX_CONE_CONFIG_BYTES).toBeGreaterThan(0);
118
131
  });
119
132
  });
133
+ describe('imsTokenExpiry', () => {
134
+ it('returns created_at + expires_in for a JWT IMS token', () => {
135
+ expect(imsTokenExpiry(fakeImsJwt(1_780_000_000_000, 86_400_000))).toBe(1_780_000_000_000 + 86_400_000);
136
+ });
137
+ it('returns undefined for an opaque (non-JWT) token', () => {
138
+ expect(imsTokenExpiry('opaque-token')).toBeUndefined();
139
+ });
140
+ it('returns undefined when the JWT payload lacks timing claims', () => {
141
+ const b64url = (o) => btoa(JSON.stringify(o)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
142
+ const token = [b64url({ alg: 'RS256' }), b64url({ sub: 'x' }), 'sig'].join('.');
143
+ expect(imsTokenExpiry(token)).toBeUndefined();
144
+ });
145
+ it('returns undefined when timing claims are non-positive', () => {
146
+ expect(imsTokenExpiry(fakeImsJwt(0, 86_400_000))).toBeUndefined();
147
+ expect(imsTokenExpiry(fakeImsJwt(1_780_000_000_000, 0))).toBeUndefined();
148
+ });
149
+ it('returns undefined for a malformed base64 payload', () => {
150
+ expect(imsTokenExpiry('a.!!!notbase64!!!.c')).toBeUndefined();
151
+ });
152
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=e2b-substrate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2b-substrate.test.d.ts","sourceRoot":"","sources":["../../tests/e2b-substrate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { isSliccTemplate } from '../src/substrates/e2b.js';
3
+ describe('isSliccTemplate (e2b list alias filter)', () => {
4
+ it('matches the production slicc alias', () => {
5
+ expect(isSliccTemplate('slicc')).toBe(true);
6
+ });
7
+ it('matches isolated test aliases (slicc-test) so they list instead of showing dead', () => {
8
+ expect(isSliccTemplate('slicc-test')).toBe(true);
9
+ expect(isSliccTemplate('slicc-pr-1108')).toBe(true);
10
+ });
11
+ it('rejects unrelated team templates', () => {
12
+ expect(isSliccTemplate('base')).toBe(false);
13
+ expect(isSliccTemplate('not-slicc')).toBe(false);
14
+ });
15
+ it('rejects a nameless sandbox without throwing', () => {
16
+ expect(isSliccTemplate(undefined)).toBe(false);
17
+ });
18
+ });
@@ -5,6 +5,7 @@ export type ParsedCloudArgs = {
5
5
  substrate: SubstrateId;
6
6
  name?: string;
7
7
  envFile?: string;
8
+ template?: string;
8
9
  };
9
10
  } | {
10
11
  subcommand: 'list';
@@ -1,19 +1,16 @@
1
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
- };
2
+ /** Subcommands that accept a positional query (sandbox ID or name). */
3
+ function takesQuery(sub) {
4
+ return sub === 'pause' || sub === 'resume' || sub === 'kill';
5
+ }
6
+ /**
7
+ * Parse the flag list that follows `--cloud <subcommand>` into a flat
8
+ * `BaseArgs` bag. The discriminated union is assembled later in
9
+ * `buildParsedArgs`; keeping the two phases separate holds each function's
10
+ * cognitive complexity under the biome cap.
11
+ */
12
+ function parseBaseArgs(rest, sub) {
13
+ const baseArgs = { substrate: 'e2b' };
17
14
  let i = 0;
18
15
  while (i < rest.length) {
19
16
  const a = rest[i];
@@ -23,15 +20,19 @@ export function parseCloudArgs(argv) {
23
20
  else if (a === '--env-file') {
24
21
  baseArgs.envFile = rest[++i];
25
22
  }
23
+ else if (a === '--template') {
24
+ // Substrate template alias to launch from (default 'slicc'). Lets
25
+ // `--cloud start` boot an isolated test template (e.g. 'slicc-test')
26
+ // built via SLICC_E2B_TEMPLATE_NAME without touching production.
27
+ baseArgs.template = rest[++i];
28
+ }
26
29
  else if (a === '--substrate') {
27
30
  const v = rest[++i];
28
31
  if (v !== 'e2b')
29
32
  throw new Error(`unsupported substrate: ${v} (MVP only supports 'e2b')`);
30
33
  baseArgs.substrate = v;
31
34
  }
32
- else if (!a.startsWith('--') &&
33
- !baseArgs.query &&
34
- (sub === 'pause' || sub === 'resume' || sub === 'kill')) {
35
+ else if (!a.startsWith('--') && !baseArgs.query && takesQuery(sub)) {
35
36
  baseArgs.query = a;
36
37
  }
37
38
  else {
@@ -39,7 +40,17 @@ export function parseCloudArgs(argv) {
39
40
  }
40
41
  i++;
41
42
  }
42
- // Build discriminated union based on subcommand
43
+ return baseArgs;
44
+ }
45
+ /** Require and return the positional query for the query-taking subcommands. */
46
+ function requireQuery(sub, query) {
47
+ if (!query) {
48
+ throw new Error(`${sub} requires a query argument (sandbox ID or name)`);
49
+ }
50
+ return query;
51
+ }
52
+ /** Assemble the discriminated union from the parsed flag bag. */
53
+ function buildParsedArgs(sub, baseArgs) {
43
54
  switch (sub) {
44
55
  case 'start':
45
56
  return {
@@ -48,32 +59,39 @@ export function parseCloudArgs(argv) {
48
59
  substrate: baseArgs.substrate,
49
60
  name: baseArgs.name,
50
61
  envFile: baseArgs.envFile,
62
+ template: baseArgs.template,
51
63
  },
52
64
  };
53
65
  case 'list':
54
66
  return { subcommand: 'list', args: { substrate: baseArgs.substrate } };
55
67
  case 'pause':
56
68
  case 'kill':
57
- if (!baseArgs.query) {
58
- throw new Error(`${sub} requires a query argument (sandbox ID or name)`);
59
- }
60
69
  return {
61
70
  subcommand: sub,
62
- args: { substrate: baseArgs.substrate, query: baseArgs.query },
71
+ args: { substrate: baseArgs.substrate, query: requireQuery(sub, baseArgs.query) },
63
72
  };
64
73
  case 'resume':
65
- if (!baseArgs.query) {
66
- throw new Error(`${sub} requires a query argument (sandbox ID or name)`);
67
- }
68
74
  return {
69
75
  subcommand: 'resume',
70
76
  args: {
71
77
  substrate: baseArgs.substrate,
72
- query: baseArgs.query,
78
+ query: requireQuery(sub, baseArgs.query),
73
79
  envFile: baseArgs.envFile,
74
80
  },
75
81
  };
76
- default:
77
- throw new Error(`unknown subcommand: ${sub}`);
78
82
  }
79
83
  }
84
+ export function parseCloudArgs(argv) {
85
+ if (argv.includes('--hosted') && argv.includes('--cloud')) {
86
+ throw new Error('--cloud and --hosted are mutually exclusive');
87
+ }
88
+ const cloudIdx = argv.indexOf('--cloud');
89
+ if (cloudIdx === -1)
90
+ return null;
91
+ const sub = argv[cloudIdx + 1];
92
+ if (!sub || !VALID_SUBCOMMANDS.includes(sub)) {
93
+ throw new Error(`unknown subcommand: ${sub ?? '(none)'} (expected one of: ${VALID_SUBCOMMANDS.join(', ')})`);
94
+ }
95
+ const baseArgs = parseBaseArgs(argv.slice(cloudIdx + 2), sub);
96
+ return buildParsedArgs(sub, baseArgs);
97
+ }
@@ -1,5 +1,19 @@
1
1
  import type { SandboxSubstrate } from '../_cloud_core/src/index.js';
2
2
  import { type SandboxHandle, type StartResult } from '../_cloud_core/src/index.js';
3
+ /**
4
+ * CLI poll budget for `/tmp/slicc-join.json` to appear after sandbox create.
5
+ *
6
+ * cloud-core defaults this to 60s, which is too tight for a COLD boot of the
7
+ * hosted-leader image: start.sh budgets chromium cold-start alone at 60s
8
+ * (SLICC_CDP_LAUNCH_TIMEOUT_MS), and node boot + tray registration push the
9
+ * observed first-boot time to ~50-70s. The 60s default loses that race on the
10
+ * coldest (first-after-build) boot, surfacing as a spurious SANDBOX_NOT_READY.
11
+ *
12
+ * The worker path deliberately keeps the tighter 60s default (its request is
13
+ * wall-clock-constrained by the dashboard fetch / CF edge), but a laptop CLI
14
+ * can afford to wait — so we give it generous headroom here.
15
+ */
16
+ export declare const CLI_START_POLL_TIMEOUT_MS = 180000;
3
17
  export interface RunStartOpts {
4
18
  substrate: SandboxSubstrate;
5
19
  envFilePath: string;
@@ -1,6 +1,20 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import { startCone } from '../_cloud_core/src/index.js';
3
3
  import { FileRegistry } from './registry-file.js';
4
+ /**
5
+ * CLI poll budget for `/tmp/slicc-join.json` to appear after sandbox create.
6
+ *
7
+ * cloud-core defaults this to 60s, which is too tight for a COLD boot of the
8
+ * hosted-leader image: start.sh budgets chromium cold-start alone at 60s
9
+ * (SLICC_CDP_LAUNCH_TIMEOUT_MS), and node boot + tray registration push the
10
+ * observed first-boot time to ~50-70s. The 60s default loses that race on the
11
+ * coldest (first-after-build) boot, surfacing as a spurious SANDBOX_NOT_READY.
12
+ *
13
+ * The worker path deliberately keeps the tighter 60s default (its request is
14
+ * wall-clock-constrained by the dashboard fetch / CF edge), but a laptop CLI
15
+ * can afford to wait — so we give it generous headroom here.
16
+ */
17
+ export const CLI_START_POLL_TIMEOUT_MS = 180_000;
4
18
  /**
5
19
  * Extract ADOBE_IMS_TOKEN (+ DOMAINS) from an env-file body so they can be
6
20
  * injected as sandbox env vars at Sandbox.create. start.sh writes them to
@@ -40,7 +54,7 @@ export async function runStart(opts) {
40
54
  template: opts.template,
41
55
  name: opts.name,
42
56
  sliccVersion: opts.sliccVersion,
43
- pollTimeoutMs: opts.pollTimeoutMs,
57
+ pollTimeoutMs: opts.pollTimeoutMs ?? CLI_START_POLL_TIMEOUT_MS,
44
58
  pollIntervalMs: opts.pollIntervalMs,
45
59
  metadata: {
46
60
  createdBy: process.env['USER'] ?? 'unknown',
@@ -1,6 +1,7 @@
1
- import type { Account } from './_cloud_core/src/index.js';
1
+ import { type Account, imsTokenExpiry } from './_cloud_core/src/index.js';
2
2
  import type { Express } from 'express';
3
3
  import type { SecretStore } from './secrets/types.js';
4
+ export { imsTokenExpiry };
4
5
  export interface HostedBootstrapPayload {
5
6
  model?: string;
6
7
  accounts?: Account[];
@@ -14,7 +14,15 @@
14
14
  * orchestrator talks to the leader through the tray-worker tunnel, not http.
15
15
  */
16
16
  import { readFileSync } from 'node:fs';
17
+ // Import from the package root (not the './cone-config' subpath): node-server's
18
+ // inline-workspaces packaging only rewrites the bare '@slicc/cloud-core'
19
+ // specifier, so a subpath import leaks an un-inlined workspace reference.
20
+ import { imsTokenExpiry } from './_cloud_core/src/index.js';
17
21
  import { requireLoopback } from './cloud-status.js';
22
+ // Re-export so existing callers/tests can keep importing it from here. The
23
+ // implementation now lives in @slicc/cloud-core/cone-config (side-effect-free,
24
+ // Buffer-free) so the worker's back-compat + resume paths reuse the same logic.
25
+ export { imsTokenExpiry };
18
26
  const CONE_CONFIG_PATH = '/slicc/cone-config.json';
19
27
  const DEFAULT_MODEL = 'adobe:claude-opus-4-6';
20
28
  export function buildHostedBootstrapPayload(sources) {
@@ -25,9 +33,17 @@ export function buildHostedBootstrapPayload(sources) {
25
33
  }
26
34
  const legacy = sources.getLegacyAdobeToken();
27
35
  if (legacy) {
36
+ const expiresAt = imsTokenExpiry(legacy);
28
37
  return {
29
38
  model: DEFAULT_MODEL,
30
- accounts: [{ providerId: 'adobe', kind: 'oauth', accessToken: legacy }],
39
+ accounts: [
40
+ {
41
+ providerId: 'adobe',
42
+ kind: 'oauth',
43
+ accessToken: legacy,
44
+ ...(expiresAt !== undefined ? { tokenExpiresAt: expiresAt } : {}),
45
+ },
46
+ ],
31
47
  adobeImsToken: legacy,
32
48
  };
33
49
  }
@@ -1360,6 +1360,7 @@ async function runCloudSubcommand(parsed) {
1360
1360
  workerBaseUrl: process.env['SLICC_TRAY_WORKER_BASE_URL']?.trim() || 'https://www.sliccy.ai',
1361
1361
  sliccVersion: localSliccVersion,
1362
1362
  name: parsed.args.name,
1363
+ template: parsed.args.template,
1363
1364
  });
1364
1365
  console.log(`Sandbox ${result.sandboxId} ready.`);
1365
1366
  console.log(`Open: ${result.joinUrl}`);