app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
master
John Lauer
v2.0.685 (insiders): The pup exe is signed as Adom Industries, Inc. (Authenticode, timestamped), so Windows 11 Smart Ap…
8e62f9e
1mo ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
#!/usr/bin/env bash
# rust-build.sh — build the Rust pup exe and its release zip (Phase 4 of docs/PLAN-rust-port.md).
#
# tools/rust-build.sh <version> # -> dist/<version>/adom-bridge-puppeteer-v<version>.zip
#
# The zip is ab's bridge package: adom-pup-bridge.exe + bridge.json (spawn.kind exe) + BRIDGE_VERSION
# at the zip root. Nothing else: every browser asset, extension, icon and font is embedded in the exe.
# Gates before the build: border check (Win32 only in host-win), embedded assets regenerated, host
# tests green. The exe version is include_str!(src/BRIDGE_VERSION), so write that first (ship.sh does).
set -euo pipefail
VER="${1:?usage: tools/rust-build.sh <version>}"
REPO="$(cd "$(dirname "$0")/.." && pwd)"; cd "$REPO"
ARCH="dist/${VER}"; ZIP="adom-bridge-puppeteer-v${VER}.zip"; EXE="rust/target/x86_64-pc-windows-gnu/release/adom-pup-bridge.exe"
[ "$(cat src/BRIDGE_VERSION)" = "$VER" ] || { echo "src/BRIDGE_VERSION is $(cat src/BRIDGE_VERSION), not $VER (ship.sh writes it first)"; exit 1; }
echo "== rust 0. gates =="
python3 tools/gen-assets.py
tools/check-borders.sh
( cd rust && sed -i "s/^version = \"[0-9.]*\"/version = \"${VER}\"/" Cargo.toml && grep -q "^version = \"${VER}\"" Cargo.toml )
TEST_OUT=$(cd rust && cargo test --workspace -q 2>&1) || { echo "$TEST_OUT" | grep -E "error|FAILED|panicked" | head -20; echo " TESTS FAILED"; exit 1; }
echo "$TEST_OUT" | grep -qE "test result: FAILED|panicked|error\[" && { echo "$TEST_OUT" | grep -E "error|FAILED|panicked" | head -20; echo " TESTS FAILED"; exit 1; }
echo " host tests green"
echo "== rust 1. release build (x86_64-pc-windows-gnu) =="
( cd rust && cargo build --release --target x86_64-pc-windows-gnu 2>&1 | grep -E "^(error|warning: unused)|Finished" )
[ -f "$EXE" ] || { echo " exe missing"; exit 1; }
grep -q "Puppeteer Bridge v" "$EXE" || { echo " exe lacks the boot banner string"; exit 1; }
echo "== rust 1b. sign as Adom Industries (never package an unsigned exe) =="
# Windows 11 Smart App Control blocks unsigned exes. The signing itself lives in a PRIVATE script outside this repo
# (John's build process); this public build only calls it and checks the result. No script, no package.
SIGNER="${ADOM_SIGN_EXE:-$HOME/.config/adom-signing/sign-exe.sh}"
[ -x "$SIGNER" ] || { echo " no private signing script at $SIGNER: refusing to package an unsigned exe"; exit 1; }
"$SIGNER" "$(pwd)/$EXE" pup || { echo " SIGNING FAILED: nothing packaged"; exit 1; }
python3 - "$EXE" <<'PYS' || { echo " the exe carries no Authenticode signature after signing: nothing packaged"; exit 1; }
import struct, sys
b = open(sys.argv[1], 'rb').read(); pe = struct.unpack_from('<I', b, 0x3c)[0]; magic = struct.unpack_from('<H', b, pe + 24)[0]
off, size = struct.unpack_from('<II', b, pe + 24 + (112 if magic == 0x20b else 96) + 32)
sys.exit(0 if size > 0 and off + size <= len(b) else 1)
PYS
grep -q "Puppeteer Bridge v" "$EXE" || { echo " the signed exe lacks the boot banner string"; exit 1; }
echo "== rust 2. package =="
mkdir -p "$ARCH"
python3 - "$VER" "$EXE" "$ARCH/$ZIP" <<'PY'
import json, sys, zipfile, hashlib, os
ver, exe, out = sys.argv[1:4]
d = json.load(open('src/bridge.json'))
assert d['version'] == ver, f"src/bridge.json is {d['version']}, not {ver}"
d['spawn'] = { 'kind': 'exe', 'entrypoint': 'adom-pup-bridge.exe', 'port': d['spawn'].get('port', 8851), 'healthEndpoint': '/status', 'stopMethod': 'kill', 'killImageName': 'adom-pup-bridge.exe', 'persistent': True,
'_hint': 'spawn.kind exe: one sha256-verified binary, no Node, no npm, no postinstall (docs/PLAN-rust-port.md). Port pinned low per ab#83 (WinNAT exclusion ranges in the ephemeral band).',
'_portNote': d['spawn'].get('_portNote', '') }
d.pop('runtime', None)
d['dependencies'] = { 'browser': d.get('dependencies', {}).get('browser', 'Chromium-family: installed Chrome or Edge, else Chrome for Testing is fetched as a last resort.') }
# The exe is Windows-only today (host-mac is stubs). Say so in the package so a Mac on the insiders
# tier is not handed a Windows binary; the Node build still serves macOS on the public tier.
d.setdefault('platforms', {})
d['platforms'] = {'windows': {'supported': True}, 'macos': {'supported': False, 'reason': 'The Rust exe is Windows-only for now; host-mac is stubbed for the Mac port. macOS runs the Node build (public tier).'}, 'linux': {'supported': False, 'reason': 'not yet'}}
d['shell'] = 'rust'
d['builtFor'] = 'x86_64-pc-windows-gnu'
with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z:
z.write(exe, 'adom-pup-bridge.exe')
z.writestr('bridge.json', json.dumps(d, indent=2) + '\n')
z.writestr('BRIDGE_VERSION', ver)
json.dump(d, open(os.path.join(os.path.dirname(out), 'bridge.json'), 'w'), indent=2)
print(f" {out} sha256={hashlib.sha256(open(out,'rb').read()).hexdigest()} size={os.path.getsize(out)} exe={os.path.getsize(exe)}")
PY