Skip to content

Latest commit

 

History

History
260 lines (217 loc) · 15.7 KB

README.md

File metadata and controls

260 lines (217 loc) · 15.7 KB

AO CLI

The ao CLI is a thin Go/Cobra client for the local Agent Orchestrator daemon. It starts, discovers, inspects, and stops the daemon through the loopback HTTP surface and the running.json handshake. It must not open SQLite directly or call runtime, workspace, tracker, or agent adapters in-process.

When using the CLI directly from a shell, make sure the daemon is running first with ao start or by opening the desktop app. Product commands such as ao agent ls and ao spawn call the loopback daemon and will fail with a "daemon is not running" error if no running.json points at a live process. From a source checkout, build and run the local binary explicitly, for example:

cd backend
go build -o ./bin/ao ./cmd/ao
./bin/ao agent ls

Current commands

Every product command resolves to a daemon HTTP route. Run ao <command> --help for the authoritative flag shape.

Daemon control

Command Purpose
ao start Start the daemon in the background and wait for /readyz.
ao stop Gracefully stop the daemon via loopback POST /shutdown after verifying daemon identity.
ao status / --json Report daemon state from running.json, process liveness, /healthz, and /readyz.
ao doctor / --json Check config, data directory, DB-file presence, daemon state, git, agent-harness presence and sign-in, and (on Darwin/Linux) tmux; on Windows conpty is built in.
ao completion <shell> Generate completions for bash, zsh, fish, or powershell.
ao version / ao --version Print build metadata.
ao daemon Hidden internal daemon entrypoint used by ao start.

Hosted VM

Command Purpose
ao setup-vm --domain <host> Prepare a hosted Ubuntu LTS VM: platform gate, preflight (DNS, 80/443, sudo) that mutates nothing on failure, then install ao, tmux, git, gh, and two systemd units.
ao vm serve Run the public TLS gateway in front of the loopback daemon. Normally started by the ao-gateway.service unit ao setup-vm writes, never by hand.
ao vm setup-harness claude Hand the terminal to the claude harness's own interactive login. Foreground only, and claude is the only supported harness.

ao setup-vm refuses to run on anything but Ubuntu with systemd and apt, and prints the manual path instead. It writes ao-daemon.service and ao-gateway.service as two separate units running two separate processes, per docs/adr/0002-hosted-public-gateway.md, with WorkingDirectory and every AO_* path set to an absolute value. It is idempotent: a second run rewrites nothing that has not changed and restarts nothing that does not need it.

It does not install an agent harness and does not configure git credentials, so it ends by printing what is still missing with the exact command for each (ao vm setup-harness claude, gh auth login). Use --dry-run to see the plan and both unit files without touching the machine.

ao vm setup-harness claude runs claude auth login with this process's own stdio, so the harness owns the terminal: it prints a URL and then waits for a code to be pasted back, which cannot be scripted. Git credentials are not wrapped by any ao command; gh auth login once is enough, and the daemon reads the credential through gh auth token.

Harness readiness is reported by ao doctor as its claude-auth check, which asks the harness itself (claude auth status --json) rather than guessing where its credentials live. A machine with no harness login is a WARN, never a FAIL, and carries a remediation field in ao doctor --json naming the exact command to run. That is what the desktop machine card reads.

Product commands

Command Daemon route
ao project add POST /api/v1/projects
ao project ls GET /api/v1/projects
ao project get <id> GET /api/v1/projects/{id}
ao project set-config <id> PUT /api/v1/projects/{id}/config
ao project rm <id> DELETE /api/v1/projects/{id}
ao agent ls POST /api/v1/agents/readiness/ensure (display)
ao agent ls --refresh POST /api/v1/agents/refresh (forced checks)
ao spawn Targeted launch ensure, then POST /api/v1/sessions
ao session ls GET /api/v1/sessions
ao session get <id> GET /api/v1/sessions/{id}
ao session kill <id> POST /api/v1/sessions/{id}/kill
ao session restore <id> POST /api/v1/sessions/{id}/restore
ao session exit-agent <id> POST /api/v1/sessions/{id}/exit-agent
ao session resume-agent <id> POST /api/v1/sessions/{id}/resume-agent
ao session switch-agent <id> <target-harness> POST /api/v1/sessions/{id}/switch-agent
ao session agent-switch ls <session-id> GET /api/v1/sessions/{id}/agent-switches
ao session handoff submit POST /api/v1/sessions/{id}/agent-switches/{switchId}/handoff
ao session rename <id> <name> PATCH /api/v1/sessions/{id}
ao session cleanup POST /api/v1/sessions/cleanup
ao session claim-pr [<id>] <pr-ref> POST /api/v1/sessions/{id}/pr/claim
ao orchestrator ls GET /api/v1/orchestrators
ao send POST /api/v1/sessions/{id}/send
ao preview [url] POST /api/v1/sessions/{id}/preview
ao preview start/status/stop POST/GET/DELETE /api/v1/sessions/{id}/preview/server
ao browser ... GET /api/v1/browser/status, POST /api/v1/browser/commands
ao hooks <agent> <event> POST /api/v1/sessions/{id}/activity (hidden)

ao agent ls asks the daemon to ensure display readiness, then prints the existing table or legacy JSON projection. The daemon alone decides whether a native check is needed. --refresh is a deprecated compatibility flag that forces fresh installation and authentication checks before printing.

ao spawn resolves project context in this order: explicit --project, AO_PROJECT_ID, AO_SESSION_ID (by fetching the current session from the daemon), then the current working directory matched against registered project paths. If AO_SESSION_ID is set but the session cannot be fetched, pass --project explicitly.

Agent switching is initially available only for worker sessions whose source and target harnesses are Claude Code or Codex. The main command accepts an idempotency key:

ao session switch-agent ao-7 codex \
  --idempotency-key switch-ao-7-to-codex

ao session agent-switch ls ao-7 --json

switch-agent and agent-switch ls both support --json. The agent-switch command also has the agent-switches alias, and ls has the list alias.

ao session handoff submit is the internal source-agent path for optional semantic enrichment, not a required human step in a normal switch. It requires the switch ID, exact source launch generation, and a regular file containing one JSON object no larger than 64 KiB. --session defaults to AO_SESSION_ID:

AO_SESSION_ID=ao-7 ao session handoff submit \
  --switch switch-123 \
  --source-generation generation-456 \
  --file /tmp/ao-handoff.json \
  --json

Switching preserves the AO worker session and worktree. It does not translate, clip, or rewrite provider transcript files; providers continue to own their native history and compaction.

ao session claim-pr <pr-ref> attaches a PR to the current worker by reading AO_SESSION_ID. From an orchestrator or external shell, pass the target explicitly with ao session claim-pr <session-id> <pr-ref>. The explicit form remains supported for backward compatibility and cross-session coordination.

If --agent / --harness is omitted, ao spawn uses the resolved project's worker.agent config. Before spawning, the CLI performs one targeted launch ensure. It fails early for unsupported or definitely missing harnesses and warns-but-continues for unauthorized or unknown observations; daemon session creation repeats launch validation and native launch remains authoritative. --skip-agent-check suppresses only the CLI warnings and early check, never the daemon validation.

ao preview resolves its session from the AO_SESSION_ID environment variable (it is meant to run inside a session), not a flag. With no argument it autodetects an index.html in the session workspace. Relative file targets are resolved from the session workspace root, regardless of the shell's current directory, and served through AO's confined loopback preview origin. Absolute paths and file:// URLs must resolve inside that workspace; explicit HTTP and HTTPS targets remain regular browser URLs.

ao preview start [configuration] loads .ao/launch.json from the session workspace, starts that exact command under a session-owned supervisor, selects or records its loopback port, waits for readiness, and opens application targets in the Browser panel. status reports bounded recent logs and stop terminates the managed process tree. Multiple configurations must be selected by name; AO does not assign confidence scores to arbitrary localhost servers. This is an optional, reusable project configuration, not a prerequisite for preview. Agents must not create it automatically. Static HTML and Markdown use the direct file preview and must not cause package-manager scaffolding, dependency installation, or a development server to be introduced.

When a browser-displayable file is the requested artifact, agents should call ao preview <workspace-path> immediately after creating or materially updating the primary output. Markdown, HTML, PDF, SVG, and common images can be served directly. Supporting assets must not replace an active application preview.

ao browser also resolves its target from AO_SESSION_ID, but controls the session-owned live Electron browser rather than only setting its preview URL. The target-isolated command set includes status, open, snapshot, click, dblclick, focus, fill, type, press, hover, scroll, scrollintoview, drag, select, check, uncheck, get, highlight, unhighlight, tabs, tab new, tab select, tab close, frame, dialog, wait, screenshot, network start/status/list/stop/clear, console, and errors. The native engine is bound internally; there is no second command or connection setup. Logical tab IDs remain stable for the session, and allowed popups become AO browser tabs rather than separate OS-browser windows. The AO desktop app must be open because Electron owns the WebContentsView. References from a snapshot are invalidated after navigation or DOM replacement; they are also invalidated when changing tabs. Take another snapshot when a command reports STALE_REFERENCE. Browser waits cover load completion, text or selector appearance and disappearance, URL matching, fixed delays, and a configurable DOM-stability window for HMR-driven verification. Browser tabs in the same worker share a memory-only Electron profile. Different workers receive distinct partitions, so cookies, authentication, local storage, and session storage do not leak between their browser runtimes. Network capture is disabled by default and must be started explicitly. It is scoped to the active tab at start time, expires after 60 seconds by default (maximum 300), retains at most 200 in-memory entries, and is cleared with the tab/session. Captured data is metadata-only: request and response bodies are never read, sensitive headers are omitted, and URL credentials, fragments, and query values are redacted.

go run . in backend/ remains a compatibility wrapper around the daemon.

PR actions are available through ao pr merge and ao pr resolve-comments. Review actions are available through ao review ls, ao review trigger (also execute and restart), ao review cancel (also stop), and ao review submit.

Configuration

The CLI and daemon share the same environment-driven config:

Var Default Purpose
AO_PORT 3001 Loopback daemon port.
AO_RUN_FILE ~/.ao/hosted/running.json PID/port handshake.
AO_DATA_DIR ~/.ao/hosted/data SQLite data directory.
AO_REQUEST_TIMEOUT 60s REST request timeout.
AO_SHUTDOWN_TIMEOUT 10s Graceful shutdown cap.
AO_KEEP_DAEMON unset (off) Keep the desktop app's daemon running after the window closes; stop only via ao stop. (fork)
AO_DISABLE_GPU unset (off) Skip Chromium hardware acceleration; escape hatch for broken Linux GPU drivers.

The daemon always binds 127.0.0.1.

Manual smoke test

cd backend
go build -o /tmp/ao ./cmd/ao

tmp=$(mktemp -d)
export AO_RUN_FILE="$tmp/running.json"
export AO_DATA_DIR="$tmp/data"
export AO_PORT=3037

/tmp/ao status --json
/tmp/ao doctor
/tmp/ao start
/tmp/ao status --json
/tmp/ao stop
/tmp/ao status --json
rm -rf "$tmp"

Adding new commands

Add a product command only when a daemon HTTP route owns the corresponding mutation/read; the CLI must call that route rather than reimplementing daemon behavior. Commands not yet exposed but with backend routes in place include ao events ... (over the CDC/SSE endpoint) and CLI parity for PR/review actions.

Do not port old in-process TypeScript CLI behavior that mixed command handling with storage and runtime implementation details.