muse-openrouter
v0.1.0
Published
A transparent compatibility gateway that lets the native Muse Code harness use OpenRouter Contributor without changing Muse semantics.
Maintainers
Readme
muse-openrouter
A transparent compatibility gateway that lets the native Muse Code harness use OpenRouter Contributor without changing Muse semantics.
Muse Code
→ localhost compatibility gateway (127.0.0.1:9911)
→ OpenRouter
→ meta/muse-spark-1.2-contributorMuse continues to think it talks to:
- provider:
meta - model:
muse-spark-1.2 - profile:
tbh
Only the top-level model name is rewritten for the upstream transport to meta/muse-spark-1.2-contributor. No silent fallback to meta/muse-spark-1.2 or any other model.
Requirements
- Node.js
>=24 - Muse Code installed separately (
muse --versionshould show0.2.1) - OpenRouter account and API key (
sk-or-v1-...)
Install
npm install -g muse-openrouterVerify:
muse-or --help
muse-or-proxy --help
muse-or doctorThe original muse binary is never replaced.
Setup
Run once:
muse-or setupThis will:
- find the real
musebinary in yourPATH - show the detected Muse version
- ask for your OpenRouter API key (input hidden)
- save it securely to
~/.config/muse-openrouter/config.jsonwith0600permissions - prepare an isolated Muse config at
~/.config/muse-openrouter/xdg/muse/settings.json - verify the setup
Setup is idempotent. Re-running it keeps existing settings unless you pass --force.
If you prefer env-based automation, OPENROUTER_API_KEY can override the stored key at runtime, but normal use is the persisted config.
Usage
Terminal 1 – gateway (foreground, Ctrl+C to stop):
muse-or-proxyStartup summary:
Muse OpenRouter Gateway
Listen 127.0.0.1:9911
Upstream OpenRouter
Model meta/muse-spark-1.2-contributor
Bridge namespace
Normalizer schema-aware
Status ready
Health: http://127.0.0.1:9911/healthTerminal 2 – Muse:
cd ~/your-project
muse-or --reasoning-effort xhigh
muse-or resume SESSION_IDAll native Muse arguments are forwarded unchanged, including --reasoning-effort. The reasoning request object is passed through untouched.
If the gateway is not running, muse-or fails fast:
gateway is not running on 127.0.0.1:9911
run: muse-or-proxyNo automatic fallback to direct Meta and no background daemon.
How it works
Muse Code 0.2.1 sends tools as a Responses type: "namespace" container (muse with nested read_file, bash, subagent tools, etc.). Some OpenAI-compatible gateways are more reliable with flat function tools, so the gateway:
- Flatten – converts the
musenamespace into ordinary top-level function tools for the OpenRouter transport. - Forward – rewrites only the top-level
modeltometa/muse-spark-1.2-contributorand proxiesPOST /v1/responsestohttps://openrouter.ai/api/v1/responses. - Restore – on the response path, restores
namespace: "muse"and normalizes tool arguments before Muse sees them. - Normalize – schema-aware, conservative coercion of tool arguments (e.g.
"100"→100,"false"→false) based on the exact JSON Schemas Muse sent, including$ref,anyOf/oneOf/allOf. Never invents missing required arguments.
Other routes (/muse-code/models, /muse-code/config, /health) are served locally.
Philosophy / non-goals
This is not a new Muse client and does not try to improve Muse.
The gateway is deliberately minimal:
- Native Muse stays native – tool semantics, prompts, catalog/profile (
tbh), limits (1,048,576 ctx / 131,072 out) remain unchanged. - No daemon/service –
muse-or-proxyis always foreground. No systemd, no background manager. - No silent fallback – if the gateway is down or OpenRouter errors, the error is surfaced.
- No semantic reinterpretation –
subagent_send_message interrupt:truereturninginvalid_inputis native Muse behavior and is not “fixed” by the adapter. - No prompt or reasoning management – Muse owns
reasoningconfiguration. - No auto-launch –
muse-ornever starts the proxy automatically. - No hidden rewrites –
/bin/bash→/bin/shis not done (opt-in only if ever needed, default off).
If Muse returns an error, the gateway passes it through.
Configuration
Persistent config lives at:
~/.config/muse-openrouter/config.json
# or $XDG_CONFIG_HOME/muse-openrouter/config.jsonDefaults:
{
"host": "127.0.0.1",
"port": 9911,
"upstream_model": "meta/muse-spark-1.2-contributor",
"openrouter_base_url": "https://openrouter.ai/api/v1/",
"api_key": "sk-or-v1-..."
}Isolated Muse settings:
~/.config/muse-openrouter/xdg/muse/settings.jsonYou can inspect it:
jq '{provider,model,endpoint_transport,model_catalog}' ~/.config/muse-openrouter/xdg/muse/settings.jsonManage config:
muse-or config show
muse-or config set port 9912
muse-or config editPermissions are set to 0700 for directories and 0600 for files.
Doctor
muse-or doctorChecks:
- Node version
>=24 - real
musebinary and version - config existence/validity and permissions
- API key configured
- isolated Muse profile (
meta/muse-spark-1.2/tbh, limits) - expected Contributor model
- namespace bridge and normalizer enabled
- gateway liveness via
/health(if running)
The API key is never printed.
Proxy options
muse-or-proxy --verbose
muse-or-proxy --debug-tools
muse-or-proxy --debug-schemas subagent_send_message,subagent_cancel
muse-or-proxy --capture /tmp/capture
muse-or-proxy --host 127.0.0.1 --port 9911For backwards compatibility, env vars still work:
DEBUG=1 TOOL_DEBUG=1 TOOL_CAPTURE_DIR=/tmp/capture OPENROUTER_API_KEY=sk-or-v1-... muse-or-proxyNormal mode logs compact request lines. --verbose enables detailed logs.
Health endpoint:
curl -s http://127.0.0.1:9911/health | jqTroubleshooting
gateway is not running on 127.0.0.1:9911 – start muse-or-proxy in another terminal.
muse: command not found – install Muse Code separately and ensure muse is in PATH. muse-or doctor shows the resolved binary.
OPENROUTER_API_KEY is required – run muse-or setup or set OPENROUTER_API_KEY env for automation.
EADDRINUSE / port occupied – another muse-or-proxy is running or the port is taken. Use muse-or config set port 9912 or muse-or-proxy --port 9912.
Auth failure / 401 from OpenRouter – verify the key at https://openrouter.ai/keys. The gateway never logs the key; check /health confirms the model but not the secret.
NixOS /bin/bash missing – the gateway does not rewrite /bin/bash to /bin/sh by default. Muse tool semantics are preserved. An explicit opt-in shim could be added if needed, but is off by default.
Tool decoding errors – run with diagnostics:
muse-or-proxy --debug-tools --capture /tmp/muse-capture
# or
DEBUG=1 TOOL_DEBUG=1 TOOL_DEBUG_ARGS=1 TOOL_CAPTURE_DIR=/tmp/capture muse-or-proxyCaptures contain r0001-request-tools.json and r0001-tool-events.ndjson (tool-related SSE only).
subagent_send_message interrupt:true → invalid_input – this is native Muse Code 0.2.1 behavior despite a permissive schema. The gateway does not reinterpret it.
Security
- The API key is stored only in
~/.config/muse-openrouter/config.json(0600). - It is never logged, never committed to the repo, never written to the isolated Muse settings, and never forwarded to Muse.
- All diagnostics redact
sk-or-...andBearer .... - Use
muse-or doctorto verify permissions without revealing the secret.
Tests
npm testCovers namespace flatten/restore, collisions, streaming SSE, argument normalization, malformed inputs, model rewrite, reasoning preservation, health, launcher forwarding, and CLI behavior.
License
MIT – see LICENSE if present in the repo.
