claude-aws-mfa 1.4.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,11 @@ 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
+
25
30
  ## [v1.4.0] - 2026-10-19
26
31
 
27
32
  ### Added
package/README.md CHANGED
@@ -58,6 +58,8 @@ Add this (plus any addtitional config you prefer) to your Claude Code settings (
58
58
  6. Saves configuration (including MFA mode and command) to `~/.config/claude-aws-mfa.json` (mode 0600) for subsequent runs
59
59
  7. Outputs temporary credentials as JSON to stdout
60
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
+
61
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.
62
64
 
63
65
  ### Claude Code profiles
@@ -119,7 +121,7 @@ claude-aws-mfa --usage='my-role/*' # quote * so the shel
119
121
 
120
122
  Only callers that assumed a role are counted; calls made directly with IAM user keys never match.
121
123
 
122
- 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.
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.
123
125
 
124
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.
125
127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-aws-mfa",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "repository": "https://github.com/aaronmeriwether/claude-aws-mfa",
6
6
  "bin": {
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/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 CHANGED
@@ -1,6 +1,7 @@
1
1
  import { BedrockClient, GetModelInvocationLoggingConfigurationCommand } from "@aws-sdk/client-bedrock";
2
2
  import { CloudWatchLogsClient, StartQueryCommand, GetQueryResultsCommand } from "@aws-sdk/client-cloudwatch-logs";
3
3
  import { DEFAULT_SESSION_NAME } from "./sts";
4
+ import { effectiveRegion } from "./config";
4
5
 
5
6
  const COLUMNS = ["requests", "inputTokens", "cacheReadTokens", "cacheWriteTokens", "outputTokens"] as const;
6
7
  const HEADERS = ["Model", "Requests", "Input", "Cache read", "Cache write", "Output"];
@@ -190,7 +191,7 @@ export async function printUsage(opts: {
190
191
  const { start, end } = usageRange(opts.from, opts.to);
191
192
  const specText = opts.spec || DEFAULT_SESSION_NAME;
192
193
  const spec = parseUsageSpec(specText);
193
- const clientConfig = { region: opts.region || undefined, credentials: opts.credentials, profile: opts.profile };
194
+ const clientConfig = { region: effectiveRegion(opts.region), credentials: opts.credentials, profile: opts.profile };
194
195
 
195
196
  const bedrock = new BedrockClient(clientConfig);
196
197
  // Prices are best-effort: token counts are still worth printing without them.
@@ -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", () => {