Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab9b8d96ca | ||
|
|
5c734c51b6 |
@@ -36,7 +36,7 @@ jobs:
|
|||||||
- { name: macOS ARM64, os: macos-latest, target: aarch64-apple-darwin, asset: agent-browser-darwin-arm64, use_zigbuild: false, ext: '' }
|
- { name: macOS ARM64, os: macos-latest, target: aarch64-apple-darwin, asset: agent-browser-darwin-arm64, use_zigbuild: false, ext: '' }
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ jobs:
|
|||||||
)
|
)
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.asset }}
|
name: ${{ matrix.asset }}
|
||||||
path: dist/${{ matrix.asset }}.tar.gz*
|
path: dist/${{ matrix.asset }}.tar.gz*
|
||||||
@@ -117,7 +117,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
path: dist
|
path: dist
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
run: ls -la dist
|
run: ls -la dist
|
||||||
|
|
||||||
- name: Attach to release
|
- name: Attach to release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v3
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
|
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
Generated
+1
-1
@@ -45,7 +45,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "agent-browser-stealth"
|
name = "agent-browser-stealth"
|
||||||
version = "0.27.0-fork.13"
|
version = "0.27.0-fork.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "agent-browser-stealth"
|
name = "agent-browser-stealth"
|
||||||
version = "0.27.0-fork.13"
|
version = "0.27.0-fork.14"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Fast browser automation CLI for AI agents"
|
description = "Fast browser automation CLI for AI agents"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|||||||
+49
-263
@@ -1,284 +1,70 @@
|
|||||||
use crate::color;
|
use crate::color;
|
||||||
use std::path::Path;
|
use std::process::{exit, Command};
|
||||||
use std::process::{exit, Command, Stdio};
|
|
||||||
|
|
||||||
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
const NPM_REGISTRY_URL: &str = "https://registry.npmjs.org/agent-browser/latest";
|
|
||||||
|
|
||||||
enum InstallMethod {
|
/// Canonical installer for the stealth fork. `upgrade` just re-runs it, so the
|
||||||
Npm,
|
/// upgrade path and the install path are identical (GitHub Release, no npm).
|
||||||
Pnpm,
|
const INSTALL_URL: &str =
|
||||||
Yarn,
|
"https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh";
|
||||||
Bun,
|
|
||||||
Homebrew,
|
|
||||||
Cargo,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fetch_latest_version() -> Result<String, String> {
|
|
||||||
let resp = reqwest::get(NPM_REGISTRY_URL)
|
|
||||||
.await
|
|
||||||
.map_err(|e| format!("Failed to fetch version info: {}", e))?;
|
|
||||||
|
|
||||||
let body: serde_json::Value = resp
|
|
||||||
.json()
|
|
||||||
.await
|
|
||||||
.map_err(|e| format!("Failed to parse version info: {}", e))?;
|
|
||||||
|
|
||||||
body.get("version")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.ok_or_else(|| "No version field in registry response".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse the `.install-method` marker written by postinstall.js.
|
|
||||||
fn read_install_method_marker(exe_dir: &Path) -> Option<InstallMethod> {
|
|
||||||
let contents = std::fs::read_to_string(exe_dir.join(".install-method")).ok()?;
|
|
||||||
match contents.trim() {
|
|
||||||
"npm" => Some(InstallMethod::Npm),
|
|
||||||
"pnpm" => Some(InstallMethod::Pnpm),
|
|
||||||
"yarn" => Some(InstallMethod::Yarn),
|
|
||||||
"bun" => Some(InstallMethod::Bun),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn detect_install_method() -> InstallMethod {
|
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
|
||||||
// Resolve symlinks to find the real binary location
|
|
||||||
let real_path = exe.canonicalize().unwrap_or(exe);
|
|
||||||
|
|
||||||
// Preferred: read the marker file written at install time
|
|
||||||
if let Some(dir) = real_path.parent() {
|
|
||||||
if let Some(method) = read_install_method_marker(dir) {
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: infer from executable path
|
|
||||||
let path_str = real_path.to_string_lossy();
|
|
||||||
|
|
||||||
if path_str.contains("/.cargo/bin/") || path_str.contains("\\.cargo\\bin\\") {
|
|
||||||
return InstallMethod::Cargo;
|
|
||||||
}
|
|
||||||
|
|
||||||
if path_str.contains("/Cellar/agent-browser/")
|
|
||||||
|| path_str.contains("/homebrew/")
|
|
||||||
|| path_str.contains("/linuxbrew/")
|
|
||||||
{
|
|
||||||
return InstallMethod::Homebrew;
|
|
||||||
}
|
|
||||||
|
|
||||||
if path_str.contains("/pnpm/") || path_str.contains("/pnpm-global/") {
|
|
||||||
return InstallMethod::Pnpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
if path_str.contains("/.yarn/") || path_str.contains("/yarn/global/") {
|
|
||||||
return InstallMethod::Yarn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if path_str.contains("/.bun/") {
|
|
||||||
return InstallMethod::Bun;
|
|
||||||
}
|
|
||||||
|
|
||||||
if path_str.contains("node_modules/agent-browser")
|
|
||||||
|| path_str.contains("node_modules\\agent-browser")
|
|
||||||
{
|
|
||||||
return InstallMethod::Npm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Last resort: probe package managers via subprocess
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
|
||||||
{
|
|
||||||
if command_succeeds("brew", &["list", "agent-browser"]) {
|
|
||||||
return InstallMethod::Homebrew;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if command_output_contains(
|
|
||||||
"pnpm",
|
|
||||||
&["list", "-g", "agent-browser", "--depth=0"],
|
|
||||||
"agent-browser",
|
|
||||||
) {
|
|
||||||
return InstallMethod::Pnpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
if command_output_contains("yarn", &["global", "list", "--depth=0"], "agent-browser") {
|
|
||||||
return InstallMethod::Yarn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if command_output_contains("bun", &["pm", "ls", "-g"], "agent-browser") {
|
|
||||||
return InstallMethod::Bun;
|
|
||||||
}
|
|
||||||
|
|
||||||
if command_succeeds("npm", &["list", "-g", "agent-browser", "--depth=0"]) {
|
|
||||||
return InstallMethod::Npm;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstallMethod::Unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_succeeds(cmd: &str, args: &[&str]) -> bool {
|
|
||||||
Command::new(cmd)
|
|
||||||
.args(args)
|
|
||||||
.stdout(Stdio::null())
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.status()
|
|
||||||
.map(|s| s.success())
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_output_contains(cmd: &str, args: &[&str], needle: &str) -> bool {
|
|
||||||
Command::new(cmd)
|
|
||||||
.args(args)
|
|
||||||
.stderr(Stdio::null())
|
|
||||||
.output()
|
|
||||||
.map(|o| o.status.success() && String::from_utf8_lossy(&o.stdout).contains(needle))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_upgrade_command(method: &InstallMethod) -> bool {
|
|
||||||
let (cmd, args, display): (&str, &[&str], &str) = match method {
|
|
||||||
InstallMethod::Npm => (
|
|
||||||
"npm",
|
|
||||||
&["install", "-g", "agent-browser@latest"],
|
|
||||||
"npm install -g agent-browser@latest",
|
|
||||||
),
|
|
||||||
InstallMethod::Pnpm => (
|
|
||||||
"pnpm",
|
|
||||||
&["add", "-g", "agent-browser@latest"],
|
|
||||||
"pnpm add -g agent-browser@latest",
|
|
||||||
),
|
|
||||||
// NOTE: `yarn global` is Yarn Classic (v1) only; Yarn Berry (v2+) removed it.
|
|
||||||
// Users on Yarn v2+ won't reach this path — detection falls through to Unknown.
|
|
||||||
InstallMethod::Yarn => (
|
|
||||||
"yarn",
|
|
||||||
&["global", "add", "agent-browser@latest"],
|
|
||||||
"yarn global add agent-browser@latest",
|
|
||||||
),
|
|
||||||
InstallMethod::Bun => (
|
|
||||||
"bun",
|
|
||||||
&["install", "-g", "agent-browser@latest"],
|
|
||||||
"bun install -g agent-browser@latest",
|
|
||||||
),
|
|
||||||
InstallMethod::Homebrew => (
|
|
||||||
"brew",
|
|
||||||
&["upgrade", "agent-browser"],
|
|
||||||
"brew upgrade agent-browser",
|
|
||||||
),
|
|
||||||
InstallMethod::Cargo => (
|
|
||||||
"cargo",
|
|
||||||
&["install", "agent-browser", "--force"],
|
|
||||||
"cargo install agent-browser --force",
|
|
||||||
),
|
|
||||||
InstallMethod::Unknown => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("Running: {}", display);
|
|
||||||
Command::new(cmd)
|
|
||||||
.args(args)
|
|
||||||
.status()
|
|
||||||
.map(|s| s.success())
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Upgrade to the latest GitHub Release.
|
||||||
|
///
|
||||||
|
/// The stealth fork ships as a prebuilt binary attached to a GitHub Release —
|
||||||
|
/// NOT via the npm registry. Earlier this command (inherited from upstream)
|
||||||
|
/// ran `npm/pnpm install -g agent-browser@latest`, which installed the
|
||||||
|
/// UNRELATED upstream `agent-browser` package and clobbered the user's setup.
|
||||||
|
/// Now `upgrade` simply re-runs install.sh into the same directory as the
|
||||||
|
/// current binary, so it always tracks the freshest GitHub Release.
|
||||||
pub fn run_upgrade() {
|
pub fn run_upgrade() {
|
||||||
let current = CURRENT_VERSION;
|
println!(
|
||||||
|
"{}",
|
||||||
|
color::cyan(&format!(
|
||||||
|
"Upgrading agent-browser-stealth (currently v{}) from the latest GitHub Release...",
|
||||||
|
CURRENT_VERSION
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
let rt = tokio::runtime::Builder::new_current_thread()
|
#[cfg(windows)]
|
||||||
.enable_all()
|
{
|
||||||
.build()
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
eprintln!(
|
|
||||||
"{} Failed to create runtime: {}",
|
|
||||||
color::error_indicator(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let latest = match rt.block_on(fetch_latest_version()) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!(
|
|
||||||
"{} Could not check latest version: {}",
|
|
||||||
color::warning_indicator(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !latest.is_empty() && current == latest.as_str() {
|
|
||||||
println!(
|
|
||||||
"{} agent-browser is already at the latest version (v{})",
|
|
||||||
color::success_indicator(),
|
|
||||||
current
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let method = detect_install_method();
|
|
||||||
|
|
||||||
let method_name = match &method {
|
|
||||||
InstallMethod::Npm => "npm",
|
|
||||||
InstallMethod::Pnpm => "pnpm",
|
|
||||||
InstallMethod::Yarn => "yarn",
|
|
||||||
InstallMethod::Bun => "bun",
|
|
||||||
InstallMethod::Homebrew => "Homebrew",
|
|
||||||
InstallMethod::Cargo => "Cargo",
|
|
||||||
InstallMethod::Unknown => "",
|
|
||||||
};
|
|
||||||
|
|
||||||
if matches!(method, InstallMethod::Unknown) {
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{} Could not detect installation method.",
|
"{} Automatic upgrade isn't supported on Windows.",
|
||||||
color::error_indicator()
|
color::warning_indicator()
|
||||||
);
|
);
|
||||||
eprintln!(" To update manually, run one of:");
|
eprintln!(" Download the latest agent-browser-win32-x64.tar.gz from:");
|
||||||
eprintln!(" npm install -g agent-browser@latest # npm");
|
eprintln!(" https://github.com/leeguooooo/agent-browser-stealth/releases/latest");
|
||||||
eprintln!(" pnpm add -g agent-browser@latest # pnpm");
|
eprintln!(" and replace agent-browser.exe on your PATH.");
|
||||||
eprintln!(" yarn global add agent-browser@latest # yarn");
|
|
||||||
eprintln!(" bun install -g agent-browser@latest # bun");
|
|
||||||
eprintln!(" brew upgrade agent-browser # Homebrew");
|
|
||||||
eprintln!(" cargo install agent-browser --force # Cargo");
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Detected installation via {}.", method_name);
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
// Install into the SAME directory as the running binary (in-place
|
||||||
|
// upgrade), so we don't create a second copy elsewhere on PATH.
|
||||||
|
let bin_dir = std::env::current_exe()
|
||||||
|
.ok()
|
||||||
|
.and_then(|p| p.canonicalize().ok())
|
||||||
|
.and_then(|p| p.parent().map(|d| d.to_path_buf()));
|
||||||
|
|
||||||
if !latest.is_empty() {
|
let install_cmd = format!("curl -fsSL {} | sh", INSTALL_URL);
|
||||||
println!(
|
println!("Running: {}", install_cmd);
|
||||||
"{}",
|
|
||||||
color::cyan(&format!(
|
|
||||||
"Upgrading agent-browser... v{} → v{}",
|
|
||||||
current, latest
|
|
||||||
))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"{}",
|
|
||||||
color::cyan(&format!("Upgrading agent-browser (v{})...", current))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let success = run_upgrade_command(&method);
|
let mut cmd = Command::new("sh");
|
||||||
|
cmd.arg("-c").arg(&install_cmd);
|
||||||
|
if let Some(ref dir) = bin_dir {
|
||||||
|
cmd.env("AGENT_BROWSER_BIN_DIR", dir);
|
||||||
|
}
|
||||||
|
|
||||||
if success {
|
let ok = cmd.status().map(|s| s.success()).unwrap_or(false);
|
||||||
if !latest.is_empty() {
|
if ok {
|
||||||
println!(
|
println!(
|
||||||
"{} Done! v{} → v{}",
|
"{} Upgrade complete — run `agent-browser-stealth --version` to confirm.",
|
||||||
color::success_indicator(),
|
color::success_indicator()
|
||||||
current,
|
|
||||||
latest
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
println!("{} Done!", color::success_indicator());
|
eprintln!("{} Upgrade failed. Install manually:", color::error_indicator());
|
||||||
|
eprintln!(" curl -fsSL {} | sh", INSTALL_URL);
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
eprintln!("{} Upgrade failed.", color::error_indicator());
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "agent-browser-stealth",
|
"name": "agent-browser-stealth",
|
||||||
"version": "0.27.0-fork.13",
|
"version": "0.27.0-fork.14",
|
||||||
"description": "Browser automation CLI for AI agents — stealth fork with anti-detection",
|
"description": "Browser automation CLI for AI agents — stealth fork with anti-detection",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@11.1.3",
|
"packageManager": "pnpm@11.1.3",
|
||||||
|
|||||||
Reference in New Issue
Block a user