@openhoo/hoopilot 0.5.3 → 0.5.5

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
@@ -124,9 +124,10 @@ $env:HOOPILOT_API_KEY = "local-key"
124
124
  npx --package @openhoo/hoopilot codexx -m gpt-5.5
125
125
  ```
126
126
 
127
- `codexx` does not start Hoopilot and does not change your shell environment. It only
128
- runs `codex` with `openai_base_url` pointed at `http://127.0.0.1:4141/v1`, maps
129
- `HOOPILOT_API_KEY` to `OPENAI_API_KEY` for that child process, passes
127
+ `codexx` does not start Hoopilot and does not change your shell environment. It runs
128
+ `codex` with a temporary `hoopilot` model provider pointed at
129
+ `http://127.0.0.1:4141/v1`, disables Codex Responses WebSockets for that provider,
130
+ maps `HOOPILOT_API_KEY` to `OPENAI_API_KEY` for that child process, passes
130
131
  `--disable network_proxy` to Codex, and removes standard proxy variables from the
131
132
  spawned Codex process so Codex talks directly to the local server. Override the local
132
133
  URL with `CODEXX_BASE_URL`, the local key with `CODEXX_API_KEY`, or the Codex
package/dist/cli.js CHANGED
@@ -909,12 +909,13 @@ function createHoopilotHandler(options = {}) {
909
909
  return async (request) => {
910
910
  const startedAt = performance.now();
911
911
  const url = new URL(request.url);
912
+ const apiPath = canonicalApiPath(url.pathname);
912
913
  const requestId = requestIdFor(request);
913
914
  const requestLogger = logger.child({
914
915
  method: request.method,
915
916
  path: url.pathname,
916
917
  requestId,
917
- route: routeFor(request.method, url.pathname)
918
+ route: routeFor(request.method, apiPath)
918
919
  });
919
920
  if (request.method === "OPTIONS") {
920
921
  return finishResponse(new Response(null, { headers: corsHeaders() }), {
@@ -935,7 +936,7 @@ function createHoopilotHandler(options = {}) {
935
936
  );
936
937
  }
937
938
  try {
938
- if (request.method === "GET" && (url.pathname === "/" || url.pathname === "/healthz")) {
939
+ if (request.method === "GET" && (apiPath === "/" || apiPath === "/healthz")) {
939
940
  return finishResponse(
940
941
  jsonResponse({
941
942
  name: "hoopilot",
@@ -945,28 +946,35 @@ function createHoopilotHandler(options = {}) {
945
946
  { logger: requestLogger, requestId, startedAt }
946
947
  );
947
948
  }
948
- if (request.method === "GET" && url.pathname === "/v1/models") {
949
+ if (request.method === "GET" && apiPath === "/v1/responses") {
950
+ return finishResponse(websocketUnsupportedResponse(), {
951
+ logger: requestLogger,
952
+ requestId,
953
+ startedAt
954
+ });
955
+ }
956
+ if (request.method === "GET" && apiPath === "/v1/models") {
949
957
  return finishResponse(await handleModels(client, request.signal, requestLogger), {
950
958
  logger: requestLogger,
951
959
  requestId,
952
960
  startedAt
953
961
  });
954
962
  }
955
- if (request.method === "POST" && url.pathname === "/v1/chat/completions") {
963
+ if (request.method === "POST" && apiPath === "/v1/chat/completions") {
956
964
  return finishResponse(await handleChatCompletions(client, request, requestLogger), {
957
965
  logger: requestLogger,
958
966
  requestId,
959
967
  startedAt
960
968
  });
961
969
  }
962
- if (request.method === "POST" && url.pathname === "/v1/completions") {
970
+ if (request.method === "POST" && apiPath === "/v1/completions") {
963
971
  return finishResponse(await handleCompletions(client, request, requestLogger), {
964
972
  logger: requestLogger,
965
973
  requestId,
966
974
  startedAt
967
975
  });
968
976
  }
969
- if (request.method === "POST" && url.pathname === "/v1/responses") {
977
+ if (request.method === "POST" && apiPath === "/v1/responses") {
970
978
  return finishResponse(await handleResponses(client, request, requestLogger), {
971
979
  logger: requestLogger,
972
980
  requestId,
@@ -1156,6 +1164,15 @@ function jsonError(status, code, message) {
1156
1164
  status
1157
1165
  );
1158
1166
  }
1167
+ function websocketUnsupportedResponse() {
1168
+ const response = jsonError(
1169
+ 426,
1170
+ "websocket_not_supported",
1171
+ "Hoopilot does not support Responses WebSocket transport; retry with HTTP Responses API."
1172
+ );
1173
+ response.headers.set("upgrade", "websocket");
1174
+ return response;
1175
+ }
1159
1176
  function corsHeaders() {
1160
1177
  return {
1161
1178
  "access-control-allow-headers": "authorization, content-type, x-api-key",
@@ -1231,6 +1248,21 @@ function requestIdFor(request) {
1231
1248
  const existing = request.headers.get("x-request-id")?.trim();
1232
1249
  return existing || crypto.randomUUID();
1233
1250
  }
1251
+ function canonicalApiPath(path) {
1252
+ const withoutTrailingSlash = path.length > 1 ? path.replace(/\/+$/, "") : path;
1253
+ switch (withoutTrailingSlash) {
1254
+ case "/models":
1255
+ return "/v1/models";
1256
+ case "/chat/completions":
1257
+ return "/v1/chat/completions";
1258
+ case "/completions":
1259
+ return "/v1/completions";
1260
+ case "/responses":
1261
+ return "/v1/responses";
1262
+ default:
1263
+ return withoutTrailingSlash;
1264
+ }
1265
+ }
1234
1266
  function routeFor(method, path) {
1235
1267
  if (method === "OPTIONS") {
1236
1268
  return "cors.preflight";
@@ -1250,6 +1282,9 @@ function routeFor(method, path) {
1250
1282
  if (method === "POST" && path === "/v1/responses") {
1251
1283
  return "responses";
1252
1284
  }
1285
+ if (method === "GET" && path === "/v1/responses") {
1286
+ return "responses_websocket";
1287
+ }
1253
1288
  return "not_found";
1254
1289
  }
1255
1290
  function isStreamingResponse(response) {