@openhoo/hoopilot 2.1.3 → 2.1.4
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 +5 -0
- package/dist/{chunk-CYR6I4C3.js → chunk-4ZG5QEYJ.js} +24 -4
- package/dist/chunk-4ZG5QEYJ.js.map +1 -0
- package/dist/cli.js +159 -3
- package/dist/cli.js.map +1 -1
- package/dist/codexx.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +158 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-CYR6I4C3.js.map +0 -1
package/dist/codexx.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2005,6 +2005,14 @@ var COPILOT_USAGE_API_VERSION = "2025-04-01";
|
|
|
2005
2005
|
var EDITOR_PLUGIN_VERSION = "hoopilot/0.1.0";
|
|
2006
2006
|
var EDITOR_VERSION = "Hoopilot/0.1.0";
|
|
2007
2007
|
var HOOPILOT_USER_AGENT = "hoopilot/0.1.0";
|
|
2008
|
+
var DEFAULT_UPSTREAM_TIMEOUT_MS = 12e4;
|
|
2009
|
+
var DEFAULT_UPSTREAM_STREAM_IDLE_TIMEOUT_MS = 12e4;
|
|
2010
|
+
var CopilotUpstreamTimeoutError = class extends Error {
|
|
2011
|
+
constructor(message) {
|
|
2012
|
+
super(message);
|
|
2013
|
+
this.name = "CopilotUpstreamTimeoutError";
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2008
2016
|
function applyCopilotHeaders(headers, token) {
|
|
2009
2017
|
headers.set("accept", headers.get("accept") ?? "application/json");
|
|
2010
2018
|
headers.set("authorization", `Bearer ${token}`);
|
|
@@ -2057,6 +2065,8 @@ var CopilotClient = class {
|
|
|
2057
2065
|
#allowUnsafeUpstream;
|
|
2058
2066
|
#fetch;
|
|
2059
2067
|
#githubApiBaseUrl;
|
|
2068
|
+
#upstreamStreamIdleTimeoutMs;
|
|
2069
|
+
#upstreamTimeoutMs;
|
|
2060
2070
|
constructor(options = {}) {
|
|
2061
2071
|
this.#auth = new CopilotAuth(options);
|
|
2062
2072
|
this.#allowUnsafeUpstream = envValue(options.env?.HOOPILOT_ALLOW_UNSAFE_UPSTREAM) === "1";
|
|
@@ -2064,6 +2074,18 @@ var CopilotClient = class {
|
|
|
2064
2074
|
this.#githubApiBaseUrl = trimTrailingSlash(
|
|
2065
2075
|
options.githubApiBaseUrl ?? envValue(options.env?.HOOPILOT_GITHUB_API_BASE_URL) ?? DEFAULT_GITHUB_API_BASE_URL
|
|
2066
2076
|
);
|
|
2077
|
+
this.#upstreamTimeoutMs = parseTimeoutMs(
|
|
2078
|
+
options.upstreamTimeoutMs,
|
|
2079
|
+
options.env?.HOOPILOT_UPSTREAM_TIMEOUT_MS,
|
|
2080
|
+
DEFAULT_UPSTREAM_TIMEOUT_MS,
|
|
2081
|
+
"HOOPILOT_UPSTREAM_TIMEOUT_MS"
|
|
2082
|
+
);
|
|
2083
|
+
this.#upstreamStreamIdleTimeoutMs = parseTimeoutMs(
|
|
2084
|
+
options.upstreamStreamIdleTimeoutMs,
|
|
2085
|
+
options.env?.HOOPILOT_UPSTREAM_STREAM_IDLE_TIMEOUT_MS,
|
|
2086
|
+
DEFAULT_UPSTREAM_STREAM_IDLE_TIMEOUT_MS,
|
|
2087
|
+
"HOOPILOT_UPSTREAM_STREAM_IDLE_TIMEOUT_MS"
|
|
2088
|
+
);
|
|
2067
2089
|
}
|
|
2068
2090
|
/**
|
|
2069
2091
|
* Fetch the Copilot account's quota / premium-request usage from the GitHub
|
|
@@ -2082,7 +2104,7 @@ var CopilotClient = class {
|
|
|
2082
2104
|
}
|
|
2083
2105
|
const access = await this.#auth.getAccess();
|
|
2084
2106
|
const headers = applyGithubApiHeaders(new Headers(), access.token);
|
|
2085
|
-
return this.#
|
|
2107
|
+
return this.#fetchWithTimeout(`${this.#githubApiBaseUrl}/copilot_internal/user`, {
|
|
2086
2108
|
headers,
|
|
2087
2109
|
method: "GET",
|
|
2088
2110
|
signal
|
|
@@ -2129,12 +2151,139 @@ var CopilotClient = class {
|
|
|
2129
2151
|
);
|
|
2130
2152
|
}
|
|
2131
2153
|
const headers = applyCopilotHeaders(new Headers(init.headers), access.token);
|
|
2132
|
-
return this.#
|
|
2154
|
+
return this.#fetchWithTimeout(`${access.apiBaseUrl}${path}`, {
|
|
2133
2155
|
...init,
|
|
2134
2156
|
headers
|
|
2135
2157
|
});
|
|
2136
2158
|
}
|
|
2159
|
+
async #fetchWithTimeout(input, init) {
|
|
2160
|
+
const timeout = abortSignalWithTimeout(init.signal ?? void 0, this.#upstreamTimeoutMs);
|
|
2161
|
+
try {
|
|
2162
|
+
const response = await this.#fetch(input, {
|
|
2163
|
+
...init,
|
|
2164
|
+
signal: timeout.signal
|
|
2165
|
+
});
|
|
2166
|
+
return responseWithStreamIdleTimeout(response, this.#upstreamStreamIdleTimeoutMs, input);
|
|
2167
|
+
} catch (error) {
|
|
2168
|
+
if (timeout.timedOut()) {
|
|
2169
|
+
throw new CopilotUpstreamTimeoutError(
|
|
2170
|
+
`Copilot upstream request timed out after ${this.#upstreamTimeoutMs} ms before response headers arrived.`
|
|
2171
|
+
);
|
|
2172
|
+
}
|
|
2173
|
+
throw error;
|
|
2174
|
+
} finally {
|
|
2175
|
+
timeout.cleanup();
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2137
2178
|
};
|
|
2179
|
+
function parseTimeoutMs(optionValue, envRaw, fallback, name) {
|
|
2180
|
+
const raw = optionValue ?? envValue(envRaw);
|
|
2181
|
+
if (raw === void 0) {
|
|
2182
|
+
return fallback;
|
|
2183
|
+
}
|
|
2184
|
+
const value = typeof raw === "number" ? raw : Number(raw);
|
|
2185
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
2186
|
+
throw new Error(`${name} must be a non-negative integer number of milliseconds.`);
|
|
2187
|
+
}
|
|
2188
|
+
return value;
|
|
2189
|
+
}
|
|
2190
|
+
function abortSignalWithTimeout(parent, timeoutMs) {
|
|
2191
|
+
if (timeoutMs === 0) {
|
|
2192
|
+
return { cleanup: () => {
|
|
2193
|
+
}, signal: parent, timedOut: () => false };
|
|
2194
|
+
}
|
|
2195
|
+
const controller = new AbortController();
|
|
2196
|
+
let timedOut = false;
|
|
2197
|
+
const timer = setTimeout(() => {
|
|
2198
|
+
if (controller.signal.aborted) {
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
timedOut = true;
|
|
2202
|
+
controller.abort(
|
|
2203
|
+
new CopilotUpstreamTimeoutError(`Copilot upstream request timed out after ${timeoutMs} ms.`)
|
|
2204
|
+
);
|
|
2205
|
+
}, timeoutMs);
|
|
2206
|
+
const onAbort = () => controller.abort(parent?.reason);
|
|
2207
|
+
if (parent?.aborted) {
|
|
2208
|
+
controller.abort(parent.reason);
|
|
2209
|
+
} else {
|
|
2210
|
+
parent?.addEventListener("abort", onAbort, { once: true });
|
|
2211
|
+
}
|
|
2212
|
+
return {
|
|
2213
|
+
cleanup: () => {
|
|
2214
|
+
clearTimeout(timer);
|
|
2215
|
+
parent?.removeEventListener("abort", onAbort);
|
|
2216
|
+
},
|
|
2217
|
+
signal: controller.signal,
|
|
2218
|
+
timedOut: () => timedOut
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
function responseWithStreamIdleTimeout(response, idleTimeoutMs, input) {
|
|
2222
|
+
if (!response.body || idleTimeoutMs === 0) {
|
|
2223
|
+
return response;
|
|
2224
|
+
}
|
|
2225
|
+
return new Response(streamWithIdleTimeout(response.body, idleTimeoutMs, input), {
|
|
2226
|
+
headers: response.headers,
|
|
2227
|
+
status: response.status,
|
|
2228
|
+
statusText: response.statusText
|
|
2229
|
+
});
|
|
2230
|
+
}
|
|
2231
|
+
function streamWithIdleTimeout(body, idleTimeoutMs, input) {
|
|
2232
|
+
const reader = body.getReader();
|
|
2233
|
+
let released = false;
|
|
2234
|
+
const release = () => {
|
|
2235
|
+
if (!released) {
|
|
2236
|
+
released = true;
|
|
2237
|
+
reader.releaseLock();
|
|
2238
|
+
}
|
|
2239
|
+
};
|
|
2240
|
+
return new ReadableStream({
|
|
2241
|
+
async pull(controller) {
|
|
2242
|
+
let timer;
|
|
2243
|
+
const read = reader.read();
|
|
2244
|
+
read.catch(() => {
|
|
2245
|
+
});
|
|
2246
|
+
try {
|
|
2247
|
+
const result = await Promise.race([
|
|
2248
|
+
read,
|
|
2249
|
+
new Promise((_, reject) => {
|
|
2250
|
+
timer = setTimeout(() => {
|
|
2251
|
+
reject(
|
|
2252
|
+
new CopilotUpstreamTimeoutError(
|
|
2253
|
+
`Copilot upstream stream was idle for ${idleTimeoutMs} ms while reading ${input}.`
|
|
2254
|
+
)
|
|
2255
|
+
);
|
|
2256
|
+
}, idleTimeoutMs);
|
|
2257
|
+
})
|
|
2258
|
+
]);
|
|
2259
|
+
if (timer) {
|
|
2260
|
+
clearTimeout(timer);
|
|
2261
|
+
}
|
|
2262
|
+
if (result.done) {
|
|
2263
|
+
controller.close();
|
|
2264
|
+
release();
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
controller.enqueue(result.value);
|
|
2268
|
+
} catch (error) {
|
|
2269
|
+
if (timer) {
|
|
2270
|
+
clearTimeout(timer);
|
|
2271
|
+
}
|
|
2272
|
+
await reader.cancel(error).catch(() => {
|
|
2273
|
+
});
|
|
2274
|
+
controller.error(error);
|
|
2275
|
+
release();
|
|
2276
|
+
}
|
|
2277
|
+
},
|
|
2278
|
+
async cancel(reason) {
|
|
2279
|
+
try {
|
|
2280
|
+
await reader.cancel(reason);
|
|
2281
|
+
} finally {
|
|
2282
|
+
release();
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2138
2287
|
function normalizeCopilotUsage(body) {
|
|
2139
2288
|
const record = asRecord(body);
|
|
2140
2289
|
const quotas = {};
|
|
@@ -4089,6 +4238,13 @@ function buildApp(deps) {
|
|
|
4089
4238
|
);
|
|
4090
4239
|
return jsonError(413, "request_too_large", message);
|
|
4091
4240
|
}
|
|
4241
|
+
if (error instanceof CopilotUpstreamTimeoutError) {
|
|
4242
|
+
logger.warn(
|
|
4243
|
+
{ err: errorDetails(error), event: "copilot.request.timeout" },
|
|
4244
|
+
"copilot upstream request timed out"
|
|
4245
|
+
);
|
|
4246
|
+
return jsonError(504, "copilot_timeout", message);
|
|
4247
|
+
}
|
|
4092
4248
|
logger.error({ err: errorDetails(error), event: "http.request.failed" }, "request failed");
|
|
4093
4249
|
return jsonError(500, "internal_error", message);
|
|
4094
4250
|
}).get("/", () => jsonResponse({ name: "hoopilot", object: "health", status: "ok" })).get("/healthz", () => jsonResponse({ name: "hoopilot", object: "health", status: "ok" })).get("/metrics", () => metricsResponse(metrics)).get("/v1/usage", ({ request }) => handleUsage(metrics, readUsage, request.signal)).get(
|