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:
@@ -97,7 +97,7 @@ jobs:
|
||||
run: cargo build
|
||||
working-directory: hh
|
||||
- name: run cross-stack smoke test
|
||||
run: bash hh/smoke-e2e.sh
|
||||
run: bash hh/scripts/smoke-e2e.sh
|
||||
|
||||
audit:
|
||||
name: dependency audit
|
||||
|
||||
@@ -50,8 +50,8 @@ Encrypted chat that runs in your terminal. You host the server, you control the
|
||||
| `cmd_chat/agent/` | The model-agnostic AI agent bridge (joins a room as an encrypted client) |
|
||||
| `models.toml` | Named provider profiles for `/ai start <profile>` (see `docs/providers.md`) |
|
||||
| `docs/providers.md` | Connect any model — profiles, flags, discovery, bring-your-own-provider |
|
||||
| `hh/lets-hack.sh` | Spin up a local test "clergy" in tmux (server + N client panes) |
|
||||
| `bootstrap-ai.sh` | Optional: install Ollama + pull a model for the local `/ai` agent |
|
||||
| `hh/scripts/lets-hack.sh` | Spin up a local test "clergy" in tmux (server + N client panes) |
|
||||
| `hh/scripts/bootstrap-ai.sh` | Optional: install Ollama + pull a model for the local `/ai` agent |
|
||||
| `hh/direnv-autostart/` | `cd` into a directory to auto-launch a session (direnv) |
|
||||
|
||||
## Quick start
|
||||
@@ -61,42 +61,42 @@ git clone https://github.com/leetcrypt/hack-house.git
|
||||
cd hack-house
|
||||
```
|
||||
|
||||
### 1. One-shot setup (`bootstrap.sh`)
|
||||
### 1. One-shot setup (`hh/scripts/bootstrap.sh`)
|
||||
|
||||
Checks prerequisites, creates the Python venv, installs the server's
|
||||
dependencies, and builds the Rust client:
|
||||
|
||||
```bash
|
||||
./bootstrap.sh # venv + deps + debug build
|
||||
./bootstrap.sh --release # release build
|
||||
./bootstrap.sh --check # report tooling only, change nothing
|
||||
hh/scripts/bootstrap.sh # venv + deps + debug build
|
||||
hh/scripts/bootstrap.sh --release # release build
|
||||
hh/scripts/bootstrap.sh --check # report tooling only, change nothing
|
||||
```
|
||||
|
||||
> `bootstrap.sh` does **not** touch direnv — the autostart in step 4 is a
|
||||
> separate, opt-in convenience.
|
||||
|
||||
**Optional AI layer (`bootstrap-ai.sh`).** Want the local `/ai` agent? This runs
|
||||
**Optional AI layer (`hh/scripts/bootstrap-ai.sh`).** Want the local `/ai` agent? This runs
|
||||
the baseline setup first, then installs Ollama (if missing) and pulls a default
|
||||
model — nothing here changes the AI-free baseline above:
|
||||
|
||||
```bash
|
||||
./bootstrap-ai.sh # baseline + Ollama + qwen2.5:3b
|
||||
./bootstrap-ai.sh --check # report only, change nothing
|
||||
HH_AI_MODEL=llama3 ./bootstrap-ai.sh # pull a different model
|
||||
hh/scripts/bootstrap-ai.sh # baseline + Ollama + qwen2.5:3b
|
||||
hh/scripts/bootstrap-ai.sh --check # report only, change nothing
|
||||
HH_AI_MODEL=llama3 hh/scripts/bootstrap-ai.sh # pull a different model
|
||||
```
|
||||
|
||||
### 2. Try it in tmux (`lets-hack.sh`)
|
||||
### 2. Try it in tmux (`scripts/lets-hack.sh`)
|
||||
|
||||
The fastest way to see it working: builds the client, boots a fresh `--no-tls`
|
||||
server on `127.0.0.1:4173`, and opens a pane per user.
|
||||
|
||||
```bash
|
||||
cd hh
|
||||
./lets-hack.sh # alice + bob, tiled in tmux
|
||||
./lets-hack.sh neo trinity # custom users
|
||||
./lets-hack.sh --theme neon # pick vestments
|
||||
./lets-hack.sh --reuse # keep a live server (reconnect tests)
|
||||
./lets-hack.sh --kill # tear it all down
|
||||
./scripts/lets-hack.sh # alice + bob, tiled in tmux
|
||||
./scripts/lets-hack.sh neo trinity # custom users
|
||||
./scripts/lets-hack.sh --theme neon # pick vestments
|
||||
./scripts/lets-hack.sh --reuse # keep a live server (reconnect tests)
|
||||
./scripts/lets-hack.sh --kill # tear it all down
|
||||
```
|
||||
|
||||
### 3. Manual setup
|
||||
@@ -180,7 +180,7 @@ server only ever sees ciphertext (same trust model as chat).
|
||||
| Backend | Isolation | Notes |
|
||||
|---|---|---|
|
||||
| `local` | none | a `bash` shell on the host — fast, for dev/testing only |
|
||||
| `docker` | container | `ubuntu:24.04` by default; `/sbx launch docker --start` boots the daemon (or run `./ensure-docker.sh`) |
|
||||
| `docker` | container | `ubuntu:24.04` by default; `/sbx launch docker --start` boots the daemon (or run `hh/scripts/ensure-docker.sh`) |
|
||||
| `multipass` | full VM | `24.04` by default; strongest isolation, ~30 s to boot, the choice for real use |
|
||||
|
||||
Tear it down with `/sbx stop` (purges the VM/container).
|
||||
@@ -247,7 +247,7 @@ when you quit). Pick a model at summon time with `/ai start <model>`.
|
||||
|
||||
- **Runs on *your* machine.** The default provider is [Ollama](https://ollama.com)
|
||||
— a local model (default `qwen2.5:3b`), no API key, nothing leaves your host.
|
||||
Run `./bootstrap-ai.sh` once to install it and pull the model.
|
||||
Run `hh/scripts/bootstrap-ai.sh` once to install it and pull the model.
|
||||
- **Addressed-only.** The agent reads room traffic like any client but forwards
|
||||
to the model *only* the messages that trigger it (`/ai …`) — no passive
|
||||
surveillance, no cost or noise when idle.
|
||||
@@ -301,8 +301,8 @@ to ~4000 lines of scrollback; the sandbox terminal keeps 2000.
|
||||
| Variable | Where | Effect |
|
||||
|---|---|---|
|
||||
| `CMD_CHAT_MAX_USERS` | server | Room capacity (default `4`) |
|
||||
| `PORT` · `PW` · `HOST` | `lets-hack.sh` | Override the test server's port / password / bind host |
|
||||
| `THEME` | `lets-hack.sh` | Vestments for every pane (`church` · `neon` · `crypt`) |
|
||||
| `PORT` · `PW` · `HOST` | `scripts/lets-hack.sh` | Override the test server's port / password / bind host |
|
||||
| `THEME` | `scripts/lets-hack.sh` | Vestments for every pane (`church` · `neon` · `crypt`) |
|
||||
| `HH_SESSION` · `HH_USER` | direnv autostart | tmux session name / your in-session name |
|
||||
|
||||
## Securing your connection
|
||||
|
||||
@@ -63,7 +63,7 @@ the chat provider is Ollama and a `qwen2.5-coder` is present (it is — pulled).
|
||||
|
||||
## Execution
|
||||
|
||||
`hh/demo-save-load.sh` drives the whole thing headlessly over tmux (per the
|
||||
`hh/scripts/demo-save-load.sh` drives the whole thing headlessly over tmux (per the
|
||||
TUI-tmux test recipe): boots the server, runs client **session A**, injects the
|
||||
beats with `send-keys`, verifies via `capture-pane` + `docker exec`, then quits
|
||||
session A and opens client **session B** to load and confirm. It is a PoC /
|
||||
|
||||
@@ -87,7 +87,7 @@ documented fallback for images without sshd.
|
||||
|
||||
Each headless VM needs a unique host loopback port for its SSH forward. Reuse the
|
||||
free-port discovery already used by the save/load PoC (see
|
||||
`docs/demo-save-load-poc.md` / `hh/demo-save-load.sh`) so two sandboxes on one
|
||||
`docs/demo-save-load-poc.md` / `hh/scripts/demo-save-load.sh`) so two sandboxes on one
|
||||
host don't collide. The owner is the only one who ever connects to it
|
||||
(`127.0.0.1:<port>`), so it never leaves the host.
|
||||
|
||||
@@ -146,7 +146,7 @@ desktop" become two ends of one workflow.
|
||||
|
||||
## 4. Installation handling — `ensure-vbox.sh` (detect first)
|
||||
|
||||
Mirror `hh/ensure-docker.sh` exactly in spirit: **a backend never installs
|
||||
Mirror `hh/scripts/ensure-docker.sh` exactly in spirit: **a backend never installs
|
||||
anything silently.**
|
||||
|
||||
### 4.1 Detection (always first, zero side effects)
|
||||
@@ -166,7 +166,7 @@ loud with the remedy**, exactly like the Docker daemon message at `app.rs:1206`:
|
||||
|
||||
### 4.2 The installer script
|
||||
|
||||
`hh/ensure-vbox.sh`, invoked as `bash ensure-vbox.sh --yes` only when the user
|
||||
`hh/scripts/ensure-vbox.sh`, invoked as `bash ensure-vbox.sh --yes` only when the user
|
||||
passed `--install` (matching how `prepare` shells `ensure-docker.sh --yes` at
|
||||
`sbx.rs:31`). It:
|
||||
|
||||
@@ -229,7 +229,7 @@ sudo delegation, and save/load machinery are all backend-agnostic already.
|
||||
|---|---|
|
||||
| `hh/src/sbx.rs` | `Backend::VirtualBox` variant; arms in `parse`/`label`/`default_image`/`prepare`/`command_for`/`provision`/`set_sudo`/`save_state`/`list_snapshots`/`teardown`; `vbox_installed()`, `vbx()` SSH helper; `export_ova()` + `open_local()` (Mode B, local-only). |
|
||||
| `hh/src/app.rs` | accept `virtualbox`/`vbox` in `/sbx launch`; generalize `/sbx load` off hardcoded Docker (`:1282`) to the broker backend; new `/sbx export`, `/sbx open`, `/sbx gui` arms; extend usage string (`:1309`); install-missing error mirroring `:1206`. |
|
||||
| `hh/ensure-vbox.sh` (new) | detect-first installer, per `§4`. |
|
||||
| `hh/scripts/ensure-vbox.sh` (new) | detect-first installer, per `§4`. |
|
||||
| `hh/src/ui.rs` | add the three new commands to the clustered help menu. |
|
||||
| `README.MD` | backend table (`:175`) gains a `virtualbox` row; a short "share a VM, run it locally" subsection. |
|
||||
| `models.toml` / docs | none. |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# OLLAMA_HOST daemon URL (default: http://localhost:11434)
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||
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"
|
||||
@@ -51,9 +51,9 @@ ollama_up() { curl -s --max-time 3 "$OLLAMA_HOST/api/tags" >/dev/null 2>&1; }
|
||||
# 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/bootstrap.sh" --no-ai --check || exit $?
|
||||
"$ROOT/hh/scripts/bootstrap.sh" --no-ai --check || exit $?
|
||||
else
|
||||
"$ROOT/bootstrap.sh" --no-ai "${RELEASE_ARGS[@]}" || exit $?
|
||||
"$ROOT/hh/scripts/bootstrap.sh" --no-ai "${RELEASE_ARGS[@]}" || exit $?
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
# 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 && ./lets-hack.sh
|
||||
# After it finishes, spin up a local test session with: cd hh && ./scripts/lets-hack.sh
|
||||
set -uo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
VENV="$ROOT/.venv"
|
||||
HH_DIR="$ROOT/hh"
|
||||
|
||||
@@ -92,14 +92,14 @@ fi
|
||||
if [[ $DO_AI -eq 1 ]]; then
|
||||
ai_args=(--ai-only)
|
||||
[[ $ASSUME_YES -eq 1 ]] && ai_args+=(--yes)
|
||||
"$ROOT/bootstrap-ai.sh" "${ai_args[@]}" \
|
||||
"$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 && ./lets-hack.sh # local test session (server + clients in tmux)"
|
||||
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 ./bootstrap-ai.sh"
|
||||
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; }
|
||||
@@ -54,7 +54,7 @@ examples:
|
||||
EOF
|
||||
}
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # repo root (where this lives)
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # repo root
|
||||
PY="$ROOT/.venv/bin/python"
|
||||
[[ -x "$PY" ]] || PY="$(command -v python3 || command -v python)"
|
||||
|
||||
@@ -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