@zerct/zerct 0.1.4 → 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.
Files changed (3) hide show
  1. package/README.md +3 -0
  2. package/bin/zerct.js +145 -5
  3. 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.4'
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 logs --app <app_id> [--api <url>] [--json]
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 billing [--api <url>] [--json]
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 response = await appGet(cli, 'logs')
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 --app <app_id> KEY=value`.', cli.json)
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'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerct/zerct",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Deploy Rust backends and static frontends to Zerct.",
5
5
  "type": "module",
6
6
  "bin": {