Two client-side fixes against the covert-gpt LiteLLM gateway, both traced from opencode throwing intermittent ContextWindowExceededError 400s. opencode (mcp-config/sync.sh): spec_for() mapped LiteLLM's /model/info onto opencode's schema 1:1, but the two mean different things by "output". LiteLLM's max_output_tokens is the whole window (vLLM caps TOTAL context, not output separately, which is correct for LiteLLM); opencode's limit.output is a RESERVE it subtracts from context to pick a compaction point. Copying one to the other told opencode to reserve the entire 262144 window, so it targeted an input of context-output and the server counted one token more than opencode did -- landing on context+1 and a hard 400, every time (measured: 196609 in + 65536 out = 262145 vs a 262144 window; the retries shrinking max_tokens by 65 never helped because the input grew to compensate). Capping the reserve alone would NOT have fixed it -- opencode would still sit exactly on the boundary, just at a different input size. So: cap the reserve (OUTPUT_RESERVE_CAP) and hold a margin back from context (CONTEXT_SAFETY_MARGIN). Generated at sync time, so it fixes every gateway model at once, including third-party ones. hermes (hermes/gateway.yaml, hermes/install.sh): Hermes reached the gateway by pretending it WAS OpenRouter -- the LiteLLM key in OPENROUTER_API_KEY, the gateway in OPENROUTER_BASE_URL. Requests routed fine, but fetch_openrouter_models hardcodes openrouter.ai/api/v1/models and ignores OPENROUTER_BASE_URL, so the picker listed OpenRouter's ~342-model catalog -- models this gateway cannot serve -- while the gateway's own models, including cg-1's, never appeared at all. Replaced with a named custom provider. install.sh now merges gateway.yaml into ~/.hermes/config.yaml, so a fresh box gets a Hermes that can actually talk to the gateway instead of one pointed at nothing. The merge is idempotent and narrow: only the covert-gpt entry is touched, model.default/provider are seeded only when unset, and a box still on the masquerade is migrated off it (fingerprint: provider openrouter + base_url pointing at this gateway; a genuine OpenRouter user never has that, and is left alone). No secrets: gateway.yaml names the key via key_env, and mcp-config/sync.sh is what exports it. Hermes needs no context safety margin -- it omits max_tokens entirely, so vLLM bounds output by whatever context is left. Also folds in the in-flight hermes bootstrap wiring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.2 KiB
dotfiles
Configures fresh VMs (RHEL-family or Debian-family) with a consistent
environment. Run ./bootstrap.sh (or curl-pipe it) to apply everything;
each module also runs standalone and is safe to re-run.
Quick start
On a fresh box, apply everything with one command:
curl -fsSL https://git.nic-oconnor.com/public/dotfiles/raw/branch/main/bootstrap.sh | sh
When piped like this (no local checkout), bootstrap.sh clones the repo to
~/.dotfiles and runs from there, so git must be installed first. Re-running
the command updates the checkout (git pull) and re-applies every module.
Override the destination or source with DOTFILES_DIR / DOTFILES_REPO.
Already have a clone? Just run it directly:
./bootstrap.sh
Modules
-
zsh/— installs zsh + oh-my-zsh, drops a managed~/.zshrc(robbyrussell theme, git plugin), and sets zsh as the login shell. The managed rc sources~/.zshrc.locallast for per-machine customizations (see below). -
git/— sets gituser.name/user.emailand configurescredential.helper store(plaintext~/.git-credentials, 0600) for HTTPS auth. -
docker/— installs full (rootful) Docker Engine from Docker's upstream package repo (get.docker.com on Debian-family; the docker-ce repo wired up directly on RHEL-family, since the convenience script rejects EL rebuilds like Oracle Linux by name), enables the service (systemd hosts), and adds the invoking user to thedockergroup (takes effect at next login). No-ops where a rootless Docker daemon is already running (e.g. Kasm) so it never fights that setup. Uses sudo. -
bin/— installs userland devops CLIs as self-contained, statically linked release binaries into~/.local/bin: jq, kubectl, helm, k9s, kubectx/kubens, stern, yq, OpenTofu, terragrunt, task (go-task, the Taskfile runner), the 1Password CLIopfor secrets storage, the opencode coding agent, zarf (DevSecOps for Offline and Airgapped Kubernetes), and packer (HashiCorp's infrastructure build tool). kind is installed only when full (non-rootless) Docker is available — it doesn't work against rootless Docker, so it is skipped on Kasm. No sudo, nothing outside$HOME, so tools persist across Kasm redeploys with no reinstall. Idempotent: each tool records its version under~/.local/bin/.versionsand is skipped when the pinned version is already present.zsh/zshrcputs~/.local/binonPATH.opencode is configured by
mcp-config/: it points at the covert-gpt LiteLLM gateway (https://llm.covert-gpt.com/v1for models,https://llm.covert-gpt.com/mcpfor MCP tools). Runmcp-config/install.shonce to store your LiteLLM key and enable config sync — without it, opencode installs but can't reach the gateway. -
ansible/— installs Ansible into a virtualenv at~/.local/opt/ansible(it's Python, not a single binary) and symlinks its CLIs into~/.local/bin. Persists across redeploys; only rebuilt when the venv is missing or broken. Building it may installpython3/python3-venvvia the system package manager (the one place this module uses sudo). -
docker-rootless-cgroup/— on systemd-less hosts (e.g. Kasm), hand-delegates cgroup v2 controllers to the rootless Docker daemon so per-container cgroups work (rootless Docker otherwise comes up withCgroupDriver=none). Uses sudo for host cgroup writes and restarts the daemon; no-ops on systemd or non-rootless hosts. Re-run at each bootstrap (the cgroup layout resets on redeploy); its PATH shim lives in$HOME. -
mcp-config/— shared MCP gateway config sync for laptops (LiteLLM / opencode). Independent ofbootstrap.sh; see its own README.
Per-machine customizations and secrets
~/.zshrc is managed — bootstrap overwrites it, so never hand-edit it.
Per-machine settings and secrets go in ~/.zshrc.local, which the managed
rc sources last (so it can override anything) and bootstrap never touches:
cp zsh/zshrc.local.example ~/.zshrc.local && chmod 600 ~/.zshrc.local
Variables modules look for:
-
GITEA_KEY— personal access token forgit.nic-oconnor.com(API/CLI use; HTTPS git auth itself goes throughgit/'s credential helper). -
LITELLM_KEY— covert-gpt gateway key used by opencode. Normally you don't set this by hand: runmcp-config/install.shonce and its shell hook exports it from~/.config/mcp-sync/key. Set it in~/.zshrc.localonly on a box without mcp-sync. -
OP_SERVICE_ACCOUNT_TOKEN— 1Password service-account token for non-interactiveop read/op run. -
GITEA_KEY,PROXMOX_MCP_TOKEN_ID,PROXMOX_MCP_TOKEN_SECRET— your own credentials for the client-side MCP servers inmcp-config/. opencode reads them from your environment ({env:VAR}) and passes them to the MCP server it spawns, so the tools act as you. Keep them in~/.zshrc.local; they are never written into any config file.These are NOT used by
ai-k8s-iac/task up: the LiteLLM gateway hosts no MCP servers and stores no MCP credentials. Seemcp-config/README.md. -
hermes/— installs Hermes Agent (Nous Research's self-improving AI agent) via npm. Hermes uses npx to spawn and downloads the Python hermes-agent from PyPI. Everything under $HOME, so it survives Kasm redeploys.It then merges
hermes/gateway.yamlinto~/.hermes/config.yaml, wiring Hermes to the covert-gpt LiteLLM gateway as a named custom provider. The merge is idempotent and touches only that provider entry, so the rest of your personal config is left alone;model.default/model.providerare seeded only on a fresh box. Unlikemcp-config/, this deploys at BOOTSTRAP time, not per-shell — re-runhermes/install.shto pick up a gateway.yaml change.Depends on
mcp-config/install.shhaving run: gateway.yaml names the key (key_env: LITELLM_KEY) but never contains it, andmcp-config/sync.shis what exports LITELLM_KEY into your shell. Without it Hermes 401s.
Secrets never go in this repo or in zsh/zshrc — that file is shared
config applied to every box.
lib/os-detect.sh provides shared package-manager detection (dnf/yum
vs apt); lib/fetch.sh provides arch detection and binary-download
helpers. Both are used by module install scripts.