mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
Initial public release
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
//! Namespaced RPC methods (§4.3).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Namespaced method string (`system.handshake`, `tool.tab_list`, …).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Method {
|
||||
#[serde(rename = "system.handshake")]
|
||||
SystemHandshake,
|
||||
#[serde(rename = "system.ping")]
|
||||
SystemPing,
|
||||
#[serde(rename = "system.status")]
|
||||
SystemStatus,
|
||||
|
||||
#[serde(rename = "session.start")]
|
||||
SessionStart,
|
||||
#[serde(rename = "session.stop")]
|
||||
SessionStop,
|
||||
#[serde(rename = "session.stop_all")]
|
||||
SessionStopAll,
|
||||
#[serde(rename = "session.list")]
|
||||
SessionList,
|
||||
|
||||
#[serde(rename = "browser.list")]
|
||||
BrowserList,
|
||||
|
||||
#[serde(rename = "tool.session_start")]
|
||||
ToolSessionStart,
|
||||
#[serde(rename = "tool.session_stop")]
|
||||
ToolSessionStop,
|
||||
#[serde(rename = "tool.tab_list")]
|
||||
ToolTabList,
|
||||
#[serde(rename = "tool.tab_create")]
|
||||
ToolTabCreate,
|
||||
#[serde(rename = "tool.tab_close")]
|
||||
ToolTabClose,
|
||||
#[serde(rename = "tool.tab_borrow")]
|
||||
ToolTabBorrow,
|
||||
#[serde(rename = "tool.tab_return")]
|
||||
ToolTabReturn,
|
||||
#[serde(rename = "tool.tab_select")]
|
||||
ToolTabSelect,
|
||||
#[serde(rename = "tool.navigate")]
|
||||
ToolNavigate,
|
||||
#[serde(rename = "tool.navigate_back")]
|
||||
ToolNavigateBack,
|
||||
#[serde(rename = "tool.navigate_forward")]
|
||||
ToolNavigateForward,
|
||||
#[serde(rename = "tool.reload")]
|
||||
ToolReload,
|
||||
#[serde(rename = "tool.click")]
|
||||
ToolClick,
|
||||
#[serde(rename = "tool.fill")]
|
||||
ToolFill,
|
||||
#[serde(rename = "tool.press")]
|
||||
ToolPress,
|
||||
#[serde(rename = "tool.select")]
|
||||
ToolSelect,
|
||||
#[serde(rename = "tool.snapshot")]
|
||||
ToolSnapshot,
|
||||
#[serde(rename = "tool.get_html")]
|
||||
ToolGetHtml,
|
||||
#[serde(rename = "tool.screenshot")]
|
||||
ToolScreenshot,
|
||||
#[serde(rename = "tool.evaluate")]
|
||||
ToolEvaluate,
|
||||
#[serde(rename = "tool.wait_for_navigation")]
|
||||
ToolWaitForNavigation,
|
||||
#[serde(rename = "tool.wait_ms")]
|
||||
ToolWaitMs,
|
||||
#[serde(rename = "tool.request_help")]
|
||||
ToolRequestHelp,
|
||||
|
||||
#[serde(rename = "cancel")]
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl Method {
|
||||
/// Whether this RPC may modify browser state.
|
||||
///
|
||||
/// Used by the daemon's pending-interrupt machinery: when the
|
||||
/// user has clicked the agent-window mask's stop button, the
|
||||
/// next *mutating* tool call for that session is rejected with
|
||||
/// `ErrorCode::UserAborted`. Read-only tools and session-
|
||||
/// lifecycle RPCs pass through transparently.
|
||||
///
|
||||
/// **Compile-time enforcement.** The match below is exhaustive
|
||||
/// (no `_ =>` fallthrough). Adding a new `Method` variant is a
|
||||
/// compile error here, so classification cannot silently be
|
||||
/// skipped — the author has to make a deliberate choice.
|
||||
///
|
||||
/// **Judgment calls** (read these before adding new variants):
|
||||
///
|
||||
/// * `tool.evaluate` is classified as mutating because the
|
||||
/// daemon cannot statically distinguish a `document.title`
|
||||
/// read from a `form.submit()` write.
|
||||
/// * `tool.wait_*` are classified as read-only: they do not
|
||||
/// initiate any browser action; they observe state only.
|
||||
/// * `session.*` and `tool.session_*` are NOT gated. Blocking
|
||||
/// `session.stop` would prevent the agent from gracefully
|
||||
/// tearing down after observing the user's interrupt.
|
||||
/// * `cancel` is NOT gated. It's a control-plane operation
|
||||
/// (stops another in-flight RPC), not a browser action.
|
||||
pub fn is_mutating(&self) -> bool {
|
||||
match self {
|
||||
// Mutating tool calls — gated by pending-interrupt.
|
||||
Method::ToolTabCreate
|
||||
| Method::ToolTabClose
|
||||
| Method::ToolTabBorrow
|
||||
| Method::ToolTabReturn
|
||||
| Method::ToolTabSelect
|
||||
| Method::ToolNavigate
|
||||
| Method::ToolNavigateBack
|
||||
| Method::ToolNavigateForward
|
||||
| Method::ToolReload
|
||||
| Method::ToolClick
|
||||
| Method::ToolFill
|
||||
| Method::ToolPress
|
||||
| Method::ToolSelect
|
||||
| Method::ToolEvaluate => true,
|
||||
|
||||
// Read-only tool calls — transparent.
|
||||
Method::ToolTabList
|
||||
| Method::ToolSnapshot
|
||||
| Method::ToolGetHtml
|
||||
| Method::ToolScreenshot
|
||||
| Method::ToolWaitForNavigation
|
||||
| Method::ToolWaitMs
|
||||
| Method::ToolRequestHelp => false,
|
||||
|
||||
// Session lifecycle — not gated.
|
||||
Method::SessionStart
|
||||
| Method::SessionStop
|
||||
| Method::SessionStopAll
|
||||
| Method::SessionList
|
||||
| Method::ToolSessionStart
|
||||
| Method::ToolSessionStop => false,
|
||||
|
||||
// System / control — not gated.
|
||||
Method::SystemHandshake
|
||||
| Method::SystemPing
|
||||
| Method::SystemStatus
|
||||
| Method::BrowserList
|
||||
| Method::Cancel => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{CancelParams, CancelResult};
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn cancel_method_round_trips() {
|
||||
let method: Method = serde_json::from_value(json!("cancel")).unwrap();
|
||||
assert_eq!(method, Method::Cancel);
|
||||
assert_eq!(serde_json::to_value(method).unwrap(), json!("cancel"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_params_and_result_round_trip() {
|
||||
let params: CancelParams = serde_json::from_value(json!({ "rpc_id": "wait-1" })).unwrap();
|
||||
assert_eq!(params.rpc_id, "wait-1");
|
||||
let result = CancelResult { cancelled: true };
|
||||
assert_eq!(
|
||||
serde_json::to_value(result).unwrap(),
|
||||
json!({ "cancelled": true })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_mutating_classifies_read_only_tools_as_non_mutating() {
|
||||
assert!(!Method::ToolTabList.is_mutating());
|
||||
assert!(!Method::ToolSnapshot.is_mutating());
|
||||
assert!(!Method::ToolGetHtml.is_mutating());
|
||||
assert!(!Method::ToolScreenshot.is_mutating());
|
||||
assert!(!Method::ToolWaitForNavigation.is_mutating());
|
||||
assert!(!Method::ToolWaitMs.is_mutating());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_mutating_classifies_mutating_tools_as_mutating() {
|
||||
assert!(Method::ToolTabCreate.is_mutating());
|
||||
assert!(Method::ToolTabClose.is_mutating());
|
||||
assert!(Method::ToolTabBorrow.is_mutating());
|
||||
assert!(Method::ToolTabReturn.is_mutating());
|
||||
assert!(Method::ToolTabSelect.is_mutating());
|
||||
assert!(Method::ToolNavigate.is_mutating());
|
||||
assert!(Method::ToolNavigateBack.is_mutating());
|
||||
assert!(Method::ToolNavigateForward.is_mutating());
|
||||
assert!(Method::ToolReload.is_mutating());
|
||||
assert!(Method::ToolClick.is_mutating());
|
||||
assert!(Method::ToolFill.is_mutating());
|
||||
assert!(Method::ToolPress.is_mutating());
|
||||
assert!(Method::ToolSelect.is_mutating());
|
||||
assert!(Method::ToolEvaluate.is_mutating());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_mutating_classifies_session_lifecycle_as_non_mutating() {
|
||||
// Session lifecycle RPCs are not "mutating" for the purposes
|
||||
// of pending-interrupt gating — gating them would prevent the
|
||||
// agent from gracefully tearing down after observing the
|
||||
// user's interrupt.
|
||||
assert!(!Method::SessionStart.is_mutating());
|
||||
assert!(!Method::SessionStop.is_mutating());
|
||||
assert!(!Method::SessionStopAll.is_mutating());
|
||||
assert!(!Method::SessionList.is_mutating());
|
||||
assert!(!Method::ToolSessionStart.is_mutating());
|
||||
assert!(!Method::ToolSessionStop.is_mutating());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_mutating_classifies_system_methods_as_non_mutating() {
|
||||
assert!(!Method::SystemHandshake.is_mutating());
|
||||
assert!(!Method::SystemPing.is_mutating());
|
||||
assert!(!Method::SystemStatus.is_mutating());
|
||||
assert!(!Method::BrowserList.is_mutating());
|
||||
assert!(!Method::Cancel.is_mutating());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user