From 19f8257e488e53992027d516c8c766055960c341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Sat, 7 Nov 2026 00:28:35 +0800 Subject: [PATCH] An upload says what the field holds now and what the page showed The tool only said it put the file in; on a page that shows nothing about it the bot spent five more steps checking (shell, read, screenshot, two scripts). Now the result names the file the field holds (and its size) and the page's new text, and an empty field is an error. Real run in the gVisor box with Grok: the sign-up form went from 10 steps / 35 s to 6 / 20 s; the second time it followed the skill it had learned, by names only, 6 steps / 21 s. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Uirha2RVfSYmrhqNnqx9Z4 --- src/agent.rs | 2 +- src/browser.rs | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index a4a3db3..1592bd1 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -1625,7 +1625,7 @@ printf 'a,b\n1,2\n' > report.csv && printf 'hello box' > '/tmp/anybot-up/上傳 } assert!(results[0].contains("served"), "{}", results[0]); assert!(results[1].contains("檔案測試"), "{}", results[1]); - assert!(results[2].contains("已把 /tmp/anybot-up/上傳測試.txt 放進上傳欄位"), "{}", results[2]); + assert!(results[2].contains("已把 /tmp/anybot-up/上傳測試.txt 放進上傳欄位,欄位裡現在是:上傳測試.txt(9 位元組)") && results[2].contains("已選:上傳測試.txt 9"), "{}", results[2]); assert!(results[3].contains("已選:上傳測試.txt 9"), "{}", results[3]); assert!(results[4].contains("這台電腦上沒有「/tmp/anybot-up/沒有這個.txt」"), "{}", results[4]); assert!(results[5].contains("下載好了:/home/bot/Downloads/report.csv"), "{}", results[5]); diff --git a/src/browser.rs b/src/browser.rs index c11221f..f6fcd98 100644 --- a/src/browser.rs +++ b/src/browser.rs @@ -727,10 +727,20 @@ pub fn run(name: &str, args: &Value) -> Result<(String, Option), String> let js = format!("(() => {{ let el = {}; if (el && el.tagName !== 'INPUT') el = el.querySelector('input[type=file]') || el.closest('label')?.querySelector('input[type=file]'); return el || document.querySelector('input[type=file]'); }})()", find_js(&rf, &text)); let r = tab.call("Runtime.evaluate", json!({"expression": js, "returnByValue": false}))?; let obj = r["result"]["objectId"].as_str().ok_or("這一頁找不到上傳檔案的欄位")?.to_string(); + let before = snapshot(&mut tab); tab.call("DOM.setFileInputFiles", json!({"files": [full], "objectId": obj}))?; tab.eval(&format!("(() => {{ const el = {js}; if (el) el.dispatchEvent(new Event('change', {{bubbles: true}})); return 1; }})()"))?; settle(1000); - Ok((format!("已把 {full} 放進上傳欄位。"), None)) + // What the field holds now (a page that shows nothing about it left the bot + // checking with five more steps), and what the page did with it. + let held = tab.eval(&format!("(() => {{ const el = {js}; return el && el.files ? Array.from(el.files).map(f => f.name + '(' + f.size + ' 位元組)').join('、') : ''; }})()")).ok().and_then(|v| v.as_str().map(String::from)).unwrap_or_default(); + if held.is_empty() { + return Err(format!("放了 {full},但上傳欄位還是空的(這一頁可能要先按「上傳」之類的按鈕,或欄位不是這一個)。")); + } + let after = snapshot(&mut tab); + let seen = changes(&before, &after); + let seen = if seen.contains("沒有變化") { String::new() } else { format!("\n{seen}") }; + Ok((format!("已把 {full} 放進上傳欄位,欄位裡現在是:{held}。{seen}"), None)) } "browser_download" => { // The file lands on the browser's own disk: Bot 的電腦's when it runs there.