@karmaniverous/get-dotenv 6.1.0 → 6.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -14
- package/dist/cli.d.ts +58 -2
- package/dist/cli.mjs +800 -364
- package/dist/cliHost.d.ts +216 -17
- package/dist/cliHost.mjs +178 -14
- package/dist/config.d.ts +12 -0
- package/dist/config.mjs +79 -2
- package/dist/env-overlay.d.ts +8 -0
- package/dist/getdotenv.cli.mjs +800 -364
- package/dist/index.d.ts +220 -35
- package/dist/index.mjs +851 -365
- package/dist/plugins-aws.d.ts +94 -6
- package/dist/plugins-aws.mjs +462 -184
- package/dist/plugins-batch.d.ts +85 -3
- package/dist/plugins-batch.mjs +203 -54
- package/dist/plugins-cmd.d.ts +85 -3
- package/dist/plugins-cmd.mjs +150 -28
- package/dist/plugins-init.d.ts +85 -3
- package/dist/plugins-init.mjs +270 -131
- package/dist/plugins.d.ts +85 -4
- package/dist/plugins.mjs +800 -364
- package/dist/templates/cli/plugins/hello/defaultAction.ts +27 -0
- package/dist/templates/cli/plugins/hello/index.ts +26 -0
- package/dist/templates/cli/plugins/hello/options.ts +31 -0
- package/dist/templates/cli/plugins/hello/strangerAction.ts +20 -0
- package/dist/templates/cli/plugins/hello/types.ts +13 -0
- package/dist/templates/defaultAction.ts +27 -0
- package/dist/templates/hello/defaultAction.ts +27 -0
- package/dist/templates/hello/index.ts +26 -0
- package/dist/templates/hello/options.ts +31 -0
- package/dist/templates/hello/strangerAction.ts +20 -0
- package/dist/templates/hello/types.ts +13 -0
- package/dist/templates/index.ts +23 -22
- package/dist/templates/options.ts +31 -0
- package/dist/templates/plugins/hello/defaultAction.ts +27 -0
- package/dist/templates/plugins/hello/index.ts +26 -0
- package/dist/templates/plugins/hello/options.ts +31 -0
- package/dist/templates/plugins/hello/strangerAction.ts +20 -0
- package/dist/templates/plugins/hello/types.ts +13 -0
- package/dist/templates/strangerAction.ts +20 -0
- package/dist/templates/types.ts +13 -0
- package/package.json +3 -4
- package/templates/cli/plugins/hello/defaultAction.ts +27 -0
- package/templates/cli/plugins/hello/index.ts +26 -0
- package/templates/cli/plugins/hello/options.ts +31 -0
- package/templates/cli/plugins/hello/strangerAction.ts +20 -0
- package/templates/cli/plugins/hello/types.ts +13 -0
- package/dist/templates/cli/plugins/hello.ts +0 -42
- package/dist/templates/hello.ts +0 -42
- package/dist/templates/plugins/hello.ts +0 -42
- package/templates/cli/plugins/hello.ts +0 -42
package/dist/plugins-aws.mjs
CHANGED
|
@@ -20,26 +20,58 @@ import { Option, Command } from '@commander-js/extra-typings';
|
|
|
20
20
|
* Minimal process env representation used by options and helpers.
|
|
21
21
|
* Values may be `undefined` to indicate "unset".
|
|
22
22
|
*/
|
|
23
|
+
/**
|
|
24
|
+
* Schema for an env-like record.
|
|
25
|
+
*
|
|
26
|
+
* Keys are environment variable names and values are either strings or `undefined`
|
|
27
|
+
* (to represent “unset”).
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
23
31
|
const processEnvSchema = z.record(z.string(), z.string().optional());
|
|
24
32
|
// RAW: all fields optional — undefined means "inherit" from lower layers.
|
|
33
|
+
/**
|
|
34
|
+
* Programmatic options schema (raw).
|
|
35
|
+
*
|
|
36
|
+
* This schema is the canonical runtime source of truth for the `getDotenv()` programmatic API.
|
|
37
|
+
* All fields are optional; `undefined` generally means “inherit default/lower layer”.
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
25
41
|
const getDotenvOptionsSchemaRaw = z.object({
|
|
42
|
+
/** Default environment name when `env` is not provided. */
|
|
26
43
|
defaultEnv: z.string().optional(),
|
|
44
|
+
/** Base dotenv filename token (default `.env`). */
|
|
27
45
|
dotenvToken: z.string().optional(),
|
|
46
|
+
/** Path to a dynamic variables module (JS/TS) to load and apply. */
|
|
28
47
|
dynamicPath: z.string().optional(),
|
|
29
|
-
|
|
48
|
+
/** Dynamic map is intentionally wide for now; refine once sources are normalized. */
|
|
30
49
|
dynamic: z.record(z.string(), z.unknown()).optional(),
|
|
50
|
+
/** Selected environment name for this invocation (for env-scoped files and overlays). */
|
|
31
51
|
env: z.string().optional(),
|
|
52
|
+
/** When true, skip applying dynamic variables. */
|
|
32
53
|
excludeDynamic: z.boolean().optional(),
|
|
54
|
+
/** When true, skip environment-scoped dotenv files. */
|
|
33
55
|
excludeEnv: z.boolean().optional(),
|
|
56
|
+
/** When true, skip global dotenv files. */
|
|
34
57
|
excludeGlobal: z.boolean().optional(),
|
|
58
|
+
/** When true, skip private dotenv files. */
|
|
35
59
|
excludePrivate: z.boolean().optional(),
|
|
60
|
+
/** When true, skip public dotenv files. */
|
|
36
61
|
excludePublic: z.boolean().optional(),
|
|
62
|
+
/** When true, merge the final composed environment into `process.env`. */
|
|
37
63
|
loadProcess: z.boolean().optional(),
|
|
64
|
+
/** When true, log the final environment map via `logger`. */
|
|
38
65
|
log: z.boolean().optional(),
|
|
66
|
+
/** Logger used when `log` is enabled (console-compatible). */
|
|
39
67
|
logger: z.unknown().default(console),
|
|
68
|
+
/** Optional output dotenv file path to write after composition. */
|
|
40
69
|
outputPath: z.string().optional(),
|
|
70
|
+
/** Dotenv search paths (ordered). */
|
|
41
71
|
paths: z.array(z.string()).optional(),
|
|
72
|
+
/** Private token suffix for private dotenv files (default `local`). */
|
|
42
73
|
privateToken: z.string().optional(),
|
|
74
|
+
/** Explicit variables to overlay onto the composed dotenv map. */
|
|
43
75
|
vars: processEnvSchema.optional(),
|
|
44
76
|
});
|
|
45
77
|
/**
|
|
@@ -47,6 +79,14 @@ const getDotenvOptionsSchemaRaw = z.object({
|
|
|
47
79
|
* For now, this mirrors the RAW schema; future stages may materialize defaults
|
|
48
80
|
* and narrow shapes as resolution is wired into the host.
|
|
49
81
|
*/
|
|
82
|
+
/**
|
|
83
|
+
* Programmatic options schema (resolved).
|
|
84
|
+
*
|
|
85
|
+
* Today this mirrors {@link getDotenvOptionsSchemaRaw}, but is kept as a distinct export
|
|
86
|
+
* so future resolution steps can narrow or materialize defaults without breaking the API.
|
|
87
|
+
*
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
50
90
|
const getDotenvOptionsSchemaResolved = getDotenvOptionsSchemaRaw;
|
|
51
91
|
|
|
52
92
|
/**
|
|
@@ -56,27 +96,55 @@ const getDotenvOptionsSchemaResolved = getDotenvOptionsSchemaRaw;
|
|
|
56
96
|
* reflect normalized types (paths: string[], vars: ProcessEnv), applied in the
|
|
57
97
|
* CLI resolution pipeline.
|
|
58
98
|
*/
|
|
99
|
+
/**
|
|
100
|
+
* CLI options schema (raw).
|
|
101
|
+
*
|
|
102
|
+
* Extends the programmatic options schema with CLI-only flags and stringly inputs
|
|
103
|
+
* which are normalized later by the host resolution pipeline.
|
|
104
|
+
*
|
|
105
|
+
* @public
|
|
106
|
+
*/
|
|
59
107
|
const getDotenvCliOptionsSchemaRaw = getDotenvOptionsSchemaRaw.extend({
|
|
60
108
|
// CLI-specific fields (stringly inputs before preprocessing)
|
|
109
|
+
/** Enable verbose debug output (host-specific). */
|
|
61
110
|
debug: z.boolean().optional(),
|
|
111
|
+
/** Fail on validation errors (schema/requiredKeys). */
|
|
62
112
|
strict: z.boolean().optional(),
|
|
113
|
+
/** Capture child process stdio (useful for CI/tests). */
|
|
63
114
|
capture: z.boolean().optional(),
|
|
115
|
+
/** Emit child env diagnostics (boolean or selected keys). */
|
|
64
116
|
trace: z.union([z.boolean(), z.array(z.string())]).optional(),
|
|
117
|
+
/** Enable presentation-time redaction in trace/log output. */
|
|
65
118
|
redact: z.boolean().optional(),
|
|
119
|
+
/** Enable entropy warnings in trace/log output. */
|
|
66
120
|
warnEntropy: z.boolean().optional(),
|
|
121
|
+
/** Entropy threshold (bits/char) for warnings. */
|
|
67
122
|
entropyThreshold: z.number().optional(),
|
|
123
|
+
/** Minimum value length to consider for entropy warnings. */
|
|
68
124
|
entropyMinLength: z.number().optional(),
|
|
125
|
+
/** Regex patterns (strings) to suppress entropy warnings by key. */
|
|
69
126
|
entropyWhitelist: z.array(z.string()).optional(),
|
|
127
|
+
/** Additional key-match patterns (strings) for redaction. */
|
|
70
128
|
redactPatterns: z.array(z.string()).optional(),
|
|
129
|
+
/** Dotenv search paths provided as a single delimited string. */
|
|
71
130
|
paths: z.string().optional(),
|
|
131
|
+
/** Delimiter string used to split `paths`. */
|
|
72
132
|
pathsDelimiter: z.string().optional(),
|
|
133
|
+
/** Regex pattern used to split `paths` (takes precedence over delimiter). */
|
|
73
134
|
pathsDelimiterPattern: z.string().optional(),
|
|
135
|
+
/** Scripts table in a permissive shape at parse time (validated elsewhere). */
|
|
74
136
|
scripts: z.record(z.string(), z.unknown()).optional(),
|
|
137
|
+
/** Shell selection (`false` for shell-off, string for explicit shell). */
|
|
75
138
|
shell: z.union([z.boolean(), z.string()]).optional(),
|
|
139
|
+
/** Extra variables expressed as a single delimited string of assignments. */
|
|
76
140
|
vars: z.string().optional(),
|
|
141
|
+
/** Assignment operator used when parsing `vars`. */
|
|
77
142
|
varsAssignor: z.string().optional(),
|
|
143
|
+
/** Regex pattern used as the assignment operator for `vars` parsing. */
|
|
78
144
|
varsAssignorPattern: z.string().optional(),
|
|
145
|
+
/** Delimiter string used to split `vars`. */
|
|
79
146
|
varsDelimiter: z.string().optional(),
|
|
147
|
+
/** Regex pattern used to split `vars` (takes precedence over delimiter). */
|
|
80
148
|
varsDelimiterPattern: z.string().optional(),
|
|
81
149
|
});
|
|
82
150
|
|
|
@@ -100,17 +168,34 @@ const envStringMap = z.record(z.string(), stringMap);
|
|
|
100
168
|
* Raw configuration schema for get‑dotenv config files (JSON/YAML/JS/TS).
|
|
101
169
|
* Validates allowed top‑level keys without performing path normalization.
|
|
102
170
|
*/
|
|
171
|
+
/**
|
|
172
|
+
* Config schema for discovered get-dotenv configuration documents (raw).
|
|
173
|
+
*
|
|
174
|
+
* This schema validates the allowed top-level keys for configuration files.
|
|
175
|
+
* It does not normalize paths or coerce types beyond Zod’s parsing.
|
|
176
|
+
*
|
|
177
|
+
* @public
|
|
178
|
+
*/
|
|
103
179
|
const getDotenvConfigSchemaRaw = z.object({
|
|
180
|
+
/** Root option defaults applied by the host (CLI-like, collapsed families). */
|
|
104
181
|
rootOptionDefaults: getDotenvCliOptionsSchemaRaw.optional(),
|
|
182
|
+
/** Help-time visibility map for root flags (false hides). */
|
|
105
183
|
rootOptionVisibility: visibilityMap.optional(),
|
|
106
|
-
|
|
184
|
+
/** Scripts table used by cmd/batch resolution (validation intentionally permissive here). */
|
|
185
|
+
scripts: z.record(z.string(), z.unknown()).optional(),
|
|
186
|
+
/** Keys required to be present in the final composed environment. */
|
|
107
187
|
requiredKeys: z.array(z.string()).optional(),
|
|
188
|
+
/** Validation schema (JS/TS only; JSON/YAML loader rejects). */
|
|
108
189
|
schema: z.unknown().optional(), // JS/TS-only; loader rejects in JSON/YAML
|
|
190
|
+
/** Public global variables (string-only). */
|
|
109
191
|
vars: stringMap.optional(), // public, global
|
|
192
|
+
/** Public per-environment variables (string-only). */
|
|
110
193
|
envVars: envStringMap.optional(), // public, per-env
|
|
111
194
|
// Dynamic in config (JS/TS only). JSON/YAML loader will reject if set.
|
|
195
|
+
/** Dynamic variable definitions (JS/TS only). */
|
|
112
196
|
dynamic: z.unknown().optional(),
|
|
113
197
|
// Per-plugin config bag; validated by plugins/host when used.
|
|
198
|
+
/** Per-plugin config slices keyed by realized mount path (for example, `aws/whoami`). */
|
|
114
199
|
plugins: z.record(z.string(), z.unknown()).optional(),
|
|
115
200
|
});
|
|
116
201
|
/**
|
|
@@ -862,13 +947,21 @@ const redactObject = (obj, opts) => {
|
|
|
862
947
|
* Base root CLI defaults (shared; kept untyped here to avoid cross-layer deps).
|
|
863
948
|
* Used as the bottom layer for CLI option resolution.
|
|
864
949
|
*/
|
|
950
|
+
const baseScripts = {
|
|
951
|
+
'git-status': {
|
|
952
|
+
cmd: 'git branch --show-current && git status -s -u',
|
|
953
|
+
shell: true,
|
|
954
|
+
},
|
|
955
|
+
};
|
|
865
956
|
/**
|
|
866
|
-
* Default values for root CLI options used by the host and helpers as the
|
|
867
|
-
* baseline layer during option resolution.
|
|
957
|
+
* Default values for root CLI options used by the host and helpers as the baseline layer during option resolution.
|
|
868
958
|
*
|
|
869
|
-
* These defaults correspond to the
|
|
870
|
-
*
|
|
871
|
-
* configuration `rootOptionDefaults`
|
|
959
|
+
* These defaults correspond to the “stringly” root surface (see `RootOptionsShape`) and are merged by precedence with:
|
|
960
|
+
* - create-time overrides
|
|
961
|
+
* - any discovered configuration `rootOptionDefaults`
|
|
962
|
+
* - and finally CLI flags at runtime
|
|
963
|
+
*
|
|
964
|
+
* @public
|
|
872
965
|
*/
|
|
873
966
|
const baseRootOptionDefaults = {
|
|
874
967
|
dotenvToken: '.env',
|
|
@@ -882,12 +975,7 @@ const baseRootOptionDefaults = {
|
|
|
882
975
|
paths: './',
|
|
883
976
|
pathsDelimiter: ' ',
|
|
884
977
|
privateToken: 'local',
|
|
885
|
-
scripts:
|
|
886
|
-
'git-status': {
|
|
887
|
-
cmd: 'git branch --show-current && git status -s -u',
|
|
888
|
-
shell: true,
|
|
889
|
-
},
|
|
890
|
-
},
|
|
978
|
+
scripts: baseScripts,
|
|
891
979
|
shell: true,
|
|
892
980
|
vars: '',
|
|
893
981
|
varsAssignor: '=',
|
|
@@ -1434,6 +1522,14 @@ async function _execNormalized(command, shell, opts = {}) {
|
|
|
1434
1522
|
return out;
|
|
1435
1523
|
}
|
|
1436
1524
|
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Execute a command and capture stdout/stderr (buffered).
|
|
1527
|
+
*
|
|
1528
|
+
* @param command - Command string (shell) or argv array (shell-off supported).
|
|
1529
|
+
* @param shell - Shell setting (false for plain execution).
|
|
1530
|
+
* @param opts - Execution options (cwd/env/timeout).
|
|
1531
|
+
* @returns A promise resolving to the captured result.
|
|
1532
|
+
*/
|
|
1437
1533
|
async function runCommandResult(command, shell, opts = {}) {
|
|
1438
1534
|
// Build opts without injecting undefined (exactOptionalPropertyTypes-safe)
|
|
1439
1535
|
const coreOpts = { stdio: 'pipe' };
|
|
@@ -1448,6 +1544,14 @@ async function runCommandResult(command, shell, opts = {}) {
|
|
|
1448
1544
|
}
|
|
1449
1545
|
return _execNormalized(command, shell, coreOpts);
|
|
1450
1546
|
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Execute a command and return its exit code.
|
|
1549
|
+
*
|
|
1550
|
+
* @param command - Command string (shell) or argv array (shell-off supported).
|
|
1551
|
+
* @param shell - Shell setting (false for plain execution).
|
|
1552
|
+
* @param opts - Execution options (cwd/env/stdio).
|
|
1553
|
+
* @returns A promise resolving to the process exit code.
|
|
1554
|
+
*/
|
|
1451
1555
|
async function runCommand(command, shell, opts) {
|
|
1452
1556
|
// Build opts without injecting undefined (exactOptionalPropertyTypes-safe)
|
|
1453
1557
|
const callOpts = {};
|
|
@@ -1907,6 +2011,16 @@ function evaluateDynamicOptions(root, resolved) {
|
|
|
1907
2011
|
visit(root);
|
|
1908
2012
|
}
|
|
1909
2013
|
|
|
2014
|
+
/**
|
|
2015
|
+
* Initialize a {@link GetDotenvCli} instance with help configuration and safe defaults.
|
|
2016
|
+
*
|
|
2017
|
+
* @remarks
|
|
2018
|
+
* This is a low-level initializer used by the host constructor to keep `GetDotenvCli.ts`
|
|
2019
|
+
* small and to centralize help/output behavior.
|
|
2020
|
+
*
|
|
2021
|
+
* @param cli - The CLI instance to initialize.
|
|
2022
|
+
* @param headerGetter - Callback returning an optional help header string.
|
|
2023
|
+
*/
|
|
1910
2024
|
function initializeInstance(cli, headerGetter) {
|
|
1911
2025
|
// Configure grouped help: show only base options in default "Options";
|
|
1912
2026
|
// subcommands show all of their own options.
|
|
@@ -2436,18 +2550,49 @@ const buildSpawnEnv = (base, overlay) => {
|
|
|
2436
2550
|
function applyAwsContext(out, ctx, setProcessEnv = true) {
|
|
2437
2551
|
const { profile, region, credentials } = out;
|
|
2438
2552
|
if (setProcessEnv) {
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2553
|
+
// Ensure AWS credential sources are mutually exclusive.
|
|
2554
|
+
// The AWS SDK warns (and may change precedence in future) when both
|
|
2555
|
+
// AWS_PROFILE and AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY are set.
|
|
2556
|
+
const clear = (keys) => {
|
|
2557
|
+
for (const k of keys) {
|
|
2558
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
2559
|
+
delete process.env[k];
|
|
2443
2560
|
}
|
|
2444
|
-
}
|
|
2561
|
+
};
|
|
2562
|
+
const clearProfileVars = () => {
|
|
2563
|
+
clear(['AWS_PROFILE', 'AWS_DEFAULT_PROFILE', 'AWS_SDK_LOAD_CONFIG']);
|
|
2564
|
+
};
|
|
2565
|
+
const clearStaticCreds = () => {
|
|
2566
|
+
clear([
|
|
2567
|
+
'AWS_ACCESS_KEY_ID',
|
|
2568
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
2569
|
+
'AWS_SESSION_TOKEN',
|
|
2570
|
+
]);
|
|
2571
|
+
};
|
|
2572
|
+
// Mode A: exported/static credentials (clear profile vars)
|
|
2445
2573
|
if (credentials) {
|
|
2574
|
+
clearProfileVars();
|
|
2446
2575
|
process.env.AWS_ACCESS_KEY_ID = credentials.accessKeyId;
|
|
2447
2576
|
process.env.AWS_SECRET_ACCESS_KEY = credentials.secretAccessKey;
|
|
2448
2577
|
if (credentials.sessionToken !== undefined) {
|
|
2449
2578
|
process.env.AWS_SESSION_TOKEN = credentials.sessionToken;
|
|
2450
2579
|
}
|
|
2580
|
+
else {
|
|
2581
|
+
delete process.env.AWS_SESSION_TOKEN;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
else if (profile) {
|
|
2585
|
+
// Mode B: profile-based (SSO) credentials (clear static creds)
|
|
2586
|
+
clearStaticCreds();
|
|
2587
|
+
process.env.AWS_PROFILE = profile;
|
|
2588
|
+
process.env.AWS_DEFAULT_PROFILE = profile;
|
|
2589
|
+
process.env.AWS_SDK_LOAD_CONFIG = '1';
|
|
2590
|
+
}
|
|
2591
|
+
if (region) {
|
|
2592
|
+
process.env.AWS_REGION = region;
|
|
2593
|
+
if (!process.env.AWS_DEFAULT_REGION) {
|
|
2594
|
+
process.env.AWS_DEFAULT_REGION = region;
|
|
2595
|
+
}
|
|
2451
2596
|
}
|
|
2452
2597
|
}
|
|
2453
2598
|
// Always publish minimal, non-sensitive metadata
|
|
@@ -2458,7 +2603,7 @@ function applyAwsContext(out, ctx, setProcessEnv = true) {
|
|
|
2458
2603
|
};
|
|
2459
2604
|
}
|
|
2460
2605
|
|
|
2461
|
-
const
|
|
2606
|
+
const AWS_CLI_TIMEOUT_MS = 15_000;
|
|
2462
2607
|
const trim = (s) => (typeof s === 'string' ? s.trim() : '');
|
|
2463
2608
|
const unquote = (s) => s.length >= 2 &&
|
|
2464
2609
|
((s.startsWith('"') && s.endsWith('"')) ||
|
|
@@ -2493,6 +2638,7 @@ const parseExportCredentialsJson = (txt) => {
|
|
|
2493
2638
|
/**
|
|
2494
2639
|
* Parse AWS credentials from environment-export output (shell-agnostic).
|
|
2495
2640
|
* Supports POSIX `export KEY=VAL` and PowerShell `$Env:KEY=VAL`.
|
|
2641
|
+
* Also supports AWS CLI `windows-cmd` (`set KEY=VAL`) and `env-no-export` (`KEY=VAL`).
|
|
2496
2642
|
*
|
|
2497
2643
|
* @param txt - Raw stdout text from the AWS CLI.
|
|
2498
2644
|
* @returns Parsed credentials, or `undefined` when the input is not recognized.
|
|
@@ -2506,12 +2652,17 @@ const parseExportCredentialsEnv = (txt) => {
|
|
|
2506
2652
|
const line = raw.trim();
|
|
2507
2653
|
if (!line)
|
|
2508
2654
|
continue;
|
|
2509
|
-
// POSIX: export AWS_ACCESS_KEY_ID=...,
|
|
2655
|
+
// POSIX: export AWS_ACCESS_KEY_ID=..., ...
|
|
2510
2656
|
let m = /^export\s+([A-Z0-9_]+)\s*=\s*(.+)$/.exec(line);
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
m = /^\$Env:([A-Z0-9_]+)\s*=\s*(.+)
|
|
2514
|
-
|
|
2657
|
+
// PowerShell: $Env:AWS_ACCESS_KEY_ID="...", etc.
|
|
2658
|
+
if (!m)
|
|
2659
|
+
m = /^\$Env:([A-Z0-9_]+)\s*=\s*(.+)$/i.exec(line);
|
|
2660
|
+
// Windows cmd: set AWS_ACCESS_KEY_ID=..., etc.
|
|
2661
|
+
if (!m)
|
|
2662
|
+
m = /^(?:set)\s+([A-Z0-9_]+)\s*=\s*(.+)$/i.exec(line);
|
|
2663
|
+
// env-no-export: AWS_ACCESS_KEY_ID=..., etc.
|
|
2664
|
+
if (!m)
|
|
2665
|
+
m = /^([A-Z0-9_]+)\s*=\s*(.+)$/.exec(line);
|
|
2515
2666
|
if (!m)
|
|
2516
2667
|
continue;
|
|
2517
2668
|
const k = m[1];
|
|
@@ -2536,7 +2687,7 @@ const parseExportCredentialsEnv = (txt) => {
|
|
|
2536
2687
|
};
|
|
2537
2688
|
return undefined;
|
|
2538
2689
|
};
|
|
2539
|
-
const getAwsConfigure = async (key, profile, timeoutMs =
|
|
2690
|
+
const getAwsConfigure = async (key, profile, timeoutMs = AWS_CLI_TIMEOUT_MS) => {
|
|
2540
2691
|
const r = await runCommandResult(['aws', 'configure', 'get', key, '--profile', profile], false, {
|
|
2541
2692
|
env: process.env,
|
|
2542
2693
|
timeoutMs,
|
|
@@ -2551,30 +2702,43 @@ const getAwsConfigure = async (key, profile, timeoutMs = DEFAULT_TIMEOUT_MS) =>
|
|
|
2551
2702
|
}
|
|
2552
2703
|
return undefined;
|
|
2553
2704
|
};
|
|
2554
|
-
const exportCredentials = async (profile, timeoutMs =
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2705
|
+
const exportCredentials = async (profile, timeoutMs = AWS_CLI_TIMEOUT_MS) => {
|
|
2706
|
+
const tryExport = async (format) => {
|
|
2707
|
+
const argv = [
|
|
2708
|
+
'aws',
|
|
2709
|
+
'configure',
|
|
2710
|
+
'export-credentials',
|
|
2711
|
+
'--profile',
|
|
2712
|
+
profile,
|
|
2713
|
+
...(format ? ['--format', format] : []),
|
|
2714
|
+
];
|
|
2715
|
+
const r = await runCommandResult(argv, false, {
|
|
2716
|
+
env: process.env,
|
|
2717
|
+
timeoutMs,
|
|
2718
|
+
});
|
|
2719
|
+
if (r.exitCode !== 0)
|
|
2720
|
+
return undefined;
|
|
2721
|
+
const out = trim(r.stdout);
|
|
2722
|
+
if (!out)
|
|
2723
|
+
return undefined;
|
|
2724
|
+
// Some formats produce JSON ("process"), some produce shell-ish env lines.
|
|
2725
|
+
return parseExportCredentialsJson(out) ?? parseExportCredentialsEnv(out);
|
|
2726
|
+
};
|
|
2727
|
+
// Prefer the default/JSON "process" format first; then fall back to shell env outputs.
|
|
2728
|
+
// Note: AWS CLI v2 supports: process | env | env-no-export | powershell | windows-cmd
|
|
2729
|
+
const formats = [
|
|
2730
|
+
'process',
|
|
2731
|
+
...(process.platform === 'win32'
|
|
2732
|
+
? ['powershell', 'windows-cmd', 'env', 'env-no-export']
|
|
2733
|
+
: ['env', 'env-no-export']),
|
|
2734
|
+
];
|
|
2735
|
+
for (const f of formats) {
|
|
2736
|
+
const creds = await tryExport(f);
|
|
2574
2737
|
if (creds)
|
|
2575
2738
|
return creds;
|
|
2576
2739
|
}
|
|
2577
|
-
|
|
2740
|
+
// Final fallback: no --format (AWS CLI default output)
|
|
2741
|
+
return tryExport(undefined);
|
|
2578
2742
|
};
|
|
2579
2743
|
/**
|
|
2580
2744
|
* Resolve AWS context (profile, region, credentials) using configuration and environment.
|
|
@@ -2606,31 +2770,27 @@ const resolveAwsContext = async ({ dotenv, cfg, }) => {
|
|
|
2606
2770
|
out.region = region;
|
|
2607
2771
|
return out;
|
|
2608
2772
|
}
|
|
2609
|
-
// Env-first credentials.
|
|
2610
2773
|
let credentials;
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
const envToken = trim(process.env.AWS_SESSION_TOKEN);
|
|
2614
|
-
if (envId && envSecret) {
|
|
2615
|
-
credentials = {
|
|
2616
|
-
accessKeyId: envId,
|
|
2617
|
-
secretAccessKey: envSecret,
|
|
2618
|
-
...(envToken ? { sessionToken: envToken } : {}),
|
|
2619
|
-
};
|
|
2620
|
-
}
|
|
2621
|
-
else if (profile) {
|
|
2774
|
+
// Profile wins over ambient env creds when present (from flags/config/dotenv).
|
|
2775
|
+
if (profile) {
|
|
2622
2776
|
// Try export-credentials
|
|
2623
2777
|
credentials = await exportCredentials(profile);
|
|
2624
2778
|
// On failure, detect SSO and optionally login then retry
|
|
2625
2779
|
if (!credentials) {
|
|
2626
2780
|
const ssoSession = await getAwsConfigure('sso_session', profile);
|
|
2627
|
-
|
|
2781
|
+
// Legacy SSO profiles use sso_start_url/sso_region rather than sso_session.
|
|
2782
|
+
const ssoStartUrl = await getAwsConfigure('sso_start_url', profile);
|
|
2783
|
+
const looksSSO = (typeof ssoSession === 'string' && ssoSession.length > 0) ||
|
|
2784
|
+
(typeof ssoStartUrl === 'string' && ssoStartUrl.length > 0);
|
|
2628
2785
|
if (looksSSO && cfg.loginOnDemand) {
|
|
2629
|
-
//
|
|
2630
|
-
await
|
|
2786
|
+
// Interactive login (no timeout by default), then retry export once.
|
|
2787
|
+
const exit = await runCommand(['aws', 'sso', 'login', '--profile', profile], false, {
|
|
2631
2788
|
env: process.env,
|
|
2632
|
-
|
|
2789
|
+
stdio: 'inherit',
|
|
2633
2790
|
});
|
|
2791
|
+
if (exit !== 0) {
|
|
2792
|
+
throw new Error(`aws sso login failed for profile '${profile}' (exit ${String(exit)})`);
|
|
2793
|
+
}
|
|
2634
2794
|
credentials = await exportCredentials(profile);
|
|
2635
2795
|
}
|
|
2636
2796
|
}
|
|
@@ -2648,6 +2808,19 @@ const resolveAwsContext = async ({ dotenv, cfg, }) => {
|
|
|
2648
2808
|
}
|
|
2649
2809
|
}
|
|
2650
2810
|
}
|
|
2811
|
+
else {
|
|
2812
|
+
// Env-first credentials when no profile is present.
|
|
2813
|
+
const envId = trim(process.env.AWS_ACCESS_KEY_ID);
|
|
2814
|
+
const envSecret = trim(process.env.AWS_SECRET_ACCESS_KEY);
|
|
2815
|
+
const envToken = trim(process.env.AWS_SESSION_TOKEN);
|
|
2816
|
+
if (envId && envSecret) {
|
|
2817
|
+
credentials = {
|
|
2818
|
+
accessKeyId: envId,
|
|
2819
|
+
secretAccessKey: envSecret,
|
|
2820
|
+
...(envToken ? { sessionToken: envToken } : {}),
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2651
2824
|
// Final region resolution
|
|
2652
2825
|
if (!region && profile)
|
|
2653
2826
|
region = await getAwsConfigure('region', profile);
|
|
@@ -2664,16 +2837,234 @@ const resolveAwsContext = async ({ dotenv, cfg, }) => {
|
|
|
2664
2837
|
};
|
|
2665
2838
|
|
|
2666
2839
|
/**
|
|
2667
|
-
*
|
|
2840
|
+
* Create the AWS plugin `afterResolve` hook.
|
|
2841
|
+
*
|
|
2842
|
+
* This runs once per invocation after the host resolves dotenv context.
|
|
2843
|
+
*
|
|
2844
|
+
* @param plugin - The AWS plugin instance.
|
|
2845
|
+
* @returns An `afterResolve` hook function suitable for assigning to `plugin.afterResolve`.
|
|
2846
|
+
*
|
|
2847
|
+
* @internal
|
|
2668
2848
|
*/
|
|
2669
|
-
|
|
2849
|
+
function attachAwsAfterResolveHook(plugin) {
|
|
2850
|
+
return async (cli, ctx) => {
|
|
2851
|
+
const cfg = plugin.readConfig(cli);
|
|
2852
|
+
const out = await resolveAwsContext({
|
|
2853
|
+
dotenv: ctx.dotenv,
|
|
2854
|
+
cfg,
|
|
2855
|
+
});
|
|
2856
|
+
applyAwsContext(out, ctx, true);
|
|
2857
|
+
// Optional: low-noise breadcrumb for diagnostics
|
|
2858
|
+
if (process.env.GETDOTENV_DEBUG) {
|
|
2859
|
+
try {
|
|
2860
|
+
const msg = JSON.stringify({
|
|
2861
|
+
profile: out.profile,
|
|
2862
|
+
region: out.region,
|
|
2863
|
+
hasCreds: Boolean(out.credentials),
|
|
2864
|
+
});
|
|
2865
|
+
process.stderr.write(`[aws] afterResolve ${msg}\n`);
|
|
2866
|
+
}
|
|
2867
|
+
catch {
|
|
2868
|
+
/* ignore */
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
/** @internal */
|
|
2875
|
+
const isRecord = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
2876
|
+
/**
|
|
2877
|
+
* Create an AWS plugin config overlay from Commander-parsed option values.
|
|
2878
|
+
*
|
|
2879
|
+
* This preserves tri-state intent:
|
|
2880
|
+
* - If a flag was not provided, it should not overwrite config-derived defaults.
|
|
2881
|
+
* - If `--no-…` was provided, it must explicitly force the boolean false.
|
|
2882
|
+
*
|
|
2883
|
+
* @param opts - Commander option values for the current invocation.
|
|
2884
|
+
* @returns A partial AWS plugin config object containing only explicit overrides.
|
|
2885
|
+
*
|
|
2886
|
+
* @internal
|
|
2887
|
+
*/
|
|
2888
|
+
function awsConfigOverridesFromCommandOpts(opts) {
|
|
2889
|
+
const o = isRecord(opts) ? opts : {};
|
|
2890
|
+
const overlay = {};
|
|
2891
|
+
// Map boolean toggles (respect explicit --no-*)
|
|
2892
|
+
if (Object.prototype.hasOwnProperty.call(o, 'loginOnDemand')) {
|
|
2893
|
+
overlay.loginOnDemand = Boolean(o.loginOnDemand);
|
|
2894
|
+
}
|
|
2895
|
+
// Strings/enums
|
|
2896
|
+
if (typeof o.profile === 'string')
|
|
2897
|
+
overlay.profile = o.profile;
|
|
2898
|
+
if (typeof o.region === 'string')
|
|
2899
|
+
overlay.region = o.region;
|
|
2900
|
+
if (typeof o.defaultRegion === 'string')
|
|
2901
|
+
overlay.defaultRegion = o.defaultRegion;
|
|
2902
|
+
if (o.strategy === 'cli-export' || o.strategy === 'none') {
|
|
2903
|
+
overlay.strategy = o.strategy;
|
|
2904
|
+
}
|
|
2905
|
+
// Advanced key overrides
|
|
2906
|
+
if (typeof o.profileKey === 'string')
|
|
2907
|
+
overlay.profileKey = o.profileKey;
|
|
2908
|
+
if (typeof o.profileFallbackKey === 'string') {
|
|
2909
|
+
overlay.profileFallbackKey = o.profileFallbackKey;
|
|
2910
|
+
}
|
|
2911
|
+
if (typeof o.regionKey === 'string')
|
|
2912
|
+
overlay.regionKey = o.regionKey;
|
|
2913
|
+
return overlay;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
/**
|
|
2917
|
+
* Attach the default action for the AWS plugin mount.
|
|
2918
|
+
*
|
|
2919
|
+
* Behavior:
|
|
2920
|
+
* - With args: forwards to AWS CLI (`aws <args...>`) under the established session.
|
|
2921
|
+
* - Without args: session-only establishment (no forward).
|
|
2922
|
+
*
|
|
2923
|
+
* @param cli - The `aws` command mount.
|
|
2924
|
+
* @param plugin - The AWS plugin instance.
|
|
2925
|
+
*
|
|
2926
|
+
* @internal
|
|
2927
|
+
*/
|
|
2928
|
+
function attachAwsDefaultAction(cli, plugin, awsCmd) {
|
|
2929
|
+
awsCmd.action(async (args, opts, thisCommand) => {
|
|
2930
|
+
// Access merged root CLI options (installed by root hooks).
|
|
2931
|
+
const bag = readMergedOptions(thisCommand);
|
|
2932
|
+
const capture = shouldCapture(bag.capture);
|
|
2933
|
+
const underTests = process.env.GETDOTENV_TEST === '1' ||
|
|
2934
|
+
typeof process.env.VITEST_WORKER_ID === 'string';
|
|
2935
|
+
// Build overlay cfg from subcommand flags layered over discovered config.
|
|
2936
|
+
const ctx = cli.getCtx();
|
|
2937
|
+
const cfgBase = plugin.readConfig(cli);
|
|
2938
|
+
const cfg = {
|
|
2939
|
+
...cfgBase,
|
|
2940
|
+
...awsConfigOverridesFromCommandOpts(opts),
|
|
2941
|
+
};
|
|
2942
|
+
// Resolve current context with overrides
|
|
2943
|
+
const out = await resolveAwsContext({
|
|
2944
|
+
dotenv: ctx.dotenv,
|
|
2945
|
+
cfg,
|
|
2946
|
+
});
|
|
2947
|
+
// Publish env/context
|
|
2948
|
+
applyAwsContext(out, ctx, true);
|
|
2949
|
+
// Forward when positional args are present; otherwise session-only.
|
|
2950
|
+
if (args.length > 0) {
|
|
2951
|
+
const argv = ['aws', ...args];
|
|
2952
|
+
const shellSetting = resolveShell(bag.scripts, 'aws', bag.shell);
|
|
2953
|
+
const exit = await runCommand(argv, shellSetting, {
|
|
2954
|
+
env: buildSpawnEnv(process.env, ctx.dotenv),
|
|
2955
|
+
stdio: capture ? 'pipe' : 'inherit',
|
|
2956
|
+
});
|
|
2957
|
+
// Deterministic termination (suppressed under tests)
|
|
2958
|
+
if (!underTests) {
|
|
2959
|
+
process.exit(typeof exit === 'number' ? exit : 0);
|
|
2960
|
+
}
|
|
2961
|
+
return;
|
|
2962
|
+
}
|
|
2963
|
+
// Session only: low-noise breadcrumb under debug
|
|
2964
|
+
if (process.env.GETDOTENV_DEBUG) {
|
|
2965
|
+
try {
|
|
2966
|
+
const msg = JSON.stringify({
|
|
2967
|
+
profile: out.profile,
|
|
2968
|
+
region: out.region,
|
|
2969
|
+
hasCreds: Boolean(out.credentials),
|
|
2970
|
+
});
|
|
2971
|
+
process.stderr.write(`[aws] session established ${msg}\n`);
|
|
2972
|
+
}
|
|
2973
|
+
catch {
|
|
2974
|
+
/* ignore */
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
if (!underTests)
|
|
2978
|
+
process.exit(0);
|
|
2979
|
+
});
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
/**
|
|
2983
|
+
* Attach options/arguments for the AWS plugin mount.
|
|
2984
|
+
*
|
|
2985
|
+
* @param cli - The `aws` command mount.
|
|
2986
|
+
* @param plugin - The AWS plugin instance (for dynamic option descriptions).
|
|
2987
|
+
*
|
|
2988
|
+
* @internal
|
|
2989
|
+
*/
|
|
2990
|
+
/** @hidden */
|
|
2991
|
+
function attachAwsOptions(cli, plugin) {
|
|
2992
|
+
return (cli
|
|
2993
|
+
// Description is owned by the plugin index (src/plugins/aws/index.ts).
|
|
2994
|
+
.enablePositionalOptions()
|
|
2995
|
+
.passThroughOptions()
|
|
2996
|
+
.allowUnknownOption(true)
|
|
2997
|
+
// Boolean toggles with dynamic help labels (effective defaults)
|
|
2998
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--login-on-demand', (_bag, cfg) => `attempt aws sso login on-demand${cfg.loginOnDemand ? ' (default)' : ''}`))
|
|
2999
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--no-login-on-demand', (_bag, cfg) => `disable sso login on-demand${cfg.loginOnDemand === false ? ' (default)' : ''}`))
|
|
3000
|
+
// Strings / enums
|
|
3001
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--profile <string>', (_bag, cfg) => `AWS profile name${cfg.profile ? ` (default: ${JSON.stringify(cfg.profile)})` : ''}`))
|
|
3002
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--region <string>', (_bag, cfg) => `AWS region${cfg.region ? ` (default: ${JSON.stringify(cfg.region)})` : ''}`))
|
|
3003
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--default-region <string>', (_bag, cfg) => `fallback region${cfg.defaultRegion ? ` (default: ${JSON.stringify(cfg.defaultRegion)})` : ''}`))
|
|
3004
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--strategy <string>', (_bag, cfg) => `credential acquisition strategy: cli-export|none${cfg.strategy ? ` (default: ${JSON.stringify(cfg.strategy)})` : ''}`))
|
|
3005
|
+
// Advanced key overrides
|
|
3006
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--profile-key <string>', (_bag, cfg) => `dotenv/config key for local profile${cfg.profileKey ? ` (default: ${JSON.stringify(cfg.profileKey)})` : ''}`))
|
|
3007
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--profile-fallback-key <string>', (_bag, cfg) => `fallback dotenv/config key for profile${cfg.profileFallbackKey ? ` (default: ${JSON.stringify(cfg.profileFallbackKey)})` : ''}`))
|
|
3008
|
+
.addOption(plugin.createPluginDynamicOption(cli, '--region-key <string>', (_bag, cfg) => `dotenv/config key for region${cfg.regionKey ? ` (default: ${JSON.stringify(cfg.regionKey)})` : ''}`))
|
|
3009
|
+
// Accept any extra operands so Commander does not error when tokens appear after "--".
|
|
3010
|
+
.argument('[args...]'));
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
/**
|
|
3014
|
+
* Attach the AWS plugin `preSubcommand` hook.
|
|
3015
|
+
*
|
|
3016
|
+
* Ensures `aws --profile/--region <child>` applies the AWS session setup before
|
|
3017
|
+
* child subcommand execution.
|
|
3018
|
+
*
|
|
3019
|
+
* @param cli - The `aws` command mount.
|
|
3020
|
+
* @param plugin - The AWS plugin instance.
|
|
3021
|
+
*
|
|
3022
|
+
* @internal
|
|
3023
|
+
*/
|
|
3024
|
+
function attachAwsPreSubcommandHook(cli, plugin) {
|
|
3025
|
+
cli.hook('preSubcommand', async (thisCommand) => {
|
|
3026
|
+
// Avoid side effects for help rendering.
|
|
3027
|
+
if (process.argv.includes('-h') || process.argv.includes('--help'))
|
|
3028
|
+
return;
|
|
3029
|
+
const ctx = cli.getCtx();
|
|
3030
|
+
const cfgBase = plugin.readConfig(cli);
|
|
3031
|
+
const cfg = {
|
|
3032
|
+
...cfgBase,
|
|
3033
|
+
...awsConfigOverridesFromCommandOpts(thisCommand.opts()),
|
|
3034
|
+
};
|
|
3035
|
+
const out = await resolveAwsContext({
|
|
3036
|
+
dotenv: ctx.dotenv,
|
|
3037
|
+
cfg,
|
|
3038
|
+
});
|
|
3039
|
+
applyAwsContext(out, ctx, true);
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
/**
|
|
3044
|
+
* AWS plugin configuration schema.
|
|
3045
|
+
*
|
|
3046
|
+
* @remarks
|
|
3047
|
+
* This Zod schema is used by the host to validate the `plugins.aws` config slice.
|
|
3048
|
+
*
|
|
3049
|
+
* @public
|
|
3050
|
+
* @hidden
|
|
3051
|
+
*/
|
|
3052
|
+
const awsPluginConfigSchema = z.object({
|
|
3053
|
+
/** Preferred AWS profile name (overrides dotenv-derived profile keys when set). */
|
|
2670
3054
|
profile: z.string().optional(),
|
|
3055
|
+
/** Preferred AWS region (overrides dotenv-derived region key when set). */
|
|
2671
3056
|
region: z.string().optional(),
|
|
3057
|
+
/** Fallback region when region cannot be resolved from config/dotenv/AWS CLI. */
|
|
2672
3058
|
defaultRegion: z.string().optional(),
|
|
3059
|
+
/** Dotenv/config key for local profile lookup (default `AWS_LOCAL_PROFILE`). */
|
|
2673
3060
|
profileKey: z.string().default('AWS_LOCAL_PROFILE').optional(),
|
|
3061
|
+
/** Dotenv/config fallback key for profile lookup (default `AWS_PROFILE`). */
|
|
2674
3062
|
profileFallbackKey: z.string().default('AWS_PROFILE').optional(),
|
|
3063
|
+
/** Dotenv/config key for region lookup (default `AWS_REGION`). */
|
|
2675
3064
|
regionKey: z.string().default('AWS_REGION').optional(),
|
|
3065
|
+
/** Credential acquisition strategy (`cli-export` to resolve via AWS CLI, or `none` to skip). */
|
|
2676
3066
|
strategy: z.enum(['cli-export', 'none']).default('cli-export').optional(),
|
|
3067
|
+
/** When true, attempt `aws sso login` on-demand when credential export fails for an SSO profile. */
|
|
2677
3068
|
loginOnDemand: z.boolean().default(false).optional(),
|
|
2678
3069
|
});
|
|
2679
3070
|
|
|
@@ -2690,129 +3081,16 @@ const AwsPluginConfigSchema = z.object({
|
|
|
2690
3081
|
const awsPlugin = () => {
|
|
2691
3082
|
const plugin = definePlugin({
|
|
2692
3083
|
ns: 'aws',
|
|
2693
|
-
configSchema:
|
|
2694
|
-
setup
|
|
2695
|
-
|
|
2696
|
-
cli
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
.passThroughOptions()
|
|
2700
|
-
.allowUnknownOption(true)
|
|
2701
|
-
// Boolean toggles with dynamic help labels (effective defaults)
|
|
2702
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--login-on-demand', (_bag, cfg) => `attempt aws sso login on-demand${cfg.loginOnDemand ? ' (default)' : ''}`))
|
|
2703
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--no-login-on-demand', (_bag, cfg) => `disable sso login on-demand${cfg.loginOnDemand === false ? ' (default)' : ''}`))
|
|
2704
|
-
// Strings / enums
|
|
2705
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--profile <string>', (_bag, cfg) => `AWS profile name${cfg.profile ? ` (default: ${JSON.stringify(cfg.profile)})` : ''}`))
|
|
2706
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--region <string>', (_bag, cfg) => `AWS region${cfg.region ? ` (default: ${JSON.stringify(cfg.region)})` : ''}`))
|
|
2707
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--default-region <string>', (_bag, cfg) => `fallback region${cfg.defaultRegion ? ` (default: ${JSON.stringify(cfg.defaultRegion)})` : ''}`))
|
|
2708
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--strategy <string>', (_bag, cfg) => `credential acquisition strategy: cli-export|none${cfg.strategy ? ` (default: ${JSON.stringify(cfg.strategy)})` : ''}`))
|
|
2709
|
-
// Advanced key overrides
|
|
2710
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--profile-key <string>', (_bag, cfg) => `dotenv/config key for local profile${cfg.profileKey ? ` (default: ${JSON.stringify(cfg.profileKey)})` : ''}`))
|
|
2711
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--profile-fallback-key <string>', (_bag, cfg) => `fallback dotenv/config key for profile${cfg.profileFallbackKey ? ` (default: ${JSON.stringify(cfg.profileFallbackKey)})` : ''}`))
|
|
2712
|
-
.addOption(plugin.createPluginDynamicOption(cli, '--region-key <string>', (_bag, cfg) => `dotenv/config key for region${cfg.regionKey ? ` (default: ${JSON.stringify(cfg.regionKey)})` : ''}`))
|
|
2713
|
-
// Accept any extra operands so Commander does not error when tokens appear after "--".
|
|
2714
|
-
.argument('[args...]')
|
|
2715
|
-
.action(async (args, opts, thisCommand) => {
|
|
2716
|
-
const pluginInst = plugin;
|
|
2717
|
-
// Access merged root CLI options (installed by passOptions())
|
|
2718
|
-
const bag = readMergedOptions(thisCommand);
|
|
2719
|
-
const capture = shouldCapture(bag.capture);
|
|
2720
|
-
const underTests = process.env.GETDOTENV_TEST === '1' ||
|
|
2721
|
-
typeof process.env.VITEST_WORKER_ID === 'string';
|
|
2722
|
-
// Build overlay cfg from subcommand flags layered over discovered config.
|
|
2723
|
-
const ctx = cli.getCtx();
|
|
2724
|
-
const cfgBase = pluginInst.readConfig(cli);
|
|
2725
|
-
const o = opts;
|
|
2726
|
-
const overlay = {};
|
|
2727
|
-
// Map boolean toggles (respect explicit --no-*)
|
|
2728
|
-
if (Object.prototype.hasOwnProperty.call(o, 'loginOnDemand'))
|
|
2729
|
-
overlay.loginOnDemand = Boolean(o.loginOnDemand);
|
|
2730
|
-
// Strings/enums
|
|
2731
|
-
if (typeof o.profile === 'string')
|
|
2732
|
-
overlay.profile = o.profile;
|
|
2733
|
-
if (typeof o.region === 'string')
|
|
2734
|
-
overlay.region = o.region;
|
|
2735
|
-
if (typeof o.defaultRegion === 'string')
|
|
2736
|
-
overlay.defaultRegion = o.defaultRegion;
|
|
2737
|
-
if (typeof o.strategy === 'string')
|
|
2738
|
-
overlay.strategy = o.strategy;
|
|
2739
|
-
// Advanced key overrides
|
|
2740
|
-
if (typeof o.profileKey === 'string')
|
|
2741
|
-
overlay.profileKey = o.profileKey;
|
|
2742
|
-
if (typeof o.profileFallbackKey === 'string')
|
|
2743
|
-
overlay.profileFallbackKey = o.profileFallbackKey;
|
|
2744
|
-
if (typeof o.regionKey === 'string')
|
|
2745
|
-
overlay.regionKey = o.regionKey;
|
|
2746
|
-
const cfg = {
|
|
2747
|
-
...cfgBase,
|
|
2748
|
-
...overlay,
|
|
2749
|
-
};
|
|
2750
|
-
// Resolve current context with overrides
|
|
2751
|
-
const out = await resolveAwsContext({
|
|
2752
|
-
dotenv: ctx.dotenv,
|
|
2753
|
-
cfg,
|
|
2754
|
-
});
|
|
2755
|
-
// Publish env/context
|
|
2756
|
-
applyAwsContext(out, ctx, true);
|
|
2757
|
-
// Forward when positional args are present; otherwise session-only.
|
|
2758
|
-
if (Array.isArray(args) && args.length > 0) {
|
|
2759
|
-
const argv = ['aws', ...args];
|
|
2760
|
-
const shellSetting = resolveShell(bag.scripts, 'aws', bag.shell);
|
|
2761
|
-
const exit = await runCommand(argv, shellSetting, {
|
|
2762
|
-
env: buildSpawnEnv(process.env, ctx.dotenv),
|
|
2763
|
-
stdio: capture ? 'pipe' : 'inherit',
|
|
2764
|
-
});
|
|
2765
|
-
// Deterministic termination (suppressed under tests)
|
|
2766
|
-
if (!underTests) {
|
|
2767
|
-
process.exit(typeof exit === 'number' ? exit : 0);
|
|
2768
|
-
}
|
|
2769
|
-
return;
|
|
2770
|
-
}
|
|
2771
|
-
else {
|
|
2772
|
-
// Session only: low-noise breadcrumb under debug
|
|
2773
|
-
if (process.env.GETDOTENV_DEBUG) {
|
|
2774
|
-
try {
|
|
2775
|
-
const msg = JSON.stringify({
|
|
2776
|
-
profile: out.profile,
|
|
2777
|
-
region: out.region,
|
|
2778
|
-
hasCreds: Boolean(out.credentials),
|
|
2779
|
-
});
|
|
2780
|
-
process.stderr.write(`[aws] session established ${msg}\n`);
|
|
2781
|
-
}
|
|
2782
|
-
catch {
|
|
2783
|
-
/* ignore */
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
|
-
if (!underTests)
|
|
2787
|
-
process.exit(0);
|
|
2788
|
-
return;
|
|
2789
|
-
}
|
|
2790
|
-
});
|
|
3084
|
+
configSchema: awsPluginConfigSchema,
|
|
3085
|
+
setup(cli) {
|
|
3086
|
+
cli.description('Establish an AWS session and optionally forward to the AWS CLI');
|
|
3087
|
+
const awsCmd = attachAwsOptions(cli, plugin);
|
|
3088
|
+
attachAwsPreSubcommandHook(cli, plugin);
|
|
3089
|
+
attachAwsDefaultAction(cli, plugin, awsCmd);
|
|
2791
3090
|
return undefined;
|
|
2792
3091
|
},
|
|
2793
|
-
afterResolve: async (_cli, ctx) => {
|
|
2794
|
-
const cfg = plugin.readConfig(_cli);
|
|
2795
|
-
const out = await resolveAwsContext({
|
|
2796
|
-
dotenv: ctx.dotenv,
|
|
2797
|
-
cfg,
|
|
2798
|
-
});
|
|
2799
|
-
applyAwsContext(out, ctx, true);
|
|
2800
|
-
// Optional: low-noise breadcrumb for diagnostics
|
|
2801
|
-
if (process.env.GETDOTENV_DEBUG) {
|
|
2802
|
-
try {
|
|
2803
|
-
const msg = JSON.stringify({
|
|
2804
|
-
profile: out.profile,
|
|
2805
|
-
region: out.region,
|
|
2806
|
-
hasCreds: Boolean(out.credentials),
|
|
2807
|
-
});
|
|
2808
|
-
process.stderr.write(`[aws] afterResolve ${msg}\n`);
|
|
2809
|
-
}
|
|
2810
|
-
catch {
|
|
2811
|
-
/* ignore */
|
|
2812
|
-
}
|
|
2813
|
-
}
|
|
2814
|
-
},
|
|
2815
3092
|
});
|
|
3093
|
+
plugin.afterResolve = attachAwsAfterResolveHook(plugin);
|
|
2816
3094
|
return plugin;
|
|
2817
3095
|
};
|
|
2818
3096
|
|