@synteq/cli 0.2.0 → 1.1.2

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 (433) hide show
  1. package/dist/api/client.d.ts +362 -5
  2. package/dist/api/client.js +559 -44
  3. package/dist/api/client.js.map +1 -1
  4. package/dist/api/factory.js +5 -2
  5. package/dist/api/factory.js.map +1 -1
  6. package/dist/api/types.d.ts +892 -3
  7. package/dist/apps-support/deploy.d.ts +197 -0
  8. package/dist/apps-support/deploy.js +639 -0
  9. package/dist/apps-support/deploy.js.map +1 -0
  10. package/dist/apps-support/dotenv.d.ts +67 -0
  11. package/dist/apps-support/dotenv.js +136 -0
  12. package/dist/apps-support/dotenv.js.map +1 -0
  13. package/dist/apps-support/env-rollout.d.ts +18 -0
  14. package/dist/apps-support/env-rollout.js +52 -0
  15. package/dist/apps-support/env-rollout.js.map +1 -0
  16. package/dist/apps-support/git.d.ts +14 -0
  17. package/dist/apps-support/git.js +25 -0
  18. package/dist/apps-support/git.js.map +1 -0
  19. package/dist/apps-support/link.d.ts +25 -0
  20. package/dist/apps-support/link.js +39 -0
  21. package/dist/apps-support/link.js.map +1 -0
  22. package/dist/apps-support/log-stream.d.ts +25 -0
  23. package/dist/apps-support/log-stream.js +85 -0
  24. package/dist/apps-support/log-stream.js.map +1 -0
  25. package/dist/apps-support/paid-app.d.ts +64 -0
  26. package/dist/apps-support/paid-app.js +195 -0
  27. package/dist/apps-support/paid-app.js.map +1 -0
  28. package/dist/apps-support/plans.d.ts +44 -0
  29. package/dist/apps-support/plans.js +43 -0
  30. package/dist/apps-support/plans.js.map +1 -0
  31. package/dist/apps-support/resolve.d.ts +10 -0
  32. package/dist/apps-support/resolve.js +53 -0
  33. package/dist/apps-support/resolve.js.map +1 -0
  34. package/dist/apps-support/time.d.ts +20 -0
  35. package/dist/apps-support/time.js +82 -0
  36. package/dist/apps-support/time.js.map +1 -0
  37. package/dist/auth/scope.js +4 -0
  38. package/dist/auth/scope.js.map +1 -1
  39. package/dist/auth/tokens.d.ts +3 -0
  40. package/dist/auth/tokens.js +6 -4
  41. package/dist/auth/tokens.js.map +1 -1
  42. package/dist/base/command.d.ts +35 -0
  43. package/dist/base/command.js +87 -4
  44. package/dist/base/command.js.map +1 -1
  45. package/dist/commands/agents.d.ts +10 -0
  46. package/dist/commands/agents.js +53 -0
  47. package/dist/commands/agents.js.map +1 -0
  48. package/dist/commands/api.d.ts +0 -1
  49. package/dist/commands/api.js +6 -5
  50. package/dist/commands/api.js.map +1 -1
  51. package/dist/commands/app/deployment/list.d.ts +14 -0
  52. package/dist/commands/app/deployment/list.js +45 -0
  53. package/dist/commands/app/deployment/list.js.map +1 -0
  54. package/dist/commands/app/deployment/rollback.d.ts +18 -0
  55. package/dist/commands/app/deployment/rollback.js +57 -0
  56. package/dist/commands/app/deployment/rollback.js.map +1 -0
  57. package/dist/commands/app/deployment/show.d.ts +26 -0
  58. package/dist/commands/app/deployment/show.js +98 -0
  59. package/dist/commands/app/deployment/show.js.map +1 -0
  60. package/dist/commands/app/domain/add.d.ts +17 -0
  61. package/dist/commands/app/domain/add.js +59 -0
  62. package/dist/commands/app/domain/add.js.map +1 -0
  63. package/dist/commands/app/domain/list.d.ts +14 -0
  64. package/dist/commands/app/domain/list.js +42 -0
  65. package/dist/commands/app/domain/list.js.map +1 -0
  66. package/dist/commands/app/domain/remove.d.ts +16 -0
  67. package/dist/commands/app/domain/remove.js +51 -0
  68. package/dist/commands/app/domain/remove.js.map +1 -0
  69. package/dist/commands/app/domain/verify.d.ts +15 -0
  70. package/dist/commands/app/domain/verify.js +39 -0
  71. package/dist/commands/app/domain/verify.js.map +1 -0
  72. package/dist/commands/app/env/add.d.ts +22 -0
  73. package/dist/commands/app/env/add.js +103 -0
  74. package/dist/commands/app/env/add.js.map +1 -0
  75. package/dist/commands/app/env/list.d.ts +17 -0
  76. package/dist/commands/app/env/list.js +40 -0
  77. package/dist/commands/app/env/list.js.map +1 -0
  78. package/dist/commands/app/env/reveal.d.ts +17 -0
  79. package/dist/commands/app/env/reveal.js +40 -0
  80. package/dist/commands/app/env/reveal.js.map +1 -0
  81. package/dist/commands/app/env/rm.d.ts +16 -0
  82. package/dist/commands/app/env/rm.js +48 -0
  83. package/dist/commands/app/env/rm.js.map +1 -0
  84. package/dist/commands/app/health.d.ts +14 -0
  85. package/dist/commands/app/health.js +40 -0
  86. package/dist/commands/app/health.js.map +1 -0
  87. package/dist/commands/app/index.d.ts +6 -0
  88. package/dist/commands/app/index.js +7 -0
  89. package/dist/commands/app/index.js.map +1 -0
  90. package/dist/commands/app/link.d.ts +22 -0
  91. package/dist/commands/app/link.js +31 -0
  92. package/dist/commands/app/link.js.map +1 -0
  93. package/dist/commands/app/logs.d.ts +25 -0
  94. package/dist/commands/app/logs.js +121 -0
  95. package/dist/commands/app/logs.js.map +1 -0
  96. package/dist/commands/app/metrics.d.ts +21 -0
  97. package/dist/commands/app/metrics.js +123 -0
  98. package/dist/commands/app/metrics.js.map +1 -0
  99. package/dist/commands/app/plan/change.d.ts +18 -0
  100. package/dist/commands/app/plan/change.js +79 -0
  101. package/dist/commands/app/plan/change.js.map +1 -0
  102. package/dist/commands/app/plan/show.d.ts +14 -0
  103. package/dist/commands/app/plan/show.js +40 -0
  104. package/dist/commands/app/plan/show.js.map +1 -0
  105. package/dist/commands/app/settings/set.d.ts +25 -0
  106. package/dist/commands/app/settings/set.js +96 -0
  107. package/dist/commands/app/settings/set.js.map +1 -0
  108. package/dist/commands/app/settings/show.d.ts +14 -0
  109. package/dist/commands/app/settings/show.js +48 -0
  110. package/dist/commands/app/settings/show.js.map +1 -0
  111. package/dist/commands/app/unlink.d.ts +8 -0
  112. package/dist/commands/app/unlink.js +25 -0
  113. package/dist/commands/app/unlink.js.map +1 -0
  114. package/dist/commands/apps/create.d.ts +26 -0
  115. package/dist/commands/apps/create.js +91 -0
  116. package/dist/commands/apps/create.js.map +1 -0
  117. package/dist/commands/apps/list.d.ts +17 -0
  118. package/dist/commands/apps/list.js +56 -0
  119. package/dist/commands/apps/list.js.map +1 -0
  120. package/dist/commands/baremetal/activity.d.ts +14 -0
  121. package/dist/commands/baremetal/activity.js +53 -0
  122. package/dist/commands/baremetal/activity.js.map +1 -0
  123. package/dist/commands/baremetal/bandwidth.d.ts +10 -0
  124. package/dist/commands/baremetal/bandwidth.js +86 -0
  125. package/dist/commands/baremetal/bandwidth.js.map +1 -0
  126. package/dist/commands/baremetal/console.d.ts +20 -0
  127. package/dist/commands/baremetal/console.js +72 -0
  128. package/dist/commands/baremetal/console.js.map +1 -0
  129. package/dist/commands/baremetal/details.d.ts +11 -0
  130. package/dist/commands/baremetal/details.js +116 -0
  131. package/dist/commands/baremetal/details.js.map +1 -0
  132. package/dist/commands/baremetal/index.d.ts +5 -0
  133. package/dist/commands/baremetal/index.js +6 -0
  134. package/dist/commands/baremetal/index.js.map +1 -0
  135. package/dist/commands/baremetal/install-password.d.ts +14 -0
  136. package/dist/commands/baremetal/install-password.js +80 -0
  137. package/dist/commands/baremetal/install-password.js.map +1 -0
  138. package/dist/commands/baremetal/list.d.ts +11 -0
  139. package/dist/commands/baremetal/list.js +59 -0
  140. package/dist/commands/baremetal/list.js.map +1 -0
  141. package/dist/commands/baremetal/order.d.ts +34 -0
  142. package/dist/commands/baremetal/order.js +394 -0
  143. package/dist/commands/baremetal/order.js.map +1 -0
  144. package/dist/commands/baremetal/power.d.ts +18 -0
  145. package/dist/commands/baremetal/power.js +168 -0
  146. package/dist/commands/baremetal/power.js.map +1 -0
  147. package/dist/commands/baremetal/reinstall.d.ts +21 -0
  148. package/dist/commands/baremetal/reinstall.js +168 -0
  149. package/dist/commands/baremetal/reinstall.js.map +1 -0
  150. package/dist/commands/config/profile/add.d.ts +11 -0
  151. package/dist/commands/config/profile/add.js +51 -0
  152. package/dist/commands/config/profile/add.js.map +1 -0
  153. package/dist/commands/config/profile/list.d.ts +7 -0
  154. package/dist/commands/config/profile/list.js +29 -0
  155. package/dist/commands/config/profile/list.js.map +1 -0
  156. package/dist/commands/config/profile/remove.d.ts +11 -0
  157. package/dist/commands/config/profile/remove.js +35 -0
  158. package/dist/commands/config/profile/remove.js.map +1 -0
  159. package/dist/commands/config/profile/use.d.ts +10 -0
  160. package/dist/commands/config/profile/use.js +44 -0
  161. package/dist/commands/config/profile/use.js.map +1 -0
  162. package/dist/commands/config/show.js +4 -1
  163. package/dist/commands/config/show.js.map +1 -1
  164. package/dist/commands/credits/add.js +2 -2
  165. package/dist/commands/credits/add.js.map +1 -1
  166. package/dist/commands/credits/index.js +5 -1
  167. package/dist/commands/credits/index.js.map +1 -1
  168. package/dist/commands/deploy.d.ts +51 -0
  169. package/dist/commands/deploy.js +515 -0
  170. package/dist/commands/deploy.js.map +1 -0
  171. package/dist/commands/function/create.d.ts +29 -0
  172. package/dist/commands/function/create.js +113 -0
  173. package/dist/commands/function/create.js.map +1 -0
  174. package/dist/commands/function/credits/add.d.ts +29 -0
  175. package/dist/commands/function/credits/add.js +209 -0
  176. package/dist/commands/function/credits/add.js.map +1 -0
  177. package/dist/commands/function/credits/auto-reload.d.ts +27 -0
  178. package/dist/commands/function/credits/auto-reload.js +107 -0
  179. package/dist/commands/function/credits/auto-reload.js.map +1 -0
  180. package/dist/commands/function/credits/index.d.ts +22 -0
  181. package/dist/commands/function/credits/index.js +80 -0
  182. package/dist/commands/function/credits/index.js.map +1 -0
  183. package/dist/commands/function/cron/add.d.ts +19 -0
  184. package/dist/commands/function/cron/add.js +74 -0
  185. package/dist/commands/function/cron/add.js.map +1 -0
  186. package/dist/commands/function/cron/list.d.ts +12 -0
  187. package/dist/commands/function/cron/list.js +55 -0
  188. package/dist/commands/function/cron/list.js.map +1 -0
  189. package/dist/commands/function/cron/rm.d.ts +17 -0
  190. package/dist/commands/function/cron/rm.js +73 -0
  191. package/dist/commands/function/cron/rm.js.map +1 -0
  192. package/dist/commands/function/cron/run.d.ts +16 -0
  193. package/dist/commands/function/cron/run.js +59 -0
  194. package/dist/commands/function/cron/run.js.map +1 -0
  195. package/dist/commands/function/cron/update.d.ts +21 -0
  196. package/dist/commands/function/cron/update.js +87 -0
  197. package/dist/commands/function/cron/update.js.map +1 -0
  198. package/dist/commands/function/delete.d.ts +23 -0
  199. package/dist/commands/function/delete.js +90 -0
  200. package/dist/commands/function/delete.js.map +1 -0
  201. package/dist/commands/function/deploy.d.ts +64 -0
  202. package/dist/commands/function/deploy.js +616 -0
  203. package/dist/commands/function/deploy.js.map +1 -0
  204. package/dist/commands/function/env/list.d.ts +12 -0
  205. package/dist/commands/function/env/list.js +46 -0
  206. package/dist/commands/function/env/list.js.map +1 -0
  207. package/dist/commands/function/env/reveal.d.ts +21 -0
  208. package/dist/commands/function/env/reveal.js +54 -0
  209. package/dist/commands/function/env/reveal.js.map +1 -0
  210. package/dist/commands/function/env/rm.d.ts +17 -0
  211. package/dist/commands/function/env/rm.js +62 -0
  212. package/dist/commands/function/env/rm.js.map +1 -0
  213. package/dist/commands/function/env/set.d.ts +28 -0
  214. package/dist/commands/function/env/set.js +124 -0
  215. package/dist/commands/function/env/set.js.map +1 -0
  216. package/dist/commands/function/index.d.ts +14 -0
  217. package/dist/commands/function/index.js +11 -0
  218. package/dist/commands/function/index.js.map +1 -0
  219. package/dist/commands/function/init.d.ts +48 -0
  220. package/dist/commands/function/init.js +277 -0
  221. package/dist/commands/function/init.js.map +1 -0
  222. package/dist/commands/function/invoke.d.ts +29 -0
  223. package/dist/commands/function/invoke.js +138 -0
  224. package/dist/commands/function/invoke.js.map +1 -0
  225. package/dist/commands/function/link.d.ts +23 -0
  226. package/dist/commands/function/link.js +80 -0
  227. package/dist/commands/function/link.js.map +1 -0
  228. package/dist/commands/function/list.d.ts +16 -0
  229. package/dist/commands/function/list.js +55 -0
  230. package/dist/commands/function/list.js.map +1 -0
  231. package/dist/commands/function/logs.d.ts +29 -0
  232. package/dist/commands/function/logs.js +171 -0
  233. package/dist/commands/function/logs.js.map +1 -0
  234. package/dist/commands/function/pause.d.ts +19 -0
  235. package/dist/commands/function/pause.js +50 -0
  236. package/dist/commands/function/pause.js.map +1 -0
  237. package/dist/commands/function/releases.d.ts +15 -0
  238. package/dist/commands/function/releases.js +62 -0
  239. package/dist/commands/function/releases.js.map +1 -0
  240. package/dist/commands/function/resume.d.ts +16 -0
  241. package/dist/commands/function/resume.js +47 -0
  242. package/dist/commands/function/resume.js.map +1 -0
  243. package/dist/commands/function/rollback.d.ts +18 -0
  244. package/dist/commands/function/rollback.js +69 -0
  245. package/dist/commands/function/rollback.js.map +1 -0
  246. package/dist/commands/function/settings/set.d.ts +23 -0
  247. package/dist/commands/function/settings/set.js +119 -0
  248. package/dist/commands/function/settings/set.js.map +1 -0
  249. package/dist/commands/function/show.d.ts +15 -0
  250. package/dist/commands/function/show.js +80 -0
  251. package/dist/commands/function/show.js.map +1 -0
  252. package/dist/commands/function/unlink.d.ts +9 -0
  253. package/dist/commands/function/unlink.js +34 -0
  254. package/dist/commands/function/unlink.js.map +1 -0
  255. package/dist/commands/link.d.ts +82 -0
  256. package/dist/commands/link.js +532 -0
  257. package/dist/commands/link.js.map +1 -0
  258. package/dist/commands/login.js +9 -0
  259. package/dist/commands/login.js.map +1 -1
  260. package/dist/commands/order.js +3 -1
  261. package/dist/commands/order.js.map +1 -1
  262. package/dist/commands/payments/methods/delete.js +2 -2
  263. package/dist/commands/payments/methods/delete.js.map +1 -1
  264. package/dist/commands/resources.js +50 -7
  265. package/dist/commands/resources.js.map +1 -1
  266. package/dist/commands/status.d.ts +12 -0
  267. package/dist/commands/status.js +71 -0
  268. package/dist/commands/status.js.map +1 -0
  269. package/dist/commands/storage/detach.js +1 -1
  270. package/dist/commands/storage/detach.js.map +1 -1
  271. package/dist/commands/storage/list.js +6 -2
  272. package/dist/commands/storage/list.js.map +1 -1
  273. package/dist/commands/storage/order.d.ts +2 -0
  274. package/dist/commands/storage/order.js +100 -18
  275. package/dist/commands/storage/order.js.map +1 -1
  276. package/dist/commands/storage/resize.js +19 -6
  277. package/dist/commands/storage/resize.js.map +1 -1
  278. package/dist/commands/tickets/create.d.ts +2 -0
  279. package/dist/commands/tickets/create.js +40 -1
  280. package/dist/commands/tickets/create.js.map +1 -1
  281. package/dist/commands/tickets/download.d.ts +18 -0
  282. package/dist/commands/tickets/download.js +138 -0
  283. package/dist/commands/tickets/download.js.map +1 -0
  284. package/dist/commands/tickets/list.js +13 -7
  285. package/dist/commands/tickets/list.js.map +1 -1
  286. package/dist/commands/tickets/reply.d.ts +2 -0
  287. package/dist/commands/tickets/reply.js +33 -4
  288. package/dist/commands/tickets/reply.js.map +1 -1
  289. package/dist/commands/vds/order.js +72 -7
  290. package/dist/commands/vds/order.js.map +1 -1
  291. package/dist/commands/vps/console.js +14 -6
  292. package/dist/commands/vps/console.js.map +1 -1
  293. package/dist/commands/vps/details.js +7 -1
  294. package/dist/commands/vps/details.js.map +1 -1
  295. package/dist/commands/vps/ip/add.js +76 -9
  296. package/dist/commands/vps/ip/add.js.map +1 -1
  297. package/dist/commands/vps/list.js +13 -4
  298. package/dist/commands/vps/list.js.map +1 -1
  299. package/dist/commands/vps/order.d.ts +19 -0
  300. package/dist/commands/vps/order.js +179 -23
  301. package/dist/commands/vps/order.js.map +1 -1
  302. package/dist/commands/vps/power.js +2 -2
  303. package/dist/commands/vps/power.js.map +1 -1
  304. package/dist/commands/vps/reinstall.js +2 -2
  305. package/dist/commands/vps/reinstall.js.map +1 -1
  306. package/dist/commands/vps/resize.js +36 -13
  307. package/dist/commands/vps/resize.js.map +1 -1
  308. package/dist/commands/whoami.d.ts +5 -0
  309. package/dist/commands/whoami.js +8 -3
  310. package/dist/commands/whoami.js.map +1 -1
  311. package/dist/config/paths.d.ts +1 -1
  312. package/dist/config/paths.js +6 -1
  313. package/dist/config/paths.js.map +1 -1
  314. package/dist/config/store.d.ts +58 -0
  315. package/dist/config/store.js +84 -2
  316. package/dist/config/store.js.map +1 -1
  317. package/dist/functions-support/credits.d.ts +64 -0
  318. package/dist/functions-support/credits.js +181 -0
  319. package/dist/functions-support/credits.js.map +1 -0
  320. package/dist/functions-support/cron.d.ts +17 -0
  321. package/dist/functions-support/cron.js +99 -0
  322. package/dist/functions-support/cron.js.map +1 -0
  323. package/dist/functions-support/env.d.ts +23 -0
  324. package/dist/functions-support/env.js +44 -0
  325. package/dist/functions-support/env.js.map +1 -0
  326. package/dist/functions-support/link.d.ts +25 -0
  327. package/dist/functions-support/link.js +51 -0
  328. package/dist/functions-support/link.js.map +1 -0
  329. package/dist/functions-support/poll.d.ts +40 -0
  330. package/dist/functions-support/poll.js +109 -0
  331. package/dist/functions-support/poll.js.map +1 -0
  332. package/dist/functions-support/resolve.d.ts +3 -0
  333. package/dist/functions-support/resolve.js +31 -0
  334. package/dist/functions-support/resolve.js.map +1 -0
  335. package/dist/functions-support/runtime.d.ts +30 -0
  336. package/dist/functions-support/runtime.js +52 -0
  337. package/dist/functions-support/runtime.js.map +1 -0
  338. package/dist/functions-support/source.d.ts +92 -0
  339. package/dist/functions-support/source.js +343 -0
  340. package/dist/functions-support/source.js.map +1 -0
  341. package/dist/help/scope-aware-help.js +10 -1
  342. package/dist/help/scope-aware-help.js.map +1 -1
  343. package/dist/tui/App.js +10 -25
  344. package/dist/tui/App.js.map +1 -1
  345. package/dist/tui/components/Menu.d.ts +3 -1
  346. package/dist/tui/components/Menu.js +7 -3
  347. package/dist/tui/components/Menu.js.map +1 -1
  348. package/dist/tui/router.d.ts +1 -1
  349. package/dist/tui/router.js.map +1 -1
  350. package/dist/tui/screens/Billing.js +56 -4
  351. package/dist/tui/screens/Billing.js.map +1 -1
  352. package/dist/tui/screens/Dashboard.js +8 -7
  353. package/dist/tui/screens/Dashboard.js.map +1 -1
  354. package/dist/tui/screens/Deploy.d.ts +2 -0
  355. package/dist/tui/screens/Deploy.js +912 -0
  356. package/dist/tui/screens/Deploy.js.map +1 -0
  357. package/dist/tui/screens/Order.js +359 -26
  358. package/dist/tui/screens/Order.js.map +1 -1
  359. package/dist/tui/screens/Resources.js +273 -31
  360. package/dist/tui/screens/Resources.js.map +1 -1
  361. package/dist/tui/screens/Tickets.js +18 -6
  362. package/dist/tui/screens/Tickets.js.map +1 -1
  363. package/dist/tui/screens/deployPreset.d.ts +20 -0
  364. package/dist/tui/screens/deployPreset.js +11 -0
  365. package/dist/tui/screens/deployPreset.js.map +1 -0
  366. package/dist/tui/screens/resources/AppDetail.d.ts +7 -0
  367. package/dist/tui/screens/resources/AppDetail.js +810 -0
  368. package/dist/tui/screens/resources/AppDetail.js.map +1 -0
  369. package/dist/tui/screens/resources/FunctionDetail.d.ts +7 -0
  370. package/dist/tui/screens/resources/FunctionDetail.js +751 -0
  371. package/dist/tui/screens/resources/FunctionDetail.js.map +1 -0
  372. package/dist/ui/attach.d.ts +15 -0
  373. package/dist/ui/attach.js +63 -0
  374. package/dist/ui/attach.js.map +1 -0
  375. package/dist/ui/baremetal-resolve.d.ts +10 -0
  376. package/dist/ui/baremetal-resolve.js +50 -0
  377. package/dist/ui/baremetal-resolve.js.map +1 -0
  378. package/dist/ui/confirm.js +1 -1
  379. package/dist/ui/confirm.js.map +1 -1
  380. package/dist/ui/cycle.d.ts +66 -0
  381. package/dist/ui/cycle.js +96 -0
  382. package/dist/ui/cycle.js.map +1 -0
  383. package/dist/ui/deploy-ui.d.ts +75 -0
  384. package/dist/ui/deploy-ui.js +243 -0
  385. package/dist/ui/deploy-ui.js.map +1 -0
  386. package/dist/ui/order-result.d.ts +4 -0
  387. package/dist/ui/order-result.js +32 -0
  388. package/dist/ui/order-result.js.map +1 -0
  389. package/dist/ui/payment-gate.d.ts +26 -0
  390. package/dist/ui/payment-gate.js +70 -0
  391. package/dist/ui/payment-gate.js.map +1 -0
  392. package/dist/ui/payment.js +10 -3
  393. package/dist/ui/payment.js.map +1 -1
  394. package/dist/ui/portal.d.ts +1 -1
  395. package/dist/ui/portal.js +7 -2
  396. package/dist/ui/portal.js.map +1 -1
  397. package/dist/ui/project-resolve.d.ts +25 -0
  398. package/dist/ui/project-resolve.js +31 -0
  399. package/dist/ui/project-resolve.js.map +1 -0
  400. package/dist/ui/render-mode.d.ts +5 -0
  401. package/dist/ui/render-mode.js +1 -1
  402. package/dist/ui/render-mode.js.map +1 -1
  403. package/dist/ui/resource-detail.js +0 -3
  404. package/dist/ui/resource-detail.js.map +1 -1
  405. package/dist/ui/resource-resolve.d.ts +9 -2
  406. package/dist/ui/resource-resolve.js +11 -4
  407. package/dist/ui/resource-resolve.js.map +1 -1
  408. package/dist/ui/spinner.d.ts +10 -1
  409. package/dist/ui/spinner.js +12 -3
  410. package/dist/ui/spinner.js.map +1 -1
  411. package/dist/ui/table.d.ts +7 -0
  412. package/dist/ui/table.js +11 -0
  413. package/dist/ui/table.js.map +1 -1
  414. package/dist/ui/tax.d.ts +10 -0
  415. package/dist/ui/tax.js +31 -0
  416. package/dist/ui/tax.js.map +1 -0
  417. package/dist/ui/ticket-resolve.d.ts +9 -1
  418. package/dist/ui/ticket-resolve.js +13 -2
  419. package/dist/ui/ticket-resolve.js.map +1 -1
  420. package/dist/ui/volume-resolve.js +4 -1
  421. package/dist/ui/volume-resolve.js.map +1 -1
  422. package/dist/ui/wait.d.ts +4 -0
  423. package/dist/ui/wait.js +7 -2
  424. package/dist/ui/wait.js.map +1 -1
  425. package/dist/ui/wizard.d.ts +2 -1
  426. package/dist/ui/wizard.js +1 -1
  427. package/dist/ui/wizard.js.map +1 -1
  428. package/dist/version-check-bin.js +2 -2
  429. package/dist/version-check-bin.js.map +1 -1
  430. package/dist/version-check.d.ts +5 -0
  431. package/dist/version-check.js +6 -1
  432. package/dist/version-check.js.map +1 -1
  433. package/package.json +53 -6
@@ -0,0 +1,639 @@
1
+ /**
2
+ * Source deploy plumbing shared by `synteq deploy` and the TUI's Deploy
3
+ * walkthrough: pack a directory, pick the build frontend, and follow a
4
+ * build → release to its outcome. Lifted out of commands/deploy.ts so the
5
+ * two surfaces cannot drift (the same excludes, the same terminal states,
6
+ * the same "a succeeded build is not a live release" rule).
7
+ */
8
+ import { spawn, spawnSync } from "node:child_process";
9
+ import { mkdtempSync, statSync, lstatSync, existsSync, readdirSync, readFileSync, createReadStream, createWriteStream, rmSync, } from "node:fs";
10
+ import { readFile } from "node:fs/promises";
11
+ import { createHash } from "node:crypto";
12
+ import { request as httpRequest } from "node:http";
13
+ import { request as httpsRequest } from "node:https";
14
+ import { tmpdir } from "node:os";
15
+ import { dirname, join } from "node:path";
16
+ import { Readable, Transform } from "node:stream";
17
+ import { pipeline } from "node:stream/promises";
18
+ import { ApiError, NetworkError } from "../api/errors.js";
19
+ import { isTransientError } from "../ui/wait.js";
20
+ /** Terminal build statuses — polling stops here. Mirrors the backend
21
+ * BuildStatus enum (pending | building | pushing | succeeded | failed). */
22
+ export const BUILD_TERMINAL = new Set(["succeeded", "failed"]);
23
+ /** Terminal release outcomes a deploy can end in. */
24
+ export const RELEASE_TERMINAL = new Set([
25
+ "live",
26
+ "start_failed",
27
+ "health_failed",
28
+ "superseded",
29
+ ]);
30
+ /** An explicit choice wins; otherwise a Dockerfile in the source root means
31
+ * `dockerfile`, and its absence means `railpack` (zero-config). Without
32
+ * this a Dockerfile-less repo would fail with "open Dockerfile: no such
33
+ * file". */
34
+ export function pickFrontend(dir, explicit) {
35
+ if (explicit === "dockerfile" || explicit === "railpack")
36
+ return explicit;
37
+ return existsSync(join(dir, "Dockerfile")) ? "dockerfile" : "railpack";
38
+ }
39
+ /**
40
+ * Tarball a directory to a temp .tgz using the system `tar`. Excludes
41
+ * dependency caches and VCS metadata ONLY — they are never build inputs and
42
+ * dominate the archive. Build OUTPUT directories (`dist`, `.next`) are
43
+ * deliberately kept: a Go app embedding its prebuilt frontend needs them,
44
+ * and anchoring an exclude to the root is not portable across GNU tar and
45
+ * bsdtar. Resolves to the archive path.
46
+ */
47
+ /** Never build inputs: dependency caches and VCS metadata. */
48
+ export const TARBALL_EXCLUDES = [".git", "node_modules", ".venv", "__pycache__"];
49
+ /** The `.env.*` spellings that document the variables an app wants, never
50
+ * their values: they ship like any other file. */
51
+ const DOTENV_DOC_SUFFIXES = [".example", ".sample", ".template"];
52
+ /**
53
+ * Is `name` (one path segment) a dotenv file: `.env` or `.env.<anything>`
54
+ * (`.env.local`, `.env.production`), but not `.env.example`, `.env.sample`
55
+ * or `.env.template`?
56
+ *
57
+ * A dotenv file holds the secrets of the machine you deploy FROM. It is never
58
+ * packed, on any intake path: the image goes to a registry and a `COPY . .`
59
+ * would bake it into every layer, so the orchestrator drops it at extraction
60
+ * too (apps `build.IsDotenvName`, the same rule). Variables for the deployed
61
+ * app are set with `synteq app env set` / `synteq function env set` or the
62
+ * Environment tab and injected at run time. There is no opt-in: `tar` has no
63
+ * negation, and a `!.env` line in `.synteqignore` is not honoured.
64
+ */
65
+ export function isDotenvName(name) {
66
+ if (name === ".env")
67
+ return true;
68
+ if (!name.startsWith(".env."))
69
+ return false;
70
+ return !DOTENV_DOC_SUFFIXES.some((s) => name.endsWith(s));
71
+ }
72
+ /** Does any segment of a slash-separated relative path name a dotenv file? */
73
+ export function hasDotenvSegment(relPath) {
74
+ return relPath.split("/").some(isDotenvName);
75
+ }
76
+ /**
77
+ * The dotenv files `makeTarball` leaves out of `dir`, as root-relative
78
+ * paths: what git lists for a checkout, else a walk of the tree (skipping
79
+ * `TARBALL_EXCLUDES` and never following symlinks). Printed by the deploy so
80
+ * nobody wonders where their `.env` went.
81
+ */
82
+ export function dotenvFiles(dir) {
83
+ const files = gitSourceListRaw(dir);
84
+ if (files !== null)
85
+ return files.filter(hasDotenvSegment).sort();
86
+ const found = [];
87
+ const walk = (abs, rel) => {
88
+ let entries;
89
+ try {
90
+ entries = readdirSync(abs);
91
+ }
92
+ catch {
93
+ return;
94
+ }
95
+ for (const name of entries.sort()) {
96
+ if (TARBALL_EXCLUDES.includes(name))
97
+ continue;
98
+ const relPath = rel ? `${rel}/${name}` : name;
99
+ let st;
100
+ try {
101
+ st = lstatSync(join(abs, name));
102
+ }
103
+ catch {
104
+ continue;
105
+ }
106
+ if (st.isSymbolicLink())
107
+ continue;
108
+ if (isDotenvName(name)) {
109
+ found.push(relPath);
110
+ continue;
111
+ }
112
+ if (st.isDirectory())
113
+ walk(join(abs, name), relPath);
114
+ }
115
+ };
116
+ walk(dir, "");
117
+ return found;
118
+ }
119
+ /** Build OUTPUT that can legitimately be shipped (a prebuilt frontend) but
120
+ * never counts as "source" on its own — a checkout with nothing else left
121
+ * in it (a branch switch that removed the tracked files, a wrong cwd) is
122
+ * exactly the upload that fails late in the builder with a bare "could not
123
+ * determine how to build the app". */
124
+ const OUTPUT_ONLY_DIRS = new Set(["dist", "build", "out", ".next", ".nuxt", "target", ".output", "coverage"]);
125
+ /**
126
+ * The names at the root of what `makeTarball` would ship that are actual
127
+ * source: anything that is not an excluded cache, not build output, and not
128
+ * OS/editor noise. Empty means there is nothing for a builder to work from.
129
+ */
130
+ export function sourceRootEntries(dir) {
131
+ return readdirSync(dir).filter((name) => !TARBALL_EXCLUDES.includes(name) &&
132
+ !OUTPUT_ONLY_DIRS.has(name) &&
133
+ !isDotenvName(name) &&
134
+ name !== ".DS_Store" &&
135
+ !name.startsWith("._"));
136
+ }
137
+ /**
138
+ * The file list `makeTarball` ships when `dir` is a git checkout: what git
139
+ * itself considers tracked or untracked-but-not-ignored, so `.gitignore` is
140
+ * honoured exactly and build output such as `.next`, `.open-next` or
141
+ * `.wrangler` never ships (the 471 MB marketing-site pack, 2026-09-27). Null
142
+ * when `dir` is not a git checkout or git is unavailable; the caller then
143
+ * falls back to the static excludes plus `.synteqignore`. Paths are relative
144
+ * to `dir`. Dotenv files (`isDotenvName`) are left out even when git tracks
145
+ * them.
146
+ */
147
+ export function gitSourceList(dir) {
148
+ return gitSourceListRaw(dir)?.filter((f) => !hasDotenvSegment(f)) ?? null;
149
+ }
150
+ /** `gitSourceList` before the dotenv rule: what git says is source. */
151
+ function gitSourceListRaw(dir) {
152
+ const probe = spawnSync("git", ["-C", dir, "rev-parse", "--is-inside-work-tree"], { encoding: "utf8" });
153
+ if (probe.status !== 0 || probe.stdout.trim() !== "true")
154
+ return null;
155
+ const ls = spawnSync("git", ["-C", dir, "ls-files", "--cached", "--others", "--exclude-standard", "-z"], { encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
156
+ if (ls.status !== 0)
157
+ return null;
158
+ const excluded = new Set(TARBALL_EXCLUDES);
159
+ return ls.stdout
160
+ .split("\0")
161
+ .filter((f) => f.length > 0)
162
+ .filter((f) => !f.split("/").some((seg) => excluded.has(seg)))
163
+ .filter((f) => existsSync(join(dir, f)));
164
+ }
165
+ /** Extra `--exclude` patterns from a `.synteqignore` in `dir` (one per line,
166
+ * `#` comments) for folders that are not git checkouts. */
167
+ export function synteqIgnorePatterns(dir) {
168
+ const f = join(dir, ".synteqignore");
169
+ if (!existsSync(f))
170
+ return [];
171
+ return readFileSync(f, "utf8")
172
+ .split("\n")
173
+ .map((l) => l.trim())
174
+ .filter((l) => l && !l.startsWith("#"));
175
+ }
176
+ /**
177
+ * Download a source tarball to a temp file — `synteq deploy --source-url`.
178
+ * The CLI fetches it and uploads it the way a packed directory goes up: the
179
+ * platform never dials a customer-supplied URL. Returns the file's path; the
180
+ * caller removes its directory, as with makeTarball.
181
+ *
182
+ * Errors name the host only: a pre-signed URL is a credential.
183
+ */
184
+ export async function fetchSourceTarball(rawUrl, opts = {}) {
185
+ let url;
186
+ try {
187
+ url = new URL(rawUrl);
188
+ }
189
+ catch {
190
+ throw new Error("--source-url is not a valid URL.");
191
+ }
192
+ if (url.protocol !== "https:" && url.protocol !== "http:") {
193
+ throw new Error("--source-url must be an http(s) URL.");
194
+ }
195
+ let res;
196
+ try {
197
+ res = await (opts.fetchFn ?? fetch)(url, { redirect: "follow" });
198
+ }
199
+ catch {
200
+ throw new Error(`Could not reach ${url.host} to download the source tarball.`);
201
+ }
202
+ if (!res.ok || !res.body) {
203
+ throw new Error(`${url.host} answered ${res.status} for the source tarball.`);
204
+ }
205
+ const tooLarge = () => new Error(`The source tarball is larger than your plan's ${Math.floor((opts.maxBytes ?? 0) / 1_000_000)} MB source limit.`);
206
+ const announced = Number(res.headers.get("content-length") ?? 0);
207
+ if (opts.maxBytes && announced > opts.maxBytes)
208
+ throw tooLarge();
209
+ const out = join(mkdtempSync(join(tmpdir(), "synteq-deploy-")), "source.tgz");
210
+ let head = Buffer.alloc(0);
211
+ let size = 0;
212
+ const meter = new Transform({
213
+ transform(chunk, _enc, cb) {
214
+ size += chunk.length;
215
+ if (head.length < 2)
216
+ head = Buffer.concat([head, chunk]).subarray(0, 2);
217
+ cb(opts.maxBytes && size > opts.maxBytes ? tooLarge() : null, chunk);
218
+ },
219
+ });
220
+ try {
221
+ await pipeline(Readable.fromWeb(res.body), meter, createWriteStream(out));
222
+ if (head.length < 2 || head[0] !== 0x1f || head[1] !== 0x8b) {
223
+ throw new Error(`${url.host} did not return a gzip tarball (.tar.gz / .tgz).`);
224
+ }
225
+ }
226
+ catch (e) {
227
+ rmSync(dirname(out), { recursive: true, force: true });
228
+ if (e instanceof Error && /source (tarball|limit)|gzip tarball/.test(e.message))
229
+ throw e;
230
+ throw new Error(`The download from ${url.host} was interrupted.`);
231
+ }
232
+ return out;
233
+ }
234
+ export function makeTarball(dir) {
235
+ const out = join(mkdtempSync(join(tmpdir(), "synteq-deploy-")), "source.tgz");
236
+ const files = gitSourceList(dir);
237
+ let args;
238
+ let stdinList = null;
239
+ if (files !== null) {
240
+ // git decides what is source: tracked + untracked-not-ignored files.
241
+ args = ["-czf", out, "-C", dir, "--null", "-T", "-"];
242
+ stdinList = files.map((f) => `./${f}`).join("\0") + (files.length ? "\0" : "");
243
+ }
244
+ else {
245
+ // Dotenv files go in by the basenames actually present (`.env`,
246
+ // `.env.local`...): a basename `--exclude` matches at any depth on both
247
+ // GNU tar and bsdtar, and naming only the files found keeps
248
+ // `.env.example` — tar has no way to say `.env.*` minus the docs.
249
+ const dotenv = [...new Set(dotenvFiles(dir).map((f) => f.slice(f.lastIndexOf("/") + 1)))];
250
+ const excludes = [...TARBALL_EXCLUDES, ...dotenv, ...synteqIgnorePatterns(dir)];
251
+ args = ["-czf", out, ...excludes.flatMap((e) => ["--exclude", e]), "-C", dir, "."];
252
+ }
253
+ return new Promise((res, rej) => {
254
+ // COPYFILE_DISABLE: macOS bsdtar otherwise writes AppleDouble `._name`
255
+ // entries next to every file with extended attributes, which the builder
256
+ // then lists as if they were source.
257
+ const p = spawn("tar", args, {
258
+ stdio: [stdinList === null ? "ignore" : "pipe", "ignore", "pipe"],
259
+ env: { ...process.env, COPYFILE_DISABLE: "1" },
260
+ });
261
+ if (stdinList !== null && p.stdin)
262
+ p.stdin.end(stdinList);
263
+ let stderr = "";
264
+ p.stderr?.on("data", (d) => (stderr += d.toString()));
265
+ p.on("error", (e) => rej(new Error(`Could not run tar (${e.message}). Is tar installed?`)));
266
+ p.on("close", (code) => code === 0 ? res(out) : rej(new Error(`tar exited ${code}: ${stderr.trim() || "unknown error"}`)));
267
+ });
268
+ }
269
+ export function humanSize(path) {
270
+ try {
271
+ const bytes = statSync(path).size;
272
+ if (bytes < 1024)
273
+ return `${bytes} B`;
274
+ if (bytes < 1024 * 1024)
275
+ return `${(bytes / 1024).toFixed(1)} KB`;
276
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
277
+ }
278
+ catch {
279
+ return "?";
280
+ }
281
+ }
282
+ export function sleep(ms) {
283
+ return new Promise((r) => setTimeout(r, ms));
284
+ }
285
+ // ── Direct source upload (task P3) ──────────────────────────────────────
286
+ //
287
+ // `synteq deploy --dir` used to send the whole tarball as the raw body of
288
+ // POST /apps/{id}/deploys/source. A proxy in front of the API rejects
289
+ // bodies over 100 MB, and the archive can legitimately be ~1 GB, so the
290
+ // direct path instead PUTs the file straight to the object store via a
291
+ // presigned URL the API mints, then tells the API which object to build
292
+ // from. `uploadSource()` is the one entry point both `commands/deploy.ts`
293
+ // and `tui/screens/Deploy.tsx` call, so the two surfaces can't diverge on
294
+ // retry/fallback behaviour.
295
+ /** Packed-source cap the CLI falls back to the raw-body upload under, when
296
+ * the cluster doesn't support direct upload at all (D6 in the design doc). */
297
+ export const RAW_FALLBACK_MAX_BYTES = 95_000_000;
298
+ const UPLOAD_MAX_ATTEMPTS = 3;
299
+ const UPLOAD_BACKOFF_MS = [1000, 3000];
300
+ /** Thrown by `putFile` for a non-2xx response from the upload host, so
301
+ * `uploadSource` can tell a signature/size mismatch (403 — do not retry)
302
+ * apart from a transient 5xx (retry with a fresh URL) without parsing
303
+ * strings. */
304
+ export class UploadPutError extends Error {
305
+ status;
306
+ constructor(status, message) {
307
+ super(message);
308
+ this.status = status;
309
+ this.name = "UploadPutError";
310
+ }
311
+ }
312
+ /**
313
+ * SHA-256 of a file, streamed — never loads the file into memory. The
314
+ * archive can be ~1 GB; `crypto.createHash` over `fs.createReadStream` is
315
+ * the only way to hash that without a huge Buffer.
316
+ */
317
+ export function sha256File(path) {
318
+ return new Promise((resolvePromise, reject) => {
319
+ const hash = createHash("sha256");
320
+ const stream = createReadStream(path);
321
+ stream.on("error", reject);
322
+ stream.on("data", (chunk) => hash.update(chunk));
323
+ stream.on("end", () => resolvePromise(hash.digest("hex")));
324
+ });
325
+ }
326
+ /**
327
+ * PUT a file's exact bytes to a presigned URL, streamed (never buffers the
328
+ * whole file). Uses `node:http`/`node:https` directly rather than `fetch`
329
+ * with a stream body: a streamed fetch body can fall back to chunked
330
+ * transfer-encoding instead of sending an explicit `Content-Length`, and the
331
+ * signed URL's signature covers `Content-Length` — a request that arrives
332
+ * chunked (or with any other length) is refused with 403. `http.request`
333
+ * with the header set explicitly always sends exactly that Content-Length
334
+ * and never adds Transfer-Encoding (verified against a local test server —
335
+ * see test/apps-support/upload.test.ts).
336
+ *
337
+ * Sends no `Authorization` header — the URL itself is the credential, and
338
+ * the caller's bearer token/API key must never reach the object-store host.
339
+ * Resolves on 2xx; rejects with `UploadPutError` (carrying the status) on
340
+ * any other response, or with the underlying error for a network failure.
341
+ *
342
+ * Settles EXACTLY ONCE no matter how the connection ends: a clean 2xx/non-2xx
343
+ * response, a request-level error, a response-level error/abort, the
344
+ * underlying connection closing before a response ever completed (I-2 — a
345
+ * server that sends headers and part of a body, then drops the socket, used
346
+ * to leave this promise unsettled forever), or a 300s stall with no response
347
+ * (`req.setTimeout`, matching the platform's own idle timeout). The file read
348
+ * stream is destroyed whenever the request settles, whichever way.
349
+ */
350
+ export function putFile(url, path, size, headers, onProgress, opts = {}) {
351
+ return new Promise((resolvePromise, reject) => {
352
+ let target;
353
+ try {
354
+ target = new URL(url);
355
+ }
356
+ catch (e) {
357
+ reject(e instanceof Error ? e : new Error(String(e)));
358
+ return;
359
+ }
360
+ const isHttps = target.protocol === "https:";
361
+ const doRequest = opts.requestFn ?? (isHttps ? httpsRequest : httpRequest);
362
+ // The signed URL's signature covers Content-Length; make sure OUR value
363
+ // wins and nothing upstream can sneak in a Transfer-Encoding instead.
364
+ const reqHeaders = { ...headers };
365
+ for (const k of Object.keys(reqHeaders)) {
366
+ if (k.toLowerCase() === "transfer-encoding")
367
+ delete reqHeaders[k];
368
+ }
369
+ reqHeaders["Content-Length"] = String(size);
370
+ const reqOptions = { method: "PUT", headers: reqHeaders };
371
+ if (isHttps && opts.insecure)
372
+ reqOptions.rejectUnauthorized = false;
373
+ let settled = false;
374
+ const stream = createReadStream(path);
375
+ const finish = (err) => {
376
+ if (settled)
377
+ return;
378
+ settled = true;
379
+ stream.destroy();
380
+ if (err)
381
+ reject(err);
382
+ else
383
+ resolvePromise();
384
+ };
385
+ const req = doRequest(target, reqOptions, (res) => {
386
+ let body = "";
387
+ res.setEncoding("utf8");
388
+ res.on("data", (chunk) => {
389
+ body += chunk;
390
+ });
391
+ res.on("end", () => {
392
+ const status = res.statusCode ?? 0;
393
+ if (status >= 200 && status < 300) {
394
+ finish();
395
+ }
396
+ else {
397
+ finish(new UploadPutError(status, `Upload PUT failed with HTTP ${status}${body ? `: ${body.slice(0, 300)}` : ""}`));
398
+ }
399
+ });
400
+ // A connection that dies mid-response (after headers, before `end`)
401
+ // used to leave the promise unsettled forever — I-2.
402
+ res.on("error", (e) => finish(e instanceof Error ? e : new Error(String(e))));
403
+ res.on("aborted", () => finish(new Error("Upload response aborted before it finished.")));
404
+ });
405
+ // Matches the platform's own 300s idle timeout on this link — a stalled
406
+ // upload (no response, connection never closes) settles rather than
407
+ // hanging the command forever.
408
+ req.setTimeout(300_000, () => {
409
+ req.destroy(new Error("upload stalled"));
410
+ });
411
+ req.on("error", (e) => finish(e));
412
+ // The connection closing without a completed response (no `end`, no
413
+ // `error`) is the other half of I-2 — `finish`'s guard makes this a
414
+ // no-op on the normal path, where `end`/`error` already settled first.
415
+ req.on("close", () => finish(new Error("Upload connection closed before a response was received.")));
416
+ let sent = 0;
417
+ stream.on("error", (e) => {
418
+ req.destroy();
419
+ finish(e);
420
+ });
421
+ stream.on("data", (chunk) => {
422
+ sent += chunk.length;
423
+ onProgress?.(sent, size);
424
+ });
425
+ stream.pipe(req);
426
+ });
427
+ }
428
+ /**
429
+ * Upload a packed source archive and trigger the build — the one path both
430
+ * `synteq deploy --dir` and the TUI's Deploy walkthrough call.
431
+ *
432
+ * Tries direct upload first: mint a presigned PUT (fresh on every attempt —
433
+ * a used or expired URL must not be reused; a retry's mint names the
434
+ * previous attempt's object key as `replaces`), stream the file to it, then
435
+ * tell the API which object to build. Up to 3 attempts total; a network
436
+ * error or a 5xx from the upload host retries (1s, then 3s backoff) with a
437
+ * brand-new URL; a 403 (signature/size mismatch) is reported immediately,
438
+ * never retried, since retrying with the same bytes would fail identically.
439
+ *
440
+ * Falls back to the existing raw-body upload (`apps.deploySource`, which
441
+ * buffers the whole tarball) when the API's upload-url endpoint answers
442
+ * 501/404/405 — an older server, or a cluster that doesn't support direct
443
+ * upload at all — and the pack is small enough for the proxy's 100 MB body
444
+ * limit (95 MB budget, D6). Otherwise fails with exit 1 (thrown Error) and
445
+ * says so.
446
+ */
447
+ export async function uploadSource(api, appId, archivePath, opts = {}) {
448
+ const size = statSync(archivePath).size;
449
+ const sha256 = await sha256File(archivePath);
450
+ const doSleep = opts.sleepFn ?? sleep;
451
+ // Each mint holds one of the user's few pending-upload slots on the API.
452
+ // A retry names the attempt it replaces so that slot is released first —
453
+ // otherwise one failed deploy (3 mints) fills them all. An older API
454
+ // rejects the unknown field with 422: mint once more without it, and stop
455
+ // sending it for the rest of this upload.
456
+ let previousKey;
457
+ let sendReplaces = true;
458
+ const mintUploadUrl = async () => {
459
+ const replaces = sendReplaces ? previousKey : undefined;
460
+ if (replaces === undefined)
461
+ return api.apps.sourceUploadUrl(appId, { sizeBytes: size, sha256 });
462
+ try {
463
+ return await api.apps.sourceUploadUrl(appId, { sizeBytes: size, sha256, replaces });
464
+ }
465
+ catch (e) {
466
+ if (!(e instanceof ApiError && e.status === 422))
467
+ throw e;
468
+ sendReplaces = false;
469
+ return api.apps.sourceUploadUrl(appId, { sizeBytes: size, sha256 });
470
+ }
471
+ };
472
+ for (let attempt = 0; attempt < UPLOAD_MAX_ATTEMPTS; attempt++) {
473
+ let mint;
474
+ try {
475
+ mint = await mintUploadUrl();
476
+ previousKey = mint.objectKey;
477
+ }
478
+ catch (e) {
479
+ // m-3: only an old orchestrator (no route at all) or a cluster that
480
+ // has switched direct upload off falls back to the raw body. A 404
481
+ // from `_load_owned_app` (the app itself is gone) shares the status
482
+ // code but carries FastAPI's real detail, never the generic
483
+ // route-missing "Not Found" — that case must surface as-is so a
484
+ // vanished app isn't reported as "direct upload not available".
485
+ if (e instanceof ApiError &&
486
+ (e.status === 501 || e.status === 405 || (e.status === 404 && e.detail === "Not Found"))) {
487
+ return fallbackToRawUpload(api, appId, archivePath, size);
488
+ }
489
+ if (e instanceof NetworkError && attempt < UPLOAD_MAX_ATTEMPTS - 1) {
490
+ await doSleep(UPLOAD_BACKOFF_MS[attempt] ?? 3000);
491
+ continue;
492
+ }
493
+ throw e;
494
+ }
495
+ let lastPercent = -1;
496
+ try {
497
+ await putFile(mint.uploadUrl, archivePath, size, mint.headers, (sentBytes, totalBytes) => {
498
+ // m-1: fire onProgress only when the integer percent changes — a
499
+ // raw per-chunk callback floods the TUI/spinner with ~16k updates
500
+ // for a 1 GB upload.
501
+ const pct = Math.floor((sentBytes / totalBytes) * 100);
502
+ if (pct !== lastPercent) {
503
+ lastPercent = pct;
504
+ opts.onProgress?.(pct);
505
+ }
506
+ }, { insecure: api.insecureTls });
507
+ }
508
+ catch (e) {
509
+ if (e instanceof UploadPutError) {
510
+ if (e.status === 403) {
511
+ throw new Error("Upload rejected (403) — the signed upload URL no longer matches this file. Re-run the deploy.");
512
+ }
513
+ if (e.status >= 500 && attempt < UPLOAD_MAX_ATTEMPTS - 1) {
514
+ await doSleep(UPLOAD_BACKOFF_MS[attempt] ?? 3000);
515
+ continue;
516
+ }
517
+ // m-2: retries exhausted (or a non-403 status that isn't retried at
518
+ // all) — surface as the CLI's NetworkError (no url: the upload host's
519
+ // URL/query/object key must never reach a message) so
520
+ // BaseCommand.fail() maps it to exit 3, matching the CLI's contract
521
+ // for a server/network fault. The synthetic cause below carries only
522
+ // the HTTP status, never `e.message` (which can hold up to 300 bytes
523
+ // of the object store's raw XML error body, itself carrying the
524
+ // object key).
525
+ throw new NetworkError(new Error(`Upload host returned HTTP ${e.status}.`));
526
+ }
527
+ // A network error talking to the upload host — same retry rule as a
528
+ // network error minting the URL. Once retries are exhausted, wrap the
529
+ // raw node error (ECONNREFUSED/ECONNRESET/etc — carries host:port at
530
+ // most, never a path or query) the same way, for the same exit code.
531
+ if (attempt < UPLOAD_MAX_ATTEMPTS - 1) {
532
+ await doSleep(UPLOAD_BACKOFF_MS[attempt] ?? 3000);
533
+ continue;
534
+ }
535
+ throw new NetworkError(e);
536
+ }
537
+ return api.apps.deploySourceObject(appId, mint.objectKey);
538
+ }
539
+ throw new NetworkError(new Error("Uploading the source failed after 3 attempts."));
540
+ }
541
+ async function fallbackToRawUpload(api, appId, archivePath, size) {
542
+ if (size > RAW_FALLBACK_MAX_BYTES) {
543
+ throw new Error(`Direct upload is not available on this cluster, and the packed source ` +
544
+ `(${(size / 1_000_000).toFixed(1)} MB) is over the ${RAW_FALLBACK_MAX_BYTES / 1_000_000} MB ` +
545
+ `limit without it. Reduce the archive size, or ask an operator to enable direct upload.`);
546
+ }
547
+ const tarball = await readFile(archivePath);
548
+ return api.apps.deploySource(appId, new Uint8Array(tarball));
549
+ }
550
+ /**
551
+ * Poll GET /apps/{id}/builds until the build reaches a terminal status or
552
+ * the timeout elapses. `onStatus` fires on every observed transition — of
553
+ * the status, or of the queue position / build VM while the status holds
554
+ * (bpool1) — so a caller can drive a spinner or a checklist. Returns the
555
+ * final build row — the caller decides what "timed out while building"
556
+ * means.
557
+ */
558
+ export async function pollBuild(api, appId, initial, opts) {
559
+ const deadline = Date.now() + opts.timeoutMs;
560
+ let current = initial;
561
+ opts.onStatus?.(current.status, current);
562
+ while (!BUILD_TERMINAL.has(current.status) && Date.now() < deadline) {
563
+ await sleep(opts.intervalMs);
564
+ try {
565
+ const builds = await api.apps.builds(appId);
566
+ const found = builds.find((b) => b.id === initial.id);
567
+ if (found && (found.status !== current.status || buildDetail(found) !== buildDetail(current))) {
568
+ opts.onStatus?.(found.status, found);
569
+ }
570
+ if (found)
571
+ current = found;
572
+ }
573
+ catch (e) {
574
+ // A dropped connection or a 5xx is worth polling through. An expired
575
+ // session (or any other 4xx) is not: it would be retried, silently,
576
+ // until the 30-minute deadline. Same classification as `waitFor`.
577
+ if (!isTransientError(e))
578
+ throw e;
579
+ }
580
+ }
581
+ return current;
582
+ }
583
+ /**
584
+ * The detail line behind a build's status while it waits or runs (bpool1):
585
+ * "Queued — position 2 of 5" from the orchestrator's build queue. Null when
586
+ * there is nothing to add (which build VM runs it stays internal).
587
+ */
588
+ export function buildDetail(b) {
589
+ if (b.status === "pending" && b.queuePosition && b.queueTotal) {
590
+ return `Queued — position ${b.queuePosition} of ${b.queueTotal}`;
591
+ }
592
+ return null;
593
+ }
594
+ /**
595
+ * After a successful build, the release it produced — matched by image
596
+ * digest, polled briefly because the status cron mints it a beat after the
597
+ * build flips to succeeded. Null when it has not appeared in time.
598
+ */
599
+ export async function resolveNewRelease(api, appId, build) {
600
+ if (!build.imageDigest)
601
+ return null;
602
+ for (let i = 0; i < 6; i++) {
603
+ try {
604
+ const releases = await api.apps.releases(appId);
605
+ const match = releases.find((r) => r.imageDigest === build.imageDigest);
606
+ if (match)
607
+ return { number: match.number, id: match.id };
608
+ }
609
+ catch {
610
+ // transient — try again
611
+ }
612
+ await sleep(1500);
613
+ }
614
+ return null;
615
+ }
616
+ /**
617
+ * Wait for a release to reach a terminal outcome — live, start_failed,
618
+ * health_failed, superseded — or return null when the window runs out (still
619
+ * rolling). This is what stops a caller from declaring "Deployed" on a build
620
+ * whose release then fails its rollout.
621
+ */
622
+ export async function waitForReleaseOutcome(api, appId, releaseNumber, timeoutSeconds, onTick) {
623
+ const deadline = Date.now() + timeoutSeconds * 1000;
624
+ while (Date.now() < deadline) {
625
+ try {
626
+ const releases = await api.apps.releases(appId);
627
+ const r = releases.find((x) => x.number === releaseNumber);
628
+ onTick?.(r?.status ?? null);
629
+ if (r && RELEASE_TERMINAL.has(r.status))
630
+ return r.status;
631
+ }
632
+ catch {
633
+ // transient — keep polling
634
+ }
635
+ await sleep(3000);
636
+ }
637
+ return null;
638
+ }
639
+ //# sourceMappingURL=deploy.js.map