@synteq/cli 0.1.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 (500) hide show
  1. package/bin/run.js +10 -0
  2. package/dist/api/client.d.ts +167 -0
  3. package/dist/api/client.js +367 -0
  4. package/dist/api/client.js.map +1 -0
  5. package/dist/api/errors.d.ts +16 -0
  6. package/dist/api/errors.js +76 -0
  7. package/dist/api/errors.js.map +1 -0
  8. package/dist/api/factory.d.ts +12 -0
  9. package/dist/api/factory.js +18 -0
  10. package/dist/api/factory.js.map +1 -0
  11. package/dist/api/types.d.ts +738 -0
  12. package/dist/api/types.js +2 -0
  13. package/dist/api/types.js.map +1 -0
  14. package/dist/auth/device-flow.d.ts +8 -0
  15. package/dist/auth/device-flow.js +36 -0
  16. package/dist/auth/device-flow.js.map +1 -0
  17. package/dist/auth/keychain.d.ts +3 -0
  18. package/dist/auth/keychain.js +34 -0
  19. package/dist/auth/keychain.js.map +1 -0
  20. package/dist/auth/logout.d.ts +4 -0
  21. package/dist/auth/logout.js +17 -0
  22. package/dist/auth/logout.js.map +1 -0
  23. package/dist/auth/tokens.d.ts +19 -0
  24. package/dist/auth/tokens.js +56 -0
  25. package/dist/auth/tokens.js.map +1 -0
  26. package/dist/base/command.d.ts +17 -0
  27. package/dist/base/command.js +89 -0
  28. package/dist/base/command.js.map +1 -0
  29. package/dist/commands/account/index.d.ts +5 -0
  30. package/dist/commands/account/index.js +9 -0
  31. package/dist/commands/account/index.js.map +1 -0
  32. package/dist/commands/account/show.d.ts +9 -0
  33. package/dist/commands/account/show.js +75 -0
  34. package/dist/commands/account/show.js.map +1 -0
  35. package/dist/commands/api.d.ts +17 -0
  36. package/dist/commands/api.js +144 -0
  37. package/dist/commands/api.js.map +1 -0
  38. package/dist/commands/bare-metal/index.d.ts +4 -0
  39. package/dist/commands/bare-metal/index.js +5 -0
  40. package/dist/commands/bare-metal/index.js.map +1 -0
  41. package/dist/commands/bare-metal/list.d.ts +9 -0
  42. package/dist/commands/bare-metal/list.js +38 -0
  43. package/dist/commands/bare-metal/list.js.map +1 -0
  44. package/dist/commands/bare-metal/order.d.ts +6 -0
  45. package/dist/commands/bare-metal/order.js +27 -0
  46. package/dist/commands/bare-metal/order.js.map +1 -0
  47. package/dist/commands/catalog.d.ts +11 -0
  48. package/dist/commands/catalog.js +115 -0
  49. package/dist/commands/catalog.js.map +1 -0
  50. package/dist/commands/config/index.d.ts +5 -0
  51. package/dist/commands/config/index.js +9 -0
  52. package/dist/commands/config/index.js.map +1 -0
  53. package/dist/commands/config/set-api-url.d.ts +10 -0
  54. package/dist/commands/config/set-api-url.js +43 -0
  55. package/dist/commands/config/set-api-url.js.map +1 -0
  56. package/dist/commands/config/show.d.ts +9 -0
  57. package/dist/commands/config/show.js +75 -0
  58. package/dist/commands/config/show.js.map +1 -0
  59. package/dist/commands/credits/add.d.ts +14 -0
  60. package/dist/commands/credits/add.js +117 -0
  61. package/dist/commands/credits/add.js.map +1 -0
  62. package/dist/commands/credits/index.d.ts +5 -0
  63. package/dist/commands/credits/index.js +9 -0
  64. package/dist/commands/credits/index.js.map +1 -0
  65. package/dist/commands/credits/list.d.ts +6 -0
  66. package/dist/commands/credits/list.js +43 -0
  67. package/dist/commands/credits/list.js.map +1 -0
  68. package/dist/commands/gpu/activity.d.ts +15 -0
  69. package/dist/commands/gpu/activity.js +8 -0
  70. package/dist/commands/gpu/activity.js.map +1 -0
  71. package/dist/commands/gpu/bandwidth.d.ts +14 -0
  72. package/dist/commands/gpu/bandwidth.js +8 -0
  73. package/dist/commands/gpu/bandwidth.js.map +1 -0
  74. package/dist/commands/gpu/boot-order.d.ts +15 -0
  75. package/dist/commands/gpu/boot-order.js +8 -0
  76. package/dist/commands/gpu/boot-order.js.map +1 -0
  77. package/dist/commands/gpu/console.d.ts +14 -0
  78. package/dist/commands/gpu/console.js +8 -0
  79. package/dist/commands/gpu/console.js.map +1 -0
  80. package/dist/commands/gpu/details.d.ts +15 -0
  81. package/dist/commands/gpu/details.js +8 -0
  82. package/dist/commands/gpu/details.js.map +1 -0
  83. package/dist/commands/gpu/index.d.ts +4 -0
  84. package/dist/commands/gpu/index.js +5 -0
  85. package/dist/commands/gpu/index.js.map +1 -0
  86. package/dist/commands/gpu/ip/add.d.ts +18 -0
  87. package/dist/commands/gpu/ip/add.js +8 -0
  88. package/dist/commands/gpu/ip/add.js.map +1 -0
  89. package/dist/commands/gpu/ip/list.d.ts +14 -0
  90. package/dist/commands/gpu/ip/list.js +8 -0
  91. package/dist/commands/gpu/ip/list.js.map +1 -0
  92. package/dist/commands/gpu/list.d.ts +6 -0
  93. package/dist/commands/gpu/list.js +13 -0
  94. package/dist/commands/gpu/list.js.map +1 -0
  95. package/dist/commands/gpu/order.d.ts +6 -0
  96. package/dist/commands/gpu/order.js +13 -0
  97. package/dist/commands/gpu/order.js.map +1 -0
  98. package/dist/commands/gpu/power.d.ts +17 -0
  99. package/dist/commands/gpu/power.js +8 -0
  100. package/dist/commands/gpu/power.js.map +1 -0
  101. package/dist/commands/gpu/reinstall.d.ts +22 -0
  102. package/dist/commands/gpu/reinstall.js +8 -0
  103. package/dist/commands/gpu/reinstall.js.map +1 -0
  104. package/dist/commands/gpu/resize.d.ts +22 -0
  105. package/dist/commands/gpu/resize.js +8 -0
  106. package/dist/commands/gpu/resize.js.map +1 -0
  107. package/dist/commands/gpu/stats.d.ts +14 -0
  108. package/dist/commands/gpu/stats.js +8 -0
  109. package/dist/commands/gpu/stats.js.map +1 -0
  110. package/dist/commands/help.d.ts +9 -0
  111. package/dist/commands/help.js +19 -0
  112. package/dist/commands/help.js.map +1 -0
  113. package/dist/commands/instant-metal/activity.d.ts +15 -0
  114. package/dist/commands/instant-metal/activity.js +8 -0
  115. package/dist/commands/instant-metal/activity.js.map +1 -0
  116. package/dist/commands/instant-metal/bandwidth.d.ts +14 -0
  117. package/dist/commands/instant-metal/bandwidth.js +8 -0
  118. package/dist/commands/instant-metal/bandwidth.js.map +1 -0
  119. package/dist/commands/instant-metal/boot-order.d.ts +15 -0
  120. package/dist/commands/instant-metal/boot-order.js +8 -0
  121. package/dist/commands/instant-metal/boot-order.js.map +1 -0
  122. package/dist/commands/instant-metal/console.d.ts +14 -0
  123. package/dist/commands/instant-metal/console.js +8 -0
  124. package/dist/commands/instant-metal/console.js.map +1 -0
  125. package/dist/commands/instant-metal/details.d.ts +15 -0
  126. package/dist/commands/instant-metal/details.js +8 -0
  127. package/dist/commands/instant-metal/details.js.map +1 -0
  128. package/dist/commands/instant-metal/index.d.ts +4 -0
  129. package/dist/commands/instant-metal/index.js +5 -0
  130. package/dist/commands/instant-metal/index.js.map +1 -0
  131. package/dist/commands/instant-metal/ip/add.d.ts +18 -0
  132. package/dist/commands/instant-metal/ip/add.js +8 -0
  133. package/dist/commands/instant-metal/ip/add.js.map +1 -0
  134. package/dist/commands/instant-metal/ip/list.d.ts +14 -0
  135. package/dist/commands/instant-metal/ip/list.js +8 -0
  136. package/dist/commands/instant-metal/ip/list.js.map +1 -0
  137. package/dist/commands/instant-metal/list.d.ts +6 -0
  138. package/dist/commands/instant-metal/list.js +13 -0
  139. package/dist/commands/instant-metal/list.js.map +1 -0
  140. package/dist/commands/instant-metal/order.d.ts +6 -0
  141. package/dist/commands/instant-metal/order.js +13 -0
  142. package/dist/commands/instant-metal/order.js.map +1 -0
  143. package/dist/commands/instant-metal/power.d.ts +17 -0
  144. package/dist/commands/instant-metal/power.js +8 -0
  145. package/dist/commands/instant-metal/power.js.map +1 -0
  146. package/dist/commands/instant-metal/reinstall.d.ts +22 -0
  147. package/dist/commands/instant-metal/reinstall.js +8 -0
  148. package/dist/commands/instant-metal/reinstall.js.map +1 -0
  149. package/dist/commands/instant-metal/resize.d.ts +22 -0
  150. package/dist/commands/instant-metal/resize.js +8 -0
  151. package/dist/commands/instant-metal/resize.js.map +1 -0
  152. package/dist/commands/instant-metal/stats.d.ts +14 -0
  153. package/dist/commands/instant-metal/stats.js +8 -0
  154. package/dist/commands/instant-metal/stats.js.map +1 -0
  155. package/dist/commands/login.d.ts +10 -0
  156. package/dist/commands/login.js +98 -0
  157. package/dist/commands/login.js.map +1 -0
  158. package/dist/commands/logout.d.ts +6 -0
  159. package/dist/commands/logout.js +20 -0
  160. package/dist/commands/logout.js.map +1 -0
  161. package/dist/commands/order/gpu.d.ts +6 -0
  162. package/dist/commands/order/gpu.js +13 -0
  163. package/dist/commands/order/gpu.js.map +1 -0
  164. package/dist/commands/order/instant-metal.d.ts +7 -0
  165. package/dist/commands/order/instant-metal.js +14 -0
  166. package/dist/commands/order/instant-metal.js.map +1 -0
  167. package/dist/commands/order/storage.d.ts +6 -0
  168. package/dist/commands/order/storage.js +11 -0
  169. package/dist/commands/order/storage.js.map +1 -0
  170. package/dist/commands/order/vps.d.ts +7 -0
  171. package/dist/commands/order/vps.js +14 -0
  172. package/dist/commands/order/vps.js.map +1 -0
  173. package/dist/commands/order.d.ts +6 -0
  174. package/dist/commands/order.js +46 -0
  175. package/dist/commands/order.js.map +1 -0
  176. package/dist/commands/payments/index.d.ts +5 -0
  177. package/dist/commands/payments/index.js +10 -0
  178. package/dist/commands/payments/index.js.map +1 -0
  179. package/dist/commands/payments/methods/add.d.ts +9 -0
  180. package/dist/commands/payments/methods/add.js +30 -0
  181. package/dist/commands/payments/methods/add.js.map +1 -0
  182. package/dist/commands/payments/methods/delete.d.ts +12 -0
  183. package/dist/commands/payments/methods/delete.js +58 -0
  184. package/dist/commands/payments/methods/delete.js.map +1 -0
  185. package/dist/commands/payments/methods/index.d.ts +5 -0
  186. package/dist/commands/payments/methods/index.js +9 -0
  187. package/dist/commands/payments/methods/index.js.map +1 -0
  188. package/dist/commands/payments/methods/list.d.ts +6 -0
  189. package/dist/commands/payments/methods/list.js +33 -0
  190. package/dist/commands/payments/methods/list.js.map +1 -0
  191. package/dist/commands/payments/methods/set-default.d.ts +9 -0
  192. package/dist/commands/payments/methods/set-default.js +31 -0
  193. package/dist/commands/payments/methods/set-default.js.map +1 -0
  194. package/dist/commands/projects/index.d.ts +5 -0
  195. package/dist/commands/projects/index.js +9 -0
  196. package/dist/commands/projects/index.js.map +1 -0
  197. package/dist/commands/projects/list.d.ts +6 -0
  198. package/dist/commands/projects/list.js +31 -0
  199. package/dist/commands/projects/list.js.map +1 -0
  200. package/dist/commands/resources.d.ts +11 -0
  201. package/dist/commands/resources.js +93 -0
  202. package/dist/commands/resources.js.map +1 -0
  203. package/dist/commands/settings/index.d.ts +6 -0
  204. package/dist/commands/settings/index.js +57 -0
  205. package/dist/commands/settings/index.js.map +1 -0
  206. package/dist/commands/settings/render-mode.d.ts +10 -0
  207. package/dist/commands/settings/render-mode.js +76 -0
  208. package/dist/commands/settings/render-mode.js.map +1 -0
  209. package/dist/commands/ssh-key/add.d.ts +11 -0
  210. package/dist/commands/ssh-key/add.js +89 -0
  211. package/dist/commands/ssh-key/add.js.map +1 -0
  212. package/dist/commands/ssh-key/delete.d.ts +12 -0
  213. package/dist/commands/ssh-key/delete.js +43 -0
  214. package/dist/commands/ssh-key/delete.js.map +1 -0
  215. package/dist/commands/ssh-key/list.d.ts +7 -0
  216. package/dist/commands/ssh-key/list.js +47 -0
  217. package/dist/commands/ssh-key/list.js.map +1 -0
  218. package/dist/commands/storage/attach.d.ts +12 -0
  219. package/dist/commands/storage/attach.js +48 -0
  220. package/dist/commands/storage/attach.js.map +1 -0
  221. package/dist/commands/storage/detach.d.ts +12 -0
  222. package/dist/commands/storage/detach.js +60 -0
  223. package/dist/commands/storage/detach.js.map +1 -0
  224. package/dist/commands/storage/list.d.ts +9 -0
  225. package/dist/commands/storage/list.js +41 -0
  226. package/dist/commands/storage/list.js.map +1 -0
  227. package/dist/commands/storage/order.d.ts +16 -0
  228. package/dist/commands/storage/order.js +223 -0
  229. package/dist/commands/storage/order.js.map +1 -0
  230. package/dist/commands/storage/resize.d.ts +16 -0
  231. package/dist/commands/storage/resize.js +129 -0
  232. package/dist/commands/storage/resize.js.map +1 -0
  233. package/dist/commands/team/details.d.ts +9 -0
  234. package/dist/commands/team/details.js +48 -0
  235. package/dist/commands/team/details.js.map +1 -0
  236. package/dist/commands/team/invite.d.ts +14 -0
  237. package/dist/commands/team/invite.js +65 -0
  238. package/dist/commands/team/invite.js.map +1 -0
  239. package/dist/commands/team.d.ts +15 -0
  240. package/dist/commands/team.js +123 -0
  241. package/dist/commands/team.js.map +1 -0
  242. package/dist/commands/tickets/close.d.ts +13 -0
  243. package/dist/commands/tickets/close.js +39 -0
  244. package/dist/commands/tickets/close.js.map +1 -0
  245. package/dist/commands/tickets/create.d.ts +16 -0
  246. package/dist/commands/tickets/create.js +111 -0
  247. package/dist/commands/tickets/create.js.map +1 -0
  248. package/dist/commands/tickets/departments.d.ts +7 -0
  249. package/dist/commands/tickets/departments.js +27 -0
  250. package/dist/commands/tickets/departments.js.map +1 -0
  251. package/dist/commands/tickets/list.d.ts +10 -0
  252. package/dist/commands/tickets/list.js +66 -0
  253. package/dist/commands/tickets/list.js.map +1 -0
  254. package/dist/commands/tickets/open.d.ts +10 -0
  255. package/dist/commands/tickets/open.js +70 -0
  256. package/dist/commands/tickets/open.js.map +1 -0
  257. package/dist/commands/tickets/reply.d.ts +13 -0
  258. package/dist/commands/tickets/reply.js +48 -0
  259. package/dist/commands/tickets/reply.js.map +1 -0
  260. package/dist/commands/transit.d.ts +9 -0
  261. package/dist/commands/transit.js +47 -0
  262. package/dist/commands/transit.js.map +1 -0
  263. package/dist/commands/tui/index.d.ts +7 -0
  264. package/dist/commands/tui/index.js +87 -0
  265. package/dist/commands/tui/index.js.map +1 -0
  266. package/dist/commands/vds/activity.d.ts +15 -0
  267. package/dist/commands/vds/activity.js +8 -0
  268. package/dist/commands/vds/activity.js.map +1 -0
  269. package/dist/commands/vds/bandwidth.d.ts +14 -0
  270. package/dist/commands/vds/bandwidth.js +8 -0
  271. package/dist/commands/vds/bandwidth.js.map +1 -0
  272. package/dist/commands/vds/boot-order.d.ts +15 -0
  273. package/dist/commands/vds/boot-order.js +8 -0
  274. package/dist/commands/vds/boot-order.js.map +1 -0
  275. package/dist/commands/vds/console.d.ts +14 -0
  276. package/dist/commands/vds/console.js +8 -0
  277. package/dist/commands/vds/console.js.map +1 -0
  278. package/dist/commands/vds/details.d.ts +15 -0
  279. package/dist/commands/vds/details.js +8 -0
  280. package/dist/commands/vds/details.js.map +1 -0
  281. package/dist/commands/vds/index.d.ts +4 -0
  282. package/dist/commands/vds/index.js +5 -0
  283. package/dist/commands/vds/index.js.map +1 -0
  284. package/dist/commands/vds/ip/add.d.ts +18 -0
  285. package/dist/commands/vds/ip/add.js +8 -0
  286. package/dist/commands/vds/ip/add.js.map +1 -0
  287. package/dist/commands/vds/ip/list.d.ts +14 -0
  288. package/dist/commands/vds/ip/list.js +8 -0
  289. package/dist/commands/vds/ip/list.js.map +1 -0
  290. package/dist/commands/vds/list.d.ts +6 -0
  291. package/dist/commands/vds/list.js +13 -0
  292. package/dist/commands/vds/list.js.map +1 -0
  293. package/dist/commands/vds/order.d.ts +6 -0
  294. package/dist/commands/vds/order.js +13 -0
  295. package/dist/commands/vds/order.js.map +1 -0
  296. package/dist/commands/vds/power.d.ts +17 -0
  297. package/dist/commands/vds/power.js +8 -0
  298. package/dist/commands/vds/power.js.map +1 -0
  299. package/dist/commands/vds/reinstall.d.ts +22 -0
  300. package/dist/commands/vds/reinstall.js +8 -0
  301. package/dist/commands/vds/reinstall.js.map +1 -0
  302. package/dist/commands/vds/resize.d.ts +22 -0
  303. package/dist/commands/vds/resize.js +8 -0
  304. package/dist/commands/vds/resize.js.map +1 -0
  305. package/dist/commands/vds/stats.d.ts +14 -0
  306. package/dist/commands/vds/stats.js +8 -0
  307. package/dist/commands/vds/stats.js.map +1 -0
  308. package/dist/commands/vps/activity.d.ts +13 -0
  309. package/dist/commands/vps/activity.js +50 -0
  310. package/dist/commands/vps/activity.js.map +1 -0
  311. package/dist/commands/vps/bandwidth.d.ts +9 -0
  312. package/dist/commands/vps/bandwidth.js +91 -0
  313. package/dist/commands/vps/bandwidth.js.map +1 -0
  314. package/dist/commands/vps/boot-order.d.ts +10 -0
  315. package/dist/commands/vps/boot-order.js +51 -0
  316. package/dist/commands/vps/boot-order.js.map +1 -0
  317. package/dist/commands/vps/console.d.ts +15 -0
  318. package/dist/commands/vps/console.js +198 -0
  319. package/dist/commands/vps/console.js.map +1 -0
  320. package/dist/commands/vps/details.d.ts +15 -0
  321. package/dist/commands/vps/details.js +78 -0
  322. package/dist/commands/vps/details.js.map +1 -0
  323. package/dist/commands/vps/ip/add.d.ts +17 -0
  324. package/dist/commands/vps/ip/add.js +212 -0
  325. package/dist/commands/vps/ip/add.js.map +1 -0
  326. package/dist/commands/vps/ip/list.d.ts +9 -0
  327. package/dist/commands/vps/ip/list.js +102 -0
  328. package/dist/commands/vps/ip/list.js.map +1 -0
  329. package/dist/commands/vps/list.d.ts +12 -0
  330. package/dist/commands/vps/list.js +55 -0
  331. package/dist/commands/vps/list.js.map +1 -0
  332. package/dist/commands/vps/order.d.ts +30 -0
  333. package/dist/commands/vps/order.js +497 -0
  334. package/dist/commands/vps/order.js.map +1 -0
  335. package/dist/commands/vps/power.d.ts +15 -0
  336. package/dist/commands/vps/power.js +123 -0
  337. package/dist/commands/vps/power.js.map +1 -0
  338. package/dist/commands/vps/reinstall.d.ts +20 -0
  339. package/dist/commands/vps/reinstall.js +166 -0
  340. package/dist/commands/vps/reinstall.js.map +1 -0
  341. package/dist/commands/vps/resize.d.ts +22 -0
  342. package/dist/commands/vps/resize.js +205 -0
  343. package/dist/commands/vps/resize.js.map +1 -0
  344. package/dist/commands/vps/stats.d.ts +12 -0
  345. package/dist/commands/vps/stats.js +83 -0
  346. package/dist/commands/vps/stats.js.map +1 -0
  347. package/dist/commands/whoami.d.ts +9 -0
  348. package/dist/commands/whoami.js +70 -0
  349. package/dist/commands/whoami.js.map +1 -0
  350. package/dist/config/paths.d.ts +3 -0
  351. package/dist/config/paths.js +17 -0
  352. package/dist/config/paths.js.map +1 -0
  353. package/dist/config/store.d.ts +15 -0
  354. package/dist/config/store.js +38 -0
  355. package/dist/config/store.js.map +1 -0
  356. package/dist/help.d.ts +9 -0
  357. package/dist/help.js +119 -0
  358. package/dist/help.js.map +1 -0
  359. package/dist/tui/App.d.ts +9 -0
  360. package/dist/tui/App.js +93 -0
  361. package/dist/tui/App.js.map +1 -0
  362. package/dist/tui/components/ActionButton.d.ts +9 -0
  363. package/dist/tui/components/ActionButton.js +52 -0
  364. package/dist/tui/components/ActionButton.js.map +1 -0
  365. package/dist/tui/components/AppHeader.d.ts +6 -0
  366. package/dist/tui/components/AppHeader.js +18 -0
  367. package/dist/tui/components/AppHeader.js.map +1 -0
  368. package/dist/tui/components/BackButton.d.ts +6 -0
  369. package/dist/tui/components/BackButton.js +15 -0
  370. package/dist/tui/components/BackButton.js.map +1 -0
  371. package/dist/tui/components/CenteredFrame.d.ts +8 -0
  372. package/dist/tui/components/CenteredFrame.js +14 -0
  373. package/dist/tui/components/CenteredFrame.js.map +1 -0
  374. package/dist/tui/components/Clickable.d.ts +9 -0
  375. package/dist/tui/components/Clickable.js +48 -0
  376. package/dist/tui/components/Clickable.js.map +1 -0
  377. package/dist/tui/components/ConfirmModal.d.ts +8 -0
  378. package/dist/tui/components/ConfirmModal.js +22 -0
  379. package/dist/tui/components/ConfirmModal.js.map +1 -0
  380. package/dist/tui/components/DataTable.d.ts +14 -0
  381. package/dist/tui/components/DataTable.js +26 -0
  382. package/dist/tui/components/DataTable.js.map +1 -0
  383. package/dist/tui/components/InputBar.d.ts +9 -0
  384. package/dist/tui/components/InputBar.js +10 -0
  385. package/dist/tui/components/InputBar.js.map +1 -0
  386. package/dist/tui/components/Menu.d.ts +16 -0
  387. package/dist/tui/components/Menu.js +78 -0
  388. package/dist/tui/components/Menu.js.map +1 -0
  389. package/dist/tui/components/NumberInput.d.ts +13 -0
  390. package/dist/tui/components/NumberInput.js +55 -0
  391. package/dist/tui/components/NumberInput.js.map +1 -0
  392. package/dist/tui/components/Panel.d.ts +13 -0
  393. package/dist/tui/components/Panel.js +11 -0
  394. package/dist/tui/components/Panel.js.map +1 -0
  395. package/dist/tui/components/Sparkline.d.ts +11 -0
  396. package/dist/tui/components/Sparkline.js +40 -0
  397. package/dist/tui/components/Sparkline.js.map +1 -0
  398. package/dist/tui/components/Spinner.d.ts +4 -0
  399. package/dist/tui/components/Spinner.js +11 -0
  400. package/dist/tui/components/Spinner.js.map +1 -0
  401. package/dist/tui/components/StatusBar.d.ts +6 -0
  402. package/dist/tui/components/StatusBar.js +9 -0
  403. package/dist/tui/components/StatusBar.js.map +1 -0
  404. package/dist/tui/components/Tip.d.ts +4 -0
  405. package/dist/tui/components/Tip.js +11 -0
  406. package/dist/tui/components/Tip.js.map +1 -0
  407. package/dist/tui/hooks/useApi.d.ts +12 -0
  408. package/dist/tui/hooks/useApi.js +15 -0
  409. package/dist/tui/hooks/useApi.js.map +1 -0
  410. package/dist/tui/hooks/useHeader.d.ts +18 -0
  411. package/dist/tui/hooks/useHeader.js +65 -0
  412. package/dist/tui/hooks/useHeader.js.map +1 -0
  413. package/dist/tui/hooks/useMouse.d.ts +27 -0
  414. package/dist/tui/hooks/useMouse.js +174 -0
  415. package/dist/tui/hooks/useMouse.js.map +1 -0
  416. package/dist/tui/hooks/useStdinCapture.d.ts +1 -0
  417. package/dist/tui/hooks/useStdinCapture.js +37 -0
  418. package/dist/tui/hooks/useStdinCapture.js.map +1 -0
  419. package/dist/tui/index.d.ts +12 -0
  420. package/dist/tui/index.js +19 -0
  421. package/dist/tui/index.js.map +1 -0
  422. package/dist/tui/router.d.ts +10 -0
  423. package/dist/tui/router.js +9 -0
  424. package/dist/tui/router.js.map +1 -0
  425. package/dist/tui/screens/Account.d.ts +2 -0
  426. package/dist/tui/screens/Account.js +212 -0
  427. package/dist/tui/screens/Account.js.map +1 -0
  428. package/dist/tui/screens/Billing.d.ts +2 -0
  429. package/dist/tui/screens/Billing.js +586 -0
  430. package/dist/tui/screens/Billing.js.map +1 -0
  431. package/dist/tui/screens/Dashboard.d.ts +2 -0
  432. package/dist/tui/screens/Dashboard.js +150 -0
  433. package/dist/tui/screens/Dashboard.js.map +1 -0
  434. package/dist/tui/screens/Order.d.ts +2 -0
  435. package/dist/tui/screens/Order.js +1169 -0
  436. package/dist/tui/screens/Order.js.map +1 -0
  437. package/dist/tui/screens/Resources.d.ts +2 -0
  438. package/dist/tui/screens/Resources.js +720 -0
  439. package/dist/tui/screens/Resources.js.map +1 -0
  440. package/dist/tui/screens/Tickets.d.ts +2 -0
  441. package/dist/tui/screens/Tickets.js +395 -0
  442. package/dist/tui/screens/Tickets.js.map +1 -0
  443. package/dist/tui/theme/colors.d.ts +12 -0
  444. package/dist/tui/theme/colors.js +29 -0
  445. package/dist/tui/theme/colors.js.map +1 -0
  446. package/dist/tui/theme/glyphs.d.ts +5 -0
  447. package/dist/tui/theme/glyphs.js +6 -0
  448. package/dist/tui/theme/glyphs.js.map +1 -0
  449. package/dist/ui/confirm.d.ts +7 -0
  450. package/dist/ui/confirm.js +18 -0
  451. package/dist/ui/confirm.js.map +1 -0
  452. package/dist/ui/glyphs.d.ts +18 -0
  453. package/dist/ui/glyphs.js +47 -0
  454. package/dist/ui/glyphs.js.map +1 -0
  455. package/dist/ui/payment.d.ts +33 -0
  456. package/dist/ui/payment.js +179 -0
  457. package/dist/ui/payment.js.map +1 -0
  458. package/dist/ui/pm-resolve.d.ts +3 -0
  459. package/dist/ui/pm-resolve.js +19 -0
  460. package/dist/ui/pm-resolve.js.map +1 -0
  461. package/dist/ui/portal.d.ts +1 -0
  462. package/dist/ui/portal.js +19 -0
  463. package/dist/ui/portal.js.map +1 -0
  464. package/dist/ui/render-mode.d.ts +10 -0
  465. package/dist/ui/render-mode.js +30 -0
  466. package/dist/ui/render-mode.js.map +1 -0
  467. package/dist/ui/sparkline.d.ts +6 -0
  468. package/dist/ui/sparkline.js +30 -0
  469. package/dist/ui/sparkline.js.map +1 -0
  470. package/dist/ui/spinner.d.ts +1 -0
  471. package/dist/ui/spinner.js +19 -0
  472. package/dist/ui/spinner.js.map +1 -0
  473. package/dist/ui/stats.d.ts +22 -0
  474. package/dist/ui/stats.js +71 -0
  475. package/dist/ui/stats.js.map +1 -0
  476. package/dist/ui/stdin.d.ts +1 -0
  477. package/dist/ui/stdin.js +8 -0
  478. package/dist/ui/stdin.js.map +1 -0
  479. package/dist/ui/table.d.ts +2 -0
  480. package/dist/ui/table.js +48 -0
  481. package/dist/ui/table.js.map +1 -0
  482. package/dist/ui/ticket-resolve.d.ts +2 -0
  483. package/dist/ui/ticket-resolve.js +17 -0
  484. package/dist/ui/ticket-resolve.js.map +1 -0
  485. package/dist/ui/volume-resolve.d.ts +3 -0
  486. package/dist/ui/volume-resolve.js +30 -0
  487. package/dist/ui/volume-resolve.js.map +1 -0
  488. package/dist/ui/vps-detail.d.ts +3 -0
  489. package/dist/ui/vps-detail.js +79 -0
  490. package/dist/ui/vps-detail.js.map +1 -0
  491. package/dist/ui/vps-resolve.d.ts +4 -0
  492. package/dist/ui/vps-resolve.js +24 -0
  493. package/dist/ui/vps-resolve.js.map +1 -0
  494. package/dist/ui/wait.d.ts +31 -0
  495. package/dist/ui/wait.js +66 -0
  496. package/dist/ui/wait.js.map +1 -0
  497. package/dist/ui/wizard.d.ts +33 -0
  498. package/dist/ui/wizard.js +168 -0
  499. package/dist/ui/wizard.js.map +1 -0
  500. package/package.json +142 -0
@@ -0,0 +1,1169 @@
1
+ import React, { useEffect, useState, useMemo, useCallback } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import TextInput from "ink-text-input";
4
+ import { Panel } from "../components/Panel.js";
5
+ import { Menu } from "../components/Menu.js";
6
+ import { StatusBar } from "../components/StatusBar.js";
7
+ import { Tip } from "../components/Tip.js";
8
+ import { Spinner } from "../components/Spinner.js";
9
+ import { CenteredFrame } from "../components/CenteredFrame.js";
10
+ import { BackButton } from "../components/BackButton.js";
11
+ import { NumberInput } from "../components/NumberInput.js";
12
+ import { ActionButton } from "../components/ActionButton.js";
13
+ import { ConfirmModal } from "../components/ConfirmModal.js";
14
+ import { useApi } from "../hooks/useApi.js";
15
+ import { useHeader } from "../hooks/useHeader.js";
16
+ import { useMousePause } from "../hooks/useMouse.js";
17
+ import { useRouter } from "../router.js";
18
+ import { colors, statusColor } from "../theme/colors.js";
19
+ import { glyphs } from "../theme/glyphs.js";
20
+ const emptyDraft = {
21
+ kind: null,
22
+ projectId: null,
23
+ planId: null,
24
+ customSpec: {},
25
+ storageSizeGb: null,
26
+ locationId: null,
27
+ imageId: null,
28
+ hostname: "",
29
+ authKind: null,
30
+ password: "",
31
+ sshPublicKey: "",
32
+ sshKeyId: null,
33
+ applyCredits: false,
34
+ paymentMethodId: null,
35
+ paymentKind: "method",
36
+ };
37
+ const PROGRESS_STEPS = [
38
+ "kind",
39
+ "project",
40
+ "plan",
41
+ "location",
42
+ "image",
43
+ "hostname",
44
+ "auth",
45
+ "payment",
46
+ "summary",
47
+ ];
48
+ function stepsForKind(kind) {
49
+ const base = ["kind", "project"];
50
+ if (kind === "vps") {
51
+ base.push("vps-spec", "image", "hostname", "auth", "payment", "summary");
52
+ }
53
+ else if (kind === "gpu" || kind === "dedicated") {
54
+ base.push("plan", "location", "image", "hostname", "auth", "payment", "summary");
55
+ }
56
+ else if (kind === "storage") {
57
+ base.push("size", "location", "hostname", "payment", "summary");
58
+ }
59
+ else {
60
+ return PROGRESS_STEPS;
61
+ }
62
+ return base;
63
+ }
64
+ export function Order() {
65
+ const api = useApi();
66
+ const { header } = useHeader();
67
+ const { navigate } = useRouter();
68
+ useMousePause(true);
69
+ const [step, setStep] = useState("kind");
70
+ const [draft, setDraft] = useState(emptyDraft);
71
+ const [cancelConfirm, setCancelConfirm] = useState(false);
72
+ const [result, setResult] = useState(null);
73
+ const [cat, setCat] = useState({
74
+ loading: true,
75
+ error: null,
76
+ orderCatalog: null,
77
+ storageCatalog: null,
78
+ projects: [],
79
+ paymentMethods: [],
80
+ creditBalance: 0,
81
+ balanceCurrency: "",
82
+ });
83
+ useEffect(() => {
84
+ let cancelled = false;
85
+ (async () => {
86
+ try {
87
+ const [orderCatalog, storageCatalog, projects, methods, balance] = await Promise.all([
88
+ api.vpsOrders.catalog().catch(() => null),
89
+ api.storage.catalog().catch(() => null),
90
+ api.projects.list().catch(() => []),
91
+ api.payments.methods().catch(() => []),
92
+ api.billing.balance().catch(() => ({})),
93
+ ]);
94
+ if (cancelled)
95
+ return;
96
+ const credit = typeof balance.credit === "number"
97
+ ? balance.credit
98
+ : typeof balance.credit === "string" ? Number(balance.credit) : 0;
99
+ setCat({
100
+ loading: false,
101
+ error: null,
102
+ orderCatalog,
103
+ storageCatalog,
104
+ projects,
105
+ paymentMethods: methods,
106
+ creditBalance: Number.isFinite(credit) && credit > 0 ? credit : 0,
107
+ balanceCurrency: balance.currency ?? "",
108
+ });
109
+ }
110
+ catch (e) {
111
+ if (cancelled)
112
+ return;
113
+ setCat((c) => ({ ...c, loading: false, error: e instanceof Error ? e.message : String(e) }));
114
+ }
115
+ })();
116
+ return () => {
117
+ cancelled = true;
118
+ };
119
+ }, [api]);
120
+ useEffect(() => {
121
+ if (!cat.loading && cat.projects.length && draft.projectId === null) {
122
+ const def = cat.projects.find((p) => p.is_default) ?? cat.projects[0];
123
+ setDraft((d) => ({ ...d, projectId: def.id }));
124
+ }
125
+ }, [cat.loading, cat.projects, draft.projectId]);
126
+ const visibleSteps = useMemo(() => stepsForKind(draft.kind), [draft.kind]);
127
+ const stepIndex = visibleSteps.indexOf(step);
128
+ const stepNumber = stepIndex >= 0 ? stepIndex + 1 : 1;
129
+ const totalSteps = visibleSteps.length;
130
+ const goPrev = useCallback(() => {
131
+ if (step === "kind")
132
+ return;
133
+ const idx = visibleSteps.indexOf(step);
134
+ if (idx > 0)
135
+ setStep(visibleSteps[idx - 1]);
136
+ }, [step, visibleSteps]);
137
+ const goNext = useCallback(() => {
138
+ const idx = visibleSteps.indexOf(step);
139
+ if (idx >= 0 && idx + 1 < visibleSteps.length)
140
+ setStep(visibleSteps[idx + 1]);
141
+ }, [step, visibleSteps]);
142
+ const textInputOwnsKeys = step === "hostname" ||
143
+ step === "size" ||
144
+ (step === "auth" && !!draft.authKind) ||
145
+ step === "vps-spec";
146
+ const canSwallowBack = !["submitting", "result"].includes(step) && !textInputOwnsKeys;
147
+ useInput((input, key) => {
148
+ if (cancelConfirm) {
149
+ if (input === "y" || input === "Y") {
150
+ navigate("dashboard");
151
+ }
152
+ else {
153
+ setCancelConfirm(false);
154
+ }
155
+ return;
156
+ }
157
+ if (step === "result") {
158
+ if (key.escape || key.return)
159
+ navigate("dashboard");
160
+ return;
161
+ }
162
+ if (step === "submitting")
163
+ return;
164
+ if (key.escape) {
165
+ if (step === "auth" && draft.authKind) {
166
+ setDraft((d) => ({ ...d, authKind: null }));
167
+ return;
168
+ }
169
+ if (step === "kind") {
170
+ setCancelConfirm(true);
171
+ }
172
+ else {
173
+ goPrev();
174
+ }
175
+ return;
176
+ }
177
+ if (canSwallowBack && (key.leftArrow || key.backspace || key.delete)) {
178
+ goPrev();
179
+ }
180
+ });
181
+ let body;
182
+ if (cat.loading) {
183
+ body = (React.createElement(Panel, { paddingX: 3, paddingY: 1, width: 80 },
184
+ React.createElement(Spinner, { label: "Loading catalog\u2026" })));
185
+ }
186
+ else if (cat.error) {
187
+ body = (React.createElement(Panel, { title: "Couldn't load catalog", paddingX: 3, paddingY: 1, width: 80 },
188
+ React.createElement(Text, { color: colors.error }, cat.error),
189
+ React.createElement(Box, { marginTop: 1 },
190
+ React.createElement(Text, { color: colors.dim }, "Esc to return to Dashboard."))));
191
+ }
192
+ else {
193
+ body = (React.createElement(StepRouter, { step: step, draft: draft, setDraft: setDraft, userEmail: header.userEmail, cat: cat, creditBalance: cat.creditBalance, balanceCurrency: cat.balanceCurrency, goNext: goNext, onSubmit: async () => {
194
+ setStep("submitting");
195
+ try {
196
+ if (draft.kind === "storage") {
197
+ const out = await api.storage.order({
198
+ name: draft.hostname || "volume",
199
+ size_gb: draft.storageSizeGb ?? 0,
200
+ location_id: draft.locationId,
201
+ payment_method_id: draft.paymentMethodId ?? undefined,
202
+ apply_credits: draft.applyCredits || undefined,
203
+ }, draft.projectId ?? undefined);
204
+ setResult({
205
+ ok: true,
206
+ orderId: out.order_id,
207
+ invoiceId: out.invoice_id,
208
+ status: out.status,
209
+ checkoutUrl: out.checkout_url,
210
+ });
211
+ }
212
+ else {
213
+ const body = {
214
+ project_id: draft.projectId,
215
+ plan_id: draft.kind === "vps" ? undefined : draft.planId ?? undefined,
216
+ custom_spec: draft.kind === "vps" ? buildCustomSpec(draft.customSpec) : undefined,
217
+ location_id: draft.locationId,
218
+ image_id: draft.imageId,
219
+ hostname: draft.hostname,
220
+ quantity: 1,
221
+ payment_kind: draft.paymentKind,
222
+ payment_method_id: draft.paymentMethodId ?? undefined,
223
+ apply_credits: draft.applyCredits || undefined,
224
+ password: draft.authKind === "password" ? draft.password : undefined,
225
+ ssh_public_key: draft.authKind === "ssh-paste" ? draft.sshPublicKey : undefined,
226
+ ssh_key_id: draft.authKind === "ssh-saved" && draft.sshKeyId
227
+ ? draft.sshKeyId
228
+ : undefined,
229
+ };
230
+ const out = await api.vpsOrders.create(body);
231
+ setResult({
232
+ ok: true,
233
+ orderId: out.order_id,
234
+ invoiceId: out.invoice_id,
235
+ status: out.status,
236
+ checkoutUrl: out.checkout_url,
237
+ });
238
+ }
239
+ setStep("result");
240
+ }
241
+ catch (e) {
242
+ setResult({ ok: false, error: e instanceof Error ? e.message : String(e) });
243
+ setStep("result");
244
+ }
245
+ }, onEdit: () => setStep("kind"), result: result }));
246
+ }
247
+ return (React.createElement(CenteredFrame, { fullHeight: true, maxWidth: 120 },
248
+ React.createElement(Box, { flexDirection: "row", paddingX: 2, width: "100%", flexShrink: 0 },
249
+ step !== "kind" && (React.createElement(React.Fragment, null,
250
+ React.createElement(BackButton, { onBack: goPrev }),
251
+ React.createElement(Text, { color: colors.dim }, " · "))),
252
+ React.createElement(Text, { color: colors.dim, wrap: "truncate-end" }, `New order · Step ${stepNumber} of ${totalSteps} `),
253
+ renderProgressDots(stepNumber, totalSteps)),
254
+ React.createElement(Box, { height: 1 }),
255
+ cancelConfirm ? (React.createElement(ConfirmModal, { title: "Cancel this order?", hint: "Your progress is kept until you leave the wizard.", onConfirm: () => navigate("dashboard"), onCancel: () => setCancelConfirm(false) })) : (React.createElement(Box, { flexGrow: 1, justifyContent: "center", alignItems: "flex-start" },
256
+ React.createElement(Box, { flexDirection: "column", width: 80 },
257
+ body,
258
+ React.createElement(LivePriceFooter, { draft: draft, cat: cat })))),
259
+ React.createElement(Box, { flexDirection: "column" },
260
+ React.createElement(Tip, null, tipForStep(step, draft, cancelConfirm)),
261
+ React.createElement(StatusBar, { right: [
262
+ header.teamName ? `Team: ${header.teamName}` : "",
263
+ header.projectName ? `Project: ${header.projectName}` : "",
264
+ header.balance ?? "",
265
+ ].filter(Boolean) }))));
266
+ }
267
+ function LivePriceFooter({ draft, cat, }) {
268
+ const est = useMemo(() => estimateMonthlyCost(draft, cat), [draft, cat]);
269
+ const shouldShow = draft.kind !== null && draft.projectId !== null;
270
+ if (!shouldShow)
271
+ return null;
272
+ return (React.createElement(Box, { paddingX: 2, paddingY: 0, marginTop: 1, borderStyle: "round", borderColor: colors.dim },
273
+ React.createElement(Text, { color: colors.dim }, "Est. monthly: "),
274
+ est ? (React.createElement(React.Fragment, null,
275
+ React.createElement(Text, { color: colors.accent, bold: true }, `$${est.amount.toFixed(2)}`),
276
+ React.createElement(Text, { color: colors.dim }, ` · ${est.label}`))) : (React.createElement(Text, { color: colors.dim }, "—"))));
277
+ }
278
+ function StepRouter(props) {
279
+ const { step, draft, setDraft, cat, goNext } = props;
280
+ switch (step) {
281
+ case "kind":
282
+ return React.createElement(KindStep, { draft: draft, setDraft: setDraft, goNext: goNext });
283
+ case "project":
284
+ return React.createElement(ProjectStep, { projects: cat.projects, draft: draft, setDraft: setDraft, goNext: goNext });
285
+ case "plan":
286
+ return (React.createElement(PlanStep, { catalog: cat.orderCatalog, draft: draft, setDraft: setDraft, goNext: goNext }));
287
+ case "vps-spec":
288
+ return (React.createElement(VpsSpecStep, { customConfig: cat.orderCatalog?.custom_config ?? null, locations: cat.orderCatalog?.locations ?? [], userEmail: props.userEmail, draft: draft, setDraft: setDraft, goNext: goNext }));
289
+ case "size":
290
+ return (React.createElement(SizeStep, { catalog: cat.storageCatalog, draft: draft, setDraft: setDraft, goNext: goNext }));
291
+ case "location":
292
+ return (React.createElement(LocationStep, { orderCatalog: cat.orderCatalog, storageCatalog: cat.storageCatalog, draft: draft, setDraft: setDraft, goNext: goNext }));
293
+ case "image":
294
+ return (React.createElement(ImageStep, { catalog: cat.orderCatalog, draft: draft, setDraft: setDraft, goNext: goNext }));
295
+ case "hostname":
296
+ return (React.createElement(HostnameStep, { draft: draft, setDraft: setDraft, goNext: goNext }));
297
+ case "auth":
298
+ return React.createElement(AuthStep, { draft: draft, setDraft: setDraft, goNext: goNext });
299
+ case "payment": {
300
+ const est = estimateMonthlyCost(draft, cat);
301
+ return (React.createElement(PaymentStep, { methods: cat.paymentMethods, creditBalance: cat.creditBalance, balanceCurrency: cat.balanceCurrency, estimatedCost: est?.amount ?? 0, draft: draft, setDraft: setDraft, goNext: goNext }));
302
+ }
303
+ case "summary":
304
+ return (React.createElement(SummaryStep, { draft: draft, cat: cat, onSubmit: props.onSubmit, onEdit: props.onEdit }));
305
+ case "submitting":
306
+ return (React.createElement(Panel, { paddingX: 3, paddingY: 1, width: 80 },
307
+ React.createElement(Spinner, { label: "Submitting order\u2026" })));
308
+ case "result":
309
+ return React.createElement(ResultStep, { result: props.result });
310
+ default:
311
+ return React.createElement(Text, null, "\u2014");
312
+ }
313
+ }
314
+ function KindStep({ draft, setDraft, goNext, }) {
315
+ const items = [
316
+ { value: "vps", label: "VPS", hint: "custom CPU/RAM/disk; shared or dedicated cores" },
317
+ { value: "gpu", label: "GPU", hint: "preset GPU-bearing plans" },
318
+ { value: "dedicated", label: "Dedicated", hint: "whole physical hosts" },
319
+ { value: "storage", label: "Storage", hint: "block volume (attach to a VPS later)" },
320
+ ];
321
+ return (React.createElement(Panel, { title: "What are you ordering?", paddingX: 3, paddingY: 1, width: 80 },
322
+ React.createElement(Menu, { items: items, initialIndex: Math.max(0, items.findIndex((i) => i.value === draft.kind)), onSelect: (v) => {
323
+ if (v !== draft.kind) {
324
+ setDraft((d) => ({
325
+ ...d,
326
+ kind: v,
327
+ planId: null,
328
+ customSpec: {},
329
+ storageSizeGb: null,
330
+ locationId: null,
331
+ imageId: null,
332
+ }));
333
+ }
334
+ else {
335
+ setDraft((d) => ({ ...d, kind: v }));
336
+ }
337
+ goNext();
338
+ } })));
339
+ }
340
+ function ProjectStep({ projects, draft, setDraft, goNext, }) {
341
+ if (projects.length === 0) {
342
+ return (React.createElement(Panel, { title: "No projects on this team", paddingX: 3, paddingY: 1, width: 80 },
343
+ React.createElement(Text, { color: colors.error }, "You need at least one project to bill against. Create one in the portal and try again."),
344
+ React.createElement(Box, { marginTop: 1 },
345
+ React.createElement(Text, { color: colors.dim }, "Press Esc to return to Dashboard."))));
346
+ }
347
+ const items = projects.map((p) => ({
348
+ value: p.id,
349
+ label: p.name,
350
+ hint: p.is_default ? "default" : (p.description ?? undefined),
351
+ }));
352
+ return (React.createElement(Panel, { title: "Pick a project to bill against", paddingX: 3, paddingY: 1, width: 80 },
353
+ React.createElement(Menu, { items: items, initialIndex: Math.max(0, items.findIndex((i) => i.value === draft.projectId)), onSelect: (v) => {
354
+ setDraft((d) => ({ ...d, projectId: v }));
355
+ goNext();
356
+ } })));
357
+ }
358
+ function PlanStep({ catalog, draft, setDraft, goNext, }) {
359
+ if (!catalog) {
360
+ return (React.createElement(Panel, { title: "Catalog unavailable", paddingX: 3, paddingY: 1, width: 80 },
361
+ React.createElement(Text, { color: colors.error }, "Couldn't load the order catalog. Try again later.")));
362
+ }
363
+ const category = draft.kind === "gpu" ? "gpu_cloud" : "dedicated";
364
+ const filtered = catalog.plans.filter((p) => p.category === category || p.category.replace("_", "-") === draft.kind);
365
+ const plans = filtered.length ? filtered : catalog.plans;
366
+ if (plans.length === 0) {
367
+ return (React.createElement(Panel, { title: "No plans in catalog", paddingX: 3, paddingY: 1, width: 80 },
368
+ React.createElement(Text, { color: colors.warning },
369
+ "No ",
370
+ draft.kind,
371
+ " plans are currently offered. Try a different resource type.")));
372
+ }
373
+ const items = plans.map((p) => ({
374
+ value: p.id,
375
+ label: planRowLabel(p),
376
+ hint: planRowHint(p),
377
+ disabled: p.in_stock ? undefined : "out of stock",
378
+ }));
379
+ return (React.createElement(Panel, { title: `Pick a ${draft.kind === "gpu" ? "GPU" : "dedicated"} plan`, paddingX: 3, paddingY: 1, width: 80 },
380
+ React.createElement(Menu, { items: items, initialIndex: Math.max(0, items.findIndex((i) => i.value === draft.planId)), onSelect: (v) => {
381
+ setDraft((d) => ({ ...d, planId: v, locationId: null }));
382
+ goNext();
383
+ } })));
384
+ }
385
+ function planRowLabel(p) {
386
+ return `${p.name} $${p.monthly_price}/mo`;
387
+ }
388
+ function planRowHint(p) {
389
+ const cores = `${p.cpu_cores}c`;
390
+ const ram = `${Math.round(p.ram_mb / 1024)}G`;
391
+ const disk = `${p.disk_gb}G`;
392
+ const extra = p.gpu_model ? ` · ${p.gpu_model}` : "";
393
+ return `${cores} ${ram} ${disk}${extra}`;
394
+ }
395
+ function VpsSpecStep({ customConfig, locations, userEmail, draft, setDraft, goNext, }) {
396
+ if (!customConfig?.enabled) {
397
+ return (React.createElement(Panel, { title: "Custom VPS unavailable", paddingX: 3, paddingY: 1, width: 80 },
398
+ React.createElement(Text, { color: colors.warning }, "Custom VPS spec is not currently offered. Pick GPU or Dedicated instead.")));
399
+ }
400
+ return (React.createElement(VpsSpecFieldsStep, { customConfig: customConfig, locations: locations, userEmail: userEmail, draft: draft, setDraft: setDraft, goNext: goNext }));
401
+ }
402
+ function VpsSpecFieldsStep({ customConfig, locations, userEmail, draft, setDraft, goNext, }) {
403
+ const api = useApi();
404
+ const dedicated = Boolean(draft.customSpec._dedicated);
405
+ const baseCfg = dedicated ? customConfig.dedicated : customConfig;
406
+ const initialCpu = draft.customSpec.cpu_cores ?? baseCfg.cpu_min;
407
+ const initialRamGb = (draft.customSpec.ram_mb ? draft.customSpec.ram_mb / 1024 : null) ??
408
+ customConfig.ram_min_gb;
409
+ const initialDisk = draft.customSpec.disk_gb ?? customConfig.disk_min_gb;
410
+ const initialBw = draft.customSpec.bandwidth_tb ?? customConfig.bandwidth_min_tb;
411
+ const [cpu, setCpu] = useState(String(initialCpu));
412
+ const [ramGb, setRamGb] = useState(String(initialRamGb));
413
+ const [disk, setDisk] = useState(String(initialDisk));
414
+ const [bw, setBw] = useState(String(initialBw));
415
+ const hasLocations = locations.length > 0;
416
+ const [pane, setPane] = useState(hasLocations && !draft.locationId ? "loc" : "spec");
417
+ const [specRow, setSpecRow] = useState(0);
418
+ const [locCursor, setLocCursor] = useState(() => {
419
+ const chosen = locations.findIndex((l) => l.id === draft.locationId);
420
+ return chosen >= 0 ? chosen : 0;
421
+ });
422
+ const [notifyState, setNotifyState] = useState("idle");
423
+ const [stockByLoc, setStockByLoc] = useState({});
424
+ const selLoc = hasLocations ? locations[locCursor] ?? null : null;
425
+ const selInStock = !hasLocations || stockByLoc[selLoc?.id ?? ""] !== false;
426
+ useEffect(() => {
427
+ if (!hasLocations)
428
+ return;
429
+ const cpuN = Number(cpu);
430
+ const ramN = Number(ramGb);
431
+ const diskN = Number(disk);
432
+ if (!(cpuN > 0 && ramN > 0 && diskN > 0))
433
+ return;
434
+ let cancelled = false;
435
+ const t = setTimeout(() => {
436
+ for (const loc of locations) {
437
+ api.vpsOrders
438
+ .customAvailability({
439
+ cpu_cores: cpuN,
440
+ ram_mb: ramN * 1024,
441
+ disk_gb: diskN,
442
+ location_id: loc.id,
443
+ dedicated,
444
+ })
445
+ .then((res) => {
446
+ if (cancelled)
447
+ return;
448
+ setStockByLoc((m) => ({ ...m, [loc.id]: res.available }));
449
+ })
450
+ .catch(() => {
451
+ });
452
+ }
453
+ }, 600);
454
+ return () => {
455
+ cancelled = true;
456
+ clearTimeout(t);
457
+ };
458
+ }, [api, hasLocations, locations, cpu, ramGb, disk, dedicated]);
459
+ useEffect(() => {
460
+ setDraft((d) => ({
461
+ ...d,
462
+ locationId: d.locationId ?? selLoc?.id ?? null,
463
+ customSpec: {
464
+ ...d.customSpec,
465
+ cpu_cores: d.customSpec.cpu_cores ?? initialCpu,
466
+ ram_mb: d.customSpec.ram_mb ?? initialRamGb * 1024,
467
+ disk_gb: d.customSpec.disk_gb ?? initialDisk,
468
+ bandwidth_tb: d.customSpec.bandwidth_tb ?? initialBw,
469
+ },
470
+ }));
471
+ // eslint-disable-next-line react-hooks/exhaustive-deps
472
+ }, []);
473
+ const cpuMin = baseCfg.cpu_min;
474
+ const cpuMax = baseCfg.cpu_max;
475
+ const cpuStep = baseCfg.cpu_stops?.length ? undefined : baseCfg.cpu_step;
476
+ const ramMin = customConfig.ram_min_gb;
477
+ const ramMax = customConfig.ram_max_gb;
478
+ const ramStep = customConfig.ram_stops?.length ? undefined : customConfig.ram_step_gb;
479
+ const diskMin = customConfig.disk_min_gb;
480
+ const diskMax = customConfig.disk_max_gb;
481
+ const diskStep = customConfig.disk_stops?.length ? undefined : customConfig.disk_step_gb;
482
+ const bwMin = customConfig.bandwidth_min_tb;
483
+ const bwMax = customConfig.bandwidth_max_tb;
484
+ const bwStep = customConfig.bandwidth_stops?.length ? undefined : customConfig.bandwidth_step_tb;
485
+ const moveLocation = (delta) => {
486
+ if (!hasLocations)
487
+ return;
488
+ setLocCursor((c) => Math.max(0, Math.min(locations.length - 1, c + delta)));
489
+ setNotifyState("idle");
490
+ };
491
+ const commit = () => {
492
+ if (!selInStock)
493
+ return;
494
+ const fields = [
495
+ { raw: cpu, min: cpuMin, max: cpuMax, row: 1 },
496
+ { raw: ramGb, min: ramMin, max: ramMax, row: 2 },
497
+ { raw: disk, min: diskMin, max: diskMax, row: 3 },
498
+ { raw: bw, min: bwMin, max: bwMax, row: 4 },
499
+ ];
500
+ for (const f of fields) {
501
+ const n = Number(f.raw);
502
+ if (!Number.isFinite(n) || n < f.min || n > f.max) {
503
+ setPane("spec");
504
+ setSpecRow(f.row);
505
+ return;
506
+ }
507
+ }
508
+ setDraft((d) => ({
509
+ ...d,
510
+ locationId: selLoc?.id ?? d.locationId,
511
+ customSpec: {
512
+ ...d.customSpec,
513
+ cpu_cores: Number(cpu),
514
+ ram_mb: Number(ramGb) * 1024,
515
+ disk_gb: Number(disk),
516
+ bandwidth_tb: Number(bw),
517
+ },
518
+ }));
519
+ goNext();
520
+ };
521
+ const requestNotify = async () => {
522
+ if (!selLoc || notifyState === "sending" || notifyState === "sent")
523
+ return;
524
+ if (!userEmail) {
525
+ setNotifyState("error");
526
+ return;
527
+ }
528
+ setNotifyState("sending");
529
+ try {
530
+ await api.marketing.notifyStock({
531
+ email: userEmail,
532
+ location_id: selLoc.id,
533
+ note: "CLI order wizard — custom VPS",
534
+ config_json: {
535
+ cpu_cores: Number(cpu) || null,
536
+ ram_gb: Number(ramGb) || null,
537
+ disk_gb: Number(disk) || null,
538
+ bandwidth_tb: Number(bw) || null,
539
+ dedicated,
540
+ },
541
+ });
542
+ setNotifyState("sent");
543
+ }
544
+ catch {
545
+ setNotifyState("error");
546
+ }
547
+ };
548
+ useInput((input, key) => {
549
+ if (key.tab) {
550
+ if (hasLocations)
551
+ setPane((p) => (p === "loc" ? "spec" : "loc"));
552
+ return;
553
+ }
554
+ if (pane === "loc") {
555
+ if (key.upArrow)
556
+ moveLocation(-1);
557
+ else if (key.downArrow)
558
+ moveLocation(1);
559
+ else if (key.return || key.rightArrow)
560
+ setPane("spec");
561
+ return;
562
+ }
563
+ if (key.upArrow) {
564
+ setSpecRow((r) => (r === 0 ? 5 : (r - 1)));
565
+ }
566
+ else if (key.downArrow) {
567
+ setSpecRow((r) => (r === 5 ? 0 : (r + 1)));
568
+ }
569
+ else if (specRow === 0 &&
570
+ (key.leftArrow || key.rightArrow || input === " ")) {
571
+ setDraft((d) => ({
572
+ ...d,
573
+ customSpec: { ...d.customSpec, _dedicated: !d.customSpec._dedicated },
574
+ }));
575
+ }
576
+ else if (specRow === 5 && key.return) {
577
+ if (selInStock)
578
+ commit();
579
+ else
580
+ void requestNotify();
581
+ }
582
+ });
583
+ const liveSet = useCallback((buf, setBuf, apply) => {
584
+ setBuf(buf);
585
+ const n = Number(buf);
586
+ if (Number.isFinite(n) && n > 0) {
587
+ setDraft((d) => ({ ...d, customSpec: { ...d.customSpec, ...apply(n) } }));
588
+ }
589
+ }, [setDraft]);
590
+ const specFocused = pane === "spec";
591
+ const buttonLabel = selInStock
592
+ ? "Continue"
593
+ : notifyState === "sent"
594
+ ? "Requested ✓"
595
+ : notifyState === "sending"
596
+ ? "Sending…"
597
+ : "Notify me";
598
+ const specPanel = (React.createElement(Panel, { title: "VPS specification", paddingX: 3, paddingY: 1, width: hasLocations ? 53 : 80, borderColor: specFocused ? colors.accent : undefined },
599
+ React.createElement(Box, { flexDirection: "column" },
600
+ React.createElement(Box, null,
601
+ React.createElement(Text, { color: specFocused && specRow === 0 ? colors.accent : colors.dim, bold: specFocused && specRow === 0 }, "CPU type"),
602
+ React.createElement(Text, { color: !dedicated ? colors.accent : colors.dim }, ` ${!dedicated ? "●" : "○"} Shared`),
603
+ React.createElement(Text, { color: dedicated ? colors.accent : colors.dim }, ` ${dedicated ? "●" : "○"} Dedicated`)),
604
+ React.createElement(Box, { marginTop: 1 }),
605
+ React.createElement(NumberInput, { label: "CPU cores", value: cpu, onChange: (v) => liveSet(v, setCpu, (n) => ({ cpu_cores: n })), onSubmit: () => setSpecRow(2), min: cpuMin, max: cpuMax, step: cpuStep, unit: "vCPU", focused: specFocused && specRow === 1 }),
606
+ React.createElement(Box, { marginTop: 1 }),
607
+ React.createElement(NumberInput, { label: "RAM", value: ramGb, onChange: (v) => liveSet(v, setRamGb, (n) => ({ ram_mb: n * 1024 })), onSubmit: () => setSpecRow(3), min: ramMin, max: ramMax, step: ramStep, unit: "GB", focused: specFocused && specRow === 2 }),
608
+ React.createElement(Box, { marginTop: 1 }),
609
+ React.createElement(NumberInput, { label: "Disk", value: disk, onChange: (v) => liveSet(v, setDisk, (n) => ({ disk_gb: n })), onSubmit: () => setSpecRow(4), min: diskMin, max: diskMax, step: diskStep, unit: "GB NVMe", focused: specFocused && specRow === 3 }),
610
+ React.createElement(Box, { marginTop: 1 }),
611
+ React.createElement(NumberInput, { label: "Bandwidth", value: bw, onChange: (v) => liveSet(v, setBw, (n) => ({ bandwidth_tb: n })), onSubmit: () => setSpecRow(5), min: bwMin, max: bwMax, step: bwStep, unit: "TB", focused: specFocused && specRow === 4 }),
612
+ React.createElement(Box, { marginTop: 1 },
613
+ React.createElement(ActionButton, { keybind: "\u21B5", label: buttonLabel, onPress: () => (selInStock ? commit() : void requestNotify()), focused: specFocused && specRow === 5 })),
614
+ !selInStock && selLoc ? (React.createElement(Box, null,
615
+ React.createElement(Text, { color: notifyState === "sent" ? colors.dim : colors.warning }, notifyState === "sent"
616
+ ? `We'll email you when ${selLoc.city ?? selLoc.name} is back in stock.`
617
+ : `${selLoc.city ?? selLoc.name} is out of stock — we can email you when it's back.`))) : null,
618
+ notifyState === "error" ? (React.createElement(Box, null,
619
+ React.createElement(Text, { color: colors.error }, "Couldn't save the notification \u2014 try again."))) : null,
620
+ React.createElement(Box, { marginTop: 1 },
621
+ React.createElement(Text, { color: colors.dim }, hasLocations
622
+ ? "Tab location/spec · ↑↓ move · ←/→ CPU type · Enter continue"
623
+ : "↑↓ to move · ←/→ CPU type · Enter to continue")))));
624
+ if (!hasLocations)
625
+ return specPanel;
626
+ return (React.createElement(Box, { flexDirection: "row", width: 80 },
627
+ React.createElement(Panel, { title: "Location", paddingX: 1, paddingY: 1, width: 27, borderColor: pane === "loc" ? colors.accent : undefined },
628
+ React.createElement(Box, { flexDirection: "column" }, locations.map((l, i) => {
629
+ const isCursor = i === locCursor;
630
+ const oos = stockByLoc[l.id] === false;
631
+ const name = [l.city ?? l.name, l.country ?? ""]
632
+ .filter(Boolean)
633
+ .join(", ");
634
+ return (React.createElement(Box, { key: l.id, flexDirection: "column" },
635
+ React.createElement(Box, null,
636
+ React.createElement(Text, { color: isCursor
637
+ ? pane === "loc"
638
+ ? colors.accent
639
+ : colors.text
640
+ : colors.dim }, isCursor ? `${glyphs.chevronRight} ` : " "),
641
+ React.createElement(Text, { wrap: "truncate-end", bold: isCursor, dimColor: oos, color: oos ? colors.dim : isCursor ? colors.accent : colors.text }, name)),
642
+ oos ? (React.createElement(Text, { color: colors.dim }, " out of stock")) : l.code ? (React.createElement(Text, { color: colors.dim }, ` ${l.code}`)) : null));
643
+ }))),
644
+ specPanel));
645
+ }
646
+ function SizeStep({ catalog, draft, setDraft, goNext, }) {
647
+ const [value, setValue] = useState(draft.storageSizeGb ? String(draft.storageSizeGb) : "");
648
+ const [err, setErr] = useState(null);
649
+ if (!catalog || !catalog.is_active) {
650
+ return (React.createElement(Panel, { title: "Storage unavailable", paddingX: 3, paddingY: 1, width: 80 },
651
+ React.createElement(Text, { color: colors.warning }, "Block storage is not currently offered on this team.")));
652
+ }
653
+ const useTb = catalog.price_unit === "tb";
654
+ const divisor = useTb ? 1024 : 1;
655
+ const unit = useTb ? "TB" : "GB";
656
+ const minUnits = Math.max(1, Math.floor((catalog.min_size_gb ?? 1) / divisor));
657
+ const maxUnits = Math.max(minUnits, Math.floor((catalog.max_size_gb ?? 10000) / divisor));
658
+ return (React.createElement(Panel, { title: `Volume size in ${unit} (${minUnits}–${maxUnits})`, paddingX: 3, paddingY: 1, width: 80 },
659
+ React.createElement(Box, null,
660
+ React.createElement(Text, { color: colors.dim }, `Size (${unit}): `),
661
+ React.createElement(TextInput, { value: value, onChange: (v) => {
662
+ setValue(v.replace(/[^0-9]/g, ""));
663
+ setErr(null);
664
+ }, onSubmit: (v) => {
665
+ const n = Number(v);
666
+ if (!Number.isFinite(n) || n < minUnits || n > maxUnits) {
667
+ setErr(`Enter a number between ${minUnits} and ${maxUnits}.`);
668
+ return;
669
+ }
670
+ const sizeGb = Math.round(n * divisor / (catalog.step_gb || 1)) * (catalog.step_gb || 1);
671
+ setDraft((d) => ({ ...d, storageSizeGb: sizeGb }));
672
+ goNext();
673
+ }, placeholder: `${minUnits}` })),
674
+ err ? (React.createElement(Box, { marginTop: 1 },
675
+ React.createElement(Text, { color: colors.error }, err))) : (React.createElement(Box, { marginTop: 1 },
676
+ React.createElement(Text, { color: colors.dim }, "Enter to confirm \u00B7 Esc to go back.")))));
677
+ }
678
+ function LocationStep({ orderCatalog, storageCatalog, draft, setDraft, goNext, }) {
679
+ const isStorage = draft.kind === "storage";
680
+ let items = [];
681
+ if (isStorage && storageCatalog) {
682
+ items = storageCatalog.locations.map((l) => ({
683
+ value: l.id,
684
+ label: `${l.city ?? l.name} ${l.country ?? ""}`.trim(),
685
+ hint: l.code,
686
+ disabled: l.in_stock ? undefined : "out of stock",
687
+ }));
688
+ }
689
+ else if (orderCatalog) {
690
+ const plan = draft.planId ? orderCatalog.plans.find((p) => p.id === draft.planId) : undefined;
691
+ items = orderCatalog.locations.map((l) => {
692
+ const stock = plan?.per_location_stock?.[l.id];
693
+ const oos = Boolean(stock && !stock.in_stock);
694
+ return {
695
+ value: l.id,
696
+ label: `${l.city}, ${l.country}`.trim(),
697
+ hint: l.code,
698
+ disabled: oos ? "out of stock" : undefined,
699
+ };
700
+ });
701
+ }
702
+ if (items.length === 0) {
703
+ return (React.createElement(Panel, { title: "No locations available", paddingX: 3, paddingY: 1, width: 80 },
704
+ React.createElement(Text, { color: colors.warning }, "Try a different plan or kind.")));
705
+ }
706
+ return (React.createElement(Panel, { title: "Pick a location", paddingX: 3, paddingY: 1, width: 80 },
707
+ React.createElement(Menu, { items: items, initialIndex: Math.max(0, items.findIndex((i) => i.value === draft.locationId)), onSelect: (v) => {
708
+ setDraft((d) => ({ ...d, locationId: v }));
709
+ goNext();
710
+ } })));
711
+ }
712
+ function ImageStep({ catalog, draft, setDraft, goNext, }) {
713
+ if (!catalog) {
714
+ return (React.createElement(Panel, { title: "Catalog unavailable", paddingX: 3, paddingY: 1, width: 80 },
715
+ React.createElement(Text, { color: colors.error }, "Couldn't load the catalog.")));
716
+ }
717
+ const items = catalog.images.map((i) => ({
718
+ value: i.id,
719
+ label: i.display_name,
720
+ hint: i.os_type,
721
+ }));
722
+ return (React.createElement(Panel, { title: "Pick an OS image", paddingX: 3, paddingY: 1, width: 80 },
723
+ React.createElement(Menu, { items: items, initialIndex: Math.max(0, items.findIndex((i) => i.value === draft.imageId)), onSelect: (v) => {
724
+ setDraft((d) => ({ ...d, imageId: v }));
725
+ goNext();
726
+ } })));
727
+ }
728
+ function HostnameStep({ draft, setDraft, goNext, }) {
729
+ const isStorage = draft.kind === "storage";
730
+ const [value, setValue] = useState(draft.hostname);
731
+ const [err, setErr] = useState(null);
732
+ const min = isStorage ? 1 : 5;
733
+ const max = isStorage ? 64 : 253;
734
+ return (React.createElement(Panel, { title: isStorage ? "Volume name" : "Hostname", paddingX: 3, paddingY: 1, width: 80 },
735
+ React.createElement(Box, null,
736
+ React.createElement(Text, { color: colors.dim }, isStorage ? "Name: " : "Hostname: "),
737
+ React.createElement(TextInput, { value: value, onChange: (v) => {
738
+ setValue(v);
739
+ setErr(null);
740
+ }, onSubmit: (v) => {
741
+ const trimmed = v.trim();
742
+ if (trimmed.length < min || trimmed.length > max) {
743
+ setErr(`${isStorage ? "Name" : "Hostname"} must be ${min}–${max} characters.`);
744
+ return;
745
+ }
746
+ setDraft((d) => ({ ...d, hostname: trimmed }));
747
+ goNext();
748
+ }, placeholder: isStorage ? "backups" : "web-01" })),
749
+ err ? (React.createElement(Box, { marginTop: 1 },
750
+ React.createElement(Text, { color: colors.error }, err))) : (React.createElement(Box, { marginTop: 1 },
751
+ React.createElement(Text, { color: colors.dim }, "Enter to confirm \u00B7 Esc to go back.")))));
752
+ }
753
+ function AuthStep({ draft, setDraft, goNext, }) {
754
+ const api = useApi();
755
+ const [value, setValue] = useState(draft.authKind === "password" ? draft.password : draft.sshPublicKey);
756
+ const [err, setErr] = useState(null);
757
+ const [savedKeys, setSavedKeys] = useState(null);
758
+ const [savedKeysErr, setSavedKeysErr] = useState(null);
759
+ useEffect(() => {
760
+ if (draft.authKind !== "ssh-saved" || savedKeys !== null)
761
+ return;
762
+ let abort = false;
763
+ (async () => {
764
+ try {
765
+ const keys = await api.sshKeys.list();
766
+ if (!abort)
767
+ setSavedKeys(keys);
768
+ }
769
+ catch (e) {
770
+ if (!abort)
771
+ setSavedKeysErr(e instanceof Error ? e.message : String(e));
772
+ }
773
+ })();
774
+ return () => {
775
+ abort = true;
776
+ };
777
+ }, [api, draft.authKind, savedKeys]);
778
+ if (!draft.authKind) {
779
+ const items = [
780
+ { value: "ssh-saved", label: "Use SSH key from account", hint: "pick from your saved keys" },
781
+ { value: "ssh-paste", label: "Paste SSH public key", hint: "ssh-ed25519 / ssh-rsa / ecdsa-…" },
782
+ { value: "password", label: "Root password", hint: "min 8 chars" },
783
+ ];
784
+ return (React.createElement(Panel, { title: "How should we authorise login?", paddingX: 3, paddingY: 1, width: 80 },
785
+ React.createElement(Menu, { items: items, onSelect: (v) => {
786
+ setDraft((d) => ({ ...d, authKind: v }));
787
+ } })));
788
+ }
789
+ if (draft.authKind === "ssh-saved") {
790
+ if (savedKeysErr) {
791
+ return (React.createElement(Panel, { title: "SSH keys on your account", paddingX: 3, paddingY: 1, width: 80 },
792
+ React.createElement(Text, { color: colors.error },
793
+ "Failed to load: ",
794
+ savedKeysErr),
795
+ React.createElement(Box, { marginTop: 1 },
796
+ React.createElement(Text, { color: colors.dim }, "Esc to go back \u00B7 pick a different auth method."))));
797
+ }
798
+ if (savedKeys === null) {
799
+ return (React.createElement(Panel, { title: "SSH keys on your account", paddingX: 3, paddingY: 1, width: 80 },
800
+ React.createElement(Spinner, { label: "Loading keys\u2026" })));
801
+ }
802
+ if (savedKeys.length === 0) {
803
+ return (React.createElement(Panel, { title: "No SSH keys on your account", paddingX: 3, paddingY: 1, width: 80 },
804
+ React.createElement(Text, { color: colors.dim }, "You don't have any saved SSH keys. Add one in the portal under Account \u2192 SSH keys, or pick a different auth method (Esc).")));
805
+ }
806
+ return (React.createElement(Panel, { title: "Pick a saved SSH key", paddingX: 3, paddingY: 1, width: 80 },
807
+ React.createElement(Menu, { items: savedKeys.map((k) => ({
808
+ value: k.id,
809
+ label: k.name,
810
+ hint: fingerprint(k.public_key),
811
+ })), onSelect: (id) => {
812
+ setDraft((d) => ({ ...d, sshKeyId: id }));
813
+ goNext();
814
+ } })));
815
+ }
816
+ const isPwd = draft.authKind === "password";
817
+ return (React.createElement(Panel, { title: isPwd ? "Root password" : "Paste OpenSSH public key", paddingX: 3, paddingY: 1, width: 80 },
818
+ React.createElement(Box, null,
819
+ React.createElement(Text, { color: colors.dim }, isPwd ? "Password: " : "Public key: "),
820
+ React.createElement(TextInput, { value: value, mask: isPwd ? "•" : undefined, onChange: (v) => {
821
+ setValue(v);
822
+ setErr(null);
823
+ }, onSubmit: (v) => {
824
+ if (isPwd) {
825
+ if (v.length < 8) {
826
+ setErr("Password must be ≥8 characters.");
827
+ return;
828
+ }
829
+ setDraft((d) => ({ ...d, password: v }));
830
+ }
831
+ else {
832
+ if (!(v.startsWith("ssh-") || v.startsWith("ecdsa-") || v.startsWith("sk-"))) {
833
+ setErr("Doesn't look like an OpenSSH public key.");
834
+ return;
835
+ }
836
+ setDraft((d) => ({ ...d, sshPublicKey: v }));
837
+ }
838
+ goNext();
839
+ }, placeholder: isPwd ? "hunter2hunter2" : "ssh-ed25519 AAAA…" })),
840
+ err ? (React.createElement(Box, { marginTop: 1 },
841
+ React.createElement(Text, { color: colors.error }, err))) : (React.createElement(Box, { marginTop: 1 },
842
+ React.createElement(Text, { color: colors.dim }, "Enter to confirm \u00B7 Esc to pick a different auth method.")))));
843
+ }
844
+ function PaymentStep({ methods, creditBalance, balanceCurrency, estimatedCost, draft, setDraft, goNext, }) {
845
+ const eligible = methods.filter((m) => !isAch(m));
846
+ const creditsCover = estimatedCost > 0 && creditBalance >= estimatedCost;
847
+ const rows = [
848
+ ...(creditsCover ? [{ kind: "credits-only" }] : []),
849
+ ...eligible.map((m) => ({ kind: "method", m })),
850
+ ...(creditBalance > 0 && !creditsCover ? [{ kind: "checkbox" }] : []),
851
+ { kind: "confirm" },
852
+ ];
853
+ const [cursor, setCursor] = useState(() => {
854
+ if (creditsCover)
855
+ return 0;
856
+ const sel = rows.findIndex((r) => r.kind === "method" && r.m.id === draft.paymentMethodId);
857
+ if (sel >= 0)
858
+ return sel;
859
+ const def = rows.findIndex((r) => r.kind === "method" && r.m.is_default);
860
+ return def >= 0 ? def : 0;
861
+ });
862
+ const [err, setErr] = useState(null);
863
+ const chosen = eligible.find((m) => m.id === draft.paymentMethodId) ?? null;
864
+ const isBitpay = chosen?.provider === "bitpay";
865
+ const creditsOnlySelected = draft.applyCredits && draft.paymentMethodId === null;
866
+ useEffect(() => {
867
+ if (creditsCover && draft.paymentMethodId === null && !draft.applyCredits) {
868
+ setDraft((d) => ({ ...d, applyCredits: true, paymentKind: "method" }));
869
+ }
870
+ // eslint-disable-next-line react-hooks/exhaustive-deps
871
+ }, []);
872
+ const activate = (row) => {
873
+ setErr(null);
874
+ if (row.kind === "credits-only") {
875
+ setDraft((d) => ({
876
+ ...d,
877
+ applyCredits: true,
878
+ paymentMethodId: null,
879
+ paymentKind: "method",
880
+ }));
881
+ }
882
+ else if (row.kind === "method") {
883
+ const bp = row.m.provider === "bitpay";
884
+ setDraft((d) => ({
885
+ ...d,
886
+ paymentMethodId: row.m.id,
887
+ paymentKind: bp ? "bitpay" : "method",
888
+ applyCredits: bp ? false : d.applyCredits,
889
+ }));
890
+ }
891
+ else if (row.kind === "checkbox") {
892
+ if (isBitpay)
893
+ return;
894
+ setDraft((d) => ({ ...d, applyCredits: !d.applyCredits }));
895
+ }
896
+ else {
897
+ if (!draft.paymentMethodId && !(draft.applyCredits && creditsCover)) {
898
+ setErr(creditBalance > 0
899
+ ? "Pick a payment method — credits don't cover the full amount."
900
+ : "Pick a payment method.");
901
+ return;
902
+ }
903
+ goNext();
904
+ }
905
+ };
906
+ useInput((input, key) => {
907
+ if (key.upArrow) {
908
+ setCursor((c) => (c === 0 ? rows.length - 1 : c - 1));
909
+ }
910
+ else if (key.downArrow) {
911
+ setCursor((c) => (c === rows.length - 1 ? 0 : c + 1));
912
+ }
913
+ else if (key.return || input === " ") {
914
+ const row = rows[cursor];
915
+ if (row)
916
+ activate(row);
917
+ }
918
+ });
919
+ if (eligible.length === 0 && !creditsCover) {
920
+ return (React.createElement(Panel, { title: "No saved payment methods", paddingX: 3, paddingY: 1, width: 80 },
921
+ React.createElement(Text, null, "You don't have any saved payment methods on this team yet."),
922
+ React.createElement(Box, { marginTop: 1 },
923
+ React.createElement(Text, { color: colors.dim }, "Open the Billing screen (Ctrl+B from the Dashboard) to add a card, PayPal mandate, or BitPay wallet, then come back.")),
924
+ React.createElement(Box, { marginTop: 1 },
925
+ React.createElement(Text, { color: colors.dim }, "Esc to go back."))));
926
+ }
927
+ const creditsLabel = `$${creditBalance.toFixed(2)}${balanceCurrency ? ` ${balanceCurrency}` : ""}`;
928
+ return (React.createElement(Panel, { title: "Payment", paddingX: 3, paddingY: 1, width: 80 },
929
+ React.createElement(Box, { flexDirection: "column" },
930
+ rows.map((row, i) => {
931
+ const focused = i === cursor;
932
+ const marker = focused ? `${glyphs.chevronRight} ` : " ";
933
+ if (row.kind === "credits-only") {
934
+ const on = creditsOnlySelected;
935
+ return (React.createElement(Box, { key: "credits-only" },
936
+ React.createElement(Text, { color: focused ? colors.accent : colors.dim }, marker),
937
+ React.createElement(Text, { bold: focused, color: on ? colors.accent : focused ? colors.text : colors.dim }, `${on ? "●" : "○"} Credits only`),
938
+ React.createElement(Text, { color: colors.dim }, ` ${creditsLabel} covers $${estimatedCost.toFixed(2)}`)));
939
+ }
940
+ if (row.kind === "method") {
941
+ const on = draft.paymentMethodId === row.m.id;
942
+ return (React.createElement(Box, { key: row.m.id },
943
+ React.createElement(Text, { color: focused ? colors.accent : colors.dim }, marker),
944
+ React.createElement(Text, { bold: focused, color: on ? colors.accent : focused ? colors.text : colors.dim }, `${on ? "●" : "○"} ${describePaymentMethod(row.m)}`),
945
+ row.m.provider === "bitpay" ? (React.createElement(Text, { color: colors.dim }, " crypto · opens hosted checkout")) : null));
946
+ }
947
+ if (row.kind === "checkbox") {
948
+ const disabled = isBitpay;
949
+ return (React.createElement(Box, { key: "checkbox", marginTop: 1 },
950
+ React.createElement(Text, { color: focused ? colors.accent : colors.dim }, marker),
951
+ React.createElement(Text, { bold: focused, color: disabled ? colors.dim : draft.applyCredits ? colors.accent : focused ? colors.text : colors.dim }, `[${draft.applyCredits ? "x" : " "}] Apply credit balance first`),
952
+ React.createElement(Text, { color: colors.dim }, disabled ? " (can't combine with crypto)" : ` ${creditsLabel}, remainder on the method`)));
953
+ }
954
+ return (React.createElement(Box, { key: "confirm", marginTop: 1, flexShrink: 0 },
955
+ React.createElement(ActionButton, { keybind: "\u21B5", label: "Confirm", onPress: () => activate(row), focused: focused })));
956
+ }),
957
+ isBitpay ? (React.createElement(Box, { marginTop: 1 },
958
+ React.createElement(Text, { color: colors.dim }, "BitPay opens a hosted checkout URL in your browser after submit."))) : null,
959
+ methods.length !== eligible.length ? (React.createElement(Box, { marginTop: 1 },
960
+ React.createElement(Text, { color: colors.dim }, "Stripe ACH hidden \u2014 it can't settle instant charges. Pay an existing invoice in Billing instead."))) : null,
961
+ err ? (React.createElement(Box, { marginTop: 1 },
962
+ React.createElement(Text, { color: colors.error }, err))) : null,
963
+ React.createElement(Box, { marginTop: 1 },
964
+ React.createElement(Text, { color: colors.dim }, "\u2191\u2193 move \u00B7 Enter or Space select \u00B7 Enter on Confirm to continue")))));
965
+ }
966
+ function isAch(m) {
967
+ return m.provider === "stripe" && m.card_brand === "us_bank_account";
968
+ }
969
+ function describePaymentMethod(m) {
970
+ const brand = m.card_brand ? capitalize(m.card_brand) : null;
971
+ const last4 = m.card_last4 ? `····${m.card_last4}` : null;
972
+ const provider = capitalize(m.provider);
973
+ const head = brand && last4 ? `${brand} ${last4}` : brand ?? last4 ?? provider;
974
+ const tail = [];
975
+ if (m.nickname)
976
+ tail.push(`(${m.nickname})`);
977
+ if (m.is_default)
978
+ tail.push("· default");
979
+ return tail.length ? `${head} ${tail.join(" ")}` : head;
980
+ }
981
+ function capitalize(s) {
982
+ return s.charAt(0).toUpperCase() + s.slice(1);
983
+ }
984
+ function SummaryStep({ draft, cat, onSubmit, onEdit, }) {
985
+ const projectName = cat.projects.find((p) => p.id === draft.projectId)?.name;
986
+ const locName = draft.kind === "storage"
987
+ ? cat.storageCatalog?.locations.find((l) => l.id === draft.locationId)?.city
988
+ : cat.orderCatalog?.locations.find((l) => l.id === draft.locationId)?.city;
989
+ const imageName = cat.orderCatalog?.images.find((i) => i.id === draft.imageId)?.display_name;
990
+ const plan = cat.orderCatalog?.plans.find((p) => p.id === draft.planId);
991
+ const chosenPm = cat.paymentMethods.find((m) => m.id === draft.paymentMethodId);
992
+ const specLine = draft.kind === "vps"
993
+ ? `${draft.customSpec.cpu_cores ?? "?"} vCPU · ${(draft.customSpec.ram_mb ?? 0) / 1024} GB RAM · ${draft.customSpec.disk_gb ?? "?"} GB disk${draft.customSpec.bandwidth_tb ? ` · ${draft.customSpec.bandwidth_tb} TB BW` : ""}${draft.customSpec._dedicated ? " · dedicated" : ""}`
994
+ : plan
995
+ ? `${plan.name} — ${plan.cpu_cores}c ${Math.round(plan.ram_mb / 1024)}G ${plan.disk_gb}G${plan.gpu_model ? ` · ${plan.gpu_model}` : ""}`
996
+ : undefined;
997
+ const paymentLabel = (() => {
998
+ const parts = [];
999
+ if (draft.applyCredits && cat.creditBalance > 0) {
1000
+ parts.push(`credits ($${cat.creditBalance.toFixed(2)}${cat.balanceCurrency ? ` ${cat.balanceCurrency}` : ""})`);
1001
+ }
1002
+ if (chosenPm)
1003
+ parts.push(describePaymentMethod(chosenPm));
1004
+ return parts.length ? parts.join(" + ") : "(none)";
1005
+ })();
1006
+ return (React.createElement(Panel, { title: "Review your order", paddingX: 3, paddingY: 1, width: 80 },
1007
+ React.createElement(Box, { flexDirection: "column" },
1008
+ React.createElement(Row, { label: "Kind", value: draft.kind ?? "—" }),
1009
+ React.createElement(Row, { label: "Project", value: projectName ?? draft.projectId ?? "—" }),
1010
+ draft.kind === "storage" ? (React.createElement(Row, { label: "Size", value: draft.storageSizeGb ? `${draft.storageSizeGb} GB` : "—" })) : (React.createElement(Row, { label: "Spec", value: specLine ?? "—" })),
1011
+ React.createElement(Row, { label: "Location", value: locName ?? draft.locationId ?? "—" }),
1012
+ draft.kind !== "storage" ? (React.createElement(Row, { label: "Image", value: imageName ?? draft.imageId ?? "—" })) : null,
1013
+ React.createElement(Row, { label: draft.kind === "storage" ? "Name" : "Hostname", value: draft.hostname || "—" }),
1014
+ draft.kind !== "storage" ? (React.createElement(Row, { label: "Auth", value: draft.authKind === "password"
1015
+ ? "root password (hidden)"
1016
+ : draft.authKind === "ssh-paste"
1017
+ ? "pasted SSH public key"
1018
+ : "—" })) : null,
1019
+ React.createElement(Row, { label: "Payment", value: paymentLabel }),
1020
+ React.createElement(Box, { marginTop: 1 },
1021
+ React.createElement(Menu, { items: [
1022
+ { value: "submit", label: "Submit order", keybind: "enter" },
1023
+ { value: "edit", label: "Edit from start", keybind: "e" },
1024
+ { value: "cancel", label: "Cancel", keybind: "esc" },
1025
+ ], onSelect: (v) => {
1026
+ if (v === "submit")
1027
+ void onSubmit();
1028
+ else if (v === "edit")
1029
+ onEdit();
1030
+ } })))));
1031
+ }
1032
+ function Row({ label, value }) {
1033
+ return (React.createElement(Box, null,
1034
+ React.createElement(Box, { width: 14 },
1035
+ React.createElement(Text, { color: colors.dim }, label)),
1036
+ React.createElement(Text, { color: colors.text }, value === undefined || value === null || value === "" ? "—" : String(value))));
1037
+ }
1038
+ function ResultStep({ result, }) {
1039
+ if (!result) {
1040
+ return (React.createElement(Panel, { title: "Done", paddingX: 3, paddingY: 1, width: 80 },
1041
+ React.createElement(Text, { color: colors.dim }, "No result yet.")));
1042
+ }
1043
+ if (!result.ok) {
1044
+ return (React.createElement(Panel, { title: "Order failed", paddingX: 3, paddingY: 1, width: 80, borderColor: colors.error },
1045
+ React.createElement(Text, { color: colors.error }, result.error),
1046
+ React.createElement(Box, { marginTop: 1 },
1047
+ React.createElement(Text, { color: colors.dim }, "Press Esc or Enter to return to Dashboard."))));
1048
+ }
1049
+ const color = statusColor(result.status ?? "") ?? colors.success;
1050
+ return (React.createElement(Panel, { title: "Order submitted", paddingX: 3, paddingY: 1, width: 80, borderColor: colors.success },
1051
+ React.createElement(Box, { flexDirection: "column" },
1052
+ React.createElement(Row, { label: "Status", value: result.status ?? "ok" }),
1053
+ React.createElement(Row, { label: "Order id", value: result.orderId ?? "—" }),
1054
+ React.createElement(Row, { label: "Invoice id", value: result.invoiceId ?? "—" }),
1055
+ result.checkoutUrl ? React.createElement(Row, { label: "Checkout URL", value: result.checkoutUrl }) : null,
1056
+ React.createElement(Box, { marginTop: 1 },
1057
+ React.createElement(Text, { color: color }, result.checkoutUrl
1058
+ ? "Open the checkout URL in your browser to complete payment."
1059
+ : "All done — see Resources to watch provisioning.")),
1060
+ React.createElement(Box, { marginTop: 1 },
1061
+ React.createElement(Text, { color: colors.dim }, "Press Esc or Enter to return to Dashboard.")))));
1062
+ }
1063
+ function renderProgressDots(current, total) {
1064
+ let done = "";
1065
+ let pending = "";
1066
+ for (let i = 1; i <= total; i++) {
1067
+ if (i <= current)
1068
+ done += "●";
1069
+ else
1070
+ pending += "○";
1071
+ }
1072
+ return (React.createElement(React.Fragment, null,
1073
+ React.createElement(Text, { color: colors.text }, done),
1074
+ pending ? React.createElement(Text, { color: colors.dim }, pending) : null));
1075
+ }
1076
+ function tipForStep(step, draft, cancelConfirm) {
1077
+ if (cancelConfirm)
1078
+ return "y to cancel and return to Dashboard, any other key to keep editing.";
1079
+ switch (step) {
1080
+ case "kind":
1081
+ return "↑↓ to choose, Enter to select. Esc to cancel.";
1082
+ case "vps-spec":
1083
+ return "Tab location/spec · ↑↓ move · Enter continue · Esc back.";
1084
+ case "hostname":
1085
+ return "Type a hostname (5–253 chars). Enter to confirm · Esc back.";
1086
+ case "auth":
1087
+ return draft.authKind
1088
+ ? "Type and Enter to confirm. Esc to pick a different method."
1089
+ : "Pick how to authorise login. Esc to go back.";
1090
+ case "size":
1091
+ return "Type a number then Enter. Esc to go back.";
1092
+ case "payment":
1093
+ return "↑↓ move · Enter/Space select · Enter on Confirm. Esc back.";
1094
+ case "summary":
1095
+ return "Enter on Submit to send. Edit to start over. Esc to go back.";
1096
+ case "submitting":
1097
+ return "Talking to the API…";
1098
+ case "result":
1099
+ return "Press Esc or Enter to return to the Dashboard.";
1100
+ default:
1101
+ return "↑↓ to choose, Enter to select. ← / Backspace to go back. Esc to cancel.";
1102
+ }
1103
+ }
1104
+ function buildCustomSpec(s) {
1105
+ if (!s.cpu_cores || !s.ram_mb || !s.disk_gb)
1106
+ return undefined;
1107
+ return {
1108
+ cpu_cores: s.cpu_cores,
1109
+ ram_mb: s.ram_mb,
1110
+ disk_gb: s.disk_gb,
1111
+ bandwidth_tb: s.bandwidth_tb,
1112
+ dedicated: s._dedicated,
1113
+ };
1114
+ }
1115
+ function fingerprint(pubKey) {
1116
+ const parts = pubKey.trim().split(/\s+/);
1117
+ if (parts.length === 0)
1118
+ return "—";
1119
+ const algo = parts[0] ?? "";
1120
+ const b64 = parts[1] ?? "";
1121
+ const comment = parts.slice(2).join(" ");
1122
+ if (comment)
1123
+ return `${algo} ${comment}`;
1124
+ if (b64)
1125
+ return `${algo} ${b64.slice(0, 12)}…`;
1126
+ return algo;
1127
+ }
1128
+ function estimateMonthlyCost(draft, cat) {
1129
+ if (draft.planId && cat.orderCatalog) {
1130
+ const plan = cat.orderCatalog.plans.find((p) => p.id === draft.planId);
1131
+ if (plan?.monthly_price != null) {
1132
+ return { amount: plan.monthly_price, label: plan.name };
1133
+ }
1134
+ }
1135
+ if (draft.kind === "vps" && cat.orderCatalog?.custom_config) {
1136
+ const cfg = cat.orderCatalog.custom_config;
1137
+ const baseCfg = draft.customSpec._dedicated ? cfg.dedicated : cfg;
1138
+ const cpu = draft.customSpec.cpu_cores;
1139
+ const ramMb = draft.customSpec.ram_mb;
1140
+ const disk = draft.customSpec.disk_gb;
1141
+ if (cpu && ramMb && disk) {
1142
+ const ramGb = ramMb / 1024;
1143
+ const bwTb = draft.customSpec.bandwidth_tb ?? cfg.bandwidth_included_tb;
1144
+ const extraTb = Math.max(0, bwTb - cfg.bandwidth_included_tb);
1145
+ const amount = baseCfg.base_price +
1146
+ cpu * baseCfg.price_per_cpu +
1147
+ ramGb * baseCfg.price_per_ram_gb +
1148
+ disk * baseCfg.price_per_disk_gb +
1149
+ extraTb * cfg.price_per_tb;
1150
+ return {
1151
+ amount,
1152
+ label: `Custom · ${cpu}c ${ramGb}GB ${disk}GB${draft.customSpec._dedicated ? " · dedicated" : ""}`,
1153
+ };
1154
+ }
1155
+ }
1156
+ if (draft.kind === "storage" && draft.storageSizeGb && cat.storageCatalog) {
1157
+ const price = Number(cat.storageCatalog.price_per_gb);
1158
+ if (Number.isFinite(price) && price > 0) {
1159
+ const useTb = cat.storageCatalog.price_unit === "tb";
1160
+ const units = useTb ? draft.storageSizeGb / 1024 : draft.storageSizeGb;
1161
+ return {
1162
+ amount: price * units,
1163
+ label: `${draft.storageSizeGb} GB block volume`,
1164
+ };
1165
+ }
1166
+ }
1167
+ return null;
1168
+ }
1169
+ //# sourceMappingURL=Order.js.map