Skip to main content
AI & Automation

AI Workbench & Forge Relay

AI coding workbench with 4 CLI tools, smart dispatcher, xterm.js terminals, Forge Relay API, and OpenClaw orchestration. Primary on Io CT 126, fallback on Titan VM 160.

February 26, 2026 Updated March 1, 2026

AI Workbench & Forge Relay

The Workbench is the admin coding environment at /admin/workbench. It provides browser-based access to 4 AI coding CLIs, a smart dispatcher that routes tasks based on subscription capacity, and OpenClaw agentic orchestration.

Architecture

Browser (xterm.js)
    │ WebSocket (wss://workbench-term.argobox.com)
    ▼
CF Tunnel (argobox-lite) ──── cloudflared
    │
ttyd (7681) ──── forge-attach.sh ──── tmux session
    │
Forge Relay v2 (7682) ──── tmux send-keys / capture-pane
    │ (https://workbench-relay.argobox.com)
    ▼
Astro API (/api/admin/forge) ──── relayFetch()
    │
OpenClaw orchestration ──── 8 Forge tools ──── agentic loop

Hosting

Primary: CT 126 workbench-io on Io (10.0.0.126)

  • Alpine 3.23, 512 MB RAM, 2 cores
  • On the local Jove network — always reachable from argobox-lite (CF tunnel host)
  • Services: ttyd (7681), Forge Relay (7682)
  • OpenRC init scripts: workbench-ttyd, workbench-relay

Fallback: VM 160 on Titan (192.168.50.160)

  • Full workbench with dispatcher, AI tool CLIs, git worktrees
  • On the remote Kronos network — requires inter-site routing (can be flaky)
  • To switch: update tunnel config on argobox-lite, restart cloudflared
Component Purpose
ttyd (7681) WebSocket terminal — bridges browser xterm.js to tmux via forge-attach.sh
Forge Relay v2 (7682) Hono.js REST API wrapping tmux programmatic control (Bearer auth)
Dispatcher (8096) FastAPI smart router, usage tracking (SQLite), git worktree management (Titan only)
tmux 5 standard sessions (forge-*) + dynamic sessions (wb-*)

Cloudflare Tunnels

Hostname Current Target Purpose
workbench-term.argobox.com http://10.0.0.126:7681 (Io) Browser WebSocket terminal
workbench-relay.argobox.com http://10.0.0.126:7682 (Io) Forge Relay API

Tunnel runs on argobox-lite (10.0.0.199), config: /home/argonaut/.cloudflared/config.yml

Switching Between Io and Titan

Edit /home/argonaut/.cloudflared/config.yml on argobox-lite:

# Io (primary — local network, reliable)
- hostname: workbench-term.argobox.com
  service: http://10.0.0.126:7681
- hostname: workbench-relay.argobox.com
  service: http://10.0.0.126:7682

# Titan (fallback — remote network, can be unreachable)
# service: http://192.168.50.160:7681
# service: http://192.168.50.160:7682

Then restart: ssh argonaut@10.0.0.199 "sudo systemctl restart cloudflared"

AI Coding CLIs

Tool Binary Version Auth Rate Limit
Claude Code claude 2.1.63 OAuth (copied from workstation) ~900 msgs/5hr (Max) or ~45/5hr (Pro)
Codex CLI codex 0.106.0 Needs codex login ~150 msgs/5hr (Plus)
OpenCode opencode 1.2.15 BYOK (API keys) Unlimited
Kilo Code kilocode 7.0.33 BYOK (API keys) Unlimited

tmux Sessions

Session Purpose
forge-shell General bash shell
forge-claude Claude Code CLI
forge-codex Codex CLI
forge-opencode OpenCode CLI
forge-kilocode Kilo Code CLI

Sessions auto-created by init-sessions.sh at ttyd startup and healthcheck. Dynamic wb-* sessions created by the dispatcher for automated tasks.

Workbench Modes

Terminal Mode

5 xterm.js terminals in browser tabs, each connecting to a tmux session via ttyd WebSocket. Lazy-connect on tab switch (only the active tab opens a WebSocket). Reconnect button for dropped connections.

WebSocket URL pattern:

  • Local dev: ws://10.0.0.126:7681/ws?arg=forge-shell
  • Production: wss://workbench-term.argobox.com/ws?arg=forge-shell

Agent Mode (Forge Dispatch)

Dispatch form sends tasks to AI tools. The buildCliCommand() function maps tool names to CLI invocations:

  • claude "instruction" (interactive) or claude --print "instruction" (headless)
  • claude --agent build-goalie "instruction" (agent-routed, see below)
  • codex --approval-mode suggest "instruction" (interactive)
  • opencode run "instruction" (headless) or opencode (interactive TUI)
  • kilocode run "instruction" (headless) or kilocode (interactive TUI)

Session grid shows status of tmux sessions. Activity log tracks dispatched commands.

Telegram → OpenClaw → Agent Pipeline

The Forge system can be driven remotely via Telegram through OpenClaw. The full flow:

Telegram (@argobox_oc_bot)
  → OpenClaw Gateway (argobox-lite:18789)
  → LLM reads AGENTS.md (includes agent routing table)
  → LLM calls forge_dispatch(tool="claude", agent="build-goalie", instruction="...", mode="headless")
  → ArgoBox API POST /api/admin/forge (action: dispatch)
  → forge.ts: ensureSession() → buildCliCommand() → relayFetch() with retry
  → Forge Relay POST /sessions/forge-claude/command
  → tmux: claude --print --agent build-goalie "instruction"
  → Output captured via forge_capture("forge-claude")
  → Result returned to Telegram

Claude Code Agents

When dispatching to Claude Code (tool: "claude"), the agent parameter routes to specialized agents defined in .claude/agents/:

Agent Shorthand Trigger Phrases Model Description
build-goalie tcg "check build", "build broken", "health check" Haiku (cheap) 3-level diagnostic: static scan → build check → runtime probe
module-fixer tcf "fix [X] module", "module broken" Opus Diagnose, fix, test, document one module at a time
v2-architect tca "work on v2", "port to v2" Opus Clean V2 rebuild

Codex Approval Modes

When dispatching to Codex (tool: "codex"), the approval parameter controls autonomy:

Mode Description
suggest (default) Suggests changes, waits for approval
auto-edit Auto-applies edits, asks before running commands
full-auto Fully autonomous, no approval needed

Resilience

  • Auto-session creation: If forge-claude or forge-codex sessions don't exist, ensureSession() creates them before dispatching
  • Retry with backoff: 3 attempts at 1s/2s/4s intervals for 5xx errors and network failures
  • Actionable errors: Error responses include hints about which service is down

Chat Mode

Streaming chat via the unified-chat endpoint. Conversation management stored in localStorage.

Smart Dispatcher (port 8096)

When tool: "auto", the dispatcher scores tools:

score = (remaining_capacity / rate_limit) * (1 / priority)
Priority Tool Strengths
1 (highest) Claude Code Architecture, refactoring, complex, debugging
2 Codex CLI Quick fixes, tests, simple features
3 OpenCode Multi-model, batch
4 (fallback) Kilo Code Automation, batch

Subscription tools first, BYOK fallback. Usage tracked in SQLite with 5-hour rolling windows.

API Routes

/api/admin/forge (forge.ts)

Proxies to Forge Relay via FORGE_RELAY_URL (through Cloudflare tunnel in production).

Action Description
dispatch Send CLI command to a tool's tmux session
send Send raw keystrokes to a session
interrupt Send Ctrl+C to a session
capture Read terminal output from a session
sessions List all tmux sessions

/api/admin/forge-git (forge-git.ts)

Action Description
create-branch Create branch via Gitea API
create-pr Create pull request via Gitea API
commit-via-relay Stage + commit via relay
push-via-relay Push to remote via relay
pipeline Full auto: branch + push + PR

/api/admin/openclaw (openclaw.ts)

OpenClaw agentic orchestration with 8 Forge tools (dispatch, status, capture, interrupt, git branch/commit/push/pr). Creates a ForgeExecutor that calls Gitea and Forge Relay APIs directly.

Dispatcher API (port 8096)

Method Path Purpose
POST /api/task Submit task (repo, prompt, tool, commit, push, create_pr, timeout)
GET /api/task/{id} Task status + output
GET /api/tools Tool capacity + availability
GET /api/repos List cloned repos
POST /api/repos Clone new repo from Gitea

Environment Variables

Variable Location Purpose
FORGE_RELAY_URL CF Pages env, local .env https://workbench-relay.argobox.com
FORGE_RELAY_SECRET CF Pages env, relay OpenRC service Bearer token for relay auth
WORKBENCH_API_TOKEN VM dispatcher service, OpenClaw .env Dispatcher API auth
GITEA_API_TOKEN CF Pages env Gitea API for git operations
OPENCLAW_API_TOKEN CF Pages env OpenClaw gateway auth
GITEA_TOKEN VM dispatcher service Git push auth

Files

VM 160 (/opt/workbench/)

File Purpose
forge-relay/server.js Hono.js REST API for tmux control (v2)
dispatcher/main.py FastAPI smart router, usage tracking, git worktrees
forge-attach.sh ttyd session attachment (reads ?arg= URL param)
init-sessions.sh Creates forge-* tmux sessions if missing
healthcheck.sh Auto-heal script (root cron every 2min)
status.sh Diagnostic dashboard (wb status)
repos/*.git Bare git clones (auto-fetch every 5min)

ArgoBox Astro

File Purpose
src/pages/admin/workbench.astro Full workbench UI (5000+ lines)
src/pages/api/admin/forge.ts Forge API route + relayFetch() proxy
src/pages/api/admin/forge-git.ts Git pipeline API route
src/pages/api/admin/openclaw.ts OpenClaw orchestration API
src/lib/forge-client.ts Browser-side Forge API client
src/lib/tool-router.ts Heuristic tool selection engine
src/config/modules/workbench.ts Module manifest

CT 126 workbench-io (/opt/workbench/)

File Purpose
forge-attach.sh tmux session attachment script for ttyd
init-sessions.sh Creates the 5 standard forge-* sessions
forge-relay/server.js Hono.js REST API (285 lines)
forge-relay/package.json Dependencies: hono, @hono/node-server

OpenRC services: /etc/init.d/workbench-ttyd, /etc/init.d/workbench-relay Logs: /var/log/workbench/ttyd.log, /var/log/workbench/relay.log

Source Repository

~/Development/argobox-workbench/ → Gitea: ArgoBox/argobox-workbench

Troubleshooting

Terminal stuck on "Connecting to forge-shell..."

Quick checks (in order):

  1. Is the container running?

    ssh root@10.0.0.200 "pct exec 126 -- rc-service workbench-ttyd status"
    
  2. Are tmux sessions alive?

    ssh root@10.0.0.200 "pct exec 126 -- tmux ls"
    

    If no sessions: ssh root@10.0.0.200 "pct exec 126 -- bash /opt/workbench/init-sessions.sh"

  3. Can argobox-lite reach the container?

    ssh argonaut@10.0.0.199 "curl -s -o /dev/null -w '%{http_code}\n' http://10.0.0.126:7681/"
    

    Should return 200.

  4. Is the CF tunnel routing correctly?

    curl -s -o /dev/null -w '%{http_code}\n' "https://workbench-term.argobox.com/"
    

    Should return 200. If 502, the tunnel can't reach the container.

  5. Does the WebSocket connect?

    cd ~/Development/argobox && node -e "
    const ws = new (require('ws'))('wss://workbench-term.argobox.com/ws?arg=forge-shell', {handshakeTimeout:5000});
    ws.on('open', () => { console.log('OK'); ws.close(); process.exit(0); });
    ws.on('error', (e) => { console.log('FAIL:', e.message); process.exit(1); });
    setTimeout(() => { console.log('TIMEOUT'); process.exit(1); }, 6000);
    "
    
  6. Browser-specific checks:

    • Hard refresh: Ctrl+Shift+R (bypass cache after CF Pages deploy)
    • Disable ad blocker for argobox.com (can block WebSocket subdomains)
    • Check browser console for WebSocket errors (filter "ws" or "websocket")
    • Try incognito/private window

Restart services on CT 126

ssh root@10.0.0.200 "pct exec 126 -- rc-service workbench-ttyd restart"
ssh root@10.0.0.200 "pct exec 126 -- rc-service workbench-relay restart"

Restart cloudflared

ssh argonaut@10.0.0.199 "sudo systemctl restart cloudflared"

Known issues

Issue Cause Fix
CORS error in console from checkTermHost() ttyd doesn't send CORS headers Code uses no-cors fetch — opaque response is expected. Console error is cosmetic if using old cached code. Hard refresh.
ERR_BLOCKED_BY_CLIENT Ad blocker blocking a CF-hashed JS chunk Disable ad blocker or add exception for argobox.com
WebSocket works from CLI but not browser Browser cache serving old code before CORS fix Hard refresh (Ctrl+Shift+R), wait for CF Pages deploy to complete
502 from CF tunnel Container down or tunnel misconfigured Check steps 1-4 above

Connection flow (browser)

1. checkTermHost() — no-cors fetch to https://workbench-term.argobox.com/
   - Opaque response (status 0) = reachable
   - Throws (AbortError/TypeError) = unreachable → show error overlay
2. connectTerminal(tab) — new WebSocket('wss://workbench-term.argobox.com/ws?arg=forge-shell')
   - 8-second connection timeout
   - On open: send resize, show terminal
   - On message: ttyd binary protocol (type byte + payload)
   - On close/error: show reconnect overlay, reset reachability cache
workbenchforgeclaude-codecodexopencodekilocodetmuxttydterminalioct126titanvm160