copilot-api-plus 1.0.57 → 1.0.59
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.en.md +3 -311
- package/README.md +3 -249
- package/dist/error-BfWAfRit.js +2 -0
- package/dist/{error-CvvAyU1E.js → error-u36czEGD.js} +1 -1
- package/dist/{error-CvvAyU1E.js.map → error-u36czEGD.js.map} +1 -1
- package/dist/{get-user-DOv07Myc.js → get-user-BxebJZB6.js} +11 -4
- package/dist/get-user-BxebJZB6.js.map +1 -0
- package/dist/get-user-D2HfoqjP.js +3 -0
- package/dist/main.js +658 -2899
- package/dist/main.js.map +1 -1
- package/dist/token-DT9ko4dY.js +4 -0
- package/dist/{token-D8U-wBLK.js → token-DmGYG1Z1.js} +26 -6
- package/dist/token-DmGYG1Z1.js.map +1 -0
- package/package.json +2 -4
- package/dist/auth-BfBWPtWx.js +0 -3
- package/dist/auth-C7a3n_4O.js +0 -536
- package/dist/auth-C7a3n_4O.js.map +0 -1
- package/dist/auth-DIDShcrD.js +0 -82
- package/dist/auth-DIDShcrD.js.map +0 -1
- package/dist/auth-g7psLP1B.js +0 -3
- package/dist/error-Djpro28X.js +0 -2
- package/dist/get-models-DIOdRXYx.js +0 -4
- package/dist/get-models-DmIjteNk.js +0 -205
- package/dist/get-models-DmIjteNk.js.map +0 -1
- package/dist/get-models-onnSXkNI.js +0 -37
- package/dist/get-models-onnSXkNI.js.map +0 -1
- package/dist/get-user-CGhBmkXO.js +0 -3
- package/dist/get-user-DOv07Myc.js.map +0 -1
- package/dist/paths-BdbyVdad.js +0 -26
- package/dist/paths-BdbyVdad.js.map +0 -1
- package/dist/state-CRpaW-qc.js +0 -13
- package/dist/state-CRpaW-qc.js.map +0 -1
- package/dist/token-CoKq3Guw.js +0 -5
- package/dist/token-D8U-wBLK.js.map +0 -1
package/dist/main.js
CHANGED
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { n as forwardError, t as HTTPError } from "./error-CvvAyU1E.js";
|
|
6
|
-
import { a as cacheModels, c as isNullish, i as setupGitHubToken, l as sleep, n as refreshCopilotToken, o as cacheVSCodeVersion, r as setupCopilotToken, s as findModel, t as clearGithubToken } from "./token-D8U-wBLK.js";
|
|
7
|
-
import { C as recordAccountFailure, _ as rotateAccount, f as getValidAccessToken, l as getCurrentAccountIndex, n as clearAntigravityAuth, o as getAntigravityAuthPath, r as disableCurrentAccount, s as getApiKey, u as getCurrentProjectId, w as recordAccountSuccess } from "./auth-C7a3n_4O.js";
|
|
8
|
-
import { n as getZenAuthPath, t as clearZenAuth } from "./auth-DIDShcrD.js";
|
|
9
|
-
import { n as getAntigravityUsage, r as isThinkingModel, t as getAntigravityModels } from "./get-models-DmIjteNk.js";
|
|
2
|
+
import { a as cacheModels, c as isNullish, d as ensurePaths, i as setupGitHubToken, l as sleep, n as refreshCopilotToken, o as cacheVSCodeVersion, r as setupCopilotToken, s as findModel, t as clearGithubToken, u as PATHS } from "./token-DmGYG1Z1.js";
|
|
3
|
+
import { c as githubHeaders, n as GITHUB_API_BASE_URL, o as copilotBaseUrl, s as copilotHeaders, u as state } from "./get-user-BxebJZB6.js";
|
|
4
|
+
import { n as forwardError, t as HTTPError } from "./error-u36czEGD.js";
|
|
10
5
|
import { createRequire } from "node:module";
|
|
11
6
|
import { defineCommand, runMain } from "citty";
|
|
12
7
|
import consola from "consola";
|
|
13
8
|
import fs from "node:fs/promises";
|
|
14
|
-
import path from "node:path";
|
|
15
9
|
import os from "node:os";
|
|
16
|
-
import
|
|
17
|
-
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
|
10
|
+
import path from "node:path";
|
|
18
11
|
import * as p from "@clack/prompts";
|
|
19
12
|
import clipboard from "clipboardy";
|
|
20
13
|
import { serve } from "srvx";
|
|
21
14
|
import invariant from "tiny-invariant";
|
|
15
|
+
import { getProxyForUrl } from "proxy-from-env";
|
|
16
|
+
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
|
22
17
|
import { execSync } from "node:child_process";
|
|
23
18
|
import process$1 from "node:process";
|
|
24
19
|
import { Hono } from "hono";
|
|
@@ -30,376 +25,84 @@ import { events } from "fetch-event-stream";
|
|
|
30
25
|
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
31
26
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
32
27
|
//#endregion
|
|
33
|
-
//#region
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"description": "Loads environment variables from .env file",
|
|
39
|
-
"main": "lib/main.js",
|
|
40
|
-
"types": "lib/main.d.ts",
|
|
41
|
-
"exports": {
|
|
42
|
-
".": {
|
|
43
|
-
"types": "./lib/main.d.ts",
|
|
44
|
-
"require": "./lib/main.js",
|
|
45
|
-
"default": "./lib/main.js"
|
|
46
|
-
},
|
|
47
|
-
"./config": "./config.js",
|
|
48
|
-
"./config.js": "./config.js",
|
|
49
|
-
"./lib/env-options": "./lib/env-options.js",
|
|
50
|
-
"./lib/env-options.js": "./lib/env-options.js",
|
|
51
|
-
"./lib/cli-options": "./lib/cli-options.js",
|
|
52
|
-
"./lib/cli-options.js": "./lib/cli-options.js",
|
|
53
|
-
"./package.json": "./package.json"
|
|
54
|
-
},
|
|
55
|
-
"scripts": {
|
|
56
|
-
"dts-check": "tsc --project tests/types/tsconfig.json",
|
|
57
|
-
"lint": "standard",
|
|
58
|
-
"pretest": "npm run lint && npm run dts-check",
|
|
59
|
-
"test": "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
60
|
-
"test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
|
|
61
|
-
"prerelease": "npm test",
|
|
62
|
-
"release": "standard-version"
|
|
63
|
-
},
|
|
64
|
-
"repository": {
|
|
65
|
-
"type": "git",
|
|
66
|
-
"url": "git://github.com/motdotla/dotenv.git"
|
|
67
|
-
},
|
|
68
|
-
"homepage": "https://github.com/motdotla/dotenv#readme",
|
|
69
|
-
"funding": "https://dotenvx.com",
|
|
70
|
-
"keywords": [
|
|
71
|
-
"dotenv",
|
|
72
|
-
"env",
|
|
73
|
-
".env",
|
|
74
|
-
"environment",
|
|
75
|
-
"variables",
|
|
76
|
-
"config",
|
|
77
|
-
"settings"
|
|
78
|
-
],
|
|
79
|
-
"readmeFilename": "README.md",
|
|
80
|
-
"license": "BSD-2-Clause",
|
|
81
|
-
"devDependencies": {
|
|
82
|
-
"@types/node": "^18.11.3",
|
|
83
|
-
"decache": "^4.6.2",
|
|
84
|
-
"sinon": "^14.0.1",
|
|
85
|
-
"standard": "^17.0.0",
|
|
86
|
-
"standard-version": "^9.5.0",
|
|
87
|
-
"tap": "^19.2.0",
|
|
88
|
-
"typescript": "^4.8.4"
|
|
89
|
-
},
|
|
90
|
-
"engines": { "node": ">=12" },
|
|
91
|
-
"browser": { "fs": false }
|
|
92
|
-
};
|
|
93
|
-
}));
|
|
94
|
-
//#endregion
|
|
95
|
-
//#region node_modules/dotenv/lib/main.js
|
|
96
|
-
var require_main = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
97
|
-
const fs$1 = __require("fs");
|
|
98
|
-
const path$1 = __require("path");
|
|
99
|
-
const os$1 = __require("os");
|
|
100
|
-
const crypto$1 = __require("crypto");
|
|
101
|
-
const version = require_package().version;
|
|
102
|
-
const TIPS = [
|
|
103
|
-
"🔐 encrypt with Dotenvx: https://dotenvx.com",
|
|
104
|
-
"🔐 prevent committing .env to code: https://dotenvx.com/precommit",
|
|
105
|
-
"🔐 prevent building .env in docker: https://dotenvx.com/prebuild",
|
|
106
|
-
"📡 add observability to secrets: https://dotenvx.com/ops",
|
|
107
|
-
"👥 sync secrets across teammates & machines: https://dotenvx.com/ops",
|
|
108
|
-
"🗂️ backup and recover secrets: https://dotenvx.com/ops",
|
|
109
|
-
"✅ audit secrets and track compliance: https://dotenvx.com/ops",
|
|
110
|
-
"🔄 add secrets lifecycle management: https://dotenvx.com/ops",
|
|
111
|
-
"🔑 add access controls to secrets: https://dotenvx.com/ops",
|
|
112
|
-
"🛠️ run anywhere with `dotenvx run -- yourcommand`",
|
|
113
|
-
"⚙️ specify custom .env file path with { path: '/custom/path/.env' }",
|
|
114
|
-
"⚙️ enable debug logging with { debug: true }",
|
|
115
|
-
"⚙️ override existing env vars with { override: true }",
|
|
116
|
-
"⚙️ suppress all logs with { quiet: true }",
|
|
117
|
-
"⚙️ write to custom object with { processEnv: myObject }",
|
|
118
|
-
"⚙️ load multiple .env files with { path: ['.env.local', '.env'] }"
|
|
119
|
-
];
|
|
120
|
-
function _getRandomTip() {
|
|
121
|
-
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
122
|
-
}
|
|
123
|
-
function parseBoolean(value) {
|
|
124
|
-
if (typeof value === "string") return ![
|
|
125
|
-
"false",
|
|
126
|
-
"0",
|
|
127
|
-
"no",
|
|
128
|
-
"off",
|
|
129
|
-
""
|
|
130
|
-
].includes(value.toLowerCase());
|
|
131
|
-
return Boolean(value);
|
|
132
|
-
}
|
|
133
|
-
function supportsAnsi() {
|
|
134
|
-
return process.stdout.isTTY;
|
|
135
|
-
}
|
|
136
|
-
function dim(text) {
|
|
137
|
-
return supportsAnsi() ? `\x1b[2m${text}\x1b[0m` : text;
|
|
138
|
-
}
|
|
139
|
-
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
|
140
|
-
function parse(src) {
|
|
141
|
-
const obj = {};
|
|
142
|
-
let lines = src.toString();
|
|
143
|
-
lines = lines.replace(/\r\n?/gm, "\n");
|
|
144
|
-
let match;
|
|
145
|
-
while ((match = LINE.exec(lines)) != null) {
|
|
146
|
-
const key = match[1];
|
|
147
|
-
let value = match[2] || "";
|
|
148
|
-
value = value.trim();
|
|
149
|
-
const maybeQuote = value[0];
|
|
150
|
-
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
151
|
-
if (maybeQuote === "\"") {
|
|
152
|
-
value = value.replace(/\\n/g, "\n");
|
|
153
|
-
value = value.replace(/\\r/g, "\r");
|
|
154
|
-
}
|
|
155
|
-
obj[key] = value;
|
|
156
|
-
}
|
|
157
|
-
return obj;
|
|
28
|
+
//#region src/auth.ts
|
|
29
|
+
async function runAuth(options) {
|
|
30
|
+
if (options.verbose) {
|
|
31
|
+
consola.level = 5;
|
|
32
|
+
consola.info("Verbose logging enabled");
|
|
158
33
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
34
|
+
state.showToken = options.showToken;
|
|
35
|
+
await ensurePaths();
|
|
36
|
+
await setupGitHubToken({ force: true });
|
|
37
|
+
consola.success("GitHub token written to", PATHS.GITHUB_TOKEN_PATH);
|
|
38
|
+
}
|
|
39
|
+
const auth = defineCommand({
|
|
40
|
+
meta: {
|
|
41
|
+
name: "auth",
|
|
42
|
+
description: "Run GitHub auth flow without running the server"
|
|
43
|
+
},
|
|
44
|
+
args: {
|
|
45
|
+
verbose: {
|
|
46
|
+
alias: "v",
|
|
47
|
+
type: "boolean",
|
|
48
|
+
default: false,
|
|
49
|
+
description: "Enable verbose logging"
|
|
50
|
+
},
|
|
51
|
+
"show-token": {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
default: false,
|
|
54
|
+
description: "Show GitHub token on auth"
|
|
178
55
|
}
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
console.log(`[dotenv@${version}][DEBUG] ${message}`);
|
|
186
|
-
}
|
|
187
|
-
function _log(message) {
|
|
188
|
-
console.log(`[dotenv@${version}] ${message}`);
|
|
189
|
-
}
|
|
190
|
-
function _dotenvKey(options) {
|
|
191
|
-
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) return options.DOTENV_KEY;
|
|
192
|
-
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) return process.env.DOTENV_KEY;
|
|
193
|
-
return "";
|
|
56
|
+
},
|
|
57
|
+
run({ args }) {
|
|
58
|
+
return runAuth({
|
|
59
|
+
verbose: args.verbose,
|
|
60
|
+
showToken: args["show-token"]
|
|
61
|
+
});
|
|
194
62
|
}
|
|
195
|
-
|
|
196
|
-
|
|
63
|
+
});
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/services/github/get-copilot-usage.ts
|
|
66
|
+
const getCopilotUsage = async () => {
|
|
67
|
+
const response = await fetch(`${GITHUB_API_BASE_URL}/copilot_internal/user`, { headers: githubHeaders(state) });
|
|
68
|
+
if (!response.ok) throw new HTTPError("Failed to get Copilot usage", response);
|
|
69
|
+
return await response.json();
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/check-usage.ts
|
|
73
|
+
const checkUsage = defineCommand({
|
|
74
|
+
meta: {
|
|
75
|
+
name: "check-usage",
|
|
76
|
+
description: "Show current GitHub Copilot usage/quota information"
|
|
77
|
+
},
|
|
78
|
+
async run() {
|
|
79
|
+
await ensurePaths();
|
|
80
|
+
await setupGitHubToken();
|
|
197
81
|
try {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
82
|
+
const usage = await getCopilotUsage();
|
|
83
|
+
const premium = usage.quota_snapshots.premium_interactions;
|
|
84
|
+
const premiumTotal = premium.entitlement;
|
|
85
|
+
const premiumUsed = premiumTotal - premium.remaining;
|
|
86
|
+
const premiumPercentUsed = premiumTotal > 0 ? premiumUsed / premiumTotal * 100 : 0;
|
|
87
|
+
const premiumPercentRemaining = premium.percent_remaining;
|
|
88
|
+
function summarizeQuota(name, snap) {
|
|
89
|
+
if (!snap) return `${name}: N/A`;
|
|
90
|
+
const total = snap.entitlement;
|
|
91
|
+
const used = total - snap.remaining;
|
|
92
|
+
const percentUsed = total > 0 ? used / total * 100 : 0;
|
|
93
|
+
const percentRemaining = snap.percent_remaining;
|
|
94
|
+
return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
|
|
204
95
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
const environment = uri.searchParams.get("environment");
|
|
214
|
-
if (!environment) {
|
|
215
|
-
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing environment part");
|
|
216
|
-
err.code = "INVALID_DOTENV_KEY";
|
|
217
|
-
throw err;
|
|
96
|
+
const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`;
|
|
97
|
+
const chatLine = summarizeQuota("Chat", usage.quota_snapshots.chat);
|
|
98
|
+
const completionsLine = summarizeQuota("Completions", usage.quota_snapshots.completions);
|
|
99
|
+
consola.box(`Copilot Usage (plan: ${usage.copilot_plan})\nQuota resets: ${usage.quota_reset_date}\n\nQuotas:\n ${premiumLine}\n ${chatLine}\n ${completionsLine}`);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
consola.error("Failed to fetch Copilot usage:", err);
|
|
102
|
+
process.exit(1);
|
|
218
103
|
}
|
|
219
|
-
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
220
|
-
const ciphertext = result.parsed[environmentKey];
|
|
221
|
-
if (!ciphertext) {
|
|
222
|
-
const err = /* @__PURE__ */ new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
|
|
223
|
-
err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
|
|
224
|
-
throw err;
|
|
225
|
-
}
|
|
226
|
-
return {
|
|
227
|
-
ciphertext,
|
|
228
|
-
key
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
function _vaultPath(options) {
|
|
232
|
-
let possibleVaultPath = null;
|
|
233
|
-
if (options && options.path && options.path.length > 0) if (Array.isArray(options.path)) {
|
|
234
|
-
for (const filepath of options.path) if (fs$1.existsSync(filepath)) possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
235
|
-
} else possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
236
|
-
else possibleVaultPath = path$1.resolve(process.cwd(), ".env.vault");
|
|
237
|
-
if (fs$1.existsSync(possibleVaultPath)) return possibleVaultPath;
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
function _resolveHome(envPath) {
|
|
241
|
-
return envPath[0] === "~" ? path$1.join(os$1.homedir(), envPath.slice(1)) : envPath;
|
|
242
|
-
}
|
|
243
|
-
function _configVault(options) {
|
|
244
|
-
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
245
|
-
const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
246
|
-
if (debug || !quiet) _log("Loading env from encrypted .env.vault");
|
|
247
|
-
const parsed = DotenvModule._parseVault(options);
|
|
248
|
-
let processEnv = process.env;
|
|
249
|
-
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
250
|
-
DotenvModule.populate(processEnv, parsed, options);
|
|
251
|
-
return { parsed };
|
|
252
|
-
}
|
|
253
|
-
function configDotenv(options) {
|
|
254
|
-
const dotenvPath = path$1.resolve(process.cwd(), ".env");
|
|
255
|
-
let encoding = "utf8";
|
|
256
|
-
let processEnv = process.env;
|
|
257
|
-
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
258
|
-
let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
259
|
-
let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
260
|
-
if (options && options.encoding) encoding = options.encoding;
|
|
261
|
-
else if (debug) _debug("No encoding is specified. UTF-8 is used by default");
|
|
262
|
-
let optionPaths = [dotenvPath];
|
|
263
|
-
if (options && options.path) if (!Array.isArray(options.path)) optionPaths = [_resolveHome(options.path)];
|
|
264
|
-
else {
|
|
265
|
-
optionPaths = [];
|
|
266
|
-
for (const filepath of options.path) optionPaths.push(_resolveHome(filepath));
|
|
267
|
-
}
|
|
268
|
-
let lastError;
|
|
269
|
-
const parsedAll = {};
|
|
270
|
-
for (const path of optionPaths) try {
|
|
271
|
-
const parsed = DotenvModule.parse(fs$1.readFileSync(path, { encoding }));
|
|
272
|
-
DotenvModule.populate(parsedAll, parsed, options);
|
|
273
|
-
} catch (e) {
|
|
274
|
-
if (debug) _debug(`Failed to load ${path} ${e.message}`);
|
|
275
|
-
lastError = e;
|
|
276
|
-
}
|
|
277
|
-
const populated = DotenvModule.populate(processEnv, parsedAll, options);
|
|
278
|
-
debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
|
|
279
|
-
quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
|
|
280
|
-
if (debug || !quiet) {
|
|
281
|
-
const keysCount = Object.keys(populated).length;
|
|
282
|
-
const shortPaths = [];
|
|
283
|
-
for (const filePath of optionPaths) try {
|
|
284
|
-
const relative = path$1.relative(process.cwd(), filePath);
|
|
285
|
-
shortPaths.push(relative);
|
|
286
|
-
} catch (e) {
|
|
287
|
-
if (debug) _debug(`Failed to load ${filePath} ${e.message}`);
|
|
288
|
-
lastError = e;
|
|
289
|
-
}
|
|
290
|
-
_log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
291
|
-
}
|
|
292
|
-
if (lastError) return {
|
|
293
|
-
parsed: parsedAll,
|
|
294
|
-
error: lastError
|
|
295
|
-
};
|
|
296
|
-
else return { parsed: parsedAll };
|
|
297
|
-
}
|
|
298
|
-
function config(options) {
|
|
299
|
-
if (_dotenvKey(options).length === 0) return DotenvModule.configDotenv(options);
|
|
300
|
-
const vaultPath = _vaultPath(options);
|
|
301
|
-
if (!vaultPath) {
|
|
302
|
-
_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
|
|
303
|
-
return DotenvModule.configDotenv(options);
|
|
304
|
-
}
|
|
305
|
-
return DotenvModule._configVault(options);
|
|
306
|
-
}
|
|
307
|
-
function decrypt(encrypted, keyStr) {
|
|
308
|
-
const key = Buffer.from(keyStr.slice(-64), "hex");
|
|
309
|
-
let ciphertext = Buffer.from(encrypted, "base64");
|
|
310
|
-
const nonce = ciphertext.subarray(0, 12);
|
|
311
|
-
const authTag = ciphertext.subarray(-16);
|
|
312
|
-
ciphertext = ciphertext.subarray(12, -16);
|
|
313
|
-
try {
|
|
314
|
-
const aesgcm = crypto$1.createDecipheriv("aes-256-gcm", key, nonce);
|
|
315
|
-
aesgcm.setAuthTag(authTag);
|
|
316
|
-
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
317
|
-
} catch (error) {
|
|
318
|
-
const isRange = error instanceof RangeError;
|
|
319
|
-
const invalidKeyLength = error.message === "Invalid key length";
|
|
320
|
-
const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
|
|
321
|
-
if (isRange || invalidKeyLength) {
|
|
322
|
-
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
|
|
323
|
-
err.code = "INVALID_DOTENV_KEY";
|
|
324
|
-
throw err;
|
|
325
|
-
} else if (decryptionFailed) {
|
|
326
|
-
const err = /* @__PURE__ */ new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
|
|
327
|
-
err.code = "DECRYPTION_FAILED";
|
|
328
|
-
throw err;
|
|
329
|
-
} else throw error;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
function populate(processEnv, parsed, options = {}) {
|
|
333
|
-
const debug = Boolean(options && options.debug);
|
|
334
|
-
const override = Boolean(options && options.override);
|
|
335
|
-
const populated = {};
|
|
336
|
-
if (typeof parsed !== "object") {
|
|
337
|
-
const err = /* @__PURE__ */ new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
|
|
338
|
-
err.code = "OBJECT_REQUIRED";
|
|
339
|
-
throw err;
|
|
340
|
-
}
|
|
341
|
-
for (const key of Object.keys(parsed)) if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
342
|
-
if (override === true) {
|
|
343
|
-
processEnv[key] = parsed[key];
|
|
344
|
-
populated[key] = parsed[key];
|
|
345
|
-
}
|
|
346
|
-
if (debug) if (override === true) _debug(`"${key}" is already defined and WAS overwritten`);
|
|
347
|
-
else _debug(`"${key}" is already defined and was NOT overwritten`);
|
|
348
|
-
} else {
|
|
349
|
-
processEnv[key] = parsed[key];
|
|
350
|
-
populated[key] = parsed[key];
|
|
351
|
-
}
|
|
352
|
-
return populated;
|
|
353
104
|
}
|
|
354
|
-
|
|
355
|
-
configDotenv,
|
|
356
|
-
_configVault,
|
|
357
|
-
_parseVault,
|
|
358
|
-
config,
|
|
359
|
-
decrypt,
|
|
360
|
-
parse,
|
|
361
|
-
populate
|
|
362
|
-
};
|
|
363
|
-
module.exports.configDotenv = DotenvModule.configDotenv;
|
|
364
|
-
module.exports._configVault = DotenvModule._configVault;
|
|
365
|
-
module.exports._parseVault = DotenvModule._parseVault;
|
|
366
|
-
module.exports.config = DotenvModule.config;
|
|
367
|
-
module.exports.decrypt = DotenvModule.decrypt;
|
|
368
|
-
module.exports.parse = DotenvModule.parse;
|
|
369
|
-
module.exports.populate = DotenvModule.populate;
|
|
370
|
-
module.exports = DotenvModule;
|
|
371
|
-
}));
|
|
372
|
-
//#endregion
|
|
373
|
-
//#region node_modules/dotenv/lib/env-options.js
|
|
374
|
-
var require_env_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
375
|
-
const options = {};
|
|
376
|
-
if (process.env.DOTENV_CONFIG_ENCODING != null) options.encoding = process.env.DOTENV_CONFIG_ENCODING;
|
|
377
|
-
if (process.env.DOTENV_CONFIG_PATH != null) options.path = process.env.DOTENV_CONFIG_PATH;
|
|
378
|
-
if (process.env.DOTENV_CONFIG_QUIET != null) options.quiet = process.env.DOTENV_CONFIG_QUIET;
|
|
379
|
-
if (process.env.DOTENV_CONFIG_DEBUG != null) options.debug = process.env.DOTENV_CONFIG_DEBUG;
|
|
380
|
-
if (process.env.DOTENV_CONFIG_OVERRIDE != null) options.override = process.env.DOTENV_CONFIG_OVERRIDE;
|
|
381
|
-
if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY;
|
|
382
|
-
module.exports = options;
|
|
383
|
-
}));
|
|
384
|
-
//#endregion
|
|
385
|
-
//#region node_modules/dotenv/lib/cli-options.js
|
|
386
|
-
var require_cli_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
387
|
-
const re = /^dotenv_config_(encoding|path|quiet|debug|override|DOTENV_KEY)=(.+)$/;
|
|
388
|
-
module.exports = function optionMatcher(args) {
|
|
389
|
-
const options = args.reduce(function(acc, cur) {
|
|
390
|
-
const matches = cur.match(re);
|
|
391
|
-
if (matches) acc[matches[1]] = matches[2];
|
|
392
|
-
return acc;
|
|
393
|
-
}, {});
|
|
394
|
-
if (!("quiet" in options)) options.quiet = "true";
|
|
395
|
-
return options;
|
|
396
|
-
};
|
|
397
|
-
}));
|
|
398
|
-
//#endregion
|
|
399
|
-
//#region node_modules/dotenv/config.js
|
|
400
|
-
(function() {
|
|
401
|
-
require_main().config(Object.assign({}, require_env_options(), require_cli_options()(process.argv)));
|
|
402
|
-
})();
|
|
105
|
+
});
|
|
403
106
|
//#endregion
|
|
404
107
|
//#region src/lib/config.ts
|
|
405
108
|
/**
|
|
@@ -481,296 +184,36 @@ async function applyProxyConfig() {
|
|
|
481
184
|
return true;
|
|
482
185
|
}
|
|
483
186
|
//#endregion
|
|
484
|
-
//#region src/
|
|
485
|
-
function
|
|
486
|
-
if (typeof Bun !== "undefined") return;
|
|
187
|
+
//#region src/debug.ts
|
|
188
|
+
async function getPackageVersion() {
|
|
487
189
|
try {
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
try {
|
|
493
|
-
const origin = typeof options.origin === "string" ? new URL(options.origin) : options.origin;
|
|
494
|
-
const raw = getProxyForUrl(origin.toString());
|
|
495
|
-
const proxyUrl = raw && raw.length > 0 ? raw : void 0;
|
|
496
|
-
if (!proxyUrl) {
|
|
497
|
-
consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
|
|
498
|
-
return direct.dispatch(options, handler);
|
|
499
|
-
}
|
|
500
|
-
let agent = proxies.get(proxyUrl);
|
|
501
|
-
if (!agent) {
|
|
502
|
-
agent = new ProxyAgent(proxyUrl);
|
|
503
|
-
proxies.set(proxyUrl, agent);
|
|
504
|
-
}
|
|
505
|
-
let label = proxyUrl;
|
|
506
|
-
try {
|
|
507
|
-
const u = new URL(proxyUrl);
|
|
508
|
-
label = `${u.protocol}//${u.host}`;
|
|
509
|
-
} catch {}
|
|
510
|
-
consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`);
|
|
511
|
-
return agent.dispatch(options, handler);
|
|
512
|
-
} catch {
|
|
513
|
-
return direct.dispatch(options, handler);
|
|
514
|
-
}
|
|
515
|
-
},
|
|
516
|
-
close() {
|
|
517
|
-
return direct.close();
|
|
518
|
-
},
|
|
519
|
-
destroy() {
|
|
520
|
-
return direct.destroy();
|
|
521
|
-
}
|
|
522
|
-
});
|
|
523
|
-
consola.debug("HTTP proxy configured from environment (per-URL)");
|
|
524
|
-
} catch (err) {
|
|
525
|
-
consola.debug("Proxy setup skipped:", err);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
//#endregion
|
|
529
|
-
//#region src/antigravity.ts
|
|
530
|
-
/**
|
|
531
|
-
* Add a new Antigravity account via OAuth
|
|
532
|
-
*/
|
|
533
|
-
async function addAccount() {
|
|
534
|
-
const { setupAntigravity, loadAntigravityAuth } = await import("./auth-BfBWPtWx.js");
|
|
535
|
-
const existingAuth = await loadAntigravityAuth();
|
|
536
|
-
if (existingAuth && existingAuth.accounts.length > 0) {
|
|
537
|
-
const enabledCount = existingAuth.accounts.filter((a) => a.enable).length;
|
|
538
|
-
consola.info(`Found ${existingAuth.accounts.length} existing accounts (${enabledCount} enabled)`);
|
|
190
|
+
const packageJsonPath = new URL("../package.json", import.meta.url).pathname;
|
|
191
|
+
return JSON.parse(await fs.readFile(packageJsonPath)).version;
|
|
192
|
+
} catch {
|
|
193
|
+
return "unknown";
|
|
539
194
|
}
|
|
540
|
-
await setupAntigravity();
|
|
541
195
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
consola.info(`\nAntigravity Accounts (${auth.accounts.length} total):`);
|
|
553
|
-
consola.info("=".repeat(50));
|
|
554
|
-
for (let i = 0; i < auth.accounts.length; i++) {
|
|
555
|
-
const account = auth.accounts[i];
|
|
556
|
-
const isCurrent = i === auth.currentIndex;
|
|
557
|
-
const status = account.enable ? "enabled" : "disabled";
|
|
558
|
-
const marker = isCurrent ? "→ " : " ";
|
|
559
|
-
const projectId = account.project_id || "unknown";
|
|
560
|
-
consola.info(`${marker}[${i}] Project: ${projectId} | Status: ${status}${isCurrent ? " (current)" : ""}`);
|
|
561
|
-
}
|
|
196
|
+
function getRuntimeInfo() {
|
|
197
|
+
const isBun = typeof Bun !== "undefined";
|
|
198
|
+
return {
|
|
199
|
+
name: isBun ? "bun" : "node",
|
|
200
|
+
version: isBun ? Bun.version : process.version.slice(1),
|
|
201
|
+
platform: os.platform(),
|
|
202
|
+
arch: os.arch()
|
|
203
|
+
};
|
|
562
204
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
if (!auth || auth.accounts.length === 0) {
|
|
570
|
-
consola.error("No Antigravity accounts configured");
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
if (index < 0 || index >= auth.accounts.length) {
|
|
574
|
-
consola.error(`Invalid index. Must be between 0 and ${auth.accounts.length - 1}`);
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
const removed = auth.accounts.splice(index, 1)[0];
|
|
578
|
-
if (auth.currentIndex >= auth.accounts.length) auth.currentIndex = Math.max(0, auth.accounts.length - 1);
|
|
579
|
-
await saveAntigravityAuth(auth);
|
|
580
|
-
consola.success(`Removed account ${index} (Project: ${removed.project_id || "unknown"})`);
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Clear all Antigravity accounts
|
|
584
|
-
*/
|
|
585
|
-
async function clearAccounts() {
|
|
586
|
-
const { clearAntigravityAuth } = await import("./auth-BfBWPtWx.js");
|
|
587
|
-
if (await consola.prompt("Are you sure you want to remove all Antigravity accounts?", {
|
|
588
|
-
type: "confirm",
|
|
589
|
-
initial: false
|
|
590
|
-
})) await clearAntigravityAuth();
|
|
591
|
-
}
|
|
592
|
-
const antigravity = defineCommand({
|
|
593
|
-
meta: {
|
|
594
|
-
name: "antigravity",
|
|
595
|
-
description: "Manage Google Antigravity accounts"
|
|
596
|
-
},
|
|
597
|
-
subCommands: {
|
|
598
|
-
add: defineCommand({
|
|
599
|
-
meta: {
|
|
600
|
-
name: "add",
|
|
601
|
-
description: "Add a new Antigravity account via OAuth"
|
|
602
|
-
},
|
|
603
|
-
async run() {
|
|
604
|
-
await ensurePaths();
|
|
605
|
-
if (await applyProxyConfig()) initProxyFromEnv();
|
|
606
|
-
await addAccount();
|
|
607
|
-
}
|
|
608
|
-
}),
|
|
609
|
-
list: defineCommand({
|
|
610
|
-
meta: {
|
|
611
|
-
name: "list",
|
|
612
|
-
description: "List all Antigravity accounts"
|
|
613
|
-
},
|
|
614
|
-
async run() {
|
|
615
|
-
await ensurePaths();
|
|
616
|
-
await listAccounts();
|
|
617
|
-
}
|
|
618
|
-
}),
|
|
619
|
-
remove: defineCommand({
|
|
620
|
-
meta: {
|
|
621
|
-
name: "remove",
|
|
622
|
-
description: "Remove an Antigravity account by index"
|
|
623
|
-
},
|
|
624
|
-
args: { index: {
|
|
625
|
-
type: "positional",
|
|
626
|
-
description: "Account index to remove (use 'list' to see indexes)",
|
|
627
|
-
required: true
|
|
628
|
-
} },
|
|
629
|
-
async run({ args }) {
|
|
630
|
-
await ensurePaths();
|
|
631
|
-
const indexStr = String(args.index);
|
|
632
|
-
const index = Number.parseInt(indexStr, 10);
|
|
633
|
-
if (Number.isNaN(index)) {
|
|
634
|
-
consola.error("Index must be a number");
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
await removeAccount(index);
|
|
638
|
-
}
|
|
639
|
-
}),
|
|
640
|
-
clear: defineCommand({
|
|
641
|
-
meta: {
|
|
642
|
-
name: "clear",
|
|
643
|
-
description: "Remove all Antigravity accounts"
|
|
644
|
-
},
|
|
645
|
-
async run() {
|
|
646
|
-
await ensurePaths();
|
|
647
|
-
await clearAccounts();
|
|
648
|
-
}
|
|
649
|
-
})
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
//#endregion
|
|
653
|
-
//#region src/auth.ts
|
|
654
|
-
async function runAuth(options) {
|
|
655
|
-
if (options.verbose) {
|
|
656
|
-
consola.level = 5;
|
|
657
|
-
consola.info("Verbose logging enabled");
|
|
658
|
-
}
|
|
659
|
-
state.showToken = options.showToken;
|
|
660
|
-
await ensurePaths();
|
|
661
|
-
await setupGitHubToken({ force: true });
|
|
662
|
-
consola.success("GitHub token written to", PATHS.GITHUB_TOKEN_PATH);
|
|
663
|
-
}
|
|
664
|
-
const auth = defineCommand({
|
|
665
|
-
meta: {
|
|
666
|
-
name: "auth",
|
|
667
|
-
description: "Run GitHub auth flow without running the server"
|
|
668
|
-
},
|
|
669
|
-
args: {
|
|
670
|
-
verbose: {
|
|
671
|
-
alias: "v",
|
|
672
|
-
type: "boolean",
|
|
673
|
-
default: false,
|
|
674
|
-
description: "Enable verbose logging"
|
|
675
|
-
},
|
|
676
|
-
"show-token": {
|
|
677
|
-
type: "boolean",
|
|
678
|
-
default: false,
|
|
679
|
-
description: "Show GitHub token on auth"
|
|
680
|
-
}
|
|
681
|
-
},
|
|
682
|
-
run({ args }) {
|
|
683
|
-
return runAuth({
|
|
684
|
-
verbose: args.verbose,
|
|
685
|
-
showToken: args["show-token"]
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
});
|
|
689
|
-
//#endregion
|
|
690
|
-
//#region src/services/github/get-copilot-usage.ts
|
|
691
|
-
const getCopilotUsage = async () => {
|
|
692
|
-
const response = await fetch(`${GITHUB_API_BASE_URL}/copilot_internal/user`, { headers: githubHeaders(state) });
|
|
693
|
-
if (!response.ok) throw new HTTPError("Failed to get Copilot usage", response);
|
|
694
|
-
return await response.json();
|
|
695
|
-
};
|
|
696
|
-
//#endregion
|
|
697
|
-
//#region src/check-usage.ts
|
|
698
|
-
const checkUsage = defineCommand({
|
|
699
|
-
meta: {
|
|
700
|
-
name: "check-usage",
|
|
701
|
-
description: "Show current GitHub Copilot usage/quota information"
|
|
702
|
-
},
|
|
703
|
-
async run() {
|
|
704
|
-
await ensurePaths();
|
|
705
|
-
await setupGitHubToken();
|
|
706
|
-
try {
|
|
707
|
-
const usage = await getCopilotUsage();
|
|
708
|
-
const premium = usage.quota_snapshots.premium_interactions;
|
|
709
|
-
const premiumTotal = premium.entitlement;
|
|
710
|
-
const premiumUsed = premiumTotal - premium.remaining;
|
|
711
|
-
const premiumPercentUsed = premiumTotal > 0 ? premiumUsed / premiumTotal * 100 : 0;
|
|
712
|
-
const premiumPercentRemaining = premium.percent_remaining;
|
|
713
|
-
function summarizeQuota(name, snap) {
|
|
714
|
-
if (!snap) return `${name}: N/A`;
|
|
715
|
-
const total = snap.entitlement;
|
|
716
|
-
const used = total - snap.remaining;
|
|
717
|
-
const percentUsed = total > 0 ? used / total * 100 : 0;
|
|
718
|
-
const percentRemaining = snap.percent_remaining;
|
|
719
|
-
return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
|
|
720
|
-
}
|
|
721
|
-
const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`;
|
|
722
|
-
const chatLine = summarizeQuota("Chat", usage.quota_snapshots.chat);
|
|
723
|
-
const completionsLine = summarizeQuota("Completions", usage.quota_snapshots.completions);
|
|
724
|
-
consola.box(`Copilot Usage (plan: ${usage.copilot_plan})\nQuota resets: ${usage.quota_reset_date}\n\nQuotas:\n ${premiumLine}\n ${chatLine}\n ${completionsLine}`);
|
|
725
|
-
} catch (err) {
|
|
726
|
-
consola.error("Failed to fetch Copilot usage:", err);
|
|
727
|
-
process.exit(1);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
//#endregion
|
|
732
|
-
//#region src/debug.ts
|
|
733
|
-
async function getPackageVersion() {
|
|
734
|
-
try {
|
|
735
|
-
const packageJsonPath = new URL("../package.json", import.meta.url).pathname;
|
|
736
|
-
return JSON.parse(await fs.readFile(packageJsonPath)).version;
|
|
737
|
-
} catch {
|
|
738
|
-
return "unknown";
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
function getRuntimeInfo() {
|
|
742
|
-
const isBun = typeof Bun !== "undefined";
|
|
743
|
-
return {
|
|
744
|
-
name: isBun ? "bun" : "node",
|
|
745
|
-
version: isBun ? Bun.version : process.version.slice(1),
|
|
746
|
-
platform: os.platform(),
|
|
747
|
-
arch: os.arch()
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
async function checkTokenExists() {
|
|
751
|
-
try {
|
|
752
|
-
if (!(await fs.stat(PATHS.GITHUB_TOKEN_PATH)).isFile()) return false;
|
|
753
|
-
return (await fs.readFile(PATHS.GITHUB_TOKEN_PATH, "utf8")).trim().length > 0;
|
|
754
|
-
} catch {
|
|
755
|
-
return false;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
async function checkFileExists(path) {
|
|
759
|
-
try {
|
|
760
|
-
if (!(await fs.stat(path)).isFile()) return false;
|
|
761
|
-
return (await fs.readFile(path, "utf8")).trim().length > 0;
|
|
762
|
-
} catch {
|
|
763
|
-
return false;
|
|
205
|
+
async function checkTokenExists() {
|
|
206
|
+
try {
|
|
207
|
+
if (!(await fs.stat(PATHS.GITHUB_TOKEN_PATH)).isFile()) return false;
|
|
208
|
+
return (await fs.readFile(PATHS.GITHUB_TOKEN_PATH, "utf8")).trim().length > 0;
|
|
209
|
+
} catch {
|
|
210
|
+
return false;
|
|
764
211
|
}
|
|
765
212
|
}
|
|
766
213
|
async function getDebugInfo() {
|
|
767
|
-
const
|
|
768
|
-
const antigravityAuthPath = getAntigravityAuthPath();
|
|
769
|
-
const [version, githubExists, zenExists, antigravityExists, proxyConfig] = await Promise.all([
|
|
214
|
+
const [version, githubExists, proxyConfig] = await Promise.all([
|
|
770
215
|
getPackageVersion(),
|
|
771
216
|
checkTokenExists(),
|
|
772
|
-
checkFileExists(zenAuthPath),
|
|
773
|
-
checkFileExists(antigravityAuthPath),
|
|
774
217
|
getProxyConfig()
|
|
775
218
|
]);
|
|
776
219
|
return {
|
|
@@ -778,15 +221,9 @@ async function getDebugInfo() {
|
|
|
778
221
|
runtime: getRuntimeInfo(),
|
|
779
222
|
paths: {
|
|
780
223
|
APP_DIR: PATHS.APP_DIR,
|
|
781
|
-
GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH
|
|
782
|
-
ZEN_AUTH_PATH: zenAuthPath,
|
|
783
|
-
ANTIGRAVITY_AUTH_PATH: antigravityAuthPath
|
|
784
|
-
},
|
|
785
|
-
credentials: {
|
|
786
|
-
github: githubExists,
|
|
787
|
-
zen: zenExists,
|
|
788
|
-
antigravity: antigravityExists
|
|
224
|
+
GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH
|
|
789
225
|
},
|
|
226
|
+
credentials: { github: githubExists },
|
|
790
227
|
proxy: proxyConfig
|
|
791
228
|
};
|
|
792
229
|
}
|
|
@@ -801,13 +238,9 @@ Runtime: ${info.runtime.name} ${info.runtime.version} (${info.runtime.platform}
|
|
|
801
238
|
Paths:
|
|
802
239
|
APP_DIR: ${info.paths.APP_DIR}
|
|
803
240
|
GITHUB_TOKEN_PATH: ${info.paths.GITHUB_TOKEN_PATH}
|
|
804
|
-
ZEN_AUTH_PATH: ${info.paths.ZEN_AUTH_PATH}
|
|
805
|
-
ANTIGRAVITY_AUTH_PATH: ${info.paths.ANTIGRAVITY_AUTH_PATH}
|
|
806
241
|
|
|
807
242
|
Credentials:
|
|
808
243
|
GitHub Copilot: ${info.credentials.github ? "✅ Configured" : "❌ Not configured"}
|
|
809
|
-
OpenCode Zen: ${info.credentials.zen ? "✅ Configured" : "❌ Not configured"}
|
|
810
|
-
Google Antigravity: ${info.credentials.antigravity ? "✅ Configured" : "❌ Not configured"}
|
|
811
244
|
|
|
812
245
|
Proxy: ${proxyStatus}`);
|
|
813
246
|
}
|
|
@@ -837,65 +270,15 @@ const debug = defineCommand({
|
|
|
837
270
|
//#region src/logout.ts
|
|
838
271
|
async function runLogout(options) {
|
|
839
272
|
await ensurePaths();
|
|
840
|
-
if (options.all) {
|
|
841
|
-
await clearGithubToken();
|
|
842
|
-
await clearZenAuth();
|
|
843
|
-
await clearAntigravityAuth();
|
|
844
|
-
consola.success("Logged out from all services");
|
|
845
|
-
consola.info(`GitHub token: ${PATHS.GITHUB_TOKEN_PATH}`);
|
|
846
|
-
consola.info(`Zen API key: ${getZenAuthPath()}`);
|
|
847
|
-
consola.info(`Antigravity accounts: ${getAntigravityAuthPath()}`);
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
if (options.github) {
|
|
273
|
+
if (options.all || options.github) {
|
|
851
274
|
await clearGithubToken();
|
|
852
275
|
consola.success("Logged out from GitHub Copilot");
|
|
853
276
|
consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
|
|
854
277
|
return;
|
|
855
278
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
consola.info(`Zen API key location: ${getZenAuthPath()}`);
|
|
860
|
-
return;
|
|
861
|
-
}
|
|
862
|
-
if (options.antigravity) {
|
|
863
|
-
await clearAntigravityAuth();
|
|
864
|
-
consola.success("Logged out from Google Antigravity");
|
|
865
|
-
consola.info(`Antigravity accounts location: ${getAntigravityAuthPath()}`);
|
|
866
|
-
return;
|
|
867
|
-
}
|
|
868
|
-
switch (await consola.prompt("Which credentials do you want to clear?", {
|
|
869
|
-
type: "select",
|
|
870
|
-
options: [
|
|
871
|
-
"GitHub Copilot token",
|
|
872
|
-
"OpenCode Zen API key",
|
|
873
|
-
"Google Antigravity accounts",
|
|
874
|
-
"All credentials"
|
|
875
|
-
]
|
|
876
|
-
})) {
|
|
877
|
-
case "GitHub Copilot token":
|
|
878
|
-
await clearGithubToken();
|
|
879
|
-
consola.success("Logged out from GitHub Copilot");
|
|
880
|
-
consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
|
|
881
|
-
break;
|
|
882
|
-
case "OpenCode Zen API key":
|
|
883
|
-
await clearZenAuth();
|
|
884
|
-
consola.success("Logged out from OpenCode Zen");
|
|
885
|
-
consola.info(`Zen API key location: ${getZenAuthPath()}`);
|
|
886
|
-
break;
|
|
887
|
-
case "Google Antigravity accounts":
|
|
888
|
-
await clearAntigravityAuth();
|
|
889
|
-
consola.success("Logged out from Google Antigravity");
|
|
890
|
-
consola.info(`Antigravity accounts location: ${getAntigravityAuthPath()}`);
|
|
891
|
-
break;
|
|
892
|
-
case "All credentials":
|
|
893
|
-
await clearGithubToken();
|
|
894
|
-
await clearZenAuth();
|
|
895
|
-
await clearAntigravityAuth();
|
|
896
|
-
consola.success("Logged out from all services");
|
|
897
|
-
break;
|
|
898
|
-
}
|
|
279
|
+
await clearGithubToken();
|
|
280
|
+
consola.success("Logged out from GitHub Copilot");
|
|
281
|
+
consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
|
|
899
282
|
}
|
|
900
283
|
const logout = defineCommand({
|
|
901
284
|
meta: {
|
|
@@ -907,31 +290,18 @@ const logout = defineCommand({
|
|
|
907
290
|
alias: "g",
|
|
908
291
|
type: "boolean",
|
|
909
292
|
default: false,
|
|
910
|
-
description: "Clear
|
|
911
|
-
},
|
|
912
|
-
zen: {
|
|
913
|
-
alias: "z",
|
|
914
|
-
type: "boolean",
|
|
915
|
-
default: false,
|
|
916
|
-
description: "Clear only OpenCode Zen API key"
|
|
917
|
-
},
|
|
918
|
-
antigravity: {
|
|
919
|
-
type: "boolean",
|
|
920
|
-
default: false,
|
|
921
|
-
description: "Clear only Google Antigravity accounts"
|
|
293
|
+
description: "Clear GitHub Copilot token"
|
|
922
294
|
},
|
|
923
295
|
all: {
|
|
924
296
|
alias: "a",
|
|
925
297
|
type: "boolean",
|
|
926
298
|
default: false,
|
|
927
|
-
description: "Clear all credentials
|
|
299
|
+
description: "Clear all credentials"
|
|
928
300
|
}
|
|
929
301
|
},
|
|
930
302
|
run({ args }) {
|
|
931
303
|
return runLogout({
|
|
932
304
|
github: args.github,
|
|
933
|
-
zen: args.zen,
|
|
934
|
-
antigravity: args.antigravity,
|
|
935
305
|
all: args.all
|
|
936
306
|
});
|
|
937
307
|
}
|
|
@@ -1095,1690 +465,559 @@ const proxy = defineCommand({
|
|
|
1095
465
|
}
|
|
1096
466
|
});
|
|
1097
467
|
//#endregion
|
|
1098
|
-
//#region
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
function extractApiKey(c) {
|
|
1158
|
-
const authHeader = c.req.header("authorization");
|
|
1159
|
-
if (authHeader?.startsWith("Bearer ")) return authHeader.slice(7);
|
|
1160
|
-
const anthropicKey = c.req.header("x-api-key");
|
|
1161
|
-
if (anthropicKey) return anthropicKey;
|
|
1162
|
-
const queryKey = c.req.query("apiKey");
|
|
1163
|
-
if (queryKey) return queryKey;
|
|
1164
|
-
}
|
|
1165
|
-
/**
|
|
1166
|
-
* API key authentication middleware
|
|
1167
|
-
* Validates that the request contains a valid API key if API keys are configured
|
|
1168
|
-
*/
|
|
1169
|
-
const apiKeyAuthMiddleware = async (c, next) => {
|
|
1170
|
-
if (!state.apiKeys || state.apiKeys.length === 0) {
|
|
1171
|
-
await next();
|
|
1172
|
-
return;
|
|
1173
|
-
}
|
|
1174
|
-
const providedKey = extractApiKey(c);
|
|
1175
|
-
if (!providedKey) throw new HTTPException(401, { message: "API key required. Please provide a valid API key in the Authorization header (Bearer token) or x-api-key header." });
|
|
1176
|
-
if (!state.apiKeys.includes(providedKey)) throw new HTTPException(401, { message: "Invalid API key. Please provide a valid API key." });
|
|
1177
|
-
await next();
|
|
1178
|
-
};
|
|
1179
|
-
//#endregion
|
|
1180
|
-
//#region src/lib/model-logger.ts
|
|
1181
|
-
/**
|
|
1182
|
-
* Get timestamp string in format HH:mm:ss
|
|
1183
|
-
*/
|
|
1184
|
-
function getTime() {
|
|
1185
|
-
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
1186
|
-
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Format duration in human readable format
|
|
1189
|
-
*/
|
|
1190
|
-
function formatDuration(ms) {
|
|
1191
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
1192
|
-
return `${(ms / 1e3).toFixed(1)}s`;
|
|
1193
|
-
}
|
|
1194
|
-
/**
|
|
1195
|
-
* Extract model name from request body
|
|
1196
|
-
*/
|
|
1197
|
-
async function extractModel(c) {
|
|
1198
|
-
try {
|
|
1199
|
-
return (await c.req.raw.clone().json()).model;
|
|
1200
|
-
} catch {
|
|
1201
|
-
return;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Custom logger middleware that shows model name before timestamp
|
|
1206
|
-
*
|
|
1207
|
-
* Output format:
|
|
1208
|
-
* [model] HH:mm:ss <-- METHOD /path
|
|
1209
|
-
* [model] HH:mm:ss --> METHOD /path STATUS DURATION
|
|
1210
|
-
*/
|
|
1211
|
-
function modelLogger() {
|
|
1212
|
-
return async (c, next) => {
|
|
1213
|
-
const method = c.req.method;
|
|
1214
|
-
const fullPath = `${c.req.path}${c.req.raw.url.includes("?") ? `?${c.req.raw.url.split("?")[1]}` : ""}`;
|
|
1215
|
-
let model;
|
|
1216
|
-
if (method === "POST" && c.req.header("content-type")?.includes("json")) model = await extractModel(c);
|
|
1217
|
-
const modelPrefix = model ? `[${model}] ` : "";
|
|
1218
|
-
const startTime = getTime();
|
|
1219
|
-
console.log(`${modelPrefix}${startTime} <-- ${method} ${fullPath}`);
|
|
1220
|
-
const start = Date.now();
|
|
1221
|
-
await next();
|
|
1222
|
-
const duration = Date.now() - start;
|
|
1223
|
-
const endTime = getTime();
|
|
1224
|
-
console.log(`${modelPrefix}${endTime} --> ${method} ${fullPath} ${c.res.status} ${formatDuration(duration)}`);
|
|
1225
|
-
};
|
|
1226
|
-
}
|
|
1227
|
-
//#endregion
|
|
1228
|
-
//#region src/services/antigravity/background-detection.ts
|
|
1229
|
-
/**
|
|
1230
|
-
* Antigravity Background Detection
|
|
1231
|
-
*
|
|
1232
|
-
* Detects agent/background requests and optionally downgrades
|
|
1233
|
-
* expensive models to cheaper alternatives to preserve quota.
|
|
1234
|
-
*
|
|
1235
|
-
* Detection uses multi-signal scoring — not a single heuristic:
|
|
1236
|
-
* 1. Explicit header: X-Request-Type: background (definitive)
|
|
1237
|
-
* 2. tool_calls / tool role pattern (strong agent signal)
|
|
1238
|
-
* 3. High density of assistant messages (weak signal alone)
|
|
1239
|
-
*
|
|
1240
|
-
* Controlled by env var: ANTIGRAVITY_BACKGROUND_DOWNGRADE=1
|
|
1241
|
-
*/
|
|
1242
|
-
const BACKGROUND_DOWNGRADE_ENABLED = process.env.ANTIGRAVITY_BACKGROUND_DOWNGRADE === "1";
|
|
1243
|
-
/**
|
|
1244
|
-
* Score threshold for downgrade (0–1).
|
|
1245
|
-
* 0.6 means we need at least one strong signal.
|
|
1246
|
-
*/
|
|
1247
|
-
const AGENT_SCORE_THRESHOLD = .6;
|
|
1248
|
-
/**
|
|
1249
|
-
* Model downgrade mapping: expensive → cheaper
|
|
1250
|
-
*/
|
|
1251
|
-
const DOWNGRADE_MAP = {
|
|
1252
|
-
"claude-sonnet-4-5": "gemini-2.5-flash",
|
|
1253
|
-
"claude-sonnet-4-5-thinking": "gemini-2.5-flash-thinking",
|
|
1254
|
-
"claude-opus-4-5-thinking": "claude-sonnet-4-5-thinking",
|
|
1255
|
-
"gemini-2.5-pro": "gemini-2.5-flash",
|
|
1256
|
-
"gemini-3-pro-high": "gemini-3-flash",
|
|
1257
|
-
"gemini-3-pro-low": "gemini-3-flash"
|
|
1258
|
-
};
|
|
1259
|
-
/**
|
|
1260
|
-
* Multi-signal agent detection.
|
|
1261
|
-
*
|
|
1262
|
-
* Scores the request on a 0–1 scale:
|
|
1263
|
-
* - Explicit header "X-Request-Type: background" → 1.0 (instant)
|
|
1264
|
-
* - Any message with tool_calls field → +0.5
|
|
1265
|
-
* - Any message with role "tool" → +0.4
|
|
1266
|
-
* - assistant messages ≥ 60% of all messages → +0.2
|
|
1267
|
-
* - More than 6 total messages → +0.1
|
|
1268
|
-
*
|
|
1269
|
-
* Score ≥ 0.6 → treated as agent/background request.
|
|
1270
|
-
*/
|
|
1271
|
-
function detectAgent(messages, headers) {
|
|
1272
|
-
const signals = [];
|
|
1273
|
-
const requestType = headers?.get("x-request-type");
|
|
1274
|
-
if (requestType === "background" || requestType === "agent") return {
|
|
1275
|
-
isAgent: true,
|
|
1276
|
-
score: 1,
|
|
1277
|
-
signals: ["explicit-header"]
|
|
1278
|
-
};
|
|
1279
|
-
let score = 0;
|
|
1280
|
-
const total = messages.length;
|
|
1281
|
-
if (total === 0) return {
|
|
1282
|
-
isAgent: false,
|
|
1283
|
-
score: 0,
|
|
1284
|
-
signals: []
|
|
1285
|
-
};
|
|
1286
|
-
if (messages.some((m) => m.tool_calls !== void 0 && m.tool_calls !== null)) {
|
|
1287
|
-
score += .5;
|
|
1288
|
-
signals.push("tool_calls");
|
|
1289
|
-
}
|
|
1290
|
-
if (messages.some((m) => m.role === "tool")) {
|
|
1291
|
-
score += .4;
|
|
1292
|
-
signals.push("tool-role");
|
|
1293
|
-
}
|
|
1294
|
-
const assistantCount = messages.filter((m) => m.role === "assistant").length;
|
|
1295
|
-
if (total >= 3 && assistantCount / total >= .6) {
|
|
1296
|
-
score += .2;
|
|
1297
|
-
signals.push("high-assistant-density");
|
|
1298
|
-
}
|
|
1299
|
-
if (total > 6) {
|
|
1300
|
-
score += .1;
|
|
1301
|
-
signals.push("long-conversation");
|
|
1302
|
-
}
|
|
1303
|
-
return {
|
|
1304
|
-
isAgent: score >= AGENT_SCORE_THRESHOLD,
|
|
1305
|
-
score: Math.min(score, 1),
|
|
1306
|
-
signals
|
|
1307
|
-
};
|
|
1308
|
-
}
|
|
1309
|
-
/**
|
|
1310
|
-
* Optionally downgrade a model for background/agent requests.
|
|
1311
|
-
*
|
|
1312
|
-
* Returns the original model if:
|
|
1313
|
-
* - ANTIGRAVITY_BACKGROUND_DOWNGRADE is not "1"
|
|
1314
|
-
* - The request is not detected as agent/background
|
|
1315
|
-
* - No downgrade mapping exists for the model
|
|
1316
|
-
*/
|
|
1317
|
-
function maybeDowngradeModel(model, messages, headers) {
|
|
1318
|
-
if (!BACKGROUND_DOWNGRADE_ENABLED) return model;
|
|
1319
|
-
const result = detectAgent(messages, headers);
|
|
1320
|
-
if (!result.isAgent) return model;
|
|
1321
|
-
const downgraded = DOWNGRADE_MAP[model];
|
|
1322
|
-
if (!downgraded) return model;
|
|
1323
|
-
consola.info(`[background-detection] agent detected (score=${result.score.toFixed(2)}, signals=[${result.signals.join(",")}]), downgrading ${model} → ${downgraded}`);
|
|
1324
|
-
return downgraded;
|
|
1325
|
-
}
|
|
1326
|
-
//#endregion
|
|
1327
|
-
//#region src/services/antigravity/circuit-breaker.ts
|
|
1328
|
-
const FAILURE_THRESHOLD = 3;
|
|
1329
|
-
const RESET_TIMEOUT_MS = 3e4;
|
|
1330
|
-
const HALF_OPEN_SUCCESS_THRESHOLD = 2;
|
|
1331
|
-
const CircuitState = {
|
|
1332
|
-
CLOSED: "CLOSED",
|
|
1333
|
-
OPEN: "OPEN",
|
|
1334
|
-
HALF_OPEN: "HALF_OPEN"
|
|
1335
|
-
};
|
|
1336
|
-
const breakers = /* @__PURE__ */ new Map();
|
|
1337
|
-
function getOrCreate(family) {
|
|
1338
|
-
const existing = breakers.get(family);
|
|
1339
|
-
if (existing) return existing;
|
|
1340
|
-
const fresh = {
|
|
1341
|
-
state: CircuitState.CLOSED,
|
|
1342
|
-
failureCount: 0,
|
|
1343
|
-
successCount: 0,
|
|
1344
|
-
lastFailureTime: 0
|
|
1345
|
-
};
|
|
1346
|
-
breakers.set(family, fresh);
|
|
1347
|
-
return fresh;
|
|
1348
|
-
}
|
|
1349
|
-
function getModelFamily(model) {
|
|
1350
|
-
if (model.includes("claude")) return "claude";
|
|
1351
|
-
if (model.includes("gemini")) return "gemini";
|
|
1352
|
-
return "other";
|
|
1353
|
-
}
|
|
1354
|
-
function canExecute(family) {
|
|
1355
|
-
const breaker = getOrCreate(family);
|
|
1356
|
-
switch (breaker.state) {
|
|
1357
|
-
case CircuitState.CLOSED: return true;
|
|
1358
|
-
case CircuitState.OPEN:
|
|
1359
|
-
if (Date.now() - breaker.lastFailureTime >= RESET_TIMEOUT_MS) {
|
|
1360
|
-
consola.info(`[circuit-breaker] ${family}: OPEN -> HALF_OPEN`);
|
|
1361
|
-
breaker.state = CircuitState.HALF_OPEN;
|
|
1362
|
-
breaker.successCount = 0;
|
|
1363
|
-
return true;
|
|
1364
|
-
}
|
|
1365
|
-
return false;
|
|
1366
|
-
case CircuitState.HALF_OPEN: return true;
|
|
1367
|
-
default: return true;
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
function recordSuccess(family) {
|
|
1371
|
-
const breaker = getOrCreate(family);
|
|
1372
|
-
switch (breaker.state) {
|
|
1373
|
-
case CircuitState.HALF_OPEN:
|
|
1374
|
-
breaker.successCount++;
|
|
1375
|
-
if (breaker.successCount >= HALF_OPEN_SUCCESS_THRESHOLD) {
|
|
1376
|
-
consola.info(`[circuit-breaker] ${family}: HALF_OPEN -> CLOSED`);
|
|
1377
|
-
breaker.state = CircuitState.CLOSED;
|
|
1378
|
-
breaker.failureCount = 0;
|
|
1379
|
-
breaker.successCount = 0;
|
|
1380
|
-
}
|
|
1381
|
-
break;
|
|
1382
|
-
case CircuitState.CLOSED:
|
|
1383
|
-
breaker.failureCount = 0;
|
|
1384
|
-
break;
|
|
1385
|
-
default: break;
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
function recordFailure(family) {
|
|
1389
|
-
const breaker = getOrCreate(family);
|
|
1390
|
-
breaker.failureCount++;
|
|
1391
|
-
breaker.lastFailureTime = Date.now();
|
|
1392
|
-
switch (breaker.state) {
|
|
1393
|
-
case CircuitState.CLOSED:
|
|
1394
|
-
if (breaker.failureCount >= FAILURE_THRESHOLD) {
|
|
1395
|
-
consola.info(`[circuit-breaker] ${family}: CLOSED -> OPEN`);
|
|
1396
|
-
breaker.state = CircuitState.OPEN;
|
|
1397
|
-
}
|
|
1398
|
-
break;
|
|
1399
|
-
case CircuitState.HALF_OPEN:
|
|
1400
|
-
consola.info(`[circuit-breaker] ${family}: HALF_OPEN -> OPEN`);
|
|
1401
|
-
breaker.state = CircuitState.OPEN;
|
|
1402
|
-
break;
|
|
1403
|
-
default: break;
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
function getBackoffDelay(family, baseDelay) {
|
|
1407
|
-
const breaker = breakers.get(family);
|
|
1408
|
-
if (!breaker || breaker.failureCount === 0) return baseDelay;
|
|
1409
|
-
const delay = baseDelay * Math.pow(2, breaker.failureCount - 1);
|
|
1410
|
-
return Math.min(delay, 3e4);
|
|
1411
|
-
}
|
|
1412
|
-
function parseRetryDelay$3(errorText) {
|
|
1413
|
-
try {
|
|
1414
|
-
const errorData = JSON.parse(errorText);
|
|
1415
|
-
const details = errorData.error?.details ?? [];
|
|
1416
|
-
for (const detail of details) {
|
|
1417
|
-
if (detail["@type"]?.includes("RetryInfo") && detail.retryDelay) {
|
|
1418
|
-
const match = /(\d+(?:\.\d+)?)s/.exec(detail.retryDelay);
|
|
1419
|
-
if (match) return Math.ceil(Number.parseFloat(match[1]) * 1e3);
|
|
1420
|
-
}
|
|
1421
|
-
if (detail.quotaResetDelay) {
|
|
1422
|
-
const match = /(\d+(?:\.\d+)?)(?:ms|s)/.exec(detail.quotaResetDelay);
|
|
1423
|
-
if (match) {
|
|
1424
|
-
const value = Number.parseFloat(match[1]);
|
|
1425
|
-
return detail.quotaResetDelay.includes("ms") ? Math.ceil(value) : Math.ceil(value * 1e3);
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
const message = errorData.error?.message ?? "";
|
|
1430
|
-
const resetMatch = /quota will reset after (\d+(?:\.\d+)?)s/i.exec(message);
|
|
1431
|
-
if (resetMatch) return Math.ceil(Number.parseFloat(resetMatch[1]) * 1e3);
|
|
1432
|
-
} catch {}
|
|
1433
|
-
return 500;
|
|
1434
|
-
}
|
|
1435
|
-
//#endregion
|
|
1436
|
-
//#region src/services/antigravity/stream-parser.ts
|
|
1437
|
-
/**
|
|
1438
|
-
* Create initial stream state
|
|
1439
|
-
*/
|
|
1440
|
-
function createStreamState() {
|
|
1441
|
-
return {
|
|
1442
|
-
buffer: "",
|
|
1443
|
-
inputTokens: 0,
|
|
1444
|
-
outputTokens: 0,
|
|
1445
|
-
contentBlockIndex: 0,
|
|
1446
|
-
thinkingBlockStarted: false,
|
|
1447
|
-
textBlockStarted: false
|
|
1448
|
-
};
|
|
1449
|
-
}
|
|
1450
|
-
/**
|
|
1451
|
-
* Parse a single SSE line and return the JSON data if valid
|
|
1452
|
-
*/
|
|
1453
|
-
function parseSSELine(line) {
|
|
1454
|
-
if (!line.startsWith("data: ")) return null;
|
|
1455
|
-
const data = line.slice(6).trim();
|
|
1456
|
-
if (data === "[DONE]" || data === "") return null;
|
|
1457
|
-
try {
|
|
1458
|
-
return JSON.parse(data);
|
|
1459
|
-
} catch {
|
|
1460
|
-
return null;
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
/**
|
|
1464
|
-
* Extract candidates and usage from parsed data
|
|
1465
|
-
*/
|
|
1466
|
-
function extractFromData(data) {
|
|
1467
|
-
return {
|
|
1468
|
-
candidates: data.response?.candidates || data.candidates || [],
|
|
1469
|
-
usage: data.response?.usageMetadata || data.usageMetadata
|
|
468
|
+
//#region node_modules/dotenv/package.json
|
|
469
|
+
var require_package = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
470
|
+
module.exports = {
|
|
471
|
+
"name": "dotenv",
|
|
472
|
+
"version": "17.2.3",
|
|
473
|
+
"description": "Loads environment variables from .env file",
|
|
474
|
+
"main": "lib/main.js",
|
|
475
|
+
"types": "lib/main.d.ts",
|
|
476
|
+
"exports": {
|
|
477
|
+
".": {
|
|
478
|
+
"types": "./lib/main.d.ts",
|
|
479
|
+
"require": "./lib/main.js",
|
|
480
|
+
"default": "./lib/main.js"
|
|
481
|
+
},
|
|
482
|
+
"./config": "./config.js",
|
|
483
|
+
"./config.js": "./config.js",
|
|
484
|
+
"./lib/env-options": "./lib/env-options.js",
|
|
485
|
+
"./lib/env-options.js": "./lib/env-options.js",
|
|
486
|
+
"./lib/cli-options": "./lib/cli-options.js",
|
|
487
|
+
"./lib/cli-options.js": "./lib/cli-options.js",
|
|
488
|
+
"./package.json": "./package.json"
|
|
489
|
+
},
|
|
490
|
+
"scripts": {
|
|
491
|
+
"dts-check": "tsc --project tests/types/tsconfig.json",
|
|
492
|
+
"lint": "standard",
|
|
493
|
+
"pretest": "npm run lint && npm run dts-check",
|
|
494
|
+
"test": "tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000",
|
|
495
|
+
"test:coverage": "tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",
|
|
496
|
+
"prerelease": "npm test",
|
|
497
|
+
"release": "standard-version"
|
|
498
|
+
},
|
|
499
|
+
"repository": {
|
|
500
|
+
"type": "git",
|
|
501
|
+
"url": "git://github.com/motdotla/dotenv.git"
|
|
502
|
+
},
|
|
503
|
+
"homepage": "https://github.com/motdotla/dotenv#readme",
|
|
504
|
+
"funding": "https://dotenvx.com",
|
|
505
|
+
"keywords": [
|
|
506
|
+
"dotenv",
|
|
507
|
+
"env",
|
|
508
|
+
".env",
|
|
509
|
+
"environment",
|
|
510
|
+
"variables",
|
|
511
|
+
"config",
|
|
512
|
+
"settings"
|
|
513
|
+
],
|
|
514
|
+
"readmeFilename": "README.md",
|
|
515
|
+
"license": "BSD-2-Clause",
|
|
516
|
+
"devDependencies": {
|
|
517
|
+
"@types/node": "^18.11.3",
|
|
518
|
+
"decache": "^4.6.2",
|
|
519
|
+
"sinon": "^14.0.1",
|
|
520
|
+
"standard": "^17.0.0",
|
|
521
|
+
"standard-version": "^9.5.0",
|
|
522
|
+
"tap": "^19.2.0",
|
|
523
|
+
"typescript": "^4.8.4"
|
|
524
|
+
},
|
|
525
|
+
"engines": { "node": ">=12" },
|
|
526
|
+
"browser": { "fs": false }
|
|
1470
527
|
};
|
|
1471
|
-
}
|
|
1472
|
-
/**
|
|
1473
|
-
* Process a single part and emit events
|
|
1474
|
-
*/
|
|
1475
|
-
function processPart(part, state, emit) {
|
|
1476
|
-
if (part.thought && part.text) {
|
|
1477
|
-
processThinkingPart(part.text, state, emit);
|
|
1478
|
-
return;
|
|
1479
|
-
}
|
|
1480
|
-
if (part.text && !part.thought) {
|
|
1481
|
-
processTextPart(part.text, state, emit);
|
|
1482
|
-
return;
|
|
1483
|
-
}
|
|
1484
|
-
if (part.functionCall) processToolPart(part.functionCall, state, emit);
|
|
1485
|
-
}
|
|
1486
|
-
/**
|
|
1487
|
-
* Process thinking content
|
|
1488
|
-
*/
|
|
1489
|
-
function processThinkingPart(text, state, emit) {
|
|
1490
|
-
if (!state.thinkingBlockStarted) {
|
|
1491
|
-
emit({
|
|
1492
|
-
type: "thinking_start",
|
|
1493
|
-
index: state.contentBlockIndex
|
|
1494
|
-
});
|
|
1495
|
-
state.thinkingBlockStarted = true;
|
|
1496
|
-
}
|
|
1497
|
-
emit({
|
|
1498
|
-
type: "thinking_delta",
|
|
1499
|
-
index: state.contentBlockIndex,
|
|
1500
|
-
text
|
|
1501
|
-
});
|
|
1502
|
-
}
|
|
1503
|
-
/**
|
|
1504
|
-
* Process text content
|
|
1505
|
-
*/
|
|
1506
|
-
function processTextPart(text, state, emit) {
|
|
1507
|
-
if (state.thinkingBlockStarted && !state.textBlockStarted) {
|
|
1508
|
-
emit({
|
|
1509
|
-
type: "thinking_stop",
|
|
1510
|
-
index: state.contentBlockIndex
|
|
1511
|
-
});
|
|
1512
|
-
state.contentBlockIndex++;
|
|
1513
|
-
state.thinkingBlockStarted = false;
|
|
1514
|
-
}
|
|
1515
|
-
if (!state.textBlockStarted) {
|
|
1516
|
-
emit({
|
|
1517
|
-
type: "text_start",
|
|
1518
|
-
index: state.contentBlockIndex
|
|
1519
|
-
});
|
|
1520
|
-
state.textBlockStarted = true;
|
|
1521
|
-
}
|
|
1522
|
-
emit({
|
|
1523
|
-
type: "text_delta",
|
|
1524
|
-
index: state.contentBlockIndex,
|
|
1525
|
-
text
|
|
1526
|
-
});
|
|
1527
|
-
}
|
|
1528
|
-
/**
|
|
1529
|
-
* Process tool/function call
|
|
1530
|
-
*/
|
|
1531
|
-
function processToolPart(functionCall, state, emit) {
|
|
1532
|
-
if (state.textBlockStarted) {
|
|
1533
|
-
emit({
|
|
1534
|
-
type: "text_stop",
|
|
1535
|
-
index: state.contentBlockIndex
|
|
1536
|
-
});
|
|
1537
|
-
state.contentBlockIndex++;
|
|
1538
|
-
state.textBlockStarted = false;
|
|
1539
|
-
} else if (state.thinkingBlockStarted) {
|
|
1540
|
-
emit({
|
|
1541
|
-
type: "thinking_stop",
|
|
1542
|
-
index: state.contentBlockIndex
|
|
1543
|
-
});
|
|
1544
|
-
state.contentBlockIndex++;
|
|
1545
|
-
state.thinkingBlockStarted = false;
|
|
1546
|
-
}
|
|
1547
|
-
emit({
|
|
1548
|
-
type: "tool_use",
|
|
1549
|
-
index: state.contentBlockIndex,
|
|
1550
|
-
name: functionCall.name,
|
|
1551
|
-
args: functionCall.args
|
|
1552
|
-
});
|
|
1553
|
-
state.contentBlockIndex++;
|
|
1554
|
-
}
|
|
1555
|
-
/**
|
|
1556
|
-
* Handle finish reason and close open blocks
|
|
1557
|
-
*/
|
|
1558
|
-
function handleFinish(state, emit) {
|
|
1559
|
-
if (state.textBlockStarted) {
|
|
1560
|
-
emit({
|
|
1561
|
-
type: "text_stop",
|
|
1562
|
-
index: state.contentBlockIndex
|
|
1563
|
-
});
|
|
1564
|
-
state.textBlockStarted = false;
|
|
1565
|
-
} else if (state.thinkingBlockStarted) {
|
|
1566
|
-
emit({
|
|
1567
|
-
type: "thinking_stop",
|
|
1568
|
-
index: state.contentBlockIndex
|
|
1569
|
-
});
|
|
1570
|
-
state.thinkingBlockStarted = false;
|
|
1571
|
-
}
|
|
1572
|
-
emit({
|
|
1573
|
-
type: "usage",
|
|
1574
|
-
inputTokens: state.inputTokens,
|
|
1575
|
-
outputTokens: state.outputTokens
|
|
1576
|
-
});
|
|
1577
|
-
emit({
|
|
1578
|
-
type: "finish",
|
|
1579
|
-
stopReason: "end_turn"
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
/**
|
|
1583
|
-
* Process chunk and update buffer, returning complete lines
|
|
1584
|
-
*/
|
|
1585
|
-
function processChunk(chunk, state) {
|
|
1586
|
-
state.buffer += chunk;
|
|
1587
|
-
const lines = state.buffer.split("\n");
|
|
1588
|
-
state.buffer = lines.pop() || "";
|
|
1589
|
-
return lines;
|
|
1590
|
-
}
|
|
528
|
+
}));
|
|
1591
529
|
//#endregion
|
|
1592
|
-
//#region
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
continue;
|
|
1620
|
-
}
|
|
1621
|
-
const role = message.role === "assistant" ? "model" : "user";
|
|
1622
|
-
const parts = buildMessageParts(message.content);
|
|
1623
|
-
contents.push({
|
|
1624
|
-
role,
|
|
1625
|
-
parts
|
|
1626
|
-
});
|
|
1627
|
-
}
|
|
1628
|
-
return {
|
|
1629
|
-
contents,
|
|
1630
|
-
systemInstruction
|
|
1631
|
-
};
|
|
1632
|
-
}
|
|
1633
|
-
/**
|
|
1634
|
-
* Build system instruction from content
|
|
1635
|
-
*/
|
|
1636
|
-
function buildSystemInstruction(content) {
|
|
1637
|
-
return { parts: [{ text: typeof content === "string" ? content : content.map((c) => c.text || "").join("") }] };
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Build message parts from content
|
|
1641
|
-
*/
|
|
1642
|
-
function buildMessageParts(content) {
|
|
1643
|
-
if (typeof content === "string") return [{ text: content }];
|
|
1644
|
-
const parts = [];
|
|
1645
|
-
for (const part of content) if (part.type === "text") parts.push({ text: part.text });
|
|
1646
|
-
else if (part.type === "image_url" && part.image_url?.url) {
|
|
1647
|
-
const imageData = parseBase64Image(part.image_url.url);
|
|
1648
|
-
if (imageData) parts.push({ inlineData: imageData });
|
|
530
|
+
//#region node_modules/dotenv/lib/main.js
|
|
531
|
+
var require_main = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
532
|
+
const fs$1 = __require("fs");
|
|
533
|
+
const path$1 = __require("path");
|
|
534
|
+
const os$1 = __require("os");
|
|
535
|
+
const crypto = __require("crypto");
|
|
536
|
+
const version = require_package().version;
|
|
537
|
+
const TIPS = [
|
|
538
|
+
"🔐 encrypt with Dotenvx: https://dotenvx.com",
|
|
539
|
+
"🔐 prevent committing .env to code: https://dotenvx.com/precommit",
|
|
540
|
+
"🔐 prevent building .env in docker: https://dotenvx.com/prebuild",
|
|
541
|
+
"📡 add observability to secrets: https://dotenvx.com/ops",
|
|
542
|
+
"👥 sync secrets across teammates & machines: https://dotenvx.com/ops",
|
|
543
|
+
"🗂️ backup and recover secrets: https://dotenvx.com/ops",
|
|
544
|
+
"✅ audit secrets and track compliance: https://dotenvx.com/ops",
|
|
545
|
+
"🔄 add secrets lifecycle management: https://dotenvx.com/ops",
|
|
546
|
+
"🔑 add access controls to secrets: https://dotenvx.com/ops",
|
|
547
|
+
"🛠️ run anywhere with `dotenvx run -- yourcommand`",
|
|
548
|
+
"⚙️ specify custom .env file path with { path: '/custom/path/.env' }",
|
|
549
|
+
"⚙️ enable debug logging with { debug: true }",
|
|
550
|
+
"⚙️ override existing env vars with { override: true }",
|
|
551
|
+
"⚙️ suppress all logs with { quiet: true }",
|
|
552
|
+
"⚙️ write to custom object with { processEnv: myObject }",
|
|
553
|
+
"⚙️ load multiple .env files with { path: ['.env.local', '.env'] }"
|
|
554
|
+
];
|
|
555
|
+
function _getRandomTip() {
|
|
556
|
+
return TIPS[Math.floor(Math.random() * TIPS.length)];
|
|
1649
557
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
return {
|
|
1660
|
-
mimeType: match[1],
|
|
1661
|
-
data: match[2]
|
|
1662
|
-
};
|
|
1663
|
-
}
|
|
1664
|
-
const GEMINI_ALLOWED_FIELDS$1 = new Set([
|
|
1665
|
-
"type",
|
|
1666
|
-
"description",
|
|
1667
|
-
"properties",
|
|
1668
|
-
"required",
|
|
1669
|
-
"items",
|
|
1670
|
-
"enum",
|
|
1671
|
-
"nullable"
|
|
1672
|
-
]);
|
|
1673
|
-
/**
|
|
1674
|
-
* Convert type value to Gemini format
|
|
1675
|
-
* Handles both string and array types (for nullable)
|
|
1676
|
-
*/
|
|
1677
|
-
function convertTypeValue$1(value) {
|
|
1678
|
-
if (typeof value === "string") return { type: value.toUpperCase() };
|
|
1679
|
-
if (Array.isArray(value)) {
|
|
1680
|
-
const nonNullType = value.find((t) => typeof t === "string" && t !== "null");
|
|
1681
|
-
if (nonNullType) return {
|
|
1682
|
-
type: nonNullType.toUpperCase(),
|
|
1683
|
-
nullable: true
|
|
1684
|
-
};
|
|
558
|
+
function parseBoolean(value) {
|
|
559
|
+
if (typeof value === "string") return ![
|
|
560
|
+
"false",
|
|
561
|
+
"0",
|
|
562
|
+
"no",
|
|
563
|
+
"off",
|
|
564
|
+
""
|
|
565
|
+
].includes(value.toLowerCase());
|
|
566
|
+
return Boolean(value);
|
|
1685
567
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
/**
|
|
1689
|
-
* Clean properties object recursively
|
|
1690
|
-
*/
|
|
1691
|
-
function cleanProperties$1(props) {
|
|
1692
|
-
const cleanedProps = {};
|
|
1693
|
-
for (const [propKey, propValue] of Object.entries(props)) cleanedProps[propKey] = cleanJsonSchema$1(propValue);
|
|
1694
|
-
return cleanedProps;
|
|
1695
|
-
}
|
|
1696
|
-
/**
|
|
1697
|
-
* Clean and convert JSON schema to Gemini format
|
|
1698
|
-
* - Removes unsupported fields like $schema, additionalProperties
|
|
1699
|
-
* - Converts type values to uppercase (string -> STRING)
|
|
1700
|
-
* - Ensures properties format is correct for Google API
|
|
1701
|
-
*/
|
|
1702
|
-
function cleanJsonSchema$1(schema) {
|
|
1703
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
1704
|
-
if (Array.isArray(schema)) return schema.map((item) => cleanJsonSchema$1(item));
|
|
1705
|
-
const obj = schema;
|
|
1706
|
-
const cleaned = {};
|
|
1707
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
1708
|
-
if (!GEMINI_ALLOWED_FIELDS$1.has(key)) continue;
|
|
1709
|
-
if (key === "type") {
|
|
1710
|
-
const result = convertTypeValue$1(value);
|
|
1711
|
-
if (result) {
|
|
1712
|
-
cleaned.type = result.type;
|
|
1713
|
-
if (result.nullable) cleaned.nullable = true;
|
|
1714
|
-
}
|
|
1715
|
-
} else if (key === "properties" && typeof value === "object" && value) cleaned.properties = cleanProperties$1(value);
|
|
1716
|
-
else if (typeof value === "object" && value !== null) cleaned[key] = cleanJsonSchema$1(value);
|
|
1717
|
-
else cleaned[key] = value;
|
|
568
|
+
function supportsAnsi() {
|
|
569
|
+
return process.stdout.isTTY;
|
|
1718
570
|
}
|
|
1719
|
-
|
|
1720
|
-
}
|
|
1721
|
-
/**
|
|
1722
|
-
* Convert tools to Antigravity format
|
|
1723
|
-
* All tools should be in a single object with functionDeclarations array
|
|
1724
|
-
*/
|
|
1725
|
-
function convertTools$1(tools) {
|
|
1726
|
-
if (!tools || tools.length === 0) return void 0;
|
|
1727
|
-
const functionDeclarations = [];
|
|
1728
|
-
for (const tool of tools) {
|
|
1729
|
-
const t = tool;
|
|
1730
|
-
if (t.type === "function" && t.function) functionDeclarations.push({
|
|
1731
|
-
name: t.function.name,
|
|
1732
|
-
description: t.function.description || "",
|
|
1733
|
-
parameters: cleanJsonSchema$1(t.function.parameters) || {}
|
|
1734
|
-
});
|
|
571
|
+
function dim(text) {
|
|
572
|
+
return supportsAnsi() ? `\x1b[2m${text}\x1b[0m` : text;
|
|
1735
573
|
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
if (tools) body.tools = tools;
|
|
1756
|
-
if (isThinkingModel(request.model)) body.generationConfig = {
|
|
1757
|
-
...body.generationConfig,
|
|
1758
|
-
thinkingConfig: { includeThoughts: true }
|
|
1759
|
-
};
|
|
1760
|
-
return body;
|
|
1761
|
-
}
|
|
1762
|
-
/**
|
|
1763
|
-
* Build Antigravity request body
|
|
1764
|
-
* The Antigravity API expects a specific nested structure with request object
|
|
1765
|
-
*/
|
|
1766
|
-
function buildAntigravityRequestBody(request) {
|
|
1767
|
-
const innerRequest = buildStandardGeminiRequestBody(request);
|
|
1768
|
-
return {
|
|
1769
|
-
model: request.model,
|
|
1770
|
-
userAgent: "antigravity",
|
|
1771
|
-
requestId: `agent-${crypto.randomUUID()}`,
|
|
1772
|
-
request: innerRequest
|
|
1773
|
-
};
|
|
1774
|
-
}
|
|
1775
|
-
/**
|
|
1776
|
-
* Create error response
|
|
1777
|
-
*/
|
|
1778
|
-
function createErrorResponse$1(message, type, status, details) {
|
|
1779
|
-
const error = {
|
|
1780
|
-
message,
|
|
1781
|
-
type
|
|
1782
|
-
};
|
|
1783
|
-
if (details) error.details = details;
|
|
1784
|
-
return new Response(JSON.stringify({ error }), {
|
|
1785
|
-
status,
|
|
1786
|
-
headers: { "Content-Type": "application/json" }
|
|
1787
|
-
});
|
|
1788
|
-
}
|
|
1789
|
-
/**
|
|
1790
|
-
* Create chat completion with Antigravity or standard Gemini API
|
|
1791
|
-
* Priority: API Key > OAuth
|
|
1792
|
-
*/
|
|
1793
|
-
async function createAntigravityChatCompletion(request, requestHeaders) {
|
|
1794
|
-
const effectiveModel = maybeDowngradeModel(request.model, request.messages, requestHeaders);
|
|
1795
|
-
const effectiveRequest = effectiveModel !== request.model ? {
|
|
1796
|
-
...request,
|
|
1797
|
-
model: effectiveModel
|
|
1798
|
-
} : request;
|
|
1799
|
-
const apiKey = getApiKey();
|
|
1800
|
-
if (apiKey) return await createWithApiKey(effectiveRequest, apiKey);
|
|
1801
|
-
const accessToken = await getValidAccessToken();
|
|
1802
|
-
if (!accessToken) return createErrorResponse$1("No valid authentication available. Please set GEMINI_API_KEY environment variable or run OAuth login.", "auth_error", 401);
|
|
1803
|
-
return await createWithOAuth(effectiveRequest, accessToken);
|
|
1804
|
-
}
|
|
1805
|
-
/**
|
|
1806
|
-
* Create chat completion using API Key (standard Gemini API)
|
|
1807
|
-
*/
|
|
1808
|
-
async function createWithApiKey(request, apiKey) {
|
|
1809
|
-
const endpoint = request.stream ? getGeminiStreamUrl(request.model, apiKey) : getGeminiNoStreamUrl(request.model, apiKey);
|
|
1810
|
-
const body = buildStandardGeminiRequestBody(request);
|
|
1811
|
-
consola.debug(`Gemini API request with model ${request.model}`);
|
|
1812
|
-
try {
|
|
1813
|
-
const response = await fetch(endpoint, {
|
|
1814
|
-
method: "POST",
|
|
1815
|
-
headers: { "Content-Type": "application/json" },
|
|
1816
|
-
body: JSON.stringify(body)
|
|
1817
|
-
});
|
|
1818
|
-
if (!response.ok) return (await handleApiError$1(response)).response;
|
|
1819
|
-
return request.stream ? transformStreamResponse$1(response, request.model) : await transformNonStreamResponse$1(response, request.model);
|
|
1820
|
-
} catch (error) {
|
|
1821
|
-
consola.error("Gemini API request error:", error);
|
|
1822
|
-
return createErrorResponse$1(`Request failed: ${String(error)}`, "request_error", 500);
|
|
574
|
+
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/gm;
|
|
575
|
+
function parse(src) {
|
|
576
|
+
const obj = {};
|
|
577
|
+
let lines = src.toString();
|
|
578
|
+
lines = lines.replace(/\r\n?/gm, "\n");
|
|
579
|
+
let match;
|
|
580
|
+
while ((match = LINE.exec(lines)) != null) {
|
|
581
|
+
const key = match[1];
|
|
582
|
+
let value = match[2] || "";
|
|
583
|
+
value = value.trim();
|
|
584
|
+
const maybeQuote = value[0];
|
|
585
|
+
value = value.replace(/^(['"`])([\s\S]*)\1$/gm, "$2");
|
|
586
|
+
if (maybeQuote === "\"") {
|
|
587
|
+
value = value.replace(/\\n/g, "\n");
|
|
588
|
+
value = value.replace(/\\r/g, "\r");
|
|
589
|
+
}
|
|
590
|
+
obj[key] = value;
|
|
591
|
+
}
|
|
592
|
+
return obj;
|
|
1823
593
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
*/
|
|
1834
|
-
const MAX_RETRIES$4 = 5;
|
|
1835
|
-
async function createWithOAuth(request, _initialAccessToken) {
|
|
1836
|
-
const endpoint = request.stream ? ANTIGRAVITY_STREAM_URL : ANTIGRAVITY_NO_STREAM_URL;
|
|
1837
|
-
const body = buildAntigravityRequestBody(request);
|
|
1838
|
-
const bodyString = JSON.stringify(body);
|
|
1839
|
-
for (let attempt = 0; attempt <= MAX_RETRIES$4; attempt++) {
|
|
1840
|
-
const family = getModelFamily(request.model);
|
|
1841
|
-
if (!canExecute(family)) {
|
|
1842
|
-
consola.warn(`[circuit-breaker] ${family} circuit OPEN, waiting for reset...`);
|
|
1843
|
-
await sleep(1e3);
|
|
1844
|
-
continue;
|
|
594
|
+
function _parseVault(options) {
|
|
595
|
+
options = options || {};
|
|
596
|
+
const vaultPath = _vaultPath(options);
|
|
597
|
+
options.path = vaultPath;
|
|
598
|
+
const result = DotenvModule.configDotenv(options);
|
|
599
|
+
if (!result.parsed) {
|
|
600
|
+
const err = /* @__PURE__ */ new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`);
|
|
601
|
+
err.code = "MISSING_DATA";
|
|
602
|
+
throw err;
|
|
1845
603
|
}
|
|
1846
|
-
const
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
"User-Agent": ANTIGRAVITY_USER_AGENT$1,
|
|
1854
|
-
Authorization: `Bearer ${accessToken}`,
|
|
1855
|
-
"Content-Type": "application/json",
|
|
1856
|
-
"Accept-Encoding": "gzip"
|
|
1857
|
-
},
|
|
1858
|
-
body: bodyString
|
|
1859
|
-
});
|
|
1860
|
-
if (response.ok) {
|
|
1861
|
-
recordSuccess(family);
|
|
1862
|
-
recordAccountSuccess(await getCurrentAccountIndex());
|
|
1863
|
-
return request.stream ? transformStreamResponse$1(response, request.model) : await transformNonStreamResponse$1(response, request.model);
|
|
1864
|
-
}
|
|
1865
|
-
const errorResult = await handleApiError$1(response);
|
|
1866
|
-
if (errorResult.shouldRetry && attempt < MAX_RETRIES$4) {
|
|
1867
|
-
recordFailure(family);
|
|
1868
|
-
recordAccountFailure(await getCurrentAccountIndex());
|
|
1869
|
-
const backoffDelay = getBackoffDelay(family, errorResult.retryDelayMs);
|
|
1870
|
-
consola.info(`Rate limited, retrying in ${backoffDelay}ms (attempt ${attempt + 1}/${MAX_RETRIES$4})`);
|
|
1871
|
-
await sleep(backoffDelay);
|
|
1872
|
-
continue;
|
|
1873
|
-
}
|
|
1874
|
-
return errorResult.response;
|
|
604
|
+
const keys = _dotenvKey(options).split(",");
|
|
605
|
+
const length = keys.length;
|
|
606
|
+
let decrypted;
|
|
607
|
+
for (let i = 0; i < length; i++) try {
|
|
608
|
+
const attrs = _instructions(result, keys[i].trim());
|
|
609
|
+
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key);
|
|
610
|
+
break;
|
|
1875
611
|
} catch (error) {
|
|
1876
|
-
|
|
1877
|
-
if (attempt < MAX_RETRIES$4) {
|
|
1878
|
-
await sleep(500);
|
|
1879
|
-
continue;
|
|
1880
|
-
}
|
|
1881
|
-
return createErrorResponse$1(`Request failed: ${String(error)}`, "request_error", 500);
|
|
612
|
+
if (i + 1 >= length) throw error;
|
|
1882
613
|
}
|
|
614
|
+
return DotenvModule.parse(decrypted);
|
|
1883
615
|
}
|
|
1884
|
-
|
|
1885
|
-
}
|
|
1886
|
-
/**
|
|
1887
|
-
* Handle API error response
|
|
1888
|
-
*/
|
|
1889
|
-
async function handleApiError$1(response) {
|
|
1890
|
-
const errorText = await response.text();
|
|
1891
|
-
consola.error(`Antigravity error: ${response.status} ${errorText}`);
|
|
1892
|
-
if (response.status === 403) await disableCurrentAccount();
|
|
1893
|
-
if (response.status === 429 || response.status === 503) {
|
|
1894
|
-
await rotateAccount();
|
|
1895
|
-
return {
|
|
1896
|
-
shouldRetry: true,
|
|
1897
|
-
retryDelayMs: parseRetryDelay$3(errorText),
|
|
1898
|
-
response: createErrorResponse$1(`Antigravity API error: ${response.status}`, "api_error", response.status, errorText)
|
|
1899
|
-
};
|
|
616
|
+
function _warn(message) {
|
|
617
|
+
console.error(`[dotenv@${version}][WARN] ${message}`);
|
|
1900
618
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
* Transform Antigravity stream response to OpenAI format
|
|
1915
|
-
*/
|
|
1916
|
-
function transformStreamResponse$1(response, model) {
|
|
1917
|
-
const reader = response.body?.getReader();
|
|
1918
|
-
if (!reader) return new Response("No response body", { status: 500 });
|
|
1919
|
-
const encoder = new TextEncoder();
|
|
1920
|
-
const decoder = new TextDecoder();
|
|
1921
|
-
const requestId = generateRequestId();
|
|
1922
|
-
const stream = new ReadableStream({ async start(controller) {
|
|
1923
|
-
const state = createStreamState();
|
|
619
|
+
function _debug(message) {
|
|
620
|
+
console.log(`[dotenv@${version}][DEBUG] ${message}`);
|
|
621
|
+
}
|
|
622
|
+
function _log(message) {
|
|
623
|
+
console.log(`[dotenv@${version}] ${message}`);
|
|
624
|
+
}
|
|
625
|
+
function _dotenvKey(options) {
|
|
626
|
+
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) return options.DOTENV_KEY;
|
|
627
|
+
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) return process.env.DOTENV_KEY;
|
|
628
|
+
return "";
|
|
629
|
+
}
|
|
630
|
+
function _instructions(result, dotenvKey) {
|
|
631
|
+
let uri;
|
|
1924
632
|
try {
|
|
1925
|
-
|
|
1926
|
-
controller.enqueue(encoder.encode("data: [DONE]\n\n"));
|
|
1927
|
-
controller.close();
|
|
633
|
+
uri = new URL(dotenvKey);
|
|
1928
634
|
} catch (error) {
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
return new Response(stream, { headers: {
|
|
1934
|
-
"Content-Type": "text/event-stream",
|
|
1935
|
-
"Cache-Control": "no-cache",
|
|
1936
|
-
Connection: "keep-alive"
|
|
1937
|
-
} });
|
|
1938
|
-
}
|
|
1939
|
-
/**
|
|
1940
|
-
* Process stream and emit OpenAI format chunks
|
|
1941
|
-
*/
|
|
1942
|
-
async function processOpenAIStream(reader, decoder, state, controller, encoder, requestId, model) {
|
|
1943
|
-
while (true) {
|
|
1944
|
-
const { done, value } = await reader.read();
|
|
1945
|
-
if (done) break;
|
|
1946
|
-
const lines = processChunk(decoder.decode(value, { stream: true }), state);
|
|
1947
|
-
for (const line of lines) {
|
|
1948
|
-
const data = parseSSELine(line);
|
|
1949
|
-
if (!data) continue;
|
|
1950
|
-
const { candidates } = extractFromData(data);
|
|
1951
|
-
const candidate = candidates[0];
|
|
1952
|
-
const parts = candidate?.content?.parts ?? [];
|
|
1953
|
-
for (const part of parts) {
|
|
1954
|
-
const chunkData = buildOpenAIChunk(part, requestId, model, candidate?.finishReason);
|
|
1955
|
-
if (chunkData) controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunkData)}\n\n`));
|
|
635
|
+
if (error.code === "ERR_INVALID_URL") {
|
|
636
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");
|
|
637
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
638
|
+
throw err;
|
|
1956
639
|
}
|
|
640
|
+
throw error;
|
|
1957
641
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
function buildOpenAIChunk(part, requestId, model, finishReason) {
|
|
1964
|
-
const baseChunk = {
|
|
1965
|
-
id: requestId,
|
|
1966
|
-
object: "chat.completion.chunk",
|
|
1967
|
-
created: Math.floor(Date.now() / 1e3),
|
|
1968
|
-
model
|
|
1969
|
-
};
|
|
1970
|
-
if (part.thought && part.text) return {
|
|
1971
|
-
...baseChunk,
|
|
1972
|
-
choices: [{
|
|
1973
|
-
index: 0,
|
|
1974
|
-
delta: { reasoning_content: part.text },
|
|
1975
|
-
finish_reason: null
|
|
1976
|
-
}]
|
|
1977
|
-
};
|
|
1978
|
-
if (part.text && !part.thought) return {
|
|
1979
|
-
...baseChunk,
|
|
1980
|
-
choices: [{
|
|
1981
|
-
index: 0,
|
|
1982
|
-
delta: { content: part.text },
|
|
1983
|
-
finish_reason: finishReason === "STOP" ? "stop" : null
|
|
1984
|
-
}]
|
|
1985
|
-
};
|
|
1986
|
-
if (part.functionCall) return {
|
|
1987
|
-
...baseChunk,
|
|
1988
|
-
choices: [{
|
|
1989
|
-
index: 0,
|
|
1990
|
-
delta: { tool_calls: [{
|
|
1991
|
-
index: 0,
|
|
1992
|
-
id: `call_${Date.now()}`,
|
|
1993
|
-
type: "function",
|
|
1994
|
-
function: {
|
|
1995
|
-
name: part.functionCall.name,
|
|
1996
|
-
arguments: JSON.stringify(part.functionCall.args)
|
|
1997
|
-
}
|
|
1998
|
-
}] },
|
|
1999
|
-
finish_reason: null
|
|
2000
|
-
}]
|
|
2001
|
-
};
|
|
2002
|
-
return null;
|
|
2003
|
-
}
|
|
2004
|
-
/**
|
|
2005
|
-
* Transform Antigravity non-stream response to OpenAI format
|
|
2006
|
-
*/
|
|
2007
|
-
async function transformNonStreamResponse$1(response, model) {
|
|
2008
|
-
const rawData = await response.json();
|
|
2009
|
-
const data = rawData.response ?? rawData;
|
|
2010
|
-
const { content, reasoningContent, toolCalls } = extractNonStreamContent((data.candidates?.[0])?.content?.parts ?? []);
|
|
2011
|
-
const message = {
|
|
2012
|
-
role: "assistant",
|
|
2013
|
-
content: content || null
|
|
2014
|
-
};
|
|
2015
|
-
if (reasoningContent) message.reasoning_content = reasoningContent;
|
|
2016
|
-
if (toolCalls.length > 0) message.tool_calls = toolCalls;
|
|
2017
|
-
const openaiResponse = {
|
|
2018
|
-
id: generateRequestId(),
|
|
2019
|
-
object: "chat.completion",
|
|
2020
|
-
created: Math.floor(Date.now() / 1e3),
|
|
2021
|
-
model,
|
|
2022
|
-
choices: [{
|
|
2023
|
-
index: 0,
|
|
2024
|
-
message,
|
|
2025
|
-
finish_reason: "stop"
|
|
2026
|
-
}],
|
|
2027
|
-
usage: {
|
|
2028
|
-
prompt_tokens: data.usageMetadata?.promptTokenCount ?? 0,
|
|
2029
|
-
completion_tokens: data.usageMetadata?.candidatesTokenCount ?? 0,
|
|
2030
|
-
total_tokens: data.usageMetadata?.totalTokenCount ?? 0
|
|
642
|
+
const key = uri.password;
|
|
643
|
+
if (!key) {
|
|
644
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing key part");
|
|
645
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
646
|
+
throw err;
|
|
2031
647
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
name: part.functionCall.name,
|
|
2050
|
-
arguments: JSON.stringify(part.functionCall.args)
|
|
2051
|
-
}
|
|
2052
|
-
});
|
|
648
|
+
const environment = uri.searchParams.get("environment");
|
|
649
|
+
if (!environment) {
|
|
650
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: Missing environment part");
|
|
651
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
652
|
+
throw err;
|
|
653
|
+
}
|
|
654
|
+
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`;
|
|
655
|
+
const ciphertext = result.parsed[environmentKey];
|
|
656
|
+
if (!ciphertext) {
|
|
657
|
+
const err = /* @__PURE__ */ new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`);
|
|
658
|
+
err.code = "NOT_FOUND_DOTENV_ENVIRONMENT";
|
|
659
|
+
throw err;
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
ciphertext,
|
|
663
|
+
key
|
|
664
|
+
};
|
|
2053
665
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
/**
|
|
2063
|
-
* Antigravity Chat Completions Route
|
|
2064
|
-
*
|
|
2065
|
-
* OpenAI-compatible chat completions endpoint for Antigravity.
|
|
2066
|
-
*/
|
|
2067
|
-
const app$1 = new Hono();
|
|
2068
|
-
app$1.post("/", async (c) => {
|
|
2069
|
-
const body = await c.req.json();
|
|
2070
|
-
const response = await createAntigravityChatCompletion(body, c.req.raw.headers);
|
|
2071
|
-
const headers = new Headers(response.headers);
|
|
2072
|
-
if (body.stream) return new Response(response.body, {
|
|
2073
|
-
status: response.status,
|
|
2074
|
-
headers
|
|
2075
|
-
});
|
|
2076
|
-
return new Response(await response.text(), {
|
|
2077
|
-
status: response.status,
|
|
2078
|
-
headers
|
|
2079
|
-
});
|
|
2080
|
-
});
|
|
2081
|
-
const antigravityChatCompletionsRoute = app$1;
|
|
2082
|
-
//#endregion
|
|
2083
|
-
//#region src/lib/request-queue.ts
|
|
2084
|
-
var RequestQueue = class {
|
|
2085
|
-
queue = [];
|
|
2086
|
-
activeCount = 0;
|
|
2087
|
-
maxConcurrent;
|
|
2088
|
-
minDelayMs;
|
|
2089
|
-
lastRequestTime = 0;
|
|
2090
|
-
constructor(maxConcurrent = 2, minDelayMs = 300) {
|
|
2091
|
-
this.maxConcurrent = maxConcurrent;
|
|
2092
|
-
this.minDelayMs = minDelayMs;
|
|
666
|
+
function _vaultPath(options) {
|
|
667
|
+
let possibleVaultPath = null;
|
|
668
|
+
if (options && options.path && options.path.length > 0) if (Array.isArray(options.path)) {
|
|
669
|
+
for (const filepath of options.path) if (fs$1.existsSync(filepath)) possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
|
|
670
|
+
} else possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`;
|
|
671
|
+
else possibleVaultPath = path$1.resolve(process.cwd(), ".env.vault");
|
|
672
|
+
if (fs$1.existsSync(possibleVaultPath)) return possibleVaultPath;
|
|
673
|
+
return null;
|
|
2093
674
|
}
|
|
2094
|
-
|
|
2095
|
-
return
|
|
2096
|
-
this.queue.push({
|
|
2097
|
-
execute,
|
|
2098
|
-
resolve,
|
|
2099
|
-
reject
|
|
2100
|
-
});
|
|
2101
|
-
this.processQueue();
|
|
2102
|
-
});
|
|
675
|
+
function _resolveHome(envPath) {
|
|
676
|
+
return envPath[0] === "~" ? path$1.join(os$1.homedir(), envPath.slice(1)) : envPath;
|
|
2103
677
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
const
|
|
2107
|
-
if (!
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
if (
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
const result = await request.execute();
|
|
2114
|
-
request.resolve(result);
|
|
2115
|
-
} catch (error) {
|
|
2116
|
-
request.reject(error);
|
|
2117
|
-
} finally {
|
|
2118
|
-
this.activeCount--;
|
|
2119
|
-
this.processQueue();
|
|
2120
|
-
}
|
|
678
|
+
function _configVault(options) {
|
|
679
|
+
const debug = parseBoolean(process.env.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
680
|
+
const quiet = parseBoolean(process.env.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
681
|
+
if (debug || !quiet) _log("Loading env from encrypted .env.vault");
|
|
682
|
+
const parsed = DotenvModule._parseVault(options);
|
|
683
|
+
let processEnv = process.env;
|
|
684
|
+
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
685
|
+
DotenvModule.populate(processEnv, parsed, options);
|
|
686
|
+
return { parsed };
|
|
2121
687
|
}
|
|
2122
|
-
|
|
2123
|
-
const
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
function createMessageStart(messageId, model) {
|
|
2137
|
-
const event = {
|
|
2138
|
-
type: "message_start",
|
|
2139
|
-
message: {
|
|
2140
|
-
id: messageId,
|
|
2141
|
-
type: "message",
|
|
2142
|
-
role: "assistant",
|
|
2143
|
-
content: [],
|
|
2144
|
-
model,
|
|
2145
|
-
stop_reason: null,
|
|
2146
|
-
stop_sequence: null,
|
|
2147
|
-
usage: {
|
|
2148
|
-
input_tokens: 0,
|
|
2149
|
-
output_tokens: 0
|
|
2150
|
-
}
|
|
688
|
+
function configDotenv(options) {
|
|
689
|
+
const dotenvPath = path$1.resolve(process.cwd(), ".env");
|
|
690
|
+
let encoding = "utf8";
|
|
691
|
+
let processEnv = process.env;
|
|
692
|
+
if (options && options.processEnv != null) processEnv = options.processEnv;
|
|
693
|
+
let debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || options && options.debug);
|
|
694
|
+
let quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || options && options.quiet);
|
|
695
|
+
if (options && options.encoding) encoding = options.encoding;
|
|
696
|
+
else if (debug) _debug("No encoding is specified. UTF-8 is used by default");
|
|
697
|
+
let optionPaths = [dotenvPath];
|
|
698
|
+
if (options && options.path) if (!Array.isArray(options.path)) optionPaths = [_resolveHome(options.path)];
|
|
699
|
+
else {
|
|
700
|
+
optionPaths = [];
|
|
701
|
+
for (const filepath of options.path) optionPaths.push(_resolveHome(filepath));
|
|
2151
702
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
}
|
|
2161
|
-
/**
|
|
2162
|
-
* Create content_block_start event for thinking
|
|
2163
|
-
*/
|
|
2164
|
-
function createThinkingBlockStart(index) {
|
|
2165
|
-
const event = {
|
|
2166
|
-
type: "content_block_start",
|
|
2167
|
-
index,
|
|
2168
|
-
content_block: {
|
|
2169
|
-
type: "thinking",
|
|
2170
|
-
thinking: ""
|
|
703
|
+
let lastError;
|
|
704
|
+
const parsedAll = {};
|
|
705
|
+
for (const path of optionPaths) try {
|
|
706
|
+
const parsed = DotenvModule.parse(fs$1.readFileSync(path, { encoding }));
|
|
707
|
+
DotenvModule.populate(parsedAll, parsed, options);
|
|
708
|
+
} catch (e) {
|
|
709
|
+
if (debug) _debug(`Failed to load ${path} ${e.message}`);
|
|
710
|
+
lastError = e;
|
|
2171
711
|
}
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
712
|
+
const populated = DotenvModule.populate(processEnv, parsedAll, options);
|
|
713
|
+
debug = parseBoolean(processEnv.DOTENV_CONFIG_DEBUG || debug);
|
|
714
|
+
quiet = parseBoolean(processEnv.DOTENV_CONFIG_QUIET || quiet);
|
|
715
|
+
if (debug || !quiet) {
|
|
716
|
+
const keysCount = Object.keys(populated).length;
|
|
717
|
+
const shortPaths = [];
|
|
718
|
+
for (const filePath of optionPaths) try {
|
|
719
|
+
const relative = path$1.relative(process.cwd(), filePath);
|
|
720
|
+
shortPaths.push(relative);
|
|
721
|
+
} catch (e) {
|
|
722
|
+
if (debug) _debug(`Failed to load ${filePath} ${e.message}`);
|
|
723
|
+
lastError = e;
|
|
724
|
+
}
|
|
725
|
+
_log(`injecting env (${keysCount}) from ${shortPaths.join(",")} ${dim(`-- tip: ${_getRandomTip()}`)}`);
|
|
2185
726
|
}
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
function
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
text: ""
|
|
727
|
+
if (lastError) return {
|
|
728
|
+
parsed: parsedAll,
|
|
729
|
+
error: lastError
|
|
730
|
+
};
|
|
731
|
+
else return { parsed: parsedAll };
|
|
732
|
+
}
|
|
733
|
+
function config(options) {
|
|
734
|
+
if (_dotenvKey(options).length === 0) return DotenvModule.configDotenv(options);
|
|
735
|
+
const vaultPath = _vaultPath(options);
|
|
736
|
+
if (!vaultPath) {
|
|
737
|
+
_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`);
|
|
738
|
+
return DotenvModule.configDotenv(options);
|
|
2199
739
|
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
740
|
+
return DotenvModule._configVault(options);
|
|
741
|
+
}
|
|
742
|
+
function decrypt(encrypted, keyStr) {
|
|
743
|
+
const key = Buffer.from(keyStr.slice(-64), "hex");
|
|
744
|
+
let ciphertext = Buffer.from(encrypted, "base64");
|
|
745
|
+
const nonce = ciphertext.subarray(0, 12);
|
|
746
|
+
const authTag = ciphertext.subarray(-16);
|
|
747
|
+
ciphertext = ciphertext.subarray(12, -16);
|
|
748
|
+
try {
|
|
749
|
+
const aesgcm = crypto.createDecipheriv("aes-256-gcm", key, nonce);
|
|
750
|
+
aesgcm.setAuthTag(authTag);
|
|
751
|
+
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`;
|
|
752
|
+
} catch (error) {
|
|
753
|
+
const isRange = error instanceof RangeError;
|
|
754
|
+
const invalidKeyLength = error.message === "Invalid key length";
|
|
755
|
+
const decryptionFailed = error.message === "Unsupported state or unable to authenticate data";
|
|
756
|
+
if (isRange || invalidKeyLength) {
|
|
757
|
+
const err = /* @__PURE__ */ new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");
|
|
758
|
+
err.code = "INVALID_DOTENV_KEY";
|
|
759
|
+
throw err;
|
|
760
|
+
} else if (decryptionFailed) {
|
|
761
|
+
const err = /* @__PURE__ */ new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");
|
|
762
|
+
err.code = "DECRYPTION_FAILED";
|
|
763
|
+
throw err;
|
|
764
|
+
} else throw error;
|
|
2213
765
|
}
|
|
2214
|
-
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
index
|
|
2224
|
-
};
|
|
2225
|
-
return encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(event)}\n\n`);
|
|
2226
|
-
}
|
|
2227
|
-
/**
|
|
2228
|
-
* Create content_block_start event for tool_use
|
|
2229
|
-
*/
|
|
2230
|
-
function createToolBlockStart(index, toolId, name) {
|
|
2231
|
-
const event = {
|
|
2232
|
-
type: "content_block_start",
|
|
2233
|
-
index,
|
|
2234
|
-
content_block: {
|
|
2235
|
-
type: "tool_use",
|
|
2236
|
-
id: toolId,
|
|
2237
|
-
name,
|
|
2238
|
-
input: {}
|
|
766
|
+
}
|
|
767
|
+
function populate(processEnv, parsed, options = {}) {
|
|
768
|
+
const debug = Boolean(options && options.debug);
|
|
769
|
+
const override = Boolean(options && options.override);
|
|
770
|
+
const populated = {};
|
|
771
|
+
if (typeof parsed !== "object") {
|
|
772
|
+
const err = /* @__PURE__ */ new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");
|
|
773
|
+
err.code = "OBJECT_REQUIRED";
|
|
774
|
+
throw err;
|
|
2239
775
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
delta: {
|
|
2251
|
-
type: "input_json_delta",
|
|
2252
|
-
partial_json: JSON.stringify(args)
|
|
776
|
+
for (const key of Object.keys(parsed)) if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
777
|
+
if (override === true) {
|
|
778
|
+
processEnv[key] = parsed[key];
|
|
779
|
+
populated[key] = parsed[key];
|
|
780
|
+
}
|
|
781
|
+
if (debug) if (override === true) _debug(`"${key}" is already defined and WAS overwritten`);
|
|
782
|
+
else _debug(`"${key}" is already defined and was NOT overwritten`);
|
|
783
|
+
} else {
|
|
784
|
+
processEnv[key] = parsed[key];
|
|
785
|
+
populated[key] = parsed[key];
|
|
2253
786
|
}
|
|
787
|
+
return populated;
|
|
788
|
+
}
|
|
789
|
+
const DotenvModule = {
|
|
790
|
+
configDotenv,
|
|
791
|
+
_configVault,
|
|
792
|
+
_parseVault,
|
|
793
|
+
config,
|
|
794
|
+
decrypt,
|
|
795
|
+
parse,
|
|
796
|
+
populate
|
|
2254
797
|
};
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
stop_reason: stopReason,
|
|
2265
|
-
stop_sequence: null
|
|
2266
|
-
},
|
|
2267
|
-
usage: { output_tokens: outputTokens }
|
|
2268
|
-
};
|
|
2269
|
-
return encoder.encode(`event: message_delta\ndata: ${JSON.stringify(event)}\n\n`);
|
|
2270
|
-
}
|
|
2271
|
-
/**
|
|
2272
|
-
* Generate a unique message ID
|
|
2273
|
-
*/
|
|
2274
|
-
function generateMessageId() {
|
|
2275
|
-
return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
2276
|
-
}
|
|
2277
|
-
/**
|
|
2278
|
-
* Generate a unique tool ID
|
|
2279
|
-
*/
|
|
2280
|
-
function generateToolId() {
|
|
2281
|
-
return `toolu_${Date.now()}`;
|
|
2282
|
-
}
|
|
798
|
+
module.exports.configDotenv = DotenvModule.configDotenv;
|
|
799
|
+
module.exports._configVault = DotenvModule._configVault;
|
|
800
|
+
module.exports._parseVault = DotenvModule._parseVault;
|
|
801
|
+
module.exports.config = DotenvModule.config;
|
|
802
|
+
module.exports.decrypt = DotenvModule.decrypt;
|
|
803
|
+
module.exports.parse = DotenvModule.parse;
|
|
804
|
+
module.exports.populate = DotenvModule.populate;
|
|
805
|
+
module.exports = DotenvModule;
|
|
806
|
+
}));
|
|
2283
807
|
//#endregion
|
|
2284
|
-
//#region
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
consola.info(`Rotating endpoint: ${ANTIGRAVITY_ENDPOINTS[oldIndex]} → ${ANTIGRAVITY_ENDPOINTS[currentEndpointIndex]}`);
|
|
2308
|
-
}
|
|
2309
|
-
const ANTIGRAVITY_USER_AGENT = "antigravity/1.11.3 windows/amd64";
|
|
2310
|
-
/**
|
|
2311
|
-
* Extract text from system content (can be string or array)
|
|
2312
|
-
*/
|
|
2313
|
-
function extractSystemText(system) {
|
|
2314
|
-
if (typeof system === "string") return system;
|
|
2315
|
-
return system.filter((block) => block.type === "text" && block.text).map((block) => block.text).join("\n\n");
|
|
2316
|
-
}
|
|
2317
|
-
/**
|
|
2318
|
-
* Convert Anthropic messages to Antigravity format
|
|
2319
|
-
*/
|
|
2320
|
-
function convertMessages(messages, system) {
|
|
2321
|
-
const contents = [];
|
|
2322
|
-
let systemInstruction;
|
|
2323
|
-
if (system) {
|
|
2324
|
-
const systemText = extractSystemText(system);
|
|
2325
|
-
if (systemText) systemInstruction = { parts: [{ text: systemText }] };
|
|
2326
|
-
}
|
|
2327
|
-
for (const message of messages) {
|
|
2328
|
-
const role = message.role === "assistant" ? "model" : "user";
|
|
2329
|
-
const parts = buildParts(message.content);
|
|
2330
|
-
if (parts.length > 0) contents.push({
|
|
2331
|
-
role,
|
|
2332
|
-
parts
|
|
2333
|
-
});
|
|
2334
|
-
}
|
|
2335
|
-
return {
|
|
2336
|
-
contents,
|
|
2337
|
-
systemInstruction
|
|
808
|
+
//#region node_modules/dotenv/lib/env-options.js
|
|
809
|
+
var require_env_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
810
|
+
const options = {};
|
|
811
|
+
if (process.env.DOTENV_CONFIG_ENCODING != null) options.encoding = process.env.DOTENV_CONFIG_ENCODING;
|
|
812
|
+
if (process.env.DOTENV_CONFIG_PATH != null) options.path = process.env.DOTENV_CONFIG_PATH;
|
|
813
|
+
if (process.env.DOTENV_CONFIG_QUIET != null) options.quiet = process.env.DOTENV_CONFIG_QUIET;
|
|
814
|
+
if (process.env.DOTENV_CONFIG_DEBUG != null) options.debug = process.env.DOTENV_CONFIG_DEBUG;
|
|
815
|
+
if (process.env.DOTENV_CONFIG_OVERRIDE != null) options.override = process.env.DOTENV_CONFIG_OVERRIDE;
|
|
816
|
+
if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY;
|
|
817
|
+
module.exports = options;
|
|
818
|
+
}));
|
|
819
|
+
//#endregion
|
|
820
|
+
//#region node_modules/dotenv/lib/cli-options.js
|
|
821
|
+
var require_cli_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
822
|
+
const re = /^dotenv_config_(encoding|path|quiet|debug|override|DOTENV_KEY)=(.+)$/;
|
|
823
|
+
module.exports = function optionMatcher(args) {
|
|
824
|
+
const options = args.reduce(function(acc, cur) {
|
|
825
|
+
const matches = cur.match(re);
|
|
826
|
+
if (matches) acc[matches[1]] = matches[2];
|
|
827
|
+
return acc;
|
|
828
|
+
}, {});
|
|
829
|
+
if (!("quiet" in options)) options.quiet = "true";
|
|
830
|
+
return options;
|
|
2338
831
|
};
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
"type",
|
|
2355
|
-
"description",
|
|
2356
|
-
"properties",
|
|
2357
|
-
"required",
|
|
2358
|
-
"items",
|
|
2359
|
-
"enum",
|
|
2360
|
-
"nullable"
|
|
2361
|
-
]);
|
|
2362
|
-
/**
|
|
2363
|
-
* Convert type value to Gemini format
|
|
2364
|
-
* Handles both string and array types (for nullable)
|
|
2365
|
-
*/
|
|
2366
|
-
function convertTypeValue(value) {
|
|
2367
|
-
if (typeof value === "string") return { type: value.toUpperCase() };
|
|
2368
|
-
if (Array.isArray(value)) {
|
|
2369
|
-
const nonNullType = value.find((t) => typeof t === "string" && t !== "null");
|
|
2370
|
-
if (nonNullType) return {
|
|
2371
|
-
type: nonNullType.toUpperCase(),
|
|
2372
|
-
nullable: true
|
|
832
|
+
}));
|
|
833
|
+
//#endregion
|
|
834
|
+
//#region node_modules/dotenv/config.js
|
|
835
|
+
(function() {
|
|
836
|
+
require_main().config(Object.assign({}, require_env_options(), require_cli_options()(process.argv)));
|
|
837
|
+
})();
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/lib/proxy.ts
|
|
840
|
+
function initProxyFromEnv() {
|
|
841
|
+
if (typeof Bun !== "undefined") return;
|
|
842
|
+
try {
|
|
843
|
+
const agentOptions = {
|
|
844
|
+
keepAliveTimeout: 3e4,
|
|
845
|
+
keepAliveMaxTimeout: 6e4,
|
|
846
|
+
connect: { timeout: 15e3 }
|
|
2373
847
|
};
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
848
|
+
const direct = new Agent(agentOptions);
|
|
849
|
+
const proxies = /* @__PURE__ */ new Map();
|
|
850
|
+
setGlobalDispatcher({
|
|
851
|
+
dispatch(options, handler) {
|
|
852
|
+
try {
|
|
853
|
+
const origin = typeof options.origin === "string" ? new URL(options.origin) : options.origin;
|
|
854
|
+
const raw = getProxyForUrl(origin.toString());
|
|
855
|
+
const proxyUrl = raw && raw.length > 0 ? raw : void 0;
|
|
856
|
+
if (!proxyUrl) {
|
|
857
|
+
consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
|
|
858
|
+
return direct.dispatch(options, handler);
|
|
859
|
+
}
|
|
860
|
+
let agent = proxies.get(proxyUrl);
|
|
861
|
+
if (!agent) {
|
|
862
|
+
agent = new ProxyAgent({
|
|
863
|
+
uri: proxyUrl,
|
|
864
|
+
...agentOptions
|
|
865
|
+
});
|
|
866
|
+
proxies.set(proxyUrl, agent);
|
|
867
|
+
}
|
|
868
|
+
let label = proxyUrl;
|
|
869
|
+
try {
|
|
870
|
+
const u = new URL(proxyUrl);
|
|
871
|
+
label = `${u.protocol}//${u.host}`;
|
|
872
|
+
} catch {}
|
|
873
|
+
consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`);
|
|
874
|
+
return agent.dispatch(options, handler);
|
|
875
|
+
} catch {
|
|
876
|
+
return direct.dispatch(options, handler);
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
close() {
|
|
880
|
+
return direct.close();
|
|
881
|
+
},
|
|
882
|
+
destroy() {
|
|
883
|
+
return direct.destroy();
|
|
2403
884
|
}
|
|
2404
|
-
} else if (key === "properties" && typeof value === "object" && value) cleaned.properties = cleanProperties(value);
|
|
2405
|
-
else if (typeof value === "object" && value !== null) cleaned[key] = cleanJsonSchema(value);
|
|
2406
|
-
else cleaned[key] = value;
|
|
2407
|
-
}
|
|
2408
|
-
return cleaned;
|
|
2409
|
-
}
|
|
2410
|
-
/**
|
|
2411
|
-
* Convert tools to Antigravity format
|
|
2412
|
-
* All tools should be in a single object with functionDeclarations array
|
|
2413
|
-
*/
|
|
2414
|
-
function convertTools(tools) {
|
|
2415
|
-
if (!tools || tools.length === 0) return void 0;
|
|
2416
|
-
const functionDeclarations = [];
|
|
2417
|
-
for (const tool of tools) {
|
|
2418
|
-
const t = tool;
|
|
2419
|
-
functionDeclarations.push({
|
|
2420
|
-
name: t.name,
|
|
2421
|
-
description: t.description || "",
|
|
2422
|
-
parameters: cleanJsonSchema(t.input_schema) || {}
|
|
2423
885
|
});
|
|
886
|
+
consola.debug("HTTP proxy configured from environment (per-URL)");
|
|
887
|
+
} catch (err) {
|
|
888
|
+
consola.debug("Proxy setup skipped:", err);
|
|
2424
889
|
}
|
|
2425
|
-
return [{ functionDeclarations }];
|
|
2426
|
-
}
|
|
2427
|
-
/**
|
|
2428
|
-
* Build Antigravity request body
|
|
2429
|
-
* The Antigravity API expects a specific nested structure with request object
|
|
2430
|
-
*/
|
|
2431
|
-
function buildGeminiRequest(request, projectId) {
|
|
2432
|
-
const { contents, systemInstruction } = convertMessages(request.messages, request.system);
|
|
2433
|
-
const tools = convertTools(request.tools);
|
|
2434
|
-
const innerRequest = {
|
|
2435
|
-
contents,
|
|
2436
|
-
generationConfig: {
|
|
2437
|
-
temperature: request.temperature ?? 1,
|
|
2438
|
-
topP: request.top_p ?? .85,
|
|
2439
|
-
topK: request.top_k ?? 50,
|
|
2440
|
-
maxOutputTokens: request.max_tokens ?? 8096
|
|
2441
|
-
}
|
|
2442
|
-
};
|
|
2443
|
-
if (systemInstruction) innerRequest.systemInstruction = systemInstruction;
|
|
2444
|
-
if (tools) innerRequest.tools = tools;
|
|
2445
|
-
if (isThinkingModel(request.model)) innerRequest.generationConfig = {
|
|
2446
|
-
...innerRequest.generationConfig,
|
|
2447
|
-
thinkingConfig: { includeThoughts: true }
|
|
2448
|
-
};
|
|
2449
|
-
const result = {
|
|
2450
|
-
model: request.model,
|
|
2451
|
-
userAgent: "antigravity",
|
|
2452
|
-
requestId: `agent-${crypto.randomUUID()}`,
|
|
2453
|
-
request: innerRequest
|
|
2454
|
-
};
|
|
2455
|
-
if (projectId) result.project = projectId;
|
|
2456
|
-
return result;
|
|
2457
|
-
}
|
|
2458
|
-
/**
|
|
2459
|
-
* Create error response
|
|
2460
|
-
*/
|
|
2461
|
-
function createErrorResponse(type, message, status) {
|
|
2462
|
-
return new Response(JSON.stringify({
|
|
2463
|
-
type: "error",
|
|
2464
|
-
error: {
|
|
2465
|
-
type,
|
|
2466
|
-
message
|
|
2467
|
-
}
|
|
2468
|
-
}), {
|
|
2469
|
-
status,
|
|
2470
|
-
headers: { "Content-Type": "application/json" }
|
|
2471
|
-
});
|
|
2472
890
|
}
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
* - Endpoint fallback (daily → prod)
|
|
2479
|
-
* - Circuit breaker per model family (CLOSED/OPEN/HALF_OPEN)
|
|
2480
|
-
* - Exponential backoff for consecutive errors
|
|
2481
|
-
* - Smart retry for short delays (≤5s on same endpoint)
|
|
2482
|
-
*/
|
|
2483
|
-
const MAX_RETRIES$3 = 5;
|
|
2484
|
-
const MAX_ENDPOINT_RETRIES = 2;
|
|
2485
|
-
async function executeAntigravityRequest(request, requestHeaders) {
|
|
2486
|
-
const effectiveModel = maybeDowngradeModel(request.model, request.messages, requestHeaders);
|
|
2487
|
-
const effectiveRequest = effectiveModel !== request.model ? {
|
|
2488
|
-
...request,
|
|
2489
|
-
model: effectiveModel
|
|
2490
|
-
} : request;
|
|
2491
|
-
const body = buildGeminiRequest(effectiveRequest, await getCurrentProjectId());
|
|
2492
|
-
let endpointRetries = 0;
|
|
2493
|
-
for (let attempt = 0; attempt <= MAX_RETRIES$3; attempt++) {
|
|
2494
|
-
const family = getModelFamily(effectiveRequest.model);
|
|
2495
|
-
if (!canExecute(family)) {
|
|
2496
|
-
consola.warn(`[circuit-breaker] ${family} circuit OPEN, waiting for reset...`);
|
|
2497
|
-
await sleep(1e3);
|
|
2498
|
-
continue;
|
|
2499
|
-
}
|
|
2500
|
-
const host = getCurrentHost();
|
|
2501
|
-
const endpoint = effectiveRequest.stream ? getStreamUrl(host) : getNoStreamUrl(host);
|
|
2502
|
-
const accessToken = await getValidAccessToken();
|
|
2503
|
-
if (!accessToken) return createErrorResponse("authentication_error", "No valid Antigravity access token available.", 401);
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/lib/shell.ts
|
|
893
|
+
function getShell() {
|
|
894
|
+
const { platform, ppid, env } = process$1;
|
|
895
|
+
if (platform === "win32") {
|
|
2504
896
|
try {
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
Host: host,
|
|
2509
|
-
"User-Agent": ANTIGRAVITY_USER_AGENT,
|
|
2510
|
-
Authorization: `Bearer ${accessToken}`,
|
|
2511
|
-
"Content-Type": "application/json",
|
|
2512
|
-
"Accept-Encoding": "gzip"
|
|
2513
|
-
},
|
|
2514
|
-
body: JSON.stringify(body)
|
|
2515
|
-
});
|
|
2516
|
-
if (response.ok) {
|
|
2517
|
-
recordSuccess(getModelFamily(effectiveRequest.model));
|
|
2518
|
-
recordAccountSuccess(await getCurrentAccountIndex());
|
|
2519
|
-
return effectiveRequest.stream ? transformStreamResponse(response, effectiveRequest.model) : await transformNonStreamResponse(response, effectiveRequest.model);
|
|
2520
|
-
}
|
|
2521
|
-
const errorResult = await handleApiError(response, effectiveRequest.model);
|
|
2522
|
-
if (errorResult.shouldRetry && attempt < MAX_RETRIES$3) {
|
|
2523
|
-
const family = getModelFamily(effectiveRequest.model);
|
|
2524
|
-
recordFailure(family);
|
|
2525
|
-
recordAccountFailure(await getCurrentAccountIndex());
|
|
2526
|
-
const backoffDelay = getBackoffDelay(family, errorResult.retryDelayMs);
|
|
2527
|
-
if (backoffDelay <= 5e3 || endpointRetries >= MAX_ENDPOINT_RETRIES) {
|
|
2528
|
-
consola.info(`Rate limited, retrying in ${backoffDelay}ms (attempt ${attempt + 1}/${MAX_RETRIES$3})`);
|
|
2529
|
-
await sleep(backoffDelay);
|
|
2530
|
-
} else {
|
|
2531
|
-
rotateEndpoint();
|
|
2532
|
-
endpointRetries++;
|
|
2533
|
-
consola.info(`Switching endpoint, retrying in ${errorResult.retryDelayMs}ms`);
|
|
2534
|
-
await sleep(errorResult.retryDelayMs);
|
|
2535
|
-
}
|
|
2536
|
-
continue;
|
|
2537
|
-
}
|
|
2538
|
-
return errorResult.response;
|
|
2539
|
-
} catch (error) {
|
|
2540
|
-
consola.error("Antigravity request error:", error);
|
|
2541
|
-
if (attempt < MAX_RETRIES$3) {
|
|
2542
|
-
if (endpointRetries < MAX_ENDPOINT_RETRIES) {
|
|
2543
|
-
rotateEndpoint();
|
|
2544
|
-
endpointRetries++;
|
|
2545
|
-
}
|
|
2546
|
-
await sleep(500);
|
|
2547
|
-
continue;
|
|
2548
|
-
}
|
|
2549
|
-
return createErrorResponse("api_error", `Request failed: ${String(error)}`, 500);
|
|
897
|
+
if (execSync(`wmic process get ParentProcessId,Name | findstr "${ppid}"`, { stdio: "pipe" }).toString().toLowerCase().includes("powershell.exe")) return "powershell";
|
|
898
|
+
} catch {
|
|
899
|
+
return "cmd";
|
|
2550
900
|
}
|
|
901
|
+
return "cmd";
|
|
902
|
+
} else {
|
|
903
|
+
const shellPath = env.SHELL;
|
|
904
|
+
if (shellPath) {
|
|
905
|
+
if (shellPath.endsWith("zsh")) return "zsh";
|
|
906
|
+
if (shellPath.endsWith("fish")) return "fish";
|
|
907
|
+
if (shellPath.endsWith("bash")) return "bash";
|
|
908
|
+
}
|
|
909
|
+
return "sh";
|
|
2551
910
|
}
|
|
2552
|
-
return createErrorResponse("api_error", "Max retries exceeded", 429);
|
|
2553
|
-
}
|
|
2554
|
-
async function createAntigravityMessages(request, requestHeaders) {
|
|
2555
|
-
return antigravityQueue.enqueue(() => executeAntigravityRequest(request, requestHeaders));
|
|
2556
|
-
}
|
|
2557
|
-
/**
|
|
2558
|
-
* Handle API error response
|
|
2559
|
-
*/
|
|
2560
|
-
async function handleApiError(response, _model) {
|
|
2561
|
-
const errorText = await response.text();
|
|
2562
|
-
consola.error(`Antigravity error: ${response.status} ${errorText}`);
|
|
2563
|
-
if (response.status === 403) await disableCurrentAccount();
|
|
2564
|
-
if (response.status === 429 || response.status === 503) {
|
|
2565
|
-
await rotateAccount();
|
|
2566
|
-
return {
|
|
2567
|
-
shouldRetry: true,
|
|
2568
|
-
retryDelayMs: parseRetryDelay$3(errorText),
|
|
2569
|
-
response: createErrorResponse("api_error", `Antigravity API error: ${response.status}`, response.status)
|
|
2570
|
-
};
|
|
2571
|
-
}
|
|
2572
|
-
return {
|
|
2573
|
-
shouldRetry: false,
|
|
2574
|
-
retryDelayMs: 0,
|
|
2575
|
-
response: createErrorResponse("api_error", `Antigravity API error: ${response.status}`, response.status)
|
|
2576
|
-
};
|
|
2577
911
|
}
|
|
2578
912
|
/**
|
|
2579
|
-
*
|
|
913
|
+
* Generates a copy-pasteable script to set multiple environment variables
|
|
914
|
+
* and run a subsequent command.
|
|
915
|
+
* @param {EnvVars} envVars - An object of environment variables to set.
|
|
916
|
+
* @param {string} commandToRun - The command to run after setting the variables.
|
|
917
|
+
* @returns {string} The formatted script string.
|
|
2580
918
|
*/
|
|
2581
|
-
function
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
case "
|
|
2587
|
-
|
|
2588
|
-
break;
|
|
2589
|
-
case "
|
|
2590
|
-
|
|
2591
|
-
break;
|
|
2592
|
-
case "
|
|
2593
|
-
|
|
2594
|
-
break;
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
case "text_stop":
|
|
2599
|
-
controller.enqueue(createBlockStop(event.index));
|
|
2600
|
-
break;
|
|
2601
|
-
case "tool_use":
|
|
2602
|
-
emitToolUseEvents(event, controller);
|
|
2603
|
-
break;
|
|
2604
|
-
case "usage":
|
|
2605
|
-
state.inputTokens = event.inputTokens;
|
|
2606
|
-
state.outputTokens = event.outputTokens;
|
|
2607
|
-
break;
|
|
2608
|
-
case "finish":
|
|
2609
|
-
controller.enqueue(createMessageDelta(event.stopReason, state.outputTokens));
|
|
919
|
+
function generateEnvScript(envVars, commandToRun = "") {
|
|
920
|
+
const shell = getShell();
|
|
921
|
+
const filteredEnvVars = Object.entries(envVars).filter(([, value]) => value !== void 0);
|
|
922
|
+
let commandBlock;
|
|
923
|
+
switch (shell) {
|
|
924
|
+
case "powershell":
|
|
925
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `$env:${key} = ${value}`).join("; ");
|
|
926
|
+
break;
|
|
927
|
+
case "cmd":
|
|
928
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `set ${key}=${value}`).join(" & ");
|
|
929
|
+
break;
|
|
930
|
+
case "fish":
|
|
931
|
+
commandBlock = filteredEnvVars.map(([key, value]) => `set -gx ${key} ${value}`).join("; ");
|
|
932
|
+
break;
|
|
933
|
+
default: {
|
|
934
|
+
const assignments = filteredEnvVars.map(([key, value]) => `${key}=${value}`).join(" ");
|
|
935
|
+
commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : "";
|
|
2610
936
|
break;
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
/**
|
|
2614
|
-
* Emit tool use events
|
|
2615
|
-
*/
|
|
2616
|
-
function emitToolUseEvents(event, controller) {
|
|
2617
|
-
const toolId = generateToolId();
|
|
2618
|
-
controller.enqueue(createToolBlockStart(event.index, toolId, event.name));
|
|
2619
|
-
controller.enqueue(createToolDelta(event.index, event.args));
|
|
2620
|
-
controller.enqueue(createBlockStop(event.index));
|
|
2621
|
-
}
|
|
2622
|
-
/**
|
|
2623
|
-
* Transform Antigravity stream response to Anthropic format
|
|
2624
|
-
*/
|
|
2625
|
-
function transformStreamResponse(response, model) {
|
|
2626
|
-
const reader = response.body?.getReader();
|
|
2627
|
-
if (!reader) return new Response("No response body", { status: 500 });
|
|
2628
|
-
const decoder = new TextDecoder();
|
|
2629
|
-
const messageId = generateMessageId();
|
|
2630
|
-
const stream = new ReadableStream({ async start(controller) {
|
|
2631
|
-
const state = createStreamState();
|
|
2632
|
-
controller.enqueue(createMessageStart(messageId, model));
|
|
2633
|
-
try {
|
|
2634
|
-
await processStream(reader, decoder, state, controller);
|
|
2635
|
-
controller.enqueue(createMessageStop());
|
|
2636
|
-
controller.close();
|
|
2637
|
-
} catch (error) {
|
|
2638
|
-
consola.error("Stream transform error:", error);
|
|
2639
|
-
controller.error(error);
|
|
2640
937
|
}
|
|
2641
|
-
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
"Cache-Control": "no-cache",
|
|
2645
|
-
Connection: "keep-alive"
|
|
2646
|
-
} });
|
|
938
|
+
}
|
|
939
|
+
if (commandBlock && commandToRun) return `${commandBlock}${shell === "cmd" ? " & " : " && "}${commandToRun}`;
|
|
940
|
+
return commandBlock || commandToRun;
|
|
2647
941
|
}
|
|
942
|
+
//#endregion
|
|
943
|
+
//#region src/lib/api-key-auth.ts
|
|
2648
944
|
/**
|
|
2649
|
-
*
|
|
945
|
+
* Retrieve an API key from the incoming request.
|
|
946
|
+
*
|
|
947
|
+
* Checks common locations where clients supply keys (Authorization Bearer header, `x-api-key` header, or `apiKey` query parameter) and returns the first one found.
|
|
948
|
+
*
|
|
949
|
+
* @returns The extracted API key, or `undefined` if no key is present.
|
|
2650
950
|
*/
|
|
2651
|
-
function
|
|
2652
|
-
const
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
return false;
|
|
951
|
+
function extractApiKey(c) {
|
|
952
|
+
const authHeader = c.req.header("authorization");
|
|
953
|
+
if (authHeader?.startsWith("Bearer ")) return authHeader.slice(7);
|
|
954
|
+
const anthropicKey = c.req.header("x-api-key");
|
|
955
|
+
if (anthropicKey) return anthropicKey;
|
|
956
|
+
const queryKey = c.req.query("apiKey");
|
|
957
|
+
if (queryKey) return queryKey;
|
|
2659
958
|
}
|
|
2660
959
|
/**
|
|
2661
|
-
*
|
|
960
|
+
* API key authentication middleware
|
|
961
|
+
* Validates that the request contains a valid API key if API keys are configured
|
|
2662
962
|
*/
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
const { done, value } = await reader.read();
|
|
2668
|
-
if (done) break;
|
|
2669
|
-
const lines = processChunk(decoder.decode(value, { stream: true }), state);
|
|
2670
|
-
for (const line of lines) {
|
|
2671
|
-
if (finished) break;
|
|
2672
|
-
const data = parseSSELine(line);
|
|
2673
|
-
if (!data) continue;
|
|
2674
|
-
const { candidates, usage } = extractFromData(data);
|
|
2675
|
-
if (usage) {
|
|
2676
|
-
state.inputTokens = usage.promptTokenCount ?? state.inputTokens;
|
|
2677
|
-
state.outputTokens = usage.candidatesTokenCount ?? state.outputTokens;
|
|
2678
|
-
}
|
|
2679
|
-
if (candidates[0] && processCandidate(candidates[0], state, emit)) {
|
|
2680
|
-
finished = true;
|
|
2681
|
-
break;
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
963
|
+
const apiKeyAuthMiddleware = async (c, next) => {
|
|
964
|
+
if (!state.apiKeys || state.apiKeys.length === 0) {
|
|
965
|
+
await next();
|
|
966
|
+
return;
|
|
2684
967
|
}
|
|
2685
|
-
|
|
968
|
+
const providedKey = extractApiKey(c);
|
|
969
|
+
if (!providedKey) throw new HTTPException(401, { message: "API key required. Please provide a valid API key in the Authorization header (Bearer token) or x-api-key header." });
|
|
970
|
+
if (!state.apiKeys.includes(providedKey)) throw new HTTPException(401, { message: "Invalid API key. Please provide a valid API key." });
|
|
971
|
+
await next();
|
|
972
|
+
};
|
|
973
|
+
//#endregion
|
|
974
|
+
//#region src/lib/model-logger.ts
|
|
2686
975
|
/**
|
|
2687
|
-
*
|
|
976
|
+
* Get timestamp string in format HH:mm:ss
|
|
2688
977
|
*/
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
const data = rawData.response ?? rawData;
|
|
2692
|
-
const content = buildNonStreamContent((data.candidates?.[0])?.content?.parts ?? []);
|
|
2693
|
-
const anthropicResponse = {
|
|
2694
|
-
id: generateMessageId(),
|
|
2695
|
-
type: "message",
|
|
2696
|
-
role: "assistant",
|
|
2697
|
-
content,
|
|
2698
|
-
model,
|
|
2699
|
-
stop_reason: "end_turn",
|
|
2700
|
-
stop_sequence: null,
|
|
2701
|
-
usage: {
|
|
2702
|
-
input_tokens: data.usageMetadata?.promptTokenCount ?? 0,
|
|
2703
|
-
output_tokens: data.usageMetadata?.candidatesTokenCount ?? 0
|
|
2704
|
-
}
|
|
2705
|
-
};
|
|
2706
|
-
return new Response(JSON.stringify(anthropicResponse), { headers: { "Content-Type": "application/json" } });
|
|
978
|
+
function getTime() {
|
|
979
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
2707
980
|
}
|
|
2708
981
|
/**
|
|
2709
|
-
*
|
|
982
|
+
* Format duration in human readable format
|
|
2710
983
|
*/
|
|
2711
|
-
function
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
if (part.thought && part.text) content.push({
|
|
2715
|
-
type: "thinking",
|
|
2716
|
-
thinking: part.text
|
|
2717
|
-
});
|
|
2718
|
-
else if (part.text) content.push({
|
|
2719
|
-
type: "text",
|
|
2720
|
-
text: part.text
|
|
2721
|
-
});
|
|
2722
|
-
if (part.functionCall) content.push({
|
|
2723
|
-
type: "tool_use",
|
|
2724
|
-
id: generateToolId(),
|
|
2725
|
-
name: part.functionCall.name,
|
|
2726
|
-
input: part.functionCall.args
|
|
2727
|
-
});
|
|
2728
|
-
}
|
|
2729
|
-
return content;
|
|
984
|
+
function formatDuration(ms) {
|
|
985
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
986
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
2730
987
|
}
|
|
2731
|
-
//#endregion
|
|
2732
|
-
//#region src/routes/antigravity/messages/route.ts
|
|
2733
988
|
/**
|
|
2734
|
-
*
|
|
2735
|
-
*
|
|
2736
|
-
* Anthropic-compatible messages endpoint for Antigravity.
|
|
2737
|
-
* This enables Claude Code to use Antigravity as backend.
|
|
989
|
+
* Extract model name from request body
|
|
2738
990
|
*/
|
|
2739
|
-
|
|
2740
|
-
antigravityMessagesRoute.post("/", async (c) => {
|
|
2741
|
-
if (!state.antigravityMode) return c.json({ error: "Antigravity mode is not enabled. Start with --antigravity flag." }, 400);
|
|
991
|
+
async function extractModel(c) {
|
|
2742
992
|
try {
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
if (body.stream) {
|
|
2747
|
-
const headers = new Headers();
|
|
2748
|
-
headers.set("Content-Type", "text/event-stream");
|
|
2749
|
-
headers.set("Cache-Control", "no-cache");
|
|
2750
|
-
headers.set("Connection", "keep-alive");
|
|
2751
|
-
return new Response(response.body, {
|
|
2752
|
-
status: response.status,
|
|
2753
|
-
headers
|
|
2754
|
-
});
|
|
2755
|
-
}
|
|
2756
|
-
const data = await response.json();
|
|
2757
|
-
return c.json(data);
|
|
2758
|
-
} catch (error) {
|
|
2759
|
-
consola.error("Antigravity message error:", error);
|
|
2760
|
-
return c.json({
|
|
2761
|
-
type: "error",
|
|
2762
|
-
error: {
|
|
2763
|
-
type: "antigravity_error",
|
|
2764
|
-
message: error instanceof Error ? error.message : "Unknown error"
|
|
2765
|
-
}
|
|
2766
|
-
}, 500);
|
|
993
|
+
return (await c.req.raw.clone().json()).model;
|
|
994
|
+
} catch {
|
|
995
|
+
return;
|
|
2767
996
|
}
|
|
2768
|
-
}
|
|
2769
|
-
//#endregion
|
|
2770
|
-
//#region src/routes/antigravity/models/route.ts
|
|
997
|
+
}
|
|
2771
998
|
/**
|
|
2772
|
-
*
|
|
999
|
+
* Custom logger middleware that shows model name before timestamp
|
|
2773
1000
|
*
|
|
2774
|
-
*
|
|
1001
|
+
* Output format:
|
|
1002
|
+
* [model] HH:mm:ss <-- METHOD /path
|
|
1003
|
+
* [model] HH:mm:ss --> METHOD /path STATUS DURATION
|
|
2775
1004
|
*/
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
1005
|
+
function modelLogger() {
|
|
1006
|
+
return async (c, next) => {
|
|
1007
|
+
const method = c.req.method;
|
|
1008
|
+
const fullPath = `${c.req.path}${c.req.raw.url.includes("?") ? `?${c.req.raw.url.split("?")[1]}` : ""}`;
|
|
1009
|
+
let model;
|
|
1010
|
+
if (method === "POST" && c.req.header("content-type")?.includes("json")) model = await extractModel(c);
|
|
1011
|
+
const modelPrefix = model ? `[${model}] ` : "";
|
|
1012
|
+
const startTime = getTime();
|
|
1013
|
+
console.log(`${modelPrefix}${startTime} <-- ${method} ${fullPath}`);
|
|
1014
|
+
const start = Date.now();
|
|
1015
|
+
await next();
|
|
1016
|
+
const duration = Date.now() - start;
|
|
1017
|
+
const endTime = getTime();
|
|
1018
|
+
console.log(`${modelPrefix}${endTime} --> ${method} ${fullPath} ${c.res.status} ${formatDuration(duration)}`);
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
2782
1021
|
//#endregion
|
|
2783
1022
|
//#region src/lib/approval.ts
|
|
2784
1023
|
const awaitApproval = async () => {
|
|
@@ -2906,9 +1145,14 @@ async function handleCompletion$1(c) {
|
|
|
2906
1145
|
}
|
|
2907
1146
|
consola.debug("Streaming response");
|
|
2908
1147
|
return streamSSE(c, async (stream) => {
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
1148
|
+
try {
|
|
1149
|
+
for await (const chunk of response) {
|
|
1150
|
+
consola.debug("Streaming chunk:", JSON.stringify(chunk));
|
|
1151
|
+
await stream.writeSSE(chunk);
|
|
1152
|
+
}
|
|
1153
|
+
} catch (error) {
|
|
1154
|
+
const message = error.message || String(error);
|
|
1155
|
+
consola.warn(`SSE stream interrupted: ${message}`);
|
|
2912
1156
|
}
|
|
2913
1157
|
});
|
|
2914
1158
|
}
|
|
@@ -3506,6 +1750,15 @@ function translateChunkToAnthropicEvents(chunk, state) {
|
|
|
3506
1750
|
}
|
|
3507
1751
|
return events;
|
|
3508
1752
|
}
|
|
1753
|
+
function translateErrorToAnthropicErrorEvent() {
|
|
1754
|
+
return {
|
|
1755
|
+
type: "error",
|
|
1756
|
+
error: {
|
|
1757
|
+
type: "api_error",
|
|
1758
|
+
message: "An unexpected error occurred during streaming."
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
3509
1762
|
//#endregion
|
|
3510
1763
|
//#region src/routes/messages/handler.ts
|
|
3511
1764
|
async function handleCompletion(c) {
|
|
@@ -3524,13 +1777,23 @@ async function handleCompletion(c) {
|
|
|
3524
1777
|
contentBlockOpen: false,
|
|
3525
1778
|
toolCalls: {}
|
|
3526
1779
|
};
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
event
|
|
3533
|
-
|
|
1780
|
+
try {
|
|
1781
|
+
for await (const rawEvent of response) {
|
|
1782
|
+
if (rawEvent.data === "[DONE]") break;
|
|
1783
|
+
if (!rawEvent.data) continue;
|
|
1784
|
+
const events = translateChunkToAnthropicEvents(JSON.parse(rawEvent.data), streamState);
|
|
1785
|
+
for (const event of events) await stream.writeSSE({
|
|
1786
|
+
event: event.type,
|
|
1787
|
+
data: JSON.stringify(event)
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
} catch (error) {
|
|
1791
|
+
const message = error.message || String(error);
|
|
1792
|
+
consola.warn(`SSE stream interrupted: ${message}`);
|
|
1793
|
+
const errorEvent = translateErrorToAnthropicErrorEvent();
|
|
1794
|
+
await stream.writeSSE({
|
|
1795
|
+
event: errorEvent.type,
|
|
1796
|
+
data: JSON.stringify(errorEvent)
|
|
3534
1797
|
});
|
|
3535
1798
|
}
|
|
3536
1799
|
});
|
|
@@ -3596,446 +1859,34 @@ tokenRoute.get("/", (c) => {
|
|
|
3596
1859
|
const usageRoute = new Hono();
|
|
3597
1860
|
usageRoute.get("/", async (c) => {
|
|
3598
1861
|
try {
|
|
3599
|
-
if (state.antigravityMode) {
|
|
3600
|
-
const usage = await getAntigravityUsage();
|
|
3601
|
-
return c.json(usage);
|
|
3602
|
-
}
|
|
3603
|
-
if (state.zenMode) return c.json({
|
|
3604
|
-
error: "Usage statistics not available for Zen mode",
|
|
3605
|
-
message: "Please check your usage at https://console.opencode.ai",
|
|
3606
|
-
mode: "zen"
|
|
3607
|
-
}, 200);
|
|
3608
1862
|
const usage = await getCopilotUsage();
|
|
3609
1863
|
return c.json(usage);
|
|
3610
1864
|
} catch (error) {
|
|
3611
1865
|
consola.error("Error fetching usage:", error);
|
|
3612
|
-
if (state.antigravityMode) return c.json({ error: "Failed to fetch Antigravity usage" }, 500);
|
|
3613
|
-
if (state.zenMode) return c.json({ error: "Failed to fetch Zen usage" }, 500);
|
|
3614
1866
|
return c.json({ error: "Failed to fetch Copilot usage" }, 500);
|
|
3615
1867
|
}
|
|
3616
1868
|
});
|
|
3617
1869
|
//#endregion
|
|
3618
|
-
//#region src/services/zen/create-chat-completions.ts
|
|
3619
|
-
/**
|
|
3620
|
-
* OpenCode Zen Chat Completions Proxy
|
|
3621
|
-
*
|
|
3622
|
-
* Proxies chat completion requests to OpenCode Zen API.
|
|
3623
|
-
*/
|
|
3624
|
-
const MAX_RETRIES$2 = 5;
|
|
3625
|
-
const DEFAULT_RETRY_DELAY$2 = 500;
|
|
3626
|
-
/**
|
|
3627
|
-
* Parse retry delay from error response
|
|
3628
|
-
*/
|
|
3629
|
-
function parseRetryDelay$2(response, errorText) {
|
|
3630
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
3631
|
-
if (retryAfter) {
|
|
3632
|
-
const seconds = Number.parseInt(retryAfter, 10);
|
|
3633
|
-
if (!Number.isNaN(seconds)) return seconds * 1e3;
|
|
3634
|
-
}
|
|
3635
|
-
try {
|
|
3636
|
-
const errorData = JSON.parse(errorText);
|
|
3637
|
-
if (errorData.error?.retry_after) return errorData.error.retry_after * 1e3;
|
|
3638
|
-
} catch {}
|
|
3639
|
-
return DEFAULT_RETRY_DELAY$2;
|
|
3640
|
-
}
|
|
3641
|
-
/**
|
|
3642
|
-
* Create chat completions via OpenCode Zen
|
|
3643
|
-
*/
|
|
3644
|
-
async function createZenChatCompletions(request, signal) {
|
|
3645
|
-
const apiKey = state.zenApiKey;
|
|
3646
|
-
if (!apiKey) throw new Error("Zen API key not configured");
|
|
3647
|
-
consola.debug(`Zen chat completions request for model: ${request.model}`);
|
|
3648
|
-
for (let attempt = 0; attempt <= MAX_RETRIES$2; attempt++) try {
|
|
3649
|
-
const response = await fetch("https://opencode.ai/zen/v1/chat/completions", {
|
|
3650
|
-
method: "POST",
|
|
3651
|
-
headers: {
|
|
3652
|
-
"Content-Type": "application/json",
|
|
3653
|
-
Authorization: `Bearer ${apiKey}`
|
|
3654
|
-
},
|
|
3655
|
-
body: JSON.stringify(request),
|
|
3656
|
-
signal
|
|
3657
|
-
});
|
|
3658
|
-
if (response.ok) return response;
|
|
3659
|
-
const errorText = await response.text();
|
|
3660
|
-
if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES$2) {
|
|
3661
|
-
const retryDelay = parseRetryDelay$2(response, errorText);
|
|
3662
|
-
consola.info(`Zen rate limited (${response.status}), retrying in ${retryDelay}ms...`);
|
|
3663
|
-
await sleep(retryDelay);
|
|
3664
|
-
continue;
|
|
3665
|
-
}
|
|
3666
|
-
consola.error(`Zen API error: ${response.status} ${errorText}`);
|
|
3667
|
-
throw new Error(`Zen API error: ${response.status} ${errorText}`);
|
|
3668
|
-
} catch (error) {
|
|
3669
|
-
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
3670
|
-
if (attempt < MAX_RETRIES$2) {
|
|
3671
|
-
consola.warn(`Zen request failed, retrying... (${attempt + 1})`);
|
|
3672
|
-
await sleep(DEFAULT_RETRY_DELAY$2);
|
|
3673
|
-
continue;
|
|
3674
|
-
}
|
|
3675
|
-
throw error;
|
|
3676
|
-
}
|
|
3677
|
-
throw new Error("Max retries exceeded");
|
|
3678
|
-
}
|
|
3679
|
-
//#endregion
|
|
3680
|
-
//#region src/routes/zen/chat-completions/route.ts
|
|
3681
|
-
/**
|
|
3682
|
-
* OpenCode Zen Chat Completions Route
|
|
3683
|
-
*
|
|
3684
|
-
* Proxies OpenAI-format chat completion requests to Zen.
|
|
3685
|
-
*/
|
|
3686
|
-
const zenCompletionRoutes = new Hono();
|
|
3687
|
-
zenCompletionRoutes.post("/", async (c) => {
|
|
3688
|
-
if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
|
|
3689
|
-
try {
|
|
3690
|
-
const body = await c.req.json();
|
|
3691
|
-
consola.debug("Zen chat completion request:", body.model);
|
|
3692
|
-
const response = await createZenChatCompletions(body);
|
|
3693
|
-
if (body.stream) {
|
|
3694
|
-
const headers = new Headers();
|
|
3695
|
-
headers.set("Content-Type", "text/event-stream");
|
|
3696
|
-
headers.set("Cache-Control", "no-cache");
|
|
3697
|
-
headers.set("Connection", "keep-alive");
|
|
3698
|
-
return new Response(response.body, {
|
|
3699
|
-
status: response.status,
|
|
3700
|
-
headers
|
|
3701
|
-
});
|
|
3702
|
-
}
|
|
3703
|
-
const data = await response.json();
|
|
3704
|
-
return c.json(data);
|
|
3705
|
-
} catch (error) {
|
|
3706
|
-
consola.error("Zen chat completion error:", error);
|
|
3707
|
-
return c.json({ error: {
|
|
3708
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
3709
|
-
type: "zen_error"
|
|
3710
|
-
} }, 500);
|
|
3711
|
-
}
|
|
3712
|
-
});
|
|
3713
|
-
//#endregion
|
|
3714
|
-
//#region src/services/zen/create-messages.ts
|
|
3715
|
-
/**
|
|
3716
|
-
* OpenCode Zen Messages Proxy
|
|
3717
|
-
*
|
|
3718
|
-
* Proxies Anthropic-format message requests to OpenCode Zen API.
|
|
3719
|
-
*/
|
|
3720
|
-
const MAX_RETRIES$1 = 5;
|
|
3721
|
-
const DEFAULT_RETRY_DELAY$1 = 500;
|
|
3722
|
-
/**
|
|
3723
|
-
* Parse retry delay from error response headers or body
|
|
3724
|
-
*/
|
|
3725
|
-
function parseRetryDelay$1(response, errorText) {
|
|
3726
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
3727
|
-
if (retryAfter) {
|
|
3728
|
-
const seconds = Number.parseInt(retryAfter, 10);
|
|
3729
|
-
if (!Number.isNaN(seconds)) return seconds * 1e3;
|
|
3730
|
-
}
|
|
3731
|
-
try {
|
|
3732
|
-
const errorData = JSON.parse(errorText);
|
|
3733
|
-
if (errorData.error?.retry_after) return errorData.error.retry_after * 1e3;
|
|
3734
|
-
} catch {}
|
|
3735
|
-
return DEFAULT_RETRY_DELAY$1;
|
|
3736
|
-
}
|
|
3737
|
-
/**
|
|
3738
|
-
* Create messages via OpenCode Zen (Anthropic format)
|
|
3739
|
-
*/
|
|
3740
|
-
async function createZenMessages(request, signal) {
|
|
3741
|
-
const apiKey = state.zenApiKey;
|
|
3742
|
-
if (!apiKey) throw new Error("Zen API key not configured");
|
|
3743
|
-
consola.debug(`Zen messages request for model: ${request.model}`);
|
|
3744
|
-
for (let attempt = 0; attempt <= MAX_RETRIES$1; attempt++) try {
|
|
3745
|
-
const response = await fetch("https://opencode.ai/zen/v1/messages", {
|
|
3746
|
-
method: "POST",
|
|
3747
|
-
headers: {
|
|
3748
|
-
"Content-Type": "application/json",
|
|
3749
|
-
"x-api-key": apiKey,
|
|
3750
|
-
"anthropic-version": "2023-06-01"
|
|
3751
|
-
},
|
|
3752
|
-
body: JSON.stringify(request),
|
|
3753
|
-
signal
|
|
3754
|
-
});
|
|
3755
|
-
if (response.ok) return response;
|
|
3756
|
-
const errorText = await response.text();
|
|
3757
|
-
if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES$1) {
|
|
3758
|
-
const retryDelay = parseRetryDelay$1(response, errorText);
|
|
3759
|
-
consola.info(`Zen rate limited (${response.status}), retrying in ${retryDelay}ms...`);
|
|
3760
|
-
await sleep(retryDelay);
|
|
3761
|
-
continue;
|
|
3762
|
-
}
|
|
3763
|
-
consola.error(`Zen Messages API error: ${response.status} ${errorText}`);
|
|
3764
|
-
throw new Error(`Zen Messages API error: ${response.status} ${errorText}`);
|
|
3765
|
-
} catch (error) {
|
|
3766
|
-
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
3767
|
-
if (attempt < MAX_RETRIES$1) {
|
|
3768
|
-
consola.warn(`Zen request failed, retrying... (${attempt + 1})`);
|
|
3769
|
-
await sleep(DEFAULT_RETRY_DELAY$1);
|
|
3770
|
-
continue;
|
|
3771
|
-
}
|
|
3772
|
-
throw error;
|
|
3773
|
-
}
|
|
3774
|
-
throw new Error("Max retries exceeded");
|
|
3775
|
-
}
|
|
3776
|
-
//#endregion
|
|
3777
|
-
//#region src/routes/zen/messages/route.ts
|
|
3778
|
-
/**
|
|
3779
|
-
* OpenCode Zen Messages Route
|
|
3780
|
-
*
|
|
3781
|
-
* Proxies Anthropic-format message requests to Zen.
|
|
3782
|
-
* This enables Claude Code to use Zen as backend.
|
|
3783
|
-
*/
|
|
3784
|
-
const zenMessageRoutes = new Hono();
|
|
3785
|
-
zenMessageRoutes.post("/", async (c) => {
|
|
3786
|
-
if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
|
|
3787
|
-
try {
|
|
3788
|
-
const body = await c.req.json();
|
|
3789
|
-
consola.debug("Zen message request:", body.model);
|
|
3790
|
-
const response = await createZenMessages(body);
|
|
3791
|
-
if (body.stream) {
|
|
3792
|
-
const headers = new Headers();
|
|
3793
|
-
headers.set("Content-Type", "text/event-stream");
|
|
3794
|
-
headers.set("Cache-Control", "no-cache");
|
|
3795
|
-
headers.set("Connection", "keep-alive");
|
|
3796
|
-
return new Response(response.body, {
|
|
3797
|
-
status: response.status,
|
|
3798
|
-
headers
|
|
3799
|
-
});
|
|
3800
|
-
}
|
|
3801
|
-
const data = await response.json();
|
|
3802
|
-
return c.json(data);
|
|
3803
|
-
} catch (error) {
|
|
3804
|
-
consola.error("Zen message error:", error);
|
|
3805
|
-
return c.json({
|
|
3806
|
-
type: "error",
|
|
3807
|
-
error: {
|
|
3808
|
-
type: "zen_error",
|
|
3809
|
-
message: error instanceof Error ? error.message : "Unknown error"
|
|
3810
|
-
}
|
|
3811
|
-
}, 500);
|
|
3812
|
-
}
|
|
3813
|
-
});
|
|
3814
|
-
//#endregion
|
|
3815
|
-
//#region src/routes/zen/models/route.ts
|
|
3816
|
-
/**
|
|
3817
|
-
* OpenCode Zen Models Route
|
|
3818
|
-
*
|
|
3819
|
-
* Returns available models from Zen.
|
|
3820
|
-
*/
|
|
3821
|
-
const zenModelRoutes = new Hono();
|
|
3822
|
-
zenModelRoutes.get("/", async (c) => {
|
|
3823
|
-
if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
|
|
3824
|
-
try {
|
|
3825
|
-
if (state.zenModels) return c.json(state.zenModels);
|
|
3826
|
-
const { getZenModels } = await import("./get-models-onnSXkNI.js");
|
|
3827
|
-
const models = await getZenModels();
|
|
3828
|
-
state.zenModels = models;
|
|
3829
|
-
return c.json(models);
|
|
3830
|
-
} catch (error) {
|
|
3831
|
-
consola.error("Zen models error:", error);
|
|
3832
|
-
return c.json({ error: {
|
|
3833
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
3834
|
-
type: "zen_error"
|
|
3835
|
-
} }, 500);
|
|
3836
|
-
}
|
|
3837
|
-
});
|
|
3838
|
-
//#endregion
|
|
3839
|
-
//#region src/services/zen/create-responses.ts
|
|
3840
|
-
/**
|
|
3841
|
-
* OpenCode Zen Responses Proxy
|
|
3842
|
-
*
|
|
3843
|
-
* Proxies OpenAI Responses API requests to OpenCode Zen.
|
|
3844
|
-
* Used for GPT-5 series models with stateful, agentic tool-use.
|
|
3845
|
-
*/
|
|
3846
|
-
const MAX_RETRIES = 5;
|
|
3847
|
-
const DEFAULT_RETRY_DELAY = 500;
|
|
3848
|
-
/**
|
|
3849
|
-
* Parse retry delay from error response
|
|
3850
|
-
*/
|
|
3851
|
-
function parseRetryDelay(response, errorText) {
|
|
3852
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
3853
|
-
if (retryAfter) {
|
|
3854
|
-
const seconds = Number.parseInt(retryAfter, 10);
|
|
3855
|
-
if (!Number.isNaN(seconds)) return seconds * 1e3;
|
|
3856
|
-
}
|
|
3857
|
-
try {
|
|
3858
|
-
const errorData = JSON.parse(errorText);
|
|
3859
|
-
if (errorData.error?.retry_after) return errorData.error.retry_after * 1e3;
|
|
3860
|
-
} catch {}
|
|
3861
|
-
return DEFAULT_RETRY_DELAY;
|
|
3862
|
-
}
|
|
3863
|
-
/**
|
|
3864
|
-
* Create responses via OpenCode Zen (OpenAI Responses API format)
|
|
3865
|
-
*/
|
|
3866
|
-
async function createZenResponses(request, signal) {
|
|
3867
|
-
const apiKey = state.zenApiKey;
|
|
3868
|
-
if (!apiKey) throw new Error("Zen API key not configured");
|
|
3869
|
-
consola.debug(`Zen responses request for model: ${request.model}`);
|
|
3870
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) try {
|
|
3871
|
-
const response = await fetch("https://opencode.ai/zen/v1/responses", {
|
|
3872
|
-
method: "POST",
|
|
3873
|
-
headers: {
|
|
3874
|
-
"Content-Type": "application/json",
|
|
3875
|
-
Authorization: `Bearer ${apiKey}`
|
|
3876
|
-
},
|
|
3877
|
-
body: JSON.stringify(request),
|
|
3878
|
-
signal
|
|
3879
|
-
});
|
|
3880
|
-
if (response.ok) return response;
|
|
3881
|
-
const errorText = await response.text();
|
|
3882
|
-
if ((response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
|
|
3883
|
-
const retryDelay = parseRetryDelay(response, errorText);
|
|
3884
|
-
consola.info(`Zen rate limited (${response.status}), retrying in ${retryDelay}ms...`);
|
|
3885
|
-
await sleep(retryDelay);
|
|
3886
|
-
continue;
|
|
3887
|
-
}
|
|
3888
|
-
consola.error(`Zen Responses API error: ${response.status} ${errorText}`);
|
|
3889
|
-
throw new Error(`Zen Responses API error: ${response.status} ${errorText}`);
|
|
3890
|
-
} catch (error) {
|
|
3891
|
-
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
3892
|
-
if (attempt < MAX_RETRIES) {
|
|
3893
|
-
consola.warn(`Zen request failed, retrying... (${attempt + 1})`);
|
|
3894
|
-
await sleep(DEFAULT_RETRY_DELAY);
|
|
3895
|
-
continue;
|
|
3896
|
-
}
|
|
3897
|
-
throw error;
|
|
3898
|
-
}
|
|
3899
|
-
throw new Error("Max retries exceeded");
|
|
3900
|
-
}
|
|
3901
|
-
//#endregion
|
|
3902
|
-
//#region src/routes/zen/responses/route.ts
|
|
3903
|
-
/**
|
|
3904
|
-
* OpenCode Zen Responses Route
|
|
3905
|
-
*
|
|
3906
|
-
* Proxies OpenAI Responses API requests to Zen.
|
|
3907
|
-
* Used for GPT-5 series models.
|
|
3908
|
-
*/
|
|
3909
|
-
const zenResponsesRoutes = new Hono();
|
|
3910
|
-
zenResponsesRoutes.post("/", async (c) => {
|
|
3911
|
-
if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
|
|
3912
|
-
try {
|
|
3913
|
-
const body = await c.req.json();
|
|
3914
|
-
consola.debug("Zen responses request:", body.model);
|
|
3915
|
-
const response = await createZenResponses(body);
|
|
3916
|
-
if (body.stream) {
|
|
3917
|
-
const headers = new Headers();
|
|
3918
|
-
headers.set("Content-Type", "text/event-stream");
|
|
3919
|
-
headers.set("Cache-Control", "no-cache");
|
|
3920
|
-
headers.set("Connection", "keep-alive");
|
|
3921
|
-
return new Response(response.body, {
|
|
3922
|
-
status: response.status,
|
|
3923
|
-
headers
|
|
3924
|
-
});
|
|
3925
|
-
}
|
|
3926
|
-
const data = await response.json();
|
|
3927
|
-
return c.json(data);
|
|
3928
|
-
} catch (error) {
|
|
3929
|
-
consola.error("Zen responses error:", error);
|
|
3930
|
-
return c.json({ error: {
|
|
3931
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
3932
|
-
type: "zen_error"
|
|
3933
|
-
} }, 500);
|
|
3934
|
-
}
|
|
3935
|
-
});
|
|
3936
|
-
//#endregion
|
|
3937
1870
|
//#region src/server.ts
|
|
3938
1871
|
const server = new Hono();
|
|
3939
1872
|
server.use(modelLogger());
|
|
3940
1873
|
server.use(cors());
|
|
3941
1874
|
server.use(apiKeyAuthMiddleware);
|
|
3942
1875
|
server.get("/", (c) => c.text("Server running"));
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
url.pathname = url.pathname.slice(basePath.length) || "/";
|
|
3946
|
-
return new Request(url.toString(), c.req.raw);
|
|
3947
|
-
}
|
|
3948
|
-
server.all("/chat/completions/*", async (c) => {
|
|
3949
|
-
const req = createSubRequest(c, "/chat/completions");
|
|
3950
|
-
if (state.zenMode) return zenCompletionRoutes.fetch(req, c.env);
|
|
3951
|
-
if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(req, c.env);
|
|
3952
|
-
return completionRoutes.fetch(req, c.env);
|
|
3953
|
-
});
|
|
3954
|
-
server.all("/chat/completions", async (c) => {
|
|
3955
|
-
const req = createSubRequest(c, "/chat/completions");
|
|
3956
|
-
if (state.zenMode) return zenCompletionRoutes.fetch(req, c.env);
|
|
3957
|
-
if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(req, c.env);
|
|
3958
|
-
return completionRoutes.fetch(req, c.env);
|
|
3959
|
-
});
|
|
3960
|
-
server.all("/models/*", async (c) => {
|
|
3961
|
-
const req = createSubRequest(c, "/models");
|
|
3962
|
-
if (state.zenMode) return zenModelRoutes.fetch(req, c.env);
|
|
3963
|
-
if (state.antigravityMode) return antigravityModelsRoute.fetch(req, c.env);
|
|
3964
|
-
return modelRoutes.fetch(req, c.env);
|
|
3965
|
-
});
|
|
3966
|
-
server.all("/models", async (c) => {
|
|
3967
|
-
const req = createSubRequest(c, "/models");
|
|
3968
|
-
if (state.zenMode) return zenModelRoutes.fetch(req, c.env);
|
|
3969
|
-
if (state.antigravityMode) return antigravityModelsRoute.fetch(req, c.env);
|
|
3970
|
-
return modelRoutes.fetch(req, c.env);
|
|
3971
|
-
});
|
|
1876
|
+
server.route("/chat/completions", completionRoutes);
|
|
1877
|
+
server.route("/models", modelRoutes);
|
|
3972
1878
|
server.route("/embeddings", embeddingRoutes);
|
|
3973
1879
|
server.route("/usage", usageRoute);
|
|
3974
1880
|
server.route("/token", tokenRoute);
|
|
3975
|
-
server.
|
|
3976
|
-
|
|
3977
|
-
if (state.zenMode) return zenCompletionRoutes.fetch(req, c.env);
|
|
3978
|
-
if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(req, c.env);
|
|
3979
|
-
return completionRoutes.fetch(req, c.env);
|
|
3980
|
-
});
|
|
3981
|
-
server.all("/v1/chat/completions", async (c) => {
|
|
3982
|
-
const req = createSubRequest(c, "/v1/chat/completions");
|
|
3983
|
-
if (state.zenMode) return zenCompletionRoutes.fetch(req, c.env);
|
|
3984
|
-
if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(req, c.env);
|
|
3985
|
-
return completionRoutes.fetch(req, c.env);
|
|
3986
|
-
});
|
|
3987
|
-
server.all("/v1/models/*", async (c) => {
|
|
3988
|
-
const req = createSubRequest(c, "/v1/models");
|
|
3989
|
-
if (state.zenMode) return zenModelRoutes.fetch(req, c.env);
|
|
3990
|
-
if (state.antigravityMode) return antigravityModelsRoute.fetch(req, c.env);
|
|
3991
|
-
return modelRoutes.fetch(req, c.env);
|
|
3992
|
-
});
|
|
3993
|
-
server.all("/v1/models", async (c) => {
|
|
3994
|
-
const req = createSubRequest(c, "/v1/models");
|
|
3995
|
-
if (state.zenMode) return zenModelRoutes.fetch(req, c.env);
|
|
3996
|
-
if (state.antigravityMode) return antigravityModelsRoute.fetch(req, c.env);
|
|
3997
|
-
return modelRoutes.fetch(req, c.env);
|
|
3998
|
-
});
|
|
1881
|
+
server.route("/v1/chat/completions", completionRoutes);
|
|
1882
|
+
server.route("/v1/models", modelRoutes);
|
|
3999
1883
|
server.route("/v1/embeddings", embeddingRoutes);
|
|
4000
|
-
server.
|
|
4001
|
-
const req = createSubRequest(c, "/v1/messages");
|
|
4002
|
-
if (state.zenMode) return zenMessageRoutes.fetch(req, c.env);
|
|
4003
|
-
if (state.antigravityMode) return antigravityMessagesRoute.fetch(req, c.env);
|
|
4004
|
-
return messageRoutes.fetch(req, c.env);
|
|
4005
|
-
});
|
|
4006
|
-
server.all("/v1/messages", async (c) => {
|
|
4007
|
-
const req = createSubRequest(c, "/v1/messages");
|
|
4008
|
-
if (state.zenMode) return zenMessageRoutes.fetch(req, c.env);
|
|
4009
|
-
if (state.antigravityMode) return antigravityMessagesRoute.fetch(req, c.env);
|
|
4010
|
-
return messageRoutes.fetch(req, c.env);
|
|
4011
|
-
});
|
|
4012
|
-
server.all("/v1/responses/*", async (c) => {
|
|
4013
|
-
const req = createSubRequest(c, "/v1/responses");
|
|
4014
|
-
if (state.zenMode) return zenResponsesRoutes.fetch(req, c.env);
|
|
4015
|
-
return c.json({ error: "Responses API requires Zen mode" }, 400);
|
|
4016
|
-
});
|
|
4017
|
-
server.all("/v1/responses", async (c) => {
|
|
4018
|
-
const req = createSubRequest(c, "/v1/responses");
|
|
4019
|
-
if (state.zenMode) return zenResponsesRoutes.fetch(req, c.env);
|
|
4020
|
-
return c.json({ error: "Responses API requires Zen mode" }, 400);
|
|
4021
|
-
});
|
|
4022
|
-
server.route("/zen/v1/chat/completions", zenCompletionRoutes);
|
|
4023
|
-
server.route("/zen/v1/models", zenModelRoutes);
|
|
4024
|
-
server.route("/zen/v1/messages", zenMessageRoutes);
|
|
4025
|
-
server.route("/zen/v1/responses", zenResponsesRoutes);
|
|
4026
|
-
server.route("/antigravity/v1/chat/completions", antigravityChatCompletionsRoute);
|
|
4027
|
-
server.route("/antigravity/v1/models", antigravityModelsRoute);
|
|
4028
|
-
server.route("/antigravity/v1/messages", antigravityMessagesRoute);
|
|
1884
|
+
server.route("/v1/messages", messageRoutes);
|
|
4029
1885
|
//#endregion
|
|
4030
1886
|
//#region src/start.ts
|
|
4031
1887
|
/**
|
|
4032
1888
|
* Start and configure the Copilot API server according to the provided options.
|
|
4033
1889
|
*
|
|
4034
|
-
* Configures proxy and logging, initializes global state and credentials, ensures
|
|
4035
|
-
* required paths and model data are cached, optionally generates a Claude Code
|
|
4036
|
-
* launch command (and attempts to copy it to the clipboard), prints a usage
|
|
4037
|
-
* viewer URL, and begins serving HTTP requests on the specified port.
|
|
4038
|
-
*
|
|
4039
1890
|
* @param options - Server startup options:
|
|
4040
1891
|
* - port: Port number to listen on
|
|
4041
1892
|
* - verbose: Enable verbose logging
|
|
@@ -4048,11 +1899,6 @@ server.route("/antigravity/v1/messages", antigravityMessagesRoute);
|
|
|
4048
1899
|
* - showToken: Expose GitHub/Copilot tokens in responses for debugging
|
|
4049
1900
|
* - proxyEnv: Initialize proxy settings from environment variables
|
|
4050
1901
|
* - apiKeys: Optional list of API keys to enable API key authentication
|
|
4051
|
-
* - zen: Enable OpenCode Zen mode (proxy to Zen instead of GitHub Copilot)
|
|
4052
|
-
* - zenApiKey: OpenCode Zen API key (optional; if omitted will prompt for setup)
|
|
4053
|
-
* - antigravity: Enable Google Antigravity mode
|
|
4054
|
-
* - antigravityClientId: Google OAuth Client ID (optional; overrides env/default)
|
|
4055
|
-
* - antigravityClientSecret: Google OAuth Client Secret (optional; overrides env/default)
|
|
4056
1902
|
*/
|
|
4057
1903
|
async function runServer(options) {
|
|
4058
1904
|
const savedProxyApplied = await applyProxyConfig();
|
|
@@ -4071,94 +1917,36 @@ async function runServer(options) {
|
|
|
4071
1917
|
state.apiKeys = options.apiKeys;
|
|
4072
1918
|
if (state.apiKeys && state.apiKeys.length > 0) consola.info(`API key authentication enabled with ${state.apiKeys.length} key(s)`);
|
|
4073
1919
|
await ensurePaths();
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
state.
|
|
4077
|
-
|
|
4078
|
-
state.zenApiKey = options.zenApiKey;
|
|
4079
|
-
consola.info("Using provided Zen API key");
|
|
4080
|
-
} else {
|
|
4081
|
-
const { setupZenApiKey, loadZenAuth } = await import("./auth-g7psLP1B.js");
|
|
4082
|
-
const existingAuth = await loadZenAuth();
|
|
4083
|
-
if (existingAuth) {
|
|
4084
|
-
state.zenApiKey = existingAuth.apiKey;
|
|
4085
|
-
consola.info("Using existing Zen API key");
|
|
4086
|
-
} else state.zenApiKey = await setupZenApiKey();
|
|
4087
|
-
}
|
|
4088
|
-
const { cacheZenModels } = await import("./get-models-onnSXkNI.js");
|
|
4089
|
-
await cacheZenModels();
|
|
4090
|
-
consola.info(`Available Zen models: \n${state.zenModels?.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
4091
|
-
} else if (options.antigravity) {
|
|
4092
|
-
consola.info("Google Antigravity mode enabled");
|
|
4093
|
-
state.antigravityMode = true;
|
|
4094
|
-
const { loadAntigravityAuth, setupAntigravity, getCurrentAccount, hasApiKey, getApiKey, setOAuthCredentials } = await import("./auth-BfBWPtWx.js");
|
|
4095
|
-
if (options.antigravityClientId && options.antigravityClientSecret) {
|
|
4096
|
-
setOAuthCredentials(options.antigravityClientId, options.antigravityClientSecret);
|
|
4097
|
-
consola.info("Using provided OAuth credentials from CLI");
|
|
4098
|
-
}
|
|
4099
|
-
if (hasApiKey()) {
|
|
4100
|
-
consola.info("Using Gemini API Key for authentication (from GEMINI_API_KEY)");
|
|
4101
|
-
consola.info(`API Key: ${getApiKey()?.slice(0, 10) ?? ""}...`);
|
|
4102
|
-
} else {
|
|
4103
|
-
const existingAuth = await loadAntigravityAuth();
|
|
4104
|
-
if (!existingAuth || existingAuth.accounts.length === 0) {
|
|
4105
|
-
consola.warn("No Antigravity accounts found and no GEMINI_API_KEY set");
|
|
4106
|
-
consola.info("");
|
|
4107
|
-
consola.info("You can authenticate using one of these methods:");
|
|
4108
|
-
consola.info("");
|
|
4109
|
-
consola.info("Method 1: API Key (Recommended - Simplest)");
|
|
4110
|
-
consola.info(" Set environment variable: GEMINI_API_KEY=your_api_key");
|
|
4111
|
-
consola.info(" Get your API key from: https://aistudio.google.com/apikey");
|
|
4112
|
-
consola.info("");
|
|
4113
|
-
consola.info("Method 2: OAuth (Current setup)");
|
|
4114
|
-
consola.info(" Will proceed with OAuth login flow...");
|
|
4115
|
-
consola.info("");
|
|
4116
|
-
await setupAntigravity();
|
|
4117
|
-
} else {
|
|
4118
|
-
const enabledCount = existingAuth.accounts.filter((a) => a.enable).length;
|
|
4119
|
-
consola.info(`Found ${existingAuth.accounts.length} Antigravity accounts (${enabledCount} enabled)`);
|
|
4120
|
-
}
|
|
4121
|
-
if (!await getCurrentAccount() && !hasApiKey()) throw new Error("No enabled Antigravity accounts available");
|
|
4122
|
-
}
|
|
4123
|
-
const { getAntigravityModels } = await import("./get-models-DIOdRXYx.js");
|
|
4124
|
-
const models = await getAntigravityModels();
|
|
4125
|
-
state.antigravityModels = models;
|
|
4126
|
-
consola.info(`Available Antigravity models: \n${models.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
4127
|
-
} else {
|
|
4128
|
-
await cacheVSCodeVersion();
|
|
4129
|
-
if (options.githubToken) {
|
|
4130
|
-
state.githubToken = options.githubToken;
|
|
4131
|
-
consola.info("Using provided GitHub token");
|
|
4132
|
-
try {
|
|
4133
|
-
const { getGitHubUser } = await import("./get-user-CGhBmkXO.js");
|
|
4134
|
-
const user = await getGitHubUser();
|
|
4135
|
-
consola.info(`Logged in as ${user.login}`);
|
|
4136
|
-
} catch (error) {
|
|
4137
|
-
consola.error("Provided GitHub token is invalid");
|
|
4138
|
-
throw error;
|
|
4139
|
-
}
|
|
4140
|
-
} else await setupGitHubToken();
|
|
1920
|
+
await cacheVSCodeVersion();
|
|
1921
|
+
if (options.githubToken) {
|
|
1922
|
+
state.githubToken = options.githubToken;
|
|
1923
|
+
consola.info("Using provided GitHub token");
|
|
4141
1924
|
try {
|
|
4142
|
-
await
|
|
1925
|
+
const { getGitHubUser } = await import("./get-user-D2HfoqjP.js");
|
|
1926
|
+
const user = await getGitHubUser();
|
|
1927
|
+
consola.info(`Logged in as ${user.login}`);
|
|
4143
1928
|
} catch (error) {
|
|
4144
|
-
|
|
4145
|
-
if (error instanceof HTTPError && error.response.status === 401) {
|
|
4146
|
-
consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
|
|
4147
|
-
const { clearGithubToken } = await import("./token-CoKq3Guw.js");
|
|
4148
|
-
await clearGithubToken();
|
|
4149
|
-
consola.info("Please restart to re-authenticate");
|
|
4150
|
-
}
|
|
1929
|
+
consola.error("Provided GitHub token is invalid");
|
|
4151
1930
|
throw error;
|
|
4152
1931
|
}
|
|
4153
|
-
|
|
4154
|
-
|
|
1932
|
+
} else await setupGitHubToken();
|
|
1933
|
+
try {
|
|
1934
|
+
await setupCopilotToken();
|
|
1935
|
+
} catch (error) {
|
|
1936
|
+
const { HTTPError } = await import("./error-BfWAfRit.js");
|
|
1937
|
+
if (error instanceof HTTPError && error.response.status === 401) {
|
|
1938
|
+
consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
|
|
1939
|
+
const { clearGithubToken } = await import("./token-DT9ko4dY.js");
|
|
1940
|
+
await clearGithubToken();
|
|
1941
|
+
consola.info("Please restart to re-authenticate");
|
|
1942
|
+
}
|
|
1943
|
+
throw error;
|
|
4155
1944
|
}
|
|
1945
|
+
await cacheModels();
|
|
1946
|
+
consola.info(`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`);
|
|
4156
1947
|
const serverUrl = `http://localhost:${options.port}`;
|
|
4157
1948
|
if (options.claudeCode) {
|
|
4158
|
-
|
|
4159
|
-
if (state.zenMode) modelList = state.zenModels?.data;
|
|
4160
|
-
else if (state.antigravityMode) modelList = state.antigravityModels?.data;
|
|
4161
|
-
else modelList = state.models?.data;
|
|
1949
|
+
const modelList = state.models?.data;
|
|
4162
1950
|
invariant(modelList, "Models should be loaded by now");
|
|
4163
1951
|
const selectedModel = await consola.prompt("Select a model to use with Claude Code", {
|
|
4164
1952
|
type: "select",
|
|
@@ -4200,7 +1988,6 @@ await runMain(defineCommand({
|
|
|
4200
1988
|
description: "A wrapper around GitHub Copilot API to make it OpenAI/Anthropic compatible. Fork with bug fixes and improvements."
|
|
4201
1989
|
},
|
|
4202
1990
|
subCommands: {
|
|
4203
|
-
antigravity,
|
|
4204
1991
|
auth,
|
|
4205
1992
|
start: defineCommand({
|
|
4206
1993
|
meta: {
|
|
@@ -4266,29 +2053,6 @@ await runMain(defineCommand({
|
|
|
4266
2053
|
"api-key": {
|
|
4267
2054
|
type: "string",
|
|
4268
2055
|
description: "API keys for authentication"
|
|
4269
|
-
},
|
|
4270
|
-
zen: {
|
|
4271
|
-
alias: "z",
|
|
4272
|
-
type: "boolean",
|
|
4273
|
-
default: false,
|
|
4274
|
-
description: "Enable OpenCode Zen mode (proxy to Zen instead of GitHub Copilot)"
|
|
4275
|
-
},
|
|
4276
|
-
"zen-api-key": {
|
|
4277
|
-
type: "string",
|
|
4278
|
-
description: "OpenCode Zen API key (get from https://opencode.ai/zen)"
|
|
4279
|
-
},
|
|
4280
|
-
antigravity: {
|
|
4281
|
-
type: "boolean",
|
|
4282
|
-
default: false,
|
|
4283
|
-
description: "Enable Google Antigravity mode (proxy to Antigravity instead of GitHub Copilot)"
|
|
4284
|
-
},
|
|
4285
|
-
"antigravity-client-id": {
|
|
4286
|
-
type: "string",
|
|
4287
|
-
description: "Google OAuth Client ID for Antigravity (create at https://console.cloud.google.com/apis/credentials)"
|
|
4288
|
-
},
|
|
4289
|
-
"antigravity-client-secret": {
|
|
4290
|
-
type: "string",
|
|
4291
|
-
description: "Google OAuth Client Secret for Antigravity"
|
|
4292
2056
|
}
|
|
4293
2057
|
},
|
|
4294
2058
|
run({ args }) {
|
|
@@ -4308,12 +2072,7 @@ await runMain(defineCommand({
|
|
|
4308
2072
|
claudeCode: args["claude-code"],
|
|
4309
2073
|
showToken: args["show-token"],
|
|
4310
2074
|
proxyEnv: args["proxy-env"],
|
|
4311
|
-
apiKeys
|
|
4312
|
-
zen: args.zen,
|
|
4313
|
-
zenApiKey: args["zen-api-key"],
|
|
4314
|
-
antigravity: args.antigravity,
|
|
4315
|
-
antigravityClientId: args["antigravity-client-id"],
|
|
4316
|
-
antigravityClientSecret: args["antigravity-client-secret"]
|
|
2075
|
+
apiKeys
|
|
4317
2076
|
});
|
|
4318
2077
|
}
|
|
4319
2078
|
}),
|