copilot-api-plus 1.0.8 → 1.0.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 +482 -507
- package/dist/{auth-KlL1W4gV.js → auth-BO-mwvoU.js} +1 -1
- package/dist/auth-Cm_0h9bp.js +268 -0
- package/dist/auth-Cm_0h9bp.js.map +1 -0
- package/dist/{auth-C5zV8JbW.js → auth-Cua-c0dq.js} +1 -1
- package/dist/auth-Cua-c0dq.js.map +1 -0
- package/dist/auth-D2wtETTq.js +4 -0
- package/dist/get-models-Bbb8B5jI.js +218 -0
- package/dist/get-models-Bbb8B5jI.js.map +1 -0
- package/dist/{get-models-Hlxa1hWY.js → get-models-DJfPj_Rg.js} +2 -2
- package/dist/get-models-DJfPj_Rg.js.map +1 -0
- package/dist/get-models-Dq2ZDU9m.js +5 -0
- package/dist/{get-user-DgPgvnrS.js → get-user-BFf5xJXg.js} +2 -2
- package/dist/{get-user-DgPgvnrS.js.map → get-user-BFf5xJXg.js.map} +1 -1
- package/dist/get-user-hpkh0FzZ.js +5 -0
- package/dist/main.js +1021 -33
- package/dist/main.js.map +1 -1
- package/dist/{state-DAw5jMjc.js → state-CcLGr8VN.js} +3 -2
- package/dist/state-CcLGr8VN.js.map +1 -0
- package/dist/{token-DYeOMeid.js → token-B8crDDoA.js} +14 -12
- package/dist/token-B8crDDoA.js.map +1 -0
- package/dist/{token-BssxOyqn.js → token-DS09XjQ5.js} +3 -3
- package/package.json +4 -2
- package/dist/auth-C5zV8JbW.js.map +0 -1
- package/dist/get-models-Hlxa1hWY.js.map +0 -1
- package/dist/get-user-M3sQS0U8.js +0 -5
- package/dist/state-DAw5jMjc.js.map +0 -1
- package/dist/token-DYeOMeid.js.map +0 -1
package/dist/main.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { PATHS, ensurePaths } from "./paths-Ch0ixSo2.js";
|
|
3
|
-
import { state } from "./state-
|
|
4
|
-
import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders } from "./get-user-
|
|
3
|
+
import { state } from "./state-CcLGr8VN.js";
|
|
4
|
+
import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders } from "./get-user-BFf5xJXg.js";
|
|
5
5
|
import { HTTPError, forwardError } from "./error-CvU5otz-.js";
|
|
6
|
-
import { cacheModels, cacheVSCodeVersion, clearGithubToken, isNullish, setupCopilotToken, setupGitHubToken, sleep } from "./token-
|
|
7
|
-
import {
|
|
6
|
+
import { cacheModels, cacheVSCodeVersion, clearGithubToken, isNullish, setupCopilotToken, setupGitHubToken, sleep } from "./token-B8crDDoA.js";
|
|
7
|
+
import { clearAntigravityAuth, disableCurrentAccount, getAntigravityAuthPath, getValidAccessToken, rotateAccount } from "./auth-Cm_0h9bp.js";
|
|
8
|
+
import { clearZenAuth, getZenAuthPath } from "./auth-Cua-c0dq.js";
|
|
9
|
+
import { getAntigravityModels, isThinkingModel } from "./get-models-Bbb8B5jI.js";
|
|
8
10
|
import { defineCommand, runMain } from "citty";
|
|
9
11
|
import consola from "consola";
|
|
10
12
|
import fs from "node:fs/promises";
|
|
11
13
|
import os from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import * as p from "@clack/prompts";
|
|
12
16
|
import clipboard from "clipboardy";
|
|
13
17
|
import { serve } from "srvx";
|
|
14
18
|
import invariant from "tiny-invariant";
|
|
@@ -185,9 +189,11 @@ async function runLogout(options) {
|
|
|
185
189
|
if (options.all) {
|
|
186
190
|
await clearGithubToken();
|
|
187
191
|
await clearZenAuth();
|
|
192
|
+
await clearAntigravityAuth();
|
|
188
193
|
consola.success("Logged out from all services");
|
|
189
194
|
consola.info(`GitHub token: ${PATHS.GITHUB_TOKEN_PATH}`);
|
|
190
195
|
consola.info(`Zen API key: ${getZenAuthPath()}`);
|
|
196
|
+
consola.info(`Antigravity accounts: ${getAntigravityAuthPath()}`);
|
|
191
197
|
return;
|
|
192
198
|
}
|
|
193
199
|
if (options.zen) {
|
|
@@ -196,26 +202,42 @@ async function runLogout(options) {
|
|
|
196
202
|
consola.info(`Zen API key location: ${getZenAuthPath()}`);
|
|
197
203
|
return;
|
|
198
204
|
}
|
|
199
|
-
|
|
205
|
+
if (options.antigravity) {
|
|
206
|
+
await clearAntigravityAuth();
|
|
207
|
+
consola.success("Logged out from Google Antigravity");
|
|
208
|
+
consola.info(`Antigravity accounts location: ${getAntigravityAuthPath()}`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
switch (await consola.prompt("Which credentials do you want to clear?", {
|
|
200
212
|
type: "select",
|
|
201
213
|
options: [
|
|
202
214
|
"GitHub Copilot token",
|
|
203
215
|
"OpenCode Zen API key",
|
|
204
|
-
"
|
|
216
|
+
"Google Antigravity accounts",
|
|
217
|
+
"All credentials"
|
|
205
218
|
]
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
})) {
|
|
220
|
+
case "GitHub Copilot token":
|
|
221
|
+
await clearGithubToken();
|
|
222
|
+
consola.success("Logged out from GitHub Copilot");
|
|
223
|
+
consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
|
|
224
|
+
break;
|
|
225
|
+
case "OpenCode Zen API key":
|
|
226
|
+
await clearZenAuth();
|
|
227
|
+
consola.success("Logged out from OpenCode Zen");
|
|
228
|
+
consola.info(`Zen API key location: ${getZenAuthPath()}`);
|
|
229
|
+
break;
|
|
230
|
+
case "Google Antigravity accounts":
|
|
231
|
+
await clearAntigravityAuth();
|
|
232
|
+
consola.success("Logged out from Google Antigravity");
|
|
233
|
+
consola.info(`Antigravity accounts location: ${getAntigravityAuthPath()}`);
|
|
234
|
+
break;
|
|
235
|
+
case "All credentials":
|
|
236
|
+
await clearGithubToken();
|
|
237
|
+
await clearZenAuth();
|
|
238
|
+
await clearAntigravityAuth();
|
|
239
|
+
consola.success("Logged out from all services");
|
|
240
|
+
break;
|
|
219
241
|
}
|
|
220
242
|
}
|
|
221
243
|
const logout = defineCommand({
|
|
@@ -230,21 +252,259 @@ const logout = defineCommand({
|
|
|
230
252
|
default: false,
|
|
231
253
|
description: "Clear only OpenCode Zen API key"
|
|
232
254
|
},
|
|
255
|
+
antigravity: {
|
|
256
|
+
type: "boolean",
|
|
257
|
+
default: false,
|
|
258
|
+
description: "Clear only Google Antigravity accounts"
|
|
259
|
+
},
|
|
233
260
|
all: {
|
|
234
261
|
alias: "a",
|
|
235
262
|
type: "boolean",
|
|
236
263
|
default: false,
|
|
237
|
-
description: "Clear all credentials (GitHub and
|
|
264
|
+
description: "Clear all credentials (GitHub, Zen, and Antigravity)"
|
|
238
265
|
}
|
|
239
266
|
},
|
|
240
267
|
run({ args }) {
|
|
241
268
|
return runLogout({
|
|
242
269
|
zen: args.zen,
|
|
270
|
+
antigravity: args.antigravity,
|
|
243
271
|
all: args.all
|
|
244
272
|
});
|
|
245
273
|
}
|
|
246
274
|
});
|
|
247
275
|
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/lib/config.ts
|
|
278
|
+
const CONFIG_FILENAME = "config.json";
|
|
279
|
+
/**
|
|
280
|
+
* Get the path to the config file
|
|
281
|
+
*/
|
|
282
|
+
function getConfigPath() {
|
|
283
|
+
return path.join(PATHS.DATA_DIR, CONFIG_FILENAME);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Load configuration from file
|
|
287
|
+
*/
|
|
288
|
+
async function loadConfig() {
|
|
289
|
+
try {
|
|
290
|
+
const configPath = getConfigPath();
|
|
291
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
292
|
+
return JSON.parse(content);
|
|
293
|
+
} catch {
|
|
294
|
+
return {};
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Save configuration to file
|
|
299
|
+
*/
|
|
300
|
+
async function saveConfig(config) {
|
|
301
|
+
const configPath = getConfigPath();
|
|
302
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
303
|
+
consola.debug(`Configuration saved to ${configPath}`);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get proxy configuration
|
|
307
|
+
*/
|
|
308
|
+
async function getProxyConfig() {
|
|
309
|
+
return (await loadConfig()).proxy;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Save proxy configuration
|
|
313
|
+
*/
|
|
314
|
+
async function saveProxyConfig(proxyConfig) {
|
|
315
|
+
const config = await loadConfig();
|
|
316
|
+
config.proxy = proxyConfig;
|
|
317
|
+
await saveConfig(config);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Clear proxy configuration
|
|
321
|
+
*/
|
|
322
|
+
async function clearProxyConfig() {
|
|
323
|
+
const config = await loadConfig();
|
|
324
|
+
delete config.proxy;
|
|
325
|
+
await saveConfig(config);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Apply saved proxy configuration to environment variables
|
|
329
|
+
* This should be called at startup to restore proxy settings
|
|
330
|
+
*/
|
|
331
|
+
async function applyProxyConfig() {
|
|
332
|
+
const proxyConfig = await getProxyConfig();
|
|
333
|
+
if (!proxyConfig || !proxyConfig.enabled) return false;
|
|
334
|
+
if (proxyConfig.httpProxy) {
|
|
335
|
+
process.env.HTTP_PROXY = proxyConfig.httpProxy;
|
|
336
|
+
process.env.http_proxy = proxyConfig.httpProxy;
|
|
337
|
+
}
|
|
338
|
+
if (proxyConfig.httpsProxy) {
|
|
339
|
+
process.env.HTTPS_PROXY = proxyConfig.httpsProxy;
|
|
340
|
+
process.env.https_proxy = proxyConfig.httpsProxy;
|
|
341
|
+
}
|
|
342
|
+
if (proxyConfig.noProxy) {
|
|
343
|
+
process.env.NO_PROXY = proxyConfig.noProxy;
|
|
344
|
+
process.env.no_proxy = proxyConfig.noProxy;
|
|
345
|
+
}
|
|
346
|
+
consola.info("Proxy configuration loaded from saved settings");
|
|
347
|
+
if (proxyConfig.httpProxy) consola.info(` HTTP_PROXY: ${proxyConfig.httpProxy}`);
|
|
348
|
+
if (proxyConfig.httpsProxy) consola.info(` HTTPS_PROXY: ${proxyConfig.httpsProxy}`);
|
|
349
|
+
if (proxyConfig.noProxy) consola.info(` NO_PROXY: ${proxyConfig.noProxy}`);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/proxy-config.ts
|
|
355
|
+
const proxy = defineCommand({
|
|
356
|
+
meta: {
|
|
357
|
+
name: "proxy",
|
|
358
|
+
description: "Configure proxy settings (persistent)"
|
|
359
|
+
},
|
|
360
|
+
args: {
|
|
361
|
+
set: {
|
|
362
|
+
type: "boolean",
|
|
363
|
+
default: false,
|
|
364
|
+
description: "Set proxy configuration interactively"
|
|
365
|
+
},
|
|
366
|
+
enable: {
|
|
367
|
+
type: "boolean",
|
|
368
|
+
default: false,
|
|
369
|
+
description: "Enable saved proxy configuration"
|
|
370
|
+
},
|
|
371
|
+
disable: {
|
|
372
|
+
type: "boolean",
|
|
373
|
+
default: false,
|
|
374
|
+
description: "Disable proxy (keep settings)"
|
|
375
|
+
},
|
|
376
|
+
clear: {
|
|
377
|
+
type: "boolean",
|
|
378
|
+
default: false,
|
|
379
|
+
description: "Clear all proxy settings"
|
|
380
|
+
},
|
|
381
|
+
show: {
|
|
382
|
+
type: "boolean",
|
|
383
|
+
default: false,
|
|
384
|
+
description: "Show current proxy configuration"
|
|
385
|
+
},
|
|
386
|
+
"http-proxy": {
|
|
387
|
+
type: "string",
|
|
388
|
+
description: "HTTP proxy URL (e.g., http://proxy:8080)"
|
|
389
|
+
},
|
|
390
|
+
"https-proxy": {
|
|
391
|
+
type: "string",
|
|
392
|
+
description: "HTTPS proxy URL (e.g., http://proxy:8080)"
|
|
393
|
+
},
|
|
394
|
+
"no-proxy": {
|
|
395
|
+
type: "string",
|
|
396
|
+
description: "Comma-separated list of hosts to bypass proxy"
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
async run({ args }) {
|
|
400
|
+
await ensurePaths();
|
|
401
|
+
if (args.show || !args.set && !args.enable && !args.disable && !args.clear && !args["http-proxy"] && !args["https-proxy"]) {
|
|
402
|
+
const config = await getProxyConfig();
|
|
403
|
+
if (!config) {
|
|
404
|
+
consola.info("No proxy configuration saved.");
|
|
405
|
+
consola.info("");
|
|
406
|
+
consola.info("To configure proxy, use one of:");
|
|
407
|
+
consola.info(" copilot-api-plus proxy --set # Interactive setup");
|
|
408
|
+
consola.info(" copilot-api-plus proxy --http-proxy http://proxy:8080 # Direct set");
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
consola.info("Current proxy configuration:");
|
|
412
|
+
consola.info(` Status: ${config.enabled ? "✅ Enabled" : "❌ Disabled"}`);
|
|
413
|
+
if (config.httpProxy) consola.info(` HTTP_PROXY: ${config.httpProxy}`);
|
|
414
|
+
if (config.httpsProxy) consola.info(` HTTPS_PROXY: ${config.httpsProxy}`);
|
|
415
|
+
if (config.noProxy) consola.info(` NO_PROXY: ${config.noProxy}`);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (args.clear) {
|
|
419
|
+
await clearProxyConfig();
|
|
420
|
+
consola.success("Proxy configuration cleared.");
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (args.enable) {
|
|
424
|
+
const config = await getProxyConfig();
|
|
425
|
+
if (!config) {
|
|
426
|
+
consola.error("No proxy configuration saved. Use --set to configure first.");
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
config.enabled = true;
|
|
430
|
+
await saveProxyConfig(config);
|
|
431
|
+
consola.success("Proxy enabled. It will be used on next server start.");
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (args.disable) {
|
|
435
|
+
const config = await getProxyConfig();
|
|
436
|
+
if (!config) {
|
|
437
|
+
consola.info("No proxy configuration to disable.");
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
config.enabled = false;
|
|
441
|
+
await saveProxyConfig(config);
|
|
442
|
+
consola.success("Proxy disabled. Settings are preserved.");
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (args["http-proxy"] || args["https-proxy"]) {
|
|
446
|
+
const newConfig = {
|
|
447
|
+
enabled: true,
|
|
448
|
+
httpProxy: args["http-proxy"],
|
|
449
|
+
httpsProxy: args["https-proxy"] || args["http-proxy"],
|
|
450
|
+
noProxy: args["no-proxy"]
|
|
451
|
+
};
|
|
452
|
+
await saveProxyConfig(newConfig);
|
|
453
|
+
consola.success("Proxy configuration saved and enabled.");
|
|
454
|
+
consola.info(` HTTP_PROXY: ${newConfig.httpProxy || "(not set)"}`);
|
|
455
|
+
consola.info(` HTTPS_PROXY: ${newConfig.httpsProxy || "(not set)"}`);
|
|
456
|
+
if (newConfig.noProxy) consola.info(` NO_PROXY: ${newConfig.noProxy}`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
if (args.set) {
|
|
460
|
+
p.intro("Proxy Configuration");
|
|
461
|
+
const existingConfig = await getProxyConfig();
|
|
462
|
+
const httpProxy = await p.text({
|
|
463
|
+
message: "HTTP Proxy URL",
|
|
464
|
+
placeholder: "http://proxy:8080",
|
|
465
|
+
initialValue: existingConfig?.httpProxy || ""
|
|
466
|
+
});
|
|
467
|
+
if (p.isCancel(httpProxy)) {
|
|
468
|
+
p.cancel("Configuration cancelled.");
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const httpsProxy = await p.text({
|
|
472
|
+
message: "HTTPS Proxy URL (leave empty to use HTTP proxy)",
|
|
473
|
+
placeholder: "http://proxy:8080",
|
|
474
|
+
initialValue: existingConfig?.httpsProxy || ""
|
|
475
|
+
});
|
|
476
|
+
if (p.isCancel(httpsProxy)) {
|
|
477
|
+
p.cancel("Configuration cancelled.");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const noProxy = await p.text({
|
|
481
|
+
message: "No Proxy (comma-separated hosts to bypass)",
|
|
482
|
+
placeholder: "localhost,127.0.0.1,.local",
|
|
483
|
+
initialValue: existingConfig?.noProxy || ""
|
|
484
|
+
});
|
|
485
|
+
if (p.isCancel(noProxy)) {
|
|
486
|
+
p.cancel("Configuration cancelled.");
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const enable = await p.confirm({
|
|
490
|
+
message: "Enable proxy now?",
|
|
491
|
+
initialValue: true
|
|
492
|
+
});
|
|
493
|
+
if (p.isCancel(enable)) {
|
|
494
|
+
p.cancel("Configuration cancelled.");
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
await saveProxyConfig({
|
|
498
|
+
enabled: enable,
|
|
499
|
+
httpProxy: httpProxy || void 0,
|
|
500
|
+
httpsProxy: httpsProxy || httpProxy || void 0,
|
|
501
|
+
noProxy: noProxy || void 0
|
|
502
|
+
});
|
|
503
|
+
p.outro(`Proxy configuration saved${enable ? " and enabled" : ""}.`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
248
508
|
//#endregion
|
|
249
509
|
//#region src/lib/proxy.ts
|
|
250
510
|
function initProxyFromEnv() {
|
|
@@ -376,6 +636,700 @@ const apiKeyAuthMiddleware = async (c, next) => {
|
|
|
376
636
|
await next();
|
|
377
637
|
};
|
|
378
638
|
|
|
639
|
+
//#endregion
|
|
640
|
+
//#region src/services/antigravity/create-chat-completions.ts
|
|
641
|
+
const ANTIGRAVITY_API_HOST$1 = "daily-cloudcode-pa.sandbox.googleapis.com";
|
|
642
|
+
const ANTIGRAVITY_STREAM_URL$1 = `https://${ANTIGRAVITY_API_HOST$1}/v1internal:streamGenerateContent?alt=sse`;
|
|
643
|
+
const ANTIGRAVITY_NO_STREAM_URL$1 = `https://${ANTIGRAVITY_API_HOST$1}/v1internal:generateContent`;
|
|
644
|
+
const ANTIGRAVITY_USER_AGENT$1 = "antigravity/1.11.3 windows/amd64";
|
|
645
|
+
/**
|
|
646
|
+
* Convert OpenAI format messages to Antigravity format
|
|
647
|
+
*/
|
|
648
|
+
function convertMessages$1(messages) {
|
|
649
|
+
const contents = [];
|
|
650
|
+
let systemInstruction = void 0;
|
|
651
|
+
for (const message of messages) {
|
|
652
|
+
if (message.role === "system") {
|
|
653
|
+
systemInstruction = {
|
|
654
|
+
role: "user",
|
|
655
|
+
parts: [{ text: typeof message.content === "string" ? message.content : message.content.map((c) => c.text || "").join("") }]
|
|
656
|
+
};
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const role = message.role === "assistant" ? "model" : "user";
|
|
660
|
+
if (typeof message.content === "string") contents.push({
|
|
661
|
+
role,
|
|
662
|
+
parts: [{ text: message.content }]
|
|
663
|
+
});
|
|
664
|
+
else {
|
|
665
|
+
const parts = [];
|
|
666
|
+
for (const part of message.content) if (part.type === "text") parts.push({ text: part.text });
|
|
667
|
+
else if (part.type === "image_url" && part.image_url?.url) {
|
|
668
|
+
const url = part.image_url.url;
|
|
669
|
+
if (url.startsWith("data:")) {
|
|
670
|
+
const match = url.match(/^data:([^;]+);base64,(.+)$/);
|
|
671
|
+
if (match) parts.push({ inlineData: {
|
|
672
|
+
mimeType: match[1],
|
|
673
|
+
data: match[2]
|
|
674
|
+
} });
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
contents.push({
|
|
678
|
+
role,
|
|
679
|
+
parts
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return {
|
|
684
|
+
contents,
|
|
685
|
+
systemInstruction
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Convert tools to Antigravity format
|
|
690
|
+
*/
|
|
691
|
+
function convertTools$1(tools) {
|
|
692
|
+
if (!tools || tools.length === 0) return;
|
|
693
|
+
return tools.map((tool) => {
|
|
694
|
+
const t = tool;
|
|
695
|
+
if (t.type === "function" && t.function) return { functionDeclarations: [{
|
|
696
|
+
name: t.function.name,
|
|
697
|
+
description: t.function.description || "",
|
|
698
|
+
parameters: t.function.parameters || {}
|
|
699
|
+
}] };
|
|
700
|
+
return tool;
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Create chat completion with Antigravity
|
|
705
|
+
*/
|
|
706
|
+
async function createAntigravityChatCompletion(request) {
|
|
707
|
+
const accessToken = await getValidAccessToken();
|
|
708
|
+
if (!accessToken) return new Response(JSON.stringify({ error: {
|
|
709
|
+
message: "No valid Antigravity access token available. Please run login first.",
|
|
710
|
+
type: "auth_error"
|
|
711
|
+
} }), {
|
|
712
|
+
status: 401,
|
|
713
|
+
headers: { "Content-Type": "application/json" }
|
|
714
|
+
});
|
|
715
|
+
const { contents, systemInstruction } = convertMessages$1(request.messages);
|
|
716
|
+
const tools = convertTools$1(request.tools);
|
|
717
|
+
const antigravityRequest = {
|
|
718
|
+
model: request.model,
|
|
719
|
+
contents,
|
|
720
|
+
generationConfig: {
|
|
721
|
+
temperature: request.temperature ?? 1,
|
|
722
|
+
topP: request.top_p ?? .85,
|
|
723
|
+
topK: request.top_k ?? 50,
|
|
724
|
+
maxOutputTokens: request.max_tokens ?? 8096
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
if (systemInstruction) antigravityRequest.systemInstruction = systemInstruction;
|
|
728
|
+
if (tools) antigravityRequest.tools = tools;
|
|
729
|
+
if (isThinkingModel(request.model)) antigravityRequest.generationConfig = {
|
|
730
|
+
...antigravityRequest.generationConfig,
|
|
731
|
+
thinkingConfig: { includeThoughts: true }
|
|
732
|
+
};
|
|
733
|
+
const endpoint = request.stream ? ANTIGRAVITY_STREAM_URL$1 : ANTIGRAVITY_NO_STREAM_URL$1;
|
|
734
|
+
consola.debug(`Antigravity request to ${endpoint} with model ${request.model}`);
|
|
735
|
+
try {
|
|
736
|
+
const response = await fetch(endpoint, {
|
|
737
|
+
method: "POST",
|
|
738
|
+
headers: {
|
|
739
|
+
Host: ANTIGRAVITY_API_HOST$1,
|
|
740
|
+
"User-Agent": ANTIGRAVITY_USER_AGENT$1,
|
|
741
|
+
Authorization: `Bearer ${accessToken}`,
|
|
742
|
+
"Content-Type": "application/json",
|
|
743
|
+
"Accept-Encoding": "gzip"
|
|
744
|
+
},
|
|
745
|
+
body: JSON.stringify(antigravityRequest)
|
|
746
|
+
});
|
|
747
|
+
if (!response.ok) {
|
|
748
|
+
const errorText = await response.text();
|
|
749
|
+
consola.error(`Antigravity error: ${response.status} ${errorText}`);
|
|
750
|
+
if (response.status === 403) await disableCurrentAccount();
|
|
751
|
+
if (response.status === 429 || response.status === 503) await rotateAccount();
|
|
752
|
+
return new Response(JSON.stringify({ error: {
|
|
753
|
+
message: `Antigravity API error: ${response.status}`,
|
|
754
|
+
type: "api_error",
|
|
755
|
+
details: errorText
|
|
756
|
+
} }), {
|
|
757
|
+
status: response.status,
|
|
758
|
+
headers: { "Content-Type": "application/json" }
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
if (request.stream) return transformStreamResponse(response, request.model);
|
|
762
|
+
else return transformNonStreamResponse(response, request.model);
|
|
763
|
+
} catch (error) {
|
|
764
|
+
consola.error("Antigravity request error:", error);
|
|
765
|
+
return new Response(JSON.stringify({ error: {
|
|
766
|
+
message: `Request failed: ${error}`,
|
|
767
|
+
type: "request_error"
|
|
768
|
+
} }), {
|
|
769
|
+
status: 500,
|
|
770
|
+
headers: { "Content-Type": "application/json" }
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Transform Antigravity stream response to OpenAI format
|
|
776
|
+
*/
|
|
777
|
+
function transformStreamResponse(response, model) {
|
|
778
|
+
const reader = response.body?.getReader();
|
|
779
|
+
if (!reader) return new Response("No response body", { status: 500 });
|
|
780
|
+
const encoder = new TextEncoder();
|
|
781
|
+
const decoder = new TextDecoder();
|
|
782
|
+
const stream = new ReadableStream({ async start(controller) {
|
|
783
|
+
let buffer = "";
|
|
784
|
+
const requestId = `chatcmpl-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
785
|
+
try {
|
|
786
|
+
while (true) {
|
|
787
|
+
const { done, value } = await reader.read();
|
|
788
|
+
if (done) {
|
|
789
|
+
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
790
|
+
controller.close();
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
buffer += decoder.decode(value, { stream: true });
|
|
794
|
+
const lines = buffer.split("\n");
|
|
795
|
+
buffer = lines.pop() || "";
|
|
796
|
+
for (const line of lines) if (line.startsWith("data: ")) {
|
|
797
|
+
const data = line.slice(6).trim();
|
|
798
|
+
if (data === "[DONE]" || data === "") continue;
|
|
799
|
+
try {
|
|
800
|
+
const parsed = JSON.parse(data);
|
|
801
|
+
const candidate = (parsed.response?.candidates || parsed.candidates)?.[0];
|
|
802
|
+
const parts = candidate?.content?.parts || [];
|
|
803
|
+
for (const part of parts) {
|
|
804
|
+
if (part.thought && part.text) {
|
|
805
|
+
const chunk = {
|
|
806
|
+
id: requestId,
|
|
807
|
+
object: "chat.completion.chunk",
|
|
808
|
+
created: Math.floor(Date.now() / 1e3),
|
|
809
|
+
model,
|
|
810
|
+
choices: [{
|
|
811
|
+
index: 0,
|
|
812
|
+
delta: { reasoning_content: part.text },
|
|
813
|
+
finish_reason: null
|
|
814
|
+
}]
|
|
815
|
+
};
|
|
816
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
if (part.text) {
|
|
820
|
+
const chunk = {
|
|
821
|
+
id: requestId,
|
|
822
|
+
object: "chat.completion.chunk",
|
|
823
|
+
created: Math.floor(Date.now() / 1e3),
|
|
824
|
+
model,
|
|
825
|
+
choices: [{
|
|
826
|
+
index: 0,
|
|
827
|
+
delta: { content: part.text },
|
|
828
|
+
finish_reason: candidate?.finishReason === "STOP" ? "stop" : null
|
|
829
|
+
}]
|
|
830
|
+
};
|
|
831
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
|
|
832
|
+
}
|
|
833
|
+
if (part.functionCall) {
|
|
834
|
+
const chunk = {
|
|
835
|
+
id: requestId,
|
|
836
|
+
object: "chat.completion.chunk",
|
|
837
|
+
created: Math.floor(Date.now() / 1e3),
|
|
838
|
+
model,
|
|
839
|
+
choices: [{
|
|
840
|
+
index: 0,
|
|
841
|
+
delta: { tool_calls: [{
|
|
842
|
+
index: 0,
|
|
843
|
+
id: `call_${Date.now()}`,
|
|
844
|
+
type: "function",
|
|
845
|
+
function: {
|
|
846
|
+
name: part.functionCall.name,
|
|
847
|
+
arguments: JSON.stringify(part.functionCall.args)
|
|
848
|
+
}
|
|
849
|
+
}] },
|
|
850
|
+
finish_reason: null
|
|
851
|
+
}]
|
|
852
|
+
};
|
|
853
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
} catch {}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
} catch (error) {
|
|
860
|
+
consola.error("Stream transform error:", error);
|
|
861
|
+
controller.error(error);
|
|
862
|
+
}
|
|
863
|
+
} });
|
|
864
|
+
return new Response(stream, { headers: {
|
|
865
|
+
"Content-Type": "text/event-stream",
|
|
866
|
+
"Cache-Control": "no-cache",
|
|
867
|
+
Connection: "keep-alive"
|
|
868
|
+
} });
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Transform Antigravity non-stream response to OpenAI format
|
|
872
|
+
*/
|
|
873
|
+
async function transformNonStreamResponse(response, model) {
|
|
874
|
+
const data = await response.json();
|
|
875
|
+
const candidate = data.candidates?.[0];
|
|
876
|
+
const parts = candidate?.content?.parts || [];
|
|
877
|
+
let content = "";
|
|
878
|
+
let reasoningContent = "";
|
|
879
|
+
const toolCalls = [];
|
|
880
|
+
for (const part of parts) {
|
|
881
|
+
if (part.thought && part.text) reasoningContent += part.text;
|
|
882
|
+
else if (part.text) content += part.text;
|
|
883
|
+
if (part.functionCall) toolCalls.push({
|
|
884
|
+
id: `call_${Date.now()}`,
|
|
885
|
+
type: "function",
|
|
886
|
+
function: {
|
|
887
|
+
name: part.functionCall.name,
|
|
888
|
+
arguments: JSON.stringify(part.functionCall.args)
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
const message = {
|
|
893
|
+
role: "assistant",
|
|
894
|
+
content: content || null
|
|
895
|
+
};
|
|
896
|
+
if (reasoningContent) message.reasoning_content = reasoningContent;
|
|
897
|
+
if (toolCalls.length > 0) message.tool_calls = toolCalls;
|
|
898
|
+
const openaiResponse = {
|
|
899
|
+
id: `chatcmpl-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
900
|
+
object: "chat.completion",
|
|
901
|
+
created: Math.floor(Date.now() / 1e3),
|
|
902
|
+
model,
|
|
903
|
+
choices: [{
|
|
904
|
+
index: 0,
|
|
905
|
+
message,
|
|
906
|
+
finish_reason: candidate?.finishReason === "STOP" ? "stop" : "stop"
|
|
907
|
+
}],
|
|
908
|
+
usage: {
|
|
909
|
+
prompt_tokens: data.usageMetadata?.promptTokenCount || 0,
|
|
910
|
+
completion_tokens: data.usageMetadata?.candidatesTokenCount || 0,
|
|
911
|
+
total_tokens: data.usageMetadata?.totalTokenCount || 0
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
return new Response(JSON.stringify(openaiResponse), { headers: { "Content-Type": "application/json" } });
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
//#endregion
|
|
918
|
+
//#region src/routes/antigravity/chat-completions/route.ts
|
|
919
|
+
const app$1 = new Hono();
|
|
920
|
+
app$1.post("/", async (c) => {
|
|
921
|
+
const body = await c.req.json();
|
|
922
|
+
const response = await createAntigravityChatCompletion(body);
|
|
923
|
+
const headers = new Headers(response.headers);
|
|
924
|
+
if (body.stream) return new Response(response.body, {
|
|
925
|
+
status: response.status,
|
|
926
|
+
headers
|
|
927
|
+
});
|
|
928
|
+
return new Response(await response.text(), {
|
|
929
|
+
status: response.status,
|
|
930
|
+
headers
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
const antigravityChatCompletionsRoute = app$1;
|
|
934
|
+
|
|
935
|
+
//#endregion
|
|
936
|
+
//#region src/services/antigravity/create-messages.ts
|
|
937
|
+
const ANTIGRAVITY_API_HOST = "daily-cloudcode-pa.sandbox.googleapis.com";
|
|
938
|
+
const ANTIGRAVITY_STREAM_URL = `https://${ANTIGRAVITY_API_HOST}/v1internal:streamGenerateContent?alt=sse`;
|
|
939
|
+
const ANTIGRAVITY_NO_STREAM_URL = `https://${ANTIGRAVITY_API_HOST}/v1internal:generateContent`;
|
|
940
|
+
const ANTIGRAVITY_USER_AGENT = "antigravity/1.11.3 windows/amd64";
|
|
941
|
+
/**
|
|
942
|
+
* Convert Anthropic messages to Antigravity format
|
|
943
|
+
*/
|
|
944
|
+
function convertMessages(messages, system) {
|
|
945
|
+
const contents = [];
|
|
946
|
+
let systemInstruction = void 0;
|
|
947
|
+
if (system) systemInstruction = {
|
|
948
|
+
role: "user",
|
|
949
|
+
parts: [{ text: system }]
|
|
950
|
+
};
|
|
951
|
+
for (const message of messages) {
|
|
952
|
+
const role = message.role === "assistant" ? "model" : "user";
|
|
953
|
+
if (typeof message.content === "string") contents.push({
|
|
954
|
+
role,
|
|
955
|
+
parts: [{ text: message.content }]
|
|
956
|
+
});
|
|
957
|
+
else {
|
|
958
|
+
const parts = [];
|
|
959
|
+
for (const block of message.content) if (block.type === "text" && block.text) parts.push({ text: block.text });
|
|
960
|
+
else if (block.type === "image" && block.source) parts.push({ inlineData: {
|
|
961
|
+
mimeType: block.source.media_type,
|
|
962
|
+
data: block.source.data
|
|
963
|
+
} });
|
|
964
|
+
if (parts.length > 0) contents.push({
|
|
965
|
+
role,
|
|
966
|
+
parts
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
return {
|
|
971
|
+
contents,
|
|
972
|
+
systemInstruction
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Convert tools to Antigravity format
|
|
977
|
+
*/
|
|
978
|
+
function convertTools(tools) {
|
|
979
|
+
if (!tools || tools.length === 0) return;
|
|
980
|
+
return tools.map((tool) => {
|
|
981
|
+
const t = tool;
|
|
982
|
+
return { functionDeclarations: [{
|
|
983
|
+
name: t.name,
|
|
984
|
+
description: t.description || "",
|
|
985
|
+
parameters: t.input_schema || {}
|
|
986
|
+
}] };
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Create Anthropic-compatible message response using Antigravity
|
|
991
|
+
*/
|
|
992
|
+
async function createAntigravityMessages(request) {
|
|
993
|
+
const accessToken = await getValidAccessToken();
|
|
994
|
+
if (!accessToken) return new Response(JSON.stringify({
|
|
995
|
+
type: "error",
|
|
996
|
+
error: {
|
|
997
|
+
type: "authentication_error",
|
|
998
|
+
message: "No valid Antigravity access token available. Please run login first."
|
|
999
|
+
}
|
|
1000
|
+
}), {
|
|
1001
|
+
status: 401,
|
|
1002
|
+
headers: { "Content-Type": "application/json" }
|
|
1003
|
+
});
|
|
1004
|
+
const { contents, systemInstruction } = convertMessages(request.messages, request.system);
|
|
1005
|
+
const tools = convertTools(request.tools);
|
|
1006
|
+
const antigravityRequest = {
|
|
1007
|
+
model: request.model,
|
|
1008
|
+
contents,
|
|
1009
|
+
generationConfig: {
|
|
1010
|
+
temperature: request.temperature ?? 1,
|
|
1011
|
+
topP: request.top_p ?? .85,
|
|
1012
|
+
topK: request.top_k ?? 50,
|
|
1013
|
+
maxOutputTokens: request.max_tokens ?? 8096
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
if (systemInstruction) antigravityRequest.systemInstruction = systemInstruction;
|
|
1017
|
+
if (tools) antigravityRequest.tools = tools;
|
|
1018
|
+
if (isThinkingModel(request.model)) antigravityRequest.generationConfig = {
|
|
1019
|
+
...antigravityRequest.generationConfig,
|
|
1020
|
+
thinkingConfig: { includeThoughts: true }
|
|
1021
|
+
};
|
|
1022
|
+
const endpoint = request.stream ? ANTIGRAVITY_STREAM_URL : ANTIGRAVITY_NO_STREAM_URL;
|
|
1023
|
+
consola.debug(`Antigravity messages request to ${endpoint} with model ${request.model}`);
|
|
1024
|
+
try {
|
|
1025
|
+
const response = await fetch(endpoint, {
|
|
1026
|
+
method: "POST",
|
|
1027
|
+
headers: {
|
|
1028
|
+
Host: ANTIGRAVITY_API_HOST,
|
|
1029
|
+
"User-Agent": ANTIGRAVITY_USER_AGENT,
|
|
1030
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1031
|
+
"Content-Type": "application/json",
|
|
1032
|
+
"Accept-Encoding": "gzip"
|
|
1033
|
+
},
|
|
1034
|
+
body: JSON.stringify(antigravityRequest)
|
|
1035
|
+
});
|
|
1036
|
+
if (!response.ok) {
|
|
1037
|
+
const errorText = await response.text();
|
|
1038
|
+
consola.error(`Antigravity error: ${response.status} ${errorText}`);
|
|
1039
|
+
if (response.status === 403) await disableCurrentAccount();
|
|
1040
|
+
if (response.status === 429 || response.status === 503) await rotateAccount();
|
|
1041
|
+
return new Response(JSON.stringify({
|
|
1042
|
+
type: "error",
|
|
1043
|
+
error: {
|
|
1044
|
+
type: "api_error",
|
|
1045
|
+
message: `Antigravity API error: ${response.status}`
|
|
1046
|
+
}
|
|
1047
|
+
}), {
|
|
1048
|
+
status: response.status,
|
|
1049
|
+
headers: { "Content-Type": "application/json" }
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
if (request.stream) return transformStreamToAnthropic(response, request.model);
|
|
1053
|
+
else return transformNonStreamToAnthropic(response, request.model);
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
consola.error("Antigravity messages request error:", error);
|
|
1056
|
+
return new Response(JSON.stringify({
|
|
1057
|
+
type: "error",
|
|
1058
|
+
error: {
|
|
1059
|
+
type: "api_error",
|
|
1060
|
+
message: `Request failed: ${error}`
|
|
1061
|
+
}
|
|
1062
|
+
}), {
|
|
1063
|
+
status: 500,
|
|
1064
|
+
headers: { "Content-Type": "application/json" }
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Transform Antigravity stream response to Anthropic format
|
|
1070
|
+
*/
|
|
1071
|
+
function transformStreamToAnthropic(response, model) {
|
|
1072
|
+
const reader = response.body?.getReader();
|
|
1073
|
+
if (!reader) return new Response("No response body", { status: 500 });
|
|
1074
|
+
const encoder = new TextEncoder();
|
|
1075
|
+
const decoder = new TextDecoder();
|
|
1076
|
+
const stream = new ReadableStream({ async start(controller) {
|
|
1077
|
+
let buffer = "";
|
|
1078
|
+
const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
1079
|
+
let inputTokens = 0;
|
|
1080
|
+
let outputTokens = 0;
|
|
1081
|
+
let contentBlockIndex = 0;
|
|
1082
|
+
let thinkingBlockStarted = false;
|
|
1083
|
+
let textBlockStarted = false;
|
|
1084
|
+
const messageStart = {
|
|
1085
|
+
type: "message_start",
|
|
1086
|
+
message: {
|
|
1087
|
+
id: messageId,
|
|
1088
|
+
type: "message",
|
|
1089
|
+
role: "assistant",
|
|
1090
|
+
content: [],
|
|
1091
|
+
model,
|
|
1092
|
+
stop_reason: null,
|
|
1093
|
+
stop_sequence: null,
|
|
1094
|
+
usage: {
|
|
1095
|
+
input_tokens: 0,
|
|
1096
|
+
output_tokens: 0
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
controller.enqueue(encoder.encode(`event: message_start\ndata: ${JSON.stringify(messageStart)}\n\n`));
|
|
1101
|
+
try {
|
|
1102
|
+
while (true) {
|
|
1103
|
+
const { done, value } = await reader.read();
|
|
1104
|
+
if (done) {
|
|
1105
|
+
controller.enqueue(encoder.encode(`event: message_stop\ndata: ${JSON.stringify({ type: "message_stop" })}\n\n`));
|
|
1106
|
+
controller.close();
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1109
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1110
|
+
const lines = buffer.split("\n");
|
|
1111
|
+
buffer = lines.pop() || "";
|
|
1112
|
+
for (const line of lines) if (line.startsWith("data: ")) {
|
|
1113
|
+
const data = line.slice(6).trim();
|
|
1114
|
+
if (data === "[DONE]" || data === "") continue;
|
|
1115
|
+
try {
|
|
1116
|
+
const parsed = JSON.parse(data);
|
|
1117
|
+
const candidate = (parsed.response?.candidates || parsed.candidates)?.[0];
|
|
1118
|
+
const parts = candidate?.content?.parts || [];
|
|
1119
|
+
const usage = parsed.response?.usageMetadata || parsed.usageMetadata;
|
|
1120
|
+
if (usage) {
|
|
1121
|
+
inputTokens = usage.promptTokenCount || inputTokens;
|
|
1122
|
+
outputTokens = usage.candidatesTokenCount || outputTokens;
|
|
1123
|
+
}
|
|
1124
|
+
for (const part of parts) {
|
|
1125
|
+
if (part.thought && part.text) {
|
|
1126
|
+
if (!thinkingBlockStarted) {
|
|
1127
|
+
const blockStart = {
|
|
1128
|
+
type: "content_block_start",
|
|
1129
|
+
index: contentBlockIndex,
|
|
1130
|
+
content_block: {
|
|
1131
|
+
type: "thinking",
|
|
1132
|
+
thinking: ""
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
controller.enqueue(encoder.encode(`event: content_block_start\ndata: ${JSON.stringify(blockStart)}\n\n`));
|
|
1136
|
+
thinkingBlockStarted = true;
|
|
1137
|
+
}
|
|
1138
|
+
const thinkingDelta = {
|
|
1139
|
+
type: "content_block_delta",
|
|
1140
|
+
index: contentBlockIndex,
|
|
1141
|
+
delta: {
|
|
1142
|
+
type: "thinking_delta",
|
|
1143
|
+
thinking: part.text
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
controller.enqueue(encoder.encode(`event: content_block_delta\ndata: ${JSON.stringify(thinkingDelta)}\n\n`));
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
if (part.text && !part.thought) {
|
|
1150
|
+
if (thinkingBlockStarted && !textBlockStarted) {
|
|
1151
|
+
const blockStop = {
|
|
1152
|
+
type: "content_block_stop",
|
|
1153
|
+
index: contentBlockIndex
|
|
1154
|
+
};
|
|
1155
|
+
controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(blockStop)}\n\n`));
|
|
1156
|
+
contentBlockIndex++;
|
|
1157
|
+
}
|
|
1158
|
+
if (!textBlockStarted) {
|
|
1159
|
+
const blockStart = {
|
|
1160
|
+
type: "content_block_start",
|
|
1161
|
+
index: contentBlockIndex,
|
|
1162
|
+
content_block: {
|
|
1163
|
+
type: "text",
|
|
1164
|
+
text: ""
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
controller.enqueue(encoder.encode(`event: content_block_start\ndata: ${JSON.stringify(blockStart)}\n\n`));
|
|
1168
|
+
textBlockStarted = true;
|
|
1169
|
+
}
|
|
1170
|
+
const textDelta = {
|
|
1171
|
+
type: "content_block_delta",
|
|
1172
|
+
index: contentBlockIndex,
|
|
1173
|
+
delta: {
|
|
1174
|
+
type: "text_delta",
|
|
1175
|
+
text: part.text
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
controller.enqueue(encoder.encode(`event: content_block_delta\ndata: ${JSON.stringify(textDelta)}\n\n`));
|
|
1179
|
+
}
|
|
1180
|
+
if (part.functionCall) {
|
|
1181
|
+
if (textBlockStarted || thinkingBlockStarted) {
|
|
1182
|
+
const blockStop = {
|
|
1183
|
+
type: "content_block_stop",
|
|
1184
|
+
index: contentBlockIndex
|
|
1185
|
+
};
|
|
1186
|
+
controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(blockStop)}\n\n`));
|
|
1187
|
+
contentBlockIndex++;
|
|
1188
|
+
textBlockStarted = false;
|
|
1189
|
+
thinkingBlockStarted = false;
|
|
1190
|
+
}
|
|
1191
|
+
const toolBlockStart = {
|
|
1192
|
+
type: "content_block_start",
|
|
1193
|
+
index: contentBlockIndex,
|
|
1194
|
+
content_block: {
|
|
1195
|
+
type: "tool_use",
|
|
1196
|
+
id: `toolu_${Date.now()}`,
|
|
1197
|
+
name: part.functionCall.name,
|
|
1198
|
+
input: {}
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
controller.enqueue(encoder.encode(`event: content_block_start\ndata: ${JSON.stringify(toolBlockStart)}\n\n`));
|
|
1202
|
+
const toolDelta = {
|
|
1203
|
+
type: "content_block_delta",
|
|
1204
|
+
index: contentBlockIndex,
|
|
1205
|
+
delta: {
|
|
1206
|
+
type: "input_json_delta",
|
|
1207
|
+
partial_json: JSON.stringify(part.functionCall.args)
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
controller.enqueue(encoder.encode(`event: content_block_delta\ndata: ${JSON.stringify(toolDelta)}\n\n`));
|
|
1211
|
+
const toolBlockStop = {
|
|
1212
|
+
type: "content_block_stop",
|
|
1213
|
+
index: contentBlockIndex
|
|
1214
|
+
};
|
|
1215
|
+
controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(toolBlockStop)}\n\n`));
|
|
1216
|
+
contentBlockIndex++;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
if (candidate?.finishReason === "STOP") {
|
|
1220
|
+
if (textBlockStarted || thinkingBlockStarted) {
|
|
1221
|
+
const blockStop = {
|
|
1222
|
+
type: "content_block_stop",
|
|
1223
|
+
index: contentBlockIndex
|
|
1224
|
+
};
|
|
1225
|
+
controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(blockStop)}\n\n`));
|
|
1226
|
+
}
|
|
1227
|
+
const messageDelta = {
|
|
1228
|
+
type: "message_delta",
|
|
1229
|
+
delta: {
|
|
1230
|
+
stop_reason: "end_turn",
|
|
1231
|
+
stop_sequence: null
|
|
1232
|
+
},
|
|
1233
|
+
usage: { output_tokens: outputTokens }
|
|
1234
|
+
};
|
|
1235
|
+
controller.enqueue(encoder.encode(`event: message_delta\ndata: ${JSON.stringify(messageDelta)}\n\n`));
|
|
1236
|
+
}
|
|
1237
|
+
} catch {}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
consola.error("Stream transform error:", error);
|
|
1242
|
+
controller.error(error);
|
|
1243
|
+
}
|
|
1244
|
+
} });
|
|
1245
|
+
return new Response(stream, { headers: {
|
|
1246
|
+
"Content-Type": "text/event-stream",
|
|
1247
|
+
"Cache-Control": "no-cache",
|
|
1248
|
+
Connection: "keep-alive"
|
|
1249
|
+
} });
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Transform Antigravity non-stream response to Anthropic format
|
|
1253
|
+
*/
|
|
1254
|
+
async function transformNonStreamToAnthropic(response, model) {
|
|
1255
|
+
const data = await response.json();
|
|
1256
|
+
const candidate = data.candidates?.[0];
|
|
1257
|
+
const parts = candidate?.content?.parts || [];
|
|
1258
|
+
const content = [];
|
|
1259
|
+
for (const part of parts) {
|
|
1260
|
+
if (part.thought && part.text) content.push({
|
|
1261
|
+
type: "thinking",
|
|
1262
|
+
thinking: part.text
|
|
1263
|
+
});
|
|
1264
|
+
else if (part.text) content.push({
|
|
1265
|
+
type: "text",
|
|
1266
|
+
text: part.text
|
|
1267
|
+
});
|
|
1268
|
+
if (part.functionCall) content.push({
|
|
1269
|
+
type: "tool_use",
|
|
1270
|
+
id: `toolu_${Date.now()}`,
|
|
1271
|
+
name: part.functionCall.name,
|
|
1272
|
+
input: part.functionCall.args
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
const anthropicResponse = {
|
|
1276
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
1277
|
+
type: "message",
|
|
1278
|
+
role: "assistant",
|
|
1279
|
+
content,
|
|
1280
|
+
model,
|
|
1281
|
+
stop_reason: candidate?.finishReason === "STOP" ? "end_turn" : "end_turn",
|
|
1282
|
+
stop_sequence: null,
|
|
1283
|
+
usage: {
|
|
1284
|
+
input_tokens: data.usageMetadata?.promptTokenCount || 0,
|
|
1285
|
+
output_tokens: data.usageMetadata?.candidatesTokenCount || 0
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
return new Response(JSON.stringify(anthropicResponse), { headers: { "Content-Type": "application/json" } });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
//#endregion
|
|
1292
|
+
//#region src/routes/antigravity/messages/route.ts
|
|
1293
|
+
const antigravityMessagesRoute = new Hono();
|
|
1294
|
+
antigravityMessagesRoute.post("/", async (c) => {
|
|
1295
|
+
if (!state.antigravityMode) return c.json({ error: "Antigravity mode is not enabled. Start with --antigravity flag." }, 400);
|
|
1296
|
+
try {
|
|
1297
|
+
const body = await c.req.json();
|
|
1298
|
+
consola.debug("Antigravity message request:", body.model);
|
|
1299
|
+
const response = await createAntigravityMessages(body);
|
|
1300
|
+
if (body.stream) {
|
|
1301
|
+
const headers = new Headers();
|
|
1302
|
+
headers.set("Content-Type", "text/event-stream");
|
|
1303
|
+
headers.set("Cache-Control", "no-cache");
|
|
1304
|
+
headers.set("Connection", "keep-alive");
|
|
1305
|
+
return new Response(response.body, {
|
|
1306
|
+
status: response.status,
|
|
1307
|
+
headers
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
const data = await response.json();
|
|
1311
|
+
return c.json(data);
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
consola.error("Antigravity message error:", error);
|
|
1314
|
+
return c.json({
|
|
1315
|
+
type: "error",
|
|
1316
|
+
error: {
|
|
1317
|
+
type: "antigravity_error",
|
|
1318
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
1319
|
+
}
|
|
1320
|
+
}, 500);
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
//#endregion
|
|
1325
|
+
//#region src/routes/antigravity/models/route.ts
|
|
1326
|
+
const app = new Hono();
|
|
1327
|
+
app.get("/", async (c) => {
|
|
1328
|
+
const models = await getAntigravityModels();
|
|
1329
|
+
return c.json(models);
|
|
1330
|
+
});
|
|
1331
|
+
const antigravityModelsRoute = app;
|
|
1332
|
+
|
|
379
1333
|
//#endregion
|
|
380
1334
|
//#region src/lib/approval.ts
|
|
381
1335
|
const awaitApproval = async () => {
|
|
@@ -1312,7 +2266,7 @@ zenModelRoutes.get("/", async (c) => {
|
|
|
1312
2266
|
if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
|
|
1313
2267
|
try {
|
|
1314
2268
|
if (state.zenModels) return c.json(state.zenModels);
|
|
1315
|
-
const { getZenModels } = await import("./get-models-
|
|
2269
|
+
const { getZenModels } = await import("./get-models-DJfPj_Rg.js");
|
|
1316
2270
|
const models = await getZenModels();
|
|
1317
2271
|
state.zenModels = models;
|
|
1318
2272
|
return c.json(models);
|
|
@@ -1332,33 +2286,41 @@ server.use(logger());
|
|
|
1332
2286
|
server.use(cors());
|
|
1333
2287
|
server.use(apiKeyAuthMiddleware);
|
|
1334
2288
|
server.get("/", (c) => c.text("Server running"));
|
|
1335
|
-
server.route("/chat/completions", new Hono().all("/*", async (c,
|
|
2289
|
+
server.route("/chat/completions", new Hono().all("/*", async (c, _next) => {
|
|
1336
2290
|
if (state.zenMode) return zenCompletionRoutes.fetch(c.req.raw, c.env);
|
|
2291
|
+
if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(c.req.raw, c.env);
|
|
1337
2292
|
return completionRoutes.fetch(c.req.raw, c.env);
|
|
1338
2293
|
}));
|
|
1339
|
-
server.route("/models", new Hono().all("/*", async (c,
|
|
2294
|
+
server.route("/models", new Hono().all("/*", async (c, _next) => {
|
|
1340
2295
|
if (state.zenMode) return zenModelRoutes.fetch(c.req.raw, c.env);
|
|
2296
|
+
if (state.antigravityMode) return antigravityModelsRoute.fetch(c.req.raw, c.env);
|
|
1341
2297
|
return modelRoutes.fetch(c.req.raw, c.env);
|
|
1342
2298
|
}));
|
|
1343
2299
|
server.route("/embeddings", embeddingRoutes);
|
|
1344
2300
|
server.route("/usage", usageRoute);
|
|
1345
2301
|
server.route("/token", tokenRoute);
|
|
1346
|
-
server.route("/v1/chat/completions", new Hono().all("/*", async (c,
|
|
2302
|
+
server.route("/v1/chat/completions", new Hono().all("/*", async (c, _next) => {
|
|
1347
2303
|
if (state.zenMode) return zenCompletionRoutes.fetch(c.req.raw, c.env);
|
|
2304
|
+
if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(c.req.raw, c.env);
|
|
1348
2305
|
return completionRoutes.fetch(c.req.raw, c.env);
|
|
1349
2306
|
}));
|
|
1350
|
-
server.route("/v1/models", new Hono().all("/*", async (c,
|
|
2307
|
+
server.route("/v1/models", new Hono().all("/*", async (c, _next) => {
|
|
1351
2308
|
if (state.zenMode) return zenModelRoutes.fetch(c.req.raw, c.env);
|
|
2309
|
+
if (state.antigravityMode) return antigravityModelsRoute.fetch(c.req.raw, c.env);
|
|
1352
2310
|
return modelRoutes.fetch(c.req.raw, c.env);
|
|
1353
2311
|
}));
|
|
1354
2312
|
server.route("/v1/embeddings", embeddingRoutes);
|
|
1355
|
-
server.route("/v1/messages", new Hono().all("/*", async (c,
|
|
2313
|
+
server.route("/v1/messages", new Hono().all("/*", async (c, _next) => {
|
|
1356
2314
|
if (state.zenMode) return zenMessageRoutes.fetch(c.req.raw, c.env);
|
|
2315
|
+
if (state.antigravityMode) return antigravityMessagesRoute.fetch(c.req.raw, c.env);
|
|
1357
2316
|
return messageRoutes.fetch(c.req.raw, c.env);
|
|
1358
2317
|
}));
|
|
1359
2318
|
server.route("/zen/v1/chat/completions", zenCompletionRoutes);
|
|
1360
2319
|
server.route("/zen/v1/models", zenModelRoutes);
|
|
1361
2320
|
server.route("/zen/v1/messages", zenMessageRoutes);
|
|
2321
|
+
server.route("/antigravity/v1/chat/completions", antigravityChatCompletionsRoute);
|
|
2322
|
+
server.route("/antigravity/v1/models", antigravityModelsRoute);
|
|
2323
|
+
server.route("/antigravity/v1/messages", antigravityMessagesRoute);
|
|
1362
2324
|
|
|
1363
2325
|
//#endregion
|
|
1364
2326
|
//#region src/start.ts
|
|
@@ -1386,7 +2348,9 @@ server.route("/zen/v1/messages", zenMessageRoutes);
|
|
|
1386
2348
|
* - zenApiKey: OpenCode Zen API key (optional; if omitted will prompt for setup)
|
|
1387
2349
|
*/
|
|
1388
2350
|
async function runServer(options) {
|
|
2351
|
+
const savedProxyApplied = await applyProxyConfig();
|
|
1389
2352
|
if (options.proxyEnv) initProxyFromEnv();
|
|
2353
|
+
else if (savedProxyApplied) initProxyFromEnv();
|
|
1390
2354
|
if (options.verbose) {
|
|
1391
2355
|
consola.level = 5;
|
|
1392
2356
|
consola.info("Verbose logging enabled");
|
|
@@ -1407,23 +2371,40 @@ async function runServer(options) {
|
|
|
1407
2371
|
state.zenApiKey = options.zenApiKey;
|
|
1408
2372
|
consola.info("Using provided Zen API key");
|
|
1409
2373
|
} else {
|
|
1410
|
-
const { setupZenApiKey, loadZenAuth } = await import("./auth-
|
|
2374
|
+
const { setupZenApiKey, loadZenAuth } = await import("./auth-BO-mwvoU.js");
|
|
1411
2375
|
const existingAuth = await loadZenAuth();
|
|
1412
2376
|
if (existingAuth) {
|
|
1413
2377
|
state.zenApiKey = existingAuth.apiKey;
|
|
1414
2378
|
consola.info("Using existing Zen API key");
|
|
1415
2379
|
} else state.zenApiKey = await setupZenApiKey();
|
|
1416
2380
|
}
|
|
1417
|
-
const { cacheZenModels } = await import("./get-models-
|
|
2381
|
+
const { cacheZenModels } = await import("./get-models-DJfPj_Rg.js");
|
|
1418
2382
|
await cacheZenModels();
|
|
1419
2383
|
consola.info(`Available Zen models: \n${state.zenModels?.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
2384
|
+
} else if (options.antigravity) {
|
|
2385
|
+
consola.info("Google Antigravity mode enabled");
|
|
2386
|
+
state.antigravityMode = true;
|
|
2387
|
+
const { loadAntigravityAuth, setupAntigravity, getCurrentAccount } = await import("./auth-D2wtETTq.js");
|
|
2388
|
+
const existingAuth = await loadAntigravityAuth();
|
|
2389
|
+
if (!existingAuth || existingAuth.accounts.length === 0) {
|
|
2390
|
+
consola.warn("No Antigravity accounts found");
|
|
2391
|
+
await setupAntigravity();
|
|
2392
|
+
} else {
|
|
2393
|
+
const enabledCount = existingAuth.accounts.filter((a) => a.enable).length;
|
|
2394
|
+
consola.info(`Found ${existingAuth.accounts.length} Antigravity accounts (${enabledCount} enabled)`);
|
|
2395
|
+
}
|
|
2396
|
+
if (!await getCurrentAccount()) throw new Error("No enabled Antigravity accounts available");
|
|
2397
|
+
const { getAntigravityModels: getAntigravityModels$1 } = await import("./get-models-Dq2ZDU9m.js");
|
|
2398
|
+
const models = await getAntigravityModels$1();
|
|
2399
|
+
state.antigravityModels = models;
|
|
2400
|
+
consola.info(`Available Antigravity models: \n${models.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
1420
2401
|
} else {
|
|
1421
2402
|
await cacheVSCodeVersion();
|
|
1422
2403
|
if (options.githubToken) {
|
|
1423
2404
|
state.githubToken = options.githubToken;
|
|
1424
2405
|
consola.info("Using provided GitHub token");
|
|
1425
2406
|
try {
|
|
1426
|
-
const { getGitHubUser } = await import("./get-user-
|
|
2407
|
+
const { getGitHubUser } = await import("./get-user-hpkh0FzZ.js");
|
|
1427
2408
|
const user = await getGitHubUser();
|
|
1428
2409
|
consola.info(`Logged in as ${user.login}`);
|
|
1429
2410
|
} catch (error) {
|
|
@@ -1437,7 +2418,7 @@ async function runServer(options) {
|
|
|
1437
2418
|
const { HTTPError: HTTPError$1 } = await import("./error-CsShqJjE.js");
|
|
1438
2419
|
if (error instanceof HTTPError$1 && error.response.status === 401) {
|
|
1439
2420
|
consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
|
|
1440
|
-
const { clearGithubToken: clearGithubToken$1 } = await import("./token-
|
|
2421
|
+
const { clearGithubToken: clearGithubToken$1 } = await import("./token-DS09XjQ5.js");
|
|
1441
2422
|
await clearGithubToken$1();
|
|
1442
2423
|
consola.info("Please restart to re-authenticate");
|
|
1443
2424
|
}
|
|
@@ -1448,7 +2429,7 @@ async function runServer(options) {
|
|
|
1448
2429
|
}
|
|
1449
2430
|
const serverUrl = `http://localhost:${options.port}`;
|
|
1450
2431
|
if (options.claudeCode) {
|
|
1451
|
-
const models = state.zenMode ? state.zenModels : state.models;
|
|
2432
|
+
const models = state.zenMode ? state.zenModels : state.antigravityMode ? state.antigravityModels : state.models;
|
|
1452
2433
|
invariant(models, "Models should be loaded by now");
|
|
1453
2434
|
const selectedModel = await consola.prompt("Select a model to use with Claude Code", {
|
|
1454
2435
|
type: "select",
|
|
@@ -1556,6 +2537,11 @@ const start = defineCommand({
|
|
|
1556
2537
|
"zen-api-key": {
|
|
1557
2538
|
type: "string",
|
|
1558
2539
|
description: "OpenCode Zen API key (get from https://opencode.ai/zen)"
|
|
2540
|
+
},
|
|
2541
|
+
antigravity: {
|
|
2542
|
+
type: "boolean",
|
|
2543
|
+
default: false,
|
|
2544
|
+
description: "Enable Google Antigravity mode (proxy to Antigravity instead of GitHub Copilot)"
|
|
1559
2545
|
}
|
|
1560
2546
|
},
|
|
1561
2547
|
run({ args }) {
|
|
@@ -1577,7 +2563,8 @@ const start = defineCommand({
|
|
|
1577
2563
|
proxyEnv: args["proxy-env"],
|
|
1578
2564
|
apiKeys,
|
|
1579
2565
|
zen: args.zen,
|
|
1580
|
-
zenApiKey: args["zen-api-key"]
|
|
2566
|
+
zenApiKey: args["zen-api-key"],
|
|
2567
|
+
antigravity: args.antigravity
|
|
1581
2568
|
});
|
|
1582
2569
|
}
|
|
1583
2570
|
});
|
|
@@ -1594,7 +2581,8 @@ const main = defineCommand({
|
|
|
1594
2581
|
start,
|
|
1595
2582
|
"check-usage": checkUsage,
|
|
1596
2583
|
debug,
|
|
1597
|
-
logout
|
|
2584
|
+
logout,
|
|
2585
|
+
proxy
|
|
1598
2586
|
}
|
|
1599
2587
|
});
|
|
1600
2588
|
await runMain(main);
|