@zerct/zerct 0.1.5 → 0.1.6
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 +3 -0
- package/bin/zerct.js +145 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,9 @@ on `0.0.0.0:$PORT` and expose the configured health endpoint.
|
|
|
20
20
|
From a full-stack repo root, the same deploy command discovers nested
|
|
21
21
|
`zerct.toml` files and deploys the whole workspace in one command.
|
|
22
22
|
|
|
23
|
+
Agents can also inspect apps, deploys, builds, app/deploy/build logs, env
|
|
24
|
+
metadata, custom domains, and billing portal links through the same CLI.
|
|
25
|
+
|
|
23
26
|
On first deploy, the CLI opens browser login, waits for GitHub or Google, stores
|
|
24
27
|
the Zerct session in the OS credential store when available, and continues the
|
|
25
28
|
deploy. Later commands reuse that session.
|
package/bin/zerct.js
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSy
|
|
|
4
4
|
import { homedir } from 'node:os'
|
|
5
5
|
import path from 'node:path'
|
|
6
6
|
|
|
7
|
-
const VERSION = '0.1.
|
|
7
|
+
const VERSION = '0.1.6'
|
|
8
8
|
const DEFAULT_API_URL = 'https://api.zerct.com'
|
|
9
9
|
const ARCHIVE_LIMIT_BYTES = 48 * 1024 * 1024
|
|
10
10
|
const SESSION_DIR = '.zerct'
|
|
@@ -65,12 +65,20 @@ Usage:
|
|
|
65
65
|
zerct doctor [path] [--json]
|
|
66
66
|
zerct login [--token <token>] [--api <url>]
|
|
67
67
|
zerct deploy [path] [--database] [--api <url>] [--json]
|
|
68
|
-
zerct
|
|
68
|
+
zerct apps [--api <url>] [--json]
|
|
69
|
+
zerct deploys --app <app_id> [--limit <n>] [--cursor <cursor>] [--api <url>] [--json]
|
|
70
|
+
zerct builds --app <app_id> [--limit <n>] [--cursor <cursor>] [--api <url>] [--json]
|
|
71
|
+
zerct logs --app <app_id> [--deploy <deploy_id>] [--build <build_id>] [--limit <n>] [--cursor <cursor>] [--api <url>] [--json]
|
|
69
72
|
zerct status --app <app_id> [--api <url>] [--json]
|
|
70
73
|
zerct inspect --app <app_id> [--api <url>] [--json]
|
|
71
74
|
zerct db --app <app_id> [--api <url>] [--json]
|
|
75
|
+
zerct env list --app <app_id> [--api <url>] [--json]
|
|
72
76
|
zerct env set --app <app_id> KEY=value [--api <url>] [--json]
|
|
73
|
-
zerct
|
|
77
|
+
zerct env delete --app <app_id> KEY [--api <url>] [--json]
|
|
78
|
+
zerct domains list --app <app_id> [--api <url>] [--json]
|
|
79
|
+
zerct domains add --app <app_id> <domain> [--api <url>] [--json]
|
|
80
|
+
zerct domains delete --app <app_id> <domain> [--api <url>] [--json]
|
|
81
|
+
zerct billing [portal] [--api <url>] [--json]
|
|
74
82
|
|
|
75
83
|
Agent contract:
|
|
76
84
|
- Rust backends keep Cargo.lock committed, listen on 0.0.0.0:$PORT, and return HTTP 200 from health.
|
|
@@ -108,6 +116,15 @@ async function main() {
|
|
|
108
116
|
case 'deploy':
|
|
109
117
|
await deploy(projectPath(cli.args[0]), cli)
|
|
110
118
|
break
|
|
119
|
+
case 'apps':
|
|
120
|
+
await apps(cli)
|
|
121
|
+
break
|
|
122
|
+
case 'deploys':
|
|
123
|
+
await deploys(cli)
|
|
124
|
+
break
|
|
125
|
+
case 'builds':
|
|
126
|
+
await builds(cli)
|
|
127
|
+
break
|
|
111
128
|
case 'logs':
|
|
112
129
|
await logs(cli)
|
|
113
130
|
break
|
|
@@ -124,6 +141,9 @@ async function main() {
|
|
|
124
141
|
case 'env':
|
|
125
142
|
await envCommand(cli)
|
|
126
143
|
break
|
|
144
|
+
case 'domains':
|
|
145
|
+
await domainsCommand(cli)
|
|
146
|
+
break
|
|
127
147
|
case 'billing':
|
|
128
148
|
await billing(cli)
|
|
129
149
|
break
|
|
@@ -138,6 +158,10 @@ function parseArgs(argv) {
|
|
|
138
158
|
args: [],
|
|
139
159
|
apiUrl: DEFAULT_API_URL,
|
|
140
160
|
app: '',
|
|
161
|
+
build: '',
|
|
162
|
+
deploy: '',
|
|
163
|
+
limit: '',
|
|
164
|
+
cursor: '',
|
|
141
165
|
token: '',
|
|
142
166
|
json: false,
|
|
143
167
|
database: false,
|
|
@@ -164,6 +188,18 @@ function parseArgs(argv) {
|
|
|
164
188
|
} else if (arg === '--app') {
|
|
165
189
|
cli.app = requireValue(argv, index, '--app')
|
|
166
190
|
index += 1
|
|
191
|
+
} else if (arg === '--build') {
|
|
192
|
+
cli.build = requireValue(argv, index, '--build')
|
|
193
|
+
index += 1
|
|
194
|
+
} else if (arg === '--deploy') {
|
|
195
|
+
cli.deploy = requireValue(argv, index, '--deploy')
|
|
196
|
+
index += 1
|
|
197
|
+
} else if (arg === '--limit') {
|
|
198
|
+
cli.limit = requireValue(argv, index, '--limit')
|
|
199
|
+
index += 1
|
|
200
|
+
} else if (arg === '--cursor') {
|
|
201
|
+
cli.cursor = requireValue(argv, index, '--cursor')
|
|
202
|
+
index += 1
|
|
167
203
|
} else if (arg === '--token') {
|
|
168
204
|
cli.token = requireValue(argv, index, '--token')
|
|
169
205
|
index += 1
|
|
@@ -471,7 +507,17 @@ function printWorkspaceDeployResults(projectDir, results, cli) {
|
|
|
471
507
|
}
|
|
472
508
|
|
|
473
509
|
async function logs(cli) {
|
|
474
|
-
const
|
|
510
|
+
const token = await readOrLoginToken(process.cwd(), cli)
|
|
511
|
+
const page = pageQuery(cli)
|
|
512
|
+
let route = ''
|
|
513
|
+
if (cli.build) {
|
|
514
|
+
route = `/v1/builds/${encodeURIComponent(cli.build)}/logs${page}`
|
|
515
|
+
} else if (cli.deploy) {
|
|
516
|
+
route = `/v1/deploys/${encodeURIComponent(cli.deploy)}/logs${page}`
|
|
517
|
+
} else {
|
|
518
|
+
route = `/v1/apps/${encodeURIComponent(requireApp(cli))}/logs${page}`
|
|
519
|
+
}
|
|
520
|
+
const response = await apiRequest(cli, 'GET', route, token, null)
|
|
475
521
|
if (cli.json) {
|
|
476
522
|
console.log(JSON.stringify(response, null, 2))
|
|
477
523
|
return
|
|
@@ -479,6 +525,30 @@ async function logs(cli) {
|
|
|
479
525
|
for (const line of response.lines || []) {
|
|
480
526
|
console.log(`[${line.timestamp}] ${line.stream}: ${line.message}`)
|
|
481
527
|
}
|
|
528
|
+
if (response.has_more && response.next_cursor) {
|
|
529
|
+
const target = cli.build
|
|
530
|
+
? `--build ${cli.build}`
|
|
531
|
+
: cli.deploy
|
|
532
|
+
? `--deploy ${cli.deploy}`
|
|
533
|
+
: `--app ${requireApp(cli)}`
|
|
534
|
+
console.log(`next npx @zerct/zerct logs ${target} --cursor ${response.next_cursor}`)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async function apps(cli) {
|
|
539
|
+
const token = await readOrLoginToken(process.cwd(), cli)
|
|
540
|
+
const response = await apiRequest(cli, 'GET', '/v1/apps', token, null)
|
|
541
|
+
printJsonOrPretty(cli, response)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
async function deploys(cli) {
|
|
545
|
+
const response = await appGet(cli, `deploys${pageQuery(cli)}`)
|
|
546
|
+
printJsonOrPretty(cli, response)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async function builds(cli) {
|
|
550
|
+
const response = await appGet(cli, `builds${pageQuery(cli)}`)
|
|
551
|
+
printJsonOrPretty(cli, response)
|
|
482
552
|
}
|
|
483
553
|
|
|
484
554
|
async function status(cli) {
|
|
@@ -497,8 +567,26 @@ async function database(cli) {
|
|
|
497
567
|
}
|
|
498
568
|
|
|
499
569
|
async function envCommand(cli) {
|
|
570
|
+
if (cli.args[0] === 'list') {
|
|
571
|
+
const response = await appGet(cli, 'env')
|
|
572
|
+
printJsonOrPretty(cli, response)
|
|
573
|
+
return
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (cli.args[0] === 'delete') {
|
|
577
|
+
const name = cli.args[1] || ''
|
|
578
|
+
if (!name) {
|
|
579
|
+
throw agentError('invalid_env', 'Environment variable name is required.', 'Use `npx @zerct/zerct env delete --app <app_id> KEY`.', cli.json)
|
|
580
|
+
}
|
|
581
|
+
const token = await readOrLoginToken(process.cwd(), cli)
|
|
582
|
+
const app = requireApp(cli)
|
|
583
|
+
const response = await apiRequest(cli, 'DELETE', `/v1/apps/${encodeURIComponent(app)}/env/${encodeURIComponent(name)}`, token, null)
|
|
584
|
+
printJsonOrPretty(cli, response)
|
|
585
|
+
return
|
|
586
|
+
}
|
|
587
|
+
|
|
500
588
|
if (cli.args[0] !== 'set') {
|
|
501
|
-
throw agentError('unknown_command', 'Unknown env command.', 'Use `npx @zerct/zerct env set
|
|
589
|
+
throw agentError('unknown_command', 'Unknown env command.', 'Use `npx @zerct/zerct env list`, `env set`, or `env delete`.', cli.json)
|
|
502
590
|
}
|
|
503
591
|
|
|
504
592
|
const assignment = cli.args[1] || ''
|
|
@@ -515,8 +603,48 @@ async function envCommand(cli) {
|
|
|
515
603
|
printJsonOrPretty(cli, response)
|
|
516
604
|
}
|
|
517
605
|
|
|
606
|
+
async function domainsCommand(cli) {
|
|
607
|
+
const action = cli.args[0] || 'list'
|
|
608
|
+
if (action === 'list') {
|
|
609
|
+
const response = await appGet(cli, 'domains')
|
|
610
|
+
printJsonOrPretty(cli, response)
|
|
611
|
+
return
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const domain = cli.args[1] || ''
|
|
615
|
+
if (!domain) {
|
|
616
|
+
throw agentError('missing_domain', 'Domain is required.', 'Use `npx @zerct/zerct domains add --app <app_id> api.example.com`.', cli.json)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const token = await readOrLoginToken(process.cwd(), cli)
|
|
620
|
+
const app = requireApp(cli)
|
|
621
|
+
if (action === 'add') {
|
|
622
|
+
const response = await apiRequest(cli, 'POST', `/v1/apps/${encodeURIComponent(app)}/domains`, token, { domain })
|
|
623
|
+
printJsonOrPretty(cli, response)
|
|
624
|
+
return
|
|
625
|
+
}
|
|
626
|
+
if (action === 'delete') {
|
|
627
|
+
const response = await apiRequest(cli, 'DELETE', `/v1/apps/${encodeURIComponent(app)}/domains/${encodeURIComponent(domain)}`, token, null)
|
|
628
|
+
printJsonOrPretty(cli, response)
|
|
629
|
+
return
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
throw agentError('unknown_command', 'Unknown domains command.', 'Use `domains list`, `domains add`, or `domains delete`.', cli.json)
|
|
633
|
+
}
|
|
634
|
+
|
|
518
635
|
async function billing(cli) {
|
|
519
636
|
const token = await readOrLoginToken(process.cwd(), cli)
|
|
637
|
+
if (cli.args[0] === 'portal') {
|
|
638
|
+
const response = await apiRequest(cli, 'POST', '/v1/billing/portal', token, null)
|
|
639
|
+
if (cli.json) {
|
|
640
|
+
console.log(JSON.stringify(response, null, 2))
|
|
641
|
+
return
|
|
642
|
+
}
|
|
643
|
+
console.log(response.checkout.url)
|
|
644
|
+
openUrl(response.checkout.url)
|
|
645
|
+
return
|
|
646
|
+
}
|
|
647
|
+
|
|
520
648
|
const response = await apiRequest(cli, 'POST', '/v1/billing/checkout', token, {
|
|
521
649
|
target_plan: 'pro',
|
|
522
650
|
reason: 'Upgrade to Zerct Pro.'
|
|
@@ -599,6 +727,18 @@ function requireApp(cli) {
|
|
|
599
727
|
return cli.app
|
|
600
728
|
}
|
|
601
729
|
|
|
730
|
+
function pageQuery(cli) {
|
|
731
|
+
const params = new URLSearchParams()
|
|
732
|
+
if (cli.limit) {
|
|
733
|
+
params.set('limit', cli.limit)
|
|
734
|
+
}
|
|
735
|
+
if (cli.cursor) {
|
|
736
|
+
params.set('cursor', cli.cursor)
|
|
737
|
+
}
|
|
738
|
+
const value = params.toString()
|
|
739
|
+
return value ? `?${value}` : ''
|
|
740
|
+
}
|
|
741
|
+
|
|
602
742
|
async function apiRequest(cli, method, route, token, body) {
|
|
603
743
|
const headers = {
|
|
604
744
|
accept: 'application/json'
|