#!/usr/bin/env bash
# Purpose: build an agentic coding workstation from nothing - the directory layout, the
#          agent configurations pointed at the gateway's role aliases, the MCP server
#          declarations, the sandbox image and the runbook - so that the whole thing can
#          be reproduced on a new machine or after a mistake.
# Platform: all (spark, strix, nvidia natively; mac with Docker Desktop or without the
#           sandbox step)
# Minimum memory: 16 GB for the agent role behind the gateway alias
# Assumes: the Part 9 gateway is running and reachable at GATEWAY_URL with the three role
#          aliases from workstation-aliases.yaml published; the configuration templates
#          from this part are in the directory this script is run from; docker and python3
#          are on PATH for the sandbox step. It refuses to overwrite an existing
#          workstation unless FORCE=1, because reproducibility is not the same as
#          clobbering.
#
# Usage:
#   ./setup-workstation.sh
#   WORKSTATION=~/agentic FORCE=1 ./setup-workstation.sh
#   SKIP_SANDBOX=1 ./setup-workstation.sh

set -euo pipefail

WORKSTATION="${WORKSTATION:-$HOME/agentic-workstation}"
TEMPLATES="${TEMPLATES:-$PWD}"
GATEWAY_URL="${GATEWAY_URL:-http://127.0.0.1:4000}"
AGENT_ALIAS="${AGENT_ALIAS:-local/agent}"
COMPLETION_ALIAS="${COMPLETION_ALIAS:-local/completion}"
JUDGE_ALIAS="${JUDGE_ALIAS:-local/judge}"
SKIP_SANDBOX="${SKIP_SANDBOX:-0}"
FORCE="${FORCE:-0}"

say() { printf '==> %s\n' "$1"; }
die() { printf 'error: %s\n' "$1" >&2; exit 1; }

for required in python3 curl; do
    command -v "$required" >/dev/null 2>&1 || die "$required is required and is not on PATH"
done

if [ "$SKIP_SANDBOX" != "1" ] && ! command -v docker >/dev/null 2>&1; then
    die "docker is not on PATH. Install it, or run with SKIP_SANDBOX=1 and read the page."
fi

if [ -e "$WORKSTATION" ] && [ "$FORCE" != "1" ]; then
    die "$WORKSTATION already exists. Move it aside, or re-run with FORCE=1."
fi

# ------------------------------------------------------------------- 1. the gateway first
# Nothing else is worth configuring if the roles are not published. Ask the gateway what it
# serves and check all three names are there before writing a single file.
say "checking the gateway at $GATEWAY_URL"
models_json="$(curl --silent --show-error --max-time 10 \
    --header "Authorization: Bearer ${GATEWAY_KEY:-}" \
    "$GATEWAY_URL/v1/models" || true)"

if [ -z "$models_json" ]; then
    die "the gateway did not answer at $GATEWAY_URL/v1/models. Start it, then re-run."
fi

missing="$(MODELS_JSON="$models_json" \
    AGENT="$AGENT_ALIAS" COMPLETION="$COMPLETION_ALIAS" JUDGE="$JUDGE_ALIAS" python3 - <<'PY'
import json
import os

try:
    payload = json.loads(os.environ["MODELS_JSON"])
except json.JSONDecodeError:
    print("the gateway did not return JSON")
    raise SystemExit(0)

published = {entry.get("id") for entry in payload.get("data", []) if isinstance(entry, dict)}
wanted = [os.environ["AGENT"], os.environ["COMPLETION"], os.environ["JUDGE"]]
absent = [name for name in wanted if name not in published]
print(" ".join(absent))
PY
)"

if [ -n "$missing" ]; then
    die "the gateway does not publish: $missing. Merge workstation-aliases.yaml and restart it."
fi
say "all three role aliases are published"

# ------------------------------------------------------------------ 2. the directory layout
say "creating $WORKSTATION"
mkdir -p \
    "$WORKSTATION/agents" \
    "$WORKSTATION/sandbox" \
    "$WORKSTATION/mcp" \
    "$WORKSTATION/projects" \
    "$WORKSTATION/evidence"

copy_template() {
    # copy_template <source-name> <destination-path>
    if [ -f "$TEMPLATES/$1" ]; then
        cp "$TEMPLATES/$1" "$2"
        say "installed $1"
    else
        printf '    skipped %s (not found in %s)\n' "$1" "$TEMPLATES"
    fi
}

# ------------------------------------------------------------------------- 3. the agents
copy_template "opencode.json"              "$WORKSTATION/agents/opencode.json"
copy_template "codex-config.toml"          "$WORKSTATION/agents/codex-config.toml"
copy_template "aider-model-settings.yml"   "$WORKSTATION/agents/aider-model-settings.yml"
copy_template "goose-local-env.txt"        "$WORKSTATION/agents/goose-local-env.txt"
copy_template "goose-recipe.yaml"          "$WORKSTATION/agents/goose-recipe.yaml"
copy_template "continue-agent-config.yaml" "$WORKSTATION/agents/continue-agent-config.yaml"
copy_template "zed-local-settings.json"    "$WORKSTATION/agents/zed-local-settings.json"

# Rewrite the alias names in the copies so that a workstation configured for different role
# names does not need six manual edits. Only the copies are touched; the templates are not.
if [ "$AGENT_ALIAS" != "local/agent" ]; then
    say "rewriting local/agent to $AGENT_ALIAS in the installed configurations"
    find "$WORKSTATION/agents" -type f -print0 \
        | xargs -0 sed -i.bak "s|local/agent|${AGENT_ALIAS}|g"
    find "$WORKSTATION/agents" -name '*.bak' -delete
fi

# ------------------------------------------------------------------------- 4. the sandbox
copy_template "agent-sandbox.Dockerfile" "$WORKSTATION/sandbox/agent-sandbox.Dockerfile"
copy_template "compose-sandbox.yaml"     "$WORKSTATION/sandbox/compose-sandbox.yaml"
copy_template "boundary-test.sh"         "$WORKSTATION/sandbox/boundary-test.sh"
copy_template "audit-sandbox.sh"         "$WORKSTATION/sandbox/audit-sandbox.sh"
copy_template "env-example.txt"          "$WORKSTATION/sandbox/env-example.txt"

if [ -f "$WORKSTATION/sandbox/env-example.txt" ] && [ ! -f "$WORKSTATION/sandbox/.env" ]; then
    cp "$WORKSTATION/sandbox/env-example.txt" "$WORKSTATION/sandbox/.env"
    say "created sandbox/.env from the template; fill it in before first use"
fi

if [ "$SKIP_SANDBOX" != "1" ] && [ -f "$WORKSTATION/sandbox/agent-sandbox.Dockerfile" ]; then
    say "building the sandbox image (this needs the network once)"
    docker build \
        --file "$WORKSTATION/sandbox/agent-sandbox.Dockerfile" \
        --tag agent-sandbox:workstation \
        "$WORKSTATION/sandbox"
fi

# ---------------------------------------------------------------------- 5. the MCP servers
# The servers themselves come from Part 24. This writes the declaration files the agents
# read, with an empty server list, so that adding one is a single edit in a known place
# rather than a search through six tools' configuration formats.
if [ ! -f "$WORKSTATION/mcp/mcp-servers.json" ]; then
    cat >"$WORKSTATION/mcp/mcp-servers.json" <<'JSON'
{
  "_readme": [
    "The MCP servers this workstation wires into its agents. Add one entry per server,",
    "using the shape each agent expects: Claude Code reads mcpServers from .mcp.json,",
    "Codex reads an mcp_servers table in config.toml, Goose takes --with-extension or an",
    "extensions block, and OpenCode has its own mcp configuration. This file is the single",
    "list you keep; the runbook records which agents it has been applied to.",
    "Every schema you add here is tokens in the prompt on every turn. Add what a task",
    "needs and no more."
  ],
  "mcpServers": {}
}
JSON
    say "created mcp/mcp-servers.json with an empty server list"
fi

# -------------------------------------------------------------------------- 6. the runbook
if [ ! -f "$WORKSTATION/workstation-runbook.md" ]; then
    copy_template "workstation-runbook.md" "$WORKSTATION/workstation-runbook.md"
fi

# ------------------------------------------------------------------------ 7. what to do next
cat <<EOF

Workstation created at: $WORKSTATION

  agents/     one configuration per tool, pointed at the gateway's role aliases
  sandbox/    the container, its boundary test and the audit script
  mcp/        the list of MCP servers this workstation wires in
  projects/   where the directories an agent may edit live
  evidence/   boundary-test sheets, audit reports and agent transcripts

Next, in this order:
  1. Fill in sandbox/.env: the project directory, the gateway address as seen from
     inside the sandbox network, and a per-sandbox gateway key.
  2. Generate one gateway virtual key per agent, so usage is attributable per tool.
  3. Run check-workstation.sh and fix whatever it reports.
  4. Write workstation-runbook.md: the models per role, the agents, the sandbox
     decisions, and the residual risks you have decided to accept.

Nothing above has been done for you, and each step needs a decision you should make
rather than inherit.
EOF
