mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 02:59:35 +00:00
feat: integrate Pi backend and Pi Web
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import { EventEmitter } from "node:events";
|
||||
import * as undici from "undici";
|
||||
|
||||
export const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
|
||||
|
||||
type DispatcherGlobal = typeof globalThis & {
|
||||
__piWebHttpDispatcherConfigured?: boolean;
|
||||
};
|
||||
|
||||
const dispatcherGlobal = globalThis as DispatcherGlobal;
|
||||
const originalGlobalFetch = globalThis.fetch;
|
||||
const ignoreUndiciDispatcherError = (): void => {};
|
||||
|
||||
function parseHttpIdleTimeoutMs(value: unknown): number | undefined {
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed.toLowerCase() === "disabled") return 0;
|
||||
if (trimmed.length === 0) return undefined;
|
||||
return parseHttpIdleTimeoutMs(Number(trimmed));
|
||||
}
|
||||
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
||||
return undefined;
|
||||
}
|
||||
return Math.floor(value);
|
||||
}
|
||||
|
||||
// Undici can emit an internal Client error while terminating a response body.
|
||||
// The body stream still rejects; this prevents the EventEmitter error from
|
||||
// terminating the Next.js process first.
|
||||
function withUndiciErrorListener<T extends undici.Dispatcher>(dispatcher: T): T {
|
||||
if (dispatcher instanceof EventEmitter) {
|
||||
EventEmitter.prototype.on.call(dispatcher, "error", ignoreUndiciDispatcherError);
|
||||
}
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
function createUndiciClient(origin: string | URL, options: object): undici.Dispatcher {
|
||||
return withUndiciErrorListener(
|
||||
new undici.Client(origin, options as undici.Client.Options),
|
||||
);
|
||||
}
|
||||
|
||||
function createUndiciOriginDispatcher(origin: string | URL, options: object): undici.Dispatcher {
|
||||
const dispatcherOptions = options as undici.Pool.Options;
|
||||
if (dispatcherOptions.connections === 1) {
|
||||
return createUndiciClient(origin, dispatcherOptions);
|
||||
}
|
||||
|
||||
return withUndiciErrorListener(
|
||||
new undici.Pool(origin, {
|
||||
...dispatcherOptions,
|
||||
factory: createUndiciClient,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function configureHttpDispatcher(
|
||||
timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS,
|
||||
): void {
|
||||
if (dispatcherGlobal.__piWebHttpDispatcherConfigured) return;
|
||||
|
||||
const normalizedTimeoutMs = parseHttpIdleTimeoutMs(timeoutMs);
|
||||
if (normalizedTimeoutMs === undefined) {
|
||||
throw new Error(`Invalid HTTP idle timeout: ${String(timeoutMs)}`);
|
||||
}
|
||||
|
||||
const dispatcher = withUndiciErrorListener(
|
||||
new undici.EnvHttpProxyAgent({
|
||||
allowH2: false,
|
||||
bodyTimeout: normalizedTimeoutMs,
|
||||
headersTimeout: normalizedTimeoutMs,
|
||||
clientFactory: createUndiciClient,
|
||||
factory: createUndiciOriginDispatcher,
|
||||
}),
|
||||
);
|
||||
undici.setGlobalDispatcher(dispatcher);
|
||||
|
||||
// Keep fetch and the dispatcher on the same undici implementation. Preserve
|
||||
// an intentional fetch override installed after this module was loaded.
|
||||
if (globalThis.fetch === originalGlobalFetch) {
|
||||
undici.install?.();
|
||||
}
|
||||
|
||||
dispatcherGlobal.__piWebHttpDispatcherConfigured = true;
|
||||
}
|
||||
Reference in New Issue
Block a user