agent-bus-cli
v0.5.5
Published
Self-hosted remote-assistant CLI for routing AI agents, model gateways, and AI-to-AI rooms across machines.
Maintainers
Readme
Agent Bus
A lightweight distributed agent and OpenAI-compatible model router for connecting Codex, Hermes, OpenClaw, and custom model gateways across machines.
Agent Bus is also an early AI-to-AI protocol surface: agents can discover each other, advertise capabilities, report shallow health, receive tasks, and coordinate inside shared rooms.
There are three entrypoint families:
server.mjs: the original SSH-based prototype.central-gateway.mjs+edge-node.mjs: the preferred Node.js gateway/edge architecture where each machine connects outward to the central gateway and runs local adapters.central_gateway.py+edge_node.py: the same gateway/edge protocol for machines without Node.js.
The core entrypoints intentionally have no npm runtime dependencies.
What Agent Bus Gives You
Agent Bus is a self-hosted remote-assistant CLI for making AI tools addressable across machines. It is designed for contributors and operators who want a small, auditable bus rather than a monolithic agent platform.
- Remote assistant nodes: keep Codex, Hermes, OpenClaw, Ollama, or shell adapters on private machines that connect outbound to a gateway.
- AI-to-AI rooms: let agents coordinate with
@agent-id,REPORT,BLACKBOARD,WAKE, andDONEdirectives instead of copying context by hand. - OpenAI-compatible routing: expose selected model aliases behind one authenticated gateway.
- Zero-dependency core: the Node.js and Python gateway/edge entrypoints use only standard libraries.
- Offline verification:
agent-bus smoke --offlinevalidates the packaged room path without model calls or external services.
Start with docs/remote-assistant-quickstart.md for the first remote node, docs/cli.md for CLI setup, docs/ai-to-ai.md for the room protocol, docs/trust-boundaries.md plus SECURITY.md for trust boundaries, CONTRIBUTING.md for contributor workflow, docs/good-first-issues.md for starter tasks, and CHANGELOG.md for release highlights.
Quick Start
Run the local smoke test:
npm run smokeRun a local demo from the installed CLI or checkout:
# Show AI-to-AI room delegation and export a share-safe report.
agent-bus demo room
npm run demo:room
# Pair a local edge and send a normal task.
agent-bus demo local
npm run demo:remote-assistantInstall the CLI from npm or from a checkout:
npm install -g agent-bus-cli
agent-bus --help
agent-bus smoke --offline
# contributor checkout install
npm install -g .
agent-bus smoke --offlineOr download a portable bundle from GitHub Releases. The bundle includes launchers for Windows and Unix-style shells, a manifest, and SHA-256 checksums. It still only requires Node.js 20+.
Release operators should follow the release checklist for the npm vs portable install matrix, checksum expectations, tag workflow, post-publish smoke tests, and release-note wording. See CHANGELOG.md for the current public release highlights.
Contributors can verify package and install paths before publishing or tagging:
npm run release:check
npm run pack:check
npm run portable:check
npm run smoke:npm-install -- --package . # pre-publish checkout install path
npm run smoke:npm-install # post-publish registry version from package.json
npm run bundle -- --archiveUse a machine as a remote assistant node:
agent-bus detect
agent-bus init edge --auto --out edge.config.json
# edit gatewayUrl, edge/admin token, pingUrl, and runCommand if needed
agent-bus doctor --config edge.config.json # add --json for CI/setup automation
agent-bus connect --config edge.config.jsonFor the full two-machine path, see Remote Assistant Quickstart.
Check live reachability and room activity from the operator machine:
agent-bus nodes --gateway https://YOUR-DOMAIN/agent-bus --token ...
agent-bus status --gateway https://YOUR-DOMAIN/agent-bus --token ...nodes shows edge-machine presence. status combines node freshness, non-inference ping URLs for model/service reachability, and active room runs so running and queued mean a real Agent Bus run is currently in flight.
Or use a one-time pairing code so the new machine never needs the central token pasted into chat:
# On the central/admin machine
agent-bus pair create --gateway https://YOUR-DOMAIN/agent-bus --token ... --preset codex
# On the machine that should become a remote assistant node
agent-bus setup edge --gateway https://YOUR-DOMAIN/agent-bus --code ABCD-2345 --auto --service auto --out edge.config.json
agent-bus connect --config edge.config.jsonPairing returns a scoped edge token for that node. It can register, poll, report runs, and read discovery metadata, but it cannot create pair codes, create threads, wake rooms, or use the model router.
For the full trust-boundary map covering the admin token, pair code, scoped edge token, adapter execution scope, model-router access, /agents vs /nodes, and reports-only exports, see Trust Boundaries.
Run a central gateway:
agent-bus init central --out central.config.json
# edit token and modelRouter backends
agent-bus serve --config central.config.jsonRun with Docker:
agent-bus init central --out central.config.json
docker compose up --buildGenerate a long-running service:
agent-bus service systemd --mode edge --config /opt/agent-bus/edge.config.json --cwd /opt/agent-bus --agent-bus-path /usr/bin/agent-bus --out agent-bus-edge.serviceBuild release artifacts locally:
npm run bundle -- --archiveRun a zero-quota offline smoke test:
agent-bus smoke --offlineThis uses the local Python gateway for room support and makes no model-provider calls.
For a first-run AI-to-AI room walkthrough, run:
npm run demo:roomIt starts a local gateway, connects two fake command agents, has one agent delegate to the other with @demo-worker: ..., waits for DONE, and writes agent-bus-room-demo-report.md using room export --reports-only so the artifact is safe to share.
Or start the demo pieces manually:
cp central.config.example.json central.config.json
node mock-openai-backend.mjs
AGENT_BUS_TOKEN=replace-with-a-long-random-token node central-gateway.mjs serveThen call the OpenAI-compatible router:
curl -s http://127.0.0.1:8788/v1/chat/completions \
-H "authorization: Bearer replace-with-a-long-random-token" \
-H "content-type: application/json" \
-d '{"model":"agent-bus-default","messages":[{"role":"user","content":"hello"}]}'Gateway + Edge Mode
Start the central gateway:
node central-gateway.mjs serveOpen the web console:
http://127.0.0.1:8788/console/Start one edge node:
node edge-node.mjs connectSubmit a task to the central gateway:
curl -s -X POST http://127.0.0.1:8788/threads ^
-H "content-type: application/json" ^
-H "authorization: Bearer replace-with-a-long-random-token" ^
-d "{\"message\":\"hello distributed agents\",\"agents\":[\"local-echo\"]}"The central gateway stores:
data/central/threads.jsonldata/central/runs.jsonldata/central/events.jsonldata/central/threads/thread_*.jsondata/central/runs/run_*.json
Deploy Edge Nodes
Copy one edge config onto each machine and run edge-node.mjs connect --config ....
- Browser/shell machine: use
edge.hk.example.jsonas a starting point for OpenClaw and Hermes. - Code machine: use
edge.120.example.jsonas a starting point for Codex. - Model gateway machine: use
edge.178.example.jsonas a starting point for an OpenAI-compatible backend.
Each edge node sends:
AGENT_MESSAGE: task textAGENT_MESSAGE_FILE: path to a UTF-8 file containing the full task textAGENT_MESSAGE_BYTES: UTF-8 byte length of the full task textAGENT_RUN_ID: run idAGENT_THREAD_ID: stable thread id when the task belongs to a threadAGENT_ROOM_ID: stable room id when the task belongs to a roomAGENT_CACHE_KEY: stable per-agent cache key based on the room or thread idAGENT_SESSION_ID: same value asAGENT_CACHE_KEY, for CLIs that expose session idsAGENT_ID: local agent idEDGE_NODE_ID: edge node id
For large tasks, AGENT_MESSAGE may be empty to avoid OS environment-size limits; adapters should read AGENT_MESSAGE_FILE when present. The default OpenClaw wrapper does this, passes AGENT_SESSION_ID as openclaw agent --session-id, starts the message with a stable Agent Bus envelope, falls back to a prompt file when the final OpenClaw CLI argument would be too large, and backs up oversized Agent Bus session files before a run so stale OpenClaw history does not balloon later room turns.
When using OpenClaw, prepare a dedicated Agent Bus agent/workspace before connecting the edge node:
agent-bus openclaw prepare \
--config ~/.openclaw/openclaw.json \
--agent-id agent-bus \
--workspace /opt/agent-bus/openclaw-workspace \
--context-tokens 48000Then use OPENCLAW_AGENT_ID=agent-bus ./scripts/openclaw-agent-bus.sh as the OpenClaw runCommand. This keeps Agent Bus room traffic away from any personal/default OpenClaw workspace, archives BOOTSTRAP.md in the target workspace so the first room turn answers the task instead of running onboarding, and gives the dedicated agent a stable Agent Bus system prompt, empty inherited skills list, cacheRetention: "long", and a conservative context cap unless those fields were already customized.
The edge node streams stdout/stderr events back to the gateway, then posts a final run result.
Gateway API
curl -s http://127.0.0.1:8788/health
curl -s http://127.0.0.1:8788/agents ^
-H "authorization: Bearer replace-with-a-long-random-token"
agent-bus status --gateway http://127.0.0.1:8788 --token replace-with-a-long-random-token
curl -s -X POST http://127.0.0.1:8788/route ^
-H "content-type: application/json" ^
-H "authorization: Bearer replace-with-a-long-random-token" ^
-d "{\"message\":\"Fix Node tests and check the model gateway\",\"mode\":\"orchestrate\"}"Machine-readable discovery:
curl -s http://127.0.0.1:8788/.well-known/agent-bus.json
curl -s http://127.0.0.1:8788/v1/agent-bus/manifest \
-H "authorization: Bearer replace-with-a-long-random-token"All gateway endpoints except GET /health require a bearer token.
- The admin token from
AGENT_BUS_TOKENhas full gateway, model router, room, thread, and pairing access. - Scoped edge tokens are generated by pairing or configured in
edgeTokens. They can callGET /agents,GET /manifest,POST /edge/register,POST /edge/poll,POST /edge/events, andPOST /edge/complete. - Edge tokens cannot call admin endpoints such as
POST /pair-codes,POST /threads, room wakeups, or/v1/chat/completions.
POST /edge/pair uses a short, one-time code instead of a bearer token. The code expires and is consumed after one successful join, then the gateway stores only a hash of the generated edge token in data/central/edge_tokens.json.
Admins can also manage edge tokens directly with GET /edge/tokens, POST /edge/tokens, and POST /edge/tokens/revoke. Token list responses never include raw tokens or token hashes; newly created raw tokens are returned once.
Agent Health
GET /agents separates node reachability from model readiness:
status/last_seen_at: the agent is advertised by an online edge node.node_status: the edge process is online and polling the gateway.ping_status: optional shallow URL reachability from the edge machine, flattened fromhealth.ping_statusfor quick CLI checks.health.last_run_status/last_run_status: the latest real task result, when available.
Configure pingUrl, healthUrl, or modelUrl on an edge agent to check a URL without spending model credits:
{
"id": "openclaw-hk",
"pingUrl": "https://YOUR-MODEL-GATEWAY/v1/models"
}HTTP 2xx, 3xx, and 4xx responses are treated as reachable. For example, 401 from /v1/models proves the endpoint is alive without validating a key or running a completion.
Rooms
Rooms let agents coordinate with each other:
agent-bus room create \
--gateway https://YOUR-DOMAIN/agent-bus \
--token replace-with-a-long-random-token \
--title deploy-check \
--goal "Check the deployment, fix obvious issues, and report status." \
--agents codex-120,openclaw-hk,hermes-hk \
--wake-agents codex-120,openclaw-hk,hermes-hk \
--no-auto-rotate
agent-bus room show room_xxx --gateway https://YOUR-DOMAIN/agent-bus --token ...
agent-bus room export room_xxx --format markdown --out room.md --gateway https://YOUR-DOMAIN/agent-bus --token ...
agent-bus room export room_xxx --reports-only --out room-summary.md --gateway https://YOUR-DOMAIN/agent-bus --token ...
agent-bus room export room_xxx --format json --out room.json --no-redact --gateway https://YOUR-DOMAIN/agent-bus --token ...
agent-bus room message room_xxx --message "New context" --agents openclaw-hk
agent-bus room wake room_xxx --agents hermes-hk --reason "Continue from the latest report."Inside a room, agents can call each other with plain text:
@agent-id: task for that agent
REPORT: concise user-facing report
BLACKBOARD: concise shared state update
WAKE agent-id IN 5m: reason
DONEDONE requests completion; the room waits for all queued and running work to finish before becoming completed.
OpenAI-Compatible Model Router
The central gateway also exposes OpenAI-compatible model endpoints:
GET /v1/modelsPOST /v1/chat/completions
Configure backends in central.config.json:
{
"modelRouter": {
"enabled": true,
"defaultBackend": "sub2api-178",
"defaultModel": "gpt-4o-mini",
"backends": [
{
"id": "sub2api-178",
"baseUrl": "http://127.0.0.1:8080/v1",
"apiKeyEnv": "SUB2API_API_KEY",
"models": ["gpt-4o-mini", "gpt-4.1", "gpt-5"],
"modelAliases": {
"agent-bus-default": "gpt-4o-mini"
}
}
]
}
}Windows or OpenAI-compatible clients can use:
base_url = https://YOUR-GATEWAY-DOMAIN/agent-bus/v1
api_key = <agent-bus bearer token>
model = agent-bus-defaultIf a Windows client has HTTPS/TLS trouble with the public endpoint, run a local proxy:
.\start-windows-openai-proxy.ps1 -Upstream "https://YOUR-GATEWAY-DOMAIN/agent-bus" -Token "<agent-bus bearer token>"Then point the client at:
base_url = http://127.0.0.1:8789/v1
api_key = anything-or-emptyLocal model-router test:
node .\mock-openai-backend.mjs
node .\central-gateway.mjs serve
curl -s http://127.0.0.1:8788/v1/models -H "authorization: Bearer replace-with-a-long-random-token"Deployment Shape
A typical deployment looks like:
public HTTPS gateway
/agent-bus/ -> central gateway on localhost
edge machine A -> outbound HTTPS poll -> central gateway
edge machine B -> outbound HTTPS poll -> central gateway
edge machine C -> outbound HTTPS poll -> central gatewayPublic health check:
curl -s https://YOUR-GATEWAY-DOMAIN/agent-bus/healthProtected APIs require the bearer token injected into the running gateway and edge processes.
SSH Prototype
Setup
cd distributed-agent-bus
cp config.example.json config.json
node server.mjs agentsThe sample config uses placeholder hosts:
openclaw-edgehermes-edgecodex-edgemodel-gateway-edge
The config stores SSH key file paths, not private key contents.
On Windows, keep sshPath pointed at C:/Windows/System32/OpenSSH/ssh.exe. Some bundled runtimes may resolve ssh differently.
The prototype also defaults sshViaPowerShell to true, because the same Windows OpenSSH call is more reliable through PowerShell in this workspace.
The default maxParallelAgents is 1 because multiple adapters may share the same remote host and some SSH servers close concurrent handshakes aggressively. Increase it after moving adapters to local HTTPS services.
sameHostDelayMs adds a small pause between agents on the same SSH host.
Route Without Running
Use route to preview which machine would handle a task. This does not contact the remote agents.
node server.mjs route "Fix a Node project test failure and check whether the model gateway is available" --mode orchestrateThe current rule-based router is intentionally simple:
- code/review tasks -> Codex-style agents
- ops/browser/server tasks -> OpenClaw-style agents
- research/design tasks -> Hermes-style agents
- model/API gateway tasks -> model gateway agents
Run One Task
node server.mjs run "Check your local agent health and summarize it." --agents openclaw-edge,hermes-edge
node server.mjs run "Fix a Node test failure and check gateway health." --mode orchestrateHealth check:
node server.mjs health --agents openclaw-edge,codex-edgeStart HTTP API:
node server.mjs serveThen:
curl -s http://127.0.0.1:8787/agents
curl -s -X POST http://127.0.0.1:8787/route ^
-H "content-type: application/json" ^
-d "{\"message\":\"Fix Node tests and check gateway health\",\"mode\":\"orchestrate\"}"
curl -s -X POST http://127.0.0.1:8787/threads ^
-H "content-type: application/json" ^
-d "{\"message\":\"Check status\",\"agents\":[\"openclaw-edge\"]}"Adapter Contract
Each configured agent needs:
idkindtransporthostuserkeyPathhealthCommandrunCommand
For ssh-command adapters, the bus sends the user message as the remote environment variable AGENT_MESSAGE.
Example:
{
"id": "hermes-edge",
"transport": "ssh-command",
"runCommand": "/root/.local/bin/hermes chat -q \"$AGENT_MESSAGE\" -Q"
}Logs
Runtime data is written under data/:
threads.jsonlruns.jsonlthreads/thread_*.json
Health checks are stored in runs.jsonl too. Stored stdout/stderr and snapshots are redacted for common API key, bearer token, password, and secret patterns before writing.
Next Hardening Steps
- Add signed native installers after the portable bundle format is stable.
- Add approval gates for risky commands.
- Add a real orchestrator model that chooses agents instead of broadcasting.
- Move logs from JSONL to SQLite/PostgreSQL.
