Commit Graph

119 Commits

Author SHA1 Message Date
王性驊 212d18b542 Merge branch 'smooth-remote': people watch and help on the bots' computer through VNC (noVNC) over the account server; unused and duplicated code removed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:27:11 +08:00
王性驊 2867c8e822 Remove what nothing uses and keep one copy of what was written several times
Unused (found by taking away the dead-code allowances; nothing else used them, tests aside):
computer::specs, quiet_button, the never-made Poke::Spin, ten icons and their SVGs, the
theme's chrome/bubble_agent colours, ImportRow.line, voice's multipart/transcribe/speak
wrappers, and the old keychain writers vault::save/remove/index_add with their live test
(the vault store replaced them; reading old keychain entries for the import stays).

One copy each: percent-encoding (httpd::url_encode, was four), tool arguments (tools::args_of,
was three), the WebSocket handshake (httpd::accept_websocket, was three), the read pacing
(wire::Pace, was two: relay and app), docker with input and a time limit (dk_with_input, was
two). Docs: FEATURES §看畫面 (VNC), protocol (v* frames, /screen, tickets), architecture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:21:47 +08:00
王性驊 02bca39b98 People watch and help on the bots' computer through VNC (noVNC), carried by the account server
The core: the bot works the computer; a person watches it like a colleague, and takes over
when it needs them. VNC is made for that (only what changed, every mouse move and key, touch on
phones), so the pictures sent one by one go, and with them their own input path.

- vnc.rs: a VNC proxy to the box's x11vnc through one docker exec (a Python bridge that ends
  with either side). It signs in to the box with the box's own password (its VNC_PASSWORD) and
  offers the viewer no password, so the password never leaves the brain. The person's clicks
  and keys make the bot's steps wait until 8 s after they stop; text typed in the viewer's box
  (any language) is pasted in the box.
- Account server: /screen (the viewer page) and /novnc.js (noVNC 1.6.0, MPL-2.0, bundled);
  /relay/vnc/<host> takes the browser's WebSocket (account session, or a 10-minute ticket the
  app asks for with POST /relay/screen-ticket; owners and admins) and carries it over that
  AnyBot's lasting connection (vopen / vdata / vtext / vclose). Both ends of that connection
  wait 2 ms instead of 40 ms to read while things move: a request and its answer took up to
  160 ms more than needed.
- Phone page: 看 Bot 的電腦 and 接管 open the viewer page (also on the local network: /api/vnc on
  the AnyBot's own phone server). This Mac's own screen moved to the ⋯ menu.
- Mac app: 看畫面 opens the viewer in the browser (a screen of a brain: the brain's computer by
  ticket; else this computer's box, noVNC straight from it). The in-app picture viewer, the
  live JPEG stream, the person's click/move/scroll/key endpoints and the input shell are gone.
- Opening a Mac app uses LaunchServices (`open -a`): agent-desktop's launch waited on the window
  through accessibility and failed without 輔助使用 now and then.

Measured on this Mac (made-up account, local relay + headless brain + the real box): viewer
signed in in 0.4–0.7 s, a request and its answer through relay + brain + box 25–58 ms median;
headless Chrome shows the desktop live in /screen. remote_e2e 28/28 ×2, 149 unit tests, relay 11.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 23:05:44 +08:00
王性驊 6c0c4d7ecd Watching and using the bots' computer from afar: pictures as the screen changes, input through a lasting shell
- One lasting docker exec takes the desktop as a JPEG inside the box and sends a picture only
  when it changed (quicker while it changes, slower while still). Viewers ask for the picture
  after the one they have and get it at once. It was one exec per picture (a 1 MB PNG decoded
  and encoded again on the brain) and a fixed 0.7 s / 1 s pause: about one picture a second.
  Measured on the Mac's box: first picture 45 ms, ~90 KB (was ~1.1 MB), 9–19 pictures a second
  while it changes; viewers take at most ~8 a second over the network.
- The loop in the box ends the one before it, ends itself when a write fails ("0" lines while
  still) and is ended when nobody watched for 10 s (ending docker exec on this side left it
  running in the box).
- The person's clicks and keys go through one lasting shell in the box (text as base64), not a
  docker exec each.
- Answers over the lasting connection keep their headers (the relay dropped them): a picture's
  number reaches the app. JPEG/PNG/PDF/zip bodies are no longer deflated again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:34:36 +08:00
王性驊 237fd9f756 Merge branch 'one-screen-path': one way to act on a screen, the result comes back with each step, phone taps exact, typing under any input method, Docker steps in one exec
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 22:04:30 +08:00
王性驊 aa9b010276 One way to act on a screen: a real click or keystroke, wherever the bot looked
- Acting by an app's number (read_app's e7) is now a real click at that control's spot, the
  same path as screenshot coordinates. agent-desktop only reads; its own actions (a second set
  with its own key names and failures: no typing in terminals, "/" and "`" failing off the main
  thread) are gone, with computer_use_select, background clicks and press_key's app.
- Every action comes back with the screen after it (or, with no picture allowed, the front
  app's numbered controls), so the bot no longer spends a step on a screenshot to check.
- Coordinates map without remembering a screenshot: the picture's size depends only on the
  screen. The phone's taps go straight to the screen as the person's, in the pixels of the
  picture it was sent; they used to borrow the bot's last screenshot scale and landed 1.25x off
  on the bots' computer, or failed with "screenshot first".
- Mac typing pastes (⌘V, clipboard put back): with 注音 on, text sent as key events vanished
  while return arrived, so commands typed into a terminal ran empty. Bare printing keys
  ("slash", "r") are typed the same way; keys like "slash" and "grave" have names.
- The bots' Docker computer: one docker exec per step (action, settle, picture), never
  `mousemove --sync` (it waits forever when the pointer is already there: a second click on
  the same spot hung two minutes), open_app (chromium = the browser browser_* drive, xterm).

Tests: box_screen_is_exact (real Docker: bot and phone coordinates exact, same spot twice in
under 2 s, ASCII + Chinese + named key typed), Calculator by numbers and Terminal typing under
注音 through the signed app, remote_e2e 28/28, Linux build + 151 tests + x11_live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:51:19 +08:00
王性驊 26fa2a8ee9 Commands on another computer run in the bot's work folder and finish before the relay gives up; read_terminal goes to the same computer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 21:28:32 +08:00
王性驊 db028b0b5b Merge branch 'computer-names': every computer by name, the person's computer, hand-off, Wayland screenshot fix, E2E tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:57:14 +08:00
王性驊 c87d598e64 E2E: remote screenshot, click and typing; a Linux Wayland run with a full runtime folder
remote_e2e.py: with E2E_SCREENSHOT=1 a screenshot of the hands comes back to the model as a
JPEG; with E2E_TYPE=1 also a click, typing (Chinese too) and Enter in a terminal there. The fake
model no longer takes a screenshot's picture message for the person speaking. E2E_APP picks the
app to open. deploy/test/wayland: the same test on a headless sway in Docker whose
XDG_RUNTIME_DIR is full (31/31).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:28:52 +08:00
王性驊 70444c683c Wayland screenshots can't crash the desktop when /run/user is full
The screenshot buffer was a sparse file in XDG_RUNTIME_DIR. With that folder full
(a 1.6 GB Hyprland log), Hyprland wrote the pixels into pages that couldn't exist
and died with SIGBUS in CScreenshareFrame::copyShm. Now the buffer is a memfd with
every page reserved up front; with no room the screenshot fails in AnyBot instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 20:06:46 +08:00
王性驊 e44614736f 「我的電腦」 follows the computer the person types on, also mid-turn and after a restart
A turn started from the phone (not a computer) kept 「don't know」 for its whole run: a message
typed on the Mac while it waited on a hand-off had its 「我的電腦」 moved to the bot's own computer.
Each step now asks which computer the person last typed on, and the brain keeps it beside its
data (person-computer.txt) across restarts. remote_e2e: a second computer, and a mid-turn
message from the other one (fails without the fix); 28 checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 19:41:49 +08:00
王性驊 61e0531d32 One AnyBot icon everywhere; the phone page fills the screen and shows the app's bot faces
- New icon: the bot's cloud body and slanted eyes (as in the app's avatars) on a dark tile —
  the Mac/Linux app, browser tabs, iPhone home screen (PNG: it ignores SVG), Android and
  notifications. The web had its own ring-and-dot.
- anybot --write-icon out.png size [file.svg] draws any SVG (for the web PNGs).
- Phone: lists that reach the bottom keep their last row above the home bar and Safari's
  toolbar (it looked cut off); bots show their shape, colour and eyes instead of a letter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 19:20:29 +08:00
王性驊 ad0234d0ce Phone: the computer list waits for a brain that is restarting and goes in by itself (it sat on 離線 until tapped)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 19:07:43 +08:00
王性驊 60f4fa7411 Reading or using an app without 輔助使用 asks for it and says so in words (it answered PERM_DENIED and asked nobody)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:50:08 +08:00
王性驊 d52def0f46 A click by number reads as the element it is in the question (點 e7(按鈕「7」)), not (0, 0)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:25:46 +08:00
王性驊 102746d9e8 A brain's box can go by its own name (ANYBOT_BOX_NAME); the server's brain takes over its old box under anybot-brain-box
A desktop AnyBot left on the server (older build) took "anybot-box" for its own and paused it
every ~20 s as idle while the brain woke it: the bots' computer kept showing as asleep. The
brain now renames its box once (files are in the volume, kept by name) and uses that name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:20:45 +08:00
王性驊 06801f549d Remote brain: a computer that just came on is known at once; an offline computer is said, not swapped; snapshots survive an open browser; end-to-end test
- The brain looked the account's computers up once a minute: for up to a minute after the Mac
  came on, 「我的電腦」 was unknown and steps went elsewhere. Now every 15 s, and at once when a
  message comes from a computer it doesn't know yet.
- A step naming a computer that is off says so in words instead of running on the chosen one
  with a note that the person hadn't asked.
- Snapshots: tar's 'file changed' (the open browser) is not a failure; browser caches are left
  out; ten minutes for a large home folder.
- box_end_to_end clicks the terminal where it opened; restore only with ANYBOT_TEST_RESTORE=1.
- deploy/test/remote_e2e.py: account server + headless brain + hands + scripted model, 27 checks
  (computers, redirect, agent-desktop on the hands, hand-off 3 ways, phone view, sync copy,
  edits, hands offline/back, brain restart, clear). anybot --sample-state for such tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:14:23 +08:00
王性驊 46c0684cb4 需要處理: a bot hands a step to the person with its computer's live screen in the chat; the window fills to the top; the 電腦 card and viewer keep their text in place
- hand_over_computer (the original's box hand-off card): instruction, live screen with 接管,
  跳過 / 我完成了 / 接管; 我完成了 also in the viewer; a typed message answers it too. Header,
  sidebar and notifications say 等你處理 / 「{name}:需要你」; the phone shows it with 接管.
- Mac: the content reaches the top (no grey title bar); the top strip drags the window.
- Panel 電腦 card: each line stops before the picture; the brain's name on its own line.
- Viewer: buttons first, the title ends in … when narrow; the picture is centred.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:29:55 +08:00
王性驊 c3e2e28ec4 Only the brain keeps its box awake: a computer showing another brain leaves its own box alone
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:10:05 +08:00
王性驊 ab564f706f The bots' computer never sleeps; a server brain doesn't work on the server; the computer choice lives in the side panel; bots default to their own computer
- No idle pause: an asleep box is woken; its files are saved once a day instead of before sleep.
- A headless brain offers no Local computer; attachments it receives go into the bot's own one.
- 「你的電腦」 kept by older versions means the bot's own computer; only an explicit choice moves it.
- The composer chip moved to the panel's 電腦 card (做事的電腦).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 17:07:04 +08:00
王性驊 5b2c974f3c Mac permissions: ask at most every ten minutes; one fixed signing key keeps them across updates; open apps by their shown name
- Every untrusted step popped macOS's prompt and opened Settings; a remote brain's steps covered
  the screen. Now once per pane per ten minutes.
- Ad hoc signing made each build a new app to macOS, so 輔助使用/螢幕錄製 silently stopped
  working after updates (agent-desktop PERM_DENIED). deploy/mac/sign.sh signs with a local key
  kept in its own keychain under AnyBot's folder.
- computer_use_open_app finds 「計算機」 by display name and says the app opened even when its
  window can't be read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:57:44 +08:00
王性驊 6b0a0babf1 Bot settings: a short hint keeps the computer row inside its card
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:49:39 +08:00
王性驊 3132f8ce91 Every computer goes by its own name; a server brain works on the computer the person uses
- A bot's own computer is 「<bot> 的電腦」; this one and the account's others go by their names.
  The server brain picked 你的電腦 by sorted host id, so a desktop left on the server won.
- 「我的電腦」 is the computer the person typed on (the relay names it); unknown means ask.
- A step moved back to the chosen computer tells the bot so.
- Composer chip and bot settings choose the computer by name; a screen asks the brain.
- A screen's 電腦 card and viewer show the brain's computer, not this one's box.
- A screen's chat copy that falls short asks for everything again instead of appending;
  清除聊天記錄 on a screen clears the brain's chat.
- Model requests to a private address go through the person's computer first.
- Phone typing on the box sends the text the tool reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 16:47:28 +08:00
王性驊 2a24606024 A multi-step turn answers once: no narrating between tool calls 2026-11-05 16:16:25 +08:00
王性驊 1a80bfdcc2 An open browser on the box buys it four times the idle time, and an open noVNC viewer keeps it awake: a bot that opened the browser left the person a frozen screen 2026-11-05 16:11:25 +08:00
王性驊 cd3f7b5c72 A bot that is unsure which computer the person means asks first instead of picking one 2026-11-05 16:06:53 +08:00
王性驊 591626408f A server brain without hands calls its server the brain server, and tells the model which computers really exist 2026-11-05 16:03:34 +08:00
王性驊 9d8f7bbead Merge pull request 'Audit fixes: sync chain, box, desktop, install, UI (B1-B30, F1-F5)' (#5) from brain-computers into main
Reviewed-on: #5
2026-11-05 07:52:28 +00:00
王性驊 dabb4113c2 Setup quotes service paths and writes launcher and unit files atomically 2026-11-05 15:48:22 +08:00
王性驊 7391e3a101 Texture caches capped; big previews decode off the UI thread; helper processes reaped 2026-11-05 15:48:22 +08:00
王性驊 21b917b0eb Demo recording keeps its last events off the UI thread; dialogs, screensaver probe and installer traps fixed 2026-11-05 15:48:22 +08:00
王性驊 a8265409a1 Docker calls time out; the box has a VNC key, limits and sandboxed files; X11/Wayland eyes and hands stop leaking 2026-11-05 15:48:22 +08:00
王性驊 5dfb5d1c6c Routines follow the clock in each timezone; channels resume where they stopped; pages find checks the bot 2026-11-05 15:48:22 +08:00
王性驊 ca30f708ed Compaction keeps tool pairs; MCP locks per server; the chosen computer needs whole words to move 2026-11-05 15:48:20 +08:00
王性驊 caa580fce9 Export drops threads; a broken keyring stops with the secret recoverable 2026-11-05 15:48:20 +08:00
王性驊 96174c4776 Stream deltas keep tool-call slots; chunked bodies accepted; portal sessions race-safe 2026-11-05 15:48:20 +08:00
王性驊 4fa5fab870 Remote: per-IP login lock, egress peers and timeouts narrowed; brain-mirror link covered by tests 2026-11-05 15:48:20 +08:00
王性驊 80fa0cb11f Relay locks, rate-limits and answers by the real peer (proxy trust opt-in); phone page follows 2026-11-05 15:48:20 +08:00
王性驊 c1e4994219 Cloud brain uses a model on the person's network through their computer; the chosen computer is where a bot works
- A brain that can't reach a model at a private address (e.g. Qwen at 10.x) sends the request
  through its hands; the hands only forward to addresses on their own network, and only for
  their brain (uses the existing device channel, so the account server is unchanged).
- The computer chosen for a bot is binding: a step naming another computer goes to the chosen
  one unless the person's message names that one (「在我的 Mac 上」).
- Browser steps follow the computer: Bot 的電腦 drives the Chromium on its own desktop (through
  docker exec, no new port), 你的電腦 its browser; before, they always used this computer's.

Checked: on this Mac, Bot 的電腦 browsing opened example.com in the box's Chromium and an
unasked 你的電腦 command ran in the box; 「在我的 Mac 上」 ran on the Mac. Linux brain in Docker
with 10.0.0.0/8 blocked + this Mac as hands: model calls went through the Mac, shell on
你的電腦 answered from the Mac, browser defaulted to the Mac's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:00:31 +08:00
王性驊 4c0db53647 Replies in Traditional Chinese when that is the setting; a cloud brain's browser is the person's
- 語言 繁體中文: a reply's Simplified characters are turned into Traditional with Taiwan
  wording (zhconv), since some models (Qwen) slip despite the instruction.
- A brain on a server: browser steps go to the person's computer (its hands) unless the
  server's own browser is named; 你的電腦 is listed first.
- update_bot says the default computer it set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 14:46:42 +08:00
王性驊 94cadca1b8 A brain on a server knows which computer is the person's; the phone page opens the brain in use
- On a server brain whose hands are the person's computer, 你的電腦 is that computer;
  the server itself is 大腦伺服器 (its browser too), so 「我的 Mac」 means the Mac.
- A brain with its own hands no longer lists the account's other brains as computers.
- 查看電腦 lists the computers the bot really has, with their real names.
- Phone page: with several brains on, it opens the one the account's computers use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 14:40:01 +08:00
王性驊 8f79c33b35 Merge branch 'first-run-guide': no starter bots, a guide, sync after signing in
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 14:25:01 +08:00
王性驊 d78e75ebec First start: no bots, a guide instead; signing in syncs this computer and the account
- A new install (desktop or anybot --serve) starts with no bots; the main window shows
  「歡迎使用 AnyBot」: connect a model, make the first bot, or sign in.
- Old data whose five sample bots were never used starts empty too.
- Signing in syncs right away: an online brain of the account is used and this computer's
  bots are moved there; otherwise the backup's missing bots are taken in and backed up again.
- The five sample bots remain only for screenshots and tests (State::samples).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 14:25:01 +08:00
王性驊 5dfb086071 Merge branch 'easy-install': 頁面, Tailscale servers, hands fixes, one-step install updates
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-05 08:53:29 +08:00
王性驊 c0dfef0995 Docs: 頁面, Tailscale servers, no-questions install, hands' virtual screen and updates
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-05 08:35:47 +08:00
王性驊 b05bec66f4 Pages: phone page fits narrow screens; long stat values a size smaller; ANYBOT_SHOT_PAGE
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-05 08:33:23 +08:00
王性驊 d2f1c76d01 Browser steps that needed a yes ran on the brain, not on the hands named
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-05 08:24:36 +08:00
王性驊 f2d89ca358 看圖小幫手 also looks at the newest screenshot
A model that can't see pictures got only 「截圖已拍好,但看不到」 for computer_use_screenshot.
Now the newest screenshot is described by a model of the person's that can (windows, text,
buttons and roughly where they are); older screenshots stay a short note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-05 08:21:34 +08:00
王性驊 cd7a4207cc Browser tools on a brain without a browser go to its hands
A brain on a server has no AnyBot browser, and browser_open/read were sent to it even when
the bot named a 手腳 computer. Now a named computer wins, and with none named the hands'
browser is used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-05 08:17:07 +08:00
王性驊 370dbd91f7 Hands: their own virtual screen, even next to a logged-in desktop
A service set up without a screen (over SSH, or on Wayland) used to pick up the desktop
session's DISPLAY/WAYLAND_DISPLAY from systemd, so screenshots waited for a 「分享螢幕」
answer nobody could give. Now it starts its own virtual screen; `anybot setup --real-screen`
keeps the desktop. `anybot setup --refresh-service` rewrites the start script after an update
(install.sh runs it), so joined computers pick up such changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-05 08:10:13 +08:00