feat: add session persistence, state management commands, and --new-tab click (#184)
Rebased and fixed implementation of PR #184 features on current main: Session persistence: - --session-name flag and AGENT_BROWSER_SESSION_NAME env var auto-save/restore cookies and localStorage across browser restarts - State files stored in ~/.agent-browser/sessions/ with owner-only permissions - AES-256-GCM encryption via AGENT_BROWSER_ENCRYPTION_KEY env var - Auto-expiration of old state files (AGENT_BROWSER_STATE_EXPIRE_DAYS, default 30) State management commands: - state list: list saved state files with metadata - state show <file>: display state summary (cookies, origins, domains) - state rename <old> <new>: rename state files - state clear [name] [--all]: clear saved states - state clean --older-than <days>: delete expired states New --new-tab flag for click command: - Opens link href in a new tab instead of navigating the current tab Security hardening: - Session name validation prevents path traversal (CLI + daemon) - safeHeaderMerge prevents prototype pollution in header merging - WebSocket stream server binds to 127.0.0.1 only - State files written with 0o600 permissions Fixes applied over the original PR: - Use color.rs module instead of hardcoded ANSI escape codes - Align CLI output field names with daemon response format - Add CLI-level --session-name validation (not just daemon-side) - Avoid adding "DOM" to tsconfig.json lib (use proper typing in evaluate) - Keep version at 0.9.3 (matches current main) - Centralize session name validation in daemon.ts helper - Update all documentation (README, SKILL.md, docs site, --help output) Co-authored-by: Chris Tate <chris@ctate.dev>
This commit is contained in:
co-authored by
Chris Tate
parent
cdd10ebb54
commit
697b788af0
@@ -4,6 +4,7 @@ mod connection;
|
||||
mod flags;
|
||||
mod install;
|
||||
mod output;
|
||||
mod validation;
|
||||
|
||||
use serde_json::json;
|
||||
use std::env;
|
||||
@@ -179,6 +180,7 @@ fn main() {
|
||||
ParseError::UnknownSubcommand { .. } => "unknown_subcommand",
|
||||
ParseError::MissingArguments { .. } => "missing_arguments",
|
||||
ParseError::InvalidValue { .. } => "invalid_value",
|
||||
ParseError::InvalidSessionName { .. } => "invalid_session_name",
|
||||
};
|
||||
println!(
|
||||
r#"{{"success":false,"error":"{}","type":"{}"}}"#,
|
||||
@@ -192,6 +194,22 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
// Validate session name before starting daemon
|
||||
if let Some(ref name) = flags.session_name {
|
||||
if !validation::is_valid_session_name(name) {
|
||||
let msg = validation::session_name_error(name);
|
||||
if flags.json {
|
||||
println!(
|
||||
r#"{{"success":false,"error":"{}","type":"invalid_session_name"}}"#,
|
||||
msg.replace('"', "\\\"")
|
||||
);
|
||||
} else {
|
||||
eprintln!("{} {}", color::error_indicator(), msg);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
let daemon_result = match ensure_daemon(
|
||||
&flags.session,
|
||||
flags.headed,
|
||||
@@ -207,6 +225,7 @@ fn main() {
|
||||
flags.state.as_deref(),
|
||||
flags.provider.as_deref(),
|
||||
flags.device.as_deref(),
|
||||
flags.session_name.as_deref(),
|
||||
) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
|
||||
Reference in New Issue
Block a user