Files
DroidDeck/tools/agent/setup
T
Kurt Himebauch db933e4232 feat(agent): an Agent tab, a session drawer page, and a bubble over the game (#2376)
* feat(agent): an Agent tab in the launcher that talks to Claude Code outside any session

* feat(agent): the agent in the session drawer and as a bubble over the game

* fix(agent): connect loopback adb, show unread replies, and fit the drawer page to the screen

* feat(agent): first-run setup in the Agent tab, a harness choice with Codex, and no permission prompts

* fix(agent): install helpers off the main thread and offer sign-in only after the install

* fix(agent): connect adb only for commands that use it and run setup checks one at a time

* fix(agent): show why a setup check failed

* fix(agent): each harness has its own setup state and conversation

* fix(agent): start the adb server before connecting and retry the connection check

* fix(agent): switching harness clears the previous harness's setup results

* feat(agent): a header, a composer with send and stop, and Markdown replies in the agent chat

* fix(agent): leave room under the chat composer

* feat(agent): continue the conversation after a Lab build restarts the app

* feat(agent): show and remove what the agent stores in the Linux userland

* fix(agent): the build tools take about 5 GB

* feat(agent): DroidDeck opens on the Agent tab after restarting on a Lab build

* feat(agent): conversation history, model and plan mode, live replies, full tool input and output, processes, attachments, queued messages and a Stop that ends the turn

* fix(agent): check the Lab build less often while nothing is happening

* fix(agent): an old Lab build's progress is not shown as a failure, and a restart counts as success

* feat(agent): rebuild a kept Lab build from Updates, and tidy the chat's tool rows, costs, processes and times

* feat(agent): a setup step that prepares fast Lab builds for the installed version, kept alive while it runs

* fix(agent): the notification names a prepare and shows its percent without repeating the title

* fix(agent): run the agent's Lab builds in the app's own process and continue the conversation that asked after the restart

* fix(agent): setup's prepare runs the build itself instead of handing it to the app

* feat(agent): a turn that ends without a reply says so and offers Retry, the resume is retried once, and each turn's raw output is kept

* fix(agent): keep the previous turn's raw output too

* fix(agent): a resumed Claude Code turn no longer ends at the result for a background task the last run left behind

* fix(agent): install Codex's code-mode host with it, without which Codex runs no commands

* feat(agent): setup and storage collapse into a summary card like Setup's, and Codex tool rows show the command without its shell wrapper

* feat(agent): drop the agent bubble over sessions for now; the drawer keeps the agent

* fix(agent): the Agent tab shows once the Linux runtime is downloaded, even while its proot pack is still being fetched

* fix(agent): before the install brings adb, the connection step checks the app's own pairing instead of failing with adb not found

* fix(agent): proot's binding warnings are not shown as a setup step's reason

* fix(agent): the session drawer's Agent page fills the height under the drawer's header instead of a guess from the screen height

* fix(agent): write the browser and GitHub helpers before every setup step, not only the install

* fix(agent): find Wireless debugging's current port when the saved one no longer answers, so sign-ins open the browser after it restarts

* fix(agent): only take a Wireless debugging port that answers, since mDNS can still advertise an old one

* fix(agent): find a loopback Wireless debugging port by scanning when mDNS advertises none

* fix(agent): keep a Codex turn going through reconnect notices and read gh login's status reliably

* fix(agent): time out hung setup checks and keep their results and actions with their harness

* fix(agent): keep the draft across settings and show the Agent tab once the runtime installs

* fix(agent): keep the backslash when quoting apostrophes in a URL the browser helper opens

* fix(agent): refresh apt's package lists before installing, since a fresh Linux runtime ships without them

* fix(agent): sign-in links open through the app itself, so they work without Wireless debugging

* fix(agent): the connection step passes only when adb really reaches the device, not on adb's own error

* fix(agent): say Wireless debugging isn't reachable instead of adb's error

* fix(agent): keep prompts, tool ids, attachments and retries with their own turn and chat

* fix(agent): the service stops only once foreground, and Rebuild waits for a ready agent

* fix(agent): the Codex install says what it is downloading, so a long download doesn't look stuck

* fix(agent): pass the prompt to both CLIs the way they were tested on devices

* fix(agent): the browser helper uses the running app's files directory, falls back to adb when the app cannot open a link, and the link is not logged
2026-11-11 14:10:02 -05:00

78 lines
4.2 KiB
Bash
Executable File

#!/bin/bash
# Sets up the on-device agent inside DroidDeck's Linux userland: Claude Code, git and gh, loopback
# adb for droiddeckctl, and the toolchain dd-lab-build uses to build Lab builds on the device.
# Run it from the DroidDeck terminal, inside a checkout of the DroidDeck repository.
set -euo pipefail
here=$(cd "$(dirname "$0")" && pwd)
export DEBIAN_FRONTEND=noninteractive
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
apt-get update
apt-get install -y --no-install-recommends git gh adb python3 zip unzip curl openjdk-17-jdk-headless qemu-user zipalign apksigner
command -v claude >/dev/null || [ -x "$HOME/.local/bin/claude" ] ||
{ curl -fsSL -o "$tmp/install-claude.sh" https://claude.ai/install.sh && bash "$tmp/install-claude.sh"; }
mkdir -p "$HOME/.local/bin"
for tool in dd-agent dd-lab-build dd-gh-login android-open; do install -m755 "$here/$tool" "$HOME/.local/bin/$tool"; done
grep -q 'droiddeck-agent-path' "$HOME/.bashrc" 2>/dev/null ||
printf '\n# droiddeck-agent-path\nexport PATH="$HOME/.local/bin:$PATH" ADB_SERIAL=127.0.0.1:5555 BROWSER="$HOME/.local/bin/android-open"\n' >> "$HOME/.bashrc"
# Google publishes aapt2 for x86-64 Linux only; it runs here under qemu-user with Ubuntu's x86-64 runtime.
x86=$HOME/x86root
mkdir -p "$HOME/aapt2/x86" "$x86/etc"
if [ ! -x "$HOME/aapt2/x86/aapt2" ]; then
agp=$(sed -n "s/.*com.android.tools.build:gradle:\([0-9.]*\).*/\1/p" "$here/../../build.gradle" | head -1)
[ -n "$agp" ] || { echo "setup: no Android Gradle plugin version in build.gradle" >&2; exit 1; }
aapt2=$(curl -fsSL "https://dl.google.com/android/maven2/com/android/tools/build/aapt2/maven-metadata.xml" |
{ grep -o "<version>$agp-[0-9]*</version>" || true; } | tail -1 | sed 's/<[^>]*>//g')
[ -n "$aapt2" ] || { echo "setup: Google publishes no aapt2 for AGP $agp" >&2; exit 1; }
curl -fsSL -o "$tmp/aapt2.jar" "https://dl.google.com/android/maven2/com/android/tools/build/aapt2/$aapt2/aapt2-$aapt2-linux.jar"
unzip -o -q "$tmp/aapt2.jar" aapt2 -d "$HOME/aapt2/x86"
chmod +x "$HOME/aapt2/x86/aapt2"
fi
if [ ! -e "$x86/lib64/ld-linux-x86-64.so.2" ]; then
codename=$(. /etc/os-release && echo "$VERSION_CODENAME")
curl -fsSL "https://archive.ubuntu.com/ubuntu/dists/$codename/main/binary-amd64/Packages.gz" | gunzip > "$tmp/amd64-packages"
for package in libc6 libgcc-s1 libstdc++6 zlib1g; do
read -r file sum < <(awk -v p="$package" '$0 == "Package: " p {found = 1} found && /^Filename:/ {f = $2}
found && /^SHA256:/ {s = $2} found && /^$/ {exit} END {print f, s}' "$tmp/amd64-packages")
[ -n "$sum" ] || { echo "setup: $package is not in Ubuntu $codename's amd64 index" >&2; exit 1; }
curl -fsSL -o "$tmp/$package.deb" "https://archive.ubuntu.com/ubuntu/$file"
echo "$sum $tmp/$package.deb" | sha256sum -c --quiet -
dpkg-deb -x "$tmp/$package.deb" "$x86"
done
ln -sfn usr/lib64 "$x86/lib64"
ln -sfn usr/lib "$x86/lib"
: > "$x86/etc/ld.so.preload"
fi
# AGP only accepts a custom aapt2 whose file is named aapt2.
printf '#!/bin/sh\nexec qemu-x86_64 -L %s %s/aapt2/x86/aapt2 "$@"\n' "$x86" "$HOME" > "$HOME/aapt2/aapt2"
chmod +x "$HOME/aapt2/aapt2"
"$HOME/aapt2/aapt2" version
sdk=$HOME/android-sdk
if [ ! -x "$sdk/cmdline-tools/latest/bin/sdkmanager" ]; then
mkdir -p "$sdk/cmdline-tools"
curl -fsSL -o "$tmp/cmdline-tools.zip" https://dl.google.com/android/repository/commandlinetools-linux-13114758_latest.zip
unzip -q -o "$tmp/cmdline-tools.zip" -d "$sdk/cmdline-tools"
rm -rf "$sdk/cmdline-tools/latest"
mv "$sdk/cmdline-tools/cmdline-tools" "$sdk/cmdline-tools/latest"
fi
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-arm64
yes | "$sdk/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null
"$sdk/cmdline-tools/latest/bin/sdkmanager" "platforms;android-34" "build-tools;35.0.0" >/dev/null
adb connect 127.0.0.1:5555 || true
# The first build compiles the whole app; doing it now makes the agent's Lab builds incremental.
if [ "${DD_SKIP_PREPARE:-}" != 1 ]; then
echo "Approve the USB debugging prompt if one appears."
if timeout 120 adb -s 127.0.0.1:5555 wait-for-device; then
"$here/dd-lab-build" --prepare || echo "The first Lab build will compile the whole app"
else
echo "adb did not connect, so the first Lab build will compile the whole app"
fi
fi
echo "Done. Run dd-agent."