bridgerapi 1.9.2 → 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 +268 -228
  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,46 +618,46 @@ 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) => {
659
+ function createBridgeServer(port) {
660
+ return (0, import_http.createServer)(async (req, res) => {
656
661
  try {
657
662
  const path = (req.url ?? "/").split("?")[0];
658
663
  const method = req.method ?? "GET";
@@ -666,7 +671,7 @@ function createBridgeServer(port2) {
666
671
  return;
667
672
  }
668
673
  if (method === "GET" && path === "/health") {
669
- handleHealth(res, port2);
674
+ handleHealth(res, port);
670
675
  return;
671
676
  }
672
677
  if (method === "POST" && (path === "/v1/chat/completions" || path === "/chat/completions")) {
@@ -681,7 +686,6 @@ function createBridgeServer(port2) {
681
686
  }
682
687
  }
683
688
  });
684
- return server;
685
689
  }
686
690
 
687
691
  // src/service.ts
@@ -694,13 +698,13 @@ var LABEL = "com.bridgerapi.server";
694
698
  function plistPath() {
695
699
  return (0, import_path3.join)(HOME2, "Library/LaunchAgents", `${LABEL}.plist`);
696
700
  }
697
- function writePlist(port2, scriptPath, nodePath, backend2) {
701
+ function writePlist(port, scriptPath, nodePath, backend) {
698
702
  const logDir = (0, import_path3.join)(HOME2, ".bridgerapi");
699
703
  (0, import_fs3.mkdirSync)(logDir, { recursive: true });
700
704
  (0, import_fs3.mkdirSync)((0, import_path3.join)(HOME2, "Library/LaunchAgents"), { recursive: true });
701
- const backendEntry = backend2 ? `
705
+ const backendEntry = backend ? `
702
706
  <key>BRIDGERAPI_BACKEND</key>
703
- <string>${backend2}</string>` : "";
707
+ <string>${backend}</string>` : "";
704
708
  const plist = `<?xml version="1.0" encoding="UTF-8"?>
705
709
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
706
710
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -719,13 +723,11 @@ function writePlist(port2, scriptPath, nodePath, backend2) {
719
723
  <key>EnvironmentVariables</key>
720
724
  <dict>
721
725
  <key>BRIDGERAPI_PORT</key>
722
- <string>${port2}</string>
726
+ <string>${port}</string>
723
727
  <key>PATH</key>
724
728
  <string>${HOME2}/.local/bin:/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
725
729
  <key>HOME</key>
726
- <string>${HOME2}</string>
727
- <key>CUSTOM_OKBRIDGER_API_KEY</key>
728
- <string>local</string>${backendEntry}
730
+ <string>${HOME2}</string>${backendEntry}
729
731
  </dict>
730
732
 
731
733
  <key>StandardOutPath</key>
@@ -746,12 +748,12 @@ function unitPath() {
746
748
  const configHome = process.env.XDG_CONFIG_HOME ?? (0, import_path3.join)(HOME2, ".config");
747
749
  return (0, import_path3.join)(configHome, "systemd/user/bridgerapi.service");
748
750
  }
749
- function writeUnit(port2, scriptPath, nodePath, backend2) {
751
+ function writeUnit(port, scriptPath, nodePath, backend) {
750
752
  const logDir = (0, import_path3.join)(HOME2, ".bridgerapi");
751
753
  (0, import_fs3.mkdirSync)(logDir, { recursive: true });
752
754
  (0, import_fs3.mkdirSync)((0, import_path3.join)(HOME2, ".config/systemd/user"), { recursive: true });
753
- const backendLine = backend2 ? `
754
- Environment=BRIDGERAPI_BACKEND=${backend2}` : "";
755
+ const backendLine = backend ? `
756
+ Environment=BRIDGERAPI_BACKEND=${backend}` : "";
755
757
  const unit = `[Unit]
756
758
  Description=bridgerapi \u2014 OpenAI-compatible bridge for AI CLIs
757
759
  After=network.target
@@ -759,9 +761,8 @@ After=network.target
759
761
  [Service]
760
762
  Type=simple
761
763
  ExecStart=${nodePath} ${scriptPath} start
762
- Environment=BRIDGERAPI_PORT=${port2}
764
+ Environment=BRIDGERAPI_PORT=${port}
763
765
  Environment=HOME=${HOME2}
764
- Environment=CUSTOM_OKBRIDGER_API_KEY=local
765
766
  Environment=PATH=${HOME2}/.local/bin:/usr/local/bin:/usr/bin:/bin${backendLine}
766
767
  Restart=always
767
768
  StandardOutput=append:${logDir}/server.log
@@ -772,7 +773,7 @@ WantedBy=default.target
772
773
  `;
773
774
  (0, import_fs3.writeFileSync)(unitPath(), unit);
774
775
  }
775
- function installService(port2, backend2) {
776
+ function installService(port, backend) {
776
777
  const scriptPath = process.argv[1];
777
778
  const nodePath = process.execPath;
778
779
  const os = (0, import_os3.platform)();
@@ -781,17 +782,17 @@ function installService(port2, backend2) {
781
782
  (0, import_child_process2.execSync)(`launchctl unload "${plistPath()}" 2>/dev/null`, { stdio: "ignore" });
782
783
  } catch {
783
784
  }
784
- writePlist(port2, scriptPath, nodePath, backend2);
785
+ writePlist(port, scriptPath, nodePath, backend);
785
786
  (0, import_child_process2.execSync)(`launchctl load -w "${plistPath()}"`);
786
- console.log(`\u2713 LaunchAgent installed \u2192 ${plistPath()}`);
787
+ console.log(` LaunchAgent installed -> ${plistPath()}`);
787
788
  } else if (os === "linux") {
788
- writeUnit(port2, scriptPath, nodePath, backend2);
789
+ writeUnit(port, scriptPath, nodePath, backend);
789
790
  try {
790
791
  (0, import_child_process2.execSync)("systemctl --user daemon-reload");
791
792
  } catch {
792
793
  }
793
794
  (0, import_child_process2.execSync)("systemctl --user enable --now bridgerapi");
794
- console.log(`\u2713 systemd user service installed`);
795
+ console.log(" systemd user service installed");
795
796
  } else {
796
797
  throw new Error(`Auto-install not supported on ${os}. Run 'bridgerapi start' manually.`);
797
798
  }
@@ -806,7 +807,7 @@ function uninstallService() {
806
807
  } catch {
807
808
  }
808
809
  (0, import_fs3.unlinkSync)(p);
809
- console.log("\u2713 LaunchAgent removed");
810
+ console.log(" LaunchAgent removed");
810
811
  } else {
811
812
  console.log(" bridgerapi service is not installed");
812
813
  }
@@ -823,7 +824,7 @@ function uninstallService() {
823
824
  } catch {
824
825
  }
825
826
  }
826
- console.log("\u2713 systemd service removed");
827
+ console.log(" systemd service removed");
827
828
  } else {
828
829
  throw new Error(`Not supported on ${os}`);
829
830
  }
@@ -867,6 +868,24 @@ function ask(question) {
867
868
  });
868
869
  });
869
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
+ }
870
889
  async function cmdSetup() {
871
890
  console.log();
872
891
  console.log(" bridgerapi \u2014 OpenAI-compatible API bridge for AI CLI tools");
@@ -876,8 +895,8 @@ async function cmdSetup() {
876
895
  console.log(" Backends detected:\n");
877
896
  for (const b of BACKENDS) {
878
897
  const ok = b.available();
879
- console.log(` ${ok ? "\u2713" : "\u2717"} ${b.name}`);
880
- 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"}`);
881
900
  }
882
901
  console.log();
883
902
  if (available.length === 0) {
@@ -894,7 +913,7 @@ async function cmdSetup() {
894
913
  const defaultIdx = names.indexOf(currentDefault);
895
914
  console.log(" Which backend do you want to use as default?\n");
896
915
  names.forEach((name, i) => {
897
- const marker = i === defaultIdx ? " \u2190 default" : "";
916
+ const marker = i === defaultIdx ? " <- default" : "";
898
917
  console.log(` ${i + 1} ${name}${marker}`);
899
918
  });
900
919
  console.log();
@@ -906,8 +925,8 @@ async function cmdSetup() {
906
925
  }
907
926
  const defaultPort = cfg.port ?? DEFAULT_PORT;
908
927
  const portAnswer = await ask(` Port [${defaultPort}]: `);
909
- const port2 = portAnswer ? parseInt(portAnswer) || defaultPort : defaultPort;
910
- saveConfig({ backend: chosenBackend, port: port2 });
928
+ const port = portAnswer ? parseInt(portAnswer) || defaultPort : defaultPort;
929
+ saveConfig({ ...cfg, backend: chosenBackend, port });
911
930
  console.log();
912
931
  console.log(" How do you want to run bridgerapi?");
913
932
  console.log(" 1 Foreground (stops when terminal closes)");
@@ -916,66 +935,51 @@ async function cmdSetup() {
916
935
  const choice = await ask(" Choose [1/2]: ");
917
936
  console.log();
918
937
  if (choice === "2") {
919
- cmdInstall(port2, chosenBackend);
938
+ cmdInstall(port, chosenBackend);
920
939
  } else {
921
- cmdStart(port2, chosenBackend);
940
+ cmdStart(port, chosenBackend);
922
941
  }
923
942
  }
924
- function cmdStart(port2, backend2) {
943
+ function cmdStart(port, backend) {
925
944
  (0, import_fs4.mkdirSync)(LOG_DIR, { recursive: true });
926
945
  const cfg = loadConfig();
927
- const activeBackend = backend2 ?? cfg.backend;
946
+ const activeBackend = backend ?? cfg.backend;
928
947
  if (activeBackend) process.env.BRIDGERAPI_BACKEND = activeBackend;
929
948
  const available = BACKENDS.filter((b) => b.available());
930
949
  if (available.length === 0) {
931
950
  console.error(" No CLI backends found. Run: bridgerapi to see setup instructions.");
932
951
  process.exit(1);
933
952
  }
934
- const server = createBridgeServer(port2);
935
- server.listen(port2, "127.0.0.1", () => {
936
- console.log(` bridgerapi is running`);
937
- console.log();
938
- console.log(` Base URL : http://127.0.0.1:${port2}/v1`);
939
- console.log(` API Key : local`);
940
- console.log();
941
- const backendLabel = activeBackend ? `${activeBackend} (all requests routed here)` : available.map((b) => b.name).join(", ") + " (auto-routed by model prefix)";
942
- console.log(` Backend : ${backendLabel}`);
943
- console.log(` Logs : ${LOG_DIR}/server.log`);
944
- console.log();
953
+ const server = createBridgeServer(port);
954
+ server.listen(port, "127.0.0.1", () => {
955
+ printBanner(port, activeBackend);
945
956
  console.log(" Ctrl+C to stop.");
946
957
  });
947
958
  server.on("error", (err) => {
948
959
  if (err.code === "EADDRINUSE") {
949
- 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`);
950
961
  } else {
951
962
  console.error(" Server error:", err.message);
952
963
  }
953
964
  process.exit(1);
954
965
  });
955
966
  }
956
- function cmdInstall(port2, backend2) {
967
+ function cmdInstall(port, backend) {
957
968
  const cfg = loadConfig();
958
- const activeBackend = backend2 ?? cfg.backend;
969
+ const activeBackend = backend ?? cfg.backend;
959
970
  try {
960
- installService(port2, activeBackend);
971
+ installService(port, activeBackend);
961
972
  console.log();
962
- console.log(" Waiting for server to start\u2026");
973
+ console.log(" Waiting for server to start...");
963
974
  let attempts = 0;
964
975
  const poll = setInterval(async () => {
965
976
  attempts++;
966
977
  try {
967
978
  const http = await import("http");
968
- http.get(`http://127.0.0.1:${port2}/health`, (res) => {
979
+ http.get(`http://127.0.0.1:${port}/health`, (res) => {
969
980
  if (res.statusCode === 200) {
970
981
  clearInterval(poll);
971
- console.log();
972
- console.log(` bridgerapi is running`);
973
- console.log();
974
- console.log(` Base URL : http://127.0.0.1:${port2}/v1`);
975
- console.log(` API Key : local`);
976
- if (activeBackend) console.log(` Backend : ${activeBackend}`);
977
- console.log();
978
- console.log(` Logs : tail -f ${LOG_DIR}/server.log`);
982
+ printBanner(port, activeBackend);
979
983
  console.log(` Stop : bridgerapi uninstall`);
980
984
  process.exit(0);
981
985
  }
@@ -1003,21 +1007,49 @@ function cmdUninstall() {
1003
1007
  process.exit(1);
1004
1008
  }
1005
1009
  }
1006
- function cmdStatus(port2) {
1010
+ function cmdStatus(port) {
1007
1011
  const cfg = loadConfig();
1008
1012
  const { running, pid } = serviceStatus();
1009
1013
  if (running) {
1014
+ const apiKey = getOrCreateApiKey();
1010
1015
  console.log(` bridgerapi is running${pid ? ` (pid ${pid})` : ""}`);
1011
- console.log(` Base URL : http://127.0.0.1:${port2}/v1`);
1012
- console.log(` API Key : local`);
1016
+ console.log(` Base URL : http://127.0.0.1:${port}/v1`);
1017
+ console.log(` API Key : ${apiKey}`);
1013
1018
  if (cfg.backend) console.log(` Backend : ${cfg.backend}`);
1014
1019
  } else {
1015
1020
  console.log(" bridgerapi is not running.");
1016
- console.log(" Run: bridgerapi \u2192 setup wizard");
1017
- console.log(" Run: bridgerapi start \u2192 start in foreground");
1018
- 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");
1019
1024
  }
1020
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;
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();
1052
+ }
1021
1053
  function cmdConfig(args) {
1022
1054
  const cfg = loadConfig();
1023
1055
  if (args[0] === "set") {
@@ -1037,7 +1069,7 @@ function cmdConfig(args) {
1037
1069
  process.exit(1);
1038
1070
  }
1039
1071
  cfg.backend = val;
1040
- console.log(` backend \u2192 ${val}`);
1072
+ console.log(` backend -> ${val}`);
1041
1073
  } else if (key === "port") {
1042
1074
  const p = parseInt(val);
1043
1075
  if (isNaN(p) || p < 1 || p > 65535) {
@@ -1045,7 +1077,7 @@ function cmdConfig(args) {
1045
1077
  process.exit(1);
1046
1078
  }
1047
1079
  cfg.port = p;
1048
- console.log(` port \u2192 ${p}`);
1080
+ console.log(` port -> ${p}`);
1049
1081
  } else {
1050
1082
  console.error(` Unknown key: ${key}`);
1051
1083
  console.error(` Valid keys: backend, port`);
@@ -1066,12 +1098,13 @@ function cmdConfig(args) {
1066
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");
1067
1099
  console.log(` backend : ${cfg.backend ?? "(auto \u2014 routed by model prefix)"}`);
1068
1100
  console.log(` port : ${cfg.port ?? `${DEFAULT_PORT} (default)`}`);
1101
+ console.log(` apiKey : ${cfg.apiKey ?? "(not generated yet \u2014 run: bridgerapi key)"}`);
1069
1102
  console.log(` file : ${(0, import_path4.join)((0, import_os4.homedir)(), ".bridgerapi/config.json")}`);
1070
1103
  console.log();
1071
1104
  console.log(" To change:");
1072
- console.log(` bridgerapi config set backend=claude`);
1073
- console.log(` bridgerapi config set port=9000`);
1074
- 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");
1075
1108
  console.log();
1076
1109
  }
1077
1110
  async function cmdBackendAdd() {
@@ -1095,8 +1128,8 @@ async function cmdBackendAdd() {
1095
1128
  const modeChoice = await ask(" Choose [1/2]: ");
1096
1129
  const promptMode = modeChoice === "2" ? "arg" : "stdin";
1097
1130
  console.log();
1098
- console.log(` Command arguments template. Use {model} as placeholder for the model name.`);
1099
- 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}");
1100
1133
  const argsRaw = await ask(" Args: ");
1101
1134
  const args = argsRaw.trim().split(/\s+/).filter(Boolean);
1102
1135
  const modelsCmdRaw = await ask(" Args to list models (leave blank to skip): ");
@@ -1111,11 +1144,8 @@ async function cmdBackendAdd() {
1111
1144
  else existing.push(def);
1112
1145
  saveConfig({ ...cfg, customBackends: existing });
1113
1146
  console.log();
1114
- console.log(` \u2713 ${name} backend saved.`);
1115
- console.log(` Restart bridgerapi for it to take effect.`);
1116
- console.log();
1117
- console.log(" Example JSON entry in ~/.bridgerapi/config.json:");
1118
- 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.");
1119
1149
  console.log();
1120
1150
  }
1121
1151
  async function cmdBackendList() {
@@ -1124,34 +1154,34 @@ async function cmdBackendList() {
1124
1154
  for (const b of BACKENDS) {
1125
1155
  if (!b.available()) {
1126
1156
  const hint = INSTALL_HINTS[b.name] ?? "not installed";
1127
- console.log(` \u2717 ${b.name}`);
1128
- console.log(` \u2192 ${hint}
1157
+ console.log(` - ${b.name}`);
1158
+ console.log(` -> ${hint}
1129
1159
  `);
1130
1160
  continue;
1131
1161
  }
1132
- process.stdout.write(` \u2713 ${b.name} (discovering models\u2026)\r`);
1162
+ process.stdout.write(` + ${b.name} (discovering models...)\r`);
1133
1163
  const modelList = await b.models();
1134
1164
  const preview = modelList.slice(0, 6).join(" ");
1135
1165
  const extra = modelList.length > 6 ? ` +${modelList.length - 6} more` : "";
1136
- console.log(` \u2713 ${b.name} `);
1166
+ console.log(` + ${b.name} `);
1137
1167
  console.log(` ${preview}${extra}
1138
1168
  `);
1139
1169
  }
1140
1170
  }
1141
- async function cmdChat(model2, backendFlag) {
1171
+ async function cmdChat(model, backendFlag) {
1142
1172
  const cfg = loadConfig();
1143
- 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;
1144
1174
  if (activeBackend) process.env.BRIDGERAPI_BACKEND = activeBackend;
1145
- 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;
1146
1176
  const available = BACKENDS.filter((b) => b.available());
1147
1177
  if (available.length === 0) {
1148
1178
  console.error(" No backends found. Run: bridgerapi to see setup instructions.");
1149
1179
  process.exit(1);
1150
1180
  }
1151
1181
  const fallbackModel = `${activeBackend ?? available[0].name}-default`;
1152
- const backend2 = pickBackend(resolvedModel ?? fallbackModel);
1182
+ const backend = pickBackend(resolvedModel ?? fallbackModel);
1153
1183
  console.log();
1154
- console.log(` bridgerapi chat \u2014 ${backend2.name}`);
1184
+ console.log(` bridgerapi chat \u2014 ${backend.name}`);
1155
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");
1156
1186
  console.log(" Type a message and press Enter. Ctrl+C to exit.");
1157
1187
  console.log();
@@ -1172,8 +1202,8 @@ async function cmdChat(model2, backendFlag) {
1172
1202
  process.stdout.write("\n");
1173
1203
  let reply = "";
1174
1204
  try {
1175
- process.stdout.write(`${backend2.name}: `);
1176
- 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)) {
1177
1207
  const piece = chunk2.toString("utf8");
1178
1208
  process.stdout.write(piece);
1179
1209
  reply += piece;
@@ -1195,79 +1225,89 @@ function showHelp() {
1195
1225
 
1196
1226
  Usage:
1197
1227
  bridgerapi Interactive setup wizard
1198
- bridgerapi chat [--model m] Chat in the terminal (routes by model prefix)
1199
- bridgerapi chat --backend <name> Chat using a specific backend
1200
1228
  bridgerapi start [--port n] Start API server in the foreground
1201
- 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
1202
1234
  bridgerapi install [--port n] Install as a background service
1203
1235
  bridgerapi uninstall Remove background service
1204
1236
  bridgerapi status Show service status
1205
1237
  bridgerapi config Show saved configuration
1206
- bridgerapi config set backend=<b> Set default backend (claude|gemini|codex|copilot|droid)
1238
+ bridgerapi config set backend=<b> Set default backend
1207
1239
  bridgerapi config set port=<n> Set default port
1208
1240
  bridgerapi config reset Clear saved configuration
1209
1241
  bridgerapi backend List all backends (built-in + custom)
1210
1242
  bridgerapi backend add Add a custom CLI backend interactively
1211
1243
 
1212
1244
  Built-in backends: claude, gemini, codex, copilot, droid
1213
- Custom backends: add any AI CLI via "bridgerapi backend add"
1214
1245
  `.trim());
1215
1246
  }
1216
1247
  function parseArgs() {
1217
1248
  const cfg = loadConfig();
1218
1249
  const args = process.argv.slice(2);
1219
- const cmd2 = args[0] ?? "";
1220
- let port2 = cfg.port ?? DEFAULT_PORT;
1221
- let model2;
1222
- let backend2;
1223
- const rest2 = [];
1250
+ const cmd = args[0] ?? "";
1251
+ let port = cfg.port ?? DEFAULT_PORT;
1252
+ let model;
1253
+ let backend;
1254
+ const rest = [];
1224
1255
  for (let i = 1; i < args.length; i++) {
1225
1256
  if ((args[i] === "--port" || args[i] === "-p") && args[i + 1]) {
1226
- port2 = parseInt(args[++i]);
1257
+ port = parseInt(args[++i]);
1227
1258
  } else if ((args[i] === "--model" || args[i] === "-m") && args[i + 1]) {
1228
- model2 = args[++i];
1259
+ model = args[++i];
1229
1260
  } else if ((args[i] === "--backend" || args[i] === "-b") && args[i + 1]) {
1230
- backend2 = args[++i];
1261
+ backend = args[++i];
1231
1262
  } else {
1232
- rest2.push(args[i]);
1263
+ rest.push(args[i]);
1233
1264
  }
1234
1265
  }
1235
- return { cmd: cmd2, port: port2, model: model2, backend: backend2, rest: rest2 };
1266
+ return { cmd, port, model, backend, rest };
1236
1267
  }
1237
- var { cmd, port, model, backend, rest } = parseArgs();
1238
- switch (cmd) {
1239
- case "":
1240
- case "setup":
1241
- cmdSetup();
1242
- break;
1243
- case "chat":
1244
- cmdChat(model, backend);
1245
- break;
1246
- case "start":
1247
- cmdStart(port, backend);
1248
- break;
1249
- case "install":
1250
- cmdInstall(port, backend);
1251
- break;
1252
- case "uninstall":
1253
- cmdUninstall();
1254
- break;
1255
- case "status":
1256
- cmdStatus(port);
1257
- break;
1258
- case "config":
1259
- cmdConfig(rest);
1260
- break;
1261
- case "backend":
1262
- if (rest[0] === "add") cmdBackendAdd();
1263
- else cmdBackendList();
1264
- break;
1265
- case "help":
1266
- case "--help":
1267
- case "-h":
1268
- showHelp();
1269
- break;
1270
- default:
1271
- showHelp();
1272
- 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
+ }
1273
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.2",
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",