ghc-proxy 0.9.3 → 0.10.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 +59 -37
- package/dist/main.mjs +44670 -42595
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/ghc-proxy)
|
|
4
4
|
[](https://github.com/wxxb789/ghc-proxy/actions/workflows/ci.yml)
|
|
5
|
-
[](https://github.com/wxxb789/ghc-proxy/blob/
|
|
5
|
+
[](https://github.com/wxxb789/ghc-proxy/blob/main/LICENSE)
|
|
6
6
|
|
|
7
7
|
A proxy that turns your GitHub Copilot subscription into an OpenAI and Anthropic compatible API. Use it to power [Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview), [Cursor](https://www.cursor.com/), or any tool that speaks the OpenAI Chat Completions, OpenAI Responses, or Anthropic Messages protocol.
|
|
8
8
|
|
|
@@ -107,13 +107,13 @@ bunx ghc-proxy@latest selfcheck # Probe tokenizer chunks and Bun/Node runti
|
|
|
107
107
|
|
|
108
108
|
| Option | Alias | Default | Description |
|
|
109
109
|
|--------|-------|---------|-------------|
|
|
110
|
-
| `--port` | `-p` | `4141` | Port to listen on |
|
|
110
|
+
| `--port` | `-p` | `4141` | Port to listen on (`1..65535`; malformed or zero values fail before startup) |
|
|
111
111
|
| `--verbose` | `-v` | `false` | Enable verbose logging |
|
|
112
112
|
| `--account-type` | `-a` | `individual` | `individual`, `business`, or `enterprise` |
|
|
113
113
|
| `--rate-limit` | `-r` | -- | Minimum seconds between requests |
|
|
114
114
|
| `--wait` | `-w` | `false` | Queue requests instead of rejecting with 429 when `--rate-limit` cooldown has not elapsed (requires `--rate-limit`) |
|
|
115
115
|
| `--manual` | -- | `false` | Manually approve each request |
|
|
116
|
-
| `--github-token` | `-g` | -- |
|
|
116
|
+
| `--github-token` | `-g` | -- | Use a GitHub token for this process only (normally obtained with `auth`); this flag does not persist it to `config.json` |
|
|
117
117
|
| `--claude-code` | `-c` | `false` | Generate a Claude Code launch command |
|
|
118
118
|
| `--show-token` | -- | `false` | Display tokens on auth and refresh |
|
|
119
119
|
| `--dump-failed-payloads` | `-D` | `false` | Dump failed `/responses` payloads on upstream 400 errors for debugging. Can also be enabled with `DUMP_FAILED_PAYLOADS=1`. |
|
|
@@ -189,30 +189,31 @@ All fields are optional. The full schema:
|
|
|
189
189
|
|
|
190
190
|
| Field | Type | Default | Description |
|
|
191
191
|
|-------|------|---------|-------------|
|
|
192
|
-
| `githubToken` | `string` |
|
|
193
|
-
| `modelRewrites` | `{ from, to }[]` |
|
|
194
|
-
| `modelFallback` | `object` |
|
|
192
|
+
| `githubToken` | `string` | unset | Persisted GitHub token. The device-code flow (`auth` or first startup) writes it automatically; `start --github-token` is runtime-only and does not write this field |
|
|
193
|
+
| `modelRewrites` | `{ from, to }[]` | `[]` | Glob-pattern model substitution rules (see [Model Rewrites](#model-rewrites)) |
|
|
194
|
+
| `modelFallback` | `object` | built-in family defaults | Override default model fallbacks (see [Customizing Fallbacks](#customizing-fallbacks)) |
|
|
195
195
|
| `modelFallback.claudeOpus` | `string` | `claude-opus-5` | Fallback for `claude-opus-*` models |
|
|
196
196
|
| `modelFallback.claudeSonnet` | `string` | `claude-sonnet-5` | Fallback for `claude-sonnet-*` models |
|
|
197
197
|
| `modelFallback.claudeHaiku` | `string` | `claude-haiku-4.5` | Fallback for `claude-haiku-*` models |
|
|
198
|
-
| `smallModel` | `string` |
|
|
198
|
+
| `smallModel` | `string` | unset | Target model for compact request routing (see [Small-Model Routing](#small-model-routing)) |
|
|
199
199
|
| `compactUseSmallModel` | `boolean` | `false` | Route compact/summarization requests to `smallModel` |
|
|
200
200
|
| `useFunctionApplyPatch` | `boolean` | `true` | Rewrite `apply_patch` custom tool as function tool on Responses path |
|
|
201
201
|
| `responsesApiAutoCompactInput` | `boolean` | `false` | Automatically trim Responses `input` to the latest `compaction` item |
|
|
202
202
|
| `responsesApiAutoContextManagement` | `boolean` | `false` | Automatically inject Responses `context_management` for selected models |
|
|
203
|
-
| `responsesApiContextManagementModels` | `string[]` |
|
|
204
|
-
| `responsesApiParameterFilters` | `{ models, params }[]` |
|
|
205
|
-
| `responsesApiParameterFiltersReplaceDefault` | `boolean` | `false` | Disable the built-in reasoning-model default rule so only your `responsesApiParameterFilters` apply |
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
203
|
+
| `responsesApiContextManagementModels` | `string[]` | `[]` | Models eligible for auto-injected Responses `context_management` |
|
|
204
|
+
| `responsesApiParameterFilters` | `{ models, params }[]` | `[]` | Extra rules to strip request parameters on the Responses boundary; the built-in reasoning-model rule remains active unless replaced (see [Responses Parameter Filters](#responses-parameter-filters)) |
|
|
205
|
+
| `responsesApiParameterFiltersReplaceDefault` | `boolean` | `false` | Disable the built-in reasoning-model default rule so only your `responsesApiParameterFilters` apply |
|
|
206
|
+
| `chatCompletionsUseMaxCompletionTokens` | `string[]` | `[]` | Extra model globs that rename Chat Completions `max_tokens` to `max_completion_tokens`; adds to the built-in `gpt-5.4` / `gpt-5.4-*` rules |
|
|
207
|
+
| `responsesOfficialEmulator` | `boolean` | `false` | Enable local OpenAI-style Responses state emulation for `previous_response_id`, `conversation`, retrieve, input_items, delete, and input_tokens |
|
|
208
|
+
| `responsesOfficialEmulatorTtlSeconds` | `number` | `14400` | In-memory TTL for locally emulated Responses state |
|
|
209
|
+
| `modelReasoningEfforts` | `Record<string, string>` | `{}`; unlisted models use `high` | Per-model reasoning effort defaults for Anthropic-to-Responses translation. Each value must be one of `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` (ascending) |
|
|
209
210
|
| `upstreamQueueConcurrency` | `number` | `10` | Maximum concurrent Copilot upstream requests |
|
|
210
211
|
| `upstreamQueueMaxRetries` | `number` | `1` | Maximum retries across capacity and approved pre-connection failures (`0..2`) |
|
|
211
212
|
| `upstreamRecoveryBudgetSeconds` | `number` | `60` | Shared recovery deadline after the first retryable outcome or active-cooldown encounter (`1..120` seconds) |
|
|
212
|
-
| `overloadFallbacks` | `Record<string, string>` |
|
|
213
|
+
| `overloadFallbacks` | `Record<string, string>` | `{}` (disabled) | Exact effective-model mappings for one opt-in fallback dispatch after terminal model `529` |
|
|
213
214
|
| `upstreamQueueBaseDelaySeconds` | `number` | `2` | Base delay (seconds) for upstream retry backoff when `Retry-After` is absent |
|
|
214
215
|
| `upstreamQueueMaxDelaySeconds` | `number` | `60` | Maximum computed backoff (seconds); does not clamp `Retry-After` |
|
|
215
|
-
| `gheDomain` | `string` |
|
|
216
|
+
| `gheDomain` | `string` | unset | GitHub Enterprise Cloud company domain (persisted automatically after GHE.com auth) |
|
|
216
217
|
|
|
217
218
|
Example:
|
|
218
219
|
|
|
@@ -229,9 +230,10 @@ Example:
|
|
|
229
230
|
"compactUseSmallModel": true,
|
|
230
231
|
"useFunctionApplyPatch": true,
|
|
231
232
|
"responsesApiAutoCompactInput": false,
|
|
232
|
-
"responsesApiAutoContextManagement": false,
|
|
233
|
-
"responsesApiContextManagementModels": ["gpt-5", "gpt-5-mini"],
|
|
234
|
-
"
|
|
233
|
+
"responsesApiAutoContextManagement": false,
|
|
234
|
+
"responsesApiContextManagementModels": ["gpt-5", "gpt-5-mini"],
|
|
235
|
+
"chatCompletionsUseMaxCompletionTokens": [],
|
|
236
|
+
"responsesOfficialEmulator": false,
|
|
235
237
|
"responsesOfficialEmulatorTtlSeconds": 14400,
|
|
236
238
|
"modelReasoningEfforts": {
|
|
237
239
|
"gpt-5": "high",
|
|
@@ -359,9 +361,11 @@ The proxy authenticates with GitHub using the [device code OAuth flow](https://d
|
|
|
359
361
|
|
|
360
362
|
When the Copilot token response includes `endpoints.api`, `ghc-proxy` now prefers that runtime API base automatically instead of relying only on the configured account type. This keeps enterprise/business routing aligned with the endpoint GitHub actually returned for the current token.
|
|
361
363
|
|
|
362
|
-
Incoming requests hit an [Elysia](https://elysiajs.com/) server. `chat/completions` requests are validated, normalized into the shared planning pipeline, and then forwarded to Copilot. `responses` requests use a native Responses path with explicit compatibility policies. `messages` requests are routed per-model and can use native Anthropic passthrough, the Responses translation path, or the existing chat-completions fallback. The translator tracks exact vs lossy vs unsupported behavior explicitly; see the [Messages Routing and Translation Guide](./docs/messages-routing-and-translation.md) and the [Anthropic Translation Matrix](./docs/anthropic-translation-matrix.md) for the current support surface.
|
|
363
|
-
|
|
364
|
-
|
|
364
|
+
Incoming requests hit an [Elysia](https://elysiajs.com/) server. `chat/completions` requests are validated, normalized into the shared planning pipeline, and then forwarded to Copilot. `responses` requests use a native Responses path with explicit compatibility policies. `messages` requests are routed per-model and can use native Anthropic passthrough, the Responses translation path, or the existing chat-completions fallback. The translator tracks exact vs lossy vs unsupported behavior explicitly; see the [Messages Routing and Translation Guide](./docs/messages-routing-and-translation.md) and the [Anthropic Translation Matrix](./docs/anthropic-translation-matrix.md) for the current support surface.
|
|
365
|
+
|
|
366
|
+
The built-in, read-only Dashboard projects process health, model routing, behavior, and recent request lifecycle metadata without storing request or response content. See [Dashboard Observability](./docs/design/dashboard-observability.md).
|
|
367
|
+
|
|
368
|
+
For Anthropic `search_result` blocks, an April 17, 2026 probe against `claude-opus-4.6` on Copilot native `/v1/messages` accepted top-level search results and pure search-result tool outputs, but rejected top-level `citations` and mixed text/search-result tool output arrays. The native path sanitizes those observed rejection cases, while translated paths flatten search results to text; re-run the probe before treating that dated upstream result as universal.
|
|
365
369
|
|
|
366
370
|
### Request Routing
|
|
367
371
|
|
|
@@ -402,22 +406,37 @@ This keeps the existing chat pipeline stable while allowing newer Copilot models
|
|
|
402
406
|
| `POST` | `/v1/messages` | Messages API with per-model routing across native Messages, Responses translation, or chat-completions fallback |
|
|
403
407
|
| `POST` | `/v1/messages/count_tokens` | Token counting |
|
|
404
408
|
|
|
405
|
-
**Utility:**
|
|
409
|
+
**Utility:**
|
|
406
410
|
|
|
407
411
|
| Method | Path | Description |
|
|
408
412
|
|--------|------|-------------|
|
|
409
413
|
| `GET` | `/health` | Liveness/readiness probe — returns `{ status, copilotToken, modelsLoaded, version }` |
|
|
410
414
|
| `GET` | `/usage` | Copilot quota / usage monitoring |
|
|
411
|
-
| `GET` | `/token` | Inspect the current Copilot token |
|
|
412
|
-
|
|
413
|
-
|
|
415
|
+
| `GET` | `/token` | Inspect the current Copilot token |
|
|
416
|
+
|
|
417
|
+
**Local Dashboard (read-only):**
|
|
418
|
+
|
|
419
|
+
| Method | Path | Description |
|
|
420
|
+
|--------|------|-------------|
|
|
421
|
+
| `GET` | `/dashboard` | Dashboard application |
|
|
422
|
+
| `GET` | `/dashboard/styles.css` | Dashboard stylesheet |
|
|
423
|
+
| `GET` | `/dashboard/app.js` | Dashboard client script |
|
|
424
|
+
| `GET` | `/dashboard/api/overview` | Process, authentication, quota, request, and queue summary |
|
|
425
|
+
| `GET` | `/dashboard/api/models` | Upstream model metadata and effective proxy capabilities |
|
|
426
|
+
| `GET` | `/dashboard/api/behavior` | Active routing, compatibility policies, strategies, and effect counters |
|
|
427
|
+
| `GET` | `/dashboard/api/requests` | Active requests and the most recent 256 completed request summaries |
|
|
428
|
+
|
|
429
|
+
Dashboard routes are restricted to local access and return `403` when the peer, request host, or supplied `Origin` fails the loopback/same-origin checks. They are excluded from request history and access logging. See [Dashboard Observability](./docs/design/dashboard-observability.md) for the projection and security contract.
|
|
430
|
+
|
|
431
|
+
> **Note:** The `/v1/` prefix is optional for OpenAI-compatible endpoints (`/chat/completions`, `/responses`, `/models`, `/embeddings`). Anthropic endpoints (`/v1/messages`, `/v1/messages/count_tokens`) require the `/v1` prefix. The utility and Dashboard endpoints are root-only and not exposed under `/v1`.
|
|
414
432
|
|
|
415
433
|
## Responses Compatibility
|
|
416
434
|
|
|
417
435
|
`/v1/responses` is designed to stay close to the OpenAI wire format while making Copilot limitations explicit:
|
|
418
436
|
|
|
419
|
-
- requests are validated before any mutation
|
|
420
|
-
-
|
|
437
|
+
- requests are validated before any mutation
|
|
438
|
+
- client-supplied `top_k` is rejected with `400` on the OpenAI Chat Completions and Responses boundaries because neither official OpenAI schema defines it; clients that send it by mistake receive an explicit error instead of a silent drop. Anthropic Messages `top_k` remains supported and is preserved when the proxy translates that request internally for Copilot
|
|
439
|
+
- common official request fields such as `conversation`, `previous_response_id`, `max_tool_calls`, `truncation`, `user`, `prompt`, and `text` are now modeled explicitly instead of relying on loose passthrough alone
|
|
421
440
|
- official `text.format` options are modeled explicitly, including `text`, `json_object`, and `json_schema`
|
|
422
441
|
- an opt-in `responsesOfficialEmulator` mode adds in-memory OpenAI-style state for `previous_response_id`, `conversation`, `GET /responses/{id}`, `GET /responses/{id}/input_items`, `DELETE /responses/{id}`, and `POST /responses/input_tokens`
|
|
423
442
|
- emulator state is memory-only and expires after `responsesOfficialEmulatorTtlSeconds` (default `14400`, or 4 hours)
|
|
@@ -427,9 +446,9 @@ This keeps the existing chat pipeline stable while allowing newer Copilot models
|
|
|
427
446
|
- automatic trimming of Responses `input` to the latest `compaction` item is disabled by default and only applies when `responsesApiAutoCompactInput` is `true`
|
|
428
447
|
- reasoning defaults for Anthropic -> Responses translation can be tuned with `modelReasoningEfforts`
|
|
429
448
|
- request parameters that a model rejects (e.g. `temperature`/`top_p` on reasoning models) are stripped on the Responses boundary rather than leaked upstream as a `400`; see [Responses Parameter Filters](#responses-parameter-filters)
|
|
430
|
-
- built-in web search (`web_search`, `web_search_preview`, and their dated variants) is forwarded to Copilot rather than blocked; every `/responses` model
|
|
431
|
-
- external image URLs on the Responses path fail explicitly with `400`; use `file_id` or data URL image input instead
|
|
432
|
-
- official `input_file` and `item_reference` input items are modeled explicitly and validated before
|
|
449
|
+
- built-in web search (`web_search`, `web_search_preview`, and their dated variants) is forwarded to Copilot rather than blocked; every `/responses` model reached by the August 4, 2026 acceptance sweep accepted the tool, while functional search execution was verified on `gpt-5.6-sol` and `gpt-5.6-terra`, see [docs/research/responses-web-search.md](docs/research/responses-web-search.md)
|
|
450
|
+
- external image URLs on the Responses path fail explicitly with `400`; use `file_id` or data URL image input instead
|
|
451
|
+
- official `input_file` and `item_reference` input items are modeled explicitly and validated, but the verified Copilot GPT Responses boundary is stateless: it rejects `store: true` and cannot resolve returned item IDs on later requests. The proxy deliberately applies a proxy-wide `store: false` policy, removes all `item_reference` items before dispatch, and removes `function_call_output` items whose `call_id` has no matching `function_call` in the same input array. Without the optional emulator, a caller that requested storage still receives a successful stateless response; retrieve/delete/continuation semantics are available only from the local emulator
|
|
433
452
|
|
|
434
453
|
Example opt-in configuration for these two Responses-specific policies:
|
|
435
454
|
|
|
@@ -451,7 +470,7 @@ Some Copilot models reject request parameters that the OpenAI wire format allows
|
|
|
451
470
|
|
|
452
471
|
This is expressed as a small rule engine that runs on both the native `/v1/responses` path and the `/v1/messages` → Responses translation path:
|
|
453
472
|
|
|
454
|
-
- **Built-in default rule:** any model that advertises `reasoning_effort` has `temperature`
|
|
473
|
+
- **Built-in default rule:** any model that advertises `reasoning_effort` has `temperature` stripped. It also has `top_p` stripped except for `*-codex` / `*-codex-*` models, which are exempt because the July 26, 2026 probe found the tested Codex model accepted `top_p` while its reasoning-model siblings rejected it. This exemption narrows only the built-in rule; an operator rule can still strip `top_p`.
|
|
455
474
|
- **`responsesApiParameterFilters`:** add your own rules. Each rule is `{ "models": [glob, ...], "params": [name, ...] }`; every rule whose `models` glob matches the resolved model contributes its `params`. Rules are **added** to the default (the union of parameters is stripped). Model globs use the same `*` wildcard as `modelRewrites`.
|
|
456
475
|
- **`responsesApiParameterFiltersReplaceDefault`:** set to `true` to disable the built-in reasoning-model rule, so only your `responsesApiParameterFilters` apply — use this to fully **overwrite** the default behavior.
|
|
457
476
|
|
|
@@ -461,7 +480,7 @@ Stripped parameters are removed entirely (never sent as `null`), because upstrea
|
|
|
461
480
|
{
|
|
462
481
|
"responsesApiParameterFilters": [
|
|
463
482
|
{ "models": ["gpt-5*", "o1*"], "params": ["temperature", "top_p"] },
|
|
464
|
-
{ "models": ["some-model"], "params": ["
|
|
483
|
+
{ "models": ["some-model"], "params": ["service_tier"] }
|
|
465
484
|
],
|
|
466
485
|
"responsesApiParameterFiltersReplaceDefault": false
|
|
467
486
|
}
|
|
@@ -516,16 +535,19 @@ services:
|
|
|
516
535
|
```bash
|
|
517
536
|
git clone https://github.com/wxxb789/ghc-proxy.git
|
|
518
537
|
cd ghc-proxy
|
|
519
|
-
bun install
|
|
520
|
-
bun run dev
|
|
538
|
+
bun install
|
|
539
|
+
bun run dev # Start with --watch
|
|
540
|
+
# Or use the production-style source command:
|
|
541
|
+
bun run start
|
|
521
542
|
```
|
|
522
543
|
|
|
523
544
|
## Development
|
|
524
545
|
|
|
525
546
|
```bash
|
|
526
|
-
bun install # Install dependencies
|
|
527
|
-
bun run dev # Start with --watch
|
|
528
|
-
bun run
|
|
547
|
+
bun install # Install dependencies
|
|
548
|
+
bun run dev # Start with --watch
|
|
549
|
+
bun run start # Start without --watch
|
|
550
|
+
bun run build # Build with tsdown
|
|
529
551
|
bun run lint # ESLint
|
|
530
552
|
bun run typecheck # tsc --noEmit
|
|
531
553
|
bun test # Run tests
|