@jeffreycao/copilot-api 1.10.23 → 1.10.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -20
- package/README.zh-CN.md +10 -20
- package/dist/{auth-Qy0uU2yI.js → auth-CnUtw9S3.js} +3 -3
- package/dist/{auth-Qy0uU2yI.js.map → auth-CnUtw9S3.js.map} +1 -1
- package/dist/{check-usage-DDfnCA0c.js → check-usage-LKHTA_Qx.js} +3 -3
- package/dist/{check-usage-DDfnCA0c.js.map → check-usage-LKHTA_Qx.js.map} +1 -1
- package/dist/{config-DtIF3Zvk.js → config-BVmeo70f.js} +5 -8
- package/dist/config-BVmeo70f.js.map +1 -0
- package/dist/{debug-Cc-hdADF.js → debug-9GrBfKqc.js} +2 -2
- package/dist/{debug-Cc-hdADF.js.map → debug-9GrBfKqc.js.map} +1 -1
- package/dist/main.js +4 -4
- package/dist/{server-G5PWGqkb.js → server-DoSH0Hby.js} +20 -12
- package/dist/server-DoSH0Hby.js.map +1 -0
- package/dist/{start-B9cpzVBW.js → start-BrG478zI.js} +4 -4
- package/dist/{start-B9cpzVBW.js.map → start-BrG478zI.js.map} +1 -1
- package/dist/{token-B-gS4RZc.js → token-DY6kM1jO.js} +2 -2
- package/dist/{token-B-gS4RZc.js.map → token-DY6kM1jO.js.map} +1 -1
- package/package.json +1 -1
- package/dist/config-DtIF3Zvk.js.map +0 -1
- package/dist/server-G5PWGqkb.js.map +0 -1
package/README.md
CHANGED
|
@@ -16,9 +16,7 @@ English | [简体中文](./README.zh-CN.md)
|
|
|
16
16
|
>
|
|
17
17
|
> 3. **Built-in `codex` provider:** Run `npx @jeffreycao/copilot-api@latest auth login --provider codex` once and the gateway will persist and refresh Codex OAuth credentials automatically.
|
|
18
18
|
>
|
|
19
|
-
> 4. **
|
|
20
|
-
>
|
|
21
|
-
> 5. **Note:** See [GitHub Copilot Security Notice](./NOTICE.md#github-copilot-security-notice) for the warning removed from the README header.
|
|
19
|
+
> 4. **Note:** See [GitHub Copilot Security Notice](./NOTICE.md#github-copilot-security-notice) for the warning removed from the README header.
|
|
22
20
|
|
|
23
21
|
---
|
|
24
22
|
|
|
@@ -94,7 +92,7 @@ https://github.com/caozhiyuan/copilot-api/releases
|
|
|
94
92
|
|
|
95
93
|
Download the installer for your platform, sign in inside the app, choose a port, start the server, then point your client at the local endpoint shown in the app. Packaged desktop builds use the bundled Electron runtime, so normal desktop usage does not require installing Node.js separately. Token usage history is enabled when that bundled runtime supports SQLite.
|
|
96
94
|
|
|
97
|
-
The desktop app's Advanced Config page reads and writes model mappings through `GET/POST /admin/config/model-mappings`. It uses `auth.adminApiKey` instead of the regular `auth.apiKeys`, and the app reads that key directly from `config.json` after the server has generated it on startup.
|
|
95
|
+
The desktop app's Advanced Config page reads and writes the shared model mappings through `GET/POST /admin/config/model-mappings`. The same mappings apply across `POST /v1/messages`, `POST /v1/messages/count_tokens`, `POST /v1/responses`, and `POST /v1/chat/completions` instead of being split per interface. It uses `auth.adminApiKey` instead of the regular `auth.apiKeys`, and the app reads that key directly from `config.json` after the server has generated it on startup.
|
|
98
96
|
|
|
99
97
|
### Desktop App Screenshots
|
|
100
98
|
|
|
@@ -195,14 +193,7 @@ The following command line options are available for the `start` command:
|
|
|
195
193
|
"enabled": true,
|
|
196
194
|
"baseUrl": "your-base-url",
|
|
197
195
|
"apiKey": "sk-your-provider-key",
|
|
198
|
-
"authType": "x-api-key"
|
|
199
|
-
"adjustInputTokens": false,
|
|
200
|
-
"models": {
|
|
201
|
-
"kimi-k2.5": {
|
|
202
|
-
"temperature": 1,
|
|
203
|
-
"topP": 0.95
|
|
204
|
-
}
|
|
205
|
-
}
|
|
196
|
+
"authType": "x-api-key"
|
|
206
197
|
},
|
|
207
198
|
"dashscope": {
|
|
208
199
|
"type": "openai-compatible",
|
|
@@ -216,8 +207,7 @@ The following command line options are available for the `start` command:
|
|
|
216
207
|
"topK": 20,
|
|
217
208
|
"extraBody": {
|
|
218
209
|
"preserve_thinking": true
|
|
219
|
-
}
|
|
220
|
-
"contextCache": true
|
|
210
|
+
}
|
|
221
211
|
},
|
|
222
212
|
"glm-5.1": {
|
|
223
213
|
"temperature": 0.7,
|
|
@@ -238,7 +228,7 @@ The following command line options are available for the `start` command:
|
|
|
238
228
|
"gpt-5.4": "<built-in commentary prompt>"
|
|
239
229
|
},
|
|
240
230
|
"smallModel": "gpt-5-mini",
|
|
241
|
-
"
|
|
231
|
+
"useResponsesApiContextManagement": true,
|
|
242
232
|
"modelReasoningEfforts": {
|
|
243
233
|
"gpt-5-mini": "low",
|
|
244
234
|
"gpt-5.3-codex": "xhigh",
|
|
@@ -252,7 +242,7 @@ The following command line options are available for the `start` command:
|
|
|
252
242
|
```
|
|
253
243
|
- **auth.apiKeys:** API keys used for request authentication on non-admin routes. Supports multiple keys for rotation. Requests can authenticate with either `x-api-key: <key>` or `Authorization: Bearer <key>`. If empty or omitted, authentication for non-admin routes is disabled.
|
|
254
244
|
- **auth.adminApiKey:** Single admin key used only for `/admin/*` routes. If missing, the server generates a random key at startup and writes it back to `config.json`. Requests use the same `x-api-key` or `Authorization: Bearer` headers, but regular `auth.apiKeys` never grant access to `/admin/*`.
|
|
255
|
-
- **modelMappings:** Exact `sourceModel -> targetModel` rewrites
|
|
245
|
+
- **modelMappings:** Exact `sourceModel -> targetModel` rewrites shared by top-level `POST /v1/messages`, `POST /v1/messages/count_tokens`, `POST /v1/responses`, and `POST /v1/chat/completions` requests. Omit it or leave it as `{}` to disable rewrites. Both the source and target must be non-empty strings. Targets can be regular model IDs or `provider/model` aliases such as `dashscope/qwen3.6-plus`, and the rewrite happens before provider alias parsing. These mappings are not split per interface. The admin endpoints `GET/POST /admin/config/model-mappings` read and update only this field.
|
|
256
246
|
- **extraPrompts:** Map of `model -> prompt` appended to the first system prompt when translating Anthropic-style requests to Copilot. Use this to inject guardrails or guidance per model. Missing default entries are auto-added without overwriting your custom prompts. The built-in prompts for `gpt-5.3-codex` and `gpt-5.4` enable phase-aware commentary, which lets the model emit a short user-facing progress update before tools or deeper reasoning.
|
|
257
247
|
- **providers:** Global upstream provider map. Each provider key (for example `dashscope`) becomes a route prefix (`/dashscope/v1/messages`). Supports `type: "anthropic"`, `type: "openai-compatible"`, and `type: "openai-responses"`. Top-level clients can also use `model: "dashscope/model-id"` with `/v1/messages`, `/v1/messages/count_tokens`, and `/v1/responses`; the gateway strips the `dashscope/` prefix before forwarding upstream. `GET /v1/models` does not aggregate provider models; use `GET /dashscope/v1/models` for provider model lists.
|
|
258
248
|
- `enabled` defaults to `true` if omitted.
|
|
@@ -269,7 +259,7 @@ The following command line options are available for the `start` command:
|
|
|
269
259
|
- `supportPdf` (optional): Controls whether the model supports PDF/document content. Defaults to `false`; unsupported PDFs are converted to a text notice. Set it to `true` to send PDF/document blocks as OpenAI Chat Completions file parts.
|
|
270
260
|
- `toolContentSupportType` (optional): Tool result content capabilities for that model, as an array of `array`, `image`, and `pdf`. Provider routes default to string-only tool content when omitted. If `supportPdf` is `true` but this list does not include `pdf`, file parts in tool results are moved to user role messages. This provider default does not change the Copilot main flow, which continues to support array + image and not PDF.
|
|
271
261
|
- **smallModel:** Fallback model used for tool-less warmup messages (e.g., Claude Code probe requests); defaults to gpt-5-mini.
|
|
272
|
-
- **
|
|
262
|
+
- **useResponsesApiContextManagement:** When `true`, the proxy adds Responses API `context_management` compaction instructions. Defaults to `true`. Set it to `false` to disable this globally. When enabled, the request includes `context_management` in the body and keeps only the latest compaction carrier on follow-up turns. This is especially useful for long-running tasks.
|
|
273
263
|
- **modelReasoningEfforts:** Per-model `reasoning.effort` sent to the Copilot Responses API. Allowed values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`. If a model isn’t listed, `high` is used by default.
|
|
274
264
|
- **useMessagesApi:** When `true`, Claude-family models that support Copilot's native `/v1/messages` endpoint will use the Messages API; otherwise they fall back to `/chat/completions`. Set to `false` to disable Messages API routing and always use `/chat/completions`. Defaults to `true`.
|
|
275
265
|
- **useResponsesApiWebSocket:** When `true`, Responses API requests use Copilot's websocket transport for models that advertise `ws:/responses`; models that only advertise `/responses` continue to use HTTP. Set to `false` to disable websocket routing and use HTTP `/responses` whenever the selected model supports it. Defaults to `true`.
|
|
@@ -520,14 +510,14 @@ Example `~/.config/opencode/opencode.json`:
|
|
|
520
510
|
"output": ["text"]
|
|
521
511
|
},
|
|
522
512
|
"limit": {
|
|
523
|
-
"context":
|
|
513
|
+
"context": 300000,
|
|
524
514
|
"output": 128000
|
|
525
515
|
}
|
|
526
516
|
},
|
|
527
517
|
"gpt-5-mini": {
|
|
528
518
|
"name": "gpt-5-mini",
|
|
529
519
|
"limit": {
|
|
530
|
-
"context":
|
|
520
|
+
"context": 200000,
|
|
531
521
|
"output": 64000
|
|
532
522
|
}
|
|
533
523
|
},
|
|
@@ -539,7 +529,7 @@ Example `~/.config/opencode/opencode.json`:
|
|
|
539
529
|
"output": ["text"]
|
|
540
530
|
},
|
|
541
531
|
"limit": {
|
|
542
|
-
"context":
|
|
532
|
+
"context": 200000,
|
|
543
533
|
"output": 32000
|
|
544
534
|
},
|
|
545
535
|
"options": {
|
package/README.zh-CN.md
CHANGED
|
@@ -16,9 +16,7 @@
|
|
|
16
16
|
>
|
|
17
17
|
> 3. **内置 `codex` provider:** 执行一次 `npx @jeffreycao/copilot-api@latest auth login --provider codex` 后,AI gateway 会自动持久化并刷新 Codex OAuth 凭据。
|
|
18
18
|
>
|
|
19
|
-
> 4.
|
|
20
|
-
>
|
|
21
|
-
> 5. **注意事项:** README 顶部移除的 GitHub Copilot warning 见 [GitHub Copilot 安全提示](./NOTICE.md#github-copilot-security-notice)。
|
|
19
|
+
> 4. **注意事项:** README 顶部移除的 GitHub Copilot warning 见 [GitHub Copilot 安全提示](./NOTICE.md#github-copilot-security-notice)。
|
|
22
20
|
|
|
23
21
|
---
|
|
24
22
|
|
|
@@ -94,7 +92,7 @@ https://github.com/caozhiyuan/copilot-api/releases
|
|
|
94
92
|
|
|
95
93
|
下载对应平台的安装包后,在应用内登录、选择端口并启动服务,再把你的客户端指向应用里显示的本地端点即可。发布版桌面应用使用随包内置的 Electron 运行时,正常使用不需要额外安装 Node.js;token usage 历史记录会在该内置运行时支持 SQLite 时启用。
|
|
96
94
|
|
|
97
|
-
桌面应用里的高级配置页会通过 `GET/POST /admin/config/model-mappings`
|
|
95
|
+
桌面应用里的高级配置页会通过 `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` 读取它来发起请求。
|
|
98
96
|
|
|
99
97
|
### 桌面应用截图
|
|
100
98
|
|
|
@@ -197,14 +195,7 @@ Copilot API 现在使用子命令结构,主要命令包括:
|
|
|
197
195
|
"enabled": true,
|
|
198
196
|
"baseUrl": "your-base-url",
|
|
199
197
|
"apiKey": "sk-your-provider-key",
|
|
200
|
-
"authType": "x-api-key"
|
|
201
|
-
"adjustInputTokens": false,
|
|
202
|
-
"models": {
|
|
203
|
-
"kimi-k2.5": {
|
|
204
|
-
"temperature": 1,
|
|
205
|
-
"topP": 0.95
|
|
206
|
-
}
|
|
207
|
-
}
|
|
198
|
+
"authType": "x-api-key"
|
|
208
199
|
},
|
|
209
200
|
"dashscope": {
|
|
210
201
|
"type": "openai-compatible",
|
|
@@ -218,8 +209,7 @@ Copilot API 现在使用子命令结构,主要命令包括:
|
|
|
218
209
|
"topK": 20,
|
|
219
210
|
"extraBody": {
|
|
220
211
|
"preserve_thinking": true
|
|
221
|
-
}
|
|
222
|
-
"contextCache": true
|
|
212
|
+
}
|
|
223
213
|
},
|
|
224
214
|
"glm-5.1": {
|
|
225
215
|
"temperature": 0.7,
|
|
@@ -240,7 +230,7 @@ Copilot API 现在使用子命令结构,主要命令包括:
|
|
|
240
230
|
"gpt-5.4": "<built-in commentary prompt>"
|
|
241
231
|
},
|
|
242
232
|
"smallModel": "gpt-5-mini",
|
|
243
|
-
"
|
|
233
|
+
"useResponsesApiContextManagement": true,
|
|
244
234
|
"modelReasoningEfforts": {
|
|
245
235
|
"gpt-5-mini": "low",
|
|
246
236
|
"gpt-5.3-codex": "xhigh",
|
|
@@ -254,7 +244,7 @@ Copilot API 现在使用子命令结构,主要命令包括:
|
|
|
254
244
|
```
|
|
255
245
|
- **auth.apiKeys:** 用于普通非 admin 路由的 API key。支持多个 key 轮换使用。请求可通过 `x-api-key: <key>` 或 `Authorization: Bearer <key>` 进行认证。若为空或省略,则普通路由的认证会被禁用。
|
|
256
246
|
- **auth.adminApiKey:** 仅用于 `/admin/*` 路由的单个 admin key。若未配置,服务会在启动时自动生成一个随机 key,并回写到 `config.json`。它同样使用 `x-api-key` 或 `Authorization: Bearer` 这两种头,但普通 `auth.apiKeys` 不能访问 `/admin/*`。
|
|
257
|
-
- **modelMappings:** 用于顶层 `POST /v1/messages` 和 `POST /v1/
|
|
247
|
+
- **modelMappings:** 用于顶层 `POST /v1/messages`、`POST /v1/messages/count_tokens`、`POST /v1/responses` 和 `POST /v1/chat/completions` 请求的精确 `sourceModel -> targetModel` 重写映射,这几类接口共用同一份规则。省略该字段或保留为 `{}` 时,不会做模型重写。`source` 和 `target` 都必须是非空字符串。`target` 可以是普通模型 ID,也可以是 `provider/model` 形式的别名,例如 `dashscope/qwen3.6-plus`;重写发生在 provider alias 解析之前。这些映射不再按接口区分。`GET/POST /admin/config/model-mappings` 管理接口读写的也只有这个字段。
|
|
258
248
|
- **extraPrompts:** `model -> prompt` 的映射。把 Anthropic 风格请求翻译给 Copilot 时,会将其附加到第一条 system prompt 后面。你可以借此为不同模型注入护栏或指引。缺失的默认项会自动补齐,但不会覆盖你自定义的 prompt。内置的 `gpt-5.3-codex` 和 `gpt-5.4` prompt 会启用带阶段感知的 commentary,让模型在工具调用或更深层推理前先发出简短的用户可见进度说明。
|
|
259
249
|
- **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`。
|
|
260
250
|
- `enabled`:可选,若省略则默认为 `true`。
|
|
@@ -271,7 +261,7 @@ Copilot API 现在使用子命令结构,主要命令包括:
|
|
|
271
261
|
- `supportPdf`:可选,控制该模型是否支持 PDF/document content。默认 `false`,不支持时会把 PDF 转成提示文本;设为 `true` 时会把 PDF/document 转成 OpenAI Chat Completions 的 file part。
|
|
272
262
|
- `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 的能力处理。
|
|
273
263
|
- **smallModel:** 无工具预热消息的回退模型(例如 Claude Code 的探测请求);默认是 `gpt-5-mini`。
|
|
274
|
-
- **
|
|
264
|
+
- **useResponsesApiContextManagement:** 当为 `true` 时,代理会为 Responses API 附加 `context_management` 压缩指令。默认值为 `true`。如需全局关闭,可设为 `false`。启用后,请求体会带上 `context_management`,并在后续轮次中仅保留最新的压缩承载内容,因此特别适合长任务场景。
|
|
275
265
|
- **modelReasoningEfforts:** 按模型配置发送到 Copilot Responses API 的 `reasoning.effort`。可选值包括 `none`、`minimal`、`low`、`medium`、`high` 和 `xhigh`。若某模型未配置,则默认使用 `high`。
|
|
276
266
|
- **useMessagesApi:** 当为 `true` 时,支持 Copilot 原生 `/v1/messages` 的 Claude 系模型会走 Messages API;否则回退到 `/chat/completions`。设为 `false` 可禁用 Messages API 路由,始终使用 `/chat/completions`。默认值为 `true`。
|
|
277
267
|
- **useResponsesApiWebSocket:** 当为 `true` 时,Responses API 请求会优先对声明了 `ws:/responses` 的模型使用 Copilot websocket transport;仅声明 `/responses` 的模型仍走 HTTP。设为 `false` 可禁用 websocket 路由,并在模型支持 `/responses` 时使用 HTTP `/responses`。默认值为 `true`。
|
|
@@ -524,14 +514,14 @@ npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
|
|
|
524
514
|
"output": ["text"]
|
|
525
515
|
},
|
|
526
516
|
"limit": {
|
|
527
|
-
"context":
|
|
517
|
+
"context": 300000,
|
|
528
518
|
"output": 128000
|
|
529
519
|
}
|
|
530
520
|
},
|
|
531
521
|
"gpt-5-mini": {
|
|
532
522
|
"name": "gpt-5-mini",
|
|
533
523
|
"limit": {
|
|
534
|
-
"context":
|
|
524
|
+
"context": 200000,
|
|
535
525
|
"output": 64000
|
|
536
526
|
}
|
|
537
527
|
},
|
|
@@ -543,7 +533,7 @@ npx @jeffreycao/copilot-api@latest --oauth-app=opencode start
|
|
|
543
533
|
"output": ["text"]
|
|
544
534
|
},
|
|
545
535
|
"limit": {
|
|
546
|
-
"context":
|
|
536
|
+
"context": 200000,
|
|
547
537
|
"output": 32000
|
|
548
538
|
},
|
|
549
539
|
"options": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as ensurePaths, y as PATHS } from "./config-
|
|
2
|
-
import { A as loginCodex, R as state, a as setupGitHubToken, n as persistCodexCredentials } from "./token-
|
|
1
|
+
import { b as ensurePaths, y as PATHS } from "./config-BVmeo70f.js";
|
|
2
|
+
import { A as loginCodex, R as state, a as setupGitHubToken, n as persistCodexCredentials } from "./token-DY6kM1jO.js";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
//#region src/auth.ts
|
|
@@ -113,4 +113,4 @@ const auth = defineCommand({
|
|
|
113
113
|
//#endregion
|
|
114
114
|
export { auth };
|
|
115
115
|
|
|
116
|
-
//# sourceMappingURL=auth-
|
|
116
|
+
//# sourceMappingURL=auth-CnUtw9S3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-
|
|
1
|
+
{"version":3,"file":"auth-CnUtw9S3.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,5 +1,5 @@
|
|
|
1
|
-
import { b as ensurePaths } from "./config-
|
|
2
|
-
import { _ as getCopilotUsage, a as setupGitHubToken } from "./token-
|
|
1
|
+
import { b as ensurePaths } from "./config-BVmeo70f.js";
|
|
2
|
+
import { _ as getCopilotUsage, a as setupGitHubToken } from "./token-DY6kM1jO.js";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
//#region src/check-usage.ts
|
|
@@ -39,4 +39,4 @@ const checkUsage = defineCommand({
|
|
|
39
39
|
//#endregion
|
|
40
40
|
export { checkUsage };
|
|
41
41
|
|
|
42
|
-
//# sourceMappingURL=check-usage-
|
|
42
|
+
//# sourceMappingURL=check-usage-LKHTA_Qx.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-usage-
|
|
1
|
+
{"version":3,"file":"check-usage-LKHTA_Qx.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"}
|
|
@@ -67,7 +67,7 @@ const defaultConfig = {
|
|
|
67
67
|
"gpt-5.5": gpt5CommentaryPrompt
|
|
68
68
|
},
|
|
69
69
|
smallModel: "gpt-5-mini",
|
|
70
|
-
|
|
70
|
+
useResponsesApiContextManagement: true,
|
|
71
71
|
modelReasoningEfforts: {
|
|
72
72
|
"gpt-5-mini": "low",
|
|
73
73
|
"gpt-5.3-codex": "xhigh",
|
|
@@ -253,11 +253,8 @@ function resolveMappedModel(model) {
|
|
|
253
253
|
function getSmallModel() {
|
|
254
254
|
return getConfig().smallModel ?? "gpt-5-mini";
|
|
255
255
|
}
|
|
256
|
-
function
|
|
257
|
-
return getConfig().
|
|
258
|
-
}
|
|
259
|
-
function isResponsesApiContextManagementModel(model) {
|
|
260
|
-
return getResponsesApiContextManagementModels().includes(model);
|
|
256
|
+
function isResponsesApiContextManagementEnabled() {
|
|
257
|
+
return getConfig().useResponsesApiContextManagement ?? true;
|
|
261
258
|
}
|
|
262
259
|
function getReasoningEffortForModel(model) {
|
|
263
260
|
return getConfig().modelReasoningEfforts?.[model] ?? "high";
|
|
@@ -360,6 +357,6 @@ function getClaudeTokenMultiplier() {
|
|
|
360
357
|
return getConfig().claudeTokenMultiplier ?? 1.15;
|
|
361
358
|
}
|
|
362
359
|
//#endregion
|
|
363
|
-
export { setModelMappings as _, getModelMappings as a, ensurePaths as b, getReasoningEffortForModel as c,
|
|
360
|
+
export { setModelMappings as _, getModelMappings as a, ensurePaths as b, getReasoningEffortForModel as c, isResponsesApiContextManagementEnabled as d, isResponsesApiWebSearchEnabled as f, resolveMappedModel as g, mergeConfigWithDefaults as h, getExtraPromptForModel as i, getSmallModel as l, listEnabledProviders as m, getClaudeTokenMultiplier as n, getProviderConfig as o, isResponsesApiWebSocketEnabled as p, getConfig as r, getRawProviderConfig as s, getAnthropicApiKey as t, isMessagesApiEnabled as u, setProviderConfig as v, PATHS as y };
|
|
364
361
|
|
|
365
|
-
//# sourceMappingURL=config-
|
|
362
|
+
//# sourceMappingURL=config-BVmeo70f.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-BVmeo70f.js","names":["fs"],"sources":["../src/lib/paths.ts","../src/lib/config.ts"],"sourcesContent":["import fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\n\nconst AUTH_APP = process.env.COPILOT_API_OAUTH_APP?.trim() || \"\"\nconst ENTERPRISE_PREFIX = process.env.COPILOT_API_ENTERPRISE_URL ? \"ent_\" : \"\"\n\nconst DEFAULT_DIR = path.join(os.homedir(), \".local\", \"share\", \"copilot-api\")\nconst APP_DIR = process.env.COPILOT_API_HOME || DEFAULT_DIR\n\nconst GITHUB_TOKEN_PATH = path.join(\n APP_DIR,\n AUTH_APP,\n ENTERPRISE_PREFIX + \"github_token\",\n)\nconst CODEX_CREDENTIAL_PATH = path.join(APP_DIR, \"codex_credentials.json\")\nconst CONFIG_PATH = path.join(APP_DIR, \"config.json\")\n\nexport const PATHS = {\n APP_DIR,\n GITHUB_TOKEN_PATH,\n CODEX_CREDENTIAL_PATH,\n CONFIG_PATH,\n}\n\nexport async function ensurePaths(): Promise<void> {\n await fs.mkdir(path.join(PATHS.APP_DIR, AUTH_APP), { recursive: true })\n await ensureFile(PATHS.GITHUB_TOKEN_PATH)\n await ensureFile(PATHS.CONFIG_PATH)\n}\n\nasync function ensureFile(filePath: string): Promise<void> {\n try {\n await fs.access(filePath, fs.constants.W_OK)\n } catch {\n await fs.writeFile(filePath, \"\")\n await fs.chmod(filePath, 0o600)\n }\n}\n","import consola from \"consola\"\nimport { randomBytes } from \"node:crypto\"\nimport fs from \"node:fs\"\n\nimport { PATHS } from \"./paths\"\n\nexport interface AppConfig {\n auth?: {\n apiKeys?: Array<string>\n adminApiKey?: string\n }\n providers?: Record<string, ProviderConfig>\n modelMappings?: Record<string, string>\n extraPrompts?: Record<string, string>\n smallModel?: string\n useResponsesApiContextManagement?: boolean\n modelReasoningEfforts?: Record<\n string,\n \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"\n >\n useMessagesApi?: boolean\n useResponsesApiWebSocket?: boolean\n anthropicApiKey?: string\n useResponsesApiWebSearch?: boolean\n claudeTokenMultiplier?: number\n}\n\nexport interface ModelConfig {\n temperature?: number\n topP?: number\n topK?: number\n extraBody?: Record<string, unknown>\n contextCache?: boolean\n supportPdf?: boolean\n toolContentSupportType?: Array<ToolContentSupportType>\n}\n\nexport type ProviderAuthType = \"authorization\" | \"oauth2\" | \"x-api-key\"\nexport type ProviderType =\n | \"anthropic\"\n | \"openai-compatible\"\n | \"openai-responses\"\nexport type ToolContentSupportType = \"array\" | \"image\" | \"pdf\"\n\nexport interface ProviderConfig {\n type?: string\n enabled?: boolean\n baseUrl?: string\n apiKey?: string\n authType?: ProviderAuthType\n models?: Record<string, ModelConfig>\n adjustInputTokens?: boolean\n}\n\nexport interface ResolvedProviderConfig {\n name: string\n type: ProviderType\n baseUrl: string\n apiKey: string\n authType: ProviderAuthType\n models?: Record<string, ModelConfig>\n adjustInputTokens?: boolean\n}\n\nconst gpt5ExplorationPrompt = `## Exploration and reading files\n- **Think first.** Before any tool call, decide ALL files/resources you will need.\n- **Batch everything.** If you need multiple files (even from different places), read them together.\n- **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.\n- **Only make sequential calls if you truly cannot know the next file without seeing a result first.**\n- **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`\n\nconst gpt5CommentaryPrompt = `# Working with the user\n\nYou interact with the user through a terminal. You have 2 ways of communicating with the users: \n- Share intermediary updates in \\`commentary\\` channel. \n- After you have completed all your work, send a message to the \\`final\\` channel. \n\n## Intermediary updates\n\n- Intermediary updates go to the \\`commentary\\` channel.\n- User updates are short updates while you are working, they are NOT final answers.\n- You use 1-2 sentence user updates to communicate progress and new information to the user as you are doing work.\n- Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements (“Done —”, “Got it”, “Great question, ”) or framing phrases.\n- You provide user updates frequently, every 20s.\n- Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such as \"Got it -\" or \"Understood -\" etc.\n- When exploring, e.g. searching, reading files, you provide user updates as you go, every 20s, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.\n- After you have sufficient context, and the work is substantial, you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).\n- Before performing file edits of any kind, you provide updates explaining what edits you are making.\n- As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.\n- Tone of your updates MUST match your personality.`\n\nconst defaultConfig: AppConfig = {\n auth: {\n apiKeys: [],\n },\n providers: {},\n modelMappings: {},\n extraPrompts: {\n \"gpt-5-mini\": gpt5ExplorationPrompt,\n \"gpt-5.3-codex\": gpt5CommentaryPrompt,\n \"gpt-5.4-mini\": gpt5CommentaryPrompt,\n \"gpt-5.4\": gpt5CommentaryPrompt,\n \"gpt-5.5\": gpt5CommentaryPrompt,\n },\n smallModel: \"gpt-5-mini\",\n useResponsesApiContextManagement: true,\n modelReasoningEfforts: {\n \"gpt-5-mini\": \"low\",\n \"gpt-5.3-codex\": \"xhigh\",\n \"gpt-5.4-mini\": \"xhigh\",\n \"gpt-5.4\": \"xhigh\",\n \"gpt-5.5\": \"xhigh\",\n },\n useMessagesApi: true,\n useResponsesApiWebSocket: true,\n useResponsesApiWebSearch: true,\n}\n\nlet cachedConfig: AppConfig | null = null\n\nfunction normalizeAdminApiKey(adminApiKey: unknown): string | null {\n if (typeof adminApiKey !== \"string\") {\n if (adminApiKey !== undefined) {\n consola.warn(\n \"Invalid auth.adminApiKey config. Expected a non-empty string.\",\n )\n }\n return null\n }\n\n const normalizedAdminApiKey = adminApiKey.trim()\n if (!normalizedAdminApiKey) {\n consola.warn(\n \"Invalid auth.adminApiKey config. Expected a non-empty string.\",\n )\n return null\n }\n\n return normalizedAdminApiKey\n}\n\nfunction generateAdminApiKey(): string {\n return randomBytes(32).toString(\"hex\")\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error\n}\n\nfunction ensureConfigFile(): void {\n try {\n fs.accessSync(PATHS.CONFIG_PATH, fs.constants.R_OK | fs.constants.W_OK)\n } catch {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n try {\n fs.chmodSync(PATHS.CONFIG_PATH, 0o600)\n } catch {\n return\n }\n }\n}\n\nfunction readConfigFromDisk(): AppConfig {\n ensureConfigFile()\n try {\n const raw = fs.readFileSync(PATHS.CONFIG_PATH, \"utf8\")\n if (!raw.trim()) {\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(defaultConfig, null, 2)}\\n`,\n \"utf8\",\n )\n return defaultConfig\n }\n return JSON.parse(raw) as AppConfig\n } catch (error) {\n consola.error(\"Failed to read config file, using default config\", error)\n return defaultConfig\n }\n}\n\nfunction readEditableConfigFromDisk(): AppConfig {\n try {\n const raw = fs.readFileSync(PATHS.CONFIG_PATH, \"utf8\")\n if (!raw.trim()) {\n return {}\n }\n return JSON.parse(raw) as AppConfig\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return {}\n }\n if (error instanceof SyntaxError) {\n throw new Error(`Config file is not valid JSON: ${PATHS.CONFIG_PATH}`)\n }\n throw error\n }\n}\n\nfunction writeConfigToDisk(config: AppConfig): void {\n fs.mkdirSync(PATHS.APP_DIR, { recursive: true })\n fs.writeFileSync(\n PATHS.CONFIG_PATH,\n `${JSON.stringify(config, null, 2)}\\n`,\n \"utf8\",\n )\n}\n\nfunction mergeDefaultConfig(config: AppConfig): {\n mergedConfig: AppConfig\n changed: boolean\n} {\n const extraPrompts = config.extraPrompts ?? {}\n const defaultExtraPrompts = defaultConfig.extraPrompts ?? {}\n const modelReasoningEfforts = config.modelReasoningEfforts ?? {}\n const defaultModelReasoningEfforts = defaultConfig.modelReasoningEfforts ?? {}\n\n const missingExtraPromptModels = Object.keys(defaultExtraPrompts).filter(\n (model) => !Object.hasOwn(extraPrompts, model),\n )\n\n const missingReasoningEffortModels = Object.keys(\n defaultModelReasoningEfforts,\n ).filter((model) => !Object.hasOwn(modelReasoningEfforts, model))\n\n const hasExtraPromptChanges = missingExtraPromptModels.length > 0\n const hasReasoningEffortChanges = missingReasoningEffortModels.length > 0\n\n if (!hasExtraPromptChanges && !hasReasoningEffortChanges) {\n return { mergedConfig: config, changed: false }\n }\n\n return {\n mergedConfig: {\n ...config,\n extraPrompts: {\n ...defaultExtraPrompts,\n ...extraPrompts,\n },\n modelReasoningEfforts: {\n ...defaultModelReasoningEfforts,\n ...modelReasoningEfforts,\n },\n },\n changed: true,\n }\n}\n\nfunction ensureAdminApiKey(config: AppConfig): {\n mergedConfig: AppConfig\n changed: boolean\n} {\n const normalizedAdminApiKey = normalizeAdminApiKey(config.auth?.adminApiKey)\n if (normalizedAdminApiKey) {\n if (config.auth?.adminApiKey === normalizedAdminApiKey) {\n return { mergedConfig: config, changed: false }\n }\n\n return {\n mergedConfig: {\n ...config,\n auth: {\n ...config.auth,\n adminApiKey: normalizedAdminApiKey,\n },\n },\n changed: true,\n }\n }\n\n const editableConfig = readEditableConfigFromDisk()\n const { mergedConfig } = mergeDefaultConfig({\n ...editableConfig,\n auth: {\n ...editableConfig.auth,\n adminApiKey: generateAdminApiKey(),\n },\n })\n\n return { mergedConfig, changed: true }\n}\n\nexport function mergeConfigWithDefaults(): AppConfig {\n const config = readConfigFromDisk()\n const { mergedConfig, changed } = mergeDefaultConfig(config)\n const {\n mergedConfig: mergedConfigWithAdminApiKey,\n changed: adminApiKeyChanged,\n } = ensureAdminApiKey(mergedConfig)\n const shouldPersistConfig = changed || adminApiKeyChanged\n\n if (shouldPersistConfig) {\n try {\n writeConfigToDisk(mergedConfigWithAdminApiKey)\n } catch (writeError) {\n if (adminApiKeyChanged) {\n throw writeError\n }\n\n consola.warn(\n \"Failed to write merged extraPrompts to config file\",\n writeError,\n )\n }\n }\n\n cachedConfig = mergedConfigWithAdminApiKey\n return mergedConfigWithAdminApiKey\n}\n\nexport function getConfig(): AppConfig {\n cachedConfig ??= mergeDefaultConfig(readConfigFromDisk()).mergedConfig\n return cachedConfig\n}\n\nexport function reloadConfig(): AppConfig {\n return mergeConfigWithDefaults()\n}\n\nexport function getExtraPromptForModel(model: string): string {\n const config = getConfig()\n return config.extraPrompts?.[model] ?? \"\"\n}\n\nexport function getModelMappings(): Record<string, string> {\n const config = getConfig()\n const modelMappings = config.modelMappings\n if (!modelMappings) {\n return { ...defaultConfig.modelMappings }\n }\n\n const validMappings: Record<string, string> = {}\n for (const [sourceModel, targetModel] of Object.entries(modelMappings)) {\n if (\n !sourceModel\n || typeof targetModel !== \"string\"\n || targetModel.length === 0\n ) {\n continue\n }\n validMappings[sourceModel] = targetModel\n }\n\n return validMappings\n}\n\nfunction validateModelMappings(\n modelMappings: Record<string, string>,\n): Record<string, string> {\n const validatedMappings: Record<string, string> = {}\n for (const [sourceModel, targetModel] of Object.entries(modelMappings)) {\n if (!sourceModel || !targetModel) {\n throw new Error(\n \"Each model mapping must use non-empty source and target values.\",\n )\n }\n validatedMappings[sourceModel] = targetModel\n }\n\n return validatedMappings\n}\n\nexport function setModelMappings(\n modelMappings: Record<string, string>,\n): Record<string, string> {\n const nextConfig = {\n ...readEditableConfigFromDisk(),\n modelMappings: validateModelMappings(modelMappings),\n }\n\n writeConfigToDisk(nextConfig)\n cachedConfig = reloadConfig()\n return getModelMappings()\n}\n\nexport function resolveMappedModel(model: string): string {\n return getModelMappings()[model] ?? model\n}\n\nexport function getSmallModel(): string {\n const config = getConfig()\n return config.smallModel ?? \"gpt-5-mini\"\n}\n\nexport function isResponsesApiContextManagementEnabled(): boolean {\n const config = getConfig()\n return config.useResponsesApiContextManagement ?? true\n}\n\nexport function getReasoningEffortForModel(\n model: string,\n): \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" {\n const config = getConfig()\n return config.modelReasoningEfforts?.[model] ?? \"high\"\n}\n\nexport function normalizeProviderBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/u, \"\")\n}\n\nfunction getDefaultProviderAuthType(\n providerType: ProviderType,\n): ProviderAuthType {\n return providerType === \"anthropic\" ? \"x-api-key\" : \"authorization\"\n}\n\nexport function resolveProviderAuthType(\n providerName: string,\n authType: string | undefined,\n providerType: ProviderType,\n): ProviderAuthType {\n const defaultAuthType = getDefaultProviderAuthType(providerType)\n if (authType === undefined) {\n return defaultAuthType\n }\n\n if (authType === \"x-api-key\") {\n return \"x-api-key\"\n }\n\n if (authType === \"oauth2\") {\n if (providerName === \"codex\") {\n return authType\n }\n\n consola.warn(\n `Provider ${providerName} has authType 'oauth2', which is only supported by the builtin codex provider, falling back to ${defaultAuthType}`,\n )\n return defaultAuthType\n }\n\n if (authType === \"authorization\") {\n return authType\n }\n\n consola.warn(\n `Provider ${providerName} has invalid authType '${authType}', falling back to ${defaultAuthType}`,\n )\n return defaultAuthType\n}\n\nfunction isProviderApiKeyRequired(\n providerName: string,\n authType: ProviderAuthType,\n): boolean {\n return !(providerName === \"codex\" && authType === \"oauth2\")\n}\n\nexport function getRawProviderConfig(name: string): ProviderConfig | null {\n const providerName = name.trim()\n if (!providerName) {\n return null\n }\n\n const config = getConfig()\n return config.providers?.[providerName] ?? null\n}\n\nexport function setProviderConfig(\n name: string,\n provider: ProviderConfig,\n): ProviderConfig {\n const providerName = name.trim()\n if (!providerName) {\n throw new Error(\"Provider name must be a non-empty string\")\n }\n\n if (isReservedProviderName(providerName)) {\n throw new Error(\n `Provider ${providerName} is reserved and cannot be configured in config.providers`,\n )\n }\n\n const editableConfig = readEditableConfigFromDisk()\n const nextConfig = {\n ...editableConfig,\n providers: {\n ...editableConfig.providers,\n [providerName]: provider,\n },\n }\n\n writeConfigToDisk(nextConfig)\n cachedConfig = reloadConfig()\n return getRawProviderConfig(providerName) ?? provider\n}\n\nexport function getProviderConfig(name: string): ResolvedProviderConfig | null {\n const providerName = name.trim()\n if (!providerName) {\n return null\n }\n\n if (isReservedProviderName(providerName)) {\n consola.warn(\n `Provider ${providerName} is reserved and cannot be configured in config.providers`,\n )\n return null\n }\n\n const provider = getRawProviderConfig(providerName)\n if (!provider) {\n return null\n }\n\n if (provider.enabled === false) {\n return null\n }\n\n const type = provider.type ?? \"anthropic\"\n if (\n type !== \"anthropic\"\n && type !== \"openai-compatible\"\n && type !== \"openai-responses\"\n ) {\n consola.warn(\n `Provider ${providerName} is ignored because type '${type}' is not supported`,\n )\n return null\n }\n\n const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? \"\")\n const authType = resolveProviderAuthType(\n providerName,\n provider.authType,\n type,\n )\n const apiKey = (provider.apiKey ?? \"\").trim()\n const missingFields = [\n ...(!baseUrl ? [\"baseUrl\"] : []),\n ...(isProviderApiKeyRequired(providerName, authType) && !apiKey ?\n [\"apiKey\"]\n : []),\n ]\n\n if (missingFields.length > 0) {\n consola.warn(\n `Provider ${providerName} is enabled but missing ${missingFields.join(\" or \")}`,\n )\n return null\n }\n\n return {\n name: providerName,\n type,\n baseUrl,\n apiKey,\n authType,\n models: provider.models,\n adjustInputTokens: provider.adjustInputTokens,\n }\n}\n\nexport function listEnabledProviders(): Array<string> {\n const config = getConfig()\n const providerNames = Object.keys(config.providers ?? {})\n return providerNames.filter((name) => getProviderConfig(name) !== null)\n}\n\nexport function isReservedProviderName(name: string): boolean {\n return name.trim() === \"copilot\"\n}\n\nexport function isMessagesApiEnabled(): boolean {\n const config = getConfig()\n return config.useMessagesApi ?? true\n}\n\nexport function isResponsesApiWebSocketEnabled(): boolean {\n const config = getConfig()\n return config.useResponsesApiWebSocket ?? true\n}\n\nexport function getAnthropicApiKey(): string | undefined {\n const config = getConfig()\n return config.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY ?? undefined\n}\n\nexport function isResponsesApiWebSearchEnabled(): boolean {\n const config = getConfig()\n return config.useResponsesApiWebSearch ?? true\n}\n\nexport function getClaudeTokenMultiplier(): number {\n const config = getConfig()\n return config.claudeTokenMultiplier ?? 1.15\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as listEnabledProviders, s as getRawProviderConfig, y as PATHS } from "./config-
|
|
1
|
+
import { m as listEnabledProviders, s as getRawProviderConfig, y as PATHS } from "./config-BVmeo70f.js";
|
|
2
2
|
import { defineCommand } from "citty";
|
|
3
3
|
import consola from "consola";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
@@ -89,4 +89,4 @@ const debug = defineCommand({
|
|
|
89
89
|
//#endregion
|
|
90
90
|
export { debug };
|
|
91
91
|
|
|
92
|
-
//# sourceMappingURL=debug-
|
|
92
|
+
//# sourceMappingURL=debug-9GrBfKqc.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug-
|
|
1
|
+
{"version":3,"file":"debug-9GrBfKqc.js","names":[],"sources":["../src/debug.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\nimport fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport { fileURLToPath } from \"node:url\"\n\nimport { getRawProviderConfig, listEnabledProviders } from \"./lib/config\"\nimport { PATHS } from \"./lib/paths\"\n\ninterface DebugInfo {\n providers: {\n codexConfigured: boolean\n enabled: Array<string>\n }\n version: string\n runtime: {\n name: string\n version: string\n platform: string\n arch: string\n }\n paths: {\n APP_DIR: string\n CONFIG_PATH: string\n GITHUB_TOKEN_PATH: string\n }\n tokenExists: boolean\n}\n\ninterface RunDebugOptions {\n json: boolean\n}\n\nasync function getPackageVersion(): Promise<string> {\n try {\n const packageJsonPath = fileURLToPath(\n new URL(\"../package.json\", import.meta.url),\n )\n // @ts-expect-error https://github.com/sindresorhus/eslint-plugin-unicorn/blob/v59.0.1/docs/rules/prefer-json-parse-buffer.md\n // JSON.parse() can actually parse buffers\n const packageJson = JSON.parse(await fs.readFile(packageJsonPath)) as {\n version: string\n }\n return packageJson.version\n } catch {\n return \"unknown\"\n }\n}\n\nfunction getRuntimeInfo() {\n const isBun = typeof Bun !== \"undefined\"\n\n return {\n name: isBun ? \"bun\" : \"node\",\n version: isBun ? Bun.version : process.version.slice(1),\n platform: os.platform(),\n arch: os.arch(),\n }\n}\n\nasync function checkFileExists(filePath: string): Promise<boolean> {\n try {\n const stats = await fs.stat(filePath)\n if (!stats.isFile()) return false\n\n const content = await fs.readFile(filePath, \"utf8\")\n return content.trim().length > 0\n } catch {\n return false\n }\n}\n\nasync function getDebugInfo(): Promise<DebugInfo> {\n const [version, tokenExists] = await Promise.all([\n getPackageVersion(),\n checkFileExists(PATHS.GITHUB_TOKEN_PATH),\n ])\n\n return {\n providers: {\n codexConfigured: getRawProviderConfig(\"codex\") !== null,\n enabled: listEnabledProviders(),\n },\n version,\n runtime: getRuntimeInfo(),\n paths: {\n APP_DIR: PATHS.APP_DIR,\n CONFIG_PATH: PATHS.CONFIG_PATH,\n GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH,\n },\n tokenExists,\n }\n}\n\nfunction printDebugInfoPlain(info: DebugInfo): void {\n consola.info(`copilot-api debug\n\nVersion: ${info.version}\nRuntime: ${info.runtime.name} ${info.runtime.version} (${info.runtime.platform} ${info.runtime.arch})\n\nProviders:\n- enabled: ${info.providers.enabled.join(\", \") || \"none\"}\n- codex configured: ${info.providers.codexConfigured ? \"Yes\" : \"No\"}\n\nPaths:\n- APP_DIR: ${info.paths.APP_DIR}\n- CONFIG_PATH: ${info.paths.CONFIG_PATH}\n- GITHUB_TOKEN_PATH: ${info.paths.GITHUB_TOKEN_PATH}\n\nGitHub token exists: ${info.tokenExists ? \"Yes\" : \"No\"}`)\n}\n\nfunction printDebugInfoJson(info: DebugInfo): void {\n console.log(JSON.stringify(info, null, 2))\n}\n\nexport async function runDebug(options: RunDebugOptions): Promise<void> {\n const debugInfo = await getDebugInfo()\n\n if (options.json) {\n printDebugInfoJson(debugInfo)\n } else {\n printDebugInfoPlain(debugInfo)\n }\n}\n\nexport const debug = defineCommand({\n meta: {\n name: \"debug\",\n description: \"Print debug information about the application\",\n },\n args: {\n json: {\n type: \"boolean\",\n default: false,\n description: \"Output debug information as JSON\",\n },\n },\n run({ args }) {\n return runDebug({\n json: args.json,\n })\n },\n})\n"],"mappings":";;;;;;;AAmCA,eAAe,oBAAqC;CAClD,IAAI;EACF,MAAM,kBAAkB,cACtB,IAAI,IAAI,mBAAmB,OAAO,KAAK,IAAI,CAC5C;EAMD,OAHoB,KAAK,MAAM,MAAM,GAAG,SAAS,gBAAgB,CAG/C,CAAC;SACb;EACN,OAAO;;;AAIX,SAAS,iBAAiB;CACxB,MAAM,QAAQ,OAAO,QAAQ;CAE7B,OAAO;EACL,MAAM,QAAQ,QAAQ;EACtB,SAAS,QAAQ,IAAI,UAAU,QAAQ,QAAQ,MAAM,EAAE;EACvD,UAAU,GAAG,UAAU;EACvB,MAAM,GAAG,MAAM;EAChB;;AAGH,eAAe,gBAAgB,UAAoC;CACjE,IAAI;EAEF,IAAI,EAAC,MADe,GAAG,KAAK,SAAS,EAC1B,QAAQ,EAAE,OAAO;EAG5B,QAAO,MADe,GAAG,SAAS,UAAU,OAAO,EACpC,MAAM,CAAC,SAAS;SACzB;EACN,OAAO;;;AAIX,eAAe,eAAmC;CAChD,MAAM,CAAC,SAAS,eAAe,MAAM,QAAQ,IAAI,CAC/C,mBAAmB,EACnB,gBAAgB,MAAM,kBAAkB,CACzC,CAAC;CAEF,OAAO;EACL,WAAW;GACT,iBAAiB,qBAAqB,QAAQ,KAAK;GACnD,SAAS,sBAAsB;GAChC;EACD;EACA,SAAS,gBAAgB;EACzB,OAAO;GACL,SAAS,MAAM;GACf,aAAa,MAAM;GACnB,mBAAmB,MAAM;GAC1B;EACD;EACD;;AAGH,SAAS,oBAAoB,MAAuB;CAClD,QAAQ,KAAK;;WAEJ,KAAK,QAAQ;WACb,KAAK,QAAQ,KAAK,GAAG,KAAK,QAAQ,QAAQ,IAAI,KAAK,QAAQ,SAAS,GAAG,KAAK,QAAQ,KAAK;;;aAGvF,KAAK,UAAU,QAAQ,KAAK,KAAK,IAAI,OAAO;sBACnC,KAAK,UAAU,kBAAkB,QAAQ,KAAK;;;aAGvD,KAAK,MAAM,QAAQ;iBACf,KAAK,MAAM,YAAY;uBACjB,KAAK,MAAM,kBAAkB;;uBAE7B,KAAK,cAAc,QAAQ,OAAO;;AAGzD,SAAS,mBAAmB,MAAuB;CACjD,QAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;;AAG5C,eAAsB,SAAS,SAAyC;CACtE,MAAM,YAAY,MAAM,cAAc;CAEtC,IAAI,QAAQ,MACV,mBAAmB,UAAU;MAE7B,oBAAoB,UAAU;;AAIlC,MAAa,QAAQ,cAAc;CACjC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,EACJ,MAAM;EACJ,MAAM;EACN,SAAS;EACT,aAAa;EACd,EACF;CACD,IAAI,EAAE,QAAQ;EACZ,OAAO,SAAS,EACd,MAAM,KAAK,MACZ,CAAC;;CAEL,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-
|
|
43
|
-
const { checkUsage } = await import("./check-usage-
|
|
44
|
-
const { debug } = await import("./debug-
|
|
42
|
+
const { auth } = await import("./auth-CnUtw9S3.js");
|
|
43
|
+
const { checkUsage } = await import("./check-usage-LKHTA_Qx.js");
|
|
44
|
+
const { debug } = await import("./debug-9GrBfKqc.js");
|
|
45
45
|
const { mcp } = await import("./mcp-DZgcvqQY.js");
|
|
46
|
-
const { start } = await import("./start-
|
|
46
|
+
const { start } = await import("./start-BrG478zI.js");
|
|
47
47
|
await runMain(defineCommand({
|
|
48
48
|
meta: {
|
|
49
49
|
name: "copilot-api",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as setModelMappings, a as getModelMappings, c as getReasoningEffortForModel, d as
|
|
2
|
-
import { B as forwardError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as createPooledWebSocketStream, I as createWebSocketUrl, M as generateTraceId, N as requestContext, O as compactSystemPromptStarts, P as resolveTraceId$1, R as state, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, _ as getCopilotUsage, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, j as forwardCodexResponses, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as HTTPError } from "./token-
|
|
1
|
+
import { _ as setModelMappings, a as getModelMappings, c as getReasoningEffortForModel, d as isResponsesApiContextManagementEnabled, f as isResponsesApiWebSearchEnabled, g as resolveMappedModel, i as getExtraPromptForModel, l as getSmallModel, n as getClaudeTokenMultiplier, o as getProviderConfig, p as isResponsesApiWebSocketEnabled, r as getConfig, s as getRawProviderConfig, t as getAnthropicApiKey, u as isMessagesApiEnabled, y as PATHS } from "./config-BVmeo70f.js";
|
|
2
|
+
import { B as forwardError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as createPooledWebSocketStream, I as createWebSocketUrl, M as generateTraceId, N as requestContext, O as compactSystemPromptStarts, P as resolveTraceId$1, R as state, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, _ as getCopilotUsage, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, j as forwardCodexResponses, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as HTTPError } from "./token-DY6kM1jO.js";
|
|
3
3
|
import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-wA-fLduL.js";
|
|
4
4
|
import consola from "consola";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
@@ -1026,8 +1026,11 @@ const createChatCompletions = async (payload, options) => {
|
|
|
1026
1026
|
//#region src/routes/chat-completions/handler.ts
|
|
1027
1027
|
const logger$7 = createHandlerLogger("chat-completions-handler");
|
|
1028
1028
|
async function handleCompletion$1(c) {
|
|
1029
|
-
await checkRateLimit(state);
|
|
1030
1029
|
let payload = await c.req.json();
|
|
1030
|
+
const requestedModel = payload.model;
|
|
1031
|
+
payload.model = resolveMappedModel(payload.model);
|
|
1032
|
+
if (payload.model !== requestedModel) logger$7.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
|
|
1033
|
+
await checkRateLimit(state);
|
|
1031
1034
|
debugJsonTail(logger$7, "Request payload:", {
|
|
1032
1035
|
value: payload,
|
|
1033
1036
|
tailLength: 400
|
|
@@ -3773,8 +3776,9 @@ const stringifyToolSearchArguments = (argumentsValue) => {
|
|
|
3773
3776
|
return;
|
|
3774
3777
|
}
|
|
3775
3778
|
};
|
|
3779
|
+
const DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO = .9;
|
|
3776
3780
|
const responsesUtilsDependencies = {
|
|
3777
|
-
|
|
3781
|
+
isResponsesApiContextManagementEnabled,
|
|
3778
3782
|
isResponsesApiWebSocketEnabled
|
|
3779
3783
|
};
|
|
3780
3784
|
const getResponsesRequestOptions = (payload) => {
|
|
@@ -3873,18 +3877,18 @@ const isResponseFunctionCallOutputItem = (item) => {
|
|
|
3873
3877
|
const isResponseInputImage = (content) => {
|
|
3874
3878
|
return typeof content === "object" && content !== null && "type" in content && content.type === "input_image";
|
|
3875
3879
|
};
|
|
3876
|
-
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
3877
|
-
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens *
|
|
3878
|
-
return
|
|
3880
|
+
const resolveResponsesCompactThreshold = (maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
|
|
3881
|
+
if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * compactThresholdRatio);
|
|
3882
|
+
return 2e5 * compactThresholdRatio;
|
|
3879
3883
|
};
|
|
3880
3884
|
const createCompactionContextManagement = (compactThreshold) => [{
|
|
3881
3885
|
type: "compaction",
|
|
3882
3886
|
compact_threshold: compactThreshold
|
|
3883
3887
|
}];
|
|
3884
|
-
const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
|
|
3888
|
+
const applyResponsesApiContextManagement = (payload, maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
|
|
3885
3889
|
if (payload.context_management !== void 0) return;
|
|
3886
|
-
if (!responsesUtilsDependencies.
|
|
3887
|
-
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
|
|
3890
|
+
if (!responsesUtilsDependencies.isResponsesApiContextManagementEnabled()) return;
|
|
3891
|
+
payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens, compactThresholdRatio));
|
|
3888
3892
|
};
|
|
3889
3893
|
const compactInputByLatestCompaction = (payload) => {
|
|
3890
3894
|
if (!Array.isArray(payload.input) || payload.input.length === 0) return;
|
|
@@ -5020,12 +5024,13 @@ async function handleProviderResponsesForProvider(c, options) {
|
|
|
5020
5024
|
message: `Provider '${provider}' does not support the /v1/responses endpoint`,
|
|
5021
5025
|
type: "invalid_request_error"
|
|
5022
5026
|
} }, 400);
|
|
5023
|
-
applyResponsesApiContextManagement(payload, (providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0)?.capabilities.limits.max_prompt_tokens ?? 0);
|
|
5027
|
+
applyResponsesApiContextManagement(payload, (providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0)?.capabilities.limits.max_prompt_tokens ?? 0, .8);
|
|
5024
5028
|
const contextManagement = payload.context_management;
|
|
5025
5029
|
debugJson(logger$2, "Translated Responses request payload:", {
|
|
5026
5030
|
contextManagement,
|
|
5027
5031
|
provider
|
|
5028
5032
|
});
|
|
5033
|
+
compactInputByLatestCompaction(payload);
|
|
5029
5034
|
if (providerConfig.name === "codex") {
|
|
5030
5035
|
const upstreamResponse = await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl);
|
|
5031
5036
|
const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
|
|
@@ -5159,6 +5164,9 @@ const responsesHandlerDependencies = {
|
|
|
5159
5164
|
};
|
|
5160
5165
|
const handleResponses = async (c) => {
|
|
5161
5166
|
const payload = await c.req.json();
|
|
5167
|
+
const requestedModel = payload.model;
|
|
5168
|
+
payload.model = resolveMappedModel(payload.model);
|
|
5169
|
+
if (payload.model !== requestedModel) logger$1.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
|
|
5162
5170
|
const providerModelAlias = parseProviderModelAlias(payload.model);
|
|
5163
5171
|
if (providerModelAlias) {
|
|
5164
5172
|
payload.model = providerModelAlias.model;
|
|
@@ -5399,4 +5407,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
5399
5407
|
//#endregion
|
|
5400
5408
|
export { server };
|
|
5401
5409
|
|
|
5402
|
-
//# sourceMappingURL=server-
|
|
5410
|
+
//# sourceMappingURL=server-DoSH0Hby.js.map
|