betahi-copilot-bridge 0.20.8 → 0.20.10
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/README.md +26 -6
- package/dist/main.js +124 -23
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">copilot-bridge</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<a href="https://www.npmjs.com/package/betahi-copilot-bridge"><img src="https://img.shields.io/npm/v/betahi-copilot-bridge.svg?v=0.20.
|
|
4
|
+
<a href="https://www.npmjs.com/package/betahi-copilot-bridge"><img src="https://img.shields.io/npm/v/betahi-copilot-bridge.svg?v=0.20.10" alt="npm version"></a>
|
|
5
5
|
<a href="https://github.com/betahi/copilot-bridge/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/betahi-copilot-bridge.svg" alt="license"></a>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
## Contents
|
|
15
15
|
- [Demo](#demo)
|
|
16
|
+
- [Why this bridge?](#why-this-bridge)
|
|
16
17
|
- [Install & run](#install--run)
|
|
17
18
|
- [Configure Codex CLI](#configure-codex-cli)
|
|
18
19
|
- [Configure Claude Code](#configure-claude-code)
|
|
@@ -34,6 +35,19 @@
|
|
|
34
35
|
|
|
35
36
|

|
|
36
37
|
|
|
38
|
+
## Why this bridge?
|
|
39
|
+
|
|
40
|
+
copilot-bridge is more than a raw HTTP proxy:
|
|
41
|
+
|
|
42
|
+
1. **Codex and Claude support.** Exposes Codex `/v1/responses`, Claude Code
|
|
43
|
+
`/v1/messages`, and OpenAI-compatible chat, embeddings, and models routes.
|
|
44
|
+
2. **Web search support.** Model-selected web search can be executed by the
|
|
45
|
+
bridge and fed back into a final model pass.
|
|
46
|
+
3. **End-to-end reasoning support.** Normalizes model aliases and
|
|
47
|
+
`reasoning_effort`, routes through the right upstream API, and preserves
|
|
48
|
+
returned `reasoning_text` / `reasoning_content` in streaming and
|
|
49
|
+
non-streaming responses.
|
|
50
|
+
|
|
37
51
|
## Install & run
|
|
38
52
|
|
|
39
53
|
```sh
|
|
@@ -83,6 +97,12 @@ requires_openai_auth = false
|
|
|
83
97
|
Use `--no-codex-setup` to skip this writer if you manage
|
|
84
98
|
`~/.codex/config.toml` yourself.
|
|
85
99
|
|
|
100
|
+
The managed config also keeps Codex CLI's `/status` context-window display in
|
|
101
|
+
sync with Copilot model metadata. For example, GPT-5.5 shows a 1.05M context
|
|
102
|
+
window through the bridge:
|
|
103
|
+
|
|
104
|
+

|
|
105
|
+
|
|
86
106
|
### Codex warning: "Model metadata ... not found"
|
|
87
107
|
|
|
88
108
|
This is a Codex client-side metadata warning, not a bridge routing failure, requests can still complete through the bridge.
|
|
@@ -169,7 +189,7 @@ COPILOT_WEB_SEARCH_BACKEND = "gpt-5.5"
|
|
|
169
189
|
|
|
170
190
|
| Value | Search path | Requirement |
|
|
171
191
|
| ----- | ----------- | ----------- |
|
|
172
|
-
| Copilot model id, for example `gpt-5.5` | Copilot HTTP `/responses` + `web_search_preview` |
|
|
192
|
+
| Copilot model id, for example `gpt-5.5` | Copilot HTTP `/responses` + `web_search_preview` | Recommended: `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.3-codex`, `gpt-5.2`, `gpt-5.2-codex`. |
|
|
173
193
|
| `searxng`, use `"COPILOT_WEB_SEARCH_BACKEND": "searxng"` | Local SearXNG at `http://localhost:8080` | Start SearXNG yourself. Setup guide: https://github.com/betaHi/openclaw-searxng-search. |
|
|
174
194
|
| `copilot-cli` or `copilot`, use `"COPILOT_WEB_SEARCH_BACKEND": "copilot-cli"` | GitHub Copilot CLI `web_search` tool, using the current request model | Install and sign in to GitHub Copilot CLI yourself. |
|
|
175
195
|
|
|
@@ -254,11 +274,11 @@ accepts upstream.
|
|
|
254
274
|
| Model | Reasoning efforts | Notes |
|
|
255
275
|
| -------------------------------- | --------------------------------------- | -------------------------------------- |
|
|
256
276
|
| `claude-opus-4.7` | `medium` | Effort sent as `output_config.effort`. |
|
|
257
|
-
| `claude-opus-4.7-1m` | `low`, `medium`, `high`, `xhigh` | 1M-token context window
|
|
258
|
-
| `claude-opus-4.7-high` | `high` | Fixed high reasoning
|
|
259
|
-
| `claude-opus-4.7-xhigh` | `xhigh` | Fixed extra-high reasoning
|
|
277
|
+
| `claude-opus-4.7-1m` | `low`, `medium`, `high`, `xhigh` | 1M-token context window, prefer use `claude-opus-4.7-[1m]` in config|
|
|
278
|
+
| `claude-opus-4.7-high` | `high` | Fixed high reasoning|
|
|
279
|
+
| `claude-opus-4.7-xhigh` | `xhigh` | Fixed extra-high reasoning|
|
|
260
280
|
| `claude-opus-4.6` | `low`, `medium`, `high` | |
|
|
261
|
-
| `claude-opus-4.6-1m` | `low`, `medium`, `high` | 1M-token context window.
|
|
281
|
+
| `claude-opus-4.6-1m` | `low`, `medium`, `high` | 1M-token context window, prefer use `claude-opus-4.6-[1m]` in config |
|
|
262
282
|
| `claude-sonnet-4.6` | `low`, `medium`, `high` | |
|
|
263
283
|
| `claude-opus-4.5` | — | Reasoning not accepted upstream. |
|
|
264
284
|
| `claude-sonnet-4.5` | — | Reasoning not accepted upstream. |
|
package/dist/main.js
CHANGED
|
@@ -3,9 +3,11 @@ import { defineCommand, runMain } from "citty";
|
|
|
3
3
|
import consola from "consola";
|
|
4
4
|
import fs, { appendFile } from "node:fs/promises";
|
|
5
5
|
import os from "node:os";
|
|
6
|
-
import path from "node:path";
|
|
6
|
+
import path, { dirname, resolve } from "node:path";
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
+
import { accessSync, constants, readFileSync } from "node:fs";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
9
11
|
import { serve } from "@hono/node-server";
|
|
10
12
|
import { Hono } from "hono";
|
|
11
13
|
import { cors } from "hono/cors";
|
|
@@ -13,7 +15,6 @@ import { logger } from "hono/logger";
|
|
|
13
15
|
import { streamSSE } from "hono/streaming";
|
|
14
16
|
import { events } from "fetch-event-stream";
|
|
15
17
|
import { execFile } from "node:child_process";
|
|
16
|
-
import { accessSync, constants } from "node:fs";
|
|
17
18
|
|
|
18
19
|
//#region src/lib/error.ts
|
|
19
20
|
var BridgeNotImplementedError = class extends Error {
|
|
@@ -148,8 +149,8 @@ const standardHeaders = () => ({
|
|
|
148
149
|
accept: "application/json",
|
|
149
150
|
"content-type": "application/json"
|
|
150
151
|
});
|
|
151
|
-
const sleep$1 = (ms) => new Promise((resolve) => {
|
|
152
|
-
setTimeout(resolve, ms);
|
|
152
|
+
const sleep$1 = (ms) => new Promise((resolve$1) => {
|
|
153
|
+
setTimeout(resolve$1, ms);
|
|
153
154
|
});
|
|
154
155
|
const readGitHubToken$1 = async () => {
|
|
155
156
|
return (await fs.readFile(PATHS.GITHUB_TOKEN_PATH, "utf8")).trim();
|
|
@@ -208,7 +209,7 @@ const getCopilotToken = async (githubToken, vsCodeVersion) => {
|
|
|
208
209
|
return await response.json();
|
|
209
210
|
};
|
|
210
211
|
const isCopilotTokenError = (error) => error instanceof HTTPError && error.message === "Failed to get Copilot token";
|
|
211
|
-
const ensureGitHubToken = async (
|
|
212
|
+
const ensureGitHubToken = async (options = {}) => {
|
|
212
213
|
await ensurePaths();
|
|
213
214
|
const existingToken = options.force ? "" : await readGitHubToken$1();
|
|
214
215
|
if (existingToken) return existingToken;
|
|
@@ -217,17 +218,23 @@ const ensureGitHubToken = async (config, options = {}) => {
|
|
|
217
218
|
const githubToken = await pollAccessToken(deviceCode);
|
|
218
219
|
await writeGitHubToken(githubToken);
|
|
219
220
|
if (options.showToken) consola.info("GitHub token:", githubToken);
|
|
220
|
-
const user = await getGitHubUser(githubToken, config.vsCodeVersion);
|
|
221
|
-
consola.success(`Logged in as ${user.login}`);
|
|
222
221
|
return githubToken;
|
|
223
222
|
};
|
|
223
|
+
const loadGitHubLogin = async (githubToken, vsCodeVersion) => {
|
|
224
|
+
try {
|
|
225
|
+
return (await getGitHubUser(githubToken, vsCodeVersion)).login;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
consola.warn("Could not load GitHub user:", error);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
};
|
|
224
231
|
const setupBridgeAuth = async (config, options = {}) => {
|
|
225
232
|
if (config.copilotToken) {
|
|
226
233
|
if (options.showToken) consola.info("Using COPILOT_TOKEN from environment");
|
|
227
234
|
await loadModels(config);
|
|
228
|
-
return;
|
|
235
|
+
return { source: "copilot-token" };
|
|
229
236
|
}
|
|
230
|
-
let githubToken = await ensureGitHubToken(
|
|
237
|
+
let githubToken = await ensureGitHubToken(options);
|
|
231
238
|
const applyCopilotToken = async () => {
|
|
232
239
|
const { refresh_in, token } = await getCopilotToken(githubToken, config.vsCodeVersion);
|
|
233
240
|
config.copilotToken = token;
|
|
@@ -240,7 +247,7 @@ const setupBridgeAuth = async (config, options = {}) => {
|
|
|
240
247
|
} catch (error) {
|
|
241
248
|
if (options.force || !isCopilotTokenError(error)) throw error;
|
|
242
249
|
consola.warn("Cached GitHub auth could not get a Copilot token; running device auth again.");
|
|
243
|
-
githubToken = await ensureGitHubToken(
|
|
250
|
+
githubToken = await ensureGitHubToken({
|
|
244
251
|
...options,
|
|
245
252
|
force: true
|
|
246
253
|
});
|
|
@@ -256,6 +263,10 @@ const setupBridgeAuth = async (config, options = {}) => {
|
|
|
256
263
|
}
|
|
257
264
|
}, refreshInterval);
|
|
258
265
|
await loadModels(config);
|
|
266
|
+
return {
|
|
267
|
+
githubLogin: await loadGitHubLogin(githubToken, config.vsCodeVersion),
|
|
268
|
+
source: "github-token"
|
|
269
|
+
};
|
|
259
270
|
};
|
|
260
271
|
const loadModels = async (config) => {
|
|
261
272
|
try {
|
|
@@ -321,10 +332,11 @@ const auth = defineCommand({
|
|
|
321
332
|
port: Number(args.port)
|
|
322
333
|
});
|
|
323
334
|
config.copilotToken = void 0;
|
|
324
|
-
await setupBridgeAuth(config, {
|
|
335
|
+
const authSession = await setupBridgeAuth(config, {
|
|
325
336
|
force: true,
|
|
326
337
|
showToken: args["show-token"]
|
|
327
338
|
});
|
|
339
|
+
if (authSession.githubLogin) consola.info(`Logged in as ${authSession.githubLogin}`);
|
|
328
340
|
consola.success("copilot-bridge auth completed");
|
|
329
341
|
}
|
|
330
342
|
});
|
|
@@ -458,6 +470,12 @@ const BEGIN_MARK = "# >>> copilot-bridge managed block — auto-generated, do no
|
|
|
458
470
|
const END_MARK = "# <<< copilot-bridge managed block — edits outside this block are preserved <<<";
|
|
459
471
|
const LEGACY_BEGIN_MARK = "# >>> copilot-bridge managed (do not edit) >>>";
|
|
460
472
|
const LEGACY_END_MARK = "# <<< copilot-bridge managed (do not edit) <<<";
|
|
473
|
+
const MANAGED_MARKERS = new Set([
|
|
474
|
+
BEGIN_MARK,
|
|
475
|
+
END_MARK,
|
|
476
|
+
LEGACY_BEGIN_MARK,
|
|
477
|
+
LEGACY_END_MARK
|
|
478
|
+
]);
|
|
461
479
|
function normalizeCodexConfigReasoningEffort(value) {
|
|
462
480
|
switch (value?.toLowerCase()) {
|
|
463
481
|
case "none":
|
|
@@ -475,9 +493,11 @@ function tomlEscape(value) {
|
|
|
475
493
|
}
|
|
476
494
|
function buildManagedBlock(input) {
|
|
477
495
|
const { baseUrl, settings } = input;
|
|
496
|
+
const modelContextWindow = normalizeModelContextWindow(input.modelContextWindow);
|
|
478
497
|
const lines = [];
|
|
479
498
|
lines.push(BEGIN_MARK);
|
|
480
499
|
if (settings.setAsDefault) lines.push(`model_provider = "${tomlEscape(settings.providerId)}"`);
|
|
500
|
+
if (modelContextWindow !== void 0) lines.push(`model_context_window = ${modelContextWindow}`);
|
|
481
501
|
lines.push("model_supports_reasoning_summaries = true");
|
|
482
502
|
lines.push("");
|
|
483
503
|
lines.push(`[model_providers.${settings.providerId}]`);
|
|
@@ -489,6 +509,15 @@ function buildManagedBlock(input) {
|
|
|
489
509
|
lines.push(END_MARK);
|
|
490
510
|
return lines.join("\n");
|
|
491
511
|
}
|
|
512
|
+
function extractPreservedContentFromManagedBlock(content) {
|
|
513
|
+
const lines = content.split("\n");
|
|
514
|
+
const preserveStart = lines.findIndex((line) => {
|
|
515
|
+
const trimmed = line.trim();
|
|
516
|
+
return trimmed.startsWith("[") && !trimmed.startsWith("[model_providers.");
|
|
517
|
+
});
|
|
518
|
+
if (preserveStart === -1) return "";
|
|
519
|
+
return lines.slice(preserveStart).join("\n").replace(/^\n+|\n+$/g, "");
|
|
520
|
+
}
|
|
492
521
|
function stripManagedBlock(content) {
|
|
493
522
|
let next = content;
|
|
494
523
|
for (const [begin, end] of [[BEGIN_MARK, END_MARK], [LEGACY_BEGIN_MARK, LEGACY_END_MARK]]) while (true) {
|
|
@@ -496,14 +525,33 @@ function stripManagedBlock(content) {
|
|
|
496
525
|
if (beginIdx === -1) break;
|
|
497
526
|
const endIdx = next.indexOf(end, beginIdx);
|
|
498
527
|
if (endIdx === -1) break;
|
|
528
|
+
const preserved = extractPreservedContentFromManagedBlock(next.slice(beginIdx + begin.length, endIdx));
|
|
499
529
|
const before = next.slice(0, beginIdx).replace(/\n*$/, "");
|
|
500
|
-
const after = next.slice(endIdx + end.length).replace(/^\n+/, "");
|
|
530
|
+
const after = [preserved, next.slice(endIdx + end.length)].filter(Boolean).join("\n").replace(/^\n+/, "");
|
|
501
531
|
if (before.length === 0) next = after;
|
|
502
532
|
else if (after.length === 0) next = `${before}\n`;
|
|
503
533
|
else next = `${before}\n\n${after}`;
|
|
504
534
|
}
|
|
505
535
|
return next;
|
|
506
536
|
}
|
|
537
|
+
function removeStrayManagedMarkerLines(content) {
|
|
538
|
+
return content.split("\n").filter((line) => !MANAGED_MARKERS.has(line.trim())).join("\n");
|
|
539
|
+
}
|
|
540
|
+
function removeManagedProviderTables(content, providerId) {
|
|
541
|
+
const lines = content.split("\n");
|
|
542
|
+
const out = [];
|
|
543
|
+
const tableHeader = `[model_providers.${providerId}]`;
|
|
544
|
+
for (let i = 0; i < lines.length;) {
|
|
545
|
+
if (lines[i].trim() === tableHeader) {
|
|
546
|
+
i += 1;
|
|
547
|
+
while (i < lines.length && !/^\s*\[/.test(lines[i])) i += 1;
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
out.push(lines[i]);
|
|
551
|
+
i += 1;
|
|
552
|
+
}
|
|
553
|
+
return out.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
554
|
+
}
|
|
507
555
|
function splitTopSection(content) {
|
|
508
556
|
const lines = content.split("\n");
|
|
509
557
|
let cut = lines.length;
|
|
@@ -541,6 +589,22 @@ function removeTopKey(topSection, key) {
|
|
|
541
589
|
const re = /* @__PURE__ */ new RegExp(`^\\s*${key}\\s*=`);
|
|
542
590
|
return lines.filter((line) => !re.test(line)).join("\n");
|
|
543
591
|
}
|
|
592
|
+
function normalizeModelContextWindow(value) {
|
|
593
|
+
if (value === void 0 || !Number.isFinite(value)) return;
|
|
594
|
+
const normalized = Math.trunc(value);
|
|
595
|
+
return normalized > 0 ? normalized : void 0;
|
|
596
|
+
}
|
|
597
|
+
function removeManagedTopLevelKeys(content, input) {
|
|
598
|
+
const { top, rest } = splitTopSection(content);
|
|
599
|
+
let nextTop = top;
|
|
600
|
+
if (input.settings.setAsDefault) nextTop = removeTopKey(nextTop, "model_provider");
|
|
601
|
+
if (normalizeModelContextWindow(input.modelContextWindow) !== void 0) nextTop = removeTopKey(nextTop, "model_context_window");
|
|
602
|
+
nextTop = removeTopKey(nextTop, "model_supports_reasoning_summaries");
|
|
603
|
+
if (nextTop === top) return content;
|
|
604
|
+
if (rest.length === 0) return nextTop.endsWith("\n") ? nextTop : `${nextTop}\n`;
|
|
605
|
+
const sep = nextTop.endsWith("\n") ? "" : "\n";
|
|
606
|
+
return `${nextTop}${sep}${rest}`;
|
|
607
|
+
}
|
|
544
608
|
function sanitizeTopReasoningEffort(topSection) {
|
|
545
609
|
const match = topSection.match(scalarRegex("model_reasoning_effort"));
|
|
546
610
|
if (!match) return topSection;
|
|
@@ -570,10 +634,17 @@ async function applyCodexConfig(input) {
|
|
|
570
634
|
created = true;
|
|
571
635
|
}
|
|
572
636
|
let stripped = stripManagedBlock(existing);
|
|
637
|
+
stripped = removeStrayManagedMarkerLines(stripped);
|
|
638
|
+
stripped = removeManagedProviderTables(stripped, input.settings.providerId);
|
|
573
639
|
stripped = applyUserScalars(stripped, input);
|
|
640
|
+
stripped = removeManagedTopLevelKeys(stripped, input);
|
|
574
641
|
const block = buildManagedBlock(input);
|
|
575
|
-
const
|
|
576
|
-
const next =
|
|
642
|
+
const { top, rest } = splitTopSection(stripped);
|
|
643
|
+
const next = `${[
|
|
644
|
+
top,
|
|
645
|
+
block,
|
|
646
|
+
rest
|
|
647
|
+
].map((part) => part.replace(/^\n+|\n+$/g, "")).filter(Boolean).join("\n\n")}\n`;
|
|
577
648
|
if (next === existing) return {
|
|
578
649
|
configPath,
|
|
579
650
|
changed: false,
|
|
@@ -858,10 +929,28 @@ const clampReasoningEffort = (modelId, requested) => {
|
|
|
858
929
|
};
|
|
859
930
|
};
|
|
860
931
|
|
|
932
|
+
//#endregion
|
|
933
|
+
//#region src/lib/version.ts
|
|
934
|
+
const PACKAGE_NAME = "betahi-copilot-bridge";
|
|
935
|
+
const readPackageVersion = () => {
|
|
936
|
+
let currentDir = dirname(fileURLToPath(import.meta.url));
|
|
937
|
+
while (true) {
|
|
938
|
+
try {
|
|
939
|
+
const packageJsonPath = resolve(currentDir, "package.json");
|
|
940
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
941
|
+
if (packageJson.name === PACKAGE_NAME && typeof packageJson.version === "string") return packageJson.version;
|
|
942
|
+
} catch {}
|
|
943
|
+
const parentDir = dirname(currentDir);
|
|
944
|
+
if (parentDir === currentDir) return "unknown";
|
|
945
|
+
currentDir = parentDir;
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
const BRIDGE_VERSION = readPackageVersion();
|
|
949
|
+
|
|
861
950
|
//#endregion
|
|
862
951
|
//#region src/lib/rate-limit.ts
|
|
863
|
-
const sleep = (ms) => new Promise((resolve) => {
|
|
864
|
-
setTimeout(resolve, ms);
|
|
952
|
+
const sleep = (ms) => new Promise((resolve$1) => {
|
|
953
|
+
setTimeout(resolve$1, ms);
|
|
865
954
|
});
|
|
866
955
|
/**
|
|
867
956
|
* Enforces the configured rate limit window between mutating upstream calls.
|
|
@@ -2492,7 +2581,7 @@ const findCopilotCliCommand = () => {
|
|
|
2492
2581
|
const candidates = (process.env.PATH ?? "").split(path.delimiter).filter(Boolean).flatMap((directory) => copilotExecutableNames().map((name) => path.join(directory, name))).filter(canExecute);
|
|
2493
2582
|
return candidates.find((candidate) => !COPILOT_CHAT_WRAPPER_PATH_PATTERN.test(candidate)) ?? candidates[0] ?? "copilot";
|
|
2494
2583
|
};
|
|
2495
|
-
const runCopilotCliCommand = (args, timeout) => new Promise((resolve, reject) => {
|
|
2584
|
+
const runCopilotCliCommand = (args, timeout) => new Promise((resolve$1, reject) => {
|
|
2496
2585
|
execFile(findCopilotCliCommand(), args, {
|
|
2497
2586
|
encoding: "utf8",
|
|
2498
2587
|
timeout
|
|
@@ -2505,7 +2594,7 @@ const runCopilotCliCommand = (args, timeout) => new Promise((resolve, reject) =>
|
|
|
2505
2594
|
});
|
|
2506
2595
|
return;
|
|
2507
2596
|
}
|
|
2508
|
-
resolve({
|
|
2597
|
+
resolve$1({
|
|
2509
2598
|
stdout,
|
|
2510
2599
|
stderr
|
|
2511
2600
|
});
|
|
@@ -4067,6 +4156,12 @@ const fetchAvailableModels = async (config) => {
|
|
|
4067
4156
|
return [];
|
|
4068
4157
|
}
|
|
4069
4158
|
};
|
|
4159
|
+
const getCodexModelContextWindow = (model) => {
|
|
4160
|
+
if (!model) return void 0;
|
|
4161
|
+
const resolvedModel = resolveModelId(model);
|
|
4162
|
+
const contextWindow = (runtimeState.models?.data.find((candidate) => candidate.id === resolvedModel || candidate.id === model || getPublicModelId(candidate.id) === model || getPublicModelId(candidate.id) === resolvedModel))?.capabilities?.limits?.max_context_window_tokens;
|
|
4163
|
+
return typeof contextWindow === "number" && contextWindow > 0 ? Math.trunc(contextWindow) : void 0;
|
|
4164
|
+
};
|
|
4070
4165
|
const start = defineCommand({
|
|
4071
4166
|
meta: {
|
|
4072
4167
|
name: "start",
|
|
@@ -4142,8 +4237,12 @@ const start = defineCommand({
|
|
|
4142
4237
|
runtimeState.rateLimitWait = Boolean(args.wait);
|
|
4143
4238
|
consola.info(`Rate limit: ${parsed}s between requests (${runtimeState.rateLimitWait ? "wait" : "reject"} on overflow)`);
|
|
4144
4239
|
}
|
|
4145
|
-
await setupBridgeAuth(config, { showToken: args["show-token"] });
|
|
4240
|
+
const authSession = await setupBridgeAuth(config, { showToken: args["show-token"] });
|
|
4146
4241
|
const server = startServer(config);
|
|
4242
|
+
consola.info(`copilot-bridge version: ${BRIDGE_VERSION}`);
|
|
4243
|
+
if (authSession.githubLogin) consola.info(`GitHub user: ${authSession.githubLogin}`);
|
|
4244
|
+
else if (authSession.source === "copilot-token") consola.info("GitHub user: unavailable (using COPILOT_TOKEN)");
|
|
4245
|
+
else consola.warn("GitHub user: unavailable");
|
|
4147
4246
|
consola.success(`copilot-bridge listening on http://${config.host}:${config.port}`);
|
|
4148
4247
|
consola.info(`copilot base url: ${config.copilotBaseUrl}`);
|
|
4149
4248
|
const baseUrl = `http://${config.host}:${config.port}`;
|
|
@@ -4180,7 +4279,8 @@ const start = defineCommand({
|
|
|
4180
4279
|
configPath: codexConfigPath,
|
|
4181
4280
|
settings: CODEX_DEFAULTS,
|
|
4182
4281
|
model: chosenModel,
|
|
4183
|
-
modelReasoningEffort: chosenEffort
|
|
4282
|
+
modelReasoningEffort: chosenEffort,
|
|
4283
|
+
modelContextWindow: getCodexModelContextWindow(chosenModel)
|
|
4184
4284
|
});
|
|
4185
4285
|
if (result.changed) {
|
|
4186
4286
|
consola.success(`codex config ${result.created ? "created" : "updated"}: ${result.configPath}`);
|
|
@@ -4221,7 +4321,8 @@ const start = defineCommand({
|
|
|
4221
4321
|
configPath: codexConfigPath,
|
|
4222
4322
|
settings: CODEX_DEFAULTS,
|
|
4223
4323
|
model: chosenModel,
|
|
4224
|
-
modelReasoningEffort: chosenEffort
|
|
4324
|
+
modelReasoningEffort: chosenEffort,
|
|
4325
|
+
modelContextWindow: getCodexModelContextWindow(chosenModel)
|
|
4225
4326
|
});
|
|
4226
4327
|
if (result.changed) consola.success(`codex config updated: ${result.configPath}`);
|
|
4227
4328
|
} catch (error) {
|
|
@@ -4229,8 +4330,8 @@ const start = defineCommand({
|
|
|
4229
4330
|
}
|
|
4230
4331
|
}
|
|
4231
4332
|
}
|
|
4232
|
-
await new Promise((resolve, reject) => {
|
|
4233
|
-
server.on("close", resolve);
|
|
4333
|
+
await new Promise((resolve$1, reject) => {
|
|
4334
|
+
server.on("close", resolve$1);
|
|
4234
4335
|
server.on("error", reject);
|
|
4235
4336
|
});
|
|
4236
4337
|
}
|