@synteq/cli 0.2.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/client.d.ts +362 -5
- package/dist/api/client.js +559 -44
- package/dist/api/client.js.map +1 -1
- package/dist/api/factory.js +5 -2
- package/dist/api/factory.js.map +1 -1
- package/dist/api/types.d.ts +892 -3
- package/dist/apps-support/deploy.d.ts +197 -0
- package/dist/apps-support/deploy.js +639 -0
- package/dist/apps-support/deploy.js.map +1 -0
- package/dist/apps-support/dotenv.d.ts +67 -0
- package/dist/apps-support/dotenv.js +136 -0
- package/dist/apps-support/dotenv.js.map +1 -0
- package/dist/apps-support/env-rollout.d.ts +18 -0
- package/dist/apps-support/env-rollout.js +52 -0
- package/dist/apps-support/env-rollout.js.map +1 -0
- package/dist/apps-support/git.d.ts +14 -0
- package/dist/apps-support/git.js +25 -0
- package/dist/apps-support/git.js.map +1 -0
- package/dist/apps-support/link.d.ts +25 -0
- package/dist/apps-support/link.js +39 -0
- package/dist/apps-support/link.js.map +1 -0
- package/dist/apps-support/log-stream.d.ts +25 -0
- package/dist/apps-support/log-stream.js +85 -0
- package/dist/apps-support/log-stream.js.map +1 -0
- package/dist/apps-support/paid-app.d.ts +64 -0
- package/dist/apps-support/paid-app.js +195 -0
- package/dist/apps-support/paid-app.js.map +1 -0
- package/dist/apps-support/plans.d.ts +44 -0
- package/dist/apps-support/plans.js +43 -0
- package/dist/apps-support/plans.js.map +1 -0
- package/dist/apps-support/resolve.d.ts +10 -0
- package/dist/apps-support/resolve.js +53 -0
- package/dist/apps-support/resolve.js.map +1 -0
- package/dist/apps-support/time.d.ts +20 -0
- package/dist/apps-support/time.js +82 -0
- package/dist/apps-support/time.js.map +1 -0
- package/dist/auth/scope.js +4 -0
- package/dist/auth/scope.js.map +1 -1
- package/dist/auth/tokens.d.ts +3 -0
- package/dist/auth/tokens.js +6 -4
- package/dist/auth/tokens.js.map +1 -1
- package/dist/base/command.d.ts +35 -0
- package/dist/base/command.js +87 -4
- package/dist/base/command.js.map +1 -1
- package/dist/commands/agents.d.ts +10 -0
- package/dist/commands/agents.js +53 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/api.d.ts +0 -1
- package/dist/commands/api.js +6 -5
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/app/deployment/list.d.ts +14 -0
- package/dist/commands/app/deployment/list.js +45 -0
- package/dist/commands/app/deployment/list.js.map +1 -0
- package/dist/commands/app/deployment/rollback.d.ts +18 -0
- package/dist/commands/app/deployment/rollback.js +57 -0
- package/dist/commands/app/deployment/rollback.js.map +1 -0
- package/dist/commands/app/deployment/show.d.ts +26 -0
- package/dist/commands/app/deployment/show.js +98 -0
- package/dist/commands/app/deployment/show.js.map +1 -0
- package/dist/commands/app/domain/add.d.ts +17 -0
- package/dist/commands/app/domain/add.js +59 -0
- package/dist/commands/app/domain/add.js.map +1 -0
- package/dist/commands/app/domain/list.d.ts +14 -0
- package/dist/commands/app/domain/list.js +42 -0
- package/dist/commands/app/domain/list.js.map +1 -0
- package/dist/commands/app/domain/remove.d.ts +16 -0
- package/dist/commands/app/domain/remove.js +51 -0
- package/dist/commands/app/domain/remove.js.map +1 -0
- package/dist/commands/app/domain/verify.d.ts +15 -0
- package/dist/commands/app/domain/verify.js +39 -0
- package/dist/commands/app/domain/verify.js.map +1 -0
- package/dist/commands/app/env/add.d.ts +22 -0
- package/dist/commands/app/env/add.js +103 -0
- package/dist/commands/app/env/add.js.map +1 -0
- package/dist/commands/app/env/list.d.ts +17 -0
- package/dist/commands/app/env/list.js +40 -0
- package/dist/commands/app/env/list.js.map +1 -0
- package/dist/commands/app/env/reveal.d.ts +17 -0
- package/dist/commands/app/env/reveal.js +40 -0
- package/dist/commands/app/env/reveal.js.map +1 -0
- package/dist/commands/app/env/rm.d.ts +16 -0
- package/dist/commands/app/env/rm.js +48 -0
- package/dist/commands/app/env/rm.js.map +1 -0
- package/dist/commands/app/health.d.ts +14 -0
- package/dist/commands/app/health.js +40 -0
- package/dist/commands/app/health.js.map +1 -0
- package/dist/commands/app/index.d.ts +6 -0
- package/dist/commands/app/index.js +7 -0
- package/dist/commands/app/index.js.map +1 -0
- package/dist/commands/app/link.d.ts +22 -0
- package/dist/commands/app/link.js +31 -0
- package/dist/commands/app/link.js.map +1 -0
- package/dist/commands/app/logs.d.ts +25 -0
- package/dist/commands/app/logs.js +121 -0
- package/dist/commands/app/logs.js.map +1 -0
- package/dist/commands/app/metrics.d.ts +21 -0
- package/dist/commands/app/metrics.js +123 -0
- package/dist/commands/app/metrics.js.map +1 -0
- package/dist/commands/app/plan/change.d.ts +18 -0
- package/dist/commands/app/plan/change.js +79 -0
- package/dist/commands/app/plan/change.js.map +1 -0
- package/dist/commands/app/plan/show.d.ts +14 -0
- package/dist/commands/app/plan/show.js +40 -0
- package/dist/commands/app/plan/show.js.map +1 -0
- package/dist/commands/app/settings/set.d.ts +25 -0
- package/dist/commands/app/settings/set.js +96 -0
- package/dist/commands/app/settings/set.js.map +1 -0
- package/dist/commands/app/settings/show.d.ts +14 -0
- package/dist/commands/app/settings/show.js +48 -0
- package/dist/commands/app/settings/show.js.map +1 -0
- package/dist/commands/app/unlink.d.ts +8 -0
- package/dist/commands/app/unlink.js +25 -0
- package/dist/commands/app/unlink.js.map +1 -0
- package/dist/commands/apps/create.d.ts +26 -0
- package/dist/commands/apps/create.js +91 -0
- package/dist/commands/apps/create.js.map +1 -0
- package/dist/commands/apps/list.d.ts +16 -0
- package/dist/commands/apps/list.js +54 -0
- package/dist/commands/apps/list.js.map +1 -0
- package/dist/commands/baremetal/activity.d.ts +14 -0
- package/dist/commands/baremetal/activity.js +53 -0
- package/dist/commands/baremetal/activity.js.map +1 -0
- package/dist/commands/baremetal/bandwidth.d.ts +10 -0
- package/dist/commands/baremetal/bandwidth.js +86 -0
- package/dist/commands/baremetal/bandwidth.js.map +1 -0
- package/dist/commands/baremetal/console.d.ts +20 -0
- package/dist/commands/baremetal/console.js +72 -0
- package/dist/commands/baremetal/console.js.map +1 -0
- package/dist/commands/baremetal/details.d.ts +11 -0
- package/dist/commands/baremetal/details.js +116 -0
- package/dist/commands/baremetal/details.js.map +1 -0
- package/dist/commands/baremetal/index.d.ts +5 -0
- package/dist/commands/baremetal/index.js +6 -0
- package/dist/commands/baremetal/index.js.map +1 -0
- package/dist/commands/baremetal/install-password.d.ts +14 -0
- package/dist/commands/baremetal/install-password.js +80 -0
- package/dist/commands/baremetal/install-password.js.map +1 -0
- package/dist/commands/baremetal/list.d.ts +11 -0
- package/dist/commands/baremetal/list.js +59 -0
- package/dist/commands/baremetal/list.js.map +1 -0
- package/dist/commands/baremetal/order.d.ts +34 -0
- package/dist/commands/baremetal/order.js +394 -0
- package/dist/commands/baremetal/order.js.map +1 -0
- package/dist/commands/baremetal/power.d.ts +18 -0
- package/dist/commands/baremetal/power.js +168 -0
- package/dist/commands/baremetal/power.js.map +1 -0
- package/dist/commands/baremetal/reinstall.d.ts +21 -0
- package/dist/commands/baremetal/reinstall.js +168 -0
- package/dist/commands/baremetal/reinstall.js.map +1 -0
- package/dist/commands/config/profile/add.d.ts +11 -0
- package/dist/commands/config/profile/add.js +51 -0
- package/dist/commands/config/profile/add.js.map +1 -0
- package/dist/commands/config/profile/list.d.ts +7 -0
- package/dist/commands/config/profile/list.js +29 -0
- package/dist/commands/config/profile/list.js.map +1 -0
- package/dist/commands/config/profile/remove.d.ts +11 -0
- package/dist/commands/config/profile/remove.js +35 -0
- package/dist/commands/config/profile/remove.js.map +1 -0
- package/dist/commands/config/profile/use.d.ts +10 -0
- package/dist/commands/config/profile/use.js +44 -0
- package/dist/commands/config/profile/use.js.map +1 -0
- package/dist/commands/config/show.js +4 -1
- package/dist/commands/config/show.js.map +1 -1
- package/dist/commands/credits/add.js +2 -2
- package/dist/commands/credits/add.js.map +1 -1
- package/dist/commands/credits/index.js +5 -1
- package/dist/commands/credits/index.js.map +1 -1
- package/dist/commands/deploy.d.ts +51 -0
- package/dist/commands/deploy.js +515 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/function/create.d.ts +29 -0
- package/dist/commands/function/create.js +113 -0
- package/dist/commands/function/create.js.map +1 -0
- package/dist/commands/function/credits/add.d.ts +29 -0
- package/dist/commands/function/credits/add.js +209 -0
- package/dist/commands/function/credits/add.js.map +1 -0
- package/dist/commands/function/credits/auto-reload.d.ts +27 -0
- package/dist/commands/function/credits/auto-reload.js +107 -0
- package/dist/commands/function/credits/auto-reload.js.map +1 -0
- package/dist/commands/function/credits/index.d.ts +22 -0
- package/dist/commands/function/credits/index.js +80 -0
- package/dist/commands/function/credits/index.js.map +1 -0
- package/dist/commands/function/cron/add.d.ts +19 -0
- package/dist/commands/function/cron/add.js +74 -0
- package/dist/commands/function/cron/add.js.map +1 -0
- package/dist/commands/function/cron/list.d.ts +12 -0
- package/dist/commands/function/cron/list.js +55 -0
- package/dist/commands/function/cron/list.js.map +1 -0
- package/dist/commands/function/cron/rm.d.ts +17 -0
- package/dist/commands/function/cron/rm.js +73 -0
- package/dist/commands/function/cron/rm.js.map +1 -0
- package/dist/commands/function/cron/run.d.ts +16 -0
- package/dist/commands/function/cron/run.js +59 -0
- package/dist/commands/function/cron/run.js.map +1 -0
- package/dist/commands/function/cron/update.d.ts +21 -0
- package/dist/commands/function/cron/update.js +87 -0
- package/dist/commands/function/cron/update.js.map +1 -0
- package/dist/commands/function/delete.d.ts +23 -0
- package/dist/commands/function/delete.js +90 -0
- package/dist/commands/function/delete.js.map +1 -0
- package/dist/commands/function/deploy.d.ts +64 -0
- package/dist/commands/function/deploy.js +608 -0
- package/dist/commands/function/deploy.js.map +1 -0
- package/dist/commands/function/env/list.d.ts +12 -0
- package/dist/commands/function/env/list.js +46 -0
- package/dist/commands/function/env/list.js.map +1 -0
- package/dist/commands/function/env/reveal.d.ts +21 -0
- package/dist/commands/function/env/reveal.js +54 -0
- package/dist/commands/function/env/reveal.js.map +1 -0
- package/dist/commands/function/env/rm.d.ts +17 -0
- package/dist/commands/function/env/rm.js +62 -0
- package/dist/commands/function/env/rm.js.map +1 -0
- package/dist/commands/function/env/set.d.ts +28 -0
- package/dist/commands/function/env/set.js +124 -0
- package/dist/commands/function/env/set.js.map +1 -0
- package/dist/commands/function/index.d.ts +14 -0
- package/dist/commands/function/index.js +11 -0
- package/dist/commands/function/index.js.map +1 -0
- package/dist/commands/function/init.d.ts +47 -0
- package/dist/commands/function/init.js +269 -0
- package/dist/commands/function/init.js.map +1 -0
- package/dist/commands/function/invoke.d.ts +29 -0
- package/dist/commands/function/invoke.js +138 -0
- package/dist/commands/function/invoke.js.map +1 -0
- package/dist/commands/function/link.d.ts +23 -0
- package/dist/commands/function/link.js +80 -0
- package/dist/commands/function/link.js.map +1 -0
- package/dist/commands/function/list.d.ts +16 -0
- package/dist/commands/function/list.js +55 -0
- package/dist/commands/function/list.js.map +1 -0
- package/dist/commands/function/logs.d.ts +29 -0
- package/dist/commands/function/logs.js +171 -0
- package/dist/commands/function/logs.js.map +1 -0
- package/dist/commands/function/pause.d.ts +19 -0
- package/dist/commands/function/pause.js +50 -0
- package/dist/commands/function/pause.js.map +1 -0
- package/dist/commands/function/releases.d.ts +15 -0
- package/dist/commands/function/releases.js +62 -0
- package/dist/commands/function/releases.js.map +1 -0
- package/dist/commands/function/resume.d.ts +16 -0
- package/dist/commands/function/resume.js +47 -0
- package/dist/commands/function/resume.js.map +1 -0
- package/dist/commands/function/rollback.d.ts +18 -0
- package/dist/commands/function/rollback.js +69 -0
- package/dist/commands/function/rollback.js.map +1 -0
- package/dist/commands/function/settings/set.d.ts +23 -0
- package/dist/commands/function/settings/set.js +119 -0
- package/dist/commands/function/settings/set.js.map +1 -0
- package/dist/commands/function/show.d.ts +15 -0
- package/dist/commands/function/show.js +80 -0
- package/dist/commands/function/show.js.map +1 -0
- package/dist/commands/function/unlink.d.ts +9 -0
- package/dist/commands/function/unlink.js +34 -0
- package/dist/commands/function/unlink.js.map +1 -0
- package/dist/commands/link.d.ts +77 -0
- package/dist/commands/link.js +530 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/login.js +9 -0
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/order.js +3 -1
- package/dist/commands/order.js.map +1 -1
- package/dist/commands/payments/methods/delete.js +2 -2
- package/dist/commands/payments/methods/delete.js.map +1 -1
- package/dist/commands/resources.js +50 -7
- package/dist/commands/resources.js.map +1 -1
- package/dist/commands/status.d.ts +12 -0
- package/dist/commands/status.js +71 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/storage/detach.js +1 -1
- package/dist/commands/storage/detach.js.map +1 -1
- package/dist/commands/storage/list.js +6 -2
- package/dist/commands/storage/list.js.map +1 -1
- package/dist/commands/storage/order.d.ts +2 -0
- package/dist/commands/storage/order.js +100 -18
- package/dist/commands/storage/order.js.map +1 -1
- package/dist/commands/storage/resize.js +19 -6
- package/dist/commands/storage/resize.js.map +1 -1
- package/dist/commands/tickets/create.d.ts +2 -0
- package/dist/commands/tickets/create.js +40 -1
- package/dist/commands/tickets/create.js.map +1 -1
- package/dist/commands/tickets/download.d.ts +18 -0
- package/dist/commands/tickets/download.js +138 -0
- package/dist/commands/tickets/download.js.map +1 -0
- package/dist/commands/tickets/list.js +13 -7
- package/dist/commands/tickets/list.js.map +1 -1
- package/dist/commands/tickets/reply.d.ts +2 -0
- package/dist/commands/tickets/reply.js +33 -4
- package/dist/commands/tickets/reply.js.map +1 -1
- package/dist/commands/vds/order.js +72 -7
- package/dist/commands/vds/order.js.map +1 -1
- package/dist/commands/vps/console.js +14 -6
- package/dist/commands/vps/console.js.map +1 -1
- package/dist/commands/vps/details.js +7 -1
- package/dist/commands/vps/details.js.map +1 -1
- package/dist/commands/vps/ip/add.js +76 -9
- package/dist/commands/vps/ip/add.js.map +1 -1
- package/dist/commands/vps/list.js +13 -4
- package/dist/commands/vps/list.js.map +1 -1
- package/dist/commands/vps/order.d.ts +19 -0
- package/dist/commands/vps/order.js +179 -23
- package/dist/commands/vps/order.js.map +1 -1
- package/dist/commands/vps/power.js +2 -2
- package/dist/commands/vps/power.js.map +1 -1
- package/dist/commands/vps/reinstall.js +2 -2
- package/dist/commands/vps/reinstall.js.map +1 -1
- package/dist/commands/vps/resize.js +36 -13
- package/dist/commands/vps/resize.js.map +1 -1
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.js +8 -3
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config/paths.d.ts +1 -1
- package/dist/config/paths.js +6 -1
- package/dist/config/paths.js.map +1 -1
- package/dist/config/store.d.ts +58 -0
- package/dist/config/store.js +84 -2
- package/dist/config/store.js.map +1 -1
- package/dist/functions-support/credits.d.ts +64 -0
- package/dist/functions-support/credits.js +181 -0
- package/dist/functions-support/credits.js.map +1 -0
- package/dist/functions-support/cron.d.ts +17 -0
- package/dist/functions-support/cron.js +99 -0
- package/dist/functions-support/cron.js.map +1 -0
- package/dist/functions-support/env.d.ts +23 -0
- package/dist/functions-support/env.js +44 -0
- package/dist/functions-support/env.js.map +1 -0
- package/dist/functions-support/link.d.ts +25 -0
- package/dist/functions-support/link.js +51 -0
- package/dist/functions-support/link.js.map +1 -0
- package/dist/functions-support/poll.d.ts +40 -0
- package/dist/functions-support/poll.js +109 -0
- package/dist/functions-support/poll.js.map +1 -0
- package/dist/functions-support/resolve.d.ts +3 -0
- package/dist/functions-support/resolve.js +31 -0
- package/dist/functions-support/resolve.js.map +1 -0
- package/dist/functions-support/runtime.d.ts +30 -0
- package/dist/functions-support/runtime.js +52 -0
- package/dist/functions-support/runtime.js.map +1 -0
- package/dist/functions-support/source.d.ts +92 -0
- package/dist/functions-support/source.js +343 -0
- package/dist/functions-support/source.js.map +1 -0
- package/dist/help/scope-aware-help.js +10 -1
- package/dist/help/scope-aware-help.js.map +1 -1
- package/dist/tui/App.js +10 -25
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/components/Menu.d.ts +3 -1
- package/dist/tui/components/Menu.js +7 -3
- package/dist/tui/components/Menu.js.map +1 -1
- package/dist/tui/router.d.ts +1 -1
- package/dist/tui/router.js.map +1 -1
- package/dist/tui/screens/Billing.js +56 -4
- package/dist/tui/screens/Billing.js.map +1 -1
- package/dist/tui/screens/Dashboard.js +8 -7
- package/dist/tui/screens/Dashboard.js.map +1 -1
- package/dist/tui/screens/Deploy.d.ts +2 -0
- package/dist/tui/screens/Deploy.js +912 -0
- package/dist/tui/screens/Deploy.js.map +1 -0
- package/dist/tui/screens/Order.js +359 -26
- package/dist/tui/screens/Order.js.map +1 -1
- package/dist/tui/screens/Resources.js +273 -31
- package/dist/tui/screens/Resources.js.map +1 -1
- package/dist/tui/screens/Tickets.js +18 -6
- package/dist/tui/screens/Tickets.js.map +1 -1
- package/dist/tui/screens/deployPreset.d.ts +20 -0
- package/dist/tui/screens/deployPreset.js +11 -0
- package/dist/tui/screens/deployPreset.js.map +1 -0
- package/dist/tui/screens/resources/AppDetail.d.ts +7 -0
- package/dist/tui/screens/resources/AppDetail.js +810 -0
- package/dist/tui/screens/resources/AppDetail.js.map +1 -0
- package/dist/tui/screens/resources/FunctionDetail.d.ts +7 -0
- package/dist/tui/screens/resources/FunctionDetail.js +751 -0
- package/dist/tui/screens/resources/FunctionDetail.js.map +1 -0
- package/dist/ui/attach.d.ts +15 -0
- package/dist/ui/attach.js +63 -0
- package/dist/ui/attach.js.map +1 -0
- package/dist/ui/baremetal-resolve.d.ts +10 -0
- package/dist/ui/baremetal-resolve.js +50 -0
- package/dist/ui/baremetal-resolve.js.map +1 -0
- package/dist/ui/confirm.js +1 -1
- package/dist/ui/confirm.js.map +1 -1
- package/dist/ui/cycle.d.ts +66 -0
- package/dist/ui/cycle.js +96 -0
- package/dist/ui/cycle.js.map +1 -0
- package/dist/ui/deploy-ui.d.ts +75 -0
- package/dist/ui/deploy-ui.js +243 -0
- package/dist/ui/deploy-ui.js.map +1 -0
- package/dist/ui/order-result.d.ts +4 -0
- package/dist/ui/order-result.js +32 -0
- package/dist/ui/order-result.js.map +1 -0
- package/dist/ui/payment-gate.d.ts +26 -0
- package/dist/ui/payment-gate.js +70 -0
- package/dist/ui/payment-gate.js.map +1 -0
- package/dist/ui/payment.js +10 -3
- package/dist/ui/payment.js.map +1 -1
- package/dist/ui/portal.d.ts +1 -1
- package/dist/ui/portal.js +7 -2
- package/dist/ui/portal.js.map +1 -1
- package/dist/ui/project-resolve.d.ts +25 -0
- package/dist/ui/project-resolve.js +31 -0
- package/dist/ui/project-resolve.js.map +1 -0
- package/dist/ui/render-mode.d.ts +5 -0
- package/dist/ui/render-mode.js +1 -1
- package/dist/ui/render-mode.js.map +1 -1
- package/dist/ui/resource-detail.js +0 -3
- package/dist/ui/resource-detail.js.map +1 -1
- package/dist/ui/resource-resolve.d.ts +9 -2
- package/dist/ui/resource-resolve.js +11 -4
- package/dist/ui/resource-resolve.js.map +1 -1
- package/dist/ui/spinner.d.ts +10 -1
- package/dist/ui/spinner.js +12 -3
- package/dist/ui/spinner.js.map +1 -1
- package/dist/ui/table.d.ts +7 -0
- package/dist/ui/table.js +11 -0
- package/dist/ui/table.js.map +1 -1
- package/dist/ui/tax.d.ts +10 -0
- package/dist/ui/tax.js +31 -0
- package/dist/ui/tax.js.map +1 -0
- package/dist/ui/ticket-resolve.d.ts +9 -1
- package/dist/ui/ticket-resolve.js +13 -2
- package/dist/ui/ticket-resolve.js.map +1 -1
- package/dist/ui/volume-resolve.js +4 -1
- package/dist/ui/volume-resolve.js.map +1 -1
- package/dist/ui/wait.d.ts +4 -0
- package/dist/ui/wait.js +7 -2
- package/dist/ui/wait.js.map +1 -1
- package/dist/ui/wizard.d.ts +2 -1
- package/dist/ui/wizard.js +1 -1
- package/dist/ui/wizard.js.map +1 -1
- package/dist/version-check-bin.js +2 -2
- package/dist/version-check-bin.js.map +1 -1
- package/dist/version-check.d.ts +5 -0
- package/dist/version-check.js +6 -1
- package/dist/version-check.js.map +1 -1
- package/package.json +53 -6
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { existsSync, rmSync, statSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, resolve } from "node:path";
|
|
5
|
+
import { Panel } from "../components/Panel.js";
|
|
6
|
+
import { Menu } from "../components/Menu.js";
|
|
7
|
+
import { StatusBar } from "../components/StatusBar.js";
|
|
8
|
+
import { Tip } from "../components/Tip.js";
|
|
9
|
+
import { Spinner } from "../components/Spinner.js";
|
|
10
|
+
import { InputBar } from "../components/InputBar.js";
|
|
11
|
+
import { CenteredFrame } from "../components/CenteredFrame.js";
|
|
12
|
+
import { BackButton } from "../components/BackButton.js";
|
|
13
|
+
import { useApi, useAppContext } from "../hooks/useApi.js";
|
|
14
|
+
import { useHeader } from "../hooks/useHeader.js";
|
|
15
|
+
import { useMousePause } from "../hooks/useMouse.js";
|
|
16
|
+
import { useRouter } from "../router.js";
|
|
17
|
+
import { colors, statusColor } from "../theme/colors.js";
|
|
18
|
+
import { glyphs } from "../theme/glyphs.js";
|
|
19
|
+
import { deployPresetRef } from "./deployPreset.js";
|
|
20
|
+
import { humanSize, dotenvFiles, makeTarball, pickFrontend, pollBuild, resolveNewRelease, uploadSource, waitForReleaseOutcome, } from "../../apps-support/deploy.js";
|
|
21
|
+
import { ApiError } from "../../api/errors.js";
|
|
22
|
+
import { chargeLine, priceLine } from "../../apps-support/paid-app.js";
|
|
23
|
+
import { isPaidPlan, planCapReason, planLine, toLocationOptions } from "../../apps-support/plans.js";
|
|
24
|
+
import { describePaymentMethod, readBalance } from "../../ui/wizard.js";
|
|
25
|
+
import { waitFor } from "../../ui/wait.js";
|
|
26
|
+
import { portalUrl } from "../../ui/portal.js";
|
|
27
|
+
import { PAYMENT_METHODS_PATH } from "../../ui/payment-gate.js";
|
|
28
|
+
import { truncationNote } from "../../ui/table.js";
|
|
29
|
+
/** The sub-steps of "create a new app" all count as the App step. */
|
|
30
|
+
const NEW_APP_STEPS = new Set([
|
|
31
|
+
"new-app-plan",
|
|
32
|
+
"new-app-region",
|
|
33
|
+
"new-app-price",
|
|
34
|
+
"new-app-pay",
|
|
35
|
+
"new-app-confirm",
|
|
36
|
+
"new-app-order",
|
|
37
|
+
]);
|
|
38
|
+
const STEP_LABELS = {
|
|
39
|
+
project: "Project",
|
|
40
|
+
app: "App",
|
|
41
|
+
"new-app-plan": "Plan",
|
|
42
|
+
"new-app-region": "Location",
|
|
43
|
+
"new-app-price": "Price",
|
|
44
|
+
"new-app-pay": "Payment",
|
|
45
|
+
"new-app-confirm": "Confirm charge",
|
|
46
|
+
"new-app-order": "Ordering",
|
|
47
|
+
dir: "Directory",
|
|
48
|
+
confirm: "Confirm",
|
|
49
|
+
deploying: "Deploying",
|
|
50
|
+
result: "Done",
|
|
51
|
+
};
|
|
52
|
+
const PROGRESS = ["project", "app", "dir", "confirm", "deploying", "result"];
|
|
53
|
+
const SLUG_RE = /^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;
|
|
54
|
+
function nameCheckMessage(reason) {
|
|
55
|
+
if (reason === "taken")
|
|
56
|
+
return "This name is taken.";
|
|
57
|
+
if (reason === "banned")
|
|
58
|
+
return "This name isn't allowed.";
|
|
59
|
+
if (reason === "invalid_shape")
|
|
60
|
+
return "Use 3–63 lowercase letters, digits and dashes.";
|
|
61
|
+
return "This name is unavailable.";
|
|
62
|
+
}
|
|
63
|
+
/** ACH settles asynchronously, so it can't underwrite instant provisioning
|
|
64
|
+
* (the same exclusion the Order screen applies). */
|
|
65
|
+
function isAch(m) {
|
|
66
|
+
return m.provider === "stripe" && m.card_brand === "us_bank_account";
|
|
67
|
+
}
|
|
68
|
+
export function Deploy() {
|
|
69
|
+
const api = useApi();
|
|
70
|
+
const { config } = useAppContext();
|
|
71
|
+
const { header } = useHeader();
|
|
72
|
+
const { navigate, goBack } = useRouter();
|
|
73
|
+
useMousePause(true);
|
|
74
|
+
const preset = useRef(deployPresetRef.current);
|
|
75
|
+
const fromOrder = useRef(Boolean(deployPresetRef.current?.newApp));
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
deployPresetRef.current = null;
|
|
78
|
+
}, []);
|
|
79
|
+
// A deploy of an existing app (preset appId) skips the project step.
|
|
80
|
+
const [step, setStep] = useState(preset.current?.appId ? "app" : "project");
|
|
81
|
+
const [projects, setProjects] = useState([]);
|
|
82
|
+
const [projectId, setProjectId] = useState(header.projectId ?? null);
|
|
83
|
+
// The App step holds the new-app name input and the existing-app list;
|
|
84
|
+
// Tab moves between them.
|
|
85
|
+
const [appPane, setAppPane] = useState("name");
|
|
86
|
+
const [nameState, setNameState] = useState({ kind: "idle" });
|
|
87
|
+
const [apps, setApps] = useState(null);
|
|
88
|
+
// The server's count: the list is one page of 200 (portal #419).
|
|
89
|
+
const [appsTotal, setAppsTotal] = useState(0);
|
|
90
|
+
const [plans, setPlans] = useState([]);
|
|
91
|
+
const [loadError, setLoadError] = useState(null);
|
|
92
|
+
const [app, setApp] = useState(null);
|
|
93
|
+
const [slug, setSlug] = useState("");
|
|
94
|
+
const [creating, setCreating] = useState(false);
|
|
95
|
+
const [dirInput, setDirInput] = useState(process.cwd());
|
|
96
|
+
const [dir, setDir] = useState(null);
|
|
97
|
+
const [dirError, setDirError] = useState(null);
|
|
98
|
+
const [frontend, setFrontend] = useState("railpack");
|
|
99
|
+
const [linkDir, setLinkDir] = useState(true);
|
|
100
|
+
const [phases, setPhases] = useState([]);
|
|
101
|
+
const [outcome, setOutcome] = useState(null);
|
|
102
|
+
const [error, setError] = useState(null);
|
|
103
|
+
const [order, setOrder] = useState(null);
|
|
104
|
+
// Catalog: the team's apps in the active project, plus plans for "new app".
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
let cancelled = false;
|
|
107
|
+
(async () => {
|
|
108
|
+
try {
|
|
109
|
+
const [appsPage, plansOut, projectsOut] = await Promise.all([
|
|
110
|
+
api.apps.listPage(),
|
|
111
|
+
api.apps.plans().catch(() => []),
|
|
112
|
+
api.projects.list().catch(() => []),
|
|
113
|
+
]);
|
|
114
|
+
if (cancelled)
|
|
115
|
+
return;
|
|
116
|
+
const list = appsPage.items;
|
|
117
|
+
setApps(list);
|
|
118
|
+
setAppsTotal(appsPage.total);
|
|
119
|
+
setPlans(plansOut);
|
|
120
|
+
setProjects(projectsOut);
|
|
121
|
+
setProjectId((cur) => cur ?? (projectsOut.find((p) => p.is_default) ?? projectsOut[0])?.id ?? null);
|
|
122
|
+
const pre = preset.current;
|
|
123
|
+
if (pre?.newApp) {
|
|
124
|
+
setStep("project");
|
|
125
|
+
}
|
|
126
|
+
else if (pre) {
|
|
127
|
+
const hit = list.find((a) => a.id === pre.appId);
|
|
128
|
+
if (hit) {
|
|
129
|
+
setApp(hit);
|
|
130
|
+
if (pre.dir)
|
|
131
|
+
setDirInput(pre.dir);
|
|
132
|
+
else {
|
|
133
|
+
const linked = linkedDirFor(config.get().links, hit.id);
|
|
134
|
+
if (linked)
|
|
135
|
+
setDirInput(linked);
|
|
136
|
+
}
|
|
137
|
+
setStep("dir");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
if (!cancelled)
|
|
143
|
+
setLoadError(e instanceof Error ? e.message : String(e));
|
|
144
|
+
}
|
|
145
|
+
})();
|
|
146
|
+
return () => {
|
|
147
|
+
cancelled = true;
|
|
148
|
+
};
|
|
149
|
+
}, [api, config]);
|
|
150
|
+
// Live slug check while the user types (debounced): the shape rule first,
|
|
151
|
+
// then the server's check-name, so a taken name shows here and not as a
|
|
152
|
+
// refused order after the plan step.
|
|
153
|
+
const nameReq = useRef(0);
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
const name = slug.trim();
|
|
156
|
+
const reqId = ++nameReq.current;
|
|
157
|
+
if (!name) {
|
|
158
|
+
setNameState({ kind: "idle" });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!SLUG_RE.test(name)) {
|
|
162
|
+
setNameState({ kind: "invalid", msg: "Use 3–63 lowercase letters, digits and dashes." });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
setNameState({ kind: "checking" });
|
|
166
|
+
const t = setTimeout(() => {
|
|
167
|
+
api.apps
|
|
168
|
+
.checkName(name)
|
|
169
|
+
.then((r) => {
|
|
170
|
+
if (reqId !== nameReq.current)
|
|
171
|
+
return;
|
|
172
|
+
setNameState(r.available ? { kind: "available", hostname: r.hostname } : { kind: "taken", msg: nameCheckMessage(r.reason) });
|
|
173
|
+
})
|
|
174
|
+
.catch((e) => {
|
|
175
|
+
if (reqId !== nameReq.current)
|
|
176
|
+
return;
|
|
177
|
+
setNameState({ kind: "taken", msg: e instanceof Error ? e.message : String(e) });
|
|
178
|
+
});
|
|
179
|
+
}, 300);
|
|
180
|
+
return () => clearTimeout(t);
|
|
181
|
+
}, [slug, api]);
|
|
182
|
+
// When an app is picked, offer the directory it is already linked to.
|
|
183
|
+
const pickApp = useCallback((a) => {
|
|
184
|
+
setApp(a);
|
|
185
|
+
const linked = linkedDirFor(config.get().links, a.id);
|
|
186
|
+
if (linked)
|
|
187
|
+
setDirInput(linked);
|
|
188
|
+
setStep("dir");
|
|
189
|
+
}, [config]);
|
|
190
|
+
const alreadyLinked = useMemo(() => {
|
|
191
|
+
if (!app || !dir)
|
|
192
|
+
return false;
|
|
193
|
+
const link = config.linkFor(dir);
|
|
194
|
+
return !!link && link.app_id === app.id;
|
|
195
|
+
}, [app, dir, config]);
|
|
196
|
+
const confirmDir = useCallback((raw) => {
|
|
197
|
+
const path = resolve(raw.trim() || ".");
|
|
198
|
+
if (!existsSync(path) || !statSync(path).isDirectory()) {
|
|
199
|
+
setDirError(`Not a directory: ${path}`);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
setDirError(null);
|
|
203
|
+
setDir(path);
|
|
204
|
+
setFrontend(pickFrontend(path));
|
|
205
|
+
setStep("confirm");
|
|
206
|
+
}, []);
|
|
207
|
+
// The deploy itself — runs once when the confirm step hands over.
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
if (step !== "deploying" || !app || !dir)
|
|
210
|
+
return;
|
|
211
|
+
let cancelled = false;
|
|
212
|
+
const started = Date.now();
|
|
213
|
+
const initial = [
|
|
214
|
+
{ label: "Pack source", state: "active" },
|
|
215
|
+
{ label: "Upload", state: "pending" },
|
|
216
|
+
{ label: "Build", state: "pending" },
|
|
217
|
+
{ label: "Release", state: "pending" },
|
|
218
|
+
];
|
|
219
|
+
setPhases(initial);
|
|
220
|
+
const mark = (i, patch) => setPhases((ps) => ps.map((p, j) => (j === i ? { ...p, ...patch } : p)));
|
|
221
|
+
(async () => {
|
|
222
|
+
try {
|
|
223
|
+
const archive = await makeTarball(dir);
|
|
224
|
+
let build;
|
|
225
|
+
try {
|
|
226
|
+
if (cancelled)
|
|
227
|
+
return;
|
|
228
|
+
const dotenv = dotenvFiles(dir);
|
|
229
|
+
mark(0, {
|
|
230
|
+
state: "done",
|
|
231
|
+
detail: `${basename(dir)} → ${humanSize(archive)}` +
|
|
232
|
+
(dotenv.length ? ` (not packed: ${dotenv.join(", ")} — set variables in the Environment tab)` : ""),
|
|
233
|
+
});
|
|
234
|
+
mark(1, { state: "active", detail: "0%" });
|
|
235
|
+
// The orchestrator decides Dockerfile vs Railpack from what it finds
|
|
236
|
+
// in the source now — the client no longer sends a frontend hint.
|
|
237
|
+
// Shared with `synteq deploy --dir` (apps-support/deploy.ts) so the
|
|
238
|
+
// two surfaces can't diverge on retry/fallback behaviour.
|
|
239
|
+
build = await uploadSource(api, app.id, archive, {
|
|
240
|
+
onProgress: (pct) => mark(1, { detail: `${pct}%` }),
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
// The pack is a full copy of the source (an un-ignored .env included)
|
|
245
|
+
// in a shared tmpdir — gone once the upload settles, either way.
|
|
246
|
+
rmSync(dirname(archive), { recursive: true, force: true });
|
|
247
|
+
}
|
|
248
|
+
if (cancelled)
|
|
249
|
+
return;
|
|
250
|
+
mark(1, { state: "done", detail: `build ${build.id.slice(0, 8)} · ${frontend}` });
|
|
251
|
+
mark(2, { state: "active", detail: build.status });
|
|
252
|
+
const final = await pollBuild(api, app.id, build, {
|
|
253
|
+
intervalMs: 4000,
|
|
254
|
+
// A large source can sit in its first status for several minutes
|
|
255
|
+
// while the API prepares it in the background (202 + poll) — a
|
|
256
|
+
// deadline under 30 minutes can cut off a still-running deploy.
|
|
257
|
+
timeoutMs: 1_800_000,
|
|
258
|
+
onStatus: (s) => mark(2, { detail: s }),
|
|
259
|
+
});
|
|
260
|
+
if (cancelled)
|
|
261
|
+
return;
|
|
262
|
+
if (final.status !== "succeeded") {
|
|
263
|
+
mark(2, { state: "failed", detail: final.status });
|
|
264
|
+
setOutcome({
|
|
265
|
+
ok: false,
|
|
266
|
+
error: final.status === "failed"
|
|
267
|
+
? [
|
|
268
|
+
"The build failed.",
|
|
269
|
+
final.failureReason ?? null,
|
|
270
|
+
"The Deployments tab in the portal carries the build log.",
|
|
271
|
+
]
|
|
272
|
+
.filter(Boolean)
|
|
273
|
+
.join(" ")
|
|
274
|
+
: `The build is still ${final.status} after 30 minutes; it may still finish server-side.`,
|
|
275
|
+
buildId: final.id,
|
|
276
|
+
});
|
|
277
|
+
setStep("result");
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
mark(2, { state: "done", detail: "succeeded" });
|
|
281
|
+
mark(3, { state: "active", detail: "waiting for the release" });
|
|
282
|
+
const release = await resolveNewRelease(api, app.id, final);
|
|
283
|
+
if (cancelled)
|
|
284
|
+
return;
|
|
285
|
+
const status = release
|
|
286
|
+
? await waitForReleaseOutcome(api, app.id, release.number, 180, (s) => mark(3, { detail: `#${release.number} ${s ?? "pending"}` }))
|
|
287
|
+
: null;
|
|
288
|
+
if (cancelled)
|
|
289
|
+
return;
|
|
290
|
+
const live = !release || status === "live";
|
|
291
|
+
mark(3, { state: live ? "done" : "failed", detail: release ? `#${release.number} ${status ?? "still rolling out"}` : "no release yet" });
|
|
292
|
+
let linked = false;
|
|
293
|
+
if (live && linkDir && !alreadyLinked) {
|
|
294
|
+
config.setLink(dir, {
|
|
295
|
+
team_id: app.teamId,
|
|
296
|
+
project_id: app.projectId,
|
|
297
|
+
app_id: app.id,
|
|
298
|
+
app_slug: app.slug,
|
|
299
|
+
linked_at: new Date().toISOString(),
|
|
300
|
+
kind: "app",
|
|
301
|
+
});
|
|
302
|
+
linked = true;
|
|
303
|
+
}
|
|
304
|
+
if (live) {
|
|
305
|
+
setOutcome({
|
|
306
|
+
ok: true,
|
|
307
|
+
release: release?.number ?? null,
|
|
308
|
+
url: `https://${app.hostname}`,
|
|
309
|
+
durationMs: Date.now() - started,
|
|
310
|
+
linked,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
setOutcome({
|
|
315
|
+
ok: false,
|
|
316
|
+
error: status === "start_failed" || status === "health_failed"
|
|
317
|
+
? `Release #${release?.number} ${status.replace("_", " ")}. The previous release keeps serving.`
|
|
318
|
+
: `Release #${release?.number} is ${status ?? "still rolling out"}.`,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
setStep("result");
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
if (cancelled)
|
|
325
|
+
return;
|
|
326
|
+
setPhases((ps) => ps.map((p) => (p.state === "active" ? { ...p, state: "failed" } : p)));
|
|
327
|
+
setOutcome({ ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
328
|
+
setStep("result");
|
|
329
|
+
}
|
|
330
|
+
})();
|
|
331
|
+
return () => {
|
|
332
|
+
cancelled = true;
|
|
333
|
+
};
|
|
334
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
335
|
+
}, [step]);
|
|
336
|
+
const createApp = useCallback(async (planTier) => {
|
|
337
|
+
setCreating(true);
|
|
338
|
+
setError(null);
|
|
339
|
+
try {
|
|
340
|
+
const created = await api.apps.create({ slug: slug.trim(), ...(planTier ? { planTier } : {}) }, projectId ?? undefined);
|
|
341
|
+
setApps((list) => [created, ...(list ?? [])]);
|
|
342
|
+
setApp(created);
|
|
343
|
+
setStep("dir");
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
347
|
+
setStep("app");
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
setCreating(false);
|
|
351
|
+
}
|
|
352
|
+
}, [api, slug, projectId]);
|
|
353
|
+
const patchOrder = useCallback((patch) => setOrder((o) => (o ? { ...o, ...patch } : o)), []);
|
|
354
|
+
// A paid tier is an ORDER, not a bare create (POST /apps refuses it with
|
|
355
|
+
// 402): walk the same steps `orderPaidApp` runs — location, price, credits,
|
|
356
|
+
// card, confirm, charge — one ink step each. Nothing charges before the
|
|
357
|
+
// confirm step's Enter.
|
|
358
|
+
const pickPlan = useCallback(async (planName) => {
|
|
359
|
+
const plan = plans.find((p) => p.name === planName);
|
|
360
|
+
if (!plan || !isPaidPlan(plan)) {
|
|
361
|
+
await createApp(planName);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
setError(null);
|
|
365
|
+
setCreating(true);
|
|
366
|
+
try {
|
|
367
|
+
const locations = toLocationOptions(await api.apps.locations());
|
|
368
|
+
if (locations.length === 0) {
|
|
369
|
+
setError("No locations are available for new apps right now. Try again later.");
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const next = {
|
|
373
|
+
plan,
|
|
374
|
+
region: locations.length === 1 ? locations[0].region : null,
|
|
375
|
+
locations,
|
|
376
|
+
quote: null,
|
|
377
|
+
credit: 0,
|
|
378
|
+
applyCredits: false,
|
|
379
|
+
methods: null,
|
|
380
|
+
methodId: null,
|
|
381
|
+
status: null,
|
|
382
|
+
};
|
|
383
|
+
setOrder(next);
|
|
384
|
+
setStep(next.region ? "new-app-price" : "new-app-region");
|
|
385
|
+
}
|
|
386
|
+
catch (e) {
|
|
387
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
388
|
+
}
|
|
389
|
+
finally {
|
|
390
|
+
setCreating(false);
|
|
391
|
+
}
|
|
392
|
+
}, [api, plans, createApp]);
|
|
393
|
+
const orderSpec = useMemo(() => order?.region
|
|
394
|
+
? { slug: slug.trim(), plan_tier: order.plan.name, region: order.region, project_id: projectId ?? undefined }
|
|
395
|
+
: null, [order?.plan.name, order?.region, slug, projectId]);
|
|
396
|
+
// Price step: the first-month quote (tax included) and the credit balance.
|
|
397
|
+
// A preview the API REFUSED (4xx other than 429) is not a degraded quote —
|
|
398
|
+
// the order would be refused the same way — so it goes back to the plan.
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
if (step !== "new-app-price" || !order || !orderSpec || order.quote)
|
|
401
|
+
return;
|
|
402
|
+
let cancelled = false;
|
|
403
|
+
(async () => {
|
|
404
|
+
let quote;
|
|
405
|
+
try {
|
|
406
|
+
const q = await api.orders.preview({ kind: "app", spec: orderSpec });
|
|
407
|
+
quote = {
|
|
408
|
+
total: Number(q.total),
|
|
409
|
+
taxTotal: Number(q.tax_total ?? 0),
|
|
410
|
+
taxLabel: q.tax_label ?? "tax",
|
|
411
|
+
currency: q.currency ?? "USD",
|
|
412
|
+
degraded: Boolean(q.estimate_degraded),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
catch (e) {
|
|
416
|
+
if (cancelled)
|
|
417
|
+
return;
|
|
418
|
+
if (e instanceof ApiError && e.status >= 400 && e.status < 500 && e.status !== 429) {
|
|
419
|
+
setError(`Can't order ${order.plan.displayName ?? order.plan.name}: ${e.detail}`);
|
|
420
|
+
setOrder(null);
|
|
421
|
+
setStep("new-app-plan");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
quote = { total: Number(order.plan.monthlyAnchorUsd ?? 0), taxTotal: 0, taxLabel: "tax", currency: "USD", degraded: true };
|
|
425
|
+
}
|
|
426
|
+
const balance = await api.billing.balance().catch(() => ({}));
|
|
427
|
+
if (cancelled)
|
|
428
|
+
return;
|
|
429
|
+
const credit = readBalance(balance.credit);
|
|
430
|
+
patchOrder({ quote, credit });
|
|
431
|
+
// No credit to ask about: straight on to the card.
|
|
432
|
+
if (credit <= 0)
|
|
433
|
+
setStep("new-app-pay");
|
|
434
|
+
})();
|
|
435
|
+
return () => {
|
|
436
|
+
cancelled = true;
|
|
437
|
+
};
|
|
438
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
439
|
+
}, [step, orderSpec]);
|
|
440
|
+
// Credits-first decided: credit that covers the whole month skips the card.
|
|
441
|
+
const decideCredits = useCallback((apply) => {
|
|
442
|
+
if (!order?.quote)
|
|
443
|
+
return;
|
|
444
|
+
patchOrder({ applyCredits: apply });
|
|
445
|
+
const covered = apply && order.credit >= order.quote.total && order.quote.total > 0;
|
|
446
|
+
setStep(covered ? "new-app-confirm" : "new-app-pay");
|
|
447
|
+
}, [order, patchOrder]);
|
|
448
|
+
// Payment step: the methods the app_order touchpoint may charge.
|
|
449
|
+
const loadMethods = useCallback(async () => {
|
|
450
|
+
patchOrder({ methods: null });
|
|
451
|
+
const rows = await api.payments.methodsForTouchpoint("app_order").catch(() => []);
|
|
452
|
+
patchOrder({ methods: rows.filter((m) => !isAch(m)) });
|
|
453
|
+
}, [api, patchOrder]);
|
|
454
|
+
useEffect(() => {
|
|
455
|
+
if (step !== "new-app-pay" || !order || order.methods)
|
|
456
|
+
return;
|
|
457
|
+
void loadMethods();
|
|
458
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
459
|
+
}, [step]);
|
|
460
|
+
const coveredByCredit = !!order?.quote && order.applyCredits && order.credit >= order.quote.total && order.quote.total > 0;
|
|
461
|
+
const chosenMethod = order?.methods?.find((m) => m.id === order.methodId) ?? null;
|
|
462
|
+
// Order step: charge, then poll the order until the app row exists.
|
|
463
|
+
useEffect(() => {
|
|
464
|
+
if (step !== "new-app-order" || !order?.quote || !orderSpec)
|
|
465
|
+
return;
|
|
466
|
+
let cancelled = false;
|
|
467
|
+
(async () => {
|
|
468
|
+
try {
|
|
469
|
+
const payment = coveredByCredit
|
|
470
|
+
? { method: "credit_only" }
|
|
471
|
+
: { method: "saved", payment_method_id: order.methodId, apply_credits: order.applyCredits && order.credit > 0 };
|
|
472
|
+
patchOrder({ status: "charging" });
|
|
473
|
+
const result = await api.orders.createApp(orderSpec, payment);
|
|
474
|
+
if (cancelled)
|
|
475
|
+
return;
|
|
476
|
+
if (result.checkout_url)
|
|
477
|
+
throw new Error(`This order needs hosted checkout: ${result.checkout_url}`);
|
|
478
|
+
if (result.status === "failed")
|
|
479
|
+
throw new Error("The charge did not go through; no app created.");
|
|
480
|
+
const wait = await waitFor({
|
|
481
|
+
timeoutSeconds: 120,
|
|
482
|
+
poll: () => api.orders.status(result.order_id),
|
|
483
|
+
classify: (o) => {
|
|
484
|
+
const st = (o.status ?? "").toLowerCase();
|
|
485
|
+
if ((st === "complete" || st === "provisioned") && o.resource_id)
|
|
486
|
+
return { state: "success", label: `order ${st}` };
|
|
487
|
+
if (st === "failed" || st === "cancelled") {
|
|
488
|
+
return { state: "failure", label: `order ${st}`, reason: `Order ${st}: ${o.error_detail ?? "no detail from server"}` };
|
|
489
|
+
}
|
|
490
|
+
return { state: "pending", label: `order ${st || "pending"}` };
|
|
491
|
+
},
|
|
492
|
+
onStatus: (l) => patchOrder({ status: l }),
|
|
493
|
+
});
|
|
494
|
+
if (cancelled)
|
|
495
|
+
return;
|
|
496
|
+
if (wait.outcome === "timeout") {
|
|
497
|
+
throw new Error(`Charged, but the app is still being created (order ${result.order_id}). It will appear in Resources shortly.`);
|
|
498
|
+
}
|
|
499
|
+
if (wait.outcome === "failure")
|
|
500
|
+
throw new Error(wait.reason);
|
|
501
|
+
const created = await api.apps.get(wait.value.resource_id);
|
|
502
|
+
if (cancelled)
|
|
503
|
+
return;
|
|
504
|
+
setApps((list) => [created, ...(list ?? [])]);
|
|
505
|
+
setApp(created);
|
|
506
|
+
setOrder(null);
|
|
507
|
+
setStep("dir");
|
|
508
|
+
}
|
|
509
|
+
catch (e) {
|
|
510
|
+
if (cancelled)
|
|
511
|
+
return;
|
|
512
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
513
|
+
setOrder(null);
|
|
514
|
+
setStep("new-app-plan");
|
|
515
|
+
}
|
|
516
|
+
})();
|
|
517
|
+
return () => {
|
|
518
|
+
cancelled = true;
|
|
519
|
+
};
|
|
520
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
521
|
+
}, [step]);
|
|
522
|
+
const textOwnsKeys = (step === "app" && appPane === "name") || step === "dir";
|
|
523
|
+
useInput((input, key) => {
|
|
524
|
+
if (step === "app" && key.tab) {
|
|
525
|
+
setAppPane((p) => (p === "name" ? "list" : "name"));
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
// Down from the name input lands on the list (Up from its first row comes back).
|
|
529
|
+
if (step === "app" && appPane === "name" && key.downArrow && appItems.length) {
|
|
530
|
+
setAppPane("list");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (textOwnsKeys) {
|
|
534
|
+
if (key.escape) {
|
|
535
|
+
if (step === "app") {
|
|
536
|
+
// Entered from New order → App or the dashboard: Esc leaves the walkthrough.
|
|
537
|
+
if (fromOrder.current || projects.length === 0)
|
|
538
|
+
goBack();
|
|
539
|
+
else
|
|
540
|
+
setStep("project");
|
|
541
|
+
}
|
|
542
|
+
else
|
|
543
|
+
setStep("app");
|
|
544
|
+
}
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (step === "deploying")
|
|
548
|
+
return; // the build continues server-side either way
|
|
549
|
+
if (key.escape) {
|
|
550
|
+
if (step === "project" || step === "result")
|
|
551
|
+
goBack();
|
|
552
|
+
else if (step === "app") {
|
|
553
|
+
if (fromOrder.current || projects.length === 0)
|
|
554
|
+
goBack();
|
|
555
|
+
else
|
|
556
|
+
setStep("project");
|
|
557
|
+
}
|
|
558
|
+
else if (step === "new-app-plan")
|
|
559
|
+
setStep("app");
|
|
560
|
+
else if (step === "new-app-region" || step === "new-app-price") {
|
|
561
|
+
setOrder(null);
|
|
562
|
+
setStep("new-app-plan");
|
|
563
|
+
}
|
|
564
|
+
else if (step === "new-app-pay")
|
|
565
|
+
setStep("new-app-price");
|
|
566
|
+
else if (step === "new-app-confirm")
|
|
567
|
+
setStep(coveredByCredit ? "new-app-price" : "new-app-pay");
|
|
568
|
+
else if (step === "confirm")
|
|
569
|
+
setStep("dir");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (step === "new-app-order")
|
|
573
|
+
return; // the charge is in flight
|
|
574
|
+
if (step === "new-app-price") {
|
|
575
|
+
if (!order?.quote || order.credit <= 0)
|
|
576
|
+
return;
|
|
577
|
+
if (input === "y" || key.return)
|
|
578
|
+
decideCredits(true);
|
|
579
|
+
else if (input === "n")
|
|
580
|
+
decideCredits(false);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (step === "new-app-pay") {
|
|
584
|
+
if (input === "r")
|
|
585
|
+
void loadMethods();
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (step === "new-app-confirm") {
|
|
589
|
+
if (key.return)
|
|
590
|
+
setStep("new-app-order");
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (step === "confirm") {
|
|
594
|
+
if (key.return)
|
|
595
|
+
setStep("deploying");
|
|
596
|
+
else if (input === "f")
|
|
597
|
+
setFrontend((f) => (f === "dockerfile" ? "railpack" : "dockerfile"));
|
|
598
|
+
else if (input === "l" && !alreadyLinked)
|
|
599
|
+
setLinkDir((v) => !v);
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
if (step === "result") {
|
|
603
|
+
if (input === "d") {
|
|
604
|
+
setOutcome(null);
|
|
605
|
+
setStep("confirm");
|
|
606
|
+
}
|
|
607
|
+
else if (input === "a" && app) {
|
|
608
|
+
navigate("resources");
|
|
609
|
+
}
|
|
610
|
+
else if (key.return)
|
|
611
|
+
goBack();
|
|
612
|
+
}
|
|
613
|
+
}, { isActive: true });
|
|
614
|
+
const stepIndex = Math.max(0, PROGRESS.indexOf(NEW_APP_STEPS.has(step) ? "app" : step));
|
|
615
|
+
const totalRows = process.stdout.rows ?? 24;
|
|
616
|
+
const paneHeight = Math.max(12, totalRows - 6);
|
|
617
|
+
const projectItems = useMemo(() => projects.map((p) => ({ value: p.id, label: p.name, hint: p.is_default ? "default" : (p.description ?? undefined) })), [projects]);
|
|
618
|
+
// Existing apps in the chosen project; "new app" is the input above the list.
|
|
619
|
+
const appItems = useMemo(() => (apps ?? [])
|
|
620
|
+
.filter((a) => !projectId || a.projectId === projectId)
|
|
621
|
+
.map((a) => ({ value: a.id, label: a.slug, hint: `${a.hostname} · ${a.planTier} · ${a.status}` })), [apps, projectId]);
|
|
622
|
+
const submitNewApp = useCallback((v) => {
|
|
623
|
+
const name = v.trim();
|
|
624
|
+
if (!SLUG_RE.test(name)) {
|
|
625
|
+
setError("Use 3–63 lowercase letters, digits and dashes.");
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (nameState.kind !== "available")
|
|
629
|
+
return; // still checking, or taken
|
|
630
|
+
setError(null);
|
|
631
|
+
setStep("new-app-plan");
|
|
632
|
+
}, [nameState.kind]);
|
|
633
|
+
const planItems = useMemo(() => plans.length
|
|
634
|
+
? plans.map((p) => ({
|
|
635
|
+
value: p.name,
|
|
636
|
+
label: p.displayName ?? p.name,
|
|
637
|
+
hint: planLine(p),
|
|
638
|
+
// At the per-team cap (or no capacity): shown, not pickable —
|
|
639
|
+
// the same rule the CLI picker applies.
|
|
640
|
+
disabled: planCapReason(p),
|
|
641
|
+
}))
|
|
642
|
+
: [{ value: "", label: "Default plan", hint: "the platform's default tier" }], [plans]);
|
|
643
|
+
const regionItems = useMemo(() => (order?.locations ?? []).map((l) => ({ value: l.region, label: l.city, hint: l.code })), [order?.locations]);
|
|
644
|
+
const methodItems = useMemo(() => (order?.methods ?? []).map((m) => ({
|
|
645
|
+
value: m.id,
|
|
646
|
+
label: describePaymentMethod(m),
|
|
647
|
+
disabled: m.eligible ? undefined : (m.ineligible_reason ?? "not allowed for apps"),
|
|
648
|
+
})), [order?.methods]);
|
|
649
|
+
const eligibleMethods = (order?.methods ?? []).filter((m) => m.eligible);
|
|
650
|
+
let body;
|
|
651
|
+
if (loadError) {
|
|
652
|
+
body = React.createElement(Text, { color: colors.error },
|
|
653
|
+
"Error: ",
|
|
654
|
+
loadError);
|
|
655
|
+
}
|
|
656
|
+
else if (apps === null) {
|
|
657
|
+
body = React.createElement(Spinner, { label: "Loading your apps\u2026" });
|
|
658
|
+
}
|
|
659
|
+
else if (step === "project") {
|
|
660
|
+
body = projects.length === 0 ? (React.createElement(Box, { flexDirection: "column" },
|
|
661
|
+
React.createElement(Text, { color: colors.error }, "You need at least one project. Create one in the portal and try again."),
|
|
662
|
+
React.createElement(Box, { marginTop: 1 },
|
|
663
|
+
React.createElement(Text, { color: colors.dim }, "Esc to go back.")))) : (React.createElement(Box, { flexDirection: "column" },
|
|
664
|
+
React.createElement(Text, { color: colors.dim }, "Which project is the app in?"),
|
|
665
|
+
React.createElement(Box, { marginTop: 1 },
|
|
666
|
+
React.createElement(Menu, { items: projectItems, initialIndex: Math.max(0, projectItems.findIndex((i) => i.value === projectId)), focused: true, onSelect: (v) => {
|
|
667
|
+
setProjectId(v);
|
|
668
|
+
setAppPane("name");
|
|
669
|
+
setStep("app");
|
|
670
|
+
} }))));
|
|
671
|
+
}
|
|
672
|
+
else if (step === "app") {
|
|
673
|
+
const nameLine = nameState.kind === "checking" ? React.createElement(Text, { color: colors.dim }, "checking\u2026")
|
|
674
|
+
: nameState.kind === "available" ? React.createElement(Text, { color: colors.success }, `available${nameState.hostname ? ` · ${nameState.hostname}` : ""}`)
|
|
675
|
+
: nameState.kind === "invalid" || nameState.kind === "taken" ? React.createElement(Text, { color: colors.error }, nameState.msg)
|
|
676
|
+
: React.createElement(Text, { color: colors.dim }, "lowercase letters, digits and dashes \u2014 it becomes the hostname");
|
|
677
|
+
body = (React.createElement(Box, { flexDirection: "column" },
|
|
678
|
+
React.createElement(Text, { color: colors.dim }, "Name a new app, or pick an existing one below (\u2193 / Tab to move between them)."),
|
|
679
|
+
error ? React.createElement(Text, { color: colors.error }, error) : null,
|
|
680
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
681
|
+
React.createElement(Text, { color: appPane === "name" ? colors.accent : colors.dim }, "New app"),
|
|
682
|
+
React.createElement(InputBar, { value: slug, onChange: setSlug, onSubmit: submitNewApp, placeholder: "my-api", focused: appPane === "name" }),
|
|
683
|
+
React.createElement(Box, { paddingX: 4 }, nameLine)),
|
|
684
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
685
|
+
React.createElement(Text, { color: appPane === "list" ? colors.accent : colors.dim }, appItems.length ? "Existing apps" : "No apps in this project yet"),
|
|
686
|
+
appItems.length ? (React.createElement(Menu, { items: appItems, focused: appPane === "list", onUpFromTop: () => setAppPane("name"), onSelect: (v) => {
|
|
687
|
+
const hit = apps.find((a) => a.id === v);
|
|
688
|
+
if (hit)
|
|
689
|
+
pickApp(hit);
|
|
690
|
+
} })) : null,
|
|
691
|
+
truncationNote(apps?.length ?? 0, appsTotal, "`synteq link --app <slug>`") ? (React.createElement(Text, { color: colors.dim }, truncationNote(apps?.length ?? 0, appsTotal, "`synteq link --app <slug>`"))) : null)));
|
|
692
|
+
}
|
|
693
|
+
else if (step === "new-app-plan") {
|
|
694
|
+
body = creating ? (React.createElement(Spinner, { label: `Creating ${slug}…` })) : (React.createElement(Box, { flexDirection: "column" },
|
|
695
|
+
React.createElement(Text, { color: colors.dim },
|
|
696
|
+
"Pick a plan for ",
|
|
697
|
+
slug,
|
|
698
|
+
". A paid plan charges its first month now."),
|
|
699
|
+
error ? React.createElement(Text, { color: colors.error }, error) : null,
|
|
700
|
+
React.createElement(Box, { marginTop: 1 },
|
|
701
|
+
React.createElement(Menu, { items: planItems, focused: true, onSelect: (v) => void pickPlan(v || null) }))));
|
|
702
|
+
}
|
|
703
|
+
else if (step === "new-app-region" && order) {
|
|
704
|
+
body = (React.createElement(Box, { flexDirection: "column" },
|
|
705
|
+
React.createElement(Text, { color: colors.dim },
|
|
706
|
+
"Where should ",
|
|
707
|
+
slug,
|
|
708
|
+
" run?"),
|
|
709
|
+
React.createElement(Box, { marginTop: 1 },
|
|
710
|
+
React.createElement(Menu, { items: regionItems, focused: true, onSelect: (region) => {
|
|
711
|
+
patchOrder({ region });
|
|
712
|
+
setStep("new-app-price");
|
|
713
|
+
} }))));
|
|
714
|
+
}
|
|
715
|
+
else if (step === "new-app-price" && order) {
|
|
716
|
+
body = !order.quote ? (React.createElement(Spinner, { label: "Pricing the first month\u2026" })) : (React.createElement(Box, { flexDirection: "column" },
|
|
717
|
+
React.createElement(Text, { color: colors.text }, priceLine({ ...order.quote, slug: slug.trim(), planName: order.plan.displayName ?? order.plan.name })),
|
|
718
|
+
order.credit > 0 ? (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
719
|
+
React.createElement(Text, { color: colors.text },
|
|
720
|
+
"Apply your credit balance ($",
|
|
721
|
+
order.credit.toFixed(2),
|
|
722
|
+
") first?"),
|
|
723
|
+
React.createElement(Text, { color: colors.dim }, "y / Enter yes \u00B7 n no \u00B7 Esc back"))) : null));
|
|
724
|
+
}
|
|
725
|
+
else if (step === "new-app-pay" && order) {
|
|
726
|
+
body = !order.methods ? (React.createElement(Spinner, { label: "Loading payment methods\u2026" })) : eligibleMethods.length === 0 ? (React.createElement(Box, { flexDirection: "column" },
|
|
727
|
+
React.createElement(Text, { color: colors.text }, "No saved payment method can be charged for an app."),
|
|
728
|
+
order.methods.map((m) => m.ineligible_reason).filter(Boolean)[0] ? (React.createElement(Text, { color: colors.dim }, order.methods.map((m) => m.ineligible_reason).filter(Boolean)[0])) : null,
|
|
729
|
+
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
730
|
+
React.createElement(Text, { color: colors.dim },
|
|
731
|
+
"Add a card in the portal: ",
|
|
732
|
+
React.createElement(Text, { color: colors.text }, portalUrl(config.get().api_base_url, PAYMENT_METHODS_PATH))),
|
|
733
|
+
React.createElement(Text, { color: colors.dim }, "r re-check \u00B7 Esc back")))) : (React.createElement(Box, { flexDirection: "column" },
|
|
734
|
+
React.createElement(Text, { color: colors.dim }, "Charge which payment method?"),
|
|
735
|
+
React.createElement(Box, { marginTop: 1 },
|
|
736
|
+
React.createElement(Menu, { items: methodItems, focused: true, onSelect: (methodId) => {
|
|
737
|
+
patchOrder({ methodId });
|
|
738
|
+
setStep("new-app-confirm");
|
|
739
|
+
} }))));
|
|
740
|
+
}
|
|
741
|
+
else if (step === "new-app-confirm" && order?.quote) {
|
|
742
|
+
body = (React.createElement(Box, { flexDirection: "column" },
|
|
743
|
+
React.createElement(Text, { color: colors.text }, chargeLine({
|
|
744
|
+
...order.quote,
|
|
745
|
+
slug: slug.trim(),
|
|
746
|
+
planName: order.plan.displayName ?? order.plan.name,
|
|
747
|
+
method: chosenMethod ?? undefined,
|
|
748
|
+
creditsApplied: order.applyCredits ? Math.min(order.credit, order.quote.total) : 0,
|
|
749
|
+
})),
|
|
750
|
+
order.quote.degraded ? (React.createElement(Text, { color: colors.dim }, "Tax estimate unavailable; the final total is on the invoice.")) : null,
|
|
751
|
+
React.createElement(Box, { marginTop: 1 },
|
|
752
|
+
React.createElement(Text, { color: colors.dim }, "Enter charges and creates the app \u00B7 Esc back"))));
|
|
753
|
+
}
|
|
754
|
+
else if (step === "new-app-order" && order) {
|
|
755
|
+
body = React.createElement(Spinner, { label: `Charging and creating ${slug.trim()}… ${order.status ?? ""}` });
|
|
756
|
+
}
|
|
757
|
+
else if (step === "dir") {
|
|
758
|
+
body = (React.createElement(Box, { flexDirection: "column" },
|
|
759
|
+
React.createElement(Text, { color: colors.dim },
|
|
760
|
+
"Directory to build and ship to ",
|
|
761
|
+
React.createElement(Text, { color: colors.text }, app?.slug),
|
|
762
|
+
". Enter keeps the path shown."),
|
|
763
|
+
dirError ? React.createElement(Text, { color: colors.error }, dirError) : null,
|
|
764
|
+
React.createElement(Box, { marginTop: 1 },
|
|
765
|
+
React.createElement(InputBar, { value: dirInput, onChange: setDirInput, onSubmit: confirmDir, placeholder: process.cwd(), focused: true }))));
|
|
766
|
+
}
|
|
767
|
+
else if (step === "confirm" && app && dir) {
|
|
768
|
+
body = (React.createElement(Box, { flexDirection: "column" },
|
|
769
|
+
React.createElement(Row, { label: "App", value: `${app.slug} ${colorless(app.status)}` }),
|
|
770
|
+
React.createElement(Row, { label: "URL", value: `https://${app.hostname}` }),
|
|
771
|
+
React.createElement(Row, { label: "Directory", value: dir }),
|
|
772
|
+
React.createElement(Row, { label: "Builder", value: frontend === "dockerfile" ? "Dockerfile (found in the directory)" : "railpack (zero-config; no Dockerfile found)" }),
|
|
773
|
+
React.createElement(Row, { label: "Link directory", value: alreadyLinked ? "already linked to this app" : linkDir ? "yes — a bare `synteq deploy` here will target this app" : "no" }),
|
|
774
|
+
React.createElement(Box, { marginTop: 1 },
|
|
775
|
+
React.createElement(Text, { color: colors.dim },
|
|
776
|
+
"Enter to deploy \u00B7 f toggle builder",
|
|
777
|
+
alreadyLinked ? "" : " · l toggle link",
|
|
778
|
+
" \u00B7 Esc back"))));
|
|
779
|
+
}
|
|
780
|
+
else if (step === "deploying") {
|
|
781
|
+
body = (React.createElement(Box, { flexDirection: "column" },
|
|
782
|
+
phases.map((p) => (React.createElement(Box, { key: p.label },
|
|
783
|
+
React.createElement(Box, { width: 3 },
|
|
784
|
+
React.createElement(Text, { color: phaseColor(p.state) }, phaseGlyph(p.state))),
|
|
785
|
+
React.createElement(Box, { width: 16 },
|
|
786
|
+
React.createElement(Text, { color: p.state === "pending" ? colors.dim : colors.text }, p.label)),
|
|
787
|
+
React.createElement(Text, { color: colors.dim }, p.detail ?? "")))),
|
|
788
|
+
React.createElement(Box, { marginTop: 1 },
|
|
789
|
+
React.createElement(Spinner, { label: "Working\u2026" }))));
|
|
790
|
+
}
|
|
791
|
+
else if (step === "result" && outcome) {
|
|
792
|
+
body = outcome.ok ? (React.createElement(Box, { flexDirection: "column" },
|
|
793
|
+
React.createElement(Text, { color: colors.success },
|
|
794
|
+
glyphs.check,
|
|
795
|
+
" Deployed ",
|
|
796
|
+
app?.slug,
|
|
797
|
+
outcome.release !== null ? ` · release #${outcome.release}` : "",
|
|
798
|
+
outcome.durationMs !== null ? ` · ${Math.round(outcome.durationMs / 1000)}s` : ""),
|
|
799
|
+
React.createElement(Box, { marginTop: 1 },
|
|
800
|
+
React.createElement(Row, { label: "URL", value: outcome.url })),
|
|
801
|
+
outcome.linked ? (React.createElement(Text, { color: colors.dim },
|
|
802
|
+
"Linked ",
|
|
803
|
+
dir,
|
|
804
|
+
" \u2192 ",
|
|
805
|
+
app?.slug,
|
|
806
|
+
". `synteq deploy` from that directory targets it now.")) : null,
|
|
807
|
+
React.createElement(Box, { marginTop: 1 },
|
|
808
|
+
React.createElement(Text, { color: colors.dim }, "d deploy again \u00B7 a open Resources (logs, metrics, env) \u00B7 Enter back")))) : (React.createElement(Box, { flexDirection: "column" },
|
|
809
|
+
React.createElement(Text, { color: colors.error }, "Deploy did not go live."),
|
|
810
|
+
React.createElement(Box, { marginTop: 1 },
|
|
811
|
+
React.createElement(Text, { color: colors.text }, outcome.error)),
|
|
812
|
+
phases.length ? (React.createElement(Box, { marginTop: 1, flexDirection: "column" }, phases.map((p) => (React.createElement(Box, { key: p.label },
|
|
813
|
+
React.createElement(Box, { width: 3 },
|
|
814
|
+
React.createElement(Text, { color: phaseColor(p.state) }, phaseGlyph(p.state))),
|
|
815
|
+
React.createElement(Box, { width: 16 },
|
|
816
|
+
React.createElement(Text, { color: colors.dim }, p.label)),
|
|
817
|
+
React.createElement(Text, { color: colors.dim }, p.detail ?? "")))))) : null,
|
|
818
|
+
React.createElement(Box, { marginTop: 1 },
|
|
819
|
+
React.createElement(Text, { color: colors.dim }, "d try again \u00B7 Enter back"))));
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
body = React.createElement(Spinner, { label: "\u2026" });
|
|
823
|
+
}
|
|
824
|
+
return (React.createElement(CenteredFrame, { fullHeight: true, maxWidth: 120 },
|
|
825
|
+
React.createElement(Box, { paddingX: 2, paddingY: 0, height: 2, alignItems: "center" },
|
|
826
|
+
React.createElement(BackButton, { onBack: () => {
|
|
827
|
+
if (step === "deploying")
|
|
828
|
+
return;
|
|
829
|
+
if (step === "app" || step === "result")
|
|
830
|
+
goBack();
|
|
831
|
+
else if (step === "confirm")
|
|
832
|
+
setStep("dir");
|
|
833
|
+
else if (step === "new-app-order")
|
|
834
|
+
return;
|
|
835
|
+
else {
|
|
836
|
+
setOrder(null);
|
|
837
|
+
setStep("app");
|
|
838
|
+
}
|
|
839
|
+
} }),
|
|
840
|
+
React.createElement(Text, { color: colors.dim },
|
|
841
|
+
" · ",
|
|
842
|
+
React.createElement(Text, { color: colors.text }, "Deploy"),
|
|
843
|
+
app ? React.createElement(Text, { color: colors.dim }, ` · ${app.slug}`) : null)),
|
|
844
|
+
React.createElement(Box, { flexGrow: 1, flexDirection: "column", paddingX: 1 },
|
|
845
|
+
React.createElement(Panel, { title: STEP_LABELS[step], rightLabel: `step ${stepIndex + 1} of ${PROGRESS.length}`, paddingX: 2, paddingY: 1, height: paneHeight, borderColor: colors.accent }, body)),
|
|
846
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
847
|
+
React.createElement(Tip, null, step === "project"
|
|
848
|
+
? "↑↓ pick a project · Enter · Esc back."
|
|
849
|
+
: step === "app"
|
|
850
|
+
? "Type a name · Enter when it reads available · Tab to the app list · Esc back."
|
|
851
|
+
: step === "dir"
|
|
852
|
+
? "Type a path · Enter to continue · Esc to pick another app."
|
|
853
|
+
: step === "confirm"
|
|
854
|
+
? "Enter deploys · f switches Dockerfile / railpack · Esc back."
|
|
855
|
+
: step === "deploying"
|
|
856
|
+
? "The build runs server-side; you can leave and it keeps going."
|
|
857
|
+
: step === "result"
|
|
858
|
+
? "d to deploy again · Enter to go back."
|
|
859
|
+
: step === "new-app-plan"
|
|
860
|
+
? "↑↓ pick a plan · greyed tiers are at this team's cap · Esc back."
|
|
861
|
+
: step === "new-app-confirm"
|
|
862
|
+
? "Nothing is charged until you press Enter here."
|
|
863
|
+
: step === "new-app-order"
|
|
864
|
+
? "Charging the first month and creating the app…"
|
|
865
|
+
: "Enter to continue · Esc back."),
|
|
866
|
+
React.createElement(StatusBar, { left: STEP_LABELS[step], right: [
|
|
867
|
+
header.projectName ? `Project: ${header.projectName}` : "",
|
|
868
|
+
header.teamName ? `Team: ${header.teamName}` : "",
|
|
869
|
+
].filter(Boolean) }))));
|
|
870
|
+
}
|
|
871
|
+
function Row({ label, value }) {
|
|
872
|
+
return (React.createElement(Box, null,
|
|
873
|
+
React.createElement(Box, { width: 16 },
|
|
874
|
+
React.createElement(Text, { color: colors.dim }, label)),
|
|
875
|
+
React.createElement(Text, { color: colors.text }, value)));
|
|
876
|
+
}
|
|
877
|
+
function colorless(s) {
|
|
878
|
+
return statusColor(s) ? s : s;
|
|
879
|
+
}
|
|
880
|
+
function phaseGlyph(state) {
|
|
881
|
+
switch (state) {
|
|
882
|
+
case "done":
|
|
883
|
+
return glyphs.check;
|
|
884
|
+
case "failed":
|
|
885
|
+
return "✗";
|
|
886
|
+
case "active":
|
|
887
|
+
return glyphs.chevronRight;
|
|
888
|
+
default:
|
|
889
|
+
return "·";
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function phaseColor(state) {
|
|
893
|
+
switch (state) {
|
|
894
|
+
case "done":
|
|
895
|
+
return colors.success;
|
|
896
|
+
case "failed":
|
|
897
|
+
return colors.error;
|
|
898
|
+
case "active":
|
|
899
|
+
return colors.accent;
|
|
900
|
+
default:
|
|
901
|
+
return colors.dim;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
/** The directory this machine has linked to the app, if any. */
|
|
905
|
+
function linkedDirFor(links, appId) {
|
|
906
|
+
for (const [path, link] of Object.entries(links ?? {})) {
|
|
907
|
+
if (link.app_id === appId && (link.kind ?? "app") === "app" && existsSync(path))
|
|
908
|
+
return path;
|
|
909
|
+
}
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
//# sourceMappingURL=Deploy.js.map
|