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.
- package/README.md +29 -4
- package/dist/index.js +329 -248
- 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.
|
|
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
|
-
#
|
|
33
|
+
# 4. Pull secrets to a local .env file
|
|
31
34
|
vaultsy pull
|
|
32
35
|
|
|
33
|
-
#
|
|
36
|
+
# 5. Push local changes back up
|
|
34
37
|
vaultsy push
|
|
35
38
|
|
|
36
|
-
#
|
|
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(
|
|
162
|
-
const lines =
|
|
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
|
|
297
|
-
|
|
303
|
+
const spinner10 = p.spinner();
|
|
304
|
+
spinner10.start("Fetching projects\u2026");
|
|
298
305
|
let projects;
|
|
299
306
|
try {
|
|
300
307
|
projects = await listProjects();
|
|
301
|
-
|
|
308
|
+
spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
|
|
302
309
|
} catch (err) {
|
|
303
|
-
|
|
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
|
|
328
|
-
|
|
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
|
-
|
|
352
|
+
spinner9.stop(`Loaded secrets for ${chalk.bold(projectTitle ?? projectId)}.`);
|
|
346
353
|
} catch (err) {
|
|
347
|
-
|
|
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
|
|
460
|
-
|
|
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
|
-
|
|
474
|
+
spinner9.stop("Token verified.");
|
|
468
475
|
} catch (err) {
|
|
469
|
-
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
582
|
+
p4.log.info(`Using project ${chalk4.cyan(projectId)} from ${chalk4.dim("vaultsy.json")}`);
|
|
505
583
|
} else {
|
|
506
|
-
const
|
|
507
|
-
|
|
584
|
+
const spinner10 = p4.spinner();
|
|
585
|
+
spinner10.start("Fetching projects\u2026");
|
|
508
586
|
let projects;
|
|
509
587
|
try {
|
|
510
588
|
projects = await listProjects();
|
|
511
|
-
|
|
589
|
+
spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
|
|
512
590
|
} catch (err) {
|
|
513
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
530
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
559
|
-
|
|
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
|
-
|
|
568
|
-
`${
|
|
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
|
|
649
|
+
const confirmed = await p4.confirm({
|
|
572
650
|
message: "Continue anyway?",
|
|
573
651
|
initialValue: false
|
|
574
652
|
});
|
|
575
|
-
if (
|
|
576
|
-
|
|
653
|
+
if (p4.isCancel(confirmed) || !confirmed) {
|
|
654
|
+
p4.cancel("Pull cancelled.");
|
|
577
655
|
process.exit(0);
|
|
578
656
|
}
|
|
579
657
|
}
|
|
580
|
-
const
|
|
581
|
-
|
|
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
|
-
|
|
586
|
-
`Pulled ${result.secrets.length} secret${result.secrets.length !== 1 ? "s" : ""} from ${
|
|
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
|
-
|
|
667
|
+
spinner9.stop("Pull failed.");
|
|
590
668
|
printApiError2(err);
|
|
591
669
|
process.exit(1);
|
|
592
670
|
}
|
|
593
671
|
if (result.secrets.length === 0) {
|
|
594
|
-
|
|
595
|
-
|
|
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
|
-
|
|
600
|
-
`${
|
|
601
|
-
${
|
|
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
|
-
|
|
685
|
+
p4.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
|
|
608
686
|
} else if (err.status === 404) {
|
|
609
|
-
|
|
687
|
+
p4.log.error("Project or environment not found. Check the project ID and environment name.");
|
|
610
688
|
} else {
|
|
611
|
-
|
|
689
|
+
p4.log.error(`API error ${err.status}: ${err.message}`);
|
|
612
690
|
}
|
|
613
691
|
} else if (err instanceof Error) {
|
|
614
|
-
|
|
692
|
+
p4.log.error(err.message);
|
|
615
693
|
} else {
|
|
616
|
-
|
|
694
|
+
p4.log.error("An unexpected error occurred.");
|
|
617
695
|
}
|
|
618
696
|
}
|
|
619
697
|
|
|
620
698
|
// src/commands/push.ts
|
|
621
|
-
import * as
|
|
622
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
715
|
+
p5.log.info(`Using project ${chalk5.cyan(projectId)} from ${chalk5.dim("vaultsy.json")}`);
|
|
638
716
|
} else {
|
|
639
|
-
const
|
|
640
|
-
|
|
717
|
+
const spinner9 = p5.spinner();
|
|
718
|
+
spinner9.start("Fetching projects\u2026");
|
|
641
719
|
let projects;
|
|
642
720
|
try {
|
|
643
721
|
projects = await listProjects();
|
|
644
|
-
|
|
722
|
+
spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
|
|
645
723
|
} catch (err) {
|
|
646
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
663
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
692
|
-
|
|
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
|
-
|
|
701
|
-
`File ${
|
|
702
|
-
Run ${
|
|
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
|
-
|
|
709
|
-
|
|
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
|
-
|
|
713
|
-
`Read ${
|
|
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 =
|
|
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
|
-
|
|
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
|
|
738
|
-
message: `Push these changes to ${
|
|
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 (
|
|
742
|
-
|
|
819
|
+
if (p5.isCancel(confirmed) || !confirmed) {
|
|
820
|
+
p5.cancel("Push cancelled.");
|
|
743
821
|
process.exit(0);
|
|
744
822
|
}
|
|
745
823
|
}
|
|
746
|
-
const pushSpinner =
|
|
747
|
-
pushSpinner.start(`Pushing to ${
|
|
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. ${
|
|
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
|
-
|
|
760
|
-
`${
|
|
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
|
-
|
|
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(` ${
|
|
876
|
+
lines.push(` ${chalk5.green("+")} ${chalk5.green(key)}`);
|
|
799
877
|
}
|
|
800
878
|
for (const key of diff.modified) {
|
|
801
|
-
lines.push(` ${
|
|
879
|
+
lines.push(` ${chalk5.yellow("~")} ${chalk5.yellow(key)}`);
|
|
802
880
|
}
|
|
803
881
|
for (const key of diff.removed) {
|
|
804
|
-
lines.push(` ${
|
|
882
|
+
lines.push(` ${chalk5.red("-")} ${chalk5.red(key)}`);
|
|
805
883
|
}
|
|
806
884
|
for (const key of diff.unchanged) {
|
|
807
|
-
lines.push(` ${
|
|
885
|
+
lines.push(` ${chalk5.dim("\xB7")} ${chalk5.dim(key)}`);
|
|
808
886
|
}
|
|
809
|
-
|
|
887
|
+
p5.log.message(lines.join("\n"));
|
|
810
888
|
const summary = [
|
|
811
|
-
diff.added.length > 0 ?
|
|
812
|
-
diff.modified.length > 0 ?
|
|
813
|
-
diff.removed.length > 0 ?
|
|
814
|
-
diff.unchanged.length > 0 ?
|
|
815
|
-
].filter(Boolean).join(
|
|
816
|
-
|
|
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
|
-
|
|
899
|
+
p5.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
|
|
822
900
|
} else if (err.status === 404) {
|
|
823
|
-
|
|
901
|
+
p5.log.error("Project or environment not found. Check the project ID and environment name.");
|
|
824
902
|
} else {
|
|
825
|
-
|
|
903
|
+
p5.log.error(`API error ${err.status}: ${err.message}`);
|
|
826
904
|
}
|
|
827
905
|
} else if (err instanceof Error) {
|
|
828
|
-
|
|
906
|
+
p5.log.error(err.message);
|
|
829
907
|
} else {
|
|
830
|
-
|
|
908
|
+
p5.log.error("An unexpected error occurred.");
|
|
831
909
|
}
|
|
832
910
|
}
|
|
833
911
|
|
|
834
912
|
// src/commands/history.ts
|
|
835
|
-
import * as
|
|
836
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
927
|
+
p6.log.info(`Using project ${chalk6.cyan(projectId)} from ${chalk6.dim("vaultsy.json")}`);
|
|
850
928
|
} else {
|
|
851
|
-
const
|
|
852
|
-
|
|
929
|
+
const spinner10 = p6.spinner();
|
|
930
|
+
spinner10.start("Fetching projects\u2026");
|
|
853
931
|
let projects;
|
|
854
932
|
try {
|
|
855
933
|
projects = await listProjects();
|
|
856
|
-
|
|
934
|
+
spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
|
|
857
935
|
} catch (err) {
|
|
858
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
875
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
904
|
-
|
|
981
|
+
if (p6.isCancel(selected)) {
|
|
982
|
+
p6.cancel("Cancelled.");
|
|
905
983
|
process.exit(0);
|
|
906
984
|
}
|
|
907
985
|
env = selected;
|
|
908
986
|
}
|
|
909
|
-
const
|
|
910
|
-
|
|
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
|
-
|
|
915
|
-
`${result.versions.length} snapshot${result.versions.length !== 1 ? "s" : ""} for ${
|
|
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
|
-
|
|
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
|
-
|
|
924
|
-
|
|
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 =
|
|
932
|
-
const divider =
|
|
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 ?
|
|
938
|
-
const vId =
|
|
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 ??
|
|
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 ?
|
|
943
|
-
return vNum +
|
|
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
|
-
|
|
947
|
-
|
|
948
|
-
`To rollback, run: ${
|
|
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
|
-
|
|
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
|
-
|
|
1052
|
+
p6.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
|
|
975
1053
|
} else if (err.status === 404) {
|
|
976
|
-
|
|
1054
|
+
p6.log.error("Project or environment not found. Check the project ID and environment name.");
|
|
977
1055
|
} else {
|
|
978
|
-
|
|
1056
|
+
p6.log.error(`API error ${err.status}: ${err.message}`);
|
|
979
1057
|
}
|
|
980
1058
|
} else if (err instanceof Error) {
|
|
981
|
-
|
|
1059
|
+
p6.log.error(err.message);
|
|
982
1060
|
} else {
|
|
983
|
-
|
|
1061
|
+
p6.log.error("An unexpected error occurred.");
|
|
984
1062
|
}
|
|
985
1063
|
}
|
|
986
1064
|
|
|
987
1065
|
// src/commands/rollback.ts
|
|
988
|
-
import * as
|
|
989
|
-
import
|
|
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
|
-
|
|
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
|
-
|
|
1080
|
+
p7.log.info(`Using project ${chalk7.cyan(projectId)} from ${chalk7.dim("vaultsy.json")}`);
|
|
1003
1081
|
} else {
|
|
1004
|
-
const
|
|
1005
|
-
|
|
1082
|
+
const spinner10 = p7.spinner();
|
|
1083
|
+
spinner10.start("Fetching projects\u2026");
|
|
1006
1084
|
let projects;
|
|
1007
1085
|
try {
|
|
1008
1086
|
projects = await listProjects();
|
|
1009
|
-
|
|
1087
|
+
spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
|
|
1010
1088
|
} catch (err) {
|
|
1011
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1028
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1057
|
-
|
|
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
|
|
1068
|
-
|
|
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
|
-
|
|
1150
|
+
spinner10.stop(
|
|
1073
1151
|
`Found ${versionsResult.versions.length} snapshot${versionsResult.versions.length !== 1 ? "s" : ""}.`
|
|
1074
1152
|
);
|
|
1075
1153
|
} catch (err) {
|
|
1076
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1094
|
-
|
|
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} (${
|
|
1103
|
-
|
|
1104
|
-
`This will overwrite all ${
|
|
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
|
|
1108
|
-
message: `Roll back ${
|
|
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 (
|
|
1112
|
-
|
|
1189
|
+
if (p7.isCancel(confirmed) || !confirmed) {
|
|
1190
|
+
p7.cancel("Rollback cancelled.");
|
|
1113
1191
|
process.exit(0);
|
|
1114
1192
|
}
|
|
1115
1193
|
}
|
|
1116
|
-
const
|
|
1117
|
-
|
|
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
|
-
|
|
1122
|
-
`Rolled back to v${result.rolledBackTo.versionNumber}. ${
|
|
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
|
-
|
|
1125
|
-
`${
|
|
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
|
-
|
|
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
|
-
|
|
1225
|
+
p7.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
|
|
1148
1226
|
} else if (err.status === 404) {
|
|
1149
|
-
|
|
1227
|
+
p7.log.error("Project or environment not found. Check the project ID and environment name.");
|
|
1150
1228
|
} else {
|
|
1151
|
-
|
|
1229
|
+
p7.log.error(`API error ${err.status}: ${err.message}`);
|
|
1152
1230
|
}
|
|
1153
1231
|
} else if (err instanceof Error) {
|
|
1154
|
-
|
|
1232
|
+
p7.log.error(err.message);
|
|
1155
1233
|
} else {
|
|
1156
|
-
|
|
1234
|
+
p7.log.error("An unexpected error occurred.");
|
|
1157
1235
|
}
|
|
1158
1236
|
}
|
|
1159
1237
|
|
|
1160
1238
|
// src/commands/run.ts
|
|
1161
|
-
import * as
|
|
1162
|
-
import
|
|
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
|
-
|
|
1246
|
+
p8.log.error(
|
|
1169
1247
|
`No command specified.
|
|
1170
|
-
Usage: ${
|
|
1171
|
-
Example: ${
|
|
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
|
-
|
|
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
|
-
|
|
1262
|
+
p8.log.info(`Using project ${chalk8.cyan(projectId)} from ${chalk8.dim("vaultsy.json")}`);
|
|
1185
1263
|
} else {
|
|
1186
|
-
const
|
|
1187
|
-
|
|
1264
|
+
const spinner10 = p8.spinner();
|
|
1265
|
+
spinner10.start("Fetching projects\u2026");
|
|
1188
1266
|
let projects;
|
|
1189
1267
|
try {
|
|
1190
1268
|
projects = await listProjects();
|
|
1191
|
-
|
|
1269
|
+
spinner10.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
|
|
1192
1270
|
} catch (err) {
|
|
1193
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1210
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1239
|
-
|
|
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
|
|
1245
|
-
|
|
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
|
-
|
|
1252
|
-
`Injecting ${secrets.length} secret${secrets.length !== 1 ? "s" : ""} from ${
|
|
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
|
-
|
|
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) =>
|
|
1267
|
-
|
|
1344
|
+
const keyList = secrets.map((s) => chalk8.dim(s.key)).join(", ");
|
|
1345
|
+
p8.log.info(`Injecting: ${keyList}`);
|
|
1268
1346
|
} else {
|
|
1269
|
-
|
|
1347
|
+
p8.log.warn("No secrets found \u2014 running with current environment only.");
|
|
1270
1348
|
}
|
|
1271
1349
|
const [bin, ...args] = commandArgs;
|
|
1272
|
-
|
|
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
|
-
|
|
1292
|
-
`Command not found: ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1418
|
+
p8.log.error("Unauthorized. Run `vaultsy login` to re-authenticate.");
|
|
1341
1419
|
} else if (err.status === 404) {
|
|
1342
|
-
|
|
1420
|
+
p8.log.error("Project or environment not found. Check the project ID and environment name.");
|
|
1343
1421
|
} else {
|
|
1344
|
-
|
|
1422
|
+
p8.log.error(`API error ${err.status}: ${err.message}`);
|
|
1345
1423
|
}
|
|
1346
1424
|
} else if (err instanceof Error) {
|
|
1347
|
-
|
|
1425
|
+
p8.log.error(err.message);
|
|
1348
1426
|
} else {
|
|
1349
|
-
|
|
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
|
|
1365
|
-
const
|
|
1445
|
+
const p9 = await import("@clack/prompts");
|
|
1446
|
+
const chalk9 = (await import("chalk")).default;
|
|
1366
1447
|
if (!configExists2()) {
|
|
1367
|
-
|
|
1448
|
+
p9.log.warn("No credentials found \u2014 already logged out.");
|
|
1368
1449
|
return;
|
|
1369
1450
|
}
|
|
1370
1451
|
clearConfig2();
|
|
1371
|
-
|
|
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
|
|
1411
|
-
const
|
|
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
|
-
|
|
1495
|
+
p9.intro(chalk9.bold.cyan("vaultsy init"));
|
|
1415
1496
|
const existing = findProjectConfig2();
|
|
1416
1497
|
if (existing) {
|
|
1417
|
-
|
|
1418
|
-
`A ${
|
|
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
|
|
1424
|
-
|
|
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
|
-
|
|
1509
|
+
spinner9.stop(`Found ${projects.length} project${projects.length !== 1 ? "s" : ""}.`);
|
|
1429
1510
|
} catch (err) {
|
|
1430
|
-
|
|
1431
|
-
if (err instanceof Error)
|
|
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
|
-
|
|
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
|
|
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 (
|
|
1447
|
-
|
|
1527
|
+
if (p9.isCancel(selectedProject)) {
|
|
1528
|
+
p9.cancel("Init cancelled.");
|
|
1448
1529
|
process.exit(0);
|
|
1449
1530
|
}
|
|
1450
|
-
const selectedEnv = await
|
|
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 (
|
|
1456
|
-
|
|
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
|
-
|
|
1461
|
-
`${
|
|
1462
|
-
Run ${
|
|
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
|
|
1467
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
1555
|
+
p9.log.error(err.message);
|
|
1475
1556
|
} else {
|
|
1476
|
-
|
|
1557
|
+
p9.log.error("Not authenticated. Run `vaultsy login` first.");
|
|
1477
1558
|
}
|
|
1478
1559
|
process.exit(1);
|
|
1479
1560
|
}
|