@geekmidas/cli 10.0.0-alpha.65 → 10.0.0-alpha.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -177
- package/dist/{HostingerProvider-x9gQTAGZ.mjs → HostingerProvider-D85ayzTi.mjs} +4 -4
- package/dist/HostingerProvider-D85ayzTi.mjs.map +1 -0
- package/dist/{HostingerProvider-Dlmbfass.cjs → HostingerProvider-dxkwynSs.cjs} +4 -4
- package/dist/HostingerProvider-dxkwynSs.cjs.map +1 -0
- package/dist/{LegacyStateStore-BGoo__sO.cjs → LegacyStateStore-BZiO9-Lw.cjs} +2 -2
- package/dist/{LegacyStateStore-BGoo__sO.cjs.map → LegacyStateStore-BZiO9-Lw.cjs.map} +1 -1
- package/dist/{LegacyStateStore-BUu1nQ5u.mjs → LegacyStateStore-C39UKwmf.mjs} +2 -2
- package/dist/{LegacyStateStore-BUu1nQ5u.mjs.map → LegacyStateStore-C39UKwmf.mjs.map} +1 -1
- package/dist/{LocalStateStore-93urXyQe.cjs → LocalStateStore-DHVsZLyF.cjs} +2 -2
- package/dist/{LocalStateStore-93urXyQe.cjs.map → LocalStateStore-DHVsZLyF.cjs.map} +1 -1
- package/dist/{LocalStateStore-BRhMQRuy.mjs → LocalStateStore-Dk3Wbvzk.mjs} +2 -2
- package/dist/{LocalStateStore-BRhMQRuy.mjs.map → LocalStateStore-Dk3Wbvzk.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-BWuDRHe9.cjs → OpenApiTsGenerator-B2zrdyP4.cjs} +2 -2
- package/dist/{OpenApiTsGenerator-BWuDRHe9.cjs.map → OpenApiTsGenerator-B2zrdyP4.cjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-C6v-IeHt.mjs → OpenApiTsGenerator-BmUAcR7d.mjs} +2 -2
- package/dist/{OpenApiTsGenerator-C6v-IeHt.mjs.map → OpenApiTsGenerator-BmUAcR7d.mjs.map} +1 -1
- package/dist/{Route53Provider-DVILBsvV.cjs → Route53Provider-Bllus2NR.cjs} +2 -2
- package/dist/Route53Provider-Bllus2NR.cjs.map +1 -0
- package/dist/{Route53Provider-Cvj40A8I.mjs → Route53Provider-QXxjkZO1.mjs} +2 -2
- package/dist/Route53Provider-QXxjkZO1.mjs.map +1 -0
- package/dist/{S3StateStore-F4dxw6kS.cjs → S3StateStore--4tYVMAL.cjs} +2 -2
- package/dist/{S3StateStore-F4dxw6kS.cjs.map → S3StateStore--4tYVMAL.cjs.map} +1 -1
- package/dist/{S3StateStore-pw1GhWwt.mjs → S3StateStore-B_ZF9eHt.mjs} +2 -2
- package/dist/{S3StateStore-pw1GhWwt.mjs.map → S3StateStore-B_ZF9eHt.mjs.map} +1 -1
- package/dist/{SSMStateStore-Bt9acuAF.cjs → SSMStateStore-ACZ8hRvZ.cjs} +2 -2
- package/dist/{SSMStateStore-Bt9acuAF.cjs.map → SSMStateStore-ACZ8hRvZ.cjs.map} +1 -1
- package/dist/{SSMStateStore-Dmj7zzFO.mjs → SSMStateStore-DjPuKqrm.mjs} +2 -2
- package/dist/{SSMStateStore-Dmj7zzFO.mjs.map → SSMStateStore-DjPuKqrm.mjs.map} +1 -1
- package/dist/{backup-provisioner-DIVCbfee.mjs → backup-provisioner-C8DnCmxK.mjs} +2 -2
- package/dist/backup-provisioner-C8DnCmxK.mjs.map +1 -0
- package/dist/{backup-provisioner-BmhIO_Yr.cjs → backup-provisioner-oDEsPj02.cjs} +2 -2
- package/dist/backup-provisioner-oDEsPj02.cjs.map +1 -0
- package/dist/{bundler-D_14JLZF.mjs → bundler-Bq3HaLOZ.mjs} +20 -5
- package/dist/bundler-Bq3HaLOZ.mjs.map +1 -0
- package/dist/{bundler-D6MlIfBT.cjs → bundler-pyLj-gS0.cjs} +20 -5
- package/dist/bundler-pyLj-gS0.cjs.map +1 -0
- package/dist/{cli-P56vvrDQ.mjs → cli-C2Tjbn27.mjs} +6 -5
- package/dist/cli-C2Tjbn27.mjs.map +1 -0
- package/dist/{cli-BfH1deAT.cjs → cli-DR7B2_fX.cjs} +6 -5
- package/dist/cli-DR7B2_fX.cjs.map +1 -0
- package/dist/{compose-391oEKj1.cjs → compose-5oNHAKNT.cjs} +137 -667
- package/dist/compose-5oNHAKNT.cjs.map +1 -0
- package/dist/{compose-DprE3ASm.mjs → compose-BjToENzG.mjs} +130 -569
- package/dist/compose-BjToENzG.mjs.map +1 -0
- package/dist/{config-V_X_BMpm.d.cts → config-4XFxX1al.d.cts} +3 -3
- package/dist/{config-BonvTW8p.d.mts.map → config-4XFxX1al.d.cts.map} +1 -1
- package/dist/{config-BonvTW8p.d.mts → config-UtKzhpFb.d.mts} +3 -3
- package/dist/{config-V_X_BMpm.d.cts.map → config-UtKzhpFb.d.mts.map} +1 -1
- package/dist/config.cjs +3 -3
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +3 -3
- package/dist/{credentials-Bzbg23n8.cjs → credentials-BVs7mrtw.cjs} +239 -3
- package/dist/credentials-BVs7mrtw.cjs.map +1 -0
- package/dist/{credentials-Dq1MnWQf.mjs → credentials-pAtB70k3.mjs} +223 -5
- package/dist/credentials-pAtB70k3.mjs.map +1 -0
- package/dist/databases-C2p5LGAp.cjs +419 -0
- package/dist/databases-C2p5LGAp.cjs.map +1 -0
- package/dist/databases-hUHTuwhx.mjs +346 -0
- package/dist/databases-hUHTuwhx.mjs.map +1 -0
- package/dist/deploy/public.cjs +37 -28
- package/dist/deploy/public.cjs.map +1 -1
- package/dist/deploy/public.d.cts +136 -19
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +136 -19
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +11 -11
- package/dist/deploy-BucRfbvX.cjs +511 -0
- package/dist/deploy-BucRfbvX.cjs.map +1 -0
- package/dist/deploy-DRAwTG72.mjs +488 -0
- package/dist/deploy-DRAwTG72.mjs.map +1 -0
- package/dist/{discover-LMrqsWSU.mjs → discover-BFoJWKGD.mjs} +4 -2
- package/dist/discover-BFoJWKGD.mjs.map +1 -0
- package/dist/{discover-DHdECueV.cjs → discover-OEfZ9OkA.cjs} +4 -2
- package/dist/discover-OEfZ9OkA.cjs.map +1 -0
- package/dist/dokploy-DIt1IZl1.mjs +38 -0
- package/dist/dokploy-DIt1IZl1.mjs.map +1 -0
- package/dist/dokploy-m345Zx02.cjs +38 -0
- package/dist/dokploy-m345Zx02.cjs.map +1 -0
- package/dist/{encryption-DF_hgIpl.mjs → encryption-Ccbahik7.mjs} +3 -5
- package/dist/{encryption-DF_hgIpl.mjs.map → encryption-Ccbahik7.mjs.map} +1 -1
- package/dist/{encryption-B1wJwgae.cjs → encryption-rZ3BghXy.cjs} +5 -7
- package/dist/{encryption-B1wJwgae.cjs.map → encryption-rZ3BghXy.cjs.map} +1 -1
- package/dist/{deploy-Cy8igpQF.cjs → engine-Cx_dvx9q.cjs} +1491 -1260
- package/dist/engine-Cx_dvx9q.cjs.map +1 -0
- package/dist/{deploy-CZ5nlpsu.mjs → engine-Gs0abjuj.mjs} +1450 -1261
- package/dist/engine-Gs0abjuj.mjs.map +1 -0
- package/dist/{github-B0YYE98x.mjs → github-CJANTYqi.mjs} +3 -3
- package/dist/{github-B0YYE98x.mjs.map → github-CJANTYqi.mjs.map} +1 -1
- package/dist/{github-CYcmTdFs.cjs → github-CrcGFkfD.cjs} +7 -7
- package/dist/{github-CYcmTdFs.cjs.map → github-CrcGFkfD.cjs.map} +1 -1
- package/dist/{index-DPUAGJaK.d.cts → index-CyApaHVe.d.cts} +2 -2
- package/dist/{index-DPUAGJaK.d.cts.map → index-CyApaHVe.d.cts.map} +1 -1
- package/dist/{index-DwKD_8lt.d.mts → index-DD1FfUHh.d.mts} +2 -2
- package/dist/{index-DwKD_8lt.d.mts.map → index-DD1FfUHh.d.mts.map} +1 -1
- package/dist/index.cjs +285 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +258 -68
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-q4pPAXtO.cjs → migrate-CQdSpttI.cjs} +16 -16
- package/dist/{migrate-q4pPAXtO.cjs.map → migrate-CQdSpttI.cjs.map} +1 -1
- package/dist/{migrate-B2qL5wj4.mjs → migrate-DCVo5gLS.mjs} +6 -6
- package/dist/{migrate-B2qL5wj4.mjs.map → migrate-DCVo5gLS.mjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/provider-B3Sv_a74.cjs +173 -0
- package/dist/provider-B3Sv_a74.cjs.map +1 -0
- package/dist/provider-D7rQzzzf.mjs +138 -0
- package/dist/provider-D7rQzzzf.mjs.map +1 -0
- package/dist/{ready-bQfu9GES.mjs → ready-2s3-CndS.mjs} +3 -3
- package/dist/{ready-bQfu9GES.mjs.map → ready-2s3-CndS.mjs.map} +1 -1
- package/dist/{ready-brEhh68f.cjs → ready-B6uHr4sm.cjs} +3 -3
- package/dist/{ready-brEhh68f.cjs.map → ready-B6uHr4sm.cjs.map} +1 -1
- package/dist/reconcile/public.cjs +1 -1
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/reconcile/public.mjs +1 -1
- package/dist/{resolve-N_i4XNd-.cjs → resolve-BexmDcsN.cjs} +4 -4
- package/dist/{resolve-N_i4XNd-.cjs.map → resolve-BexmDcsN.cjs.map} +1 -1
- package/dist/{resolve-DLkIXTct.mjs → resolve-DhZgtvZe.mjs} +4 -4
- package/dist/{resolve-DLkIXTct.mjs.map → resolve-DhZgtvZe.mjs.map} +1 -1
- package/dist/rollback-BnY7KuG_.cjs +161 -0
- package/dist/rollback-BnY7KuG_.cjs.map +1 -0
- package/dist/rollback-m4Yeym5x.mjs +132 -0
- package/dist/rollback-m4Yeym5x.mjs.map +1 -0
- package/dist/run-BJLIerMM.mjs +133 -0
- package/dist/run-BJLIerMM.mjs.map +1 -0
- package/dist/run-fVZ-rLnc.cjs +174 -0
- package/dist/run-fVZ-rLnc.cjs.map +1 -0
- package/dist/{runtime-BnD7Nn96.d.cts → runtime-BRqFOvPM.d.cts} +2 -2
- package/dist/{runtime-BnD7Nn96.d.cts.map → runtime-BRqFOvPM.d.cts.map} +1 -1
- package/dist/{runtime-j-tmFL65.d.mts → runtime-CEk5n4VH.d.mts} +2 -2
- package/dist/{runtime-j-tmFL65.d.mts.map → runtime-CEk5n4VH.d.mts.map} +1 -1
- package/dist/sandbox/discover-worker.cjs +1 -1
- package/dist/sandbox/discover-worker.mjs +1 -1
- package/dist/sandbox/migrate-worker.cjs +61 -0
- package/dist/sandbox/migrate-worker.cjs.map +1 -0
- package/dist/sandbox/migrate-worker.d.cts +1 -0
- package/dist/sandbox/migrate-worker.d.mts +1 -0
- package/dist/sandbox/migrate-worker.mjs +62 -0
- package/dist/sandbox/migrate-worker.mjs.map +1 -0
- package/dist/{schemas-D2LCF689.mjs → schemas-Cfhtpdo0.mjs} +10 -2
- package/dist/{schemas-Ckm_aBnF.cjs.map → schemas-Cfhtpdo0.mjs.map} +1 -1
- package/dist/{schemas-Ckm_aBnF.cjs → schemas-RUlKHu27.cjs} +15 -1
- package/dist/{schemas-D2LCF689.mjs.map → schemas-RUlKHu27.cjs.map} +1 -1
- package/dist/{sniffer-BZ-OJO4Y.mjs → sniffer-BdFeLSmA.mjs} +97 -600
- package/dist/sniffer-BdFeLSmA.mjs.map +1 -0
- package/dist/{sniffer-BSwaL-dX.cjs → sniffer-D66HIvi9.cjs} +110 -674
- package/dist/sniffer-D66HIvi9.cjs.map +1 -0
- package/dist/{sst-B2HbrDR3.mjs → sst-DRi3H5TB.mjs} +3 -3
- package/dist/{sst-B2HbrDR3.mjs.map → sst-DRi3H5TB.mjs.map} +1 -1
- package/dist/{sst-DcfsVH6x.cjs → sst-hBrKouBy.cjs} +5 -5
- package/dist/{sst-DcfsVH6x.cjs.map → sst-hBrKouBy.cjs.map} +1 -1
- package/dist/{state-DF26e_lo.cjs → state-DgmM4EKq.cjs} +2787 -1890
- package/dist/state-DgmM4EKq.cjs.map +1 -0
- package/dist/{state-B8YwmUZU.mjs → state-DkUStoqJ.mjs} +2813 -2084
- package/dist/state-DkUStoqJ.mjs.map +1 -0
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-CfbiYUbZ.mjs → terminal-C3cv-OQu.mjs} +5 -5
- package/dist/{terminal-CfbiYUbZ.mjs.map → terminal-C3cv-OQu.mjs.map} +1 -1
- package/dist/{terminal-Bs5E1oxm.cjs → terminal-DjzgLu6m.cjs} +12 -16
- package/dist/{terminal-Bs5E1oxm.cjs.map → terminal-DjzgLu6m.cjs.map} +1 -1
- package/dist/{types-ByMWkXKP.d.cts → types-CSw2w69a.d.mts} +68 -10
- package/dist/types-CSw2w69a.d.mts.map +1 -0
- package/dist/{types-BCzMWBb6.d.mts → types-D5WIxRDP.d.cts} +69 -9
- package/dist/types-D5WIxRDP.d.cts.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-C06_pW4g.mjs → workspace-Be6uBhlF.mjs} +10 -3
- package/dist/workspace-Be6uBhlF.mjs.map +1 -0
- package/dist/{workspace-BewAHZIo.cjs → workspace-ON-bqiJz.cjs} +10 -3
- package/dist/workspace-ON-bqiJz.cjs.map +1 -0
- package/package.json +13 -13
- package/dist/HostingerProvider-Dlmbfass.cjs.map +0 -1
- package/dist/HostingerProvider-x9gQTAGZ.mjs.map +0 -1
- package/dist/Route53Provider-Cvj40A8I.mjs.map +0 -1
- package/dist/Route53Provider-DVILBsvV.cjs.map +0 -1
- package/dist/backup-provisioner-BmhIO_Yr.cjs.map +0 -1
- package/dist/backup-provisioner-DIVCbfee.mjs.map +0 -1
- package/dist/bundler-D6MlIfBT.cjs.map +0 -1
- package/dist/bundler-D_14JLZF.mjs.map +0 -1
- package/dist/cli-BfH1deAT.cjs.map +0 -1
- package/dist/cli-P56vvrDQ.mjs.map +0 -1
- package/dist/compose-391oEKj1.cjs.map +0 -1
- package/dist/compose-DprE3ASm.mjs.map +0 -1
- package/dist/credentials-Bzbg23n8.cjs.map +0 -1
- package/dist/credentials-Dq1MnWQf.mjs.map +0 -1
- package/dist/deploy-9MBG_QLd.mjs +0 -137
- package/dist/deploy-9MBG_QLd.mjs.map +0 -1
- package/dist/deploy-B80idPUZ.cjs +0 -142
- package/dist/deploy-B80idPUZ.cjs.map +0 -1
- package/dist/deploy-CZ5nlpsu.mjs.map +0 -1
- package/dist/deploy-Cy8igpQF.cjs.map +0 -1
- package/dist/discover-DHdECueV.cjs.map +0 -1
- package/dist/discover-LMrqsWSU.mjs.map +0 -1
- package/dist/dokploy-BdtBePjp.mjs +0 -32
- package/dist/dokploy-BdtBePjp.mjs.map +0 -1
- package/dist/dokploy-iFY8MY-L.cjs +0 -32
- package/dist/dokploy-iFY8MY-L.cjs.map +0 -1
- package/dist/local-BdH4XN0E.cjs +0 -409
- package/dist/local-BdH4XN0E.cjs.map +0 -1
- package/dist/local-CWR4Y_kW.mjs +0 -350
- package/dist/local-CWR4Y_kW.mjs.map +0 -1
- package/dist/sniffer-BSwaL-dX.cjs.map +0 -1
- package/dist/sniffer-BZ-OJO4Y.mjs.map +0 -1
- package/dist/state-B8YwmUZU.mjs.map +0 -1
- package/dist/state-DF26e_lo.cjs.map +0 -1
- package/dist/storage-BzaUxmqe.mjs +0 -124
- package/dist/storage-BzaUxmqe.mjs.map +0 -1
- package/dist/storage-CSGoC3bK.cjs +0 -152
- package/dist/storage-CSGoC3bK.cjs.map +0 -1
- package/dist/store-CJF1Tqkq.cjs +0 -510
- package/dist/store-CJF1Tqkq.cjs.map +0 -1
- package/dist/store-CLiHnqOY.mjs +0 -415
- package/dist/store-CLiHnqOY.mjs.map +0 -1
- package/dist/types-BCzMWBb6.d.mts.map +0 -1
- package/dist/types-ByMWkXKP.d.cts.map +0 -1
- package/dist/workspace-BewAHZIo.cjs.map +0 -1
- package/dist/workspace-C06_pW4g.mjs.map +0 -1
package/dist/store-CLiHnqOY.mjs
DELETED
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
|
|
2
|
-
import { u as gkmHome } from "./local-CWR4Y_kW.mjs";
|
|
3
|
-
import { basename, join } from "node:path";
|
|
4
|
-
import { constants, existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import { chmod, copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
6
|
-
import { homedir } from "node:os";
|
|
7
|
-
import { kebabCase, scopedName } from "@geekmidas/manifest";
|
|
8
|
-
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
9
|
-
//#region src/deploy/identity.ts
|
|
10
|
-
/**
|
|
11
|
-
* Who a deploy is, on a target it shares with other workspaces.
|
|
12
|
-
*
|
|
13
|
-
* A Dokploy server is one flat namespace of projects, images and Docker
|
|
14
|
-
* service names. Deploys used to identify everything they made by the
|
|
15
|
-
* workspace name alone, so two workspaces called `shop` — or `Shop` and
|
|
16
|
-
* `shop` — deploying to one server landed in the same project, pushed over
|
|
17
|
-
* each other's images and redeployed each other's applications. A
|
|
18
|
-
* `DeployIdentity` is what a deploy names and claims its resources by instead.
|
|
19
|
-
*/
|
|
20
|
-
/** A namespace that cannot be part of a Dokploy name or an image path. */
|
|
21
|
-
var DeployNamespaceInvalid = class extends Error {
|
|
22
|
-
namespace;
|
|
23
|
-
constructor(namespace) {
|
|
24
|
-
super(`deploy.namespace '${namespace}' is not usable as a name: use lowercase letters, digits and single '-' between them (e.g. 'acme' or 'acme-platform') in gkm.config.ts.`);
|
|
25
|
-
this.namespace = namespace;
|
|
26
|
-
this.name = "DeployNamespaceInvalid";
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
/** A workspace name with nothing a target can name a resource by. */
|
|
30
|
-
var DeployProjectNameInvalid = class extends Error {
|
|
31
|
-
workspaceName;
|
|
32
|
-
constructor(workspaceName) {
|
|
33
|
-
super(`The workspace name '${workspaceName}' has no letters or digits to name deployed resources by. Set "name" in gkm.config.ts.`);
|
|
34
|
-
this.workspaceName = workspaceName;
|
|
35
|
-
this.name = "DeployProjectNameInvalid";
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
39
|
-
/**
|
|
40
|
-
* Lowercase `[a-z0-9-]`, runs of anything else collapsed to one `-`: valid as
|
|
41
|
-
* a Docker image path component, a Docker service name and a Dokploy name.
|
|
42
|
-
*/
|
|
43
|
-
function slug(value) {
|
|
44
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
45
|
-
}
|
|
46
|
-
/** The namespace a workspace is in when it does not name one. */
|
|
47
|
-
function defaultNamespace(workspaceName) {
|
|
48
|
-
return slug(kebabCase(workspaceName));
|
|
49
|
-
}
|
|
50
|
-
function deployIdentity(workspace, stage) {
|
|
51
|
-
const project = slug(workspace.name);
|
|
52
|
-
if (!project) throw new DeployProjectNameInvalid(workspace.name);
|
|
53
|
-
const fallback = defaultNamespace(workspace.name);
|
|
54
|
-
const namespace = workspace.deploy?.namespace ?? fallback;
|
|
55
|
-
if (!NAME.test(namespace)) throw new DeployNamespaceInvalid(namespace);
|
|
56
|
-
return {
|
|
57
|
-
namespace,
|
|
58
|
-
project,
|
|
59
|
-
stage,
|
|
60
|
-
key: `${namespace}/${project}`,
|
|
61
|
-
scope: namespace === fallback ? project : `${namespace}-${project}`
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* The token a Dokploy project's description carries to say which identity
|
|
66
|
-
* created it. A project with the right name and no marker belongs to someone
|
|
67
|
-
* else, and a deploy never adopts it.
|
|
68
|
-
*/
|
|
69
|
-
function ownershipMarker(identity) {
|
|
70
|
-
return `gkm:${identity.key}`;
|
|
71
|
-
}
|
|
72
|
-
/** Whether a project description carries `identity`'s marker. */
|
|
73
|
-
function isOwnedBy(description, identity) {
|
|
74
|
-
const marker = ownershipMarker(identity);
|
|
75
|
-
return (description ?? "").split(/\s+/).includes(marker);
|
|
76
|
-
}
|
|
77
|
-
/** Any `gkm:` marker a description carries — whoever's it is. */
|
|
78
|
-
function markerOf(description) {
|
|
79
|
-
return (description ?? "").split(/\s+/).find((t) => t.startsWith("gkm:"));
|
|
80
|
-
}
|
|
81
|
-
/** What the Dokploy project is called. */
|
|
82
|
-
function projectName(identity) {
|
|
83
|
-
return identity.scope;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* The repository an app's image is pushed to, below the registry:
|
|
87
|
-
* `<namespace>/<project>-<app>`.
|
|
88
|
-
*
|
|
89
|
-
* The namespace is a path segment of its own so that a registry that maps
|
|
90
|
-
* paths to owners (an organisation's packages, a Harbor project) can grant it
|
|
91
|
-
* as one; the app is joined to the project so the two read as one name.
|
|
92
|
-
*/
|
|
93
|
-
function imageName(identity, app) {
|
|
94
|
-
return `${identity.namespace}/${identity.project}-${slug(kebabCase(app))}`;
|
|
95
|
-
}
|
|
96
|
-
/** The full image ref an app is built, pushed and deployed as. */
|
|
97
|
-
function imageRef(identity, app, registry, tag) {
|
|
98
|
-
const name = imageName(identity, app);
|
|
99
|
-
return registry ? `${registry.replace(/\/+$/, "")}/${name}:${tag}` : `${name}:${tag}`;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* What one application is called on the target.
|
|
103
|
-
*
|
|
104
|
-
* The same rule the constructs use, through the same `scopedName`, scoped by
|
|
105
|
-
* the stage and the identity: the application beside
|
|
106
|
-
* `production-kitchen-sink-database` is `production-kitchen-sink-api`. A
|
|
107
|
-
* Dokploy application's name is also its Docker service name, which is unique
|
|
108
|
-
* on the whole server, so the namespace has to be in it too — `acme`'s and
|
|
109
|
-
* `globex`'s `shop` would otherwise both run `production-shop-api`.
|
|
110
|
-
*
|
|
111
|
-
* The app id is dropped when it repeats the project, so a project named for its
|
|
112
|
-
* one application is `production-kitchen-sink` rather than
|
|
113
|
-
* `production-kitchen-sink-kitchen-sink`.
|
|
114
|
-
*/
|
|
115
|
-
function applicationName(identity, app) {
|
|
116
|
-
return slug(app) === identity.project ? `${identity.stage}-${identity.scope}` : scopedName([identity.stage, identity.scope], app);
|
|
117
|
-
}
|
|
118
|
-
//#endregion
|
|
119
|
-
//#region src/secrets/keystore.ts
|
|
120
|
-
/**
|
|
121
|
-
* The keys that open each stage's encrypted secrets file, kept outside the
|
|
122
|
-
* project in the CLI's home.
|
|
123
|
-
*
|
|
124
|
-
* Keys were kept at `~/.gkm/<folder>/<stage>.key`, named after the project
|
|
125
|
-
* directory — so two checkouts in folders with the same name (`~/work/api`
|
|
126
|
-
* and `~/oss/api`) shared one key, and the second `gkm secrets:init`
|
|
127
|
-
* overwrote the first project's. They are now kept under the project's
|
|
128
|
-
* identity, `<home>/keys/<namespace>/<project>/<stage>.key`, the same
|
|
129
|
-
* `<namespace>/<project>` a deploy claims its Dokploy project by. A key still
|
|
130
|
-
* at the old place is copied to the new one the first time it is read; the
|
|
131
|
-
* old file stays, so an older CLI on the same machine keeps working.
|
|
132
|
-
*/
|
|
133
|
-
/** Key length for AES-256 encryption */
|
|
134
|
-
const KEY_LENGTH = 32;
|
|
135
|
-
/** A keystore project key that is not `<namespace>/<project>`. */
|
|
136
|
-
var KeystoreProjectInvalid = class extends Error {
|
|
137
|
-
key;
|
|
138
|
-
constructor(key) {
|
|
139
|
-
super(`'${key}' is not a keystore project: expected '<namespace>/<project>' in lowercase letters, digits and '-', as deployIdentity() produces. Pass the workspace's identity key.`);
|
|
140
|
-
this.key = key;
|
|
141
|
-
this.name = "KeystoreProjectInvalid";
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
/** A stage has no key, here or at the place keys used to be kept. */
|
|
145
|
-
var KeyNotFound = class extends Error {
|
|
146
|
-
stage;
|
|
147
|
-
project;
|
|
148
|
-
path;
|
|
149
|
-
constructor(stage, project, path) {
|
|
150
|
-
super(`Encryption key not found for stage "${stage}" in project "${project}". Expected key at: ${path}. Run \`gkm secrets:init --stage ${stage}\` to create one, or copy the stage's key there.`);
|
|
151
|
-
this.stage = stage;
|
|
152
|
-
this.project = project;
|
|
153
|
-
this.path = path;
|
|
154
|
-
this.name = "KeyNotFound";
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
const PROJECT_KEY = /^[a-z0-9]+(?:-[a-z0-9]+)*\/[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
158
|
-
/**
|
|
159
|
-
* The keystore key of a workspace: its deploy identity without the stage, so
|
|
160
|
-
* a project's keys and its Dokploy project are named by the same thing.
|
|
161
|
-
*/
|
|
162
|
-
function projectKey(workspace) {
|
|
163
|
-
return deployIdentity(workspace, "").key;
|
|
164
|
-
}
|
|
165
|
-
/** The keystore project of a loaded workspace. */
|
|
166
|
-
function keystoreProject(workspace, home) {
|
|
167
|
-
const key = projectKey(workspace);
|
|
168
|
-
const unscoped = projectKey({ name: workspace.name });
|
|
169
|
-
return {
|
|
170
|
-
key,
|
|
171
|
-
legacy: [.../* @__PURE__ */ new Set([folderName(workspace.root), workspace.name])].filter((name) => !!name),
|
|
172
|
-
...unscoped !== key ? { aliases: [unscoped] } : {},
|
|
173
|
-
...home ? { home } : {}
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Whether `name` stays inside the home it is joined to. A workspace name was
|
|
178
|
-
* joined as is — `@acme/shop` is `~/.gkm/@acme/shop` — but never upwards.
|
|
179
|
-
*/
|
|
180
|
-
function isLegacyName(name) {
|
|
181
|
-
return name.split(/[\\/]/).every((part) => part !== ".." && part !== "");
|
|
182
|
-
}
|
|
183
|
-
/** The last segment of a path, as the old keystore named projects. */
|
|
184
|
-
function folderName(path) {
|
|
185
|
-
const name = path.split(/[\\/]/).filter(Boolean).at(-1);
|
|
186
|
-
return name && name !== "." && name !== ".." ? name : void 0;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Get the keystore directory for a project:
|
|
190
|
-
* `<home>/keys/<namespace>/<project>`.
|
|
191
|
-
*/
|
|
192
|
-
function getKeystoreDir(project) {
|
|
193
|
-
if (!PROJECT_KEY.test(project.key)) throw new KeystoreProjectInvalid(project.key);
|
|
194
|
-
const [namespace, name] = project.key.split("/");
|
|
195
|
-
return join(project.home ?? gkmHome(), "keys", namespace, name);
|
|
196
|
-
}
|
|
197
|
-
/** Get the path to a stage's encryption key. */
|
|
198
|
-
function getKeyPath(stage, project) {
|
|
199
|
-
return join(getKeystoreDir(project), `${stage}.key`);
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Where else a stage's key may be, most likely first: under an alias of the
|
|
203
|
-
* project, then where keys were kept before they were keyed by identity —
|
|
204
|
-
* under the configured home, then under `~/.gkm`, which is where every CLI
|
|
205
|
-
* before `GKM_HOME` wrote.
|
|
206
|
-
*/
|
|
207
|
-
function legacyKeyPaths(stage, project) {
|
|
208
|
-
const aliases = (project.aliases ?? []).map((key) => getKeyPath(stage, {
|
|
209
|
-
key,
|
|
210
|
-
home: project.home
|
|
211
|
-
}));
|
|
212
|
-
const homes = [.../* @__PURE__ */ new Set([project.home ?? gkmHome(), join(homedir(), ".gkm")])];
|
|
213
|
-
const names = (project.legacy ?? []).filter(isLegacyName);
|
|
214
|
-
return [...aliases, ...names.flatMap((name) => homes.map((home) => join(home, name, `${stage}.key`)))];
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Generate a new encryption key for a stage, stored owner-only at
|
|
218
|
-
* `<home>/keys/<namespace>/<project>/<stage>.key`.
|
|
219
|
-
*/
|
|
220
|
-
async function generateKey(stage, project, flag = "w") {
|
|
221
|
-
const keyDir = getKeystoreDir(project);
|
|
222
|
-
const keyPath = getKeyPath(stage, project);
|
|
223
|
-
await mkdir(keyDir, {
|
|
224
|
-
recursive: true,
|
|
225
|
-
mode: 448
|
|
226
|
-
});
|
|
227
|
-
const key = randomBytes(KEY_LENGTH).toString("hex");
|
|
228
|
-
await writeFile(keyPath, key, {
|
|
229
|
-
mode: 384,
|
|
230
|
-
encoding: "utf-8",
|
|
231
|
-
flag
|
|
232
|
-
});
|
|
233
|
-
await chmod(keyPath, 384);
|
|
234
|
-
return key;
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Copy a key from where it used to be kept to where it is kept now.
|
|
238
|
-
*
|
|
239
|
-
* Copied, not moved: an older CLI on the same machine still reads the old
|
|
240
|
-
* place. Exclusive, so two processes migrating at once cannot overwrite a key
|
|
241
|
-
* one of them already put there.
|
|
242
|
-
*/
|
|
243
|
-
async function migrateLegacyKey(stage, project) {
|
|
244
|
-
const legacy = legacyKeyPaths(stage, project).find((path) => existsSync(path));
|
|
245
|
-
if (!legacy) return false;
|
|
246
|
-
const keyPath = getKeyPath(stage, project);
|
|
247
|
-
await mkdir(getKeystoreDir(project), {
|
|
248
|
-
recursive: true,
|
|
249
|
-
mode: 448
|
|
250
|
-
});
|
|
251
|
-
try {
|
|
252
|
-
await copyFile(legacy, keyPath, constants.COPYFILE_EXCL);
|
|
253
|
-
} catch (error) {
|
|
254
|
-
if (error.code !== "EEXIST") throw error;
|
|
255
|
-
}
|
|
256
|
-
await chmod(keyPath, 384);
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Read an encryption key for a stage, or `null` when there is none — after
|
|
261
|
-
* copying it over from where keys used to be kept, if it is still there.
|
|
262
|
-
*/
|
|
263
|
-
async function readKey(stage, project) {
|
|
264
|
-
const keyPath = getKeyPath(stage, project);
|
|
265
|
-
if (!existsSync(keyPath) && !await migrateLegacyKey(stage, project)) return null;
|
|
266
|
-
return (await readFile(keyPath, "utf-8")).trim();
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Read an encryption key for a stage, throwing {@link KeyNotFound} if there
|
|
270
|
-
* is none.
|
|
271
|
-
*/
|
|
272
|
-
async function requireKey(stage, project) {
|
|
273
|
-
const key = await readKey(stage, project);
|
|
274
|
-
if (!key) throw new KeyNotFound(stage, project.key, getKeyPath(stage, project));
|
|
275
|
-
return key;
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Get or create a key for a stage.
|
|
279
|
-
* If the key already exists, it is returned. Otherwise, a new key is generated.
|
|
280
|
-
*/
|
|
281
|
-
async function getOrCreateKey(stage, project) {
|
|
282
|
-
const existingKey = await readKey(stage, project);
|
|
283
|
-
if (existingKey) return existingKey;
|
|
284
|
-
try {
|
|
285
|
-
return await generateKey(stage, project, "wx");
|
|
286
|
-
} catch (error) {
|
|
287
|
-
if (error.code !== "EEXIST") throw error;
|
|
288
|
-
return requireKey(stage, project);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
//#endregion
|
|
292
|
-
//#region src/secrets/file.ts
|
|
293
|
-
/** Where the files live, relative to the project root. */
|
|
294
|
-
const SECRETS_DIR = ".gkm/secrets";
|
|
295
|
-
/** AES-256-GCM configuration */
|
|
296
|
-
const ALGORITHM = "aes-256-gcm";
|
|
297
|
-
const IV_LENGTH = 12;
|
|
298
|
-
/**
|
|
299
|
-
* A stage's secrets as an encrypted file on this machine:
|
|
300
|
-
* `.gkm/secrets/<stage>.json`, with its key in the CLI's home at
|
|
301
|
-
* `keys/<namespace>/<project>/<stage>.key`.
|
|
302
|
-
*
|
|
303
|
-
* The local stage's store, always — its secrets belong to the machine running
|
|
304
|
-
* `gkm dev` — and a deployed stage's when `secrets.store` is `'file'`.
|
|
305
|
-
*/
|
|
306
|
-
var FileSecretsStore = class {
|
|
307
|
-
root;
|
|
308
|
-
name = "file";
|
|
309
|
-
project;
|
|
310
|
-
/**
|
|
311
|
-
* @param project - Whose keys: a workspace's `keystoreProject()`. Outside a
|
|
312
|
-
* workspace there is no config to name the project, so it is named the
|
|
313
|
-
* way a workspace without `name` is — its package name, else its folder.
|
|
314
|
-
*/
|
|
315
|
-
constructor(root, project) {
|
|
316
|
-
this.root = root;
|
|
317
|
-
this.project = project ?? {
|
|
318
|
-
key: projectKey({ name: packageName(root) ?? basename(root) }),
|
|
319
|
-
legacy: [basename(root)]
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
/** The file a stage's secrets are kept in. */
|
|
323
|
-
path(stage) {
|
|
324
|
-
return join(this.root, SECRETS_DIR, `${stage}.json`);
|
|
325
|
-
}
|
|
326
|
-
async read(stage) {
|
|
327
|
-
const path = this.path(stage);
|
|
328
|
-
if (!existsSync(path)) return null;
|
|
329
|
-
const data = JSON.parse(await readFile(path, "utf-8"));
|
|
330
|
-
if (!(data.version === 1 && data.encrypted && data.iv)) return data;
|
|
331
|
-
const key = await readKey(stage, this.project);
|
|
332
|
-
if (!key) throw new MissingSecretsKey(stage, this.project.key, getKeyPath(stage, this.project));
|
|
333
|
-
return decrypt(data, key);
|
|
334
|
-
}
|
|
335
|
-
async write(stage, secrets) {
|
|
336
|
-
await mkdir(join(this.root, SECRETS_DIR), { recursive: true });
|
|
337
|
-
const key = await getOrCreateKey(stage, this.project);
|
|
338
|
-
await writeFile(this.path(stage), JSON.stringify(encrypt(secrets, key), null, 2), "utf-8");
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
/** A stage's secrets file exists, and the key that opens it does not. */
|
|
342
|
-
var MissingSecretsKey = class extends Error {
|
|
343
|
-
stage;
|
|
344
|
-
project;
|
|
345
|
-
path;
|
|
346
|
-
constructor(stage, project, path) {
|
|
347
|
-
super(`Decryption key not found for stage "${stage}". Expected key at: ${path}. Copy the stage's key there from whoever created it, or set secrets.store to a store this machine can reach.`);
|
|
348
|
-
this.stage = stage;
|
|
349
|
-
this.project = project;
|
|
350
|
-
this.path = path;
|
|
351
|
-
this.name = "MissingSecretsKey";
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
/** A project's package name without its scope, as a workspace name reads it. */
|
|
355
|
-
function packageName(root) {
|
|
356
|
-
try {
|
|
357
|
-
const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf-8"));
|
|
358
|
-
return typeof pkg.name === "string" ? pkg.name.replace(/^@[^/]+\//, "") : void 0;
|
|
359
|
-
} catch {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
function encrypt(secrets, keyHex) {
|
|
364
|
-
const key = Buffer.from(keyHex, "hex");
|
|
365
|
-
const iv = randomBytes(IV_LENGTH);
|
|
366
|
-
const cipher = createCipheriv(ALGORITHM, key, iv);
|
|
367
|
-
const ciphertext = Buffer.concat([cipher.update(JSON.stringify(secrets), "utf-8"), cipher.final()]);
|
|
368
|
-
return {
|
|
369
|
-
version: 1,
|
|
370
|
-
encrypted: Buffer.concat([ciphertext, cipher.getAuthTag()]).toString("base64"),
|
|
371
|
-
iv: iv.toString("hex")
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
function decrypt(data, keyHex) {
|
|
375
|
-
const key = Buffer.from(keyHex, "hex");
|
|
376
|
-
const combined = Buffer.from(data.encrypted, "base64");
|
|
377
|
-
const decipher = createDecipheriv(ALGORITHM, key, Buffer.from(data.iv, "hex"));
|
|
378
|
-
decipher.setAuthTag(combined.subarray(-16));
|
|
379
|
-
const plaintext = Buffer.concat([decipher.update(combined.subarray(0, -16)), decipher.final()]);
|
|
380
|
-
return JSON.parse(plaintext.toString("utf-8"));
|
|
381
|
-
}
|
|
382
|
-
//#endregion
|
|
383
|
-
//#region src/secrets/store.ts
|
|
384
|
-
var store_exports = /* @__PURE__ */ __exportAll({
|
|
385
|
-
FileSecretsStore: () => FileSecretsStore,
|
|
386
|
-
isRemoteStore: () => isRemoteStore,
|
|
387
|
-
secretsStoreFor: () => secretsStoreFor
|
|
388
|
-
});
|
|
389
|
-
/**
|
|
390
|
-
* The store a stage's secrets are kept in — what every command reads and
|
|
391
|
-
* writes, for the stage it acts on.
|
|
392
|
-
*
|
|
393
|
-
* The local stage is always the file: its secrets belong to the machine
|
|
394
|
-
* running `gkm dev`. A deployed stage uses `secrets.store`.
|
|
395
|
-
*/
|
|
396
|
-
async function secretsStoreFor(workspace, stage, options = {}) {
|
|
397
|
-
const configured = workspace.secrets.store ?? "file";
|
|
398
|
-
if (stage === workspace.stages.local || configured === "file") return new FileSecretsStore(workspace.root, keystoreProject(workspace, options.home));
|
|
399
|
-
if (configured.provider !== "ssm") return configured.provider;
|
|
400
|
-
const { AwsSecretsStore } = await import("./aws-DojzQnLd.mjs");
|
|
401
|
-
return new AwsSecretsStore({
|
|
402
|
-
project: workspace.name,
|
|
403
|
-
region: configured.region,
|
|
404
|
-
...options.profile ? { profile: options.profile } : {}
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
/** Whether a stage's secrets live somewhere other than this machine. */
|
|
408
|
-
function isRemoteStore(workspace, stage) {
|
|
409
|
-
const configured = workspace.secrets.store ?? "file";
|
|
410
|
-
return stage !== workspace.stages.local && configured !== "file";
|
|
411
|
-
}
|
|
412
|
-
//#endregion
|
|
413
|
-
export { getKeyPath as a, readKey as c, imageName as d, imageRef as f, projectName as g, ownershipMarker as h, FileSecretsStore as i, applicationName as l, markerOf as m, secretsStoreFor as n, keystoreProject as o, isOwnedBy as p, store_exports as r, projectKey as s, isRemoteStore as t, deployIdentity as u };
|
|
414
|
-
|
|
415
|
-
//# sourceMappingURL=store-CLiHnqOY.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"store-CLiHnqOY.mjs","names":[],"sources":["../src/deploy/identity.ts","../src/secrets/keystore.ts","../src/secrets/file.ts","../src/secrets/store.ts"],"sourcesContent":["/**\n * Who a deploy is, on a target it shares with other workspaces.\n *\n * A Dokploy server is one flat namespace of projects, images and Docker\n * service names. Deploys used to identify everything they made by the\n * workspace name alone, so two workspaces called `shop` — or `Shop` and\n * `shop` — deploying to one server landed in the same project, pushed over\n * each other's images and redeployed each other's applications. A\n * `DeployIdentity` is what a deploy names and claims its resources by instead.\n */\n\nimport { kebabCase, scopedName } from '@geekmidas/manifest';\n\nexport interface DeployIdentity {\n\t/**\n\t * Whose deploy this is — an organisation, a team, a repository. Two\n\t * workspaces with the same name are told apart by it. Defaults to the\n\t * kebab-cased workspace name; set `deploy.namespace` to choose one.\n\t */\n\treadonly namespace: string;\n\t/** The workspace name as the target spells it: lowercase, `[a-z0-9-]`. */\n\treadonly project: string;\n\treadonly stage: string;\n\t/**\n\t * `<namespace>/<project>`: what a Dokploy project is claimed by.\n\t *\n\t * The stage is not part of it because one Dokploy project holds every stage\n\t * of a workspace, each as an environment — the key has to be the same from\n\t * every stage that deploys into it.\n\t */\n\treadonly key: string;\n\t/**\n\t * The prefix every name on the target carries: the project alone in the\n\t * default namespace, `<namespace>-<project>` in any other.\n\t *\n\t * The default adds nothing so that a workspace that never set a namespace\n\t * keeps the names it was already deployed under — a Postgres is found by\n\t * its name, and a new name would be a new, empty database.\n\t */\n\treadonly scope: string;\n}\n\n/** A namespace that cannot be part of a Dokploy name or an image path. */\nexport class DeployNamespaceInvalid extends Error {\n\tconstructor(readonly namespace: string) {\n\t\tsuper(\n\t\t\t`deploy.namespace '${namespace}' is not usable as a name: use lowercase letters, digits and single '-' between them (e.g. 'acme' or 'acme-platform') in gkm.config.ts.`,\n\t\t);\n\t\tthis.name = 'DeployNamespaceInvalid';\n\t}\n}\n\n/** A workspace name with nothing a target can name a resource by. */\nexport class DeployProjectNameInvalid extends Error {\n\tconstructor(readonly workspaceName: string) {\n\t\tsuper(\n\t\t\t`The workspace name '${workspaceName}' has no letters or digits to name deployed resources by. Set \"name\" in gkm.config.ts.`,\n\t\t);\n\t\tthis.name = 'DeployProjectNameInvalid';\n\t}\n}\n\nconst NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * Lowercase `[a-z0-9-]`, runs of anything else collapsed to one `-`: valid as\n * a Docker image path component, a Docker service name and a Dokploy name.\n */\nfunction slug(value: string): string {\n\treturn value\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t.replace(/^-+|-+$/g, '');\n}\n\n/** The namespace a workspace is in when it does not name one. */\nexport function defaultNamespace(workspaceName: string): string {\n\treturn slug(kebabCase(workspaceName));\n}\n\n/** What an identity is derived from: a workspace's name and namespace. */\nexport interface IdentitySource {\n\tname: string;\n\tdeploy?: { namespace?: string };\n}\n\nexport function deployIdentity(\n\tworkspace: IdentitySource,\n\tstage: string,\n): DeployIdentity {\n\t// Lowercased rather than kebab-cased: the names a workspace was already\n\t// deployed under were `${stage}-${name}`.toLowerCase(), and this has to\n\t// reproduce them for the databases it finds by name.\n\tconst project = slug(workspace.name);\n\tif (!project) throw new DeployProjectNameInvalid(workspace.name);\n\n\tconst fallback = defaultNamespace(workspace.name);\n\tconst namespace = workspace.deploy?.namespace ?? fallback;\n\tif (!NAME.test(namespace)) throw new DeployNamespaceInvalid(namespace);\n\n\treturn {\n\t\tnamespace,\n\t\tproject,\n\t\tstage,\n\t\tkey: `${namespace}/${project}`,\n\t\tscope: namespace === fallback ? project : `${namespace}-${project}`,\n\t};\n}\n\n/**\n * The token a Dokploy project's description carries to say which identity\n * created it. A project with the right name and no marker belongs to someone\n * else, and a deploy never adopts it.\n */\nexport function ownershipMarker(identity: DeployIdentity): string {\n\treturn `gkm:${identity.key}`;\n}\n\n/** Whether a project description carries `identity`'s marker. */\nexport function isOwnedBy(\n\tdescription: string | null | undefined,\n\tidentity: DeployIdentity,\n): boolean {\n\tconst marker = ownershipMarker(identity);\n\treturn (description ?? '').split(/\\s+/).includes(marker);\n}\n\n/** Any `gkm:` marker a description carries — whoever's it is. */\nexport function markerOf(\n\tdescription: string | null | undefined,\n): string | undefined {\n\treturn (description ?? '').split(/\\s+/).find((t) => t.startsWith('gkm:'));\n}\n\n/** What the Dokploy project is called. */\nexport function projectName(identity: DeployIdentity): string {\n\treturn identity.scope;\n}\n\n/**\n * The repository an app's image is pushed to, below the registry:\n * `<namespace>/<project>-<app>`.\n *\n * The namespace is a path segment of its own so that a registry that maps\n * paths to owners (an organisation's packages, a Harbor project) can grant it\n * as one; the app is joined to the project so the two read as one name.\n */\nexport function imageName(identity: DeployIdentity, app: string): string {\n\treturn `${identity.namespace}/${identity.project}-${slug(kebabCase(app))}`;\n}\n\n/** The full image ref an app is built, pushed and deployed as. */\nexport function imageRef(\n\tidentity: DeployIdentity,\n\tapp: string,\n\tregistry: string | undefined,\n\ttag: string,\n): string {\n\tconst name = imageName(identity, app);\n\treturn registry\n\t\t? `${registry.replace(/\\/+$/, '')}/${name}:${tag}`\n\t\t: `${name}:${tag}`;\n}\n\n/**\n * What one application is called on the target.\n *\n * The same rule the constructs use, through the same `scopedName`, scoped by\n * the stage and the identity: the application beside\n * `production-kitchen-sink-database` is `production-kitchen-sink-api`. A\n * Dokploy application's name is also its Docker service name, which is unique\n * on the whole server, so the namespace has to be in it too — `acme`'s and\n * `globex`'s `shop` would otherwise both run `production-shop-api`.\n *\n * The app id is dropped when it repeats the project, so a project named for its\n * one application is `production-kitchen-sink` rather than\n * `production-kitchen-sink-kitchen-sink`.\n */\nexport function applicationName(identity: DeployIdentity, app: string): string {\n\treturn slug(app) === identity.project\n\t\t? `${identity.stage}-${identity.scope}`\n\t\t: scopedName([identity.stage, identity.scope], app);\n}\n","/**\n * The keys that open each stage's encrypted secrets file, kept outside the\n * project in the CLI's home.\n *\n * Keys were kept at `~/.gkm/<folder>/<stage>.key`, named after the project\n * directory — so two checkouts in folders with the same name (`~/work/api`\n * and `~/oss/api`) shared one key, and the second `gkm secrets:init`\n * overwrote the first project's. They are now kept under the project's\n * identity, `<home>/keys/<namespace>/<project>/<stage>.key`, the same\n * `<namespace>/<project>` a deploy claims its Dokploy project by. A key still\n * at the old place is copied to the new one the first time it is read; the\n * old file stays, so an older CLI on the same machine keeps working.\n */\n\nimport { randomBytes } from 'node:crypto';\nimport { constants, existsSync } from 'node:fs';\nimport {\n\tchmod,\n\tcopyFile,\n\tmkdir,\n\treadFile,\n\trm,\n\twriteFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { deployIdentity, type IdentitySource } from '../deploy/identity';\nimport { gkmHome } from '../home';\n\n/** Key length for AES-256 encryption */\nconst KEY_LENGTH = 32; // 256 bits\n\n/** Whose keys, and where the CLI's home is. */\nexport interface KeystoreProject {\n\t/** `<namespace>/<project>`, as `DeployIdentity.key` spells it. */\n\tkey: string;\n\t/**\n\t * The names the project's keys were kept under, `~/.gkm/<name>/`, before\n\t * they were keyed by identity: its directory's basename, which the secrets\n\t * file used, and its workspace name, which `gkm init` and the generated CI\n\t * workflow wrote to. Looked in, and copied from, when the project has no\n\t * key of its own yet.\n\t */\n\tlegacy?: readonly string[];\n\t/**\n\t * Other project keys this project's keys may be copied from when it has\n\t * none: the default namespace's, for a workspace that has just set\n\t * `deploy.namespace` — the secrets file in the checkout is the same one.\n\t */\n\taliases?: string[];\n\t/** The CLI's home. Defaults to `GKM_HOME`, else `~/.gkm`. */\n\thome?: string;\n}\n\n/** A keystore project key that is not `<namespace>/<project>`. */\nexport class KeystoreProjectInvalid extends Error {\n\tconstructor(readonly key: string) {\n\t\tsuper(\n\t\t\t`'${key}' is not a keystore project: expected '<namespace>/<project>' in lowercase letters, digits and '-', as deployIdentity() produces. Pass the workspace's identity key.`,\n\t\t);\n\t\tthis.name = 'KeystoreProjectInvalid';\n\t}\n}\n\n/** A stage has no key, here or at the place keys used to be kept. */\nexport class KeyNotFound extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly project: string,\n\t\treadonly path: string,\n\t) {\n\t\tsuper(\n\t\t\t`Encryption key not found for stage \"${stage}\" in project \"${project}\". ` +\n\t\t\t\t`Expected key at: ${path}. Run \\`gkm secrets:init --stage ${stage}\\` to create one, or copy the stage's key there.`,\n\t\t);\n\t\tthis.name = 'KeyNotFound';\n\t}\n}\n\nconst PROJECT_KEY = /^[a-z0-9]+(?:-[a-z0-9]+)*\\/[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * The keystore key of a workspace: its deploy identity without the stage, so\n * a project's keys and its Dokploy project are named by the same thing.\n */\nexport function projectKey(workspace: IdentitySource): string {\n\treturn deployIdentity(workspace, '').key;\n}\n\n/** The keystore project of a loaded workspace. */\nexport function keystoreProject(\n\tworkspace: IdentitySource & { root: string },\n\thome?: string,\n): KeystoreProject {\n\tconst key = projectKey(workspace);\n\tconst unscoped = projectKey({ name: workspace.name });\n\tconst legacy = [\n\t\t...new Set([folderName(workspace.root), workspace.name]),\n\t].filter((name): name is string => !!name);\n\treturn {\n\t\tkey,\n\t\tlegacy,\n\t\t...(unscoped !== key ? { aliases: [unscoped] } : {}),\n\t\t...(home ? { home } : {}),\n\t};\n}\n\n/**\n * Whether `name` stays inside the home it is joined to. A workspace name was\n * joined as is — `@acme/shop` is `~/.gkm/@acme/shop` — but never upwards.\n */\nfunction isLegacyName(name: string): boolean {\n\treturn name.split(/[\\\\/]/).every((part) => part !== '..' && part !== '');\n}\n\n/** The last segment of a path, as the old keystore named projects. */\nfunction folderName(path: string): string | undefined {\n\tconst name = path.split(/[\\\\/]/).filter(Boolean).at(-1);\n\treturn name && name !== '.' && name !== '..' ? name : undefined;\n}\n\n/**\n * Get the keystore directory for a project:\n * `<home>/keys/<namespace>/<project>`.\n */\nexport function getKeystoreDir(project: KeystoreProject): string {\n\tif (!PROJECT_KEY.test(project.key)) {\n\t\tthrow new KeystoreProjectInvalid(project.key);\n\t}\n\tconst [namespace, name] = project.key.split('/') as [string, string];\n\treturn join(project.home ?? gkmHome(), 'keys', namespace, name);\n}\n\n/** Get the path to a stage's encryption key. */\nexport function getKeyPath(stage: string, project: KeystoreProject): string {\n\treturn join(getKeystoreDir(project), `${stage}.key`);\n}\n\n/**\n * Where else a stage's key may be, most likely first: under an alias of the\n * project, then where keys were kept before they were keyed by identity —\n * under the configured home, then under `~/.gkm`, which is where every CLI\n * before `GKM_HOME` wrote.\n */\nfunction legacyKeyPaths(stage: string, project: KeystoreProject): string[] {\n\tconst aliases = (project.aliases ?? []).map((key) =>\n\t\tgetKeyPath(stage, { key, home: project.home }),\n\t);\n\tconst homes = [\n\t\t...new Set([project.home ?? gkmHome(), join(homedir(), '.gkm')]),\n\t];\n\tconst names = (project.legacy ?? []).filter(isLegacyName);\n\treturn [\n\t\t...aliases,\n\t\t...names.flatMap((name) =>\n\t\t\thomes.map((home) => join(home, name, `${stage}.key`)),\n\t\t),\n\t];\n}\n\n/** Check if a key exists for a stage, here or where it used to be kept. */\nexport function keyExists(stage: string, project: KeystoreProject): boolean {\n\treturn (\n\t\texistsSync(getKeyPath(stage, project)) ||\n\t\tlegacyKeyPaths(stage, project).some((path) => existsSync(path))\n\t);\n}\n\n/**\n * Generate a new encryption key for a stage, stored owner-only at\n * `<home>/keys/<namespace>/<project>/<stage>.key`.\n */\nexport async function generateKey(\n\tstage: string,\n\tproject: KeystoreProject,\n\t/** `wx` to refuse replacing a key that is already there. */\n\tflag: 'w' | 'wx' = 'w',\n): Promise<string> {\n\tconst keyDir = getKeystoreDir(project);\n\tconst keyPath = getKeyPath(stage, project);\n\n\t// Ensure keystore directory exists with restricted permissions\n\tawait mkdir(keyDir, { recursive: true, mode: 0o700 });\n\n\t// Generate random key\n\tconst key = randomBytes(KEY_LENGTH).toString('hex');\n\n\t// Write key with restricted permissions (owner read/write only)\n\tawait writeFile(keyPath, key, { mode: 0o600, encoding: 'utf-8', flag });\n\n\t// Ensure permissions are set correctly (in case file existed)\n\tawait chmod(keyPath, 0o600);\n\n\treturn key;\n}\n\n/**\n * Copy a key from where it used to be kept to where it is kept now.\n *\n * Copied, not moved: an older CLI on the same machine still reads the old\n * place. Exclusive, so two processes migrating at once cannot overwrite a key\n * one of them already put there.\n */\nasync function migrateLegacyKey(\n\tstage: string,\n\tproject: KeystoreProject,\n): Promise<boolean> {\n\tconst legacy = legacyKeyPaths(stage, project).find((path) =>\n\t\texistsSync(path),\n\t);\n\tif (!legacy) return false;\n\n\tconst keyPath = getKeyPath(stage, project);\n\tawait mkdir(getKeystoreDir(project), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait copyFile(legacy, keyPath, constants.COPYFILE_EXCL);\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n\t}\n\tawait chmod(keyPath, 0o600);\n\treturn true;\n}\n\n/**\n * Read an encryption key for a stage, or `null` when there is none — after\n * copying it over from where keys used to be kept, if it is still there.\n */\nexport async function readKey(\n\tstage: string,\n\tproject: KeystoreProject,\n): Promise<string | null> {\n\tconst keyPath = getKeyPath(stage, project);\n\n\tif (!existsSync(keyPath) && !(await migrateLegacyKey(stage, project))) {\n\t\treturn null;\n\t}\n\n\tconst key = await readFile(keyPath, 'utf-8');\n\treturn key.trim();\n}\n\n/**\n * Read an encryption key for a stage, throwing {@link KeyNotFound} if there\n * is none.\n */\nexport async function requireKey(\n\tstage: string,\n\tproject: KeystoreProject,\n): Promise<string> {\n\tconst key = await readKey(stage, project);\n\n\tif (!key) {\n\t\tthrow new KeyNotFound(stage, project.key, getKeyPath(stage, project));\n\t}\n\n\treturn key;\n}\n\n/**\n * Delete a key for a stage. Only the current one: a key left at the old place\n * belongs to whichever older CLI still reads it.\n */\nexport async function deleteKey(\n\tstage: string,\n\tproject: KeystoreProject,\n): Promise<void> {\n\tawait rm(getKeyPath(stage, project), { force: true });\n}\n\n/**\n * Get or create a key for a stage.\n * If the key already exists, it is returned. Otherwise, a new key is generated.\n */\nexport async function getOrCreateKey(\n\tstage: string,\n\tproject: KeystoreProject,\n): Promise<string> {\n\tconst existingKey = await readKey(stage, project);\n\n\tif (existingKey) {\n\t\treturn existingKey;\n\t}\n\n\t// Exclusively: a key is now shared by every checkout of one project, and\n\t// two commands creating it at once must not each keep a different one.\n\ttry {\n\t\treturn await generateKey(stage, project, 'wx');\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n\t\treturn requireKey(stage, project);\n\t}\n}\n","import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, join } from 'node:path';\nimport {\n\tgetKeyPath,\n\tgetOrCreateKey,\n\ttype KeystoreProject,\n\tprojectKey,\n\treadKey,\n} from './keystore';\nimport type { SecretsStore } from './store.js';\nimport type { StageSecrets } from './types';\n\n/** Where the files live, relative to the project root. */\nconst SECRETS_DIR = '.gkm/secrets';\n\n/** AES-256-GCM configuration */\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 12; // 96 bits for GCM\nconst AUTH_TAG_LENGTH = 16; // 128 bits\n\n/** Encrypted secrets file structure */\ninterface EncryptedSecretsFile {\n\t/** Version for future format changes */\n\tversion: 1;\n\t/** Base64 encoded encrypted data (ciphertext + auth tag) */\n\tencrypted: string;\n\t/** Hex encoded IV */\n\tiv: string;\n}\n\n/**\n * A stage's secrets as an encrypted file on this machine:\n * `.gkm/secrets/<stage>.json`, with its key in the CLI's home at\n * `keys/<namespace>/<project>/<stage>.key`.\n *\n * The local stage's store, always — its secrets belong to the machine running\n * `gkm dev` — and a deployed stage's when `secrets.store` is `'file'`.\n */\nexport class FileSecretsStore implements SecretsStore {\n\treadonly name = 'file';\n\tprivate readonly project: KeystoreProject;\n\n\t/**\n\t * @param project - Whose keys: a workspace's `keystoreProject()`. Outside a\n\t * workspace there is no config to name the project, so it is named the\n\t * way a workspace without `name` is — its package name, else its folder.\n\t */\n\tconstructor(\n\t\tprivate readonly root: string,\n\t\tproject?: KeystoreProject,\n\t) {\n\t\tthis.project = project ?? {\n\t\t\tkey: projectKey({ name: packageName(root) ?? basename(root) }),\n\t\t\tlegacy: [basename(root)],\n\t\t};\n\t}\n\n\t/** The file a stage's secrets are kept in. */\n\tpath(stage: string): string {\n\t\treturn join(this.root, SECRETS_DIR, `${stage}.json`);\n\t}\n\n\tasync read(stage: string): Promise<StageSecrets | null> {\n\t\tconst path = this.path(stage);\n\t\tif (!existsSync(path)) return null;\n\n\t\tconst data = JSON.parse(await readFile(path, 'utf-8'));\n\n\t\t// Legacy: unencrypted format (for backwards compatibility)\n\t\tif (!(data.version === 1 && data.encrypted && data.iv)) {\n\t\t\treturn data as StageSecrets;\n\t\t}\n\n\t\tconst key = await readKey(stage, this.project);\n\t\tif (!key) {\n\t\t\tthrow new MissingSecretsKey(\n\t\t\t\tstage,\n\t\t\t\tthis.project.key,\n\t\t\t\tgetKeyPath(stage, this.project),\n\t\t\t);\n\t\t}\n\n\t\treturn decrypt(data as EncryptedSecretsFile, key);\n\t}\n\n\tasync write(stage: string, secrets: StageSecrets): Promise<void> {\n\t\tawait mkdir(join(this.root, SECRETS_DIR), { recursive: true });\n\n\t\tconst key = await getOrCreateKey(stage, this.project);\n\t\tawait writeFile(\n\t\t\tthis.path(stage),\n\t\t\tJSON.stringify(encrypt(secrets, key), null, 2),\n\t\t\t'utf-8',\n\t\t);\n\t}\n}\n\n/** A stage's secrets file exists, and the key that opens it does not. */\nexport class MissingSecretsKey extends Error {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly project: string,\n\t\treadonly path: string,\n\t) {\n\t\tsuper(\n\t\t\t`Decryption key not found for stage \"${stage}\". ` +\n\t\t\t\t`Expected key at: ${path}. Copy the stage's key there from whoever created it, or set secrets.store to a store this machine can reach.`,\n\t\t);\n\t\tthis.name = 'MissingSecretsKey';\n\t}\n}\n\n/** A project's package name without its scope, as a workspace name reads it. */\nfunction packageName(root: string): string | undefined {\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8'));\n\t\treturn typeof pkg.name === 'string'\n\t\t\t? pkg.name.replace(/^@[^/]+\\//, '')\n\t\t\t: undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction encrypt(secrets: StageSecrets, keyHex: string): EncryptedSecretsFile {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst iv = randomBytes(IV_LENGTH);\n\n\tconst cipher = createCipheriv(ALGORITHM, key, iv);\n\tconst ciphertext = Buffer.concat([\n\t\tcipher.update(JSON.stringify(secrets), 'utf-8'),\n\t\tcipher.final(),\n\t]);\n\n\t// Ciphertext and auth tag, together\n\tconst combined = Buffer.concat([ciphertext, cipher.getAuthTag()]);\n\n\treturn {\n\t\tversion: 1,\n\t\tencrypted: combined.toString('base64'),\n\t\tiv: iv.toString('hex'),\n\t};\n}\n\nfunction decrypt(data: EncryptedSecretsFile, keyHex: string): StageSecrets {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst combined = Buffer.from(data.encrypted, 'base64');\n\n\tconst decipher = createDecipheriv(\n\t\tALGORITHM,\n\t\tkey,\n\t\tBuffer.from(data.iv, 'hex'),\n\t);\n\tdecipher.setAuthTag(combined.subarray(-AUTH_TAG_LENGTH));\n\n\tconst plaintext = Buffer.concat([\n\t\tdecipher.update(combined.subarray(0, -AUTH_TAG_LENGTH)),\n\t\tdecipher.final(),\n\t]);\n\n\treturn JSON.parse(plaintext.toString('utf-8')) as StageSecrets;\n}\n","/**\n * Where a stage's secrets are kept.\n *\n * Every command that needs a stage's secrets — `gkm dev`, `test`, `deploy`,\n * `build`, `setup`, `secrets:*` — asks {@link secretsStoreFor} for that\n * stage's store and reads or writes it. None of them knows whether that is a\n * file on this machine or SSM in the stage's account: the local stage is\n * always the file, and a deployed stage is whatever `secrets.store` names.\n *\n * The same shape deploy state has (`StateProvider`): one interface, a backend\n * per place, and a custom object for any place this package does not ship.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { FileSecretsStore } from './file.js';\nimport { keystoreProject } from './keystore.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface SecretsStore {\n\t/** Which kind of store this is — `'file'`, `'ssm'`, or a custom one's. */\n\treadonly name: string;\n\t/** The stage's secrets, or null when it has none here. */\n\tread(stage: string): Promise<StageSecrets | null>;\n\t/** Replace the stage's secrets. */\n\twrite(stage: string, secrets: StageSecrets): Promise<void>;\n}\n\n/** Secrets in SSM Parameter Store, in the account of the active credentials. */\nexport interface SsmSecretsStoreConfig {\n\tprovider: 'ssm';\n\tregion: string;\n}\n\n/** Any other backend: an object implementing {@link SecretsStore}. */\nexport interface CustomSecretsStoreConfig {\n\tprovider: SecretsStore;\n}\n\n/**\n * `secrets.store` in gkm.config.ts.\n *\n * `'file'` — the default — keeps each stage's secrets in the encrypted\n * `.gkm/secrets/<stage>.json`, its key in the CLI's home (`GKM_HOME`, else\n * `~/.gkm`). It cannot serve a\n * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.\n */\nexport type SecretsStoreConfig =\n\t| 'file'\n\t| SsmSecretsStoreConfig\n\t| CustomSecretsStoreConfig;\n\nexport interface SecretsStoreOptions {\n\t/**\n\t * The AWS profile for the stage's account. Only that profile is used —\n\t * never AWS_* from the environment — so a store cannot quietly land in\n\t * whichever account the shell was exported for.\n\t */\n\tprofile?: string;\n\t/**\n\t * The CLI's home, where a file store's keys are kept. Defaults to\n\t * `GKM_HOME`, else `~/.gkm`.\n\t */\n\thome?: string;\n}\n\n/**\n * The store a stage's secrets are kept in — what every command reads and\n * writes, for the stage it acts on.\n *\n * The local stage is always the file: its secrets belong to the machine\n * running `gkm dev`. A deployed stage uses `secrets.store`.\n */\nexport async function secretsStoreFor(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<SecretsStore> {\n\tconst configured = workspace.secrets.store ?? 'file';\n\n\tif (stage === workspace.stages.local || configured === 'file') {\n\t\treturn new FileSecretsStore(\n\t\t\tworkspace.root,\n\t\t\tkeystoreProject(workspace, options.home),\n\t\t);\n\t}\n\n\tif (configured.provider !== 'ssm') {\n\t\treturn configured.provider;\n\t}\n\n\tconst { AwsSecretsStore } = await import('./aws.js');\n\treturn new AwsSecretsStore({\n\t\tproject: workspace.name,\n\t\tregion: configured.region,\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n}\n\n/** Whether a stage's secrets live somewhere other than this machine. */\nexport function isRemoteStore(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): boolean {\n\tconst configured = workspace.secrets.store ?? 'file';\n\treturn stage !== workspace.stages.local && configured !== 'file';\n}\n\nexport { FileSecretsStore };\n"],"mappings":"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"}
|