env-secrets 0.5.3 → 0.5.4

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 (52) hide show
  1. package/.claude/rules/cicd.md +189 -0
  2. package/.claude/rules/docs.md +96 -0
  3. package/.claude/rules/git-hooks.md +43 -0
  4. package/.claude/rules/local-dev-badges.md +91 -0
  5. package/.claude/rules/local-dev-env.md +382 -0
  6. package/.claude/rules/local-dev-license.md +104 -0
  7. package/.claude/rules/local-dev-mcp.md +70 -0
  8. package/.claude/rules/observability.md +23 -0
  9. package/.claude/rules/publishing-api.md +158 -0
  10. package/.claude/rules/publishing-apps.md +204 -0
  11. package/.claude/rules/publishing-apt.md +146 -0
  12. package/.claude/rules/publishing-brew.md +110 -0
  13. package/.claude/rules/publishing-cli.md +238 -0
  14. package/.claude/rules/publishing-libraries.md +115 -0
  15. package/.claude/rules/publishing-sdks.md +109 -0
  16. package/.claude/rules/publishing-web.md +185 -0
  17. package/.claude/rules/typescript-linting.md +141 -0
  18. package/.claude/rules/typescript-logging.md +356 -0
  19. package/.claude/rules/typescript-testing.md +185 -0
  20. package/.claude/settings.json +18 -0
  21. package/.claude/skills/github-health-check.skill +0 -0
  22. package/.codex/rules/cicd.md +21 -0
  23. package/.codex/rules/docs.md +98 -0
  24. package/.codex/rules/git-hooks.md +43 -0
  25. package/.codex/rules/github-health-check.md +440 -0
  26. package/.codex/rules/local-dev-env.md +47 -0
  27. package/.codex/rules/local-dev-license.md +3 -1
  28. package/.codex/rules/publishing-api.md +160 -0
  29. package/.codex/rules/publishing-apps.md +206 -0
  30. package/.codex/rules/publishing-apt.md +148 -0
  31. package/.codex/rules/publishing-brew.md +112 -0
  32. package/.codex/rules/publishing-cli.md +240 -0
  33. package/.codex/rules/publishing-libraries.md +117 -0
  34. package/.codex/rules/publishing-sdks.md +111 -0
  35. package/.codex/rules/publishing-web.md +187 -0
  36. package/.codex/rules/typescript-linting.md +143 -0
  37. package/.codex/rules/typescript-logging.md +358 -0
  38. package/.codex/rules/typescript-testing.md +187 -0
  39. package/.github/workflows/deploy-docs.yml +1 -1
  40. package/.github/workflows/unittests.yaml +1 -1
  41. package/.rulesrc.json +20 -0
  42. package/AGENTS.md +34 -0
  43. package/CLAUDE.md +58 -0
  44. package/README.md +17 -3
  45. package/__e2e__/aws-secret-value-args.test.ts +142 -0
  46. package/__tests__/cli/helpers.test.ts +35 -0
  47. package/dist/cli/helpers.js +13 -1
  48. package/dist/index.js +79 -40
  49. package/docs/AWS.md +42 -13
  50. package/package.json +5 -5
  51. package/src/cli/helpers.ts +16 -0
  52. package/src/index.ts +97 -48
package/.rulesrc.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "targets": ["claude", "codex"],
3
+ "agents": [
4
+ "local-dev",
5
+ "docs",
6
+ "cicd",
7
+ "observability",
8
+ "publishing",
9
+ "git-hooks",
10
+ "linting",
11
+ "logging",
12
+ "testing"
13
+ ],
14
+ "skills": ["github-health-check"],
15
+ "ballastVersion": "5.9.2",
16
+ "languages": ["typescript"],
17
+ "paths": {
18
+ "typescript": ["."]
19
+ }
20
+ }
package/AGENTS.md CHANGED
@@ -156,3 +156,37 @@ yarn install
156
156
  brew install awscli-local
157
157
  yarn build
158
158
  ```
159
+
160
+ ## Installed agent rules
161
+
162
+ Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
163
+
164
+ Read and follow these rule files in `.codex/rules/` when they apply:
165
+
166
+ - `.codex/rules/local-dev-badges.md` — Add standard badges (CI, Release, License, GitHub Release, npm) to the top of README.md
167
+ - `.codex/rules/local-dev-env.md` — Local development environment specialist - reproducible dev setup, DX, and documentation
168
+ - `.codex/rules/local-dev-license.md` — License setup - ensure LICENSE file, package.json license field, and README reference (default MIT; overridable in AGENTS.md/CLAUDE.md)
169
+ - `.codex/rules/local-dev-mcp.md` — Optional: use GitHub MCP and issues MCP (Jira/Linear/GitHub) for local-dev context
170
+ - `.codex/rules/docs.md` — Documentation specialist - GitHub Markdown docs by default, or maintain existing Docusaurus sites with publish-docs automation
171
+ - `.codex/rules/cicd.md` — CI/CD specialist - pipeline design, quality gates, and deployment
172
+ - `.codex/rules/observability.md` — Observability specialist - logging, tracing, metrics, and SLOs
173
+ - `.codex/rules/publishing-api.md` — REST API publishing specialist - Docker CD with Kubernetes health probes and Helm chart update
174
+ - `.codex/rules/publishing-apps.md` — App publishing specialist - npmjs for Node apps, PyPI for Python apps, GitHub Releases for Go apps
175
+ - `.codex/rules/publishing-apt.md` — APT/deb package publishing specialist - GoReleaser nfpms and GitHub Releases
176
+ - `.codex/rules/publishing-brew.md` — Homebrew tap publishing specialist - GoReleaser brews block and tap repo setup
177
+ - `.codex/rules/publishing-cli.md` — CLI publishing specialist - GoReleaser for Go, npmjs for Node, PyPI for Python
178
+ - `.codex/rules/publishing-libraries.md` — Library publishing specialist - npmjs for TypeScript, PyPI for Python, GitHub tags/releases for Go
179
+ - `.codex/rules/publishing-sdks.md` — SDK publishing specialist - npmjs for TypeScript SDKs, PyPI for Python SDKs, GitHub tags/releases for Go SDKs
180
+ - `.codex/rules/publishing-web.md` — Web app publishing specialist - Docker to GHCR/Docker Hub with Helm chart CD on push to main
181
+ - `.codex/rules/git-hooks.md` — Git hook specialist - configure pre-commit, pre-push, and Husky workflows that match the repository layout
182
+ - `.codex/rules/typescript-linting.md` — TypeScript linting specialist - implements comprehensive linting and code formatting for TypeScript/JavaScript projects
183
+ - `.codex/rules/typescript-logging.md` — Centralized logging specialist - configures Pino with Fluentd for Node/Next.js, and pino-browser to /api/logs
184
+ - `.codex/rules/typescript-testing.md` — Testing specialist - sets up Jest (default) or Vitest for Vite projects, 50% coverage, and test step in build GitHub Action
185
+
186
+ ## Installed skills
187
+
188
+ Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
189
+
190
+ Read and use these skill files in `.codex/rules/` when they are relevant:
191
+
192
+ - `.codex/rules/github-health-check.md` — Run a comprehensive GitHub repository health check. Use this skill whenever the user asks to: check GitHub health, audit the repo, check CI status, review open PRs, merge Dependabot PRs, check code coverage, check GitHub Code Quality, check GitHub security feature enablement, check security advisories, check Dependabot alerts, check code scanning alerts, check secret scanning alerts, check Snyk integration, keep GitHub in good shape, or any variation of "how is the repo doing". Also trigger for: "check dependabot PRs", "any PRs to merge", "check branch status", "repo health", "GitHub status check", "what needs attention in GitHub", "tidy up GitHub".
package/CLAUDE.md ADDED
@@ -0,0 +1,58 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code for working in this repository.
4
+
5
+ ## Repository Facts
6
+
7
+ Use this section for durable repo-specific facts that agents repeatedly need. Prefer facts stored here over re-deriving them with shell commands on every task.
8
+
9
+ Keep only stable, reviewable metadata here. Do not store secrets, credentials, or ephemeral runtime state.
10
+
11
+ Suggested facts to record:
12
+
13
+ - Canonical GitHub repo: `<OWNER/REPO>`
14
+ - Default branch: `<main>`
15
+ - Primary package manager: `<pnpm | npm | yarn | uv | go>`
16
+ - Version-file locations agents should check first: `<.nvmrc, packageManager, pyproject.toml, go.mod, etc.>`
17
+ - Canonical config files: `<paths agents should read before falling back to discovery>`
18
+ - Primary CI workflows: `<workflow filenames>`
19
+ - Primary release/publish workflows: `<workflow filenames>`
20
+ - Preferred build/test/lint/format/coverage commands: `<commands>`
21
+ - Coverage threshold: `<value>`
22
+ - Generated or protected paths agents should avoid editing directly: `<paths>`
23
+
24
+ Update this section when those facts change. If live runtime state is required, discover it separately instead of treating it as a durable repo fact.
25
+
26
+ ## Installed agent rules
27
+
28
+ Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
29
+
30
+ Read and follow these rule files in `.claude/rules/` when they apply:
31
+
32
+ - `.claude/rules/local-dev-badges.md` — Add standard badges (CI, Release, License, GitHub Release, npm) to the top of README.md
33
+ - `.claude/rules/local-dev-env.md` — Local development environment specialist - reproducible dev setup, DX, and documentation
34
+ - `.claude/rules/local-dev-license.md` — License setup - ensure LICENSE file, package.json license field, and README reference (default MIT; overridable in AGENTS.md/CLAUDE.md)
35
+ - `.claude/rules/local-dev-mcp.md` — Optional: use GitHub MCP and issues MCP (Jira/Linear/GitHub) for local-dev context
36
+ - `.claude/rules/docs.md` — Documentation specialist - GitHub Markdown docs by default, or maintain existing Docusaurus sites with publish-docs automation
37
+ - `.claude/rules/cicd.md` — CI/CD specialist - pipeline design, quality gates, and deployment
38
+ - `.claude/rules/observability.md` — Observability specialist - logging, tracing, metrics, and SLOs
39
+ - `.claude/rules/publishing-api.md` — REST API publishing specialist - Docker CD with Kubernetes health probes and Helm chart update
40
+ - `.claude/rules/publishing-apps.md` — App publishing specialist - npmjs for Node apps, PyPI for Python apps, GitHub Releases for Go apps
41
+ - `.claude/rules/publishing-apt.md` — APT/deb package publishing specialist - GoReleaser nfpms and GitHub Releases
42
+ - `.claude/rules/publishing-brew.md` — Homebrew tap publishing specialist - GoReleaser brews block and tap repo setup
43
+ - `.claude/rules/publishing-cli.md` — CLI publishing specialist - GoReleaser for Go, npmjs for Node, PyPI for Python
44
+ - `.claude/rules/publishing-libraries.md` — Library publishing specialist - npmjs for TypeScript, PyPI for Python, GitHub tags/releases for Go
45
+ - `.claude/rules/publishing-sdks.md` — SDK publishing specialist - npmjs for TypeScript SDKs, PyPI for Python SDKs, GitHub tags/releases for Go SDKs
46
+ - `.claude/rules/publishing-web.md` — Web app publishing specialist - Docker to GHCR/Docker Hub with Helm chart CD on push to main
47
+ - `.claude/rules/git-hooks.md` — Git hook specialist - configure pre-commit, pre-push, and Husky workflows that match the repository layout
48
+ - `.claude/rules/typescript-linting.md` — TypeScript linting specialist - implements comprehensive linting and code formatting for TypeScript/JavaScript projects
49
+ - `.claude/rules/typescript-logging.md` — Centralized logging specialist - configures Pino with Fluentd for Node/Next.js, and pino-browser to /api/logs
50
+ - `.claude/rules/typescript-testing.md` — Testing specialist - sets up Jest (default) or Vitest for Vite projects, 50% coverage, and test step in build GitHub Action
51
+
52
+ ## Installed skills
53
+
54
+ Created by [Ballast](https://github.com/everydaydevopsio/ballast) v5.9.2. Do not edit this section.
55
+
56
+ Read and use these skill files in `.claude/skills/` when they are relevant:
57
+
58
+ - `.claude/skills/github-health-check.skill` — Run a comprehensive GitHub repository health check. Use this skill whenever the user asks to: check GitHub health, audit the repo, check CI status, review open PRs, merge Dependabot PRs, check code coverage, check GitHub Code Quality, check GitHub security feature enablement, check security advisories, check Dependabot alerts, check code scanning alerts, check secret scanning alerts, check Snyk integration, keep GitHub in good shape, or any variation of "how is the repo doing". Also trigger for: "check dependabot PRs", "any PRs to merge", "check branch status", "repo health", "GitHub status check", "what needs attention in GitHub", "tidy up GitHub".
package/README.md CHANGED
@@ -101,9 +101,10 @@ env-secrets aws -s my-app-secrets -r us-east-1 -- node app.js
101
101
  - `-r, --region <region>` (optional): AWS region where the secret is stored. If not provided, uses `AWS_DEFAULT_REGION` environment variable
102
102
  - `-p, --profile <profile>` (optional): Local AWS profile to use. If not provided, uses `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables
103
103
  - `-o, --output <file>` (optional): Output secrets to a file instead of injecting into environment variables. File will be created with 0400 permissions and will not overwrite existing files
104
+ - `--no-shell` (optional): Run the program directly without a shell wrapper. Disables shell expansion — use when you do not need `$VAR` interpolation in arguments
104
105
  - `-- <program-to-run>`: The program to run with the injected environment variables (only used when `-o` is not specified)
105
106
 
106
- For `aws secret` management subcommands (`create`, `update`, `append`, `remove`, `upsert`/`import`, `list`, `get`, `delete`), use:
107
+ For `aws secret` management subcommands (`create`, `update`, `upsert`/`import`, `append`, `remove`, `list`, `get`, `value`, `delete`), use:
107
108
 
108
109
  - `-r, --region <region>` to target a specific region
109
110
  - `-p, --profile <profile>` to select credentials profile
@@ -111,8 +112,8 @@ For `aws secret` management subcommands (`create`, `update`, `append`, `remove`,
111
112
 
112
113
  These options are honored consistently on `aws secret` subcommands.
113
114
 
114
- `env-secrets aws -s` is for fetching/injecting secret values into a child process.
115
- `env-secrets aws secret ...` is for lifecycle management commands (`create`, `update`, `append`, `remove`, `upsert`/`import`, `list`, `get`, `delete`).
115
+ `env-secrets aws -s` is for fetching/injecting secret values into a child process. Use `--no-shell` to run the program directly without a shell wrapper (disables shell expansion).
116
+ `env-secrets aws secret ...` is for lifecycle management commands (`create`, `update`, `upsert`/`import`, `append`, `remove`, `list`, `get`, `value`, `delete`).
116
117
 
117
118
  #### Examples
118
119
 
@@ -262,6 +263,19 @@ env-secrets aws secret append -n app/dev --key JIRA_EMAIL_TOKEN -v blah --output
262
263
  env-secrets aws secret remove -n app/dev --key API_KEY --key OLD_TOKEN --output json
263
264
  ```
264
265
 
266
+ 13. **View the values of a secret:**
267
+
268
+ ```bash
269
+ # Table output — values masked as **** by default
270
+ env-secrets aws secret value -n app/dev -r us-east-1
271
+
272
+ # Reveal actual values (warning printed to stderr)
273
+ env-secrets aws secret value -n app/dev -r us-east-1 --reveal
274
+
275
+ # JSON output — full values, suitable for scripting (warns if stdout is a terminal)
276
+ env-secrets aws secret value -n app/dev -r us-east-1 --output json
277
+ ```
278
+
265
279
  ## Security Considerations
266
280
 
267
281
  - 🔐 **Credential Management**: The tool respects AWS credential precedence (environment variables, IAM roles, profiles)
@@ -0,0 +1,142 @@
1
+ import * as fs from 'fs';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+
5
+ import { cliWithEnv, cleanupTempFile } from './utils/test-utils';
6
+ import { registerAwsE2eContext } from './utils/aws-e2e-context';
7
+
8
+ describe('AWS Secret Value CLI Args', () => {
9
+ const { getLocalStackEnv } = registerAwsE2eContext();
10
+
11
+ test('should show masked values by default (table output)', async () => {
12
+ const secretName = `e2e-value-masked-${Date.now()}`;
13
+ const tempFile = path.join(
14
+ os.tmpdir(),
15
+ `env-secrets-value-${Date.now()}.env`
16
+ );
17
+ fs.writeFileSync(tempFile, 'DB_PASSWORD=super-secret\nAPI_KEY=abc123');
18
+
19
+ const createResult = await cliWithEnv(
20
+ ['aws', 'secret', 'upsert', '--file', tempFile, '--name', secretName],
21
+ getLocalStackEnv()
22
+ );
23
+ expect(createResult.code).toBe(0);
24
+
25
+ const valueResult = await cliWithEnv(
26
+ ['aws', 'secret', 'value', '-n', secretName],
27
+ getLocalStackEnv()
28
+ );
29
+ expect(valueResult.code).toBe(0);
30
+ expect(valueResult.stdout).toContain('DB_PASSWORD');
31
+ expect(valueResult.stdout).toContain('API_KEY');
32
+ expect(valueResult.stdout).toContain('****');
33
+ expect(valueResult.stdout).not.toContain('super-secret');
34
+ expect(valueResult.stdout).not.toContain('abc123');
35
+
36
+ const deleteResult1 = await cliWithEnv(
37
+ [
38
+ 'aws',
39
+ 'secret',
40
+ 'delete',
41
+ '-n',
42
+ secretName,
43
+ '--force-delete-without-recovery',
44
+ '--yes'
45
+ ],
46
+ getLocalStackEnv()
47
+ );
48
+ expect(deleteResult1.code).toBe(0);
49
+ cleanupTempFile(tempFile);
50
+ });
51
+
52
+ test('should reveal values with --reveal flag and warn on stderr', async () => {
53
+ const secretName = `e2e-value-reveal-${Date.now()}`;
54
+ const tempFile = path.join(
55
+ os.tmpdir(),
56
+ `env-secrets-reveal-${Date.now()}.env`
57
+ );
58
+ fs.writeFileSync(tempFile, 'DB_PASSWORD=super-secret\nAPI_KEY=abc123');
59
+
60
+ const createResult = await cliWithEnv(
61
+ ['aws', 'secret', 'upsert', '--file', tempFile, '--name', secretName],
62
+ getLocalStackEnv()
63
+ );
64
+ expect(createResult.code).toBe(0);
65
+
66
+ const valueResult = await cliWithEnv(
67
+ ['aws', 'secret', 'value', '-n', secretName, '--reveal'],
68
+ getLocalStackEnv()
69
+ );
70
+ expect(valueResult.code).toBe(0);
71
+ expect(valueResult.stdout).toContain('DB_PASSWORD');
72
+ expect(valueResult.stdout).toContain('super-secret');
73
+ expect(valueResult.stdout).toContain('API_KEY');
74
+ expect(valueResult.stdout).toContain('abc123');
75
+ expect(valueResult.stderr).toContain(
76
+ 'Warning: displaying sensitive secret values.'
77
+ );
78
+
79
+ const deleteResult2 = await cliWithEnv(
80
+ [
81
+ 'aws',
82
+ 'secret',
83
+ 'delete',
84
+ '-n',
85
+ secretName,
86
+ '--force-delete-without-recovery',
87
+ '--yes'
88
+ ],
89
+ getLocalStackEnv()
90
+ );
91
+ expect(deleteResult2.code).toBe(0);
92
+ cleanupTempFile(tempFile);
93
+ });
94
+
95
+ test('should output full values as JSON without --reveal', async () => {
96
+ const secretName = `e2e-value-json-${Date.now()}`;
97
+ const tempFile = path.join(
98
+ os.tmpdir(),
99
+ `env-secrets-json-${Date.now()}.env`
100
+ );
101
+ fs.writeFileSync(tempFile, 'DB_PASSWORD=super-secret\nAPI_KEY=abc123');
102
+
103
+ const createResult = await cliWithEnv(
104
+ ['aws', 'secret', 'upsert', '--file', tempFile, '--name', secretName],
105
+ getLocalStackEnv()
106
+ );
107
+ expect(createResult.code).toBe(0);
108
+
109
+ const valueResult = await cliWithEnv(
110
+ ['aws', 'secret', 'value', '-n', secretName, '--output', 'json'],
111
+ getLocalStackEnv()
112
+ );
113
+ expect(valueResult.code).toBe(0);
114
+ const parsed = JSON.parse(valueResult.stdout) as Record<string, string>;
115
+ expect(parsed.DB_PASSWORD).toBe('super-secret');
116
+ expect(parsed.API_KEY).toBe('abc123');
117
+
118
+ const deleteResult3 = await cliWithEnv(
119
+ [
120
+ 'aws',
121
+ 'secret',
122
+ 'delete',
123
+ '-n',
124
+ secretName,
125
+ '--force-delete-without-recovery',
126
+ '--yes'
127
+ ],
128
+ getLocalStackEnv()
129
+ );
130
+ expect(deleteResult3.code).toBe(0);
131
+ cleanupTempFile(tempFile);
132
+ });
133
+
134
+ test('should fail for a non-existent secret', async () => {
135
+ const valueResult = await cliWithEnv(
136
+ ['aws', 'secret', 'value', '-n', 'does-not-exist-e2e'],
137
+ getLocalStackEnv()
138
+ );
139
+ expect(valueResult.code).not.toBe(0);
140
+ expect(valueResult.stderr).toContain('not found');
141
+ });
142
+ });
@@ -11,6 +11,7 @@ import {
11
11
  readStdin,
12
12
  renderTable,
13
13
  resolveAwsScope,
14
+ resolveOutputFormat,
14
15
  resolveSecretValue
15
16
  } from '../../src/cli/helpers';
16
17
 
@@ -214,4 +215,38 @@ describe('cli/helpers', () => {
214
215
  region: 'us-west-2'
215
216
  });
216
217
  });
218
+
219
+ describe('resolveOutputFormat', () => {
220
+ it('uses explicit local output option', () => {
221
+ expect(resolveOutputFormat({ output: 'json' })).toBe('json');
222
+ expect(resolveOutputFormat({ output: 'table' })).toBe('table');
223
+ });
224
+
225
+ it('throws for invalid local output option', () => {
226
+ expect(() => resolveOutputFormat({ output: 'xml' })).toThrow(
227
+ 'Invalid output format'
228
+ );
229
+ });
230
+
231
+ it('inherits json or table from global options', () => {
232
+ const jsonCmd = { optsWithGlobals: () => ({ output: 'json' }) };
233
+ const tableCmd = { optsWithGlobals: () => ({ output: 'table' }) };
234
+ expect(resolveOutputFormat({}, jsonCmd)).toBe('json');
235
+ expect(resolveOutputFormat({}, tableCmd)).toBe('table');
236
+ });
237
+
238
+ it('ignores a file path in global output and defaults to table', () => {
239
+ const command = { optsWithGlobals: () => ({ output: 'secrets.env' }) };
240
+ expect(resolveOutputFormat({}, command)).toBe('table');
241
+ });
242
+
243
+ it('defaults to table when no options are provided', () => {
244
+ expect(resolveOutputFormat({})).toBe('table');
245
+ });
246
+
247
+ it('prefers local option over global option', () => {
248
+ const command = { optsWithGlobals: () => ({ output: 'json' }) };
249
+ expect(resolveOutputFormat({ output: 'table' }, command)).toBe('table');
250
+ });
251
+ });
217
252
  });
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
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;
12
+ exports.resolveAwsScope = exports.resolveOutputFormat = exports.parseEnvSecretsFile = exports.parseEnvSecrets = exports.resolveSecretValue = exports.readStdin = exports.parseRecoveryDays = exports.printData = exports.renderTable = exports.asOutputFormat = void 0;
13
13
  const promises_1 = require("node:fs/promises");
14
14
  const asOutputFormat = (value) => {
15
15
  if (value !== 'json' && value !== 'table') {
@@ -156,6 +156,18 @@ const parseEnvSecretsFile = (path) => __awaiter(void 0, void 0, void 0, function
156
156
  return (0, exports.parseEnvSecrets)(content);
157
157
  });
158
158
  exports.parseEnvSecretsFile = parseEnvSecretsFile;
159
+ const resolveOutputFormat = (options, command) => {
160
+ var _a, _b;
161
+ if (options.output) {
162
+ return (0, exports.asOutputFormat)(options.output);
163
+ }
164
+ const globalOutput = (_b = (_a = command === null || command === void 0 ? void 0 : command.optsWithGlobals) === null || _a === void 0 ? void 0 : _a.call(command)) === null || _b === void 0 ? void 0 : _b.output;
165
+ if (globalOutput === 'json' || globalOutput === 'table') {
166
+ return globalOutput;
167
+ }
168
+ return 'table';
169
+ };
170
+ exports.resolveOutputFormat = resolveOutputFormat;
159
171
  const resolveAwsScope = (options, command) => {
160
172
  var _a;
161
173
  const globalOptions = ((_a = command === null || command === void 0 ? void 0 : command.optsWithGlobals) === null || _a === void 0 ? void 0 : _a.call(command)) || {};
package/dist/index.js CHANGED
@@ -159,13 +159,9 @@ secretCommand
159
159
  .option('-r, --region <region>', 'region to use')
160
160
  .option('--output <format>', 'output format: json|table')
161
161
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
162
- var _a;
163
162
  try {
164
163
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
165
- const globalOptions = command.optsWithGlobals();
166
- const output = (_a = options.output) !== null && _a !== void 0 ? _a : (typeof globalOptions.output === 'string'
167
- ? globalOptions.output
168
- : 'table');
164
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
169
165
  const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
170
166
  if (!value) {
171
167
  throw new Error('Secret value is required. Provide --value, --value-stdin, or --file.');
@@ -203,13 +199,9 @@ secretCommand
203
199
  .option('-r, --region <region>', 'region to use')
204
200
  .option('--output <format>', 'output format: json|table')
205
201
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
206
- var _b;
207
202
  try {
208
203
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
209
- const globalOptions = command.optsWithGlobals();
210
- const output = (_b = options.output) !== null && _b !== void 0 ? _b : (typeof globalOptions.output === 'string'
211
- ? globalOptions.output
212
- : 'table');
204
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
213
205
  const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
214
206
  if (!value && !options.description && !options.kmsKeyId) {
215
207
  throw new Error('Nothing to update. Provide --value/--value-stdin/--file, --description, or --kms-key-id.');
@@ -245,13 +237,9 @@ secretCommand
245
237
  .option('-r, --region <region>', 'region to use')
246
238
  .option('--output <format>', 'output format: json|table')
247
239
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
248
- var _c;
249
240
  try {
250
241
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
251
- const globalOptions = command.optsWithGlobals();
252
- const output = (_c = options.output) !== null && _c !== void 0 ? _c : (typeof globalOptions.output === 'string'
253
- ? globalOptions.output
254
- : 'table');
242
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
255
243
  const parsed = yield (0, helpers_1.parseEnvSecretsFile)(options.file);
256
244
  if (parsed.entries.length === 0) {
257
245
  throw new Error('No env entries found. Include lines like KEY=value or export KEY=value.');
@@ -357,13 +345,9 @@ secretCommand
357
345
  .option('-r, --region <region>', 'region to use')
358
346
  .option('--output <format>', 'output format: json|table')
359
347
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
360
- var _d;
361
348
  try {
362
349
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
363
- const globalOptions = command.optsWithGlobals();
364
- const output = (_d = options.output) !== null && _d !== void 0 ? _d : (typeof globalOptions.output === 'string'
365
- ? globalOptions.output
366
- : 'table');
350
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
367
351
  const value = yield (0, helpers_1.resolveSecretValue)(options.value, options.valueStdin, options.file);
368
352
  if (!value) {
369
353
  throw new Error('Append value is required. Provide --value, --value-stdin, or --file.');
@@ -404,13 +388,9 @@ secretCommand
404
388
  .option('-r, --region <region>', 'region to use')
405
389
  .option('--output <format>', 'output format: json|table')
406
390
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
407
- var _e;
408
391
  try {
409
392
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
410
- const globalOptions = command.optsWithGlobals();
411
- const output = (_e = options.output) !== null && _e !== void 0 ? _e : (typeof globalOptions.output === 'string'
412
- ? globalOptions.output
413
- : 'table');
393
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
414
394
  const keys = options.key;
415
395
  const current = yield (0, secretsmanager_admin_1.getSecretString)({
416
396
  name: options.name,
@@ -461,13 +441,9 @@ secretCommand
461
441
  .option('-r, --region <region>', 'region to use')
462
442
  .option('--output <format>', 'output format: json|table')
463
443
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
464
- var _f;
465
444
  try {
466
445
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
467
- const globalOptions = command.optsWithGlobals();
468
- const output = (_f = options.output) !== null && _f !== void 0 ? _f : (typeof globalOptions.output === 'string'
469
- ? globalOptions.output
470
- : 'table');
446
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
471
447
  const result = yield (0, secretsmanager_admin_1.listSecrets)({
472
448
  prefix: options.prefix,
473
449
  tags: options.tag,
@@ -497,13 +473,9 @@ secretCommand
497
473
  .option('-r, --region <region>', 'region to use')
498
474
  .option('--output <format>', 'output format: json|table')
499
475
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
500
- var _g;
501
476
  try {
502
477
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
503
- const globalOptions = command.optsWithGlobals();
504
- const output = (_g = options.output) !== null && _g !== void 0 ? _g : (typeof globalOptions.output === 'string'
505
- ? globalOptions.output
506
- : 'table');
478
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
507
479
  const result = yield (0, secretsmanager_admin_1.getSecretMetadata)({
508
480
  name: options.name,
509
481
  profile,
@@ -532,6 +504,77 @@ secretCommand
532
504
  exitWithError(error);
533
505
  }
534
506
  }));
507
+ secretCommand
508
+ .command('value')
509
+ .description('get the values of a secret')
510
+ .requiredOption('-n, --name <name>', 'secret name')
511
+ .option('--reveal', 'reveal secret values in table output (values are masked by default)', false)
512
+ .option('-p, --profile <profile>', 'profile to use')
513
+ .option('-r, --region <region>', 'region to use')
514
+ .option('--output <format>', 'output format: json|table')
515
+ .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
516
+ try {
517
+ const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
518
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
519
+ let secretString;
520
+ try {
521
+ secretString = yield (0, secretsmanager_admin_1.getSecretString)({
522
+ name: options.name,
523
+ profile,
524
+ region
525
+ });
526
+ }
527
+ catch (error) {
528
+ const msg = error instanceof Error ? error.message : String(error);
529
+ if (msg.includes('cannot be edited with append/remove')) {
530
+ throw new Error(`Secret "${options.name}" is stored as binary and cannot be displayed as text.`);
531
+ }
532
+ throw error;
533
+ }
534
+ let jsonEntries;
535
+ try {
536
+ const parsed = JSON.parse(secretString);
537
+ if (parsed && !Array.isArray(parsed) && typeof parsed === 'object') {
538
+ jsonEntries = Object.entries(parsed).map(([key, value]) => ({ key, value }));
539
+ }
540
+ else {
541
+ jsonEntries = [{ key: options.name, value: secretString }];
542
+ }
543
+ }
544
+ catch (_a) {
545
+ jsonEntries = [{ key: options.name, value: secretString }];
546
+ }
547
+ if (output === 'json') {
548
+ if (process.stdout.isTTY) {
549
+ // eslint-disable-next-line no-console
550
+ console.error('Warning: displaying sensitive secret values.');
551
+ }
552
+ const result = Object.fromEntries(jsonEntries.map(({ key, value }) => [key, value]));
553
+ // eslint-disable-next-line no-console
554
+ console.log(JSON.stringify(result, null, 2));
555
+ return;
556
+ }
557
+ if (options.reveal) {
558
+ // eslint-disable-next-line no-console
559
+ console.error('Warning: displaying sensitive secret values.');
560
+ }
561
+ const rows = jsonEntries.map(({ key, value }) => ({
562
+ key,
563
+ value: options.reveal
564
+ ? typeof value === 'string'
565
+ ? value
566
+ : JSON.stringify(value)
567
+ : '****'
568
+ }));
569
+ (0, helpers_1.printData)((0, helpers_1.asOutputFormat)(output), [
570
+ { key: 'key', label: 'Key' },
571
+ { key: 'value', label: 'Value' }
572
+ ], rows);
573
+ }
574
+ catch (error) {
575
+ exitWithError(error);
576
+ }
577
+ }));
535
578
  secretCommand
536
579
  .command('delete')
537
580
  .description('delete a secret in AWS Secrets Manager')
@@ -543,13 +586,9 @@ secretCommand
543
586
  .option('-r, --region <region>', 'region to use')
544
587
  .option('--output <format>', 'output format: json|table')
545
588
  .action((options, command) => __awaiter(void 0, void 0, void 0, function* () {
546
- var _h;
547
589
  try {
548
590
  const { profile, region } = (0, helpers_1.resolveAwsScope)(options, command);
549
- const globalOptions = command.optsWithGlobals();
550
- const output = (_h = options.output) !== null && _h !== void 0 ? _h : (typeof globalOptions.output === 'string'
551
- ? globalOptions.output
552
- : 'table');
591
+ const output = (0, helpers_1.resolveOutputFormat)(options, command);
553
592
  if (!options.yes) {
554
593
  throw new Error('Delete requires --yes confirmation.');
555
594
  }