@geekmidas/cli 10.0.0-alpha.64 → 10.0.0-alpha.66

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 (243) hide show
  1. package/README.md +15 -1
  2. package/dist/{HostingerProvider-Bx2lHHvu.cjs → HostingerProvider-BznqRC2S.cjs} +5 -5
  3. package/dist/HostingerProvider-BznqRC2S.cjs.map +1 -0
  4. package/dist/{HostingerProvider-Cd4aS5Kj.mjs → HostingerProvider-CY_VpVud.mjs} +4 -4
  5. package/dist/HostingerProvider-CY_VpVud.mjs.map +1 -0
  6. package/dist/{LegacyStateStore-C2abV49N.cjs → LegacyStateStore-BFcTT2QL.cjs} +2 -2
  7. package/dist/{LegacyStateStore-C2abV49N.cjs.map → LegacyStateStore-BFcTT2QL.cjs.map} +1 -1
  8. package/dist/{LegacyStateStore-DIgkGaUP.mjs → LegacyStateStore-CFheFo62.mjs} +2 -2
  9. package/dist/{LegacyStateStore-DIgkGaUP.mjs.map → LegacyStateStore-CFheFo62.mjs.map} +1 -1
  10. package/dist/{LocalStateStore-DiJqKyPy.cjs → LocalStateStore-BVUexSvu.cjs} +2 -2
  11. package/dist/{LocalStateStore-DiJqKyPy.cjs.map → LocalStateStore-BVUexSvu.cjs.map} +1 -1
  12. package/dist/{LocalStateStore-Cy1ZBW7U.mjs → LocalStateStore-Bm3x9oOg.mjs} +2 -2
  13. package/dist/{LocalStateStore-Cy1ZBW7U.mjs.map → LocalStateStore-Bm3x9oOg.mjs.map} +1 -1
  14. package/dist/{OpenApiTsGenerator-BWuDRHe9.cjs → OpenApiTsGenerator-B2zrdyP4.cjs} +2 -2
  15. package/dist/{OpenApiTsGenerator-BWuDRHe9.cjs.map → OpenApiTsGenerator-B2zrdyP4.cjs.map} +1 -1
  16. package/dist/{OpenApiTsGenerator-C6v-IeHt.mjs → OpenApiTsGenerator-BmUAcR7d.mjs} +2 -2
  17. package/dist/{OpenApiTsGenerator-C6v-IeHt.mjs.map → OpenApiTsGenerator-BmUAcR7d.mjs.map} +1 -1
  18. package/dist/{Route53Provider-DVILBsvV.cjs → Route53Provider-Bllus2NR.cjs} +2 -2
  19. package/dist/Route53Provider-Bllus2NR.cjs.map +1 -0
  20. package/dist/{Route53Provider-Cvj40A8I.mjs → Route53Provider-QXxjkZO1.mjs} +2 -2
  21. package/dist/Route53Provider-QXxjkZO1.mjs.map +1 -0
  22. package/dist/{S3StateStore-D3x6ivBL.mjs → S3StateStore-BFHuctWM.mjs} +2 -2
  23. package/dist/{S3StateStore-D3x6ivBL.mjs.map → S3StateStore-BFHuctWM.mjs.map} +1 -1
  24. package/dist/{S3StateStore-Ds0V1Lgm.cjs → S3StateStore-CoXBhILd.cjs} +2 -2
  25. package/dist/{S3StateStore-Ds0V1Lgm.cjs.map → S3StateStore-CoXBhILd.cjs.map} +1 -1
  26. package/dist/{SSMStateStore-CLipvx47.cjs → SSMStateStore-BqCA9TP2.cjs} +2 -2
  27. package/dist/{SSMStateStore-CLipvx47.cjs.map → SSMStateStore-BqCA9TP2.cjs.map} +1 -1
  28. package/dist/{SSMStateStore-DfF174xQ.mjs → SSMStateStore-C1kvHScY.mjs} +2 -2
  29. package/dist/{SSMStateStore-DfF174xQ.mjs.map → SSMStateStore-C1kvHScY.mjs.map} +1 -1
  30. package/dist/{backup-provisioner-DIVCbfee.mjs → backup-provisioner-C8DnCmxK.mjs} +2 -2
  31. package/dist/backup-provisioner-C8DnCmxK.mjs.map +1 -0
  32. package/dist/{backup-provisioner-BmhIO_Yr.cjs → backup-provisioner-oDEsPj02.cjs} +2 -2
  33. package/dist/backup-provisioner-oDEsPj02.cjs.map +1 -0
  34. package/dist/{bundler-DYgm9xsd.mjs → bundler-Dvaiud0h.mjs} +4 -4
  35. package/dist/{bundler-DYgm9xsd.mjs.map → bundler-Dvaiud0h.mjs.map} +1 -1
  36. package/dist/{bundler-C8uJf324.cjs → bundler-orRqUJNZ.cjs} +4 -4
  37. package/dist/{bundler-C8uJf324.cjs.map → bundler-orRqUJNZ.cjs.map} +1 -1
  38. package/dist/{cli-Bogz-zEj.mjs → cli-CfHm0AO7.mjs} +7 -6
  39. package/dist/cli-CfHm0AO7.mjs.map +1 -0
  40. package/dist/{cli-D3jofJ6j.cjs → cli-D1JkkOwg.cjs} +8 -7
  41. package/dist/cli-D1JkkOwg.cjs.map +1 -0
  42. package/dist/compose-CNQL8BnO.mjs +1398 -0
  43. package/dist/compose-CNQL8BnO.mjs.map +1 -0
  44. package/dist/compose-jrLy7KGO.cjs +1427 -0
  45. package/dist/compose-jrLy7KGO.cjs.map +1 -0
  46. package/dist/{config-Be-4_I6H.d.cts → config-4XFxX1al.d.cts} +3 -3
  47. package/dist/{config-Be-4_I6H.d.cts.map → config-4XFxX1al.d.cts.map} +1 -1
  48. package/dist/{config-DqNRiQOp.d.mts → config-UtKzhpFb.d.mts} +3 -3
  49. package/dist/{config-DqNRiQOp.d.mts.map → config-UtKzhpFb.d.mts.map} +1 -1
  50. package/dist/config.cjs +3 -3
  51. package/dist/config.d.cts +3 -3
  52. package/dist/config.d.mts +3 -3
  53. package/dist/config.mjs +3 -3
  54. package/dist/{credentials-B70vmLcm.cjs → credentials-Bzbg23n8.cjs} +176 -3
  55. package/dist/credentials-Bzbg23n8.cjs.map +1 -0
  56. package/dist/{credentials-s0wMeZBU.mjs → credentials-Dq1MnWQf.mjs} +128 -3
  57. package/dist/credentials-Dq1MnWQf.mjs.map +1 -0
  58. package/dist/databases-C2p5LGAp.cjs +419 -0
  59. package/dist/databases-C2p5LGAp.cjs.map +1 -0
  60. package/dist/databases-hUHTuwhx.mjs +346 -0
  61. package/dist/databases-hUHTuwhx.mjs.map +1 -0
  62. package/dist/{define-CYs0PEfZ.mjs → define-DR8xGYfL.mjs} +5 -1
  63. package/dist/define-DR8xGYfL.mjs.map +1 -0
  64. package/dist/{define-zUsP72ym.cjs → define-DzRz8FgH.cjs} +5 -1
  65. package/dist/define-DzRz8FgH.cjs.map +1 -0
  66. package/dist/deploy/public.cjs +35 -26
  67. package/dist/deploy/public.cjs.map +1 -1
  68. package/dist/deploy/public.d.cts +140 -20
  69. package/dist/deploy/public.d.cts.map +1 -1
  70. package/dist/deploy/public.d.mts +140 -20
  71. package/dist/deploy/public.d.mts.map +1 -1
  72. package/dist/deploy/public.mjs +13 -13
  73. package/dist/{terminal-Bdv--jhY.cjs → deploy-B7aJi_Ah.cjs} +146 -116
  74. package/dist/deploy-B7aJi_Ah.cjs.map +1 -0
  75. package/dist/{terminal-D7Ntz6WE.mjs → deploy-DWn7YfIc.mjs} +137 -89
  76. package/dist/deploy-DWn7YfIc.mjs.map +1 -0
  77. package/dist/{discover-LMrqsWSU.mjs → discover-BFoJWKGD.mjs} +4 -2
  78. package/dist/discover-BFoJWKGD.mjs.map +1 -0
  79. package/dist/{discover-DHdECueV.cjs → discover-OEfZ9OkA.cjs} +4 -2
  80. package/dist/discover-OEfZ9OkA.cjs.map +1 -0
  81. package/dist/dokploy-B4LcYG1E.cjs +38 -0
  82. package/dist/dokploy-B4LcYG1E.cjs.map +1 -0
  83. package/dist/dokploy-DalwLML5.mjs +38 -0
  84. package/dist/dokploy-DalwLML5.mjs.map +1 -0
  85. package/dist/{encryption-C9X98u2F.cjs → encryption-3KlGS1sl.cjs} +6 -8
  86. package/dist/{encryption-C9X98u2F.cjs.map → encryption-3KlGS1sl.cjs.map} +1 -1
  87. package/dist/{encryption-CKHjMpCr.mjs → encryption-CPfENwWO.mjs} +3 -5
  88. package/dist/{encryption-CKHjMpCr.mjs.map → encryption-CPfENwWO.mjs.map} +1 -1
  89. package/dist/{deploy-ZRXIPp1c.mjs → engine-CunvOG3s.mjs} +2116 -1855
  90. package/dist/engine-CunvOG3s.mjs.map +1 -0
  91. package/dist/{deploy-55rJkdKz.cjs → engine-DR6KurWX.cjs} +2172 -1869
  92. package/dist/engine-DR6KurWX.cjs.map +1 -0
  93. package/dist/{github-Dd3tMa1u.cjs → github-BJ5gh9bw.cjs} +7 -7
  94. package/dist/{github-Dd3tMa1u.cjs.map → github-BJ5gh9bw.cjs.map} +1 -1
  95. package/dist/{github-D-byNI7T.mjs → github-MXA9v7Ge.mjs} +3 -3
  96. package/dist/{github-D-byNI7T.mjs.map → github-MXA9v7Ge.mjs.map} +1 -1
  97. package/dist/{index-CfE9Y885.d.cts → index-CyApaHVe.d.cts} +16 -3
  98. package/dist/index-CyApaHVe.d.cts.map +1 -0
  99. package/dist/{index-CJ2qsSpp.d.mts → index-DD1FfUHh.d.mts} +16 -3
  100. package/dist/index-DD1FfUHh.d.mts.map +1 -0
  101. package/dist/index.cjs +465 -1691
  102. package/dist/index.cjs.map +1 -1
  103. package/dist/index.mjs +405 -1619
  104. package/dist/index.mjs.map +1 -1
  105. package/dist/{local-C7wnrcYK.cjs → local-BdH4XN0E.cjs} +1 -178
  106. package/dist/local-BdH4XN0E.cjs.map +1 -0
  107. package/dist/{local-Cd8ZYQG3.mjs → local-CWR4Y_kW.mjs} +3 -126
  108. package/dist/local-CWR4Y_kW.mjs.map +1 -0
  109. package/dist/{migrate-B4gngTLe.mjs → migrate-BJBcm4sG.mjs} +7 -6
  110. package/dist/{migrate-B4gngTLe.mjs.map → migrate-BJBcm4sG.mjs.map} +1 -1
  111. package/dist/{migrate-tx9-P9uz.cjs → migrate-CF4J4GQS.cjs} +20 -19
  112. package/dist/{migrate-tx9-P9uz.cjs.map → migrate-CF4J4GQS.cjs.map} +1 -1
  113. package/dist/openapi.cjs +1 -1
  114. package/dist/openapi.d.cts +1 -1
  115. package/dist/openapi.d.mts +1 -1
  116. package/dist/openapi.mjs +1 -1
  117. package/dist/provider-B3Sv_a74.cjs +173 -0
  118. package/dist/provider-B3Sv_a74.cjs.map +1 -0
  119. package/dist/provider-C9_-45d5.cjs +56 -0
  120. package/dist/provider-C9_-45d5.cjs.map +1 -0
  121. package/dist/provider-D7rQzzzf.mjs +138 -0
  122. package/dist/provider-D7rQzzzf.mjs.map +1 -0
  123. package/dist/provider-q4lsYzYG.mjs +45 -0
  124. package/dist/provider-q4lsYzYG.mjs.map +1 -0
  125. package/dist/{ready-bQfu9GES.mjs → ready-2s3-CndS.mjs} +3 -3
  126. package/dist/{ready-bQfu9GES.mjs.map → ready-2s3-CndS.mjs.map} +1 -1
  127. package/dist/{ready-brEhh68f.cjs → ready-B6uHr4sm.cjs} +3 -3
  128. package/dist/{ready-brEhh68f.cjs.map → ready-B6uHr4sm.cjs.map} +1 -1
  129. package/dist/reconcile/public.cjs +1 -1
  130. package/dist/reconcile/public.d.cts +1 -1
  131. package/dist/reconcile/public.d.mts +1 -1
  132. package/dist/reconcile/public.mjs +1 -1
  133. package/dist/resolve-CVmch0wI.cjs +315 -0
  134. package/dist/resolve-CVmch0wI.cjs.map +1 -0
  135. package/dist/resolve-Db0tNlrW.mjs +262 -0
  136. package/dist/resolve-Db0tNlrW.mjs.map +1 -0
  137. package/dist/rollback-DvAo4OKS.mjs +133 -0
  138. package/dist/rollback-DvAo4OKS.mjs.map +1 -0
  139. package/dist/rollback-DxJK84hy.cjs +162 -0
  140. package/dist/rollback-DxJK84hy.cjs.map +1 -0
  141. package/dist/{runtime-CJEYK4Gz.d.mts → runtime-BRqFOvPM.d.cts} +2 -2
  142. package/dist/{runtime-CJEYK4Gz.d.mts.map → runtime-BRqFOvPM.d.cts.map} +1 -1
  143. package/dist/{runtime-Cjq1jlVX.d.cts → runtime-CEk5n4VH.d.mts} +2 -2
  144. package/dist/{runtime-Cjq1jlVX.d.cts.map → runtime-CEk5n4VH.d.mts.map} +1 -1
  145. package/dist/sandbox/discover-worker.cjs +1 -1
  146. package/dist/sandbox/discover-worker.mjs +1 -1
  147. package/dist/sandbox/migrate-worker.cjs +61 -0
  148. package/dist/sandbox/migrate-worker.cjs.map +1 -0
  149. package/dist/sandbox/migrate-worker.d.cts +1 -0
  150. package/dist/sandbox/migrate-worker.d.mts +1 -0
  151. package/dist/sandbox/migrate-worker.mjs +62 -0
  152. package/dist/sandbox/migrate-worker.mjs.map +1 -0
  153. package/dist/{schemas-D2LCF689.mjs → schemas-Cfhtpdo0.mjs} +10 -2
  154. package/dist/{schemas-Ckm_aBnF.cjs.map → schemas-Cfhtpdo0.mjs.map} +1 -1
  155. package/dist/{schemas-Ckm_aBnF.cjs → schemas-RUlKHu27.cjs} +15 -1
  156. package/dist/{schemas-D2LCF689.mjs.map → schemas-RUlKHu27.cjs.map} +1 -1
  157. package/dist/sniffer-5xmrkh2a.cjs +1742 -0
  158. package/dist/sniffer-5xmrkh2a.cjs.map +1 -0
  159. package/dist/sniffer-BAQhLEsI.mjs +1617 -0
  160. package/dist/sniffer-BAQhLEsI.mjs.map +1 -0
  161. package/dist/{sst-Cper040I.mjs → sst-B2HbrDR3.mjs} +4 -4
  162. package/dist/{sst-Cper040I.mjs.map → sst-B2HbrDR3.mjs.map} +1 -1
  163. package/dist/{sst-BdALKEas.cjs → sst-DcfsVH6x.cjs} +4 -4
  164. package/dist/{sst-BdALKEas.cjs.map → sst-DcfsVH6x.cjs.map} +1 -1
  165. package/dist/{sniffer-ByhbEW5A.cjs → state-Y6ATakMX.cjs} +1769 -2759
  166. package/dist/state-Y6ATakMX.cjs.map +1 -0
  167. package/dist/{sniffer-49KPbDgq.mjs → state-uuYqnRVO.mjs} +1763 -2764
  168. package/dist/state-uuYqnRVO.mjs.map +1 -0
  169. package/dist/target/public.cjs +6 -6
  170. package/dist/target/public.d.cts +2 -2
  171. package/dist/target/public.d.mts +2 -2
  172. package/dist/target/public.mjs +3 -3
  173. package/dist/terminal-CHy_MdjG.cjs +84 -0
  174. package/dist/terminal-CHy_MdjG.cjs.map +1 -0
  175. package/dist/terminal-DUhcZ1ND.mjs +84 -0
  176. package/dist/terminal-DUhcZ1ND.mjs.map +1 -0
  177. package/dist/{types-DkrDOMxw.d.cts → types-CSw2w69a.d.mts} +110 -20
  178. package/dist/types-CSw2w69a.d.mts.map +1 -0
  179. package/dist/{types-B_bEQvb9.d.mts → types-D5WIxRDP.d.cts} +111 -19
  180. package/dist/types-D5WIxRDP.d.cts.map +1 -0
  181. package/dist/vitest.cjs +1 -1
  182. package/dist/vitest.mjs +1 -1
  183. package/dist/workspace/index.cjs +3 -1
  184. package/dist/workspace/index.d.cts +3 -3
  185. package/dist/workspace/index.d.mts +3 -3
  186. package/dist/workspace/index.mjs +2 -2
  187. package/dist/{workspace-Bwit4q9O.mjs → workspace-Be6uBhlF.mjs} +36 -6
  188. package/dist/workspace-Be6uBhlF.mjs.map +1 -0
  189. package/dist/{workspace-BwgWJK7h.cjs → workspace-ON-bqiJz.cjs} +47 -5
  190. package/dist/workspace-ON-bqiJz.cjs.map +1 -0
  191. package/package.json +13 -13
  192. package/dist/HostingerProvider-Bx2lHHvu.cjs.map +0 -1
  193. package/dist/HostingerProvider-Cd4aS5Kj.mjs.map +0 -1
  194. package/dist/Route53Provider-Cvj40A8I.mjs.map +0 -1
  195. package/dist/Route53Provider-DVILBsvV.cjs.map +0 -1
  196. package/dist/backup-provisioner-BmhIO_Yr.cjs.map +0 -1
  197. package/dist/backup-provisioner-DIVCbfee.mjs.map +0 -1
  198. package/dist/cli-Bogz-zEj.mjs.map +0 -1
  199. package/dist/cli-D3jofJ6j.cjs.map +0 -1
  200. package/dist/credentials-B70vmLcm.cjs.map +0 -1
  201. package/dist/credentials-s0wMeZBU.mjs.map +0 -1
  202. package/dist/define-CYs0PEfZ.mjs.map +0 -1
  203. package/dist/define-zUsP72ym.cjs.map +0 -1
  204. package/dist/deploy-55rJkdKz.cjs.map +0 -1
  205. package/dist/deploy-Dc2LBJaK.mjs +0 -136
  206. package/dist/deploy-Dc2LBJaK.mjs.map +0 -1
  207. package/dist/deploy-ZRXIPp1c.mjs.map +0 -1
  208. package/dist/deploy-yUcR2OlW.cjs +0 -141
  209. package/dist/deploy-yUcR2OlW.cjs.map +0 -1
  210. package/dist/discover-DHdECueV.cjs.map +0 -1
  211. package/dist/discover-LMrqsWSU.mjs.map +0 -1
  212. package/dist/dokploy-CepdXuMl.mjs +0 -32
  213. package/dist/dokploy-CepdXuMl.mjs.map +0 -1
  214. package/dist/dokploy-D1d37rvM.cjs +0 -32
  215. package/dist/dokploy-D1d37rvM.cjs.map +0 -1
  216. package/dist/index-CJ2qsSpp.d.mts.map +0 -1
  217. package/dist/index-CfE9Y885.d.cts.map +0 -1
  218. package/dist/local-C7wnrcYK.cjs.map +0 -1
  219. package/dist/local-Cd8ZYQG3.mjs.map +0 -1
  220. package/dist/resolve-BXsNQjMR.cjs +0 -220
  221. package/dist/resolve-BXsNQjMR.cjs.map +0 -1
  222. package/dist/resolve-DZ2IJ0qY.mjs +0 -173
  223. package/dist/resolve-DZ2IJ0qY.mjs.map +0 -1
  224. package/dist/runtime-BGNDrHzh.cjs +0 -163
  225. package/dist/runtime-BGNDrHzh.cjs.map +0 -1
  226. package/dist/runtime-Cmn-FeYO.mjs +0 -134
  227. package/dist/runtime-Cmn-FeYO.mjs.map +0 -1
  228. package/dist/sniffer-49KPbDgq.mjs.map +0 -1
  229. package/dist/sniffer-ByhbEW5A.cjs.map +0 -1
  230. package/dist/storage-BzaUxmqe.mjs +0 -124
  231. package/dist/storage-BzaUxmqe.mjs.map +0 -1
  232. package/dist/storage-CSGoC3bK.cjs +0 -152
  233. package/dist/storage-CSGoC3bK.cjs.map +0 -1
  234. package/dist/store-BaaonhkJ.mjs +0 -415
  235. package/dist/store-BaaonhkJ.mjs.map +0 -1
  236. package/dist/store-D_2ByRtW.cjs +0 -510
  237. package/dist/store-D_2ByRtW.cjs.map +0 -1
  238. package/dist/terminal-Bdv--jhY.cjs.map +0 -1
  239. package/dist/terminal-D7Ntz6WE.mjs.map +0 -1
  240. package/dist/types-B_bEQvb9.d.mts.map +0 -1
  241. package/dist/types-DkrDOMxw.d.cts.map +0 -1
  242. package/dist/workspace-BwgWJK7h.cjs.map +0 -1
  243. package/dist/workspace-Bwit4q9O.mjs.map +0 -1
@@ -0,0 +1,1617 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
+ import { t as LocalSandbox } from "./local-CWR4Y_kW.mjs";
3
+ import { d as output, s as nodeWithTsx } from "./discover-BFoJWKGD.mjs";
4
+ import { i as activeSandbox } from "./sandbox-Dm247NGx.mjs";
5
+ import { h as getPublicEnvPrefix } from "./workspace-Be6uBhlF.mjs";
6
+ import { dirname, resolve } from "node:path";
7
+ import { existsSync } from "node:fs";
8
+ import { hostname, userInfo } from "node:os";
9
+ import { fileURLToPath } from "node:url";
10
+ import { createCipheriv, createHash, randomBytes, randomUUID } from "node:crypto";
11
+ //#region src/workspace/client-generator.ts
12
+ /**
13
+ * Normalize routes to an array of patterns.
14
+ * Handles a glob or a list of them.
15
+ * @internal Exported for use in dev command
16
+ */
17
+ function normalizeRoutes(routes) {
18
+ if (!routes) return [];
19
+ return Array.isArray(routes) ? routes : [routes];
20
+ }
21
+ //#endregion
22
+ //#region src/secrets/encryption.ts
23
+ var encryption_exports = /* @__PURE__ */ __exportAll({
24
+ encryptSecrets: () => encryptSecrets,
25
+ generateDefineOptions: () => generateDefineOptions,
26
+ keyFingerprint: () => keyFingerprint
27
+ });
28
+ /** AES-256-GCM configuration */
29
+ const ALGORITHM = "aes-256-gcm";
30
+ const KEY_LENGTH = 32;
31
+ const IV_LENGTH = 12;
32
+ /**
33
+ * Encrypt secrets for embedding in a bundle.
34
+ * Uses AES-256-GCM with a randomly generated ephemeral key.
35
+ *
36
+ * @param secrets - Key-value pairs to encrypt
37
+ * @returns Encrypted payload with ephemeral master key
38
+ */
39
+ function encryptSecrets(secrets) {
40
+ const masterKey = randomBytes(KEY_LENGTH);
41
+ const iv = randomBytes(IV_LENGTH);
42
+ const plaintext = JSON.stringify(secrets);
43
+ const cipher = createCipheriv(ALGORITHM, masterKey, iv);
44
+ const ciphertext = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
45
+ const authTag = cipher.getAuthTag();
46
+ return {
47
+ encrypted: Buffer.concat([ciphertext, authTag]).toString("base64"),
48
+ iv: iv.toString("hex"),
49
+ masterKey: masterKey.toString("hex")
50
+ };
51
+ }
52
+ /**
53
+ * Generate the define options for tsdown/esbuild.
54
+ * These will be injected at build time.
55
+ */
56
+ function generateDefineOptions(payload) {
57
+ return {
58
+ __GKM_ENCRYPTED_CREDENTIALS__: JSON.stringify(payload.encrypted),
59
+ __GKM_CREDENTIALS_IV__: JSON.stringify(payload.iv)
60
+ };
61
+ }
62
+ /**
63
+ * A short, one-way name for a master key: the first 8 hex characters of its
64
+ * SHA-256.
65
+ *
66
+ * Printed where the key itself used to be, so a person can tell which key a
67
+ * build used — and match it against the one they hold — without the output
68
+ * carrying a value that decrypts every secret of the stage.
69
+ */
70
+ function keyFingerprint(masterKey) {
71
+ return createHash("sha256").update(masterKey).digest("hex").slice(0, 8);
72
+ }
73
+ //#endregion
74
+ //#region src/deploy/events.ts
75
+ /** An error as an event carries it: its name and message, nothing else. */
76
+ function eventError(error) {
77
+ if (error instanceof Error) return {
78
+ name: error.name,
79
+ message: error.message
80
+ };
81
+ return {
82
+ name: "Error",
83
+ message: String(error)
84
+ };
85
+ }
86
+ //#endregion
87
+ //#region src/deploy/StateProvider.ts
88
+ /**
89
+ * Check if value is a StateProvider implementation.
90
+ */
91
+ function isStateProvider(value) {
92
+ return typeof value === "object" && value !== null && typeof value.read === "function" && typeof value.write === "function";
93
+ }
94
+ //#endregion
95
+ //#region src/deploy/StateStore.ts
96
+ /**
97
+ * Deploy state store.
98
+ *
99
+ * `StateProvider` reads and writes a whole stage at once, with nothing to stop
100
+ * two runs of the same stage from overwriting each other and nowhere to record
101
+ * a resource the moment it exists. A `StateStore` adds the three things a
102
+ * deploy needs to be safe against crashes and concurrent runs:
103
+ *
104
+ * - `lock(stage)`: one run per stage. A second gets `StateLocked`, naming the
105
+ * holder, instead of racing the first.
106
+ * - Versioned `read` and conditional `write`: a write names the version it was
107
+ * based on, and raises `StateVersionConflict` when the stored state moved on
108
+ * underneath it, so nothing is lost silently even without the lock.
109
+ * - Per-resource records: `putResource` journals one resource at a time, so a
110
+ * run that dies half way leaves behind the ids of everything it created.
111
+ *
112
+ * Built-in stores: `LocalStateStore`, `SSMStateStore`, `S3StateStore`. A
113
+ * custom `StateProvider` is wrapped by `LegacyStateStore`, which keeps it
114
+ * working but cannot lock.
115
+ */
116
+ var StateLocked = class extends Error {
117
+ stage;
118
+ holder;
119
+ location;
120
+ constructor(stage, holder, location) {
121
+ const by = holder ? `${holder.owner}@${holder.host} (pid ${holder.pid}) since ${holder.acquiredAt}${holder.operation ? ` for '${holder.operation}'` : ""}` : "a run that did not record who it was";
122
+ super(`Deploy state for stage '${stage}' is locked by ${by} (${location}). Wait for that run to finish; if it crashed, release the lock with \`gkm state:unlock --stage ${stage}\`.`);
123
+ this.stage = stage;
124
+ this.holder = holder;
125
+ this.location = location;
126
+ this.name = "StateLocked";
127
+ }
128
+ };
129
+ var StateVersionConflict = class extends Error {
130
+ stage;
131
+ expectedVersion;
132
+ actualVersion;
133
+ constructor(stage, expectedVersion, actualVersion) {
134
+ super(`Deploy state for stage '${stage}' changed since it was read (expected ${expectedVersion ? `version ${expectedVersion}` : "no state"}, found ${actualVersion ? `version ${actualVersion}` : "no state"}). Another run wrote it: read the state again and re-apply the change, and hold the stage's lock so runs take turns.`);
135
+ this.stage = stage;
136
+ this.expectedVersion = expectedVersion;
137
+ this.actualVersion = actualVersion;
138
+ this.name = "StateVersionConflict";
139
+ }
140
+ };
141
+ var StageStateMissing = class extends Error {
142
+ stage;
143
+ constructor(stage) {
144
+ super(`There is no deploy state for stage '${stage}' to record a resource in. Write the stage's state first (write with expectedVersion: null).`);
145
+ this.stage = stage;
146
+ this.name = "StageStateMissing";
147
+ }
148
+ };
149
+ var StateUnreadable = class extends Error {
150
+ stage;
151
+ location;
152
+ constructor(stage, location, cause) {
153
+ super(`Deploy state for stage '${stage}' at ${location} is not valid JSON. Restore it from a backup (or the provider's history) rather than deploying without it — a deploy without state recreates every resource.`, { cause });
154
+ this.stage = stage;
155
+ this.location = location;
156
+ this.name = "StateUnreadable";
157
+ }
158
+ };
159
+ var StateSchemaTooNew = class extends Error {
160
+ stage;
161
+ schemaVersion;
162
+ constructor(stage, schemaVersion) {
163
+ super(`Deploy state for stage '${stage}' has schema version ${String(schemaVersion)}, newer than this @geekmidas/cli understands (2). Upgrade @geekmidas/cli to the version that wrote it.`);
164
+ this.stage = stage;
165
+ this.schemaVersion = schemaVersion;
166
+ this.name = "StateSchemaTooNew";
167
+ }
168
+ };
169
+ /**
170
+ * Not thrown: emitted (once per store) when a custom `StateProvider` stands
171
+ * in for a store, so a deploy that cannot be protected from a concurrent one
172
+ * says so.
173
+ */
174
+ var StateStoreWithoutLocking = class extends Error {
175
+ provider;
176
+ constructor(provider) {
177
+ super(`The custom state provider '${provider}' has no lock and no versions, so two deploys of the same stage can still overwrite each other. Implement StateStore (lock, versioned read, conditional write) or use the 'local', 'ssm' or 's3' state provider.`);
178
+ this.provider = provider;
179
+ this.name = "StateStoreWithoutLocking";
180
+ }
181
+ };
182
+ var StateStoreNeedsWorkspaceName = class extends Error {
183
+ provider;
184
+ constructor(provider) {
185
+ super(`The '${provider}' state provider keys state by workspace name. Set "name" in gkm.config.ts.`);
186
+ this.provider = provider;
187
+ this.name = "StateStoreNeedsWorkspaceName";
188
+ }
189
+ };
190
+ var UnknownStateProvider = class extends Error {
191
+ config;
192
+ constructor(config) {
193
+ super(`Unknown state provider ${JSON.stringify(config)}. Use 'local', 'ssm', 's3', or an object implementing StateStore or StateProvider.`);
194
+ this.config = config;
195
+ this.name = "UnknownStateProvider";
196
+ }
197
+ };
198
+ /** A lock or write that could not get at the state backend in time. */
199
+ var StateStoreBusy = class extends Error {
200
+ stage;
201
+ location;
202
+ constructor(stage, location) {
203
+ super(`Timed out waiting to write deploy state for stage '${stage}' (${location}). If no other gkm process is running, remove ${location}.`);
204
+ this.stage = stage;
205
+ this.location = location;
206
+ this.name = "StateStoreBusy";
207
+ }
208
+ };
209
+ function decodeDocument(stage, body, location) {
210
+ let parsed;
211
+ try {
212
+ parsed = JSON.parse(body);
213
+ } catch (error) {
214
+ throw new StateUnreadable(stage, location, error);
215
+ }
216
+ if (typeof parsed !== "object" || parsed === null) throw new StateUnreadable(stage, location, parsed);
217
+ const schemaVersion = parsed.schemaVersion;
218
+ if (schemaVersion === void 0) return {
219
+ schemaVersion: 1,
220
+ state: parsed
221
+ };
222
+ if (schemaVersion === 2) return {
223
+ schemaVersion: 2,
224
+ document: parsed
225
+ };
226
+ throw new StateSchemaTooNew(stage, schemaVersion);
227
+ }
228
+ function encodeDocument(document) {
229
+ return JSON.stringify(document, null, 2);
230
+ }
231
+ /**
232
+ * v1 state as a v2 document. The ids v1 kept are seeded as `ready` records,
233
+ * so the first journaled deploy after an upgrade adopts them rather than
234
+ * treating them as unknown.
235
+ */
236
+ function migrateV1(stage, state) {
237
+ const now = (/* @__PURE__ */ new Date()).toISOString();
238
+ const resources = {};
239
+ const seed = (type, name, id) => {
240
+ if (!id) return;
241
+ const key = name ? `${type}:${name}` : type;
242
+ resources[key] = {
243
+ key,
244
+ type,
245
+ id,
246
+ status: "ready",
247
+ updatedAt: now
248
+ };
249
+ };
250
+ seed("project", null, state.projectId);
251
+ seed("environment", null, state.environmentId);
252
+ for (const [app, id] of Object.entries(state.applications ?? {})) seed("application", app, id);
253
+ seed("postgres", null, state.services?.postgresId);
254
+ seed("redis", null, state.services?.redisId);
255
+ seed("backup-destination", null, state.backups?.destinationId);
256
+ return {
257
+ schemaVersion: 2,
258
+ stage,
259
+ serial: 1,
260
+ state,
261
+ resources,
262
+ updatedAt: now
263
+ };
264
+ }
265
+ function newLockHolder(options) {
266
+ let owner = "unknown";
267
+ try {
268
+ owner = userInfo().username;
269
+ } catch {}
270
+ return {
271
+ id: randomUUID(),
272
+ owner,
273
+ host: hostname(),
274
+ pid: process.pid,
275
+ acquiredAt: (/* @__PURE__ */ new Date()).toISOString(),
276
+ operation: options?.operation
277
+ };
278
+ }
279
+ function contentVersion(body) {
280
+ return createHash("sha256").update(body).digest("hex").slice(0, 16);
281
+ }
282
+ /**
283
+ * A store over a backend that can read and conditionally write one blob per
284
+ * stage, and create a lock object only if it does not exist. Migration and
285
+ * resource records are the same for every such backend, so they live here.
286
+ */
287
+ var DocumentStateStore = class {
288
+ async lock(stage, options) {
289
+ const holder = newLockHolder(options);
290
+ await this.createLock(stage, holder);
291
+ let released = false;
292
+ return {
293
+ stage,
294
+ holder,
295
+ release: async () => {
296
+ if (released) return;
297
+ released = true;
298
+ await this.removeLock(stage, holder);
299
+ }
300
+ };
301
+ }
302
+ async forceUnlock(stage) {
303
+ const holder = await this.readLock(stage);
304
+ await this.removeLock(stage, null);
305
+ return holder;
306
+ }
307
+ async read(stage) {
308
+ const current = await this.readDocument(stage);
309
+ if (!current) return null;
310
+ return {
311
+ state: current.document.state,
312
+ resources: current.document.resources,
313
+ version: current.version
314
+ };
315
+ }
316
+ async write(stage, state, options) {
317
+ if (options.expectedVersion === null) {
318
+ const document = {
319
+ schemaVersion: 2,
320
+ stage,
321
+ serial: 1,
322
+ state,
323
+ resources: {},
324
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
325
+ };
326
+ return this.writeRaw(stage, encodeDocument(document), null);
327
+ }
328
+ return this.mutate(stage, options.expectedVersion, (document) => ({
329
+ ...document,
330
+ state
331
+ }));
332
+ }
333
+ async putResource(stage, record, options) {
334
+ return this.mutate(stage, options?.expectedVersion, (document) => ({
335
+ ...document,
336
+ resources: {
337
+ ...document.resources,
338
+ [record.key]: {
339
+ ...record,
340
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
341
+ }
342
+ }
343
+ }));
344
+ }
345
+ async deleteResource(stage, key, options) {
346
+ return this.mutate(stage, options?.expectedVersion, (document) => {
347
+ const { [key]: _removed, ...resources } = document.resources;
348
+ return {
349
+ ...document,
350
+ resources
351
+ };
352
+ });
353
+ }
354
+ async mutate(stage, expectedVersion, change) {
355
+ const current = await this.readDocument(stage);
356
+ if (!current) {
357
+ if (expectedVersion !== void 0) throw new StateVersionConflict(stage, expectedVersion, null);
358
+ throw new StageStateMissing(stage);
359
+ }
360
+ if (expectedVersion !== void 0 && current.version !== expectedVersion) throw new StateVersionConflict(stage, expectedVersion, current.version);
361
+ const next = change(current.document);
362
+ next.serial = current.document.serial + 1;
363
+ next.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
364
+ return this.writeRaw(stage, encodeDocument(next), current.version);
365
+ }
366
+ /**
367
+ * Reads the stage's document, migrating v1 in place on the way. The
368
+ * migration is itself a conditional write: if another run migrated (or
369
+ * wrote) first, its result is read instead.
370
+ */
371
+ async readDocument(stage, attempt = 0) {
372
+ const raw = await this.readRaw(stage);
373
+ if (!raw) return null;
374
+ const decoded = decodeDocument(stage, raw.body, this.location(stage));
375
+ if (decoded.schemaVersion === 2) return {
376
+ document: decoded.document,
377
+ version: raw.version
378
+ };
379
+ await this.writeV1Backup(stage, raw.body);
380
+ const document = migrateV1(stage, decoded.state);
381
+ try {
382
+ return {
383
+ document,
384
+ version: await this.writeRaw(stage, encodeDocument(document), raw.version)
385
+ };
386
+ } catch (error) {
387
+ if (error instanceof StateVersionConflict && attempt < 2) return this.readDocument(stage, attempt + 1);
388
+ throw error;
389
+ }
390
+ }
391
+ };
392
+ function isStateStore(value) {
393
+ return typeof value === "object" && value !== null && typeof value.lock === "function" && typeof value.read === "function" && typeof value.write === "function" && typeof value.putResource === "function";
394
+ }
395
+ /**
396
+ * The store for a workspace's `state` config:
397
+ *
398
+ * - none / `'local'`: `LocalStateStore` under `.gkm/`
399
+ * - `'ssm'`: `SSMStateStore`, with no local cache — SSM is read directly, so
400
+ * a stale local copy can never win over it
401
+ * - `'s3'`: `S3StateStore`
402
+ * - a `StateStore` object: used as is
403
+ * - a `StateProvider` object: wrapped by `LegacyStateStore`
404
+ */
405
+ async function createStateStore(options) {
406
+ const { config, workspaceRoot, workspaceName } = options;
407
+ if (!config || config.provider === "local") {
408
+ const { LocalStateStore } = await import("./LocalStateStore-Bm3x9oOg.mjs").then((n) => n.n);
409
+ return new LocalStateStore(workspaceRoot);
410
+ }
411
+ if (isStateStore(config.provider)) return config.provider;
412
+ if (isStateProvider(config.provider)) {
413
+ const { LegacyStateStore } = await import("./LegacyStateStore-CFheFo62.mjs");
414
+ return new LegacyStateStore(config.provider, { warn: options.warn });
415
+ }
416
+ if (config.provider === "ssm") {
417
+ if (!workspaceName) throw new StateStoreNeedsWorkspaceName("ssm");
418
+ const { SSMStateStore } = await import("./SSMStateStore-C1kvHScY.mjs");
419
+ const ssm = config;
420
+ return SSMStateStore.create({
421
+ workspaceName,
422
+ region: ssm.region,
423
+ profile: ssm.profile
424
+ });
425
+ }
426
+ if (config.provider === "s3") {
427
+ if (!workspaceName) throw new StateStoreNeedsWorkspaceName("s3");
428
+ const { S3StateStore } = await import("./S3StateStore-BFHuctWM.mjs");
429
+ const s3 = config;
430
+ return S3StateStore.create({
431
+ workspaceName,
432
+ bucket: s3.bucket,
433
+ prefix: s3.prefix,
434
+ region: s3.region,
435
+ profile: s3.profile
436
+ });
437
+ }
438
+ throw new UnknownStateProvider(config);
439
+ }
440
+ //#endregion
441
+ //#region src/target/dokploy/dokploy-api.ts
442
+ var dokploy_api_exports = /* @__PURE__ */ __exportAll({
443
+ DEFAULT_DEPLOYMENT_TIMEOUT_MS: () => DEFAULT_DEPLOYMENT_TIMEOUT_MS,
444
+ DEFAULT_DOKPLOY_TIMEOUT_MS: () => DEFAULT_DOKPLOY_TIMEOUT_MS,
445
+ DeploymentFailed: () => DeploymentFailed,
446
+ DeploymentTimedOut: () => DeploymentTimedOut,
447
+ DokployApi: () => DokployApi,
448
+ DokployApiError: () => DokployApiError,
449
+ DokployRequestTimedOut: () => DokployRequestTimedOut
450
+ });
451
+ /** Per request, so a stalled server fails the deploy, not the CI job. */
452
+ const DEFAULT_DOKPLOY_TIMEOUT_MS = 3e4;
453
+ /**
454
+ * Dokploy accepted the connection but did not answer in time.
455
+ *
456
+ * Not retried: a request that arrived and stalled may still be acted on, and
457
+ * sending it again could repeat a deploy or a create the server is already
458
+ * doing.
459
+ */
460
+ var DokployRequestTimedOut = class extends Error {
461
+ baseUrl;
462
+ endpoint;
463
+ timeoutMs;
464
+ constructor(baseUrl, endpoint, timeoutMs) {
465
+ super(`Dokploy at ${baseUrl} did not answer ${endpoint} within ${timeoutMs}ms. Check that the Dokploy server is healthy, or raise \`timeoutMs\` if this call is known to be slow.`);
466
+ this.baseUrl = baseUrl;
467
+ this.endpoint = endpoint;
468
+ this.timeoutMs = timeoutMs;
469
+ this.name = "DokployRequestTimedOut";
470
+ }
471
+ };
472
+ /**
473
+ * Waits `ms` between retries, or rejects with the signal's reason the moment
474
+ * it aborts — a caller who gave up should not sit through the backoff.
475
+ */
476
+ function pause(ms, signal) {
477
+ return new Promise((resolve, reject) => {
478
+ if (signal?.aborted) {
479
+ reject(signal.reason);
480
+ return;
481
+ }
482
+ const onAbort = () => {
483
+ clearTimeout(timer);
484
+ reject(signal?.reason);
485
+ };
486
+ const timer = setTimeout(() => {
487
+ signal?.removeEventListener("abort", onAbort);
488
+ resolve();
489
+ }, ms);
490
+ signal?.addEventListener("abort", onAbort, { once: true });
491
+ });
492
+ }
493
+ /** How long a deployment may take to finish before a deploy gives up on it. */
494
+ const DEFAULT_DEPLOYMENT_TIMEOUT_MS = 6e5;
495
+ /** A deployment Dokploy ran and reported as failed. */
496
+ var DeploymentFailed = class extends Error {
497
+ applicationId;
498
+ deployment;
499
+ constructor(applicationId, deployment) {
500
+ super(`Dokploy's deployment ${deployment.deploymentId} of application ${applicationId} ended in "${deployment.status}"${deployment.errorMessage ? `: ${deployment.errorMessage}` : ""}. Its log in Dokploy says why — usually an image the server could not pull, or a container that exited on start.`);
501
+ this.applicationId = applicationId;
502
+ this.deployment = deployment;
503
+ this.name = "DeploymentFailed";
504
+ }
505
+ };
506
+ /** A deployment that had not finished when the deploy stopped waiting. */
507
+ var DeploymentTimedOut = class extends Error {
508
+ applicationId;
509
+ timeoutMs;
510
+ lastStatus;
511
+ constructor(applicationId, timeoutMs, lastStatus) {
512
+ super(`Application ${applicationId} did not finish deploying within ${Math.round(timeoutMs / 1e3)}s (${lastStatus ? `last status "${lastStatus}"` : "no deployment started"}). Check its deployment log in Dokploy, or raise deploy.dokploy.verify.deploymentTimeoutMs for an image that is slow to pull.`);
513
+ this.applicationId = applicationId;
514
+ this.timeoutMs = timeoutMs;
515
+ this.lastStatus = lastStatus;
516
+ this.name = "DeploymentTimedOut";
517
+ }
518
+ };
519
+ var DokployApiError = class extends Error {
520
+ status;
521
+ statusText;
522
+ issues;
523
+ constructor(message, status, statusText, issues) {
524
+ super(message);
525
+ this.status = status;
526
+ this.statusText = statusText;
527
+ this.issues = issues;
528
+ this.name = "DokployApiError";
529
+ }
530
+ };
531
+ /**
532
+ * Dokploy API client
533
+ */
534
+ var DokployApi = class {
535
+ baseUrl;
536
+ token;
537
+ timeoutMs;
538
+ signal;
539
+ constructor(options) {
540
+ this.baseUrl = options.baseUrl.replace(/\/$/, "");
541
+ this.token = options.token;
542
+ this.timeoutMs = options.timeoutMs ?? 3e4;
543
+ this.signal = options.signal;
544
+ }
545
+ /**
546
+ * Make a GET request to the Dokploy API
547
+ */
548
+ async get(endpoint) {
549
+ return this.request("GET", endpoint);
550
+ }
551
+ /**
552
+ * Make a POST request to the Dokploy API
553
+ */
554
+ async post(endpoint, body) {
555
+ return this.request("POST", endpoint, body);
556
+ }
557
+ /**
558
+ * Make a request to the Dokploy API
559
+ */
560
+ async request(method, endpoint, body) {
561
+ const url = `${this.baseUrl}/api/${endpoint}`;
562
+ let response;
563
+ let timeout;
564
+ let lastError;
565
+ for (let attempt = 1; attempt <= 3; attempt++) {
566
+ this.signal?.throwIfAborted();
567
+ timeout = AbortSignal.timeout(this.timeoutMs);
568
+ try {
569
+ response = await fetch(url, {
570
+ method,
571
+ headers: {
572
+ "Content-Type": "application/json",
573
+ "x-api-key": this.token
574
+ },
575
+ body: body ? JSON.stringify(body) : void 0,
576
+ signal: this.signal ? AbortSignal.any([this.signal, timeout]) : timeout
577
+ });
578
+ break;
579
+ } catch (error) {
580
+ const stopped = this.stopped(timeout, endpoint);
581
+ if (stopped) throw stopped;
582
+ lastError = error;
583
+ if (attempt === 3) break;
584
+ await pause(attempt * 750, this.signal);
585
+ }
586
+ }
587
+ if (!response || !timeout) {
588
+ const detail = lastError instanceof Error ? lastError.message : String(lastError);
589
+ throw new DokployApiError(`Could not reach Dokploy at ${this.baseUrl} (${endpoint}): ${detail}`, 0, "Network error");
590
+ }
591
+ try {
592
+ return await this.read(response);
593
+ } catch (error) {
594
+ throw this.stopped(timeout, endpoint) ?? error;
595
+ }
596
+ }
597
+ /**
598
+ * What a request cut short should reject with: the caller's own reason
599
+ * when they aborted, `DokployRequestTimedOut` when the deadline passed,
600
+ * nothing when it was neither.
601
+ */
602
+ stopped(timeout, endpoint) {
603
+ if (this.signal?.aborted) return this.signal.reason;
604
+ if (timeout.aborted) return new DokployRequestTimedOut(this.baseUrl, endpoint, this.timeoutMs);
605
+ }
606
+ /** Turns a response into its JSON body, or a `DokployApiError`. */
607
+ async read(response) {
608
+ if (!response.ok) {
609
+ let errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;
610
+ let issues;
611
+ try {
612
+ const errorBody = await response.json();
613
+ if (errorBody.message) errorMessage = `Dokploy API error: ${errorBody.message}`;
614
+ if (errorBody.issues?.length) {
615
+ issues = errorBody.issues;
616
+ errorMessage += `\n Issues: ${errorBody.issues.map((i) => i.message).join(", ")}`;
617
+ }
618
+ } catch {}
619
+ throw new DokployApiError(errorMessage, response.status, response.statusText, issues);
620
+ }
621
+ const text = await response.text();
622
+ if (!text || text.trim() === "") return;
623
+ return JSON.parse(text);
624
+ }
625
+ /**
626
+ * Every external port published on this server, across every project.
627
+ *
628
+ * Dokploy knows what it has bound; guessing does not. A port that looks free
629
+ * from here can be held by a service in a project this deploy has never
630
+ * heard of — which surfaces as `Port 5432 is already in use by container
631
+ * "…"`, naming something the caller cannot see.
632
+ *
633
+ * `project.all` is a summary, so each project is fetched: the per-service
634
+ * `externalPort` only appears on `project.one`.
635
+ */
636
+ async publishedPorts() {
637
+ const ports = /* @__PURE__ */ new Set();
638
+ const projects = await this.listProjects();
639
+ for (const project of projects) {
640
+ const details = await this.getProject(project.projectId);
641
+ for (const environment of details.environments ?? []) {
642
+ const resources = [
643
+ ...environment.postgres ?? [],
644
+ ...environment.redis ?? [],
645
+ ...environment.applications ?? []
646
+ ];
647
+ for (const resource of resources) if (typeof resource.externalPort === "number") ports.add(resource.externalPort);
648
+ }
649
+ }
650
+ return ports;
651
+ }
652
+ /**
653
+ * Validate the API token by making a test request
654
+ */
655
+ async validateToken() {
656
+ try {
657
+ await this.get("project.all");
658
+ return true;
659
+ } catch {
660
+ return false;
661
+ }
662
+ }
663
+ /**
664
+ * List all projects
665
+ */
666
+ async listProjects() {
667
+ return this.get("project.all");
668
+ }
669
+ /**
670
+ * Get a single project by ID
671
+ */
672
+ async getProject(projectId) {
673
+ return this.get(`project.one?projectId=${projectId}`);
674
+ }
675
+ /**
676
+ * Create a new project
677
+ */
678
+ async createProject(name, description) {
679
+ return this.post("project.create", {
680
+ name,
681
+ description: description ?? `Created by gkm CLI`
682
+ });
683
+ }
684
+ /**
685
+ * Change a project's name or description.
686
+ *
687
+ * The name is sent with the description because `project.update` validates
688
+ * the whole project on some Dokploy versions, and a missing name fails it.
689
+ */
690
+ async updateProject(projectId, updates) {
691
+ await this.post("project.update", {
692
+ projectId,
693
+ ...updates
694
+ });
695
+ }
696
+ /**
697
+ * Delete a project and all its resources
698
+ */
699
+ async deleteProject(projectId) {
700
+ await this.post("project.remove", { projectId });
701
+ }
702
+ /**
703
+ * Create an environment in a project
704
+ */
705
+ async createEnvironment(projectId, name, description) {
706
+ return this.post("environment.create", {
707
+ projectId,
708
+ name,
709
+ description: description ?? `${name} environment`
710
+ });
711
+ }
712
+ /**
713
+ * List all applications in a project
714
+ */
715
+ async listApplications(projectId) {
716
+ return this.listInProject(projectId, (env) => env.applications);
717
+ }
718
+ /**
719
+ * Everything of one kind in a project, across its environments.
720
+ *
721
+ * From `project.one`, because the per-kind `*.all?projectId=` endpoints
722
+ * return **404** on current Dokploy: resources live under environments now.
723
+ * Those calls used to be wrapped in `catch { return [] }`, which turned "I
724
+ * could not ask" into "there are none" — and for a find-or-create that is
725
+ * the worst available answer. Every deploy created a second application, a
726
+ * second Postgres and a second Redis, silently, for as long as the project
727
+ * existed.
728
+ *
729
+ * A failure here is not swallowed: creating a duplicate is worse than
730
+ * stopping.
731
+ */
732
+ async listInProject(projectId, pick) {
733
+ return ((await this.getProject(projectId)).environments ?? []).flatMap((environment) => (pick(environment) ?? []).map((resource) => ({
734
+ ...resource,
735
+ environmentId: environment.environmentId
736
+ })));
737
+ }
738
+ /**
739
+ * Find an application by name in a project
740
+ */
741
+ async findApplicationByName(projectId, name, environmentId) {
742
+ const applications = await this.listApplications(projectId);
743
+ const normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
744
+ return applications.find((app) => (!environmentId || app.environmentId === environmentId) && (app.name === name || app.appName === normalizedName));
745
+ }
746
+ /**
747
+ * Create a new application
748
+ */
749
+ async createApplication(name, projectId, environmentId) {
750
+ return this.post("application.create", {
751
+ name,
752
+ projectId,
753
+ environmentId,
754
+ appName: name.toLowerCase().replace(/[^a-z0-9-]/g, "-")
755
+ });
756
+ }
757
+ /**
758
+ * Find or create an application by name
759
+ */
760
+ async findOrCreateApplication(name, projectId, environmentId) {
761
+ const existing = await this.findApplicationByName(projectId, name, environmentId);
762
+ if (existing) return {
763
+ application: existing,
764
+ created: false
765
+ };
766
+ return {
767
+ application: await this.createApplication(name, projectId, environmentId),
768
+ created: true
769
+ };
770
+ }
771
+ /**
772
+ * Get an application by ID
773
+ */
774
+ async getApplication(applicationId) {
775
+ try {
776
+ return await this.get(`application.one?applicationId=${applicationId}`);
777
+ } catch {
778
+ return null;
779
+ }
780
+ }
781
+ /**
782
+ * Update an application
783
+ */
784
+ async updateApplication(applicationId, updates) {
785
+ await this.post("application.update", {
786
+ applicationId,
787
+ ...updates
788
+ });
789
+ }
790
+ /**
791
+ * Save environment variables for an application
792
+ */
793
+ async saveApplicationEnv(applicationId, env, options) {
794
+ await this.post("application.saveEnvironment", {
795
+ applicationId,
796
+ env,
797
+ buildArgs: options?.buildArgs ?? null,
798
+ buildSecrets: options?.buildSecrets ?? null,
799
+ createEnvFile: false
800
+ });
801
+ }
802
+ /**
803
+ * Configure application to use Docker provider (pull from registry)
804
+ *
805
+ * For private registries, either:
806
+ * - Use `registryId` if the registry is configured in Dokploy
807
+ * - Or provide `username`, `password`, and `registryUrl` directly
808
+ */
809
+ async saveDockerProvider(applicationId, dockerImage, options) {
810
+ await this.post("application.saveDockerProvider", {
811
+ applicationId,
812
+ dockerImage,
813
+ ...options,
814
+ username: options?.username ?? null,
815
+ password: options?.password ?? null,
816
+ registryUrl: options?.registryUrl ?? null
817
+ });
818
+ }
819
+ /**
820
+ * Deploy an application
821
+ */
822
+ async deployApplication(applicationId) {
823
+ await this.post("application.deploy", { applicationId });
824
+ }
825
+ /**
826
+ * An application's deployments, newest first.
827
+ *
828
+ * Sorted here rather than trusted to arrive sorted: which deployment is
829
+ * the latest is the whole question `waitForDeployment` asks.
830
+ */
831
+ async listDeployments(applicationId) {
832
+ return [...await this.get(`deployment.all?applicationId=${encodeURIComponent(applicationId)}`)].sort((a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt));
833
+ }
834
+ /**
835
+ * When the application's latest deployment was created, by Dokploy's own
836
+ * clock — what `waitForDeployment`'s `since` is measured against. Epoch
837
+ * when it has none.
838
+ */
839
+ async latestDeploymentAt(applicationId) {
840
+ const [latest] = await this.listDeployments(applicationId);
841
+ return new Date(latest ? Date.parse(latest.createdAt) : 0);
842
+ }
843
+ /**
844
+ * Wait for the deployment `application.deploy` queued to finish.
845
+ *
846
+ * `application.deploy` only queues one: it answers before the image is
847
+ * pulled, let alone running, so a deploy that stopped there reported
848
+ * success for containers that had not started. This polls the latest
849
+ * deployment created after `since` — Dokploy's clock, from
850
+ * `latestDeploymentAt` before deploying, so a laptop's skew cannot pick an
851
+ * older one — and the application's own status, backing off from
852
+ * `intervalMs` to `maxIntervalMs`.
853
+ *
854
+ * @throws {DeploymentFailed} when the deployment ends in `error` or is
855
+ * cancelled, or the application reports `error` once it has finished.
856
+ * @throws {DeploymentTimedOut} when it has not finished within `timeoutMs`.
857
+ */
858
+ async waitForDeployment(applicationId, options) {
859
+ const { since, timeoutMs = DEFAULT_DEPLOYMENT_TIMEOUT_MS, signal, intervalMs = 2e3, maxIntervalMs = 15e3 } = options;
860
+ const after = since.getTime();
861
+ const deadline = Date.now() + timeoutMs;
862
+ let interval = intervalMs;
863
+ let last;
864
+ while (true) {
865
+ signal?.throwIfAborted();
866
+ const deployment = (await this.listDeployments(applicationId)).find((d) => Date.parse(d.createdAt) > after);
867
+ last = deployment ?? last;
868
+ if (deployment) {
869
+ if (deployment.status === "error" || deployment.status === "cancelled") throw new DeploymentFailed(applicationId, deployment);
870
+ if (deployment.status === "done") {
871
+ const { applicationStatus } = await this.get(`application.one?applicationId=${encodeURIComponent(applicationId)}`);
872
+ if (applicationStatus === "error") throw new DeploymentFailed(applicationId, deployment);
873
+ if (applicationStatus !== "running") return deployment;
874
+ }
875
+ }
876
+ const left = deadline - Date.now();
877
+ if (left <= 0) throw new DeploymentTimedOut(applicationId, timeoutMs, last?.status);
878
+ await pause(Math.min(interval, left), signal);
879
+ interval = Math.min(interval * 2, maxIntervalMs);
880
+ }
881
+ }
882
+ /**
883
+ * Delete an application
884
+ */
885
+ async deleteApplication(applicationId) {
886
+ await this.post("application.remove", { applicationId });
887
+ }
888
+ /**
889
+ * List all registries
890
+ */
891
+ async listRegistries() {
892
+ return this.get("registry.all");
893
+ }
894
+ /**
895
+ * Create a new registry
896
+ */
897
+ async createRegistry(registryName, registryUrl, username, password, options) {
898
+ return this.post("registry.create", {
899
+ registryName,
900
+ registryUrl,
901
+ username,
902
+ password,
903
+ imagePrefix: options?.imagePrefix
904
+ });
905
+ }
906
+ /**
907
+ * Get a registry by ID
908
+ */
909
+ async getRegistry(registryId) {
910
+ return this.get(`registry.one?registryId=${registryId}`);
911
+ }
912
+ /**
913
+ * Update a registry
914
+ */
915
+ async updateRegistry(registryId, updates) {
916
+ await this.post("registry.update", {
917
+ registryId,
918
+ ...updates
919
+ });
920
+ }
921
+ /**
922
+ * Delete a registry
923
+ */
924
+ async deleteRegistry(registryId) {
925
+ await this.post("registry.remove", { registryId });
926
+ }
927
+ /**
928
+ * List all Postgres databases in a project
929
+ */
930
+ async listPostgres(projectId) {
931
+ return this.listInProject(projectId, (env) => env.postgres);
932
+ }
933
+ /**
934
+ * Find a Postgres database by name in a project
935
+ */
936
+ async findPostgresByName(projectId, name) {
937
+ const databases = await this.listPostgres(projectId);
938
+ const normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
939
+ const match = databases.find((db) => db.name === name || db.appName === normalizedName);
940
+ if (!match) return void 0;
941
+ return this.getPostgres(match.postgresId);
942
+ }
943
+ /**
944
+ * Create a new Postgres database
945
+ */
946
+ async createPostgres(name, projectId, environmentId, options) {
947
+ return this.post("postgres.create", {
948
+ name,
949
+ projectId,
950
+ environmentId,
951
+ appName: options?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, "-"),
952
+ databaseName: options?.databaseName,
953
+ databaseUser: options?.databaseUser ?? "postgres",
954
+ databasePassword: options?.databasePassword,
955
+ dockerImage: options?.dockerImage ?? "postgres:18",
956
+ description: options?.description ?? `Postgres database for ${name}`
957
+ });
958
+ }
959
+ /**
960
+ * Find or create a Postgres database by name
961
+ */
962
+ async findOrCreatePostgres(name, projectId, environmentId, options) {
963
+ const existing = await this.findPostgresByName(projectId, name);
964
+ if (existing) return {
965
+ postgres: existing,
966
+ created: false
967
+ };
968
+ return {
969
+ postgres: await this.createPostgres(name, projectId, environmentId, options),
970
+ created: true
971
+ };
972
+ }
973
+ /**
974
+ * Get a Postgres database by ID
975
+ */
976
+ async getPostgres(postgresId) {
977
+ return this.get(`postgres.one?postgresId=${postgresId}`);
978
+ }
979
+ /**
980
+ * Deploy a Postgres database
981
+ */
982
+ async deployPostgres(postgresId) {
983
+ await this.post("postgres.deploy", { postgresId });
984
+ }
985
+ /**
986
+ * Save environment variables for Postgres
987
+ */
988
+ async savePostgresEnv(postgresId, env) {
989
+ await this.post("postgres.saveEnvironment", {
990
+ postgresId,
991
+ env
992
+ });
993
+ }
994
+ /**
995
+ * Set external port for Postgres (for external access)
996
+ */
997
+ async savePostgresExternalPort(postgresId, externalPort) {
998
+ await this.post("postgres.saveExternalPort", {
999
+ postgresId,
1000
+ externalPort
1001
+ });
1002
+ }
1003
+ /**
1004
+ * Update Postgres configuration
1005
+ */
1006
+ async updatePostgres(postgresId, updates) {
1007
+ await this.post("postgres.update", {
1008
+ postgresId,
1009
+ ...updates
1010
+ });
1011
+ }
1012
+ /**
1013
+ * Delete a Postgres database
1014
+ */
1015
+ async deletePostgres(postgresId) {
1016
+ await this.post("postgres.remove", { postgresId });
1017
+ }
1018
+ /**
1019
+ * The compose stacks in a project.
1020
+ *
1021
+ * Dokploy has first-class primitives for Postgres and Redis and none for
1022
+ * MinIO, so an object store is a compose stack — the one kind whose contents
1023
+ * this target writes rather than configures.
1024
+ */
1025
+ async listCompose(projectId) {
1026
+ return this.listInProject(projectId, (env) => env.compose);
1027
+ }
1028
+ /** A compose stack by name, hydrated — `project.one` returns summaries. */
1029
+ async findComposeByName(projectId, name) {
1030
+ const summary = (await this.listCompose(projectId)).find((compose) => compose.name === name);
1031
+ return summary ? this.getCompose(summary.composeId) : void 0;
1032
+ }
1033
+ async getCompose(composeId) {
1034
+ return this.get(`compose.one?composeId=${composeId}`);
1035
+ }
1036
+ /**
1037
+ * A compose stack carrying the file this target wrote.
1038
+ *
1039
+ * Created empty and then updated, because `compose.create` takes no file:
1040
+ * it defaults to `sourceType: 'github'` and expects a repository. `raw` is
1041
+ * what makes the file ours.
1042
+ */
1043
+ async findOrCreateCompose(name, projectId, environmentId, composeFile) {
1044
+ const existing = await this.findComposeByName(projectId, name);
1045
+ const compose = existing ?? await this.post("compose.create", {
1046
+ name,
1047
+ environmentId,
1048
+ composeType: "docker-compose"
1049
+ });
1050
+ await this.post("compose.update", {
1051
+ composeId: compose.composeId,
1052
+ sourceType: "raw",
1053
+ composeFile
1054
+ });
1055
+ return {
1056
+ compose,
1057
+ created: !existing
1058
+ };
1059
+ }
1060
+ async deployCompose(composeId) {
1061
+ await this.post("compose.deploy", { composeId });
1062
+ }
1063
+ async deleteCompose(composeId) {
1064
+ await this.post("compose.delete", {
1065
+ composeId,
1066
+ deleteVolumes: false
1067
+ });
1068
+ }
1069
+ /**
1070
+ * List all Redis instances in a project
1071
+ */
1072
+ async listRedis(projectId) {
1073
+ return this.listInProject(projectId, (env) => env.redis);
1074
+ }
1075
+ /**
1076
+ * Find a Redis instance by name in a project
1077
+ */
1078
+ async findRedisByName(projectId, name) {
1079
+ const instances = await this.listRedis(projectId);
1080
+ const normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
1081
+ const match = instances.find((redis) => redis.name === name || redis.appName === normalizedName);
1082
+ return match ? this.getRedis(match.redisId) : void 0;
1083
+ }
1084
+ /**
1085
+ * Create a new Redis instance
1086
+ */
1087
+ async createRedis(name, projectId, environmentId, options) {
1088
+ return this.post("redis.create", {
1089
+ name,
1090
+ projectId,
1091
+ environmentId,
1092
+ appName: options?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, "-"),
1093
+ databasePassword: options?.databasePassword,
1094
+ dockerImage: options?.dockerImage ?? "redis:8",
1095
+ description: options?.description ?? `Redis instance for ${name}`
1096
+ });
1097
+ }
1098
+ /**
1099
+ * Find or create a Redis instance by name
1100
+ */
1101
+ async findOrCreateRedis(name, projectId, environmentId, options) {
1102
+ const existing = await this.findRedisByName(projectId, name);
1103
+ if (existing) return {
1104
+ redis: existing,
1105
+ created: false
1106
+ };
1107
+ return {
1108
+ redis: await this.createRedis(name, projectId, environmentId, options),
1109
+ created: true
1110
+ };
1111
+ }
1112
+ /**
1113
+ * Get a Redis instance by ID
1114
+ */
1115
+ async getRedis(redisId) {
1116
+ return this.get(`redis.one?redisId=${redisId}`);
1117
+ }
1118
+ /**
1119
+ * Deploy a Redis instance
1120
+ */
1121
+ async deployRedis(redisId) {
1122
+ await this.post("redis.deploy", { redisId });
1123
+ }
1124
+ /**
1125
+ * Save environment variables for Redis
1126
+ */
1127
+ async saveRedisEnv(redisId, env) {
1128
+ await this.post("redis.saveEnvironment", {
1129
+ redisId,
1130
+ env
1131
+ });
1132
+ }
1133
+ /**
1134
+ * Set external port for Redis (for external access)
1135
+ */
1136
+ async saveRedisExternalPort(redisId, externalPort) {
1137
+ await this.post("redis.saveExternalPort", {
1138
+ redisId,
1139
+ externalPort
1140
+ });
1141
+ }
1142
+ /**
1143
+ * Update Redis configuration
1144
+ */
1145
+ async updateRedis(redisId, updates) {
1146
+ await this.post("redis.update", {
1147
+ redisId,
1148
+ ...updates
1149
+ });
1150
+ }
1151
+ /**
1152
+ * Delete a Redis instance
1153
+ */
1154
+ async deleteRedis(redisId) {
1155
+ await this.post("redis.remove", { redisId });
1156
+ }
1157
+ /**
1158
+ * Create a new domain for an application
1159
+ */
1160
+ async createDomain(options) {
1161
+ return this.post("domain.create", options);
1162
+ }
1163
+ /**
1164
+ * Update an existing domain
1165
+ */
1166
+ async updateDomain(domainId, updates) {
1167
+ await this.post("domain.update", {
1168
+ domainId,
1169
+ ...updates
1170
+ });
1171
+ }
1172
+ /**
1173
+ * Delete a domain
1174
+ */
1175
+ async deleteDomain(domainId) {
1176
+ await this.post("domain.delete", { domainId });
1177
+ }
1178
+ /**
1179
+ * Get a domain by ID
1180
+ */
1181
+ async getDomain(domainId) {
1182
+ return this.get(`domain.one?domainId=${domainId}`);
1183
+ }
1184
+ /**
1185
+ * Get all domains for an application
1186
+ */
1187
+ async getDomainsByApplicationId(applicationId) {
1188
+ return this.get(`domain.byApplicationId?applicationId=${applicationId}`);
1189
+ }
1190
+ /**
1191
+ * Validate a domain and trigger SSL certificate generation
1192
+ *
1193
+ * This should be called after DNS records are created and propagated.
1194
+ * It triggers Let's Encrypt certificate generation for HTTPS domains.
1195
+ *
1196
+ * @param domain - The domain hostname to validate (e.g., 'api.example.com')
1197
+ */
1198
+ async validateDomain(domain) {
1199
+ return this.post("domain.validateDomain", { domain });
1200
+ }
1201
+ /**
1202
+ * Auto-generate a domain name for an application
1203
+ */
1204
+ async generateDomain(appName, serverId) {
1205
+ return this.post("domain.generateDomain", {
1206
+ appName,
1207
+ serverId
1208
+ });
1209
+ }
1210
+ /**
1211
+ * List all backup destinations
1212
+ */
1213
+ async listDestinations() {
1214
+ return this.get("destination.all");
1215
+ }
1216
+ /**
1217
+ * Get a destination by ID
1218
+ */
1219
+ async getDestination(destinationId) {
1220
+ return this.get(`destination.one?destinationId=${destinationId}`);
1221
+ }
1222
+ /**
1223
+ * Create a new S3 backup destination
1224
+ */
1225
+ async createDestination(options) {
1226
+ return this.post("destination.create", { ...options });
1227
+ }
1228
+ /**
1229
+ * Find a destination by name
1230
+ */
1231
+ async findDestinationByName(name) {
1232
+ return (await this.listDestinations()).find((d) => d.name === name);
1233
+ }
1234
+ /**
1235
+ * Find or create a destination by name
1236
+ */
1237
+ async findOrCreateDestination(name, options) {
1238
+ const existing = await this.findDestinationByName(name);
1239
+ if (existing) return {
1240
+ destination: existing,
1241
+ created: false
1242
+ };
1243
+ return {
1244
+ destination: await this.createDestination({
1245
+ name,
1246
+ ...options
1247
+ }),
1248
+ created: true
1249
+ };
1250
+ }
1251
+ /**
1252
+ * Update a destination
1253
+ */
1254
+ async updateDestination(destinationId, updates) {
1255
+ await this.post("destination.update", {
1256
+ destinationId,
1257
+ ...updates
1258
+ });
1259
+ }
1260
+ /**
1261
+ * Delete a destination
1262
+ */
1263
+ async deleteDestination(destinationId) {
1264
+ await this.post("destination.remove", { destinationId });
1265
+ }
1266
+ /**
1267
+ * Test connection to a destination
1268
+ */
1269
+ async testDestinationConnection(destinationId) {
1270
+ return this.post("destination.testConnection", { destinationId });
1271
+ }
1272
+ /**
1273
+ * Create a backup schedule for postgres
1274
+ */
1275
+ async createPostgresBackup(options) {
1276
+ return this.post("backup.create", {
1277
+ ...options,
1278
+ databaseType: "postgres"
1279
+ });
1280
+ }
1281
+ /**
1282
+ * List backups for a postgres database
1283
+ */
1284
+ async listPostgresBackups(postgresId) {
1285
+ return this.get(`backup.all?postgresId=${postgresId}&databaseType=postgres`);
1286
+ }
1287
+ /**
1288
+ * Get a backup by ID
1289
+ */
1290
+ async getBackup(backupId) {
1291
+ return this.get(`backup.one?backupId=${backupId}`);
1292
+ }
1293
+ /**
1294
+ * Update a backup schedule
1295
+ */
1296
+ async updateBackup(backupId, updates) {
1297
+ await this.post("backup.update", {
1298
+ backupId,
1299
+ ...updates
1300
+ });
1301
+ }
1302
+ /**
1303
+ * Delete a backup schedule
1304
+ */
1305
+ async deleteBackup(backupId) {
1306
+ await this.post("backup.remove", { backupId });
1307
+ }
1308
+ /**
1309
+ * Trigger a manual backup run
1310
+ */
1311
+ async runBackupManually(backupId) {
1312
+ await this.post("backup.manualBackup", { backupId });
1313
+ }
1314
+ };
1315
+ //#endregion
1316
+ //#region src/deploy/sniffer.ts
1317
+ const __filename = fileURLToPath(import.meta.url);
1318
+ const __dirname = dirname(__filename);
1319
+ /**
1320
+ * Resolve the path to a sniffer helper file.
1321
+ * Handles both dev (.ts with tsx) and production (.mjs from dist).
1322
+ *
1323
+ * In production: sniffer.ts is bundled into dist/index.mjs, but sniffer helper
1324
+ * files are output to dist/deploy/ as standalone modules for subprocess loading.
1325
+ *
1326
+ * In development: All files are in src/deploy/ and loaded via tsx.
1327
+ */
1328
+ function resolveSnifferFile(baseName) {
1329
+ const deployMjsPath = resolve(__dirname, "deploy", `${baseName}.mjs`);
1330
+ if (existsSync(deployMjsPath)) return deployMjsPath;
1331
+ const mjsPath = resolve(__dirname, `${baseName}.mjs`);
1332
+ if (existsSync(mjsPath)) return mjsPath;
1333
+ const tsPath = resolve(__dirname, `${baseName}.ts`);
1334
+ if (existsSync(tsPath)) return tsPath;
1335
+ return tsPath;
1336
+ }
1337
+ /**
1338
+ * Run a sniffer worker in the sandbox, with the sandbox's environment and
1339
+ * nothing of the deploy's: the deploy's Dokploy token, registry login and AWS
1340
+ * keys stay where they are. A worker that outlives the timeout is killed, and
1341
+ * comes back as the error the caller reports.
1342
+ */
1343
+ async function runSniffer(run, workspacePath, cwd, nodeArgs) {
1344
+ const sandbox = run.sandbox ?? activeSandbox() ?? new LocalSandbox({ root: resolve(workspacePath) });
1345
+ try {
1346
+ const result = await sandbox.exec("node", nodeArgs, {
1347
+ cwd,
1348
+ env: { ...sandbox.env },
1349
+ timeoutMs: run.timeoutMs ?? 3e4,
1350
+ output: "capture"
1351
+ });
1352
+ return {
1353
+ stdout: result.stdout,
1354
+ stderr: result.stderr,
1355
+ code: result.exitCode
1356
+ };
1357
+ } catch (error) {
1358
+ return { error: error instanceof Error ? error : new Error(String(error)) };
1359
+ }
1360
+ }
1361
+ /** The worker's JSON answer: the last object on stdout naming `envVars`. */
1362
+ function snifferAnswer(stdout) {
1363
+ try {
1364
+ const jsonMatch = stdout.match(/\{[^{}]*"envVars"[^{}]*\}[^{]*$/);
1365
+ return jsonMatch ? JSON.parse(jsonMatch[0]) : void 0;
1366
+ } catch {
1367
+ return;
1368
+ }
1369
+ }
1370
+ /**
1371
+ * Get required environment variables for an app.
1372
+ *
1373
+ * Detection strategy (in order):
1374
+ * 1. Frontend apps: Returns empty (no server secrets)
1375
+ * 2. Apps with `requiredEnv`: Uses explicit list from config
1376
+ * 3. Entry apps: Imports entry file in subprocess to capture config.parse() calls
1377
+ * 4. Route-based apps: Loads route files and calls getEnvironment() on each construct
1378
+ * 5. Apps with `envParser` (no routes): Runs SnifferEnvironmentParser to detect usage
1379
+ * 6. Apps with neither: Returns empty
1380
+ *
1381
+ * This function handles "fire and forget" async operations gracefully,
1382
+ * capturing errors and unhandled rejections without failing the build.
1383
+ *
1384
+ * @param app - The normalized app configuration
1385
+ * @param appName - The name of the app
1386
+ * @param workspacePath - Absolute path to the workspace root
1387
+ * @param options - Optional configuration for sniffing behavior
1388
+ * @returns The sniffed environment with required variables
1389
+ */
1390
+ async function sniffAppEnvironment(app, appName, workspacePath, options = {}) {
1391
+ const { logWarnings = true, markOptional = false } = options;
1392
+ const run = {
1393
+ ...options.sandbox ? { sandbox: options.sandbox } : {},
1394
+ ...options.timeoutMs ? { timeoutMs: options.timeoutMs } : {}
1395
+ };
1396
+ if (app.type === "web" || app.type === "mobile") {
1397
+ const publicPrefix = getPublicEnvPrefix(app.framework);
1398
+ const depVars = publicPrefix ? (app.dependencies ?? []).map((dep) => `${publicPrefix}${dep.toUpperCase()}_URL`) : [];
1399
+ if (app.config) {
1400
+ const sniffedVars = [];
1401
+ const sniffedOptional = [];
1402
+ const configPaths = [];
1403
+ if (app.config.client) configPaths.push(app.config.client);
1404
+ if (app.config.server) configPaths.push(app.config.server);
1405
+ for (const configPath of configPaths) {
1406
+ const result = await sniffEntryFile(configPath, app.path, workspacePath, run);
1407
+ if (logWarnings && result.error) output.warn(`[sniffer] ${appName}: Config file "${configPath}" threw error during sniffing (env vars still captured): ${result.error.message}`);
1408
+ sniffedVars.push(...result.envVars);
1409
+ sniffedOptional.push(...result.optionalEnvVars);
1410
+ }
1411
+ const optionalEnvVars = [...new Set(sniffedOptional)];
1412
+ return {
1413
+ appName,
1414
+ requiredEnvVars: applyMarkOptional([.../* @__PURE__ */ new Set([...depVars, ...sniffedVars])], optionalEnvVars, markOptional),
1415
+ optionalEnvVars
1416
+ };
1417
+ }
1418
+ return {
1419
+ appName,
1420
+ requiredEnvVars: depVars,
1421
+ optionalEnvVars: []
1422
+ };
1423
+ }
1424
+ if (app.entry) {
1425
+ const result = await sniffEntryFile(app.entry, app.path, workspacePath, run);
1426
+ if (logWarnings && result.error) output.warn(`[sniffer] ${appName}: Entry file threw error during sniffing (env vars still captured): ${result.error.message}`);
1427
+ return {
1428
+ appName,
1429
+ requiredEnvVars: applyMarkOptional(result.envVars, result.optionalEnvVars, markOptional),
1430
+ optionalEnvVars: result.optionalEnvVars
1431
+ };
1432
+ }
1433
+ if (app.routes) {
1434
+ const result = await sniffRouteFiles(normalizeRoutes(app.routes), app.path, workspacePath, run);
1435
+ if (logWarnings && result.error) output.warn(`[sniffer] ${appName}: Route sniffing threw error (env vars still captured): ${result.error.message}`);
1436
+ return {
1437
+ appName,
1438
+ requiredEnvVars: applyMarkOptional(result.envVars, result.optionalEnvVars, markOptional),
1439
+ optionalEnvVars: result.optionalEnvVars
1440
+ };
1441
+ }
1442
+ if (app.envParser) {
1443
+ const result = await sniffEnvParser(app.envParser, app.path, workspacePath, run);
1444
+ if (logWarnings) {
1445
+ if (result.error) output.warn(`[sniffer] ${appName}: envParser threw error during sniffing (env vars still captured): ${result.error.message}`);
1446
+ if (result.unhandledRejections.length > 0) output.warn(`[sniffer] ${appName}: Fire-and-forget rejections during sniffing (suppressed): ${result.unhandledRejections.map((e) => e.message).join(", ")}`);
1447
+ }
1448
+ return {
1449
+ appName,
1450
+ requiredEnvVars: applyMarkOptional(result.envVars, result.optionalEnvVars, markOptional),
1451
+ optionalEnvVars: result.optionalEnvVars
1452
+ };
1453
+ }
1454
+ return {
1455
+ appName,
1456
+ requiredEnvVars: [],
1457
+ optionalEnvVars: []
1458
+ };
1459
+ }
1460
+ /**
1461
+ * Apply the `?` suffix to optional variables in an env var list.
1462
+ * Only modifies the list when `markOptional` is true and there are optional vars.
1463
+ */
1464
+ function applyMarkOptional(envVars, optionalEnvVars, markOptional) {
1465
+ if (!markOptional || optionalEnvVars.length === 0) return envVars;
1466
+ const optionalSet = new Set(optionalEnvVars);
1467
+ return envVars.map((v) => optionalSet.has(v) ? `${v}?` : v);
1468
+ }
1469
+ /**
1470
+ * Sniff an entry file by importing it in a subprocess.
1471
+ *
1472
+ * Entry apps call `config.parse()` at module load time. To capture which
1473
+ * env vars are accessed, we:
1474
+ * 1. Spawn a subprocess with a module loader hook
1475
+ * 2. The loader intercepts `@geekmidas/envkit` and replaces EnvironmentParser
1476
+ * with SnifferEnvironmentParser
1477
+ * 3. Import the entry file (triggers config.parse())
1478
+ * 4. Capture and return the accessed env var names
1479
+ *
1480
+ * This approach provides process isolation - each app is sniffed in its own
1481
+ * subprocess, preventing module cache pollution.
1482
+ *
1483
+ * @param entryPath - Relative path to the entry file (e.g., './src/index.ts')
1484
+ * @param appPath - The app's path relative to workspace (e.g., 'apps/auth')
1485
+ * @param workspacePath - Absolute path to workspace root
1486
+ * @returns EntrySniffResult with env vars and optional error
1487
+ */
1488
+ async function sniffEntryFile(entryPath, appPath, workspacePath, run = {}) {
1489
+ const fullEntryPath = resolve(workspacePath, appPath, entryPath);
1490
+ const loaderPath = resolveSnifferFile("sniffer-loader");
1491
+ const workerPath = resolveSnifferFile("sniffer-worker");
1492
+ const ran = await runSniffer(run, workspacePath, resolve(workspacePath, appPath), nodeWithTsx(workerPath, [fullEntryPath], [loaderPath]));
1493
+ if ("error" in ran) return {
1494
+ envVars: [],
1495
+ optionalEnvVars: [],
1496
+ error: ran.error
1497
+ };
1498
+ const answer = snifferAnswer(ran.stdout);
1499
+ if (answer) return {
1500
+ envVars: answer.envVars || [],
1501
+ optionalEnvVars: answer.optionalEnvVars || [],
1502
+ error: answer.error ? new Error(answer.error) : void 0
1503
+ };
1504
+ return {
1505
+ envVars: [],
1506
+ optionalEnvVars: [],
1507
+ error: /* @__PURE__ */ new Error(`Failed to sniff entry file (exit code ${ran.code}): ${ran.stderr || ran.stdout || "No output"}`)
1508
+ };
1509
+ }
1510
+ /**
1511
+ * Sniff route files by loading constructs and calling getEnvironment().
1512
+ *
1513
+ * Route-based apps have endpoints, functions, crons, and subscribers that
1514
+ * use services. Each service's register() method accesses environment variables.
1515
+ *
1516
+ * This runs in a subprocess with tsx loader to properly handle TypeScript
1517
+ * compilation and path alias resolution (e.g., `src/...` imports).
1518
+ *
1519
+ * @param routes - Glob pattern(s) for route files
1520
+ * @param appPath - The app's path relative to workspace (e.g., 'apps/api')
1521
+ * @param workspacePath - Absolute path to workspace root
1522
+ * @returns EntrySniffResult with env vars and optional error
1523
+ */
1524
+ async function sniffRouteFiles(routes, appPath, workspacePath, run = {}) {
1525
+ const fullAppPath = resolve(workspacePath, appPath);
1526
+ const workerPath = resolveSnifferFile("sniffer-routes-worker");
1527
+ const pattern = (Array.isArray(routes) ? routes : [routes])[0];
1528
+ if (!pattern) return {
1529
+ envVars: [],
1530
+ optionalEnvVars: [],
1531
+ error: /* @__PURE__ */ new Error("No route patterns provided")
1532
+ };
1533
+ const ran = await runSniffer(run, workspacePath, fullAppPath, nodeWithTsx(workerPath, [fullAppPath, pattern]));
1534
+ if ("error" in ran) return {
1535
+ envVars: [],
1536
+ optionalEnvVars: [],
1537
+ error: ran.error
1538
+ };
1539
+ if (ran.stderr) ran.stderr.split("\n").filter((line) => line.trim()).forEach((line) => output.warn(line));
1540
+ const answer = snifferAnswer(ran.stdout);
1541
+ if (answer) return {
1542
+ envVars: answer.envVars || [],
1543
+ optionalEnvVars: answer.optionalEnvVars || [],
1544
+ error: answer.error ? new Error(answer.error) : void 0
1545
+ };
1546
+ return {
1547
+ envVars: [],
1548
+ optionalEnvVars: [],
1549
+ error: /* @__PURE__ */ new Error(`Failed to sniff route files (exit code ${ran.code}): ${ran.stderr || ran.stdout || "No output"}`)
1550
+ };
1551
+ }
1552
+ /**
1553
+ * Run the SnifferEnvironmentParser on an envParser module to detect
1554
+ * which environment variables it accesses.
1555
+ *
1556
+ * In a subprocess, like the entry and route sniffers: the module is the
1557
+ * project's code, and it was imported into the deploy itself before — with
1558
+ * every credential the deploy held in reach. Fire-and-forget rejections are
1559
+ * collected there with the shared `sniffWithFireAndForget`.
1560
+ *
1561
+ * @param envParserPath - The envParser config (e.g., './src/config/env#envParser')
1562
+ * @param appPath - The app's path relative to workspace
1563
+ * @param workspacePath - Absolute path to workspace root
1564
+ * @returns SniffResult with env vars and any errors encountered
1565
+ */
1566
+ async function sniffEnvParser(envParserPath, appPath, workspacePath, run = {}) {
1567
+ const [modulePath, exportName = "default"] = envParserPath.split("#");
1568
+ if (!modulePath) return {
1569
+ envVars: [],
1570
+ optionalEnvVars: [],
1571
+ unhandledRejections: []
1572
+ };
1573
+ const appRoot = resolve(workspacePath, appPath);
1574
+ const fullPath = resolve(appRoot, modulePath);
1575
+ const workerPath = resolveSnifferFile("sniffer-envparser-worker");
1576
+ const ran = await runSniffer(run, workspacePath, appRoot, nodeWithTsx(workerPath, [fullPath, exportName]));
1577
+ if ("error" in ran) return {
1578
+ envVars: [],
1579
+ optionalEnvVars: [],
1580
+ unhandledRejections: [],
1581
+ error: ran.error
1582
+ };
1583
+ const answer = snifferAnswer(ran.stdout);
1584
+ if (!answer) return {
1585
+ envVars: [],
1586
+ optionalEnvVars: [],
1587
+ unhandledRejections: [],
1588
+ error: /* @__PURE__ */ new Error(`Failed to sniff envParser (exit code ${ran.code}): ${ran.stderr || ran.stdout || "No output"}`)
1589
+ };
1590
+ for (const warning of answer.warnings ?? []) output.warn(warning);
1591
+ return {
1592
+ envVars: answer.envVars ?? [],
1593
+ optionalEnvVars: answer.optionalEnvVars ?? [],
1594
+ unhandledRejections: (answer.unhandledRejections ?? []).map((message) => new Error(message)),
1595
+ ...answer.error ? { error: new Error(answer.error) } : {}
1596
+ };
1597
+ }
1598
+ /**
1599
+ * Sniff environment requirements for multiple apps.
1600
+ *
1601
+ * @param apps - Map of app name to app config
1602
+ * @param workspacePath - Absolute path to workspace root
1603
+ * @param options - Optional configuration for sniffing behavior
1604
+ * @returns Map of app name to sniffed environment
1605
+ */
1606
+ async function sniffAllApps(apps, workspacePath, options = {}) {
1607
+ const results = /* @__PURE__ */ new Map();
1608
+ for (const [appName, app] of Object.entries(apps)) {
1609
+ const sniffed = await sniffAppEnvironment(app, appName, workspacePath, options);
1610
+ results.set(appName, sniffed);
1611
+ }
1612
+ return results;
1613
+ }
1614
+ //#endregion
1615
+ export { newLockHolder as _, DeploymentTimedOut as a, encryption_exports as b, dokploy_api_exports as c, StateLocked as d, StateStoreBusy as f, createStateStore as g, contentVersion as h, DeploymentFailed as i, DocumentStateStore as l, StateVersionConflict as m, sniffAppEnvironment as n, DokployApi as o, StateStoreWithoutLocking as p, DEFAULT_DEPLOYMENT_TIMEOUT_MS as r, DokployApiError as s, sniffAllApps as t, StageStateMissing as u, eventError as v, keyFingerprint as x, encryptSecrets as y };
1616
+
1617
+ //# sourceMappingURL=sniffer-BAQhLEsI.mjs.map