bridgerapi 1.9.1 → 2.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.
Files changed (2) hide show
  1. package/dist/cli.js +293 -246
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/server.ts
27
27
  var import_http = require("http");
28
- var import_crypto = require("crypto");
28
+ var import_crypto2 = require("crypto");
29
29
 
30
30
  // src/messages.ts
31
31
  function extractText(content) {
@@ -41,17 +41,24 @@ function messagesToPrompt(messages) {
41
41
  for (const m of messages) {
42
42
  const text = extractText(m.content).trim();
43
43
  if (!text) continue;
44
- if (m.role === "system") {
45
- system.push(text);
46
- } else if (m.role === "assistant") {
47
- turns.push(`Assistant: ${text}`);
48
- } else {
49
- turns.push(`User: ${text}`);
44
+ switch (m.role) {
45
+ case "system":
46
+ system.push(text);
47
+ break;
48
+ case "assistant":
49
+ turns.push(`Assistant: ${text}`);
50
+ break;
51
+ case "tool":
52
+ turns.push(`Tool result: ${text}`);
53
+ break;
54
+ default:
55
+ turns.push(`User: ${text}`);
56
+ break;
50
57
  }
51
58
  }
52
59
  const parts = [];
53
60
  if (system.length) parts.push("System instructions:\n" + system.join("\n"));
54
- if (turns.length) parts.push(turns.join("\n"));
61
+ if (turns.length) parts.push(turns.join("\n\n"));
55
62
  return parts.join("\n\n");
56
63
  }
57
64
 
@@ -63,6 +70,7 @@ var import_path2 = require("path");
63
70
 
64
71
  // src/config.ts
65
72
  var import_fs = require("fs");
73
+ var import_crypto = require("crypto");
66
74
  var import_os = require("os");
67
75
  var import_path = require("path");
68
76
  var CONFIG_DIR = (0, import_path.join)((0, import_os.homedir)(), ".bridgerapi");
@@ -80,12 +88,19 @@ function saveConfig(cfg) {
80
88
  (0, import_fs.mkdirSync)(CONFIG_DIR, { recursive: true });
81
89
  (0, import_fs.writeFileSync)(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n");
82
90
  }
91
+ function getOrCreateApiKey() {
92
+ const cfg = loadConfig();
93
+ if (cfg.apiKey) return cfg.apiKey;
94
+ const key = `sk-bridger-${(0, import_crypto.randomBytes)(24).toString("hex")}`;
95
+ saveConfig({ ...cfg, apiKey: key });
96
+ return key;
97
+ }
83
98
 
84
99
  // src/backends.ts
85
100
  var HOME = (0, import_os2.homedir)();
86
- function which(cmd2) {
101
+ function which(cmd) {
87
102
  try {
88
- return (0, import_child_process.execFileSync)("which", [cmd2], { encoding: "utf8" }).trim();
103
+ return (0, import_child_process.execFileSync)("which", [cmd], { encoding: "utf8" }).trim();
89
104
  } catch {
90
105
  return "";
91
106
  }
@@ -150,8 +165,8 @@ async function tryDiscover(bin, strategies) {
150
165
  }
151
166
  return [];
152
167
  }
153
- async function* spawnStream(cmd2, args, stdin) {
154
- const proc = (0, import_child_process.spawn)(cmd2, args, { env: process.env, stdio: ["pipe", "pipe", "pipe"] });
168
+ async function* spawnStream(cmd, args, stdin) {
169
+ const proc = (0, import_child_process.spawn)(cmd, args, { env: process.env, stdio: ["pipe", "pipe", "pipe"] });
155
170
  if (stdin !== void 0) proc.stdin.end(stdin);
156
171
  const stderrBufs = [];
157
172
  proc.stderr.on("data", (c) => stderrBufs.push(c));
@@ -169,7 +184,7 @@ async function* spawnStream(cmd2, args, stdin) {
169
184
  });
170
185
  if (!hasOutput && exitCode !== 0) {
171
186
  const stderr = Buffer.concat(stderrBufs).toString().trim();
172
- throw new Error(stderr || `${(0, import_path2.basename)(cmd2)} exited with code ${exitCode}`);
187
+ throw new Error(stderr || `${(0, import_path2.basename)(cmd)} exited with code ${exitCode}`);
173
188
  }
174
189
  }
175
190
  var GenericBackend = class {
@@ -199,11 +214,11 @@ var GenericBackend = class {
199
214
  }
200
215
  return this.def.models ?? [];
201
216
  }
202
- buildArgs(model2) {
203
- return this.def.args.map((a) => a === "{model}" ? model2 : a);
217
+ buildArgs(model) {
218
+ return this.def.args.map((a) => a === "{model}" ? model : a);
204
219
  }
205
- async runBlocking(prompt, model2) {
206
- const args = this.buildArgs(model2);
220
+ async runBlocking(prompt, model) {
221
+ const args = this.buildArgs(model);
207
222
  let out;
208
223
  try {
209
224
  if (this.def.promptMode === "stdin") {
@@ -216,8 +231,8 @@ var GenericBackend = class {
216
231
  }
217
232
  return [out.trim(), null];
218
233
  }
219
- async *stream(prompt, model2) {
220
- const args = this.buildArgs(model2);
234
+ async *stream(prompt, model) {
235
+ const args = this.buildArgs(model);
221
236
  if (this.def.promptMode === "stdin") {
222
237
  yield* spawnStream(this.bin, args, prompt);
223
238
  } else {
@@ -248,21 +263,22 @@ var ClaudeBackend = class {
248
263
  return [
249
264
  "claude-opus-4-6",
250
265
  "claude-opus-4-6-fast",
266
+ "claude-opus-4-5-20251101",
251
267
  "claude-sonnet-4-6",
252
268
  "claude-sonnet-4-5-20250929",
253
269
  "claude-haiku-4-5-20251001"
254
270
  ];
255
271
  }
256
- async runBlocking(prompt, model2) {
272
+ async runBlocking(prompt, model) {
257
273
  let out;
258
274
  try {
259
- out = (0, import_child_process.execFileSync)(this.bin, ["-p", "--output-format", "json", "--model", model2], {
275
+ out = (0, import_child_process.execFileSync)(this.bin, ["-p", "--output-format", "json", "--model", model], {
260
276
  input: prompt,
261
277
  encoding: "utf8",
262
278
  timeout: 3e5
263
279
  });
264
280
  } catch (e) {
265
- throw new Error(e.stderr?.trim() || `claude exited non-zero`);
281
+ throw new Error(e.stderr?.trim() || "claude exited non-zero");
266
282
  }
267
283
  try {
268
284
  const data = JSON.parse(out.trim() || "{}");
@@ -271,8 +287,8 @@ var ClaudeBackend = class {
271
287
  return [out.trim(), null];
272
288
  }
273
289
  }
274
- async *stream(prompt, model2) {
275
- yield* spawnStream(this.bin, ["-p", "--output-format", "text", "--model", model2], prompt);
290
+ async *stream(prompt, model) {
291
+ yield* spawnStream(this.bin, ["-p", "--output-format", "text", "--model", model], prompt);
276
292
  }
277
293
  };
278
294
  var GeminiBackend = class {
@@ -297,18 +313,16 @@ var GeminiBackend = class {
297
313
  if (found.length > 0) return found;
298
314
  return [
299
315
  "gemini-3.1-pro-preview",
300
- "gemini-3-pro-preview",
301
316
  "gemini-3-flash-preview",
302
317
  "gemini-2.5-pro",
303
318
  "gemini-2.5-flash",
304
- "gemini-2.0-flash",
305
- "gemini-1.5-pro"
319
+ "gemini-2.0-flash"
306
320
  ];
307
321
  }
308
- async runBlocking(prompt, model2) {
322
+ async runBlocking(prompt, model) {
309
323
  let out;
310
324
  try {
311
- out = (0, import_child_process.execFileSync)(this.bin, ["--output-format", "json", "--model", model2, "--approval-mode", "yolo"], {
325
+ out = (0, import_child_process.execFileSync)(this.bin, ["--output-format", "json", "--model", model, "--approval-mode", "yolo"], {
312
326
  input: prompt,
313
327
  encoding: "utf8",
314
328
  timeout: 3e5,
@@ -317,8 +331,8 @@ var GeminiBackend = class {
317
331
  } catch (e) {
318
332
  const err = e.stderr?.trim() ?? "";
319
333
  if (/auth|login|sign.?in/i.test(err))
320
- throw new Error(`Gemini not authenticated. Run: gemini auth OR export GEMINI_API_KEY=<key>`);
321
- throw new Error(err || `gemini exited non-zero`);
334
+ throw new Error("Gemini not authenticated. Run: gemini auth OR export GEMINI_API_KEY=<key>");
335
+ throw new Error(err || "gemini exited non-zero");
322
336
  }
323
337
  const raw = out.trim();
324
338
  try {
@@ -328,10 +342,10 @@ var GeminiBackend = class {
328
342
  return [raw, null];
329
343
  }
330
344
  }
331
- async *stream(prompt, model2) {
345
+ async *stream(prompt, model) {
332
346
  yield* spawnStream(
333
347
  this.bin,
334
- ["--output-format", "text", "--model", model2, "--approval-mode", "yolo"],
348
+ ["--output-format", "text", "--model", model, "--approval-mode", "yolo"],
335
349
  prompt
336
350
  );
337
351
  }
@@ -339,7 +353,7 @@ var GeminiBackend = class {
339
353
  var CodexBackend = class {
340
354
  constructor() {
341
355
  this.name = "codex";
342
- this.prefixes = ["gpt", "o3", "o4", "o1"];
356
+ this.prefixes = ["gpt", "o3", "o4"];
343
357
  }
344
358
  get bin() {
345
359
  return process.env.CODEX_BIN ?? which("codex") ?? "codex";
@@ -356,38 +370,35 @@ var CodexBackend = class {
356
370
  ]);
357
371
  if (found.length > 0) return found;
358
372
  return [
359
- "gpt-5-codex",
360
- "gpt-5.1-codex",
361
- "gpt-5.1-codex-max",
362
- "gpt-5.1",
363
373
  "gpt-5.2",
364
374
  "gpt-5.2-codex",
365
375
  "gpt-5.3-codex",
366
- "gpt-5-2025-08-07",
376
+ "gpt-5.4",
377
+ "gpt-5.4-mini",
367
378
  "o4-mini",
368
379
  "o3"
369
380
  ];
370
381
  }
371
- async runBlocking(prompt, model2) {
382
+ async runBlocking(prompt, model) {
372
383
  let out;
373
384
  try {
374
- out = (0, import_child_process.execFileSync)(this.bin, ["-q", "--model", model2, prompt], {
385
+ out = (0, import_child_process.execFileSync)(this.bin, ["-q", "--model", model, prompt], {
375
386
  encoding: "utf8",
376
387
  timeout: 3e5
377
388
  });
378
389
  } catch (e) {
379
- throw new Error(e.stderr?.trim() || `codex exited non-zero`);
390
+ throw new Error(e.stderr?.trim() || "codex exited non-zero");
380
391
  }
381
392
  return [out.trim(), null];
382
393
  }
383
- async *stream(prompt, model2) {
384
- yield* spawnStream(this.bin, ["-q", "--model", model2, prompt]);
394
+ async *stream(prompt, model) {
395
+ yield* spawnStream(this.bin, ["-q", "--model", model, prompt]);
385
396
  }
386
397
  };
387
398
  var CopilotBackend = class {
388
399
  constructor() {
389
400
  this.name = "copilot";
390
- this.prefixes = ["copilot", "github-copilot"];
401
+ this.prefixes = ["copilot"];
391
402
  }
392
403
  get bin() {
393
404
  return process.env.GH_BIN ?? which("gh") ?? "gh";
@@ -404,53 +415,47 @@ var CopilotBackend = class {
404
415
  async models() {
405
416
  const found = await tryDiscover(this.bin, [
406
417
  ["copilot", "models"],
407
- ["copilot", "models", "list"],
408
- ["copilot", "--list-models"]
418
+ ["copilot", "models", "list"]
409
419
  ]);
410
420
  if (found.length > 0) return found;
411
421
  return ["copilot"];
412
422
  }
413
- async runBlocking(prompt, model2) {
423
+ async runBlocking(prompt, model) {
414
424
  let out;
415
425
  try {
416
- out = (0, import_child_process.execFileSync)(this.bin, ["copilot", "suggest", "-t", "general", prompt], {
426
+ out = (0, import_child_process.execFileSync)(this.bin, ["copilot", "explain", prompt], {
417
427
  encoding: "utf8",
418
428
  timeout: 12e4
419
429
  });
420
430
  } catch (e) {
421
- throw new Error(e.stderr?.trim() || `gh copilot exited non-zero`);
431
+ throw new Error(e.stderr?.trim() || "gh copilot exited non-zero");
422
432
  }
423
433
  return [out.trim(), null];
424
434
  }
425
- async *stream(prompt, model2) {
426
- yield* spawnStream(this.bin, ["copilot", "suggest", "-t", "general", prompt]);
435
+ async *stream(prompt, model) {
436
+ yield* spawnStream(this.bin, ["copilot", "explain", prompt]);
427
437
  }
428
438
  };
429
439
  var DroidBackend = class _DroidBackend {
430
440
  constructor() {
431
441
  this.name = "droid";
432
- this.prefixes = ["droid", "glm", "kimi", "minimax"];
442
+ this.prefixes = ["droid", "factory", "glm", "kimi", "minimax"];
433
443
  }
434
444
  static {
435
- // Up-to-date as of March 2026 — source: Factory docs + droid exec --help
436
445
  this.KNOWN_MODELS = [
437
- "gpt-5-codex",
438
- "gpt-5.1-codex",
439
- "gpt-5.1-codex-max",
440
- "gpt-5.1",
441
- "gpt-5.2",
442
- "gpt-5.2-codex",
443
- "gpt-5.3-codex",
444
- "gpt-5-2025-08-07",
445
446
  "claude-opus-4-6",
446
447
  "claude-opus-4-6-fast",
447
- "claude-opus-4-1-20250805",
448
+ "claude-opus-4-5-20251101",
449
+ "claude-sonnet-4-6",
448
450
  "claude-sonnet-4-5-20250929",
449
451
  "claude-haiku-4-5-20251001",
452
+ "gpt-5.2",
453
+ "gpt-5.2-codex",
454
+ "gpt-5.3-codex",
455
+ "gpt-5.4",
456
+ "gpt-5.4-mini",
450
457
  "gemini-3.1-pro-preview",
451
- "gemini-3-pro-preview",
452
458
  "gemini-3-flash-preview",
453
- "glm-4.6",
454
459
  "glm-4.7",
455
460
  "glm-5",
456
461
  "kimi-k2.5",
@@ -476,23 +481,23 @@ var DroidBackend = class _DroidBackend {
476
481
  }
477
482
  return _DroidBackend.KNOWN_MODELS;
478
483
  }
479
- async runBlocking(prompt, model2) {
484
+ async runBlocking(prompt, model) {
480
485
  let out;
481
486
  try {
482
487
  out = (0, import_child_process.execFileSync)(
483
488
  which("droid") || this.bin,
484
- ["exec", "--output-format", "text", "--model", model2, "-"],
489
+ ["exec", "--output-format", "text", "--model", model],
485
490
  { input: prompt, encoding: "utf8", timeout: 3e5 }
486
491
  );
487
492
  } catch (e) {
488
- throw new Error(e.stderr?.trim() || `droid exited non-zero`);
493
+ throw new Error(e.stderr?.trim() || "droid exited non-zero");
489
494
  }
490
495
  return [out.trim(), null];
491
496
  }
492
- async *stream(prompt, model2) {
497
+ async *stream(prompt, model) {
493
498
  yield* spawnStream(
494
499
  which("droid") || this.bin,
495
- ["exec", "--output-format", "text", "--model", model2, "-"],
500
+ ["exec", "--output-format", "text", "--model", model],
496
501
  prompt
497
502
  );
498
503
  }
@@ -513,13 +518,13 @@ function loadUserBackends() {
513
518
  }
514
519
  }
515
520
  var BACKENDS = [...BUILTIN, ...loadUserBackends()];
516
- function pickBackend(model2) {
521
+ function pickBackend(model) {
517
522
  const override = process.env.BRIDGERAPI_BACKEND?.toLowerCase();
518
523
  if (override) {
519
524
  const forced = BACKENDS.find((b) => b.name === override && b.available());
520
525
  if (forced) return forced;
521
526
  }
522
- const m = model2.toLowerCase();
527
+ const m = model.toLowerCase();
523
528
  for (const b of BACKENDS) {
524
529
  if (b.prefixes.some((p) => m.startsWith(p)) && b.available()) return b;
525
530
  }
@@ -532,23 +537,23 @@ function sse(data) {
532
537
 
533
538
  `;
534
539
  }
535
- function chunk(id, ts, model2, delta, finish) {
540
+ function chunk(id, ts, model, delta, finish) {
536
541
  return sse({
537
542
  id,
538
543
  object: "chat.completion.chunk",
539
544
  created: ts,
540
- model: model2,
545
+ model,
541
546
  choices: [{ index: 0, delta, finish_reason: finish ?? null }]
542
547
  });
543
548
  }
544
- function completion(id, ts, model2, text, usage) {
549
+ function completion(id, ts, model, text, usage) {
545
550
  const pt = usage ? (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.promptTokenCount ?? 0) : 0;
546
551
  const ct = usage ? (usage.output_tokens ?? 0) + (usage.candidatesTokenCount ?? 0) : 0;
547
552
  return {
548
553
  id,
549
554
  object: "chat.completion",
550
555
  created: ts,
551
- model: model2,
556
+ model,
552
557
  choices: [{ index: 0, message: { role: "assistant", content: text }, finish_reason: "stop" }],
553
558
  usage: { prompt_tokens: pt, completion_tokens: ct, total_tokens: pt + ct }
554
559
  };
@@ -595,10 +600,10 @@ async function handleModels(res) {
595
600
  }
596
601
  sendJson(res, 200, { object: "list", data: allModels });
597
602
  }
598
- function handleHealth(res, port2) {
603
+ function handleHealth(res, port) {
599
604
  const backends = {};
600
605
  for (const b of BACKENDS) backends[b.name] = b.available();
601
- sendJson(res, 200, { status: "ok", port: port2, backends });
606
+ sendJson(res, 200, { status: "ok", port, backends });
602
607
  }
603
608
  async function handleChat(req, res) {
604
609
  let body;
@@ -613,68 +618,74 @@ async function handleChat(req, res) {
613
618
  sendJson(res, 400, { error: { message: "messages required", type: "invalid_request_error" } });
614
619
  return;
615
620
  }
616
- const model2 = body.model ?? "claude-sonnet-4-6";
621
+ const model = body.model ?? "claude-opus-4-6";
617
622
  const streaming = Boolean(body.stream);
618
623
  const prompt = messagesToPrompt(messages);
619
- const backend2 = pickBackend(model2);
620
- const id = `chatcmpl-${(0, import_crypto.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
624
+ const backend = pickBackend(model);
625
+ const id = `chatcmpl-${(0, import_crypto2.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
621
626
  const ts = Math.floor(Date.now() / 1e3);
622
- console.log(` ${backend2.name} model=${model2} stream=${streaming} turns=${messages.length}`);
627
+ console.log(` ${backend.name} model=${model} stream=${streaming} turns=${messages.length}`);
623
628
  if (streaming) {
624
629
  cors(res, 200);
625
630
  res.setHeader("Content-Type", "text/event-stream");
626
631
  res.setHeader("Cache-Control", "no-cache");
627
632
  res.setHeader("X-Accel-Buffering", "no");
628
633
  res.flushHeaders();
629
- res.write(chunk(id, ts, model2, { role: "assistant" }));
634
+ res.write(chunk(id, ts, model, { role: "assistant" }));
630
635
  try {
631
- for await (const raw of backend2.stream(prompt, model2)) {
632
- res.write(chunk(id, ts, model2, { content: raw.toString("utf8") }));
636
+ for await (const raw of backend.stream(prompt, model)) {
637
+ res.write(chunk(id, ts, model, { content: raw.toString("utf8") }));
633
638
  }
634
639
  } catch (err) {
635
640
  const msg = err.message ?? String(err);
636
- console.error(` stream error [${backend2.name}]: ${msg}`);
637
- res.write(chunk(id, ts, model2, { content: `
641
+ console.error(` stream error [${backend.name}]: ${msg}`);
642
+ res.write(chunk(id, ts, model, { content: `
638
643
 
639
- \u26A0\uFE0F ${backend2.name} error: ${msg}` }));
644
+ [bridgerapi] ${backend.name} error: ${msg}` }));
640
645
  }
641
- res.write(chunk(id, ts, model2, {}, "stop"));
646
+ res.write(chunk(id, ts, model, {}, "stop"));
642
647
  res.write("data: [DONE]\n\n");
643
648
  res.end();
644
649
  } else {
645
650
  try {
646
- const [text, usage] = await backend2.runBlocking(prompt, model2);
647
- sendJson(res, 200, completion(id, ts, model2, text, usage));
651
+ const [text, usage] = await backend.runBlocking(prompt, model);
652
+ sendJson(res, 200, completion(id, ts, model, text, usage));
648
653
  } catch (err) {
649
654
  console.error(` error: ${err.message}`);
650
655
  sendJson(res, 500, { error: { message: err.message, type: "server_error" } });
651
656
  }
652
657
  }
653
658
  }
654
- function createBridgeServer(port2) {
655
- const server = (0, import_http.createServer)(async (req, res) => {
656
- const path = (req.url ?? "/").split("?")[0];
657
- const method = req.method ?? "GET";
658
- if (method === "OPTIONS") {
659
- cors(res, 200);
660
- res.end();
661
- return;
662
- }
663
- if (method === "GET" && (path === "/v1/models" || path === "/models")) {
664
- await handleModels(res);
665
- return;
666
- }
667
- if (method === "GET" && path === "/health") {
668
- handleHealth(res, port2);
669
- return;
670
- }
671
- if (method === "POST" && (path === "/v1/chat/completions" || path === "/chat/completions")) {
672
- await handleChat(req, res);
673
- return;
659
+ function createBridgeServer(port) {
660
+ return (0, import_http.createServer)(async (req, res) => {
661
+ try {
662
+ const path = (req.url ?? "/").split("?")[0];
663
+ const method = req.method ?? "GET";
664
+ if (method === "OPTIONS") {
665
+ cors(res, 200);
666
+ res.end();
667
+ return;
668
+ }
669
+ if (method === "GET" && (path === "/v1/models" || path === "/models")) {
670
+ await handleModels(res);
671
+ return;
672
+ }
673
+ if (method === "GET" && path === "/health") {
674
+ handleHealth(res, port);
675
+ return;
676
+ }
677
+ if (method === "POST" && (path === "/v1/chat/completions" || path === "/chat/completions")) {
678
+ await handleChat(req, res);
679
+ return;
680
+ }
681
+ sendJson(res, 404, { error: { message: "not found", type: "not_found_error" } });
682
+ } catch (err) {
683
+ console.error(" unhandled server error:", err.message);
684
+ if (!res.headersSent) {
685
+ sendJson(res, 500, { error: { message: "internal server error", type: "server_error" } });
686
+ }
674
687
  }
675
- sendJson(res, 404, { error: { message: "not found", type: "not_found_error" } });
676
688
  });
677
- return server;
678
689
  }
679
690
 
680
691
  // src/service.ts
@@ -687,13 +698,13 @@ var LABEL = "com.bridgerapi.server";
687
698
  function plistPath() {
688
699
  return (0, import_path3.join)(HOME2, "Library/LaunchAgents", `${LABEL}.plist`);
689
700
  }
690
- function writePlist(port2, scriptPath, nodePath, backend2) {
701
+ function writePlist(port, scriptPath, nodePath, backend) {
691
702
  const logDir = (0, import_path3.join)(HOME2, ".bridgerapi");
692
703
  (0, import_fs3.mkdirSync)(logDir, { recursive: true });
693
704
  (0, import_fs3.mkdirSync)((0, import_path3.join)(HOME2, "Library/LaunchAgents"), { recursive: true });
694
- const backendEntry = backend2 ? `
705
+ const backendEntry = backend ? `
695
706
  <key>BRIDGERAPI_BACKEND</key>
696
- <string>${backend2}</string>` : "";
707
+ <string>${backend}</string>` : "";
697
708
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
698
709
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
699
710
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -712,13 +723,11 @@ function writePlist(port2, scriptPath, nodePath, backend2) {
712
723
  <key>EnvironmentVariables</key>
713
724
  <dict>
714
725
  <key>BRIDGERAPI_PORT</key>
715
- <string>${port2}</string>
726
+ <string>${port}</string>
716
727
  <key>PATH</key>
717
728
  <string>${HOME2}/.local/bin:/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
718
729
  <key>HOME</key>
719
- <string>${HOME2}</string>
720
- <key>CUSTOM_OKBRIDGER_API_KEY</key>
721
- <string>local</string>${backendEntry}
730
+ <string>${HOME2}</string>${backendEntry}
722
731
  </dict>
723
732
 
724
733
  <key>StandardOutPath</key>
@@ -739,12 +748,12 @@ function unitPath() {
739
748
  const configHome = process.env.XDG_CONFIG_HOME ?? (0, import_path3.join)(HOME2, ".config");
740
749
  return (0, import_path3.join)(configHome, "systemd/user/bridgerapi.service");
741
750
  }
742
- function writeUnit(port2, scriptPath, nodePath, backend2) {
751
+ function writeUnit(port, scriptPath, nodePath, backend) {
743
752
  const logDir = (0, import_path3.join)(HOME2, ".bridgerapi");
744
753
  (0, import_fs3.mkdirSync)(logDir, { recursive: true });
745
754
  (0, import_fs3.mkdirSync)((0, import_path3.join)(HOME2, ".config/systemd/user"), { recursive: true });
746
- const backendLine = backend2 ? `
747
- Environment=BRIDGERAPI_BACKEND=${backend2}` : "";
755
+ const backendLine = backend ? `
756
+ Environment=BRIDGERAPI_BACKEND=${backend}` : "";
748
757
  const unit = `[Unit]
749
758
  Description=bridgerapi \u2014 OpenAI-compatible bridge for AI CLIs
750
759
  After=network.target
@@ -752,9 +761,8 @@ After=network.target
752
761
  [Service]
753
762
  Type=simple
754
763
  ExecStart=${nodePath} ${scriptPath} start
755
- Environment=BRIDGERAPI_PORT=${port2}
764
+ Environment=BRIDGERAPI_PORT=${port}
756
765
  Environment=HOME=${HOME2}
757
- Environment=CUSTOM_OKBRIDGER_API_KEY=local
758
766
  Environment=PATH=${HOME2}/.local/bin:/usr/local/bin:/usr/bin:/bin${backendLine}
759
767
  Restart=always
760
768
  StandardOutput=append:${logDir}/server.log
@@ -765,7 +773,7 @@ WantedBy=default.target
765
773
  `;
766
774
  (0, import_fs3.writeFileSync)(unitPath(), unit);
767
775
  }
768
- function installService(port2, backend2) {
776
+ function installService(port, backend) {
769
777
  const scriptPath = process.argv[1];
770
778
  const nodePath = process.execPath;
771
779
  const os = (0, import_os3.platform)();
@@ -774,17 +782,17 @@ function installService(port2, backend2) {
774
782
  (0, import_child_process2.execSync)(`launchctl unload "${plistPath()}" 2>/dev/null`, { stdio: "ignore" });
775
783
  } catch {
776
784
  }
777
- writePlist(port2, scriptPath, nodePath, backend2);
785
+ writePlist(port, scriptPath, nodePath, backend);
778
786
  (0, import_child_process2.execSync)(`launchctl load -w "${plistPath()}"`);
779
- console.log(`\u2713 LaunchAgent installed \u2192 ${plistPath()}`);
787
+ console.log(` LaunchAgent installed -> ${plistPath()}`);
780
788
  } else if (os === "linux") {
781
- writeUnit(port2, scriptPath, nodePath, backend2);
789
+ writeUnit(port, scriptPath, nodePath, backend);
782
790
  try {
783
791
  (0, import_child_process2.execSync)("systemctl --user daemon-reload");
784
792
  } catch {
785
793
  }
786
794
  (0, import_child_process2.execSync)("systemctl --user enable --now bridgerapi");
787
- console.log(`\u2713 systemd user service installed`);
795
+ console.log(" systemd user service installed");
788
796
  } else {
789
797
  throw new Error(`Auto-install not supported on ${os}. Run 'bridgerapi start' manually.`);
790
798
  }
@@ -799,7 +807,7 @@ function uninstallService() {
799
807
  } catch {
800
808
  }
801
809
  (0, import_fs3.unlinkSync)(p);
802
- console.log("\u2713 LaunchAgent removed");
810
+ console.log(" LaunchAgent removed");
803
811
  } else {
804
812
  console.log(" bridgerapi service is not installed");
805
813
  }
@@ -816,7 +824,7 @@ function uninstallService() {
816
824
  } catch {
817
825
  }
818
826
  }
819
- console.log("\u2713 systemd service removed");
827
+ console.log(" systemd service removed");
820
828
  } else {
821
829
  throw new Error(`Not supported on ${os}`);
822
830
  }
@@ -860,6 +868,24 @@ function ask(question) {
860
868
  });
861
869
  });
862
870
  }
871
+ function printBanner(port, activeBackend) {
872
+ const apiKey = getOrCreateApiKey();
873
+ const available = BACKENDS.filter((b) => b.available());
874
+ const backendLabel = activeBackend ? `${activeBackend} (all requests routed here)` : available.map((b) => b.name).join(", ") + " (auto-routed by model prefix)";
875
+ console.log();
876
+ console.log(" bridgerapi is running");
877
+ console.log();
878
+ console.log(` Base URL : http://127.0.0.1:${port}/v1`);
879
+ console.log(` API Key : ${apiKey}`);
880
+ console.log();
881
+ console.log(` Backend : ${backendLabel}`);
882
+ console.log(` Logs : ${LOG_DIR}/server.log`);
883
+ console.log();
884
+ console.log(" Goose / OpenAI-compatible config:");
885
+ console.log(` OPENAI_API_BASE=http://127.0.0.1:${port}/v1`);
886
+ console.log(` OPENAI_API_KEY=${apiKey}`);
887
+ console.log();
888
+ }
863
889
  async function cmdSetup() {
864
890
  console.log();
865
891
  console.log(" bridgerapi \u2014 OpenAI-compatible API bridge for AI CLI tools");
@@ -869,8 +895,8 @@ async function cmdSetup() {
869
895
  console.log(" Backends detected:\n");
870
896
  for (const b of BACKENDS) {
871
897
  const ok = b.available();
872
- console.log(` ${ok ? "\u2713" : "\u2717"} ${b.name}`);
873
- if (!ok) console.log(` \u2192 ${INSTALL_HINTS[b.name]}`);
898
+ console.log(` ${ok ? "+" : "-"} ${b.name}`);
899
+ if (!ok) console.log(` -> ${INSTALL_HINTS[b.name] ?? "not installed"}`);
874
900
  }
875
901
  console.log();
876
902
  if (available.length === 0) {
@@ -887,7 +913,7 @@ async function cmdSetup() {
887
913
  const defaultIdx = names.indexOf(currentDefault);
888
914
  console.log(" Which backend do you want to use as default?\n");
889
915
  names.forEach((name, i) => {
890
- const marker = i === defaultIdx ? " \u2190 default" : "";
916
+ const marker = i === defaultIdx ? " <- default" : "";
891
917
  console.log(` ${i + 1} ${name}${marker}`);
892
918
  });
893
919
  console.log();
@@ -899,8 +925,8 @@ async function cmdSetup() {
899
925
  }
900
926
  const defaultPort = cfg.port ?? DEFAULT_PORT;
901
927
  const portAnswer = await ask(` Port [${defaultPort}]: `);
902
- const port2 = portAnswer ? parseInt(portAnswer) || defaultPort : defaultPort;
903
- saveConfig({ backend: chosenBackend, port: port2 });
928
+ const port = portAnswer ? parseInt(portAnswer) || defaultPort : defaultPort;
929
+ saveConfig({ ...cfg, backend: chosenBackend, port });
904
930
  console.log();
905
931
  console.log(" How do you want to run bridgerapi?");
906
932
  console.log(" 1 Foreground (stops when terminal closes)");
@@ -909,66 +935,51 @@ async function cmdSetup() {
909
935
  const choice = await ask(" Choose [1/2]: ");
910
936
  console.log();
911
937
  if (choice === "2") {
912
- cmdInstall(port2, chosenBackend);
938
+ cmdInstall(port, chosenBackend);
913
939
  } else {
914
- cmdStart(port2, chosenBackend);
940
+ cmdStart(port, chosenBackend);
915
941
  }
916
942
  }
917
- function cmdStart(port2, backend2) {
943
+ function cmdStart(port, backend) {
918
944
  (0, import_fs4.mkdirSync)(LOG_DIR, { recursive: true });
919
945
  const cfg = loadConfig();
920
- const activeBackend = backend2 ?? cfg.backend;
946
+ const activeBackend = backend ?? cfg.backend;
921
947
  if (activeBackend) process.env.BRIDGERAPI_BACKEND = activeBackend;
922
948
  const available = BACKENDS.filter((b) => b.available());
923
949
  if (available.length === 0) {
924
950
  console.error(" No CLI backends found. Run: bridgerapi to see setup instructions.");
925
951
  process.exit(1);
926
952
  }
927
- const server = createBridgeServer(port2);
928
- server.listen(port2, "127.0.0.1", () => {
929
- console.log(` bridgerapi is running`);
930
- console.log();
931
- console.log(` Base URL : http://127.0.0.1:${port2}/v1`);
932
- console.log(` API Key : local`);
933
- console.log();
934
- const backendLabel = activeBackend ? `${activeBackend} (all requests routed here)` : available.map((b) => b.name).join(", ") + " (auto-routed by model prefix)";
935
- console.log(` Backend : ${backendLabel}`);
936
- console.log(` Logs : ${LOG_DIR}/server.log`);
937
- console.log();
953
+ const server = createBridgeServer(port);
954
+ server.listen(port, "127.0.0.1", () => {
955
+ printBanner(port, activeBackend);
938
956
  console.log(" Ctrl+C to stop.");
939
957
  });
940
958
  server.on("error", (err) => {
941
959
  if (err.code === "EADDRINUSE") {
942
- console.error(` Port ${port2} is already in use. Try: bridgerapi start --port 9000`);
960
+ console.error(` Port ${port} is already in use. Try: bridgerapi start --port 9000`);
943
961
  } else {
944
962
  console.error(" Server error:", err.message);
945
963
  }
946
964
  process.exit(1);
947
965
  });
948
966
  }
949
- function cmdInstall(port2, backend2) {
967
+ function cmdInstall(port, backend) {
950
968
  const cfg = loadConfig();
951
- const activeBackend = backend2 ?? cfg.backend;
969
+ const activeBackend = backend ?? cfg.backend;
952
970
  try {
953
- installService(port2, activeBackend);
971
+ installService(port, activeBackend);
954
972
  console.log();
955
- console.log(" Waiting for server to start\u2026");
973
+ console.log(" Waiting for server to start...");
956
974
  let attempts = 0;
957
975
  const poll = setInterval(async () => {
958
976
  attempts++;
959
977
  try {
960
978
  const http = await import("http");
961
- http.get(`http://127.0.0.1:${port2}/health`, (res) => {
979
+ http.get(`http://127.0.0.1:${port}/health`, (res) => {
962
980
  if (res.statusCode === 200) {
963
981
  clearInterval(poll);
964
- console.log();
965
- console.log(` bridgerapi is running`);
966
- console.log();
967
- console.log(` Base URL : http://127.0.0.1:${port2}/v1`);
968
- console.log(` API Key : local`);
969
- if (activeBackend) console.log(` Backend : ${activeBackend}`);
970
- console.log();
971
- console.log(` Logs : tail -f ${LOG_DIR}/server.log`);
982
+ printBanner(port, activeBackend);
972
983
  console.log(` Stop : bridgerapi uninstall`);
973
984
  process.exit(0);
974
985
  }
@@ -996,20 +1007,48 @@ function cmdUninstall() {
996
1007
  process.exit(1);
997
1008
  }
998
1009
  }
999
- function cmdStatus(port2) {
1010
+ function cmdStatus(port) {
1000
1011
  const cfg = loadConfig();
1001
1012
  const { running, pid } = serviceStatus();
1002
1013
  if (running) {
1014
+ const apiKey = getOrCreateApiKey();
1003
1015
  console.log(` bridgerapi is running${pid ? ` (pid ${pid})` : ""}`);
1004
- console.log(` Base URL : http://127.0.0.1:${port2}/v1`);
1005
- console.log(` API Key : local`);
1016
+ console.log(` Base URL : http://127.0.0.1:${port}/v1`);
1017
+ console.log(` API Key : ${apiKey}`);
1006
1018
  if (cfg.backend) console.log(` Backend : ${cfg.backend}`);
1007
1019
  } else {
1008
1020
  console.log(" bridgerapi is not running.");
1009
- console.log(" Run: bridgerapi \u2192 setup wizard");
1010
- console.log(" Run: bridgerapi start \u2192 start in foreground");
1011
- console.log(" Run: bridgerapi install \u2192 install background service");
1021
+ console.log(" Run: bridgerapi -> setup wizard");
1022
+ console.log(" Run: bridgerapi start -> start in foreground");
1023
+ console.log(" Run: bridgerapi install -> install background service");
1024
+ }
1025
+ }
1026
+ function cmdKey(args) {
1027
+ if (args[0] === "reset") {
1028
+ const cfg2 = loadConfig();
1029
+ delete cfg2.apiKey;
1030
+ saveConfig(cfg2);
1031
+ const newKey = getOrCreateApiKey();
1032
+ console.log(` API key regenerated: ${newKey}`);
1033
+ return;
1012
1034
  }
1035
+ const apiKey = getOrCreateApiKey();
1036
+ const cfg = loadConfig();
1037
+ const port = cfg.port ?? DEFAULT_PORT;
1038
+ console.log();
1039
+ console.log(" bridgerapi API key (OpenAI-compatible)");
1040
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1041
+ console.log();
1042
+ console.log(` ${apiKey}`);
1043
+ console.log();
1044
+ console.log(" Use this as your OPENAI_API_KEY in Goose, Cursor, etc.");
1045
+ console.log();
1046
+ console.log(" Quick setup:");
1047
+ console.log(` export OPENAI_API_BASE=http://127.0.0.1:${port}/v1`);
1048
+ console.log(` export OPENAI_API_KEY=${apiKey}`);
1049
+ console.log();
1050
+ console.log(" Reset: bridgerapi key reset");
1051
+ console.log();
1013
1052
  }
1014
1053
  function cmdConfig(args) {
1015
1054
  const cfg = loadConfig();
@@ -1030,7 +1069,7 @@ function cmdConfig(args) {
1030
1069
  process.exit(1);
1031
1070
  }
1032
1071
  cfg.backend = val;
1033
- console.log(` backend \u2192 ${val}`);
1072
+ console.log(` backend -> ${val}`);
1034
1073
  } else if (key === "port") {
1035
1074
  const p = parseInt(val);
1036
1075
  if (isNaN(p) || p < 1 || p > 65535) {
@@ -1038,7 +1077,7 @@ function cmdConfig(args) {
1038
1077
  process.exit(1);
1039
1078
  }
1040
1079
  cfg.port = p;
1041
- console.log(` port \u2192 ${p}`);
1080
+ console.log(` port -> ${p}`);
1042
1081
  } else {
1043
1082
  console.error(` Unknown key: ${key}`);
1044
1083
  console.error(` Valid keys: backend, port`);
@@ -1059,12 +1098,13 @@ function cmdConfig(args) {
1059
1098
  console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1060
1099
  console.log(` backend : ${cfg.backend ?? "(auto \u2014 routed by model prefix)"}`);
1061
1100
  console.log(` port : ${cfg.port ?? `${DEFAULT_PORT} (default)`}`);
1101
+ console.log(` apiKey : ${cfg.apiKey ?? "(not generated yet \u2014 run: bridgerapi key)"}`);
1062
1102
  console.log(` file : ${(0, import_path4.join)((0, import_os4.homedir)(), ".bridgerapi/config.json")}`);
1063
1103
  console.log();
1064
1104
  console.log(" To change:");
1065
- console.log(` bridgerapi config set backend=claude`);
1066
- console.log(` bridgerapi config set port=9000`);
1067
- console.log(` bridgerapi config reset`);
1105
+ console.log(" bridgerapi config set backend=droid");
1106
+ console.log(" bridgerapi config set port=9000");
1107
+ console.log(" bridgerapi config reset");
1068
1108
  console.log();
1069
1109
  }
1070
1110
  async function cmdBackendAdd() {
@@ -1088,8 +1128,8 @@ async function cmdBackendAdd() {
1088
1128
  const modeChoice = await ask(" Choose [1/2]: ");
1089
1129
  const promptMode = modeChoice === "2" ? "arg" : "stdin";
1090
1130
  console.log();
1091
- console.log(` Command arguments template. Use {model} as placeholder for the model name.`);
1092
- console.log(` Example: exec --output-format text --model {model} -`);
1131
+ console.log(" Command arguments template. Use {model} as placeholder for the model name.");
1132
+ console.log(" Example: exec --output-format text --model {model}");
1093
1133
  const argsRaw = await ask(" Args: ");
1094
1134
  const args = argsRaw.trim().split(/\s+/).filter(Boolean);
1095
1135
  const modelsCmdRaw = await ask(" Args to list models (leave blank to skip): ");
@@ -1104,11 +1144,8 @@ async function cmdBackendAdd() {
1104
1144
  else existing.push(def);
1105
1145
  saveConfig({ ...cfg, customBackends: existing });
1106
1146
  console.log();
1107
- console.log(` \u2713 ${name} backend saved.`);
1108
- console.log(` Restart bridgerapi for it to take effect.`);
1109
- console.log();
1110
- console.log(" Example JSON entry in ~/.bridgerapi/config.json:");
1111
- console.log(` ${JSON.stringify(def, null, 2).split("\n").join("\n ")}`);
1147
+ console.log(` ${name} backend saved.`);
1148
+ console.log(" Restart bridgerapi for it to take effect.");
1112
1149
  console.log();
1113
1150
  }
1114
1151
  async function cmdBackendList() {
@@ -1117,34 +1154,34 @@ async function cmdBackendList() {
1117
1154
  for (const b of BACKENDS) {
1118
1155
  if (!b.available()) {
1119
1156
  const hint = INSTALL_HINTS[b.name] ?? "not installed";
1120
- console.log(` \u2717 ${b.name}`);
1121
- console.log(` \u2192 ${hint}
1157
+ console.log(` - ${b.name}`);
1158
+ console.log(` -> ${hint}
1122
1159
  `);
1123
1160
  continue;
1124
1161
  }
1125
- process.stdout.write(` \u2713 ${b.name} (discovering models\u2026)\r`);
1162
+ process.stdout.write(` + ${b.name} (discovering models...)\r`);
1126
1163
  const modelList = await b.models();
1127
1164
  const preview = modelList.slice(0, 6).join(" ");
1128
1165
  const extra = modelList.length > 6 ? ` +${modelList.length - 6} more` : "";
1129
- console.log(` \u2713 ${b.name} `);
1166
+ console.log(` + ${b.name} `);
1130
1167
  console.log(` ${preview}${extra}
1131
1168
  `);
1132
1169
  }
1133
1170
  }
1134
- async function cmdChat(model2, backendFlag) {
1171
+ async function cmdChat(model, backendFlag) {
1135
1172
  const cfg = loadConfig();
1136
- const activeBackend = backendFlag ?? (model2 && BACKENDS.find((b) => b.name === model2?.toLowerCase())?.name) ?? cfg.backend;
1173
+ const activeBackend = backendFlag ?? (model && BACKENDS.find((b) => b.name === model?.toLowerCase())?.name) ?? cfg.backend;
1137
1174
  if (activeBackend) process.env.BRIDGERAPI_BACKEND = activeBackend;
1138
- const resolvedModel = model2 && BACKENDS.find((b) => b.name === model2.toLowerCase()) ? void 0 : model2;
1175
+ const resolvedModel = model && BACKENDS.find((b) => b.name === model.toLowerCase()) ? void 0 : model;
1139
1176
  const available = BACKENDS.filter((b) => b.available());
1140
1177
  if (available.length === 0) {
1141
1178
  console.error(" No backends found. Run: bridgerapi to see setup instructions.");
1142
1179
  process.exit(1);
1143
1180
  }
1144
1181
  const fallbackModel = `${activeBackend ?? available[0].name}-default`;
1145
- const backend2 = pickBackend(resolvedModel ?? fallbackModel);
1182
+ const backend = pickBackend(resolvedModel ?? fallbackModel);
1146
1183
  console.log();
1147
- console.log(` bridgerapi chat \u2014 ${backend2.name}`);
1184
+ console.log(` bridgerapi chat \u2014 ${backend.name}`);
1148
1185
  console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
1149
1186
  console.log(" Type a message and press Enter. Ctrl+C to exit.");
1150
1187
  console.log();
@@ -1165,8 +1202,8 @@ async function cmdChat(model2, backendFlag) {
1165
1202
  process.stdout.write("\n");
1166
1203
  let reply = "";
1167
1204
  try {
1168
- process.stdout.write(`${backend2.name}: `);
1169
- for await (const chunk2 of backend2.stream(messagesToPrompt(history), resolvedModel ?? fallbackModel)) {
1205
+ process.stdout.write(`${backend.name}: `);
1206
+ for await (const chunk2 of backend.stream(messagesToPrompt(history), resolvedModel ?? fallbackModel)) {
1170
1207
  const piece = chunk2.toString("utf8");
1171
1208
  process.stdout.write(piece);
1172
1209
  reply += piece;
@@ -1188,79 +1225,89 @@ function showHelp() {
1188
1225
 
1189
1226
  Usage:
1190
1227
  bridgerapi Interactive setup wizard
1191
- bridgerapi chat [--model m] Chat in the terminal (routes by model prefix)
1192
- bridgerapi chat --backend <name> Chat using a specific backend
1193
1228
  bridgerapi start [--port n] Start API server in the foreground
1194
- bridgerapi start --backend <name> Start forcing a specific backend for all requests
1229
+ bridgerapi start --backend <name> Start forcing a specific backend
1230
+ bridgerapi chat [--model m] Chat in the terminal
1231
+ bridgerapi chat --backend <name> Chat using a specific backend
1232
+ bridgerapi key Show your OpenAI-compatible API key
1233
+ bridgerapi key reset Regenerate the API key
1195
1234
  bridgerapi install [--port n] Install as a background service
1196
1235
  bridgerapi uninstall Remove background service
1197
1236
  bridgerapi status Show service status
1198
1237
  bridgerapi config Show saved configuration
1199
- bridgerapi config set backend=<b> Set default backend (claude|gemini|codex|copilot|droid)
1238
+ bridgerapi config set backend=<b> Set default backend
1200
1239
  bridgerapi config set port=<n> Set default port
1201
1240
  bridgerapi config reset Clear saved configuration
1202
1241
  bridgerapi backend List all backends (built-in + custom)
1203
1242
  bridgerapi backend add Add a custom CLI backend interactively
1204
1243
 
1205
1244
  Built-in backends: claude, gemini, codex, copilot, droid
1206
- Custom backends: add any AI CLI via "bridgerapi backend add"
1207
1245
  `.trim());
1208
1246
  }
1209
1247
  function parseArgs() {
1210
1248
  const cfg = loadConfig();
1211
1249
  const args = process.argv.slice(2);
1212
- const cmd2 = args[0] ?? "";
1213
- let port2 = cfg.port ?? DEFAULT_PORT;
1214
- let model2;
1215
- let backend2;
1216
- const rest2 = [];
1250
+ const cmd = args[0] ?? "";
1251
+ let port = cfg.port ?? DEFAULT_PORT;
1252
+ let model;
1253
+ let backend;
1254
+ const rest = [];
1217
1255
  for (let i = 1; i < args.length; i++) {
1218
1256
  if ((args[i] === "--port" || args[i] === "-p") && args[i + 1]) {
1219
- port2 = parseInt(args[++i]);
1257
+ port = parseInt(args[++i]);
1220
1258
  } else if ((args[i] === "--model" || args[i] === "-m") && args[i + 1]) {
1221
- model2 = args[++i];
1259
+ model = args[++i];
1222
1260
  } else if ((args[i] === "--backend" || args[i] === "-b") && args[i + 1]) {
1223
- backend2 = args[++i];
1261
+ backend = args[++i];
1224
1262
  } else {
1225
- rest2.push(args[i]);
1263
+ rest.push(args[i]);
1226
1264
  }
1227
1265
  }
1228
- return { cmd: cmd2, port: port2, model: model2, backend: backend2, rest: rest2 };
1266
+ return { cmd, port, model, backend, rest };
1229
1267
  }
1230
- var { cmd, port, model, backend, rest } = parseArgs();
1231
- switch (cmd) {
1232
- case "":
1233
- case "setup":
1234
- cmdSetup();
1235
- break;
1236
- case "chat":
1237
- cmdChat(model, backend);
1238
- break;
1239
- case "start":
1240
- cmdStart(port, backend);
1241
- break;
1242
- case "install":
1243
- cmdInstall(port, backend);
1244
- break;
1245
- case "uninstall":
1246
- cmdUninstall();
1247
- break;
1248
- case "status":
1249
- cmdStatus(port);
1250
- break;
1251
- case "config":
1252
- cmdConfig(rest);
1253
- break;
1254
- case "backend":
1255
- if (rest[0] === "add") cmdBackendAdd();
1256
- else cmdBackendList();
1257
- break;
1258
- case "help":
1259
- case "--help":
1260
- case "-h":
1261
- showHelp();
1262
- break;
1263
- default:
1264
- showHelp();
1265
- process.exit(1);
1268
+ async function main() {
1269
+ const { cmd, port, model, backend, rest } = parseArgs();
1270
+ switch (cmd) {
1271
+ case "":
1272
+ case "setup":
1273
+ await cmdSetup();
1274
+ break;
1275
+ case "chat":
1276
+ await cmdChat(model, backend);
1277
+ break;
1278
+ case "start":
1279
+ cmdStart(port, backend);
1280
+ break;
1281
+ case "install":
1282
+ cmdInstall(port, backend);
1283
+ break;
1284
+ case "uninstall":
1285
+ cmdUninstall();
1286
+ break;
1287
+ case "status":
1288
+ cmdStatus(port);
1289
+ break;
1290
+ case "key":
1291
+ cmdKey(rest);
1292
+ break;
1293
+ case "config":
1294
+ cmdConfig(rest);
1295
+ break;
1296
+ case "backend":
1297
+ if (rest[0] === "add") await cmdBackendAdd();
1298
+ else await cmdBackendList();
1299
+ break;
1300
+ case "help":
1301
+ case "--help":
1302
+ case "-h":
1303
+ showHelp();
1304
+ break;
1305
+ default:
1306
+ showHelp();
1307
+ process.exit(1);
1308
+ }
1266
1309
  }
1310
+ main().catch((err) => {
1311
+ console.error(" Fatal:", err.message);
1312
+ process.exit(1);
1313
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bridgerapi",
3
- "version": "1.9.1",
3
+ "version": "2.0.0",
4
4
  "description": "Turn any AI CLI (Claude Code, Gemini, Codex, GitHub Copilot) into an OpenAI-compatible API — no API keys needed",
5
5
  "keywords": [
6
6
  "claude",