@absolutejs/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,660 @@
1
+ // @bun
2
+ // src/loadConfig.ts
3
+ import { existsSync, statSync } from "fs";
4
+ import { dirname, join, resolve } from "path";
5
+ var CONFIG_NAMES = [
6
+ "absolutejs.config.ts",
7
+ "absolutejs.config.mts",
8
+ "absolutejs.config.js",
9
+ "absolutejs.config.mjs"
10
+ ];
11
+ var findConfigPath = (startDir) => {
12
+ let dir = resolve(startDir);
13
+ for (;; ) {
14
+ for (const name of CONFIG_NAMES) {
15
+ const candidate = join(dir, name);
16
+ try {
17
+ if (existsSync(candidate) && statSync(candidate).isFile()) {
18
+ return candidate;
19
+ }
20
+ } catch {}
21
+ }
22
+ const parent = dirname(dir);
23
+ if (parent === dir)
24
+ return;
25
+ dir = parent;
26
+ }
27
+ };
28
+ var loadConfig = async (startDir = process.cwd()) => {
29
+ const path = findConfigPath(startDir);
30
+ if (path === undefined) {
31
+ throw new Error(`[absolutejs] no config file found.
32
+ ` + `Looked for: ${CONFIG_NAMES.join(", ")} (walked up from ${startDir})
33
+
34
+ ` + `Create an absolutejs.config.ts with:
35
+
36
+ ` + ` import { defineConfig } from '@absolutejs/cli';
37
+ ` + ` export default defineConfig({
38
+ ` + ` secrets: /* your SecretBroker */,
39
+ ` + ` deployments: [],
40
+ ` + ` });`);
41
+ }
42
+ let mod;
43
+ try {
44
+ mod = await import(path);
45
+ } catch (error) {
46
+ throw new Error(`[absolutejs] failed to load ${path}: ${error instanceof Error ? error.message : String(error)}`);
47
+ }
48
+ const config = mod.default;
49
+ if (config === undefined || typeof config !== "object") {
50
+ throw new Error(`[absolutejs] ${path} must \`export default defineConfig({...})\``);
51
+ }
52
+ return { config, path };
53
+ };
54
+
55
+ // src/utils/output.ts
56
+ var renderTable = (headers, rows) => {
57
+ if (rows.length === 0) {
58
+ return `${headers.join(" ")}
59
+ (no rows)
60
+ `;
61
+ }
62
+ const widths = headers.map((header, columnIndex) => {
63
+ const cells = rows.map((row) => row[columnIndex] ?? "");
64
+ return Math.max(header.length, ...cells.map((cell) => cell.length));
65
+ });
66
+ const formatRow = (row) => row.map((cell, columnIndex) => (cell ?? "").padEnd(widths[columnIndex] ?? 0)).join(" ").trimEnd();
67
+ const lines = [formatRow(headers), formatRow(widths.map((w) => "-".repeat(w)))];
68
+ for (const row of rows)
69
+ lines.push(formatRow(row));
70
+ return `${lines.join(`
71
+ `)}
72
+ `;
73
+ };
74
+ var writeOut = (text) => {
75
+ process.stdout.write(text.endsWith(`
76
+ `) ? text : `${text}
77
+ `);
78
+ };
79
+ var writeJson = (value) => {
80
+ process.stdout.write(`${JSON.stringify(value, null, 2)}
81
+ `);
82
+ };
83
+ var writeErr = (text) => {
84
+ process.stderr.write(text.endsWith(`
85
+ `) ? text : `${text}
86
+ `);
87
+ };
88
+ var formatRelativeTime = (msAgo) => {
89
+ if (msAgo < 60000)
90
+ return `${Math.round(msAgo / 1000)}s ago`;
91
+ if (msAgo < 3600000)
92
+ return `${Math.round(msAgo / 60000)}m ago`;
93
+ if (msAgo < 86400000)
94
+ return `${Math.round(msAgo / 3600000)}h ago`;
95
+ return `${Math.round(msAgo / 86400000)}d ago`;
96
+ };
97
+
98
+ // src/commands/secrets.ts
99
+ var requireBroker = (config) => {
100
+ if (config.secrets === undefined) {
101
+ throw new Error("config.secrets is not set \u2014 `secrets` verbs need a SecretBroker");
102
+ }
103
+ return config.secrets;
104
+ };
105
+ var requireAdapter = (config) => {
106
+ if (config.secretAdapter === undefined) {
107
+ throw new Error("config.secretAdapter is not set \u2014 pass the SecretAdapter you used to build the broker");
108
+ }
109
+ return config.secretAdapter;
110
+ };
111
+ var runSecrets = async (config, args, mode) => {
112
+ const { verb, positional, flags } = args;
113
+ switch (verb) {
114
+ case "list": {
115
+ const adapter = requireAdapter(config);
116
+ const broker = requireBroker(config);
117
+ if (adapter.list === undefined) {
118
+ throw new Error("the configured SecretAdapter does not implement `list()`");
119
+ }
120
+ const names = await adapter.list();
121
+ const rows = [];
122
+ for (const name of names.sort()) {
123
+ const value = await adapter.fetch(name);
124
+ const fingerprint = value === null ? "(empty)" : broker.fingerprint(value);
125
+ rows.push([name, fingerprint]);
126
+ }
127
+ if (mode === "json") {
128
+ writeJson(rows.map(([name, fingerprint]) => ({ fingerprint, name })));
129
+ } else {
130
+ writeOut(renderTable(["name", "fingerprint"], rows));
131
+ }
132
+ return 0;
133
+ }
134
+ case "get": {
135
+ const broker = requireBroker(config);
136
+ const name = positional[0];
137
+ if (name === undefined) {
138
+ throw new Error("usage: secrets get <name> [--show]");
139
+ }
140
+ const resolved = await broker.resolve(name);
141
+ if (resolved === null) {
142
+ writeErr(`(no value for ${name})`);
143
+ return 2;
144
+ }
145
+ if (mode === "json") {
146
+ writeJson({
147
+ fingerprint: resolved.fingerprint,
148
+ name,
149
+ value: flags.show === true ? resolved.value : "(redacted; pass --show)"
150
+ });
151
+ } else if (flags.show === true) {
152
+ writeOut(resolved.value);
153
+ } else {
154
+ writeOut(`${name}: fingerprint=${resolved.fingerprint} (pass --show to print plaintext)`);
155
+ }
156
+ return 0;
157
+ }
158
+ case "set": {
159
+ const adapter = requireAdapter(config);
160
+ if (adapter.put === undefined) {
161
+ throw new Error("the configured SecretAdapter does not implement `put()`");
162
+ }
163
+ const pair = positional[0];
164
+ if (pair === undefined) {
165
+ throw new Error("usage: secrets set <NAME>=<value>");
166
+ }
167
+ const eq = pair.indexOf("=");
168
+ if (eq <= 0) {
169
+ throw new Error("usage: secrets set <NAME>=<value> (missing `=`)");
170
+ }
171
+ const name = pair.slice(0, eq);
172
+ const value = pair.slice(eq + 1);
173
+ await adapter.put(name, value);
174
+ if (mode === "json") {
175
+ writeJson({ name, set: true });
176
+ } else {
177
+ writeOut(`set ${name}`);
178
+ }
179
+ return 0;
180
+ }
181
+ case "rotate": {
182
+ const broker = requireBroker(config);
183
+ const name = positional[0];
184
+ if (name === undefined) {
185
+ throw new Error("usage: secrets rotate <name>");
186
+ }
187
+ const rotated = await broker.rotate(name);
188
+ if (mode === "json") {
189
+ writeJson({
190
+ fingerprint: rotated.fingerprint,
191
+ name,
192
+ rotated: true
193
+ });
194
+ } else {
195
+ writeOut(`rotated ${name} (new fingerprint: ${rotated.fingerprint})`);
196
+ }
197
+ return 0;
198
+ }
199
+ default:
200
+ throw new Error(`unknown secrets verb: "${verb}". try: list | get | set | rotate`);
201
+ }
202
+ };
203
+
204
+ // src/commands/env.ts
205
+ var KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
206
+ var NEEDS_QUOTING = /[\s"'`$\\#&|;<>(){}*?!]/;
207
+ var validateKey = (key) => {
208
+ if (!KEY_PATTERN.test(key)) {
209
+ throw new Error(`invalid env key "${key}" \u2014 must match /^[A-Z_][A-Z0-9_]*$/`);
210
+ }
211
+ };
212
+ var serializeLine = (key, value) => {
213
+ validateKey(key);
214
+ if (value.includes(`
215
+ `) || value.includes("\r")) {
216
+ throw new Error(`value for "${key}" contains a newline \u2014 env files cannot represent multi-line values`);
217
+ }
218
+ if (NEEDS_QUOTING.test(value) || value.startsWith("=") || value === "") {
219
+ const escaped = value.replaceAll("\\", "\\\\").replaceAll('"', "\\\"");
220
+ return `${key}="${escaped}"`;
221
+ }
222
+ return `${key}=${value}`;
223
+ };
224
+ var serializeEnvFile = (values) => {
225
+ const lines = [];
226
+ for (const key of Object.keys(values).sort()) {
227
+ const value = values[key];
228
+ if (value === undefined)
229
+ continue;
230
+ lines.push(serializeLine(key, value));
231
+ }
232
+ return `${lines.join(`
233
+ `)}
234
+ `;
235
+ };
236
+ var unquoteValue = (raw) => {
237
+ if (raw.length >= 2 && raw.startsWith('"') && raw.endsWith('"')) {
238
+ return raw.slice(1, -1).replaceAll("\\\"", '"').replaceAll("\\\\", "\\");
239
+ }
240
+ if (raw.length >= 2 && raw.startsWith("'") && raw.endsWith("'")) {
241
+ return raw.slice(1, -1);
242
+ }
243
+ return raw;
244
+ };
245
+ var parseEnvFile = (text) => {
246
+ const result = {};
247
+ for (const rawLine of text.split(`
248
+ `)) {
249
+ const line = rawLine.trim();
250
+ if (line.length === 0 || line.startsWith("#"))
251
+ continue;
252
+ const eq = line.indexOf("=");
253
+ if (eq <= 0)
254
+ continue;
255
+ const key = line.slice(0, eq).trim();
256
+ const value = unquoteValue(line.slice(eq + 1).trim());
257
+ if (KEY_PATTERN.test(key))
258
+ result[key] = value;
259
+ }
260
+ return result;
261
+ };
262
+ var findDeployment = (config, stage) => {
263
+ const match = (config.deployments ?? []).find((d) => d.name === stage);
264
+ if (match === undefined) {
265
+ const names = (config.deployments ?? []).map((d) => d.name);
266
+ throw new Error(`unknown deployment "${stage}". configured: ${names.length > 0 ? names.join(", ") : "(none)"}`);
267
+ }
268
+ return match;
269
+ };
270
+ var resolveValuesForDeployment = async (config, deployment) => {
271
+ const merged = {};
272
+ for (const [key, value] of Object.entries(deployment.extras ?? {})) {
273
+ validateKey(key);
274
+ merged[key] = value;
275
+ }
276
+ if ((deployment.secretNames ?? []).length > 0) {
277
+ if (config.secrets === undefined) {
278
+ throw new Error(`deployment "${deployment.name}" declares secretNames but config.secrets is not set`);
279
+ }
280
+ for (const name of deployment.secretNames ?? []) {
281
+ const resolved = await config.secrets.resolve(name);
282
+ if (resolved === null) {
283
+ throw new Error(`secret "${name}" not found in broker (deployment: ${deployment.name})`);
284
+ }
285
+ if (merged[name] !== undefined) {
286
+ throw new Error(`"${name}" defined in BOTH extras and secretNames for ${deployment.name}`);
287
+ }
288
+ merged[name] = resolved.value;
289
+ }
290
+ }
291
+ return merged;
292
+ };
293
+ var shellQuote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
294
+ var readRemoteFile = async (deployment) => {
295
+ const target = await deployment.target();
296
+ const sentinel = "__ABS_DEPLOY_ENV_ABSENT__";
297
+ const result = await target.exec(`if [ -f ${shellQuote(deployment.remotePath)} ]; then cat ${shellQuote(deployment.remotePath)}; else echo ${sentinel}; fi`);
298
+ if (result.exitCode !== 0) {
299
+ throw new Error(`failed to read ${deployment.remotePath}: exit ${result.exitCode}: ${result.stderr || result.stdout}`);
300
+ }
301
+ if (result.stdout.trim() === sentinel)
302
+ return;
303
+ return result.stdout;
304
+ };
305
+ var writeRemoteFile = async (deployment, contents) => {
306
+ const target = await deployment.target();
307
+ const tempPath = `${deployment.remotePath}.new.${Math.floor(Date.now() / 1000)}`;
308
+ const dir = deployment.remotePath.split("/").slice(0, -1).join("/") || "/";
309
+ const mkdir = await target.exec(`mkdir -p ${shellQuote(dir)}`);
310
+ if (mkdir.exitCode !== 0) {
311
+ throw new Error(`mkdir ${dir} failed: ${mkdir.stderr || mkdir.stdout}`);
312
+ }
313
+ const write = await target.exec(`cat > ${shellQuote(tempPath)}`, {
314
+ stdin: contents
315
+ });
316
+ if (write.exitCode !== 0) {
317
+ throw new Error(`write to ${tempPath} failed: ${write.stderr || write.stdout}`);
318
+ }
319
+ const mode = deployment.mode ?? "600";
320
+ const chmod = await target.exec(`chmod ${shellQuote(mode)} ${shellQuote(tempPath)}`);
321
+ if (chmod.exitCode !== 0) {
322
+ throw new Error(`chmod failed: ${chmod.stderr || chmod.stdout}`);
323
+ }
324
+ if (deployment.owner !== undefined) {
325
+ const chown = await target.exec(`chown ${shellQuote(deployment.owner)} ${shellQuote(tempPath)}`);
326
+ if (chown.exitCode !== 0) {
327
+ throw new Error(`chown failed: ${chown.stderr || chown.stdout}`);
328
+ }
329
+ }
330
+ const mv = await target.exec(`mv ${shellQuote(tempPath)} ${shellQuote(deployment.remotePath)}`);
331
+ if (mv.exitCode !== 0) {
332
+ throw new Error(`mv failed: ${mv.stderr || mv.stdout}`);
333
+ }
334
+ if (deployment.reload !== undefined) {
335
+ const reload = await target.exec(deployment.reload);
336
+ if (reload.exitCode !== 0) {
337
+ throw new Error(`reload command failed: ${reload.stderr || reload.stdout}`);
338
+ }
339
+ }
340
+ };
341
+ var runEnv = async (config, args, mode) => {
342
+ const { verb, positional, flags } = args;
343
+ const stage = positional[0];
344
+ if (stage === undefined) {
345
+ throw new Error(`usage: env ${verb} <stage>`);
346
+ }
347
+ const deployment = findDeployment(config, stage);
348
+ switch (verb) {
349
+ case "pull": {
350
+ const remoteText = await readRemoteFile(deployment);
351
+ if (remoteText === undefined) {
352
+ if (mode === "json")
353
+ writeJson({ exists: false });
354
+ else
355
+ writeErr(`(no env file at ${deployment.remotePath})`);
356
+ return 2;
357
+ }
358
+ if (mode === "json") {
359
+ writeJson({
360
+ exists: true,
361
+ path: deployment.remotePath,
362
+ values: parseEnvFile(remoteText)
363
+ });
364
+ } else {
365
+ writeOut(remoteText);
366
+ }
367
+ return 0;
368
+ }
369
+ case "push": {
370
+ const resolved = await resolveValuesForDeployment(config, deployment);
371
+ const next = serializeEnvFile(resolved);
372
+ await writeRemoteFile(deployment, next);
373
+ const message = `pushed ${Object.keys(resolved).length} keys to ${stage}:${deployment.remotePath}`;
374
+ if (mode === "json") {
375
+ writeJson({
376
+ keys: Object.keys(resolved).sort(),
377
+ path: deployment.remotePath,
378
+ pushed: true,
379
+ stage
380
+ });
381
+ } else {
382
+ writeOut(message);
383
+ }
384
+ return 0;
385
+ }
386
+ case "diff": {
387
+ const remoteText = await readRemoteFile(deployment);
388
+ const remote = remoteText === undefined ? {} : parseEnvFile(remoteText);
389
+ const next = await resolveValuesForDeployment(config, deployment);
390
+ const allKeys = [
391
+ ...new Set([...Object.keys(remote), ...Object.keys(next)])
392
+ ].sort();
393
+ const rows = [];
394
+ for (const key of allKeys) {
395
+ const before = remote[key];
396
+ const after = next[key];
397
+ if (before === undefined) {
398
+ rows.push({
399
+ detail: "(new)",
400
+ key,
401
+ status: "added"
402
+ });
403
+ } else if (after === undefined) {
404
+ rows.push({
405
+ detail: "(removed)",
406
+ key,
407
+ status: "removed"
408
+ });
409
+ } else if (before === after) {
410
+ if (flags.all === true) {
411
+ rows.push({ detail: "(unchanged)", key, status: "same" });
412
+ }
413
+ } else {
414
+ rows.push({
415
+ detail: `before \u2260 after`,
416
+ key,
417
+ status: "changed"
418
+ });
419
+ }
420
+ }
421
+ if (mode === "json") {
422
+ writeJson({ diff: rows, stage });
423
+ } else {
424
+ if (rows.length === 0) {
425
+ writeOut(`no differences (${allKeys.length} keys match)`);
426
+ } else {
427
+ writeOut(renderTable(["key", "status", "detail"], rows.map((r) => [r.key, r.status, r.detail])));
428
+ }
429
+ }
430
+ return 0;
431
+ }
432
+ default:
433
+ throw new Error(`unknown env verb: "${verb}". try: pull | push | diff`);
434
+ }
435
+ };
436
+
437
+ // src/commands/deploy.ts
438
+ var findDeployment2 = (config, stage) => {
439
+ const match = (config.deployments ?? []).find((d) => d.name === stage);
440
+ if (match === undefined) {
441
+ const names = (config.deployments ?? []).map((d) => d.name);
442
+ throw new Error(`unknown deployment "${stage}". configured: ${names.length > 0 ? names.join(", ") : "(none)"}`);
443
+ }
444
+ return match;
445
+ };
446
+ var requireDeployer = async (deployment) => {
447
+ if (deployment.deployer === undefined) {
448
+ throw new Error(`deployment "${deployment.name}" has no deployer() factory in config`);
449
+ }
450
+ return deployment.deployer();
451
+ };
452
+ var runDeploy = async (config, args, mode) => {
453
+ const { verb, positional, flags } = args;
454
+ const stage = positional[0];
455
+ if (stage === undefined) {
456
+ throw new Error(`usage: deploy ${verb} <stage>`);
457
+ }
458
+ const deployment = findDeployment2(config, stage);
459
+ const deployer = await requireDeployer(deployment);
460
+ switch (verb) {
461
+ case "releases": {
462
+ if (deployer.listReleases === undefined) {
463
+ throw new Error("the deployer for this stage does not implement listReleases()");
464
+ }
465
+ const releases = await deployer.listReleases();
466
+ if (mode === "json") {
467
+ writeJson({ releases, stage });
468
+ } else {
469
+ const rows = releases.map((release) => [
470
+ release.active === true ? "*" : " ",
471
+ release.id,
472
+ new Date(release.at).toISOString(),
473
+ formatRelativeTime(Date.now() - release.at)
474
+ ]);
475
+ writeOut(renderTable(["", "id", "at", "age"], rows));
476
+ }
477
+ return 0;
478
+ }
479
+ case "status": {
480
+ const releases = deployer.listReleases ? await deployer.listReleases() : [];
481
+ const currentId = deployer.currentReleaseId ? await deployer.currentReleaseId() : undefined;
482
+ if (mode === "json") {
483
+ writeJson({
484
+ currentReleaseId: currentId ?? null,
485
+ recentReleases: releases.slice(0, 5),
486
+ stage
487
+ });
488
+ } else {
489
+ writeOut(`stage: ${stage}`);
490
+ writeOut(`current release: ${currentId ?? "(unknown)"}`);
491
+ if (releases.length > 0) {
492
+ writeOut(`
493
+ recent releases:`);
494
+ const rows = releases.slice(0, 5).map((release) => [
495
+ release.active === true ? "*" : " ",
496
+ release.id,
497
+ formatRelativeTime(Date.now() - release.at)
498
+ ]);
499
+ writeOut(renderTable(["", "id", "age"], rows));
500
+ }
501
+ }
502
+ return 0;
503
+ }
504
+ case "rollback": {
505
+ if (deployer.rollback === undefined) {
506
+ throw new Error("the deployer for this stage does not implement rollback()");
507
+ }
508
+ let target = typeof flags.to === "string" ? flags.to : undefined;
509
+ if (target === undefined) {
510
+ if (deployer.listReleases === undefined) {
511
+ throw new Error("rollback without --to requires deployer.listReleases() to find the previous release");
512
+ }
513
+ const releases = await deployer.listReleases();
514
+ if (releases.length < 2) {
515
+ throw new Error(`stage ${stage} has fewer than 2 releases \u2014 nothing to roll back to`);
516
+ }
517
+ const activeIndex = releases.findIndex((r) => r.active === true);
518
+ const previous = activeIndex >= 0 && activeIndex + 1 < releases.length ? releases[activeIndex + 1] : releases[1];
519
+ target = previous?.id;
520
+ if (target === undefined) {
521
+ throw new Error("could not determine previous release id");
522
+ }
523
+ }
524
+ await deployer.rollback(target);
525
+ if (mode === "json") {
526
+ writeJson({ rolledBackTo: target, stage });
527
+ } else {
528
+ writeOut(`${stage}: rolled back to ${target}`);
529
+ }
530
+ return 0;
531
+ }
532
+ default:
533
+ throw new Error(`unknown deploy verb: "${verb}". try: releases | status | rollback`);
534
+ }
535
+ };
536
+
537
+ // src/cli.ts
538
+ var parseArgs = (argv) => {
539
+ const positional = [];
540
+ const flags = {};
541
+ let command;
542
+ let verb;
543
+ let index = 0;
544
+ while (index < argv.length) {
545
+ const arg = argv[index];
546
+ if (arg.startsWith("--")) {
547
+ const body = arg.slice(2);
548
+ const eq = body.indexOf("=");
549
+ if (eq >= 0) {
550
+ flags[body.slice(0, eq)] = body.slice(eq + 1);
551
+ } else {
552
+ const next = argv[index + 1];
553
+ if (next !== undefined && !next.startsWith("-")) {
554
+ flags[body] = next;
555
+ index += 1;
556
+ } else {
557
+ flags[body] = true;
558
+ }
559
+ }
560
+ } else if (command === undefined) {
561
+ command = arg;
562
+ } else if (verb === undefined) {
563
+ verb = arg;
564
+ } else {
565
+ positional.push(arg);
566
+ }
567
+ index += 1;
568
+ }
569
+ return { command, flags, positional, verb };
570
+ };
571
+ var HELP = `absolutejs \u2014 substrate CLI for the AbsoluteJS PaaS
572
+
573
+ USAGE
574
+ absolutejs <command> <verb> [args...] [--flags]
575
+
576
+ COMMANDS
577
+ secrets list list secret names + fingerprints from the broker
578
+ secrets get <name> [--show] resolve one secret (--show prints plaintext)
579
+ secrets set <NAME>=<value> put a value via the configured adapter
580
+ secrets rotate <name> generate + persist a new value, fire onRotate
581
+
582
+ env push <stage> resolve secrets + extras, atomic write remote env file
583
+ env pull <stage> print the remote env file (or --json its values)
584
+ env diff <stage> [--all] show what env push would change
585
+
586
+ deploy releases <stage> list release history for a stage
587
+ deploy status <stage> current release id + recent history
588
+ deploy rollback <stage> [--to <id>] roll back to <id> or the previous release
589
+
590
+ GLOBAL FLAGS
591
+ --json machine-readable output
592
+ --help this banner
593
+
594
+ CONFIG
595
+ Reads ./absolutejs.config.ts (walks parent dirs). Author it with:
596
+
597
+ import { defineConfig } from '@absolutejs/cli';
598
+ export default defineConfig({
599
+ secrets: /* SecretBroker */,
600
+ secretAdapter: /* SecretAdapter */,
601
+ deployments: [
602
+ { name: 'prod', target: () => ..., remotePath: '/etc/api.env',
603
+ secretNames: ['STRIPE_KEY'], reload: 'systemctl reload api' }
604
+ ],
605
+ });`;
606
+ var main = async (argv) => {
607
+ const args = parseArgs(argv);
608
+ if (args.flags.help === true || args.command === undefined) {
609
+ writeOut(HELP);
610
+ return args.command === undefined && args.flags.help !== true ? 1 : 0;
611
+ }
612
+ const mode = args.flags.json === true ? "json" : "human";
613
+ const verb = args.verb;
614
+ if (verb === undefined) {
615
+ writeErr(`missing verb for "${args.command}". run \`absolutejs --help\``);
616
+ return 2;
617
+ }
618
+ try {
619
+ const { config } = await loadConfig();
620
+ switch (args.command) {
621
+ case "secrets":
622
+ return await runSecrets(config, {
623
+ flags: args.flags,
624
+ positional: args.positional,
625
+ verb
626
+ }, mode);
627
+ case "env":
628
+ return await runEnv(config, {
629
+ flags: args.flags,
630
+ positional: args.positional,
631
+ verb
632
+ }, mode);
633
+ case "deploy":
634
+ return await runDeploy(config, {
635
+ flags: args.flags,
636
+ positional: args.positional,
637
+ verb
638
+ }, mode);
639
+ default:
640
+ writeErr(`unknown command "${args.command}". try: secrets | env | deploy`);
641
+ return 2;
642
+ }
643
+ } catch (error) {
644
+ const message = error instanceof Error ? error.message : String(error);
645
+ if (mode === "json") {
646
+ process.stdout.write(`${JSON.stringify({ error: message })}
647
+ `);
648
+ } else {
649
+ writeErr(`error: ${message}`);
650
+ }
651
+ return 1;
652
+ }
653
+ };
654
+ export {
655
+ parseArgs,
656
+ main
657
+ };
658
+
659
+ //# debugId=1486CB5B222DF56264756E2164756E21
660
+ //# sourceMappingURL=cli.js.map