@dianshuv/copilot-api 0.9.0 → 0.10.0
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 +157 -29
- package/dist/main.mjs +1162 -1272
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
- **Graceful shutdown**: 4-phase shutdown sequence — stops accepting requests, waits for in-flight requests to complete, sends abort signal, then force-closes. Configurable via `--shutdown-graceful-wait` and `--shutdown-abort-wait`.
|
|
16
16
|
- **Stream repetition detection**: Detects when models get stuck in repetitive output loops using KMP-based pattern matching and logs a warning.
|
|
17
17
|
- **Stale request reaping**: Automatically force-fails requests that exceed a configurable maximum age (default 600s) to prevent resource leaks.
|
|
18
|
-
- **Gemini API compatibility**: `/v1beta/models` endpoints translate Gemini API requests to OpenAI format for Copilot. Enables Google Gemini CLI to use Copilot models via `GOOGLE_GEMINI_BASE_URL` environment variable.
|
|
19
18
|
- **PostHog analytics**: Optional PostHog Cloud integration (`--posthog-key`) sends per-request token usage events for long-term trend analysis. Free tier (1M events/month) is more than sufficient for individual use.
|
|
20
19
|
|
|
21
20
|
## Quick Start
|
|
@@ -70,6 +69,7 @@ make down
|
|
|
70
69
|
| `--github-token`, `-g` | Provide GitHub token directly | none |
|
|
71
70
|
| `--claude-code`, `-c` | Generate Claude Code launch command | false |
|
|
72
71
|
| `--show-token` | Show tokens on fetch/refresh | false |
|
|
72
|
+
| `--show-all-models` | Show all upstream models including the hardcoded blacklist (see "Hidden Models" below) | false |
|
|
73
73
|
| `--proxy-env` | Use proxy from environment | false |
|
|
74
74
|
| `--no-history` | Disable request history UI at `/history` | false |
|
|
75
75
|
| `--history-limit` | Max history entries in memory | 1000 |
|
|
@@ -80,6 +80,36 @@ make down
|
|
|
80
80
|
| `--context-editing` | Context editing mode: off, clear-thinking, clear-tooluse, clear-both | off |
|
|
81
81
|
| `--timezone-offset` | Timezone offset in hours from UTC for log timestamps (e.g., +8, -5, 0) | +8 |
|
|
82
82
|
| `--posthog-key` | PostHog API key for token usage analytics (opt-in) | none |
|
|
83
|
+
| `--api-key` | Proxy API key for inbound authentication (see [Authentication](#authentication)). Empty = disabled | none |
|
|
84
|
+
|
|
85
|
+
### Hidden Models
|
|
86
|
+
|
|
87
|
+
By default the proxy hides 30 stale / duplicate / unused upstream model ids from
|
|
88
|
+
its **listing surfaces** (`/v1/models`, the startup banner, and the
|
|
89
|
+
`--claude-code` interactive prompts). Pass `--show-all-models` to see the full
|
|
90
|
+
upstream catalogue.
|
|
91
|
+
|
|
92
|
+
**Important**: this is a *display* filter only. The remaining POST endpoints
|
|
93
|
+
(`/v1/chat/completions`, `/v1/messages`, `/v1/responses`) do **not** reject
|
|
94
|
+
hidden ids — an explicit `POST` with a hidden id is still forwarded to
|
|
95
|
+
upstream verbatim, even without `--show-all-models`. (The embedding and
|
|
96
|
+
Gemini ids in the list have no callable endpoint in this proxy at all;
|
|
97
|
+
they're hidden purely to denoise the `/v1/models` listing — the upstream
|
|
98
|
+
`/models` catalogue still returns them.)
|
|
99
|
+
|
|
100
|
+
To change the blacklist, edit `src/lib/hidden-models.ts` and re-publish (this
|
|
101
|
+
fork uses `/publish`).
|
|
102
|
+
|
|
103
|
+
Currently hidden (grouped):
|
|
104
|
+
|
|
105
|
+
- **Legacy GPT** — `gpt-3.5-turbo`, `gpt-3.5-turbo-0613`, `gpt-4`, `gpt-4-0613`, `gpt-4-0125-preview`
|
|
106
|
+
- **GPT-4o family** — `gpt-4o`, `gpt-4o-mini`, `gpt-4-o-preview`, `gpt-4o-2024-05-13`, `gpt-4o-2024-08-06`, `gpt-4o-2024-11-20`, `gpt-4o-mini-2024-07-18`
|
|
107
|
+
- **GPT-4.1 family** — `gpt-4.1`, `gpt-4.1-2025-04-14`, `gpt-41-copilot`
|
|
108
|
+
- **Older / smaller GPT-5** — `gpt-5-mini`, `gpt-5.3-codex`, `gpt-5.4`
|
|
109
|
+
- **All embeddings** — `text-embedding-ada-002`, `text-embedding-3-small`, `text-embedding-3-small-inference`
|
|
110
|
+
- **Older Gemini** — `gemini-2.5-pro`, `gemini-3-flash-preview`
|
|
111
|
+
- **Older / variant Claude** — `claude-opus-4.5`, `claude-opus-4.6`, `claude-opus-4.7-high`, `claude-opus-4.7-xhigh`, `claude-sonnet-4.5`
|
|
112
|
+
- **Special-purpose** — `mai-code-1-flash-internal`, `trajectory-compaction`
|
|
83
113
|
|
|
84
114
|
### Patch-Claude Command Options
|
|
85
115
|
|
|
@@ -98,7 +128,6 @@ make down
|
|
|
98
128
|
|----------|--------|-------------|
|
|
99
129
|
| `/v1/chat/completions` | POST | Chat completions |
|
|
100
130
|
| `/v1/models` | GET | List available models |
|
|
101
|
-
| `/v1/embeddings` | POST | Text embeddings |
|
|
102
131
|
| `/v1/responses` | POST | Responses API (for codex models) |
|
|
103
132
|
|
|
104
133
|
### Anthropic Compatible
|
|
@@ -109,14 +138,6 @@ make down
|
|
|
109
138
|
| `/v1/messages/count_tokens` | POST | Token counting |
|
|
110
139
|
| `/v1/event_logging/batch` | POST | Event logging (no-op) |
|
|
111
140
|
|
|
112
|
-
### Gemini Compatible
|
|
113
|
-
|
|
114
|
-
| Endpoint | Method | Description |
|
|
115
|
-
|----------|--------|-------------|
|
|
116
|
-
| `/v1beta/models/{model}:generateContent` | POST | Non-streaming generation |
|
|
117
|
-
| `/v1beta/models/{model}:streamGenerateContent` | POST | Streaming generation (SSE) |
|
|
118
|
-
| `/v1beta/models/{model}:countTokens` | POST | Token counting |
|
|
119
|
-
|
|
120
141
|
### Utility
|
|
121
142
|
|
|
122
143
|
| Endpoint | Method | Description |
|
|
@@ -137,6 +158,130 @@ When enabled (default), auto-truncate automatically compacts conversation histor
|
|
|
137
158
|
- **Smart compression**: With `--compress-tool-results`, old tool results are compressed before removing messages, preserving more conversation context.
|
|
138
159
|
- **Orphan filtering**: After truncation, orphaned tool results (without matching tool calls) are automatically removed.
|
|
139
160
|
|
|
161
|
+
## Authentication
|
|
162
|
+
|
|
163
|
+
By default the proxy is **unauthenticated**: every endpoint is open and any
|
|
164
|
+
request passes through unchanged. You can optionally protect the proxy's
|
|
165
|
+
*inbound* surface with a **Proxy API key** (this is your own secret for this
|
|
166
|
+
proxy — it is unrelated to the GitHub OAuth / Copilot token the proxy uses
|
|
167
|
+
upstream). When a key is configured, all endpoints **except `/` and `/health`**
|
|
168
|
+
require it (fail-closed: any route not on that exemption list — including future
|
|
169
|
+
ones — is protected). Genuine CORS preflight `OPTIONS` requests are also let
|
|
170
|
+
through (they carry no payload), so browser clients aren't tripped by an opaque
|
|
171
|
+
preflight failure.
|
|
172
|
+
|
|
173
|
+
### Enabling it
|
|
174
|
+
|
|
175
|
+
Provide the key via either source:
|
|
176
|
+
|
|
177
|
+
- `--api-key <key>` flag
|
|
178
|
+
- `COPILOT_API_KEY` environment variable
|
|
179
|
+
|
|
180
|
+
Rules:
|
|
181
|
+
|
|
182
|
+
- **Precedence**: when both are set to a non-empty value, the **flag wins** and
|
|
183
|
+
the env value is ignored.
|
|
184
|
+
- **Trim + empty disables**: each source is trimmed first; a value that is empty
|
|
185
|
+
or whitespace-only (`--api-key ""`, `COPILOT_API_KEY=`) counts as *not
|
|
186
|
+
provided*. If neither source yields a non-empty value, auth stays **disabled**
|
|
187
|
+
(the default).
|
|
188
|
+
|
|
189
|
+
The startup banner prints whether auth is on/off and, when on, the source
|
|
190
|
+
(`flag` / `env`) — but **never** the key value itself.
|
|
191
|
+
|
|
192
|
+
> [!CAUTION]
|
|
193
|
+
> **Ambient `COPILOT_API_KEY` footgun.** Because the env var is read on every
|
|
194
|
+
> start, a `COPILOT_API_KEY` exported anywhere the process can see it (your
|
|
195
|
+
> shell profile, a `.env` sourced into the environment, a systemd unit, a
|
|
196
|
+
> container env) **silently enables authentication** for that instance — even
|
|
197
|
+
> when you did not pass `--api-key`. If a previously-open instance suddenly
|
|
198
|
+
> returns `401`, check for an inherited `COPILOT_API_KEY`. The startup banner's
|
|
199
|
+
> `source: env` line makes this visible.
|
|
200
|
+
|
|
201
|
+
### Network binding
|
|
202
|
+
|
|
203
|
+
The proxy **binds all interfaces (`0.0.0.0`) by default**, so a key-less
|
|
204
|
+
instance is reachable from anywhere on your network. For a local-only,
|
|
205
|
+
unauthenticated instance, bind loopback explicitly:
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
copilot-api start --host 127.0.0.1
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The startup banner reports the **real** bind address (`0.0.0.0` for an
|
|
212
|
+
all-interfaces bind, or the narrowed host you passed), so a wide-open bind is
|
|
213
|
+
never hidden.
|
|
214
|
+
|
|
215
|
+
### Choosing a key
|
|
216
|
+
|
|
217
|
+
Use a **high-entropy** secret (e.g. `openssl rand -hex 32`), not a guessable
|
|
218
|
+
word. The comparison is constant-time, but a weak key can still be guessed
|
|
219
|
+
offline or brute-forced over the network.
|
|
220
|
+
|
|
221
|
+
### Migrating clients
|
|
222
|
+
|
|
223
|
+
Once a key is configured, clients that previously sent a placeholder must send
|
|
224
|
+
the real key. Two header shapes are accepted and **either** is sufficient:
|
|
225
|
+
|
|
226
|
+
- **OpenAI-compatible clients** → `Authorization: Bearer <key>` (the scheme is
|
|
227
|
+
matched case-insensitively; a bare value with no `Bearer ` prefix is also
|
|
228
|
+
tolerated).
|
|
229
|
+
- **Anthropic-native clients** → `x-api-key: <key>`.
|
|
230
|
+
|
|
231
|
+
If both headers are present, a match on *either* one is accepted.
|
|
232
|
+
|
|
233
|
+
Per client:
|
|
234
|
+
|
|
235
|
+
- **opencode** (`opencode.json`): set `provider.<name>.options.apiKey` from
|
|
236
|
+
`"dummy"` to your real key (the `@ai-sdk/openai-compatible` provider sends it
|
|
237
|
+
as `Authorization: Bearer`).
|
|
238
|
+
- **Claude Code** (`.claude/settings.json`, see [below](#using-with-claude-code)):
|
|
239
|
+
change `ANTHROPIC_AUTH_TOKEN` from `"dummy"` to your real key. Claude Code
|
|
240
|
+
talks to the proxy's `/v1/messages` (Anthropic) surface; the auth gate accepts
|
|
241
|
+
the token from either header. `--claude-code` keeps emitting the `"dummy"`
|
|
242
|
+
placeholder on purpose (so the secret never lands in your clipboard / shell
|
|
243
|
+
history) and prints a reminder to replace it when auth is on.
|
|
244
|
+
- **Raw OpenAI SDK** → `Authorization: Bearer <key>`; **raw Anthropic SDK** →
|
|
245
|
+
`x-api-key: <key>`.
|
|
246
|
+
|
|
247
|
+
> [!TIP]
|
|
248
|
+
> **Keep the key out of shell history.** Don't paste the literal key onto a
|
|
249
|
+
> command line (e.g. `curl -H "Authorization: Bearer sk-real-key" …`), or it
|
|
250
|
+
> lands in `~/.zsh_history`. Read it from an env var or a file instead:
|
|
251
|
+
>
|
|
252
|
+
> ```sh
|
|
253
|
+
> # Put the key in an env var read from a non-echoed prompt or a 0600 file,
|
|
254
|
+
> # then reference the var — the literal secret never appears in argv/history.
|
|
255
|
+
> read -rs COPILOT_API_KEY # typed key is not echoed or saved
|
|
256
|
+
> export COPILOT_API_KEY
|
|
257
|
+
> curl -H "Authorization: Bearer $COPILOT_API_KEY" http://127.0.0.1:4141/v1/models
|
|
258
|
+
> ```
|
|
259
|
+
|
|
260
|
+
A failed auth returns `401` with a client-appropriate body: requests to the
|
|
261
|
+
Anthropic surface (`/v1/messages`, `/v1/messages/count_tokens`) get
|
|
262
|
+
`{"type":"error","error":{"type":"authentication_error", …}}`; every other
|
|
263
|
+
endpoint gets the OpenAI-style `{"error":{"code":"invalid_api_key", …}}`.
|
|
264
|
+
Missing and wrong keys return the identical body (no oracle), always with a
|
|
265
|
+
`WWW-Authenticate: Bearer` header.
|
|
266
|
+
|
|
267
|
+
### Browser tools when auth is on
|
|
268
|
+
|
|
269
|
+
The request logger does **not** record the `Authorization` / `x-api-key`
|
|
270
|
+
headers, so your key never lands in logs. But the browser-based tools can't send
|
|
271
|
+
a custom header, so when auth is on they stop working:
|
|
272
|
+
|
|
273
|
+
- **History UI** (`/history`) and its live-update WebSocket (`/history/ws`) — the
|
|
274
|
+
page load and socket upgrade are gated like any other protected route.
|
|
275
|
+
- **External Usage Viewer** (the `ericc-ch.github.io` link in the banner) — it
|
|
276
|
+
fetches `/usage`, which is now protected, so it can't load your data.
|
|
277
|
+
|
|
278
|
+
Workarounds:
|
|
279
|
+
|
|
280
|
+
- Run a **separate local, unauthenticated instance** (`--host 127.0.0.1` with no
|
|
281
|
+
`--api-key`) for the browser UI, or
|
|
282
|
+
- Hit the history JSON API directly with a key, e.g.
|
|
283
|
+
`curl -H "Authorization: Bearer $COPILOT_API_KEY" http://127.0.0.1:4141/history/api/entries`.
|
|
284
|
+
|
|
140
285
|
## Using with Claude Code
|
|
141
286
|
|
|
142
287
|
Create `.claude/settings.json` in your project:
|
|
@@ -146,8 +291,8 @@ Create `.claude/settings.json` in your project:
|
|
|
146
291
|
"env": {
|
|
147
292
|
"ANTHROPIC_BASE_URL": "http://localhost:4141",
|
|
148
293
|
"ANTHROPIC_AUTH_TOKEN": "dummy",
|
|
149
|
-
"ANTHROPIC_MODEL": "
|
|
150
|
-
"ANTHROPIC_SMALL_FAST_MODEL": "
|
|
294
|
+
"ANTHROPIC_MODEL": "claude-opus-4.8",
|
|
295
|
+
"ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4.5",
|
|
151
296
|
"DISABLE_NON_ESSENTIAL_MODEL_CALLS": "1",
|
|
152
297
|
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
|
|
153
298
|
},
|
|
@@ -163,23 +308,6 @@ Or use the interactive setup:
|
|
|
163
308
|
bun run start --claude-code
|
|
164
309
|
```
|
|
165
310
|
|
|
166
|
-
## Using with Gemini CLI
|
|
167
|
-
|
|
168
|
-
```bash
|
|
169
|
-
# Start the proxy
|
|
170
|
-
copilot-api start
|
|
171
|
-
|
|
172
|
-
# Configure Gemini CLI to use the proxy
|
|
173
|
-
export GEMINI_API_KEY="placeholder"
|
|
174
|
-
export GOOGLE_GEMINI_BASE_URL="http://localhost:4141"
|
|
175
|
-
|
|
176
|
-
# Basic conversation
|
|
177
|
-
gemini -p "Explain this code"
|
|
178
|
-
|
|
179
|
-
# Pipe review
|
|
180
|
-
git diff HEAD~1 | gemini -p "Review this diff for bugs"
|
|
181
|
-
```
|
|
182
|
-
|
|
183
311
|
## Upstream Project
|
|
184
312
|
|
|
185
313
|
For the original project documentation, features, and updates, see: [ericc-ch/copilot-api](https://github.com/ericc-ch/copilot-api)
|