mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
feat: add console observation command
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ConsoleEntry",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"level",
|
||||
"sequence",
|
||||
"text"
|
||||
],
|
||||
"properties": {
|
||||
"column": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"kind": {
|
||||
"$ref": "#/definitions/ConsoleEntryKind"
|
||||
},
|
||||
"level": {
|
||||
"type": "string"
|
||||
},
|
||||
"line": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"sequence": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"stack_trace": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConsoleStackFrame"
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": [
|
||||
"number",
|
||||
"null"
|
||||
],
|
||||
"format": "double"
|
||||
},
|
||||
"truncated": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"ConsoleEntryKind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"console",
|
||||
"exception",
|
||||
"log"
|
||||
]
|
||||
},
|
||||
"ConsoleStackFrame": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"column": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"function_name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"line": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ConsoleParams",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"session_id"
|
||||
],
|
||||
"properties": {
|
||||
"include_stack": {
|
||||
"description": "Include structured stack frames. Defaults to false.",
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"limit": {
|
||||
"description": "Maximum number of entries to return. Extension applies safe defaults and caps.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 1.0
|
||||
},
|
||||
"max_text_chars": {
|
||||
"description": "Maximum characters returned per entry text. Extension applies safe defaults and caps.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 1.0
|
||||
},
|
||||
"session_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"since": {
|
||||
"description": "Return entries with sequence strictly greater than this cursor.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"tab_id": {
|
||||
"description": "Optional target tab. Defaults to the Agent Window's currently active tab.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ConsoleResult",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"next_since",
|
||||
"tab_id"
|
||||
],
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConsoleEntry"
|
||||
}
|
||||
},
|
||||
"next_since": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"tab_id": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"truncated": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"ConsoleEntry": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"kind",
|
||||
"level",
|
||||
"sequence",
|
||||
"text"
|
||||
],
|
||||
"properties": {
|
||||
"column": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"kind": {
|
||||
"$ref": "#/definitions/ConsoleEntryKind"
|
||||
},
|
||||
"level": {
|
||||
"type": "string"
|
||||
},
|
||||
"line": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"sequence": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"stack_trace": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ConsoleStackFrame"
|
||||
}
|
||||
},
|
||||
"text": {
|
||||
"type": "string"
|
||||
},
|
||||
"timestamp": {
|
||||
"type": [
|
||||
"number",
|
||||
"null"
|
||||
],
|
||||
"format": "double"
|
||||
},
|
||||
"truncated": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ConsoleEntryKind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"console",
|
||||
"exception",
|
||||
"log"
|
||||
]
|
||||
},
|
||||
"ConsoleStackFrame": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"column": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"function_name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"line": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ConsoleStackFrame",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"column": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"function_name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"line": {
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "int64"
|
||||
},
|
||||
"url": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,10 @@ fn main() {
|
||||
dump!(GetHtmlResult, "tool_get_html_result");
|
||||
dump!(ScreenshotParams, "tool_screenshot_params");
|
||||
dump!(ScreenshotResult, "tool_screenshot_result");
|
||||
dump!(ConsoleParams, "tool_console_params");
|
||||
dump!(ConsoleResult, "tool_console_result");
|
||||
dump!(ConsoleEntry, "tool_console_entry");
|
||||
dump!(ConsoleStackFrame, "tool_console_stack_frame");
|
||||
|
||||
dump!(EvaluateParams, "tool_evaluate_params");
|
||||
dump!(EvaluateResult, "tool_evaluate_result");
|
||||
|
||||
@@ -62,6 +62,8 @@ pub enum Method {
|
||||
ToolGetHtml,
|
||||
#[serde(rename = "tool.screenshot")]
|
||||
ToolScreenshot,
|
||||
#[serde(rename = "tool.console")]
|
||||
ToolConsole,
|
||||
#[serde(rename = "tool.evaluate")]
|
||||
ToolEvaluate,
|
||||
#[serde(rename = "tool.wait_for_navigation")]
|
||||
@@ -124,6 +126,7 @@ impl Method {
|
||||
| Method::ToolSnapshot
|
||||
| Method::ToolGetHtml
|
||||
| Method::ToolScreenshot
|
||||
| Method::ToolConsole
|
||||
| Method::ToolWaitForNavigation
|
||||
| Method::ToolWaitMs
|
||||
| Method::ToolRequestHelp => false,
|
||||
@@ -159,6 +162,13 @@ mod tests {
|
||||
assert_eq!(serde_json::to_value(method).unwrap(), json!("cancel"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn console_method_round_trips() {
|
||||
let method: Method = serde_json::from_value(json!("tool.console")).unwrap();
|
||||
assert_eq!(method, Method::ToolConsole);
|
||||
assert_eq!(serde_json::to_value(method).unwrap(), json!("tool.console"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_params_and_result_round_trip() {
|
||||
let params: CancelParams = serde_json::from_value(json!({ "rpc_id": "wait-1" })).unwrap();
|
||||
@@ -176,6 +186,7 @@ mod tests {
|
||||
assert!(!Method::ToolSnapshot.is_mutating());
|
||||
assert!(!Method::ToolGetHtml.is_mutating());
|
||||
assert!(!Method::ToolScreenshot.is_mutating());
|
||||
assert!(!Method::ToolConsole.is_mutating());
|
||||
assert!(!Method::ToolWaitForNavigation.is_mutating());
|
||||
assert!(!Method::ToolWaitMs.is_mutating());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
//! `tool.console` — read buffered console/log/exception messages.
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ConsoleParams {
|
||||
pub session_id: String,
|
||||
/// Optional target tab. Defaults to the Agent Window's currently active tab.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub tab_id: Option<i64>,
|
||||
/// Return entries with sequence strictly greater than this cursor.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub since: Option<u64>,
|
||||
/// Maximum number of entries to return. Extension applies safe defaults and caps.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(range(min = 1))]
|
||||
pub limit: Option<u32>,
|
||||
/// Maximum characters returned per entry text. Extension applies safe defaults and caps.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[schemars(range(min = 1))]
|
||||
pub max_text_chars: Option<u32>,
|
||||
/// Include structured stack frames. Defaults to false.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub include_stack: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ConsoleEntryKind {
|
||||
Console,
|
||||
Exception,
|
||||
Log,
|
||||
}
|
||||
|
||||
impl ConsoleEntryKind {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Console => "console",
|
||||
Self::Exception => "exception",
|
||||
Self::Log => "log",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ConsoleStackFrame {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub function_name: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub url: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub line: Option<i64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub column: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ConsoleEntry {
|
||||
pub sequence: u64,
|
||||
pub kind: ConsoleEntryKind,
|
||||
pub level: String,
|
||||
pub text: String,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub url: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub line: Option<i64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub column: Option<i64>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub timestamp: Option<f64>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub stack_trace: Vec<ConsoleStackFrame>,
|
||||
#[serde(default)]
|
||||
pub truncated: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ConsoleResult {
|
||||
pub tab_id: i64,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub entries: Vec<ConsoleEntry>,
|
||||
pub next_since: u64,
|
||||
#[serde(default)]
|
||||
pub truncated: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn console_params_omit_optional_fields() {
|
||||
let params = ConsoleParams {
|
||||
session_id: "aa11".into(),
|
||||
tab_id: None,
|
||||
since: None,
|
||||
limit: None,
|
||||
max_text_chars: None,
|
||||
include_stack: None,
|
||||
};
|
||||
let value = serde_json::to_value(¶ms).unwrap();
|
||||
assert_eq!(value["session_id"], "aa11");
|
||||
assert!(value.get("tab_id").is_none());
|
||||
assert!(value.get("since").is_none());
|
||||
assert!(value.get("limit").is_none());
|
||||
assert!(value.get("max_text_chars").is_none());
|
||||
assert!(value.get("include_stack").is_none());
|
||||
let round: ConsoleParams = serde_json::from_value(value).unwrap();
|
||||
assert_eq!(round, params);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn console_result_round_trips_with_stack_and_truncation() {
|
||||
let result = ConsoleResult {
|
||||
tab_id: 7,
|
||||
entries: vec![ConsoleEntry {
|
||||
sequence: 12,
|
||||
kind: ConsoleEntryKind::Exception,
|
||||
level: "error".into(),
|
||||
text: "Uncaught TypeError: x is undefined".into(),
|
||||
url: Some("https://example.test/app.js".into()),
|
||||
line: Some(42),
|
||||
column: Some(7),
|
||||
timestamp: Some(1234.5),
|
||||
stack_trace: vec![ConsoleStackFrame {
|
||||
function_name: Some("render".into()),
|
||||
url: Some("https://example.test/app.js".into()),
|
||||
line: Some(42),
|
||||
column: Some(7),
|
||||
}],
|
||||
truncated: true,
|
||||
}],
|
||||
next_since: 12,
|
||||
truncated: true,
|
||||
};
|
||||
let value = serde_json::to_value(&result).unwrap();
|
||||
assert_eq!(value["entries"][0]["kind"], json!("exception"));
|
||||
assert_eq!(
|
||||
value["entries"][0]["stack_trace"][0]["function_name"],
|
||||
json!("render")
|
||||
);
|
||||
let round: ConsoleResult = serde_json::from_value(value).unwrap();
|
||||
assert_eq!(round, result);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Typed params/results for the 21 `tool.*` RPC methods (§7).
|
||||
|
||||
pub mod console;
|
||||
pub mod dialog;
|
||||
pub mod human_loop;
|
||||
pub mod interaction;
|
||||
@@ -10,6 +11,7 @@ pub mod session;
|
||||
pub mod tabs;
|
||||
pub mod waits;
|
||||
|
||||
pub use console::*;
|
||||
pub use dialog::*;
|
||||
pub use human_loop::*;
|
||||
pub use interaction::*;
|
||||
|
||||
Reference in New Issue
Block a user