@geekmidas/cli 10.0.0-alpha.63 → 10.0.0-alpha.65

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 (216) hide show
  1. package/README.md +1 -1
  2. package/dist/{HostingerProvider-mJ7YruxH.cjs → HostingerProvider-Dlmbfass.cjs} +3 -3
  3. package/dist/{HostingerProvider-mJ7YruxH.cjs.map → HostingerProvider-Dlmbfass.cjs.map} +1 -1
  4. package/dist/{HostingerProvider-DBBwpDjn.mjs → HostingerProvider-x9gQTAGZ.mjs} +2 -2
  5. package/dist/{HostingerProvider-DBBwpDjn.mjs.map → HostingerProvider-x9gQTAGZ.mjs.map} +1 -1
  6. package/dist/{LegacyStateStore-CP7SZwTP.cjs → LegacyStateStore-BGoo__sO.cjs} +2 -2
  7. package/dist/{LegacyStateStore-CP7SZwTP.cjs.map → LegacyStateStore-BGoo__sO.cjs.map} +1 -1
  8. package/dist/{LegacyStateStore-Q3GmKOdw.mjs → LegacyStateStore-BUu1nQ5u.mjs} +2 -2
  9. package/dist/{LegacyStateStore-Q3GmKOdw.mjs.map → LegacyStateStore-BUu1nQ5u.mjs.map} +1 -1
  10. package/dist/{LocalStateStore-sS8mvLeq.cjs → LocalStateStore-93urXyQe.cjs} +2 -2
  11. package/dist/{LocalStateStore-sS8mvLeq.cjs.map → LocalStateStore-93urXyQe.cjs.map} +1 -1
  12. package/dist/{LocalStateStore-BGIXcrvX.mjs → LocalStateStore-BRhMQRuy.mjs} +2 -2
  13. package/dist/{LocalStateStore-BGIXcrvX.mjs.map → LocalStateStore-BRhMQRuy.mjs.map} +1 -1
  14. package/dist/{OpenApiTsGenerator-CjHWFRDd.cjs → OpenApiTsGenerator-BWuDRHe9.cjs} +2 -2
  15. package/dist/{OpenApiTsGenerator-CjHWFRDd.cjs.map → OpenApiTsGenerator-BWuDRHe9.cjs.map} +1 -1
  16. package/dist/{OpenApiTsGenerator-CnAwhGxi.mjs → OpenApiTsGenerator-C6v-IeHt.mjs} +2 -2
  17. package/dist/{OpenApiTsGenerator-CnAwhGxi.mjs.map → OpenApiTsGenerator-C6v-IeHt.mjs.map} +1 -1
  18. package/dist/{S3StateStore-6jqITH2F.cjs → S3StateStore-F4dxw6kS.cjs} +2 -2
  19. package/dist/{S3StateStore-6jqITH2F.cjs.map → S3StateStore-F4dxw6kS.cjs.map} +1 -1
  20. package/dist/{S3StateStore-D7l_lWJ3.mjs → S3StateStore-pw1GhWwt.mjs} +2 -2
  21. package/dist/{S3StateStore-D7l_lWJ3.mjs.map → S3StateStore-pw1GhWwt.mjs.map} +1 -1
  22. package/dist/{SSMStateStore-B__F7IJa.cjs → SSMStateStore-Bt9acuAF.cjs} +2 -2
  23. package/dist/{SSMStateStore-B__F7IJa.cjs.map → SSMStateStore-Bt9acuAF.cjs.map} +1 -1
  24. package/dist/{SSMStateStore-CAQDVpoc.mjs → SSMStateStore-Dmj7zzFO.mjs} +2 -2
  25. package/dist/{SSMStateStore-CAQDVpoc.mjs.map → SSMStateStore-Dmj7zzFO.mjs.map} +1 -1
  26. package/dist/{bundler-DLfOn0ls.cjs → bundler-D6MlIfBT.cjs} +3 -3
  27. package/dist/{bundler-DLfOn0ls.cjs.map → bundler-D6MlIfBT.cjs.map} +1 -1
  28. package/dist/{bundler-DyWI41Iy.mjs → bundler-D_14JLZF.mjs} +3 -3
  29. package/dist/{bundler-DyWI41Iy.mjs.map → bundler-D_14JLZF.mjs.map} +1 -1
  30. package/dist/{cli-D-ZM-X2s.cjs → cli-BfH1deAT.cjs} +8 -7
  31. package/dist/{cli-D-ZM-X2s.cjs.map → cli-BfH1deAT.cjs.map} +1 -1
  32. package/dist/{cli-PkVF28LM.mjs → cli-P56vvrDQ.mjs} +5 -4
  33. package/dist/{cli-PkVF28LM.mjs.map → cli-P56vvrDQ.mjs.map} +1 -1
  34. package/dist/compose-391oEKj1.cjs +1982 -0
  35. package/dist/compose-391oEKj1.cjs.map +1 -0
  36. package/dist/compose-DprE3ASm.mjs +1862 -0
  37. package/dist/compose-DprE3ASm.mjs.map +1 -0
  38. package/dist/{config-C2BUosLm.d.mts → config-BonvTW8p.d.mts} +3 -3
  39. package/dist/{config-C2BUosLm.d.mts.map → config-BonvTW8p.d.mts.map} +1 -1
  40. package/dist/{config-CemHVcIr.d.cts → config-V_X_BMpm.d.cts} +3 -3
  41. package/dist/{config-CemHVcIr.d.cts.map → config-V_X_BMpm.d.cts.map} +1 -1
  42. package/dist/config.cjs +416 -17
  43. package/dist/config.cjs.map +1 -0
  44. package/dist/config.d.cts +3 -3
  45. package/dist/config.d.mts +3 -3
  46. package/dist/config.mjs +401 -2
  47. package/dist/config.mjs.map +1 -0
  48. package/dist/credentials-Bzbg23n8.cjs +298 -0
  49. package/dist/credentials-Bzbg23n8.cjs.map +1 -0
  50. package/dist/credentials-Dq1MnWQf.mjs +233 -0
  51. package/dist/credentials-Dq1MnWQf.mjs.map +1 -0
  52. package/dist/{define-CHBIEc5j.mjs → define-DR8xGYfL.mjs} +6 -3
  53. package/dist/define-DR8xGYfL.mjs.map +1 -0
  54. package/dist/{define-Cv_QvDrg.cjs → define-DzRz8FgH.cjs} +6 -3
  55. package/dist/define-DzRz8FgH.cjs.map +1 -0
  56. package/dist/deploy/public.cjs +39 -33
  57. package/dist/deploy/public.cjs.map +1 -1
  58. package/dist/deploy/public.d.cts +44 -5
  59. package/dist/deploy/public.d.cts.map +1 -1
  60. package/dist/deploy/public.d.mts +44 -5
  61. package/dist/deploy/public.d.mts.map +1 -1
  62. package/dist/deploy/public.mjs +15 -12
  63. package/dist/deploy/public.mjs.map +1 -1
  64. package/dist/{deploy-CGW-QY9D.mjs → deploy-9MBG_QLd.mjs} +11 -9
  65. package/dist/deploy-9MBG_QLd.mjs.map +1 -0
  66. package/dist/{deploy-BiMrP9i7.cjs → deploy-B80idPUZ.cjs} +17 -15
  67. package/dist/deploy-B80idPUZ.cjs.map +1 -0
  68. package/dist/{deploy-CG8bfFij.mjs → deploy-CZ5nlpsu.mjs} +16 -119
  69. package/dist/deploy-CZ5nlpsu.mjs.map +1 -0
  70. package/dist/{deploy-ABBI0lf1.cjs → deploy-Cy8igpQF.cjs} +48 -151
  71. package/dist/deploy-Cy8igpQF.cjs.map +1 -0
  72. package/dist/derive-B4vDpzgP.mjs +150 -0
  73. package/dist/derive-B4vDpzgP.mjs.map +1 -0
  74. package/dist/derive-DAbo49sm.cjs +161 -0
  75. package/dist/derive-DAbo49sm.cjs.map +1 -0
  76. package/dist/{discover-C_6COHEv.cjs → discover-DHdECueV.cjs} +6 -157
  77. package/dist/discover-DHdECueV.cjs.map +1 -0
  78. package/dist/{discover-BaT6bpnA.mjs → discover-LMrqsWSU.mjs} +8 -117
  79. package/dist/discover-LMrqsWSU.mjs.map +1 -0
  80. package/dist/{dokploy-CwdmlyQL.mjs → dokploy-BdtBePjp.mjs} +3 -3
  81. package/dist/{dokploy-CwdmlyQL.mjs.map → dokploy-BdtBePjp.mjs.map} +1 -1
  82. package/dist/{dokploy-QJxMhxdf.cjs → dokploy-iFY8MY-L.cjs} +3 -3
  83. package/dist/{dokploy-QJxMhxdf.cjs.map → dokploy-iFY8MY-L.cjs.map} +1 -1
  84. package/dist/{encryption-DXw3jj0c.cjs → encryption-B1wJwgae.cjs} +6 -6
  85. package/dist/{encryption-DXw3jj0c.cjs.map → encryption-B1wJwgae.cjs.map} +1 -1
  86. package/dist/{encryption-CbQG_ayW.mjs → encryption-DF_hgIpl.mjs} +5 -5
  87. package/dist/{encryption-CbQG_ayW.mjs.map → encryption-DF_hgIpl.mjs.map} +1 -1
  88. package/dist/{github-ciwbIwVa.mjs → github-B0YYE98x.mjs} +4 -4
  89. package/dist/{github-ciwbIwVa.mjs.map → github-B0YYE98x.mjs.map} +1 -1
  90. package/dist/{github-BH_mETav.cjs → github-CYcmTdFs.cjs} +4 -4
  91. package/dist/{github-BH_mETav.cjs.map → github-CYcmTdFs.cjs.map} +1 -1
  92. package/dist/{index-DrKzpHl-.d.mts → index-DPUAGJaK.d.cts} +16 -3
  93. package/dist/index-DPUAGJaK.d.cts.map +1 -0
  94. package/dist/{index-BtXDZQuF.d.cts → index-DwKD_8lt.d.mts} +16 -3
  95. package/dist/index-DwKD_8lt.d.mts.map +1 -0
  96. package/dist/index.cjs +280 -1849
  97. package/dist/index.cjs.map +1 -1
  98. package/dist/index.mjs +221 -1774
  99. package/dist/index.mjs.map +1 -1
  100. package/dist/local-BdH4XN0E.cjs +409 -0
  101. package/dist/local-BdH4XN0E.cjs.map +1 -0
  102. package/dist/local-CWR4Y_kW.mjs +350 -0
  103. package/dist/local-CWR4Y_kW.mjs.map +1 -0
  104. package/dist/{migrate-ImBKTBcW.mjs → migrate-B2qL5wj4.mjs} +8 -7
  105. package/dist/{migrate-ImBKTBcW.mjs.map → migrate-B2qL5wj4.mjs.map} +1 -1
  106. package/dist/{migrate-6oa9y_f2.cjs → migrate-q4pPAXtO.cjs} +21 -20
  107. package/dist/{migrate-6oa9y_f2.cjs.map → migrate-q4pPAXtO.cjs.map} +1 -1
  108. package/dist/openapi.cjs +2 -2
  109. package/dist/openapi.d.cts +1 -1
  110. package/dist/openapi.d.mts +1 -1
  111. package/dist/openapi.mjs +2 -2
  112. package/dist/provider-C9_-45d5.cjs +56 -0
  113. package/dist/provider-C9_-45d5.cjs.map +1 -0
  114. package/dist/provider-q4lsYzYG.mjs +45 -0
  115. package/dist/provider-q4lsYzYG.mjs.map +1 -0
  116. package/dist/{ready-Bpdnn2ON.mjs → ready-bQfu9GES.mjs} +3 -3
  117. package/dist/{ready-Bpdnn2ON.mjs.map → ready-bQfu9GES.mjs.map} +1 -1
  118. package/dist/{ready-BiG0rt4h.cjs → ready-brEhh68f.cjs} +3 -3
  119. package/dist/{ready-BiG0rt4h.cjs.map → ready-brEhh68f.cjs.map} +1 -1
  120. package/dist/reconcile/public.cjs +1 -1
  121. package/dist/reconcile/public.d.cts +1 -1
  122. package/dist/reconcile/public.d.mts +1 -1
  123. package/dist/reconcile/public.mjs +1 -1
  124. package/dist/resolve-DLkIXTct.mjs +262 -0
  125. package/dist/resolve-DLkIXTct.mjs.map +1 -0
  126. package/dist/resolve-N_i4XNd-.cjs +315 -0
  127. package/dist/resolve-N_i4XNd-.cjs.map +1 -0
  128. package/dist/{runtime-Dn_n-29S.d.cts → runtime-BnD7Nn96.d.cts} +2 -2
  129. package/dist/{runtime-CXPlkZzs.d.mts.map → runtime-BnD7Nn96.d.cts.map} +1 -1
  130. package/dist/{runtime-CXPlkZzs.d.mts → runtime-j-tmFL65.d.mts} +2 -2
  131. package/dist/{runtime-Dn_n-29S.d.cts.map → runtime-j-tmFL65.d.mts.map} +1 -1
  132. package/dist/sandbox/discover-worker.cjs +1 -1
  133. package/dist/sandbox/discover-worker.mjs +1 -1
  134. package/dist/sandbox-CL--ZDui.cjs +164 -0
  135. package/dist/sandbox-CL--ZDui.cjs.map +1 -0
  136. package/dist/sandbox-Dm247NGx.mjs +117 -0
  137. package/dist/sandbox-Dm247NGx.mjs.map +1 -0
  138. package/dist/sniffer-BSwaL-dX.cjs +2236 -0
  139. package/dist/sniffer-BSwaL-dX.cjs.map +1 -0
  140. package/dist/sniffer-BZ-OJO4Y.mjs +2068 -0
  141. package/dist/sniffer-BZ-OJO4Y.mjs.map +1 -0
  142. package/dist/sst-B2HbrDR3.mjs +430 -0
  143. package/dist/sst-B2HbrDR3.mjs.map +1 -0
  144. package/dist/sst-DcfsVH6x.cjs +453 -0
  145. package/dist/sst-DcfsVH6x.cjs.map +1 -0
  146. package/dist/{sniffer-aO_-s5vq.mjs → state-B8YwmUZU.mjs} +1204 -3236
  147. package/dist/state-B8YwmUZU.mjs.map +1 -0
  148. package/dist/{sniffer-D_SYc_pO.cjs → state-DF26e_lo.cjs} +1251 -3494
  149. package/dist/state-DF26e_lo.cjs.map +1 -0
  150. package/dist/{store-KvNQwYaB.cjs → store-CJF1Tqkq.cjs} +4 -4
  151. package/dist/{store-KvNQwYaB.cjs.map → store-CJF1Tqkq.cjs.map} +1 -1
  152. package/dist/{store-BS8A7NcA.mjs → store-CLiHnqOY.mjs} +2 -2
  153. package/dist/{store-BS8A7NcA.mjs.map → store-CLiHnqOY.mjs.map} +1 -1
  154. package/dist/target/public.cjs +6 -6
  155. package/dist/target/public.d.cts +3 -3
  156. package/dist/target/public.d.mts +3 -3
  157. package/dist/target/public.mjs +3 -3
  158. package/dist/terminal-Bs5E1oxm.cjs +88 -0
  159. package/dist/terminal-Bs5E1oxm.cjs.map +1 -0
  160. package/dist/terminal-CfbiYUbZ.mjs +84 -0
  161. package/dist/terminal-CfbiYUbZ.mjs.map +1 -0
  162. package/dist/{types-DIA7Li-J.d.cts → types-BCzMWBb6.d.mts} +84 -17
  163. package/dist/types-BCzMWBb6.d.mts.map +1 -0
  164. package/dist/{types-CfLKGfC_.d.mts → types-ByMWkXKP.d.cts} +85 -16
  165. package/dist/types-ByMWkXKP.d.cts.map +1 -0
  166. package/dist/utils-D2Nk1Nv2.cjs +205 -0
  167. package/dist/utils-D2Nk1Nv2.cjs.map +1 -0
  168. package/dist/utils-D_fxUqvU.mjs +156 -0
  169. package/dist/utils-D_fxUqvU.mjs.map +1 -0
  170. package/dist/vitest.cjs +1 -1
  171. package/dist/vitest.mjs +1 -1
  172. package/dist/workspace/index.cjs +3 -1
  173. package/dist/workspace/index.d.cts +3 -3
  174. package/dist/workspace/index.d.mts +3 -3
  175. package/dist/workspace/index.mjs +2 -2
  176. package/dist/{workspace-Kl4NG9IH.cjs → workspace-BewAHZIo.cjs} +41 -6
  177. package/dist/workspace-BewAHZIo.cjs.map +1 -0
  178. package/dist/{workspace-Dh7rr7l4.mjs → workspace-C06_pW4g.mjs} +30 -7
  179. package/dist/workspace-C06_pW4g.mjs.map +1 -0
  180. package/package.json +13 -13
  181. package/dist/config-BEJYKd4s.cjs +0 -648
  182. package/dist/config-BEJYKd4s.cjs.map +0 -1
  183. package/dist/config-LKKCVCnC.mjs +0 -547
  184. package/dist/config-LKKCVCnC.mjs.map +0 -1
  185. package/dist/define-CHBIEc5j.mjs.map +0 -1
  186. package/dist/define-Cv_QvDrg.cjs.map +0 -1
  187. package/dist/deploy-ABBI0lf1.cjs.map +0 -1
  188. package/dist/deploy-BiMrP9i7.cjs.map +0 -1
  189. package/dist/deploy-CG8bfFij.mjs.map +0 -1
  190. package/dist/deploy-CGW-QY9D.mjs.map +0 -1
  191. package/dist/discover-BaT6bpnA.mjs.map +0 -1
  192. package/dist/discover-C_6COHEv.cjs.map +0 -1
  193. package/dist/home-DOzBaWaL.cjs +0 -33
  194. package/dist/home-DOzBaWaL.cjs.map +0 -1
  195. package/dist/home-GI7pJl1M.mjs +0 -22
  196. package/dist/home-GI7pJl1M.mjs.map +0 -1
  197. package/dist/index-BtXDZQuF.d.cts.map +0 -1
  198. package/dist/index-DrKzpHl-.d.mts.map +0 -1
  199. package/dist/resolve-B1T0hUaR.mjs +0 -170
  200. package/dist/resolve-B1T0hUaR.mjs.map +0 -1
  201. package/dist/resolve-xytBTtG4.cjs +0 -217
  202. package/dist/resolve-xytBTtG4.cjs.map +0 -1
  203. package/dist/runtime-CMwtq0AL.cjs +0 -163
  204. package/dist/runtime-CMwtq0AL.cjs.map +0 -1
  205. package/dist/runtime-DbakJS8u.mjs +0 -134
  206. package/dist/runtime-DbakJS8u.mjs.map +0 -1
  207. package/dist/sniffer-D_SYc_pO.cjs.map +0 -1
  208. package/dist/sniffer-aO_-s5vq.mjs.map +0 -1
  209. package/dist/terminal-D0f1u0LG.cjs +0 -576
  210. package/dist/terminal-D0f1u0LG.cjs.map +0 -1
  211. package/dist/terminal-gMxAo9Dj.mjs +0 -517
  212. package/dist/terminal-gMxAo9Dj.mjs.map +0 -1
  213. package/dist/types-CfLKGfC_.d.mts.map +0 -1
  214. package/dist/types-DIA7Li-J.d.cts.map +0 -1
  215. package/dist/workspace-Dh7rr7l4.mjs.map +0 -1
  216. package/dist/workspace-Kl4NG9IH.cjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_home = require("./home-DOzBaWaL.cjs");
2
+ const require_local = require("./local-BdH4XN0E.cjs");
3
3
  let node_path = require("node:path");
4
4
  let node_fs = require("node:fs");
5
5
  let node_fs_promises = require("node:fs/promises");
@@ -192,7 +192,7 @@ function folderName(path) {
192
192
  function getKeystoreDir(project) {
193
193
  if (!PROJECT_KEY.test(project.key)) throw new KeystoreProjectInvalid(project.key);
194
194
  const [namespace, name] = project.key.split("/");
195
- return (0, node_path.join)(project.home ?? require_home.gkmHome(), "keys", namespace, name);
195
+ return (0, node_path.join)(project.home ?? require_local.gkmHome(), "keys", namespace, name);
196
196
  }
197
197
  /** Get the path to a stage's encryption key. */
198
198
  function getKeyPath(stage, project) {
@@ -209,7 +209,7 @@ function legacyKeyPaths(stage, project) {
209
209
  key,
210
210
  home: project.home
211
211
  }));
212
- const homes = [.../* @__PURE__ */ new Set([project.home ?? require_home.gkmHome(), (0, node_path.join)((0, node_os.homedir)(), ".gkm")])];
212
+ const homes = [.../* @__PURE__ */ new Set([project.home ?? require_local.gkmHome(), (0, node_path.join)((0, node_os.homedir)(), ".gkm")])];
213
213
  const names = (project.legacy ?? []).filter(isLegacyName);
214
214
  return [...aliases, ...names.flatMap((name) => homes.map((home) => (0, node_path.join)(home, name, `${stage}.key`)))];
215
215
  }
@@ -507,4 +507,4 @@ Object.defineProperty(exports, "store_exports", {
507
507
  }
508
508
  });
509
509
 
510
- //# sourceMappingURL=store-KvNQwYaB.cjs.map
510
+ //# sourceMappingURL=store-CJF1Tqkq.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-KvNQwYaB.cjs","names":["kebabCase","scopedName","join","gkmHome","homedir","mkdir","randomBytes","writeFile","chmod","existsSync","copyFile","constants","readFile","basename","join","existsSync","readFile","mkdir","writeFile","readFileSync","randomBytes","createCipheriv","createDecipheriv"],"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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"}
1
+ {"version":3,"file":"store-CJF1Tqkq.cjs","names":["kebabCase","scopedName","join","gkmHome","homedir","mkdir","randomBytes","writeFile","chmod","existsSync","copyFile","constants","readFile","basename","join","existsSync","readFile","mkdir","writeFile","readFileSync","randomBytes","createCipheriv","createDecipheriv"],"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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"}
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { n as gkmHome } from "./home-GI7pJl1M.mjs";
2
+ import { u as gkmHome } from "./local-CWR4Y_kW.mjs";
3
3
  import { basename, join } from "node:path";
4
4
  import { constants, existsSync, readFileSync } from "node:fs";
5
5
  import { chmod, copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
@@ -412,4 +412,4 @@ function isRemoteStore(workspace, stage) {
412
412
  //#endregion
413
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
414
 
415
- //# sourceMappingURL=store-BS8A7NcA.mjs.map
415
+ //# sourceMappingURL=store-CLiHnqOY.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-BS8A7NcA.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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"}
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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"}
@@ -1,15 +1,15 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_define = require("../define-Cv_QvDrg.cjs");
3
- const require_runtime = require("../runtime-CMwtq0AL.cjs");
4
- const require_resolve = require("../resolve-xytBTtG4.cjs");
2
+ const require_define = require("../define-DzRz8FgH.cjs");
3
+ const require_resolve = require("../resolve-N_i4XNd-.cjs");
4
+ const require_provider = require("../provider-C9_-45d5.cjs");
5
5
  exports.DeployTargetNotYetSupported = require_define.DeployTargetNotYetSupported;
6
6
  exports.InvalidTargetOptions = require_resolve.InvalidTargetOptions;
7
- exports.ProviderRemoved = require_resolve.ProviderRemoved;
7
+ exports.ProviderRemoved = require_provider.ProviderRemoved;
8
8
  exports.TargetEntryInvalid = require_resolve.TargetEntryInvalid;
9
9
  exports.TargetPackageInvalid = require_resolve.TargetPackageInvalid;
10
- exports.TargetPackageNotFound = require_runtime.TargetPackageNotFound;
10
+ exports.TargetPackageNotFound = require_resolve.TargetPackageNotFound;
11
11
  exports.TargetRuntimeMismatch = require_resolve.TargetRuntimeMismatch;
12
- exports.TargetRuntimeUndeclared = require_runtime.TargetRuntimeUndeclared;
12
+ exports.TargetRuntimeUndeclared = require_resolve.TargetRuntimeUndeclared;
13
13
  exports.UnknownDeployTarget = require_define.UnknownDeployTarget;
14
14
  exports.defineTarget = require_define.defineTarget;
15
15
  exports.isDeployTarget = require_define.isDeployTarget;
@@ -1,5 +1,5 @@
1
- import { $ as DeployEventError, B as DeployFailure, G as DeployTargetCapabilities, H as DeployRuntime, J as TargetEvent, K as DeployTargetEntry, Q as DeployEvent, U as DeploySecrets, V as DeployPhaseContext, W as DeployTarget, X as TargetOptionsIssue, Y as TargetOptions, Z as TargetOptionsSchema, at as DeployResult, ct as CredentialKinds, et as DeployEventType, gt as StateStore, it as AppDeployResult, lt as CredentialProvider, nt as ResourceChange, ot as Credential, q as DeployTargetLogger, rt as ResourceVia, st as CredentialKind, tt as DeployPhase, ut as CredentialRequest, vt as StageSecrets, yt as DeployIdentity, z as AnyDeployTarget } from "../types-DIA7Li-J.cjs";
2
- import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-Dn_n-29S.cjs";
1
+ import { $ as DeployEventError, B as DeployFailure, G as DeployTargetCapabilities, H as DeployRuntime, J as TargetEvent, K as DeployTargetEntry, Q as DeployEvent, U as DeploySecrets, V as DeployPhaseContext, W as DeployTarget, X as TargetOptionsIssue, Y as TargetOptions, Z as TargetOptionsSchema, _t as StateStore, at as DeployResult, bt as DeployIdentity, ct as CredentialKind, dt as CredentialRequest, et as DeployEventType, it as AppDeployResult, lt as CredentialKinds, nt as ResourceChange, ot as AwsCredential, q as DeployTargetLogger, rt as ResourceVia, st as Credential, tt as DeployPhase, ut as CredentialProvider, yt as StageSecrets, z as AnyDeployTarget } from "../types-ByMWkXKP.cjs";
2
+ import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-BnD7Nn96.cjs";
3
3
  //#region src/target/define.d.ts
4
4
  /**
5
5
  * A deploy target, typed from what it is written with: the options from its
@@ -24,5 +24,5 @@ export declare function defineTarget<Options = undefined, Run = void>(target: De
24
24
  /** Whether `value` has the shape of a target. */
25
25
  export declare function isDeployTarget(value: unknown): value is AnyDeployTarget;
26
26
  //#endregion
27
- export { type AnyDeployTarget, type AppDeployResult, type Credential, type CredentialKind, type CredentialKinds, type CredentialProvider, type CredentialRequest, type DeployEvent, type DeployEventError, type DeployEventType, type DeployFailure, type DeployIdentity, type DeployPhase, type DeployPhaseContext, type DeployResult, type DeployRuntime, type DeploySecrets, type DeployTarget, type DeployTargetCapabilities, type DeployTargetEntry, type DeployTargetLogger, DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, type ResourceChange, type ResourceVia, type StageSecrets, type StateStore, TargetEntryInvalid, type TargetEvent, type TargetOptions, type TargetOptionsIssue, type TargetOptionsSchema, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget };
27
+ export { type AnyDeployTarget, type AppDeployResult, type AwsCredential, type Credential, type CredentialKind, type CredentialKinds, type CredentialProvider, type CredentialRequest, type DeployEvent, type DeployEventError, type DeployEventType, type DeployFailure, type DeployIdentity, type DeployPhase, type DeployPhaseContext, type DeployResult, type DeployRuntime, type DeploySecrets, type DeployTarget, type DeployTargetCapabilities, type DeployTargetEntry, type DeployTargetLogger, DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, type ResourceChange, type ResourceVia, type StageSecrets, type StateStore, TargetEntryInvalid, type TargetEvent, type TargetOptions, type TargetOptionsIssue, type TargetOptionsSchema, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget };
28
28
  //# sourceMappingURL=public.d.cts.map
@@ -1,5 +1,5 @@
1
- import { $ as DeployEventError, B as DeployFailure, G as DeployTargetCapabilities, H as DeployRuntime, J as TargetEvent, K as DeployTargetEntry, Q as DeployEvent, U as DeploySecrets, V as DeployPhaseContext, W as DeployTarget, X as TargetOptionsIssue, Y as TargetOptions, Z as TargetOptionsSchema, at as DeployResult, ct as CredentialKinds, et as DeployEventType, gt as StateStore, it as AppDeployResult, lt as CredentialProvider, nt as ResourceChange, ot as Credential, q as DeployTargetLogger, rt as ResourceVia, st as CredentialKind, tt as DeployPhase, ut as CredentialRequest, vt as StageSecrets, yt as DeployIdentity, z as AnyDeployTarget } from "../types-CfLKGfC_.mjs";
2
- import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-CXPlkZzs.mjs";
1
+ import { $ as DeployEventError, B as DeployFailure, G as DeployTargetCapabilities, H as DeployRuntime, J as TargetEvent, K as DeployTargetEntry, Q as DeployEvent, U as DeploySecrets, V as DeployPhaseContext, W as DeployTarget, X as TargetOptionsIssue, Y as TargetOptions, Z as TargetOptionsSchema, _t as StateStore, at as DeployResult, bt as DeployIdentity, ct as CredentialKind, dt as CredentialRequest, et as DeployEventType, it as AppDeployResult, lt as CredentialKinds, nt as ResourceChange, ot as AwsCredential, q as DeployTargetLogger, rt as ResourceVia, st as Credential, tt as DeployPhase, ut as CredentialProvider, yt as StageSecrets, z as AnyDeployTarget } from "../types-BCzMWBb6.mjs";
2
+ import { a as TargetRuntimeMismatch, c as DeployTargetNotYetSupported, i as TargetPackageInvalid, l as UnknownDeployTarget, n as InvalidTargetOptions, o as ProviderRemoved, r as TargetEntryInvalid, s as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-j-tmFL65.mjs";
3
3
  //#region src/target/define.d.ts
4
4
  /**
5
5
  * A deploy target, typed from what it is written with: the options from its
@@ -24,5 +24,5 @@ export declare function defineTarget<Options = undefined, Run = void>(target: De
24
24
  /** Whether `value` has the shape of a target. */
25
25
  export declare function isDeployTarget(value: unknown): value is AnyDeployTarget;
26
26
  //#endregion
27
- export { type AnyDeployTarget, type AppDeployResult, type Credential, type CredentialKind, type CredentialKinds, type CredentialProvider, type CredentialRequest, type DeployEvent, type DeployEventError, type DeployEventType, type DeployFailure, type DeployIdentity, type DeployPhase, type DeployPhaseContext, type DeployResult, type DeployRuntime, type DeploySecrets, type DeployTarget, type DeployTargetCapabilities, type DeployTargetEntry, type DeployTargetLogger, DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, type ResourceChange, type ResourceVia, type StageSecrets, type StateStore, TargetEntryInvalid, type TargetEvent, type TargetOptions, type TargetOptionsIssue, type TargetOptionsSchema, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget };
27
+ export { type AnyDeployTarget, type AppDeployResult, type AwsCredential, type Credential, type CredentialKind, type CredentialKinds, type CredentialProvider, type CredentialRequest, type DeployEvent, type DeployEventError, type DeployEventType, type DeployFailure, type DeployIdentity, type DeployPhase, type DeployPhaseContext, type DeployResult, type DeployRuntime, type DeploySecrets, type DeployTarget, type DeployTargetCapabilities, type DeployTargetEntry, type DeployTargetLogger, DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, type ResourceChange, type ResourceVia, type StageSecrets, type StateStore, TargetEntryInvalid, type TargetEvent, type TargetOptions, type TargetOptionsIssue, type TargetOptionsSchema, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget };
28
28
  //# sourceMappingURL=public.d.mts.map
@@ -1,4 +1,4 @@
1
- import { a as UnknownDeployTarget, i as DeployTargetNotYetSupported, n as isDeployTarget, t as defineTarget } from "../define-CHBIEc5j.mjs";
2
- import { i as TargetPackageNotFound, t as TargetRuntimeUndeclared } from "../runtime-DbakJS8u.mjs";
3
- import { i as TargetRuntimeMismatch, n as TargetEntryInvalid, r as TargetPackageInvalid, s as ProviderRemoved, t as InvalidTargetOptions } from "../resolve-B1T0hUaR.mjs";
1
+ import { a as UnknownDeployTarget, i as DeployTargetNotYetSupported, n as isDeployTarget, t as defineTarget } from "../define-DR8xGYfL.mjs";
2
+ import { i as TargetRuntimeMismatch, l as TargetPackageNotFound, n as TargetEntryInvalid, r as TargetPackageInvalid, s as TargetRuntimeUndeclared, t as InvalidTargetOptions } from "../resolve-DLkIXTct.mjs";
3
+ import { t as ProviderRemoved } from "../provider-q4lsYzYG.mjs";
4
4
  export { DeployTargetNotYetSupported, InvalidTargetOptions, ProviderRemoved, TargetEntryInvalid, TargetPackageInvalid, TargetPackageNotFound, TargetRuntimeMismatch, TargetRuntimeUndeclared, UnknownDeployTarget, defineTarget, isDeployTarget };
@@ -0,0 +1,88 @@
1
+ const require_sniffer = require("./sniffer-BSwaL-dX.cjs");
2
+ const require_credentials = require("./credentials-Bzbg23n8.cjs");
3
+ //#region src/deploy/terminal.ts
4
+ /**
5
+ * The credentials `gkm deploy` can ask a person for. CLI layer only.
6
+ *
7
+ * `deploy()` asks its `CredentialProvider` and stops with `MissingCredential`
8
+ * when the answer is nothing. At a terminal, `gkm deploy` would rather ask:
9
+ * this provider reads the environment and the stored login first, and only
10
+ * when both are empty prompts — and stores a Dokploy login it validated, so
11
+ * the next deploy does not ask again. With no terminal it answers nothing, and
12
+ * the deploy says what was missing instead of waiting on a prompt nobody sees.
13
+ */
14
+ const logger = require("./discover-DHdECueV.cjs").output;
15
+ /** What answers a deploy prompt without a terminal. */
16
+ const INSTEAD = "Please configure manually.";
17
+ /** The Dokploy URL typed at the prompt is not a URL. */
18
+ var DokployEndpointInvalid = class extends Error {
19
+ endpoint;
20
+ constructor(endpoint) {
21
+ super("Invalid URL format");
22
+ this.endpoint = endpoint;
23
+ this.name = "DokployEndpointInvalid";
24
+ }
25
+ };
26
+ /** Dokploy refused the token typed at the prompt; nothing was stored. */
27
+ var DokployTokenRejected = class extends Error {
28
+ endpoint;
29
+ constructor(endpoint) {
30
+ super("Invalid credentials. Please check your token.");
31
+ this.endpoint = endpoint;
32
+ this.name = "DokployTokenRejected";
33
+ }
34
+ };
35
+ /** Asks the person at the terminal for what nothing else supplied. */
36
+ function asking() {
37
+ return { async get(asked) {
38
+ if (!require_sniffer.canPrompt()) return void 0;
39
+ const request = asked;
40
+ if (request.kind === "dokploy") {
41
+ logger.log("\n📋 Dokploy credentials not found. Let's set them up.");
42
+ const endpoint = (await require_sniffer.prompt("Dokploy URL (e.g., https://dokploy.example.com): ", { instead: INSTEAD })).replace(/\/$/, "");
43
+ try {
44
+ new URL(endpoint);
45
+ } catch {
46
+ throw new DokployEndpointInvalid(endpoint);
47
+ }
48
+ logger.log(`\nGenerate a token at: ${endpoint}/settings/profile\n`);
49
+ const token = await require_sniffer.prompt("API Token: ", {
50
+ hidden: true,
51
+ instead: INSTEAD
52
+ });
53
+ logger.log("\nValidating credentials...");
54
+ if (!await require_sniffer.validateDokployToken(endpoint, token)) throw new DokployTokenRejected(endpoint);
55
+ await require_credentials.storeDokployCredentials(token, endpoint);
56
+ logger.log("✓ Credentials saved");
57
+ return {
58
+ endpoint,
59
+ token
60
+ };
61
+ }
62
+ if (request.kind !== "registry") return void 0;
63
+ logger.log(` Dokploy has no registry for ${request.url}. Let's create one.`);
64
+ return {
65
+ username: await require_sniffer.prompt("Registry username: ", { instead: INSTEAD }),
66
+ password: await require_sniffer.prompt("Registry password/token: ", {
67
+ hidden: true,
68
+ instead: INSTEAD
69
+ })
70
+ };
71
+ } };
72
+ }
73
+ /**
74
+ * The environment, then the stored login, then — at a terminal — the person
75
+ * running the command.
76
+ */
77
+ function terminalCredentials(base = require_credentials.storedCredentials()) {
78
+ return require_credentials.chainCredentials(base, asking());
79
+ }
80
+ //#endregion
81
+ Object.defineProperty(exports, "terminalCredentials", {
82
+ enumerable: true,
83
+ get: function() {
84
+ return terminalCredentials;
85
+ }
86
+ });
87
+
88
+ //# sourceMappingURL=terminal-Bs5E1oxm.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-Bs5E1oxm.cjs","names":["output","canPrompt","prompt","validateDokployToken","storeDokployCredentials","storedCredentials","chainCredentials"],"sources":["../src/deploy/terminal.ts"],"sourcesContent":["/**\n * The credentials `gkm deploy` can ask a person for. CLI layer only.\n *\n * `deploy()` asks its `CredentialProvider` and stops with `MissingCredential`\n * when the answer is nothing. At a terminal, `gkm deploy` would rather ask:\n * this provider reads the environment and the stored login first, and only\n * when both are empty prompts — and stores a Dokploy login it validated, so\n * the next deploy does not ask again. With no terminal it answers nothing, and\n * the deploy says what was missing instead of waiting on a prompt nobody sees.\n */\n\nimport { validateDokployToken } from '../auth';\nimport { storeDokployCredentials } from '../auth/credentials';\nimport { output } from '../output';\nimport { canPrompt, prompt } from '../prompt';\nimport {\n\ttype CredentialProvider,\n\ttype CredentialRequest,\n\tchainCredentials,\n\tstoredCredentials,\n} from './credentials';\n\nconst logger = output;\n\n/** What answers a deploy prompt without a terminal. */\nconst INSTEAD = 'Please configure manually.';\n\n/** The Dokploy URL typed at the prompt is not a URL. */\nexport class DokployEndpointInvalid extends Error {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid URL format');\n\t\tthis.name = 'DokployEndpointInvalid';\n\t}\n}\n\n/** Dokploy refused the token typed at the prompt; nothing was stored. */\nexport class DokployTokenRejected extends Error {\n\tconstructor(readonly endpoint: string) {\n\t\tsuper('Invalid credentials. Please check your token.');\n\t\tthis.name = 'DokployTokenRejected';\n\t}\n}\n\n/** Asks the person at the terminal for what nothing else supplied. */\nfunction asking(): CredentialProvider {\n\treturn {\n\t\tasync get(asked) {\n\t\t\tif (!canPrompt()) return undefined;\n\t\t\t// The union, so checking `kind` narrows it: `K` itself never is.\n\t\t\tconst request = asked as CredentialRequest;\n\n\t\t\tif (request.kind === 'dokploy') {\n\t\t\t\tlogger.log(\"\\n📋 Dokploy credentials not found. Let's set them up.\");\n\t\t\t\tconst endpoint = (\n\t\t\t\t\tawait prompt('Dokploy URL (e.g., https://dokploy.example.com): ', {\n\t\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t\t})\n\t\t\t\t).replace(/\\/$/, '');\n\n\t\t\t\ttry {\n\t\t\t\t\tnew URL(endpoint);\n\t\t\t\t} catch {\n\t\t\t\t\tthrow new DokployEndpointInvalid(endpoint);\n\t\t\t\t}\n\n\t\t\t\tlogger.log(`\\nGenerate a token at: ${endpoint}/settings/profile\\n`);\n\t\t\t\tconst token = await prompt('API Token: ', {\n\t\t\t\t\thidden: true,\n\t\t\t\t\tinstead: INSTEAD,\n\t\t\t\t});\n\n\t\t\t\tlogger.log('\\nValidating credentials...');\n\t\t\t\tif (!(await validateDokployToken(endpoint, token))) {\n\t\t\t\t\tthrow new DokployTokenRejected(endpoint);\n\t\t\t\t}\n\n\t\t\t\tawait storeDokployCredentials(token, endpoint);\n\t\t\t\tlogger.log('✓ Credentials saved');\n\t\t\t\treturn { endpoint, token } as never;\n\t\t\t}\n\n\t\t\t// AWS is a profile or a role, set up with the AWS CLI — not keys\n\t\t\t// typed into a deploy.\n\t\t\tif (request.kind !== 'registry') return undefined;\n\n\t\t\tlogger.log(\n\t\t\t\t` Dokploy has no registry for ${request.url}. Let's create one.`,\n\t\t\t);\n\t\t\tconst username = await prompt('Registry username: ', {\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\tconst password = await prompt('Registry password/token: ', {\n\t\t\t\thidden: true,\n\t\t\t\tinstead: INSTEAD,\n\t\t\t});\n\t\t\treturn { username, password } as never;\n\t\t},\n\t};\n}\n\n/**\n * The environment, then the stored login, then — at a terminal — the person\n * running the command.\n */\nexport function terminalCredentials(\n\tbase: CredentialProvider = storedCredentials(),\n): CredentialProvider {\n\treturn chainCredentials(base, asking());\n}\n"],"mappings":";;;;;;;;;;;;;AAsBA,MAAM,0CAASA,CAAAA,CAAAA;;AAGf,MAAM,UAAU;;AAGhB,IAAa,yBAAb,cAA4C,MAAM;CAC5B;CAArB,YAAY,UAA2B;EACtC,MAAM,oBAAoB;EADN,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,IAAa,uBAAb,cAA0C,MAAM;CAC1B;CAArB,YAAY,UAA2B;EACtC,MAAM,+CAA+C;EADjC,KAAA,WAAA;EAEpB,KAAK,OAAO;CACb;AACD;;AAGA,SAAS,SAA6B;CACrC,OAAO,EACN,MAAM,IAAI,OAAO;EAChB,IAAI,CAACC,gBAAAA,UAAU,GAAG,OAAO,KAAA;EAEzB,MAAM,UAAU;EAEhB,IAAI,QAAQ,SAAS,WAAW;GAC/B,OAAO,IAAI,wDAAwD;GACnE,MAAM,YACL,MAAMC,gBAAAA,OAAO,qDAAqD,EACjE,SAAS,QACV,CAAC,EAAA,CACA,QAAQ,OAAO,EAAE;GAEnB,IAAI;IACH,IAAI,IAAI,QAAQ;GACjB,QAAQ;IACP,MAAM,IAAI,uBAAuB,QAAQ;GAC1C;GAEA,OAAO,IAAI,0BAA0B,SAAS,oBAAoB;GAClE,MAAM,QAAQ,MAAMA,gBAAAA,OAAO,eAAe;IACzC,QAAQ;IACR,SAAS;GACV,CAAC;GAED,OAAO,IAAI,6BAA6B;GACxC,IAAI,CAAE,MAAMC,gBAAAA,qBAAqB,UAAU,KAAK,GAC/C,MAAM,IAAI,qBAAqB,QAAQ;GAGxC,MAAMC,oBAAAA,wBAAwB,OAAO,QAAQ;GAC7C,OAAO,IAAI,qBAAqB;GAChC,OAAO;IAAE;IAAU;GAAM;EAC1B;EAIA,IAAI,QAAQ,SAAS,YAAY,OAAO,KAAA;EAExC,OAAO,IACN,kCAAkC,QAAQ,IAAI,oBAC/C;EAQA,OAAO;GAAE,UAAA,MAPcF,gBAAAA,OAAO,uBAAuB,EACpD,SAAS,QACV,CAAC;GAKkB,UAAA,MAJIA,gBAAAA,OAAO,6BAA6B;IAC1D,QAAQ;IACR,SAAS;GACV,CAAC;EAC2B;CAC7B,EACD;AACD;;;;;AAMA,SAAgB,oBACf,OAA2BG,oBAAAA,kBAAkB,GACxB;CACrB,OAAOC,oBAAAA,iBAAiB,MAAM,OAAO,CAAC;AACvC"}