@vaultkeeper/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/approve-XNONSHTO.js +30 -0
- package/dist/approve-XNONSHTO.js.map +1 -0
- package/dist/bin.js +69 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-FFQCXUYN.js +14 -0
- package/dist/chunk-FFQCXUYN.js.map +1 -0
- package/dist/config-FDKYUDXX.js +85 -0
- package/dist/config-FDKYUDXX.js.map +1 -0
- package/dist/delete-WNUM2ART.js +44 -0
- package/dist/delete-WNUM2ART.js.map +1 -0
- package/dist/dev-mode-QKWOGXIZ.js +43 -0
- package/dist/dev-mode-QKWOGXIZ.js.map +1 -0
- package/dist/doctor-O5MH7VDR.js +44 -0
- package/dist/doctor-O5MH7VDR.js.map +1 -0
- package/dist/exec-YE2YX45A.js +276 -0
- package/dist/exec-YE2YX45A.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/rotate-key-3PTCQYUP.js +23 -0
- package/dist/rotate-key-3PTCQYUP.js.map +1 -0
- package/dist/store-K4PPONOU.js +59 -0
- package/dist/store-K4PPONOU.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present Mike North
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/approve.ts
|
|
4
|
+
import { parseArgs } from "util";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
function approveCommand(args) {
|
|
7
|
+
const { values } = parseArgs({
|
|
8
|
+
args,
|
|
9
|
+
options: {
|
|
10
|
+
script: { type: "string" }
|
|
11
|
+
},
|
|
12
|
+
strict: true
|
|
13
|
+
});
|
|
14
|
+
if (values.script === void 0) {
|
|
15
|
+
process.stderr.write("Error: --script is required\n");
|
|
16
|
+
process.stderr.write("Usage: vaultkeeper approve --script <path>\n");
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
const scriptPath = path.resolve(values.script);
|
|
20
|
+
process.stdout.write(`Script approved: ${scriptPath}
|
|
21
|
+
`);
|
|
22
|
+
process.stdout.write(
|
|
23
|
+
"The script hash will be recorded on first use with vaultkeeper exec.\n"
|
|
24
|
+
);
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
approveCommand
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=approve-XNONSHTO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/approve.ts"],"sourcesContent":["import { parseArgs } from 'node:util'\nimport * as path from 'node:path'\n\nexport function approveCommand(args: string[]): number {\n const { values } = parseArgs({\n args,\n options: {\n script: { type: 'string' },\n },\n strict: true,\n })\n\n if (values.script === undefined) {\n process.stderr.write('Error: --script is required\\n')\n process.stderr.write('Usage: vaultkeeper approve --script <path>\\n')\n return 1\n }\n\n const scriptPath = path.resolve(values.script)\n\n // The TOFU manifest records the executable hash on first use via setup().\n // Direct manifest manipulation is not part of the public vaultkeeper API.\n // The hash will be recorded automatically the first time this script\n // runs `vaultkeeper exec`.\n process.stdout.write(`Script approved: ${scriptPath}\\n`)\n process.stdout.write(\n 'The script hash will be recorded on first use with vaultkeeper exec.\\n',\n )\n return 0\n}\n"],"mappings":";;;AAAA,SAAS,iBAAiB;AAC1B,YAAY,UAAU;AAEf,SAAS,eAAe,MAAwB;AACrD,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B;AAAA,IACA,SAAS;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,OAAO,WAAW,QAAW;AAC/B,YAAQ,OAAO,MAAM,+BAA+B;AACpD,YAAQ,OAAO,MAAM,8CAA8C;AACnE,WAAO;AAAA,EACT;AAEA,QAAM,aAAkB,aAAQ,OAAO,MAAM;AAM7C,UAAQ,OAAO,MAAM,oBAAoB,UAAU;AAAA,CAAI;AACvD,UAAQ,OAAO;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/bin.ts
|
|
5
|
+
import { parseArgs } from "util";
|
|
6
|
+
var { positionals } = parseArgs({
|
|
7
|
+
allowPositionals: true,
|
|
8
|
+
strict: false
|
|
9
|
+
});
|
|
10
|
+
var subcommand = positionals[0];
|
|
11
|
+
var commandArgs = process.argv.slice(3);
|
|
12
|
+
function printHelp() {
|
|
13
|
+
process.stdout.write(
|
|
14
|
+
"Usage: vaultkeeper <command> [options]\n\nCommands:\n exec Run a command with a secret injected as an env var\n doctor Run preflight checks\n approve Pre-record a script hash in the TOFU manifest\n dev-mode Toggle development mode for a script\n store Store a secret (reads from stdin)\n delete Delete a secret\n config Manage configuration\n rotate-key Rotate the encryption key\n"
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
async function main() {
|
|
18
|
+
if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h") {
|
|
19
|
+
printHelp();
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
switch (subcommand) {
|
|
23
|
+
case "exec": {
|
|
24
|
+
const { execCommand } = await import("./exec-YE2YX45A.js");
|
|
25
|
+
return execCommand(commandArgs);
|
|
26
|
+
}
|
|
27
|
+
case "doctor": {
|
|
28
|
+
const { doctorCommand } = await import("./doctor-O5MH7VDR.js");
|
|
29
|
+
return doctorCommand(commandArgs);
|
|
30
|
+
}
|
|
31
|
+
case "approve": {
|
|
32
|
+
const { approveCommand } = await import("./approve-XNONSHTO.js");
|
|
33
|
+
return approveCommand(commandArgs);
|
|
34
|
+
}
|
|
35
|
+
case "dev-mode": {
|
|
36
|
+
const { devModeCommand } = await import("./dev-mode-QKWOGXIZ.js");
|
|
37
|
+
return devModeCommand(commandArgs);
|
|
38
|
+
}
|
|
39
|
+
case "store": {
|
|
40
|
+
const { storeCommand } = await import("./store-K4PPONOU.js");
|
|
41
|
+
return storeCommand(commandArgs);
|
|
42
|
+
}
|
|
43
|
+
case "delete": {
|
|
44
|
+
const { deleteCommand } = await import("./delete-WNUM2ART.js");
|
|
45
|
+
return deleteCommand(commandArgs);
|
|
46
|
+
}
|
|
47
|
+
case "config": {
|
|
48
|
+
const { configCommand } = await import("./config-FDKYUDXX.js");
|
|
49
|
+
return configCommand(commandArgs);
|
|
50
|
+
}
|
|
51
|
+
case "rotate-key": {
|
|
52
|
+
const { rotateKeyCommand } = await import("./rotate-key-3PTCQYUP.js");
|
|
53
|
+
return rotateKeyCommand(commandArgs);
|
|
54
|
+
}
|
|
55
|
+
default:
|
|
56
|
+
process.stderr.write(`Unknown command: ${subcommand}
|
|
57
|
+
`);
|
|
58
|
+
printHelp();
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
main().then((code) => {
|
|
63
|
+
process.exitCode = code;
|
|
64
|
+
}).catch((err) => {
|
|
65
|
+
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|
|
66
|
+
`);
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI entry point for vaultkeeper.\n *\n * Each subcommand is lazy-loaded via dynamic import() to minimize startup\n * time — only the requested command's module (and its dependencies) is loaded.\n *\n * argv layout: [node, script, subcommand, ...commandArgs]\n * parseArgs consumes argv[2..] and extracts the subcommand as positionals[0].\n * commandArgs is argv[3..] — everything after the subcommand.\n *\n * @internal\n */\n\nimport { parseArgs } from 'node:util'\n\nconst { positionals } = parseArgs({\n allowPositionals: true,\n strict: false,\n})\n\nconst subcommand = positionals[0]\n// argv[0]=node, argv[1]=script, argv[2]=subcommand, argv[3..]=commandArgs\nconst commandArgs = process.argv.slice(3)\n\nfunction printHelp(): void {\n process.stdout.write(\n 'Usage: vaultkeeper <command> [options]\\n\\n' +\n 'Commands:\\n' +\n ' exec Run a command with a secret injected as an env var\\n' +\n ' doctor Run preflight checks\\n' +\n ' approve Pre-record a script hash in the TOFU manifest\\n' +\n ' dev-mode Toggle development mode for a script\\n' +\n ' store Store a secret (reads from stdin)\\n' +\n ' delete Delete a secret\\n' +\n ' config Manage configuration\\n' +\n ' rotate-key Rotate the encryption key\\n',\n )\n}\n\nasync function main(): Promise<number> {\n // [S1 fix] Handle --help and no-argument invocations\n if (subcommand === undefined || subcommand === '--help' || subcommand === '-h') {\n printHelp()\n return 0\n }\n\n switch (subcommand) {\n case 'exec': {\n const { execCommand } = await import('./commands/exec.js')\n return execCommand(commandArgs)\n }\n case 'doctor': {\n const { doctorCommand } = await import('./commands/doctor.js')\n return doctorCommand(commandArgs)\n }\n case 'approve': {\n const { approveCommand } = await import('./commands/approve.js')\n return approveCommand(commandArgs)\n }\n case 'dev-mode': {\n const { devModeCommand } = await import('./commands/dev-mode.js')\n return devModeCommand(commandArgs)\n }\n case 'store': {\n const { storeCommand } = await import('./commands/store.js')\n return storeCommand(commandArgs)\n }\n case 'delete': {\n const { deleteCommand } = await import('./commands/delete.js')\n return deleteCommand(commandArgs)\n }\n case 'config': {\n const { configCommand } = await import('./commands/config.js')\n return configCommand(commandArgs)\n }\n case 'rotate-key': {\n const { rotateKeyCommand } = await import('./commands/rotate-key.js')\n return rotateKeyCommand(commandArgs)\n }\n default:\n process.stderr.write(`Unknown command: ${subcommand}\\n`)\n printHelp()\n return 1\n }\n}\n\nmain()\n .then((code) => {\n process.exitCode = code\n })\n .catch((err: unknown) => {\n process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\\n`)\n process.exitCode = 1\n })\n"],"mappings":";;;;AAcA,SAAS,iBAAiB;AAE1B,IAAM,EAAE,YAAY,IAAI,UAAU;AAAA,EAChC,kBAAkB;AAAA,EAClB,QAAQ;AACV,CAAC;AAED,IAAM,aAAa,YAAY,CAAC;AAEhC,IAAM,cAAc,QAAQ,KAAK,MAAM,CAAC;AAExC,SAAS,YAAkB;AACzB,UAAQ,OAAO;AAAA,IACb;AAAA,EAUF;AACF;AAEA,eAAe,OAAwB;AAErC,MAAI,eAAe,UAAa,eAAe,YAAY,eAAe,MAAM;AAC9E,cAAU;AACV,WAAO;AAAA,EACT;AAEA,UAAQ,YAAY;AAAA,IAClB,KAAK,QAAQ;AACX,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAoB;AACzD,aAAO,YAAY,WAAW;AAAA,IAChC;AAAA,IACA,KAAK,UAAU;AACb,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAsB;AAC7D,aAAO,cAAc,WAAW;AAAA,IAClC;AAAA,IACA,KAAK,WAAW;AACd,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,uBAAuB;AAC/D,aAAO,eAAe,WAAW;AAAA,IACnC;AAAA,IACA,KAAK,YAAY;AACf,YAAM,EAAE,eAAe,IAAI,MAAM,OAAO,wBAAwB;AAChE,aAAO,eAAe,WAAW;AAAA,IACnC;AAAA,IACA,KAAK,SAAS;AACZ,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,qBAAqB;AAC3D,aAAO,aAAa,WAAW;AAAA,IACjC;AAAA,IACA,KAAK,UAAU;AACb,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAsB;AAC7D,aAAO,cAAc,WAAW;AAAA,IAClC;AAAA,IACA,KAAK,UAAU;AACb,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,sBAAsB;AAC7D,aAAO,cAAc,WAAW;AAAA,IAClC;AAAA,IACA,KAAK,cAAc;AACjB,YAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,0BAA0B;AACpE,aAAO,iBAAiB,WAAW;AAAA,IACrC;AAAA,IACA;AACE,cAAQ,OAAO,MAAM,oBAAoB,UAAU;AAAA,CAAI;AACvD,gBAAU;AACV,aAAO;AAAA,EACX;AACF;AAEA,KAAK,EACF,KAAK,CAAC,SAAS;AACd,UAAQ,WAAW;AACrB,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,UAAQ,OAAO,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AACnF,UAAQ,WAAW;AACrB,CAAC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/output.ts"],"sourcesContent":["/**\n * Formatted output helpers for CLI display.\n *\n * @internal\n */\n\n/** Check if stdout is a TTY at call time (not module load time). */\nfunction isTTY(): boolean {\n return process.stdout.isTTY\n}\n\n/** Wrap text in ANSI bold if stdout is a TTY. */\nexport function bold(text: string): string {\n return isTTY() ? `\\x1b[1m${text}\\x1b[22m` : text\n}\n\n/** Wrap text in ANSI dim if stdout is a TTY. */\nexport function dim(text: string): string {\n return isTTY() ? `\\x1b[2m${text}\\x1b[22m` : text\n}\n\n/** Format an error for display on stderr. */\nexport function formatError(err: unknown): string {\n if (err instanceof Error) {\n return `${err.name}: ${err.message}`\n }\n return String(err)\n}\n"],"mappings":";;;AAsBO,SAAS,YAAY,KAAsB;AAChD,MAAI,eAAe,OAAO;AACxB,WAAO,GAAG,IAAI,IAAI,KAAK,IAAI,OAAO;AAAA,EACpC;AACA,SAAO,OAAO,GAAG;AACnB;","names":[]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
formatError
|
|
4
|
+
} from "./chunk-FFQCXUYN.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/config.ts
|
|
7
|
+
import { parseArgs } from "util";
|
|
8
|
+
import * as fs from "fs/promises";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import * as os from "os";
|
|
11
|
+
function getDefaultConfigDir() {
|
|
12
|
+
if (process.platform === "win32") {
|
|
13
|
+
const appData = process.env.APPDATA;
|
|
14
|
+
if (appData !== void 0) {
|
|
15
|
+
return path.join(appData, "vaultkeeper");
|
|
16
|
+
}
|
|
17
|
+
return path.join(os.homedir(), "AppData", "Roaming", "vaultkeeper");
|
|
18
|
+
}
|
|
19
|
+
return path.join(os.homedir(), ".config", "vaultkeeper");
|
|
20
|
+
}
|
|
21
|
+
var DEFAULT_CONFIG = JSON.stringify(
|
|
22
|
+
{
|
|
23
|
+
version: 1,
|
|
24
|
+
backends: [{ type: "keychain", enabled: true }],
|
|
25
|
+
keyRotation: { gracePeriodDays: 7 },
|
|
26
|
+
defaults: { ttlMinutes: 60, trustTier: 3 }
|
|
27
|
+
},
|
|
28
|
+
null,
|
|
29
|
+
2
|
|
30
|
+
);
|
|
31
|
+
async function configCommand(args) {
|
|
32
|
+
const { positionals } = parseArgs({
|
|
33
|
+
args,
|
|
34
|
+
allowPositionals: true,
|
|
35
|
+
strict: false
|
|
36
|
+
});
|
|
37
|
+
const subcommand = positionals[0];
|
|
38
|
+
switch (subcommand) {
|
|
39
|
+
case "init": {
|
|
40
|
+
try {
|
|
41
|
+
const configDir = getDefaultConfigDir();
|
|
42
|
+
const configPath = path.join(configDir, "config.json");
|
|
43
|
+
await fs.mkdir(configDir, { recursive: true, mode: 448 });
|
|
44
|
+
try {
|
|
45
|
+
await fs.access(configPath);
|
|
46
|
+
process.stderr.write(`Config already exists at ${configPath}
|
|
47
|
+
`);
|
|
48
|
+
return 1;
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
await fs.writeFile(configPath, DEFAULT_CONFIG + "\n", { encoding: "utf8", mode: 384 });
|
|
52
|
+
process.stdout.write(`Config created at ${configPath}
|
|
53
|
+
`);
|
|
54
|
+
return 0;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
process.stderr.write(`${formatError(err)}
|
|
57
|
+
`);
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
case "show": {
|
|
62
|
+
try {
|
|
63
|
+
const configDir = getDefaultConfigDir();
|
|
64
|
+
const configPath = path.join(configDir, "config.json");
|
|
65
|
+
const content = await fs.readFile(configPath, "utf8");
|
|
66
|
+
process.stdout.write(content);
|
|
67
|
+
if (!content.endsWith("\n")) {
|
|
68
|
+
process.stdout.write("\n");
|
|
69
|
+
}
|
|
70
|
+
return 0;
|
|
71
|
+
} catch (err) {
|
|
72
|
+
process.stderr.write(`${formatError(err)}
|
|
73
|
+
`);
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
default:
|
|
78
|
+
process.stderr.write("Usage: vaultkeeper config <init|show>\n");
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
configCommand
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=config-FDKYUDXX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/config.ts"],"sourcesContent":["import { parseArgs } from 'node:util'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport { formatError } from '../output.js'\n\nfunction getDefaultConfigDir(): string {\n if (process.platform === 'win32') {\n const appData = process.env.APPDATA\n if (appData !== undefined) {\n return path.join(appData, 'vaultkeeper')\n }\n return path.join(os.homedir(), 'AppData', 'Roaming', 'vaultkeeper')\n }\n return path.join(os.homedir(), '.config', 'vaultkeeper')\n}\n\nconst DEFAULT_CONFIG = JSON.stringify(\n {\n version: 1,\n backends: [{ type: 'keychain', enabled: true }],\n keyRotation: { gracePeriodDays: 7 },\n defaults: { ttlMinutes: 60, trustTier: 3 },\n },\n null,\n 2,\n)\n\nexport async function configCommand(args: string[]): Promise<number> {\n const { positionals } = parseArgs({\n args,\n allowPositionals: true,\n strict: false,\n })\n\n const subcommand = positionals[0]\n\n switch (subcommand) {\n case 'init': {\n try {\n const configDir = getDefaultConfigDir()\n const configPath = path.join(configDir, 'config.json')\n // [W4 fix] Create config directory with restrictive permissions\n await fs.mkdir(configDir, { recursive: true, mode: 0o700 })\n\n try {\n await fs.access(configPath)\n process.stderr.write(`Config already exists at ${configPath}\\n`)\n return 1\n } catch {\n // File doesn't exist — create it\n }\n\n await fs.writeFile(configPath, DEFAULT_CONFIG + '\\n', { encoding: 'utf8', mode: 0o600 })\n process.stdout.write(`Config created at ${configPath}\\n`)\n return 0\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n }\n\n case 'show': {\n try {\n const configDir = getDefaultConfigDir()\n const configPath = path.join(configDir, 'config.json')\n const content = await fs.readFile(configPath, 'utf8')\n process.stdout.write(content)\n if (!content.endsWith('\\n')) {\n process.stdout.write('\\n')\n }\n return 0\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n }\n\n default:\n process.stderr.write('Usage: vaultkeeper config <init|show>\\n')\n return 1\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,iBAAiB;AAC1B,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,QAAQ;AAGpB,SAAS,sBAA8B;AACrC,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAM,UAAU,QAAQ,IAAI;AAC5B,QAAI,YAAY,QAAW;AACzB,aAAY,UAAK,SAAS,aAAa;AAAA,IACzC;AACA,WAAY,UAAQ,WAAQ,GAAG,WAAW,WAAW,aAAa;AAAA,EACpE;AACA,SAAY,UAAQ,WAAQ,GAAG,WAAW,aAAa;AACzD;AAEA,IAAM,iBAAiB,KAAK;AAAA,EAC1B;AAAA,IACE,SAAS;AAAA,IACT,UAAU,CAAC,EAAE,MAAM,YAAY,SAAS,KAAK,CAAC;AAAA,IAC9C,aAAa,EAAE,iBAAiB,EAAE;AAAA,IAClC,UAAU,EAAE,YAAY,IAAI,WAAW,EAAE;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AACF;AAEA,eAAsB,cAAc,MAAiC;AACnE,QAAM,EAAE,YAAY,IAAI,UAAU;AAAA,IAChC;AAAA,IACA,kBAAkB;AAAA,IAClB,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,aAAa,YAAY,CAAC;AAEhC,UAAQ,YAAY;AAAA,IAClB,KAAK,QAAQ;AACX,UAAI;AACF,cAAM,YAAY,oBAAoB;AACtC,cAAM,aAAkB,UAAK,WAAW,aAAa;AAErD,cAAS,SAAM,WAAW,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAE1D,YAAI;AACF,gBAAS,UAAO,UAAU;AAC1B,kBAAQ,OAAO,MAAM,4BAA4B,UAAU;AAAA,CAAI;AAC/D,iBAAO;AAAA,QACT,QAAQ;AAAA,QAER;AAEA,cAAS,aAAU,YAAY,iBAAiB,MAAM,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AACvF,gBAAQ,OAAO,MAAM,qBAAqB,UAAU;AAAA,CAAI;AACxD,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,gBAAQ,OAAO,MAAM,GAAG,YAAY,GAAG,CAAC;AAAA,CAAI;AAC5C,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,KAAK,QAAQ;AACX,UAAI;AACF,cAAM,YAAY,oBAAoB;AACtC,cAAM,aAAkB,UAAK,WAAW,aAAa;AACrD,cAAM,UAAU,MAAS,YAAS,YAAY,MAAM;AACpD,gBAAQ,OAAO,MAAM,OAAO;AAC5B,YAAI,CAAC,QAAQ,SAAS,IAAI,GAAG;AAC3B,kBAAQ,OAAO,MAAM,IAAI;AAAA,QAC3B;AACA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,gBAAQ,OAAO,MAAM,GAAG,YAAY,GAAG,CAAC;AAAA,CAAI;AAC5C,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA;AACE,cAAQ,OAAO,MAAM,yCAAyC;AAC9D,aAAO;AAAA,EACX;AACF;","names":[]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
formatError
|
|
4
|
+
} from "./chunk-FFQCXUYN.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/delete.ts
|
|
7
|
+
import { parseArgs } from "util";
|
|
8
|
+
import { BackendRegistry, VaultKeeper } from "vaultkeeper";
|
|
9
|
+
async function deleteCommand(args) {
|
|
10
|
+
const { values } = parseArgs({
|
|
11
|
+
args,
|
|
12
|
+
options: {
|
|
13
|
+
name: { type: "string" }
|
|
14
|
+
},
|
|
15
|
+
strict: true
|
|
16
|
+
});
|
|
17
|
+
if (values.name === void 0) {
|
|
18
|
+
process.stderr.write("Error: --name is required\n");
|
|
19
|
+
process.stderr.write("Usage: vaultkeeper delete --name <name>\n");
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
await VaultKeeper.init();
|
|
24
|
+
const types = BackendRegistry.getTypes();
|
|
25
|
+
const firstType = types[0];
|
|
26
|
+
if (firstType === void 0) {
|
|
27
|
+
process.stderr.write("Error: No backends available\n");
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
const backend = BackendRegistry.create(firstType);
|
|
31
|
+
await backend.delete(values.name);
|
|
32
|
+
process.stdout.write(`Secret "${values.name}" deleted.
|
|
33
|
+
`);
|
|
34
|
+
return 0;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
process.stderr.write(`${formatError(err)}
|
|
37
|
+
`);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
deleteCommand
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=delete-WNUM2ART.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/delete.ts"],"sourcesContent":["import { parseArgs } from 'node:util'\nimport { BackendRegistry, VaultKeeper } from 'vaultkeeper'\nimport { formatError } from '../output.js'\n\nexport async function deleteCommand(args: string[]): Promise<number> {\n const { values } = parseArgs({\n args,\n options: {\n name: { type: 'string' },\n },\n strict: true,\n })\n\n if (values.name === undefined) {\n process.stderr.write('Error: --name is required\\n')\n process.stderr.write('Usage: vaultkeeper delete --name <name>\\n')\n return 1\n }\n\n try {\n // Initialize vault to ensure backends are registered and doctor passes\n await VaultKeeper.init()\n\n const types = BackendRegistry.getTypes()\n const firstType = types[0]\n if (firstType === undefined) {\n process.stderr.write('Error: No backends available\\n')\n return 1\n }\n\n const backend = BackendRegistry.create(firstType)\n await backend.delete(values.name)\n process.stdout.write(`Secret \"${values.name}\" deleted.\\n`)\n return 0\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB,mBAAmB;AAG7C,eAAsB,cAAc,MAAiC;AACnE,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B;AAAA,IACA,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,SAAS;AAAA,IACzB;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,OAAO,SAAS,QAAW;AAC7B,YAAQ,OAAO,MAAM,6BAA6B;AAClD,YAAQ,OAAO,MAAM,2CAA2C;AAChE,WAAO;AAAA,EACT;AAEA,MAAI;AAEF,UAAM,YAAY,KAAK;AAEvB,UAAM,QAAQ,gBAAgB,SAAS;AACvC,UAAM,YAAY,MAAM,CAAC;AACzB,QAAI,cAAc,QAAW;AAC3B,cAAQ,OAAO,MAAM,gCAAgC;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,gBAAgB,OAAO,SAAS;AAChD,UAAM,QAAQ,OAAO,OAAO,IAAI;AAChC,YAAQ,OAAO,MAAM,WAAW,OAAO,IAAI;AAAA,CAAc;AACzD,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,GAAG,YAAY,GAAG,CAAC;AAAA,CAAI;AAC5C,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
formatError
|
|
4
|
+
} from "./chunk-FFQCXUYN.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/dev-mode.ts
|
|
7
|
+
import { parseArgs } from "util";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import { VaultKeeper } from "vaultkeeper";
|
|
10
|
+
async function devModeCommand(args) {
|
|
11
|
+
const { positionals, values } = parseArgs({
|
|
12
|
+
args,
|
|
13
|
+
allowPositionals: true,
|
|
14
|
+
options: {
|
|
15
|
+
script: { type: "string" }
|
|
16
|
+
},
|
|
17
|
+
strict: true
|
|
18
|
+
});
|
|
19
|
+
const action = positionals[0];
|
|
20
|
+
if (action !== "enable" && action !== "disable" || values.script === void 0) {
|
|
21
|
+
process.stderr.write("Usage: vaultkeeper dev-mode <enable|disable> --script <path>\n");
|
|
22
|
+
return 1;
|
|
23
|
+
}
|
|
24
|
+
const scriptPath = path.resolve(values.script);
|
|
25
|
+
const enabled = action === "enable";
|
|
26
|
+
try {
|
|
27
|
+
const vault = await VaultKeeper.init();
|
|
28
|
+
await vault.setDevelopmentMode(scriptPath, enabled);
|
|
29
|
+
process.stdout.write(
|
|
30
|
+
`Development mode ${enabled ? "enabled" : "disabled"} for ${scriptPath}
|
|
31
|
+
`
|
|
32
|
+
);
|
|
33
|
+
return 0;
|
|
34
|
+
} catch (err) {
|
|
35
|
+
process.stderr.write(`${formatError(err)}
|
|
36
|
+
`);
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
devModeCommand
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=dev-mode-QKWOGXIZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/dev-mode.ts"],"sourcesContent":["import { parseArgs } from 'node:util'\nimport * as path from 'node:path'\nimport { VaultKeeper } from 'vaultkeeper'\nimport { formatError } from '../output.js'\n\nexport async function devModeCommand(args: string[]): Promise<number> {\n const { positionals, values } = parseArgs({\n args,\n allowPositionals: true,\n options: {\n script: { type: 'string' },\n },\n strict: true,\n })\n\n const action = positionals[0]\n\n if ((action !== 'enable' && action !== 'disable') || values.script === undefined) {\n process.stderr.write('Usage: vaultkeeper dev-mode <enable|disable> --script <path>\\n')\n return 1\n }\n\n const scriptPath = path.resolve(values.script)\n const enabled = action === 'enable'\n\n try {\n const vault = await VaultKeeper.init()\n await vault.setDevelopmentMode(scriptPath, enabled)\n process.stdout.write(\n `Development mode ${enabled ? 'enabled' : 'disabled'} for ${scriptPath}\\n`,\n )\n return 0\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,iBAAiB;AAC1B,YAAY,UAAU;AACtB,SAAS,mBAAmB;AAG5B,eAAsB,eAAe,MAAiC;AACpE,QAAM,EAAE,aAAa,OAAO,IAAI,UAAU;AAAA,IACxC;AAAA,IACA,kBAAkB;AAAA,IAClB,SAAS;AAAA,MACP,QAAQ,EAAE,MAAM,SAAS;AAAA,IAC3B;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,SAAS,YAAY,CAAC;AAE5B,MAAK,WAAW,YAAY,WAAW,aAAc,OAAO,WAAW,QAAW;AAChF,YAAQ,OAAO,MAAM,gEAAgE;AACrF,WAAO;AAAA,EACT;AAEA,QAAM,aAAkB,aAAQ,OAAO,MAAM;AAC7C,QAAM,UAAU,WAAW;AAE3B,MAAI;AACF,UAAM,QAAQ,MAAM,YAAY,KAAK;AACrC,UAAM,MAAM,mBAAmB,YAAY,OAAO;AAClD,YAAQ,OAAO;AAAA,MACb,oBAAoB,UAAU,YAAY,UAAU,QAAQ,UAAU;AAAA;AAAA,IACxE;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,GAAG,YAAY,GAAG,CAAC;AAAA,CAAI;AAC5C,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
formatError
|
|
4
|
+
} from "./chunk-FFQCXUYN.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/doctor.ts
|
|
7
|
+
import { VaultKeeper } from "vaultkeeper";
|
|
8
|
+
async function doctorCommand(_args) {
|
|
9
|
+
try {
|
|
10
|
+
const result = await VaultKeeper.doctor();
|
|
11
|
+
for (const check of result.checks) {
|
|
12
|
+
const icon = check.status === "ok" ? "\u2713" : "\u2717";
|
|
13
|
+
const version = check.version !== void 0 ? ` (${check.version})` : "";
|
|
14
|
+
const reason = check.reason !== void 0 ? ` \u2014 ${check.reason}` : "";
|
|
15
|
+
process.stdout.write(` ${icon} ${check.name}${version}${reason}
|
|
16
|
+
`);
|
|
17
|
+
}
|
|
18
|
+
if (result.warnings.length > 0) {
|
|
19
|
+
process.stdout.write("\nWarnings:\n");
|
|
20
|
+
for (const warning of result.warnings) {
|
|
21
|
+
process.stdout.write(` \u26A0 ${warning}
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (result.ready) {
|
|
26
|
+
process.stdout.write("\nSystem ready.\n");
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
process.stdout.write("\nNext steps:\n");
|
|
30
|
+
for (const step of result.nextSteps) {
|
|
31
|
+
process.stdout.write(` \u2192 ${step}
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
return 1;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
process.stderr.write(`${formatError(err)}
|
|
37
|
+
`);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
doctorCommand
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=doctor-O5MH7VDR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/doctor.ts"],"sourcesContent":["import { VaultKeeper } from 'vaultkeeper'\nimport { formatError } from '../output.js'\n\nexport async function doctorCommand(_args: string[]): Promise<number> {\n try {\n const result = await VaultKeeper.doctor()\n\n for (const check of result.checks) {\n const icon = check.status === 'ok' ? '✓' : '✗'\n const version = check.version !== undefined ? ` (${check.version})` : ''\n const reason = check.reason !== undefined ? ` — ${check.reason}` : ''\n process.stdout.write(` ${icon} ${check.name}${version}${reason}\\n`)\n }\n\n if (result.warnings.length > 0) {\n process.stdout.write('\\nWarnings:\\n')\n for (const warning of result.warnings) {\n process.stdout.write(` ⚠ ${warning}\\n`)\n }\n }\n\n if (result.ready) {\n process.stdout.write('\\nSystem ready.\\n')\n return 0\n }\n\n process.stdout.write('\\nNext steps:\\n')\n for (const step of result.nextSteps) {\n process.stdout.write(` → ${step}\\n`)\n }\n return 1\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,mBAAmB;AAG5B,eAAsB,cAAc,OAAkC;AACpE,MAAI;AACF,UAAM,SAAS,MAAM,YAAY,OAAO;AAExC,eAAW,SAAS,OAAO,QAAQ;AACjC,YAAM,OAAO,MAAM,WAAW,OAAO,WAAM;AAC3C,YAAM,UAAU,MAAM,YAAY,SAAY,KAAK,MAAM,OAAO,MAAM;AACtE,YAAM,SAAS,MAAM,WAAW,SAAY,WAAM,MAAM,MAAM,KAAK;AACnE,cAAQ,OAAO,MAAM,KAAK,IAAI,IAAI,MAAM,IAAI,GAAG,OAAO,GAAG,MAAM;AAAA,CAAI;AAAA,IACrE;AAEA,QAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,cAAQ,OAAO,MAAM,eAAe;AACpC,iBAAW,WAAW,OAAO,UAAU;AACrC,gBAAQ,OAAO,MAAM,YAAO,OAAO;AAAA,CAAI;AAAA,MACzC;AAAA,IACF;AAEA,QAAI,OAAO,OAAO;AAChB,cAAQ,OAAO,MAAM,mBAAmB;AACxC,aAAO;AAAA,IACT;AAEA,YAAQ,OAAO,MAAM,iBAAiB;AACtC,eAAW,QAAQ,OAAO,WAAW;AACnC,cAAQ,OAAO,MAAM,YAAO,IAAI;AAAA,CAAI;AAAA,IACtC;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,GAAG,YAAY,GAAG,CAAC;AAAA,CAAI;AAC5C,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
formatError
|
|
4
|
+
} from "./chunk-FFQCXUYN.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/exec.ts
|
|
7
|
+
import { parseArgs } from "util";
|
|
8
|
+
import { spawn } from "child_process";
|
|
9
|
+
import * as path2 from "path";
|
|
10
|
+
import { VaultKeeper } from "vaultkeeper";
|
|
11
|
+
|
|
12
|
+
// src/approval.ts
|
|
13
|
+
import * as readline from "readline";
|
|
14
|
+
async function promptApproval(info) {
|
|
15
|
+
if (!process.stdin.isTTY) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"Secret access requires interactive approval. Run this command in a terminal."
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const lines = [
|
|
21
|
+
"\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510",
|
|
22
|
+
"\u2502 Secret Access Request \u2502",
|
|
23
|
+
"\u2502 \u2502",
|
|
24
|
+
`\u2502 Caller: ${pad(info.caller)}\u2502`,
|
|
25
|
+
`\u2502 Trust: ${pad(info.trustInfo)}\u2502`,
|
|
26
|
+
`\u2502 Secret: ${pad(info.secret)}\u2502`
|
|
27
|
+
];
|
|
28
|
+
if (info.reason !== void 0) {
|
|
29
|
+
lines.push(`\u2502 Reason: ${pad(info.reason)}\u2502`);
|
|
30
|
+
}
|
|
31
|
+
lines.push(
|
|
32
|
+
"\u2502 \u2502",
|
|
33
|
+
"\u2502 Allow? [y/N] \u2502",
|
|
34
|
+
"\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"
|
|
35
|
+
);
|
|
36
|
+
process.stderr.write(lines.join("\n") + "\n");
|
|
37
|
+
const answer = await readLine();
|
|
38
|
+
return answer.trim().toLowerCase() === "y";
|
|
39
|
+
}
|
|
40
|
+
function pad(text) {
|
|
41
|
+
const maxWidth = 37;
|
|
42
|
+
if (text.length >= maxWidth) {
|
|
43
|
+
return text.slice(0, maxWidth - 3) + "...";
|
|
44
|
+
}
|
|
45
|
+
return text + " ".repeat(maxWidth - text.length);
|
|
46
|
+
}
|
|
47
|
+
function readLine() {
|
|
48
|
+
return new Promise((resolve2) => {
|
|
49
|
+
const rl = readline.createInterface({
|
|
50
|
+
input: process.stdin,
|
|
51
|
+
output: process.stderr
|
|
52
|
+
});
|
|
53
|
+
rl.question("", (answer) => {
|
|
54
|
+
rl.close();
|
|
55
|
+
resolve2(answer);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/cache.ts
|
|
61
|
+
import * as crypto from "crypto";
|
|
62
|
+
import * as fs from "fs/promises";
|
|
63
|
+
import * as path from "path";
|
|
64
|
+
import * as os from "os";
|
|
65
|
+
function getCacheDir() {
|
|
66
|
+
const xdg = process.env.XDG_RUNTIME_DIR;
|
|
67
|
+
if (xdg !== void 0 && xdg !== "") {
|
|
68
|
+
return path.join(xdg, "vaultkeeper");
|
|
69
|
+
}
|
|
70
|
+
const username = os.userInfo().username;
|
|
71
|
+
const tmpdir2 = os.tmpdir();
|
|
72
|
+
return path.join(tmpdir2, `vaultkeeper-${username}`);
|
|
73
|
+
}
|
|
74
|
+
function cacheFileName(callerPath, secretName) {
|
|
75
|
+
const hash = crypto.createHash("sha256");
|
|
76
|
+
hash.update(callerPath);
|
|
77
|
+
hash.update("\0");
|
|
78
|
+
hash.update(secretName);
|
|
79
|
+
return hash.digest("hex") + ".jwe";
|
|
80
|
+
}
|
|
81
|
+
async function readCachedToken(callerPath, secretName) {
|
|
82
|
+
const filePath = path.join(getCacheDir(), cacheFileName(callerPath, secretName));
|
|
83
|
+
try {
|
|
84
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
85
|
+
return content.length > 0 ? content : void 0;
|
|
86
|
+
} catch {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function writeCachedToken(callerPath, secretName, jwe) {
|
|
91
|
+
const dir = getCacheDir();
|
|
92
|
+
await fs.mkdir(dir, { recursive: true, mode: 448 });
|
|
93
|
+
const filePath = path.join(dir, cacheFileName(callerPath, secretName));
|
|
94
|
+
const tmpPath = filePath + `.${crypto.randomUUID()}.tmp`;
|
|
95
|
+
await fs.writeFile(tmpPath, jwe, { encoding: "utf8", mode: 384 });
|
|
96
|
+
await fs.rename(tmpPath, filePath);
|
|
97
|
+
}
|
|
98
|
+
async function invalidateCache(callerPath, secretName) {
|
|
99
|
+
const filePath = path.join(getCacheDir(), cacheFileName(callerPath, secretName));
|
|
100
|
+
try {
|
|
101
|
+
await fs.unlink(filePath);
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/redact.ts
|
|
107
|
+
import { Transform } from "stream";
|
|
108
|
+
var RedactingStream = class extends Transform {
|
|
109
|
+
#secret;
|
|
110
|
+
#replacement;
|
|
111
|
+
#tail;
|
|
112
|
+
constructor(secret, replacement = "[REDACTED]") {
|
|
113
|
+
super();
|
|
114
|
+
this.#secret = secret;
|
|
115
|
+
this.#replacement = replacement;
|
|
116
|
+
this.#tail = "";
|
|
117
|
+
}
|
|
118
|
+
_transform(chunk, _encoding, callback) {
|
|
119
|
+
if (this.#secret.length === 0) {
|
|
120
|
+
this.push(chunk);
|
|
121
|
+
callback();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const str = this.#tail + chunk.toString("utf8");
|
|
125
|
+
const redacted = str.replaceAll(this.#secret, this.#replacement);
|
|
126
|
+
const bufferSize = this.#secret.length - 1;
|
|
127
|
+
if (redacted.length > bufferSize) {
|
|
128
|
+
this.#tail = redacted.slice(-bufferSize);
|
|
129
|
+
this.push(redacted.slice(0, -bufferSize));
|
|
130
|
+
} else {
|
|
131
|
+
this.#tail = redacted;
|
|
132
|
+
}
|
|
133
|
+
callback();
|
|
134
|
+
}
|
|
135
|
+
_flush(callback) {
|
|
136
|
+
if (this.#tail.length > 0) {
|
|
137
|
+
this.push(this.#tail);
|
|
138
|
+
this.#tail = "";
|
|
139
|
+
}
|
|
140
|
+
callback();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/commands/exec.ts
|
|
145
|
+
async function execCommand(args) {
|
|
146
|
+
const dashDashIdx = args.indexOf("--");
|
|
147
|
+
if (dashDashIdx === -1) {
|
|
148
|
+
process.stderr.write("Error: Must provide command after --\n");
|
|
149
|
+
process.stderr.write(
|
|
150
|
+
"Usage: vaultkeeper exec --secret <name> --env <VAR> --caller <path> -- <command...>\n"
|
|
151
|
+
);
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
const flagArgs = args.slice(0, dashDashIdx);
|
|
155
|
+
const command = args.slice(dashDashIdx + 1);
|
|
156
|
+
if (command.length === 0) {
|
|
157
|
+
process.stderr.write("Error: No command provided after --\n");
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
const { values } = parseArgs({
|
|
161
|
+
args: flagArgs,
|
|
162
|
+
options: {
|
|
163
|
+
secret: { type: "string" },
|
|
164
|
+
env: { type: "string" },
|
|
165
|
+
caller: { type: "string" },
|
|
166
|
+
reason: { type: "string" },
|
|
167
|
+
cache: { type: "boolean", default: false },
|
|
168
|
+
"no-redact": { type: "boolean", default: false }
|
|
169
|
+
},
|
|
170
|
+
strict: true
|
|
171
|
+
});
|
|
172
|
+
const secret = values.secret;
|
|
173
|
+
const envVar = values.env;
|
|
174
|
+
const caller = values.caller;
|
|
175
|
+
if (secret === void 0 || envVar === void 0 || caller === void 0) {
|
|
176
|
+
process.stderr.write("Error: --secret, --env, and --caller are required\n");
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
const callerPath = path2.resolve(caller);
|
|
180
|
+
const useCache = values.cache;
|
|
181
|
+
const noRedact = values["no-redact"];
|
|
182
|
+
try {
|
|
183
|
+
const vault = await VaultKeeper.init();
|
|
184
|
+
let jwe;
|
|
185
|
+
if (useCache) {
|
|
186
|
+
jwe = await readCachedToken(callerPath, secret);
|
|
187
|
+
}
|
|
188
|
+
if (jwe === void 0) {
|
|
189
|
+
const approved = await promptApproval({
|
|
190
|
+
caller: callerPath,
|
|
191
|
+
trustInfo: "Pending verification",
|
|
192
|
+
secret,
|
|
193
|
+
reason: values.reason
|
|
194
|
+
});
|
|
195
|
+
if (!approved) {
|
|
196
|
+
process.stderr.write("Access denied by user.\n");
|
|
197
|
+
return 1;
|
|
198
|
+
}
|
|
199
|
+
jwe = await vault.setup(secret, { executablePath: callerPath });
|
|
200
|
+
if (useCache) {
|
|
201
|
+
await writeCachedToken(callerPath, secret, jwe);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
let secretValue;
|
|
205
|
+
try {
|
|
206
|
+
const { token } = await vault.authorize(jwe);
|
|
207
|
+
const accessor = vault.getSecret(token);
|
|
208
|
+
accessor.read((buf) => {
|
|
209
|
+
secretValue = buf.toString("utf8");
|
|
210
|
+
});
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (useCache) {
|
|
213
|
+
await invalidateCache(callerPath, secret);
|
|
214
|
+
process.stderr.write("Cached token expired, re-authenticating...\n");
|
|
215
|
+
jwe = void 0;
|
|
216
|
+
const approved = await promptApproval({
|
|
217
|
+
caller: callerPath,
|
|
218
|
+
trustInfo: "Pending verification",
|
|
219
|
+
secret,
|
|
220
|
+
reason: values.reason
|
|
221
|
+
});
|
|
222
|
+
if (!approved) {
|
|
223
|
+
process.stderr.write("Access denied by user.\n");
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
jwe = await vault.setup(secret, { executablePath: callerPath });
|
|
227
|
+
await writeCachedToken(callerPath, secret, jwe);
|
|
228
|
+
const retryResult = await vault.authorize(jwe);
|
|
229
|
+
const retryAccessor = vault.getSecret(retryResult.token);
|
|
230
|
+
retryAccessor.read((buf) => {
|
|
231
|
+
secretValue = buf.toString("utf8");
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (secretValue === void 0) {
|
|
238
|
+
process.stderr.write("Error: Failed to read secret value\n");
|
|
239
|
+
return 1;
|
|
240
|
+
}
|
|
241
|
+
const commandName = command[0];
|
|
242
|
+
if (commandName === void 0) {
|
|
243
|
+
process.stderr.write("Error: Empty command\n");
|
|
244
|
+
return 1;
|
|
245
|
+
}
|
|
246
|
+
const child = spawn(commandName, command.slice(1), {
|
|
247
|
+
env: { ...process.env, [envVar]: secretValue },
|
|
248
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
249
|
+
});
|
|
250
|
+
if (noRedact) {
|
|
251
|
+
child.stdout.pipe(process.stdout);
|
|
252
|
+
child.stderr.pipe(process.stderr);
|
|
253
|
+
} else {
|
|
254
|
+
const stdoutRedactor = new RedactingStream(secretValue);
|
|
255
|
+
const stderrRedactor = new RedactingStream(secretValue);
|
|
256
|
+
child.stdout.pipe(stdoutRedactor).pipe(process.stdout);
|
|
257
|
+
child.stderr.pipe(stderrRedactor).pipe(process.stderr);
|
|
258
|
+
}
|
|
259
|
+
return await new Promise((resolve2, reject) => {
|
|
260
|
+
child.on("error", (err) => {
|
|
261
|
+
reject(err);
|
|
262
|
+
});
|
|
263
|
+
child.on("close", (code) => {
|
|
264
|
+
resolve2(code ?? 1);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
} catch (err) {
|
|
268
|
+
process.stderr.write(`${formatError(err)}
|
|
269
|
+
`);
|
|
270
|
+
return 1;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
export {
|
|
274
|
+
execCommand
|
|
275
|
+
};
|
|
276
|
+
//# sourceMappingURL=exec-YE2YX45A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/exec.ts","../src/approval.ts","../src/cache.ts","../src/redact.ts"],"sourcesContent":["/**\n * The `vaultkeeper exec` command — inject a secret as an env var and run a command.\n *\n * Flow:\n * 1. Parse flags and the command after `--`\n * 2. If `--cache`, check for a cached JWE token\n * 3. If no cached token: prompt for approval, THEN retrieve and setup the secret\n * 4. Authorize the JWE → obtain a CapabilityToken\n * 5. Read the secret value via the accessor\n * 6. Spawn the child process with the secret injected as an env var\n * 7. Pipe stdout/stderr through RedactingStream (unless `--no-redact`)\n * 8. Exit with the child's exit code\n *\n * Note on secret lifetime: The secret must be converted to a JS string for env\n * var injection via `child_process.spawn`. This pins it in the V8 heap beyond\n * the `SecretAccessor` callback scope. This is an accepted tradeoff — the CLI\n * spawn boundary requires a string, and RedactingStream prevents leakage in\n * output. The string is not persisted or returned.\n *\n * @internal\n */\n\nimport { parseArgs } from 'node:util'\nimport { spawn } from 'node:child_process'\nimport * as path from 'node:path'\nimport { VaultKeeper } from 'vaultkeeper'\nimport { promptApproval } from '../approval.js'\nimport { readCachedToken, writeCachedToken, invalidateCache } from '../cache.js'\nimport { RedactingStream } from '../redact.js'\nimport { formatError } from '../output.js'\n\nexport async function execCommand(args: string[]): Promise<number> {\n // Find the -- separator to split CLI flags from the wrapped command\n const dashDashIdx = args.indexOf('--')\n if (dashDashIdx === -1) {\n process.stderr.write('Error: Must provide command after --\\n')\n process.stderr.write(\n 'Usage: vaultkeeper exec --secret <name> --env <VAR> --caller <path> -- <command...>\\n',\n )\n return 1\n }\n\n const flagArgs = args.slice(0, dashDashIdx)\n const command = args.slice(dashDashIdx + 1)\n\n if (command.length === 0) {\n process.stderr.write('Error: No command provided after --\\n')\n return 1\n }\n\n const { values } = parseArgs({\n args: flagArgs,\n options: {\n secret: { type: 'string' },\n env: { type: 'string' },\n caller: { type: 'string' },\n reason: { type: 'string' },\n cache: { type: 'boolean', default: false },\n 'no-redact': { type: 'boolean', default: false },\n },\n strict: true,\n })\n\n const secret = values.secret\n const envVar = values.env\n const caller = values.caller\n\n if (secret === undefined || envVar === undefined || caller === undefined) {\n process.stderr.write('Error: --secret, --env, and --caller are required\\n')\n return 1\n }\n\n const callerPath = path.resolve(caller)\n // parseArgs with default: false types these as boolean (never undefined)\n const useCache: boolean = values.cache\n const noRedact: boolean = values['no-redact']\n\n try {\n const vault = await VaultKeeper.init()\n\n // Check cache first if --cache\n let jwe: string | undefined\n if (useCache) {\n jwe = await readCachedToken(callerPath, secret)\n }\n\n if (jwe === undefined) {\n // [C1 fix] Prompt for approval BEFORE retrieving the secret.\n // vault.setup() retrieves the secret from the backend and embeds it\n // in a JWE, so we must get user consent first.\n const approved = await promptApproval({\n caller: callerPath,\n trustInfo: 'Pending verification',\n secret,\n reason: values.reason,\n })\n\n if (!approved) {\n process.stderr.write('Access denied by user.\\n')\n return 1\n }\n\n jwe = await vault.setup(secret, { executablePath: callerPath })\n\n // Cache if requested\n if (useCache) {\n await writeCachedToken(callerPath, secret, jwe)\n }\n }\n\n // Authorize and get secret\n let secretValue: string | undefined\n try {\n const { token } = await vault.authorize(jwe)\n const accessor = vault.getSecret(token)\n accessor.read((buf) => {\n secretValue = buf.toString('utf8')\n })\n } catch (err) {\n // If cached token failed, invalidate and retry without cache\n if (useCache) {\n await invalidateCache(callerPath, secret)\n process.stderr.write('Cached token expired, re-authenticating...\\n')\n // [C2 fix] Retry by toggling the useCache flag off internally\n // rather than filtering args (which could corrupt the wrapped command).\n jwe = undefined\n const approved = await promptApproval({\n caller: callerPath,\n trustInfo: 'Pending verification',\n secret,\n reason: values.reason,\n })\n if (!approved) {\n process.stderr.write('Access denied by user.\\n')\n return 1\n }\n jwe = await vault.setup(secret, { executablePath: callerPath })\n // Write the new token back to cache so subsequent invocations benefit\n await writeCachedToken(callerPath, secret, jwe)\n const retryResult = await vault.authorize(jwe)\n const retryAccessor = vault.getSecret(retryResult.token)\n retryAccessor.read((buf) => {\n secretValue = buf.toString('utf8')\n })\n } else {\n throw err\n }\n }\n\n if (secretValue === undefined) {\n process.stderr.write('Error: Failed to read secret value\\n')\n return 1\n }\n\n // Spawn child process\n const commandName = command[0]\n if (commandName === undefined) {\n process.stderr.write('Error: Empty command\\n')\n return 1\n }\n\n const child = spawn(commandName, command.slice(1), {\n env: { ...process.env, [envVar]: secretValue },\n stdio: ['inherit', 'pipe', 'pipe'],\n })\n\n // Pipe output through redaction (or directly)\n if (noRedact) {\n child.stdout.pipe(process.stdout)\n child.stderr.pipe(process.stderr)\n } else {\n const stdoutRedactor = new RedactingStream(secretValue)\n const stderrRedactor = new RedactingStream(secretValue)\n child.stdout.pipe(stdoutRedactor).pipe(process.stdout)\n child.stderr.pipe(stderrRedactor).pipe(process.stderr)\n }\n\n // [W7 fix] Wait for child to exit, handling both 'close' and 'error' events\n return await new Promise<number>((resolve, reject) => {\n child.on('error', (err) => {\n reject(err)\n })\n child.on('close', (code) => {\n resolve(code ?? 1)\n })\n })\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n}\n","import * as readline from 'node:readline'\nimport type { ApprovalInfo } from './types.js'\n\n/**\n * Display an interactive approval prompt on the TTY.\n *\n * @param info - The access request details to display.\n * @returns `true` if the user approves, `false` otherwise.\n * @throws If stdin is not a TTY.\n *\n * @internal\n */\nexport async function promptApproval(info: ApprovalInfo): Promise<boolean> {\n if (!process.stdin.isTTY) {\n throw new Error(\n 'Secret access requires interactive approval. Run this command in a terminal.',\n )\n }\n\n const lines = [\n '┌─────────────────────────────────────────────────┐',\n '│ Secret Access Request │',\n '│ │',\n `│ Caller: ${pad(info.caller)}│`,\n `│ Trust: ${pad(info.trustInfo)}│`,\n `│ Secret: ${pad(info.secret)}│`,\n ]\n\n if (info.reason !== undefined) {\n lines.push(`│ Reason: ${pad(info.reason)}│`)\n }\n\n lines.push(\n '│ │',\n '│ Allow? [y/N] │',\n '└─────────────────────────────────────────────────┘',\n )\n\n process.stderr.write(lines.join('\\n') + '\\n')\n\n const answer = await readLine()\n return answer.trim().toLowerCase() === 'y'\n}\n\nfunction pad(text: string): string {\n const maxWidth = 37 // 49 total width - 10 prefix \"│ Label: \" - 1 trailing \"│\"\n if (text.length >= maxWidth) {\n return text.slice(0, maxWidth - 3) + '...'\n }\n return text + ' '.repeat(maxWidth - text.length)\n}\n\nfunction readLine(): Promise<string> {\n return new Promise((resolve) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n })\n rl.question('', (answer) => {\n rl.close()\n resolve(answer)\n })\n })\n}\n","import * as crypto from 'node:crypto'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\n\n/**\n * Resolve the cache directory for JWE tokens.\n *\n * - Linux: `$XDG_RUNTIME_DIR/vaultkeeper/` (tmpfs, secure)\n * - macOS/other: `$TMPDIR/vaultkeeper-<username>/`\n * - Fallback: `os.tmpdir()/vaultkeeper-<username>/`\n *\n * @internal\n */\nexport function getCacheDir(): string {\n const xdg = process.env.XDG_RUNTIME_DIR\n if (xdg !== undefined && xdg !== '') {\n return path.join(xdg, 'vaultkeeper')\n }\n\n // Use username for cross-platform compatibility (process.getuid() is\n // undefined on Windows, and uid 0 would cause all users to share a cache).\n const username = os.userInfo().username\n const tmpdir = os.tmpdir()\n return path.join(tmpdir, `vaultkeeper-${username}`)\n}\n\n/**\n * Compute a deterministic cache filename from a caller path and secret name.\n * Uses a null-byte separator to prevent collisions between paths that share\n * a prefix (e.g. \"/a\" + \"b\" vs \"/ab\" + \"\").\n */\nfunction cacheFileName(callerPath: string, secretName: string): string {\n const hash = crypto.createHash('sha256')\n hash.update(callerPath)\n hash.update('\\0')\n hash.update(secretName)\n return hash.digest('hex') + '.jwe'\n}\n\n/**\n * Read a cached JWE token for the given caller and secret.\n *\n * @returns The cached JWE string, or `undefined` if no cache exists.\n *\n * @internal\n */\nexport async function readCachedToken(\n callerPath: string,\n secretName: string,\n): Promise<string | undefined> {\n const filePath = path.join(getCacheDir(), cacheFileName(callerPath, secretName))\n try {\n const content = await fs.readFile(filePath, 'utf8')\n return content.length > 0 ? content : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Write a JWE token to the cache.\n *\n * Creates the cache directory (mode 0o700) if it does not exist.\n * Uses atomic write-then-rename to prevent race conditions where another\n * process could read a partially-written file.\n *\n * @internal\n */\nexport async function writeCachedToken(\n callerPath: string,\n secretName: string,\n jwe: string,\n): Promise<void> {\n const dir = getCacheDir()\n await fs.mkdir(dir, { recursive: true, mode: 0o700 })\n const filePath = path.join(dir, cacheFileName(callerPath, secretName))\n // Atomic write: write to a temp file with correct permissions,\n // then rename into place. rename() is atomic on POSIX filesystems.\n const tmpPath = filePath + `.${crypto.randomUUID()}.tmp`\n await fs.writeFile(tmpPath, jwe, { encoding: 'utf8', mode: 0o600 })\n await fs.rename(tmpPath, filePath)\n}\n\n/**\n * Remove a cached JWE token.\n *\n * @internal\n */\nexport async function invalidateCache(\n callerPath: string,\n secretName: string,\n): Promise<void> {\n const filePath = path.join(getCacheDir(), cacheFileName(callerPath, secretName))\n try {\n await fs.unlink(filePath)\n } catch {\n // File may not exist — that's fine\n }\n}\n","import { Transform } from 'node:stream'\nimport type { TransformCallback } from 'node:stream'\n\n/**\n * A Transform stream that replaces all occurrences of a secret value\n * with a replacement string in the piped output.\n *\n * Handles secrets that may be split across chunk boundaries by buffering\n * up to `secret.length - 1` bytes from the end of each chunk.\n *\n * @internal\n */\nexport class RedactingStream extends Transform {\n readonly #secret: string\n readonly #replacement: string\n #tail: string\n\n constructor(secret: string, replacement = '[REDACTED]') {\n super()\n this.#secret = secret\n this.#replacement = replacement\n this.#tail = ''\n }\n\n override _transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: TransformCallback,\n ): void {\n if (this.#secret.length === 0) {\n this.push(chunk)\n callback()\n return\n }\n\n const str = this.#tail + chunk.toString('utf8')\n const redacted = str.replaceAll(this.#secret, this.#replacement)\n\n // Buffer the last (secret.length - 1) chars in case the secret\n // is split across this chunk and the next one.\n const bufferSize = this.#secret.length - 1\n if (redacted.length > bufferSize) {\n this.#tail = redacted.slice(-bufferSize)\n this.push(redacted.slice(0, -bufferSize))\n } else {\n this.#tail = redacted\n }\n\n callback()\n }\n\n override _flush(callback: TransformCallback): void {\n if (this.#tail.length > 0) {\n this.push(this.#tail)\n this.#tail = ''\n }\n callback()\n }\n}\n"],"mappings":"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","names":["path","resolve","tmpdir","resolve"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Options parsed from the `vaultkeeper exec` command line. */
|
|
2
|
+
interface ExecCommandOptions {
|
|
3
|
+
/** Name of the secret to retrieve. */
|
|
4
|
+
secret: string;
|
|
5
|
+
/** Environment variable name to inject the secret as. */
|
|
6
|
+
env: string;
|
|
7
|
+
/** Path to the calling script for identity verification. */
|
|
8
|
+
caller: string;
|
|
9
|
+
/** Human-readable reason for the access request. */
|
|
10
|
+
reason?: string | undefined;
|
|
11
|
+
/** Whether to use JWE token caching. */
|
|
12
|
+
cache?: boolean | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Whether to skip output redaction.
|
|
15
|
+
* When `true`, the raw secret value may appear in stdout or stderr.
|
|
16
|
+
* Use only when piping binary data or in controlled environments.
|
|
17
|
+
*/
|
|
18
|
+
noRedact?: boolean | undefined;
|
|
19
|
+
/** The command and arguments to execute. */
|
|
20
|
+
command: string[];
|
|
21
|
+
}
|
|
22
|
+
/** Information displayed in the TTY approval prompt. */
|
|
23
|
+
interface ApprovalInfo {
|
|
24
|
+
/** Path to the calling script. */
|
|
25
|
+
caller: string;
|
|
26
|
+
/** Trust level description (e.g. "★★☆ Registry (SHA-256: a1b2c3...)"). */
|
|
27
|
+
trustInfo: string;
|
|
28
|
+
/** Name of the secret being requested. */
|
|
29
|
+
secret: string;
|
|
30
|
+
/** Human-readable reason for the access request. */
|
|
31
|
+
reason?: string | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type { ApprovalInfo, ExecCommandOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
formatError
|
|
4
|
+
} from "./chunk-FFQCXUYN.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/rotate-key.ts
|
|
7
|
+
import { VaultKeeper } from "vaultkeeper";
|
|
8
|
+
async function rotateKeyCommand(_args) {
|
|
9
|
+
try {
|
|
10
|
+
const vault = await VaultKeeper.init();
|
|
11
|
+
await vault.rotateKey();
|
|
12
|
+
process.stdout.write("Key rotated successfully.\n");
|
|
13
|
+
return 0;
|
|
14
|
+
} catch (err) {
|
|
15
|
+
process.stderr.write(`${formatError(err)}
|
|
16
|
+
`);
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
rotateKeyCommand
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=rotate-key-3PTCQYUP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/rotate-key.ts"],"sourcesContent":["import { VaultKeeper } from 'vaultkeeper'\nimport { formatError } from '../output.js'\n\nexport async function rotateKeyCommand(_args: string[]): Promise<number> {\n try {\n const vault = await VaultKeeper.init()\n await vault.rotateKey()\n process.stdout.write('Key rotated successfully.\\n')\n return 0\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,mBAAmB;AAG5B,eAAsB,iBAAiB,OAAkC;AACvE,MAAI;AACF,UAAM,QAAQ,MAAM,YAAY,KAAK;AACrC,UAAM,MAAM,UAAU;AACtB,YAAQ,OAAO,MAAM,6BAA6B;AAClD,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,GAAG,YAAY,GAAG,CAAC;AAAA,CAAI;AAC5C,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
formatError
|
|
4
|
+
} from "./chunk-FFQCXUYN.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/store.ts
|
|
7
|
+
import { parseArgs } from "util";
|
|
8
|
+
import { BackendRegistry, VaultKeeper } from "vaultkeeper";
|
|
9
|
+
async function storeCommand(args) {
|
|
10
|
+
const { values } = parseArgs({
|
|
11
|
+
args,
|
|
12
|
+
options: {
|
|
13
|
+
name: { type: "string" }
|
|
14
|
+
},
|
|
15
|
+
strict: true
|
|
16
|
+
});
|
|
17
|
+
if (values.name === void 0) {
|
|
18
|
+
process.stderr.write("Error: --name is required\n");
|
|
19
|
+
process.stderr.write('Usage: echo "secret" | vaultkeeper store --name <name>\n');
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const chunks = [];
|
|
24
|
+
for await (const chunk of process.stdin) {
|
|
25
|
+
if (chunk instanceof Buffer) {
|
|
26
|
+
chunks.push(chunk);
|
|
27
|
+
} else if (typeof chunk === "string") {
|
|
28
|
+
chunks.push(Buffer.from(chunk));
|
|
29
|
+
} else {
|
|
30
|
+
chunks.push(Buffer.from(String(chunk)));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const secret = Buffer.concat(chunks).toString("utf8").trimEnd();
|
|
34
|
+
if (secret.length === 0) {
|
|
35
|
+
process.stderr.write("Error: No secret provided on stdin\n");
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
await VaultKeeper.init();
|
|
39
|
+
const types = BackendRegistry.getTypes();
|
|
40
|
+
const firstType = types[0];
|
|
41
|
+
if (firstType === void 0) {
|
|
42
|
+
process.stderr.write("Error: No backends available\n");
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
const backend = BackendRegistry.create(firstType);
|
|
46
|
+
await backend.store(values.name, secret);
|
|
47
|
+
process.stdout.write(`Secret "${values.name}" stored successfully.
|
|
48
|
+
`);
|
|
49
|
+
return 0;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
process.stderr.write(`${formatError(err)}
|
|
52
|
+
`);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
storeCommand
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=store-K4PPONOU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/store.ts"],"sourcesContent":["import { parseArgs } from 'node:util'\nimport { BackendRegistry, VaultKeeper } from 'vaultkeeper'\nimport { formatError } from '../output.js'\n\nexport async function storeCommand(args: string[]): Promise<number> {\n const { values } = parseArgs({\n args,\n options: {\n name: { type: 'string' },\n },\n strict: true,\n })\n\n if (values.name === undefined) {\n process.stderr.write('Error: --name is required\\n')\n process.stderr.write('Usage: echo \"secret\" | vaultkeeper store --name <name>\\n')\n return 1\n }\n\n try {\n // Read secret from stdin\n const chunks: Buffer[] = []\n for await (const chunk of process.stdin) {\n if (chunk instanceof Buffer) {\n chunks.push(chunk)\n } else if (typeof chunk === 'string') {\n chunks.push(Buffer.from(chunk))\n } else {\n chunks.push(Buffer.from(String(chunk)))\n }\n }\n const secret = Buffer.concat(chunks).toString('utf8').trimEnd()\n\n if (secret.length === 0) {\n process.stderr.write('Error: No secret provided on stdin\\n')\n return 1\n }\n\n // Initialize vault to run doctor checks and register backends.\n // The return value is not used — init() is called for its side effects\n // (doctor checks and backend registration). TODO: VaultKeeper should expose\n // a public store() method; until then we use BackendRegistry.create() with\n // the config-resolved type.\n await VaultKeeper.init()\n const types = BackendRegistry.getTypes()\n const firstType = types[0]\n if (firstType === undefined) {\n process.stderr.write('Error: No backends available\\n')\n return 1\n }\n\n const backend = BackendRegistry.create(firstType)\n await backend.store(values.name, secret)\n process.stdout.write(`Secret \"${values.name}\" stored successfully.\\n`)\n return 0\n } catch (err) {\n process.stderr.write(`${formatError(err)}\\n`)\n return 1\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB,mBAAmB;AAG7C,eAAsB,aAAa,MAAiC;AAClE,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B;AAAA,IACA,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,SAAS;AAAA,IACzB;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,OAAO,SAAS,QAAW;AAC7B,YAAQ,OAAO,MAAM,6BAA6B;AAClD,YAAQ,OAAO,MAAM,0DAA0D;AAC/E,WAAO;AAAA,EACT;AAEA,MAAI;AAEF,UAAM,SAAmB,CAAC;AAC1B,qBAAiB,SAAS,QAAQ,OAAO;AACvC,UAAI,iBAAiB,QAAQ;AAC3B,eAAO,KAAK,KAAK;AAAA,MACnB,WAAW,OAAO,UAAU,UAAU;AACpC,eAAO,KAAK,OAAO,KAAK,KAAK,CAAC;AAAA,MAChC,OAAO;AACL,eAAO,KAAK,OAAO,KAAK,OAAO,KAAK,CAAC,CAAC;AAAA,MACxC;AAAA,IACF;AACA,UAAM,SAAS,OAAO,OAAO,MAAM,EAAE,SAAS,MAAM,EAAE,QAAQ;AAE9D,QAAI,OAAO,WAAW,GAAG;AACvB,cAAQ,OAAO,MAAM,sCAAsC;AAC3D,aAAO;AAAA,IACT;AAOA,UAAM,YAAY,KAAK;AACvB,UAAM,QAAQ,gBAAgB,SAAS;AACvC,UAAM,YAAY,MAAM,CAAC;AACzB,QAAI,cAAc,QAAW;AAC3B,cAAQ,OAAO,MAAM,gCAAgC;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,gBAAgB,OAAO,SAAS;AAChD,UAAM,QAAQ,MAAM,OAAO,MAAM,MAAM;AACvC,YAAQ,OAAO,MAAM,WAAW,OAAO,IAAI;AAAA,CAA0B;AACrE,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,YAAQ,OAAO,MAAM,GAAG,YAAY,GAAG,CAAC;AAAA,CAAI;AAC5C,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vaultkeeper/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Command-line interface for vaultkeeper — store, retrieve, and inject secrets with policy-enforced access control",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"vaultkeeper": "./dist/bin.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.0.0"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"secret",
|
|
25
|
+
"secrets",
|
|
26
|
+
"credential",
|
|
27
|
+
"keychain",
|
|
28
|
+
"cli",
|
|
29
|
+
"vault",
|
|
30
|
+
"security",
|
|
31
|
+
"secret-management"
|
|
32
|
+
],
|
|
33
|
+
"author": "Mike North <michael.l.north@gmail.com>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"vaultkeeper": "0.2.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@microsoft/api-extractor": "^7.57.2",
|
|
43
|
+
"@types/node": "^22.0.0",
|
|
44
|
+
"tsup": "^8.0.0",
|
|
45
|
+
"tsx": "^4.21.0"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"clean": "node -e \"import('node:fs').then(fs=>fs.rmSync('dist',{recursive:true,force:true}))\"",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"check:typecheck": "tsc --noEmit",
|
|
52
|
+
"check:lint-ts": "eslint .",
|
|
53
|
+
"check:api-report": "api-extractor run",
|
|
54
|
+
"check": "pnpm check:typecheck && pnpm check:lint-ts && pnpm check:api-report",
|
|
55
|
+
"generate:api-report": "api-extractor run --local"
|
|
56
|
+
}
|
|
57
|
+
}
|