switch-profile 2.3.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -3
- package/dist/cli.js +364 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ npx switch-profile
|
|
|
21
21
|
│ ○ globex-dev ReadOnly 333333333333 us-east-1
|
|
22
22
|
│ ○ initech-dev Admin 444444444444 eu-west-1
|
|
23
23
|
│
|
|
24
|
-
│ [Log in] [Add] [Remove] [Log out] [Settings]
|
|
24
|
+
│ [Log in] [Add] [Portals] [Rename] [Remove] [Log out] [Settings]
|
|
25
25
|
└ ↑↓ choose · enter switch · tab actions · esc quit
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -100,8 +100,9 @@ No profiles yet? `switch-profile` starts the **Add profiles** flow. To import ev
|
|
|
100
100
|
Running `sp` (or `npx switch-profile`) with no arguments opens the home screen:
|
|
101
101
|
|
|
102
102
|
- **Type and press Enter** to switch. That's the whole path for everyday use.
|
|
103
|
-
- **Tab** moves to the action bar below the list, which is always visible however many profiles you have: **Log in**, **Add**, **Remove**, **Log out**, **Settings**. Use ←→ to choose and Enter to open.
|
|
103
|
+
- **Tab** moves to the action bar below the list, which is always visible however many profiles you have: **Log in**, **Add**, **Portals**, **Rename**, **Remove**, **Log out**, **Settings**. Use ←→ to choose and Enter to open.
|
|
104
104
|
- **Every action opens as a page and comes back** to the home screen when it's done. **Esc** goes back from a page, and quits from the home screen.
|
|
105
|
+
- **When something fails, it stops and tells you.** An error screen says what failed, what AWS answered, why, and which SSO portal (start URL and region) was involved. Then it asks what to do: fix the portal's region in one step, edit the portal, try again, or go back.
|
|
105
106
|
- A switch ends the session, which is what lets `sp` set `AWS_PROFILE` in your terminal.
|
|
106
107
|
|
|
107
108
|
## Commands
|
|
@@ -115,7 +116,9 @@ Running `sp` (or `npx switch-profile`) with no arguments opens the home screen:
|
|
|
115
116
|
| `logout [--yes]` | Ends every SSO session on this machine (`aws sso logout`, plus `aws logout --all` on AWS CLI 2.32+). |
|
|
116
117
|
| `add` | Adds profiles: import accounts from an SSO portal (recommended), a single SSO profile, a console sign-in profile, or access keys. |
|
|
117
118
|
| `add --from-sso [session]` | Imports or re-syncs every account and role of an `[sso-session]`. Options: `--region`, `--prefix`, `--prune`, `--yes`. |
|
|
119
|
+
| `rename [profile] [new-name]` | Renames a profile (alias `mv`), for example an imported `acme-prod-workloads-administratoraccess` to `acme-prod`. Also updates `source_profile` references, the default profile and the home screen order. SSO logins are kept. Scripts, terminals and tools that use the old name need the new one. |
|
|
118
120
|
| `remove [profiles...] [--yes]` | Removes profiles (alias `rm`). The current default cannot be removed. |
|
|
121
|
+
| `portals [name]` | Lists your SSO portals (`[sso-session]`) and edits one in place: start URL and SSO region (alias `portal`). Flags a portal whose region differs from the one its portal page names, and fixes it in one step. `--region` / `--start-url` edit without prompting; `--json` lists them (`--check` adds the region read from each portal). |
|
|
119
122
|
| `settings` | Per-terminal switching, SSO login mode, legacy SSO upgrade. |
|
|
120
123
|
|
|
121
124
|
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.
|
|
@@ -155,7 +158,7 @@ sp add # then choose "Accounts from an SSO portal"
|
|
|
155
158
|
|
|
156
159
|
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.
|
|
157
160
|
2. Log in if needed.
|
|
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`.
|
|
161
|
+
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`. Rename any of them later with `sp rename` (re-imports still recognize them).
|
|
159
162
|
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).
|
|
160
163
|
|
|
161
164
|
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.
|
|
@@ -241,6 +244,8 @@ The SSO region is wrong. It must be the region of your IAM Identity Center insta
|
|
|
241
244
|
|
|
242
245
|
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
246
|
|
|
247
|
+
When importing from a portal (`sp add` > Accounts from an SSO portal) or switching to one of its profiles, the same mistake shows as `Invalid request` during the login. `switch-profile` reads the portal's real region from its start page and offers to fix the saved portal. You can also check and fix every portal any time with `sp portals` (or **Portals** on the home screen).
|
|
248
|
+
|
|
244
249
|
### Something failed and the message is not enough
|
|
245
250
|
|
|
246
251
|
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.
|
package/dist/cli.js
CHANGED
|
@@ -5419,7 +5419,7 @@ var require_package = __commonJS({
|
|
|
5419
5419
|
"package.json"(exports2, module2) {
|
|
5420
5420
|
module2.exports = {
|
|
5421
5421
|
name: "switch-profile",
|
|
5422
|
-
version: "2.
|
|
5422
|
+
version: "2.5.0",
|
|
5423
5423
|
main: "index.js",
|
|
5424
5424
|
bin: {
|
|
5425
5425
|
"switch-profile": "index.js"
|
|
@@ -5602,6 +5602,9 @@ var require_ini = __commonJS({
|
|
|
5602
5602
|
return str;
|
|
5603
5603
|
let newline = detectNewline(str);
|
|
5604
5604
|
return tidy(stringify(blocks.filter((b3) => b3.name !== name), newline), newline);
|
|
5605
|
+
}, renameSection = (str, name, newName) => {
|
|
5606
|
+
let blocks = parse(str), block2 = findBlock(blocks, name);
|
|
5607
|
+
return block2 ? (block2.lines[0] = `[${newName}]`, stringify(blocks, detectNewline(str))) : str;
|
|
5605
5608
|
}, setKeys = (str, name, changes) => {
|
|
5606
5609
|
let blocks = parse(str), block2 = findBlock(blocks, name);
|
|
5607
5610
|
if (!block2)
|
|
@@ -5636,6 +5639,7 @@ var require_ini = __commonJS({
|
|
|
5636
5639
|
getSection,
|
|
5637
5640
|
setSection,
|
|
5638
5641
|
removeSection,
|
|
5642
|
+
renameSection,
|
|
5639
5643
|
setKeys
|
|
5640
5644
|
};
|
|
5641
5645
|
}
|
|
@@ -5755,14 +5759,35 @@ var require_transforms = __commonJS({
|
|
|
5755
5759
|
for (let name of stale)
|
|
5756
5760
|
config = ini.removeSection(config, configSectionName(config, name)), removed.push(name);
|
|
5757
5761
|
return { config, added, addedEntries, existing, stale, removed };
|
|
5762
|
+
}, escapeRegex = (text2) => text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), renameProfile = (configStr, credsStr, from, to) => {
|
|
5763
|
+
let section = from == "default" ? null : configSectionName(configStr, from);
|
|
5764
|
+
if (!section)
|
|
5765
|
+
throw new Error(`Profile ${from} not found in ~/.aws/config.`);
|
|
5766
|
+
if (to == "default" || configSectionName(configStr, to) || ini.getSection(credsStr, to))
|
|
5767
|
+
throw new Error(`Profile ${to} already exists.`);
|
|
5768
|
+
let config = ini.renameSection(configStr, section, `profile ${to}`), creds = ini.renameSection(credsStr, from, to), sources = [], processes = [], mentions = new RegExp(`--profile[\\s=]+["']?${escapeRegex(from)}(?=["'\\s]|$)`);
|
|
5769
|
+
for (let s of ini.listSections(config).filter((s2) => s2 != "default" && !s2.startsWith("sso-session "))) {
|
|
5770
|
+
let params = ini.getSection(config, s), name = s.replace(/^profile\s+/, "");
|
|
5771
|
+
params.source_profile == from && (config = ini.setKeys(config, s, { source_profile: to }), sources.push(name)), params.credential_process && mentions.test(params.credential_process) && processes.push(name);
|
|
5772
|
+
}
|
|
5773
|
+
let defaults = ini.getSection(config, "default") || {}, wasDefault = defaults[NAME_KEY] == from;
|
|
5774
|
+
return wasDefault && (config = ini.setKeys(config, "default", { [NAME_KEY]: to, ...defaults.source_profile == from ? { source_profile: to } : {} })), { config, creds, sources, wasDefault, processes };
|
|
5758
5775
|
}, 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}`, [
|
|
5759
5776
|
["sso_start_url", startUrl],
|
|
5760
5777
|
["sso_region", region],
|
|
5761
5778
|
["sso_registration_scopes", SSO_SCOPES],
|
|
5762
5779
|
[VERSION_KEY, version]
|
|
5763
|
-
])
|
|
5780
|
+
]), updateSsoSession = (configStr, { name, startUrl, region, version }) => {
|
|
5781
|
+
let section = `sso-session ${name}`;
|
|
5782
|
+
return ini.getSection(configStr, section) ? ini.setKeys(configStr, section, {
|
|
5783
|
+
...startUrl ? { sso_start_url: startUrl } : {},
|
|
5784
|
+
...region ? { sso_region: region } : {},
|
|
5785
|
+
[VERSION_KEY]: version
|
|
5786
|
+
}) : configStr;
|
|
5787
|
+
};
|
|
5764
5788
|
module2.exports = {
|
|
5765
5789
|
GENERATED_KEY,
|
|
5790
|
+
updateSsoSession,
|
|
5766
5791
|
ACCOUNT_NAME_KEY,
|
|
5767
5792
|
generatedProfileName,
|
|
5768
5793
|
populateSsoProfiles,
|
|
@@ -5781,7 +5806,8 @@ var require_transforms = __commonJS({
|
|
|
5781
5806
|
sessionNameFromUrl,
|
|
5782
5807
|
upgradeLegacySsoProfiles,
|
|
5783
5808
|
stampProfile,
|
|
5784
|
-
listProfiles
|
|
5809
|
+
listProfiles,
|
|
5810
|
+
renameProfile
|
|
5785
5811
|
};
|
|
5786
5812
|
}
|
|
5787
5813
|
});
|
|
@@ -5799,13 +5825,22 @@ var require_login = __commonJS({
|
|
|
5799
5825
|
text2 = text2 || "";
|
|
5800
5826
|
let urls = text2.match(URL_REGEX) || [], code = (text2.match(CODE_REGEX) || [])[1] || null, completeUrl = urls.find((u5) => /user_code=/.test(u5)) || null;
|
|
5801
5827
|
return { url: urls.find((u5) => u5 !== completeUrl) || completeUrl || null, code, completeUrl };
|
|
5802
|
-
}, WRONG_REGION = /Invalid start url|invalid_request|InvalidRequestException/i,
|
|
5828
|
+
}, WRONG_REGION = /Invalid start url|invalid_request|InvalidRequestException/i, isWrongSsoRegion = (output) => WRONG_REGION.test(output || ""), PORTAL_REGION = /portal\.sso\.([a-z]{2}(?:-[a-z]+)+-\d+)\.amazonaws\.com/, portalRegionFromHeaders = (values) => {
|
|
5829
|
+
for (let v of values || []) {
|
|
5830
|
+
let m3 = (v || "").match(PORTAL_REGION);
|
|
5831
|
+
if (m3)
|
|
5832
|
+
return m3[1];
|
|
5833
|
+
}
|
|
5834
|
+
return null;
|
|
5835
|
+
}, configureSsoFailure = (output, sessions) => {
|
|
5803
5836
|
let lines = (output || "").split(`
|
|
5804
5837
|
`).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
|
|
5838
|
+
return isWrongSsoRegion(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}` };
|
|
5806
5839
|
};
|
|
5807
5840
|
module2.exports = {
|
|
5808
5841
|
configureSsoFailure,
|
|
5842
|
+
isWrongSsoRegion,
|
|
5843
|
+
portalRegionFromHeaders,
|
|
5809
5844
|
isRemoteSession,
|
|
5810
5845
|
resolveLoginMode,
|
|
5811
5846
|
loginFlags,
|
|
@@ -5926,7 +5961,7 @@ var require_regions = __commonJS({
|
|
|
5926
5961
|
// src/aws/index.js
|
|
5927
5962
|
var require_aws = __commonJS({
|
|
5928
5963
|
"src/aws/index.js"(exports2, module2) {
|
|
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 ? `
|
|
5964
|
+
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, portalRegionFromHeaders } = require_login(), regions = require_regions(), log3 = require_log(), IS_WINDOWS = process.platform === "win32", NL = IS_WINDOWS ? `
|
|
5930
5965
|
` : 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 () => {
|
|
5931
5966
|
if (_awsCliVersion)
|
|
5932
5967
|
return _awsCliVersion;
|
|
@@ -6029,6 +6064,18 @@ var require_aws = __commonJS({
|
|
|
6029
6064
|
}, readSsoToken = async ({ ssoSession, startUrl }) => {
|
|
6030
6065
|
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);
|
|
6031
6066
|
return !token || !token.accessToken || new Date(token.expiresAt).getTime() < Date.now() + 60 * 1e3 ? null : token;
|
|
6067
|
+
}, detectSsoRegion = async (startUrl, { timeoutMs = 5e3 } = {}) => {
|
|
6068
|
+
let started = Date.now();
|
|
6069
|
+
try {
|
|
6070
|
+
let url = new URL(startUrl);
|
|
6071
|
+
url.hash = "";
|
|
6072
|
+
let res = await fetch(url, { redirect: "follow", signal: AbortSignal.timeout(timeoutMs) });
|
|
6073
|
+
await res.body?.cancel();
|
|
6074
|
+
let region = portalRegionFromHeaders([res.headers.get("link"), res.headers.get("content-security-policy")]);
|
|
6075
|
+
return log3.info(`SSO region of ${url} \u2192 ${region || "not found"} (HTTP ${res.status} in ${Date.now() - started}ms)`), region;
|
|
6076
|
+
} catch (err) {
|
|
6077
|
+
return log3.tolerated(`SSO region lookup of ${startUrl}`, null)(err);
|
|
6078
|
+
}
|
|
6032
6079
|
}, listSsoAccounts = async (accessToken, region) => {
|
|
6033
6080
|
let data = await run2("aws", ["sso", "list-accounts", "--access-token", accessToken, "--region", region, "--output", "json"]);
|
|
6034
6081
|
return (JSON.parse(data).accountList || []).map((a2) => ({ accountId: a2.accountId, accountName: a2.accountName }));
|
|
@@ -6078,6 +6125,16 @@ var require_aws = __commonJS({
|
|
|
6078
6125
|
configStr = deleteProfileFromConfig(profile, configStr), credsStr = deleteProfileFromCreds(profile, credsStr);
|
|
6079
6126
|
}
|
|
6080
6127
|
updateConfig && await writeAwsFile(AWS_CONFIG_FILE, configStr), updateCreds && await writeAwsFile(AWS_CREDS_FILE, credsStr);
|
|
6128
|
+
})()), renameProfile = (from, to) => catchErrors((async () => {
|
|
6129
|
+
let errMsg = `Fail to rename profile ${from} to ${to}`, [configStrErrors, configStr] = await getConfigFile(), [credsStrErrors, credsStr] = await getCredsFile();
|
|
6130
|
+
if (configStrErrors || credsStrErrors)
|
|
6131
|
+
throw wrapErrors(errMsg, configStrErrors || credsStrErrors);
|
|
6132
|
+
try {
|
|
6133
|
+
let { config, creds, sources, wasDefault, processes } = transforms.renameProfile(configStr, credsStr, from, to);
|
|
6134
|
+
return await writeAwsFiles({ config, creds }), { sources, wasDefault, processes };
|
|
6135
|
+
} catch (err) {
|
|
6136
|
+
throw wrapErrors(errMsg, [err]);
|
|
6137
|
+
}
|
|
6081
6138
|
})()), createProfile = ({ name, aws_access_key_id, aws_secret_access_key, region }) => catchErrors((async () => {
|
|
6082
6139
|
await awsCliV2Exists();
|
|
6083
6140
|
let errMsg = "Fail to create AWS profile";
|
|
@@ -6128,6 +6185,7 @@ var require_aws = __commonJS({
|
|
|
6128
6185
|
awsLogin,
|
|
6129
6186
|
logoutAll,
|
|
6130
6187
|
readSsoToken,
|
|
6188
|
+
detectSsoRegion,
|
|
6131
6189
|
listSsoAccounts,
|
|
6132
6190
|
listSsoRoles,
|
|
6133
6191
|
getSsoLoginStatus,
|
|
@@ -6135,6 +6193,7 @@ var require_aws = __commonJS({
|
|
|
6135
6193
|
getDefaultProfile,
|
|
6136
6194
|
setDefaultProfile,
|
|
6137
6195
|
deleteProfiles,
|
|
6196
|
+
renameProfile,
|
|
6138
6197
|
createProfile,
|
|
6139
6198
|
createSsoProfile,
|
|
6140
6199
|
backupAwsFiles,
|
|
@@ -6151,7 +6210,7 @@ var require_aws = __commonJS({
|
|
|
6151
6210
|
// src/shell.js
|
|
6152
6211
|
var require_shell = __commonJS({
|
|
6153
6212
|
"src/shell.js"(exports2, module2) {
|
|
6154
|
-
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}() {
|
|
6213
|
+
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", "rename", "portals"], PROFILES_SED = "sed -n 's/^\\[profile \\(.*\\)\\]$/\\1/p'", POSIX_FUNCTION = (shell) => `${FUNCTION_NAME}() {
|
|
6155
6214
|
local __sp_file __sp_status
|
|
6156
6215
|
__sp_file="$(mktemp "\${TMPDIR:-/tmp}/switch-profile.XXXXXX")" || return 1
|
|
6157
6216
|
if [ -n "\${SWITCH_PROFILE_DEV_BIN:-}" ]; then
|
|
@@ -6171,7 +6230,7 @@ var require_shell = __commonJS({
|
|
|
6171
6230
|
items=(\${(f)"$(${PROFILES_SED} "\${AWS_CONFIG_FILE:-$HOME/.aws/config}" 2>/dev/null)"})
|
|
6172
6231
|
if (( CURRENT == 2 )); then
|
|
6173
6232
|
compadd -- $items ${SUBCOMMANDS.join(" ")}
|
|
6174
|
-
elif [[ $words[2] == (remove|rm) ]] || { (( CURRENT == 3 )) && [[ $words[2] == (use|login) ]] }; then
|
|
6233
|
+
elif [[ $words[2] == (remove|rm) ]] || { (( CURRENT == 3 )) && [[ $words[2] == (use|login|rename|mv) ]] }; then
|
|
6175
6234
|
compadd -- $items
|
|
6176
6235
|
fi
|
|
6177
6236
|
}
|
|
@@ -6182,7 +6241,7 @@ fi`, BASH_COMPLETION = `_switch_profile_complete() {
|
|
|
6182
6241
|
items="$(${PROFILES_SED} "\${AWS_CONFIG_FILE:-$HOME/.aws/config}" 2>/dev/null)"
|
|
6183
6242
|
if [ "$COMP_CWORD" -eq 1 ]; then
|
|
6184
6243
|
items="$items ${SUBCOMMANDS.join(" ")}"
|
|
6185
|
-
elif [ "$COMP_CWORD" -lt 2 ] || ! case "\${COMP_WORDS[1]}" in use|login|remove|rm) true ;; *) false ;; esac; then
|
|
6244
|
+
elif [ "$COMP_CWORD" -lt 2 ] || ! case "\${COMP_WORDS[1]}" in use|login|rename|mv|remove|rm) true ;; *) false ;; esac; then
|
|
6186
6245
|
return 0
|
|
6187
6246
|
elif [ "$COMP_CWORD" -gt 2 ] && [ "\${COMP_WORDS[1]}" != remove ] && [ "\${COMP_WORDS[1]}" != rm ]; then
|
|
6188
6247
|
return 0
|
|
@@ -6211,7 +6270,7 @@ function __switch_profile_profiles
|
|
|
6211
6270
|
end
|
|
6212
6271
|
for __sp_cmd in ${FUNCTION_NAME} switch-profile
|
|
6213
6272
|
complete -c $__sp_cmd -f -n 'test (count (commandline -opc)) -eq 1' -a '(__switch_profile_profiles) ${SUBCOMMANDS.join(" ")}'
|
|
6214
|
-
complete -c $__sp_cmd -f -n 'test (count (commandline -opc)) -eq 2; and __fish_seen_subcommand_from use login' -a '(__switch_profile_profiles)'
|
|
6273
|
+
complete -c $__sp_cmd -f -n 'test (count (commandline -opc)) -eq 2; and __fish_seen_subcommand_from use login rename mv' -a '(__switch_profile_profiles)'
|
|
6215
6274
|
complete -c $__sp_cmd -f -n '__fish_seen_subcommand_from remove rm' -a '(__switch_profile_profiles)'
|
|
6216
6275
|
end`, POWERSHELL_FUNCTION = `function ${FUNCTION_NAME} {
|
|
6217
6276
|
param([Parameter(ValueFromRemainingArguments = $true)] [string[]] $Rest)
|
|
@@ -6467,9 +6526,15 @@ var require_common = __commonJS({
|
|
|
6467
6526
|
return { text: null, needsLogin: !1 };
|
|
6468
6527
|
let [, login] = await aws.getSsoLoginStatus(profile.sso_start_url), at = (state && state.settings.logins || {})[loginKey(profile)], since = at ? ui2.dim(`logged in ${ui2.ago(at)}`) : null, s = login ? login.state : "none";
|
|
6469
6528
|
return s == "refreshable" ? { text: ui2.join([ui2.ok(`SSO ${ui2.sym.ok}`) + " auto-refresh", since]), needsLogin: !1 } : s == "active" ? { text: ui2.join([ui2.ok(`SSO ${ui2.sym.ok}`) + ` ${Math.round((login.expiresAt - Date.now()) / 6e4)} min left`, since]), needsLogin: !1 } : { text: ui2.warn(s == "expired" ? "SSO login expired" : "not logged in"), needsLogin: !0 };
|
|
6470
|
-
}
|
|
6529
|
+
}, chooseRegion = async (message, initialValue) => ask(p4.autocomplete({
|
|
6530
|
+
message,
|
|
6531
|
+
maxItems: 8,
|
|
6532
|
+
initialValue,
|
|
6533
|
+
options: aws.regions.map((r2) => ({ value: r2.code, label: r2.code, hint: r2.name }))
|
|
6534
|
+
}));
|
|
6471
6535
|
module2.exports = {
|
|
6472
6536
|
CancelError: CancelError2,
|
|
6537
|
+
chooseRegion,
|
|
6473
6538
|
ask,
|
|
6474
6539
|
preflight,
|
|
6475
6540
|
upgradeLegacySso,
|
|
@@ -7484,7 +7549,7 @@ var require_clipboard = __commonJS({
|
|
|
7484
7549
|
// src/commands/login-flow.js
|
|
7485
7550
|
var require_login_flow = __commonJS({
|
|
7486
7551
|
"src/commands/login-flow.js"(exports2, module2) {
|
|
7487
|
-
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 }) => {
|
|
7552
|
+
var { spawn } = require("child_process"), p4 = (init_dist4(), __toCommonJS(dist_exports2)), { renderUnicodeCompact } = require_dist(), aws = require_aws(), settings2 = require_settings(), { parseLoginOutput, isRemoteSession, atLeast, isWrongSsoRegion } = 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 }) => {
|
|
7488
7553
|
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);
|
|
7489
7554
|
if (!interactive)
|
|
7490
7555
|
return plainLogin(args, label);
|
|
@@ -7542,12 +7607,12 @@ ${ui2.dim(`If nothing opens, visit: ${parsed.url}`)}`);
|
|
|
7542
7607
|
spin && spin.error("Login did not complete");
|
|
7543
7608
|
let lines = output.trim().split(`
|
|
7544
7609
|
`).map((l2) => l2.trim()).filter(Boolean), detail = lines.filter((l2) => /error|exception|denied|invalid|expired|unable/i.test(l2)).slice(-2).join(`
|
|
7545
|
-
`) || lines.slice(-1)[0] || ""
|
|
7546
|
-
throw new ui2.CliError(`SSO login for ${label} failed.${detail ? `
|
|
7610
|
+
`) || lines.slice(-1)[0] || "", wrongRegion = isWrongSsoRegion(output), error = new ui2.CliError(`SSO login for ${label} failed.${detail ? `
|
|
7547
7611
|
${detail}` : ""}`, {
|
|
7548
7612
|
cause: Object.assign(new Error(`aws ${args.join(" ")} exited with code ${code}`), { output }),
|
|
7549
|
-
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()}`
|
|
7613
|
+
hint: wrongRegion ? "The SSO region is most likely wrong: it must be the region where IAM Identity Center lives, not where you deploy. Check and fix it with: sp portals" : /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()}`
|
|
7550
7614
|
});
|
|
7615
|
+
throw error.wrongRegion = wrongRegion, error.target = target, error;
|
|
7551
7616
|
}, plainLogin = async (args, label) => {
|
|
7552
7617
|
console.error(`Logging in to ${label}. Approve the request using the URL and code below.`), await new Promise((resolve, reject) => {
|
|
7553
7618
|
let started = Date.now(), child = spawn("aws", args, { stdio: ["ignore", process.stderr, process.stderr], ...IS_WINDOWS ? { shell: !0 } : {} });
|
|
@@ -7568,6 +7633,93 @@ ${detail}` : ""}`, {
|
|
|
7568
7633
|
}
|
|
7569
7634
|
});
|
|
7570
7635
|
|
|
7636
|
+
// src/commands/portals.js
|
|
7637
|
+
var require_portals = __commonJS({
|
|
7638
|
+
"src/commands/portals.js"(exports2, module2) {
|
|
7639
|
+
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), aws = require_aws(), transforms = require_transforms(), settings2 = require_settings(), ui2 = require_ui(), { ask, chooseRegion } = require_common(), { ssoLogin } = require_login_flow(), validateStartUrl = (v) => /^https:\/\/\S+$/.test(v || "") ? void 0 : "Enter the https:// start URL of the portal.", validateRegion = (v) => aws.regions.some((r2) => r2.code == v) ? void 0 : `Unknown region ${v}.`, readConfig = async () => {
|
|
7640
|
+
let [errors, configStr] = await aws.getConfigFile();
|
|
7641
|
+
if (errors)
|
|
7642
|
+
throw ui2.cliErrorFrom(errors);
|
|
7643
|
+
return configStr;
|
|
7644
|
+
}, listPortals = async (state, { detect } = {}) => {
|
|
7645
|
+
let portals = transforms.listSsoSessions(await readConfig()).map((s) => ({
|
|
7646
|
+
name: s.name,
|
|
7647
|
+
startUrl: s.sso_start_url || null,
|
|
7648
|
+
region: s.sso_region || null,
|
|
7649
|
+
profiles: state.profiles.filter((x2) => x2.sso_session == s.name).map((x2) => x2.name)
|
|
7650
|
+
}));
|
|
7651
|
+
return detect && await Promise.all(portals.map(async (x2) => {
|
|
7652
|
+
x2.detectedRegion = x2.startUrl ? await aws.detectSsoRegion(x2.startUrl, { timeoutMs: 4e3 }) : null;
|
|
7653
|
+
})), portals;
|
|
7654
|
+
}, findPortal = async (state, name) => {
|
|
7655
|
+
let portal = (await listPortals(state)).find((x2) => x2.name == name);
|
|
7656
|
+
if (!portal)
|
|
7657
|
+
throw new ui2.CliError(`There is no SSO portal named ${name}.`, { code: 3, hint: "List them with: switch-profile portals" });
|
|
7658
|
+
return portal;
|
|
7659
|
+
}, regionMismatch = (region, detectedRegion) => !!(detectedRegion && region && detectedRegion != region), regionNote = ({ region, detectedRegion }) => regionMismatch(region, detectedRegion) ? ui2.join([region, ui2.warn(`${ui2.sym.warn} its portal is in ${detectedRegion}`)]) : detectedRegion ? ui2.join([region, ui2.dim("matches the portal")]) : region || ui2.warn("no region"), portalHint = (x2) => ui2.join([regionNote(x2), `${x2.profiles.length} profile${x2.profiles.length == 1 ? "" : "s"}`, x2.startUrl]), updatePortal = async (name, { startUrl, region }) => {
|
|
7660
|
+
let configStr = await readConfig();
|
|
7661
|
+
await aws.writeAwsFile(aws.AWS_CONFIG_FILE, transforms.updateSsoSession(configStr, { name, startUrl, region, version: settings2.CLI_VERSION }));
|
|
7662
|
+
}, editPortal = async (name, state, { offerLogin = !0, detectedRegion } = {}) => {
|
|
7663
|
+
let changed = !1, detected = detectedRegion;
|
|
7664
|
+
for (; ; ) {
|
|
7665
|
+
let portal = await findPortal(state, name);
|
|
7666
|
+
detected === void 0 && (detected = portal.startUrl ? await aws.detectSsoRegion(portal.startUrl) : null);
|
|
7667
|
+
let mismatch = regionMismatch(portal.region, detected);
|
|
7668
|
+
p4.log.info([
|
|
7669
|
+
ui2.strong(`Portal ${name}`),
|
|
7670
|
+
`${ui2.dim("Start URL ")} ${portal.startUrl || ui2.warn("missing")}`,
|
|
7671
|
+
`${ui2.dim("SSO region")} ${regionNote({ region: portal.region, detectedRegion: detected })}`,
|
|
7672
|
+
detected ? null : ui2.dim(" (could not read the region from the portal page)"),
|
|
7673
|
+
`${ui2.dim("Profiles ")} ${portal.profiles.length ? `${portal.profiles.slice(0, 4).join(", ")}${portal.profiles.length > 4 ? `, +${portal.profiles.length - 4} more` : ""}` : "none"}`
|
|
7674
|
+
].filter(Boolean).join(`
|
|
7675
|
+
`));
|
|
7676
|
+
let choice = await ask(p4.select({
|
|
7677
|
+
message: `Edit ${name}`,
|
|
7678
|
+
options: [
|
|
7679
|
+
...mismatch ? [{ value: "fix", label: `Change the SSO region to ${detected}`, hint: "what the portal page says" }] : [],
|
|
7680
|
+
{ value: "url", label: "Start URL", hint: portal.startUrl || void 0 },
|
|
7681
|
+
{ value: "region", label: "SSO region", hint: portal.region || void 0 },
|
|
7682
|
+
{ value: "done", label: changed ? "Done" : "Back" }
|
|
7683
|
+
]
|
|
7684
|
+
}));
|
|
7685
|
+
if (choice == "done")
|
|
7686
|
+
break;
|
|
7687
|
+
if (choice == "fix")
|
|
7688
|
+
await updatePortal(name, { region: detected }), p4.log.success(`${name} now uses the SSO region ${ui2.accent(detected)}`);
|
|
7689
|
+
else if (choice == "url") {
|
|
7690
|
+
let startUrl = (await ask(p4.text({ message: `Start URL of ${name}`, initialValue: portal.startUrl || "", validate: validateStartUrl }))).trim();
|
|
7691
|
+
if (startUrl == portal.startUrl)
|
|
7692
|
+
continue;
|
|
7693
|
+
await updatePortal(name, { startUrl }), detected = void 0, p4.log.success(`${name} now uses ${ui2.accent(startUrl)}`);
|
|
7694
|
+
} else {
|
|
7695
|
+
let region = await chooseRegion(`SSO region of ${name} ${ui2.dim("(where IAM Identity Center lives)")}`, detected || portal.region);
|
|
7696
|
+
if (region == portal.region)
|
|
7697
|
+
continue;
|
|
7698
|
+
await updatePortal(name, { region }), p4.log.success(`${name} now uses the SSO region ${ui2.accent(region)}`);
|
|
7699
|
+
}
|
|
7700
|
+
changed = !0;
|
|
7701
|
+
}
|
|
7702
|
+
return changed && offerLogin && await ask(p4.confirm({ message: `Log in to ${name} now to check it works?` })) && await ssoLogin({ ssoSession: name }, { label: name, loginKey: name, interactive: !0 }), { changed };
|
|
7703
|
+
}, fixSsoRegion = async (session, loginError) => {
|
|
7704
|
+
let current = session.sso_region, detected = await aws.detectSsoRegion(session.sso_start_url), region = null;
|
|
7705
|
+
if (detected && detected != current ? (p4.log.warn(`${session.name} is set to the SSO region ${current}, but its portal is in ${detected}.`), await ask(p4.confirm({ message: `Change ${session.name} to ${detected} and log in again?` })) && (region = detected)) : p4.log.warn(`AWS rejected the SSO region of ${session.name} (${current}). Pick the region where IAM Identity Center lives (Identity Center console \u203A Settings).`), region || (region = await chooseRegion(`SSO region of ${session.name} ${ui2.dim("(where IAM Identity Center lives)")}`, detected || current)), region == current)
|
|
7706
|
+
throw loginError;
|
|
7707
|
+
return await updatePortal(session.name, { region }), region;
|
|
7708
|
+
};
|
|
7709
|
+
module2.exports = {
|
|
7710
|
+
validateStartUrl,
|
|
7711
|
+
validateRegion,
|
|
7712
|
+
listPortals,
|
|
7713
|
+
findPortal,
|
|
7714
|
+
regionMismatch,
|
|
7715
|
+
portalHint,
|
|
7716
|
+
updatePortal,
|
|
7717
|
+
editPortal,
|
|
7718
|
+
fixSsoRegion
|
|
7719
|
+
};
|
|
7720
|
+
}
|
|
7721
|
+
});
|
|
7722
|
+
|
|
7571
7723
|
// src/home.js
|
|
7572
7724
|
var require_home = __commonJS({
|
|
7573
7725
|
"src/home.js"(exports2, module2) {
|
|
@@ -7632,10 +7784,63 @@ ${gray(S_BAR2)}`;
|
|
|
7632
7784
|
}
|
|
7633
7785
|
});
|
|
7634
7786
|
|
|
7787
|
+
// src/commands/recovery.js
|
|
7788
|
+
var require_recovery = __commonJS({
|
|
7789
|
+
"src/commands/recovery.js"(exports2, module2) {
|
|
7790
|
+
var p4 = (init_dist4(), __toCommonJS(dist_exports2)), aws = require_aws(), transforms = require_transforms(), ui2 = require_ui(), log3 = require_log(), { CancelError: CancelError2 } = require_common(), portals = require_portals(), portalOfError = (err, profiles, sessions) => {
|
|
7791
|
+
let target = err && err.target || {}, name = target.ssoSession || target.profile && ((profiles || []).find((x2) => x2.name == target.profile) || {}).sso_session, session = name && (sessions || []).find((s) => s.name == name);
|
|
7792
|
+
return session ? { name: session.name, startUrl: session.sso_start_url || null, region: session.sso_region || null } : null;
|
|
7793
|
+
}, recoveryOptions = ({ portal, wrongRegion, detectedRegion }) => [
|
|
7794
|
+
portal && wrongRegion && portals.regionMismatch(portal.region, detectedRegion) ? { value: "fix-region", label: `Change the SSO region of ${portal.name} to ${detectedRegion}`, hint: `currently ${portal.region}` } : null,
|
|
7795
|
+
portal ? { value: "edit-portal", label: `Edit the portal ${portal.name}`, hint: "start URL and SSO region" } : null,
|
|
7796
|
+
{ value: "retry", label: "Try again" },
|
|
7797
|
+
{ value: "back", label: "Back to the home screen" }
|
|
7798
|
+
].filter(Boolean), errorLines = (err, { portal, detectedRegion }) => [
|
|
7799
|
+
ui2.strong(err.message || String(err)),
|
|
7800
|
+
err.hint ? `
|
|
7801
|
+
${err.hint}` : null,
|
|
7802
|
+
portal ? `
|
|
7803
|
+
${ui2.dim("Portal ")} ${portal.name} ${ui2.dim(portal.startUrl || "")}` : null,
|
|
7804
|
+
portal ? `${ui2.dim("SSO region")} ${portal.region || "?"}${portals.regionMismatch(portal.region, detectedRegion) ? ` ${ui2.warn(`${ui2.sym.warn} its portal page says ${detectedRegion}`)}` : detectedRegion ? ` ${ui2.dim("(matches its portal page)")}` : ""}` : null,
|
|
7805
|
+
`
|
|
7806
|
+
${ui2.dim(`Details: ${log3.displayPath()}`)}`
|
|
7807
|
+
].filter((v) => v !== null).join(`
|
|
7808
|
+
`), recover = async (err, state, what) => {
|
|
7809
|
+
log3.error(`${what} failed (error screen)`, err);
|
|
7810
|
+
let [, configStr] = await aws.getConfigFile(), portal = portalOfError(err, state.profiles, transforms.listSsoSessions(configStr || "")), detectedRegion = portal && err.wrongRegion && portal.startUrl ? await aws.detectSsoRegion(portal.startUrl) : null;
|
|
7811
|
+
p4.log.error(errorLines(err, { portal, detectedRegion }));
|
|
7812
|
+
let choice = await p4.select({ message: "What now?", options: recoveryOptions({ portal, wrongRegion: err.wrongRegion, detectedRegion }) });
|
|
7813
|
+
if (p4.isCancel(choice) || choice == "back")
|
|
7814
|
+
return "back";
|
|
7815
|
+
try {
|
|
7816
|
+
if (choice == "fix-region")
|
|
7817
|
+
await portals.updatePortal(portal.name, { region: detectedRegion }), p4.log.success(`${portal.name} now uses the SSO region ${ui2.accent(detectedRegion)}. Trying again\u2026`);
|
|
7818
|
+
else if (choice == "edit-portal") {
|
|
7819
|
+
let { changed } = await portals.editPortal(portal.name, state, { offerLogin: !1, detectedRegion: err.wrongRegion ? detectedRegion : void 0 });
|
|
7820
|
+
if (!changed)
|
|
7821
|
+
return "back";
|
|
7822
|
+
}
|
|
7823
|
+
} catch (e) {
|
|
7824
|
+
if (e instanceof CancelError2)
|
|
7825
|
+
return "back";
|
|
7826
|
+
if (!(e instanceof ui2.CliError))
|
|
7827
|
+
throw e;
|
|
7828
|
+
return log3.error("fixing the portal failed", e), ui2.printError(e), "back";
|
|
7829
|
+
}
|
|
7830
|
+
return "retry";
|
|
7831
|
+
};
|
|
7832
|
+
module2.exports = {
|
|
7833
|
+
portalOfError,
|
|
7834
|
+
recoveryOptions,
|
|
7835
|
+
recover
|
|
7836
|
+
};
|
|
7837
|
+
}
|
|
7838
|
+
});
|
|
7839
|
+
|
|
7635
7840
|
// src/commands/switch.js
|
|
7636
7841
|
var require_switch = __commonJS({
|
|
7637
7842
|
"src/commands/switch.js"(exports2, module2) {
|
|
7638
|
-
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 } = {}) => {
|
|
7843
|
+
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(), { recover } = require_recovery(), header = async (state, { warnings } = {}) => {
|
|
7639
7844
|
p4.intro(`${ui2.badge("switch-profile")} ${ui2.dim(settings2.CLI_VERSION)}`);
|
|
7640
7845
|
let lines = [];
|
|
7641
7846
|
if (state.defaultProfile) {
|
|
@@ -7661,6 +7866,8 @@ ${ui2.dim(viaShortcut ? "Switching clears them from this terminal." : `Unset ${s
|
|
|
7661
7866
|
}, ACTIONS = [
|
|
7662
7867
|
{ key: "login", label: "Log in" },
|
|
7663
7868
|
{ key: "add", label: "Add" },
|
|
7869
|
+
{ key: "portals", label: "Portals" },
|
|
7870
|
+
{ key: "rename", label: "Rename" },
|
|
7664
7871
|
{ key: "remove", label: "Remove" },
|
|
7665
7872
|
{ key: "logout", label: "Log out" },
|
|
7666
7873
|
{ key: "settings", label: "Settings" }
|
|
@@ -7757,35 +7964,51 @@ ${ui2.dim("Tip: enable per-terminal switching in Settings.")}` : ""}`);
|
|
|
7757
7964
|
${ui2.dim(`Open a new terminal or run: ${installed.reload}`)}`), installed.name == "powershell" && p4.log.message(ui2.dim("If PowerShell refuses to load your profile: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned"));
|
|
7758
7965
|
}, runAction = async (key, state, opts) => {
|
|
7759
7966
|
let commands2 = require_commands();
|
|
7760
|
-
return key == "login" ? commands2.login(state.defaultName, { ...opts, fromPicker: !0 }) : commands2[key](opts.args || [], { ...opts, fromPicker: !0 });
|
|
7967
|
+
return key == "login" ? commands2.login(state.defaultName, { ...opts, fromPicker: !0 }) : key == "rename" ? commands2.rename(null, null, { ...opts, fromPicker: !0 }) : key == "portals" ? commands2.portals(null, { ...opts, fromPicker: !0 }) : commands2[key](opts.args || [], { ...opts, fromPicker: !0 });
|
|
7761
7968
|
}, routeChoice = (choice) => {
|
|
7762
7969
|
if (p4.isCancel(choice))
|
|
7763
7970
|
return { type: "quit" };
|
|
7764
7971
|
let value = String(choice);
|
|
7765
7972
|
return value.startsWith(ACTION_PREFIX) ? { type: "page", key: value.slice(ACTION_PREFIX.length) } : { type: "switch", name: value };
|
|
7766
|
-
}, afterPage = (result, err) => err ? err instanceof CancelError2 ? "back" : err instanceof ui2.CliError ? "error" : "throw" : result && result.switched ? "exit" : "home",
|
|
7767
|
-
let
|
|
7973
|
+
}, afterPage = (result, err) => err ? err instanceof CancelError2 ? "back" : err instanceof ui2.CliError ? "error" : "throw" : result && result.switched ? "exit" : "home", findOrFail = (state, name) => {
|
|
7974
|
+
let profile = state.profiles.find((x2) => x2.name == name);
|
|
7975
|
+
if (!profile)
|
|
7976
|
+
throw new ui2.CliError(`There is no profile named ${name} any more.`, { code: 3 });
|
|
7977
|
+
return profile;
|
|
7978
|
+
}, withRecovery = async (run2, state, what, { exitOnCancel } = {}) => {
|
|
7768
7979
|
for (; ; ) {
|
|
7769
|
-
await header(state, { warnings: first });
|
|
7770
|
-
let route = routeChoice(await pick(state, first ? initialQuery : void 0));
|
|
7771
|
-
if (first = !1, route.type == "quit") {
|
|
7772
|
-
p4.outro(ui2.dim(`Bye ${ui2.unicode ? "\u{1F44B}" : ""}`));
|
|
7773
|
-
return;
|
|
7774
|
-
}
|
|
7775
|
-
if (route.type == "switch")
|
|
7776
|
-
return switchTo(state.profiles.find((x2) => x2.name == route.name), state, opts);
|
|
7777
7980
|
let result, error;
|
|
7778
7981
|
try {
|
|
7779
|
-
result = await
|
|
7982
|
+
result = await run2(state);
|
|
7780
7983
|
} catch (err) {
|
|
7781
7984
|
error = err;
|
|
7782
7985
|
}
|
|
7783
7986
|
let next = afterPage(result, error);
|
|
7784
|
-
if (next == "exit")
|
|
7987
|
+
if (next == "exit" || next == "home")
|
|
7785
7988
|
return result;
|
|
7786
|
-
if (next == "throw")
|
|
7989
|
+
if (next == "throw" || next == "back" && exitOnCancel)
|
|
7787
7990
|
throw error;
|
|
7788
|
-
next == "back"
|
|
7991
|
+
if (next == "back") {
|
|
7992
|
+
p4.cancel(ui2.dim(`${ui2.unicode ? "\u2190" : "<-"} Back`));
|
|
7993
|
+
return;
|
|
7994
|
+
}
|
|
7995
|
+
if (state = await loadState(), await recover(error, state, what) != "retry")
|
|
7996
|
+
return;
|
|
7997
|
+
state = await loadState();
|
|
7998
|
+
}
|
|
7999
|
+
}, homeLoop = async (state, opts, initialQuery) => {
|
|
8000
|
+
let first = !0;
|
|
8001
|
+
for (; ; ) {
|
|
8002
|
+
await header(state, { warnings: first });
|
|
8003
|
+
let route = routeChoice(await pick(state, first ? initialQuery : void 0));
|
|
8004
|
+
if (first = !1, route.type == "quit") {
|
|
8005
|
+
p4.outro(ui2.dim(`Bye ${ui2.unicode ? "\u{1F44B}" : ""}`));
|
|
8006
|
+
return;
|
|
8007
|
+
}
|
|
8008
|
+
let result = route.type == "switch" ? await withRecovery((s) => switchTo(findOrFail(s, route.name), s, opts), state, `switch to ${route.name}`, { exitOnCancel: !0 }) : await withRecovery((s) => runAction(route.key, s, opts), state, route.key);
|
|
8009
|
+
if (afterPage(result) == "exit")
|
|
8010
|
+
return result;
|
|
8011
|
+
state = await loadState();
|
|
7789
8012
|
}
|
|
7790
8013
|
}, switchCommand = async (query, opts) => {
|
|
7791
8014
|
let interactive = ui2.isInteractive(opts);
|
|
@@ -7798,8 +8021,13 @@ ${ui2.dim(`Open a new terminal or run: ${installed.reload}`)}`), installed.name
|
|
|
7798
8021
|
}
|
|
7799
8022
|
if (query) {
|
|
7800
8023
|
let { profile, candidates } = rank.resolveQuery(state.profiles, query, { usage: state.settings.usage, current: state.defaultName });
|
|
7801
|
-
if (profile)
|
|
7802
|
-
|
|
8024
|
+
if (profile) {
|
|
8025
|
+
if (!interactive || opts.json)
|
|
8026
|
+
return switchTo(profile, state, { ...opts, interactive });
|
|
8027
|
+
p4.intro(`${ui2.badge("switch-profile")} ${ui2.dim(settings2.CLI_VERSION)}`);
|
|
8028
|
+
let result = await withRecovery((s) => switchTo(findOrFail(s, profile.name), s, { ...opts, interactive }), state, `switch to ${profile.name}`, { exitOnCancel: !0 });
|
|
8029
|
+
return result || homeLoop(await loadState(), { ...opts, interactive });
|
|
8030
|
+
}
|
|
7803
8031
|
if (!interactive)
|
|
7804
8032
|
throw new ui2.CliError(candidates.length ? `"${query}" matches ${candidates.length} profiles.` : `No profile matches "${query}".`, {
|
|
7805
8033
|
code: 3,
|
|
@@ -7824,7 +8052,7 @@ ${ui2.dim(`Open a new terminal or run: ${installed.reload}`)}`), installed.name
|
|
|
7824
8052
|
// src/commands/index.js
|
|
7825
8053
|
var require_commands = __commonJS({
|
|
7826
8054
|
"src/commands/index.js"(exports2, module2) {
|
|
7827
|
-
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) => {
|
|
8055
|
+
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, chooseRegion } = require_common(), { ssoLogin, rememberLogin } = require_login_flow(), portals = require_portals(), { switchCommand, switchTo, connect, enableShortcut, exportHint } = require_switch(), intro2 = (opts) => {
|
|
7828
8056
|
!opts.fromPicker && !opts.json && p4.intro(`${ui2.badge("switch-profile")} ${ui2.dim(settings2.CLI_VERSION)}`);
|
|
7829
8057
|
}, begin = async (opts, { needsInput } = {}) => {
|
|
7830
8058
|
let interactive = ui2.isInteractive(opts);
|
|
@@ -7879,12 +8107,7 @@ var require_commands = __commonJS({
|
|
|
7879
8107
|
return p4.outro(ui2.dim("Nothing changed."));
|
|
7880
8108
|
}
|
|
7881
8109
|
await aws.logoutAll(), await settings2.update({ logins: {} }), p4.outro(`${ui2.ok(ui2.sym.ok)} Logged out ${ui2.unicode ? "\u{1F44B}" : ""}${isRemoteSession(process.env, process.platform) ? ui2.dim(" This machine no longer has AWS access.") : ""}`);
|
|
7882
|
-
},
|
|
7883
|
-
message,
|
|
7884
|
-
maxItems: 8,
|
|
7885
|
-
initialValue,
|
|
7886
|
-
options: aws.regions.map((r2) => ({ value: r2.code, label: r2.code, hint: r2.name }))
|
|
7887
|
-
})), validateProfileName = (taken) => (value) => {
|
|
8110
|
+
}, validateProfileName = (taken) => (value) => {
|
|
7888
8111
|
if (!value)
|
|
7889
8112
|
return "A name is required.";
|
|
7890
8113
|
if (!/^[a-z0-9][a-z0-9_-]+$/.test(value))
|
|
@@ -7974,7 +8197,7 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7974
8197
|
]
|
|
7975
8198
|
})) : "__new__";
|
|
7976
8199
|
if (choice == "__new__") {
|
|
7977
|
-
let startUrl = await ask(p4.text({ message: "SSO start URL", placeholder: "https://acme.awsapps.com/start", validate:
|
|
8200
|
+
let startUrl = (await ask(p4.text({ message: "SSO start URL", placeholder: "https://acme.awsapps.com/start", validate: portals.validateStartUrl }))).trim(), detected = await aws.detectSsoRegion(startUrl), region2 = await chooseRegion(`SSO region ${ui2.dim(detected ? `(where IAM Identity Center lives: ${detected}, read from the portal)` : "(where IAM Identity Center lives)")}`, detected || "us-east-1"), suggested = transforms.sessionNameFromUrl(startUrl).replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
7978
8201
|
sessionName = await ask(p4.text({ message: `Short name for this portal ${ui2.dim("(usually the client)")}`, initialValue: suggested, validate: (v) => /^[a-zA-Z0-9_-]+$/.test(v || "") ? sessions.some((s) => s.name == v) ? `${v} already exists.` : void 0 : "Letters, numbers, - and _ only." })), configStr = transforms.addSsoSession(configStr, { name: sessionName, startUrl, region: region2, version: settings2.CLI_VERSION }), await aws.writeAwsFile(aws.AWS_CONFIG_FILE, configStr);
|
|
7979
8202
|
} else
|
|
7980
8203
|
sessionName = choice;
|
|
@@ -7983,7 +8206,17 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
7983
8206
|
if (!token) {
|
|
7984
8207
|
if (!interactive)
|
|
7985
8208
|
throw new ui2.CliError(`Not logged in to ${sessionName}.`, { code: 2, hint: `Run: aws sso login --sso-session ${sessionName}` });
|
|
7986
|
-
|
|
8209
|
+
for (; ; )
|
|
8210
|
+
try {
|
|
8211
|
+
await ssoLogin({ ssoSession: sessionName }, { label: sessionName, loginKey: sessionName, interactive });
|
|
8212
|
+
break;
|
|
8213
|
+
} catch (err) {
|
|
8214
|
+
if (!err.wrongRegion)
|
|
8215
|
+
throw err;
|
|
8216
|
+
let region2 = await portals.fixSsoRegion(session, err);
|
|
8217
|
+
configStr = (await aws.getConfigFile())[1] || configStr, session = { ...session, sso_region: region2 };
|
|
8218
|
+
}
|
|
8219
|
+
if (token = await aws.readSsoToken({ ssoSession: sessionName }), !token)
|
|
7987
8220
|
throw new ui2.CliError(`Logged in, but no token was found for ${sessionName}.`);
|
|
7988
8221
|
}
|
|
7989
8222
|
let spin = opts.json ? null : ui2.spinner();
|
|
@@ -8060,6 +8293,90 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
8060
8293
|
if (errors)
|
|
8061
8294
|
throw ui2.cliErrorFrom(errors);
|
|
8062
8295
|
p4.outro(`${ui2.ok(ui2.sym.ok)} Removed ${names.join(", ")}`);
|
|
8296
|
+
}, moveKey = (obj, from, to) => {
|
|
8297
|
+
if (!obj || !(from in obj))
|
|
8298
|
+
return obj;
|
|
8299
|
+
let { [from]: value, ...rest } = obj;
|
|
8300
|
+
return { ...rest, [to]: value };
|
|
8301
|
+
}, rename = async (from, to, opts) => {
|
|
8302
|
+
let interactive = await begin(opts), state = await loadState();
|
|
8303
|
+
if (!from) {
|
|
8304
|
+
if (!interactive)
|
|
8305
|
+
throw new ui2.CliError("Which profile?", { code: 3, hint: "Run: switch-profile rename <profile> <new-name>" });
|
|
8306
|
+
from = await ask(p4.autocomplete({
|
|
8307
|
+
message: "Rename which profile?",
|
|
8308
|
+
maxItems: 12,
|
|
8309
|
+
options: state.profiles.map((x2) => ({ value: x2.name, label: x2.name, hint: describe(x2) }))
|
|
8310
|
+
}));
|
|
8311
|
+
}
|
|
8312
|
+
let profile = findProfile(state, from), taken = [...state.profiles.map((x2) => x2.name).filter((n3) => n3 != from), "default"], validate = (v) => v == from ? "That is already its name." : validateProfileName(taken)(v);
|
|
8313
|
+
if (!to) {
|
|
8314
|
+
if (!interactive)
|
|
8315
|
+
throw new ui2.CliError("What is the new name?", { code: 3, hint: `Run: switch-profile rename ${from} <new-name>` });
|
|
8316
|
+
to = await ask(p4.text({ message: `New name for ${from}`, initialValue: from, validate }));
|
|
8317
|
+
}
|
|
8318
|
+
let invalid = validate(to);
|
|
8319
|
+
if (invalid)
|
|
8320
|
+
throw new ui2.CliError(invalid, { code: 3 });
|
|
8321
|
+
let [errors, result] = await aws.renameProfile(from, to);
|
|
8322
|
+
if (errors)
|
|
8323
|
+
throw ui2.cliErrorFrom(errors);
|
|
8324
|
+
let current = await settings2.read();
|
|
8325
|
+
await settings2.update({
|
|
8326
|
+
usage: moveKey(current.usage, from, to),
|
|
8327
|
+
...loginKey(profile) == from ? { logins: moveKey(current.logins, from, to) } : {}
|
|
8328
|
+
});
|
|
8329
|
+
let inTerminal = process.env.AWS_PROFILE == from, terminal = inTerminal ? await shell.exportProfile(to).catch(log3.tolerated("handing the profile to sp", !1)) : !1;
|
|
8330
|
+
if (opts.json) {
|
|
8331
|
+
process.stdout.write(JSON.stringify({ from, to, default: result.wasDefault, sources: result.sources, processes: result.processes, terminal }) + `
|
|
8332
|
+
`);
|
|
8333
|
+
return;
|
|
8334
|
+
}
|
|
8335
|
+
let lines = [
|
|
8336
|
+
result.wasDefault ? ui2.dim("still the default profile") : null,
|
|
8337
|
+
result.sources.length ? ui2.dim(`source_profile updated in ${result.sources.join(", ")}`) : null,
|
|
8338
|
+
terminal ? ui2.dim("this terminal now uses the new name") : null
|
|
8339
|
+
].filter(Boolean);
|
|
8340
|
+
p4.log.success([`Renamed ${ui2.accent(from)} to ${ui2.accent(to)}`, ...lines].join(`
|
|
8341
|
+
`)), result.processes.length && p4.log.warn(`The credential_process of ${result.processes.join(", ")} still uses --profile ${from}. Update ${result.processes.length == 1 ? "it" : "them"} in ~/.aws/config.`), inTerminal && !terminal && p4.log.warn(`This terminal still uses ${from}. Run:
|
|
8342
|
+
${ui2.accent(exportHint(to))}`), p4.outro(ui2.dim(`Other terminals, scripts and tools that use ${from} (AWS_PROFILE or --profile) need the new name.`));
|
|
8343
|
+
}, portalsCommand = async (name, opts) => {
|
|
8344
|
+
let interactive = await begin(opts), state = await loadState();
|
|
8345
|
+
if (opts.region || opts.startUrl) {
|
|
8346
|
+
if (!name)
|
|
8347
|
+
throw new ui2.CliError("Which portal?", { code: 3, hint: "Run: switch-profile portals <name> --region <region>" });
|
|
8348
|
+
let invalid = opts.region && portals.validateRegion(opts.region) || opts.startUrl && portals.validateStartUrl(opts.startUrl);
|
|
8349
|
+
if (invalid)
|
|
8350
|
+
throw new ui2.CliError(invalid, { code: 3 });
|
|
8351
|
+
await portals.findPortal(state, name), await portals.updatePortal(name, { region: opts.region, startUrl: opts.startUrl });
|
|
8352
|
+
let portal = await portals.findPortal(state, name);
|
|
8353
|
+
return opts.json ? void process.stdout.write(JSON.stringify(portal) + `
|
|
8354
|
+
`) : (p4.log.success(`Saved ${ui2.accent(name)}: ${ui2.join([portal.startUrl, portal.region])}`), p4.outro(ui2.dim(`Log in again: switch-profile login <a ${name} profile>`)));
|
|
8355
|
+
}
|
|
8356
|
+
if (!interactive || opts.json) {
|
|
8357
|
+
let list = await portals.listPortals(state, { detect: !!opts.check }), selected = name ? list.filter((x2) => x2.name == name) : list;
|
|
8358
|
+
if (name && !selected.length && await portals.findPortal(state, name), opts.json)
|
|
8359
|
+
return void process.stdout.write(JSON.stringify({ portals: selected }) + `
|
|
8360
|
+
`);
|
|
8361
|
+
for (let x2 of selected)
|
|
8362
|
+
console.log(`${x2.name} ${x2.region || "-"} ${x2.startUrl || "-"} ${x2.profiles.length} profiles${opts.check ? ` portal says ${x2.detectedRegion || "?"}` : ""}`);
|
|
8363
|
+
return;
|
|
8364
|
+
}
|
|
8365
|
+
if (!name) {
|
|
8366
|
+
let spin = ui2.spinner();
|
|
8367
|
+
spin.start("Reading the portals");
|
|
8368
|
+
let list = await portals.listPortals(state, { detect: !0 });
|
|
8369
|
+
if (spin.stop(`${list.length} SSO portal${list.length == 1 ? "" : "s"}`), !list.length)
|
|
8370
|
+
return p4.outro(ui2.dim("No SSO portals yet. Add one with Add \u203A Accounts from an SSO portal."));
|
|
8371
|
+
let picked = await ask(p4.select({
|
|
8372
|
+
message: "Which portal?",
|
|
8373
|
+
options: list.map((x2) => ({ value: x2.name, label: x2.name, hint: portals.portalHint(x2) }))
|
|
8374
|
+
})), { changed: changed2 } = await portals.editPortal(picked, state, { detectedRegion: list.find((x2) => x2.name == picked).detectedRegion });
|
|
8375
|
+
return p4.outro(ui2.dim(changed2 ? "Saved." : "Nothing changed."));
|
|
8376
|
+
}
|
|
8377
|
+
await portals.findPortal(state, name);
|
|
8378
|
+
let { changed } = await portals.editPortal(name, state);
|
|
8379
|
+
p4.outro(ui2.dim(changed ? "Saved." : "Nothing changed."));
|
|
8063
8380
|
}, LOGIN_MODE_LABELS = {
|
|
8064
8381
|
auto: "Auto: device code over SSH, browser otherwise",
|
|
8065
8382
|
device: "Always device code (approve from any device)",
|
|
@@ -8111,6 +8428,8 @@ AWS recommends SSO or console sign-in instead.`), !await ask(p4.confirm({ messag
|
|
|
8111
8428
|
logout,
|
|
8112
8429
|
add,
|
|
8113
8430
|
remove,
|
|
8431
|
+
rename,
|
|
8432
|
+
portals: portalsCommand,
|
|
8114
8433
|
settings: settingsCommand
|
|
8115
8434
|
};
|
|
8116
8435
|
}
|
|
@@ -8126,6 +8445,8 @@ Examples:
|
|
|
8126
8445
|
$ sp acme prod fuzzy match: switches if only one profile matches
|
|
8127
8446
|
$ sp login log in again now (device code over SSH)
|
|
8128
8447
|
$ sp add import every account of an SSO portal
|
|
8448
|
+
$ sp rename <profile> <new> give a profile a friendlier name
|
|
8449
|
+
$ sp portals check or fix an SSO portal's start URL and region
|
|
8129
8450
|
$ switch-profile status --json
|
|
8130
8451
|
|
|
8131
8452
|
'sp' is the shortcut switch-profile installs on first use (or from Settings). It also
|
|
@@ -8155,6 +8476,6 @@ Exit codes: 0 ok \xB7 1 error \xB7 2 login required \xB7 3 bad input \xB7 130 ca
|
|
|
8155
8476
|
}, main = (argv) => {
|
|
8156
8477
|
log2.start({ version: CLI_VERSION, argv }), process.on("uncaughtException", crashed), process.on("unhandledRejection", crashed), process.on("exit", (code) => log2.info(`exit ${code}`));
|
|
8157
8478
|
let program = new Command();
|
|
8158
|
-
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);
|
|
8479
|
+
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("rename").alias("mv").argument("[profile]").argument("[new-name]").description("rename a profile").action(run(commands.rename)), program.command("portals").alias("portal").argument("[name]", "the [sso-session] to edit").description("list SSO portals, check their region, and edit the start URL or region").option("--region <region>", "set the SSO region (where IAM Identity Center lives)").option("--start-url <url>", "set the start URL").option("--check", "with --json or no terminal: also read each portal's region from its start page").action(run(commands.portals)), 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);
|
|
8159
8480
|
};
|
|
8160
8481
|
module.exports = { main };
|