feat(dist): ship via GitHub Release binaries + install.sh (drop npm as primary)

Distribute the prebuilt binary through GitHub Releases instead of the npm
registry — zero auth for the publisher (CI's GITHUB_TOKEN) and zero auth for
consumers (no npm token / 2FA / OTP, no GitHub Packages .npmrc).

- install.sh: detects OS/arch (incl. linux musl), downloads the matching
  agent-browser-<platform>.tar.gz from the GitHub Release, verifies .sha256,
  installs `agent-browser` + `abs` to /usr/local/bin or ~/.local/bin.
  Override via AGENT_BROWSER_VERSION / AGENT_BROWSER_BIN_DIR.
- .github/workflows/release-binaries.yml: on tag push (v*), build all 7
  platform variants (reusing the zigbuild cross-compile matrix), package each
  as .tar.gz + .sha256, attach to the tag's GitHub Release. No npm, no token.
- remove .github/workflows/release.yml: it published to npm (--provenance) and
  built the (removed) dashboard, so it broke on every main push.
- README install now leads with `curl … install.sh | sh`; npm demoted to a
  legacy alternative.
- skill stub self-heals: if `agent-browser` is missing, run install.sh (don't
  fall back to other browser tools).
This commit is contained in:
leeguooooo
2026-06-01 16:46:11 +09:00
parent 27dff19105
commit 6032deabd5
5 changed files with 256 additions and 334 deletions
+137
View File
@@ -0,0 +1,137 @@
name: Release binaries
# Build per-platform binaries and attach them to the GitHub Release for the
# pushed tag. No npm, no tokens — only the built-in GITHUB_TOKEN. Consumers
# install with: curl -fsSL .../install.sh | sh
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Existing tag to (re)build binaries for, e.g. v0.27.0-fork.12'
required: true
permissions:
contents: write
concurrency: release-binaries-${{ github.ref }}
jobs:
build:
name: Build ${{ matrix.name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- { name: Linux x64, os: ubuntu-latest, target: x86_64-unknown-linux-gnu, asset: agent-browser-linux-x64, use_zigbuild: true, ext: '' }
- { name: Linux ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-gnu, asset: agent-browser-linux-arm64, use_zigbuild: true, ext: '' }
- { name: Linux musl x64, os: ubuntu-latest, target: x86_64-unknown-linux-musl, asset: agent-browser-linux-musl-x64, use_zigbuild: true, ext: '' }
- { name: Linux musl ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-musl, asset: agent-browser-linux-musl-arm64, use_zigbuild: true, ext: '' }
- { name: Windows x64, os: ubuntu-latest, target: x86_64-pc-windows-gnu, asset: agent-browser-win32-x64, use_zigbuild: false, ext: '.exe' }
- { name: macOS x64, os: macos-latest, target: x86_64-apple-darwin, asset: agent-browser-darwin-x64, use_zigbuild: false, ext: '' }
- { name: macOS ARM64, os: macos-latest, target: aarch64-apple-darwin, asset: agent-browser-darwin-arm64, use_zigbuild: false, ext: '' }
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag || github.ref }}
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu gcc-x86-64-linux-gnu mingw-w64
- name: Install cargo-zigbuild
if: matrix.use_zigbuild
run: |
pip3 install ziglang
cargo install cargo-zigbuild
- name: Configure Rust linkers
if: runner.os == 'Linux'
run: |
mkdir -p ~/.cargo
cat >> ~/.cargo/config.toml << 'EOF'
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
EOF
- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
workspaces: cli
- name: Build (zigbuild)
if: matrix.use_zigbuild
run: cargo zigbuild --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }}
- name: Build (cargo)
if: '!matrix.use_zigbuild'
run: cargo build --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }}
- name: Package (.tar.gz + .sha256)
shell: bash
run: |
set -euo pipefail
mkdir -p dist
src="cli/target/${{ matrix.target }}/release/agent-browser${{ matrix.ext }}"
# The binary inside every archive is named `agent-browser` (or .exe);
# install.sh extracts that fixed name regardless of platform.
cp "$src" "dist/agent-browser${{ matrix.ext }}"
chmod +x "dist/agent-browser${{ matrix.ext }}" || true
( cd dist
tar czf "${{ matrix.asset }}.tar.gz" "agent-browser${{ matrix.ext }}"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "${{ matrix.asset }}.tar.gz" > "${{ matrix.asset }}.tar.gz.sha256"
else
shasum -a 256 "${{ matrix.asset }}.tar.gz" > "${{ matrix.asset }}.tar.gz.sha256"
fi
)
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.asset }}
path: dist/${{ matrix.asset }}.tar.gz*
retention-days: 3
release:
name: Attach binaries to GitHub Release
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: List assets
run: ls -la dist
- name: Attach to release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
files: |
dist/*.tar.gz
dist/*.tar.gz.sha256
fail_on_unmatched_files: true
# keep existing release notes if the release was created beforehand
append_body: false
-332
View File
@@ -1,332 +0,0 @@
name: Release
on:
push:
branches:
- main
workflow_dispatch:
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: read
jobs:
check-release:
name: Check for new version
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
outputs:
should_release: ${{ steps.check.outputs.should_release }}
needs_github_release: ${{ steps.check.outputs.needs_github_release }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Compare package.json version to npm and check GitHub release
id: check
run: |
LOCAL_VERSION=$(node -p "require('./package.json').version")
echo "Local version: $LOCAL_VERSION"
NPM_VERSION=$(npm view agent-browser version 2>/dev/null || echo "0.0.0")
echo "npm version: $NPM_VERSION"
if [ "$LOCAL_VERSION" != "$NPM_VERSION" ]; then
echo "Version changed: $NPM_VERSION -> $LOCAL_VERSION"
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "needs_github_release=true" >> "$GITHUB_OUTPUT"
else
echo "Version unchanged on npm, skipping build and publish"
echo "should_release=false" >> "$GITHUB_OUTPUT"
# Check if GitHub release exists; it may be missing if a prior run
# published to npm but failed before creating the release.
TAG="v$LOCAL_VERSION"
if gh release view "$TAG" &>/dev/null; then
echo "GitHub release $TAG exists"
echo "needs_github_release=false" >> "$GITHUB_OUTPUT"
else
echo "GitHub release $TAG is missing, will rebuild and create it"
echo "needs_github_release=true" >> "$GITHUB_OUTPUT"
fi
fi
echo "version=$LOCAL_VERSION" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-binaries:
name: Build ${{ matrix.name }}
needs: check-release
if: needs.check-release.outputs.should_release == 'true' || needs.check-release.outputs.needs_github_release == 'true'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: Linux x64
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary: agent-browser-linux-x64
use_zigbuild: true
- name: Linux ARM64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
binary: agent-browser-linux-arm64
use_zigbuild: true
- name: Linux musl x64
os: ubuntu-latest
target: x86_64-unknown-linux-musl
binary: agent-browser-linux-musl-x64
use_zigbuild: true
- name: Linux musl ARM64
os: ubuntu-latest
target: aarch64-unknown-linux-musl
binary: agent-browser-linux-musl-arm64
use_zigbuild: true
- name: Windows x64
os: ubuntu-latest
target: x86_64-pc-windows-gnu
binary: agent-browser-win32-x64.exe
use_zigbuild: false
- name: macOS x64
os: macos-latest
target: x86_64-apple-darwin
binary: agent-browser-darwin-x64
use_zigbuild: false
- name: macOS ARM64
os: macos-latest
target: aarch64-apple-darwin
binary: agent-browser-darwin-arm64
use_zigbuild: false
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .node-version
cache: pnpm
- name: Install npm dependencies
run: pnpm install --frozen-lockfile
- name: Sync version
run: pnpm run version:sync
- name: Build dashboard
run: pnpm --filter dashboard build
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu gcc-x86-64-linux-gnu mingw-w64
- name: Install cargo-zigbuild
if: matrix.use_zigbuild
run: |
pip3 install ziglang
cargo install cargo-zigbuild
- name: Configure Rust linkers
if: runner.os == 'Linux'
run: |
mkdir -p ~/.cargo
cat >> ~/.cargo/config.toml << 'EOF'
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
EOF
- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
workspaces: cli
- name: Build with zigbuild
if: matrix.use_zigbuild
run: cargo zigbuild --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }}
- name: Build with cargo
if: '!matrix.use_zigbuild'
run: cargo build --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }}
- name: Copy binary
run: |
mkdir -p artifacts
if [[ "${{ matrix.target }}" == *"windows"* ]]; then
cp cli/target/${{ matrix.target }}/release/agent-browser.exe artifacts/${{ matrix.binary }}
else
cp cli/target/${{ matrix.target }}/release/agent-browser artifacts/${{ matrix.binary }}
chmod +x artifacts/${{ matrix.binary }}
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.binary }}
path: artifacts/${{ matrix.binary }}
retention-days: 7
publish:
name: Publish to npm
needs: [check-release, build-binaries]
if: needs.check-release.outputs.should_release == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
environment: Release
permissions:
contents: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .node-version
cache: pnpm
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download all binary artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
- name: Move binaries to bin directory
run: |
mkdir -p bin
find artifacts -type f -name 'agent-browser-*' -exec mv {} bin/ \;
rm -rf artifacts
chmod +x bin/agent-browser-* 2>/dev/null || true
echo "Binaries in bin/:"
ls -la bin/
- name: Verify all binaries exist
run: |
EXPECTED_BINARIES=(
"agent-browser-linux-x64"
"agent-browser-linux-arm64"
"agent-browser-linux-musl-x64"
"agent-browser-linux-musl-arm64"
"agent-browser-win32-x64.exe"
"agent-browser-darwin-x64"
"agent-browser-darwin-arm64"
)
MIN_SIZE=100000
ERRORS=0
for binary in "${EXPECTED_BINARIES[@]}"; do
if [ ! -f "bin/$binary" ]; then
echo "ERROR: Missing bin/$binary"
ERRORS=$((ERRORS + 1))
else
SIZE=$(stat -c%s "bin/$binary" 2>/dev/null || stat -f%z "bin/$binary")
if [ "$SIZE" -lt "$MIN_SIZE" ]; then
echo "ERROR: bin/$binary is too small ($SIZE bytes, expected >= $MIN_SIZE)"
ERRORS=$((ERRORS + 1))
else
echo "OK: bin/$binary ($SIZE bytes)"
fi
fi
done
if [ "$ERRORS" -gt 0 ]; then
echo "Error: $ERRORS binary issues found"
exit 1
fi
echo "All 7 platform binaries present and valid"
- name: Publish to npm
run: npm publish --provenance
github-release:
name: Create GitHub Release
needs: [check-release, build-binaries, publish]
if: always() && needs.build-binaries.result == 'success' && needs.check-release.outputs.needs_github_release == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts/
- name: Move binaries to bin directory
run: |
mkdir -p bin
find artifacts -type f -name 'agent-browser-*' -exec mv {} bin/ \;
rm -rf artifacts
chmod +x bin/agent-browser-* 2>/dev/null || true
ls -la bin/
- name: Verify binaries exist
run: |
BINARY_COUNT=$(ls bin/agent-browser-* 2>/dev/null | wc -l)
if [ "$BINARY_COUNT" -lt 7 ]; then
echo "Error: Expected 7 binaries, found $BINARY_COUNT"
ls -la bin/
exit 1
fi
echo "Found $BINARY_COUNT binaries"
- name: Extract changelog entry
run: |
VERSION="${{ needs.check-release.outputs.version }}"
awk '/<!-- release:start -->/{found=1; next} /<!-- release:end -->/{found=0} found{print}' CHANGELOG.md > /tmp/release-notes.md
LINES=$(wc -l < /tmp/release-notes.md | tr -d ' ')
if [ "$LINES" -lt 2 ]; then
echo "Error: No release notes found between <!-- release:start --> and <!-- release:end --> markers in CHANGELOG.md"
exit 1
fi
echo "Extracted release notes for $VERSION ($LINES lines)"
- name: Create GitHub Release
run: |
VERSION="${{ needs.check-release.outputs.version }}"
TAG="v$VERSION"
if gh release view "$TAG" &>/dev/null; then
echo "Release $TAG already exists, uploading assets..."
gh release upload "$TAG" bin/agent-browser-* --clobber
else
echo "Creating release $TAG..."
gh release create "$TAG" \
--title "$TAG" \
--notes-file /tmp/release-notes.md \
bin/agent-browser-*
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+12 -1
View File
@@ -21,9 +21,20 @@ For basic usage, commands, and API reference, see the [upstream documentation](h
## Install
```bash
npm install -g agent-browser-stealth
curl -fsSL https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh | sh
```
Downloads the prebuilt binary for your platform from the latest [GitHub Release](https://github.com/leeguooooo/agent-browser-stealth/releases) and installs `agent-browser` (+ the `abs` alias). No npm, no tokens.
<details>
<summary>Other ways to install</summary>
- **Pin a version:** `AGENT_BROWSER_VERSION=v0.27.0-fork.11 curl -fsSL https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh | sh`
- **Custom location:** `AGENT_BROWSER_BIN_DIR=$HOME/bin curl -fsSL … | sh`
- **Windows:** download `agent-browser-win32-x64.tar.gz` from the [Releases page](https://github.com/leeguooooo/agent-browser-stealth/releases) and put `agent-browser.exe` on your PATH.
- **npm (legacy):** `npm install -g agent-browser-stealth` — still published, but GitHub Releases is the primary channel now.
</details>
### Install the AI agent skills
The repo ships SKILL.md files for Claude Code, Cursor, etc. Pull them into the current project with [skills.sh](https://skills.sh):
Executable
+100
View File
@@ -0,0 +1,100 @@
#!/bin/sh
# agent-browser-stealth installer — downloads the prebuilt binary from the
# GitHub Release (no npm, no auth for you or your users).
#
# curl -fsSL https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh | sh
#
# Env overrides:
# AGENT_BROWSER_VERSION=v0.27.0-fork.11 pin a specific release tag
# AGENT_BROWSER_BIN_DIR=/usr/local/bin install location (auto-detected otherwise)
set -eu
REPO="leeguooooo/agent-browser-stealth"
BIN_NAME="agent-browser"
err() { printf '\033[31merror:\033[0m %s\n' "$1" >&2; exit 1; }
info() { printf '\033[36m==>\033[0m %s\n' "$1" >&2; }
command -v curl >/dev/null 2>&1 || err "curl is required"
command -v tar >/dev/null 2>&1 || err "tar is required"
# --- detect platform -> release asset name -------------------------------
os=$(uname -s)
arch=$(uname -m)
case "$os" in
Darwin) plat="darwin" ;;
Linux) plat="linux" ;;
*) err "unsupported OS: $os (use the Windows .exe asset from the Releases page)" ;;
esac
case "$arch" in
x86_64|amd64) cpu="x64" ;;
arm64|aarch64) cpu="arm64" ;;
*) err "unsupported architecture: $arch" ;;
esac
# musl (Alpine etc.) gets the statically-linked Linux build
libc=""
if [ "$plat" = "linux" ] && ! ldd /bin/sh 2>/dev/null | grep -qi 'gnu\|glibc'; then
if [ -e /lib/ld-musl-x86_64.so.1 ] || [ -e /lib/ld-musl-aarch64.so.1 ]; then
libc="-musl"
fi
fi
asset="agent-browser-${plat}${libc}-${cpu}"
# --- resolve release tag --------------------------------------------------
tag="${AGENT_BROWSER_VERSION:-}"
if [ -z "$tag" ]; then
info "resolving latest release..."
tag=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" \
| grep '"tag_name"' | head -1 | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')
[ -n "$tag" ] || err "could not resolve latest release tag (set AGENT_BROWSER_VERSION=vX.Y.Z)"
fi
base="https://github.com/${REPO}/releases/download/${tag}"
tgz_url="${base}/${asset}.tar.gz"
sha_url="${tgz_url}.sha256"
# --- download + verify ----------------------------------------------------
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
info "downloading ${asset} (${tag})..."
curl -fsSL "$tgz_url" -o "$tmp/pkg.tar.gz" \
|| err "download failed: $tgz_url (is asset '${asset}.tar.gz' attached to release ${tag}?)"
if curl -fsSL "$sha_url" -o "$tmp/pkg.sha256" 2>/dev/null; then
info "verifying checksum..."
expected=$(awk '{print $1}' "$tmp/pkg.sha256")
if command -v shasum >/dev/null 2>&1; then
actual=$(shasum -a 256 "$tmp/pkg.tar.gz" | awk '{print $1}')
elif command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "$tmp/pkg.tar.gz" | awk '{print $1}')
else
actual=""; info "no sha256 tool found, skipping verification"
fi
[ -z "$actual" ] || [ "$expected" = "$actual" ] || err "checksum mismatch (expected $expected, got $actual)"
else
info "no .sha256 published, skipping verification"
fi
tar -xzf "$tmp/pkg.tar.gz" -C "$tmp"
[ -f "$tmp/${BIN_NAME}" ] || err "archive did not contain ${BIN_NAME}"
chmod +x "$tmp/${BIN_NAME}"
# --- choose install dir ---------------------------------------------------
bindir="${AGENT_BROWSER_BIN_DIR:-}"
if [ -z "$bindir" ]; then
if [ -w /usr/local/bin ] 2>/dev/null; then bindir="/usr/local/bin"; else bindir="$HOME/.local/bin"; fi
fi
mkdir -p "$bindir"
mv "$tmp/${BIN_NAME}" "$bindir/${BIN_NAME}"
# convenience aliases: `abs` (short) -> agent-browser
ln -sf "$bindir/${BIN_NAME}" "$bindir/abs" 2>/dev/null || true
info "installed ${BIN_NAME} -> ${bindir}/${BIN_NAME}"
"$bindir/${BIN_NAME}" --version 2>/dev/null || true
case ":$PATH:" in
*":$bindir:"*) : ;;
*) printf '\033[33mnote:\033[0m %s is not on your PATH. Add:\n export PATH="%s:$PATH"\n' "$bindir" "$bindir" >&2 ;;
esac
+7 -1
View File
@@ -10,7 +10,13 @@ hidden: true
Fast browser automation CLI for AI agents. Chrome/Chromium via CDP with
accessibility-tree snapshots and compact `@eN` element refs.
Install: `npm i -g agent-browser && agent-browser install`
**Install / self-heal:** if the `agent-browser` (or `abs`) command is missing —
e.g. a command errors with "command not found" — install it from the GitHub
Release (no npm, no token), then retry. Do NOT fall back to other browser tools:
```bash
curl -fsSL https://raw.githubusercontent.com/leeguooooo/agent-browser-stealth/main/install.sh | sh
```
## Start here