ocb-cli 1.0.2 → 1.0.4

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/dist/proxy.js CHANGED
@@ -19,7 +19,9 @@ async function fetchModelsFromOpenCode() {
19
19
  if (data.all) {
20
20
  providers = {};
21
21
  availableModels = [];
22
- for (const [providerID, providerData] of Object.entries(data.all)) {
22
+ const providerList = Array.isArray(data.all) ? data.all : Object.values(data.all);
23
+ for (const providerData of providerList) {
24
+ const providerID = providerData.id || providerData.providerID;
23
25
  const p = providerData;
24
26
  providers[providerID] = { name: p.name, source: p.source };
25
27
  if (p.models) {
@@ -109,7 +111,8 @@ app.use(express.json());
109
111
  app.use((req, res, next) => {
110
112
  res.header("Access-Control-Allow-Origin", "*");
111
113
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
112
- res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
114
+ res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-api-key");
115
+ res.header("Content-Type", "application/json");
113
116
  if (req.method === "OPTIONS")
114
117
  return res.sendStatus(200);
115
118
  next();
@@ -158,16 +161,77 @@ app.post("/api/reset-stats", (req, res) => {
158
161
  totalRequests = 0;
159
162
  res.json({ success: true });
160
163
  });
164
+ const modelAliases = {
165
+ "claude-opus-4-5": "opencode/minimax-m2.5-free",
166
+ "claude-opus-4-5-thinking": "opencode/minimax-m2.5-free",
167
+ "claude-opus-4-6": "opencode/minimax-m2.5-free",
168
+ "claude-opus-4-6-thinking": "opencode/minimax-m2.5-free",
169
+ "claude-sonnet-4-5": "opencode/minimax-m2.5-free",
170
+ "claude-sonnet-4-5-thinking": "opencode/minimax-m2.5-free",
171
+ "claude-sonnet-4-5-20250929": "opencode/minimax-m2.5-free",
172
+ "claude-sonnet-4-5-20250929-thinking": "opencode/minimax-m2.5-free",
173
+ "claude-haiku-4-5": "opencode/minimax-m2.5-free",
174
+ "claude-haiku-4-5-20251001": "opencode/minimax-m2.5-free",
175
+ };
161
176
  app.post("/api/reset-session", async (req, res) => {
162
177
  currentSessionId = await createSession(process.cwd());
163
178
  res.json({ success: true, sessionId: currentSessionId });
164
179
  });
165
180
  app.get("/v1/authenticate", (req, res) => res.json({ type: "authentication", authenticated: true }));
166
181
  app.get("/v1/whoami", (req, res) => res.json({ type: "user", id: "opencode-user", email: "opencode@local" }));
167
- app.get("/v1/models", (req, res) => res.json({ data: availableModels.map(m => ({ id: m.id, type: "model", name: m.name, supports_cached_previews: true, supports_system_instructions: true })) }));
168
- app.get("/v1/models/list", (req, res) => res.json({ data: availableModels.map(m => ({ id: m.id, type: "model", name: m.name, supports_cached_previews: true, supports_system_instructions: true })) }));
182
+ app.get("/v1/models", (req, res) => {
183
+ const aliasModels = Object.keys(modelAliases).map(id => ({
184
+ id,
185
+ type: "model",
186
+ name: id,
187
+ display_name: id,
188
+ supports_cached_previews: true,
189
+ supports_system_instructions: true,
190
+ supports_reasoning: true,
191
+ supports_vision: false
192
+ }));
193
+ const allModels = [...aliasModels, ...availableModels.map(m => ({
194
+ id: m.id,
195
+ type: "model",
196
+ name: m.name,
197
+ display_name: m.name,
198
+ supports_cached_previews: true,
199
+ supports_system_instructions: true,
200
+ supports_reasoning: true,
201
+ supports_vision: false
202
+ }))];
203
+ res.json({ data: allModels });
204
+ });
205
+ app.get("/v1/models/list", (req, res) => {
206
+ const aliasModels = Object.keys(modelAliases).map(id => ({
207
+ id,
208
+ type: "model",
209
+ name: id,
210
+ display_name: id,
211
+ supports_cached_previews: true,
212
+ supports_system_instructions: true,
213
+ supports_reasoning: true,
214
+ supports_vision: false
215
+ }));
216
+ const allModels = [...aliasModels, ...availableModels.map(m => ({
217
+ id: m.id,
218
+ type: "model",
219
+ name: m.name,
220
+ display_name: m.name,
221
+ supports_cached_previews: true,
222
+ supports_system_instructions: true,
223
+ supports_reasoning: true,
224
+ supports_vision: false
225
+ }))];
226
+ res.json({ data: allModels });
227
+ });
169
228
  app.post("/v1/messages", async (req, res) => {
170
229
  try {
230
+ const requestedModel = req.body?.model || currentModel;
231
+ const actualModel = modelAliases[requestedModel] || requestedModel;
232
+ if (modelAliases[requestedModel]) {
233
+ currentModel = modelAliases[requestedModel];
234
+ }
171
235
  if (req.body?.max_tokens === undefined && req.body?.messages) {
172
236
  let totalTokens = 0;
173
237
  for (const msg of req.body.messages) {
@@ -180,7 +244,7 @@ app.post("/v1/messages", async (req, res) => {
180
244
  const { text, tokens } = await sendMessage(currentSessionId, req.body.messages);
181
245
  totalRequests++;
182
246
  totalTokensUsed += tokens;
183
- res.json({ id: `msg_${Date.now()}`, type: "message", role: "assistant", content: [{ type: "text", text }], model: currentModel, stop_reason: "end_turn", usage: { input_tokens: Math.ceil(JSON.stringify(req.body.messages).length / 4), output_tokens: Math.ceil(text.length / 4) } });
247
+ res.json({ id: `msg_${Date.now()}`, type: "message", role: "assistant", content: [{ type: "text", text }], model: actualModel, stop_reason: "end_turn", usage: { input_tokens: Math.ceil(JSON.stringify(req.body.messages).length / 4), output_tokens: Math.ceil(text.length / 4) } });
184
248
  }
185
249
  catch (error) {
186
250
  res.status(500).json({ error: { type: "api_error", message: error instanceof Error ? error.message : String(error) } });
@@ -330,9 +394,9 @@ function generateHTML() {
330
394
  <div class="bg-zinc-950 rounded-xl p-4 font-mono text-sm text-zinc-300 overflow-x-auto">
331
395
  <pre id="configJson">{
332
396
  "env": {
333
- "ANTHROPIC_BASE_URL": "http://localhost:8300/v1",
334
- "ANTHROPIC_AUTH_TOKEN": "test",
335
- "ANTHROPIC_MODEL": "minimax-m2.5-free"
397
+ "ANTHROPIC_BASE_URL": "http://localhost:8300",
398
+ "ANTHROPIC_API_KEY": "test",
399
+ "ANTHROPIC_MODEL": "claude-sonnet-4-5"
336
400
  }
337
401
  }</pre>
338
402
  </div>
@@ -460,7 +524,7 @@ function generateHTML() {
460
524
  currentModelId = modelId;
461
525
  loadStatus();
462
526
  renderModels(currentProvider === 'all' ? allModels : (groupedModels[currentProvider] || []));
463
- document.getElementById('configJson').textContent = JSON.stringify({ env: { ANTHROPIC_BASE_URL: "http://localhost:8300/v1", ANTHROPIC_AUTH_TOKEN: "test", ANTHROPIC_MODEL: modelId } }, null, 2);
527
+ document.getElementById('configJson').textContent = JSON.stringify({ env: { ANTHROPIC_BASE_URL: "http://localhost:8300", ANTHROPIC_API_KEY: "test", ANTHROPIC_MODEL: modelId } }, null, 2);
464
528
  }
465
529
  }
466
530
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ocb-cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "OpenCode Bridge - Use OpenCode AI models in Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/proxy.js",
package/src/proxy.ts CHANGED
@@ -34,7 +34,10 @@ async function fetchModelsFromOpenCode() {
34
34
  providers = {};
35
35
  availableModels = [];
36
36
 
37
- for (const [providerID, providerData] of Object.entries(data.all as Record<string, any>)) {
37
+ const providerList = Array.isArray(data.all) ? data.all : Object.values(data.all);
38
+
39
+ for (const providerData of providerList) {
40
+ const providerID = providerData.id || providerData.providerID;
38
41
  const p = providerData as { name: string; source: string; models: Record<string, any> };
39
42
  providers[providerID] = { name: p.name, source: p.source };
40
43
 
@@ -136,7 +139,8 @@ app.use(express.json());
136
139
  app.use((req, res, next) => {
137
140
  res.header("Access-Control-Allow-Origin", "*");
138
141
  res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
139
- res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
142
+ res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-api-key");
143
+ res.header("Content-Type", "application/json");
140
144
  if (req.method === "OPTIONS") return res.sendStatus(200);
141
145
  next();
142
146
  });
@@ -190,6 +194,19 @@ app.post("/api/reset-stats", (req, res) => {
190
194
  res.json({ success: true });
191
195
  });
192
196
 
197
+ const modelAliases: Record<string, string> = {
198
+ "claude-opus-4-5": "opencode/minimax-m2.5-free",
199
+ "claude-opus-4-5-thinking": "opencode/minimax-m2.5-free",
200
+ "claude-opus-4-6": "opencode/minimax-m2.5-free",
201
+ "claude-opus-4-6-thinking": "opencode/minimax-m2.5-free",
202
+ "claude-sonnet-4-5": "opencode/minimax-m2.5-free",
203
+ "claude-sonnet-4-5-thinking": "opencode/minimax-m2.5-free",
204
+ "claude-sonnet-4-5-20250929": "opencode/minimax-m2.5-free",
205
+ "claude-sonnet-4-5-20250929-thinking": "opencode/minimax-m2.5-free",
206
+ "claude-haiku-4-5": "opencode/minimax-m2.5-free",
207
+ "claude-haiku-4-5-20251001": "opencode/minimax-m2.5-free",
208
+ };
209
+
193
210
  app.post("/api/reset-session", async (req, res) => {
194
211
  currentSessionId = await createSession(process.cwd());
195
212
  res.json({ success: true, sessionId: currentSessionId });
@@ -198,11 +215,62 @@ app.post("/api/reset-session", async (req, res) => {
198
215
  app.get("/v1/authenticate", (req, res) => res.json({ type: "authentication", authenticated: true }));
199
216
  app.get("/v1/whoami", (req, res) => res.json({ type: "user", id: "opencode-user", email: "opencode@local" }));
200
217
 
201
- app.get("/v1/models", (req, res) => res.json({ data: availableModels.map(m => ({ id: m.id, type: "model", name: m.name, supports_cached_previews: true, supports_system_instructions: true })) }));
202
- app.get("/v1/models/list", (req, res) => res.json({ data: availableModels.map(m => ({ id: m.id, type: "model", name: m.name, supports_cached_previews: true, supports_system_instructions: true })) }));
218
+ app.get("/v1/models", (req, res) => {
219
+ const aliasModels = Object.keys(modelAliases).map(id => ({
220
+ id,
221
+ type: "model" as const,
222
+ name: id,
223
+ display_name: id,
224
+ supports_cached_previews: true,
225
+ supports_system_instructions: true,
226
+ supports_reasoning: true,
227
+ supports_vision: false
228
+ }));
229
+ const allModels = [...aliasModels, ...availableModels.map(m => ({
230
+ id: m.id,
231
+ type: "model" as const,
232
+ name: m.name,
233
+ display_name: m.name,
234
+ supports_cached_previews: true,
235
+ supports_system_instructions: true,
236
+ supports_reasoning: true,
237
+ supports_vision: false
238
+ }))];
239
+ res.json({ data: allModels });
240
+ });
241
+ app.get("/v1/models/list", (req, res) => {
242
+ const aliasModels = Object.keys(modelAliases).map(id => ({
243
+ id,
244
+ type: "model" as const,
245
+ name: id,
246
+ display_name: id,
247
+ supports_cached_previews: true,
248
+ supports_system_instructions: true,
249
+ supports_reasoning: true,
250
+ supports_vision: false
251
+ }));
252
+ const allModels = [...aliasModels, ...availableModels.map(m => ({
253
+ id: m.id,
254
+ type: "model" as const,
255
+ name: m.name,
256
+ display_name: m.name,
257
+ supports_cached_previews: true,
258
+ supports_system_instructions: true,
259
+ supports_reasoning: true,
260
+ supports_vision: false
261
+ }))];
262
+ res.json({ data: allModels });
263
+ });
203
264
 
204
265
  app.post("/v1/messages", async (req, res) => {
205
266
  try {
267
+ const requestedModel = req.body?.model || currentModel;
268
+ const actualModel = modelAliases[requestedModel] || requestedModel;
269
+
270
+ if (modelAliases[requestedModel]) {
271
+ currentModel = modelAliases[requestedModel];
272
+ }
273
+
206
274
  if (req.body?.max_tokens === undefined && req.body?.messages) {
207
275
  let totalTokens = 0;
208
276
  for (const msg of req.body.messages) {
@@ -214,7 +282,7 @@ app.post("/v1/messages", async (req, res) => {
214
282
  const { text, tokens } = await sendMessage(currentSessionId, req.body.messages);
215
283
  totalRequests++;
216
284
  totalTokensUsed += tokens;
217
- res.json({ id: `msg_${Date.now()}`, type: "message", role: "assistant", content: [{ type: "text", text }], model: currentModel, stop_reason: "end_turn", usage: { input_tokens: Math.ceil(JSON.stringify(req.body.messages).length / 4), output_tokens: Math.ceil(text.length / 4) } });
285
+ res.json({ id: `msg_${Date.now()}`, type: "message", role: "assistant", content: [{ type: "text", text }], model: actualModel, stop_reason: "end_turn", usage: { input_tokens: Math.ceil(JSON.stringify(req.body.messages).length / 4), output_tokens: Math.ceil(text.length / 4) } });
218
286
  } catch (error) {
219
287
  res.status(500).json({ error: { type: "api_error", message: error instanceof Error ? error.message : String(error) } });
220
288
  }
@@ -365,9 +433,9 @@ function generateHTML() {
365
433
  <div class="bg-zinc-950 rounded-xl p-4 font-mono text-sm text-zinc-300 overflow-x-auto">
366
434
  <pre id="configJson">{
367
435
  "env": {
368
- "ANTHROPIC_BASE_URL": "http://localhost:8300/v1",
369
- "ANTHROPIC_AUTH_TOKEN": "test",
370
- "ANTHROPIC_MODEL": "minimax-m2.5-free"
436
+ "ANTHROPIC_BASE_URL": "http://localhost:8300",
437
+ "ANTHROPIC_API_KEY": "test",
438
+ "ANTHROPIC_MODEL": "claude-sonnet-4-5"
371
439
  }
372
440
  }</pre>
373
441
  </div>
@@ -495,7 +563,7 @@ function generateHTML() {
495
563
  currentModelId = modelId;
496
564
  loadStatus();
497
565
  renderModels(currentProvider === 'all' ? allModels : (groupedModels[currentProvider] || []));
498
- document.getElementById('configJson').textContent = JSON.stringify({ env: { ANTHROPIC_BASE_URL: "http://localhost:8300/v1", ANTHROPIC_AUTH_TOKEN: "test", ANTHROPIC_MODEL: modelId } }, null, 2);
566
+ document.getElementById('configJson').textContent = JSON.stringify({ env: { ANTHROPIC_BASE_URL: "http://localhost:8300", ANTHROPIC_API_KEY: "test", ANTHROPIC_MODEL: modelId } }, null, 2);
499
567
  }
500
568
  }
501
569