Merge p2/english: English entry point (P2)
This commit is contained in:
commit
f2bf838e6d
|
|
@ -0,0 +1,118 @@
|
|||
# AnyBot
|
||||
|
||||
[中文 README](README.md) · **English**
|
||||
|
||||
Your own AI bot. It chats, and it *does things*: files, shell commands, your browser, your screen, scheduled jobs. Self-hosted — your data stays on machines you own.
|
||||
|
||||
- Every bot can use a different model: xAI Grok, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama (local), or any OpenAI-compatible endpoint
|
||||
- Desktop app (macOS, Linux) + mobile web app (install to home screen, push notifications)
|
||||
- Multiple computers: one **brain**, the others are its **hands** — all linked through your own account server, no ports opened
|
||||
|
||||
## Demo
|
||||
|
||||
<!-- Drop a GIF/video at docs/demo/anybot.gif (see docs/demo/README.md) and it shows here. -->
|
||||
📹 *No recording yet — see [docs/demo/README.md](docs/demo/README.md).*
|
||||
|
||||
## How it compares
|
||||
|
||||
| | AnyBot | OpenClaw | OpenHands | Khoj | UI-TARS-Desktop |
|
||||
|---|---|---|---|---|---|
|
||||
| Positioning | Personal bot across your machines | Agent gateway on your hardware | Coding agent in sandboxes | Personal knowledge/RAG assistant | GUI agent on one desktop |
|
||||
| Chat with your own bots | ✅ multi-bot, per-bot model | ✅ via gateways | coding-focused | ✅ | — |
|
||||
| Controls your real desktop/browser | ✅ + approvals, VNC takeover | via plugins | sandboxed, not your desktop | — | ✅ (its whole product) |
|
||||
| Multi-device brain/hands/phone | ✅ built in, zero open ports | bring your own | — | server + apps | single machine |
|
||||
| Bot's own sandboxed computer (Docker, optional gVisor) | ✅ | host shell | ✅ | — | — |
|
||||
| Secrets never enter the model (vault fills forms directly) | ✅ | — | — | — | — |
|
||||
| Phone web app + push, no app store | ✅ | community | — | ✅ | — |
|
||||
| One binary, JSON files, no database server | ✅ | ✅ | — | needs services | Electron |
|
||||
| License | Apache-2.0 | MIT | MIT | AGPL-3.0 | Apache-2.0 |
|
||||
|
||||
AnyBot's bet: a bot that touches your real machines is only trustworthy when the **harness** — not the model — owns the safety gates: per-action approvals, a sensitive-click policy the model cannot talk its way past, secrets painted out of screenshots, and an audit trail that survives crashes.
|
||||
|
||||
## Install
|
||||
|
||||
Pick one row, paste it into a terminal. The script installs what's needed (system packages, Rust, Docker).
|
||||
|
||||
| I want… | On which machine | Command |
|
||||
|---|---|---|
|
||||
| **Desktop app** | macOS, Linux with a screen | `curl -fsSL https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.sh \| sh` |
|
||||
| **Account server** (+ brain + HTTPS) | A Linux host with a domain | `curl -fsSL https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.sh \| sh -s -- server` |
|
||||
| **Add a computer as hands** | Any Linux (headless fine) | In AnyBot press “Add a computer”, paste the one-liner it shows |
|
||||
|
||||
First run compiles on your machine (5–15 min); later updates take 1–2 min. Read the script first: [install.sh](install.sh). It installs into your user folders — no root.
|
||||
|
||||
## Getting started
|
||||
|
||||
**One computer:** install the desktop app → Settings › Models → paste an API key → chat.
|
||||
|
||||
**Phone + several computers:** run the server once (asks only for URL, account, password; gets HTTPS by itself), open the URL on your phone and “Add to Home Screen”, then pair other computers with one-time pairing codes. Any Linux box can become the bot's own computer, running inside Docker (optionally gVisor). A Mac can be the brain instead of a server ([docs/multi-device.md](docs/multi-device.md), method B). No database to run: state is a handful of JSON files, crash-safe writes; back up two Docker volumes.
|
||||
|
||||
## Updates, removal
|
||||
|
||||
| What | Command |
|
||||
|---|---|
|
||||
| Update app / hands | re-run the install line |
|
||||
| Update server | on the server: `cd ~/AnyBot && make server` → “keep settings, update, restart” |
|
||||
| Uninstall the program | `cd ~/AnyBot && make uninstall` (your bots and chats are kept) |
|
||||
| Stop a hands machine | `systemctl --user disable --now anybot` |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```sh
|
||||
cd ~/AnyBot && make doctor
|
||||
```
|
||||
|
||||
It tells you what's missing. Common: `Killed`/`signal: 9` while compiling → add 2 GB swap; `requires rustc 1.95` → `rustup update stable`; Mac says “cannot verify developer” → right-click › Open once; phone says “brain offline” → the brain machine is asleep ([docs/multi-device.md](docs/multi-device.md)).
|
||||
|
||||
## Build from source
|
||||
|
||||
```sh
|
||||
git clone https://code.30cm.net/daniel.w/AnyBot.git
|
||||
cd AnyBot
|
||||
make deps && make && make install # deps: system packages + Rust 1.95+ (one time)
|
||||
```
|
||||
|
||||
`make run` · `make test` · `make doctor` · `make serve` (headless) · `make setup` (pair this machine) · `make server` · `make dist` · `make help`. Needs: Rust ≥1.95, a C compiler, git; on Linux also `pkg-config` + `libasound2-dev` (deps installs all).
|
||||
|
||||
## Deployment
|
||||
|
||||
| Method | For | Notes |
|
||||
|---|---|---|
|
||||
| **Docker Compose** (default) | server | `install.sh server`: relay + brain + Caddy (auto-HTTPS) containers; data in Docker volumes; Tailscale URLs skip Caddy ([compose file](deploy/server/docker-compose.yml)) |
|
||||
| Unattended | servers, Ansible | `ANYBOT_SETUP_USER=… ANYBOT_SETUP_PASSWORD=… [ANYBOT_SETUP_DOMAIN=…] sh install.sh server` |
|
||||
| Portainer/Coolify/Dokploy | existing PaaS | they take the standard `deploy/server/docker-compose.yml` — set `ANYBOT_USER`, `ANYBOT_PASSWORD`, `ANYBOT_RELAY_PUBLIC_URL` |
|
||||
| **systemd** user service | hands, headless brains | `anybot setup` installs `anybot.service`; `loginctl enable-linger` to keep it running logged-out |
|
||||
| **launchd** | Mac as brain/relay | see [docs/multi-device.md](docs/multi-device.md) |
|
||||
| Gitea Actions | release builds on `v*` tags | see [docs/release.md](docs/release.md) |
|
||||
|
||||
## Security in one paragraph
|
||||
|
||||
Commands run in a macOS sandbox (deny ~/.aws, Keychain, AnyBot's own data; allow only the attachments dir); per-action approvals with a policy layer the model cannot negotiate; clicking anything labelled “send/pay/transfer” is asked *every time*, even in direct mode, with no grant that silences it; card numbers and OTPs in screenshots are painted black before the model sees them; everything read from outside is wrapped as `<untrusted>` data, never treated as orders; the bot's own computer is a Docker container (gVisor for a second wall); relay stores only hashes (argon2id) and supports 2FA with recovery codes; egress binds loopback + detected bridge gateways, never 0.0.0.0. Details: [docs/architecture.md](docs/architecture.md) (Chinese; [English summary](docs/en/architecture.md)).
|
||||
|
||||
## Documentation
|
||||
|
||||
| Doc | Contents |
|
||||
|---|---|
|
||||
| [docs/install.md](docs/install.md) ([en](docs/en/install.md)) | per-platform details, manual steps, FAQ |
|
||||
| [docs/multi-device.md](docs/multi-device.md) ([en](docs/en/multi-device.md)) | brain / hands / screen: setup, pairing, offline |
|
||||
| [docs/architecture.md](docs/architecture.md) ([en](docs/en/architecture.md)) | layers, data storage, security design |
|
||||
| [docs/protocol.md](docs/protocol.md) ([en](docs/en/protocol.md)) | relay API and long-connection wire format |
|
||||
| [docs/release.md](docs/release.md) | packaging, CI |
|
||||
| [FEATURES.md](FEATURES.md) | every feature (Chinese) |
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0 — see [LICENSE](LICENSE). Copyright the contributors (The AnyBot contributors). Calls to third-party services (xAI, OpenAI, …) go through their public APIs; follow each service's terms and your own key agreements.
|
||||
|
||||
## Repository layout
|
||||
|
||||
```
|
||||
install.sh one-line installer (app, hands, server)
|
||||
src/ AnyBot: desktop app, headless, `anybot setup`
|
||||
relay/ account server
|
||||
mobile/ phone web app (single file, embedded)
|
||||
box/ the bot's own computer (Docker container)
|
||||
deploy/ per-platform install & deployment files
|
||||
docs/ technical docs (docs/en/ = English)
|
||||
bench/ computer-use benchmark (tasks, scoring, red-team suite)
|
||||
```
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
# AnyBot
|
||||
|
||||
中文 · [English →](README.en.md)
|
||||
|
||||
你自己的 AI Bot。可以聊天,也能幫你動手做事:處理檔案、執行指令、操作瀏覽器和螢幕、跑例行任務。
|
||||
|
||||
- 每個 Bot 可以用不同的 AI:xAI Grok、OpenAI、Claude、Gemini、OpenRouter、OpenCode Go、Ollama(本機),或任何 OpenAI 相容的服務
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
# Demo media
|
||||
|
||||
The README shows a demo here. To record one:
|
||||
|
||||
1. Start AnyBot (desktop) and a bot with a key; run at 1440p, hide notifications.
|
||||
2. Record a 20–40 s clip of one complete task, e.g. "organise the Downloads folder"
|
||||
(chat → plan → tool steps → files moved) and one computer-use task
|
||||
(bot opens the browser, fills a form, stops before Send — the approval card
|
||||
popping up **is** the product).
|
||||
3. Export GIF: `ffmpeg -i clip.mov -vf "fps=12,scale=960:-1:flags=lanczos,split[a][b];[a]palettegen[p];[b][p]paletteuse" docs/demo/anybot.gif`
|
||||
(keep it under ~8 MB; if bigger, cut to one task).
|
||||
4. Name it `docs/demo/anybot.gif`. The README comment shows where to un-comment
|
||||
the `<img>` line.
|
||||
|
||||
Preferred: one GIF (desktop, computer-use with the approval card) + one 30 s
|
||||
MP4 (`demo.mp4`) of the phone app pairing a new machine.
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# Architecture (English summary)
|
||||
|
||||
The canonical, up-to-date document is [docs/architecture.md](../architecture.md) (Chinese). This is a faithful, condensed English version for contributors who don't read Chinese.
|
||||
|
||||
AnyBot is one Rust binary (`anybot`, egui/eframe UI) with three modes, plus a small account server (`anybot-relay`):
|
||||
|
||||
| Mode | Command | Purpose |
|
||||
|---|---|---|
|
||||
| Window | `anybot` | desktop app |
|
||||
| Headless | `anybot --serve` | servers, always-on machines, "hands" |
|
||||
| Setup | `anybot setup …` | terminal pairing wizard (`src/setup.rs`) |
|
||||
|
||||
## Layers
|
||||
|
||||
```
|
||||
UI (src/app.rs, src/app/*) — chat, sidebar, settings, computers, file preview, brain mirror
|
||||
│ │
|
||||
Turn loop (agent.rs) Remote (remote.rs, tunnel.rs, wire.rs)
|
||||
call model, run tools, phone API, device pairing,
|
||||
approvals, summarising brain sync, long WebSocket to relay
|
||||
│ │
|
||||
Models Tools (tools.rs, cloudbox.rs, botbrowser.rs, computer.rs,
|
||||
(providers) axread.rs/axwrite.rs, macapps.rs, vault*.rs, mcp.rs, memory.rs,
|
||||
routines.rs, triggers.rs, channels.rs)
|
||||
```
|
||||
|
||||
Key modules: `agent.rs` (one turn: system prompt, streaming, tool loop, approval gates, per-step model routing `StepPlan`), `providers.rs` (OpenAI-compatible/Anthropic/OpenCode… + streaming + retries + a vision helper for models that can't see images), `policy.rs` (allow/ask/deny per action), `computer.rs` (one `computer` tool: screenshot, click/type, plus semantic actions `menu_click`/`fill_form`/`wait_for`; macOS reads the real accessibility tree, screenshots are annotated with numbered marks), `cloudbox.rs`+`box/` (the bot's own Docker computer with desktop + browser), `vnc.rs` (view/take over the bot's computer through the relay), `httpd.rs` (tiny shared HTTP server), `mobile/index.html` (phone UI, single embedded file).
|
||||
|
||||
## Storage
|
||||
|
||||
All in the data dir (macOS `~/Library/Application Support/AnyBot/`, Linux `~/.local/share/AnyBot/`, `%APPDATA%\AnyBot\`; `ANYBOT_DATA_DIR` overrides): `state.json` (bots, chats, settings, keys — written via temp file + rename, never half-written), `attachments/`, `browser/` (bot browser profile), `oplog/bot-<id>.jsonl` (append-only work log: every step written to disk *before* it runs; used to repair chats and offer a "continue" card after a crash), `image-notes.json`, `anybot.lock` (OS file lock: one AnyBot per data dir).
|
||||
|
||||
## Security design
|
||||
|
||||
- **Approval gates** (`policy.rs`): actions run directly, ask first, or are denied. "Direct mode" skips asks — except for the gates below, which have no grant.
|
||||
- **Sensitive clicks** (labels containing send/pay/transfer/delete/publish…): asked *every time*, direct mode included — a harness policy the model cannot negotiate past.
|
||||
- **Command sandbox (macOS)**: bot commands run under `sandbox-exec`; AnyBot's data dir, `~/.gnupg`, Keychain, `~/.aws` are unreadable/unwritable; only `attachments/` and `.ssh` are readable, nothing outside is writable. Model API keys are stripped from the environment. Documented exceptions: App Store app CLIs, `ps`/`top`, and a first-time-asked, pattern-filtered single `osascript -e`.
|
||||
- **The bot's own computer**: a Docker container; with gVisor installed on the Docker host, isolation becomes two walls (virtual kernel + Chromium sandbox). On macOS a Lima VM running Docker+gVisor is provided (`deploy/mac/gvisor-vm.sh`).
|
||||
- **Vault**: passwords, card numbers, CVCs never enter the model; AnyBot fills them into web forms directly.
|
||||
- **Screenshots are painted before the model sees them**: card numbers and OTP boxes are masked black (`marks::mask_secrets`).
|
||||
- **Everything read from outside** (screens, web pages, files) arrives wrapped in `<untrusted source="…">`; the system prompt makes it data, never orders. The real wall is the harness gates, not the wording.
|
||||
- **Relay**: stores only hashes (argon2id; legacy PBKDF2 still works, upgraded on next password change); optional 2FA (TOTP + one-time recovery codes); one-page logout for all devices; login lockout per IP+account (8/5 min).
|
||||
- **Egress**: `--serve` binds loopback + detected Docker bridge gateways only, never 0.0.0.0 (`src/egress.rs`).
|
||||
- **docker.sock** is not mounted into the bot's container: a whitelisting docker-socket-proxy sidecar is used instead; rootless Docker works via `DOCKER_SOCKET`.
|
||||
|
||||
## The computer-use control chain
|
||||
|
||||
Model gets a screenshot + numbered marks built from the accessibility tree (macOS) → clicks a number, or names it (`press_named`) → harness translates to an AX press (preferred) or a pixel click (fallback) → the diff of the control tree between looks (`marks::diff`) is appended so the model sees only what changed. `bench/computer-use/` measures tasks end-to-end (model inference is ~90% of wall time) and includes the R1–R7 red-team suite for the gates above.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# Install details (English summary)
|
||||
|
||||
Canonical document: [docs/install.md](../install.md) (Chinese).
|
||||
|
||||
`install.sh` does four steps: system packages (per-distro names: apt/dnf/pacman/zypper), toolchain (rustup → `~/.cargo`; Docker for the server mode), sources into `~/AnyBot`, then build + install. No root for the app; it picks desktop vs. hands automatically (Mac / Linux-with-desktop → app, headless Linux → hands). Low-RAM machines compile with fewer parallel jobs (one per 2 GB). Env: `ANYBOT_DIR`, `ANYBOT_BRANCH`.
|
||||
|
||||
## Requirements
|
||||
|
||||
| Platform | Needs |
|
||||
|---|---|
|
||||
| All | Rust **≥ 1.95** via rustup (apt's is too old), git, make, C compiler |
|
||||
| macOS ≥ 12 | Xcode Command Line Tools (`xcode-select --install`) |
|
||||
| Linux | `build-essential pkg-config libasound2-dev` (+ X11/Wayland, notify, clipboard libs — install.sh installs them) |
|
||||
| Windows | VS Build Tools (C++); `cargo build --release` works, installer packaging is not done |
|
||||
|
||||
## Make targets
|
||||
|
||||
`make deps` (system packages + Rust, one time) · `make` (build) · `make install` (Mac → `~/Applications/AnyBot.app`, Linux → `~/.local/bin/anybot` + menu entry) · `make run` · `make test` · `make doctor` (what's missing on this machine) · `make serve` (headless) · `make setup` (pair this machine to your brain) · `make server` (host the account server here) · `make dist` (release archives into `dist/`) · `make relay` (build the relay alone).
|
||||
|
||||
## FAQ
|
||||
|
||||
- **`Killed` / `signal: 9` while compiling** — out of memory; add swap: `sudo fallocate -l 2G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile`.
|
||||
- **`requires rustc 1.95`** — `rustup update stable`; don't use the distro's rustc.
|
||||
- **`alsa` / `pkg-config` not found** — `make deps`.
|
||||
- **Mac: "cannot verify developer"** — right-click the app › Open, once.
|
||||
- **Server URL unreachable** — DNS A record? ports 80/443 open? `cd ~/AnyBot/deploy/server && docker compose logs caddy`.
|
||||
- **Uninstall** — `make uninstall` removes the program, keeps your bots and chats.
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Multi-device: brain, hands, screen (English summary)
|
||||
|
||||
Canonical document: [docs/multi-device.md](../multi-device.md) (Chinese).
|
||||
|
||||
One AnyBot in your account is the **brain**: it stores every bot, chat, memory and schedule, and does the thinking. Other machines are its **hands** (screen, input, files, browser), phones are web clients. Everything talks through your **account server** (relay); each machine opens an outbound long connection, so nothing needs open ports or a static IP.
|
||||
|
||||
| Role | What it is | How to set |
|
||||
|---|---|---|
|
||||
| Brain | stores and thinks; keep it always on | logging into the account makes it the brain (default) |
|
||||
| Screen | a machine with a window: shows and edits the brain's bots, and acts as hands | Settings › General › Phone & account › Brain › “use this as brain” |
|
||||
| Hands | lets the brain's bots drive this machine | the “Add a computer” one-liner, or `anybot setup` |
|
||||
| Phone | web app on the relay's URL | sign into the same account |
|
||||
|
||||
## Method A — server as relay + brain (recommended)
|
||||
|
||||
A Linux host (≥2 GB RAM) plus one of: a **domain** (A record, 80/443; HTTPS certificates issued automatically), a **Tailscale** HTTPS URL (`https://host.tailnet.ts.net`, no ports opened at all), or a **LAN IP** (self-issued, auto-renewed certificate — trust it once on each device; instructions in the Chinese doc).
|
||||
|
||||
```sh
|
||||
curl -fsSL https://code.30cm.net/daniel.w/AnyBot/raw/branch/main/install.sh | sh -s -- server
|
||||
```
|
||||
|
||||
It asks three questions (URL, account, password), installs Docker if needed, gets HTTPS, creates the account, starts the brain. Update later with `cd ~/AnyBot && make server` (“keep settings, update, restart”). Release archives (built by Gitea Actions on `v*` tags) avoid building on the server: download `AnyBot-<ver>-server-<arch>.tar.gz`, unpack, `cd deploy/server && ./setup.sh`.
|
||||
|
||||
## Method B — a Mac as the brain (home, Tailscale)
|
||||
|
||||
1. `make relay`; install `anybot-relay` under `~/Library/Application Support/AnyBot/relay/` and load `deploy/mac/com.anybot.relay.plist` via `launchctl` (edit the URL inside first).
|
||||
2. `tailscale serve --bg 8780` → you get `https://mac-mini.tailnet.ts.net`.
|
||||
3. AnyBot › Settings › General › Phone & account → enter that URL, create the account, sign in. This Mac is now the brain; other devices pair to it like Method A.
|
||||
|
||||
## Pairing computers (hands)
|
||||
|
||||
“⋯ › Add a computer” on the phone (or Settings on the brain) shows a one-time pairing line like:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://your-domain/…/install.sh | sh -s -- hands https://your-domain 482913
|
||||
```
|
||||
|
||||
The 6-digit code lives 10 minutes, works once, carries no password. The machine installs itself, registers a host token, starts `anybot.service`, and runs at boot (enable `loginctl enable-linger $USER` to keep it running when logged out).
|
||||
|
||||
## When a device can't connect
|
||||
|
||||
- “Brain offline” → the brain machine is off/asleep, AnyBot isn't running there, or it lost the relay connection (each side keeps an outbound connection; a network change recovers by itself in seconds).
|
||||
- A hands machine offline → check `systemctl --user status anybot`; re-run the pairing line to re-pair.
|
||||
- A screen machine keeps a read-only **brain copy** (`brain-copy.json`) so recent chats stay readable while the brain is away.
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# Protocol (English summary)
|
||||
|
||||
Canonical document: [docs/protocol.md](../protocol.md) (Chinese). For anyone modifying the relay or writing another client. All replies are JSON; errors are `{"error": "plain sentence"}` with an HTTP status.
|
||||
|
||||
## Identity
|
||||
|
||||
| Who | Proves itself with | From |
|
||||
|---|---|---|
|
||||
| Person (phone) | `Authorization: Bearer <session>` | `POST /relay/login` |
|
||||
| An AnyBot (host) | `Authorization: Bearer <host token>` | `POST /relay/hosts` (after login) or `POST /relay/pair/claim` (pairing code) |
|
||||
|
||||
The server stores only hashes. Passwords are argon2id (legacy PBKDF2 hashes still sign in; upgraded on next password change).
|
||||
|
||||
## Relay endpoints
|
||||
|
||||
### Account
|
||||
|
||||
| Method | Path | Notes |
|
||||
|---|---|---|
|
||||
| POST | `/relay/signup` | `{user, password, invite}`. First account needs no invite; later ones need `ANYBOT_RELAY_INVITE` |
|
||||
| POST | `/relay/login` | `{user, password, device}` → `{token}`; with 2FA on → `{need_2fa, challenge}` (valid 5 min). Lockout 8 tries / 5 min per IP+account |
|
||||
| POST | `/relay/2fa/verify` | Second step. Sign-in: `{challenge, code}` → `{token}` (TOTP code, or a one-time recovery code → adds `used_recovery`). Enrollment (with session): `{code}` activates the new secret |
|
||||
| POST | `/relay/2fa/enroll` | (session) → `{secret, uri, recovery_codes}` — codes shown once; 2FA is on only after a verified code |
|
||||
| POST | `/relay/2fa/disable` | (session) `{code}` turns 2FA off |
|
||||
| POST | `/relay/sessions/logout` | (session) kicks **every** device of the account, including this one |
|
||||
| POST | `/relay/logout` | ends this session |
|
||||
| GET | `/relay/me` | account, `two_factor`, usable AnyBots (`online`, `uses`, `hands`), signed-in devices, backup presence |
|
||||
|
||||
### Hosts and proxying
|
||||
|
||||
| Method | Path | Notes |
|
||||
|---|---|---|
|
||||
| POST | `/relay/hosts` | (person) `{name}` → `{id, token}`; re-registering a name rotates the token |
|
||||
| POST | `/relay/hosts/<id>/remove` | (person) remove a host |
|
||||
| GET | `/relay/host/ws` | (host) the long connection — see wire format below |
|
||||
| GET | `/relay/host/next` · POST `/relay/host/reply/<id>` | legacy long-polling for hosts without WebSocket |
|
||||
| GET | `/relay/host/peers` · GET/POST `/relay/host/members` | other hosts; team roles (admin/member) |
|
||||
| POST | `/relay/host/notify` | push notification to the account's phones |
|
||||
| POST | `/relay/host/sites/<name>` | publish a bot-made website at `/s/<name>/` |
|
||||
| PUT/GET | `/relay/backup` | encrypted backup; the server cannot open it |
|
||||
|
||||
Proxying: `/h/<host>/api/...` (phone → that AnyBot, role owner/admin/member); `/relay/hosts/<host>/api/sync[/...]` (host → brain's `/api/sync…`, role `mirror`); `/relay/hosts/<host>/api/device/run` (host → hands, role `device`). The relay injects `user`, `role`, `from`. Offline → 503 immediately; no answer in 35 s → 504. VNC streaming: `POST /relay/screen-ticket {host}` → 10-minute ticket; `WebSocket /relay/vnc/<host>?t=|?ticket=` carries binary VNC bytes and typed text.
|
||||
|
||||
## AnyBot's own API (`remote::handle`)
|
||||
|
||||
Reachable only through the relay (or on the LAN with the phone password). Highlights: `GET /api/state?since=` (bots, activity, pending approvals; long-poll ≤20 s), `GET /api/bots/<id>`, `POST /api/bots/<id>/<action>` (`send`, `stop`, `answer`, `read`, `new`, `upload`…), `GET/POST /api/screen/mac` (drive this Mac from the phone, if enabled), `/screen` + `/api/vnc` (view the bot's computer, noVNC), and the brain-sync family: `GET /api/sync?since=`, `POST /api/sync/edit` (`{ops, client, seq}`), `/api/sync/act`, `/api/sync/upload/<bot>`, `GET /api/sync/file`, `POST /api/sync/models`, `POST/GET /api/sync/screen` (thumbnails; `{kind:"hold"|"wake"}`).
|
||||
|
||||
## Wire format (`src/wire.rs`)
|
||||
|
||||
WebSocket binary frames, each message: `[4-byte big-endian header length][header JSON][raw body]` — bodies are not base64'd; `z: true` means the body is deflate-compressed, `z_ok: true` says the asker understands compressed replies.
|
||||
|
||||
| `t` | Direction | Header fields | Meaning |
|
||||
|---|---|---|---|
|
||||
| `hello` | AnyBot → relay | `brain`, `hands` | which brain this machine follows; is it hands |
|
||||
| `job` | relay → AnyBot | `id, method, path, query, user, role, from, z, z_ok` | a request for this machine |
|
||||
| `reply` | AnyBot → relay | `id, status, ctype, headers, z` | answer to a job |
|
||||
| `call` | AnyBot → relay | `id, to, method, path, query, z` | ask another host (only `/api/sync…`, `/api/device/run`) |
|
||||
| `ret` | relay → AnyBot | `id, status, ctype, z, headers` | answer to a call |
|
||||
| `vopen` / `vdata` / `vtext` / `vclose` | both | `sid`, `why` | VNC session lifecycle: open, bytes, typed text, close |
|
||||
|
||||
Both loops poll every 2 ms while busy, 40 ms idle (`wire::Pace`), then flush queued messages; keepalive via WebSocket ping/pong. On disconnect the relay answers pending jobs 503, the AnyBot fails pending calls.
|
||||
|
||||
## Sync ops (`/api/sync/edit`)
|
||||
|
||||
`set` (one key; `next_*` counters only grow), `patch` (map/list item-level), `merge` (providers/skills/mcp_servers on move), `bot_add` (id collision → new id), `bot_del` (primary bot protected), `bot_set`, `bot_patch` (routines keep the brain's run times), `bot_read`, `bot_order`.
|
||||
|
||||
`patch` shapes: `{"value": …}` replaces; `{"map": {"put": {…}, "del": […]}}`; `{"list": {"put": […], "del": [keys], "order": [keys]}}` where keys are item `id`s (or `name`, raw values as-is). Items without id/name, or duplicate keys → send the whole value instead.
|
||||
Loading…
Reference in New Issue