@openhoo/hoopilot 0.5.6 → 0.5.8

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/dist/codexx.js CHANGED
@@ -6,6 +6,8 @@ import { constants as osConstants } from "os";
6
6
  var DEFAULT_BASE_URL = "http://127.0.0.1:4141/v1";
7
7
  var DEFAULT_API_KEY = "local-key";
8
8
  var DEFAULT_CODEX_BIN = "codex";
9
+ var DEFAULT_MODEL = "gpt-5.5";
10
+ var DEFAULT_REASONING_EFFORT = "xhigh";
9
11
  var PROXY_ENV_KEYS = [
10
12
  "ALL_PROXY",
11
13
  "HTTPS_PROXY",
@@ -20,6 +22,8 @@ function buildCodexxInvocation(argv, env = process.env) {
20
22
  const baseUrl = env.CODEXX_BASE_URL ?? DEFAULT_BASE_URL;
21
23
  const apiKey = env.CODEXX_API_KEY ?? env.HOOPILOT_API_KEY ?? env.OPENAI_API_KEY ?? DEFAULT_API_KEY;
22
24
  const command = env.CODEXX_CODEX_BIN ?? DEFAULT_CODEX_BIN;
25
+ const model = env.CODEXX_MODEL ?? DEFAULT_MODEL;
26
+ const reasoningEffort = env.CODEXX_MODEL_REASONING_EFFORT ?? DEFAULT_REASONING_EFFORT;
23
27
  const providerConfig = [
24
28
  '{ name = "Hoopilot"',
25
29
  `base_url = ${JSON.stringify(baseUrl)}`,
@@ -35,13 +39,19 @@ function buildCodexxInvocation(argv, env = process.env) {
35
39
  'model_provider="hoopilot"',
36
40
  "-c",
37
41
  `model_providers.hoopilot=${providerConfig}`,
42
+ "-m",
43
+ model,
44
+ "-c",
45
+ `model_reasoning_effort=${JSON.stringify(reasoningEffort)}`,
38
46
  ...argv
39
47
  ],
48
+ baseUrl,
40
49
  command,
41
50
  env: withoutProxyEnv({
42
51
  ...env,
43
52
  OPENAI_API_KEY: apiKey
44
- })
53
+ }),
54
+ model
45
55
  };
46
56
  }
47
57
  function withoutProxyEnv(env) {
@@ -57,6 +67,9 @@ async function main(argv = Bun.argv.slice(2), env = process.env) {
57
67
  return;
58
68
  }
59
69
  const invocation = buildCodexxInvocation(argv, env);
70
+ if (env.CODEXX_SKIP_MODEL_PREFLIGHT !== "1") {
71
+ await verifyCodexxModel(invocation);
72
+ }
60
73
  const child = spawn(invocation.command, invocation.args, {
61
74
  env: invocation.env,
62
75
  shell: process.platform === "win32",
@@ -74,6 +87,34 @@ async function main(argv = Bun.argv.slice(2), env = process.env) {
74
87
  });
75
88
  process.exitCode = exitCode;
76
89
  }
90
+ async function verifyCodexxModel(invocation, fetcher = fetch) {
91
+ const modelsUrl = `${invocation.baseUrl.replace(/\/+$/, "")}/models`;
92
+ let response;
93
+ try {
94
+ response = await fetcher(modelsUrl, {
95
+ headers: {
96
+ accept: "application/json",
97
+ authorization: `Bearer ${invocation.env.OPENAI_API_KEY ?? DEFAULT_API_KEY}`
98
+ },
99
+ method: "GET"
100
+ });
101
+ } catch (error) {
102
+ throw new Error(
103
+ `Could not reach Hoopilot at ${modelsUrl}. Start Hoopilot first, or set CODEXX_SKIP_MODEL_PREFLIGHT=1 to skip this check. ${errorMessage(error)}`
104
+ );
105
+ }
106
+ if (!response.ok) {
107
+ throw new Error(
108
+ `Could not verify model ${JSON.stringify(invocation.model)} because ${modelsUrl} returned ${response.status}: ${await shortResponseText(response)}`
109
+ );
110
+ }
111
+ const models = modelIds(await response.json().catch(() => void 0));
112
+ if (models.length > 0 && !models.includes(invocation.model)) {
113
+ throw new Error(
114
+ `The logged-in Copilot account does not advertise model ${JSON.stringify(invocation.model)} at ${modelsUrl}. Available models: ${models.join(", ")}. After upgrading Hoopilot, rerun "hoopilot login" to refresh the Copilot OAuth token, or set CODEXX_MODEL to one of the advertised model IDs.`
115
+ );
116
+ }
117
+ }
77
118
  function helpText() {
78
119
  return `codexx
79
120
 
@@ -88,12 +129,31 @@ Environment:
88
129
  HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.
89
130
  OPENAI_API_KEY Used as the API key when both CODEXX_API_KEY and HOOPILOT_API_KEY are unset.
90
131
  CODEXX_CODEX_BIN Codex executable to run. Default: ${DEFAULT_CODEX_BIN}
132
+ CODEXX_MODEL Codex model to use. Default: ${DEFAULT_MODEL}
133
+ CODEXX_MODEL_REASONING_EFFORT
134
+ Codex reasoning effort. Default: ${DEFAULT_REASONING_EFFORT}
135
+ CODEXX_SKIP_MODEL_PREFLIGHT
136
+ Set to 1 to skip checking /v1/models before starting Codex.
91
137
 
92
- codexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;
138
+ codexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, uses ${DEFAULT_MODEL} with ${DEFAULT_REASONING_EFFORT} reasoning by default, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;
93
139
  }
94
140
  function signalNumber(signal) {
95
141
  return osConstants.signals[signal] ?? 1;
96
142
  }
143
+ function modelIds(value) {
144
+ const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
145
+ const data = "data" in record && Array.isArray(record.data) ? record.data : [];
146
+ return data.map(
147
+ (entry) => entry && typeof entry === "object" && "id" in entry && typeof entry.id === "string" ? entry.id : void 0
148
+ ).filter((id) => typeof id === "string" && id.length > 0);
149
+ }
150
+ async function shortResponseText(response) {
151
+ const text = await response.text();
152
+ return text.slice(0, 500);
153
+ }
154
+ function errorMessage(error) {
155
+ return error instanceof Error ? error.message : String(error);
156
+ }
97
157
  if (import.meta.main) {
98
158
  main().catch((error) => {
99
159
  console.error(error instanceof Error ? error.message : String(error));
@@ -102,6 +162,7 @@ if (import.meta.main) {
102
162
  }
103
163
  export {
104
164
  buildCodexxInvocation,
105
- main
165
+ main,
166
+ verifyCodexxModel
106
167
  };
107
168
  //# sourceMappingURL=codexx.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/codexx.ts"],"sourcesContent":["#!/usr/bin/env bun\n\nimport { spawn } from \"node:child_process\";\nimport { constants as osConstants } from \"node:os\";\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:4141/v1\";\nconst DEFAULT_API_KEY = \"local-key\";\nconst DEFAULT_CODEX_BIN = \"codex\";\nconst PROXY_ENV_KEYS = [\n \"ALL_PROXY\",\n \"HTTPS_PROXY\",\n \"HTTP_PROXY\",\n \"NO_PROXY\",\n \"all_proxy\",\n \"https_proxy\",\n \"http_proxy\",\n \"no_proxy\",\n];\n\nexport interface CodexxInvocation {\n args: string[];\n command: string;\n env: NodeJS.ProcessEnv;\n}\n\nexport function buildCodexxInvocation(\n argv: string[],\n env: NodeJS.ProcessEnv = process.env,\n): CodexxInvocation {\n const baseUrl = env.CODEXX_BASE_URL ?? DEFAULT_BASE_URL;\n const apiKey =\n env.CODEXX_API_KEY ?? env.HOOPILOT_API_KEY ?? env.OPENAI_API_KEY ?? DEFAULT_API_KEY;\n const command = env.CODEXX_CODEX_BIN ?? DEFAULT_CODEX_BIN;\n const providerConfig = [\n '{ name = \"Hoopilot\"',\n `base_url = ${JSON.stringify(baseUrl)}`,\n 'env_key = \"OPENAI_API_KEY\"',\n 'wire_api = \"responses\"',\n \"supports_websockets = false }\",\n ].join(\", \");\n\n return {\n args: [\n \"--disable\",\n \"network_proxy\",\n \"-c\",\n 'model_provider=\"hoopilot\"',\n \"-c\",\n `model_providers.hoopilot=${providerConfig}`,\n ...argv,\n ],\n command,\n env: withoutProxyEnv({\n ...env,\n OPENAI_API_KEY: apiKey,\n }),\n };\n}\n\nfunction withoutProxyEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const next = { ...env };\n for (const key of PROXY_ENV_KEYS) {\n delete next[key];\n }\n return next;\n}\n\nexport async function main(argv = Bun.argv.slice(2), env = process.env): Promise<void> {\n if (argv.length === 1 && (argv[0] === \"--help\" || argv[0] === \"-h\")) {\n console.log(helpText());\n return;\n }\n\n const invocation = buildCodexxInvocation(argv, env);\n const child = spawn(invocation.command, invocation.args, {\n env: invocation.env,\n shell: process.platform === \"win32\",\n stdio: \"inherit\",\n });\n\n const exitCode = await new Promise<number>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code, signal) => {\n if (typeof code === \"number\") {\n resolve(code);\n return;\n }\n resolve(signal ? 128 + signalNumber(signal) : 1);\n });\n });\n\n process.exitCode = exitCode;\n}\n\nfunction helpText(): string {\n return `codexx\n\nRun Codex against an already-running local Hoopilot server.\n\nUsage:\n codexx [codex options] [prompt]\n\nEnvironment:\n CODEXX_BASE_URL OpenAI-compatible base URL. Default: ${DEFAULT_BASE_URL}\n CODEXX_API_KEY API key sent to the local Hoopilot server.\n HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.\n OPENAI_API_KEY Used as the API key when both CODEXX_API_KEY and HOOPILOT_API_KEY are unset.\n CODEXX_CODEX_BIN Codex executable to run. Default: ${DEFAULT_CODEX_BIN}\n\ncodexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;\n}\n\nfunction signalNumber(signal: NodeJS.Signals): number {\n return osConstants.signals[signal] ?? 1;\n}\n\nif (import.meta.main) {\n main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n });\n}\n"],"mappings":";;;AAEA,SAAS,aAAa;AACtB,SAAS,aAAa,mBAAmB;AAEzC,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQO,SAAS,sBACd,MACA,MAAyB,QAAQ,KACf;AAClB,QAAM,UAAU,IAAI,mBAAmB;AACvC,QAAM,SACJ,IAAI,kBAAkB,IAAI,oBAAoB,IAAI,kBAAkB;AACtE,QAAM,UAAU,IAAI,oBAAoB;AACxC,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,cAAc,KAAK,UAAU,OAAO,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,SAAO;AAAA,IACL,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,4BAA4B,cAAc;AAAA,MAC1C,GAAG;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK,gBAAgB;AAAA,MACnB,GAAG;AAAA,MACH,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AACF;AAEA,SAAS,gBAAgB,KAA2C;AAClE,QAAM,OAAO,EAAE,GAAG,IAAI;AACtB,aAAW,OAAO,gBAAgB;AAChC,WAAO,KAAK,GAAG;AAAA,EACjB;AACA,SAAO;AACT;AAEA,eAAsB,KAAK,OAAO,IAAI,KAAK,MAAM,CAAC,GAAG,MAAM,QAAQ,KAAoB;AACrF,MAAI,KAAK,WAAW,MAAM,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,OAAO;AACnE,YAAQ,IAAI,SAAS,CAAC;AACtB;AAAA,EACF;AAEA,QAAM,aAAa,sBAAsB,MAAM,GAAG;AAClD,QAAM,QAAQ,MAAM,WAAW,SAAS,WAAW,MAAM;AAAA,IACvD,KAAK,WAAW;AAAA,IAChB,OAAO,QAAQ,aAAa;AAAA,IAC5B,OAAO;AAAA,EACT,CAAC;AAED,QAAM,WAAW,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9D,UAAM,KAAK,SAAS,MAAM;AAC1B,UAAM,KAAK,QAAQ,CAAC,MAAM,WAAW;AACnC,UAAI,OAAO,SAAS,UAAU;AAC5B,gBAAQ,IAAI;AACZ;AAAA,MACF;AACA,cAAQ,SAAS,MAAM,aAAa,MAAM,IAAI,CAAC;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AAED,UAAQ,WAAW;AACrB;AAEA,SAAS,WAAmB;AAC1B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8DAQqD,gBAAgB;AAAA;AAAA;AAAA;AAAA,2DAInB,iBAAiB;AAAA;AAAA;AAG5E;AAEA,SAAS,aAAa,QAAgC;AACpD,SAAO,YAAY,QAAQ,MAAM,KAAK;AACxC;AAEA,IAAI,YAAY,MAAM;AACpB,OAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,YAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../src/codexx.ts"],"sourcesContent":["#!/usr/bin/env bun\n\nimport { spawn } from \"node:child_process\";\nimport { constants as osConstants } from \"node:os\";\nimport type { FetchLike } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:4141/v1\";\nconst DEFAULT_API_KEY = \"local-key\";\nconst DEFAULT_CODEX_BIN = \"codex\";\nconst DEFAULT_MODEL = \"gpt-5.5\";\nconst DEFAULT_REASONING_EFFORT = \"xhigh\";\nconst PROXY_ENV_KEYS = [\n \"ALL_PROXY\",\n \"HTTPS_PROXY\",\n \"HTTP_PROXY\",\n \"NO_PROXY\",\n \"all_proxy\",\n \"https_proxy\",\n \"http_proxy\",\n \"no_proxy\",\n];\n\nexport interface CodexxInvocation {\n args: string[];\n baseUrl: string;\n command: string;\n env: NodeJS.ProcessEnv;\n model: string;\n}\n\nexport function buildCodexxInvocation(\n argv: string[],\n env: NodeJS.ProcessEnv = process.env,\n): CodexxInvocation {\n const baseUrl = env.CODEXX_BASE_URL ?? DEFAULT_BASE_URL;\n const apiKey =\n env.CODEXX_API_KEY ?? env.HOOPILOT_API_KEY ?? env.OPENAI_API_KEY ?? DEFAULT_API_KEY;\n const command = env.CODEXX_CODEX_BIN ?? DEFAULT_CODEX_BIN;\n const model = env.CODEXX_MODEL ?? DEFAULT_MODEL;\n const reasoningEffort = env.CODEXX_MODEL_REASONING_EFFORT ?? DEFAULT_REASONING_EFFORT;\n const providerConfig = [\n '{ name = \"Hoopilot\"',\n `base_url = ${JSON.stringify(baseUrl)}`,\n 'env_key = \"OPENAI_API_KEY\"',\n 'wire_api = \"responses\"',\n \"supports_websockets = false }\",\n ].join(\", \");\n\n return {\n args: [\n \"--disable\",\n \"network_proxy\",\n \"-c\",\n 'model_provider=\"hoopilot\"',\n \"-c\",\n `model_providers.hoopilot=${providerConfig}`,\n \"-m\",\n model,\n \"-c\",\n `model_reasoning_effort=${JSON.stringify(reasoningEffort)}`,\n ...argv,\n ],\n baseUrl,\n command,\n env: withoutProxyEnv({\n ...env,\n OPENAI_API_KEY: apiKey,\n }),\n model,\n };\n}\n\nfunction withoutProxyEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const next = { ...env };\n for (const key of PROXY_ENV_KEYS) {\n delete next[key];\n }\n return next;\n}\n\nexport async function main(argv = Bun.argv.slice(2), env = process.env): Promise<void> {\n if (argv.length === 1 && (argv[0] === \"--help\" || argv[0] === \"-h\")) {\n console.log(helpText());\n return;\n }\n\n const invocation = buildCodexxInvocation(argv, env);\n if (env.CODEXX_SKIP_MODEL_PREFLIGHT !== \"1\") {\n await verifyCodexxModel(invocation);\n }\n const child = spawn(invocation.command, invocation.args, {\n env: invocation.env,\n shell: process.platform === \"win32\",\n stdio: \"inherit\",\n });\n\n const exitCode = await new Promise<number>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code, signal) => {\n if (typeof code === \"number\") {\n resolve(code);\n return;\n }\n resolve(signal ? 128 + signalNumber(signal) : 1);\n });\n });\n\n process.exitCode = exitCode;\n}\n\nexport async function verifyCodexxModel(\n invocation: Pick<CodexxInvocation, \"baseUrl\" | \"env\" | \"model\">,\n fetcher: FetchLike = fetch,\n): Promise<void> {\n const modelsUrl = `${invocation.baseUrl.replace(/\\/+$/, \"\")}/models`;\n let response: Response;\n try {\n response = await fetcher(modelsUrl, {\n headers: {\n accept: \"application/json\",\n authorization: `Bearer ${invocation.env.OPENAI_API_KEY ?? DEFAULT_API_KEY}`,\n },\n method: \"GET\",\n });\n } catch (error) {\n throw new Error(\n `Could not reach Hoopilot at ${modelsUrl}. Start Hoopilot first, or set CODEXX_SKIP_MODEL_PREFLIGHT=1 to skip this check. ${errorMessage(error)}`,\n );\n }\n\n if (!response.ok) {\n throw new Error(\n `Could not verify model ${JSON.stringify(invocation.model)} because ${modelsUrl} returned ${response.status}: ${await shortResponseText(response)}`,\n );\n }\n\n const models = modelIds(await response.json().catch(() => undefined));\n if (models.length > 0 && !models.includes(invocation.model)) {\n throw new Error(\n `The logged-in Copilot account does not advertise model ${JSON.stringify(invocation.model)} at ${modelsUrl}. Available models: ${models.join(\", \")}. After upgrading Hoopilot, rerun \"hoopilot login\" to refresh the Copilot OAuth token, or set CODEXX_MODEL to one of the advertised model IDs.`,\n );\n }\n}\n\nfunction helpText(): string {\n return `codexx\n\nRun Codex against an already-running local Hoopilot server.\n\nUsage:\n codexx [codex options] [prompt]\n\nEnvironment:\n CODEXX_BASE_URL OpenAI-compatible base URL. Default: ${DEFAULT_BASE_URL}\n CODEXX_API_KEY API key sent to the local Hoopilot server.\n HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.\n OPENAI_API_KEY Used as the API key when both CODEXX_API_KEY and HOOPILOT_API_KEY are unset.\n CODEXX_CODEX_BIN Codex executable to run. Default: ${DEFAULT_CODEX_BIN}\n CODEXX_MODEL Codex model to use. Default: ${DEFAULT_MODEL}\n CODEXX_MODEL_REASONING_EFFORT\n Codex reasoning effort. Default: ${DEFAULT_REASONING_EFFORT}\n CODEXX_SKIP_MODEL_PREFLIGHT\n Set to 1 to skip checking /v1/models before starting Codex.\n\ncodexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, uses ${DEFAULT_MODEL} with ${DEFAULT_REASONING_EFFORT} reasoning by default, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;\n}\n\nfunction signalNumber(signal: NodeJS.Signals): number {\n return osConstants.signals[signal] ?? 1;\n}\n\nfunction modelIds(value: unknown): string[] {\n const record = value && typeof value === \"object\" && !Array.isArray(value) ? value : {};\n const data = \"data\" in record && Array.isArray(record.data) ? record.data : [];\n return data\n .map((entry) =>\n entry && typeof entry === \"object\" && \"id\" in entry && typeof entry.id === \"string\"\n ? entry.id\n : undefined,\n )\n .filter((id): id is string => typeof id === \"string\" && id.length > 0);\n}\n\nasync function shortResponseText(response: Response): Promise<string> {\n const text = await response.text();\n return text.slice(0, 500);\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nif (import.meta.main) {\n main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -174,6 +174,16 @@ var CopilotClient = class {
174
174
  signal
175
175
  });
176
176
  }
177
+ async responses(body, signal) {
178
+ return this.fetchCopilot("/responses", {
179
+ body,
180
+ headers: {
181
+ "content-type": "application/json"
182
+ },
183
+ method: "POST",
184
+ signal
185
+ });
186
+ }
177
187
  async models(signal) {
178
188
  return this.fetchCopilot("/models", {
179
189
  headers: {
@@ -203,7 +213,7 @@ var CopilotClient = class {
203
213
 
204
214
  // src/github-device.ts
205
215
  var import_promises = require("timers/promises");
206
- var DEFAULT_GITHUB_COPILOT_CLIENT_ID = "Iv23lijnNxm2e9UX3CF8";
216
+ var DEFAULT_GITHUB_COPILOT_CLIENT_ID = "Ov23li8tweQw6odWQebz";
207
217
  var DEFAULT_GITHUB_DOMAIN = "github.com";
208
218
  var DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
209
219
  var POLLING_SAFETY_MARGIN_MS = 3e3;
@@ -423,10 +433,6 @@ function isLogLevel(value) {
423
433
 
424
434
  // src/openai.ts
425
435
  var DEFAULT_MODEL = "gpt-4.1";
426
- var MODEL_ALIASES = {
427
- "gpt-5.5": DEFAULT_MODEL,
428
- "gpt-5.5-codex": DEFAULT_MODEL
429
- };
430
436
  function responsesRequestToChatCompletion(request) {
431
437
  const messages = [];
432
438
  const instructions = contentToText(request.instructions);
@@ -471,10 +477,7 @@ function completionsRequestToChatCompletion(request) {
471
477
  }
472
478
  function normalizeRequestedModel(model) {
473
479
  const requested = contentToText(model).trim();
474
- if (!requested) {
475
- return DEFAULT_MODEL;
476
- }
477
- return MODEL_ALIASES[requested.toLowerCase()] ?? requested;
480
+ return requested || DEFAULT_MODEL;
478
481
  }
479
482
  function chatCompletionToResponse(completion, responseId) {
480
483
  const id = responseId ?? `resp_${randomId()}`;
@@ -1125,8 +1128,7 @@ async function handleModels(client, signal, logger) {
1125
1128
  }
1126
1129
  async function handleChatCompletions(client, request, logger) {
1127
1130
  const chatRequest = normalizeChatCompletionRequest(await readJson(request));
1128
- const result = await sendChatCompletions(client, chatRequest, request.signal, logger);
1129
- const upstream = result.upstream;
1131
+ const upstream = await client.chatCompletions(chatRequest, request.signal);
1130
1132
  if (!upstream.ok) {
1131
1133
  return proxyError(upstream, logger);
1132
1134
  }
@@ -1135,13 +1137,10 @@ async function handleChatCompletions(client, request, logger) {
1135
1137
  }
1136
1138
  async function handleCompletions(client, request, logger) {
1137
1139
  const body = await readJson(request);
1138
- const result = await sendChatCompletions(
1139
- client,
1140
+ const upstream = await client.chatCompletions(
1140
1141
  completionsRequestToChatCompletion(body),
1141
- request.signal,
1142
- logger
1142
+ request.signal
1143
1143
  );
1144
- const upstream = result.upstream;
1145
1144
  if (!upstream.ok) {
1146
1145
  return proxyError(upstream, logger);
1147
1146
  }
@@ -1149,76 +1148,13 @@ async function handleCompletions(client, request, logger) {
1149
1148
  return jsonResponse(chatCompletionToCompletion(await upstream.json()));
1150
1149
  }
1151
1150
  async function handleResponses(client, request, logger) {
1152
- const body = await readJson(request);
1153
- const chatRequest = responsesRequestToChatCompletion(body);
1154
- const result = await sendChatCompletions(client, chatRequest, request.signal, logger);
1155
- const upstream = result.upstream;
1151
+ const body = await readJsonText(request);
1152
+ const upstream = await client.responses(body, request.signal);
1156
1153
  if (!upstream.ok) {
1157
1154
  return proxyError(upstream, logger);
1158
1155
  }
1159
- logUpstreamSuccess(logger, "/chat/completions", upstream.status);
1160
- if (body.stream === true && upstream.body) {
1161
- return new Response(
1162
- responsesStreamFromChatStream(upstream.body, {
1163
- model: result.model
1164
- }),
1165
- {
1166
- headers: {
1167
- ...corsHeaders(),
1168
- "cache-control": "no-cache",
1169
- connection: "keep-alive",
1170
- "content-type": "text/event-stream; charset=utf-8"
1171
- }
1172
- }
1173
- );
1174
- }
1175
- return jsonResponse(chatCompletionToResponse(await upstream.json()));
1176
- }
1177
- async function sendChatCompletions(client, chatRequest, signal, logger) {
1178
- const model = requestModel(chatRequest);
1179
- const upstream = await client.chatCompletions(chatRequest, signal);
1180
- if (upstream.ok || isUpstreamAuthStatus(upstream.status)) {
1181
- return { model, upstream };
1182
- }
1183
- const text = await upstream.text();
1184
- if (!shouldRetryWithDefaultModel(upstream.status, text, model)) {
1185
- return { model, upstream: textResponse(upstream, text) };
1186
- }
1187
- logger.warn(
1188
- {
1189
- event: "copilot.model.fallback",
1190
- fallbackModel: DEFAULT_MODEL,
1191
- upstreamPath: "/chat/completions",
1192
- upstreamStatus: upstream.status
1193
- },
1194
- "retrying chat completion with fallback model"
1195
- );
1196
- return {
1197
- model: DEFAULT_MODEL,
1198
- upstream: await client.chatCompletions({ ...chatRequest, model: DEFAULT_MODEL }, signal)
1199
- };
1200
- }
1201
- function shouldRetryWithDefaultModel(status, text, model) {
1202
- if (model === DEFAULT_MODEL || status < 400 || status >= 500) {
1203
- return false;
1204
- }
1205
- const normalized = text.toLowerCase();
1206
- return normalized.includes("model") && (normalized.includes("not supported") || normalized.includes("unsupported") || normalized.includes("invalid model"));
1207
- }
1208
- function requestModel(request) {
1209
- return typeof request.model === "string" && request.model.trim() ? request.model : DEFAULT_MODEL;
1210
- }
1211
- function textResponse(upstream, text) {
1212
- const headers = new Headers();
1213
- const contentType = upstream.headers.get("content-type");
1214
- if (contentType) {
1215
- headers.set("content-type", contentType);
1216
- }
1217
- return new Response(text, {
1218
- headers,
1219
- status: upstream.status,
1220
- statusText: upstream.statusText
1221
- });
1156
+ logUpstreamSuccess(logger, "/responses", upstream.status);
1157
+ return proxyResponse(upstream);
1222
1158
  }
1223
1159
  async function proxyError(upstream, logger) {
1224
1160
  const text = await upstream.text();
@@ -1257,6 +1193,15 @@ async function readJson(request) {
1257
1193
  throw new Error(INVALID_JSON_MESSAGE);
1258
1194
  }
1259
1195
  }
1196
+ async function readJsonText(request) {
1197
+ const text = await request.text();
1198
+ try {
1199
+ JSON.parse(text);
1200
+ return text;
1201
+ } catch {
1202
+ throw new Error(INVALID_JSON_MESSAGE);
1203
+ }
1204
+ }
1260
1205
  function jsonResponse(body, status = 200) {
1261
1206
  return new Response(JSON.stringify(body), {
1262
1207
  headers: {