diff --git a/FEATURES.md b/FEATURES.md index f7ca202..96c1210 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -738,7 +738,7 @@ AnyBot 是一個桌面聊天 App,介面和運作方式照 Grok Bot 重做。 - 預設是這台 Mac 的 Docker(OrbStack 或 Docker Desktop)。 - 可以填 Docker 主機,例如 `ssh://me@server`(Tailscale 的機器名稱也行),Bot 的電腦就會在那台主機上;看畫面時 AnyBot 會自己開 SSH 通道。 -- **容器隔離**(設定 › 電腦 › 在哪裡執行):標準(runc:容器和 Docker 主機共用核心,Chromium 關掉自己的沙盒,容器是唯一那道牆)或強隔離(gVisor/runsc:電腦有自己的虛擬核心,權限全拿掉只留開機要的幾個,Chromium 的沙盒打開,兩道牆)。Docker 主機沒有 gVisor 時強隔離不能選;設定是強隔離、主機卻沒有 gVisor 時(例如換了 Docker 主機)先用標準開機並寫進紀錄,不會整台開不起來。那一列顯示電腦實際用的是哪種,和設定不同時提示按「更新」。Mac 上用 `deploy/mac/gvisor-vm.sh` 建一台自帶 Docker+gVisor 的 Lima 虛擬機(4 GB;虛擬機沒開時 AnyBot 會自己開,例如 Mac 重開機後;虛擬機用過的記憶體 Mac 拿不回來,開越大就長越大,以前 6 GB 會整個長滿。腳本也關掉虛擬機用不到的數據機、磁碟自動掛載、多路徑儲存服務,約 70 MB),這台 AnyBot 是別的大腦的螢幕時(Bot 用的是大腦那邊的電腦,只借這台的畫面、瀏覽器和 App),自己的電腦閒置 2 分鐘就停下來,虛擬機裡沒有別的東西在跑就連虛擬機一起關掉,把記憶體還給 Mac;開機時看設定就知道自己是螢幕,不會先把虛擬機叫醒;變回大腦時兩個都會自己開回來(第一次多等 30–60 秒)。螢幕上也不跑「保住資料卷」的小容器和排程更新。`gvisor-migrate.sh` 把原本的檔案和登入搬過去(AnyBot 要先完全退出)。實測(Mac 上的 Lima VM):開機、畫面、VNC、無障礙編號、xdotool、截圖都正常;13 個 Chromium 程序都沒有 --no-sandbox,網頁程序 seccomp=2、有自己的命名空間;每一步 exec 55–69 ms、截圖 95 ms。 +- **容器隔離**(設定 › 電腦 › 在哪裡執行):標準(runc:容器和 Docker 主機共用核心,Chromium 關掉自己的沙盒,容器是唯一那道牆)或強隔離(gVisor/runsc:電腦有自己的虛擬核心,權限全拿掉只留開機要的幾個,Chromium 的沙盒打開,兩道牆)。Docker 主機沒有 gVisor 時強隔離不能選;設定是強隔離、主機卻沒有 gVisor 時(例如換了 Docker 主機)先用標準開機並寫進紀錄,不會整台開不起來。那一列顯示電腦實際用的是哪種,和設定不同時提示按「更新」。Mac 上用 `deploy/mac/gvisor-vm.sh` 建一台自帶 Docker+gVisor 的 Lima 虛擬機(4 GB;虛擬機沒開時 AnyBot 會自己開,例如 Mac 重開機後;虛擬機用過的記憶體 Mac 拿不回來,開越大就長越大,以前 6 GB 會整個長滿。腳本也關掉虛擬機用不到的數據機、磁碟自動掛載、多路徑儲存服務,約 70 MB),這台 AnyBot 設定成連到別的大腦(當螢幕)時,從一開始就不開自己的電腦和虛擬機(Bot 用的是大腦那邊的電腦,只借這台的畫面、瀏覽器和 App):啟動時看設定就知道,已經開著的馬上停掉,虛擬機裡沒有別的東西在跑就連虛擬機一起關掉,把記憶體還給 Mac。螢幕上也不跑「保住資料卷」的小容器、排程更新和「上次更新被中斷就接著做」。設定成用這台自己當大腦時才開,變回大腦時兩個都會自己開回來(第一次多等 30–60 秒)。虛擬機也不跟著 Mac 開機,交給 AnyBot 需要時自己開。`gvisor-migrate.sh` 把原本的檔案和登入搬過去(AnyBot 要先完全退出)。實測(Mac 上的 Lima VM):開機、畫面、VNC、無障礙編號、xdotool、截圖都正常;13 個 Chromium 程序都沒有 --no-sandbox,網頁程序 seccomp=2、有自己的命名空間;每一步 exec 55–69 ms、截圖 95 ms。 - 換位置後會在新的地方重新建立;舊檔案可以先「儲存快照」,換過去再「從快照復原」。 - **「網路流量改走這台 Mac」**(原版:網路流量可以改走你的本機電腦,「僅對新連線生效」,管理員可以關掉):打開後,Bot 的電腦之後執行的指令會經過這台 Mac 上的代理伺服器出去(例如要連公司內網或 VPN 才看得到的服務)。代理只聽這台 Mac 自己(127.0.0.1),用原版的埠 8791,被占用就往後找。設定頁顯示經過的連線數。Bot 的電腦在別的主機上時不能用。管理員可以用 `defaults write com.anybot.AnyBot AllowBoxTrafficViaThisMac false` 鎖住,這時顯示「🔒 由你的管理員設定,無法變更」。 diff --git a/deploy/mac/gvisor-vm.sh b/deploy/mac/gvisor-vm.sh index 607c95c..b66afb2 100755 --- a/deploy/mac/gvisor-vm.sh +++ b/deploy/mac/gvisor-vm.sh @@ -71,9 +71,8 @@ sudo systemctl disable --now ModemManager.service udisks2.service multipathd.ser docker run --rm --runtime=runsc --entrypoint sh alpine:3.20 -c "grep -q gVisor /proc/version && uname -r" | sed "s/^/runsc 容器核心: /" ' -# Bot 的電腦 lives in this VM: start it with the Mac, or after a restart the computer is gone -# until someone runs this again. -limactl autostart enable "$VM" >/dev/null 2>&1 || limactl start-at-login "$VM" --enabled >/dev/null 2>&1 || echo "(沒能設定開機自動啟動:Mac 重開機後要再跑一次這個腳本)" +# Not started with the Mac: AnyBot starts this VM itself when it is the brain and wants its +# computer, and leaves it off while it only shows another brain (the VM's memory is the Mac's). # Lima forwards the VM's Docker socket to the Mac: AnyBot's Docker 主機用這個 unix:// 位址, # 不用 ssh、不用碰 ~/.ssh/config。 diff --git a/src/cloudbox.rs b/src/cloudbox.rs index 9c63dba..23d4cf2 100644 --- a/src/cloudbox.rs +++ b/src/cloudbox.rs @@ -1124,7 +1124,12 @@ pub fn start_background() { std::thread::spawn(|| { adopt_usual_box(); set_run(probe()); - resume_interrupted(); + if IS_BRAIN.load(Ordering::SeqCst) { + resume_interrupted(); + } else { + // Showing another brain from the start: this box and its VM aren't wanted at all. + sleep_on_a_screen(); + } loop { // (First wait a little: a computer showing another brain says so right after start.) std::thread::sleep(Duration::from_secs(20)); @@ -1139,7 +1144,7 @@ pub fn start_background() { let _ = ensure_running(); } if !brain { - sleep_on_a_screen(idle); + sleep_on_a_screen(); } let quiet = idle >= QUIET_SECS && !is_busy() && !HELD_BY_PERSON.load(Ordering::SeqCst) && !viewer_open(); let due = snapshot_time().map(|t| (chrono::Local::now() - t).num_seconds() >= SNAPSHOT_EVERY_SECS).unwrap_or(true); @@ -1183,16 +1188,13 @@ fn update_when_idle(idle_secs: u64) { } } -/// How long a computer showing another brain keeps its own box after its last use. -const SCREEN_SLEEP_SECS: u64 = 120; - /// This AnyBot shows another brain: the bots work on that brain's computer (and borrow only this /// one's screen, browser and apps), so this box — and on a Mac the Lima VM it runs in — only held -/// memory: the VM grew to its whole 6 GB, which the Mac never gets back while it runs. Stop the -/// box once nobody has used it for a while, and the VM when nothing else runs in it. Becoming a -/// brain again starts both (probe and ensure_running wake the VM). -fn sleep_on_a_screen(idle_secs: u64) { - if IS_BRAIN.load(Ordering::SeqCst) || idle_secs < SCREEN_SLEEP_SECS || is_busy() || HELD_BY_PERSON.load(Ordering::SeqCst) || viewer_open() { +/// memory: the VM grew to its whole 6 GB, which the Mac never gets back while it runs. The person +/// wants neither running then, from the start: stop the box at once, and the VM when nothing +/// else runs in it. Becoming a brain again starts both (probe and ensure_running wake the VM). +fn sleep_on_a_screen() { + if IS_BRAIN.load(Ordering::SeqCst) || is_busy() || HELD_BY_PERSON.load(Ordering::SeqCst) || viewer_open() { return; } // Docker is down already (the VM sleeps), or nothing of ours runs: nothing to do.