@nick3/copilot-api 1.10.9 → 1.10.30
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 +140 -45
- package/README.zh-CN.md +140 -45
- package/dist/{account-COtMmvzU.js → account-DpW8RaT6.js} +3 -3
- package/dist/{account-COtMmvzU.js.map → account-DpW8RaT6.js.map} +1 -1
- package/dist/admin/AGENTS.md +19 -0
- package/dist/admin/assets/{index-BAh4eOwM.js → index-BGmG-ckX.js} +34 -34
- package/dist/admin/index.html +1 -1
- package/dist/auth-CnwhQOu6.js +327 -0
- package/dist/auth-CnwhQOu6.js.map +1 -0
- package/dist/{check-usage-DdevqHE5.js → check-usage-CseltoFJ.js} +4 -42
- package/dist/check-usage-CseltoFJ.js.map +1 -0
- package/dist/config-XZv75uoU.js +591 -0
- package/dist/config-XZv75uoU.js.map +1 -0
- package/dist/{debug-BMo6ltbp.js → debug-D8xHblDV.js} +18 -7
- package/dist/debug-D8xHblDV.js.map +1 -0
- package/dist/main.js +5 -10
- package/dist/main.js.map +1 -1
- package/dist/mcp-http-BhELuvog.js +2 -0
- package/dist/mcp-http-DI4Vz01p.js +82 -0
- package/dist/mcp-http-DI4Vz01p.js.map +1 -0
- package/dist/mcp-http-config-DMdUDz1D.js +39 -0
- package/dist/mcp-http-config-DMdUDz1D.js.map +1 -0
- package/dist/mcp-pLTPS0tO.js +79 -0
- package/dist/mcp-pLTPS0tO.js.map +1 -0
- package/dist/{tool-search-BrN7M0Dd.js → mcp-server-DEqHrXFq.js} +25 -2
- package/dist/mcp-server-DEqHrXFq.js.map +1 -0
- package/dist/{paths-CclKwouX.js → paths-Bpsb62LK.js} +3 -1
- package/dist/paths-Bpsb62LK.js.map +1 -0
- package/dist/{poll-access-token-BAgM2-7k.js → poll-access-token-GzVkiTH8.js} +71 -4
- package/dist/poll-access-token-GzVkiTH8.js.map +1 -0
- package/dist/{request-outbound-BJjWS_jF.js → request-outbound-BkEA8Wgb.js} +1 -1
- package/dist/{request-outbound-Pu1kp2x8.js → request-outbound-DZTxxtcx.js} +3 -3
- package/dist/{request-outbound-Pu1kp2x8.js.map → request-outbound-DZTxxtcx.js.map} +1 -1
- package/dist/{proxy-_U-hgwIn.js → responses-bridge-registry-JjUvTCST.js} +127 -581
- package/dist/responses-bridge-registry-JjUvTCST.js.map +1 -0
- package/dist/{server-GxNB5Syq.js → server-BYKxAFro.js} +678 -249
- package/dist/server-BYKxAFro.js.map +1 -0
- package/dist/start-DVlCJ0Ma.js +526 -0
- package/dist/start-DVlCJ0Ma.js.map +1 -0
- package/dist/token-9O2KJcF5.js +946 -0
- package/dist/token-9O2KJcF5.js.map +1 -0
- package/package.json +2 -2
- package/dist/auth-B0y-2njL.js +0 -226
- package/dist/auth-B0y-2njL.js.map +0 -1
- package/dist/check-usage-DdevqHE5.js.map +0 -1
- package/dist/debug-BMo6ltbp.js.map +0 -1
- package/dist/get-copilot-token-8Rm-rVsp.js +0 -17
- package/dist/get-copilot-token-8Rm-rVsp.js.map +0 -1
- package/dist/mcp-9Hgepkc5.js +0 -37
- package/dist/mcp-9Hgepkc5.js.map +0 -1
- package/dist/paths-CclKwouX.js.map +0 -1
- package/dist/poll-access-token-BAgM2-7k.js.map +0 -1
- package/dist/proxy-_U-hgwIn.js.map +0 -1
- package/dist/server-GxNB5Syq.js.map +0 -1
- package/dist/start-DdrurmQ3.js +0 -274
- package/dist/start-DdrurmQ3.js.map +0 -1
- package/dist/tool-search-BrN7M0Dd.js.map +0 -1
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import { I as initOpencodeVersion, a as cacheVsCodeSessionId, h as getDeviceCode, i as cacheVsCodeDeviceId, j as state, m as getGitHubUser, n as cacheMacMachineId, r as cacheVSCodeVersion, t as pollAccessToken, y as initProxyFromEnv } from "./poll-access-token-GzVkiTH8.js";
|
|
2
|
+
import { h as saveAccountToken, n as parseAccountType, r as addAccountToRegistry } from "./account-DpW8RaT6.js";
|
|
3
|
+
import { r as ensurePaths } from "./paths-Bpsb62LK.js";
|
|
4
|
+
import { T as mergeConfigWithDefaults, _ as isAccountAffinityEnabled, u as getModelRefreshIntervalMs } from "./config-XZv75uoU.js";
|
|
5
|
+
import { i as isMcpHttpEnabledFromEnv } from "./mcp-http-config-DMdUDz1D.js";
|
|
6
|
+
import { T as isAuthorizedHeaders, a as startQuotaRefreshSchedulerFromConfig, c as accountsManager, i as registerQuotaRefreshSchedulerShutdownCleanup, l as applySharedSessionAffinityRetention, n as registerResponsesBridge, o as stopQuotaRefreshScheduler, r as unregisterResponsesBridge, w as createUnauthorizedRawResponse } from "./responses-bridge-registry-JjUvTCST.js";
|
|
7
|
+
import { defineCommand } from "citty";
|
|
8
|
+
import consola from "consola";
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { execSync } from "node:child_process";
|
|
11
|
+
import clipboard from "clipboardy";
|
|
12
|
+
import process$1 from "node:process";
|
|
13
|
+
//#region src/routes/responses/websocket.ts
|
|
14
|
+
const RESPONSES_BRIDGE_ID_HEADER = "x-responses-bridge-id";
|
|
15
|
+
const RESPONSES_WEBSOCKET_PATHS = new Set(["/responses", "/v1/responses"]);
|
|
16
|
+
const SSE_RECORD_SEPARATOR = /\r?\n\r?\n/u;
|
|
17
|
+
const FORWARDED_HEADER_NAMES = [
|
|
18
|
+
"authorization",
|
|
19
|
+
"x-api-key",
|
|
20
|
+
"user-agent",
|
|
21
|
+
"x-session-id",
|
|
22
|
+
"x-session-affinity",
|
|
23
|
+
"x-parent-session-id",
|
|
24
|
+
"x-trace-id"
|
|
25
|
+
];
|
|
26
|
+
function createResponsesWebSocketHandler(appFetch) {
|
|
27
|
+
return {
|
|
28
|
+
data: {},
|
|
29
|
+
idleTimeout: 0,
|
|
30
|
+
open(ws) {
|
|
31
|
+
registerResponsesBridge(ws.data.bridgeId, () => {
|
|
32
|
+
try {
|
|
33
|
+
ws.close();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
consola.warn("Failed to close Responses websocket bridge:", error);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
async message(ws, message) {
|
|
40
|
+
await handleResponsesWebSocketMessage(ws, message, appFetch);
|
|
41
|
+
},
|
|
42
|
+
close(ws) {
|
|
43
|
+
unregisterResponsesBridge(ws.data.bridgeId);
|
|
44
|
+
for (const controller of ws.data.controllers) controller.abort();
|
|
45
|
+
ws.data.controllers.clear();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function handleResponsesWebSocketUpgrade(req, server) {
|
|
50
|
+
if (!isResponsesWebSocketUpgrade(req)) return null;
|
|
51
|
+
if (!isAuthorizedHeaders(req.headers)) return createUnauthorizedRawResponse();
|
|
52
|
+
if (server.upgrade(req, { data: buildResponsesWebSocketData(req) })) return;
|
|
53
|
+
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
54
|
+
}
|
|
55
|
+
function isResponsesWebSocketUpgrade(req) {
|
|
56
|
+
if (req.method !== "GET") return false;
|
|
57
|
+
const pathname = new URL(req.url).pathname;
|
|
58
|
+
if (!RESPONSES_WEBSOCKET_PATHS.has(pathname)) return false;
|
|
59
|
+
return req.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
60
|
+
}
|
|
61
|
+
async function handleResponsesWebSocketMessage(ws, message, appFetch) {
|
|
62
|
+
let frame;
|
|
63
|
+
try {
|
|
64
|
+
frame = JSON.parse(normalizeWebSocketMessage(message));
|
|
65
|
+
} catch {
|
|
66
|
+
sendResponsesWebSocketError(ws, "Invalid websocket JSON frame", 400);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (frame.type === "response.processed") return;
|
|
70
|
+
if (frame.type !== "response.create") {
|
|
71
|
+
sendResponsesWebSocketError(ws, "Unsupported websocket frame type", 400);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const queued = ws.data.queue.catch(() => {}).then(() => processResponseCreateFrame(ws, frame, appFetch));
|
|
75
|
+
ws.data.queue = queued;
|
|
76
|
+
await queued.catch(() => {});
|
|
77
|
+
}
|
|
78
|
+
async function processResponseCreateFrame(ws, frame, appFetch) {
|
|
79
|
+
if (frame.generate === false) {
|
|
80
|
+
sendResponsesWebSocketWarmupCompleted(ws);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
ws.data.controllers.add(controller);
|
|
85
|
+
try {
|
|
86
|
+
await forwardResponseCreateFrame(ws, frame, appFetch, controller);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (controller.signal.aborted) return;
|
|
89
|
+
consola.warn("Responses websocket bridge failed:", error);
|
|
90
|
+
sendResponsesWebSocketError(ws, error instanceof Error ? error.message : String(error), 500);
|
|
91
|
+
} finally {
|
|
92
|
+
ws.data.controllers.delete(controller);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function parseSseRecords(chunk) {
|
|
96
|
+
const events = [];
|
|
97
|
+
let remainder = chunk;
|
|
98
|
+
while (true) {
|
|
99
|
+
const match = SSE_RECORD_SEPARATOR.exec(remainder);
|
|
100
|
+
if (!match) break;
|
|
101
|
+
const record = remainder.slice(0, match.index);
|
|
102
|
+
remainder = remainder.slice(match.index + match[0].length);
|
|
103
|
+
const data = parseSseRecordData(record);
|
|
104
|
+
if (data !== null && data !== "[DONE]") events.push(data);
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
events,
|
|
108
|
+
remainder
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function buildResponsesWebSocketData(req) {
|
|
112
|
+
return {
|
|
113
|
+
queue: Promise.resolve(),
|
|
114
|
+
controllers: /* @__PURE__ */ new Set(),
|
|
115
|
+
headers: collectForwardedHeaders(req.headers),
|
|
116
|
+
url: req.url,
|
|
117
|
+
bridgeId: randomUUID()
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function collectForwardedHeaders(headers) {
|
|
121
|
+
const forwarded = [];
|
|
122
|
+
for (const name of FORWARDED_HEADER_NAMES) {
|
|
123
|
+
const value = headers.get(name);
|
|
124
|
+
if (value) forwarded.push([name, value]);
|
|
125
|
+
}
|
|
126
|
+
return forwarded;
|
|
127
|
+
}
|
|
128
|
+
async function forwardResponseCreateFrame(ws, frame, appFetch, controller) {
|
|
129
|
+
const payload = {
|
|
130
|
+
...frame,
|
|
131
|
+
stream: true
|
|
132
|
+
};
|
|
133
|
+
delete payload.type;
|
|
134
|
+
const response = await appFetch(new Request(buildInternalResponsesUrl(ws.data.url), {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: buildInternalResponsesHeaders(ws.data.headers, ws.data.bridgeId),
|
|
137
|
+
body: JSON.stringify(payload),
|
|
138
|
+
signal: controller.signal
|
|
139
|
+
}));
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
sendResponsesWebSocketError(ws, await readErrorResponseMessage(response), response.status);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (!response.body) {
|
|
145
|
+
sendResponsesWebSocketError(ws, "Responses stream body is empty", 500);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
await streamSseResponseToWebSocket(ws, response.body, controller);
|
|
149
|
+
}
|
|
150
|
+
function buildInternalResponsesUrl(sourceUrl) {
|
|
151
|
+
const url = new URL(sourceUrl);
|
|
152
|
+
url.protocol = "http:";
|
|
153
|
+
url.pathname = url.pathname.startsWith("/v1/") ? "/v1/responses" : "/responses";
|
|
154
|
+
url.search = "";
|
|
155
|
+
url.hash = "";
|
|
156
|
+
return url.toString();
|
|
157
|
+
}
|
|
158
|
+
function buildInternalResponsesHeaders(forwardedHeaders, bridgeId) {
|
|
159
|
+
const headers = new Headers(forwardedHeaders);
|
|
160
|
+
headers.set("content-type", "application/json");
|
|
161
|
+
headers.set(RESPONSES_BRIDGE_ID_HEADER, bridgeId);
|
|
162
|
+
return headers;
|
|
163
|
+
}
|
|
164
|
+
async function streamSseResponseToWebSocket(ws, body, controller) {
|
|
165
|
+
const reader = body.getReader();
|
|
166
|
+
const decoder = new TextDecoder();
|
|
167
|
+
let buffer = "";
|
|
168
|
+
const onAbort = () => {
|
|
169
|
+
reader.cancel();
|
|
170
|
+
};
|
|
171
|
+
controller.signal.addEventListener("abort", onAbort);
|
|
172
|
+
try {
|
|
173
|
+
while (!controller.signal.aborted) {
|
|
174
|
+
const { done, value } = await reader.read();
|
|
175
|
+
if (done) break;
|
|
176
|
+
buffer += decoder.decode(value, { stream: true });
|
|
177
|
+
const parsed = parseSseRecords(buffer);
|
|
178
|
+
buffer = parsed.remainder;
|
|
179
|
+
if (!relaySseEvents(ws, parsed.events, controller)) return;
|
|
180
|
+
}
|
|
181
|
+
if (controller.signal.aborted) return;
|
|
182
|
+
buffer += decoder.decode();
|
|
183
|
+
relaySseEvents(ws, parseSseRecords(`${buffer}\n\n`).events, controller);
|
|
184
|
+
} finally {
|
|
185
|
+
controller.signal.removeEventListener("abort", onAbort);
|
|
186
|
+
try {
|
|
187
|
+
reader.releaseLock();
|
|
188
|
+
} catch {}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Relay parsed SSE events to the client. Returns `false` if a send failed and
|
|
193
|
+
* the upstream request was aborted, signalling the caller to stop streaming.
|
|
194
|
+
*/
|
|
195
|
+
function relaySseEvents(ws, events, controller) {
|
|
196
|
+
for (const event of events) {
|
|
197
|
+
if (controller.signal.aborted) return false;
|
|
198
|
+
try {
|
|
199
|
+
ws.send(event);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
consola.warn("Responses websocket send failed:", error);
|
|
202
|
+
controller.abort();
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
function parseSseRecordData(record) {
|
|
209
|
+
const dataLines = record.split(/\r?\n/u).filter((line) => line.startsWith("data:")).map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5));
|
|
210
|
+
if (dataLines.length === 0) return null;
|
|
211
|
+
return dataLines.join("\n");
|
|
212
|
+
}
|
|
213
|
+
function normalizeWebSocketMessage(message) {
|
|
214
|
+
if (typeof message === "string") return message;
|
|
215
|
+
return new TextDecoder().decode(message);
|
|
216
|
+
}
|
|
217
|
+
function sendResponsesWebSocketError(ws, message, status) {
|
|
218
|
+
ws.send(JSON.stringify({
|
|
219
|
+
code: status ? String(status) : null,
|
|
220
|
+
message,
|
|
221
|
+
param: null,
|
|
222
|
+
sequence_number: 0,
|
|
223
|
+
type: "error"
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
function sendResponsesWebSocketWarmupCompleted(ws) {
|
|
227
|
+
ws.send(JSON.stringify({
|
|
228
|
+
response: { id: "" },
|
|
229
|
+
sequence_number: 0,
|
|
230
|
+
type: "response.completed"
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
async function readErrorResponseMessage(response) {
|
|
234
|
+
const body = await response.text();
|
|
235
|
+
if (!body) return `Responses request failed with status ${response.status}`;
|
|
236
|
+
try {
|
|
237
|
+
const parsed = JSON.parse(body);
|
|
238
|
+
return (typeof parsed.error?.message === "string" ? parsed.error.message : typeof parsed.message === "string" ? parsed.message : void 0) ?? body;
|
|
239
|
+
} catch {
|
|
240
|
+
return body;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/lib/shell.ts
|
|
245
|
+
function getShell() {
|
|
246
|
+
const { platform, ppid, env } = process$1;
|
|
247
|
+
if (platform === "win32") {
|
|
248
|
+
try {
|
|
249
|
+
if (execSync(`wmic process get ParentProcessId,Name | findstr "${ppid}"`, { stdio: "pipe" }).toString().toLowerCase().includes("powershell.exe")) return "powershell";
|
|
250
|
+
} catch {
|
|
251
|
+
return "cmd";
|
|
252
|
+
}
|
|
253
|
+
return "cmd";
|
|
254
|
+
} else {
|
|
255
|
+
const shellPath = env.SHELL;
|
|
256
|
+
if (shellPath) {
|
|
257
|
+
if (shellPath.endsWith("zsh")) return "zsh";
|
|
258
|
+
if (shellPath.endsWith("fish")) return "fish";
|
|
259
|
+
if (shellPath.endsWith("bash")) return "bash";
|
|
260
|
+
}
|
|
261
|
+
return "sh";
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Generates a copy-pasteable script to set multiple environment variables
|
|
266
|
+
* and run a subsequent command.
|
|
267
|
+
* @param {EnvVars} envVars - An object of environment variables to set.
|
|
268
|
+
* @param {string} commandToRun - The command to run after setting the variables.
|
|
269
|
+
* @returns {string} The formatted script string.
|
|
270
|
+
*/
|
|
271
|
+
function generateEnvScript(envVars, commandToRun = "") {
|
|
272
|
+
const shell = getShell();
|
|
273
|
+
const filteredEnvVars = Object.entries(envVars).filter(([, value]) => value !== void 0);
|
|
274
|
+
let commandBlock;
|
|
275
|
+
switch (shell) {
|
|
276
|
+
case "powershell":
|
|
277
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `$env:${key} = ${value}`).join("; ");
|
|
278
|
+
break;
|
|
279
|
+
case "cmd":
|
|
280
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `set ${key}=${value}`).join(" & ");
|
|
281
|
+
break;
|
|
282
|
+
case "fish":
|
|
283
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `set -gx ${key} ${value}`).join("; ");
|
|
284
|
+
break;
|
|
285
|
+
default: {
|
|
286
|
+
const assignments = filteredEnvVars.map(([key, value]) => `${key}=${value}`).join(" ");
|
|
287
|
+
commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : "";
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (commandBlock && commandToRun) return `${commandBlock}${shell === "cmd" ? " & " : " && "}${commandToRun}`;
|
|
292
|
+
return commandBlock || commandToRun;
|
|
293
|
+
}
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/start.ts
|
|
296
|
+
/**
|
|
297
|
+
* Run the interactive authentication flow to add a new account.
|
|
298
|
+
* Called automatically when no accounts are found.
|
|
299
|
+
*/
|
|
300
|
+
async function runAuthFlow(accountType) {
|
|
301
|
+
consola.warn("No accounts found. Starting authentication flow...");
|
|
302
|
+
const deviceResponse = await getDeviceCode();
|
|
303
|
+
consola.info(`Please enter the code "${deviceResponse.user_code}" at ${deviceResponse.verification_uri}`);
|
|
304
|
+
const token = await pollAccessToken(deviceResponse);
|
|
305
|
+
if (state.showToken) consola.info("GitHub token:", token);
|
|
306
|
+
const accountId = (await getGitHubUser({
|
|
307
|
+
githubToken: token,
|
|
308
|
+
accountType
|
|
309
|
+
})).login;
|
|
310
|
+
await saveAccountToken(accountId, token);
|
|
311
|
+
await addAccountToRegistry({
|
|
312
|
+
id: accountId,
|
|
313
|
+
accountType,
|
|
314
|
+
addedAt: Date.now()
|
|
315
|
+
});
|
|
316
|
+
consola.success(`Account "${accountId}" added successfully!`);
|
|
317
|
+
}
|
|
318
|
+
function logClaudeCodeTip() {
|
|
319
|
+
consola.log("\n💡 Tip: The --claude-code flag simply generates a clipboard command for launching Claude Code. \nAll models remain fully accessible without this flag, just configure the model ID directly in your settings.json file.");
|
|
320
|
+
}
|
|
321
|
+
async function setupClaudeCode(models, serverUrl) {
|
|
322
|
+
const selectedModel = await consola.prompt("Select a model to use with Claude Code", {
|
|
323
|
+
type: "select",
|
|
324
|
+
options: models.data.map((model) => model.id)
|
|
325
|
+
});
|
|
326
|
+
const command = generateEnvScript({
|
|
327
|
+
ANTHROPIC_BASE_URL: serverUrl,
|
|
328
|
+
ANTHROPIC_AUTH_TOKEN: "dummy",
|
|
329
|
+
ANTHROPIC_MODEL: selectedModel,
|
|
330
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: selectedModel,
|
|
331
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: await consola.prompt("Select a small model to use with Claude Code", {
|
|
332
|
+
type: "select",
|
|
333
|
+
options: models.data.map((model) => model.id)
|
|
334
|
+
}),
|
|
335
|
+
DISABLE_NON_ESSENTIAL_MODEL_CALLS: "1",
|
|
336
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
337
|
+
CLAUDE_CODE_ATTRIBUTION_HEADER: "0",
|
|
338
|
+
CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: "false",
|
|
339
|
+
CLAUDE_CODE_DISABLE_TERMINAL_TITLE: "true",
|
|
340
|
+
CLAUDE_CODE_ENABLE_AWAY_SUMMARY: "0",
|
|
341
|
+
CLAUDE_PLUGIN_ENABLE_QUESTION_RULES: "true"
|
|
342
|
+
}, "claude");
|
|
343
|
+
try {
|
|
344
|
+
clipboard.writeSync(command);
|
|
345
|
+
consola.success("Copied Claude Code command to clipboard!");
|
|
346
|
+
} catch {
|
|
347
|
+
consola.warn("Failed to copy to clipboard. Here is the Claude Code command:");
|
|
348
|
+
consola.log(command);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async function runServer(options) {
|
|
352
|
+
if (typeof Bun === "undefined") {
|
|
353
|
+
consola.error("The Responses WebSocket transport requires the Bun runtime. Start the proxy with 'bun' or 'bunx --bun' instead of Node.");
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
consola.options.throttle = 0;
|
|
357
|
+
mergeConfigWithDefaults();
|
|
358
|
+
accountsManager.setAccountAffinityEnabled(isAccountAffinityEnabled());
|
|
359
|
+
accountsManager.setModelsRefreshIntervalMs(getModelRefreshIntervalMs());
|
|
360
|
+
await initOpencodeVersion();
|
|
361
|
+
if (options.proxyEnv) initProxyFromEnv();
|
|
362
|
+
const enableMcpHttp = options.enableMcpHttp || isMcpHttpEnabledFromEnv();
|
|
363
|
+
if (enableMcpHttp) consola.warn("Main server MCP endpoint is enabled and unauthenticated at /mcp.");
|
|
364
|
+
state.verbose = options.verbose;
|
|
365
|
+
if (options.verbose) {
|
|
366
|
+
consola.level = 5;
|
|
367
|
+
consola.info("Verbose logging enabled");
|
|
368
|
+
}
|
|
369
|
+
state.accountType = options.accountType;
|
|
370
|
+
if (options.accountType !== "individual") consola.info(`Using ${options.accountType} plan GitHub account`);
|
|
371
|
+
state.manualApprove = options.manual;
|
|
372
|
+
state.rateLimitSeconds = options.rateLimit;
|
|
373
|
+
state.rateLimitWait = options.rateLimitWait;
|
|
374
|
+
state.showToken = options.showToken;
|
|
375
|
+
await ensurePaths();
|
|
376
|
+
applySharedSessionAffinityRetention();
|
|
377
|
+
await cacheVSCodeVersion();
|
|
378
|
+
cacheMacMachineId();
|
|
379
|
+
cacheVsCodeSessionId();
|
|
380
|
+
await cacheVsCodeDeviceId();
|
|
381
|
+
await accountsManager.initialize(state.vsCodeVersion);
|
|
382
|
+
if (options.githubToken) {
|
|
383
|
+
await accountsManager.setTemporaryAccount(options.githubToken, options.accountType);
|
|
384
|
+
consola.info("Using provided GitHub token as temporary account");
|
|
385
|
+
}
|
|
386
|
+
if (!accountsManager.hasAccounts()) if (options.skipAuth) consola.warn("No accounts found. Skipping auth flow (--skip-auth). Add accounts via the Admin UI.");
|
|
387
|
+
else try {
|
|
388
|
+
await runAuthFlow(options.accountType);
|
|
389
|
+
accountsManager.shutdown();
|
|
390
|
+
stopQuotaRefreshScheduler();
|
|
391
|
+
await accountsManager.initialize(state.vsCodeVersion);
|
|
392
|
+
accountsManager.setModelsRefreshIntervalMs(getModelRefreshIntervalMs());
|
|
393
|
+
} catch (error) {
|
|
394
|
+
consola.error("Failed to add account:", error);
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
startQuotaRefreshSchedulerFromConfig();
|
|
398
|
+
registerQuotaRefreshSchedulerShutdownCleanup();
|
|
399
|
+
const models = accountsManager.getFirstAccountModels();
|
|
400
|
+
consola.info(`Available models: \n${models?.data.map((model) => `- ${model.id}`).join("\n") ?? "(no models loaded)"}`);
|
|
401
|
+
const serverUrl = `http://localhost:${options.port}`;
|
|
402
|
+
if (options.claudeCode) {
|
|
403
|
+
logClaudeCodeTip();
|
|
404
|
+
if (!models?.data.length) {
|
|
405
|
+
consola.error("Claude Code requires available models. Add an account via the Admin UI or remove --claude-code.");
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
await setupClaudeCode(models, serverUrl);
|
|
409
|
+
}
|
|
410
|
+
consola.box(`🌐 Admin UI: ${serverUrl}/admin`);
|
|
411
|
+
const { createServer } = await import("./server-BYKxAFro.js");
|
|
412
|
+
const server = createServer({ enableMcpHttp });
|
|
413
|
+
const responsesWebSocketHandler = createResponsesWebSocketHandler(server.fetch);
|
|
414
|
+
Bun.serve({
|
|
415
|
+
port: options.port,
|
|
416
|
+
idleTimeout: 0,
|
|
417
|
+
fetch: (request, bunServer) => {
|
|
418
|
+
const websocketResponse = handleResponsesWebSocketUpgrade(request, bunServer);
|
|
419
|
+
if (websocketResponse !== null) return websocketResponse;
|
|
420
|
+
return server.fetch(request);
|
|
421
|
+
},
|
|
422
|
+
websocket: responsesWebSocketHandler
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
const start = defineCommand({
|
|
426
|
+
meta: {
|
|
427
|
+
name: "start",
|
|
428
|
+
description: "Start the Copilot API server"
|
|
429
|
+
},
|
|
430
|
+
args: {
|
|
431
|
+
port: {
|
|
432
|
+
alias: "p",
|
|
433
|
+
type: "string",
|
|
434
|
+
default: "4141",
|
|
435
|
+
description: "Port to listen on"
|
|
436
|
+
},
|
|
437
|
+
verbose: {
|
|
438
|
+
alias: "v",
|
|
439
|
+
type: "boolean",
|
|
440
|
+
default: false,
|
|
441
|
+
description: "Enable verbose logging"
|
|
442
|
+
},
|
|
443
|
+
"account-type": {
|
|
444
|
+
alias: "a",
|
|
445
|
+
type: "string",
|
|
446
|
+
default: "individual",
|
|
447
|
+
description: "Account type to use (individual, business, enterprise)"
|
|
448
|
+
},
|
|
449
|
+
manual: {
|
|
450
|
+
type: "boolean",
|
|
451
|
+
default: false,
|
|
452
|
+
description: "Enable manual request approval"
|
|
453
|
+
},
|
|
454
|
+
"rate-limit": {
|
|
455
|
+
alias: "r",
|
|
456
|
+
type: "string",
|
|
457
|
+
description: "Rate limit in seconds between requests"
|
|
458
|
+
},
|
|
459
|
+
wait: {
|
|
460
|
+
alias: "w",
|
|
461
|
+
type: "boolean",
|
|
462
|
+
default: false,
|
|
463
|
+
description: "Wait instead of error when rate limit is hit. Has no effect if rate limit is not set"
|
|
464
|
+
},
|
|
465
|
+
"github-token": {
|
|
466
|
+
alias: "g",
|
|
467
|
+
type: "string",
|
|
468
|
+
description: "Provide GitHub token directly (must be generated using the `auth` subcommand)"
|
|
469
|
+
},
|
|
470
|
+
"claude-code": {
|
|
471
|
+
alias: "c",
|
|
472
|
+
type: "boolean",
|
|
473
|
+
default: false,
|
|
474
|
+
description: "Generate a command to launch Claude Code with Copilot API config"
|
|
475
|
+
},
|
|
476
|
+
"show-token": {
|
|
477
|
+
type: "boolean",
|
|
478
|
+
default: false,
|
|
479
|
+
description: "Show GitHub and Copilot tokens on fetch and refresh"
|
|
480
|
+
},
|
|
481
|
+
"proxy-env": {
|
|
482
|
+
type: "boolean",
|
|
483
|
+
default: false,
|
|
484
|
+
description: "Initialize proxy from environment variables"
|
|
485
|
+
},
|
|
486
|
+
"skip-auth": {
|
|
487
|
+
type: "boolean",
|
|
488
|
+
default: false,
|
|
489
|
+
description: "Skip the initial CLI auth flow when no accounts are found. Use this to add accounts via the Admin UI instead."
|
|
490
|
+
},
|
|
491
|
+
"enable-mcp-http": {
|
|
492
|
+
type: "boolean",
|
|
493
|
+
default: false,
|
|
494
|
+
description: "Expose the unauthenticated MCP Streamable HTTP endpoint at /mcp."
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
run({ args }) {
|
|
498
|
+
const rateLimitRaw = args["rate-limit"];
|
|
499
|
+
const rateLimit = rateLimitRaw === void 0 ? void 0 : Number.parseInt(rateLimitRaw, 10);
|
|
500
|
+
let accountType;
|
|
501
|
+
try {
|
|
502
|
+
accountType = parseAccountType(args["account-type"]);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
consola.error(error instanceof Error ? error.message : String(error));
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
return runServer({
|
|
508
|
+
port: Number.parseInt(args.port, 10),
|
|
509
|
+
verbose: args.verbose,
|
|
510
|
+
accountType,
|
|
511
|
+
manual: args.manual,
|
|
512
|
+
rateLimit,
|
|
513
|
+
rateLimitWait: args.wait,
|
|
514
|
+
githubToken: args["github-token"],
|
|
515
|
+
claudeCode: args["claude-code"],
|
|
516
|
+
showToken: args["show-token"],
|
|
517
|
+
proxyEnv: args["proxy-env"],
|
|
518
|
+
skipAuth: args["skip-auth"],
|
|
519
|
+
enableMcpHttp: args["enable-mcp-http"]
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
//#endregion
|
|
524
|
+
export { start };
|
|
525
|
+
|
|
526
|
+
//# sourceMappingURL=start-DVlCJ0Ma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-DVlCJ0Ma.js","names":["process"],"sources":["../src/routes/responses/websocket.ts","../src/lib/shell.ts","../src/start.ts"],"sourcesContent":["import consola from \"consola\"\nimport { randomUUID } from \"node:crypto\"\n\nimport {\n createUnauthorizedRawResponse,\n isAuthorizedHeaders,\n} from \"~/lib/request-auth\"\nimport {\n registerResponsesBridge,\n unregisterResponsesBridge,\n} from \"~/services/copilot/responses-bridge-registry\"\n\nconst RESPONSES_BRIDGE_ID_HEADER = \"x-responses-bridge-id\"\n\nconst RESPONSES_WEBSOCKET_PATHS = new Set([\"/responses\", \"/v1/responses\"])\nconst SSE_RECORD_SEPARATOR = /\\r?\\n\\r?\\n/u\nconst FORWARDED_HEADER_NAMES = [\n \"authorization\",\n \"x-api-key\",\n \"user-agent\",\n \"x-session-id\",\n \"x-session-affinity\",\n \"x-parent-session-id\",\n \"x-trace-id\",\n]\n\ntype ServerWebSocket = Bun.ServerWebSocket<ResponsesWebSocketData>\ntype AppFetch = (request: Request) => Response | Promise<Response>\n\nexport type ResponsesWebSocketData = {\n // Codex keeps a single persistent websocket per session and pipelines turns\n // over it: a turn's `response.create` can arrive before the previous turn's\n // SSE relay has finished tearing down. Bun does not serialize async `message`\n // callbacks, so we chain work on this promise to process frames strictly in\n // arrival order instead of rejecting concurrent frames.\n queue: Promise<void>\n // In-flight upstream requests, one AbortController per active `response.create`\n // frame, so a client disconnect can abort all of them.\n controllers: Set<AbortController>\n headers: Array<[string, string]>\n url: string\n // Stable id minted per bridge connection. It is forwarded to the upstream\n // Responses pool (via `x-responses-bridge-id`) so the pool can close this exact\n // socket when its upstream connection is reaped, forcing Codex to rebuild a\n // fresh full-context session instead of stalling on a stale\n // `previous_response_id`.\n bridgeId: string\n}\n\ntype ResponsesWebSocketFrame = {\n type?: unknown\n [key: string]: unknown\n}\n\nexport function createResponsesWebSocketHandler(\n appFetch: AppFetch,\n): Bun.WebSocketHandler<ResponsesWebSocketData> {\n return {\n data: {} as ResponsesWebSocketData,\n idleTimeout: 0,\n open(ws) {\n // Register a closer so the upstream pool can drop this socket when its\n // GitHub connection is reaped while idle.\n registerResponsesBridge(ws.data.bridgeId, () => {\n try {\n ws.close()\n } catch (error) {\n consola.warn(\"Failed to close Responses websocket bridge:\", error)\n }\n })\n },\n async message(ws, message) {\n await handleResponsesWebSocketMessage(ws, message, appFetch)\n },\n close(ws) {\n unregisterResponsesBridge(ws.data.bridgeId)\n // Abort every in-flight upstream Responses request so we stop consuming\n // tokens/quota and worker time once the client disconnects.\n for (const controller of ws.data.controllers) {\n controller.abort()\n }\n ws.data.controllers.clear()\n },\n }\n}\n\nexport function handleResponsesWebSocketUpgrade(\n req: Request,\n server: Bun.Server<ResponsesWebSocketData>,\n): Response | null | undefined {\n if (!isResponsesWebSocketUpgrade(req)) {\n return null\n }\n\n if (!isAuthorizedHeaders(req.headers)) {\n return createUnauthorizedRawResponse()\n }\n\n const upgraded = server.upgrade(req, {\n data: buildResponsesWebSocketData(req),\n })\n\n if (upgraded) {\n return undefined\n }\n\n return new Response(\"WebSocket upgrade failed\", { status: 400 })\n}\n\nexport function isResponsesWebSocketUpgrade(req: Request): boolean {\n if (req.method !== \"GET\") {\n return false\n }\n\n const pathname = new URL(req.url).pathname\n if (!RESPONSES_WEBSOCKET_PATHS.has(pathname)) {\n return false\n }\n\n return req.headers.get(\"upgrade\")?.toLowerCase() === \"websocket\"\n}\n\nexport async function handleResponsesWebSocketMessage(\n ws: ServerWebSocket,\n message: string | ArrayBuffer | Uint8Array,\n appFetch: AppFetch,\n): Promise<void> {\n let frame: ResponsesWebSocketFrame\n try {\n frame = JSON.parse(\n normalizeWebSocketMessage(message),\n ) as ResponsesWebSocketFrame\n } catch {\n sendResponsesWebSocketError(ws, \"Invalid websocket JSON frame\", 400)\n return\n }\n\n if (frame.type === \"response.processed\") {\n return\n }\n\n if (frame.type !== \"response.create\") {\n sendResponsesWebSocketError(ws, \"Unsupported websocket frame type\", 400)\n return\n }\n\n // Chain this frame after any in-flight work so pipelined turns are processed\n // strictly in arrival order. The assignment is synchronous, so even though Bun\n // may dispatch overlapping `message` callbacks, the ordering reflects the order\n // frames were received. We swallow any rejection from the prior link before\n // chaining so a single failed turn (e.g. `ws.send` throwing while the socket is\n // closing) can never poison `ws.data.queue` and block every subsequent turn for\n // the lifetime of the connection.\n const queued = ws.data.queue\n .catch(() => {})\n .then(() => processResponseCreateFrame(ws, frame, appFetch))\n ws.data.queue = queued\n await queued.catch(() => {})\n}\n\nasync function processResponseCreateFrame(\n ws: ServerWebSocket,\n frame: ResponsesWebSocketFrame,\n appFetch: AppFetch,\n): Promise<void> {\n if (frame.generate === false) {\n sendResponsesWebSocketWarmupCompleted(ws)\n return\n }\n\n const controller = new AbortController()\n ws.data.controllers.add(controller)\n try {\n await forwardResponseCreateFrame(ws, frame, appFetch, controller)\n } catch (error) {\n // A client disconnect aborts in-flight controllers; that is expected\n // teardown, not a failure to report back to the (now gone) client.\n if (controller.signal.aborted) {\n return\n }\n consola.warn(\"Responses websocket bridge failed:\", error)\n sendResponsesWebSocketError(\n ws,\n error instanceof Error ? error.message : String(error),\n 500,\n )\n } finally {\n ws.data.controllers.delete(controller)\n }\n}\n\nexport function parseSseRecords(chunk: string): {\n events: Array<string>\n remainder: string\n} {\n const events: Array<string> = []\n let remainder = chunk\n\n while (true) {\n const match = SSE_RECORD_SEPARATOR.exec(remainder)\n if (!match) {\n break\n }\n\n const record = remainder.slice(0, match.index)\n remainder = remainder.slice(match.index + match[0].length)\n const data = parseSseRecordData(record)\n if (data !== null && data !== \"[DONE]\") {\n events.push(data)\n }\n }\n\n return { events, remainder }\n}\n\nfunction buildResponsesWebSocketData(req: Request): ResponsesWebSocketData {\n return {\n queue: Promise.resolve(),\n controllers: new Set(),\n headers: collectForwardedHeaders(req.headers),\n url: req.url,\n bridgeId: randomUUID(),\n }\n}\n\nfunction collectForwardedHeaders(headers: Headers): Array<[string, string]> {\n const forwarded: Array<[string, string]> = []\n for (const name of FORWARDED_HEADER_NAMES) {\n const value = headers.get(name)\n if (value) {\n forwarded.push([name, value])\n }\n }\n return forwarded\n}\n\nasync function forwardResponseCreateFrame(\n ws: ServerWebSocket,\n frame: ResponsesWebSocketFrame,\n appFetch: AppFetch,\n controller: AbortController,\n): Promise<void> {\n const payload = { ...frame, stream: true }\n delete payload.type\n\n const response = await appFetch(\n new Request(buildInternalResponsesUrl(ws.data.url), {\n method: \"POST\",\n headers: buildInternalResponsesHeaders(ws.data.headers, ws.data.bridgeId),\n body: JSON.stringify(payload),\n signal: controller.signal,\n }),\n )\n\n if (!response.ok) {\n sendResponsesWebSocketError(\n ws,\n await readErrorResponseMessage(response),\n response.status,\n )\n return\n }\n\n if (!response.body) {\n sendResponsesWebSocketError(ws, \"Responses stream body is empty\", 500)\n return\n }\n\n await streamSseResponseToWebSocket(ws, response.body, controller)\n}\n\nfunction buildInternalResponsesUrl(sourceUrl: string): string {\n const url = new URL(sourceUrl)\n url.protocol = \"http:\"\n url.pathname =\n url.pathname.startsWith(\"/v1/\") ? \"/v1/responses\" : \"/responses\"\n url.search = \"\"\n url.hash = \"\"\n return url.toString()\n}\n\nfunction buildInternalResponsesHeaders(\n forwardedHeaders: Array<[string, string]>,\n bridgeId: string,\n): Headers {\n const headers = new Headers(forwardedHeaders)\n headers.set(\"content-type\", \"application/json\")\n headers.set(RESPONSES_BRIDGE_ID_HEADER, bridgeId)\n return headers\n}\n\nasync function streamSseResponseToWebSocket(\n ws: ServerWebSocket,\n body: ReadableStream<Uint8Array>,\n controller: AbortController,\n): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n\n // Cancel the upstream reader as soon as the connection is aborted (client\n // close or a failed send) so the upstream stream is torn down promptly.\n const onAbort = () => {\n void reader.cancel()\n }\n controller.signal.addEventListener(\"abort\", onAbort)\n\n try {\n while (!controller.signal.aborted) {\n const { done, value } = await reader.read()\n if (done) {\n break\n }\n\n buffer += decoder.decode(value, { stream: true })\n const parsed = parseSseRecords(buffer)\n buffer = parsed.remainder\n if (!relaySseEvents(ws, parsed.events, controller)) {\n return\n }\n }\n\n if (controller.signal.aborted) {\n return\n }\n\n buffer += decoder.decode()\n const parsed = parseSseRecords(`${buffer}\\n\\n`)\n relaySseEvents(ws, parsed.events, controller)\n } finally {\n controller.signal.removeEventListener(\"abort\", onAbort)\n try {\n reader.releaseLock()\n } catch {\n // Reader may already be released after cancellation; ignore.\n }\n }\n}\n\n/**\n * Relay parsed SSE events to the client. Returns `false` if a send failed and\n * the upstream request was aborted, signalling the caller to stop streaming.\n */\nfunction relaySseEvents(\n ws: ServerWebSocket,\n events: Array<string>,\n controller: AbortController,\n): boolean {\n for (const event of events) {\n if (controller.signal.aborted) {\n return false\n }\n try {\n ws.send(event)\n } catch (error) {\n consola.warn(\"Responses websocket send failed:\", error)\n controller.abort()\n return false\n }\n }\n return true\n}\n\nfunction parseSseRecordData(record: string): string | null {\n const lines = record.split(/\\r?\\n/u)\n const dataLines = lines\n .filter((line) => line.startsWith(\"data:\"))\n // Per the SSE spec, strip only a single optional leading space after the\n // colon (not arbitrary whitespace), preserving whitespace-sensitive data.\n .map((line) => (line.startsWith(\"data: \") ? line.slice(6) : line.slice(5)))\n\n if (dataLines.length === 0) {\n return null\n }\n\n return dataLines.join(\"\\n\")\n}\n\nfunction normalizeWebSocketMessage(\n message: string | ArrayBuffer | Uint8Array,\n): string {\n if (typeof message === \"string\") {\n return message\n }\n\n return new TextDecoder().decode(message)\n}\n\nfunction sendResponsesWebSocketError(\n ws: ServerWebSocket,\n message: string,\n status: number,\n): void {\n // Mirror the Responses stream error contract (see ResponseErrorEvent in\n // src/services/copilot/create-responses.ts) so clients always receive the\n // same top-level event shape regardless of where the error originates.\n ws.send(\n JSON.stringify({\n code: status ? String(status) : null,\n message,\n param: null,\n sequence_number: 0,\n type: \"error\",\n }),\n )\n}\n\nfunction sendResponsesWebSocketWarmupCompleted(ws: ServerWebSocket): void {\n ws.send(\n JSON.stringify({\n response: {\n id: \"\",\n },\n sequence_number: 0,\n type: \"response.completed\",\n }),\n )\n}\n\nasync function readErrorResponseMessage(response: Response): Promise<string> {\n const body = await response.text()\n if (!body) {\n return `Responses request failed with status ${response.status}`\n }\n\n try {\n const parsed = JSON.parse(body) as {\n error?: { message?: unknown }\n message?: unknown\n }\n const message =\n typeof parsed.error?.message === \"string\" ? parsed.error.message\n : typeof parsed.message === \"string\" ? parsed.message\n : undefined\n return message ?? body\n } catch {\n return body\n }\n}\n","import { execSync } from \"node:child_process\"\nimport process from \"node:process\"\n\ntype ShellName = \"bash\" | \"zsh\" | \"fish\" | \"powershell\" | \"cmd\" | \"sh\"\ntype EnvVars = Record<string, string | undefined>\n\nfunction getShell(): ShellName {\n const { platform, ppid, env } = process\n\n if (platform === \"win32\") {\n try {\n const command = `wmic process get ParentProcessId,Name | findstr \"${ppid}\"`\n const parentProcess = execSync(command, { stdio: \"pipe\" }).toString()\n\n if (parentProcess.toLowerCase().includes(\"powershell.exe\")) {\n return \"powershell\"\n }\n } catch {\n return \"cmd\"\n }\n\n return \"cmd\"\n } else {\n const shellPath = env.SHELL\n if (shellPath) {\n if (shellPath.endsWith(\"zsh\")) return \"zsh\"\n if (shellPath.endsWith(\"fish\")) return \"fish\"\n if (shellPath.endsWith(\"bash\")) return \"bash\"\n }\n\n return \"sh\"\n }\n}\n\n/**\n * Generates a copy-pasteable script to set multiple environment variables\n * and run a subsequent command.\n * @param {EnvVars} envVars - An object of environment variables to set.\n * @param {string} commandToRun - The command to run after setting the variables.\n * @returns {string} The formatted script string.\n */\nexport function generateEnvScript(\n envVars: EnvVars,\n commandToRun: string = \"\",\n): string {\n const shell = getShell()\n const filteredEnvVars = Object.entries(envVars).filter(\n ([, value]) => value !== undefined,\n ) as Array<[string, string]>\n\n let commandBlock: string\n\n switch (shell) {\n case \"powershell\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `$env:${key} = ${value}`)\n .join(\"; \")\n break\n }\n case \"cmd\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set ${key}=${value}`)\n .join(\" & \")\n break\n }\n case \"fish\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set -gx ${key} ${value}`)\n .join(\"; \")\n break\n }\n default: {\n // bash, zsh, sh\n const assignments = filteredEnvVars\n .map(([key, value]) => `${key}=${value}`)\n .join(\" \")\n commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : \"\"\n break\n }\n }\n\n if (commandBlock && commandToRun) {\n const separator = shell === \"cmd\" ? \" & \" : \" && \"\n return `${commandBlock}${separator}${commandToRun}`\n }\n\n return commandBlock || commandToRun\n}\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport clipboard from \"clipboardy\"\nimport consola from \"consola\"\n\nimport {\n registerQuotaRefreshSchedulerShutdownCleanup,\n startQuotaRefreshSchedulerFromConfig,\n stopQuotaRefreshScheduler,\n} from \"~/lib/quota-refresh-scheduler-runtime\"\nimport { isMcpHttpEnabledFromEnv } from \"~/mcp-http-config\"\nimport {\n createResponsesWebSocketHandler,\n handleResponsesWebSocketUpgrade,\n} from \"~/routes/responses/websocket\"\n\nimport { accountsManager } from \"./lib/accounts-manager\"\nimport { addAccountToRegistry, saveAccountToken } from \"./lib/accounts-registry\"\nimport {\n getModelRefreshIntervalMs,\n isAccountAffinityEnabled,\n mergeConfigWithDefaults,\n} from \"./lib/config\"\nimport { initOpencodeVersion } from \"./lib/opencode\"\nimport { ensurePaths } from \"./lib/paths\"\nimport { initProxyFromEnv } from \"./lib/proxy\"\nimport { applySharedSessionAffinityRetention } from \"./lib/session-affinity-store\"\nimport { generateEnvScript } from \"./lib/shell\"\nimport { state } from \"./lib/state\"\nimport { parseAccountType, type AccountType } from \"./lib/types/account\"\nimport {\n cacheMacMachineId,\n cacheVSCodeVersion,\n cacheVsCodeSessionId,\n cacheVsCodeDeviceId,\n} from \"./lib/utils\"\nimport { getDeviceCode } from \"./services/github/get-device-code\"\nimport { getGitHubUser } from \"./services/github/get-user\"\nimport { pollAccessToken } from \"./services/github/poll-access-token\"\n\ninterface RunServerOptions {\n port: number\n verbose: boolean\n accountType: AccountType\n manual: boolean\n rateLimit?: number\n rateLimitWait: boolean\n githubToken?: string\n claudeCode: boolean\n showToken: boolean\n proxyEnv: boolean\n skipAuth: boolean\n enableMcpHttp: boolean\n}\n\n/**\n * Run the interactive authentication flow to add a new account.\n * Called automatically when no accounts are found.\n */\nasync function runAuthFlow(accountType: AccountType): Promise<void> {\n consola.warn(\"No accounts found. Starting authentication flow...\")\n\n // Start device code flow\n const deviceResponse = await getDeviceCode()\n consola.info(\n `Please enter the code \"${deviceResponse.user_code}\" at ${deviceResponse.verification_uri}`,\n )\n\n // Poll for access token\n const token = await pollAccessToken(deviceResponse)\n\n if (state.showToken) {\n consola.info(\"GitHub token:\", token)\n }\n\n // Get user info to determine account ID\n const user = await getGitHubUser({\n githubToken: token,\n accountType,\n })\n const accountId = user.login\n\n // Save token and add to registry\n await saveAccountToken(accountId, token)\n await addAccountToRegistry({\n id: accountId,\n accountType,\n addedAt: Date.now(),\n })\n\n consola.success(`Account \"${accountId}\" added successfully!`)\n}\n\nfunction logClaudeCodeTip(): void {\n consola.log(\n \"\\n💡 Tip: The --claude-code flag simply generates a clipboard command for launching Claude Code. \\n\"\n + \"All models remain fully accessible without this flag, just configure the model ID directly in your settings.json file.\",\n )\n}\n\ntype AvailableModels = NonNullable<\n ReturnType<typeof accountsManager.getFirstAccountModels>\n>\n\nasync function setupClaudeCode(\n models: AvailableModels,\n serverUrl: string,\n): Promise<void> {\n const selectedModel = await consola.prompt(\n \"Select a model to use with Claude Code\",\n {\n type: \"select\",\n options: models.data.map((model) => model.id),\n },\n )\n\n const selectedSmallModel = await consola.prompt(\n \"Select a small model to use with Claude Code\",\n {\n type: \"select\",\n options: models.data.map((model) => model.id),\n },\n )\n\n const command = generateEnvScript(\n {\n ANTHROPIC_BASE_URL: serverUrl,\n ANTHROPIC_AUTH_TOKEN: \"dummy\",\n ANTHROPIC_MODEL: selectedModel,\n ANTHROPIC_DEFAULT_SONNET_MODEL: selectedModel,\n ANTHROPIC_DEFAULT_HAIKU_MODEL: selectedSmallModel,\n DISABLE_NON_ESSENTIAL_MODEL_CALLS: \"1\",\n CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: \"1\",\n CLAUDE_CODE_ATTRIBUTION_HEADER: \"0\",\n CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: \"false\",\n CLAUDE_CODE_DISABLE_TERMINAL_TITLE: \"true\",\n CLAUDE_CODE_ENABLE_AWAY_SUMMARY: \"0\",\n CLAUDE_PLUGIN_ENABLE_QUESTION_RULES: \"true\",\n },\n \"claude\",\n )\n\n try {\n clipboard.writeSync(command)\n consola.success(\"Copied Claude Code command to clipboard!\")\n } catch {\n consola.warn(\n \"Failed to copy to clipboard. Here is the Claude Code command:\",\n )\n consola.log(command)\n }\n}\n\nexport async function runServer(options: RunServerOptions): Promise<void> {\n // Fail fast before any config merge, account init, interactive auth, or quota\n // scheduler work: the server relies on Bun.serve for the Responses WebSocket\n // transport, so running under Node would otherwise crash with\n // \"ReferenceError: Bun is not defined\" only after all that setup.\n if (typeof Bun === \"undefined\") {\n consola.error(\n \"The Responses WebSocket transport requires the Bun runtime. Start the proxy with 'bun' or 'bunx --bun' instead of Node.\",\n )\n process.exit(1)\n }\n\n // Work around unjs/consola#357 until a release includes PR #359.\n consola.options.throttle = 0\n\n // Ensure config is merged with defaults at startup\n mergeConfigWithDefaults()\n accountsManager.setAccountAffinityEnabled(isAccountAffinityEnabled())\n accountsManager.setModelsRefreshIntervalMs(getModelRefreshIntervalMs())\n\n await initOpencodeVersion()\n\n if (options.proxyEnv) {\n initProxyFromEnv()\n }\n\n const enableMcpHttp = options.enableMcpHttp || isMcpHttpEnabledFromEnv()\n if (enableMcpHttp) {\n consola.warn(\n \"Main server MCP endpoint is enabled and unauthenticated at /mcp.\",\n )\n }\n\n state.verbose = options.verbose\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.accountType = options.accountType\n if (options.accountType !== \"individual\") {\n consola.info(`Using ${options.accountType} plan GitHub account`)\n }\n\n state.manualApprove = options.manual\n state.rateLimitSeconds = options.rateLimit\n state.rateLimitWait = options.rateLimitWait\n state.showToken = options.showToken\n\n await ensurePaths()\n applySharedSessionAffinityRetention()\n await cacheVSCodeVersion()\n cacheMacMachineId()\n cacheVsCodeSessionId()\n await cacheVsCodeDeviceId()\n\n // Initialize accounts manager with VS Code version\n await accountsManager.initialize(state.vsCodeVersion)\n\n // If --github-token is provided, set it as a temporary (high priority) account\n if (options.githubToken) {\n await accountsManager.setTemporaryAccount(\n options.githubToken,\n options.accountType,\n )\n consola.info(\"Using provided GitHub token as temporary account\")\n }\n\n // Check if we have any accounts, if not, start the auth flow\n if (!accountsManager.hasAccounts()) {\n if (options.skipAuth) {\n consola.warn(\n \"No accounts found. Skipping auth flow (--skip-auth). Add accounts via the Admin UI.\",\n )\n } else {\n try {\n await runAuthFlow(options.accountType)\n\n // Re-initialize accounts manager with the new account\n accountsManager.shutdown()\n stopQuotaRefreshScheduler()\n await accountsManager.initialize(state.vsCodeVersion)\n accountsManager.setModelsRefreshIntervalMs(getModelRefreshIntervalMs())\n } catch (error) {\n consola.error(\"Failed to add account:\", error)\n process.exit(1)\n }\n }\n }\n\n startQuotaRefreshSchedulerFromConfig()\n registerQuotaRefreshSchedulerShutdownCleanup()\n\n // Get models from the first available account\n const models = accountsManager.getFirstAccountModels()\n\n consola.info(\n `Available models: \\n${models?.data.map((model) => `- ${model.id}`).join(\"\\n\") ?? \"(no models loaded)\"}`,\n )\n\n const serverUrl = `http://localhost:${options.port}`\n\n if (options.claudeCode) {\n logClaudeCodeTip()\n if (!models?.data.length) {\n consola.error(\n \"Claude Code requires available models. Add an account via the Admin UI or remove --claude-code.\",\n )\n process.exit(1)\n }\n await setupClaudeCode(models, serverUrl)\n }\n\n consola.box(`🌐 Admin UI: ${serverUrl}/admin`)\n\n const { createServer } = await import(\"./server\")\n const server = createServer({ enableMcpHttp })\n const responsesWebSocketHandler = createResponsesWebSocketHandler(\n server.fetch,\n )\n\n Bun.serve({\n port: options.port,\n idleTimeout: 0,\n fetch: (request, bunServer) => {\n const websocketResponse = handleResponsesWebSocketUpgrade(\n request,\n bunServer,\n )\n if (websocketResponse !== null) {\n return websocketResponse\n }\n\n return server.fetch(request)\n },\n websocket: responsesWebSocketHandler,\n })\n}\n\nexport const start = defineCommand({\n meta: {\n name: \"start\",\n description: \"Start the Copilot API server\",\n },\n args: {\n port: {\n alias: \"p\",\n type: \"string\",\n default: \"4141\",\n description: \"Port to listen on\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"account-type\": {\n alias: \"a\",\n type: \"string\",\n default: \"individual\",\n description: \"Account type to use (individual, business, enterprise)\",\n },\n manual: {\n type: \"boolean\",\n default: false,\n description: \"Enable manual request approval\",\n },\n \"rate-limit\": {\n alias: \"r\",\n type: \"string\",\n description: \"Rate limit in seconds between requests\",\n },\n wait: {\n alias: \"w\",\n type: \"boolean\",\n default: false,\n description:\n \"Wait instead of error when rate limit is hit. Has no effect if rate limit is not set\",\n },\n \"github-token\": {\n alias: \"g\",\n type: \"string\",\n description:\n \"Provide GitHub token directly (must be generated using the `auth` subcommand)\",\n },\n \"claude-code\": {\n alias: \"c\",\n type: \"boolean\",\n default: false,\n description:\n \"Generate a command to launch Claude Code with Copilot API config\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub and Copilot tokens on fetch and refresh\",\n },\n \"proxy-env\": {\n type: \"boolean\",\n default: false,\n description: \"Initialize proxy from environment variables\",\n },\n \"skip-auth\": {\n type: \"boolean\",\n default: false,\n description:\n \"Skip the initial CLI auth flow when no accounts are found. Use this to add accounts via the Admin UI instead.\",\n },\n \"enable-mcp-http\": {\n type: \"boolean\",\n default: false,\n description:\n \"Expose the unauthenticated MCP Streamable HTTP endpoint at /mcp.\",\n },\n },\n run({ args }) {\n const rateLimitRaw = args[\"rate-limit\"]\n const rateLimit =\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n rateLimitRaw === undefined ? undefined : Number.parseInt(rateLimitRaw, 10)\n\n let accountType: AccountType\n try {\n accountType = parseAccountType(args[\"account-type\"])\n } catch (error) {\n consola.error(error instanceof Error ? error.message : String(error))\n process.exit(1)\n }\n\n return runServer({\n port: Number.parseInt(args.port, 10),\n verbose: args.verbose,\n accountType,\n manual: args.manual,\n rateLimit,\n rateLimitWait: args.wait,\n githubToken: args[\"github-token\"],\n claudeCode: args[\"claude-code\"],\n showToken: args[\"show-token\"],\n proxyEnv: args[\"proxy-env\"],\n skipAuth: args[\"skip-auth\"],\n enableMcpHttp: args[\"enable-mcp-http\"],\n })\n },\n})\n"],"mappings":"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"}
|