xiaodcs-copilot-api-edge 2.3.9-edge.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/LICENSE +21 -0
- package/README.md +888 -0
- package/README.zh-CN.md +940 -0
- package/dist/auth-BiVetBYt.js +446 -0
- package/dist/auth-Bu4MXadr.js +2 -0
- package/dist/config-SytZjLq8.js +544 -0
- package/dist/debug-BFadhEB4.js +90 -0
- package/dist/electron-fetch-BRX-ug5E.js +20 -0
- package/dist/fast-path-BoMnZCVC.js +9 -0
- package/dist/main.js +49 -0
- package/dist/mcp-fpSlKZxK.js +14 -0
- package/dist/mcp-server-BeNu_Edl.js +25 -0
- package/dist/mcp-server-DQ4r-fAy.js +2 -0
- package/dist/models-YMUf33c-.js +88 -0
- package/dist/server-CFQmvoAJ.js +11710 -0
- package/dist/start-FFVCi8su.js +528 -0
- package/dist/tls-Aq1Dd8E2.js +14 -0
- package/dist/token-D9svRIYW.js +1950 -0
- package/dist/tool-search-Ds1vbmGG.js +114 -0
- package/package.json +96 -0
- package/pages/index.html +2257 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import { F as ensurePaths, M as mergeConfigWithDefaults, i as listEnabledProviders } from "./config-SytZjLq8.js";
|
|
2
|
+
import { K as state, S as initOpencodeVersion, _ as copilotModelsHeaders, h as copilotBaseUrl, i as setupCopilotToken, j as getMissingApiKeysMessage, m as readGitHubToken, q as HTTPError, t as logUser } from "./token-D9svRIYW.js";
|
|
3
|
+
import { i as runProviderSetup } from "./auth-BiVetBYt.js";
|
|
4
|
+
import { n as getLatestModelForFamily } from "./models-YMUf33c-.js";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
import consola from "consola";
|
|
7
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { networkInterfaces } from "node:os";
|
|
10
|
+
import { getProxyForUrl } from "proxy-from-env";
|
|
11
|
+
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
|
12
|
+
import { execSync } from "node:child_process";
|
|
13
|
+
import clipboard from "clipboardy";
|
|
14
|
+
import { serve } from "srvx";
|
|
15
|
+
import invariant from "tiny-invariant";
|
|
16
|
+
import process$1 from "node:process";
|
|
17
|
+
//#region src/lib/proxy.ts
|
|
18
|
+
let proxyEnvDispatcher;
|
|
19
|
+
function initProxyFromEnv() {
|
|
20
|
+
try {
|
|
21
|
+
const direct = new Agent();
|
|
22
|
+
const proxies = /* @__PURE__ */ new Map();
|
|
23
|
+
proxyEnvDispatcher = {
|
|
24
|
+
dispatch(options, handler) {
|
|
25
|
+
try {
|
|
26
|
+
const origin = typeof options.origin === "string" ? new URL(options.origin) : options.origin;
|
|
27
|
+
const raw = getProxyForUrl(origin.toString());
|
|
28
|
+
const proxyUrl = raw && raw.length > 0 ? raw : void 0;
|
|
29
|
+
if (!proxyUrl) {
|
|
30
|
+
consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
|
|
31
|
+
return direct.dispatch(options, handler);
|
|
32
|
+
}
|
|
33
|
+
let agent = proxies.get(proxyUrl);
|
|
34
|
+
if (!agent) {
|
|
35
|
+
agent = new ProxyAgent(proxyUrl);
|
|
36
|
+
proxies.set(proxyUrl, agent);
|
|
37
|
+
}
|
|
38
|
+
let label = proxyUrl;
|
|
39
|
+
try {
|
|
40
|
+
const u = new URL(proxyUrl);
|
|
41
|
+
label = `${u.protocol}//${u.host}`;
|
|
42
|
+
} catch {}
|
|
43
|
+
consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`);
|
|
44
|
+
return agent.dispatch(options, handler);
|
|
45
|
+
} catch {
|
|
46
|
+
return direct.dispatch(options, handler);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
close() {
|
|
50
|
+
return direct.close();
|
|
51
|
+
},
|
|
52
|
+
destroy() {
|
|
53
|
+
return direct.destroy();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
if (typeof Bun !== "undefined") {
|
|
57
|
+
consola.debug("WebSocket proxy configured from environment (per-URL)");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
setGlobalDispatcher(proxyEnvDispatcher);
|
|
61
|
+
consola.debug("HTTP proxy configured from environment (per-URL)");
|
|
62
|
+
} catch (err) {
|
|
63
|
+
consola.debug("Proxy setup skipped:", err);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/lib/shell.ts
|
|
68
|
+
function getShell() {
|
|
69
|
+
const { platform, ppid, env } = process$1;
|
|
70
|
+
if (platform === "win32") {
|
|
71
|
+
try {
|
|
72
|
+
if (execSync(`wmic process get ParentProcessId,Name | findstr "${ppid}"`, { stdio: "pipe" }).toString().toLowerCase().includes("powershell.exe")) return "powershell";
|
|
73
|
+
} catch {
|
|
74
|
+
return "cmd";
|
|
75
|
+
}
|
|
76
|
+
return "cmd";
|
|
77
|
+
} else {
|
|
78
|
+
const shellPath = env.SHELL;
|
|
79
|
+
if (shellPath) {
|
|
80
|
+
if (shellPath.endsWith("zsh")) return "zsh";
|
|
81
|
+
if (shellPath.endsWith("fish")) return "fish";
|
|
82
|
+
if (shellPath.endsWith("bash")) return "bash";
|
|
83
|
+
}
|
|
84
|
+
return "sh";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generates a copy-pasteable script to set multiple environment variables
|
|
89
|
+
* and run a subsequent command.
|
|
90
|
+
* @param {EnvVars} envVars - An object of environment variables to set.
|
|
91
|
+
* @param {string} commandToRun - The command to run after setting the variables.
|
|
92
|
+
* @returns {string} The formatted script string.
|
|
93
|
+
*/
|
|
94
|
+
function generateEnvScript(envVars, commandToRun = "") {
|
|
95
|
+
const shell = getShell();
|
|
96
|
+
const filteredEnvVars = Object.entries(envVars).filter(([, value]) => value !== void 0);
|
|
97
|
+
let commandBlock;
|
|
98
|
+
switch (shell) {
|
|
99
|
+
case "powershell":
|
|
100
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `$env:${key} = ${value}`).join("; ");
|
|
101
|
+
break;
|
|
102
|
+
case "cmd":
|
|
103
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `set ${key}=${value}`).join(" & ");
|
|
104
|
+
break;
|
|
105
|
+
case "fish":
|
|
106
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `set -gx ${key} ${value}`).join("; ");
|
|
107
|
+
break;
|
|
108
|
+
default: {
|
|
109
|
+
const assignments = filteredEnvVars.map(([key, value]) => `${key}=${value}`).join(" ");
|
|
110
|
+
commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : "";
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (commandBlock && commandToRun) return `${commandBlock}${shell === "cmd" ? " & " : " && "}${commandToRun}`;
|
|
115
|
+
return commandBlock || commandToRun;
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/services/copilot/get-models.ts
|
|
119
|
+
const getModels = async () => {
|
|
120
|
+
consola.info(`Fetching models from ${copilotBaseUrl(state)}/models`);
|
|
121
|
+
const response = await fetch(`${copilotBaseUrl(state)}/models`, { headers: copilotModelsHeaders(state) });
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
const errorText = await response.clone().text();
|
|
124
|
+
consola.error("Failed to get models response body", errorText);
|
|
125
|
+
throw new HTTPError("Failed to get models", response);
|
|
126
|
+
}
|
|
127
|
+
return await response.json();
|
|
128
|
+
};
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/services/copilot/models-cache.ts
|
|
131
|
+
const MODELS_REFRESH_BASE_MS = 1800 * 1e3;
|
|
132
|
+
let modelsRefreshTimer = null;
|
|
133
|
+
const stopModelsRefreshLoop = () => {
|
|
134
|
+
if (modelsRefreshTimer) {
|
|
135
|
+
clearTimeout(modelsRefreshTimer);
|
|
136
|
+
modelsRefreshTimer = null;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const refreshModels = async (fetcher) => {
|
|
140
|
+
const prevIds = new Set(state.models?.data.map((m) => m.id) ?? []);
|
|
141
|
+
const models = await fetcher();
|
|
142
|
+
state.models = {
|
|
143
|
+
...models,
|
|
144
|
+
data: models.data.filter((model) => model.policy?.state !== "disabled" && (model.model_picker_enabled || model.capabilities.type === "embeddings"))
|
|
145
|
+
};
|
|
146
|
+
const nextIds = state.models.data.map((m) => m.id);
|
|
147
|
+
const added = nextIds.filter((id) => !prevIds.has(id));
|
|
148
|
+
if (added.length > 0) consola.info(`Models refresh: ${added.length} new`);
|
|
149
|
+
else consola.debug(`Models refresh: no changes (${nextIds.length} total)`);
|
|
150
|
+
};
|
|
151
|
+
const scheduleModelsRefresh = (fetcher, intervalMs) => {
|
|
152
|
+
const delay = intervalMs + Math.floor(Math.random() * (intervalMs / 6));
|
|
153
|
+
consola.debug(`Scheduling next models refresh in ${Math.round(delay / 1e3)} seconds`);
|
|
154
|
+
stopModelsRefreshLoop();
|
|
155
|
+
modelsRefreshTimer = setTimeout(async () => {
|
|
156
|
+
try {
|
|
157
|
+
await refreshModels(fetcher);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
consola.warn("Failed to refresh models, keeping previous cache.", error);
|
|
160
|
+
} finally {
|
|
161
|
+
scheduleModelsRefresh(fetcher, intervalMs);
|
|
162
|
+
}
|
|
163
|
+
}, delay);
|
|
164
|
+
};
|
|
165
|
+
async function cacheModels(fetcher = getModels, intervalMs = MODELS_REFRESH_BASE_MS) {
|
|
166
|
+
await refreshModels(fetcher);
|
|
167
|
+
scheduleModelsRefresh(fetcher, intervalMs);
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/lib/deviceid.ts
|
|
171
|
+
const WINDOWS_DEVICE_ID_KEY = String.raw`\SOFTWARE\Microsoft\DeveloperTools`;
|
|
172
|
+
const WINDOWS_DEVICE_ID_NAME = "deviceid";
|
|
173
|
+
const windows64Architectures = new Set([
|
|
174
|
+
"AMD64",
|
|
175
|
+
"ARM64",
|
|
176
|
+
"IA64"
|
|
177
|
+
]);
|
|
178
|
+
const getPosixHomeDir = () => {
|
|
179
|
+
if (!process.env.HOME) throw new Error("Home directory not found");
|
|
180
|
+
return process.env.HOME;
|
|
181
|
+
};
|
|
182
|
+
const getDeviceIdFilePath = () => {
|
|
183
|
+
let folder;
|
|
184
|
+
switch (process.platform) {
|
|
185
|
+
case "darwin":
|
|
186
|
+
folder = path.posix.join(getPosixHomeDir(), "Library", "Application Support");
|
|
187
|
+
break;
|
|
188
|
+
case "linux":
|
|
189
|
+
folder = process.env.XDG_CACHE_HOME ?? path.posix.join(getPosixHomeDir(), ".cache");
|
|
190
|
+
break;
|
|
191
|
+
default: throw new Error("Unsupported platform");
|
|
192
|
+
}
|
|
193
|
+
return path.posix.join(folder, "Microsoft", "DeveloperTools", "deviceid");
|
|
194
|
+
};
|
|
195
|
+
const isMissingFileError = (error) => {
|
|
196
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
197
|
+
};
|
|
198
|
+
const readStoredDeviceIdFile = async (filePath) => {
|
|
199
|
+
const { readFile } = await import("node:fs/promises");
|
|
200
|
+
try {
|
|
201
|
+
return await readFile(filePath, "utf8");
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (isMissingFileError(error)) return;
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const writeStoredDeviceIdFile = async (filePath, deviceId) => {
|
|
208
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
209
|
+
await mkdir(path.posix.dirname(filePath), { recursive: true });
|
|
210
|
+
await writeFile(filePath, deviceId, "utf8");
|
|
211
|
+
};
|
|
212
|
+
const getWindowsRegistryArch = () => {
|
|
213
|
+
const architecture = (process.env.PROCESSOR_ARCHITEW6432 ?? process.env.PROCESSOR_ARCHITECTURE)?.toUpperCase();
|
|
214
|
+
return architecture && windows64Architectures.has(architecture) ? "x64" : void 0;
|
|
215
|
+
};
|
|
216
|
+
const loadWinreg = async () => {
|
|
217
|
+
const module = await import("winreg");
|
|
218
|
+
return "default" in module ? module.default : module;
|
|
219
|
+
};
|
|
220
|
+
const isMissingRegistryError = (error) => {
|
|
221
|
+
if (!error) return false;
|
|
222
|
+
const errorCode = Number(error.code);
|
|
223
|
+
return Number.isFinite(errorCode) && errorCode === 1;
|
|
224
|
+
};
|
|
225
|
+
const createWindowsRegistry = async () => {
|
|
226
|
+
const Winreg = await loadWinreg();
|
|
227
|
+
return {
|
|
228
|
+
registry: new Winreg({
|
|
229
|
+
hive: Winreg.HKCU,
|
|
230
|
+
key: WINDOWS_DEVICE_ID_KEY,
|
|
231
|
+
arch: getWindowsRegistryArch()
|
|
232
|
+
}),
|
|
233
|
+
regSz: Winreg.REG_SZ
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
const readRegistryString = async (registry, name) => {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
registry.get(name, (error, item) => {
|
|
239
|
+
if (isMissingRegistryError(error)) {
|
|
240
|
+
resolve(void 0);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (error) {
|
|
244
|
+
reject(error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown registry error"));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
resolve(item?.value);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
const writeRegistryString = async ({ registry, regSz, name, value }) => {
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
registry.set(name, regSz, value, (error) => {
|
|
254
|
+
if (error) {
|
|
255
|
+
reject(error instanceof Error ? error : /* @__PURE__ */ new Error("Unknown registry error"));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
resolve();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
const getStoredVSCodeDeviceId = async () => {
|
|
263
|
+
switch (process.platform) {
|
|
264
|
+
case "win32": {
|
|
265
|
+
const { registry } = await createWindowsRegistry();
|
|
266
|
+
return readRegistryString(registry, WINDOWS_DEVICE_ID_NAME);
|
|
267
|
+
}
|
|
268
|
+
case "darwin":
|
|
269
|
+
case "linux": return readStoredDeviceIdFile(getDeviceIdFilePath());
|
|
270
|
+
default: throw new Error("Unsupported platform");
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const setStoredVSCodeDeviceId = async (deviceId) => {
|
|
274
|
+
switch (process.platform) {
|
|
275
|
+
case "win32": {
|
|
276
|
+
const { registry, regSz } = await createWindowsRegistry();
|
|
277
|
+
await writeRegistryString({
|
|
278
|
+
registry,
|
|
279
|
+
regSz,
|
|
280
|
+
name: WINDOWS_DEVICE_ID_NAME,
|
|
281
|
+
value: deviceId
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
case "darwin":
|
|
286
|
+
case "linux":
|
|
287
|
+
await writeStoredDeviceIdFile(getDeviceIdFilePath(), deviceId);
|
|
288
|
+
return;
|
|
289
|
+
default: throw new Error("Unsupported platform");
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const createVSCodeDeviceId = () => randomUUID().toLowerCase();
|
|
293
|
+
async function getVSCodeDeviceId() {
|
|
294
|
+
let deviceId;
|
|
295
|
+
try {
|
|
296
|
+
deviceId = await getStoredVSCodeDeviceId();
|
|
297
|
+
} catch (error) {
|
|
298
|
+
consola.debug("Failed to read VSCode device id", error);
|
|
299
|
+
}
|
|
300
|
+
if (deviceId) return deviceId;
|
|
301
|
+
const newDeviceId = createVSCodeDeviceId();
|
|
302
|
+
try {
|
|
303
|
+
await setStoredVSCodeDeviceId(newDeviceId);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
consola.warn("Failed to persist VSCode device id, using ephemeral id", error);
|
|
306
|
+
}
|
|
307
|
+
return newDeviceId;
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/services/get-vscode-version.ts
|
|
311
|
+
const FALLBACK = "1.130.0";
|
|
312
|
+
async function getVSCodeVersion() {
|
|
313
|
+
await Promise.resolve();
|
|
314
|
+
return FALLBACK;
|
|
315
|
+
}
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/services/vscode-env.ts
|
|
318
|
+
const cacheVSCodeVersion = async () => {
|
|
319
|
+
const response = await getVSCodeVersion();
|
|
320
|
+
state.vsCodeVersion = response;
|
|
321
|
+
consola.info(`Using VSCode version: ${response}`);
|
|
322
|
+
};
|
|
323
|
+
const invalidMacAddresses = new Set([
|
|
324
|
+
"00:00:00:00:00:00",
|
|
325
|
+
"ff:ff:ff:ff:ff:ff",
|
|
326
|
+
"ac:de:48:00:11:22"
|
|
327
|
+
]);
|
|
328
|
+
function validateMacAddress(candidate) {
|
|
329
|
+
const tempCandidate = candidate.replaceAll("-", ":").toLowerCase();
|
|
330
|
+
return !invalidMacAddresses.has(tempCandidate);
|
|
331
|
+
}
|
|
332
|
+
function getMac() {
|
|
333
|
+
const ifaces = networkInterfaces();
|
|
334
|
+
for (const name in ifaces) {
|
|
335
|
+
const networkInterface = ifaces[name];
|
|
336
|
+
if (networkInterface) {
|
|
337
|
+
for (const { mac } of networkInterface) if (validateMacAddress(mac)) return mac;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
const cacheMacMachineId = () => {
|
|
343
|
+
const macAddress = getMac() ?? randomUUID();
|
|
344
|
+
state.macMachineId = createHash("sha256").update(macAddress, "utf8").digest("hex");
|
|
345
|
+
consola.debug(`Using machine ID: ${state.macMachineId}`);
|
|
346
|
+
};
|
|
347
|
+
const cacheVsCodeDeviceId = async () => {
|
|
348
|
+
state.vsCodeDeviceId = await getVSCodeDeviceId();
|
|
349
|
+
consola.debug(`Using VSCode device ID: ${state.vsCodeDeviceId}`);
|
|
350
|
+
};
|
|
351
|
+
const SESSION_REFRESH_BASE_MS = 3600 * 1e3;
|
|
352
|
+
const SESSION_REFRESH_JITTER_MS = 1200 * 1e3;
|
|
353
|
+
let vsCodeSessionRefreshTimer = null;
|
|
354
|
+
const generateSessionId = () => {
|
|
355
|
+
state.vsCodeSessionId = randomUUID() + Date.now().toString();
|
|
356
|
+
consola.debug(`Generated VSCode session ID: ${state.vsCodeSessionId}`);
|
|
357
|
+
};
|
|
358
|
+
const stopVsCodeSessionRefreshLoop = () => {
|
|
359
|
+
if (vsCodeSessionRefreshTimer) {
|
|
360
|
+
clearTimeout(vsCodeSessionRefreshTimer);
|
|
361
|
+
vsCodeSessionRefreshTimer = null;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
const scheduleSessionIdRefresh = () => {
|
|
365
|
+
const delay = SESSION_REFRESH_BASE_MS + Math.floor(Math.random() * SESSION_REFRESH_JITTER_MS);
|
|
366
|
+
consola.debug(`Scheduling next VSCode session ID refresh in ${Math.round(delay / 1e3)} seconds`);
|
|
367
|
+
stopVsCodeSessionRefreshLoop();
|
|
368
|
+
vsCodeSessionRefreshTimer = setTimeout(() => {
|
|
369
|
+
try {
|
|
370
|
+
generateSessionId();
|
|
371
|
+
} catch (error) {
|
|
372
|
+
consola.error("Failed to refresh session ID, rescheduling...", error);
|
|
373
|
+
} finally {
|
|
374
|
+
scheduleSessionIdRefresh();
|
|
375
|
+
}
|
|
376
|
+
}, delay);
|
|
377
|
+
};
|
|
378
|
+
const cacheVsCodeSessionId = () => {
|
|
379
|
+
stopVsCodeSessionRefreshLoop();
|
|
380
|
+
generateSessionId();
|
|
381
|
+
scheduleSessionIdRefresh();
|
|
382
|
+
};
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/start.ts
|
|
385
|
+
async function setupCopilotMode(githubToken, fromCli, serverUrl, claudeCode) {
|
|
386
|
+
state.githubToken = githubToken;
|
|
387
|
+
consola.info(fromCli ? "Using provided GitHub token" : "Using GitHub token from local file");
|
|
388
|
+
await logUser();
|
|
389
|
+
await cacheVSCodeVersion();
|
|
390
|
+
cacheMacMachineId();
|
|
391
|
+
cacheVsCodeSessionId();
|
|
392
|
+
await cacheVsCodeDeviceId();
|
|
393
|
+
await setupCopilotToken();
|
|
394
|
+
await cacheModels();
|
|
395
|
+
consola.info(`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
396
|
+
if (claudeCode) runClaudeCode(serverUrl);
|
|
397
|
+
}
|
|
398
|
+
function runClaudeCode(serverUrl) {
|
|
399
|
+
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.");
|
|
400
|
+
invariant(state.models, "Models should be loaded by now");
|
|
401
|
+
const opusModel = getLatestModelForFamily("opus")?.id;
|
|
402
|
+
const sonnetModel = getLatestModelForFamily("sonnet")?.id;
|
|
403
|
+
const haikuModel = getLatestModelForFamily("haiku")?.id;
|
|
404
|
+
consola.info(`Selected default Claude Code models:
|
|
405
|
+
- Opus: ${opusModel ?? "(none available)"}\n- Sonnet: ${sonnetModel ?? "(none available)"}\n- Haiku: ${haikuModel ?? "(none available)"}`);
|
|
406
|
+
const command = generateEnvScript({
|
|
407
|
+
ANTHROPIC_BASE_URL: serverUrl,
|
|
408
|
+
ANTHROPIC_AUTH_TOKEN: "dummy",
|
|
409
|
+
ANTHROPIC_MODEL: sonnetModel ?? opusModel,
|
|
410
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel,
|
|
411
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel,
|
|
412
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: haikuModel,
|
|
413
|
+
CLAUDE_CODE_USE_VERTEX: "0",
|
|
414
|
+
CLAUDE_CODE_USE_BEDROCK: "0",
|
|
415
|
+
DISABLE_NON_ESSENTIAL_MODEL_CALLS: "1",
|
|
416
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
417
|
+
CLAUDE_CODE_ATTRIBUTION_HEADER: "0",
|
|
418
|
+
CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: "false",
|
|
419
|
+
CLAUDE_CODE_DISABLE_TERMINAL_TITLE: "true",
|
|
420
|
+
CLAUDE_CODE_ENABLE_AWAY_SUMMARY: "0",
|
|
421
|
+
CLAUDE_CODE_TOTAL_TOKENS_REMINDER: "off",
|
|
422
|
+
CLAUDE_CODE_EFFORT_LEVEL: "max",
|
|
423
|
+
MCP_CONNECT_TIMEOUT_MS: "20000"
|
|
424
|
+
}, "claude");
|
|
425
|
+
try {
|
|
426
|
+
clipboard.writeSync(command);
|
|
427
|
+
consola.success("Copied Claude Code command to clipboard!");
|
|
428
|
+
} catch {
|
|
429
|
+
consola.warn("Failed to copy to clipboard. Here is the Claude Code command:");
|
|
430
|
+
consola.log(command);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
async function setupProviderMode(serverUrl, claudeCode) {
|
|
434
|
+
const enabledProviders = listEnabledProviders();
|
|
435
|
+
if (enabledProviders.length > 0) {
|
|
436
|
+
consola.info(`Using enabled providers: ${enabledProviders.join(", ")}`);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
consola.info("No enabled providers found. Setting one up...");
|
|
440
|
+
await runProviderSetup();
|
|
441
|
+
if (state.githubToken) {
|
|
442
|
+
await setupCopilotMode(state.githubToken, false, serverUrl, claudeCode);
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
const providersAfterSetup = listEnabledProviders();
|
|
446
|
+
if (providersAfterSetup.length === 0) throw new Error("Failed to configure any provider. Run `copilot-api auth login` to set one up.");
|
|
447
|
+
consola.info(`Configured providers: ${providersAfterSetup.join(", ")}`);
|
|
448
|
+
}
|
|
449
|
+
async function runServer(options) {
|
|
450
|
+
(await import("./tls-Aq1Dd8E2.js")).enableSystemCACompat();
|
|
451
|
+
consola.options.throttle = 0;
|
|
452
|
+
mergeConfigWithDefaults();
|
|
453
|
+
const missingApiKeysMessage = getMissingApiKeysMessage();
|
|
454
|
+
if (missingApiKeysMessage) consola.info(missingApiKeysMessage);
|
|
455
|
+
await initOpencodeVersion();
|
|
456
|
+
if (options.proxyEnv) initProxyFromEnv();
|
|
457
|
+
state.verbose = options.verbose;
|
|
458
|
+
if (options.verbose) {
|
|
459
|
+
consola.level = 5;
|
|
460
|
+
consola.info("Verbose logging enabled");
|
|
461
|
+
}
|
|
462
|
+
state.showToken = options.showToken;
|
|
463
|
+
await ensurePaths();
|
|
464
|
+
const serverUrl = `http://localhost:${options.port}`;
|
|
465
|
+
const githubToken = options.githubToken || await readGitHubToken();
|
|
466
|
+
if (githubToken) await setupCopilotMode(githubToken, Boolean(options.githubToken), serverUrl, options.claudeCode);
|
|
467
|
+
else await setupProviderMode(serverUrl, options.claudeCode);
|
|
468
|
+
consola.box(`š Usage Viewer: ${serverUrl}/usage-viewer?endpoint=${serverUrl}/usage`);
|
|
469
|
+
const { server } = await import("./server-CFQmvoAJ.js");
|
|
470
|
+
serve({
|
|
471
|
+
fetch: server.fetch,
|
|
472
|
+
port: options.port,
|
|
473
|
+
bun: { idleTimeout: 0 }
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
const start = defineCommand({
|
|
477
|
+
meta: {
|
|
478
|
+
name: "start",
|
|
479
|
+
description: "Start the Copilot API server"
|
|
480
|
+
},
|
|
481
|
+
args: {
|
|
482
|
+
port: {
|
|
483
|
+
alias: "p",
|
|
484
|
+
type: "string",
|
|
485
|
+
default: "4141",
|
|
486
|
+
description: "Port to listen on"
|
|
487
|
+
},
|
|
488
|
+
verbose: {
|
|
489
|
+
alias: "v",
|
|
490
|
+
type: "boolean",
|
|
491
|
+
default: false,
|
|
492
|
+
description: "Enable verbose logging"
|
|
493
|
+
},
|
|
494
|
+
"github-token": {
|
|
495
|
+
alias: "g",
|
|
496
|
+
type: "string",
|
|
497
|
+
description: "Provide GitHub token directly (must be generated using the `auth` subcommand)"
|
|
498
|
+
},
|
|
499
|
+
"claude-code": {
|
|
500
|
+
alias: "c",
|
|
501
|
+
type: "boolean",
|
|
502
|
+
default: false,
|
|
503
|
+
description: "Generate a command to launch Claude Code with Copilot API config"
|
|
504
|
+
},
|
|
505
|
+
"show-token": {
|
|
506
|
+
type: "boolean",
|
|
507
|
+
default: false,
|
|
508
|
+
description: "Show GitHub and Copilot tokens on fetch and refresh"
|
|
509
|
+
},
|
|
510
|
+
"proxy-env": {
|
|
511
|
+
type: "boolean",
|
|
512
|
+
default: false,
|
|
513
|
+
description: "Initialize proxy from environment variables"
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
run({ args }) {
|
|
517
|
+
return runServer({
|
|
518
|
+
port: Number.parseInt(args.port, 10),
|
|
519
|
+
verbose: args.verbose,
|
|
520
|
+
githubToken: args["github-token"],
|
|
521
|
+
claudeCode: args["claude-code"],
|
|
522
|
+
showToken: args["show-token"],
|
|
523
|
+
proxyEnv: args["proxy-env"]
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
//#endregion
|
|
528
|
+
export { start };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import consola from "consola";
|
|
2
|
+
import tls from "node:tls";
|
|
3
|
+
//#region src/lib/tls.ts
|
|
4
|
+
function enableSystemCACompat() {
|
|
5
|
+
if (!(typeof Bun !== "undefined")) try {
|
|
6
|
+
const defaultCerts = tls.getCACertificates("default");
|
|
7
|
+
const systemCerts = tls.getCACertificates("system");
|
|
8
|
+
tls.setDefaultCACertificates([...defaultCerts, ...systemCerts]);
|
|
9
|
+
} catch {
|
|
10
|
+
consola.log("[tls] system ca not available");
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { enableSystemCACompat };
|