@jeffreycao/copilot-api 1.10.10 → 1.10.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.zh-CN.md CHANGED
@@ -2,25 +2,6 @@
2
2
 
3
3
  [English](./README.md) | 简体中文
4
4
 
5
- > [!WARNING]
6
- > 这是一个通过逆向工程实现的 GitHub Copilot API 代理。它不受 GitHub 官方支持,并且可能随时异常失效。请自行承担使用风险。当前版本中,如果不使用 opencode OAuth,设备 ID 和机器 ID 会被发送给 GitHub Copilot。不建议在单台设备上使用大量账号;如确有需要,建议放在 Docker 容器中运行。
7
-
8
- > [!WARNING]
9
- > **GitHub 安全提示:**
10
- > 过度自动化或脚本化地使用 Copilot(包括高频或批量请求,例如通过自动化工具发起)可能触发 GitHub 的滥用检测系统。
11
- > 你可能会收到 GitHub Security 的警告,进一步的异常活动还可能导致 Copilot 访问权限被暂时停用。
12
- >
13
- > GitHub 禁止使用其服务器进行过度批量自动化活动,或任何对其基础设施造成不当负载的行为。
14
- >
15
- > 请阅读:
16
- >
17
- > - [GitHub Acceptable Use Policies](https://docs.github.com/site-policy/acceptable-use-policies/github-acceptable-use-policies#4-spam-and-inauthentic-activity-on-github)
18
- > - [GitHub Copilot Terms](https://docs.github.com/site-policy/github-terms/github-terms-for-additional-products-and-features#github-copilot)
19
- >
20
- > 请负责任地使用本代理,避免账号受到限制。
21
-
22
- ---
23
-
24
5
  ## 重要说明
25
6
 
26
7
  > [!IMPORTANT]
@@ -28,100 +9,35 @@
28
9
  >
29
10
  > 1. **Claude Code 配置:** 与 Claude Code 搭配使用时,请将模型 ID 配置为 `claude-opus-4-6` 或 `claude-opus-4.6`(不要带 `[1m]` 后缀,超出 GitHub Copilot 上下文窗口限制太多可能导致账号被封)。示例 claude `settings.json` 见 [通过 `settings.json` 手动配置](#manual-configuration-with-settingsjson)。
30
11
  >
31
- > 2. **推荐给 opencode 用户:** opencode 搭配时,推荐优先使用 opencode OAuth app 启动。该方式与 opencode 内置的 GitHub Copilot provider 行为一致,且不存在 Terms of Service 风险:
12
+ > 2. **推荐给 opencode 用户:** opencode 而言,优先使用 opencode OAuth app。它与 opencode 内置的 GitHub Copilot provider 行为一致,且没有 Terms of Service 风险:
32
13
  > ```sh
33
14
  > npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
34
15
  > ```
35
16
  >
36
- > 3. **通过 codex 使用时请关闭 multi agent:** 如果你是通过 GitHub Copilot 使用 codex,建议关闭 multi agent 功能。目前 GitHub Copilot 在 codex 场景下会按最后一条消息是否为 user role 计费,而这部分计费逻辑尚未调整。
17
+ > 3. **内置 `codex` provider:** 执行一次 `npx @jeffreycao/copilot-api@latest auth login --provider codex` 后,AI gateway 会自动持久化并刷新 Codex OAuth 凭据。
18
+ >
19
+ > 4. **通过 codex 使用时请关闭 multi agent:** 如果你是通过 GitHub Copilot 使用 codex,请关闭 multi agent。当前 Copilot 会按最后一条消息是否为 user role 对 codex 流量计费,而这部分逻辑尚未调整。
20
+ >
21
+ > 5. **注意事项:** README 顶部移除的 GitHub Copilot warning 见 [GitHub Copilot 安全提示](./NOTICE.md#github-copilot-security-notice)。
37
22
 
38
23
  ---
39
24
 
40
25
  ## 项目概览
41
26
 
42
- 这是一个通过逆向工程实现的 GitHub Copilot API 代理,它将 Copilot 暴露为同时兼容 OpenAI 和 Anthropic 的服务。这样你就可以在任何支持 OpenAI Chat Completions / Responses API Anthropic Messages API 的工具中使用 GitHub Copilot,包括把它作为 [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) 的后端。
27
+ 这个项目最初是一个通过逆向工程实现的 GitHub Copilot API 代理,但现在也可以看作一个小型 AI gateway。除了 Copilot,它还可以在同一个 OpenAI / Anthropic 兼容入口后面路由内置的 `codex` provider 和第三方 provider,例如 DashScope,让 [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) 这类工具复用同一个本地端点。
43
28
 
44
- 相比单纯把所有请求都转成 Chat Completions 兼容模式,这个代理可以对 Claude 系模型优先使用 Copilot 原生的 Anthropic 风格 Messages API,保留更多原生思考与工具调用语义,减少预热或恢复工具轮次时不必要的 Premium 请求消耗,并暴露带阶段感知的 `gpt-5.4` / `gpt-5.3-codex` 响应,让用户更容易跟踪模型正在做什么。
29
+ GitHub Copilot 路径上,AI gateway 会在可用时优先使用 Copilot 原生的 Anthropic 风格 Messages API,在重工具调用场景下保留更原生的 Claude 行为。
45
30
 
46
31
  ## 功能特性
47
32
 
48
- - **OpenAI 与 Anthropic 双兼容**:以 OpenAI 兼容接口(`/v1/responses`、`/v1/chat/completions`、`/v1/models`、`/v1/embeddings`)和 Anthropic 兼容接口(`/v1/messages`)对外暴露 GitHub Copilot
49
- - **Claude 模型优先走 Anthropic 原生路由**:当模型支持 Copilot 原生 `/v1/messages` 端点时,代理会优先使用它,而不是 `/responses` `/chat/completions`,从而保留 Anthropic 风格的 `tool_use` / `tool_result` 流程以及更原生的 Claude 行为。
50
- - **减少不必要的 Premium 请求**:通过把预热请求路由到 `smallModel`、将 `tool_result` 的后续消息重新并入工具流,以及把恢复的工具轮次视为延续流量而非全新高级交互,减少浪费的 premium 使用量。
51
- - **分阶段的 `gpt-5.4` 与 `gpt-5.3-codex`**:这些模型可以在更深入推理或调用工具前先发出面向用户的 commentary,让长时间运行的编码操作更容易理解,而不是突然开始一串工具调用。
52
- - **支持 Claude 原生 Beta 能力**:在 Messages API 路径上支持 Anthropic 原生能力,例如 `interleaved-thinking`、`advanced-tool-use` 和 `context-management`;这些能力在普通 Chat Completions 兼容模式下通常很难支持,或根本不可用。
53
- - **Subagent 标记集成**:Claude Code 与 opencode 插件可以注入 `__SUBAGENT_MARKER__...`,并传递 `x-session-id`,从而让 subagent 流量保留正确的根会话以及 agent/user 语义。
54
- - **通过 `@ai-sdk/anthropic` 接入 OpenCode**:可以将 OpenCode 指向这个代理作为 Anthropic provider,从而端到端保留 Anthropic Messages 语义、premium request 优化以及更原生的 Claude 行为。
55
- - **Claude Code 集成**:可通过简单的命令行参数(`--claude-code`)快速配置并启动 [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) 使用 Copilot 作为后端。
56
- - **使用量看板**:提供基于 Web 的看板,用于监控 Copilot API 使用情况、查看额度以及详细统计数据。
57
- - **速率限制控制**:通过速率限制选项(`--rate-limit`)和等待机制(`--wait`)管理 API 使用,避免因请求过快而报错。
58
- - **手动请求审批**:可对每个 API 请求进行手动批准或拒绝,实现更细粒度的使用控制(`--manual`)。
59
- - **令牌可见性**:可在认证和刷新期间显示 GitHub 与 Copilot token,便于调试(`--show-token`)。
60
- - **灵活认证方式**:既可交互式认证,也可以直接传入 GitHub token,适合 CI/CD 等非交互环境。
61
- - **支持不同账号类型**:兼容个人版、Business 和 Enterprise 的 GitHub Copilot 方案。
62
- - **支持 opencode OAuth**:可通过设置环境变量 `COPILOT_API_OAUTH_APP=opencode` 或使用命令行参数 `--oauth-app=opencode` 来启用 opencode GitHub Copilot 认证。
63
- - **支持 GitHub Enterprise**:可通过设置环境变量 `COPILOT_API_ENTERPRISE_URL`(例如 `company.ghe.com`)或命令行参数 `--enterprise-url=company.ghe.com` 连接到 GHE.com。
64
- - **自定义数据目录**:可通过环境变量 `COPILOT_API_HOME` 或命令行参数 `--api-home=/path/to/dir` 修改默认数据目录(存放 token 和配置)。
65
- - **多 Provider Messages 代理路由**:可以添加全局 provider 配置,并通过 `/:provider/v1/messages` 与 `/:provider/v1/models` 调用外部 Anthropic 或 OpenAI 兼容 API,也可以把 `model` 写成 `"provider/model"` 后直接发到顶层 `/v1/messages`。
66
- - **精确的 Claude Token 计数**:可以选择将 Claude 模型的 `/v1/messages/count_tokens` 请求转发到 Anthropic 的免费 token counting 端点,以获得精确计数,而不是依赖 GPT tokenizer 估算。
67
- - **GPT 上下文管理**:可通过 `responsesApiContextManagementModels` 为长上下文 GPT 对话启用可配置的上下文压缩,在接近 token 限制时减少不必要的 Premium 请求。详见 [配置](#configuration-configjson)。
68
-
69
- ## 更好的 Agent 语义
70
-
71
- ### 在可用时优先使用原生 Anthropic Messages API
72
-
73
- 对于那些声明支持 Copilot `/v1/messages` 的模型,本项目会优先把请求发送到原生 Messages API,只有在需要时才回退到 `/responses` 或 `/chat/completions`。
74
-
75
- 相比仅通过 Chat Completions 兼容层使用 Claude 系模型,Messages API 路径能保留更多 Anthropic 原生行为,包括支持:
76
-
77
- - `interleaved-thinking-2025-05-14`
78
- - `advanced-tool-use-2025-11-20`
79
- - `context-management-2025-06-27`
80
-
81
- 支持的 `anthropic-beta` 值会在原生 Messages 路径中过滤并透传;当请求了非自适应扩展思考的 thinking budget 时,也会自动添加 `interleaved-thinking`。
82
-
83
- ### 减少不必要的 Premium 请求
84
-
85
- 这个代理内置了一些请求计费保护逻辑,专门面向重工具调用的编码工作流:
86
-
87
- - 无工具的预热或探测请求可以强制走 `smallModel`,避免后台检查消耗 premium 使用量;
88
- - 混合了 `tool_result` 和补充提示文本的消息块会重新并入 `tool_result` 流,而不会被当成新的用户轮次计费;
89
- - `x-initiator` 会根据最新一条消息或 item 推导,而不是依赖陈旧的 assistant 历史。
90
-
91
- 这样可以让恢复的工具轮次被视为既有工作流的延续,而不是一条全新的 Premium 请求。
92
-
93
- ### 分阶段的 `gpt-5.4` 与 `gpt-5.3-codex`
94
-
95
- 默认情况下,内置的 `extraPrompts` 会为 `gpt-5.4` 与 `gpt-5.3-codex` 启用中间进度更新行为,代理会在工具调用前把 assistant 轮次翻译成 `phase: "commentary"`,并在最终回复时使用 `phase: "final_answer"`。
96
-
97
- 这样客户端在更深入的推理或工具执行开始前,就能先收到一段简短、面向用户的说明。
98
-
99
- ### Subagent 标记集成
100
-
101
- 对于基于 subagent 的客户端,本项目可以保留根会话上下文,并正确识别来自 subagent 的流量。
102
-
103
- 这一标记流程会在 `<system-reminder>` 中放入 `__SUBAGENT_MARKER__...`,同时传递根级 `x-session-id`。当检测到该标记后,代理可以保留父会话身份、推导 `x-initiator: agent`,并把交互标记为 subagent 流量,而不是新的顶层请求。
104
-
105
- 项目中已经为 Claude Code 和 opencode 都提供了插件集成;配置方法见下文 [插件集成](#plugin-integrations)。
106
-
107
- <a id="accurate-claude-token-counting"></a>
108
-
109
- ### 精确的 Claude Token 计数
110
-
111
- 默认情况下,`/v1/messages/count_tokens` 会使用 GPT 的 `o200k_base` tokenizer,并乘以 1.15 倍来估算 Claude token 数。这个估算通常会低于 Claude 的真实 token 使用量,导致像 Claude Code 这类工具压缩上下文太晚,从而触发 “prompt token count exceeds limit” 之类的错误。
112
-
113
- 当配置了 Anthropic API key 后,代理会把 Claude 模型的 token 计数请求转发到 [Anthropic 真实的 `/v1/messages/count_tokens` 端点](https://docs.anthropic.com/en/docs/build-with-claude/token-counting)。这样能返回精确计数,消除估算误差。不属于 Claude 的模型,以及转发失败的情况,会自动回退到 GPT tokenizer 估算。
114
-
115
- **设置方式:**
116
-
117
- 1. 在 [console.anthropic.com](https://console.anthropic.com) 创建 Anthropic API 账户,并至少充值 5 美元信用额度(这是激活 API key 所需条件,但 token counting 端点本身是免费的)
118
- 2. 在 Settings > API Keys 中创建一个 API key
119
- 3. 通过以下 **任一** 方式配置:
120
- - `config.json`:设置 `"anthropicApiKey": "sk-ant-..."`
121
- - 环境变量:`ANTHROPIC_API_KEY=sk-ant-...`
122
-
123
- > [!NOTE]
124
- > Anthropic 的 `/v1/messages/count_tokens` 端点是 **免费的**(不会按 token 收费)。在 Tier 1 下速率限制为 100 RPM。这里要求预充值 5 美元,只是为了激活 API 访问权限,token counting 调用本身不产生费用。
33
+ - **OpenAI 与 Anthropic 双兼容**:通过 `/v1/responses`、`/v1/chat/completions`、`/v1/models`、`/v1/embeddings` `/v1/messages` 对外暴露同一个本地 AI gateway
34
+ - **同一网关接入 Copilot、`codex` 与第三方 provider**:可统一路由 GitHub Copilot、内置 `codex` provider 和配置好的外部 provider。
35
+ - **面向 Claude 的更原生 Copilot 路由**:优先使用原生 `/v1/messages`,保留 Claude 风格工具流,支持 Anthropic beta 能力,并保留 subagent / session 标记。
36
+ - **Claude CodeOpenCode 集成**:兼容 Claude Code 与 OpenCode,也支持通过 `@ai-sdk/anthropic` 直接作为 Anthropic provider 使用。
37
+ - **灵活的认证与部署选项**:支持交互式登录、直接 token、个人 / Business / Enterprise、GitHub Enterprise、opencode OAuth 和自定义数据目录。
38
+ - **本地控制与可观测性**:提供使用量看板、速率限制、手动审批,以及调试时显示 token 的能力。
39
+ - **多 provider 路由**:可暴露 `/:provider/...` 路由,也可在顶层 API 上使用 `model: "provider/model"`。
40
+ - **更好的 token 与上下文管理**:支持精确 Claude token 计数,以及面向长对话的 GPT 上下文压缩。
125
41
 
126
42
  ## 前置要求
127
43
 
@@ -197,57 +113,21 @@ https://github.com/caozhiyuan/copilot-api/releases
197
113
  docker build -t copilot-api .
198
114
  ```
199
115
 
200
- 运行容器:
116
+ 通过 bind mount 运行容器,让认证数据在重启后保留:
201
117
 
202
118
  ```sh
203
- # 在宿主机创建目录,用于持久化 GitHub token 及相关数据
204
119
  mkdir -p ./copilot-data
205
-
206
- # 通过 bind mount 持久化 token
207
- # 这样容器重启后认证信息仍会保留
208
-
209
120
  docker run -p 4141:4141 -v $(pwd)/copilot-data:/root/.local/share/copilot-api copilot-api
210
121
  ```
211
122
 
212
- > **注意:**
213
- > GitHub token 及相关数据会保存在宿主机的 `copilot-data` 目录中。该目录会映射到容器内的 `/root/.local/share/copilot-api`,从而在容器重启后继续保留数据。
123
+ 这会把宿主机上的 `./copilot-data` 映射到容器内的 `/root/.local/share/copilot-api`,用于持久化 GitHub 认证数据。
214
124
 
215
- ### Docker 中使用环境变量
216
-
217
- 你也可以直接通过环境变量把 GitHub token 传给容器:
125
+ 也可以直接通过环境变量传入 GitHub token:
218
126
 
219
127
  ```sh
220
- # 构建时注入 GitHub token
221
- docker build --build-arg GH_TOKEN=your_github_token_here -t copilot-api .
222
-
223
- # 运行时注入 GitHub token
224
128
  docker run -p 4141:4141 -e GH_TOKEN=your_github_token_here copilot-api
225
-
226
- # 搭配附加选项运行
227
- docker run -p 4141:4141 -e GH_TOKEN=your_token copilot-api start --verbose --port 4141
228
- ```
229
-
230
- ### Docker Compose 示例
231
-
232
- ```yaml
233
- version: "3.8"
234
- services:
235
- copilot-api:
236
- build: .
237
- ports:
238
- - "4141:4141"
239
- environment:
240
- - GH_TOKEN=your_github_token_here
241
- restart: unless-stopped
242
129
  ```
243
130
 
244
- Docker 镜像包含:
245
-
246
- - 多阶段构建,以优化镜像体积
247
- - 非 root 用户,以增强安全性
248
- - 用于容器监控的健康检查
249
- - 固定基础镜像版本,以保证可复现构建
250
-
251
131
  ## 命令结构
252
132
 
253
133
  Copilot API 现在使用子命令结构,主要命令包括:
@@ -376,7 +256,7 @@ Copilot API 现在使用子命令结构,主要命令包括:
376
256
  - **auth.adminApiKey:** 仅用于 `/admin/*` 路由的单个 admin key。若未配置,服务会在启动时自动生成一个随机 key,并回写到 `config.json`。它同样使用 `x-api-key` 或 `Authorization: Bearer` 这两种头,但普通 `auth.apiKeys` 不能访问 `/admin/*`。
377
257
  - **modelMappings:** 用于顶层 `POST /v1/messages` 和 `POST /v1/messages/count_tokens` 请求的精确 `sourceModel -> targetModel` 重写映射。省略该字段或保留为 `{}` 时,不会做模型重写。`source` 和 `target` 都必须是非空字符串。`target` 可以是普通模型 ID,也可以是 `provider/model` 形式的别名,例如 `dashscope/qwen3.6-plus`;重写发生在 provider alias 解析之前。`GET/POST /admin/config/model-mappings` 管理接口读写的也只有这个字段。
378
258
  - **extraPrompts:** `model -> prompt` 的映射。把 Anthropic 风格请求翻译给 Copilot 时,会将其附加到第一条 system prompt 后面。你可以借此为不同模型注入护栏或指引。缺失的默认项会自动补齐,但不会覆盖你自定义的 prompt。内置的 `gpt-5.3-codex` 和 `gpt-5.4` prompt 会启用带阶段感知的 commentary,让模型在工具调用或更深层推理前先发出简短的用户可见进度说明。
379
- - **providers:** 全局上游 provider 映射。每个 provider key(例如 `custom`)都会变成一个路由前缀(`/custom/v1/messages`)。支持 `type: "anthropic"`、`type: "openai-compatible"` 和 `type: "openai-responses"`。顶层客户端也可以在 `/v1/messages`、`/v1/messages/count_tokens` 和 `/v1/responses` 中使用 `model: "custom/model-id"`;代理会在转发上游前移除 `custom/` 前缀。`GET /v1/models` 不聚合 provider 模型;provider 模型列表请使用 `GET /custom/v1/models`。
259
+ - **providers:** 全局上游 provider 映射。每个 provider key(例如 `dashscope`)都会变成一个路由前缀(`/dashscope/v1/messages`)。支持 `type: "anthropic"`、`type: "openai-compatible"` 和 `type: "openai-responses"`。顶层客户端也可以在 `/v1/messages`、`/v1/messages/count_tokens` 和 `/v1/responses` 中使用 `model: "dashscope/model-id"`;AI gateway 会在转发上游前移除 `dashscope/` 前缀。`GET /v1/models` 不聚合 provider 模型;provider 模型列表请使用 `GET /dashscope/v1/models`。
380
260
  - `enabled`:可选,若省略则默认为 `true`。
381
261
  - `baseUrl`:provider API 的基础 URL,不要带结尾的 endpoint。Anthropic provider 不要带 `/v1/messages`;OpenAI 兼容 provider 不要带 `/v1/chat/completions`;OpenAI Responses provider 不要带 `/v1/responses`。
382
262
  - `apiKey`:作为上游凭据值使用;普通 provider 必须配置。
@@ -390,14 +270,14 @@ Copilot API 现在使用子命令结构,主要命令包括:
390
270
  - `contextCache`:可选,OpenAI 兼容 provider 默认 `true`,用于启用阿里云百炼/DashScope 的显式缓存(explicit context cache),会按其 Context Cache 格式在最多 4 个 content block 上注入 `cache_control: { "type": "ephemeral" }`。缓存断点策略与 opencode 主链路保持一致:前 2 条 system 消息 + 最后 2 条非 system 消息。标记字符串 content 时会把 `system` / `user` / `assistant` / `tool` 消息转换为 text content part 数组;已有数组 content 则标记最后一个 part。如果模型本身已经支持隐式缓存,或上游不支持该显式缓存扩展字段,可在模型配置中设为 `false`。
391
271
  - `supportPdf`:可选,控制该模型是否支持 PDF/document content。默认 `false`,不支持时会把 PDF 转成提示文本;设为 `true` 时会把 PDF/document 转成 OpenAI Chat Completions 的 file part。
392
272
  - `toolContentSupportType`:可选,配置该模型的 tool result content 支持能力,值为 `array`、`image`、`pdf` 的数组。provider 侧未配置时默认只发送 string tool content。若 `supportPdf` 为 `true` 但这里不包含 `pdf`,tool result 里的 file part 会被转成 user role 消息。Copilot 主链路不使用这个 provider 默认,仍按 array + image 且不支持 PDF 的能力处理。
393
- - **smallModel:** 无工具预热消息的回退模型(例如 Claude Code 的探测请求),用于避免消耗 premium requests;默认是 `gpt-5-mini`。
394
- - **responsesApiContextManagementModels:** 需要启用 Responses API `context_management` 压缩指令的 GPT 模型 ID 列表。默认是 `[]`,需要你显式开启。一个不错的起点是 `["gpt-5-mini", "gpt-5.3-codex", "gpt-5.4-mini", "gpt-5.4"]`。启用后,请求体会带上 `context_management`,并在后续轮次中仅保留最新的压缩承载内容。实际压缩由服务端完成,看起来会在 usage 接近模型 `maxPromptTokens` 的约 90% 时开始,因此特别适合长任务场景,同时不会额外消耗 premium requests。实践中 `compact_threshold` 似乎也是服务端固定的,所以在本项目中修改它目前不会改变压缩行为。当前该优化仅面向 GPT 系模型。
273
+ - **smallModel:** 无工具预热消息的回退模型(例如 Claude Code 的探测请求);默认是 `gpt-5-mini`。
274
+ - **responsesApiContextManagementModels:** 需要启用 Responses API `context_management` 压缩指令的 GPT 模型 ID 列表。默认是 `[]`,需要你显式开启。一个不错的起点是 `["gpt-5-mini", "gpt-5.3-codex", "gpt-5.4-mini", "gpt-5.4"]`。启用后,请求体会带上 `context_management`,并在后续轮次中仅保留最新的压缩承载内容。实际压缩由服务端完成,看起来会在 usage 接近模型 `maxPromptTokens` 的约 90% 时开始,因此特别适合长任务场景。实践中 `compact_threshold` 似乎也是服务端固定的,所以在本项目中修改它目前不会改变压缩行为。当前该优化仅面向 GPT 系模型。
395
275
  - **modelReasoningEfforts:** 按模型配置发送到 Copilot Responses API 的 `reasoning.effort`。可选值包括 `none`、`minimal`、`low`、`medium`、`high` 和 `xhigh`。若某模型未配置,则默认使用 `high`。
396
276
  - **useMessagesApi:** 当为 `true` 时,支持 Copilot 原生 `/v1/messages` 的 Claude 系模型会走 Messages API;否则回退到 `/chat/completions`。设为 `false` 可禁用 Messages API 路由,始终使用 `/chat/completions`。默认值为 `true`。
397
277
  - **useResponsesApiWebSocket:** 当为 `true` 时,Responses API 请求会优先对声明了 `ws:/responses` 的模型使用 Copilot websocket transport;仅声明 `/responses` 的模型仍走 HTTP。设为 `false` 可禁用 websocket 路由,并在模型支持 `/responses` 时使用 HTTP `/responses`。默认值为 `true`。
398
278
  - **useResponsesApiWebSearch:** 当为 `true` 时,服务端会保留 Responses API 中 `type: "web_search"` 的工具并透传到上游。设为 `false` 则会从 `/responses` payload 中移除这些工具。默认值为 `true`。
399
279
  - **claudeTokenMultiplier:** 用于 Claude `/v1/messages/count_tokens` 请求在本地走 GPT tokenizer 估算时的乘数。默认值为 `1.15`。如果你的客户端仍然过晚触发上下文压缩,可以适当调大。这个配置只会在代理本地估算 Claude token 时生效;如果已经配置 `anthropicApiKey` 且 Anthropic token counting 调用成功,则会直接返回 Anthropic 的精确计数,不会使用这个乘数。
400
- - **anthropicApiKey:** 用于精确 Claude token 计数的 Anthropic API key(参见下方 [精确的 Claude Token 计数](#accurate-claude-token-counting))。也可通过环境变量 `ANTHROPIC_API_KEY` 设置。若未配置,则回退到 GPT tokenizer 估算。
280
+ - **anthropicApiKey:** 用于把 Claude `/v1/messages/count_tokens` 请求转发到 Anthropic 真实 token counting 端点的 API key,这样会返回精确计数,而不是 GPT tokenizer 估算值。也可通过环境变量 `ANTHROPIC_API_KEY` 设置。若未配置,或上游调用失败,则回退到由 `claudeTokenMultiplier` 控制的本地 GPT tokenizer 估算。
401
281
 
402
282
  编辑此文件后即可自定义 prompts,或替换为你自己的快速模型。修改完成后请重启服务(或重新执行命令),让缓存中的配置刷新生效。
403
283
 
@@ -472,7 +352,7 @@ curl http://localhost:4141/admin/config/model-mappings \
472
352
 
473
353
  ## 使用示例
474
354
 
475
- 通过 npx 使用:
355
+ 常用 `npx` 命令:
476
356
 
477
357
  ```sh
478
358
  # 基础启动
@@ -481,69 +361,24 @@ npx @jeffreycao/copilot-api@latest start
481
361
  # 自定义端口并开启详细日志
482
362
  npx @jeffreycao/copilot-api@latest start --port 8080 --verbose
483
363
 
484
- # 使用 GitHub Business 方案账号
485
- npx @jeffreycao/copilot-api@latest start --account-type business
486
-
487
- # 使用 GitHub Enterprise 方案账号
488
- npx @jeffreycao/copilot-api@latest start --account-type enterprise
489
-
490
- # 对每个请求启用手动审批
491
- npx @jeffreycao/copilot-api@latest start --manual
492
-
493
- # 将请求间隔限制为 30 秒
494
- npx @jeffreycao/copilot-api@latest start --rate-limit 30
495
-
496
- # 命中速率限制时等待,而不是直接报错
497
- npx @jeffreycao/copilot-api@latest start --rate-limit 30 --wait
498
-
499
- # 直接传入 GitHub token
500
- npx @jeffreycao/copilot-api@latest start --github-token ghp_YOUR_TOKEN_HERE
501
-
502
- # 仅执行认证流程,并交互式选择内置 provider
364
+ # 执行认证流程
503
365
  npx @jeffreycao/copilot-api@latest auth login
504
366
 
505
- # 显式登录 Codex
506
- npx @jeffreycao/copilot-api@latest auth login --provider codex
507
-
508
- # 显式登录 Copilot,并启用详细日志
509
- npx @jeffreycao/copilot-api@latest auth login --provider copilot --verbose
510
-
511
367
  # 在终端中查看 Copilot 用量与额度(无需启动服务)
512
368
  npx @jeffreycao/copilot-api@latest check-usage
513
369
 
514
- # 输出调试信息,便于排障
515
- npx @jeffreycao/copilot-api@latest debug
516
-
517
370
  # 以 JSON 格式输出调试信息
518
371
  npx @jeffreycao/copilot-api@latest debug --json
519
372
 
520
- # 从环境变量初始化代理(HTTP_PROXY、HTTPS_PROXY 等)
521
- npx @jeffreycao/copilot-api@latest start --proxy-env
522
-
523
- # 使用 opencode GitHub Copilot 认证
524
- COPILOT_API_OAUTH_APP=opencode npx @jeffreycao/copilot-api@latest start
525
-
526
- # 通过命令行设置自定义 API home 目录
527
- npx @jeffreycao/copilot-api@latest --api-home=/path/to/custom/dir start
528
-
529
- # 通过命令行使用 GitHub Enterprise
530
- npx @jeffreycao/copilot-api@latest --enterprise-url=company.ghe.com start
531
-
532
- # 通过命令行使用 opencode OAuth
533
- npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
534
-
535
- # 组合多个全局选项
536
- npx @jeffreycao/copilot-api@latest --api-home=/custom/path --oauth-app=opencode --enterprise-url=company.ghe.com start
537
-
538
373
  # 用 Bun 而不是 Node.js 运行已发布 CLI
539
374
  bunx --bun @jeffreycao/copilot-api@latest start
540
375
  ```
541
376
 
542
377
  ## 与 Claude Code 一起使用
543
378
 
544
- 这个代理可以为 [Claude Code](https://docs.anthropic.com/en/claude-code) 提供后端能力。Claude Code 是 Anthropic 提供的实验性面向开发者的对话式 AI 助手。
379
+ 这个 AI gateway 可以为 [Claude Code](https://docs.anthropic.com/en/claude-code) 提供后端能力。Claude Code 是 Anthropic 提供的实验性面向开发者的对话式 AI 助手。
545
380
 
546
- 有两种方式可以把 Claude Code 配置为使用这个代理:
381
+ 有两种方式可以把 Claude Code 配置为使用这个 AI gateway:
547
382
 
548
383
  ### 通过 `--claude-code` 标志进行交互式配置
549
384
 
@@ -553,7 +388,7 @@ bunx --bun @jeffreycao/copilot-api@latest start
553
388
  npx @jeffreycao/copilot-api@latest start --claude-code
554
389
  ```
555
390
 
556
- 你会被提示选择一个主模型,以及一个用于后台任务的 “small, fast” 模型。选择完成后,会有一条命令被复制到剪贴板中。该命令会设置 Claude Code 使用该代理所需的环境变量。
391
+ 你会被提示选择一个主模型,以及一个用于后台任务的 “small, fast” 模型。选择完成后,会有一条命令被复制到剪贴板中。该命令会设置 Claude Code 使用这个 AI gateway 所需的环境变量。
557
392
 
558
393
  在新的终端中粘贴并执行这条命令,即可启动 Claude Code。
559
394
 
@@ -602,9 +437,9 @@ npx @jeffreycao/copilot-api@latest start --claude-code
602
437
 
603
438
  ## GPT Tool Search
604
439
 
605
- 对于 `gpt-5.4+` 这类 GPT Responses 模型,本代理可以通过一个很小的 MCP bridge 暴露 Responses `tool_search`。Claude Code 和 opencode 都可以使用同一个 bridge,前提是客户端会加载 MCP server,并且 Anthropic Messages 流量会经过本代理。
440
+ 对于 `gpt-5.4+` 这类 GPT Responses 模型,这个 AI gateway 可以通过一个很小的 MCP bridge 暴露 Responses `tool_search`。Claude Code 和 opencode 都可以使用同一个 bridge,前提是客户端会加载 MCP server,并且 Anthropic Messages 流量会经过这个 AI gateway。
606
441
 
607
- GPT 模型不要设置 Claude Code 原生的 `ENABLE_TOOL_SEARCH`。这个开关启用的是 Claude Code 自己的客户端 tool search 模式,可能导致 deferred 工具定义不再转发给代理。本代理需要完整的工具定义,这样才能只保留那一小组常驻加载工具,其余工具统一转换为 Responses deferred namespace。
442
+ GPT 模型不要设置 Claude Code 原生的 `ENABLE_TOOL_SEARCH`。这个开关启用的是 Claude Code 自己的客户端 tool search 模式,可能导致 deferred 工具定义不再转发给 AI gateway。这个 AI gateway 需要完整的工具定义,这样才能只保留那一小组常驻加载工具,其余工具统一转换为 Responses deferred namespace。
608
443
 
609
444
  如果你安装了 `tool-search@copilot-api-marketplace`,Claude Code 会自动带上这个 MCP bridge,可以跳过下面这段 Claude Code MCP 手动配置。
610
445
 
@@ -637,23 +472,23 @@ GPT 模型不要设置 Claude Code 原生的 `ENABLE_TOOL_SEARCH`。这个开关
637
472
 
638
473
  本地开发时可以将命令换成 `bun`,参数换成 `["run", "./src/main.ts", "mcp"]`。
639
474
 
640
- 代理内部现在会把 OpenAI Responses `tool_search` 配置成 client-executed 模式。deferred tools 仍然会作为可搜索 namespace 暴露给模型,但会明确要求模型直接返回下一步要加载的精确工具名列表。
475
+ AI gateway 内部现在会把 OpenAI Responses `tool_search` 配置成 client-executed 模式。deferred tools 仍然会作为可搜索 namespace 暴露给模型,但会明确要求模型直接返回下一步要加载的精确工具名列表。
641
476
 
642
477
  该 bridge 使用直接工具选择,不做 query 搜索。工具入参是 `names`,值为逗号分隔的精确 deferred 工具名,例如 `TaskList,TaskGet,mcp__fetch__fetch`。
643
478
 
644
479
  ## 与 OpenCode 一起使用
645
480
 
646
- OpenCode 已经有直接的 GitHub Copilot provider。本节适用于你希望让 OpenCode 通过 `@ai-sdk/anthropic` 指向这个代理,并复用本 README 前面提到的 agent 行为时。
481
+ OpenCode 已经有直接的 GitHub Copilot provider。本节适用于你希望让 OpenCode 通过 `@ai-sdk/anthropic` 指向这个 AI gateway,并复用本 README 前面提到的 agent 行为时。
647
482
 
648
483
  ### 最小配置
649
484
 
650
- 使用 OpenCode OAuth app 启动代理:
485
+ 使用 OpenCode OAuth app 启动 AI gateway:
651
486
 
652
487
  ```sh
653
488
  npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
654
489
  ```
655
490
 
656
- 然后让 OpenCode 通过 `@ai-sdk/anthropic` 指向该代理。
491
+ 然后让 OpenCode 通过 `@ai-sdk/anthropic` 指向这个 AI gateway。
657
492
 
658
493
  示例 `~/.config/opencode/opencode.json`:
659
494
 
@@ -726,7 +561,7 @@ npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
726
561
 
727
562
  这些字段的重要性:
728
563
 
729
- - `npm: "@ai-sdk/anthropic"` 是关键。OpenCode 会以 Anthropic Messages 语义与该代理通信,而不是把一切扁平化为 OpenAI Chat Completions。
564
+ - `npm: "@ai-sdk/anthropic"` 是关键。OpenCode 会以 Anthropic Messages 语义与这个 AI gateway 通信,而不是把一切扁平化为 OpenAI Chat Completions。
730
565
  - `options.baseURL` 应设为 `http://localhost:4141/v1`;Anthropic SDK 会自动补上 `/messages`、`/models` 和 `/messages/count_tokens`。
731
566
  - `model`、`small_model` 与 `agent.*.model` 让你可以把 `gpt-5.4` 用于 build/plan,同时把探索和后台工作路由到 `gpt-5-mini`。
732
567
  - 如果你在此代理中启用了 `auth.apiKeys`,请把 `dummy` 替换为真实 key;否则任意占位值都可以。
@@ -741,7 +576,7 @@ npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
741
576
 
742
577
  Claude Code 集成现在拆分为两个插件:
743
578
 
744
- - `agent-inject` 会在 `SubagentStart` 时注入 `__SUBAGENT_MARKER__...`,以便本代理推导 `x-initiator: agent`。
579
+ - `agent-inject` 会在 `SubagentStart` 时注入 `__SUBAGENT_MARKER__...`,以便 AI gateway 推导 `x-initiator: agent`。
745
580
  - `tool-search` 会注册用于 GPT Responses deferred tool loading 的 `tool_search` MCP bridge。
746
581
 
747
582
  - 本仓库中的 marketplace catalog:`.claude-plugin/marketplace.json`
@@ -760,7 +595,7 @@ Claude Code 集成现在拆分为两个插件:
760
595
  /plugin install tool-search@copilot-api-marketplace
761
596
  ```
762
597
 
763
- 安装后,`agent-inject` 会在 `SubagentStart` 时注入 `__SUBAGENT_MARKER__...`,该代理会利用它推导 `x-initiator: agent`。
598
+ 安装后,`agent-inject` 会在 `SubagentStart` 时注入 `__SUBAGENT_MARKER__...`,AI gateway 会利用它推导 `x-initiator: agent`。
764
599
 
765
600
  `agent-inject` 还会注册一个 `UserPromptSubmit` hook,并返回 `{"continue": true}`;同时它也可以通过环境变量注入 `SessionStart` reminder 规则:
766
601
 
@@ -789,7 +624,7 @@ cp plugin/opencode/subagent-marker.js ~/.config/opencode/plugins/
789
624
  - 跟踪 subagent 创建的子会话
790
625
  - 自动在 subagent 聊天消息前添加 marker system reminder(`__SUBAGENT_MARKER__...`)
791
626
  - 设置 `x-session-id` 请求头以跟踪会话
792
- - 让该代理能够把来自 subagent 的请求识别为 `x-initiator: agent`
627
+ - 让这个 AI gateway 能够把来自 subagent 的请求识别为 `x-initiator: agent`
793
628
 
794
629
  该插件会挂接到 `session.created`、`session.deleted`、`chat.message` 和 `chat.headers` 事件上,以无缝提供 subagent marker 能力。
795
630
 
@@ -1,4 +1,4 @@
1
- import { H as state, M as loginCodex, a as setupGitHubToken, n as persistCodexCredentials } from "./token-1SfgxCRm.js";
1
+ import { H as state, M as loginCodex, a as setupGitHubToken, n as persistCodexCredentials } from "./token-BVXHiYEl.js";
2
2
  import { b as ensurePaths, y as PATHS } from "./config-ztdkLu9o.js";
3
3
  import { defineCommand } from "citty";
4
4
  import consola from "consola";
@@ -113,4 +113,4 @@ const auth = defineCommand({
113
113
  //#endregion
114
114
  export { auth };
115
115
 
116
- //# sourceMappingURL=auth-CeZtnQE_.js.map
116
+ //# sourceMappingURL=auth-BO_SkMVw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-CeZtnQE_.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { loginCodex } from \"./lib/oauth/codex\"\nimport { PATHS, ensurePaths } from \"./lib/paths\"\nimport { state } from \"./lib/state\"\nimport { persistCodexCredentials, setupGitHubToken } from \"./lib/token\"\n\ninterface RunAuthOptions {\n provider?: string\n verbose: boolean\n showToken: boolean\n}\n\nconst authArgs = {\n provider: {\n type: \"string\",\n description: \"Provider to log in with (copilot or codex)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show provider access token on auth\",\n },\n} as const\n\nconst BUILTIN_PROVIDER_NAMES = [\"copilot\", \"codex\"] as const\n\ntype BuiltinProviderName = (typeof BUILTIN_PROVIDER_NAMES)[number]\n\nconst BUILTIN_PROVIDER_LABELS: Record<BuiltinProviderName, string> = {\n copilot: \"GitHub Copilot\",\n codex: \"OpenAI Codex\",\n}\n\nfunction isBuiltinProviderName(\n providerName: string,\n): providerName is BuiltinProviderName {\n return BUILTIN_PROVIDER_NAMES.includes(providerName as BuiltinProviderName)\n}\n\nasync function resolveProviderSelection(\n providerArg: string | undefined,\n): Promise<BuiltinProviderName> {\n const availableProviders = [...BUILTIN_PROVIDER_NAMES]\n\n if (providerArg !== undefined) {\n const providerName = providerArg.trim()\n if (!isBuiltinProviderName(providerName)) {\n throw new Error(\n `Unknown provider '${providerArg}'. Expected one of: ${availableProviders.join(\", \")}`,\n )\n }\n return providerName\n }\n\n if (availableProviders.length === 1) {\n return availableProviders[0]\n }\n\n const provider = await consola.prompt(\"Select a provider to log in with\", {\n type: \"select\",\n options: availableProviders.map((providerName) => ({\n label: `${BUILTIN_PROVIDER_LABELS[providerName]} (${providerName})`,\n value: providerName,\n })),\n })\n\n if (!provider || !isBuiltinProviderName(provider)) {\n throw new Error(\"No provider selected\")\n }\n\n return provider\n}\n\nasync function loginWithCodex(): Promise<void> {\n const credentials = await loginCodex({\n onAuth(info) {\n consola.info(\"Open the following URL to authenticate with Codex:\")\n consola.log(info.url)\n if (info.instructions) {\n consola.info(info.instructions)\n }\n },\n onPrompt(message) {\n return consola.prompt(message, {\n type: \"text\",\n })\n },\n onProgress(message) {\n consola.debug(message)\n },\n })\n\n await persistCodexCredentials(credentials, { enableProvider: true })\n consola.success(\n `Codex provider config written to ${PATHS.CONFIG_PATH} and credentials written to ${PATHS.CODEX_CREDENTIAL_PATH}`,\n )\n}\n\nasync function loginWithProvider(provider: BuiltinProviderName): Promise<void> {\n if (provider === \"copilot\") {\n await setupGitHubToken({ force: true })\n consola.success(\"GitHub token written to\", PATHS.GITHUB_TOKEN_PATH)\n return\n }\n\n await loginWithCodex()\n}\n\nexport async function runAuthLogin(options: RunAuthOptions): Promise<void> {\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = options.showToken\n\n await ensurePaths()\n const provider = await resolveProviderSelection(options.provider)\n\n consola.info(`Logging in with ${BUILTIN_PROVIDER_LABELS[provider]}`)\n await loginWithProvider(provider)\n}\n\nconst authLogin = defineCommand({\n meta: {\n name: \"login\",\n description: \"Authenticate a builtin provider without running the server\",\n },\n args: authArgs,\n run({ args }) {\n return runAuthLogin({\n provider: args.provider,\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n\nexport const auth = defineCommand({\n meta: {\n name: \"auth\",\n description: \"Run authentication flows without running the server\",\n },\n args: authArgs,\n subCommands: {\n login: authLogin,\n },\n run({ args }) {\n if ((args._[0] ?? \"\").trim()) {\n return\n }\n\n return runAuthLogin({\n provider: args.provider,\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n"],"mappings":";;;;;AAgBA,MAAM,WAAW;CACf,UAAU;EACR,MAAM;EACN,aAAa;EACd;CACD,SAAS;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,aAAa;EACd;CACD,cAAc;EACZ,MAAM;EACN,SAAS;EACT,aAAa;EACd;CACF;AAED,MAAM,yBAAyB,CAAC,WAAW,QAAQ;AAInD,MAAM,0BAA+D;CACnE,SAAS;CACT,OAAO;CACR;AAED,SAAS,sBACP,cACqC;CACrC,OAAO,uBAAuB,SAAS,aAAoC;;AAG7E,eAAe,yBACb,aAC8B;CAC9B,MAAM,qBAAqB,CAAC,GAAG,uBAAuB;CAEtD,IAAI,gBAAgB,KAAA,GAAW;EAC7B,MAAM,eAAe,YAAY,MAAM;EACvC,IAAI,CAAC,sBAAsB,aAAa,EACtC,MAAM,IAAI,MACR,qBAAqB,YAAY,sBAAsB,mBAAmB,KAAK,KAAK,GACrF;EAEH,OAAO;;CAGT,IAAI,mBAAmB,WAAW,GAChC,OAAO,mBAAmB;CAG5B,MAAM,WAAW,MAAM,QAAQ,OAAO,oCAAoC;EACxE,MAAM;EACN,SAAS,mBAAmB,KAAK,kBAAkB;GACjD,OAAO,GAAG,wBAAwB,cAAc,IAAI,aAAa;GACjE,OAAO;GACR,EAAE;EACJ,CAAC;CAEF,IAAI,CAAC,YAAY,CAAC,sBAAsB,SAAS,EAC/C,MAAM,IAAI,MAAM,uBAAuB;CAGzC,OAAO;;AAGT,eAAe,iBAAgC;CAmB7C,MAAM,wBAAwB,MAlBJ,WAAW;EACnC,OAAO,MAAM;GACX,QAAQ,KAAK,qDAAqD;GAClE,QAAQ,IAAI,KAAK,IAAI;GACrB,IAAI,KAAK,cACP,QAAQ,KAAK,KAAK,aAAa;;EAGnC,SAAS,SAAS;GAChB,OAAO,QAAQ,OAAO,SAAS,EAC7B,MAAM,QACP,CAAC;;EAEJ,WAAW,SAAS;GAClB,QAAQ,MAAM,QAAQ;;EAEzB,CAAC,EAEyC,EAAE,gBAAgB,MAAM,CAAC;CACpE,QAAQ,QACN,oCAAoC,MAAM,YAAY,8BAA8B,MAAM,wBAC3F;;AAGH,eAAe,kBAAkB,UAA8C;CAC7E,IAAI,aAAa,WAAW;EAC1B,MAAM,iBAAiB,EAAE,OAAO,MAAM,CAAC;EACvC,QAAQ,QAAQ,2BAA2B,MAAM,kBAAkB;EACnE;;CAGF,MAAM,gBAAgB;;AAGxB,eAAsB,aAAa,SAAwC;CACzE,IAAI,QAAQ,SAAS;EACnB,QAAQ,QAAQ;EAChB,QAAQ,KAAK,0BAA0B;;CAGzC,MAAM,YAAY,QAAQ;CAE1B,MAAM,aAAa;CACnB,MAAM,WAAW,MAAM,yBAAyB,QAAQ,SAAS;CAEjE,QAAQ,KAAK,mBAAmB,wBAAwB,YAAY;CACpE,MAAM,kBAAkB,SAAS;;AAkBnC,MAAa,OAAO,cAAc;CAChC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;CACN,aAAa,EACX,OAtBc,cAAc;EAC9B,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM;EACN,IAAI,EAAE,QAAQ;GACZ,OAAO,aAAa;IAClB,UAAU,KAAK;IACf,SAAS,KAAK;IACd,WAAW,KAAK;IACjB,CAAC;;EAEL,CASU,EACR;CACD,IAAI,EAAE,QAAQ;EACZ,KAAK,KAAK,EAAE,MAAM,IAAI,MAAM,EAC1B;EAGF,OAAO,aAAa;GAClB,UAAU,KAAK;GACf,SAAS,KAAK;GACd,WAAW,KAAK;GACjB,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"auth-BO_SkMVw.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { loginCodex } from \"./lib/oauth/codex\"\nimport { PATHS, ensurePaths } from \"./lib/paths\"\nimport { state } from \"./lib/state\"\nimport { persistCodexCredentials, setupGitHubToken } from \"./lib/token\"\n\ninterface RunAuthOptions {\n provider?: string\n verbose: boolean\n showToken: boolean\n}\n\nconst authArgs = {\n provider: {\n type: \"string\",\n description: \"Provider to log in with (copilot or codex)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show provider access token on auth\",\n },\n} as const\n\nconst BUILTIN_PROVIDER_NAMES = [\"copilot\", \"codex\"] as const\n\ntype BuiltinProviderName = (typeof BUILTIN_PROVIDER_NAMES)[number]\n\nconst BUILTIN_PROVIDER_LABELS: Record<BuiltinProviderName, string> = {\n copilot: \"GitHub Copilot\",\n codex: \"OpenAI Codex\",\n}\n\nfunction isBuiltinProviderName(\n providerName: string,\n): providerName is BuiltinProviderName {\n return BUILTIN_PROVIDER_NAMES.includes(providerName as BuiltinProviderName)\n}\n\nasync function resolveProviderSelection(\n providerArg: string | undefined,\n): Promise<BuiltinProviderName> {\n const availableProviders = [...BUILTIN_PROVIDER_NAMES]\n\n if (providerArg !== undefined) {\n const providerName = providerArg.trim()\n if (!isBuiltinProviderName(providerName)) {\n throw new Error(\n `Unknown provider '${providerArg}'. Expected one of: ${availableProviders.join(\", \")}`,\n )\n }\n return providerName\n }\n\n if (availableProviders.length === 1) {\n return availableProviders[0]\n }\n\n const provider = await consola.prompt(\"Select a provider to log in with\", {\n type: \"select\",\n options: availableProviders.map((providerName) => ({\n label: `${BUILTIN_PROVIDER_LABELS[providerName]} (${providerName})`,\n value: providerName,\n })),\n })\n\n if (!provider || !isBuiltinProviderName(provider)) {\n throw new Error(\"No provider selected\")\n }\n\n return provider\n}\n\nasync function loginWithCodex(): Promise<void> {\n const credentials = await loginCodex({\n onAuth(info) {\n consola.info(\"Open the following URL to authenticate with Codex:\")\n consola.log(info.url)\n if (info.instructions) {\n consola.info(info.instructions)\n }\n },\n onPrompt(message) {\n return consola.prompt(message, {\n type: \"text\",\n })\n },\n onProgress(message) {\n consola.debug(message)\n },\n })\n\n await persistCodexCredentials(credentials, { enableProvider: true })\n consola.success(\n `Codex provider config written to ${PATHS.CONFIG_PATH} and credentials written to ${PATHS.CODEX_CREDENTIAL_PATH}`,\n )\n}\n\nasync function loginWithProvider(provider: BuiltinProviderName): Promise<void> {\n if (provider === \"copilot\") {\n await setupGitHubToken({ force: true })\n consola.success(\"GitHub token written to\", PATHS.GITHUB_TOKEN_PATH)\n return\n }\n\n await loginWithCodex()\n}\n\nexport async function runAuthLogin(options: RunAuthOptions): Promise<void> {\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = options.showToken\n\n await ensurePaths()\n const provider = await resolveProviderSelection(options.provider)\n\n consola.info(`Logging in with ${BUILTIN_PROVIDER_LABELS[provider]}`)\n await loginWithProvider(provider)\n}\n\nconst authLogin = defineCommand({\n meta: {\n name: \"login\",\n description: \"Authenticate a builtin provider without running the server\",\n },\n args: authArgs,\n run({ args }) {\n return runAuthLogin({\n provider: args.provider,\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n\nexport const auth = defineCommand({\n meta: {\n name: \"auth\",\n description: \"Run authentication flows without running the server\",\n },\n args: authArgs,\n subCommands: {\n login: authLogin,\n },\n run({ args }) {\n if ((args._[0] ?? \"\").trim()) {\n return\n }\n\n return runAuthLogin({\n provider: args.provider,\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n"],"mappings":";;;;;AAgBA,MAAM,WAAW;CACf,UAAU;EACR,MAAM;EACN,aAAa;EACd;CACD,SAAS;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,aAAa;EACd;CACD,cAAc;EACZ,MAAM;EACN,SAAS;EACT,aAAa;EACd;CACF;AAED,MAAM,yBAAyB,CAAC,WAAW,QAAQ;AAInD,MAAM,0BAA+D;CACnE,SAAS;CACT,OAAO;CACR;AAED,SAAS,sBACP,cACqC;CACrC,OAAO,uBAAuB,SAAS,aAAoC;;AAG7E,eAAe,yBACb,aAC8B;CAC9B,MAAM,qBAAqB,CAAC,GAAG,uBAAuB;CAEtD,IAAI,gBAAgB,KAAA,GAAW;EAC7B,MAAM,eAAe,YAAY,MAAM;EACvC,IAAI,CAAC,sBAAsB,aAAa,EACtC,MAAM,IAAI,MACR,qBAAqB,YAAY,sBAAsB,mBAAmB,KAAK,KAAK,GACrF;EAEH,OAAO;;CAGT,IAAI,mBAAmB,WAAW,GAChC,OAAO,mBAAmB;CAG5B,MAAM,WAAW,MAAM,QAAQ,OAAO,oCAAoC;EACxE,MAAM;EACN,SAAS,mBAAmB,KAAK,kBAAkB;GACjD,OAAO,GAAG,wBAAwB,cAAc,IAAI,aAAa;GACjE,OAAO;GACR,EAAE;EACJ,CAAC;CAEF,IAAI,CAAC,YAAY,CAAC,sBAAsB,SAAS,EAC/C,MAAM,IAAI,MAAM,uBAAuB;CAGzC,OAAO;;AAGT,eAAe,iBAAgC;CAmB7C,MAAM,wBAAwB,MAlBJ,WAAW;EACnC,OAAO,MAAM;GACX,QAAQ,KAAK,qDAAqD;GAClE,QAAQ,IAAI,KAAK,IAAI;GACrB,IAAI,KAAK,cACP,QAAQ,KAAK,KAAK,aAAa;;EAGnC,SAAS,SAAS;GAChB,OAAO,QAAQ,OAAO,SAAS,EAC7B,MAAM,QACP,CAAC;;EAEJ,WAAW,SAAS;GAClB,QAAQ,MAAM,QAAQ;;EAEzB,CAAC,EAEyC,EAAE,gBAAgB,MAAM,CAAC;CACpE,QAAQ,QACN,oCAAoC,MAAM,YAAY,8BAA8B,MAAM,wBAC3F;;AAGH,eAAe,kBAAkB,UAA8C;CAC7E,IAAI,aAAa,WAAW;EAC1B,MAAM,iBAAiB,EAAE,OAAO,MAAM,CAAC;EACvC,QAAQ,QAAQ,2BAA2B,MAAM,kBAAkB;EACnE;;CAGF,MAAM,gBAAgB;;AAGxB,eAAsB,aAAa,SAAwC;CACzE,IAAI,QAAQ,SAAS;EACnB,QAAQ,QAAQ;EAChB,QAAQ,KAAK,0BAA0B;;CAGzC,MAAM,YAAY,QAAQ;CAE1B,MAAM,aAAa;CACnB,MAAM,WAAW,MAAM,yBAAyB,QAAQ,SAAS;CAEjE,QAAQ,KAAK,mBAAmB,wBAAwB,YAAY;CACpE,MAAM,kBAAkB,SAAS;;AAkBnC,MAAa,OAAO,cAAc;CAChC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;CACN,aAAa,EACX,OAtBc,cAAc;EAC9B,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM;EACN,IAAI,EAAE,QAAQ;GACZ,OAAO,aAAa;IAClB,UAAU,KAAK;IACf,SAAS,KAAK;IACd,WAAW,KAAK;IACjB,CAAC;;EAEL,CASU,EACR;CACD,IAAI,EAAE,QAAQ;EACZ,KAAK,KAAK,EAAE,MAAM,IAAI,MAAM,EAC1B;EAGF,OAAO,aAAa;GAClB,UAAU,KAAK;GACf,SAAS,KAAK;GACd,WAAW,KAAK;GACjB,CAAC;;CAEL,CAAC"}
@@ -1,4 +1,4 @@
1
- import { _ as getCopilotUsage, a as setupGitHubToken } from "./token-1SfgxCRm.js";
1
+ import { _ as getCopilotUsage, a as setupGitHubToken } from "./token-BVXHiYEl.js";
2
2
  import { b as ensurePaths } from "./config-ztdkLu9o.js";
3
3
  import { defineCommand } from "citty";
4
4
  import consola from "consola";
@@ -39,4 +39,4 @@ const checkUsage = defineCommand({
39
39
  //#endregion
40
40
  export { checkUsage };
41
41
 
42
- //# sourceMappingURL=check-usage-CTShKCvR.js.map
42
+ //# sourceMappingURL=check-usage-D-W6VD7k.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"check-usage-CTShKCvR.js","names":[],"sources":["../src/check-usage.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n"],"mappings":";;;;;AAUA,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,MAAM;EACV,MAAM,aAAa;EACnB,MAAM,kBAAkB;EACxB,IAAI;GACF,MAAM,QAAQ,MAAM,iBAAiB;GACrC,MAAM,UAAU,MAAM,gBAAgB;GACtC,MAAM,eAAe,QAAQ;GAC7B,MAAM,cAAc,eAAe,QAAQ;GAC3C,MAAM,qBACJ,eAAe,IAAK,cAAc,eAAgB,MAAM;GAC1D,MAAM,0BAA0B,QAAQ;GAGxC,SAAS,eAAe,MAAc,MAA+B;IACnE,IAAI,CAAC,MAAM,OAAO,GAAG,KAAK;IAC1B,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,QAAQ,KAAK;IAC1B,MAAM,cAAc,QAAQ,IAAK,OAAO,QAAS,MAAM;IACvD,MAAM,mBAAmB,KAAK;IAC9B,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,MAAM,SAAS,YAAY,QAAQ,EAAE,CAAC,UAAU,iBAAiB,QAAQ,EAAE,CAAC;;GAGzG,MAAM,cAAc,YAAY,YAAY,GAAG,aAAa,SAAS,mBAAmB,QAAQ,EAAE,CAAC,UAAU,wBAAwB,QAAQ,EAAE,CAAC;GAChJ,MAAM,WAAW,eAAe,QAAQ,MAAM,gBAAgB,KAAK;GACnE,MAAM,kBAAkB,eACtB,eACA,MAAM,gBAAgB,YACvB;GAED,QAAQ,IACN,wBAAwB,MAAM,aAAa,mBACtB,MAAM,iBAAiB,iBAEnC,YAAY,MACZ,SAAS,MACT,kBACV;WACM,KAAK;GACZ,QAAQ,MAAM,kCAAkC,IAAI;GACpD,QAAQ,KAAK,EAAE;;;CAGpB,CAAC"}
1
+ {"version":3,"file":"check-usage-D-W6VD7k.js","names":[],"sources":["../src/check-usage.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n"],"mappings":";;;;;AAUA,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,MAAM;EACV,MAAM,aAAa;EACnB,MAAM,kBAAkB;EACxB,IAAI;GACF,MAAM,QAAQ,MAAM,iBAAiB;GACrC,MAAM,UAAU,MAAM,gBAAgB;GACtC,MAAM,eAAe,QAAQ;GAC7B,MAAM,cAAc,eAAe,QAAQ;GAC3C,MAAM,qBACJ,eAAe,IAAK,cAAc,eAAgB,MAAM;GAC1D,MAAM,0BAA0B,QAAQ;GAGxC,SAAS,eAAe,MAAc,MAA+B;IACnE,IAAI,CAAC,MAAM,OAAO,GAAG,KAAK;IAC1B,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,QAAQ,KAAK;IAC1B,MAAM,cAAc,QAAQ,IAAK,OAAO,QAAS,MAAM;IACvD,MAAM,mBAAmB,KAAK;IAC9B,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,MAAM,SAAS,YAAY,QAAQ,EAAE,CAAC,UAAU,iBAAiB,QAAQ,EAAE,CAAC;;GAGzG,MAAM,cAAc,YAAY,YAAY,GAAG,aAAa,SAAS,mBAAmB,QAAQ,EAAE,CAAC,UAAU,wBAAwB,QAAQ,EAAE,CAAC;GAChJ,MAAM,WAAW,eAAe,QAAQ,MAAM,gBAAgB,KAAK;GACnE,MAAM,kBAAkB,eACtB,eACA,MAAM,gBAAgB,YACvB;GAED,QAAQ,IACN,wBAAwB,MAAM,aAAa,mBACtB,MAAM,iBAAiB,iBAEnC,YAAY,MACZ,SAAS,MACT,kBACV;WACM,KAAK;GACZ,QAAQ,MAAM,kCAAkC,IAAI;GACpD,QAAQ,KAAK,EAAE;;;CAGpB,CAAC"}
package/dist/main.js CHANGED
@@ -39,11 +39,11 @@ if (typeof args["api-home"] === "string") process.env.COPILOT_API_HOME = args["a
39
39
  if (typeof args["oauth-app"] === "string") process.env.COPILOT_API_OAUTH_APP = args["oauth-app"];
40
40
  if (typeof args["enterprise-url"] === "string") process.env.COPILOT_API_ENTERPRISE_URL = args["enterprise-url"];
41
41
  bindElectronFetch();
42
- const { auth } = await import("./auth-CeZtnQE_.js");
43
- const { checkUsage } = await import("./check-usage-CTShKCvR.js");
42
+ const { auth } = await import("./auth-BO_SkMVw.js");
43
+ const { checkUsage } = await import("./check-usage-D-W6VD7k.js");
44
44
  const { debug } = await import("./debug-BVHmoCzY.js");
45
45
  const { mcp } = await import("./mcp-DZgcvqQY.js");
46
- const { start } = await import("./start-BhPxHgu8.js");
46
+ const { start } = await import("./start-CM-b3DRX.js");
47
47
  await runMain(defineCommand({
48
48
  meta: {
49
49
  name: "copilot-api",