feat: add generated scene skill platform hardening

This commit is contained in:
木炎
2026-04-21 23:19:06 +08:00
parent 118fc77935
commit 956f0c2b68
439 changed files with 61974 additions and 3645 deletions

View File

@@ -12,7 +12,7 @@ use tungstenite::{accept, Message};
const RUNTIME_DROP_PANIC_TEXT: &str =
"Cannot drop a runtime in a context where blocking is not allowed";
const TEST_ZHIHU_SKILLS_DIR: &str = "D:/data/ideaSpace/rust/sgClaw/claw/claw/skills";
const TEST_ZHIHU_SKILLS_DIR: &str = "D:/data/ideaSpace/rust/sgClaw/claw/claw/skills";
fn read_ws_text(stream: &mut tungstenite::WebSocket<std::net::TcpStream>) -> String {
match stream.read().unwrap() {
@@ -117,8 +117,12 @@ fn start_callback_host_hotlist_browser_server(
let handle = thread::spawn(move || {
let (stream, _) = listener.accept().unwrap();
stream.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
stream.set_write_timeout(Some(Duration::from_secs(2))).unwrap();
stream
.set_read_timeout(Some(Duration::from_secs(2)))
.unwrap();
stream
.set_write_timeout(Some(Duration::from_secs(2)))
.unwrap();
let mut websocket = accept(stream).unwrap();
let register = match websocket.read().unwrap() {
@@ -149,7 +153,9 @@ fn start_callback_host_hotlist_browser_server(
other => panic!("expected second browser action frame, got {other:?}"),
};
event_tx
.send(CallbackHostBrowserEvent::BrowserFrame(second_action.clone()))
.send(CallbackHostBrowserEvent::BrowserFrame(
second_action.clone(),
))
.unwrap();
let Some(close_values) = first_action.as_array() else {
@@ -328,7 +334,8 @@ fn start_callback_host_hotlist_browser_server(
(format!("ws://{address}"), handle)
}
fn start_direct_zhihu_browser_ws_server() -> (String, Arc<Mutex<Vec<String>>>, thread::JoinHandle<()>) {
fn start_direct_zhihu_browser_ws_server(
) -> (String, Arc<Mutex<Vec<String>>>, thread::JoinHandle<()>) {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let address = listener.local_addr().unwrap();
let frames = Arc::new(Mutex::new(Vec::new()));
@@ -336,8 +343,12 @@ fn start_direct_zhihu_browser_ws_server() -> (String, Arc<Mutex<Vec<String>>>, t
let handle = thread::spawn(move || {
let (stream, _) = listener.accept().unwrap();
stream.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
stream.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
stream
.set_read_timeout(Some(Duration::from_secs(5)))
.unwrap();
stream
.set_write_timeout(Some(Duration::from_secs(5)))
.unwrap();
let mut socket = accept(stream).unwrap();
let mut action_count = 0_u64;
@@ -364,7 +375,9 @@ fn start_direct_zhihu_browser_ws_server() -> (String, Arc<Mutex<Vec<String>>>, t
continue;
}
let values = parsed.as_array().expect("browser action frame should be an array");
let values = parsed
.as_array()
.expect("browser action frame should be an array");
let request_url = values[0].as_str().expect("request_url should be a string");
let action = values[1].as_str().expect("action should be a string");
action_count += 1;
@@ -380,7 +393,9 @@ fn start_direct_zhihu_browser_ws_server() -> (String, Arc<Mutex<Vec<String>>>, t
let callback_frame = match action {
"sgHideBrowserCallAfterLoaded" => {
let target_url = values[2].as_str().expect("navigate target_url should be a string");
let target_url = values[2]
.as_str()
.expect("navigate target_url should be a string");
json!([
request_url,
"callBackJsToCpp",
@@ -390,7 +405,9 @@ fn start_direct_zhihu_browser_ws_server() -> (String, Arc<Mutex<Vec<String>>>, t
])
}
"sgBrowserExcuteJsCodeByArea" => {
let target_url = values[2].as_str().expect("script target_url should be a string");
let target_url = values[2]
.as_str()
.expect("script target_url should be a string");
let response_text = if action_count == 2 {
"知乎热榜\n1 问题一 344万热度\n2 问题二 266万热度".to_string()
} else {
@@ -534,7 +551,10 @@ fn client_sends_connect_request_and_exits_after_status() {
assert!(output.status.success());
assert_eq!(request, ClientMessage::Connect);
let stdout = String::from_utf8(output.stdout).unwrap();
assert_eq!(stdout.lines().collect::<Vec<_>>(), vec!["status: connected"]);
assert_eq!(
stdout.lines().collect::<Vec<_>>(),
vec!["status: connected"]
);
}
#[test]
@@ -603,7 +623,10 @@ fn client_prints_completion_only_once() {
let mut websocket = accept(stream).unwrap();
let payload = read_ws_text(&mut websocket);
let request: ClientMessage = serde_json::from_str(&payload).unwrap();
assert_eq!(request.into_submit_task_request().unwrap().instruction, "打开百度搜索天气");
assert_eq!(
request.into_submit_task_request().unwrap().instruction,
"打开百度搜索天气"
);
websocket
.send(Message::Text(
@@ -663,7 +686,10 @@ fn client_prints_log_entries_in_order_before_completion() {
let mut websocket = accept(stream).unwrap();
let payload = read_ws_text(&mut websocket);
let request: ClientMessage = serde_json::from_str(&payload).unwrap();
assert_eq!(request.into_submit_task_request().unwrap().instruction, "打开百度搜索天气");
assert_eq!(
request.into_submit_task_request().unwrap().instruction,
"打开百度搜索天气"
);
for message in [
ServiceMessage::LogEntry {
@@ -680,7 +706,9 @@ fn client_prints_log_entries_in_order_before_completion() {
},
] {
websocket
.send(Message::Text(serde_json::to_string(&message).unwrap().into()))
.send(Message::Text(
serde_json::to_string(&message).unwrap().into(),
))
.unwrap();
}
websocket.close(None).unwrap();
@@ -758,11 +786,15 @@ fn client_exits_with_failure_when_service_disconnects_before_completion() {
assert!(!status.success());
let request = server.join().unwrap();
assert_eq!(request.into_submit_task_request().unwrap().instruction, "打开百度搜索天气");
assert_eq!(
request.into_submit_task_request().unwrap().instruction,
"打开百度搜索天气"
);
}
#[test]
fn client_to_service_regression_routes_zhihu_through_callback_host_without_invalid_hmac_seed_output() {
fn client_to_service_regression_routes_zhihu_through_callback_host_without_invalid_hmac_seed_output(
) {
let service_listener = TcpListener::bind("127.0.0.1:0").unwrap();
let service_addr = service_listener.local_addr().unwrap();
drop(service_listener);
@@ -770,7 +802,8 @@ fn client_to_service_regression_routes_zhihu_through_callback_host_without_inval
let (event_tx, event_rx) = mpsc::channel();
let (browser_ws_url, browser_server) = start_callback_host_hotlist_browser_server(event_tx);
let root = std::env::temp_dir().join(format!("sgclaw-service-task-flow-{}", uuid::Uuid::new_v4()));
let root =
std::env::temp_dir().join(format!("sgclaw-service-task-flow-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(&root).unwrap();
let config_path = root.join("sgclaw_config.json");
std::fs::write(
@@ -888,7 +921,8 @@ fn client_to_service_regression_routes_zhihu_through_callback_host_without_inval
let client_stdout = String::from_utf8_lossy(&client_output.stdout).into_owned();
let client_stderr = String::from_utf8_lossy(&client_output.stderr).into_owned();
let combined_output = format!("{client_stdout}\n{client_stderr}\n{service_stdout}\n{service_stderr}");
let combined_output =
format!("{client_stdout}\n{client_stderr}\n{service_stdout}\n{service_stderr}");
let register = match register {
CallbackHostBrowserEvent::BrowserFrame(value) => value,
@@ -927,13 +961,19 @@ fn client_to_service_regression_routes_zhihu_through_callback_host_without_inval
other => panic!("expected open-page command envelope, got {other:?}"),
};
assert_eq!(open_page["command"]["action"], json!("sgBrowerserOpenPage"));
assert_eq!(open_page["command"]["args"][0], json!("https://www.zhihu.com/hot"));
assert_eq!(
open_page["command"]["args"][0],
json!("https://www.zhihu.com/hot")
);
let get_text = match get_text {
CallbackHostBrowserEvent::CommandEnvelope(value) => value,
other => panic!("expected getText command envelope, got {other:?}"),
};
assert_eq!(get_text["command"]["action"], json!("sgBrowserExcuteJsCodeByDomain"));
assert_eq!(
get_text["command"]["action"],
json!("sgBrowserExcuteJsCodeByDomain")
);
assert_eq!(get_text["command"]["args"][0], json!("www.zhihu.com"));
assert!(get_text["command"]["args"][1]
.as_str()
@@ -943,15 +983,24 @@ fn client_to_service_regression_routes_zhihu_through_callback_host_without_inval
CallbackHostBrowserEvent::CommandEnvelope(value) => value,
other => panic!("expected eval command envelope, got {other:?}"),
};
assert_eq!(eval["command"]["action"], json!("sgBrowserExcuteJsCodeByDomain"));
assert_eq!(
eval["command"]["action"],
json!("sgBrowserExcuteJsCodeByDomain")
);
assert_eq!(eval["command"]["args"][0], json!("www.zhihu.com"));
assert!(eval["command"]["args"][1]
.as_str()
.is_some_and(|script| script.contains("sgclawOnEval")));
assert!(client_output.status.success());
assert!(client_stdout.contains("已导出并打开知乎热榜 Excel"), "client stdout={client_stdout}");
assert!(client_stdout.contains(".xlsx"), "client stdout={client_stdout}");
assert!(
client_stdout.contains("已导出并打开知乎热榜 Excel"),
"client stdout={client_stdout}"
);
assert!(
client_stdout.contains(".xlsx"),
"client stdout={client_stdout}"
);
assert!(
!combined_output.contains("invalid hmac seed: session key must not be empty"),
"target behavior must avoid the invalid hmac seed failure; combined_output={combined_output}"