@jeffreycao/copilot-api 1.10.9 → 1.10.10

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 (33) hide show
  1. package/README.md +16 -13
  2. package/README.zh-CN.md +16 -13
  3. package/dist/auth-CeZtnQE_.js +116 -0
  4. package/dist/auth-CeZtnQE_.js.map +1 -0
  5. package/dist/{check-usage-BdXGp1Wr.js → check-usage-CTShKCvR.js} +3 -4
  6. package/dist/{check-usage-BdXGp1Wr.js.map → check-usage-CTShKCvR.js.map} +1 -1
  7. package/dist/{proxy-DvlF9a-7.js → config-ztdkLu9o.js} +83 -70
  8. package/dist/config-ztdkLu9o.js.map +1 -0
  9. package/dist/{debug-C_TBkyUw.js → debug-BVHmoCzY.js} +17 -7
  10. package/dist/debug-BVHmoCzY.js.map +1 -0
  11. package/dist/main.js +5 -5
  12. package/dist/{mcp-CTb-DbQH.js → mcp-DZgcvqQY.js} +2 -2
  13. package/dist/{mcp-CTb-DbQH.js.map → mcp-DZgcvqQY.js.map} +1 -1
  14. package/dist/{server-FPXzFkg9.js → server-D5O9IzAY.js} +1773 -1692
  15. package/dist/server-D5O9IzAY.js.map +1 -0
  16. package/dist/{start-CbKg_0bY.js → start-BhPxHgu8.js} +4 -6
  17. package/dist/{start-CbKg_0bY.js.map → start-BhPxHgu8.js.map} +1 -1
  18. package/dist/token-1SfgxCRm.js +1875 -0
  19. package/dist/token-1SfgxCRm.js.map +1 -0
  20. package/dist/{tool-search-D3SN0jX-.js → tool-search-wA-fLduL.js} +1 -1
  21. package/dist/{tool-search-D3SN0jX-.js.map → tool-search-wA-fLduL.js.map} +1 -1
  22. package/package.json +2 -2
  23. package/dist/auth-BHa2OHXf.js +0 -45
  24. package/dist/auth-BHa2OHXf.js.map +0 -1
  25. package/dist/debug-C_TBkyUw.js.map +0 -1
  26. package/dist/paths-DC-mqCY3.js +0 -30
  27. package/dist/paths-DC-mqCY3.js.map +0 -1
  28. package/dist/proxy-DvlF9a-7.js.map +0 -1
  29. package/dist/server-FPXzFkg9.js.map +0 -1
  30. package/dist/token-Dj8XsAxn.js +0 -170
  31. package/dist/token-Dj8XsAxn.js.map +0 -1
  32. package/dist/utils-jHLgqAq2.js +0 -657
  33. package/dist/utils-jHLgqAq2.js.map +0 -1
package/README.md CHANGED
@@ -372,11 +372,11 @@ The following command line options are available for the `start` command:
372
372
  - **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/*`.
373
373
  - **modelMappings:** Exact `sourceModel -> targetModel` rewrites for top-level `POST /v1/messages` and `POST /v1/messages/count_tokens` 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. The admin endpoints `GET/POST /admin/config/model-mappings` read and update only this field.
374
374
  - **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.
375
- - **providers:** Global upstream provider map. Each provider key (for example `custom`) becomes a route prefix (`/custom/v1/messages`). Supports `type: "anthropic"` and `type: "openai-compatible"`. Top-level Anthropic clients can also use `model: "custom/model-id"` with `/v1/messages` and `/v1/messages/count_tokens`; the proxy strips the `custom/` prefix before forwarding upstream. `GET /v1/models` does not aggregate provider models; use `GET /custom/v1/models` for provider model lists.
375
+ - **providers:** Global upstream provider map. Each provider key (for example `custom`) becomes a route prefix (`/custom/v1/messages`). Supports `type: "anthropic"`, `type: "openai-compatible"`, and `type: "openai-responses"`. Top-level clients can also use `model: "custom/model-id"` with `/v1/messages`, `/v1/messages/count_tokens`, and `/v1/responses`; the proxy strips the `custom/` prefix before forwarding upstream. `GET /v1/models` does not aggregate provider models; use `GET /custom/v1/models` for provider model lists.
376
376
  - `enabled` defaults to `true` if omitted.
377
- - `baseUrl` should be provider API base URL without the final endpoint. For Anthropic providers, omit `/v1/messages`; for OpenAI-compatible providers, omit `/v1/chat/completions`.
378
- - `apiKey` is used as the upstream credential value.
379
- - `authType` (optional): Controls how `apiKey` is sent upstream. Supports `x-api-key` and `authorization`. Anthropic providers default to `x-api-key`; OpenAI-compatible providers default to `authorization`. When set to `authorization`, the proxy sends `Authorization: Bearer <apiKey>`.
377
+ - `baseUrl` should be provider API base URL without the final endpoint. For Anthropic providers, omit `/v1/messages`; for OpenAI-compatible providers, omit `/v1/chat/completions`; for OpenAI Responses providers, omit `/v1/responses`.
378
+ - `apiKey` is used as the upstream credential value and is required for regular providers.
379
+ - `authType` (optional): Controls how `apiKey` is sent upstream. Supports `x-api-key` and `authorization` for regular providers. Anthropic providers default to `x-api-key`; OpenAI-compatible and OpenAI Responses providers default to `authorization`. When set to `authorization`, the proxy sends `Authorization: Bearer <apiKey>`. `oauth2` is reserved for the built-in `codex` provider and is written automatically by `auth login --provider codex`.
380
380
  - `adjustInputTokens` (optional): When `true`, the proxy will adjust the `input_tokens` in the usage response by subtracting `cache_read_input_tokens` and `cache_creation_input_tokens`.
381
381
  - `models` (optional): Per-model configuration map. Each key is a model ID (matching the model name in requests), and the value is:
382
382
  - `temperature` (optional): Default temperature value used when the request does not specify one.
@@ -431,7 +431,7 @@ These endpoints mimic the OpenAI API structure.
431
431
 
432
432
  | Endpoint | Method | Description |
433
433
  | --------------------------- | ------ | ---------------------------------------------------------------- |
434
- | `POST /v1/responses` | `POST` | OpenAI Most advanced interface for generating model responses. |
434
+ | `POST /v1/responses` | `POST` | OpenAI Most advanced interface for generating model responses. Supports `provider/model` aliases for `openai-responses` providers. |
435
435
  | `POST /v1/chat/completions` | `POST` | Creates a model response for the given chat conversation. |
436
436
  | `GET /v1/models` | `GET` | Lists the currently available models. |
437
437
  | `POST /v1/embeddings` | `POST` | Creates an embedding vector representing the input text. |
@@ -444,7 +444,7 @@ These endpoints are designed to be compatible with the Anthropic Messages API.
444
444
  | -------------------------------- | ------ | ------------------------------------------------------------ |
445
445
  | `POST /v1/messages` | `POST` | Creates a model response for a given conversation. Supports `provider/model` aliases for configured providers. |
446
446
  | `POST /v1/messages/count_tokens` | `POST` | Calculates the number of tokens for a given set of messages. Supports `provider/model` aliases for configured providers. |
447
- | `POST /:provider/v1/messages` | `POST` | Proxies Anthropic Messages requests to the configured Anthropic or OpenAI-compatible provider. |
447
+ | `POST /:provider/v1/messages` | `POST` | Proxies Anthropic Messages requests to the configured Anthropic, OpenAI-compatible, or OpenAI Responses provider. |
448
448
  | `GET /:provider/v1/models` | `GET` | Proxies model listing requests to the configured provider. |
449
449
  | `POST /:provider/v1/messages/count_tokens` | `POST` | Calculates tokens locally for provider route requests. |
450
450
 
@@ -495,11 +495,14 @@ npx @jeffreycao/copilot-api@latest start --rate-limit 30 --wait
495
495
  # Provide GitHub token directly
496
496
  npx @jeffreycao/copilot-api@latest start --github-token ghp_YOUR_TOKEN_HERE
497
497
 
498
- # Run only the auth flow
499
- npx @jeffreycao/copilot-api@latest auth
498
+ # Run only the auth flow and choose a builtin provider interactively
499
+ npx @jeffreycao/copilot-api@latest auth login
500
+
501
+ # Authenticate Codex explicitly
502
+ npx @jeffreycao/copilot-api@latest auth login --provider codex
500
503
 
501
- # Run auth flow with verbose logging
502
- npx @jeffreycao/copilot-api@latest auth --verbose
504
+ # Authenticate Copilot explicitly with verbose logging
505
+ npx @jeffreycao/copilot-api@latest auth login --provider copilot --verbose
503
506
 
504
507
  # Show your Copilot usage/quota in the terminal (no server needed)
505
508
  npx @jeffreycao/copilot-api@latest check-usage
@@ -734,7 +737,7 @@ The Claude Code integration is packaged as two plugins:
734
737
  - `tool-search` registers the `tool_search` MCP bridge used for GPT Responses deferred tool loading.
735
738
 
736
739
  - Marketplace catalog in this repository: `.claude-plugin/marketplace.json`
737
- - Plugin sources in this repository: `claude-plugin/agent-inject`, `claude-plugin/tool-search`
740
+ - Plugin sources in this repository: `plugin/claude/agent-inject`, `plugin/claude/tool-search`
738
741
 
739
742
  Add the marketplace remotely:
740
743
 
@@ -760,7 +763,7 @@ The `tool-search` plugin bundles the same MCP bridge described in [GPT Tool Sear
760
763
 
761
764
  #### Opencode plugin
762
765
 
763
- The subagent marker producer is packaged as an opencode plugin located at `.opencode/plugins/subagent-marker.js`.
766
+ The subagent marker producer is packaged as an opencode plugin located at `plugin/opencode/subagent-marker.js`.
764
767
 
765
768
  **Installation:**
766
769
 
@@ -768,7 +771,7 @@ Copy the plugin file to your opencode plugins directory:
768
771
 
769
772
  ```sh
770
773
  # Clone or download this repository, then copy the plugin
771
- cp .opencode/plugins/subagent-marker.js ~/.config/opencode/plugins/
774
+ cp plugin/opencode/subagent-marker.js ~/.config/opencode/plugins/
772
775
  ```
773
776
 
774
777
  Or manually create the file at `~/.config/opencode/plugins/subagent-marker.js` with the plugin content.
package/README.zh-CN.md CHANGED
@@ -376,11 +376,11 @@ Copilot API 现在使用子命令结构,主要命令包括:
376
376
  - **auth.adminApiKey:** 仅用于 `/admin/*` 路由的单个 admin key。若未配置,服务会在启动时自动生成一个随机 key,并回写到 `config.json`。它同样使用 `x-api-key` 或 `Authorization: Bearer` 这两种头,但普通 `auth.apiKeys` 不能访问 `/admin/*`。
377
377
  - **modelMappings:** 用于顶层 `POST /v1/messages` 和 `POST /v1/messages/count_tokens` 请求的精确 `sourceModel -> targetModel` 重写映射。省略该字段或保留为 `{}` 时,不会做模型重写。`source` 和 `target` 都必须是非空字符串。`target` 可以是普通模型 ID,也可以是 `provider/model` 形式的别名,例如 `dashscope/qwen3.6-plus`;重写发生在 provider alias 解析之前。`GET/POST /admin/config/model-mappings` 管理接口读写的也只有这个字段。
378
378
  - **extraPrompts:** `model -> prompt` 的映射。把 Anthropic 风格请求翻译给 Copilot 时,会将其附加到第一条 system prompt 后面。你可以借此为不同模型注入护栏或指引。缺失的默认项会自动补齐,但不会覆盖你自定义的 prompt。内置的 `gpt-5.3-codex` 和 `gpt-5.4` prompt 会启用带阶段感知的 commentary,让模型在工具调用或更深层推理前先发出简短的用户可见进度说明。
379
- - **providers:** 全局上游 provider 映射。每个 provider key(例如 `custom`)都会变成一个路由前缀(`/custom/v1/messages`)。支持 `type: "anthropic"` 和 `type: "openai-compatible"`。顶层 Anthropic 客户端也可以在 `/v1/messages` 和 `/v1/messages/count_tokens` 中使用 `model: "custom/model-id"`;代理会在转发上游前移除 `custom/` 前缀。`GET /v1/models` 不聚合 provider 模型;provider 模型列表请使用 `GET /custom/v1/models`。
379
+ - **providers:** 全局上游 provider 映射。每个 provider key(例如 `custom`)都会变成一个路由前缀(`/custom/v1/messages`)。支持 `type: "anthropic"`、`type: "openai-compatible"` 和 `type: "openai-responses"`。顶层客户端也可以在 `/v1/messages`、`/v1/messages/count_tokens` 和 `/v1/responses` 中使用 `model: "custom/model-id"`;代理会在转发上游前移除 `custom/` 前缀。`GET /v1/models` 不聚合 provider 模型;provider 模型列表请使用 `GET /custom/v1/models`。
380
380
  - `enabled`:可选,若省略则默认为 `true`。
381
- - `baseUrl`:provider API 的基础 URL,不要带结尾的 endpoint。Anthropic provider 不要带 `/v1/messages`;OpenAI 兼容 provider 不要带 `/v1/chat/completions`。
382
- - `apiKey`:作为上游凭据值使用。
383
- - `authType`:可选,控制 `apiKey` 如何发送到上游。支持 `x-api-key` 和 `authorization`。Anthropic provider 默认 `x-api-key`;OpenAI 兼容 provider 默认 `authorization`。当设置为 `authorization` 时,代理会发送 `Authorization: Bearer <apiKey>`。
381
+ - `baseUrl`:provider API 的基础 URL,不要带结尾的 endpoint。Anthropic provider 不要带 `/v1/messages`;OpenAI 兼容 provider 不要带 `/v1/chat/completions`;OpenAI Responses provider 不要带 `/v1/responses`。
382
+ - `apiKey`:作为上游凭据值使用;普通 provider 必须配置。
383
+ - `authType`:可选,控制 `apiKey` 如何发送到上游。普通 provider 支持 `x-api-key` 和 `authorization`。Anthropic provider 默认 `x-api-key`;OpenAI 兼容和 OpenAI Responses provider 默认 `authorization`。当设置为 `authorization` 时,代理会发送 `Authorization: Bearer <apiKey>`。`oauth2` 仅保留给内置 `codex` provider,并由 `auth login --provider codex` 自动写入。
384
384
  - `adjustInputTokens`:可选,当为 `true` 时,代理会在 usage 响应里用 `input_tokens` 减去 `cache_read_input_tokens` 和 `cache_creation_input_tokens`。
385
385
  - `models`:可选,按模型 ID 配置的映射。每个键为请求中的模型名,值支持:
386
386
  - `temperature`:可选,当请求未指定时使用的默认温度。
@@ -435,7 +435,7 @@ curl http://localhost:4141/admin/config/model-mappings \
435
435
 
436
436
  | 端点 | 方法 | 说明 |
437
437
  | --- | --- | --- |
438
- | `POST /v1/responses` | `POST` | OpenAI 中用于生成模型响应的高级接口。 |
438
+ | `POST /v1/responses` | `POST` | OpenAI 中用于生成模型响应的高级接口。支持 `openai-responses` provider 的 `provider/model` 别名。 |
439
439
  | `POST /v1/chat/completions` | `POST` | 为给定聊天对话创建模型响应。 |
440
440
  | `GET /v1/models` | `GET` | 列出当前可用模型。 |
441
441
  | `POST /v1/embeddings` | `POST` | 创建表示输入文本的向量嵌入。 |
@@ -448,7 +448,7 @@ curl http://localhost:4141/admin/config/model-mappings \
448
448
  | --- | --- | --- |
449
449
  | `POST /v1/messages` | `POST` | 为给定对话创建模型响应。支持已配置 provider 的 `provider/model` 别名。 |
450
450
  | `POST /v1/messages/count_tokens` | `POST` | 计算一组消息的 token 数。支持已配置 provider 的 `provider/model` 别名。 |
451
- | `POST /:provider/v1/messages` | `POST` | 将 Anthropic Messages 请求代理到已配置的 Anthropic OpenAI 兼容 provider。 |
451
+ | `POST /:provider/v1/messages` | `POST` | 将 Anthropic Messages 请求代理到已配置的 Anthropic、OpenAI 兼容或 OpenAI Responses provider。 |
452
452
  | `GET /:provider/v1/models` | `GET` | 将模型列表请求代理到已配置的 provider。 |
453
453
  | `POST /:provider/v1/messages/count_tokens` | `POST` | 为 provider 路由请求在本地计算 token 数。 |
454
454
 
@@ -499,11 +499,14 @@ npx @jeffreycao/copilot-api@latest start --rate-limit 30 --wait
499
499
  # 直接传入 GitHub token
500
500
  npx @jeffreycao/copilot-api@latest start --github-token ghp_YOUR_TOKEN_HERE
501
501
 
502
- # 仅执行认证流程
503
- npx @jeffreycao/copilot-api@latest auth
502
+ # 仅执行认证流程,并交互式选择内置 provider
503
+ npx @jeffreycao/copilot-api@latest auth login
504
+
505
+ # 显式登录 Codex
506
+ npx @jeffreycao/copilot-api@latest auth login --provider codex
504
507
 
505
- # 认证时启用详细日志
506
- npx @jeffreycao/copilot-api@latest auth --verbose
508
+ # 显式登录 Copilot,并启用详细日志
509
+ npx @jeffreycao/copilot-api@latest auth login --provider copilot --verbose
507
510
 
508
511
  # 在终端中查看 Copilot 用量与额度(无需启动服务)
509
512
  npx @jeffreycao/copilot-api@latest check-usage
@@ -742,7 +745,7 @@ Claude Code 集成现在拆分为两个插件:
742
745
  - `tool-search` 会注册用于 GPT Responses deferred tool loading 的 `tool_search` MCP bridge。
743
746
 
744
747
  - 本仓库中的 marketplace catalog:`.claude-plugin/marketplace.json`
745
- - 本仓库中的插件源码:`claude-plugin/agent-inject`、`claude-plugin/tool-search`
748
+ - 本仓库中的插件源码:`plugin/claude/agent-inject`、`plugin/claude/tool-search`
746
749
 
747
750
  远程添加 marketplace:
748
751
 
@@ -768,7 +771,7 @@ Claude Code 集成现在拆分为两个插件:
768
771
 
769
772
  #### Opencode 插件
770
773
 
771
- subagent 标记生成器被打包为一个 opencode 插件,位于 `.opencode/plugins/subagent-marker.js`。
774
+ subagent 标记生成器被打包为一个 opencode 插件,位于 `plugin/opencode/subagent-marker.js`。
772
775
 
773
776
  **安装方式:**
774
777
 
@@ -776,7 +779,7 @@ subagent 标记生成器被打包为一个 opencode 插件,位于 `.opencode/p
776
779
 
777
780
  ```sh
778
781
  # 克隆或下载本仓库后复制该插件
779
- cp .opencode/plugins/subagent-marker.js ~/.config/opencode/plugins/
782
+ cp plugin/opencode/subagent-marker.js ~/.config/opencode/plugins/
780
783
  ```
781
784
 
782
785
  或者手动在 `~/.config/opencode/plugins/subagent-marker.js` 创建该文件,并填入插件内容。
@@ -0,0 +1,116 @@
1
+ import { H as state, M as loginCodex, a as setupGitHubToken, n as persistCodexCredentials } from "./token-1SfgxCRm.js";
2
+ import { b as ensurePaths, y as PATHS } from "./config-ztdkLu9o.js";
3
+ import { defineCommand } from "citty";
4
+ import consola from "consola";
5
+ //#region src/auth.ts
6
+ const authArgs = {
7
+ provider: {
8
+ type: "string",
9
+ description: "Provider to log in with (copilot or codex)"
10
+ },
11
+ verbose: {
12
+ alias: "v",
13
+ type: "boolean",
14
+ default: false,
15
+ description: "Enable verbose logging"
16
+ },
17
+ "show-token": {
18
+ type: "boolean",
19
+ default: false,
20
+ description: "Show provider access token on auth"
21
+ }
22
+ };
23
+ const BUILTIN_PROVIDER_NAMES = ["copilot", "codex"];
24
+ const BUILTIN_PROVIDER_LABELS = {
25
+ copilot: "GitHub Copilot",
26
+ codex: "OpenAI Codex"
27
+ };
28
+ function isBuiltinProviderName(providerName) {
29
+ return BUILTIN_PROVIDER_NAMES.includes(providerName);
30
+ }
31
+ async function resolveProviderSelection(providerArg) {
32
+ const availableProviders = [...BUILTIN_PROVIDER_NAMES];
33
+ if (providerArg !== void 0) {
34
+ const providerName = providerArg.trim();
35
+ if (!isBuiltinProviderName(providerName)) throw new Error(`Unknown provider '${providerArg}'. Expected one of: ${availableProviders.join(", ")}`);
36
+ return providerName;
37
+ }
38
+ if (availableProviders.length === 1) return availableProviders[0];
39
+ const provider = await consola.prompt("Select a provider to log in with", {
40
+ type: "select",
41
+ options: availableProviders.map((providerName) => ({
42
+ label: `${BUILTIN_PROVIDER_LABELS[providerName]} (${providerName})`,
43
+ value: providerName
44
+ }))
45
+ });
46
+ if (!provider || !isBuiltinProviderName(provider)) throw new Error("No provider selected");
47
+ return provider;
48
+ }
49
+ async function loginWithCodex() {
50
+ await persistCodexCredentials(await loginCodex({
51
+ onAuth(info) {
52
+ consola.info("Open the following URL to authenticate with Codex:");
53
+ consola.log(info.url);
54
+ if (info.instructions) consola.info(info.instructions);
55
+ },
56
+ onPrompt(message) {
57
+ return consola.prompt(message, { type: "text" });
58
+ },
59
+ onProgress(message) {
60
+ consola.debug(message);
61
+ }
62
+ }), { enableProvider: true });
63
+ consola.success(`Codex provider config written to ${PATHS.CONFIG_PATH} and credentials written to ${PATHS.CODEX_CREDENTIAL_PATH}`);
64
+ }
65
+ async function loginWithProvider(provider) {
66
+ if (provider === "copilot") {
67
+ await setupGitHubToken({ force: true });
68
+ consola.success("GitHub token written to", PATHS.GITHUB_TOKEN_PATH);
69
+ return;
70
+ }
71
+ await loginWithCodex();
72
+ }
73
+ async function runAuthLogin(options) {
74
+ if (options.verbose) {
75
+ consola.level = 5;
76
+ consola.info("Verbose logging enabled");
77
+ }
78
+ state.showToken = options.showToken;
79
+ await ensurePaths();
80
+ const provider = await resolveProviderSelection(options.provider);
81
+ consola.info(`Logging in with ${BUILTIN_PROVIDER_LABELS[provider]}`);
82
+ await loginWithProvider(provider);
83
+ }
84
+ const auth = defineCommand({
85
+ meta: {
86
+ name: "auth",
87
+ description: "Run authentication flows without running the server"
88
+ },
89
+ args: authArgs,
90
+ subCommands: { login: defineCommand({
91
+ meta: {
92
+ name: "login",
93
+ description: "Authenticate a builtin provider without running the server"
94
+ },
95
+ args: authArgs,
96
+ run({ args }) {
97
+ return runAuthLogin({
98
+ provider: args.provider,
99
+ verbose: args.verbose,
100
+ showToken: args["show-token"]
101
+ });
102
+ }
103
+ }) },
104
+ run({ args }) {
105
+ if ((args._[0] ?? "").trim()) return;
106
+ return runAuthLogin({
107
+ provider: args.provider,
108
+ verbose: args.verbose,
109
+ showToken: args["show-token"]
110
+ });
111
+ }
112
+ });
113
+ //#endregion
114
+ export { auth };
115
+
116
+ //# sourceMappingURL=auth-CeZtnQE_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-CeZtnQE_.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { loginCodex } from \"./lib/oauth/codex\"\nimport { PATHS, ensurePaths } from \"./lib/paths\"\nimport { state } from \"./lib/state\"\nimport { persistCodexCredentials, setupGitHubToken } from \"./lib/token\"\n\ninterface RunAuthOptions {\n provider?: string\n verbose: boolean\n showToken: boolean\n}\n\nconst authArgs = {\n provider: {\n type: \"string\",\n description: \"Provider to log in with (copilot or codex)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show provider access token on auth\",\n },\n} as const\n\nconst BUILTIN_PROVIDER_NAMES = [\"copilot\", \"codex\"] as const\n\ntype BuiltinProviderName = (typeof BUILTIN_PROVIDER_NAMES)[number]\n\nconst BUILTIN_PROVIDER_LABELS: Record<BuiltinProviderName, string> = {\n copilot: \"GitHub Copilot\",\n codex: \"OpenAI Codex\",\n}\n\nfunction isBuiltinProviderName(\n providerName: string,\n): providerName is BuiltinProviderName {\n return BUILTIN_PROVIDER_NAMES.includes(providerName as BuiltinProviderName)\n}\n\nasync function resolveProviderSelection(\n providerArg: string | undefined,\n): Promise<BuiltinProviderName> {\n const availableProviders = [...BUILTIN_PROVIDER_NAMES]\n\n if (providerArg !== undefined) {\n const providerName = providerArg.trim()\n if (!isBuiltinProviderName(providerName)) {\n throw new Error(\n `Unknown provider '${providerArg}'. Expected one of: ${availableProviders.join(\", \")}`,\n )\n }\n return providerName\n }\n\n if (availableProviders.length === 1) {\n return availableProviders[0]\n }\n\n const provider = await consola.prompt(\"Select a provider to log in with\", {\n type: \"select\",\n options: availableProviders.map((providerName) => ({\n label: `${BUILTIN_PROVIDER_LABELS[providerName]} (${providerName})`,\n value: providerName,\n })),\n })\n\n if (!provider || !isBuiltinProviderName(provider)) {\n throw new Error(\"No provider selected\")\n }\n\n return provider\n}\n\nasync function loginWithCodex(): Promise<void> {\n const credentials = await loginCodex({\n onAuth(info) {\n consola.info(\"Open the following URL to authenticate with Codex:\")\n consola.log(info.url)\n if (info.instructions) {\n consola.info(info.instructions)\n }\n },\n onPrompt(message) {\n return consola.prompt(message, {\n type: \"text\",\n })\n },\n onProgress(message) {\n consola.debug(message)\n },\n })\n\n await persistCodexCredentials(credentials, { enableProvider: true })\n consola.success(\n `Codex provider config written to ${PATHS.CONFIG_PATH} and credentials written to ${PATHS.CODEX_CREDENTIAL_PATH}`,\n )\n}\n\nasync function loginWithProvider(provider: BuiltinProviderName): Promise<void> {\n if (provider === \"copilot\") {\n await setupGitHubToken({ force: true })\n consola.success(\"GitHub token written to\", PATHS.GITHUB_TOKEN_PATH)\n return\n }\n\n await loginWithCodex()\n}\n\nexport async function runAuthLogin(options: RunAuthOptions): Promise<void> {\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = options.showToken\n\n await ensurePaths()\n const provider = await resolveProviderSelection(options.provider)\n\n consola.info(`Logging in with ${BUILTIN_PROVIDER_LABELS[provider]}`)\n await loginWithProvider(provider)\n}\n\nconst authLogin = defineCommand({\n meta: {\n name: \"login\",\n description: \"Authenticate a builtin provider without running the server\",\n },\n args: authArgs,\n run({ args }) {\n return runAuthLogin({\n provider: args.provider,\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n\nexport const auth = defineCommand({\n meta: {\n name: \"auth\",\n description: \"Run authentication flows without running the server\",\n },\n args: authArgs,\n subCommands: {\n login: authLogin,\n },\n run({ args }) {\n if ((args._[0] ?? \"\").trim()) {\n return\n }\n\n return runAuthLogin({\n provider: args.provider,\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n"],"mappings":";;;;;AAgBA,MAAM,WAAW;CACf,UAAU;EACR,MAAM;EACN,aAAa;EACd;CACD,SAAS;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,aAAa;EACd;CACD,cAAc;EACZ,MAAM;EACN,SAAS;EACT,aAAa;EACd;CACF;AAED,MAAM,yBAAyB,CAAC,WAAW,QAAQ;AAInD,MAAM,0BAA+D;CACnE,SAAS;CACT,OAAO;CACR;AAED,SAAS,sBACP,cACqC;CACrC,OAAO,uBAAuB,SAAS,aAAoC;;AAG7E,eAAe,yBACb,aAC8B;CAC9B,MAAM,qBAAqB,CAAC,GAAG,uBAAuB;CAEtD,IAAI,gBAAgB,KAAA,GAAW;EAC7B,MAAM,eAAe,YAAY,MAAM;EACvC,IAAI,CAAC,sBAAsB,aAAa,EACtC,MAAM,IAAI,MACR,qBAAqB,YAAY,sBAAsB,mBAAmB,KAAK,KAAK,GACrF;EAEH,OAAO;;CAGT,IAAI,mBAAmB,WAAW,GAChC,OAAO,mBAAmB;CAG5B,MAAM,WAAW,MAAM,QAAQ,OAAO,oCAAoC;EACxE,MAAM;EACN,SAAS,mBAAmB,KAAK,kBAAkB;GACjD,OAAO,GAAG,wBAAwB,cAAc,IAAI,aAAa;GACjE,OAAO;GACR,EAAE;EACJ,CAAC;CAEF,IAAI,CAAC,YAAY,CAAC,sBAAsB,SAAS,EAC/C,MAAM,IAAI,MAAM,uBAAuB;CAGzC,OAAO;;AAGT,eAAe,iBAAgC;CAmB7C,MAAM,wBAAwB,MAlBJ,WAAW;EACnC,OAAO,MAAM;GACX,QAAQ,KAAK,qDAAqD;GAClE,QAAQ,IAAI,KAAK,IAAI;GACrB,IAAI,KAAK,cACP,QAAQ,KAAK,KAAK,aAAa;;EAGnC,SAAS,SAAS;GAChB,OAAO,QAAQ,OAAO,SAAS,EAC7B,MAAM,QACP,CAAC;;EAEJ,WAAW,SAAS;GAClB,QAAQ,MAAM,QAAQ;;EAEzB,CAAC,EAEyC,EAAE,gBAAgB,MAAM,CAAC;CACpE,QAAQ,QACN,oCAAoC,MAAM,YAAY,8BAA8B,MAAM,wBAC3F;;AAGH,eAAe,kBAAkB,UAA8C;CAC7E,IAAI,aAAa,WAAW;EAC1B,MAAM,iBAAiB,EAAE,OAAO,MAAM,CAAC;EACvC,QAAQ,QAAQ,2BAA2B,MAAM,kBAAkB;EACnE;;CAGF,MAAM,gBAAgB;;AAGxB,eAAsB,aAAa,SAAwC;CACzE,IAAI,QAAQ,SAAS;EACnB,QAAQ,QAAQ;EAChB,QAAQ,KAAK,0BAA0B;;CAGzC,MAAM,YAAY,QAAQ;CAE1B,MAAM,aAAa;CACnB,MAAM,WAAW,MAAM,yBAAyB,QAAQ,SAAS;CAEjE,QAAQ,KAAK,mBAAmB,wBAAwB,YAAY;CACpE,MAAM,kBAAkB,SAAS;;AAkBnC,MAAa,OAAO,cAAc;CAChC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM;CACN,aAAa,EACX,OAtBc,cAAc;EAC9B,MAAM;GACJ,MAAM;GACN,aAAa;GACd;EACD,MAAM;EACN,IAAI,EAAE,QAAQ;GACZ,OAAO,aAAa;IAClB,UAAU,KAAK;IACf,SAAS,KAAK;IACd,WAAW,KAAK;IACjB,CAAC;;EAEL,CASU,EACR;CACD,IAAI,EAAE,QAAQ;EACZ,KAAK,KAAK,EAAE,MAAM,IAAI,MAAM,EAC1B;EAGF,OAAO,aAAa;GAClB,UAAU,KAAK;GACf,SAAS,KAAK;GACd,WAAW,KAAK;GACjB,CAAC;;CAEL,CAAC"}
@@ -1,6 +1,5 @@
1
- import { n as ensurePaths } from "./paths-DC-mqCY3.js";
2
- import { f as getCopilotUsage } from "./utils-jHLgqAq2.js";
3
- import { r as setupGitHubToken } from "./token-Dj8XsAxn.js";
1
+ import { _ as getCopilotUsage, a as setupGitHubToken } from "./token-1SfgxCRm.js";
2
+ import { b as ensurePaths } from "./config-ztdkLu9o.js";
4
3
  import { defineCommand } from "citty";
5
4
  import consola from "consola";
6
5
  //#region src/check-usage.ts
@@ -40,4 +39,4 @@ const checkUsage = defineCommand({
40
39
  //#endregion
41
40
  export { checkUsage };
42
41
 
43
- //# sourceMappingURL=check-usage-BdXGp1Wr.js.map
42
+ //# sourceMappingURL=check-usage-CTShKCvR.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"check-usage-BdXGp1Wr.js","names":[],"sources":["../src/check-usage.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n"],"mappings":";;;;;;AAUA,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,MAAM;EACV,MAAM,aAAa;EACnB,MAAM,kBAAkB;EACxB,IAAI;GACF,MAAM,QAAQ,MAAM,iBAAiB;GACrC,MAAM,UAAU,MAAM,gBAAgB;GACtC,MAAM,eAAe,QAAQ;GAC7B,MAAM,cAAc,eAAe,QAAQ;GAC3C,MAAM,qBACJ,eAAe,IAAK,cAAc,eAAgB,MAAM;GAC1D,MAAM,0BAA0B,QAAQ;GAGxC,SAAS,eAAe,MAAc,MAA+B;IACnE,IAAI,CAAC,MAAM,OAAO,GAAG,KAAK;IAC1B,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,QAAQ,KAAK;IAC1B,MAAM,cAAc,QAAQ,IAAK,OAAO,QAAS,MAAM;IACvD,MAAM,mBAAmB,KAAK;IAC9B,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,MAAM,SAAS,YAAY,QAAQ,EAAE,CAAC,UAAU,iBAAiB,QAAQ,EAAE,CAAC;;GAGzG,MAAM,cAAc,YAAY,YAAY,GAAG,aAAa,SAAS,mBAAmB,QAAQ,EAAE,CAAC,UAAU,wBAAwB,QAAQ,EAAE,CAAC;GAChJ,MAAM,WAAW,eAAe,QAAQ,MAAM,gBAAgB,KAAK;GACnE,MAAM,kBAAkB,eACtB,eACA,MAAM,gBAAgB,YACvB;GAED,QAAQ,IACN,wBAAwB,MAAM,aAAa,mBACtB,MAAM,iBAAiB,iBAEnC,YAAY,MACZ,SAAS,MACT,kBACV;WACM,KAAK;GACZ,QAAQ,MAAM,kCAAkC,IAAI;GACpD,QAAQ,KAAK,EAAE;;;CAGpB,CAAC"}
1
+ {"version":3,"file":"check-usage-CTShKCvR.js","names":[],"sources":["../src/check-usage.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n"],"mappings":";;;;;AAUA,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,MAAM;EACV,MAAM,aAAa;EACnB,MAAM,kBAAkB;EACxB,IAAI;GACF,MAAM,QAAQ,MAAM,iBAAiB;GACrC,MAAM,UAAU,MAAM,gBAAgB;GACtC,MAAM,eAAe,QAAQ;GAC7B,MAAM,cAAc,eAAe,QAAQ;GAC3C,MAAM,qBACJ,eAAe,IAAK,cAAc,eAAgB,MAAM;GAC1D,MAAM,0BAA0B,QAAQ;GAGxC,SAAS,eAAe,MAAc,MAA+B;IACnE,IAAI,CAAC,MAAM,OAAO,GAAG,KAAK;IAC1B,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,QAAQ,KAAK;IAC1B,MAAM,cAAc,QAAQ,IAAK,OAAO,QAAS,MAAM;IACvD,MAAM,mBAAmB,KAAK;IAC9B,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,MAAM,SAAS,YAAY,QAAQ,EAAE,CAAC,UAAU,iBAAiB,QAAQ,EAAE,CAAC;;GAGzG,MAAM,cAAc,YAAY,YAAY,GAAG,aAAa,SAAS,mBAAmB,QAAQ,EAAE,CAAC,UAAU,wBAAwB,QAAQ,EAAE,CAAC;GAChJ,MAAM,WAAW,eAAe,QAAQ,MAAM,gBAAgB,KAAK;GACnE,MAAM,kBAAkB,eACtB,eACA,MAAM,gBAAgB,YACvB;GAED,QAAQ,IACN,wBAAwB,MAAM,aAAa,mBACtB,MAAM,iBAAiB,iBAEnC,YAAY,MACZ,SAAS,MACT,kBACV;WACM,KAAK;GACZ,QAAQ,MAAM,kCAAkC,IAAI;GACpD,QAAQ,KAAK,EAAE;;;CAGpB,CAAC"}
@@ -1,9 +1,34 @@
1
- import { t as PATHS } from "./paths-DC-mqCY3.js";
2
1
  import consola from "consola";
3
2
  import { randomBytes } from "node:crypto";
4
3
  import fs from "node:fs";
5
- import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
6
- import { getProxyForUrl } from "proxy-from-env";
4
+ import fs$1 from "node:fs/promises";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ //#region src/lib/paths.ts
8
+ const AUTH_APP = process.env.COPILOT_API_OAUTH_APP?.trim() || "";
9
+ const ENTERPRISE_PREFIX = process.env.COPILOT_API_ENTERPRISE_URL ? "ent_" : "";
10
+ const DEFAULT_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
11
+ const APP_DIR = process.env.COPILOT_API_HOME || DEFAULT_DIR;
12
+ const PATHS = {
13
+ APP_DIR,
14
+ GITHUB_TOKEN_PATH: path.join(APP_DIR, AUTH_APP, ENTERPRISE_PREFIX + "github_token"),
15
+ CODEX_CREDENTIAL_PATH: path.join(APP_DIR, "codex_credentials.json"),
16
+ CONFIG_PATH: path.join(APP_DIR, "config.json")
17
+ };
18
+ async function ensurePaths() {
19
+ await fs$1.mkdir(path.join(PATHS.APP_DIR, AUTH_APP), { recursive: true });
20
+ await ensureFile(PATHS.GITHUB_TOKEN_PATH);
21
+ await ensureFile(PATHS.CONFIG_PATH);
22
+ }
23
+ async function ensureFile(filePath) {
24
+ try {
25
+ await fs$1.access(filePath, fs$1.constants.W_OK);
26
+ } catch {
27
+ await fs$1.writeFile(filePath, "");
28
+ await fs$1.chmod(filePath, 384);
29
+ }
30
+ }
31
+ //#endregion
7
32
  //#region src/lib/config.ts
8
33
  const gpt5ExplorationPrompt = `## Exploration and reading files
9
34
  - **Think first.** Before any tool call, decide ALL files/resources you will need.
@@ -154,7 +179,7 @@ function ensureAdminApiKey(config) {
154
179
  mergedConfig: {
155
180
  ...config,
156
181
  auth: {
157
- ...config.auth ?? {},
182
+ ...config.auth,
158
183
  adminApiKey: normalizedAdminApiKey
159
184
  }
160
185
  },
@@ -165,7 +190,7 @@ function ensureAdminApiKey(config) {
165
190
  const { mergedConfig } = mergeDefaultConfig({
166
191
  ...editableConfig,
167
192
  auth: {
168
- ...editableConfig.auth ?? {},
193
+ ...editableConfig.auth,
169
194
  adminApiKey: generateAdminApiKey()
170
195
  }
171
196
  });
@@ -198,7 +223,7 @@ function getExtraPromptForModel(model) {
198
223
  }
199
224
  function getModelMappings() {
200
225
  const modelMappings = getConfig().modelMappings;
201
- if (!modelMappings) return { ...defaultConfig.modelMappings ?? {} };
226
+ if (!modelMappings) return { ...defaultConfig.modelMappings };
202
227
  const validMappings = {};
203
228
  for (const [sourceModel, targetModel] of Object.entries(modelMappings)) {
204
229
  if (!sourceModel || typeof targetModel !== "string" || targetModel.length === 0) continue;
@@ -241,31 +266,65 @@ function normalizeProviderBaseUrl(url) {
241
266
  return url.trim().replace(/\/+$/u, "");
242
267
  }
243
268
  function getDefaultProviderAuthType(providerType) {
244
- return providerType === "openai-compatible" ? "authorization" : "x-api-key";
269
+ return providerType === "anthropic" ? "x-api-key" : "authorization";
245
270
  }
246
271
  function resolveProviderAuthType(providerName, authType, providerType) {
247
- if (authType === void 0) return getDefaultProviderAuthType(providerType);
272
+ const defaultAuthType = getDefaultProviderAuthType(providerType);
273
+ if (authType === void 0) return defaultAuthType;
248
274
  if (authType === "x-api-key") return "x-api-key";
275
+ if (authType === "oauth2") {
276
+ if (providerName === "codex") return authType;
277
+ consola.warn(`Provider ${providerName} has authType 'oauth2', which is only supported by the builtin codex provider, falling back to ${defaultAuthType}`);
278
+ return defaultAuthType;
279
+ }
249
280
  if (authType === "authorization") return authType;
250
- consola.warn(`Provider ${providerName} has invalid authType '${authType}', falling back to ${getDefaultProviderAuthType(providerType)}`);
251
- return getDefaultProviderAuthType(providerType);
281
+ consola.warn(`Provider ${providerName} has invalid authType '${authType}', falling back to ${defaultAuthType}`);
282
+ return defaultAuthType;
283
+ }
284
+ function isProviderApiKeyRequired(providerName, authType) {
285
+ return !(providerName === "codex" && authType === "oauth2");
286
+ }
287
+ function getRawProviderConfig(name) {
288
+ const providerName = name.trim();
289
+ if (!providerName) return null;
290
+ return getConfig().providers?.[providerName] ?? null;
291
+ }
292
+ function setProviderConfig(name, provider) {
293
+ const providerName = name.trim();
294
+ if (!providerName) throw new Error("Provider name must be a non-empty string");
295
+ if (isReservedProviderName(providerName)) throw new Error(`Provider ${providerName} is reserved and cannot be configured in config.providers`);
296
+ const editableConfig = readEditableConfigFromDisk();
297
+ writeConfigToDisk({
298
+ ...editableConfig,
299
+ providers: {
300
+ ...editableConfig.providers,
301
+ [providerName]: provider
302
+ }
303
+ });
304
+ cachedConfig = reloadConfig();
305
+ return getRawProviderConfig(providerName) ?? provider;
252
306
  }
253
307
  function getProviderConfig(name) {
254
308
  const providerName = name.trim();
255
309
  if (!providerName) return null;
256
- const provider = getConfig().providers?.[providerName];
310
+ if (isReservedProviderName(providerName)) {
311
+ consola.warn(`Provider ${providerName} is reserved and cannot be configured in config.providers`);
312
+ return null;
313
+ }
314
+ const provider = getRawProviderConfig(providerName);
257
315
  if (!provider) return null;
258
316
  if (provider.enabled === false) return null;
259
317
  const type = provider.type ?? "anthropic";
260
- if (type !== "anthropic" && type !== "openai-compatible") {
318
+ if (type !== "anthropic" && type !== "openai-compatible" && type !== "openai-responses") {
261
319
  consola.warn(`Provider ${providerName} is ignored because type '${type}' is not supported`);
262
320
  return null;
263
321
  }
264
322
  const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? "");
265
- const apiKey = (provider.apiKey ?? "").trim();
266
323
  const authType = resolveProviderAuthType(providerName, provider.authType, type);
267
- if (!baseUrl || !apiKey) {
268
- consola.warn(`Provider ${providerName} is enabled but missing baseUrl or apiKey`);
324
+ const apiKey = (provider.apiKey ?? "").trim();
325
+ const missingFields = [...!baseUrl ? ["baseUrl"] : [], ...isProviderApiKeyRequired(providerName, authType) && !apiKey ? ["apiKey"] : []];
326
+ if (missingFields.length > 0) {
327
+ consola.warn(`Provider ${providerName} is enabled but missing ${missingFields.join(" or ")}`);
269
328
  return null;
270
329
  }
271
330
  return {
@@ -278,6 +337,13 @@ function getProviderConfig(name) {
278
337
  adjustInputTokens: provider.adjustInputTokens
279
338
  };
280
339
  }
340
+ function listEnabledProviders() {
341
+ const config = getConfig();
342
+ return Object.keys(config.providers ?? {}).filter((name) => getProviderConfig(name) !== null);
343
+ }
344
+ function isReservedProviderName(name) {
345
+ return name.trim() === "copilot";
346
+ }
281
347
  function isMessagesApiEnabled() {
282
348
  return getConfig().useMessagesApi ?? true;
283
349
  }
@@ -294,59 +360,6 @@ function getClaudeTokenMultiplier() {
294
360
  return getConfig().claudeTokenMultiplier ?? 1.15;
295
361
  }
296
362
  //#endregion
297
- //#region src/lib/proxy.ts
298
- let proxyEnvDispatcher;
299
- function getProxyEnvDispatcher() {
300
- return proxyEnvDispatcher;
301
- }
302
- function initProxyFromEnv() {
303
- try {
304
- const direct = new Agent();
305
- const proxies = /* @__PURE__ */ new Map();
306
- proxyEnvDispatcher = {
307
- dispatch(options, handler) {
308
- try {
309
- const origin = typeof options.origin === "string" ? new URL(options.origin) : options.origin;
310
- const raw = getProxyForUrl(origin.toString());
311
- const proxyUrl = raw && raw.length > 0 ? raw : void 0;
312
- if (!proxyUrl) {
313
- consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
314
- return direct.dispatch(options, handler);
315
- }
316
- let agent = proxies.get(proxyUrl);
317
- if (!agent) {
318
- agent = new ProxyAgent(proxyUrl);
319
- proxies.set(proxyUrl, agent);
320
- }
321
- let label = proxyUrl;
322
- try {
323
- const u = new URL(proxyUrl);
324
- label = `${u.protocol}//${u.host}`;
325
- } catch {}
326
- consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`);
327
- return agent.dispatch(options, handler);
328
- } catch {
329
- return direct.dispatch(options, handler);
330
- }
331
- },
332
- close() {
333
- return direct.close();
334
- },
335
- destroy() {
336
- return direct.destroy();
337
- }
338
- };
339
- if (typeof Bun !== "undefined") {
340
- consola.debug("WebSocket proxy configured from environment (per-URL)");
341
- return;
342
- }
343
- setGlobalDispatcher(proxyEnvDispatcher);
344
- consola.debug("HTTP proxy configured from environment (per-URL)");
345
- } catch (err) {
346
- consola.debug("Proxy setup skipped:", err);
347
- }
348
- }
349
- //#endregion
350
- export { setModelMappings as _, getConfig as a, getProviderConfig as c, isMessagesApiEnabled as d, isResponsesApiContextManagementModel as f, resolveMappedModel as g, mergeConfigWithDefaults as h, getClaudeTokenMultiplier as i, getReasoningEffortForModel as l, isResponsesApiWebSocketEnabled as m, initProxyFromEnv as n, getExtraPromptForModel as o, isResponsesApiWebSearchEnabled as p, getAnthropicApiKey as r, getModelMappings as s, getProxyEnvDispatcher as t, getSmallModel as u };
363
+ export { setModelMappings as _, getModelMappings as a, ensurePaths as b, getReasoningEffortForModel as c, isResponsesApiContextManagementModel 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 };
351
364
 
352
- //# sourceMappingURL=proxy-DvlF9a-7.js.map
365
+ //# sourceMappingURL=config-ztdkLu9o.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-ztdkLu9o.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 responsesApiContextManagementModels?: Array<string>\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 responsesApiContextManagementModels: [],\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 getResponsesApiContextManagementModels(): Array<string> {\n const config = getConfig()\n return (\n config.responsesApiContextManagementModels\n ?? defaultConfig.responsesApiContextManagementModels\n ?? []\n )\n}\n\nexport function isResponsesApiContextManagementModel(model: string): boolean {\n return getResponsesApiContextManagementModels().includes(model)\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":";;;;;;;AAIA,MAAM,WAAW,QAAQ,IAAI,uBAAuB,MAAM,IAAI;AAC9D,MAAM,oBAAoB,QAAQ,IAAI,6BAA6B,SAAS;AAE5E,MAAM,cAAc,KAAK,KAAK,GAAG,SAAS,EAAE,UAAU,SAAS,cAAc;AAC7E,MAAM,UAAU,QAAQ,IAAI,oBAAoB;AAUhD,MAAa,QAAQ;CACnB;CACA,mBAVwB,KAAK,KAC7B,SACA,UACA,oBAAoB,eAOpB;CACA,uBAN4B,KAAK,KAAK,SAAS,yBAM/C;CACA,aANkB,KAAK,KAAK,SAAS,cAMrC;CACD;AAED,eAAsB,cAA6B;CACjD,MAAMA,KAAG,MAAM,KAAK,KAAK,MAAM,SAAS,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;CACvE,MAAM,WAAW,MAAM,kBAAkB;CACzC,MAAM,WAAW,MAAM,YAAY;;AAGrC,eAAe,WAAW,UAAiC;CACzD,IAAI;EACF,MAAMA,KAAG,OAAO,UAAUA,KAAG,UAAU,KAAK;SACtC;EACN,MAAMA,KAAG,UAAU,UAAU,GAAG;EAChC,MAAMA,KAAG,MAAM,UAAU,IAAM;;;;;AC4BnC,MAAM,wBAAwB;;;;;;AAO9B,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;AAoB7B,MAAM,gBAA2B;CAC/B,MAAM,EACJ,SAAS,EAAE,EACZ;CACD,WAAW,EAAE;CACb,eAAe,EAAE;CACjB,cAAc;EACZ,cAAc;EACd,iBAAiB;EACjB,gBAAgB;EAChB,WAAW;EACX,WAAW;EACZ;CACD,YAAY;CACZ,qCAAqC,EAAE;CACvC,uBAAuB;EACrB,cAAc;EACd,iBAAiB;EACjB,gBAAgB;EAChB,WAAW;EACX,WAAW;EACZ;CACD,gBAAgB;CAChB,0BAA0B;CAC1B,0BAA0B;CAC3B;AAED,IAAI,eAAiC;AAErC,SAAS,qBAAqB,aAAqC;CACjE,IAAI,OAAO,gBAAgB,UAAU;EACnC,IAAI,gBAAgB,KAAA,GAClB,QAAQ,KACN,gEACD;EAEH,OAAO;;CAGT,MAAM,wBAAwB,YAAY,MAAM;CAChD,IAAI,CAAC,uBAAuB;EAC1B,QAAQ,KACN,gEACD;EACD,OAAO;;CAGT,OAAO;;AAGT,SAAS,sBAA8B;CACrC,OAAO,YAAY,GAAG,CAAC,SAAS,MAAM;;AAGxC,SAAS,YAAY,OAAgD;CACnE,OAAO,iBAAiB,SAAS,UAAU;;AAG7C,SAAS,mBAAyB;CAChC,IAAI;EACF,GAAG,WAAW,MAAM,aAAa,GAAG,UAAU,OAAO,GAAG,UAAU,KAAK;SACjE;EACN,GAAG,UAAU,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;EAChD,GAAG,cACD,MAAM,aACN,GAAG,KAAK,UAAU,eAAe,MAAM,EAAE,CAAC,KAC1C,OACD;EACD,IAAI;GACF,GAAG,UAAU,MAAM,aAAa,IAAM;UAChC;GACN;;;;AAKN,SAAS,qBAAgC;CACvC,kBAAkB;CAClB,IAAI;EACF,MAAM,MAAM,GAAG,aAAa,MAAM,aAAa,OAAO;EACtD,IAAI,CAAC,IAAI,MAAM,EAAE;GACf,GAAG,cACD,MAAM,aACN,GAAG,KAAK,UAAU,eAAe,MAAM,EAAE,CAAC,KAC1C,OACD;GACD,OAAO;;EAET,OAAO,KAAK,MAAM,IAAI;UACf,OAAO;EACd,QAAQ,MAAM,oDAAoD,MAAM;EACxE,OAAO;;;AAIX,SAAS,6BAAwC;CAC/C,IAAI;EACF,MAAM,MAAM,GAAG,aAAa,MAAM,aAAa,OAAO;EACtD,IAAI,CAAC,IAAI,MAAM,EACb,OAAO,EAAE;EAEX,OAAO,KAAK,MAAM,IAAI;UACf,OAAO;EACd,IAAI,YAAY,MAAM,IAAI,MAAM,SAAS,UACvC,OAAO,EAAE;EAEX,IAAI,iBAAiB,aACnB,MAAM,IAAI,MAAM,kCAAkC,MAAM,cAAc;EAExE,MAAM;;;AAIV,SAAS,kBAAkB,QAAyB;CAClD,GAAG,UAAU,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;CAChD,GAAG,cACD,MAAM,aACN,GAAG,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,KACnC,OACD;;AAGH,SAAS,mBAAmB,QAG1B;CACA,MAAM,eAAe,OAAO,gBAAgB,EAAE;CAC9C,MAAM,sBAAsB,cAAc,gBAAgB,EAAE;CAC5D,MAAM,wBAAwB,OAAO,yBAAyB,EAAE;CAChE,MAAM,+BAA+B,cAAc,yBAAyB,EAAE;CAE9E,MAAM,2BAA2B,OAAO,KAAK,oBAAoB,CAAC,QAC/D,UAAU,CAAC,OAAO,OAAO,cAAc,MAAM,CAC/C;CAED,MAAM,+BAA+B,OAAO,KAC1C,6BACD,CAAC,QAAQ,UAAU,CAAC,OAAO,OAAO,uBAAuB,MAAM,CAAC;CAEjE,MAAM,wBAAwB,yBAAyB,SAAS;CAChE,MAAM,4BAA4B,6BAA6B,SAAS;CAExE,IAAI,CAAC,yBAAyB,CAAC,2BAC7B,OAAO;EAAE,cAAc;EAAQ,SAAS;EAAO;CAGjD,OAAO;EACL,cAAc;GACZ,GAAG;GACH,cAAc;IACZ,GAAG;IACH,GAAG;IACJ;GACD,uBAAuB;IACrB,GAAG;IACH,GAAG;IACJ;GACF;EACD,SAAS;EACV;;AAGH,SAAS,kBAAkB,QAGzB;CACA,MAAM,wBAAwB,qBAAqB,OAAO,MAAM,YAAY;CAC5E,IAAI,uBAAuB;EACzB,IAAI,OAAO,MAAM,gBAAgB,uBAC/B,OAAO;GAAE,cAAc;GAAQ,SAAS;GAAO;EAGjD,OAAO;GACL,cAAc;IACZ,GAAG;IACH,MAAM;KACJ,GAAG,OAAO;KACV,aAAa;KACd;IACF;GACD,SAAS;GACV;;CAGH,MAAM,iBAAiB,4BAA4B;CACnD,MAAM,EAAE,iBAAiB,mBAAmB;EAC1C,GAAG;EACH,MAAM;GACJ,GAAG,eAAe;GAClB,aAAa,qBAAqB;GACnC;EACF,CAAC;CAEF,OAAO;EAAE;EAAc,SAAS;EAAM;;AAGxC,SAAgB,0BAAqC;CAEnD,MAAM,EAAE,cAAc,YAAY,mBADnB,oBAC4C,CAAC;CAC5D,MAAM,EACJ,cAAc,6BACd,SAAS,uBACP,kBAAkB,aAAa;CAGnC,IAF4B,WAAW,oBAGrC,IAAI;EACF,kBAAkB,4BAA4B;UACvC,YAAY;EACnB,IAAI,oBACF,MAAM;EAGR,QAAQ,KACN,sDACA,WACD;;CAIL,eAAe;CACf,OAAO;;AAGT,SAAgB,YAAuB;CACrC,iBAAiB,mBAAmB,oBAAoB,CAAC,CAAC;CAC1D,OAAO;;AAGT,SAAgB,eAA0B;CACxC,OAAO,yBAAyB;;AAGlC,SAAgB,uBAAuB,OAAuB;CAE5D,OADe,WACF,CAAC,eAAe,UAAU;;AAGzC,SAAgB,mBAA2C;CAEzD,MAAM,gBADS,WACa,CAAC;CAC7B,IAAI,CAAC,eACH,OAAO,EAAE,GAAG,cAAc,eAAe;CAG3C,MAAM,gBAAwC,EAAE;CAChD,KAAK,MAAM,CAAC,aAAa,gBAAgB,OAAO,QAAQ,cAAc,EAAE;EACtE,IACE,CAAC,eACE,OAAO,gBAAgB,YACvB,YAAY,WAAW,GAE1B;EAEF,cAAc,eAAe;;CAG/B,OAAO;;AAGT,SAAS,sBACP,eACwB;CACxB,MAAM,oBAA4C,EAAE;CACpD,KAAK,MAAM,CAAC,aAAa,gBAAgB,OAAO,QAAQ,cAAc,EAAE;EACtE,IAAI,CAAC,eAAe,CAAC,aACnB,MAAM,IAAI,MACR,kEACD;EAEH,kBAAkB,eAAe;;CAGnC,OAAO;;AAGT,SAAgB,iBACd,eACwB;CAMxB,kBAAkB;EAJhB,GAAG,4BAA4B;EAC/B,eAAe,sBAAsB,cAAc;EAGzB,CAAC;CAC7B,eAAe,cAAc;CAC7B,OAAO,kBAAkB;;AAG3B,SAAgB,mBAAmB,OAAuB;CACxD,OAAO,kBAAkB,CAAC,UAAU;;AAGtC,SAAgB,gBAAwB;CAEtC,OADe,WACF,CAAC,cAAc;;AAG9B,SAAgB,yCAAwD;CAEtE,OADe,WAEP,CAAC,uCACJ,cAAc,uCACd,EAAE;;AAIT,SAAgB,qCAAqC,OAAwB;CAC3E,OAAO,wCAAwC,CAAC,SAAS,MAAM;;AAGjE,SAAgB,2BACd,OAC0D;CAE1D,OADe,WACF,CAAC,wBAAwB,UAAU;;AAGlD,SAAgB,yBAAyB,KAAqB;CAC5D,OAAO,IAAI,MAAM,CAAC,QAAQ,SAAS,GAAG;;AAGxC,SAAS,2BACP,cACkB;CAClB,OAAO,iBAAiB,cAAc,cAAc;;AAGtD,SAAgB,wBACd,cACA,UACA,cACkB;CAClB,MAAM,kBAAkB,2BAA2B,aAAa;CAChE,IAAI,aAAa,KAAA,GACf,OAAO;CAGT,IAAI,aAAa,aACf,OAAO;CAGT,IAAI,aAAa,UAAU;EACzB,IAAI,iBAAiB,SACnB,OAAO;EAGT,QAAQ,KACN,YAAY,aAAa,iGAAiG,kBAC3H;EACD,OAAO;;CAGT,IAAI,aAAa,iBACf,OAAO;CAGT,QAAQ,KACN,YAAY,aAAa,yBAAyB,SAAS,qBAAqB,kBACjF;CACD,OAAO;;AAGT,SAAS,yBACP,cACA,UACS;CACT,OAAO,EAAE,iBAAiB,WAAW,aAAa;;AAGpD,SAAgB,qBAAqB,MAAqC;CACxE,MAAM,eAAe,KAAK,MAAM;CAChC,IAAI,CAAC,cACH,OAAO;CAIT,OADe,WACF,CAAC,YAAY,iBAAiB;;AAG7C,SAAgB,kBACd,MACA,UACgB;CAChB,MAAM,eAAe,KAAK,MAAM;CAChC,IAAI,CAAC,cACH,MAAM,IAAI,MAAM,2CAA2C;CAG7D,IAAI,uBAAuB,aAAa,EACtC,MAAM,IAAI,MACR,YAAY,aAAa,2DAC1B;CAGH,MAAM,iBAAiB,4BAA4B;CASnD,kBAAkB;EAPhB,GAAG;EACH,WAAW;GACT,GAAG,eAAe;IACjB,eAAe;GACjB;EAGyB,CAAC;CAC7B,eAAe,cAAc;CAC7B,OAAO,qBAAqB,aAAa,IAAI;;AAG/C,SAAgB,kBAAkB,MAA6C;CAC7E,MAAM,eAAe,KAAK,MAAM;CAChC,IAAI,CAAC,cACH,OAAO;CAGT,IAAI,uBAAuB,aAAa,EAAE;EACxC,QAAQ,KACN,YAAY,aAAa,2DAC1B;EACD,OAAO;;CAGT,MAAM,WAAW,qBAAqB,aAAa;CACnD,IAAI,CAAC,UACH,OAAO;CAGT,IAAI,SAAS,YAAY,OACvB,OAAO;CAGT,MAAM,OAAO,SAAS,QAAQ;CAC9B,IACE,SAAS,eACN,SAAS,uBACT,SAAS,oBACZ;EACA,QAAQ,KACN,YAAY,aAAa,4BAA4B,KAAK,oBAC3D;EACD,OAAO;;CAGT,MAAM,UAAU,yBAAyB,SAAS,WAAW,GAAG;CAChE,MAAM,WAAW,wBACf,cACA,SAAS,UACT,KACD;CACD,MAAM,UAAU,SAAS,UAAU,IAAI,MAAM;CAC7C,MAAM,gBAAgB,CACpB,GAAI,CAAC,UAAU,CAAC,UAAU,GAAG,EAAE,EAC/B,GAAI,yBAAyB,cAAc,SAAS,IAAI,CAAC,SACvD,CAAC,SAAS,GACV,EAAE,CACL;CAED,IAAI,cAAc,SAAS,GAAG;EAC5B,QAAQ,KACN,YAAY,aAAa,0BAA0B,cAAc,KAAK,OAAO,GAC9E;EACD,OAAO;;CAGT,OAAO;EACL,MAAM;EACN;EACA;EACA;EACA;EACA,QAAQ,SAAS;EACjB,mBAAmB,SAAS;EAC7B;;AAGH,SAAgB,uBAAsC;CACpD,MAAM,SAAS,WAAW;CAE1B,OADsB,OAAO,KAAK,OAAO,aAAa,EAAE,CACpC,CAAC,QAAQ,SAAS,kBAAkB,KAAK,KAAK,KAAK;;AAGzE,SAAgB,uBAAuB,MAAuB;CAC5D,OAAO,KAAK,MAAM,KAAK;;AAGzB,SAAgB,uBAAgC;CAE9C,OADe,WACF,CAAC,kBAAkB;;AAGlC,SAAgB,iCAA0C;CAExD,OADe,WACF,CAAC,4BAA4B;;AAG5C,SAAgB,qBAAyC;CAEvD,OADe,WACF,CAAC,mBAAmB,QAAQ,IAAI,qBAAqB,KAAA;;AAGpE,SAAgB,iCAA0C;CAExD,OADe,WACF,CAAC,4BAA4B;;AAG5C,SAAgB,2BAAmC;CAEjD,OADe,WACF,CAAC,yBAAyB"}