claude-aws-mfa 1.3.0 → 1.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/CHANGELOG.md CHANGED
@@ -22,6 +22,20 @@ Releases are automated via CI. To create a new release:
22
22
  Stable versions (e.g. `v1.2.0`) publish to npm `@latest`. Pre-release versions
23
23
  (e.g. `v1.2.0-rc.1`) publish to npm `@next` and create a GitHub pre-release.
24
24
 
25
+ ## [v1.5.0] - 2026-10-19
26
+
27
+ ### Added
28
+ - `AWS_REGION`, when set, overrides the configured region for STS and `--usage`, so a report can target a region other than the one in the config.
29
+
30
+ ## [v1.4.0] - 2026-10-19
31
+
32
+ ### Added
33
+ - `--help` / `-h` prints a summary of the CLI options.
34
+ - `--usage[=[<role>/]<session>]` prints Bedrock token usage from model invocation logs for a session name (default: the one in the config), optionally under one role, or itemized per role or session with `*` (`--usage='my-role/*'`, `--usage='*'`), with `--from=`/`--to=` for the time range (default: current month) and `--profile=` to query with a different AWS profile. Each model's estimated cost is shown, at current AWS list prices.
35
+
36
+ ### Changed
37
+ - Updated `@aws-sdk/client-sts` to 3.1152 (shared with the new Bedrock and CloudWatch Logs clients) and `@types/bun` to 1.4.3.
38
+
25
39
  ## [v1.3.0] - 2026-10-14
26
40
 
27
41
  ### Added
package/README.md CHANGED
@@ -39,9 +39,13 @@ Add this (plus any addtitional config you prefer) to your Claude Code settings (
39
39
  | Flag | Description |
40
40
  |------|-------------|
41
41
  | `--setup` | Launch the guided setup wizard |
42
+ | `-h`, `--help` | Show usage help |
42
43
  | `--cache-session` | Cache STS session credentials and reuse them until they expire (default: on) |
43
44
  | `--auto-mfa` | Automatically run the configured MFA command and obtain credentials without showing the dialog, requires MFA Command mode (default: on) |
44
45
  | `--single-instance-lock` | Prevent multiple concurrent instances from showing overlapping dialogs; subsequent invocations wait for the first to finish and reuse its cached session (default: on) |
46
+ | `--usage[=[<role>/]<session>]` | Print Bedrock token usage for a session name (default: the one in the config), optionally under one role; `*` itemizes instead of credentials (see [Usage report](#usage-report)) |
47
+ | `--profile=<name>` | With `--usage`: query using this AWS profile instead of the tool's own credentials |
48
+ | `--from=<date>` / `--to=<date>` | With `--usage`: time range (default: start of the current month to now; `--to` is exclusive) |
45
49
  | `--no-ui` | Never show any GUI dialog. Prints credentials JSON to stdout on success, or an error to stderr on failure — useful for scripting or letting an LLM drive the tool during setup/debugging |
46
50
 
47
51
  ## How it works
@@ -54,6 +58,8 @@ Add this (plus any addtitional config you prefer) to your Claude Code settings (
54
58
  6. Saves configuration (including MFA mode and command) to `~/.config/claude-aws-mfa.json` (mode 0600) for subsequent runs
55
59
  7. Outputs temporary credentials as JSON to stdout
56
60
 
61
+ If `AWS_REGION` is set, it overrides the configured region for all AWS calls (STS and `--usage`); the saved config is left unchanged. When Claude Code runs the tool, it sees whatever `AWS_REGION` Claude Code's environment has (including one set in `settings.json`); when you run it yourself from a shell, it only sees your shell's.
62
+
57
63
  On subsequent runs, all fields are pre-populated from the saved config — just enter a fresh MFA code and hit OK. If you use the command mode, the TOTP code is fetched automatically so no manual entry is needed at all.
58
64
 
59
65
  ### Claude Code profiles
@@ -91,6 +97,34 @@ Claude Code does not surface a failing command's stderr to the user, so when MFA
91
97
 
92
98
  With `--no-ui`, no GUI is ever shown — not even the error dialog above — and the tool never imports the GUI toolkit, so it also works in headless environments without a display. On success, credentials are printed to stdout as usual, keeping stdout cleanly parsable. On failure, an error message is printed to **stderr** instead (e.g. `Missing config: roleArn, mfaCommand` if the config is incomplete, or the underlying STS error if authentication itself failed) and the process exits non-zero. This is intended for scripting and for letting an LLM drive the tool directly while setting up or debugging a configuration — full non-interactive operation requires **MFA Command mode**, since a static "code" mode value can't be re-entered headlessly. The command doesn't need to be a real TOTP generator: for a one-off test or debugging run, `mfaCommand` can simply be `echo <code>` with a fresh, not-yet-used code from your authenticator — it only needs to produce a valid code once.
93
99
 
100
+ ### Usage report
101
+
102
+ `claude-aws-mfa --usage` prints per-model request and token counts (input, cache read/write, output), with an estimated cost, for every Bedrock call made under the configured STS session name.
103
+
104
+ `--usage=` takes `[<role>/]<session>` to choose whose usage to report. Either part can be `*`, which gives one row per role or session (all models merged) instead of one row per model:
105
+
106
+ | Value | Reports |
107
+ |-------|---------|
108
+ | *(none)* | The session name from the config, any role, per model |
109
+ | `claude-aws-mfa` | That session name under any role, per model |
110
+ | `my-role/claude-aws-mfa` | That session of role `my-role`, per model |
111
+ | `my-role/*` | Each session of role `my-role` |
112
+ | `*` | Each role (its sessions merged) |
113
+ | `*/*` | Each role and session pair |
114
+
115
+ ```bash
116
+ claude-aws-mfa --usage # this month, using the tool's own role session
117
+ claude-aws-mfa --usage --from=2026-09-01 --to=2026-10-01 # September
118
+ claude-aws-mfa --usage --profile=billing # query with a different AWS profile
119
+ claude-aws-mfa --usage='my-role/*' # quote * so the shell doesn't expand it
120
+ ```
121
+
122
+ Only callers that assumed a role are counted; calls made directly with IAM user keys never match.
123
+
124
+ Only one region is queried: `AWS_REGION` if set, otherwise the configured region. If your Claude Code `settings.json` sets `AWS_REGION` to something other than the configured region, pass the same value when running `--usage` from a shell (`AWS_REGION=eu-west-1 claude-aws-mfa --usage`) to report on the region Claude Code uses. Cross-region inference calls (`us.`/`global.` profiles) are logged in the region they were made from, so they are included. The data comes from Bedrock [model invocation logs](https://docs.aws.amazon.com/bedrock/latest/userguide/model-invocation-logging.html), so invocation logging to CloudWatch Logs must be enabled in the account and region. The querying credentials need `bedrock:GetModelInvocationLoggingConfiguration`, `logs:StartQuery` and `logs:GetQueryResults`. Without `--profile`, credentials are obtained exactly as for Claude Code (cached session, auto-MFA, or the dialog). Errors go to stderr.
125
+
126
+ Costs are estimated from AWS's public [Price List](https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/price-changes.html) for the region (no extra permissions needed), at current base on-demand rates — `us.`/`eu.` cross-region profiles at their geo price, `global.` at the global price. They exclude negotiated discounts and the long-context and 1-hour-cache-write surcharges, which the aggregated logs can't distinguish, so treat them as an estimate rather than your bill. If prices can't be fetched, token counts are still printed.
127
+
94
128
  ## System requirements
95
129
 
96
130
  The GUI dialog uses [webview-bun](https://github.com/tr1ckydev/webview-bun), which requires:
package/bun.lock CHANGED
@@ -5,11 +5,13 @@
5
5
  "": {
6
6
  "name": "claude-bedrock-mfa",
7
7
  "dependencies": {
8
- "@aws-sdk/client-sts": "^3.998.0",
8
+ "@aws-sdk/client-bedrock": "^3.1152.0",
9
+ "@aws-sdk/client-cloudwatch-logs": "^3.1152.0",
10
+ "@aws-sdk/client-sts": "^3.1152.0",
9
11
  "webview-bun": "^2.4.0",
10
12
  },
11
13
  "devDependencies": {
12
- "@types/bun": "latest",
14
+ "@types/bun": "^1.4.3",
13
15
  "node-screenshots": "^0.2.8",
14
16
  },
15
17
  "peerDependencies": {
@@ -18,151 +20,61 @@
18
20
  },
19
21
  },
20
22
  "packages": {
21
- "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
23
+ "@aws-sdk/client-bedrock": ["@aws-sdk/client-bedrock@3.1152.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/credential-provider-node": "^3.972.86", "@aws-sdk/token-providers": "3.1152.0", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-mZQk6H/Gt34V5LWDJfSf4Ar/FMaUrGwu9TOvstKP7GNo81PJJOkgS+ICjhoV3YX/AZXjZD8mNCp+MYynRitRag=="],
22
24
 
23
- "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
25
+ "@aws-sdk/client-cloudwatch-logs": ["@aws-sdk/client-cloudwatch-logs@3.1152.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/credential-provider-node": "^3.972.86", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-626LS/9YffzAUH68bvnyNwXo/WYtShhs7+X6kEH5ARTom1sSk3hHJmR9F+1zc50mHSfJNwYEbUNDmgC5vW4/Xg=="],
24
26
 
25
- "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="],
27
+ "@aws-sdk/client-sts": ["@aws-sdk/client-sts@3.1152.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/credential-provider-node": "^3.972.86", "@aws-sdk/signature-v4-multi-region": "^3.996.48", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-dLvk02UzZPRTeLLh8C86OpJBRzsJZ+fSxGqqdzoWOXACwZOf3xH6d4rQn54eZNPKYWaQNlt4VmfA5mZsfItbgw=="],
26
28
 
27
- "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
29
+ "@aws-sdk/core": ["@aws-sdk/core@3.978.3", "", { "dependencies": { "@aws-sdk/types": "^3.974.6", "@aws-sdk/xml-builder": "^3.972.41", "@aws/lambda-invoke-store": "^0.3.0", "@smithy/core": "^3.35.2", "@smithy/signature-v4": "^5.7.4", "@smithy/types": "^4.19.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-nslpzUCTI9b2IQMWSd6mSAXldBexPc8rr0d3dXUutXYFLFaTdsHa2GD7sJdsn3PhyjK0O5K8ypd6zi+RXXPVlg=="],
28
30
 
29
- "@aws-sdk/client-sts": ["@aws-sdk/client-sts@3.998.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.14", "@aws-sdk/credential-provider-node": "^3.972.13", "@aws-sdk/middleware-host-header": "^3.972.5", "@aws-sdk/middleware-logger": "^3.972.5", "@aws-sdk/middleware-recursion-detection": "^3.972.5", "@aws-sdk/middleware-user-agent": "^3.972.14", "@aws-sdk/region-config-resolver": "^3.972.5", "@aws-sdk/types": "^3.973.3", "@aws-sdk/util-endpoints": "^3.996.2", "@aws-sdk/util-user-agent-browser": "^3.972.5", "@aws-sdk/util-user-agent-node": "^3.972.13", "@smithy/config-resolver": "^4.4.9", "@smithy/core": "^3.23.6", "@smithy/fetch-http-handler": "^5.3.11", "@smithy/hash-node": "^4.2.10", "@smithy/invalid-dependency": "^4.2.10", "@smithy/middleware-content-length": "^4.2.10", "@smithy/middleware-endpoint": "^4.4.20", "@smithy/middleware-retry": "^4.4.37", "@smithy/middleware-serde": "^4.2.11", "@smithy/middleware-stack": "^4.2.10", "@smithy/node-config-provider": "^4.3.10", "@smithy/node-http-handler": "^4.4.12", "@smithy/protocol-http": "^5.3.10", "@smithy/smithy-client": "^4.12.0", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.10", "@smithy/util-base64": "^4.3.1", "@smithy/util-body-length-browser": "^4.2.1", "@smithy/util-body-length-node": "^4.2.2", "@smithy/util-defaults-mode-browser": "^4.3.36", "@smithy/util-defaults-mode-node": "^4.2.39", "@smithy/util-endpoints": "^3.3.1", "@smithy/util-middleware": "^4.2.10", "@smithy/util-retry": "^4.2.10", "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-2KI4ZtA0ilFfjBk2nPC6F0TEnzSKvbZbuYF6fguPa4po5DaNeheASpmWrXqdIte4orbNJU+Du2pSanxpHxDhvg=="],
31
+ "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.74", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-rsRJcW2iFUhCgclyD6OSbMEaPl8Eu0qSeRZb2uuCK1ViZ66hl5wOe/p6Vr6MO3b+ydyKgFeqza2p6tZThvILlw=="],
30
32
 
31
- "@aws-sdk/core": ["@aws-sdk/core@3.973.14", "", { "dependencies": { "@aws-sdk/types": "^3.973.3", "@aws-sdk/xml-builder": "^3.972.7", "@smithy/core": "^3.23.6", "@smithy/node-config-provider": "^4.3.10", "@smithy/property-provider": "^4.2.10", "@smithy/protocol-http": "^5.3.10", "@smithy/signature-v4": "^5.3.10", "@smithy/smithy-client": "^4.12.0", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.1", "@smithy/util-middleware": "^4.2.10", "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-iAQ1jIGESTVjoqNNY9VlsE9FnCz+Hc8s+dgurF6WrgFyVIw+uggH+V102RFhwjRv4dLSSLfzjDwvQnLszov7TQ=="],
33
+ "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.76", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-pnKdh0Br7OJPj+7qKjzgTfDTFW0RhEMKXNDR2jv9CAM4oHuG/4z3hRh8KtutLfpmItuRtDbKBmwWkDqHwwAWXw=="],
32
34
 
33
- "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.12", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/types": "^3.973.3", "@smithy/property-provider": "^4.2.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-WPtj/iAYHHd+NDM6AZoilZwUz0nMaPxbTPGLA7nhyIYRZN2L8trqfbNvm7g/Jr3gzfKp1LpO6AtBTnrhz9WW2g=="],
35
+ "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.973.19", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/credential-provider-env": "^3.972.74", "@aws-sdk/credential-provider-http": "^3.972.76", "@aws-sdk/credential-provider-login": "^3.972.81", "@aws-sdk/credential-provider-process": "^3.972.74", "@aws-sdk/credential-provider-sso": "^3.973.18", "@aws-sdk/credential-provider-web-identity": "^3.972.80", "@aws-sdk/nested-clients": "^3.997.48", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-kcjvAxqQEV8yuwkX58RelNn3ne65o7JgRWf3Z/gVvbi7Xuw1qSDLJlatrLD76+40cmRxmNkLxBoyp/vAQChQ5g=="],
34
36
 
35
- "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.14", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/types": "^3.973.3", "@smithy/fetch-http-handler": "^5.3.11", "@smithy/node-http-handler": "^4.4.12", "@smithy/property-provider": "^4.2.10", "@smithy/protocol-http": "^5.3.10", "@smithy/smithy-client": "^4.12.0", "@smithy/types": "^4.13.0", "@smithy/util-stream": "^4.5.15", "tslib": "^2.6.2" } }, "sha512-umtjCicH2o/Fcc8Fu1562UkDyt6gql4czTYVlUfHfAM8S4QEKggzmtHYYYpPfQcjFj1ajyy68ahYSuF67x4ptQ=="],
37
+ "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.81", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/nested-clients": "^3.997.48", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-CeQHSbE2CnVdaHIJ/NayRaCecIER+Pm9faUPkmRATO54rCoMTEPSbVieGHzEwvm1dZM2HIs6qQZCznhayNTWJQ=="],
36
38
 
37
- "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.12", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/credential-provider-env": "^3.972.12", "@aws-sdk/credential-provider-http": "^3.972.14", "@aws-sdk/credential-provider-login": "^3.972.12", "@aws-sdk/credential-provider-process": "^3.972.12", "@aws-sdk/credential-provider-sso": "^3.972.12", "@aws-sdk/credential-provider-web-identity": "^3.972.12", "@aws-sdk/nested-clients": "^3.996.2", "@aws-sdk/types": "^3.973.3", "@smithy/credential-provider-imds": "^4.2.10", "@smithy/property-provider": "^4.2.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-qjzgnMl6GIBbVeK74jBqSF07+s6kyeZl5R88qjMs302JlqkxE57jkvflDmZ9I017ffEWqIUa9/M4Hfp28qyu1g=="],
39
+ "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.86", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.74", "@aws-sdk/credential-provider-http": "^3.972.76", "@aws-sdk/credential-provider-ini": "^3.973.19", "@aws-sdk/credential-provider-process": "^3.972.74", "@aws-sdk/credential-provider-sso": "^3.973.18", "@aws-sdk/credential-provider-web-identity": "^3.972.80", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/credential-provider-imds": "^4.5.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-WNttSyr6e8innPtBSty//CdnqJcgZN6eAbtw3v/CR2IObz6MYZEo8eusdtuCHJ2ujwFqC2bSPHVyhXGHx8HruQ=="],
38
40
 
39
- "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.12", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/nested-clients": "^3.996.2", "@aws-sdk/types": "^3.973.3", "@smithy/property-provider": "^4.2.10", "@smithy/protocol-http": "^5.3.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-AO57y46PzG24bJzxWLk+FYJG6MzxvXoFXnOKnmKUGV43ub4/FS/4Rz7zCC6ThqUotgqEFd30l5LTAd65RP65pg=="],
41
+ "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.74", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-Yj6kZFJQdbR+PygTkqdVLPHHpdDpOS9GCYVcDCuweCRRig9LF24ELQBMfyUw1vspvrZJUfnOGVyMjhaL094ahw=="],
40
42
 
41
- "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.13", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.12", "@aws-sdk/credential-provider-http": "^3.972.14", "@aws-sdk/credential-provider-ini": "^3.972.12", "@aws-sdk/credential-provider-process": "^3.972.12", "@aws-sdk/credential-provider-sso": "^3.972.12", "@aws-sdk/credential-provider-web-identity": "^3.972.12", "@aws-sdk/types": "^3.973.3", "@smithy/credential-provider-imds": "^4.2.10", "@smithy/property-provider": "^4.2.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-ME2sgus+gFRtiudy5Xqj9iT/tj8lHOIGrFgktuO5skJU4EngOvTZ1Hpj8mknrW4FgWXmpWhc88NtEscUuuDpKw=="],
43
+ "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.973.18", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/nested-clients": "^3.997.48", "@aws-sdk/token-providers": "3.1150.0", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-FnpPhdY4qUGtX/8X6eKxh+uD+9Y19i9FYibgk4SBe0M935s9vocHkHzaxaMNk+YkHmvzgdu8cHA5SCoeU+kkew=="],
42
44
 
43
- "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.12", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/types": "^3.973.3", "@smithy/property-provider": "^4.2.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-msxrHBpVP5AOIDohNPCINUtL47f7XI1TEru3N13uM3nWUMvIRA1vFa8Tlxbxm1EntPPvLAxRmvE5EbjDjOZkbw=="],
45
+ "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.80", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/nested-clients": "^3.997.48", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-TJF25Ndwy2HxcVj6oxMgp8qVlJAawlExHYBXqhuC6Zt2m+A7WpdkjBD6QglNbiArlteYljZHEY3R7KKmno6ISA=="],
44
46
 
45
- "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.12", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/nested-clients": "^3.996.2", "@aws-sdk/token-providers": "3.998.0", "@aws-sdk/types": "^3.973.3", "@smithy/property-provider": "^4.2.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-D5iC5546hJyhobJN0szOT4KVeJQ8z/meZq2B3lEDZFcvHONKw+tzq36DAJUy3qLTueeB2geSxiHXngQlA11eoA=="],
47
+ "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.48", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/signature-v4-multi-region": "^3.996.48", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/fetch-http-handler": "^5.8.0", "@smithy/node-http-handler": "^4.12.1", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-B01jdXJtAW/XOiUzGdI2mz67WyEHbGEc9X6Euzw3bjLAa+txTqdZDER4X2FsIEcXzV2ABqudVZ5thali8tCCzQ=="],
46
48
 
47
- "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.12", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/nested-clients": "^3.996.2", "@aws-sdk/types": "^3.973.3", "@smithy/property-provider": "^4.2.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-yluBahBVsduoA/zgV0NAXtwwXvQ6tNn95dNA3Hg+vISdiPWA46QY0d9PLO2KpNbjtm+1oGcWxemS4fYTwJ0W1w=="],
49
+ "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.48", "", { "dependencies": { "@aws-sdk/types": "^3.974.6", "@smithy/signature-v4": "^5.7.4", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-cFDXfEbXaGwbnpvR/DyqfxfjxpjXVRVthQLgkwvcfDS5u3+8G9WSj1X1Ag/pTRg/QOeOno1Tq3ycEdrKsCUcOA=="],
48
50
 
49
- "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.3", "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-dVA0m1cEQ2iA6yB19aHvWNeUVTuvTt3AXzT0aiIu2uxk0S7AcmwDCDaRgYa/v+eFHcJVxEnpYTozqA7X62xinw=="],
51
+ "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1152.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/nested-clients": "^3.997.48", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-XcQlQFkIDOk4P1dF/ADQAjPK361mBChR/AJghM4my6SgDnsOX77Z7zCUtZfXXbQG4E+vpoWfbySzX/oKXcmV5w=="],
50
52
 
51
- "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.3", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-03RqplLZjUTkYi0dDPR/bbOLnDLFNdaVvNENgA3XK7Ph1MhEBhUYlgoGfOyRAKApDZ+WG4ykOoA8jI8J04jmFA=="],
53
+ "@aws-sdk/types": ["@aws-sdk/types@3.974.6", "", { "dependencies": { "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg=="],
52
54
 
53
- "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.3", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-2QSuuVkpHTe84+mDdnFjHX8rAP3g0yYwLVAhS3lQN1rW5Z/zNsf8/pYQrLjLO4n4sPCsUAkTa0Vrod0lk+o1Tg=="],
55
+ "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.41", "", { "dependencies": { "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ=="],
54
56
 
55
- "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.14", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/types": "^3.973.3", "@aws-sdk/util-endpoints": "^3.996.2", "@smithy/core": "^3.23.6", "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-PzDz+yRAQuIzd+4ZY3s6/TYRzlNKAn4Gae3E5uLV7NnYHqrZHFoAfKE4beXcu3C51pA2/FQ3X2qOGSYqUoN1WQ=="],
57
+ "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.3.0", "", {}, "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ=="],
56
58
 
57
- "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.2", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.14", "@aws-sdk/middleware-host-header": "^3.972.5", "@aws-sdk/middleware-logger": "^3.972.5", "@aws-sdk/middleware-recursion-detection": "^3.972.5", "@aws-sdk/middleware-user-agent": "^3.972.14", "@aws-sdk/region-config-resolver": "^3.972.5", "@aws-sdk/types": "^3.973.3", "@aws-sdk/util-endpoints": "^3.996.2", "@aws-sdk/util-user-agent-browser": "^3.972.5", "@aws-sdk/util-user-agent-node": "^3.972.13", "@smithy/config-resolver": "^4.4.9", "@smithy/core": "^3.23.6", "@smithy/fetch-http-handler": "^5.3.11", "@smithy/hash-node": "^4.2.10", "@smithy/invalid-dependency": "^4.2.10", "@smithy/middleware-content-length": "^4.2.10", "@smithy/middleware-endpoint": "^4.4.20", "@smithy/middleware-retry": "^4.4.37", "@smithy/middleware-serde": "^4.2.11", "@smithy/middleware-stack": "^4.2.10", "@smithy/node-config-provider": "^4.3.10", "@smithy/node-http-handler": "^4.4.12", "@smithy/protocol-http": "^5.3.10", "@smithy/smithy-client": "^4.12.0", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.10", "@smithy/util-base64": "^4.3.1", "@smithy/util-body-length-browser": "^4.2.1", "@smithy/util-body-length-node": "^4.2.2", "@smithy/util-defaults-mode-browser": "^4.3.36", "@smithy/util-defaults-mode-node": "^4.2.39", "@smithy/util-endpoints": "^3.3.1", "@smithy/util-middleware": "^4.2.10", "@smithy/util-retry": "^4.2.10", "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-W+u6EM8WRxOIhAhR2mXMHSaUygqItpTehkgxLwJngXqr9RlAR4t6CtECH7o7QK0ct3oyi5Z8ViDHtPbel+D2Rg=="],
59
+ "@smithy/core": ["@smithy/core@3.36.0", "", { "dependencies": { "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-3hJiwG1F5t1SoXqp/yOCmYXziyx7ZVqF63S5W6YU201e/qwVaHPex+E/3vvyY9a3ORq/b+BydeNZpP2saQ9X2g=="],
58
60
 
59
- "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.3", "@smithy/config-resolver": "^4.4.9", "@smithy/node-config-provider": "^4.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-AOitrygDwfTNCLCW7L+GScDy1p49FZ6WutTUFWROouoPetfVNmpL4q8TWD3MhfY/ynhoGhleUQENrBH374EU8w=="],
61
+ "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.5.2", "", { "dependencies": { "@smithy/core": "^3.33.2", "@smithy/types": "^4.17.2", "tslib": "^2.6.2" } }, "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg=="],
60
62
 
61
- "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.998.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.14", "@aws-sdk/nested-clients": "^3.996.2", "@aws-sdk/types": "^3.973.3", "@smithy/property-provider": "^4.2.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-JFzi44tQnENZQ+1DYcHfoa/wTRKkccz0VsNMow0rvsxZtqUEkeV2pYFbir35mHTyUKju9995ay1MAGxLt1dpRA=="],
63
+ "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.8.0", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA=="],
62
64
 
63
- "@aws-sdk/types": ["@aws-sdk/types@3.973.3", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-tma6D8/xHZHJEUqmr6ksZjZ0onyIUqKDQLyp50ttZJmS0IwFYzxBgp5CxFvpYAnah52V3UtgrqGA6E83gtT7NQ=="],
65
+ "@smithy/node-http-handler": ["@smithy/node-http-handler@4.12.1", "", { "dependencies": { "@smithy/core": "^3.33.3", "@smithy/types": "^4.18.0", "tslib": "^2.6.2" } }, "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw=="],
64
66
 
65
- "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.2", "", { "dependencies": { "@aws-sdk/types": "^3.973.3", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.10", "@smithy/util-endpoints": "^3.3.1", "tslib": "^2.6.2" } }, "sha512-83E6T1CKi0/IozPzqRBKqduW0mS4UQdI3soBH6CG7UgupTADWunqEMOTuPWCs9XGjpJJ4ujj+yu7pn8svhp5yg=="],
67
+ "@smithy/signature-v4": ["@smithy/signature-v4@5.7.4", "", { "dependencies": { "@smithy/core": "^3.35.0", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ=="],
66
68
 
67
- "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.965.4", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog=="],
69
+ "@smithy/types": ["@smithy/types@4.19.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q=="],
68
70
 
69
- "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.5", "", { "dependencies": { "@aws-sdk/types": "^3.973.3", "@smithy/types": "^4.13.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-2ja1WqtuBaEAMgVoHYuWx393DF6ULqdt3OozeO7BosqouYaoU47Adtp9vEF+GImSG/Q8A+dqfwDULTTdMkHGUQ=="],
70
-
71
- "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.972.13", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.14", "@aws-sdk/types": "^3.973.3", "@smithy/node-config-provider": "^4.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-PHErmuu+v6iAST48zcsB2cYwDKW45gk6qCp49t1p0NGZ4EaFPr/tA5jl0X/ekDwvWbuT0LTj++fjjdVQAbuh0Q=="],
72
-
73
- "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.7", "", { "dependencies": { "@smithy/types": "^4.13.0", "fast-xml-parser": "5.3.6", "tslib": "^2.6.2" } }, "sha512-9GF86s6mHuc1TYCbuKatMDWl2PyK3KIkpRaI7ul2/gYZPfaLzKZ+ISHhxzVb9KVeakf75tUQe6CXW2gugSCXNw=="],
74
-
75
- "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.3", "", {}, "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw=="],
76
-
77
- "@smithy/abort-controller": ["@smithy/abort-controller@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-qocxM/X4XGATqQtUkbE9SPUB6wekBi+FyJOMbPj0AhvyvFGYEmOlz6VB22iMePCQsFmMIvFSeViDvA7mZJG47g=="],
78
-
79
- "@smithy/config-resolver": ["@smithy/config-resolver@4.4.9", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.10", "@smithy/types": "^4.13.0", "@smithy/util-config-provider": "^4.2.1", "@smithy/util-endpoints": "^3.3.1", "@smithy/util-middleware": "^4.2.10", "tslib": "^2.6.2" } }, "sha512-ejQvXqlcU30h7liR9fXtj7PIAau1t/sFbJpgWPfiYDs7zd16jpH0IsSXKcba2jF6ChTXvIjACs27kNMc5xxE2Q=="],
80
-
81
- "@smithy/core": ["@smithy/core@3.23.6", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.11", "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.1", "@smithy/util-body-length-browser": "^4.2.1", "@smithy/util-middleware": "^4.2.10", "@smithy/util-stream": "^4.5.15", "@smithy/util-utf8": "^4.2.1", "@smithy/uuid": "^1.1.1", "tslib": "^2.6.2" } }, "sha512-4xE+0L2NrsFKpEVFlFELkIHQddBvMbQ41LRIP74dGCXnY1zQ9DgksrBcRBDJT+iOzGy4VEJIeU3hkUK5mn06kg=="],
82
-
83
- "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.10", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.10", "@smithy/property-provider": "^4.2.10", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.10", "tslib": "^2.6.2" } }, "sha512-3bsMLJJLTZGZqVGGeBVFfLzuRulVsGTj12BzRKODTHqUABpIr0jMN1vN3+u6r2OfyhAQ2pXaMZWX/swBK5I6PQ=="],
84
-
85
- "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.11", "", { "dependencies": { "@smithy/protocol-http": "^5.3.10", "@smithy/querystring-builder": "^4.2.10", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-wbTRjOxdFuyEg0CpumjZO0hkUl+fetJFqxNROepuLIoijQh51aMBmzFLfoQdwRjxsuuS2jizzIUTjPWgd8pd7g=="],
86
-
87
- "@smithy/hash-node": ["@smithy/hash-node@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-buffer-from": "^4.2.1", "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-1VzIOI5CcsvMDvP3iv1vG/RfLJVVVc67dCRyLSB2Hn9SWCZrDO3zvcIzj3BfEtqRW5kcMg5KAeVf1K3dR6nD3w=="],
88
-
89
- "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-vy9KPNSFUU0ajFYk0sDZIYiUlAWGEAhRfehIr5ZkdFrRFTAuXEPUd41USuqHU6vvLX4r6Q9X7MKBco5+Il0Org=="],
90
-
91
- "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q=="],
92
-
93
- "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.10", "", { "dependencies": { "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-TQZ9kX5c6XbjhaEBpvhSvMEZ0klBs1CFtOdPFwATZSbC9UeQfKHPLPN9Y+I6wZGMOavlYTOlHEPDrt42PMSH9w=="],
94
-
95
- "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.20", "", { "dependencies": { "@smithy/core": "^3.23.6", "@smithy/middleware-serde": "^4.2.11", "@smithy/node-config-provider": "^4.3.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.10", "@smithy/util-middleware": "^4.2.10", "tslib": "^2.6.2" } }, "sha512-9W6Np4ceBP3XCYAGLoMCmn8t2RRVzuD1ndWPLBbv7H9CrwM9Bprf6Up6BM9ZA/3alodg0b7Kf6ftBK9R1N04vw=="],
96
-
97
- "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.37", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.10", "@smithy/protocol-http": "^5.3.10", "@smithy/service-error-classification": "^4.2.10", "@smithy/smithy-client": "^4.12.0", "@smithy/types": "^4.13.0", "@smithy/util-middleware": "^4.2.10", "@smithy/util-retry": "^4.2.10", "@smithy/uuid": "^1.1.1", "tslib": "^2.6.2" } }, "sha512-/1psZZllBBSQ7+qo5+hhLz7AEPGLx3Z0+e3ramMBEuPK2PfvLK4SrncDB9VegX5mBn+oP/UTDrM6IHrFjvX1ZA=="],
98
-
99
- "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.11", "", { "dependencies": { "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-STQdONGPwbbC7cusL60s7vOa6He6A9w2jWhoapL0mgVjmR19pr26slV+yoSP76SIssMTX/95e5nOZ6UQv6jolg=="],
100
-
101
- "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-pmts/WovNcE/tlyHa8z/groPeOtqtEpp61q3W0nW1nDJuMq/x+hWa/OVQBtgU0tBqupeXq0VBOLA4UZwE8I0YA=="],
102
-
103
- "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.10", "", { "dependencies": { "@smithy/property-provider": "^4.2.10", "@smithy/shared-ini-file-loader": "^4.4.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-UALRbJtVX34AdP2VECKVlnNgidLHA2A7YgcJzwSBg1hzmnO/bZBHl/LDQQyYifzUwp1UOODnl9JJ3KNawpUJ9w=="],
104
-
105
- "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.12", "", { "dependencies": { "@smithy/abort-controller": "^4.2.10", "@smithy/protocol-http": "^5.3.10", "@smithy/querystring-builder": "^4.2.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-zo1+WKJkR9x7ZtMeMDAAsq2PufwiLDmkhcjpWPRRkmeIuOm6nq1qjFICSZbnjBvD09ei8KMo26BWxsu2BUU+5w=="],
106
-
107
- "@smithy/property-provider": ["@smithy/property-provider@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-5jm60P0CU7tom0eNrZ7YrkgBaoLFXzmqB0wVS+4uK8PPGmosSrLNf6rRd50UBvukztawZ7zyA8TxlrKpF5z9jw=="],
108
-
109
- "@smithy/protocol-http": ["@smithy/protocol-http@5.3.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-2NzVWpYY0tRdfeCJLsgrR89KE3NTWT2wGulhNUxYlRmtRmPwLQwKzhrfVaiNlA9ZpJvbW7cjTVChYKgnkqXj1A=="],
110
-
111
- "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-uri-escape": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-HeN7kEvuzO2DmAzLukE9UryiUvejD3tMp9a1D1NJETerIfKobBUCLfviP6QEk500166eD2IATaXM59qgUI+YDA=="],
112
-
113
- "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-4Mh18J26+ao1oX5wXJfWlTT+Q1OpDR8ssiC9PDOuEgVBGloqg18Fw7h5Ct8DyT9NBYwJgtJ2nLjKKFU6RP1G1Q=="],
114
-
115
- "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0" } }, "sha512-0R/+/Il5y8nB/By90o8hy/bWVYptbIfvoTYad0igYQO5RefhNCDmNzqxaMx7K1t/QWo0d6UynqpqN5cCQt1MCg=="],
116
-
117
- "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.5", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-pHgASxl50rrtOztgQCPmOXFjRW+mCd7ALr/3uXNzRrRoGV5G2+78GOsQ3HlQuBVHCh9o6xqMNvlIKZjWn4Euug=="],
118
-
119
- "@smithy/signature-v4": ["@smithy/signature-v4@5.3.10", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.1", "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "@smithy/util-hex-encoding": "^4.2.1", "@smithy/util-middleware": "^4.2.10", "@smithy/util-uri-escape": "^4.2.1", "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-Wab3wW8468WqTKIxI+aZe3JYO52/RYT/8sDOdzkUhjnLakLe9qoQqIcfih/qxcF4qWEFoWBszY0mj5uxffaVXA=="],
120
-
121
- "@smithy/smithy-client": ["@smithy/smithy-client@4.12.0", "", { "dependencies": { "@smithy/core": "^3.23.6", "@smithy/middleware-endpoint": "^4.4.20", "@smithy/middleware-stack": "^4.2.10", "@smithy/protocol-http": "^5.3.10", "@smithy/types": "^4.13.0", "@smithy/util-stream": "^4.5.15", "tslib": "^2.6.2" } }, "sha512-R8bQ9K3lCcXyZmBnQqUZJF4ChZmtWT5NLi6x5kgWx5D+/j0KorXcA0YcFg/X5TOgnTCy1tbKc6z2g2y4amFupQ=="],
122
-
123
- "@smithy/types": ["@smithy/types@4.13.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw=="],
124
-
125
- "@smithy/url-parser": ["@smithy/url-parser@4.2.10", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-uypjF7fCDsRk26u3qHmFI/ePL7bxxB9vKkE+2WKEciHhz+4QtbzWiHRVNRJwU3cKhrYDYQE3b0MRFtqfLYdA4A=="],
126
-
127
- "@smithy/util-base64": ["@smithy/util-base64@4.3.1", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.1", "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w=="],
128
-
129
- "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g=="],
130
-
131
- "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4rHqBvxtJEBvsZcFQSPQqXP2b/yy/YlB66KlcEgcH2WNoOKCKB03DSLzXmOsXjbl8dJ4OEYTn31knhdznwk7zw=="],
132
-
133
- "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.1", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig=="],
134
-
135
- "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A=="],
136
-
137
- "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.36", "", { "dependencies": { "@smithy/property-provider": "^4.2.10", "@smithy/smithy-client": "^4.12.0", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-R0smq7EHQXRVMxkAxtH5akJ/FvgAmNF6bUy/GwY/N20T4GrwjT633NFm0VuRpC+8Bbv8R9A0DoJ9OiZL/M3xew=="],
138
-
139
- "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.39", "", { "dependencies": { "@smithy/config-resolver": "^4.4.9", "@smithy/credential-provider-imds": "^4.2.10", "@smithy/node-config-provider": "^4.3.10", "@smithy/property-provider": "^4.2.10", "@smithy/smithy-client": "^4.12.0", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-otWuoDm35btJV1L8MyHrPl462B07QCdMTktKc7/yM+Psv6KbED/ziXiHnmr7yPHUjfIwE9S8Max0LO24Mo3ZVg=="],
140
-
141
- "@smithy/util-endpoints": ["@smithy/util-endpoints@3.3.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-xyctc4klmjmieQiF9I1wssBWleRV0RhJ2DpO8+8yzi2LO1Z+4IWOZNGZGNj4+hq9kdo+nyfrRLmQTzc16Op2Vg=="],
142
-
143
- "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA=="],
144
-
145
- "@smithy/util-middleware": ["@smithy/util-middleware@4.2.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-LxaQIWLp4y0r72eA8mwPNQ9va4h5KeLM0I3M/HV9klmFaY2kN766wf5vsTzmaOpNNb7GgXAd9a25P3h8T49PSA=="],
146
-
147
- "@smithy/util-retry": ["@smithy/util-retry@4.2.10", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.10", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-HrBzistfpyE5uqTwiyLsFHscgnwB0kgv8vySp7q5kZ0Eltn/tjosaSGGDj/jJ9ys7pWzIP/icE2d+7vMKXLv7A=="],
148
-
149
- "@smithy/util-stream": ["@smithy/util-stream@4.5.15", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.11", "@smithy/node-http-handler": "^4.4.12", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.1", "@smithy/util-buffer-from": "^4.2.1", "@smithy/util-hex-encoding": "^4.2.1", "@smithy/util-utf8": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-OlOKnaqnkU9X+6wEkd7mN+WB7orPbCVDauXOj22Q7VtiTkvy7ZdSsOg4QiNAZMgI4OkvNf+/VLUC3VXkxuWJZw=="],
150
-
151
- "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q=="],
152
-
153
- "@smithy/util-utf8": ["@smithy/util-utf8@4.2.1", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.1", "tslib": "^2.6.2" } }, "sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g=="],
154
-
155
- "@smithy/uuid": ["@smithy/uuid@1.1.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw=="],
156
-
157
- "@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
71
+ "@types/bun": ["@types/bun@1.4.3", "", { "dependencies": { "bun-types": "1.4.3" } }, "sha512-N67Pqy8JXbAY/j0tNraS3V5mubGvc6rrrNDmDCtZLlFzccbUJPDb0tvgUBC5q4m8ybkP93Jcn1QB+RYQ6PvBhw=="],
158
72
 
159
73
  "@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="],
160
74
 
161
75
  "bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
162
76
 
163
- "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
164
-
165
- "fast-xml-parser": ["fast-xml-parser@5.3.6", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA=="],
77
+ "bun-types": ["bun-types@1.4.3", "", { "dependencies": { "@types/node": "*", "undici-types": "*" } }, "sha512-tinRJNGOmUN+tEAsn2y2e942n9JHRHR42iUYzDH75NmhJqVBokGSQkkfSFdNWwsANIdrK6ACH9t01M452YyTwg=="],
166
78
 
167
79
  "node-screenshots": ["node-screenshots@0.2.8", "", { "optionalDependencies": { "node-screenshots-darwin-arm64": "0.2.8", "node-screenshots-darwin-x64": "0.2.8", "node-screenshots-linux-arm64-gnu": "0.2.8", "node-screenshots-linux-loong64-gnu": "0.2.8", "node-screenshots-linux-x64-gnu": "0.2.8", "node-screenshots-linux-x64-musl": "0.2.8", "node-screenshots-win32-arm64-msvc": "0.2.8", "node-screenshots-win32-ia32-msvc": "0.2.8", "node-screenshots-win32-x64-msvc": "0.2.8" } }, "sha512-7O2ab89hkUgiXkrYj9iYCbJjY1iFJqIyZt0vuVtHFKlnajeFoyW+hur4CPxwwrnCUK0ahr0WLWQ5A6rCzF8rWQ=="],
168
80
 
@@ -184,8 +96,6 @@
184
96
 
185
97
  "node-screenshots-win32-x64-msvc": ["node-screenshots-win32-x64-msvc@0.2.8", "", { "os": "win32", "cpu": "x64" }, "sha512-DdFORB74FghM1L6UtE1TltIR2H0CCCmfQEa8gp2CTSdtBr2b0TKL7ZlbEV6x/wVKRFT59w7Kd6BULdeMmp0X2w=="],
186
98
 
187
- "strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="],
188
-
189
99
  "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
190
100
 
191
101
  "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
@@ -194,16 +104,6 @@
194
104
 
195
105
  "webview-bun": ["webview-bun@2.4.0", "", {}, "sha512-0+ugnQlcUHmuW+iLeb+Lzb8rGUJh7WEdXvNsuvaVEXT3EagK380XdD7heVJu0Ek/mNxMY3G2JM142YRQ1hDUGQ=="],
196
106
 
197
- "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
198
-
199
- "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
200
-
201
- "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
202
-
203
- "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
204
-
205
- "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
206
-
207
- "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
107
+ "@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1150.0", "", { "dependencies": { "@aws-sdk/core": "^3.978.3", "@aws-sdk/nested-clients": "^3.997.48", "@aws-sdk/types": "^3.974.6", "@smithy/core": "^3.35.2", "@smithy/types": "^4.19.0", "tslib": "^2.6.2" } }, "sha512-wDU36ofhabZYu+d2KSriyDbwzxNoaOp+4dG0p3qkabk7HeALR5nIWyQbjoyJZLn3iqWkv/xNdyUfbfevDVDzzg=="],
208
108
  }
209
109
  }
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "claude-aws-mfa",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "repository": "https://github.com/aaronmeriwether/claude-aws-mfa",
6
6
  "bin": {
7
7
  "claude-aws-mfa": "./src/index.ts"
8
8
  },
9
9
  "devDependencies": {
10
- "@types/bun": "latest",
10
+ "@types/bun": "^1.4.3",
11
11
  "node-screenshots": "^0.2.8"
12
12
  },
13
13
  "peerDependencies": {
14
14
  "typescript": "^5"
15
15
  },
16
16
  "dependencies": {
17
- "@aws-sdk/client-sts": "^3.998.0",
17
+ "@aws-sdk/client-bedrock": "^3.1152.0",
18
+ "@aws-sdk/client-cloudwatch-logs": "^3.1152.0",
19
+ "@aws-sdk/client-sts": "^3.1152.0",
18
20
  "webview-bun": "^2.4.0"
19
21
  }
20
22
  }
package/src/config.ts CHANGED
@@ -259,6 +259,15 @@ export function missingConfigFields(cfg: Partial<Config>): string[] {
259
259
  return missing;
260
260
  }
261
261
 
262
+ /**
263
+ * Region for AWS calls: AWS_REGION, when set, overrides the configured one, as
264
+ * it does for the AWS SDK and CLI. Only applied at call time — the config keeps
265
+ * the region entered in the dialog.
266
+ */
267
+ export function effectiveRegion(configured?: string): string | undefined {
268
+ return process.env.AWS_REGION || configured || undefined;
269
+ }
270
+
262
271
  export async function seedDefaults(): Promise<Partial<Config>> {
263
272
  const defaults: Partial<Config> = {};
264
273
  try {
package/src/flags.ts CHANGED
@@ -1,21 +1,66 @@
1
+ export const HELP = `Usage: claude-aws-mfa [options]
2
+
3
+ With no options, prints temporary AWS credentials as JSON for Claude Code's
4
+ awsCredentialExport, prompting for MFA when needed.
5
+
6
+ Options:
7
+ --setup Launch the guided setup wizard
8
+ --[no-]cache-session Reuse cached STS credentials until they expire (default: on)
9
+ --[no-]auto-mfa Run the configured MFA command without showing the dialog (default: on)
10
+ --[no-]single-instance-lock Let only one instance show a dialog at a time (default: on)
11
+ --no-ui Never show a GUI; errors go to stderr
12
+ --usage[=[<role>/]<session>]
13
+ Print Bedrock token usage and estimated cost instead of
14
+ credentials, for a session name (default: the one in the
15
+ config), optionally under one role. Use * to itemize:
16
+ --usage=my-role/* one row per session of role my-role
17
+ --usage=* one row per role
18
+ --profile=<name> With --usage: query using this AWS profile instead of the
19
+ tool's own credentials
20
+ --from=<date> With --usage: start of range (default: start of this month)
21
+ --to=<date> With --usage: end of range, exclusive (default: now)
22
+ -h, --help Show this help`;
23
+
1
24
  export interface CliFlags {
25
+ help: boolean;
2
26
  setup: boolean;
3
27
  cacheSession: boolean | undefined; // undefined = use config default
4
28
  autoMfa: boolean | undefined;
5
29
  singleInstanceLock: boolean | undefined;
6
30
  noUi: boolean;
31
+ usage: boolean;
32
+ usageSpec?: string; // --usage=[<role>/]<session>; undefined = session name from config
33
+ profile?: string;
34
+ from?: string;
35
+ to?: string;
7
36
  }
8
37
 
9
38
  export function parseFlags(argv: string[]): CliFlags {
10
39
  const flags: CliFlags = {
40
+ help: false,
11
41
  setup: false,
12
42
  cacheSession: undefined,
13
43
  autoMfa: undefined,
14
44
  singleInstanceLock: undefined,
15
45
  noUi: false,
46
+ usage: false,
16
47
  };
17
48
  for (const arg of argv.slice(2)) {
49
+ const [key, value] = arg.split(/=(.*)/s);
50
+ if (value !== undefined && (key === "--profile" || key === "--from" || key === "--to")) {
51
+ flags[key.slice(2) as "profile" | "from" | "to"] = value;
52
+ continue;
53
+ }
54
+ if (key === "--usage" && value !== undefined) {
55
+ flags.usage = true;
56
+ flags.usageSpec = value || undefined;
57
+ continue;
58
+ }
18
59
  switch (arg) {
60
+ case "-h":
61
+ case "--help":
62
+ flags.help = true;
63
+ break;
19
64
  case "setup":
20
65
  case "--setup":
21
66
  flags.setup = true;
@@ -41,6 +86,9 @@ export function parseFlags(argv: string[]): CliFlags {
41
86
  case "--no-ui":
42
87
  flags.noUi = true;
43
88
  break;
89
+ case "--usage":
90
+ flags.usage = true;
91
+ break;
44
92
  }
45
93
  }
46
94
  return flags;
package/src/index.ts CHANGED
@@ -15,10 +15,15 @@ if (process.platform === "linux" && !process.env.WEBKIT_DISABLE_SANDBOX_THIS_IS_
15
15
  process.exit(proc.exitCode);
16
16
  }
17
17
 
18
- import { parseFlags } from "./flags";
18
+ import { parseFlags, HELP } from "./flags";
19
19
 
20
20
  const flags = parseFlags(process.argv);
21
21
 
22
+ if (flags.help) {
23
+ console.log(HELP);
24
+ process.exit(0);
25
+ }
26
+
22
27
  if (flags.setup) {
23
28
  const { runSetup } = await import("./setup");
24
29
  process.exit(runSetup() ? 0 : 2);
@@ -44,9 +49,31 @@ function isSessionValid(session: CachedSession | undefined): session is CachedSe
44
49
  return new Date(session.Expiration).getTime() > Date.now();
45
50
  }
46
51
 
47
- // --- Helper: output credentials and save config ---
52
+ // --- Helper: query Bedrock usage (--usage); a CLI command, so errors go to stderr ---
53
+
54
+ async function runUsage(opts: Omit<Parameters<typeof import("./usage").printUsage>[0], "spec" | "from" | "to">): Promise<never> {
55
+ const { printUsage } = await import("./usage");
56
+ try {
57
+ await printUsage({ ...opts, spec: flags.usageSpec ?? defaults.sessionName, from: flags.from, to: flags.to });
58
+ process.exit(0);
59
+ } catch (err) {
60
+ process.stderr.write(`Usage query failed: ${err instanceof Error ? err.message : err}\n`);
61
+ process.exit(1);
62
+ }
63
+ }
48
64
 
49
- function outputCredentials(session: CachedSession): void {
65
+ // With --profile, the query uses that AWS profile instead of this tool's own credentials.
66
+ if (flags.usage && flags.profile) await runUsage({ region: defaults.region, profile: flags.profile });
67
+
68
+ // --- Helper: output credentials (or, with --usage, use them for the usage query) ---
69
+
70
+ async function outputCredentials(session: CachedSession): Promise<void> {
71
+ if (flags.usage) {
72
+ await runUsage({
73
+ region: defaults.region,
74
+ credentials: { accessKeyId: session.AccessKeyId, secretAccessKey: session.SecretAccessKey, sessionToken: session.SessionToken },
75
+ });
76
+ }
50
77
  console.log(JSON.stringify({
51
78
  Credentials: {
52
79
  AccessKeyId: session.AccessKeyId,
@@ -59,7 +86,7 @@ function outputCredentials(session: CachedSession): void {
59
86
  // --- Helper: report a failure, respecting --no-ui (dialog is never shown in that mode) ---
60
87
 
61
88
  async function reportFailure(message: string): Promise<never> {
62
- if (flags.noUi) {
89
+ if (flags.noUi || flags.usage) {
63
90
  // stdout is reserved for cleanly-parsable credentials JSON on success.
64
91
  process.stderr.write(`${message}\n`);
65
92
  } else {
@@ -174,7 +201,7 @@ async function obtainViaDialog(cfg: Partial<Config>): Promise<void> {
174
201
  ...(useCache ? { cachedSession: session } : {}),
175
202
  });
176
203
 
177
- outputCredentials(session);
204
+ await outputCredentials(session);
178
205
  }
179
206
 
180
207
  // --- Main flow ---
@@ -184,7 +211,7 @@ try {
184
211
  if (useCache && config?.cachedSession) {
185
212
  if (isSessionValid(config.cachedSession)) {
186
213
  // Return cached credentials without showing dialog
187
- outputCredentials(config.cachedSession);
214
+ await outputCredentials(config.cachedSession);
188
215
  process.exit(0);
189
216
  }
190
217
  // Expired — clear the cached session
@@ -196,7 +223,7 @@ try {
196
223
  try {
197
224
  const session = await tryAutoMfa(defaults);
198
225
  if (session) {
199
- outputCredentials(session);
226
+ await outputCredentials(session);
200
227
  process.exit(0);
201
228
  }
202
229
  } catch (err) {
@@ -226,7 +253,7 @@ try {
226
253
  if (useCache) {
227
254
  const freshConfig = loadConfig();
228
255
  if (freshConfig?.cachedSession && isSessionValid(freshConfig.cachedSession)) {
229
- outputCredentials(freshConfig.cachedSession);
256
+ await outputCredentials(freshConfig.cachedSession);
230
257
  process.exit(0);
231
258
  }
232
259
  }
@@ -238,7 +265,7 @@ try {
238
265
  if (useCache) {
239
266
  const freshConfig = loadConfig();
240
267
  if (freshConfig?.cachedSession && isSessionValid(freshConfig.cachedSession)) {
241
- outputCredentials(freshConfig.cachedSession);
268
+ await outputCredentials(freshConfig.cachedSession);
242
269
  process.exit(0);
243
270
  }
244
271
  }
package/src/sts.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
2
+ import { effectiveRegion } from "./config";
2
3
 
3
4
  export const DEFAULT_SESSION_NAME = "claude-aws-mfa";
4
5
 
@@ -30,7 +31,7 @@ export async function assumeRoleWithMfa(params: {
30
31
  sessionName?: string;
31
32
  }) {
32
33
  const client = new STSClient({
33
- region: params.region,
34
+ region: effectiveRegion(params.region),
34
35
  credentials: { accessKeyId: params.accessKeyId, secretAccessKey: params.secretAccessKey },
35
36
  });
36
37
 
package/src/usage.ts ADDED
@@ -0,0 +1,227 @@
1
+ import { BedrockClient, GetModelInvocationLoggingConfigurationCommand } from "@aws-sdk/client-bedrock";
2
+ import { CloudWatchLogsClient, StartQueryCommand, GetQueryResultsCommand } from "@aws-sdk/client-cloudwatch-logs";
3
+ import { DEFAULT_SESSION_NAME } from "./sts";
4
+ import { effectiveRegion } from "./config";
5
+
6
+ const COLUMNS = ["requests", "inputTokens", "cacheReadTokens", "cacheWriteTokens", "outputTokens"] as const;
7
+ const HEADERS = ["Model", "Requests", "Input", "Cache read", "Cache write", "Output"];
8
+
9
+ /** Start of the current month (local time) through now, unless overridden. */
10
+ export function usageRange(from?: string, to?: string): { start: Date; end: Date } {
11
+ const now = new Date();
12
+ const start = from ? new Date(from) : new Date(now.getFullYear(), now.getMonth(), 1);
13
+ const end = to ? new Date(to) : now;
14
+ if (isNaN(start.getTime())) throw new Error(`Invalid --from date: ${from}`);
15
+ if (isNaN(end.getTime())) throw new Error(`Invalid --to date: ${to}`);
16
+ if (start >= end) throw new Error("--from must be before --to");
17
+ return { start, end };
18
+ }
19
+
20
+ type GroupField = "roleName" | "sessionName";
21
+ const GROUP_HEADERS: Record<GroupField, string> = { roleName: "Role", sessionName: "Session" };
22
+
23
+ /** What `--usage=<spec>` selects; an undefined role/session matches any. */
24
+ export interface UsageSpec {
25
+ role?: string;
26
+ session?: string;
27
+ groupBy: GroupField[];
28
+ }
29
+
30
+ /**
31
+ * `--usage=[<role>/]<session>`. Either part may be `*`, meaning "itemize by
32
+ * it": `my-role/*` gives one row per session of role my-role. A bare `*` itemizes
33
+ * by role (sessions merged); a bare name matches that session under any role.
34
+ */
35
+ export function parseUsageSpec(spec: string): UsageSpec {
36
+ if (spec === "*") return { groupBy: ["roleName"] };
37
+ const parts = spec.split("/");
38
+ if (parts.length > 2) throw new Error(`Invalid --usage value "${spec}": expected [<role>/]<session-name>`);
39
+ const [role, session] = parts.length === 2 ? parts : [undefined, parts[0]];
40
+ // IAM's charset for role and session names; also keeps them safe to embed as query literals.
41
+ for (const name of [role, session]) {
42
+ if (name !== undefined && name !== "*" && !/^[\w+=,.@-]+$/.test(name)) {
43
+ throw new Error(`Invalid role or session name "${name}" in --usage value "${spec}"`);
44
+ }
45
+ }
46
+ return {
47
+ role: role === "*" ? undefined : role,
48
+ session: session === "*" ? undefined : session,
49
+ groupBy: [role === "*" && "roleName", session === "*" && "sessionName"].filter((g): g is GroupField => !!g),
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Logs Insights query summing token counts per model (and per group) for the
55
+ * assumed-role callers a spec selects. The caller's ARN is
56
+ * `arn:aws:sts::<acct>:assumed-role/<role>/<session>`; other callers (IAM
57
+ * users, root) never match.
58
+ */
59
+ export function usageQuery(spec: UsageSpec): string {
60
+ const filters = ["ispresent(sessionName)"];
61
+ if (spec.role) filters.push(`roleName = "${spec.role}"`);
62
+ if (spec.session) filters.push(`sessionName = "${spec.session}"`);
63
+ return [
64
+ "parse identity.arn /:assumed-role\\/(?<roleName>[^\\/]+)\\/(?<sessionName>[^\\/]+)$/",
65
+ `| filter ${filters.join(" and ")}`,
66
+ "| stats count(*) as requests, sum(input.inputTokenCount) as inputTokens,",
67
+ " sum(input.cacheReadInputTokenCount) as cacheReadTokens, sum(input.cacheWriteInputTokenCount) as cacheWriteTokens,",
68
+ ` sum(output.outputTokenCount) as outputTokens by ${[...spec.groupBy, "modelId"].join(", ")}`,
69
+ ].join("\n");
70
+ }
71
+
72
+ type Column = (typeof COLUMNS)[number];
73
+ /** Per-token USD prices, keyed by `<Price List model name>|<global?>`. */
74
+ export type PriceTable = Record<string, Partial<Record<Column, number>>>;
75
+
76
+ const PRICED_COLUMNS: Record<string, Column> = {
77
+ "Input tokens": "inputTokens",
78
+ "Output tokens": "outputTokens",
79
+ "Prompt cache read input tokens": "cacheReadTokens",
80
+ "Prompt cache write input tokens": "cacheWriteTokens",
81
+ };
82
+
83
+ /**
84
+ * Standard on-demand token prices from an AWS Price List offer file
85
+ * (AmazonBedrockFoundationModels). Long-context, 1h-cache-write and
86
+ * latency-optimized variants are skipped: the aggregated logs can't tell those
87
+ * requests apart, so every token is priced at the base rate.
88
+ */
89
+ export function parsePriceList(offer: any): PriceTable {
90
+ const table: PriceTable = {};
91
+ for (const [sku, product] of Object.entries<any>(offer.products ?? {})) {
92
+ const a = product.attributes ?? {};
93
+ const column = PRICED_COLUMNS[a.inferenceType];
94
+ if (a.feature !== "On-demand Inference" || !column || /long_ctx|1h|LatencyOptimized/i.test(a.usagetype)) continue;
95
+ const dim: any = Object.values<any>(Object.values<any>(offer.terms?.OnDemand?.[sku] ?? {})[0]?.priceDimensions ?? {})[0];
96
+ if (dim?.unit !== "1M tokens") continue;
97
+ (table[`${a.model}|${/global/i.test(a.usagetype)}`] ??= {})[column] = Number(dim.pricePerUnit.USD) / 1e6;
98
+ }
99
+ return table;
100
+ }
101
+
102
+ /** `…/us.anthropic.claude-sonnet-4-5-20250929-v1:0` → `Claude Sonnet 4.5`, the Price List's model name. */
103
+ export function priceListModelName(modelId: string): string {
104
+ const words = modelId.split("/").pop()!.split(".").pop()!.replace(/-\d{8}.*$|-v\d+(:\d+)?$/, "").split("-");
105
+ return words
106
+ .map((w, i) => (i === 0 ? "" : /^\d+$/.test(w) && /^\d+$/.test(words[i - 1]) ? "." : " ") + w[0].toUpperCase() + w.slice(1))
107
+ .join("");
108
+ }
109
+
110
+ /** USD cost of one row, or undefined if any token type it used has no known price. */
111
+ export function rowCost(row: Record<string, string>, prices: PriceTable): number | undefined {
112
+ const isGlobal = row.modelId.split("/").pop()!.startsWith("global.");
113
+ const price = prices[`${priceListModelName(row.modelId)}|${isGlobal}`] ?? {};
114
+ let cost = 0;
115
+ for (const c of COLUMNS.slice(1)) {
116
+ const tokens = Number(row[c] || 0);
117
+ if (!tokens) continue;
118
+ if (price[c] === undefined) return undefined;
119
+ cost += tokens * price[c]!;
120
+ }
121
+ return cost;
122
+ }
123
+
124
+ const usd = (n: number) => "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
125
+
126
+ interface Tally {
127
+ label: string;
128
+ counts: number[];
129
+ cost: number;
130
+ unpriced: boolean; // some tokens had no list price, so `cost` is a lower bound
131
+ }
132
+
133
+ function tally(label: string, rows: Record<string, string>[], prices: PriceTable): Tally {
134
+ const t: Tally = { label, counts: COLUMNS.map(() => 0), cost: 0, unpriced: false };
135
+ for (const r of rows) {
136
+ COLUMNS.forEach((c, i) => (t.counts[i] += Number(r[c] || 0)));
137
+ const cost = rowCost(r, prices);
138
+ if (cost === undefined) t.unpriced = true;
139
+ else t.cost += cost;
140
+ }
141
+ return t;
142
+ }
143
+
144
+ /** One line per model, or per group (role and/or session, models merged) when itemizing. */
145
+ export function formatUsageTable(rows: Record<string, string>[], prices: PriceTable = {}, groupBy: GroupField[] = []): string {
146
+ if (!rows.length) return "No Bedrock invocations found.";
147
+ const labelOf = (r: Record<string, string>) =>
148
+ groupBy.length ? groupBy.map((g) => r[g]).join("/") : (r.modelId ?? "").split("/").pop()!;
149
+ const groups = Map.groupBy(rows, labelOf);
150
+ const tallies = [...groups].map(([label, rs]) => tally(label, rs, prices)).sort((a, b) => b.cost - a.cost);
151
+ const total = tally("TOTAL", rows, prices);
152
+
153
+ const cost = (t: Tally) => (t.unpriced && !t.cost ? "?" : usd(t.cost) + (t.unpriced ? "+" : ""));
154
+ const cells = (t: Tally) => [t.label, ...t.counts.map((n) => n.toLocaleString("en-US")), cost(t)];
155
+ const headers = [groupBy.length ? groupBy.map((g) => GROUP_HEADERS[g]).join("/") : "Model", ...HEADERS.slice(1), "Cost (USD)"];
156
+ const table = [...tallies, total].map(cells);
157
+ const widths = headers.map((h, i) => Math.max(h.length, ...table.map((r) => r[i].length)));
158
+ const line = (cs: string[]) => cs.map((c, i) => (i ? c.padStart(widths[i]) : c.padEnd(widths[i]))).join(" ");
159
+ return [
160
+ line(headers),
161
+ ...table.slice(0, -1).map(line),
162
+ widths.map((w) => "-".repeat(w)).join(" "),
163
+ line(table.at(-1)!),
164
+ "",
165
+ "Cost is an estimate at current AWS list prices (base on-demand rates; excludes discounts, long-context and 1h-cache surcharges).",
166
+ ...(total.unpriced ? ["? / + = some models have no list price and are excluded from the cost."] : []),
167
+ ].join("\n");
168
+ }
169
+
170
+ /** Bedrock prices for `region` from the public AWS Price List bulk API (no credentials needed). */
171
+ async function fetchPrices(region: string): Promise<PriceTable> {
172
+ const url = `https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonBedrockFoundationModels/current/${region}/index.json`;
173
+ const res = await fetch(url);
174
+ if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
175
+ return parsePriceList(await res.json());
176
+ }
177
+
178
+ /**
179
+ * Query Bedrock model invocation logs (CloudWatch Logs destination) for usage
180
+ * by the role/session that `spec` (see parseUsageSpec) selects. Requires invocation logging to be enabled.
181
+ * `credentials` undefined + `profile` set → the SDK resolves that profile.
182
+ */
183
+ export async function printUsage(opts: {
184
+ region?: string;
185
+ credentials?: { accessKeyId: string; secretAccessKey: string; sessionToken?: string };
186
+ profile?: string;
187
+ spec?: string;
188
+ from?: string;
189
+ to?: string;
190
+ }): Promise<void> {
191
+ const { start, end } = usageRange(opts.from, opts.to);
192
+ const specText = opts.spec || DEFAULT_SESSION_NAME;
193
+ const spec = parseUsageSpec(specText);
194
+ const clientConfig = { region: effectiveRegion(opts.region), credentials: opts.credentials, profile: opts.profile };
195
+
196
+ const bedrock = new BedrockClient(clientConfig);
197
+ // Prices are best-effort: token counts are still worth printing without them.
198
+ const prices = bedrock.config.region().then(fetchPrices).catch((err) => {
199
+ process.stderr.write(`Could not fetch Bedrock prices, costs omitted: ${err instanceof Error ? err.message : err}\n`);
200
+ return {};
201
+ });
202
+
203
+ const { loggingConfig } = await bedrock.send(new GetModelInvocationLoggingConfigurationCommand({}));
204
+ const logGroupName = loggingConfig?.cloudWatchConfig?.logGroupName;
205
+ if (!logGroupName) {
206
+ throw new Error("Bedrock model invocation logging to CloudWatch Logs is not enabled in this account/region, so there is no per-session usage to query.");
207
+ }
208
+
209
+ const logs = new CloudWatchLogsClient(clientConfig);
210
+ const { queryId } = await logs.send(new StartQueryCommand({
211
+ logGroupName,
212
+ startTime: Math.floor(start.getTime() / 1000),
213
+ endTime: Math.floor(end.getTime() / 1000),
214
+ queryString: usageQuery(spec),
215
+ }));
216
+
217
+ let res;
218
+ do {
219
+ await Bun.sleep(1000);
220
+ res = await logs.send(new GetQueryResultsCommand({ queryId }));
221
+ } while (res.status === "Scheduled" || res.status === "Running");
222
+ if (res.status !== "Complete") throw new Error(`Logs Insights query ${res.status}`);
223
+
224
+ const rows = (res.results ?? []).map((r) => Object.fromEntries(r.map((f) => [f.field!, f.value ?? ""])));
225
+ console.log(`Bedrock usage for ${specText}, ${start.toISOString()} – ${end.toISOString()} (log group ${logGroupName})\n`);
226
+ console.log(formatUsageTable(rows, await prices, spec.groupBy));
227
+ }
@@ -3,7 +3,25 @@ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync, rmSync,
3
3
  import { join, resolve } from "path";
4
4
  import { tmpdir } from "os";
5
5
  import { homedir } from "os";
6
- import { activeProfile, missingConfigFields, profileSuffix } from "../src/config";
6
+ import { activeProfile, effectiveRegion, missingConfigFields, profileSuffix } from "../src/config";
7
+
8
+ describe("effectiveRegion", () => {
9
+ test("AWS_REGION overrides the configured region", () => {
10
+ const saved = process.env.AWS_REGION;
11
+ try {
12
+ delete process.env.AWS_REGION;
13
+ expect(effectiveRegion("us-west-2")).toBe("us-west-2");
14
+ expect(effectiveRegion(undefined)).toBeUndefined();
15
+ process.env.AWS_REGION = "eu-central-1";
16
+ expect(effectiveRegion("us-west-2")).toBe("eu-central-1");
17
+ process.env.AWS_REGION = "";
18
+ expect(effectiveRegion("us-west-2")).toBe("us-west-2");
19
+ } finally {
20
+ if (saved === undefined) delete process.env.AWS_REGION;
21
+ else process.env.AWS_REGION = saved;
22
+ }
23
+ });
24
+ });
7
25
 
8
26
  describe("profileSuffix", () => {
9
27
  test("is empty for no profile and for the default config dir", () => {
@@ -18,6 +18,12 @@ describe("parseFlags", () => {
18
18
  expect(parseFlags([...base, "--no-ui"]).noUi).toBe(true);
19
19
  });
20
20
 
21
+ test("parses --help and -h", () => {
22
+ expect(parseFlags(base).help).toBe(false);
23
+ expect(parseFlags([...base, "--help"]).help).toBe(true);
24
+ expect(parseFlags([...base, "-h"]).help).toBe(true);
25
+ });
26
+
21
27
  test("parses --setup", () => {
22
28
  expect(parseFlags([...base, "--setup"]).setup).toBe(true);
23
29
  expect(parseFlags([...base, "setup"]).setup).toBe(true);
@@ -0,0 +1,115 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { parseUsageSpec, usageQuery, usageRange, formatUsageTable, parsePriceList, priceListModelName, rowCost } from "../src/usage";
3
+ import { parseFlags } from "../src/flags";
4
+
5
+ describe("usage", () => {
6
+ test("parses --usage, --profile=, --from=, --to=", () => {
7
+ const f = parseFlags(["bun", "x", "--usage", "--profile=work", "--from=2026-09-01", "--to=2026-10-01"]);
8
+ expect(f).toMatchObject({ usage: true, profile: "work", from: "2026-09-01", to: "2026-10-01" });
9
+ });
10
+
11
+ test("parses --usage=<spec>", () => {
12
+ expect(parseFlags(["bun", "x", "--usage=my-role/*"])).toMatchObject({ usage: true, usageSpec: "my-role/*" });
13
+ expect(parseFlags(["bun", "x", "--usage"]).usageSpec).toBeUndefined();
14
+ });
15
+
16
+ test("parses usage specs", () => {
17
+ expect(parseUsageSpec("sess")).toEqual({ role: undefined, session: "sess", groupBy: [] });
18
+ expect(parseUsageSpec("my-role/sess")).toEqual({ role: "my-role", session: "sess", groupBy: [] });
19
+ expect(parseUsageSpec("my-role/*")).toEqual({ role: "my-role", session: undefined, groupBy: ["sessionName"] });
20
+ expect(parseUsageSpec("*")).toEqual({ groupBy: ["roleName"] });
21
+ expect(parseUsageSpec("*/*")).toEqual({ role: undefined, session: undefined, groupBy: ["roleName", "sessionName"] });
22
+ expect(parseUsageSpec("jane.doe@example.com").session).toBe("jane.doe@example.com");
23
+ expect(() => parseUsageSpec("a/b/c")).toThrow("expected");
24
+ expect(() => parseUsageSpec('x" or 1=1')).toThrow("Invalid role or session");
25
+ expect(() => parseUsageSpec("my-role/")).toThrow("Invalid role or session");
26
+ });
27
+
28
+ test("defaults to the current month", () => {
29
+ const { start, end } = usageRange();
30
+ const now = new Date();
31
+ expect(start).toEqual(new Date(now.getFullYear(), now.getMonth(), 1));
32
+ expect(end.getTime()).toBeGreaterThanOrEqual(start.getTime());
33
+ });
34
+
35
+ test("rejects bad ranges", () => {
36
+ expect(() => usageRange("nope")).toThrow("Invalid --from");
37
+ expect(() => usageRange("2026-10-02", "2026-10-01")).toThrow("before");
38
+ });
39
+
40
+ test("query parses role and session from the caller ARN and filters/groups by the spec", () => {
41
+ const q = usageQuery(parseUsageSpec("my-role/*"));
42
+ expect(q).toContain('filter ispresent(sessionName) and roleName = "my-role"\n');
43
+ expect(q).toContain("by sessionName, modelId");
44
+ const re = new RegExp(q.match(/^parse identity\.arn \/(.*)\/$/m)![1]);
45
+ expect("arn:aws:sts::1:assumed-role/my-role/jane.doe@example.com".match(re)?.groups).toEqual({ roleName: "my-role", sessionName: "jane.doe@example.com" });
46
+ expect(re.test("arn:aws:iam::1:user/bob")).toBe(false);
47
+ expect(usageQuery(parseUsageSpec("sess"))).toContain('and sessionName = "sess"\n');
48
+ expect(usageQuery(parseUsageSpec("sess"))).toContain("by modelId");
49
+ });
50
+
51
+ test("formats a table with totals", () => {
52
+ const out = formatUsageTable([
53
+ { modelId: "m1", requests: "2", inputTokens: "1000", outputTokens: "50" },
54
+ { modelId: "m2", requests: "1", inputTokens: "10", cacheReadTokens: "5", outputTokens: "1" },
55
+ ]);
56
+ expect(out).toContain("TOTAL");
57
+ expect(out.split("\n").find((l) => l.startsWith("TOTAL"))).toMatch(/^TOTAL\s+3\s+1,010\s+5\s+0\s+51\s+\?$/);
58
+ expect(formatUsageTable([])).toBe("No Bedrock invocations found.");
59
+ });
60
+
61
+ test("maps log model IDs to Price List model names", () => {
62
+ expect(priceListModelName("arn:aws:bedrock:us-west-2:1:inference-profile/us.anthropic.claude-opus-5-5")).toBe("Claude Opus 5.5");
63
+ expect(priceListModelName("global.anthropic.claude-sonnet-5")).toBe("Claude Sonnet 5");
64
+ expect(priceListModelName("us.anthropic.claude-sonnet-4-5-20250929-v1:0")).toBe("Claude Sonnet 4.5");
65
+ expect(priceListModelName("anthropic.claude-3-5-haiku-20241022-v1:0")).toBe("Claude 3.5 Haiku");
66
+ });
67
+
68
+ const product = (model: string, inferenceType: string, usagetype: string) => ({
69
+ attributes: { feature: "On-demand Inference", model, inferenceType, usagetype },
70
+ });
71
+ const term = (usd: string) => ({ t: { priceDimensions: { d: { unit: "1M tokens", pricePerUnit: { USD: usd } } } } });
72
+ const offer = {
73
+ products: {
74
+ a: product("Claude Opus 5.5", "Input tokens", "USW2-MP:USW2_input_tokens_standard-Units"),
75
+ b: product("Claude Opus 5.5", "Input tokens", "USW2-MP:USW2_input_tokens_global_standard-Units"),
76
+ c: product("Claude Opus 5.5", "Input tokens", "USW2-MP:USW2_input_tokens_long_ctx_standard-Units"),
77
+ d: product("Claude Opus 5.5", "Prompt cache write input tokens", "USW2-MP:USW2_cache_write_tokens_1h_standard-Units"),
78
+ e: product("Claude Opus 5.5", "Prompt cache write input tokens", "USW2-MP:USW2_cache_write_tokens_standard-Units"),
79
+ f: product("Claude Opus 5.5", "Output tokens", "USW2-MP:USW2_output_tokens_standard-Units"),
80
+ },
81
+ terms: { OnDemand: { a: term("4.4"), b: term("4"), c: term("99"), d: term("99"), e: term("5.5"), f: term("22") } },
82
+ };
83
+
84
+ test("parses base on-demand prices, skipping long-context and 1h variants", () => {
85
+ expect(parsePriceList(offer)).toEqual({
86
+ "Claude Opus 5.5|false": { inputTokens: 4.4e-6, cacheWriteTokens: 5.5e-6, outputTokens: 22e-6 },
87
+ "Claude Opus 5.5|true": { inputTokens: 4e-6 },
88
+ });
89
+ });
90
+
91
+ test("costs a row, picking global vs geo prices; unknown prices give undefined", () => {
92
+ const prices = parsePriceList(offer);
93
+ expect(rowCost({ modelId: "us.anthropic.claude-opus-5-5", inputTokens: "1000000", outputTokens: "1000000" }, prices)).toBeCloseTo(26.4);
94
+ expect(rowCost({ modelId: "global.anthropic.claude-opus-5-5", inputTokens: "1000000" }, prices)).toBeCloseTo(4);
95
+ expect(rowCost({ modelId: "global.anthropic.claude-opus-5-5", outputTokens: "1" }, prices)).toBeUndefined();
96
+ expect(rowCost({ modelId: "us.anthropic.claude-unknown-9" , inputTokens: "1" }, prices)).toBeUndefined();
97
+ });
98
+
99
+ test("itemized tables merge models per group and cost each model separately", () => {
100
+ const prices = parsePriceList(offer);
101
+ const out = formatUsageTable(
102
+ [
103
+ { sessionName: "a", modelId: "us.anthropic.claude-opus-5-5", requests: "1", inputTokens: "1000000" },
104
+ { sessionName: "a", modelId: "global.anthropic.claude-opus-5-5", requests: "1", inputTokens: "1000000" },
105
+ { sessionName: "b", modelId: "us.anthropic.claude-unknown-9", requests: "1", inputTokens: "5" },
106
+ ],
107
+ prices,
108
+ ["sessionName"],
109
+ ).split("\n");
110
+ expect(out[0]).toMatch(/^Session\s/);
111
+ expect(out[1]).toMatch(/^a\s+2\s+2,000,000\s.*\$8\.40$/);
112
+ expect(out[2]).toMatch(/^b\s+1\s+5\s.*\?$/);
113
+ expect(out[4]).toMatch(/^TOTAL\s+3\s.*\$8\.40\+$/);
114
+ });
115
+ });