fix(observability): stamp page URL on screenshot/network; enable capture on --clear (issue #8)
Field report #8: in extension-relay sessions, reads (eval/screenshot/network) could silently run against whatever tab drifted into focus, with no signal, and network capture was intermittently empty. - #8.1: screenshot and `network requests` now print `screenshot @ <url>` / `network @ <url>` to stderr (mirrors the existing `eval @ <url>`), and the responses carry `origin`. A read against the wrong/drifted tab — and the "0 captured" vs "wrong page" ambiguity — is now obvious. - #8.3: `network requests --clear` now enables Network capture immediately instead of lazily on the next read, so requests fired between `--clear` and the following read are tracked (fixes the "No requests captured" on first try, works on retry" race). Extracted enable_request_tracking helper. - #8.2: the daemon version-mismatch restart notice now spells out that in-memory context (active tab, refs, captured requests) is reset and tells the user to re-open the target URL if the next read looks blank/wrong. Verified on a launched browser: coordinate clicks land, screenshot/network stamps appear, and a fetch after --clear is captured on the first read.
This commit is contained in:
@@ -625,7 +625,10 @@ pub fn ensure_daemon(session: &str, opts: &DaemonOptions) -> Result<DaemonResult
|
||||
// version (e.g. after an upgrade), kill it and start a fresh one.
|
||||
if !daemon_version_matches(session) {
|
||||
eprintln!(
|
||||
"{} Daemon version mismatch detected, restarting...",
|
||||
"{} Daemon version mismatch detected, restarting... \
|
||||
In-memory context (active tab, refs, captured requests) is reset. \
|
||||
If the next read looks blank or lands on the wrong page, re-open \
|
||||
your target URL before retrying (issue #8.2).",
|
||||
crate::color::warning_indicator()
|
||||
);
|
||||
// Best-effort: ask the old daemon for its current URL so the
|
||||
|
||||
+43
-12
@@ -2981,6 +2981,13 @@ async fn handle_screenshot(cmd: &Value, state: &mut DaemonState) -> Result<Value
|
||||
response["annotations"] = serde_json::to_value(&result.annotations)
|
||||
.map_err(|e| format!("Failed to serialize annotations: {}", e))?;
|
||||
}
|
||||
// Stamp which page was captured so a screenshot of the wrong tab is obvious
|
||||
// (issue #8.1: relay sessions can drift to whatever tab the user activated).
|
||||
if let Ok(url) = mgr.get_url().await {
|
||||
if !url.is_empty() {
|
||||
response["origin"] = json!(url);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
@@ -7914,23 +7921,40 @@ pub fn matches_status_filter(status: Option<i64>, filter: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn enable_request_tracking(state: &mut DaemonState) {
|
||||
if state.request_tracking {
|
||||
return;
|
||||
}
|
||||
state.request_tracking = true;
|
||||
if let Some(ref mgr) = state.browser {
|
||||
if let Ok(session_id) = mgr.active_session_id() {
|
||||
let _ = mgr
|
||||
.client
|
||||
.send_command_no_params("Network.enable", Some(session_id))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_requests(cmd: &Value, state: &mut DaemonState) -> Result<Value, String> {
|
||||
if cmd.get("clear").and_then(|v| v.as_bool()).unwrap_or(false) {
|
||||
state.tracked_requests.clear();
|
||||
// Enable Network capture NOW, on `--clear`, not lazily on the next read.
|
||||
// `--clear` is the canonical "start capturing fresh" call, so requests
|
||||
// fired between it and the following `requests` read must be tracked.
|
||||
// Lazy-enabling only on read missed exactly those → intermittent
|
||||
// "No requests captured" on the first try, fine on retry (issue #8.3).
|
||||
enable_request_tracking(state).await;
|
||||
return Ok(json!({ "cleared": true }));
|
||||
}
|
||||
|
||||
if !state.request_tracking {
|
||||
state.request_tracking = true;
|
||||
if let Some(ref mgr) = state.browser {
|
||||
if let Ok(session_id) = mgr.active_session_id() {
|
||||
let _ = mgr
|
||||
.client
|
||||
.send_command_no_params("Network.enable", Some(session_id))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
enable_request_tracking(state).await;
|
||||
// Current page URL, so a `requests` read on a drifted/wrong tab is obvious
|
||||
// and "0 captured" can't be confused with "wrong page" (issues #8.1/#8.3).
|
||||
let origin = match state.browser.as_ref() {
|
||||
Some(mgr) => mgr.get_url().await.ok().filter(|u| !u.is_empty()),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let filter = cmd.get("filter").and_then(|v| v.as_str());
|
||||
let type_filter = cmd.get("type").and_then(|v| v.as_str());
|
||||
@@ -7967,7 +7991,14 @@ async fn handle_requests(cmd: &Value, state: &mut DaemonState) -> Result<Value,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(json!({ "requests": requests }))
|
||||
// NB: do NOT add a top-level `count` field here — the human formatter treats
|
||||
// any `{count}` as a `get count` result and prints just the number, which
|
||||
// would swallow the request list. The list length is self-evident.
|
||||
let mut response = json!({ "requests": requests });
|
||||
if let Some(o) = origin {
|
||||
response["origin"] = json!(o);
|
||||
}
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn handle_request_detail(cmd: &Value, state: &mut DaemonState) -> Result<Value, String> {
|
||||
|
||||
@@ -595,6 +595,15 @@ pub fn print_response_with_opts(resp: &Response, action: Option<&str>, opts: &Ou
|
||||
}
|
||||
// Network requests
|
||||
if let Some(requests) = data.get("requests").and_then(|v| v.as_array()) {
|
||||
// Stamp the page these requests were read from, mirroring `eval @ url`,
|
||||
// so a read against a drifted/wrong tab is obvious (issue #8.1).
|
||||
if let Some(o) = data
|
||||
.get("origin")
|
||||
.and_then(|v| v.as_str())
|
||||
.filter(|o| !o.is_empty())
|
||||
{
|
||||
eprintln!("network @ {o}");
|
||||
}
|
||||
if requests.is_empty() {
|
||||
println!("No requests captured");
|
||||
} else {
|
||||
@@ -798,6 +807,15 @@ pub fn print_response_with_opts(resp: &Response, action: Option<&str>, opts: &Ou
|
||||
color::success_indicator(),
|
||||
color::green(path)
|
||||
);
|
||||
// Stamp which page was captured (mirrors `eval @ url`) so a
|
||||
// screenshot of the wrong/drifted tab is obvious (issue #8.1).
|
||||
if let Some(o) = data
|
||||
.get("origin")
|
||||
.and_then(|v| v.as_str())
|
||||
.filter(|o| !o.is_empty())
|
||||
{
|
||||
eprintln!("screenshot @ {o}");
|
||||
}
|
||||
if let Some(annotations) = data.get("annotations").and_then(|v| v.as_array()) {
|
||||
// Cap the printed legend on dense pages (it can be
|
||||
// hundreds of lines and flood the terminal). The image
|
||||
|
||||
Reference in New Issue
Block a user