№ 0297GitHub
pproxy for muse on OpenRouter
A translating proxy that lets the muse coding agent run on OpenRouter despite its compiled-out provider support; the included verifier drives the real muse binary and passes 19/19 checks.
# pproxy
A translating proxy that lets the `muse` coding agent run on OpenRouter.
`muse`'s own provider support for OpenRouter is compiled out of the shipped binary
(`provider 'openrouter' is not compiled into this build`), and its `meta` provider
speaks a proprietary dialect. This proxy sits between them: `muse` is pointed at
`pproxy` with `--base-url`, and `pproxy` talks to OpenRouter.
**Status: verified working end to end.** `uv run pproxy-verify` drives the real
`muse` binary through the proxy and passes 19/19 checks.
## Quick start
```sh
export OPENROUTER_API_KEY=sk-or-...
uv run pproxy # starts on 127.0.0.1:8817
# in another shell — META_API_KEY is an unused placeholder
MUSE_NO_AUTO_UPDATE=1 META_API_KEY=unused \
muse --provider meta --base-url http://127.0.0.1:8817 \
--model anthropic/claude-opus-5 --yolo
```
## What it translates
| muse sends | pproxy does | upstream |
| ----------------------- | ------------------------------------------- | ----------------- |
| `GET /muse-code/models` | rewrite schema | `GET /models` |
| `POST /responses` | swap credential, backfill `sequence_number` | `POST /responses` |
Two findings drove the design, both established by experiment rather than assumed:
1. **The catalog is proprietary.** `/muse-code/models` is muse-specific in both path
and schema. Rows need `id`, `model_id`, `provider_id`, `profile_id`,
`display_label`, `visibility`, `release_date`, `display_order`, `is_default`,
`context_limit`, `output_limit`; exactly one row must be `is_default` and visible;
ids must be unique. `provider_id` stays `"meta"` on every row — muse rejects rows
whose provider disagrees with the selected provider. The OpenRouter slug rides in
`id`/`model_id`.
2. **muse requires `sequence_number` on every SSE event.** It reports only
`error_kind=decode` with no field name. `pproxy-schemaprobe` bisected it: a stream
identical except for that field is rejected, and accepted with it. OpenRouter does
not always emit it, so the proxy backfills. The request body needs no translation
at all — muse speaks the Responses API and OpenRouter serves `/responses`.
## Credentials
muse only ever sends `Bearer $META_API_KEY` and has no concept of an OpenRouter key,
so the pro


ChatForm
Tgmlabs