Files
localgenai/opencode/install.sh
noisedestroyers 2c4bfefa95 Initial commit: localgenai stack
Containerized local LLM stack for the Framework Desktop / Strix Halo,
plus the OpenCode harness on the Mac side.

- pyinfra/framework/: pyinfra deploy targeting the box
  - llama.cpp (Vulkan), vLLM (ROCm), Ollama (ROCm with HSA override
    for gfx1151), OpenWebUI
  - Beszel (host + container + AMD GPU dashboard via sysfs)
  - OpenLIT (LLM fleet metrics)
  - Phoenix (per-trace agent waterfall)
  - OpenHands (autonomous agent in a Docker sandbox)
- opencode/: OpenCode config + Phoenix bridge plugin (OTel exporter)
  - install.sh deploys to ~/.config/opencode/
- StrixHaloSetup.md / StrixHaloMemory.md / Roadmap.md / TODO.md:
  documentation and planning
- testing/qwen3-coder-30b/: small evaluation harness

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 11:35:10 -04:00

146 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Bootstrap or re-sync the OpenCode harness on this Mac.
#
# Idempotent — re-run after pulling changes to opencode.json or the
# Phoenix bridge plugin. Each step checks before doing work.
#
# What this does:
# 1. Verify Homebrew is present
# 2. Install node, uv, opencode, jq (skips if already at latest)
# 3. Pre-cache Playwright's chromium so the first MCP call is instant
# 4. Install the Phoenix bridge plugin's OTel deps
# 5. Generate ~/.config/opencode/opencode.json from the repo's
# opencode.json with relative plugin paths rewritten to absolute,
# so opencode loads the plugin regardless of where it's launched.
#
# Usage: ./install.sh (from this directory)
set -euo pipefail
cd "$(dirname "$0")"
HERE="$(pwd)"
# --- Pretty printing ---------------------------------------------------------
bold() { printf '\033[1m%s\033[0m\n' "$*"; }
ok() { printf ' \033[32m✓\033[0m %s\n' "$*"; }
info() { printf ' → %s\n' "$*"; }
warn() { printf ' \033[33m!\033[0m %s\n' "$*"; }
fail() { printf ' \033[31m✗\033[0m %s\n' "$*"; exit 1; }
# --- 1. Homebrew -------------------------------------------------------------
bold "[1/5] Homebrew"
if ! command -v brew >/dev/null 2>&1; then
fail "brew not found. Install from https://brew.sh, then re-run."
fi
ok "brew $(brew --version | head -1 | awk '{print $2}')"
# --- 2. CLI deps -------------------------------------------------------------
bold "[2/5] CLI dependencies"
brew_install_if_missing() {
local pkg="$1"
local bin="${2:-$1}"
if command -v "$bin" >/dev/null 2>&1; then
ok "$pkg already installed ($(command -v "$bin"))"
else
info "installing $pkg"
brew install "$pkg"
ok "$pkg installed"
fi
}
brew_install_if_missing node node
brew_install_if_missing uv uv
brew_install_if_missing jq jq
# opencode is in a tap; check the binary, not the formula name.
if command -v opencode >/dev/null 2>&1; then
ok "opencode already installed ($(command -v opencode))"
else
info "tapping sst/tap and installing opencode"
brew install sst/tap/opencode
ok "opencode installed"
fi
# --- 3. Playwright browsers --------------------------------------------------
bold "[3/5] Playwright browser cache"
PW_CACHE="${HOME}/Library/Caches/ms-playwright"
if [[ -d "$PW_CACHE" ]] && find "$PW_CACHE" -name "chrome" -o -name "Chromium*" 2>/dev/null | grep -q .; then
ok "browsers already cached at $PW_CACHE"
else
info "downloading chromium (~200 MB) — first run only"
npx -y @playwright/mcp@latest --help >/dev/null 2>&1 || true
ok "browsers cached"
fi
# --- 4. Phoenix bridge plugin deps ------------------------------------------
bold "[4/5] Phoenix bridge plugin deps"
if [[ -d ".opencode/plugin/node_modules" && -f ".opencode/plugin/package-lock.json" ]]; then
# Re-run npm install if package.json is newer than the lockfile, otherwise skip.
if [[ ".opencode/plugin/package.json" -nt ".opencode/plugin/package-lock.json" ]]; then
info "package.json is newer than lockfile — running npm install"
( cd .opencode/plugin && npm install )
ok "deps updated"
else
ok "deps already installed"
fi
else
info "installing OTel deps (one-time, ~40 MB)"
( cd .opencode/plugin && npm install )
ok "deps installed"
fi
# --- 5. Generate ~/.config/opencode/opencode.json ---------------------------
# The repo's opencode.json uses relative plugin paths so it stays valid
# in-place. Rewriting them to absolute paths here makes opencode find the
# plugin regardless of which directory it was launched from. Re-run this
# script after editing opencode.json.
bold "[5/5] Deploy global config"
mkdir -p "${HOME}/.config/opencode"
src="${HERE}/opencode.json"
dst="${HOME}/.config/opencode/opencode.json"
# If the user previously had a symlink from the old install.sh, replace it.
if [[ -L "$dst" ]]; then
info "removing stale symlink at $dst"
rm "$dst"
fi
# And the old .opencode dir symlink — no longer needed now that plugin
# paths are absolute.
if [[ -L "${HOME}/.config/opencode/.opencode" ]]; then
info "removing stale ~/.config/opencode/.opencode symlink"
rm "${HOME}/.config/opencode/.opencode"
fi
# Rewrite any relative plugin path (./foo, ../foo) to an absolute path
# rooted at this directory. Absolute paths and npm-package refs pass
# through untouched.
jq --arg here "$HERE" '
.plugin = (
(.plugin // [])
| map(
if type == "string" and (startswith("./") or startswith("../"))
then ($here + "/" + ltrimstr("./") | gsub("/\\./"; "/"))
else .
end
)
)
' "$src" > "$dst.tmp"
mv "$dst.tmp" "$dst"
ok "wrote $dst"
info "plugin paths resolved to:"
jq -r '.plugin[]?' "$dst" | sed 's/^/ /'
echo
bold "Done."
cat <<EOF
Re-run this script after editing opencode.json — the deployed copy at
~/.config/opencode/opencode.json is generated, not symlinked.
Next steps:
- Verify the model server: curl -s http://framework:11434/v1/models | jq '.data[].id'
- Verify Phoenix is up: curl -sf http://framework:6006/
- Run opencode and send one prompt. A trace should appear at
http://framework:6006 within a couple of seconds.
- If nothing appears, check ~/.local/share/opencode/log/*.log for a
line starting with "[phoenix-bridge]".
EOF