Add CLI flags for cookie URL, domain, path, httpOnly, secure, and expires (#266)

* Add CLI flags for cookie URL, domain, path, httpOnly, secure, and expires

Extends the `cookies set` command to support setting cookies with additional parameters before loading a page, solving authentication workflows where cookies need to be set for different domains.

**Key changes:**
- Added CLI flags: `--url`, `--domain`, `--path`, `--httpOnly`, `--secure`, `--sameSite`, `--expires`
- Added comprehensive test coverage for all new flags and combinations
- Updated help documentation with usage examples
- No daemon changes needed - it already supported these parameters

**Example usage:**
```bash
agent-browser cookies set session_id "abc123" --url https://app.example.com --httpOnly --secure
```

This allows setting cookies for a URL before opening the page, eliminating the need for workarounds in cross-domain authentication scenarios.

Fixes #261

* Update lock

* Fix compilation error
This commit is contained in:
Chris Tate
2026-01-25 11:53:49 -06:00
committed by GitHub
parent 53187a603c
commit ae09fdd431
3 changed files with 226 additions and 10 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ version = 4
[[package]]
name = "agent-browser"
version = "0.6.0"
version = "0.7.1"
dependencies = [
"dirs",
"libc",
+191 -3
View File
@@ -485,14 +485,106 @@ pub fn parse_command(args: &[String], flags: &Flags) -> Result<Value, ParseError
"set" => {
let name = rest.get(1).ok_or_else(|| ParseError::MissingArguments {
context: "cookies set".to_string(),
usage: "cookies set <name> <value>",
usage: "cookies set <name> <value> [--url <url>] [--domain <domain>] [--path <path>] [--httpOnly] [--secure] [--sameSite <Strict|Lax|None>] [--expires <timestamp>]",
})?;
let value = rest.get(2).ok_or_else(|| ParseError::MissingArguments {
context: "cookies set".to_string(),
usage: "cookies set <name> <value>",
usage: "cookies set <name> <value> [--url <url>] [--domain <domain>] [--path <path>] [--httpOnly] [--secure] [--sameSite <Strict|Lax|None>] [--expires <timestamp>]",
})?;
let mut cookie = json!({ "name": name, "value": value });
// Parse optional flags
let mut i = 3;
while i < rest.len() {
match rest[i] {
"--url" => {
if let Some(url) = rest.get(i + 1) {
cookie["url"] = json!(url);
i += 2;
} else {
return Err(ParseError::MissingArguments {
context: "cookies set --url".to_string(),
usage: "--url <url>",
});
}
}
"--domain" => {
if let Some(domain) = rest.get(i + 1) {
cookie["domain"] = json!(domain);
i += 2;
} else {
return Err(ParseError::MissingArguments {
context: "cookies set --domain".to_string(),
usage: "--domain <domain>",
});
}
}
"--path" => {
if let Some(path) = rest.get(i + 1) {
cookie["path"] = json!(path);
i += 2;
} else {
return Err(ParseError::MissingArguments {
context: "cookies set --path".to_string(),
usage: "--path <path>",
});
}
}
"--httpOnly" => {
cookie["httpOnly"] = json!(true);
i += 1;
}
"--secure" => {
cookie["secure"] = json!(true);
i += 1;
}
"--sameSite" => {
if let Some(same_site) = rest.get(i + 1) {
// Validate sameSite value
if *same_site == "Strict" || *same_site == "Lax" || *same_site == "None" {
cookie["sameSite"] = json!(same_site);
i += 2;
} else {
return Err(ParseError::MissingArguments {
context: "cookies set --sameSite".to_string(),
usage: "--sameSite <Strict|Lax|None>",
});
}
} else {
return Err(ParseError::MissingArguments {
context: "cookies set --sameSite".to_string(),
usage: "--sameSite <Strict|Lax|None>",
});
}
}
"--expires" => {
if let Some(expires_str) = rest.get(i + 1) {
if let Ok(expires) = expires_str.parse::<i64>() {
cookie["expires"] = json!(expires);
i += 2;
} else {
return Err(ParseError::MissingArguments {
context: "cookies set --expires".to_string(),
usage: "--expires <timestamp>",
});
}
} else {
return Err(ParseError::MissingArguments {
context: "cookies set --expires".to_string(),
usage: "--expires <timestamp>",
});
}
}
_ => {
// Unknown flag, skip it (or could error)
i += 1;
}
}
}
Ok(
json!({ "id": id, "action": "cookies_set", "cookies": [{ "name": name, "value": value }] }),
json!({ "id": id, "action": "cookies_set", "cookies": [cookie] }),
)
}
"clear" => Ok(json!({ "id": id, "action": "cookies_clear" })),
@@ -1254,6 +1346,102 @@ mod tests {
assert_eq!(cmd["action"], "cookies_clear");
}
#[test]
fn test_cookies_set_with_url() {
let cmd = parse_command(&args("cookies set mycookie myvalue --url https://example.com"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["url"], "https://example.com");
}
#[test]
fn test_cookies_set_with_domain() {
let cmd = parse_command(&args("cookies set mycookie myvalue --domain example.com"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["domain"], "example.com");
}
#[test]
fn test_cookies_set_with_path() {
let cmd = parse_command(&args("cookies set mycookie myvalue --path /api"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["path"], "/api");
}
#[test]
fn test_cookies_set_with_httponly() {
let cmd = parse_command(&args("cookies set mycookie myvalue --httpOnly"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["httpOnly"], true);
}
#[test]
fn test_cookies_set_with_secure() {
let cmd = parse_command(&args("cookies set mycookie myvalue --secure"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["secure"], true);
}
#[test]
fn test_cookies_set_with_samesite() {
let cmd = parse_command(&args("cookies set mycookie myvalue --sameSite Strict"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["sameSite"], "Strict");
}
#[test]
fn test_cookies_set_with_expires() {
let cmd = parse_command(&args("cookies set mycookie myvalue --expires 1234567890"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["expires"], 1234567890);
}
#[test]
fn test_cookies_set_with_multiple_flags() {
let cmd = parse_command(&args("cookies set mycookie myvalue --url https://example.com --httpOnly --secure --sameSite Lax"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["url"], "https://example.com");
assert_eq!(cmd["cookies"][0]["httpOnly"], true);
assert_eq!(cmd["cookies"][0]["secure"], true);
assert_eq!(cmd["cookies"][0]["sameSite"], "Lax");
}
#[test]
fn test_cookies_set_with_all_flags() {
let cmd = parse_command(&args("cookies set mycookie myvalue --url https://example.com --domain example.com --path /api --httpOnly --secure --sameSite None --expires 9999999999"), &default_flags()).unwrap();
assert_eq!(cmd["action"], "cookies_set");
assert_eq!(cmd["cookies"][0]["name"], "mycookie");
assert_eq!(cmd["cookies"][0]["value"], "myvalue");
assert_eq!(cmd["cookies"][0]["url"], "https://example.com");
assert_eq!(cmd["cookies"][0]["domain"], "example.com");
assert_eq!(cmd["cookies"][0]["path"], "/api");
assert_eq!(cmd["cookies"][0]["httpOnly"], true);
assert_eq!(cmd["cookies"][0]["secure"], true);
assert_eq!(cmd["cookies"][0]["sameSite"], "None");
assert_eq!(cmd["cookies"][0]["expires"], 9999999999i64);
}
#[test]
fn test_cookies_set_invalid_samesite() {
let result = parse_command(&args("cookies set mycookie myvalue --sameSite Invalid"), &default_flags());
assert!(result.is_err());
}
// === Storage Tests ===
#[test]
+34 -6
View File
@@ -1082,18 +1082,46 @@ Usage: agent-browser cookies [operation] [args]
Manage browser cookies for the current context.
Operations:
get Get all cookies (default)
set <name> <value> Set a cookie
clear Clear all cookies
get Get all cookies (default)
set <name> <value> [options] Set a cookie with optional properties
clear Clear all cookies
Cookie Set Options:
--url <url> URL for the cookie (allows setting before page load)
--domain <domain> Cookie domain (e.g., ".example.com")
--path <path> Cookie path (e.g., "/api")
--httpOnly Set HttpOnly flag (prevents JavaScript access)
--secure Set Secure flag (HTTPS only)
--sameSite <Strict|Lax|None> SameSite policy
--expires <timestamp> Expiration time (Unix timestamp in seconds)
Note: If --url, --domain, and --path are all omitted, the cookie will be set
for the current page URL.
Global Options:
--json Output as JSON
--session <name> Use specific session
Examples:
agent-browser cookies
agent-browser cookies get
# Simple cookie for current page
agent-browser cookies set session_id "abc123"
# Set cookie for a URL before loading it (useful for authentication)
agent-browser cookies set session_id "abc123" --url https://app.example.com
# Set secure, httpOnly cookie with domain and path
agent-browser cookies set auth_token "xyz789" --domain example.com --path /api --httpOnly --secure
# Set cookie with SameSite policy
agent-browser cookies set tracking_consent "yes" --sameSite Strict
# Set cookie with expiration (Unix timestamp)
agent-browser cookies set temp_token "temp123" --expires 1735689600
# Get all cookies
agent-browser cookies
# Clear all cookies
agent-browser cookies clear
"##
}
@@ -1494,7 +1522,7 @@ Network: agent-browser network <action>
requests [--clear] [--filter <pattern>]
Storage:
cookies [get|set|clear] Manage cookies
cookies [get|set|clear] Manage cookies (set supports --url, --domain, --path, --httpOnly, --secure, --sameSite, --expires)
storage <local|session> Manage web storage
Tabs: