AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
// Screen control on 你的電腦 (computer use), like Grok Bot's Computer Use helper: the bot looks at
// the screen and uses the mouse and keyboard. Same tool names and arguments as the original's
// local daemon (`computer_use_screenshot`, `computer_use_click` {method, x, y, button, count,
// modifier_keys}, `computer_use_scroll`, `computer_use_mouse_move`, `computer_use_drag`,
// `computer_use_typing` {value}, `computer_use_press_key` {key, hold_duration_ms}).
//
// Screenshot sizing (local-exec-daemon main.cjs): `zct` fits the long edge to 1024 (Gct) keeping
// both edges at least 8 (Hs); images stay under 1 MiB (mg = 1024*1024); JPEG is tried at quality
// 85, 70, 55 ($3r), taking the first that fits, else the smallest.
use base64 ::Engine ;
use serde_json ::{ json , Value } ;
use std ::sync ::Mutex ;
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 13:51:19 +00:00
pub const NAMES : [ & str ; 11 ] = [ " computer_use_screenshot " , " computer_use_click " , " computer_use_typing " , " computer_use_press_key " , " computer_use_scroll " , " computer_use_mouse_move " , " computer_use_drag " , " computer_use_mouse_button " , " computer_use_check_permissions " , " computer_use_read_app " , " computer_use_open_app " ] ;
/// Wait after an action before taking the picture that comes back with it (menus open, pages
/// start to change; estimated).
pub const SETTLE_MS : u64 = 500 ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
/// The person took control back from the bot (the floating bar): screen actions wait.
pub static HELD_BY_PERSON : std ::sync ::atomic ::AtomicBool = std ::sync ::atomic ::AtomicBool ::new ( false ) ;
/// When the bot last moved the mouse or typed on this Mac (for the floating bar), and who.
pub static LAST_ACTION : Mutex < Option < ( std ::time ::Instant , String ) > > = Mutex ::new ( None ) ;
2026-11-05 07:48:22 +00:00
/// Spawn a short-lived helper (`open`, a player, …) and reap it in the background:
/// spawn-without-wait left one zombie per call for the app's whole life.
pub fn spawn_reaped ( c : & mut std ::process ::Command ) -> std ::io ::Result < ( ) > {
let mut child = c . spawn ( ) ? ;
std ::thread ::spawn ( move | | {
let _ = child . wait ( ) ;
} ) ;
Ok ( ( ) )
}
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
/// Longest screenshot edge sent to the model (Gct).
pub const MAX_LONG_EDGE : u32 = 1024 ;
/// Shortest edge never goes below this (Hs).
pub const MIN_EDGE : u32 = 8 ;
/// Largest screenshot sent (mg = 1024*1024).
pub const MAX_IMAGE_BYTES : usize = 1048576 ;
/// JPEG qualities tried in order ($3r).
pub const JPEG_QUALITIES : [ u8 ; 3 ] = [ 85 , 70 , 55 ] ;
/// Screenshots kept in what's sent to the model; older ones become a note (estimated).
pub const KEEP_SCREENSHOTS : usize = 3 ;
/// Most UTF-16 units per typed chunk; characters are never split (CUTypedCharacters.utf16Chunks
/// @0x1000beb00: capacity 0x14, a new chunk when the next character would pass 20).
#[ cfg_attr(not(target_os = " macos " ), allow(dead_code)) ]
pub const TYPE_CHUNK_UNITS : usize = 20 ;
/// Virtual key the typed text rides on (postChunks @0x1000beebc: CGEventCreateKeyboardEvent(_, 0x80, _)).
#[ cfg_attr(not(target_os = " macos " ), allow(dead_code)) ]
const TYPE_KEY_CODE : u16 = 128 ;
/// Key down → key up for typed chunks and pressed keys (postChunks / handlePressKey: sleep 0.035 s).
pub const KEY_HOLD_SECS : f64 = 0.035 ;
/// Between typed chunks and between keys of one press (postChunks / handlePressKey: sleep 0.02 s).
pub const KEY_GAP_SECS : f64 = 0.02 ;
/// Mouse down → up when no hold is given (CUActuator.click @0x100010120: hold > 0 ? hold : 0.03).
pub const CLICK_HOLD_SECS : f64 = 0.03 ;
/// Between the clicks of a double/triple click (CUActuator.click: sleep 0.08 s).
pub const CLICK_REPEAT_GAP_SECS : f64 = 0.08 ;
/// Interpolated dragged events per path segment (handleDrag @0x10009dee4 passes steps: 6).
pub const DRAG_STEPS : usize = 6 ;
/// Hold after pressing, before moving (handleDrag @0x10009dee4 passes holdSeconds: 0.04).
pub const DRAG_HOLD_SECS : f64 = 0.04 ;
/// The dragged-event points: each segment split into `DRAG_STEPS` equal parts, ending on its end
/// point (CURemoteEventPoster.drag @0x10008c34c: start + delta * i / steps, i = 1…steps).
pub fn drag_points ( path : & [ ( f64 , f64 ) ] ) -> Vec < ( f64 , f64 ) > {
path . windows ( 2 )
. flat_map ( | w | {
let ( ( ax , ay ) , ( bx , by ) ) = ( w [ 0 ] , w [ 1 ] ) ;
( 1 ..= DRAG_STEPS ) . map ( move | i | {
let t = i as f64 / DRAG_STEPS as f64 ;
( ax + ( bx - ax ) * t , ay + ( by - ay ) * t )
} )
} )
. collect ( )
}
/// Scroll steps when none is given, and the allowed range (handleScroll @0x10009e200: default 3,
/// max(amount, 1), min(…, 20)).
pub const SCROLL_DEFAULT_AMOUNT : i64 = 3 ;
pub const SCROLL_MAX_AMOUNT : i64 = 20 ;
/// Pixels per scroll step (handleScroll @0x10009e200: (amount + amount) << 3).
pub const SCROLL_PIXELS_PER_STEP : i32 = 16 ;
pub fn scroll_amount ( amount : Option < i64 > ) -> i32 {
amount . unwrap_or ( SCROLL_DEFAULT_AMOUNT ) . clamp ( 1 , SCROLL_MAX_AMOUNT ) as i32
}
/// (vertical, horizontal) pixel deltas: up and left positive, down and right negative.
pub fn scroll_pixels ( direction : & str , amount : i32 ) -> Option < ( i32 , i32 ) > {
let px = amount * SCROLL_PIXELS_PER_STEP ;
match direction {
" up " = > Some ( ( px , 0 ) ) ,
" down " = > Some ( ( - px , 0 ) ) ,
" left " = > Some ( ( 0 , px ) ) ,
" right " = > Some ( ( 0 , - px ) ) ,
_ = > None ,
}
}
/// Pause between mouse move / drag steps (estimated).
#[ cfg_attr(not(target_os = " macos " ), allow(dead_code)) ]
const EVENT_GAP_MS : u64 = 8 ;
/// Longest key hold allowed (estimated).
const MAX_HOLD_MS : u64 = 5000 ;
/// utf16Chunks: split text into chunks of at most `TYPE_CHUNK_UNITS` UTF-16 units without
/// splitting a character (a character longer than that gets its own chunk).
#[ cfg_attr(not(target_os = " macos " ), allow(dead_code)) ]
pub fn utf16_chunks ( text : & str ) -> Vec < Vec < u16 > > {
let mut out : Vec < Vec < u16 > > = vec! [ ] ;
let mut cur : Vec < u16 > = Vec ::with_capacity ( TYPE_CHUNK_UNITS ) ;
for c in text . chars ( ) {
let mut buf = [ 0 u16 ; 2 ] ;
let units = c . encode_utf16 ( & mut buf ) ;
if ! cur . is_empty ( ) & & cur . len ( ) + units . len ( ) > TYPE_CHUNK_UNITS {
out . push ( std ::mem ::take ( & mut cur ) ) ;
}
cur . extend_from_slice ( units ) ;
}
if ! cur . is_empty ( ) {
out . push ( cur ) ;
}
out
}
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 13:51:19 +00:00
/// One key of a press: a key with its modifiers, or a character typed (「@」「?」 have no key of
/// their own on every keyboard; refusing them sent the bot looking for workarounds).
#[ derive(Debug, PartialEq) ]
pub enum Key {
Combo ( u16 , u64 ) ,
Char ( String ) ,
}
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
/// "cmd+a delete" → each key of one press, in order (handlePressKey presses a list of keys).
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 13:51:19 +00:00
pub fn key_plan ( keys : & str ) -> Result < Vec < Key > , String > {
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let list : Vec < & str > = keys . split_whitespace ( ) . collect ( ) ;
if list . is_empty ( ) {
return Err ( " 請給要按的鍵 " . into ( ) ) ;
}
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 13:51:19 +00:00
list . into_iter ( )
. map ( | k | match parse_combo ( k ) {
// A letter, digit or symbol without a modifier is a character: typed, so an input
// method (注音) can't turn it into a syllable. Named symbol keys mean their character.
Ok ( ( c , 0 ) ) if printable_of ( c ) . is_some ( ) = > Ok ( Key ::Char ( printable_of ( c ) . unwrap_or_default ( ) ) ) ,
Ok ( ( c , f ) ) = > Ok ( Key ::Combo ( c , f ) ) ,
Err ( _ ) if k . chars ( ) . count ( ) = = 1 = > Ok ( Key ::Char ( k . to_string ( ) ) ) ,
Err ( e ) = > Err ( e ) ,
} )
. collect ( )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
/// The character a printing key gives without shift (US layout), or None for keys like return.
fn printable_of ( code : u16 ) -> Option < String > {
const LETTERS : [ u16 ; 26 ] = [ 0 , 11 , 8 , 2 , 14 , 3 , 5 , 4 , 34 , 38 , 40 , 37 , 46 , 45 , 31 , 35 , 12 , 15 , 1 , 17 , 32 , 9 , 13 , 7 , 16 , 6 ] ;
const DIGITS : [ u16 ; 10 ] = [ 29 , 18 , 19 , 20 , 21 , 23 , 22 , 26 , 28 , 25 ] ;
if let Some ( i ) = LETTERS . iter ( ) . position ( | c | * c = = code ) {
return Some ( ( ( b 'a' + i as u8 ) as char ) . to_string ( ) ) ;
}
if let Some ( i ) = DIGITS . iter ( ) . position ( | c | * c = = code ) {
return Some ( ( ( b '0' + i as u8 ) as char ) . to_string ( ) ) ;
}
let c = match code {
27 = > '-' ,
24 = > '=' ,
33 = > '[' ,
30 = > ']' ,
41 = > ';' ,
39 = > '\'' ,
43 = > ',' ,
47 = > '.' ,
44 = > '/' ,
42 = > '\\' ,
50 = > '`' ,
_ = > return None ,
} ;
Some ( c . to_string ( ) )
}
/// The picture the bot and the phone get of this screen: its size, always the same for the same
/// screen. Coordinates on it map to the screen without remembering any earlier screenshot (that
/// hidden state made clicks fail with "screenshot first" and land wrong for a viewer whose
/// picture was a different size).
pub fn shot_size ( ) -> ( u32 , u32 ) {
let ( px , py ) = mac ::screen_pixels ( ) ;
let ( w , h , _ ) = fit ( px . max ( 1 ) , py . max ( 1 ) ) ;
( w , h )
}
/// Screen points (the mouse's units) per picture pixel.
pub fn scale_here ( ) -> ( f64 , f64 ) {
let ( pw , ph ) = mac ::screen_points ( ) ;
let ( w , h ) = shot_size ( ) ;
( pw / w . max ( 1 ) as f64 , ph / h . max ( 1 ) as f64 )
}
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
pub fn is_computer_tool ( name : & str ) -> bool {
NAMES . contains ( & name )
}
pub fn label ( name : & str ) -> & 'static str {
match name {
" computer_use_screenshot " = > " 看你的螢幕 " ,
" computer_use_click " = > " 點一下 " ,
" computer_use_typing " = > " 打字 " ,
" computer_use_press_key " = > " 按鍵 " ,
" computer_use_scroll " = > " 捲動 " ,
" computer_use_mouse_move " = > " 移動滑鼠 " ,
" computer_use_drag " = > " 拖曳 " ,
" computer_use_mouse_button " = > " 按住或放開滑鼠 " ,
" computer_use_check_permissions " = > " 檢查螢幕權限 " ,
2026-11-04 09:19:59 +00:00
" computer_use_read_app " = > " 讀 App 畫面 " ,
" computer_use_open_app " = > " 打開 App " ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
_ = > " 操作畫面 " ,
}
}
fn args_of ( args : & str ) -> Value {
serde_json ::from_str ( if args . trim ( ) . is_empty ( ) { " {} " } else { args } ) . unwrap_or ( Value ::Null )
}
pub fn describe ( name : & str , args : & str ) -> String {
let v = args_of ( args ) ;
2026-11-05 10:25:46 +00:00
// By number from the app's list (e7), else where on the screen (it said "(0, 0)" for every
// click by number).
let xy = | | match v [ " ref " ] . as_str ( ) . map ( str ::trim ) . filter ( | r | ! r . is_empty ( ) ) {
Some ( r ) = > r . to_string ( ) ,
None = > format! ( " ( {} , {} ) " , v [ " x " ] . as_f64 ( ) . unwrap_or ( 0.0 ) , v [ " y " ] . as_f64 ( ) . unwrap_or ( 0.0 ) ) ,
} ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
match name {
" computer_use_screenshot " = > " 螢幕截圖 " . into ( ) ,
" computer_use_click " = > {
let b = v [ " button " ] . as_str ( ) . unwrap_or ( " left " ) ;
let n = v [ " count " ] . as_u64 ( ) . unwrap_or ( 1 ) ;
format! ( " {} {} {} {} " , if b = = " right " { " 右鍵 " } else { " " } , if n > = 2 { " 連點 " } else { " 點 " } , if n > = 2 { format! ( " {n} 下 " ) } else { String ::new ( ) } , xy ( ) )
}
" computer_use_typing " = > {
let t = v [ " value " ] . as_str ( ) . unwrap_or ( " " ) ;
let short : String = t . chars ( ) . take ( 60 ) . collect ( ) ;
2026-11-05 10:25:46 +00:00
let at = v [ " ref " ] . as_str ( ) . filter ( | r | ! r . trim ( ) . is_empty ( ) ) . map ( | r | format! ( " 在 {r} " ) ) . unwrap_or_default ( ) ;
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 13:51:19 +00:00
format! ( " {at} 「 {short} {} 」 {} " , if t . chars ( ) . count ( ) > 60 { " … " } else { " " } , if v [ " submit " ] . as_bool ( ) . unwrap_or ( false ) { " 再按 return " } else { " " } )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
" computer_use_press_key " = > v [ " key " ] . as_str ( ) . unwrap_or ( " " ) . to_string ( ) ,
" computer_use_scroll " = > format! ( " {} {} 格,在 {} " , v [ " direction " ] . as_str ( ) . unwrap_or ( " down " ) , v [ " amount " ] . as_i64 ( ) . unwrap_or ( 3 ) , xy ( ) ) ,
" computer_use_mouse_move " = > xy ( ) ,
" computer_use_drag " = > format! ( " {} 個點 " , v [ " path " ] . as_array ( ) . map ( | a | a . len ( ) ) . unwrap_or ( 0 ) ) ,
" computer_use_mouse_button " = > format! ( " {} {} " , if v [ " action " ] . as_str ( ) = = Some ( " release " ) { " 放開 " } else { " 按住 " } , if v [ " x " ] . is_null ( ) { String ::new ( ) } else { format! ( " {} " , xy ( ) ) } ) ,
" computer_use_check_permissions " = > " 螢幕錄製、輔助使用 " . into ( ) ,
2026-11-04 09:19:59 +00:00
" computer_use_read_app " | " computer_use_open_app " = > v [ " app " ] . as_str ( ) . filter ( | a | ! a . is_empty ( ) ) . unwrap_or ( " 最前面的 App " ) . to_string ( ) ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
_ = > String ::new ( ) ,
}
}
#[ allow(dead_code) ]
pub fn specs ( ) -> Vec < Value > {
2026-11-05 08:47:28 +00:00
specs_for ( & [ ] , " " )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
/// The screen tools, each with a `computer` choice. One way to act (a real click or keystroke),
/// two ways to see: the picture, or an app's controls as numbered text.
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
pub fn specs_for ( computers : & [ String ] , default : & str ) -> Vec < Value > {
let mods = json! ( { " type " : " array " , " items " : { " type " : " string " , " enum " : [ " cmd " , " shift " , " option " , " control " ] } } ) ;
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 13:51:19 +00:00
let rf = json! ( { " type " : " string " , " description " : " A control's number from computer_use_read_app (e.g. e12), instead of x, y " } ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let mut v = vec! [
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 13:51:19 +00:00
json! ( { " name " : " computer_use_screenshot " , " description " : " See a computer's screen. x, y in the other computer_use tools are pixels of this picture. Every action below already returns a fresh picture of the result, so call this only to start or to look again. " ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
" parameters " : { " type " : " object " , " properties " : { } } } ) ,
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 13:51:19 +00:00
json! ( { " name " : " computer_use_click " , " description " : " Click at x, y (pixels of the latest picture), or on a control by its number (ref) from computer_use_read_app. Returns the screen after the click. " ,
" parameters " : { " type " : " object " , " properties " : { " x " : { " type " : " number " } , " y " : { " type " : " number " } , " ref " : rf , " button " : { " type " : " string " , " enum " : [ " left " , " right " , " middle " ] } , " count " : { " type " : " integer " , " description " : " 2 for a double click " } , " modifier_keys " : mods } } } ) ,
json! ( { " name " : " computer_use_typing " , " description " : " Type text (any language) where the cursor is, or into a field by its number (ref; it is clicked first). submit=true presses return after the text (send a message, run a command). Returns the screen after. " ,
" parameters " : { " type " : " object " , " properties " : { " value " : { " type " : " string " } , " ref " : rf , " submit " : { " type " : " boolean " } } , " required " : [ " value " ] } } ) ,
json! ( { " name " : " computer_use_press_key " , " description " : " Press a key or shortcut in the app in front: return, tab, escape, delete, up, slash, cmd+c, cmd+shift+t, cmd+space. Several in order: separate with spaces ( \" cmd+a delete \" ). On a Linux computer cmd means ctrl. Returns the screen after. " ,
" parameters " : { " type " : " object " , " properties " : { " key " : { " type " : " string " } , " hold_duration_ms " : { " type " : " integer " } } , " required " : [ " key " ] } } ) ,
json! ( { " name " : " computer_use_scroll " , " description " : " Scroll at x, y or on a control by number (ref); neither = where the pointer is. Returns the screen after. " ,
" parameters " : { " type " : " object " , " properties " : { " x " : { " type " : " number " } , " y " : { " type " : " number " } , " ref " : rf , " direction " : { " type " : " string " , " enum " : [ " up " , " down " , " left " , " right " ] } , " amount " : { " type " : " integer " , " description " : " Scroll steps of 16 pixels, 1-20, default 3 " } , " modifier_keys " : mods } , " required " : [ " direction " ] } } ) ,
json! ( { " name " : " computer_use_open_app " , " description " : " Open an app, or bring it to the front when it is already open (its name as shown, e.g. Ghostty, Calculator, 計算機, Chromium, xterm). Returns the screen after. " ,
" parameters " : { " type " : " object " , " properties " : { " app " : { " type " : " string " } } , " required " : [ " app " ] } } ) ,
json! ( { " name " : " computer_use_read_app " , " description " : " Read an app's window as text: buttons, fields, menus, list items and visible text, each control numbered (e1, e2…) for click/typing/scroll's ref. For when a picture can't be taken or isn't enough (exact text, long lists). app: its name; leave out for the app in front. Only on computers that offer it (not the bots' own Linux computer). " ,
" parameters " : { " type " : " object " , " properties " : { " app " : { " type " : " string " } } } } ) ,
json! ( { " name " : " computer_use_mouse_move " , " description " : " Move the mouse to x, y (e.g. to show a hover menu). Returns the screen after. " ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
" parameters " : { " type " : " object " , " properties " : { " x " : { " type " : " number " } , " y " : { " type " : " number " } } , " required " : [ " x " , " y " ] } } ) ,
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 13:51:19 +00:00
json! ( { " name " : " computer_use_drag " , " description " : " Drag with the mouse along a path of points (first = press, last = release). Returns the screen after. " ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
" parameters " : { " type " : " object " , " properties " : { " path " : { " type " : " array " , " items " : { " type " : " object " , " properties " : { " x " : { " type " : " number " } , " y " : { " type " : " number " } } , " required " : [ " x " , " y " ] } } , " button " : { " type " : " string " , " enum " : [ " left " , " right " ] } , " modifier_keys " : mods } , " required " : [ " path " ] } } ) ,
json! ( { " name " : " computer_use_mouse_button " , " description " : " Press and hold, or release, a mouse button (optionally moving to x, y first). For long presses and custom drags. " ,
" parameters " : { " type " : " object " , " properties " : { " action " : { " type " : " string " , " enum " : [ " press " , " release " ] } , " button " : { " type " : " string " , " enum " : [ " left " , " right " , " middle " ] } , " x " : { " type " : " number " } , " y " : { " type " : " number " } } , " required " : [ " action " ] } } ) ,
json! ( { " name " : " computer_use_check_permissions " , " description " : " Check whether the screen can be seen and the mouse and keyboard used on that computer. " ,
" parameters " : { " type " : " object " , " properties " : { } } } ) ,
] ;
2026-11-05 08:47:28 +00:00
// Always named: each computer goes by its own name (the instructions say which is which),
// so the bot says whose screen it means and the person can tell where it worked.
if ! computers . is_empty ( ) {
let desc = format! ( " Whose screen, by the computer's name. Default: {default} (the one the person chose). Use another only when the person names it. " ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
for t in v . iter_mut ( ) {
2026-11-05 08:03:34 +00:00
t [ " parameters " ] [ " properties " ] [ " computer " ] = json! ( { " type " : " string " , " enum " : computers , " description " : desc } ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
}
v
}
/// zct: the size to send. Long edge down to `MAX_LONG_EDGE`, never an edge under `MIN_EDGE`.
pub fn fit ( w : u32 , h : u32 ) -> ( u32 , u32 , bool ) {
if w < 1 | | h < 1 {
return ( w , h , false ) ;
}
let long = w . max ( h ) ;
if long < = MAX_LONG_EDGE {
return ( w , h , false ) ;
}
let short = w . min ( h ) ;
if short < MIN_EDGE {
return ( w , h , false ) ;
}
let mut i = MAX_LONG_EDGE as f64 / long as f64 ;
let mut o = ( ( w as f64 * i ) . round ( ) as u32 ) . max ( 1 ) ;
let mut a = ( ( h as f64 * i ) . round ( ) as u32 ) . max ( 1 ) ;
if o < MIN_EDGE | | a < MIN_EDGE {
i = MIN_EDGE as f64 / short as f64 ;
o = ( ( w as f64 * i ) . round ( ) as u32 ) . max ( MIN_EDGE ) ;
a = ( ( h as f64 * i ) . round ( ) as u32 ) . max ( MIN_EDGE ) ;
}
( o , a , o ! = w | | a ! = h )
}
/// j3r: JPEG at 85, 70, 55: the first within the budget, else the smallest if it's under the cap.
pub fn encode_jpeg ( img : & image ::RgbImage ) -> Option < Vec < u8 > > {
let mut best : Option < Vec < u8 > > = None ;
for q in JPEG_QUALITIES {
let mut out = vec! [ ] ;
let enc = image ::codecs ::jpeg ::JpegEncoder ::new_with_quality ( & mut out , q ) ;
if img . write_with_encoder ( enc ) . is_err ( ) {
continue ;
}
if out . len ( ) < = MAX_IMAGE_BYTES {
return Some ( out ) ;
}
if best . as_ref ( ) . map_or ( true , | b | out . len ( ) < b . len ( ) ) {
best = Some ( out ) ;
}
}
best . filter ( | b | b . len ( ) < = MAX_IMAGE_BYTES )
}
/// Screenshot pixel → screen point.
pub fn to_screen ( x : f64 , y : f64 , scale : ( f64 , f64 ) ) -> ( f64 , f64 ) {
( x * scale . 0 , y * scale . 1 )
}
/// Key name → (key code, needs shift). US layout codes.
pub fn key_code ( name : & str ) -> Option < u16 > {
let n = name . trim ( ) . to_lowercase ( ) ;
let named = match n . as_str ( ) {
" return " | " enter " = > 36 ,
" tab " = > 48 ,
" space " = > 49 ,
" delete " | " backspace " = > 51 ,
" escape " | " esc " = > 53 ,
" forwarddelete " | " forward_delete " | " del " = > 117 ,
" home " = > 115 ,
" end " = > 119 ,
" pageup " | " page_up " = > 116 ,
" pagedown " | " page_down " = > 121 ,
" left " | " arrowleft " = > 123 ,
" right " | " arrowright " = > 124 ,
" down " | " arrowdown " = > 125 ,
" up " | " arrowup " = > 126 ,
" f1 " = > 122 ,
" f2 " = > 120 ,
" f3 " = > 99 ,
" f4 " = > 118 ,
" f5 " = > 96 ,
" f6 " = > 97 ,
" f7 " = > 98 ,
" f8 " = > 100 ,
" f9 " = > 101 ,
" f10 " = > 109 ,
" f11 " = > 103 ,
" f12 " = > 111 ,
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 13:51:19 +00:00
// Symbol keys by name (a model writes "slash" as often as "/").
" slash " = > 44 ,
" backslash " = > 42 ,
" comma " = > 43 ,
" period " | " dot " = > 47 ,
" semicolon " = > 41 ,
" quote " | " apostrophe " = > 39 ,
" minus " | " hyphen " = > 27 ,
" equal " | " equals " = > 24 ,
" grave " | " backtick " | " backquote " = > 50 ,
" bracketleft " | " leftbracket " = > 33 ,
" bracketright " | " rightbracket " = > 30 ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
_ = > u16 ::MAX ,
} ;
if named ! = u16 ::MAX {
return Some ( named ) ;
}
let mut cs = n . chars ( ) ;
let ( Some ( c ) , None ) = ( cs . next ( ) , cs . next ( ) ) else { return None } ;
const LETTERS : [ u16 ; 26 ] = [ 0 , 11 , 8 , 2 , 14 , 3 , 5 , 4 , 34 , 38 , 40 , 37 , 46 , 45 , 31 , 35 , 12 , 15 , 1 , 17 , 32 , 9 , 13 , 7 , 16 , 6 ] ;
const DIGITS : [ u16 ; 10 ] = [ 29 , 18 , 19 , 20 , 21 , 23 , 22 , 26 , 28 , 25 ] ;
match c {
'a' ..= 'z' = > Some ( LETTERS [ ( c as u8 - b 'a' ) as usize ] ) ,
'0' ..= '9' = > Some ( DIGITS [ ( c as u8 - b '0' ) as usize ] ) ,
'-' = > Some ( 27 ) ,
'=' = > Some ( 24 ) ,
'[' = > Some ( 33 ) ,
']' = > Some ( 30 ) ,
';' = > Some ( 41 ) ,
'\'' = > Some ( 39 ) ,
',' = > Some ( 43 ) ,
'.' = > Some ( 47 ) ,
'/' = > Some ( 44 ) ,
'\\' = > Some ( 42 ) ,
'`' = > Some ( 50 ) ,
_ = > None ,
}
}
pub const FLAG_SHIFT : u64 = 1 < < 17 ;
pub const FLAG_CONTROL : u64 = 1 < < 18 ;
pub const FLAG_OPTION : u64 = 1 < < 19 ;
pub const FLAG_CMD : u64 = 1 < < 20 ;
pub fn modifier_flag ( m : & str ) -> Option < u64 > {
match m . trim ( ) . to_lowercase ( ) . as_str ( ) {
" cmd " | " command " | " meta " | " super " | " ⌘ " = > Some ( FLAG_CMD ) ,
" shift " = > Some ( FLAG_SHIFT ) ,
" option " | " alt " | " opt " = > Some ( FLAG_OPTION ) ,
" control " | " ctrl " = > Some ( FLAG_CONTROL ) ,
_ = > None ,
}
}
/// "cmd+shift+t" → (key code, flags).
pub fn parse_combo ( combo : & str ) -> Result < ( u16 , u64 ) , String > {
let parts : Vec < & str > = combo . split ( '+' ) . map ( str ::trim ) . filter ( | p | ! p . is_empty ( ) ) . collect ( ) ;
let Some ( ( key , mods ) ) = parts . split_last ( ) else { return Err ( " 請給要按的鍵 " . into ( ) ) } ;
let mut flags = 0 ;
for m in mods {
flags | = modifier_flag ( m ) . ok_or_else ( | | format! ( " 不認得「 {m} 」這個修飾鍵 " ) ) ? ;
}
let code = key_code ( key ) . ok_or_else ( | | format! ( " 不認得「 {key} 」這個鍵 " ) ) ? ;
Ok ( ( code , flags ) )
}
fn flags_of ( v : & Value ) -> u64 {
v [ " modifier_keys " ] . as_array ( ) . map ( | a | a . iter ( ) . filter_map ( | m | m . as_str ( ) . and_then ( modifier_flag ) ) . fold ( 0 , | f , x | f | x ) ) . unwrap_or ( 0 )
}
/// Run a computer_use tool. Returns (text, ok, screenshot as base64 JPEG).
#[ allow(dead_code) ]
pub fn run ( name : & str , args : & str ) -> ( String , bool , Option < String > ) {
run_by ( name , args , " " )
}
/// Same, remembering which bot acted (for the 「正在操作你的電腦」 bar).
pub fn run_by ( name : & str , args : & str , who : & str ) -> ( String , bool , Option < String > ) {
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 13:51:19 +00:00
if moves_things ( name ) {
2026-11-05 07:48:22 +00:00
* LAST_ACTION . lock ( ) . unwrap_or_else ( | e | e . into_inner ( ) ) = Some ( ( std ::time ::Instant ::now ( ) , who . to_string ( ) ) ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
match run_inner ( name , & args_of ( args ) ) {
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
Ok ( ( t , img ) ) = > ( t , true , img ) ,
Err ( e ) = > ( e , false , None ) ,
}
}
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 13:51:19 +00:00
/// Steps that use the mouse or keyboard (and come back with the screen after them).
pub fn moves_things ( name : & str ) -> bool {
matches! ( name , " computer_use_click " | " computer_use_typing " | " computer_use_press_key " | " computer_use_scroll " | " computer_use_mouse_move " | " computer_use_drag " | " computer_use_mouse_button " | " computer_use_open_app " )
2026-11-04 09:19:59 +00:00
}
2026-11-05 08:57:44 +00:00
/// Ask macOS for a permission (its own prompt, then 系統設定 at that pane) at most once every ten
/// minutes. Asking on every step meant a bot working through several steps — or a brain far away
/// working on this computer — covered the screen with prompts and Settings windows, and the
/// person could do nothing else.
pub fn ask_permission ( pane : & str ) {
static LAST : std ::sync ::Mutex < Vec < ( String , std ::time ::Instant ) > > = std ::sync ::Mutex ::new ( Vec ::new ( ) ) ;
{
let mut l = LAST . lock ( ) . unwrap_or_else ( | e | e . into_inner ( ) ) ;
if l . iter ( ) . any ( | ( p , t ) | p = = pane & & t . elapsed ( ) < std ::time ::Duration ::from_secs ( 600 ) ) {
return ;
}
l . retain ( | ( p , _ ) | p ! = pane ) ;
l . push ( ( pane . to_string ( ) , std ::time ::Instant ::now ( ) ) ) ;
}
if ! cfg! ( target_os = " macos " ) {
return ;
}
match pane {
" Privacy_Accessibility " = > {
let _ = crate ::axread ::trusted ( true ) ;
}
#[ cfg(target_os = " macos " ) ]
" Privacy_ScreenCapture " = > {
let _ = mac ::screen_capture_allowed ( ) ;
}
_ = > { }
}
let _ = spawn_reaped ( & mut std ::process ::Command ::new ( " open " ) . arg ( format! ( " x-apple.systempreferences:com.apple.preference.security? {pane} " ) ) ) ;
}
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 13:51:19 +00:00
const NO_ACCESSIBILITY : & str = " macOS 還沒讓 AnyBot 使用「輔助使用」,所以不能讀 App, 也不能動滑鼠和鍵盤。這台電腦上已經跳出詢問並打開「系統設定 → 隱私權與安全性 → 輔助使用」:請使用者把 AnyBot 打開(只要一次),再叫我試一次。 " ;
const NO_SCREEN_RECORDING : & str = " macOS 還沒讓 AnyBot 錄製螢幕,所以看不到畫面。這台電腦上已經打開「系統設定 → 隱私權與安全性 → 螢幕與系統錄音」:請使用者把 AnyBot 打開(只要一次)。在那之前可以用 computer_use_read_app 讀 App 的文字。 " ;
/// Reading apps and using the mouse and keyboard need 輔助使用 on macOS (other systems: always).
fn controls_allowed ( ) -> bool {
! cfg! ( target_os = " macos " ) | | crate ::axread ::trusted ( false )
}
2026-11-05 10:50:08 +00:00
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 13:51:19 +00:00
fn need_controls ( ) -> Result < ( ) , String > {
if controls_allowed ( ) {
return Ok ( ( ) ) ;
2026-11-05 10:50:08 +00:00
}
ask_permission ( " Privacy_Accessibility " ) ;
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 13:51:19 +00:00
Err ( NO_ACCESSIBILITY . into ( ) )
2026-11-05 10:50:08 +00:00
}
2026-11-05 08:57:44 +00:00
/// The app a shown name stands for (「計算機」 → Calculator), from the apps' display names.
#[ cfg(target_os = " macos " ) ]
fn app_named ( name : & str ) -> Option < String > {
if name . is_empty ( ) | | name . contains ( [ '"' , '\\' , '\'' ] ) {
return None ;
}
let q = format! ( " kMDItemDisplayName == \" {name} \" cd && kMDItemContentType == \" com.apple.application-bundle \" " ) ;
let out = std ::process ::Command ::new ( " mdfind " ) . arg ( q ) . output ( ) . ok ( ) ? ;
let path = String ::from_utf8_lossy ( & out . stdout ) . lines ( ) . next ( ) ? . trim ( ) . to_string ( ) ;
std ::path ::Path ::new ( & path ) . file_stem ( ) . map ( | s | s . to_string_lossy ( ) . to_string ( ) )
}
#[ cfg(not(target_os = " macos " )) ]
fn app_named ( _name : & str ) -> Option < String > {
None
}
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 13:51:19 +00:00
/// Open an app or bring it to the front. A name as the person sees it (「計算機」 on a Chinese
/// Mac) isn't one the system launches by; find the app it is and launch that.
fn open_app ( app : & str ) -> Result < String , String > {
let app = app . trim ( ) ;
if app . is_empty ( ) {
return Err ( " 請給要打開的 App 名稱 " . into ( ) ) ;
}
crate ::desktop ::launch ( app ) . or_else ( | e | match app_named ( app ) {
Some ( real ) if real ! = app = > crate ::desktop ::launch ( & real ) ,
_ = > Err ( e ) ,
} ) ? ;
Ok ( format! ( " 已打開「 {app} 」,放在最前面。 " ) )
2026-11-04 09:19:59 +00:00
}
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 13:51:19 +00:00
/// Bring the app a control belongs to in front, so a click at its spot lands in it (and not in
/// a window lying over it). Nothing to do when it is in front already.
fn bring_forward ( app : & str ) {
// macOS only: there "launch" brings a running app forward; on Linux it can start a second
// copy, and the names a window and its accessibility give differ.
if ! cfg! ( target_os = " macos " ) {
return ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
let front = mac ::front_app ( ) . map ( | f | f . 0 ) . unwrap_or_default ( ) ;
if app . is_empty ( ) | | front . eq_ignore_ascii_case ( app ) {
return ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
if crate ::desktop ::launch ( app ) . is_ok ( ) {
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 300 ) ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
}
/// Where a step acts: a control's number (from computer_use_read_app), or x, y on the picture.
/// Returns the screen point and how to say it (「e7( 按鈕「7」) 」, 「(512, 300)」).
fn place_of ( v : & Value , scale : ( f64 , f64 ) ) -> Result < Option < ( f64 , f64 , String ) > , String > {
if let Some ( r ) = v [ " ref " ] . as_str ( ) . map ( str ::trim ) . filter ( | r | ! r . is_empty ( ) ) {
let s = crate ::desktop ::spot ( r ) ? ;
bring_forward ( & s . app ) ;
return Ok ( Some ( ( s . x , s . y , format! ( " {} ( {} ) " , crate ::desktop ::short_ref ( r ) ? , s . what ) ) ) ) ;
}
match ( v [ " x " ] . as_f64 ( ) , v [ " y " ] . as_f64 ( ) ) {
( Some ( x ) , Some ( y ) ) = > {
let ( sx , sy ) = to_screen ( x , y , scale ) ;
Ok ( Some ( ( sx , sy , format! ( " ( {x} , {y} ) " ) ) ) )
}
( None , None ) if v [ " x " ] . is_null ( ) & & v [ " y " ] . is_null ( ) = > Ok ( None ) ,
_ = > Err ( " x 和 y 要一起給,而且要是數字(截圖上的位置) " . into ( ) ) ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
}
/// Press each key of "cmd+a delete" in order.
fn press_keys ( keys : & str , hold : f64 ) -> Result < ( ) , String > {
for ( i , k ) in key_plan ( keys ) ? . iter ( ) . enumerate ( ) {
if i > 0 {
std ::thread ::sleep ( std ::time ::Duration ::from_secs_f64 ( KEY_GAP_SECS ) ) ;
}
match k {
Key ::Combo ( code , flags ) = > mac ::press ( * code , * flags , hold ) ,
Key ::Char ( ch ) = > mac ::type_text ( ch ) ,
}
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
Ok ( ( ) )
}
/// Do one mouse or keyboard step at the screen's own coordinates (`scale`: screen points per
/// picture pixel). The bot's tools and a person tapping on the phone's picture both come here:
/// one way of acting on a screen.
pub fn act ( name : & str , v : & Value , scale : ( f64 , f64 ) ) -> Result < String , String > {
let button = v [ " button " ] . as_str ( ) . unwrap_or ( " left " ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
match name {
" computer_use_click " = > {
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 13:51:19 +00:00
let ( x , y , at ) = place_of ( v , scale ) ? . ok_or ( " 請給 x、y( 截圖上的位置) 或 ref( computer_use_read_app 的編號) " ) ? ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let count = v [ " count " ] . as_u64 ( ) . unwrap_or ( 1 ) . clamp ( 1 , 3 ) as i64 ;
mac ::click ( x , y , button , count , flags_of ( v ) ) ;
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 13:51:19 +00:00
let how = format! ( " {} {} " , if button = = " right " { " 右鍵 " } else { " " } , if count > = 2 { format! ( " 連點 {count} 下 " ) } else { " 點 " . into ( ) } ) ;
Ok ( format! ( " 已 {how} {at} 。 " ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
" computer_use_mouse_move " = > {
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 13:51:19 +00:00
let ( x , y , at ) = place_of ( v , scale ) ? . ok_or ( " 請給 x 和 y " ) ? ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
mac ::move_to ( x , y ) ;
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 13:51:19 +00:00
Ok ( format! ( " 已把滑鼠移到 {at} 。 " ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
" computer_use_scroll " = > {
// CURemoteToolService.handleScroll @0x10009e200 / closure @0x10009e998.
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 13:51:19 +00:00
let at = place_of ( v , scale ) ? ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
if ! v [ " amount " ] . is_null ( ) & & v [ " amount " ] . as_i64 ( ) . is_none ( ) {
return Err ( " amount 要是整數 " . into ( ) ) ;
}
let amount = scroll_amount ( v [ " amount " ] . as_i64 ( ) ) ;
let dir = v [ " direction " ] . as_str ( ) . unwrap_or ( " " ) . trim ( ) . to_lowercase ( ) ;
let ( dy , dx ) = scroll_pixels ( & dir , amount ) . ok_or_else ( | | format! ( " 方向「 {dir} 」要是 up、down、left 或 right " ) ) ? ;
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 13:51:19 +00:00
mac ::scroll ( at . as_ref ( ) . map ( | ( x , y , _ ) | ( * x , * y ) ) , dy , dx , flags_of ( v ) ) ;
Ok ( format! ( " 已捲動 {dir} × {amount} {} 。 " , at . map ( | ( _ , _ , a ) | format! ( " ,在 {a} " ) ) . unwrap_or_default ( ) ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
" computer_use_drag " = > {
2026-11-05 07:48:22 +00:00
// Every point must be valid: points that failed to parse used to be dropped in
// silence, so the drag ran on fewer points and reported success at the wrong place.
let raw = v [ " path " ] . as_array ( ) . ok_or ( " 拖曳要給 path: 一個點陣, 每個點有數字 x 和 y( 用截圖上的座標) " ) ? ;
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 13:51:19 +00:00
let pts : Vec < ( f64 , f64 ) > = raw . iter ( ) . map ( | p | match ( p [ " x " ] . as_f64 ( ) , p [ " y " ] . as_f64 ( ) ) {
( Some ( x ) , Some ( y ) ) = > Ok ( ( x , y ) ) ,
_ = > Err ( " 拖曳的每個點都要有數字 x 和 y( 用截圖上的座標) " . to_string ( ) ) ,
} ) . collect ::< Result < _ , _ > > ( ) ? ;
if pts . len ( ) < 2 {
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
return Err ( " 拖曳至少要兩個點(每個點要有數字 x 和 y, 用截圖上的座標) " . into ( ) ) ;
}
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 13:51:19 +00:00
let path : Vec < ( f64 , f64 ) > = pts . iter ( ) . map ( | ( x , y ) | to_screen ( * x , * y , scale ) ) . collect ( ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
mac ::drag ( & path , button , flags_of ( v ) ) ;
// Like the original's "Dragged from (x, y) to (x, y) with left button."
let side = match button {
" right " = > " 右鍵 " ,
" middle " = > " 中鍵 " ,
_ = > " 左鍵 " ,
} ;
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 13:51:19 +00:00
let ( a , b ) = ( pts [ 0 ] , pts [ pts . len ( ) - 1 ] ) ;
Ok ( format! ( " 已從 ( {:.0} , {:.0} ) 拖曳到 ( {:.0} , {:.0} )( {side} )。 " , a . 0 , a . 1 , b . 0 , b . 1 ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
" computer_use_mouse_button " = > {
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 13:51:19 +00:00
let at = place_of ( v , scale ) ? . map ( | ( x , y , _ ) | ( x , y ) ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let release = v [ " action " ] . as_str ( ) = = Some ( " release " ) ;
mac ::mouse_button ( at , button , ! release ) ;
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 13:51:19 +00:00
Ok ( ( if release { " 已放開滑鼠鍵。 " } else { " 已按住滑鼠鍵(記得之後放開)。 " } ) . into ( ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
" computer_use_typing " = > {
let text = v [ " value " ] . as_str ( ) . unwrap_or ( " " ) ;
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 13:51:19 +00:00
let submit = v [ " submit " ] . as_bool ( ) . unwrap_or ( false ) ;
if text . is_empty ( ) & & ! submit {
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
return Err ( " 請給要打的字 " . into ( ) ) ;
}
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 13:51:19 +00:00
// Into a field by its number: click it first, like a person would.
let at = place_of ( & json! ( { " ref " : v [ " ref " ] } ) , scale ) ? ;
if let Some ( ( x , y , _ ) ) = & at {
mac ::click ( * x , * y , " left " , 1 , 0 ) ;
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 150 ) ) ;
}
if ! text . is_empty ( ) {
mac ::type_text ( text ) ;
}
if submit {
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( 60 ) ) ;
mac ::press ( 36 , 0 , KEY_HOLD_SECS ) ;
}
Ok ( format! ( " 已打字( {} 個字) {} {} 。 " , text . chars ( ) . count ( ) , at . map ( | ( _ , _ , a ) | format! ( " 在 {a} " ) ) . unwrap_or_default ( ) , if submit { " ,再按了 return " } else { " " } ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
" computer_use_press_key " = > {
let hold = v [ " hold_duration_ms " ] . as_u64 ( ) . map ( | ms | ms . min ( MAX_HOLD_MS ) as f64 / 1000.0 ) . filter ( | s | * s > 0.0 ) . unwrap_or ( KEY_HOLD_SECS ) ;
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 13:51:19 +00:00
press_keys ( v [ " key " ] . as_str ( ) . unwrap_or ( " " ) , hold ) ? ;
Ok ( format! ( " 已按 {} 。 " , v [ " key " ] . as_str ( ) . unwrap_or ( " " ) ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
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 13:51:19 +00:00
" computer_use_open_app " = > open_app ( v [ " app " ] . as_str ( ) . unwrap_or ( " " ) ) ,
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
other = > Err ( format! ( " 沒有「 {other} 」這個工具 " ) ) ,
}
}
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 13:51:19 +00:00
/// What the screen looks like now, after a step: the picture when this computer can take one,
/// else the app in front as numbered text. The bot sees the result of each step in the same
/// answer instead of spending another step (another trip to the model) on a screenshot.
fn observe ( note : String ) -> ( String , Option < String > ) {
std ::thread ::sleep ( std ::time ::Duration ::from_millis ( SETTLE_MS ) ) ;
if mac ::screen_capture_preflight ( ) {
return match screenshot ( ) {
Ok ( ( t , img ) ) = > ( format! ( " {note} \n {t} " ) , Some ( img ) ) ,
Err ( e ) = > ( format! ( " {note} \n (看不到結果: {e} ) " ) , None ) ,
} ;
}
if controls_allowed ( ) {
if let Ok ( ( app , lines ) ) = crate ::desktop ::read_app ( " " ) {
let shown : Vec < & String > = lines . iter ( ) . take ( 120 ) . collect ( ) ;
return ( format! ( " {note} \n (這台現在拍不到畫面,改讀最前面的「 {app} 」; e 編號可以用在 ref) \n {} " , shown . iter ( ) . map ( | l | l . as_str ( ) ) . collect ::< Vec < _ > > ( ) . join ( " \n " ) ) , None ) ;
}
}
( format! ( " {note} (這台現在看不到畫面,無法確認結果) " ) , None )
}
fn run_inner ( name : & str , v : & Value ) -> Result < ( String , Option < String > ) , String > {
if ! cfg! ( any ( target_os = " macos " , target_os = " linux " ) ) {
return Err ( " 操作畫面只支援 macOS 和 Linux " . into ( ) ) ;
}
#[ cfg(target_os = " linux " ) ]
if ! crate ::desk ::available ( ) {
return Err ( crate ::desk ::describe ( ) . into ( ) ) ;
}
match name {
" computer_use_screenshot " = > return screenshot ( ) . map ( | ( t , img ) | ( t , Some ( img ) ) ) ,
" computer_use_check_permissions " = > {
#[ cfg(target_os = " linux " ) ]
return Ok ( ( crate ::desk ::describe ( ) . into ( ) , None ) ) ;
#[ allow(unreachable_code) ]
{
let see = mac ::screen_capture_preflight ( ) ;
let control = controls_allowed ( ) ;
return Ok ( ( format! ( " 螢幕錄製(看畫面): {} \n 輔助使用(滑鼠、鍵盤、讀 App) : {} {} " , if see { " 已允許 " } else { " 還沒允許 " } , if control { " 已允許 " } else { " 還沒允許 " } , if see & & control { " " } else { " \n 請使用者到「系統設定 → 隱私權與安全性」打開 AnyBot。 " } ) , None ) ) ;
}
}
" computer_use_read_app " = > {
need_controls ( ) ? ;
let ( app , lines ) = crate ::desktop ::read_app ( v [ " app " ] . as_str ( ) . unwrap_or ( " " ) ) . map_err ( plain_words ) ? ;
return Ok ( ( if lines . is_empty ( ) {
format! ( " 「 {app} 」的畫面讀不到內容(有些 App 不提供輔助使用資訊)。可以改用 computer_use_screenshot 看。 " )
} else {
format! ( " 「 {app} 」的畫面( e 編號可以用在 click / typing / scroll 的 ref) : \n {} " , lines . join ( " \n " ) )
} , None ) ) ;
}
_ = > { }
}
if ! moves_things ( name ) {
return Err ( format! ( " 沒有「 {name} 」這個工具 " ) ) ;
}
if HELD_BY_PERSON . load ( std ::sync ::atomic ::Ordering ::SeqCst ) {
return Err ( " 使用者正在自己操作這台電腦(已取得控制權)。請等他交還控制權後再操作;可以先截圖看看。 " . into ( ) ) ;
}
if name ! = " computer_use_open_app " {
need_controls ( ) ? ;
}
let note = act ( name , v , scale_here ( ) ) . map_err ( plain_words ) ? ;
// Holding a button down is the middle of something (a long press, a drag): no picture yet.
if name = = " computer_use_mouse_button " {
return Ok ( ( note , None ) ) ;
}
Ok ( observe ( note ) )
}
/// agent-desktop's error codes, as words the bot can pass on.
fn plain_words ( t : String ) -> String {
if t . contains ( " PERM_DENIED " ) {
ask_permission ( " Privacy_Accessibility " ) ;
return NO_ACCESSIBILITY . to_string ( ) ;
}
if t . contains ( " APP_NOT_FOUND " ) {
return format! ( " 找不到這個 App( 沒有開著, 或名字不對) : 先用 computer_use_open_app 打開它。( {t} ) " ) ;
}
t
}
/// The phone or another viewer acting on this screen as the person: their tap is on the picture
/// they were sent (`shot_size()`), so it maps exactly like the bot's coordinates. Not refused
/// while the person holds the screen (it is the person), and no picture back (the viewer
/// refreshes its own).
pub fn person_act ( name : & str , v : & Value ) -> Result < ( ) , String > {
need_controls ( ) ? ;
act ( name , v , scale_here ( ) ) . map ( | _ | ( ) )
}
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
/// The app a screen action will land in: the window under the point for clicks, else the
/// frontmost app. Used for the per-app permission (「AnyBot 可以看到 {app},並在其中點擊和輸入」).
pub fn target_app ( name : & str , args : & str ) -> Option < String > {
let v = args_of ( args ) ;
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 13:51:19 +00:00
if let Some ( app ) = v [ " app " ] . as_str ( ) . filter ( | a | ! a . trim ( ) . is_empty ( ) & & matches! ( name , " computer_use_read_app " | " computer_use_open_app " ) ) {
2026-11-04 09:19:59 +00:00
return Some ( app . trim ( ) . to_string ( ) ) ;
}
if name = = " computer_use_read_app " {
return frontmost_app ( ) ;
}
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 13:51:19 +00:00
if let Some ( r ) = v [ " ref " ] . as_str ( ) . filter ( | r | ! r . trim ( ) . is_empty ( ) ) {
return crate ::desktop ::spot ( r ) . ok ( ) . map ( | s | s . app ) . or_else ( crate ::desktop ::last_app ) ;
2026-11-04 09:19:59 +00:00
}
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 13:51:19 +00:00
let s = scale_here ( ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let point = match name {
" computer_use_click " | " computer_use_mouse_move " | " computer_use_scroll " | " computer_use_mouse_button " if ! v [ " x " ] . is_null ( ) = > Some ( to_screen ( v [ " x " ] . as_f64 ( ) ? , v [ " y " ] . as_f64 ( ) ? , s ) ) ,
" computer_use_drag " = > v [ " path " ] . as_array ( ) . and_then ( | a | a . first ( ) ) . and_then ( | p | Some ( to_screen ( p [ " x " ] . as_f64 ( ) ? , p [ " y " ] . as_f64 ( ) ? , s ) ) ) ,
_ = > None ,
} ;
match point {
Some ( ( x , y ) ) = > mac ::window_at ( x , y ) . map ( | w | w . 0 ) ,
None = > mac ::front_app ( ) . map ( | w | w . 0 ) ,
}
}
/// Screenshot pixels per screen point (for the demonstration recorder).
pub fn points_to_shot ( ) -> f64 {
let ( pw , _ ) = mac ::screen_points ( ) ;
let ( px , py ) = mac ::screen_pixels ( ) ;
let ( fw , _ , _ ) = fit ( px . max ( 1 ) , py . max ( 1 ) ) ;
if pw > 0.0 { fw as f64 / pw } else { 1.0 }
}
/// The person's own clicks and keys can be recorded (教它一項任務): macOS, Linux on X11.
/// Wayland doesn't let apps watch other apps' input.
pub fn demo_supported ( ) -> bool {
#[ cfg(target_os = " linux " ) ]
{
crate ::desk ::kind ( ) = = crate ::desk ::Kind ::X11
}
#[ cfg(not(target_os = " linux " )) ]
{
cfg! ( target_os = " macos " )
}
}
/// This system's screen can be seen and used (macOS; Linux on X11 or Wayland).
pub fn screen_supported ( ) -> bool {
#[ cfg(target_os = " linux " ) ]
{
crate ::desk ::available ( )
}
#[ cfg(not(target_os = " linux " )) ]
{
cfg! ( target_os = " macos " )
}
}
pub fn screen_capture_ok ( ) -> bool {
mac ::screen_capture_preflight ( )
}
#[ cfg_attr(not(target_os = " macos " ), allow(dead_code)) ]
pub fn frontmost_app ( ) -> Option < String > {
mac ::front_app ( ) . map ( | w | w . 0 )
}
#[ cfg_attr(not(target_os = " macos " ), allow(dead_code)) ]
pub fn app_at_point ( x : f64 , y : f64 ) -> Option < String > {
mac ::window_at ( x , y ) . map ( | w | w . 0 )
}
fn screenshot ( ) -> Result < ( String , String ) , String > {
2026-11-05 08:57:44 +00:00
if ! mac ::screen_capture_preflight ( ) {
ask_permission ( " Privacy_ScreenCapture " ) ;
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 13:51:19 +00:00
return Err ( NO_SCREEN_RECORDING . into ( ) ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
let img = capture_screen ( ) ? ;
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 13:51:19 +00:00
// Always the same size for this screen (shot_size), so coordinates map without state.
let ( fw , fh ) = shot_size ( ) ;
let img = if img . dimensions ( ) ! = ( fw , fh ) { image ::imageops ::resize ( & img , fw , fh , image ::imageops ::FilterType ::Triangle ) } else { img } ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let jpeg = encode_jpeg ( & img ) . ok_or ( " 截圖太大了,沒辦法送出 " ) ? ;
let b64 = base64 ::engine ::general_purpose ::STANDARD . encode ( & jpeg ) ;
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 13:51:19 +00:00
Ok ( ( format! ( " 螢幕截圖( {fw} × {fh} )。座標用這張圖上的像素。 " ) , b64 ) )
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
/// The whole screen as RGB (backing pixels).
pub fn capture_screen ( ) -> Result < image ::RgbImage , String > {
#[ cfg(target_os = " linux " ) ]
return crate ::desk ::capture ( ) ;
#[ allow(unreachable_code) ]
{
2026-11-05 07:48:22 +00:00
// A random tail: two bots screenshotting the same millisecond must not delete each
// other's file (pid+time alone collides).
let file = std ::env ::temp_dir ( ) . join ( format! ( " anybot-screen- {} - {} - {} .png " , std ::process ::id ( ) , crate ::model ::now_ms ( ) , crate ::httpd ::random_hex ( 4 ) ) ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let st = std ::process ::Command ::new ( " screencapture " ) . args ( [ " -x " , " -m " , " -t " , " png " ] ) . arg ( & file ) . status ( ) . map_err ( | e | e . to_string ( ) ) ? ;
let bytes = std ::fs ::read ( & file ) ;
let _ = std ::fs ::remove_file ( & file ) ;
if ! st . success ( ) {
return Err ( " 截圖失敗 " . into ( ) ) ;
}
Ok ( image ::load_from_memory ( & bytes . map_err ( | e | format! ( " 截圖失敗: {e} " ) ) ? ) . map_err ( | e | format! ( " 截圖讀不出來: {e} " ) ) ? . to_rgb8 ( ) )
}
}
#[ cfg(target_os = " linux " ) ]
use crate ::desk as mac ;
#[ cfg(target_os = " macos " ) ]
mod mac {
use super ::* ;
use std ::ffi ::c_void ;
use std ::time ::Duration ;
#[ repr(C) ]
#[ derive(Clone, Copy) ]
struct CGPoint {
x : f64 ,
y : f64 ,
}
#[ repr(C) ]
#[ derive(Clone, Copy) ]
struct CGSize {
width : f64 ,
height : f64 ,
}
#[ repr(C) ]
#[ derive(Clone, Copy) ]
struct CGRect {
origin : CGPoint ,
size : CGSize ,
}
type Ev = * mut c_void ;
type Src = * mut c_void ;
#[ link(name = " CoreGraphics " , kind = " framework " ) ]
extern " C " {
fn CGMainDisplayID ( ) -> u32 ;
fn CGDisplayBounds ( d : u32 ) -> CGRect ;
fn CGPreflightScreenCaptureAccess ( ) -> bool ;
fn CGRequestScreenCaptureAccess ( ) -> bool ;
fn CGEventSourceCreate ( state : i32 ) -> Src ;
fn CGEventSourceSetLocalEventsSuppressionInterval ( src : Src , secs : f64 ) ;
fn CGEventCreateMouseEvent ( src : Src , kind : u32 , at : CGPoint , button : u32 ) -> Ev ;
fn CGEventCreateKeyboardEvent ( src : Src , key : u16 , down : bool ) -> Ev ;
fn CGEventKeyboardSetUnicodeString ( e : Ev , len : usize , s : * const u16 ) ;
fn CGEventSetFlags ( e : Ev , flags : u64 ) ;
fn CGEventSetIntegerValueField ( e : Ev , field : u32 , v : i64 ) ;
fn CGEventPost ( tap : u32 , e : Ev ) ;
fn CGEventCreateScrollWheelEvent ( src : Src , units : u32 , count : u32 , wheel1 : i32 , .. . ) -> Ev ;
fn CGDisplayPixelsWide ( d : u32 ) -> usize ;
fn CGDisplayPixelsHigh ( d : u32 ) -> usize ;
fn CGWindowListCopyWindowInfo ( option : u32 , relative_to : u32 ) -> * const c_void ;
fn CGRectMakeWithDictionaryRepresentation ( dict : * const c_void , rect : * mut CGRect ) -> bool ;
static kCGWindowOwnerName : * const c_void ;
static kCGWindowOwnerPID : * const c_void ;
static kCGWindowBounds : * const c_void ;
static kCGWindowLayer : * const c_void ;
}
#[ link(name = " CoreFoundation " , kind = " framework " ) ]
extern " C " {
fn CFArrayGetCount ( a : * const c_void ) -> isize ;
fn CFArrayGetValueAtIndex ( a : * const c_void , i : isize ) -> * const c_void ;
fn CFDictionaryGetValue ( d : * const c_void , k : * const c_void ) -> * const c_void ;
fn CFNumberGetValue ( n : * const c_void , kind : isize , out : * mut c_void ) -> bool ;
fn CFStringGetCString ( s : * const c_void , buf : * mut std ::ffi ::c_char , len : isize , enc : u32 ) -> bool ;
}
/// On-screen windows front to back: (owner app, pid, bounds), normal layer only.
fn windows ( ) -> Vec < ( String , i32 , CGRect ) > {
let mut out = vec! [ ] ;
unsafe {
// kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements
let list = CGWindowListCopyWindowInfo ( 1 | 16 , 0 ) ;
if list . is_null ( ) {
return out ;
}
for i in 0 .. CFArrayGetCount ( list ) {
let d = CFArrayGetValueAtIndex ( list , i ) ;
let mut layer : i32 = - 1 ;
let l = CFDictionaryGetValue ( d , kCGWindowLayer ) ;
if l . is_null ( ) | | ! CFNumberGetValue ( l , 3 , & mut layer as * mut i32 as * mut c_void ) | | layer ! = 0 {
continue ;
}
let mut pid : i32 = 0 ;
let p = CFDictionaryGetValue ( d , kCGWindowOwnerPID ) ;
if ! p . is_null ( ) {
CFNumberGetValue ( p , 3 , & mut pid as * mut i32 as * mut c_void ) ;
}
let mut name = String ::new ( ) ;
let n = CFDictionaryGetValue ( d , kCGWindowOwnerName ) ;
if ! n . is_null ( ) {
let mut buf = [ 0 u8 ; 256 ] ;
if CFStringGetCString ( n , buf . as_mut_ptr ( ) as * mut std ::ffi ::c_char , buf . len ( ) as isize , 0x0800_0100 ) {
let end = buf . iter ( ) . position ( | b | * b = = 0 ) . unwrap_or ( buf . len ( ) ) ;
name = String ::from_utf8_lossy ( & buf [ .. end ] ) . to_string ( ) ;
}
}
let mut r = CGRect { origin : CGPoint { x : 0.0 , y : 0.0 } , size : CGSize { width : 0.0 , height : 0.0 } } ;
let b = CFDictionaryGetValue ( d , kCGWindowBounds ) ;
if ! b . is_null ( ) {
CGRectMakeWithDictionaryRepresentation ( b , & mut r ) ;
}
out . push ( ( name , pid , r ) ) ;
}
CFRelease ( list ) ;
}
out
}
/// The app whose window is under a screen point.
pub fn window_at ( x : f64 , y : f64 ) -> Option < ( String , i32 ) > {
windows ( ) . into_iter ( ) . find ( | ( _ , _ , r ) | x > = r . origin . x & & y > = r . origin . y & & x < r . origin . x + r . size . width & & y < r . origin . y + r . size . height ) . map ( | ( n , p , _ ) | ( n , p ) )
}
/// The frontmost app (its frontmost normal window).
pub fn front_app ( ) -> Option < ( String , i32 ) > {
windows ( ) . into_iter ( ) . find ( | ( n , _ , r ) | ! n . is_empty ( ) & & r . size . width > 1.0 ) . map ( | ( n , p , _ ) | ( n , p ) )
}
pub fn screen_capture_preflight ( ) -> bool {
unsafe { CGPreflightScreenCaptureAccess ( ) }
}
pub fn screen_pixels ( ) -> ( u32 , u32 ) {
let d = unsafe { CGMainDisplayID ( ) } ;
let ( pw , ph ) = screen_points ( ) ;
// CGDisplayPixelsWide reports points on Retina; screencapture gives backing pixels.
let ( w , h ) = unsafe { ( CGDisplayPixelsWide ( d ) as f64 , CGDisplayPixelsHigh ( d ) as f64 ) } ;
let scale = backing_scale ( ) ;
let _ = ( w , h ) ;
( ( pw * scale ) as u32 , ( ph * scale ) as u32 )
}
fn backing_scale ( ) -> f64 {
static S : std ::sync ::OnceLock < f64 > = std ::sync ::OnceLock ::new ( ) ;
* S . get_or_init ( | | {
let file = std ::env ::temp_dir ( ) . join ( format! ( " anybot-scale- {} .png " , std ::process ::id ( ) ) ) ;
let ok = std ::process ::Command ::new ( " screencapture " ) . args ( [ " -x " , " -m " , " -t " , " png " , " -R0,0,10,10 " ] ) . arg ( & file ) . status ( ) . map ( | s | s . success ( ) ) . unwrap_or ( false ) ;
let w = if ok { image ::image_dimensions ( & file ) . map ( | d | d . 0 ) . unwrap_or ( 10 ) } else { 10 } ;
let _ = std ::fs ::remove_file ( & file ) ;
( w as f64 / 10.0 ) . max ( 1.0 )
} )
}
/// Press or release a mouse button where the pointer is (or at a point).
pub fn mouse_button ( at : Option < ( f64 , f64 ) > , button : & str , press : bool ) {
let ( down , up , _ , b ) = kinds ( button ) ;
let p = match at {
Some ( ( x , y ) ) = > {
move_to ( x , y ) ;
gap ( ) ;
CGPoint { x , y }
}
None = > current_point ( ) ,
} ;
let src = Source ::new ( ) ;
let e = unsafe { CGEventCreateMouseEvent ( src . 0 , if press { down } else { up } , p , b ) } ;
if ! e . is_null ( ) {
unsafe { CGEventSetIntegerValueField ( e , CLICK_STATE , 1 ) } ;
}
post ( e , 0 ) ;
}
fn current_point ( ) -> CGPoint {
extern " C " {
fn CGEventCreate ( src : Src ) -> Ev ;
fn CGEventGetLocation ( e : Ev ) -> CGPoint ;
}
unsafe {
let e = CGEventCreate ( std ::ptr ::null_mut ( ) ) ;
if e . is_null ( ) {
return CGPoint { x : 0.0 , y : 0.0 } ;
}
let p = CGEventGetLocation ( e ) ;
CFRelease ( e ) ;
p
}
}
#[ link(name = " CoreFoundation " , kind = " framework " ) ]
extern " C " {
fn CFRelease ( x : * const c_void ) ;
}
const HID_TAP : u32 = 0 ;
/// kCGEventSourceStateHIDSystemState, as CUActuator uses (CGEventSourceCreate(1)).
const HID_STATE : i32 = 1 ;
const CLICK_STATE : u32 = 1 ;
const MOVED : u32 = 5 ;
/// kCGScrollEventUnitPixel, as CGEventCreateScrollWheelEvent2(_, 0, 2, …) in the original.
const SCROLL_PIXEL : u32 = 0 ;
/// An event source like the original's: HID system state, local-event suppression 0 s, so
/// the person's own mouse and keys aren't frozen after each synthetic event.
struct Source ( Src ) ;
impl Source {
fn new ( ) -> Self {
let s = unsafe { CGEventSourceCreate ( HID_STATE ) } ;
if ! s . is_null ( ) {
unsafe { CGEventSourceSetLocalEventsSuppressionInterval ( s , 0.0 ) } ;
}
Source ( s )
}
}
impl Drop for Source {
fn drop ( & mut self ) {
if ! self . 0. is_null ( ) {
unsafe { CFRelease ( self . 0 as * const c_void ) }
}
}
}
fn post ( e : Ev , flags : u64 ) {
if e . is_null ( ) {
return ;
}
unsafe {
CGEventSetFlags ( e , flags ) ;
CGEventPost ( HID_TAP , e ) ;
CFRelease ( e ) ;
}
}
fn gap ( ) {
std ::thread ::sleep ( Duration ::from_millis ( EVENT_GAP_MS ) ) ;
}
fn sleep_secs ( s : f64 ) {
std ::thread ::sleep ( Duration ::from_secs_f64 ( s ) ) ;
}
/// (down, up, dragged) event kinds and button number.
fn kinds ( button : & str ) -> ( u32 , u32 , u32 , u32 ) {
match button {
" right " = > ( 3 , 4 , 7 , 1 ) ,
" middle " = > ( 25 , 26 , 27 , 2 ) ,
_ = > ( 1 , 2 , 6 , 0 ) ,
}
}
pub fn screen_capture_allowed ( ) -> bool {
unsafe { CGPreflightScreenCaptureAccess ( ) | | CGRequestScreenCaptureAccess ( ) }
}
pub fn screen_points ( ) -> ( f64 , f64 ) {
let r = unsafe { CGDisplayBounds ( CGMainDisplayID ( ) ) } ;
( r . size . width , r . size . height )
}
pub fn move_to ( x : f64 , y : f64 ) {
let src = Source ::new ( ) ;
post ( unsafe { CGEventCreateMouseEvent ( src . 0 , MOVED , CGPoint { x , y } , 0 ) } , 0 ) ;
}
/// CUActuator.click: press, hold 0.03 s, release; 0.08 s between the clicks of a multi-click.
pub fn click ( x : f64 , y : f64 , button : & str , count : i64 , flags : u64 ) {
let ( down , up , _ , b ) = kinds ( button ) ;
let at = CGPoint { x , y } ;
move_to ( x , y ) ;
gap ( ) ;
let src = Source ::new ( ) ;
for n in 1 ..= count {
if n > 1 {
sleep_secs ( CLICK_REPEAT_GAP_SECS ) ;
}
for ( i , kind ) in [ down , up ] . into_iter ( ) . enumerate ( ) {
if i = = 1 {
sleep_secs ( CLICK_HOLD_SECS ) ;
}
let e = unsafe { CGEventCreateMouseEvent ( src . 0 , kind , at , b ) } ;
if ! e . is_null ( ) {
unsafe { CGEventSetIntegerValueField ( e , CLICK_STATE , n ) } ;
}
post ( e , flags ) ;
}
}
}
/// CURemoteEventPoster.drag(path:button:steps:holdSeconds:): press at the first point (click
/// state 1), hold, then walk each segment in `DRAG_STEPS` interpolated dragged events, then
/// release at the last point.
pub fn drag ( path : & [ ( f64 , f64 ) ] , button : & str , flags : u64 ) {
let ( down , up , dragged , b ) = kinds ( button ) ;
let ( x0 , y0 ) = path [ 0 ] ;
move_to ( x0 , y0 ) ;
gap ( ) ;
let src = Source ::new ( ) ;
let event = | kind : u32 , x : f64 , y : f64 | {
let e = unsafe { CGEventCreateMouseEvent ( src . 0 , kind , CGPoint { x , y } , b ) } ;
if ! e . is_null ( ) {
unsafe { CGEventSetIntegerValueField ( e , CLICK_STATE , 1 ) } ;
}
post ( e , flags ) ;
} ;
event ( down , x0 , y0 ) ;
sleep_secs ( DRAG_HOLD_SECS ) ;
for ( x , y ) in drag_points ( path ) {
gap ( ) ;
event ( dragged , x , y ) ;
}
let ( xn , yn ) = path [ path . len ( ) - 1 ] ;
event ( up , xn , yn ) ;
}
/// handleScroll closure @0x10009e998: a mouse-moved event at the spot (the pointer's own
/// position when no x/y), then one pixel-unit scroll event with 2 wheels at that spot.
pub fn scroll ( at : Option < ( f64 , f64 ) > , dy : i32 , dx : i32 , flags : u64 ) {
if let Some ( ( x , y ) ) = at {
move_to ( x , y ) ;
gap ( ) ;
}
let src = Source ::new ( ) ;
post ( unsafe { CGEventCreateScrollWheelEvent ( src . 0 , SCROLL_PIXEL , 2 , dy , dx ) } , flags ) ;
}
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 13:51:19 +00:00
/// Type text so that any app takes it, whatever the keyboard layout or input method: paste
/// it (⌘V) and put the clipboard back. Text sent as key events (the original's
/// CUTypedCharacters, key 128 carrying the characters) went into the input method instead
/// when one was on — with 注音 every letter vanished while return still arrived, so a
/// command typed into a terminal ran empty. Falls back to those key events without a
/// clipboard.
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
pub fn type_text ( text : & str ) {
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 13:51:19 +00:00
if ! paste ( text ) {
type_as_keys ( text ) ;
}
}
/// Wait for the app to read the clipboard before it is put back (Electron apps read it a
/// moment after ⌘V; estimated).
const PASTE_READ_MS : u64 = 350 ;
fn paste ( text : & str ) -> bool {
let Ok ( mut cb ) = arboard ::Clipboard ::new ( ) else { return false } ;
// What the person had there comes back afterwards (text or a picture).
let saved_text = cb . get_text ( ) . ok ( ) ;
let saved_image = if saved_text . is_none ( ) { cb . get_image ( ) . ok ( ) } else { None } ;
if cb . set_text ( text . to_string ( ) ) . is_err ( ) {
return false ;
}
std ::thread ::sleep ( Duration ::from_millis ( 40 ) ) ;
press ( 9 , FLAG_CMD , KEY_HOLD_SECS ) ;
std ::thread ::sleep ( Duration ::from_millis ( PASTE_READ_MS ) ) ;
match ( saved_text , saved_image ) {
( Some ( t ) , _ ) = > {
let _ = cb . set_text ( t ) ;
}
( None , Some ( i ) ) = > {
let _ = cb . set_image ( i ) ;
}
_ = > {
let _ = cb . clear ( ) ;
}
}
true
}
/// CUTypedCharacters.postChunks: each chunk on key 128, down, 0.035 s, up; 0.02 s between chunks.
fn type_as_keys ( text : & str ) {
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
let src = Source ::new ( ) ;
let chunks = utf16_chunks ( text ) ;
for ( i , chunk ) in chunks . iter ( ) . enumerate ( ) {
if i > 0 {
sleep_secs ( KEY_GAP_SECS ) ;
}
for down in [ true , false ] {
if ! down {
sleep_secs ( KEY_HOLD_SECS ) ;
}
let e = unsafe { CGEventCreateKeyboardEvent ( src . 0 , TYPE_KEY_CODE , down ) } ;
if ! e . is_null ( ) {
unsafe { CGEventKeyboardSetUnicodeString ( e , chunk . len ( ) , chunk . as_ptr ( ) ) } ;
}
post ( e , 0 ) ;
}
}
}
/// handlePressKey: key down, hold (0.035 s unless told), key up.
pub fn press ( code : u16 , flags : u64 , hold_secs : f64 ) {
let src = Source ::new ( ) ;
post ( unsafe { CGEventCreateKeyboardEvent ( src . 0 , code , true ) } , flags ) ;
sleep_secs ( hold_secs ) ;
post ( unsafe { CGEventCreateKeyboardEvent ( src . 0 , code , false ) } , flags ) ;
}
}
#[ cfg(not(any(target_os = " macos " , target_os = " linux " ))) ]
mod mac {
pub fn screen_capture_allowed ( ) -> bool {
false
}
pub fn screen_points ( ) -> ( f64 , f64 ) {
( 0.0 , 0.0 )
}
pub fn move_to ( _ : f64 , _ : f64 ) { }
pub fn click ( _ : f64 , _ : f64 , _ : & str , _ : i64 , _ : u64 ) { }
pub fn drag ( _ : & [ ( f64 , f64 ) ] , _ : & str , _ : u64 ) { }
pub fn scroll ( _ : Option < ( f64 , f64 ) > , _ : i32 , _ : i32 , _ : u64 ) { }
pub fn type_text ( _ : & str ) { }
pub fn press ( _ : u16 , _ : u64 , _ : f64 ) { }
pub fn window_at ( _ : f64 , _ : f64 ) -> Option < ( String , i32 ) > {
None
}
pub fn front_app ( ) -> Option < ( String , i32 ) > {
None
}
pub fn screen_capture_preflight ( ) -> bool {
false
}
pub fn screen_pixels ( ) -> ( u32 , u32 ) {
( 0 , 0 )
}
pub fn mouse_button ( _ : Option < ( f64 , f64 ) > , _ : & str , _ : bool ) { }
}
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn screenshots_fit_like_the_original ( ) {
assert_eq! ( fit ( 2940 , 1912 ) , ( 1024 , 666 , true ) ) ;
assert_eq! ( fit ( 1024 , 640 ) , ( 1024 , 640 , false ) ) ;
assert_eq! ( fit ( 800 , 600 ) , ( 800 , 600 , false ) ) ;
assert_eq! ( fit ( 4000 , 7 ) , ( 4000 , 7 , false ) ) ;
// A very wide strip keeps its short edge at 8.
assert_eq! ( fit ( 4000 , 10 ) , ( 3200 , 8 , true ) ) ;
}
#[ test ]
fn jpeg_stays_under_the_cap ( ) {
let img = image ::RgbImage ::from_fn ( 1024 , 640 , | x , y | image ::Rgb ( [ ( x % 256 ) as u8 , ( y % 256 ) as u8 , ( ( x ^ y ) % 256 ) as u8 ] ) ) ;
let j = encode_jpeg ( & img ) . unwrap ( ) ;
assert! ( j . len ( ) < = MAX_IMAGE_BYTES & & j . starts_with ( & [ 0xFF , 0xD8 ] ) ) ;
}
#[ test ]
fn shortcuts_are_parsed ( ) {
assert_eq! ( parse_combo ( " cmd+c " ) . unwrap ( ) , ( 8 , FLAG_CMD ) ) ;
assert_eq! ( parse_combo ( " cmd+shift+t " ) . unwrap ( ) , ( 17 , FLAG_CMD | FLAG_SHIFT ) ) ;
assert_eq! ( parse_combo ( " Return " ) . unwrap ( ) , ( 36 , 0 ) ) ;
assert_eq! ( parse_combo ( " ctrl+option+space " ) . unwrap ( ) , ( 49 , FLAG_CONTROL | FLAG_OPTION ) ) ;
assert! ( parse_combo ( " hyper+x " ) . is_err ( ) ) ;
assert! ( parse_combo ( " " ) . is_err ( ) ) ;
}
#[ test ]
fn typed_text_is_chunked_like_the_original ( ) {
let c = utf16_chunks ( & " a " . repeat ( 45 ) ) ;
assert_eq! ( c . iter ( ) . map ( Vec ::len ) . collect ::< Vec < _ > > ( ) , vec! [ 20 , 20 , 5 ] ) ;
// An emoji (2 UTF-16 units) is never split across chunks.
let c = utf16_chunks ( & format! ( " {} 😀b " , " a " . repeat ( 19 ) ) ) ;
assert_eq! ( c . iter ( ) . map ( Vec ::len ) . collect ::< Vec < _ > > ( ) , vec! [ 19 , 3 ] ) ;
assert_eq! ( utf16_chunks ( " 你好 " ) . len ( ) , 1 ) ;
assert! ( utf16_chunks ( " " ) . is_empty ( ) ) ;
}
#[ test ]
fn scrolls_like_the_original ( ) {
assert_eq! ( scroll_amount ( None ) , 3 ) ;
assert_eq! ( scroll_amount ( Some ( 0 ) ) , 1 ) ;
assert_eq! ( scroll_amount ( Some ( 99 ) ) , 20 ) ;
assert_eq! ( scroll_pixels ( " down " , 3 ) , Some ( ( - 48 , 0 ) ) ) ;
assert_eq! ( scroll_pixels ( " left " , 2 ) , Some ( ( 0 , 32 ) ) ) ;
assert_eq! ( scroll_pixels ( " sideways " , 1 ) , None ) ;
}
#[ test ]
fn drags_walk_six_steps_per_segment ( ) {
let pts = drag_points ( & [ ( 0.0 , 0.0 ) , ( 60.0 , 0.0 ) , ( 60.0 , 30.0 ) ] ) ;
assert_eq! ( pts . len ( ) , 2 * DRAG_STEPS ) ;
assert_eq! ( pts [ 0 ] , ( 10.0 , 0.0 ) ) ;
assert_eq! ( pts [ DRAG_STEPS - 1 ] , ( 60.0 , 0.0 ) ) ;
assert_eq! ( * pts . last ( ) . unwrap ( ) , ( 60.0 , 30.0 ) ) ;
}
#[ test ]
fn a_press_can_be_several_keys ( ) {
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 13:51:19 +00:00
assert_eq! ( key_plan ( " cmd+a delete " ) . unwrap ( ) , vec! [ Key ::Combo ( 0 , FLAG_CMD ) , Key ::Combo ( 51 , 0 ) ] ) ;
assert! ( key_plan ( " " ) . is_err ( ) ) ;
// Keys by the names models use, and characters without a key typed instead.
assert_eq! ( key_plan ( " cmd+grave " ) . unwrap ( ) , vec! [ Key ::Combo ( 50 , FLAG_CMD ) ] ) ;
// A printing key alone is its character (typed, so 注音 can't swallow it).
assert_eq! ( key_plan ( " slash " ) . unwrap ( ) , vec! [ Key ::Char ( " / " . into ( ) ) ] ) ;
assert_eq! ( key_plan ( " / @ r return " ) . unwrap ( ) , vec! [ Key ::Char ( " / " . into ( ) ) , Key ::Char ( " @ " . into ( ) ) , Key ::Char ( " r " . into ( ) ) , Key ::Combo ( 36 , 0 ) ] ) ;
assert! ( key_plan ( " hyper+x " ) . is_err ( ) ) ;
}
#[ test ]
fn pictures_and_clicks_share_one_size ( ) {
// The picture's size depends only on the screen, so a tap or a click maps the same
// whoever saw which screenshot last (a 2940× 1912 Retina screen of 1470× 956 points).
let ( w , h , _ ) = fit ( 2940 , 1912 ) ;
let s = ( 1470.0 / w as f64 , 956.0 / h as f64 ) ;
let ( x , y ) = to_screen ( 512.0 , 333.0 , s ) ;
assert! ( ( x - 735.0 ) . abs ( ) < 0.01 & & ( y - 478.0 ) . abs ( ) < 1.0 ) ;
AnyBot: first commit of the working desktop app
Rust/egui rebuild of Grok Bot as of 2026-11-04: chat with streaming
providers (xAI, OpenAI, Claude, Gemini, OpenRouter, OpenCode Go, Ollama,
custom), tools and approvals, the bot's Docker computer, computer use on
macOS / Linux X11 / Wayland, AnyBot's browser with a full DevTools-based
toolset, the encrypted vault, pinned grid and sidebar sections, Markdown
with Mermaid diagrams, phone access through the relay, and the Linux
build and test scripts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uirha3oEtWeHh58zA7ErHa
2026-11-04 08:51:51 +00:00
}
#[ test ]
fn screenshot_pixels_map_to_screen_points ( ) {
// A 1470-point-wide screen sent as a 1024-wide picture.
let s = ( 1470.0 / 1024.0 , 956.0 / 666.0 ) ;
let ( x , y ) = to_screen ( 512.0 , 333.0 , s ) ;
assert! ( ( x - 735.0 ) . abs ( ) < 0.01 & & ( y - 478.0 ) . abs ( ) < 0.01 ) ;
}
}
#[ cfg(test) ]
mod window_tests {
/// `cargo test --release -- --ignored finds_apps_on_screen --nocapture`
#[ test ]
#[ ignore ]
fn finds_apps_on_screen ( ) {
let front = super ::frontmost_app ( ) ;
println! ( " front: {front:?} " ) ;
println! ( " at (200,200): {:?} " , super ::app_at_point ( 200.0 , 200.0 ) ) ;
println! ( " points→shot: {} " , super ::points_to_shot ( ) ) ;
assert! ( front . is_some ( ) ) ;
}
}