@nick3/copilot-api 1.2.4 → 1.3.1
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 +218 -22
- package/dist/{accounts-manager-eec8Wj3_.js → accounts-manager-BeKvbv0T.js} +109 -8
- package/dist/accounts-manager-BeKvbv0T.js.map +1 -0
- package/dist/main.js +13 -9
- package/dist/main.js.map +1 -1
- package/dist/{server-Cxlbm6kJ.js → server-D3A61KAx.js} +314 -95
- package/dist/server-D3A61KAx.js.map +1 -0
- package/package.json +1 -1
- package/dist/accounts-manager-eec8Wj3_.js.map +0 -1
- package/dist/server-Cxlbm6kJ.js.map +0 -1
package/README.md
CHANGED
|
@@ -23,7 +23,8 @@ English | [中文](./README_CN.md)
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
> [!NOTE]
|
|
27
|
+
> [opencode](https://github.com/sst/opencode) already ships with a built-in GitHub Copilot provider, so you may not need this project for basic usage. This proxy is still useful if you want OpenCode to talk to Copilot through `@ai-sdk/anthropic`, preserve Anthropic Messages semantics for tool use, prefer the native Messages API over plain Chat Completions for Claude-family models, use `gpt-5.4` phase-aware commentary, or fine-tune premium-request usage with small-model fallbacks.
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -31,9 +32,17 @@ English | [中文](./README_CN.md)
|
|
|
31
32
|
|
|
32
33
|
A reverse-engineered proxy for the GitHub Copilot API that exposes it as an OpenAI and Anthropic compatible service. This allows you to use GitHub Copilot with any tool that supports the OpenAI Chat Completions API or the Anthropic Messages API, including to power [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview).
|
|
33
34
|
|
|
35
|
+
Compared with routing everything through plain Chat Completions compatibility, this proxy can prefer Copilot's native Anthropic-style Messages API for Claude-family models, preserve more native thinking/tool semantics, reduce unnecessary Premium request consumption on warmup or resumed tool turns, and expose phase-aware `gpt-5.4` / `gpt-5.3-codex` responses that are easier for users to follow.
|
|
36
|
+
|
|
34
37
|
## Features
|
|
35
38
|
|
|
36
39
|
- **OpenAI & Anthropic Compatibility**: Exposes GitHub Copilot as an OpenAI-compatible (`/v1/responses`, `/v1/chat/completions`, `/v1/models`, `/v1/embeddings`) and Anthropic-compatible (`/v1/messages`) API.
|
|
40
|
+
- **Anthropic-First Routing for Claude Models**: When a model supports Copilot's native `/v1/messages` endpoint, the proxy prefers it over `/responses` or `/chat/completions`, preserving Anthropic-style `tool_use` / `tool_result` flows and more Claude-native behavior.
|
|
41
|
+
- **Fewer Unnecessary Premium Requests**: Reduces wasted premium usage by routing warmup and compact/background requests to `smallModel`, merging `tool_result` follow-ups back into the tool flow, and treating resumed tool turns as continuation traffic instead of fresh premium interactions.
|
|
42
|
+
- **Phase-Aware `gpt-5.4` and `gpt-5.3-codex`**: These models can emit user-friendly commentary before deeper reasoning or tool use, so long-running coding actions are easier to understand instead of appearing as a sudden tool burst.
|
|
43
|
+
- **Claude Native Beta Support**: On the Messages API path, supports Anthropic-native capabilities such as `interleaved-thinking`, `advanced-tool-use`, and `context-management`, which are difficult or unavailable through plain Chat Completions compatibility.
|
|
44
|
+
- **Subagent Marker Integration**: Optional Claude Code and opencode plugins can inject `__SUBAGENT_MARKER__...` and propagate `x-session-id` so subagent traffic keeps the correct root session and agent/user semantics.
|
|
45
|
+
- **OpenCode via `@ai-sdk/anthropic`**: Point OpenCode at this proxy as an Anthropic provider so Anthropic Messages semantics, premium-request optimizations, and Claude-native behavior are preserved end to end.
|
|
37
46
|
- **Claude Code Integration**: Easily configure and launch [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) to use Copilot as its backend with a simple command-line flag (`--claude-code`).
|
|
38
47
|
- **Usage Dashboard**: A web-based dashboard to monitor your Copilot API usage, view quotas, and see detailed statistics.
|
|
39
48
|
- **Admin UI**: Modern admin console (`/admin`) to inspect account runtime status and request history, with rich filtering and a request-detail JSON viewer (search/copy/download). Includes theme (system/light/dark) and motion (magic/subtle/off) toggles.
|
|
@@ -43,6 +52,49 @@ A reverse-engineered proxy for the GitHub Copilot API that exposes it as an Open
|
|
|
43
52
|
- **Flexible Authentication**: Authenticate interactively or provide a GitHub token directly, suitable for CI/CD environments.
|
|
44
53
|
- **Support for Different Account Types**: Works with individual, business, and enterprise GitHub Copilot plans.
|
|
45
54
|
- **Multi-Account Support**: Use multiple GitHub Copilot accounts with automatic routing: premium models use accounts in order and fall back on quota exhaustion; free models are distributed round-robin across accounts by default (configurable in config.json).
|
|
55
|
+
- **Opencode OAuth Support**: Use opencode GitHub Copilot authentication by setting `COPILOT_API_OAUTH_APP=opencode` environment variable.
|
|
56
|
+
- **GitHub Enterprise Support**: Connect to GHE.com by setting `COPILOT_API_ENTERPRISE_URL` environment variable (e.g., `company.ghe.com`).
|
|
57
|
+
- **Custom Data Directory**: Change the default data directory (where tokens and config are stored) by setting `COPILOT_API_HOME` environment variable.
|
|
58
|
+
- **Multi-Provider Anthropic Proxy Routes**: Add global provider configs and call external Anthropic-compatible APIs via `/:provider/v1/messages` and `/:provider/v1/models`.
|
|
59
|
+
|
|
60
|
+
## Better Agent Semantics
|
|
61
|
+
|
|
62
|
+
### Native Anthropic Messages API when available
|
|
63
|
+
|
|
64
|
+
For models that advertise Copilot support for `/v1/messages`, this project sends the request to the native Messages API first and only falls back to `/responses` or `/chat/completions` when needed.
|
|
65
|
+
|
|
66
|
+
Compared with using Claude-family models only through Chat Completions compatibility, the Messages API path keeps more Anthropic-native behavior, including support for:
|
|
67
|
+
|
|
68
|
+
- `interleaved-thinking-2025-05-14`
|
|
69
|
+
- `advanced-tool-use-2025-11-20`
|
|
70
|
+
- `context-management-2025-06-27`
|
|
71
|
+
|
|
72
|
+
Supported `anthropic-beta` values are filtered and forwarded on the native Messages path, and `interleaved-thinking` is added automatically when a thinking budget is requested for non-adaptive extended thinking.
|
|
73
|
+
|
|
74
|
+
### Fewer unnecessary Premium requests
|
|
75
|
+
|
|
76
|
+
The proxy includes request-accounting safeguards designed for tool-heavy coding workflows:
|
|
77
|
+
|
|
78
|
+
- tool-less warmup or probe requests can be forced onto `smallModel` so background checks do not spend premium usage;
|
|
79
|
+
- compact/background requests can be downgraded to `smallModel` automatically;
|
|
80
|
+
- mixed `tool_result` + reminder text blocks are merged back into the `tool_result` flow instead of being counted like fresh user turns;
|
|
81
|
+
- `x-initiator` is derived from the latest message or item, not stale assistant history.
|
|
82
|
+
|
|
83
|
+
This helps resumed tool turns continue the existing workflow instead of consuming an extra Premium request as a brand-new interaction.
|
|
84
|
+
|
|
85
|
+
### Phase-aware `gpt-5.4` and `gpt-5.3-codex`
|
|
86
|
+
|
|
87
|
+
By default, the built-in `extraPrompts` for `gpt-5.4` and `gpt-5.3-codex` enable intermediary-update behavior, and the proxy translates assistant turns into `phase: "commentary"` before tool calls and `phase: "final_answer"` for the final response.
|
|
88
|
+
|
|
89
|
+
That gives clients a short, user-friendly explanation of what the model is about to do before deeper reasoning or tool execution begins.
|
|
90
|
+
|
|
91
|
+
### Subagent marker integration
|
|
92
|
+
|
|
93
|
+
For subagent-based clients, this project can preserve root session context and correctly classify subagent-originated traffic.
|
|
94
|
+
|
|
95
|
+
The marker flow uses `__SUBAGENT_MARKER__...` inside a `<system-reminder>` block together with root `x-session-id` propagation. When a marker is detected, the proxy can keep the parent session identity, infer `x-initiator: agent`, and tag the interaction as subagent traffic instead of a fresh top-level request.
|
|
96
|
+
|
|
97
|
+
Optional marker producers are included for both Claude Code and opencode; see [Subagent Marker Integration](#subagent-marker-integration-optional) below for setup details.
|
|
46
98
|
|
|
47
99
|
## Demo
|
|
48
100
|
|
|
@@ -245,14 +297,32 @@ The `<target>` can be either the account ID (GitHub username) or a 1-based index
|
|
|
245
297
|
"auth": {
|
|
246
298
|
"apiKeys": []
|
|
247
299
|
},
|
|
300
|
+
"providers": {
|
|
301
|
+
"custom": {
|
|
302
|
+
"type": "anthropic",
|
|
303
|
+
"enabled": true,
|
|
304
|
+
"baseUrl": "your-base-url",
|
|
305
|
+
"apiKey": "sk-your-provider-key",
|
|
306
|
+
"models": {
|
|
307
|
+
"kimi-k2.5": {
|
|
308
|
+
"temperature": 1,
|
|
309
|
+
"topP": 0.95
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
},
|
|
248
314
|
"extraPrompts": {
|
|
249
315
|
"gpt-5-mini": "<built-in exploration prompt>",
|
|
250
|
-
"gpt-5.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
316
|
+
"gpt-5.3-codex": "<built-in commentary prompt>",
|
|
317
|
+
"gpt-5.4": "<built-in commentary prompt>"
|
|
318
|
+
},
|
|
319
|
+
"smallModel": "gpt-5-mini",
|
|
320
|
+
"freeModelLoadBalancing": true,
|
|
321
|
+
"responsesApiContextManagementModels": [],
|
|
322
|
+
"modelReasoningEfforts": {
|
|
323
|
+
"gpt-5-mini": "low",
|
|
324
|
+
"gpt-5.3-codex": "xhigh",
|
|
325
|
+
"gpt-5.4": "xhigh"
|
|
256
326
|
},
|
|
257
327
|
"modelAliases": {
|
|
258
328
|
"fast": { "target": "gpt-5-mini", "allowOriginal": false },
|
|
@@ -265,13 +335,22 @@ The `<target>` can be either the account ID (GitHub username) or a 1-based index
|
|
|
265
335
|
}
|
|
266
336
|
```
|
|
267
337
|
- **auth.apiKeys:** API keys used for request authentication. Supports multiple keys for rotation. Requests can authenticate with either `x-api-key: <key>` or `Authorization: Bearer <key>`. If empty or omitted, authentication is disabled.
|
|
268
|
-
- **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.
|
|
269
|
-
- **
|
|
270
|
-
-
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
-
|
|
274
|
-
-
|
|
338
|
+
- **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.
|
|
339
|
+
- **providers:** Global upstream provider map. Each provider key (for example `custom`) becomes a route prefix (`/custom/v1/messages`). Currently only `type: "anthropic"` is supported.
|
|
340
|
+
- `enabled` defaults to `true` if omitted.
|
|
341
|
+
- `baseUrl` should be provider API base URL without trailing `/v1/messages`.
|
|
342
|
+
- `apiKey` is used as upstream `x-api-key`.
|
|
343
|
+
- `models` (optional): Per-model configuration map. Each key is a model ID (matching the model name in requests), and the value is:
|
|
344
|
+
- `temperature` (optional): Default temperature value used when the request does not specify one.
|
|
345
|
+
- `topP` (optional): Default top_p value used when the request does not specify one.
|
|
346
|
+
- `topK` (optional): Default top_k value used when the request does not specify one.
|
|
347
|
+
- **responsesApiContextManagementModels:** List of model IDs that should receive Responses API `context_management` compaction instructions. Use this when a model supports server-side context management and you want the proxy to keep only the latest compaction carrier on follow-up turns.
|
|
348
|
+
- **smallModel:** Fallback model used for tool-less warmup messages, compact/background requests, and other short housekeeping turns (for example from Claude Code or OpenCode) to avoid spending premium requests; defaults to `gpt-5-mini`. If original names are blocked and this points to an aliased target, it resolves to the preferred alias.
|
|
349
|
+
- **freeModelLoadBalancing:** Enable round-robin routing for free-model requests across multiple accounts. Defaults to `true`. Set to `false` to route free-model requests sequentially (same ordering strategy as premium models).
|
|
350
|
+
- **apiKey (deprecated):** Legacy single-key field kept for migration compatibility. Prefer `auth.apiKeys`. When `auth.apiKeys` is empty, the server falls back to `COPILOT_API_KEY` and then `apiKey`.
|
|
351
|
+
- **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.
|
|
352
|
+
- **modelAliases:** Map of `alias -> { target, allowOriginal? }` (legacy string values are still accepted). Alias keys are normalized (trim + lowercase) and must be non-empty; aliases cannot map to themselves (case-insensitive), and conflicting normalized aliases are rejected. `allowOriginal` overrides the global default per alias. If multiple aliases map to the same target, original names are allowed when any alias sets `allowOriginal: true` (allow-wins). Admin UI/API rejects blocked keys (`__proto__`, `constructor`, `prototype`). Aliases can be used in downstream requests.
|
|
353
|
+
- **allowOriginalModelNamesForAliases:** Global default for aliases that omit `allowOriginal`. When `false` (default), targets are blocked unless an alias explicitly allows them; when `true`, targets are allowed unless all aliases explicitly block them.
|
|
275
354
|
- **useFunctionApplyPatch:** When `true` (default), `POST /v1/responses` converts a `tools` entry with `{ "type": "custom", "name": "apply_patch" }` into an OpenAI-style `function` tool (with a parameter schema) for upstream compatibility. Set to `false` to leave custom tools untouched.
|
|
276
355
|
- **forceAgent:** When `true`, `POST /v1/responses` treats a request as agent-initiated if **any** input item has `role: "assistant"`. When `false` (default), only the **last** input item is checked.
|
|
277
356
|
- **compactUseSmallModel:** When `true`, detected "compact" requests (e.g., from Claude Code or opencode compact mode) will automatically use the configured `smallModel` to avoid consuming premium usage for short/background tasks. Defaults to `true`.
|
|
@@ -319,6 +398,9 @@ These endpoints are designed to be compatible with the Anthropic Messages API.
|
|
|
319
398
|
| -------------------------------- | ------ | ------------------------------------------------------------ |
|
|
320
399
|
| `POST /v1/messages` | `POST` | Creates a model response for a given conversation. |
|
|
321
400
|
| `POST /v1/messages/count_tokens` | `POST` | Calculates the number of tokens for a given set of messages. |
|
|
401
|
+
| `POST /:provider/v1/messages` | `POST` | Proxies Anthropic Messages API to the configured provider. |
|
|
402
|
+
| `GET /:provider/v1/models` | `GET` | Proxies Anthropic Models API to the configured provider. |
|
|
403
|
+
| `POST /:provider/v1/messages/count_tokens` | `POST` | Calculates tokens locally for provider route requests. |
|
|
322
404
|
|
|
323
405
|
### Usage Monitoring Endpoints
|
|
324
406
|
|
|
@@ -445,8 +527,122 @@ npx @nick3/copilot-api@latest debug --json
|
|
|
445
527
|
|
|
446
528
|
# Initialize proxy from environment variables (HTTP_PROXY, HTTPS_PROXY, etc.)
|
|
447
529
|
npx @nick3/copilot-api@latest start --proxy-env
|
|
530
|
+
|
|
531
|
+
# Use opencode GitHub Copilot authentication
|
|
532
|
+
COPILOT_API_OAUTH_APP=opencode npx @nick3/copilot-api@latest start
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Opencode OAuth Authentication
|
|
536
|
+
|
|
537
|
+
You can use opencode GitHub Copilot authentication instead of the default one:
|
|
538
|
+
|
|
539
|
+
```sh
|
|
540
|
+
# Set environment variable before running any command
|
|
541
|
+
export COPILOT_API_OAUTH_APP=opencode
|
|
542
|
+
|
|
543
|
+
# Then run start or auth commands
|
|
544
|
+
npx @nick3/copilot-api@latest start
|
|
545
|
+
npx @nick3/copilot-api@latest auth
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Or use inline environment variable:
|
|
549
|
+
|
|
550
|
+
```sh
|
|
551
|
+
COPILOT_API_OAUTH_APP=opencode npx @nick3/copilot-api@latest start
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
## Using with OpenCode
|
|
555
|
+
|
|
556
|
+
OpenCode already has a direct GitHub Copilot provider. Use this section when you want OpenCode to point at this proxy through `@ai-sdk/anthropic` and reuse the agent behaviors described earlier in this README.
|
|
557
|
+
|
|
558
|
+
### Minimal setup
|
|
559
|
+
|
|
560
|
+
Start the proxy with the OpenCode OAuth app:
|
|
561
|
+
|
|
562
|
+
```sh
|
|
563
|
+
COPILOT_API_OAUTH_APP=opencode npx @nick3/copilot-api@latest start
|
|
448
564
|
```
|
|
449
565
|
|
|
566
|
+
Then point OpenCode at the proxy with `@ai-sdk/anthropic`.
|
|
567
|
+
|
|
568
|
+
Example `~/.config/opencode/opencode.json`:
|
|
569
|
+
|
|
570
|
+
```json
|
|
571
|
+
{
|
|
572
|
+
"$schema": "https://opencode.ai/config.json",
|
|
573
|
+
"model": "local/gpt-5.4",
|
|
574
|
+
"small_model": "local/gpt-5-mini",
|
|
575
|
+
"agent": {
|
|
576
|
+
"build": {
|
|
577
|
+
"model": "local/gpt-5.4"
|
|
578
|
+
},
|
|
579
|
+
"plan": {
|
|
580
|
+
"model": "local/gpt-5.4"
|
|
581
|
+
},
|
|
582
|
+
"explore": {
|
|
583
|
+
"model": "local/gpt-5-mini"
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
"provider": {
|
|
587
|
+
"local": {
|
|
588
|
+
"npm": "@ai-sdk/anthropic",
|
|
589
|
+
"name": "Copilot API Proxy",
|
|
590
|
+
"options": {
|
|
591
|
+
"baseURL": "http://localhost:4141/v1",
|
|
592
|
+
"apiKey": "dummy"
|
|
593
|
+
},
|
|
594
|
+
"models": {
|
|
595
|
+
"gpt-5.4": {
|
|
596
|
+
"name": "gpt-5.4",
|
|
597
|
+
"modalities": {
|
|
598
|
+
"input": ["text", "image"],
|
|
599
|
+
"output": ["text"]
|
|
600
|
+
},
|
|
601
|
+
"limit": {
|
|
602
|
+
"context": 272000,
|
|
603
|
+
"output": 128000
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
"gpt-5-mini": {
|
|
607
|
+
"name": "gpt-5-mini",
|
|
608
|
+
"limit": {
|
|
609
|
+
"context": 128000,
|
|
610
|
+
"output": 64000
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
"claude-sonnet-4.6": {
|
|
614
|
+
"id": "claude-sonnet-4.6",
|
|
615
|
+
"name": "claude-sonnet-4.6",
|
|
616
|
+
"modalities": {
|
|
617
|
+
"input": ["text", "image"],
|
|
618
|
+
"output": ["text"]
|
|
619
|
+
},
|
|
620
|
+
"limit": {
|
|
621
|
+
"context": 128000,
|
|
622
|
+
"output": 32000
|
|
623
|
+
},
|
|
624
|
+
"options": {
|
|
625
|
+
"thinking": {
|
|
626
|
+
"type": "enabled",
|
|
627
|
+
"budgetTokens": 31999
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Why these fields matter:
|
|
638
|
+
|
|
639
|
+
- `npm: "@ai-sdk/anthropic"` is the important part. OpenCode will speak Anthropic Messages semantics to this proxy instead of flattening everything into OpenAI Chat Completions.
|
|
640
|
+
- `options.baseURL` should be `http://localhost:4141/v1`; the Anthropic SDK will append `/messages`, `/models`, and `/messages/count_tokens` automatically.
|
|
641
|
+
- `model`, `small_model`, and `agent.*.model` let you keep `gpt-5.4` for build/plan work while routing exploration and background work to `gpt-5-mini`.
|
|
642
|
+
- If you enable `auth.apiKeys` in this proxy, replace `dummy` with a real key. Otherwise any placeholder value is fine.
|
|
643
|
+
|
|
644
|
+
## Using the Usage Viewer
|
|
645
|
+
|
|
450
646
|
### Admin API examples
|
|
451
647
|
|
|
452
648
|
```sh
|
|
@@ -544,8 +740,8 @@ Here is an example `.claude/settings.json` file:
|
|
|
544
740
|
"env": {
|
|
545
741
|
"ANTHROPIC_BASE_URL": "http://localhost:4141",
|
|
546
742
|
"ANTHROPIC_AUTH_TOKEN": "dummy",
|
|
547
|
-
"ANTHROPIC_MODEL": "gpt-5.
|
|
548
|
-
"ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.
|
|
743
|
+
"ANTHROPIC_MODEL": "gpt-5.4",
|
|
744
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL": "gpt-5.4",
|
|
549
745
|
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "gpt-5-mini",
|
|
550
746
|
"DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1",
|
|
551
747
|
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
|
|
@@ -561,20 +757,20 @@ Here is an example `.claude/settings.json` file:
|
|
|
561
757
|
}
|
|
562
758
|
```
|
|
563
759
|
|
|
564
|
-
### CLAUDE.md
|
|
760
|
+
### CLAUDE.md / AGENTS.md (Optional)
|
|
565
761
|
|
|
566
|
-
|
|
762
|
+
If you're using Claude Code (or another agent runner) and want stricter “ask before acting” behavior, you may choose to add rules like:
|
|
567
763
|
|
|
568
|
-
-
|
|
569
|
-
-
|
|
764
|
+
- Prefer using the runner's dedicated question tool for user-facing questions.
|
|
765
|
+
- Ask the user to confirm completion before ending the task.
|
|
570
766
|
|
|
571
767
|
You can find more options here: [Claude Code settings](https://docs.anthropic.com/en/docs/claude-code/settings#environment-variables)
|
|
572
768
|
|
|
573
769
|
You can also read more about IDE integration here: [Add Claude Code to your IDE](https://docs.anthropic.com/en/docs/claude-code/ide-integrations)
|
|
574
770
|
|
|
575
|
-
|
|
771
|
+
## Subagent Marker Integration (Optional)
|
|
576
772
|
|
|
577
|
-
This project supports `x-initiator: agent` for subagent-originated requests.
|
|
773
|
+
This project supports `x-initiator: agent` for subagent-originated requests and can preserve the root session identity with `x-session-id` when a subagent marker is present.
|
|
578
774
|
|
|
579
775
|
#### Claude Code plugin producer (marketplace-based)
|
|
580
776
|
|
|
@@ -7,9 +7,11 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
7
7
|
import fs$1 from "node:fs";
|
|
8
8
|
|
|
9
9
|
//#region src/lib/paths.ts
|
|
10
|
+
const AUTH_APP = process.env.COPILOT_API_OAUTH_APP?.trim() || "";
|
|
11
|
+
const ENTERPRISE_PREFIX = process.env.COPILOT_API_ENTERPRISE_URL ? "ent_" : "";
|
|
10
12
|
const DEFAULT_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
|
|
11
13
|
const APP_DIR = process.env.COPILOT_API_HOME || DEFAULT_DIR;
|
|
12
|
-
const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
|
|
14
|
+
const GITHUB_TOKEN_PATH = path.join(APP_DIR, AUTH_APP, ENTERPRISE_PREFIX + "github_token");
|
|
13
15
|
const CONFIG_PATH = path.join(APP_DIR, "config.json");
|
|
14
16
|
const MODELS_PATH = path.join(APP_DIR, "models.json");
|
|
15
17
|
const TOKENS_DIR = path.join(APP_DIR, "tokens");
|
|
@@ -32,6 +34,7 @@ function accountTokenPath(id) {
|
|
|
32
34
|
}
|
|
33
35
|
async function ensurePaths() {
|
|
34
36
|
await fs.mkdir(PATHS.APP_DIR, { recursive: true });
|
|
37
|
+
await fs.mkdir(path.join(PATHS.APP_DIR, AUTH_APP), { recursive: true });
|
|
35
38
|
await fs.mkdir(PATHS.TOKENS_DIR, { recursive: true });
|
|
36
39
|
await ensureFile(PATHS.GITHUB_TOKEN_PATH);
|
|
37
40
|
await ensureFile(PATHS.CONFIG_PATH);
|
|
@@ -245,6 +248,59 @@ function accountFromState() {
|
|
|
245
248
|
|
|
246
249
|
//#endregion
|
|
247
250
|
//#region src/lib/api-config.ts
|
|
251
|
+
const isOpencodeOauthApp = () => {
|
|
252
|
+
return process.env.COPILOT_API_OAUTH_APP?.trim() === "opencode";
|
|
253
|
+
};
|
|
254
|
+
const normalizeDomain = (input) => {
|
|
255
|
+
return input.trim().replace(/^https?:\/\//u, "").replace(/\/+$/u, "");
|
|
256
|
+
};
|
|
257
|
+
const getEnterpriseDomain = () => {
|
|
258
|
+
const raw = (process.env.COPILOT_API_ENTERPRISE_URL ?? "").trim();
|
|
259
|
+
if (!raw) return null;
|
|
260
|
+
return normalizeDomain(raw) || null;
|
|
261
|
+
};
|
|
262
|
+
const getGitHubBaseUrl = () => {
|
|
263
|
+
const resolvedDomain = getEnterpriseDomain();
|
|
264
|
+
return resolvedDomain ? `https://${resolvedDomain}` : GITHUB_BASE_URL;
|
|
265
|
+
};
|
|
266
|
+
const getGitHubApiBaseUrl = () => {
|
|
267
|
+
const resolvedDomain = getEnterpriseDomain();
|
|
268
|
+
return resolvedDomain ? `https://${resolvedDomain}/api/v3` : GITHUB_API_BASE_URL;
|
|
269
|
+
};
|
|
270
|
+
const getOpencodeOauthHeaders = () => {
|
|
271
|
+
return {
|
|
272
|
+
Accept: "application/json",
|
|
273
|
+
"Content-Type": "application/json",
|
|
274
|
+
"User-Agent": "opencode/1.2.16 ai-sdk/provider-utils/3.0.21 runtime/bun/1.3.10, opencode/1.2.16"
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
const getOauthUrls = () => {
|
|
278
|
+
const githubBaseUrl = getGitHubBaseUrl();
|
|
279
|
+
return {
|
|
280
|
+
deviceCodeUrl: `${githubBaseUrl}/login/device/code`,
|
|
281
|
+
accessTokenUrl: `${githubBaseUrl}/login/oauth/access_token`
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
const getOauthAppConfig = () => {
|
|
285
|
+
if (isOpencodeOauthApp()) return {
|
|
286
|
+
clientId: OPENCODE_GITHUB_CLIENT_ID,
|
|
287
|
+
headers: getOpencodeOauthHeaders(),
|
|
288
|
+
scope: GITHUB_APP_SCOPES
|
|
289
|
+
};
|
|
290
|
+
return {
|
|
291
|
+
clientId: GITHUB_CLIENT_ID,
|
|
292
|
+
headers: standardHeaders(),
|
|
293
|
+
scope: GITHUB_APP_SCOPES
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
const prepareInteractionHeaders = (sessionId, isSubagent, headers) => {
|
|
297
|
+
const sendInteractionHeaders = !isOpencodeOauthApp();
|
|
298
|
+
if (isSubagent) {
|
|
299
|
+
headers["x-initiator"] = "agent";
|
|
300
|
+
if (sendInteractionHeaders) headers["x-interaction-type"] = "conversation-subagent";
|
|
301
|
+
}
|
|
302
|
+
if (sessionId && sendInteractionHeaders) headers["x-interaction-id"] = sessionId;
|
|
303
|
+
};
|
|
248
304
|
const standardHeaders = () => ({
|
|
249
305
|
"content-type": "application/json",
|
|
250
306
|
accept: "application/json"
|
|
@@ -253,8 +309,21 @@ const COPILOT_VERSION = "0.38.2";
|
|
|
253
309
|
const EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`;
|
|
254
310
|
const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`;
|
|
255
311
|
const API_VERSION = "2025-10-01";
|
|
256
|
-
const copilotBaseUrl = (account) =>
|
|
312
|
+
const copilotBaseUrl = (account) => {
|
|
313
|
+
const enterpriseDomain = getEnterpriseDomain();
|
|
314
|
+
if (enterpriseDomain) return `https://copilot-api.${enterpriseDomain}`;
|
|
315
|
+
return account.accountType === "individual" ? "https://api.githubcopilot.com" : `https://api.${account.accountType}.githubcopilot.com`;
|
|
316
|
+
};
|
|
257
317
|
const copilotHeaders = (account, vision = false, requestId) => {
|
|
318
|
+
if (isOpencodeOauthApp()) {
|
|
319
|
+
const headers$1 = {
|
|
320
|
+
Authorization: `Bearer ${account.copilotToken ?? account.githubToken}`,
|
|
321
|
+
...getOpencodeOauthHeaders(),
|
|
322
|
+
"Openai-Intent": "conversation-edits"
|
|
323
|
+
};
|
|
324
|
+
if (vision) headers$1["Copilot-Vision-Request"] = "true";
|
|
325
|
+
return headers$1;
|
|
326
|
+
}
|
|
258
327
|
const resolvedRequestId = requestId ?? randomUUID();
|
|
259
328
|
const headers = {
|
|
260
329
|
Authorization: `Bearer ${account.copilotToken}`,
|
|
@@ -288,6 +357,7 @@ const githubHeaders = (account) => ({
|
|
|
288
357
|
const GITHUB_BASE_URL = "https://github.com";
|
|
289
358
|
const GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98";
|
|
290
359
|
const GITHUB_APP_SCOPES = ["read:user"].join(" ");
|
|
360
|
+
const OPENCODE_GITHUB_CLIENT_ID = "Ov23li8tweQw6odWQebz";
|
|
291
361
|
|
|
292
362
|
//#endregion
|
|
293
363
|
//#region src/lib/error.ts
|
|
@@ -324,7 +394,7 @@ async function forwardError(c, error) {
|
|
|
324
394
|
//#region src/services/github/get-copilot-usage.ts
|
|
325
395
|
const getCopilotUsage = async (account) => {
|
|
326
396
|
const ctx = account ?? accountFromState();
|
|
327
|
-
const response = await fetch(`${
|
|
397
|
+
const response = await fetch(`${getGitHubApiBaseUrl()}/copilot_internal/user`, { headers: githubHeaders(ctx) });
|
|
328
398
|
if (!response.ok) throw new HTTPError("Failed to get Copilot usage", response);
|
|
329
399
|
return await response.json();
|
|
330
400
|
};
|
|
@@ -333,7 +403,8 @@ const getCopilotUsage = async (account) => {
|
|
|
333
403
|
//#region src/services/github/get-user.ts
|
|
334
404
|
async function getGitHubUser(account) {
|
|
335
405
|
const token = account?.githubToken ?? state.githubToken;
|
|
336
|
-
|
|
406
|
+
if (!token) throw new Error("GitHub token not set");
|
|
407
|
+
const response = await fetch(`${getGitHubApiBaseUrl()}/user`, { headers: {
|
|
337
408
|
authorization: `token ${token}`,
|
|
338
409
|
...standardHeaders()
|
|
339
410
|
} });
|
|
@@ -476,7 +547,7 @@ const getUUID = (content) => {
|
|
|
476
547
|
//#region src/services/github/get-copilot-token.ts
|
|
477
548
|
const getCopilotToken = async (account) => {
|
|
478
549
|
const ctx = account ?? accountFromState();
|
|
479
|
-
const response = await fetch(`${
|
|
550
|
+
const response = await fetch(`${getGitHubApiBaseUrl()}/copilot_internal/v2/token`, { headers: githubHeaders(ctx) });
|
|
480
551
|
if (!response.ok) throw new HTTPError("Failed to get Copilot token", response);
|
|
481
552
|
return await response.json();
|
|
482
553
|
};
|
|
@@ -510,6 +581,7 @@ You interact with the user through a terminal. You have 2 ways of communicating
|
|
|
510
581
|
- Tone of your updates MUST match your personality.`;
|
|
511
582
|
const defaultConfig = {
|
|
512
583
|
auth: { apiKeys: [] },
|
|
584
|
+
providers: {},
|
|
513
585
|
extraPrompts: {
|
|
514
586
|
"gpt-5-mini": gpt5ExplorationPrompt,
|
|
515
587
|
"gpt-5.3-codex": gpt5CommentaryPrompt,
|
|
@@ -520,7 +592,8 @@ const defaultConfig = {
|
|
|
520
592
|
responsesApiContextManagementModels: [],
|
|
521
593
|
modelReasoningEfforts: {
|
|
522
594
|
"gpt-5-mini": "low",
|
|
523
|
-
"gpt-5.3-codex": "xhigh"
|
|
595
|
+
"gpt-5.3-codex": "xhigh",
|
|
596
|
+
"gpt-5.4": "xhigh"
|
|
524
597
|
},
|
|
525
598
|
allowOriginalModelNamesForAliases: false,
|
|
526
599
|
useFunctionApplyPatch: true,
|
|
@@ -809,6 +882,34 @@ function isForceAgentEnabled() {
|
|
|
809
882
|
function shouldCompactUseSmallModel() {
|
|
810
883
|
return getConfig().compactUseSmallModel ?? true;
|
|
811
884
|
}
|
|
885
|
+
function normalizeProviderBaseUrl(url) {
|
|
886
|
+
return url.trim().replace(/\/+$/u, "");
|
|
887
|
+
}
|
|
888
|
+
function getProviderConfig(name) {
|
|
889
|
+
const providerName = name.trim();
|
|
890
|
+
if (!providerName) return null;
|
|
891
|
+
const provider = getConfig().providers?.[providerName];
|
|
892
|
+
if (!provider) return null;
|
|
893
|
+
if (provider.enabled === false) return null;
|
|
894
|
+
const type = provider.type ?? "anthropic";
|
|
895
|
+
if (type !== "anthropic") {
|
|
896
|
+
consola.warn(`Provider ${providerName} is ignored because only anthropic type is supported`);
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
const baseUrl = normalizeProviderBaseUrl(provider.baseUrl ?? "");
|
|
900
|
+
const apiKey = (provider.apiKey ?? "").trim();
|
|
901
|
+
if (!baseUrl || !apiKey) {
|
|
902
|
+
consola.warn(`Provider ${providerName} is enabled but missing baseUrl or apiKey`);
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
return {
|
|
906
|
+
name: providerName,
|
|
907
|
+
type,
|
|
908
|
+
baseUrl,
|
|
909
|
+
apiKey,
|
|
910
|
+
models: provider.models
|
|
911
|
+
};
|
|
912
|
+
}
|
|
812
913
|
|
|
813
914
|
//#endregion
|
|
814
915
|
//#region src/lib/accounts-manager-auth.ts
|
|
@@ -1615,5 +1716,5 @@ var AccountsManager = class {
|
|
|
1615
1716
|
const accountsManager = new AccountsManager();
|
|
1616
1717
|
|
|
1617
1718
|
//#endregion
|
|
1618
|
-
export {
|
|
1619
|
-
//# sourceMappingURL=accounts-manager-
|
|
1719
|
+
export { HTTPError, PATHS, accountFromState, accountsManager, addAccountToRegistry, cacheMacMachineId, cacheVSCodeVersion, cacheVsCodeSessionId, copilotBaseUrl, copilotHeaders, ensurePaths, forwardError, generateRequestIdFromPayload, getAliasTargetSet, getConfig, getCopilotUsage, getExtraPromptForModel, getGitHubUser, getModelAliases, getModelAliasesInfo, getModelRefreshIntervalMs, getOauthAppConfig, getOauthUrls, getProviderConfig, getReasoningEffortForModel, getRootSessionId, getSmallModel, getUUID, isForceAgentEnabled, isFreeModelLoadBalancingEnabled, isMessageStartInputTokensFallbackEnabled, isNullish, isResponsesApiContextManagementModel, listAccountsFromRegistry, loadAccountToken, mergeConfigWithDefaults, prepareInteractionHeaders, removeAccountFromRegistry, removeAccountToken, saveAccountToken, saveRegistry, shouldCompactUseSmallModel, sleep, state };
|
|
1720
|
+
//# sourceMappingURL=accounts-manager-BeKvbv0T.js.map
|