relight-cli 0.1.0 → 0.3.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.
- package/README.md +77 -34
- package/package.json +12 -4
- package/src/cli.js +350 -1
- package/src/commands/apps.js +128 -0
- package/src/commands/auth.js +13 -4
- package/src/commands/config.js +282 -0
- package/src/commands/cost.js +593 -0
- package/src/commands/db.js +775 -0
- package/src/commands/deploy.js +264 -0
- package/src/commands/doctor.js +69 -13
- package/src/commands/domains.js +223 -0
- package/src/commands/logs.js +111 -0
- package/src/commands/open.js +42 -0
- package/src/commands/ps.js +121 -0
- package/src/commands/scale.js +132 -0
- package/src/commands/service.js +227 -0
- package/src/lib/clouds/aws.js +309 -35
- package/src/lib/clouds/cf.js +401 -2
- package/src/lib/clouds/gcp.js +255 -4
- package/src/lib/clouds/neon.js +147 -0
- package/src/lib/clouds/slicervm.js +139 -0
- package/src/lib/config.js +200 -2
- package/src/lib/docker.js +34 -0
- package/src/lib/link.js +31 -5
- package/src/lib/providers/aws/app.js +481 -0
- package/src/lib/providers/aws/db.js +504 -0
- package/src/lib/providers/aws/dns.js +232 -0
- package/src/lib/providers/aws/registry.js +59 -0
- package/src/lib/providers/cf/app.js +596 -0
- package/src/lib/providers/cf/bundle.js +70 -0
- package/src/lib/providers/cf/db.js +181 -0
- package/src/lib/providers/cf/dns.js +148 -0
- package/src/lib/providers/cf/registry.js +17 -0
- package/src/lib/providers/gcp/app.js +429 -0
- package/src/lib/providers/gcp/db.js +372 -0
- package/src/lib/providers/gcp/dns.js +166 -0
- package/src/lib/providers/gcp/registry.js +30 -0
- package/src/lib/providers/neon/db.js +306 -0
- package/src/lib/providers/resolve.js +79 -0
- package/src/lib/providers/slicervm/app.js +396 -0
- package/src/lib/providers/slicervm/db.js +33 -0
- package/src/lib/providers/slicervm/dns.js +58 -0
- package/src/lib/providers/slicervm/registry.js +7 -0
- package/worker-template/package.json +10 -0
- package/worker-template/src/index.js +260 -0
package/README.md
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
Deploy Docker containers to your cloud with scale-to-zero. Apps sleep when idle and wake on the next request.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
$ relight deploy myapp . --
|
|
6
|
+
$ relight deploy myapp . --cloud gcp
|
|
7
7
|
Building image...
|
|
8
|
-
Pushing to
|
|
9
|
-
Deploying to Cloud Run (us-
|
|
8
|
+
Pushing to us-docker.pkg.dev/my-project/relight/myapp:v1...
|
|
9
|
+
Deploying to Cloud Run (us-central1)...
|
|
10
10
|
|
|
11
11
|
--> Live at https://myapp-abc123.run.app
|
|
12
12
|
Sleeps after 30s idle. $0 when sleeping.
|
|
@@ -16,15 +16,14 @@ $ relight deploy myapp . --compute gcp
|
|
|
16
16
|
|
|
17
17
|
Relight is a CLI that deploys and manages Docker containers across multiple cloud providers. It talks directly to each cloud's API using your own credentials. No vendor infrastructure gets installed in your account.
|
|
18
18
|
|
|
19
|
-
Supported
|
|
19
|
+
Supported backends:
|
|
20
20
|
|
|
21
|
-
| Cloud | Backend | Scale to zero |
|
|
22
|
-
|
|
23
|
-
| GCP | Cloud Run | Yes |
|
|
24
|
-
| AWS | App Runner | Yes |
|
|
25
|
-
| Cloudflare | Containers (Workers + Durable Objects) | Yes |
|
|
26
|
-
|
|
27
|
-
More backends planned: Azure Container Apps.
|
|
21
|
+
| Cloud | Backend | Scale to zero | Database | DNS |
|
|
22
|
+
|---|---|---|---|---|
|
|
23
|
+
| GCP | Cloud Run | Yes | Cloud SQL (PostgreSQL) | Cloud DNS |
|
|
24
|
+
| AWS | App Runner | Yes | RDS (PostgreSQL) | Route 53 |
|
|
25
|
+
| Cloudflare | Containers (Workers + Durable Objects) | Yes | D1 (SQLite) | Cloudflare DNS |
|
|
26
|
+
| SlicerVM | Self-hosted | Yes | - | - |
|
|
28
27
|
|
|
29
28
|
## Install
|
|
30
29
|
|
|
@@ -38,7 +37,7 @@ Requires Node.js 20+ and Docker.
|
|
|
38
37
|
|
|
39
38
|
```sh
|
|
40
39
|
# Authenticate with a cloud provider
|
|
41
|
-
relight auth --
|
|
40
|
+
relight auth --cloud gcp
|
|
42
41
|
|
|
43
42
|
# Deploy from a Dockerfile
|
|
44
43
|
relight deploy myapp .
|
|
@@ -51,6 +50,9 @@ relight logs myapp
|
|
|
51
50
|
|
|
52
51
|
# Open in browser
|
|
53
52
|
relight open myapp
|
|
53
|
+
|
|
54
|
+
# Create a database
|
|
55
|
+
relight db create myapp
|
|
54
56
|
```
|
|
55
57
|
|
|
56
58
|
The first deploy links the current directory to the app name. After that, `relight deploy` is enough.
|
|
@@ -74,7 +76,16 @@ relight scale [name] Show or adjust instance count and resources
|
|
|
74
76
|
relight domains list [name] List custom domains
|
|
75
77
|
relight domains add [domain] Add a custom domain with DNS setup
|
|
76
78
|
relight domains remove [domain] Remove a custom domain
|
|
77
|
-
relight
|
|
79
|
+
relight db create [name] Create a managed database
|
|
80
|
+
relight db destroy [name] Destroy a database
|
|
81
|
+
relight db info [name] Show database details
|
|
82
|
+
relight db shell [name] Interactive SQL shell
|
|
83
|
+
relight db query [name] <sql> Run a SQL query
|
|
84
|
+
relight db import [name] <file> Import a SQL file
|
|
85
|
+
relight db export [name] Export database as SQL
|
|
86
|
+
relight db token [name] Show or rotate DB token
|
|
87
|
+
relight db reset [name] Drop all tables
|
|
88
|
+
relight regions [--cloud <cloud>] List available regions for a cloud
|
|
78
89
|
relight cost [name] Show estimated costs
|
|
79
90
|
relight doctor Check local setup and cloud connectivity
|
|
80
91
|
```
|
|
@@ -83,13 +94,13 @@ Config changes (`config set`, `config unset`, `scale`, `domains`) are applied li
|
|
|
83
94
|
|
|
84
95
|
## How it works
|
|
85
96
|
|
|
86
|
-
1. `relight auth` stores
|
|
97
|
+
1. `relight auth` stores credentials locally at `~/.relight/config.json`. One set of credentials per cloud provider. No cross-account IAM roles, no OAuth flows, no vendor access to your account.
|
|
87
98
|
|
|
88
|
-
2. `relight deploy` builds a Docker image locally, pushes it to the cloud's container registry (
|
|
99
|
+
2. `relight deploy` builds a Docker image locally, pushes it to the cloud's container registry (Artifact Registry, ECR, or Cloudflare Registry), and deploys it using the cloud's native container service.
|
|
89
100
|
|
|
90
101
|
3. The deployed app sleeps after a configurable idle period (default 30s). The next incoming request wakes it. Cold starts are typically 1-5 seconds depending on the cloud and image size.
|
|
91
102
|
|
|
92
|
-
4. All app state (config, scaling, domains) lives in the cloud provider's API. The only local files are your auth
|
|
103
|
+
4. All app state (config, scaling, domains, database bindings) lives in the cloud provider's API. The only local files are your auth credentials and a `.relight.yaml` link file in your project directory. You can manage your apps from any machine.
|
|
93
104
|
|
|
94
105
|
## Fleet view
|
|
95
106
|
|
|
@@ -101,18 +112,38 @@ $ relight apps
|
|
|
101
112
|
NAME CLOUD STATUS INSTANCES COST/MTD LAST ACTIVE
|
|
102
113
|
myapi gcp sleeping 0/3 $0.12 2h ago
|
|
103
114
|
frontend cf active 2/5 $1.84 now
|
|
115
|
+
worker aws sleeping 0/1 $0.05 1h ago
|
|
104
116
|
dashboard gcp sleeping 0/1 $0.00 3d ago
|
|
105
|
-
|
|
106
|
-
TOTAL $
|
|
117
|
+
-----------------------------------------------------------------
|
|
118
|
+
TOTAL $2.01
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Databases
|
|
122
|
+
|
|
123
|
+
Relight manages databases alongside your apps. Each cloud uses its native database service:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
# Create a database for your app
|
|
127
|
+
relight db create myapp
|
|
128
|
+
|
|
129
|
+
# Interactive SQL shell
|
|
130
|
+
relight db shell myapp
|
|
131
|
+
|
|
132
|
+
# Run a query
|
|
133
|
+
relight db query myapp "SELECT * FROM users"
|
|
107
134
|
```
|
|
108
135
|
|
|
136
|
+
GCP and AWS use PostgreSQL (Cloud SQL and RDS). Cloudflare uses D1 (SQLite). The connection URL and credentials are automatically injected into your app's environment as `DATABASE_URL` and `DB_TOKEN`.
|
|
137
|
+
|
|
138
|
+
Cross-cloud databases are supported - you can attach an AWS RDS database to a GCP Cloud Run app by specifying `--db aws`.
|
|
139
|
+
|
|
109
140
|
## BYOC model
|
|
110
141
|
|
|
111
142
|
Relight deploys to cloud accounts you own. You pay the cloud provider directly at their published rates. Relight itself is free for individual use.
|
|
112
143
|
|
|
113
144
|
What this means in practice:
|
|
114
145
|
|
|
115
|
-
- **Your credentials stay local.**
|
|
146
|
+
- **Your credentials stay local.** Credentials are stored on your machine. Relight has no backend service that holds your cloud credentials.
|
|
116
147
|
- **Nothing is installed in your account.** No Kubernetes clusters, no CloudFormation stacks, no VPCs, no agent processes. Relight uses the cloud's existing container services.
|
|
117
148
|
- **You can stop using Relight anytime.** Your apps are standard Cloud Run services / App Runner services / CF Workers. They continue running without Relight. There's nothing to uninstall.
|
|
118
149
|
|
|
@@ -140,12 +171,12 @@ Relight does not autoscale. You set the maximum instance count and the cloud pro
|
|
|
140
171
|
relight domains add myapp.example.com
|
|
141
172
|
|
|
142
173
|
# Relight creates the DNS record if your domain's DNS is on a supported provider
|
|
174
|
+
# (Cloud DNS, Route 53, or Cloudflare DNS)
|
|
143
175
|
# Otherwise it prints the record for you to create manually
|
|
144
176
|
```
|
|
145
177
|
|
|
146
178
|
## What Relight doesn't do
|
|
147
179
|
|
|
148
|
-
- **Not a general infrastructure tool.** Relight deploys Docker containers with scale-to-zero. It doesn't manage databases, queues, cron jobs, or other infrastructure. Use Terraform, Pulumi, or cloud consoles for those.
|
|
149
180
|
- **No autoscaling.** You set max instances. The cloud scales between 0 and your max based on traffic. There's no custom autoscaling logic.
|
|
150
181
|
- **No CI/CD.** Relight is a deployment tool, not a build pipeline. Integrate it into your CI by running `relight deploy` in your workflow.
|
|
151
182
|
- **Cold starts.** Sleeping apps take 1-5 seconds to respond to the first request. This is inherent to scale-to-zero and varies by cloud and image size.
|
|
@@ -154,24 +185,35 @@ relight domains add myapp.example.com
|
|
|
154
185
|
|
|
155
186
|
### GCP (Cloud Run)
|
|
156
187
|
|
|
157
|
-
- Requires a GCP project with Cloud Run
|
|
158
|
-
-
|
|
159
|
-
- Regions: any Cloud Run region. Run `relight regions --
|
|
188
|
+
- Requires a GCP project with Cloud Run, Artifact Registry, Cloud SQL Admin, Cloud DNS, Logging, and Monitoring APIs enabled.
|
|
189
|
+
- Credentials: service account key JSON file.
|
|
190
|
+
- Regions: any Cloud Run region. Run `relight regions --cloud gcp` to list.
|
|
160
191
|
- Scale-to-zero is native. Minimum instances can be set to 0.
|
|
192
|
+
- Database: Cloud SQL PostgreSQL 15 (`db-f1-micro` by default).
|
|
161
193
|
|
|
162
194
|
### AWS (App Runner)
|
|
163
195
|
|
|
164
|
-
- Requires an AWS account with
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
196
|
+
- Requires an AWS account with IAM user credentials.
|
|
197
|
+
- Required IAM policies: `AWSAppRunnerFullAccess`, `AmazonEC2ContainerRegistryFullAccess`, `AmazonRDSFullAccess`, `AmazonRoute53FullAccess`, `AmazonEC2ReadOnlyAccess`, `CloudWatchLogsReadOnlyAccess`, `IAMFullAccess`.
|
|
198
|
+
- Credentials: IAM access key ID and secret access key.
|
|
199
|
+
- Regions: 9 App Runner regions. Run `relight regions --cloud aws` to list.
|
|
200
|
+
- Scale-to-zero: App Runner pauses services when idle. Minimum 1 provisioned instance ($0.007/vCPU-hr when idle).
|
|
201
|
+
- Database: RDS PostgreSQL 15 (`db.t4g.micro` by default). Provisioning takes 5-15 minutes.
|
|
202
|
+
- Registry: ECR. Repositories and IAM access roles are created automatically.
|
|
168
203
|
|
|
169
204
|
### Cloudflare (Containers)
|
|
170
205
|
|
|
171
206
|
- Requires a Cloudflare account with Containers access (paid Workers plan).
|
|
172
|
-
-
|
|
207
|
+
- Credentials: Cloudflare API token with Workers and Containers permissions.
|
|
173
208
|
- Each app is a Worker backed by Durable Objects running your container. The CLI bundles and uploads the Worker template automatically.
|
|
174
|
-
- Regions use Durable Object `locationHints`
|
|
209
|
+
- Regions use Durable Object `locationHints` - placement is best-effort, not guaranteed.
|
|
210
|
+
- Database: D1 (SQLite).
|
|
211
|
+
|
|
212
|
+
### SlicerVM (Self-hosted)
|
|
213
|
+
|
|
214
|
+
- Requires a running SlicerVM host.
|
|
215
|
+
- Credentials: API URL + token, or a Unix socket for local development.
|
|
216
|
+
- Images are uploaded directly to the host (no external registry needed).
|
|
175
217
|
|
|
176
218
|
## Configuration
|
|
177
219
|
|
|
@@ -180,18 +222,19 @@ Auth config is stored at `~/.relight/config.json`:
|
|
|
180
222
|
```json
|
|
181
223
|
{
|
|
182
224
|
"clouds": {
|
|
183
|
-
"gcp": { "
|
|
225
|
+
"gcp": { "clientEmail": "...", "privateKey": "...", "project": "my-project" },
|
|
184
226
|
"aws": { "accessKeyId": "...", "secretAccessKey": "...", "region": "us-east-1" },
|
|
185
227
|
"cf": { "token": "...", "accountId": "..." }
|
|
186
228
|
},
|
|
187
|
-
"
|
|
229
|
+
"default_cloud": "gcp"
|
|
188
230
|
}
|
|
189
231
|
```
|
|
190
232
|
|
|
191
|
-
Per-project app linking is stored in a `.relight` file in your project directory:
|
|
233
|
+
Per-project app linking is stored in a `.relight.yaml` file in your project directory:
|
|
192
234
|
|
|
193
|
-
```
|
|
194
|
-
|
|
235
|
+
```yaml
|
|
236
|
+
app: myapp
|
|
237
|
+
cloud: gcp
|
|
195
238
|
```
|
|
196
239
|
|
|
197
240
|
## License
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relight-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Deploy
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Deploy web apps across clouds with scale-to-zero",
|
|
5
5
|
"bin": {
|
|
6
6
|
"relight": "./src/cli.js"
|
|
7
7
|
},
|
|
8
8
|
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./src/cli.js",
|
|
11
|
+
"./worker-template": "./worker-template/src/index.js"
|
|
12
|
+
},
|
|
9
13
|
"license": "MIT",
|
|
10
14
|
"files": [
|
|
11
|
-
"src"
|
|
15
|
+
"src",
|
|
16
|
+
"worker-template"
|
|
12
17
|
],
|
|
13
18
|
"keywords": [
|
|
14
19
|
"docker",
|
|
@@ -22,6 +27,9 @@
|
|
|
22
27
|
],
|
|
23
28
|
"dependencies": {
|
|
24
29
|
"commander": "^13.0.0",
|
|
25
|
-
"
|
|
30
|
+
"esbuild": "^0.25.0",
|
|
31
|
+
"kleur": "^4.1.5",
|
|
32
|
+
"pg": "^8.13.0",
|
|
33
|
+
"yaml": "^2.8.2"
|
|
26
34
|
}
|
|
27
35
|
}
|
package/src/cli.js
CHANGED
|
@@ -3,13 +3,38 @@
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { auth } from "./commands/auth.js";
|
|
5
5
|
import { doctor } from "./commands/doctor.js";
|
|
6
|
+
import { deploy } from "./commands/deploy.js";
|
|
7
|
+
import { appsList, appsInfo, appsDestroy } from "./commands/apps.js";
|
|
8
|
+
import {
|
|
9
|
+
configShow,
|
|
10
|
+
configSet,
|
|
11
|
+
configGet,
|
|
12
|
+
configUnset,
|
|
13
|
+
configImport,
|
|
14
|
+
} from "./commands/config.js";
|
|
15
|
+
import { scale } from "./commands/scale.js";
|
|
16
|
+
import { domainsList, domainsAdd, domainsRemove } from "./commands/domains.js";
|
|
17
|
+
import { ps } from "./commands/ps.js";
|
|
18
|
+
import { logs } from "./commands/logs.js";
|
|
19
|
+
import { open } from "./commands/open.js";
|
|
20
|
+
import { cost } from "./commands/cost.js";
|
|
21
|
+
import { serviceList, serviceAdd, serviceRemove } from "./commands/service.js";
|
|
22
|
+
import {
|
|
23
|
+
dbCreate, dbDestroy, dbList, dbInfo, dbAttach, dbDetach,
|
|
24
|
+
dbShell, dbQuery, dbImport, dbExport, dbToken, dbReset,
|
|
25
|
+
} from "./commands/db.js";
|
|
26
|
+
import { fmt } from "./lib/output.js";
|
|
27
|
+
import { createRequire } from "module";
|
|
28
|
+
|
|
29
|
+
var require = createRequire(import.meta.url);
|
|
30
|
+
var { version } = require("../package.json");
|
|
6
31
|
|
|
7
32
|
var program = new Command();
|
|
8
33
|
|
|
9
34
|
program
|
|
10
35
|
.name("relight")
|
|
11
36
|
.description("Deploy and manage Docker containers across clouds with scale-to-zero")
|
|
12
|
-
.version(
|
|
37
|
+
.version(version);
|
|
13
38
|
|
|
14
39
|
// --- Auth ---
|
|
15
40
|
|
|
@@ -22,6 +47,320 @@ program
|
|
|
22
47
|
)
|
|
23
48
|
.action(auth);
|
|
24
49
|
|
|
50
|
+
// --- Service ---
|
|
51
|
+
|
|
52
|
+
var serviceCmd = program.command("service").description("Manage services (compute, databases, etc.)");
|
|
53
|
+
|
|
54
|
+
serviceCmd
|
|
55
|
+
.command("list", { isDefault: true })
|
|
56
|
+
.description("List registered services")
|
|
57
|
+
.action(serviceList);
|
|
58
|
+
|
|
59
|
+
serviceCmd
|
|
60
|
+
.command("add [name]")
|
|
61
|
+
.description("Register a service")
|
|
62
|
+
.action(serviceAdd);
|
|
63
|
+
|
|
64
|
+
serviceCmd
|
|
65
|
+
.command("remove <name>")
|
|
66
|
+
.description("Remove a service")
|
|
67
|
+
.action(serviceRemove);
|
|
68
|
+
|
|
69
|
+
// --- Deploy ---
|
|
70
|
+
|
|
71
|
+
program
|
|
72
|
+
.command("deploy [name] [path]")
|
|
73
|
+
.description("Deploy an app from a Dockerfile (name auto-generated if omitted)")
|
|
74
|
+
.option("-c, --cloud <cloud>", "Cloud provider (cf, gcp, aws)")
|
|
75
|
+
.option("--compute <name>", "Compute service name")
|
|
76
|
+
.option("-t, --tag <tag>", "Image tag (default: deploy-<timestamp>)")
|
|
77
|
+
.option("-e, --env <vars...>", "Set env vars (KEY=VALUE)")
|
|
78
|
+
.option(
|
|
79
|
+
"--regions <hints>",
|
|
80
|
+
"Comma-separated location hints (wnam,enam,sam,weur,eeur,apac,oc,afr,me)"
|
|
81
|
+
)
|
|
82
|
+
.option("-i, --instances <n>", "Instances per region", parseInt)
|
|
83
|
+
.option("--port <port>", "Container port", parseInt)
|
|
84
|
+
.option("--sleep <duration>", "Sleep after idle (e.g. 5m, 30s, never)", "30s")
|
|
85
|
+
.option("--instance-type <type>", "Instance type (lite, base, standard, large)")
|
|
86
|
+
.option("--vcpu <n>", "vCPU allocation (e.g. 0.0625, 0.5, 1, 2)", parseFloat)
|
|
87
|
+
.option("--memory <mb>", "Memory in MiB (e.g. 256, 512, 1024)", parseInt)
|
|
88
|
+
.option("--disk <mb>", "Disk in MB (e.g. 2000, 5000)", parseInt)
|
|
89
|
+
.option("--dns <cloud>", "Cloud provider for DNS records (cross-cloud)")
|
|
90
|
+
.option("--no-observability", "Disable Workers observability/logs")
|
|
91
|
+
.option("--json", "Output result as JSON")
|
|
92
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
93
|
+
.action(deploy);
|
|
94
|
+
|
|
95
|
+
// --- Apps (topic root = list) ---
|
|
96
|
+
|
|
97
|
+
var apps = program.command("apps").description("Manage apps");
|
|
98
|
+
|
|
99
|
+
apps
|
|
100
|
+
.command("list", { isDefault: true })
|
|
101
|
+
.description("List all deployed apps")
|
|
102
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
103
|
+
.option("--compute <name>", "Compute service name")
|
|
104
|
+
.option("--json", "Output as JSON")
|
|
105
|
+
.action(appsList);
|
|
106
|
+
|
|
107
|
+
apps
|
|
108
|
+
.command("info [name]")
|
|
109
|
+
.description("Show detailed app information")
|
|
110
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
111
|
+
.option("--compute <name>", "Compute service name")
|
|
112
|
+
.option("--json", "Output as JSON")
|
|
113
|
+
.action(appsInfo);
|
|
114
|
+
|
|
115
|
+
apps
|
|
116
|
+
.command("destroy [name]")
|
|
117
|
+
.description("Destroy an app and its resources")
|
|
118
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
119
|
+
.option("--compute <name>", "Compute service name")
|
|
120
|
+
.option("--confirm <name>", "Confirm by providing the app name")
|
|
121
|
+
.action(appsDestroy);
|
|
122
|
+
|
|
123
|
+
// --- Config (topic root = show) ---
|
|
124
|
+
|
|
125
|
+
var configCmd = program
|
|
126
|
+
.command("config")
|
|
127
|
+
.description("Manage app config/env vars")
|
|
128
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
129
|
+
.option("--compute <name>", "Compute service name");
|
|
130
|
+
|
|
131
|
+
function configOpts(cmd) {
|
|
132
|
+
var parentOpts = cmd?.parent?.opts() || {};
|
|
133
|
+
return { cloud: parentOpts.cloud, compute: parentOpts.compute };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
configCmd
|
|
137
|
+
.command("show [name]", { isDefault: true })
|
|
138
|
+
.description("Show all env vars for an app")
|
|
139
|
+
.option("--json", "Output as JSON")
|
|
140
|
+
.action((name, options, cmd) => configShow(name, { ...options, ...configOpts(cmd) }));
|
|
141
|
+
|
|
142
|
+
configCmd
|
|
143
|
+
.command("set <args...>")
|
|
144
|
+
.description("Set env vars ([name] KEY=VALUE ...) - applies live")
|
|
145
|
+
.option("-s, --secret", "Store values as encrypted secrets (write-only)")
|
|
146
|
+
.action((args, options, cmd) => configSet(args, { ...options, ...configOpts(cmd) }));
|
|
147
|
+
|
|
148
|
+
configCmd
|
|
149
|
+
.command("get <args...>")
|
|
150
|
+
.description("Get a single env var value ([name] KEY)")
|
|
151
|
+
.action((args, options, cmd) => configGet(args, { ...options, ...configOpts(cmd) }));
|
|
152
|
+
|
|
153
|
+
configCmd
|
|
154
|
+
.command("unset <args...>")
|
|
155
|
+
.description("Remove env vars ([name] KEY ...) - applies live")
|
|
156
|
+
.action((args, options, cmd) => configUnset(args, { ...options, ...configOpts(cmd) }));
|
|
157
|
+
|
|
158
|
+
configCmd
|
|
159
|
+
.command("import [name]")
|
|
160
|
+
.description("Import env vars from .env file or stdin")
|
|
161
|
+
.option("-f, --file <path>", "Path to .env file")
|
|
162
|
+
.option("-s, --secret", "Store values as encrypted secrets (write-only)")
|
|
163
|
+
.action((name, options, cmd) => configImport(name, { ...options, ...configOpts(cmd) }));
|
|
164
|
+
|
|
165
|
+
// --- Scale ---
|
|
166
|
+
|
|
167
|
+
program
|
|
168
|
+
.command("scale [name]")
|
|
169
|
+
.description("Show or adjust app scaling")
|
|
170
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
171
|
+
.option("--compute <name>", "Compute service name")
|
|
172
|
+
.option(
|
|
173
|
+
"-r, --regions <hints>",
|
|
174
|
+
"Comma-separated location hints (wnam,enam,sam,weur,eeur,apac,oc,afr,me)"
|
|
175
|
+
)
|
|
176
|
+
.option("-i, --instances <n>", "Instances per region", parseInt)
|
|
177
|
+
.option("--instance-type <type>", "Instance type (lite, base, standard, large)")
|
|
178
|
+
.option("--vcpu <n>", "vCPU allocation (e.g. 0.0625, 0.5, 1, 2)", parseFloat)
|
|
179
|
+
.option("--memory <mb>", "Memory in MiB (e.g. 256, 512, 1024)", parseInt)
|
|
180
|
+
.option("--disk <mb>", "Disk in MB (e.g. 2000, 5000)", parseInt)
|
|
181
|
+
.option("--json", "Output as JSON")
|
|
182
|
+
.action(scale);
|
|
183
|
+
|
|
184
|
+
// --- Domains (topic root = list) ---
|
|
185
|
+
|
|
186
|
+
var domainsCmd = program
|
|
187
|
+
.command("domains")
|
|
188
|
+
.description("Manage custom domains")
|
|
189
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
190
|
+
.option("--compute <name>", "Compute service name")
|
|
191
|
+
.option("--dns <cloud>", "Cloud provider for DNS records (cross-cloud)");
|
|
192
|
+
|
|
193
|
+
domainsCmd
|
|
194
|
+
.command("list [name]", { isDefault: true })
|
|
195
|
+
.description("List custom domains for an app")
|
|
196
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
197
|
+
.option("--compute <name>", "Compute service name")
|
|
198
|
+
.option("--json", "Output as JSON")
|
|
199
|
+
.action(domainsList);
|
|
200
|
+
|
|
201
|
+
function domainsOpts(cmd) {
|
|
202
|
+
var parentOpts = cmd?.parent?.opts() || {};
|
|
203
|
+
return { cloud: parentOpts.cloud, compute: parentOpts.compute, dns: parentOpts.dns };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
domainsCmd
|
|
207
|
+
.command("add [args...]")
|
|
208
|
+
.description("Add a custom domain (interactive if no domain given)")
|
|
209
|
+
.action((args, options, cmd) => domainsAdd(args, { ...options, ...domainsOpts(cmd) }));
|
|
210
|
+
|
|
211
|
+
domainsCmd
|
|
212
|
+
.command("remove <args...>")
|
|
213
|
+
.description("Remove a custom domain ([name] domain) - applies live")
|
|
214
|
+
.action((args, options, cmd) => domainsRemove(args, { ...options, ...domainsOpts(cmd) }));
|
|
215
|
+
|
|
216
|
+
// --- DB (topic root = list) ---
|
|
217
|
+
|
|
218
|
+
var dbCmd = program
|
|
219
|
+
.command("db")
|
|
220
|
+
.description("Manage databases");
|
|
221
|
+
|
|
222
|
+
dbCmd
|
|
223
|
+
.command("list", { isDefault: true })
|
|
224
|
+
.description("List all databases")
|
|
225
|
+
.option("--json", "Output as JSON")
|
|
226
|
+
.action(dbList);
|
|
227
|
+
|
|
228
|
+
dbCmd
|
|
229
|
+
.command("create <name>")
|
|
230
|
+
.description("Create a standalone database")
|
|
231
|
+
.option("--provider <id>", "Provider: cf, gcp, aws, or service name (e.g. neon)")
|
|
232
|
+
.option("--location <hint>", "Location hint (wnam, enam, weur, eeur, apac)")
|
|
233
|
+
.option("--jurisdiction <j>", "Jurisdiction (eu, fedramp)")
|
|
234
|
+
.option("--json", "Output as JSON")
|
|
235
|
+
.action(dbCreate);
|
|
236
|
+
|
|
237
|
+
dbCmd
|
|
238
|
+
.command("destroy <name>")
|
|
239
|
+
.description("Destroy a database")
|
|
240
|
+
.option("--confirm <name>", "Confirm by providing the database name")
|
|
241
|
+
.action(dbDestroy);
|
|
242
|
+
|
|
243
|
+
dbCmd
|
|
244
|
+
.command("info <name>")
|
|
245
|
+
.description("Show database details")
|
|
246
|
+
.option("--json", "Output as JSON")
|
|
247
|
+
.action(dbInfo);
|
|
248
|
+
|
|
249
|
+
dbCmd
|
|
250
|
+
.command("attach <name> [app]")
|
|
251
|
+
.description("Attach database to an app (injects env vars)")
|
|
252
|
+
.option("-c, --cloud <cloud>", "App cloud")
|
|
253
|
+
.option("--compute <name>", "App compute service")
|
|
254
|
+
.action(dbAttach);
|
|
255
|
+
|
|
256
|
+
dbCmd
|
|
257
|
+
.command("detach [app]")
|
|
258
|
+
.description("Detach database from an app (removes env vars)")
|
|
259
|
+
.option("-c, --cloud <cloud>", "App cloud")
|
|
260
|
+
.option("--compute <name>", "App compute service")
|
|
261
|
+
.action(dbDetach);
|
|
262
|
+
|
|
263
|
+
dbCmd
|
|
264
|
+
.command("shell <name>")
|
|
265
|
+
.description("Interactive SQL REPL")
|
|
266
|
+
.action(dbShell);
|
|
267
|
+
|
|
268
|
+
dbCmd
|
|
269
|
+
.command("query <args...>")
|
|
270
|
+
.description("Run a single SQL query ([name] <sql>)")
|
|
271
|
+
.option("--json", "Output as JSON")
|
|
272
|
+
.action(dbQuery);
|
|
273
|
+
|
|
274
|
+
dbCmd
|
|
275
|
+
.command("import <args...>")
|
|
276
|
+
.description("Import a .sql file (<name> <path>)")
|
|
277
|
+
.action(dbImport);
|
|
278
|
+
|
|
279
|
+
dbCmd
|
|
280
|
+
.command("export <name>")
|
|
281
|
+
.description("Export database as SQL dump")
|
|
282
|
+
.option("-o, --output <path>", "Write to file instead of stdout")
|
|
283
|
+
.action(dbExport);
|
|
284
|
+
|
|
285
|
+
dbCmd
|
|
286
|
+
.command("token <name>")
|
|
287
|
+
.description("Show or rotate auth token")
|
|
288
|
+
.option("--rotate", "Generate a new token")
|
|
289
|
+
.action(dbToken);
|
|
290
|
+
|
|
291
|
+
dbCmd
|
|
292
|
+
.command("reset <name>")
|
|
293
|
+
.description("Drop all tables (confirmation required)")
|
|
294
|
+
.option("--confirm <name>", "Confirm by providing the database name")
|
|
295
|
+
.action(dbReset);
|
|
296
|
+
|
|
297
|
+
// --- PS ---
|
|
298
|
+
|
|
299
|
+
program
|
|
300
|
+
.command("ps [name]")
|
|
301
|
+
.description("Show app containers and status")
|
|
302
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
303
|
+
.option("--compute <name>", "Compute service name")
|
|
304
|
+
.option("--json", "Output as JSON")
|
|
305
|
+
.action(ps);
|
|
306
|
+
|
|
307
|
+
// --- Logs ---
|
|
308
|
+
|
|
309
|
+
program
|
|
310
|
+
.command("logs [name]")
|
|
311
|
+
.description("Stream live logs from an app")
|
|
312
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
313
|
+
.option("--compute <name>", "Compute service name")
|
|
314
|
+
.action(logs);
|
|
315
|
+
|
|
316
|
+
// --- Open ---
|
|
317
|
+
|
|
318
|
+
program
|
|
319
|
+
.command("open [name]")
|
|
320
|
+
.description("Open app in browser")
|
|
321
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
322
|
+
.option("--compute <name>", "Compute service name")
|
|
323
|
+
.action(open);
|
|
324
|
+
|
|
325
|
+
// --- Regions ---
|
|
326
|
+
|
|
327
|
+
program
|
|
328
|
+
.command("regions")
|
|
329
|
+
.description("List available deployment regions")
|
|
330
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
331
|
+
.option("--compute <name>", "Compute service name")
|
|
332
|
+
.option("--json", "Output as JSON")
|
|
333
|
+
.action(async function (options) {
|
|
334
|
+
var { resolveTarget } = await import("./lib/providers/resolve.js");
|
|
335
|
+
var target = await resolveTarget(options);
|
|
336
|
+
var appProvider = await target.provider("app");
|
|
337
|
+
|
|
338
|
+
var regions = appProvider.getRegions();
|
|
339
|
+
if (options.json) {
|
|
340
|
+
console.log(JSON.stringify(regions, null, 2));
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
console.log("");
|
|
344
|
+
for (var r of regions) {
|
|
345
|
+
console.log(` ${fmt.bold(r.code.padEnd(6))} ${r.name.padEnd(25)} ${fmt.dim(r.location)}`);
|
|
346
|
+
}
|
|
347
|
+
console.log("");
|
|
348
|
+
console.log(fmt.dim(" These are location hints. The cloud provider will attempt to place"));
|
|
349
|
+
console.log(fmt.dim(" containers near the specified region but exact placement is not guaranteed."));
|
|
350
|
+
console.log("");
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// --- Cost ---
|
|
354
|
+
|
|
355
|
+
program
|
|
356
|
+
.command("cost [name]")
|
|
357
|
+
.description("Show estimated costs for an app or all apps")
|
|
358
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
359
|
+
.option("--compute <name>", "Compute service name")
|
|
360
|
+
.option("--since <period>", "Date range: Nd (e.g. 7d) or YYYY-MM-DD (default: month to date)")
|
|
361
|
+
.option("--json", "Output as JSON")
|
|
362
|
+
.action(cost);
|
|
363
|
+
|
|
25
364
|
// --- Doctor ---
|
|
26
365
|
|
|
27
366
|
program
|
|
@@ -29,4 +368,14 @@ program
|
|
|
29
368
|
.description("Check system setup and cloud connectivity")
|
|
30
369
|
.action(doctor);
|
|
31
370
|
|
|
371
|
+
// --- Top-level aliases ---
|
|
372
|
+
|
|
373
|
+
program
|
|
374
|
+
.command("destroy [name]")
|
|
375
|
+
.description("Destroy an app (alias for apps destroy)")
|
|
376
|
+
.option("-c, --cloud <cloud>", "Cloud provider")
|
|
377
|
+
.option("--compute <name>", "Compute service name")
|
|
378
|
+
.option("--confirm <name>", "Confirm by providing the app name")
|
|
379
|
+
.action(appsDestroy);
|
|
380
|
+
|
|
32
381
|
program.parse();
|