chore: consolidate all shell scripts into hh/scripts/
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run

Move the 14 hack-house scripts (bootstrap, lets-hack, host-room, smoke,
demo/film harnesses, connect/join, ensure-docker/vbox) into hh/scripts/
and fix their path resolution for the deeper location. Update cross-refs:
sbx.rs script consts, app.rs hints, CI, direnv .envrc, README, and docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-06-06 17:49:32 -07:00
parent 9f7ab92270
commit 1fa8c332ed
21 changed files with 57 additions and 57 deletions
+1 -1
View File
@@ -31,5 +31,5 @@ if ! command -v tmux >/dev/null 2>&1; then
elif tmux has-session -t "$SESSION" 2>/dev/null; then
echo "'$SESSION' already live — tmux attach -t $SESSION (reveal its password in-app with /pw)"
else
PW="${PW:-$(gen_pw)}" "$HH_DIR/lets-hack.sh" "$HH_USER"
PW="${PW:-$(gen_pw)}" "$HH_DIR/scripts/lets-hack.sh" "$HH_USER"
fi
+134
View File
@@ -0,0 +1,134 @@
#!/usr/bin/env bash
# bootstrap-ai.sh — optional AI layer for hack-house (Ollama, local-first)
#
# Runs the baseline ./bootstrap.sh first, then sets up everything the /ai agent
# bridge needs to run a LOCAL model: installs Ollama (if missing) and pulls a
# default model. Cloud providers (anthropic / openai) need no install — just an
# API key in the agent's env — so this script only handles the local default.
#
# The baseline is untouched: ./bootstrap.sh alone never installs or enables AI.
#
# usage:
# ./bootstrap-ai.sh # baseline setup + Ollama + default model
# ./bootstrap-ai.sh --release # ...and build the client in release mode
# ./bootstrap-ai.sh --check # report only; install/pull nothing
# ./bootstrap-ai.sh --yes # don't prompt before installing Ollama / pulling the model
# ./bootstrap-ai.sh --ai-only # only the AI layer; skip the baseline ./bootstrap.sh
# ./bootstrap-ai.sh -h | --help # this help
#
# environment:
# HH_AI_MODEL model to pull (default: qwen2.5:3b)
# OLLAMA_HOST daemon URL (default: http://localhost:11434)
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
MODEL="${HH_AI_MODEL:-qwen2.5:3b}"
OLLAMA_HOST="${OLLAMA_HOST:-http://localhost:11434}"
INSTALLER_URL="https://ollama.com/install.sh"
RELEASE_ARGS=()
CHECK_ONLY=0
ASSUME_YES=0
AI_ONLY=0
for arg in "$@"; do
case "$arg" in
--release) RELEASE_ARGS+=(--release) ;;
--check) CHECK_ONLY=1 ;;
--yes|-y) ASSUME_YES=1 ;;
--ai-only) AI_ONLY=1 ;;
-h|--help|-help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) echo "✖ unknown arg: $arg (try --release / --check / --yes / --ai-only / --help)" >&2; exit 2 ;;
esac
done
have() { command -v "$1" >/dev/null 2>&1; }
ollama_up() { curl -s --max-time 3 "$OLLAMA_HOST/api/tags" >/dev/null 2>&1; }
# 0. Baseline setup (venv + server/agent deps + client build). The agent's own
# runtime deps (requests, websockets) are already in requirements.txt, so the
# baseline install covers them — this script adds only the model runtime.
# --no-ai stops bootstrap.sh re-entering this script (it now runs the AI layer
# by default). --ai-only skips the baseline entirely (caller already ran it).
if [[ $AI_ONLY -ne 1 ]]; then
if [[ $CHECK_ONLY -eq 1 ]]; then
"$ROOT/hh/scripts/bootstrap.sh" --no-ai --check || exit $?
else
"$ROOT/hh/scripts/bootstrap.sh" --no-ai "${RELEASE_ARGS[@]}" || exit $?
fi
fi
echo
echo "── AI layer (Ollama, local) ──"
# 1. Report current state.
if have ollama; then echo " ✓ ollama ($(ollama --version 2>&1 | head -1))"
else echo " · ollama not installed"; fi
if ollama_up; then echo " ✓ ollama daemon reachable at $OLLAMA_HOST"
else echo " · ollama daemon not reachable at $OLLAMA_HOST"; fi
if [[ $CHECK_ONLY -eq 1 ]]; then
echo "--check: no changes made"
exit 0
fi
# 2. Install Ollama if missing. The official installer pipes a remote script into
# a shell, so we print the exact command and — on an interactive terminal —
# ask first (skip with --yes). Already installed → nothing to do.
if ! have ollama; then
if [[ "$(uname -s)" != "Linux" ]]; then
echo " ✖ automatic install is Linux-only." >&2
echo " install Ollama for your OS from https://ollama.com/download, then re-run." >&2
exit 1
fi
echo " Ollama is not installed. The official installer will run:"
echo " curl -fsSL $INSTALLER_URL | sh"
if [[ $ASSUME_YES -ne 1 && -t 0 ]]; then
read -r -p " proceed? [y/N] " ans
[[ "$ans" == [yY]* ]] || { echo " aborted — install Ollama yourself, then re-run." >&2; exit 1; }
fi
curl -fsSL "$INSTALLER_URL" | sh || { echo " ✖ Ollama install failed" >&2; exit 1; }
have ollama || { echo " ✖ ollama still not on PATH after install" >&2; exit 1; }
echo " ✓ ollama installed"
fi
# 3. Make sure the daemon is up. The Linux installer usually registers a systemd
# service; if it isn't running we start one in the background so we can pull.
if ! ollama_up; then
echo " starting ollama daemon…"
if have systemctl && systemctl start ollama 2>/dev/null; then :
else nohup ollama serve >/tmp/hh-ollama.log 2>&1 & fi
for _ in $(seq 1 20); do ollama_up && break; sleep 1; done
ollama_up || { echo " ✖ could not reach ollama at $OLLAMA_HOST (see /tmp/hh-ollama.log)" >&2; exit 1; }
echo " ✓ daemon up"
fi
# 4. Pull the default model (idempotent). Pulling downloads several GB onto THIS
# machine, so ask first on an interactive terminal (skip with --yes) — never
# pull a model onto someone's box without their say-so.
if ollama list 2>/dev/null | awk 'NR>1{print $1}' | grep -Fxq "$MODEL"; then
echo " ✓ model '$MODEL' already present"
else
echo " model '$MODEL' is not present — pulling it downloads several GB to THIS machine."
if [[ $ASSUME_YES -ne 1 ]]; then
if [[ -t 0 ]]; then
read -r -p " pull '$MODEL' now? [y/N] " ans
[[ "$ans" == [yY]* ]] || { echo " aborted — no model pulled. pull it yourself with: ollama pull $MODEL" >&2; exit 1; }
else
# No terminal to confirm on — never pull onto someone's machine
# unasked. Require explicit --yes (or an interactive yes) instead.
echo " ✖ not pulling '$MODEL' without confirmation (no terminal to ask)." >&2
echo " re-run with --yes to allow it, or pull manually: ollama pull $MODEL" >&2
exit 1
fi
fi
echo " pulling model '$MODEL' (first pull can take a while)…"
ollama pull "$MODEL" || { echo " ✖ failed to pull '$MODEL'" >&2; exit 1; }
echo " ✓ model '$MODEL' ready"
fi
echo
echo "AI ready. start a local agent against a running room with:"
echo " .venv/bin/python -m cmd_chat.agent <host> <port> \\"
echo " --password <room-pw> --provider ollama --model $MODEL --no-tls"
echo
echo "tip: pick a different model with HH_AI_MODEL=llama3 ./bootstrap-ai.sh"
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# bootstrap.sh — one-shot setup for hack-house
#
# Gets a fresh clone ready to run: checks prerequisites, creates a Python venv
# for the server, installs its dependencies, and builds the Rust client.
#
# usage:
# ./bootstrap.sh # full setup (venv + deps + client + AI layer)
# ./bootstrap.sh --release # build the client in release mode
# ./bootstrap.sh --no-ai # skip the AI layer (no Ollama install / model pull)
# ./bootstrap.sh --yes # don't prompt during the AI layer (install / pull)
# ./bootstrap.sh --check # only report what's installed; change nothing
# ./bootstrap.sh -h | --help # this help
#
# The AI layer (local Ollama + a default model for the /ai agent) is set up by
# default so the agent works out of the box and nobody hits "model not found".
# It still prompts before installing/pulling on an interactive terminal — skip
# the whole thing with --no-ai, or skip the prompts with --yes.
#
# After it finishes, spin up a local test session with: cd hh && ./scripts/lets-hack.sh
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
VENV="$ROOT/.venv"
HH_DIR="$ROOT/hh"
RELEASE=0
CHECK_ONLY=0
DO_AI=1
ASSUME_YES=0
for arg in "$@"; do
case "$arg" in
--release) RELEASE=1 ;;
--check) CHECK_ONLY=1 ;;
--no-ai) DO_AI=0 ;;
--yes|-y) ASSUME_YES=1 ;;
-h|--help|-help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
*) echo "✖ unknown arg: $arg (try --release / --no-ai / --yes / --check / --help)" >&2; exit 2 ;;
esac
done
have() { command -v "$1" >/dev/null 2>&1; }
# 1. Prerequisites. python3 + cargo are required; tmux/docker/multipass are
# optional (only needed for the test harness / certain sandbox backends).
echo "checking prerequisites…"
missing=0
for bin in python3 cargo; do
if have "$bin"; then echo "$bin ($($bin --version 2>&1 | head -1))"
else echo "$bin — REQUIRED"; missing=1; fi
done
for bin in tmux docker multipass direnv; do
if have "$bin"; then echo "$bin (optional)"
else echo " · $bin not found (optional)"; fi
done
if [[ $missing -eq 1 ]]; then
echo "✖ install the required tools above, then re-run ./bootstrap.sh" >&2
exit 1
fi
if [[ $CHECK_ONLY -eq 1 ]]; then
echo "--check: prerequisites OK, no changes made"
exit 0
fi
# 2. Python venv + server dependencies.
if [[ ! -d "$VENV" ]]; then
echo "creating venv at $VENV"
python3 -m venv "$VENV" || { echo "✖ could not create venv" >&2; exit 1; }
fi
echo "installing server dependencies…"
"$VENV/bin/pip" install --quiet --upgrade pip || true
"$VENV/bin/pip" install --quiet -r "$ROOT/requirements.txt" \
|| { echo "✖ pip install failed — see output above" >&2; exit 1; }
echo " ✓ server deps installed into .venv"
# 3. Build the Rust client.
echo "building the client…"
if [[ $RELEASE -eq 1 ]]; then
( cd "$HH_DIR" && cargo build --release ) || { echo "✖ cargo build --release failed" >&2; exit 1; }
echo " ✓ client built: hh/target/release/hack-house"
else
( cd "$HH_DIR" && cargo build ) || { echo "✖ cargo build failed" >&2; exit 1; }
echo " ✓ client built: hh/target/debug/hack-house"
fi
# 4. AI layer (on by default): install Ollama + pull the default model so the
# /ai agent works out of the box and nobody hits "model not found" later. The
# logic lives in bootstrap-ai.sh; --ai-only skips its baseline re-run (we just
# did it). Declining/failing here is non-fatal — the baseline setup still
# stands and the AI layer can be added later with ./bootstrap-ai.sh.
if [[ $DO_AI -eq 1 ]]; then
ai_args=(--ai-only)
[[ $ASSUME_YES -eq 1 ]] && ai_args+=(--yes)
"$ROOT/hh/scripts/bootstrap-ai.sh" "${ai_args[@]}" \
|| echo "⚠ AI layer not completed — re-run ./bootstrap-ai.sh when ready" >&2
fi
echo
echo "ready. next steps:"
echo " cd hh && ./scripts/lets-hack.sh # local test session (server + clients in tmux)"
echo " # or run the server + client by hand — see README.MD"
if [[ $DO_AI -eq 0 ]]; then
echo " # AI layer skipped (--no-ai); add it later with hh/scripts/bootstrap-ai.sh"
fi
+1 -1
View File
@@ -19,7 +19,7 @@
# -P port (default: 4173, or $HH_PORT)
# --tls use wss/https instead of the default plaintext-over-Tailscale
set -euo pipefail
cd "$(dirname "$0")"
cd "$(dirname "$0")/.."
DEFAULT_PORT=4173
DEFAULT_HOST=127.0.0.1
@@ -10,12 +10,12 @@
# Headless: drives the ratatui client over tmux send-keys, asserts via
# capture-pane + `docker exec`. PoC/correctness first; feeds video-toolkit later.
#
# Usage: hh/demo-save-load.sh [--keep]
# Usage: hh/scripts/demo-save-load.sh [--keep]
# --keep leave the server, container, image and tmux sessions up afterwards
set -uo pipefail
# ---- config -----------------------------------------------------------------
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Pick a free TCP port so we never collide with a stale server from another
# session (a leftover server on a fixed port answers SRP with its own password
# → spurious 401s). Honour an explicit $PORT if the caller forces one.
@@ -12,13 +12,13 @@
# session; `asciinema rec` runs in its own detached session that `tmux attach`es
# to the inner one, so it mirrors exactly what we drive with send-keys.
#
# Usage: hh/film-save-load.sh [--keep] [--no-render]
# Usage: hh/scripts/film-save-load.sh [--keep] [--no-render]
# --keep leave server/sessions/container/image up afterwards
# --no-render stop after writing the .cast (skip the mp4 render)
set -uo pipefail
# ---- config -----------------------------------------------------------------
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
pick_port() { local p; for p in $(seq 4200 4280); do ss -ltn 2>/dev/null | grep -q ":$p " || { echo "$p"; return; }; done; echo 4173; }
PORT="${PORT:-$(pick_port)}"
PW="${PW:-malware-bless}"
@@ -11,13 +11,13 @@
# stitches title→terminal→gui→result; edge-tts narration + an SRT are muxed/burned
# on top.
#
# Usage: hh/film-virtualbox.sh [--keep] [--no-render] [--no-vm]
# Usage: hh/scripts/film-virtualbox.sh [--keep] [--no-render] [--no-vm]
# --keep leave server/sessions up; don't power off the VM
# --no-render stop after the captures (skip compose)
# --no-vm skip booting the VM (reuse an already-running one for GUI grab)
set -uo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
VTK="$HOME/coding/video-toolkit"
FORGE_PY="$HOME/anaconda3/bin/python3"
pick_port() { local p; for p in $(seq 4240 4290); do ss -ltn 2>/dev/null | grep -q ":$p " || { echo "$p"; return; }; done; echo 4173; }
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env bash
# host-room.sh — host a hack-house room (the server) for others to join.
#
# Thin, friendly wrapper around `cmd_chat.py serve`: picks the project venv,
# mints a room password if you don't supply one, prints the exact join command
# (preferring your Tailscale IP), and runs the server in the foreground until
# you Ctrl-C it.
#
# usage:
# ./host-room.sh # 0.0.0.0:4173, --no-tls, random password
# ./host-room.sh 4200 # custom port
# ./host-room.sh --host 100.x.y.z # bind a specific address
# PW=hunter2 ./host-room.sh # pin a known password (or --password)
# ./host-room.sh --tls --cert c.pem --key k.pem # real TLS instead of --no-tls
# ./host-room.sh -h # full usage
#
# The password lives in memory only (passed via $CMD_CHAT_PASSWORD, never on the
# command line, so it won't show up in `ps`). Reveal/share it in-app with /pw.
set -uo pipefail
usage() {
cat <<EOF
host-room.sh — host a hack-house room (the server)
usage:
./host-room.sh [PORT] [--host ADDR] [--password PW]
./host-room.sh --tls --cert CERT --key KEY [PORT]
./host-room.sh -h | --help
flags:
--host ADDR bind address (default: 0.0.0.0 — reachable on all NICs)
--port PORT listen port (default: 4173; also accepted positionally)
--password PW room password (default: random; or set PW=… / --password)
--tls serve real TLS (requires --cert and --key)
--cert CERT TLS certificate path (implies --tls)
--key KEY TLS private key path (implies --tls)
-h, --help show this help and exit
environment (override any default):
HOST bind address (default: 0.0.0.0)
PORT listen port (default: 4173)
PW room password (default: random, openssl-generated)
notes:
- Default transport is --no-tls — the norm over a Tailscale tunnel. Pass --tls
with a cert/key for a public/untrusted network.
- Runs in the foreground; press Ctrl-C to stop the room.
- Missing Python deps? run ./bootstrap.sh first.
examples:
./host-room.sh # quick room on 0.0.0.0:4173 (--no-tls)
PW=hunter2 ./host-room.sh 4200 # known password, port 4200
./host-room.sh --tls --cert fullchain.pem --key privkey.pem 443
EOF
}
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # repo root
PY="$ROOT/.venv/bin/python"
[[ -x "$PY" ]] || PY="$(command -v python3 || command -v python)"
HOST="${HOST:-0.0.0.0}"
PORT="${PORT:-4173}"
PW="${PW:-}"
USE_TLS=0
CERT=""
KEY=""
# Parse flags (a lone number is taken as the port, for `./host-room.sh 4200`).
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help|-help) usage; exit 0 ;;
--host) HOST="$2"; shift 2 ;;
--host=*) HOST="${1#--host=}"; shift ;;
--port) PORT="$2"; shift 2 ;;
--port=*) PORT="${1#--port=}"; shift ;;
--password) PW="$2"; shift 2 ;;
--password=*) PW="${1#--password=}"; shift ;;
--tls) USE_TLS=1; shift ;;
--cert) CERT="$2"; USE_TLS=1; shift 2 ;;
--cert=*) CERT="${1#--cert=}"; USE_TLS=1; shift ;;
--key) KEY="$2"; USE_TLS=1; shift 2 ;;
--key=*) KEY="${1#--key=}"; USE_TLS=1; shift ;;
[0-9]*) PORT="$1"; shift ;;
*) echo "✖ unknown argument: $1 (try --help)" >&2; exit 2 ;;
esac
done
# Mint a strong password if none was supplied — random per run, in memory only.
if [[ -z "$PW" ]]; then
if command -v openssl >/dev/null 2>&1; then PW="$(openssl rand -hex 12)"
else PW="$(head -c 18 /dev/urandom | base64)"; fi
fi
# Resolve transport: --no-tls by default; --tls needs both cert and key.
if [[ $USE_TLS -eq 1 ]]; then
[[ -n "$CERT" && -n "$KEY" ]] || { echo "✖ --tls needs both --cert and --key" >&2; exit 2; }
[[ -f "$CERT" ]] || { echo "✖ cert not found: $CERT" >&2; exit 2; }
[[ -f "$KEY" ]] || { echo "✖ key not found: $KEY" >&2; exit 2; }
SERVE_FLAGS=(--cert "$CERT" --key "$KEY")
PROTO="https"; CLIENT_FLAG="--insecure"
else
SERVE_FLAGS=(--no-tls)
PROTO="http"; CLIENT_FLAG="--no-tls"
fi
# Sanity-check deps so failures are an actionable hint, not a stack trace.
if ! "$PY" -c "import sanic" >/dev/null 2>&1; then
echo "✖ Python deps missing (sanic not importable with $PY)." >&2
echo " run ./bootstrap.sh to set up the .venv, then retry." >&2
exit 1
fi
# Best IP to hand a joiner: Tailscale first (encrypted tunnel), else a global LAN IP.
TS_IP="$(tailscale ip -4 2>/dev/null | head -1 || true)"
LAN_IP="$(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1)"
JOIN_IP="${TS_IP:-${LAN_IP:-$HOST}}"
echo "═══════════════════════════════════════════════"
echo " ⛧ hack-house room — $PROTO://$HOST:$PORT"
echo "═══════════════════════════════════════════════"
[[ -n "$TS_IP" ]] && echo " tailscale : $TS_IP (recommended — encrypted)"
[[ -n "$LAN_IP" ]] && echo " lan : $LAN_IP"
echo " password : $PW (share it; reveal in-app with /pw)"
echo
echo " others join with:"
echo " python cmd_chat.py connect $JOIN_IP $PORT <name> $CLIENT_FLAG"
echo " or, with the TUI client:"
echo " hh/target/debug/hack-house connect $JOIN_IP $PORT <name> $CLIENT_FLAG"
echo
echo " Ctrl-C to stop the room."
echo "═══════════════════════════════════════════════"
# Hand the password to the server via env (resolve_password reads it), so it
# never appears in the process list. Foreground exec: Ctrl-C stops the room.
cd "$ROOT"
exec env CMD_CHAT_PASSWORD="$PW" "$PY" cmd_chat.py serve "$HOST" "$PORT" "${SERVE_FLAGS[@]}"
+1 -1
View File
@@ -6,7 +6,7 @@
# prompt — never hardcoded, so it can't drift from the room's random password.
NAME="${1:-guest}"
HOST="${2:-127.0.0.1}"
cd "$(dirname "$0")"
cd "$(dirname "$0")/.."
# Sync the latest code before joining, then rebuild so it takes effect. Pulls
# are best-effort and fast-forward only: an unreachable remote or diverged
+1 -1
View File
@@ -65,7 +65,7 @@ examples:
EOF
}
HERE="$(cd "$(dirname "$0")" && pwd)" # .../hh
HERE="$(cd "$(dirname "$0")/.." && pwd)" # .../hh
ROOT="$(cd "$HERE/.." && pwd)" # repo root
PY="$ROOT/.venv/bin/python" # venv always from the real checkout
BIN="$HERE/target/debug/hack-house"
+1 -1
View File
@@ -15,7 +15,7 @@
# Env overrides: PY=<python> BIN=<client binary> PORT=<port> PW=<password>
set -uo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Python: prefer the repo venv locally, fall back to PATH (CI installs into the
# job's own interpreter).
if [[ -z "${PY:-}" ]]; then
+2 -2
View File
@@ -3,10 +3,10 @@
# Exercises the full use-case path end to end against a live server:
# rust unit tests → SRP self-test → boot server → rust client handshake +
# round-trip → cross-language (python decrypts what the rust client sent).
# Run from anywhere: hh/smoke.sh
# Run from anywhere: hh/scripts/smoke.sh
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)" # .../hh
HERE="$(cd "$(dirname "$0")/.." && pwd)" # .../hh
ROOT="$(cd "$HERE/.." && pwd)" # repo root
PY="$ROOT/.venv/bin/python"
BIN="$HERE/target/debug/hack-house"
@@ -14,7 +14,7 @@
# tmux attach -t hh-autotest
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)" # .../hh
HERE="$(cd "$(dirname "$0")/.." && pwd)" # .../hh
ROOT="$(cd "$HERE/.." && pwd)" # repo root
PY="$ROOT/.venv/bin/python"
BIN="$HERE/target/debug/hack-house"
+4 -4
View File
@@ -1397,7 +1397,7 @@ fn handle_command(
&& !start_daemon
&& !sbx::docker_daemon_up()
{
app.err("docker daemon is not running — retry with `/sbx launch docker --start` to boot it (sudo), or run ./ensure-docker.sh in a terminal first");
app.err("docker daemon is not running — retry with `/sbx launch docker --start` to boot it (sudo), or run ./scripts/ensure-docker.sh in a terminal first");
} else {
let sz = term.size().map(|s| (s.width, s.height)).unwrap_or((80, 24));
let (rows, cols) = sbx_dims(sz.0, sz.1);
@@ -1518,7 +1518,7 @@ fn handle_command(
tokio::spawn(async move {
let res = tokio::task::spawn_blocking(|| {
let ver = sbx::vbox_version().ok_or_else(|| {
"VirtualBox isn't installed — install it with `/sbx gui <vm> --install`, or run ./ensure-vbox.sh".to_string()
"VirtualBox isn't installed — install it with `/sbx gui <vm> --install`, or run ./scripts/ensure-vbox.sh".to_string()
})?;
let vms = sbx::list_vms().map_err(|e| e.to_string())?;
Ok::<_, String>((ver, vms))
@@ -1821,10 +1821,10 @@ fn handle_command(
ms.join(", ")
),
Ok(_) => "ollama is reachable but has no models pulled — \
`ollama pull qwen2.5:3b` or run ./bootstrap-ai.sh"
`ollama pull qwen2.5:3b` or run ./scripts/bootstrap-ai.sh"
.to_string(),
Err(_) => "ollama not reachable at localhost:11434 — run \
./bootstrap-ai.sh, or `/ai start <profile>` for a cloud model"
./scripts/bootstrap-ai.sh, or `/ai start <profile>` for a cloud model"
.to_string(),
};
let _ = tx.send(Net::Sys(msg));
+4 -4
View File
@@ -12,10 +12,10 @@ use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std::sync::mpsc;
/// Helper that ensures the Docker daemon is running (ships beside this source).
const ENSURE_DOCKER: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/ensure-docker.sh");
/// Detect-first VirtualBox installer (ships beside this source).
const ENSURE_VBOX: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/ensure-vbox.sh");
/// Helper that ensures the Docker daemon is running (ships in hh/scripts/).
const ENSURE_DOCKER: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/scripts/ensure-docker.sh");
/// Detect-first VirtualBox installer (ships in hh/scripts/).
const ENSURE_VBOX: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/scripts/ensure-vbox.sh");
/// Is the Docker daemon accepting connections? (`docker info` succeeds.)
pub fn docker_daemon_up() -> bool {