terraform { required_providers { coder = { source = "coder/coder" } docker = { source = "kreuzwerker/docker" } } } provider "docker" {} data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} locals { username = data.coder_workspace_owner.me.name home_dir = "/home/${local.username}" home_host_path = "/mnt/pve/unas/services/coder/${local.username}/${data.coder_workspace.me.name}" } resource "coder_agent" "main" { arch = "amd64" os = "linux" startup_script = <<-EOT set -e # --- minimal deps (uv image is slim) --- if ! command -v git >/dev/null; then sudo apt-get update -qq sudo apt-get install -y --no-install-recommends git curl ca-certificates sudo openssh-client nano fi # --- latest stable Python via uv (tracks current release channel) --- uv python install --default uv python pin "$(uv python find | xargs -n1 basename | head -1)" 2>/dev/null || true echo "Python: $(uv run python --version)" # --- code-server (browser VS Code) --- if ! command -v code-server >/dev/null; then curl -fsSL https://code-server.dev/install.sh | sh fi mkdir -p ~/.config/code-server cat > ~/.config/code-server/config.yaml </tmp/code-server.log 2>&1 & # --- opencode CLI (uses LiteLLM + MCP gateway) --- if ! command -v opencode >/dev/null 2>&1; then curl -fsSL https://opencode.ai/install | sh >/dev/null 2>&1 || true fi # --- Claude Code CLI (routed via LiteLLM ANTHROPIC_BASE_URL) --- if ! command -v claude >/dev/null 2>&1; then curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - >/dev/null sudo apt-get install -y --no-install-recommends nodejs >/dev/null sudo npm install -g @anthropic-ai/claude-code >/dev/null fi # --- workspace scaffold (idempotent) --- mkdir -p ~/work ~/home [ -f ~/.bashrc ] || echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.bashrc # --- dotfiles (optional; user sets via UI param) --- if [ -n "${data.coder_parameter.dotfiles_uri.value}" ]; then coder dotfiles -y "${data.coder_parameter.dotfiles_uri.value}" || true fi EOT metadata { display_name = "CPU" key = "0_cpu" script = "coder stat cpu" interval = 10 timeout = 1 } metadata { display_name = "RAM" key = "1_ram" script = "coder stat mem" interval = 10 timeout = 1 } metadata { display_name = "Disk" key = "2_disk" script = "coder stat disk" interval = 60 timeout = 1 } metadata { display_name = "Python" key = "3_python" script = "uv run python --version 2>/dev/null || echo n/a" interval = 60 timeout = 5 } } data "coder_parameter" "dotfiles_uri" { name = "dotfiles_uri" display_name = "Dotfiles repo (optional)" description = "Git URL of a dotfiles repo. Coder will clone it and run install.sh on workspace start." type = "string" default = "https://git.nuclide.systems/fkrebs/dotfiles.git" mutable = true } resource "coder_app" "code-server" { agent_id = coder_agent.main.id slug = "code-server" display_name = "VS Code" url = "http://localhost:13337" icon = "/icon/code.svg" subdomain = false share = "owner" healthcheck { url = "http://localhost:13337/healthz" interval = 5 threshold = 6 } } resource "docker_image" "workspace" { name = "ghcr.io/astral-sh/uv:bookworm-slim" } resource "docker_container" "workspace" { count = data.coder_workspace.me.start_count image = docker_image.workspace.name name = "coder-${local.username}-${lower(data.coder_workspace.me.name)}" hostname = data.coder_workspace.me.name # The agent's init script bootstraps everything. entrypoint = ["sh", "-c", <<-EOT apt-get update -qq && apt-get install -y --no-install-recommends curl ca-certificates sudo procps >/dev/null 2>&1 ${coder_agent.main.init_script} EOT ] env = [ "CODER_AGENT_TOKEN=${coder_agent.main.token}", "OPENAI_API_KEY=sk-tapirnase", "OPENAI_BASE_URL=http://192.168.1.40:14000/v1", "OPENAI_MODEL=qwen3-coder-30b-a3b-instruct", "AIDER_MODEL=qwen3-coder-30b-a3b-instruct", "AIDER_WEAK_MODEL=mistral-small-latest", "EMBEDDING_MODEL=text-embedding-3-large", "ANTHROPIC_BASE_URL=http://192.168.1.40:14000", "ANTHROPIC_API_KEY=sk-tapirnase", "CHAT_ARTIFACTS_ENDPOINT=https://s3.nuclide.systems", "CHAT_ARTIFACTS_ACCESS_KEY=GK50bfcfcb7e05fb8421193ab0", "CHAT_ARTIFACTS_SECRET_KEY=fa139e0422c0140331a69be4053eb7fb0d04bd64cd33790714099481ba19e0bf", "CHAT_ARTIFACTS_BUCKET=chat-artifacts", "MCP_GATEWAY_TOKEN=JxxCqKw32XoN4LOHunDikS6u1RpS7R5ythzaqADPuIA", ] # Persistent home backed by UNAS. volumes { container_path = local.home_dir host_path = local.home_host_path read_only = false } # Intel Arc GPU. devices { host_path = "/dev/dri/renderD128" } # Sane resource caps; tune later. memory = 8192 cpu_shares = 1024 host { host = "host.docker.internal" ip = "host-gateway" } }