switch-profile 2.2.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 +12 -2
- package/dist/cli.js +170 -57
- package/package.json +1 -1
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
|
|
|
@@ -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"
|
|
@@ -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
|
}
|
|
@@ -5713,8 +5799,13 @@ var require_login = __commonJS({
|
|
|
5713
5799
|
text2 = text2 || "";
|
|
5714
5800
|
let urls = text2.match(URL_REGEX) || [], code = (text2.match(CODE_REGEX) || [])[1] || null, completeUrl = urls.find((u5) => /user_code=/.test(u5)) || null;
|
|
5715
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}` };
|
|
5716
5806
|
};
|
|
5717
5807
|
module2.exports = {
|
|
5808
|
+
configureSsoFailure,
|
|
5718
5809
|
isRemoteSession,
|
|
5719
5810
|
resolveLoginMode,
|
|
5720
5811
|
loginFlags,
|
|
@@ -5835,12 +5926,12 @@ var require_regions = __commonJS({
|
|
|
5835
5926
|
// src/aws/index.js
|
|
5836
5927
|
var require_aws = __commonJS({
|
|
5837
5928
|
"src/aws/index.js"(exports2, module2) {
|
|
5838
|
-
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 ? `
|
|
5839
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 () => {
|
|
5840
5931
|
if (_awsCliVersion)
|
|
5841
5932
|
return _awsCliVersion;
|
|
5842
|
-
let path2 = await whichAws(), stat = path2 ? await fs.promises.stat(fs.realpathSync(path2)).catch(() => null) : null, cached = (await settings2.read().catch((
|
|
5843
|
-
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);
|
|
5844
5935
|
}, awsCliV2Exists = (noFailIfMissing) => catchErrors((async () => {
|
|
5845
5936
|
let [awsCliErrors] = await awsExists();
|
|
5846
5937
|
if (awsCliErrors) {
|
|
@@ -5932,9 +6023,9 @@ var require_aws = __commonJS({
|
|
|
5932
6023
|
if (!atLeast(version, [2, 32, 0]))
|
|
5933
6024
|
throw new Error(`'aws login' needs AWS CLI 2.32 or later (you have ${version}). Update the AWS CLI and try again.`);
|
|
5934
6025
|
let remote = isRemoteSession(process.env, process.platform) ? ["--remote"] : [];
|
|
5935
|
-
await run2("aws", ["login", "--profile", profile, ...remote], {
|
|
6026
|
+
await run2("aws", ["login", "--profile", profile, ...remote], { tee: !0 });
|
|
5936
6027
|
}, logoutAll = async () => {
|
|
5937
|
-
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));
|
|
5938
6029
|
}, readSsoToken = async ({ ssoSession, startUrl }) => {
|
|
5939
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);
|
|
5940
6031
|
return !token || !token.accessToken || new Date(token.expiresAt).getTime() < Date.now() + 60 * 1e3 ? null : token;
|
|
@@ -6016,9 +6107,9 @@ var require_aws = __commonJS({
|
|
|
6016
6107
|
})()), createSsoProfile = (name) => catchErrors((async () => {
|
|
6017
6108
|
await awsCliV2Exists();
|
|
6018
6109
|
try {
|
|
6019
|
-
await run2("aws", ["configure", "sso", "--profile", name], {
|
|
6110
|
+
await run2("aws", ["configure", "sso", "--profile", name], { tee: !0 });
|
|
6020
6111
|
} catch (err) {
|
|
6021
|
-
throw new Error(`'aws configure sso' failed
|
|
6112
|
+
throw Object.assign(new Error(`'aws configure sso' failed: ${err.message}`, { cause: err }), { output: err.output || "" });
|
|
6022
6113
|
}
|
|
6023
6114
|
let [configStrErrors, configStr] = await getConfigFile();
|
|
6024
6115
|
if (configStrErrors)
|
|
@@ -6059,7 +6150,7 @@ var require_aws = __commonJS({
|
|
|
6059
6150
|
// src/shell.js
|
|
6060
6151
|
var require_shell = __commonJS({
|
|
6061
6152
|
"src/shell.js"(exports2, module2) {
|
|
6062
|
-
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}() {
|
|
6063
6154
|
local __sp_file __sp_status
|
|
6064
6155
|
__sp_file="$(mktemp "\${TMPDIR:-/tmp}/switch-profile.XXXXXX")" || return 1
|
|
6065
6156
|
if [ -n "\${SWITCH_PROFILE_DEV_BIN:-}" ]; then
|
|
@@ -6193,7 +6284,7 @@ Register-ArgumentCompleter -CommandName ${FUNCTION_NAME} -ParameterName Rest -Sc
|
|
|
6193
6284
|
if (IS_WINDOWS && !shellPath) {
|
|
6194
6285
|
let files = [];
|
|
6195
6286
|
for (let exe of ["pwsh", "powershell"]) {
|
|
6196
|
-
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", ""));
|
|
6197
6288
|
file && !files.includes(file) && files.push(file);
|
|
6198
6289
|
}
|
|
6199
6290
|
if (files.length)
|
|
@@ -6316,7 +6407,7 @@ var require_migrate = __commonJS({
|
|
|
6316
6407
|
// src/commands/common.js
|
|
6317
6408
|
var require_common = __commonJS({
|
|
6318
6409
|
"src/commands/common.js"(exports2, module2) {
|
|
6319
|
-
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 {
|
|
6320
6411
|
}, ask = async (promise) => {
|
|
6321
6412
|
let value = await promise;
|
|
6322
6413
|
if (p4.isCancel(value))
|
|
@@ -6329,14 +6420,14 @@ var require_common = __commonJS({
|
|
|
6329
6420
|
}, preflight = async ({ interactive, quiet }) => {
|
|
6330
6421
|
let [cliErrors, cliExists] = await aws.awsCliV2Exists(!0);
|
|
6331
6422
|
if (cliErrors)
|
|
6332
|
-
throw
|
|
6423
|
+
throw ui2.cliErrorFrom(cliErrors, { hint: "switch-profile needs AWS CLI v2." });
|
|
6333
6424
|
if (!cliExists)
|
|
6334
6425
|
throw new ui2.CliError("The AWS CLI is not installed.", { hint: INSTALL_HINTS[process.platform] || INSTALL_HINTS.linux });
|
|
6335
6426
|
let result;
|
|
6336
6427
|
try {
|
|
6337
6428
|
result = await migrate.runMigrations();
|
|
6338
6429
|
} catch (err) {
|
|
6339
|
-
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;
|
|
6340
6431
|
}
|
|
6341
6432
|
if (result.migrated && !quiet && p4.log.info([
|
|
6342
6433
|
`Upgraded your AWS setup for switch-profile ${settings2.CLI_VERSION} ${ui2.unicode ? "\u{1F389}" : ""}`,
|
|
@@ -6347,7 +6438,7 @@ var require_common = __commonJS({
|
|
|
6347
6438
|
let legacy = await migrate.findLegacySsoProfiles();
|
|
6348
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."))));
|
|
6349
6440
|
}
|
|
6350
|
-
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));
|
|
6351
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)")}`);
|
|
6352
6443
|
}, upgradeLegacySso = async (names) => {
|
|
6353
6444
|
let { sessions, backups } = await migrate.upgradeLegacySsoProfiles(names);
|
|
@@ -6360,10 +6451,10 @@ var require_common = __commonJS({
|
|
|
6360
6451
|
}, loadState = async () => {
|
|
6361
6452
|
let [listErrors, profiles] = await aws.listProfiles();
|
|
6362
6453
|
if (listErrors)
|
|
6363
|
-
throw
|
|
6454
|
+
throw ui2.cliErrorFrom(listErrors);
|
|
6364
6455
|
let [defaultErrors, info] = await aws.getDefaultProfile();
|
|
6365
6456
|
if (defaultErrors)
|
|
6366
|
-
throw
|
|
6457
|
+
throw ui2.cliErrorFrom(defaultErrors);
|
|
6367
6458
|
let defaultProfile = profiles.find((x2) => x2.name == info.profile) || null;
|
|
6368
6459
|
return { profiles, defaultName: defaultProfile ? defaultProfile.name : null, defaultProfile, settings: await settings2.read() };
|
|
6369
6460
|
}, loginKey = (profile) => profile.sso_session || profile.sso_start_url || profile.name, describe = (profile, identity) => ui2.join([
|
|
@@ -7392,11 +7483,11 @@ var require_clipboard = __commonJS({
|
|
|
7392
7483
|
// src/commands/login-flow.js
|
|
7393
7484
|
var require_login_flow = __commonJS({
|
|
7394
7485
|
"src/commands/login-flow.js"(exports2, module2) {
|
|
7395
|
-
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 }) => {
|
|
7396
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);
|
|
7397
7488
|
if (!interactive)
|
|
7398
7489
|
return plainLogin(args, label);
|
|
7399
|
-
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) => {
|
|
7400
7491
|
if (key === "") {
|
|
7401
7492
|
child.kill();
|
|
7402
7493
|
return;
|
|
@@ -7441,7 +7532,7 @@ ${ui2.dim(`If nothing opens, visit: ${parsed.url}`)}`);
|
|
|
7441
7532
|
}, RAW_OUTPUT_AFTER_MS), code = await new Promise((resolve) => {
|
|
7442
7533
|
child.on("error", () => resolve(-1)), child.on("close", resolve);
|
|
7443
7534
|
});
|
|
7444
|
-
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) {
|
|
7445
7536
|
spin ? spin.stop(`${ui2.unicode ? "\u{1F513} " : ""}Logged in`) : p4.log.success("Logged in"), await rememberLogin(loginKey);
|
|
7446
7537
|
return;
|
|
7447
7538
|
}
|
|
@@ -7453,12 +7544,15 @@ ${ui2.dim(`If nothing opens, visit: ${parsed.url}`)}`);
|
|
|
7453
7544
|
`) || lines.slice(-1)[0] || "";
|
|
7454
7545
|
throw new ui2.CliError(`SSO login for ${label} failed.${detail ? `
|
|
7455
7546
|
${detail}` : ""}`, {
|
|
7456
|
-
|
|
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()}`
|
|
7457
7549
|
});
|
|
7458
7550
|
}, plainLogin = async (args, label) => {
|
|
7459
7551
|
console.error(`Logging in to ${label}. Approve the request using the URL and code below.`), await new Promise((resolve, reject) => {
|
|
7460
|
-
let child = spawn("aws", args, { stdio: ["ignore", process.stderr, process.stderr], ...IS_WINDOWS ? { shell: !0 } : {} });
|
|
7461
|
-
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
|
+
});
|
|
7462
7556
|
});
|
|
7463
7557
|
}, rememberLogin = async (key) => {
|
|
7464
7558
|
if (!key)
|
|
@@ -7540,7 +7634,7 @@ ${gray(S_BAR2)}`;
|
|
|
7540
7634
|
// src/commands/switch.js
|
|
7541
7635
|
var require_switch = __commonJS({
|
|
7542
7636
|
"src/commands/switch.js"(exports2, module2) {
|
|
7543
|
-
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 } = {}) => {
|
|
7544
7638
|
p4.intro(`${ui2.badge("switch-profile")} ${ui2.dim(settings2.CLI_VERSION)}`);
|
|
7545
7639
|
let lines = [];
|
|
7546
7640
|
if (state.defaultProfile) {
|
|
@@ -7601,20 +7695,20 @@ ${ui2.dim(viaShortcut ? "Switching clears them from this terminal." : `Unset ${s
|
|
|
7601
7695
|
return result;
|
|
7602
7696
|
let loginProfile = profile.kind == "role" && profile.source_profile ? state.profiles.find((x2) => x2.name == profile.source_profile) : profile;
|
|
7603
7697
|
if (!(loginProfile && (loginProfile.kind == "sso" || loginProfile.kind == "login")))
|
|
7604
|
-
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 });
|
|
7605
7699
|
if (!interactive && !force)
|
|
7606
7700
|
throw new ui2.CliError(`${profile.name} needs a login.`, { code: 2, hint: `Run: switch-profile login ${loginProfile.name}` });
|
|
7607
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)
|
|
7608
|
-
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 });
|
|
7609
7703
|
return result;
|
|
7610
7704
|
}, firstLine = (text2) => String(text2 || "").trim().split(`
|
|
7611
7705
|
`).filter(Boolean).slice(-1)[0], switchTo = async (profile, state, opts) => (await doSwitch(profile, state, opts), { switched: !0 }), doSwitch = async (profile, state, opts) => {
|
|
7612
7706
|
let { interactive, json } = opts, identity = await connect(profile, state, { interactive, quiet: json, force: opts.force }), [defaultErrors] = await aws.setDefaultProfile(profile.name);
|
|
7613
7707
|
if (defaultErrors)
|
|
7614
|
-
throw
|
|
7708
|
+
throw ui2.cliErrorFrom(defaultErrors);
|
|
7615
7709
|
let current = await settings2.read();
|
|
7616
7710
|
await settings2.update({ usage: rank.recordUsage(current.usage, profile.name) });
|
|
7617
|
-
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));
|
|
7618
7712
|
if (json) {
|
|
7619
7713
|
process.stdout.write(JSON.stringify({ profile: profile.name, account: identity.account, arn: identity.arn, region: profile.region || null, default: !0, terminal }) + `
|
|
7620
7714
|
`);
|
|
@@ -7634,7 +7728,7 @@ ${ui2.dim(viaShortcut ? "Switching clears them from this terminal." : `Unset ${s
|
|
|
7634
7728
|
${ui2.dim("set as the default profile")}`), await offerShortcut(profile);
|
|
7635
7729
|
}, exportHint = (name) => process.platform == "win32" ? `$env:AWS_PROFILE = "${name}" ${ui2.dim("(PowerShell)")}
|
|
7636
7730
|
set AWS_PROFILE=${name} ${ui2.dim("(CMD)")}` : `export AWS_PROFILE=${name}`, offerShortcut = async (profile) => {
|
|
7637
|
-
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:")}
|
|
7638
7732
|
${ui2.accent(exportHint(profile.name))}`;
|
|
7639
7733
|
if (status && status.installed) {
|
|
7640
7734
|
p4.outro(`${hint}
|
|
@@ -7690,7 +7784,7 @@ ${ui2.dim(`Open a new terminal or run: ${installed.reload}`)}`), installed.name
|
|
|
7690
7784
|
return result;
|
|
7691
7785
|
if (next == "throw")
|
|
7692
7786
|
throw error;
|
|
7693
|
-
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();
|
|
7694
7788
|
}
|
|
7695
7789
|
}, switchCommand = async (query, opts) => {
|
|
7696
7790
|
let interactive = ui2.isInteractive(opts);
|
|
@@ -7729,7 +7823,7 @@ ${ui2.dim(`Open a new terminal or run: ${installed.reload}`)}`), installed.name
|
|
|
7729
7823
|
// src/commands/index.js
|
|
7730
7824
|
var require_commands = __commonJS({
|
|
7731
7825
|
"src/commands/index.js"(exports2, module2) {
|
|
7732
|
-
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) => {
|
|
7733
7827
|
!opts.fromPicker && !opts.json && p4.intro(`${ui2.badge("switch-profile")} ${ui2.dim(settings2.CLI_VERSION)}`);
|
|
7734
7828
|
}, begin = async (opts, { needsInput } = {}) => {
|
|
7735
7829
|
let interactive = ui2.isInteractive(opts);
|
|
@@ -7748,7 +7842,7 @@ var require_commands = __commonJS({
|
|
|
7748
7842
|
return interactive && !opts.json && intro2(opts), switchTo(findProfile(state, name), state, { ...opts, interactive });
|
|
7749
7843
|
}, status = async (args, opts) => {
|
|
7750
7844
|
await begin(opts);
|
|
7751
|
-
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 };
|
|
7752
7846
|
if (opts.json) {
|
|
7753
7847
|
let at = d ? (state.settings.logins || {})[loginKey(d)] : null;
|
|
7754
7848
|
process.stdout.write(JSON.stringify({
|
|
@@ -7813,21 +7907,31 @@ var require_commands = __commonJS({
|
|
|
7813
7907
|
return importFromSso(state, { ...opts, interactive });
|
|
7814
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) }));
|
|
7815
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);
|
|
7816
7914
|
p4.log.message([
|
|
7817
7915
|
"Handing over to aws configure sso. You will be asked for:",
|
|
7818
|
-
`${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)")}`,
|
|
7819
7917
|
`${ui2.dim("2.")} the start URL ${ui2.dim("(e.g. https://acme.awsapps.com/start)")}`,
|
|
7820
|
-
`${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.`)] : []
|
|
7821
7920
|
].join(`
|
|
7822
7921
|
`));
|
|
7823
7922
|
let [errors, upgraded] = await aws.createSsoProfile(name);
|
|
7824
|
-
if (errors)
|
|
7825
|
-
|
|
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
|
+
}
|
|
7826
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.`);
|
|
7827
7931
|
} else if (kind == "login") {
|
|
7828
7932
|
let region = await chooseRegion("Default region", "us-east-1"), [configErrors, configStr] = await aws.getConfigFile();
|
|
7829
7933
|
if (configErrors)
|
|
7830
|
-
throw
|
|
7934
|
+
throw ui2.cliErrorFrom(configErrors);
|
|
7831
7935
|
let ini = require_ini();
|
|
7832
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);
|
|
7833
7937
|
} else {
|
|
@@ -7836,7 +7940,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7836
7940
|
return p4.outro(ui2.dim("Nothing changed."));
|
|
7837
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 });
|
|
7838
7942
|
if (errors)
|
|
7839
|
-
throw
|
|
7943
|
+
throw ui2.cliErrorFrom(errors);
|
|
7840
7944
|
}
|
|
7841
7945
|
p4.log.success(`Profile ${ui2.accent(name)} created`);
|
|
7842
7946
|
let next = await loadState();
|
|
@@ -7854,7 +7958,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7854
7958
|
}, importFromSso = async (state, opts) => {
|
|
7855
7959
|
let { interactive } = opts, [configErrors, configStr] = await aws.getConfigFile();
|
|
7856
7960
|
if (configErrors)
|
|
7857
|
-
throw
|
|
7961
|
+
throw ui2.cliErrorFrom(configErrors);
|
|
7858
7962
|
let sessions = transforms.listSsoSessions(configStr), sessionName = opts.fromSso && opts.fromSso !== !0 ? opts.fromSso : null;
|
|
7859
7963
|
if (sessionName && !sessions.some((s) => s.name == sessionName))
|
|
7860
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." });
|
|
@@ -7892,7 +7996,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7892
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"}`);
|
|
7893
7997
|
} catch (err) {
|
|
7894
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(`
|
|
7895
|
-
`).slice(-1)[0] });
|
|
7999
|
+
`).slice(-1)[0], cause: err });
|
|
7896
8000
|
}
|
|
7897
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 });
|
|
7898
8002
|
if (!preview.added.length && !preview.stale.length)
|
|
@@ -7953,7 +8057,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7953
8057
|
}
|
|
7954
8058
|
let [errors] = await aws.deleteProfiles(names);
|
|
7955
8059
|
if (errors)
|
|
7956
|
-
throw
|
|
8060
|
+
throw ui2.cliErrorFrom(errors);
|
|
7957
8061
|
p4.outro(`${ui2.ok(ui2.sym.ok)} Removed ${names.join(", ")}`);
|
|
7958
8062
|
}, LOGIN_MODE_LABELS = {
|
|
7959
8063
|
auto: "Auto: device code over SSH, browser otherwise",
|
|
@@ -8012,7 +8116,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
8012
8116
|
});
|
|
8013
8117
|
|
|
8014
8118
|
// src/cli.js
|
|
8015
|
-
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();
|
|
8016
8120
|
process.env.AWS_CLI_AGENT_TOOLKIT_HINT_DISABLED = "true";
|
|
8017
8121
|
var EXAMPLES = `
|
|
8018
8122
|
Examples:
|
|
@@ -8027,19 +8131,28 @@ Examples:
|
|
|
8027
8131
|
sets AWS_PROFILE in the current terminal. Without it, use 'npx switch-profile'.
|
|
8028
8132
|
|
|
8029
8133
|
Exit codes: 0 ok \xB7 1 error \xB7 2 login required \xB7 3 bad input \xB7 130 cancelled`, run = (action) => async (...args) => {
|
|
8030
|
-
let
|
|
8134
|
+
let cmd = args[args.length - 1], opts = cmd.optsWithGlobals();
|
|
8031
8135
|
opts.debug && (process.env.SWITCH_PROFILE_DEBUG = "1");
|
|
8032
8136
|
try {
|
|
8033
8137
|
await action(...args.slice(0, -2), opts);
|
|
8034
8138
|
} catch (err) {
|
|
8035
8139
|
if (err instanceof CancelError) {
|
|
8036
|
-
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;
|
|
8037
8141
|
return;
|
|
8038
8142
|
}
|
|
8039
|
-
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 }) + `
|
|
8040
8144
|
`) : ui.printError(err), process.exitCode = err.exitCode || 1;
|
|
8041
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);
|
|
8042
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}`));
|
|
8043
8156
|
let program = new Command();
|
|
8044
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);
|
|
8045
8158
|
};
|