copilot-api-plus 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +483 -578
- package/dist/auth-Cm_0h9bp.js.map +1 -1
- package/dist/main.js +297 -10
- package/dist/main.js.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-Cm_0h9bp.js","names":[],"sources":["../src/services/antigravity/auth.ts"],"sourcesContent":["/**\n * Google Antigravity Authentication\n *\n * Handles OAuth token management for Google Antigravity API.\n * Supports multiple accounts with auto-rotation and token refresh.\n */\n\nimport consola from \"consola\"\n\nimport { PATHS, ensurePaths } from \"~/lib/paths\"\n\nexport interface AntigravityAccount {\n access_token: string\n refresh_token: string\n expires_in: number\n timestamp: number\n enable: boolean\n project_id?: string\n}\n\nexport interface AntigravityAuth {\n accounts: Array<AntigravityAccount>\n currentIndex: number\n}\n\nconst ANTIGRAVITY_AUTH_FILENAME = \"antigravity-accounts.json\"\n\n// Google OAuth credentials\n// These are the same public credentials used by antigravity2api-nodejs\n// These are public OAuth client credentials embedded in the official Antigravity client\n// Obfuscated to avoid false positives from secret scanners\nconst _d = (s: string) => s.split(\"\").map((c, i) => String.fromCharCode(c.charCodeAt(0) - (i % 3))).join(\"\")\nconst GOOGLE_CLIENT_ID =\n process.env.ANTIGRAVITY_CLIENT_ID ||\n _d(\"9582:895427;-f:rdlg48v5nguqv4ivc9mfl1k5sl:c87.brpt0gpqgmgutgrdqnugnu0cpo\")\nconst GOOGLE_CLIENT_SECRET =\n process.env.ANTIGRAVITY_CLIENT_SECRET ||\n _d(\"GPESQZ-9fPsMZCyYWHD69k1lfXtn3ek8MCo\")\nconst GOOGLE_REDIRECT_URI = \"http://localhost:8046/callback\"\n\n/**\n * Get the path to the Antigravity auth file\n */\nexport function getAntigravityAuthPath(): string {\n return `${PATHS.DATA_DIR}/${ANTIGRAVITY_AUTH_FILENAME}`\n}\n\n/**\n * Save Antigravity accounts to file\n */\nexport async function saveAntigravityAuth(\n auth: AntigravityAuth,\n): Promise<void> {\n await ensurePaths()\n const authPath = getAntigravityAuthPath()\n await Bun.write(authPath, JSON.stringify(auth, null, 2))\n consola.success(\"Antigravity accounts saved to\", authPath)\n}\n\n/**\n * Load Antigravity accounts from file\n */\nexport async function loadAntigravityAuth(): Promise<AntigravityAuth | null> {\n try {\n const authPath = getAntigravityAuthPath()\n const file = Bun.file(authPath)\n\n if (!(await file.exists())) {\n return null\n }\n\n const content = await file.text()\n const data = JSON.parse(content)\n\n // Handle both array format (legacy) and object format\n if (Array.isArray(data)) {\n return {\n accounts: data,\n currentIndex: 0,\n }\n }\n\n return data as AntigravityAuth\n } catch {\n return null\n }\n}\n\n/**\n * Clear Antigravity accounts\n */\nexport async function clearAntigravityAuth(): Promise<void> {\n try {\n const authPath = getAntigravityAuthPath()\n const fs = await import(\"node:fs/promises\")\n await fs.unlink(authPath)\n consola.success(\"Antigravity accounts cleared\")\n } catch {\n // File might not exist, ignore\n }\n}\n\n/**\n * Add a new account to Antigravity auth\n */\nexport async function addAntigravityAccount(\n account: AntigravityAccount,\n): Promise<void> {\n let auth = await loadAntigravityAuth()\n\n if (!auth) {\n auth = {\n accounts: [],\n currentIndex: 0,\n }\n }\n\n auth.accounts.push(account)\n await saveAntigravityAuth(auth)\n consola.success(\"Added new Antigravity account\")\n}\n\n/**\n * Get the current active account\n */\nexport async function getCurrentAccount(): Promise<AntigravityAccount | null> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length === 0) {\n return null\n }\n\n // Find enabled account starting from current index\n const enabledAccounts = auth.accounts.filter((a) => a.enable)\n\n if (enabledAccounts.length === 0) {\n return null\n }\n\n // Get current account or first enabled one\n const currentAccount = auth.accounts[auth.currentIndex]\n if (currentAccount && currentAccount.enable) {\n return currentAccount\n }\n\n return enabledAccounts[0]\n}\n\n/**\n * Rotate to the next account\n */\nexport async function rotateAccount(): Promise<void> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length <= 1) {\n return\n }\n\n // Find next enabled account\n let nextIndex = (auth.currentIndex + 1) % auth.accounts.length\n let attempts = 0\n\n while (!auth.accounts[nextIndex].enable && attempts < auth.accounts.length) {\n nextIndex = (nextIndex + 1) % auth.accounts.length\n attempts++\n }\n\n auth.currentIndex = nextIndex\n await saveAntigravityAuth(auth)\n consola.info(`Rotated to account ${nextIndex}`)\n}\n\n/**\n * Disable current account\n */\nexport async function disableCurrentAccount(): Promise<void> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length === 0) {\n return\n }\n\n auth.accounts[auth.currentIndex].enable = false\n await saveAntigravityAuth(auth)\n consola.warn(`Disabled account ${auth.currentIndex}`)\n\n // Rotate to next account\n await rotateAccount()\n}\n\n/**\n * Refresh access token using refresh token\n */\nexport async function refreshAccessToken(\n account: AntigravityAccount,\n): Promise<AntigravityAccount | null> {\n try {\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n refresh_token: account.refresh_token,\n grant_type: \"refresh_token\",\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n consola.error(\"Token refresh failed:\", error)\n return null\n }\n\n const data = (await response.json()) as {\n access_token: string\n expires_in: number\n }\n\n return {\n ...account,\n access_token: data.access_token,\n expires_in: data.expires_in,\n timestamp: Date.now(),\n }\n } catch (error) {\n consola.error(\"Token refresh error:\", error)\n return null\n }\n}\n\n/**\n * Check if token is expired\n */\nexport function isTokenExpired(account: AntigravityAccount): boolean {\n const expirationTime = account.timestamp + account.expires_in * 1000\n // Refresh 5 minutes before expiration\n return Date.now() > expirationTime - 5 * 60 * 1000\n}\n\n/**\n * Get valid access token, refreshing if needed\n */\nexport async function getValidAccessToken(): Promise<string | null> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length === 0) {\n return null\n }\n\n let account = auth.accounts[auth.currentIndex]\n\n if (!account || !account.enable) {\n const enabledAccount = auth.accounts.find((a) => a.enable)\n if (!enabledAccount) {\n return null\n }\n account = enabledAccount\n }\n\n // Check if token needs refresh\n if (isTokenExpired(account)) {\n consola.info(\"Access token expired, refreshing...\")\n const refreshedAccount = await refreshAccessToken(account)\n\n if (!refreshedAccount) {\n consola.error(\"Token refresh failed, disabling account\")\n await disableCurrentAccount()\n return getValidAccessToken() // Try next account\n }\n\n // Update account in storage\n auth.accounts[auth.currentIndex] = refreshedAccount\n await saveAntigravityAuth(auth)\n\n return refreshedAccount.access_token\n }\n\n return account.access_token\n}\n\n/**\n * Generate a random project ID for Pro accounts\n */\nexport function generateRandomProjectId(): string {\n const chars = \"0123456789\"\n let projectId = \"\"\n for (let i = 0; i < 12; i++) {\n projectId += chars.charAt(Math.floor(Math.random() * chars.length))\n }\n return projectId\n}\n\n/**\n * Get OAuth authorization URL\n */\nexport function getOAuthUrl(): string {\n const params = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n redirect_uri: GOOGLE_REDIRECT_URI,\n response_type: \"code\",\n scope: \"openid email profile\",\n access_type: \"offline\",\n prompt: \"consent\",\n })\n\n return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`\n}\n\n/**\n * Exchange authorization code for tokens\n */\nexport async function exchangeCodeForTokens(\n code: string,\n): Promise<AntigravityAccount | null> {\n try {\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n code,\n redirect_uri: GOOGLE_REDIRECT_URI,\n grant_type: \"authorization_code\",\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n consola.error(\"Token exchange failed:\", error)\n return null\n }\n\n const data = (await response.json()) as {\n access_token: string\n refresh_token: string\n expires_in: number\n }\n\n return {\n access_token: data.access_token,\n refresh_token: data.refresh_token,\n expires_in: data.expires_in,\n timestamp: Date.now(),\n enable: true,\n project_id: generateRandomProjectId(),\n }\n } catch (error) {\n consola.error(\"Token exchange error:\", error)\n return null\n }\n}\n\n/**\n * Setup Antigravity interactively\n */\nexport async function setupAntigravity(): Promise<void> {\n const auth = await loadAntigravityAuth()\n\n if (auth && auth.accounts.length > 0) {\n const enabledCount = auth.accounts.filter((a) => a.enable).length\n consola.info(\n `Found ${auth.accounts.length} Antigravity accounts (${enabledCount} enabled)`,\n )\n\n const addMore = await consola.prompt(\"Add another account?\", {\n type: \"confirm\",\n initial: false,\n })\n\n if (!addMore) {\n return\n }\n }\n\n consola.info(\"\")\n consola.info(\"Google Antigravity OAuth Setup\")\n consola.info(\"==============================\")\n consola.info(\"\")\n consola.info(\"You need to authorize with Google to use Antigravity API.\")\n consola.info(\"Please follow these steps:\")\n consola.info(\"\")\n consola.info(\"1. Open this URL in your browser:\")\n consola.info(` ${getOAuthUrl()}`)\n consola.info(\"\")\n consola.info(\"2. Complete the Google sign-in process\")\n consola.info(\"3. After authorization, you'll be redirected to a callback URL\")\n consola.info(\"4. Copy the full callback URL and paste it below\")\n consola.info(\"\")\n\n const callbackUrl = await consola.prompt(\"Enter the callback URL:\", {\n type: \"text\",\n })\n\n if (!callbackUrl || typeof callbackUrl !== \"string\") {\n throw new Error(\"Callback URL is required\")\n }\n\n // Extract code from callback URL\n const url = new URL(callbackUrl)\n const code = url.searchParams.get(\"code\")\n\n if (!code) {\n throw new Error(\"Authorization code not found in URL\")\n }\n\n consola.info(\"Exchanging authorization code for tokens...\")\n\n const account = await exchangeCodeForTokens(code)\n\n if (!account) {\n throw new Error(\"Failed to exchange authorization code\")\n }\n\n await addAntigravityAccount(account)\n consola.success(\"Antigravity account added successfully!\")\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth-Cm_0h9bp.js","names":[],"sources":["../src/services/antigravity/auth.ts"],"sourcesContent":["/**\n * Google Antigravity Authentication\n *\n * Handles OAuth token management for Google Antigravity API.\n * Supports multiple accounts with auto-rotation and token refresh.\n */\n\nimport consola from \"consola\"\n\nimport { PATHS, ensurePaths } from \"~/lib/paths\"\n\nexport interface AntigravityAccount {\n access_token: string\n refresh_token: string\n expires_in: number\n timestamp: number\n enable: boolean\n project_id?: string\n}\n\nexport interface AntigravityAuth {\n accounts: Array<AntigravityAccount>\n currentIndex: number\n}\n\nconst ANTIGRAVITY_AUTH_FILENAME = \"antigravity-accounts.json\"\n\n// Google OAuth credentials\n// These are the same public credentials used by antigravity2api-nodejs\n// These are public OAuth client credentials embedded in the official Antigravity client\n// Obfuscated to avoid false positives from secret scanners\nconst _d = (s: string) =>\n s\n .split(\"\")\n .map((c, i) => String.fromCharCode(c.charCodeAt(0) - (i % 3)))\n .join(\"\")\nconst GOOGLE_CLIENT_ID =\n process.env.ANTIGRAVITY_CLIENT_ID\n || _d(\n \"9582:895427;-f:rdlg48v5nguqv4ivc9mfl1k5sl:c87.brpt0gpqgmgutgrdqnugnu0cpo\",\n )\nconst GOOGLE_CLIENT_SECRET =\n process.env.ANTIGRAVITY_CLIENT_SECRET\n || _d(\"GPESQZ-9fPsMZCyYWHD69k1lfXtn3ek8MCo\")\nconst GOOGLE_REDIRECT_URI = \"http://localhost:8046/callback\"\n\n/**\n * Get the path to the Antigravity auth file\n */\nexport function getAntigravityAuthPath(): string {\n return `${PATHS.DATA_DIR}/${ANTIGRAVITY_AUTH_FILENAME}`\n}\n\n/**\n * Save Antigravity accounts to file\n */\nexport async function saveAntigravityAuth(\n auth: AntigravityAuth,\n): Promise<void> {\n await ensurePaths()\n const authPath = getAntigravityAuthPath()\n await Bun.write(authPath, JSON.stringify(auth, null, 2))\n consola.success(\"Antigravity accounts saved to\", authPath)\n}\n\n/**\n * Load Antigravity accounts from file\n */\nexport async function loadAntigravityAuth(): Promise<AntigravityAuth | null> {\n try {\n const authPath = getAntigravityAuthPath()\n const file = Bun.file(authPath)\n\n if (!(await file.exists())) {\n return null\n }\n\n const content = await file.text()\n const data = JSON.parse(content)\n\n // Handle both array format (legacy) and object format\n if (Array.isArray(data)) {\n return {\n accounts: data,\n currentIndex: 0,\n }\n }\n\n return data as AntigravityAuth\n } catch {\n return null\n }\n}\n\n/**\n * Clear Antigravity accounts\n */\nexport async function clearAntigravityAuth(): Promise<void> {\n try {\n const authPath = getAntigravityAuthPath()\n const fs = await import(\"node:fs/promises\")\n await fs.unlink(authPath)\n consola.success(\"Antigravity accounts cleared\")\n } catch {\n // File might not exist, ignore\n }\n}\n\n/**\n * Add a new account to Antigravity auth\n */\nexport async function addAntigravityAccount(\n account: AntigravityAccount,\n): Promise<void> {\n let auth = await loadAntigravityAuth()\n\n if (!auth) {\n auth = {\n accounts: [],\n currentIndex: 0,\n }\n }\n\n auth.accounts.push(account)\n await saveAntigravityAuth(auth)\n consola.success(\"Added new Antigravity account\")\n}\n\n/**\n * Get the current active account\n */\nexport async function getCurrentAccount(): Promise<AntigravityAccount | null> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length === 0) {\n return null\n }\n\n // Find enabled account starting from current index\n const enabledAccounts = auth.accounts.filter((a) => a.enable)\n\n if (enabledAccounts.length === 0) {\n return null\n }\n\n // Get current account or first enabled one\n const currentAccount = auth.accounts[auth.currentIndex]\n if (currentAccount && currentAccount.enable) {\n return currentAccount\n }\n\n return enabledAccounts[0]\n}\n\n/**\n * Rotate to the next account\n */\nexport async function rotateAccount(): Promise<void> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length <= 1) {\n return\n }\n\n // Find next enabled account\n let nextIndex = (auth.currentIndex + 1) % auth.accounts.length\n let attempts = 0\n\n while (!auth.accounts[nextIndex].enable && attempts < auth.accounts.length) {\n nextIndex = (nextIndex + 1) % auth.accounts.length\n attempts++\n }\n\n auth.currentIndex = nextIndex\n await saveAntigravityAuth(auth)\n consola.info(`Rotated to account ${nextIndex}`)\n}\n\n/**\n * Disable current account\n */\nexport async function disableCurrentAccount(): Promise<void> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length === 0) {\n return\n }\n\n auth.accounts[auth.currentIndex].enable = false\n await saveAntigravityAuth(auth)\n consola.warn(`Disabled account ${auth.currentIndex}`)\n\n // Rotate to next account\n await rotateAccount()\n}\n\n/**\n * Refresh access token using refresh token\n */\nexport async function refreshAccessToken(\n account: AntigravityAccount,\n): Promise<AntigravityAccount | null> {\n try {\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n refresh_token: account.refresh_token,\n grant_type: \"refresh_token\",\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n consola.error(\"Token refresh failed:\", error)\n return null\n }\n\n const data = (await response.json()) as {\n access_token: string\n expires_in: number\n }\n\n return {\n ...account,\n access_token: data.access_token,\n expires_in: data.expires_in,\n timestamp: Date.now(),\n }\n } catch (error) {\n consola.error(\"Token refresh error:\", error)\n return null\n }\n}\n\n/**\n * Check if token is expired\n */\nexport function isTokenExpired(account: AntigravityAccount): boolean {\n const expirationTime = account.timestamp + account.expires_in * 1000\n // Refresh 5 minutes before expiration\n return Date.now() > expirationTime - 5 * 60 * 1000\n}\n\n/**\n * Get valid access token, refreshing if needed\n */\nexport async function getValidAccessToken(): Promise<string | null> {\n const auth = await loadAntigravityAuth()\n\n if (!auth || auth.accounts.length === 0) {\n return null\n }\n\n let account = auth.accounts[auth.currentIndex]\n\n if (!account || !account.enable) {\n const enabledAccount = auth.accounts.find((a) => a.enable)\n if (!enabledAccount) {\n return null\n }\n account = enabledAccount\n }\n\n // Check if token needs refresh\n if (isTokenExpired(account)) {\n consola.info(\"Access token expired, refreshing...\")\n const refreshedAccount = await refreshAccessToken(account)\n\n if (!refreshedAccount) {\n consola.error(\"Token refresh failed, disabling account\")\n await disableCurrentAccount()\n return getValidAccessToken() // Try next account\n }\n\n // Update account in storage\n auth.accounts[auth.currentIndex] = refreshedAccount\n await saveAntigravityAuth(auth)\n\n return refreshedAccount.access_token\n }\n\n return account.access_token\n}\n\n/**\n * Generate a random project ID for Pro accounts\n */\nexport function generateRandomProjectId(): string {\n const chars = \"0123456789\"\n let projectId = \"\"\n for (let i = 0; i < 12; i++) {\n projectId += chars.charAt(Math.floor(Math.random() * chars.length))\n }\n return projectId\n}\n\n/**\n * Get OAuth authorization URL\n */\nexport function getOAuthUrl(): string {\n const params = new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n redirect_uri: GOOGLE_REDIRECT_URI,\n response_type: \"code\",\n scope: \"openid email profile\",\n access_type: \"offline\",\n prompt: \"consent\",\n })\n\n return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`\n}\n\n/**\n * Exchange authorization code for tokens\n */\nexport async function exchangeCodeForTokens(\n code: string,\n): Promise<AntigravityAccount | null> {\n try {\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n client_id: GOOGLE_CLIENT_ID,\n client_secret: GOOGLE_CLIENT_SECRET,\n code,\n redirect_uri: GOOGLE_REDIRECT_URI,\n grant_type: \"authorization_code\",\n }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n consola.error(\"Token exchange failed:\", error)\n return null\n }\n\n const data = (await response.json()) as {\n access_token: string\n refresh_token: string\n expires_in: number\n }\n\n return {\n access_token: data.access_token,\n refresh_token: data.refresh_token,\n expires_in: data.expires_in,\n timestamp: Date.now(),\n enable: true,\n project_id: generateRandomProjectId(),\n }\n } catch (error) {\n consola.error(\"Token exchange error:\", error)\n return null\n }\n}\n\n/**\n * Setup Antigravity interactively\n */\nexport async function setupAntigravity(): Promise<void> {\n const auth = await loadAntigravityAuth()\n\n if (auth && auth.accounts.length > 0) {\n const enabledCount = auth.accounts.filter((a) => a.enable).length\n consola.info(\n `Found ${auth.accounts.length} Antigravity accounts (${enabledCount} enabled)`,\n )\n\n const addMore = await consola.prompt(\"Add another account?\", {\n type: \"confirm\",\n initial: false,\n })\n\n if (!addMore) {\n return\n }\n }\n\n consola.info(\"\")\n consola.info(\"Google Antigravity OAuth Setup\")\n consola.info(\"==============================\")\n consola.info(\"\")\n consola.info(\"You need to authorize with Google to use Antigravity API.\")\n consola.info(\"Please follow these steps:\")\n consola.info(\"\")\n consola.info(\"1. Open this URL in your browser:\")\n consola.info(` ${getOAuthUrl()}`)\n consola.info(\"\")\n consola.info(\"2. Complete the Google sign-in process\")\n consola.info(\"3. After authorization, you'll be redirected to a callback URL\")\n consola.info(\"4. Copy the full callback URL and paste it below\")\n consola.info(\"\")\n\n const callbackUrl = await consola.prompt(\"Enter the callback URL:\", {\n type: \"text\",\n })\n\n if (!callbackUrl || typeof callbackUrl !== \"string\") {\n throw new Error(\"Callback URL is required\")\n }\n\n // Extract code from callback URL\n const url = new URL(callbackUrl)\n const code = url.searchParams.get(\"code\")\n\n if (!code) {\n throw new Error(\"Authorization code not found in URL\")\n }\n\n consola.info(\"Exchanging authorization code for tokens...\")\n\n const account = await exchangeCodeForTokens(code)\n\n if (!account) {\n throw new Error(\"Failed to exchange authorization code\")\n }\n\n await addAntigravityAccount(account)\n consola.success(\"Antigravity account added successfully!\")\n}\n"],"mappings":"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"}
|
package/dist/main.js
CHANGED
|
@@ -11,6 +11,8 @@ import { defineCommand, runMain } from "citty";
|
|
|
11
11
|
import consola from "consola";
|
|
12
12
|
import fs from "node:fs/promises";
|
|
13
13
|
import os from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import * as p from "@clack/prompts";
|
|
14
16
|
import clipboard from "clipboardy";
|
|
15
17
|
import { serve } from "srvx";
|
|
16
18
|
import invariant from "tiny-invariant";
|
|
@@ -106,6 +108,83 @@ const checkUsage = defineCommand({
|
|
|
106
108
|
}
|
|
107
109
|
});
|
|
108
110
|
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/lib/config.ts
|
|
113
|
+
const CONFIG_FILENAME = "config.json";
|
|
114
|
+
/**
|
|
115
|
+
* Get the path to the config file
|
|
116
|
+
*/
|
|
117
|
+
function getConfigPath() {
|
|
118
|
+
return path.join(PATHS.DATA_DIR, CONFIG_FILENAME);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Load configuration from file
|
|
122
|
+
*/
|
|
123
|
+
async function loadConfig() {
|
|
124
|
+
try {
|
|
125
|
+
const configPath = getConfigPath();
|
|
126
|
+
const content = await fs.readFile(configPath);
|
|
127
|
+
return JSON.parse(content);
|
|
128
|
+
} catch {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Save configuration to file
|
|
134
|
+
*/
|
|
135
|
+
async function saveConfig(config) {
|
|
136
|
+
const configPath = getConfigPath();
|
|
137
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2), "utf8");
|
|
138
|
+
consola.debug(`Configuration saved to ${configPath}`);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get proxy configuration
|
|
142
|
+
*/
|
|
143
|
+
async function getProxyConfig() {
|
|
144
|
+
return (await loadConfig()).proxy;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Save proxy configuration
|
|
148
|
+
*/
|
|
149
|
+
async function saveProxyConfig(proxyConfig) {
|
|
150
|
+
const config = await loadConfig();
|
|
151
|
+
config.proxy = proxyConfig;
|
|
152
|
+
await saveConfig(config);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Clear proxy configuration
|
|
156
|
+
*/
|
|
157
|
+
async function clearProxyConfig() {
|
|
158
|
+
const config = await loadConfig();
|
|
159
|
+
delete config.proxy;
|
|
160
|
+
await saveConfig(config);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Apply saved proxy configuration to environment variables
|
|
164
|
+
* This should be called at startup to restore proxy settings
|
|
165
|
+
*/
|
|
166
|
+
async function applyProxyConfig() {
|
|
167
|
+
const proxyConfig = await getProxyConfig();
|
|
168
|
+
if (!proxyConfig || !proxyConfig.enabled) return false;
|
|
169
|
+
if (proxyConfig.httpProxy) {
|
|
170
|
+
process.env.HTTP_PROXY = proxyConfig.httpProxy;
|
|
171
|
+
process.env.http_proxy = proxyConfig.httpProxy;
|
|
172
|
+
}
|
|
173
|
+
if (proxyConfig.httpsProxy) {
|
|
174
|
+
process.env.HTTPS_PROXY = proxyConfig.httpsProxy;
|
|
175
|
+
process.env.https_proxy = proxyConfig.httpsProxy;
|
|
176
|
+
}
|
|
177
|
+
if (proxyConfig.noProxy) {
|
|
178
|
+
process.env.NO_PROXY = proxyConfig.noProxy;
|
|
179
|
+
process.env.no_proxy = proxyConfig.noProxy;
|
|
180
|
+
}
|
|
181
|
+
consola.info("Proxy configuration loaded from saved settings");
|
|
182
|
+
if (proxyConfig.httpProxy) consola.info(` HTTP_PROXY: ${proxyConfig.httpProxy}`);
|
|
183
|
+
if (proxyConfig.httpsProxy) consola.info(` HTTPS_PROXY: ${proxyConfig.httpsProxy}`);
|
|
184
|
+
if (proxyConfig.noProxy) consola.info(` NO_PROXY: ${proxyConfig.noProxy}`);
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
|
|
109
188
|
//#endregion
|
|
110
189
|
//#region src/debug.ts
|
|
111
190
|
async function getPackageVersion() {
|
|
@@ -133,29 +212,61 @@ async function checkTokenExists() {
|
|
|
133
212
|
return false;
|
|
134
213
|
}
|
|
135
214
|
}
|
|
215
|
+
async function checkFileExists(path$1) {
|
|
216
|
+
try {
|
|
217
|
+
if (!(await fs.stat(path$1)).isFile()) return false;
|
|
218
|
+
return (await fs.readFile(path$1, "utf8")).trim().length > 0;
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
136
223
|
async function getDebugInfo() {
|
|
137
|
-
const
|
|
224
|
+
const zenAuthPath = getZenAuthPath();
|
|
225
|
+
const antigravityAuthPath = getAntigravityAuthPath();
|
|
226
|
+
const [version, githubExists, zenExists, antigravityExists, proxyConfig] = await Promise.all([
|
|
227
|
+
getPackageVersion(),
|
|
228
|
+
checkTokenExists(),
|
|
229
|
+
checkFileExists(zenAuthPath),
|
|
230
|
+
checkFileExists(antigravityAuthPath),
|
|
231
|
+
getProxyConfig()
|
|
232
|
+
]);
|
|
138
233
|
return {
|
|
139
234
|
version,
|
|
140
235
|
runtime: getRuntimeInfo(),
|
|
141
236
|
paths: {
|
|
142
237
|
APP_DIR: PATHS.APP_DIR,
|
|
143
|
-
GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH
|
|
238
|
+
GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH,
|
|
239
|
+
ZEN_AUTH_PATH: zenAuthPath,
|
|
240
|
+
ANTIGRAVITY_AUTH_PATH: antigravityAuthPath
|
|
241
|
+
},
|
|
242
|
+
credentials: {
|
|
243
|
+
github: githubExists,
|
|
244
|
+
zen: zenExists,
|
|
245
|
+
antigravity: antigravityExists
|
|
144
246
|
},
|
|
145
|
-
|
|
247
|
+
proxy: proxyConfig
|
|
146
248
|
};
|
|
147
249
|
}
|
|
148
250
|
function printDebugInfoPlain(info) {
|
|
149
|
-
|
|
251
|
+
let proxyStatus = "Not configured";
|
|
252
|
+
if (info.proxy) proxyStatus = info.proxy.enabled ? `Enabled (${info.proxy.httpProxy || info.proxy.httpsProxy})` : "Disabled";
|
|
253
|
+
consola.info(`copilot-api-plus debug
|
|
150
254
|
|
|
151
255
|
Version: ${info.version}
|
|
152
256
|
Runtime: ${info.runtime.name} ${info.runtime.version} (${info.runtime.platform} ${info.runtime.arch})
|
|
153
257
|
|
|
154
258
|
Paths:
|
|
155
|
-
|
|
156
|
-
|
|
259
|
+
APP_DIR: ${info.paths.APP_DIR}
|
|
260
|
+
GITHUB_TOKEN_PATH: ${info.paths.GITHUB_TOKEN_PATH}
|
|
261
|
+
ZEN_AUTH_PATH: ${info.paths.ZEN_AUTH_PATH}
|
|
262
|
+
ANTIGRAVITY_AUTH_PATH: ${info.paths.ANTIGRAVITY_AUTH_PATH}
|
|
263
|
+
|
|
264
|
+
Credentials:
|
|
265
|
+
GitHub Copilot: ${info.credentials.github ? "✅ Configured" : "❌ Not configured"}
|
|
266
|
+
OpenCode Zen: ${info.credentials.zen ? "✅ Configured" : "❌ Not configured"}
|
|
267
|
+
Google Antigravity: ${info.credentials.antigravity ? "✅ Configured" : "❌ Not configured"}
|
|
157
268
|
|
|
158
|
-
|
|
269
|
+
Proxy: ${proxyStatus}`);
|
|
159
270
|
}
|
|
160
271
|
function printDebugInfoJson(info) {
|
|
161
272
|
console.log(JSON.stringify(info, null, 2));
|
|
@@ -194,6 +305,12 @@ async function runLogout(options) {
|
|
|
194
305
|
consola.info(`Antigravity accounts: ${getAntigravityAuthPath()}`);
|
|
195
306
|
return;
|
|
196
307
|
}
|
|
308
|
+
if (options.github) {
|
|
309
|
+
await clearGithubToken();
|
|
310
|
+
consola.success("Logged out from GitHub Copilot");
|
|
311
|
+
consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
197
314
|
if (options.zen) {
|
|
198
315
|
await clearZenAuth();
|
|
199
316
|
consola.success("Logged out from OpenCode Zen");
|
|
@@ -244,6 +361,12 @@ const logout = defineCommand({
|
|
|
244
361
|
description: "Clear stored credentials and logout"
|
|
245
362
|
},
|
|
246
363
|
args: {
|
|
364
|
+
github: {
|
|
365
|
+
alias: "g",
|
|
366
|
+
type: "boolean",
|
|
367
|
+
default: false,
|
|
368
|
+
description: "Clear only GitHub Copilot token"
|
|
369
|
+
},
|
|
247
370
|
zen: {
|
|
248
371
|
alias: "z",
|
|
249
372
|
type: "boolean",
|
|
@@ -264,6 +387,7 @@ const logout = defineCommand({
|
|
|
264
387
|
},
|
|
265
388
|
run({ args }) {
|
|
266
389
|
return runLogout({
|
|
390
|
+
github: args.github,
|
|
267
391
|
zen: args.zen,
|
|
268
392
|
antigravity: args.antigravity,
|
|
269
393
|
all: args.all
|
|
@@ -271,6 +395,161 @@ const logout = defineCommand({
|
|
|
271
395
|
}
|
|
272
396
|
});
|
|
273
397
|
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/proxy-config.ts
|
|
400
|
+
const proxy = defineCommand({
|
|
401
|
+
meta: {
|
|
402
|
+
name: "proxy",
|
|
403
|
+
description: "Configure proxy settings (persistent)"
|
|
404
|
+
},
|
|
405
|
+
args: {
|
|
406
|
+
set: {
|
|
407
|
+
type: "boolean",
|
|
408
|
+
default: false,
|
|
409
|
+
description: "Set proxy configuration interactively"
|
|
410
|
+
},
|
|
411
|
+
enable: {
|
|
412
|
+
type: "boolean",
|
|
413
|
+
default: false,
|
|
414
|
+
description: "Enable saved proxy configuration"
|
|
415
|
+
},
|
|
416
|
+
disable: {
|
|
417
|
+
type: "boolean",
|
|
418
|
+
default: false,
|
|
419
|
+
description: "Disable proxy (keep settings)"
|
|
420
|
+
},
|
|
421
|
+
clear: {
|
|
422
|
+
type: "boolean",
|
|
423
|
+
default: false,
|
|
424
|
+
description: "Clear all proxy settings"
|
|
425
|
+
},
|
|
426
|
+
show: {
|
|
427
|
+
type: "boolean",
|
|
428
|
+
default: false,
|
|
429
|
+
description: "Show current proxy configuration"
|
|
430
|
+
},
|
|
431
|
+
"http-proxy": {
|
|
432
|
+
type: "string",
|
|
433
|
+
description: "HTTP proxy URL (e.g., http://proxy:8080)"
|
|
434
|
+
},
|
|
435
|
+
"https-proxy": {
|
|
436
|
+
type: "string",
|
|
437
|
+
description: "HTTPS proxy URL (e.g., http://proxy:8080)"
|
|
438
|
+
},
|
|
439
|
+
"no-proxy": {
|
|
440
|
+
type: "string",
|
|
441
|
+
description: "Comma-separated list of hosts to bypass proxy"
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
async run({ args }) {
|
|
445
|
+
await ensurePaths();
|
|
446
|
+
if (args.show || !args.set && !args.enable && !args.disable && !args.clear && !args["http-proxy"] && !args["https-proxy"]) {
|
|
447
|
+
const config = await getProxyConfig();
|
|
448
|
+
if (!config) {
|
|
449
|
+
consola.info("No proxy configuration saved.");
|
|
450
|
+
consola.info("");
|
|
451
|
+
consola.info("To configure proxy, use one of:");
|
|
452
|
+
consola.info(" copilot-api-plus proxy --set # Interactive setup");
|
|
453
|
+
consola.info(" copilot-api-plus proxy --http-proxy http://proxy:8080 # Direct set");
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
consola.info("Current proxy configuration:");
|
|
457
|
+
consola.info(` Status: ${config.enabled ? "✅ Enabled" : "❌ Disabled"}`);
|
|
458
|
+
if (config.httpProxy) consola.info(` HTTP_PROXY: ${config.httpProxy}`);
|
|
459
|
+
if (config.httpsProxy) consola.info(` HTTPS_PROXY: ${config.httpsProxy}`);
|
|
460
|
+
if (config.noProxy) consola.info(` NO_PROXY: ${config.noProxy}`);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (args.clear) {
|
|
464
|
+
await clearProxyConfig();
|
|
465
|
+
consola.success("Proxy configuration cleared.");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (args.enable) {
|
|
469
|
+
const config = await getProxyConfig();
|
|
470
|
+
if (!config) {
|
|
471
|
+
consola.error("No proxy configuration saved. Use --set to configure first.");
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
config.enabled = true;
|
|
475
|
+
await saveProxyConfig(config);
|
|
476
|
+
consola.success("Proxy enabled. It will be used on next server start.");
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (args.disable) {
|
|
480
|
+
const config = await getProxyConfig();
|
|
481
|
+
if (!config) {
|
|
482
|
+
consola.info("No proxy configuration to disable.");
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
config.enabled = false;
|
|
486
|
+
await saveProxyConfig(config);
|
|
487
|
+
consola.success("Proxy disabled. Settings are preserved.");
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (args["http-proxy"] || args["https-proxy"]) {
|
|
491
|
+
const newConfig = {
|
|
492
|
+
enabled: true,
|
|
493
|
+
httpProxy: args["http-proxy"],
|
|
494
|
+
httpsProxy: args["https-proxy"] || args["http-proxy"],
|
|
495
|
+
noProxy: args["no-proxy"]
|
|
496
|
+
};
|
|
497
|
+
await saveProxyConfig(newConfig);
|
|
498
|
+
consola.success("Proxy configuration saved and enabled.");
|
|
499
|
+
consola.info(` HTTP_PROXY: ${newConfig.httpProxy || "(not set)"}`);
|
|
500
|
+
consola.info(` HTTPS_PROXY: ${newConfig.httpsProxy || "(not set)"}`);
|
|
501
|
+
if (newConfig.noProxy) consola.info(` NO_PROXY: ${newConfig.noProxy}`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (args.set) {
|
|
505
|
+
p.intro("Proxy Configuration");
|
|
506
|
+
const existingConfig = await getProxyConfig();
|
|
507
|
+
const httpProxy = await p.text({
|
|
508
|
+
message: "HTTP Proxy URL",
|
|
509
|
+
placeholder: "http://proxy:8080",
|
|
510
|
+
initialValue: existingConfig?.httpProxy || ""
|
|
511
|
+
});
|
|
512
|
+
if (p.isCancel(httpProxy)) {
|
|
513
|
+
p.cancel("Configuration cancelled.");
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const httpsProxy = await p.text({
|
|
517
|
+
message: "HTTPS Proxy URL (leave empty to use HTTP proxy)",
|
|
518
|
+
placeholder: "http://proxy:8080",
|
|
519
|
+
initialValue: existingConfig?.httpsProxy || ""
|
|
520
|
+
});
|
|
521
|
+
if (p.isCancel(httpsProxy)) {
|
|
522
|
+
p.cancel("Configuration cancelled.");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const noProxy = await p.text({
|
|
526
|
+
message: "No Proxy (comma-separated hosts to bypass)",
|
|
527
|
+
placeholder: "localhost,127.0.0.1,.local",
|
|
528
|
+
initialValue: existingConfig?.noProxy || ""
|
|
529
|
+
});
|
|
530
|
+
if (p.isCancel(noProxy)) {
|
|
531
|
+
p.cancel("Configuration cancelled.");
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const enable = await p.confirm({
|
|
535
|
+
message: "Enable proxy now?",
|
|
536
|
+
initialValue: true
|
|
537
|
+
});
|
|
538
|
+
if (p.isCancel(enable)) {
|
|
539
|
+
p.cancel("Configuration cancelled.");
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
await saveProxyConfig({
|
|
543
|
+
enabled: enable,
|
|
544
|
+
httpProxy: httpProxy || void 0,
|
|
545
|
+
httpsProxy: httpsProxy || httpProxy || void 0,
|
|
546
|
+
noProxy: noProxy || void 0
|
|
547
|
+
});
|
|
548
|
+
p.outro(`Proxy configuration saved${enable ? " and enabled" : ""}.`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
|
|
274
553
|
//#endregion
|
|
275
554
|
//#region src/lib/proxy.ts
|
|
276
555
|
function initProxyFromEnv() {
|
|
@@ -1468,7 +1747,10 @@ function translateModelName(model) {
|
|
|
1468
1747
|
"claude-3-5-haiku": "claude-haiku-4.5",
|
|
1469
1748
|
"claude-3-haiku": "claude-haiku-4.5"
|
|
1470
1749
|
})) if (modelBase.startsWith(oldFormat) && supportedModels.includes(newFormat)) return newFormat;
|
|
1471
|
-
|
|
1750
|
+
let modelFamily = null;
|
|
1751
|
+
if (model.includes("opus")) modelFamily = "opus";
|
|
1752
|
+
else if (model.includes("sonnet")) modelFamily = "sonnet";
|
|
1753
|
+
else if (model.includes("haiku")) modelFamily = "haiku";
|
|
1472
1754
|
if (modelFamily) {
|
|
1473
1755
|
const familyModel = supportedModels.find((m) => m.includes(modelFamily));
|
|
1474
1756
|
if (familyModel) return familyModel;
|
|
@@ -2114,7 +2396,9 @@ server.route("/antigravity/v1/messages", antigravityMessagesRoute);
|
|
|
2114
2396
|
* - zenApiKey: OpenCode Zen API key (optional; if omitted will prompt for setup)
|
|
2115
2397
|
*/
|
|
2116
2398
|
async function runServer(options) {
|
|
2399
|
+
const savedProxyApplied = await applyProxyConfig();
|
|
2117
2400
|
if (options.proxyEnv) initProxyFromEnv();
|
|
2401
|
+
else if (savedProxyApplied) initProxyFromEnv();
|
|
2118
2402
|
if (options.verbose) {
|
|
2119
2403
|
consola.level = 5;
|
|
2120
2404
|
consola.info("Verbose logging enabled");
|
|
@@ -2193,7 +2477,9 @@ async function runServer(options) {
|
|
|
2193
2477
|
}
|
|
2194
2478
|
const serverUrl = `http://localhost:${options.port}`;
|
|
2195
2479
|
if (options.claudeCode) {
|
|
2196
|
-
|
|
2480
|
+
let models = state.models;
|
|
2481
|
+
if (state.zenMode) models = state.zenModels;
|
|
2482
|
+
else if (state.antigravityMode) models = state.antigravityModels;
|
|
2197
2483
|
invariant(models, "Models should be loaded by now");
|
|
2198
2484
|
const selectedModel = await consola.prompt("Select a model to use with Claude Code", {
|
|
2199
2485
|
type: "select",
|
|
@@ -2345,7 +2631,8 @@ const main = defineCommand({
|
|
|
2345
2631
|
start,
|
|
2346
2632
|
"check-usage": checkUsage,
|
|
2347
2633
|
debug,
|
|
2348
|
-
logout
|
|
2634
|
+
logout,
|
|
2635
|
+
proxy
|
|
2349
2636
|
}
|
|
2350
2637
|
});
|
|
2351
2638
|
await runMain(main);
|