diff --git a/src/computer.rs b/src/computer.rs index dddc149..33ef2d3 100644 --- a/src/computer.rs +++ b/src/computer.rs @@ -430,6 +430,20 @@ pub fn ask_permission(pane: &str) { let _ = spawn_reaped(&mut std::process::Command::new("open").arg(format!("x-apple.systempreferences:com.apple.preference.security?{pane}"))); } +const NO_ACCESSIBILITY: &str = "macOS 還沒讓 AnyBot 使用「輔助使用」,所以讀不到、也按不了這台電腦上的 App。這台電腦上已經跳出詢問並打開「系統設定 → 隱私權與安全性 → 輔助使用」:請使用者把 AnyBot 打開(只要一次),再叫我試一次。"; + +/// agent-desktop's PERM_DENIED (no 輔助使用), as words the bot can pass on — and the person asked. +fn plain_permission_words(t: String) -> String { + if !t.contains("PERM_DENIED") { + return t; + } + ask_permission("Privacy_Accessibility"); + match t.split_once(",但讀不到它的畫面") { + Some((opened, _)) => format!("{opened},但讀不到它的畫面:{NO_ACCESSIBILITY}"), + None => NO_ACCESSIBILITY.to_string(), + } +} + /// The app a shown name stands for (「計算機」 → Calculator), from the apps' display names. #[cfg(target_os = "macos")] fn app_named(name: &str) -> Option { @@ -503,8 +517,16 @@ fn run_inner(name: &str, v: &Value) -> Result<(String, Option), String> return Err("使用者正在自己操作這台電腦(已取得控制權)。請等他交還控制權後再操作。".into()); } } + // Reading and using apps by their controls needs 輔助使用: without it macOS answers + // PERM_DENIED and nobody was asked. Ask (prompt + the Settings pane, now and then) and say it + // plainly; opening an app still opens it. + let by_controls = name == "computer_use_read_app" || run_text_mode_name(name, v).is_some(); + if cfg!(target_os = "macos") && by_controls && name != "computer_use_open_app" && !crate::axread::trusted(false) { + ask_permission("Privacy_Accessibility"); + return Err(NO_ACCESSIBILITY.into()); + } if let Some(r) = run_text_mode(name, v) { - return r; + return r.map(|(t, img)| (plain_permission_words(t), img)).map_err(plain_permission_words); } if !cfg!(any(target_os = "macos", target_os = "linux")) { return Err("操作畫面只支援 macOS 和 Linux".into());