@openhoo/hoopilot 0.5.7 → 0.6.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 +8 -3
- package/dist/cli.js +115 -56
- package/dist/cli.js.map +1 -1
- package/dist/codexx.js +52 -2
- package/dist/codexx.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ First sign in with GitHub Copilot OAuth in your browser:
|
|
|
68
68
|
npx @openhoo/hoopilot login
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
The login command prints a one-time code, opens `https://github.com/login/device` best-effort, verifies that the returned OAuth token can reach the Copilot API, and stores it in Hoopilot's auth file.
|
|
71
|
+
The login command prints a one-time code, opens `https://github.com/login/device` best-effort, verifies that the returned OAuth token can reach the Copilot API, and stores it in Hoopilot's auth file. Re-run `npx @openhoo/hoopilot login` after upgrading Hoopilot if Copilot reports a supported model as unavailable; older stored tokens can have a reduced model set.
|
|
72
72
|
|
|
73
73
|
Then start the proxy:
|
|
74
74
|
|
|
@@ -137,7 +137,10 @@ effort with `CODEXX_MODEL_REASONING_EFFORT`.
|
|
|
137
137
|
`codexx` defaults to `gpt-5.5` with `model_reasoning_effort="xhigh"`. Codex sends
|
|
138
138
|
those requests through its Responses API provider, and Hoopilot forwards them to
|
|
139
139
|
Copilot's Responses endpoint because `gpt-5.5` is not available through Copilot's
|
|
140
|
-
chat-completions endpoint.
|
|
140
|
+
chat-completions endpoint. Before starting Codex, `codexx` checks
|
|
141
|
+
`http://127.0.0.1:4141/v1/models` and reports if the logged-in Copilot account does
|
|
142
|
+
not advertise the requested model. Set `CODEXX_MODEL` to one of the listed models,
|
|
143
|
+
or log in with a Copilot account that has `gpt-5.5`.
|
|
141
144
|
|
|
142
145
|
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.
|
|
143
146
|
|
|
@@ -178,7 +181,7 @@ Direct bearer tokens, GitHub CLI token fallback, classic GitHub PATs, and fine-g
|
|
|
178
181
|
Supported authentication-related settings:
|
|
179
182
|
|
|
180
183
|
- `HOOPILOT_AUTH_FILE`: OAuth credential store path.
|
|
181
|
-
- `HOOPILOT_GITHUB_CLIENT_ID`: GitHub OAuth app client ID override.
|
|
184
|
+
- `HOOPILOT_GITHUB_CLIENT_ID`: GitHub OAuth app client ID override. The default uses the same GitHub Copilot OAuth app as opencode's Copilot provider.
|
|
182
185
|
- `HOOPILOT_GITHUB_DOMAIN`: GitHub domain override. Default: `github.com`.
|
|
183
186
|
- `COPILOT_API_BASE_URL`: upstream Copilot API base URL override. Default: `https://api.githubcopilot.com`.
|
|
184
187
|
|
|
@@ -210,6 +213,7 @@ If that returns `401 copilot_auth_error`, rerun `npx @openhoo/hoopilot login` an
|
|
|
210
213
|
```powershell
|
|
211
214
|
hoopilot [serve] [options]
|
|
212
215
|
hoopilot login [options]
|
|
216
|
+
hoopilot models [options]
|
|
213
217
|
```
|
|
214
218
|
|
|
215
219
|
Commands:
|
|
@@ -217,6 +221,7 @@ Commands:
|
|
|
217
221
|
```txt
|
|
218
222
|
serve Start the proxy server (default)
|
|
219
223
|
login Sign in through GitHub OAuth in a browser and verify Copilot access
|
|
224
|
+
models List available GitHub Copilot model IDs
|
|
220
225
|
update, upgrade Update hoopilot to the latest release
|
|
221
226
|
```
|
|
222
227
|
|
package/dist/cli.js
CHANGED
|
@@ -101,9 +101,64 @@ function trimTrailingSlash(value) {
|
|
|
101
101
|
return value.replace(/\/+$/, "");
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// src/copilot.ts
|
|
105
|
+
var CopilotClient = class {
|
|
106
|
+
#auth;
|
|
107
|
+
#fetch;
|
|
108
|
+
constructor(options = {}) {
|
|
109
|
+
this.#auth = new CopilotAuth(options);
|
|
110
|
+
this.#fetch = options.fetch ?? fetch;
|
|
111
|
+
}
|
|
112
|
+
async chatCompletions(body, signal) {
|
|
113
|
+
return this.fetchCopilot("/chat/completions", {
|
|
114
|
+
body: JSON.stringify(body),
|
|
115
|
+
headers: {
|
|
116
|
+
"content-type": "application/json"
|
|
117
|
+
},
|
|
118
|
+
method: "POST",
|
|
119
|
+
signal
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async responses(body, signal) {
|
|
123
|
+
return this.fetchCopilot("/responses", {
|
|
124
|
+
body,
|
|
125
|
+
headers: {
|
|
126
|
+
"content-type": "application/json"
|
|
127
|
+
},
|
|
128
|
+
method: "POST",
|
|
129
|
+
signal
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async models(signal) {
|
|
133
|
+
return this.fetchCopilot("/models", {
|
|
134
|
+
headers: {
|
|
135
|
+
accept: "application/json"
|
|
136
|
+
},
|
|
137
|
+
method: "GET",
|
|
138
|
+
signal
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async fetchCopilot(path, init) {
|
|
142
|
+
const access = await this.#auth.getAccess();
|
|
143
|
+
const headers = new Headers(init.headers);
|
|
144
|
+
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
145
|
+
headers.set("authorization", `Bearer ${access.token}`);
|
|
146
|
+
headers.set("copilot-integration-id", "vscode-chat");
|
|
147
|
+
headers.set("editor-plugin-version", "hoopilot/0.1.0");
|
|
148
|
+
headers.set("editor-version", "Hoopilot/0.1.0");
|
|
149
|
+
headers.set("openai-intent", "conversation-panel");
|
|
150
|
+
headers.set("user-agent", "hoopilot/0.1.0");
|
|
151
|
+
headers.set("x-github-api-version", "2026-06-01");
|
|
152
|
+
return this.#fetch(`${access.apiBaseUrl}${path}`, {
|
|
153
|
+
...init,
|
|
154
|
+
headers
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
104
159
|
// src/github-device.ts
|
|
105
160
|
import { setTimeout as sleep } from "timers/promises";
|
|
106
|
-
var DEFAULT_GITHUB_COPILOT_CLIENT_ID = "
|
|
161
|
+
var DEFAULT_GITHUB_COPILOT_CLIENT_ID = "Ov23li8tweQw6odWQebz";
|
|
107
162
|
var DEFAULT_GITHUB_DOMAIN = "github.com";
|
|
108
163
|
var DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
109
164
|
var POLLING_SAFETY_MARGIN_MS = 3e3;
|
|
@@ -321,61 +376,6 @@ function isLogLevel(value) {
|
|
|
321
376
|
return LOG_LEVELS.includes(value);
|
|
322
377
|
}
|
|
323
378
|
|
|
324
|
-
// src/copilot.ts
|
|
325
|
-
var CopilotClient = class {
|
|
326
|
-
#auth;
|
|
327
|
-
#fetch;
|
|
328
|
-
constructor(options = {}) {
|
|
329
|
-
this.#auth = new CopilotAuth(options);
|
|
330
|
-
this.#fetch = options.fetch ?? fetch;
|
|
331
|
-
}
|
|
332
|
-
async chatCompletions(body, signal) {
|
|
333
|
-
return this.fetchCopilot("/chat/completions", {
|
|
334
|
-
body: JSON.stringify(body),
|
|
335
|
-
headers: {
|
|
336
|
-
"content-type": "application/json"
|
|
337
|
-
},
|
|
338
|
-
method: "POST",
|
|
339
|
-
signal
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
async responses(body, signal) {
|
|
343
|
-
return this.fetchCopilot("/responses", {
|
|
344
|
-
body,
|
|
345
|
-
headers: {
|
|
346
|
-
"content-type": "application/json"
|
|
347
|
-
},
|
|
348
|
-
method: "POST",
|
|
349
|
-
signal
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
async models(signal) {
|
|
353
|
-
return this.fetchCopilot("/models", {
|
|
354
|
-
headers: {
|
|
355
|
-
accept: "application/json"
|
|
356
|
-
},
|
|
357
|
-
method: "GET",
|
|
358
|
-
signal
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
async fetchCopilot(path, init) {
|
|
362
|
-
const access = await this.#auth.getAccess();
|
|
363
|
-
const headers = new Headers(init.headers);
|
|
364
|
-
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
365
|
-
headers.set("authorization", `Bearer ${access.token}`);
|
|
366
|
-
headers.set("copilot-integration-id", "vscode-chat");
|
|
367
|
-
headers.set("editor-plugin-version", "hoopilot/0.1.0");
|
|
368
|
-
headers.set("editor-version", "Hoopilot/0.1.0");
|
|
369
|
-
headers.set("openai-intent", "conversation-panel");
|
|
370
|
-
headers.set("user-agent", "hoopilot/0.1.0");
|
|
371
|
-
headers.set("x-github-api-version", "2026-06-01");
|
|
372
|
-
return this.#fetch(`${access.apiBaseUrl}${path}`, {
|
|
373
|
-
...init,
|
|
374
|
-
headers
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
|
|
379
379
|
// src/openai.ts
|
|
380
380
|
var DEFAULT_MODEL = "gpt-4.1";
|
|
381
381
|
function normalizeChatCompletionRequest(request) {
|
|
@@ -1460,6 +1460,20 @@ async function main(argv = Bun.argv.slice(2)) {
|
|
|
1460
1460
|
await runLogin(args2);
|
|
1461
1461
|
return;
|
|
1462
1462
|
}
|
|
1463
|
+
if (command === "models") {
|
|
1464
|
+
const args2 = withRuntimeEnv(parseArgs(argv.slice(1)));
|
|
1465
|
+
if (args2.help) {
|
|
1466
|
+
console.log(helpText(await getVersion()));
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
args2.logger = createHoopilotLogger({
|
|
1470
|
+
env: args2.env,
|
|
1471
|
+
format: args2.logFormat,
|
|
1472
|
+
level: args2.logLevel
|
|
1473
|
+
}).child({ component: "cli", command: "models" });
|
|
1474
|
+
await runModels(args2);
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1463
1477
|
const args = withRuntimeEnv(parseArgs(argv));
|
|
1464
1478
|
if (args.help) {
|
|
1465
1479
|
console.log(helpText(await getVersion()));
|
|
@@ -1585,6 +1599,30 @@ async function runLogin(options = {}) {
|
|
|
1585
1599
|
console.log(`Copilot OAuth credential stored at ${path}`);
|
|
1586
1600
|
console.log("Copilot authentication ready.");
|
|
1587
1601
|
}
|
|
1602
|
+
async function runModels(options = {}) {
|
|
1603
|
+
const logger = options.logger?.child({ component: "models" }) ?? noopLogger;
|
|
1604
|
+
logger.debug({ event: "models.list.started" }, "fetching github copilot models");
|
|
1605
|
+
const response = await new CopilotClient(options).models();
|
|
1606
|
+
if (!response.ok) {
|
|
1607
|
+
const message = `GitHub Copilot API model list failed with ${response.status}: ${await safeResponseText2(response)}`;
|
|
1608
|
+
if (response.status === 401 || response.status === 403) {
|
|
1609
|
+
throw new CopilotAuthError(message);
|
|
1610
|
+
}
|
|
1611
|
+
throw new Error(message);
|
|
1612
|
+
}
|
|
1613
|
+
const ids = modelIdsFromResponse(await response.json().catch(() => void 0));
|
|
1614
|
+
if (ids.length === 0) {
|
|
1615
|
+
throw new Error("GitHub Copilot API returned no model IDs.");
|
|
1616
|
+
}
|
|
1617
|
+
logger.debug(
|
|
1618
|
+
{ count: ids.length, event: "models.list.succeeded" },
|
|
1619
|
+
"github copilot models fetched"
|
|
1620
|
+
);
|
|
1621
|
+
for (const id of ids) {
|
|
1622
|
+
console.log(id);
|
|
1623
|
+
}
|
|
1624
|
+
return ids;
|
|
1625
|
+
}
|
|
1588
1626
|
async function verifyCopilotOAuthToken(token, options = {}) {
|
|
1589
1627
|
const apiBaseUrl = trimTrailingSlash2(
|
|
1590
1628
|
options.copilotApiBaseUrl ?? options.env?.COPILOT_API_BASE_URL ?? DEFAULT_COPILOT_API_BASE_URL2
|
|
@@ -1640,6 +1678,24 @@ async function safeResponseText2(response) {
|
|
|
1640
1678
|
function trimTrailingSlash2(value) {
|
|
1641
1679
|
return value.replace(/\/+$/, "");
|
|
1642
1680
|
}
|
|
1681
|
+
function modelIdsFromResponse(body) {
|
|
1682
|
+
const record = asRecord2(body);
|
|
1683
|
+
const data = Array.isArray(record.data) ? record.data : Array.isArray(body) ? body : [];
|
|
1684
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1685
|
+
const ids = [];
|
|
1686
|
+
for (const model of data) {
|
|
1687
|
+
const id = asRecord2(model).id;
|
|
1688
|
+
if (typeof id !== "string" || id.length === 0 || seen.has(id)) {
|
|
1689
|
+
continue;
|
|
1690
|
+
}
|
|
1691
|
+
seen.add(id);
|
|
1692
|
+
ids.push(id);
|
|
1693
|
+
}
|
|
1694
|
+
return ids;
|
|
1695
|
+
}
|
|
1696
|
+
function asRecord2(value) {
|
|
1697
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1698
|
+
}
|
|
1643
1699
|
function withRuntimeEnv(args) {
|
|
1644
1700
|
return { ...args, env: process.env };
|
|
1645
1701
|
}
|
|
@@ -1651,12 +1707,14 @@ OpenAI-compatible proxy for GitHub Copilot.
|
|
|
1651
1707
|
Usage:
|
|
1652
1708
|
hoopilot [serve] [options]
|
|
1653
1709
|
hoopilot login [options]
|
|
1710
|
+
hoopilot models [options]
|
|
1654
1711
|
hoopilot update
|
|
1655
1712
|
npx @openhoo/hoopilot [options]
|
|
1656
1713
|
|
|
1657
1714
|
Commands:
|
|
1658
1715
|
serve Start the proxy server (default)
|
|
1659
1716
|
login Sign in through GitHub OAuth in a browser and verify Copilot access
|
|
1717
|
+
models List available GitHub Copilot model IDs
|
|
1660
1718
|
update, upgrade Update hoopilot to the latest release
|
|
1661
1719
|
|
|
1662
1720
|
Options:
|
|
@@ -1692,6 +1750,7 @@ if (import.meta.main) {
|
|
|
1692
1750
|
export {
|
|
1693
1751
|
main,
|
|
1694
1752
|
parseArgs,
|
|
1753
|
+
runModels,
|
|
1695
1754
|
verifyCopilotOAuthToken
|
|
1696
1755
|
};
|
|
1697
1756
|
//# sourceMappingURL=cli.js.map
|