vaulter-cli 2.3.1 → 2.3.3
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/package.json +2 -1
- package/src/commands/help.js +1 -0
- package/src/commands/view.js +68 -24
- package/src/index.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vaulter-cli",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "CLI tool for Vaulter - Secure API Key Manager",
|
|
5
5
|
"author": "faris-sait",
|
|
6
6
|
"repository": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"chalk": "^5.3.0",
|
|
33
33
|
"cli-table3": "^0.6.3",
|
|
34
|
+
"clipboardy": "^4.0.0",
|
|
34
35
|
"commander": "^12.0.0",
|
|
35
36
|
"inquirer": "^9.2.0",
|
|
36
37
|
"open": "^10.0.0",
|
package/src/commands/help.js
CHANGED
|
@@ -22,6 +22,7 @@ export async function showHelp() {
|
|
|
22
22
|
{ name: 'ls', desc: 'List all API keys in your vault' },
|
|
23
23
|
{ name: 'add <name>', desc: 'Add a new API key to your vault' },
|
|
24
24
|
{ name: 'remove <name-or-id>', desc: 'Remove an API key from your vault' },
|
|
25
|
+
{ name: 'view [key_names...]', desc: 'Decrypt and display one or more API keys in your terminal' },
|
|
25
26
|
{ name: 'make [file]', desc: 'Generate a .env file from your vault keys' },
|
|
26
27
|
{ name: 'save [file]', desc: 'Upload a local .env file to your vault' },
|
|
27
28
|
{ name: 'web-app', desc: 'Open the Vaulter web app in your browser' },
|
package/src/commands/view.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import Table from 'cli-table3';
|
|
4
3
|
import chalk from 'chalk';
|
|
4
|
+
import clipboardy from 'clipboardy';
|
|
5
5
|
import { apiFetch } from '../lib/api.js';
|
|
6
|
-
import { purple, dim, error, warn } from '../lib/ui.js';
|
|
6
|
+
import { purple, dim, error, warn, green } from '../lib/ui.js';
|
|
7
|
+
|
|
8
|
+
const DIVIDER_WIDTH = 60;
|
|
9
|
+
const divider = chalk.dim('─'.repeat(DIVIDER_WIDTH));
|
|
10
|
+
|
|
11
|
+
function printKeyCard(name, value, index, total) {
|
|
12
|
+
const label = purple.bold(name);
|
|
13
|
+
const counter = chalk.dim(`[${index + 1}/${total}]`);
|
|
14
|
+
console.log(` ${label} ${counter}`);
|
|
15
|
+
console.log(` ${divider}`);
|
|
16
|
+
console.log(` ${chalk.hex('#a78bfa')(value)}`);
|
|
17
|
+
console.log('');
|
|
18
|
+
}
|
|
7
19
|
|
|
8
20
|
export async function viewKeys(names) {
|
|
9
21
|
const spinner = ora({ text: 'Fetching vault keys...', color: 'magenta' }).start();
|
|
@@ -30,7 +42,6 @@ export async function viewKeys(names) {
|
|
|
30
42
|
let selected;
|
|
31
43
|
|
|
32
44
|
if (names.length === 0) {
|
|
33
|
-
// Interactive checkbox selection
|
|
34
45
|
console.log('');
|
|
35
46
|
const { selectedKeys } = await inquirer.prompt([
|
|
36
47
|
{
|
|
@@ -54,7 +65,6 @@ export async function viewKeys(names) {
|
|
|
54
65
|
|
|
55
66
|
selected = selectedKeys;
|
|
56
67
|
} else {
|
|
57
|
-
// Match provided names case-insensitively
|
|
58
68
|
selected = [];
|
|
59
69
|
for (const name of names) {
|
|
60
70
|
const match = keys.find((k) => k.name.toLowerCase() === name.toLowerCase());
|
|
@@ -78,35 +88,69 @@ export async function viewKeys(names) {
|
|
|
78
88
|
for (const key of selected) {
|
|
79
89
|
try {
|
|
80
90
|
const data = await apiFetch(`/api/keys/${key.id}?decrypt=true`);
|
|
81
|
-
rows.push(
|
|
91
|
+
rows.push({ name: key.name, value: data.decrypted_key });
|
|
82
92
|
} catch (err) {
|
|
83
|
-
rows.push(
|
|
93
|
+
rows.push({ name: key.name, value: null });
|
|
84
94
|
}
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
decryptSpinner.stop();
|
|
88
98
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
for (const row of rows) {
|
|
104
|
-
table.push(row);
|
|
99
|
+
// Display cards
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(` ${chalk.dim('━'.repeat(DIVIDER_WIDTH))}`);
|
|
102
|
+
console.log('');
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < rows.length; i++) {
|
|
105
|
+
const { name, value } = rows[i];
|
|
106
|
+
if (value === null) {
|
|
107
|
+
printKeyCard(name, chalk.dim('(failed to decrypt)'), i, rows.length);
|
|
108
|
+
} else {
|
|
109
|
+
printKeyCard(name, value, i, rows.length);
|
|
110
|
+
}
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
console.log(` ${chalk.dim('━'.repeat(DIVIDER_WIDTH))}`);
|
|
107
114
|
console.log('');
|
|
108
|
-
console.log(
|
|
115
|
+
console.log(` ${chalk.yellow('⚠')} ${chalk.yellow.bold('These values are sensitive.')} ${chalk.dim('Clear your terminal when done.')}`);
|
|
116
|
+
console.log(` ${chalk.dim('💡 Tip: Select a key below to copy it to your clipboard.')}`);
|
|
109
117
|
console.log('');
|
|
110
|
-
|
|
118
|
+
|
|
119
|
+
// Clipboard copy loop
|
|
120
|
+
const copyableRows = rows.filter((r) => r.value !== null);
|
|
121
|
+
if (copyableRows.length === 0) return;
|
|
122
|
+
|
|
123
|
+
let copying = true;
|
|
124
|
+
while (copying) {
|
|
125
|
+
const { toCopy } = await inquirer.prompt([
|
|
126
|
+
{
|
|
127
|
+
type: 'list',
|
|
128
|
+
name: 'toCopy',
|
|
129
|
+
message: purple('Copy a key to clipboard:'),
|
|
130
|
+
choices: [
|
|
131
|
+
...copyableRows.map((r) => ({
|
|
132
|
+
name: `${chalk.white(r.name)} ${chalk.dim(r.value.slice(0, 24) + (r.value.length > 24 ? '...' : ''))}`,
|
|
133
|
+
value: r,
|
|
134
|
+
})),
|
|
135
|
+
new inquirer.Separator(),
|
|
136
|
+
{ name: chalk.dim('Done'), value: null },
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
]);
|
|
140
|
+
|
|
141
|
+
if (toCopy === null) {
|
|
142
|
+
copying = false;
|
|
143
|
+
} else {
|
|
144
|
+
try {
|
|
145
|
+
await clipboardy.write(toCopy.value);
|
|
146
|
+
console.log(` ${green('✔')} ${chalk.white(toCopy.name)} copied to clipboard.`);
|
|
147
|
+
console.log('');
|
|
148
|
+
} catch {
|
|
149
|
+
warn('Could not access clipboard. Copy the value manually from above.');
|
|
150
|
+
console.log('');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
111
155
|
console.log('');
|
|
112
156
|
}
|