# Declarative OpenCode + oh-my-openagent (oh-my-opencode) plugin setup. # # Mirrors ~/.config/opencode/ on the current workstation. # Imported by home/server.nix (sapphira). Auto-enables programs.opencode. # # Three files this module owns on disk (via xdg.configFile): # ~/.config/opencode/opencode.json <- programs.opencode.settings # ~/.config/opencode/tui.json <- programs.opencode.tui # ~/.config/opencode/oh-my-openagent.json <- oh-my-openagent plugin config # # Override any field in the importing module if needed. { config, lib, pkgs, ... }: let # Body of ~/.config/opencode/oh-my-openagent.json. # Loaded by the oh-my-openagent opencode plugin on startup. ohMyOpenagentConfig = { "$schema" = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json"; agents = { sisyphus = { model = "opencode/claude-opus-5"; variant = "max"; fallback_models = [ { model = "opencode/kimi-k3"; } { model = "opencode/gpt-5.6-sol"; variant = "medium"; } { model = "opencode/glm-5"; } { model = "opencode/big-pickle"; } ]; }; hephaestus = { model = "opencode/gpt-5.6-sol"; variant = "medium"; }; oracle = { model = "opencode/gpt-5.6-sol"; variant = "xhigh"; fallback_models = [ { model = "opencode/gemini-3.1-pro"; variant = "high"; } { model = "opencode/claude-opus-5"; variant = "max"; } ]; }; librarian = { model = "minimax-coding-plan/MiniMax-M3"; }; explore = { model = "opencode/gpt-5-nano"; fallback_models = [ { model = "minimax-coding-plan/MiniMax-M3"; } ]; }; "multimodal-looker" = { model = "opencode/gpt-5.6-sol"; variant = "low"; fallback_models = [ { model = "opencode/gpt-5-nano"; } ]; }; prometheus = { model = "opencode/claude-fable-5"; variant = "high"; fallback_models = [ { model = "opencode/kimi-k3"; variant = "high"; } ]; }; metis = { model = "opencode/claude-opus-5"; variant = "high"; fallback_models = [ { model = "opencode/kimi-k3"; variant = "low"; } ]; }; momus = { model = "opencode/gpt-5.6-sol"; variant = "xhigh"; fallback_models = [ { model = "opencode/claude-opus-5"; variant = "max"; } { model = "opencode/gemini-3.1-pro"; variant = "high"; } ]; }; atlas = { model = "opencode/claude-sonnet-4-6"; fallback_models = [ { model = "opencode/gpt-5.6-sol"; variant = "medium"; } { model = "minimax-coding-plan/MiniMax-M3"; } ]; }; "sisyphus-junior" = { model = "opencode/claude-sonnet-4-6"; fallback_models = [ { model = "opencode/gpt-5.6-sol"; variant = "medium"; } { model = "minimax-coding-plan/MiniMax-M3"; } { model = "opencode/big-pickle"; } ]; }; }; categories = { "visual-engineering" = { model = "opencode/gemini-3.1-pro"; variant = "high"; fallback_models = [ { model = "opencode/glm-5"; } { model = "opencode/claude-opus-5"; variant = "max"; } ]; }; ultrabrain = { model = "opencode/gpt-5.6-sol"; variant = "xhigh"; fallback_models = [ { model = "opencode/gemini-3.1-pro"; variant = "high"; } { model = "opencode/claude-opus-5"; variant = "max"; } ]; }; deep = { model = "opencode/gpt-5.6-sol"; variant = "medium"; fallback_models = [ { model = "opencode/claude-opus-5"; variant = "max"; } { model = "opencode/gemini-3.1-pro"; variant = "high"; } ]; }; artistry = { model = "opencode/gemini-3.1-pro"; variant = "high"; fallback_models = [ { model = "opencode/claude-opus-5"; variant = "max"; } { model = "opencode/gpt-5.6-sol"; variant = "high"; } ]; }; quick = { model = "opencode/gpt-5.4-mini"; fallback_models = [ { model = "opencode/gemini-3-flash"; } { model = "minimax-coding-plan/MiniMax-M3"; } { model = "opencode/gpt-5-nano"; } ]; }; "unspecified-low" = { model = "opencode/claude-sonnet-4-6"; fallback_models = [ { model = "opencode/gpt-5.6-sol"; variant = "medium"; } { model = "opencode/gemini-3-flash"; } { model = "minimax-coding-plan/MiniMax-M3"; } ]; }; "unspecified-high" = { model = "opencode/claude-sonnet-4-6"; fallback_models = [ { model = "opencode/gpt-5.6-sol"; variant = "medium"; } { model = "opencode/gemini-3-flash"; } { model = "minimax-coding-plan/MiniMax-M3"; } ]; }; writing = { model = "opencode/gemini-3-flash"; fallback_models = [ { model = "opencode/claude-sonnet-4-6"; } { model = "minimax-coding-plan/MiniMax-M3"; } ]; }; }; }; in { programs.opencode = { enable = true; # npx for MCP servers (webpage-mcp) and the plugin loader itself. # nodejs_22 includes npm; plain nodejs does NOT. extraPackages = [ pkgs.nodejs_22 ]; # ~/.config/opencode/opencode.json settings = { plugin = [ "oh-my-openagent@latest" ]; mcp = { webpage = { type = "local"; command = [ "npx" "-y" "-p" "webpage-mcp@latest" "webpage-mcp-stdio" ]; }; }; }; # ~/.config/opencode/tui.json # Mirrors workstation: oh-my-openagent also registered for the TUI. tui = { plugin = [ "oh-my-openagent@latest" ]; }; }; # ~/.config/opencode/oh-my-openagent.json — read by the plugin on startup. xdg.configFile."opencode/oh-my-openagent.json".text = builtins.toJSON ohMyOpenagentConfig; # Expose `opencode web` as a systemd user service. nginx on sapphira # proxies https://opencode.zeroq.su -> 127.0.0.1:4096. # # --hostname 0.0.0.0 binds the listener to every interface (matches the # "0.0.0.0" intent; nginx then reverse-proxies 127.0.0.1:4096 internally). # --cors https://opencode.zeroq.su lets the browser session reach the # server from that origin without CORS rejection. # # SECURITY: with no password, anyone reaching the upstream socket gets full # opencode. Bind 0.0.0.0 + listener == bridge == shell. The password is # supplied via sops-managed EnvironmentFile, declared in modules/users.nix # and decrypted to a path hardcoded here (home-manager modules cannot read # `config.sops.*` — sops-nix options are NixOS-only). programs.opencode.web = { enable = true; environmentFile = "${config.home.homeDirectory}/.config/opencode/server.env"; extraArgs = [ "--hostname" "0.0.0.0" "--cors" "https://opencode.zeroq.su" ]; }; }