A community-driven registry for Claude, Cursor, Windsurf, Cline & more. Not affiliated with Anthropic.
Are you the author? Sign in to claim
WebSocket bridge for CLI AI agents. Stream responses from Claude Code and Codex CLI over WebSocket. A dumb pipe: no prom
WebSocket bridge for CLI AI agents. Stream responses from Claude Code and Codex CLI over WebSocket. A dumb pipe: no prompt engineering, no credential handling, just transport.
npm install -g @anthropic-ai/claude-code)npm install -g @openai/codex)# From npm
npm install -g agent-ws
# Or run directly
npx agent-ws
# Or clone and run locally
git clone https://github.com/Lisovate/agent-ws.git
cd agent-ws
npm install
npm run build
npm start
# Start the WebSocket bridge
agent-ws
# The server prints a one-time auth token on startup:
# Auth token: <token>
# Connect: ws://localhost:9999?token=<token>
If you want to embed the WebSocket server into your own Node.js backend (e.g. Express, Fastify, or a Next.js API route) instead of running the CLI:
import { AgentWS } from "agent-ws";
import { randomBytes } from "node:crypto";
const agent = new AgentWS({
port: 9999,
host: "localhost",
mode: "agentic", // safe (default) | agentic | unrestricted
authToken: randomBytes(32).toString("hex"), // omit only for trusted environments
agentName: "my-app", // identity in connected messages
sessionDir: "my-sessions", // temp directory name for sessions
});
await agent.start();
Note: This is server-side only. Browser/React clients should connect to the running agent-ws server as a WebSocket client (see Protocol).
-p, --port <port> WebSocket server port (default: 9999)
-H, --host <host> WebSocket server host (default: localhost)
-m, --mode <mode> Permission mode: safe, agentic, unrestricted (default: safe)
--sandbox <preference> Sandbox backend: auto, none, os, seatbelt, bwrap (default: none)
-c, --claude-path <path> Path to Claude CLI (default: claude)
--codex-path <path> Path to Codex CLI (default: codex)
-t, --timeout <seconds> Process timeout in seconds (default: 600)
--no-auth Disable auth token (allows any application to connect)
--log-level <level> Log level: debug, info, warn, error (default: info)
--origins <origins> Comma-separated allowed origins
-V, --version Output version number
-h, --help Display help
Control what the CLI agents can do with --mode:
agent-ws --mode safe # Default — text generation only
agent-ws --mode agentic # File operations (read/write/edit)
agent-ws --mode unrestricted # Full system access — shell, network, everything
| Mode | Claude CLI flags | Codex CLI flags | Capabilities |
|---|---|---|---|
safe | --max-turns 1 --tools "" | --sandbox read-only --ask-for-approval never | Text only, no tools |
agentic | --permission-mode dontAsk --allowedTools "Read(**),Write(**),Edit(**),Glob(**),Grep(**)" | --sandbox workspace-write --ask-for-approval never | File ops only, no shell/network |
unrestricted | --dangerously-skip-permissions | --sandbox danger-full-access --ask-for-approval never | Everything |
Choosing a mode:
safe when you only need text responses (Q&A, code generation without file access)agentic when the CLI needs to read/write project files but shouldn't run arbitrary commandsunrestricted only in trusted, isolated environments where full system access is acceptableIn agentic and unrestricted modes, both runners inject sandbox instructions requiring all file operations to use relative paths. Claude also writes a CLAUDE.md to the session directory reinforcing this constraint.
--mode controls what the CLI agent is asked to do. --sandbox controls what the spawned process can actually do at the OS level. Treat the two as belt-and-braces — flags can be ignored or bypassed; an OS sandbox enforces the boundary at the kernel.
agent-ws --sandbox none # Default. No OS isolation; trust the CLI's own flags.
agent-ws --sandbox os # Pick the best OS-native sandbox for this platform.
agent-ws --sandbox auto # Same as os, but fall back to none with a warning instead of erroring.
agent-ws --sandbox seatbelt # macOS Seatbelt (sandbox-exec). Errors if unavailable.
agent-ws --sandbox bwrap # Linux bubblewrap. Errors if unavailable.
| Backend | Platform | Mechanism | Notes |
|---|---|---|---|
none | all | no-op | Same behavior as before this flag existed |
seatbelt | macOS | /usr/bin/sandbox-exec with curated SBPL profile | Apple-deprecated but still functional. Used by Claude Code, Gemini CLI, and most published agent setups today. |
bwrap | Linux | bubblewrap mount namespaces + --unshare-all --share-net | Requires unprivileged user namespaces. Ubuntu 24.04+ ships with these restricted; see Troubleshooting. |
What the sandbox restricts:
safe mode also blocks session-dir writes)~/.claude, ~/.codex and their ~/.config/... equivalents) are mounted read-only so the CLI can authenticate but not exfiltratesafe mode (except for a tiny shell whitelist the CLI needs to fork its own helpers)Honest caveats:
seatbelt profiles need maintenance per macOS minor release and Apple may remove sandbox-exec entirely (no announced date).bwrap cannot restrict outbound network by hostname (only by port). The Linux bwrap profile permits all outbound traffic in v1; pair with an external egress proxy if you need hostname allowlisting.bwrap backend instead.none so existing setups keep working. Opt in explicitly with --sandbox os and report breakage.Clients can query what's actually available on the server at runtime:
// Client → Agent
{ "type": "capabilities" }
// Agent → Client
{
"type": "capabilities",
"agent": "agent-ws",
"version": "1.1",
"mode": "agentic",
"sandbox": { "active": "seatbelt", "available": ["none", "seatbelt"] },
"providers": [
{ "id": "claude", "available": true, "version": "2.1.141" },
{ "id": "codex", "available": false }
]
}
The reply is cached for the lifetime of the server, so CLI installations after startup require an agent-ws restart to be reflected.
┌───────────────┐ WebSocket ┌─────────────┐ stdio ┌─────────────┐
│ Your App │ <=================> │ agent-ws │ <===============> │ Claude Code │
│ (any client) │ localhost:9999 │ (Node.js) │ stdio │ / Codex │
└───────────────┘ └─────────────┘ └─────────────┘
Any WebSocket client can connect — browser frontends, backend services, scripts, other CLI tools. Each connection gets its own CLI process. The agent:
| Agent | Provider field | CLI |
|---|---|---|
| Claude Code | "claude" (default) | claude --print --verbose --output-format stream-json |
| Codex | "codex" | codex --json |
{ "type": "prompt", "prompt": "Build a login form", "requestId": "uuid", "model": "opus", "provider": "claude" }
{ "type": "prompt", "prompt": "...", "requestId": "uuid", "projectId": "my-app", "systemPrompt": "...", "thinkingTokens": 2048 }
{ "type": "prompt", "prompt": "Describe this image", "requestId": "uuid", "images": [{ "media_type": "image/png", "data": "<base64>" }] }
{ "type": "cancel", "requestId": "uuid" }
{ "type": "capabilities" }
| Field | Required | Description |
|---|---|---|
prompt | yes | The prompt text (max 512KB) |
requestId | yes | Unique request identifier (max 256 chars) |
model | no | Model name (e.g. "sonnet", "opus") |
provider | no | "claude" (default) or "codex". Unknown values are rejected. |
projectId | no | Scopes CLI session by directory. Enables --continue for multi-turn. Alphanumeric, hyphens, underscores, dots only. |
systemPrompt | no | Appended as system prompt (max 64KB). Cached per-connection: if omitted on follow-up messages, the last value is reused. |
thinkingTokens | no | Max thinking tokens. 0 disables thinking. Omit to let Claude decide. Codex ignores this field. |
images | no | Array of { media_type, data } objects. Up to 4 images, max 10MB base64 each. Supported types: image/png, image/jpeg, image/gif, image/webp. |
files | no | Array of { path, content } objects. Up to 100 files, max 50MB total. Written to session directory for Claude to read/edit. |
{ "type": "connected", "version": "1.1", "agent": "agent-ws", "mode": "safe" }
{ "type": "capabilities", "agent": "agent-ws", "version": "1.1", "mode": "safe", "sandbox": { "active": "none", "available": ["none"] }, "providers": [{ "id": "claude", "available": true, "version": "2.1.141" }, { "id": "codex", "available": false }] }
{ "type": "chunk", "content": "Here's a login form...", "requestId": "uuid" }
{ "type": "chunk", "content": "Let me think...", "requestId": "uuid", "thinking": true }
{ "type": "tool_event", "requestId": "uuid", "event": "start", "toolName": "Write", "toolId": "toolu_01" }
{ "type": "tool_event", "requestId": "uuid", "event": "complete", "toolName": "Write", "toolId": "toolu_01", "input": { "file_path": "src/App.tsx", "content": "..." } }
{ "type": "file_change", "requestId": "uuid", "path": "src/App.tsx", "changeType": "create", "content": "..." }
{ "type": "complete", "requestId": "uuid" }
{ "type": "error", "message": "Process timed out", "requestId": "uuid" }
Chunks with thinking: true contain Claude's reasoning. Clients can display these as a thinking indicator or ignore them.
tool_event and file_change are emitted in agentic and unrestricted modes when the CLI agent invokes file-modifying tools. Clients that don't care about live tool activity can ignore them. Edit tools fire two file_change events: a synchronous one (no content) when the edit starts, and an async one with post-edit content read from disk.
Common error messages:
| Message | When it fires |
|---|---|
Invalid JSON | Malformed message |
Request already in progress | Prompt sent while another is running |
Process timed out | CLI exceeded --timeout |
Runner has been disposed | Connection was cleaned up |
<Agent> CLI exited with code N | CLI process failed |
Request cancelled | Cancel message received |
Server is shutting down | Graceful shutdown in progress |
agent-ws generates a random auth token on every startup. Clients must include it as a query parameter:
ws://localhost:9999?token=<token>
This prevents other websites or applications from connecting to your local agent-ws instance. Without this, any page you visit could open a WebSocket to localhost:9999 and execute commands on your machine (browsers don't enforce CORS on WebSocket connections).
To disable authentication (e.g. for local development/testing):
agent-ws --no-auth
When using the library API, pass authToken in options:
import { AgentWS } from "agent-ws";
import { randomBytes } from "node:crypto";
const token = randomBytes(32).toString("hex");
const agent = new AgentWS({ authToken: token });
await agent.start();
--no-auth)--mode safe restricts to text-only responses with no tool access--sandbox os wraps every spawned CLI in Seatbelt (macOS) or bubblewrap (Linux). See Sandboxing for guarantees and caveats.localhost by default--origins flag restricts allowed originsnpm install # Install dependencies
npm run build # Build TypeScript
npm test # Run tests
npm run typecheck # Type check
npm start # Start from built output
src/
├── index.ts # Barrel export (library entry point)
├── cli.ts # CLI entry point (Commander)
├── agent.ts # Orchestrator: wires server + logger
├── server/
│ ├── websocket.ts # WebSocket server, heartbeat, per-connection state, capabilities handshake
│ └── protocol.ts # Message types, validation, PROTOCOL_VERSION
├── process/
│ ├── base-runner.ts # Abstract BaseRunner: spawn/kill/timeout scaffolding, file-write sandbox
│ ├── claude-runner.ts # ClaudeRunner (extends BaseRunner): wires the parser, post-edit reads
│ ├── claude-stream-parser.ts # Stateless parser for Claude's stream-json NDJSON output
│ ├── codex-runner.ts # CodexRunner (extends BaseRunner): JSONL parsing, thread resumption
│ ├── output-cleaner.ts # ANSI stripping via node:util
│ └── sandbox/
│ ├── index.ts # selectSandbox factory + probeAvailableSandboxes
│ ├── types.ts # Sandbox interface and SandboxSpawnOpts
│ ├── noop.ts # NoopSandbox (default — no isolation)
│ ├── seatbelt.ts # macOS sandbox-exec backend + buildSeatbeltProfile
│ └── bwrap.ts # Linux bubblewrap backend + buildBwrapArgs
└── utils/
├── logger.ts # Pino logger factory
└── claude-check.ts # CLI availability probe
Make sure Claude Code is installed:
npm install -g @anthropic-ai/claude-code
claude --version
agent-ws no longer hard-requires Claude — it'll start with only Codex (or vice versa). The capabilities handshake reports which providers are actually usable.
Another instance might be running. Kill it or use a different port:
agent-ws --port 9998
--sandbox=bwrap requested but unavailable: unprivileged user namespaces are restrictedUbuntu 24.04+ disables unprivileged user namespaces by default. Either:
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 # ephemeral
# or persist:
echo "kernel.apparmor_restrict_unprivileged_userns=0" | sudo tee /etc/sysctl.d/90-agent-ws.conf
Or install an AppArmor profile that whitelists the bwrap binary path. agent-ws falls back to none (with a startup warning) when you ask for --sandbox auto on a host where bwrap is blocked.
sandbox-exec is deprecated warnings on macOSApple has marked sandbox-exec as deprecated but no removal date is announced and no replacement exists for sandboxing non-bundled binaries. Every published agent sandbox setup uses it today. The warning is cosmetic — see Sandboxing for the trade-offs.
MIT
1000+ skills curated from Anthropic, Vercel, Stripe, and other engineering teams
Claude Code skill for YouTube creators — channel audits, video SEO, retention scripts, thumbnails, content strategy, Sho
Design enforcement with memory — keeps your UI consistent across a project
AI image generation skill for Claude Code -- Creative Director powered by Gemini