env-secrets 0.3.3 → 0.5.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.
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.resolveAwsScope = exports.parseEnvSecretsFile = exports.parseEnvSecrets = exports.resolveSecretValue = exports.readStdin = exports.parseRecoveryDays = exports.printData = exports.renderTable = exports.asOutputFormat = void 0;
13
+ const promises_1 = require("node:fs/promises");
14
+ const asOutputFormat = (value) => {
15
+ if (value !== 'json' && value !== 'table') {
16
+ throw new Error(`Invalid output format "${value}". Use "json" or "table".`);
17
+ }
18
+ return value;
19
+ };
20
+ exports.asOutputFormat = asOutputFormat;
21
+ const renderTable = (headers, rows) => {
22
+ if (rows.length === 0) {
23
+ return 'No results.';
24
+ }
25
+ const widths = headers.map((header) => {
26
+ return Math.max(header.label.length, ...rows.map((row) => String(row[header.key] || '').length));
27
+ });
28
+ const headerLine = headers
29
+ .map((header, index) => header.label.padEnd(widths[index]))
30
+ .join(' ');
31
+ const divider = headers
32
+ .map((_, index) => '-'.repeat(widths[index]))
33
+ .join(' ');
34
+ const lines = rows.map((row) => headers
35
+ .map((header, index) => String(row[header.key] || '').padEnd(widths[index]))
36
+ .join(' '));
37
+ return [headerLine, divider, ...lines].join('\n');
38
+ };
39
+ exports.renderTable = renderTable;
40
+ const printData = (format, headers, rows) => {
41
+ if (format === 'json') {
42
+ // eslint-disable-next-line no-console
43
+ console.log(JSON.stringify(rows, null, 2));
44
+ return;
45
+ }
46
+ // eslint-disable-next-line no-console
47
+ console.log((0, exports.renderTable)(headers, rows));
48
+ };
49
+ exports.printData = printData;
50
+ const parseRecoveryDays = (value) => {
51
+ const parsed = Number(value);
52
+ if (!Number.isInteger(parsed) || parsed < 7 || parsed > 30) {
53
+ throw new Error('Recovery days must be an integer between 7 and 30.');
54
+ }
55
+ return parsed;
56
+ };
57
+ exports.parseRecoveryDays = parseRecoveryDays;
58
+ const readStdin = (stdin = process.stdin) => __awaiter(void 0, void 0, void 0, function* () {
59
+ const chunks = [];
60
+ return yield new Promise((resolve, reject) => {
61
+ const onData = (chunk) => {
62
+ chunks.push(chunk);
63
+ };
64
+ const onEnd = () => {
65
+ cleanup();
66
+ resolve(Buffer.concat(chunks)
67
+ .toString('utf8')
68
+ .replace(/\r?\n$/, ''));
69
+ };
70
+ const onError = (error) => {
71
+ cleanup();
72
+ reject(error);
73
+ };
74
+ const cleanup = () => {
75
+ stdin.off('data', onData);
76
+ stdin.off('end', onEnd);
77
+ stdin.off('error', onError);
78
+ };
79
+ stdin.on('data', onData);
80
+ stdin.once('end', onEnd);
81
+ stdin.once('error', onError);
82
+ });
83
+ });
84
+ exports.readStdin = readStdin;
85
+ const resolveSecretValue = (value, valueStdin, valueFile) => __awaiter(void 0, void 0, void 0, function* () {
86
+ const providedSources = [
87
+ value !== undefined,
88
+ valueStdin === true,
89
+ valueFile !== undefined
90
+ ].filter(Boolean).length;
91
+ if (providedSources > 1) {
92
+ throw new Error('Use only one secret value source: --value, --value-stdin, or --file.');
93
+ }
94
+ if (valueStdin) {
95
+ if (process.stdin.isTTY) {
96
+ throw new Error('No stdin detected. Pipe a value when using --value-stdin.');
97
+ }
98
+ return yield (0, exports.readStdin)();
99
+ }
100
+ if (valueFile) {
101
+ const content = yield (0, promises_1.readFile)(valueFile, 'utf8');
102
+ return content.replace(/\r?\n$/, '');
103
+ }
104
+ return value;
105
+ });
106
+ exports.resolveSecretValue = resolveSecretValue;
107
+ const parseEnvLine = (line, lineNumber) => {
108
+ const trimmed = line.trim();
109
+ if (!trimmed || trimmed.startsWith('#')) {
110
+ return undefined;
111
+ }
112
+ const candidate = trimmed.startsWith('export ')
113
+ ? trimmed.slice('export '.length).trimStart()
114
+ : trimmed;
115
+ const separatorIndex = candidate.indexOf('=');
116
+ if (separatorIndex <= 0) {
117
+ throw new Error(`Malformed env line ${lineNumber}. Expected KEY=value or export KEY=value.`);
118
+ }
119
+ const key = candidate.slice(0, separatorIndex).trim();
120
+ const value = candidate.slice(separatorIndex + 1).trim();
121
+ if (!key) {
122
+ throw new Error(`Malformed env line ${lineNumber}. Expected KEY=value or export KEY=value.`);
123
+ }
124
+ return { key, value };
125
+ };
126
+ const parseEnvSecrets = (content) => {
127
+ const seenKeys = new Set();
128
+ const entries = [];
129
+ const skipped = [];
130
+ const lines = content.split(/\r?\n/);
131
+ for (let index = 0; index < lines.length; index += 1) {
132
+ const parsed = parseEnvLine(lines[index], index + 1);
133
+ if (!parsed) {
134
+ continue;
135
+ }
136
+ if (seenKeys.has(parsed.key)) {
137
+ skipped.push({
138
+ key: parsed.key,
139
+ line: index + 1,
140
+ reason: 'duplicate key'
141
+ });
142
+ continue;
143
+ }
144
+ seenKeys.add(parsed.key);
145
+ entries.push({
146
+ key: parsed.key,
147
+ value: parsed.value,
148
+ line: index + 1
149
+ });
150
+ }
151
+ return { entries, skipped };
152
+ };
153
+ exports.parseEnvSecrets = parseEnvSecrets;
154
+ const parseEnvSecretsFile = (path) => __awaiter(void 0, void 0, void 0, function* () {
155
+ const content = yield (0, promises_1.readFile)(path, 'utf8');
156
+ return (0, exports.parseEnvSecrets)(content);
157
+ });
158
+ exports.parseEnvSecretsFile = parseEnvSecretsFile;
159
+ const resolveAwsScope = (options, command) => {
160
+ var _a;
161
+ const globalOptions = ((_a = command === null || command === void 0 ? void 0 : command.optsWithGlobals) === null || _a === void 0 ? void 0 : _a.call(command)) || {};
162
+ const profile = options.profile ||
163
+ (typeof globalOptions.profile === 'string'
164
+ ? globalOptions.profile
165
+ : undefined);
166
+ const region = options.region ||
167
+ (typeof globalOptions.region === 'string'
168
+ ? globalOptions.region
169
+ : undefined);
170
+ return { profile, region };
171
+ };
172
+ exports.resolveAwsScope = resolveAwsScope;
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ /* istanbul ignore file */
3
4
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
5
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
6
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -19,24 +20,47 @@ const node_fs_1 = require("node:fs");
19
20
  const debug_1 = __importDefault(require("debug"));
20
21
  const version_1 = require("./version");
21
22
  const secretsmanager_1 = require("./vaults/secretsmanager");
23
+ const secretsmanager_admin_1 = require("./vaults/secretsmanager-admin");
24
+ const helpers_1 = require("./cli/helpers");
22
25
  const utils_1 = require("./vaults/utils");
23
26
  const debug = (0, debug_1.default)('env-secrets');
24
27
  const program = new commander_1.Command();
28
+ const exitWithError = (error) => {
29
+ // eslint-disable-next-line no-console
30
+ console.error(error instanceof Error ? error.message : String(error));
31
+ process.exit(1);
32
+ };
33
+ const parseSecretJsonObject = (secretName, value) => {
34
+ let parsed;
35
+ try {
36
+ parsed = JSON.parse(value);
37
+ }
38
+ catch (_a) {
39
+ throw new Error(`Secret "${secretName}" is not valid JSON. append/remove requires a JSON object secret.`);
40
+ }
41
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') {
42
+ throw new Error(`Secret "${secretName}" must be a JSON object. append/remove does not support arrays or scalar values.`);
43
+ }
44
+ return parsed;
45
+ };
25
46
  // main program
26
47
  program
27
48
  .name('env-secrets')
28
49
  .description('pull secrets from vaults and inject them into the running environment')
29
50
  .version(version_1.LIB_VERSION);
30
51
  // aws secretsmanager
31
- program
52
+ const awsCommand = program
32
53
  .command('aws')
33
54
  .description('get secrets from AWS secrets manager')
34
55
  .addArgument(new commander_1.Argument('[program...]', 'program to run'))
35
- .requiredOption('-s, --secret <secret>', 'secret to get')
56
+ .option('-s, --secret <secret>', 'secret to get')
36
57
  .option('-p, --profile <profile>', 'profile to use')
37
58
  .option('-r, --region <region>', 'region to use')
38
59
  .option('-o, --output <file>', 'output secrets to file instead of environment variables')
39
60
  .action((program, options) => __awaiter(void 0, void 0, void 0, function* () {
61
+ if (!options.secret) {
62
+ exitWithError(new Error('Missing required option --secret for this command.'));
63
+ }
40
64
  const secrets = yield (0, secretsmanager_1.secretsmanager)(options);
41
65
  debug(secrets);
42
66
  if (options.output) {
@@ -72,4 +96,434 @@ program
72
96
  }
73
97
  }
74
98
  }));
99
+ const secretCommand = awsCommand
100
+ .command('secret')
101
+ .description('manage AWS secrets');
102
+ secretCommand
103
+ .command('create')
104
+ .description('create a secret in AWS Secrets Manager')
105
+ .requiredOption('-n, --name <name>', 'secret name')
106
+ .option('-v, --value <value>', 'secret value')
107
+ .option('--value-stdin', 'read secret value from stdin')
108
+ .option('-f, --file <path>', 'read secret value from local file')
109
+ .option('-d, --description <description>', 'secret description')
110
+ .option('-k, --kms-key-id <kmsKeyId>', 'kms key id')
111
+ .option('-t, --tag <tag...>', 'tag in key=value format')
112
+ .option('-p, --profile <profile>', 'profile to use')
113
+ .option('-r, --region <region>', 'region to use')
114
+ .option('--output <format>', 'output format: json|table')
115
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
116
+ var _a;
117
+ try {
118
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
119
+ const globalOptions = command.optsWithGlobals();
120
+ const output = (_a = options.output) !== null && _a !== void 0 ? _a : (typeof globalOptions.output === 'string'
121
+ ? globalOptions.output
122
+ : 'table');
123
+ const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
124
+ if (!value) {
125
+ throw new Error('Secret value is required. Provide --value, --value-stdin, or --file.');
126
+ }
127
+ const result = yield (0, secretsmanager_admin_1.createSecret)({
128
+ name: options.name,
129
+ value,
130
+ description: options.description,
131
+ kmsKeyId: options.kmsKeyId,
132
+ tags: options.tag,
133
+ profile,
134
+ region
135
+ });
136
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
137
+ { key: 'name', label: 'Name' },
138
+ { key: 'arn', label: 'ARN' },
139
+ { key: 'versionId', label: 'VersionId' }
140
+ ], [result]);
141
+ }
142
+ catch (error) {
143
+ exitWithError(error);
144
+ }
145
+ }));
146
+ secretCommand
147
+ .command('update')
148
+ .description('update secret value or metadata')
149
+ .requiredOption('-n, --name <name>', 'secret name')
150
+ .option('-v, --value <value>', 'new secret value')
151
+ .option('--value-stdin', 'read secret value from stdin')
152
+ .option('-f, --file <path>', 'read secret value from local file')
153
+ .option('-d, --description <description>', 'secret description')
154
+ .option('-k, --kms-key-id <kmsKeyId>', 'kms key id')
155
+ .option('-p, --profile <profile>', 'profile to use')
156
+ .option('-r, --region <region>', 'region to use')
157
+ .option('--output <format>', 'output format: json|table')
158
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
159
+ var _b;
160
+ try {
161
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
162
+ const globalOptions = command.optsWithGlobals();
163
+ const output = (_b = options.output) !== null && _b !== void 0 ? _b : (typeof globalOptions.output === 'string'
164
+ ? globalOptions.output
165
+ : 'table');
166
+ const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
167
+ if (!value && !options.description && !options.kmsKeyId) {
168
+ throw new Error('Nothing to update. Provide --value/--value-stdin/--file, --description, or --kms-key-id.');
169
+ }
170
+ const result = yield (0, secretsmanager_admin_1.updateSecret)({
171
+ name: options.name,
172
+ value,
173
+ description: options.description,
174
+ kmsKeyId: options.kmsKeyId,
175
+ profile,
176
+ region
177
+ });
178
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
179
+ { key: 'name', label: 'Name' },
180
+ { key: 'arn', label: 'ARN' },
181
+ { key: 'versionId', label: 'VersionId' }
182
+ ], [result]);
183
+ }
184
+ catch (error) {
185
+ exitWithError(error);
186
+ }
187
+ }));
188
+ secretCommand
189
+ .command('upsert')
190
+ .alias('import')
191
+ .description('create or update a secret from a local env file')
192
+ .requiredOption('-f, --file <path>', 'path to env file')
193
+ .requiredOption('-n, --name <name>', 'secret name')
194
+ .option('-d, --description <description>', 'secret description')
195
+ .option('-k, --kms-key-id <kmsKeyId>', 'kms key id')
196
+ .option('-t, --tag <tag...>', 'tag in key=value format (applies on create)')
197
+ .option('-p, --profile <profile>', 'profile to use')
198
+ .option('-r, --region <region>', 'region to use')
199
+ .option('--output <format>', 'output format: json|table')
200
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
201
+ var _c;
202
+ try {
203
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
204
+ const globalOptions = command.optsWithGlobals();
205
+ const output = (_c = options.output) !== null && _c !== void 0 ? _c : (typeof globalOptions.output === 'string'
206
+ ? globalOptions.output
207
+ : 'table');
208
+ const parsed = yield (0, helpers_1.parseEnvSecretsFile)(options.file);
209
+ if (parsed.entries.length === 0) {
210
+ throw new Error('No env entries found. Include lines like KEY=value or export KEY=value.');
211
+ }
212
+ const payload = Object.fromEntries(parsed.entries.map((entry) => [entry.key, entry.value]));
213
+ const value = JSON.stringify(payload);
214
+ const rows = [];
215
+ let created = 0;
216
+ let updated = 0;
217
+ let failed = 0;
218
+ const skipped = parsed.skipped.length;
219
+ for (const skip of parsed.skipped) {
220
+ rows.push({
221
+ name: options.name,
222
+ status: 'skipped',
223
+ line: String(skip.line),
224
+ message: `${skip.reason}: ${skip.key}`
225
+ });
226
+ }
227
+ try {
228
+ try {
229
+ yield (0, secretsmanager_admin_1.createSecret)({
230
+ name: options.name,
231
+ value,
232
+ description: options.description,
233
+ kmsKeyId: options.kmsKeyId,
234
+ tags: options.tag,
235
+ profile,
236
+ region
237
+ });
238
+ created += 1;
239
+ rows.push({
240
+ name: options.name,
241
+ status: 'created',
242
+ message: `imported ${parsed.entries.length} keys`
243
+ });
244
+ }
245
+ catch (createError) {
246
+ const message = createError instanceof Error
247
+ ? createError.message
248
+ : String(createError);
249
+ if (!/already exists/i.test(message)) {
250
+ throw createError;
251
+ }
252
+ yield (0, secretsmanager_admin_1.updateSecret)({
253
+ name: options.name,
254
+ value,
255
+ description: options.description,
256
+ kmsKeyId: options.kmsKeyId,
257
+ profile,
258
+ region
259
+ });
260
+ updated += 1;
261
+ rows.push({
262
+ name: options.name,
263
+ status: 'updated',
264
+ message: `imported ${parsed.entries.length} keys`
265
+ });
266
+ }
267
+ }
268
+ catch (error) {
269
+ failed += 1;
270
+ rows.push({
271
+ name: options.name,
272
+ status: 'failed',
273
+ message: error instanceof Error ? error.message : String(error)
274
+ });
275
+ }
276
+ const summary = { created, updated, skipped, failed };
277
+ if (output === 'json') {
278
+ // eslint-disable-next-line no-console
279
+ console.log(JSON.stringify({ summary, results: rows }, null, 2));
280
+ if (failed > 0) {
281
+ process.exitCode = 1;
282
+ }
283
+ return;
284
+ }
285
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
286
+ { key: 'name', label: 'Name' },
287
+ { key: 'status', label: 'Status' },
288
+ { key: 'line', label: 'Line' },
289
+ { key: 'message', label: 'Message' }
290
+ ], rows);
291
+ // eslint-disable-next-line no-console
292
+ console.log(`Summary: created=${created}, updated=${updated}, skipped=${skipped}, failed=${failed}`);
293
+ if (failed > 0) {
294
+ process.exitCode = 1;
295
+ }
296
+ }
297
+ catch (error) {
298
+ exitWithError(error);
299
+ }
300
+ }));
301
+ secretCommand
302
+ .command('append')
303
+ .description('append or overwrite one key in an existing JSON secret')
304
+ .requiredOption('-n, --name <name>', 'secret name')
305
+ .requiredOption('--key <key>', 'key to append/update')
306
+ .option('-v, --value <value>', 'value for the key')
307
+ .option('--value-stdin', 'read value from stdin')
308
+ .option('-f, --file <path>', 'read value from local file')
309
+ .option('-p, --profile <profile>', 'profile to use')
310
+ .option('-r, --region <region>', 'region to use')
311
+ .option('--output <format>', 'output format: json|table')
312
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
313
+ var _d;
314
+ try {
315
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
316
+ const globalOptions = command.optsWithGlobals();
317
+ const output = (_d = options.output) !== null && _d !== void 0 ? _d : (typeof globalOptions.output === 'string'
318
+ ? globalOptions.output
319
+ : 'table');
320
+ const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
321
+ if (!value) {
322
+ throw new Error('Append value is required. Provide --value, --value-stdin, or --file.');
323
+ }
324
+ const current = yield (0, secretsmanager_admin_1.getSecretString)({
325
+ name: options.name,
326
+ profile,
327
+ region
328
+ });
329
+ const payload = parseSecretJsonObject(options.name, current);
330
+ payload[options.key] = value;
331
+ const result = yield (0, secretsmanager_admin_1.updateSecret)({
332
+ name: options.name,
333
+ value: JSON.stringify(payload),
334
+ profile,
335
+ region
336
+ });
337
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
338
+ { key: 'name', label: 'Name' },
339
+ { key: 'arn', label: 'ARN' },
340
+ { key: 'versionId', label: 'VersionId' },
341
+ { key: 'key', label: 'Key' },
342
+ { key: 'action', label: 'Action' }
343
+ ], [
344
+ Object.assign(Object.assign({}, result), { key: options.key, action: 'appended' })
345
+ ]);
346
+ }
347
+ catch (error) {
348
+ exitWithError(error);
349
+ }
350
+ }));
351
+ secretCommand
352
+ .command('remove')
353
+ .description('remove one or more keys from an existing JSON secret')
354
+ .requiredOption('-n, --name <name>', 'secret name')
355
+ .requiredOption('--key <key...>', 'one or more keys to remove')
356
+ .option('-p, --profile <profile>', 'profile to use')
357
+ .option('-r, --region <region>', 'region to use')
358
+ .option('--output <format>', 'output format: json|table')
359
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
360
+ var _e;
361
+ try {
362
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
363
+ const globalOptions = command.optsWithGlobals();
364
+ const output = (_e = options.output) !== null && _e !== void 0 ? _e : (typeof globalOptions.output === 'string'
365
+ ? globalOptions.output
366
+ : 'table');
367
+ const keys = options.key;
368
+ const current = yield (0, secretsmanager_admin_1.getSecretString)({
369
+ name: options.name,
370
+ profile,
371
+ region
372
+ });
373
+ const payload = parseSecretJsonObject(options.name, current);
374
+ const removed = [];
375
+ const missing = [];
376
+ for (const key of keys) {
377
+ if (Object.prototype.hasOwnProperty.call(payload, key)) {
378
+ delete payload[key];
379
+ removed.push(key);
380
+ }
381
+ else {
382
+ missing.push(key);
383
+ }
384
+ }
385
+ if (removed.length === 0) {
386
+ throw new Error(`None of the requested keys exist in secret "${options.name}".`);
387
+ }
388
+ const result = yield (0, secretsmanager_admin_1.updateSecret)({
389
+ name: options.name,
390
+ value: JSON.stringify(payload),
391
+ profile,
392
+ region
393
+ });
394
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
395
+ { key: 'name', label: 'Name' },
396
+ { key: 'arn', label: 'ARN' },
397
+ { key: 'versionId', label: 'VersionId' },
398
+ { key: 'removed', label: 'RemovedKeys' },
399
+ { key: 'missing', label: 'MissingKeys' }
400
+ ], [
401
+ Object.assign(Object.assign({}, result), { removed: removed.join(','), missing: missing.join(',') })
402
+ ]);
403
+ }
404
+ catch (error) {
405
+ exitWithError(error);
406
+ }
407
+ }));
408
+ secretCommand
409
+ .command('list')
410
+ .description('list secrets in AWS Secrets Manager')
411
+ .option('--prefix <prefix>', 'filter secrets by name prefix')
412
+ .option('-t, --tag <tag...>', 'filter tags in key=value format')
413
+ .option('-p, --profile <profile>', 'profile to use')
414
+ .option('-r, --region <region>', 'region to use')
415
+ .option('--output <format>', 'output format: json|table')
416
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
417
+ var _f;
418
+ try {
419
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
420
+ const globalOptions = command.optsWithGlobals();
421
+ const output = (_f = options.output) !== null && _f !== void 0 ? _f : (typeof globalOptions.output === 'string'
422
+ ? globalOptions.output
423
+ : 'table');
424
+ const result = yield (0, secretsmanager_admin_1.listSecrets)({
425
+ prefix: options.prefix,
426
+ tags: options.tag,
427
+ profile,
428
+ region
429
+ });
430
+ const rows = result.map((secret) => ({
431
+ name: secret.name,
432
+ description: secret.description,
433
+ lastChangedDate: secret.lastChangedDate
434
+ }));
435
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
436
+ { key: 'name', label: 'Name' },
437
+ { key: 'description', label: 'Description' },
438
+ { key: 'lastChangedDate', label: 'LastChanged' }
439
+ ], rows);
440
+ }
441
+ catch (error) {
442
+ exitWithError(error);
443
+ }
444
+ }));
445
+ secretCommand
446
+ .command('get')
447
+ .description('get secret metadata and version information')
448
+ .requiredOption('-n, --name <name>', 'secret name')
449
+ .option('-p, --profile <profile>', 'profile to use')
450
+ .option('-r, --region <region>', 'region to use')
451
+ .option('--output <format>', 'output format: json|table')
452
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
453
+ var _g;
454
+ try {
455
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
456
+ const globalOptions = command.optsWithGlobals();
457
+ const output = (_g = options.output) !== null && _g !== void 0 ? _g : (typeof globalOptions.output === 'string'
458
+ ? globalOptions.output
459
+ : 'table');
460
+ const result = yield (0, secretsmanager_admin_1.getSecretMetadata)({
461
+ name: options.name,
462
+ profile,
463
+ region
464
+ });
465
+ const row = {
466
+ name: result.name,
467
+ arn: result.arn,
468
+ description: result.description,
469
+ kmsKeyId: result.kmsKeyId,
470
+ createdDate: result.createdDate,
471
+ lastChangedDate: result.lastChangedDate,
472
+ deletedDate: result.deletedDate
473
+ };
474
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
475
+ { key: 'name', label: 'Name' },
476
+ { key: 'arn', label: 'ARN' },
477
+ { key: 'description', label: 'Description' },
478
+ { key: 'kmsKeyId', label: 'KmsKeyId' },
479
+ { key: 'createdDate', label: 'Created' },
480
+ { key: 'lastChangedDate', label: 'LastChanged' },
481
+ { key: 'deletedDate', label: 'Deleted' }
482
+ ], [row]);
483
+ }
484
+ catch (error) {
485
+ exitWithError(error);
486
+ }
487
+ }));
488
+ secretCommand
489
+ .command('delete')
490
+ .description('delete a secret in AWS Secrets Manager')
491
+ .requiredOption('-n, --name <name>', 'secret name')
492
+ .option('--recovery-days <days>', 'recovery window in days (7-30)', helpers_1.parseRecoveryDays)
493
+ .option('--force-delete-without-recovery', 'permanently delete secret without recovery window', false)
494
+ .option('-y, --yes', 'confirm delete action', false)
495
+ .option('-p, --profile <profile>', 'profile to use')
496
+ .option('-r, --region <region>', 'region to use')
497
+ .option('--output <format>', 'output format: json|table')
498
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
499
+ var _h;
500
+ try {
501
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
502
+ const globalOptions = command.optsWithGlobals();
503
+ const output = (_h = options.output) !== null && _h !== void 0 ? _h : (typeof globalOptions.output === 'string'
504
+ ? globalOptions.output
505
+ : 'table');
506
+ if (!options.yes) {
507
+ throw new Error('Delete requires --yes confirmation.');
508
+ }
509
+ if (options.recoveryDays && options.forceDeleteWithoutRecovery) {
510
+ throw new Error('Use either --recovery-days or --force-delete-without-recovery, not both.');
511
+ }
512
+ const result = yield (0, secretsmanager_admin_1.deleteSecret)({
513
+ name: options.name,
514
+ recoveryDays: options.recoveryDays,
515
+ forceDeleteWithoutRecovery: options.forceDeleteWithoutRecovery,
516
+ profile,
517
+ region
518
+ });
519
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
520
+ { key: 'name', label: 'Name' },
521
+ { key: 'arn', label: 'ARN' },
522
+ { key: 'deletedDate', label: 'DeletedDate' }
523
+ ], [result]);
524
+ }
525
+ catch (error) {
526
+ exitWithError(error);
527
+ }
528
+ }));
75
529
  program.parse();
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAwsClientConfig = void 0;
4
+ const credential_providers_1 = require("@aws-sdk/credential-providers");
5
+ const getCredentialsProvider = (options) => {
6
+ const { AWS_ACCESS_KEY_ID: awsAccessKeyId, AWS_SECRET_ACCESS_KEY: awsSecretAccessKey } = process.env;
7
+ if (options.profile) {
8
+ return (0, credential_providers_1.fromIni)({ profile: options.profile });
9
+ }
10
+ if (awsAccessKeyId && awsSecretAccessKey) {
11
+ return undefined;
12
+ }
13
+ return (0, credential_providers_1.fromIni)({ profile: 'default' });
14
+ };
15
+ const getEndpoint = () => {
16
+ return (process.env.AWS_ENDPOINT_URL || process.env.AWS_SECRETS_MANAGER_ENDPOINT);
17
+ };
18
+ const buildAwsClientConfig = (options) => {
19
+ const endpoint = getEndpoint();
20
+ const config = {
21
+ region: options.region,
22
+ credentials: getCredentialsProvider(options)
23
+ };
24
+ if (endpoint) {
25
+ config.endpoint = endpoint;
26
+ }
27
+ return config;
28
+ };
29
+ exports.buildAwsClientConfig = buildAwsClientConfig;