@geekmidas/cli 10.0.0-alpha.10 → 10.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/dist/{CachedStateProvider-CqPdW7CB.cjs → CachedStateProvider-BEBl2wQT.cjs} +1 -1
  2. package/dist/{CachedStateProvider-CqPdW7CB.cjs.map → CachedStateProvider-BEBl2wQT.cjs.map} +1 -1
  3. package/dist/CachedStateProvider-C-8wtlto.mjs +3 -0
  4. package/dist/{CachedStateProvider-DTniehMP.mjs → CachedStateProvider-Co4n5Wvs.mjs} +1 -1
  5. package/dist/{CachedStateProvider-DTniehMP.mjs.map → CachedStateProvider-Co4n5Wvs.mjs.map} +1 -1
  6. package/dist/{CachedStateProvider-BLo7zv7S.cjs → CachedStateProvider-DqhGpzsm.cjs} +1 -1
  7. package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-BOvsgATO.cjs} +1 -1
  8. package/dist/{HostingerProvider-h3EbEp-Z.cjs.map → HostingerProvider-BOvsgATO.cjs.map} +1 -1
  9. package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-BcyFRbqV.mjs} +1 -1
  10. package/dist/{HostingerProvider-Cft7lEYr.mjs.map → HostingerProvider-BcyFRbqV.mjs.map} +1 -1
  11. package/dist/{LocalStateProvider-Bn6tS-oL.mjs → LocalStateProvider-Bq-0C0sg.mjs} +1 -1
  12. package/dist/{LocalStateProvider-Bn6tS-oL.mjs.map → LocalStateProvider-Bq-0C0sg.mjs.map} +1 -1
  13. package/dist/{LocalStateProvider-Db9SEjyH.cjs → LocalStateProvider-yjXZXZDM.cjs} +1 -1
  14. package/dist/{LocalStateProvider-Db9SEjyH.cjs.map → LocalStateProvider-yjXZXZDM.cjs.map} +1 -1
  15. package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-C0xmer8U.mjs} +1 -1
  16. package/dist/{Route53Provider-RdpUDJQk.mjs.map → Route53Provider-C0xmer8U.mjs.map} +1 -1
  17. package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-CH9XH7bz.cjs} +1 -1
  18. package/dist/{Route53Provider-di2fMM5N.cjs.map → Route53Provider-CH9XH7bz.cjs.map} +1 -1
  19. package/dist/{SSMStateProvider-DyX8s9HI.cjs → SSMStateProvider-CXtNgPfy.cjs} +1 -1
  20. package/dist/{SSMStateProvider-DyX8s9HI.cjs.map → SSMStateProvider-CXtNgPfy.cjs.map} +1 -1
  21. package/dist/{SSMStateProvider-BRt2py-A.mjs → SSMStateProvider-DOZifZfE.mjs} +1 -1
  22. package/dist/{SSMStateProvider-BRt2py-A.mjs.map → SSMStateProvider-DOZifZfE.mjs.map} +1 -1
  23. package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-DXXg6FIL.mjs} +1 -1
  24. package/dist/{backup-provisioner-Bgj5Vi_L.mjs.map → backup-provisioner-DXXg6FIL.mjs.map} +1 -1
  25. package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-cE2PuCTb.cjs} +1 -1
  26. package/dist/{backup-provisioner-BrsHwYG0.cjs.map → backup-provisioner-cE2PuCTb.cjs.map} +1 -1
  27. package/dist/{bundler-BY1OmV1e.cjs → bundler-CaTGtiaO.cjs} +3 -3
  28. package/dist/{bundler-BY1OmV1e.cjs.map → bundler-CaTGtiaO.cjs.map} +1 -1
  29. package/dist/{bundler-Be8Zg4Wd.mjs → bundler-S3ek7ea-.mjs} +3 -3
  30. package/dist/{bundler-Be8Zg4Wd.mjs.map → bundler-S3ek7ea-.mjs.map} +1 -1
  31. package/dist/{config-DVVph9yz.mjs → config-B0L2tvvC.mjs} +2 -2
  32. package/dist/{config-DVVph9yz.mjs.map → config-B0L2tvvC.mjs.map} +1 -1
  33. package/dist/{config-BZ1F0WuA.cjs → config-HhDpJmLO.cjs} +2 -2
  34. package/dist/{config-BZ1F0WuA.cjs.map → config-HhDpJmLO.cjs.map} +1 -1
  35. package/dist/config.cjs +2 -2
  36. package/dist/config.d.mts +2 -2
  37. package/dist/config.mjs +2 -2
  38. package/dist/{credentials-Xamqe_NJ.cjs → credentials-B2Pyh3bJ.cjs} +5 -5
  39. package/dist/{credentials-Xamqe_NJ.cjs.map → credentials-B2Pyh3bJ.cjs.map} +1 -1
  40. package/dist/{credentials-dP8vRVRv.mjs → credentials-CZrys9C5.mjs} +5 -5
  41. package/dist/{credentials-dP8vRVRv.mjs.map → credentials-CZrys9C5.mjs.map} +1 -1
  42. package/dist/dokploy-api-5cK2InBm.cjs +3 -0
  43. package/dist/dokploy-api-D2Ny4Jrc.mjs +3 -0
  44. package/dist/{dokploy-api-aTFPz7_4.mjs → dokploy-api-pdaBryoY.mjs} +1 -1
  45. package/dist/{dokploy-api-aTFPz7_4.mjs.map → dokploy-api-pdaBryoY.mjs.map} +1 -1
  46. package/dist/{dokploy-api-BsVH0y_s.cjs → dokploy-api-zqKe9lHH.cjs} +1 -1
  47. package/dist/{dokploy-api-BsVH0y_s.cjs.map → dokploy-api-zqKe9lHH.cjs.map} +1 -1
  48. package/dist/{emit-CiZS_5Ub.mjs → emit-CIlvnDvV.mjs} +1 -1
  49. package/dist/{emit-CiZS_5Ub.mjs.map → emit-CIlvnDvV.mjs.map} +1 -1
  50. package/dist/{emit-CbcRolsa.cjs → emit-CUD1BWls.cjs} +1 -1
  51. package/dist/{emit-CbcRolsa.cjs.map → emit-CUD1BWls.cjs.map} +1 -1
  52. package/dist/{encryption-C6jaU0hE.cjs → encryption-D2pL3tHH.cjs} +1 -1
  53. package/dist/{encryption-C6jaU0hE.cjs.map → encryption-D2pL3tHH.cjs.map} +1 -1
  54. package/dist/encryption-YDsXXi6w.mjs +3 -0
  55. package/dist/{encryption-D-abyhZx.mjs → encryption-_4zwjOAR.mjs} +1 -1
  56. package/dist/{encryption-D-abyhZx.mjs.map → encryption-_4zwjOAR.mjs.map} +1 -1
  57. package/dist/{encryption-CHzzeR8a.cjs → encryption-sQ2Oxx0d.cjs} +1 -1
  58. package/dist/{fullstack-secrets-C-GGohke.cjs → fullstack-secrets-D6MyHhQP.cjs} +1 -1
  59. package/dist/{fullstack-secrets-C-GGohke.cjs.map → fullstack-secrets-D6MyHhQP.cjs.map} +1 -1
  60. package/dist/{fullstack-secrets-D1o9-G5l.mjs → fullstack-secrets-METaRLS1.mjs} +1 -1
  61. package/dist/{fullstack-secrets-D1o9-G5l.mjs.map → fullstack-secrets-METaRLS1.mjs.map} +1 -1
  62. package/dist/github-BrvaPkFf.cjs +197 -0
  63. package/dist/github-BrvaPkFf.cjs.map +1 -0
  64. package/dist/github-CrpsXSfV.mjs +196 -0
  65. package/dist/github-CrpsXSfV.mjs.map +1 -0
  66. package/dist/{index-C6e-ULLx.d.mts → index-Ayxg_DQY.d.mts} +2 -2
  67. package/dist/{index-C6e-ULLx.d.mts.map → index-Ayxg_DQY.d.mts.map} +1 -1
  68. package/dist/index.cjs +109 -63
  69. package/dist/index.cjs.map +1 -1
  70. package/dist/index.mjs +108 -62
  71. package/dist/index.mjs.map +1 -1
  72. package/dist/keystore-C9rint2E.cjs +101 -0
  73. package/dist/keystore-C9rint2E.cjs.map +1 -0
  74. package/dist/keystore-TxeYM7an.mjs +83 -0
  75. package/dist/keystore-TxeYM7an.mjs.map +1 -0
  76. package/dist/{openapi-SjsgARie.cjs → openapi-DaKlTKxz.cjs} +2 -2
  77. package/dist/{openapi-SjsgARie.cjs.map → openapi-DaKlTKxz.cjs.map} +1 -1
  78. package/dist/{openapi-react-query-CmJ1xDbn.cjs → openapi-react-query-BKH334Ug.cjs} +1 -1
  79. package/dist/{openapi-react-query-CmJ1xDbn.cjs.map → openapi-react-query-BKH334Ug.cjs.map} +1 -1
  80. package/dist/{openapi-react-query-Byltvm_7.mjs → openapi-react-query-DoR5CmzQ.mjs} +1 -1
  81. package/dist/{openapi-react-query-Byltvm_7.mjs.map → openapi-react-query-DoR5CmzQ.mjs.map} +1 -1
  82. package/dist/openapi-react-query.cjs +1 -1
  83. package/dist/openapi-react-query.mjs +1 -1
  84. package/dist/{openapi-Xx0A7Wg0.mjs → openapi-y1d7Kalt.mjs} +2 -2
  85. package/dist/{openapi-Xx0A7Wg0.mjs.map → openapi-y1d7Kalt.mjs.map} +1 -1
  86. package/dist/openapi.cjs +3 -3
  87. package/dist/openapi.d.mts +1 -1
  88. package/dist/openapi.mjs +3 -3
  89. package/dist/reconcile/public.cjs +1 -1
  90. package/dist/reconcile/public.d.mts +1 -1
  91. package/dist/reconcile/public.mjs +1 -1
  92. package/dist/{reconcile-CFs8C_1a.mjs → reconcile-C13ERt1U.mjs} +2 -2
  93. package/dist/{reconcile-CFs8C_1a.mjs.map → reconcile-C13ERt1U.mjs.map} +1 -1
  94. package/dist/{reconcile-D0pimNiy.cjs → reconcile-CYMhFSYy.cjs} +2 -2
  95. package/dist/{reconcile-D0pimNiy.cjs.map → reconcile-CYMhFSYy.cjs.map} +1 -1
  96. package/dist/setup-BWgMs4gg.mjs +12 -0
  97. package/dist/{setup-hC6hI86f.cjs → setup-DOv92pIu.cjs} +6 -6
  98. package/dist/{setup-hC6hI86f.cjs.map → setup-DOv92pIu.cjs.map} +1 -1
  99. package/dist/{setup-C8KOx8_8.mjs → setup-DQo_h_yK.mjs} +6 -6
  100. package/dist/{setup-C8KOx8_8.mjs.map → setup-DQo_h_yK.mjs.map} +1 -1
  101. package/dist/setup-lCx_DcUo.cjs +12 -0
  102. package/dist/{storage-Bi24WfoW.mjs → storage-B020InPW.mjs} +4 -79
  103. package/dist/storage-B020InPW.mjs.map +1 -0
  104. package/dist/{storage-BJmzYxt8.cjs → storage-CSNplaJn.cjs} +4 -85
  105. package/dist/storage-CSNplaJn.cjs.map +1 -0
  106. package/dist/{storage-CYlGwWTj.mjs → storage-DGBzMLZa.mjs} +2 -1
  107. package/dist/{storage-BM9hGpgv.cjs → storage-DlVol_oj.cjs} +2 -1
  108. package/dist/{sync-SizaChmC.cjs → sync-0ILCb-1S.cjs} +2 -2
  109. package/dist/{sync-SizaChmC.cjs.map → sync-0ILCb-1S.cjs.map} +1 -1
  110. package/dist/{sync-CmEuNZsB.mjs → sync-CzHXiR9c.mjs} +2 -2
  111. package/dist/{sync-CmEuNZsB.mjs.map → sync-CzHXiR9c.mjs.map} +1 -1
  112. package/dist/sync-DYENfOt4.cjs +4 -0
  113. package/dist/{sync-hOqAWHR6.mjs → sync-HAau6h_R.mjs} +1 -1
  114. package/dist/{types-6pkp2RRj.d.mts → types-CL7jPFM2.d.mts} +1 -1
  115. package/dist/{types-6pkp2RRj.d.mts.map → types-CL7jPFM2.d.mts.map} +1 -1
  116. package/dist/workspace/index.cjs +1 -1
  117. package/dist/workspace/index.d.mts +2 -2
  118. package/dist/workspace/index.mjs +1 -1
  119. package/dist/{workspace-C6VJQVtG.cjs → workspace-C-a3G_U0.cjs} +30 -4
  120. package/dist/workspace-C-a3G_U0.cjs.map +1 -0
  121. package/dist/workspace-DQxxs7Jk.cjs +10 -0
  122. package/dist/{workspace-zoqRbihZ.mjs → workspace-DaEX7FEg.mjs} +25 -5
  123. package/dist/workspace-DaEX7FEg.mjs.map +1 -0
  124. package/dist/workspace-DbI580qS.mjs +9 -0
  125. package/package.json +11 -11
  126. package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
  127. package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
  128. package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
  129. package/dist/encryption-CQUncu7v.mjs +0 -3
  130. package/dist/setup-BA8ciTPU.mjs +0 -11
  131. package/dist/setup-BsZvZP6f.cjs +0 -11
  132. package/dist/storage-BJmzYxt8.cjs.map +0 -1
  133. package/dist/storage-Bi24WfoW.mjs.map +0 -1
  134. package/dist/sync-D306yOWu.cjs +0 -4
  135. package/dist/workspace-BzrG_2SI.mjs +0 -8
  136. package/dist/workspace-C6VJQVtG.cjs.map +0 -1
  137. package/dist/workspace-XXtNhqJl.cjs +0 -9
  138. package/dist/workspace-zoqRbihZ.mjs.map +0 -1
@@ -0,0 +1,196 @@
1
+ import "./discover-CAlFgYec.mjs";
2
+ import { loadWorkspaceConfig } from "./config-B0L2tvvC.mjs";
3
+ import { assertDeployedStage } from "./workspace-DaEX7FEg.mjs";
4
+ import { getKeyPath } from "./keystore-TxeYM7an.mjs";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { execFileSync } from "node:child_process";
7
+ import { AttachRolePolicyCommand, CreateOpenIDConnectProviderCommand, CreateRoleCommand, GetRoleCommand, IAMClient, ListOpenIDConnectProvidersCommand, UpdateAssumeRolePolicyCommand } from "@aws-sdk/client-iam";
8
+
9
+ //#region src/deploy/github.ts
10
+ const logger = console;
11
+ const GITHUB_OIDC_HOST = "token.actions.githubusercontent.com";
12
+ const AUDIENCE = "sts.amazonaws.com";
13
+ /**
14
+ * What SST needs to create a stack is most of AWS, so this is the default —
15
+ * said out loud in the output, and replaceable with `--policy-arn`.
16
+ */
17
+ const DEFAULT_POLICY_ARN = "arn:aws:iam::aws:policy/AdministratorAccess";
18
+ const defaultGh = (args, input) => execFileSync("gh", args, {
19
+ encoding: "utf-8",
20
+ ...input === void 0 ? {} : {
21
+ input,
22
+ stdio: [
23
+ "pipe",
24
+ "pipe",
25
+ "pipe"
26
+ ]
27
+ }
28
+ }).trim();
29
+ /** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */
30
+ function roleName(project, stage) {
31
+ return `${project}-github-${stage}`.toLowerCase().replace(/[^a-z0-9+=,.@_-]/g, "-").slice(0, 64);
32
+ }
33
+ /**
34
+ * Who may assume the role: a workflow job running in this repository's
35
+ * `<stage>` environment, and nothing else — not another branch's job, not
36
+ * another environment's. That is what keeps a staging deploy from using the
37
+ * production role.
38
+ */
39
+ function trustPolicy(providerArn, repo, stage) {
40
+ return JSON.stringify({
41
+ Version: "2012-10-17",
42
+ Statement: [{
43
+ Effect: "Allow",
44
+ Principal: { Federated: providerArn },
45
+ Action: "sts:AssumeRoleWithWebIdentity",
46
+ Condition: { StringEquals: {
47
+ [`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,
48
+ [`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`
49
+ } }
50
+ }]
51
+ });
52
+ }
53
+ /** GitHub's OIDC provider in this account, created if it is not there. */
54
+ async function ensureOidcProvider(iam) {
55
+ const { OpenIDConnectProviderList = [] } = await iam.send(new ListOpenIDConnectProvidersCommand({}));
56
+ const existing = OpenIDConnectProviderList.find((p) => p.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`))?.Arn;
57
+ if (existing) return existing;
58
+ const created = await iam.send(new CreateOpenIDConnectProviderCommand({
59
+ Url: `https://${GITHUB_OIDC_HOST}`,
60
+ ClientIDList: [AUDIENCE]
61
+ }));
62
+ return created.OpenIDConnectProviderArn;
63
+ }
64
+ /** The deploy role, created or brought back to this trust and policy. */
65
+ async function ensureRole(iam, name, trust, policyArn) {
66
+ let arn;
67
+ try {
68
+ const { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));
69
+ await iam.send(new UpdateAssumeRolePolicyCommand({
70
+ RoleName: name,
71
+ PolicyDocument: trust
72
+ }));
73
+ arn = Role.Arn;
74
+ } catch (error) {
75
+ if (error.name !== "NoSuchEntityException") throw error;
76
+ const { Role } = await iam.send(new CreateRoleCommand({
77
+ RoleName: name,
78
+ AssumeRolePolicyDocument: trust,
79
+ Description: "Deploys from GitHub Actions (gkm deploy:github)",
80
+ MaxSessionDuration: 3600 * 2
81
+ }));
82
+ arn = Role.Arn;
83
+ }
84
+ await iam.send(new AttachRolePolicyCommand({
85
+ RoleName: name,
86
+ PolicyArn: policyArn
87
+ }));
88
+ return arn;
89
+ }
90
+ /**
91
+ * An IAM client for the stage's account.
92
+ *
93
+ * `fromIni` for a named profile, deliberately: it resolves exactly that
94
+ * profile — keys, assume-role, `credential_process` or SSO, whichever
95
+ * ~/.aws/config says — and never falls back to AWS_* in the environment. The
96
+ * provider chain checks the environment first, so exported staging keys would
97
+ * quietly win over `--profile prod` and the production role would land in the
98
+ * staging account.
99
+ */
100
+ async function iamFor(profile, endpoint) {
101
+ const config = { region: "us-east-1" };
102
+ if (profile) {
103
+ const { fromIni } = await import("@aws-sdk/credential-providers");
104
+ config.credentials = fromIni({ profile });
105
+ }
106
+ if (endpoint) {
107
+ config.endpoint = endpoint;
108
+ config.credentials = {
109
+ accessKeyId: "test",
110
+ secretAccessKey: "test"
111
+ };
112
+ }
113
+ return new IAMClient(config);
114
+ }
115
+ async function deployGithubCommand(options, deps = {}) {
116
+ const gh = deps.gh ?? defaultGh;
117
+ const { workspace } = await loadWorkspaceConfig(deps.cwd);
118
+ assertDeployedStage(workspace.stages, options.stage);
119
+ const repo = options.repo ?? gh([
120
+ "repo",
121
+ "view",
122
+ "--json",
123
+ "nameWithOwner",
124
+ "-q",
125
+ ".nameWithOwner"
126
+ ]);
127
+ const role = roleName(workspace.name, options.stage);
128
+ const policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;
129
+ const keyPath = getKeyPath(options.stage, workspace.name);
130
+ const hasKey = existsSync(keyPath);
131
+ logger.log(`\n🔐 GitHub → AWS for stage "${options.stage}"\n`);
132
+ logger.log(` Repository: ${repo}`);
133
+ logger.log(` AWS profile: ${options.profile ?? "(default credentials)"}`);
134
+ logger.log(` Role: ${role}`);
135
+ logger.log(` Policy: ${policyArn}${options.policyArn ? "" : " (default; --policy-arn to narrow)"}`);
136
+ logger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);
137
+ logger.log(` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`);
138
+ if (options.dryRun) {
139
+ logger.log("\n --dry-run: nothing changed.\n");
140
+ return { repo };
141
+ }
142
+ const iam = deps.iam ?? await iamFor(options.profile);
143
+ const providerArn = await ensureOidcProvider(iam).catch((error) => {
144
+ if (/SSO session/i.test(String(error?.message)) && options.profile) throw new SsoSessionExpired(options.profile);
145
+ throw error;
146
+ });
147
+ const roleArn = await ensureRole(iam, role, trustPolicy(providerArn, repo, options.stage), policyArn);
148
+ logger.log(`\n ✓ AWS: ${roleArn}`);
149
+ gh([
150
+ "api",
151
+ "--method",
152
+ "PUT",
153
+ `repos/${repo}/environments/${options.stage}`
154
+ ]);
155
+ gh([
156
+ "variable",
157
+ "set",
158
+ "AWS_ROLE_ARN",
159
+ "--env",
160
+ options.stage,
161
+ "--repo",
162
+ repo,
163
+ "--body",
164
+ roleArn
165
+ ]);
166
+ logger.log(` ✓ GitHub: environment "${options.stage}", AWS_ROLE_ARN`);
167
+ if (hasKey) {
168
+ gh([
169
+ "secret",
170
+ "set",
171
+ "GKM_SECRETS_KEY",
172
+ "--env",
173
+ options.stage,
174
+ "--repo",
175
+ repo
176
+ ], readFileSync(keyPath, "utf-8").trim());
177
+ logger.log(" ✓ GitHub: GKM_SECRETS_KEY");
178
+ } else logger.log(` ⚠ No secrets key for "${options.stage}"; run gkm secrets:init --stage ${options.stage}, then this again.`);
179
+ logger.log(`\n Protect it: add required reviewers to the "${options.stage}" environment in the repository settings.\n`);
180
+ return {
181
+ roleArn,
182
+ repo
183
+ };
184
+ }
185
+ /** The profile's SSO login has lapsed; the fix is one command away. */
186
+ var SsoSessionExpired = class extends Error {
187
+ constructor(profile) {
188
+ super(`The SSO session for profile "${profile}" has expired or is invalid. Run: aws sso login --profile ${profile}`);
189
+ this.profile = profile;
190
+ this.name = "SsoSessionExpired";
191
+ }
192
+ };
193
+
194
+ //#endregion
195
+ export { deployGithubCommand };
196
+ //# sourceMappingURL=github-CrpsXSfV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-CrpsXSfV.mjs","names":["defaultGh: Gh","project: string","stage: string","providerArn: string","repo: string","iam: IAMClient","name: string","trust: string","policyArn: string","arn: string","profile?: string","endpoint?: string","config: IAMClientConfig","options: DeployGithubOptions","deps: { iam?: IAMClient; gh?: Gh; cwd?: string }","profile: string"],"sources":["../src/deploy/github.ts"],"sourcesContent":["/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and the stage's secrets key as\n * `GKM_SECRETS_KEY`. Re-running it converges rather than duplicating.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListOpenIDConnectProvidersCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { getKeyPath } from '../secrets/keystore.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default —\n * said out loud in the output, and replaceable with `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\nconst defaultGh: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t...(input === undefined\n\t\t\t? {}\n\t\t\t: { input, stdio: ['pipe', 'pipe', 'pipe'] as const }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job running in this repository's\n * `<stage>` environment, and nothing else — not another branch's job, not\n * another environment's. That is what keeps a staging deploy from using the\n * production role.\n */\nexport function trustPolicy(\n\tproviderArn: string,\n\trepo: string,\n\tstage: string,\n): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: `repo:${repo}:environment:${stage}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/** The deploy role, created or brought back to this trust and policy. */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\tpolicyArn: string,\n): Promise<string> {\n\tlet arn: string;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name !== 'NoSuchEntityException') {\n\t\t\tthrow error;\n\t\t}\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t}\n\n\t// Attaching an attached policy is a no-op, so this converges too.\n\tawait iam.send(\n\t\tnew AttachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t);\n\treturn arn;\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string }> {\n\tconst gh = deps.gh ?? defaultGh;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\tconst policyArn = options.policyArn ?? DEFAULT_POLICY_ARN;\n\tconst keyPath = getKeyPath(options.stage, workspace.name);\n\tconst hasKey = existsSync(keyPath);\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tlogger.log(\n\t\t` Policy: ${policyArn}${options.policyArn ? '' : ' (default; --policy-arn to narrow)'}`,\n\t);\n\tlogger.log(` Trusted by: repo:${repo}:environment:${options.stage} only`);\n\tlogger.log(\n\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t);\n\n\tif (options.dryRun) {\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst roleArn = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, repo, options.stage),\n\t\tpolicyArn,\n\t);\n\tlogger.log(`\\n ✓ AWS: ${roleArn}`);\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\treadFileSync(keyPath, 'utf-8').trim(),\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo };\n}\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends Error {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-6pkp2RRj.mjs";
1
+ import { AppsRecord, Framework, GkmConfig, InferredWorkspaceConfig, LoadedConfig, NormalizedWorkspace, Routes, WorkspaceConfig, WorkspaceInput } from "./types-CL7jPFM2.mjs";
2
2
 
3
3
  //#region src/workspace/publicEnv.d.ts
4
4
 
@@ -172,4 +172,4 @@ declare function getEndpointForStage(dokployConfig: {
172
172
  //# sourceMappingURL=index.d.ts.map
173
173
  //#endregion
174
174
  export { PUBLIC_ENV_PREFIXES, allConstructGlobs, appConstructGlobs, constructPatterns, defineWorkspace, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getEndpointForStage, getPublicEnvPrefix, normalizeWorkspace, processConfig, stripPublicPrefix, workspaceConstructGlobs, wrapSingleAppAsWorkspace };
175
- //# sourceMappingURL=index-C6e-ULLx.d.mts.map
175
+ //# sourceMappingURL=index-Ayxg_DQY.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-C6e-ULLx.d.mts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"sourcesContent":[],"mappings":";;;;;;AASA;AAoBA;AAoBA;;;cAxCa;AC2Hb;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA+CtB;AAAwC,iBDzLxB,kBAAA,CCyLwB,SAAA,CAAA,EDzLO,SCyLP,CAAA,EAAA,MAAA;;;AAGlB;AAsEtB;AAA6B,iBD9Ob,iBAAA,CC8Oa,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;;;;;;;;AA3J7B;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA+CtB;;;;AAGsB;AAsEtB;;;;;AAGe;AA6Bf;AAmBA;AAwBA;AAaA;AAiBA;;;;AAGY;AAmCZ;AAmCA;;;;AAIS;AAgDO,iBAjYA,eAmYqB,CAAA,oBAnYe,UAmYT,CAAA,CAAA,MAAA,EAlYlC,cAkYkC,CAlYnB,KAkYmB,CAAA,CAAA,EAjYxC,uBAiYwC,CAjYhB,KAiYgB,CAAA;;;;iBAnW3B,kBAAA,SACP,+BAEN;;;;;iBA+Ca,wBAAA,SACP,yBAEN;;;;;iBAsEa,aAAA,SACP,YAAY,+BAElB;;;;;;;iBA6Ba,uBAAA,YACJ;;;;;;;;;;;;iBAkBI,iBAAA,YACJ;;;;;;;iBAuBI,iBAAA,YAA6B;;iBAa7B,iBAAA,SAA0B;;;;;iBAiB1B,eAAA,YACJ,uCAET;;;;;iBAmCa,gBAAA,YAA4B;;;;;;;;;;iBAmC5B,oBAAA,YACJ,2DAGT;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDa,mBAAA;;cAEqB"}
1
+ {"version":3,"file":"index-Ayxg_DQY.d.mts","names":[],"sources":["../src/workspace/publicEnv.ts","../src/workspace/index.ts"],"sourcesContent":[],"mappings":";;;;;;AASA;AAoBA;AAoBA;;;cAxCa;AC2Hb;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA+CtB;AAAwC,iBDzLxB,kBAAA,CCyLwB,SAAA,CAAA,EDzLO,SCyLP,CAAA,EAAA,MAAA;;;AAGlB;AAsEtB;AAA6B,iBD9Ob,iBAAA,CC8Oa,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA,GAAA,IAAA;;;;;;;;AA3J7B;;;;;;;AAE0B;AA8B1B;;;;AAGsB;AA+CtB;;;;AAGsB;AAsEtB;;;;;AAGe;AA6Bf;AAmBA;AAwBA;AAaA;AAiBA;;;;AAGY;AAmCZ;AAmCA;;;;AAIS;AAgDO,iBAjYA,eAmYqB,CAAA,oBAnYe,UAmYT,CAAA,CAAA,MAAA,EAlYlC,cAkYkC,CAlYnB,KAkYmB,CAAA,CAAA,EAjYxC,uBAiYwC,CAjYhB,KAiYgB,CAAA;;;;iBAnW3B,kBAAA,SACP,+BAEN;;;;;iBA+Ca,wBAAA,SACP,yBAEN;;;;;iBAsEa,aAAA,SACP,YAAY,+BAElB;;;;;;;iBA6Ba,uBAAA,YACJ;;;;;;;;;;;;iBAkBI,iBAAA,YACJ;;;;;;;iBAuBI,iBAAA,YAA6B;;iBAa7B,iBAAA,SAA0B;;;;;iBAiB1B,eAAA,YACJ,uCAET;;;;;iBAmCa,gBAAA,YAA4B;;;;;;;;;;iBAmC5B,oBAAA,YACJ,2DAGT;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDa,mBAAA;;cAEqB"}
package/dist/index.cjs CHANGED
@@ -1,21 +1,22 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  const require_chunk = require('./chunk-CUT6urMc.cjs');
3
3
  const require_discover = require('./discover-DTmFeloL.cjs');
4
- const require_config = require('./config-BZ1F0WuA.cjs');
5
- const require_workspace = require('./workspace-C6VJQVtG.cjs');
4
+ const require_config = require('./config-HhDpJmLO.cjs');
5
+ const require_workspace = require('./workspace-C-a3G_U0.cjs');
6
6
  const require_credentials = require('./credentials-BapzjyV5.cjs');
7
- const require_credentials$1 = require('./credentials-Xamqe_NJ.cjs');
7
+ const require_credentials$1 = require('./credentials-B2Pyh3bJ.cjs');
8
8
  const require_generator = require('./generator-CNzBw7g5.cjs');
9
- const require_storage = require('./storage-BJmzYxt8.cjs');
10
- const require_openapi = require('./openapi-SjsgARie.cjs');
11
- const require_emit = require('./emit-CbcRolsa.cjs');
12
- const require_dokploy_api = require('./dokploy-api-BsVH0y_s.cjs');
13
- const require_encryption = require('./encryption-C6jaU0hE.cjs');
14
- const require_CachedStateProvider = require('./CachedStateProvider-CqPdW7CB.cjs');
15
- const require_fullstack_secrets = require('./fullstack-secrets-C-GGohke.cjs');
16
- const require_openapi_react_query = require('./openapi-react-query-CmJ1xDbn.cjs');
17
- require('./sync-SizaChmC.cjs');
18
- const require_setup = require('./setup-hC6hI86f.cjs');
9
+ const require_keystore = require('./keystore-C9rint2E.cjs');
10
+ const require_storage = require('./storage-CSNplaJn.cjs');
11
+ const require_openapi = require('./openapi-DaKlTKxz.cjs');
12
+ const require_emit = require('./emit-CUD1BWls.cjs');
13
+ const require_dokploy_api = require('./dokploy-api-zqKe9lHH.cjs');
14
+ const require_encryption = require('./encryption-D2pL3tHH.cjs');
15
+ const require_CachedStateProvider = require('./CachedStateProvider-BEBl2wQT.cjs');
16
+ const require_fullstack_secrets = require('./fullstack-secrets-D6MyHhQP.cjs');
17
+ const require_openapi_react_query = require('./openapi-react-query-BKH334Ug.cjs');
18
+ require('./sync-0ILCb-1S.cjs');
19
+ const require_setup = require('./setup-DOv92pIu.cjs');
19
20
  const node_fs = require_chunk.__toESM(require("node:fs"));
20
21
  const node_path = require_chunk.__toESM(require("node:path"));
21
22
  const __geekmidas_manifest = require_chunk.__toESM(require("@geekmidas/manifest"));
@@ -43,7 +44,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
43
44
 
44
45
  //#region package.json
45
46
  var name = "@geekmidas/cli";
46
- var version = "10.0.0-alpha.10";
47
+ var version = "10.0.0-alpha.11";
47
48
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
48
49
  var private$1 = false;
49
50
  var type = "module";
@@ -188,7 +189,7 @@ const logger$11 = console;
188
189
  * Validate Dokploy token by making a test API call
189
190
  */
190
191
  async function validateDokployToken(endpoint, token) {
191
- const { DokployApi: DokployApi$1 } = await Promise.resolve().then(() => require("./dokploy-api-BuxDRo3l.cjs"));
192
+ const { DokployApi: DokployApi$1 } = await Promise.resolve().then(() => require("./dokploy-api-5cK2InBm.cjs"));
192
193
  const api = new DokployApi$1({
193
194
  baseUrl: endpoint,
194
195
  token
@@ -2639,7 +2640,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
2639
2640
  let masterKey;
2640
2641
  if (context.production?.bundle && !skipBundle) {
2641
2642
  logger$7.log(`\n📦 Bundling production server...`);
2642
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-BY1OmV1e.cjs"));
2643
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-CaTGtiaO.cjs"));
2643
2644
  const allConstructs = [
2644
2645
  ...endpoints.map((e) => e.construct),
2645
2646
  ...functions.map((f) => f.construct),
@@ -3592,11 +3593,11 @@ async function createDnsProvider(options) {
3592
3593
  if (isDnsProvider(config.provider)) return config.provider;
3593
3594
  const provider = config.provider;
3594
3595
  if (provider === "hostinger") {
3595
- const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-h3EbEp-Z.cjs"));
3596
+ const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-BOvsgATO.cjs"));
3596
3597
  return new HostingerProvider();
3597
3598
  }
3598
3599
  if (provider === "route53") {
3599
- const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-di2fMM5N.cjs"));
3600
+ const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-CH9XH7bz.cjs"));
3600
3601
  const route53Config = config;
3601
3602
  return new Route53Provider({
3602
3603
  region: route53Config.region,
@@ -5923,20 +5924,20 @@ function isStateProvider(value) {
5923
5924
  async function createStateProvider(options) {
5924
5925
  const { config, workspaceRoot, workspaceName } = options;
5925
5926
  if (!config) {
5926
- const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Db9SEjyH.cjs"));
5927
+ const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-yjXZXZDM.cjs"));
5927
5928
  return new LocalStateProvider(workspaceRoot);
5928
5929
  }
5929
5930
  if (isStateProvider(config.provider)) return config.provider;
5930
5931
  const provider = config.provider;
5931
5932
  if (provider === "local") {
5932
- const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Db9SEjyH.cjs"));
5933
+ const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-yjXZXZDM.cjs"));
5933
5934
  return new LocalStateProvider(workspaceRoot);
5934
5935
  }
5935
5936
  if (provider === "ssm") {
5936
5937
  if (!workspaceName) throw new Error("Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.");
5937
- const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Db9SEjyH.cjs"));
5938
- const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-DyX8s9HI.cjs"));
5939
- const { CachedStateProvider: CachedStateProvider$1 } = await Promise.resolve().then(() => require("./CachedStateProvider-BLo7zv7S.cjs"));
5938
+ const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-yjXZXZDM.cjs"));
5939
+ const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-CXtNgPfy.cjs"));
5940
+ const { CachedStateProvider: CachedStateProvider$1 } = await Promise.resolve().then(() => require("./CachedStateProvider-DqhGpzsm.cjs"));
5940
5941
  const ssmConfig = config;
5941
5942
  const local = new LocalStateProvider(workspaceRoot);
5942
5943
  const ssm = SSMStateProvider.create({
@@ -6812,7 +6813,7 @@ async function workspaceDeployCommand(configured, options) {
6812
6813
  const backupCluster = Object.values(declaredClusters)[0];
6813
6814
  if (workspace.deploy?.backups && backupCluster) {
6814
6815
  logger$3.log("\n💾 Provisioning backup destination...");
6815
- const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-BrsHwYG0.cjs"));
6816
+ const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-cE2PuCTb.cjs"));
6816
6817
  const backupState = await provisionBackupDestination({
6817
6818
  api,
6818
6819
  projectId: project.projectId,
@@ -8026,26 +8027,26 @@ const CLI_VERSION = `~${pkg.version}`;
8026
8027
  * Run: pnpm --filter @geekmidas/cli sync-versions
8027
8028
  */
8028
8029
  const GEEKMIDAS_VERSIONS = {
8029
- "@geekmidas/audit": "~10.0.0-alpha.10",
8030
- "@geekmidas/auth": "~10.0.0-alpha.10",
8031
- "@geekmidas/cache": "~10.0.0-alpha.10",
8032
- "@geekmidas/client": "~10.0.0-alpha.10",
8033
- "@geekmidas/cloud": "~10.0.0-alpha.10",
8034
- "@geekmidas/constructs": "~10.0.0-alpha.10",
8035
- "@geekmidas/db": "~10.0.0-alpha.10",
8036
- "@geekmidas/emailkit": "~10.0.0-alpha.10",
8037
- "@geekmidas/envkit": "~10.0.0-alpha.10",
8038
- "@geekmidas/errors": "~10.0.0-alpha.10",
8039
- "@geekmidas/events": "~10.0.0-alpha.10",
8040
- "@geekmidas/logger": "~10.0.0-alpha.10",
8041
- "@geekmidas/manifest": "~10.0.0-alpha.10",
8042
- "@geekmidas/rate-limit": "~10.0.0-alpha.10",
8043
- "@geekmidas/schema": "~10.0.0-alpha.10",
8044
- "@geekmidas/services": "~10.0.0-alpha.10",
8045
- "@geekmidas/storage": "~10.0.0-alpha.10",
8046
- "@geekmidas/studio": "~10.0.0-alpha.10",
8047
- "@geekmidas/telescope": "~10.0.0-alpha.10",
8048
- "@geekmidas/testkit": "~10.0.0-alpha.10",
8030
+ "@geekmidas/audit": "~10.0.0-alpha.11",
8031
+ "@geekmidas/auth": "~10.0.0-alpha.11",
8032
+ "@geekmidas/cache": "~10.0.0-alpha.11",
8033
+ "@geekmidas/client": "~10.0.0-alpha.11",
8034
+ "@geekmidas/cloud": "~10.0.0-alpha.11",
8035
+ "@geekmidas/constructs": "~10.0.0-alpha.11",
8036
+ "@geekmidas/db": "~10.0.0-alpha.11",
8037
+ "@geekmidas/emailkit": "~10.0.0-alpha.11",
8038
+ "@geekmidas/envkit": "~10.0.0-alpha.11",
8039
+ "@geekmidas/errors": "~10.0.0-alpha.11",
8040
+ "@geekmidas/events": "~10.0.0-alpha.11",
8041
+ "@geekmidas/logger": "~10.0.0-alpha.11",
8042
+ "@geekmidas/manifest": "~10.0.0-alpha.11",
8043
+ "@geekmidas/rate-limit": "~10.0.0-alpha.11",
8044
+ "@geekmidas/schema": "~10.0.0-alpha.11",
8045
+ "@geekmidas/services": "~10.0.0-alpha.11",
8046
+ "@geekmidas/storage": "~10.0.0-alpha.11",
8047
+ "@geekmidas/studio": "~10.0.0-alpha.11",
8048
+ "@geekmidas/telescope": "~10.0.0-alpha.11",
8049
+ "@geekmidas/testkit": "~10.0.0-alpha.11",
8049
8050
  "@geekmidas/cli": CLI_VERSION
8050
8051
  };
8051
8052
 
@@ -9842,7 +9843,7 @@ function deployPackage(options) {
9842
9843
  }
9843
9844
  function generateDeployFiles(options) {
9844
9845
  if (options.deployTarget !== "sst") return [];
9845
- if (!options.region) throw new Error("An SST deploy needs a region.");
9846
+ if (!options.region) throw new SstNeedsRegion();
9846
9847
  const db = databaseFor();
9847
9848
  const mail = emailFor();
9848
9849
  const hasDatabase = options.template === "fullstack" || options.services.db;
@@ -9910,6 +9911,16 @@ ${inputs.join("\n")}
9910
9911
  `
9911
9912
  }];
9912
9913
  }
9914
+ /**
9915
+ * An SST scaffold asked for without a region. `init` always resolves one —
9916
+ * asked, `--region`, or eu-west-1 under `--yes` — so this is a caller's bug.
9917
+ */
9918
+ var SstNeedsRegion = class extends Error {
9919
+ constructor() {
9920
+ super("An SST deploy needs a region: pass --region, e.g. eu-west-1.");
9921
+ this.name = "SstNeedsRegion";
9922
+ }
9923
+ };
9913
9924
 
9914
9925
  //#endregion
9915
9926
  //#region src/init/generators/monorepo.ts
@@ -14091,8 +14102,8 @@ const AWS_REGION = /^[a-z]{2}(-gov)?-[a-z]+-\d$/;
14091
14102
  async function initCommand(projectName, options = {}) {
14092
14103
  const cwd = process.cwd();
14093
14104
  const detectedPkgManager = detectPackageManager(cwd);
14094
- if (options.deploy && !deployTargetChoices.some((choice) => choice.value === options.deploy)) throw new Error(`Unknown deploy target "${options.deploy}". Use ${deployTargetChoices.map((c) => c.value).join(", ")}.`);
14095
- if (options.region && !AWS_REGION.test(options.region)) throw new Error(`"${options.region}" is not an AWS region. Use one like eu-west-1.`);
14105
+ if (options.deploy && !deployTargetChoices.some((choice) => choice.value === options.deploy)) throw new UnknownDeployTarget(options.deploy, deployTargetChoices.map((c) => c.value));
14106
+ if (options.region && !AWS_REGION.test(options.region)) throw new NotAnAwsRegion(options.region);
14096
14107
  if (options.stages || options.localStage || options.protectedStage) {
14097
14108
  const deployed = stageList(options.stages ?? "");
14098
14109
  const problems = [
@@ -14103,7 +14114,7 @@ async function initCommand(projectName, options = {}) {
14103
14114
  }) : [],
14104
14115
  ...options.protectedStage && !deployed.includes(options.protectedStage) ? [`--protected-stage "${options.protectedStage}" is not in --stages`] : []
14105
14116
  ];
14106
- if (problems.length) throw new Error([...new Set(problems)].join("\n"));
14117
+ if (problems.length) throw new require_workspace.InvalidStages([...new Set(problems)]);
14107
14118
  }
14108
14119
  let deployedSoFar = [];
14109
14120
  prompts.default.override({});
@@ -14382,7 +14393,7 @@ async function initCommand(projectName, options = {}) {
14382
14393
  }
14383
14394
  devSecrets.custom = customSecrets;
14384
14395
  await require_storage.writeStageSecrets(devSecrets, targetDir);
14385
- const keyPath = require_storage.getKeyPath(local, name$1);
14396
+ const keyPath = require_keystore.getKeyPath(local, name$1);
14386
14397
  console.log(` Secrets: .gkm/secrets/${local}.json (encrypted)`);
14387
14398
  console.log(` Key: ${keyPath}\n`);
14388
14399
  if (!options.skipInstall) {
@@ -14511,9 +14522,26 @@ function resolveStages(options, answers) {
14511
14522
  ...kept ? { protected: [kept] } : {}
14512
14523
  };
14513
14524
  const problems = require_workspace.stageProblems(stages);
14514
- if (problems.length) throw new Error(problems.join("\n"));
14525
+ if (problems.length) throw new require_workspace.InvalidStages(problems);
14515
14526
  return stages;
14516
14527
  }
14528
+ /** `--deploy` naming a target init has no scaffold for. */
14529
+ var UnknownDeployTarget = class extends Error {
14530
+ constructor(target, known) {
14531
+ super(`Unknown deploy target "${target}". Use ${known.join(", ")}.`);
14532
+ this.target = target;
14533
+ this.known = known;
14534
+ this.name = "UnknownDeployTarget";
14535
+ }
14536
+ };
14537
+ /** `--region` that is not shaped like one, e.g. `europe` for `eu-west-1`. */
14538
+ var NotAnAwsRegion = class extends Error {
14539
+ constructor(region) {
14540
+ super(`"${region}" is not an AWS region. Use one like eu-west-1.`);
14541
+ this.region = region;
14542
+ this.name = "NotAnAwsRegion";
14543
+ }
14544
+ };
14517
14545
 
14518
14546
  //#endregion
14519
14547
  //#region src/secrets/index.ts
@@ -14726,11 +14754,11 @@ function maskUrl(url) {
14726
14754
  async function testCommand(options = {}) {
14727
14755
  const cwd = process.cwd();
14728
14756
  const stage = options.stage ?? await require_config.loadWorkspaceConfig(cwd).then((loaded) => loaded.workspace.stages.local).catch(() => void 0);
14729
- if (!stage) throw new Error("No stage to test with: add `stages` to gkm.config.ts, or pass --stage.");
14757
+ if (!stage) throw new NoStageToTest();
14730
14758
  console.log(`\n🧪 Running tests with ${stage} environment...\n`);
14731
14759
  const autoSetup = options.autoSetup || Boolean(process.env.GKM_AUTO_SETUP);
14732
14760
  if (autoSetup) {
14733
- const { ensureStageSecrets } = await Promise.resolve().then(() => require("./setup-BsZvZP6f.cjs"));
14761
+ const { ensureStageSecrets } = await Promise.resolve().then(() => require("./setup-lCx_DcUo.cjs"));
14734
14762
  const generated = await ensureStageSecrets(stage, cwd);
14735
14763
  if (generated) console.log(` 🔐 Generated fresh ${stage} secrets (auto-setup)`);
14736
14764
  }
@@ -14817,6 +14845,13 @@ function rewriteDatabaseUrlForTests(env) {
14817
14845
  }
14818
14846
  return result;
14819
14847
  }
14848
+ /** `gkm test` outside a project that declares stages, with none named. */
14849
+ var NoStageToTest = class extends Error {
14850
+ constructor() {
14851
+ super("No stage to test with: add `stages` to gkm.config.ts, or pass --stage.");
14852
+ this.name = "NoStageToTest";
14853
+ }
14854
+ };
14820
14855
 
14821
14856
  //#endregion
14822
14857
  //#region src/upgrade/version.ts
@@ -15359,13 +15394,13 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
15359
15394
  const globalOptions = program.opts();
15360
15395
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
15361
15396
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
15362
- const { pushSecrets } = await Promise.resolve().then(() => require("./sync-D306yOWu.cjs"));
15363
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-D0pimNiy.cjs"));
15364
- const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-BM9hGpgv.cjs"));
15397
+ const { pushSecrets } = await Promise.resolve().then(() => require("./sync-DYENfOt4.cjs"));
15398
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CYMhFSYy.cjs"));
15399
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DlVol_oj.cjs"));
15365
15400
  const { workspace } = await loadWorkspaceConfig$1();
15366
15401
  const secrets = await readStageSecrets$1(options.stage, workspace.root);
15367
15402
  if (secrets) {
15368
- const { derivedContainers: derivedContainers$1 } = await Promise.resolve().then(() => require("./workspace-XXtNhqJl.cjs"));
15403
+ const { derivedContainers: derivedContainers$1 } = await Promise.resolve().then(() => require("./workspace-DQxxs7Jk.cjs"));
15369
15404
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers$1(workspace, options.stage));
15370
15405
  if (result) {
15371
15406
  await writeStageSecrets$1(result.secrets, workspace.root);
@@ -15385,16 +15420,16 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
15385
15420
  const globalOptions = program.opts();
15386
15421
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
15387
15422
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
15388
- const { pullSecrets } = await Promise.resolve().then(() => require("./sync-D306yOWu.cjs"));
15389
- const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-BM9hGpgv.cjs"));
15390
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-D0pimNiy.cjs"));
15423
+ const { pullSecrets } = await Promise.resolve().then(() => require("./sync-DYENfOt4.cjs"));
15424
+ const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DlVol_oj.cjs"));
15425
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CYMhFSYy.cjs"));
15391
15426
  const { workspace } = await loadWorkspaceConfig$1();
15392
15427
  let secrets = await pullSecrets(options.stage, workspace);
15393
15428
  if (!secrets) {
15394
15429
  console.error(`No remote secrets found for stage "${options.stage}".`);
15395
15430
  process.exit(1);
15396
15431
  }
15397
- const { derivedContainers: derivedContainers$1 } = await Promise.resolve().then(() => require("./workspace-XXtNhqJl.cjs"));
15432
+ const { derivedContainers: derivedContainers$1 } = await Promise.resolve().then(() => require("./workspace-DQxxs7Jk.cjs"));
15398
15433
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers$1(workspace, options.stage));
15399
15434
  if (result) {
15400
15435
  secrets = result.secrets;
@@ -15413,8 +15448,8 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
15413
15448
  const globalOptions = program.opts();
15414
15449
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
15415
15450
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
15416
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-D0pimNiy.cjs"));
15417
- const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-BM9hGpgv.cjs"));
15451
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-CYMhFSYy.cjs"));
15452
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-DlVol_oj.cjs"));
15418
15453
  const { workspace } = await loadWorkspaceConfig$1();
15419
15454
  const stage = options.stage ?? workspace.stages.local;
15420
15455
  const secrets = await readStageSecrets$1(stage, workspace.root);
@@ -15422,7 +15457,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
15422
15457
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
15423
15458
  process.exit(1);
15424
15459
  }
15425
- const { derivedContainers: derivedContainers$1 } = await Promise.resolve().then(() => require("./workspace-XXtNhqJl.cjs"));
15460
+ const { derivedContainers: derivedContainers$1 } = await Promise.resolve().then(() => require("./workspace-DQxxs7Jk.cjs"));
15426
15461
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers$1(workspace, stage));
15427
15462
  if (!result) {
15428
15463
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -15477,6 +15512,17 @@ program.command("deploy:init").description("Initialize Dokploy deployment (creat
15477
15512
  process.exit(1);
15478
15513
  }
15479
15514
  });
15515
+ program.command("deploy:github").description("Let GitHub Actions deploy a stage: OIDC role in the stage's AWS account, and the GitHub environment the deploy workflow uses").requiredOption("--stage <stage>", "A deployed stage from gkm.config.ts").option("--profile <profile>", "AWS profile for the stage's account").option("--repo <owner/name>", "GitHub repository (default: this one)").option("--policy-arn <arn>", "Policy for the deploy role (default: AdministratorAccess)").option("--dry-run", "Show the plan without changing AWS or GitHub").action(async (options) => {
15516
+ try {
15517
+ const globalOptions = program.opts();
15518
+ if (globalOptions.cwd) process.chdir(globalOptions.cwd);
15519
+ const { deployGithubCommand } = await Promise.resolve().then(() => require("./github-BrvaPkFf.cjs"));
15520
+ await deployGithubCommand(options);
15521
+ } catch (error) {
15522
+ console.error(formatError(error));
15523
+ process.exit(1);
15524
+ }
15525
+ });
15480
15526
  program.command("deploy:list").description("List Dokploy resources (projects, registries)").option("--endpoint <url>", "Dokploy server URL (uses stored credentials if logged in)").option("--projects", "List projects").option("--registries", "List registries").action(async (options) => {
15481
15527
  try {
15482
15528
  const globalOptions = program.opts();