@supatype/cli 0.1.0-alpha.9 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/.turbo/turbo-test.log +285 -69
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/assets/supatype-logo-wordmark.ascii.txt +6 -0
  5. package/bin/dev-entry.ts +2 -1
  6. package/dist/app/framework.js +1 -3
  7. package/dist/app/framework.js.map +1 -1
  8. package/dist/app/proxy-dev-app.d.ts +14 -0
  9. package/dist/app/proxy-dev-app.d.ts.map +1 -1
  10. package/dist/app/proxy-dev-app.js +110 -6
  11. package/dist/app/proxy-dev-app.js.map +1 -1
  12. package/dist/app-config.d.ts +10 -0
  13. package/dist/app-config.d.ts.map +1 -1
  14. package/dist/app-config.js +72 -0
  15. package/dist/app-config.js.map +1 -1
  16. package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
  17. package/dist/binary-cache.d.ts +19 -7
  18. package/dist/binary-cache.d.ts.map +1 -1
  19. package/dist/binary-cache.js +92 -46
  20. package/dist/binary-cache.js.map +1 -1
  21. package/dist/cli.d.ts +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +17 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/commands/add.d.ts +3 -0
  26. package/dist/commands/add.d.ts.map +1 -0
  27. package/dist/commands/add.js +86 -0
  28. package/dist/commands/add.js.map +1 -0
  29. package/dist/commands/admin.d.ts +28 -1
  30. package/dist/commands/admin.d.ts.map +1 -1
  31. package/dist/commands/admin.js +297 -149
  32. package/dist/commands/admin.js.map +1 -1
  33. package/dist/commands/adopt.d.ts +3 -0
  34. package/dist/commands/adopt.d.ts.map +1 -0
  35. package/dist/commands/adopt.js +55 -0
  36. package/dist/commands/adopt.js.map +1 -0
  37. package/dist/commands/app.d.ts.map +1 -1
  38. package/dist/commands/app.js +20 -17
  39. package/dist/commands/app.js.map +1 -1
  40. package/dist/commands/cache.d.ts.map +1 -1
  41. package/dist/commands/cache.js +11 -10
  42. package/dist/commands/cache.js.map +1 -1
  43. package/dist/commands/cloud.d.ts +4 -9
  44. package/dist/commands/cloud.d.ts.map +1 -1
  45. package/dist/commands/cloud.js +75 -125
  46. package/dist/commands/cloud.js.map +1 -1
  47. package/dist/commands/db.d.ts.map +1 -1
  48. package/dist/commands/db.js +37 -58
  49. package/dist/commands/db.js.map +1 -1
  50. package/dist/commands/deploy.d.ts.map +1 -1
  51. package/dist/commands/deploy.js +140 -96
  52. package/dist/commands/deploy.js.map +1 -1
  53. package/dist/commands/dev.d.ts.map +1 -1
  54. package/dist/commands/dev.js +74 -39
  55. package/dist/commands/dev.js.map +1 -1
  56. package/dist/commands/diff.d.ts.map +1 -1
  57. package/dist/commands/diff.js +39 -39
  58. package/dist/commands/diff.js.map +1 -1
  59. package/dist/commands/doctor.d.ts +3 -0
  60. package/dist/commands/doctor.d.ts.map +1 -0
  61. package/dist/commands/doctor.js +78 -0
  62. package/dist/commands/doctor.js.map +1 -0
  63. package/dist/commands/engine.d.ts.map +1 -1
  64. package/dist/commands/engine.js +5 -4
  65. package/dist/commands/engine.js.map +1 -1
  66. package/dist/commands/functions.d.ts.map +1 -1
  67. package/dist/commands/functions.js +172 -119
  68. package/dist/commands/functions.js.map +1 -1
  69. package/dist/commands/generate.d.ts.map +1 -1
  70. package/dist/commands/generate.js +5 -4
  71. package/dist/commands/generate.js.map +1 -1
  72. package/dist/commands/init.d.ts +35 -1
  73. package/dist/commands/init.d.ts.map +1 -1
  74. package/dist/commands/init.js +883 -107
  75. package/dist/commands/init.js.map +1 -1
  76. package/dist/commands/introspect.d.ts +3 -0
  77. package/dist/commands/introspect.d.ts.map +1 -0
  78. package/dist/commands/introspect.js +35 -0
  79. package/dist/commands/introspect.js.map +1 -0
  80. package/dist/commands/keys.d.ts +15 -1
  81. package/dist/commands/keys.d.ts.map +1 -1
  82. package/dist/commands/keys.js +46 -10
  83. package/dist/commands/keys.js.map +1 -1
  84. package/dist/commands/link-helpers.d.ts +15 -0
  85. package/dist/commands/link-helpers.d.ts.map +1 -0
  86. package/dist/commands/link-helpers.js +225 -0
  87. package/dist/commands/link-helpers.js.map +1 -0
  88. package/dist/commands/logs.d.ts.map +1 -1
  89. package/dist/commands/logs.js +5 -4
  90. package/dist/commands/logs.js.map +1 -1
  91. package/dist/commands/migrate-from-v1.d.ts.map +1 -1
  92. package/dist/commands/migrate-from-v1.js +3 -2
  93. package/dist/commands/migrate-from-v1.js.map +1 -1
  94. package/dist/commands/migrate.d.ts.map +1 -1
  95. package/dist/commands/migrate.js +119 -26
  96. package/dist/commands/migrate.js.map +1 -1
  97. package/dist/commands/pg.d.ts.map +1 -1
  98. package/dist/commands/pg.js +11 -12
  99. package/dist/commands/pg.js.map +1 -1
  100. package/dist/commands/plugins.d.ts.map +1 -1
  101. package/dist/commands/plugins.js +55 -46
  102. package/dist/commands/plugins.js.map +1 -1
  103. package/dist/commands/pull.d.ts.map +1 -1
  104. package/dist/commands/pull.js +33 -5
  105. package/dist/commands/pull.js.map +1 -1
  106. package/dist/commands/push.d.ts.map +1 -1
  107. package/dist/commands/push.js +111 -138
  108. package/dist/commands/push.js.map +1 -1
  109. package/dist/commands/seed.d.ts.map +1 -1
  110. package/dist/commands/seed.js +4 -3
  111. package/dist/commands/seed.js.map +1 -1
  112. package/dist/commands/self-host.d.ts +2 -2
  113. package/dist/commands/self-host.d.ts.map +1 -1
  114. package/dist/commands/self-host.js +65 -50
  115. package/dist/commands/self-host.js.map +1 -1
  116. package/dist/commands/self-update.d.ts.map +1 -1
  117. package/dist/commands/self-update.js +3 -2
  118. package/dist/commands/self-update.js.map +1 -1
  119. package/dist/commands/status.d.ts +1 -1
  120. package/dist/commands/status.d.ts.map +1 -1
  121. package/dist/commands/status.js +95 -29
  122. package/dist/commands/status.js.map +1 -1
  123. package/dist/commands/types.d.ts.map +1 -1
  124. package/dist/commands/types.js +3 -2
  125. package/dist/commands/types.js.map +1 -1
  126. package/dist/commands/update.d.ts.map +1 -1
  127. package/dist/commands/update.js +54 -21
  128. package/dist/commands/update.js.map +1 -1
  129. package/dist/compose-rename.d.ts +10 -0
  130. package/dist/compose-rename.d.ts.map +1 -0
  131. package/dist/compose-rename.js +67 -0
  132. package/dist/compose-rename.js.map +1 -0
  133. package/dist/config.d.ts +2 -1
  134. package/dist/config.d.ts.map +1 -1
  135. package/dist/config.js.map +1 -1
  136. package/dist/dev-compose.d.ts +26 -0
  137. package/dist/dev-compose.d.ts.map +1 -1
  138. package/dist/dev-compose.js +357 -79
  139. package/dist/dev-compose.js.map +1 -1
  140. package/dist/dev-log-bus.d.ts +30 -0
  141. package/dist/dev-log-bus.d.ts.map +1 -0
  142. package/dist/dev-log-bus.js +87 -0
  143. package/dist/dev-log-bus.js.map +1 -0
  144. package/dist/dev-log-filter.d.ts +10 -0
  145. package/dist/dev-log-filter.d.ts.map +1 -0
  146. package/dist/dev-log-filter.js +36 -0
  147. package/dist/dev-log-filter.js.map +1 -0
  148. package/dist/dev-logo.d.ts +12 -0
  149. package/dist/dev-logo.d.ts.map +1 -0
  150. package/dist/dev-logo.js +56 -0
  151. package/dist/dev-logo.js.map +1 -0
  152. package/dist/dev-ports.d.ts +27 -0
  153. package/dist/dev-ports.d.ts.map +1 -0
  154. package/dist/dev-ports.js +171 -0
  155. package/dist/dev-ports.js.map +1 -0
  156. package/dist/dev-session-lock.d.ts +25 -0
  157. package/dist/dev-session-lock.d.ts.map +1 -0
  158. package/dist/dev-session-lock.js +81 -0
  159. package/dist/dev-session-lock.js.map +1 -0
  160. package/dist/dev-session.d.ts +26 -0
  161. package/dist/dev-session.d.ts.map +1 -0
  162. package/dist/dev-session.js +106 -0
  163. package/dist/dev-session.js.map +1 -0
  164. package/dist/dev-shutdown.d.ts +25 -0
  165. package/dist/dev-shutdown.d.ts.map +1 -0
  166. package/dist/dev-shutdown.js +114 -0
  167. package/dist/dev-shutdown.js.map +1 -0
  168. package/dist/dev-task-colors.d.ts +13 -0
  169. package/dist/dev-task-colors.d.ts.map +1 -0
  170. package/dist/dev-task-colors.js +43 -0
  171. package/dist/dev-task-colors.js.map +1 -0
  172. package/dist/dev-tui.d.ts +24 -0
  173. package/dist/dev-tui.d.ts.map +1 -0
  174. package/dist/dev-tui.js +188 -0
  175. package/dist/dev-tui.js.map +1 -0
  176. package/dist/diff-output.d.ts +5 -1
  177. package/dist/diff-output.d.ts.map +1 -1
  178. package/dist/diff-output.js +69 -0
  179. package/dist/diff-output.js.map +1 -1
  180. package/dist/docker-runtime.d.ts +30 -0
  181. package/dist/docker-runtime.d.ts.map +1 -0
  182. package/dist/docker-runtime.js +118 -0
  183. package/dist/docker-runtime.js.map +1 -0
  184. package/dist/engine-client.d.ts +10 -1
  185. package/dist/engine-client.d.ts.map +1 -1
  186. package/dist/engine-client.js +76 -17
  187. package/dist/engine-client.js.map +1 -1
  188. package/dist/engine-push-output.d.ts +17 -0
  189. package/dist/engine-push-output.d.ts.map +1 -0
  190. package/dist/engine-push-output.js +64 -0
  191. package/dist/engine-push-output.js.map +1 -0
  192. package/dist/ensure-binary.js +2 -2
  193. package/dist/ensure-binary.js.map +1 -1
  194. package/dist/env-file.d.ts +5 -0
  195. package/dist/env-file.d.ts.map +1 -0
  196. package/dist/env-file.js +33 -0
  197. package/dist/env-file.js.map +1 -0
  198. package/dist/gitignore.d.ts +8 -0
  199. package/dist/gitignore.d.ts.map +1 -0
  200. package/dist/gitignore.js +41 -0
  201. package/dist/gitignore.js.map +1 -0
  202. package/dist/kong-config.d.ts +9 -0
  203. package/dist/kong-config.d.ts.map +1 -1
  204. package/dist/kong-config.js +18 -1
  205. package/dist/kong-config.js.map +1 -1
  206. package/dist/link.d.ts +66 -0
  207. package/dist/link.d.ts.map +1 -0
  208. package/dist/link.js +160 -0
  209. package/dist/link.js.map +1 -0
  210. package/dist/process-manager.d.ts +8 -0
  211. package/dist/process-manager.d.ts.map +1 -1
  212. package/dist/process-manager.js +53 -9
  213. package/dist/process-manager.js.map +1 -1
  214. package/dist/project-config.d.ts +30 -3
  215. package/dist/project-config.d.ts.map +1 -1
  216. package/dist/project-config.js +37 -4
  217. package/dist/project-config.js.map +1 -1
  218. package/dist/prompts.d.ts +3 -0
  219. package/dist/prompts.d.ts.map +1 -0
  220. package/dist/prompts.js +3 -0
  221. package/dist/prompts.js.map +1 -0
  222. package/dist/pull-utils.d.ts +50 -14
  223. package/dist/pull-utils.d.ts.map +1 -1
  224. package/dist/pull-utils.js +152 -12
  225. package/dist/pull-utils.js.map +1 -1
  226. package/dist/resolve-target.d.ts +86 -0
  227. package/dist/resolve-target.d.ts.map +1 -0
  228. package/dist/resolve-target.js +291 -0
  229. package/dist/resolve-target.js.map +1 -0
  230. package/dist/restore-system-relation-targets.d.ts +3 -0
  231. package/dist/restore-system-relation-targets.d.ts.map +1 -0
  232. package/dist/restore-system-relation-targets.js +45 -0
  233. package/dist/restore-system-relation-targets.js.map +1 -0
  234. package/dist/runtime-routes.d.ts.map +1 -1
  235. package/dist/runtime-routes.js +7 -0
  236. package/dist/runtime-routes.js.map +1 -1
  237. package/dist/schema-ast-v2.d.ts +1 -1
  238. package/dist/schema-ast-v2.d.ts.map +1 -1
  239. package/dist/schema-ast-v2.js +2 -2
  240. package/dist/schema-ast-v2.js.map +1 -1
  241. package/dist/schema-sources.d.ts +40 -0
  242. package/dist/schema-sources.d.ts.map +1 -0
  243. package/dist/schema-sources.js +183 -0
  244. package/dist/schema-sources.js.map +1 -0
  245. package/dist/scripts/postinstall.js +5 -1
  246. package/dist/scripts/postinstall.js.map +1 -1
  247. package/dist/self-host-compose.d.ts +37 -1
  248. package/dist/self-host-compose.d.ts.map +1 -1
  249. package/dist/self-host-compose.js +234 -43
  250. package/dist/self-host-compose.js.map +1 -1
  251. package/dist/storage-provision.d.ts +4 -0
  252. package/dist/storage-provision.d.ts.map +1 -1
  253. package/dist/storage-provision.js +24 -2
  254. package/dist/storage-provision.js.map +1 -1
  255. package/dist/supatype-eval-1781522769253.d.mts +2 -0
  256. package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
  257. package/dist/supatype-eval-1781522769253.mjs +3 -0
  258. package/dist/supatype-eval-1781522769253.mjs.map +1 -0
  259. package/dist/systemd.js +2 -2
  260. package/dist/systemd.js.map +1 -1
  261. package/dist/target-client.d.ts +10 -0
  262. package/dist/target-client.d.ts.map +1 -0
  263. package/dist/target-client.js +22 -0
  264. package/dist/target-client.js.map +1 -0
  265. package/dist/type-extractor.d.ts +11 -0
  266. package/dist/type-extractor.d.ts.map +1 -1
  267. package/dist/type-extractor.js +95 -8
  268. package/dist/type-extractor.js.map +1 -1
  269. package/dist/ui/brand.d.ts +9 -0
  270. package/dist/ui/brand.d.ts.map +1 -0
  271. package/dist/ui/brand.js +11 -0
  272. package/dist/ui/brand.js.map +1 -0
  273. package/dist/ui/confirm.d.ts +12 -0
  274. package/dist/ui/confirm.d.ts.map +1 -0
  275. package/dist/ui/confirm.js +28 -0
  276. package/dist/ui/confirm.js.map +1 -0
  277. package/dist/ui/fatal.d.ts +10 -0
  278. package/dist/ui/fatal.d.ts.map +1 -0
  279. package/dist/ui/fatal.js +34 -0
  280. package/dist/ui/fatal.js.map +1 -0
  281. package/dist/ui/index.d.ts +9 -0
  282. package/dist/ui/index.d.ts.map +1 -0
  283. package/dist/ui/index.js +9 -0
  284. package/dist/ui/index.js.map +1 -0
  285. package/dist/ui/interactive.d.ts +3 -0
  286. package/dist/ui/interactive.d.ts.map +1 -0
  287. package/dist/ui/interactive.js +5 -0
  288. package/dist/ui/interactive.js.map +1 -0
  289. package/dist/ui/messages.d.ts +10 -0
  290. package/dist/ui/messages.d.ts.map +1 -0
  291. package/dist/ui/messages.js +35 -0
  292. package/dist/ui/messages.js.map +1 -0
  293. package/dist/ui/next-steps.d.ts +3 -0
  294. package/dist/ui/next-steps.d.ts.map +1 -0
  295. package/dist/ui/next-steps.js +10 -0
  296. package/dist/ui/next-steps.js.map +1 -0
  297. package/dist/ui/progress.d.ts +5 -0
  298. package/dist/ui/progress.d.ts.map +1 -0
  299. package/dist/ui/progress.js +24 -0
  300. package/dist/ui/progress.js.map +1 -0
  301. package/dist/ui/prompts.d.ts +14 -0
  302. package/dist/ui/prompts.d.ts.map +1 -0
  303. package/dist/ui/prompts.js +34 -0
  304. package/dist/ui/prompts.js.map +1 -0
  305. package/package.json +5 -2
  306. package/src/app/framework.ts +1 -3
  307. package/src/app/proxy-dev-app.ts +114 -6
  308. package/src/app-config.ts +80 -0
  309. package/src/binary-cache.ts +102 -52
  310. package/src/cli.ts +16 -2
  311. package/src/commands/add.ts +97 -0
  312. package/src/commands/admin.ts +381 -190
  313. package/src/commands/adopt.ts +82 -0
  314. package/src/commands/app.ts +20 -17
  315. package/src/commands/cache.ts +11 -10
  316. package/src/commands/cloud.ts +91 -142
  317. package/src/commands/db.ts +40 -63
  318. package/src/commands/deploy.ts +186 -126
  319. package/src/commands/dev.ts +98 -55
  320. package/src/commands/diff.ts +52 -43
  321. package/src/commands/doctor.ts +103 -0
  322. package/src/commands/engine.ts +5 -4
  323. package/src/commands/functions.ts +187 -123
  324. package/src/commands/generate.ts +5 -4
  325. package/src/commands/init.ts +1087 -104
  326. package/src/commands/introspect.ts +48 -0
  327. package/src/commands/keys.ts +56 -14
  328. package/src/commands/link-helpers.ts +273 -0
  329. package/src/commands/logs.ts +5 -4
  330. package/src/commands/migrate-from-v1.ts +3 -2
  331. package/src/commands/migrate.ts +167 -27
  332. package/src/commands/pg.ts +13 -18
  333. package/src/commands/plugins.ts +55 -46
  334. package/src/commands/pull.ts +38 -9
  335. package/src/commands/push.ts +148 -175
  336. package/src/commands/seed.ts +5 -4
  337. package/src/commands/self-host.ts +85 -54
  338. package/src/commands/self-update.ts +3 -2
  339. package/src/commands/status.ts +102 -33
  340. package/src/commands/types.ts +3 -2
  341. package/src/commands/update.ts +59 -23
  342. package/src/compose-rename.ts +76 -0
  343. package/src/config.ts +2 -1
  344. package/src/dev-compose.ts +462 -76
  345. package/src/dev-log-bus.ts +101 -0
  346. package/src/dev-log-filter.ts +32 -0
  347. package/src/dev-logo.ts +61 -0
  348. package/src/dev-ports.ts +212 -0
  349. package/src/dev-session-lock.ts +101 -0
  350. package/src/dev-session.ts +130 -0
  351. package/src/dev-shutdown.ts +147 -0
  352. package/src/dev-task-colors.ts +47 -0
  353. package/src/dev-tui.ts +232 -0
  354. package/src/diff-output.ts +79 -1
  355. package/src/docker-runtime.ts +151 -0
  356. package/src/engine-client.ts +81 -17
  357. package/src/engine-push-output.ts +75 -0
  358. package/src/ensure-binary.ts +2 -2
  359. package/src/env-file.ts +37 -0
  360. package/src/gitignore.ts +48 -0
  361. package/src/kong-config.ts +24 -1
  362. package/src/link.ts +243 -0
  363. package/src/process-manager.ts +66 -10
  364. package/src/project-config.ts +62 -7
  365. package/src/prompts.ts +2 -0
  366. package/src/pull-utils.ts +217 -23
  367. package/src/resolve-target.ts +419 -0
  368. package/src/restore-system-relation-targets.ts +45 -0
  369. package/src/runtime-routes.ts +7 -0
  370. package/src/schema-ast-v2.ts +2 -1
  371. package/src/schema-sources.ts +248 -0
  372. package/src/scripts/postinstall.ts +7 -1
  373. package/src/self-host-compose.ts +262 -46
  374. package/src/storage-provision.ts +33 -1
  375. package/src/supatype-eval-1781522769253.mts +1 -0
  376. package/src/systemd.ts +2 -2
  377. package/src/target-client.ts +40 -0
  378. package/src/type-extractor.ts +124 -11
  379. package/src/ui/README.md +17 -0
  380. package/src/ui/brand.ts +12 -0
  381. package/src/ui/confirm.ts +38 -0
  382. package/src/ui/fatal.ts +43 -0
  383. package/src/ui/index.ts +8 -0
  384. package/src/ui/interactive.ts +4 -0
  385. package/src/ui/messages.ts +43 -0
  386. package/src/ui/next-steps.ts +10 -0
  387. package/src/ui/progress.ts +28 -0
  388. package/src/ui/prompts.ts +40 -0
  389. package/tests/admin-ensure.test.ts +59 -0
  390. package/tests/cli-help.test.ts +27 -2
  391. package/tests/config.test.ts +29 -2
  392. package/tests/dev-ports.test.ts +41 -0
  393. package/tests/dev-session-lock.test.ts +54 -0
  394. package/tests/dev-ui.test.ts +162 -0
  395. package/tests/docker-runtime.test.ts +236 -0
  396. package/tests/engine-push-output.test.ts +67 -0
  397. package/tests/init.test.ts +197 -18
  398. package/tests/link.test.ts +148 -0
  399. package/tests/minisign.test.ts +102 -0
  400. package/tests/proxy-dev-app.test.ts +45 -1
  401. package/tests/pull-utils.test.ts +5 -4
  402. package/tests/runtime-contract.test.ts +186 -2
  403. package/tests/schema-sources.test.ts +119 -0
  404. package/tests/storage-provision.test.ts +100 -0
  405. package/tests/ui-confirm.test.ts +41 -0
  406. package/tests/ui-messages.test.ts +66 -0
  407. package/tsconfig.tsbuildinfo +1 -1
@@ -7,10 +7,13 @@
7
7
  *
8
8
  * Security model:
9
9
  * 1. Download checksums.sha256 + checksums.sha256.minisig from CDN.
10
- * 2. Verify Ed25519 minisign signature on the checksum file using the
11
- * embedded public key (SUPATYPE_RELEASE_PUBLIC_KEY).
10
+ * 2. Verify the Ed25519 minisign signature on the checksum file using the
11
+ * release public key (embedded at publish, overridable via
12
+ * SUPATYPE_RELEASE_PUBLIC_KEY).
12
13
  * 3. Verify SHA256 of the downloaded binary against the signed checksum.
13
- * Both checks are mandatory when SUPATYPE_RELEASE_PUBLIC_KEY is set.
14
+ * Verification is mandatory and fails closed: if no public key is configured,
15
+ * the download errors out rather than silently degrading to SHA256-only.
16
+ * The only escape hatch is the explicit SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS=1.
14
17
  */
15
18
 
16
19
  import { createHash, createPublicKey, verify as cryptoVerify } from "node:crypto"
@@ -23,6 +26,7 @@ import {
23
26
  openSync,
24
27
  readFileSync,
25
28
  readSync,
29
+ rmdirSync,
26
30
  statSync,
27
31
  unlinkSync,
28
32
  writeFileSync,
@@ -31,6 +35,7 @@ import { chmod } from "node:fs/promises"
31
35
  import { homedir } from "node:os"
32
36
  import { basename, join, resolve, isAbsolute } from "node:path"
33
37
  import type { SupatypeProjectConfig } from "./project-config.js"
38
+ import { loadProjectLink, migrateLegacyLinkFiles } from "./link.js"
34
39
  import { releasePublicKey } from "./release-public-key.js"
35
40
 
36
41
  /**
@@ -81,12 +86,16 @@ export function describeActiveOverrides(config: SupatypeProjectConfig): string[]
81
86
 
82
87
  /**
83
88
  * True when this working tree is associated with a remote Supatype Cloud project:
84
- * `project.ref`, `.supatype/cloud.json` (schema deploy link), or `.supatype/linked.json` (functions link).
89
+ * `project.ref` or `.supatype/link.json` (cloud kind).
85
90
  */
86
91
  export function isLinkedToCloudProject(cwd: string, config: SupatypeProjectConfig): boolean {
87
92
  const ref = config.project.ref
88
93
  if (typeof ref === "string" && ref.trim() !== "") return true
89
94
 
95
+ migrateLegacyLinkFiles(cwd)
96
+ const link = loadProjectLink(cwd)
97
+ if (link?.kind === "cloud" && link.projectRef.trim() !== "") return true
98
+
90
99
  const linkedPath = join(cwd, ".supatype", "linked.json")
91
100
  if (existsSync(linkedPath)) {
92
101
  try {
@@ -112,7 +121,7 @@ export function isLinkedToCloudProject(cwd: string, config: SupatypeProjectConfi
112
121
 
113
122
  export type { Component, ComponentVersions } from "./components.js"
114
123
  export { BINARY_COMPONENTS } from "./components.js"
115
- import { BINARY_COMPONENTS, type Component } from "./components.js"
124
+ import { BINARY_COMPONENTS, type Component, type ComponentVersions } from "./components.js"
116
125
 
117
126
  export interface PlatformId {
118
127
  os: "linux" | "darwin" | "windows"
@@ -130,17 +139,6 @@ export function postgresArchiveTag(version: string): string {
130
139
  return version.split(".")[0]!
131
140
  }
132
141
 
133
- /**
134
- * Supatype release signing public key (minisign format).
135
- * Generated with: minisign -G
136
- * Rotate by: generating a new pair, updating this constant, and updating
137
- * the MINISIGN_PRIVATE_KEY GitHub Actions secret.
138
- *
139
- * ⚠ PLACEHOLDER — replace with actual public key before first release.
140
- * When empty, minisign verification is skipped with a warning (SHA256 only).
141
- */
142
- const SUPATYPE_RELEASE_PUBLIC_KEY = ""
143
-
144
142
  // CDN path templates per component.
145
143
  const CDN_PATHS: Record<Component, (version: string, platform: PlatformId) => string> = {
146
144
  engine: (v, p) => `/engine/v${v}/supatype-engine-${p.os}-${p.arch}${p.os === "windows" ? ".exe" : ""}`,
@@ -226,7 +224,7 @@ export async function resolveBinary(
226
224
  }
227
225
 
228
226
  const overridePath = config.overrides?.[component === "postgres" ? "postgres_dir" : component]
229
- const version = versionFor(component, config)
227
+ const version = await resolveVersionFor(component, config)
230
228
 
231
229
  if (version === VERSION_PIN_LOCAL && !overridePath) {
232
230
  const key = component === "postgres" ? "postgres_dir" : component
@@ -335,14 +333,14 @@ export async function download(
335
333
 
336
334
  console.log(`[supatype] Downloading ${component} v${version} (${platform.os}/${platform.arch})...`)
337
335
 
338
- // ── Fetch checksums + optional minisig ────────────────────────────────────
339
- const expectedChecksum = await withRetry(() =>
340
- fetchChecksums(checksumsUrl, minisigUrl, name),
341
- )
342
-
343
- // ── Stream-download binary with progress ─────────────────────────────────
344
336
  const tmpPath = destPath + ".tmp"
345
337
  try {
338
+ // ── Fetch checksums + optional minisig (retried on transient failures) ───
339
+ const expectedChecksum = await withRetry(() =>
340
+ fetchChecksums(checksumsUrl, minisigUrl, name),
341
+ )
342
+
343
+ // ── Stream-download binary with progress (retried on transient failures) ─
346
344
  await withRetry(() => streamToFileWithProgress(binaryUrl, tmpPath))
347
345
 
348
346
  // ── Verify SHA256 ────────────────────────────────────────────────────────
@@ -354,9 +352,17 @@ export async function download(
354
352
  if (process.platform !== "win32" && EXECUTABLE_COMPONENTS.has(component)) {
355
353
  await chmod(destPath, 0o755)
356
354
  }
355
+ } catch (err) {
356
+ // Never leave a partial binary or an empty version directory behind: a stale
357
+ // empty dir makes the next resolve look fine while silently lacking a binary.
358
+ try { if (existsSync(destPath)) unlinkSync(destPath) } catch { /* ignore */ }
359
+ try { rmdirSync(dir) } catch { /* dir not empty or already removed */ }
360
+ throw new Error(
361
+ `Failed to download ${component} v${version} from ${CDN_BASE}: ${(err as Error).message}`,
362
+ )
357
363
  } finally {
358
364
  if (existsSync(tmpPath)) {
359
- try { require("node:fs").unlinkSync(tmpPath) } catch { /* ignore */ }
365
+ try { unlinkSync(tmpPath) } catch { /* ignore */ }
360
366
  }
361
367
  }
362
368
 
@@ -379,24 +385,36 @@ async function fetchChecksums(
379
385
  const checksumsText = await csResp.text()
380
386
 
381
387
  const pubKey = releasePublicKey()
382
- if (pubKey) {
383
- // Minisign signature is required when a public key is embedded.
384
- const sigResp = await fetch(minisigUrl)
385
- if (!sigResp.ok) {
386
- throw new Error(
387
- `Failed to fetch checksum signature from ${minisigUrl}: HTTP ${sigResp.status}\n` +
388
- "Cannot verify release integrity. Aborting download.",
388
+ if (!pubKey) {
389
+ // Fail closed: a missing public key means we cannot verify authenticity, only
390
+ // integrity (SHA256). Published builds always embed the key, so this only
391
+ // happens in source/contributor builds — never silently downgrade.
392
+ if (process.env["SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS"] === "1") {
393
+ console.warn(
394
+ "[supatype] \u26a0 SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS=1 no minisign public " +
395
+ "key configured; verifying SHA256 only (authenticity NOT checked).",
389
396
  )
397
+ return extractChecksum(checksumsText, binaryFilename)
390
398
  }
391
- const sigText = await sigResp.text()
392
- verifyMinisign(Buffer.from(checksumsText, "utf8"), sigText, pubKey)
393
- } else {
394
- console.warn(
395
- "[supatype] \u26a0 Minisign public key not configured " +
396
- "skipping signature verification (SHA256 only).",
399
+ throw new Error(
400
+ "No minisign public key configured — cannot verify release authenticity.\n" +
401
+ "Published @supatype/cli builds embed the key automatically; if you are building " +
402
+ "from source, set SUPATYPE_RELEASE_PUBLIC_KEY to the release public key, or set " +
403
+ "SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS=1 to download with SHA256-only verification (unsafe).",
397
404
  )
398
405
  }
399
406
 
407
+ // Minisign signature is mandatory when a public key is configured.
408
+ const sigResp = await fetch(minisigUrl)
409
+ if (!sigResp.ok) {
410
+ throw new Error(
411
+ `Failed to fetch checksum signature from ${minisigUrl}: HTTP ${sigResp.status}\n` +
412
+ "Cannot verify release integrity. Aborting download.",
413
+ )
414
+ }
415
+ const sigText = await sigResp.text()
416
+ verifyMinisign(Buffer.from(checksumsText, "utf8"), sigText, pubKey)
417
+
400
418
  return extractChecksum(checksumsText, binaryFilename)
401
419
  }
402
420
 
@@ -420,10 +438,10 @@ async function fetchChecksums(
420
438
  const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex")
421
439
 
422
440
  /**
423
- * Verify a minisign signature (Ed25519 legacy mode, algorithm bytes "Ed").
424
- * Throws if verification fails.
441
+ * Verify a minisign signature. Supports both Ed25519 legacy mode ("Ed", over the
442
+ * raw file) and prehashed mode ("ED", over BLAKE2b-512(file)). Throws if invalid.
425
443
  */
426
- function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: string): void {
444
+ export function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: string): void {
427
445
  // Parse public key: [2 algo][8 keyId][32 ed25519 key]
428
446
  const pkLines = pubKeyStr.trim().split("\n")
429
447
  const pkBytes = Buffer.from(pkLines[pkLines.length - 1]!.trim(), "base64")
@@ -445,14 +463,17 @@ function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: st
445
463
  const sigKeyId = sigBytes.subarray(2, 10)
446
464
  const signature = sigBytes.subarray(10, 74)
447
465
 
448
- // Only Ed25519 legacy mode ("Ed" = 0x45, 0x64) is supported.
449
- // Hashed mode ("ED") requires BLAKE2b prehashing not implemented.
450
- if (algo[0] !== 0x45 || algo[1] !== 0x64) {
466
+ // Both Ed25519 modes are supported:
467
+ // "Ed" (0x45, 0x64) legacy: signature is over the raw file bytes.
468
+ // "ED" (0x45, 0x44) prehashed: signature is over BLAKE2b-512(file).
469
+ // Modern minisign (and our release pipeline) default to prehashed mode.
470
+ if (algo[0] !== 0x45 || (algo[1] !== 0x64 && algo[1] !== 0x44)) {
451
471
  throw new Error(
452
- "Unsupported minisign algorithm — only Ed25519 legacy mode supported.\n" +
472
+ "Unsupported minisign algorithm — expected Ed25519 ('Ed' legacy or 'ED' prehashed).\n" +
453
473
  `Got: 0x${algo[0]?.toString(16)}${algo[1]?.toString(16)}`,
454
474
  )
455
475
  }
476
+ const prehashed = algo[1] === 0x44
456
477
 
457
478
  if (!sigKeyId.equals(pkKeyId)) {
458
479
  throw new Error(
@@ -464,7 +485,13 @@ function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: st
464
485
  const spkiDer = Buffer.concat([ED25519_SPKI_PREFIX, pkEd25519])
465
486
  const keyObject = createPublicKey({ key: spkiDer, format: "der", type: "spki" })
466
487
 
467
- const valid = cryptoVerify(null, fileBytes, keyObject, signature)
488
+ // Pure Ed25519 (PureEdDSA) verifies over the message directly; for prehashed
489
+ // minisign the "message" is the BLAKE2b-512 digest of the file.
490
+ const signedData = prehashed
491
+ ? createHash("blake2b512").update(fileBytes).digest()
492
+ : fileBytes
493
+
494
+ const valid = cryptoVerify(null, signedData, keyObject, signature)
468
495
  if (!valid) {
469
496
  throw new Error(
470
497
  "Minisign signature verification FAILED — the checksum file may have been tampered with.\n" +
@@ -730,10 +757,30 @@ export function normalisePlatformPath(p: string): string {
730
757
  return result
731
758
  }
732
759
 
760
+ export function pinnedVersion(component: Component, config: SupatypeProjectConfig): string | undefined {
761
+ const version = config.versions?.[component]
762
+ if (typeof version !== "string") return undefined
763
+ const trimmed = version.trim()
764
+ return trimmed === "" ? undefined : trimmed
765
+ }
766
+
767
+ /** Pinned version from config, or latest from CDN when unset. */
768
+ export async function resolveVersionFor(
769
+ component: Component,
770
+ config: SupatypeProjectConfig,
771
+ ): Promise<string> {
772
+ const pinned = pinnedVersion(component, config)
773
+ if (pinned) return pinned
774
+ return fetchLatestVersion(component)
775
+ }
776
+
777
+ /** @deprecated Prefer {@link pinnedVersion} or {@link resolveVersionFor}. */
733
778
  export function versionFor(component: Component, config: SupatypeProjectConfig): string {
734
- const version = config.versions[component]
735
- if (typeof version !== "string" || version.trim() === "") {
736
- throw new Error(`[supatype] versions.${component} must be set in supatype.config.ts`)
779
+ const version = pinnedVersion(component, config)
780
+ if (!version) {
781
+ throw new Error(
782
+ `[supatype] versions.${component} is not pinned in supatype.config.ts (omit versions to use latest)`,
783
+ )
737
784
  }
738
785
  return version
739
786
  }
@@ -780,7 +827,10 @@ export async function fetchAllLatestVersions(): Promise<Record<Component, string
780
827
  * Verify all cached binaries for the current platform (used by integration CI).
781
828
  * Throws if any cached component is missing or fails format checks.
782
829
  */
783
- export function verifyCachedBinaries(versions: SupatypeProjectConfig["versions"]): void {
830
+ export function verifyCachedBinaries(versions: Partial<ComponentVersions> | undefined): void {
831
+ if (!versions) {
832
+ throw new Error("[supatype] verifyCachedBinaries requires pinned versions")
833
+ }
784
834
  const platform = currentPlatform()
785
835
  for (const component of BINARY_COMPONENTS) {
786
836
  const version = versions[component]
@@ -798,15 +848,15 @@ export function verifyCachedBinaries(versions: SupatypeProjectConfig["versions"]
798
848
  }
799
849
 
800
850
  export async function downloadAll(
801
- versions: SupatypeProjectConfig["versions"],
851
+ versions: Partial<ComponentVersions> | undefined,
802
852
  graceful = false,
803
853
  ): Promise<void> {
804
854
  const platform = currentPlatform()
805
855
  const components: Component[] = [...BINARY_COMPONENTS]
806
- const fakeConfig = { versions } as SupatypeProjectConfig
856
+ const latest = await fetchAllLatestVersions()
807
857
 
808
858
  for (const component of components) {
809
- const version = versionFor(component, fakeConfig)
859
+ const version = versions?.[component] ?? latest[component]
810
860
  if (version === VERSION_PIN_LOCAL) continue
811
861
  try {
812
862
  await download(component, version, platform)
package/src/cli.ts CHANGED
@@ -7,11 +7,15 @@ import { registerPg } from "./commands/pg.js"
7
7
  import { registerPush } from "./commands/push.js"
8
8
  import { registerDiff } from "./commands/diff.js"
9
9
  import { registerPull } from "./commands/pull.js"
10
+ import { registerDoctor } from "./commands/doctor.js"
11
+ import { registerIntrospect } from "./commands/introspect.js"
12
+ import { registerAdopt } from "./commands/adopt.js"
10
13
  import { registerGenerate } from "./commands/generate.js"
11
14
  import { registerMigrate } from "./commands/migrate.js"
12
15
  import { registerSeed } from "./commands/seed.js"
13
16
  import { registerKeys } from "./commands/keys.js"
14
17
  import { registerApp } from "./commands/app.js"
18
+ import { registerAdd } from "./commands/add.js"
15
19
  import { registerSelfHost } from "./commands/self-host.js"
16
20
  import { registerCloud } from "./commands/cloud.js"
17
21
  import { registerEngine } from "./commands/engine.js"
@@ -25,8 +29,9 @@ import { registerPlugins } from "./commands/plugins.js"
25
29
  import { registerTypes } from "./commands/types.js"
26
30
  import { registerMigrateFromV1 } from "./commands/migrate-from-v1.js"
27
31
  import { registerSelfUpdate } from "./commands/self-update.js"
32
+ import { reportCliFatal } from "./ui/fatal.js"
28
33
 
29
- export function run(): void {
34
+ export async function run(): Promise<void> {
30
35
  const program = new Command()
31
36
  .name("supatype")
32
37
  .description("Supatype — schema-first Postgres API")
@@ -41,11 +46,15 @@ export function run(): void {
41
46
  registerPush(program)
42
47
  registerDiff(program)
43
48
  registerPull(program)
49
+ registerDoctor(program)
50
+ registerIntrospect(program)
51
+ registerAdopt(program)
44
52
  registerGenerate(program)
45
53
  registerMigrate(program)
46
54
  registerSeed(program)
47
55
  registerKeys(program)
48
56
  registerApp(program)
57
+ registerAdd(program)
49
58
  registerSelfHost(program)
50
59
  registerCloud(program)
51
60
  registerEngine(program)
@@ -59,5 +68,10 @@ export function run(): void {
59
68
  registerTypes(program)
60
69
  registerMigrateFromV1(program)
61
70
 
62
- program.parse()
71
+ try {
72
+ await program.parseAsync(process.argv)
73
+ } catch (err) {
74
+ reportCliFatal(err)
75
+ process.exit(1)
76
+ }
63
77
  }
@@ -0,0 +1,97 @@
1
+ import type { Command } from "commander"
2
+ import * as p from "@clack/prompts"
3
+ import { loadConfig } from "../config.js"
4
+ import { selfHostTlsEnabled } from "../project-config.js"
5
+ import { updateServerConfigInProject } from "../app-config.js"
6
+ import { ensureNotCancelled, printLogo } from "../ui/prompts.js"
7
+ import { file, error, info, plain } from "../ui/messages.js"
8
+ import { nextSteps } from "../ui/next-steps.js"
9
+
10
+ export function registerAdd(program: Command): void {
11
+ const addCmd = program
12
+ .command("add")
13
+ .description("Add capabilities to an existing project")
14
+
15
+ addCmd
16
+ .command("domain [domain]")
17
+ .description("Add a custom domain with automatic HTTPS (self-host)")
18
+ .option("--email <email>", "Email for Let's Encrypt TLS certificates")
19
+ .action(async (domainArg: string | undefined, opts: { email?: string }) => {
20
+ await addDomain(domainArg, opts.email)
21
+ })
22
+ }
23
+
24
+ async function promptDomain(initial?: string): Promise<string> {
25
+ const trimmed = initial?.trim()
26
+ if (trimmed) return trimmed
27
+ return ensureNotCancelled(
28
+ await p.text({
29
+ message: "Please provide domain:",
30
+ placeholder: "demo.supatype.com",
31
+ validate: (v) => ((v ?? "").trim().length === 0 ? "Domain is required" : undefined),
32
+ }),
33
+ ).trim()
34
+ }
35
+
36
+ async function promptEmail(initial?: string): Promise<string> {
37
+ const trimmed = initial?.trim()
38
+ if (trimmed) return trimmed
39
+ return ensureNotCancelled(
40
+ await p.text({
41
+ message: "Please provide email for TLS",
42
+ placeholder: "hello@supatype.com",
43
+ validate: (v) => ((v ?? "").trim().length === 0 ? "Email is required" : undefined),
44
+ }),
45
+ ).trim()
46
+ }
47
+
48
+ async function addDomain(domainArg?: string, emailArg?: string): Promise<void> {
49
+ const cwd = process.cwd()
50
+ const interactive = !domainArg?.trim() || !emailArg?.trim()
51
+
52
+ if (interactive) {
53
+ printLogo()
54
+ p.intro("Add a custom domain")
55
+ }
56
+
57
+ const domain = await promptDomain(domainArg)
58
+ const email = await promptEmail(emailArg)
59
+
60
+ try {
61
+ const configPath = updateServerConfigInProject(cwd, { domain, tlsEmail: email })
62
+ if (interactive) {
63
+ p.outro(`Updated ${configPath}`)
64
+ } else {
65
+ file("updated", configPath)
66
+ }
67
+ printDomainNextSteps(cwd, domain)
68
+ } catch (err) {
69
+ error((err as Error).message)
70
+ process.exit(1)
71
+ }
72
+ }
73
+
74
+ function printDomainNextSteps(cwd: string, domain: string): void {
75
+ let tlsActive = true
76
+ try {
77
+ tlsActive = selfHostTlsEnabled(loadConfig(cwd))
78
+ } catch {
79
+ // config re-load is best-effort for the warning below
80
+ }
81
+
82
+ info(`Domain set to ${domain} with automatic HTTPS.`)
83
+ if (!tlsActive) {
84
+ plain(
85
+ "\nNote: a supatype.local.config.ts override (server.mode=dev) is suppressing HTTPS locally.\n" +
86
+ "That file is gitignored, so HTTPS still activates on your production server.",
87
+ )
88
+ }
89
+ nextSteps("Go live:", [
90
+ `Point DNS: an A record for ${domain} -> your server's public IP`,
91
+ "Open ports 80 and 443 on the server firewall",
92
+ "supatype self-host compose up -d # Kong provisions HTTPS automatically",
93
+ `Platform URL: https://${domain}`,
94
+ ])
95
+ plain(" App, REST, Auth, Storage, Realtime, Functions, and Studio — one HTTPS domain.")
96
+ plain(" Certificates persist in the valkey-data volume.\n")
97
+ }