copilot-api-plus 1.0.49 → 1.0.51
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 +31 -0
- package/dist/error-DNWWcl_s.js +3 -0
- package/dist/{error-CvU5otz-.js → error-SzJ4KHd8.js} +7 -2
- package/dist/error-SzJ4KHd8.js.map +1 -0
- package/dist/get-models-uEbEgq0L.js.map +1 -1
- package/dist/{get-user-BzIEATcF.js → get-user-DEDD9jIs.js} +2 -2
- package/dist/get-user-DEDD9jIs.js.map +1 -0
- package/dist/get-user-HhhC3uQr.js +5 -0
- package/dist/main.js +50 -18
- package/dist/main.js.map +1 -1
- package/dist/{token-B777vbx8.js → token-CpxbiiIw.js} +31 -9
- package/dist/token-CpxbiiIw.js.map +1 -0
- package/dist/token-DkNaoDp7.js +7 -0
- package/package.json +1 -1
- package/dist/error-CsShqJjE.js +0 -3
- package/dist/error-CvU5otz-.js.map +0 -1
- package/dist/get-user-BzIEATcF.js.map +0 -1
- package/dist/get-user-CsQCc3Qx.js +0 -5
- package/dist/token-B777vbx8.js.map +0 -1
- package/dist/token-CCg0yU7a.js +0 -7
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Copilot API Plus
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/copilot-api-plus)
|
|
4
|
+
[](https://github.com/imbuxiangnan-cyber/copilot-api-plus/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
> A proxy that converts GitHub Copilot, OpenCode Zen, and Google Antigravity into OpenAI & Anthropic compatible APIs. Works with Claude Code, opencode, and more.
|
|
7
|
+
|
|
3
8
|
将 GitHub Copilot、OpenCode Zen、Google Antigravity 等 AI 服务转换为 **OpenAI** 和 **Anthropic** 兼容 API,支持与 [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview)、[opencode](https://github.com/sst/opencode) 等工具无缝集成。
|
|
4
9
|
|
|
5
10
|
---
|
|
@@ -673,6 +678,32 @@ Google Antigravity 模式内置了可靠性保障:
|
|
|
673
678
|
- **按模型族速率追踪**:分别追踪 Gemini 和 Claude 模型族的速率限制状态
|
|
674
679
|
- **指数退避重试**:429/503 等限流错误自动退避重试,短间隔走同端点,长间隔切换端点
|
|
675
680
|
|
|
681
|
+
### 请求日志
|
|
682
|
+
|
|
683
|
+
每次 API 请求会输出一行日志,包含模型名、耗时和 token 用量:
|
|
684
|
+
|
|
685
|
+
```
|
|
686
|
+
[claude-opus-4-6] 13:13:39 --> POST /v1/messages?beta=true 200 20.1s [in:87356 out:171 cache_read:13016]
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
- `in` — 输入 token 数(不含缓存命中部分)
|
|
690
|
+
- `out` — 输出 token 数
|
|
691
|
+
- `cache_read` — 缓存命中的 token 数(仅在有缓存时显示)
|
|
692
|
+
|
|
693
|
+
触发上下文压缩时会额外输出一行:
|
|
694
|
+
|
|
695
|
+
```
|
|
696
|
+
Truncated: 190385 -> 117537 tokens (-59 msgs)
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### 网络重试
|
|
700
|
+
|
|
701
|
+
对上游 API 的请求内置了瞬时网络错误重试(TLS 断开、连接重置等):
|
|
702
|
+
|
|
703
|
+
- 最多重试 2 次(共 3 次尝试)
|
|
704
|
+
- 退避间隔:1s、2s
|
|
705
|
+
- 仅重试网络层错误,HTTP 错误码(如 400/500)不重试
|
|
706
|
+
|
|
676
707
|
---
|
|
677
708
|
|
|
678
709
|
## 🐳 Docker 部署
|
|
@@ -11,7 +11,12 @@ var HTTPError = class extends Error {
|
|
|
11
11
|
async function forwardError(c, error) {
|
|
12
12
|
consola.error("Error occurred:", error);
|
|
13
13
|
if (error instanceof HTTPError) {
|
|
14
|
-
|
|
14
|
+
let errorText;
|
|
15
|
+
try {
|
|
16
|
+
errorText = await error.response.text();
|
|
17
|
+
} catch {
|
|
18
|
+
errorText = error.message;
|
|
19
|
+
}
|
|
15
20
|
let errorJson;
|
|
16
21
|
try {
|
|
17
22
|
errorJson = JSON.parse(errorText);
|
|
@@ -32,4 +37,4 @@ async function forwardError(c, error) {
|
|
|
32
37
|
|
|
33
38
|
//#endregion
|
|
34
39
|
export { HTTPError, forwardError };
|
|
35
|
-
//# sourceMappingURL=error-
|
|
40
|
+
//# sourceMappingURL=error-SzJ4KHd8.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-SzJ4KHd8.js","names":["errorText: string","errorJson: unknown"],"sources":["../src/lib/error.ts"],"sourcesContent":["import type { Context } from \"hono\"\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\"\n\nimport consola from \"consola\"\n\nexport class HTTPError extends Error {\n response: Response\n\n constructor(message: string, response: Response) {\n super(message)\n this.response = response\n }\n}\n\nexport async function forwardError(c: Context, error: unknown) {\n consola.error(\"Error occurred:\", error)\n\n if (error instanceof HTTPError) {\n // Try to read error body, but it may already be consumed by the caller\n let errorText: string\n try {\n errorText = await error.response.text()\n } catch {\n // Body already read — fall back to the error message\n errorText = error.message\n }\n let errorJson: unknown\n try {\n errorJson = JSON.parse(errorText)\n } catch {\n errorJson = errorText\n }\n consola.error(\"HTTP error:\", errorJson)\n return c.json(\n {\n error: {\n message: errorText,\n type: \"error\",\n },\n },\n error.response.status as ContentfulStatusCode,\n )\n }\n\n return c.json(\n {\n error: {\n message: (error as Error).message,\n type: \"error\",\n },\n },\n 500,\n )\n}\n"],"mappings":";;;AAKA,IAAa,YAAb,cAA+B,MAAM;CACnC;CAEA,YAAY,SAAiB,UAAoB;AAC/C,QAAM,QAAQ;AACd,OAAK,WAAW;;;AAIpB,eAAsB,aAAa,GAAY,OAAgB;AAC7D,SAAQ,MAAM,mBAAmB,MAAM;AAEvC,KAAI,iBAAiB,WAAW;EAE9B,IAAIA;AACJ,MAAI;AACF,eAAY,MAAM,MAAM,SAAS,MAAM;UACjC;AAEN,eAAY,MAAM;;EAEpB,IAAIC;AACJ,MAAI;AACF,eAAY,KAAK,MAAM,UAAU;UAC3B;AACN,eAAY;;AAEd,UAAQ,MAAM,eAAe,UAAU;AACvC,SAAO,EAAE,KACP,EACE,OAAO;GACL,SAAS;GACT,MAAM;GACP,EACF,EACD,MAAM,SAAS,OAChB;;AAGH,QAAO,EAAE,KACP,EACE,OAAO;EACL,SAAU,MAAgB;EAC1B,MAAM;EACP,EACF,EACD,IACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-models-uEbEgq0L.js","names":["FALLBACK_MODELS: Array<AntigravityModel>","cachedModels: Array<AntigravityModel> | null","cacheTimestamp: number","models: Array<AntigravityModel>","modelsQuota: Record<string, {\r\n remaining_fraction: number\r\n reset_time: string\r\n percent_remaining: number\r\n }>"],"sources":["../src/services/antigravity/get-models.ts"],"sourcesContent":["/**\r\n * Google Antigravity Models\r\n *\r\n * Provides list of available models from Antigravity.\r\n * Based on: https://github.com/liuw1535/antigravity2api-nodejs\r\n */\r\n\r\n/* eslint-disable require-atomic-updates */\r\n\r\nimport consola from \"consola\"\r\n\r\nimport { getValidAccessToken } from \"./auth\"\r\n\r\n// Antigravity API endpoints\r\nconst ANTIGRAVITY_API_HOST = \"daily-cloudcode-pa.sandbox.googleapis.com\"\r\nconst ANTIGRAVITY_MODELS_URL = `https://${ANTIGRAVITY_API_HOST}/v1internal:fetchAvailableModels`\r\nconst ANTIGRAVITY_USER_AGENT = \"antigravity/1.11.3 windows/amd64\"\r\n\r\nexport interface AntigravityQuotaInfo {\r\n remainingFraction: number\r\n resetTime: string\r\n}\r\n\r\nexport interface AntigravityModel {\r\n id: string\r\n object: string\r\n created: number\r\n owned_by: string\r\n quotaInfo?: AntigravityQuotaInfo\r\n}\r\n\r\nexport interface AntigravityModelsResponse {\r\n object: string\r\n data: Array<AntigravityModel>\r\n}\r\n\r\n/**\r\n * Fallback Antigravity models when API is unavailable\r\n * Updated based on actual API response (December 2024)\r\n */\r\nconst FALLBACK_MODELS: Array<AntigravityModel> = [\r\n // Gemini models\r\n {\r\n id: \"gemini-2.5-pro\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n {\r\n id: \"gemini-2.5-flash\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n {\r\n id: \"gemini-2.5-flash-lite\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n {\r\n id: \"gemini-2.5-flash-thinking\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n {\r\n id: \"gemini-3-pro-low\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n {\r\n id: \"gemini-3-pro-high\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n {\r\n id: \"gemini-3-pro-image\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n {\r\n id: \"gemini-3-flash\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"google\",\r\n },\r\n\r\n // Claude models (via Antigravity)\r\n {\r\n id: \"claude-sonnet-4-5\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"anthropic\",\r\n },\r\n {\r\n id: \"claude-sonnet-4-5-thinking\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"anthropic\",\r\n },\r\n {\r\n id: \"claude-opus-4-5-thinking\",\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: \"anthropic\",\r\n },\r\n]\r\n\r\n// Cache for fetched models\r\nlet cachedModels: Array<AntigravityModel> | null = null\r\nlet cacheTimestamp: number = 0\r\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\r\n\r\n/**\r\n * Fetch models from Antigravity API\r\n */\r\nasync function fetchModelsFromApi(): Promise<Array<AntigravityModel> | null> {\r\n const accessToken = await getValidAccessToken()\r\n\r\n if (!accessToken) {\r\n consola.debug(\"No access token available, using fallback models\")\r\n return null\r\n }\r\n\r\n try {\r\n const response = await fetch(ANTIGRAVITY_MODELS_URL, {\r\n method: \"POST\",\r\n headers: {\r\n Host: ANTIGRAVITY_API_HOST,\r\n \"User-Agent\": ANTIGRAVITY_USER_AGENT,\r\n Authorization: `Bearer ${accessToken}`,\r\n \"Content-Type\": \"application/json\",\r\n \"Accept-Encoding\": \"gzip\",\r\n },\r\n body: JSON.stringify({}),\r\n })\r\n\r\n if (!response.ok) {\r\n consola.warn(`Failed to fetch Antigravity models: ${response.status}`)\r\n return null\r\n }\r\n\r\n // API returns models as object (dictionary), not array\r\n // Format: { \"models\": { \"model-id\": { \"quotaInfo\": {...}, \"apiProvider\": \"...\", ... }, ... } }\r\n const data = (await response.json()) as {\r\n models?: Record<string, {\r\n displayName?: string\r\n maxTokens?: number\r\n apiProvider?: string\r\n model?: string\r\n quotaInfo?: {\r\n remainingFraction?: number\r\n resetTime?: string\r\n }\r\n }>\r\n }\r\n\r\n if (!data.models || typeof data.models !== \"object\") {\r\n consola.warn(\"No models object in response\")\r\n return null\r\n }\r\n\r\n // Convert object to array format\r\n const modelEntries = Object.entries(data.models)\r\n consola.debug(`Antigravity API returned ${modelEntries.length} models`)\r\n\r\n // Filter to only include Gemini and Claude models (skip internal models like chat_20706)\r\n const models: Array<AntigravityModel> = modelEntries\r\n .filter(([modelId, info]) => {\r\n // Only include gemini, learnlm, and claude models\r\n const isPublicModel = modelId.startsWith(\"gemini\") ||\r\n modelId.startsWith(\"learnlm\") ||\r\n modelId.startsWith(\"claude\")\r\n // Filter out models with no remaining quota\r\n const remaining = info.quotaInfo?.remainingFraction ?? 1\r\n return isPublicModel && remaining > 0\r\n })\r\n .map(([modelId, info]) => {\r\n const isGoogle = modelId.startsWith(\"gemini\") || modelId.startsWith(\"learnlm\")\r\n\r\n return {\r\n id: modelId,\r\n object: \"model\",\r\n created: 1700000000,\r\n owned_by: isGoogle ? \"google\" : \"anthropic\",\r\n quotaInfo: info.quotaInfo ? {\r\n remainingFraction: info.quotaInfo.remainingFraction ?? 1,\r\n resetTime: info.quotaInfo.resetTime ?? \"\",\r\n } : undefined,\r\n }\r\n })\r\n\r\n consola.debug(`Fetched ${models.length} models from Antigravity API`)\r\n return models\r\n } catch (error) {\r\n consola.warn(\"Error fetching Antigravity models:\", error)\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Get available Antigravity models\r\n */\r\nexport async function getAntigravityModels(): Promise<AntigravityModelsResponse> {\r\n // Check cache\r\n if (cachedModels && Date.now() - cacheTimestamp < CACHE_TTL) {\r\n consola.debug(`Returning ${cachedModels.length} cached Antigravity models`)\r\n return {\r\n object: \"list\",\r\n data: cachedModels,\r\n }\r\n }\r\n\r\n // Try to fetch from API\r\n const apiModels = await fetchModelsFromApi()\r\n\r\n if (apiModels && apiModels.length > 0) {\r\n cachedModels = apiModels\r\n cacheTimestamp = Date.now()\r\n\r\n return {\r\n object: \"list\",\r\n data: apiModels,\r\n }\r\n }\r\n\r\n // Use fallback models\r\n consola.debug(\r\n `Returning ${FALLBACK_MODELS.length} fallback Antigravity models`,\r\n )\r\n\r\n return {\r\n object: \"list\",\r\n data: FALLBACK_MODELS,\r\n }\r\n}\r\n\r\n/**\r\n * Antigravity usage response format (compatible with Copilot usage viewer)\r\n */\r\nexport interface AntigravityUsageResponse {\r\n copilot_plan: string\r\n quota_reset_date: string\r\n quota_snapshots: {\r\n models: Record<string, {\r\n remaining_fraction: number\r\n reset_time: string\r\n percent_remaining: number\r\n }>\r\n }\r\n}\r\n\r\n/**\r\n * Get Antigravity usage/quota information\r\n */\r\nexport async function getAntigravityUsage(): Promise<AntigravityUsageResponse> {\r\n // Force refresh models to get latest quota\r\n cachedModels = null\r\n cacheTimestamp = 0\r\n\r\n const modelsResponse = await getAntigravityModels()\r\n\r\n // Find earliest reset time\r\n let earliestResetTime = \"\"\r\n const modelsQuota: Record<string, {\r\n remaining_fraction: number\r\n reset_time: string\r\n percent_remaining: number\r\n }> = {}\r\n\r\n let modelsWithQuota = 0\r\n for (const model of modelsResponse.data) {\r\n if (model.quotaInfo) {\r\n modelsWithQuota++\r\n const resetTime = model.quotaInfo.resetTime\r\n if (!earliestResetTime || (resetTime && resetTime < earliestResetTime)) {\r\n earliestResetTime = resetTime\r\n }\r\n\r\n modelsQuota[model.id] = {\r\n remaining_fraction: model.quotaInfo.remainingFraction,\r\n reset_time: model.quotaInfo.resetTime,\r\n percent_remaining: Math.round(model.quotaInfo.remainingFraction * 100),\r\n }\r\n }\r\n }\r\n\r\n consola.debug(`Antigravity usage: ${modelsWithQuota}/${modelsResponse.data.length} models have quota info`)\r\n\r\n return {\r\n copilot_plan: \"antigravity\",\r\n quota_reset_date: earliestResetTime,\r\n quota_snapshots: {\r\n models: modelsQuota,\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * Check if a model is a Claude model\r\n */\r\nexport function isClaudeModel(modelId: string): boolean {\r\n return modelId.startsWith(\"claude-\")\r\n}\r\n\r\n/**\r\n * Check if a model is a thinking/reasoning model\r\n */\r\nexport function isThinkingModel(modelId: string): boolean {\r\n return modelId.includes(\"thinking\")\r\n}\r\n\r\n/**\r\n * Check if a model is an image generation model\r\n */\r\nexport function isImageModel(modelId: string): boolean {\r\n return modelId.includes(\"image\")\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"get-models-uEbEgq0L.js","names":["FALLBACK_MODELS: Array<AntigravityModel>","cachedModels: Array<AntigravityModel> | null","cacheTimestamp: number","models: Array<AntigravityModel>","modelsQuota: Record<\n string,\n {\n remaining_fraction: number\n reset_time: string\n percent_remaining: number\n }\n >"],"sources":["../src/services/antigravity/get-models.ts"],"sourcesContent":["/**\n * Google Antigravity Models\n *\n * Provides list of available models from Antigravity.\n * Based on: https://github.com/liuw1535/antigravity2api-nodejs\n */\n\n/* eslint-disable require-atomic-updates */\n\nimport consola from \"consola\"\n\nimport { getValidAccessToken } from \"./auth\"\n\n// Antigravity API endpoints\nconst ANTIGRAVITY_API_HOST = \"daily-cloudcode-pa.sandbox.googleapis.com\"\nconst ANTIGRAVITY_MODELS_URL = `https://${ANTIGRAVITY_API_HOST}/v1internal:fetchAvailableModels`\nconst ANTIGRAVITY_USER_AGENT = \"antigravity/1.11.3 windows/amd64\"\n\nexport interface AntigravityQuotaInfo {\n remainingFraction: number\n resetTime: string\n}\n\nexport interface AntigravityModel {\n id: string\n object: string\n created: number\n owned_by: string\n quotaInfo?: AntigravityQuotaInfo\n}\n\nexport interface AntigravityModelsResponse {\n object: string\n data: Array<AntigravityModel>\n}\n\n/**\n * Fallback Antigravity models when API is unavailable\n * Updated based on actual API response (December 2024)\n */\nconst FALLBACK_MODELS: Array<AntigravityModel> = [\n // Gemini models\n {\n id: \"gemini-2.5-pro\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n {\n id: \"gemini-2.5-flash\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n {\n id: \"gemini-2.5-flash-lite\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n {\n id: \"gemini-2.5-flash-thinking\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n {\n id: \"gemini-3-pro-low\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n {\n id: \"gemini-3-pro-high\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n {\n id: \"gemini-3-pro-image\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n {\n id: \"gemini-3-flash\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"google\",\n },\n\n // Claude models (via Antigravity)\n {\n id: \"claude-sonnet-4-5\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"anthropic\",\n },\n {\n id: \"claude-sonnet-4-5-thinking\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"anthropic\",\n },\n {\n id: \"claude-opus-4-5-thinking\",\n object: \"model\",\n created: 1700000000,\n owned_by: \"anthropic\",\n },\n]\n\n// Cache for fetched models\nlet cachedModels: Array<AntigravityModel> | null = null\nlet cacheTimestamp: number = 0\nconst CACHE_TTL = 5 * 60 * 1000 // 5 minutes\n\n/**\n * Fetch models from Antigravity API\n */\nasync function fetchModelsFromApi(): Promise<Array<AntigravityModel> | null> {\n const accessToken = await getValidAccessToken()\n\n if (!accessToken) {\n consola.debug(\"No access token available, using fallback models\")\n return null\n }\n\n try {\n const response = await fetch(ANTIGRAVITY_MODELS_URL, {\n method: \"POST\",\n headers: {\n Host: ANTIGRAVITY_API_HOST,\n \"User-Agent\": ANTIGRAVITY_USER_AGENT,\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n \"Accept-Encoding\": \"gzip\",\n },\n body: JSON.stringify({}),\n })\n\n if (!response.ok) {\n consola.warn(`Failed to fetch Antigravity models: ${response.status}`)\n return null\n }\n\n // API returns models as object (dictionary), not array\n // Format: { \"models\": { \"model-id\": { \"quotaInfo\": {...}, \"apiProvider\": \"...\", ... }, ... } }\n const data = (await response.json()) as {\n models?: Record<\n string,\n {\n displayName?: string\n maxTokens?: number\n apiProvider?: string\n model?: string\n quotaInfo?: {\n remainingFraction?: number\n resetTime?: string\n }\n }\n >\n }\n\n if (!data.models || typeof data.models !== \"object\") {\n consola.warn(\"No models object in response\")\n return null\n }\n\n // Convert object to array format\n const modelEntries = Object.entries(data.models)\n consola.debug(`Antigravity API returned ${modelEntries.length} models`)\n\n // Filter to only include Gemini and Claude models (skip internal models like chat_20706)\n const models: Array<AntigravityModel> = modelEntries\n .filter(([modelId, info]) => {\n // Only include gemini, learnlm, and claude models\n const isPublicModel =\n modelId.startsWith(\"gemini\")\n || modelId.startsWith(\"learnlm\")\n || modelId.startsWith(\"claude\")\n // Filter out models with no remaining quota\n const remaining = info.quotaInfo?.remainingFraction ?? 1\n return isPublicModel && remaining > 0\n })\n .map(([modelId, info]) => {\n const isGoogle =\n modelId.startsWith(\"gemini\") || modelId.startsWith(\"learnlm\")\n\n return {\n id: modelId,\n object: \"model\",\n created: 1700000000,\n owned_by: isGoogle ? \"google\" : \"anthropic\",\n quotaInfo:\n info.quotaInfo ?\n {\n remainingFraction: info.quotaInfo.remainingFraction ?? 1,\n resetTime: info.quotaInfo.resetTime ?? \"\",\n }\n : undefined,\n }\n })\n\n consola.debug(`Fetched ${models.length} models from Antigravity API`)\n return models\n } catch (error) {\n consola.warn(\"Error fetching Antigravity models:\", error)\n return null\n }\n}\n\n/**\n * Get available Antigravity models\n */\nexport async function getAntigravityModels(): Promise<AntigravityModelsResponse> {\n // Check cache\n if (cachedModels && Date.now() - cacheTimestamp < CACHE_TTL) {\n consola.debug(`Returning ${cachedModels.length} cached Antigravity models`)\n return {\n object: \"list\",\n data: cachedModels,\n }\n }\n\n // Try to fetch from API\n const apiModels = await fetchModelsFromApi()\n\n if (apiModels && apiModels.length > 0) {\n cachedModels = apiModels\n cacheTimestamp = Date.now()\n\n return {\n object: \"list\",\n data: apiModels,\n }\n }\n\n // Use fallback models\n consola.debug(\n `Returning ${FALLBACK_MODELS.length} fallback Antigravity models`,\n )\n\n return {\n object: \"list\",\n data: FALLBACK_MODELS,\n }\n}\n\n/**\n * Antigravity usage response format (compatible with Copilot usage viewer)\n */\nexport interface AntigravityUsageResponse {\n copilot_plan: string\n quota_reset_date: string\n quota_snapshots: {\n models: Record<\n string,\n {\n remaining_fraction: number\n reset_time: string\n percent_remaining: number\n }\n >\n }\n}\n\n/**\n * Get Antigravity usage/quota information\n */\nexport async function getAntigravityUsage(): Promise<AntigravityUsageResponse> {\n // Force refresh models to get latest quota\n cachedModels = null\n cacheTimestamp = 0\n\n const modelsResponse = await getAntigravityModels()\n\n // Find earliest reset time\n let earliestResetTime = \"\"\n const modelsQuota: Record<\n string,\n {\n remaining_fraction: number\n reset_time: string\n percent_remaining: number\n }\n > = {}\n\n let modelsWithQuota = 0\n for (const model of modelsResponse.data) {\n if (model.quotaInfo) {\n modelsWithQuota++\n const resetTime = model.quotaInfo.resetTime\n if (!earliestResetTime || (resetTime && resetTime < earliestResetTime)) {\n earliestResetTime = resetTime\n }\n\n modelsQuota[model.id] = {\n remaining_fraction: model.quotaInfo.remainingFraction,\n reset_time: model.quotaInfo.resetTime,\n percent_remaining: Math.round(model.quotaInfo.remainingFraction * 100),\n }\n }\n }\n\n consola.debug(\n `Antigravity usage: ${modelsWithQuota}/${modelsResponse.data.length} models have quota info`,\n )\n\n return {\n copilot_plan: \"antigravity\",\n quota_reset_date: earliestResetTime,\n quota_snapshots: {\n models: modelsQuota,\n },\n }\n}\n\n/**\n * Check if a model is a Claude model\n */\nexport function isClaudeModel(modelId: string): boolean {\n return modelId.startsWith(\"claude-\")\n}\n\n/**\n * Check if a model is a thinking/reasoning model\n */\nexport function isThinkingModel(modelId: string): boolean {\n return modelId.includes(\"thinking\")\n}\n\n/**\n * Check if a model is an image generation model\n */\nexport function isImageModel(modelId: string): boolean {\n return modelId.includes(\"image\")\n}\n"],"mappings":";;;;AAcA,MAAM,uBAAuB;AAC7B,MAAM,yBAAyB,WAAW,qBAAqB;AAC/D,MAAM,yBAAyB;;;;;AAwB/B,MAAMA,kBAA2C;CAE/C;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CAGD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACD;EACE,IAAI;EACJ,QAAQ;EACR,SAAS;EACT,UAAU;EACX;CACF;AAGD,IAAIC,eAA+C;AACnD,IAAIC,iBAAyB;AAC7B,MAAM,YAAY,MAAS;;;;AAK3B,eAAe,qBAA8D;CAC3E,MAAM,cAAc,MAAM,qBAAqB;AAE/C,KAAI,CAAC,aAAa;AAChB,UAAQ,MAAM,mDAAmD;AACjE,SAAO;;AAGT,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,wBAAwB;GACnD,QAAQ;GACR,SAAS;IACP,MAAM;IACN,cAAc;IACd,eAAe,UAAU;IACzB,gBAAgB;IAChB,mBAAmB;IACpB;GACD,MAAM,KAAK,UAAU,EAAE,CAAC;GACzB,CAAC;AAEF,MAAI,CAAC,SAAS,IAAI;AAChB,WAAQ,KAAK,uCAAuC,SAAS,SAAS;AACtE,UAAO;;EAKT,MAAM,OAAQ,MAAM,SAAS,MAAM;AAgBnC,MAAI,CAAC,KAAK,UAAU,OAAO,KAAK,WAAW,UAAU;AACnD,WAAQ,KAAK,+BAA+B;AAC5C,UAAO;;EAIT,MAAM,eAAe,OAAO,QAAQ,KAAK,OAAO;AAChD,UAAQ,MAAM,4BAA4B,aAAa,OAAO,SAAS;EAGvE,MAAMC,SAAkC,aACrC,QAAQ,CAAC,SAAS,UAAU;GAE3B,MAAM,gBACJ,QAAQ,WAAW,SAAS,IACzB,QAAQ,WAAW,UAAU,IAC7B,QAAQ,WAAW,SAAS;GAEjC,MAAM,YAAY,KAAK,WAAW,qBAAqB;AACvD,UAAO,iBAAiB,YAAY;IACpC,CACD,KAAK,CAAC,SAAS,UAAU;GACxB,MAAM,WACJ,QAAQ,WAAW,SAAS,IAAI,QAAQ,WAAW,UAAU;AAE/D,UAAO;IACL,IAAI;IACJ,QAAQ;IACR,SAAS;IACT,UAAU,WAAW,WAAW;IAChC,WACE,KAAK,YACH;KACE,mBAAmB,KAAK,UAAU,qBAAqB;KACvD,WAAW,KAAK,UAAU,aAAa;KACxC,GACD;IACL;IACD;AAEJ,UAAQ,MAAM,WAAW,OAAO,OAAO,8BAA8B;AACrE,SAAO;UACA,OAAO;AACd,UAAQ,KAAK,sCAAsC,MAAM;AACzD,SAAO;;;;;;AAOX,eAAsB,uBAA2D;AAE/E,KAAI,gBAAgB,KAAK,KAAK,GAAG,iBAAiB,WAAW;AAC3D,UAAQ,MAAM,aAAa,aAAa,OAAO,4BAA4B;AAC3E,SAAO;GACL,QAAQ;GACR,MAAM;GACP;;CAIH,MAAM,YAAY,MAAM,oBAAoB;AAE5C,KAAI,aAAa,UAAU,SAAS,GAAG;AACrC,iBAAe;AACf,mBAAiB,KAAK,KAAK;AAE3B,SAAO;GACL,QAAQ;GACR,MAAM;GACP;;AAIH,SAAQ,MACN,aAAa,gBAAgB,OAAO,8BACrC;AAED,QAAO;EACL,QAAQ;EACR,MAAM;EACP;;;;;AAwBH,eAAsB,sBAAyD;AAE7E,gBAAe;AACf,kBAAiB;CAEjB,MAAM,iBAAiB,MAAM,sBAAsB;CAGnD,IAAI,oBAAoB;CACxB,MAAMC,cAOF,EAAE;CAEN,IAAI,kBAAkB;AACtB,MAAK,MAAM,SAAS,eAAe,KACjC,KAAI,MAAM,WAAW;AACnB;EACA,MAAM,YAAY,MAAM,UAAU;AAClC,MAAI,CAAC,qBAAsB,aAAa,YAAY,kBAClD,qBAAoB;AAGtB,cAAY,MAAM,MAAM;GACtB,oBAAoB,MAAM,UAAU;GACpC,YAAY,MAAM,UAAU;GAC5B,mBAAmB,KAAK,MAAM,MAAM,UAAU,oBAAoB,IAAI;GACvE;;AAIL,SAAQ,MACN,sBAAsB,gBAAgB,GAAG,eAAe,KAAK,OAAO,yBACrE;AAED,QAAO;EACL,cAAc;EACd,kBAAkB;EAClB,iBAAiB,EACf,QAAQ,aACT;EACF;;;;;AAaH,SAAgB,gBAAgB,SAA0B;AACxD,QAAO,QAAQ,SAAS,WAAW"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { state } from "./state-CcLGr8VN.js";
|
|
2
|
-
import { HTTPError } from "./error-
|
|
2
|
+
import { HTTPError } from "./error-SzJ4KHd8.js";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
|
|
5
5
|
//#region src/lib/api-config.ts
|
|
@@ -58,4 +58,4 @@ async function getGitHubUser() {
|
|
|
58
58
|
|
|
59
59
|
//#endregion
|
|
60
60
|
export { GITHUB_API_BASE_URL, GITHUB_APP_SCOPES, GITHUB_BASE_URL, GITHUB_CLIENT_ID, copilotBaseUrl, copilotHeaders, getGitHubUser, githubHeaders, standardHeaders };
|
|
61
|
-
//# sourceMappingURL=get-user-
|
|
61
|
+
//# sourceMappingURL=get-user-DEDD9jIs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-user-DEDD9jIs.js","names":["state","headers: Record<string, string>"],"sources":["../src/lib/api-config.ts","../src/services/github/get-user.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\"\n\nimport type { State } from \"./state\"\n\nexport const standardHeaders = () => ({\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n})\n\nconst COPILOT_VERSION = \"0.26.7\"\nconst EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`\nconst USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`\n\n// Updated to match latest Zed implementation - 2025-05-01 returns Claude models\nconst API_VERSION = \"2025-05-01\"\n\n// Use the API endpoint from token response if available, otherwise fall back to default\nexport const copilotBaseUrl = (state: State) => {\n if (state.copilotApiEndpoint) {\n return state.copilotApiEndpoint\n }\n return state.accountType === \"individual\" ?\n \"https://api.githubcopilot.com\"\n : `https://api.${state.accountType}.githubcopilot.com`\n}\nexport const copilotHeaders = (state: State, vision: boolean = false) => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${state.copilotToken}`,\n \"content-type\": standardHeaders()[\"content-type\"],\n \"copilot-integration-id\": \"vscode-chat\",\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"openai-intent\": \"conversation-panel\",\n \"x-github-api-version\": API_VERSION,\n \"x-request-id\": randomUUID(),\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n }\n\n if (vision) headers[\"copilot-vision-request\"] = \"true\"\n\n return headers\n}\n\nexport const GITHUB_API_BASE_URL = \"https://api.github.com\"\nexport const githubHeaders = (state: State) => ({\n ...standardHeaders(),\n authorization: `token ${state.githubToken}`,\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"x-github-api-version\": API_VERSION,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n})\n\nexport const GITHUB_BASE_URL = \"https://github.com\"\nexport const GITHUB_CLIENT_ID = \"Iv1.b507a08c87ecfe98\"\nexport const GITHUB_APP_SCOPES = [\"read:user\"].join(\" \")\n","import { GITHUB_API_BASE_URL, standardHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport async function getGitHubUser() {\n const response = await fetch(`${GITHUB_API_BASE_URL}/user`, {\n headers: {\n authorization: `token ${state.githubToken}`,\n ...standardHeaders(),\n },\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get GitHub user\", response)\n\n return (await response.json()) as GithubUserResponse\n}\n\n// Trimmed for the sake of simplicity\ninterface GithubUserResponse {\n login: string\n}\n"],"mappings":";;;;;AAIA,MAAa,yBAAyB;CACpC,gBAAgB;CAChB,QAAQ;CACT;AAED,MAAM,kBAAkB;AACxB,MAAM,wBAAwB,gBAAgB;AAC9C,MAAM,aAAa,qBAAqB;AAGxC,MAAM,cAAc;AAGpB,MAAa,kBAAkB,YAAiB;AAC9C,KAAIA,QAAM,mBACR,QAAOA,QAAM;AAEf,QAAOA,QAAM,gBAAgB,eACzB,kCACA,eAAeA,QAAM,YAAY;;AAEvC,MAAa,kBAAkB,SAAc,SAAkB,UAAU;CACvE,MAAMC,UAAkC;EACtC,eAAe,UAAUD,QAAM;EAC/B,gBAAgB,iBAAiB,CAAC;EAClC,0BAA0B;EAC1B,kBAAkB,UAAUA,QAAM;EAClC,yBAAyB;EACzB,cAAc;EACd,iBAAiB;EACjB,wBAAwB;EACxB,gBAAgB,YAAY;EAC5B,uCAAuC;EACxC;AAED,KAAI,OAAQ,SAAQ,4BAA4B;AAEhD,QAAO;;AAGT,MAAa,sBAAsB;AACnC,MAAa,iBAAiB,aAAkB;CAC9C,GAAG,iBAAiB;CACpB,eAAe,SAASA,QAAM;CAC9B,kBAAkB,UAAUA,QAAM;CAClC,yBAAyB;CACzB,cAAc;CACd,wBAAwB;CACxB,uCAAuC;CACxC;AAED,MAAa,kBAAkB;AAC/B,MAAa,mBAAmB;AAChC,MAAa,oBAAoB,CAAC,YAAY,CAAC,KAAK,IAAI;;;;ACrDxD,eAAsB,gBAAgB;CACpC,MAAM,WAAW,MAAM,MAAM,GAAG,oBAAoB,QAAQ,EAC1D,SAAS;EACP,eAAe,SAAS,MAAM;EAC9B,GAAG,iBAAiB;EACrB,EACF,CAAC;AAEF,KAAI,CAAC,SAAS,GAAI,OAAM,IAAI,UAAU,6BAA6B,SAAS;AAE5E,QAAQ,MAAM,SAAS,MAAM"}
|
package/dist/main.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { PATHS, ensurePaths } from "./paths-CVYLp61D.js";
|
|
3
3
|
import { state } from "./state-CcLGr8VN.js";
|
|
4
|
-
import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders } from "./get-user-
|
|
5
|
-
import { HTTPError, forwardError } from "./error-
|
|
6
|
-
import { cacheModels, cacheVSCodeVersion, clearGithubToken, findModel, isNullish, setupCopilotToken, setupGitHubToken, sleep } from "./token-
|
|
4
|
+
import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders } from "./get-user-DEDD9jIs.js";
|
|
5
|
+
import { HTTPError, forwardError } from "./error-SzJ4KHd8.js";
|
|
6
|
+
import { cacheModels, cacheVSCodeVersion, clearGithubToken, findModel, isNullish, refreshCopilotToken, setupCopilotToken, setupGitHubToken, sleep } from "./token-CpxbiiIw.js";
|
|
7
7
|
import { clearAntigravityAuth, disableCurrentAccount, getAntigravityAuthPath, getApiKey, getCurrentProjectId, getValidAccessToken, rotateAccount } from "./auth-CWGl6kMf.js";
|
|
8
8
|
import { clearZenAuth, getZenAuthPath } from "./auth-BrdL89xk.js";
|
|
9
9
|
import { getAntigravityModels, getAntigravityUsage, isThinkingModel } from "./get-models-uEbEgq0L.js";
|
|
@@ -1197,12 +1197,24 @@ const apiKeyAuthMiddleware = async (c, next) => {
|
|
|
1197
1197
|
* Global token usage store for passing usage info from handlers to logger.
|
|
1198
1198
|
* Handlers call setTokenUsage() when usage is available,
|
|
1199
1199
|
* logger reads and clears it after await next().
|
|
1200
|
+
*
|
|
1201
|
+
* For streaming responses, usage arrives after next() returns.
|
|
1202
|
+
* In that case the handler calls signalStreamDone() when the stream ends,
|
|
1203
|
+
* and the logger waits for it with a timeout.
|
|
1200
1204
|
*/
|
|
1201
1205
|
let pendingTokenUsage;
|
|
1206
|
+
let streamDoneResolve;
|
|
1202
1207
|
function setTokenUsage(usage) {
|
|
1203
1208
|
pendingTokenUsage = usage;
|
|
1204
1209
|
}
|
|
1205
1210
|
/**
|
|
1211
|
+
* Notify the logger that a streaming response has finished sending.
|
|
1212
|
+
* Must be called at the end of streamSSE callbacks.
|
|
1213
|
+
*/
|
|
1214
|
+
function signalStreamDone() {
|
|
1215
|
+
streamDoneResolve?.();
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1206
1218
|
* Get timestamp string in format HH:mm:ss
|
|
1207
1219
|
*/
|
|
1208
1220
|
function getTime() {
|
|
@@ -1252,9 +1264,16 @@ function modelLogger() {
|
|
|
1252
1264
|
const modelPrefix = model ? `[${model}] ` : "";
|
|
1253
1265
|
const startTime = getTime();
|
|
1254
1266
|
pendingTokenUsage = void 0;
|
|
1267
|
+
const localStreamDone = new Promise((resolve) => {
|
|
1268
|
+
streamDoneResolve = resolve;
|
|
1269
|
+
});
|
|
1255
1270
|
console.log(`${modelPrefix}${startTime} <-- ${method} ${fullPath}`);
|
|
1256
1271
|
const start$1 = Date.now();
|
|
1257
1272
|
await next();
|
|
1273
|
+
if (c.res.headers.get("content-type")?.includes("text/event-stream") && !pendingTokenUsage) {
|
|
1274
|
+
const timeout = new Promise((resolve) => setTimeout(resolve, 12e4));
|
|
1275
|
+
await Promise.race([localStreamDone, timeout]);
|
|
1276
|
+
}
|
|
1258
1277
|
const duration = Date.now() - start$1;
|
|
1259
1278
|
const endTime = getTime();
|
|
1260
1279
|
const usage = pendingTokenUsage;
|
|
@@ -2944,10 +2963,10 @@ const createChatCompletions = async (payload) => {
|
|
|
2944
2963
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
2945
2964
|
const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x$1) => x$1.type === "image_url"));
|
|
2946
2965
|
const isAgentCall = payload.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
|
|
2947
|
-
const
|
|
2966
|
+
const buildHeaders = () => ({
|
|
2948
2967
|
...copilotHeaders(state, enableVision),
|
|
2949
2968
|
"X-Initiator": isAgentCall ? "agent" : "user"
|
|
2950
|
-
};
|
|
2969
|
+
});
|
|
2951
2970
|
consola.debug("Sending request to Copilot:", {
|
|
2952
2971
|
model: payload.model,
|
|
2953
2972
|
endpoint: `${copilotBaseUrl(state)}/chat/completions`
|
|
@@ -2957,16 +2976,16 @@ const createChatCompletions = async (payload) => {
|
|
|
2957
2976
|
...payload,
|
|
2958
2977
|
stream_options: { include_usage: true }
|
|
2959
2978
|
} : payload;
|
|
2960
|
-
const
|
|
2961
|
-
method: "POST",
|
|
2962
|
-
headers,
|
|
2963
|
-
body: JSON.stringify(body)
|
|
2964
|
-
};
|
|
2979
|
+
const bodyString = JSON.stringify(body);
|
|
2965
2980
|
const maxRetries = 2;
|
|
2966
2981
|
let lastError;
|
|
2967
2982
|
let response;
|
|
2968
2983
|
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
2969
|
-
response = await fetch(url,
|
|
2984
|
+
response = await fetch(url, {
|
|
2985
|
+
method: "POST",
|
|
2986
|
+
headers: buildHeaders(),
|
|
2987
|
+
body: bodyString
|
|
2988
|
+
});
|
|
2970
2989
|
break;
|
|
2971
2990
|
} catch (error) {
|
|
2972
2991
|
lastError = error;
|
|
@@ -2977,6 +2996,19 @@ const createChatCompletions = async (payload) => {
|
|
|
2977
2996
|
}
|
|
2978
2997
|
}
|
|
2979
2998
|
if (!response) throw lastError;
|
|
2999
|
+
if (response.status === 401) {
|
|
3000
|
+
consola.warn("Copilot token expired, refreshing and retrying...");
|
|
3001
|
+
try {
|
|
3002
|
+
await refreshCopilotToken();
|
|
3003
|
+
response = await fetch(url, {
|
|
3004
|
+
method: "POST",
|
|
3005
|
+
headers: buildHeaders(),
|
|
3006
|
+
body: bodyString
|
|
3007
|
+
});
|
|
3008
|
+
} catch (refreshError) {
|
|
3009
|
+
consola.error("Failed to refresh token:", refreshError);
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
2980
3012
|
if (!response.ok) {
|
|
2981
3013
|
const errorBody = await response.text();
|
|
2982
3014
|
consola.error("Failed to create chat completions", {
|
|
@@ -3006,7 +3038,7 @@ async function processPayloadTokens(payload) {
|
|
|
3006
3038
|
}
|
|
3007
3039
|
try {
|
|
3008
3040
|
const tokenCount = await getTokenCount(payload, selectedModel);
|
|
3009
|
-
consola.
|
|
3041
|
+
consola.debug("Current token count:", tokenCount);
|
|
3010
3042
|
const truncated = await truncateMessages(payload, selectedModel);
|
|
3011
3043
|
if (isNullish(truncated.max_tokens)) {
|
|
3012
3044
|
const withMaxTokens = {
|
|
@@ -3053,12 +3085,12 @@ async function handleCompletion$1(c) {
|
|
|
3053
3085
|
cacheReadTokens: parsed.usage.prompt_tokens_details?.cached_tokens
|
|
3054
3086
|
};
|
|
3055
3087
|
setTokenUsage(usage);
|
|
3056
|
-
console.log(`[${formatTokenUsage(usage)}]`);
|
|
3057
3088
|
}
|
|
3058
3089
|
}
|
|
3059
3090
|
} catch {}
|
|
3060
3091
|
await stream.writeSSE(chunk);
|
|
3061
3092
|
}
|
|
3093
|
+
signalStreamDone();
|
|
3062
3094
|
});
|
|
3063
3095
|
}
|
|
3064
3096
|
const isNonStreaming$1 = (response) => Object.hasOwn(response, "choices");
|
|
@@ -3341,7 +3373,7 @@ async function handleCountTokens(c) {
|
|
|
3341
3373
|
let finalTokenCount = tokenCount.input + tokenCount.output;
|
|
3342
3374
|
if (anthropicPayload.model.startsWith("claude")) finalTokenCount = Math.round(finalTokenCount * 1.15);
|
|
3343
3375
|
else if (anthropicPayload.model.startsWith("grok")) finalTokenCount = Math.round(finalTokenCount * 1.03);
|
|
3344
|
-
consola.
|
|
3376
|
+
consola.debug("Token count:", finalTokenCount);
|
|
3345
3377
|
return c.json({ input_tokens: finalTokenCount });
|
|
3346
3378
|
} catch (error) {
|
|
3347
3379
|
consola.error("Error counting tokens:", error);
|
|
@@ -3530,13 +3562,13 @@ async function handleCompletion(c) {
|
|
|
3530
3562
|
cacheReadTokens: chunk.usage.prompt_tokens_details?.cached_tokens
|
|
3531
3563
|
};
|
|
3532
3564
|
setTokenUsage(usage);
|
|
3533
|
-
console.log(`[${formatTokenUsage(usage)}]`);
|
|
3534
3565
|
}
|
|
3535
3566
|
for (const event of events$1) await stream.writeSSE({
|
|
3536
3567
|
event: event.type,
|
|
3537
3568
|
data: JSON.stringify(event)
|
|
3538
3569
|
});
|
|
3539
3570
|
}
|
|
3571
|
+
signalStreamDone();
|
|
3540
3572
|
});
|
|
3541
3573
|
}
|
|
3542
3574
|
const isNonStreaming = (response) => Object.hasOwn(response, "choices");
|
|
@@ -4109,7 +4141,7 @@ async function runServer(options$1) {
|
|
|
4109
4141
|
state.githubToken = options$1.githubToken;
|
|
4110
4142
|
consola.info("Using provided GitHub token");
|
|
4111
4143
|
try {
|
|
4112
|
-
const { getGitHubUser } = await import("./get-user-
|
|
4144
|
+
const { getGitHubUser } = await import("./get-user-HhhC3uQr.js");
|
|
4113
4145
|
const user = await getGitHubUser();
|
|
4114
4146
|
consola.info(`Logged in as ${user.login}`);
|
|
4115
4147
|
} catch (error) {
|
|
@@ -4120,10 +4152,10 @@ async function runServer(options$1) {
|
|
|
4120
4152
|
try {
|
|
4121
4153
|
await setupCopilotToken();
|
|
4122
4154
|
} catch (error) {
|
|
4123
|
-
const { HTTPError: HTTPError$1 } = await import("./error-
|
|
4155
|
+
const { HTTPError: HTTPError$1 } = await import("./error-DNWWcl_s.js");
|
|
4124
4156
|
if (error instanceof HTTPError$1 && error.response.status === 401) {
|
|
4125
4157
|
consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
|
|
4126
|
-
const { clearGithubToken: clearGithubToken$1 } = await import("./token-
|
|
4158
|
+
const { clearGithubToken: clearGithubToken$1 } = await import("./token-DkNaoDp7.js");
|
|
4127
4159
|
await clearGithubToken$1();
|
|
4128
4160
|
consola.info("Please restart to re-authenticate");
|
|
4129
4161
|
}
|