@geekmidas/cli 9.0.2 → 10.0.0-alpha.1

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 (390) hide show
  1. package/README.md +66 -122
  2. package/dist/{CachedStateProvider-BdX7klRc.cjs → CachedStateProvider-BLo7zv7S.cjs} +1 -1
  3. package/dist/CachedStateProvider-BxoAm-B2.mjs +3 -0
  4. package/dist/{CachedStateProvider-klLWZXWF.cjs → CachedStateProvider-CqPdW7CB.cjs} +1 -1
  5. package/dist/{CachedStateProvider-klLWZXWF.cjs.map → CachedStateProvider-CqPdW7CB.cjs.map} +1 -1
  6. package/dist/{CachedStateProvider-CzyFct42.mjs → CachedStateProvider-DTniehMP.mjs} +1 -1
  7. package/dist/{CachedStateProvider-CzyFct42.mjs.map → CachedStateProvider-DTniehMP.mjs.map} +1 -1
  8. package/dist/{HostingerProvider-B2ZYz7Yc.mjs → HostingerProvider-Cft7lEYr.mjs} +4 -3
  9. package/dist/HostingerProvider-Cft7lEYr.mjs.map +1 -0
  10. package/dist/{HostingerProvider-CrHfsgz9.cjs → HostingerProvider-h3EbEp-Z.cjs} +4 -3
  11. package/dist/HostingerProvider-h3EbEp-Z.cjs.map +1 -0
  12. package/dist/{LocalStateProvider-CP49rjzt.mjs → LocalStateProvider-Bn6tS-oL.mjs} +1 -1
  13. package/dist/{LocalStateProvider-CP49rjzt.mjs.map → LocalStateProvider-Bn6tS-oL.mjs.map} +1 -1
  14. package/dist/{LocalStateProvider-CalzNfNA.cjs → LocalStateProvider-Db9SEjyH.cjs} +1 -1
  15. package/dist/{LocalStateProvider-CalzNfNA.cjs.map → LocalStateProvider-Db9SEjyH.cjs.map} +1 -1
  16. package/dist/{Route53Provider-tlgBzrRQ.mjs → Route53Provider-RdpUDJQk.mjs} +1 -1
  17. package/dist/{Route53Provider-tlgBzrRQ.mjs.map → Route53Provider-RdpUDJQk.mjs.map} +1 -1
  18. package/dist/{Route53Provider-DwgXiLxt.cjs → Route53Provider-di2fMM5N.cjs} +1 -1
  19. package/dist/{Route53Provider-DwgXiLxt.cjs.map → Route53Provider-di2fMM5N.cjs.map} +1 -1
  20. package/dist/{SSMStateProvider-BlrTiV5m.mjs → SSMStateProvider-BRt2py-A.mjs} +1 -1
  21. package/dist/{SSMStateProvider-BlrTiV5m.mjs.map → SSMStateProvider-BRt2py-A.mjs.map} +1 -1
  22. package/dist/{SSMStateProvider-Bt6r8CTL.cjs → SSMStateProvider-DyX8s9HI.cjs} +1 -1
  23. package/dist/{SSMStateProvider-Bt6r8CTL.cjs.map → SSMStateProvider-DyX8s9HI.cjs.map} +1 -1
  24. package/dist/{backup-provisioner-Xunq-p_8.mjs → backup-provisioner-Bgj5Vi_L.mjs} +2 -2
  25. package/dist/{backup-provisioner-Xunq-p_8.mjs.map → backup-provisioner-Bgj5Vi_L.mjs.map} +1 -1
  26. package/dist/{backup-provisioner-B3HBN-2k.cjs → backup-provisioner-BrsHwYG0.cjs} +2 -2
  27. package/dist/{backup-provisioner-B3HBN-2k.cjs.map → backup-provisioner-BrsHwYG0.cjs.map} +1 -1
  28. package/dist/{bundler-Dufq3uvI.cjs → bundler-BY1OmV1e.cjs} +44 -14
  29. package/dist/bundler-BY1OmV1e.cjs.map +1 -0
  30. package/dist/{bundler-DkA4qzUD.mjs → bundler-Be8Zg4Wd.mjs} +45 -15
  31. package/dist/bundler-Be8Zg4Wd.mjs.map +1 -0
  32. package/dist/config-D8o6I_7Z.mjs +469 -0
  33. package/dist/config-D8o6I_7Z.mjs.map +1 -0
  34. package/dist/config-DbPE1KXF.cjs +535 -0
  35. package/dist/config-DbPE1KXF.cjs.map +1 -0
  36. package/dist/config.cjs +3 -2
  37. package/dist/config.d.cts +2 -2
  38. package/dist/config.d.cts.map +1 -1
  39. package/dist/config.d.mts +2 -2
  40. package/dist/config.d.mts.map +1 -1
  41. package/dist/config.mjs +3 -2
  42. package/dist/{credentials-s1kLcIzK.mjs → credentials-B5zRHdAl.mjs} +21 -6
  43. package/dist/credentials-B5zRHdAl.mjs.map +1 -0
  44. package/dist/{credentials-C8DWtnMY.cjs → credentials-BapzjyV5.cjs} +26 -5
  45. package/dist/credentials-BapzjyV5.cjs.map +1 -0
  46. package/dist/credentials-Dh0Q6HzY.mjs +2548 -0
  47. package/dist/credentials-Dh0Q6HzY.mjs.map +1 -0
  48. package/dist/credentials-K961ThG3.cjs +2680 -0
  49. package/dist/credentials-K961ThG3.cjs.map +1 -0
  50. package/dist/deploy/sniffer-routes-worker.cjs +6 -2
  51. package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
  52. package/dist/deploy/sniffer-routes-worker.mjs +6 -2
  53. package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
  54. package/dist/discover-CAlFgYec.mjs +229 -0
  55. package/dist/discover-CAlFgYec.mjs.map +1 -0
  56. package/dist/discover-DTmFeloL.cjs +271 -0
  57. package/dist/discover-DTmFeloL.cjs.map +1 -0
  58. package/dist/dokploy-api-BS2xnJLZ.mjs +3 -0
  59. package/dist/{dokploy-api-CqOIKHme.cjs → dokploy-api-BsVH0y_s.cjs} +141 -32
  60. package/dist/dokploy-api-BsVH0y_s.cjs.map +1 -0
  61. package/dist/dokploy-api-BuxDRo3l.cjs +3 -0
  62. package/dist/{dokploy-api-sfZFlWuE.mjs → dokploy-api-aTFPz7_4.mjs} +141 -32
  63. package/dist/dokploy-api-aTFPz7_4.mjs.map +1 -0
  64. package/dist/emit-CbcRolsa.cjs +236 -0
  65. package/dist/emit-CbcRolsa.cjs.map +1 -0
  66. package/dist/emit-CiZS_5Ub.mjs +206 -0
  67. package/dist/emit-CiZS_5Ub.mjs.map +1 -0
  68. package/dist/{encryption-MfXAiFTm.cjs → encryption-C6jaU0hE.cjs} +1 -1
  69. package/dist/{encryption-MfXAiFTm.cjs.map → encryption-C6jaU0hE.cjs.map} +1 -1
  70. package/dist/{encryption-BVS7Vmzs.cjs → encryption-CHzzeR8a.cjs} +1 -1
  71. package/dist/encryption-CQUncu7v.mjs +3 -0
  72. package/dist/{encryption-CO5Q4Bhf.mjs → encryption-D-abyhZx.mjs} +1 -1
  73. package/dist/{encryption-CO5Q4Bhf.mjs.map → encryption-D-abyhZx.mjs.map} +1 -1
  74. package/dist/fullstack-secrets-C-GGohke.cjs +111 -0
  75. package/dist/fullstack-secrets-C-GGohke.cjs.map +1 -0
  76. package/dist/fullstack-secrets-D1o9-G5l.mjs +87 -0
  77. package/dist/fullstack-secrets-D1o9-G5l.mjs.map +1 -0
  78. package/dist/{fullstack-secrets-BYzi6Hwl.cjs → generator-CNzBw7g5.cjs} +1 -114
  79. package/dist/generator-CNzBw7g5.cjs.map +1 -0
  80. package/dist/{fullstack-secrets-l8s3II_H.mjs → generator-VE2JrFlY.mjs} +2 -85
  81. package/dist/generator-VE2JrFlY.mjs.map +1 -0
  82. package/dist/index-BThUzEIp.d.mts +175 -0
  83. package/dist/index-BThUzEIp.d.mts.map +1 -0
  84. package/dist/index-CHB4tSzX.d.cts +175 -0
  85. package/dist/index-CHB4tSzX.d.cts.map +1 -0
  86. package/dist/index.cjs +2530 -1810
  87. package/dist/index.cjs.map +1 -1
  88. package/dist/index.mjs +2506 -1786
  89. package/dist/index.mjs.map +1 -1
  90. package/dist/{openapi-Cfwi0fqF.mjs → openapi-CgpMWMmM.mjs} +174 -95
  91. package/dist/openapi-CgpMWMmM.mjs.map +1 -0
  92. package/dist/{openapi-BzNOC7_i.cjs → openapi-CljONd_M.cjs} +175 -108
  93. package/dist/openapi-CljONd_M.cjs.map +1 -0
  94. package/dist/{openapi-react-query-C4iIvpPf.mjs → openapi-react-query-Byltvm_7.mjs} +2 -2
  95. package/dist/openapi-react-query-Byltvm_7.mjs.map +1 -0
  96. package/dist/{openapi-react-query-CYWik72n.cjs → openapi-react-query-CmJ1xDbn.cjs} +2 -2
  97. package/dist/openapi-react-query-CmJ1xDbn.cjs.map +1 -0
  98. package/dist/openapi-react-query.cjs +1 -1
  99. package/dist/openapi-react-query.mjs +1 -1
  100. package/dist/openapi.cjs +4 -3
  101. package/dist/openapi.d.cts +1 -1
  102. package/dist/openapi.d.mts +1 -1
  103. package/dist/openapi.mjs +4 -3
  104. package/dist/reconcile/public.cjs +9 -0
  105. package/dist/reconcile/public.d.cts +125 -0
  106. package/dist/reconcile/public.d.cts.map +1 -0
  107. package/dist/reconcile/public.d.mts +125 -0
  108. package/dist/reconcile/public.d.mts.map +1 -0
  109. package/dist/reconcile/public.mjs +4 -0
  110. package/dist/{reconcile-B2FoJE0a.mjs → reconcile-CFs8C_1a.mjs} +5 -4
  111. package/dist/reconcile-CFs8C_1a.mjs.map +1 -0
  112. package/dist/{reconcile-BzqKJ2Rt.cjs → reconcile-D0pimNiy.cjs} +5 -4
  113. package/dist/reconcile-D0pimNiy.cjs.map +1 -0
  114. package/dist/setup-CJiQM2pX.cjs +473 -0
  115. package/dist/setup-CJiQM2pX.cjs.map +1 -0
  116. package/dist/setup-DMtU3p41.cjs +11 -0
  117. package/dist/setup-DOsYueeQ.mjs +443 -0
  118. package/dist/setup-DOsYueeQ.mjs.map +1 -0
  119. package/dist/setup-DSutDAlt.mjs +11 -0
  120. package/dist/{storage-B1wvztiJ.cjs → storage-BJmzYxt8.cjs} +1 -1
  121. package/dist/{storage-B1wvztiJ.cjs.map → storage-BJmzYxt8.cjs.map} +1 -1
  122. package/dist/{storage-DOEtT2Hr.cjs → storage-BM9hGpgv.cjs} +1 -1
  123. package/dist/{storage-dbb9RyBl.mjs → storage-Bi24WfoW.mjs} +1 -1
  124. package/dist/{storage-dbb9RyBl.mjs.map → storage-Bi24WfoW.mjs.map} +1 -1
  125. package/dist/{storage-Cs4WBsc4.mjs → storage-CYlGwWTj.mjs} +1 -1
  126. package/dist/{sync-CSooqRQ8.mjs → sync-CmEuNZsB.mjs} +2 -2
  127. package/dist/{sync-CSooqRQ8.mjs.map → sync-CmEuNZsB.mjs.map} +1 -1
  128. package/dist/sync-D306yOWu.cjs +4 -0
  129. package/dist/{sync-o_CtEbvR.cjs → sync-SizaChmC.cjs} +2 -2
  130. package/dist/{sync-o_CtEbvR.cjs.map → sync-SizaChmC.cjs.map} +1 -1
  131. package/dist/{sync-B5UHXwRM.mjs → sync-hOqAWHR6.mjs} +1 -1
  132. package/dist/{index-Bw548Bta.d.mts → types-C-arnQ1r.d.mts} +590 -143
  133. package/dist/types-C-arnQ1r.d.mts.map +1 -0
  134. package/dist/{index-DTpkeFb8.d.cts → types-abJQuk7m.d.cts} +590 -143
  135. package/dist/types-abJQuk7m.d.cts.map +1 -0
  136. package/dist/workspace/index.cjs +5 -1
  137. package/dist/workspace/index.d.cts +3 -3
  138. package/dist/workspace/index.d.mts +3 -3
  139. package/dist/workspace/index.mjs +2 -2
  140. package/dist/{workspace-D-37Ylqo.mjs → workspace-7FqprxuJ.mjs} +117 -30
  141. package/dist/workspace-7FqprxuJ.mjs.map +1 -0
  142. package/dist/workspace-8acupeaI.cjs +9 -0
  143. package/dist/{workspace-CQTT7HJQ.cjs → workspace-XvqO9jIv.cjs} +139 -28
  144. package/dist/workspace-XvqO9jIv.cjs.map +1 -0
  145. package/dist/workspace-yP5AHnrn.mjs +8 -0
  146. package/package.json +26 -9
  147. package/CHANGELOG.md +0 -650
  148. package/FUNCTION_CRON_SUPPORT.md +0 -266
  149. package/dist/CachedStateProvider-GcGziXh1.mjs +0 -3
  150. package/dist/HostingerProvider-B2ZYz7Yc.mjs.map +0 -1
  151. package/dist/HostingerProvider-CrHfsgz9.cjs.map +0 -1
  152. package/dist/bundler-DkA4qzUD.mjs.map +0 -1
  153. package/dist/bundler-Dufq3uvI.cjs.map +0 -1
  154. package/dist/config-Bbu1JKvf.mjs +0 -211
  155. package/dist/config-Bbu1JKvf.mjs.map +0 -1
  156. package/dist/config-DujV8DbR.cjs +0 -253
  157. package/dist/config-DujV8DbR.cjs.map +0 -1
  158. package/dist/credentials-C8DWtnMY.cjs.map +0 -1
  159. package/dist/credentials-s1kLcIzK.mjs.map +0 -1
  160. package/dist/dokploy-api-BPYkG-ZZ.cjs +0 -3
  161. package/dist/dokploy-api-BiSzWM4v.mjs +0 -3
  162. package/dist/dokploy-api-CqOIKHme.cjs.map +0 -1
  163. package/dist/dokploy-api-sfZFlWuE.mjs.map +0 -1
  164. package/dist/encryption-Bd_m4q3g.mjs +0 -3
  165. package/dist/fullstack-secrets-BYzi6Hwl.cjs.map +0 -1
  166. package/dist/fullstack-secrets-l8s3II_H.mjs.map +0 -1
  167. package/dist/index-Bw548Bta.d.mts.map +0 -1
  168. package/dist/index-DTpkeFb8.d.cts.map +0 -1
  169. package/dist/openapi-BzNOC7_i.cjs.map +0 -1
  170. package/dist/openapi-Cfwi0fqF.mjs.map +0 -1
  171. package/dist/openapi-react-query-C4iIvpPf.mjs.map +0 -1
  172. package/dist/openapi-react-query-CYWik72n.cjs.map +0 -1
  173. package/dist/reconcile-B2FoJE0a.mjs.map +0 -1
  174. package/dist/reconcile-BzqKJ2Rt.cjs.map +0 -1
  175. package/dist/setup-DDXUXzzl.mjs +0 -765
  176. package/dist/setup-DDXUXzzl.mjs.map +0 -1
  177. package/dist/setup-DLwhfyt-.cjs +0 -8
  178. package/dist/setup-Dh-B7YTX.mjs +0 -8
  179. package/dist/setup-WZ-xqAQO.cjs +0 -849
  180. package/dist/setup-WZ-xqAQO.cjs.map +0 -1
  181. package/dist/sync-BLA94rgC.cjs +0 -4
  182. package/dist/types-B3GQUg3k.d.mts +0 -280
  183. package/dist/types-B3GQUg3k.d.mts.map +0 -1
  184. package/dist/types-BWQDiooe.d.cts +0 -280
  185. package/dist/types-BWQDiooe.d.cts.map +0 -1
  186. package/dist/workspace-CQTT7HJQ.cjs.map +0 -1
  187. package/dist/workspace-D-37Ylqo.mjs.map +0 -1
  188. package/docs/OPENAPI_TYPESCRIPT_DESIGN.md +0 -408
  189. package/docs/dev-server-resilience-design.md +0 -189
  190. package/docs/manifest-refactor-design.md +0 -287
  191. package/examples/cron-example.ts +0 -45
  192. package/examples/env.ts +0 -33
  193. package/examples/function-example.ts +0 -40
  194. package/examples/gkm.config.json +0 -22
  195. package/examples/gkm.config.ts +0 -14
  196. package/examples/gkm.minimal.config.json +0 -7
  197. package/examples/gkm.production.config.json +0 -27
  198. package/examples/logger.ts +0 -10
  199. package/scripts/sync-versions.ts +0 -108
  200. package/src/__tests__/EndpointGenerator.hooks.spec.ts +0 -204
  201. package/src/__tests__/config.spec.ts +0 -422
  202. package/src/__tests__/loadEnvFiles.spec.ts +0 -131
  203. package/src/__tests__/normalizeHooksConfig.spec.ts +0 -63
  204. package/src/__tests__/openapi-react-query.spec.ts +0 -506
  205. package/src/__tests__/openapi.spec.ts +0 -625
  206. package/src/__tests__/test-helpers.ts +0 -181
  207. package/src/auth/__tests__/credentials.spec.ts +0 -331
  208. package/src/auth/__tests__/index.spec.ts +0 -237
  209. package/src/auth/credentials.ts +0 -286
  210. package/src/auth/index.ts +0 -233
  211. package/src/build/__tests__/bundler.spec.ts +0 -446
  212. package/src/build/__tests__/endpoint-analyzer.spec.ts +0 -623
  213. package/src/build/__tests__/handler-templates.spec.ts +0 -1287
  214. package/src/build/__tests__/index-new.spec.ts +0 -550
  215. package/src/build/__tests__/manifests.spec.ts +0 -583
  216. package/src/build/__tests__/partitions.spec.ts +0 -110
  217. package/src/build/__tests__/workspace-build.spec.ts +0 -215
  218. package/src/build/bundler.ts +0 -256
  219. package/src/build/endpoint-analyzer.ts +0 -236
  220. package/src/build/handler-templates.ts +0 -1253
  221. package/src/build/index.ts +0 -616
  222. package/src/build/manifests.ts +0 -285
  223. package/src/build/partitions.ts +0 -58
  224. package/src/build/providerResolver.ts +0 -184
  225. package/src/build/types.ts +0 -104
  226. package/src/config.ts +0 -330
  227. package/src/credentials/__tests__/fullDockerPorts.spec.ts +0 -144
  228. package/src/credentials/__tests__/helpers.ts +0 -112
  229. package/src/credentials/__tests__/prepareEntryCredentials.spec.ts +0 -125
  230. package/src/credentials/__tests__/readonlyDockerPorts.spec.ts +0 -190
  231. package/src/credentials/__tests__/workspaceCredentials.spec.ts +0 -209
  232. package/src/credentials/index.ts +0 -843
  233. package/src/debug.ts +0 -68
  234. package/src/deploy/CachedStateProvider.ts +0 -86
  235. package/src/deploy/LocalStateProvider.ts +0 -57
  236. package/src/deploy/SSMStateProvider.ts +0 -117
  237. package/src/deploy/StateProvider.ts +0 -175
  238. package/src/deploy/__tests__/CachedStateProvider.spec.ts +0 -235
  239. package/src/deploy/__tests__/HostingerProvider.spec.ts +0 -347
  240. package/src/deploy/__tests__/LocalStateProvider.spec.ts +0 -130
  241. package/src/deploy/__tests__/Route53Provider.spec.ts +0 -425
  242. package/src/deploy/__tests__/SSMStateProvider.spec.ts +0 -201
  243. package/src/deploy/__tests__/__fixtures__/entry-apps/async-entry.ts +0 -24
  244. package/src/deploy/__tests__/__fixtures__/entry-apps/nested-config-entry.ts +0 -24
  245. package/src/deploy/__tests__/__fixtures__/entry-apps/no-env-entry.ts +0 -12
  246. package/src/deploy/__tests__/__fixtures__/entry-apps/simple-entry.ts +0 -14
  247. package/src/deploy/__tests__/__fixtures__/entry-apps/throwing-entry.ts +0 -16
  248. package/src/deploy/__tests__/__fixtures__/env-parsers/non-function-export.ts +0 -10
  249. package/src/deploy/__tests__/__fixtures__/env-parsers/optional-env-parser.ts +0 -21
  250. package/src/deploy/__tests__/__fixtures__/env-parsers/parseable-env-parser.ts +0 -18
  251. package/src/deploy/__tests__/__fixtures__/env-parsers/throwing-env-parser.ts +0 -16
  252. package/src/deploy/__tests__/__fixtures__/env-parsers/valid-env-parser.ts +0 -16
  253. package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/auth.ts +0 -16
  254. package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/health.ts +0 -13
  255. package/src/deploy/__tests__/__fixtures__/route-apps/endpoints/users.ts +0 -15
  256. package/src/deploy/__tests__/__fixtures__/route-apps/services.ts +0 -55
  257. package/src/deploy/__tests__/backup-provisioner.spec.ts +0 -428
  258. package/src/deploy/__tests__/createDnsProvider.spec.ts +0 -195
  259. package/src/deploy/__tests__/createStateProvider.spec.ts +0 -126
  260. package/src/deploy/__tests__/dns-orchestration.spec.ts +0 -192
  261. package/src/deploy/__tests__/dns-verification.spec.ts +0 -229
  262. package/src/deploy/__tests__/docker.spec.ts +0 -149
  263. package/src/deploy/__tests__/dokploy-api.spec.ts +0 -697
  264. package/src/deploy/__tests__/dokploy.spec.ts +0 -435
  265. package/src/deploy/__tests__/domain.spec.ts +0 -235
  266. package/src/deploy/__tests__/env-resolver.spec.ts +0 -903
  267. package/src/deploy/__tests__/index.spec.ts +0 -1091
  268. package/src/deploy/__tests__/init.spec.ts +0 -793
  269. package/src/deploy/__tests__/secrets.spec.ts +0 -303
  270. package/src/deploy/__tests__/sniffer.spec.ts +0 -838
  271. package/src/deploy/__tests__/state-e2e.spec.ts +0 -387
  272. package/src/deploy/__tests__/state.spec.ts +0 -876
  273. package/src/deploy/__tests__/undeploy.spec.ts +0 -758
  274. package/src/deploy/backup-provisioner.ts +0 -316
  275. package/src/deploy/dns/DnsProvider.ts +0 -201
  276. package/src/deploy/dns/HostingerProvider.ts +0 -174
  277. package/src/deploy/dns/Route53Provider.ts +0 -340
  278. package/src/deploy/dns/hostinger-api.ts +0 -261
  279. package/src/deploy/dns/index.ts +0 -627
  280. package/src/deploy/docker.ts +0 -260
  281. package/src/deploy/dokploy-api.ts +0 -1101
  282. package/src/deploy/dokploy.ts +0 -177
  283. package/src/deploy/domain.ts +0 -134
  284. package/src/deploy/env-resolver.ts +0 -305
  285. package/src/deploy/index.ts +0 -2094
  286. package/src/deploy/init.ts +0 -427
  287. package/src/deploy/secrets.ts +0 -187
  288. package/src/deploy/sniffer-envkit-patch.ts +0 -61
  289. package/src/deploy/sniffer-hooks.ts +0 -57
  290. package/src/deploy/sniffer-loader.ts +0 -28
  291. package/src/deploy/sniffer-routes-worker.ts +0 -104
  292. package/src/deploy/sniffer-worker.ts +0 -75
  293. package/src/deploy/sniffer.ts +0 -578
  294. package/src/deploy/state-commands.ts +0 -274
  295. package/src/deploy/state.ts +0 -482
  296. package/src/deploy/types.ts +0 -102
  297. package/src/deploy/undeploy.ts +0 -407
  298. package/src/dev/__tests__/entry-integration.spec.ts +0 -347
  299. package/src/dev/__tests__/entry.spec.ts +0 -144
  300. package/src/dev/__tests__/index.spec.ts +0 -2084
  301. package/src/dev/index.ts +0 -1649
  302. package/src/docker/__tests__/compose.spec.ts +0 -1142
  303. package/src/docker/__tests__/templates.spec.ts +0 -694
  304. package/src/docker/compose.ts +0 -808
  305. package/src/docker/index.ts +0 -523
  306. package/src/docker/templates.ts +0 -1067
  307. package/src/exec/index.ts +0 -118
  308. package/src/generators/CronGenerator.ts +0 -105
  309. package/src/generators/EndpointGenerator.ts +0 -954
  310. package/src/generators/FunctionGenerator.ts +0 -117
  311. package/src/generators/Generator.ts +0 -147
  312. package/src/generators/OpenApiTsGenerator.ts +0 -821
  313. package/src/generators/QueueGenerator.ts +0 -245
  314. package/src/generators/SubscriberGenerator.ts +0 -282
  315. package/src/generators/TopicGenerator.ts +0 -45
  316. package/src/generators/__tests__/CronGenerator.spec.ts +0 -445
  317. package/src/generators/__tests__/EndpointGenerator.spec.ts +0 -544
  318. package/src/generators/__tests__/FunctionGenerator.spec.ts +0 -256
  319. package/src/generators/__tests__/Generator.bustCache.spec.ts +0 -72
  320. package/src/generators/__tests__/OpenApiTsGenerator.registry.spec.ts +0 -98
  321. package/src/generators/__tests__/OpenApiTsGenerator.responseType.spec.ts +0 -56
  322. package/src/generators/__tests__/QueueGenerator.spec.ts +0 -141
  323. package/src/generators/__tests__/SubscriberGenerator.spec.ts +0 -410
  324. package/src/generators/index.ts +0 -11
  325. package/src/index.ts +0 -972
  326. package/src/init/__tests__/generators.spec.ts +0 -1070
  327. package/src/init/__tests__/init.spec.ts +0 -577
  328. package/src/init/__tests__/utils.spec.ts +0 -104
  329. package/src/init/generators/auth.ts +0 -225
  330. package/src/init/generators/config.ts +0 -316
  331. package/src/init/generators/docker.ts +0 -388
  332. package/src/init/generators/env.ts +0 -62
  333. package/src/init/generators/index.ts +0 -6
  334. package/src/init/generators/mobile-expo.ts +0 -576
  335. package/src/init/generators/models.ts +0 -153
  336. package/src/init/generators/monorepo.ts +0 -475
  337. package/src/init/generators/package.ts +0 -95
  338. package/src/init/generators/source.ts +0 -15
  339. package/src/init/generators/test.ts +0 -178
  340. package/src/init/generators/ui.ts +0 -1473
  341. package/src/init/generators/web-tanstack.ts +0 -348
  342. package/src/init/generators/web.ts +0 -363
  343. package/src/init/index.ts +0 -542
  344. package/src/init/templates/api.ts +0 -404
  345. package/src/init/templates/index.ts +0 -342
  346. package/src/init/templates/minimal.ts +0 -207
  347. package/src/init/templates/serverless.ts +0 -148
  348. package/src/init/templates/worker.ts +0 -216
  349. package/src/init/utils.ts +0 -197
  350. package/src/init/versions.ts +0 -52
  351. package/src/openapi-react-query.ts +0 -288
  352. package/src/openapi.ts +0 -259
  353. package/src/secrets/__tests__/encryption.spec.ts +0 -226
  354. package/src/secrets/__tests__/generator.spec.ts +0 -575
  355. package/src/secrets/__tests__/index.spec.ts +0 -91
  356. package/src/secrets/__tests__/reconcile.spec.ts +0 -123
  357. package/src/secrets/__tests__/storage.spec.ts +0 -645
  358. package/src/secrets/encryption.ts +0 -91
  359. package/src/secrets/generator.ts +0 -329
  360. package/src/secrets/index.ts +0 -417
  361. package/src/secrets/keystore.ts +0 -144
  362. package/src/secrets/reconcile.ts +0 -53
  363. package/src/secrets/storage.ts +0 -337
  364. package/src/secrets/sync.ts +0 -136
  365. package/src/secrets/types.ts +0 -70
  366. package/src/setup/__tests__/auto-setup.spec.ts +0 -147
  367. package/src/setup/__tests__/reconcile-secrets.spec.ts +0 -439
  368. package/src/setup/fullstack-secrets.ts +0 -140
  369. package/src/setup/index.ts +0 -411
  370. package/src/test/__tests__/__fixtures__/workspace.ts +0 -104
  371. package/src/test/__tests__/api.spec.ts +0 -199
  372. package/src/test/__tests__/auth.spec.ts +0 -162
  373. package/src/test/__tests__/index.spec.ts +0 -502
  374. package/src/test/__tests__/web.spec.ts +0 -210
  375. package/src/test/index.ts +0 -198
  376. package/src/types.ts +0 -363
  377. package/src/upgrade/__tests__/index.spec.ts +0 -354
  378. package/src/upgrade/index.ts +0 -253
  379. package/src/workspace/__tests__/client-generator.spec.ts +0 -326
  380. package/src/workspace/__tests__/index.spec.ts +0 -766
  381. package/src/workspace/__tests__/publicEnv.spec.ts +0 -64
  382. package/src/workspace/__tests__/schema.spec.ts +0 -737
  383. package/src/workspace/__tests__/type-inference.spec.ts +0 -251
  384. package/src/workspace/client-generator.ts +0 -109
  385. package/src/workspace/index.ts +0 -433
  386. package/src/workspace/publicEnv.ts +0 -57
  387. package/src/workspace/schema.ts +0 -861
  388. package/src/workspace/types.ts +0 -943
  389. package/tsconfig.json +0 -9
  390. package/tsdown.config.ts +0 -22
@@ -0,0 +1,443 @@
1
+ import { loadWorkspaceConfig } from "./config-D8o6I_7Z.mjs";
2
+ import { LOCAL_CA_PATH, derivedContainers, reconcileWorkspace } from "./credentials-Dh0Q6HzY.mjs";
3
+ import { createStageSecrets, generateConnectionUrls, generateSecurePassword, generateServiceCredentials } from "./generator-VE2JrFlY.mjs";
4
+ import { readStageSecrets, secretsExist, writeStageSecrets } from "./storage-Bi24WfoW.mjs";
5
+ import { generateFullstackCustomSecrets, writeDockerEnvFromSecrets } from "./fullstack-secrets-D1o9-G5l.mjs";
6
+ import { isSSMConfigured, pullSecrets, pushSecrets } from "./sync-CmEuNZsB.mjs";
7
+ import { existsSync } from "node:fs";
8
+ import { dirname, join } from "node:path";
9
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
10
+ import { execFile } from "node:child_process";
11
+ import { promisify } from "node:util";
12
+ import prompts from "prompts";
13
+
14
+ //#region src/trust/index.ts
15
+ const logger$1 = console;
16
+ const run = promisify(execFile);
17
+ /**
18
+ * How each platform is told about a new authority.
19
+ *
20
+ * Separate rather than shelled through one script because the failure modes
21
+ * differ and the message should name the real one: macOS refuses without an
22
+ * admin password, Debian needs the file in a specific directory before its
23
+ * updater will look at it.
24
+ */
25
+ function installation(platform, certificate) {
26
+ if (platform === "darwin") return {
27
+ describe: "the System keychain",
28
+ steps: [["sudo", [
29
+ "security",
30
+ "add-trusted-cert",
31
+ "-d",
32
+ "-r",
33
+ "trustRoot",
34
+ "-k",
35
+ "/Library/Keychains/System.keychain",
36
+ certificate
37
+ ]]]
38
+ };
39
+ if (platform === "linux") {
40
+ const debian = existsSync("/usr/local/share/ca-certificates");
41
+ return {
42
+ describe: debian ? "the system CA bundle" : "the system trust anchors",
43
+ steps: debian ? [["sudo", [
44
+ "cp",
45
+ certificate,
46
+ "/usr/local/share/ca-certificates/gkm-local-ca.crt"
47
+ ]], ["sudo", ["update-ca-certificates"]]] : [["sudo", [
48
+ "cp",
49
+ certificate,
50
+ "/etc/pki/ca-trust/source/anchors/gkm-local-ca.crt"
51
+ ]], ["sudo", ["update-ca-trust"]]]
52
+ };
53
+ }
54
+ throw new UnsupportedPlatform(platform, certificate);
55
+ }
56
+ /**
57
+ * Trust the certificate authority this project's edge issues from.
58
+ *
59
+ * Idempotent in effect: installing a root that is already trusted replaces the
60
+ * same entry, so running it twice is not an error.
61
+ */
62
+ async function trustCommand(options = {}) {
63
+ const { workspace } = await loadWorkspaceConfig();
64
+ const certificate = join(workspace.root, LOCAL_CA_PATH);
65
+ if (!existsSync(certificate)) throw new NoLocalAuthority(certificate);
66
+ const { describe, steps } = installation(process.platform, certificate);
67
+ logger$1.log(`\n🔐 Trusting the local authority in ${describe}`);
68
+ logger$1.log(` ${certificate}\n`);
69
+ if (options.dryRun) {
70
+ for (const [command, args] of steps) logger$1.log(` ${command} ${args.join(" ")}`);
71
+ return;
72
+ }
73
+ logger$1.log(" This needs an admin password.\n");
74
+ for (const [command, args] of steps) await run(command, args, { stdio: "inherit" });
75
+ logger$1.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
76
+ }
77
+ /**
78
+ * Trust the authority if this project wants that and this machine lacks it.
79
+ *
80
+ * The decision, in order: what the project configured, then what a person
81
+ * previously answered, then a prompt. Never a silent `sudo` — that is the one
82
+ * thing a setup command should not do on somebody's behalf.
83
+ *
84
+ * Silent and free when the machine already trusts it, which is what makes it
85
+ * acceptable to call on every `gkm setup`.
86
+ */
87
+ async function ensureTrusted(root, url, options = {}) {
88
+ if (options.configured === false) return;
89
+ if (await isTrusted(url)) return;
90
+ if (options.configured !== true && !options.assumeYes) {
91
+ if (!process.stdin.isTTY) {
92
+ logger$1.log(" Local https addresses are not trusted here. Run \"gkm trust\", or set services.trustLocalCa.");
93
+ return;
94
+ }
95
+ if (await declined(root)) return;
96
+ const { install } = await prompts({
97
+ type: "confirm",
98
+ name: "install",
99
+ message: "Trust this project's local certificate authority, so a browser accepts its https addresses? (needs sudo)",
100
+ initial: false
101
+ });
102
+ if (!install) {
103
+ await recordDeclined(root);
104
+ logger$1.log(" Skipped. Run \"gkm trust\" later, or set services.trustLocalCa.");
105
+ return;
106
+ }
107
+ }
108
+ await trustCommand();
109
+ }
110
+ /** Where a declined answer is remembered, so setup asks once and not again. */
111
+ const TRUST_STATE = ".gkm/trust.json";
112
+ async function declined(root) {
113
+ try {
114
+ const raw = await readFile(join(root, TRUST_STATE), "utf-8");
115
+ return JSON.parse(raw).declined === true;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+ async function recordDeclined(root) {
121
+ const path = join(root, TRUST_STATE);
122
+ await mkdir(dirname(path), { recursive: true });
123
+ await writeFile(path, `${JSON.stringify({ declined: true }, null, 2)}\n`);
124
+ }
125
+ /**
126
+ * Whether this machine already trusts the local authority.
127
+ *
128
+ * Asked by connecting rather than by reading a keychain: what matters is
129
+ * whether a client with no special configuration verifies the edge, and that is
130
+ * one platform-neutral question instead of three platform-specific ones. It is
131
+ * also the exact thing a browser is about to do.
132
+ *
133
+ * A child process, because `NODE_EXTRA_CA_CERTS` is read once at startup and
134
+ * this process almost certainly has it set — asking in-process would answer
135
+ * "yes" for everyone.
136
+ */
137
+ async function isTrusted(url) {
138
+ const { NODE_EXTRA_CA_CERTS: _ignored,...env } = process.env;
139
+ try {
140
+ await run(process.execPath, ["-e", `fetch(${JSON.stringify(url)}).then(()=>0,()=>process.exit(1))`], {
141
+ env,
142
+ timeout: 15e3
143
+ });
144
+ return true;
145
+ } catch {
146
+ return false;
147
+ }
148
+ }
149
+ /** The edge has not run, so there is no authority to trust yet. */
150
+ var NoLocalAuthority = class extends Error {
151
+ constructor(path) {
152
+ super(`No local certificate authority at ${path}. It is generated the first time the edge starts, so run "gkm setup" (or "gkm dev") once — and if this project declares no file server, nothing needs one.`);
153
+ this.path = path;
154
+ this.name = "NoLocalAuthority";
155
+ }
156
+ };
157
+ /** A platform with no trust store this knows how to write to. */
158
+ var UnsupportedPlatform = class extends Error {
159
+ constructor(platform, certificate) {
160
+ super(`gkm does not know how to install a certificate authority on ${platform}. The root is at ${certificate} — add it to the trust store by hand, or point a client at it with NODE_EXTRA_CA_CERTS.`);
161
+ this.platform = platform;
162
+ this.certificate = certificate;
163
+ this.name = "UnsupportedPlatform";
164
+ }
165
+ };
166
+
167
+ //#endregion
168
+ //#region src/setup/index.ts
169
+ const logger = console;
170
+ /**
171
+ * Setup development environment.
172
+ *
173
+ * Orchestrates:
174
+ * 1. Load workspace config
175
+ * 2. Resolve secrets (local → SSM → generate fresh)
176
+ * 3. Write docker/.env from secrets
177
+ * 4. Start Docker services
178
+ */
179
+ async function setupCommand(options = {}) {
180
+ const stage = options.stage ?? "development";
181
+ logger.log("\n🔧 Setting up development environment...\n");
182
+ let loadedConfig;
183
+ try {
184
+ loadedConfig = await loadWorkspaceConfig();
185
+ } catch {
186
+ logger.error("❌ No gkm.config.ts found. Run this command from a workspace root.");
187
+ process.exit(1);
188
+ }
189
+ const { workspace } = loadedConfig;
190
+ const isMultiApp = Object.keys(workspace.apps).length > 1;
191
+ logger.log(`📦 Workspace: ${workspace.name}`);
192
+ logger.log(`📱 Apps: ${Object.keys(workspace.apps).join(", ")}`);
193
+ logger.log(`🔑 Stage: ${stage}\n`);
194
+ const secrets = await resolveSecrets(stage, workspace, options);
195
+ if (!secrets) {
196
+ logger.error("❌ Failed to resolve secrets. Exiting.");
197
+ process.exit(1);
198
+ }
199
+ const containers = await derivedContainers(workspace, stage);
200
+ if (isMultiApp && containers.includes("postgres")) {
201
+ await writeDockerEnvFromSecrets(secrets, workspace.root);
202
+ logger.log("📄 Generated docker/.env with database passwords");
203
+ }
204
+ if (!options.skipDocker) {
205
+ logger.log("");
206
+ await reconcileLocal(workspace, stage, options);
207
+ }
208
+ printSummary(workspace, stage);
209
+ }
210
+ /**
211
+ * Converge this machine on what the manifest declares.
212
+ *
213
+ * Says what it did in one line and stays silent when it did nothing, because
214
+ * reconciling on every start is only tolerable if the converged case is both
215
+ * free and quiet.
216
+ */
217
+ async function reconcileLocal(workspace, stage, options) {
218
+ const result = await reconcileWorkspace(workspace, { stage });
219
+ if (result.plan.containers.length === 0) {
220
+ logger.log("🐳 No containers declared");
221
+ return;
222
+ }
223
+ if (!result.changed) {
224
+ logger.log("🐳 Services already up to date");
225
+ return;
226
+ }
227
+ logger.log(`🐳 Services: ${result.plan.containers.join(", ")}`);
228
+ for (const [container, address] of Object.entries(result.addresses)) logger.log(` ${container}: ${address}`);
229
+ const served = result.plan.resources.find((r) => r.kind === "file-server");
230
+ const url = served ? result.env[served.envKey] : void 0;
231
+ if (url) await ensureTrusted(workspace.root, url, {
232
+ ...workspace.services.trustLocalCa === void 0 ? {} : { configured: workspace.services.trustLocalCa },
233
+ ...options.yes ? { assumeYes: true } : {}
234
+ });
235
+ }
236
+ /**
237
+ * Resolve secrets with priority:
238
+ * 1. Local secrets exist → use them (preserves manual additions)
239
+ * 2. SSM configured and has secrets → pull and use
240
+ * 3. Neither → generate fresh secrets
241
+ *
242
+ * --force skips checks 1 and 2 and always regenerates.
243
+ */
244
+ async function resolveSecrets(stage, workspace, options) {
245
+ if (options.force) {
246
+ logger.log("🔐 Generating fresh secrets (--force)...");
247
+ return generateFreshSecrets(stage, workspace, options);
248
+ }
249
+ if (secretsExist(stage, workspace.root)) {
250
+ logger.log("🔐 Using existing local secrets");
251
+ const secrets = await readStageSecrets(stage, workspace.root);
252
+ if (secrets) {
253
+ const reconciled = reconcileSecrets(secrets, workspace, await derivedContainers(workspace, stage));
254
+ if (reconciled) await writeStageSecrets(reconciled, workspace.root);
255
+ return reconciled ?? secrets;
256
+ }
257
+ }
258
+ if (isSSMConfigured(workspace)) {
259
+ logger.log("☁️ Checking for remote secrets in SSM...");
260
+ try {
261
+ const remoteSecrets = await pullSecrets(stage, workspace);
262
+ if (remoteSecrets) {
263
+ logger.log("✅ Pulled secrets from SSM");
264
+ await writeStageSecrets(remoteSecrets, workspace.root);
265
+ return remoteSecrets;
266
+ }
267
+ logger.log(" No remote secrets found");
268
+ } catch (error) {
269
+ logger.warn(`⚠️ Failed to pull from SSM: ${error.message}`);
270
+ }
271
+ }
272
+ logger.log("🔐 Generating fresh development secrets...");
273
+ return generateFreshSecrets(stage, workspace, options);
274
+ }
275
+ /**
276
+ * Reconcile existing secrets with expected workspace-derived keys.
277
+ * Adds missing keys (e.g. BETTER_AUTH_*) without overwriting existing values.
278
+ * Returns the updated secrets if changes were made, or null if no changes needed.
279
+ * @internal Exported for testing
280
+ */
281
+ /**
282
+ * Which containers hold a credential worth generating.
283
+ *
284
+ * Reconcile's container names, mapped to the service a credential is stored
285
+ * under. `redis-http` is the Upstash-protocol proxy in front of Redis and holds
286
+ * no credential of its own — its token is derived — so it maps to nothing, and
287
+ * `caddy` is an edge with nothing to log into.
288
+ */
289
+ function credentialedServices(containers) {
290
+ const byContainer = {
291
+ postgres: "postgres",
292
+ redis: "redis",
293
+ minio: "minio",
294
+ mailpit: "mailpit",
295
+ localstack: "localstack",
296
+ rabbitmq: "rabbitmq"
297
+ };
298
+ return [...new Set(containers.map((container) => byContainer[container]).filter((name) => Boolean(name)))];
299
+ }
300
+ function reconcileSecrets(secrets, workspace, containers = []) {
301
+ let changed = false;
302
+ let result = { ...secrets };
303
+ for (const name of credentialedServices(containers)) if (!result.services[name]) {
304
+ const creds = generateServiceCredentials(name);
305
+ if (name === "minio") {
306
+ creds.bucket = workspace.name;
307
+ creds.username = workspace.name;
308
+ }
309
+ result = {
310
+ ...result,
311
+ services: {
312
+ ...result.services,
313
+ [name]: creds
314
+ }
315
+ };
316
+ result.urls = generateConnectionUrls(result.services, result.eventsBackend);
317
+ logger.log(` 🔄 Adding missing service credentials: ${name}`);
318
+ changed = true;
319
+ }
320
+ if (result.services.postgres && !result.services.pgboss) {
321
+ result = {
322
+ ...result,
323
+ services: {
324
+ ...result.services,
325
+ pgboss: {
326
+ host: result.services.postgres.host,
327
+ port: result.services.postgres.port,
328
+ username: "pgboss",
329
+ password: generateSecurePassword(),
330
+ database: result.services.postgres.database ?? "app"
331
+ }
332
+ }
333
+ };
334
+ result.urls = generateConnectionUrls(result.services, result.eventsBackend);
335
+ logger.log(" 🔄 Adding missing service credentials: pgboss");
336
+ changed = true;
337
+ }
338
+ const isMultiApp = Object.keys(workspace.apps).length > 1;
339
+ if (isMultiApp) {
340
+ const expected = generateFullstackCustomSecrets(workspace, containers);
341
+ const missing = {};
342
+ for (const [key, value] of Object.entries(expected)) if (!(key in result.custom)) missing[key] = value;
343
+ if (Object.keys(missing).length > 0) {
344
+ logger.log(` 🔄 Adding missing secrets: ${Object.keys(missing).join(", ")}`);
345
+ result = {
346
+ ...result,
347
+ custom: {
348
+ ...result.custom,
349
+ ...missing
350
+ }
351
+ };
352
+ changed = true;
353
+ }
354
+ }
355
+ if (!changed) return null;
356
+ return {
357
+ ...result,
358
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
359
+ };
360
+ }
361
+ /**
362
+ * Build a fresh StageSecrets object for a workspace: service credentials,
363
+ * connection URLs, and custom secrets. Pure — does not write to disk or touch
364
+ * SSM. Service passwords are freshly randomized, so the result must be the same
365
+ * one used to start the matching Docker containers.
366
+ * @internal Exported for reuse by `gkm test` auto-setup.
367
+ */
368
+ function createFreshWorkspaceSecrets(stage, workspace, containers = []) {
369
+ const serviceNames = credentialedServices(containers);
370
+ const secrets = createStageSecrets(stage, serviceNames, {
371
+ projectName: workspace.name,
372
+ eventsBackend: workspace.services.events
373
+ });
374
+ const isMultiApp = Object.keys(workspace.apps).length > 1;
375
+ if (isMultiApp) secrets.custom = generateFullstackCustomSecrets(workspace, containers);
376
+ else secrets.custom = {
377
+ NODE_ENV: "development",
378
+ PORT: "3000",
379
+ LOG_LEVEL: "debug",
380
+ JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
381
+ };
382
+ return secrets;
383
+ }
384
+ /**
385
+ * Ensure a usable secret set exists for a stage, generating a fresh one from the
386
+ * workspace config when none is present. No-op if secrets already exist.
387
+ *
388
+ * Powers `gkm test --auto-setup` / `GKM_AUTO_SETUP`: CI can run without a
389
+ * committed secrets file or a shared encryption key — the stage is regenerated
390
+ * from the committed `gkm.config.ts`, and `writeStageSecrets` mints a local key.
391
+ *
392
+ * @returns true if fresh secrets were generated, false if existing ones were kept
393
+ */
394
+ async function ensureStageSecrets(stage, cwd = process.cwd()) {
395
+ const { workspace } = await loadWorkspaceConfig(cwd);
396
+ if (secretsExist(stage, workspace.root)) return false;
397
+ const secrets = createFreshWorkspaceSecrets(stage, workspace, await derivedContainers(workspace, stage));
398
+ await writeStageSecrets(secrets, workspace.root);
399
+ return true;
400
+ }
401
+ /**
402
+ * Generate fresh secrets for the workspace.
403
+ */
404
+ async function generateFreshSecrets(stage, workspace, options) {
405
+ const secrets = createFreshWorkspaceSecrets(stage, workspace, await derivedContainers(workspace, stage));
406
+ await writeStageSecrets(secrets, workspace.root);
407
+ logger.log(` Secrets written to .gkm/secrets/${stage}.json`);
408
+ if (isSSMConfigured(workspace) && !options.yes) {
409
+ const { shouldPush } = await prompts({
410
+ type: "confirm",
411
+ name: "shouldPush",
412
+ message: "Push secrets to SSM for team sharing?",
413
+ initial: true
414
+ });
415
+ if (shouldPush) try {
416
+ await pushSecrets(stage, workspace);
417
+ logger.log("☁️ Secrets pushed to SSM");
418
+ } catch (error) {
419
+ logger.warn(`⚠️ Failed to push to SSM: ${error.message}`);
420
+ }
421
+ }
422
+ return secrets;
423
+ }
424
+ /**
425
+ * Print setup summary with next steps.
426
+ */
427
+ function printSummary(workspace, stage) {
428
+ logger.log(`\n${"─".repeat(50)}`);
429
+ logger.log("\n✅ Development environment ready!\n");
430
+ logger.log("📋 Apps:");
431
+ for (const [name, app] of Object.entries(workspace.apps)) {
432
+ const icon = app.type === "web" ? "🌐" : app.type === "mobile" ? "📱" : "🔧";
433
+ logger.log(` ${icon} ${name} → http://localhost:${app.port}`);
434
+ }
435
+ logger.log("\n🚀 Next steps:");
436
+ logger.log(" gkm dev # Start all apps");
437
+ logger.log(` gkm secrets:show --stage ${stage} # View secrets`);
438
+ logger.log("");
439
+ }
440
+
441
+ //#endregion
442
+ export { createFreshWorkspaceSecrets, ensureStageSecrets, reconcileSecrets, setupCommand, trustCommand };
443
+ //# sourceMappingURL=setup-DOsYueeQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-DOsYueeQ.mjs","names":["logger","platform: NodeJS.Platform","certificate: string","options: TrustOptions","root: string","url: string","options: { configured?: boolean; assumeYes?: boolean }","path: string","platform: string","options: SetupOptions","loadedConfig: LoadedConfig","workspace: NormalizedWorkspace","stage: string","containers: readonly string[]","byContainer: Readonly<Record<string, ComposeServiceName>>","secrets: StageSecrets","missing: Record<string, string>","cwd: string"],"sources":["../src/trust/index.ts","../src/setup/index.ts"],"sourcesContent":["/**\n * Installing the local edge's certificate authority into the OS trust store.\n *\n * `NODE_EXTRA_CA_CERTS` covers everything gkm starts — the dev server, the test\n * suite, anything under `gkm exec` — because reconcile injects it. A browser\n * reads none of that: it asks the operating system, so until the root is in the\n * system store a local `https://` address is \"not secure\", which is true and\n * unhelpful.\n *\n * A separate command rather than part of `gkm setup`, for one reason: this\n * needs `sudo`, and a setup that silently escalates on every run is a worse\n * trade than one that tells you what to type. Caddy's own `caddy trust` draws\n * the line in the same place.\n */\n\nimport { execFile } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { promisify } from 'node:util';\nimport prompts from 'prompts';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { LOCAL_CA_PATH } from '../reconcile/index.js';\n\nconst logger = console;\n\nconst run = promisify(execFile);\n\nexport interface TrustOptions {\n\t/** Print the commands rather than running them. */\n\tdryRun?: boolean;\n}\n\n/**\n * How each platform is told about a new authority.\n *\n * Separate rather than shelled through one script because the failure modes\n * differ and the message should name the real one: macOS refuses without an\n * admin password, Debian needs the file in a specific directory before its\n * updater will look at it.\n */\nfunction installation(\n\tplatform: NodeJS.Platform,\n\tcertificate: string,\n): { describe: string; steps: [string, string[]][] } {\n\tif (platform === 'darwin') {\n\t\treturn {\n\t\t\tdescribe: 'the System keychain',\n\t\t\tsteps: [\n\t\t\t\t[\n\t\t\t\t\t'sudo',\n\t\t\t\t\t[\n\t\t\t\t\t\t'security',\n\t\t\t\t\t\t'add-trusted-cert',\n\t\t\t\t\t\t'-d',\n\t\t\t\t\t\t'-r',\n\t\t\t\t\t\t'trustRoot',\n\t\t\t\t\t\t'-k',\n\t\t\t\t\t\t'/Library/Keychains/System.keychain',\n\t\t\t\t\t\tcertificate,\n\t\t\t\t\t],\n\t\t\t\t],\n\t\t\t],\n\t\t};\n\t}\n\n\tif (platform === 'linux') {\n\t\t// Debian and Fedora keep anchors in different places and run different\n\t\t// updaters. Whichever directory exists is the one this system uses.\n\t\tconst debian = existsSync('/usr/local/share/ca-certificates');\n\n\t\treturn {\n\t\t\tdescribe: debian ? 'the system CA bundle' : 'the system trust anchors',\n\t\t\tsteps: debian\n\t\t\t\t? [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'sudo',\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'cp',\n\t\t\t\t\t\t\t\tcertificate,\n\t\t\t\t\t\t\t\t'/usr/local/share/ca-certificates/gkm-local-ca.crt',\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t],\n\t\t\t\t\t\t['sudo', ['update-ca-certificates']],\n\t\t\t\t\t]\n\t\t\t\t: [\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'sudo',\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t'cp',\n\t\t\t\t\t\t\t\tcertificate,\n\t\t\t\t\t\t\t\t'/etc/pki/ca-trust/source/anchors/gkm-local-ca.crt',\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t],\n\t\t\t\t\t\t['sudo', ['update-ca-trust']],\n\t\t\t\t\t],\n\t\t};\n\t}\n\n\tthrow new UnsupportedPlatform(platform, certificate);\n}\n\n/**\n * Trust the certificate authority this project's edge issues from.\n *\n * Idempotent in effect: installing a root that is already trusted replaces the\n * same entry, so running it twice is not an error.\n */\nexport async function trustCommand(options: TrustOptions = {}): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\tconst certificate = join(workspace.root, LOCAL_CA_PATH);\n\n\tif (!existsSync(certificate)) {\n\t\tthrow new NoLocalAuthority(certificate);\n\t}\n\n\tconst { describe, steps } = installation(process.platform, certificate);\n\n\tlogger.log(`\\n🔐 Trusting the local authority in ${describe}`);\n\tlogger.log(` ${certificate}\\n`);\n\n\tif (options.dryRun) {\n\t\tfor (const [command, args] of steps) {\n\t\t\tlogger.log(` ${command} ${args.join(' ')}`);\n\t\t}\n\t\treturn;\n\t}\n\n\tlogger.log(' This needs an admin password.\\n');\n\n\tfor (const [command, args] of steps) {\n\t\t// Inherited stdio, because sudo prompts on the terminal and a captured\n\t\t// prompt is a command that appears to hang.\n\t\tawait run(command, args, { stdio: 'inherit' } as never);\n\t}\n\n\tlogger.log('\\n✅ Local https addresses are trusted. Restart your browser.\\n');\n}\n\n/**\n * Trust the authority if this project wants that and this machine lacks it.\n *\n * The decision, in order: what the project configured, then what a person\n * previously answered, then a prompt. Never a silent `sudo` — that is the one\n * thing a setup command should not do on somebody's behalf.\n *\n * Silent and free when the machine already trusts it, which is what makes it\n * acceptable to call on every `gkm setup`.\n */\nexport async function ensureTrusted(\n\troot: string,\n\turl: string,\n\toptions: { configured?: boolean; assumeYes?: boolean } = {},\n): Promise<void> {\n\tif (options.configured === false) return;\n\tif (await isTrusted(url)) return;\n\n\tif (options.configured !== true && !options.assumeYes) {\n\t\t// Nothing to answer a prompt in CI, a hook, or a piped shell — and a\n\t\t// setup that stops there waiting is worse than one that leaves the\n\t\t// authority untrusted. Not recorded as declined, because nobody\n\t\t// declined: the next interactive run should still ask.\n\t\tif (!process.stdin.isTTY) {\n\t\t\tlogger.log(\n\t\t\t\t' Local https addresses are not trusted here. Run \"gkm trust\", ' +\n\t\t\t\t\t'or set services.trustLocalCa.',\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// A recorded \"no\" is why this asks once rather than every start. There is\n\t\t// no recorded \"yes\": trusting is checked directly above, so a yes that\n\t\t// worked answers itself and a yes that did not should ask again.\n\t\tif (await declined(root)) return;\n\n\t\tconst { install } = await prompts({\n\t\t\ttype: 'confirm',\n\t\t\tname: 'install',\n\t\t\tmessage:\n\t\t\t\t\"Trust this project's local certificate authority, so a browser \" +\n\t\t\t\t'accepts its https addresses? (needs sudo)',\n\t\t\tinitial: false,\n\t\t});\n\n\t\tif (!install) {\n\t\t\tawait recordDeclined(root);\n\t\t\tlogger.log(\n\t\t\t\t' Skipped. Run \"gkm trust\" later, or set services.trustLocalCa.',\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tawait trustCommand();\n}\n\n/** Where a declined answer is remembered, so setup asks once and not again. */\nconst TRUST_STATE = '.gkm/trust.json';\n\nasync function declined(root: string): Promise<boolean> {\n\ttry {\n\t\tconst raw = await readFile(join(root, TRUST_STATE), 'utf-8');\n\n\t\treturn (JSON.parse(raw) as { declined?: boolean }).declined === true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function recordDeclined(root: string): Promise<void> {\n\tconst path = join(root, TRUST_STATE);\n\n\tawait mkdir(dirname(path), { recursive: true });\n\tawait writeFile(path, `${JSON.stringify({ declined: true }, null, 2)}\\n`);\n}\n\n/**\n * Whether this machine already trusts the local authority.\n *\n * Asked by connecting rather than by reading a keychain: what matters is\n * whether a client with no special configuration verifies the edge, and that is\n * one platform-neutral question instead of three platform-specific ones. It is\n * also the exact thing a browser is about to do.\n *\n * A child process, because `NODE_EXTRA_CA_CERTS` is read once at startup and\n * this process almost certainly has it set — asking in-process would answer\n * \"yes\" for everyone.\n */\nexport async function isTrusted(url: string): Promise<boolean> {\n\tconst { NODE_EXTRA_CA_CERTS: _ignored, ...env } = process.env;\n\n\ttry {\n\t\tawait run(\n\t\t\tprocess.execPath,\n\t\t\t['-e', `fetch(${JSON.stringify(url)}).then(()=>0,()=>process.exit(1))`],\n\t\t\t{ env, timeout: 15_000 },\n\t\t);\n\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Copy the root somewhere a tool that wants a file can read it.\n *\n * For anything that takes a CA bundle rather than reading the system store —\n * another language's HTTP client, a container, a CI job.\n */\nexport async function exportLocalAuthority(to: string): Promise<void> {\n\tconst { workspace } = await loadWorkspaceConfig();\n\tconst certificate = join(workspace.root, LOCAL_CA_PATH);\n\n\tif (!existsSync(certificate)) throw new NoLocalAuthority(certificate);\n\n\tawait copyFile(certificate, to);\n}\n\n/** The edge has not run, so there is no authority to trust yet. */\nexport class NoLocalAuthority extends Error {\n\tconstructor(readonly path: string) {\n\t\tsuper(\n\t\t\t`No local certificate authority at ${path}. It is generated the first ` +\n\t\t\t\t`time the edge starts, so run \"gkm setup\" (or \"gkm dev\") once — and ` +\n\t\t\t\t`if this project declares no file server, nothing needs one.`,\n\t\t);\n\t\tthis.name = 'NoLocalAuthority';\n\t}\n}\n\n/** A platform with no trust store this knows how to write to. */\nexport class UnsupportedPlatform extends Error {\n\tconstructor(\n\t\treadonly platform: string,\n\t\treadonly certificate: string,\n\t) {\n\t\tsuper(\n\t\t\t`gkm does not know how to install a certificate authority on ` +\n\t\t\t\t`${platform}. The root is at ${certificate} — add it to the trust ` +\n\t\t\t\t`store by hand, or point a client at it with NODE_EXTRA_CA_CERTS.`,\n\t\t);\n\t\tthis.name = 'UnsupportedPlatform';\n\t}\n}\n","import prompts from 'prompts';\nimport { loadWorkspaceConfig } from '../config.js';\nimport {\n\tderivedContainers,\n\treconcileWorkspace,\n} from '../reconcile/workspace.js';\nimport {\n\tcreateStageSecrets,\n\tgenerateConnectionUrls,\n\tgenerateSecurePassword,\n\tgenerateServiceCredentials,\n} from '../secrets/generator.js';\nimport {\n\treadStageSecrets,\n\tsecretsExist,\n\twriteStageSecrets,\n} from '../secrets/storage.js';\nimport { isSSMConfigured, pullSecrets, pushSecrets } from '../secrets/sync.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport { ensureTrusted } from '../trust/index.js';\nimport type { ComposeServiceName } from '../types.js';\nimport type { LoadedConfig, NormalizedWorkspace } from '../workspace/types.js';\nimport {\n\tgenerateFullstackCustomSecrets,\n\twriteDockerEnvFromSecrets,\n} from './fullstack-secrets.js';\n\nconst logger = console;\n\nexport interface SetupOptions {\n\tstage?: string;\n\tforce?: boolean;\n\tskipDocker?: boolean;\n\tyes?: boolean;\n}\n\n/**\n * Setup development environment.\n *\n * Orchestrates:\n * 1. Load workspace config\n * 2. Resolve secrets (local → SSM → generate fresh)\n * 3. Write docker/.env from secrets\n * 4. Start Docker services\n */\nexport async function setupCommand(options: SetupOptions = {}): Promise<void> {\n\tconst stage = options.stage ?? 'development';\n\n\tlogger.log('\\n🔧 Setting up development environment...\\n');\n\n\t// 1. Load workspace config\n\tlet loadedConfig: LoadedConfig;\n\ttry {\n\t\tloadedConfig = await loadWorkspaceConfig();\n\t} catch {\n\t\tlogger.error(\n\t\t\t'❌ No gkm.config.ts found. Run this command from a workspace root.',\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\n\tconst { workspace } = loadedConfig;\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\n\tlogger.log(`📦 Workspace: ${workspace.name}`);\n\tlogger.log(`📱 Apps: ${Object.keys(workspace.apps).join(', ')}`);\n\tlogger.log(`🔑 Stage: ${stage}\\n`);\n\n\t// 2. Resolve secrets\n\tconst secrets = await resolveSecrets(stage, workspace, options);\n\n\tif (!secrets) {\n\t\tlogger.error('❌ Failed to resolve secrets. Exiting.');\n\t\tprocess.exit(1);\n\t}\n\n\t// 3. Write docker/.env from secrets (always regenerated as derived file)\n\tconst containers = await derivedContainers(workspace, stage);\n\n\tif (isMultiApp && containers.includes('postgres')) {\n\t\tawait writeDockerEnvFromSecrets(secrets, workspace.root);\n\t\tlogger.log('📄 Generated docker/.env with database passwords');\n\t}\n\n\t// 4. Reconcile the local target: derive containers, allocate ports, start.\n\tif (!options.skipDocker) {\n\t\tlogger.log('');\n\n\t\tawait reconcileLocal(workspace, stage, options);\n\t}\n\n\t// Print summary\n\tprintSummary(workspace, stage);\n}\n\n/**\n * Converge this machine on what the manifest declares.\n *\n * Says what it did in one line and stays silent when it did nothing, because\n * reconciling on every start is only tolerable if the converged case is both\n * free and quiet.\n */\nasync function reconcileLocal(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SetupOptions,\n): Promise<void> {\n\tconst result = await reconcileWorkspace(workspace, { stage });\n\n\tif (result.plan.containers.length === 0) {\n\t\tlogger.log('🐳 No containers declared');\n\t\treturn;\n\t}\n\n\tif (!result.changed) {\n\t\tlogger.log('🐳 Services already up to date');\n\t\treturn;\n\t}\n\n\tlogger.log(`🐳 Services: ${result.plan.containers.join(', ')}`);\n\tfor (const [container, address] of Object.entries(result.addresses)) {\n\t\tlogger.log(` ${container}: ${address}`);\n\t}\n\n\t// Only where something actually answers on https. A project with no edge has\n\t// no authority to trust and should never be asked about one.\n\tconst served = result.plan.resources.find((r) => r.kind === 'file-server');\n\tconst url = served ? result.env[served.envKey] : undefined;\n\n\tif (url) {\n\t\tawait ensureTrusted(workspace.root, url, {\n\t\t\t...(workspace.services.trustLocalCa === undefined\n\t\t\t\t? {}\n\t\t\t\t: { configured: workspace.services.trustLocalCa }),\n\t\t\t...(options.yes ? { assumeYes: true } : {}),\n\t\t});\n\t}\n}\n\n/**\n * Resolve secrets with priority:\n * 1. Local secrets exist → use them (preserves manual additions)\n * 2. SSM configured and has secrets → pull and use\n * 3. Neither → generate fresh secrets\n *\n * --force skips checks 1 and 2 and always regenerates.\n */\nasync function resolveSecrets(\n\tstage: string,\n\tworkspace: NormalizedWorkspace,\n\toptions: SetupOptions,\n) {\n\t// Force regeneration\n\tif (options.force) {\n\t\tlogger.log('🔐 Generating fresh secrets (--force)...');\n\t\treturn generateFreshSecrets(stage, workspace, options);\n\t}\n\n\t// Check local secrets first\n\tif (secretsExist(stage, workspace.root)) {\n\t\tlogger.log('🔐 Using existing local secrets');\n\t\tconst secrets = await readStageSecrets(stage, workspace.root);\n\t\tif (secrets) {\n\t\t\t// Reconcile: add any missing workspace-derived keys without overwriting\n\t\t\tconst reconciled = reconcileSecrets(\n\t\t\t\tsecrets,\n\t\t\t\tworkspace,\n\t\t\t\tawait derivedContainers(workspace, stage),\n\t\t\t);\n\t\t\tif (reconciled) {\n\t\t\t\tawait writeStageSecrets(reconciled, workspace.root);\n\t\t\t}\n\t\t\treturn reconciled ?? secrets;\n\t\t}\n\t}\n\n\t// Try SSM pull if configured\n\tif (isSSMConfigured(workspace)) {\n\t\tlogger.log('☁️ Checking for remote secrets in SSM...');\n\t\ttry {\n\t\t\tconst remoteSecrets = await pullSecrets(stage, workspace);\n\t\t\tif (remoteSecrets) {\n\t\t\t\tlogger.log('✅ Pulled secrets from SSM');\n\t\t\t\tawait writeStageSecrets(remoteSecrets, workspace.root);\n\t\t\t\treturn remoteSecrets;\n\t\t\t}\n\t\t\tlogger.log(' No remote secrets found');\n\t\t} catch (error) {\n\t\t\tlogger.warn(`⚠️ Failed to pull from SSM: ${(error as Error).message}`);\n\t\t}\n\t}\n\n\t// Generate fresh secrets\n\tlogger.log('🔐 Generating fresh development secrets...');\n\treturn generateFreshSecrets(stage, workspace, options);\n}\n\n/**\n * Reconcile existing secrets with expected workspace-derived keys.\n * Adds missing keys (e.g. BETTER_AUTH_*) without overwriting existing values.\n * Returns the updated secrets if changes were made, or null if no changes needed.\n * @internal Exported for testing\n */\n/**\n * Which containers hold a credential worth generating.\n *\n * Reconcile's container names, mapped to the service a credential is stored\n * under. `redis-http` is the Upstash-protocol proxy in front of Redis and holds\n * no credential of its own — its token is derived — so it maps to nothing, and\n * `caddy` is an edge with nothing to log into.\n */\nfunction credentialedServices(\n\tcontainers: readonly string[],\n): ComposeServiceName[] {\n\tconst byContainer: Readonly<Record<string, ComposeServiceName>> = {\n\t\tpostgres: 'postgres',\n\t\tredis: 'redis',\n\t\tminio: 'minio',\n\t\tmailpit: 'mailpit',\n\t\tlocalstack: 'localstack',\n\t\trabbitmq: 'rabbitmq',\n\t};\n\n\treturn [\n\t\t...new Set(\n\t\t\tcontainers\n\t\t\t\t.map((container) => byContainer[container])\n\t\t\t\t.filter((name): name is ComposeServiceName => Boolean(name)),\n\t\t),\n\t];\n}\n\nexport function reconcileSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tcontainers: readonly string[] = [],\n): StageSecrets | null {\n\tlet changed = false;\n\tlet result = { ...secrets };\n\n\t// Reconcile service credentials: add missing services\n\tfor (const name of credentialedServices(containers)) {\n\t\tif (!result.services[name]) {\n\t\t\tconst creds = generateServiceCredentials(name);\n\t\t\t// Override defaults with project-derived names\n\t\t\tif (name === 'minio') {\n\t\t\t\tcreds.bucket = workspace.name;\n\t\t\t\tcreds.username = workspace.name;\n\t\t\t}\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tservices: { ...result.services, [name]: creds },\n\t\t\t};\n\t\t\tresult.urls = generateConnectionUrls(\n\t\t\t\tresult.services,\n\t\t\t\tresult.eventsBackend,\n\t\t\t);\n\t\t\tlogger.log(` 🔄 Adding missing service credentials: ${name}`);\n\t\t\tchanged = true;\n\t\t}\n\t}\n\n\t// Always add pgboss credentials when postgres is available\n\tif (result.services.postgres && !result.services.pgboss) {\n\t\tresult = {\n\t\t\t...result,\n\t\t\tservices: {\n\t\t\t\t...result.services,\n\t\t\t\tpgboss: {\n\t\t\t\t\thost: result.services.postgres.host,\n\t\t\t\t\tport: result.services.postgres.port,\n\t\t\t\t\tusername: 'pgboss',\n\t\t\t\t\tpassword: generateSecurePassword(),\n\t\t\t\t\tdatabase: result.services.postgres.database ?? 'app',\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tresult.urls = generateConnectionUrls(result.services, result.eventsBackend);\n\t\tlogger.log(' 🔄 Adding missing service credentials: pgboss');\n\t\tchanged = true;\n\t}\n\n\t// Reconcile custom secrets for multi-app workspaces\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (isMultiApp) {\n\t\tconst expected = generateFullstackCustomSecrets(workspace, containers);\n\t\tconst missing: Record<string, string> = {};\n\n\t\tfor (const [key, value] of Object.entries(expected)) {\n\t\t\tif (!(key in result.custom)) {\n\t\t\t\tmissing[key] = value;\n\t\t\t}\n\t\t}\n\n\t\tif (Object.keys(missing).length > 0) {\n\t\t\tlogger.log(\n\t\t\t\t` 🔄 Adding missing secrets: ${Object.keys(missing).join(', ')}`,\n\t\t\t);\n\t\t\tresult = {\n\t\t\t\t...result,\n\t\t\t\tcustom: { ...result.custom, ...missing },\n\t\t\t};\n\t\t\tchanged = true;\n\t\t}\n\t}\n\n\tif (!changed) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\t...result,\n\t\tupdatedAt: new Date().toISOString(),\n\t};\n}\n\n/**\n * Build a fresh StageSecrets object for a workspace: service credentials,\n * connection URLs, and custom secrets. Pure — does not write to disk or touch\n * SSM. Service passwords are freshly randomized, so the result must be the same\n * one used to start the matching Docker containers.\n * @internal Exported for reuse by `gkm test` auto-setup.\n */\nexport function createFreshWorkspaceSecrets(\n\tstage: string,\n\tworkspace: NormalizedWorkspace,\n\tcontainers: readonly string[] = [],\n): StageSecrets {\n\t// Which services need a credential is which containers exist, and that is\n\t// the manifest's answer. It used to be a boolean per service read from\n\t// config, which generated a Postgres password for a project with no\n\t// database and none for a project that declared one and set no flag.\n\tconst serviceNames = credentialedServices(containers);\n\n\t// Create base secrets with service credentials\n\tconst secrets = createStageSecrets(stage, serviceNames, {\n\t\tprojectName: workspace.name,\n\t\teventsBackend: workspace.services.events,\n\t});\n\n\t// Generate fullstack-aware custom secrets\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (isMultiApp) {\n\t\tsecrets.custom = generateFullstackCustomSecrets(workspace, containers);\n\t} else {\n\t\tsecrets.custom = {\n\t\t\tNODE_ENV: 'development',\n\t\t\tPORT: '3000',\n\t\t\tLOG_LEVEL: 'debug',\n\t\t\tJWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n\t\t};\n\t}\n\n\treturn secrets;\n}\n\n/**\n * Ensure a usable secret set exists for a stage, generating a fresh one from the\n * workspace config when none is present. No-op if secrets already exist.\n *\n * Powers `gkm test --auto-setup` / `GKM_AUTO_SETUP`: CI can run without a\n * committed secrets file or a shared encryption key — the stage is regenerated\n * from the committed `gkm.config.ts`, and `writeStageSecrets` mints a local key.\n *\n * @returns true if fresh secrets were generated, false if existing ones were kept\n */\nexport async function ensureStageSecrets(\n\tstage: string,\n\tcwd: string = process.cwd(),\n): Promise<boolean> {\n\tconst { workspace } = await loadWorkspaceConfig(cwd);\n\n\tif (secretsExist(stage, workspace.root)) {\n\t\treturn false;\n\t}\n\n\tconst secrets = createFreshWorkspaceSecrets(\n\t\tstage,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\tawait writeStageSecrets(secrets, workspace.root);\n\treturn true;\n}\n\n/**\n * Generate fresh secrets for the workspace.\n */\nasync function generateFreshSecrets(\n\tstage: string,\n\tworkspace: NormalizedWorkspace,\n\toptions: SetupOptions,\n) {\n\tconst secrets = createFreshWorkspaceSecrets(\n\t\tstage,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\n\t// Write secrets\n\tawait writeStageSecrets(secrets, workspace.root);\n\tlogger.log(` Secrets written to .gkm/secrets/${stage}.json`);\n\n\t// Offer to push to SSM if configured\n\tif (isSSMConfigured(workspace) && !options.yes) {\n\t\tconst { shouldPush } = await prompts({\n\t\t\ttype: 'confirm',\n\t\t\tname: 'shouldPush',\n\t\t\tmessage: 'Push secrets to SSM for team sharing?',\n\t\t\tinitial: true,\n\t\t});\n\n\t\tif (shouldPush) {\n\t\t\ttry {\n\t\t\t\tawait pushSecrets(stage, workspace);\n\t\t\t\tlogger.log('☁️ Secrets pushed to SSM');\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(`⚠️ Failed to push to SSM: ${(error as Error).message}`);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn secrets;\n}\n\n/**\n * Print setup summary with next steps.\n */\nfunction printSummary(workspace: NormalizedWorkspace, stage: string): void {\n\tlogger.log(`\\n${'─'.repeat(50)}`);\n\tlogger.log('\\n✅ Development environment ready!\\n');\n\n\tlogger.log('📋 Apps:');\n\tfor (const [name, app] of Object.entries(workspace.apps)) {\n\t\tconst icon =\n\t\t\tapp.type === 'web' ? '🌐' : app.type === 'mobile' ? '📱' : '🔧';\n\t\tlogger.log(` ${icon} ${name} → http://localhost:${app.port}`);\n\t}\n\n\tlogger.log('\\n🚀 Next steps:');\n\tlogger.log(' gkm dev # Start all apps');\n\tlogger.log(` gkm secrets:show --stage ${stage} # View secrets`);\n\tlogger.log('');\n}\n"],"mappings":";;;;;;;;;;;;;;AAwBA,MAAMA,WAAS;AAEf,MAAM,MAAM,UAAU,SAAS;;;;;;;;;AAe/B,SAAS,aACRC,UACAC,aACoD;AACpD,KAAI,aAAa,SAChB,QAAO;EACN,UAAU;EACV,OAAO,CACN,CACC,QACA;GACC;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACA,CACD,CACD;CACD;AAGF,KAAI,aAAa,SAAS;EAGzB,MAAM,SAAS,WAAW,mCAAmC;AAE7D,SAAO;GACN,UAAU,SAAS,yBAAyB;GAC5C,OAAO,SACJ,CACA,CACC,QACA;IACC;IACA;IACA;GACA,CACD,GACD,CAAC,QAAQ,CAAC,wBAAyB,CAAC,CACpC,IACA,CACA,CACC,QACA;IACC;IACA;IACA;GACA,CACD,GACD,CAAC,QAAQ,CAAC,iBAAkB,CAAC,CAC7B;EACH;CACD;AAED,OAAM,IAAI,oBAAoB,UAAU;AACxC;;;;;;;AAQD,eAAsB,aAAaC,UAAwB,CAAE,GAAiB;CAC7E,MAAM,EAAE,WAAW,GAAG,MAAM,qBAAqB;CACjD,MAAM,cAAc,KAAK,UAAU,MAAM,cAAc;AAEvD,MAAK,WAAW,YAAY,CAC3B,OAAM,IAAI,iBAAiB;CAG5B,MAAM,EAAE,UAAU,OAAO,GAAG,aAAa,QAAQ,UAAU,YAAY;AAEvE,UAAO,KAAK,uCAAuC,SAAS,EAAE;AAC9D,UAAO,KAAK,KAAK,YAAY,IAAI;AAEjC,KAAI,QAAQ,QAAQ;AACnB,OAAK,MAAM,CAAC,SAAS,KAAK,IAAI,MAC7B,UAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,KAAK,IAAI,CAAC,EAAE;AAE9C;CACA;AAED,UAAO,IAAI,qCAAqC;AAEhD,MAAK,MAAM,CAAC,SAAS,KAAK,IAAI,MAG7B,OAAM,IAAI,SAAS,MAAM,EAAE,OAAO,UAAW,EAAU;AAGxD,UAAO,IAAI,iEAAiE;AAC5E;;;;;;;;;;;AAYD,eAAsB,cACrBC,MACAC,KACAC,UAAyD,CAAE,GAC3C;AAChB,KAAI,QAAQ,eAAe,MAAO;AAClC,KAAI,MAAM,UAAU,IAAI,CAAE;AAE1B,KAAI,QAAQ,eAAe,SAAS,QAAQ,WAAW;AAKtD,OAAK,QAAQ,MAAM,OAAO;AACzB,YAAO,IACN,kGAEA;AACD;EACA;AAKD,MAAI,MAAM,SAAS,KAAK,CAAE;EAE1B,MAAM,EAAE,SAAS,GAAG,MAAM,QAAQ;GACjC,MAAM;GACN,MAAM;GACN,SACC;GAED,SAAS;EACT,EAAC;AAEF,OAAK,SAAS;AACb,SAAM,eAAe,KAAK;AAC1B,YAAO,IACN,qEACA;AACD;EACA;CACD;AAED,OAAM,cAAc;AACpB;;AAGD,MAAM,cAAc;AAEpB,eAAe,SAASF,MAAgC;AACvD,KAAI;EACH,MAAM,MAAM,MAAM,SAAS,KAAK,MAAM,YAAY,EAAE,QAAQ;AAE5D,SAAQ,KAAK,MAAM,IAAI,CAA4B,aAAa;CAChE,QAAO;AACP,SAAO;CACP;AACD;AAED,eAAe,eAAeA,MAA6B;CAC1D,MAAM,OAAO,KAAK,MAAM,YAAY;AAEpC,OAAM,MAAM,QAAQ,KAAK,EAAE,EAAE,WAAW,KAAM,EAAC;AAC/C,OAAM,UAAU,OAAO,EAAE,KAAK,UAAU,EAAE,UAAU,KAAM,GAAE,MAAM,EAAE,CAAC,IAAI;AACzE;;;;;;;;;;;;;AAcD,eAAsB,UAAUC,KAA+B;CAC9D,MAAM,EAAE,qBAAqB,SAAU,GAAG,KAAK,GAAG,QAAQ;AAE1D,KAAI;AACH,QAAM,IACL,QAAQ,UACR,CAAC,OAAO,QAAQ,KAAK,UAAU,IAAI,CAAC,kCAAmC,GACvE;GAAE;GAAK,SAAS;EAAQ,EACxB;AAED,SAAO;CACP,QAAO;AACP,SAAO;CACP;AACD;;AAkBD,IAAa,mBAAb,cAAsC,MAAM;CAC3C,YAAqBE,MAAc;AAClC,SACE,oCAAoC,KAAK,4JAG1C;EALmB;AAMpB,OAAK,OAAO;CACZ;AACD;;AAGD,IAAa,sBAAb,cAAyC,MAAM;CAC9C,YACUC,UACAN,aACR;AACD,SACE,8DACG,SAAS,mBAAmB,YAAY,yFAE5C;EAPQ;EACA;AAOT,OAAK,OAAO;CACZ;AACD;;;;ACjQD,MAAM,SAAS;;;;;;;;;;AAkBf,eAAsB,aAAaO,UAAwB,CAAE,GAAiB;CAC7E,MAAM,QAAQ,QAAQ,SAAS;AAE/B,QAAO,IAAI,+CAA+C;CAG1D,IAAIC;AACJ,KAAI;AACH,iBAAe,MAAM,qBAAqB;CAC1C,QAAO;AACP,SAAO,MACN,oEACA;AACD,UAAQ,KAAK,EAAE;CACf;CAED,MAAM,EAAE,WAAW,GAAG;CACtB,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AAExD,QAAO,KAAK,gBAAgB,UAAU,KAAK,EAAE;AAC7C,QAAO,KAAK,WAAW,OAAO,KAAK,UAAU,KAAK,CAAC,KAAK,KAAK,CAAC,EAAE;AAChE,QAAO,KAAK,YAAY,MAAM,IAAI;CAGlC,MAAM,UAAU,MAAM,eAAe,OAAO,WAAW,QAAQ;AAE/D,MAAK,SAAS;AACb,SAAO,MAAM,wCAAwC;AACrD,UAAQ,KAAK,EAAE;CACf;CAGD,MAAM,aAAa,MAAM,kBAAkB,WAAW,MAAM;AAE5D,KAAI,cAAc,WAAW,SAAS,WAAW,EAAE;AAClD,QAAM,0BAA0B,SAAS,UAAU,KAAK;AACxD,SAAO,IAAI,mDAAmD;CAC9D;AAGD,MAAK,QAAQ,YAAY;AACxB,SAAO,IAAI,GAAG;AAEd,QAAM,eAAe,WAAW,OAAO,QAAQ;CAC/C;AAGD,cAAa,WAAW,MAAM;AAC9B;;;;;;;;AASD,eAAe,eACdC,WACAC,OACAH,SACgB;CAChB,MAAM,SAAS,MAAM,mBAAmB,WAAW,EAAE,MAAO,EAAC;AAE7D,KAAI,OAAO,KAAK,WAAW,WAAW,GAAG;AACxC,SAAO,IAAI,4BAA4B;AACvC;CACA;AAED,MAAK,OAAO,SAAS;AACpB,SAAO,IAAI,iCAAiC;AAC5C;CACA;AAED,QAAO,KAAK,eAAe,OAAO,KAAK,WAAW,KAAK,KAAK,CAAC,EAAE;AAC/D,MAAK,MAAM,CAAC,WAAW,QAAQ,IAAI,OAAO,QAAQ,OAAO,UAAU,CAClE,QAAO,KAAK,KAAK,UAAU,IAAI,QAAQ,EAAE;CAK1C,MAAM,SAAS,OAAO,KAAK,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,cAAc;CAC1E,MAAM,MAAM,SAAS,OAAO,IAAI,OAAO;AAEvC,KAAI,IACH,OAAM,cAAc,UAAU,MAAM,KAAK;EACxC,GAAI,UAAU,SAAS,0BACpB,CAAE,IACF,EAAE,YAAY,UAAU,SAAS,aAAc;EAClD,GAAI,QAAQ,MAAM,EAAE,WAAW,KAAM,IAAG,CAAE;CAC1C,EAAC;AAEH;;;;;;;;;AAUD,eAAe,eACdG,OACAD,WACAF,SACC;AAED,KAAI,QAAQ,OAAO;AAClB,SAAO,IAAI,2CAA2C;AACtD,SAAO,qBAAqB,OAAO,WAAW,QAAQ;CACtD;AAGD,KAAI,aAAa,OAAO,UAAU,KAAK,EAAE;AACxC,SAAO,IAAI,kCAAkC;EAC7C,MAAM,UAAU,MAAM,iBAAiB,OAAO,UAAU,KAAK;AAC7D,MAAI,SAAS;GAEZ,MAAM,aAAa,iBAClB,SACA,WACA,MAAM,kBAAkB,WAAW,MAAM,CACzC;AACD,OAAI,WACH,OAAM,kBAAkB,YAAY,UAAU,KAAK;AAEpD,UAAO,cAAc;EACrB;CACD;AAGD,KAAI,gBAAgB,UAAU,EAAE;AAC/B,SAAO,IAAI,4CAA4C;AACvD,MAAI;GACH,MAAM,gBAAgB,MAAM,YAAY,OAAO,UAAU;AACzD,OAAI,eAAe;AAClB,WAAO,IAAI,4BAA4B;AACvC,UAAM,kBAAkB,eAAe,UAAU,KAAK;AACtD,WAAO;GACP;AACD,UAAO,IAAI,6BAA6B;EACxC,SAAQ,OAAO;AACf,UAAO,MAAM,+BAAgC,MAAgB,QAAQ,EAAE;EACvE;CACD;AAGD,QAAO,IAAI,6CAA6C;AACxD,QAAO,qBAAqB,OAAO,WAAW,QAAQ;AACtD;;;;;;;;;;;;;;;AAgBD,SAAS,qBACRI,YACuB;CACvB,MAAMC,cAA4D;EACjE,UAAU;EACV,OAAO;EACP,OAAO;EACP,SAAS;EACT,YAAY;EACZ,UAAU;CACV;AAED,QAAO,CACN,GAAG,IAAI,IACN,WACE,IAAI,CAAC,cAAc,YAAY,WAAW,CAC1C,OAAO,CAAC,SAAqC,QAAQ,KAAK,CAAC,CAE9D;AACD;AAED,SAAgB,iBACfC,SACAJ,WACAE,aAAgC,CAAE,GACZ;CACtB,IAAI,UAAU;CACd,IAAI,SAAS,EAAE,GAAG,QAAS;AAG3B,MAAK,MAAM,QAAQ,qBAAqB,WAAW,CAClD,MAAK,OAAO,SAAS,OAAO;EAC3B,MAAM,QAAQ,2BAA2B,KAAK;AAE9C,MAAI,SAAS,SAAS;AACrB,SAAM,SAAS,UAAU;AACzB,SAAM,WAAW,UAAU;EAC3B;AACD,WAAS;GACR,GAAG;GACH,UAAU;IAAE,GAAG,OAAO;KAAW,OAAO;GAAO;EAC/C;AACD,SAAO,OAAO,uBACb,OAAO,UACP,OAAO,cACP;AACD,SAAO,KAAK,4CAA4C,KAAK,EAAE;AAC/D,YAAU;CACV;AAIF,KAAI,OAAO,SAAS,aAAa,OAAO,SAAS,QAAQ;AACxD,WAAS;GACR,GAAG;GACH,UAAU;IACT,GAAG,OAAO;IACV,QAAQ;KACP,MAAM,OAAO,SAAS,SAAS;KAC/B,MAAM,OAAO,SAAS,SAAS;KAC/B,UAAU;KACV,UAAU,wBAAwB;KAClC,UAAU,OAAO,SAAS,SAAS,YAAY;IAC/C;GACD;EACD;AACD,SAAO,OAAO,uBAAuB,OAAO,UAAU,OAAO,cAAc;AAC3E,SAAO,IAAI,mDAAmD;AAC9D,YAAU;CACV;CAGD,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,KAAI,YAAY;EACf,MAAM,WAAW,+BAA+B,WAAW,WAAW;EACtE,MAAMG,UAAkC,CAAE;AAE1C,OAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,SAAS,CAClD,OAAM,OAAO,OAAO,QACnB,SAAQ,OAAO;AAIjB,MAAI,OAAO,KAAK,QAAQ,CAAC,SAAS,GAAG;AACpC,UAAO,KACL,gCAAgC,OAAO,KAAK,QAAQ,CAAC,KAAK,KAAK,CAAC,EACjE;AACD,YAAS;IACR,GAAG;IACH,QAAQ;KAAE,GAAG,OAAO;KAAQ,GAAG;IAAS;GACxC;AACD,aAAU;EACV;CACD;AAED,MAAK,QACJ,QAAO;AAGR,QAAO;EACN,GAAG;EACH,WAAW,qBAAI,QAAO,aAAa;CACnC;AACD;;;;;;;;AASD,SAAgB,4BACfJ,OACAD,WACAE,aAAgC,CAAE,GACnB;CAKf,MAAM,eAAe,qBAAqB,WAAW;CAGrD,MAAM,UAAU,mBAAmB,OAAO,cAAc;EACvD,aAAa,UAAU;EACvB,eAAe,UAAU,SAAS;CAClC,EAAC;CAGF,MAAM,aAAa,OAAO,KAAK,UAAU,KAAK,CAAC,SAAS;AACxD,KAAI,WACH,SAAQ,SAAS,+BAA+B,WAAW,WAAW;KAEtE,SAAQ,SAAS;EAChB,UAAU;EACV,MAAM;EACN,WAAW;EACX,aAAa,MAAM,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC;CACrE;AAGF,QAAO;AACP;;;;;;;;;;;AAYD,eAAsB,mBACrBD,OACAK,MAAc,QAAQ,KAAK,EACR;CACnB,MAAM,EAAE,WAAW,GAAG,MAAM,oBAAoB,IAAI;AAEpD,KAAI,aAAa,OAAO,UAAU,KAAK,CACtC,QAAO;CAGR,MAAM,UAAU,4BACf,OACA,WACA,MAAM,kBAAkB,WAAW,MAAM,CACzC;AACD,OAAM,kBAAkB,SAAS,UAAU,KAAK;AAChD,QAAO;AACP;;;;AAKD,eAAe,qBACdL,OACAD,WACAF,SACC;CACD,MAAM,UAAU,4BACf,OACA,WACA,MAAM,kBAAkB,WAAW,MAAM,CACzC;AAGD,OAAM,kBAAkB,SAAS,UAAU,KAAK;AAChD,QAAO,KAAK,qCAAqC,MAAM,OAAO;AAG9D,KAAI,gBAAgB,UAAU,KAAK,QAAQ,KAAK;EAC/C,MAAM,EAAE,YAAY,GAAG,MAAM,QAAQ;GACpC,MAAM;GACN,MAAM;GACN,SAAS;GACT,SAAS;EACT,EAAC;AAEF,MAAI,WACH,KAAI;AACH,SAAM,YAAY,OAAO,UAAU;AACnC,UAAO,IAAI,4BAA4B;EACvC,SAAQ,OAAO;AACf,UAAO,MAAM,6BAA8B,MAAgB,QAAQ,EAAE;EACrE;CAEF;AAED,QAAO;AACP;;;;AAKD,SAAS,aAAaE,WAAgCC,OAAqB;AAC1E,QAAO,KAAK,IAAI,IAAI,OAAO,GAAG,CAAC,EAAE;AACjC,QAAO,IAAI,uCAAuC;AAElD,QAAO,IAAI,WAAW;AACtB,MAAK,MAAM,CAAC,MAAM,IAAI,IAAI,OAAO,QAAQ,UAAU,KAAK,EAAE;EACzD,MAAM,OACL,IAAI,SAAS,QAAQ,OAAO,IAAI,SAAS,WAAW,OAAO;AAC5D,SAAO,KAAK,KAAK,KAAK,GAAG,KAAK,sBAAsB,IAAI,KAAK,EAAE;CAC/D;AAED,QAAO,IAAI,mBAAmB;AAC9B,QAAO,IAAI,iDAAiD;AAC5D,QAAO,KAAK,8BAA8B,MAAM,kBAAkB;AAClE,QAAO,IAAI,GAAG;AACd"}
@@ -0,0 +1,11 @@
1
+ import "./discover-CAlFgYec.mjs";
2
+ import "./config-D8o6I_7Z.mjs";
3
+ import "./workspace-7FqprxuJ.mjs";
4
+ import "./credentials-Dh0Q6HzY.mjs";
5
+ import "./generator-VE2JrFlY.mjs";
6
+ import "./storage-Bi24WfoW.mjs";
7
+ import "./fullstack-secrets-D1o9-G5l.mjs";
8
+ import "./sync-CmEuNZsB.mjs";
9
+ import { createFreshWorkspaceSecrets, ensureStageSecrets, reconcileSecrets, setupCommand } from "./setup-DOsYueeQ.mjs";
10
+
11
+ export { ensureStageSecrets };
@@ -360,4 +360,4 @@ Object.defineProperty(exports, 'writeStageSecrets', {
360
360
  return writeStageSecrets;
361
361
  }
362
362
  });
363
- //# sourceMappingURL=storage-B1wvztiJ.cjs.map
363
+ //# sourceMappingURL=storage-BJmzYxt8.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage-B1wvztiJ.cjs","names":["projectName?: string","stage: string","stage: string","secrets: StageSecrets","keyHex: string","data: EncryptedSecretsFile","key: string","value: string","updated: StageSecrets","password: string","requiredVars: string[]","missing: string[]","provided: string[]"],"sources":["../src/secrets/keystore.ts","../src/secrets/storage.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, join } from 'node:path';\n\n/** Key length for AES-256 encryption */\nconst KEY_LENGTH = 32; // 256 bits\n\n/**\n * Get the keystore directory for a project.\n * Keys are stored at ~/.gkm/{project-name}/\n *\n * @param projectName - Name of the project (defaults to current directory name)\n * @returns Path to the keystore directory\n */\nexport function getKeystoreDir(projectName?: string): string {\n\tconst name = projectName ?? basename(process.cwd());\n\treturn join(homedir(), '.gkm', name);\n}\n\n/**\n * Get the path to a stage's encryption key.\n *\n * @param stage - Stage name (e.g., 'development', 'production')\n * @param projectName - Name of the project (defaults to current directory name)\n * @returns Path to the key file\n */\nexport function getKeyPath(stage: string, projectName?: string): string {\n\treturn join(getKeystoreDir(projectName), `${stage}.key`);\n}\n\n/**\n * Check if a key exists for a stage.\n */\nexport function keyExists(stage: string, projectName?: string): boolean {\n\treturn existsSync(getKeyPath(stage, projectName));\n}\n\n/**\n * Generate a new encryption key for a stage.\n * The key is stored at ~/.gkm/{project-name}/{stage}.key with restricted permissions.\n *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The generated key as a hex string\n */\nexport async function generateKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst keyDir = getKeystoreDir(projectName);\n\tconst keyPath = getKeyPath(stage, projectName);\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' });\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 * Read an encryption key for a stage.\n *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The key as a hex string, or null if not found\n */\nexport async function readKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string | null> {\n\tconst keyPath = getKeyPath(stage, projectName);\n\n\tif (!existsSync(keyPath)) {\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 if not found.\n */\nexport async function requireKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst key = await readKey(stage, projectName);\n\n\tif (!key) {\n\t\tconst name = projectName ?? basename(process.cwd());\n\t\tthrow new Error(\n\t\t\t`Encryption key not found for stage \"${stage}\" in project \"${name}\". ` +\n\t\t\t\t`Expected key at: ${getKeyPath(stage, projectName)}`,\n\t\t);\n\t}\n\n\treturn key;\n}\n\n/**\n * Delete a key for a stage.\n */\nexport async function deleteKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<void> {\n\tconst keyPath = getKeyPath(stage, projectName);\n\n\tif (existsSync(keyPath)) {\n\t\tawait rm(keyPath);\n\t}\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 *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The key as a hex string\n */\nexport async function getOrCreateKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst existingKey = await readKey(stage, projectName);\n\n\tif (existingKey) {\n\t\treturn existingKey;\n\t}\n\n\treturn generateKey(stage, projectName);\n}\n","import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, join } from 'node:path';\nimport { getOrCreateKey, readKey } from './keystore';\nimport type { EmbeddableSecrets, StageSecrets } from './types';\n\n/** Default secrets directory relative to 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 * Get the secrets directory path.\n */\nexport function getSecretsDir(cwd = process.cwd()): string {\n\treturn join(cwd, SECRETS_DIR);\n}\n\n/**\n * Get the secrets file path for a stage.\n */\nexport function getSecretsPath(stage: string, cwd = process.cwd()): string {\n\treturn join(getSecretsDir(cwd), `${stage}.json`);\n}\n\n/**\n * Check if secrets exist for a stage.\n */\nexport function secretsExist(stage: string, cwd = process.cwd()): boolean {\n\treturn existsSync(getSecretsPath(stage, cwd));\n}\n\n/**\n * Initialize an empty StageSecrets object for a stage.\n */\nexport function initStageSecrets(stage: string): StageSecrets {\n\tconst now = new Date().toISOString();\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tservices: {},\n\t\turls: {},\n\t\tcustom: {},\n\t};\n}\n\n/**\n * Encrypt secrets using a key.\n */\nfunction encryptSecretsData(\n\tsecrets: StageSecrets,\n\tkeyHex: string,\n): EncryptedSecretsFile {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst iv = randomBytes(IV_LENGTH);\n\n\t// Serialize secrets to JSON\n\tconst plaintext = JSON.stringify(secrets);\n\n\t// Encrypt\n\tconst cipher = createCipheriv(ALGORITHM, key, iv);\n\tconst ciphertext = Buffer.concat([\n\t\tcipher.update(plaintext, 'utf-8'),\n\t\tcipher.final(),\n\t]);\n\n\t// Get auth tag\n\tconst authTag = cipher.getAuthTag();\n\n\t// Combine ciphertext + auth tag\n\tconst combined = Buffer.concat([ciphertext, authTag]);\n\n\treturn {\n\t\tversion: 1,\n\t\tencrypted: combined.toString('base64'),\n\t\tiv: iv.toString('hex'),\n\t};\n}\n\n/**\n * Decrypt secrets using a key.\n */\nfunction decryptSecretsData(\n\tdata: EncryptedSecretsFile,\n\tkeyHex: string,\n): StageSecrets {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst ivBuffer = Buffer.from(data.iv, 'hex');\n\tconst combined = Buffer.from(data.encrypted, 'base64');\n\n\t// Split ciphertext and auth tag\n\tconst ciphertext = combined.subarray(0, -AUTH_TAG_LENGTH);\n\tconst authTag = combined.subarray(-AUTH_TAG_LENGTH);\n\n\t// Decrypt\n\tconst decipher = createDecipheriv(ALGORITHM, key, ivBuffer);\n\tdecipher.setAuthTag(authTag);\n\n\tconst plaintext = Buffer.concat([\n\t\tdecipher.update(ciphertext),\n\t\tdecipher.final(),\n\t]);\n\n\treturn JSON.parse(plaintext.toString('utf-8')) as StageSecrets;\n}\n\n/**\n * Read secrets for a stage (encrypted).\n * Requires the decryption key to be present at ~/.gkm/{project}/{stage}.key\n *\n * @returns StageSecrets or null if not found\n */\nexport async function readStageSecrets(\n\tstage: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets | null> {\n\tconst path = getSecretsPath(stage, cwd);\n\n\tif (!existsSync(path)) {\n\t\treturn null;\n\t}\n\n\tconst content = await readFile(path, 'utf-8');\n\tconst data = JSON.parse(content);\n\n\t// Check if this is an encrypted file (has version field)\n\tif (data.version === 1 && data.encrypted && data.iv) {\n\t\tconst projectName = basename(cwd);\n\t\tconst key = await readKey(stage, projectName);\n\n\t\tif (!key) {\n\t\t\tthrow new Error(\n\t\t\t\t`Decryption key not found for stage \"${stage}\". ` +\n\t\t\t\t\t`Expected key at: ~/.gkm/${projectName}/${stage}.key`,\n\t\t\t);\n\t\t}\n\n\t\treturn decryptSecretsData(data as EncryptedSecretsFile, key);\n\t}\n\n\t// Legacy: unencrypted format (for backwards compatibility)\n\treturn data as StageSecrets;\n}\n\n/**\n * Write secrets for a stage (encrypted).\n * Creates or uses existing encryption key at ~/.gkm/{project}/{stage}.key\n */\nexport async function writeStageSecrets(\n\tsecrets: StageSecrets,\n\tcwd = process.cwd(),\n): Promise<void> {\n\tconst dir = getSecretsDir(cwd);\n\tconst path = getSecretsPath(secrets.stage, cwd);\n\tconst projectName = basename(cwd);\n\n\t// Ensure directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Get or create encryption key\n\tconst key = await getOrCreateKey(secrets.stage, projectName);\n\n\t// Encrypt and write\n\tconst encrypted = encryptSecretsData(secrets, key);\n\tawait writeFile(path, JSON.stringify(encrypted, null, 2), 'utf-8');\n}\n\n/**\n * Convert StageSecrets to embeddable format (flat key-value pairs).\n * This is what gets encrypted and embedded in the bundle.\n */\nexport function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {\n\treturn {\n\t\t...secrets.urls,\n\t\t...secrets.custom,\n\t\t// Also include individual service credentials if needed\n\t\t...(secrets.services.postgres && {\n\t\t\tPOSTGRES_USER: secrets.services.postgres.username,\n\t\t\tPOSTGRES_PASSWORD: secrets.services.postgres.password,\n\t\t\tPOSTGRES_DB: secrets.services.postgres.database ?? 'app',\n\t\t\tPOSTGRES_HOST: secrets.services.postgres.host,\n\t\t\tPOSTGRES_PORT: String(secrets.services.postgres.port),\n\t\t}),\n\t\t...(secrets.services.redis && {\n\t\t\tREDIS_PASSWORD: secrets.services.redis.password,\n\t\t\tREDIS_HOST: secrets.services.redis.host,\n\t\t\tREDIS_PORT: String(secrets.services.redis.port),\n\t\t}),\n\t\t...(secrets.services.rabbitmq && {\n\t\t\tRABBITMQ_USER: secrets.services.rabbitmq.username,\n\t\t\tRABBITMQ_PASSWORD: secrets.services.rabbitmq.password,\n\t\t\tRABBITMQ_HOST: secrets.services.rabbitmq.host,\n\t\t\tRABBITMQ_PORT: String(secrets.services.rabbitmq.port),\n\t\t\tRABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',\n\t\t}),\n\t\t...(secrets.services.minio && {\n\t\t\tSTORAGE_ACCESS_KEY_ID: secrets.services.minio.username,\n\t\t\tSTORAGE_SECRET_ACCESS_KEY: secrets.services.minio.password,\n\t\t\tSTORAGE_BUCKET: secrets.services.minio.bucket ?? 'app',\n\t\t\tSTORAGE_REGION: 'eu-west-1',\n\t\t\tSTORAGE_FORCE_PATH_STYLE: 'true',\n\t\t}),\n\t\t...(secrets.services.mailpit && {\n\t\t\tSMTP_HOST: secrets.services.mailpit.host,\n\t\t\tSMTP_PORT: String(secrets.services.mailpit.port),\n\t\t\tSMTP_USER: secrets.services.mailpit.username,\n\t\t\tSMTP_PASS: secrets.services.mailpit.password,\n\t\t\tSMTP_SECURE: 'false',\n\t\t\tMAIL_FROM: 'noreply@localhost',\n\t\t}),\n\t\t...(secrets.services.localstack && {\n\t\t\tAWS_ACCESS_KEY_ID:\n\t\t\t\tsecrets.services.localstack.accessKeyId ??\n\t\t\t\tsecrets.services.localstack.username,\n\t\t\tAWS_SECRET_ACCESS_KEY: secrets.services.localstack.password,\n\t\t\tAWS_REGION: secrets.services.localstack.region ?? 'us-east-1',\n\t\t\tAWS_ENDPOINT_URL: `http://${secrets.services.localstack.host}:${secrets.services.localstack.port}`,\n\t\t}),\n\t\t...(secrets.services.pgboss && {\n\t\t\tPGBOSS_DB_USER: secrets.services.pgboss.username,\n\t\t\tPGBOSS_DB_PASSWORD: secrets.services.pgboss.password,\n\t\t}),\n\t};\n}\n\n/**\n * Update a custom secret in the secrets file.\n */\nexport async function setCustomSecret(\n\tstage: string,\n\tkey: string,\n\tvalue: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets> {\n\tconst secrets = await readStageSecrets(stage, cwd);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`Secrets not found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tconst updated: StageSecrets = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: {\n\t\t\t...secrets.custom,\n\t\t\t[key]: value,\n\t\t},\n\t};\n\n\tawait writeStageSecrets(updated, cwd);\n\treturn updated;\n}\n\n/**\n * Mask a password for display (show first 4 and last 2 chars).\n */\nexport function maskPassword(password: string): string {\n\tif (password.length <= 8) {\n\t\treturn '********';\n\t}\n\treturn `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;\n}\n\n/**\n * Result of environment variable validation.\n */\nexport interface EnvValidationResult {\n\t/** Whether all required environment variables are present */\n\tvalid: boolean;\n\t/** List of missing environment variable names */\n\tmissing: string[];\n\t/** List of environment variables that are provided */\n\tprovided: string[];\n\t/** List of environment variables that were required */\n\trequired: string[];\n}\n\n/**\n * Validate that all required environment variables are present in secrets.\n *\n * @param requiredVars - Array of environment variable names required by the application\n * @param secrets - Stage secrets to validate against\n * @returns Validation result with missing and provided variables\n *\n * @example\n * ```typescript\n * const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];\n * const secrets = await readStageSecrets('production');\n * const result = validateEnvironmentVariables(required, secrets);\n *\n * if (!result.valid) {\n * console.error(`Missing environment variables: ${result.missing.join(', ')}`);\n * }\n * ```\n */\nexport function validateEnvironmentVariables(\n\trequiredVars: string[],\n\tsecrets: StageSecrets,\n): EnvValidationResult {\n\tconst embeddable = toEmbeddableSecrets(secrets);\n\tconst availableVars = new Set(Object.keys(embeddable));\n\n\tconst missing: string[] = [];\n\tconst provided: string[] = [];\n\n\tfor (const varName of requiredVars) {\n\t\tif (availableVars.has(varName)) {\n\t\t\tprovided.push(varName);\n\t\t} else {\n\t\t\tmissing.push(varName);\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: missing.length === 0,\n\t\tmissing: missing.sort(),\n\t\tprovided: provided.sort(),\n\t\trequired: [...requiredVars].sort(),\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage-BJmzYxt8.cjs","names":["projectName?: string","stage: string","stage: string","secrets: StageSecrets","keyHex: string","data: EncryptedSecretsFile","key: string","value: string","updated: StageSecrets","password: string","requiredVars: string[]","missing: string[]","provided: string[]"],"sources":["../src/secrets/keystore.ts","../src/secrets/storage.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, join } from 'node:path';\n\n/** Key length for AES-256 encryption */\nconst KEY_LENGTH = 32; // 256 bits\n\n/**\n * Get the keystore directory for a project.\n * Keys are stored at ~/.gkm/{project-name}/\n *\n * @param projectName - Name of the project (defaults to current directory name)\n * @returns Path to the keystore directory\n */\nexport function getKeystoreDir(projectName?: string): string {\n\tconst name = projectName ?? basename(process.cwd());\n\treturn join(homedir(), '.gkm', name);\n}\n\n/**\n * Get the path to a stage's encryption key.\n *\n * @param stage - Stage name (e.g., 'development', 'production')\n * @param projectName - Name of the project (defaults to current directory name)\n * @returns Path to the key file\n */\nexport function getKeyPath(stage: string, projectName?: string): string {\n\treturn join(getKeystoreDir(projectName), `${stage}.key`);\n}\n\n/**\n * Check if a key exists for a stage.\n */\nexport function keyExists(stage: string, projectName?: string): boolean {\n\treturn existsSync(getKeyPath(stage, projectName));\n}\n\n/**\n * Generate a new encryption key for a stage.\n * The key is stored at ~/.gkm/{project-name}/{stage}.key with restricted permissions.\n *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The generated key as a hex string\n */\nexport async function generateKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst keyDir = getKeystoreDir(projectName);\n\tconst keyPath = getKeyPath(stage, projectName);\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' });\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 * Read an encryption key for a stage.\n *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The key as a hex string, or null if not found\n */\nexport async function readKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string | null> {\n\tconst keyPath = getKeyPath(stage, projectName);\n\n\tif (!existsSync(keyPath)) {\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 if not found.\n */\nexport async function requireKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst key = await readKey(stage, projectName);\n\n\tif (!key) {\n\t\tconst name = projectName ?? basename(process.cwd());\n\t\tthrow new Error(\n\t\t\t`Encryption key not found for stage \"${stage}\" in project \"${name}\". ` +\n\t\t\t\t`Expected key at: ${getKeyPath(stage, projectName)}`,\n\t\t);\n\t}\n\n\treturn key;\n}\n\n/**\n * Delete a key for a stage.\n */\nexport async function deleteKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<void> {\n\tconst keyPath = getKeyPath(stage, projectName);\n\n\tif (existsSync(keyPath)) {\n\t\tawait rm(keyPath);\n\t}\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 *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The key as a hex string\n */\nexport async function getOrCreateKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst existingKey = await readKey(stage, projectName);\n\n\tif (existingKey) {\n\t\treturn existingKey;\n\t}\n\n\treturn generateKey(stage, projectName);\n}\n","import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, join } from 'node:path';\nimport { getOrCreateKey, readKey } from './keystore';\nimport type { EmbeddableSecrets, StageSecrets } from './types';\n\n/** Default secrets directory relative to 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 * Get the secrets directory path.\n */\nexport function getSecretsDir(cwd = process.cwd()): string {\n\treturn join(cwd, SECRETS_DIR);\n}\n\n/**\n * Get the secrets file path for a stage.\n */\nexport function getSecretsPath(stage: string, cwd = process.cwd()): string {\n\treturn join(getSecretsDir(cwd), `${stage}.json`);\n}\n\n/**\n * Check if secrets exist for a stage.\n */\nexport function secretsExist(stage: string, cwd = process.cwd()): boolean {\n\treturn existsSync(getSecretsPath(stage, cwd));\n}\n\n/**\n * Initialize an empty StageSecrets object for a stage.\n */\nexport function initStageSecrets(stage: string): StageSecrets {\n\tconst now = new Date().toISOString();\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tservices: {},\n\t\turls: {},\n\t\tcustom: {},\n\t};\n}\n\n/**\n * Encrypt secrets using a key.\n */\nfunction encryptSecretsData(\n\tsecrets: StageSecrets,\n\tkeyHex: string,\n): EncryptedSecretsFile {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst iv = randomBytes(IV_LENGTH);\n\n\t// Serialize secrets to JSON\n\tconst plaintext = JSON.stringify(secrets);\n\n\t// Encrypt\n\tconst cipher = createCipheriv(ALGORITHM, key, iv);\n\tconst ciphertext = Buffer.concat([\n\t\tcipher.update(plaintext, 'utf-8'),\n\t\tcipher.final(),\n\t]);\n\n\t// Get auth tag\n\tconst authTag = cipher.getAuthTag();\n\n\t// Combine ciphertext + auth tag\n\tconst combined = Buffer.concat([ciphertext, authTag]);\n\n\treturn {\n\t\tversion: 1,\n\t\tencrypted: combined.toString('base64'),\n\t\tiv: iv.toString('hex'),\n\t};\n}\n\n/**\n * Decrypt secrets using a key.\n */\nfunction decryptSecretsData(\n\tdata: EncryptedSecretsFile,\n\tkeyHex: string,\n): StageSecrets {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst ivBuffer = Buffer.from(data.iv, 'hex');\n\tconst combined = Buffer.from(data.encrypted, 'base64');\n\n\t// Split ciphertext and auth tag\n\tconst ciphertext = combined.subarray(0, -AUTH_TAG_LENGTH);\n\tconst authTag = combined.subarray(-AUTH_TAG_LENGTH);\n\n\t// Decrypt\n\tconst decipher = createDecipheriv(ALGORITHM, key, ivBuffer);\n\tdecipher.setAuthTag(authTag);\n\n\tconst plaintext = Buffer.concat([\n\t\tdecipher.update(ciphertext),\n\t\tdecipher.final(),\n\t]);\n\n\treturn JSON.parse(plaintext.toString('utf-8')) as StageSecrets;\n}\n\n/**\n * Read secrets for a stage (encrypted).\n * Requires the decryption key to be present at ~/.gkm/{project}/{stage}.key\n *\n * @returns StageSecrets or null if not found\n */\nexport async function readStageSecrets(\n\tstage: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets | null> {\n\tconst path = getSecretsPath(stage, cwd);\n\n\tif (!existsSync(path)) {\n\t\treturn null;\n\t}\n\n\tconst content = await readFile(path, 'utf-8');\n\tconst data = JSON.parse(content);\n\n\t// Check if this is an encrypted file (has version field)\n\tif (data.version === 1 && data.encrypted && data.iv) {\n\t\tconst projectName = basename(cwd);\n\t\tconst key = await readKey(stage, projectName);\n\n\t\tif (!key) {\n\t\t\tthrow new Error(\n\t\t\t\t`Decryption key not found for stage \"${stage}\". ` +\n\t\t\t\t\t`Expected key at: ~/.gkm/${projectName}/${stage}.key`,\n\t\t\t);\n\t\t}\n\n\t\treturn decryptSecretsData(data as EncryptedSecretsFile, key);\n\t}\n\n\t// Legacy: unencrypted format (for backwards compatibility)\n\treturn data as StageSecrets;\n}\n\n/**\n * Write secrets for a stage (encrypted).\n * Creates or uses existing encryption key at ~/.gkm/{project}/{stage}.key\n */\nexport async function writeStageSecrets(\n\tsecrets: StageSecrets,\n\tcwd = process.cwd(),\n): Promise<void> {\n\tconst dir = getSecretsDir(cwd);\n\tconst path = getSecretsPath(secrets.stage, cwd);\n\tconst projectName = basename(cwd);\n\n\t// Ensure directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Get or create encryption key\n\tconst key = await getOrCreateKey(secrets.stage, projectName);\n\n\t// Encrypt and write\n\tconst encrypted = encryptSecretsData(secrets, key);\n\tawait writeFile(path, JSON.stringify(encrypted, null, 2), 'utf-8');\n}\n\n/**\n * Convert StageSecrets to embeddable format (flat key-value pairs).\n * This is what gets encrypted and embedded in the bundle.\n */\nexport function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {\n\treturn {\n\t\t...secrets.urls,\n\t\t...secrets.custom,\n\t\t// Also include individual service credentials if needed\n\t\t...(secrets.services.postgres && {\n\t\t\tPOSTGRES_USER: secrets.services.postgres.username,\n\t\t\tPOSTGRES_PASSWORD: secrets.services.postgres.password,\n\t\t\tPOSTGRES_DB: secrets.services.postgres.database ?? 'app',\n\t\t\tPOSTGRES_HOST: secrets.services.postgres.host,\n\t\t\tPOSTGRES_PORT: String(secrets.services.postgres.port),\n\t\t}),\n\t\t...(secrets.services.redis && {\n\t\t\tREDIS_PASSWORD: secrets.services.redis.password,\n\t\t\tREDIS_HOST: secrets.services.redis.host,\n\t\t\tREDIS_PORT: String(secrets.services.redis.port),\n\t\t}),\n\t\t...(secrets.services.rabbitmq && {\n\t\t\tRABBITMQ_USER: secrets.services.rabbitmq.username,\n\t\t\tRABBITMQ_PASSWORD: secrets.services.rabbitmq.password,\n\t\t\tRABBITMQ_HOST: secrets.services.rabbitmq.host,\n\t\t\tRABBITMQ_PORT: String(secrets.services.rabbitmq.port),\n\t\t\tRABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',\n\t\t}),\n\t\t...(secrets.services.minio && {\n\t\t\tSTORAGE_ACCESS_KEY_ID: secrets.services.minio.username,\n\t\t\tSTORAGE_SECRET_ACCESS_KEY: secrets.services.minio.password,\n\t\t\tSTORAGE_BUCKET: secrets.services.minio.bucket ?? 'app',\n\t\t\tSTORAGE_REGION: 'eu-west-1',\n\t\t\tSTORAGE_FORCE_PATH_STYLE: 'true',\n\t\t}),\n\t\t...(secrets.services.mailpit && {\n\t\t\tSMTP_HOST: secrets.services.mailpit.host,\n\t\t\tSMTP_PORT: String(secrets.services.mailpit.port),\n\t\t\tSMTP_USER: secrets.services.mailpit.username,\n\t\t\tSMTP_PASS: secrets.services.mailpit.password,\n\t\t\tSMTP_SECURE: 'false',\n\t\t\tMAIL_FROM: 'noreply@localhost',\n\t\t}),\n\t\t...(secrets.services.localstack && {\n\t\t\tAWS_ACCESS_KEY_ID:\n\t\t\t\tsecrets.services.localstack.accessKeyId ??\n\t\t\t\tsecrets.services.localstack.username,\n\t\t\tAWS_SECRET_ACCESS_KEY: secrets.services.localstack.password,\n\t\t\tAWS_REGION: secrets.services.localstack.region ?? 'us-east-1',\n\t\t\tAWS_ENDPOINT_URL: `http://${secrets.services.localstack.host}:${secrets.services.localstack.port}`,\n\t\t}),\n\t\t...(secrets.services.pgboss && {\n\t\t\tPGBOSS_DB_USER: secrets.services.pgboss.username,\n\t\t\tPGBOSS_DB_PASSWORD: secrets.services.pgboss.password,\n\t\t}),\n\t};\n}\n\n/**\n * Update a custom secret in the secrets file.\n */\nexport async function setCustomSecret(\n\tstage: string,\n\tkey: string,\n\tvalue: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets> {\n\tconst secrets = await readStageSecrets(stage, cwd);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`Secrets not found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tconst updated: StageSecrets = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: {\n\t\t\t...secrets.custom,\n\t\t\t[key]: value,\n\t\t},\n\t};\n\n\tawait writeStageSecrets(updated, cwd);\n\treturn updated;\n}\n\n/**\n * Mask a password for display (show first 4 and last 2 chars).\n */\nexport function maskPassword(password: string): string {\n\tif (password.length <= 8) {\n\t\treturn '********';\n\t}\n\treturn `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;\n}\n\n/**\n * Result of environment variable validation.\n */\nexport interface EnvValidationResult {\n\t/** Whether all required environment variables are present */\n\tvalid: boolean;\n\t/** List of missing environment variable names */\n\tmissing: string[];\n\t/** List of environment variables that are provided */\n\tprovided: string[];\n\t/** List of environment variables that were required */\n\trequired: string[];\n}\n\n/**\n * Validate that all required environment variables are present in secrets.\n *\n * @param requiredVars - Array of environment variable names required by the application\n * @param secrets - Stage secrets to validate against\n * @returns Validation result with missing and provided variables\n *\n * @example\n * ```typescript\n * const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];\n * const secrets = await readStageSecrets('production');\n * const result = validateEnvironmentVariables(required, secrets);\n *\n * if (!result.valid) {\n * console.error(`Missing environment variables: ${result.missing.join(', ')}`);\n * }\n * ```\n */\nexport function validateEnvironmentVariables(\n\trequiredVars: string[],\n\tsecrets: StageSecrets,\n): EnvValidationResult {\n\tconst embeddable = toEmbeddableSecrets(secrets);\n\tconst availableVars = new Set(Object.keys(embeddable));\n\n\tconst missing: string[] = [];\n\tconst provided: string[] = [];\n\n\tfor (const varName of requiredVars) {\n\t\tif (availableVars.has(varName)) {\n\t\t\tprovided.push(varName);\n\t\t} else {\n\t\t\tmissing.push(varName);\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: missing.length === 0,\n\t\tmissing: missing.sort(),\n\t\tprovided: provided.sort(),\n\t\trequired: [...requiredVars].sort(),\n\t};\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const require_storage = require('./storage-B1wvztiJ.cjs');
1
+ const require_storage = require('./storage-BJmzYxt8.cjs');
2
2
 
3
3
  exports.initStageSecrets = require_storage.initStageSecrets;
4
4
  exports.readStageSecrets = require_storage.readStageSecrets;
@@ -294,4 +294,4 @@ function validateEnvironmentVariables(requiredVars, secrets) {
294
294
 
295
295
  //#endregion
296
296
  export { getKeyPath, getSecretsDir, getSecretsPath, initStageSecrets, maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, validateEnvironmentVariables, writeStageSecrets };
297
- //# sourceMappingURL=storage-dbb9RyBl.mjs.map
297
+ //# sourceMappingURL=storage-Bi24WfoW.mjs.map