-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcli.ts
More file actions
363 lines (332 loc) · 10.6 KB
/
cli.ts
File metadata and controls
363 lines (332 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#!/usr/bin/env node
import { program } from 'commander';
import {
checkAction,
configBackupAction,
configGetAction,
configListAction,
configMigrateAction,
configRestoreAction,
configSetAction,
configValidateAction,
copyAction,
delAction,
editAction,
exportAction,
getAction,
initAction,
listAction,
migrateAction,
setAction,
showAction,
templateAction,
} from './commands';
import { info, LogTag, verbose, warn } from './utils/logger';
const version = '0.1.0';
// Set up the main program
program
.name('envg')
.description('Local-first secret management for developers')
.version(version)
.option('-v, --verbose', 'Enable verbose logging')
.hook('preAction', (thisCommand) => {
const options: { verbose?: boolean } = thisCommand.opts();
verbose(
options.verbose === true,
LogTag.LOG,
`[EnvGuard v${version}] Running command: ${thisCommand.name()}`
);
});
// Init command
program
.command('init')
.description('Initialize EnvGuard in the current directory')
.option(
'-t, --template <path>',
'Path to template file (relative or absolute)'
)
.option('-p, --package <name>', 'Package name (skips auto-detection)')
.option('-f, --force', 'Reinitialize if already initialized')
.action(async (options) => {
await initAction(options);
});
/**
* Store a secret in the OS keychain
* @command set <key> <value>
* @param key - The key of the secret to store
* @param value - The value of the secret to store
* @option -v, --verbose - Enable verbose logging
* @option -o, --optional - Mark this secret as optional (default: required)
* @example
* ```bash
* envg set API_KEY abc123 --verbose
* envg set OPTIONAL_KEY value --optional
* ```
* @remarks
* This command stores a secret in the OS keychain using the package name from config.
* By default, secrets are marked as required. Use --optional to mark them as optional.
*
* @see {@link SystemKeychain} for the underlying keychain implementation.
*/
program
.command('set <key> <value>')
.description('Store a secret in the OS keychain')
.option('-v, --verbose', 'Enable verbose logging', false)
.option(
'-o, --optional',
'Mark this secret as optional (default: required)',
false
)
.option('-e, --env <environment>', 'Environment name (default: development)')
.action(
async (
key: string,
value: string,
options: { verbose?: boolean; optional?: boolean; env?: string }
) => {
await setAction(key, value, options);
}
);
/**
* Retrieve a secret from the OS keychain
* @command get <key>
* @param key - The key of the secret to retrieve
* @option -v, --verbose - Enable verbose logging
* @option -df, --defaultFallback <value> - Default value if secret not found
* @example
* ```bash
* envg get API_KEY --verbose --defaultFallback "default_value"
* ```
* @remarks
* This command retrieves a secret stored in the OS keychain using the package name from config.
* If the secret is not found, it can return a default fallback value if provided.
*
* @see {@link SystemKeychain} for the underlying keychain implementation.
*/
program
.command('get <key>')
.description('Retrieve a secret from the OS keychain')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('-df, --defaultFallback <value>', 'Default value if secret not found')
.option('-e, --env <environment>', 'Environment name (default: development)')
.action(
async (
key: string,
options: { verbose?: boolean; defaultFallback?: unknown; env?: string }
) => {
await getAction(key, options);
}
);
program
.command('del <key>')
.description('Delete a secret from the OS keychain')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('-e, --env <environment>', 'Environment name (default: development)')
.action(async (key: string, options: { verbose?: boolean; env?: string }) => {
await delAction(key, options);
});
program
.command('list')
.description('List all stored secrets (keys only)')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (options: { verbose: boolean }) => {
await listAction(options);
});
program
.command('check')
.description('Check secrets and security issues')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('--secrets', 'Only check secrets')
.option('--security', 'Only check security issues')
.action(
async (options: {
verbose?: boolean;
secrets?: boolean;
security?: boolean;
}) => {
await checkAction(options);
}
);
program
.command('migrate')
.description('Migrate from .env files to EnvGuard')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('--auto', 'Auto-accept all prompts')
.option('--keep-files', 'Keep .env files after migration')
.option('--from <file>', 'Migrate from specific file')
.action(
async (options: {
verbose?: boolean;
auto?: boolean;
keepFiles?: boolean;
from?: string;
}) => {
await migrateAction(options);
}
);
program
.command('export')
.description('Export secrets to .env file (INSECURE)')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('--unsafe', 'Confirm you understand the security risks', false)
.option('--to <file>', 'Output filename', '.env')
.option('-e, --env <environment>', 'Environment to export')
.action(
async (options: {
verbose?: boolean;
unsafe?: boolean;
to?: string;
env?: string;
}) => {
await exportAction(options);
}
);
program
.command('template')
.description('Generate .env.template from current secrets')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('-f, --force', 'Overwrite existing template')
.action(async (options: { verbose?: boolean; force?: boolean }) => {
await templateAction(options);
});
program
.command('edit [key]')
.description('Edit secrets interactively')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('-e, --env <environment>', 'Environment name')
.option('--add', 'Add new secret')
.action(
async (
key: string | undefined,
options: { verbose?: boolean; env?: string; add?: boolean }
) => {
await editAction(key, options);
}
);
program
.command('show <key>')
.description('Show secret value (masked by default)')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('-e, --env <environment>', 'Environment name')
.option('--reveal', 'Show actual value (unmasked)')
.action(
async (
key: string,
options: { verbose?: boolean; env?: string; reveal?: boolean }
) => {
await showAction(key, options);
}
);
program
.command('copy [key]')
.description('Copy secrets between environments')
.requiredOption('--from <environment>', 'Source environment')
.requiredOption('--to <environment>', 'Destination environment')
.option('-v, --verbose', 'Enable verbose logging', false)
.option('-f, --force', 'Overwrite existing secrets')
.action(
async (
key: string | undefined,
options: { from: string; to: string; verbose?: boolean; force?: boolean }
) => {
await copyAction(key, options);
}
);
// Config command with subcommands
const configCommand = program
.command('config')
.description('Manage EnvGuard configuration');
configCommand
.command('get <key>')
.description('Get a config value (supports dot notation)')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (key: string, options) => {
await configGetAction(key, options);
});
configCommand
.command('set <key> <value>')
.description('Set a config value (supports dot notation)')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (key: string, value: string, options) => {
await configSetAction(key, value, options);
});
configCommand
.command('list')
.description('List all config values')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (options) => {
await configListAction(options);
});
configCommand
.command('validate')
.description('Validate current config')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (options) => {
await configValidateAction(options);
});
configCommand
.command('backup')
.description('Backup current config')
.option('-o, --output <path>', 'Output path for backup file')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (options) => {
await configBackupAction(options);
});
configCommand
.command('restore <file>')
.description('Restore config from backup')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (file: string, options) => {
await configRestoreAction(file, options);
});
configCommand
.command('migrate')
.description('Migrate config from v1 to v2')
.option('--no-backup', 'Skip creating backup before migration')
.option('-v, --verbose', 'Enable verbose logging', false)
.action(async (options) => {
await configMigrateAction(options);
});
program
.command('status')
.description('Show current EnvGuard status and configuration')
.action(() => {
info('EnvGuard Status');
info(`Version: ${version}`);
info(`Node.js: ${process.version}`);
info(`Platform: ${process.platform} ${process.arch}`);
warn('Full functionality coming soon.');
info('');
info('Implementation Progress:');
info(' • Keychain integration (in progress)');
info(' • CLI commands');
info(' • Config validation');
info(' • Runtime runners');
});
// Add help examples
program.addHelpText(
'after',
`
Examples:
$ envg status Show current status
$ envg init Initialize in current directory
$ envg set API_KEY abc123 Store a secret in default environment (development)
$ envg set API_KEY xyz789 --env prod Store a secret in production environment
$ envg get API_KEY Retrieve a secret from development
$ envg get API_KEY --env production Retrieve from production environment
$ envg del API_KEY --env staging Delete from staging environment
$ envg list List all secrets
Environment Support:
Use --env to specify environment (development, staging, production, etc.)
Default environment is 'development' if not specified.
Documentation:
Visit https://github.com/amannirala13/envguard for full documentation,
guides, and examples.
`
);
// Parse CLI arguments
program.parse(process.argv);
// Show help if no arguments provided
if (process.argv.slice(2).length === 0) {
program.outputHelp();
}