sliccy 6.256.7 → 6.256.9
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/cone-config-rules.d.mts +89 -0
- package/dist/node-server/_cloud_core/src/cone-config/cone-config-rules.d.mts.map +1 -0
- package/dist/node-server/_cloud_core/src/cone-config/cone-config-rules.mjs +170 -0
- package/dist/node-server/_cloud_core/src/cone-config/index.d.ts +2 -3
- package/dist/node-server/_cloud_core/src/cone-config/index.d.ts.map +1 -1
- package/dist/node-server/_cloud_core/src/cone-config/index.js +13 -79
- package/dist/node-server/_cloud_core/tests/cone-config/bundle-cases.test.d.ts +2 -0
- package/dist/node-server/_cloud_core/tests/cone-config/bundle-cases.test.d.ts.map +1 -0
- package/dist/node-server/_cloud_core/tests/cone-config/bundle-cases.test.js +57 -0
- package/dist/node-server/_cloud_core/tests/cone-config/cone-config.test.js +7 -5
- package/dist/node-server/_cloud_core/tests/fixtures/cone-config-cases.d.mts +37 -0
- package/dist/node-server/_cloud_core/tests/fixtures/cone-config-cases.d.mts.map +1 -0
- package/dist/node-server/_cloud_core/tests/fixtures/cone-config-cases.mjs +139 -0
- package/dist/node-server/_cloud_core/tests/resume-cone-config.test.js +17 -0
- package/dist/ui/.vite/manifest.json +184 -184
- package/dist/ui/assets/{account-store-btH-rhSC.js → account-store-C53Um_mm.js} +1 -1
- package/dist/ui/assets/{account-store-CzcIBhIW.js → account-store-D6K6yEpr.js} +1 -1
- package/dist/ui/assets/{adobe-CbwrOy6h.js → adobe-BA_LWoXE.js} +1 -1
- package/dist/ui/assets/{adobe-CJagkQaj.js → adobe-BoUssaQx.js} +1 -1
- package/dist/ui/assets/{agent-panels-CWkdH3No.js → agent-panels-BN_g6RId.js} +1 -1
- package/dist/ui/assets/{almost-bash-shell-headless-CJ_eP6rj.js → almost-bash-shell-headless-BKZkvJ6e.js} +15 -15
- package/dist/ui/assets/{apply-layout-BIG5g9tp.js → apply-layout-B1yCBAUQ.js} +1 -1
- package/dist/ui/assets/{azure-openai-Cw7yGm0E.js → azure-openai-0Br25NSz.js} +1 -1
- package/dist/ui/assets/{azure-openai-DmBsM65e.js → azure-openai-CgjU22QR.js} +1 -1
- package/dist/ui/assets/{cdp-fHtNpyG6.js → cdp-CHnFD10W.js} +1 -1
- package/dist/ui/assets/{cerebras-BF4ZkjX5.js → cerebras-BekGtwM3.js} +1 -1
- package/dist/ui/assets/{cerebras-BRvzPIo6.js → cerebras-Dp-xxYie.js} +1 -1
- package/dist/ui/assets/{composer-speech-CYKpfumH.js → composer-speech-Cdhkyi5i.js} +1 -1
- package/dist/ui/assets/{connect-surface-eAvkKA7C.js → connect-surface-SdQMNXt8.js} +1 -1
- package/dist/ui/assets/{directed-approval-FuN8_c_j.js → directed-approval-WBVpFoZZ.js} +1 -1
- package/dist/ui/assets/{emscripten-vfs-hook-Cc4WeSPd.js → emscripten-vfs-hook-DZwJD4y2.js} +1 -1
- package/dist/ui/assets/frames-bfeIWWdf.js +1 -0
- package/dist/ui/assets/{frozen-archive-writer-CmH9_k8F.js → frozen-archive-writer-BQ8dZRjd.js} +1 -1
- package/dist/ui/assets/{frozen-session-delete-fM62FhkX.js → frozen-session-delete-ep62Dh64.js} +1 -1
- package/dist/ui/assets/{frozen-session-identity-blXQV4n9.js → frozen-session-identity-BawyNd51.js} +1 -1
- package/dist/ui/assets/{gelatiere-unit-Bo7PCnbF.js → gelatiere-unit-BdvxF-bo.js} +1 -1
- package/dist/ui/assets/{github-C1rbK9rb.js → github-685Sxap0.js} +1 -1
- package/dist/ui/assets/{github-CQol1Ste.js → github-B3ki0kW7.js} +1 -1
- package/dist/ui/assets/{github-copilot-Dh5Sw_oV.js → github-copilot-Bn1Tofcl.js} +1 -1
- package/dist/ui/assets/{github-copilot-Da1-tlWc.js → github-copilot-lc6Ufqa3.js} +1 -1
- package/dist/ui/assets/{hear-DhXegde8.js → hear-0djFXv_9.js} +1 -1
- package/dist/ui/assets/{js-realm-worker-BSYR2sfX.js → js-realm-worker--QdwOZAk.js} +2 -2
- package/dist/ui/assets/{jsh-executor-v3qKqkZF.js → jsh-executor-BxZA2hXH.js} +3 -3
- package/dist/ui/assets/{kernel-worker-BItmIWYW.js → kernel-worker-CLURD9IR.js} +8 -8
- package/dist/ui/assets/{lick-ws-bridge-gBNtu0RL.js → lick-ws-bridge-Ck6kaT2S.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-BOvw32aW.js → lick-ws-bridge-CoG2qesw.js} +1 -1
- package/dist/ui/assets/{live-session-curation-8wVj6wZH.js → live-session-curation-DC5ilmnE.js} +1 -1
- package/dist/ui/assets/{live-session-curation-7CTGumla.js → live-session-curation-DRsIIv8O.js} +1 -1
- package/dist/ui/assets/{live-session-snapshot-D8qPxZ7E.js → live-session-snapshot-CkkdDwxI.js} +1 -1
- package/dist/ui/assets/{local-llm-CgwaZ6Gd.js → local-llm-DGOkH498.js} +1 -1
- package/dist/ui/assets/{local-llm-DvbP3RyJ.js → local-llm-DkHtUWu5.js} +1 -1
- package/dist/ui/assets/{main-IZrprgCE.js → main-D9oUgloo.js} +3 -3
- package/dist/ui/assets/{main-cherry-CiVZP2ux.js → main-cherry-CG3eAQlV.js} +1 -1
- package/dist/ui/assets/{mcp-command-BQnTrbHl.js → mcp-command-CNI5aZm4.js} +10 -10
- package/dist/ui/assets/{memory-curation-seam-CjpYohBK.js → memory-curation-seam-CQRsPoO7.js} +1 -1
- package/dist/ui/assets/{memory-health-DiVvDJo6.js → memory-health-qES7fXVO.js} +1 -1
- package/dist/ui/assets/{memory-write-execute-CPlnwj08.js → memory-write-execute-Dz2AOQlc.js} +1 -1
- package/dist/ui/assets/{merge-folded-cost-into-frozen-CDpUEipJ.js → merge-folded-cost-into-frozen-Cp-3wSiw.js} +1 -1
- package/dist/ui/assets/{new-session-DMBvlX7U.js → new-session-oUWvETeg.js} +2 -2
- package/dist/ui/assets/{oauth-bootstrap-z9Ak2U5k.js → oauth-bootstrap-DFYdogxM.js} +2 -2
- package/dist/ui/assets/{onboarding-orchestrator-Cmp_7XS6.js → onboarding-orchestrator-DwzGpye8.js} +1 -1
- package/dist/ui/assets/{openai-codex-XtzU7AV5.js → openai-codex-Lc8obIKU.js} +1 -1
- package/dist/ui/assets/{openai-codex-DObnOrti.js → openai-codex-a3KU-PYz.js} +1 -1
- package/dist/ui/assets/{openrouter-h4PBvqqd.js → openrouter-CQJlo-7Q.js} +1 -1
- package/dist/ui/assets/{openrouter-B9YPpMez.js → openrouter-DCWj0mxO.js} +1 -1
- package/dist/ui/assets/{openrouter-free-Bw98ZXz_.js → openrouter-free-C6ait2MT.js} +1 -1
- package/dist/ui/assets/{openrouter-free-DsUNrtke.js → openrouter-free-CB_5hU3Q.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-DlBVHDiI.js → openrouter-oauth-B5nARrbg.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-CtAf-wKQ.js → openrouter-oauth-BFc8K4Yo.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-C2H1GujD.js → panel-rpc-handlers-C9VEsFxZ.js} +2 -2
- package/dist/ui/assets/{panelize-shell-DjEoCne9.js → panelize-shell-DDW-oKWB.js} +1 -1
- package/dist/ui/assets/{pi-edit-execution-DdXRrxQk.js → pi-edit-execution-DATJLr54.js} +1 -1
- package/dist/ui/assets/{provider-D88ZP1Lj.js → provider-BFovwIok.js} +1 -1
- package/dist/ui/assets/{provider-CkcZnU60.js → provider-C-y9IsNb.js} +1 -1
- package/dist/ui/assets/{provider-settings-D6OJgUt2.js → provider-settings-BaLOxUmM.js} +1 -1
- package/dist/ui/assets/provider-store-access-BKZWsf7y.js +1 -0
- package/dist/ui/assets/provider-store-access-DeL4f_Qp.js +1 -0
- package/dist/ui/assets/{providers-Bh-gB-84.js → providers-3AZB7Al_.js} +1 -1
- package/dist/ui/assets/{providers-CNaCaxZH.js → providers-DErgbS_D.js} +2 -2
- package/dist/ui/assets/{quick-llm-D3vw1Iw4.js → quick-llm-D9onASDw.js} +1 -1
- package/dist/ui/assets/{realm-inprocess-c5_SKCj3.js → realm-inprocess-NmMZhEli.js} +2 -2
- package/dist/ui/assets/{realm-unhandled-rejections-B-HhibQP.js → realm-unhandled-rejections-BC_zQVgp.js} +1 -1
- package/dist/ui/assets/{realm-unhandled-rejections-V5oYilet.js → realm-unhandled-rejections-Dj1YS7MU.js} +1 -1
- package/dist/ui/assets/{remote-terminal-view-ByfloxIg.js → remote-terminal-view-CIQ1PZ9w.js} +2 -2
- package/dist/ui/assets/{restore-BByLi1Yd.js → restore-UlheFg8K.js} +1 -1
- package/dist/ui/assets/{run-BG76L9eu.js → run-BDovOz9c.js} +1 -1
- package/dist/ui/assets/{run-C-tci7Wb.js → run-BgxWF2oM.js} +1 -1
- package/dist/ui/assets/{run-B9uXGQYv.js → run-Bi_CtYdz.js} +2 -2
- package/dist/ui/assets/{run-BZCg7fqf.js → run-Cph20KL5.js} +2 -2
- package/dist/ui/assets/{run-CJQ6QgvR.js → run-Q1zeRJAj.js} +2 -2
- package/dist/ui/assets/{scale-B8PGb3fG.js → scale-DGaWcXnU.js} +1 -1
- package/dist/ui/assets/{serve-command-fOeuDlHT.js → serve-command-Dh9-nJKr.js} +2 -2
- package/dist/ui/assets/{serve-runtime-BgsoA65p.js → serve-runtime-CWel-hw-.js} +1 -1
- package/dist/ui/assets/{session-command-memory-Z4gG5kc5.js → session-command-memory-GUh-MoKZ.js} +2 -2
- package/dist/ui/assets/{session-jsonl-CRoUGl0k.js → session-jsonl-DnrB6xFa.js} +1 -1
- package/dist/ui/assets/{session-search-index-C9AzKIIE.js → session-search-index-C2YxS1lH.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-HCx9Z23R.js → setup-feature-flags-remote-B4z23nRG.js} +1 -1
- package/dist/ui/assets/{setup-model-catalog-C_9E6bou.js → setup-model-catalog-Do0Mpo0E.js} +1 -1
- package/dist/ui/assets/{silent-renew-backoff-ByLo6zsQ.js → silent-renew-backoff-ki1gZZUf.js} +1 -1
- package/dist/ui/assets/slicc-version-C-ysp_tA.js +1 -0
- package/dist/ui/assets/{soundscape-KuEhg0nr.js → soundscape-AyeLTgUD.js} +1 -1
- package/dist/ui/assets/{speak-Di_o2s0D.js → speak-CCP5aDYi.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-4tRf736T.js → sprinkle-manager-Dsh6A0ZY.js} +1 -1
- package/dist/ui/assets/{sprinkle-renderer-EbUVsHUB.js → sprinkle-renderer-tXii7z20.js} +1 -1
- package/dist/ui/assets/{ssh-COAG0Prl.js → ssh-QXOZ_9C5.js} +1 -1
- package/dist/ui/assets/{sudo-BlHPVMLU.js → sudo-CRyyMDJI.js} +1 -1
- package/dist/ui/assets/{supervisor-BjtHX13A.js → supervisor-BHGgl-nU.js} +1 -1
- package/dist/ui/assets/{sync-dialog-CG5xrbzN.js → sync-dialog-LmjIPOl5.js} +1 -1
- package/dist/ui/assets/{tab-D9i-XOmT.js → tab-BxQIB_Z9.js} +1 -1
- package/dist/ui/assets/{transformers-env-BCIh311V.js → transformers-env-DviFGXIh.js} +1 -1
- package/dist/ui/assets/{tray-leader-DfoPsK4G.js → tray-leader-DRAiFyH0.js} +1 -1
- package/dist/ui/assets/{tray-leave-runtime-Cn6l6kQM.js → tray-leave-runtime-CBTcgox7.js} +1 -1
- package/dist/ui/assets/{tray-sidecar-AttyJaV8.js → tray-sidecar-DR2gfFr1.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-BPnMErp6.js → upgrade-detection-CLcKWSrE.js} +1 -1
- package/dist/ui/assets/{upgrade-to-seven-oM6Y2jpf.js → upgrade-to-seven-DGK7_pzH.js} +1 -1
- package/dist/ui/assets/{v86-BhyJfU3t.js → v86-hpXjGyeJ.js} +1 -1
- package/dist/ui/assets/{voice-reply-DhFXmtN1.js → voice-reply-CbIPZ3RI.js} +1 -1
- package/dist/ui/assets/{wc-attach-B2_dNi4B.js → wc-attach-SKMBIFz8.js} +2 -2
- package/dist/ui/assets/{wc-browser-BvSPcFnk.js → wc-browser-BmbHK6T2.js} +1 -1
- package/dist/ui/assets/{wc-computers-P_YXw5mk.js → wc-computers-CCW36xmA.js} +1 -1
- package/dist/ui/assets/{wc-detached-C4uhpekQ.js → wc-detached-wYR_-B4N.js} +1 -1
- package/dist/ui/assets/{wc-extension-D3l9KUxg.js → wc-extension-CowOxRjX.js} +2 -2
- package/dist/ui/assets/{wc-follower-oauth-PiJAsXE9.js → wc-follower-oauth-B4ghxt1Z.js} +1 -1
- package/dist/ui/assets/{wc-follower-BN7GwkbB.js → wc-follower-trWpq3AT.js} +3 -3
- package/dist/ui/assets/{wc-gelatiere-aEAOP3ay.js → wc-gelatiere-DCWY8DLx.js} +1 -1
- package/dist/ui/assets/{wc-live-BeX-_7T4.js → wc-live-DMxoy1ey.js} +5 -5
- package/dist/ui/assets/{wc-nav-D7tE4cOW.js → wc-nav-B60Q1YWx.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-BsRWvnNO.js → wc-onboarding-DFKkF2SS.js} +2 -2
- package/dist/ui/assets/{wc-permissions-xh98BF4d.js → wc-permissions-0ZaDh6E3.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-C2e75ECK.js → wc-placeholder-C1lk5MWN.js} +2 -2
- package/dist/ui/assets/{wc-profile-export-BOUD9Teb.js → wc-profile-export-C_CPVCiz.js} +3 -3
- package/dist/ui/assets/{wc-secret-request-9hLiDY0q.js → wc-secret-request-BVUEEzXV.js} +1 -1
- package/dist/ui/assets/{wc-settings-C1Ew3xtK.js → wc-settings-BGMa1-7M.js} +3 -3
- package/dist/ui/assets/{wc-shell-BQm4_AZ-.js → wc-shell-C8i5txed.js} +47 -42
- package/dist/ui/assets/{wc-shortcut-config-CG3A_xXd.js → wc-shortcut-config-D_X09nxe.js} +1 -1
- package/dist/ui/assets/{wc-sprinkles-DfhVMlB0.js → wc-sprinkles-DxoN0545.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-D5lf1B5S.js → wc-transcript-export-Cl9ciceV.js} +2 -2
- package/dist/ui/assets/{wc-tray-DrfK5jNi.js → wc-tray-CnnaCOxC.js} +4 -4
- package/dist/ui/assets/welcome-detection-BJdrXx9O.js +1 -0
- package/dist/ui/assets/{whisper-session-B-Uac3-A.js → whisper-session-DkP7Jo2t.js} +2 -2
- package/dist/ui/assets/{xai-grok-CHwFD6MH.js → xai-grok-0KYkTeE-.js} +1 -1
- package/dist/ui/assets/{xai-grok-xZGJWELa.js → xai-grok-SPC-yKdi.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/packages/webapp/index.html +3 -3
- package/dist/ui/packages/webapp/upgrade-to-seven.html +1 -1
- package/dist/ui/upgrade-to-seven.html +1 -1
- package/package.json +2 -2
- package/dist/ui/assets/frames-CU-jXCBt.js +0 -1
- package/dist/ui/assets/provider-store-access-Am4wDSnr.js +0 -1
- package/dist/ui/assets/provider-store-access-bHvRNk17.js +0 -1
- package/dist/ui/assets/slicc-version-BOvQ0meA.js +0 -1
- package/dist/ui/assets/welcome-detection-D98VbCw6.js +0 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export type EffortLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
2
|
+
export type OAuthAccount = {
|
|
3
|
+
providerId: string;
|
|
4
|
+
kind: 'oauth';
|
|
5
|
+
accessToken: string;
|
|
6
|
+
refreshToken?: string;
|
|
7
|
+
tokenExpiresAt?: number;
|
|
8
|
+
userName?: string;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ApiKeyAccount = {
|
|
12
|
+
providerId: string;
|
|
13
|
+
kind: 'apikey';
|
|
14
|
+
apiKey: string;
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
deployment?: string;
|
|
17
|
+
apiVersion?: string;
|
|
18
|
+
};
|
|
19
|
+
export type SecretEntry = {
|
|
20
|
+
name: string;
|
|
21
|
+
value: string;
|
|
22
|
+
domains: string[];
|
|
23
|
+
};
|
|
24
|
+
/** @typedef {'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'} EffortLevel */
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {{
|
|
27
|
+
* providerId: string;
|
|
28
|
+
* kind: 'oauth';
|
|
29
|
+
* accessToken: string;
|
|
30
|
+
* refreshToken?: string;
|
|
31
|
+
* tokenExpiresAt?: number;
|
|
32
|
+
* userName?: string;
|
|
33
|
+
* baseUrl?: string;
|
|
34
|
+
* }} OAuthAccount
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {{
|
|
38
|
+
* providerId: string;
|
|
39
|
+
* kind: 'apikey';
|
|
40
|
+
* apiKey: string;
|
|
41
|
+
* baseUrl?: string;
|
|
42
|
+
* deployment?: string;
|
|
43
|
+
* apiVersion?: string;
|
|
44
|
+
* }} ApiKeyAccount
|
|
45
|
+
*/
|
|
46
|
+
/** @typedef {{ name: string; value: string; domains: string[] }} SecretEntry */
|
|
47
|
+
/** @type {readonly EffortLevel[]} */
|
|
48
|
+
export declare const EFFORT_LEVELS: readonly EffortLevel[];
|
|
49
|
+
/**
|
|
50
|
+
* @param {unknown} value
|
|
51
|
+
* @returns {value is EffortLevel}
|
|
52
|
+
*/
|
|
53
|
+
export declare function isEffortLevel(value: unknown): value is EffortLevel;
|
|
54
|
+
/**
|
|
55
|
+
* Secret names: identifier-shaped, plus `.` and `-` for namespaced subsystem
|
|
56
|
+
* secrets (`s3.<profile>.access_key_id`). The `secrets.env` key is everything
|
|
57
|
+
* before the first `=`, so none of these characters can split a line; only
|
|
58
|
+
* POSIX-identifier names ever reach the agent shell as `$NAME`.
|
|
59
|
+
*/
|
|
60
|
+
export declare const SECRET_NAME_RE: RegExp;
|
|
61
|
+
/**
|
|
62
|
+
* Validate one untrusted provider account. Only the known fields of the
|
|
63
|
+
* chosen kind survive; the credential and `providerId` must be non-empty.
|
|
64
|
+
* @param {unknown} input
|
|
65
|
+
* @param {string} where error-message prefix, e.g. `cone-config: accounts[0]`
|
|
66
|
+
* @returns {OAuthAccount | ApiKeyAccount}
|
|
67
|
+
*/
|
|
68
|
+
export declare function validateAccount(input: unknown, where: string): OAuthAccount | ApiKeyAccount;
|
|
69
|
+
/**
|
|
70
|
+
* Validate one untrusted secret against the line-oriented `secrets.env`
|
|
71
|
+
* schema (no escaping): a {@link SECRET_NAME_RE} name outside the reserved
|
|
72
|
+
* `oauth.*` namespace and not ending in `_DOMAINS`, a single-line value, and
|
|
73
|
+
* at least one domain, each trimmed, non-blank, single-line and comma-free.
|
|
74
|
+
* node-server drops a secret with no domains, so `[]` is rejected here
|
|
75
|
+
* rather than shipped as a secret that silently never loads.
|
|
76
|
+
* @param {unknown} input
|
|
77
|
+
* @param {string} where error-message prefix, e.g. `cone-config: secrets[0]`
|
|
78
|
+
* @returns {SecretEntry}
|
|
79
|
+
*/
|
|
80
|
+
export declare function validateSecret(input: unknown, where: string): SecretEntry;
|
|
81
|
+
/**
|
|
82
|
+
* Serialize validated secrets to the `NAME=value` / `NAME_DOMAINS=a,b` lines
|
|
83
|
+
* node-server's EnvSecretStore reads. Values are written verbatim, so callers
|
|
84
|
+
* pass entries that went through {@link validateSecret}.
|
|
85
|
+
* @param {readonly SecretEntry[]} secrets
|
|
86
|
+
* @returns {string}
|
|
87
|
+
*/
|
|
88
|
+
export declare function serializeSecretsEnv(secrets: readonly SecretEntry[]): string;
|
|
89
|
+
//# sourceMappingURL=cone-config-rules.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cone-config-rules.d.mts","sourceRoot":"","sources":["../../../src/cone-config/cone-config-rules.mjs"],"names":[],"mappings":"AAaI,YAAmE,WAAW,GAApE,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAa;AAG/E,YAQG,YAAY,GARL;IACR,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,CAAc;AAIf,YAOG,aAAa,GAPN;IACR,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,CAAe;AAGf,YAA8D,WAAW,GAA/D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAa;AAzB7E,qFAAqF;AAErF;;;;;;;;;;GAUG;AAEH;;;;;;;;;GASG;AAEH,gFAAgF;AAEhF,qCAAqC;AACrC,eAAO,MAAM,aAAa,EADf,SAAS,WAAW,EACiE,CAAC;AAEjG;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAHxB,OAGwB,GAFtB,KAAK,IAAI,WAAW,CAMhC;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAA+B,CAAC;AAkC3D;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAJ1B,OAI0B,EAAE,KAAK,EAHjC,MAGiC,GAF/B,YAAY,GAAG,aAAa,CAgCxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAJzB,OAIyB,EAAE,KAAK,EAHhC,MAGgC,GAF9B,WAAW,CAyBvB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAHhC,SAAS,WAAW,EAGY,GAF9B,MAAM,CASlB"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
// Zero-dependency rules for the untrusted cone-config bundle: provider
|
|
3
|
+
// accounts, domain-scoped secrets, the effort-level set, and the `secrets.env`
|
|
4
|
+
// serializer. Plain JS with no imports, so both floats that write a cone's
|
|
5
|
+
// credential files run the exact same checks.
|
|
6
|
+
//
|
|
7
|
+
// CANONICAL COPY: packages/cloud-core/src/cone-config/cone-config-rules.mjs
|
|
8
|
+
// (wrapped with types by cone-config/index.ts). packages/github-workflow/
|
|
9
|
+
// scripts/cone-config-rules.mjs is a byte-identical vendored copy, because the
|
|
10
|
+
// workflow scripts run from a sparse checkout of packages/github-workflow and
|
|
11
|
+
// cannot import another package. Edit the canonical file, then copy it over;
|
|
12
|
+
// github-workflow's cone-config-rules.test.mjs fails while the two differ.
|
|
13
|
+
/** @typedef {'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'} EffortLevel */
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {{
|
|
16
|
+
* providerId: string;
|
|
17
|
+
* kind: 'oauth';
|
|
18
|
+
* accessToken: string;
|
|
19
|
+
* refreshToken?: string;
|
|
20
|
+
* tokenExpiresAt?: number;
|
|
21
|
+
* userName?: string;
|
|
22
|
+
* baseUrl?: string;
|
|
23
|
+
* }} OAuthAccount
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {{
|
|
27
|
+
* providerId: string;
|
|
28
|
+
* kind: 'apikey';
|
|
29
|
+
* apiKey: string;
|
|
30
|
+
* baseUrl?: string;
|
|
31
|
+
* deployment?: string;
|
|
32
|
+
* apiVersion?: string;
|
|
33
|
+
* }} ApiKeyAccount
|
|
34
|
+
*/
|
|
35
|
+
/** @typedef {{ name: string; value: string; domains: string[] }} SecretEntry */
|
|
36
|
+
/** @type {readonly EffortLevel[]} */
|
|
37
|
+
export const EFFORT_LEVELS = Object.freeze(['off', 'minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
38
|
+
/**
|
|
39
|
+
* @param {unknown} value
|
|
40
|
+
* @returns {value is EffortLevel}
|
|
41
|
+
*/
|
|
42
|
+
export function isEffortLevel(value) {
|
|
43
|
+
return (typeof value === 'string' && /** @type {readonly string[]} */ (EFFORT_LEVELS).includes(value));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Secret names: identifier-shaped, plus `.` and `-` for namespaced subsystem
|
|
47
|
+
* secrets (`s3.<profile>.access_key_id`). The `secrets.env` key is everything
|
|
48
|
+
* before the first `=`, so none of these characters can split a line; only
|
|
49
|
+
* POSIX-identifier names ever reach the agent shell as `$NAME`.
|
|
50
|
+
*/
|
|
51
|
+
export const SECRET_NAME_RE = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
|
|
52
|
+
/** Owned by node-server's in-memory OAuth replica store; a bundle may not write it. */
|
|
53
|
+
const RESERVED_PREFIX = 'oauth.';
|
|
54
|
+
/** `secrets.env` reads any `<NAME>_DOMAINS` key as NAME's domain list. */
|
|
55
|
+
const DOMAINS_SUFFIX = '_DOMAINS';
|
|
56
|
+
/**
|
|
57
|
+
* @param {unknown} v
|
|
58
|
+
* @returns {v is string}
|
|
59
|
+
*/
|
|
60
|
+
function isStr(v) {
|
|
61
|
+
return typeof v === 'string';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} v
|
|
65
|
+
* @returns {boolean}
|
|
66
|
+
*/
|
|
67
|
+
function hasNewline(v) {
|
|
68
|
+
return /[\r\n]/.test(v);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* @param {Record<string, unknown>} obj
|
|
72
|
+
* @param {string} key
|
|
73
|
+
* @returns {Record<string, string>}
|
|
74
|
+
*/
|
|
75
|
+
function optionalStr(obj, key) {
|
|
76
|
+
const v = obj[key];
|
|
77
|
+
return isStr(v) ? { [key]: v } : {};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Validate one untrusted provider account. Only the known fields of the
|
|
81
|
+
* chosen kind survive; the credential and `providerId` must be non-empty.
|
|
82
|
+
* @param {unknown} input
|
|
83
|
+
* @param {string} where error-message prefix, e.g. `cone-config: accounts[0]`
|
|
84
|
+
* @returns {OAuthAccount | ApiKeyAccount}
|
|
85
|
+
*/
|
|
86
|
+
export function validateAccount(input, where) {
|
|
87
|
+
if (input === null || typeof input !== 'object')
|
|
88
|
+
throw new Error(`${where} is not an object`);
|
|
89
|
+
const a = /** @type {Record<string, unknown>} */ (input);
|
|
90
|
+
if (!isStr(a.providerId) || !a.providerId)
|
|
91
|
+
throw new Error(`${where}.providerId required`);
|
|
92
|
+
if (a.kind === 'oauth') {
|
|
93
|
+
if (!isStr(a.accessToken) || !a.accessToken) {
|
|
94
|
+
throw new Error(`${where}: oauth account requires accessToken`);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
providerId: a.providerId,
|
|
98
|
+
kind: 'oauth',
|
|
99
|
+
accessToken: a.accessToken,
|
|
100
|
+
...optionalStr(a, 'refreshToken'),
|
|
101
|
+
...(typeof a.tokenExpiresAt === 'number' ? { tokenExpiresAt: a.tokenExpiresAt } : {}),
|
|
102
|
+
...optionalStr(a, 'userName'),
|
|
103
|
+
...optionalStr(a, 'baseUrl'),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (a.kind === 'apikey') {
|
|
107
|
+
if (!isStr(a.apiKey) || !a.apiKey)
|
|
108
|
+
throw new Error(`${where}: apikey account requires apiKey`);
|
|
109
|
+
return {
|
|
110
|
+
providerId: a.providerId,
|
|
111
|
+
kind: 'apikey',
|
|
112
|
+
apiKey: a.apiKey,
|
|
113
|
+
...optionalStr(a, 'baseUrl'),
|
|
114
|
+
...optionalStr(a, 'deployment'),
|
|
115
|
+
...optionalStr(a, 'apiVersion'),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`${where}.kind must be 'oauth' | 'apikey'`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Validate one untrusted secret against the line-oriented `secrets.env`
|
|
122
|
+
* schema (no escaping): a {@link SECRET_NAME_RE} name outside the reserved
|
|
123
|
+
* `oauth.*` namespace and not ending in `_DOMAINS`, a single-line value, and
|
|
124
|
+
* at least one domain, each trimmed, non-blank, single-line and comma-free.
|
|
125
|
+
* node-server drops a secret with no domains, so `[]` is rejected here
|
|
126
|
+
* rather than shipped as a secret that silently never loads.
|
|
127
|
+
* @param {unknown} input
|
|
128
|
+
* @param {string} where error-message prefix, e.g. `cone-config: secrets[0]`
|
|
129
|
+
* @returns {SecretEntry}
|
|
130
|
+
*/
|
|
131
|
+
export function validateSecret(input, where) {
|
|
132
|
+
if (input === null || typeof input !== 'object')
|
|
133
|
+
throw new Error(`${where} is not an object`);
|
|
134
|
+
const s = /** @type {Record<string, unknown>} */ (input);
|
|
135
|
+
if (!isStr(s.name) || !SECRET_NAME_RE.test(s.name)) {
|
|
136
|
+
throw new Error(`${where}: name must be an identifier matching ${SECRET_NAME_RE}`);
|
|
137
|
+
}
|
|
138
|
+
if (s.name.startsWith(RESERVED_PREFIX)) {
|
|
139
|
+
throw new Error(`${where} ${s.name}: the oauth.* namespace is reserved for OAuth replicas`);
|
|
140
|
+
}
|
|
141
|
+
if (s.name.endsWith(DOMAINS_SUFFIX)) {
|
|
142
|
+
throw new Error(`${where} ${s.name}: names ending in _DOMAINS are reserved for domain lines`);
|
|
143
|
+
}
|
|
144
|
+
if (!isStr(s.value) || hasNewline(s.value)) {
|
|
145
|
+
throw new Error(`${where} ${s.name}: value must be a single-line string`);
|
|
146
|
+
}
|
|
147
|
+
if (!Array.isArray(s.domains) || s.domains.length === 0 || !s.domains.every(isStr)) {
|
|
148
|
+
throw new Error(`${where} ${s.name}: domains must be a non-empty string[]`);
|
|
149
|
+
}
|
|
150
|
+
const domains = s.domains.map((d) => d.trim());
|
|
151
|
+
if (domains.some((d) => !d || hasNewline(d) || d.includes(','))) {
|
|
152
|
+
throw new Error(`${where} ${s.name}: domains must be non-blank, single-line and comma-free`);
|
|
153
|
+
}
|
|
154
|
+
return { name: s.name, value: s.value, domains };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Serialize validated secrets to the `NAME=value` / `NAME_DOMAINS=a,b` lines
|
|
158
|
+
* node-server's EnvSecretStore reads. Values are written verbatim, so callers
|
|
159
|
+
* pass entries that went through {@link validateSecret}.
|
|
160
|
+
* @param {readonly SecretEntry[]} secrets
|
|
161
|
+
* @returns {string}
|
|
162
|
+
*/
|
|
163
|
+
export function serializeSecretsEnv(secrets) {
|
|
164
|
+
const lines = [];
|
|
165
|
+
for (const s of secrets) {
|
|
166
|
+
lines.push(`${s.name}=${s.value}`);
|
|
167
|
+
lines.push(`${s.name}${DOMAINS_SUFFIX}=${s.domains.join(',')}`);
|
|
168
|
+
}
|
|
169
|
+
return lines.length ? `${lines.join('\n')}\n` : '';
|
|
170
|
+
}
|
|
@@ -72,9 +72,8 @@ export declare function mergeConeConfig(base: ConeConfig, delta: ConeConfigDelta
|
|
|
72
72
|
/**
|
|
73
73
|
* Serialize flat secrets to the `NAME=value` / `NAME_DOMAINS=a,b` line format
|
|
74
74
|
* that node-server's EnvSecretStore reads. Values are written verbatim (no
|
|
75
|
-
* escaping — matching the existing parser), so
|
|
76
|
-
*
|
|
77
|
-
* must not break `NAME=value` parsing). Callers sanitize/validate inputs.
|
|
75
|
+
* escaping — matching the existing parser), so entries must have passed the
|
|
76
|
+
* bundle validators (single-line values, comma-free domains, safe names).
|
|
78
77
|
*/
|
|
79
78
|
export declare function serializeSecretsEnv(secrets: SecretEntry[]): string;
|
|
80
79
|
export declare function bundleToFiles(cfg: ConeConfig): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cone-config/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cone-config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// Side-effect-free shared contract for cloud-cone configuration.
|
|
2
2
|
// MUST NOT import e2b, node:*, or any runtime substrate — it is imported
|
|
3
3
|
// by the browser webapp via the @slicc/cloud-core/cone-config subpath.
|
|
4
|
+
// Account and secret rules (including the secrets.env line-format constraints)
|
|
5
|
+
// live in the zero-dependency cone-config-rules.mjs, shared byte-for-byte with
|
|
6
|
+
// packages/github-workflow so both floats accept exactly the same bundles.
|
|
7
|
+
import { isEffortLevel, serializeSecretsEnv as serializeSecretLines, validateAccount, validateSecret, } from './cone-config-rules.mjs';
|
|
4
8
|
/** Max serialized bundle size (bytes) accepted as a preboot env payload. */
|
|
5
9
|
export const MAX_CONE_CONFIG_BYTES = 256 * 1024;
|
|
6
10
|
/**
|
|
@@ -17,7 +21,6 @@ function isUntrustedObject(value) {
|
|
|
17
21
|
function isStr(v) {
|
|
18
22
|
return typeof v === 'string';
|
|
19
23
|
}
|
|
20
|
-
const VALID_EFFORT_LEVELS = new Set(['off', 'minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
21
24
|
export function validateConeConfig(input) {
|
|
22
25
|
if (!isUntrustedObject(input))
|
|
23
26
|
throw new Error('cone-config: not an object');
|
|
@@ -28,80 +31,17 @@ export function validateConeConfig(input) {
|
|
|
28
31
|
throw new Error('cone-config: accounts must be an array');
|
|
29
32
|
if (!Array.isArray(cfg.secrets))
|
|
30
33
|
throw new Error('cone-config: secrets must be an array');
|
|
31
|
-
const accounts = cfg.accounts.map((a) => validateAccount(a));
|
|
32
|
-
const secrets = cfg.secrets.map((s) => validateSecret(s));
|
|
34
|
+
const accounts = cfg.accounts.map((a, i) => validateAccount(a, `cone-config: accounts[${i}]`));
|
|
35
|
+
const secrets = cfg.secrets.map((s, i) => validateSecret(s, `cone-config: secrets[${i}]`));
|
|
33
36
|
const result = { model: cfg.model, accounts, secrets };
|
|
34
37
|
if (cfg.effortLevel !== undefined) {
|
|
35
|
-
if (!
|
|
38
|
+
if (!isEffortLevel(cfg.effortLevel)) {
|
|
36
39
|
throw new Error('cone-config: effortLevel must be one of off|minimal|low|medium|high|xhigh');
|
|
37
40
|
}
|
|
38
41
|
result.effortLevel = cfg.effortLevel;
|
|
39
42
|
}
|
|
40
43
|
return result;
|
|
41
44
|
}
|
|
42
|
-
function validateAccount(a) {
|
|
43
|
-
if (!isUntrustedObject(a))
|
|
44
|
-
throw new Error('cone-config: account not an object');
|
|
45
|
-
const acc = a;
|
|
46
|
-
if (!isStr(acc.providerId))
|
|
47
|
-
throw new Error('cone-config: account.providerId required');
|
|
48
|
-
if (!isStr(acc.kind))
|
|
49
|
-
throw new Error('cone-config: account.kind required');
|
|
50
|
-
if (acc.kind === 'oauth') {
|
|
51
|
-
if (!isStr(acc.accessToken))
|
|
52
|
-
throw new Error('cone-config: oauth account requires accessToken');
|
|
53
|
-
return {
|
|
54
|
-
providerId: acc.providerId,
|
|
55
|
-
kind: 'oauth',
|
|
56
|
-
accessToken: acc.accessToken,
|
|
57
|
-
...(isStr(acc.refreshToken) ? { refreshToken: acc.refreshToken } : {}),
|
|
58
|
-
...(typeof acc.tokenExpiresAt === 'number' ? { tokenExpiresAt: acc.tokenExpiresAt } : {}),
|
|
59
|
-
...(isStr(acc.userName) ? { userName: acc.userName } : {}),
|
|
60
|
-
...(isStr(acc.baseUrl) ? { baseUrl: acc.baseUrl } : {}),
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
if (acc.kind === 'apikey') {
|
|
64
|
-
if (!isStr(acc.apiKey))
|
|
65
|
-
throw new Error('cone-config: apikey account requires apiKey');
|
|
66
|
-
return {
|
|
67
|
-
providerId: acc.providerId,
|
|
68
|
-
kind: 'apikey',
|
|
69
|
-
apiKey: acc.apiKey,
|
|
70
|
-
...(isStr(acc.baseUrl) ? { baseUrl: acc.baseUrl } : {}),
|
|
71
|
-
...(isStr(acc.deployment) ? { deployment: acc.deployment } : {}),
|
|
72
|
-
...(isStr(acc.apiVersion) ? { apiVersion: acc.apiVersion } : {}),
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
throw new Error(`cone-config: account.kind must be 'oauth' | 'apikey'`);
|
|
76
|
-
}
|
|
77
|
-
// secrets.env is line-based (NAME=value / NAME_DOMAINS=a,b) with no escaping,
|
|
78
|
-
// so a newline/CR in a name or value would inject phantom lines on round-trip.
|
|
79
|
-
// Names must be env-var identifiers; values and domains must be single-line.
|
|
80
|
-
const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
81
|
-
function hasNewline(v) {
|
|
82
|
-
return /[\r\n]/.test(v);
|
|
83
|
-
}
|
|
84
|
-
function validateSecret(s) {
|
|
85
|
-
if (!isUntrustedObject(s))
|
|
86
|
-
throw new Error('cone-config: secret not an object');
|
|
87
|
-
const sec = s;
|
|
88
|
-
if (!isStr(sec.name))
|
|
89
|
-
throw new Error('cone-config: secret.name required');
|
|
90
|
-
if (!ENV_NAME_RE.test(sec.name)) {
|
|
91
|
-
throw new Error('cone-config: secret.name must be an env-var identifier ([A-Za-z_][A-Za-z0-9_]*)');
|
|
92
|
-
}
|
|
93
|
-
if (!isStr(sec.value))
|
|
94
|
-
throw new Error('cone-config: secret.value required');
|
|
95
|
-
if (hasNewline(sec.value))
|
|
96
|
-
throw new Error('cone-config: secret.value must be single-line');
|
|
97
|
-
if (!Array.isArray(sec.domains) || !sec.domains.every(isStr)) {
|
|
98
|
-
throw new Error('cone-config: secret.domains must be string[]');
|
|
99
|
-
}
|
|
100
|
-
if (sec.domains.some((d) => hasNewline(d) || d.includes(','))) {
|
|
101
|
-
throw new Error('cone-config: secret.domains entries must be single-line and comma-free');
|
|
102
|
-
}
|
|
103
|
-
return { name: sec.name, value: sec.value, domains: sec.domains };
|
|
104
|
-
}
|
|
105
45
|
/** Validate the `upsert` arm of a resume delta (accounts/secrets reuse the full-bundle validators). */
|
|
106
46
|
function validateDeltaUpsert(input) {
|
|
107
47
|
if (!isUntrustedObject(input)) {
|
|
@@ -113,13 +53,13 @@ function validateDeltaUpsert(input) {
|
|
|
113
53
|
if (!Array.isArray(up.accounts)) {
|
|
114
54
|
throw new Error('cone-config: delta.upsert.accounts must be an array');
|
|
115
55
|
}
|
|
116
|
-
upsert.accounts = up.accounts.map((a) => validateAccount(a));
|
|
56
|
+
upsert.accounts = up.accounts.map((a, i) => validateAccount(a, `cone-config: delta.upsert.accounts[${i}]`));
|
|
117
57
|
}
|
|
118
58
|
if (up.secrets !== undefined) {
|
|
119
59
|
if (!Array.isArray(up.secrets)) {
|
|
120
60
|
throw new Error('cone-config: delta.upsert.secrets must be an array');
|
|
121
61
|
}
|
|
122
|
-
upsert.secrets = up.secrets.map((s) => validateSecret(s));
|
|
62
|
+
upsert.secrets = up.secrets.map((s, i) => validateSecret(s, `cone-config: delta.upsert.secrets[${i}]`));
|
|
123
63
|
}
|
|
124
64
|
return upsert;
|
|
125
65
|
}
|
|
@@ -164,7 +104,7 @@ export function validateConeConfigDelta(input) {
|
|
|
164
104
|
if (d.effortLevel === null) {
|
|
165
105
|
out.effortLevel = null;
|
|
166
106
|
}
|
|
167
|
-
else if (!
|
|
107
|
+
else if (!isEffortLevel(d.effortLevel)) {
|
|
168
108
|
throw new Error('cone-config: delta.effortLevel must be one of off|minimal|low|medium|high|xhigh or null');
|
|
169
109
|
}
|
|
170
110
|
else {
|
|
@@ -204,17 +144,11 @@ export function mergeConeConfig(base, delta) {
|
|
|
204
144
|
/**
|
|
205
145
|
* Serialize flat secrets to the `NAME=value` / `NAME_DOMAINS=a,b` line format
|
|
206
146
|
* that node-server's EnvSecretStore reads. Values are written verbatim (no
|
|
207
|
-
* escaping — matching the existing parser), so
|
|
208
|
-
*
|
|
209
|
-
* must not break `NAME=value` parsing). Callers sanitize/validate inputs.
|
|
147
|
+
* escaping — matching the existing parser), so entries must have passed the
|
|
148
|
+
* bundle validators (single-line values, comma-free domains, safe names).
|
|
210
149
|
*/
|
|
211
150
|
export function serializeSecretsEnv(secrets) {
|
|
212
|
-
|
|
213
|
-
for (const s of secrets) {
|
|
214
|
-
lines.push(`${s.name}=${s.value}`);
|
|
215
|
-
lines.push(`${s.name}_DOMAINS=${s.domains.join(',')}`);
|
|
216
|
-
}
|
|
217
|
-
return lines.length ? lines.join('\n') + '\n' : '';
|
|
151
|
+
return serializeSecretLines(secrets);
|
|
218
152
|
}
|
|
219
153
|
export function bundleToFiles(cfg) {
|
|
220
154
|
return {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-cases.test.d.ts","sourceRoot":"","sources":["../../../tests/cone-config/bundle-cases.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { bundleToFiles, validateConeConfig, validateConeConfigDelta, } from '../../src/cone-config/index.js';
|
|
3
|
+
import { ACCOUNT_CASES, INVALID_EFFORT_LEVELS, SECRET_CASES, VALID_EFFORT_LEVELS, } from '../fixtures/cone-config-cases.mjs';
|
|
4
|
+
// The same table runs against github-workflow's buildConeConfigFiles in
|
|
5
|
+
// packages/github-workflow/scripts/cone-config-rules.test.mjs (#4129).
|
|
6
|
+
const bundle = (extra) => ({ model: 'm', accounts: [], secrets: [], ...extra });
|
|
7
|
+
describe('shared cone-config cases: secrets', () => {
|
|
8
|
+
for (const c of SECRET_CASES) {
|
|
9
|
+
if (c.reject) {
|
|
10
|
+
it(`rejects ${c.id} (bundle and resume delta)`, () => {
|
|
11
|
+
const re = new RegExp(c.reject);
|
|
12
|
+
expect(() => validateConeConfig(bundle({ secrets: [c.secret] }))).toThrow(re);
|
|
13
|
+
expect(() => validateConeConfigDelta({ upsert: { secrets: [c.secret] } })).toThrow(re);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
it(`accepts ${c.id}`, () => {
|
|
18
|
+
const cfg = validateConeConfig(bundle({ secrets: [c.secret] }));
|
|
19
|
+
const { name, value } = c.secret;
|
|
20
|
+
expect(cfg.secrets).toEqual([{ name, value, domains: c.domains }]);
|
|
21
|
+
expect(bundleToFiles(cfg).secretsEnv).toBe(`${name}=${value}\n${name}_DOMAINS=${c.domains.join(',')}\n`);
|
|
22
|
+
expect(validateConeConfigDelta({ upsert: { secrets: [c.secret] } }).upsert?.secrets).toEqual(cfg.secrets);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
describe('shared cone-config cases: accounts', () => {
|
|
28
|
+
for (const c of ACCOUNT_CASES) {
|
|
29
|
+
if (c.reject) {
|
|
30
|
+
it(`rejects ${c.id}`, () => {
|
|
31
|
+
const re = new RegExp(c.reject);
|
|
32
|
+
expect(() => validateConeConfig(bundle({ accounts: [c.account] }))).toThrow(re);
|
|
33
|
+
expect(() => validateConeConfigDelta({ upsert: { accounts: [c.account] } })).toThrow(re);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
it(`accepts ${c.id}`, () => {
|
|
38
|
+
const [account] = validateConeConfig(bundle({ accounts: [c.account] })).accounts;
|
|
39
|
+
const allowed = ['providerId', 'kind', 'baseUrl'].concat(account.kind === 'oauth'
|
|
40
|
+
? ['accessToken', 'refreshToken', 'tokenExpiresAt', 'userName']
|
|
41
|
+
: ['apiKey', 'deployment', 'apiVersion']);
|
|
42
|
+
for (const key of Object.keys(account))
|
|
43
|
+
expect(allowed).toContain(key);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
describe('shared cone-config cases: effort levels', () => {
|
|
49
|
+
it.each(VALID_EFFORT_LEVELS)('accepts %s', (level) => {
|
|
50
|
+
expect(validateConeConfig(bundle({ effortLevel: level })).effortLevel).toBe(level);
|
|
51
|
+
expect(validateConeConfigDelta({ effortLevel: level }).effortLevel).toBe(level);
|
|
52
|
+
});
|
|
53
|
+
it.each(INVALID_EFFORT_LEVELS)('rejects %s', (level) => {
|
|
54
|
+
expect(() => validateConeConfig(bundle({ effortLevel: level }))).toThrow(/effortLevel/);
|
|
55
|
+
expect(() => validateConeConfigDelta({ effortLevel: level })).toThrow(/effortLevel/);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -44,13 +44,13 @@ describe('validateConeConfig', () => {
|
|
|
44
44
|
expect(() => validateConeConfig({ ...base, secrets: [{ name: 'X', value: 'v', domains: 'a,b' }] })).toThrow(/domains/);
|
|
45
45
|
});
|
|
46
46
|
it('rejects an account with a missing/invalid kind', () => {
|
|
47
|
-
expect(() => validateConeConfig({ ...base, accounts: [{ providerId: 'x' }] })).toThrow(/kind
|
|
47
|
+
expect(() => validateConeConfig({ ...base, accounts: [{ providerId: 'x' }] })).toThrow(/kind must be/);
|
|
48
48
|
});
|
|
49
|
-
it('rejects a secret name that is not an
|
|
50
|
-
expect(() => validateConeConfig({ ...base, secrets: [{ name: 'FOO=BAR', value: 'v', domains: [] }] })).toThrow(/identifier/);
|
|
49
|
+
it('rejects a secret name that is not an identifier', () => {
|
|
50
|
+
expect(() => validateConeConfig({ ...base, secrets: [{ name: 'FOO=BAR', value: 'v', domains: ['d'] }] })).toThrow(/identifier/);
|
|
51
51
|
});
|
|
52
52
|
it('rejects a secret value or domain with a newline (secrets.env is line-based)', () => {
|
|
53
|
-
expect(() => validateConeConfig({ ...base, secrets: [{ name: 'X', value: 'a\nb', domains: [] }] })).toThrow(/single-line/);
|
|
53
|
+
expect(() => validateConeConfig({ ...base, secrets: [{ name: 'X', value: 'a\nb', domains: ['d'] }] })).toThrow(/single-line/);
|
|
54
54
|
expect(() => validateConeConfig({ ...base, secrets: [{ name: 'X', value: 'v', domains: ['a,b'] }] })).toThrow(/comma-free/);
|
|
55
55
|
});
|
|
56
56
|
it('accepts a config with a valid effortLevel', () => {
|
|
@@ -80,7 +80,9 @@ describe('validateConeConfigDelta', () => {
|
|
|
80
80
|
expect(() => validateConeConfigDelta({ upsert: { accounts: 'nope' } })).toThrow(/upsert.accounts must be an array/);
|
|
81
81
|
});
|
|
82
82
|
it('rejects a nested secret that would inject a newline', () => {
|
|
83
|
-
expect(() => validateConeConfigDelta({
|
|
83
|
+
expect(() => validateConeConfigDelta({
|
|
84
|
+
upsert: { secrets: [{ name: 'X', value: 'a\nb', domains: ['d'] }] },
|
|
85
|
+
})).toThrow(/single-line/);
|
|
84
86
|
});
|
|
85
87
|
it('rejects a non-string delete.secretNames entry', () => {
|
|
86
88
|
expect(() => validateConeConfigDelta({ delete: { secretNames: [1] } })).toThrow(/secretNames must be string\[\]/);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type SecretCase = {
|
|
2
|
+
id: string;
|
|
3
|
+
secret: unknown;
|
|
4
|
+
reject?: string;
|
|
5
|
+
domains?: string[];
|
|
6
|
+
changed?: Array<'cloud-core' | 'github-workflow'>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {{
|
|
10
|
+
* id: string;
|
|
11
|
+
* secret: unknown;
|
|
12
|
+
* reject?: string;
|
|
13
|
+
* domains?: string[];
|
|
14
|
+
* changed?: Array<'cloud-core' | 'github-workflow'>;
|
|
15
|
+
* }} SecretCase
|
|
16
|
+
*/
|
|
17
|
+
/** @type {SecretCase[]} */
|
|
18
|
+
export declare const SECRET_CASES: SecretCase[];
|
|
19
|
+
export type AccountCase = {
|
|
20
|
+
id: string;
|
|
21
|
+
account: unknown;
|
|
22
|
+
reject?: string;
|
|
23
|
+
changed?: Array<'cloud-core' | 'github-workflow'>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {{
|
|
27
|
+
* id: string;
|
|
28
|
+
* account: unknown;
|
|
29
|
+
* reject?: string;
|
|
30
|
+
* changed?: Array<'cloud-core' | 'github-workflow'>;
|
|
31
|
+
* }} AccountCase
|
|
32
|
+
*/
|
|
33
|
+
/** @type {AccountCase[]} */
|
|
34
|
+
export declare const ACCOUNT_CASES: AccountCase[];
|
|
35
|
+
export declare const VALID_EFFORT_LEVELS: string[];
|
|
36
|
+
export declare const INVALID_EFFORT_LEVELS: string[];
|
|
37
|
+
//# sourceMappingURL=cone-config-cases.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cone-config-cases.d.mts","sourceRoot":"","sources":["../../../tests/fixtures/cone-config-cases.mjs"],"names":[],"mappings":"AAWG,YAMG,UAAU,GANH;IACR,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,iBAAiB,CAAC,CAAC;CACnD,CAAY;AAPhB;;;;;;;;GAQG;AAEH,2BAA2B;AAC3B,eAAO,MAAM,YAAY,EADd,UAAU,EA0DpB,CAAC;AAGC,YAKG,WAAW,GALJ;IACR,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,iBAAiB,CAAC,CAAC;CACnD,CAAa;AANjB;;;;;;;GAOG;AAEH,4BAA4B;AAC5B,eAAO,MAAM,aAAa,EADf,WAAW,EAoDrB,CAAC;AAEF,eAAO,MAAM,mBAAmB,UAAuD,CAAC;AACxF,eAAO,MAAM,qBAAqB,UAA2B,CAAC"}
|