Automatic OAuth credential rotation for CLI tools with quota limits
Project description
credential-rotation
Automatic OAuth credential rotation for CLI tools with quota limits.
Overview
This package provides automatic credential rotation for CLI tools that have usage quota limits. When a tool reports quota exhaustion, the system automatically switches to the next available account in a round-robin fashion.
Currently Supported:
- Qwen CLI (
qwen)
Planned Support:
- OpenAI CLI
- Anthropic Claude CLI
- Generic pattern for any CLI tool
Installation
From PyPI (when published)
pip install credential-rotation
From GitHub
pip install git+https://github.com/dawid/credential-rotation.git
Development Install
git clone https://github.com/dawid/credential-rotation.git
cd credential-rotation
pip install -e ".[dev,qwen]"
Quick Start
1. Set Up Multiple Accounts
First, configure multiple Qwen accounts:
account-qwen --setup
This will guide you through:
- Opening a new terminal for each account (5 accounts recommended)
- Running
qwento initiate OAuth login - Completing the browser authentication
- Saving credentials automatically
2. Use in Your Code
from credential_rotation import QwenWrapper
# Create wrapper with automatic rotation
wrapper = QwenWrapper(max_retries=5)
# Call with automatic fallback
result = wrapper.call_with_fallback(
"Analyze this code",
fallback_message="Analysis unavailable",
timeout=45
)
if result:
print(result)
else:
print("All accounts exhausted")
3. Manual Account Management
# List all accounts with status
account-qwen --list
# Switch to specific account
account-qwen --switch 3
# Switch to next account (round-robin)
account-qwen --switch-next
# View usage statistics
account-qwen --stats
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Your Application │
└───────────────────────────┬─────────────────────────────────────┘
│ calls
▼
┌─────────────────────────────────────────────────────────────────┐
│ QwenWrapper │
│ • Executes CLI command │
│ • Detects quota errors │
│ • Calls AccountManager.switch_next() on quota error │
│ • Retries with new account │
└───────────────────────────┬─────────────────────────────────────┘
│ switches
▼
┌─────────────────────────────────────────────────────────────────┐
│ AccountManager │
│ • Atomic symlink update (os.replace) │
│ • File locking (fcntl.flock) │
│ • State persistence (state.yaml) │
│ • Audit logging (rotation.log) │
└───────────────────────────┬─────────────────────────────────────┘
│ updates
▼
┌─────────────────────────────────────────────────────────────────┐
│ ~/.qwen/ structure │
│ ├── accounts/ │
│ │ ├── oauth_creds_1.json │
│ │ ├── oauth_creds_2.json │
│ │ └── ... │
│ ├── oauth_creds.json → symlink to active account │
│ ├── state.yaml │
│ └── rotation.log │
└─────────────────────────────────────────────────────────────────┘
Credential Storage
Credentials are stored in ~/.qwen/:
~/.qwen/
├── accounts/
│ ├── oauth_creds_1.json # Account 1
│ ├── oauth_creds_2.json # Account 2
│ ├── oauth_creds_3.json # Account 3
│ ├── oauth_creds_4.json # Account 4
│ └── oauth_creds_5.json # Account 5
├── oauth_creds.json # Symlink to current account
├── state.yaml # Rotation state
└── rotation.log # Audit log
API Reference
QwenWrapper
from credential_rotation import QwenWrapper
wrapper = QwenWrapper(max_retries=5)
# Basic call with automatic rotation
result = wrapper.call(prompt, timeout=45)
# Call with fallback message
output = wrapper.call_with_fallback(
prompt,
fallback_message="Fallback text",
timeout=30
)
Returns:
WrapperResult(success=bool, output=str, error=str|None, attempts=int)
AccountManager
from credential_rotation import AccountManager
manager = AccountManager()
# Switch to specific account
manager.switch_to(3)
# Switch to next account (round-robin)
switched, next_index = manager.switch_next()
# List all accounts
accounts = manager.list_accounts()
# Get statistics
stats = manager.get_stats()
Testing
# Run tests
pytest
# Run with coverage
pytest --cov=credential_rotation --cov-report=html
Docker Usage
Mount the .qwen directory to share credentials:
services:
app:
image: your-app
volumes:
- ~/.qwen:/root/.qwen:ro
Security
- Atomic Operations: Uses
os.replace()for atomic symlink updates - File Locking: Uses
fcntl.flock()to prevent concurrent modifications - Audit Trail: All switches logged to
rotation.log - No Hardcoded Secrets: All credentials externalized to user directory
License
MIT License - see LICENSE file for details.
Contributing
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
Roadmap
- OpenAI CLI support
- Anthropic Claude CLI support
- Per-account quota tracking
- Configurable backoff strategies
- Webhook notifications
- Dashboard/monitoring UI
References
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file credential_rotation-0.1.0.tar.gz.
File metadata
- Download URL: credential_rotation-0.1.0.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de3014f318cb9a91137b4995e5c75db6d89f5fc4de14176f2bd7e3fb1b08c144
|
|
| MD5 |
bf2b72d57d61f54638d59b34f9bb1ccc
|
|
| BLAKE2b-256 |
dc7a7e0d294f3f5dc09971e8cbffca27875a4a338b13f16a5b20daca064fc235
|
File details
Details for the file credential_rotation-0.1.0-py3-none-any.whl.
File metadata
- Download URL: credential_rotation-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c93d0c37baa15a26830d3560690a2e9d16ae9e91f677d0b2cc2eb412856704d
|
|
| MD5 |
9402937977e9b09e4bb364c48392b8c9
|
|
| BLAKE2b-256 |
b48b47804460877237fb120923fb41be1453f756f76cf709dad3c95bbc44ca1f
|