bridgerapi 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +91 -37
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -91,11 +91,11 @@ var ClaudeBackend = class {
|
|
|
91
91
|
available() {
|
|
92
92
|
return (0, import_fs.existsSync)(this.bin) || Boolean(which("claude"));
|
|
93
93
|
}
|
|
94
|
-
async runBlocking(prompt,
|
|
94
|
+
async runBlocking(prompt, model2) {
|
|
95
95
|
const bin = which("claude") || this.bin;
|
|
96
96
|
let out;
|
|
97
97
|
try {
|
|
98
|
-
out = (0, import_child_process.execFileSync)(bin, ["-p", "--output-format", "json", "--model",
|
|
98
|
+
out = (0, import_child_process.execFileSync)(bin, ["-p", "--output-format", "json", "--model", model2], {
|
|
99
99
|
input: prompt,
|
|
100
100
|
encoding: "utf8",
|
|
101
101
|
timeout: 3e5
|
|
@@ -106,9 +106,9 @@ var ClaudeBackend = class {
|
|
|
106
106
|
const data = JSON.parse(out.trim() || "{}");
|
|
107
107
|
return [data.result ?? "", data.usage ?? null];
|
|
108
108
|
}
|
|
109
|
-
async *stream(prompt,
|
|
109
|
+
async *stream(prompt, model2) {
|
|
110
110
|
const bin = which("claude") || this.bin;
|
|
111
|
-
yield* spawnStream(bin, ["-p", "--output-format", "text", "--model",
|
|
111
|
+
yield* spawnStream(bin, ["-p", "--output-format", "text", "--model", model2], prompt);
|
|
112
112
|
}
|
|
113
113
|
};
|
|
114
114
|
var GeminiBackend = class {
|
|
@@ -123,13 +123,13 @@ var GeminiBackend = class {
|
|
|
123
123
|
available() {
|
|
124
124
|
return Boolean(which("gemini")) || (0, import_fs.existsSync)(this.bin);
|
|
125
125
|
}
|
|
126
|
-
async runBlocking(prompt,
|
|
126
|
+
async runBlocking(prompt, model2) {
|
|
127
127
|
const bin = which("gemini") || this.bin;
|
|
128
128
|
let out;
|
|
129
129
|
try {
|
|
130
130
|
out = (0, import_child_process.execFileSync)(
|
|
131
131
|
bin,
|
|
132
|
-
["--output-format", "json", "--model",
|
|
132
|
+
["--output-format", "json", "--model", model2, "--approval-mode", "yolo"],
|
|
133
133
|
{ input: prompt, encoding: "utf8", timeout: 3e5, env: process.env }
|
|
134
134
|
);
|
|
135
135
|
} catch (e) {
|
|
@@ -149,11 +149,11 @@ var GeminiBackend = class {
|
|
|
149
149
|
return [raw, null];
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
async *stream(prompt,
|
|
152
|
+
async *stream(prompt, model2) {
|
|
153
153
|
const bin = which("gemini") || this.bin;
|
|
154
154
|
yield* spawnStream(
|
|
155
155
|
bin,
|
|
156
|
-
["--output-format", "text", "--model",
|
|
156
|
+
["--output-format", "text", "--model", model2, "--approval-mode", "yolo"],
|
|
157
157
|
prompt
|
|
158
158
|
);
|
|
159
159
|
}
|
|
@@ -170,10 +170,10 @@ var CodexBackend = class {
|
|
|
170
170
|
available() {
|
|
171
171
|
return Boolean(which("codex"));
|
|
172
172
|
}
|
|
173
|
-
async runBlocking(prompt,
|
|
173
|
+
async runBlocking(prompt, model2) {
|
|
174
174
|
let out;
|
|
175
175
|
try {
|
|
176
|
-
out = (0, import_child_process.execFileSync)(this.bin, ["-q", "--model",
|
|
176
|
+
out = (0, import_child_process.execFileSync)(this.bin, ["-q", "--model", model2, prompt], {
|
|
177
177
|
encoding: "utf8",
|
|
178
178
|
timeout: 3e5
|
|
179
179
|
});
|
|
@@ -182,8 +182,8 @@ var CodexBackend = class {
|
|
|
182
182
|
}
|
|
183
183
|
return [out.trim(), null];
|
|
184
184
|
}
|
|
185
|
-
async *stream(prompt,
|
|
186
|
-
yield* spawnStream(this.bin, ["-q", "--model",
|
|
185
|
+
async *stream(prompt, model2) {
|
|
186
|
+
yield* spawnStream(this.bin, ["-q", "--model", model2, prompt]);
|
|
187
187
|
}
|
|
188
188
|
};
|
|
189
189
|
var CopilotBackend = class {
|
|
@@ -204,7 +204,7 @@ var CopilotBackend = class {
|
|
|
204
204
|
return false;
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
-
async runBlocking(prompt,
|
|
207
|
+
async runBlocking(prompt, model2) {
|
|
208
208
|
let out;
|
|
209
209
|
try {
|
|
210
210
|
out = (0, import_child_process.execFileSync)(this.bin, ["copilot", "suggest", "-t", "general", prompt], {
|
|
@@ -216,7 +216,7 @@ var CopilotBackend = class {
|
|
|
216
216
|
}
|
|
217
217
|
return [out.trim(), null];
|
|
218
218
|
}
|
|
219
|
-
async *stream(prompt,
|
|
219
|
+
async *stream(prompt, model2) {
|
|
220
220
|
yield* spawnStream(this.bin, ["copilot", "suggest", "-t", "general", prompt]);
|
|
221
221
|
}
|
|
222
222
|
};
|
|
@@ -226,8 +226,8 @@ var BACKENDS = [
|
|
|
226
226
|
new CodexBackend(),
|
|
227
227
|
new CopilotBackend()
|
|
228
228
|
];
|
|
229
|
-
function pickBackend(
|
|
230
|
-
const m =
|
|
229
|
+
function pickBackend(model2) {
|
|
230
|
+
const m = model2.toLowerCase();
|
|
231
231
|
for (const b of BACKENDS) {
|
|
232
232
|
if (b.prefixes.some((p) => m.startsWith(p))) {
|
|
233
233
|
if (b.available()) return b;
|
|
@@ -245,23 +245,23 @@ function sse(data) {
|
|
|
245
245
|
|
|
246
246
|
`;
|
|
247
247
|
}
|
|
248
|
-
function chunk(id, ts,
|
|
248
|
+
function chunk(id, ts, model2, delta, finish) {
|
|
249
249
|
return sse({
|
|
250
250
|
id,
|
|
251
251
|
object: "chat.completion.chunk",
|
|
252
252
|
created: ts,
|
|
253
|
-
model,
|
|
253
|
+
model: model2,
|
|
254
254
|
choices: [{ index: 0, delta, finish_reason: finish ?? null }]
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
|
-
function completion(id, ts,
|
|
257
|
+
function completion(id, ts, model2, text, usage) {
|
|
258
258
|
const pt = usage ? (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.promptTokenCount ?? 0) : 0;
|
|
259
259
|
const ct = usage ? (usage.output_tokens ?? 0) + (usage.candidatesTokenCount ?? 0) : 0;
|
|
260
260
|
return {
|
|
261
261
|
id,
|
|
262
262
|
object: "chat.completion",
|
|
263
263
|
created: ts,
|
|
264
|
-
model,
|
|
264
|
+
model: model2,
|
|
265
265
|
choices: [{ index: 0, message: { role: "assistant", content: text }, finish_reason: "stop" }],
|
|
266
266
|
usage: { prompt_tokens: pt, completion_tokens: ct, total_tokens: pt + ct }
|
|
267
267
|
};
|
|
@@ -313,34 +313,34 @@ async function handleChat(req, res) {
|
|
|
313
313
|
sendJson(res, 400, { error: { message: "messages required", type: "invalid_request_error" } });
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
|
-
const
|
|
316
|
+
const model2 = body.model ?? "claude-sonnet-4-6";
|
|
317
317
|
const streaming = Boolean(body.stream);
|
|
318
318
|
const prompt = messagesToPrompt(messages);
|
|
319
|
-
const backend = pickBackend(
|
|
319
|
+
const backend = pickBackend(model2);
|
|
320
320
|
const id = `chatcmpl-${(0, import_crypto.randomUUID)().replace(/-/g, "").slice(0, 20)}`;
|
|
321
321
|
const ts = Math.floor(Date.now() / 1e3);
|
|
322
|
-
console.log(` ${backend.name} model=${
|
|
322
|
+
console.log(` ${backend.name} model=${model2} stream=${streaming} turns=${messages.length}`);
|
|
323
323
|
if (streaming) {
|
|
324
324
|
cors(res, 200);
|
|
325
325
|
res.setHeader("Content-Type", "text/event-stream");
|
|
326
326
|
res.setHeader("Cache-Control", "no-cache");
|
|
327
327
|
res.setHeader("X-Accel-Buffering", "no");
|
|
328
328
|
res.flushHeaders();
|
|
329
|
-
res.write(chunk(id, ts,
|
|
329
|
+
res.write(chunk(id, ts, model2, { role: "assistant" }));
|
|
330
330
|
try {
|
|
331
|
-
for await (const raw of backend.stream(prompt,
|
|
332
|
-
res.write(chunk(id, ts,
|
|
331
|
+
for await (const raw of backend.stream(prompt, model2)) {
|
|
332
|
+
res.write(chunk(id, ts, model2, { content: raw.toString("utf8") }));
|
|
333
333
|
}
|
|
334
334
|
} catch (err) {
|
|
335
335
|
console.error(` stream error: ${err.message}`);
|
|
336
336
|
}
|
|
337
|
-
res.write(chunk(id, ts,
|
|
337
|
+
res.write(chunk(id, ts, model2, {}, "stop"));
|
|
338
338
|
res.write("data: [DONE]\n\n");
|
|
339
339
|
res.end();
|
|
340
340
|
} else {
|
|
341
341
|
try {
|
|
342
|
-
const [text, usage] = await backend.runBlocking(prompt,
|
|
343
|
-
sendJson(res, 200, completion(id, ts,
|
|
342
|
+
const [text, usage] = await backend.runBlocking(prompt, model2);
|
|
343
|
+
sendJson(res, 200, completion(id, ts, model2, text, usage));
|
|
344
344
|
} catch (err) {
|
|
345
345
|
console.error(` error: ${err.message}`);
|
|
346
346
|
sendJson(res, 500, { error: { message: err.message, type: "server_error" } });
|
|
@@ -598,12 +598,15 @@ function parseArgs() {
|
|
|
598
598
|
const args = process.argv.slice(2);
|
|
599
599
|
const cmd2 = args[0] ?? "";
|
|
600
600
|
let port2 = PORT;
|
|
601
|
+
let model2;
|
|
601
602
|
for (let i = 1; i < args.length; i++) {
|
|
602
603
|
if ((args[i] === "--port" || args[i] === "-p") && args[i + 1]) {
|
|
603
604
|
port2 = parseInt(args[++i]);
|
|
605
|
+
} else if ((args[i] === "--model" || args[i] === "-m") && args[i + 1]) {
|
|
606
|
+
model2 = args[++i];
|
|
604
607
|
}
|
|
605
608
|
}
|
|
606
|
-
return { cmd: cmd2, port: port2 };
|
|
609
|
+
return { cmd: cmd2, port: port2, model: model2 };
|
|
607
610
|
}
|
|
608
611
|
function cmdStart(port2) {
|
|
609
612
|
(0, import_fs3.mkdirSync)(LOG_DIR, { recursive: true });
|
|
@@ -719,17 +722,65 @@ function cmdBackends() {
|
|
|
719
722
|
`);
|
|
720
723
|
}
|
|
721
724
|
}
|
|
725
|
+
async function cmdChat(model2) {
|
|
726
|
+
const available = BACKENDS.filter((b) => b.available());
|
|
727
|
+
if (available.length === 0) {
|
|
728
|
+
console.error(" No backends found. Run: bridgerapi to see setup instructions.");
|
|
729
|
+
process.exit(1);
|
|
730
|
+
}
|
|
731
|
+
const resolvedModel = model2 ?? available[0].models[0];
|
|
732
|
+
const backend = pickBackend(resolvedModel);
|
|
733
|
+
console.log();
|
|
734
|
+
console.log(` bridgerapi chat \u2014 ${backend.name} \u2014 ${resolvedModel}`);
|
|
735
|
+
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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
736
|
+
console.log(" Type your message and press Enter. Ctrl+C to exit.");
|
|
737
|
+
console.log();
|
|
738
|
+
const history = [];
|
|
739
|
+
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
740
|
+
const prompt = () => {
|
|
741
|
+
rl.question("You: ", async (input) => {
|
|
742
|
+
const text = input.trim();
|
|
743
|
+
if (!text) {
|
|
744
|
+
prompt();
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
history.push({ role: "user", content: text });
|
|
748
|
+
process.stdout.write("\n");
|
|
749
|
+
let reply = "";
|
|
750
|
+
try {
|
|
751
|
+
process.stdout.write(`${backend.name}: `);
|
|
752
|
+
for await (const chunk2 of backend.stream(messagesToPrompt(history), resolvedModel)) {
|
|
753
|
+
const piece = chunk2.toString("utf8");
|
|
754
|
+
process.stdout.write(piece);
|
|
755
|
+
reply += piece;
|
|
756
|
+
}
|
|
757
|
+
} catch (err) {
|
|
758
|
+
process.stdout.write(`
|
|
759
|
+
Error: ${err.message}`);
|
|
760
|
+
}
|
|
761
|
+
process.stdout.write("\n\n");
|
|
762
|
+
if (reply) history.push({ role: "assistant", content: reply });
|
|
763
|
+
prompt();
|
|
764
|
+
});
|
|
765
|
+
};
|
|
766
|
+
rl.on("close", () => {
|
|
767
|
+
console.log("\n Goodbye.");
|
|
768
|
+
process.exit(0);
|
|
769
|
+
});
|
|
770
|
+
prompt();
|
|
771
|
+
}
|
|
722
772
|
function showHelp() {
|
|
723
773
|
console.log(`
|
|
724
774
|
bridgerapi \u2014 OpenAI-compatible API bridge for AI CLI tools
|
|
725
775
|
|
|
726
776
|
Usage:
|
|
727
|
-
bridgerapi
|
|
728
|
-
bridgerapi
|
|
729
|
-
bridgerapi
|
|
730
|
-
bridgerapi
|
|
731
|
-
bridgerapi
|
|
732
|
-
bridgerapi
|
|
777
|
+
bridgerapi Interactive setup wizard
|
|
778
|
+
bridgerapi chat [--model <name>] Interactive chat session in terminal
|
|
779
|
+
bridgerapi start [--port n] Start API server in the foreground
|
|
780
|
+
bridgerapi install [--port n] Install as a background service
|
|
781
|
+
bridgerapi uninstall Remove background service
|
|
782
|
+
bridgerapi status Show service status
|
|
783
|
+
bridgerapi backends List detected backends
|
|
733
784
|
|
|
734
785
|
Supported backends (auto-detected):
|
|
735
786
|
claude-* \u2192 Claude Code CLI (claude login)
|
|
@@ -738,12 +789,15 @@ function showHelp() {
|
|
|
738
789
|
copilot \u2192 GitHub Copilot (gh auth login)
|
|
739
790
|
`.trim());
|
|
740
791
|
}
|
|
741
|
-
var { cmd, port } = parseArgs();
|
|
792
|
+
var { cmd, port, model } = parseArgs();
|
|
742
793
|
switch (cmd) {
|
|
743
794
|
case "":
|
|
744
795
|
case "setup":
|
|
745
796
|
cmdSetup();
|
|
746
797
|
break;
|
|
798
|
+
case "chat":
|
|
799
|
+
cmdChat(model);
|
|
800
|
+
break;
|
|
747
801
|
case "start":
|
|
748
802
|
cmdStart(port);
|
|
749
803
|
break;
|