opencode-claude-max-proxy 1.7.3 → 1.8.1
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/README.md +53 -4
- package/bin/docker-auth.sh +77 -0
- package/bin/docker-entrypoint.sh +24 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,9 +59,17 @@ No monkey-patching. No forked SDKs. No fragile stream rewriting. Just a hook and
|
|
|
59
59
|
|
|
60
60
|
1. **Claude Max subscription** — [Subscribe here](https://claude.ai/settings/subscription)
|
|
61
61
|
2. **Claude CLI** authenticated: `npm install -g @anthropic-ai/claude-code && claude login`
|
|
62
|
-
3. **Bun** runtime: `curl -fsSL https://bun.sh/install | bash`
|
|
63
62
|
|
|
64
|
-
###
|
|
63
|
+
### Option A: npm Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install -g opencode-claude-max-proxy
|
|
67
|
+
|
|
68
|
+
# Start in passthrough mode (recommended)
|
|
69
|
+
CLAUDE_PROXY_PASSTHROUGH=1 claude-max-proxy
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Option B: From Source
|
|
65
73
|
|
|
66
74
|
```bash
|
|
67
75
|
git clone https://github.com/rynfar/opencode-claude-max-proxy
|
|
@@ -72,21 +80,62 @@ bun install
|
|
|
72
80
|
CLAUDE_PROXY_PASSTHROUGH=1 bun run proxy
|
|
73
81
|
```
|
|
74
82
|
|
|
83
|
+
> **Note:** Running from source requires [Bun](https://bun.sh): `curl -fsSL https://bun.sh/install | bash`
|
|
84
|
+
|
|
85
|
+
### Option C: Docker
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
git clone https://github.com/rynfar/opencode-claude-max-proxy
|
|
89
|
+
cd opencode-claude-max-proxy
|
|
90
|
+
|
|
91
|
+
# Start the container
|
|
92
|
+
docker compose up -d
|
|
93
|
+
|
|
94
|
+
# Login to Claude inside the container (one-time)
|
|
95
|
+
docker compose exec proxy claude login
|
|
96
|
+
|
|
97
|
+
# Verify
|
|
98
|
+
curl http://127.0.0.1:3456/health
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
> **Note:** On macOS, `claude login` must be run inside the container (keychain credentials can't be mounted). On Linux, volume-mounting `~/.claude` may work without re-login.
|
|
102
|
+
|
|
75
103
|
### Connect OpenCode
|
|
76
104
|
|
|
105
|
+
#### Environment Variables
|
|
106
|
+
|
|
77
107
|
```bash
|
|
78
108
|
ANTHROPIC_API_KEY=dummy ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode
|
|
79
109
|
```
|
|
80
110
|
|
|
81
111
|
The `ANTHROPIC_API_KEY` can be any non-empty string — the proxy doesn't use it. Authentication is handled by your `claude login` session.
|
|
82
112
|
|
|
83
|
-
|
|
113
|
+
#### Shell Alias
|
|
84
114
|
|
|
85
115
|
```bash
|
|
86
|
-
# Add to ~/.zshrc or ~/.bashrc
|
|
116
|
+
# Add to ~/.zshrc or ~/.bashrc or ~/.config/fish/config.fish
|
|
87
117
|
alias oc='ANTHROPIC_API_KEY=dummy ANTHROPIC_BASE_URL=http://127.0.0.1:3456 opencode'
|
|
88
118
|
```
|
|
89
119
|
|
|
120
|
+
#### OpenCode Config File
|
|
121
|
+
|
|
122
|
+
Alternatively, the proxy URL and API key can be set globally in `~/.config/opencode/opencode.json`. This has the benefit of working in OpenCode Desktop as well.
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
...
|
|
127
|
+
"provider": {
|
|
128
|
+
"anthropic": {
|
|
129
|
+
"options": {
|
|
130
|
+
"baseURL": "http://127.0.0.1:3456",
|
|
131
|
+
"apiKey": "dummy"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
...
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
90
139
|
## Modes
|
|
91
140
|
|
|
92
141
|
### Passthrough Mode (recommended)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Copy Claude credentials from host into Docker container.
|
|
3
|
+
#
|
|
4
|
+
# macOS stores scopes in Keychain, so the credentials file has scopes: ""
|
|
5
|
+
# Linux Claude CLI needs scopes as an array. This script fixes the format.
|
|
6
|
+
#
|
|
7
|
+
# Usage: ./bin/docker-auth.sh
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
cd "$SCRIPT_DIR/.."
|
|
13
|
+
|
|
14
|
+
HOST_CREDS="$HOME/.claude/.credentials.json"
|
|
15
|
+
|
|
16
|
+
if [ ! -f "$HOST_CREDS" ]; then
|
|
17
|
+
echo "❌ No credentials found at $HOST_CREDS"
|
|
18
|
+
echo " Run 'claude login' on your host first."
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Check if host is logged in
|
|
23
|
+
if ! claude auth status 2>/dev/null | grep -q '"loggedIn": true'; then
|
|
24
|
+
echo "❌ Not logged in on host. Run 'claude login' first."
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
echo "📋 Reading credentials from host..."
|
|
29
|
+
|
|
30
|
+
# Fix the scopes field and copy into container
|
|
31
|
+
python3 -c "
|
|
32
|
+
import json, sys
|
|
33
|
+
|
|
34
|
+
with open('$HOST_CREDS') as f:
|
|
35
|
+
creds = json.load(f)
|
|
36
|
+
|
|
37
|
+
oauth = creds.get('claudeAiOauth', {})
|
|
38
|
+
if not oauth.get('accessToken'):
|
|
39
|
+
print('❌ No access token found in credentials')
|
|
40
|
+
sys.exit(1)
|
|
41
|
+
|
|
42
|
+
# Fix scopes: empty string -> proper array
|
|
43
|
+
if not oauth.get('scopes') or oauth['scopes'] == '':
|
|
44
|
+
oauth['scopes'] = [
|
|
45
|
+
'user:profile',
|
|
46
|
+
'user:inference',
|
|
47
|
+
'user:sessions:claude_code',
|
|
48
|
+
'user:mcp_servers',
|
|
49
|
+
'user:file_upload'
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
creds['claudeAiOauth'] = oauth
|
|
53
|
+
print(json.dumps(creds))
|
|
54
|
+
" | docker compose exec -T proxy bash -c 'cat > /home/claude/.claude/.credentials.json'
|
|
55
|
+
|
|
56
|
+
if [ $? -ne 0 ]; then
|
|
57
|
+
echo "❌ Failed to copy credentials. Is the container running?"
|
|
58
|
+
echo " Run 'docker compose up -d' first."
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Also copy .claude.json if it exists
|
|
63
|
+
if [ -f "$HOME/.claude/.claude.json" ]; then
|
|
64
|
+
docker compose exec -T proxy bash -c 'cat > /home/claude/.claude/.claude.json' < "$HOME/.claude/.claude.json"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Verify
|
|
68
|
+
echo "🔍 Verifying..."
|
|
69
|
+
AUTH=$(docker compose exec proxy claude auth status 2>&1)
|
|
70
|
+
if echo "$AUTH" | grep -q '"loggedIn": true'; then
|
|
71
|
+
EMAIL=$(echo "$AUTH" | grep -o '"email": "[^"]*"' | head -1)
|
|
72
|
+
echo "✅ Docker container authenticated! $EMAIL"
|
|
73
|
+
else
|
|
74
|
+
echo "❌ Authentication failed inside container"
|
|
75
|
+
echo "$AUTH"
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Docker entrypoint:
|
|
3
|
+
# 1. Fix volume permissions (created as root, need claude ownership)
|
|
4
|
+
# 2. Symlink .claude.json into persistent volume
|
|
5
|
+
|
|
6
|
+
CLAUDE_DIR="/home/claude/.claude"
|
|
7
|
+
CLAUDE_JSON="/home/claude/.claude.json"
|
|
8
|
+
CLAUDE_JSON_VOL="$CLAUDE_DIR/.claude.json"
|
|
9
|
+
|
|
10
|
+
# Fix ownership if volume was created as root
|
|
11
|
+
if [ -d "$CLAUDE_DIR" ] && [ ! -w "$CLAUDE_DIR" ]; then
|
|
12
|
+
echo "[entrypoint] Fixing volume permissions..."
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Symlink .claude.json into volume so it persists across restarts
|
|
16
|
+
if [ -f "$CLAUDE_JSON_VOL" ] && [ ! -f "$CLAUDE_JSON" ]; then
|
|
17
|
+
ln -sf "$CLAUDE_JSON_VOL" "$CLAUDE_JSON"
|
|
18
|
+
elif [ -f "$CLAUDE_JSON" ] && [ ! -L "$CLAUDE_JSON" ] && [ -w "$CLAUDE_DIR" ]; then
|
|
19
|
+
cp "$CLAUDE_JSON" "$CLAUDE_JSON_VOL" 2>/dev/null
|
|
20
|
+
rm -f "$CLAUDE_JSON"
|
|
21
|
+
ln -sf "$CLAUDE_JSON_VOL" "$CLAUDE_JSON"
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
exec "$@"
|