vaultsy-cli 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +29 -4
  2. package/dist/index.js +329 -248
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -24,16 +24,19 @@ bun add -g vaultsy-cli
24
24
  # 1. Authenticate (opens a token prompt — no URL needed)
25
25
  vaultsy login
26
26
 
27
- # 2. Pin a project to the current directory (optional but recommended)
27
+ # 2. Create a new project (or skip if you already have one)
28
+ vaultsy create
29
+
30
+ # 3. Pin a project to the current directory (optional but recommended)
28
31
  vaultsy init
29
32
 
30
- # 3. Pull secrets to a local .env file
33
+ # 4. Pull secrets to a local .env file
31
34
  vaultsy pull
32
35
 
33
- # 4. Push local changes back up
36
+ # 5. Push local changes back up
34
37
  vaultsy push
35
38
 
36
- # 5. Run a command with secrets injected — nothing ever touches disk
39
+ # 6. Run a command with secrets injected — nothing ever touches disk
37
40
  vaultsy run -- node server.js
38
41
  ```
39
42
 
@@ -94,6 +97,28 @@ vaultsy logout
94
97
 
95
98
  ---
96
99
 
100
+ ### `vaultsy create`
101
+
102
+ Create a new project on your Vaultsy instance.
103
+
104
+ ```sh
105
+ # Interactive prompt
106
+ vaultsy create
107
+
108
+ # With project title
109
+ vaultsy create --title "My Project"
110
+ ```
111
+
112
+ You'll be asked if you want to save the project config to `vaultsy.json` in the current directory. This is optional but recommended.
113
+
114
+ #### Options
115
+
116
+ | Flag | Description |
117
+ |---|---|
118
+ | `-t, --title <title>` | Project title (skips the interactive prompt) |
119
+
120
+ ---
121
+
97
122
  ### `vaultsy whoami`
98
123
 
99
124
  Show the currently authenticated user.
package/dist/index.js CHANGED
@@ -70,6 +70,7 @@ var init_config = __esm({
70
70
  var api_exports = {};
71
71
  __export(api_exports, {
72
72
  ApiError: () => ApiError,
73
+ createProject: () => createProject,
73
74
  getMe: () => getMe,
74
75
  listProjects: () => listProjects,
75
76
  listVersions: () => listVersions,
@@ -129,6 +130,12 @@ async function rollback(projectId, env, versionId) {
129
130
  body: JSON.stringify({ versionId })
130
131
  });
131
132
  }
133
+ async function createProject(title) {
134
+ return apiFetch("/api/v1/projects", {
135
+ method: "POST",
136
+ body: JSON.stringify({ title })
137
+ });
138
+ }
132
139
  var ApiError;
133
140
  var init_api = __esm({
134
141
  "src/lib/api.ts"() {
@@ -158,8 +165,8 @@ __export(env_exports, {
158
165
  });
159
166
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
160
167
  import { join as join2, resolve } from "path";
161
- function parseEnvText(text) {
162
- const lines = text.split(/\r?\n/);
168
+ function parseEnvText(text2) {
169
+ const lines = text2.split(/\r?\n/);
163
170
  const result = [];
164
171
  for (const rawLine of lines) {
165
172
  let line = rawLine.trim();
@@ -293,14 +300,14 @@ async function envsCommand(projectArg, opts) {
293
300
  projectId = found.config.project;
294
301
  p.log.info(`Using project ${chalk.cyan(projectId)} from ${chalk.dim("vaultsy.json")}`);
295
302
  } else {
296
- const spinner9 = p.spinner();
297
- spinner9.start("Fetching projects\u2026");
303
+ const spinner10 = p.spinner();
304
+ spinner10.start("Fetching projects\u2026");
298
305
  let projects;
299
306
  try {
300
307
  projects = await listProjects();
301
- spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
308
+ spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
302
309
  } catch (err) {
303
- spinner9.stop("Failed to fetch projects.");
310
+ spinner10.stop("Failed to fetch projects.");
304
311
  printApiError(err);
305
312
  process.exit(1);
306
313
  }
@@ -324,8 +331,8 @@ async function envsCommand(projectArg, opts) {
324
331
  projectTitle = projects.find((proj) => proj.id === selected)?.title;
325
332
  }
326
333
  }
327
- const spinner8 = p.spinner();
328
- spinner8.start(
334
+ const spinner9 = p.spinner();
335
+ spinner9.start(
329
336
  `Fetching ${envsToShow.length === 1 ? envsToShow[0] : "all"} environment${envsToShow.length !== 1 ? "s" : ""}\u2026`
330
337
  );
331
338
  let results;
@@ -342,9 +349,9 @@ async function envsCommand(projectArg, opts) {
342
349
  }
343
350
  })
344
351
  );
345
- spinner8.stop(`Loaded secrets for ${chalk.bold(projectTitle ?? projectId)}.`);
352
+ spinner9.stop(`Loaded secrets for ${chalk.bold(projectTitle ?? projectId)}.`);
346
353
  } catch (err) {
347
- spinner8.stop("Failed to fetch secrets.");
354
+ spinner9.stop("Failed to fetch secrets.");
348
355
  printApiError(err);
349
356
  process.exit(1);
350
357
  }
@@ -456,17 +463,17 @@ async function loginCommand(opts) {
456
463
  }
457
464
  token = input.trim();
458
465
  }
459
- const spinner8 = p2.spinner();
460
- spinner8.start("Verifying token\u2026");
466
+ const spinner9 = p2.spinner();
467
+ spinner9.start("Verifying token\u2026");
461
468
  let userName;
462
469
  let userEmail;
463
470
  try {
464
471
  const me = await getMe({ baseUrl, token });
465
472
  userName = me.name;
466
473
  userEmail = me.email;
467
- spinner8.stop("Token verified.");
474
+ spinner9.stop("Token verified.");
468
475
  } catch (err) {
469
- spinner8.stop("Verification failed.");
476
+ spinner9.stop("Verification failed.");
470
477
  if (err instanceof ApiError) {
471
478
  if (err.status === 401) {
472
479
  p2.log.error("Invalid or expired token. Generate a new one and try again.");
@@ -485,14 +492,85 @@ async function loginCommand(opts) {
485
492
  );
486
493
  }
487
494
 
488
- // src/commands/pull.ts
495
+ // src/commands/create.ts
496
+ init_api();
497
+ init_env();
489
498
  import * as p3 from "@clack/prompts";
490
499
  import chalk3 from "chalk";
500
+ async function createCommand(opts) {
501
+ p3.intro(chalk3.bold.cyan("vaultsy create"));
502
+ let title;
503
+ if (opts.title) {
504
+ title = opts.title;
505
+ } else {
506
+ const input = await p3.text({
507
+ message: "Project title",
508
+ placeholder: "My Secret Project",
509
+ validate(value) {
510
+ if (!value.trim()) return "Title is required.";
511
+ if (value.trim().length > 100) return "Title must be less than 100 characters.";
512
+ }
513
+ });
514
+ if (p3.isCancel(input)) {
515
+ p3.cancel("Project creation cancelled.");
516
+ process.exit(0);
517
+ }
518
+ title = input.trim();
519
+ }
520
+ const spinner9 = p3.spinner();
521
+ spinner9.start("Creating project\u2026");
522
+ let projectId;
523
+ let projectTitle;
524
+ try {
525
+ const result = await createProject(title);
526
+ projectId = result.project.id;
527
+ projectTitle = result.project.title;
528
+ spinner9.stop("Project created.");
529
+ } catch (err) {
530
+ spinner9.stop("Project creation failed.");
531
+ if (err instanceof ApiError) {
532
+ if (err.status === 401) {
533
+ p3.log.error("Unauthorized. Run `vaultsy login` to authenticate first.");
534
+ } else if (err.status === 400) {
535
+ p3.log.error(`Invalid input: ${err.message}`);
536
+ } else {
537
+ p3.log.error(`Server responded with ${err.status}: ${err.message}`);
538
+ }
539
+ } else if (err instanceof Error) {
540
+ p3.log.error(err.message);
541
+ } else {
542
+ p3.log.error("An unexpected error occurred.");
543
+ }
544
+ process.exit(1);
545
+ }
546
+ const saveConfig = await p3.confirm({
547
+ message: `Save project config to ${chalk3.dim("vaultsy.json")} in the current directory?`,
548
+ initialValue: true
549
+ });
550
+ if (!p3.isCancel(saveConfig) && saveConfig) {
551
+ writeProjectConfig({ project: projectId, defaultEnv: "development" });
552
+ p3.outro(
553
+ `${chalk3.green("\u2713")} Project ${chalk3.bold(projectTitle)} created!
554
+ Project ID: ${chalk3.cyan(projectId)}
555
+ Config saved to ${chalk3.dim("vaultsy.json")}`
556
+ );
557
+ } else {
558
+ p3.outro(
559
+ `${chalk3.green("\u2713")} Project ${chalk3.bold(projectTitle)} created!
560
+ Project ID: ${chalk3.cyan(projectId)}
561
+ Run ${chalk3.cyan("vaultsy init")} to create a config file.`
562
+ );
563
+ }
564
+ }
565
+
566
+ // src/commands/pull.ts
567
+ import * as p4 from "@clack/prompts";
568
+ import chalk4 from "chalk";
491
569
  import { resolve as resolve2 } from "path";
492
570
  init_api();
493
571
  init_env();
494
572
  async function pullCommand(projectArg, envArg, opts) {
495
- p3.intro(chalk3.bold.cyan("vaultsy pull"));
573
+ p4.intro(chalk4.bold.cyan("vaultsy pull"));
496
574
  let projectId;
497
575
  let projectTitle;
498
576
  if (projectArg) {
@@ -501,24 +579,24 @@ async function pullCommand(projectArg, envArg, opts) {
501
579
  const found = findProjectConfig();
502
580
  if (found) {
503
581
  projectId = found.config.project;
504
- p3.log.info(`Using project ${chalk3.cyan(projectId)} from ${chalk3.dim("vaultsy.json")}`);
582
+ p4.log.info(`Using project ${chalk4.cyan(projectId)} from ${chalk4.dim("vaultsy.json")}`);
505
583
  } else {
506
- const spinner9 = p3.spinner();
507
- spinner9.start("Fetching projects\u2026");
584
+ const spinner10 = p4.spinner();
585
+ spinner10.start("Fetching projects\u2026");
508
586
  let projects;
509
587
  try {
510
588
  projects = await listProjects();
511
- spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
589
+ spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
512
590
  } catch (err) {
513
- spinner9.stop("Failed to fetch projects.");
591
+ spinner10.stop("Failed to fetch projects.");
514
592
  printApiError2(err);
515
593
  process.exit(1);
516
594
  }
517
595
  if (projects.length === 0) {
518
- p3.log.error("No projects found. Create one at your Vaultsy dashboard first.");
596
+ p4.log.error("No projects found. Create one at your Vaultsy dashboard first.");
519
597
  process.exit(1);
520
598
  }
521
- const selected = await p3.select({
599
+ const selected = await p4.select({
522
600
  message: "Select a project",
523
601
  options: projects.map((proj) => ({
524
602
  value: proj.id,
@@ -526,8 +604,8 @@ async function pullCommand(projectArg, envArg, opts) {
526
604
  hint: proj.id
527
605
  }))
528
606
  });
529
- if (p3.isCancel(selected)) {
530
- p3.cancel("Pull cancelled.");
607
+ if (p4.isCancel(selected)) {
608
+ p4.cancel("Pull cancelled.");
531
609
  process.exit(0);
532
610
  }
533
611
  projectId = selected;
@@ -537,7 +615,7 @@ async function pullCommand(projectArg, envArg, opts) {
537
615
  let env;
538
616
  if (envArg) {
539
617
  if (!EnvironmentType.includes(envArg)) {
540
- p3.log.error(
618
+ p4.log.error(
541
619
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
542
620
  );
543
621
  process.exit(1);
@@ -546,7 +624,7 @@ async function pullCommand(projectArg, envArg, opts) {
546
624
  } else {
547
625
  const found = findProjectConfig();
548
626
  const defaultEnv = found?.config.defaultEnv;
549
- const selected = await p3.select({
627
+ const selected = await p4.select({
550
628
  message: "Select an environment",
551
629
  options: EnvironmentType.map((e) => ({
552
630
  value: e,
@@ -555,8 +633,8 @@ async function pullCommand(projectArg, envArg, opts) {
555
633
  })),
556
634
  initialValue: defaultEnv ?? "development"
557
635
  });
558
- if (p3.isCancel(selected)) {
559
- p3.cancel("Pull cancelled.");
636
+ if (p4.isCancel(selected)) {
637
+ p4.cancel("Pull cancelled.");
560
638
  process.exit(0);
561
639
  }
562
640
  env = selected;
@@ -564,68 +642,68 @@ async function pullCommand(projectArg, envArg, opts) {
564
642
  const filename = opts.output ?? envFileName(env);
565
643
  const outputPath = resolve2(process.cwd(), filename);
566
644
  if (!opts.yes && !isGitIgnored(filename)) {
567
- p3.log.warn(
568
- `${chalk3.yellow(filename)} does not appear to be in ${chalk3.dim(".gitignore")}.
645
+ p4.log.warn(
646
+ `${chalk4.yellow(filename)} does not appear to be in ${chalk4.dim(".gitignore")}.
569
647
  Make sure you don't accidentally commit secrets to version control.`
570
648
  );
571
- const confirmed = await p3.confirm({
649
+ const confirmed = await p4.confirm({
572
650
  message: "Continue anyway?",
573
651
  initialValue: false
574
652
  });
575
- if (p3.isCancel(confirmed) || !confirmed) {
576
- p3.cancel("Pull cancelled.");
653
+ if (p4.isCancel(confirmed) || !confirmed) {
654
+ p4.cancel("Pull cancelled.");
577
655
  process.exit(0);
578
656
  }
579
657
  }
580
- const spinner8 = p3.spinner();
581
- spinner8.start(`Pulling ${chalk3.cyan(env)} secrets\u2026`);
658
+ const spinner9 = p4.spinner();
659
+ spinner9.start(`Pulling ${chalk4.cyan(env)} secrets\u2026`);
582
660
  let result;
583
661
  try {
584
662
  result = await pullSecrets(projectId, env);
585
- spinner8.stop(
586
- `Pulled ${result.secrets.length} secret${result.secrets.length !== 1 ? "s" : ""} from ${chalk3.bold(projectTitle ?? result.project.title)} / ${chalk3.cyan(env)}.`
663
+ spinner9.stop(
664
+ `Pulled ${result.secrets.length} secret${result.secrets.length !== 1 ? "s" : ""} from ${chalk4.bold(projectTitle ?? result.project.title)} / ${chalk4.cyan(env)}.`
587
665
  );
588
666
  } catch (err) {
589
- spinner8.stop("Pull failed.");
667
+ spinner9.stop("Pull failed.");
590
668
  printApiError2(err);
591
669
  process.exit(1);
592
670
  }
593
671
  if (result.secrets.length === 0) {
594
- p3.log.warn(`No secrets found for the ${chalk3.cyan(env)} environment.`);
595
- p3.outro(chalk3.dim("Nothing written."));
672
+ p4.log.warn(`No secrets found for the ${chalk4.cyan(env)} environment.`);
673
+ p4.outro(chalk4.dim("Nothing written."));
596
674
  return;
597
675
  }
598
676
  writeEnvFile(outputPath, result.secrets);
599
- p3.outro(
600
- `${chalk3.green("\u2713")} Written to ${chalk3.bold(filename)}
601
- ${chalk3.dim(outputPath)}`
677
+ p4.outro(
678
+ `${chalk4.green("\u2713")} Written to ${chalk4.bold(filename)}
679
+ ${chalk4.dim(outputPath)}`
602
680
  );
603
681
  }
604
682
  function printApiError2(err) {
605
683
  if (err instanceof ApiError) {
606
684
  if (err.status === 401) {
607
- p3.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
685
+ p4.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
608
686
  } else if (err.status === 404) {
609
- p3.log.error("Project or environment not found. Check the project ID and environment name.");
687
+ p4.log.error("Project or environment not found. Check the project ID and environment name.");
610
688
  } else {
611
- p3.log.error(`API error ${err.status}: ${err.message}`);
689
+ p4.log.error(`API error ${err.status}: ${err.message}`);
612
690
  }
613
691
  } else if (err instanceof Error) {
614
- p3.log.error(err.message);
692
+ p4.log.error(err.message);
615
693
  } else {
616
- p3.log.error("An unexpected error occurred.");
694
+ p4.log.error("An unexpected error occurred.");
617
695
  }
618
696
  }
619
697
 
620
698
  // src/commands/push.ts
621
- import * as p4 from "@clack/prompts";
622
- import chalk4 from "chalk";
699
+ import * as p5 from "@clack/prompts";
700
+ import chalk5 from "chalk";
623
701
  import { resolve as resolve3 } from "path";
624
702
  import { existsSync as existsSync3 } from "fs";
625
703
  init_api();
626
704
  init_env();
627
705
  async function pushCommand(projectArg, envArg, opts) {
628
- p4.intro(chalk4.bold.cyan("vaultsy push"));
706
+ p5.intro(chalk5.bold.cyan("vaultsy push"));
629
707
  let projectId;
630
708
  let projectTitle;
631
709
  if (projectArg) {
@@ -634,24 +712,24 @@ async function pushCommand(projectArg, envArg, opts) {
634
712
  const found = findProjectConfig();
635
713
  if (found) {
636
714
  projectId = found.config.project;
637
- p4.log.info(`Using project ${chalk4.cyan(projectId)} from ${chalk4.dim("vaultsy.json")}`);
715
+ p5.log.info(`Using project ${chalk5.cyan(projectId)} from ${chalk5.dim("vaultsy.json")}`);
638
716
  } else {
639
- const spinner8 = p4.spinner();
640
- spinner8.start("Fetching projects\u2026");
717
+ const spinner9 = p5.spinner();
718
+ spinner9.start("Fetching projects\u2026");
641
719
  let projects;
642
720
  try {
643
721
  projects = await listProjects();
644
- spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
722
+ spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
645
723
  } catch (err) {
646
- spinner8.stop("Failed to fetch projects.");
724
+ spinner9.stop("Failed to fetch projects.");
647
725
  printApiError3(err);
648
726
  process.exit(1);
649
727
  }
650
728
  if (projects.length === 0) {
651
- p4.log.error("No projects found. Create one at your Vaultsy dashboard first.");
729
+ p5.log.error("No projects found. Create one at your Vaultsy dashboard first.");
652
730
  process.exit(1);
653
731
  }
654
- const selected = await p4.select({
732
+ const selected = await p5.select({
655
733
  message: "Select a project",
656
734
  options: projects.map((proj) => ({
657
735
  value: proj.id,
@@ -659,8 +737,8 @@ async function pushCommand(projectArg, envArg, opts) {
659
737
  hint: proj.id
660
738
  }))
661
739
  });
662
- if (p4.isCancel(selected)) {
663
- p4.cancel("Push cancelled.");
740
+ if (p5.isCancel(selected)) {
741
+ p5.cancel("Push cancelled.");
664
742
  process.exit(0);
665
743
  }
666
744
  projectId = selected;
@@ -670,7 +748,7 @@ async function pushCommand(projectArg, envArg, opts) {
670
748
  let env;
671
749
  if (envArg) {
672
750
  if (!EnvironmentType.includes(envArg)) {
673
- p4.log.error(
751
+ p5.log.error(
674
752
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
675
753
  );
676
754
  process.exit(1);
@@ -679,7 +757,7 @@ async function pushCommand(projectArg, envArg, opts) {
679
757
  } else {
680
758
  const found = findProjectConfig();
681
759
  const defaultEnv = found?.config.defaultEnv;
682
- const selected = await p4.select({
760
+ const selected = await p5.select({
683
761
  message: "Select an environment",
684
762
  options: EnvironmentType.map((e) => ({
685
763
  value: e,
@@ -688,8 +766,8 @@ async function pushCommand(projectArg, envArg, opts) {
688
766
  })),
689
767
  initialValue: defaultEnv ?? "development"
690
768
  });
691
- if (p4.isCancel(selected)) {
692
- p4.cancel("Push cancelled.");
769
+ if (p5.isCancel(selected)) {
770
+ p5.cancel("Push cancelled.");
693
771
  process.exit(0);
694
772
  }
695
773
  env = selected;
@@ -697,22 +775,22 @@ async function pushCommand(projectArg, envArg, opts) {
697
775
  const filename = opts.input ?? envFileName(env);
698
776
  const inputPath = resolve3(process.cwd(), filename);
699
777
  if (!existsSync3(inputPath)) {
700
- p4.log.error(
701
- `File ${chalk4.bold(filename)} not found.
702
- Run ${chalk4.cyan(`vaultsy pull ${projectId} ${env}`)} first, or specify a file with ${chalk4.dim("--input <file>")}.`
778
+ p5.log.error(
779
+ `File ${chalk5.bold(filename)} not found.
780
+ Run ${chalk5.cyan(`vaultsy pull ${projectId} ${env}`)} first, or specify a file with ${chalk5.dim("--input <file>")}.`
703
781
  );
704
782
  process.exit(1);
705
783
  }
706
784
  const localSecrets = readEnvFile(inputPath).filter((r) => r.key && r.value);
707
785
  if (localSecrets.length === 0) {
708
- p4.log.warn(`${chalk4.bold(filename)} is empty or contains no valid KEY=VALUE pairs.`);
709
- p4.outro(chalk4.dim("Nothing pushed."));
786
+ p5.log.warn(`${chalk5.bold(filename)} is empty or contains no valid KEY=VALUE pairs.`);
787
+ p5.outro(chalk5.dim("Nothing pushed."));
710
788
  return;
711
789
  }
712
- p4.log.info(
713
- `Read ${chalk4.bold(String(localSecrets.length))} secret${localSecrets.length !== 1 ? "s" : ""} from ${chalk4.bold(filename)}.`
790
+ p5.log.info(
791
+ `Read ${chalk5.bold(String(localSecrets.length))} secret${localSecrets.length !== 1 ? "s" : ""} from ${chalk5.bold(filename)}.`
714
792
  );
715
- const diffSpinner = p4.spinner();
793
+ const diffSpinner = p5.spinner();
716
794
  diffSpinner.start("Computing diff against remote\u2026");
717
795
  let remoteSecrets;
718
796
  let resolvedTitle;
@@ -730,34 +808,34 @@ async function pushCommand(projectArg, envArg, opts) {
730
808
  printDiff(diff);
731
809
  const hasChanges = diff.added.length > 0 || diff.modified.length > 0 || diff.removed.length > 0;
732
810
  if (!hasChanges) {
733
- p4.outro(`${chalk4.dim("No changes.")} Remote ${chalk4.cyan(env)} is already up to date.`);
811
+ p5.outro(`${chalk5.dim("No changes.")} Remote ${chalk5.cyan(env)} is already up to date.`);
734
812
  return;
735
813
  }
736
814
  if (!opts.yes) {
737
- const confirmed = await p4.confirm({
738
- message: `Push these changes to ${chalk4.bold(resolvedTitle)} / ${chalk4.cyan(env)}?`,
815
+ const confirmed = await p5.confirm({
816
+ message: `Push these changes to ${chalk5.bold(resolvedTitle)} / ${chalk5.cyan(env)}?`,
739
817
  initialValue: true
740
818
  });
741
- if (p4.isCancel(confirmed) || !confirmed) {
742
- p4.cancel("Push cancelled.");
819
+ if (p5.isCancel(confirmed) || !confirmed) {
820
+ p5.cancel("Push cancelled.");
743
821
  process.exit(0);
744
822
  }
745
823
  }
746
- const pushSpinner = p4.spinner();
747
- pushSpinner.start(`Pushing to ${chalk4.cyan(env)}\u2026`);
824
+ const pushSpinner = p5.spinner();
825
+ pushSpinner.start(`Pushing to ${chalk5.cyan(env)}\u2026`);
748
826
  try {
749
827
  const result = await pushSecrets(projectId, env, localSecrets);
750
828
  const { added, modified, removed, unchanged } = result.changes;
751
829
  pushSpinner.stop(
752
- `Done. ${chalk4.green(`+${added}`)} added, ${chalk4.yellow(`~${modified}`)} modified, ${chalk4.red(`-${removed}`)} removed, ${chalk4.dim(`${unchanged} unchanged`)}.`
830
+ `Done. ${chalk5.green(`+${added}`)} added, ${chalk5.yellow(`~${modified}`)} modified, ${chalk5.red(`-${removed}`)} removed, ${chalk5.dim(`${unchanged} unchanged`)}.`
753
831
  );
754
832
  } catch (err) {
755
833
  pushSpinner.stop("Push failed.");
756
834
  printApiError3(err);
757
835
  process.exit(1);
758
836
  }
759
- p4.outro(
760
- `${chalk4.green("\u2713")} ${chalk4.bold(resolvedTitle)} / ${chalk4.cyan(env)} updated successfully.`
837
+ p5.outro(
838
+ `${chalk5.green("\u2713")} ${chalk5.bold(resolvedTitle)} / ${chalk5.cyan(env)} updated successfully.`
761
839
  );
762
840
  }
763
841
  function computeDiff(remote, local) {
@@ -790,54 +868,54 @@ function computeDiff(remote, local) {
790
868
  function printDiff(diff) {
791
869
  const total = diff.added.length + diff.modified.length + diff.removed.length + diff.unchanged.length;
792
870
  if (total === 0) {
793
- p4.log.info(chalk4.dim("No secrets on remote or local."));
871
+ p5.log.info(chalk5.dim("No secrets on remote or local."));
794
872
  return;
795
873
  }
796
874
  const lines = [];
797
875
  for (const key of diff.added) {
798
- lines.push(` ${chalk4.green("+")} ${chalk4.green(key)}`);
876
+ lines.push(` ${chalk5.green("+")} ${chalk5.green(key)}`);
799
877
  }
800
878
  for (const key of diff.modified) {
801
- lines.push(` ${chalk4.yellow("~")} ${chalk4.yellow(key)}`);
879
+ lines.push(` ${chalk5.yellow("~")} ${chalk5.yellow(key)}`);
802
880
  }
803
881
  for (const key of diff.removed) {
804
- lines.push(` ${chalk4.red("-")} ${chalk4.red(key)}`);
882
+ lines.push(` ${chalk5.red("-")} ${chalk5.red(key)}`);
805
883
  }
806
884
  for (const key of diff.unchanged) {
807
- lines.push(` ${chalk4.dim("\xB7")} ${chalk4.dim(key)}`);
885
+ lines.push(` ${chalk5.dim("\xB7")} ${chalk5.dim(key)}`);
808
886
  }
809
- p4.log.message(lines.join("\n"));
887
+ p5.log.message(lines.join("\n"));
810
888
  const summary = [
811
- diff.added.length > 0 ? chalk4.green(`+${diff.added.length} to add`) : null,
812
- diff.modified.length > 0 ? chalk4.yellow(`~${diff.modified.length} to modify`) : null,
813
- diff.removed.length > 0 ? chalk4.red(`-${diff.removed.length} to remove`) : null,
814
- diff.unchanged.length > 0 ? chalk4.dim(`${diff.unchanged.length} unchanged`) : null
815
- ].filter(Boolean).join(chalk4.dim(", "));
816
- p4.log.info(summary);
889
+ diff.added.length > 0 ? chalk5.green(`+${diff.added.length} to add`) : null,
890
+ diff.modified.length > 0 ? chalk5.yellow(`~${diff.modified.length} to modify`) : null,
891
+ diff.removed.length > 0 ? chalk5.red(`-${diff.removed.length} to remove`) : null,
892
+ diff.unchanged.length > 0 ? chalk5.dim(`${diff.unchanged.length} unchanged`) : null
893
+ ].filter(Boolean).join(chalk5.dim(", "));
894
+ p5.log.info(summary);
817
895
  }
818
896
  function printApiError3(err) {
819
897
  if (err instanceof ApiError) {
820
898
  if (err.status === 401) {
821
- p4.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
899
+ p5.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
822
900
  } else if (err.status === 404) {
823
- p4.log.error("Project or environment not found. Check the project ID and environment name.");
901
+ p5.log.error("Project or environment not found. Check the project ID and environment name.");
824
902
  } else {
825
- p4.log.error(`API error ${err.status}: ${err.message}`);
903
+ p5.log.error(`API error ${err.status}: ${err.message}`);
826
904
  }
827
905
  } else if (err instanceof Error) {
828
- p4.log.error(err.message);
906
+ p5.log.error(err.message);
829
907
  } else {
830
- p4.log.error("An unexpected error occurred.");
908
+ p5.log.error("An unexpected error occurred.");
831
909
  }
832
910
  }
833
911
 
834
912
  // src/commands/history.ts
835
- import * as p5 from "@clack/prompts";
836
- import chalk5 from "chalk";
913
+ import * as p6 from "@clack/prompts";
914
+ import chalk6 from "chalk";
837
915
  init_api();
838
916
  init_env();
839
917
  async function historyCommand(projectArg, envArg) {
840
- p5.intro(chalk5.bold.cyan("vaultsy history"));
918
+ p6.intro(chalk6.bold.cyan("vaultsy history"));
841
919
  let projectId;
842
920
  let projectTitle;
843
921
  if (projectArg) {
@@ -846,24 +924,24 @@ async function historyCommand(projectArg, envArg) {
846
924
  const found = findProjectConfig();
847
925
  if (found) {
848
926
  projectId = found.config.project;
849
- p5.log.info(`Using project ${chalk5.cyan(projectId)} from ${chalk5.dim("vaultsy.json")}`);
927
+ p6.log.info(`Using project ${chalk6.cyan(projectId)} from ${chalk6.dim("vaultsy.json")}`);
850
928
  } else {
851
- const spinner9 = p5.spinner();
852
- spinner9.start("Fetching projects\u2026");
929
+ const spinner10 = p6.spinner();
930
+ spinner10.start("Fetching projects\u2026");
853
931
  let projects;
854
932
  try {
855
933
  projects = await listProjects();
856
- spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
934
+ spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
857
935
  } catch (err) {
858
- spinner9.stop("Failed to fetch projects.");
936
+ spinner10.stop("Failed to fetch projects.");
859
937
  printApiError4(err);
860
938
  process.exit(1);
861
939
  }
862
940
  if (projects.length === 0) {
863
- p5.log.error("No projects found. Create one at your Vaultsy dashboard first.");
941
+ p6.log.error("No projects found. Create one at your Vaultsy dashboard first.");
864
942
  process.exit(1);
865
943
  }
866
- const selected = await p5.select({
944
+ const selected = await p6.select({
867
945
  message: "Select a project",
868
946
  options: projects.map((proj) => ({
869
947
  value: proj.id,
@@ -871,8 +949,8 @@ async function historyCommand(projectArg, envArg) {
871
949
  hint: proj.id
872
950
  }))
873
951
  });
874
- if (p5.isCancel(selected)) {
875
- p5.cancel("Cancelled.");
952
+ if (p6.isCancel(selected)) {
953
+ p6.cancel("Cancelled.");
876
954
  process.exit(0);
877
955
  }
878
956
  projectId = selected;
@@ -882,7 +960,7 @@ async function historyCommand(projectArg, envArg) {
882
960
  let env;
883
961
  if (envArg) {
884
962
  if (!EnvironmentType.includes(envArg)) {
885
- p5.log.error(
963
+ p6.log.error(
886
964
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
887
965
  );
888
966
  process.exit(1);
@@ -891,7 +969,7 @@ async function historyCommand(projectArg, envArg) {
891
969
  } else {
892
970
  const found = findProjectConfig();
893
971
  const defaultEnv = found?.config.defaultEnv;
894
- const selected = await p5.select({
972
+ const selected = await p6.select({
895
973
  message: "Select an environment",
896
974
  options: EnvironmentType.map((e) => ({
897
975
  value: e,
@@ -900,54 +978,54 @@ async function historyCommand(projectArg, envArg) {
900
978
  })),
901
979
  initialValue: defaultEnv ?? "development"
902
980
  });
903
- if (p5.isCancel(selected)) {
904
- p5.cancel("Cancelled.");
981
+ if (p6.isCancel(selected)) {
982
+ p6.cancel("Cancelled.");
905
983
  process.exit(0);
906
984
  }
907
985
  env = selected;
908
986
  }
909
- const spinner8 = p5.spinner();
910
- spinner8.start(`Fetching history for ${chalk5.cyan(env)}\u2026`);
987
+ const spinner9 = p6.spinner();
988
+ spinner9.start(`Fetching history for ${chalk6.cyan(env)}\u2026`);
911
989
  let result;
912
990
  try {
913
991
  result = await listVersions(projectId, env);
914
- spinner8.stop(
915
- `${result.versions.length} snapshot${result.versions.length !== 1 ? "s" : ""} for ${chalk5.bold(projectTitle ?? result.project.title)} / ${chalk5.cyan(env)}.`
992
+ spinner9.stop(
993
+ `${result.versions.length} snapshot${result.versions.length !== 1 ? "s" : ""} for ${chalk6.bold(projectTitle ?? result.project.title)} / ${chalk6.cyan(env)}.`
916
994
  );
917
995
  } catch (err) {
918
- spinner8.stop("Failed to fetch history.");
996
+ spinner9.stop("Failed to fetch history.");
919
997
  printApiError4(err);
920
998
  process.exit(1);
921
999
  }
922
1000
  if (result.versions.length === 0) {
923
- p5.log.warn(`No version history found for the ${chalk5.cyan(env)} environment.`);
924
- p5.outro(chalk5.dim("Nothing to show."));
1001
+ p6.log.warn(`No version history found for the ${chalk6.cyan(env)} environment.`);
1002
+ p6.outro(chalk6.dim("Nothing to show."));
925
1003
  return;
926
1004
  }
927
1005
  const COL_VER = 7;
928
1006
  const COL_SECRETS = 7;
929
1007
  const COL_BY = 20;
930
1008
  const COL_DATE = 22;
931
- 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));
932
- const divider = chalk5.dim(
1009
+ const header = chalk6.bold(padEnd2("#", COL_VER)) + chalk6.dim(" \u2502 ") + chalk6.bold(padEnd2("VERSION ID", 26)) + chalk6.dim(" \u2502 ") + chalk6.bold(padEnd2("KEYS", COL_SECRETS)) + chalk6.dim(" \u2502 ") + chalk6.bold(padEnd2("CREATED BY", COL_BY)) + chalk6.dim(" \u2502 ") + chalk6.bold(padEnd2("DATE", COL_DATE));
1010
+ const divider = chalk6.dim(
933
1011
  "\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)
934
1012
  );
935
1013
  const rows = result.versions.map((v, i) => {
936
1014
  const isLatest = i === 0;
937
- const vNum = isLatest ? chalk5.green(padEnd2(`v${v.versionNumber}`, COL_VER)) : chalk5.dim(padEnd2(`v${v.versionNumber}`, COL_VER));
938
- const vId = chalk5.dim(padEnd2(v.id, 26));
1015
+ const vNum = isLatest ? chalk6.green(padEnd2(`v${v.versionNumber}`, COL_VER)) : chalk6.dim(padEnd2(`v${v.versionNumber}`, COL_VER));
1016
+ const vId = chalk6.dim(padEnd2(v.id, 26));
939
1017
  const secrets = padEnd2(String(v.secretCount), COL_SECRETS);
940
- const by = padEnd2(v.createdBy?.name ?? chalk5.italic("system"), COL_BY);
1018
+ const by = padEnd2(v.createdBy?.name ?? chalk6.italic("system"), COL_BY);
941
1019
  const date = padEnd2(formatDate(v.createdAt), COL_DATE);
942
- const latestBadge = isLatest ? chalk5.green(" \u2190 latest") : "";
943
- return vNum + chalk5.dim(" \u2502 ") + vId + chalk5.dim(" \u2502 ") + secrets + chalk5.dim(" \u2502 ") + by + chalk5.dim(" \u2502 ") + date + latestBadge;
1020
+ const latestBadge = isLatest ? chalk6.green(" \u2190 latest") : "";
1021
+ return vNum + chalk6.dim(" \u2502 ") + vId + chalk6.dim(" \u2502 ") + secrets + chalk6.dim(" \u2502 ") + by + chalk6.dim(" \u2502 ") + date + latestBadge;
944
1022
  });
945
1023
  const lines = [header, divider, ...rows];
946
- p5.log.message(lines.join("\n"));
947
- p5.log.info(
948
- `To rollback, run: ${chalk5.cyan(`vaultsy rollback ${projectId} ${env} <VERSION_ID>`)}`
1024
+ p6.log.message(lines.join("\n"));
1025
+ p6.log.info(
1026
+ `To rollback, run: ${chalk6.cyan(`vaultsy rollback ${projectId} ${env} <VERSION_ID>`)}`
949
1027
  );
950
- p5.outro(chalk5.dim("Done."));
1028
+ p6.outro(chalk6.dim("Done."));
951
1029
  }
952
1030
  var ANSI_REGEX2 = new RegExp("\x1B\\[[0-9;]*m", "g");
953
1031
  function padEnd2(str, length) {
@@ -971,26 +1049,26 @@ function formatDate(iso) {
971
1049
  function printApiError4(err) {
972
1050
  if (err instanceof ApiError) {
973
1051
  if (err.status === 401) {
974
- p5.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1052
+ p6.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
975
1053
  } else if (err.status === 404) {
976
- p5.log.error("Project or environment not found. Check the project ID and environment name.");
1054
+ p6.log.error("Project or environment not found. Check the project ID and environment name.");
977
1055
  } else {
978
- p5.log.error(`API error ${err.status}: ${err.message}`);
1056
+ p6.log.error(`API error ${err.status}: ${err.message}`);
979
1057
  }
980
1058
  } else if (err instanceof Error) {
981
- p5.log.error(err.message);
1059
+ p6.log.error(err.message);
982
1060
  } else {
983
- p5.log.error("An unexpected error occurred.");
1061
+ p6.log.error("An unexpected error occurred.");
984
1062
  }
985
1063
  }
986
1064
 
987
1065
  // src/commands/rollback.ts
988
- import * as p6 from "@clack/prompts";
989
- import chalk6 from "chalk";
1066
+ import * as p7 from "@clack/prompts";
1067
+ import chalk7 from "chalk";
990
1068
  init_api();
991
1069
  init_env();
992
1070
  async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
993
- p6.intro(chalk6.bold.cyan("vaultsy rollback"));
1071
+ p7.intro(chalk7.bold.cyan("vaultsy rollback"));
994
1072
  let projectId;
995
1073
  let projectTitle;
996
1074
  if (projectArg) {
@@ -999,24 +1077,24 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
999
1077
  const found = findProjectConfig();
1000
1078
  if (found) {
1001
1079
  projectId = found.config.project;
1002
- p6.log.info(`Using project ${chalk6.cyan(projectId)} from ${chalk6.dim("vaultsy.json")}`);
1080
+ p7.log.info(`Using project ${chalk7.cyan(projectId)} from ${chalk7.dim("vaultsy.json")}`);
1003
1081
  } else {
1004
- const spinner9 = p6.spinner();
1005
- spinner9.start("Fetching projects\u2026");
1082
+ const spinner10 = p7.spinner();
1083
+ spinner10.start("Fetching projects\u2026");
1006
1084
  let projects;
1007
1085
  try {
1008
1086
  projects = await listProjects();
1009
- spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1087
+ spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1010
1088
  } catch (err) {
1011
- spinner9.stop("Failed to fetch projects.");
1089
+ spinner10.stop("Failed to fetch projects.");
1012
1090
  printApiError5(err);
1013
1091
  process.exit(1);
1014
1092
  }
1015
1093
  if (projects.length === 0) {
1016
- p6.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1094
+ p7.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1017
1095
  process.exit(1);
1018
1096
  }
1019
- const selected = await p6.select({
1097
+ const selected = await p7.select({
1020
1098
  message: "Select a project",
1021
1099
  options: projects.map((proj) => ({
1022
1100
  value: proj.id,
@@ -1024,8 +1102,8 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
1024
1102
  hint: proj.id
1025
1103
  }))
1026
1104
  });
1027
- if (p6.isCancel(selected)) {
1028
- p6.cancel("Rollback cancelled.");
1105
+ if (p7.isCancel(selected)) {
1106
+ p7.cancel("Rollback cancelled.");
1029
1107
  process.exit(0);
1030
1108
  }
1031
1109
  projectId = selected;
@@ -1035,7 +1113,7 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
1035
1113
  let env;
1036
1114
  if (envArg) {
1037
1115
  if (!EnvironmentType.includes(envArg)) {
1038
- p6.log.error(
1116
+ p7.log.error(
1039
1117
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
1040
1118
  );
1041
1119
  process.exit(1);
@@ -1044,7 +1122,7 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
1044
1122
  } else {
1045
1123
  const found = findProjectConfig();
1046
1124
  const defaultEnv = found?.config.defaultEnv;
1047
- const selected = await p6.select({
1125
+ const selected = await p7.select({
1048
1126
  message: "Select an environment",
1049
1127
  options: EnvironmentType.map((e) => ({
1050
1128
  value: e,
@@ -1053,8 +1131,8 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
1053
1131
  })),
1054
1132
  initialValue: defaultEnv ?? "development"
1055
1133
  });
1056
- if (p6.isCancel(selected)) {
1057
- p6.cancel("Rollback cancelled.");
1134
+ if (p7.isCancel(selected)) {
1135
+ p7.cancel("Rollback cancelled.");
1058
1136
  process.exit(0);
1059
1137
  }
1060
1138
  env = selected;
@@ -1064,25 +1142,25 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
1064
1142
  if (versionIdArg) {
1065
1143
  versionId = versionIdArg;
1066
1144
  } else {
1067
- const spinner9 = p6.spinner();
1068
- spinner9.start(`Fetching version history for ${chalk6.cyan(env)}\u2026`);
1145
+ const spinner10 = p7.spinner();
1146
+ spinner10.start(`Fetching version history for ${chalk7.cyan(env)}\u2026`);
1069
1147
  let versionsResult;
1070
1148
  try {
1071
1149
  versionsResult = await listVersions(projectId, env);
1072
- spinner9.stop(
1150
+ spinner10.stop(
1073
1151
  `Found ${versionsResult.versions.length} snapshot${versionsResult.versions.length !== 1 ? "s" : ""}.`
1074
1152
  );
1075
1153
  } catch (err) {
1076
- spinner9.stop("Failed to fetch version history.");
1154
+ spinner10.stop("Failed to fetch version history.");
1077
1155
  printApiError5(err);
1078
1156
  process.exit(1);
1079
1157
  }
1080
1158
  if (versionsResult.versions.length === 0) {
1081
- p6.log.error(`No version history found for the ${chalk6.cyan(env)} environment.`);
1159
+ p7.log.error(`No version history found for the ${chalk7.cyan(env)} environment.`);
1082
1160
  process.exit(1);
1083
1161
  }
1084
1162
  const pickable = versionsResult.versions;
1085
- const selected = await p6.select({
1163
+ const selected = await p7.select({
1086
1164
  message: "Select a version to roll back to",
1087
1165
  options: pickable.map((v, i) => ({
1088
1166
  value: v.id,
@@ -1090,8 +1168,8 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
1090
1168
  hint: i === 0 ? "current" : v.createdBy?.name ? `by ${v.createdBy.name}` : void 0
1091
1169
  }))
1092
1170
  });
1093
- if (p6.isCancel(selected)) {
1094
- p6.cancel("Rollback cancelled.");
1171
+ if (p7.isCancel(selected)) {
1172
+ p7.cancel("Rollback cancelled.");
1095
1173
  process.exit(0);
1096
1174
  }
1097
1175
  versionId = selected;
@@ -1099,33 +1177,33 @@ async function rollbackCommand(projectArg, envArg, versionIdArg, opts) {
1099
1177
  projectTitle ??= versionsResult.project.title;
1100
1178
  }
1101
1179
  if (!opts.yes) {
1102
- const label = versionNumber !== void 0 ? `v${versionNumber} (${chalk6.dim(versionId)})` : chalk6.dim(versionId);
1103
- p6.log.warn(
1104
- `This will overwrite all ${chalk6.bold(env)} secrets with the state from snapshot ${label}.
1180
+ const label = versionNumber !== void 0 ? `v${versionNumber} (${chalk7.dim(versionId)})` : chalk7.dim(versionId);
1181
+ p7.log.warn(
1182
+ `This will overwrite all ${chalk7.bold(env)} secrets with the state from snapshot ${label}.
1105
1183
  A new snapshot will be created automatically so you can undo this rollback too.`
1106
1184
  );
1107
- const confirmed = await p6.confirm({
1108
- message: `Roll back ${chalk6.bold(projectTitle ?? projectId)} / ${chalk6.cyan(env)} to ${label}?`,
1185
+ const confirmed = await p7.confirm({
1186
+ message: `Roll back ${chalk7.bold(projectTitle ?? projectId)} / ${chalk7.cyan(env)} to ${label}?`,
1109
1187
  initialValue: false
1110
1188
  });
1111
- if (p6.isCancel(confirmed) || !confirmed) {
1112
- p6.cancel("Rollback cancelled.");
1189
+ if (p7.isCancel(confirmed) || !confirmed) {
1190
+ p7.cancel("Rollback cancelled.");
1113
1191
  process.exit(0);
1114
1192
  }
1115
1193
  }
1116
- const spinner8 = p6.spinner();
1117
- spinner8.start("Rolling back\u2026");
1194
+ const spinner9 = p7.spinner();
1195
+ spinner9.start("Rolling back\u2026");
1118
1196
  try {
1119
1197
  const result = await rollback(projectId, env, versionId);
1120
1198
  const { added, modified, removed, unchanged } = result.changes;
1121
- spinner8.stop(
1122
- `Rolled back to v${result.rolledBackTo.versionNumber}. ${chalk6.green(`+${added}`)} added, ${chalk6.yellow(`~${modified}`)} modified, ${chalk6.red(`-${removed}`)} removed, ${chalk6.dim(`${unchanged} unchanged`)}.`
1199
+ spinner9.stop(
1200
+ `Rolled back to v${result.rolledBackTo.versionNumber}. ${chalk7.green(`+${added}`)} added, ${chalk7.yellow(`~${modified}`)} modified, ${chalk7.red(`-${removed}`)} removed, ${chalk7.dim(`${unchanged} unchanged`)}.`
1123
1201
  );
1124
- p6.outro(
1125
- `${chalk6.green("\u2713")} ${chalk6.bold(projectTitle ?? result.project.title)} / ${chalk6.cyan(env)} rolled back to ${chalk6.bold(`v${result.rolledBackTo.versionNumber}`)}.`
1202
+ p7.outro(
1203
+ `${chalk7.green("\u2713")} ${chalk7.bold(projectTitle ?? result.project.title)} / ${chalk7.cyan(env)} rolled back to ${chalk7.bold(`v${result.rolledBackTo.versionNumber}`)}.`
1126
1204
  );
1127
1205
  } catch (err) {
1128
- spinner8.stop("Rollback failed.");
1206
+ spinner9.stop("Rollback failed.");
1129
1207
  printApiError5(err);
1130
1208
  process.exit(1);
1131
1209
  }
@@ -1144,35 +1222,35 @@ function formatDate2(iso) {
1144
1222
  function printApiError5(err) {
1145
1223
  if (err instanceof ApiError) {
1146
1224
  if (err.status === 401) {
1147
- p6.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1225
+ p7.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1148
1226
  } else if (err.status === 404) {
1149
- p6.log.error("Project or environment not found. Check the project ID and environment name.");
1227
+ p7.log.error("Project or environment not found. Check the project ID and environment name.");
1150
1228
  } else {
1151
- p6.log.error(`API error ${err.status}: ${err.message}`);
1229
+ p7.log.error(`API error ${err.status}: ${err.message}`);
1152
1230
  }
1153
1231
  } else if (err instanceof Error) {
1154
- p6.log.error(err.message);
1232
+ p7.log.error(err.message);
1155
1233
  } else {
1156
- p6.log.error("An unexpected error occurred.");
1234
+ p7.log.error("An unexpected error occurred.");
1157
1235
  }
1158
1236
  }
1159
1237
 
1160
1238
  // src/commands/run.ts
1161
- import * as p7 from "@clack/prompts";
1162
- import chalk7 from "chalk";
1239
+ import * as p8 from "@clack/prompts";
1240
+ import chalk8 from "chalk";
1163
1241
  import { spawn } from "child_process";
1164
1242
  init_api();
1165
1243
  init_env();
1166
1244
  async function runCommand(projectArg, envArg, commandArgs, _opts) {
1167
1245
  if (commandArgs.length === 0) {
1168
- p7.log.error(
1246
+ p8.log.error(
1169
1247
  `No command specified.
1170
- Usage: ${chalk7.cyan("vaultsy run <project> <env> -- <command> [args...]")}
1171
- Example: ${chalk7.dim("vaultsy run my-app production -- node server.js")}`
1248
+ Usage: ${chalk8.cyan("vaultsy run <project> <env> -- <command> [args...]")}
1249
+ Example: ${chalk8.dim("vaultsy run my-app production -- node server.js")}`
1172
1250
  );
1173
1251
  process.exit(1);
1174
1252
  }
1175
- p7.intro(chalk7.bold.cyan("vaultsy run"));
1253
+ p8.intro(chalk8.bold.cyan("vaultsy run"));
1176
1254
  let projectId;
1177
1255
  let projectTitle;
1178
1256
  if (projectArg) {
@@ -1181,24 +1259,24 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1181
1259
  const found = findProjectConfig();
1182
1260
  if (found) {
1183
1261
  projectId = found.config.project;
1184
- p7.log.info(`Using project ${chalk7.cyan(projectId)} from ${chalk7.dim("vaultsy.json")}`);
1262
+ p8.log.info(`Using project ${chalk8.cyan(projectId)} from ${chalk8.dim("vaultsy.json")}`);
1185
1263
  } else {
1186
- const spinner9 = p7.spinner();
1187
- spinner9.start("Fetching projects\u2026");
1264
+ const spinner10 = p8.spinner();
1265
+ spinner10.start("Fetching projects\u2026");
1188
1266
  let projects;
1189
1267
  try {
1190
1268
  projects = await listProjects();
1191
- spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1269
+ spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1192
1270
  } catch (err) {
1193
- spinner9.stop("Failed to fetch projects.");
1271
+ spinner10.stop("Failed to fetch projects.");
1194
1272
  printApiError6(err);
1195
1273
  process.exit(1);
1196
1274
  }
1197
1275
  if (projects.length === 0) {
1198
- p7.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1276
+ p8.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1199
1277
  process.exit(1);
1200
1278
  }
1201
- const selected = await p7.select({
1279
+ const selected = await p8.select({
1202
1280
  message: "Select a project",
1203
1281
  options: projects.map((proj) => ({
1204
1282
  value: proj.id,
@@ -1206,8 +1284,8 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1206
1284
  hint: proj.id
1207
1285
  }))
1208
1286
  });
1209
- if (p7.isCancel(selected)) {
1210
- p7.cancel("Run cancelled.");
1287
+ if (p8.isCancel(selected)) {
1288
+ p8.cancel("Run cancelled.");
1211
1289
  process.exit(0);
1212
1290
  }
1213
1291
  projectId = selected;
@@ -1217,7 +1295,7 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1217
1295
  let env;
1218
1296
  if (envArg) {
1219
1297
  if (!EnvironmentType.includes(envArg)) {
1220
- p7.log.error(
1298
+ p8.log.error(
1221
1299
  `Invalid environment "${envArg}". Must be one of: ${EnvironmentType.join(", ")}.`
1222
1300
  );
1223
1301
  process.exit(1);
@@ -1226,7 +1304,7 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1226
1304
  } else {
1227
1305
  const found = findProjectConfig();
1228
1306
  const defaultEnv = found?.config.defaultEnv;
1229
- const selected = await p7.select({
1307
+ const selected = await p8.select({
1230
1308
  message: "Select an environment",
1231
1309
  options: EnvironmentType.map((e) => ({
1232
1310
  value: e,
@@ -1235,24 +1313,24 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1235
1313
  })),
1236
1314
  initialValue: defaultEnv ?? "development"
1237
1315
  });
1238
- if (p7.isCancel(selected)) {
1239
- p7.cancel("Run cancelled.");
1316
+ if (p8.isCancel(selected)) {
1317
+ p8.cancel("Run cancelled.");
1240
1318
  process.exit(0);
1241
1319
  }
1242
1320
  env = selected;
1243
1321
  }
1244
- const spinner8 = p7.spinner();
1245
- spinner8.start(`Pulling ${chalk7.cyan(env)} secrets\u2026`);
1322
+ const spinner9 = p8.spinner();
1323
+ spinner9.start(`Pulling ${chalk8.cyan(env)} secrets\u2026`);
1246
1324
  let secrets;
1247
1325
  try {
1248
1326
  const result = await pullSecrets(projectId, env);
1249
1327
  secrets = result.secrets;
1250
1328
  projectTitle ??= result.project.title;
1251
- spinner8.stop(
1252
- `Injecting ${secrets.length} secret${secrets.length !== 1 ? "s" : ""} from ${chalk7.bold(projectTitle)} / ${chalk7.cyan(env)}.`
1329
+ spinner9.stop(
1330
+ `Injecting ${secrets.length} secret${secrets.length !== 1 ? "s" : ""} from ${chalk8.bold(projectTitle)} / ${chalk8.cyan(env)}.`
1253
1331
  );
1254
1332
  } catch (err) {
1255
- spinner8.stop("Failed to pull secrets.");
1333
+ spinner9.stop("Failed to pull secrets.");
1256
1334
  printApiError6(err);
1257
1335
  process.exit(1);
1258
1336
  }
@@ -1263,13 +1341,13 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1263
1341
  // shell env wins
1264
1342
  };
1265
1343
  if (secrets.length > 0) {
1266
- const keyList = secrets.map((s) => chalk7.dim(s.key)).join(", ");
1267
- p7.log.info(`Injecting: ${keyList}`);
1344
+ const keyList = secrets.map((s) => chalk8.dim(s.key)).join(", ");
1345
+ p8.log.info(`Injecting: ${keyList}`);
1268
1346
  } else {
1269
- p7.log.warn("No secrets found \u2014 running with current environment only.");
1347
+ p8.log.warn("No secrets found \u2014 running with current environment only.");
1270
1348
  }
1271
1349
  const [bin, ...args] = commandArgs;
1272
- p7.log.step(`${chalk7.bold("$")} ${chalk7.white([bin, ...args].join(" "))}`);
1350
+ p8.log.step(`${chalk8.bold("$")} ${chalk8.white([bin, ...args].join(" "))}`);
1273
1351
  process.stdout.write("");
1274
1352
  const child = spawn(bin, args, {
1275
1353
  env: injectedEnv,
@@ -1288,12 +1366,12 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1288
1366
  process.on("SIGHUP", () => forwardSignal("SIGHUP"));
1289
1367
  child.on("error", (err) => {
1290
1368
  if (err.code === "ENOENT") {
1291
- p7.log.error(
1292
- `Command not found: ${chalk7.bold(bin)}
1369
+ p8.log.error(
1370
+ `Command not found: ${chalk8.bold(bin)}
1293
1371
  Make sure it is installed and available in your PATH.`
1294
1372
  );
1295
1373
  } else {
1296
- p7.log.error(`Failed to start process: ${err.message}`);
1374
+ p8.log.error(`Failed to start process: ${err.message}`);
1297
1375
  }
1298
1376
  process.exit(1);
1299
1377
  });
@@ -1304,7 +1382,7 @@ async function runCommand(projectArg, envArg, commandArgs, _opts) {
1304
1382
  }
1305
1383
  const exitCode = code ?? 1;
1306
1384
  if (exitCode !== 0) {
1307
- p7.log.warn(`Process exited with code ${chalk7.bold(String(exitCode))}.`);
1385
+ p8.log.warn(`Process exited with code ${chalk8.bold(String(exitCode))}.`);
1308
1386
  }
1309
1387
  process.exit(exitCode);
1310
1388
  });
@@ -1337,16 +1415,16 @@ function signalToNumber(signal) {
1337
1415
  function printApiError6(err) {
1338
1416
  if (err instanceof ApiError) {
1339
1417
  if (err.status === 401) {
1340
- p7.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1418
+ p8.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
1341
1419
  } else if (err.status === 404) {
1342
- p7.log.error("Project or environment not found. Check the project ID and environment name.");
1420
+ p8.log.error("Project or environment not found. Check the project ID and environment name.");
1343
1421
  } else {
1344
- p7.log.error(`API error ${err.status}: ${err.message}`);
1422
+ p8.log.error(`API error ${err.status}: ${err.message}`);
1345
1423
  }
1346
1424
  } else if (err instanceof Error) {
1347
- p7.log.error(err.message);
1425
+ p8.log.error(err.message);
1348
1426
  } else {
1349
- p7.log.error("An unexpected error occurred.");
1427
+ p8.log.error("An unexpected error occurred.");
1350
1428
  }
1351
1429
  }
1352
1430
 
@@ -1359,16 +1437,19 @@ program.command("login").description("Authenticate with your Vaultsy instance an
1359
1437
  ).action(async (opts) => {
1360
1438
  await loginCommand(opts);
1361
1439
  });
1440
+ program.command("create").description("Create a new project on your Vaultsy instance").option("-t, --title <title>", "Project title (skip the interactive prompt)").action(async (opts) => {
1441
+ await createCommand(opts);
1442
+ });
1362
1443
  program.command("logout").description("Remove locally stored credentials (~/.vaultsy/config.json)").action(async () => {
1363
1444
  const { clearConfig: clearConfig2, configExists: configExists2 } = await Promise.resolve().then(() => (init_config(), config_exports));
1364
- const p8 = await import("@clack/prompts");
1365
- const chalk8 = (await import("chalk")).default;
1445
+ const p9 = await import("@clack/prompts");
1446
+ const chalk9 = (await import("chalk")).default;
1366
1447
  if (!configExists2()) {
1367
- p8.log.warn("No credentials found \u2014 already logged out.");
1448
+ p9.log.warn("No credentials found \u2014 already logged out.");
1368
1449
  return;
1369
1450
  }
1370
1451
  clearConfig2();
1371
- p8.log.success(chalk8.green("\u2713") + " Logged out. Credentials removed.");
1452
+ p9.log.success(chalk9.green("\u2713") + " Logged out. Credentials removed.");
1372
1453
  });
1373
1454
  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(
1374
1455
  async (project, env, opts) => {
@@ -1407,35 +1488,35 @@ program.command("run [project] [env]").description(
1407
1488
  program.command("init").description(
1408
1489
  "Create a vaultsy.json in the current directory to pin a project and default environment"
1409
1490
  ).action(async () => {
1410
- const p8 = await import("@clack/prompts");
1411
- const chalk8 = (await import("chalk")).default;
1491
+ const p9 = await import("@clack/prompts");
1492
+ const chalk9 = (await import("chalk")).default;
1412
1493
  const { listProjects: listProjects2 } = await Promise.resolve().then(() => (init_api(), api_exports));
1413
1494
  const { writeProjectConfig: writeProjectConfig2, findProjectConfig: findProjectConfig2 } = await Promise.resolve().then(() => (init_env(), env_exports));
1414
- p8.intro(chalk8.bold.cyan("vaultsy init"));
1495
+ p9.intro(chalk9.bold.cyan("vaultsy init"));
1415
1496
  const existing = findProjectConfig2();
1416
1497
  if (existing) {
1417
- p8.log.warn(
1418
- `A ${chalk8.bold("vaultsy.json")} already exists at ${chalk8.dim(existing.dir)}.
1498
+ p9.log.warn(
1499
+ `A ${chalk9.bold("vaultsy.json")} already exists at ${chalk9.dim(existing.dir)}.
1419
1500
  Delete it first if you want to re-initialise.`
1420
1501
  );
1421
1502
  process.exit(0);
1422
1503
  }
1423
- const spinner8 = p8.spinner();
1424
- spinner8.start("Fetching your projects\u2026");
1504
+ const spinner9 = p9.spinner();
1505
+ spinner9.start("Fetching your projects\u2026");
1425
1506
  let projects;
1426
1507
  try {
1427
1508
  projects = await listProjects2();
1428
- spinner8.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1509
+ spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
1429
1510
  } catch (err) {
1430
- spinner8.stop("Failed to fetch projects.");
1431
- if (err instanceof Error) p8.log.error(err.message);
1511
+ spinner9.stop("Failed to fetch projects.");
1512
+ if (err instanceof Error) p9.log.error(err.message);
1432
1513
  process.exit(1);
1433
1514
  }
1434
1515
  if (projects.length === 0) {
1435
- p8.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1516
+ p9.log.error("No projects found. Create one at your Vaultsy dashboard first.");
1436
1517
  process.exit(1);
1437
1518
  }
1438
- const selectedProject = await p8.select({
1519
+ const selectedProject = await p9.select({
1439
1520
  message: "Which project does this directory belong to?",
1440
1521
  options: projects.map((proj) => ({
1441
1522
  value: proj.id,
@@ -1443,37 +1524,37 @@ program.command("init").description(
1443
1524
  hint: proj.id
1444
1525
  }))
1445
1526
  });
1446
- if (p8.isCancel(selectedProject)) {
1447
- p8.cancel("Init cancelled.");
1527
+ if (p9.isCancel(selectedProject)) {
1528
+ p9.cancel("Init cancelled.");
1448
1529
  process.exit(0);
1449
1530
  }
1450
- const selectedEnv = await p8.select({
1531
+ const selectedEnv = await p9.select({
1451
1532
  message: "Default environment for this directory?",
1452
1533
  options: EnvironmentType.map((e) => ({ value: e, label: e })),
1453
1534
  initialValue: "development"
1454
1535
  });
1455
- if (p8.isCancel(selectedEnv)) {
1456
- p8.cancel("Init cancelled.");
1536
+ if (p9.isCancel(selectedEnv)) {
1537
+ p9.cancel("Init cancelled.");
1457
1538
  process.exit(0);
1458
1539
  }
1459
1540
  writeProjectConfig2({ project: selectedProject, defaultEnv: selectedEnv });
1460
- p8.outro(
1461
- `${chalk8.green("\u2713")} Created ${chalk8.bold("vaultsy.json")}
1462
- Run ${chalk8.cyan("vaultsy pull")} or ${chalk8.cyan("vaultsy push")} with no arguments from this directory.`
1541
+ p9.outro(
1542
+ `${chalk9.green("\u2713")} Created ${chalk9.bold("vaultsy.json")}
1543
+ Run ${chalk9.cyan("vaultsy pull")} or ${chalk9.cyan("vaultsy push")} with no arguments from this directory.`
1463
1544
  );
1464
1545
  });
1465
1546
  program.command("whoami").description("Show the currently authenticated user").action(async () => {
1466
- const p8 = await import("@clack/prompts");
1467
- const chalk8 = (await import("chalk")).default;
1547
+ const p9 = await import("@clack/prompts");
1548
+ const chalk9 = (await import("chalk")).default;
1468
1549
  const { getMe: getMe2 } = await Promise.resolve().then(() => (init_api(), api_exports));
1469
1550
  try {
1470
1551
  const me = await getMe2();
1471
- p8.log.success(`Logged in as ${chalk8.bold(me.name)} ${chalk8.dim(`<${me.email}>`)}`);
1552
+ p9.log.success(`Logged in as ${chalk9.bold(me.name)} ${chalk9.dim(`<${me.email}>`)}`);
1472
1553
  } catch (err) {
1473
1554
  if (err instanceof Error) {
1474
- p8.log.error(err.message);
1555
+ p9.log.error(err.message);
1475
1556
  } else {
1476
- p8.log.error("Not authenticated. Run `vaultsy login` first.");
1557
+ p9.log.error("Not authenticated. Run `vaultsy login` first.");
1477
1558
  }
1478
1559
  process.exit(1);
1479
1560
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaultsy-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Official CLI for Vaultsy — pull, push, and inject secrets from your terminal",
5
5
  "type": "module",
6
6
  "bin": {