Files
nixpkgs/pkgs/by-name/ag/agent-browser/package.nix
2026-09-25 14:54:36 +02:00

141 lines
3.6 KiB
Nix

{
lib,
stdenv,
fetchFromGitHub,
fetchPnpmDeps,
rustPlatform,
nodejs,
pnpm_11,
pnpmConfigHook,
geist-font,
nix-update-script,
which,
writableTmpDirAsHomeHook,
}:
let
pnpm = pnpm_11;
version = "0.38.1";
src = fetchFromGitHub {
owner = "vercel-labs";
repo = "agent-browser";
tag = "v${version}";
hash = "sha256-C+XplCHOdFDQGPUnrCDuq7U4LkAX0QB3fC4uVA8o11w=";
};
# The Rust CLI embeds the dashboard UI via RustEmbed at compile time.
# Build the Next.js static export so it can be placed at the expected path.
dashboard = stdenv.mkDerivation {
pname = "agent-browser-dashboard";
inherit version src;
nativeBuildInputs = [
nodejs
pnpm
pnpmConfigHook
];
__darwinAllowLocalNetworking = true;
pnpmDeps = fetchPnpmDeps {
pname = "agent-browser-dashboard";
inherit version src pnpm;
pnpmWorkspaces = [ "dashboard" ];
fetcherVersion = 4;
hash = "sha256-AEWwJtzmAUspGwFrMqoCvUfefPg2aTvMilBfJPSF9jA=";
};
pnpmWorkspaces = [ "dashboard" ];
# Replace Google Fonts fetch with a local font from nixpkgs since the
# Nix sandbox has no network access.
postPatch = ''
substituteInPlace packages/dashboard/src/app/layout.tsx --replace-fail \
'{ Geist } from "next/font/google"' \
'localFont from "next/font/local"'
substituteInPlace packages/dashboard/src/app/layout.tsx --replace-fail \
'Geist({ subsets: ["latin"], variable: "--font-sans" })' \
'localFont({ src: "./Geist-Regular.otf", variable: "--font-sans" })'
cp "${geist-font}/share/fonts/opentype/Geist-Regular.otf" \
packages/dashboard/src/app/Geist-Regular.otf
'';
buildPhase = ''
runHook preBuild
pnpm --filter dashboard build
runHook postBuild
'';
installPhase = ''
runHook preInstall
cp -r packages/dashboard/out $out
runHook postInstall
'';
};
in
rustPlatform.buildRustPackage (finalAttrs: {
pname = "agent-browser";
inherit version src;
sourceRoot = "${finalAttrs.src.name}/cli";
cargoHash = "sha256-Ei26Iz0qMqayucULLCSwN+fjw0VJ3S2L2A4vGhfYGqI=";
# Place the pre-built dashboard where RustEmbed expects it
postUnpack = ''
chmod u+w source/packages/dashboard
cp -r ${dashboard} source/packages/dashboard/out
'';
# `which_exists` spawns the external `which` binary at runtime to probe
# for optional tools; pin it to an absolute store path.
postPatch = ''
substituteInPlace src/doctor/helpers.rs src/install.rs \
src/native/cdp/chrome.rs src/native/cdp/lightpanda.rs --replace-fail \
'"which"' '"${lib.getExe which}"'
'';
nativeCheckInputs = [
writableTmpDirAsHomeHook
];
# Some tests use a shared state that makes them flaky when run concurrently.
dontUseCargoParallelTests = true;
__darwinAllowLocalNetworking = true;
# The `skills` subcommand looks for `skills/` and `skill-data/` next to
# `bin/`, relative to the canonical exe path. See cli/src/skills.rs.
postInstall = ''
cp -r ../skills $out/skills
cp -r ../skill-data $out/skill-data
'';
passthru = {
inherit dashboard;
updateScript = nix-update-script {
extraArgs = [
"--subpackage"
"dashboard"
];
};
};
meta = {
description = "Headless browser automation CLI for AI agents";
homepage = "https://github.com/vercel-labs/agent-browser";
license = lib.licenses.asl20;
sourceProvenance = with lib.sourceTypes; [ fromSource ];
platforms = lib.platforms.linux ++ lib.platforms.darwin;
maintainers = with lib.maintainers; [
baptiste0928
codgician
];
mainProgram = "agent-browser";
};
})