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