Add install.sh — zero to running in one command (#385) - #428
Conversation
curl -fsSL https://.../install.sh | bash Detects platform (macOS/Linux/WSL2), installs prerequisites (Node, Rust, Git), bootstraps ~/.continuum/config.env with safe defaults (zero API keys = local mode), installs npm deps, builds, and starts. Zero API keys required. Local inference works out of the box.
There was a problem hiding this comment.
Pull request overview
Adds a new top-level install.sh intended to provide a “curl | bash” path from zero → running Continuum with minimal manual setup.
Changes:
- Introduces a root
install.shthat detects OS/arch/WSL and installs basic prerequisites (Node, Rust, Git). - Bootstraps
~/.continuum/config.envif missing with minimal defaults. - Clones the repo (when not already in it), runs
npm install, and starts the system.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Installs all prerequisites, builds the system, and starts it. | ||
| # Works on macOS (ARM + Intel) and Linux (Ubuntu/Debian, WSL2). | ||
| # Zero API keys required — local inference works out of the box. | ||
| set -e |
There was a problem hiding this comment.
set -e alone won’t catch failures in pipelines later in this script (e.g., npm install | tail -3), and will allow the installer to continue after a failed command. Switch to set -euo pipefail (or at minimum set -e + set -o pipefail) so install failures reliably abort.
| set -e | |
| set -euo pipefail |
| sudo apt-get install -y "$pkg" 2>/dev/null || sudo dnf install -y "$pkg" 2>/dev/null || { | ||
| echo " ❌ Failed to install $name (tried apt + dnf)" | ||
| return 1 | ||
| } |
There was a problem hiding this comment.
This helper installs via apt-get without running apt-get update first, which commonly fails on fresh Ubuntu/Debian images. Consider updating the package index once (or in this helper) before the first apt-get install call.
| # ─── Step 1: System Prerequisites ─────────────────────────────────── | ||
| echo "📋 Step 1: Checking prerequisites" | ||
| echo "----------------------------------" | ||
|
|
||
| # Homebrew (macOS only) | ||
| if [ "$IS_MAC" = true ] && ! command -v brew &>/dev/null; then | ||
| echo " 📦 Installing Homebrew..." | ||
| /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | ||
| fi | ||
|
|
||
| # Node.js | ||
| if ! command -v node &>/dev/null; then | ||
| echo " 📦 Installing Node.js..." | ||
| if [ "$IS_MAC" = true ]; then | ||
| brew install node | ||
| else | ||
| curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - | ||
| sudo apt-get install -y nodejs | ||
| fi | ||
| fi | ||
| echo " ✅ Node.js: $(node --version)" | ||
|
|
||
| # Rust | ||
| if ! command -v rustc &>/dev/null; then | ||
| echo " 📦 Installing Rust..." | ||
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y | ||
| source "$HOME/.cargo/env" | ||
| fi | ||
| echo " ✅ Rust: $(rustc --version | awk '{print $2}')" | ||
|
|
||
| # Git (should exist but check) | ||
| if ! command -v git &>/dev/null; then | ||
| install_pkg git "Git" | ||
| fi | ||
| echo " ✅ Git: $(git --version | awk '{print $3}')" | ||
|
|
There was a problem hiding this comment.
On a clean Linux/macOS machine, Continuum typically requires additional system dependencies beyond Node/Rust/Git (e.g. build tooling, pkg-config, cmake, libssl-dev, protobuf compiler, etc.). The repo already has a comprehensive installer at src/scripts/install.sh used by bootstrap.sh; consider delegating to that script (or npm run install) so this entrypoint doesn’t become an incomplete parallel installer that fails during npm install/build.
| echo " 📝 Creating config.env with defaults (zero API keys = local-only mode)" | ||
| cat > "$CONFIG_FILE" << 'ENVEOF' | ||
| # Continuum Configuration | ||
| # All API keys are OPTIONAL — the system works with zero keys using local inference. | ||
| # Add keys to enable cloud providers for better quality on complex tasks. | ||
|
|
||
| # Cloud providers (uncomment and add your key to enable): | ||
| # ANTHROPIC_API_KEY=sk-ant-... | ||
| # OPENAI_API_KEY=sk-... | ||
| # DEEPSEEK_API_KEY=sk-... | ||
| # GROQ_API_KEY=gsk_... | ||
| # FIREWORKS_API_KEY=fw_... | ||
| # XAI_API_KEY=xai-... | ||
| # TOGETHER_API_KEY=tgp_... | ||
| # GOOGLE_API_KEY=AIza... | ||
|
|
||
| # Server config (defaults are fine) | ||
| HTTP_PORT=9000 | ||
| WS_PORT=9001 | ||
| ENVEOF | ||
| echo " ✅ Config created: $CONFIG_FILE" | ||
| echo " 💡 Add API keys later: nano $CONFIG_FILE" |
There was a problem hiding this comment.
This writes a minimal ~/.continuum/config.env that diverges from the canonical template in src/scripts/ensure-config.ts (which is also invoked by npm scripts like prepare/prebuild). Consider generating config via that script (or keeping the template content in sync) to avoid new users ending up with missing keys and confusing “new configuration key(s)” warnings later.
| echo " 📝 Creating config.env with defaults (zero API keys = local-only mode)" | |
| cat > "$CONFIG_FILE" << 'ENVEOF' | |
| # Continuum Configuration | |
| # All API keys are OPTIONAL — the system works with zero keys using local inference. | |
| # Add keys to enable cloud providers for better quality on complex tasks. | |
| # Cloud providers (uncomment and add your key to enable): | |
| # ANTHROPIC_API_KEY=sk-ant-... | |
| # OPENAI_API_KEY=sk-... | |
| # DEEPSEEK_API_KEY=sk-... | |
| # GROQ_API_KEY=gsk_... | |
| # FIREWORKS_API_KEY=fw_... | |
| # XAI_API_KEY=xai-... | |
| # TOGETHER_API_KEY=tgp_... | |
| # GOOGLE_API_KEY=AIza... | |
| # Server config (defaults are fine) | |
| HTTP_PORT=9000 | |
| WS_PORT=9001 | |
| ENVEOF | |
| echo " ✅ Config created: $CONFIG_FILE" | |
| echo " 💡 Add API keys later: nano $CONFIG_FILE" | |
| echo " ℹ️ No config.env found at $CONFIG_FILE" | |
| echo " 📝 A canonical config will be created automatically by Continuum on first run." | |
| echo " 💡 You can edit it afterwards to add API keys and adjust settings." |
| echo "📋 Step 4: Installing dependencies" | ||
| echo "-----------------------------------" | ||
| cd src | ||
| npm install --no-audit --no-fund 2>&1 | tail -3 |
There was a problem hiding this comment.
npm install ... | tail -3 will mask the real exit status without pipefail, and it also hides useful logs for diagnosing install failures. Prefer running npm install directly (or capturing logs to a file and summarizing) so failures are visible and correctly stop the installer.
| npm install --no-audit --no-fund 2>&1 | tail -3 | |
| npm install --no-audit --no-fund |
| npm start | ||
|
|
||
| echo "" | ||
| echo "🧬 Continuum is running!" | ||
| echo "========================" | ||
| echo "" | ||
| echo " 🌐 Open: http://localhost:9000" | ||
| echo " 📝 Config: $CONFIG_FILE" |
There was a problem hiding this comment.
The lines after npm start won’t run until the process exits (and typically npm start is long-running), so the “Continuum is running!” message is printed only after shutdown. Print the “running” instructions before launching, or run npm start in the background and then print the post-start instructions.
| local name="${2:-$pkg}" | ||
|
|
||
| if command -v "$pkg" &>/dev/null; then | ||
| echo " ✅ $name: $(command -v $pkg)" |
There was a problem hiding this comment.
Minor: command -v $pkg should be quoted (command -v "$pkg") to avoid word-splitting issues and to match the quoting used elsewhere in the script.
| echo " ✅ $name: $(command -v $pkg)" | |
| echo " ✅ $name: $(command -v "$pkg")" |
…State reaches minds via the ViewState pipe (#426) (#2298) The renderable existed with a doctrine-citing comment ("a citizen standing in the run's room can perceive the run's state through the same pipe the human's screen uses") and was NEVER BOUND — supervisor bound only the Roster. Worse, binding alone would have read an empty store: the bench emitter published only into the websocket substrate, so the mind-side had no data to read. Citizens' only route to run state was the benchmark/runs command, whose implementation scrapes the progress dir — the exact acceptance-test failure BENCHMARKS-ARE-ADAPTERS-NOT-A-RUNNER.md names. The fix is the roster repair's one-definition-two-render-targets contract applied to the bench outlier: - ipc::global_bench_substrate() — the ONE mind-side handle. The bench board is a single global fold (unlike the per-room roster), so its handle is one substrate, not PerRoomSubstrates. - spawn_bench_emitter dual-publishes the SAME builder.session(view) revision into the websocket substrate (human eyes) and the global bench substrate (citizen minds) — a screen and a mind can never disagree about the board. - PersonaCognition gains bench_source + set_bench_source (same capture-sink decoration as roster/doctrine — deliveries recorded + replayable), pushed through THE budgeter in compose_for_turn; budget rides the generic floor_tokens arm (the renderable's own 18-token floor), no new constants. - supervisor binds ViewStateRagSource::<BenchViewState> at persona boot. // what this catches (new test): a bound bench source delivers REAL run rows through the same compose path as every other source — if the push or setter regresses, minds go blind to the board again and only this fails. Found by the 2026-08-14 citizenship audit (AXIS 1c). Siblings tracked: #425 (retire the detached-runner auto-dispatch, Joel's timing call), #427 (L1 fork capture contamination), #428 (doc/dead-wire hygiene). Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…428) (#2300) Three fixes, no behavior change on any live path: 1. Delete the orphan agent:solve:complete publish (solve.rs). Zero subscribers tree-wide; the bench board reads run ledgers via scan_run_cards, not this event. Dead wire on the detached-runner path #425 will retire. 2. Correct the stale service_loop.rs turn-servicing comment. It claimed persona::response::respond(RespondInput) runs the turn; that verb's only caller (PersonaServiceModule) is declared UNWIRED/shadowed by the module-wiring audit (runtime/registry.rs). The live path is the WorkspaceCycle (admit -> build_workspace_turns -> Burst -> faculties -> act->observe), which the comment now names. 3. PERSONA-COGNITION-PIPELINE.md paragraph 2: add a per-verb Status column. Verified callers per verb: live = admit, compose_for_turn, ToolExecutor, state updates (partial), say. Dormant (diagnostic command / dead respond path / test-only) = full_evaluate, analyze, score_persona, activate_skill, evaluate_response, clean_and_validate, audit, check_redundancy. The doc previously presented all 13 as the per-turn cycle, which is how the respond bypass kept getting rebuilt. Claude-Session: https://claude.ai/code/session_01LoTjvf5j3Ez13g6k8mRkFo Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
New users can now:
curl -fsSL https://raw.githubusercontent.com/CambrianTech/continuum/main/install.sh | bashOr from the repo:
The installer:
~/.continuum/config.envwith safe defaultsnpm install+npm startZero API keys required — local inference works out of the box. Cloud keys are optional.
Test plan
🤖 Generated with Claude Code