← Commit history

v2.0.646 (insiders): pup-bridge#121: orphaned lane browser swept at launch and reported by pup_readiness; upgrade never reloads the worker in place (pup's own kill + recovery restore)

John Lauer ·7d4f91585c ·1mo ago ·parent 22bfff1
4 files changed +52−17
rust/Cargo.lock+12−12
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-pup-bridge"-version = "2.0.645"+version = "2.0.646" dependencies = [  "pup-browser",  "pup-cdp",@@ -655,7 +655,7 @@ dependencies = [  [[package]] name = "pup-browser"-version = "2.0.645"+version = "2.0.646" dependencies = [  "pup-core",  "pup-host",@@ -667,7 +667,7 @@ dependencies = [  [[package]] name = "pup-cdp"-version = "2.0.645"+version = "2.0.646" dependencies = [  "serde_json",  "thiserror",@@ -677,7 +677,7 @@ dependencies = [  [[package]] name = "pup-core"-version = "2.0.645"+version = "2.0.646" dependencies = [  "serde",  "serde_json",@@ -687,7 +687,7 @@ dependencies = [  [[package]] name = "pup-dashboard"-version = "2.0.645"+version = "2.0.646" dependencies = [  "pup-cdp",  "pup-core",@@ -700,7 +700,7 @@ dependencies = [  [[package]] name = "pup-host"-version = "2.0.645"+version = "2.0.646" dependencies = [  "serde",  "serde_json",@@ -709,14 +709,14 @@ dependencies = [  [[package]] name = "pup-host-mac"-version = "2.0.645"+version = "2.0.646" dependencies = [  "pup-host", ]  [[package]] name = "pup-host-win"-version = "2.0.645"+version = "2.0.646" dependencies = [  "pup-core",  "pup-host",@@ -725,7 +725,7 @@ dependencies = [  [[package]] name = "pup-identity"-version = "2.0.645"+version = "2.0.646" dependencies = [  "fontdue",  "image",@@ -735,7 +735,7 @@ dependencies = [  [[package]] name = "pup-session"-version = "2.0.645"+version = "2.0.646" dependencies = [  "pup-core",  "serde",@@ -745,7 +745,7 @@ dependencies = [  [[package]] name = "pup-vault"-version = "2.0.645"+version = "2.0.646" dependencies = [  "pup-core",  "pup-host",@@ -756,7 +756,7 @@ dependencies = [  [[package]] name = "pup-verbs"-version = "2.0.645"+version = "2.0.646" dependencies = [  "base64",  "png 0.17.16",
rust/Cargo.toml+1−1
@@ -4,7 +4,7 @@ members = ["core", "host", "host-win", "host-mac", "cdp", "browser", "session",  # One version for the whole bridge, in lockstep with src/BRIDGE_VERSION (tools/ship.sh bumps both). [workspace.package]-version = "2.0.645"+version = "2.0.646" edition = "2021" license = "MIT" 
rust/verbs/src/extdrive.rs+6−4
@@ -99,10 +99,12 @@ pub fn reload_stale_worker(ctx: &Ctx) {     let shim = crate::lane_shim(ctx);     let staged = pup_browser::stage_extensions_lane(&*ctx.host, ctx.port, "adom-you", true, Some(&lane), shim);     if staged.is_empty() { pup_core::log("[extdrive] worker reload skipped: re-staging the extension produced nothing".to_string()); return; }-    match run(json!({ "action": "reload" }), Duration::from_secs(8)) {-        Ok(_) => pup_core::log(format!("[extdrive] lane worker {} is behind pup {}; re-staged the extension and asked the worker to reload in place", have.replace('-', "."), want.replace('-', "."))),-        Err(e) => pup_core::log(format!("[extdrive] worker reload request failed: {}", e["error"].as_str().unwrap_or("?"))),-    }+    // 2.0.646 (pup-bridge#121): NEVER reload in place. chrome.runtime.reload() on the running worker broke the+    // channel: every verb on the open windows answered browser_gone while 13 browser processes and the windows+    // were alive (AdomLapper, 2026-09-19). End the lane browser as pup's OWN kill instead: recovery relaunches it+    // on the re-staged extension and restores every window at its last page, the same path a Chrome crash takes.+    let n = crate::kill_extdrive(ctx);+    pup_core::log(format!("[extdrive] lane worker {} is behind pup {}; re-staged the extension and ended the lane browser ({n} process(es)) so it comes back on the new files; every window is restored by recovery", have.replace('-', "."), want.replace('-', "."))); }  pub fn lane_worker_stale() -> Option<(String, String)> {
rust/verbs/src/lib.rs+33
@@ -175,6 +175,18 @@ fn schedule_birth_reassert(ctx: &Ctx, sid: &str, why: &'static str) {     }).ok(); } +/// pup-bridge#121: pids of browser processes whose command line names `profile_dir` (Windows: WMI through the+/// host's shell runner). Used only when the lane is NOT alive, so every match is an orphan.+pub fn orphan_browser_pids(ctx: &Ctx, profile_dir: &std::path::Path) -> Vec<u32> {+    if !cfg!(windows) { return Vec::new(); }+    let needle = profile_dir.to_string_lossy().replace('\\', "\\\\").replace('\'', "''");+    let script = format!("Get-CimInstance Win32_Process | Where-Object {{ $_.Name -eq 'chrome.exe' -and $_.CommandLine -and $_.CommandLine.Contains('{needle}') }} | ForEach-Object {{ $_.ProcessId }}");+    let out = ctx.host.run_shell_script(&script, Duration::from_secs(20));+    let mut v: Vec<u32> = out.lines().filter_map(|l| l.trim().parse().ok()).collect();+    v.retain(|p| *p != std::process::id());+    v+}+ pub fn launch_extdrive(ctx: &Ctx, url: &str) -> Result<u32, Value> {     // 2.0.517: the lane runs on adom-you, the ONE durable jar, because John is right that nothing should     // ever use anything else: a separate adom-you-ext jar had none of his cookies, logins or history,@@ -240,6 +252,18 @@ pub fn launch_extdrive(ctx: &Ctx, url: &str) -> Result<u32, Value> {         }         pup_core::log(format!("[extdrive] ended {stale} stale browser process(es) holding profile \"{profile}\" before relaunching"));     }+    // 2.0.646 (pup-bridge#121): an ORPHANED lane browser (launched by a pup instance that has since restarted,+    // so no pid list knows it) keeps the profile lock; every new launch then prints "Opening in existing+    // browser session." and exits 0, and pup retried blindly into that wall for 60 s per open (AdomLapper,+    // 2026-09-19, one hour). The lane is not alive here, so any browser on this profile is an orphan: end it,+    // then the lock below is removable. Only processes whose command line names THIS profile dir; never the+    // user's own Chrome.+    let orphans = orphan_browser_pids(ctx, &dir);+    if !orphans.is_empty() {+        for p in &orphans { ctx.host.kill(*p); }+        std::thread::sleep(Duration::from_millis(800));+        pup_core::log(format!("[lane] ended {} orphaned browser process(es) holding profile {} (no live lane owned them); launching fresh", orphans.len(), dir.display()));+    }     for f in ["SingletonLock", "SingletonCookie", "SingletonSocket", "lockfile"] { let _ = std::fs::remove_file(dir.join(f)); }     *EXTDRIVE_LANE.lock().unwrap() = lane.clone();   // the kill is done; this token is now the live one     let before: Vec<u32> = ctx.host.pids_for_image(&image);@@ -1029,6 +1053,15 @@ fn readiness(ctx: &Ctx) -> Value {     if let Some(o) = rd.as_object_mut() { if let Value::Object(cs) = pup_browser::chrome_stable::snapshot() { for (k, v) in cs { o.insert(k, v); } } }     let ready = rd.get("ready").and_then(|b| b.as_bool()).unwrap_or(false);     let installing = rd.get("installing").and_then(|b| b.as_bool()).unwrap_or(false);+    // 2.0.646 (pup-bridge#121): a lane browser nobody tracks (a pup restart lost its pid list) holds the profile+    // lock and makes every launch exit with "Opening in existing browser session". Say so here, with the pids.+    if !extdrive::alive(ctx) {+        let dir = pup_browser::profiles_dir(&*ctx.host).join("adom-you");+        let orphans = orphan_browser_pids(ctx, &dir);+        if !orphans.is_empty() {+            if let Some(o) = rd.as_object_mut() { o.insert("orphanedBrowser".into(), json!({ "pids": orphans, "profile": dir.to_string_lossy(), "_hint": "A pup browser on this profile is running but no live lane owns it (left over from a pup restart). It holds the profile lock, so a launch would exit at once. The next pup_open_window ends it and launches fresh; nothing to do by hand." })); }+        }+    }     let hint = if ready { "A drivable browser exists: pup_open_window {url} will open in the background. chromeForTestingInstalled tells you whether the toolbar build is cached (pup fetches it in the background on every box)." }         else if installing { "Chrome for Testing is downloading (active[] carries percent, elapsed and ETA). Poll this verb; do not start a second download. A system Chrome/Edge, if present, is drivable already." }         else if rd.get("installPhase").and_then(|p| p.as_str()) == Some("failed") { "The Chrome for Testing install FAILED (lastError). Retry pup_prewarm once the cause (network/proxy, disk) is fixed; a system Chrome/Edge is drivable meanwhile." }