@runa-ai/runa-cli 0.5.72 → 0.7.0

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 (528) hide show
  1. package/dist/build-V66FAQXB.js +1719 -0
  2. package/dist/cache-N7WNPEYF.js +111 -0
  3. package/dist/check-LOMVIRHX.js +12 -0
  4. package/dist/chunk-2APB25TT.js +442 -0
  5. package/dist/chunk-3FDQW524.js +544 -0
  6. package/dist/chunk-3WDV32GA.js +33 -0
  7. package/dist/chunk-5FT3F36G.js +59 -0
  8. package/dist/chunk-5NKWR4FF.js +254 -0
  9. package/dist/chunk-644FVGIQ.js +194 -0
  10. package/dist/chunk-6AALH2ED.js +121 -0
  11. package/dist/chunk-6FAU4IGR.js +63 -0
  12. package/dist/chunk-6Y3LAUGL.js +35 -0
  13. package/dist/chunk-7B5C6U2K.js +274 -0
  14. package/dist/chunk-AAIE4F2U.js +140 -0
  15. package/dist/chunk-AIP6MR42.js +12 -0
  16. package/dist/chunk-CCKG5R4Y.js +59 -0
  17. package/dist/chunk-DRSUEMAK.js +123 -0
  18. package/dist/chunk-FHG3ILE4.js +2011 -0
  19. package/dist/chunk-H2AHNI75.js +31 -0
  20. package/dist/chunk-HD74F6W2.js +460 -0
  21. package/dist/chunk-HKUWEGUX.js +36 -0
  22. package/dist/chunk-IBVVGH6X.js +33 -0
  23. package/dist/chunk-II7VYQEM.js +179 -0
  24. package/dist/chunk-JMJP4A47.js +204 -0
  25. package/dist/chunk-JQXOVCOP.js +574 -0
  26. package/dist/chunk-KE6QJBZG.js +41 -0
  27. package/dist/chunk-KWX3JHCY.js +85 -0
  28. package/dist/chunk-MXRWBNIY.js +74 -0
  29. package/dist/chunk-NPSRD26F.js +149 -0
  30. package/dist/chunk-QDF7QXBL.js +67 -0
  31. package/dist/chunk-QM53IQHM.js +209 -0
  32. package/dist/chunk-RZLYEO4U.js +219 -0
  33. package/dist/chunk-SGJG3BKD.js +351 -0
  34. package/dist/chunk-TYIAD6SB.js +74 -0
  35. package/dist/chunk-UWWSAPDR.js +31 -0
  36. package/dist/chunk-VM3IWOT5.js +458 -0
  37. package/dist/chunk-VRXHCR5K.js +42 -0
  38. package/dist/chunk-WJXC4MVY.js +75 -0
  39. package/dist/chunk-XDCHRVE3.js +215 -0
  40. package/dist/chunk-Z4Z5DNW4.js +1196 -0
  41. package/dist/chunk-ZZOXM6Q4.js +8 -0
  42. package/dist/ci-ZWRVWNFX.js +9298 -0
  43. package/dist/cli/contract-output.d.ts +1 -0
  44. package/dist/cli/index.d.ts +7 -1
  45. package/dist/cli/requested-command.d.ts +8 -0
  46. package/dist/cli-2JNBJUBB.js +704 -0
  47. package/dist/commands/build/actors/db-sync.d.ts +2 -0
  48. package/dist/commands/build/actors/static-checks.d.ts +7 -6
  49. package/dist/commands/build/actors/validate.d.ts +2 -0
  50. package/dist/commands/build/contract.d.ts +30 -30
  51. package/dist/commands/build/machine-dry-run.d.ts +3 -0
  52. package/dist/commands/build/machine-e2e-meta.d.ts +120 -0
  53. package/dist/commands/build/machine.d.ts +22 -22
  54. package/dist/commands/build/types.d.ts +2 -4
  55. package/dist/commands/check/commands/check.d.ts +8 -3
  56. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts +9 -6
  57. package/dist/commands/ci/machine/actors/db/schema-canonical-diff.d.ts +55 -0
  58. package/dist/commands/ci/machine/actors/db/schema-stats.d.ts +11 -0
  59. package/dist/commands/ci/machine/actors/db/sync-schema.d.ts +9 -1
  60. package/dist/commands/ci/machine/contract.d.ts +26 -26
  61. package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts +1 -5
  62. package/dist/commands/ci/machine/formatters/sections/format-helpers.d.ts +5 -0
  63. package/dist/commands/ci/machine/formatters/sections/index.d.ts +2 -2
  64. package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts +3 -3
  65. package/dist/commands/ci/machine/machine-execution-helpers.d.ts +40 -0
  66. package/dist/commands/ci/machine/machine-state-helpers.d.ts +14 -0
  67. package/dist/commands/ci/machine/machine.d.ts +12 -12
  68. package/dist/commands/ci/machine/types.d.ts +2 -5
  69. package/dist/commands/ci/utils/ci-summary.d.ts +15 -15
  70. package/dist/commands/ci/utils/execa-helpers.d.ts +2 -0
  71. package/dist/commands/db/apply/actors/idempotent-actors.d.ts +34 -0
  72. package/dist/commands/db/apply/actors/lock-actors.d.ts +16 -0
  73. package/dist/commands/db/apply/actors/pg-schema-diff-actors.d.ts +31 -0
  74. package/dist/commands/db/apply/actors/seed-actors.d.ts +11 -0
  75. package/dist/commands/db/apply/actors/shared.d.ts +9 -0
  76. package/dist/commands/db/apply/actors.d.ts +16 -65
  77. package/dist/commands/db/apply/contract.d.ts +8 -1
  78. package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts +3 -4
  79. package/dist/commands/db/apply/helpers/data-integrity-verifier.d.ts +37 -0
  80. package/dist/commands/db/apply/helpers/fresh-db-handler.d.ts +34 -0
  81. package/dist/commands/db/apply/helpers/hazard-handler.d.ts +60 -0
  82. package/dist/commands/db/apply/helpers/idempotent-object-registry.d.ts +96 -0
  83. package/dist/commands/db/apply/helpers/idempotent-transaction.d.ts +20 -0
  84. package/dist/commands/db/apply/helpers/index.d.ts +6 -0
  85. package/dist/commands/db/apply/helpers/partition-validator.d.ts +2 -15
  86. package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts +18 -162
  87. package/dist/commands/db/apply/helpers/pg-schema-diff-patterns.d.ts +55 -0
  88. package/dist/commands/db/apply/helpers/pg-schema-diff-version.d.ts +50 -0
  89. package/dist/commands/db/apply/helpers/plan-validator.d.ts +4 -10
  90. package/dist/commands/db/apply/helpers/rbac-password-manager.d.ts +34 -0
  91. package/dist/commands/db/apply/helpers/retry-logic.d.ts +16 -2
  92. package/dist/commands/db/apply/helpers/shadow-db-manager.d.ts +1 -1
  93. package/dist/commands/db/apply/helpers/sql-utils.d.ts +26 -0
  94. package/dist/commands/db/apply/machine.d.ts +52 -1
  95. package/dist/commands/db/commands/db-apply.d.ts +18 -0
  96. package/dist/commands/db/commands/db-sync/boundary-classifier.d.ts +21 -0
  97. package/dist/commands/db/commands/db-sync/error-classifier.d.ts +9 -0
  98. package/dist/commands/db/commands/db-sync/plan-hazard-analyzer.d.ts +13 -0
  99. package/dist/commands/db/commands/db-sync/risk-reporter.d.ts +19 -0
  100. package/dist/commands/db/commands/db-sync/sql-parser.d.ts +25 -0
  101. package/dist/commands/db/commands/db-sync/types.d.ts +47 -0
  102. package/dist/commands/db/commands/db-sync.d.ts +14 -0
  103. package/dist/commands/db/sync/contract.d.ts +6 -2
  104. package/dist/commands/db/sync/machine.d.ts +2 -1
  105. package/dist/commands/db/types.d.ts +2 -0
  106. package/dist/commands/db/utils/boundary-policy/rule-compiler.d.ts +11 -0
  107. package/dist/commands/db/utils/boundary-policy/types.d.ts +105 -0
  108. package/dist/commands/db/utils/boundary-policy/validation.d.ts +20 -0
  109. package/dist/commands/db/utils/boundary-policy-runtime.d.ts +28 -0
  110. package/dist/commands/db/utils/boundary-policy.d.ts +5 -0
  111. package/dist/commands/db/utils/idempotent-risk-context.d.ts +29 -0
  112. package/dist/commands/db/utils/preflight-check.d.ts +14 -0
  113. package/dist/commands/db/utils/preflight-checks/domain-naming-checks.d.ts +106 -0
  114. package/dist/commands/db/utils/preflight-checks/orphan-checks.d.ts +36 -0
  115. package/dist/commands/db/utils/preflight-checks/schema-risk-checks.d.ts +22 -0
  116. package/dist/commands/db/utils/preflight-checks/supabase-checks.d.ts +55 -0
  117. package/dist/commands/db/utils/risk-detector-loader.d.ts +8 -0
  118. package/dist/commands/db/utils/schema-precheck-budget.d.ts +17 -0
  119. package/dist/commands/db/utils/sql-boundary-parser.d.ts +12 -0
  120. package/dist/commands/db/utils/sql-file-collector.d.ts +8 -0
  121. package/dist/commands/db/utils/sql-filename-parser.d.ts +20 -0
  122. package/dist/commands/db/utils/sql-table-extractor-ast.d.ts +19 -0
  123. package/dist/commands/db/utils/sql-table-extractor-regex.d.ts +50 -0
  124. package/dist/commands/db/utils/sql-table-extractor-rls.d.ts +13 -0
  125. package/dist/commands/db/utils/sql-table-extractor.d.ts +79 -1
  126. package/dist/commands/db/utils/table-registry-introspection.d.ts +68 -0
  127. package/dist/commands/db/utils/table-registry.d.ts +3 -38
  128. package/dist/commands/dev/actors/app-lifecycle.d.ts +18 -0
  129. package/dist/commands/dev/actors/index.d.ts +12 -2
  130. package/dist/commands/dev/actors/process-check.d.ts +12 -0
  131. package/dist/commands/dev/actors/shared.d.ts +15 -0
  132. package/dist/commands/dev/actors/tables-manifest.d.ts +16 -0
  133. package/dist/commands/dev/contract.d.ts +3 -3
  134. package/dist/commands/dev/guards.d.ts +24 -0
  135. package/dist/commands/dev/machine.d.ts +27 -32
  136. package/dist/commands/dev/types.d.ts +2 -0
  137. package/dist/commands/doctor.d.ts +9 -0
  138. package/dist/commands/env/commands/env-pull/auth.d.ts +13 -0
  139. package/dist/commands/env/commands/env-pull/dotenv-files.d.ts +14 -0
  140. package/dist/commands/env/commands/env-pull/security.d.ts +12 -0
  141. package/dist/commands/env/commands/env-pull/service.d.ts +8 -0
  142. package/dist/commands/env/commands/env-pull/shared.d.ts +79 -0
  143. package/dist/commands/env/commands/setup/types.d.ts +1 -1
  144. package/dist/commands/env/constants/local-supabase.d.ts +2 -0
  145. package/dist/commands/inject-test-attrs/defaults.d.ts +9 -0
  146. package/dist/commands/template-check/contract.d.ts +6 -6
  147. package/dist/commands/template-check/machine.d.ts +2 -2
  148. package/dist/commands/template-check/types.d.ts +0 -4
  149. package/dist/commands/template-check/utils/diff-analyzer.d.ts +0 -4
  150. package/dist/commands/utils/machine-state-logging.d.ts +20 -0
  151. package/dist/commands/utils/repo-root.d.ts +2 -0
  152. package/dist/config/env.d.ts +4 -4
  153. package/dist/config-loader-GT3HAQ7U.js +7 -0
  154. package/dist/db-XULCILOU.js +14137 -0
  155. package/dist/dev-5YXNPTCJ.js +992 -0
  156. package/dist/doctor-MZLOA53G.js +44 -0
  157. package/dist/env-HMMRSYCI.js +7 -0
  158. package/dist/env-SS66PZ4B.js +2623 -0
  159. package/dist/env-files-2UIUYLLR.js +8 -0
  160. package/dist/error-handler-HEXBRNVV.js +460 -0
  161. package/dist/hotfix-YA3DGLOM.js +1477 -0
  162. package/dist/index.d.ts +5 -1
  163. package/dist/index.js +48 -42995
  164. package/dist/init-ZIL6LRFO.js +631 -0
  165. package/dist/inject-test-attrs-P44BVTQS.js +23 -0
  166. package/dist/internal/machines/snapshot-helpers.d.ts +6 -0
  167. package/dist/lib/sql-comment-utils.d.ts +25 -0
  168. package/dist/license-OB7GVJQ2.js +468 -0
  169. package/dist/link-VSNDVZZD.js +59 -0
  170. package/dist/manifest-TMFLESHW.js +19 -0
  171. package/dist/prepare-32DOVHTE.js +250 -0
  172. package/dist/risk-detector-4U6ZJ2G5.js +6 -0
  173. package/dist/risk-detector-core-TK4OAI3N.js +166 -0
  174. package/dist/risk-detector-plpgsql-HWKS4OLR.js +1886 -0
  175. package/dist/sdk-XK6HQU7S.js +348 -0
  176. package/dist/services-7VK5KZTO.js +177 -0
  177. package/dist/session-SFW5QSXZ.js +142 -0
  178. package/dist/signal-handler-DO3OANW5.js +6 -0
  179. package/dist/status-UTKS63AB.js +94 -0
  180. package/dist/telemetry-P56UBLZ2.js +93 -0
  181. package/dist/template-check-3P4HZXVY.js +1944 -0
  182. package/dist/test-V4KQL574.js +650 -0
  183. package/dist/test-gen-FS4CEY3P.js +88 -0
  184. package/dist/ui-RJAMCWUI.js +331 -0
  185. package/dist/upgrade-NUK3ZBCL.js +637 -0
  186. package/dist/utils/config-loader.d.ts +0 -3
  187. package/dist/validate-CAAW4Y44.js +54 -0
  188. package/dist/validators/risk-detector-content-risks.d.ts +13 -0
  189. package/dist/validators/risk-detector-core.d.ts +25 -0
  190. package/dist/validators/risk-detector-patterns.d.ts +15 -0
  191. package/dist/validators/risk-detector-plpgsql-expression-resolver.d.ts +22 -0
  192. package/dist/validators/risk-detector-plpgsql-parser.d.ts +5 -0
  193. package/dist/validators/risk-detector-plpgsql-tokenizer.d.ts +18 -0
  194. package/dist/validators/risk-detector-plpgsql.d.ts +9 -0
  195. package/dist/validators/risk-detector-text-utils.d.ts +6 -0
  196. package/dist/validators/risk-detector-types.d.ts +16 -0
  197. package/dist/validators/risk-detector.d.ts +7 -26
  198. package/dist/vuln-check-2W7N5TA2.js +121 -0
  199. package/dist/vuln-checker-IQJ56RUV.js +3223 -0
  200. package/dist/watch-PNTKZYFB.js +911 -0
  201. package/dist/workflow-H75N4BXX.js +897 -0
  202. package/package.json +5 -2
  203. package/dist/cli/contract-mode.d.ts.map +0 -1
  204. package/dist/cli/contract-output.d.ts.map +0 -1
  205. package/dist/cli/early-flags.d.ts.map +0 -1
  206. package/dist/cli/error-handler.d.ts.map +0 -1
  207. package/dist/cli/exec.d.ts.map +0 -1
  208. package/dist/cli/index.d.ts.map +0 -1
  209. package/dist/cli/json-output.d.ts.map +0 -1
  210. package/dist/cli/non-interactive.d.ts.map +0 -1
  211. package/dist/cli/output-format.d.ts.map +0 -1
  212. package/dist/cli/signal-handler.d.ts.map +0 -1
  213. package/dist/commands/build/actors/build.d.ts.map +0 -1
  214. package/dist/commands/build/actors/clean.d.ts.map +0 -1
  215. package/dist/commands/build/actors/db-sync.d.ts.map +0 -1
  216. package/dist/commands/build/actors/index.d.ts.map +0 -1
  217. package/dist/commands/build/actors/manifest.d.ts.map +0 -1
  218. package/dist/commands/build/actors/setup.d.ts.map +0 -1
  219. package/dist/commands/build/actors/static-checks.d.ts.map +0 -1
  220. package/dist/commands/build/actors/validate.d.ts.map +0 -1
  221. package/dist/commands/build/commands/build.d.ts.map +0 -1
  222. package/dist/commands/build/contract.d.ts.map +0 -1
  223. package/dist/commands/build/guards.d.ts.map +0 -1
  224. package/dist/commands/build/index.d.ts.map +0 -1
  225. package/dist/commands/build/machine.d.ts.map +0 -1
  226. package/dist/commands/build/types.d.ts.map +0 -1
  227. package/dist/commands/cache.d.ts.map +0 -1
  228. package/dist/commands/check/commands/check.d.ts.map +0 -1
  229. package/dist/commands/check/index.d.ts.map +0 -1
  230. package/dist/commands/ci/commands/ci-checks.d.ts.map +0 -1
  231. package/dist/commands/ci/commands/ci-layer-content.d.ts.map +0 -1
  232. package/dist/commands/ci/commands/ci-pr-capabilities.d.ts.map +0 -1
  233. package/dist/commands/ci/commands/ci-prod-apply.d.ts.map +0 -1
  234. package/dist/commands/ci/commands/ci-prod-db-operations.d.ts.map +0 -1
  235. package/dist/commands/ci/commands/ci-prod-github.d.ts.map +0 -1
  236. package/dist/commands/ci/commands/ci-prod-types.d.ts.map +0 -1
  237. package/dist/commands/ci/commands/ci-prod-utils.d.ts.map +0 -1
  238. package/dist/commands/ci/commands/ci-prod-workflow.d.ts.map +0 -1
  239. package/dist/commands/ci/commands/ci-resolvers.d.ts.map +0 -1
  240. package/dist/commands/ci/commands/ci-static.d.ts.map +0 -1
  241. package/dist/commands/ci/commands/ci-supabase-local.d.ts.map +0 -1
  242. package/dist/commands/ci/index.d.ts.map +0 -1
  243. package/dist/commands/ci/machine/actors/build/app-build.d.ts.map +0 -1
  244. package/dist/commands/ci/machine/actors/build/app-start.d.ts.map +0 -1
  245. package/dist/commands/ci/machine/actors/build/build-and-playwright.d.ts.map +0 -1
  246. package/dist/commands/ci/machine/actors/build/index.d.ts.map +0 -1
  247. package/dist/commands/ci/machine/actors/build/playwright-install.d.ts.map +0 -1
  248. package/dist/commands/ci/machine/actors/build/static-checks.d.ts.map +0 -1
  249. package/dist/commands/ci/machine/actors/db/apply-seeds.d.ts.map +0 -1
  250. package/dist/commands/ci/machine/actors/db/collect-schema-stats.d.ts.map +0 -1
  251. package/dist/commands/ci/machine/actors/db/index.d.ts.map +0 -1
  252. package/dist/commands/ci/machine/actors/db/pgtap-install.d.ts.map +0 -1
  253. package/dist/commands/ci/machine/actors/db/production-preview.d.ts.map +0 -1
  254. package/dist/commands/ci/machine/actors/db/pull-production.d.ts.map +0 -1
  255. package/dist/commands/ci/machine/actors/db/reset.d.ts.map +0 -1
  256. package/dist/commands/ci/machine/actors/db/schema-stats.d.ts.map +0 -1
  257. package/dist/commands/ci/machine/actors/db/setup-roles.d.ts.map +0 -1
  258. package/dist/commands/ci/machine/actors/db/sync-schema.d.ts.map +0 -1
  259. package/dist/commands/ci/machine/actors/finalize/github.d.ts.map +0 -1
  260. package/dist/commands/ci/machine/actors/finalize/index.d.ts.map +0 -1
  261. package/dist/commands/ci/machine/actors/finalize/summary.d.ts.map +0 -1
  262. package/dist/commands/ci/machine/actors/index.d.ts.map +0 -1
  263. package/dist/commands/ci/machine/actors/setup/index.d.ts.map +0 -1
  264. package/dist/commands/ci/machine/actors/setup/local.d.ts.map +0 -1
  265. package/dist/commands/ci/machine/actors/setup/pr-common.d.ts.map +0 -1
  266. package/dist/commands/ci/machine/actors/setup/pr-local.d.ts.map +0 -1
  267. package/dist/commands/ci/machine/actors/test/capabilities.d.ts.map +0 -1
  268. package/dist/commands/ci/machine/actors/test/index.d.ts.map +0 -1
  269. package/dist/commands/ci/machine/actors/test/run-layers.d.ts.map +0 -1
  270. package/dist/commands/ci/machine/commands/ci-local.d.ts.map +0 -1
  271. package/dist/commands/ci/machine/commands/ci-pr.d.ts.map +0 -1
  272. package/dist/commands/ci/machine/commands/index.d.ts.map +0 -1
  273. package/dist/commands/ci/machine/commands/machine-runner.d.ts.map +0 -1
  274. package/dist/commands/ci/machine/commands/runtime-env.d.ts.map +0 -1
  275. package/dist/commands/ci/machine/contract.d.ts.map +0 -1
  276. package/dist/commands/ci/machine/formatters/github-comment-types.d.ts.map +0 -1
  277. package/dist/commands/ci/machine/formatters/github-comment.d.ts.map +0 -1
  278. package/dist/commands/ci/machine/formatters/index.d.ts.map +0 -1
  279. package/dist/commands/ci/machine/formatters/sections/final-comment.d.ts.map +0 -1
  280. package/dist/commands/ci/machine/formatters/sections/format-helpers.d.ts.map +0 -1
  281. package/dist/commands/ci/machine/formatters/sections/index.d.ts.map +0 -1
  282. package/dist/commands/ci/machine/formatters/sections/progress-comment.d.ts.map +0 -1
  283. package/dist/commands/ci/machine/formatters/sections/schema-matrix.d.ts.map +0 -1
  284. package/dist/commands/ci/machine/formatters/summary.d.ts.map +0 -1
  285. package/dist/commands/ci/machine/guards.d.ts.map +0 -1
  286. package/dist/commands/ci/machine/helpers.d.ts.map +0 -1
  287. package/dist/commands/ci/machine/index.d.ts.map +0 -1
  288. package/dist/commands/ci/machine/machine.d.ts.map +0 -1
  289. package/dist/commands/ci/machine/types.d.ts.map +0 -1
  290. package/dist/commands/ci/utils/ai-report.d.ts.map +0 -1
  291. package/dist/commands/ci/utils/app-process.d.ts.map +0 -1
  292. package/dist/commands/ci/utils/app-runtime.d.ts.map +0 -1
  293. package/dist/commands/ci/utils/ci-config.d.ts.map +0 -1
  294. package/dist/commands/ci/utils/ci-env-schema.d.ts.map +0 -1
  295. package/dist/commands/ci/utils/ci-logging.d.ts.map +0 -1
  296. package/dist/commands/ci/utils/ci-summary.d.ts.map +0 -1
  297. package/dist/commands/ci/utils/config-readers.d.ts.map +0 -1
  298. package/dist/commands/ci/utils/db-url-utils.d.ts.map +0 -1
  299. package/dist/commands/ci/utils/e2e-auth-setup.d.ts.map +0 -1
  300. package/dist/commands/ci/utils/env-security.d.ts.map +0 -1
  301. package/dist/commands/ci/utils/execa-helpers.d.ts.map +0 -1
  302. package/dist/commands/ci/utils/exit-code-computation.d.ts.map +0 -1
  303. package/dist/commands/ci/utils/github-api.d.ts.map +0 -1
  304. package/dist/commands/ci/utils/github.d.ts.map +0 -1
  305. package/dist/commands/ci/utils/index.d.ts.map +0 -1
  306. package/dist/commands/ci/utils/pgtap-installer.d.ts.map +0 -1
  307. package/dist/commands/ci/utils/rls-verification.d.ts.map +0 -1
  308. package/dist/commands/ci/utils/schema-operations.d.ts.map +0 -1
  309. package/dist/commands/ci/utils/seed-operations.d.ts.map +0 -1
  310. package/dist/commands/ci/utils/test-parallel.d.ts.map +0 -1
  311. package/dist/commands/ci/utils/timestamp-invariants.d.ts.map +0 -1
  312. package/dist/commands/ci/utils/workflow-idempotency.d.ts.map +0 -1
  313. package/dist/commands/db/apply/actors.d.ts.map +0 -1
  314. package/dist/commands/db/apply/contract.d.ts.map +0 -1
  315. package/dist/commands/db/apply/helpers/advisory-lock.d.ts.map +0 -1
  316. package/dist/commands/db/apply/helpers/data-compatibility-checker.d.ts.map +0 -1
  317. package/dist/commands/db/apply/helpers/index.d.ts.map +0 -1
  318. package/dist/commands/db/apply/helpers/partition-acl-cleaner.d.ts.map +0 -1
  319. package/dist/commands/db/apply/helpers/partition-prefilter.d.ts.map +0 -1
  320. package/dist/commands/db/apply/helpers/partition-validator.d.ts.map +0 -1
  321. package/dist/commands/db/apply/helpers/pg-schema-diff-helpers.d.ts.map +0 -1
  322. package/dist/commands/db/apply/helpers/plan-validator.d.ts.map +0 -1
  323. package/dist/commands/db/apply/helpers/retry-logic.d.ts.map +0 -1
  324. package/dist/commands/db/apply/helpers/shadow-db-manager.d.ts.map +0 -1
  325. package/dist/commands/db/apply/index.d.ts.map +0 -1
  326. package/dist/commands/db/apply/machine.d.ts.map +0 -1
  327. package/dist/commands/db/commands/db-apply.d.ts.map +0 -1
  328. package/dist/commands/db/commands/db-audit.d.ts.map +0 -1
  329. package/dist/commands/db/commands/db-backup.d.ts.map +0 -1
  330. package/dist/commands/db/commands/db-cleanup.d.ts.map +0 -1
  331. package/dist/commands/db/commands/db-derive-role-passwords.d.ts.map +0 -1
  332. package/dist/commands/db/commands/db-derive-urls.d.ts.map +0 -1
  333. package/dist/commands/db/commands/db-diagram.d.ts.map +0 -1
  334. package/dist/commands/db/commands/db-drizzle.d.ts.map +0 -1
  335. package/dist/commands/db/commands/db-extension.d.ts.map +0 -1
  336. package/dist/commands/db/commands/db-generate-password.d.ts.map +0 -1
  337. package/dist/commands/db/commands/db-lifecycle.d.ts.map +0 -1
  338. package/dist/commands/db/commands/db-rollback.d.ts.map +0 -1
  339. package/dist/commands/db/commands/db-schema.d.ts.map +0 -1
  340. package/dist/commands/db/commands/db-seed-metadata.d.ts.map +0 -1
  341. package/dist/commands/db/commands/db-seed-verify.d.ts.map +0 -1
  342. package/dist/commands/db/commands/db-seed.d.ts.map +0 -1
  343. package/dist/commands/db/commands/db-snapshot.d.ts.map +0 -1
  344. package/dist/commands/db/commands/db-stack.d.ts.map +0 -1
  345. package/dist/commands/db/commands/db-stats.d.ts.map +0 -1
  346. package/dist/commands/db/commands/db-sync.d.ts.map +0 -1
  347. package/dist/commands/db/commands/db-test.d.ts.map +0 -1
  348. package/dist/commands/db/constants.d.ts.map +0 -1
  349. package/dist/commands/db/extension-registry.d.ts.map +0 -1
  350. package/dist/commands/db/index.d.ts.map +0 -1
  351. package/dist/commands/db/preflight/actors.d.ts.map +0 -1
  352. package/dist/commands/db/preflight/contract.d.ts.map +0 -1
  353. package/dist/commands/db/preflight/index.d.ts.map +0 -1
  354. package/dist/commands/db/sync/actors.d.ts.map +0 -1
  355. package/dist/commands/db/sync/contract.d.ts.map +0 -1
  356. package/dist/commands/db/sync/index.d.ts.map +0 -1
  357. package/dist/commands/db/sync/machine.d.ts.map +0 -1
  358. package/dist/commands/db/types.d.ts.map +0 -1
  359. package/dist/commands/db/utils/db-target.d.ts.map +0 -1
  360. package/dist/commands/db/utils/db-url-builder.d.ts.map +0 -1
  361. package/dist/commands/db/utils/error-handlers.d.ts.map +0 -1
  362. package/dist/commands/db/utils/import-impact-analyzer.d.ts.map +0 -1
  363. package/dist/commands/db/utils/preflight-check.d.ts.map +0 -1
  364. package/dist/commands/db/utils/psql.d.ts.map +0 -1
  365. package/dist/commands/db/utils/schema-detector.d.ts.map +0 -1
  366. package/dist/commands/db/utils/schema-sync.d.ts.map +0 -1
  367. package/dist/commands/db/utils/script-runner.d.ts.map +0 -1
  368. package/dist/commands/db/utils/seed-manager.d.ts.map +0 -1
  369. package/dist/commands/db/utils/semantic-mapper.d.ts.map +0 -1
  370. package/dist/commands/db/utils/sql-table-extractor.d.ts.map +0 -1
  371. package/dist/commands/db/utils/stack-detector.d.ts.map +0 -1
  372. package/dist/commands/db/utils/table-registry.d.ts.map +0 -1
  373. package/dist/commands/db/utils/table-source-classifier.d.ts.map +0 -1
  374. package/dist/commands/dev/actors/index.d.ts.map +0 -1
  375. package/dist/commands/dev/commands/dev.d.ts.map +0 -1
  376. package/dist/commands/dev/contract.d.ts.map +0 -1
  377. package/dist/commands/dev/guards.d.ts.map +0 -1
  378. package/dist/commands/dev/helpers/stale-process-detector.d.ts.map +0 -1
  379. package/dist/commands/dev/machine.d.ts.map +0 -1
  380. package/dist/commands/dev/types.d.ts.map +0 -1
  381. package/dist/commands/env/commands/env-check.d.ts.map +0 -1
  382. package/dist/commands/env/commands/env-encrypt.d.ts.map +0 -1
  383. package/dist/commands/env/commands/env-pull.d.ts.map +0 -1
  384. package/dist/commands/env/commands/env-setup.d.ts.map +0 -1
  385. package/dist/commands/env/commands/env-sync.d.ts.map +0 -1
  386. package/dist/commands/env/commands/setup/action.d.ts.map +0 -1
  387. package/dist/commands/env/commands/setup/auth.d.ts.map +0 -1
  388. package/dist/commands/env/commands/setup/file-export.d.ts.map +0 -1
  389. package/dist/commands/env/commands/setup/github-api.d.ts.map +0 -1
  390. package/dist/commands/env/commands/setup/helpers.d.ts.map +0 -1
  391. package/dist/commands/env/commands/setup/index.d.ts.map +0 -1
  392. package/dist/commands/env/commands/setup/parsers.d.ts.map +0 -1
  393. package/dist/commands/env/commands/setup/prompts.d.ts.map +0 -1
  394. package/dist/commands/env/commands/setup/supabase-api.d.ts.map +0 -1
  395. package/dist/commands/env/commands/setup/types.d.ts.map +0 -1
  396. package/dist/commands/env/commands/setup/vercel-api.d.ts.map +0 -1
  397. package/dist/commands/env/constants/local-supabase.d.ts.map +0 -1
  398. package/dist/commands/env/index.d.ts.map +0 -1
  399. package/dist/commands/hotfix/actors.d.ts.map +0 -1
  400. package/dist/commands/hotfix/commands/hotfix-complete.d.ts.map +0 -1
  401. package/dist/commands/hotfix/commands/hotfix-create.d.ts.map +0 -1
  402. package/dist/commands/hotfix/commands/hotfix-deploy.d.ts.map +0 -1
  403. package/dist/commands/hotfix/commands/hotfix-status.d.ts.map +0 -1
  404. package/dist/commands/hotfix/contract.d.ts.map +0 -1
  405. package/dist/commands/hotfix/index.d.ts.map +0 -1
  406. package/dist/commands/hotfix/machine.d.ts.map +0 -1
  407. package/dist/commands/hotfix/metadata.d.ts.map +0 -1
  408. package/dist/commands/hotfix/utils/hotfix-machine-helper.d.ts.map +0 -1
  409. package/dist/commands/init.d.ts.map +0 -1
  410. package/dist/commands/inject-test-attrs/action.d.ts.map +0 -1
  411. package/dist/commands/inject-test-attrs/commands/inject-test-attrs.d.ts.map +0 -1
  412. package/dist/commands/inject-test-attrs/contract.d.ts.map +0 -1
  413. package/dist/commands/inject-test-attrs/detection-diagnostics.d.ts.map +0 -1
  414. package/dist/commands/inject-test-attrs/formatter.d.ts.map +0 -1
  415. package/dist/commands/inject-test-attrs/index.d.ts.map +0 -1
  416. package/dist/commands/inject-test-attrs/manifest-generator.d.ts.map +0 -1
  417. package/dist/commands/inject-test-attrs/processor-utils.d.ts.map +0 -1
  418. package/dist/commands/inject-test-attrs/processor.d.ts.map +0 -1
  419. package/dist/commands/inject-test-attrs/types.d.ts.map +0 -1
  420. package/dist/commands/link.d.ts.map +0 -1
  421. package/dist/commands/manifest/index.d.ts.map +0 -1
  422. package/dist/commands/prepare/commands/prepare.d.ts.map +0 -1
  423. package/dist/commands/prepare/index.d.ts.map +0 -1
  424. package/dist/commands/sdk/commands/publish.d.ts.map +0 -1
  425. package/dist/commands/sdk/index.d.ts.map +0 -1
  426. package/dist/commands/services/index.d.ts.map +0 -1
  427. package/dist/commands/session/index.d.ts.map +0 -1
  428. package/dist/commands/status.d.ts.map +0 -1
  429. package/dist/commands/telemetry.d.ts.map +0 -1
  430. package/dist/commands/template-check/actors/compare.d.ts.map +0 -1
  431. package/dist/commands/template-check/actors/discover.d.ts.map +0 -1
  432. package/dist/commands/template-check/actors/index.d.ts.map +0 -1
  433. package/dist/commands/template-check/actors/report.d.ts.map +0 -1
  434. package/dist/commands/template-check/commands/template-check.d.ts.map +0 -1
  435. package/dist/commands/template-check/config.d.ts.map +0 -1
  436. package/dist/commands/template-check/contract.d.ts.map +0 -1
  437. package/dist/commands/template-check/index.d.ts.map +0 -1
  438. package/dist/commands/template-check/machine.d.ts.map +0 -1
  439. package/dist/commands/template-check/types.d.ts.map +0 -1
  440. package/dist/commands/template-check/utils/diff-analyzer.d.ts.map +0 -1
  441. package/dist/commands/template-check/utils/normalizer.d.ts.map +0 -1
  442. package/dist/commands/template-check/utils/path-mapping.d.ts.map +0 -1
  443. package/dist/commands/test/commands/test-db.d.ts.map +0 -1
  444. package/dist/commands/test/commands/test-e2e.d.ts.map +0 -1
  445. package/dist/commands/test/commands/test-fast.d.ts.map +0 -1
  446. package/dist/commands/test/commands/test-integration.d.ts.map +0 -1
  447. package/dist/commands/test/commands/test-layer.d.ts.map +0 -1
  448. package/dist/commands/test/commands/test-owasp-generate.d.ts.map +0 -1
  449. package/dist/commands/test/commands/test-service.d.ts.map +0 -1
  450. package/dist/commands/test/commands/test-static.d.ts.map +0 -1
  451. package/dist/commands/test/commands/test.d.ts.map +0 -1
  452. package/dist/commands/test/index.d.ts.map +0 -1
  453. package/dist/commands/test-gen.d.ts.map +0 -1
  454. package/dist/commands/ui.d.ts.map +0 -1
  455. package/dist/commands/upgrade.d.ts.map +0 -1
  456. package/dist/commands/validate.d.ts.map +0 -1
  457. package/dist/commands/vuln-check.d.ts.map +0 -1
  458. package/dist/commands/watch.d.ts.map +0 -1
  459. package/dist/commands/workflow/commands/deploy-production.d.ts.map +0 -1
  460. package/dist/commands/workflow/commands/final-status.d.ts.map +0 -1
  461. package/dist/commands/workflow/commands/log.d.ts.map +0 -1
  462. package/dist/commands/workflow/commands/notify.d.ts.map +0 -1
  463. package/dist/commands/workflow/commands/paths.d.ts.map +0 -1
  464. package/dist/commands/workflow/commands/sync.d.ts.map +0 -1
  465. package/dist/commands/workflow/commands/validate.d.ts.map +0 -1
  466. package/dist/commands/workflow/commands/verify-credentials.d.ts.map +0 -1
  467. package/dist/commands/workflow/index.d.ts.map +0 -1
  468. package/dist/commands/workflow/types.d.ts.map +0 -1
  469. package/dist/config/env-files.d.ts.map +0 -1
  470. package/dist/config/env.d.ts.map +0 -1
  471. package/dist/constants/versions.d.ts.map +0 -1
  472. package/dist/contracts/envelope.d.ts.map +0 -1
  473. package/dist/errors/catalog.d.ts.map +0 -1
  474. package/dist/errors/exit-codes.d.ts.map +0 -1
  475. package/dist/errors/index.d.ts.map +0 -1
  476. package/dist/incremental/affected-tests.d.ts.map +0 -1
  477. package/dist/index.d.ts.map +0 -1
  478. package/dist/internal/machines/index.d.ts.map +0 -1
  479. package/dist/internal/machines/machine-runner.d.ts.map +0 -1
  480. package/dist/internal/machines/snapshot-helpers.d.ts.map +0 -1
  481. package/dist/internal/machines/types.d.ts.map +0 -1
  482. package/dist/internal/vuln-checker/analyzers/dependency-analyzer.d.ts.map +0 -1
  483. package/dist/internal/vuln-checker/analyzers/rls-analyzer.d.ts.map +0 -1
  484. package/dist/internal/vuln-checker/analyzers/secret-analyzer.d.ts.map +0 -1
  485. package/dist/internal/vuln-checker/analyzers/typescript-analyzer.d.ts.map +0 -1
  486. package/dist/internal/vuln-checker/config/loader.d.ts.map +0 -1
  487. package/dist/internal/vuln-checker/constants.d.ts.map +0 -1
  488. package/dist/internal/vuln-checker/ignore/matcher.d.ts.map +0 -1
  489. package/dist/internal/vuln-checker/index.d.ts.map +0 -1
  490. package/dist/internal/vuln-checker/reporters/console-reporter.d.ts.map +0 -1
  491. package/dist/internal/vuln-checker/reporters/json-reporter.d.ts.map +0 -1
  492. package/dist/internal/vuln-checker/reporters/markdown-reporter.d.ts.map +0 -1
  493. package/dist/internal/vuln-checker/reporters/sarif-reporter.d.ts.map +0 -1
  494. package/dist/internal/vuln-checker/security/path-validation.d.ts.map +0 -1
  495. package/dist/internal/vuln-checker/types.d.ts.map +0 -1
  496. package/dist/notifiers/desktop-notifier.d.ts.map +0 -1
  497. package/dist/ui/components/db-panel.d.ts.map +0 -1
  498. package/dist/ui/components/status-bar.d.ts.map +0 -1
  499. package/dist/ui/components/test-panel.d.ts.map +0 -1
  500. package/dist/ui/dashboard.d.ts.map +0 -1
  501. package/dist/ui/index.d.ts.map +0 -1
  502. package/dist/utils/config-loader.d.ts.map +0 -1
  503. package/dist/utils/config-updater.d.ts.map +0 -1
  504. package/dist/utils/diagnostics.d.ts.map +0 -1
  505. package/dist/utils/dotenvx.d.ts.map +0 -1
  506. package/dist/utils/env-local-bridge.d.ts.map +0 -1
  507. package/dist/utils/execution-plan.d.ts.map +0 -1
  508. package/dist/utils/github-output-security.d.ts.map +0 -1
  509. package/dist/utils/help-system.d.ts.map +0 -1
  510. package/dist/utils/license/admin-auth.d.ts.map +0 -1
  511. package/dist/utils/license/allowlist-checker.d.ts.map +0 -1
  512. package/dist/utils/license/ci-detector.d.ts.map +0 -1
  513. package/dist/utils/license/index.d.ts.map +0 -1
  514. package/dist/utils/license/owner-resolver.d.ts.map +0 -1
  515. package/dist/utils/license/types.d.ts.map +0 -1
  516. package/dist/utils/license/validate-owner.d.ts.map +0 -1
  517. package/dist/utils/path-security.d.ts.map +0 -1
  518. package/dist/utils/port-allocator.d.ts.map +0 -1
  519. package/dist/utils/secure-exec.d.ts.map +0 -1
  520. package/dist/utils/template-fetcher.d.ts.map +0 -1
  521. package/dist/utils/type-guards.d.ts.map +0 -1
  522. package/dist/utils/vercel-project.d.ts.map +0 -1
  523. package/dist/utils/workspace-detector.d.ts.map +0 -1
  524. package/dist/validators/risk-detector.d.ts.map +0 -1
  525. package/dist/validators/schema-validator.d.ts.map +0 -1
  526. package/dist/version.d.ts.map +0 -1
  527. package/dist/watchers/schema-watcher.d.ts.map +0 -1
  528. package/dist/watchers/test-watcher.d.ts.map +0 -1
@@ -0,0 +1,2623 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';
3
+ import { tryResolveDatabaseUrl } from './chunk-KWX3JHCY.js';
4
+ import { registerCleanup } from './chunk-TYIAD6SB.js';
5
+ import { isNonInteractiveEnabled } from './chunk-6Y3LAUGL.js';
6
+ import './chunk-NPSRD26F.js';
7
+ import { loadEnvFiles } from './chunk-644FVGIQ.js';
8
+ import { getVercelRootDirectory } from './chunk-MXRWBNIY.js';
9
+ import { syncRunaConfigWithVercel } from './chunk-6AALH2ED.js';
10
+ import './chunk-DRSUEMAK.js';
11
+ import './chunk-II7VYQEM.js';
12
+ import { init_local_supabase, getLocalSupabaseEnvValues, getLocalValueDescriptions } from './chunk-VM3IWOT5.js';
13
+ import { emitJsonSuccess } from './chunk-KE6QJBZG.js';
14
+ import './chunk-WJXC4MVY.js';
15
+ import './chunk-HKUWEGUX.js';
16
+ import './chunk-JMJP4A47.js';
17
+ import { init_esm_shims } from './chunk-VRXHCR5K.js';
18
+ import { Command } from 'commander';
19
+ import { existsSync, rmSync, readFileSync, mkdtempSync, writeFileSync, chmodSync, realpathSync, unlinkSync } from 'fs';
20
+ import path, { resolve, join, basename, dirname } from 'path';
21
+ import { CLIError, createCLILogger, syncEnvironment, EnvSyncOutputSchema } from '@runa-ai/runa';
22
+ import { execa } from 'execa';
23
+ import { z } from 'zod';
24
+ import { fileURLToPath } from 'url';
25
+ import { parse } from 'dotenv';
26
+ import { stdout, stdin } from 'process';
27
+ import * as readline from 'readline/promises';
28
+ import { randomBytes } from 'crypto';
29
+ import { spawn } from 'child_process';
30
+ import { tmpdir } from 'os';
31
+
32
+ createRequire(import.meta.url);
33
+
34
+ // src/commands/env/index.ts
35
+ init_esm_shims();
36
+
37
+ // src/commands/env/commands/env-check.ts
38
+ init_esm_shims();
39
+ var EnvCheckOutputSchema = z.object({
40
+ local: z.object({
41
+ ready: z.boolean(),
42
+ checks: z.array(
43
+ z.object({
44
+ name: z.string(),
45
+ status: z.enum(["ok", "warning", "error", "info"]),
46
+ message: z.string(),
47
+ hint: z.string().optional()
48
+ })
49
+ )
50
+ }),
51
+ production: z.object({
52
+ checks: z.array(
53
+ z.object({
54
+ name: z.string(),
55
+ status: z.enum(["ok", "warning", "error", "info"]),
56
+ message: z.string(),
57
+ hint: z.string().optional()
58
+ })
59
+ )
60
+ })
61
+ });
62
+ function checkEnvDevelopment(workDir) {
63
+ const filePath = resolve(workDir, ".env.development");
64
+ if (!existsSync(filePath)) {
65
+ return {
66
+ name: ".env.development",
67
+ status: "warning",
68
+ message: "Not found",
69
+ hint: "Run: runa env pull (or cp .env.example .env.development)"
70
+ };
71
+ }
72
+ const content = readFileSync(filePath, "utf-8");
73
+ const isEncrypted = content.includes("encrypted:");
74
+ return {
75
+ name: ".env.development",
76
+ status: "ok",
77
+ message: isEncrypted ? "Found (encrypted)" : "Found (plaintext)",
78
+ hint: isEncrypted ? void 0 : "Consider: runa env encrypt"
79
+ };
80
+ }
81
+ function checkEnvKeys(workDir) {
82
+ const filePath = resolve(workDir, ".env.keys");
83
+ if (!existsSync(filePath)) {
84
+ const devEnv = resolve(workDir, ".env.development");
85
+ if (existsSync(devEnv)) {
86
+ const content = readFileSync(devEnv, "utf-8");
87
+ if (content.includes("encrypted:")) {
88
+ return {
89
+ name: ".env.keys",
90
+ status: "error",
91
+ message: "Missing (required for encrypted .env files)",
92
+ hint: "Run: runa env pull (extracts keys from Vercel)"
93
+ };
94
+ }
95
+ }
96
+ return {
97
+ name: ".env.keys",
98
+ status: "info",
99
+ message: "Not found (optional if .env files are plaintext)"
100
+ };
101
+ }
102
+ return {
103
+ name: ".env.keys",
104
+ status: "ok",
105
+ message: "Found"
106
+ };
107
+ }
108
+ async function checkSupabaseLocal() {
109
+ try {
110
+ await execa("supabase", ["--version"], { stdio: "pipe" });
111
+ const result = await execa("supabase", ["status"], {
112
+ stdio: "pipe",
113
+ reject: false
114
+ });
115
+ if (result.exitCode === 0) {
116
+ const output3 = result.stdout;
117
+ const dbMatch = output3.match(/DB URL:\s+postgresql:\/\/.*:(\d+)/);
118
+ const port = dbMatch ? dbMatch[1] : "54322";
119
+ return {
120
+ name: "Supabase local",
121
+ status: "ok",
122
+ message: `Running (port ${port})`
123
+ };
124
+ }
125
+ return {
126
+ name: "Supabase local",
127
+ status: "warning",
128
+ message: "Not running",
129
+ hint: "Run: pnpm db:start"
130
+ };
131
+ } catch {
132
+ return {
133
+ name: "Supabase local",
134
+ status: "info",
135
+ message: "CLI not found",
136
+ hint: "Install: npm i -g supabase"
137
+ };
138
+ }
139
+ }
140
+ function checkVercelLinked(workDir) {
141
+ const projectJson = resolve(workDir, ".vercel", "project.json");
142
+ if (!existsSync(projectJson)) {
143
+ return {
144
+ name: "Vercel project",
145
+ status: "info",
146
+ message: "Not linked",
147
+ hint: "Run: vercel link"
148
+ };
149
+ }
150
+ try {
151
+ const content = JSON.parse(readFileSync(projectJson, "utf-8"));
152
+ const projectId = content.projectId || "unknown";
153
+ return {
154
+ name: "Vercel project",
155
+ status: "ok",
156
+ message: `Linked (${projectId.substring(0, 12)}...)`
157
+ };
158
+ } catch {
159
+ return {
160
+ name: "Vercel project",
161
+ status: "warning",
162
+ message: "Invalid project.json",
163
+ hint: "Run: vercel link"
164
+ };
165
+ }
166
+ }
167
+ async function checkGitHubRepo() {
168
+ try {
169
+ const result = await execa("gh", ["repo", "view", "--json", "nameWithOwner"], {
170
+ stdio: "pipe",
171
+ reject: false
172
+ });
173
+ if (result.exitCode === 0) {
174
+ const data = JSON.parse(result.stdout);
175
+ return {
176
+ name: "GitHub repo",
177
+ status: "ok",
178
+ message: data.nameWithOwner,
179
+ hint: "Verify secrets: gh secret list"
180
+ };
181
+ }
182
+ return {
183
+ name: "GitHub repo",
184
+ status: "info",
185
+ message: "Not a GitHub repo or gh not authenticated",
186
+ hint: "Run: gh auth login"
187
+ };
188
+ } catch {
189
+ return {
190
+ name: "GitHub repo",
191
+ status: "info",
192
+ message: "gh CLI not found",
193
+ hint: "Install: brew install gh"
194
+ };
195
+ }
196
+ }
197
+ var STATUS_ICONS = {
198
+ ok: "\u2705",
199
+ error: "\u274C",
200
+ warning: "\u26A0\uFE0F",
201
+ info: "\u2139\uFE0F"
202
+ };
203
+ function logCheckResult(logger, check, verbose) {
204
+ const icon = STATUS_ICONS[check.status];
205
+ logger.info(`${icon} ${check.name.padEnd(20)} ${check.message}`);
206
+ if (verbose && check.hint) {
207
+ logger.info(` \u2514\u2500 ${check.hint}`);
208
+ }
209
+ }
210
+ function logCheckSection(logger, title, checks, verbose) {
211
+ logger.info(`\u2501\u2501\u2501 ${title} \u2501\u2501\u2501`);
212
+ for (const check of checks) {
213
+ logCheckResult(logger, check, verbose);
214
+ }
215
+ }
216
+ function logRemoteVerificationHints(logger) {
217
+ logger.info("");
218
+ logger.info("\u2501\u2501\u2501 Remote Verification (run manually) \u2501\u2501\u2501");
219
+ logger.info("\u2139\uFE0F GitHub Secrets: gh secret list");
220
+ logger.info("\u2139\uFE0F Vercel env vars: vercel env ls");
221
+ }
222
+ function logRequiredSecrets(logger) {
223
+ logger.info("");
224
+ logger.info("\u{1F4CB} Required GitHub Secrets for production:");
225
+ logger.info(" \u2022 GH_DATABASE_URL_ADMIN (Direct, port 5432)");
226
+ logger.info(" \u2022 GH_DATABASE_URL (Pooler, port 6543)");
227
+ logger.info("");
228
+ logger.info("\u{1F4CB} Required Vercel env vars:");
229
+ logger.info(" \u2022 NEXT_PUBLIC_SUPABASE_URL");
230
+ logger.info(" \u2022 NEXT_PUBLIC_SUPABASE_ANON_KEY");
231
+ logger.info(" \u2022 SUPABASE_SERVICE_ROLE_KEY");
232
+ logger.info(" \u2022 SUPABASE_PROJECT_REF");
233
+ logger.info(" \u2022 DATABASE_URL (Pooler, port 6543)");
234
+ }
235
+ async function runEnvCheckAction(options) {
236
+ const logger = createCLILogger("env:check");
237
+ const workDir = options.cwd || process.cwd();
238
+ const verbose = options.verbose ?? false;
239
+ logger.section("Environment Check");
240
+ const localChecks = [
241
+ checkEnvDevelopment(workDir),
242
+ checkEnvKeys(workDir),
243
+ await checkSupabaseLocal()
244
+ ];
245
+ logCheckSection(logger, "Local Development", localChecks, verbose);
246
+ const localReady = localChecks.every((c) => c.status !== "error");
247
+ let productionChecks = [];
248
+ if (!options.local) {
249
+ productionChecks = [checkVercelLinked(workDir), await checkGitHubRepo()];
250
+ logger.info("");
251
+ logCheckSection(logger, "Production Setup", productionChecks, verbose);
252
+ logRemoteVerificationHints(logger);
253
+ }
254
+ logger.info("");
255
+ if (localReady) {
256
+ logger.success("\u{1F4A1} Local development environment is ready!");
257
+ } else {
258
+ logger.warn("\u26A0\uFE0F Some issues found. See hints above.");
259
+ }
260
+ if (!options.local) {
261
+ logRequiredSecrets(logger);
262
+ }
263
+ emitJsonSuccess(checkCommand, EnvCheckOutputSchema, {
264
+ local: { ready: localReady, checks: localChecks },
265
+ production: { checks: productionChecks }
266
+ });
267
+ if (!localReady) {
268
+ process.exitCode = 1;
269
+ }
270
+ }
271
+ var checkCommand = new Command("check").description("Check environment configuration status").option("--local", "Check local development environment only").option("-v, --verbose", "Show hints for each check").option("--cwd <path>", "Working directory (default: current directory)").action(async (options) => {
272
+ await runEnvCheckAction(options);
273
+ });
274
+
275
+ // src/commands/env/commands/env-encrypt.ts
276
+ init_esm_shims();
277
+
278
+ // src/utils/dotenvx.ts
279
+ init_esm_shims();
280
+ async function getDotenvxCliPath() {
281
+ const pkgJsonUrl = import.meta.resolve("@dotenvx/dotenvx/package.json");
282
+ const pkgDir = dirname(fileURLToPath(pkgJsonUrl));
283
+ return join(pkgDir, "src", "cli", "dotenvx.js");
284
+ }
285
+ async function execDotenvx(args, options) {
286
+ const dotenvxPath = await getDotenvxCliPath();
287
+ return execa("node", [dotenvxPath, ...args], options);
288
+ }
289
+ async function isDotenvxAvailable() {
290
+ try {
291
+ await execDotenvx(["--version"], { stdio: "pipe" });
292
+ return true;
293
+ } catch {
294
+ return false;
295
+ }
296
+ }
297
+
298
+ // src/commands/env/commands/env-encrypt.ts
299
+ var EnvEncryptOutputSchema = z.object({
300
+ results: z.array(
301
+ z.object({
302
+ environment: z.enum(["development", "preview", "production"]),
303
+ file: z.string(),
304
+ success: z.boolean(),
305
+ alreadyEncrypted: z.boolean().optional(),
306
+ error: z.string().optional()
307
+ })
308
+ ),
309
+ keysFile: z.string().optional(),
310
+ totalEncrypted: z.number(),
311
+ totalFailed: z.number()
312
+ });
313
+ var VERCEL_ENVIRONMENTS = ["development", "preview", "production"];
314
+ function isAlreadyEncrypted(filePath) {
315
+ if (!existsSync(filePath)) return false;
316
+ const content = readFileSync(filePath, "utf-8");
317
+ const hasPublicKeyDeclaration = /^DOTENV_PUBLIC_KEY_\w+\s*=\s*["']?[A-Za-z0-9+/=]+/m.test(
318
+ content
319
+ );
320
+ if (!hasPublicKeyDeclaration) return false;
321
+ const hasEncryptedValue = /^[A-Z_][A-Z0-9_]*\s*=\s*["']?encrypted:/m.test(content);
322
+ return hasEncryptedValue;
323
+ }
324
+ async function encryptFile(workDir, environment, logger, options) {
325
+ const fileName = `.env.${environment}`;
326
+ const filePath = resolve(workDir, fileName);
327
+ if (!existsSync(filePath)) {
328
+ logger.warn(` \u23ED\uFE0F ${fileName} (not found)`);
329
+ return {
330
+ environment,
331
+ file: fileName,
332
+ success: false,
333
+ error: "File not found"
334
+ };
335
+ }
336
+ if (isAlreadyEncrypted(filePath)) {
337
+ logger.info(` \u23ED\uFE0F ${fileName} (already encrypted)`);
338
+ return {
339
+ environment,
340
+ file: fileName,
341
+ success: true,
342
+ alreadyEncrypted: true
343
+ };
344
+ }
345
+ logger.info(` \u{1F510} Encrypting ${fileName}...`);
346
+ try {
347
+ const args = ["encrypt", "-f", filePath];
348
+ if (options.stdout) {
349
+ args.push("--stdout");
350
+ }
351
+ const result = await execDotenvx(args, {
352
+ cwd: workDir,
353
+ stdio: "pipe"
354
+ });
355
+ if (options.stdout && typeof result.stdout === "string") {
356
+ logger.info(result.stdout);
357
+ }
358
+ logger.success(` \u2713 ${fileName}`);
359
+ return {
360
+ environment,
361
+ file: fileName,
362
+ success: true
363
+ };
364
+ } catch (error) {
365
+ const message = error instanceof Error ? error.message : "Unknown error";
366
+ logger.error(` \u2717 ${fileName}: ${message}`);
367
+ return {
368
+ environment,
369
+ file: fileName,
370
+ success: false,
371
+ error: message
372
+ };
373
+ }
374
+ }
375
+ async function runEnvEncryptAction(options) {
376
+ const logger = createCLILogger("env:encrypt");
377
+ const workDir = options.cwd ?? process.cwd();
378
+ logger.section("dotenvx Encryption (Vercel-aligned)");
379
+ logger.info(`Working directory: ${workDir}`);
380
+ if (!await isDotenvxAvailable()) {
381
+ throw new CLIError("dotenvx CLI not available", "ENV_ENCRYPT_DOTENVX_NOT_FOUND", [
382
+ "This is unexpected - dotenvx should be bundled with runa CLI.",
383
+ "Try reinstalling: pnpm install"
384
+ ]);
385
+ }
386
+ const environments = options.environment ? [options.environment] : VERCEL_ENVIRONMENTS;
387
+ logger.info(`Environments: ${environments.join(", ")}`);
388
+ logger.info("");
389
+ const results = [];
390
+ for (const env of environments) {
391
+ const result = await encryptFile(workDir, env, logger, { stdout: options.stdout });
392
+ results.push(result);
393
+ }
394
+ const totalEncrypted = results.filter((r) => r.success && !r.alreadyEncrypted).length;
395
+ const totalFailed = results.filter((r) => !r.success).length;
396
+ const keysFile = resolve(workDir, ".env.keys");
397
+ logger.info("");
398
+ if (totalFailed > 0 && totalFailed === results.length) {
399
+ logger.error("\u274C No files encrypted");
400
+ } else if (totalFailed > 0) {
401
+ logger.warn(`\u26A0\uFE0F Encrypted ${totalEncrypted}/${results.length} files`);
402
+ } else {
403
+ logger.success(`\u2705 Encrypted ${totalEncrypted} file(s)`);
404
+ }
405
+ if (existsSync(keysFile) && !options.stdout) {
406
+ logger.info("");
407
+ logger.info("Keys saved to .env.keys (gitignore).");
408
+ logger.info("");
409
+ logger.info("Vercel & GitHub Secrets:");
410
+ logger.info(" DOTENV_PRIVATE_KEY_DEVELOPMENT");
411
+ logger.info(" DOTENV_PRIVATE_KEY_PREVIEW");
412
+ logger.info(" DOTENV_PRIVATE_KEY_PRODUCTION");
413
+ }
414
+ emitJsonSuccess(encryptCommand, EnvEncryptOutputSchema, {
415
+ results,
416
+ keysFile: existsSync(keysFile) ? keysFile : void 0,
417
+ totalEncrypted,
418
+ totalFailed
419
+ });
420
+ if (totalFailed > 0 && totalFailed === results.length) {
421
+ process.exitCode = 1;
422
+ }
423
+ }
424
+ var encryptCommand = new Command("encrypt").description("Encrypt .env.{environment} files using dotenvx").option("-e, --environment <env>", "Specific environment (development, preview, production)").option("--stdout", "Output encrypted content to stdout").option("--cwd <path>", "Working directory (default: current directory)").action(async (options) => {
425
+ try {
426
+ await runEnvEncryptAction(options);
427
+ } catch (error) {
428
+ if (error instanceof CLIError) throw error;
429
+ throw new CLIError(
430
+ error instanceof Error ? error.message : "Encryption failed",
431
+ "ENV_ENCRYPT_FAILED",
432
+ [
433
+ "Check dotenvx is installed: dotenvx --version",
434
+ "Ensure .env.{environment} files exist",
435
+ "Try: runa env pull --all first"
436
+ ],
437
+ error instanceof Error ? error : void 0
438
+ );
439
+ }
440
+ });
441
+
442
+ // src/commands/env/commands/env-pull.ts
443
+ init_esm_shims();
444
+
445
+ // src/commands/env/commands/env-pull/service.ts
446
+ init_esm_shims();
447
+
448
+ // src/commands/env/commands/env-pull/auth.ts
449
+ init_esm_shims();
450
+ function resolveVercelAuth(workDir, options, logger) {
451
+ if (options.token && options.project) {
452
+ logger.info("Auth: Using --token and --project flags");
453
+ return {
454
+ method: "env",
455
+ token: options.token,
456
+ projectId: options.project,
457
+ orgId: options.org
458
+ };
459
+ }
460
+ const envToken = process.env.VERCEL_TOKEN;
461
+ const envProjectId = process.env.VERCEL_PROJECT_ID;
462
+ const envOrgId = process.env.VERCEL_ORG_ID;
463
+ if (envToken && envProjectId) {
464
+ logger.info("Auth: Using VERCEL_TOKEN and VERCEL_PROJECT_ID environment variables");
465
+ return {
466
+ method: "env",
467
+ token: envToken,
468
+ projectId: envProjectId,
469
+ orgId: envOrgId
470
+ };
471
+ }
472
+ const projectJson = resolve(workDir, ".vercel", "project.json");
473
+ if (existsSync(projectJson)) {
474
+ try {
475
+ const config = JSON.parse(readFileSync(projectJson, "utf-8"));
476
+ if (config.projectId) {
477
+ logger.info("Auth: Using .vercel/project.json (vercel link)");
478
+ return {
479
+ method: "file",
480
+ projectId: config.projectId,
481
+ orgId: config.orgId
482
+ };
483
+ }
484
+ } catch {
485
+ }
486
+ }
487
+ throw new CLIError("Vercel authentication not configured", "ENV_PULL_NOT_AUTHENTICATED", [
488
+ "Option A (AI-friendly): Set environment variables:",
489
+ " export VERCEL_TOKEN=<your_token>",
490
+ " export VERCEL_PROJECT_ID=<project_id>",
491
+ "",
492
+ "Option B (CLI flags): runa env pull --token xxx --project prj_xxx",
493
+ "",
494
+ "Option C (Interactive): vercel link"
495
+ ]);
496
+ }
497
+ function buildVercelCmdEnv(auth) {
498
+ const cmdEnv = { ...process.env };
499
+ if (auth.method === "env") {
500
+ if (auth.token) cmdEnv.VERCEL_TOKEN = auth.token;
501
+ if (auth.projectId) cmdEnv.VERCEL_PROJECT_ID = auth.projectId;
502
+ if (auth.orgId) cmdEnv.VERCEL_ORG_ID = auth.orgId;
503
+ }
504
+ return cmdEnv;
505
+ }
506
+ async function ensureVercelCli() {
507
+ try {
508
+ await execa("vercel", ["--version"], { stdio: "pipe" });
509
+ } catch {
510
+ throw new CLIError("Vercel CLI not found", "ENV_PULL_VERCEL_NOT_FOUND", [
511
+ "Install Vercel CLI: npm i -g vercel",
512
+ "Or: pnpm add -g vercel"
513
+ ]);
514
+ }
515
+ }
516
+ async function ensureDotenvxCli() {
517
+ if (!await isDotenvxAvailable()) {
518
+ throw new CLIError(
519
+ "dotenvx CLI not available (required for --encrypt)",
520
+ "ENV_PULL_DOTENVX_NOT_FOUND",
521
+ [
522
+ "This is unexpected - dotenvx should be bundled with runa CLI.",
523
+ "Try reinstalling: pnpm install"
524
+ ]
525
+ );
526
+ }
527
+ }
528
+
529
+ // src/commands/env/commands/env-pull/dotenv-files.ts
530
+ init_esm_shims();
531
+ init_local_supabase();
532
+
533
+ // src/commands/env/commands/env-pull/security.ts
534
+ init_esm_shims();
535
+ var ERROR_MESSAGES = {
536
+ INVALID_PATH: "Invalid working directory path",
537
+ PATH_TRAVERSAL: "Working directory path validation failed"
538
+ };
539
+ function sanitizeErrorMessage(message) {
540
+ if (!message || typeof message !== "string") {
541
+ return "Unknown error";
542
+ }
543
+ let sanitized = message;
544
+ sanitized = sanitized.replace(
545
+ /\b(vercel_|vc_|VERCEL_TOKEN[=:]\s*)[a-zA-Z0-9_-]{20,}/gi,
546
+ "$1[REDACTED]"
547
+ );
548
+ sanitized = sanitized.replace(/\b(Bearer\s+)[a-zA-Z0-9._-]{20,}/gi, "$1[REDACTED]");
549
+ sanitized = sanitized.replace(
550
+ /\b(token[=:]\s*["']?)[a-zA-Z0-9._-]{20,}(["']?)/gi,
551
+ "$1[REDACTED]$2"
552
+ );
553
+ sanitized = sanitized.replace(
554
+ /\b(api[_-]?key[=:]\s*["']?)[a-zA-Z0-9._-]{20,}(["']?)/gi,
555
+ "$1[REDACTED]$2"
556
+ );
557
+ sanitized = sanitized.replace(
558
+ /\b(Authorization[=:]\s*["']?)[a-zA-Z0-9\s._-]{20,}(["']?)/gi,
559
+ "$1[REDACTED]$2"
560
+ );
561
+ sanitized = sanitized.replace(/:\/\/[^@\s]+@/g, "://[CREDENTIALS]@");
562
+ return sanitized;
563
+ }
564
+ function containsPathTraversal(inputPath) {
565
+ const normalized = path.normalize(inputPath);
566
+ return normalized.includes("..") || inputPath.includes("\0");
567
+ }
568
+ function isPathWithinBase(filePath, baseDir) {
569
+ try {
570
+ const resolvedFile = existsSync(filePath) ? realpathSync(filePath) : path.resolve(filePath);
571
+ const resolvedBase = existsSync(baseDir) ? realpathSync(baseDir) : path.resolve(baseDir);
572
+ const normalizedFile = path.normalize(resolvedFile);
573
+ const normalizedBase = path.normalize(resolvedBase);
574
+ return normalizedFile === normalizedBase || normalizedFile.startsWith(normalizedBase + path.sep);
575
+ } catch {
576
+ return false;
577
+ }
578
+ }
579
+ function validateCustomWorkingDir(cwdPath, projectRoot) {
580
+ if (containsPathTraversal(cwdPath)) {
581
+ throw new CLIError(ERROR_MESSAGES.PATH_TRAVERSAL, "ENV_PULL_PATH_TRAVERSAL");
582
+ }
583
+ const absolutePath = path.isAbsolute(cwdPath) ? cwdPath : path.resolve(projectRoot, cwdPath);
584
+ if (!isPathWithinBase(absolutePath, projectRoot)) {
585
+ throw new CLIError(ERROR_MESSAGES.PATH_TRAVERSAL, "ENV_PULL_PATH_TRAVERSAL");
586
+ }
587
+ if (!existsSync(absolutePath)) {
588
+ throw new CLIError(ERROR_MESSAGES.INVALID_PATH, "ENV_PULL_CWD_NOT_FOUND");
589
+ }
590
+ return absolutePath;
591
+ }
592
+ function resolveWorkingDir(options, logger) {
593
+ const projectRoot = process.cwd();
594
+ if (options.app) {
595
+ logger.warn(
596
+ `--app is deprecated. Environment files are managed at the monorepo root only.
597
+ Turbo auto-propagates root .env.* files to all workspaces.
598
+ Writing to project root instead of apps/${options.app}/.`
599
+ );
600
+ }
601
+ if (options.cwd) {
602
+ return validateCustomWorkingDir(options.cwd, projectRoot);
603
+ }
604
+ return projectRoot;
605
+ }
606
+
607
+ // src/commands/env/commands/env-pull/dotenv-files.ts
608
+ var LOCAL_BOOTSTRAP_REQUIRED_KEYS = [
609
+ "LOCAL_SUPABASE_HOST",
610
+ "LOCAL_SUPABASE_API_PORT",
611
+ "LOCAL_SUPABASE_DB_PORT"
612
+ ];
613
+ function getMissingLocalBootstrapKeys(parsed) {
614
+ return LOCAL_BOOTSTRAP_REQUIRED_KEYS.filter((key) => !(key in parsed));
615
+ }
616
+ function getLocalBootstrapStatusFromParsed(parsed) {
617
+ const missing = getMissingLocalBootstrapKeys(parsed);
618
+ return {
619
+ status: missing.length === 0 ? "ok" : "partial",
620
+ missing
621
+ };
622
+ }
623
+ function missingLocalBootstrapReport() {
624
+ return {
625
+ status: "missing",
626
+ missing: [...LOCAL_BOOTSTRAP_REQUIRED_KEYS]
627
+ };
628
+ }
629
+ function formatEnvValue(value) {
630
+ if (value === void 0 || value === null) {
631
+ return "";
632
+ }
633
+ if (value === "") {
634
+ return "";
635
+ }
636
+ const needsQuotes = value.includes("\n") || value.includes('"') || value.includes("'") || value.includes(" ") || value.includes("#") || value.includes("$") || value.startsWith(" ") || value.endsWith(" ");
637
+ if (!needsQuotes) {
638
+ return value;
639
+ }
640
+ return `"${value.replace(/"/g, '\\"')}"`;
641
+ }
642
+ function getOutputPath(workDir, environment) {
643
+ return resolve(workDir, `.env.${environment}`);
644
+ }
645
+ function extractAndSavePrivateKeys(workDir, envFiles, logger) {
646
+ const privateKeys = {};
647
+ for (const filePath of envFiles) {
648
+ if (!existsSync(filePath)) continue;
649
+ const content = readFileSync(filePath, "utf-8");
650
+ const parsed = parse(content);
651
+ for (const [key, value] of Object.entries(parsed)) {
652
+ if (key.startsWith("DOTENV_PRIVATE_KEY_") && value) {
653
+ privateKeys[key] = value;
654
+ }
655
+ }
656
+ }
657
+ if (Object.keys(privateKeys).length === 0) {
658
+ logger.warn(" \u26A0\uFE0F No DOTENV_PRIVATE_KEY_* found in pulled files");
659
+ logger.warn(" Keys may need to be registered in Vercel first");
660
+ return false;
661
+ }
662
+ const keysContent = [
663
+ "#/-------------------[DOTENV_PRIVATE_KEYS]--------------------/",
664
+ "#/ private decryption keys. DO NOT commit to /",
665
+ "#/ source control (gitignore) /",
666
+ "#/----------------------------------------------------------/",
667
+ "",
668
+ ...Object.entries(privateKeys).map(([key, value]) => `${key}=${formatEnvValue(value)}`),
669
+ ""
670
+ ].join("\n");
671
+ const keysFile = resolve(workDir, ".env.keys");
672
+ writeFileSync(keysFile, keysContent, "utf-8");
673
+ chmodSync(keysFile, 384);
674
+ logger.success(` \u{1F511} Extracted ${Object.keys(privateKeys).length} keys \u2192 .env.keys`);
675
+ return true;
676
+ }
677
+ function removePrivateKeysFromEnvFile(filePath) {
678
+ if (!existsSync(filePath)) return;
679
+ const content = readFileSync(filePath, "utf-8");
680
+ const parsed = parse(content);
681
+ const filtered = Object.entries(parsed).filter(([key]) => !key.startsWith("DOTENV_PRIVATE_KEY_"));
682
+ const newContent = filtered.map(([key, value]) => `${key}=${formatEnvValue(value)}`).join("\n");
683
+ writeFileSync(filePath, `${newContent}
684
+ `, "utf-8");
685
+ }
686
+ function loadParsedEnvFileForLocalBootstrap(filePath, logger) {
687
+ if (!existsSync(filePath)) {
688
+ logger.warn(" \u26A0\uFE0F .env.development not found (was pull successful?)");
689
+ return null;
690
+ }
691
+ const content = readFileSync(filePath, "utf-8");
692
+ if (!content.trim()) {
693
+ logger.warn(" \u26A0\uFE0F .env.development is empty (no env vars in Vercel?)");
694
+ return null;
695
+ }
696
+ const parsed = parse(content);
697
+ if (Object.keys(parsed).length === 0) {
698
+ logger.warn(" \u26A0\uFE0F No valid env vars found in .env.development (check syntax)");
699
+ return null;
700
+ }
701
+ return parsed;
702
+ }
703
+ function hasSupabaseRelatedKeys(parsed) {
704
+ return Object.keys(parsed).some(
705
+ (key) => key.includes("SUPABASE") || key.includes("DATABASE_URL")
706
+ );
707
+ }
708
+ function applyLocalSupabaseReplacements(parsed, localEnvValues) {
709
+ const replacementKeys = Object.keys(localEnvValues);
710
+ const hasSupabaseContext = hasSupabaseRelatedKeys(parsed) || replacementKeys.some((key) => key in parsed);
711
+ const replacedKeys = [];
712
+ for (const key of replacementKeys) {
713
+ if (key in parsed || hasSupabaseContext) {
714
+ parsed[key] = localEnvValues[key];
715
+ replacedKeys.push(key);
716
+ }
717
+ }
718
+ return replacedKeys;
719
+ }
720
+ function logNoReplacementOutcome(logger, parsed) {
721
+ if (hasSupabaseRelatedKeys(parsed)) {
722
+ logger.warn(" \u26A0\uFE0F Found Supabase keys but none matched local replacement patterns");
723
+ return;
724
+ }
725
+ logger.info(" No Supabase variables found (may be expected for new projects)");
726
+ }
727
+ function renderEnvFileContent(parsed) {
728
+ return Object.entries(parsed).map(([key, value]) => `${key}=${formatEnvValue(value)}`).join("\n");
729
+ }
730
+ function logLocalReplacementSummary(logger, replacedKeys, localDescriptions, ensureRequired, dryRun) {
731
+ const prefix = dryRun ? "Would replace" : "Replaced";
732
+ const method = dryRun ? logger.info.bind(logger) : logger.success.bind(logger);
733
+ method(` \u{1F3E0} ${prefix} ${replacedKeys.length} variables with local values:`);
734
+ for (const key of replacedKeys) {
735
+ const description = localDescriptions[key] || "localhost";
736
+ logger.info(` ${key} \u2192 ${description}`);
737
+ }
738
+ logger.info(
739
+ ` ${ensureRequired ? "\u2705" : "\u26A0\uFE0F"} Local bootstrap keys ready: ${ensureRequired ? "yes" : "partial"}`
740
+ );
741
+ if (dryRun) {
742
+ logger.info(" (dry-run: no files modified)");
743
+ }
744
+ }
745
+ function replaceWithLocalValues(filePath, workDir, logger, dryRun = false) {
746
+ const parsed = loadParsedEnvFileForLocalBootstrap(filePath, logger);
747
+ if (!parsed) {
748
+ return missingLocalBootstrapReport();
749
+ }
750
+ const localEnvValues = getLocalSupabaseEnvValues(workDir);
751
+ const localDescriptions = getLocalValueDescriptions(workDir);
752
+ const replacedKeys = applyLocalSupabaseReplacements(parsed, localEnvValues);
753
+ if (replacedKeys.length === 0) {
754
+ logNoReplacementOutcome(logger, parsed);
755
+ return getLocalBootstrapStatusFromParsed(parsed);
756
+ }
757
+ const bootstrapStatus = getLocalBootstrapStatusFromParsed(parsed);
758
+ if (bootstrapStatus.missing.length > 0) {
759
+ logger.warn(
760
+ ` \u26A0\uFE0F Some local bootstrap keys were not updated: ${bootstrapStatus.missing.join(", ")}`
761
+ );
762
+ }
763
+ const ensureRequired = bootstrapStatus.status === "ok";
764
+ const newContent = renderEnvFileContent(parsed);
765
+ if (dryRun) {
766
+ logLocalReplacementSummary(logger, replacedKeys, localDescriptions, ensureRequired, true);
767
+ return bootstrapStatus;
768
+ }
769
+ writeFileSync(filePath, `${newContent}
770
+ `, "utf-8");
771
+ logLocalReplacementSummary(logger, replacedKeys, localDescriptions, ensureRequired, false);
772
+ return bootstrapStatus;
773
+ }
774
+ async function encryptFile2(workDir, filePath, logger) {
775
+ if (!existsSync(filePath)) {
776
+ return false;
777
+ }
778
+ try {
779
+ const keysFile = resolve(workDir, ".env.keys");
780
+ const args = ["encrypt", "-f", filePath];
781
+ if (existsSync(keysFile)) {
782
+ args.push("-fk", keysFile);
783
+ }
784
+ await execDotenvx(args, {
785
+ cwd: workDir,
786
+ stdio: "pipe"
787
+ });
788
+ logger.success(` \u{1F510} Encrypted \u2192 ${basename(filePath)}`);
789
+ return true;
790
+ } catch (error) {
791
+ const message = error instanceof Error ? error.message : "Unknown error";
792
+ logger.warn(` \u26A0\uFE0F Encryption failed: ${sanitizeErrorMessage(message)}`);
793
+ return false;
794
+ }
795
+ }
796
+
797
+ // src/commands/env/commands/env-pull/service.ts
798
+ function mapVercelError(message, environment, outputPath, logger) {
799
+ const safeMessage = sanitizeErrorMessage(message);
800
+ if (message.includes("not linked")) {
801
+ logger.error(` \u2717 ${environment}: Project not linked to Vercel`);
802
+ return {
803
+ environment,
804
+ outputPath,
805
+ success: false,
806
+ error: "Project not linked. Set VERCEL_TOKEN + VERCEL_PROJECT_ID or run: vercel link"
807
+ };
808
+ }
809
+ if (message.includes("No Project")) {
810
+ logger.error(` \u2717 ${environment}: No Vercel project found`);
811
+ return {
812
+ environment,
813
+ outputPath,
814
+ success: false,
815
+ error: "No Vercel project. Set VERCEL_PROJECT_ID or run: vercel link"
816
+ };
817
+ }
818
+ if (message.includes("Invalid token") || message.includes("Unauthorized")) {
819
+ logger.error(` \u2717 ${environment}: Invalid Vercel token`);
820
+ return {
821
+ environment,
822
+ outputPath,
823
+ success: false,
824
+ error: "Invalid VERCEL_TOKEN. Check your token is valid."
825
+ };
826
+ }
827
+ logger.error(` \u2717 ${environment}: ${safeMessage}`);
828
+ return { environment, outputPath, success: false, error: safeMessage };
829
+ }
830
+ async function pullEnvironment(workDir, environment, auth, logger) {
831
+ const outputPath = getOutputPath(workDir, environment);
832
+ logger.info(`Pulling ${environment} environment...`);
833
+ logger.info(` Output: ${outputPath}`);
834
+ try {
835
+ const args = ["env", "pull", outputPath, "--environment", environment, "--yes"];
836
+ const cmdEnv = buildVercelCmdEnv(auth);
837
+ await execa("vercel", args, { cwd: workDir, stdio: "pipe", env: cmdEnv });
838
+ logger.success(` \u2713 ${environment} \u2192 ${basename(outputPath)}`);
839
+ return { environment, outputPath, success: true, encrypted: false };
840
+ } catch (error) {
841
+ const message = error instanceof Error ? error.message : "Unknown error";
842
+ return mapVercelError(message, environment, outputPath, logger);
843
+ }
844
+ }
845
+ async function pullAllEnvironments(workDir, environments, auth, logger) {
846
+ const results = [];
847
+ for (const environment of environments) {
848
+ results.push(await pullEnvironment(workDir, environment, auth, logger));
849
+ }
850
+ return results;
851
+ }
852
+ async function encryptPulledFiles(workDir, results, logger) {
853
+ const successfulPulls = results.filter((result) => result.success);
854
+ const envFiles = successfulPulls.map((result) => result.outputPath);
855
+ if (envFiles.length === 0) return;
856
+ logger.info("");
857
+ logger.info("Extracting encryption keys from Vercel...");
858
+ const keysExtracted = extractAndSavePrivateKeys(workDir, envFiles, logger);
859
+ if (!keysExtracted) return;
860
+ for (const filePath of envFiles) {
861
+ removePrivateKeysFromEnvFile(filePath);
862
+ }
863
+ logger.info("");
864
+ logger.info("Encrypting with Vercel-stored keys...");
865
+ for (const result of successfulPulls) {
866
+ result.encrypted = await encryptFile2(workDir, result.outputPath, logger);
867
+ }
868
+ }
869
+ async function syncVercelRootDirectory(workDir, logger) {
870
+ logger.info("");
871
+ logger.info("Checking Vercel Root Directory...");
872
+ const rootDirectory = await getVercelRootDirectory();
873
+ if (!rootDirectory) {
874
+ logger.info(" No Root Directory configured in Vercel project");
875
+ return {
876
+ detected: false,
877
+ rootDirectory: null,
878
+ configUpdated: false,
879
+ reason: "No Root Directory configured"
880
+ };
881
+ }
882
+ logger.info(` Detected: ${rootDirectory}`);
883
+ const syncResult = syncRunaConfigWithVercel(workDir, rootDirectory);
884
+ if (!syncResult) {
885
+ return {
886
+ detected: true,
887
+ rootDirectory,
888
+ configUpdated: false,
889
+ reason: "Sync not needed"
890
+ };
891
+ }
892
+ if (syncResult.updated) {
893
+ logger.success(` \u2705 Updated runa.config.ts: ${syncResult.field} = '${rootDirectory}'`);
894
+ return {
895
+ detected: true,
896
+ rootDirectory,
897
+ configUpdated: true,
898
+ reason: syncResult.reason
899
+ };
900
+ }
901
+ logger.info(` runa.config.ts: ${syncResult.reason}`);
902
+ return {
903
+ detected: true,
904
+ rootDirectory,
905
+ configUpdated: false,
906
+ reason: syncResult.reason
907
+ };
908
+ }
909
+ function printPullSummary(logger, results, encrypt) {
910
+ const totalPulled = results.filter((result) => result.success).length;
911
+ const totalFailed = results.filter((result) => !result.success).length;
912
+ const totalEncrypted = results.filter((result) => result.encrypted).length;
913
+ logger.info("");
914
+ if (totalFailed > 0) {
915
+ logger.warn(`\u26A0\uFE0F Pulled ${totalPulled}/${results.length} environments`);
916
+ for (const result of results.filter((item) => !item.success)) {
917
+ logger.warn(` ${result.environment}: ${result.error}`);
918
+ }
919
+ } else {
920
+ logger.success(`\u2705 Pulled ${totalPulled} environment(s)`);
921
+ if (totalEncrypted > 0) {
922
+ logger.success(`\u{1F510} Encrypted ${totalEncrypted} file(s)`);
923
+ }
924
+ }
925
+ logger.info("");
926
+ if (encrypt) {
927
+ logger.info("Files encrypted for LOCAL SECURITY (not for git).");
928
+ logger.info("Keys saved to .env.keys (gitignore).");
929
+ logger.info("Decrypt: dotenvx run -- pnpm dev");
930
+ } else {
931
+ logger.warn("\u26A0\uFE0F Files NOT encrypted. Use without --no-encrypt for security.");
932
+ }
933
+ return { totalPulled, totalFailed, totalEncrypted };
934
+ }
935
+ function validateEnvPullOptions(options) {
936
+ if (options.localhost && options.productionAll) {
937
+ throw new CLIError(
938
+ "--localhost and --production-all are mutually exclusive. Choose one.",
939
+ "INVALID_OPTIONS"
940
+ );
941
+ }
942
+ }
943
+ function resolveTargetEnvironments(options) {
944
+ return options.environment ? [options.environment] : ["development", "preview", "production"];
945
+ }
946
+ function logEnvPullMode(logger, workDir, options) {
947
+ const localhostMode = !options.productionAll;
948
+ logger.section("Vercel Environment Pull");
949
+ logger.info(`Working directory: ${workDir}`);
950
+ if (options.check) {
951
+ logger.info("Mode: DRY-RUN (preview only, no files will be written)");
952
+ }
953
+ logger.info(
954
+ `Development env: ${localhostMode ? "localhost (supabase start)" : "production URLs"}`
955
+ );
956
+ logger.info("");
957
+ }
958
+ function logTargetEnvironments(logger, environments, options) {
959
+ logger.info(`Environments: ${environments.join(", ")}`);
960
+ if (options.encrypt) {
961
+ logger.info("Encryption: enabled");
962
+ }
963
+ if (options.check) {
964
+ logger.info("Mode: dry-run (pull only, skip localhost replacement and encryption writes)");
965
+ }
966
+ logger.info("");
967
+ }
968
+ function shouldApplyLocalBootstrap(options) {
969
+ return !options.productionAll && (!options.environment || options.environment === "development");
970
+ }
971
+ function evaluateLocalBootstrap(options, results, workDir, logger) {
972
+ if (!shouldApplyLocalBootstrap(options)) {
973
+ return void 0;
974
+ }
975
+ const devResult = results.find(
976
+ (result) => result.environment === "development" && result.success
977
+ );
978
+ if (!devResult) {
979
+ logger.warn(" LOCAL_BOOTSTRAP: skipped (development env pull failed or missing)");
980
+ return missingLocalBootstrapReport();
981
+ }
982
+ logger.info("");
983
+ logger.info("Applying local Supabase values to .env.development...");
984
+ return replaceWithLocalValues(devResult.outputPath, workDir, logger, options.check);
985
+ }
986
+ async function runEncryptionStep(options, workDir, results, logger) {
987
+ if (!options.encrypt) {
988
+ return;
989
+ }
990
+ if (options.check) {
991
+ logger.info("");
992
+ logger.info("Encryption: skipped (dry-run mode)");
993
+ return;
994
+ }
995
+ await encryptPulledFiles(workDir, results, logger);
996
+ }
997
+ async function syncVercelConfigAfterPull(workDir, totalPulled, checkMode, logger) {
998
+ if (totalPulled === 0) {
999
+ return void 0;
1000
+ }
1001
+ if (checkMode) {
1002
+ logger.info("Config sync: skipped (dry-run mode)");
1003
+ return void 0;
1004
+ }
1005
+ return syncVercelRootDirectory(workDir, logger);
1006
+ }
1007
+ async function runEnvPullAction(options) {
1008
+ const logger = createCLILogger("env:pull");
1009
+ const workDir = resolveWorkingDir(options, logger);
1010
+ validateEnvPullOptions(options);
1011
+ logEnvPullMode(logger, workDir, options);
1012
+ await ensureVercelCli();
1013
+ if (options.encrypt) {
1014
+ await ensureDotenvxCli();
1015
+ }
1016
+ const auth = resolveVercelAuth(workDir, options, logger);
1017
+ const environments = resolveTargetEnvironments(options);
1018
+ logTargetEnvironments(logger, environments, options);
1019
+ const results = await pullAllEnvironments(workDir, environments, auth, logger);
1020
+ const localBootstrap = evaluateLocalBootstrap(options, results, workDir, logger);
1021
+ await runEncryptionStep(options, workDir, results, logger);
1022
+ const totalPulled = results.filter((result) => result.success).length;
1023
+ const vercelSyncResult = await syncVercelConfigAfterPull(
1024
+ workDir,
1025
+ totalPulled,
1026
+ options.check === true,
1027
+ logger
1028
+ );
1029
+ const { totalFailed, totalEncrypted } = printPullSummary(logger, results, options.encrypt);
1030
+ return {
1031
+ results,
1032
+ totalPulled,
1033
+ totalFailed,
1034
+ totalEncrypted,
1035
+ localBootstrap,
1036
+ vercelSync: vercelSyncResult
1037
+ };
1038
+ }
1039
+
1040
+ // src/commands/env/commands/env-pull/shared.ts
1041
+ init_esm_shims();
1042
+ var EnvPullOutputSchema = z.object({
1043
+ results: z.array(
1044
+ z.object({
1045
+ environment: z.enum(["development", "preview", "production"]),
1046
+ outputPath: z.string(),
1047
+ success: z.boolean(),
1048
+ encrypted: z.boolean().optional(),
1049
+ error: z.string().optional()
1050
+ })
1051
+ ),
1052
+ totalPulled: z.number(),
1053
+ totalFailed: z.number(),
1054
+ totalEncrypted: z.number().optional(),
1055
+ localBootstrap: z.object({
1056
+ status: z.enum(["ok", "partial", "missing"]),
1057
+ missing: z.array(z.string())
1058
+ }).optional(),
1059
+ vercelSync: z.object({
1060
+ detected: z.boolean(),
1061
+ rootDirectory: z.string().nullable(),
1062
+ configUpdated: z.boolean(),
1063
+ reason: z.string().optional()
1064
+ }).optional()
1065
+ });
1066
+
1067
+ // src/commands/env/commands/env-pull.ts
1068
+ var pullCommand = new Command("pull").description("Pull all environments from Vercel and encrypt (Vercel = SSOT)").option(
1069
+ "-e, --environment <env>",
1070
+ "Pull single environment only (development, preview, production)"
1071
+ ).option("--app <name>", "App name for monorepo (e.g., dashboard)").option("--cwd <path>", "Working directory (default: current directory)").option("--no-encrypt", "Skip encryption (not recommended)").option(
1072
+ "--production-all",
1073
+ "Use production URLs for all environments (skip localhost replacement for development)"
1074
+ ).option("--localhost", "Use localhost for development (default behavior, for explicit intent)").option("--check", "Dry-run mode: show what would be changed without writing files").option("--token <token>", "Vercel token (AI-friendly, alternative to vercel link)").option("--project <id>", "Vercel project ID (required with --token)").option("--org <id>", "Vercel org/team ID (optional with --token)").action(async (options) => {
1075
+ try {
1076
+ const result = await runEnvPullAction(options);
1077
+ emitJsonSuccess(pullCommand, EnvPullOutputSchema, {
1078
+ results: result.results,
1079
+ totalPulled: result.totalPulled,
1080
+ totalFailed: result.totalFailed,
1081
+ totalEncrypted: options.encrypt ? result.totalEncrypted : void 0,
1082
+ localBootstrap: result.localBootstrap,
1083
+ vercelSync: result.vercelSync
1084
+ });
1085
+ if (result.totalFailed > 0) {
1086
+ process.exitCode = 1;
1087
+ }
1088
+ } catch (error) {
1089
+ if (error instanceof CLIError) throw error;
1090
+ const rawMessage = error instanceof Error ? error.message : "Environment pull failed";
1091
+ throw new CLIError(sanitizeErrorMessage(rawMessage), "ENV_PULL_FAILED", [
1092
+ "Check Vercel CLI is installed: vercel --version",
1093
+ "Ensure project is linked: vercel link",
1094
+ "Check network connectivity"
1095
+ ]);
1096
+ }
1097
+ });
1098
+
1099
+ // src/commands/env/commands/env-setup.ts
1100
+ init_esm_shims();
1101
+
1102
+ // src/commands/env/commands/setup/action.ts
1103
+ init_esm_shims();
1104
+
1105
+ // src/commands/env/commands/setup/auth.ts
1106
+ init_esm_shims();
1107
+ async function checkSupabaseCliAuth() {
1108
+ try {
1109
+ const result = await execa("supabase", ["projects", "list"], {
1110
+ reject: false,
1111
+ timeout: 1e4
1112
+ });
1113
+ if (result.exitCode !== 0) {
1114
+ const stderr = result.stderr || "";
1115
+ if (stderr.includes("not logged in") || stderr.includes("login")) {
1116
+ return { authenticated: false, error: "Not logged in" };
1117
+ }
1118
+ if (stderr.includes("token") || stderr.includes("expired")) {
1119
+ return { authenticated: false, error: "Token invalid or expired" };
1120
+ }
1121
+ return { authenticated: false, error: stderr || "Authentication failed" };
1122
+ }
1123
+ const linkPath = join(process.cwd(), "supabase", ".temp", "project-ref");
1124
+ let linkedProject;
1125
+ if (existsSync(linkPath)) {
1126
+ linkedProject = readFileSync(linkPath, "utf-8").trim();
1127
+ }
1128
+ return { authenticated: true, linkedProject };
1129
+ } catch (error) {
1130
+ const message = error instanceof Error ? error.message : "Unknown error";
1131
+ if (message.includes("ENOENT")) {
1132
+ return { authenticated: false, error: "Supabase CLI not installed" };
1133
+ }
1134
+ return { authenticated: false, error: "Failed to verify authentication" };
1135
+ }
1136
+ }
1137
+ async function checkAuth(logger) {
1138
+ logger.info("Checking authentication status...");
1139
+ const status = {
1140
+ vercel: { authenticated: false },
1141
+ github: { authenticated: false },
1142
+ supabase: { authenticated: false }
1143
+ };
1144
+ try {
1145
+ const result = await execa("vercel", ["whoami"], { reject: false });
1146
+ if (result.exitCode === 0) {
1147
+ status.vercel = { authenticated: true, user: result.stdout.trim() };
1148
+ logger.success(` \u2713 Vercel: ${status.vercel.user}`);
1149
+ } else {
1150
+ status.vercel = { authenticated: false, error: "Not logged in" };
1151
+ logger.warn(" \u2717 Vercel: Not logged in");
1152
+ }
1153
+ } catch {
1154
+ status.vercel = { authenticated: false, error: "CLI not found" };
1155
+ logger.warn(" \u2717 Vercel: CLI not found");
1156
+ }
1157
+ try {
1158
+ const result = await execa("gh", ["auth", "status"], { reject: false });
1159
+ if (result.exitCode === 0) {
1160
+ const output3 = result.stderr || result.stdout;
1161
+ const userMatch = output3.match(/Logged in to .+ as (\S+)/);
1162
+ status.github = { authenticated: true, user: userMatch?.[1] };
1163
+ logger.success(` \u2713 GitHub: ${status.github.user || "authenticated"}`);
1164
+ } else {
1165
+ status.github = { authenticated: false, error: "Not logged in" };
1166
+ logger.warn(" \u2717 GitHub: Not logged in");
1167
+ }
1168
+ } catch {
1169
+ status.github = { authenticated: false, error: "CLI not found" };
1170
+ logger.warn(" \u2717 GitHub: CLI not found");
1171
+ }
1172
+ const supabaseAuth = await checkSupabaseCliAuth();
1173
+ if (supabaseAuth.authenticated) {
1174
+ status.supabase = { authenticated: true };
1175
+ if (supabaseAuth.linkedProject) {
1176
+ logger.success(` \u2713 Supabase: Logged in, linked to ${supabaseAuth.linkedProject}`);
1177
+ } else {
1178
+ logger.success(" \u2713 Supabase: Logged in");
1179
+ }
1180
+ } else {
1181
+ status.supabase = { authenticated: false, error: supabaseAuth.error };
1182
+ logger.warn(` \u2717 Supabase: ${supabaseAuth.error}`);
1183
+ }
1184
+ return status;
1185
+ }
1186
+
1187
+ // src/commands/env/commands/setup/file-export.ts
1188
+ init_esm_shims();
1189
+ var ENV_SETUP_TMP_FILE = ".env.setup-tmp";
1190
+ var cleanupRegistered = false;
1191
+ var SECURE_FILE_MODE = 384;
1192
+ function writeEnvSetupFile(envVars, drizzleAppPassword, drizzleServicePassword, logger) {
1193
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1194
+ const vercelVars = envVars.filter((e) => e.target.includes("vercel"));
1195
+ const githubVars = envVars.filter((e) => e.target.includes("github"));
1196
+ let content = `# runa env setup --export
1197
+ # Generated at: ${timestamp}
1198
+ # Review and manually register these values
1199
+ #
1200
+ # IMPORTANT: This file contains sensitive credentials!
1201
+ # - Do NOT commit this file to git (it's gitignored)
1202
+ # - Delete this file after registering the values
1203
+ #
1204
+ # Next Steps:
1205
+ # 1. Review the values below
1206
+ # 2. Register to Vercel: echo "<VALUE>" | vercel env add <KEY>
1207
+ # 3. Register to GitHub: gh secret set <KEY>
1208
+ # 4. Run RBAC setup: DRIZZLE_APP_PASSWORD="..." DRIZZLE_SERVICE_PASSWORD="..." pnpm db:setup-roles
1209
+ # 5. Delete this file: rm ${ENV_SETUP_TMP_FILE}
1210
+
1211
+ `;
1212
+ content += `# \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1213
+ # Vercel Environment Variables
1214
+ # Register: echo "<VALUE>" | vercel env add <KEY>
1215
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1216
+ `;
1217
+ for (const env of vercelVars) {
1218
+ content += `
1219
+ # ${env.description}
1220
+ `;
1221
+ content += `${env.key}=${env.value}
1222
+ `;
1223
+ }
1224
+ content += `
1225
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1226
+ # GitHub Secrets
1227
+ # Register: gh secret set <KEY>
1228
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1229
+ `;
1230
+ for (const env of githubVars) {
1231
+ content += `
1232
+ # ${env.description}
1233
+ `;
1234
+ content += `${env.key}=${env.value}
1235
+ `;
1236
+ }
1237
+ content += `
1238
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1239
+ # RBAC Setup (Run after registering the above)
1240
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1241
+
1242
+ # Copy this command to set up database roles:
1243
+ # DRIZZLE_APP_PASSWORD="${drizzleAppPassword}" \\
1244
+ # DRIZZLE_SERVICE_PASSWORD="${drizzleServicePassword}" \\
1245
+ # pnpm db:setup-roles
1246
+ `;
1247
+ const filePath = join(process.cwd(), ENV_SETUP_TMP_FILE);
1248
+ writeFileSync(filePath, content, { encoding: "utf-8", mode: SECURE_FILE_MODE });
1249
+ try {
1250
+ chmodSync(filePath, SECURE_FILE_MODE);
1251
+ } catch {
1252
+ }
1253
+ if (!cleanupRegistered) {
1254
+ registerCleanup(cleanupEnvSetupFileInternal);
1255
+ cleanupRegistered = true;
1256
+ }
1257
+ logger.success(`\u2713 Written to ${ENV_SETUP_TMP_FILE} (permissions: owner-only)`);
1258
+ }
1259
+ function cleanupEnvSetupFileInternal() {
1260
+ try {
1261
+ const filePath = join(process.cwd(), ENV_SETUP_TMP_FILE);
1262
+ if (existsSync(filePath)) {
1263
+ unlinkSync(filePath);
1264
+ }
1265
+ } catch {
1266
+ }
1267
+ }
1268
+
1269
+ // src/commands/env/commands/setup/helpers.ts
1270
+ init_esm_shims();
1271
+
1272
+ // src/commands/env/commands/setup/github-api.ts
1273
+ init_esm_shims();
1274
+ async function checkGitHubSecret(name, repoFullName) {
1275
+ try {
1276
+ const result = await execa("gh", ["secret", "list", "--json", "name", "--repo", repoFullName], {
1277
+ reject: false
1278
+ });
1279
+ if (result.exitCode !== 0) return false;
1280
+ const secrets = JSON.parse(result.stdout);
1281
+ return secrets.some((s) => s.name === name);
1282
+ } catch {
1283
+ return false;
1284
+ }
1285
+ }
1286
+ async function setGitHubSecret(name, value, overwrite, repoFullName, logger) {
1287
+ const exists = await checkGitHubSecret(name, repoFullName);
1288
+ if (exists && !overwrite) {
1289
+ return {
1290
+ key: name,
1291
+ target: "github",
1292
+ success: true,
1293
+ action: "skipped"
1294
+ };
1295
+ }
1296
+ try {
1297
+ await execa("gh", ["secret", "set", name, "--body", value, "--repo", repoFullName]);
1298
+ logger.success(` \u2713 GitHub: ${name} ${exists ? "updated" : "created"}`);
1299
+ return {
1300
+ key: name,
1301
+ target: "github",
1302
+ success: true,
1303
+ action: exists ? "updated" : "created"
1304
+ };
1305
+ } catch (error) {
1306
+ return {
1307
+ key: name,
1308
+ target: "github",
1309
+ success: false,
1310
+ action: "skipped",
1311
+ error: error instanceof Error ? error.message : "Unknown error"
1312
+ };
1313
+ }
1314
+ }
1315
+
1316
+ // src/commands/env/commands/setup/parsers.ts
1317
+ init_esm_shims();
1318
+ function generateDrizzleAppPassword() {
1319
+ return randomBytes(32).toString("base64url");
1320
+ }
1321
+ function generateDrizzleServicePassword() {
1322
+ return randomBytes(32).toString("base64url");
1323
+ }
1324
+ function generateDotenvxPrivateKey() {
1325
+ return randomBytes(32).toString("hex");
1326
+ }
1327
+ function parseVercelUrl(url) {
1328
+ const cleanUrl = url.trim().replace(/^https?:\/\//, "").replace(/\/$/, "");
1329
+ const match = cleanUrl.match(/^vercel\.com\/([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+)/);
1330
+ if (!match) return null;
1331
+ return { team: match[1], project: match[2] };
1332
+ }
1333
+ function isValidGitHubOwner(name) {
1334
+ if (!name || name.length > 39) return false;
1335
+ if (!/^[a-zA-Z0-9-]+$/.test(name)) return false;
1336
+ if (name.startsWith("-") || name.endsWith("-")) return false;
1337
+ return true;
1338
+ }
1339
+ function isValidGitHubRepo(name) {
1340
+ if (!name || name.length > 100) return false;
1341
+ if (!/^[a-zA-Z0-9._-]+$/.test(name)) return false;
1342
+ if (name.startsWith(".") && name !== ".github") return false;
1343
+ if (/\.\./.test(name)) return false;
1344
+ return true;
1345
+ }
1346
+ function parseGitHubUrl(url) {
1347
+ const cleanUrl = url.trim().replace(/^https?:\/\//, "").replace(/\.git$/, "").replace(/\/$/, "");
1348
+ const githubMatch = cleanUrl.match(/^github\.com\/([^/]+)\/([^/]+)/);
1349
+ if (githubMatch) {
1350
+ const owner = githubMatch[1];
1351
+ const repo = githubMatch[2];
1352
+ if (owner && repo && isValidGitHubOwner(owner) && isValidGitHubRepo(repo)) {
1353
+ return { owner, repo };
1354
+ }
1355
+ }
1356
+ const sshMatch = url.trim().match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
1357
+ if (sshMatch) {
1358
+ const owner = sshMatch[1];
1359
+ const repo = sshMatch[2];
1360
+ if (owner && repo && isValidGitHubOwner(owner) && isValidGitHubRepo(repo)) {
1361
+ return { owner, repo };
1362
+ }
1363
+ }
1364
+ const parts = cleanUrl.split("/");
1365
+ if (parts.length === 2) {
1366
+ const owner = parts[0];
1367
+ const repo = parts[1];
1368
+ if (owner && repo && isValidGitHubOwner(owner) && isValidGitHubRepo(repo)) {
1369
+ return { owner, repo };
1370
+ }
1371
+ }
1372
+ return null;
1373
+ }
1374
+ function parseSupabaseUrl(url) {
1375
+ const cleanUrl = url.trim().replace(/^https?:\/\//, "");
1376
+ const dashboardMatch = cleanUrl.match(/supabase\.com\/dashboard\/project\/([a-z0-9]+)/);
1377
+ if (dashboardMatch) {
1378
+ const projectRef = dashboardMatch[1];
1379
+ if (projectRef.length >= 8 && projectRef.length <= 30) {
1380
+ return projectRef;
1381
+ }
1382
+ }
1383
+ const appMatch = cleanUrl.match(/app\.supabase\.com\/project\/([a-z0-9]+)/);
1384
+ if (appMatch) {
1385
+ const projectRef = appMatch[1];
1386
+ if (projectRef.length >= 8 && projectRef.length <= 30) {
1387
+ return projectRef;
1388
+ }
1389
+ }
1390
+ return null;
1391
+ }
1392
+ function encodePasswordForUrl(password) {
1393
+ return encodeURIComponent(password);
1394
+ }
1395
+
1396
+ // src/commands/env/commands/setup/prompts.ts
1397
+ init_esm_shims();
1398
+ var CHAR_ENTER_CR = 13;
1399
+ var CHAR_ENTER_LF = 10;
1400
+ var CHAR_CTRL_C = 3;
1401
+ var CHAR_BACKSPACE_DEL = 127;
1402
+ var CHAR_BACKSPACE = 8;
1403
+ var CHAR_PRINTABLE_MIN = 32;
1404
+ function getCharAction(charCode) {
1405
+ if (charCode === CHAR_ENTER_CR || charCode === CHAR_ENTER_LF) return "enter";
1406
+ if (charCode === CHAR_CTRL_C) return "exit";
1407
+ if (charCode === CHAR_BACKSPACE_DEL || charCode === CHAR_BACKSPACE) return "backspace";
1408
+ if (charCode >= CHAR_PRINTABLE_MIN) return "char";
1409
+ return "ignore";
1410
+ }
1411
+ function handlePasswordChar(char, password) {
1412
+ const charCode = char.charCodeAt(0);
1413
+ const action = getCharAction(charCode);
1414
+ switch (action) {
1415
+ case "enter":
1416
+ case "exit":
1417
+ return { action, password };
1418
+ case "backspace":
1419
+ if (password.length > 0) {
1420
+ process.stdout.write("\b \b");
1421
+ return { action, password: password.slice(0, -1) };
1422
+ }
1423
+ return { action: "ignore", password };
1424
+ case "char":
1425
+ process.stdout.write("*");
1426
+ return { action, password: password + char };
1427
+ default:
1428
+ return { action: "ignore", password };
1429
+ }
1430
+ }
1431
+ async function promptForUrl(rl, prompt, existingValue) {
1432
+ const answer = await rl.question(`${prompt}: `);
1433
+ return answer.trim();
1434
+ }
1435
+ async function promptForPassword(prompt) {
1436
+ if (!process.stdin.isTTY) {
1437
+ const rl = readline.createInterface({ input: stdin, output: stdout });
1438
+ process.stdout.write(`${prompt}: `);
1439
+ const answer = await rl.question("");
1440
+ rl.close();
1441
+ return answer.trim();
1442
+ }
1443
+ return new Promise((resolve5) => {
1444
+ process.stdout.write(`${prompt}: `);
1445
+ let password = "";
1446
+ process.stdin.setRawMode(true);
1447
+ process.stdin.resume();
1448
+ process.stdin.setEncoding("utf8");
1449
+ const cleanup = () => {
1450
+ process.stdin.setRawMode(false);
1451
+ process.stdin.pause();
1452
+ process.stdin.removeListener("data", onData);
1453
+ };
1454
+ const onData = (data) => {
1455
+ for (const char of data) {
1456
+ const result = handlePasswordChar(char, password);
1457
+ password = result.password;
1458
+ if (result.action === "enter") {
1459
+ cleanup();
1460
+ process.stdout.write("\n");
1461
+ resolve5(password);
1462
+ return;
1463
+ }
1464
+ if (result.action === "exit") {
1465
+ cleanup();
1466
+ process.stdout.write("\n");
1467
+ process.exit(1);
1468
+ }
1469
+ }
1470
+ };
1471
+ process.stdin.on("data", onData);
1472
+ });
1473
+ }
1474
+ async function promptForConfirmation(rl, message, autoApprove) {
1475
+ const answer = await rl.question(`${message} (y/N): `);
1476
+ return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
1477
+ }
1478
+ async function collectServiceRoleKey(rl, options, isSecretKeyMasked, projectRef, logger) {
1479
+ if (options.serviceRoleKey) {
1480
+ return options.serviceRoleKey;
1481
+ }
1482
+ if (rl) {
1483
+ logger.info("");
1484
+ logger.warn("\u26A0\uFE0F The sb_secret_ key is masked in API response.");
1485
+ logger.info("Enter your secret key (Supabase Dashboard \u2192 Settings \u2192 API keys \u2192 secret keys):");
1486
+ logger.info("");
1487
+ logger.info(` https://supabase.com/dashboard/project/${projectRef}/settings/api-keys`);
1488
+ logger.info("");
1489
+ logger.info(" (Leave empty to use legacy JWT format instead)");
1490
+ rl.close();
1491
+ const key = await promptForPassword("> Secret Key");
1492
+ return key || void 0;
1493
+ }
1494
+ return void 0;
1495
+ }
1496
+ async function promptForProjectLink(rl, needsVercel, needsSupabase) {
1497
+ const services = [needsVercel ? "Vercel" : null, needsSupabase ? "Supabase" : null].filter(Boolean).join(" and ");
1498
+ console.log("");
1499
+ console.log(`Link ${services} to current directory?`);
1500
+ console.log("This enables other commands to work without re-entering URLs.");
1501
+ return promptForConfirmation(rl, `Link ${services}?`);
1502
+ }
1503
+ async function collectPostgresPassword(rl, options, projectRef, logger) {
1504
+ if (options.postgresPassword) {
1505
+ return options.postgresPassword;
1506
+ }
1507
+ if (rl) {
1508
+ logger.info("");
1509
+ logger.info("Enter your postgres password (Supabase Dashboard \u2192 Settings \u2192 Database):");
1510
+ logger.info("");
1511
+ logger.info(` https://supabase.com/dashboard/project/${projectRef}/settings/database`);
1512
+ logger.info("");
1513
+ logger.info(
1514
+ ' Note: If you forgot your password, click "Reset database password" on the page above.'
1515
+ );
1516
+ logger.info(" Only reset if necessary - existing connections will be invalidated.");
1517
+ rl.close();
1518
+ const password = await promptForPassword("> Postgres Password");
1519
+ if (!password) {
1520
+ throw new CLIError("Postgres password required", "PASSWORD_REQUIRED", [
1521
+ `Get it from: https://supabase.com/dashboard/project/${projectRef}/settings/database`
1522
+ ]);
1523
+ }
1524
+ return password;
1525
+ }
1526
+ throw new CLIError("Postgres password required", "PASSWORD_REQUIRED", [
1527
+ "In non-interactive mode, provide password via flag:",
1528
+ " runa env setup --postgres-password <password>"
1529
+ ]);
1530
+ }
1531
+
1532
+ // src/commands/env/commands/setup/supabase-api.ts
1533
+ init_esm_shims();
1534
+ function checkSupabaseLinked(cwd) {
1535
+ const projectRefPath = join(cwd, ".supabase", "project-ref");
1536
+ if (existsSync(projectRefPath)) {
1537
+ const projectRef = readFileSync(projectRefPath, "utf-8").trim();
1538
+ if (projectRef) return { linked: true, projectRef };
1539
+ }
1540
+ const tempProjectRefPath = join(cwd, "supabase", ".temp", "project-ref");
1541
+ if (existsSync(tempProjectRefPath)) {
1542
+ const projectRef = readFileSync(tempProjectRefPath, "utf-8").trim();
1543
+ if (projectRef) return { linked: true, projectRef };
1544
+ }
1545
+ return { linked: false };
1546
+ }
1547
+ async function linkSupabaseInCwd(projectRef, logger) {
1548
+ const result = await execa("supabase", ["link", "--project-ref", projectRef], {
1549
+ reject: false
1550
+ });
1551
+ if (result.exitCode !== 0) {
1552
+ throw new Error(`Failed to link Supabase project: ${result.stderr || result.stdout}`);
1553
+ }
1554
+ logger.success(` \u2713 Linked Supabase project: ${projectRef}`);
1555
+ }
1556
+ async function fetchSupabaseProjects() {
1557
+ const result = await execa("supabase", ["projects", "list", "-o", "json"], { reject: false });
1558
+ if (result.exitCode !== 0) {
1559
+ const errorOutput = result.stderr || result.stdout || "";
1560
+ throwSupabaseListError(errorOutput);
1561
+ }
1562
+ return JSON.parse(result.stdout);
1563
+ }
1564
+ function throwSupabaseListError(errorOutput) {
1565
+ const isAuthError = errorOutput.includes("not logged in") || errorOutput.includes("unauthorized");
1566
+ const isAccessError = errorOutput.includes("access") || errorOutput.includes("permission") || errorOutput.includes("forbidden");
1567
+ if (isAuthError) {
1568
+ throw new Error("Not logged in to Supabase. Run: supabase login");
1569
+ }
1570
+ if (isAccessError) {
1571
+ throw new Error(
1572
+ `Cannot access Supabase projects. Check:
1573
+ 1. You are logged in: supabase login
1574
+ 2. You have access to this organization in Supabase Dashboard`
1575
+ );
1576
+ }
1577
+ throw new Error(
1578
+ `Failed to list Supabase projects.
1579
+ Steps to fix:
1580
+ 1. supabase login (authenticate)
1581
+ 2. Verify access in Supabase Dashboard
1582
+ Error: ${errorOutput?.trim() || "No error message provided"}`
1583
+ );
1584
+ }
1585
+ async function fetchSupabaseApiKeys(projectRef) {
1586
+ const result = await execa(
1587
+ "supabase",
1588
+ ["projects", "api-keys", "--project-ref", projectRef, "-o", "json"],
1589
+ { reject: false }
1590
+ );
1591
+ if (result.exitCode !== 0) {
1592
+ throw new Error("Failed to fetch API keys");
1593
+ }
1594
+ return JSON.parse(result.stdout);
1595
+ }
1596
+ async function resolveServiceRoleKey(keys, providedKey, rl, projectRef, logger) {
1597
+ if (providedKey) {
1598
+ const format = providedKey.startsWith("sb_secret_") ? "secret (provided)" : "provided";
1599
+ return { key: providedKey, format };
1600
+ }
1601
+ const newSecretKey = keys.find((k) => k.type === "secret")?.api_key;
1602
+ const legacyKey = keys.find((k) => k.name === "service_role" && k.type === "legacy")?.api_key;
1603
+ const isSecretKeyMasked = newSecretKey?.includes("\xB7");
1604
+ if (isSecretKeyMasked) {
1605
+ const emptyOptions = {};
1606
+ const interactiveKey = await collectServiceRoleKey(rl, emptyOptions, true, projectRef, logger);
1607
+ if (interactiveKey) {
1608
+ const format = interactiveKey.startsWith("sb_secret_") ? "secret (input)" : "input";
1609
+ return { key: interactiveKey, format };
1610
+ }
1611
+ if (legacyKey) {
1612
+ logger.info(" Using legacy JWT format for service_role key.");
1613
+ return { key: legacyKey, format: "legacy" };
1614
+ }
1615
+ }
1616
+ if (newSecretKey && !isSecretKeyMasked) {
1617
+ return { key: newSecretKey, format: "secret" };
1618
+ }
1619
+ if (legacyKey) {
1620
+ return { key: legacyKey, format: "legacy" };
1621
+ }
1622
+ throw new Error("Could not find service_role API key");
1623
+ }
1624
+ function resolveAnonKey(keys) {
1625
+ const newKey = keys.find((k) => k.type === "publishable")?.api_key;
1626
+ const legacyKey = keys.find((k) => k.name === "anon" && k.type === "legacy")?.api_key;
1627
+ if (newKey) return { key: newKey, format: "publishable" };
1628
+ if (legacyKey) return { key: legacyKey, format: "legacy" };
1629
+ throw new Error("Could not find anon API key");
1630
+ }
1631
+ async function getSupabaseProjectInfo(projectRef, logger, rl, providedServiceRoleKey) {
1632
+ logger.info(`Fetching Supabase project info: ${projectRef}...`);
1633
+ try {
1634
+ const projects = await fetchSupabaseProjects();
1635
+ const project = projects.find((p) => p.id === projectRef);
1636
+ if (!project) {
1637
+ throw new Error(`Project ${projectRef} not found. Ensure you have access to this project.`);
1638
+ }
1639
+ const keys = await fetchSupabaseApiKeys(projectRef);
1640
+ const anon = resolveAnonKey(keys);
1641
+ const service = await resolveServiceRoleKey(
1642
+ keys,
1643
+ providedServiceRoleKey,
1644
+ rl,
1645
+ projectRef,
1646
+ logger
1647
+ );
1648
+ logger.success(` \u2713 Found project: ${project.name}`);
1649
+ logger.success(` \u2713 Retrieved anon key (${anon.format})`);
1650
+ logger.success(` \u2713 Retrieved service_role key (${service.format})`);
1651
+ return {
1652
+ projectRef,
1653
+ projectName: project.name,
1654
+ anonKey: anon.key,
1655
+ serviceRoleKey: service.key,
1656
+ supabaseUrl: `https://${projectRef}.supabase.co`
1657
+ };
1658
+ } catch (error) {
1659
+ throw new CLIError(
1660
+ `Failed to get Supabase project info: ${error instanceof Error ? error.message : "Unknown error"}`,
1661
+ "SUPABASE_PROJECT_ERROR",
1662
+ [
1663
+ "Ensure you are logged in: supabase login",
1664
+ "Link your project: supabase link --project-ref <your-project-ref>",
1665
+ "Ensure you have access to this project in Supabase Dashboard"
1666
+ ],
1667
+ error instanceof Error ? error : void 0
1668
+ );
1669
+ }
1670
+ }
1671
+
1672
+ // src/commands/env/commands/setup/vercel-api.ts
1673
+ init_esm_shims();
1674
+ function checkVercelLinked2(cwd) {
1675
+ const projectJsonPath = join(cwd, ".vercel", "project.json");
1676
+ if (!existsSync(projectJsonPath)) {
1677
+ return { linked: false };
1678
+ }
1679
+ try {
1680
+ const content = JSON.parse(readFileSync(projectJsonPath, "utf-8"));
1681
+ return { linked: true, projectId: content.projectId };
1682
+ } catch {
1683
+ return { linked: false };
1684
+ }
1685
+ }
1686
+ async function linkVercelInCwd(team, project, logger) {
1687
+ const args = ["link", "--project", project, "--yes"];
1688
+ if (team) {
1689
+ args.push("--scope", team);
1690
+ }
1691
+ const result = await execa("vercel", args, { reject: false });
1692
+ if (result.exitCode !== 0) {
1693
+ throw new Error(`Failed to link Vercel project: ${result.stderr || result.stdout}`);
1694
+ }
1695
+ logger.success(` \u2713 Linked Vercel project: ${project}`);
1696
+ }
1697
+ async function getVercelProjectInfo(team, project, logger) {
1698
+ logger.info(`Fetching Vercel project info: ${team}/${project}...`);
1699
+ const linkedDir = mkdtempSync(join(tmpdir(), "runa-vercel-"));
1700
+ try {
1701
+ const result = await execa("vercel", ["project", "ls", "--json", "--scope", team], {
1702
+ reject: false
1703
+ });
1704
+ if (result.exitCode !== 0) {
1705
+ const fallbackResult = await execa("vercel", ["project", "ls", "--json"], {
1706
+ reject: false
1707
+ });
1708
+ if (fallbackResult.exitCode !== 0) {
1709
+ rmSync(linkedDir, { recursive: true, force: true });
1710
+ throw new Error(`Failed to list Vercel projects (team: ${team})`);
1711
+ }
1712
+ const projectInfo2 = parseVercelProjectList(fallbackResult.stdout, team, project, logger);
1713
+ await linkVercelProject(projectInfo2.projectName, void 0, linkedDir, logger);
1714
+ return { ...projectInfo2, linkedDir };
1715
+ }
1716
+ const projectInfo = parseVercelProjectList(result.stdout, team, project, logger);
1717
+ await linkVercelProject(projectInfo.projectName, team, linkedDir, logger);
1718
+ return { ...projectInfo, linkedDir };
1719
+ } catch (error) {
1720
+ rmSync(linkedDir, { recursive: true, force: true });
1721
+ throw new CLIError(
1722
+ `Failed to get Vercel project info: ${error instanceof Error ? error.message : "Unknown error"}`,
1723
+ "VERCEL_PROJECT_ERROR",
1724
+ [
1725
+ "Ensure you have access to this project",
1726
+ `Verify team access: vercel teams ls`,
1727
+ `Try: vercel project ls --scope ${team}`,
1728
+ "Or: vercel login (to refresh authentication)"
1729
+ ]
1730
+ );
1731
+ }
1732
+ }
1733
+ async function linkVercelProject(projectName, teamSlug, linkedDir, logger) {
1734
+ const args = ["link", "--project", projectName, "--yes", "--cwd", linkedDir];
1735
+ if (teamSlug) {
1736
+ args.push("--scope", teamSlug);
1737
+ }
1738
+ const result = await execa("vercel", args, {
1739
+ reject: false
1740
+ });
1741
+ if (result.exitCode !== 0) {
1742
+ throw new Error(
1743
+ `Failed to link Vercel project: ${result.stderr || result.stdout || "Unknown error"}`
1744
+ );
1745
+ }
1746
+ logger.success(` \u2713 Linked project for env operations`);
1747
+ }
1748
+ function parseVercelProjectList(stdout, team, project, logger) {
1749
+ const lines = stdout.split("\n");
1750
+ let jsonString = "";
1751
+ let braceCount = 0;
1752
+ let inJson = false;
1753
+ for (const line of lines) {
1754
+ if (!inJson && line.trim().startsWith("{")) {
1755
+ inJson = true;
1756
+ jsonString = "";
1757
+ }
1758
+ if (inJson) {
1759
+ jsonString += `${line}
1760
+ `;
1761
+ braceCount += (line.match(/\{/g) || []).length;
1762
+ braceCount -= (line.match(/\}/g) || []).length;
1763
+ if (braceCount === 0) {
1764
+ break;
1765
+ }
1766
+ }
1767
+ }
1768
+ if (!jsonString.trim()) {
1769
+ throw new Error("No JSON found in Vercel output");
1770
+ }
1771
+ const parsed = JSON.parse(jsonString.trim());
1772
+ const foundProject = parsed.projects.find(
1773
+ (p) => p.name === project || p.name === `${team}/${project}` || p.name.toLowerCase() === project.toLowerCase()
1774
+ );
1775
+ if (!foundProject) {
1776
+ const availableProjects = parsed.projects.map((p) => p.name).slice(0, 5);
1777
+ throw new Error(
1778
+ `Project "${project}" not found in team "${team}". Available projects: ${availableProjects.join(", ")}${parsed.projects.length > 5 ? "..." : ""}`
1779
+ );
1780
+ }
1781
+ logger.success(` \u2713 Found project: ${foundProject.name} (${foundProject.id})`);
1782
+ return {
1783
+ projectId: foundProject.id,
1784
+ projectName: foundProject.name,
1785
+ orgId: foundProject.accountId,
1786
+ teamSlug: team
1787
+ // Store team for subsequent API calls
1788
+ };
1789
+ }
1790
+ async function getVercelEnvVar(linkedDir, key) {
1791
+ try {
1792
+ const args = ["env", "ls", "--cwd", linkedDir];
1793
+ const result = await execa("vercel", args, {
1794
+ reject: false
1795
+ });
1796
+ if (result.exitCode !== 0) return { exists: false };
1797
+ const lines = result.stdout.split("\n");
1798
+ for (const line of lines) {
1799
+ const trimmedLine = line.trim();
1800
+ if (trimmedLine.startsWith(key) && /^\s/.test(line.charAt(key.length + 1) || "")) {
1801
+ const parts = trimmedLine.split(/\s{2,}/);
1802
+ if (parts[0] === key) {
1803
+ return { exists: true };
1804
+ }
1805
+ }
1806
+ }
1807
+ return { exists: false };
1808
+ } catch {
1809
+ return { exists: false };
1810
+ }
1811
+ }
1812
+ async function setVercelEnvVar(linkedDir, key, value, overwrite, logger) {
1813
+ const existing = await getVercelEnvVar(linkedDir, key);
1814
+ if (existing.exists && !overwrite) {
1815
+ return {
1816
+ key,
1817
+ target: "vercel",
1818
+ success: true,
1819
+ action: "skipped"
1820
+ };
1821
+ }
1822
+ try {
1823
+ if (existing.exists) {
1824
+ const rmArgs = ["env", "rm", key, "--yes", "--cwd", linkedDir];
1825
+ await execa("vercel", rmArgs, {
1826
+ reject: false
1827
+ // Don't throw if variable doesn't exist in some environments
1828
+ });
1829
+ }
1830
+ const environments = ["development", "preview", "production"];
1831
+ for (const env of environments) {
1832
+ const addArgs = ["env", "add", key, env, "--cwd", linkedDir];
1833
+ await new Promise((resolve5, reject) => {
1834
+ const proc = spawn("vercel", addArgs, {
1835
+ stdio: ["pipe", "pipe", "pipe"]
1836
+ });
1837
+ let stderr = "";
1838
+ proc.stderr?.on("data", (data) => {
1839
+ stderr += data.toString();
1840
+ });
1841
+ proc.stdin?.write(`${value}
1842
+ `);
1843
+ proc.stdin?.end();
1844
+ proc.on("close", (code) => {
1845
+ if (code === 0) {
1846
+ resolve5();
1847
+ } else {
1848
+ reject(
1849
+ new Error(`vercel env add (${env}) failed with exit code ${code}: ${stderr.trim()}`)
1850
+ );
1851
+ }
1852
+ });
1853
+ proc.on("error", (err) => {
1854
+ reject(err);
1855
+ });
1856
+ });
1857
+ }
1858
+ logger.success(` \u2713 Vercel: ${key} ${existing.exists ? "updated" : "created"}`);
1859
+ return {
1860
+ key,
1861
+ target: "vercel",
1862
+ success: true,
1863
+ action: existing.exists ? "updated" : "created"
1864
+ };
1865
+ } catch (error) {
1866
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
1867
+ logger.warn(` \u2717 Vercel: ${key} failed - ${errorMsg}`);
1868
+ return {
1869
+ key,
1870
+ target: "vercel",
1871
+ success: false,
1872
+ action: "skipped",
1873
+ error: errorMsg
1874
+ };
1875
+ }
1876
+ }
1877
+
1878
+ // src/commands/env/commands/setup/helpers.ts
1879
+ function checkEarlyLinkStatus(logger) {
1880
+ const cwd = process.cwd();
1881
+ const vercelStatus = checkVercelLinked2(cwd);
1882
+ const supabaseStatus = checkSupabaseLinked(cwd);
1883
+ logger.info("Project Status:");
1884
+ if (vercelStatus.linked) {
1885
+ logger.info(` \u2713 Vercel: linked (${vercelStatus.projectId})`);
1886
+ } else {
1887
+ logger.info(" \u25CB Vercel: not linked (will link during setup)");
1888
+ }
1889
+ if (supabaseStatus.linked) {
1890
+ logger.info(` \u2713 Supabase: linked (${supabaseStatus.projectRef})`);
1891
+ } else {
1892
+ logger.info(" \u25CB Supabase: not linked (will link during setup)");
1893
+ }
1894
+ logger.info("");
1895
+ return {
1896
+ vercelLinked: vercelStatus.linked,
1897
+ vercelProjectId: vercelStatus.projectId,
1898
+ supabaseLinked: supabaseStatus.linked,
1899
+ supabaseProjectRef: supabaseStatus.projectRef
1900
+ };
1901
+ }
1902
+ async function validateAuthenticationWithLinkStatus(authStatus, options, linkStatus) {
1903
+ const linkHints = [];
1904
+ if (!linkStatus.vercelLinked) {
1905
+ linkHints.push("Note: Project linking will be prompted during setup if needed");
1906
+ }
1907
+ if (!linkStatus.supabaseLinked && linkStatus.vercelLinked) {
1908
+ linkHints.push("Note: Supabase linking will be prompted during setup if needed");
1909
+ }
1910
+ if (!authStatus.vercel.authenticated && !options.skipVercel) {
1911
+ throw new CLIError("Vercel authentication required", "AUTH_REQUIRED", [
1912
+ "Run: vercel login",
1913
+ ...linkHints.length > 0 ? ["", ...linkHints] : []
1914
+ ]);
1915
+ }
1916
+ if (!authStatus.github.authenticated && !options.skipGithub) {
1917
+ throw new CLIError("GitHub authentication required", "AUTH_REQUIRED", [
1918
+ "Run: gh auth login",
1919
+ ...linkHints.length > 0 ? ["", ...linkHints] : []
1920
+ ]);
1921
+ }
1922
+ if (!authStatus.supabase.authenticated) {
1923
+ throw new CLIError("Supabase authentication required", "AUTH_REQUIRED", [
1924
+ "Run: supabase login",
1925
+ ...linkHints.length > 0 ? ["", ...linkHints] : []
1926
+ ]);
1927
+ }
1928
+ }
1929
+ async function collectUrls(rl, options, logger) {
1930
+ let vercelUrl = options.vercelUrl;
1931
+ let githubUrl = options.githubUrl;
1932
+ let supabaseUrl = options.supabaseUrl;
1933
+ if (rl) {
1934
+ if (!githubUrl) {
1935
+ logger.info("Enter your GitHub repo URL (e.g., https://github.com/owner/repo):");
1936
+ githubUrl = await promptForUrl(rl, "> GitHub URL");
1937
+ }
1938
+ if (!vercelUrl) {
1939
+ logger.info("");
1940
+ logger.info("Enter your Vercel project URL (e.g., https://vercel.com/team/project):");
1941
+ vercelUrl = await promptForUrl(rl, "> Vercel URL");
1942
+ }
1943
+ if (!supabaseUrl) {
1944
+ logger.info("");
1945
+ logger.info(
1946
+ "Enter your Supabase project URL (e.g., https://supabase.com/dashboard/project/abcd1234):"
1947
+ );
1948
+ supabaseUrl = await promptForUrl(rl, "> Supabase URL");
1949
+ }
1950
+ }
1951
+ if (!vercelUrl || !githubUrl || !supabaseUrl) {
1952
+ throw new CLIError("URLs required", "URLS_REQUIRED", [
1953
+ "In non-interactive mode, provide URLs via flags:",
1954
+ " runa env setup --vercel-url <url> --github-url <url> --supabase-url <url>"
1955
+ ]);
1956
+ }
1957
+ return { vercelUrl, githubUrl, supabaseUrl };
1958
+ }
1959
+ function parseAndValidateUrls(vercelUrl, githubUrl, supabaseUrl) {
1960
+ const vercelParsed = parseVercelUrl(vercelUrl);
1961
+ if (!vercelParsed) {
1962
+ throw new CLIError("Invalid Vercel URL format", "INVALID_URL", [
1963
+ "Expected: https://vercel.com/team/project"
1964
+ ]);
1965
+ }
1966
+ const githubParsed = parseGitHubUrl(githubUrl);
1967
+ if (!githubParsed) {
1968
+ throw new CLIError("Invalid GitHub URL format", "INVALID_URL", [
1969
+ "Expected: https://github.com/owner/repo"
1970
+ ]);
1971
+ }
1972
+ const supabaseRef = parseSupabaseUrl(supabaseUrl);
1973
+ if (!supabaseRef) {
1974
+ throw new CLIError("Invalid Supabase URL format", "INVALID_URL", [
1975
+ "Expected: https://supabase.com/dashboard/project/<project_ref>"
1976
+ ]);
1977
+ }
1978
+ return { vercelParsed, githubParsed, supabaseRef };
1979
+ }
1980
+ function buildEnvVarList(supabaseProject, supabaseRef, postgresPassword, drizzleAppPassword, drizzleServicePassword, dotenvxKeys) {
1981
+ const encodedPostgresPassword = encodePasswordForUrl(postgresPassword);
1982
+ const encodedDrizzleAppPassword = encodePasswordForUrl(drizzleAppPassword);
1983
+ const encodedDrizzleServicePassword = encodePasswordForUrl(drizzleServicePassword);
1984
+ return [
1985
+ // Supabase credentials
1986
+ {
1987
+ key: "NEXT_PUBLIC_SUPABASE_URL",
1988
+ value: supabaseProject.supabaseUrl,
1989
+ target: ["vercel"],
1990
+ description: "Supabase project URL (public)"
1991
+ },
1992
+ {
1993
+ key: "NEXT_PUBLIC_SUPABASE_ANON_KEY",
1994
+ value: supabaseProject.anonKey,
1995
+ target: ["vercel"],
1996
+ description: "Supabase anon key (public)"
1997
+ },
1998
+ {
1999
+ key: "SUPABASE_SERVICE_ROLE_KEY",
2000
+ value: supabaseProject.serviceRoleKey,
2001
+ target: ["vercel"],
2002
+ description: "Supabase service role key (server-only)"
2003
+ },
2004
+ // Database URLs (Vercel)
2005
+ {
2006
+ key: "DATABASE_URL",
2007
+ value: `postgresql://drizzle_app:${encodedDrizzleAppPassword}@db.${supabaseRef}.supabase.co:5432/postgres`,
2008
+ target: ["vercel"],
2009
+ description: "App database URL (drizzle_app role, DML only)"
2010
+ },
2011
+ {
2012
+ key: "DATABASE_URL_ADMIN",
2013
+ value: `postgresql://postgres:${encodedPostgresPassword}@db.${supabaseRef}.supabase.co:5432/postgres`,
2014
+ target: ["vercel"],
2015
+ description: "Admin database URL (postgres role, DDL)"
2016
+ },
2017
+ {
2018
+ key: "DATABASE_URL_SERVICE",
2019
+ value: `postgresql://drizzle_service:${encodedDrizzleServicePassword}@db.${supabaseRef}.supabase.co:5432/postgres`,
2020
+ target: ["vercel"],
2021
+ description: "Service database URL (drizzle_service role, RLS bypassed - Webhooks/Background Jobs only)"
2022
+ },
2023
+ // Database URLs (GitHub CI)
2024
+ {
2025
+ key: "GH_DATABASE_URL_ADMIN",
2026
+ value: `postgresql://postgres:${encodedPostgresPassword}@db.${supabaseRef}.supabase.co:5432/postgres`,
2027
+ target: ["github"],
2028
+ description: "CI migrations URL (postgres role, DDL)"
2029
+ },
2030
+ {
2031
+ key: "GH_DATABASE_URL",
2032
+ value: `postgresql://drizzle_app:${encodedDrizzleAppPassword}@db.${supabaseRef}.supabase.co:5432/postgres`,
2033
+ target: ["github"],
2034
+ description: "CI verification URL (drizzle_app role, DML)"
2035
+ },
2036
+ // NOTE: VERCEL_PROJECT_ID is NOT registered to GitHub Secrets.
2037
+ // Vercel GitHub Integration handles deployments automatically (no CI secrets needed).
2038
+ // dotenvx encryption keys (Vercel only - for runa env pull)
2039
+ {
2040
+ key: "DOTENV_PRIVATE_KEY_DEVELOPMENT",
2041
+ value: dotenvxKeys.development,
2042
+ target: ["vercel"],
2043
+ description: "dotenvx key for .env.development"
2044
+ },
2045
+ {
2046
+ key: "DOTENV_PRIVATE_KEY_PREVIEW",
2047
+ value: dotenvxKeys.preview,
2048
+ target: ["vercel"],
2049
+ description: "dotenvx key for .env.preview"
2050
+ },
2051
+ {
2052
+ key: "DOTENV_PRIVATE_KEY_PRODUCTION",
2053
+ value: dotenvxKeys.production,
2054
+ target: ["vercel"],
2055
+ description: "dotenvx key for .env.production"
2056
+ }
2057
+ ];
2058
+ }
2059
+ function maskSensitiveValue(key, value) {
2060
+ if (key.includes("DATABASE_URL")) {
2061
+ return value.replace(/:([^:@]+)@/, ":***@");
2062
+ }
2063
+ if (key.includes("SERVICE_ROLE")) {
2064
+ if (value.length > 20) {
2065
+ return `${value.substring(0, 8)}...${value.substring(value.length - 8)}`;
2066
+ }
2067
+ }
2068
+ if (key.includes("ANON_KEY")) {
2069
+ if (value.length > 20) {
2070
+ return `${value.substring(0, 8)}...${value.substring(value.length - 8)}`;
2071
+ }
2072
+ }
2073
+ if (key.includes("DOTENV_PRIVATE_KEY")) {
2074
+ return `${value.substring(0, 8)}...`;
2075
+ }
2076
+ if (value.length > 60) {
2077
+ return `${value.substring(0, 57)}...`;
2078
+ }
2079
+ return value;
2080
+ }
2081
+ function displayEnvVarsSummary(envVars, overwrites, logger) {
2082
+ logger.info("");
2083
+ logger.section("Environment Variables to Register");
2084
+ logger.info("");
2085
+ for (const env of envVars) {
2086
+ const targets = env.target.join(", ");
2087
+ const displayValue = maskSensitiveValue(env.key, env.value);
2088
+ const isOverwrite = env.target.includes("vercel") && overwrites.vercel.includes(env.key) || env.target.includes("github") && overwrites.github.includes(env.key);
2089
+ const overwriteIndicator = isOverwrite ? " [OVERWRITE]" : " [NEW]";
2090
+ logger.info(` ${env.key}${overwriteIndicator}`);
2091
+ logger.info(` Target: ${targets}`);
2092
+ logger.info(` Value: ${displayValue}`);
2093
+ logger.info(` ${env.description}`);
2094
+ logger.info("");
2095
+ }
2096
+ }
2097
+ async function checkExistingOverwrites(envVars, vercelLinkedDir, githubRepoFullName, options) {
2098
+ const vercelVars = envVars.filter((e) => e.target.includes("vercel"));
2099
+ const githubVars = envVars.filter((e) => e.target.includes("github"));
2100
+ const vercelOverwrites = [];
2101
+ if (!options.skipVercel) {
2102
+ for (const env of vercelVars) {
2103
+ const existing = await getVercelEnvVar(vercelLinkedDir, env.key);
2104
+ if (existing.exists) {
2105
+ vercelOverwrites.push(env.key);
2106
+ }
2107
+ }
2108
+ }
2109
+ const githubOverwrites = [];
2110
+ if (!options.skipGithub && githubRepoFullName) {
2111
+ for (const env of githubVars) {
2112
+ const exists = await checkGitHubSecret(env.key, githubRepoFullName);
2113
+ if (exists) {
2114
+ githubOverwrites.push(env.key);
2115
+ }
2116
+ }
2117
+ }
2118
+ return { vercelOverwrites, githubOverwrites };
2119
+ }
2120
+ async function confirmOverwrites(rl, vercelOverwrites, githubOverwrites, vercelProjectName, githubRepoFullName, options, logger) {
2121
+ let vercelApproved = options.autoApprove ?? false;
2122
+ let githubApproved = options.autoApprove ?? false;
2123
+ if (rl && vercelOverwrites.length > 0 && !options.autoApprove) {
2124
+ logger.warn("");
2125
+ logger.warn(`\u26A0\uFE0F Vercel project: ${vercelProjectName}`);
2126
+ logger.warn(` The following env vars already exist: ${vercelOverwrites.join(", ")}`);
2127
+ vercelApproved = await promptForConfirmation(
2128
+ rl,
2129
+ `Overwrite existing env vars in Vercel project "${vercelProjectName}"?`);
2130
+ }
2131
+ if (rl && githubOverwrites.length > 0 && !options.autoApprove) {
2132
+ logger.warn("");
2133
+ logger.warn(`\u26A0\uFE0F GitHub repository: ${githubRepoFullName}`);
2134
+ logger.warn(` The following secrets already exist: ${githubOverwrites.join(", ")}`);
2135
+ githubApproved = await promptForConfirmation(
2136
+ rl,
2137
+ `Overwrite existing secrets in GitHub repo "${githubRepoFullName}"?`);
2138
+ }
2139
+ return { vercelApproved, githubApproved };
2140
+ }
2141
+ async function registerToVercel(envVars, linkedDir, projectDisplayName, overwrites, overwriteApproved, logger) {
2142
+ const results = [];
2143
+ const vercelVars = envVars.filter((e) => e.target.includes("vercel"));
2144
+ logger.info(`Vercel (${projectDisplayName}):`);
2145
+ for (const env of vercelVars) {
2146
+ const isOverwrite = overwrites.includes(env.key);
2147
+ const shouldOverwrite = isOverwrite ? overwriteApproved : true;
2148
+ const result = await setVercelEnvVar(linkedDir, env.key, env.value, shouldOverwrite, logger);
2149
+ results.push(result);
2150
+ }
2151
+ return results;
2152
+ }
2153
+ async function registerToGitHub(envVars, overwrites, overwriteApproved, repoFullName, logger) {
2154
+ const results = [];
2155
+ const githubVars = envVars.filter((e) => e.target.includes("github"));
2156
+ logger.info("");
2157
+ logger.info(`GitHub (${repoFullName}):`);
2158
+ for (const env of githubVars) {
2159
+ const isOverwrite = overwrites.includes(env.key);
2160
+ const shouldOverwrite = isOverwrite ? overwriteApproved : true;
2161
+ const result = await setGitHubSecret(env.key, env.value, shouldOverwrite, repoFullName, logger);
2162
+ results.push(result);
2163
+ }
2164
+ return results;
2165
+ }
2166
+ function printFinalSummary(results, _drizzleAppPassword, _drizzleServicePassword, logger) {
2167
+ logger.info("");
2168
+ logger.section("Summary");
2169
+ logger.info("");
2170
+ const created = results.filter((r) => r.action === "created").length;
2171
+ const updated = results.filter((r) => r.action === "updated").length;
2172
+ const skipped = results.filter((r) => r.action === "skipped").length;
2173
+ const failed = results.filter((r) => !r.success && r.action !== "skipped").length;
2174
+ logger.info(` Created: ${created}`);
2175
+ logger.info(` Updated: ${updated}`);
2176
+ logger.info(` Skipped: ${skipped}`);
2177
+ if (failed > 0) {
2178
+ logger.warn(` Failed: ${failed}`);
2179
+ }
2180
+ logger.info("");
2181
+ logger.section("Next Steps");
2182
+ logger.info("");
2183
+ logger.info(" 1. Apply schema and set role passwords:");
2184
+ logger.info(" runa db apply production");
2185
+ logger.info("");
2186
+ logger.info(" This will automatically:");
2187
+ logger.info(" - Create RBAC roles (15_rbac_roles.sql)");
2188
+ logger.info(" - Extract passwords from DATABASE_URL, DATABASE_URL_SERVICE");
2189
+ logger.info(" - Set passwords on drizzle_app, drizzle_service roles");
2190
+ logger.info(" - Apply schema changes");
2191
+ logger.info("");
2192
+ logger.info(" 2. Role Security Summary (3-Role Architecture):");
2193
+ logger.info(" drizzle_app - User API, Server Components (RLS enforced)");
2194
+ logger.info(" drizzle_service - Webhooks, Background Jobs (RLS bypassed)");
2195
+ logger.info(" postgres - Migrations only (DDL access)");
2196
+ logger.info("");
2197
+ logger.info(" 3. Deploy your application to Vercel");
2198
+ logger.info("");
2199
+ logger.success("\u2705 Environment setup complete!");
2200
+ }
2201
+ function reportLinkingStatus(vercelStatus, supabaseStatus, logger) {
2202
+ if (vercelStatus.linked) {
2203
+ logger.success(` \u2713 Vercel: Already linked (${vercelStatus.projectId})`);
2204
+ } else {
2205
+ logger.info(" \u25CB Vercel: Not linked");
2206
+ }
2207
+ if (supabaseStatus.linked) {
2208
+ logger.success(` \u2713 Supabase: Already linked (${supabaseStatus.projectRef})`);
2209
+ } else {
2210
+ logger.info(" \u25CB Supabase: Not linked");
2211
+ }
2212
+ }
2213
+ async function performLinking(needsVercel, needsSupabase, vercelParsed, supabaseRef, logger) {
2214
+ if (needsVercel) {
2215
+ await linkVercelInCwd(vercelParsed.team, vercelParsed.project, logger);
2216
+ }
2217
+ if (needsSupabase) {
2218
+ await linkSupabaseInCwd(supabaseRef, logger);
2219
+ }
2220
+ }
2221
+ async function checkAndLinkProjects(rl, vercelParsed, supabaseRef, options, logger) {
2222
+ const cwd = process.cwd();
2223
+ const vercelStatus = checkVercelLinked2(cwd);
2224
+ const supabaseStatus = checkSupabaseLinked(cwd);
2225
+ logger.info("");
2226
+ logger.section("Project Linking");
2227
+ reportLinkingStatus(vercelStatus, supabaseStatus, logger);
2228
+ if (vercelStatus.linked && supabaseStatus.linked) {
2229
+ return { vercelLinked: true, supabaseLinked: true };
2230
+ }
2231
+ const needsVercel = !vercelStatus.linked;
2232
+ const needsSupabase = !supabaseStatus.linked;
2233
+ const needsAnyLink = needsVercel || needsSupabase;
2234
+ if (!needsAnyLink) {
2235
+ return { vercelLinked: vercelStatus.linked, supabaseLinked: supabaseStatus.linked };
2236
+ }
2237
+ if (options.autoApprove) {
2238
+ await performLinking(needsVercel, needsSupabase, vercelParsed, supabaseRef, logger);
2239
+ return { vercelLinked: true, supabaseLinked: true };
2240
+ }
2241
+ if (rl) {
2242
+ const shouldLink = await promptForProjectLink(rl, needsVercel, needsSupabase);
2243
+ if (shouldLink) {
2244
+ await performLinking(needsVercel, needsSupabase, vercelParsed, supabaseRef, logger);
2245
+ return { vercelLinked: true, supabaseLinked: true };
2246
+ }
2247
+ }
2248
+ return { vercelLinked: vercelStatus.linked, supabaseLinked: supabaseStatus.linked };
2249
+ }
2250
+
2251
+ // src/commands/env/commands/setup/types.ts
2252
+ init_esm_shims();
2253
+ var EnvSetupOutputSchema = z.object({
2254
+ authStatus: z.object({
2255
+ vercel: z.object({
2256
+ authenticated: z.boolean(),
2257
+ user: z.string().optional(),
2258
+ error: z.string().optional()
2259
+ }),
2260
+ github: z.object({
2261
+ authenticated: z.boolean(),
2262
+ user: z.string().optional(),
2263
+ error: z.string().optional()
2264
+ }),
2265
+ supabase: z.object({
2266
+ authenticated: z.boolean(),
2267
+ error: z.string().optional()
2268
+ })
2269
+ }),
2270
+ vercelProject: z.object({
2271
+ projectId: z.string(),
2272
+ projectName: z.string(),
2273
+ orgId: z.string().optional()
2274
+ }).optional(),
2275
+ supabaseProject: z.object({
2276
+ projectRef: z.string(),
2277
+ projectName: z.string()
2278
+ }).optional(),
2279
+ registrationResults: z.array(
2280
+ z.object({
2281
+ key: z.string(),
2282
+ target: z.enum(["vercel", "github"]),
2283
+ success: z.boolean(),
2284
+ action: z.enum(["created", "updated", "skipped"]),
2285
+ error: z.string().optional()
2286
+ })
2287
+ ),
2288
+ success: z.boolean()
2289
+ });
2290
+
2291
+ // src/commands/env/commands/setup/action.ts
2292
+ function createReadlineIfInteractive(existing) {
2293
+ if (isNonInteractiveEnabled()) {
2294
+ return null;
2295
+ }
2296
+ try {
2297
+ existing?.close();
2298
+ } catch {
2299
+ }
2300
+ return readline.createInterface({ input: stdin, output: stdout });
2301
+ }
2302
+ function buildSuccessOutput(authStatus, vercelProject, supabaseProject, results) {
2303
+ return {
2304
+ authStatus,
2305
+ vercelProject,
2306
+ supabaseProject: {
2307
+ projectRef: supabaseProject.projectRef,
2308
+ projectName: supabaseProject.projectName
2309
+ },
2310
+ registrationResults: results,
2311
+ success: true
2312
+ };
2313
+ }
2314
+ function handleDryRunMode(setupCommand2, authStatus, vercelProject, supabaseProject, logger) {
2315
+ logger.info("Dry run mode - no changes made.");
2316
+ emitJsonSuccess(
2317
+ setupCommand2,
2318
+ EnvSetupOutputSchema,
2319
+ buildSuccessOutput(authStatus, vercelProject, supabaseProject, [])
2320
+ );
2321
+ }
2322
+ function handleExportMode(setupCommand2, authStatus, vercelProject, supabaseProject, envVars, drizzleAppPassword, drizzleServicePassword, logger) {
2323
+ logger.info("");
2324
+ logger.section("Exporting to File");
2325
+ logger.info("");
2326
+ writeEnvSetupFile(envVars, drizzleAppPassword, drizzleServicePassword, logger);
2327
+ logger.info("");
2328
+ logger.info("Review the file and manually register the values:");
2329
+ logger.info(` cat ${ENV_SETUP_TMP_FILE}`);
2330
+ logger.info("");
2331
+ logger.info("Then run without --export to auto-register:");
2332
+ logger.info(" runa env setup");
2333
+ logger.info("");
2334
+ logger.warn(`Remember to delete ${ENV_SETUP_TMP_FILE} after use!`);
2335
+ emitJsonSuccess(
2336
+ setupCommand2,
2337
+ EnvSetupOutputSchema,
2338
+ buildSuccessOutput(authStatus, vercelProject, supabaseProject, [])
2339
+ );
2340
+ }
2341
+ async function runEnvSetupAction(options, setupCommand2) {
2342
+ const logger = createCLILogger("env:setup");
2343
+ logger.section("Environment Setup");
2344
+ logger.info("This command will configure production environment variables.");
2345
+ logger.info("");
2346
+ const earlyLinkStatus = checkEarlyLinkStatus(logger);
2347
+ const authStatus = await checkAuth(logger);
2348
+ await validateAuthenticationWithLinkStatus(authStatus, options, earlyLinkStatus);
2349
+ logger.info("");
2350
+ let rl = createReadlineIfInteractive(null);
2351
+ let vercelLinkedDir = null;
2352
+ try {
2353
+ const { vercelUrl, githubUrl, supabaseUrl } = await collectUrls(rl, options, logger);
2354
+ const { vercelParsed, githubParsed, supabaseRef } = parseAndValidateUrls(
2355
+ vercelUrl,
2356
+ githubUrl,
2357
+ supabaseUrl
2358
+ );
2359
+ await checkAndLinkProjects(rl, vercelParsed, supabaseRef, options, logger);
2360
+ logger.info("");
2361
+ const vercelProject = await getVercelProjectInfo(
2362
+ vercelParsed.team,
2363
+ vercelParsed.project,
2364
+ logger
2365
+ );
2366
+ vercelLinkedDir = vercelProject.linkedDir;
2367
+ const supabaseProject = await getSupabaseProjectInfo(
2368
+ supabaseRef,
2369
+ logger,
2370
+ rl,
2371
+ options.serviceRoleKey
2372
+ );
2373
+ rl = createReadlineIfInteractive(rl);
2374
+ const postgresPassword = await collectPostgresPassword(rl, options, supabaseRef, logger);
2375
+ rl = createReadlineIfInteractive(rl);
2376
+ logger.info("");
2377
+ logger.info("Generating credentials...");
2378
+ const drizzleAppPassword = generateDrizzleAppPassword();
2379
+ logger.success("\u2713 Generated drizzle_app password (DML + RLS enforced)");
2380
+ const drizzleServicePassword = generateDrizzleServicePassword();
2381
+ logger.success("\u2713 Generated drizzle_service password (DML + RLS bypassed)");
2382
+ const dotenvxKeys = {
2383
+ development: generateDotenvxPrivateKey(),
2384
+ preview: generateDotenvxPrivateKey(),
2385
+ production: generateDotenvxPrivateKey()
2386
+ };
2387
+ logger.success("\u2713 Generated dotenvx encryption keys");
2388
+ logger.info(` GitHub repo: ${githubParsed.owner}/${githubParsed.repo}`);
2389
+ const envVars = buildEnvVarList(
2390
+ supabaseProject,
2391
+ supabaseRef,
2392
+ postgresPassword,
2393
+ drizzleAppPassword,
2394
+ drizzleServicePassword,
2395
+ dotenvxKeys
2396
+ );
2397
+ const githubRepoFullName = `${githubParsed.owner}/${githubParsed.repo}`;
2398
+ const { vercelOverwrites, githubOverwrites } = await checkExistingOverwrites(
2399
+ envVars,
2400
+ vercelProject.linkedDir,
2401
+ githubRepoFullName,
2402
+ options
2403
+ );
2404
+ displayEnvVarsSummary(envVars, { vercel: vercelOverwrites, github: githubOverwrites }, logger);
2405
+ if (options.dryRun) {
2406
+ handleDryRunMode(setupCommand2, authStatus, vercelProject, supabaseProject, logger);
2407
+ return;
2408
+ }
2409
+ if (options.export) {
2410
+ handleExportMode(
2411
+ setupCommand2,
2412
+ authStatus,
2413
+ vercelProject,
2414
+ supabaseProject,
2415
+ envVars,
2416
+ drizzleAppPassword,
2417
+ drizzleServicePassword,
2418
+ logger
2419
+ );
2420
+ return;
2421
+ }
2422
+ const { vercelApproved, githubApproved } = await confirmOverwrites(
2423
+ rl,
2424
+ vercelOverwrites,
2425
+ githubOverwrites,
2426
+ vercelProject.projectName,
2427
+ githubRepoFullName,
2428
+ options,
2429
+ logger
2430
+ );
2431
+ if (rl && !options.autoApprove) {
2432
+ logger.info("");
2433
+ const proceed = await promptForConfirmation(rl, "Proceed with registration?", false);
2434
+ if (!proceed) {
2435
+ logger.info("Cancelled.");
2436
+ return;
2437
+ }
2438
+ }
2439
+ logger.info("");
2440
+ logger.section("Registering Environment Variables");
2441
+ logger.info("");
2442
+ const results = [];
2443
+ if (!options.skipVercel) {
2444
+ const vercelDisplayName = vercelProject.teamSlug ? `${vercelProject.projectName} @ ${vercelProject.teamSlug}` : vercelProject.projectName;
2445
+ const vercelResults = await registerToVercel(
2446
+ envVars,
2447
+ vercelProject.linkedDir,
2448
+ vercelDisplayName,
2449
+ vercelOverwrites,
2450
+ vercelApproved,
2451
+ logger
2452
+ );
2453
+ results.push(...vercelResults);
2454
+ }
2455
+ if (!options.skipGithub) {
2456
+ const githubResults = await registerToGitHub(
2457
+ envVars,
2458
+ githubOverwrites,
2459
+ githubApproved,
2460
+ githubRepoFullName,
2461
+ logger
2462
+ );
2463
+ results.push(...githubResults);
2464
+ }
2465
+ printFinalSummary(results, drizzleAppPassword, drizzleServicePassword, logger);
2466
+ emitJsonSuccess(
2467
+ setupCommand2,
2468
+ EnvSetupOutputSchema,
2469
+ buildSuccessOutput(authStatus, vercelProject, supabaseProject, results)
2470
+ );
2471
+ } finally {
2472
+ rl?.close();
2473
+ if (vercelLinkedDir) {
2474
+ try {
2475
+ rmSync(vercelLinkedDir, { recursive: true, force: true });
2476
+ } catch {
2477
+ }
2478
+ }
2479
+ }
2480
+ }
2481
+
2482
+ // src/commands/env/commands/env-setup.ts
2483
+ var setupCommand = new Command("setup").description("Set up production environment variables (Vercel + GitHub + Supabase)").option("--vercel-url <url>", "Vercel project URL (e.g., https://vercel.com/team/project)").option("--github-url <url>", "GitHub repo URL (e.g., https://github.com/owner/repo)").option(
2484
+ "--supabase-url <url>",
2485
+ "Supabase dashboard URL (e.g., https://supabase.com/dashboard/project/abcd1234)"
2486
+ ).option("--postgres-password <password>", "Postgres password (from Supabase Dashboard)").option(
2487
+ "--service-role-key <key>",
2488
+ "Supabase service role key (sb_secret_* format, from Dashboard)"
2489
+ ).option("--auto-approve", "Skip confirmation prompts for overwrites").option("--skip-vercel", "Skip Vercel registration").option("--skip-github", "Skip GitHub registration").option("--dry-run", "Show what would be done without making changes").option("--export", "Write to .env.setup-tmp file instead of auto-registering").action(async (options) => {
2490
+ try {
2491
+ await runEnvSetupAction(options, setupCommand);
2492
+ } catch (error) {
2493
+ if (error instanceof CLIError) throw error;
2494
+ throw new CLIError(
2495
+ error instanceof Error ? error.message : "Environment setup failed",
2496
+ "ENV_SETUP_FAILED",
2497
+ [
2498
+ "Check authentication: vercel whoami, gh auth status, supabase login",
2499
+ "Provide URLs: --vercel-url, --github-url, --supabase-url",
2500
+ "Try: runa env setup --dry-run (to see what would be done)"
2501
+ ],
2502
+ error instanceof Error ? error : void 0
2503
+ );
2504
+ }
2505
+ });
2506
+
2507
+ // src/commands/env/commands/env-sync.ts
2508
+ init_esm_shims();
2509
+ function toRunaEnv(value) {
2510
+ if (value === "preview") return "preview";
2511
+ if (value === "main") return "main";
2512
+ if (value === "production") return "production";
2513
+ return "local";
2514
+ }
2515
+ function resolveDbUrl(env, options) {
2516
+ if (options?.loadEnv) {
2517
+ loadEnvFiles({ runaEnv: toRunaEnv(env), silent: true });
2518
+ }
2519
+ const url = tryResolveDatabaseUrl(toRunaEnv(env));
2520
+ if (!url) {
2521
+ throw new CLIError(`No database URL found for environment: ${env}`, "ENV_SYNC_URL_MISSING", [
2522
+ `Set ${env.toUpperCase()}_DATABASE_URL`,
2523
+ "Set DATABASE_URL_ADMIN for production"
2524
+ ]);
2525
+ }
2526
+ return url;
2527
+ }
2528
+ async function runEnvSyncAction(options) {
2529
+ const logger = createCLILogger("env:sync");
2530
+ const validEnvs = ["local", "preview", "production"];
2531
+ if (!validEnvs.includes(options.from)) {
2532
+ throw new CLIError(`Invalid source environment: ${options.from}`, "ENV_SYNC_INVALID_SOURCE", [
2533
+ "Valid environments: local, preview, production"
2534
+ ]);
2535
+ }
2536
+ if (!validEnvs.includes(options.to)) {
2537
+ throw new CLIError(`Invalid target environment: ${options.to}`, "ENV_SYNC_INVALID_TARGET", [
2538
+ "Valid environments: local, preview, production"
2539
+ ]);
2540
+ }
2541
+ if (options.from === options.to) {
2542
+ throw new CLIError("Source and target environments cannot be the same", "ENV_SYNC_SAME_ENV", [
2543
+ "Use different --from and --to values"
2544
+ ]);
2545
+ }
2546
+ if (options.to === "production") {
2547
+ throw new CLIError(
2548
+ "Syncing TO production is not allowed (safety restriction)",
2549
+ "ENV_SYNC_PROD_TARGET_BLOCKED",
2550
+ ["Only sync FROM production, never TO it"]
2551
+ );
2552
+ }
2553
+ const sourceUrl = resolveDbUrl(options.from, { loadEnv: true });
2554
+ const targetUrl = resolveDbUrl(options.to, { loadEnv: true });
2555
+ const schema = options.schema || "public";
2556
+ const sampleLimit = options.sample ? Number.parseInt(options.sample, 10) : void 0;
2557
+ const specificTables = options.tables?.split(",").map((t) => t.trim());
2558
+ const maskPii = options.maskPii !== false;
2559
+ logger.section("Environment Data Sync");
2560
+ logger.info(`Source: ${options.from} \u2192 Target: ${options.to}`);
2561
+ logger.info(`Schema: ${schema}`);
2562
+ logger.info(`PII Masking: ${maskPii ? "ENABLED" : "disabled"}`);
2563
+ if (sampleLimit) logger.info(`Sample limit: ${sampleLimit} rows per table`);
2564
+ if (specificTables) logger.info(`Tables: ${specificTables.join(", ")}`);
2565
+ if (options.dryRun) {
2566
+ logger.info("\n[DRY RUN] Would sync data with above settings");
2567
+ return;
2568
+ }
2569
+ if (!options.autoApprove) {
2570
+ throw new CLIError("Data sync requires explicit confirmation", "ENV_SYNC_CONFIRM_REQUIRED", [
2571
+ "Use --auto-approve to confirm",
2572
+ "Use --dry-run to preview",
2573
+ "\u26A0\uFE0F This will overwrite data in target environment"
2574
+ ]);
2575
+ }
2576
+ const result = await syncEnvironment({
2577
+ sourceUrl,
2578
+ targetUrl,
2579
+ schema,
2580
+ tables: specificTables,
2581
+ sampleLimit,
2582
+ maskPii,
2583
+ onProgress: (message) => logger.info(message)
2584
+ });
2585
+ logger.info(`
2586
+ Processed ${result.tablesProcessed} tables`);
2587
+ logger.info(` \u2713 Synced: ${result.tablesSynced}`);
2588
+ if (result.tablesFailed > 0) {
2589
+ logger.warn(` \u2717 Failed: ${result.tablesFailed}`);
2590
+ }
2591
+ for (const tableResult of result.results) {
2592
+ if (tableResult.status === "failed") {
2593
+ logger.warn(` \u2717 ${tableResult.table}: ${tableResult.error}`);
2594
+ }
2595
+ }
2596
+ logger.success("\n\u2705 Environment sync completed");
2597
+ emitJsonSuccess(syncCommand, EnvSyncOutputSchema, result);
2598
+ }
2599
+ var syncCommand = new Command("sync").description("Sync data between environments with PII masking").requiredOption("--from <env>", "Source environment (local, preview, production)").requiredOption("--to <env>", "Target environment (local, preview)").option("--sample <count>", "Limit rows per table (for testing)").option("--tables <list>", "Specific tables to sync (comma-separated)").option("--no-mask-pii", "Disable PII masking (dangerous)").option("--schema <name>", "Schema to sync (default: public)", "public").option("--auto-approve", "Skip confirmation prompts").option("--dry-run", "Show what would be synced without applying").action(async (options) => {
2600
+ try {
2601
+ await runEnvSyncAction(options);
2602
+ } catch (error) {
2603
+ if (error instanceof CLIError) throw error;
2604
+ throw new CLIError(
2605
+ error instanceof Error ? error.message : "Environment sync failed",
2606
+ "ENV_SYNC_FAILED",
2607
+ ["Check database connections", "Verify pg_dump/psql are installed", "Run with --dry-run"],
2608
+ error instanceof Error ? error : void 0
2609
+ );
2610
+ }
2611
+ });
2612
+
2613
+ // src/commands/env/index.ts
2614
+ var envCommand = new Command("env").description(
2615
+ "Environment management (check, pull, encrypt, sync)"
2616
+ );
2617
+ envCommand.addCommand(checkCommand);
2618
+ envCommand.addCommand(pullCommand);
2619
+ envCommand.addCommand(encryptCommand);
2620
+ envCommand.addCommand(syncCommand);
2621
+ envCommand.addCommand(setupCommand);
2622
+
2623
+ export { envCommand };