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
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:
@@ -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
|
||||
|
||||
Executable
+134
@@ -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"
|
||||
Executable
+105
@@ -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
|
||||
@@ -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; }
|
||||
Executable
+136
@@ -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[@]}"
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user