import { access, readFile } from "node:fs/promises";import { createRequire } from "node:module";import { homedir } from "node:os";import { isAbsolute, resolve } from "node:path";import { fileURLToPath, pathToFileURL } from "node:url";import { createFxAgent as createWasmAgent, createFxTerminal as createWasmTerminal, encodeXtermKeyEvent, fxSdkApiVersion, supportsJspi, xtermAdapter,} from "./fx-sdk.js"; export { encodeXtermKeyEvent, fxSdkApiVersion, supportsJspi, xtermAdapter };export const libfxApiVersion = 2; const fetchOperationStale = 0;const fetchOperationApplied = 1;const fetchOperationBackpressure = 2; const require = createRequire(import.meta.url);const defaultCoreWasm = new URL("./fx-core.wasm", import.meta.url);const defaultTermWasm = new URL("./fx-term.wasm", import.meta.url);const defaultNativeCandidates = [ "./libfx.node", `./libfx.${process.platform}-${process.arch}.node`,];let nativeBackendPromise; function jspiFallbackError(surface, nativeError) { const nativeDetail = nativeError ? ` Native loading failed: ${nativeError.message}.` : " No compatible native addon was found."; const error = new Error( `libfx could not start the ${surface} backend.${nativeDetail} ` + "The WebAssembly fallback requires JavaScript Promise Integration (JSPI). " + "Run Node with --experimental-wasm-jspi or install a libfx package containing a compatible native addon.", ); error.code = "LIBFX_JSPI_REQUIRED"; error.cause = nativeError; return error;} async function loadNativeCandidate(candidate) { if (candidate == null) return null; if (candidate instanceof URL) { if (candidate.protocol === "file:" && candidate.pathname.endsWith(".node")) { return require(fileURLToPath(candidate)); } const imported = await import(candidate.href); return imported.default ?? imported; } if (typeof candidate === "object") return candidate.default ?? candidate; if (typeof candidate !== "string") { throw new TypeError("nativeAddon must be a module, path, URL, false, or undefined"); } if (candidate.endsWith(".node")) return require(isAbsolute(candidate) ? candidate : resolve(candidate)); const imported = await import(candidate.startsWith("file:") ? candidate : pathToFileURL(candidate).href); return imported.default ?? imported;} function validateNativeBackend(backend) { if (!backend) return null; const hasLowLevelCore = typeof backend.createCore === "function"; if ((hasLowLevelCore && backend.libfxApiVersion !== libfxApiVersion) || (!hasLowLevelCore && backend.libfxApiVersion !== undefined && backend.libfxApiVersion !== libfxApiVersion)) { const actualVersion = backend.libfxApiVersion ?? "missing"; throw new Error(`native addon API version ${actualVersion} is incompatible with libfx API version ${libfxApiVersion}`); } if (typeof backend.createFxAgent !== "function" && typeof backend.createCore !== "function" && typeof backend.createFxTerminal !== "function") { throw new Error("native addon must export createFxAgent(), createCore(), or createFxTerminal()"); } return backend;} async function discoverNativeBackend() { for (const relativePath of defaultNativeCandidates) { const url = new URL(relativePath, import.meta.url); try { await access(fileURLToPath(url)); } catch (error) { if (error?.code === "ENOENT") continue; return { backend: null, error }; } try { return { backend: validateNativeBackend(await loadNativeCandidate(url)), error: null }; } catch (error) { return { backend: null, error }; } } return { backend: null, error: null };} async function resolveNativeBackend(nativeAddon) { if (nativeAddon === false) return { backend: null, error: null }; if (nativeAddon !== undefined) { try { return { backend: validateNativeBackend(await loadNativeCandidate(nativeAddon)), error: null }; } catch (error) { return { backend: null, error }; } } nativeBackendPromise ??= discoverNativeBackend(); return nativeBackendPromise;} async function wasmBytes(input) { if (input instanceof URL && input.protocol === "file:") return readFile(input); if (typeof input === "string" && !URL.canParse(input)) return readFile(input); return input;} function validateGatewayChatUrl(value) { if (value === undefined) return; if (typeof value !== "string") throw new TypeError("FX_GATEWAY_CHAT_URL must be a string"); let url; try { url = new URL(value); } catch { throw new TypeError("FX_GATEWAY_CHAT_URL must be a valid URL"); } if (url.username || url.password || url.hash) { throw new TypeError("FX_GATEWAY_CHAT_URL must not contain credentials or a fragment"); } if (url.href === "https://ai-gateway.vercel.sh/v3/ai/language-model") return; const loopback = url.hostname === "127.0.0.1" || url.hostname === "[::1]" || url.hostname === "localhost"; if (url.protocol !== "http:" || !loopback || !url.port) { throw new TypeError("FX_GATEWAY_CHAT_URL must use the canonical Gateway or explicit loopback HTTP"); }} function createNativeCoreRuntime(addon, options) { const apiKey = options.env?.AI_GATEWAY_API_KEY; const model = options.env?.FX_MODEL; const gatewayChatUrl = options.env?.FX_GATEWAY_CHAT_URL; validateGatewayChatUrl(gatewayChatUrl); const core = addon.createCore({ apiKey, home: options.home ?? homedir(), workspaceRoot: options.workspaceRoot ?? process.cwd(), ...(model === undefined ? {} : { model }), ...(gatewayChatUrl === undefined ? {} : { gatewayChatUrl }), }); let exitedResolve; let lineHandler = null; let lineBuffer = ""; let settled = false; let fetchState = null; const exited = new Promise((resolve) => { exitedResolve = resolve; }); const abortHostEffects = () => { fetchState?.controller.abort(); try { addon.abortCoreFetch(core); } catch {} }; const finish = (code) => { if (settled) return; settled = true; clearInterval(timer); abortHostEffects(); try { addon.destroyCore(core); } catch {} exitedResolve(code); }; const pumpFetch = async (request) => { const controller = new AbortController(); const state = { handle: request.handle, controller }; fetchState = state; try { const response = await (options.fetch ?? globalThis.fetch)(request.url, { method: request.method, headers: new Headers(JSON.parse(request.headers).map(({ name, value }) => [name, value])), body: request.body?.length ? Buffer.from(request.body, "base64") : undefined, signal: controller.signal, }); const started = addon.startCoreFetchResponse(core, state.handle, response.status); if (started === fetchOperationStale) return; if (started !== fetchOperationApplied) throw new Error(`invalid native fetch start result ${started}`); if (response.body) { for await (const chunk of response.body) { const buffer = Buffer.from(chunk); let offset = 0; while (offset < buffer.length) { const end = Math.min(offset + 64 * 1024, buffer.length); const pushed = addon.pushCoreFetchResponse(core, state.handle, buffer.subarray(offset, end)); if (pushed === fetchOperationApplied) { offset = end; continue; } if (pushed === fetchOperationStale) return; if (pushed !== fetchOperationBackpressure) throw new Error(`invalid native fetch push result ${pushed}`); await new Promise((resolve) => setTimeout(resolve, 2)); } } } const finished = addon.finishCoreFetch(core, state.handle); if (finished !== fetchOperationApplied && finished !== fetchOperationStale) { throw new Error(`invalid native fetch finish result ${finished}`); } } catch (error) { if (error?.name !== "AbortError" || !controller.signal.aborted) { try { if (addon.coreFetchActive(core, state.handle)) addon.failCoreFetch(core, state.handle); } catch {} } } finally { if (fetchState === state) fetchState = null; } }; const timer = setInterval(() => { try { if (fetchState) { if (!fetchState.controller.signal.aborted && !addon.coreFetchActive(core, fetchState.handle)) { fetchState.controller.abort(); } } else { const fetchRequest = addon.takeCoreFetch(core); if (fetchRequest) void pumpFetch(JSON.parse(fetchRequest.toString("utf8"))); } const chunk = addon.drainCore(core); if (chunk.length && lineHandler) { lineBuffer += chunk.toString("utf8"); for (;;) { const newline = lineBuffer.indexOf("\n"); if (newline < 0) break; const line = lineBuffer.slice(0, newline); lineBuffer = lineBuffer.slice(newline + 1); if (line) lineHandler(JSON.parse(line)); } } if (addon.coreExited(core)) finish(addon.coreExitCode(core)); } catch { finish(1); } }, 2); return { exited, write(data) { addon.writeCore(core, Buffer.from(data)); }, closeStdin() { addon.closeCore(core); }, abortHostEffects, abort() { abortHostEffects(); addon.closeCore(core); }, setLineHandler(handler) { lineHandler = handler; }, };} function createNativeAgent(addon, options) { return createWasmAgent({ ...options, runtimeFactory(runtimeOptions) { return createNativeCoreRuntime(addon, runtimeOptions); }, });} async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWasm, options) { const { nativeAddon, backend = "auto", ...runtimeOptions } = options ?? {}; validateGatewayChatUrl(runtimeOptions.env?.FX_GATEWAY_CHAT_URL); if (!new Set(["auto", "native", "wasm"]).has(backend)) { throw new TypeError('backend must be "auto", "native", or "wasm"'); } let nativeError; if (backend !== "wasm") { const native = await resolveNativeBackend(nativeAddon); nativeError = native.error; if (typeof native.backend?.[nativeMethod] === "function" || (surface === "agent" && typeof native.backend?.createCore === "function")) { try { if (typeof native.backend?.[nativeMethod] === "function") { return await native.backend[nativeMethod](runtimeOptions); } return await createNativeAgent(native.backend, runtimeOptions); } catch (error) { nativeError = error; if (backend === "native") throw error; } } if (backend === "native") { const error = nativeError ?? new Error(`native addon does not provide ${nativeMethod}()`); error.code ??= "LIBFX_NATIVE_UNAVAILABLE"; throw error; } } if (!supportsJspi()) throw jspiFallbackError(surface, nativeError); return wasmFactory({ ...runtimeOptions, wasm: await wasmBytes(runtimeOptions.wasm ?? defaultWasm), });} export function createFxAgent(options = {}) { return createWithFallback("agent", "createFxAgent", createWasmAgent, defaultCoreWasm, options);} export function createFxTerminal(options = {}) { return createWithFallback("terminal", "createFxTerminal", createWasmTerminal, defaultTermWasm, options);}
import { access, readFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { homedir } from "node:os";
import { isAbsolute, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import {
createFxAgent as createWasmAgent,
createFxTerminal as createWasmTerminal,
encodeXtermKeyEvent,
fxSdkApiVersion,
supportsJspi,
xtermAdapter,
} from "./fx-sdk.js";
export { encodeXtermKeyEvent, fxSdkApiVersion, supportsJspi, xtermAdapter };
export const libfxApiVersion = 2;
const fetchOperationStale = 0;
const fetchOperationApplied = 1;
const fetchOperationBackpressure = 2;
const require = createRequire(import.meta.url);
const defaultCoreWasm = new URL("./fx-core.wasm", import.meta.url);
const defaultTermWasm = new URL("./fx-term.wasm", import.meta.url);
const defaultNativeCandidates = [
"./libfx.node",
`./libfx.${process.platform}-${process.arch}.node`,
];
let nativeBackendPromise;
function jspiFallbackError(surface, nativeError) {
const nativeDetail = nativeError ? ` Native loading failed: ${nativeError.message}.` : " No compatible native addon was found.";
const error = new Error(
`libfx could not start the ${surface} backend.${nativeDetail} ` +
"The WebAssembly fallback requires JavaScript Promise Integration (JSPI). " +
"Run Node with --experimental-wasm-jspi or install a libfx package containing a compatible native addon.",
);
error.code = "LIBFX_JSPI_REQUIRED";
error.cause = nativeError;
return error;
}
async function loadNativeCandidate(candidate) {
if (candidate == null) return null;
if (candidate instanceof URL) {
if (candidate.protocol === "file:" && candidate.pathname.endsWith(".node")) {
return require(fileURLToPath(candidate));
}
const imported = await import(candidate.href);
return imported.default ?? imported;
}
if (typeof candidate === "object") return candidate.default ?? candidate;
if (typeof candidate !== "string") {
throw new TypeError("nativeAddon must be a module, path, URL, false, or undefined");
}
if (candidate.endsWith(".node")) return require(isAbsolute(candidate) ? candidate : resolve(candidate));
const imported = await import(candidate.startsWith("file:") ? candidate : pathToFileURL(candidate).href);
return imported.default ?? imported;
}
function validateNativeBackend(backend) {
if (!backend) return null;
const hasLowLevelCore = typeof backend.createCore === "function";
if ((hasLowLevelCore && backend.libfxApiVersion !== libfxApiVersion) ||
(!hasLowLevelCore && backend.libfxApiVersion !== undefined && backend.libfxApiVersion !== libfxApiVersion)) {
const actualVersion = backend.libfxApiVersion ?? "missing";
throw new Error(`native addon API version ${actualVersion} is incompatible with libfx API version ${libfxApiVersion}`);
}
if (typeof backend.createFxAgent !== "function" && typeof backend.createCore !== "function" &&
typeof backend.createFxTerminal !== "function") {
throw new Error("native addon must export createFxAgent(), createCore(), or createFxTerminal()");
}
return backend;
}
async function discoverNativeBackend() {
for (const relativePath of defaultNativeCandidates) {
const url = new URL(relativePath, import.meta.url);
try {
await access(fileURLToPath(url));
} catch (error) {
if (error?.code === "ENOENT") continue;
return { backend: null, error };
}
try {
return { backend: validateNativeBackend(await loadNativeCandidate(url)), error: null };
} catch (error) {
return { backend: null, error };
}
}
return { backend: null, error: null };
}
async function resolveNativeBackend(nativeAddon) {
if (nativeAddon === false) return { backend: null, error: null };
if (nativeAddon !== undefined) {
try {
return { backend: validateNativeBackend(await loadNativeCandidate(nativeAddon)), error: null };
} catch (error) {
return { backend: null, error };
}
}
nativeBackendPromise ??= discoverNativeBackend();
return nativeBackendPromise;
}
async function wasmBytes(input) {
if (input instanceof URL && input.protocol === "file:") return readFile(input);
if (typeof input === "string" && !URL.canParse(input)) return readFile(input);
return input;
}
function validateGatewayChatUrl(value) {
if (value === undefined) return;
if (typeof value !== "string") throw new TypeError("FX_GATEWAY_CHAT_URL must be a string");
let url;
try { url = new URL(value); } catch { throw new TypeError("FX_GATEWAY_CHAT_URL must be a valid URL"); }
if (url.username || url.password || url.hash) {
throw new TypeError("FX_GATEWAY_CHAT_URL must not contain credentials or a fragment");
}
if (url.href === "https://ai-gateway.vercel.sh/v3/ai/language-model") return;
const loopback = url.hostname === "127.0.0.1" || url.hostname === "[::1]" || url.hostname === "localhost";
if (url.protocol !== "http:" || !loopback || !url.port) {
throw new TypeError("FX_GATEWAY_CHAT_URL must use the canonical Gateway or explicit loopback HTTP");
}
}
function createNativeCoreRuntime(addon, options) {
const apiKey = options.env?.AI_GATEWAY_API_KEY;
const model = options.env?.FX_MODEL;
const gatewayChatUrl = options.env?.FX_GATEWAY_CHAT_URL;
validateGatewayChatUrl(gatewayChatUrl);
const core = addon.createCore({
apiKey,
home: options.home ?? homedir(),
workspaceRoot: options.workspaceRoot ?? process.cwd(),
...(model === undefined ? {} : { model }),
...(gatewayChatUrl === undefined ? {} : { gatewayChatUrl }),
});
let exitedResolve;
let lineHandler = null;
let lineBuffer = "";
let settled = false;
let fetchState = null;
const exited = new Promise((resolve) => { exitedResolve = resolve; });
const abortHostEffects = () => {
fetchState?.controller.abort();
try { addon.abortCoreFetch(core); } catch {}
};
const finish = (code) => {
if (settled) return;
settled = true;
clearInterval(timer);
abortHostEffects();
try { addon.destroyCore(core); } catch {}
exitedResolve(code);
};
const pumpFetch = async (request) => {
const controller = new AbortController();
const state = { handle: request.handle, controller };
fetchState = state;
try {
const response = await (options.fetch ?? globalThis.fetch)(request.url, {
method: request.method,
headers: new Headers(JSON.parse(request.headers).map(({ name, value }) => [name, value])),
body: request.body?.length ? Buffer.from(request.body, "base64") : undefined,
signal: controller.signal,
});
const started = addon.startCoreFetchResponse(core, state.handle, response.status);
if (started === fetchOperationStale) return;
if (started !== fetchOperationApplied) throw new Error(`invalid native fetch start result ${started}`);
if (response.body) {
for await (const chunk of response.body) {
const buffer = Buffer.from(chunk);
let offset = 0;
while (offset < buffer.length) {
const end = Math.min(offset + 64 * 1024, buffer.length);
const pushed = addon.pushCoreFetchResponse(core, state.handle, buffer.subarray(offset, end));
if (pushed === fetchOperationApplied) {
offset = end;
continue;
}
if (pushed === fetchOperationStale) return;
if (pushed !== fetchOperationBackpressure) throw new Error(`invalid native fetch push result ${pushed}`);
await new Promise((resolve) => setTimeout(resolve, 2));
}
}
}
const finished = addon.finishCoreFetch(core, state.handle);
if (finished !== fetchOperationApplied && finished !== fetchOperationStale) {
throw new Error(`invalid native fetch finish result ${finished}`);
}
} catch (error) {
if (error?.name !== "AbortError" || !controller.signal.aborted) {
try {
if (addon.coreFetchActive(core, state.handle)) addon.failCoreFetch(core, state.handle);
} catch {}
}
} finally {
if (fetchState === state) fetchState = null;
}
};
const timer = setInterval(() => {
try {
if (fetchState) {
if (!fetchState.controller.signal.aborted && !addon.coreFetchActive(core, fetchState.handle)) {
fetchState.controller.abort();
}
} else {
const fetchRequest = addon.takeCoreFetch(core);
if (fetchRequest) void pumpFetch(JSON.parse(fetchRequest.toString("utf8")));
}
const chunk = addon.drainCore(core);
if (chunk.length && lineHandler) {
lineBuffer += chunk.toString("utf8");
for (;;) {
const newline = lineBuffer.indexOf("\n");
if (newline < 0) break;
const line = lineBuffer.slice(0, newline);
lineBuffer = lineBuffer.slice(newline + 1);
if (line) lineHandler(JSON.parse(line));
}
}
if (addon.coreExited(core)) finish(addon.coreExitCode(core));
} catch {
finish(1);
}
}, 2);
return {
exited,
write(data) { addon.writeCore(core, Buffer.from(data)); },
closeStdin() { addon.closeCore(core); },
abortHostEffects,
abort() { abortHostEffects(); addon.closeCore(core); },
setLineHandler(handler) { lineHandler = handler; },
};
}
function createNativeAgent(addon, options) {
return createWasmAgent({
...options,
runtimeFactory(runtimeOptions) {
return createNativeCoreRuntime(addon, runtimeOptions);
},
});
}
async function createWithFallback(surface, nativeMethod, wasmFactory, defaultWasm, options) {
const { nativeAddon, backend = "auto", ...runtimeOptions } = options ?? {};
validateGatewayChatUrl(runtimeOptions.env?.FX_GATEWAY_CHAT_URL);
if (!new Set(["auto", "native", "wasm"]).has(backend)) {
throw new TypeError('backend must be "auto", "native", or "wasm"');
}
let nativeError;
if (backend !== "wasm") {
const native = await resolveNativeBackend(nativeAddon);
nativeError = native.error;
if (typeof native.backend?.[nativeMethod] === "function" ||
(surface === "agent" && typeof native.backend?.createCore === "function")) {
try {
if (typeof native.backend?.[nativeMethod] === "function") {
return await native.backend[nativeMethod](runtimeOptions);
}
return await createNativeAgent(native.backend, runtimeOptions);
} catch (error) {
nativeError = error;
if (backend === "native") throw error;
}
}
if (backend === "native") {
const error = nativeError ?? new Error(`native addon does not provide ${nativeMethod}()`);
error.code ??= "LIBFX_NATIVE_UNAVAILABLE";
throw error;
}
}
if (!supportsJspi()) throw jspiFallbackError(surface, nativeError);
return wasmFactory({
...runtimeOptions,
wasm: await wasmBytes(runtimeOptions.wasm ?? defaultWasm),
});
}
export function createFxAgent(options = {}) {
return createWithFallback("agent", "createFxAgent", createWasmAgent, defaultCoreWasm, options);
}
export function createFxTerminal(options = {}) {
return createWithFallback("terminal", "createFxTerminal", createWasmTerminal, defaultTermWasm, options);
}