ocb-cli 1.0.0
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 +66 -0
- package/dist/cli.js +225 -0
- package/dist/proxy.js +504 -0
- package/package.json +37 -0
- package/src/cli.ts +274 -0
- package/src/proxy.ts +546 -0
- package/tsconfig.json +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# OCB - OpenCode Bridge
|
|
2
|
+
|
|
3
|
+
**Use OpenCode AI models directly in Claude Code!**
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Clone the repo
|
|
9
|
+
git clone https://github.com/veokhan/ocb.git
|
|
10
|
+
cd ocb
|
|
11
|
+
|
|
12
|
+
# Install dependencies
|
|
13
|
+
npm install
|
|
14
|
+
|
|
15
|
+
# Build
|
|
16
|
+
npm run build
|
|
17
|
+
|
|
18
|
+
# Start the server
|
|
19
|
+
npm run start
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage Commands
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Start server only
|
|
26
|
+
npm run start
|
|
27
|
+
|
|
28
|
+
# Stop server
|
|
29
|
+
npm run stop
|
|
30
|
+
|
|
31
|
+
# Setup Claude Code config
|
|
32
|
+
npm run cli -- setup
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Or Use npx (No Install Needed)
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Clone
|
|
39
|
+
git clone https://github.com/veokhan/ocb.git
|
|
40
|
+
cd ocb
|
|
41
|
+
|
|
42
|
+
# Install deps
|
|
43
|
+
npm install
|
|
44
|
+
|
|
45
|
+
# Use via npx
|
|
46
|
+
npx tsx src/cli.ts start
|
|
47
|
+
npx tsx src/cli.ts setup
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## After Running
|
|
51
|
+
|
|
52
|
+
1. **Dashboard:** http://localhost:8300
|
|
53
|
+
2. **Use Claude Code:**
|
|
54
|
+
```bash
|
|
55
|
+
claude --print
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Features
|
|
59
|
+
|
|
60
|
+
- š **2540+ Models** from 89 providers
|
|
61
|
+
- š **Usage Tracking**
|
|
62
|
+
- ā” **Auto-Configuration**
|
|
63
|
+
|
|
64
|
+
## GitHub
|
|
65
|
+
|
|
66
|
+
https://github.com/veokhan/ocb
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn, execSync } from "child_process";
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
4
|
+
import { join, dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
const PROXY_PORT = 8300;
|
|
9
|
+
const OPENCODE_PORT = 4096;
|
|
10
|
+
const isWindows = process.platform === 'win32';
|
|
11
|
+
const OPENCODE_CMD = isWindows ? 'opencode.cmd' : 'opencode';
|
|
12
|
+
const colors = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
bright: '\x1b[1m',
|
|
15
|
+
green: '\x1b[32m',
|
|
16
|
+
yellow: '\x1b[33m',
|
|
17
|
+
blue: '\x1b[34m',
|
|
18
|
+
cyan: '\x1b[36m'
|
|
19
|
+
};
|
|
20
|
+
function log(msg, color = colors.reset) {
|
|
21
|
+
console.log(`${color}${msg}${colors.reset}`);
|
|
22
|
+
}
|
|
23
|
+
function getHomeDir() {
|
|
24
|
+
return process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || '';
|
|
25
|
+
}
|
|
26
|
+
function getClaudeSettingsPath() {
|
|
27
|
+
const home = getHomeDir();
|
|
28
|
+
return join(home, '.claude', 'settings.json');
|
|
29
|
+
}
|
|
30
|
+
function getOpencodeSettingsPath() {
|
|
31
|
+
const home = getHomeDir();
|
|
32
|
+
return join(home, '.config', 'opencode', 'opencode.json');
|
|
33
|
+
}
|
|
34
|
+
function ensureDir(path) {
|
|
35
|
+
const dir = dirname(path);
|
|
36
|
+
if (!existsSync(dir)) {
|
|
37
|
+
mkdirSync(dir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function readJson(path) {
|
|
41
|
+
try {
|
|
42
|
+
if (existsSync(path)) {
|
|
43
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (e) { }
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
function writeJson(path, data) {
|
|
50
|
+
ensureDir(path);
|
|
51
|
+
writeFileSync(path, JSON.stringify(data, null, 2));
|
|
52
|
+
}
|
|
53
|
+
async function configureClaudeCode(port) {
|
|
54
|
+
log('\nš Configuring Claude Code...', colors.cyan);
|
|
55
|
+
const settingsPath = getClaudeSettingsPath();
|
|
56
|
+
const settings = readJson(settingsPath);
|
|
57
|
+
settings.env = settings.env || {};
|
|
58
|
+
settings.env.ANTHROPIC_BASE_URL = `http://localhost:${port}`;
|
|
59
|
+
settings.env.ANTHROPIC_API_KEY = 'test-key';
|
|
60
|
+
writeJson(settingsPath, settings);
|
|
61
|
+
log('ā
Claude Code configured!', colors.green);
|
|
62
|
+
log(` Settings: ${settingsPath}`, colors.yellow);
|
|
63
|
+
}
|
|
64
|
+
async function unconfigureClaudeCode() {
|
|
65
|
+
log('\nš Removing Claude Code configuration...', colors.cyan);
|
|
66
|
+
const settingsPath = getClaudeSettingsPath();
|
|
67
|
+
const settings = readJson(settingsPath);
|
|
68
|
+
if (settings.env) {
|
|
69
|
+
delete settings.env.ANTHROPIC_BASE_URL;
|
|
70
|
+
delete settings.env.ANTHROPIC_API_KEY;
|
|
71
|
+
}
|
|
72
|
+
if (Object.keys(settings.env || {}).length === 0) {
|
|
73
|
+
delete settings.env;
|
|
74
|
+
}
|
|
75
|
+
if (Object.keys(settings).length > 0) {
|
|
76
|
+
writeJson(settingsPath, settings);
|
|
77
|
+
}
|
|
78
|
+
else if (existsSync(settingsPath)) {
|
|
79
|
+
const fs = await import('fs');
|
|
80
|
+
fs.unlinkSync(settingsPath);
|
|
81
|
+
}
|
|
82
|
+
log('ā
Claude Code configuration removed!', colors.green);
|
|
83
|
+
}
|
|
84
|
+
function checkOpenCode() {
|
|
85
|
+
log('\nš Checking OpenCode...', colors.cyan);
|
|
86
|
+
try {
|
|
87
|
+
const result = execSync(OPENCODE_CMD + ' --version', { encoding: 'utf-8' });
|
|
88
|
+
log(`ā
OpenCode found: ${result.trim()}`, colors.green);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
log('ā OpenCode not found!', colors.yellow);
|
|
93
|
+
log(' Install: npm install -g opencode-ai', colors.yellow);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function startOpenCode() {
|
|
98
|
+
log('\nš Starting OpenCode server...', colors.cyan);
|
|
99
|
+
return new Promise((resolve) => {
|
|
100
|
+
const proc = spawn(OPENCODE_CMD, ['serve', '--port', OPENCODE_PORT.toString()], {
|
|
101
|
+
stdio: 'pipe',
|
|
102
|
+
detached: false,
|
|
103
|
+
shell: true
|
|
104
|
+
});
|
|
105
|
+
proc.stdout.on('data', (data) => {
|
|
106
|
+
if (data.toString().includes('listening')) {
|
|
107
|
+
log('ā
OpenCode server running on port ' + OPENCODE_PORT, colors.green);
|
|
108
|
+
resolve(proc);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
proc.stderr.on('data', (data) => {
|
|
112
|
+
if (data.toString().includes('listening')) {
|
|
113
|
+
log('ā
OpenCode server running on port ' + OPENCODE_PORT, colors.green);
|
|
114
|
+
resolve(proc);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
setTimeout(() => resolve(proc), 3000);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function startProxy() {
|
|
121
|
+
log('\nš Starting Bridge server...', colors.cyan);
|
|
122
|
+
const proxyPath = join(__dirname, 'proxy.js');
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
const proc = spawn('node', [proxyPath], {
|
|
125
|
+
stdio: 'pipe',
|
|
126
|
+
env: { ...process.env, PROXY_PORT: PROXY_PORT.toString() },
|
|
127
|
+
detached: false
|
|
128
|
+
});
|
|
129
|
+
proc.stdout.on('data', (data) => {
|
|
130
|
+
if (data.toString().includes('running')) {
|
|
131
|
+
log('ā
Bridge server running!', colors.green);
|
|
132
|
+
resolve(proc);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
proc.stderr.on('data', (data) => {
|
|
136
|
+
const str = data.toString();
|
|
137
|
+
if (str.includes('running')) {
|
|
138
|
+
log('ā
Bridge server running!', colors.green);
|
|
139
|
+
resolve(proc);
|
|
140
|
+
}
|
|
141
|
+
console.error(str);
|
|
142
|
+
});
|
|
143
|
+
setTimeout(() => resolve(proc), 2000);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async function setup() {
|
|
147
|
+
log('\nš ļø Setting up OpenCode Bridge...', colors.blue);
|
|
148
|
+
// Check if OpenCode is installed
|
|
149
|
+
if (!checkOpenCode()) {
|
|
150
|
+
log('\nā ļø Please install OpenCode first:', colors.yellow);
|
|
151
|
+
log(' npm install -g opencode-ai', colors.yellow);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
// Configure Claude Code
|
|
155
|
+
await configureClaudeCode(PROXY_PORT);
|
|
156
|
+
log('\nā
Setup complete!', colors.green);
|
|
157
|
+
}
|
|
158
|
+
async function start() {
|
|
159
|
+
log('\nš Starting OpenCode Bridge...', colors.blue);
|
|
160
|
+
// Check if OpenCode is installed
|
|
161
|
+
if (!checkOpenCode()) {
|
|
162
|
+
log('\nā ļø Please install OpenCode first:', colors.yellow);
|
|
163
|
+
log(' npm install -g opencode-ai', colors.yellow);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
// Start OpenCode server
|
|
167
|
+
await startOpenCode();
|
|
168
|
+
// Start proxy
|
|
169
|
+
await startProxy();
|
|
170
|
+
log('\n' + '='.repeat(50), colors.green);
|
|
171
|
+
log('š All services running!', colors.green);
|
|
172
|
+
log('='.repeat(50), colors.green);
|
|
173
|
+
log('\nš Dashboard: http://localhost:' + PROXY_PORT, colors.cyan);
|
|
174
|
+
log('š¤ Claude Code: claude --print', colors.cyan);
|
|
175
|
+
log('\nPress Ctrl+C to stop all services\n', colors.yellow);
|
|
176
|
+
}
|
|
177
|
+
async function stop() {
|
|
178
|
+
log('\nš Stopping OpenCode Bridge...', colors.yellow);
|
|
179
|
+
// Kill processes on ports
|
|
180
|
+
try {
|
|
181
|
+
execSync('taskkill /F /IM node.exe 2>nul', { stdio: 'ignore' });
|
|
182
|
+
}
|
|
183
|
+
catch (e) { }
|
|
184
|
+
try {
|
|
185
|
+
execSync('taskkill /F /IM opencode.exe 2>nul', { stdio: 'ignore' });
|
|
186
|
+
}
|
|
187
|
+
catch (e) { }
|
|
188
|
+
log('ā
Services stopped!', colors.green);
|
|
189
|
+
}
|
|
190
|
+
async function remove() {
|
|
191
|
+
log('\nšļø Removing OpenCode Bridge...', colors.yellow);
|
|
192
|
+
// Unconfigure Claude Code
|
|
193
|
+
await unconfigureClaudeCode();
|
|
194
|
+
log('ā
Bridge removed from Claude Code!', colors.green);
|
|
195
|
+
}
|
|
196
|
+
// CLI Commands
|
|
197
|
+
const command = process.argv[2];
|
|
198
|
+
switch (command) {
|
|
199
|
+
case 'setup':
|
|
200
|
+
setup();
|
|
201
|
+
break;
|
|
202
|
+
case 'start':
|
|
203
|
+
start();
|
|
204
|
+
break;
|
|
205
|
+
case 'stop':
|
|
206
|
+
stop();
|
|
207
|
+
break;
|
|
208
|
+
case 'remove':
|
|
209
|
+
remove();
|
|
210
|
+
break;
|
|
211
|
+
case 'install':
|
|
212
|
+
setup().then(() => start());
|
|
213
|
+
break;
|
|
214
|
+
default:
|
|
215
|
+
log('\nš OCB - OpenCode Bridge CLI', colors.blue);
|
|
216
|
+
log('\nUsage:', colors.cyan);
|
|
217
|
+
log(' ocb setup - Configure Claude Code', colors.reset);
|
|
218
|
+
log(' ocb start - Start all services', colors.reset);
|
|
219
|
+
log(' ocb install - Setup + Start (all in one)', colors.reset);
|
|
220
|
+
log(' ocb stop - Stop all services', colors.reset);
|
|
221
|
+
log(' ocb remove - Remove configuration', colors.reset);
|
|
222
|
+
log('\nOr run with npx:', colors.yellow);
|
|
223
|
+
log(' npx ocb install\n', colors.yellow);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|