Problem
Switchyard already ships switchyard launch subcommands for Claude Code, Codex CLI, and OpenClaw, but there is no equivalent for two widely-used coding agents:
- OpenCode (
opencode-ai) — no launcher, so users must hand-write a transient OPENCODE_CONFIG_DIR with an @ai-sdk/openai-compatible provider to point it at a Switchyard deployment.
- Hermes (
hermes-agent) — no launcher, so users must manually override OPENROUTER_BASE_URL / OPENROUTER_API_KEY plus --provider custom -m <route>.
Both are otherwise well supported by Switchyard's native, health-aware multi-endpoint serving path, but wiring them up is error-prone and undocumented. Additionally, none of the existing launchers run on Windows: shell_tui.py imports pty/fcntl/termios/tty at module load, banner_pause assumes POSIX raw mode, binary discovery relies on the executable bit, and *.cmd/*.bat npm shims cannot be exec'd directly by CreateProcess.
Proposed solution
Add two peer launchers in switchyard/cli/launchers/, mirroring the existing claude/codex/openclaw shape:
opencode_launcher.py — writes a transient opencode.json under a temp dir, declares a switchyard provider (@ai-sdk/openai-compatible, base URL → http://127.0.0.1:<port>/v1), selects the route via the config's model field, and launches opencode with the user's own command forwarded verbatim (no injected -m, which non-model subcommands like serve/debug reject). Workspace is cleaned up on success/error/interrupt.
hermes_launcher.py — sets OPENROUTER_BASE_URL/OPENROUTER_API_KEY to the local proxy and runs hermes --provider custom -m <route> (interactive chat when nothing is forwarded; otherwise the user's command verbatim). The user's ~/.hermes/config.yaml is never touched.
Both are registered in launch_command.py (cmd_launch_opencode, cmd_launch_hermes) and switchyard_cli.py (launcher_parsers), with --model and forwarded args surfacing exactly like the existing launchers.
Windows support is handled in the shared runtime rather than duplicated per-launcher:
run_command() — routes *.cmd/*.bat shims through cmd.exe via subprocess.run(..., shell=True); native executables run shell-less.
is_executable_file() — treats any existing file as runnable on Windows (no exec bit); POSIX keeps the os.X_OK check.
stdin_is_tty() returns False off-POSIX, so the interactive PTY footer is skipped and the launcher falls back to the plain subprocess path.
shell_tui.py imports pty/fcntl/termios/tty under an os.name guard so the module still imports on Windows.
banner_pause() no-ops off-POSIX.
- Debug/state logging uses
%LOCALAPPDATA% on Windows.
Alternatives considered
- One launcher flag per routing algorithm — rejected; routing config belongs in the TOML deployment, not the launcher surface.
- Inject
-m <model> on the OpenCode CLI — rejected; opencode rejects -m on non-model subcommands (serve, debug, models, …), so model selection lives in the transient config instead.
- A separate Windows-only launcher variant — rejected; a single shared runtime branch keeps one code path and one set of tests.
Scope notes
- Extension of an existing component (launcher surface), not a new chain role; no changes to
RequestProcessor/LLMBackend/etc.
- Does not touch the public API in
switchyard/__init__.py.__all__.
- CLI-only (
nemo-switchyard[cli]); no new runtime dependencies. @ai-sdk/openai-compatible is bundled in the opencode binary.
- No backward-compatibility concerns: purely additive subcommands and a behavior-preserving shared-runtime change on POSIX.
- Deploy/route wiring for a specific host (e.g. OpenRouter
routes.toml) is intentionally out of scope for the launcher patch.
Additional context
- Prior art: the existing
claude_code_launcher.py, codex_cli_launcher.py, and openclaw_launcher.py establish the launcher contract (transient config, supervision, cleanup).
@ai-sdk/openai-compatible provider schema: https://opencode.ai/docs/providers/.
Problem
Switchyard already ships
switchyard launchsubcommands for Claude Code, Codex CLI, and OpenClaw, but there is no equivalent for two widely-used coding agents:opencode-ai) — no launcher, so users must hand-write a transientOPENCODE_CONFIG_DIRwith an@ai-sdk/openai-compatibleprovider to point it at a Switchyard deployment.hermes-agent) — no launcher, so users must manually overrideOPENROUTER_BASE_URL/OPENROUTER_API_KEYplus--provider custom -m <route>.Both are otherwise well supported by Switchyard's native, health-aware multi-endpoint serving path, but wiring them up is error-prone and undocumented. Additionally, none of the existing launchers run on Windows:
shell_tui.pyimportspty/fcntl/termios/ttyat module load,banner_pauseassumes POSIX raw mode, binary discovery relies on the executable bit, and*.cmd/*.batnpm shims cannot be exec'd directly byCreateProcess.Proposed solution
Add two peer launchers in
switchyard/cli/launchers/, mirroring the existing claude/codex/openclaw shape:opencode_launcher.py— writes a transientopencode.jsonunder a temp dir, declares aswitchyardprovider (@ai-sdk/openai-compatible, base URL →http://127.0.0.1:<port>/v1), selects the route via the config'smodelfield, and launchesopencodewith the user's own command forwarded verbatim (no injected-m, which non-model subcommands likeserve/debugreject). Workspace is cleaned up on success/error/interrupt.hermes_launcher.py— setsOPENROUTER_BASE_URL/OPENROUTER_API_KEYto the local proxy and runshermes --provider custom -m <route>(interactivechatwhen nothing is forwarded; otherwise the user's command verbatim). The user's~/.hermes/config.yamlis never touched.Both are registered in
launch_command.py(cmd_launch_opencode,cmd_launch_hermes) andswitchyard_cli.py(launcher_parsers), with--modeland forwarded args surfacing exactly like the existing launchers.Windows support is handled in the shared runtime rather than duplicated per-launcher:
run_command()— routes*.cmd/*.batshims throughcmd.exeviasubprocess.run(..., shell=True); native executables run shell-less.is_executable_file()— treats any existing file as runnable on Windows (no exec bit); POSIX keeps theos.X_OKcheck.stdin_is_tty()returns False off-POSIX, so the interactive PTY footer is skipped and the launcher falls back to the plain subprocess path.shell_tui.pyimportspty/fcntl/termios/ttyunder anos.nameguard so the module still imports on Windows.banner_pause()no-ops off-POSIX.%LOCALAPPDATA%on Windows.Alternatives considered
-m <model>on the OpenCode CLI — rejected;opencoderejects-mon non-model subcommands (serve,debug,models, …), so model selection lives in the transient config instead.Scope notes
RequestProcessor/LLMBackend/etc.switchyard/__init__.py.__all__.nemo-switchyard[cli]); no new runtime dependencies.@ai-sdk/openai-compatibleis bundled in the opencode binary.routes.toml) is intentionally out of scope for the launcher patch.Additional context
claude_code_launcher.py,codex_cli_launcher.py, andopenclaw_launcher.pyestablish the launcher contract (transient config, supervision, cleanup).@ai-sdk/openai-compatibleprovider schema:https://opencode.ai/docs/providers/.