feat: add network request detail and filtering for request tracking (#935)

* feat: add network request detail and filtering for request tracking

- Add `network request <requestId>` command to view full request/response
  details including response body via CDP Network.getResponseBody
- Add --type, --method, --status filter flags to `network requests`
  - --type: comma-separated resource types (xhr,fetch,document)
  - --method: filter by HTTP method
  - --status: supports exact (200), class (2xx), range (400-499)
- Extend TrackedRequest with request_id, post_data, status,
  response_headers, mime_type fields
- Update Network.responseReceived handler to also populate
  tracked_requests (previously only updated HAR entries)
- Add tests for parse commands and matches_status_filter
- Update README, SKILL.md, docs, and help text

Closes #932

* fix: show request ID and status in network requests output
This commit is contained in:
ChunHao Chen
2026-03-23 10:17:17 -05:00
committed by GitHub
parent 5c5c0d8081
commit ceaee00952
7 changed files with 293 additions and 28 deletions
+72 -2
View File
@@ -2078,7 +2078,7 @@ fn parse_set(rest: &[&str], id: &str) -> Result<Value, ParseError> {
/// Parse network interception, request inspection, and HAR recording commands.
fn parse_network(rest: &[&str], id: &str) -> Result<Value, ParseError> {
const VALID: &[&str] = &["route", "unroute", "requests", "har"];
const VALID: &[&str] = &["route", "unroute", "requests", "request", "har"];
match rest.first().copied() {
Some("route") => {
@@ -2102,12 +2102,34 @@ fn parse_network(rest: &[&str], id: &str) -> Result<Value, ParseError> {
let clear = rest.contains(&"--clear");
let filter_idx = rest.iter().position(|&s| s == "--filter");
let filter = filter_idx.and_then(|i| rest.get(i + 1).copied());
let type_idx = rest.iter().position(|&s| s == "--type");
let rtype = type_idx.and_then(|i| rest.get(i + 1).copied());
let method_idx = rest.iter().position(|&s| s == "--method");
let method = method_idx.and_then(|i| rest.get(i + 1).copied());
let status_idx = rest.iter().position(|&s| s == "--status");
let status = status_idx.and_then(|i| rest.get(i + 1).copied());
let mut cmd = json!({ "id": id, "action": "requests", "clear": clear });
if let Some(f) = filter {
cmd["filter"] = json!(f);
}
if let Some(t) = rtype {
cmd["type"] = json!(t);
}
if let Some(m) = method {
cmd["method"] = json!(m);
}
if let Some(s) = status {
cmd["status"] = json!(s);
}
Ok(cmd)
}
Some("request") => {
let request_id = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
context: "network request".to_string(),
usage: "network request <requestId>",
})?;
Ok(json!({ "id": id, "action": "request_detail", "requestId": request_id }))
}
Some("har") => {
const HAR_VALID: &[&str] = &["start", "stop"];
match rest.get(1).copied() {
@@ -2135,7 +2157,7 @@ fn parse_network(rest: &[&str], id: &str) -> Result<Value, ParseError> {
}),
None => Err(ParseError::MissingArguments {
context: "network".to_string(),
usage: "network <route|unroute|requests|har> [args...]",
usage: "network <route|unroute|requests|request|har> [args...]",
}),
}
}
@@ -2742,6 +2764,54 @@ mod tests {
assert!(matches!(result, Err(ParseError::MissingArguments { .. })));
}
#[test]
fn test_network_requests_type_filter() {
let cmd =
parse_command(&args("network requests --type xhr,fetch"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "requests");
assert_eq!(cmd["type"], "xhr,fetch");
}
#[test]
fn test_network_requests_method_filter() {
let cmd = parse_command(&args("network requests --method POST"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "requests");
assert_eq!(cmd["method"], "POST");
}
#[test]
fn test_network_requests_status_filter() {
let cmd = parse_command(&args("network requests --status 2xx"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "requests");
assert_eq!(cmd["status"], "2xx");
}
#[test]
fn test_network_requests_combined_filters() {
let cmd = parse_command(
&args("network requests --filter api --type xhr --method GET --status 200"),
&default_flags(),
)
.unwrap();
assert_eq!(cmd["filter"], "api");
assert_eq!(cmd["type"], "xhr");
assert_eq!(cmd["method"], "GET");
assert_eq!(cmd["status"], "200");
}
#[test]
fn test_network_request_detail() {
let cmd = parse_command(&args("network request 1234.5"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "request_detail");
assert_eq!(cmd["requestId"], "1234.5");
}
#[test]
fn test_network_request_detail_requires_id() {
let result = parse_command(&args("network request"), &default_flags());
assert!(matches!(result, Err(ParseError::MissingArguments { .. })));
}
// === Screenshot ===
#[test]