cc-aws-keepalive
Keep Claude Code sessions alive through AWS credential expiration. No more restarting terminal tabs every time your SSO/SAML session expires.
Problem
When using Claude Code with AWS Bedrock, the AWS SDK caches credentials in memory. After your SSO/SAML session expires (typically every 1-12 hours), all Claude Code sessions become unresponsive and must be restarted — often corrupting conversations and losing context.
Solution
Four Node.js scripts (cross-platform: macOS, Linux, Windows) that hook into Claude Code's credential lifecycle:
| Script | Purpose | CC Setting |
|---|
aws-cred-export.mjs | Reads fresh creds from ~/.aws/credentials, bypassing SDK in-memory cache | awsCredentialExport |
aws-auth-refresh.mjs | On auth failure, checks if you already re-authed in another terminal | awsAuthRefresh |
aws-cred-check.mjs | Proactive check before each prompt — warns if expired or nearing expiry | hooks.UserPromptSubmit |
aws-statusline.mjs | Optional persistent timer in the status bar (e.g., AWS: 4h23m) | statusLine |
How it works
Before expiry (proactive):
- You submit a prompt in Claude Code
- The
UserPromptSubmit hook checks credential expiration
- If nearing expiry and
autoLoginCmd is configured: fires it in the background (you get a notification, approve MFA, session renews silently)
- If nearing expiry without
autoLoginCmd: inline warning with re-auth command
- If expired: warns inline — the prompt proceeds and
awsAuthRefresh handles recovery
After expiry (reactive):
- Claude Code hits a Bedrock 403
awsAuthRefresh runs — checks if you already re-authed in another terminal
- If still expired and
autoLoginCmd is configured, runs it synchronously (waits up to 3 minutes for password + MFA)
awsCredentialExport reads fresh creds from disk (bypassing SDK memory cache)
- Claude Code retries the API call — session continues without restart
The key insight
Claude Code's AWS SDK caches credentials in memory and doesn't re-read ~/.aws/credentials after expiry (known issue). The awsCredentialExport setting forces Claude Code to call our script instead, which always reads the latest credentials from disk.
Install
Requires Node.js (ships with Claude Code).
Option A: As a Claude Code plugin (recommended)
# Add the marketplace to your settings.json:
# "extraKnownMarketplaces": {
# "cc-aws-keepalive": {
# "source": { "source": "git", "url": "https://github.com/GeiserX/cc-aws-keepalive.git" }
# }
# }
#
# Then enable the plugin:
# "enabledPlugins": { "cc-aws-keepalive@cc-aws-keepalive": true }
The plugin auto-registers the UserPromptSubmit hook. You still need to add awsCredentialExport and awsAuthRefresh to ~/.claude/settings.json — point them at the cached plugin path:
{
"awsCredentialExport": "node ~/.claude/plugins/cache/cc-aws-keepalive/cc-aws-keepalive/<version>/aws-cred-export.mjs",
"awsAuthRefresh": "node ~/.claude/plugins/cache/cc-aws-keepalive/cc-aws-keepalive/<version>/aws-auth-refresh.mjs"
}
Replace <version> with the installed version (e.g., 0.3.0). Then create and edit your config:
cp config.example.json ~/.config/cc-aws-keepalive/config.json
Option B: Manual (no plugin system)
git clone https://github.com/GeiserX/cc-aws-keepalive.git
cd cc-aws-keepalive
node install.mjs
The installer creates a config and prints all settings to add to ~/.claude/settings.json.
Upgrading
After upgrading, re-run the installer to update paths:
- Plugin:
node ~/.claude/plugins/cache/cc-aws-keepalive/cc-aws-keepalive/<version>/install.mjs
- Manual:
git pull && node install.mjs
The installer automatically:
- OMC HUD wrapper: Cleans up any legacy timer patch from
omc-hud.mjs and updates the aws-hud-wrapper.mjs with the current path
- settings.json paths: Updates
awsCredentialExport and awsAuthRefresh to point to the new version directory (preserves any custom wrapper commands)
Configure
Edit ~/.config/cc-aws-keepalive/config.json:
{
"profile": "my-bedrock-profile",
"expirationField": "x_security_token_expires",
"loginCmd": "saml2aws login --profile my-bedrock-profile",
"autoLoginCmd": "",
"autoLoginMinutes": 30,
"warnMinutes": 30,
"timerWarnMinutes": 60,
"statusLineCmd": ""
}