vaultsy-cli 0.1.2 → 0.1.4

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 (2) hide show
  1. package/dist/index.js +453 -273
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -259,72 +259,246 @@ var init_env = __esm({
259
259
  // src/index.ts
260
260
  import { Command } from "commander";
261
261
 
262
+ // src/commands/envs.ts
263
+ import * as p from "@clack/prompts";
264
+ import chalk from "chalk";
265
+
262
266
  // ../shared/dist/enums.js
263
267
  var EnvironmentType = ["development", "staging", "preview", "production"];
264
268
 
269
+ // src/commands/envs.ts
270
+ init_api();
271
+ init_env();
272
+ async function envsCommand(projectArg, opts) {
273
+ p.intro(chalk.bold.cyan("vaultsy envs"));
274
+ let envsToShow;
275
+ if (opts.env) {
276
+ if (!EnvironmentType.includes(opts.env)) {
277
+ p.log.error(
278
+ `Invalid environment "${opts.env}". Must be one of: ${EnvironmentType.join(", ")}.`
279
+ );
280
+ process.exit(1);
281
+ }
282
+ envsToShow = [opts.env];
283
+ } else {
284
+ envsToShow = [...EnvironmentType];
285
+ }
286
+ let projectId;
287
+ let projectTitle;
288
+ if (projectArg) {
289
+ projectId = projectArg;
290
+ } else {
291
+ const found = findProjectConfig();
292
+ if (found) {
293
+ projectId = found.config.project;
294
+ p.log.info(`Using project ${chalk.cyan(projectId)} from ${chalk.dim("vaultsy.json")}`);
295
+ } else {
296
+ const spinner9 = p.spinner();
297
+ spinner9.start("Fetching projects\u2026");
298
+ let projects;
299
+ try {
300
+ projects = await listProjects();
301
+ spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
302
+ } catch (err) {
303
+ spinner9.stop("Failed to fetch projects.");
304
+ printApiError(err);
305
+ process.exit(1);
306
+ }
307
+ if (projects.length === 0) {
308
+ p.log.error("No projects found. Create one at your Vaultsy dashboard first.");
309
+ process.exit(1);
310
+ }
311
+ const selected = await p.select({
312
+ message: "Select a project",
313
+ options: projects.map((proj) => ({
314
+ value: proj.id,
315
+ label: proj.title,
316
+ hint: proj.id
317
+ }))
318
+ });
319
+ if (p.isCancel(selected)) {
320
+ p.cancel("Cancelled.");
321
+ process.exit(0);
322
+ }
323
+ projectId = selected;
324
+ projectTitle = projects.find((proj) => proj.id === selected)?.title;
325
+ }
326
+ }
327
+ const spinner8 = p.spinner();
328
+ spinner8.start(
329
+ `Fetching ${envsToShow.length === 1 ? envsToShow[0] : "all"} environment${envsToShow.length !== 1 ? "s" : ""}\u2026`
330
+ );
331
+ let results;
332
+ try {
333
+ results = await Promise.all(
334
+ envsToShow.map(async (env) => {
335
+ try {
336
+ const res = await pullSecrets(projectId, env);
337
+ if (!projectTitle) projectTitle = res.project.title;
338
+ return { env, secrets: res.secrets, error: null };
339
+ } catch (err) {
340
+ const message = err instanceof ApiError ? `${err.status}: ${err.message}` : "Unknown error";
341
+ return { env, secrets: null, error: message };
342
+ }
343
+ })
344
+ );
345
+ spinner8.stop(`Loaded secrets for ${chalk.bold(projectTitle ?? projectId)}.`);
346
+ } catch (err) {
347
+ spinner8.stop("Failed to fetch secrets.");
348
+ printApiError(err);
349
+ process.exit(1);
350
+ }
351
+ const totalSecrets = results.reduce((sum, r) => sum + (r.secrets?.length ?? 0), 0);
352
+ if (totalSecrets === 0 && results.every((r) => r.error === null)) {
353
+ p.log.warn("No secrets found across any environment.");
354
+ p.outro(chalk.dim("Nothing to show."));
355
+ return;
356
+ }
357
+ const lines = [];
358
+ for (const result of results) {
359
+ const envLabel = envBadge(result.env);
360
+ lines.push("");
361
+ lines.push(envLabel);
362
+ lines.push(chalk.dim("\u2500".repeat(60)));
363
+ if (result.error !== null) {
364
+ lines.push(` ${chalk.red("\u2717")} Failed to load: ${chalk.dim(result.error)}`);
365
+ continue;
366
+ }
367
+ if (result.secrets === null || result.secrets.length === 0) {
368
+ lines.push(` ${chalk.dim("No secrets.")}`);
369
+ continue;
370
+ }
371
+ const maxKeyLen = Math.min(
372
+ Math.max(...result.secrets.map((s) => s.key.length), 3),
373
+ 40
374
+ );
375
+ const maxValLen = opts.showValues ? Math.min(Math.max(...result.secrets.map((s) => s.value.length), 5), 60) : 16;
376
+ const colHeader = " " + chalk.bold(padEnd("KEY", maxKeyLen)) + chalk.dim(" ") + chalk.bold(padEnd(opts.showValues ? "VALUE" : "VALUE", maxValLen));
377
+ lines.push(colHeader);
378
+ lines.push(" " + chalk.dim("\xB7".repeat(maxKeyLen + maxValLen + 3)));
379
+ for (const secret of result.secrets) {
380
+ const key = chalk.cyan(padEnd(truncate(secret.key, 40), maxKeyLen));
381
+ let value;
382
+ if (opts.showValues) {
383
+ value = chalk.white(truncate(secret.value, 60));
384
+ } else {
385
+ value = chalk.dim("\u25CF".repeat(Math.min(secret.value.length, 12)) || "(empty)");
386
+ }
387
+ lines.push(` ${key} ${value}`);
388
+ }
389
+ lines.push(
390
+ " " + chalk.dim(
391
+ `${result.secrets.length} secret${result.secrets.length !== 1 ? "s" : ""}`
392
+ )
393
+ );
394
+ }
395
+ p.log.message(lines.join("\n"));
396
+ if (!opts.showValues) {
397
+ p.log.info(
398
+ `Values are hidden. Run with ${chalk.cyan("--show-values")} to reveal them.`
399
+ );
400
+ }
401
+ p.outro(
402
+ `${chalk.bold(projectTitle ?? projectId)} \u2014 ${totalSecrets} secret${totalSecrets !== 1 ? "s" : ""} across ${results.length} environment${results.length !== 1 ? "s" : ""}.`
403
+ );
404
+ }
405
+ var ENV_COLORS = {
406
+ development: chalk.green,
407
+ staging: chalk.yellow,
408
+ preview: chalk.blue,
409
+ production: chalk.red
410
+ };
411
+ function envBadge(env) {
412
+ const color = ENV_COLORS[env];
413
+ return ` ${color("\u25CF")} ${chalk.bold(color(env.toUpperCase()))}`;
414
+ }
415
+ function padEnd(str, length) {
416
+ const visible = str.replace(/\u001b\[[0-9;]*m/g, "");
417
+ const pad = Math.max(0, length - visible.length);
418
+ return str + " ".repeat(pad);
419
+ }
420
+ function truncate(str, max) {
421
+ return str.length > max ? str.slice(0, max - 1) + "\u2026" : str;
422
+ }
423
+ function printApiError(err) {
424
+ if (err instanceof ApiError) {
425
+ if (err.status === 401) {
426
+ p.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
427
+ } else if (err.status === 404) {
428
+ p.log.error("Project not found. Check the project ID.");
429
+ } else {
430
+ p.log.error(`API error ${err.status}: ${err.message}`);
431
+ }
432
+ } else if (err instanceof Error) {
433
+ p.log.error(err.message);
434
+ } else {
435
+ p.log.error("An unexpected error occurred.");
436
+ }
437
+ }
438
+
265
439
  // src/commands/login.ts
266
440
  init_config();
267
441
  init_api();
268
- import * as p from "@clack/prompts";
269
- import chalk from "chalk";
270
- var DEFAULT_BASE_URL = "https://vaultsy.app";
442
+ import * as p2 from "@clack/prompts";
443
+ import chalk2 from "chalk";
444
+ var DEFAULT_BASE_URL = "https://vaultsy.vercel.app";
271
445
  async function loginCommand(opts) {
272
- p.intro(chalk.bold.cyan("vaultsy login"));
446
+ p2.intro(chalk2.bold.cyan("vaultsy login"));
273
447
  const baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
274
448
  let token;
275
449
  if (opts.token) {
276
450
  token = opts.token;
277
451
  } else {
278
- p.log.info(`Create a token at ${chalk.cyan(baseUrl + "/dashboard/settings")}`);
279
- const input = await p.password({
452
+ p2.log.info(`Create a token at ${chalk2.cyan(baseUrl + "/dashboard/settings")}`);
453
+ const input = await p2.password({
280
454
  message: "Paste your API token",
281
455
  validate(value) {
282
456
  if (!value.trim()) return "Token is required.";
283
457
  }
284
458
  });
285
- if (p.isCancel(input)) {
286
- p.cancel("Login cancelled.");
459
+ if (p2.isCancel(input)) {
460
+ p2.cancel("Login cancelled.");
287
461
  process.exit(0);
288
462
  }
289
463
  token = input.trim();
290
464
  }
291
- const spinner7 = p.spinner();
292
- spinner7.start("Verifying token\u2026");
465
+ const spinner8 = p2.spinner();
466
+ spinner8.start("Verifying token\u2026");
293
467
  let userName;
294
468
  let userEmail;
295
469
  try {
296
470
  const me = await getMe({ baseUrl, token });
297
471
  userName = me.name;
298
472
  userEmail = me.email;
299
- spinner7.stop("Token verified.");
473
+ spinner8.stop("Token verified.");
300
474
  } catch (err) {
301
- spinner7.stop("Verification failed.");
475
+ spinner8.stop("Verification failed.");
302
476
  if (err instanceof ApiError) {
303
477
  if (err.status === 401) {
304
- p.log.error("Invalid or expired token. Generate a new one and try again.");
478
+ p2.log.error("Invalid or expired token. Generate a new one and try again.");
305
479
  } else {
306
- p.log.error(`Server responded with ${err.status}: ${err.message}`);
480
+ p2.log.error(`Server responded with ${err.status}: ${err.message}`);
307
481
  }
308
482
  } else {
309
- p.log.error(`Could not reach ${baseUrl}. Check your network connection.`);
483
+ p2.log.error(`Could not reach ${baseUrl}. Check your network connection.`);
310
484
  }
311
485
  process.exit(1);
312
486
  }
313
487
  writeConfig({ token, baseUrl });
314
- p.outro(
315
- `${chalk.green("\u2713")} Logged in as ${chalk.bold(userName)} ${chalk.dim(`<${userEmail}>`)}
316
- Config saved to ${chalk.dim("~/.vaultsy/config.json")}`
488
+ p2.outro(
489
+ `${chalk2.green("\u2713")} Logged in as ${chalk2.bold(userName)} ${chalk2.dim(`<${userEmail}>`)}
490
+ Config saved to ${chalk2.dim("~/.vaultsy/config.json")}`
317
491
  );
318
492
  }
319
493
 
320
494
  // src/commands/pull.ts
321
- import * as p2 from "@clack/prompts";
322
- import chalk2 from "chalk";
495
+ import * as p3 from "@clack/prompts";
496
+ import chalk3 from "chalk";
323
497
  import { resolve as resolve2 } from "path";
324
498
  init_api();
325
499
  init_env();
326
500
  async function pullCommand(projectArg, envArg, opts) {
327
- p2.intro(chalk2.bold.cyan("vaultsy pull"));
501
+ p3.intro(chalk3.bold.cyan("vaultsy pull"));
328
502
  let projectId;
329
503
  let projectTitle;
330
504
  if (projectArg) {
@@ -333,24 +507,24 @@ async function pullCommand(projectArg, envArg, opts) {
333
507
  const found = findProjectConfig();
334
508
  if (found) {
335
509
  projectId = found.config.project;
336
- p2.log.info(`Using project ${chalk2.cyan(projectId)} from ${chalk2.dim("vaultsy.json")}`);
510
+ p3.log.info(`Using project ${chalk3.cyan(projectId)} from ${chalk3.dim("vaultsy.json")}`);
337
511
  } else {
338
- const spinner8 = p2.spinner();
339
- spinner8.start("Fetching projects\u2026");
512
+ const spinner9 = p3.spinner();
513
+ spinner9.start("Fetching projects\u2026");
340
514
  let projects;
341
515
  try {
342
516
  projects = await listProjects();
343
- spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
517
+ spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
344
518
  } catch (err) {
345
- spinner8.stop("Failed to fetch projects.");
346
- printApiError(err);
519
+ spinner9.stop("Failed to fetch projects.");
520
+ printApiError2(err);
347
521
  process.exit(1);
348
522
  }
349
523
  if (projects.length === 0) {
350
- p2.log.error("No projects found. Create one at your Vaultsy dashboard first.");
524
+ p3.log.error("No projects found. Create one at your Vaultsy dashboard first.");
351
525
  process.exit(1);
352
526
  }
353
- const selected = await p2.select({
527
+ const selected = await p3.select({
354
528
  message: "Select a project",
355
529
  options: projects.map((proj) => ({
356
530
  value: proj.id,
@@ -358,8 +532,8 @@ async function pullCommand(projectArg, envArg, opts) {
358
532
  hint: proj.id
359
533
  }))
360
534
  });
361
- if (p2.isCancel(selected)) {
362
- p2.cancel("Pull cancelled.");
535
+ if (p3.isCancel(selected)) {
536
+ p3.cancel("Pull cancelled.");
363
537
  process.exit(0);
364
538
  }
365
539
  projectId = selected;
@@ -369,7 +543,7 @@ async function pullCommand(projectArg, envArg, opts) {
369
543
  let env;
370
544
  if (envArg) {
371
545
  if (!EnvironmentType.includes(envArg)) {
372
- p2.log.error(
546
+ p3.log.error(
373
547
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
374
548
  );
375
549
  process.exit(1);
@@ -378,7 +552,7 @@ async function pullCommand(projectArg, envArg, opts) {
378
552
  } else {
379
553
  const found = findProjectConfig();
380
554
  const defaultEnv = found?.config.defaultEnv;
381
- const selected = await p2.select({
555
+ const selected = await p3.select({
382
556
  message: "Select an environment",
383
557
  options: EnvironmentType.map((e) => ({
384
558
  value: e,
@@ -387,8 +561,8 @@ async function pullCommand(projectArg, envArg, opts) {
387
561
  })),
388
562
  initialValue: defaultEnv ?? "development"
389
563
  });
390
- if (p2.isCancel(selected)) {
391
- p2.cancel("Pull cancelled.");
564
+ if (p3.isCancel(selected)) {
565
+ p3.cancel("Pull cancelled.");
392
566
  process.exit(0);
393
567
  }
394
568
  env = selected;
@@ -396,68 +570,68 @@ async function pullCommand(projectArg, envArg, opts) {
396
570
  const filename = opts.output ?? envFileName(env);
397
571
  const outputPath = resolve2(process.cwd(), filename);
398
572
  if (!opts.yes && !isGitIgnored(filename)) {
399
- p2.log.warn(
400
- `${chalk2.yellow(filename)} does not appear to be in ${chalk2.dim(".gitignore")}.
573
+ p3.log.warn(
574
+ `${chalk3.yellow(filename)} does not appear to be in ${chalk3.dim(".gitignore")}.
401
575
  Make sure you don't accidentally commit secrets to version control.`
402
576
  );
403
- const confirmed = await p2.confirm({
577
+ const confirmed = await p3.confirm({
404
578
  message: "Continue anyway?",
405
579
  initialValue: false
406
580
  });
407
- if (p2.isCancel(confirmed) || !confirmed) {
408
- p2.cancel("Pull cancelled.");
581
+ if (p3.isCancel(confirmed) || !confirmed) {
582
+ p3.cancel("Pull cancelled.");
409
583
  process.exit(0);
410
584
  }
411
585
  }
412
- const spinner7 = p2.spinner();
413
- spinner7.start(`Pulling ${chalk2.cyan(env)} secrets\u2026`);
586
+ const spinner8 = p3.spinner();
587
+ spinner8.start(`Pulling ${chalk3.cyan(env)} secrets\u2026`);
414
588
  let result;
415
589
  try {
416
590
  result = await pullSecrets(projectId, env);
417
- spinner7.stop(
418
- `Pulled ${result.secrets.length} secret${result.secrets.length !== 1 ? "s" : ""} from ${chalk2.bold(projectTitle ?? result.project.title)} / ${chalk2.cyan(env)}.`
591
+ spinner8.stop(
592
+ `Pulled ${result.secrets.length} secret${result.secrets.length !== 1 ? "s" : ""} from ${chalk3.bold(projectTitle ?? result.project.title)} / ${chalk3.cyan(env)}.`
419
593
  );
420
594
  } catch (err) {
421
- spinner7.stop("Pull failed.");
422
- printApiError(err);
595
+ spinner8.stop("Pull failed.");
596
+ printApiError2(err);
423
597
  process.exit(1);
424
598
  }
425
599
  if (result.secrets.length === 0) {
426
- p2.log.warn(`No secrets found for the ${chalk2.cyan(env)} environment.`);
427
- p2.outro(chalk2.dim("Nothing written."));
600
+ p3.log.warn(`No secrets found for the ${chalk3.cyan(env)} environment.`);
601
+ p3.outro(chalk3.dim("Nothing written."));
428
602
  return;
429
603
  }
430
604
  writeEnvFile(outputPath, result.secrets);
431
- p2.outro(
432
- `${chalk2.green("\u2713")} Written to ${chalk2.bold(filename)}
433
- ${chalk2.dim(outputPath)}`
605
+ p3.outro(
606
+ `${chalk3.green("\u2713")} Written to ${chalk3.bold(filename)}
607
+ ${chalk3.dim(outputPath)}`
434
608
  );
435
609
  }
436
- function printApiError(err) {
610
+ function printApiError2(err) {
437
611
  if (err instanceof ApiError) {
438
612
  if (err.status === 401) {
439
- p2.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
613
+ p3.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
440
614
  } else if (err.status === 404) {
441
- p2.log.error("Project or environment not found. Check the project ID and environment name.");
615
+ p3.log.error("Project or environment not found. Check the project ID and environment name.");
442
616
  } else {
443
- p2.log.error(`API error ${err.status}: ${err.message}`);
617
+ p3.log.error(`API error ${err.status}: ${err.message}`);
444
618
  }
445
619
  } else if (err instanceof Error) {
446
- p2.log.error(err.message);
620
+ p3.log.error(err.message);
447
621
  } else {
448
- p2.log.error("An unexpected error occurred.");
622
+ p3.log.error("An unexpected error occurred.");
449
623
  }
450
624
  }
451
625
 
452
626
  // src/commands/push.ts
453
- import * as p3 from "@clack/prompts";
454
- import chalk3 from "chalk";
627
+ import * as p4 from "@clack/prompts";
628
+ import chalk4 from "chalk";
455
629
  import { resolve as resolve3 } from "path";
456
630
  import { existsSync as existsSync3 } from "fs";
457
631
  init_api();
458
632
  init_env();
459
633
  async function pushCommand(projectArg, envArg, opts) {
460
- p3.intro(chalk3.bold.cyan("vaultsy push"));
634
+ p4.intro(chalk4.bold.cyan("vaultsy push"));
461
635
  let projectId;
462
636
  let projectTitle;
463
637
  if (projectArg) {
@@ -466,24 +640,24 @@ async function pushCommand(projectArg, envArg, opts) {
466
640
  const found = findProjectConfig();
467
641
  if (found) {
468
642
  projectId = found.config.project;
469
- p3.log.info(`Using project ${chalk3.cyan(projectId)} from ${chalk3.dim("vaultsy.json")}`);
643
+ p4.log.info(`Using project ${chalk4.cyan(projectId)} from ${chalk4.dim("vaultsy.json")}`);
470
644
  } else {
471
- const spinner7 = p3.spinner();
472
- spinner7.start("Fetching projects\u2026");
645
+ const spinner8 = p4.spinner();
646
+ spinner8.start("Fetching projects\u2026");
473
647
  let projects;
474
648
  try {
475
649
  projects = await listProjects();
476
- spinner7.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
650
+ spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
477
651
  } catch (err) {
478
- spinner7.stop("Failed to fetch projects.");
479
- printApiError2(err);
652
+ spinner8.stop("Failed to fetch projects.");
653
+ printApiError3(err);
480
654
  process.exit(1);
481
655
  }
482
656
  if (projects.length === 0) {
483
- p3.log.error("No projects found. Create one at your Vaultsy dashboard first.");
657
+ p4.log.error("No projects found. Create one at your Vaultsy dashboard first.");
484
658
  process.exit(1);
485
659
  }
486
- const selected = await p3.select({
660
+ const selected = await p4.select({
487
661
  message: "Select a project",
488
662
  options: projects.map((proj) => ({
489
663
  value: proj.id,
@@ -491,8 +665,8 @@ async function pushCommand(projectArg, envArg, opts) {
491
665
  hint: proj.id
492
666
  }))
493
667
  });
494
- if (p3.isCancel(selected)) {
495
- p3.cancel("Push cancelled.");
668
+ if (p4.isCancel(selected)) {
669
+ p4.cancel("Push cancelled.");
496
670
  process.exit(0);
497
671
  }
498
672
  projectId = selected;
@@ -502,7 +676,7 @@ async function pushCommand(projectArg, envArg, opts) {
502
676
  let env;
503
677
  if (envArg) {
504
678
  if (!EnvironmentType.includes(envArg)) {
505
- p3.log.error(
679
+ p4.log.error(
506
680
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
507
681
  );
508
682
  process.exit(1);
@@ -511,7 +685,7 @@ async function pushCommand(projectArg, envArg, opts) {
511
685
  } else {
512
686
  const found = findProjectConfig();
513
687
  const defaultEnv = found?.config.defaultEnv;
514
- const selected = await p3.select({
688
+ const selected = await p4.select({
515
689
  message: "Select an environment",
516
690
  options: EnvironmentType.map((e) => ({
517
691
  value: e,
@@ -520,8 +694,8 @@ async function pushCommand(projectArg, envArg, opts) {
520
694
  })),
521
695
  initialValue: defaultEnv ?? "development"
522
696
  });
523
- if (p3.isCancel(selected)) {
524
- p3.cancel("Push cancelled.");
697
+ if (p4.isCancel(selected)) {
698
+ p4.cancel("Push cancelled.");
525
699
  process.exit(0);
526
700
  }
527
701
  env = selected;
@@ -529,22 +703,22 @@ async function pushCommand(projectArg, envArg, opts) {
529
703
  const filename = opts.input ?? envFileName(env);
530
704
  const inputPath = resolve3(process.cwd(), filename);
531
705
  if (!existsSync3(inputPath)) {
532
- p3.log.error(
533
- `File ${chalk3.bold(filename)} not found.
534
- Run ${chalk3.cyan(`vaultsy pull ${projectId} ${env}`)} first, or specify a file with ${chalk3.dim("--input <file>")}.`
706
+ p4.log.error(
707
+ `File ${chalk4.bold(filename)} not found.
708
+ Run ${chalk4.cyan(`vaultsy pull ${projectId} ${env}`)} first, or specify a file with ${chalk4.dim("--input <file>")}.`
535
709
  );
536
710
  process.exit(1);
537
711
  }
538
712
  const localSecrets = readEnvFile(inputPath).filter((r) => r.key && r.value);
539
713
  if (localSecrets.length === 0) {
540
- p3.log.warn(`${chalk3.bold(filename)} is empty or contains no valid KEY=VALUE pairs.`);
541
- p3.outro(chalk3.dim("Nothing pushed."));
714
+ p4.log.warn(`${chalk4.bold(filename)} is empty or contains no valid KEY=VALUE pairs.`);
715
+ p4.outro(chalk4.dim("Nothing pushed."));
542
716
  return;
543
717
  }
544
- p3.log.info(
545
- `Read ${chalk3.bold(String(localSecrets.length))} secret${localSecrets.length !== 1 ? "s" : ""} from ${chalk3.bold(filename)}.`
718
+ p4.log.info(
719
+ `Read ${chalk4.bold(String(localSecrets.length))} secret${localSecrets.length !== 1 ? "s" : ""} from ${chalk4.bold(filename)}.`
546
720
  );
547
- const diffSpinner = p3.spinner();
721
+ const diffSpinner = p4.spinner();
548
722
  diffSpinner.start("Computing diff against remote\u2026");
549
723
  let remoteSecrets;
550
724
  let resolvedTitle;
@@ -555,41 +729,41 @@ async function pushCommand(projectArg, envArg, opts) {
555
729
  diffSpinner.stop("Diff computed.");
556
730
  } catch (err) {
557
731
  diffSpinner.stop("Failed to fetch remote secrets.");
558
- printApiError2(err);
732
+ printApiError3(err);
559
733
  process.exit(1);
560
734
  }
561
735
  const diff = computeDiff(remoteSecrets, localSecrets);
562
736
  printDiff(diff);
563
737
  const hasChanges = diff.added.length > 0 || diff.modified.length > 0 || diff.removed.length > 0;
564
738
  if (!hasChanges) {
565
- p3.outro(`${chalk3.dim("No changes.")} Remote ${chalk3.cyan(env)} is already up to date.`);
739
+ p4.outro(`${chalk4.dim("No changes.")} Remote ${chalk4.cyan(env)} is already up to date.`);
566
740
  return;
567
741
  }
568
742
  if (!opts.yes) {
569
- const confirmed = await p3.confirm({
570
- message: `Push these changes to ${chalk3.bold(resolvedTitle)} / ${chalk3.cyan(env)}?`,
743
+ const confirmed = await p4.confirm({
744
+ message: `Push these changes to ${chalk4.bold(resolvedTitle)} / ${chalk4.cyan(env)}?`,
571
745
  initialValue: true
572
746
  });
573
- if (p3.isCancel(confirmed) || !confirmed) {
574
- p3.cancel("Push cancelled.");
747
+ if (p4.isCancel(confirmed) || !confirmed) {
748
+ p4.cancel("Push cancelled.");
575
749
  process.exit(0);
576
750
  }
577
751
  }
578
- const pushSpinner = p3.spinner();
579
- pushSpinner.start(`Pushing to ${chalk3.cyan(env)}\u2026`);
752
+ const pushSpinner = p4.spinner();
753
+ pushSpinner.start(`Pushing to ${chalk4.cyan(env)}\u2026`);
580
754
  try {
581
755
  const result = await pushSecrets(projectId, env, localSecrets);
582
756
  const { added, modified, removed, unchanged } = result.changes;
583
757
  pushSpinner.stop(
584
- `Done. ${chalk3.green(`+${added}`)} added, ${chalk3.yellow(`~${modified}`)} modified, ${chalk3.red(`-${removed}`)} removed, ${chalk3.dim(`${unchanged} unchanged`)}.`
758
+ `Done. ${chalk4.green(`+${added}`)} added, ${chalk4.yellow(`~${modified}`)} modified, ${chalk4.red(`-${removed}`)} removed, ${chalk4.dim(`${unchanged} unchanged`)}.`
585
759
  );
586
760
  } catch (err) {
587
761
  pushSpinner.stop("Push failed.");
588
- printApiError2(err);
762
+ printApiError3(err);
589
763
  process.exit(1);
590
764
  }
591
- p3.outro(
592
- `${chalk3.green("\u2713")} ${chalk3.bold(resolvedTitle)} / ${chalk3.cyan(env)} updated successfully.`
765
+ p4.outro(
766
+ `${chalk4.green("\u2713")} ${chalk4.bold(resolvedTitle)} / ${chalk4.cyan(env)} updated successfully.`
593
767
  );
594
768
  }
595
769
  function computeDiff(remote, local) {
@@ -622,54 +796,54 @@ function computeDiff(remote, local) {
622
796
  function printDiff(diff) {
623
797
  const total = diff.added.length + diff.modified.length + diff.removed.length + diff.unchanged.length;
624
798
  if (total === 0) {
625
- p3.log.info(chalk3.dim("No secrets on remote or local."));
799
+ p4.log.info(chalk4.dim("No secrets on remote or local."));
626
800
  return;
627
801
  }
628
802
  const lines = [];
629
803
  for (const key of diff.added) {
630
- lines.push(` ${chalk3.green("+")} ${chalk3.green(key)}`);
804
+ lines.push(` ${chalk4.green("+")} ${chalk4.green(key)}`);
631
805
  }
632
806
  for (const key of diff.modified) {
633
- lines.push(` ${chalk3.yellow("~")} ${chalk3.yellow(key)}`);
807
+ lines.push(` ${chalk4.yellow("~")} ${chalk4.yellow(key)}`);
634
808
  }
635
809
  for (const key of diff.removed) {
636
- lines.push(` ${chalk3.red("-")} ${chalk3.red(key)}`);
810
+ lines.push(` ${chalk4.red("-")} ${chalk4.red(key)}`);
637
811
  }
638
812
  for (const key of diff.unchanged) {
639
- lines.push(` ${chalk3.dim("\xB7")} ${chalk3.dim(key)}`);
813
+ lines.push(` ${chalk4.dim("\xB7")} ${chalk4.dim(key)}`);
640
814
  }
641
- p3.log.message(lines.join("\n"));
815
+ p4.log.message(lines.join("\n"));
642
816
  const summary = [
643
- diff.added.length > 0 ? chalk3.green(`+${diff.added.length} to add`) : null,
644
- diff.modified.length > 0 ? chalk3.yellow(`~${diff.modified.length} to modify`) : null,
645
- diff.removed.length > 0 ? chalk3.red(`-${diff.removed.length} to remove`) : null,
646
- diff.unchanged.length > 0 ? chalk3.dim(`${diff.unchanged.length} unchanged`) : null
647
- ].filter(Boolean).join(chalk3.dim(", "));
648
- p3.log.info(summary);
817
+ diff.added.length > 0 ? chalk4.green(`+${diff.added.length} to add`) : null,
818
+ diff.modified.length > 0 ? chalk4.yellow(`~${diff.modified.length} to modify`) : null,
819
+ diff.removed.length > 0 ? chalk4.red(`-${diff.removed.length} to remove`) : null,
820
+ diff.unchanged.length > 0 ? chalk4.dim(`${diff.unchanged.length} unchanged`) : null
821
+ ].filter(Boolean).join(chalk4.dim(", "));
822
+ p4.log.info(summary);
649
823
  }
650
- function printApiError2(err) {
824
+ function printApiError3(err) {
651
825
  if (err instanceof ApiError) {
652
826
  if (err.status === 401) {
653
- p3.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
827
+ p4.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
654
828
  } else if (err.status === 404) {
655
- p3.log.error("Project or environment not found. Check the project ID and environment name.");
829
+ p4.log.error("Project or environment not found. Check the project ID and environment name.");
656
830
  } else {
657
- p3.log.error(`API error ${err.status}: ${err.message}`);
831
+ p4.log.error(`API error ${err.status}: ${err.message}`);
658
832
  }
659
833
  } else if (err instanceof Error) {
660
- p3.log.error(err.message);
834
+ p4.log.error(err.message);
661
835
  } else {
662
- p3.log.error("An unexpected error occurred.");
836
+ p4.log.error("An unexpected error occurred.");
663
837
  }
664
838
  }
665
839
 
666
840
  // src/commands/history.ts
667
- import * as p4 from "@clack/prompts";
668
- import chalk4 from "chalk";
841
+ import * as p5 from "@clack/prompts";
842
+ import chalk5 from "chalk";
669
843
  init_api();
670
844
  init_env();
671
845
  async function historyCommand(projectArg, envArg) {
672
- p4.intro(chalk4.bold.cyan("vaultsy history"));
846
+ p5.intro(chalk5.bold.cyan("vaultsy history"));
673
847
  let projectId;
674
848
  let projectTitle;
675
849
  if (projectArg) {
@@ -678,24 +852,24 @@ async function historyCommand(projectArg, envArg) {
678
852
  const found = findProjectConfig();
679
853
  if (found) {
680
854
  projectId = found.config.project;
681
- p4.log.info(`Using project ${chalk4.cyan(projectId)} from ${chalk4.dim("vaultsy.json")}`);
855
+ p5.log.info(`Using project ${chalk5.cyan(projectId)} from ${chalk5.dim("vaultsy.json")}`);
682
856
  } else {
683
- const spinner8 = p4.spinner();
684
- spinner8.start("Fetching projects\u2026");
857
+ const spinner9 = p5.spinner();
858
+ spinner9.start("Fetching projects\u2026");
685
859
  let projects;
686
860
  try {
687
861
  projects = await listProjects();
688
- spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
862
+ spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
689
863
  } catch (err) {
690
- spinner8.stop("Failed to fetch projects.");
691
- printApiError3(err);
864
+ spinner9.stop("Failed to fetch projects.");
865
+ printApiError4(err);
692
866
  process.exit(1);
693
867
  }
694
868
  if (projects.length === 0) {
695
- p4.log.error("No projects found. Create one at your Vaultsy dashboard first.");
869
+ p5.log.error("No projects found. Create one at your Vaultsy dashboard first.");
696
870
  process.exit(1);
697
871
  }
698
- const selected = await p4.select({
872
+ const selected = await p5.select({
699
873
  message: "Select a project",
700
874
  options: projects.map((proj) => ({
701
875
  value: proj.id,
@@ -703,8 +877,8 @@ async function historyCommand(projectArg, envArg) {
703
877
  hint: proj.id
704
878
  }))
705
879
  });
706
- if (p4.isCancel(selected)) {
707
- p4.cancel("Cancelled.");
880
+ if (p5.isCancel(selected)) {
881
+ p5.cancel("Cancelled.");
708
882
  process.exit(0);
709
883
  }
710
884
  projectId = selected;
@@ -714,7 +888,7 @@ async function historyCommand(projectArg, envArg) {
714
888
  let env;
715
889
  if (envArg) {
716
890
  if (!EnvironmentType.includes(envArg)) {
717
- p4.log.error(
891
+ p5.log.error(
718
892
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
719
893
  );
720
894
  process.exit(1);
@@ -723,7 +897,7 @@ async function historyCommand(projectArg, envArg) {
723
897
  } else {
724
898
  const found = findProjectConfig();
725
899
  const defaultEnv = found?.config.defaultEnv;
726
- const selected = await p4.select({
900
+ const selected = await p5.select({
727
901
  message: "Select an environment",
728
902
  options: EnvironmentType.map((e) => ({
729
903
  value: e,
@@ -732,57 +906,57 @@ async function historyCommand(projectArg, envArg) {
732
906
  })),
733
907
  initialValue: defaultEnv ?? "development"
734
908
  });
735
- if (p4.isCancel(selected)) {
736
- p4.cancel("Cancelled.");
909
+ if (p5.isCancel(selected)) {
910
+ p5.cancel("Cancelled.");
737
911
  process.exit(0);
738
912
  }
739
913
  env = selected;
740
914
  }
741
- const spinner7 = p4.spinner();
742
- spinner7.start(`Fetching history for ${chalk4.cyan(env)}\u2026`);
915
+ const spinner8 = p5.spinner();
916
+ spinner8.start(`Fetching history for ${chalk5.cyan(env)}\u2026`);
743
917
  let result;
744
918
  try {
745
919
  result = await listVersions(projectId, env);
746
- spinner7.stop(
747
- `${result.versions.length} snapshot${result.versions.length !== 1 ? "s" : ""} for ${chalk4.bold(projectTitle ?? result.project.title)} / ${chalk4.cyan(env)}.`
920
+ spinner8.stop(
921
+ `${result.versions.length} snapshot${result.versions.length !== 1 ? "s" : ""} for ${chalk5.bold(projectTitle ?? result.project.title)} / ${chalk5.cyan(env)}.`
748
922
  );
749
923
  } catch (err) {
750
- spinner7.stop("Failed to fetch history.");
751
- printApiError3(err);
924
+ spinner8.stop("Failed to fetch history.");
925
+ printApiError4(err);
752
926
  process.exit(1);
753
927
  }
754
928
  if (result.versions.length === 0) {
755
- p4.log.warn(`No version history found for the ${chalk4.cyan(env)} environment.`);
756
- p4.outro(chalk4.dim("Nothing to show."));
929
+ p5.log.warn(`No version history found for the ${chalk5.cyan(env)} environment.`);
930
+ p5.outro(chalk5.dim("Nothing to show."));
757
931
  return;
758
932
  }
759
933
  const COL_VER = 7;
760
934
  const COL_SECRETS = 7;
761
935
  const COL_BY = 20;
762
936
  const COL_DATE = 22;
763
- const header = chalk4.bold(padEnd("#", COL_VER)) + chalk4.dim(" \u2502 ") + chalk4.bold(padEnd("VERSION ID", 26)) + chalk4.dim(" \u2502 ") + chalk4.bold(padEnd("KEYS", COL_SECRETS)) + chalk4.dim(" \u2502 ") + chalk4.bold(padEnd("CREATED BY", COL_BY)) + chalk4.dim(" \u2502 ") + chalk4.bold(padEnd("DATE", COL_DATE));
764
- const divider = chalk4.dim(
937
+ const header = chalk5.bold(padEnd2("#", COL_VER)) + chalk5.dim(" \u2502 ") + chalk5.bold(padEnd2("VERSION ID", 26)) + chalk5.dim(" \u2502 ") + chalk5.bold(padEnd2("KEYS", COL_SECRETS)) + chalk5.dim(" \u2502 ") + chalk5.bold(padEnd2("CREATED BY", COL_BY)) + chalk5.dim(" \u2502 ") + chalk5.bold(padEnd2("DATE", COL_DATE));
938
+ const divider = chalk5.dim(
765
939
  "\u2500".repeat(COL_VER) + "\u2500\u253C\u2500" + "\u2500".repeat(26) + "\u2500\u253C\u2500" + "\u2500".repeat(COL_SECRETS) + "\u2500\u253C\u2500" + "\u2500".repeat(COL_BY) + "\u2500\u253C\u2500" + "\u2500".repeat(COL_DATE)
766
940
  );
767
941
  const rows = result.versions.map((v, i) => {
768
942
  const isLatest = i === 0;
769
- const vNum = isLatest ? chalk4.green(padEnd(`v${v.versionNumber}`, COL_VER)) : chalk4.dim(padEnd(`v${v.versionNumber}`, COL_VER));
770
- const vId = chalk4.dim(padEnd(v.id, 26));
771
- const secrets = padEnd(String(v.secretCount), COL_SECRETS);
772
- const by = padEnd(v.createdBy?.name ?? chalk4.italic("system"), COL_BY);
773
- const date = padEnd(formatDate(v.createdAt), COL_DATE);
774
- const latestBadge = isLatest ? chalk4.green(" \u2190 latest") : "";
775
- return vNum + chalk4.dim(" \u2502 ") + vId + chalk4.dim(" \u2502 ") + secrets + chalk4.dim(" \u2502 ") + by + chalk4.dim(" \u2502 ") + date + latestBadge;
943
+ const vNum = isLatest ? chalk5.green(padEnd2(`v${v.versionNumber}`, COL_VER)) : chalk5.dim(padEnd2(`v${v.versionNumber}`, COL_VER));
944
+ const vId = chalk5.dim(padEnd2(v.id, 26));
945
+ const secrets = padEnd2(String(v.secretCount), COL_SECRETS);
946
+ const by = padEnd2(v.createdBy?.name ?? chalk5.italic("system"), COL_BY);
947
+ const date = padEnd2(formatDate(v.createdAt), COL_DATE);
948
+ const latestBadge = isLatest ? chalk5.green(" \u2190 latest") : "";
949
+ return vNum + chalk5.dim(" \u2502 ") + vId + chalk5.dim(" \u2502 ") + secrets + chalk5.dim(" \u2502 ") + by + chalk5.dim(" \u2502 ") + date + latestBadge;
776
950
  });
777
951
  const lines = [header, divider, ...rows];
778
- p4.log.message(lines.join("\n"));
779
- p4.log.info(
780
- `To rollback, run: ${chalk4.cyan(`vaultsy rollback ${projectId} ${env} <VERSION_ID>`)}`
952
+ p5.log.message(lines.join("\n"));
953
+ p5.log.info(
954
+ `To rollback, run: ${chalk5.cyan(`vaultsy rollback ${projectId} ${env} <VERSION_ID>`)}`
781
955
  );
782
- p4.outro(chalk4.dim("Done."));
956
+ p5.outro(chalk5.dim("Done."));
783
957
  }
784
958
  var ANSI_REGEX = new RegExp("\x1B\\[[0-9;]*m", "g");
785
- function padEnd(str, length) {
959
+ function padEnd2(str, length) {
786
960
  const visible = str.replace(ANSI_REGEX, "");
787
961
  const pad = Math.max(0, length - visible.length);
788
962
  return str + " ".repeat(pad);
@@ -800,29 +974,29 @@ function formatDate(iso) {
800
974
  });
801
975
  return `${date}, ${time}`;
802
976
  }
803
- function printApiError3(err) {
977
+ function printApiError4(err) {
804
978
  if (err instanceof ApiError) {
805
979
  if (err.status === 401) {
806
- p4.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
980
+ p5.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
807
981
  } else if (err.status === 404) {
808
- p4.log.error("Project or environment not found. Check the project ID and environment name.");
982
+ p5.log.error("Project or environment not found. Check the project ID and environment name.");
809
983
  } else {
810
- p4.log.error(`API error ${err.status}: ${err.message}`);
984
+ p5.log.error(`API error ${err.status}: ${err.message}`);
811
985
  }
812
986
  } else if (err instanceof Error) {
813
- p4.log.error(err.message);
987
+ p5.log.error(err.message);
814
988
  } else {
815
- p4.log.error("An unexpected error occurred.");
989
+ p5.log.error("An unexpected error occurred.");
816
990
  }
817
991
  }
818
992
 
819
993
  // src/commands/rollback.ts
820
- import * as p5 from "@clack/prompts";
821
- import chalk5 from "chalk";
994
+ import * as p6 from "@clack/prompts";
995
+ import chalk6 from "chalk";
822
996
  init_api();
823
997
  init_env();
824
998
  async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
825
- p5.intro(chalk5.bold.cyan("vaultsy rollback"));
999
+ p6.intro(chalk6.bold.cyan("vaultsy rollback"));
826
1000
  let projectId;
827
1001
  let projectTitle;
828
1002
  if (projectArg) {
@@ -831,24 +1005,24 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
831
1005
  const found = findProjectConfig();
832
1006
  if (found) {
833
1007
  projectId = found.config.project;
834
- p5.log.info(`Using project ${chalk5.cyan(projectId)} from ${chalk5.dim("vaultsy.json")}`);
1008
+ p6.log.info(`Using project ${chalk6.cyan(projectId)} from ${chalk6.dim("vaultsy.json")}`);
835
1009
  } else {
836
- const spinner8 = p5.spinner();
837
- spinner8.start("Fetching projects\u2026");
1010
+ const spinner9 = p6.spinner();
1011
+ spinner9.start("Fetching projects\u2026");
838
1012
  let projects;
839
1013
  try {
840
1014
  projects = await listProjects();
841
- spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1015
+ spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
842
1016
  } catch (err) {
843
- spinner8.stop("Failed to fetch projects.");
844
- printApiError4(err);
1017
+ spinner9.stop("Failed to fetch projects.");
1018
+ printApiError5(err);
845
1019
  process.exit(1);
846
1020
  }
847
1021
  if (projects.length === 0) {
848
- p5.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1022
+ p6.log.error("No projects found. Create one at your Vaultsy dashboard first.");
849
1023
  process.exit(1);
850
1024
  }
851
- const selected = await p5.select({
1025
+ const selected = await p6.select({
852
1026
  message: "Select a project",
853
1027
  options: projects.map((proj) => ({
854
1028
  value: proj.id,
@@ -856,8 +1030,8 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
856
1030
  hint: proj.id
857
1031
  }))
858
1032
  });
859
- if (p5.isCancel(selected)) {
860
- p5.cancel("Rollback cancelled.");
1033
+ if (p6.isCancel(selected)) {
1034
+ p6.cancel("Rollback cancelled.");
861
1035
  process.exit(0);
862
1036
  }
863
1037
  projectId = selected;
@@ -867,7 +1041,7 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
867
1041
  let env;
868
1042
  if (envArg) {
869
1043
  if (!EnvironmentType.includes(envArg)) {
870
- p5.log.error(
1044
+ p6.log.error(
871
1045
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
872
1046
  );
873
1047
  process.exit(1);
@@ -876,7 +1050,7 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
876
1050
  } else {
877
1051
  const found = findProjectConfig();
878
1052
  const defaultEnv = found?.config.defaultEnv;
879
- const selected = await p5.select({
1053
+ const selected = await p6.select({
880
1054
  message: "Select an environment",
881
1055
  options: EnvironmentType.map((e) => ({
882
1056
  value: e,
@@ -885,8 +1059,8 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
885
1059
  })),
886
1060
  initialValue: defaultEnv ?? "development"
887
1061
  });
888
- if (p5.isCancel(selected)) {
889
- p5.cancel("Rollback cancelled.");
1062
+ if (p6.isCancel(selected)) {
1063
+ p6.cancel("Rollback cancelled.");
890
1064
  process.exit(0);
891
1065
  }
892
1066
  env = selected;
@@ -896,25 +1070,25 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
896
1070
  if (versionIdArg) {
897
1071
  versionId = versionIdArg;
898
1072
  } else {
899
- const spinner8 = p5.spinner();
900
- spinner8.start(`Fetching version history for ${chalk5.cyan(env)}\u2026`);
1073
+ const spinner9 = p6.spinner();
1074
+ spinner9.start(`Fetching version history for ${chalk6.cyan(env)}\u2026`);
901
1075
  let versionsResult;
902
1076
  try {
903
1077
  versionsResult = await listVersions(projectId, env);
904
- spinner8.stop(
1078
+ spinner9.stop(
905
1079
  `Found ${versionsResult.versions.length} snapshot${versionsResult.versions.length !== 1 ? "s" : ""}.`
906
1080
  );
907
1081
  } catch (err) {
908
- spinner8.stop("Failed to fetch version history.");
909
- printApiError4(err);
1082
+ spinner9.stop("Failed to fetch version history.");
1083
+ printApiError5(err);
910
1084
  process.exit(1);
911
1085
  }
912
1086
  if (versionsResult.versions.length === 0) {
913
- p5.log.error(`No version history found for the ${chalk5.cyan(env)} environment.`);
1087
+ p6.log.error(`No version history found for the ${chalk6.cyan(env)} environment.`);
914
1088
  process.exit(1);
915
1089
  }
916
1090
  const pickable = versionsResult.versions;
917
- const selected = await p5.select({
1091
+ const selected = await p6.select({
918
1092
  message: "Select a version to roll back to",
919
1093
  options: pickable.map((v, i) => ({
920
1094
  value: v.id,
@@ -922,8 +1096,8 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
922
1096
  hint: i === 0 ? "current" : v.createdBy?.name ? `by ${v.createdBy.name}` : void 0
923
1097
  }))
924
1098
  });
925
- if (p5.isCancel(selected)) {
926
- p5.cancel("Rollback cancelled.");
1099
+ if (p6.isCancel(selected)) {
1100
+ p6.cancel("Rollback cancelled.");
927
1101
  process.exit(0);
928
1102
  }
929
1103
  versionId = selected;
@@ -931,34 +1105,34 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
931
1105
  projectTitle ??= versionsResult.project.title;
932
1106
  }
933
1107
  if (!opts.yes) {
934
- const label = versionNumber !== void 0 ? `v${versionNumber} (${chalk5.dim(versionId)})` : chalk5.dim(versionId);
935
- p5.log.warn(
936
- `This will overwrite all ${chalk5.bold(env)} secrets with the state from snapshot ${label}.
1108
+ const label = versionNumber !== void 0 ? `v${versionNumber} (${chalk6.dim(versionId)})` : chalk6.dim(versionId);
1109
+ p6.log.warn(
1110
+ `This will overwrite all ${chalk6.bold(env)} secrets with the state from snapshot ${label}.
937
1111
  A new snapshot will be created automatically so you can undo this rollback too.`
938
1112
  );
939
- const confirmed = await p5.confirm({
940
- message: `Roll back ${chalk5.bold(projectTitle ?? projectId)} / ${chalk5.cyan(env)} to ${label}?`,
1113
+ const confirmed = await p6.confirm({
1114
+ message: `Roll back ${chalk6.bold(projectTitle ?? projectId)} / ${chalk6.cyan(env)} to ${label}?`,
941
1115
  initialValue: false
942
1116
  });
943
- if (p5.isCancel(confirmed) || !confirmed) {
944
- p5.cancel("Rollback cancelled.");
1117
+ if (p6.isCancel(confirmed) || !confirmed) {
1118
+ p6.cancel("Rollback cancelled.");
945
1119
  process.exit(0);
946
1120
  }
947
1121
  }
948
- const spinner7 = p5.spinner();
949
- spinner7.start("Rolling back\u2026");
1122
+ const spinner8 = p6.spinner();
1123
+ spinner8.start("Rolling back\u2026");
950
1124
  try {
951
1125
  const result = await rollback(projectId, env, versionId);
952
1126
  const { added, modified, removed, unchanged } = result.changes;
953
- spinner7.stop(
954
- `Rolled back to v${result.rolledBackTo.versionNumber}. ${chalk5.green(`+${added}`)} added, ${chalk5.yellow(`~${modified}`)} modified, ${chalk5.red(`-${removed}`)} removed, ${chalk5.dim(`${unchanged} unchanged`)}.`
1127
+ spinner8.stop(
1128
+ `Rolled back to v${result.rolledBackTo.versionNumber}. ${chalk6.green(`+${added}`)} added, ${chalk6.yellow(`~${modified}`)} modified, ${chalk6.red(`-${removed}`)} removed, ${chalk6.dim(`${unchanged} unchanged`)}.`
955
1129
  );
956
- p5.outro(
957
- `${chalk5.green("\u2713")} ${chalk5.bold(projectTitle ?? result.project.title)} / ${chalk5.cyan(env)} rolled back to ${chalk5.bold(`v${result.rolledBackTo.versionNumber}`)}.`
1130
+ p6.outro(
1131
+ `${chalk6.green("\u2713")} ${chalk6.bold(projectTitle ?? result.project.title)} / ${chalk6.cyan(env)} rolled back to ${chalk6.bold(`v${result.rolledBackTo.versionNumber}`)}.`
958
1132
  );
959
1133
  } catch (err) {
960
- spinner7.stop("Rollback failed.");
961
- printApiError4(err);
1134
+ spinner8.stop("Rollback failed.");
1135
+ printApiError5(err);
962
1136
  process.exit(1);
963
1137
  }
964
1138
  }
@@ -973,38 +1147,38 @@ function formatDate2(iso) {
973
1147
  minute: "2-digit"
974
1148
  });
975
1149
  }
976
- function printApiError4(err) {
1150
+ function printApiError5(err) {
977
1151
  if (err instanceof ApiError) {
978
1152
  if (err.status === 401) {
979
- p5.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1153
+ p6.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
980
1154
  } else if (err.status === 404) {
981
- p5.log.error("Project or environment not found. Check the project ID and environment name.");
1155
+ p6.log.error("Project or environment not found. Check the project ID and environment name.");
982
1156
  } else {
983
- p5.log.error(`API error ${err.status}: ${err.message}`);
1157
+ p6.log.error(`API error ${err.status}: ${err.message}`);
984
1158
  }
985
1159
  } else if (err instanceof Error) {
986
- p5.log.error(err.message);
1160
+ p6.log.error(err.message);
987
1161
  } else {
988
- p5.log.error("An unexpected error occurred.");
1162
+ p6.log.error("An unexpected error occurred.");
989
1163
  }
990
1164
  }
991
1165
 
992
1166
  // src/commands/run.ts
993
- import * as p6 from "@clack/prompts";
994
- import chalk6 from "chalk";
1167
+ import * as p7 from "@clack/prompts";
1168
+ import chalk7 from "chalk";
995
1169
  import { spawn } from "child_process";
996
1170
  init_api();
997
1171
  init_env();
998
1172
  async function runCommand(projectArg, envArg, commandArgs, _opts) {
999
1173
  if (commandArgs.length === 0) {
1000
- p6.log.error(
1174
+ p7.log.error(
1001
1175
  `No command specified.
1002
- Usage: ${chalk6.cyan("vaultsy run <project> <env> -- <command> [args...]")}
1003
- Example: ${chalk6.dim("vaultsy run my-app production -- node server.js")}`
1176
+ Usage: ${chalk7.cyan("vaultsy run <project> <env> -- <command> [args...]")}
1177
+ Example: ${chalk7.dim("vaultsy run my-app production -- node server.js")}`
1004
1178
  );
1005
1179
  process.exit(1);
1006
1180
  }
1007
- p6.intro(chalk6.bold.cyan("vaultsy run"));
1181
+ p7.intro(chalk7.bold.cyan("vaultsy run"));
1008
1182
  let projectId;
1009
1183
  let projectTitle;
1010
1184
  if (projectArg) {
@@ -1013,24 +1187,24 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1013
1187
  const found = findProjectConfig();
1014
1188
  if (found) {
1015
1189
  projectId = found.config.project;
1016
- p6.log.info(`Using project ${chalk6.cyan(projectId)} from ${chalk6.dim("vaultsy.json")}`);
1190
+ p7.log.info(`Using project ${chalk7.cyan(projectId)} from ${chalk7.dim("vaultsy.json")}`);
1017
1191
  } else {
1018
- const spinner8 = p6.spinner();
1019
- spinner8.start("Fetching projects\u2026");
1192
+ const spinner9 = p7.spinner();
1193
+ spinner9.start("Fetching projects\u2026");
1020
1194
  let projects;
1021
1195
  try {
1022
1196
  projects = await listProjects();
1023
- spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1197
+ spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1024
1198
  } catch (err) {
1025
- spinner8.stop("Failed to fetch projects.");
1026
- printApiError5(err);
1199
+ spinner9.stop("Failed to fetch projects.");
1200
+ printApiError6(err);
1027
1201
  process.exit(1);
1028
1202
  }
1029
1203
  if (projects.length === 0) {
1030
- p6.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1204
+ p7.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1031
1205
  process.exit(1);
1032
1206
  }
1033
- const selected = await p6.select({
1207
+ const selected = await p7.select({
1034
1208
  message: "Select a project",
1035
1209
  options: projects.map((proj) => ({
1036
1210
  value: proj.id,
@@ -1038,8 +1212,8 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1038
1212
  hint: proj.id
1039
1213
  }))
1040
1214
  });
1041
- if (p6.isCancel(selected)) {
1042
- p6.cancel("Run cancelled.");
1215
+ if (p7.isCancel(selected)) {
1216
+ p7.cancel("Run cancelled.");
1043
1217
  process.exit(0);
1044
1218
  }
1045
1219
  projectId = selected;
@@ -1049,7 +1223,7 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1049
1223
  let env;
1050
1224
  if (envArg) {
1051
1225
  if (!EnvironmentType.includes(envArg)) {
1052
- p6.log.error(
1226
+ p7.log.error(
1053
1227
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
1054
1228
  );
1055
1229
  process.exit(1);
@@ -1058,7 +1232,7 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1058
1232
  } else {
1059
1233
  const found = findProjectConfig();
1060
1234
  const defaultEnv = found?.config.defaultEnv;
1061
- const selected = await p6.select({
1235
+ const selected = await p7.select({
1062
1236
  message: "Select an environment",
1063
1237
  options: EnvironmentType.map((e) => ({
1064
1238
  value: e,
@@ -1067,25 +1241,25 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1067
1241
  })),
1068
1242
  initialValue: defaultEnv ?? "development"
1069
1243
  });
1070
- if (p6.isCancel(selected)) {
1071
- p6.cancel("Run cancelled.");
1244
+ if (p7.isCancel(selected)) {
1245
+ p7.cancel("Run cancelled.");
1072
1246
  process.exit(0);
1073
1247
  }
1074
1248
  env = selected;
1075
1249
  }
1076
- const spinner7 = p6.spinner();
1077
- spinner7.start(`Pulling ${chalk6.cyan(env)} secrets\u2026`);
1250
+ const spinner8 = p7.spinner();
1251
+ spinner8.start(`Pulling ${chalk7.cyan(env)} secrets\u2026`);
1078
1252
  let secrets;
1079
1253
  try {
1080
1254
  const result = await pullSecrets(projectId, env);
1081
1255
  secrets = result.secrets;
1082
1256
  projectTitle ??= result.project.title;
1083
- spinner7.stop(
1084
- `Injecting ${secrets.length} secret${secrets.length !== 1 ? "s" : ""} from ${chalk6.bold(projectTitle)} / ${chalk6.cyan(env)}.`
1257
+ spinner8.stop(
1258
+ `Injecting ${secrets.length} secret${secrets.length !== 1 ? "s" : ""} from ${chalk7.bold(projectTitle)} / ${chalk7.cyan(env)}.`
1085
1259
  );
1086
1260
  } catch (err) {
1087
- spinner7.stop("Failed to pull secrets.");
1088
- printApiError5(err);
1261
+ spinner8.stop("Failed to pull secrets.");
1262
+ printApiError6(err);
1089
1263
  process.exit(1);
1090
1264
  }
1091
1265
  const injectedEnv = {
@@ -1095,13 +1269,13 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1095
1269
  // shell env wins
1096
1270
  };
1097
1271
  if (secrets.length > 0) {
1098
- const keyList = secrets.map((s) => chalk6.dim(s.key)).join(", ");
1099
- p6.log.info(`Injecting: ${keyList}`);
1272
+ const keyList = secrets.map((s) => chalk7.dim(s.key)).join(", ");
1273
+ p7.log.info(`Injecting: ${keyList}`);
1100
1274
  } else {
1101
- p6.log.warn("No secrets found \u2014 running with current environment only.");
1275
+ p7.log.warn("No secrets found \u2014 running with current environment only.");
1102
1276
  }
1103
1277
  const [bin, ...args] = commandArgs;
1104
- p6.log.step(`${chalk6.bold("$")} ${chalk6.white([bin, ...args].join(" "))}`);
1278
+ p7.log.step(`${chalk7.bold("$")} ${chalk7.white([bin, ...args].join(" "))}`);
1105
1279
  process.stdout.write("");
1106
1280
  const child = spawn(bin, args, {
1107
1281
  env: injectedEnv,
@@ -1120,12 +1294,12 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1120
1294
  process.on("SIGHUP", () => forwardSignal("SIGHUP"));
1121
1295
  child.on("error", (err) => {
1122
1296
  if (err.code === "ENOENT") {
1123
- p6.log.error(
1124
- `Command not found: ${chalk6.bold(bin)}
1297
+ p7.log.error(
1298
+ `Command not found: ${chalk7.bold(bin)}
1125
1299
  Make sure it is installed and available in your PATH.`
1126
1300
  );
1127
1301
  } else {
1128
- p6.log.error(`Failed to start process: ${err.message}`);
1302
+ p7.log.error(`Failed to start process: ${err.message}`);
1129
1303
  }
1130
1304
  process.exit(1);
1131
1305
  });
@@ -1136,7 +1310,7 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1136
1310
  }
1137
1311
  const exitCode = code ?? 1;
1138
1312
  if (exitCode !== 0) {
1139
- p6.log.warn(`Process exited with code ${chalk6.bold(String(exitCode))}.`);
1313
+ p7.log.warn(`Process exited with code ${chalk7.bold(String(exitCode))}.`);
1140
1314
  }
1141
1315
  process.exit(exitCode);
1142
1316
  });
@@ -1166,19 +1340,19 @@ function signalToNumber(signal) {
1166
1340
  };
1167
1341
  return map[signal] ?? 0;
1168
1342
  }
1169
- function printApiError5(err) {
1343
+ function printApiError6(err) {
1170
1344
  if (err instanceof ApiError) {
1171
1345
  if (err.status === 401) {
1172
- p6.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1346
+ p7.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1173
1347
  } else if (err.status === 404) {
1174
- p6.log.error("Project or environment not found. Check the project ID and environment name.");
1348
+ p7.log.error("Project or environment not found. Check the project ID and environment name.");
1175
1349
  } else {
1176
- p6.log.error(`API error ${err.status}: ${err.message}`);
1350
+ p7.log.error(`API error ${err.status}: ${err.message}`);
1177
1351
  }
1178
1352
  } else if (err instanceof Error) {
1179
- p6.log.error(err.message);
1353
+ p7.log.error(err.message);
1180
1354
  } else {
1181
- p6.log.error("An unexpected error occurred.");
1355
+ p7.log.error("An unexpected error occurred.");
1182
1356
  }
1183
1357
  }
1184
1358
 
@@ -1193,14 +1367,14 @@ program.command("login").description("Authenticate with your Vaultsy instance an
1193
1367
  });
1194
1368
  program.command("logout").description("Remove locally stored credentials (~/.vaultsy/config.json)").action(async () => {
1195
1369
  const { clearConfig: clearConfig2, configExists: configExists2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1196
- const p7 = await import("@clack/prompts");
1197
- const chalk7 = (await import("chalk")).default;
1370
+ const p8 = await import("@clack/prompts");
1371
+ const chalk8 = (await import("chalk")).default;
1198
1372
  if (!configExists2()) {
1199
- p7.log.warn("No credentials found \u2014 already logged out.");
1373
+ p8.log.warn("No credentials found \u2014 already logged out.");
1200
1374
  return;
1201
1375
  }
1202
1376
  clearConfig2();
1203
- p7.log.success(chalk7.green("\u2713") + " Logged out. Credentials removed.");
1377
+ p8.log.success(chalk8.green("\u2713") + " Logged out. Credentials removed.");
1204
1378
  });
1205
1379
  program.command("pull [project] [env]").description("Pull secrets from Vaultsy and write them to a local .env file").option("-o, --output <file>", "Output file path (default: .env or .env.<env>)").option("-y, --yes", "Skip confirmation prompts").action(
1206
1380
  async (project, env, opts) => {
@@ -1212,6 +1386,12 @@ program.command("push [project] [env]").description("Push secrets from a local .
1212
1386
  await pushCommand(project, env, opts);
1213
1387
  }
1214
1388
  );
1389
+ program.command("envs [project]").description("Show secrets for a project across all environments (values hidden by default)").option(
1390
+ "-e, --env <env>",
1391
+ "Show only a specific environment (development, staging, preview, production)"
1392
+ ).option("-s, --show-values", "Reveal secret values in the output").action(async (project, opts) => {
1393
+ await envsCommand(project, opts);
1394
+ });
1215
1395
  program.command("history [project] [env]").description("List version snapshots for an environment").action(async (project, env) => {
1216
1396
  await historyCommand(project, env);
1217
1397
  });
@@ -1233,35 +1413,35 @@ program.command("run [project] [env]").description(
1233
1413
  program.command("init").description(
1234
1414
  "Create a vaultsy.json in the current directory to pin a project and default environment"
1235
1415
  ).action(async () => {
1236
- const p7 = await import("@clack/prompts");
1237
- const chalk7 = (await import("chalk")).default;
1416
+ const p8 = await import("@clack/prompts");
1417
+ const chalk8 = (await import("chalk")).default;
1238
1418
  const { listProjects: listProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
1239
1419
  const { writeProjectConfig: writeProjectConfig2, findProjectConfig: findProjectConfig2 } = await Promise.resolve().then(() => (init_env(), env_exports));
1240
- p7.intro(chalk7.bold.cyan("vaultsy init"));
1420
+ p8.intro(chalk8.bold.cyan("vaultsy init"));
1241
1421
  const existing = findProjectConfig2();
1242
1422
  if (existing) {
1243
- p7.log.warn(
1244
- `A ${chalk7.bold("vaultsy.json")} already exists at ${chalk7.dim(existing.dir)}.
1423
+ p8.log.warn(
1424
+ `A ${chalk8.bold("vaultsy.json")} already exists at ${chalk8.dim(existing.dir)}.
1245
1425
  Delete it first if you want to re-initialise.`
1246
1426
  );
1247
1427
  process.exit(0);
1248
1428
  }
1249
- const spinner7 = p7.spinner();
1250
- spinner7.start("Fetching your projects\u2026");
1429
+ const spinner8 = p8.spinner();
1430
+ spinner8.start("Fetching your projects\u2026");
1251
1431
  let projects;
1252
1432
  try {
1253
1433
  projects = await listProjects2();
1254
- spinner7.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1434
+ spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1255
1435
  } catch (err) {
1256
- spinner7.stop("Failed to fetch projects.");
1257
- if (err instanceof Error) p7.log.error(err.message);
1436
+ spinner8.stop("Failed to fetch projects.");
1437
+ if (err instanceof Error) p8.log.error(err.message);
1258
1438
  process.exit(1);
1259
1439
  }
1260
1440
  if (projects.length === 0) {
1261
- p7.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1441
+ p8.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1262
1442
  process.exit(1);
1263
1443
  }
1264
- const selectedProject = await p7.select({
1444
+ const selectedProject = await p8.select({
1265
1445
  message: "Which project does this directory belong to?",
1266
1446
  options: projects.map((proj) => ({
1267
1447
  value: proj.id,
@@ -1269,37 +1449,37 @@ program.command("init").description(
1269
1449
  hint: proj.id
1270
1450
  }))
1271
1451
  });
1272
- if (p7.isCancel(selectedProject)) {
1273
- p7.cancel("Init cancelled.");
1452
+ if (p8.isCancel(selectedProject)) {
1453
+ p8.cancel("Init cancelled.");
1274
1454
  process.exit(0);
1275
1455
  }
1276
- const selectedEnv = await p7.select({
1456
+ const selectedEnv = await p8.select({
1277
1457
  message: "Default environment for this directory?",
1278
1458
  options: EnvironmentType.map((e) => ({ value: e, label: e })),
1279
1459
  initialValue: "development"
1280
1460
  });
1281
- if (p7.isCancel(selectedEnv)) {
1282
- p7.cancel("Init cancelled.");
1461
+ if (p8.isCancel(selectedEnv)) {
1462
+ p8.cancel("Init cancelled.");
1283
1463
  process.exit(0);
1284
1464
  }
1285
1465
  writeProjectConfig2({ project: selectedProject, defaultEnv: selectedEnv });
1286
- p7.outro(
1287
- `${chalk7.green("\u2713")} Created ${chalk7.bold("vaultsy.json")}
1288
- Run ${chalk7.cyan("vaultsy pull")} or ${chalk7.cyan("vaultsy push")} with no arguments from this directory.`
1466
+ p8.outro(
1467
+ `${chalk8.green("\u2713")} Created ${chalk8.bold("vaultsy.json")}
1468
+ Run ${chalk8.cyan("vaultsy pull")} or ${chalk8.cyan("vaultsy push")} with no arguments from this directory.`
1289
1469
  );
1290
1470
  });
1291
1471
  program.command("whoami").description("Show the currently authenticated user").action(async () => {
1292
- const p7 = await import("@clack/prompts");
1293
- const chalk7 = (await import("chalk")).default;
1472
+ const p8 = await import("@clack/prompts");
1473
+ const chalk8 = (await import("chalk")).default;
1294
1474
  const { getMe: getMe2 } = await Promise.resolve().then(() => (init_api(), api_exports));
1295
1475
  try {
1296
1476
  const me = await getMe2();
1297
- p7.log.success(`Logged in as ${chalk7.bold(me.name)} ${chalk7.dim(`<${me.email}>`)}`);
1477
+ p8.log.success(`Logged in as ${chalk8.bold(me.name)} ${chalk8.dim(`<${me.email}>`)}`);
1298
1478
  } catch (err) {
1299
1479
  if (err instanceof Error) {
1300
- p7.log.error(err.message);
1480
+ p8.log.error(err.message);
1301
1481
  } else {
1302
- p7.log.error("Not authenticated. Run `vaultsy login` first.");
1482
+ p8.log.error("Not authenticated. Run `vaultsy login` first.");
1303
1483
  }
1304
1484
  process.exit(1);
1305
1485
  }