Sync your existing Claude CLI credentials to OpenCode — no separate Anthropic login needed.
🔧 Getting 429 errors? The old built-in
opencode-anthropic-auth@0.0.13plugin may still be cached and interfering with token refresh. Remove it:rm -rf ~/.cache/opencode/node_modules/opencode-anthropic-authIf it keeps coming back, also remove
opencode-anthropic-authfrom~/.cache/opencode/package.json. Then restart OpenCode and try again.
Why not an npm plugin? When auth breaks, npm packages pop up fast — but installing unknown packages that handle your OAuth tokens is a risk. This tool is a plain shell script you can read in full before running. No
node_modules, no dependency tree, no trust required.
curl -fsSL https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/install.sh | bashirm https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/install.ps1 | iexDon't want a scheduler? Install without automatic syncing:
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/install.sh | bash -s -- --no-scheduler
# Windows (PowerShell)
& { irm https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/install.ps1 -OutFile $env:TEMP\install.ps1; & $env:TEMP\install.ps1 --no-scheduler }Then just run the sync manually whenever you need it:
claude-sync # Linux / macOS (after install)
~/.local/bin/sync-claude-to-opencode.sh # Linux / macOS direct path
claude-sync # Windows (after install)
& "$HOME\.local\bin\sync-claude-to-opencode.ps1" # Windows direct pathopencode providers list # Should show: Anthropic oauth
opencode models anthropic # Should list Claude models (e.g. claude-opus-4-6)# Normal sync (default, also runs via scheduler)
claude-sync
# Check token status without syncing
claude-sync --status
# Force refresh token via Claude CLI regardless of expiry
claude-sync --forceManage multiple Claude accounts with quota visibility, automatic rotation, and the shorter claude-sync command.
Important: Claude CLI itself only supports one logged-in account at a time. Multi-account here means this tool stores multiple credential sets in its own account store, then switches which one is written into OpenCode's auth.json.
Account store:
~/.config/opencode-claude-auth-sync/accounts.json
There are two ways to add an account.
If claude is already authenticated with the account you want to save, just capture the current session:
claude-sync --add personal
claude-sync --add workWindows:
claude-sync --add personal
claude-sync --add workThis is the most reliable path on remote servers because Claude's login flow can be interactive.
Use --login if you want the script to trigger Claude login and then save the result:
claude-sync --login personal
claude-sync --login work
claude-sync --login backupWindows:
claude-sync --login personal
claude-sync --login workEach --login logs out the current Claude session, starts Claude login, then saves the credentials under the given label.
If you're on SSH and --login feels awkward, use this flow instead:
claude
# run /login inside Claude if needed
exit
claude-sync --add work# List stored accounts
claude-sync --list
# Show active account status + current 5h / 7d usage
claude-sync --status
# Switch active account immediately
claude-sync --switch work
# Rotate to the next account (round-robin)
claude-sync --rotate
# Remove a stored account
claude-sync --remove backupExample --status output:
Account: work (2 total)
Status: valid (7h 56m remaining)
Expires: 2026-03-21T10:55:26.162Z
Plan: max
Usage: 5h 2% (reset: 2026-03-21T07:00:00.152Z)
7d 0% (reset: 2026-03-28T02:00:00.153Z)
sonnet 3%
- OpenCode still uses a single Anthropic entry in
auth.json - This tool switches which stored account is written into that slot
- If the active account is expired, the script first tries another non-expired stored account
- If all stored accounts are expired, it falls back to Claude CLI refresh for the currently logged-in Claude account
- 429 rate limits are not auto-detected yet; if one account is rate-limited, run
claude-sync --rotatemanually --statusshows the current account's 5h / 7d usage so you can decide when to rotate- The same Claude account can still be saved under two different labels if you add it twice
{
"accounts": {
"personal": {
"accessToken": "...",
"refreshToken": "...",
"expiresAt": 1774027458398,
"subscriptionType": "max",
"rateLimitTier": "default_claude_max_20x",
"addedAt": "2026-03-20T09:55:32.366Z"
}
},
"active": "personal",
"rotationIndex": 0
}| Platform | Claude credentials | Scheduler | Install command |
|---|---|---|---|
| Linux / WSL | ~/.claude/.credentials.json |
cron | curl | bash |
| macOS | macOS Keychain → file fallback | LaunchAgent | curl | bash |
| Windows (native) | %USERPROFILE%\.claude\.credentials.json |
Task Scheduler | PowerShell |
This tool is not an npm package — it's a plain shell script you can read before running.
- No
node_modules, no dependency tree, no supply chain risk - Single-file scripts:
sync-claude-to-opencode.sh(bash) /.ps1(PowerShell) - Credentials are passed via stdin, never exposed in process arguments
- All JSON writes are atomic (temp file + rename) to prevent corruption
- Review the source before installing:
sync-claude-to-opencode.sh(~850 lines) /.ps1(~620 lines)
# Inspect before running
curl -fsSL https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/sync-claude-to-opencode.sh | less- OpenCode v1.2.27+
- Claude CLI — authenticated (run
claudeat least once) - Node.js (bundled with OpenCode, or standalone)
OpenCode no longer provides built-in Anthropic login. If you want to use Claude models (Opus, Sonnet, Haiku, etc.) in OpenCode, you need to bring your own credentials.
This tool bridges the gap: it reads your existing Claude CLI OAuth tokens and writes them into OpenCode's auth store, letting an opencode-anthropic-auth plugin handle the rest (see v1.3+ compatibility).
┌─────────────────────────┐ sync script ┌─────────────────────────────┐
│ ~/.claude/ │ (launchd/cron/task) │ ~/.local/share/opencode/ │
│ .credentials.json │ ──────────────────▶ │ auth.json │
│ │ │ │
│ claudeAiOauth { │ reads & compares │ anthropic { │
│ accessToken, │ ─────────────────▶ │ type: "oauth", │
│ refreshToken, │ writes if changed │ access: <accessToken>, │
│ expiresAt │ │ refresh: <refreshToken>, │
│ } │ │ expires: <expiresAt> │
│ │ │ } │
└─────────────────────────┘ └─────────────────────────────┘
│
▼
opencode-anthropic-auth plugin
handles token refresh,
request signing, OAuth beta
headers, and API routing.
Credential sources (platform-aware):
| Platform | Claude CLI stores credentials in | How this script reads them |
|---|---|---|
| macOS | macOS Keychain (service: Claude Code-credentials) |
security find-generic-password |
| Linux / WSL / Windows | ~/.claude/.credentials.json |
Direct file read |
Step by step:
- Claude CLI stores OAuth credentials after you run
claudeand authenticate (Keychain on macOS, file on Linux/Windows) - This sync script detects the platform and reads the
claudeAiOauthobject from the appropriate source - If the token is already expired, it automatically runs
claudeCLI to refresh the token before syncing - It compares the
accessToken,refreshToken, andexpiresAtwith what's currently in OpenCode'sauth.json - If they differ (or the Anthropic entry doesn't exist), it writes the new credentials
- If they're identical, it logs the remaining token lifetime and exits (no unnecessary writes)
- Once the credentials are in
auth.json, theopencode-anthropic-authplugin handles everything else: token refresh, request signing, OAuth beta headers, and user-agent
Claude CLI tokens are valid for approximately 5–6 hours. The sync job runs every 15 minutes (LaunchAgent on macOS, cron on Linux, Task Scheduler on Windows). Once a token has expired, the script uses Claude CLI to refresh it and then re-syncs auth.json. On macOS, LaunchAgent catches up on missed runs after sleep/wake.
Paste this to your AI coding agent (Claude Code, OpenCode, Cursor, etc.):
Install opencode-claude-auth-sync from https://github.com/lehdqlsl/opencode-claude-auth-sync
1. Run the install script:
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/install.sh | bash
# Windows (PowerShell as Admin)
irm https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/install.ps1 | iex
2. Check the sync output. If it says "EXPIRED", the stored token is stale.
The script will try to refresh it automatically via Claude CLI. If that still fails, run `claude` manually, then re-run the sync:
# Linux / macOS
claude-sync
# Windows
claude-sync
3. Verify — the output should show remaining time, not EXPIRED:
opencode providers list # Should show: Anthropic oauth
opencode models anthropic # Should list Claude models
4. If you see "Token refresh failed: 429", that means expired tokens were used.
Re-authenticate with `claude` first, then sync again.
5. If already installed and just need to refresh tokens (no reinstall needed):
Run `claude` to re-authenticate, then sync:
# Linux / macOS
claude-sync
# Windows
claude-sync
6. If the user doesn't want a background scheduler, install with --no-scheduler:
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/install.sh | bash -s -- --no-scheduler
# Windows
Download and run install.ps1 with --no-scheduler flag
Then sync manually whenever needed.
mkdir -p ~/.local/bin
curl -fsSL https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/sync-claude-to-opencode.sh \
-o ~/.local/bin/sync-claude-to-opencode.sh
chmod +x ~/.local/bin/sync-claude-to-opencode.sh
ln -sf ~/.local/bin/sync-claude-to-opencode.sh ~/.local/bin/claude-sync
claude-sync(Optional) Set up automatic syncing (every 15 minutes):
# macOS — LaunchAgent (recommended, catches up after sleep)
# Use the install script: curl ... | bash
# Linux — cron
(crontab -l 2>/dev/null; echo "*/15 * * * * \$HOME/.local/bin/sync-claude-to-opencode.sh >> \$HOME/.local/share/opencode/sync-claude.log 2>&1") | crontab -New-Item -ItemType Directory -Force -Path "$HOME\.local\bin" | Out-Null
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/sync-claude-to-opencode.ps1" `
-OutFile "$HOME\.local\bin\sync-claude-to-opencode.ps1"
@"
@echo off
setlocal
powershell.exe -ExecutionPolicy Bypass -File "%~dp0sync-claude-to-opencode.ps1" %*
"@ | Set-Content -Path "$HOME\.local\bin\claude-sync.cmd"
claude-sync| Environment Variable | Default | Description |
|---|---|---|
CLAUDE_CREDENTIALS_PATH |
~/.claude/.credentials.json (Linux/Win) or Keychain (macOS) |
Path to Claude CLI credentials |
OPENCODE_AUTH_PATH |
~/.local/share/opencode/auth.json |
Path to OpenCode auth store |
curl -fsSL https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/uninstall.sh | bashirm https://raw.githubusercontent.com/lehdqlsl/opencode-claude-auth-sync/main/uninstall.ps1 | iexIf you're using opencode-claude-auth (v0.5+), you don't need this tool — that plugin syncs credentials in-process. Choose one or the other, not both.
Early versions (v0.2.x) had issues that have since been fixed. If you're on an old version, update or remove it.
The sync script attempts an automatic refresh via Claude CLI once the token is expired. If Claude CLI can refresh successfully, the next sync writes fresh credentials back to OpenCode.
Note: If the token expired while OpenCode was running, you may need to restart OpenCode after the sync to pick up the new credentials. This is rare — normally Claude CLI refreshes tokens before they expire, so OpenCode reads them seamlessly.
If auto-refresh fails (e.g. claude CLI not in PATH, or network issues):
- Re-authenticate manually:
claude
- Re-run the sync:
# Linux / macOS claude-sync # Windows claude-sync
This means OpenCode tried to use an expired token. The sync script's auto-refresh should prevent this, but if it occurs, re-authenticate with claude and sync again.
If the deprecated built-in plugin keeps being reinstalled, remove both:
rm -rf ~/.cache/opencode/node_modules/opencode-anthropic-authand the opencode-anthropic-auth dependency entry from ~/.cache/opencode/package.json, then restart OpenCode.
If you're on OpenCode v1.2.27 and the deprecated plugin keeps coming back on every startup, that's an upstream built-in plugin issue. A practical CLI-side workaround is:
- Start OpenCode with
OPENCODE_DISABLE_DEFAULT_PLUGINS=true - Explicitly register
opencode-claude-auth@latestinopencode.json
Example:
{
"plugin": [
"opencode-claude-auth@latest"
]
}This disables the old built-in plugin injection while still loading a Claude auth provider explicitly.
Check the sync history:
cat ~/.local/share/opencode/sync-claude.logOpenCode v1.3 removes the built-in opencode-anthropic-auth plugin (PR #18186) per Anthropic's legal request. This tool depends on that plugin to handle token refresh and request signing.
You need to register an auth plugin manually in your opencode.json. Pick one:
| Plugin | Install | Notes |
|---|---|---|
@ex-machina/opencode-anthropic-auth |
npm i -g @ex-machina/opencode-anthropic-auth |
TypeScript rewrite, fixes 429 bug |
op-anthropic-auth |
npm i -g op-anthropic-auth |
Fork of the original |
opencode-anthropic-auth@0.0.13 |
npm i -g opencode-anthropic-auth@0.0.13 |
Original (deprecated) |
Then add to opencode.json:
{
"plugin": ["@ex-machina/opencode-anthropic-auth"]
}If the npm packages get unpublished, this repo includes a bundled copy of the original plugin (opencode-anthropic-auth-0.0.13.tgz). Extract it and reference the local file:
{
"plugin": ["/path/to/index.mjs"]
}This tool (opencode-claude-auth-sync) itself only copies credentials and has no legal concerns. The compatibility risk is with the auth plugin that actually uses them.
MIT