Skip to content

Instantly share code, notes, and snippets.

@ConProgramming
Created March 27, 2026 21:49
Show Gist options
  • Select an option

  • Save ConProgramming/b436377fd71de605765497ab52e899cf to your computer and use it in GitHub Desktop.

Select an option

Save ConProgramming/b436377fd71de605765497ab52e899cf to your computer and use it in GitHub Desktop.
AI SDK codemode + Secure Exec in Trigger.dev — createCodeModeTool, executor, network adapter, test task
/**
* SecureExecExecutor — runs LLM-generated code in a Secure Exec V8 isolate.
*
* Two-file pattern: the LLM writes a full ESM module with
* `export default async (codemode) => { ... }`. The executor writes three
* VFS files per execution:
* 1. _codemode_{port}.mjs — RPC proxy that exports the `codemode` object
* 2. user-code-{port}.mjs — LLM's code, written AS-IS
* 3. entry-{port}.mjs — static entrypoint that imports both and
* captures __RESULT__/__ERROR__
*
* Tool calls from sandboxed code are routed back to the host via a local
* HTTP RPC server. The sandbox only has network access to localhost.
*/
import {
NodeRuntime,
createNodeDriver,
createNodeRuntimeDriverFactory,
createInMemoryFileSystem,
} from "secure-exec";
import type { VirtualFileSystem } from "secure-exec";
import type { FsAccessRequest, NetworkAccessRequest, PermissionDecision } from "@secure-exec/core";
import { createServer, type Server } from "node:http";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { outdent } from "outdent";
import { forked__createDefaultNetworkAdapter } from "./network-adapter";
import { serializeError } from "serialize-error";
const findNodeModulesRoot = (): string => {
let dir = process.cwd();
for (;;) {
if (existsSync(join(dir, "node_modules"))) return dir;
const parent = dirname(dir);
if (parent === dir) return process.cwd();
dir = parent;
}
};
export interface ExecuteResult {
result: unknown;
error?: unknown;
logs?: string[];
}
export interface SecureExecExecutorOptions {
memoryLimit?: number;
cpuTimeLimitMs?: number;
}
export const DEFAULT_SECURE_EXEC_MEMORY_LIMIT = 64;
export const DEFAULT_SECURE_EXEC_CPU_TIME_LIMIT_MS = 30_000;
const ALLOWED_FS_PREFIXES = ["/root"];
export class SecureExecExecutor {
#runtime: NodeRuntime;
#filesystem: VirtualFileSystem;
#server: Server | null = null;
get filesystem(): VirtualFileSystem {
return this.#filesystem;
}
constructor(options: SecureExecExecutorOptions = {}) {
this.#filesystem = createInMemoryFileSystem();
this.#runtime = new NodeRuntime({
systemDriver: createNodeDriver({
filesystem: this.#filesystem,
networkAdapter: forked__createDefaultNetworkAdapter(),
moduleAccess: { cwd: findNodeModulesRoot() },
permissions: {
fs: this.#checkFs,
network: this.#checkNetwork,
},
}),
runtimeDriverFactory: createNodeRuntimeDriverFactory(),
onStdio: (event) => {
console.log("[onStdio] event", event);
},
memoryLimit: options.memoryLimit ?? DEFAULT_SECURE_EXEC_MEMORY_LIMIT,
cpuTimeLimitMs: options.cpuTimeLimitMs ?? DEFAULT_SECURE_EXEC_CPU_TIME_LIMIT_MS,
});
}
async execute(
code: string,
fns: Record<string, (...args: unknown[]) => Promise<unknown>>,
): Promise<ExecuteResult> {
const { server, port } = await this.#startRpcServer(fns);
try {
await this.#filesystem.mkdir("/root/app");
await this.#writeCodemodeModule(Object.keys(fns), port);
await this.#writeUserCode(code, port);
const entryCode = await this.#getEntryModule(port);
const execResult = await this.#runtime.run<{ result: Error | unknown }>(
entryCode,
`/root/app/entry-${port}.mjs`,
);
if (execResult.exports?.result instanceof Error) {
console.error("Error executing code", serializeError(execResult.exports?.result));
return {
result: undefined,
error: serializeError(execResult.exports?.result),
};
}
if (execResult.code !== 0) {
return {
result: undefined,
error: { message: execResult.errorMessage ?? `Exit code ${execResult.code}` },
};
}
return {
result: execResult.exports?.result,
error: undefined,
};
} catch (err) {
console.error("Error executing code", err);
return {
result: undefined,
error: serializeError(err),
};
} finally {
server.close();
}
}
dispose(): void {
this.#runtime.dispose();
this.#server?.close();
}
async #writeCodemodeModule(toolNames: string[], port: number): Promise<void> {
const proxyMethods = toolNames
.map((name) => ` ${name}: (input) => rpc('${name}', input)`)
.join(",\n");
const moduleCode = outdent`
async function rpc(toolName, args) {
try {
const res = await fetch('http://127.0.0.1:${port}', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tool: toolName, args }),
});
const data = JSON.parse(typeof res.text === 'function' ? await res.text() : res.body);
if (data.error) throw new Error(data.error);
return data.result;
} catch (err) {
return { error: err.toString() };
}
}
export const codemode = {
${proxyMethods}
};
`;
await this.#filesystem.writeFile(`/root/app/_codemode-${port}.mjs`, moduleCode);
}
async #writeUserCode(userCode: string, port: number): Promise<void> {
const cleaned = userCode
.trim()
.replace(/^```(?:js|javascript|typescript|ts|tsx|jsx)?\s*\n/, "")
.replace(/\n?```\s*$/, "")
.trim();
await this.#filesystem.writeFile(`/root/app/user-code-${port}.mjs`, cleaned);
}
async #getEntryModule(port: number): Promise<string> {
const entryCode = outdent`
import { codemode } from "./_codemode-${port}.mjs";
import { default as run } from "./user-code-${port}.mjs";
export const result = await run(codemode).catch((err) => new Error("Failed to execute code", { cause: err }));
`;
return entryCode;
}
#startRpcServer(
fns: Record<string, (...args: unknown[]) => Promise<unknown>>,
): Promise<{ server: Server; port: number }> {
return new Promise((resolve) => {
const server = createServer(async (req, res) => {
let body = "";
for await (const chunk of req) body += chunk;
try {
const { tool: name, args } = JSON.parse(body);
const fn = fns[name];
if (!fn) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: `Unknown tool: ${name}` }));
return;
}
const result = await fn(args);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ result }));
} catch (err) {
res.writeHead(500, { "Content-Type": "application/json" });
res.end(
JSON.stringify({
error: err instanceof Error ? err.message : String(err),
}),
);
}
});
server.listen(0, "127.0.0.1", () => {
const addr = server.address() as { port: number };
resolve({ server, port: addr.port });
});
});
}
#checkFs = (req: FsAccessRequest): PermissionDecision => {
const allowed = ALLOWED_FS_PREFIXES.some((prefix) => req.path.startsWith(prefix));
// eslint-disable-next-line no-console
console.log(`[sandbox-fs] ${allowed ? "ALLOW" : "DENY"} ${req.op} ${req.path}`);
return { allow: allowed };
};
#checkNetwork = (req: NetworkAccessRequest): PermissionDecision => {
// eslint-disable-next-line no-console
console.log(`[sandbox-net] ALLOW ${req.op} ${req.hostname ?? ""} ${req.url ?? ""}`);
return { allow: true };
};
}
/**
* Creates a "codemode" tool that lets the LLM write JavaScript ESM modules
* orchestrating multiple tool calls in a single sandbox execution.
*
* The LLM writes: `export default async (codemode) => { ... }`
* The executor writes a static entrypoint that imports and calls it.
*/
import { tool, type Tool } from "ai";
import { z } from "zod";
import { logger } from "@trigger.dev/sdk";
import { outdent } from "outdent";
import { SecureExecExecutor, type SecureExecExecutorOptions } from "./executor.js";
export { SecureExecExecutor } from "./executor.js";
export type { ExecuteResult, SecureExecExecutorOptions } from "./executor.js";
export type { VirtualFileSystem } from "secure-exec";
export type { CodeModeToolOptions };
interface CodeModeToolOptions {
memoryLimit?: number;
cpuTimeLimitMs?: number;
}
export const createCodeModeTool = (
tools: Record<string, Tool>,
options: SecureExecExecutorOptions = {},
) => {
const executor = new SecureExecExecutor(options);
const toolEntries = Object.entries(tools)
.filter(([, t]) => typeof t.execute === "function")
.map(
([name, t]) => ` /** ${t.description ?? name} */\n ${name}: (input) => Promise<unknown>;`,
)
.join("\n");
const codeToolDescription = outdent`
Execute JavaScript in a sandboxed V8 isolate by writing a full ESM module.
The sandbox has an in-memory virtual filesystem (import * as fs from "fs") and
access to npm packages from the host. Files placed in /root/workspace/ persist
across codemode calls within the same session.
Write a JavaScript ESM module. Export a default async function that receives
${"`codemode`"} as its argument. Use import statements at the top for npm packages
and skill helpers. Return the result from the function.
npm packages MUST use namespace imports: import * as pkg from "pkg"
CORRECT FORMAT:
import * as JSZip from "jszip";
import { validateDocx } from "/root/skills/docx/helpers/validate-docx.mjs";
export default async (codemode) => {
const a = await codemode.toolA({ param: "value" });
const b = await codemode.toolB({ param: "value" });
return { a, b };
};
WRONG (require/eval — use import instead):
const JSZip = require("jszip");
eval(fs.readFileSync(...));
WRONG (default/named imports for CJS npm packages — use import * as):
import JSZip from "jszip";
import { Document } from "docx";
WRONG (bare statements, no export default):
const a = await codemode.toolA({ param: "value" });
return a;
Available codemode API:
declare const codemode: {
${toolEntries}
}
`;
const codemodeTool = tool({
description: codeToolDescription,
inputSchema: z.object({
code: z
.string()
.describe("A JavaScript ESM module with export default async (codemode) => { ... }"),
}),
execute: async ({ code }) => {
logger.info("codemode received generated code", {
codeLength: code.length,
codePreview: code.slice(0, 1000),
});
const fns: Record<string, (...args: unknown[]) => Promise<unknown>> = {};
for (const [name, t] of Object.entries(tools)) {
if (typeof t.execute === "function") {
fns[name] = t.execute as (...args: unknown[]) => Promise<unknown>;
}
}
const result = await executor.execute(code, fns);
logger.info("codemode sandbox execution completed", {
hasError: Boolean(result.error),
logCount: result.logs?.length ?? 0,
});
if (result.error) throw new Error(result.error);
return result;
},
});
return {
tool: codemodeTool,
filesystem: executor.filesystem,
dispose: () => executor.dispose(),
};
};
import * as dns from "node:dns";
import * as net from "node:net";
import * as http from "node:http";
import * as https from "node:https";
import * as zlib from "node:zlib";
import type { NetworkAdapter } from "@secure-exec/core";
export interface DefaultNetworkAdapterOptions {
/** Pre-seed loopback ports that should bypass SSRF checks (e.g. host-managed servers). */
initialExemptPorts?: Iterable<number>;
}
interface LoopbackAwareNetworkAdapter extends NetworkAdapter {
__setLoopbackPortChecker?(checker: (hostname: string, port: number) => boolean): void;
}
/** Check whether an IP address falls in a private/reserved range (SSRF protection). */
export function isPrivateIp(ip: string): boolean {
// Normalize IPv4-mapped IPv6 (::ffff:a.b.c.d → a.b.c.d)
const normalized = ip.startsWith("::ffff:") ? ip.slice(7) : ip;
if (net.isIPv4(normalized)) {
const parts = normalized.split(".").map(Number);
const [a, b] = parts;
return (
a === 10 ||
(a === 172 && b >= 16 && b <= 31) ||
(a === 192 && b === 168) ||
a === 127 ||
(a === 169 && b === 254) ||
a === 0 ||
(a >= 224 && a <= 239) ||
a >= 240
);
}
if (net.isIPv6(normalized)) {
const lower = normalized.toLowerCase();
return (
lower === "::1" ||
lower === "::" ||
lower.startsWith("fc") ||
lower.startsWith("fd") ||
lower.startsWith("fe80") ||
lower.startsWith("ff")
);
}
return false;
}
/** Check whether a hostname is a loopback address (127.x.x.x, ::1, localhost). */
function isLoopbackHost(hostname: string): boolean {
const bare =
hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
if (bare === "localhost" || bare === "::1") return true;
if (net.isIPv4(bare) && bare.startsWith("127.")) return true;
return false;
}
function getUrlPort(parsed: URL): number {
return parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
}
/**
* Resolve hostname to IP and block private/reserved ranges (SSRF protection).
*
* Loopback requests are allowed only when an explicit exemption or the
* runtime-provided kernel listener checker claims the requested port.
*/
async function assertNotPrivateHost(
url: string,
allowLoopbackPort?: (hostname: string, port: number) => boolean,
): Promise<void> {
// WARNING: This is a temporary fix to allow loopback requests to the RPC server.
// TODO: Remove this once we receive a proper fix from the Secure Exec team.
return false;
const parsed = new URL(url);
if (parsed.protocol === "data:" || parsed.protocol === "blob:") return;
const hostname = parsed.hostname;
const bare =
hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
if (isLoopbackHost(hostname)) {
const port = getUrlPort(parsed);
if (allowLoopbackPort?.(hostname, port)) {
return;
}
}
if (net.isIP(bare)) {
if (isPrivateIp(bare)) {
throw new Error(`SSRF blocked: ${hostname} resolves to private IP`);
}
return;
}
const address = await new Promise<string>((resolve, reject) => {
dns.lookup(bare, (err, addr) => {
if (err) reject(err);
else resolve(addr);
});
});
if (isPrivateIp(address)) {
throw new Error(`SSRF blocked: ${hostname} resolves to private IP ${address}`);
}
}
const MAX_REDIRECTS = 20;
/**
* Create a Node.js network adapter that provides real fetch, DNS, and HTTP
* client support. Binary responses are base64-encoded with an
* `x-body-encoding` header so the bridge can decode them.
*/
export function forked__createDefaultNetworkAdapter(
options?: DefaultNetworkAdapterOptions,
): NetworkAdapter {
const upgradeSockets = new Map<number, import("stream").Duplex>();
const initialExemptPorts = new Set<number>(options?.initialExemptPorts);
let nextUpgradeSocketId = 1;
let onUpgradeSocketData: ((socketId: number, dataBase64: string) => void) | null = null;
let onUpgradeSocketEnd: ((socketId: number) => void) | null = null;
let dynamicLoopbackPortChecker: ((hostname: string, port: number) => boolean) | undefined;
const allowLoopbackPort = (hostname: string, port: number): boolean => {
if (initialExemptPorts.has(port)) return true;
if (dynamicLoopbackPortChecker?.(hostname, port)) return true;
return false;
};
const adapter: LoopbackAwareNetworkAdapter = {
__setLoopbackPortChecker(checker) {
dynamicLoopbackPortChecker = checker;
},
upgradeSocketWrite(socketId, dataBase64) {
const socket = upgradeSockets.get(socketId);
if (socket && !socket.destroyed) {
socket.write(Buffer.from(dataBase64, "base64"));
}
},
upgradeSocketEnd(socketId) {
const socket = upgradeSockets.get(socketId);
if (socket && !socket.destroyed) {
socket.end();
}
},
upgradeSocketDestroy(socketId) {
const socket = upgradeSockets.get(socketId);
if (socket) {
socket.destroy();
upgradeSockets.delete(socketId);
}
},
setUpgradeSocketCallbacks(callbacks) {
onUpgradeSocketData = callbacks.onData;
onUpgradeSocketEnd = callbacks.onEnd;
},
async fetch(url, requestOptions) {
let currentUrl = url;
let redirected = false;
for (let i = 0; i <= MAX_REDIRECTS; i++) {
await assertNotPrivateHost(currentUrl, allowLoopbackPort);
const response = await fetch(currentUrl, {
method: requestOptions?.method || "GET",
headers: requestOptions?.headers,
body: requestOptions?.body,
redirect: "manual",
});
const status = response.status;
if (
status === 301 ||
status === 302 ||
status === 303 ||
status === 307 ||
status === 308
) {
const location = response.headers.get("location");
if (!location) break;
currentUrl = new URL(location, currentUrl).href;
redirected = true;
if (status === 301 || status === 302 || status === 303) {
requestOptions = { ...requestOptions, method: "GET", body: undefined };
}
continue;
}
const headers: Record<string, string> = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
delete headers["content-encoding"];
const contentType = response.headers.get("content-type") || "";
const isBinary =
contentType.includes("octet-stream") ||
contentType.includes("gzip") ||
currentUrl.endsWith(".tgz");
let body: string;
if (isBinary) {
const buffer = await response.arrayBuffer();
body = Buffer.from(buffer).toString("base64");
headers["x-body-encoding"] = "base64";
} else {
body = await response.text();
}
return {
ok: response.ok,
status: response.status,
statusText: response.statusText,
headers,
body,
url: currentUrl,
redirected,
};
}
throw new Error("Too many redirects");
},
async dnsLookup(hostname) {
return new Promise((resolve) => {
dns.lookup(hostname, (err, address, family) => {
if (err) {
resolve({ error: err.message, code: err.code || "ENOTFOUND" });
} else {
resolve({ address, family });
}
});
});
},
async httpRequest(url, requestOptions) {
await assertNotPrivateHost(url, allowLoopbackPort);
type HttpRequestResult = Awaited<ReturnType<NetworkAdapter["httpRequest"]>> & {
rawHeaders?: string[];
};
return new Promise<HttpRequestResult>((resolve, reject) => {
const urlObj = new URL(url);
const isHttps = urlObj.protocol === "https:";
const transport = isHttps ? https : http;
const reqOptions: https.RequestOptions = {
hostname: urlObj.hostname,
port: urlObj.port || (isHttps ? 443 : 80),
path: urlObj.pathname + urlObj.search,
method: requestOptions?.method || "GET",
headers: requestOptions?.headers || {},
// Keep host-side pooling disabled so sandbox http.Agent semantics
// are controlled entirely by the bridge layer.
agent: false,
...(isHttps &&
requestOptions?.rejectUnauthorized !== undefined && {
rejectUnauthorized: requestOptions.rejectUnauthorized,
}),
};
const req = transport.request(reqOptions, (res) => {
const chunks: Buffer[] = [];
res.on("data", (chunk: Buffer) => chunks.push(chunk));
res.on("end", async () => {
let buffer: Buffer = Buffer.concat(chunks);
const contentEncoding = res.headers["content-encoding"];
if (contentEncoding === "gzip" || contentEncoding === "deflate") {
try {
buffer = await new Promise((responseResolve, responseReject) => {
const decompress = contentEncoding === "gzip" ? zlib.gunzip : zlib.inflate;
decompress(buffer, (err, result) => {
if (err) responseReject(err);
else responseResolve(result);
});
});
} catch {
// Preserve the original buffer when decompression fails.
}
}
const contentType = res.headers["content-type"] || "";
const isBinary =
contentType.includes("octet-stream") ||
contentType.includes("gzip") ||
url.endsWith(".tgz");
const headers: Record<string, string> = {};
const rawHeaders = [...res.rawHeaders];
Object.entries(res.headers).forEach(([key, value]) => {
if (typeof value === "string") headers[key] = value;
else if (Array.isArray(value)) headers[key] = value.join(", ");
});
delete headers["content-encoding"];
const trailers: Record<string, string> = {};
if (res.trailers) {
Object.entries(res.trailers).forEach(([key, value]) => {
if (typeof value === "string") trailers[key] = value;
});
}
const hasTrailers = Object.keys(trailers).length > 0;
const base = {
status: res.statusCode || 200,
statusText: res.statusMessage || "OK",
headers,
rawHeaders,
url,
...(hasTrailers ? { trailers } : {}),
};
if (isBinary) {
headers["x-body-encoding"] = "base64";
resolve({ ...base, body: buffer.toString("base64") });
} else {
resolve({ ...base, body: buffer.toString("utf-8") });
}
});
res.on("error", reject);
});
req.on("upgrade", (res, socket, head) => {
const headers: Record<string, string> = {};
const rawHeaders = [...res.rawHeaders];
Object.entries(res.headers).forEach(([key, value]) => {
if (typeof value === "string") headers[key] = value;
else if (Array.isArray(value)) headers[key] = value.join(", ");
});
const socketId = nextUpgradeSocketId++;
upgradeSockets.set(socketId, socket);
socket.on("data", (chunk) => {
if (onUpgradeSocketData) {
onUpgradeSocketData(socketId, chunk.toString("base64"));
}
});
socket.on("close", () => {
if (onUpgradeSocketEnd) {
onUpgradeSocketEnd(socketId);
}
upgradeSockets.delete(socketId);
});
resolve({
status: res.statusCode || 101,
statusText: res.statusMessage || "Switching Protocols",
headers,
rawHeaders,
body: head.toString("base64"),
url,
upgradeSocketId: socketId,
});
});
req.on("connect", (res, socket, head) => {
const headers: Record<string, string> = {};
const rawHeaders = [...res.rawHeaders];
Object.entries(res.headers).forEach(([key, value]) => {
if (typeof value === "string") headers[key] = value;
else if (Array.isArray(value)) headers[key] = value.join(", ");
});
const socketId = nextUpgradeSocketId++;
upgradeSockets.set(socketId, socket);
socket.on("data", (chunk) => {
if (onUpgradeSocketData) {
onUpgradeSocketData(socketId, chunk.toString("base64"));
}
});
socket.on("close", () => {
if (onUpgradeSocketEnd) {
onUpgradeSocketEnd(socketId);
}
upgradeSockets.delete(socketId);
});
resolve({
status: res.statusCode || 200,
statusText: res.statusMessage || "Connection established",
headers,
rawHeaders,
body: head.toString("base64"),
url,
upgradeSocketId: socketId,
});
});
req.on("error", reject);
if (requestOptions?.body) req.write(requestOptions.body);
req.end();
});
},
};
return adapter;
}
/**
* Code Mode with Secure Exec
*
* Same pattern as @cloudflare/codemode, but using Secure Exec as the sandbox
* instead of Cloudflare Workers. The LLM writes code that calls tools via
* `codemode.*`, executed safely in a V8 isolate.
*
* See: https://blog.cloudflare.com/code-mode/
*/
import { logger, task } from "@trigger.dev/sdk";
import { generateText, tool, stepCountIs } from "ai";
import { createAIProvider } from "@govsignals/ai/providers";
import { z } from "zod";
import { createCodeModeTool } from "../lib/create-codemode-tool";
const tools = {
getWeather: tool({
description:
"Get current weather for a city. Returns { temp_f: number, condition: string, humidity: number }.",
inputSchema: z.object({
city: z.string().describe("City name"),
}),
execute: async ({ city }) => {
const data: Record<string, { temp_f: number; condition: string; humidity: number }> = {
"San Francisco": { temp_f: 62, condition: "Foggy", humidity: 78 },
"New York": { temp_f: 84, condition: "Sunny", humidity: 55 },
London: { temp_f: 58, condition: "Rainy", humidity: 88 },
Tokyo: { temp_f: 91, condition: "Humid", humidity: 72 },
};
const output = data[city] ?? { temp_f: 70, condition: "Clear", humidity: 50 };
logger.info("getWeather output", { output });
return output;
},
}),
};
export const testCodeMode = task({
id: "test-code-mode",
retry: { maxAttempts: 1 },
run: async () => {
const { tool: codemodeTool, dispose } = createCodeModeTool(tools, {
cpuTimeLimitMs: 10_000,
});
try {
const AI = await createAIProvider();
logger.info("test-code-mode starting", {
model: "thinking",
});
const { text } = await generateText({
model: AI.languageModel("thinking"),
prompt:
"Compare the weather in San Francisco and Tokyo. Calculate the temperature difference in both Fahrenheit and Celsius.",
stopWhen: stepCountIs(10),
tools: {
codemode: codemodeTool,
},
experimental_telemetry: { isEnabled: true },
});
logger.info("Code mode test completed", {
textLength: text.length,
textPreview: text.slice(0, 500),
});
return { text };
} finally {
dispose();
}
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment