switch-profile 2.1.0 → 2.3.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 +13 -3
- package/dist/cli.js +193 -66
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -64,6 +64,8 @@ npx switch-profile
|
|
|
64
64
|
- [Requirements](#requirements)
|
|
65
65
|
- [Troubleshooting](#troubleshooting)
|
|
66
66
|
- [`invalid_grant` during an SSO login](#invalid_grant-during-an-sso-login)
|
|
67
|
+
- [`Invalid start url provided` when adding an SSO profile](#invalid-start-url-provided-when-adding-an-sso-profile)
|
|
68
|
+
- [Something failed and the message is not enough](#something-failed-and-the-message-is-not-enough)
|
|
67
69
|
- [`Error loading SSO Token` or "session has expired"](#error-loading-sso-token-or-session-has-expired)
|
|
68
70
|
- [SSO login hangs on a remote machine](#sso-login-hangs-on-a-remote-machine)
|
|
69
71
|
- [The code is not in my clipboard over SSH](#the-code-is-not-in-my-clipboard-over-ssh)
|
|
@@ -116,7 +118,7 @@ Running `sp` (or `npx switch-profile`) with no arguments opens the home screen:
|
|
|
116
118
|
| `remove [profiles...] [--yes]` | Removes profiles (alias `rm`). The current default cannot be removed. |
|
|
117
119
|
| `settings` | Per-terminal switching, SSO login mode, legacy SSO upgrade. |
|
|
118
120
|
|
|
119
|
-
Global options: `--json`, `--no-input` (never prompt), `--debug` (show stack traces), `-v, --version`. In examples, `sp` and `switch-profile` are interchangeable; only `sp` changes the current terminal.
|
|
121
|
+
Global options: `--json`, `--no-input` (never prompt), `--debug` (show stack traces), `-v, --version`. Every run, AWS CLI call and error is logged to `~/.switch-profile/switch-profile.log` (secrets redacted). In examples, `sp` and `switch-profile` are interchangeable; only `sp` changes the current terminal.
|
|
120
122
|
|
|
121
123
|
See [CLI Interface](docs/cli-interface.md) for every screen.
|
|
122
124
|
|
|
@@ -154,7 +156,7 @@ sp add # then choose "Accounts from an SSO portal"
|
|
|
154
156
|
1. Pick an existing `[sso-session]` or create one: start URL (e.g. `https://acme.awsapps.com/start`), SSO region (where Identity Center lives, not where you deploy), and a short name, usually the client.
|
|
155
157
|
2. Log in if needed.
|
|
156
158
|
3. `switch-profile` lists every account and role you can access, then asks for a default region and a name prefix. Profiles are named `<prefix>-<account-name>-<role>`, for example `acme-prod-workloads-admin`.
|
|
157
|
-
4. A preview shows the new profiles, the ones already set up, and the ones that no longer exist.
|
|
159
|
+
4. A preview shows the new profiles, the ones already set up, and the ones that no longer exist. Then pick the new profiles to add: all are selected, so press Enter to add everything, or untick the ones you don't want (type to search by name, account or role).
|
|
158
160
|
|
|
159
161
|
Run it again later to pick up new accounts. Account/role pairs that already have a profile are never duplicated, whatever its name. Generated profiles that no longer exist can be pruned (after a backup); profiles you wrote by hand are never pruned.
|
|
160
162
|
|
|
@@ -235,6 +237,14 @@ The published package has no runtime dependencies: everything is bundled into on
|
|
|
235
237
|
|
|
236
238
|
The SSO region is wrong. It must be the region of your IAM Identity Center instance, not the region you deploy to. Fix `sso_region` in the `[sso-session]` section of `~/.aws/config` (or remove the portal and add it again).
|
|
237
239
|
|
|
240
|
+
### `Invalid start url provided` when adding an SSO profile
|
|
241
|
+
|
|
242
|
+
The start URL is usually fine: AWS sends this error when the **SSO region** is wrong. Use the region where IAM Identity Center lives (Identity Center console › Settings), not where you deploy. If the portal is already set up, type its existing session name instead (`sp add` lists them with their regions).
|
|
243
|
+
|
|
244
|
+
### Something failed and the message is not enough
|
|
245
|
+
|
|
246
|
+
Open `~/.switch-profile/switch-profile.log`. It has every run, every AWS CLI call with its exit code and output, and every error with its full chain. Secrets are redacted, so you can share it.
|
|
247
|
+
|
|
238
248
|
### `Error loading SSO Token` or "session has expired"
|
|
239
249
|
|
|
240
250
|
The SSO login is missing or expired. Run `sp login` (or just switch to the profile again: `switch-profile` logs in when needed). There is no need to recreate the profile.
|
|
@@ -271,7 +281,7 @@ The completion registers only if `compinit` ran before the `switch-profile` bloc
|
|
|
271
281
|
- [Architecture](docs/architecture.md) — Module map, data flow and bundling of the switch-profile CLI.
|
|
272
282
|
- [AWS Profile Management](docs/aws-profile-management.md) — Profile kinds, switching, SSO logins, SSO import, migrations and the sp shell function.
|
|
273
283
|
- [CLI Interface](docs/cli-interface.md) — Walkthrough of every command and screen of the CLI.
|
|
274
|
-
- [Configuration Files](docs/configuration-files.md) — Exact formats of the AWS files, the settings
|
|
284
|
+
- [Configuration Files](docs/configuration-files.md) — Exact formats of the AWS files, the settings and log files and the shell block written by switch-profile.
|
|
275
285
|
- [Development Guide](docs/development-guide.md) — Build, lint, tests, manual testing and releases.
|
|
276
286
|
- [Gotchas](docs/gotchas.md) — Non-obvious pitfalls for users and maintainers.
|
|
277
287
|
<!-- END doc-index -->
|
package/dist/cli.js
CHANGED
|
@@ -5235,10 +5235,77 @@ ${i2}
|
|
|
5235
5235
|
}
|
|
5236
5236
|
});
|
|
5237
5237
|
|
|
5238
|
+
// src/log.js
|
|
5239
|
+
var require_log = __commonJS({
|
|
5240
|
+
"src/log.js"(exports2, module2) {
|
|
5241
|
+
var fs = require("fs"), { homedir } = require("os"), { join: join2, dirname: dirname2 } = require("path"), logFile = () => process.env.SWITCH_PROFILE_LOG_FILE || join2(homedir(), ".switch-profile", "switch-profile.log"), MAX_BYTES = 1024 * 1024, MAX_OUTPUT = 4e3, SECRET_FLAGS = ["--access-token", "--client-secret", "--otp"], SECRET_PATTERNS = [
|
|
5242
|
+
/((?:aws_secret_access_key|aws_session_token|aws_security_token)\s*[=:]\s*)\S+/gi,
|
|
5243
|
+
/("?(?:accessToken|refreshToken|clientSecret|secretAccessKey|sessionToken)"?\s*[=:]\s*"?)[^\s",}]+/gi,
|
|
5244
|
+
/(--(?:access-token|client-secret|otp)[=\s]+)\S+/gi
|
|
5245
|
+
], redact = (text2) => SECRET_PATTERNS.reduce((s, re) => s.replace(re, "$1***"), String(text2 ?? "")), redactArgs = (args) => (args || []).map((a2, i2) => SECRET_FLAGS.includes(args[i2 - 1]) ? "***" : redact(a2)), truncate = (text2, max = MAX_OUTPUT) => {
|
|
5246
|
+
let s = String(text2 ?? "").trim();
|
|
5247
|
+
return s.length > max ? `\u2026${s.slice(-max)}` : s;
|
|
5248
|
+
}, indent = (text2) => String(text2).split(`
|
|
5249
|
+
`).map((l2) => ` ${l2}`).join(`
|
|
5250
|
+
`), describeError = (err, depth = 0) => {
|
|
5251
|
+
if (depth > 8)
|
|
5252
|
+
return " (cause chain truncated)";
|
|
5253
|
+
if (!(err instanceof Error))
|
|
5254
|
+
return ` ${redact(typeof err == "string" ? err : JSON.stringify(err))}`;
|
|
5255
|
+
let lines = [` ${err.name || "Error"}: ${redact(err.message)}`];
|
|
5256
|
+
err.hint && lines.push(` hint: ${redact(err.hint)}`), err.exitCode != null && lines.push(` exit code: ${err.exitCode}`), err.command && lines.push(` command: ${redact(err.command)}`), err.code != null && err.exitCode == null && lines.push(` code: ${err.code}`), err.output && lines.push(" output:", indent(redact(truncate(err.output)))), err.stack && lines.push(indent(redact(err.stack.split(`
|
|
5257
|
+
`).slice(1).join(`
|
|
5258
|
+
`).replace(/^\s+/gm, ""))));
|
|
5259
|
+
for (let e of err.errors || [])
|
|
5260
|
+
lines.push(" wrapped:", describeError(e, depth + 1));
|
|
5261
|
+
return err.cause && lines.push(" caused by:", describeError(err.cause, depth + 1)), lines.join(`
|
|
5262
|
+
`);
|
|
5263
|
+
}, rotated = !1, write = (level, message, details) => {
|
|
5264
|
+
try {
|
|
5265
|
+
let file = logFile();
|
|
5266
|
+
if (fs.existsSync(dirname2(file)) || fs.mkdirSync(dirname2(file), { recursive: !0, mode: 448 }), !rotated) {
|
|
5267
|
+
rotated = !0;
|
|
5268
|
+
let stat = fs.statSync(file, { throwIfNoEntry: !1 });
|
|
5269
|
+
stat && stat.size > MAX_BYTES && fs.renameSync(file, `${file}.1`);
|
|
5270
|
+
}
|
|
5271
|
+
let line = `${(/* @__PURE__ */ new Date()).toISOString()} [${process.pid}] ${level.padEnd(5)} ${redact(message)}${details ? `
|
|
5272
|
+
${details}` : ""}
|
|
5273
|
+
`;
|
|
5274
|
+
fs.appendFileSync(file, line, { mode: 384 });
|
|
5275
|
+
} catch {
|
|
5276
|
+
}
|
|
5277
|
+
}, info = (message, details) => write("INFO", message, details && redact(details)), warn = (message, details) => write("WARN", message, details && redact(details)), error = (context, err) => write("ERROR", context, describeError(err)), tolerated = (context, fallback) => (err) => (write("WARN", `${context} failed (ignored)`, describeError(err)), fallback), command = ({ cmd, args, code, ms, output, inherit }) => {
|
|
5278
|
+
let line = `${cmd} ${redactArgs(args).join(" ")}`.trim(), status = code === 0 ? "ok" : `exit ${code}`, details = code !== 0 && output ? indent(redact(truncate(output))) : null;
|
|
5279
|
+
write(code === 0 ? "INFO" : "WARN", `exec ${line} \u2192 ${status} in ${ms}ms${inherit ? " (interactive)" : ""}`, details);
|
|
5280
|
+
}, start = ({ version, argv }) => write("INFO", `run switch-profile ${version} ${redactArgs((argv || []).slice(2)).join(" ")}`.trim(), indent([
|
|
5281
|
+
`node ${process.version} \xB7 ${process.platform} ${process.arch} \xB7 ${process.stdout.isTTY ? "tty" : "no tty"}${process.env.SSH_CONNECTION ? " \xB7 ssh" : ""}`,
|
|
5282
|
+
`shell ${process.env.SWITCH_PROFILE_SHELL || process.env.SHELL || "?"}${process.env.SWITCH_PROFILE_ENV_FILE ? " (via sp)" : ""}`
|
|
5283
|
+
].join(`
|
|
5284
|
+
`))), displayPath = () => {
|
|
5285
|
+
let file = logFile(), home = homedir();
|
|
5286
|
+
return file.startsWith(home + "/") || file.startsWith(home + "\\") ? `~${file.slice(home.length)}` : file;
|
|
5287
|
+
};
|
|
5288
|
+
module2.exports = {
|
|
5289
|
+
logFile,
|
|
5290
|
+
MAX_BYTES,
|
|
5291
|
+
redact,
|
|
5292
|
+
redactArgs,
|
|
5293
|
+
describeError,
|
|
5294
|
+
displayPath,
|
|
5295
|
+
start,
|
|
5296
|
+
info,
|
|
5297
|
+
warn,
|
|
5298
|
+
error,
|
|
5299
|
+
tolerated,
|
|
5300
|
+
command
|
|
5301
|
+
};
|
|
5302
|
+
}
|
|
5303
|
+
});
|
|
5304
|
+
|
|
5238
5305
|
// src/ui.js
|
|
5239
5306
|
var require_ui = __commonJS({
|
|
5240
5307
|
"src/ui.js"(exports2, module2) {
|
|
5241
|
-
var { styleText: styleText3 } = require("node:util"), env = process.env, colorEnabled = () => env.FORCE_COLOR && env.FORCE_COLOR !== "0" ? !0 : env.NO_COLOR || env.TERM == "dumb" ? !1 : !!process.stdout.isTTY, style = (format, text2) => colorEnabled() ? styleText3(format, String(text2)) : String(text2), unicode2 = process.platform !== "win32" || !!env.WT_SESSION || env.TERM_PROGRAM == "vscode" || !!env.TERMINAL_EMULATOR, sym = {
|
|
5308
|
+
var { styleText: styleText3 } = require("node:util"), log3 = require_log(), env = process.env, colorEnabled = () => env.FORCE_COLOR && env.FORCE_COLOR !== "0" ? !0 : env.NO_COLOR || env.TERM == "dumb" ? !1 : !!process.stdout.isTTY, style = (format, text2) => colorEnabled() ? styleText3(format, String(text2)) : String(text2), unicode2 = process.platform !== "win32" || !!env.WT_SESSION || env.TERM_PROGRAM == "vscode" || !!env.TERMINAL_EMULATOR, sym = {
|
|
5242
5309
|
ok: unicode2 ? "\u2713" : "v",
|
|
5243
5310
|
err: unicode2 ? "\u2717" : "x",
|
|
5244
5311
|
warn: unicode2 ? "\u25B2" : "!",
|
|
@@ -5265,13 +5332,13 @@ var require_ui = __commonJS({
|
|
|
5265
5332
|
fit: (text2, width) => (text2 = String(text2 || ""), text2.length > width ? width > 1 ? text2.slice(0, width - 1) + (unicode2 ? "\u2026" : "~") : text2.slice(0, width) : text2 + " ".repeat(width - text2.length)),
|
|
5266
5333
|
columns: () => process.stdout.columns || 100
|
|
5267
5334
|
}, isInteractive = (options) => options && options.input === !1 || env.CI && env.CI !== "false" ? !1 : !!(process.stdin.isTTY && process.stdout.isTTY), CliError = class extends Error {
|
|
5268
|
-
constructor(message, { hint, code, cause } = {}) {
|
|
5269
|
-
super(message), this.hint = hint, this.exitCode = code || 1, this.cause = cause;
|
|
5335
|
+
constructor(message, { hint, code, cause, errors } = {}) {
|
|
5336
|
+
super(message), this.name = "CliError", this.hint = hint, this.exitCode = code || 1, this.cause = cause, this.errors = errors;
|
|
5270
5337
|
}
|
|
5271
|
-
}, debugEnabled = () => !!(env.SWITCH_PROFILE_DEBUG || env.DEBUG && /switch-profile|\*/.test(env.DEBUG)), errorsMessage = (errors) => (errors || []).map((e) => e && e.message || String(e)).filter(Boolean).join(`
|
|
5338
|
+
}, cliErrorFrom = (errors, options) => new CliError(errorsMessage(errors), { ...options, errors }), debugEnabled = () => !!(env.SWITCH_PROFILE_DEBUG || env.DEBUG && /switch-profile|\*/.test(env.DEBUG)), errorsMessage = (errors) => (errors || []).map((e) => e && e.message || String(e)).filter(Boolean).join(`
|
|
5272
5339
|
`), printError = (err) => {
|
|
5273
5340
|
let lines = [`${ui2.bad(sym.err)} ${err.message || String(err)}`];
|
|
5274
|
-
err.hint && lines.push(` ${ui2.dim(err.hint)}`), debugEnabled() && err.stack
|
|
5341
|
+
err.hint && lines.push(` ${ui2.dim(err.hint)}`), debugEnabled() && err.stack && lines.push(ui2.dim(err.stack)), (!(err instanceof CliError) || err.exitCode == 1) && lines.push(` ${ui2.dim(`Details: ${log3.displayPath()}`)}`), console.error(lines.join(`
|
|
5275
5342
|
`));
|
|
5276
5343
|
}, ago = (date2) => {
|
|
5277
5344
|
let ms = Date.now() - new Date(date2).getTime();
|
|
@@ -5341,6 +5408,7 @@ var require_ui = __commonJS({
|
|
|
5341
5408
|
debugEnabled,
|
|
5342
5409
|
errorsMessage,
|
|
5343
5410
|
printError,
|
|
5411
|
+
cliErrorFrom,
|
|
5344
5412
|
ago
|
|
5345
5413
|
};
|
|
5346
5414
|
}
|
|
@@ -5351,7 +5419,7 @@ var require_package = __commonJS({
|
|
|
5351
5419
|
"package.json"(exports2, module2) {
|
|
5352
5420
|
module2.exports = {
|
|
5353
5421
|
name: "switch-profile",
|
|
5354
|
-
version: "2.
|
|
5422
|
+
version: "2.3.0",
|
|
5355
5423
|
main: "index.js",
|
|
5356
5424
|
bin: {
|
|
5357
5425
|
"switch-profile": "index.js"
|
|
@@ -5396,7 +5464,7 @@ var require_package = __commonJS({
|
|
|
5396
5464
|
eslint: "^10.0.0",
|
|
5397
5465
|
fuzzysort: "^3.1.0",
|
|
5398
5466
|
globals: "^17.13.0",
|
|
5399
|
-
mocha: "^8.
|
|
5467
|
+
mocha: "^11.8.0",
|
|
5400
5468
|
uqr: "^0.1.3"
|
|
5401
5469
|
},
|
|
5402
5470
|
files: [
|
|
@@ -5451,23 +5519,40 @@ var require_settings = __commonJS({
|
|
|
5451
5519
|
// src/core.js
|
|
5452
5520
|
var require_core = __commonJS({
|
|
5453
5521
|
"src/core.js"(exports2, module2) {
|
|
5454
|
-
var { exec: exec2, spawn } = require("child_process"), WrappedError = class extends Error {
|
|
5522
|
+
var { exec: exec2, spawn } = require("child_process"), log3 = require_log(), WrappedError = class extends Error {
|
|
5455
5523
|
constructor(message, errors) {
|
|
5456
5524
|
super(message), this.errors = errors || [];
|
|
5457
5525
|
}
|
|
5458
5526
|
}, wrapErrors = (message, errors) => new WrappedError(message, errors), flattenErrors = (err) => err instanceof WrappedError ? [new Error(err.message), ...err.errors.flatMap(flattenErrors)] : [err instanceof Error ? err : new Error(String(err))], catchErrors = (promise) => Promise.resolve(promise).then((value) => [null, value], (err) => [flattenErrors(err)]), IS_WINDOWS = process.platform == "win32", _exec = (cmd) => new Promise((next, fail) => {
|
|
5527
|
+
let started = Date.now();
|
|
5459
5528
|
exec2(cmd, (error, stdout2, stderr) => {
|
|
5460
|
-
error || stderr ? fail(error || stderr) : next(stdout2);
|
|
5529
|
+
log3.command({ cmd, args: [], code: error ? error.code ?? 1 : stderr ? "stderr" : 0, ms: Date.now() - started, output: stderr || error && error.message }), error || stderr ? fail(error || stderr) : next(stdout2);
|
|
5461
5530
|
});
|
|
5462
|
-
}), run2 = (cmd, args, options) => new Promise((next, fail) => {
|
|
5463
|
-
let { inherit } = options || {}, child = spawn(cmd, args || [], {
|
|
5464
|
-
stdio: inherit ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
5531
|
+
}), commandError = (message, { command, exitCode, output }) => Object.assign(new Error(message), { command, code: exitCode, output }), run2 = (cmd, args, options) => new Promise((next, fail) => {
|
|
5532
|
+
let { inherit, tee } = options || {}, started = Date.now(), command = `${cmd} ${log3.redactArgs(args).join(" ")}`.trim(), child = spawn(cmd, args || [], {
|
|
5533
|
+
stdio: tee ? ["inherit", "inherit", "pipe"] : inherit ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
5465
5534
|
...IS_WINDOWS ? { shell: !0 } : {}
|
|
5466
5535
|
}), stdout2 = "", stderr = "";
|
|
5467
|
-
|
|
5468
|
-
|
|
5536
|
+
tee ? child.stderr.on("data", (d) => {
|
|
5537
|
+
stderr += d, process.stderr.write(d);
|
|
5538
|
+
}) : inherit || (child.stdout.on("data", (d) => stdout2 += d), child.stderr.on("data", (d) => stderr += d)), child.on("error", (err) => {
|
|
5539
|
+
log3.command({ cmd, args, code: err.code || "spawn error", ms: Date.now() - started, output: err.message, inherit: inherit || tee }), fail(Object.assign(err, { command }));
|
|
5540
|
+
}), child.on("close", (code) => {
|
|
5541
|
+
let output = (stderr || stdout2 || "").trim();
|
|
5542
|
+
log3.command({ cmd, args, code, ms: Date.now() - started, output, inherit: inherit || tee }), code === 0 ? next(stdout2) : fail(commandError(lastErrorLine(output) || `'${command}' exited with code ${code}`, { command, exitCode: code, output }));
|
|
5469
5543
|
});
|
|
5470
|
-
}),
|
|
5544
|
+
}), lastErrorLine = (output) => {
|
|
5545
|
+
let text2 = (output || "").trim();
|
|
5546
|
+
if (!text2)
|
|
5547
|
+
return "";
|
|
5548
|
+
let lines = text2.split(`
|
|
5549
|
+
`).map((l2) => l2.trim()).filter(Boolean);
|
|
5550
|
+
if (lines.length <= 3)
|
|
5551
|
+
return text2;
|
|
5552
|
+
let telling = lines.filter((l2) => /error|exception|denied|invalid|expired|unable|not found|failed/i.test(l2));
|
|
5553
|
+
return telling.length ? telling.slice(-2).join(`
|
|
5554
|
+
`) : text2;
|
|
5555
|
+
}, _commandExistsResult = {}, isCommandExist = (cmd, errorMsg) => () => catchErrors((async () => {
|
|
5471
5556
|
if (!cmd)
|
|
5472
5557
|
return !1;
|
|
5473
5558
|
if (_commandExistsResult[cmd] !== void 0)
|
|
@@ -5481,6 +5566,7 @@ var require_core = __commonJS({
|
|
|
5481
5566
|
wrapErrors,
|
|
5482
5567
|
exec: _exec,
|
|
5483
5568
|
run: run2,
|
|
5569
|
+
lastErrorLine,
|
|
5484
5570
|
isCommandExist
|
|
5485
5571
|
};
|
|
5486
5572
|
}
|
|
@@ -5640,14 +5726,16 @@ var require_transforms = __commonJS({
|
|
|
5640
5726
|
login_session: params.login_session || null
|
|
5641
5727
|
};
|
|
5642
5728
|
return p4.isSso = !!p4.sso_start_url, p4.isLegacySso = p4.isSso && !p4.sso_session, p4.kind = p4.isSso ? "sso" : p4.login_session ? "login" : p4.role_arn ? "role" : params.credential_process ? "process" : "keys", !p4.sso_account_id && p4.role_arn && (p4.sso_account_id = (p4.role_arn.match(/^arn:aws[^:]*:iam::(\d{12}):/) || [])[1] || null), p4;
|
|
5643
|
-
}), slug = (text2) => String(text2 || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), generatedProfileName = (prefix, accountName, accountId, roleName) => [slug(prefix), slug(accountName) || accountId, slug(roleName)].filter(Boolean).join("-"), populateSsoProfiles = (configStr, { ssoSession, entries, region, prefix, prune, version }) => {
|
|
5644
|
-
let config = configStr, profiles = listProfiles(config), session = ini.getSection(config, `sso-session ${ssoSession}`) || {}, samePortal = (p4) => p4.sso_session == ssoSession || p4.sso_start_url && p4.sso_start_url == session.sso_start_url, key =
|
|
5729
|
+
}), slug = (text2) => String(text2 || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""), generatedProfileName = (prefix, accountName, accountId, roleName) => [slug(prefix), slug(accountName) || accountId, slug(roleName)].filter(Boolean).join("-"), ssoEntryKey = (accountId, roleName) => `${accountId}/${roleName}`, populateSsoProfiles = (configStr, { ssoSession, entries, region, prefix, prune, include, version }) => {
|
|
5730
|
+
let config = configStr, profiles = listProfiles(config), session = ini.getSection(config, `sso-session ${ssoSession}`) || {}, samePortal = (p4) => p4.sso_session == ssoSession || p4.sso_start_url && p4.sso_start_url == session.sso_start_url, key = ssoEntryKey, included = include ? new Set(include) : null, byKey = new Map(profiles.filter((p4) => p4.isSso && samePortal(p4)).map((p4) => [key(p4.sso_account_id, p4.sso_role_name), p4])), taken = new Set(profiles.map((p4) => p4.name)), wanted = new Set(entries.map((e) => key(e.accountId, e.roleName))), added = [], addedEntries = [], existing = [];
|
|
5645
5731
|
for (let e of entries) {
|
|
5646
5732
|
let found = byKey.get(key(e.accountId, e.roleName));
|
|
5647
5733
|
if (found) {
|
|
5648
5734
|
existing.push(found.name);
|
|
5649
5735
|
continue;
|
|
5650
5736
|
}
|
|
5737
|
+
if (included && !included.has(key(e.accountId, e.roleName)))
|
|
5738
|
+
continue;
|
|
5651
5739
|
let base = generatedProfileName(prefix || ssoSession, e.accountName, e.accountId, e.roleName), name = base;
|
|
5652
5740
|
for (let i2 = 2; taken.has(name); i2++)
|
|
5653
5741
|
name = `${base}-${i2}`;
|
|
@@ -5660,13 +5748,13 @@ var require_transforms = __commonJS({
|
|
|
5660
5748
|
...e.accountName ? [[ACCOUNT_NAME_KEY, e.accountName]] : [],
|
|
5661
5749
|
[GENERATED_KEY, ssoSession],
|
|
5662
5750
|
[VERSION_KEY, version]
|
|
5663
|
-
]), added.push(name);
|
|
5751
|
+
]), added.push(name), addedEntries.push({ name, accountId: e.accountId, accountName: e.accountName, roleName: e.roleName });
|
|
5664
5752
|
}
|
|
5665
5753
|
let stale = profiles.filter((p4) => p4.generated == ssoSession && !wanted.has(key(p4.sso_account_id, p4.sso_role_name))).map((p4) => p4.name), removed = [];
|
|
5666
5754
|
if (prune)
|
|
5667
5755
|
for (let name of stale)
|
|
5668
5756
|
config = ini.removeSection(config, configSectionName(config, name)), removed.push(name);
|
|
5669
|
-
return { config, added, existing, stale, removed };
|
|
5757
|
+
return { config, added, addedEntries, existing, stale, removed };
|
|
5670
5758
|
}, listSsoSessions = (configStr) => ini.listSections(configStr).filter((s) => s.startsWith("sso-session ")).map((s) => ({ name: s.replace(/^sso-session\s+/, ""), ...ini.getSection(configStr, s) })), addSsoSession = (configStr, { name, startUrl, region, version }) => ini.getSection(configStr, `sso-session ${name}`) ? configStr : ini.setSection(configStr, `sso-session ${name}`, [
|
|
5671
5759
|
["sso_start_url", startUrl],
|
|
5672
5760
|
["sso_region", region],
|
|
@@ -5678,6 +5766,7 @@ var require_transforms = __commonJS({
|
|
|
5678
5766
|
ACCOUNT_NAME_KEY,
|
|
5679
5767
|
generatedProfileName,
|
|
5680
5768
|
populateSsoProfiles,
|
|
5769
|
+
ssoEntryKey,
|
|
5681
5770
|
listSsoSessions,
|
|
5682
5771
|
addSsoSession,
|
|
5683
5772
|
NAME_KEY,
|
|
@@ -5710,8 +5799,13 @@ var require_login = __commonJS({
|
|
|
5710
5799
|
text2 = text2 || "";
|
|
5711
5800
|
let urls = text2.match(URL_REGEX) || [], code = (text2.match(CODE_REGEX) || [])[1] || null, completeUrl = urls.find((u5) => /user_code=/.test(u5)) || null;
|
|
5712
5801
|
return { url: urls.find((u5) => u5 !== completeUrl) || completeUrl || null, code, completeUrl };
|
|
5802
|
+
}, WRONG_REGION = /Invalid start url|invalid_request|InvalidRequestException/i, configureSsoFailure = (output, sessions) => {
|
|
5803
|
+
let lines = (output || "").split(`
|
|
5804
|
+
`).map((l2) => l2.trim()).filter(Boolean), detail = (lines.filter((l2) => /error/i.test(l2)).slice(-1)[0] || lines.slice(-1)[0] || "").replace(/^aws:\s*\[ERROR\]:\s*/i, ""), description = ((output || "").match(/error_description:\s*(.+)/) || [])[1], known = (sessions || []).filter((s) => s.sso_region).map((s) => `${s.name} (${s.sso_region}, ${s.sso_start_url})`), reuse = known.length ? ` Portals already set up here: ${known.join(", ")}. Type one of these names as the session name to reuse it.` : "", message = `aws configure sso failed${description || detail ? `: ${(description || detail).trim()}` : "."}`;
|
|
5805
|
+
return WRONG_REGION.test(output || "") ? { message, hint: `The SSO region is most likely wrong: it must be the region where IAM Identity Center lives (Identity Center console \u203A Settings), not where you deploy. AWS reports a wrong region as an invalid start URL.${reuse}` } : { message, hint: `Run sp add to try again.${reuse}` };
|
|
5713
5806
|
};
|
|
5714
5807
|
module2.exports = {
|
|
5808
|
+
configureSsoFailure,
|
|
5715
5809
|
isRemoteSession,
|
|
5716
5810
|
resolveLoginMode,
|
|
5717
5811
|
loginFlags,
|
|
@@ -5832,12 +5926,12 @@ var require_regions = __commonJS({
|
|
|
5832
5926
|
// src/aws/index.js
|
|
5833
5927
|
var require_aws = __commonJS({
|
|
5834
5928
|
"src/aws/index.js"(exports2, module2) {
|
|
5835
|
-
var fs = require("fs"), crypto = require("crypto"), { homedir, EOL } = require("os"), { join: join2, dirname: dirname2, basename } = require("path"), { exec: exec2, run: run2, isCommandExist, catchErrors, wrapErrors } = require_core(), settings2 = require_settings(), { CLI_VERSION: CLI_VERSION2 } = settings2, transforms = require_transforms(), { loginFlags, isRemoteSession, atLeast } = require_login(), regions = require_regions(), IS_WINDOWS = process.platform === "win32", NL = IS_WINDOWS ? `
|
|
5929
|
+
var fs = require("fs"), crypto = require("crypto"), { homedir, EOL } = require("os"), { join: join2, dirname: dirname2, basename } = require("path"), { exec: exec2, run: run2, isCommandExist, catchErrors, wrapErrors } = require_core(), settings2 = require_settings(), { CLI_VERSION: CLI_VERSION2 } = settings2, transforms = require_transforms(), { loginFlags, isRemoteSession, atLeast } = require_login(), regions = require_regions(), log3 = require_log(), IS_WINDOWS = process.platform === "win32", NL = IS_WINDOWS ? `
|
|
5836
5930
|
` : EOL, AWS_CONFIG_FILE = join2(homedir(), ".aws", "config"), AWS_CREDS_FILE = join2(homedir(), ".aws", "credentials"), AWS_SSO_FOLDER = join2(homedir(), ".aws", "sso", "cache"), awsExists = isCommandExist("aws"), _awsCliVersion = null, whichAws = () => exec2(`${process.platform == "win32" ? "where" : "which"} aws`).then((out) => (out || "").split(/\r?\n/)[0].trim(), () => null), getAwsCliVersion = async () => {
|
|
5837
5931
|
if (_awsCliVersion)
|
|
5838
5932
|
return _awsCliVersion;
|
|
5839
|
-
let path2 = await whichAws(), stat = path2 ? await fs.promises.stat(fs.realpathSync(path2)).catch(() => null) : null, cached = (await settings2.read().catch((
|
|
5840
|
-
return stat && cached && cached.path == path2 && cached.mtimeMs == stat.mtimeMs && cached.version ? _awsCliVersion = cached.version : (_awsCliVersion = ((await exec2("aws --version") || "").match(/aws-cli\/(\S+)/) || [])[1] || null, stat && _awsCliVersion && await settings2.update({ awsCli: { path: path2, mtimeMs: stat.mtimeMs, version: _awsCliVersion } }).catch((
|
|
5933
|
+
let path2 = await whichAws(), stat = path2 ? await fs.promises.stat(fs.realpathSync(path2)).catch(() => null) : null, cached = (await settings2.read().catch(log3.tolerated("reading the settings", {}))).awsCli;
|
|
5934
|
+
return stat && cached && cached.path == path2 && cached.mtimeMs == stat.mtimeMs && cached.version ? _awsCliVersion = cached.version : (_awsCliVersion = ((await exec2("aws --version") || "").match(/aws-cli\/(\S+)/) || [])[1] || null, stat && _awsCliVersion && await settings2.update({ awsCli: { path: path2, mtimeMs: stat.mtimeMs, version: _awsCliVersion } }).catch(log3.tolerated("caching the AWS CLI version", null)), _awsCliVersion);
|
|
5841
5935
|
}, awsCliV2Exists = (noFailIfMissing) => catchErrors((async () => {
|
|
5842
5936
|
let [awsCliErrors] = await awsExists();
|
|
5843
5937
|
if (awsCliErrors) {
|
|
@@ -5929,9 +6023,9 @@ var require_aws = __commonJS({
|
|
|
5929
6023
|
if (!atLeast(version, [2, 32, 0]))
|
|
5930
6024
|
throw new Error(`'aws login' needs AWS CLI 2.32 or later (you have ${version}). Update the AWS CLI and try again.`);
|
|
5931
6025
|
let remote = isRemoteSession(process.env, process.platform) ? ["--remote"] : [];
|
|
5932
|
-
await run2("aws", ["login", "--profile", profile, ...remote], {
|
|
6026
|
+
await run2("aws", ["login", "--profile", profile, ...remote], { tee: !0 });
|
|
5933
6027
|
}, logoutAll = async () => {
|
|
5934
|
-
await run2("aws", ["sso", "logout"]), atLeast(await getAwsCliVersion(), [2, 32, 0]) && await run2("aws", ["logout", "--all"]).catch((
|
|
6028
|
+
await run2("aws", ["sso", "logout"]), atLeast(await getAwsCliVersion(), [2, 32, 0]) && await run2("aws", ["logout", "--all"]).catch(log3.tolerated("aws logout --all", null));
|
|
5935
6029
|
}, readSsoToken = async ({ ssoSession, startUrl }) => {
|
|
5936
6030
|
let key = crypto.createHash("sha1").update(ssoSession || startUrl).digest("hex"), token = await fs.promises.readFile(join2(AWS_SSO_FOLDER, `${key}.json`), "utf8").then(JSON.parse).catch(() => null);
|
|
5937
6031
|
return !token || !token.accessToken || new Date(token.expiresAt).getTime() < Date.now() + 60 * 1e3 ? null : token;
|
|
@@ -6013,9 +6107,9 @@ var require_aws = __commonJS({
|
|
|
6013
6107
|
})()), createSsoProfile = (name) => catchErrors((async () => {
|
|
6014
6108
|
await awsCliV2Exists();
|
|
6015
6109
|
try {
|
|
6016
|
-
await run2("aws", ["configure", "sso", "--profile", name], {
|
|
6110
|
+
await run2("aws", ["configure", "sso", "--profile", name], { tee: !0 });
|
|
6017
6111
|
} catch (err) {
|
|
6018
|
-
throw new Error(`'aws configure sso' failed
|
|
6112
|
+
throw Object.assign(new Error(`'aws configure sso' failed: ${err.message}`, { cause: err }), { output: err.output || "" });
|
|
6019
6113
|
}
|
|
6020
6114
|
let [configStrErrors, configStr] = await getConfigFile();
|
|
6021
6115
|
if (configStrErrors)
|
|
@@ -6056,7 +6150,7 @@ var require_aws = __commonJS({
|
|
|
6056
6150
|
// src/shell.js
|
|
6057
6151
|
var require_shell = __commonJS({
|
|
6058
6152
|
"src/shell.js"(exports2, module2) {
|
|
6059
|
-
var fs = require("fs"), { homedir } = require("os"), { join: join2, dirname: dirname2, basename } = require("path"), { run: run2 } = require_core(), IS_WINDOWS = process.platform === "win32", IS_MAC = process.platform === "darwin", FUNCTION_NAME = "sp", BLOCK_START_PREFIX = "# >>> switch-profile", BLOCK_END = "# <<< switch-profile <<<", BLOCK_REGEX = /(^|\r?\n)# >>> switch-profile[^\n]*\r?\n[\s\S]*?# <<< switch-profile <<<[^\n]*(\r?\n)?/, blockStart = (version) => `${BLOCK_START_PREFIX} v${version} (managed by switch-profile, do not edit) >>>`, CLEARED_VARS = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "AWS_DEFAULT_PROFILE"], SUBCOMMANDS = ["status", "login", "logout", "add", "remove", "settings", "use"], PROFILES_SED = "sed -n 's/^\\[profile \\(.*\\)\\]$/\\1/p'", POSIX_FUNCTION = (shell) => `${FUNCTION_NAME}() {
|
|
6153
|
+
var fs = require("fs"), { homedir } = require("os"), { join: join2, dirname: dirname2, basename } = require("path"), { run: run2 } = require_core(), log3 = require_log(), IS_WINDOWS = process.platform === "win32", IS_MAC = process.platform === "darwin", FUNCTION_NAME = "sp", BLOCK_START_PREFIX = "# >>> switch-profile", BLOCK_END = "# <<< switch-profile <<<", BLOCK_REGEX = /(^|\r?\n)# >>> switch-profile[^\n]*\r?\n[\s\S]*?# <<< switch-profile <<<[^\n]*(\r?\n)?/, blockStart = (version) => `${BLOCK_START_PREFIX} v${version} (managed by switch-profile, do not edit) >>>`, CLEARED_VARS = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "AWS_DEFAULT_PROFILE"], SUBCOMMANDS = ["status", "login", "logout", "add", "remove", "settings", "use"], PROFILES_SED = "sed -n 's/^\\[profile \\(.*\\)\\]$/\\1/p'", POSIX_FUNCTION = (shell) => `${FUNCTION_NAME}() {
|
|
6060
6154
|
local __sp_file __sp_status
|
|
6061
6155
|
__sp_file="$(mktemp "\${TMPDIR:-/tmp}/switch-profile.XXXXXX")" || return 1
|
|
6062
6156
|
if [ -n "\${SWITCH_PROFILE_DEV_BIN:-}" ]; then
|
|
@@ -6190,7 +6284,7 @@ Register-ArgumentCompleter -CommandName ${FUNCTION_NAME} -ParameterName Rest -Sc
|
|
|
6190
6284
|
if (IS_WINDOWS && !shellPath) {
|
|
6191
6285
|
let files = [];
|
|
6192
6286
|
for (let exe of ["pwsh", "powershell"]) {
|
|
6193
|
-
let file = await run2(exe, ["-NoProfile", "-NonInteractive", "-Command", "$PROFILE.CurrentUserAllHosts"]).then((out) => (out || "").trim()).catch((
|
|
6287
|
+
let file = await run2(exe, ["-NoProfile", "-NonInteractive", "-Command", "$PROFILE.CurrentUserAllHosts"]).then((out) => (out || "").trim()).catch(log3.tolerated("reading the PowerShell profile path", ""));
|
|
6194
6288
|
file && !files.includes(file) && files.push(file);
|
|
6195
6289
|
}
|
|
6196
6290
|
if (files.length)
|
|
@@ -6313,7 +6407,7 @@ var require_migrate = __commonJS({
|
|
|
6313
6407
|
// src/commands/common.js
|
|
6314
6408
|
var require_common = __commonJS({
|
|
6315
6409
|
"src/commands/common.js"(exports2, module2) {
|
|
6316
|
-
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), aws = require_aws(), settings2 = require_settings(), shell = require_shell(), migrate = require_migrate(), ui2 = require_ui(), CancelError2 = class extends Error {
|
|
6410
|
+
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), log3 = require_log(), aws = require_aws(), settings2 = require_settings(), shell = require_shell(), migrate = require_migrate(), ui2 = require_ui(), CancelError2 = class extends Error {
|
|
6317
6411
|
}, ask = async (promise) => {
|
|
6318
6412
|
let value = await promise;
|
|
6319
6413
|
if (p4.isCancel(value))
|
|
@@ -6326,14 +6420,14 @@ var require_common = __commonJS({
|
|
|
6326
6420
|
}, preflight = async ({ interactive, quiet }) => {
|
|
6327
6421
|
let [cliErrors, cliExists] = await aws.awsCliV2Exists(!0);
|
|
6328
6422
|
if (cliErrors)
|
|
6329
|
-
throw
|
|
6423
|
+
throw ui2.cliErrorFrom(cliErrors, { hint: "switch-profile needs AWS CLI v2." });
|
|
6330
6424
|
if (!cliExists)
|
|
6331
6425
|
throw new ui2.CliError("The AWS CLI is not installed.", { hint: INSTALL_HINTS[process.platform] || INSTALL_HINTS.linux });
|
|
6332
6426
|
let result;
|
|
6333
6427
|
try {
|
|
6334
6428
|
result = await migrate.runMigrations();
|
|
6335
6429
|
} catch (err) {
|
|
6336
|
-
throw err instanceof migrate.NewerFormatError ? new ui2.CliError(err.message) : err;
|
|
6430
|
+
throw err instanceof migrate.NewerFormatError ? new ui2.CliError(err.message, { cause: err }) : err;
|
|
6337
6431
|
}
|
|
6338
6432
|
if (result.migrated && !quiet && p4.log.info([
|
|
6339
6433
|
`Upgraded your AWS setup for switch-profile ${settings2.CLI_VERSION} ${ui2.unicode ? "\u{1F389}" : ""}`,
|
|
@@ -6344,7 +6438,7 @@ var require_common = __commonJS({
|
|
|
6344
6438
|
let legacy = await migrate.findLegacySsoProfiles();
|
|
6345
6439
|
legacy.length && (p4.log.warn(`${legacy.length > 1 ? `${legacy.length} profiles use` : "1 profile uses"} the old SSO format, which never auto-refreshes: ${legacy.join(", ")}`), await ask(p4.confirm({ message: "Upgrade them now? You'll log in once per SSO portal." })) ? await upgradeLegacySso(legacy) : (await settings2.update({ ssoUpgradeDeclined: !0 }), p4.log.message(ui2.dim("No problem. You can upgrade later from Settings."))));
|
|
6346
6440
|
}
|
|
6347
|
-
let status = await shell.getStatus(settings2.CLI_VERSION).catch((
|
|
6441
|
+
let status = await shell.getStatus(settings2.CLI_VERSION).catch(log3.tolerated("reading the sp shortcut status", null));
|
|
6348
6442
|
status && status.installed && status.outdated && await shell.install(settings2.CLI_VERSION).then(() => !0, () => !1) && !quiet && p4.log.info(`Updated the ${ui2.accent(shell.FUNCTION_NAME)} shortcut in ${status.shell.files.map(shell.tilde).join(", ")} ${ui2.dim("(new terminals pick it up)")}`);
|
|
6349
6443
|
}, upgradeLegacySso = async (names) => {
|
|
6350
6444
|
let { sessions, backups } = await migrate.upgradeLegacySsoProfiles(names);
|
|
@@ -6357,10 +6451,10 @@ var require_common = __commonJS({
|
|
|
6357
6451
|
}, loadState = async () => {
|
|
6358
6452
|
let [listErrors, profiles] = await aws.listProfiles();
|
|
6359
6453
|
if (listErrors)
|
|
6360
|
-
throw
|
|
6454
|
+
throw ui2.cliErrorFrom(listErrors);
|
|
6361
6455
|
let [defaultErrors, info] = await aws.getDefaultProfile();
|
|
6362
6456
|
if (defaultErrors)
|
|
6363
|
-
throw
|
|
6457
|
+
throw ui2.cliErrorFrom(defaultErrors);
|
|
6364
6458
|
let defaultProfile = profiles.find((x2) => x2.name == info.profile) || null;
|
|
6365
6459
|
return { profiles, defaultName: defaultProfile ? defaultProfile.name : null, defaultProfile, settings: await settings2.read() };
|
|
6366
6460
|
}, loginKey = (profile) => profile.sso_session || profile.sso_start_url || profile.name, describe = (profile, identity) => ui2.join([
|
|
@@ -7389,11 +7483,11 @@ var require_clipboard = __commonJS({
|
|
|
7389
7483
|
// src/commands/login-flow.js
|
|
7390
7484
|
var require_login_flow = __commonJS({
|
|
7391
7485
|
"src/commands/login-flow.js"(exports2, module2) {
|
|
7392
|
-
var { spawn } = require("child_process"), p4 = (init_dist4(), __toCommonJS(dist_exports2)), { renderUnicodeCompact } = require_dist(), aws = require_aws(), settings2 = require_settings(), { parseLoginOutput, isRemoteSession, atLeast } = require_login(), clipboard = require_clipboard(), ui2 = require_ui(), { CancelError: CancelError2 } = require_common(), IS_WINDOWS = process.platform == "win32", RAW_OUTPUT_AFTER_MS = 1e4, clipboardNote = (result) => result == "local" ? ui2.ok("\u{1F4CB} copied") : result == "terminal" ? ui2.dim("\u{1F4CB} sent to your clipboard") : "", ssoLogin = async (target, { label, loginKey, interactive, loginMode }) => {
|
|
7486
|
+
var { spawn } = require("child_process"), p4 = (init_dist4(), __toCommonJS(dist_exports2)), { renderUnicodeCompact } = require_dist(), aws = require_aws(), settings2 = require_settings(), { parseLoginOutput, isRemoteSession, atLeast } = require_login(), clipboard = require_clipboard(), ui2 = require_ui(), log3 = require_log(), { CancelError: CancelError2 } = require_common(), IS_WINDOWS = process.platform == "win32", RAW_OUTPUT_AFTER_MS = 1e4, clipboardNote = (result) => result == "local" ? ui2.ok("\u{1F4CB} copied") : result == "terminal" ? ui2.dim("\u{1F4CB} sent to your clipboard") : "", ssoLogin = async (target, { label, loginKey, interactive, loginMode }) => {
|
|
7393
7487
|
let current = await settings2.read(), args = await aws.ssoLoginArgs(target, loginMode || settings2.getLoginMode(current)), legacyCli = !atLeast(await aws.getAwsCliVersion(), [2, 22, 0]), device = args.includes("--use-device-code") || legacyCli, remote = isRemoteSession(process.env, process.platform);
|
|
7394
7488
|
if (!interactive)
|
|
7395
7489
|
return plainLogin(args, label);
|
|
7396
|
-
let child = spawn("aws", args, { stdio: ["ignore", "pipe", "pipe"], ...IS_WINDOWS ? { shell: !0 } : {} }), output = "", shown = !1, rawMode = !1, spin = null, parsed = {}, stdin2 = process.stdin, onKey = async (key) => {
|
|
7490
|
+
let started = Date.now(), child = spawn("aws", args, { stdio: ["ignore", "pipe", "pipe"], ...IS_WINDOWS ? { shell: !0 } : {} }), output = "", shown = !1, rawMode = !1, spin = null, parsed = {}, stdin2 = process.stdin, onKey = async (key) => {
|
|
7397
7491
|
if (key === "") {
|
|
7398
7492
|
child.kill();
|
|
7399
7493
|
return;
|
|
@@ -7438,7 +7532,7 @@ ${ui2.dim(`If nothing opens, visit: ${parsed.url}`)}`);
|
|
|
7438
7532
|
}, RAW_OUTPUT_AFTER_MS), code = await new Promise((resolve) => {
|
|
7439
7533
|
child.on("error", () => resolve(-1)), child.on("close", resolve);
|
|
7440
7534
|
});
|
|
7441
|
-
if (clearTimeout(fallback), cleanup(), code === 0) {
|
|
7535
|
+
if (clearTimeout(fallback), cleanup(), log3.command({ cmd: "aws", args, code: child.killed ? "killed (cancelled)" : code, ms: Date.now() - started, output }), code === 0) {
|
|
7442
7536
|
spin ? spin.stop(`${ui2.unicode ? "\u{1F513} " : ""}Logged in`) : p4.log.success("Logged in"), await rememberLogin(loginKey);
|
|
7443
7537
|
return;
|
|
7444
7538
|
}
|
|
@@ -7450,12 +7544,15 @@ ${ui2.dim(`If nothing opens, visit: ${parsed.url}`)}`);
|
|
|
7450
7544
|
`) || lines.slice(-1)[0] || "";
|
|
7451
7545
|
throw new ui2.CliError(`SSO login for ${label} failed.${detail ? `
|
|
7452
7546
|
${detail}` : ""}`, {
|
|
7453
|
-
|
|
7547
|
+
cause: Object.assign(new Error(`aws ${args.join(" ")} exited with code ${code}`), { output }),
|
|
7548
|
+
hint: /invalid_grant|InvalidGrant/i.test(output) ? "Check the SSO region of this profile: it must be the region of your IAM Identity Center, not where your resources live." : `Try again. Details: ${log3.displayPath()}`
|
|
7454
7549
|
});
|
|
7455
7550
|
}, plainLogin = async (args, label) => {
|
|
7456
7551
|
console.error(`Logging in to ${label}. Approve the request using the URL and code below.`), await new Promise((resolve, reject) => {
|
|
7457
|
-
let child = spawn("aws", args, { stdio: ["ignore", process.stderr, process.stderr], ...IS_WINDOWS ? { shell: !0 } : {} });
|
|
7458
|
-
child.on("error", reject), child.on("close", (code) =>
|
|
7552
|
+
let started = Date.now(), child = spawn("aws", args, { stdio: ["ignore", process.stderr, process.stderr], ...IS_WINDOWS ? { shell: !0 } : {} });
|
|
7553
|
+
child.on("error", reject), child.on("close", (code) => {
|
|
7554
|
+
log3.command({ cmd: "aws", args, code, ms: Date.now() - started, output: "(output passed through to stderr)" }), code === 0 ? resolve() : reject(new ui2.CliError(`SSO login for ${label} failed.`, { code: 2 }));
|
|
7555
|
+
});
|
|
7459
7556
|
});
|
|
7460
7557
|
}, rememberLogin = async (key) => {
|
|
7461
7558
|
if (!key)
|
|
@@ -7537,7 +7634,7 @@ ${gray(S_BAR2)}`;
|
|
|
7537
7634
|
// src/commands/switch.js
|
|
7538
7635
|
var require_switch = __commonJS({
|
|
7539
7636
|
"src/commands/switch.js"(exports2, module2) {
|
|
7540
|
-
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), aws = require_aws(), settings2 = require_settings(), shell = require_shell(), rank = require_rank(), ui2 = require_ui(), { ask, preflight, loadState, loginKey, describe, loginState, CancelError: CancelError2 } = require_common(), { ssoLogin } = require_login_flow(), { home, ACTION_PREFIX } = require_home(), header = async (state, { warnings } = {}) => {
|
|
7637
|
+
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), aws = require_aws(), settings2 = require_settings(), shell = require_shell(), rank = require_rank(), ui2 = require_ui(), log3 = require_log(), { ask, preflight, loadState, loginKey, describe, loginState, CancelError: CancelError2 } = require_common(), { ssoLogin } = require_login_flow(), { home, ACTION_PREFIX } = require_home(), header = async (state, { warnings } = {}) => {
|
|
7541
7638
|
p4.intro(`${ui2.badge("switch-profile")} ${ui2.dim(settings2.CLI_VERSION)}`);
|
|
7542
7639
|
let lines = [];
|
|
7543
7640
|
if (state.defaultProfile) {
|
|
@@ -7598,20 +7695,20 @@ ${ui2.dim(viaShortcut ? "Switching clears them from this terminal." : `Unset ${s
|
|
|
7598
7695
|
return result;
|
|
7599
7696
|
let loginProfile = profile.kind == "role" && profile.source_profile ? state.profiles.find((x2) => x2.name == profile.source_profile) : profile;
|
|
7600
7697
|
if (!(loginProfile && (loginProfile.kind == "sso" || loginProfile.kind == "login")))
|
|
7601
|
-
throw new ui2.CliError(`Could not get credentials for ${profile.name}.`, { hint: firstLine(result.message) });
|
|
7698
|
+
throw new ui2.CliError(`Could not get credentials for ${profile.name}.`, { hint: firstLine(result.message), cause: result });
|
|
7602
7699
|
if (!interactive && !force)
|
|
7603
7700
|
throw new ui2.CliError(`${profile.name} needs a login.`, { code: 2, hint: `Run: switch-profile login ${loginProfile.name}` });
|
|
7604
7701
|
if (loginProfile.kind == "sso" ? await ssoLogin({ profile: loginProfile.name }, { label: loginProfile.sso_session || loginProfile.name, loginKey: loginKey(loginProfile), interactive }) : await aws.awsLogin(loginProfile.name), result = await tryIdentity(), result instanceof Error)
|
|
7605
|
-
throw new ui2.CliError(`Logged in, but ${profile.name} still has no access.`, { hint: firstLine(result.message) });
|
|
7702
|
+
throw new ui2.CliError(`Logged in, but ${profile.name} still has no access.`, { hint: firstLine(result.message), cause: result });
|
|
7606
7703
|
return result;
|
|
7607
7704
|
}, firstLine = (text2) => String(text2 || "").trim().split(`
|
|
7608
7705
|
`).filter(Boolean).slice(-1)[0], switchTo = async (profile, state, opts) => (await doSwitch(profile, state, opts), { switched: !0 }), doSwitch = async (profile, state, opts) => {
|
|
7609
7706
|
let { interactive, json } = opts, identity = await connect(profile, state, { interactive, quiet: json, force: opts.force }), [defaultErrors] = await aws.setDefaultProfile(profile.name);
|
|
7610
7707
|
if (defaultErrors)
|
|
7611
|
-
throw
|
|
7708
|
+
throw ui2.cliErrorFrom(defaultErrors);
|
|
7612
7709
|
let current = await settings2.read();
|
|
7613
7710
|
await settings2.update({ usage: rank.recordUsage(current.usage, profile.name) });
|
|
7614
|
-
let terminal = await shell.exportProfile(profile.name).catch((
|
|
7711
|
+
let terminal = await shell.exportProfile(profile.name).catch(log3.tolerated("handing the profile to sp", !1));
|
|
7615
7712
|
if (json) {
|
|
7616
7713
|
process.stdout.write(JSON.stringify({ profile: profile.name, account: identity.account, arn: identity.arn, region: profile.region || null, default: !0, terminal }) + `
|
|
7617
7714
|
`);
|
|
@@ -7631,7 +7728,7 @@ ${ui2.dim(viaShortcut ? "Switching clears them from this terminal." : `Unset ${s
|
|
|
7631
7728
|
${ui2.dim("set as the default profile")}`), await offerShortcut(profile);
|
|
7632
7729
|
}, exportHint = (name) => process.platform == "win32" ? `$env:AWS_PROFILE = "${name}" ${ui2.dim("(PowerShell)")}
|
|
7633
7730
|
set AWS_PROFILE=${name} ${ui2.dim("(CMD)")}` : `export AWS_PROFILE=${name}`, offerShortcut = async (profile) => {
|
|
7634
|
-
let status = await shell.getStatus(settings2.CLI_VERSION).catch((
|
|
7731
|
+
let status = await shell.getStatus(settings2.CLI_VERSION).catch(log3.tolerated("reading the sp shortcut status", null)), hint = `${ui2.dim("To use it in this terminal only, run:")}
|
|
7635
7732
|
${ui2.accent(exportHint(profile.name))}`;
|
|
7636
7733
|
if (status && status.installed) {
|
|
7637
7734
|
p4.outro(`${hint}
|
|
@@ -7687,7 +7784,7 @@ ${ui2.dim(`Open a new terminal or run: ${installed.reload}`)}`), installed.name
|
|
|
7687
7784
|
return result;
|
|
7688
7785
|
if (next == "throw")
|
|
7689
7786
|
throw error;
|
|
7690
|
-
next == "back" && p4.cancel(ui2.dim(`${ui2.unicode ? "\u2190" : "<-"} Back`)), next == "error" && ui2.printError(error), state = await loadState();
|
|
7787
|
+
next == "back" && p4.cancel(ui2.dim(`${ui2.unicode ? "\u2190" : "<-"} Back`)), next == "error" && (log3.error(`${route.key} failed (back to the home screen)`, error), ui2.printError(error)), state = await loadState();
|
|
7691
7788
|
}
|
|
7692
7789
|
}, switchCommand = async (query, opts) => {
|
|
7693
7790
|
let interactive = ui2.isInteractive(opts);
|
|
@@ -7726,7 +7823,7 @@ ${ui2.dim(`Open a new terminal or run: ${installed.reload}`)}`), installed.name
|
|
|
7726
7823
|
// src/commands/index.js
|
|
7727
7824
|
var require_commands = __commonJS({
|
|
7728
7825
|
"src/commands/index.js"(exports2, module2) {
|
|
7729
|
-
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), aws = require_aws(), transforms = require_transforms(), settings2 = require_settings(), shell = require_shell(), migrate = require_migrate(), rank = require_rank(), { resolveLoginMode, isRemoteSession } = require_login(), ui2 = require_ui(), { ask, preflight, loadState, loginKey, describe, loginState, upgradeLegacySso } = require_common(), { ssoLogin, rememberLogin } = require_login_flow(), { switchCommand, switchTo, connect, enableShortcut } = require_switch(), intro2 = (opts) => {
|
|
7826
|
+
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), log3 = require_log(), aws = require_aws(), transforms = require_transforms(), settings2 = require_settings(), shell = require_shell(), migrate = require_migrate(), rank = require_rank(), { resolveLoginMode, isRemoteSession, configureSsoFailure } = require_login(), ui2 = require_ui(), { ask, preflight, loadState, loginKey, describe, loginState, upgradeLegacySso } = require_common(), { ssoLogin, rememberLogin } = require_login_flow(), { switchCommand, switchTo, connect, enableShortcut } = require_switch(), intro2 = (opts) => {
|
|
7730
7827
|
!opts.fromPicker && !opts.json && p4.intro(`${ui2.badge("switch-profile")} ${ui2.dim(settings2.CLI_VERSION)}`);
|
|
7731
7828
|
}, begin = async (opts, { needsInput } = {}) => {
|
|
7732
7829
|
let interactive = ui2.isInteractive(opts);
|
|
@@ -7745,7 +7842,7 @@ var require_commands = __commonJS({
|
|
|
7745
7842
|
return interactive && !opts.json && intro2(opts), switchTo(findProfile(state, name), state, { ...opts, interactive });
|
|
7746
7843
|
}, status = async (args, opts) => {
|
|
7747
7844
|
await begin(opts);
|
|
7748
|
-
let state = await loadState(), terminal = process.env.AWS_PROFILE || null, shortcut = await shell.getStatus(settings2.CLI_VERSION).catch((
|
|
7845
|
+
let state = await loadState(), terminal = process.env.AWS_PROFILE || null, shortcut = await shell.getStatus(settings2.CLI_VERSION).catch(log3.tolerated("reading the sp shortcut status", null)), d = state.defaultProfile, login2 = d ? await loginState(d, state) : { text: null, needsLogin: !1 };
|
|
7749
7846
|
if (opts.json) {
|
|
7750
7847
|
let at = d ? (state.settings.logins || {})[loginKey(d)] : null;
|
|
7751
7848
|
process.stdout.write(JSON.stringify({
|
|
@@ -7810,21 +7907,31 @@ var require_commands = __commonJS({
|
|
|
7810
7907
|
return importFromSso(state, { ...opts, interactive });
|
|
7811
7908
|
let taken = state.profiles.map((x2) => x2.name), name = await ask(p4.text({ message: "Profile name", placeholder: "e.g. acme-prod", validate: validateProfileName(taken) }));
|
|
7812
7909
|
if (kind == "sso") {
|
|
7910
|
+
let [configErrors, configStr] = await aws.getConfigFile();
|
|
7911
|
+
if (configErrors)
|
|
7912
|
+
throw ui2.cliErrorFrom(configErrors);
|
|
7913
|
+
let sessions = transforms.listSsoSessions(configStr);
|
|
7813
7914
|
p4.log.message([
|
|
7814
7915
|
"Handing over to aws configure sso. You will be asked for:",
|
|
7815
|
-
`${ui2.dim("1.")} a session name ${ui2.dim("(reuse the same one for every profile of a portal: one login covers them all)")}`,
|
|
7916
|
+
`${ui2.dim("1.")} a session name ${ui2.dim("(a short name such as acme, not the URL; reuse the same one for every profile of a portal: one login covers them all)")}`,
|
|
7816
7917
|
`${ui2.dim("2.")} the start URL ${ui2.dim("(e.g. https://acme.awsapps.com/start)")}`,
|
|
7817
|
-
`${ui2.dim("3.")} the SSO region ${ui2.dim("(where IAM Identity Center lives, not where you deploy)")}
|
|
7918
|
+
`${ui2.dim("3.")} the SSO region ${ui2.dim("(where IAM Identity Center lives, not where you deploy)")}`,
|
|
7919
|
+
...sessions.length ? [ui2.dim(`Portals already set up: ${sessions.map((x2) => `${x2.name} (${x2.sso_region || "?"})`).join(", ")}. Type one of these names to reuse it.`)] : []
|
|
7818
7920
|
].join(`
|
|
7819
7921
|
`));
|
|
7820
7922
|
let [errors, upgraded] = await aws.createSsoProfile(name);
|
|
7821
|
-
if (errors)
|
|
7822
|
-
|
|
7923
|
+
if (errors) {
|
|
7924
|
+
let failed = errors.find((e) => e.output != null);
|
|
7925
|
+
if (!failed)
|
|
7926
|
+
throw ui2.cliErrorFrom(errors);
|
|
7927
|
+
let { message, hint } = configureSsoFailure(failed.output, sessions);
|
|
7928
|
+
throw new ui2.CliError(message, { hint, errors });
|
|
7929
|
+
}
|
|
7823
7930
|
upgraded && p4.log.info(`No session name was given, so ${name} now uses an [sso-session] for auto-refresh. You'll log in once more.`);
|
|
7824
7931
|
} else if (kind == "login") {
|
|
7825
7932
|
let region = await chooseRegion("Default region", "us-east-1"), [configErrors, configStr] = await aws.getConfigFile();
|
|
7826
7933
|
if (configErrors)
|
|
7827
|
-
throw
|
|
7934
|
+
throw ui2.cliErrorFrom(configErrors);
|
|
7828
7935
|
let ini = require_ini();
|
|
7829
7936
|
await aws.writeAwsFile(aws.AWS_CONFIG_FILE, ini.setSection(configStr, `profile ${name}`, [["region", region], [transforms.VERSION_KEY, settings2.CLI_VERSION]])), p4.log.step("Signing in with aws login\u2026"), await aws.awsLogin(name);
|
|
7830
7937
|
} else {
|
|
@@ -7833,7 +7940,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7833
7940
|
return p4.outro(ui2.dim("Nothing changed."));
|
|
7834
7941
|
let aws_access_key_id = await ask(p4.text({ message: "Access key ID", validate: (v) => v ? void 0 : "Required." })), aws_secret_access_key = await ask(p4.password({ message: "Secret access key", validate: (v) => v ? void 0 : "Required." })), region = await chooseRegion("Default region", "us-east-1"), [errors] = await aws.createProfile({ name, aws_access_key_id, aws_secret_access_key, region });
|
|
7835
7942
|
if (errors)
|
|
7836
|
-
throw
|
|
7943
|
+
throw ui2.cliErrorFrom(errors);
|
|
7837
7944
|
}
|
|
7838
7945
|
p4.log.success(`Profile ${ui2.accent(name)} created`);
|
|
7839
7946
|
let next = await loadState();
|
|
@@ -7851,7 +7958,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7851
7958
|
}, importFromSso = async (state, opts) => {
|
|
7852
7959
|
let { interactive } = opts, [configErrors, configStr] = await aws.getConfigFile();
|
|
7853
7960
|
if (configErrors)
|
|
7854
|
-
throw
|
|
7961
|
+
throw ui2.cliErrorFrom(configErrors);
|
|
7855
7962
|
let sessions = transforms.listSsoSessions(configStr), sessionName = opts.fromSso && opts.fromSso !== !0 ? opts.fromSso : null;
|
|
7856
7963
|
if (sessionName && !sessions.some((s) => s.name == sessionName))
|
|
7857
7964
|
throw new ui2.CliError(`There is no [sso-session ${sessionName}] in ~/.aws/config.`, { code: 3, hint: sessions.length ? `Known sessions: ${sessions.map((s) => s.name).join(", ")}` : "Run switch-profile add in a terminal to add a portal." });
|
|
@@ -7889,7 +7996,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7889
7996
|
entries = accounts.flatMap((a2, i2) => roles[i2].map((roleName) => ({ ...a2, roleName }))), spin && spin.stop(`Found ${accounts.length} account${accounts.length == 1 ? "" : "s"} and ${entries.length} role${entries.length == 1 ? "" : "s"}`);
|
|
7890
7997
|
} catch (err) {
|
|
7891
7998
|
throw spin && spin.error("Could not read the accounts"), new ui2.CliError(`Could not list the accounts of ${sessionName}.`, { hint: String(err.message).trim().split(`
|
|
7892
|
-
`).slice(-1)[0] });
|
|
7999
|
+
`).slice(-1)[0], cause: err });
|
|
7893
8000
|
}
|
|
7894
8001
|
let sessionRegions = state.profiles.filter((x2) => x2.sso_session == sessionName && x2.region).map((x2) => x2.region), region = opts.region || (interactive ? await chooseRegion("Default region for these profiles", sessionRegions[0] || session.sso_region) : session.sso_region), prefix = opts.prefix || (interactive ? await ask(p4.text({ message: `Name prefix ${ui2.dim("(profiles become <prefix>-<account>-<role>)")}`, initialValue: sessionName, validate: (v) => /^[a-z0-9_-]*$/.test(v || "") ? void 0 : "Lowercase letters, numbers, - and _ only." })) : sessionName), preview = transforms.populateSsoProfiles(configStr, { ssoSession: sessionName, entries, region, prefix, prune: !1, version: settings2.CLI_VERSION });
|
|
7895
8002
|
if (!preview.added.length && !preview.stale.length)
|
|
@@ -7902,14 +8009,25 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7902
8009
|
].filter(Boolean).join(`
|
|
7903
8010
|
`);
|
|
7904
8011
|
opts.json || p4.log.message(summary);
|
|
7905
|
-
let prune = !!opts.prune;
|
|
8012
|
+
let prune = !!opts.prune, include;
|
|
7906
8013
|
if (interactive && !opts.yes) {
|
|
7907
|
-
if (preview.added.length
|
|
7908
|
-
|
|
8014
|
+
if (preview.added.length) {
|
|
8015
|
+
let all = preview.addedEntries.map((e) => transforms.ssoEntryKey(e.accountId, e.roleName)), picked = await ask(p4.autocompleteMultiselect({
|
|
8016
|
+
message: `Add which profiles? ${ui2.dim("(all selected: space to untick, type to search, enter to confirm)")}`,
|
|
8017
|
+
maxItems: 12,
|
|
8018
|
+
initialValues: all,
|
|
8019
|
+
options: preview.addedEntries.map((e, i2) => ({ value: all[i2], label: e.name, hint: [e.accountName, e.accountId, e.roleName].filter(Boolean).join(" \xB7 ") }))
|
|
8020
|
+
}));
|
|
8021
|
+
if (!picked.length && !preview.stale.length)
|
|
8022
|
+
return p4.outro(ui2.dim("Nothing changed."));
|
|
8023
|
+
picked.length < all.length && (include = picked);
|
|
8024
|
+
}
|
|
7909
8025
|
preview.stale.length && (prune = await ask(p4.confirm({ message: `Remove the ${preview.stale.length} profile${preview.stale.length == 1 ? "" : "s"} that no longer exist${preview.stale.length == 1 ? "s" : ""}?`, initialValue: !1 })));
|
|
7910
8026
|
}
|
|
8027
|
+
if (include && !include.length && !prune)
|
|
8028
|
+
return p4.outro(ui2.dim("Nothing changed."));
|
|
7911
8029
|
prune && await aws.backupAwsFiles();
|
|
7912
|
-
let result = transforms.populateSsoProfiles(configStr, { ssoSession: sessionName, entries, region, prefix, prune, version: settings2.CLI_VERSION });
|
|
8030
|
+
let result = transforms.populateSsoProfiles(configStr, { ssoSession: sessionName, entries, region, prefix, prune, include, version: settings2.CLI_VERSION });
|
|
7913
8031
|
if (await aws.writeAwsFile(aws.AWS_CONFIG_FILE, result.config), await settings2.update({}), opts.json) {
|
|
7914
8032
|
process.stdout.write(JSON.stringify({ added: result.added, existing: result.existing, removed: result.removed, stale: prune ? [] : result.stale }) + `
|
|
7915
8033
|
`);
|
|
@@ -7939,7 +8057,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7939
8057
|
}
|
|
7940
8058
|
let [errors] = await aws.deleteProfiles(names);
|
|
7941
8059
|
if (errors)
|
|
7942
|
-
throw
|
|
8060
|
+
throw ui2.cliErrorFrom(errors);
|
|
7943
8061
|
p4.outro(`${ui2.ok(ui2.sym.ok)} Removed ${names.join(", ")}`);
|
|
7944
8062
|
}, LOGIN_MODE_LABELS = {
|
|
7945
8063
|
auto: "Auto: device code over SSH, browser otherwise",
|
|
@@ -7998,7 +8116,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7998
8116
|
});
|
|
7999
8117
|
|
|
8000
8118
|
// src/cli.js
|
|
8001
|
-
var { Command } = require_commander(), p3 = (init_dist4(), __toCommonJS(dist_exports2)), ui = require_ui(), { CLI_VERSION } = require_settings(), { CancelError } = require_common(), commands = require_commands();
|
|
8119
|
+
var { Command } = require_commander(), p3 = (init_dist4(), __toCommonJS(dist_exports2)), ui = require_ui(), log2 = require_log(), { CLI_VERSION } = require_settings(), { CancelError } = require_common(), commands = require_commands();
|
|
8002
8120
|
process.env.AWS_CLI_AGENT_TOOLKIT_HINT_DISABLED = "true";
|
|
8003
8121
|
var EXAMPLES = `
|
|
8004
8122
|
Examples:
|
|
@@ -8013,19 +8131,28 @@ Examples:
|
|
|
8013
8131
|
sets AWS_PROFILE in the current terminal. Without it, use 'npx switch-profile'.
|
|
8014
8132
|
|
|
8015
8133
|
Exit codes: 0 ok \xB7 1 error \xB7 2 login required \xB7 3 bad input \xB7 130 cancelled`, run = (action) => async (...args) => {
|
|
8016
|
-
let
|
|
8134
|
+
let cmd = args[args.length - 1], opts = cmd.optsWithGlobals();
|
|
8017
8135
|
opts.debug && (process.env.SWITCH_PROFILE_DEBUG = "1");
|
|
8018
8136
|
try {
|
|
8019
8137
|
await action(...args.slice(0, -2), opts);
|
|
8020
8138
|
} catch (err) {
|
|
8021
8139
|
if (err instanceof CancelError) {
|
|
8022
|
-
p3.cancel(`Cancelled ${ui.unicode ? "\u{1F44B}" : ""}`), process.exitCode = 130;
|
|
8140
|
+
log2.info("cancelled by the user"), p3.cancel(`Cancelled ${ui.unicode ? "\u{1F44B}" : ""}`), process.exitCode = 130;
|
|
8023
8141
|
return;
|
|
8024
8142
|
}
|
|
8025
|
-
opts.json && err instanceof ui.CliError ? process.stderr.write(JSON.stringify({ error: err.message, hint: err.hint || null, code: err.exitCode }) + `
|
|
8143
|
+
log2.error(`${cmd.name()} failed`, err), opts.json && err instanceof ui.CliError ? process.stderr.write(JSON.stringify({ error: err.message, hint: err.hint || null, code: err.exitCode }) + `
|
|
8026
8144
|
`) : ui.printError(err), process.exitCode = err.exitCode || 1;
|
|
8027
8145
|
}
|
|
8146
|
+
}, crashed = (err) => {
|
|
8147
|
+
log2.error("unexpected crash", err);
|
|
8148
|
+
try {
|
|
8149
|
+
ui.printError(err instanceof Error ? err : new Error(String(err)));
|
|
8150
|
+
} catch {
|
|
8151
|
+
console.error(err);
|
|
8152
|
+
}
|
|
8153
|
+
process.exit(1);
|
|
8028
8154
|
}, main = (argv) => {
|
|
8155
|
+
log2.start({ version: CLI_VERSION, argv }), process.on("uncaughtException", crashed), process.on("unhandledRejection", crashed), process.on("exit", (code) => log2.info(`exit ${code}`));
|
|
8029
8156
|
let program = new Command();
|
|
8030
8157
|
return program.name("switch-profile").description("Switch between AWS profiles, log in with SSO, and keep every terminal on the right account.").version(CLI_VERSION, "-v, --version").argument("[profile...]", "profile name or search text").option("--json", "machine-readable output").option("--no-input", "never prompt (fails with a hint instead)").option("--debug", "show error details").addHelpText("after", EXAMPLES).showSuggestionAfterError().action(run((words, opts) => commands.switch((words || []).join(" "), opts))), program.command("use").argument("<profile>").description("switch to a profile by its exact name").action(run(commands.use)), program.command("status").description("show the current profile and login state").action(run((opts) => commands.status([], opts))), program.command("login").argument("[profile]", "defaults to this terminal's profile, then the default profile").description("log in again now, even if the session is still valid").option("--device", "use a device code (approve from any device)").option("--browser", "use the browser on this machine").action(run(commands.login)), program.command("logout").description("end every SSO session on this machine").option("-y, --yes", "do not ask for confirmation").action(run((opts) => commands.logout([], opts))), program.command("add").description("add profiles: import an SSO portal, or create one").option("--from-sso [session]", "import (or re-sync) every account and role of an [sso-session]").option("--region <region>", "default region of imported profiles").option("--prefix <prefix>", "name prefix of imported profiles").option("--prune", "remove imported profiles that no longer exist").option("-y, --yes", "do not ask for confirmation").action(run((opts) => commands.add([], opts))), program.command("remove").alias("rm").argument("[profiles...]").description("remove profiles").option("-y, --yes", "do not ask for confirmation").action(run(commands.remove)), program.command("settings").description("per-terminal switching, login mode and more").action(run((opts) => commands.settings([], opts))), program.command("switch", { hidden: !0 }).action(run((opts) => commands.switch("", opts))), program.parseAsync(argv);
|
|
8031
8158
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switch-profile",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"switch-profile": "index.js"
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"eslint": "^10.0.0",
|
|
46
46
|
"fuzzysort": "^3.1.0",
|
|
47
47
|
"globals": "^17.13.0",
|
|
48
|
-
"mocha": "^8.
|
|
48
|
+
"mocha": "^11.8.0",
|
|
49
49
|
"uqr": "^0.1.3"
|
|
50
50
|
},
|
|
51
51
|
"files": [
|