Use Muse Code (muse) with any OpenAI-compatible API without creating a Meta account or using muse login.
Muse Code's meta provider defaults to https://api.meta.ai/v1, but you can override it with --base-url. The meta label is just a provider name — storing a key with muse auth set --provider meta does not require a Meta OAuth login. muse login is optional and only for Meta's hosted auth; API-key auth is enough.
- Route Muse Spark (
muse-spark-1.2, etc.) through a gateway/proxy (e.g., self-hosted, OpenCode, Helicone, Cloudflare AI Gateway, LiteLLM) - Avoid
muse login/ browser OAuth entirely - Keep the same CLI (
muse exec,museTUI) while swapping the backend
curl -fsSL https://dev.meta.ai/install.sh | sh
muse --version # Muse Code 0.2.1This does NOT create a Meta account. It just saves a key under the
metaprovider name.
# paste your gateway/proxy API key on stdin (never as an argv)
echo -n "YOUR_GATEWAY_API_KEY" | muse auth set --provider meta --api-key-stdin
# verify
cat ~/.config/muse/auth.json # { "schema_version": 1, "providers": { "meta": { "api_key": "..." } } }
# alternative: env var (takes priority over file)
export META_API_KEY="YOUR_GATEWAY_API_KEY"You never need to run muse login.
Muse Code accepts --base-url per invocation. Pass your OpenAI-compatible base URL:
# headless (CI / scripts)
muse exec --base-url https://your-gateway.example.com/v1 "say hi in 5 words"
# with image (Muse Spark supports image/pdf/video -> text)
muse exec --base-url https://your-gateway.example.com/v1 --image ./photo.jpg "describe this image"
# interactive TUI with custom endpoint
muse --base-url https://your-gateway.example.com/v1 "fix the tests in this repo"Model defaults to muse-spark-1.2 (~/.config/muse/settings.json → {"schema_version":1,"model":"muse-spark-1.2","provider":"meta"}). Override per run:
muse exec --base-url https://your-gateway.example.com/v1 --model muse-spark-1.2 "task"To avoid typing --base-url every time, wrap the binary:
BIN="$HOME/.local/bin/muse-bin-0.2.1-R1215.1" # or wherever `muse` is installed
BASE="https://your-gateway.example.com/v1"
cat > ~/.local/bin/muse-wrapper << SH
#!/bin/bash
BIN="$BIN"
BASE="$BASE"
case "\$1" in
exec|resume|config|export|trace|skills|sandbox|session-message|auth|login|logout|init)
exec "\$BIN" "\$1" --base-url "\$BASE" "\${@:2}"
;;
*)
exec "\$BIN" --base-url "\$BASE" "\$@"
;;
esac
SH
chmod +x ~/.local/bin/muse-wrapper
ln -sf ~/.local/bin/muse-wrapper ~/.local/bin/muse
# now just
muse exec "hello"
muse --image ./pic.png exec "what's in this image?"Note on
execordering:muse execexpects options afterexec(muse exec --base-url ...), not before. The wrapper above handles this.
If your custom provider is a local proxy (e.g., translating APIs), run it as a LaunchAgent:
<!-- ~/Library/LaunchAgents/com.muse-proxy.plist -->
<dict>
<key>Label</key><string>com.muse-proxy</string>
<key>ProgramArguments</key><array><string>/usr/bin/python3</string><string>/Users/you/.local/bin/my-muse-proxy.py</string></array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict>launchctl load ~/Library/LaunchAgents/com.muse-proxy.plist
curl -s http://127.0.0.1:8914/v1/models | headForward Muse's POST /responses to your gateway. Rewrite model names if needed:
import http.server, urllib.request, json
UPSTREAM = "https://your-gateway.example.com/v1"
API_KEY = "YOUR_GATEWAY_API_KEY" # read from env/keychain, don't hardcode
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if "models" in self.path:
body = json.dumps({"object":"list","data":[{"id":"muse-spark-1.2","object":"model"}]}).encode()
self.send_response(200); self.send_header("Content-Type","application/json")
self.send_header("Content-Length", str(len(body))); self.end_headers(); self.wfile.write(body); return
self.send_response(404); self.end_headers()
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length",0)) or 0)
# optional: rewrite model aliases
try:
j=json.loads(body); j["model"]=j.get("model","muse-spark-1.2"); body=json.dumps(j).encode()
except: pass
req = urllib.request.Request(UPSTREAM+"/responses", data=body,
headers={"Authorization":f"Bearer {API_KEY}","Content-Type":"application/json","User-Agent":"Muse/0.2.1"})
with urllib.request.urlopen(req) as r:
data=r.read(); self.send_response(r.status)
self.send_header("Content-Length", str(len(data))); self.end_headers(); self.wfile.write(data)
def log_message(self,*a,**k): pass
http.server.HTTPServer(("127.0.0.1",8914),H).serve_forever()OpenCode also supports custom OpenAI-compatible providers via opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"model": "my-gateway/muse-spark-1.2",
"provider": {
"my-gateway": {
"npm": "@ai-sdk/openai-compatible",
"name": "My Gateway",
"options": { "baseURL": "https://your-gateway.example.com/v1" },
"models": {
"muse-spark-1.2": { "name": "Muse Spark 1.2", "limit": { "context": 1048576, "output": 131072 } }
}
}
}
}opencode run -m my-gateway/muse-spark-1.2 "fix tests"Do I need a Meta account? No. muse auth set + --base-url is enough. muse login is only for Meta-hosted OAuth.
Does provider: meta mean I’m calling Meta directly? No—it's just the provider key. With --base-url it calls your gateway instead. Your Authorization: Bearer ... header is your gateway key.
What about images/PDFs? muse-spark-1.2 supports image, pdf, video as input modalities (1M context). Pass --image (repeatable) to muse exec.
Can I use this with OpenCode Go / other gateways? Yes—any OpenAI-compatible /v1/responses or /v1/chat/completions endpoint works; adjust the proxy’s upstream path.
- Never paste API keys as CLI args (they appear in
ps). Use--api-key-stdinor env vars. - Don’t commit
~/.config/muse/auth.jsonoropencodeauth files. - Gateways should use
Bearertokens over HTTPS only.
MIT — PRs welcome. Not affiliated with Meta.