@openhoo/hoopilot 0.6.0 → 0.6.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 CHANGED
@@ -97,20 +97,7 @@ $env:OPENAI_BASE_URL = "http://127.0.0.1:4141/v1"
97
97
  $env:OPENAI_API_KEY = "local-key"
98
98
  ```
99
99
 
100
- Use with Codex CLI after Hoopilot is running:
101
-
102
- ```powershell
103
- $env:OPENAI_API_KEY = "local-key"
104
- codex -m gpt-5.5 -c 'model_reasoning_effort="xhigh"' -c 'openai_base_url="http://127.0.0.1:4141/v1"'
105
- ```
106
-
107
- One-line PowerShell form:
108
-
109
- ```powershell
110
- $env:OPENAI_API_KEY = "local-key"; codex -m gpt-5.5 -c 'model_reasoning_effort="xhigh"' -c 'openai_base_url="http://127.0.0.1:4141/v1"'
111
- ```
112
-
113
- Or use the bundled `codexx` convenience command after Hoopilot is already running:
100
+ Use with Codex CLI after Hoopilot is running, via the bundled `codexx` command. It runs Codex against the local server with the right model provider — selecting `gpt-5.5` over Copilot's Responses API, which a plain `openai_base_url` override does not configure (see the note below):
114
101
 
115
102
  ```powershell
116
103
  $env:HOOPILOT_API_KEY = "local-key"
@@ -203,7 +190,7 @@ Then, in another PowerShell session:
203
190
  $env:OPENAI_API_KEY = "local-key"
204
191
  Invoke-RestMethod -Headers @{ Authorization = "Bearer $env:OPENAI_API_KEY" } `
205
192
  http://127.0.0.1:4141/v1/models
206
- codex -m gpt-5.5 -c 'model_reasoning_effort="xhigh"' -c 'openai_base_url="http://127.0.0.1:4141/v1"'
193
+ codexx
207
194
  ```
208
195
 
209
196
  If that returns `401 copilot_auth_error`, rerun `npx @openhoo/hoopilot login` and confirm the GitHub account has active Copilot access.
@@ -230,7 +217,7 @@ Options:
230
217
  ```txt
231
218
  -p, --port <port> Port to listen on. Default: 4141
232
219
  --host <host> Host to listen on. Default: 127.0.0.1
233
- --api-key <key> Require clients to send Authorization: Bearer <key>
220
+ --api-key <key> Require clients to send Authorization: Bearer <key> or x-api-key: <key>
234
221
  --auth-file <path> OAuth credential store path
235
222
  --copilot-api-base-url <url> Copilot API base URL override
236
223
  --log-level <level> trace, debug, info, warn, error, fatal, or silent
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { spawn } from "child_process";
5
5
 
6
6
  // src/auth-store.ts
7
- import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "fs";
7
+ import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
8
8
  import { dirname, join } from "path";
9
9
  function authStorePath(env = process.env) {
10
10
  if (env.HOOPILOT_AUTH_FILE) {
@@ -36,25 +36,36 @@ function readStoredCopilotAuth(path = authStorePath()) {
36
36
  }
37
37
  function writeStoredCopilotAuth(auth, path = authStorePath()) {
38
38
  mkdirSync(dirname(path), { recursive: true });
39
- writeFileSync(
40
- path,
41
- `${JSON.stringify(
42
- {
43
- ...auth,
44
- createdAt: auth.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
45
- },
46
- null,
47
- 2
48
- )}
49
- `,
50
- { mode: 384 }
51
- );
39
+ const data = `${JSON.stringify(
40
+ {
41
+ ...auth,
42
+ createdAt: auth.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
43
+ },
44
+ null,
45
+ 2
46
+ )}
47
+ `;
48
+ const tmpPath = `${path}.${process.pid}.tmp`;
49
+ writeFileSync(tmpPath, data, { mode: 384 });
50
+ renameSync(tmpPath, path);
52
51
  try {
53
52
  chmodSync(path, 384);
54
53
  } catch {
55
54
  }
56
55
  }
57
56
 
57
+ // src/util.ts
58
+ function trimTrailingSlash(value) {
59
+ return value.replace(/\/+$/, "");
60
+ }
61
+ async function truncatedResponseText(response, max = 500) {
62
+ const text = await response.text();
63
+ return text.slice(0, max);
64
+ }
65
+ function asRecord(value) {
66
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
67
+ }
68
+
58
69
  // src/auth.ts
59
70
  var DEFAULT_COPILOT_API_BASE_URL = "https://api.githubcopilot.com";
60
71
  var REFRESH_SKEW_MS = 6e4;
@@ -97,11 +108,19 @@ var CopilotAuth = class {
97
108
  return access;
98
109
  }
99
110
  };
100
- function trimTrailingSlash(value) {
101
- return value.replace(/\/+$/, "");
102
- }
103
111
 
104
112
  // src/copilot.ts
113
+ function applyCopilotHeaders(headers, token) {
114
+ headers.set("accept", headers.get("accept") ?? "application/json");
115
+ headers.set("authorization", `Bearer ${token}`);
116
+ headers.set("copilot-integration-id", "vscode-chat");
117
+ headers.set("editor-plugin-version", "hoopilot/0.1.0");
118
+ headers.set("editor-version", "Hoopilot/0.1.0");
119
+ headers.set("openai-intent", "conversation-panel");
120
+ headers.set("user-agent", "hoopilot/0.1.0");
121
+ headers.set("x-github-api-version", "2026-06-01");
122
+ return headers;
123
+ }
105
124
  var CopilotClient = class {
106
125
  #auth;
107
126
  #fetch;
@@ -140,15 +159,7 @@ var CopilotClient = class {
140
159
  }
141
160
  async fetchCopilot(path, init) {
142
161
  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");
162
+ const headers = applyCopilotHeaders(new Headers(init.headers), access.token);
152
163
  return this.#fetch(`${access.apiBaseUrl}${path}`, {
153
164
  ...init,
154
165
  headers
@@ -162,6 +173,7 @@ var DEFAULT_GITHUB_COPILOT_CLIENT_ID = "Ov23li8tweQw6odWQebz";
162
173
  var DEFAULT_GITHUB_DOMAIN = "github.com";
163
174
  var DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
164
175
  var POLLING_SAFETY_MARGIN_MS = 3e3;
176
+ var REQUEST_TIMEOUT_MS = 15e3;
165
177
  async function githubCopilotDeviceLogin(options = {}) {
166
178
  const env = options.env ?? process.env;
167
179
  const fetcher = options.fetch ?? fetch;
@@ -196,16 +208,20 @@ async function requestDeviceCode(fetcher, domain, clientId) {
196
208
  scope: "read:user"
197
209
  }),
198
210
  headers: oauthHeaders(),
199
- method: "POST"
211
+ method: "POST",
212
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
200
213
  });
201
214
  if (!response.ok) {
202
215
  throw new Error(
203
- `GitHub device authorization failed with ${response.status}: ${await safeResponseText(
216
+ `GitHub device authorization failed with ${response.status}: ${await truncatedResponseText(
204
217
  response
205
218
  )}`
206
219
  );
207
220
  }
208
- return await response.json();
221
+ return parseJsonResponse(
222
+ response,
223
+ "GitHub device authorization response was not valid JSON"
224
+ );
209
225
  }
210
226
  async function pollForAccessToken(fetcher, sleeper, domain, clientId, device) {
211
227
  let intervalMs = device.interval * 1e3 + POLLING_SAFETY_MARGIN_MS;
@@ -219,16 +235,20 @@ async function pollForAccessToken(fetcher, sleeper, domain, clientId, device) {
219
235
  grant_type: DEVICE_GRANT_TYPE
220
236
  }),
221
237
  headers: oauthHeaders(),
222
- method: "POST"
238
+ method: "POST",
239
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
223
240
  });
224
241
  if (!response.ok) {
225
242
  throw new Error(
226
- `GitHub device token exchange failed with ${response.status}: ${await safeResponseText(
243
+ `GitHub device token exchange failed with ${response.status}: ${await truncatedResponseText(
227
244
  response
228
245
  )}`
229
246
  );
230
247
  }
231
- const data = await response.json();
248
+ const data = await parseJsonResponse(
249
+ response,
250
+ "GitHub device token response was not valid JSON"
251
+ );
232
252
  if (data.access_token) {
233
253
  return data.access_token;
234
254
  }
@@ -264,9 +284,13 @@ function normalizeDomain(value) {
264
284
  function positiveSeconds(value, fallback) {
265
285
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
266
286
  }
267
- async function safeResponseText(response) {
287
+ async function parseJsonResponse(response, context) {
268
288
  const text = await response.text();
269
- return text.slice(0, 500);
289
+ try {
290
+ return JSON.parse(text);
291
+ } catch {
292
+ throw new Error(`${context}: ${text.slice(0, 500)}`);
293
+ }
270
294
  }
271
295
 
272
296
  // src/logger.ts
@@ -369,6 +393,16 @@ function shouldCreateLogger(options) {
369
393
  options.logger || options.logFormat || options.logLevel || options.env?.HOOPILOT_LOG_FORMAT || options.env?.HOOPILOT_LOG_LEVEL
370
394
  );
371
395
  }
396
+ function errorDetails(error) {
397
+ if (error instanceof Error) {
398
+ return {
399
+ message: error.message,
400
+ name: error.name,
401
+ stack: error.stack
402
+ };
403
+ }
404
+ return { message: String(error) };
405
+ }
372
406
  function isLogFormat(value) {
373
407
  return LOG_FORMATS.includes(value);
374
408
  }
@@ -476,9 +510,6 @@ function firstChoice(completion) {
476
510
  function removeUndefined(record) {
477
511
  return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== void 0));
478
512
  }
479
- function asRecord(value) {
480
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
481
- }
482
513
  function randomId() {
483
514
  return crypto.randomUUID().replaceAll("-", "");
484
515
  }
@@ -668,6 +699,9 @@ async function handleCompletions(client, request, logger) {
668
699
  return proxyError(upstream, logger);
669
700
  }
670
701
  logUpstreamSuccess(logger, "/chat/completions", upstream.status);
702
+ if (isStreamingResponse(upstream)) {
703
+ return proxyResponse(upstream);
704
+ }
671
705
  return jsonResponse(chatCompletionToCompletion(await upstream.json()));
672
706
  }
673
707
  async function handleResponses(client, request, logger) {
@@ -710,8 +744,7 @@ function proxyResponse(upstream) {
710
744
  }
711
745
  async function readJson(request) {
712
746
  try {
713
- const value = await request.json();
714
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
747
+ return asRecord(await request.json());
715
748
  } catch {
716
749
  throw new Error(INVALID_JSON_MESSAGE);
717
750
  }
@@ -882,16 +915,6 @@ function logUpstreamSuccess(logger, upstreamPath, status) {
882
915
  "copilot upstream request completed"
883
916
  );
884
917
  }
885
- function errorDetails(error) {
886
- if (error instanceof Error) {
887
- return {
888
- message: error.message,
889
- name: error.name,
890
- stack: error.stack
891
- };
892
- }
893
- return { message: String(error) };
894
- }
895
918
 
896
919
  // src/update.ts
897
920
  import { execFileSync } from "child_process";
@@ -902,7 +925,7 @@ import {
902
925
  existsSync,
903
926
  mkdirSync as mkdirSync2,
904
927
  realpathSync,
905
- renameSync,
928
+ renameSync as renameSync2,
906
929
  rmSync
907
930
  } from "fs";
908
931
  import { readFile, writeFile } from "fs/promises";
@@ -1116,7 +1139,7 @@ async function getVersion() {
1116
1139
  }
1117
1140
 
1118
1141
  // src/update.ts
1119
- var REQUEST_TIMEOUT_MS = 8e3;
1142
+ var REQUEST_TIMEOUT_MS2 = 8e3;
1120
1143
  var SHA256SUMS = "SHA256SUMS";
1121
1144
  function userAgent(version) {
1122
1145
  return `hoopilot/${version}`;
@@ -1153,7 +1176,7 @@ async function fetchLatest(version, etag) {
1153
1176
  }
1154
1177
  const response = await fetch(latestReleaseApiUrl(), {
1155
1178
  headers,
1156
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
1179
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
1157
1180
  });
1158
1181
  if (response.status === 304) {
1159
1182
  return { status: 304, etag: etag ?? null, release: null };
@@ -1192,7 +1215,7 @@ async function maybeNotifyUpdate(currentVersion, kind, logger) {
1192
1215
  logger?.debug({ event: "update.check.refresh_queued" }, "queued update check refresh");
1193
1216
  void refreshState(currentVersion, state.etag ?? null, logger).catch((error) => {
1194
1217
  logger?.debug(
1195
- { err: errorDetails2(error), event: "update.check.refresh_failed" },
1218
+ { err: errorDetails(error), event: "update.check.refresh_failed" },
1196
1219
  "update check refresh failed"
1197
1220
  );
1198
1221
  });
@@ -1254,7 +1277,7 @@ async function downloadToFile(url, dest, version) {
1254
1277
  const response = await fetch(url, {
1255
1278
  headers: { "User-Agent": userAgent(version) },
1256
1279
  redirect: "follow",
1257
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS * 10)
1280
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2 * 10)
1258
1281
  });
1259
1282
  if (!response.ok || !response.body) {
1260
1283
  throw new Error(`Download failed (${response.status}) for ${url}`);
@@ -1274,7 +1297,7 @@ async function verifyChecksum(release, assetName, file, version) {
1274
1297
  const response = await fetch(sums.url, {
1275
1298
  headers: { "User-Agent": userAgent(version) },
1276
1299
  redirect: "follow",
1277
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
1300
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
1278
1301
  });
1279
1302
  if (!response.ok) {
1280
1303
  throw new Error(`Could not download ${SHA256SUMS} (${response.status}).`);
@@ -1295,15 +1318,15 @@ function swapBinary(tmpFile, exePath) {
1295
1318
  rmSync(oldExe, { force: true });
1296
1319
  } catch {
1297
1320
  }
1298
- renameSync(exePath, oldExe);
1321
+ renameSync2(exePath, oldExe);
1299
1322
  const restore = () => {
1300
1323
  try {
1301
- renameSync(oldExe, exePath);
1324
+ renameSync2(oldExe, exePath);
1302
1325
  } catch {
1303
1326
  }
1304
1327
  };
1305
1328
  try {
1306
- renameSync(tmpFile, exePath);
1329
+ renameSync2(tmpFile, exePath);
1307
1330
  } catch (error) {
1308
1331
  if (error.code === "EXDEV") {
1309
1332
  try {
@@ -1320,7 +1343,7 @@ function swapBinary(tmpFile, exePath) {
1320
1343
  return;
1321
1344
  }
1322
1345
  try {
1323
- renameSync(tmpFile, exePath);
1346
+ renameSync2(tmpFile, exePath);
1324
1347
  } catch (error) {
1325
1348
  const code = error.code;
1326
1349
  if (code === "EXDEV") {
@@ -1416,19 +1439,8 @@ async function runUpdate(currentVersion, logger) {
1416
1439
  console.log("Restart hoopilot to run the new version.");
1417
1440
  }
1418
1441
  }
1419
- function errorDetails2(error) {
1420
- if (error instanceof Error) {
1421
- return {
1422
- message: error.message,
1423
- name: error.name,
1424
- stack: error.stack
1425
- };
1426
- }
1427
- return { message: String(error) };
1428
- }
1429
1442
 
1430
1443
  // src/cli.ts
1431
- var DEFAULT_COPILOT_API_BASE_URL2 = "https://api.githubcopilot.com";
1432
1444
  async function main(argv = Bun.argv.slice(2)) {
1433
1445
  cleanupOldBinary();
1434
1446
  const command = argv[0];
@@ -1438,11 +1450,7 @@ async function main(argv = Bun.argv.slice(2)) {
1438
1450
  console.log(helpText(await getVersion()));
1439
1451
  return;
1440
1452
  }
1441
- const logger2 = createHoopilotLogger({
1442
- env: args2.env,
1443
- format: args2.logFormat,
1444
- level: args2.logLevel
1445
- }).child({ component: "cli", command });
1453
+ const logger2 = commandLogger(args2, command);
1446
1454
  await runUpdate(await getVersion(), logger2);
1447
1455
  return;
1448
1456
  }
@@ -1452,11 +1460,7 @@ async function main(argv = Bun.argv.slice(2)) {
1452
1460
  console.log(helpText(await getVersion()));
1453
1461
  return;
1454
1462
  }
1455
- args2.logger = createHoopilotLogger({
1456
- env: args2.env,
1457
- format: args2.logFormat,
1458
- level: args2.logLevel
1459
- }).child({ component: "cli", command: "login" });
1463
+ args2.logger = commandLogger(args2, "login");
1460
1464
  await runLogin(args2);
1461
1465
  return;
1462
1466
  }
@@ -1466,11 +1470,7 @@ async function main(argv = Bun.argv.slice(2)) {
1466
1470
  console.log(helpText(await getVersion()));
1467
1471
  return;
1468
1472
  }
1469
- args2.logger = createHoopilotLogger({
1470
- env: args2.env,
1471
- format: args2.logFormat,
1472
- level: args2.logLevel
1473
- }).child({ component: "cli", command: "models" });
1473
+ args2.logger = commandLogger(args2, "models");
1474
1474
  await runModels(args2);
1475
1475
  return;
1476
1476
  }
@@ -1483,11 +1483,7 @@ async function main(argv = Bun.argv.slice(2)) {
1483
1483
  console.log(await getVersion());
1484
1484
  return;
1485
1485
  }
1486
- const logger = createHoopilotLogger({
1487
- env: args.env,
1488
- format: args.logFormat,
1489
- level: args.logLevel
1490
- }).child({ component: "cli", command: "serve" });
1486
+ const logger = commandLogger(args, "serve");
1491
1487
  args.logger = logger;
1492
1488
  const started = startHoopilotServer(args);
1493
1489
  logger.info(
@@ -1498,7 +1494,7 @@ async function main(argv = Bun.argv.slice(2)) {
1498
1494
  },
1499
1495
  "hoopilot server started"
1500
1496
  );
1501
- if (!args.noUpdateCheck && process.env.HOOPILOT_NO_UPDATE_CHECK !== "1") {
1497
+ if (!args.noUpdateCheck) {
1502
1498
  void maybeNotifyUpdate(
1503
1499
  await getVersion(),
1504
1500
  IS_STANDALONE_BINARY ? "binary" : "npm",
@@ -1604,7 +1600,7 @@ async function runModels(options = {}) {
1604
1600
  logger.debug({ event: "models.list.started" }, "fetching github copilot models");
1605
1601
  const response = await new CopilotClient(options).models();
1606
1602
  if (!response.ok) {
1607
- const message = `GitHub Copilot API model list failed with ${response.status}: ${await safeResponseText2(response)}`;
1603
+ const message = `GitHub Copilot API model list failed with ${response.status}: ${await truncatedResponseText(response)}`;
1608
1604
  if (response.status === 401 || response.status === 403) {
1609
1605
  throw new CopilotAuthError(message);
1610
1606
  }
@@ -1624,16 +1620,16 @@ async function runModels(options = {}) {
1624
1620
  return ids;
1625
1621
  }
1626
1622
  async function verifyCopilotOAuthToken(token, options = {}) {
1627
- const apiBaseUrl = trimTrailingSlash2(
1628
- options.copilotApiBaseUrl ?? options.env?.COPILOT_API_BASE_URL ?? DEFAULT_COPILOT_API_BASE_URL2
1623
+ const apiBaseUrl = trimTrailingSlash(
1624
+ options.copilotApiBaseUrl ?? options.env?.COPILOT_API_BASE_URL ?? DEFAULT_COPILOT_API_BASE_URL
1629
1625
  );
1630
1626
  const fetcher = options.fetch ?? fetch;
1631
1627
  const response = await fetcher(`${apiBaseUrl}/models`, {
1632
- headers: copilotHeaders(token),
1628
+ headers: applyCopilotHeaders(new Headers(), token),
1633
1629
  method: "GET"
1634
1630
  });
1635
1631
  if (!response.ok) {
1636
- const message = `GitHub Copilot API verification failed with ${response.status}: ${await safeResponseText2(response)}`;
1632
+ const message = `GitHub Copilot API verification failed with ${response.status}: ${await truncatedResponseText(response)}`;
1637
1633
  if (response.status === 401 || response.status === 403) {
1638
1634
  throw new CopilotAuthError(message);
1639
1635
  }
@@ -1659,32 +1655,13 @@ function openBrowserBestEffort(url) {
1659
1655
  } catch {
1660
1656
  }
1661
1657
  }
1662
- function copilotHeaders(token) {
1663
- const headers = new Headers();
1664
- headers.set("accept", "application/json");
1665
- headers.set("authorization", `Bearer ${token}`);
1666
- headers.set("copilot-integration-id", "vscode-chat");
1667
- headers.set("editor-plugin-version", "hoopilot/0.1.0");
1668
- headers.set("editor-version", "Hoopilot/0.1.0");
1669
- headers.set("openai-intent", "conversation-panel");
1670
- headers.set("user-agent", "hoopilot/0.1.0");
1671
- headers.set("x-github-api-version", "2026-06-01");
1672
- return headers;
1673
- }
1674
- async function safeResponseText2(response) {
1675
- const text = await response.text();
1676
- return text.slice(0, 500);
1677
- }
1678
- function trimTrailingSlash2(value) {
1679
- return value.replace(/\/+$/, "");
1680
- }
1681
1658
  function modelIdsFromResponse(body) {
1682
- const record = asRecord2(body);
1659
+ const record = asRecord(body);
1683
1660
  const data = Array.isArray(record.data) ? record.data : Array.isArray(body) ? body : [];
1684
1661
  const seen = /* @__PURE__ */ new Set();
1685
1662
  const ids = [];
1686
1663
  for (const model of data) {
1687
- const id = asRecord2(model).id;
1664
+ const id = asRecord(model).id;
1688
1665
  if (typeof id !== "string" || id.length === 0 || seen.has(id)) {
1689
1666
  continue;
1690
1667
  }
@@ -1693,12 +1670,16 @@ function modelIdsFromResponse(body) {
1693
1670
  }
1694
1671
  return ids;
1695
1672
  }
1696
- function asRecord2(value) {
1697
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1698
- }
1699
1673
  function withRuntimeEnv(args) {
1700
1674
  return { ...args, env: process.env };
1701
1675
  }
1676
+ function commandLogger(args, command) {
1677
+ return createHoopilotLogger({
1678
+ env: args.env,
1679
+ format: args.logFormat,
1680
+ level: args.logLevel
1681
+ }).child({ command, component: "cli" });
1682
+ }
1702
1683
  function helpText(version) {
1703
1684
  return `hoopilot ${version}
1704
1685
 
@@ -1720,7 +1701,7 @@ Commands:
1720
1701
  Options:
1721
1702
  -p, --port <port> Port to listen on. Default: 4141
1722
1703
  --host <host> Host to listen on. Default: 127.0.0.1
1723
- --api-key <key> Require clients to send Authorization: Bearer <key>
1704
+ --api-key <key> Require clients to send Authorization: Bearer <key> or x-api-key: <key>
1724
1705
  --auth-file <path> OAuth credential store path
1725
1706
  --copilot-api-base-url <url> Copilot API base URL override
1726
1707
  --log-level <level> trace, debug, info, warn, error, fatal, or silent