feat: route staged scene skills through runtime
Add registry-driven scene routing and multi-root skill loading so fault-details and 95598 scene skills can be triggered from natural language while still running through the browser-backed runtime. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,9 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use common::MockTransport;
|
||||
use serde_json::json;
|
||||
use sgclaw::browser::{BrowserBackend, PipeBrowserBackend};
|
||||
use sgclaw::compat::browser_script_skill_tool::BrowserScriptSkillTool;
|
||||
use sgclaw::compat::browser_script_skill_tool::{
|
||||
execute_browser_script_tool, BrowserScriptSkillTool,
|
||||
};
|
||||
use sgclaw::pipe::{Action, AgentMessage, BrowserMessage, BrowserPipeTool, Timing};
|
||||
use sgclaw::security::MacPolicy;
|
||||
use zeroclaw::skills::SkillTool;
|
||||
@@ -113,6 +115,125 @@ return {
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn browser_script_helper_executes_packaged_script_via_eval() {
|
||||
let skill_dir = unique_temp_dir("sgclaw-browser-script-helper");
|
||||
let scripts_dir = skill_dir.join("scripts");
|
||||
fs::create_dir_all(&scripts_dir).unwrap();
|
||||
fs::write(
|
||||
scripts_dir.join("collect_fault_details.js"),
|
||||
r#"
|
||||
return {
|
||||
sheet_name: "故障明细",
|
||||
rows: [[args.period, "已完成"]]
|
||||
};
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response {
|
||||
seq: 1,
|
||||
success: true,
|
||||
data: json!({
|
||||
"text": {
|
||||
"sheet_name": "故障明细",
|
||||
"rows": [["2026-04", "已完成"]]
|
||||
}
|
||||
}),
|
||||
aom_snapshot: vec![],
|
||||
timing: Timing {
|
||||
queue_ms: 1,
|
||||
exec_ms: 5,
|
||||
},
|
||||
}]));
|
||||
let browser_tool = BrowserPipeTool::new(
|
||||
transport.clone(),
|
||||
test_policy(),
|
||||
vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
)
|
||||
.with_response_timeout(Duration::from_secs(1));
|
||||
let backend: Arc<dyn BrowserBackend> = Arc::new(PipeBrowserBackend::from_inner(browser_tool));
|
||||
|
||||
let mut args = HashMap::new();
|
||||
args.insert("period".to_string(), "Target report period".to_string());
|
||||
let skill_tool = SkillTool {
|
||||
name: "collect_fault_details".to_string(),
|
||||
description: "Collect fault detail rows".to_string(),
|
||||
kind: "browser_script".to_string(),
|
||||
command: "scripts/collect_fault_details.js".to_string(),
|
||||
args,
|
||||
};
|
||||
|
||||
let result = execute_browser_script_tool(&skill_tool, &skill_dir, backend, json!({
|
||||
"expected_domain": "https://www.zhihu.com/hot",
|
||||
"period": "2026-04"
|
||||
}))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let sent = transport.sent_messages();
|
||||
assert!(result.success);
|
||||
assert_eq!(
|
||||
serde_json::from_str::<serde_json::Value>(&result.output).unwrap(),
|
||||
json!({
|
||||
"sheet_name": "故障明细",
|
||||
"rows": [["2026-04", "已完成"]]
|
||||
})
|
||||
);
|
||||
assert!(matches!(
|
||||
&sent[0],
|
||||
AgentMessage::Command {
|
||||
action,
|
||||
params,
|
||||
security,
|
||||
..
|
||||
} if action == &Action::Eval
|
||||
&& security.expected_domain == "www.zhihu.com"
|
||||
&& params["script"].as_str().unwrap().contains("const args = {\"period\":\"2026-04\"};")
|
||||
&& params["script"].as_str().unwrap().contains("sheet_name")
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn browser_script_helper_requires_expected_domain() {
|
||||
let skill_dir = unique_temp_dir("sgclaw-browser-script-helper-missing-domain");
|
||||
let scripts_dir = skill_dir.join("scripts");
|
||||
fs::create_dir_all(&scripts_dir).unwrap();
|
||||
fs::write(scripts_dir.join("collect_fault_details.js"), "return { ok: true };\n").unwrap();
|
||||
|
||||
let transport = Arc::new(MockTransport::new(vec![]));
|
||||
let browser_tool = BrowserPipeTool::new(
|
||||
transport.clone(),
|
||||
test_policy(),
|
||||
vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||
)
|
||||
.with_response_timeout(Duration::from_secs(1));
|
||||
let backend: Arc<dyn BrowserBackend> = Arc::new(PipeBrowserBackend::from_inner(browser_tool));
|
||||
|
||||
let mut args = HashMap::new();
|
||||
args.insert("period".to_string(), "Target report period".to_string());
|
||||
let skill_tool = SkillTool {
|
||||
name: "collect_fault_details".to_string(),
|
||||
description: "Collect fault detail rows".to_string(),
|
||||
kind: "browser_script".to_string(),
|
||||
command: "scripts/collect_fault_details.js".to_string(),
|
||||
args,
|
||||
};
|
||||
|
||||
let result = execute_browser_script_tool(&skill_tool, &skill_dir, backend, json!({
|
||||
"period": "2026-04"
|
||||
}))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!result.success);
|
||||
assert_eq!(
|
||||
result.error.as_deref(),
|
||||
Some("missing required field expected_domain")
|
||||
);
|
||||
assert!(transport.sent_messages().is_empty());
|
||||
}
|
||||
|
||||
fn unique_temp_dir(prefix: &str) -> PathBuf {
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
|
||||
Reference in New Issue
Block a user