@jeffreycao/copilot-api 2.2.0 → 2.2.2

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.
Files changed (35) hide show
  1. package/README.md +177 -206
  2. package/README.zh-CN.md +196 -221
  3. package/dist/{auth-HR8NHHhJ.js → auth-Bb1k-ECx.js} +4 -4
  4. package/dist/{auth-HR8NHHhJ.js.map → auth-Bb1k-ECx.js.map} +1 -1
  5. package/dist/auth-Rglf5pV9.js +2 -0
  6. package/dist/{config-CHzejzwY.js → config-D0EIG26T.js} +48 -7
  7. package/dist/config-D0EIG26T.js.map +1 -0
  8. package/dist/{debug-ChC85ySp.js → debug-DdGqmvyk.js} +2 -2
  9. package/dist/{debug-ChC85ySp.js.map → debug-DdGqmvyk.js.map} +1 -1
  10. package/dist/{electron-fetch-DPhDE6JE.js → electron-fetch-BRX-ug5E.js} +1 -1
  11. package/dist/{electron-fetch-DPhDE6JE.js.map → electron-fetch-BRX-ug5E.js.map} +1 -1
  12. package/dist/fast-path-BoMnZCVC.js +11 -0
  13. package/dist/fast-path-BoMnZCVC.js.map +1 -0
  14. package/dist/main.js +45 -39
  15. package/dist/main.js.map +1 -1
  16. package/dist/mcp-fpSlKZxK.js +16 -0
  17. package/dist/mcp-fpSlKZxK.js.map +1 -0
  18. package/dist/{mcp-BG6fpi6q.js → mcp-server-BeNu_Edl.js} +3 -13
  19. package/dist/mcp-server-BeNu_Edl.js.map +1 -0
  20. package/dist/mcp-server-DQ4r-fAy.js +2 -0
  21. package/dist/{models-CJuY4JZ5.js → models-CgxSOPld.js} +2 -2
  22. package/dist/{models-CJuY4JZ5.js.map → models-CgxSOPld.js.map} +1 -1
  23. package/dist/{server-DN95QRWA.js → server-ooE0nEgj.js} +77 -40
  24. package/dist/server-ooE0nEgj.js.map +1 -0
  25. package/dist/{start-BL3AiN3r.js → start-CoFNjnwH.js} +8 -8
  26. package/dist/{start-BL3AiN3r.js.map → start-CoFNjnwH.js.map} +1 -1
  27. package/dist/{tls-BmWaOfKV.js → tls-Aq1Dd8E2.js} +1 -1
  28. package/dist/{tls-BmWaOfKV.js.map → tls-Aq1Dd8E2.js.map} +1 -1
  29. package/dist/{token-Ctj6sfG4.js → token-Drm4iN8n.js} +3 -3
  30. package/dist/{token-Ctj6sfG4.js.map → token-Drm4iN8n.js.map} +1 -1
  31. package/package.json +4 -3
  32. package/dist/auth-PIo0IPSc.js +0 -2
  33. package/dist/config-CHzejzwY.js.map +0 -1
  34. package/dist/mcp-BG6fpi6q.js.map +0 -1
  35. package/dist/server-DN95QRWA.js.map +0 -1
package/README.zh-CN.md CHANGED
@@ -1,4 +1,14 @@
1
- # Copilot API Proxy
1
+ # Copilot API
2
+
3
+ <p align="center">
4
+ <img src="./docs/hero/copilot-api-hero.svg" alt="Copilot API - Universal AI Gateway" width="1600" />
5
+ </p>
6
+
7
+ <p align="center">
8
+ <strong>Universal AI Gateway</strong><br />
9
+ One Gateway. Any Client. Multiple AI Providers.<br />
10
+ Chat Completions &middot; OpenAI Responses &middot; Anthropic Messages
11
+ </p>
2
12
 
3
13
  <p align="center">
4
14
  <a href="https://www.npmjs.com/package/@jeffreycao/copilot-api"><img src="https://img.shields.io/npm/v/@jeffreycao/copilot-api.svg" alt="npm version"></a>
@@ -8,84 +18,9 @@
8
18
  <a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node-%3E%3D22.13.0-green.svg" alt="Node >= 22.13.0"></a>
9
19
  </p>
10
20
 
11
- [English](./README.md) | 简体中文
12
-
13
- ## 目录
14
-
15
- - [Copilot API Proxy](#copilot-api-proxy)
16
- - [目录](#目录)
17
- - [重要说明](#重要说明)
18
- - [项目概览](#项目概览)
19
- - [快速开始](#快速开始)
20
- - [功能特性](#功能特性)
21
- - [前置要求](#前置要求)
22
- - [安装](#安装)
23
- - [从源码运行](#从源码运行)
24
- - [开发模式](#开发模式)
25
- - [生产模式](#生产模式)
26
- - [通过 npx 使用](#通过-npx-使用)
27
- - [配合 Docker 使用](#配合-docker-使用)
28
- - [Electron 桌面应用](#electron-桌面应用)
29
- - [桌面应用截图](#桌面应用截图)
30
- - [与 Claude Code 一起使用](#与-claude-code-一起使用)
31
- - [通过 `--claude-code` 标志进行交互式配置](#通过---claude-code-标志进行交互式配置)
32
- - [通过 `settings.json` 手动配置](#通过-settingsjson-手动配置)
33
- - [与 OpenCode 一起使用](#与-opencode-一起使用)
34
- - [最小配置](#最小配置)
35
- - [与 Codex 一起使用](#与-codex-一起使用)
36
- - [Codex `config.toml` 参考配置](#codex-configtoml-参考配置)
37
- - [GPT Tool Search](#gpt-tool-search)
38
- - [插件集成](#插件集成)
39
- - [Claude Code 插件集成(基于 marketplace)](#claude-code-插件集成基于-marketplace)
40
- - [Opencode 插件](#opencode-插件)
41
- - [使用量查看器](#使用量查看器)
42
- - [Usage Viewer 截图](#usage-viewer-截图)
43
- - [命令结构](#命令结构)
44
- - [命令行选项](#命令行选项)
45
- - [全局选项](#全局选项)
46
- - [Start 命令选项](#start-命令选项)
47
- - [Auth 命令选项](#auth-命令选项)
48
- - [Debug 命令选项](#debug-命令选项)
49
- - [配置(config.json)](#配置configjson)
50
- - [API 认证](#api-认证)
51
- - [API 端点](#api-端点)
52
- - [OpenAI 兼容端点](#openai-兼容端点)
53
- - [Codex 后端代理端点](#codex-后端代理端点)
54
- - [Anthropic 兼容端点](#anthropic-兼容端点)
55
- - [使用量监控端点](#使用量监控端点)
56
- - [Admin / 配置端点](#admin--配置端点)
57
- - [使用示例](#使用示例)
58
- - [使用建议](#使用建议)
59
- - [CLAUDE.md 或 AGENTS.md 推荐内容](#claudemd-或-agentsmd-推荐内容)
60
-
61
- <a id="important-notes"></a>
62
-
63
- ## 重要说明
64
-
65
- > [!IMPORTANT]
66
- > **使用前请先注意以下几点:**
67
- >
68
- > 1. **Codex 配置:** 与 Codex 搭配使用时,请在 `~/.codex/config.toml` 中添加 gateway provider,详见 [Codex `config.toml` 参考配置](#codex-configtoml-参考配置)。
69
- >
70
- > 2. **Claude Code 配置:** 与 Claude Code 搭配使用时,请将模型 ID 配置为 `claude-opus-4-8[1m]`。示例 claude `settings.json` 见 [通过 `settings.json` 手动配置](#manual-configuration-with-settingsjson)。
71
- >
72
- > 3. **OpenCode 配置:** 与 OpenCode 搭配使用时,请使用 `@ai-sdk/anthropic` 配置 `~/.config/opencode/opencode.json`,详见 [与 OpenCode 一起使用](#与-opencode-一起使用)。
73
- >
74
- > 4. **内置 `copilot`、`codex` 与第三方 provider:** 执行 `npx @jeffreycao/copilot-api@latest auth`,可选择 `copilot`、`codex`、`deepseek`、`custom` 等 provider。
75
- >
76
- > 5. **注意事项:** README 顶部移除的 GitHub Copilot warning 见 [GitHub Copilot 安全提示](./NOTICE.md#github-copilot-security-notice)。
77
-
78
- ---
79
-
80
- <a id="project-overview"></a>
81
-
82
- ## 项目概览
83
-
84
- 这是一个小型 AI gateway,可以使用 GitHub Copilot、内置 `codex` provider,也可以使用 DashScope 等已配置的第三方 provider。GitHub Copilot 现在是可选能力:如果本地没有 GitHub token,只要至少配置了一个启用中的 provider,服务仍可按 provider-only 模式启动。
85
-
86
- AI gateway 会从同一个本地端点暴露 OpenAI / Anthropic 兼容 API,让 [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview)、OpenCode、Codex 和 OpenAI 兼容客户端可以共用同一个本地服务。
87
-
88
- 在 GitHub Copilot 路径上,AI gateway 会在可用时优先使用 Copilot 原生的 Anthropic 风格 Messages API,在重工具调用场景下保留更原生的 Claude 行为。
21
+ <p align="center">
22
+ <a href="./README.md">English</a> | 简体中文
23
+ </p>
89
24
 
90
25
  <a id="quick-start"></a>
91
26
 
@@ -114,148 +49,47 @@ curl http://localhost:4141/v1/models
114
49
 
115
50
  接下来可按你的客户端选择指南:[与 Claude Code 一起使用](#using-with-claude-code)、[与 OpenCode 一起使用](#using-with-opencode)、[与 Codex 一起使用](#using-with-codex),或通过 [Docker](#using-with-docker) 运行。
116
51
 
117
- <a id="features"></a>
52
+ <a id="highlights"></a>
118
53
 
119
- ## 功能特性
54
+ ## 功能亮点
120
55
 
121
- - **OpenAI Anthropic 双兼容**:通过 `/v1/responses`、`/v1/chat/completions`、`/v1/models`、`/v1/embeddings` `/v1/messages` 对外暴露同一个本地 AI gateway。
122
- - **Copilot 可选**:有 GitHub 凭据时可以使用 GitHub Copilot,没有 GitHub 凭据时也可以只依赖已配置的 provider 运行。
123
- - **同一网关接入 Copilot、`codex` 与第三方 provider**:可统一路由 GitHub Copilot、内置 `codex` provider 和配置好的外部 provider。
124
- - **第三方 provider 可独立启动**:配置 DashScope、DeepSeek、OpenRouter 或自定义 provider 后,不需要 GitHub Copilot 登录即可启动 AI gateway
125
- - **OpenAI 兼容 provider 同时支持 chat 和 Messages API**:`openai-compatible` provider 可通过顶层 `/v1/chat/completions` 搭配 `model: "provider/model"` 提供 Chat Completions,也可通过 `/v1/messages` 完成 Anthropic Messages 的请求/响应翻译。
126
- - **面向 Claude 的更原生 Copilot 路由**:优先使用原生 `/v1/messages`,保留 Claude 风格工具流,支持 Anthropic beta 能力、通过 Responses-capable 模型支持 Claude WebSearch,并保留 subagent / session 标记。
127
- - **Claude Code 与 OpenCode 集成**:兼容 Claude Code 与 OpenCode,也支持通过 `@ai-sdk/anthropic` 直接作为 Anthropic provider 使用。
128
- - **灵活的认证与部署选项**:支持交互式登录、直接 token、个人 / Business / Enterprise、GitHub Enterprise、opencode OAuth 和自定义数据目录。
129
- - **多 provider 路由**:可暴露 `/:provider/...` 路由,也可在顶层 API 上使用 `model: "provider/model"`。
56
+ - **统一 API 网关**:在同一个本地端点上提供 OpenAI 兼容的 Chat Completions(`/v1/chat/completions`)、OpenAI Responses API(`/v1/responses`)和 Anthropic 兼容的 Messages(`/v1/messages`)。
57
+ - **多 Provider 接入**:在同一个网关后面统一路由 GitHub Copilot、内置 `codex` provider 和第三方 provider(Kimi、DeepSeek、DashScope、OpenRouter、OpenCode Go 或自定义 provider)。GitHub Copilot 是可选能力——只要至少有一个启用中的 provider,无需 GitHub token 也能按 provider-only 模式启动。
58
+ - **为 Coding Agent 而生**:为 Claude Code、OpenCode 和 Codex 提供完整的配置指南,包括交互式 `--claude-code` 启动器和面向 Codex 的合并模型目录。
59
+ - **Streaming WebSocket**:三种面向客户端的协议都支持 SSE 流式输出。上游 Copilot Responses 流量会根据每个模型声明的端点选择 WebSocket 或 HTTP;内置 `codex` provider 的流式 Responses 请求默认走 WebSocket,关闭 `useResponsesApiWebSocket` 后改走 HTTP
60
+ - **桌面应用**:Electron 图形界面,支持 GitHub Copilot 登录、Codex OAuth、provider 配置、token 用量、日志查看和一键启动 / 停止。
130
61
 
131
- <a id="prerequisites"></a>
62
+ <a id="compatibility"></a>
132
63
 
133
- ## 前置要求
64
+ ## 兼容性
134
65
 
135
- - Bun(>= 1.2.x)
136
- - 如果要通过 `npx` 运行已发布 CLI,需要 Node.js
137
- - 只有在使用 GitHub Copilot provider 时,才需要已订阅 Copilot 的 GitHub 账号
138
- - 如果不使用 GitHub Copilot,需要至少一个已配置 provider 的 API key 或 OAuth 登录
66
+ 所有客户端都访问同一个本地端点。网关会把每个请求路由到 GitHub Copilot、内置 `codex` provider 或已配置的第三方 provider,并在 provider 使用不同协议时进行协议翻译。
139
67
 
140
- <a id="installation"></a>
68
+ **客户端 / 协议矩阵**
141
69
 
142
- ## 安装
70
+ | 客户端 | Chat Completions | Responses | Anthropic Messages | 推荐 |
71
+ |---|:---:|:---:|:---:|---|
72
+ | Claude Code | — | — | ✅ 原生 / 适配 | Anthropic Messages |
73
+ | OpenCode | ✅ 原生 | ✅ 原生 / 适配 | ✅ 原生 / 适配(通过 `@ai-sdk/anthropic`) | Anthropic Messages |
74
+ | Codex | — | ✅ 原生 / 适配 | — | Responses |
75
+ | OpenAI 兼容客户端 | ✅ 原生 | ✅ 原生 / 适配 | — | Chat Completions |
76
+ | Anthropic 兼容客户端 | — | — | ✅ 原生 / 适配 | Anthropic Messages |
143
77
 
144
- 安装依赖:
78
+ **Provider 与协议。** 协议能力按模型决定。Chat Completions 必须使用原生端点,Responses 和 Messages 则可在存在受支持路径时进行适配。内置 `codex` provider 原生使用 Responses;第三方 provider 可选择 `anthropic`、`openai-compatible` 或 `openai-responses`,也可按模型覆盖。
145
79
 
146
- ```sh
147
- bun install
148
- ```
149
-
150
- <a id="running-from-source"></a>
151
-
152
- ## 从源码运行
153
-
154
- 本项目可以通过多种方式从源码运行:
155
-
156
- ### 开发模式
157
-
158
- ```sh
159
- bun run dev start
160
- ```
161
-
162
- ### 生产模式
163
-
164
- ```sh
165
- bun run start start
166
- ```
167
-
168
- > 结尾的 `start` 是传给 `src/main.ts` 的 CLI 子命令,不是笔误:`bun run dev start` 是 watch 模式,`bun run start start` 是生产模式。
169
-
170
- <a id="using-with-npx"></a>
171
-
172
- ## 通过 npx 使用
173
-
174
- 你可以直接用 npx 运行本项目:
175
-
176
- > [!IMPORTANT]
177
- > 通过 `npx` 运行时,token usage 存储会使用 Node 内置的 `node:sqlite` 模块。该能力会在 Node.js >= 22.13.0 时启用;Node.js < 22.13.0 时 CLI 仍可启动,但会禁用 token usage 存储。
178
- >
179
- > 如果不升级 Node.js 但仍需要 token usage 存储,可以改用 Bun 运行已发布 CLI:`bunx --bun @jeffreycao/copilot-api@latest start`。
180
-
181
- ```sh
182
- npx @jeffreycao/copilot-api@latest start
183
- ```
184
-
185
- 带参数示例:
186
-
187
- ```sh
188
- npx @jeffreycao/copilot-api@latest start --port 8080
189
- ```
190
-
191
- 如果只想做认证或 provider 配置:
192
-
193
- ```sh
194
- npx @jeffreycao/copilot-api@latest auth
195
- ```
196
-
197
- 如果要不依赖 GitHub Copilot 运行,先配置至少一个 provider,然后正常启动服务:
198
-
199
- ```sh
200
- npx @jeffreycao/copilot-api@latest auth login --provider dashscope
201
- npx @jeffreycao/copilot-api@latest start
202
- ```
203
-
204
- <a id="using-with-docker"></a>
205
-
206
- ## 配合 Docker 使用
207
-
208
- 构建镜像:
209
-
210
- ```sh
211
- docker build -t copilot-api .
212
- ```
213
-
214
- 通过 bind mount 运行容器,让认证数据在重启后保留:
215
-
216
- ```sh
217
- mkdir -p ./copilot-data
218
- docker run -p 4141:4141 -v $(pwd)/copilot-data:/root/.local/share/copilot-api copilot-api
219
- ```
220
-
221
- 这会把宿主机上的 `./copilot-data` 映射到容器内的 `/root/.local/share/copilot-api`,用于持久化 GitHub 认证数据、provider 配置和其他 gateway 状态。
222
-
223
- 也可以直接通过环境变量传入 GitHub token:
224
-
225
- ```sh
226
- docker run -p 4141:4141 -e GH_TOKEN=your_github_token_here copilot-api
227
- ```
228
-
229
- <a id="electron-desktop-app"></a>
230
-
231
- ## Electron 桌面应用
232
-
233
- 如果你更喜欢图形界面,仓库里还提供了位于 `desktop/` 的 Electron 桌面应用。它支持 GitHub Copilot 登录、OpenAI Codex OAuth,以及 Kimi、DeepSeek、DashScope、OpenRouter 或自定义 provider 的 API Key 配置。授权或配置 provider 后,可以一键启动或停止本地代理,并在界面里直接查看本地端点、鉴权 Header、可用模型、额度和日志。
234
-
235
- 设置页还可以配置 `OAuth App`、`API Home`、`Enterprise URL`、详细日志以及最小化到托盘。Windows x64(`.exe`)、macOS Apple Silicon(`.dmg`)和 Linux x64(`.AppImage`)安装包发布在 GitHub Releases:
236
-
237
- https://github.com/caozhiyuan/copilot-api/releases
238
-
239
- Linux 用户需要先为下载的 AppImage 添加执行权限:
240
-
241
- ```sh
242
- chmod +x Copilot-API-*-linux-x86_64.AppImage
243
- ./Copilot-API-*-linux-x86_64.AppImage
244
- ```
245
-
246
- 下载对应平台的安装包后,在应用内授权或配置 provider,选择端口并启动服务,再把你的客户端指向应用里显示的本地端点即可。发布版桌面应用使用随包内置的 Electron 运行时,正常使用不需要额外安装 Node.js;token usage 历史记录会在该内置运行时支持 SQLite 时启用。
247
-
248
- 桌面应用里的高级配置页会通过 `GET/POST /admin/config/model-mappings` 读写这份共享的模型映射。同一份映射会统一作用于 `POST /v1/messages`、`POST /v1/messages/count_tokens`、`POST /v1/responses` 和 `POST /v1/chat/completions`,不再按接口区分。它使用的是 `auth.adminApiKey`,不是普通的 `auth.apiKeys`;应用会在服务启动并自动生成该 key 后,直接从 `config.json` 读取它来发起请求。
80
+ <a id="desktop-app"></a>
249
81
 
250
- ### 桌面应用截图
82
+ ## 桌面应用
251
83
 
252
- 下面展示了桌面应用中的首页、Token 用量统计页面:
84
+ 更喜欢图形界面?`desktop/` 目录下的 Electron 桌面应用支持 GitHub Copilot 登录、OpenAI Codex OAuth,以及 Kimi、DeepSeek、DashScope、OpenRouter 或自定义 provider 的 API Key 配置——可以一键启动 / 停止本地服务,并在一个窗口里查看本地端点、鉴权 Header、可用模型、用量和日志。
253
85
 
254
86
  <p align="center">
255
87
  <img src="./docs/screenshots/desktop-dashboard.png" alt="Copilot API 桌面应用首页" width="49%" />
256
88
  <img src="./docs/screenshots/desktop-token-usage.png" alt="Copilot API 桌面应用 Token 用量页" width="49%" />
257
89
  </p>
258
90
 
91
+ Windows x64(`.exe`)、macOS Apple Silicon(`.dmg`)和 Linux x64(`.AppImage`)安装包发布在 [GitHub Releases](https://github.com/caozhiyuan/copilot-api/releases)。完整配置与高级设置见 [Electron 桌面应用](#electron-desktop-app)。
92
+
259
93
  <a id="using-with-claude-code"></a>
260
94
 
261
95
  ## 与 Claude Code 一起使用
@@ -468,6 +302,154 @@ Responses Lite 的工具定义从 `input` 中的 `additional_tools` 读取,而
468
302
 
469
303
  该映射只作用于顶层 GitHub Copilot 路由。provider-scoped 路由不会使用 `modelMappings`,因此内置 `/codex` provider 仍会原生处理 `codex-auto-review`。
470
304
 
305
+ ---
306
+
307
+ <a id="project-overview"></a>
308
+
309
+ ## 项目概览
310
+
311
+ 这是一个小型 AI gateway,可以使用 GitHub Copilot、内置 `codex` provider,也可以使用 DashScope 等已配置的第三方 provider。GitHub Copilot 现在是可选能力:如果本地没有 GitHub token,只要至少配置了一个启用中的 provider,服务仍可按 provider-only 模式启动。
312
+
313
+ AI gateway 会从同一个本地端点暴露 OpenAI / Anthropic 兼容 API,让 [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview)、OpenCode、Codex 和 OpenAI 兼容客户端可以共用同一个本地服务。
314
+
315
+ 在 GitHub Copilot 路径上,AI gateway 会在可用时优先使用 Copilot 原生的 Anthropic 风格 Messages API,在重工具调用场景下保留更原生的 Claude 行为。
316
+
317
+ <a id="important-notes"></a>
318
+
319
+ ## 重要说明
320
+
321
+ > [!IMPORTANT]
322
+ > **使用前请先注意以下几点:**
323
+ >
324
+ > 1. **Codex 配置:** 与 Codex 搭配使用时,请在 `~/.codex/config.toml` 中添加 gateway provider,详见 [Codex `config.toml` 参考配置](#codex-configtoml-参考配置)。
325
+ >
326
+ > 2. **Claude Code 配置:** 与 Claude Code 搭配使用时,请将模型 ID 配置为 `claude-opus-4-8[1m]`。示例 claude `settings.json` 见 [通过 `settings.json` 手动配置](#manual-configuration-with-settingsjson)。
327
+ >
328
+ > 3. **OpenCode 配置:** 与 OpenCode 搭配使用时,请使用 `@ai-sdk/anthropic` 配置 `~/.config/opencode/opencode.json`,详见 [与 OpenCode 一起使用](#与-opencode-一起使用)。
329
+ >
330
+ > 4. **内置 `copilot`、`codex` 与第三方 provider:** 执行 `npx @jeffreycao/copilot-api@latest auth`,可选择 `copilot`、`codex`、`deepseek`、`custom` 等 provider。
331
+ >
332
+ > 5. **注意事项:** README 顶部移除的 GitHub Copilot warning 见 [GitHub Copilot 安全提示](./NOTICE.md#github-copilot-security-notice)。
333
+
334
+ <a id="prerequisites"></a>
335
+
336
+ ## 前置要求
337
+
338
+ - Bun(>= 1.2.x)
339
+ - 如果要通过 `npx` 运行已发布 CLI,需要 Node.js
340
+ - 只有在使用 GitHub Copilot provider 时,才需要已订阅 Copilot 的 GitHub 账号
341
+ - 如果不使用 GitHub Copilot,需要至少一个已配置 provider 的 API key 或 OAuth 登录
342
+
343
+ <a id="installation"></a>
344
+
345
+ ## 安装
346
+
347
+ 安装依赖:
348
+
349
+ ```sh
350
+ bun install
351
+ ```
352
+
353
+ <a id="running-from-source"></a>
354
+
355
+ ## 从源码运行
356
+
357
+ 本项目可以通过多种方式从源码运行:
358
+
359
+ ### 开发模式
360
+
361
+ ```sh
362
+ bun run dev start
363
+ ```
364
+
365
+ ### 生产模式
366
+
367
+ ```sh
368
+ bun run start start
369
+ ```
370
+
371
+ > 结尾的 `start` 是传给 `src/main.ts` 的 CLI 子命令,不是笔误:`bun run dev start` 是 watch 模式,`bun run start start` 是生产模式。
372
+
373
+ <a id="using-with-npx"></a>
374
+
375
+ ## 通过 npx 使用
376
+
377
+ 你可以直接用 npx 运行本项目:
378
+
379
+ > [!IMPORTANT]
380
+ > 通过 `npx` 运行时,token usage 存储会使用 Node 内置的 `node:sqlite` 模块。该能力会在 Node.js >= 22.13.0 时启用;Node.js < 22.13.0 时 CLI 仍可启动,但会禁用 token usage 存储。
381
+ >
382
+ > 如果不升级 Node.js 但仍需要 token usage 存储,可以改用 Bun 运行已发布 CLI:`bunx --bun @jeffreycao/copilot-api@latest start`。
383
+
384
+ ```sh
385
+ npx @jeffreycao/copilot-api@latest start
386
+ ```
387
+
388
+ 带参数示例:
389
+
390
+ ```sh
391
+ npx @jeffreycao/copilot-api@latest start --port 8080
392
+ ```
393
+
394
+ 如果只想做认证或 provider 配置:
395
+
396
+ ```sh
397
+ npx @jeffreycao/copilot-api@latest auth
398
+ ```
399
+
400
+ 如果要不依赖 GitHub Copilot 运行,先配置至少一个 provider,然后正常启动服务:
401
+
402
+ ```sh
403
+ npx @jeffreycao/copilot-api@latest auth login --provider dashscope
404
+ npx @jeffreycao/copilot-api@latest start
405
+ ```
406
+
407
+ <a id="using-with-docker"></a>
408
+
409
+ ## 配合 Docker 使用
410
+
411
+ 构建镜像:
412
+
413
+ ```sh
414
+ docker build -t copilot-api .
415
+ ```
416
+
417
+ 通过 bind mount 运行容器,让认证数据在重启后保留:
418
+
419
+ ```sh
420
+ mkdir -p ./copilot-data
421
+ docker run -p 4141:4141 -v $(pwd)/copilot-data:/root/.local/share/copilot-api copilot-api
422
+ ```
423
+
424
+ 这会把宿主机上的 `./copilot-data` 映射到容器内的 `/root/.local/share/copilot-api`,用于持久化 GitHub 认证数据、provider 配置和其他 gateway 状态。
425
+
426
+ 也可以直接通过环境变量传入 GitHub token:
427
+
428
+ ```sh
429
+ docker run -p 4141:4141 -e GH_TOKEN=your_github_token_here copilot-api
430
+ ```
431
+
432
+ <a id="electron-desktop-app"></a>
433
+
434
+ ## Electron 桌面应用
435
+
436
+ 如果你更喜欢图形界面,仓库里还提供了位于 `desktop/` 的 Electron 桌面应用。它支持 GitHub Copilot 登录、OpenAI Codex OAuth,以及 Kimi、DeepSeek、DashScope、OpenRouter 或自定义 provider 的 API Key 配置。授权或配置 provider 后,可以一键启动或停止本地代理,并在界面里直接查看本地端点、鉴权 Header、可用模型、额度和日志。
437
+
438
+ 设置页还可以配置 `OAuth App`、`API Home`、`Enterprise URL`、详细日志以及最小化到托盘。Windows x64(`.exe`)、macOS Apple Silicon(`.dmg`)和 Linux x64(`.AppImage`)安装包发布在 GitHub Releases:
439
+
440
+ https://github.com/caozhiyuan/copilot-api/releases
441
+
442
+ Linux 用户需要先为下载的 AppImage 添加执行权限:
443
+
444
+ ```sh
445
+ chmod +x Copilot-API-*-linux-x86_64.AppImage
446
+ ./Copilot-API-*-linux-x86_64.AppImage
447
+ ```
448
+
449
+ 下载对应平台的安装包后,在应用内授权或配置 provider,选择端口并启动服务,再把你的客户端指向应用里显示的本地端点即可。发布版桌面应用使用随包内置的 Electron 运行时,正常使用不需要额外安装 Node.js;token usage 历史记录会在该内置运行时支持 SQLite 时启用。
450
+
451
+ 桌面应用里的高级配置页会通过 `GET/POST /admin/config/model-mappings` 读写这份共享的模型映射。同一份映射会统一作用于 `POST /v1/messages`、`POST /v1/messages/count_tokens`、`POST /v1/responses` 和 `POST /v1/chat/completions`,不再按接口区分。它使用的是 `auth.adminApiKey`,不是普通的 `auth.apiKeys`;应用会在服务启动并自动生成该 key 后,直接从 `config.json` 读取它来发起请求。
452
+
471
453
  <a id="gpt-tool-search"></a>
472
454
 
473
455
  ## GPT Tool Search
@@ -746,8 +728,8 @@ Copilot API 现在使用子命令结构,主要命令包括:
746
728
  - **优先级:** 请求中的 `output_config.effort` > `modelReasoningEfforts[model]` > 内置默认值(GPT-5.3+ 模型为 `xhigh`,其他模型为 `high`)。
747
729
  - **转发字段:** 走 Copilot 原生 Messages API 时,最终值写入 `output_config.effort`;转换为 Responses API 时,最终值写入 `reasoning.effort`。
748
730
  - **配置可选值:** `none`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`。
749
- - **useMessagesApi:** 当为 `true` 时,支持 Copilot 原生 `/v1/messages` Claude 系模型会走 Messages API;否则回退到 `/chat/completions`。设为 `false` 可禁用 Messages API 路由,始终使用 `/chat/completions`。默认值为 `true`。
750
- - **useResponsesApiWebSocket:** 当为 `true` 时,Responses API 请求会优先对声明了 `ws:/responses` 的模型使用 Copilot websocket transport;仅声明 `/responses` 的模型仍走 HTTP。设为 `false` 可禁用 websocket 路由,并在模型支持 `/responses` 时使用 HTTP `/responses`。默认值为 `true`。如果遇到 Responses API WebSocket closed,一般是自己的网络问题。如果使用了 VPN,建议切换节点。
731
+ - **useMessagesApi:** 当为 `true` 时,声明了 Copilot 原生 `/v1/messages` 端点的模型会使用 Messages API。如果所选模型未声明 Messages 端点或关闭了该配置,网关会在模型声明了 Responses 端点时使用 Responses,否则在模型支持时回退到 Chat Completions。设为 `false` 可跳过原生 Messages 路由。默认值为 `true`。
732
+ - **useResponsesApiWebSocket:** 当为 `true` 时,Copilot Responses 请求会对声明了 `ws:/responses` 的模型使用 WebSocket;仅声明 `/responses` 的模型使用 HTTP。内置 `codex` provider 的流式 Responses 请求只要启用了该配置就会使用 WebSocket,非流式 Codex 请求始终使用 HTTP。设为 `false` 后,Copilot 会在所选模型声明了 `/responses` 时使用 HTTP,Codex 的流式 Responses 请求也会改走 HTTP。WebSocket 失败后不会自动通过 HTTP 重试。默认值为 `true`。如果代理、VPN 或网络会阻断或干扰 WebSocket 流量,请关闭该配置或切换网络。
751
733
  - **responsesTransport:** 所有上游 Responses transport 共用的生命周期与缓冲区正整数限制。无效值、零或负数会回退到上面列出的默认值。`headersTimeoutMs` 从连接建立开始计算,到收到 HTTP 响应头为止,并不是整个生成过程的总时限。每收到一个 HTTP body chunk 或 WebSocket message 都会重置 `streamInactivityTimeoutMs`,因此持续活跃的长推理任务不会被短总时限中断。`websocketOpenTimeoutMs` 限制 WebSocket 握手时间;`websocketPoolIdleTimeoutMs` 只控制已正常完成且可复用的空闲连接。WebSocket 队列同时受字节数和消息数上限约束;超过任一上限时会终止该 stream 并使 socket 失效,而不会丢弃或重排事件。
752
734
  - **useResponsesApiWebSearch:** 当为 `true` 时,服务端会保留 Responses API 中 `type: "web_search"` 的工具并透传到上游。设为 `false` 则会从 `/responses` payload 中移除这些工具。默认值为 `true`。
753
735
  - **alphaSearchCodexPriority:** 默认值为 `true`。顶层 alpha-search 请求优先使用 Codex alpha-search 端点,因为它不会消耗 provider 配额。若 Codex 不可用,或该配置设为 `false`,使用非 `codex/model` 的 `provider/model` 别名的请求会调用目标 provider 的 `/v1/responses` 端点,没有 provider 前缀的请求使用 GitHub Copilot Responses web search。该适配器会识别当前所有 Codex search command;不受支持的 `image_query` 和 `screenshot` 会返回成功且明确要求不要重试的 tool output。
@@ -789,7 +771,7 @@ curl http://localhost:4141/admin/config/model-mappings \
789
771
 
790
772
  ## API 端点
791
773
 
792
- 服务端提供多个 OpenAI / Anthropic 兼容端点。请求会根据所选模型和 `provider/model` 别名路由到 GitHub Copilot、内置 `codex` provider 或已配置的 provider
774
+ 服务端提供多个 OpenAI / Anthropic 兼容端点。请求会根据所选模型和 `provider/model` 别名路由到 GitHub Copilot、内置 `codex` provider 或已配置的 provider。下列每个 `/v1/...` 端点也都支持 `/:provider/v1/...` 形式的 provider 级路径,表格中不再重复列出。
793
775
 
794
776
  ### OpenAI 兼容端点
795
777
 
@@ -802,33 +784,26 @@ curl http://localhost:4141/admin/config/model-mappings \
802
784
  | `GET /v1/models` | `GET` | 列出 Copilot 模型以及已启用 provider 的 `provider/model-id` 模型。来自 Codex 客户端(`User-Agent` 以 `codex` 开头)的请求会转发到 Codex Models 上游。 |
803
785
  | `POST /v1/embeddings` | `POST` | 创建表示输入文本的向量嵌入。 |
804
786
 
805
- ### Codex 后端代理端点
787
+ ### Codex 后端端点
806
788
 
807
- 这些端点要求已有可用的 Codex 登录态。每个端点同时提供无版本前缀和 `/v1` 两种路径。
789
+ 这些端点实现 Codex 后端 API。顶层图片请求要求已有可用的 Codex 登录态;alpha-search 则可以使用 Codex 后端或 Responses web-search 适配器。
808
790
 
809
791
  | 端点 | 方法 | 说明 |
810
792
  | ---------------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------- |
811
- | `POST /alpha/search`<br>`POST /v1/alpha/search` | `POST` | 将 JSON 请求体和查询参数透明转发到 Codex Alpha Search 上游。 |
812
- | `POST /images/generations`<br>`POST /v1/images/generations` | `POST` | 将 JSON 图片生成请求转发到 Codex Images 上游。请求未携带 `Content-Type` 时,网关默认补充 `application/json`。 |
813
- | `POST /images/edits`<br>`POST /v1/images/edits` | `POST` | 将图片编辑请求转发到 Codex Images 上游。请使用 `multipart/form-data`,并让 HTTP 客户端自动生成 `boundary`;网关会保留传入的 content type,并以流式方式转发上传请求体。 |
793
+ | `POST /v1/alpha/search` | `POST` | 将 Codex alpha-search 请求路由到 Codex 后端,或在本地及通过 Responses web search 处理支持的命令。 |
794
+ | `POST /v1/images/generations` | `POST` | 将 JSON 图片生成请求转发到 Codex Images 上游。请求未携带 `Content-Type` 时,网关默认补充 `application/json`。 |
795
+ | `POST /v1/images/edits` | `POST` | 将图片编辑请求转发到 Codex Images 上游。请使用 `multipart/form-data`,并让 HTTP 客户端自动生成 `boundary`;网关会保留传入的 content type,并以流式方式转发上传请求体。 |
814
796
 
815
- 对于以上所有端点,网关都会使用当前 Codex 登录态覆盖客户端的 authorization 和 account header,保留查询参数及兼容的请求头,并返回上游状态码、响应头和响应体。
797
+ 对于路由到 Codex 后端的请求,网关会使用当前 Codex 登录态覆盖客户端的 authorization 和 account header,并保留兼容的请求元数据。基于 Responses 的 alpha-search 则遵循所选 Copilot 或 provider 的路由。
816
798
 
817
799
  ### Anthropic 兼容端点
818
800
 
819
- 这些端点设计为兼容 Anthropic Messages API。provider 级的 models、Responses、alpha-search 和 images 路由同时支持无版本前缀与 `/v1` 两种路径;Messages 路由仍使用 `/v1`。
801
+ 这些端点设计为兼容 Anthropic Messages API。
820
802
 
821
803
  | 端点 | 方法 | 说明 |
822
804
  | ------------------------------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------- |
823
805
  | `POST /v1/messages` | `POST` | 为给定对话创建模型响应。支持已配置 provider 的 `provider/model` 别名,包括通过 `openai-compatible` provider 做翻译。 |
824
806
  | `POST /v1/messages/count_tokens` | `POST` | 计算一组消息的 token 数。支持已配置 provider 的 `provider/model` 别名。 |
825
- | `POST /:provider/v1/messages` | `POST` | 将 Anthropic Messages 请求代理到已配置的 Anthropic provider,或翻译到 OpenAI 兼容 / OpenAI Responses provider。 |
826
- | `GET /:provider/models`<br>`GET /:provider/v1/models` | `GET` | 将模型列表请求代理到已配置的 provider。对 `codex` 默认返回内置模型目录;Codex 客户端(`User-Agent` 以 `codex` 开头)会转发到 Codex Models 上游。 |
827
- | `POST /:provider/v1/messages/count_tokens` | `POST` | 为 provider 路由请求在本地计算 token 数。 |
828
- | `POST /:provider/responses`<br>`POST /:provider/v1/responses` | `POST` | 将 OpenAI Responses 请求代理到已配置的 `openai-responses` provider(含 `codex`)。 |
829
- | `POST /:provider/alpha/search`<br>`POST /:provider/v1/alpha/search` | `POST` | 代理 alpha-search 请求。对 `codex` 转发到 Codex Alpha Search 上游;其他 provider 转发到 `{baseUrl}/v1/alpha/search`。 |
830
- | `POST /:provider/images/generations`<br>`POST /:provider/v1/images/generations` | `POST` | 代理图片生成。对 `codex` 使用 Codex Images 上游;其他 provider 转发到 `{baseUrl}/v1/images/generations`(15 分钟超时)。 |
831
- | `POST /:provider/images/edits`<br>`POST /:provider/v1/images/edits` | `POST` | 代理图片编辑。对 `codex` 使用 Codex Images 上游;其他 provider 以 multipart/流式方式转发到 `{baseUrl}/v1/images/edits`(15 分钟超时)。 |
832
807
 
833
808
  ### 使用量监控端点
834
809
 
@@ -1,5 +1,5 @@
1
- import { M as PATHS, N as ensurePaths, a as normalizeProviderBaseUrl, c as setProviderConfig, n as getRawProviderConfig, r as isSupportedProviderType, y as SUPPORTED_PROVIDER_TYPES } from "./config-CHzejzwY.js";
2
- import { H as state, O as loginCodex, a as setupGitHubToken, n as persistCodexCredentials } from "./token-Ctj6sfG4.js";
1
+ import { M as PATHS, N as ensurePaths, a as normalizeProviderBaseUrl, c as setProviderConfig, n as getRawProviderConfig, r as isSupportedProviderType, y as SUPPORTED_PROVIDER_TYPES } from "./config-D0EIG26T.js";
2
+ import { H as state, O as loginCodex, a as setupGitHubToken, n as persistCodexCredentials } from "./token-Drm4iN8n.js";
3
3
  import { defineCommand } from "citty";
4
4
  import consola from "consola";
5
5
  //#region src/lib/quick-providers.ts
@@ -311,7 +311,7 @@ async function runProviderSetup() {
311
311
  await loginWithProvider(provider);
312
312
  }
313
313
  async function runAuthLogin(options) {
314
- (await import("./tls-BmWaOfKV.js")).enableSystemCACompat();
314
+ (await import("./tls-Aq1Dd8E2.js")).enableSystemCACompat();
315
315
  if (options.verbose) {
316
316
  consola.level = 5;
317
317
  consola.info("Verbose logging enabled");
@@ -354,4 +354,4 @@ const auth = defineCommand({
354
354
  //#endregion
355
355
  export { runAuthLogin as n, runProviderSetup as r, auth as t };
356
356
 
357
- //# sourceMappingURL=auth-HR8NHHhJ.js.map
357
+ //# sourceMappingURL=auth-Bb1k-ECx.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-HR8NHHhJ.js","names":[],"sources":["../src/lib/quick-providers.ts","../src/auth.ts"],"sourcesContent":["import type { ProviderType } from \"./config\"\n\ninterface QuickProviderConfig {\n type: ProviderType\n baseUrl: string\n pricingCurrency: string\n editableType: boolean\n}\n\nexport const QUICK_PROVIDER_CONFIGS = {\n \"opencode-go\": {\n type: \"openai-compatible\",\n baseUrl: \"https://opencode.ai/zen/go\",\n pricingCurrency: \"USD\",\n editableType: false,\n },\n kimi: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.kimi.com/coding\",\n pricingCurrency: \"USD\",\n editableType: false,\n },\n deepseek: {\n type: \"anthropic\",\n baseUrl: \"https://api.deepseek.com/anthropic\",\n pricingCurrency: \"CNY\",\n editableType: true,\n },\n dashscope: {\n type: \"openai-compatible\",\n baseUrl: \"https://dashscope.aliyuncs.com/compatible-mode\",\n pricingCurrency: \"CNY\",\n editableType: true,\n },\n openrouter: {\n type: \"anthropic\",\n baseUrl: \"https://openrouter.ai/api\",\n pricingCurrency: \"USD\",\n editableType: false,\n },\n} satisfies Record<string, QuickProviderConfig>\n\nexport type QuickProviderName = keyof typeof QUICK_PROVIDER_CONFIGS\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport {\n getRawProviderConfig,\n isSupportedProviderType,\n normalizeProviderBaseUrl,\n setProviderConfig,\n SUPPORTED_PROVIDER_TYPES,\n type ProviderAuthType,\n type ProviderConfig,\n type ProviderType,\n} from \"./lib/config\"\nimport { loginCodex } from \"./lib/oauth/codex\"\nimport { PATHS, ensurePaths } from \"./lib/paths\"\nimport {\n QUICK_PROVIDER_CONFIGS,\n type QuickProviderName,\n} from \"./lib/quick-providers\"\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:\n \"Provider to log in with or configure (copilot, codex, opencode-go, kimi, deepseek, dashscope, openrouter, custom)\",\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\nconst QUICK_PROVIDER_NAMES = Object.keys(\n QUICK_PROVIDER_CONFIGS,\n) as Array<QuickProviderName>\nconst AUTH_PROVIDER_NAMES = [\n ...BUILTIN_PROVIDER_NAMES,\n ...QUICK_PROVIDER_NAMES,\n \"custom\",\n] as const\nconst CUSTOM_PROVIDER_AUTH_TYPE_OPTION = \"__default__\"\nconst QUICK_PROVIDER_DEFAULT_TYPE_OPTION = \"__default__\"\nconst CUSTOM_PROVIDER_AUTH_TYPES = [\"x-api-key\", \"authorization\"] as const\n\ntype BuiltinProviderName = (typeof BUILTIN_PROVIDER_NAMES)[number]\ntype AuthProviderName = (typeof AUTH_PROVIDER_NAMES)[number]\ntype CustomProviderAuthType = (typeof CUSTOM_PROVIDER_AUTH_TYPES)[number]\n\nconst BUILTIN_PROVIDER_LABELS: Record<BuiltinProviderName, string> = {\n copilot: \"GitHub Copilot\",\n codex: \"OpenAI Codex\",\n}\nconst AUTH_PROVIDER_LABELS: Record<AuthProviderName, string> = {\n ...BUILTIN_PROVIDER_LABELS,\n \"opencode-go\": \"OpenCode Go\",\n kimi: \"Kimi\",\n deepseek: \"DeepSeek\",\n dashscope: \"DashScope\",\n openrouter: \"OpenRouter\",\n custom: \"Custom provider\",\n}\n\nfunction isAuthProviderName(\n providerName: string,\n): providerName is AuthProviderName {\n return AUTH_PROVIDER_NAMES.includes(providerName as AuthProviderName)\n}\n\nfunction isCustomProviderAuthType(\n value: string,\n): value is CustomProviderAuthType {\n return CUSTOM_PROVIDER_AUTH_TYPES.includes(value as CustomProviderAuthType)\n}\n\nfunction isQuickProviderName(\n providerName: AuthProviderName,\n): providerName is QuickProviderName {\n return QUICK_PROVIDER_NAMES.includes(providerName as QuickProviderName)\n}\n\nasync function resolveProviderSelection(\n providerArg: string | undefined,\n): Promise<AuthProviderName> {\n const availableProviders = [...AUTH_PROVIDER_NAMES]\n\n if (providerArg !== undefined) {\n const providerName = providerArg.trim()\n if (!isAuthProviderName(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: `${AUTH_PROVIDER_LABELS[providerName]} (${providerName})`,\n value: providerName,\n })),\n })\n\n if (!provider || !isAuthProviderName(provider)) {\n throw new Error(\"No provider selected\")\n }\n\n return provider\n}\n\nfunction assertCustomProviderName(providerName: string): void {\n if (!providerName) {\n throw new Error(\"Provider name must be a non-empty string\")\n }\n\n if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(providerName)) {\n throw new Error(\n \"Provider name must start with a letter or number and contain only letters, numbers, underscores, or hyphens\",\n )\n }\n\n if (providerName === \"copilot\" || providerName === \"codex\") {\n throw new Error(\n `Provider name '${providerName}' is reserved for a builtin provider`,\n )\n }\n}\n\nasync function promptRequiredText(\n message: string,\n fieldName: string,\n): Promise<string> {\n const value = await consola.prompt(message, { type: \"text\" })\n const normalizedValue = typeof value === \"string\" ? value.trim() : \"\"\n if (!normalizedValue) {\n throw new Error(`${fieldName} must be a non-empty string`)\n }\n return normalizedValue\n}\n\nfunction canUseMaskedPrompt(): boolean {\n return Boolean(\n process.stdin.isTTY\n && process.stdout.isTTY\n && typeof process.stdin.setRawMode === \"function\",\n )\n}\n\nasync function promptMaskedText(message: string): Promise<string> {\n if (!canUseMaskedPrompt()) {\n const value = await consola.prompt(message, { type: \"text\" })\n return typeof value === \"string\" ? value : \"\"\n }\n\n return await new Promise<string>((resolve, reject) => {\n let value = \"\"\n const rawModeWasEnabled = process.stdin.isRaw === true\n\n function cleanup(): void {\n process.stdin.off(\"data\", onData)\n process.stdin.setRawMode(rawModeWasEnabled)\n process.stdin.pause()\n }\n\n function finish(): void {\n cleanup()\n process.stdout.write(\"\\n\")\n resolve(value)\n }\n\n function cancel(): void {\n cleanup()\n process.stdout.write(\"\\n\")\n reject(new Error(\"Prompt cancelled\"))\n }\n\n function onData(chunk: Buffer): void {\n const input = chunk.toString(\"utf8\")\n\n if (input.startsWith(\"\\u001B\")) {\n return\n }\n\n for (const char of input) {\n if (char === \"\\u0003\") {\n cancel()\n return\n }\n\n if (char === \"\\r\" || char === \"\\n\") {\n finish()\n return\n }\n\n if (char === \"\\b\" || char === \"\\u007F\") {\n if (value.length > 0) {\n value = value.slice(0, -1)\n process.stdout.write(\"\\b \\b\")\n }\n continue\n }\n\n if (char >= \" \") {\n value += char\n process.stdout.write(\"*\")\n }\n }\n }\n\n process.stdout.write(`${message}: `)\n process.stdin.setRawMode(true)\n process.stdin.resume()\n process.stdin.on(\"data\", onData)\n })\n}\n\nasync function promptRequiredSecret(\n message: string,\n fieldName: string,\n): Promise<string> {\n const value = await promptMaskedText(message)\n const normalizedValue = value.trim()\n if (!normalizedValue) {\n throw new Error(`${fieldName} must be a non-empty string`)\n }\n return normalizedValue\n}\n\nasync function promptCustomProviderName(): Promise<string> {\n const providerName = await promptRequiredText(\n \"Enter provider name\",\n \"Provider name\",\n )\n assertCustomProviderName(providerName)\n return providerName\n}\n\nasync function promptCustomProviderType(): Promise<ProviderType> {\n const providerType = await consola.prompt(\"Select provider type\", {\n type: \"select\",\n options: SUPPORTED_PROVIDER_TYPES.map((type) => ({\n label: type,\n value: type,\n })),\n })\n\n if (\n typeof providerType !== \"string\"\n || !isSupportedProviderType(providerType)\n ) {\n throw new Error(\"No provider type selected\")\n }\n\n return providerType\n}\n\nasync function promptQuickProviderType(\n defaultType: ProviderType,\n): Promise<ProviderType> {\n const providerType = await consola.prompt(\n `Select provider type (default: ${defaultType})`,\n {\n type: \"select\",\n options: [\n {\n label: `Default (${defaultType})`,\n value: QUICK_PROVIDER_DEFAULT_TYPE_OPTION,\n },\n ...SUPPORTED_PROVIDER_TYPES.map((type) => ({\n label: type,\n value: type,\n })),\n ],\n },\n )\n\n if (providerType === QUICK_PROVIDER_DEFAULT_TYPE_OPTION) {\n return defaultType\n }\n\n if (\n typeof providerType === \"string\"\n && isSupportedProviderType(providerType)\n ) {\n return providerType\n }\n\n throw new Error(\"No provider type selected\")\n}\n\nfunction getDefaultProviderAuthType(\n providerType: ProviderType,\n): ProviderAuthType {\n return providerType === \"anthropic\" ? \"x-api-key\" : \"authorization\"\n}\n\nasync function promptCustomProviderAuthType(\n providerType: ProviderType,\n): Promise<ProviderAuthType | undefined> {\n const defaultAuthType = getDefaultProviderAuthType(providerType)\n const authType = await consola.prompt(\"Select provider auth type\", {\n type: \"select\",\n options: [\n {\n label: `Default (${defaultAuthType})`,\n value: CUSTOM_PROVIDER_AUTH_TYPE_OPTION,\n },\n ...CUSTOM_PROVIDER_AUTH_TYPES.map((value) => ({\n label: value,\n value,\n })),\n ],\n })\n\n if (authType === CUSTOM_PROVIDER_AUTH_TYPE_OPTION) {\n return undefined\n }\n\n if (typeof authType === \"string\" && isCustomProviderAuthType(authType)) {\n return authType\n }\n\n throw new Error(\"No provider auth type selected\")\n}\n\nasync function promptQuickProviderBaseUrl(\n defaultBaseUrl: string,\n): Promise<string> {\n const value = await consola.prompt(\n `Enter provider baseUrl (default: ${defaultBaseUrl})`,\n {\n type: \"text\",\n default: defaultBaseUrl,\n initial: defaultBaseUrl,\n },\n )\n const baseUrl = normalizeProviderBaseUrl(\n typeof value === \"string\" && value.trim() ? value : defaultBaseUrl,\n )\n if (!baseUrl) {\n throw new Error(\"baseUrl must be a non-empty string\")\n }\n\n return baseUrl\n}\n\nfunction buildCustomProviderConfig(\n existingProviderConfig: ProviderConfig,\n options: {\n apiKey: string\n authType?: ProviderAuthType\n baseUrl: string\n pricingCurrency?: string\n type: ProviderType\n },\n): ProviderConfig {\n return {\n type: options.type,\n enabled: true,\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n ...(options.authType ? { authType: options.authType } : {}),\n pricingCurrency:\n options.pricingCurrency ?? existingProviderConfig.pricingCurrency,\n ...(existingProviderConfig.models ?\n { models: existingProviderConfig.models }\n : {}),\n }\n}\n\nasync function configureCustomProvider(): Promise<void> {\n const providerName = await promptCustomProviderName()\n const type = await promptCustomProviderType()\n const baseUrl = normalizeProviderBaseUrl(\n await promptRequiredText(\"Enter provider baseUrl\", \"baseUrl\"),\n )\n if (!baseUrl) {\n throw new Error(\"baseUrl must be a non-empty string\")\n }\n\n const apiKey = await promptRequiredSecret(\"Enter provider apiKey\", \"apiKey\")\n const authType = await promptCustomProviderAuthType(type)\n const existingProviderConfig = getRawProviderConfig(providerName) ?? {}\n\n setProviderConfig(\n providerName,\n buildCustomProviderConfig(existingProviderConfig, {\n apiKey,\n authType,\n baseUrl,\n type,\n }),\n )\n\n consola.success(\n `Custom provider '${providerName}' written to ${PATHS.CONFIG_PATH}`,\n )\n}\n\nasync function configureQuickProvider(\n providerName: QuickProviderName,\n): Promise<void> {\n const defaultProviderConfig = QUICK_PROVIDER_CONFIGS[providerName]\n const apiKey = await promptRequiredSecret(\n `Enter ${providerName} apiKey`,\n \"apiKey\",\n )\n const type =\n defaultProviderConfig.editableType ?\n await promptQuickProviderType(defaultProviderConfig.type)\n : defaultProviderConfig.type\n const baseUrl = await promptQuickProviderBaseUrl(\n defaultProviderConfig.baseUrl,\n )\n const existingProviderConfig = getRawProviderConfig(providerName) ?? {}\n\n setProviderConfig(\n providerName,\n buildCustomProviderConfig(existingProviderConfig, {\n apiKey,\n baseUrl,\n pricingCurrency: defaultProviderConfig.pricingCurrency,\n type,\n }),\n )\n\n consola.success(\n `${AUTH_PROVIDER_LABELS[providerName]} provider '${providerName}' written to ${PATHS.CONFIG_PATH}`,\n )\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: AuthProviderName): 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 if (provider === \"codex\") {\n await loginWithCodex()\n return\n }\n\n if (isQuickProviderName(provider)) {\n await configureQuickProvider(provider)\n return\n }\n\n await configureCustomProvider()\n}\n\nexport async function runProviderSetup(): Promise<void> {\n const provider = await resolveProviderSelection(undefined)\n consola.info(`Logging in with ${AUTH_PROVIDER_LABELS[provider]}`)\n await loginWithProvider(provider)\n}\n\nexport async function runAuthLogin(options: RunAuthOptions): Promise<void> {\n const tlsModule = await import(\"./lib/tls\")\n tlsModule.enableSystemCACompat()\n\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 ${AUTH_PROVIDER_LABELS[provider]}`)\n await loginWithProvider(provider)\n}\n\nconst authLogin = defineCommand({\n meta: {\n name: \"login\",\n description:\n \"Authenticate or configure a 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":"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"}
1
+ {"version":3,"file":"auth-Bb1k-ECx.js","names":[],"sources":["../src/lib/quick-providers.ts","../src/auth.ts"],"sourcesContent":["import type { ProviderType } from \"./config\"\n\ninterface QuickProviderConfig {\n type: ProviderType\n baseUrl: string\n pricingCurrency: string\n editableType: boolean\n}\n\nexport const QUICK_PROVIDER_CONFIGS = {\n \"opencode-go\": {\n type: \"openai-compatible\",\n baseUrl: \"https://opencode.ai/zen/go\",\n pricingCurrency: \"USD\",\n editableType: false,\n },\n kimi: {\n type: \"openai-compatible\",\n baseUrl: \"https://api.kimi.com/coding\",\n pricingCurrency: \"USD\",\n editableType: false,\n },\n deepseek: {\n type: \"anthropic\",\n baseUrl: \"https://api.deepseek.com/anthropic\",\n pricingCurrency: \"CNY\",\n editableType: true,\n },\n dashscope: {\n type: \"openai-compatible\",\n baseUrl: \"https://dashscope.aliyuncs.com/compatible-mode\",\n pricingCurrency: \"CNY\",\n editableType: true,\n },\n openrouter: {\n type: \"anthropic\",\n baseUrl: \"https://openrouter.ai/api\",\n pricingCurrency: \"USD\",\n editableType: false,\n },\n} satisfies Record<string, QuickProviderConfig>\n\nexport type QuickProviderName = keyof typeof QUICK_PROVIDER_CONFIGS\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport {\n getRawProviderConfig,\n isSupportedProviderType,\n normalizeProviderBaseUrl,\n setProviderConfig,\n SUPPORTED_PROVIDER_TYPES,\n type ProviderAuthType,\n type ProviderConfig,\n type ProviderType,\n} from \"./lib/config\"\nimport { loginCodex } from \"./lib/oauth/codex\"\nimport { PATHS, ensurePaths } from \"./lib/paths\"\nimport {\n QUICK_PROVIDER_CONFIGS,\n type QuickProviderName,\n} from \"./lib/quick-providers\"\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:\n \"Provider to log in with or configure (copilot, codex, opencode-go, kimi, deepseek, dashscope, openrouter, custom)\",\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\nconst QUICK_PROVIDER_NAMES = Object.keys(\n QUICK_PROVIDER_CONFIGS,\n) as Array<QuickProviderName>\nconst AUTH_PROVIDER_NAMES = [\n ...BUILTIN_PROVIDER_NAMES,\n ...QUICK_PROVIDER_NAMES,\n \"custom\",\n] as const\nconst CUSTOM_PROVIDER_AUTH_TYPE_OPTION = \"__default__\"\nconst QUICK_PROVIDER_DEFAULT_TYPE_OPTION = \"__default__\"\nconst CUSTOM_PROVIDER_AUTH_TYPES = [\"x-api-key\", \"authorization\"] as const\n\ntype BuiltinProviderName = (typeof BUILTIN_PROVIDER_NAMES)[number]\ntype AuthProviderName = (typeof AUTH_PROVIDER_NAMES)[number]\ntype CustomProviderAuthType = (typeof CUSTOM_PROVIDER_AUTH_TYPES)[number]\n\nconst BUILTIN_PROVIDER_LABELS: Record<BuiltinProviderName, string> = {\n copilot: \"GitHub Copilot\",\n codex: \"OpenAI Codex\",\n}\nconst AUTH_PROVIDER_LABELS: Record<AuthProviderName, string> = {\n ...BUILTIN_PROVIDER_LABELS,\n \"opencode-go\": \"OpenCode Go\",\n kimi: \"Kimi\",\n deepseek: \"DeepSeek\",\n dashscope: \"DashScope\",\n openrouter: \"OpenRouter\",\n custom: \"Custom provider\",\n}\n\nfunction isAuthProviderName(\n providerName: string,\n): providerName is AuthProviderName {\n return AUTH_PROVIDER_NAMES.includes(providerName as AuthProviderName)\n}\n\nfunction isCustomProviderAuthType(\n value: string,\n): value is CustomProviderAuthType {\n return CUSTOM_PROVIDER_AUTH_TYPES.includes(value as CustomProviderAuthType)\n}\n\nfunction isQuickProviderName(\n providerName: AuthProviderName,\n): providerName is QuickProviderName {\n return QUICK_PROVIDER_NAMES.includes(providerName as QuickProviderName)\n}\n\nasync function resolveProviderSelection(\n providerArg: string | undefined,\n): Promise<AuthProviderName> {\n const availableProviders = [...AUTH_PROVIDER_NAMES]\n\n if (providerArg !== undefined) {\n const providerName = providerArg.trim()\n if (!isAuthProviderName(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: `${AUTH_PROVIDER_LABELS[providerName]} (${providerName})`,\n value: providerName,\n })),\n })\n\n if (!provider || !isAuthProviderName(provider)) {\n throw new Error(\"No provider selected\")\n }\n\n return provider\n}\n\nfunction assertCustomProviderName(providerName: string): void {\n if (!providerName) {\n throw new Error(\"Provider name must be a non-empty string\")\n }\n\n if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/u.test(providerName)) {\n throw new Error(\n \"Provider name must start with a letter or number and contain only letters, numbers, underscores, or hyphens\",\n )\n }\n\n if (providerName === \"copilot\" || providerName === \"codex\") {\n throw new Error(\n `Provider name '${providerName}' is reserved for a builtin provider`,\n )\n }\n}\n\nasync function promptRequiredText(\n message: string,\n fieldName: string,\n): Promise<string> {\n const value = await consola.prompt(message, { type: \"text\" })\n const normalizedValue = typeof value === \"string\" ? value.trim() : \"\"\n if (!normalizedValue) {\n throw new Error(`${fieldName} must be a non-empty string`)\n }\n return normalizedValue\n}\n\nfunction canUseMaskedPrompt(): boolean {\n return Boolean(\n process.stdin.isTTY\n && process.stdout.isTTY\n && typeof process.stdin.setRawMode === \"function\",\n )\n}\n\nasync function promptMaskedText(message: string): Promise<string> {\n if (!canUseMaskedPrompt()) {\n const value = await consola.prompt(message, { type: \"text\" })\n return typeof value === \"string\" ? value : \"\"\n }\n\n return await new Promise<string>((resolve, reject) => {\n let value = \"\"\n const rawModeWasEnabled = process.stdin.isRaw === true\n\n function cleanup(): void {\n process.stdin.off(\"data\", onData)\n process.stdin.setRawMode(rawModeWasEnabled)\n process.stdin.pause()\n }\n\n function finish(): void {\n cleanup()\n process.stdout.write(\"\\n\")\n resolve(value)\n }\n\n function cancel(): void {\n cleanup()\n process.stdout.write(\"\\n\")\n reject(new Error(\"Prompt cancelled\"))\n }\n\n function onData(chunk: Buffer): void {\n const input = chunk.toString(\"utf8\")\n\n if (input.startsWith(\"\\u001B\")) {\n return\n }\n\n for (const char of input) {\n if (char === \"\\u0003\") {\n cancel()\n return\n }\n\n if (char === \"\\r\" || char === \"\\n\") {\n finish()\n return\n }\n\n if (char === \"\\b\" || char === \"\\u007F\") {\n if (value.length > 0) {\n value = value.slice(0, -1)\n process.stdout.write(\"\\b \\b\")\n }\n continue\n }\n\n if (char >= \" \") {\n value += char\n process.stdout.write(\"*\")\n }\n }\n }\n\n process.stdout.write(`${message}: `)\n process.stdin.setRawMode(true)\n process.stdin.resume()\n process.stdin.on(\"data\", onData)\n })\n}\n\nasync function promptRequiredSecret(\n message: string,\n fieldName: string,\n): Promise<string> {\n const value = await promptMaskedText(message)\n const normalizedValue = value.trim()\n if (!normalizedValue) {\n throw new Error(`${fieldName} must be a non-empty string`)\n }\n return normalizedValue\n}\n\nasync function promptCustomProviderName(): Promise<string> {\n const providerName = await promptRequiredText(\n \"Enter provider name\",\n \"Provider name\",\n )\n assertCustomProviderName(providerName)\n return providerName\n}\n\nasync function promptCustomProviderType(): Promise<ProviderType> {\n const providerType = await consola.prompt(\"Select provider type\", {\n type: \"select\",\n options: SUPPORTED_PROVIDER_TYPES.map((type) => ({\n label: type,\n value: type,\n })),\n })\n\n if (\n typeof providerType !== \"string\"\n || !isSupportedProviderType(providerType)\n ) {\n throw new Error(\"No provider type selected\")\n }\n\n return providerType\n}\n\nasync function promptQuickProviderType(\n defaultType: ProviderType,\n): Promise<ProviderType> {\n const providerType = await consola.prompt(\n `Select provider type (default: ${defaultType})`,\n {\n type: \"select\",\n options: [\n {\n label: `Default (${defaultType})`,\n value: QUICK_PROVIDER_DEFAULT_TYPE_OPTION,\n },\n ...SUPPORTED_PROVIDER_TYPES.map((type) => ({\n label: type,\n value: type,\n })),\n ],\n },\n )\n\n if (providerType === QUICK_PROVIDER_DEFAULT_TYPE_OPTION) {\n return defaultType\n }\n\n if (\n typeof providerType === \"string\"\n && isSupportedProviderType(providerType)\n ) {\n return providerType\n }\n\n throw new Error(\"No provider type selected\")\n}\n\nfunction getDefaultProviderAuthType(\n providerType: ProviderType,\n): ProviderAuthType {\n return providerType === \"anthropic\" ? \"x-api-key\" : \"authorization\"\n}\n\nasync function promptCustomProviderAuthType(\n providerType: ProviderType,\n): Promise<ProviderAuthType | undefined> {\n const defaultAuthType = getDefaultProviderAuthType(providerType)\n const authType = await consola.prompt(\"Select provider auth type\", {\n type: \"select\",\n options: [\n {\n label: `Default (${defaultAuthType})`,\n value: CUSTOM_PROVIDER_AUTH_TYPE_OPTION,\n },\n ...CUSTOM_PROVIDER_AUTH_TYPES.map((value) => ({\n label: value,\n value,\n })),\n ],\n })\n\n if (authType === CUSTOM_PROVIDER_AUTH_TYPE_OPTION) {\n return undefined\n }\n\n if (typeof authType === \"string\" && isCustomProviderAuthType(authType)) {\n return authType\n }\n\n throw new Error(\"No provider auth type selected\")\n}\n\nasync function promptQuickProviderBaseUrl(\n defaultBaseUrl: string,\n): Promise<string> {\n const value = await consola.prompt(\n `Enter provider baseUrl (default: ${defaultBaseUrl})`,\n {\n type: \"text\",\n default: defaultBaseUrl,\n initial: defaultBaseUrl,\n },\n )\n const baseUrl = normalizeProviderBaseUrl(\n typeof value === \"string\" && value.trim() ? value : defaultBaseUrl,\n )\n if (!baseUrl) {\n throw new Error(\"baseUrl must be a non-empty string\")\n }\n\n return baseUrl\n}\n\nfunction buildCustomProviderConfig(\n existingProviderConfig: ProviderConfig,\n options: {\n apiKey: string\n authType?: ProviderAuthType\n baseUrl: string\n pricingCurrency?: string\n type: ProviderType\n },\n): ProviderConfig {\n return {\n type: options.type,\n enabled: true,\n baseUrl: options.baseUrl,\n apiKey: options.apiKey,\n ...(options.authType ? { authType: options.authType } : {}),\n pricingCurrency:\n options.pricingCurrency ?? existingProviderConfig.pricingCurrency,\n ...(existingProviderConfig.models ?\n { models: existingProviderConfig.models }\n : {}),\n }\n}\n\nasync function configureCustomProvider(): Promise<void> {\n const providerName = await promptCustomProviderName()\n const type = await promptCustomProviderType()\n const baseUrl = normalizeProviderBaseUrl(\n await promptRequiredText(\"Enter provider baseUrl\", \"baseUrl\"),\n )\n if (!baseUrl) {\n throw new Error(\"baseUrl must be a non-empty string\")\n }\n\n const apiKey = await promptRequiredSecret(\"Enter provider apiKey\", \"apiKey\")\n const authType = await promptCustomProviderAuthType(type)\n const existingProviderConfig = getRawProviderConfig(providerName) ?? {}\n\n setProviderConfig(\n providerName,\n buildCustomProviderConfig(existingProviderConfig, {\n apiKey,\n authType,\n baseUrl,\n type,\n }),\n )\n\n consola.success(\n `Custom provider '${providerName}' written to ${PATHS.CONFIG_PATH}`,\n )\n}\n\nasync function configureQuickProvider(\n providerName: QuickProviderName,\n): Promise<void> {\n const defaultProviderConfig = QUICK_PROVIDER_CONFIGS[providerName]\n const apiKey = await promptRequiredSecret(\n `Enter ${providerName} apiKey`,\n \"apiKey\",\n )\n const type =\n defaultProviderConfig.editableType ?\n await promptQuickProviderType(defaultProviderConfig.type)\n : defaultProviderConfig.type\n const baseUrl = await promptQuickProviderBaseUrl(\n defaultProviderConfig.baseUrl,\n )\n const existingProviderConfig = getRawProviderConfig(providerName) ?? {}\n\n setProviderConfig(\n providerName,\n buildCustomProviderConfig(existingProviderConfig, {\n apiKey,\n baseUrl,\n pricingCurrency: defaultProviderConfig.pricingCurrency,\n type,\n }),\n )\n\n consola.success(\n `${AUTH_PROVIDER_LABELS[providerName]} provider '${providerName}' written to ${PATHS.CONFIG_PATH}`,\n )\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: AuthProviderName): 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 if (provider === \"codex\") {\n await loginWithCodex()\n return\n }\n\n if (isQuickProviderName(provider)) {\n await configureQuickProvider(provider)\n return\n }\n\n await configureCustomProvider()\n}\n\nexport async function runProviderSetup(): Promise<void> {\n const provider = await resolveProviderSelection(undefined)\n consola.info(`Logging in with ${AUTH_PROVIDER_LABELS[provider]}`)\n await loginWithProvider(provider)\n}\n\nexport async function runAuthLogin(options: RunAuthOptions): Promise<void> {\n const tlsModule = await import(\"./lib/tls\")\n tlsModule.enableSystemCACompat()\n\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 ${AUTH_PROVIDER_LABELS[provider]}`)\n await loginWithProvider(provider)\n}\n\nconst authLogin = defineCommand({\n meta: {\n name: \"login\",\n description:\n \"Authenticate or configure a 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":"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"}