npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cloudflare/codemode

v0.3.4

Published

Code Mode: use LLMs to generate executable code that performs tool calls

Readme

@cloudflare/codemode

Instead of asking LLMs to call tools directly, Code Mode lets them write executable code that orchestrates multiple operations. LLMs are better at writing code than calling tools — they've seen millions of lines of real-world code but only contrived tool-calling examples.

Code Mode generates TypeScript type definitions from your tools for LLM context, and executes the generated JavaScript in secure, isolated sandboxes with millisecond startup times.

Experimental — may have breaking changes. Use with caution in production.

Installation

# With Vercel AI SDK
npm install @cloudflare/codemode agents ai zod

# With TanStack AI
npm install @cloudflare/codemode agents @tanstack/ai zod

# Utilities only (no framework peer deps needed)
npm install @cloudflare/codemode

The main entry point (@cloudflare/codemode) has no peer dependency on ai, @tanstack/ai, or zod. Framework-specific packages are only required when importing from @cloudflare/codemode/ai or @cloudflare/codemode/tanstack-ai.

Quick Start

createCodeTool takes your tools and an executor, and returns a single AI SDK tool that lets the LLM write code instead of making individual tool calls.

import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { streamText, tool } from "ai";
import { z } from "zod";

// 1. Define your tools using the AI SDK tool() wrapper
const tools = {
  getWeather: tool({
    description: "Get weather for a location",
    inputSchema: z.object({ location: z.string() }),
    execute: async ({ location }) => `Weather in ${location}: 72°F, sunny`
  }),
  sendEmail: tool({
    description: "Send an email",
    inputSchema: z.object({
      to: z.string(),
      subject: z.string(),
      body: z.string()
    }),
    execute: async ({ to, subject, body }) => `Email sent to ${to}`
  })
};

// 2. Create an executor (runs code in an isolated Worker)
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });

// 3. Create the codemode tool
const codemode = createCodeTool({ tools, executor });

// 4. Use it with streamText — the LLM writes code that calls your tools
const result = streamText({
  model,
  system: "You are a helpful assistant.",
  messages,
  tools: { codemode }
});

The LLM sees a typed codemode object and writes code like:

async () => {
  const weather = await codemode.getWeather({ location: "London" });
  if (weather.includes("sunny")) {
    await codemode.sendEmail({
      to: "[email protected]",
      subject: "Nice day!",
      body: `It's ${weather}`
    });
  }
  return { weather, notified: true };
};

TanStack AI

If you're using TanStack AI instead of the Vercel AI SDK, import from @cloudflare/codemode/tanstack-ai:

import {
  createCodeTool,
  tanstackTools
} from "@cloudflare/codemode/tanstack-ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { chat } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";

// 1. Define your tools using TanStack AI's toolDefinition()
const getWeather = toolDefinition({
  name: "get_weather",
  description: "Get weather for a location",
  inputSchema: z.object({ location: z.string() })
}).server(async ({ location }) => `Weather in ${location}: 72°F, sunny`);

// 2. Create the codemode tool
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const codeTool = createCodeTool({
  tools: [tanstackTools([getWeather])],
  executor
});

// 3. Use it with TanStack AI's chat()
const stream = chat({
  adapter: openaiText("gpt-4o"),
  tools: [codeTool],
  messages
});

tanstackTools() converts TanStack AI tools (array-based) into the record-based ToolProvider format. It also accepts an optional namespace:

createCodeTool({
  tools: [tanstackTools(weatherTools, "weather"), tanstackTools(dbTools, "db")],
  executor
});

Tool Providers

Tool providers let you compose capabilities from different packages into a single sandbox execution. Each provider contributes tools under a namespace — the LLM can use all of them in the same code block.

import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { stateTools } from "@cloudflare/shell/workers";

const executor = new DynamicWorkerExecutor({ loader: env.LOADER });

const codemode = createCodeTool({
  tools: [
    { tools: myTools }, // codemode.myTool({ query: "test" })
    stateTools(workspace) // state.readFile("/path")
  ],
  executor
});

The sandbox has both codemode.* and state.*:

async () => {
  const files = await state.glob("/src/**/*.ts");
  const results = await Promise.all(
    files.map((f) => codemode.analyzeFile({ path: f }))
  );
  await state.writeJson("/report.json", results);
  return results.length;
};

The ToolProvider interface

Any package can provide tools to the sandbox. A ToolProvider describes:

  • an optional name (the namespace in the sandbox, e.g. "state", "db" — defaults to "codemode")
  • tools — AI SDK tools, tool descriptors, or simple { execute } records
  • optional types for LLM context (auto-generated from tools if omitted)
  • optional positionalArgs flag (e.g. state.readFile("/path") vs codemode.search({ query }))
import type { ToolProvider } from "@cloudflare/codemode";

const dbProvider: ToolProvider = {
  name: "db",
  tools: {
    query: {
      description: "Run a SQL query",
      execute: async (sql: unknown) => db.prepare(sql as string).all()
    }
  },
  positionalArgs: true
};

Pass providers as the tools array in createCodeTool, or resolve them for direct executor use:

import { resolveProvider } from "@cloudflare/codemode";

await executor.execute(code, [resolveProvider(dbProvider)]);

Architecture

How it works

┌─────────────────────┐        ┌─────────────────────────────────────────────┐
│                     │        │  Dynamic Worker (isolated sandbox)           │
│  Host Worker        │  RPC   │                                              │
│                     │◄──────►│  LLM-generated code runs here               │
│  ToolDispatchers    │        │  codemode.myTool() → dispatcher.call()       │
│  (one per namespace)│        │  state.readFile()  → dispatcher.call()       │
│                     │        │  db.query()        → dispatcher.call()       │
│                     │        │                                              │
│                     │        │  fetch() blocked by default                  │
└─────────────────────┘        └─────────────────────────────────────────────┘
  1. createCodeTool generates TypeScript type definitions from your tool providers and builds a description the LLM can read
  2. The LLM writes an async arrow function that calls codemode.toolName(args) and any provider namespaces (state.*, db.*, etc.)
  3. Code is normalized via AST parsing (acorn) and sent to the executor
  4. DynamicWorkerExecutor spins up an isolated Worker via WorkerLoader, with one ToolDispatcher per namespace
  5. Inside the sandbox, a Proxy per namespace intercepts calls and routes them back via Workers RPC
  6. Console output is captured and returned alongside the result

Network isolation

External fetch() and connect() are blocked by default — enforced at the Workers runtime level via globalOutbound: null. Sandboxed code can only interact with the host through namespaced tool calls.

To allow controlled outbound access, pass a Fetcher:

const executor = new DynamicWorkerExecutor({
  loader: env.LOADER,
  globalOutbound: null // default — fully isolated
  // globalOutbound: env.MY_OUTBOUND_SERVICE, // route through a Fetcher
});

The Executor Interface

The Executor interface is deliberately minimal — implement it to run code in any sandbox:

interface Executor {
  execute(
    code: string,
    providers:
      | ResolvedProvider[]
      | Record<string, (...args: unknown[]) => Promise<unknown>>
  ): Promise<ExecuteResult>;
}

interface ExecuteResult {
  result: unknown;
  error?: string;
  logs?: string[];
}

DynamicWorkerExecutor is the Cloudflare Workers implementation, but you can build your own for Node VM, QuickJS, containers, or anything else.

// Example: a simple Node VM executor
class NodeVMExecutor implements Executor {
  async execute(code, fns): Promise<ExecuteResult> {
    try {
      const fn = new AsyncFunction("codemode", `return await (${code})()`);
      const result = await fn(fns);
      return { result };
    } catch (err) {
      return { result: undefined, error: err.message };
    }
  }
}

Configuration

Wrangler bindings

// wrangler.jsonc
{
  "worker_loaders": [{ "binding": "LOADER" }],
  "compatibility_flags": ["nodejs_compat"]
}

DynamicWorkerExecutor options

| Option | Type | Default | Description | | ---------------- | ------------------------ | -------- | ------------------------------------------------------------ | | loader | WorkerLoader | required | Worker Loader binding from env.LOADER | | timeout | number | 30000 | Execution timeout in ms | | globalOutbound | Fetcher \| null | null | Network access control. null = blocked, Fetcher = routed | | modules | Record<string, string> | {} | Extra modules importable in the sandbox |

createCodeTool options

| Option | Type | Default | Description | | ------------- | ---------------------------- | -------------- | ------------------------------------------------------ | | tools | ToolSet \| ToolDescriptors | required | Your tools (AI SDK tool() or raw descriptors) | | executor | Executor | required | Where to run the generated code | | description | string | auto-generated | Custom tool description. Use {{types}} for type defs |

Agent Integration

The user sends a message, the agent passes it to an LLM with the codemode tool, and the LLM writes and executes code to fulfill the request.

import { Agent } from "agents";
import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { streamText, convertToModelMessages, stepCountIs } from "ai";

export class MyAgent extends Agent<Env, State> {
  async onChatMessage() {
    const executor = new DynamicWorkerExecutor({ loader: this.env.LOADER });

    const codemode = createCodeTool({ tools: myTools, executor });

    const result = streamText({
      model,
      system: "You are a helpful assistant.",
      messages: await convertToModelMessages(this.state.messages),
      tools: { codemode },
      stopWhen: stepCountIs(10)
    });

    // Stream response back to client...
  }
}

With filesystem access (via @cloudflare/shell)

Combine codemode.* tools with state.* filesystem operations:

import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { Workspace } from "@cloudflare/shell";
import { stateTools } from "@cloudflare/shell/workers";

export class MyAgent extends Agent<Env> {
  workspace = new Workspace({ sql: this.ctx.storage.sql });

  getCodemodeTool() {
    const executor = new DynamicWorkerExecutor({ loader: this.env.LOADER });
    return createCodeTool({
      tools: [{ tools: myDomainTools }, stateTools(this.workspace)],
      executor
    });
  }
}

With MCP tools

MCP tools work the same way — just merge them into the tool set:

const codemode = createCodeTool({
  tools: {
    ...myTools,
    ...this.mcp.getAITools()
  },
  executor
});

Utilities

sanitizeToolName(name)

Converts tool names into valid JavaScript identifiers. Handles hyphens, dots, digits, reserved words. Called automatically by DynamicWorkerExecutor on fns keys — you only need this for custom use cases.

import { sanitizeToolName } from "@cloudflare/codemode";

sanitizeToolName("my-tool"); // "my_tool"
sanitizeToolName("3d-render"); // "_3d_render"
sanitizeToolName("delete"); // "delete_"

normalizeCode(code)

Normalizes LLM-generated code into a valid async arrow function. Strips markdown code fences, handles various function formats. Called automatically by DynamicWorkerExecutor — you only need this for custom use cases.

import { normalizeCode } from "@cloudflare/codemode";

normalizeCode("```js\nconst x = 1;\nx\n```");
// "async () => {\nconst x = 1;\nreturn (x)\n}"

generateTypesFromJsonSchema(tools)

Generates TypeScript type definitions from tool descriptors with plain JSON Schema. No AI SDK or Zod dependency required.

import { generateTypesFromJsonSchema } from "@cloudflare/codemode";

const types = generateTypesFromJsonSchema({
  getWeather: {
    description: "Get weather for a city",
    inputSchema: {
      type: "object",
      properties: {
        city: { type: "string", description: "City name" }
      },
      required: ["city"]
    }
  }
});
// Returns TypeScript declarations like:
// type GetWeatherInput = { city: string }
// declare const codemode: { getWeather: (input: GetWeatherInput) => Promise<...>; }

generateTypes(tools) (AI SDK)

Generates TypeScript type definitions from AI SDK tools or Zod-based tool descriptors. Requires ai and zod peer dependencies.

import { generateTypes } from "@cloudflare/codemode/ai";

const types = generateTypes(myAiSdkTools);

Module Structure

| Module | Peer deps | Exports | | ---------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | @cloudflare/codemode | None | sanitizeToolName, normalizeCode, generateTypesFromJsonSchema, jsonSchemaToType, DynamicWorkerExecutor, ToolDispatcher, ToolProvider, resolveProvider | | @cloudflare/codemode/ai | ai, zod | createCodeTool, generateTypes, aiTools, resolveProvider, ToolDescriptor, ToolDescriptors | | @cloudflare/codemode/tanstack-ai | @tanstack/ai, zod | createCodeTool, generateTypes, tanstackTools, resolveProvider |

Limitations

  • Tool approval (needsApproval) is not supported yet. Tools with needsApproval: true execute immediately inside the sandbox without pausing for approval. Support for approval flows within codemode is planned. For now, do not pass approval-required tools to createCodeTool — use them through standard AI SDK tool calling instead.
  • Requires Cloudflare Workers environment for DynamicWorkerExecutor
  • Limited to JavaScript execution

Examples

License

MIT