cloudflare-computer
Skill15 starsChanged 7 days agoMITView on GitHub
Use the @cloudflare/computer package to give a Durable Object a durable SQLite-backed filesystem (workspace.fs) and pluggable command/code execution backends (container, worker-shell, worker-javascript), including AI SDK tools, git, and stub disposal rules. Load when working with @cloudflare/computer, Workspace, withWorkspace/getWorkspace, workspace.fs, or runtime.exec.
- Reads credentials
- Installs packages
In this repository86 other files it gives its agents.
- AGENTS.mdAGENTS.md
- apple-apple-notesSkill
- apple-apple-remindersSkill
- apple-findmySkill
- apple-imessageSkill
- autonomous-ai-agents-claude-codeSkill
- autonomous-ai-agents-codexSkill
- autonomous-ai-agents-hermes-agentSkill
- autonomous-ai-agents-opencodeSkill
- cloudflare-platform-skillsSkill
- computer-use-permissionsSkill
- creative-architecture-diagramSkill
- creative-ascii-artSkill
- creative-ascii-videoSkill
- creative-baoyu-comicSkill
- creative-baoyu-infographicSkill
- creative-claude-designSkill
- creative-comfyuiSkill
- creative-design-mdSkill
- creative-excalidrawSkill
- creative-humanizerSkill
- creative-ideationSkill
- creative-manim-videoSkill
- creative-p5jsSkill
- creative-pixel-artSkill
- creative-popular-web-designsSkill
- creative-pretextSkill
- creative-sketchSkill
- creative-songwriting-and-ai-musicSkill
- creative-touchdesigner-mcpSkill
- data-science-jupyter-live-kernelSkill
- dogfoodSkill
- email-himalayaSkill
- gaming-minecraft-modpack-serverSkill
- gaming-pokemon-playerSkill
- github-codebase-inspectionSkill
- github-github-authSkill
- github-github-code-reviewSkill
- github-github-issuesSkill
- github-github-pr-workflowSkill
- github-github-repo-managementSkill
- mcp-native-mcpSkill
- media-gif-searchSkill
- media-heartmulaSkill
- media-songseeSkill
- media-spotifySkill
- media-youtube-contentSkill
- mlops-evaluation-evaluating-llms-harnessSkill
- mlops-evaluation-weights-and-biasesSkill
- mlops-huggingface-hubSkill
- mlops-inference-llama-cppSkill
- mlops-inference-obliteratusSkill
- mlops-inference-outlinesSkill
- mlops-inference-serving-llms-vllmSkill
- mlops-models-audiocraft-audio-generationSkill
- mlops-models-segment-anything-modelSkill
- mlops-research-dspySkill
- mlops-training-axolotlSkill
- mlops-training-fine-tuning-with-trlSkill
- mlops-training-unslothSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 0 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 126
Add the loader binding and the experimental flag to wrangler.jsonc:
- Very specific: line 351
Don't worry about ws.fs, ws.runtime, or ws.git — they ride with the parent.
- Very specific: line 425
When assigning a workspace to a Think agent's workspace, pass useThink: true so Think's compatibility methods are added alongside …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Using @cloudflare/computer
@cloudflare/computer gives a Cloudflare Durable Object a persistent,
SQLite-backed virtual filesystem plus pluggable command and code
execution. It is built for agents that need a small, portable working
directory and the tools to read, write, and run things in it.
PREVIEW ONLY. The package is provided as a preview for feedback only. APIs are unstable and the design is subject to change. Suitable for experiments and prototypes; NOT suitable for production use.
Upstream: https://github.com/cloudflare/computer (MIT license — see
the LICENSE file next to this skill).
What you get
- A filesystem in your Durable Object.
workspace.fslooks likenode:fs/promises—readFile,writeFile,mkdir,readdir,rm,grep— durable across DO restarts, backed by the DO's own SQLite storage. - One execution surface, several backends.
workspace.runtime.exec()runs a shell command or an ECMAScript module. Pick a full Linux container, a fast in-Worker shell, or an isolated JavaScript runtime — all against the same files. - Batteries for agents. Ready-made AI SDK
tools (
read,write,edit,ls,exec), a git client, R2-backed read-only mounts, and helpers for publishing files.
The Workspace can also run with no execution backend at all — just the filesystem.
Limits
- ~10 GB per workspace (shares storage with the DO).
- The container-side filesystem is held in memory. Aim for agent-scale workspaces, not full monorepos.
- Container access goes through FUSE, so heavy I/O (large
node_modulesinstalls, big tarball extractions) is slower than a native disk.
Installation
npm install @cloudflare/computerYour Worker needs the nodejs_compat compatibility flag. The
worker-shell and worker-javascript backends additionally need the
experimental flag and a Worker Loader binding. Each backend has its
own binding requirements — see Choosing a backend.
Optional peer dependencies, installed only if you use the matching
feature: ai and zod (for @cloudflare/computer/tools),
@platformatic/vfs (for the Node-side VFS provider).
Quick start
The smallest useful thing is a filesystem with no execution backend. Add
withWorkspace to a Durable Object and you have durable files:
import { withWorkspace, getWorkspace } from "@cloudflare/computer";
import { DurableObject } from "cloudflare:workers";
export class Agent extends withWorkspace(
class extends DurableObject<Env> {},
(self) => ({ storage: self.ctx.storage }),
) {}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const id = env.Agent.idFromName("user-123");
using ws = await getWorkspace(env.Agent.get(id));
await ws.fs.writeFile("/notes.md", "- [ ] ship it\n");
const notes = await ws.fs.readFile("/notes.md", "utf8");
return new Response(notes);
},
} satisfies ExportedHandler<Env>;wrangler.jsonc:
{
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [{ "name": "Agent", "class_name": "Agent" }]
},
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["Agent"] }
]
}To run commands against those files, add an execution backend. The
worker-shell backend needs no container and no Docker, so it's the
quickest way to get exec working:
import { withWorkspace, getWorkspace } from "@cloudflare/computer";
import { WorkerShellBackend } from "@cloudflare/computer/backends/worker-shell";
import { DurableObject } from "cloudflare:workers";
export class Agent extends withWorkspace(
class extends DurableObject<Env> {},
(self) => ({
storage: self.ctx.storage,
backends: [
new WorkerShellBackend({
loader: self.env.LOADER,
workspace: { binding: "Agent", id: self.ctx.id.toString() },
ctx: self.ctx,
}),
],
}),
) {}Add the loader binding and the experimental flag to wrangler.jsonc:
{
"compatibility_flags": ["nodejs_compat", "experimental"],
"worker_loaders": [{ "binding": "LOADER" }]
}Now exec runs against the same files your fs calls wrote:
using ws = await getWorkspace(env.Agent.get(id));
await ws.fs.writeFile("/hello.txt", "world");
using run = await ws.runtime.exec("cat /hello.txt");
const { stdout, exitCode } = await run.result();The filesystem
workspace.fs is async, uses absolute paths, and is durable across DO
restarts. Strings default to UTF-8; pass a Uint8Array or a
ReadableStream for binary content.
// Write a string, bytes, or a stream straight to disk.
await ws.fs.writeFile("/notes/todo.md", "- [ ] ship it\n");
await ws.fs.writeFile("/data/blob.bin", new Uint8Array([1, 2, 3]));
await ws.fs.writeFile("/uploads/big.csv", request.body!);
// Read back as a string or as a stream.
const todo = await ws.fs.readFile("/notes/todo.md", "utf8");
const stream = await ws.fs.readFile("/uploads/big.csv");
return new Response(stream);
// Directories.
await ws.fs.mkdir("/notes/daily", { recursive: true });
for (const entry of await ws.fs.readdir("/notes")) {
console.log(entry.isDirectory ? `d ${entry.name}` : `f ${entry.name}`);
}
// Remove and search.
await ws.fs.rm("/notes/daily", { recursive: true });
const hits = await ws.fs.grep("TODO", "/", { ignoreCase: true });Read-only mounts
Pre-fill part of the tree from an R2 bucket. Files under the mount
point are read-only; writes reject with EROFS.
import { R2Bucket } from "@cloudflare/computer";
new Workspace({
storage: ctx.storage,
mounts: { "/workspace/r2": R2Bucket(env.Bucket) },
});Running commands and code
workspace.runtime.exec(source, options) is the single execution entry
point. What source means depends on the backend: a shell command for
the container and worker-shell backends, an ECMAScript module for the
worker-javascript backend.
using run = await ws.runtime.exec("ls -la /workspace", { encoding: "utf8" });
const { stdout, stderr, exitCode } = await run.result();The handle is also a ReadableStream of live events, so you can
forward output as it happens — for example, as Server-Sent Events:
async fetch(request: Request) {
const run = await ws.runtime.exec("npm test", { encoding: "utf8" });
const sse = run.pipeThrough(
new TransformStream({
transform(event, controller) {
const frame = `event: ${event.name}\ndata: ${JSON.stringify(event.value)}\n\n`;
controller.enqueue(new TextEncoder().encode(frame));
},
}),
);
return new Response(sse, {
headers: { "content-type": "text/event-stream", "cache-control": "no-cache" },
});
}Alongside exec, the runtime exposes getExec, killExec, and
disposeExec.
Choosing a backend
| Backend | Import | Runs | Needs |
| Container | @cloudflare/computer/backends/container | Shell commands in full Linux userland (real binaries, npm, node, network) | A Cloudflare Container running computerd |
| Worker shell | @cloudflare/computer/backends/worker-shell | Shell commands via just-bash in a Dynamic Worker | A Worker Loader binding; experimental flag |
| Worker JavaScript | @cloudflare/computer/backends/worker-javascript | ECMAScript modules in a fresh Dynamic Worker | A Worker Loader binding; experimental flag |
- Container cold-starts more slowly but gives you a real Linux environment. The container owns its own SQLite-backed VFS and the package syncs the two stores across a capnweb WebSocket.
- Worker shell is fast and needs no container. Every filesystem operation forwards back to the same Durable Object, so there's no second store and no sync round trip.
- Worker JavaScript evaluates a module with structured
input/results, durable relative imports, configured libraries,
Workspace-backed
node:fs/promises, and trustedws:git/ws:artifactsmodules.
You can register several backends on one Workspace and route each call to a named one — see Multiple backends.
Tools for agents
@cloudflare/computer/tools ships AI SDK tools that wrap the Workspace
surfaces, ready to hand to generateText, streamText, or an agent
framework's getTools(). The default set is read, write, edit,
and ls; exec and publish are added when you configure them.
import { createAITools } from "@cloudflare/computer/tools";
const tools = createAITools({
workspace,
read: { maxBytes: 32 * 1024, maxLines: 800 },
shell: {
defaultBackend: "shell",
backends: {
shell: { description: "Fast Worker shell with built-in text commands." },
container: { description: "Full Linux userland in a Cloudflare Container." },
},
},
});The model reads each backend's description when deciding where a
command should run, so write them in plain language.
Git
workspace.git is an opt-in typed git client backed by
isomorphic-git,
operating directly on the local SQLite VFS — no backend or shell
required. Enable it by passing createGitClient() from
@cloudflare/computer/git:
import { Workspace } from "@cloudflare/computer";
import { createGitClient } from "@cloudflare/computer/git";
const ws = new Workspace({
storage: ctx.storage,
git: createGitClient(),
defaultGitIdentity: { name: "Agent", email: "agent@example.test" },
});
await ws.git.clone({ url: "https://github.com/example/repo.git" });
await ws.fs.writeFile("/notes.md", "hello");
await ws.git.add({ paths: ["notes.md"] });
await ws.git.commit({ message: "add notes" });There's also an argv entry point (workspace.git.cli({ argv })), and
when git is configured the worker-shell backend exposes a built-in
git command.
Sharing files
Two ways to get a file out of the workspace and into the world:
- Assets (
@cloudflare/computer/assets):createAssets(...).shareuploads a workspace file to R2 and returns a presigned URL. Attach it throughWorkspaceOptions.assetsto expose an in-shellassets publish <path> [<expiry>]command. - Artifacts (
@cloudflare/computer/artifacts):createArtifact(binding, sessionId)is a session-scoped facade over the Cloudflare Artifacts binding. Every repository name is implicitly prefixed with the session id, so one namespace hosts many isolated sessions.
import { createArtifact } from "@cloudflare/computer/artifacts";
const artifacts = createArtifact(env.ARTIFACTS, agentId);
const repo = await artifacts.create("build-cache", { description: "CI artifacts" });
const token = await artifacts.createToken("build-cache", "read", 3600);
const mine = await artifacts.list(); // only this session's reposCrossing the Worker → DO boundary
The Durable Object owns the Workspace; a Worker reaches it through a
stub. withWorkspace installs the plumbing, and getWorkspace(stub)
returns a client:
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const id = env.Agent.idFromName("user-123");
using ws = await getWorkspace(env.Agent.get(id));
await ws.fs.writeFile("/notes.md", "hello");
using run = await ws.runtime.exec("ls /workspace");
const { exitCode, stdout } = await run.result();
return new Response(stdout, { status: exitCode === 0 ? 200 : 500 });
},
} satisfies ExportedHandler<Env>;Dispose your stubs
The one gotcha worth internalizing: the RPC layer does not garbage-collect remote stubs. On long-lived sessions undisposed stubs accumulate on the peer until the session ends. The rules are short:
usingthe value fromgetWorkspace(...).usingthe handle fromws.runtime.exec(...).- Don't worry about
ws.fs,ws.runtime, orws.git— they ride with the parent. - Pure-value returns (
readFileas a string,stat,readdir,git.cli({...})) carry no stubs; nothing to dispose.
Short-lived single-shot Workers tear the session down with the request,
so this matters most on long-lived isolates that keep grabbing fresh
stubs or on busy exec workloads. To hunt leaks, set
CAPNWEB_TRACK_STUBS=1 and read stubSnapshot() from
@cloudflare/computer-rpc/debug, or hit GET /__computerd/stubs on a
computerd instance.
Package entrypoints
| Entrypoint | Purpose |
@cloudflare/computer | The Workspace facade, workspace.runtime, stub types, the R2 mount, and proxy classes. |
@cloudflare/computer/backends/container | CloudflareContainerBackend and withWorkspaceContainer. Pulls in the computerd / capnweb sync plumbing. |
@cloudflare/computer/backends/worker-shell | WorkerShellBackend and the bundled just-bash runtime. |
@cloudflare/computer/backends/worker-javascript | WorkerJavaScriptBackend, configured libraries, durable imports, node:fs/promises, and trusted ws:git / ws:artifacts. |
@cloudflare/computer/tools | AI SDK tools for agents: read, write, edit, ls, optional exec and publish. |
@cloudflare/computer/git | Opt-in isomorphic-git glue for checkouts inside the workspace. |
@cloudflare/computer/assets | createAssets — share a workspace file to R2 as a presigned URL. |
@cloudflare/computer/artifacts | createArtifact and its CLI, a session-scoped facade over the Cloudflare Artifacts binding. |
@cloudflare/computer/observe/cloudflare | Cloudflare-runtime adapter for the observability hook. |
A consumer that only uses the container backend never imports the worker
subpaths, so unused backend payloads tree-shake away. Wire types shared
with the in-container service live in the sibling package
@cloudflare/computer-rpc.
Advanced
Multiple backends
A Workspace can carry more than one backend, each registered under a
stable selector id (defaulting to "worker-shell",
"container-shell", or "worker-javascript"). runtime.exec uses the
first backend unless the caller names one:
const ws = new Workspace({
storage: ctx.storage,
backends: [
new WorkerShellBackend({ id: "shell", loader: env.LOADER, /* ... */ }),
new CloudflareContainerBackend({ id: "sandbox", container: () => this, /* ... */ }),
],
});
const grep = await ws.runtime.exec("grep -r TODO /workspace"); // default: "shell"
const build = await ws.runtime.exec("npm test", { backend: "sandbox" });Backends connect lazily — the first exec, push, pull, or
ready(id) for an id dials it. ready({ all: true }) pre-warms every
backend, which is handy from an agent's onStart. Per-backend sync
cursors are independent, so activity on one never disturbs the other.
A backend that accepts a structured input and returns a structured
value declares callable: true (the worker-javascript backend does).
Passing input to a non-callable backend is a clear error rather than a
silent drop.
Constructing without the mixin
withWorkspace is the shortcut. You can also construct a Workspace
directly and expose it through a stub yourself:
const ws = new Workspace({ storage: ctx.storage, backends: [/* ... */] });
await ws.ready();
const stub = ws.stub(); // crosses the Workers-RPC boundaryWhen assigning a workspace to a Think agent's workspace, pass
useThink: true so Think's compatibility methods are added alongside
workspace.fs and workspace.runtime.
Durable pending-sync retries
A command can change backend files and then have its post-command pull
fail; the result exposes sync: { status: "pending", ... }. Configure a
SyncRetryScheduler on Workspace to persist one coalesced retry per
backend, then call workspace.retryPendingSync(backend) from your DO's
alarm. Retries use bounded exponential backoff and return "exhausted"
after the configured maximum. The library does not own your DO's alarm.
Observability
Pass an observer to the Workspace constructor to receive one span
per documented operation (workspace.connect, workspace.sync.push,
workspace.sync.pull, workspace.runtime.exec.spawn,
workspace.fs.<op>). The span(name, attributes, run) shape fits the
Cloudflare runtime's ctx.tracing, OpenTelemetry, or a test recorder;
attribute values are restricted to boolean | number | string. The
default is a zero-cost no-op, so there's no overhead unless you opt in.
Where to read more
- Upstream repo and examples: https://github.com/cloudflare/computer
(
examples/worker-shell,examples/worker-javascript,examples/container,examples/think,examples/tutorial). - Design spec under the repo's
docs/— forward-looking; read it for intent, not as a description of the code today. - For broader Cloudflare platform guidance, load the companion
cloudflare-platform-skillsskill.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

