@openhoo/hoopilot 0.10.0 → 1.1.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 +225 -150
- package/dist/{chunk-7GSQVYYT.js → chunk-JU6F5L34.js} +9 -5
- package/dist/chunk-JU6F5L34.js.map +1 -0
- package/dist/cli.js +212 -22
- package/dist/cli.js.map +1 -1
- package/dist/codexx.js +1 -1
- package/dist/index.cjs +190 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +189 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-7GSQVYYT.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,34 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Hoopilot
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@openhoo/hoopilot)
|
|
4
4
|
[](https://github.com/openhoo/hoopilot/actions/workflows/ci.yml)
|
|
5
5
|
|
|
6
|
-
OpenAI- and Anthropic-compatible
|
|
6
|
+
Hoopilot is a local OpenAI- and Anthropic-compatible proxy for GitHub Copilot accounts. It runs on Bun and exposes OpenAI-style `/v1/chat/completions`, `/v1/responses`, `/v1/completions`, and `/v1/models` routes, plus Claude Code-compatible `/v1/messages` and `/v1/messages/count_tokens` routes.
|
|
7
7
|
|
|
8
|
-
This project uses GitHub Copilot
|
|
8
|
+
This project uses GitHub Copilot service endpoints and is not an official GitHub product. Upstream behavior can change without notice. Use Hoopilot only with accounts and usage patterns you are allowed to use.
|
|
9
|
+
|
|
10
|
+
## Highlights
|
|
11
|
+
|
|
12
|
+
- Browser-based GitHub Copilot OAuth login with a local credential store.
|
|
13
|
+
- OpenAI-compatible Chat Completions, Responses, legacy Completions, and model-list routes.
|
|
14
|
+
- Anthropic Messages compatibility for Claude Code and other Anthropic-style clients.
|
|
15
|
+
- Bundled `codexx` launcher that runs Codex against a local Hoopilot server with the right Responses API provider settings.
|
|
16
|
+
- Local API-key gate, loopback-safe defaults, structured logs, Prometheus metrics, and Copilot quota reporting.
|
|
17
|
+
- npm package, standalone binaries, Docker image, and self-update support for release binaries.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Sign in once, then start the proxy on localhost:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npx @openhoo/hoopilot login
|
|
25
|
+
npx @openhoo/hoopilot
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
By default the server listens on `127.0.0.1:4141` and accepts local requests without authentication, so any placeholder works as the client key:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
export OPENAI_BASE_URL=http://127.0.0.1:4141/v1
|
|
32
|
+
export OPENAI_API_KEY=hoopilot
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
PowerShell:
|
|
36
|
+
|
|
37
|
+
```powershell
|
|
38
|
+
$env:OPENAI_BASE_URL = "http://127.0.0.1:4141/v1"
|
|
39
|
+
$env:OPENAI_API_KEY = "hoopilot"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
To require clients to authenticate — recommended whenever you expose the proxy beyond localhost — set `HOOPILOT_API_KEY` to a strong, unique secret and send that value as the client key:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
export HOOPILOT_API_KEY=$(openssl rand -hex 24)
|
|
46
|
+
npx @openhoo/hoopilot
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Run Codex through Hoopilot after the server is running:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npx --package @openhoo/hoopilot codexx
|
|
53
|
+
```
|
|
9
54
|
|
|
10
55
|
## Install
|
|
11
56
|
|
|
12
57
|
### npm
|
|
13
58
|
|
|
14
|
-
|
|
59
|
+
Run without installing:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
15
62
|
npx @openhoo/hoopilot
|
|
16
63
|
```
|
|
17
64
|
|
|
18
|
-
Or install
|
|
65
|
+
Or install the package globally:
|
|
19
66
|
|
|
20
|
-
```
|
|
67
|
+
```sh
|
|
21
68
|
npm install -g @openhoo/hoopilot
|
|
22
69
|
bun add -g @openhoo/hoopilot
|
|
23
70
|
```
|
|
24
71
|
|
|
25
|
-
### Standalone
|
|
72
|
+
### Standalone binary
|
|
26
73
|
|
|
27
|
-
When
|
|
74
|
+
When npm is unavailable but GitHub releases are reachable, install a prebuilt self-contained binary. Node.js and Bun are not required to run the binary.
|
|
28
75
|
|
|
29
|
-
Linux
|
|
76
|
+
Linux/macOS:
|
|
30
77
|
|
|
31
|
-
```
|
|
78
|
+
```sh
|
|
32
79
|
curl -fsSL https://raw.githubusercontent.com/openhoo/hoopilot/main/scripts/install.sh | sh
|
|
33
80
|
```
|
|
34
81
|
|
|
@@ -38,9 +85,9 @@ Windows PowerShell:
|
|
|
38
85
|
irm https://raw.githubusercontent.com/openhoo/hoopilot/main/scripts/install.ps1 | iex
|
|
39
86
|
```
|
|
40
87
|
|
|
41
|
-
The installer detects your OS, CPU architecture, and libc, downloads the matching binary, verifies its SHA-256 checksum, and installs it to `~/.local/bin` on Linux/macOS or `%LOCALAPPDATA%\Programs\hoopilot` on Windows. Override the
|
|
88
|
+
The installer detects your OS, CPU architecture, and libc, downloads the matching binary, verifies its SHA-256 checksum, and installs it to `~/.local/bin` on Linux/macOS or `%LOCALAPPDATA%\Programs\hoopilot` on Windows. Override the install directory with `HOOPILOT_INSTALL_DIR`, or pin a version:
|
|
42
89
|
|
|
43
|
-
```
|
|
90
|
+
```sh
|
|
44
91
|
curl -fsSL https://raw.githubusercontent.com/openhoo/hoopilot/main/scripts/install.sh | sh -s -- --version <version> --dir ~/bin
|
|
45
92
|
```
|
|
46
93
|
|
|
@@ -58,16 +105,23 @@ Run Hoopilot as a long-lived service from the published multi-arch image on the
|
|
|
58
105
|
# 1. Sign in once; the OAuth credential is written to the persisted /data volume.
|
|
59
106
|
docker run --rm -it -v hoopilot-data:/data ghcr.io/openhoo/hoopilot login
|
|
60
107
|
|
|
61
|
-
# 2. Run the proxy on localhost.
|
|
108
|
+
# 2. Run the proxy on localhost with a strong, unique API key.
|
|
109
|
+
export HOOPILOT_API_KEY=$(openssl rand -hex 24)
|
|
62
110
|
docker run -d --name hoopilot --restart unless-stopped \
|
|
63
111
|
-p 127.0.0.1:4141:4141 \
|
|
112
|
+
-e HOOPILOT_API_KEY \
|
|
64
113
|
-v hoopilot-data:/data ghcr.io/openhoo/hoopilot
|
|
65
114
|
```
|
|
66
115
|
|
|
67
|
-
Tags follow the release version
|
|
116
|
+
Tags follow the release version, for example `ghcr.io/openhoo/hoopilot:0.10`, `:0.10.0`, and `:latest`. The image listens on `0.0.0.0:4141` (required so Docker port publishing can reach it), runs as a non-root user, and stores its OAuth credential at `/data/auth.json` by default. Override that path with `HOOPILOT_AUTH_FILE`.
|
|
117
|
+
|
|
118
|
+
Because it binds a non-loopback interface, the image fails closed: it refuses to start unless you set `HOOPILOT_API_KEY` to a strong, unique secret (well-known demo keys are rejected). Clients then send that key as `Authorization: Bearer <key>` or `x-api-key: <key>`. To intentionally run without authentication — for example behind your own authenticating proxy — set `HOOPILOT_ALLOW_UNAUTHENTICATED=1`.
|
|
119
|
+
|
|
120
|
+
A `docker-compose.yml` is provided. Set `HOOPILOT_API_KEY` first; compose passes it through to the container:
|
|
68
121
|
|
|
69
122
|
```sh
|
|
70
|
-
docker compose run --rm hoopilot login
|
|
123
|
+
docker compose run --rm hoopilot login
|
|
124
|
+
export HOOPILOT_API_KEY=$(openssl rand -hex 24)
|
|
71
125
|
docker compose up -d
|
|
72
126
|
```
|
|
73
127
|
|
|
@@ -75,220 +129,233 @@ docker compose up -d
|
|
|
75
129
|
|
|
76
130
|
Standalone binaries update themselves in place from the latest GitHub release:
|
|
77
131
|
|
|
78
|
-
```
|
|
132
|
+
```sh
|
|
79
133
|
hoopilot update
|
|
80
134
|
```
|
|
81
135
|
|
|
82
136
|
npm installs report when a newer version is available and print the right command. Hoopilot checks GitHub at most once a day in the background. Disable the check with `--no-update-check`, `HOOPILOT_NO_UPDATE_CHECK`, or `NO_UPDATE_NOTIFIER`.
|
|
83
137
|
|
|
84
|
-
##
|
|
138
|
+
## Running the proxy
|
|
85
139
|
|
|
86
|
-
|
|
140
|
+
Login uses GitHub's browser/device flow, verifies that the returned OAuth token can reach the Copilot API, and stores it locally:
|
|
87
141
|
|
|
88
|
-
```
|
|
89
|
-
|
|
142
|
+
```sh
|
|
143
|
+
hoopilot login
|
|
90
144
|
```
|
|
91
145
|
|
|
92
|
-
|
|
146
|
+
Default credential paths:
|
|
147
|
+
|
|
148
|
+
- Linux/macOS: `$HOME/.config/hoopilot/auth.json`
|
|
149
|
+
- Windows: `%APPDATA%\hoopilot\auth.json`
|
|
93
150
|
|
|
94
|
-
|
|
151
|
+
Override the path with `HOOPILOT_AUTH_FILE` or `--auth-file`.
|
|
95
152
|
|
|
96
|
-
|
|
153
|
+
Start the server:
|
|
97
154
|
|
|
98
155
|
```sh
|
|
99
|
-
hoopilot
|
|
156
|
+
hoopilot --port 4141
|
|
100
157
|
```
|
|
101
158
|
|
|
102
|
-
|
|
159
|
+
By default Hoopilot listens on `127.0.0.1:4141`. If `HOOPILOT_API_KEY` is unset, local requests are accepted without client authentication. Binding to a non-loopback host requires either a strong, unique `HOOPILOT_API_KEY` or the explicit `--allow-unauthenticated` / `HOOPILOT_ALLOW_UNAUTHENTICATED=1` opt-in. Well-known demo keys are always rejected on a non-loopback host, even with the unauthenticated opt-in.
|
|
103
160
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Add-Content -Encoding utf8 .env
|
|
108
|
-
```
|
|
161
|
+
When an API key is configured, clients may send it as either `Authorization: Bearer <key>` or `x-api-key: <key>`.
|
|
162
|
+
|
|
163
|
+
Cross-origin browser requests are always blocked, even when an API key is set, so a malicious web page cannot drive the local proxy. Requests from loopback origins are allowed; to permit specific web origins, list them in `HOOPILOT_ALLOWED_ORIGINS` (comma-separated).
|
|
109
164
|
|
|
110
|
-
|
|
165
|
+
## Client setup
|
|
166
|
+
|
|
167
|
+
### OpenAI-compatible clients
|
|
111
168
|
|
|
112
169
|
```sh
|
|
113
|
-
|
|
114
|
-
|
|
170
|
+
export OPENAI_BASE_URL=http://127.0.0.1:4141/v1
|
|
171
|
+
export OPENAI_API_KEY=hoopilot
|
|
115
172
|
```
|
|
116
173
|
|
|
117
|
-
|
|
174
|
+
The client key value is arbitrary when the server runs without `HOOPILOT_API_KEY`; if you set one, use that value here instead.
|
|
118
175
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
176
|
+
Use any model returned by:
|
|
177
|
+
|
|
178
|
+
```sh
|
|
179
|
+
hoopilot models
|
|
123
180
|
```
|
|
124
181
|
|
|
125
|
-
|
|
182
|
+
### Claude Code and Anthropic-style clients
|
|
126
183
|
|
|
127
|
-
```
|
|
128
|
-
|
|
184
|
+
```sh
|
|
185
|
+
export ANTHROPIC_BASE_URL=http://127.0.0.1:4141
|
|
186
|
+
export ANTHROPIC_AUTH_TOKEN=hoopilot
|
|
187
|
+
claude
|
|
129
188
|
```
|
|
130
189
|
|
|
131
|
-
|
|
190
|
+
Hoopilot accepts the local key as `x-api-key` too, so `ANTHROPIC_API_KEY` also works for clients that send Anthropic's standard API-key header.
|
|
132
191
|
|
|
133
|
-
|
|
134
|
-
- Windows: `$env:APPDATA\hoopilot\auth.json`
|
|
192
|
+
### Codex
|
|
135
193
|
|
|
136
|
-
|
|
194
|
+
Use the bundled `codexx` command after Hoopilot is running:
|
|
137
195
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
```powershell
|
|
141
|
-
$env:HOOPILOT_API_KEY = "local-key"
|
|
142
|
-
npx @openhoo/hoopilot --port 4141
|
|
196
|
+
```sh
|
|
197
|
+
codexx
|
|
143
198
|
```
|
|
144
199
|
|
|
145
|
-
|
|
200
|
+
Without a global install:
|
|
146
201
|
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
$env:OPENAI_API_KEY = "local-key"
|
|
202
|
+
```sh
|
|
203
|
+
npx --package @openhoo/hoopilot codexx
|
|
150
204
|
```
|
|
151
205
|
|
|
152
|
-
|
|
206
|
+
If the server requires an API key, set `HOOPILOT_API_KEY` (or `CODEXX_API_KEY`) in the `codexx` environment to match.
|
|
153
207
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
208
|
+
`codexx` does not start Hoopilot and does not alter your shell environment. It starts `codex` with a temporary `hoopilot` model provider pointed at `http://127.0.0.1:4141/v1`, uses the Responses API wire format, disables Responses WebSockets for that provider, maps `HOOPILOT_API_KEY` (or a random throwaway key when none is set) to `OPENAI_API_KEY` for the child process, passes `--disable network_proxy`, and removes standard proxy variables from the spawned Codex process.
|
|
209
|
+
|
|
210
|
+
`codexx` defaults to `gpt-5.5` with `model_reasoning_effort="xhigh"`. Before starting Codex, it checks `/v1/models` and reports if the logged-in Copilot account does not advertise the requested model. Set `CODEXX_MODEL` to one of the listed models, or log in with a Copilot account that has access to the default model.
|
|
211
|
+
|
|
212
|
+
Codex compaction posts to `/v1/responses/compact` for OpenAI- and Azure-named providers. Hoopilot handles that route with a unary Copilot Responses request and returns the `{ "output": [...] }` summary Codex expects, so compaction works through either `codexx` or a direct OpenAI-compatible base URL override.
|
|
213
|
+
|
|
214
|
+
## Authentication
|
|
215
|
+
|
|
216
|
+
Hoopilot supports one upstream credential flow: GitHub Copilot OAuth browser login.
|
|
217
|
+
|
|
218
|
+
```sh
|
|
219
|
+
hoopilot login
|
|
220
|
+
hoopilot
|
|
158
221
|
```
|
|
159
222
|
|
|
160
|
-
|
|
161
|
-
`x-api-key: <key>`, so `ANTHROPIC_API_KEY` also works for clients that send
|
|
162
|
-
Anthropic's `x-api-key` header.
|
|
223
|
+
Direct bearer tokens, GitHub CLI token fallback, classic GitHub PATs, and fine-grained GitHub PATs are not supported.
|
|
163
224
|
|
|
164
|
-
|
|
225
|
+
Re-run `hoopilot login` after upgrading Hoopilot if Copilot reports a supported model as unavailable. Older stored tokens can have a reduced model set.
|
|
165
226
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
227
|
+
To print the verified OAuth token for another local tool, use `--print-key`. Login status goes to stderr, so stdout contains only the token.
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
hoopilot login --print-key | sed 's/^/COPILOT_OAUTH_TOKEN=/' >> .env
|
|
169
231
|
```
|
|
170
232
|
|
|
171
|
-
|
|
233
|
+
PowerShell:
|
|
172
234
|
|
|
173
235
|
```powershell
|
|
174
|
-
|
|
175
|
-
|
|
236
|
+
hoopilot login --print-key |
|
|
237
|
+
ForEach-Object { "COPILOT_OAUTH_TOKEN=$_" } |
|
|
238
|
+
Add-Content -Encoding utf8 .env
|
|
176
239
|
```
|
|
177
240
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
URL with `CODEXX_BASE_URL`, the local key with `CODEXX_API_KEY`, or the Codex
|
|
185
|
-
executable with `CODEXX_CODEX_BIN`, the model with `CODEXX_MODEL`, or the reasoning
|
|
186
|
-
effort with `CODEXX_MODEL_REASONING_EFFORT`.
|
|
187
|
-
|
|
188
|
-
`codexx` defaults to `gpt-5.5` with `model_reasoning_effort="xhigh"`. Codex sends
|
|
189
|
-
those requests through its Responses API provider, and Hoopilot forwards them to
|
|
190
|
-
Copilot's Responses endpoint because `gpt-5.5` is not available through Copilot's
|
|
191
|
-
chat-completions endpoint. Before starting Codex, `codexx` checks
|
|
192
|
-
`http://127.0.0.1:4141/v1/models` and reports if the logged-in Copilot account does
|
|
193
|
-
not advertise the requested model. Set `CODEXX_MODEL` to one of the listed models,
|
|
194
|
-
or log in with a Copilot account that has `gpt-5.5`.
|
|
195
|
-
|
|
196
|
-
When Codex compacts a long session it POSTs to `/v1/responses/compact` — a server-side
|
|
197
|
-
endpoint it expects from `OpenAI`- and Azure-named providers and for which it has no
|
|
198
|
-
local fallback, so an unhandled route would abort compaction. Hoopilot handles it by
|
|
199
|
-
running the supplied conversation through Copilot's Responses endpoint as a unary
|
|
200
|
-
request and returning the resulting `{ "output": [...] }` summary, so compaction works
|
|
201
|
-
whether Codex points at Hoopilot through `codexx` or through a plain `OPENAI_BASE_URL`
|
|
202
|
-
override of the built-in `openai` provider.
|
|
203
|
-
|
|
204
|
-
If no `HOOPILOT_API_KEY` is configured, Hoopilot accepts local requests without client authentication. Binding to a non-loopback host requires `HOOPILOT_API_KEY` unless `--allow-unauthenticated` is set.
|
|
241
|
+
Docker:
|
|
242
|
+
|
|
243
|
+
```sh
|
|
244
|
+
docker run --rm -v hoopilot-data:/data ghcr.io/openhoo/hoopilot login --print-key \
|
|
245
|
+
| sed 's/^/COPILOT_OAUTH_TOKEN=/' >> .env
|
|
246
|
+
```
|
|
205
247
|
|
|
206
248
|
## Logging
|
|
207
249
|
|
|
208
|
-
Hoopilot uses Pino for structured logs. Server startup, request completion, upstream Copilot failures, model-list fallback, auth failures, and update-check diagnostics are logged with stable event names and request IDs.
|
|
250
|
+
Hoopilot uses Pino for structured logs. Server startup, request completion, upstream Copilot failures, model-list fallback, auth failures, and update-check diagnostics are logged with stable event names and request IDs.
|
|
251
|
+
|
|
252
|
+
Logs never include request bodies, prompt text, completions, stream chunks, OAuth tokens, API keys, authorization headers, cookies, or auth-file contents.
|
|
209
253
|
|
|
210
254
|
Console logs default to pretty output at `info` level:
|
|
211
255
|
|
|
212
|
-
```
|
|
213
|
-
|
|
256
|
+
```sh
|
|
257
|
+
hoopilot --log-level info --log-format pretty
|
|
214
258
|
```
|
|
215
259
|
|
|
216
|
-
For
|
|
260
|
+
For newline-delimited JSON:
|
|
217
261
|
|
|
218
|
-
```
|
|
219
|
-
|
|
262
|
+
```sh
|
|
263
|
+
hoopilot --log-level info --log-format json
|
|
220
264
|
```
|
|
221
265
|
|
|
222
|
-
Equivalent environment variables:
|
|
223
|
-
|
|
224
|
-
- `HOOPILOT_LOG_LEVEL`: `trace`, `debug`, `info`, `warn`, `error`, `fatal`, or `silent`. Default: `info`.
|
|
225
|
-
- `HOOPILOT_LOG_FORMAT`: `json` or `pretty`. Default: `pretty`.
|
|
226
|
-
|
|
227
266
|
Incoming `x-request-id` headers are preserved on responses. If a request has no ID, Hoopilot generates one and returns it as `x-request-id`.
|
|
228
267
|
|
|
229
268
|
## Metrics and usage
|
|
230
269
|
|
|
231
|
-
Hoopilot tracks token usage, request counts, and latency in memory while the server runs
|
|
270
|
+
Hoopilot tracks token usage, request counts, and latency in memory while the server runs. It can also report your GitHub Copilot account quota and premium-request usage, plus your GitHub REST API rate-limit budget.
|
|
271
|
+
|
|
272
|
+
- `GET /metrics` returns Prometheus text (`text/plain; version=0.0.4`). It exposes request counters, upstream call counters, token counters by model and type, a request-duration histogram, an in-flight gauge, Copilot quota gauges, and GitHub REST API rate-limit gauges (`hoopilot_github_ratelimit_limit`, `_remaining`, `_used`, `_reset_timestamp_seconds`, `_retry_after_seconds`, labelled by `resource`) — the quota and rate-limit series appear after `/v1/usage` has been fetched at least once. Counters reset to zero on restart, which Prometheus handles natively.
|
|
273
|
+
- `GET /v1/usage` returns JSON combining the proxy metrics snapshot with live Copilot quota fetched from GitHub and cached for 60 seconds. If quota cannot be read, `copilot` is `null` and `copilot_error` explains why. The snapshot's `proxy.githubRateLimit` field reports the most recent GitHub REST rate-limit budget per resource (`limit`, `remaining`, `used`, `resetAt`, `retryAfterSeconds`, `observedAt`).
|
|
274
|
+
- `hoopilot usage` prints your Copilot plan and quota — and, when GitHub returns them, your GitHub API rate-limit budget — from the command line.
|
|
232
275
|
|
|
233
|
-
|
|
234
|
-
- `GET /v1/usage` returns JSON combining the proxy metrics snapshot with live Copilot quota fetched from GitHub (cached for 60 seconds). If the quota cannot be read, `copilot` is `null` and `copilot_error` explains why, but the proxy metrics are still returned.
|
|
235
|
-
- `hoopilot usage` prints your Copilot plan and quota from the command line.
|
|
276
|
+
Token usage is read from the upstream `usage` object. For streaming chat completions, usage is only available when the client sends `stream_options: {"include_usage": true}`; Hoopilot does not inject that flag. Responses API streaming always reports usage, so streamed Responses requests are fully accounted.
|
|
236
277
|
|
|
237
|
-
|
|
278
|
+
GitHub API usage is read from the `x-ratelimit-*` response headers that `api.github.com` returns on the `copilot_internal/user` quota call Hoopilot already makes, so it costs no extra request. (The Copilot completion host `api.githubcopilot.com` does not currently emit these headers, so per-completion rate-limit data is not yet available there.)
|
|
238
279
|
|
|
239
280
|
`/metrics` and `/v1/usage` are subject to the same `HOOPILOT_API_KEY` gate as the other routes.
|
|
240
281
|
|
|
241
|
-
##
|
|
282
|
+
## Troubleshooting
|
|
242
283
|
|
|
243
|
-
|
|
284
|
+
### Codex auth errors
|
|
244
285
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
286
|
+
Hoopilot does not return raw `403` responses to Codex for authentication or Copilot-entitlement failures. Local Hoopilot API-key problems return `401 invalid_api_key`; OAuth credential and upstream Copilot auth failures return `401 copilot_auth_error`.
|
|
287
|
+
|
|
288
|
+
Verify browser login and the local proxy before retrying Codex:
|
|
289
|
+
|
|
290
|
+
```sh
|
|
291
|
+
hoopilot login
|
|
292
|
+
hoopilot --port 4141
|
|
248
293
|
```
|
|
249
294
|
|
|
250
|
-
|
|
295
|
+
Then, in another shell:
|
|
251
296
|
|
|
252
|
-
|
|
297
|
+
```sh
|
|
298
|
+
curl http://127.0.0.1:4141/v1/models
|
|
299
|
+
codexx
|
|
300
|
+
```
|
|
253
301
|
|
|
254
|
-
|
|
255
|
-
- `HOOPILOT_GITHUB_CLIENT_ID`: GitHub OAuth app client ID override. The default uses the same GitHub Copilot OAuth app as opencode's Copilot provider.
|
|
256
|
-
- `HOOPILOT_GITHUB_DOMAIN`: GitHub domain override. Default: `github.com`.
|
|
257
|
-
- `COPILOT_API_BASE_URL`: upstream Copilot API base URL override. Default: `https://api.githubcopilot.com`.
|
|
258
|
-
- `HOOPILOT_GITHUB_API_BASE_URL`: GitHub REST API base URL used for the Copilot quota lookup. Default: `https://api.github.com`.
|
|
259
|
-
- `HOOPILOT_ALLOW_UNSAFE_UPSTREAM=1`: allow sending the stored OAuth token to nonstandard HTTPS Copilot/GitHub API hosts. Use only for trusted test or enterprise endpoints.
|
|
302
|
+
If you started the server with `HOOPILOT_API_KEY`, add `-H "Authorization: Bearer $HOOPILOT_API_KEY"` to the curl command and set the same `HOOPILOT_API_KEY` for `codexx`.
|
|
260
303
|
|
|
261
|
-
|
|
304
|
+
If `/v1/models` returns `401 copilot_auth_error`, rerun `hoopilot login` and confirm that the GitHub account has active Copilot access.
|
|
262
305
|
|
|
263
|
-
|
|
306
|
+
## Configuration
|
|
264
307
|
|
|
265
|
-
|
|
308
|
+
Server and local-client settings:
|
|
266
309
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
310
|
+
| Setting | Description |
|
|
311
|
+
| --- | --- |
|
|
312
|
+
| `HOST` / `--host` | Host to listen on. Default: `127.0.0.1` for local runs; Docker sets `0.0.0.0`. |
|
|
313
|
+
| `PORT` / `--port` | Port to listen on. Default: `4141`. |
|
|
314
|
+
| `HOOPILOT_API_KEY` / `--api-key` | Require clients to send `Authorization: Bearer <key>` or `x-api-key: <key>`. Must be a strong, unique secret on non-loopback binds; well-known demo keys are rejected. |
|
|
315
|
+
| `--api-key-file` | Read the local API key from a file instead of argv. |
|
|
316
|
+
| `HOOPILOT_ALLOWED_ORIGINS` | Comma-separated browser origins allowed to make cross-origin requests. Loopback origins are always allowed; every other origin is blocked. |
|
|
317
|
+
| `HOOPILOT_ALLOW_UNAUTHENTICATED` / `--allow-unauthenticated` | Allow non-loopback binds without a local API key. |
|
|
318
|
+
| `HOOPILOT_STREAM_MODE` / `--stream-mode` | `auto`, `live`, or `buffer`. `auto` buffers streams for Windows standalone binaries. |
|
|
272
319
|
|
|
273
|
-
|
|
320
|
+
Copilot and GitHub settings:
|
|
274
321
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
322
|
+
| Setting | Description |
|
|
323
|
+
| --- | --- |
|
|
324
|
+
| `HOOPILOT_AUTH_FILE` / `--auth-file` | OAuth credential store path. |
|
|
325
|
+
| `HOOPILOT_GITHUB_CLIENT_ID` | GitHub OAuth app client ID override. |
|
|
326
|
+
| `HOOPILOT_GITHUB_DOMAIN` | GitHub domain override. Default: `github.com`. |
|
|
327
|
+
| `COPILOT_API_BASE_URL` / `--copilot-api-base-url` | Upstream Copilot API base URL. Default: `https://api.githubcopilot.com`. |
|
|
328
|
+
| `HOOPILOT_GITHUB_API_BASE_URL` | GitHub REST API base URL used for quota lookup. Default: `https://api.github.com`. |
|
|
329
|
+
| `HOOPILOT_ALLOW_UNSAFE_UPSTREAM=1` | Allow sending the stored OAuth token to nonstandard HTTPS Copilot/GitHub API hosts. Use only for trusted test or enterprise endpoints. |
|
|
281
330
|
|
|
282
|
-
|
|
331
|
+
Logging and update settings:
|
|
283
332
|
|
|
284
|
-
|
|
333
|
+
| Setting | Description |
|
|
334
|
+
| --- | --- |
|
|
335
|
+
| `HOOPILOT_LOG_LEVEL` / `--log-level` | `trace`, `debug`, `info`, `warn`, `error`, `fatal`, or `silent`. Default: `info`. |
|
|
336
|
+
| `HOOPILOT_LOG_FORMAT` / `--log-format` | `pretty` or `json`. Default: `pretty`. |
|
|
337
|
+
| `HOOPILOT_NO_UPDATE_CHECK` / `--no-update-check` | Disable background update checks. `NO_UPDATE_NOTIFIER` is also honored. |
|
|
285
338
|
|
|
286
|
-
|
|
339
|
+
`codexx` settings:
|
|
340
|
+
|
|
341
|
+
| Setting | Description |
|
|
342
|
+
| --- | --- |
|
|
343
|
+
| `CODEXX_BASE_URL` | OpenAI-compatible Hoopilot base URL. Default: `http://127.0.0.1:4141/v1`. |
|
|
344
|
+
| `CODEXX_API_KEY` | API key sent to Hoopilot. Falls back to `HOOPILOT_API_KEY`, then a random per-run key for an unauthenticated local server. |
|
|
345
|
+
| `CODEXX_CODEX_BIN` | Codex executable to run. Default: `codex`. |
|
|
346
|
+
| `CODEXX_MODEL` | Codex model to use. Default: `gpt-5.5`. |
|
|
347
|
+
| `CODEXX_MODEL_REASONING_EFFORT` | Codex reasoning effort. Default: `xhigh`. |
|
|
348
|
+
| `CODEXX_SKIP_MODEL_PREFLIGHT=1` | Skip the `/v1/models` availability check before starting Codex. |
|
|
349
|
+
|
|
350
|
+
## CLI reference
|
|
351
|
+
|
|
352
|
+
```txt
|
|
287
353
|
hoopilot [serve] [options]
|
|
288
354
|
hoopilot codexx [codex options] [prompt]
|
|
289
355
|
hoopilot login [options]
|
|
290
356
|
hoopilot models [options]
|
|
291
357
|
hoopilot usage [options]
|
|
358
|
+
hoopilot update
|
|
292
359
|
```
|
|
293
360
|
|
|
294
361
|
Commands:
|
|
@@ -314,34 +381,38 @@ Options:
|
|
|
314
381
|
--print-key Login: print the received OAuth token to stdout
|
|
315
382
|
--log-level <level> trace, debug, info, warn, error, fatal, or silent
|
|
316
383
|
--log-format <format> json or pretty. Default: pretty
|
|
384
|
+
--stream-mode <mode> auto, live, or buffer. Auto buffers Windows standalone streams.
|
|
317
385
|
--no-update-check Do not check GitHub for a newer release
|
|
318
386
|
--allow-unauthenticated Allow non-loopback bind without --api-key
|
|
387
|
+
-h, --help Show help
|
|
388
|
+
-v, --version Show version
|
|
319
389
|
```
|
|
320
390
|
|
|
321
391
|
## Endpoints
|
|
322
392
|
|
|
323
|
-
- `GET /healthz`
|
|
393
|
+
- `GET /` and `GET /healthz`
|
|
324
394
|
- `GET /metrics`
|
|
325
395
|
- `GET /v1/models`
|
|
326
396
|
- `GET /v1/usage`
|
|
327
|
-
- `POST /v1/messages`
|
|
328
|
-
- `POST /v1/messages/count_tokens`
|
|
329
397
|
- `POST /v1/chat/completions`
|
|
330
398
|
- `POST /v1/responses`
|
|
399
|
+
- `POST /v1/responses/compact`
|
|
331
400
|
- `POST /v1/completions`
|
|
401
|
+
- `POST /v1/messages`
|
|
402
|
+
- `POST /v1/messages/count_tokens`
|
|
332
403
|
|
|
333
|
-
`/v1/chat/completions` and `/v1/responses` are proxied to the matching Copilot endpoints as directly as possible. `/v1/messages` translates Anthropic Messages requests and responses to Copilot's Responses endpoint
|
|
404
|
+
`/v1/chat/completions` and `/v1/responses` are proxied to the matching Copilot endpoints as directly as possible. `/v1/messages` translates Anthropic Messages requests and responses to Copilot's Responses endpoint. `/v1/messages/count_tokens` returns a local token estimate for Claude Code preflights because Copilot does not expose Anthropic's count-tokens route. `/v1/completions` translates legacy completion requests and responses to the closest chat-completions equivalent. `GET /v1/responses` returns an explicit unsupported-WebSocket response; `codexx` configures Codex to use HTTP Responses instead.
|
|
334
405
|
|
|
335
406
|
## Development
|
|
336
407
|
|
|
337
|
-
```
|
|
408
|
+
```sh
|
|
338
409
|
bun install
|
|
339
410
|
bun run check
|
|
340
411
|
```
|
|
341
412
|
|
|
342
413
|
Useful scripts:
|
|
343
414
|
|
|
344
|
-
```
|
|
415
|
+
```sh
|
|
345
416
|
bun run test
|
|
346
417
|
bun run test:coverage
|
|
347
418
|
bun run typecheck
|
|
@@ -351,7 +422,11 @@ bun run biome:fix
|
|
|
351
422
|
|
|
352
423
|
## Release
|
|
353
424
|
|
|
354
|
-
Commits merged to `main` are evaluated by hooversion after CI passes. When a release is produced, the release workflow creates the release commit, tag, and GitHub release automatically, publishes the package through npm trusted publishing, then cross-compiles standalone binaries for every supported platform
|
|
425
|
+
Commits merged to `main` are evaluated by hooversion after CI passes. When a release is produced, the release workflow creates the release commit, tag, and GitHub release automatically, publishes the package through npm trusted publishing, then cross-compiles standalone binaries for every supported platform with `scripts/build-binaries.sh` and attaches them plus a `SHA256SUMS` manifest to the GitHub release. Build all binaries locally with:
|
|
426
|
+
|
|
427
|
+
```sh
|
|
428
|
+
bun run build:binaries
|
|
429
|
+
```
|
|
355
430
|
|
|
356
431
|
Configure npm trusted publishing for `@openhoo/hoopilot` on npmjs.com before relying on automatic publication. The workflow uses GitHub Actions OIDC with `npm publish --access public --provenance`.
|
|
357
432
|
|
|
@@ -52,7 +52,6 @@ function asRecord(value) {
|
|
|
52
52
|
|
|
53
53
|
// src/codexx.ts
|
|
54
54
|
var DEFAULT_BASE_URL = "http://127.0.0.1:4141/v1";
|
|
55
|
-
var DEFAULT_API_KEY = "local-key";
|
|
56
55
|
var DEFAULT_CODEX_BIN = "codex";
|
|
57
56
|
var DEFAULT_MODEL = "gpt-5.5";
|
|
58
57
|
var DEFAULT_REASONING_EFFORT = "xhigh";
|
|
@@ -68,7 +67,7 @@ var PROXY_ENV_KEYS = [
|
|
|
68
67
|
];
|
|
69
68
|
function buildCodexxInvocation(argv, env = process.env) {
|
|
70
69
|
const baseUrl = envValue(env.CODEXX_BASE_URL) ?? DEFAULT_BASE_URL;
|
|
71
|
-
const apiKey = envValue(env.CODEXX_API_KEY) ?? envValue(env.HOOPILOT_API_KEY) ??
|
|
70
|
+
const apiKey = envValue(env.CODEXX_API_KEY) ?? envValue(env.HOOPILOT_API_KEY) ?? generateEphemeralApiKey();
|
|
72
71
|
const command = envValue(env.CODEXX_CODEX_BIN) ?? DEFAULT_CODEX_BIN;
|
|
73
72
|
const model = envValue(env.CODEXX_MODEL) ?? DEFAULT_MODEL;
|
|
74
73
|
const reasoningEffort = envValue(env.CODEXX_MODEL_REASONING_EFFORT) ?? DEFAULT_REASONING_EFFORT;
|
|
@@ -102,6 +101,9 @@ function buildCodexxInvocation(argv, env = process.env) {
|
|
|
102
101
|
model
|
|
103
102
|
};
|
|
104
103
|
}
|
|
104
|
+
function generateEphemeralApiKey() {
|
|
105
|
+
return `codexx-${crypto.randomUUID()}`;
|
|
106
|
+
}
|
|
105
107
|
function withoutProxyEnv(env) {
|
|
106
108
|
const next = { ...env };
|
|
107
109
|
for (const key of PROXY_ENV_KEYS) {
|
|
@@ -142,7 +144,7 @@ async function verifyCodexxModel(invocation, fetcher = fetch) {
|
|
|
142
144
|
response = await fetcher(modelsUrl, {
|
|
143
145
|
headers: {
|
|
144
146
|
accept: "application/json",
|
|
145
|
-
authorization: `Bearer ${invocation.env.OPENAI_API_KEY ??
|
|
147
|
+
authorization: `Bearer ${invocation.env.OPENAI_API_KEY ?? generateEphemeralApiKey()}`
|
|
146
148
|
},
|
|
147
149
|
method: "GET"
|
|
148
150
|
});
|
|
@@ -174,7 +176,9 @@ Usage:
|
|
|
174
176
|
Environment:
|
|
175
177
|
CODEXX_BASE_URL OpenAI-compatible base URL. Default: ${DEFAULT_BASE_URL}
|
|
176
178
|
CODEXX_API_KEY API key sent to the local Hoopilot server.
|
|
177
|
-
HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.
|
|
179
|
+
HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset. When
|
|
180
|
+
neither is set, a random throwaway key is generated for
|
|
181
|
+
an unauthenticated local server.
|
|
178
182
|
CODEXX_CODEX_BIN Codex executable to run. Default: ${DEFAULT_CODEX_BIN}
|
|
179
183
|
CODEXX_MODEL Codex model to use. Default: ${DEFAULT_MODEL}
|
|
180
184
|
CODEXX_MODEL_REASONING_EFFORT
|
|
@@ -218,4 +222,4 @@ export {
|
|
|
218
222
|
main,
|
|
219
223
|
verifyCodexxModel
|
|
220
224
|
};
|
|
221
|
-
//# sourceMappingURL=chunk-
|
|
225
|
+
//# sourceMappingURL=chunk-JU6F5L34.js.map
|