app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
← Commit history
docs/PLAN-rust-port.md+7−1@@ -96,7 +96,13 @@ John's words. Written 2026-09-05 against pup 2.0.396; John approved the plan the edge, window born in the background then raised by a REAL click on its taskbar button, which is also the only thing that clears the Windows 11 attention tint) and measured pup-bridge#99: Windows 11 25H2 (build 26200) paints NO taskbar progress for any app, plain COM on Notepad included; the skills say so now.- The recorder HUD shows once the last window closes, so the closing line minimizes it and captions top. Jump lists: the two-right-clicks flash is ab#155+ The recorder HUD shows once the last window closes, so the closing line minimizes it and captions top.+ 2.0.420 (2026-09-06, candidate on arav-rog, smoke pending): pup-bridge#98 runtime data under+ `ADOM_BRIDGE_DATA_DIR` (profiles, sessions, screenshots, recordings; legacy dirs moved once by rename,+ fallback to the legacy path when the variable is absent, which is the case until ab ships it) plus+ retention in the daily janitor (screenshots 14 d / 1 GB, recordings 30 d / 3 GB); both unit-tested+ natively in pup_core. ab#155 closed by ab: only a visible Start Menu shortcut anchors the jump list,+ opt-in `anchor:true` in ab 2.1.49; `aumidShortcut` maps onto it (off by default, John's call). Jump lists: the two-right-clicks flash is ab#155 (registry-only AUMIDs, no anchoring shortcut); `aumidShortcut` (opt-in) asks ab for the anchor; human verification pending. Filed: ab#152 (kind:exe seed npm), ab#155 (jump-list anchor), ab#157 (forward bridge consent prompts into ah for boxes that block toasts).
rust/core/src/lib.rs+32@@ -334,3 +334,35 @@ pub fn retention_sweep(dir: &std::path::Path, max_age_days: u64, max_bytes: u64) if let Ok(rd) = std::fs::read_dir(dir) { for e in rd.flatten() { if e.path().is_dir() { let _ = std::fs::remove_dir(e.path()); } } } (removed, freed) }++#[cfg(test)]+mod data_dir_tests {+ use super::*;+ fn tmp(name: &str) -> std::path::PathBuf { let d = std::env::temp_dir().join(format!("pup-core-test-{name}-{}", std::process::id())); let _ = std::fs::remove_dir_all(&d); std::fs::create_dir_all(&d).unwrap(); d }+ #[test]+ fn data_subdir_moves_legacy_once_and_falls_back_without_env() {+ let root = tmp("data"); let legacy = root.join("legacy-profiles"); std::fs::create_dir_all(legacy.join("adom-you")).unwrap(); std::fs::write(legacy.join("adom-you").join("Cookies"), b"x").unwrap();+ std::env::remove_var("ADOM_BRIDGE_DATA_DIR");+ assert_eq!(data_subdir("profiles", legacy.clone()), legacy, "no env: legacy path");+ std::env::set_var("ADOM_BRIDGE_DATA_DIR", root.join("bridge-data").to_string_lossy().to_string());+ let new = data_subdir("profiles", legacy.clone());+ assert_eq!(new, root.join("bridge-data").join("profiles"));+ assert!(new.join("adom-you").join("Cookies").exists(), "legacy profile moved over");+ assert!(!legacy.exists(), "legacy dir gone after the move");+ assert_eq!(data_subdir("profiles", legacy.clone()), new, "second resolve is stable");+ std::env::remove_var("ADOM_BRIDGE_DATA_DIR");+ }+ #[test]+ fn retention_sweep_drops_old_then_oldest_over_cap_and_keeps_recent() {+ let d = tmp("ret");+ let old = d.join("old.png"); std::fs::write(&old, vec![0u8; 100]).unwrap();+ let t = std::time::SystemTime::now() - std::time::Duration::from_secs(20 * 86_400);+ std::fs::File::open(&old).unwrap().set_modified(t).unwrap();+ let mid = d.join("mid.png"); std::fs::write(&mid, vec![0u8; 600]).unwrap();+ std::fs::File::open(&mid).unwrap().set_modified(std::time::SystemTime::now() - std::time::Duration::from_secs(3 * 86_400)).unwrap();+ let fresh = d.join("fresh.png"); std::fs::write(&fresh, vec![0u8; 600]).unwrap();+ let (n, b) = retention_sweep(&d, 14, 500);+ assert!(!old.exists(), "older than 14 d removed"); assert!(!mid.exists(), "oldest over the cap removed"); assert!(fresh.exists(), "touched in the last 10 min kept");+ assert_eq!(n, 2); assert_eq!(b, 700);+ }+}