Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/network-timing-dashboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agent-browser": minor
---

Add timing and transfer size columns to the dashboard network panel. Each request row now shows the encoded transfer size and total duration computed from CDP `Network.loadingFinished` events.
61 changes: 50 additions & 11 deletions cli/src/native/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ pub struct TrackedRequest {
pub response_headers: Option<Value>,
#[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(rename = "encodedDataLength", skip_serializing_if = "Option::is_none")]
pub encoded_data_length: Option<i64>,
#[serde(rename = "durationMs", skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<f64>,
#[serde(skip)]
pub mono_start: Option<f64>,
}

pub struct FetchPausedRequest {
Expand Down Expand Up @@ -677,6 +683,8 @@ impl DaemonState {
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
let mono_start =
event.params.get("timestamp").and_then(|v| v.as_f64());
self.tracked_requests.push(TrackedRequest {
url,
method,
Expand All @@ -691,6 +699,9 @@ impl DaemonState {
status: None,
response_headers: None,
mime_type: None,
encoded_data_length: None,
duration_ms: None,
mono_start,
});
}
}
Expand Down Expand Up @@ -754,6 +765,10 @@ impl DaemonState {
.get("mimeType")
.and_then(|v| v.as_str())
.map(String::from);
let resp_encoded_len = response
.get("encodedDataLength")
.and_then(|v| v.as_i64())
.filter(|&n| n >= 0);
if let Some(entry) = self
.tracked_requests
.iter_mut()
Expand All @@ -763,11 +778,14 @@ impl DaemonState {
entry.status = status;
entry.mime_type = resp_mime;
entry.response_headers = resp_headers;
entry.encoded_data_length = resp_encoded_len;
}
}
}
}
"Network.loadingFinished" if self.har_recording => {
"Network.loadingFinished"
if self.har_recording || self.request_tracking =>
{
let request_id = event
.params
.get("requestId")
Expand All @@ -778,17 +796,38 @@ impl DaemonState {
.params
.get("encodedDataLength")
.and_then(|v| v.as_i64());
if let Some(entry) = self
.har_entries
.iter_mut()
.rev()
.find(|e| e.request_id == request_id)
{
if let Some(ts) = timestamp {
entry.loading_finished_timestamp = Some(ts);
if self.har_recording {
if let Some(entry) = self
.har_entries
.iter_mut()
.rev()
.find(|e| e.request_id == request_id)
{
if let Some(ts) = timestamp {
entry.loading_finished_timestamp = Some(ts);
}
if let Some(len) = encoded_data_length {
entry.response_body_size = len;
}
}
if let Some(len) = encoded_data_length {
entry.response_body_size = len;
}
if self.request_tracking {
if let Some(entry) = self
.tracked_requests
.iter_mut()
.rev()
.find(|e| e.request_id == request_id)
{
if let Some(len) = encoded_data_length {
entry.encoded_data_length = Some(len);
}
if let (Some(start), Some(end)) = (entry.mono_start, timestamp)
{
let ms = ((end - start) * 1000.0).round();
if ms >= 0.0 {
entry.duration_ms = Some(ms);
}
}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions cli/src/native/parity_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,9 @@ async fn test_tracked_request_struct() {
status: Some(200),
response_headers: None,
mime_type: Some("text/html".to_string()),
encoded_data_length: None,
duration_ms: None,
mono_start: None,
};
let serialized = serde_json::to_value(&tr).unwrap();
assert_eq!(serialized["url"], "https://example.com/api");
Expand All @@ -576,6 +579,9 @@ async fn test_request_tracking_state() {
status: None,
response_headers: None,
mime_type: None,
encoded_data_length: None,
duration_ms: None,
mono_start: None,
});
state.tracked_requests.push(super::actions::TrackedRequest {
url: "https://other.com".to_string(),
Expand All @@ -588,6 +594,9 @@ async fn test_request_tracking_state() {
status: None,
response_headers: None,
mime_type: None,
encoded_data_length: None,
duration_ms: None,
mono_start: None,
});
assert_eq!(state.tracked_requests.len(), 2);

Expand Down
22 changes: 22 additions & 0 deletions packages/dashboard/src/components/network-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ interface NetworkRequest {
requestId: string;
mimeType?: string;
timestamp: number;
encodedDataLength?: number;
durationMs?: number;
}

type TypeFilter = "all" | "xhr" | "doc" | "css" | "js" | "img" | "font" | "other";
Expand Down Expand Up @@ -71,6 +73,20 @@ function urlHost(url: string): string {
}
}

function formatSize(bytes?: number): string {
if (bytes == null) return "—";
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

function formatDuration(ms?: number): string {
if (ms == null) return "—";
if (ms < 1) return "<1ms";
if (ms < 1000) return `${Math.round(ms)}ms`;
return `${(ms / 1000).toFixed(1)}s`;
}

export function NetworkPanel() {
const sessionName = useAtomValue(activeSessionNameAtom);

Expand Down Expand Up @@ -292,6 +308,12 @@ export function NetworkPanel() {
<span className="min-w-0 flex-1 truncate text-foreground" title={r.url}>
{truncateUrl(r.url, 80)}
</span>
<span className="w-14 shrink-0 text-right tabular-nums text-[10px] text-muted-foreground/60">
{formatSize(r.encodedDataLength)}
</span>
<span className="w-12 shrink-0 text-right tabular-nums text-[10px] text-muted-foreground/60">
{formatDuration(r.durationMs)}
</span>
<span className="shrink-0 text-[10px] text-muted-foreground/60">
{r.resourceType}
</span>
Expand Down
Loading