opencode-qwen-cli-auth
OAuth plugin for OpenCode to use Qwen for free via Qwen Account, compatible with the qwen-code CLI mechanism.
Features
- OAuth 2.0 Device Authorization Grant (RFC 8628) - login with your Qwen Account
- No API key required - utilize Qwen's free tier
- Automatic token refresh when expired
- Multi-account support - add multiple Qwen accounts and keep one active account
- DashScope compatibility - automatically injects required headers for the OAuth flow
- Smart output token limit - auto-caps tokens based on model (65K for coder-model, 8K for vision-model)
- Reasoning capability in UI (coder-model) - model tooltip shows reasoning support in OpenCode
- Reasoning-effort safety - strips reasoning control fields from outbound payload for OAuth compatibility
- Retry & Fallback - handles quota/rate limit errors with payload degradation mechanism
- Logging & Debugging - detailed debugging support via environment variables
Installation
Requirements
- Node.js >= 20.0.0
- OpenCode with plugin support
- Qwen Account (free)
Add to OpenCode
Configure in opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["opencode-qwen-cli-auth"],
"model": "qwen-code/coder-model"
}
Login
opencode auth login
Select provider Qwen Code (qwen.ai OAuth) and follow the instructions:
- Open the URL displayed in the terminal
- Enter the provided code
- The plugin will automatically poll and save the token
To add more accounts, run opencode auth login again.
The plugin stores each successful login in the multi-account store and can auto-switch on quota exhaustion.
Supported Models
| Qwen Coder (Qwen 3.5 Plus) | coder-model | text | text | 1M tokens | 65,536 tokens | Free |
| Qwen VL Plus (Vision) | vision-model | text, image | text | 128K tokens | 8,192 tokens | Free |
Reasoning Note
coder-model is marked as reasoning-capable in OpenCode UI.
- This release is UI-only for reasoning and does not enable runtime reasoning-effort controls for Qwen OAuth.
- If clients send
reasoning, reasoningEffort, or reasoning_effort, the plugin removes these fields before forwarding requests.
Configuration
Environment Variables
QWEN_CLI_PATH | Path to qwen CLI (for fallback) | Default: auto-detect |
QWEN_MODE | Qwen mode toggle | 1/true (default) |
DEBUG_QWEN_PLUGIN=1 | Enable debug logging | Optional |
ENABLE_PLUGIN_REQUEST_LOGGING=1 | Enable request logging to file | Optional |
OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1 | Enable CLI fallback on quota error | Optional |
OPENCODE_QWEN_ACCOUNTS_PATH | Override multi-account store path (must be inside ~/.qwen) | Optional |
OPENCODE_QWEN_QUOTA_COOLDOWN_MS | Cooldown for exhausted accounts | Default: 86400000 (24h) |
Debug & Logging
DEBUG_QWEN_PLUGIN=1 opencode run "hello" --model=qwen-code/coder-model
ENABLE_PLUGIN_REQUEST_LOGGING=1 opencode run "hello" --model=qwen-code/coder-model
Log files are stored at: ~/.opencode/logs/qwen-plugin/
How It Works
OAuth Flow
1. OpenCode requests auth -> Plugin
2. Plugin requests device code from Qwen OAuth Server
3. Displays URL + code to user
4. User opens URL and enters code to authorize
5. Plugin polls token from Qwen OAuth Server
6. Saves token and returns to OpenCode
7. All API requests are injected with headers and sent to DashScope
Token Storage
- Location:
~/.qwen/oauth_creds.json
- Format: JSON with access_token, refresh_token, expiry_date, resource_url
- Auto-refresh: Triggered when less than 30 seconds to expiration
- Lock mechanism: Safe multi-process token refresh
- Multi-account store:
~/.qwen/oauth_accounts.json
- Multi-account lock:
~/.qwen/oauth_accounts.lock
The plugin automatically injects required headers for DashScope OAuth:
X-DashScope-AuthType: qwen-oauth
X-DashScope-CacheControl: enable
User-Agent: QwenCode/{cli-version} ({platform}; {arch})
X-DashScope-UserAgent: QwenCode/{cli-version} ({platform}; {arch})
Error Handling
Insufficient Quota
When hitting a 429 insufficient_quota error, the plugin automatically:
- Marks current account exhausted for cooldown window
- Switches to next healthy account and retries with same payload
- Degrades payload if no healthy account can be switched
- CLI fallback (optional) - invokes
qwen CLI only for text-only payloads when OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1 is set
- Multimodal safety guard - skips CLI fallback for non-text parts (image/audio/file/video) to avoid semantic loss
Token Expiration
- Automatically uses refresh token
- Retries up to 2 times for transient errors (timeout, network)
- On refresh
401/403, marks current account as auth_invalid and switches to next healthy account when available
- If no healthy account is available, requests re-authentication (
opencode auth login)
Authentication Management
Check Status
cat ~/.qwen/oauth_creds.json
cat ~/.qwen/oauth_accounts.json
Remove Authentication
PowerShell:
Remove-Item -Recurse -Force "$HOME/.opencode/qwen"
Remove-Item -Force "$HOME/.qwen/oauth_creds.json"
Bash (Linux/macOS):
rm -rf ~/.opencode/qwen
rm ~/.qwen/oauth_creds.json
Manual Refresh
opencode auth logout
opencode auth login
Plugin Architecture
dist/
├── index.js # Entry point, exports QwenAuthPlugin
├── lib/
│ ├── auth/
│ │ ├── auth.js # OAuth flow: device code, poll token, refresh
│ │ └── browser.js # Browser opener utility
│ ├── config.js # Config paths, QWEN_MODE
│ ├── constants.js # Constants: OAuth endpoints, headers, errors
│ ├── logger.js # Logging utilities
│ └── types.js # TypeScript types
Internal Hooks Used
auth.loader | Provides apiKey, baseURL, custom fetch |
auth.methods.authorize | Device Authorization OAuth flow |
config | Registers provider and models |
chat.params | Sets timeout, maxRetries, max_tokens limits |
chat.headers | Injects DashScope headers |
Comparison with Previous Plugin
| OAuth Device Flow | ✓ | ✓ |
| Custom fetch layer | ✗ | ✓ |
| DashScope headers | ✗ | ✓ (auto-inject) |
| Output token capping | ✗ | ✓ |
| Quota degradation | ✗ | ✓ |
| CLI fallback | ✗ | ✓ (optional) |
| Multi-process lock | ✗ | ✓ |
| Legacy token migration | ✗ | ✓ |
Troubleshooting
Common Issues
1. Persistent insufficient_quota errors
- Your account may have exhausted the free tier quota
- Try deleting the token and logging in again
- Enable CLI fallback:
OPENCODE_QWEN_ENABLE_CLI_FALLBACK=1
2. OAuth timeout
- Check network connection
- Increase timeout in config if needed
- View detailed logs with
DEBUG_QWEN_PLUGIN=1
3. Cannot find qwen CLI
- Install qwen-code:
npm install -g @qwen-code/qwen-code
- Or set env var:
QWEN_CLI_PATH=/path/to/qwen
4. Token not saving
- Check write permissions for
~/.qwen/ directory
- View logs with
ENABLE_PLUGIN_REQUEST_LOGGING=1
Development
Build
npm install
npm run build
Test
npm test
Type Check
npm run typecheck
Lint & Format
npm run lint
npm run format
License
MIT
Repository
Author
Geoff Hammond
Contributing
All contributions (PRs, issues, feedback) are welcome at the GitHub repository.