lockbox-vault 1.0.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.
Files changed (50) hide show
  1. package/README.md +180 -0
  2. package/dist/cli/env-utils.d.ts +46 -0
  3. package/dist/cli/env-utils.d.ts.map +1 -0
  4. package/dist/cli/env-utils.js +97 -0
  5. package/dist/cli/env-utils.js.map +1 -0
  6. package/dist/cli/helpers.d.ts +49 -0
  7. package/dist/cli/helpers.d.ts.map +1 -0
  8. package/dist/cli/helpers.js +168 -0
  9. package/dist/cli/helpers.js.map +1 -0
  10. package/dist/cli/index.d.ts +6 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +728 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/cli/session.d.ts +52 -0
  15. package/dist/cli/session.d.ts.map +1 -0
  16. package/dist/cli/session.js +188 -0
  17. package/dist/cli/session.js.map +1 -0
  18. package/dist/crypto/encryption.d.ts +32 -0
  19. package/dist/crypto/encryption.d.ts.map +1 -0
  20. package/dist/crypto/encryption.js +54 -0
  21. package/dist/crypto/encryption.js.map +1 -0
  22. package/dist/crypto/keyDerivation.d.ts +22 -0
  23. package/dist/crypto/keyDerivation.d.ts.map +1 -0
  24. package/dist/crypto/keyDerivation.js +47 -0
  25. package/dist/crypto/keyDerivation.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +35 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/mcp/audit.d.ts +31 -0
  31. package/dist/mcp/audit.d.ts.map +1 -0
  32. package/dist/mcp/audit.js +69 -0
  33. package/dist/mcp/audit.js.map +1 -0
  34. package/dist/mcp/server.d.ts +22 -0
  35. package/dist/mcp/server.d.ts.map +1 -0
  36. package/dist/mcp/server.js +189 -0
  37. package/dist/mcp/server.js.map +1 -0
  38. package/dist/types/index.d.ts +35 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/index.js +5 -0
  41. package/dist/types/index.js.map +1 -0
  42. package/dist/vault/config.d.ts +29 -0
  43. package/dist/vault/config.d.ts.map +1 -0
  44. package/dist/vault/config.js +66 -0
  45. package/dist/vault/config.js.map +1 -0
  46. package/dist/vault/vault.d.ts +130 -0
  47. package/dist/vault/vault.d.ts.map +1 -0
  48. package/dist/vault/vault.js +296 -0
  49. package/dist/vault/vault.js.map +1 -0
  50. package/package.json +62 -0
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Lockbox
2
+
3
+ **Encrypted API key vault with MCP integration for AI coding tools.**
4
+
5
+ Lockbox stores your API keys in an AES-256-GCM encrypted vault on your machine. Access them from the CLI, pipe them into scripts, or let AI coding agents (Claude Code, Cursor, Windsurf) retrieve secrets through the Model Context Protocol — without ever putting real keys in your codebase.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npm install -g lockbox-vault
11
+
12
+ lockbox init # create your encrypted vault
13
+ lockbox add openai API_KEY sk-... # store a secret
14
+ lockbox get openai API_KEY # retrieve it
15
+ ```
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -g lockbox-vault
21
+ ```
22
+
23
+ Requires Node.js 18+.
24
+
25
+ ## CLI Commands
26
+
27
+ ### Vault Lifecycle
28
+
29
+ ```bash
30
+ lockbox init # create a new encrypted vault
31
+ lockbox unlock # unlock with your master password (15-min session)
32
+ lockbox lock # lock the vault immediately
33
+ lockbox status # show vault info, lock state, key count
34
+ lockbox doctor # run health checks on the vault
35
+ ```
36
+
37
+ ### Secret Management
38
+
39
+ ```bash
40
+ lockbox add openai API_KEY sk-abc123 # store a secret
41
+ lockbox add stripe SECRET --project myapp # organize by project
42
+ lockbox add aws ACCESS_KEY # prompts for value (hidden)
43
+ lockbox get openai API_KEY # print to stdout
44
+ lockbox get openai API_KEY --copy # copy to clipboard (clears in 30s)
45
+ lockbox remove openai API_KEY # delete (with confirmation)
46
+ lockbox list # show all keys (never values)
47
+ lockbox list --project myapp # filter by project
48
+ lockbox search stripe # search by name, service, or notes
49
+ ```
50
+
51
+ ### Import & Export
52
+
53
+ ```bash
54
+ lockbox import .env # import from .env file
55
+ lockbox import creds.json --project myapp # import from JSON
56
+ lockbox export > .env # export as .env format
57
+ lockbox export --format json # export as JSON
58
+ lockbox export --format shell >> ~/.bashrc # export as shell exports
59
+ ```
60
+
61
+ ### Proxy Key System
62
+
63
+ The proxy key system lets you commit `.env` files to git without exposing real secrets. Instead of actual values, your env file contains `lockbox://` references that are resolved at runtime.
64
+
65
+ ```bash
66
+ # generate a .env.proxy file (safe to commit)
67
+ lockbox proxy-init --project myapp
68
+
69
+ # the generated file looks like:
70
+ # OPENAI_API_KEY=lockbox://openai/API_KEY
71
+ # STRIPE_SECRET_KEY=lockbox://stripe/SECRET_KEY
72
+
73
+ # run your app with real values injected
74
+ lockbox run --env-file .env.proxy "npm start"
75
+ lockbox run --env-file .env.proxy "python app.py"
76
+ ```
77
+
78
+ Non-proxy values (like `DATABASE_URL=postgresql://localhost/mydb`) are passed through unchanged.
79
+
80
+ ### Audit & Security
81
+
82
+ ```bash
83
+ lockbox audit # show last 50 audit entries
84
+ lockbox audit --since 2025-01-01 # filter by date
85
+ lockbox audit -n 10 # limit entries
86
+ ```
87
+
88
+ ## MCP Setup
89
+
90
+ Lockbox exposes your vault to AI coding agents via the Model Context Protocol. The MCP server provides 6 tools: `store_secret`, `get_secret`, `list_secrets`, `delete_secret`, `export_env`, and `list_projects`.
91
+
92
+ **Prerequisites:** Unlock your vault before using MCP tools:
93
+
94
+ ```bash
95
+ lockbox unlock
96
+ ```
97
+
98
+ ### Claude Code
99
+
100
+ Add to your project's `.mcp.json`:
101
+
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "lockbox": {
106
+ "command": "npx",
107
+ "args": ["lockbox-mcp"]
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Cursor
114
+
115
+ Add to `.cursor/mcp.json` in your project root (or `~/.cursor/mcp.json` for global):
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "lockbox": {
121
+ "command": "npx",
122
+ "args": ["lockbox-mcp"]
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ ### Windsurf
129
+
130
+ Add to `~/.codeium/windsurf/mcp_config.json`:
131
+
132
+ ```json
133
+ {
134
+ "mcpServers": {
135
+ "lockbox": {
136
+ "command": "npx",
137
+ "args": ["lockbox-mcp"]
138
+ }
139
+ }
140
+ }
141
+ ```
142
+
143
+ ## Security Model
144
+
145
+ | Layer | Implementation |
146
+ |-------|---------------|
147
+ | **Encryption** | AES-256-GCM with 12-byte random IV (fresh on every save) |
148
+ | **Key Derivation** | Argon2id (64 MB memory, 3 iterations, 4 threads) |
149
+ | **Integrity** | HMAC-SHA256 verified before decryption attempt |
150
+ | **Session** | Derived key encrypted to temp file, auto-expires after 15 minutes |
151
+ | **Rate Limiting** | 5 failed unlock attempts per 60s triggers 60s lockout |
152
+ | **Audit Trail** | Append-only log of all operations (values never logged) |
153
+ | **Clipboard** | Auto-clears after 30 seconds when using `--copy` |
154
+ | **Memory** | Derived key zeroed on lock; secrets only in memory while unlocked |
155
+
156
+ ### Vault File Format
157
+
158
+ The vault is a single encrypted JSON file at `~/.config/lockbox/vault.enc`:
159
+
160
+ ```
161
+ { salt, iv, tag, ciphertext, hmac } (all base64-encoded)
162
+ ```
163
+
164
+ The decrypted payload contains versioned key entries with per-key metadata (project, notes, timestamps, expiry).
165
+
166
+ ## Contributing
167
+
168
+ Contributions are welcome! Please open an issue or submit a pull request on [GitHub](https://github.com/brentschroeter/lockbox).
169
+
170
+ ```bash
171
+ git clone https://github.com/brentschroeter/lockbox.git
172
+ cd lockbox
173
+ npm install
174
+ npm run build
175
+ npm test
176
+ ```
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Shared .env parsing, key-name conversion, and proxy URI utilities
3
+ */
4
+ /** Proxy URI prefix */
5
+ export declare const PROXY_PREFIX = "lockbox://";
6
+ /**
7
+ * Convert a vault key name (e.g. "openai/API_KEY") to an env-style name.
8
+ * "openai/API_KEY" → "OPENAI_API_KEY"
9
+ */
10
+ export declare function envKeyName(name: string): string;
11
+ /**
12
+ * Split a key name into [service, keyName].
13
+ * If no "/" present, defaults to service "imported".
14
+ */
15
+ export declare function splitKeyName(key: string): [string, string];
16
+ /**
17
+ * Parse a .env file into key-value pairs.
18
+ * Supports: KEY=value, KEY="value", KEY='value'
19
+ * Skips blank lines and # comments.
20
+ */
21
+ export declare function parseEnvFile(raw: string): {
22
+ key: string;
23
+ value: string;
24
+ }[];
25
+ /**
26
+ * Parse a JSON file into key-value pairs.
27
+ * Expects a flat { "KEY": "value" } object.
28
+ */
29
+ export declare function parseJsonImport(raw: string): {
30
+ key: string;
31
+ value: string;
32
+ }[];
33
+ /**
34
+ * Parse a lockbox proxy URI: "lockbox://service/KEY_NAME"
35
+ * Returns { service, keyName } or null if the value is not a proxy URI.
36
+ */
37
+ export declare function parseProxyUri(value: string): {
38
+ service: string;
39
+ keyName: string;
40
+ } | null;
41
+ /**
42
+ * Build a proxy URI from service and key name.
43
+ * "openai", "API_KEY" → "lockbox://openai/API_KEY"
44
+ */
45
+ export declare function buildProxyUri(service: string, keyName: string): string;
46
+ //# sourceMappingURL=env-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-utils.d.ts","sourceRoot":"","sources":["../../src/cli/env-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,uBAAuB;AACvB,eAAO,MAAM,YAAY,eAAe,CAAC;AAEzC;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAW1D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CA4B1E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAU7E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgBxF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtE"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Shared .env parsing, key-name conversion, and proxy URI utilities
3
+ */
4
+ /** Proxy URI prefix */
5
+ export const PROXY_PREFIX = 'lockbox://';
6
+ /**
7
+ * Convert a vault key name (e.g. "openai/API_KEY") to an env-style name.
8
+ * "openai/API_KEY" → "OPENAI_API_KEY"
9
+ */
10
+ export function envKeyName(name) {
11
+ return name.replace(/\//g, '_').toUpperCase();
12
+ }
13
+ /**
14
+ * Split a key name into [service, keyName].
15
+ * If no "/" present, defaults to service "imported".
16
+ */
17
+ export function splitKeyName(key) {
18
+ if (key.includes('/')) {
19
+ const idx = key.indexOf('/');
20
+ return [key.slice(0, idx), key.slice(idx + 1)];
21
+ }
22
+ // Best guess: split on first underscore for service (e.g. OPENAI_API_KEY → openai, API_KEY)
23
+ const parts = key.split('_');
24
+ if (parts.length >= 2) {
25
+ return [parts[0].toLowerCase(), parts.slice(1).join('_')];
26
+ }
27
+ return ['imported', key];
28
+ }
29
+ /**
30
+ * Parse a .env file into key-value pairs.
31
+ * Supports: KEY=value, KEY="value", KEY='value'
32
+ * Skips blank lines and # comments.
33
+ */
34
+ export function parseEnvFile(raw) {
35
+ const entries = [];
36
+ for (const line of raw.split('\n')) {
37
+ const trimmed = line.trim();
38
+ // Skip empty lines and comments
39
+ if (!trimmed || trimmed.startsWith('#'))
40
+ continue;
41
+ // Skip lines without '='
42
+ const eqIdx = trimmed.indexOf('=');
43
+ if (eqIdx === -1)
44
+ continue;
45
+ const key = trimmed.slice(0, eqIdx).trim();
46
+ let value = trimmed.slice(eqIdx + 1).trim();
47
+ // Strip surrounding quotes
48
+ if ((value.startsWith('"') && value.endsWith('"')) ||
49
+ (value.startsWith("'") && value.endsWith("'"))) {
50
+ value = value.slice(1, -1);
51
+ }
52
+ if (key) {
53
+ entries.push({ key, value });
54
+ }
55
+ }
56
+ return entries;
57
+ }
58
+ /**
59
+ * Parse a JSON file into key-value pairs.
60
+ * Expects a flat { "KEY": "value" } object.
61
+ */
62
+ export function parseJsonImport(raw) {
63
+ const obj = JSON.parse(raw);
64
+ if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
65
+ throw new Error('JSON import expects a flat { "KEY": "value" } object');
66
+ }
67
+ return Object.entries(obj).map(([key, value]) => ({
68
+ key,
69
+ value: String(value),
70
+ }));
71
+ }
72
+ /**
73
+ * Parse a lockbox proxy URI: "lockbox://service/KEY_NAME"
74
+ * Returns { service, keyName } or null if the value is not a proxy URI.
75
+ */
76
+ export function parseProxyUri(value) {
77
+ if (!value.startsWith(PROXY_PREFIX)) {
78
+ return null;
79
+ }
80
+ const path = value.slice(PROXY_PREFIX.length);
81
+ const slashIdx = path.indexOf('/');
82
+ if (slashIdx === -1 || slashIdx === 0 || slashIdx === path.length - 1) {
83
+ return null; // malformed: "lockbox://", "lockbox:///KEY", "lockbox://service/"
84
+ }
85
+ return {
86
+ service: path.slice(0, slashIdx),
87
+ keyName: path.slice(slashIdx + 1),
88
+ };
89
+ }
90
+ /**
91
+ * Build a proxy URI from service and key name.
92
+ * "openai", "API_KEY" → "lockbox://openai/API_KEY"
93
+ */
94
+ export function buildProxyUri(service, keyName) {
95
+ return `${PROXY_PREFIX}${service}/${keyName}`;
96
+ }
97
+ //# sourceMappingURL=env-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-utils.js","sourceRoot":"","sources":["../../src/cli/env-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * CLI helper utilities — prompts, clipboard, table formatting, colours
3
+ */
4
+ import chalk from 'chalk';
5
+ /**
6
+ * Prompt for a password with hidden (muted) input.
7
+ */
8
+ export declare function promptPassword(message: string): Promise<string>;
9
+ /**
10
+ * Prompt for a password and confirm it matches.
11
+ * @returns The confirmed password
12
+ * @throws If passwords don't match
13
+ */
14
+ export declare function promptPasswordWithConfirm(): Promise<string>;
15
+ /**
16
+ * Ask a yes/no confirmation question.
17
+ */
18
+ export declare function promptConfirm(message: string): Promise<boolean>;
19
+ /**
20
+ * Copy text to the system clipboard (platform-aware).
21
+ */
22
+ export declare function copyToClipboard(text: string): Promise<void>;
23
+ /**
24
+ * Copy value to clipboard and schedule auto-clear after N seconds.
25
+ * Keeps the process alive until the clipboard is cleared.
26
+ */
27
+ export declare function copyAndScheduleClear(value: string, seconds: number): Promise<void>;
28
+ /**
29
+ * Format data as a simple aligned table.
30
+ */
31
+ export declare function formatTable(headers: string[], rows: string[][]): string;
32
+ /**
33
+ * Print an error message and exit.
34
+ */
35
+ export declare function exitWithError(message: string): never;
36
+ /**
37
+ * Format an ISO date string as a short readable date.
38
+ */
39
+ export declare function shortDate(iso: string | null): string;
40
+ /**
41
+ * Prompt the user to choose from a set of options.
42
+ * Returns the key of the chosen option.
43
+ */
44
+ export declare function promptChoice(message: string, choices: {
45
+ key: string;
46
+ label: string;
47
+ }[]): Promise<string>;
48
+ export { chalk };
49
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBrE;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,MAAM,CAAC,CAajE;AAID;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYrE;AAID;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB3D;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWxF;AAID;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,CAiBvE;AAID;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAGpD;AAID;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAQpD;AAID;;;GAGG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAGD,OAAO,EAAE,KAAK,EAAE,CAAC"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * CLI helper utilities — prompts, clipboard, table formatting, colours
3
+ */
4
+ import { createInterface } from 'node:readline';
5
+ import { Writable } from 'node:stream';
6
+ import { exec } from 'node:child_process';
7
+ import { platform } from 'node:os';
8
+ import chalk from 'chalk';
9
+ // ─── Password prompt (hidden input) ─────────────────────────────────────────
10
+ /**
11
+ * Prompt for a password with hidden (muted) input.
12
+ */
13
+ export async function promptPassword(message) {
14
+ // Create a muted output so readline doesn't echo typed characters
15
+ const muted = new Writable({
16
+ write(_chunk, _encoding, callback) {
17
+ callback();
18
+ },
19
+ });
20
+ const rl = createInterface({
21
+ input: process.stdin,
22
+ output: muted,
23
+ terminal: true,
24
+ });
25
+ process.stderr.write(message);
26
+ return new Promise((resolve) => {
27
+ rl.question('', (answer) => {
28
+ rl.close();
29
+ process.stderr.write('\n');
30
+ resolve(answer);
31
+ });
32
+ });
33
+ }
34
+ /**
35
+ * Prompt for a password and confirm it matches.
36
+ * @returns The confirmed password
37
+ * @throws If passwords don't match
38
+ */
39
+ export async function promptPasswordWithConfirm() {
40
+ const pw1 = await promptPassword(chalk.cyan('Enter master password: '));
41
+ const pw2 = await promptPassword(chalk.cyan('Confirm master password: '));
42
+ if (pw1 !== pw2) {
43
+ throw new Error('Passwords do not match');
44
+ }
45
+ if (pw1.length < 8) {
46
+ throw new Error('Password must be at least 8 characters');
47
+ }
48
+ return pw1;
49
+ }
50
+ // ─── Confirm prompt ──────────────────────────────────────────────────────────
51
+ /**
52
+ * Ask a yes/no confirmation question.
53
+ */
54
+ export async function promptConfirm(message) {
55
+ const rl = createInterface({
56
+ input: process.stdin,
57
+ output: process.stderr,
58
+ });
59
+ return new Promise((resolve) => {
60
+ rl.question(`${message} (y/N): `, (answer) => {
61
+ rl.close();
62
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
63
+ });
64
+ });
65
+ }
66
+ // ─── Clipboard ───────────────────────────────────────────────────────────────
67
+ /**
68
+ * Copy text to the system clipboard (platform-aware).
69
+ */
70
+ export function copyToClipboard(text) {
71
+ return new Promise((resolve, reject) => {
72
+ const p = platform();
73
+ let cmd;
74
+ if (p === 'win32') {
75
+ cmd = 'clip';
76
+ }
77
+ else if (p === 'darwin') {
78
+ cmd = 'pbcopy';
79
+ }
80
+ else {
81
+ cmd = 'xclip -selection clipboard';
82
+ }
83
+ const child = exec(cmd, (err) => {
84
+ if (err)
85
+ reject(err);
86
+ else
87
+ resolve();
88
+ });
89
+ child.stdin?.write(text);
90
+ child.stdin?.end();
91
+ });
92
+ }
93
+ /**
94
+ * Copy value to clipboard and schedule auto-clear after N seconds.
95
+ * Keeps the process alive until the clipboard is cleared.
96
+ */
97
+ export async function copyAndScheduleClear(value, seconds) {
98
+ await copyToClipboard(value);
99
+ process.stderr.write(chalk.green(`✓ Copied to clipboard. Auto-clearing in ${seconds}s...\n`));
100
+ return new Promise((resolve) => {
101
+ setTimeout(async () => {
102
+ await copyToClipboard('');
103
+ process.stderr.write(chalk.dim(`✓ Clipboard cleared.\n`));
104
+ resolve();
105
+ }, seconds * 1000);
106
+ });
107
+ }
108
+ // ─── Table formatting ────────────────────────────────────────────────────────
109
+ /**
110
+ * Format data as a simple aligned table.
111
+ */
112
+ export function formatTable(headers, rows) {
113
+ // Calculate column widths
114
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || '').length)));
115
+ const divider = widths.map((w) => '─'.repeat(w + 2)).join('┼');
116
+ const headerLine = headers
117
+ .map((h, i) => chalk.bold(h.padEnd(widths[i])))
118
+ .join(' │ ');
119
+ const body = rows
120
+ .map((row) => row.map((cell, i) => (cell || '').padEnd(widths[i])).join(' │ '))
121
+ .join('\n');
122
+ return `${headerLine}\n${divider}\n${body}`;
123
+ }
124
+ // ─── Error formatting ────────────────────────────────────────────────────────
125
+ /**
126
+ * Print an error message and exit.
127
+ */
128
+ export function exitWithError(message) {
129
+ process.stderr.write(chalk.red(`✗ ${message}\n`));
130
+ process.exit(1);
131
+ }
132
+ // ─── Date formatting ─────────────────────────────────────────────────────────
133
+ /**
134
+ * Format an ISO date string as a short readable date.
135
+ */
136
+ export function shortDate(iso) {
137
+ if (!iso)
138
+ return '—';
139
+ const d = new Date(iso);
140
+ return d.toLocaleDateString('en-US', {
141
+ year: 'numeric',
142
+ month: 'short',
143
+ day: 'numeric',
144
+ });
145
+ }
146
+ // ─── Choice prompt ───────────────────────────────────────────────────────────
147
+ /**
148
+ * Prompt the user to choose from a set of options.
149
+ * Returns the key of the chosen option.
150
+ */
151
+ export async function promptChoice(message, choices) {
152
+ const rl = createInterface({
153
+ input: process.stdin,
154
+ output: process.stderr,
155
+ });
156
+ const optionStr = choices.map((c) => c.label).join('/');
157
+ return new Promise((resolve) => {
158
+ rl.question(`${message} (${optionStr}): `, (answer) => {
159
+ rl.close();
160
+ const lower = answer.toLowerCase().trim();
161
+ const match = choices.find((c) => c.key === lower || c.label.toLowerCase().startsWith(lower));
162
+ resolve(match ? match.key : choices[0].key);
163
+ });
164
+ });
165
+ }
166
+ // Re-export chalk for use in commands
167
+ export { chalk };
168
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * CLI command registration — wires up all lockbox commands
3
+ */
4
+ import type { Command } from 'commander';
5
+ export declare function registerCommands(program: Command): void;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+CzC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4xBvD"}