Start typing to search the documentation.

Build navigation

Cloudflare

Use @opencode/sdk/workerd inside a Cloudflare Durable Object. This profile uses the object’s SQLite storage, persists durable events for eviction recovery, and replaces unavailable local filesystem and process services.

bun add @opencode/sdk

Hold one host for the lifetime of the Durable Object instance instead of creating one for every request.

import { OpenCodeWorkerd } from "@opencode/sdk/workerd"
import myPlugin from "./my-plugin"

export class OpenCodeDO {
  private readonly opencode: Promise<OpenCodeWorkerd.Interface>

  constructor(state: DurableObjectState) {
    this.opencode = state.blockConcurrencyWhile(() =>
      OpenCodeWorkerd.create({
        storage: state.storage,
        config: { default_agent: "build" },
        plugins: [myPlugin],
      }),
    )
  }

  async fetch() {
    const opencode = await this.opencode
    return Response.json(await opencode.server.info())
  }
}

blockConcurrencyWhile keeps every Durable Object event out until the host is ready and resets the object if initialization fails. The retained Promise gives request handlers direct access to the same host after startup.

constructor(state: DurableObjectState) {
  this.opencode = state.blockConcurrencyWhile(() =>
    OpenCodeWorkerd.create({ storage: state.storage }),
  )
}

Wrangler selects OpenCode’s Workerd-safe implementations through the workerd package condition. Cloudflare may evict a Durable Object without running cleanup, so correctness does not depend on close() being called.

wrangler.jsonc
{
  "compatibility_flags": ["nodejs_compat"]
}

Route outbound requests

Pass fetch to route OpenCode’s outbound HTTP requests (OpenCode Console config, model requests, web fetch and search, well-known config, and remote MCP servers) through a custom handler such as a Service Binding. Agent code in Code Mode continues to use globalThis.fetch.

OpenCodeWorkerd.create({
  storage: state.storage,
  fetch: (input, init) => {
    const request = new Request(input, init)
    return new URL(request.url).hostname === "console.example.com" ? env.CONSOLE.fetch(request) : fetch(request)
  },
})

OpenCode Console

With an OpenCode Console connection, the Workerd profile refreshes the Console’s config when a prompt starts, not every minute, so an idle Durable Object can hibernate. Pass console: { poll: true } to refresh every minute as other hosts do.

Customize

Customize your OpenCode instance by registering plugins bundled with your Worker. Pass plugins to OpenCodeWorkerd.create() to customize agents, models, tools, and other behavior:

import { Plugin } from "@opencode/plugin"
import { OpenCodeWorkerd } from "@opencode/sdk/workerd"

const plugin = Plugin.define({
  id: "customize-agent",
  async setup(ctx) {
    await ctx.agent.transform((agents) => {
      agents.update("build", (agent) => {
        agent.description = "Builds features and fixes bugs for our team"
      })
    })
  },
})

await OpenCodeWorkerd.create({
  storage: state.storage,
  config: { default_agent: "build" },
  plugins: [plugin],
})

See the full plugins documentation for plugin hooks, transforms, tools, and the complete plugin context.