740 lines · 25.3 KB · TypeScript
RawDownload
/* oxlint-disable executor/no-try-catch-or-throw, executor/no-promise-reject, executor/no-instanceof-tagged-error -- subprocess boundary normalizes JavaScript errors into typed runtime errors */import { spawn, spawnSync, type ChildProcessByStdio } from "node:child_process";import { existsSync, readFileSync } from "node:fs";import { access, mkdir, rm, writeFile } from "node:fs/promises";import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";import { createRequire } from "node:module";import { createServer as createNetServer, type Server as NetServer } from "node:net";import type { Readable } from "node:stream";import { tmpdir } from "node:os";import { dirname, join } from "node:path";import { rootCertificates } from "node:tls";import { fileURLToPath } from "node:url"; import {  recoverExecutionBody,  stripTypeScript,  type CodeExecutor,  type ExecuteResult,  type SandboxToolInvoker,} from "@executor-js/codemode-core";import * as Cause from "effect/Cause";import * as Data from "effect/Data";import * as Effect from "effect/Effect"; export const WORKERD_VERSION = "1.20260708.1"; const DEFAULT_TIMEOUT_MS = 5 * 60_000;const DEFAULT_HOST_TIMEOUT_GRACE_MS = 30_000;export class WorkerdSubprocessError extends Data.TaggedError("WorkerdSubprocessError")<{  readonly message: string;  readonly cause?: unknown;}> {} export type WorkerdToolBridge = {  readonly call: (toolPath: string, args: unknown) => Promise<unknown>;}; export type WorkerdModuleSource =  | string  | {      readonly kind: "esModule";      readonly source: string;    }  | {      readonly kind: "wasm";      readonly bytes: Uint8Array;    }; export type WorkerdModuleRunnerOptions = {  readonly modules: Readonly<Record<string, WorkerdModuleSource>>;  readonly mainModule: string;  readonly compatibilityDate?: string;  readonly compatibilityFlags?: readonly string[];  readonly toolBridge?: WorkerdToolBridge;  readonly hostToken?: string;  readonly unsafeEval?: boolean;  readonly globalOutbound?: "blocked" | "internet";  readonly workerdBin?: string;  readonly startupTimeoutMs?: number;  readonly restartBackoffMs?: number;}; export type WorkerdRunResult<T = unknown> = {  readonly status: number;  readonly body: T;  readonly elapsedMs: number;}; export type WorkerdModuleRunner = {  readonly pid: () => number | undefined;  readonly ensureStarted: () => Promise<void>;  readonly run: <T = unknown>(payload: unknown, timeoutMs?: number) => Promise<WorkerdRunResult<T>>;  readonly dispose: () => Promise<void>;  readonly crashForTest: () => void;  readonly tempDirForTest: () => string | undefined;}; type PendingRequest = {  readonly reject: (error: Error) => void;}; const packageDir = dirname(dirname(fileURLToPath(import.meta.url)));const packageRequire = createRequire(import.meta.url); const normalizeError = (cause: unknown): Error =>  cause instanceof Error ? cause : new Error(String(cause)); const renderCause = (cause: Cause.Cause<unknown>): string => {  const squashed = Cause.squash(cause);  if (squashed instanceof Error) return squashed.message;  return String(squashed);}; const readRequestBody = (request: IncomingMessage): Promise<unknown> =>  new Promise((resolve, reject) => {    let body = "";    request.setEncoding("utf8");    request.on("data", (chunk) => {      body += chunk;    });    request.on("error", reject);    request.on("end", () => {      try {        resolve(body.length > 0 ? JSON.parse(body) : {});      } catch (cause) {        reject(cause);      }    });  }); const writeJson = (response: ServerResponse, status: number, body: unknown): void => {  response.writeHead(status, { "content-type": "application/json" });  response.end(JSON.stringify(body));}; const closeServer = (server: Server): Promise<void> =>  new Promise((resolve) => {    server.close(() => resolve());  }); const listenLocal = (server: Server): Promise<number> =>  new Promise((resolve, reject) => {    server.on("error", reject);    server.listen(0, "127.0.0.1", () => {      const address = server.address();      if (typeof address === "object" && address !== null) {        resolve(address.port);        return;      }      reject(new Error("server did not expose a TCP port"));    });  }); const reserveLocalPort = (): Promise<{  readonly port: number;  readonly close: () => Promise<void>;}> =>  new Promise((resolve, reject) => {    const server: NetServer = createNetServer();    server.on("error", reject);    server.listen(0, "127.0.0.1", () => {      const address = server.address();      if (typeof address === "object" && address !== null) {        resolve({          port: address.port,          close: () =>            new Promise((closeResolve) => {              server.close(() => closeResolve());            }),        });        return;      }      reject(new Error("reservation did not expose a TCP port"));    });  }); const workerdBinaryName = (platform: NodeJS.Platform = process.platform): string =>  platform === "win32" ? "workerd.exe" : "workerd"; const detectLinuxMusl = (): boolean => {  if (process.platform !== "linux") return false;  if (existsSync("/etc/alpine-release")) return true;  try {    return readFileSync("/usr/bin/ldd", "utf8").toLowerCase().includes("musl");  } catch {    return false;  }}; const workerdPlatformPackage = (  platform: NodeJS.Platform = process.platform,  arch: NodeJS.Architecture = process.arch,): string | null => {  if (platform === "darwin" && arch === "arm64") return "@cloudflare/workerd-darwin-arm64";  if (platform === "darwin" && arch === "x64") return "@cloudflare/workerd-darwin-64";  if (platform === "linux" && detectLinuxMusl()) return null;  if (platform === "linux" && arch === "arm64") return "@cloudflare/workerd-linux-arm64";  if (platform === "linux" && arch === "x64") return "@cloudflare/workerd-linux-64";  if (platform === "win32" && arch === "x64") return "@cloudflare/workerd-windows-64";  return null;}; const workerdPackageDirCandidates = (packageName: string): readonly string[] => {  const packageJson = `${packageName}/package.json`;  const resolved = new Set<string>();  for (const req of [    packageRequire,    createRequire(join(process.cwd(), "package.json")),    createRequire(join(packageDir, "package.json")),  ]) {    try {      resolved.add(dirname(req.resolve(packageJson)));    } catch {      // Try the next package-manager layout.    }  }  return [...resolved];}; export const workerdBinaryCandidatesForTest = (  roots: readonly string[],  platform: NodeJS.Platform,  arch: NodeJS.Architecture,): readonly string[] => {  const binary = workerdBinaryName(platform);  const platformPackage = workerdPlatformPackage(platform, arch);  return [    ...roots.map((root) => join(root, "node_modules", "workerd", "bin", binary)),    ...(platformPackage      ? roots.flatMap((root) => [          join(root, "node_modules", platformPackage, "bin", binary),          join(root, "node_modules", platformPackage, binary),        ])      : []),  ];}; const workerdBinaryCandidates = (): readonly string[] => {  const roots = [packageDir, join(packageDir, "..", "..", ".."), process.cwd()];  const platformPackage = workerdPlatformPackage();  return [    ...(platformPackage      ? workerdPackageDirCandidates(platformPackage).flatMap((dir) => [          join(dir, "bin", workerdBinaryName()),          join(dir, workerdBinaryName()),        ])      : []),    ...workerdBinaryCandidatesForTest(roots, process.platform, process.arch),  ];}; const findWorkerdBin = async (configured?: string): Promise<string> => {  if (configured) return configured;  if (process.env.EXECUTOR_WORKERD_BIN) return process.env.EXECUTOR_WORKERD_BIN;   for (const candidate of workerdBinaryCandidates()) {    try {      await access(candidate);      return candidate;    } catch {      // Try the next package-manager layout.    }  }   if (workerdPlatformPackage() === null) throw new Error("workerd is unavailable on this platform");  const pathWorkerd = spawnSync("workerd", ["--version"], {    stdio: "ignore",    timeout: 5000,  });  if (pathWorkerd.error !== undefined || pathWorkerd.status !== 0) {    throw new Error("workerd is unavailable on this platform");  }  return "workerd";}; export const isWorkerdAvailable = (workerdBin?: string): boolean => {  const configured = workerdBin ?? process.env.EXECUTOR_WORKERD_BIN;  if (configured) {    const result = spawnSync(configured, ["--version"], {      stdio: "ignore",      timeout: 5000,    });    return result.error === undefined && result.status === 0;  }   for (const candidate of workerdBinaryCandidates()) {    const result = spawnSync(candidate, ["--version"], {      stdio: "ignore",      timeout: 5000,    });    if (result.error === undefined && result.status === 0) return true;  }   if (workerdPlatformPackage() === null) return false;  const result = spawnSync("workerd", ["--version"], {    stdio: "ignore",    timeout: 5000,  });  return result.error === undefined && result.status === 0;}; const capnpString = (value: string): string => JSON.stringify(value); const buildConfig = (input: {  readonly listenPort: number;  readonly hostPort: number | null;  readonly modules: Readonly<Record<string, WorkerdModuleSource>>;  readonly mainModule: string;  readonly compatibilityDate: string;  readonly compatibilityFlags: readonly string[];  readonly unsafeEval: boolean;  readonly globalOutbound: "blocked" | "internet";}): string => {  const moduleNames = [    input.mainModule,    ...Object.keys(input.modules)      .filter((name) => name !== input.mainModule)      .sort(),  ];  const moduleEntries = moduleNames    .map((name) => {      const source = input.modules[name];      const field = typeof source === "object" && source.kind === "wasm" ? "wasm" : "esModule";      return `( name = ${capnpString(name)}, ${field} = embed ${capnpString(name)} )`;    })    .join(",\n    ");  // workerd's built-in `internet` service relies on the platform TLS trust  // store, which kj/tls cannot read on Windows ("unable to get local issuer  // certificate"). Declaring our own internet service with the runtime's  // bundled Mozilla roots gives every platform the same trust behavior.  const trustedCertificates = rootCertificates.map(capnpString).join(",\n      ");  const services = [    '( name = "main", worker = .mainWorker )',    '( name = "blocked", worker = .blockedWorker )',    ...(input.globalOutbound === "internet"      ? [          `( name = "internet", network = ( allow = ["public"], tlsOptions = ( trustedCertificates = [\n      ${trustedCertificates}\n    ] ) ) )`,        ]      : []),    ...(input.hostPort === null      ? []      : [`( name = "host", external = ( address = "127.0.0.1:${input.hostPort}", http = () ) )`]),  ].join(",\n    ");  const bindingEntries = [    ...(input.hostPort === null ? [] : ['( name = "HOST", service = "host" )']),    ...(input.unsafeEval ? ['( name = "UNSAFE_EVAL", unsafeEval = void )'] : []),  ];  const bindings =    bindingEntries.length === 0 ? "" : `\n  bindings = [${bindingEntries.join(", ")}],`;  const flags =    input.compatibilityFlags.length === 0      ? ""      : `\n  compatibilityFlags = [${input.compatibilityFlags.map(capnpString).join(", ")}],`;   return `using Workerd = import "/workerd/workerd.capnp"; const config :Workerd.Config = (  services = [    ${services},  ],  sockets = [    ( name = "http", address = "127.0.0.1:${input.listenPort}", http = (), service = "main" ),  ],); const mainWorker :Workerd.Worker = (  modules = [    ${moduleEntries},  ],  compatibilityDate = ${capnpString(input.compatibilityDate)},${flags}${bindings}  globalOutbound = ${capnpString(input.globalOutbound)},); const blockedWorker :Workerd.Worker = (  serviceWorkerScript = "addEventListener('fetch', event => { event.respondWith(new Response('Outbound fetch is blocked.', { status: 403 })); })",  compatibilityDate = ${capnpString(input.compatibilityDate)},);`;}; const startToolServer = async (  bridge: WorkerdToolBridge | undefined,  token: string,): Promise<{ readonly server: Server; readonly port: number }> => {  const server = createServer((request, response) => {    void (async () => {      if (request.method !== "POST" || request.url !== "/tool") {        writeJson(response, 404, { ok: false, error: "not found" });        return;      }      if (request.headers["x-executor-token"] !== token) {        writeJson(response, 401, { ok: false, error: "unauthorized" });        return;      }      if (!bridge) {        writeJson(response, 403, { ok: false, error: "tool bridge is not bound" });        return;      }      try {        const body = (await readRequestBody(request)) as { toolPath?: unknown; args?: unknown };        if (typeof body.toolPath !== "string" || body.toolPath.length === 0) {          writeJson(response, 400, { ok: false, error: "toolPath is required" });          return;        }        const result = await bridge.call(body.toolPath, body.args);        writeJson(response, 200, { ok: true, result });      } catch (cause) {        writeJson(response, 200, { ok: false, error: normalizeError(cause).message });      }    })();  });  const port = await listenLocal(server);  return { server, port };}; const waitReady = async (port: number, token: string, timeoutMs: number): Promise<void> => {  const startedAt = performance.now();  let lastError: unknown;  while (performance.now() - startedAt < timeoutMs) {    try {      const response = await fetch(`http://127.0.0.1:${port}/__health`, {        method: "POST",        headers: { "x-executor-token": token },      });      if (response.ok) {        const body = (await response.json().catch(() => null)) as {          readonly runnerToken?: unknown;        } | null;        if (body?.runnerToken === token) return;        lastError = new Error("health check did not echo the runner token");      } else {        lastError = new Error(`health check returned ${response.status}`);      }    } catch (cause) {      lastError = cause;    }    await new Promise((resolve) => setTimeout(resolve, 20));  }  throw new Error(`workerd did not become ready: ${normalizeError(lastError).message}`);}; export const createWorkerdModuleRunner = (  options: WorkerdModuleRunnerOptions,): WorkerdModuleRunner => {  const token = options.hostToken ?? crypto.randomUUID();  const startupTimeoutMs = Math.max(100, options.startupTimeoutMs ?? 30_000);  const restartBackoffMs = Math.max(0, options.restartBackoffMs ?? 100);  const compatibilityDate = options.compatibilityDate ?? "2025-06-01";  const compatibilityFlags = options.compatibilityFlags ?? ["nodejs_compat"];  const pending = new Set<PendingRequest>();  let processState:    | {        readonly proc: ChildProcessByStdio<null, Readable, Readable>;        readonly tmp: string;        readonly listenPort: number;        readonly hostServer: Server;      }    | undefined;  let starting: Promise<void> | undefined;  let disposed = false;  let lastExitAt = 0;   const failPending = (message: string) => {    const error = new Error(message);    for (const request of pending) {      request.reject(error);    }    pending.clear();  };   const disposeState = async (kill: boolean) => {    const state = processState;    processState = undefined;    if (!state) return;    if (kill && state.proc.exitCode === null && state.proc.signalCode === null) {      // workerd serve ignores SIGTERM in this version. SIGKILL is the verified immediate shutdown path.      const exited = new Promise<void>((resolve) => state.proc.once("exit", () => resolve()));      state.proc.kill("SIGKILL");      await exited;    }    await closeServer(state.hostServer).catch(() => undefined);    await rm(state.tmp, { recursive: true, force: true }).catch(() => undefined);  };   const start = async () => {    if (disposed) throw new Error("workerd runner has been disposed");    if (processState) return;    if (starting) return starting;     starting = (async () => {      const now = Date.now();      const delayMs = Math.max(0, restartBackoffMs - (now - lastExitAt));      if (delayMs > 0) {        await new Promise((resolve) => setTimeout(resolve, delayMs));      }       const tmp = join(        tmpdir(),        `executor-workerd-${process.pid}-${Date.now()}-${crypto.randomUUID()}`,      );      await mkdir(tmp, { recursive: true, mode: 0o700 });      for (const [name, source] of Object.entries(options.modules)) {        await mkdir(dirname(join(tmp, name)), { recursive: true, mode: 0o700 });        if (typeof source === "string") {          await writeFile(join(tmp, name), source, { mode: 0o600 });        } else if (source.kind === "wasm") {          await writeFile(join(tmp, name), source.bytes, { mode: 0o600 });        } else {          await writeFile(join(tmp, name), source.source, { mode: 0o600 });        }      }       const hostServer = await startToolServer(options.toolBridge, token);      const reservation = await reserveLocalPort();      const listenPort = reservation.port;      await writeFile(        join(tmp, "config.capnp"),        buildConfig({          listenPort,          hostPort: options.toolBridge ? hostServer.port : null,

Lines 1–500 of 740.