Skip to content

atharen/openclaw-plugin-opencode

 
 

Repository files navigation

OpenClaw Plugin: OpenCode

Fork of 13rac1/openclaw-plugin-claude-code

This is a community fork that replaces Claude Code with OpenCode — an open-source, provider-agnostic coding agent. While the upstream plugin is locked to Anthropic's Claude Code CLI, this fork works with any of the 75+ LLM providers that OpenCode supports.

See Differences from Upstream for a full comparison.

License Node.js TypeScript

Run OpenCode in secure, isolated containers with any LLM provider.

An OpenClaw plugin that executes OpenCode agent sessions in rootless Podman containers. Let your AI agents delegate complex coding tasks to OpenCode without risking your host system — and without being locked to a single provider.

Table of Contents

Differences from Upstream

This fork is a full architectural rewrite. The tool interface (6 tools, same parameters) is preserved for compatibility, but everything behind it has changed.

Aspect Upstream (Claude Code) This Fork (OpenCode)
Agent Claude Code CLI (claude) OpenCode (opencode serve)
Provider support Anthropic only 75+ providers (Anthropic, OpenAI, Google, local models, etc.)
Communication Parse CLI stdout (stream-json) HTTP API via OpenCode SDK
Container lifecycle One-shot per job (start, run, exit) Long-lived server per session (reused across jobs)
Auth mechanism ~/.claude/.credentials.json or ANTHROPIC_API_KEY ~/.local/share/opencode/auth.json (multi-provider)
Permission bypass --dangerously-skip-permissions CLI flag "permission": "allow" in config (container is the boundary)
Session resume --resume <id> CLI flag SDK session API (create once, send multiple prompts)
Port mapping None (stdout-based) Dynamic host port -> container port 4096
Rate limit handling Regex parsing of stdout errors Handled internally by OpenCode per-provider
Server health N/A (process-based) GET /global/health with exponential backoff
Output capture Stream-json parsing with regex extraction Structured SDK responses written to local log files
Orphan recovery Kill orphaned containers Reconnect to healthy orphaned servers via port readback
Container user claude (UID 1000) opencode (UID 1000)
Tool names claude_code_* opencode_*

Why Fork?

The upstream plugin is tightly coupled to Claude Code's proprietary CLI, its stream-json output format, and Anthropic's authentication. OpenCode is open-source, supports many providers, and has a proper headless server mode with an HTTP API — making it a better fit for a containerized agent architecture.

The long-lived server model also eliminates cold-start overhead for multi-turn sessions: the first job starts the server, and subsequent jobs reuse it instantly.

Why Use This Plugin?

Provider Freedom

Use any LLM provider OpenCode supports — Anthropic, OpenAI, Google, Groq, local models via Ollama, and many more. Configure your provider once with opencode auth login and the plugin handles the rest.

Contain the Blast Radius

OpenCode with "permission": "allow" can modify any file and run any command. This plugin contains each session in a rootless container with:

  • All Linux capabilities dropped
  • Configurable network isolation
  • Memory and CPU limits
  • Optional AppArmor profiles

Bad code, infinite loops, or accidental rm -rf stays inside the container. Your host system remains untouched.

Parallel Workspaces

Run multiple OpenCode sessions simultaneously, each in its own isolated workspace. Sessions maintain state across interactions, enabling complex multi-step workflows without interference.

Long-lived Servers

Unlike the upstream's one-shot model, each session runs a persistent OpenCode server. The first job pays the startup cost; subsequent jobs in the same session get instant responses with no cold start.

Works With Any OpenClaw Agent

Any OpenClaw agent can use the opencode_start tool to offload coding tasks. The orchestrating agent stays lightweight while OpenCode handles the heavy lifting in its own container.

FAQ

Why Podman instead of Docker?

Security. This plugin runs AI-generated code with all permissions allowed — the container is your only safety net. Podman is rootless by default: no daemon, no root process, no privilege escalation path. If an AI agent escapes the container, it lands in an unprivileged user namespace with no capabilities.

Docker's default mode runs a root daemon. A container escape from a root-daemon Docker setup gives the attacker full root access to the host. Docker can run rootless, but it requires additional setup and isn't the default.

The plugin supports runtime: "docker" for users who have configured rootless Docker, but Podman is strongly recommended.

Why use this plugin instead of the coding-agent skill?

The built-in coding-agent skill runs agents directly on your host. This plugin provides containment: each session runs in a rootless Podman container with dropped capabilities, resource limits, and tmpfs isolation.

Use the coding-agent skill when:

  • Quick setup matters more than isolation
  • You're already comfortable with OpenClaw's sandbox mode
  • Tasks are short-lived and don't need session persistence

Use this plugin when:

  • You want real containment for code execution
  • You need persistent sessions that survive across interactions
  • You want structured job management (status, output pagination, activity detection, crash recovery)
  • You're running untrusted or experimental code and need hard resource limits

Features

  • Isolated Execution: Each OpenCode session runs in its own container with dropped capabilities
  • SDK-based Communication: Structured HTTP API instead of fragile stdout parsing
  • Long-lived Servers: One server per session, reused across multiple jobs
  • Provider Agnostic: Works with any provider OpenCode supports (75+)
  • Session Persistence: Sessions maintain state across multiple interactions
  • Resource Limits: Configurable memory, CPU, and PID limits
  • AppArmor Support: Optional AppArmor profile for additional security hardening
  • Automatic Cleanup: Idle sessions are automatically cleaned up
  • Orphan Recovery: Reconnects to healthy orphaned servers on startup instead of killing them

Requirements

  • OpenClaw >= 2025.1.0
  • Podman (recommended) or Docker
  • OpenCode installed locally (for opencode auth login)
  • Node.js >= 22

Installation

From npm

openclaw plugins install @13rac1/openclaw-plugin-opencode

From GitHub Release

Download the latest release zip from GitHub Releases and extract to your plugins directory:

unzip openclaw-plugin-opencode-*.zip -d ~/.openclaw/plugins/openclaw-plugin-opencode

Container Image

The plugin requires a container image with OpenCode installed. Pull the pre-built image:

podman pull ghcr.io/13rac1/openclaw-opencode:latest

Or build it yourself:

podman build -t ghcr.io/13rac1/openclaw-opencode:latest .

Set Up OpenCode Authentication

Before using the plugin, configure OpenCode with your preferred provider:

# Install OpenCode if not already installed
npm install -g opencode-ai

# Login to your provider (interactive)
opencode auth login

This creates credentials at ~/.local/share/opencode/auth.json which the plugin mounts into each container.

Configuration

Add to your openclaw.json:

{
  "plugins": {
    "enabled": true,
    "load": {
      "paths": ["path/to/dist/opencode.js"]
    },
    "entries": {
      "opencode": {
        "enabled": true,
        "config": {
          "image": "ghcr.io/13rac1/openclaw-opencode:latest",
          "runtime": "podman",
          "serverReadyTimeout": 30,
          "idleTimeout": 120,
          "memory": "512m",
          "cpus": "1.0",
          "network": "bridge",
          "sessionsDir": "~/.openclaw/opencode-sessions",
          "workspacesDir": "~/.openclaw/workspaces",
          "sessionIdleTimeout": 3600
        }
      }
    }
  }
}

Configuration Options

Option Type Default Description
image string ghcr.io/13rac1/openclaw-opencode:latest Container image for OpenCode
runtime string podman Container runtime (podman or docker)
serverReadyTimeout number 30 Seconds to wait for OpenCode server to become healthy
idleTimeout number 120 Kill container after this many seconds of no activity
memory string 512m Memory limit for containers
cpus string 1.0 CPU limit for containers
network string bridge Network mode (none, bridge, host). Must be bridge or host for LLM API access.
sessionsDir string ~/.openclaw/opencode-sessions Directory for session metadata
workspacesDir string ~/.openclaw/workspaces Directory for session workspaces
sessionIdleTimeout number 3600 Cleanup idle sessions after this many seconds
apparmorProfile string "" AppArmor profile name (empty = disabled)
maxOutputSize number 10485760 Maximum output size in bytes (10MB default, 0 = unlimited)
notifyWebhookUrl string http://localhost:18789/hooks/agent OpenClaw webhook URL for notifications
hooksToken string "" Webhook auth token (must match OpenClaw hooks.token to enable notifications)

Authentication

OpenCode manages its own multi-provider authentication. The plugin mounts your local OpenCode configuration into each container:

  • ~/.config/opencode/ — provider configuration, model settings
  • ~/.local/share/opencode/ — authentication credentials

Setup

Run opencode auth login once on the host to configure your provider. The plugin will fail with a clear error if ~/.local/share/opencode/auth.json does not exist.

opencode auth login

This supports any provider OpenCode offers: Anthropic, OpenAI, Google, Groq, Mistral, local models, and many more.

Job Completion Notifications

The plugin sends webhook notifications when jobs complete. No polling required.

Setup

Enable webhooks in OpenClaw config (~/.openclaw/openclaw.json):

{
  "hooks": {
    "enabled": true,
    "token": "your-secret-token"
  }
}

The plugin automatically reads the token from the OpenClaw config file. When a job completes, fails, or is cancelled, the agent receives a message with the job status, duration, and output size.

Sandbox Tool Allowlist

If you use OpenClaw's sandbox mode, add the plugin's tools to your sandbox tool allowlist:

{
  "tools": {
    "sandbox": {
      "tools": {
        "allow": [
          "exec",
          "process",
          "read",
          "write",
          "edit",
          "apply_patch",
          "image",
          "sessions_list",
          "sessions_history",
          "sessions_send",
          "sessions_spawn",
          "session_status",
          "opencode_start",
          "opencode_status",
          "opencode_output",
          "opencode_cancel",
          "opencode_cleanup",
          "opencode_sessions"
        ]
      }
    }
  }
}

Registered Tools

opencode_start

Start an OpenCode task in the background. Returns immediately with a job ID.

Parameters:

  • prompt (required): The task or prompt to send to OpenCode
  • session_id (optional): Session ID to continue a previous session

Returns: { jobId, sessionKey, status: "running" }

opencode_status

Check the status of a running or completed job.

Parameters:

  • job_id (required): Job ID returned from opencode_start
  • session_id (optional): Session ID

Returns:

  • status: Job status (pending, running, completed, failed, cancelled)
  • elapsedSeconds: Time since job started
  • outputSize: Total output size in bytes
  • tailOutput: Last ~500 chars of output (for quick preview)
  • lastOutputSecondsAgo: Seconds since last output was produced
  • activityState: "active" (producing output), "processing" (CPU busy), or "idle"
  • metrics: CPU and memory usage
  • exitCode: Process exit code (when completed)
  • error: Error message (if failed)

opencode_output

Read output from a job. Supports reading partial output while job is running.

Parameters:

  • job_id (required): Job ID
  • session_id (optional): Session ID
  • offset (optional): Byte offset to start reading from (for pagination)
  • limit (optional): Maximum bytes to read (default 64KB)

Returns: Output content with hasMore flag for pagination

opencode_cancel

Cancel a running job. Attempts to abort via the OpenCode API first, then kills the container.

Parameters:

  • job_id (required): Job ID
  • session_id (optional): Session ID

opencode_cleanup

Clean up idle sessions and their containers.

Parameters:

  • delete_workspaces (optional, boolean): Also delete workspace data (code, files, git history). Default false — only session metadata is removed.

opencode_sessions

List all active sessions with age, last activity, message count, server health, and active job info.

Security

The plugin implements multiple layers of security:

  1. Rootless Containers: Uses Podman rootless mode by default
  2. Capability Dropping: All Linux capabilities are dropped (--cap-drop ALL)
  3. Resource Limits: Memory, CPU, and PID limits prevent resource exhaustion
  4. tmpfs: /tmp is mounted as tmpfs with nosuid (512MB, exec allowed for compilers)
  5. Network Isolation: Configurable network mode (can be set to none for fully offline tasks, but LLM API calls require bridge)
  6. AppArmor: Optional AppArmor profile support for MAC enforcement
  7. User Namespace Mapping: --userns=keep-id:uid=1000,gid=1000 maps the host user to the container's opencode user

Development

Setup

git clone https://github.com/atharen/openclaw-plugin-opencode.git
cd openclaw-plugin-opencode
npm install

Build

npm run build

Test

# Unit tests (mocked)
npm test

# Integration tests (requires Podman + container image)
npm run test:integration

# All tests
npm run test:all

# Watch mode
npm run test:watch

Local Development

Link the plugin for development:

openclaw plugins install -l ./path/to/openclaw-plugin-opencode

Test Harness

Run the test harness against a real container (requires OpenCode auth configured):

npx tsx src/test-harness.ts

Container Image

The included Dockerfile creates a Debian Bookworm-based image with:

  • Node.js 22
  • OpenCode CLI (opencode-ai npm package)
  • Go 1.22.5 + TinyGo 0.32.0
  • Python 3 with venv
  • Common dev tools: git, ripgrep, fd-find, jq, curl, sqlite3

The image runs opencode serve --port 4096 --hostname 0.0.0.0 by default, with a baked-in config that sets "permission": "allow" (the container is the security boundary).

Building

# Single architecture (current platform)
podman build -t ghcr.io/13rac1/openclaw-opencode:latest .

# Multi-architecture (arm64 + amd64)
GITHUB_USERNAME=13rac1 ./scripts/build-and-push.sh --multi-arch

Releasing

Releases are automated via GitHub Actions when a version tag is pushed.

Prerequisites

  1. Configure NPM_TOKEN secret in GitHub repository settings
  2. Ensure you have push access to the repository

Release Process

  1. Update version in package.json
  2. Update CHANGELOG.md with release notes
  3. Commit the changes:
    git add package.json CHANGELOG.md
    git commit -m "chore: release v2.x.x"
  4. Create and push a version tag:
    git tag v2.x.x
    git push origin main --tags

The release workflow will automatically:

  • Run tests
  • Publish to npm with provenance
  • Build and push multi-arch container images to ghcr.io

Troubleshooting

Container image not found

Error: Container image not found: ghcr.io/13rac1/openclaw-opencode:latest

Pull or build the container image:

podman pull ghcr.io/13rac1/openclaw-opencode:latest

OpenCode not configured

Error: OpenCode not configured. Run 'opencode auth login' first to set up provider credentials.

Install OpenCode and authenticate with your provider:

npm install -g opencode-ai
opencode auth login

Server ready timeout

Error: OpenCode server did not become healthy within 30 seconds on port 12345

The OpenCode server inside the container failed to start. Check:

  • Container image is valid and built correctly
  • Sufficient system resources (memory, CPU)
  • Network connectivity (if network: bridge)
  • Container logs: podman logs opencode-<session-key>

Increase serverReadyTimeout if needed.

Idle timeout

Error: idle_timeout - No activity for 120 seconds

OpenCode stopped producing output or consuming CPU. This may indicate:

  • Task completed but output wasn't captured
  • The agent is stuck
  • Task requires more time (increase idleTimeout)

Git identity not configured

Error: Git identity not configured. Set it before using OpenCode:
  git config --global user.name "Your Name"
  git config --global user.email "you@example.com"

The plugin injects your host git identity into containers. Configure it before starting jobs.

Upstream

This project is a fork of 13rac1/openclaw-plugin-claude-code (Apache-2.0). The upstream plugin runs Anthropic's Claude Code CLI; this fork replaces it with OpenCode for provider-agnostic operation and SDK-based communication.

License

Apache-2.0 — see LICENSE

About

OpenClaw Plugin - Run OpenCode in Podman/Docker containers using your Pro/Max subscription

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • TypeScript 95.6%
  • Shell 2.3%
  • JavaScript 1.2%
  • Dockerfile 0.9%