Skip to content

~3 min read

Cockpit — fomo.cagatay.my·

One screen. The agent is the spine; everything else is its face, its hands, and a red button.

the cockpit after one turn: live eyes and twin on the left, the log on the right with a `read · joints` tool row and Fomo's answer
Desk, 2026-09-09 03:28Z, live arm. Asked without moving where it is: one tool row, read · joints 0.1 s, then the answer — folded, board on the bench, 0.11 m ahead. Real capture, real turn.

fomo dash runs FastAPI on 127.0.0.1:8091. It owns the serial port: one process per adapter, so the tools go through it whenever it is up. It boots with no arm attached and hot-attaches the bus within ~2 s of the adapter appearing (Arm watcher).

The screen·

One screen, the agent is the spine. Top bar: a tally light that says what Fomo is doing (idle / thinking / talking / moving), status pills (bus, rail voltage, torque, pose, eyes, link period), a big red STOP (/api/control/stop, always in reach, also on the phone with the dock open) and ⚙ settings. Face on the left: the eyes (live MJPEG, tap to start, fps · watchers) and the digital twin, following the servos at 5 Hz. Talk on the right: the log and the composer. On a phone (< 900 px) the face sits on top behind an eyes/twin tab, the composer is pinned above the home indicator, and the twin's 590 KB of three.js is not even fetched until the twin tab is opened.

The log is the narrative, not a chat window bolted onto a control panel. Your prompts, Fomo's streamed answers (first-token latency shown), and every tool call as a row in words — move · elbow → 200.5°, look · pan +30°, pose · go home, motion · happy · 40°/s, photo (the picture inline) — with its duration, and a progress bar while a motion plays (state.busy = motion:happy 3/7). Errors come back in words too: the guard's {'wrist_flex': 21.2} deg behind reads wrist_flex 21.2° behind the goal, a stall says whether the arm folded itself afterwards. Manual actions land in the same log as notes ("moved elbow to 200° · 0.8 s · 3 legs"), so the agent and you share one story; the agent's next turn sees them.

the dock open on the right: six sliders on their measured windows, look pad, poses, motions, torque and STOP
The dock — d. Sliders sit on the measured windows (base 66–251°, elbow 69–262°), every pose and motion is a button, STOP is in the corner too.

Drive by hand — 🎚 or d — is a dock (a bottom sheet on the phone, above the composer): one slider per joint bound to its measured window, look buttons (pan/tilt), fold + every pose, every motion (/api/motions, one button each, disabled while busy), torque on/off, a speed slider capped at the guard's 90 °/s. Sliders move on release, never while dragging. Nothing bypasses the guard.

Voice — 🎤 or m — is browser-native: SpeechRecognition types into the composer and sends the final phrase; 🔊 reads finished answers with speechSynthesis. No keys, no server, so it works only where the browser has it (Safari, Chrome). While you talk, the thinker holds back.

Thinker — the think toggle in the bar (/api/agent/thinker, off by default, stored in ~/.fomo/agentic.json): every every_s seconds Fomo takes one photo, reads its state and says one line about what it sees, streamed over /ws/thinker into the log as a thinking turn. It never moves joints 1–4; with may_move it may pan/tilt to frame. It skips a tick while the arm is busy, while you are typing or talking, and past its hourly budget.

the cockpit on a phone: eyes on top behind an eyes/twin tab, Fomo's opening line, suggested prompts, composer pinned above the home indicator the phone with the dock open as a bottom sheet over the composer
iPhone 13, same minute. The face on top behind an eyes/twin tab; the dock is a bottom sheet. STOP never leaves the bar.

Settings (⚙) hold the panel key, the thinker's cadence, the agent's prompt and model; the panel installs as an app and says "offline — the arm is not reachable" instead of a blank page.

Every route, the auth chain, sim mode and the launchd/cloudflared recipe: Reference.