mirasim-relay-bridge
Health Uyari
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Gecti
- Code scan — Scanned 2 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
把 Mirasim 内置的 Anthropic 反代接给独立 claude CLI 和 Zed ACP agent — 动态端口发现,不复制凭证
mirasim-relay-bridge
把 Mirasim 内置的 Anthropic 反代接出来,让独立的 claude CLI 和
Zed 的 ACP agent 也能走同一条链路 —— 不复制凭证、不碰 token 刷新、不写死端口。
macOS only(依赖 lsof 的行为和 macOS 上的进程模型)。
前提比你想的严格:反代不是应用级常驻的。Mirasim 为每次 agent run 起一个代理,
run 结束就dispose()。所以光开着 Mirasim 不够 —— 得有一个正在跑的 agent 会话,
代理才存在。详见限制。
问题
Mirasim 是个 Electron 应用,它把 Claude Code 包在自己的反代后面:启动 agent 会话时,往子进程注入
ANTHROPIC_BASE_URL=http://127.0.0.1:<proxyPort>
ANTHROPIC_AUTH_TOKEN=mirasim-relay-managed-credential
这个本地端点会把请求转发到官方 relay,并在转发时换上你账号的真实托管凭证。
问题是这套注入只对它自己拉起的进程生效。你在终端里直接敲 claude、或者让 Zed 通过 ACP
拉起 Claude,走的都是原生登录态,跟 Mirasim 的额度和记录完全是两条线。
这个仓库把那条链路接出来。
排查思路
值得单独写出来的是过程,而不是结论 —— 结论就三个脚本。
1. 先看 env,确认本地端点存在
在 Mirasim 拉起的会话里 env | grep -i anthropic,能看到 ANTHROPIC_BASE_URL 指向127.0.0.1 的某个高位端口,ANTHROPIC_AUTH_TOKEN 是个占位符字符串(不是真 key)。
占位符这一点很关键:说明鉴权不是由客户端负责的,本地端点会自己换凭证。
2. 确认本地端点不做鉴权
curl http://127.0.0.1:<port>/v1/messages \
-H 'content-type: application/json' -H 'x-api-key: whatever' \
-d '{"model":"...","max_tokens":8,"messages":[{"role":"user","content":"hi"}]}'
拿 x-api-key: whatever 就能拿到正常回复 —— 它忽略传入的 token,只认「请求来自本机」。
所以任何本机进程指过去就能用,这是整个方案成立的前提。
3. 找上游反代
lsof 定位监听方是应用主进程,再去 app bundle 里 grep 编译进去的常量:
lsof -nP -iTCP:<port>
grep -ohE 'https://[a-zA-Z0-9._-]+' /Applications/Mirasim.app/Contents/Resources/server.cjs \
| sort -u | grep -i relay
得到 https://mirasim-relay.mirofish.ai,以及解析逻辑:环境变量 RELAY_BASE_URL / RELAY_URL
优先,否则用编译进去的常量(release 版写死,只有 dev build 能覆盖)。
完整链路:
claude / Zed ACP
↓ ANTHROPIC_BASE_URL
127.0.0.1:<proxyPort> ← Mirasim 主进程,在这里换上真实托管凭证
↓
https://mirasim-relay.mirofish.ai
↓
上游模型服务
4. 试过但放弃的路:直连 relay
拿配置文件里的托管 token 直接打 relay,401 invalid user token。
一开始我以为是 token 过期(它确实有 exp,靠 refreshToken 续期)。但等应用刷新出一个还有
47 分钟才过期的新 token 再试,依然 401 —— 所以过期不是原因。回去翻 bundle 才看到真正的门槛:
grep -ohE "'x-mirasim-[a-z0-9-]+'" server.cjs | sort -u
# x-mirasim-device x-mirasim-nonce x-mirasim-sig x-mirasim-ts x-mirasim-token …
relay 要的是签名请求 —— 时间戳 + nonce + 设备标识 + HMAC 签名。光有 bearer token 没用。
要直连就得把它的签名协议重新实现一遍,既脆弱又没必要。
顺带一个结论:不要去调登录后端的 /auth/refresh。服务端一旦轮换 refreshToken,应用自己存的
那份就作废了,代价是被强制重新登录。为了省一个进程依赖去换「可能把你踢下线」的风险,不划算。
所以最终方案是走本地端点,签名和凭证刷新都继续交给应用 —— 代价是必须让 Mirasim 有活着的会话。
三个设计决策
端口必须动态发现
proxyPort 是运行时分配的,而且跟着 agent run 的生命周期走:
// server.cjs 里的形状(反混淆后)
finally { await proxy.dispose(); }
return { exitCode, sessionId, proxyPort: proxy.port, … }
同时开两个会话就有两个端口,会话结束端口就消失,应用重启全变。所以任何写死端口的配置(包括写进settings.json 的 env)都会失效。
判别哪个端口是反代,用 GET /v1/models:反代返回 JSON 模型列表,应用自己的 web UI 端口返回
HTML,shell 端点返回 401 bad shell token。零 token 开销,比发一个真 prompt 去试要好。
(早期版本假设「应用启动时会建一个常驻代理,端口号最小的那个活得最久」—— 这是错的,压根没有
常驻代理。现在只是按端口号顺序取第一个能用的。)
不用 pgrep
第一版用 pgrep -f Mirasim 找主进程,结果只匹配到 helper 进程,主进程匹配不上(macOS 上pgrep -f 读不到它的完整 argv)。改成从 lsof 的监听套接字反推,一步到位,顺便直接拿到端口。
不用 npx -y 拉 ACP 适配器
Zed 的 registry 条目是用 npx 按需拉 @agentclientprotocol/claude-agent-acp。包装脚本里照抄
这个做法会踩两个坑:npx 的缓存目录在受限环境下可能写不进去(ENOENT),以及每次启动都要付
一次包解析开销。改成 npm install --prefix 装到固定位置、直接 exec node <bin>。
还有一条硬约束:ACP 的 stdout 是 JSON-RPC 通道,包装脚本往 stdout 写任何一个字节都会把协议
弄坏。所有诊断输出必须走 stderr。
安装
git clone https://github.com/<you>/mirasim-relay-bridge
cd mirasim-relay-bridge
./install.sh
装三个脚本到 ~/.local/bin,装 ACP 适配器到 ~/.local/share/mirasim-acp,然后打印 Zed 配置片段。
幂等,可反复跑。
| 脚本 | 作用 |
|---|---|
mirasim-relay-port |
打印反代端口,找不到则退出 1。另外两个脚本都调它 |
claude-mira |
包 claude CLI:发现端口 → 注入 env → exec claude "$@" |
claude-acp-mira |
包 ACP 适配器,给 Zed 用 |
用法
CLI
claude-mira # 交互式
claude-mira -p 'hello' # 一次性
claude-mira --model claude-opus-5 # 参数原样透传
MIRA_RELAY_PORT=62873 claude-mira # 手动指定端口,跳过发现
MIRA_WAIT_SECS=30 claude-mira # 等最多 30s 让代理出现
想让交互式 shell 里的 claude 默认走反代,加到 ~/.zshrc:
[[ -z ${MIRASIM_INSTANCE_KEY:-} ]] && alias claude='claude-mira'
那个守卫是为了别在 Mirasim 自己拉起的会话里套第二层(那边 env 已经注入好了,再发现一次可能挑到
别的会话的端口)。逃生口是 command claude(或 \claude),走原生登录态。
alias 只作用于交互式 shell,所以脚本、IDE 插件、其他工具里的 claude 调用不受影响 —— 这是有意的。
Zed
并进 ~/.config/zed/settings.json 的 agent_servers,重启 Zed:
"claude-mira": {
"type": "custom",
"command": "/Users/<you>/.local/bin/claude-acp-mira",
"args": [],
"env": {}
}
建议保留原来的 claude-acp registry 条目 —— 那是你的原生登录态逃生口,两个可以并存,在 agent
面板里自由切。
注意 Zed 的 agent_servers.env 只能写静态值,所以不能在那里直接写 ANTHROPIC_BASE_URL:
端口是动态的。包一层脚本正是为了这个。
验证
python3 tools/acp-smoke.py
真跑一轮 ACP 握手(initialize → session/new → session/prompt),期望看到:
initialize: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,...}}
session/new: {..."sessionId":"..."}
session/prompt: {..."stopReason":"end_turn","usage":{...}}
AGENT TEXT: ACP RELAY OK
STDERR: claude-acp-mira: → http://127.0.0.1:61855
卸载
rm ~/.local/bin/{mirasim-relay-port,claude-mira,claude-acp-mira}
rm -rf ~/.local/share/mirasim-acp
再删掉 ~/.zshrc 里的 alias 和 Zed settings.json 里的 claude-mira 条目。
限制
- Mirasim 里必须有一个正在跑的 agent 会话,不只是应用开着。反代跟着 agent run 走,run 结束
就dispose()。没有代理时脚本会明确报错退出(不静默失败),并提示怎么让它起来。 MIRA_WAIT_SECS=<n>可以让脚本等代理出现(轮询,默认 0 = 立刻失败)。适合「先启动 Zed
agent,再去 Mirasim 开会话」这种顺序。- 端口在启动时发现一次。Zed 的 ACP server 是长驻进程,如果它绑定的那个会话结束了,端口就没了,
这个 agent 也就断了 —— 需要在 Zed 里重启 agent / 开新 thread 重新发现。
(想彻底解决得自己起一个常驻转发器,每个请求重新解析当前端口。本仓库没做。) - 走这条链路的请求计入你的 Mirasim 配额,并会被 relay 按应用自身的记录设置记录下来。
- 适配器版本写在
install.sh的ACP_VERSION里,跟 Zed registry 的版本不会自动同步。
免责
这是互操作性工具:用你自己的账号、自己的额度,把你已经装在自己机器上的应用的本地端点接给
同机的其他客户端用。没有绕过任何计费或鉴权 —— 凭证始终由应用持有和刷新,脚本从不读取、复制或
存储任何凭证。
依赖的是 Mirasim 的内部实现细节(端口分配方式、本地端点行为),版本更新可能随时打破。
跟 Mirofish 和 Zed Industries 均无关联。
License
MIT
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi