Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions deploy/phone/00-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# Step 00 — prerequisite check.
# Verifies you have adb, the phone is connected, Termux is installed,
# and root (KernelSU / Magisk) responds. Run this first on a fresh setup
# and any time something seems off.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$HERE/_common.sh"

hr
say "Step 00 — prerequisite check"
hr

# 1. adb installed
if ! command -v adb >/dev/null 2>&1; then
die "adb is not on PATH. Install with 'brew install --cask android-platform-tools' (macOS)."
fi
ok "adb installed: $(adb version | head -1)"

# 2. Phone connected
SERIAL=$(detect_serial)
ok "device: $SERIAL"

# 3. Android version
android_ver=$(adb -s "$SERIAL" shell getprop ro.build.version.release 2>/dev/null | tr -d '\r')
model=$(adb -s "$SERIAL" shell getprop ro.product.model 2>/dev/null | tr -d '\r')
ok "model: $model (Android $android_ver)"

# 4. Termux installed
if adb -s "$SERIAL" shell 'pm list packages com.termux' 2>/dev/null | grep -q com.termux; then
ok "Termux installed (com.termux)"
else
die "Termux is not installed. Install Termux + Termux:API from F-Droid first."
fi

# 5. Root access (KernelSU/Magisk needed for the file-staging step).
# Probe with `|| true` so set -e doesn't halt when su is missing.
set +e
root_probe=$(adb -s "$SERIAL" shell 'su root id 2>&1' 2>/dev/null)
set -e
if echo "$root_probe" | grep -q 'uid=0'; then
ok "root (su) available"
HAS_ROOT=1
else
warn "root not detected — staging files into Termux home will need an alternative path."
warn "Either install KernelSU/Magisk, OR run \`termux-setup-storage\` once inside Termux and we'll"
warn "fall back to using /sdcard/Download as a transfer staging area."
HAS_ROOT=0
fi

# 6. Termux UID — only meaningful if root works
if [ "$HAS_ROOT" = "1" ]; then
uid=$(adb -s "$SERIAL" shell "su root sh -c 'stat -c %u /data/data/com.termux'" 2>/dev/null | tr -d '\r' || true)
if [[ "$uid" =~ ^[0-9]+$ ]]; then
ok "Termux uid: $uid (export TERMUX_UID=$uid to pin if it changes)"
else
warn "Could not read Termux uid — falling back to default $TERMUX_UID_DEFAULT"
fi
else
warn "Skipping Termux uid probe (no root)"
fi

# 7. USB-CAN adapter (optional, world-readable so no root needed)
set +e
acm=$(adb -s "$SERIAL" shell 'ls /dev/ttyACM* 2>/dev/null' 2>/dev/null | tr -d '\r')
set -e
if [ -n "$acm" ]; then
ok "USB-CAN device(s) present: $acm"
else
warn "No /dev/ttyACM* — fine if you only want to replay sessions; plug in the CANable for live CAN."
fi

# 8. LocalLLM (optional)
set +e
llm_pkg=$(adb -s "$SERIAL" shell 'pm list packages com.localllm.app' 2>/dev/null)
set -e
if echo "$llm_pkg" | grep -q localllm; then
ok "LocalLLM app installed (start it manually + set 'Max Input Tokens' ≥ 1024)"
else
warn "LocalLLM not installed — coach brief will fall back to honest empty state with error message"
fi

hr
ok "Prerequisites look good. Next: ./deploy/phone/10-termux-packages.sh"
39 changes: 39 additions & 0 deletions deploy/phone/10-termux-packages.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Step 10 — install the Termux packages pitwall needs.
# Idempotent; safe to rerun. Takes ~2-5 min on first run.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$HERE/_common.sh"

hr
say "Step 10 — install Termux packages"
hr
SERIAL=$(detect_serial)

# Mirror selection: pick a US mirror automatically (Termux's default
# selector sometimes hangs). Skip if user already configured one.
termuxrun "$SERIAL" '
if [ ! -f $PREFIX/etc/apt/sources.list.d/_pitwall_mirror ]; then
echo "deb https://mirror.fcix.net/termux/termux-main stable main" > $PREFIX/etc/apt/sources.list.d/_pitwall_mirror
fi
echo "--- pkg update ---"
pkg update -y 2>&1 | tail -5
' >/dev/null 2>&1 || true

say "Installing core packages (python git clang make rust cmake ninja openssh termux-tools libduckdb)…"
termuxrun "$SERIAL" '
pkg install -y \
python git clang make pkg-config rust cmake ninja openssh termux-tools \
libduckdb 2>&1 | tail -10
'

say "Verifying:"
termuxrun "$SERIAL" '
python --version
pip --version
git --version
'

hr
ok "Termux packages installed. Next: ./deploy/phone/20-stage-repo.sh"
39 changes: 39 additions & 0 deletions deploy/phone/20-stage-repo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Step 20 — stage the pitwall repo onto the phone.
# Tars the parts of the repo the bridge needs (src/, data/), pushes via
# adb, and extracts into ~/pitwall on the phone. Idempotent.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$HERE/_common.sh"

hr
say "Step 20 — stage repo into Termux home"
hr
SERIAL=$(detect_serial)

# Build a tarball of just what the bridge needs. Skip caches + the big
# duckdb file (we'll stage that separately in step 40 if you want it).
TAR=/tmp/pitwall-src-$$.tgz
say "Building tarball at ${TAR}…"
tar -czf "$TAR" \
--exclude='__pycache__' --exclude='*.pyc' --exclude='.venv' \
--exclude='node_modules' --exclude='*.duckdb' --exclude='*.duckdb.*' \
-C "$REPO_ROOT" \
src/pitwall src/simulator data pyproject.toml
ok "tarball: $(ls -lh "$TAR" | awk '{print $5}')"

say "Pushing to /data/local/tmp/…"
adb -s "$SERIAL" push "$TAR" /data/local/tmp/pitwall-src.tgz | tail -1

say "Extracting into ~/pitwall…"
termuxrun "$SERIAL" '
mkdir -p ~/pitwall ~/pitwall/logs
tar -xzf /data/local/tmp/pitwall-src.tgz -C ~/pitwall 2>/dev/null
ls -la ~/pitwall | head -10
'
rm -f "$TAR"
adb -s "$SERIAL" shell 'rm -f /data/local/tmp/pitwall-src.tgz' >/dev/null 2>&1 || true

hr
ok "Repo staged at ~/pitwall on the phone. Next: ./deploy/phone/30-python-deps.sh"
52 changes: 52 additions & 0 deletions deploy/phone/30-python-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Step 30 — create the Python venv and install pitwall's runtime deps.
# - flask, flask-cors, waitress, numpy, pyyaml, python-can, cantools, pyserial
# - duckdb stub (no aarch64 wheel; backend falls back to SQLite from stdlib)
# Idempotent — re-running just reconciles the venv.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$HERE/_common.sh"

hr
say "Step 30 — Python venv + pip deps"
hr
SERIAL=$(detect_serial)

termuxrun "$SERIAL" '
cd ~/pitwall
if [ ! -d .venv ]; then
python -m venv .venv
fi
. .venv/bin/activate
pip install --upgrade pip 2>&1 | tail -2
echo "--- installing runtime deps ---"
pip install --prefer-binary \
flask flask-cors waitress numpy pyyaml python-can cantools pyserial 2>&1 | tail -5
echo
echo "--- verifying imports ---"
python -c "import flask, can, cantools, yaml, serial, numpy; print(\"ok — flask\",flask.__version__,\"can\",can.__version__,\"cantools\",cantools.__version__)"
'

say "Installing duckdb stub (Termux has no aarch64 wheel → bridge falls back to SQLite)…"
termuxrun "$SERIAL" '
cd ~/pitwall
. .venv/bin/activate
SP=$(python -c "import sys; print([p for p in sys.path if \"site-packages\" in p][0])")
mkdir -p "$SP/duckdb"
cat > "$SP/duckdb/__init__.py" <<EOF
"""Stub duckdb so importing succeeds — the real backend used at runtime
is SQLite (pitwall.db detects this via probe-and-fallback)."""
class Error(Exception): pass
class DuckDBPyConnection: pass
class _Unavailable(RuntimeError): pass
def connect(*a, **kw):
raise _Unavailable("duckdb not available on this platform")
__version__ = "0.0.0-stub"
EOF
python -c "import sqlite3, sys; sys.path.insert(0,\"src\"); from pitwall.db import db_backend; print(\"db backend:\", db_backend())"
'

hr
ok "Python deps installed. Next: ./deploy/phone/40-stage-recording.sh (optional, for replay)"
ok " or skip straight to: ./deploy/phone/60-forward-ports.sh + 70-start-bridge.sh"
61 changes: 61 additions & 0 deletions deploy/phone/40-stage-recording.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Step 40 — OPTIONAL — push a recorded session onto the phone so you can
# replay it through the bridge instead of needing live CAN.
# By default pushes the prepared 23-min Sonoma session at
# .context/recordings/track-sonoma-2026-05-23-1.sqlite if present.
# Pass --file <path> to push something else.
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$HERE/_common.sh"

REC="${1:-$REPO_ROOT/.context/recordings/track-sonoma-2026-05-23-1.sqlite}"
if [ "$1" = "--file" ] && [ -n "${2:-}" ]; then
REC="$2"
fi

hr
say "Step 40 — stage recording → phone bridge DB"
hr
SERIAL=$(detect_serial)

if [ ! -f "$REC" ]; then
warn "Recording not found at: $REC"
warn "Either record one with the bridge first, or pass a different path:"
warn " ./deploy/phone/40-stage-recording.sh /path/to/session.sqlite"
exit 1
fi

SIZE=$(ls -lh "$REC" | awk '{print $5}')
say "Source: $REC ($SIZE)"
say "Archiving any existing on-phone DB first…"
termuxrun "$SERIAL" '
mkdir -p ~/pitwall/data ~/pitwall/data/archive
if [ -f ~/pitwall/data/pitwall_sessions.duckdb ]; then
mv ~/pitwall/data/pitwall_sessions.duckdb \
~/pitwall/data/archive/pre-stage-$(date +%Y%m%dT%H%M%S).sqlite
rm -f ~/pitwall/data/pitwall_sessions.duckdb.wal \
~/pitwall/data/pitwall_sessions.duckdb.shm
fi
'

say "Pushing $SIZE …"
adb -s "$SERIAL" push "$REC" /data/local/tmp/pitwall-upload.sqlite | tail -1

termuxrun "$SERIAL" '
cp /data/local/tmp/pitwall-upload.sqlite ~/pitwall/data/pitwall_sessions.duckdb
python - <<EOF
import sqlite3
c = sqlite3.connect("/data/data/com.termux/files/home/pitwall/data/pitwall_sessions.duckdb")
sids = [r[0] for r in c.execute("SELECT session_id FROM sessions ORDER BY started_at DESC LIMIT 10")]
for tbl in ("sessions","telemetry","telemetry_signals","signal_registry"):
n = c.execute(f"SELECT COUNT(*) FROM {tbl}").fetchone()[0]
print(f" {tbl:22s} {n}")
print("session_ids:", sids[:5])
EOF
'

hr
ok "Recording staged. Replay it later with:"
ok " curl -X POST http://127.0.0.1:8765/session/replay/start \\"
ok " -d '{\"source_session_id\":\"<id>\", \"speed\": 1, \"loop\": false}'"
61 changes: 61 additions & 0 deletions deploy/phone/50-build-pwa.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Step 50 — build the PWA on the Mac and start a local static server.
# The server keeps running in the background; PWA is reached from the
# phone via 'adb reverse tcp:5173 tcp:5173' (set up in step 60).
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$HERE/_common.sh"

hr
say "Step 50 — build PWA + serve from Mac"
hr
PWA_DIR="$REPO_ROOT/src/pwa"

if ! command -v node >/dev/null 2>&1; then
die "node not on PATH. Install via 'brew install node' (macOS) or https://nodejs.org"
fi
ok "node: $(node -v)"

cd "$PWA_DIR"
if [ ! -d node_modules ]; then
say "First-time install (npm install)…"
npm install 2>&1 | tail -3
fi

say "Building dist…"
npm run build 2>&1 | tail -3

# Tear down any previous serve on the port
EXISTING=$(lsof -ti :"$PWA_PORT" 2>/dev/null || true)
if [ -n "$EXISTING" ]; then
warn "Port $PWA_PORT in use by pid $EXISTING — killing"
kill -9 $EXISTING 2>/dev/null || true
sleep 1
fi

say "Starting npx serve on :${PWA_PORT}…"
nohup npx serve -s dist -l "tcp://127.0.0.1:${PWA_PORT}" \
> /tmp/pwa-serve.log 2>&1 &
SERVE_PID=$!
ok "serve pid=$SERVE_PID log=/tmp/pwa-serve.log"

# npx-serve takes 1-5 s to bind; poll up to 10 s for the HTTP check.
HTTP="000"
for _ in $(seq 1 20); do
if ! kill -0 "$SERVE_PID" 2>/dev/null; then
die "serve died — check /tmp/pwa-serve.log"
fi
HTTP=$(curl -sS -o /dev/null -w '%{http_code}' "http://127.0.0.1:${PWA_PORT}/" 2>/dev/null || echo "000")
if [ "$HTTP" = "200" ]; then break; fi
sleep 0.5
done

if [ "$HTTP" = "200" ]; then
ok "http://127.0.0.1:${PWA_PORT}/ → 200"
else
warn "http://127.0.0.1:${PWA_PORT}/ → $HTTP after 10 s — check /tmp/pwa-serve.log"
fi

hr
ok "PWA built + served. Next: ./deploy/phone/60-forward-ports.sh"
22 changes: 22 additions & 0 deletions deploy/phone/60-forward-ports.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Step 60 — wire the adb port bridges.
# adb reverse :5173 → phone Chrome can reach Mac's PWA static server
# adb forward :8765 → Mac can reach phone's pitwall bridge
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "$HERE/_common.sh"

hr
say "Step 60 — adb port wiring"
hr
SERIAL=$(detect_serial)

adb -s "$SERIAL" reverse "tcp:${PWA_PORT}" "tcp:${PWA_PORT}"
ok "phone:${PWA_PORT} ← Mac:${PWA_PORT} (PWA reachable in phone Chrome)"

adb -s "$SERIAL" forward "tcp:${BRIDGE_PORT}" "tcp:${BRIDGE_PORT}"
ok "Mac:${BRIDGE_PORT} → phone:${BRIDGE_PORT} (bridge curl/PWA testing from Mac)"

hr
ok "Port bridges live. Next: ./deploy/phone/70-start-bridge.sh"
Loading
Loading