关于 Binky
Binky 为每个 Git worktree 创建独立本地环境,协调端口、环境变量、服务进程以及共享数据库或其他服务的资源切片。它面向需要在同一台机器上并行运行编码 Agent 或分支的团队。
主要功能
- 01为每个 worktree 隔离端口、URL 和环境
- 02为服务配置独立资源切片
- 03启动、停止并检查 worktree 服务
- 04准入控制与后台进程监管
- 05命令行与实时 worktree 面板
适用场景
- 01并行运行编码 Agent,避免端口冲突
- 02为每个分支分配独立数据库切片
- 03向测试或 Agent 提供 worktree URL 和环境
MCP 相关
`binky mcp`通过 stdio 运行 MCP 服务,提供`up`、`down`、`status`、`env`和`verify`工具,并通过与命令行相同的守护进程执行操作。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
5收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| root | string | 否 | |
| group | string | 否 | |
| worktree | string | 是 |
详细介绍
Binky
每个 git worktree 都有一个隔离的本地环境——端口、环境变量、数据库、URL。
这样,一群编码 Agent 就能在一台机器上并行运行,互不冲突。
docker-compose 是为一次运行一个进程而构建的。Binky 则是为同时运行十五个进程而构建的。
状态:alpha。 核心功能已端到端可用——worktree 隔离、端口、适配器、进程监管、协调器、准入控制、代理、HTTPS。配置格式已稳定;CLI 仍可能调整。
问题
你让三个 Agent 处理同一个仓库。第二个运行 pnpm dev,结果遇到 EADDRINUSE。
第三个运行迁移,删除了第一个正在读取的表。你开始手动编辑
.env 文件,现在你成了调度器。
Binky 就是调度器。每个 Agent 运行一次 binky up <branch>,就能获得自己的端口、自己的
数据库和自己的 URL——全部来自同一份已提交的配置。
安装
需要 Python 3.11+ 和 git,支持 Linux 或 Windows——Binky 目前在这两个平台上经过实际测试。 macOS 目前还不是支持目标平台;欢迎为其移植并充分测试,作为一项贡献。
uv tool install binky # or: pipx install binky
binky --version
也可以从克隆的仓库安装,以运行尚未发布的 main 分支或参与开发:
git clone https://github.com/andreisilva1/binky && cd binky
uv tool install . # or: pipx install .
需要驱动程序的适配器会作为额外依赖提供——安装用于 Redis 的 Binky 时,不会拉取 boto3:
uv tool install "binky[postgres]" # or [redis], [mysql], [mongodb], [s3], [kafka]
uv tool install "binky[all-adapters]" # the whole matrix
快速入门
已经有 docker-compose.yml?从它开始:
binky init # writes a binky.toml you can read and edit
binky check # validate it
也可以手动编写——这就是完整的配置:
[project]
name = "acme"
[services.web]
cmd = "pnpm dev --port ${self.port}"
url = "web" # → web.<worktree>.acme.localhost
depends_on = ["api"]
env = { API_URL = "${api.url}" }
[services.api]
cmd = "go run ./cmd/server"
url = "api" # asking for a URL is what asks for a port
depends_on = ["db"]
env = { DATABASE_URL = "${db.url}" }
[services.db]
adapter = "postgres" # a private database per worktree
admin_url = "${env.POSTGRES_ADMIN_URL}"
migrate = "make migrate"
seed = "make seed"
clone_from = "golden" # migrate+seed once, then copy per worktree
然后并行启动 worktree:
$ binky up login-fix
api pid 16560 :4846
web pid 29732 :4504
✓ up: login-fix (2 service(s))
$ binky up checkout-v2
api pid 21768 :4137
web pid 46336 :4605
✓ up: checkout-v2 (2 service(s))
$ binky status
acme
checkout-v2 [running]
api :4137
web :4605
login-fix [running]
api :4846
web :4504
两个分支、四个进程、四个无需手动挑选的端口。把某个 worktree 的连接信息交给需要它的人或程序——shell、Agent、测试运行:
$ binky env login-fix --dotenv
WEB_URL=http://web.login-fix.acme.localhost
API_URL=http://api.login-fix.acme.localhost
以及其余的生命周期操作:
binky down login-fix # pause (keeps files + ports); `up` again is instant
binky remove login-fix # destroy it (refuses if there are uncommitted changes)
没有任何内容是硬编码的,也不会在运行时进行发现:每个进程启动时都已知晓完整的端口映射,包括自身和依赖项的端口。
务必牢记一条规则。 服务声明
url或ports后才会获得端口——端口必须显式请求。两者都未声明的服务不会获得端口;对于工作进程或一次性任务,这完全正确,但如果你随后在其cmd中写入${self.port},就会掉进陷阱。未知的占位符会原样保留,而不会被清空,因此字面量${self.port}会传到 shell,进程会在自身的参数解析阶段失败。服务出现这种情况时,binky check会发出警告。
运行 N 个世界的成本
我们的主张是,N 个隔离世界的成本不应该是单个世界的 N 倍。不是口说无凭——
benchmarks/worlds.py 位于此仓库中,可以复现下表(postgres:16,每个世界 200,000 行数据,
在 Windows 11 上使用 Docker Desktop):
| worlds | RAM:每个世界一个容器 | RAM:Binky | 时间:每个世界一个容器 | 时间:Binky |
|---|---|---|---|---|
| 1 | 150 MiB | 174 MiB | 6s | 4s |
| 2 | 298 MiB | 182 MiB | 11s | 6s |
| 4 | 596 MiB | 228 MiB | 20s | 10s |
| 8 | 1197 MiB | 295 MiB | 40s | 16s |
第 8 个世界如果使用单独的容器,需要额外 150 MiB 和 4.8s;如果使用 Binky,则只需额外 17 MiB 和 1.7s。
也请看第一行。只有一个世界时,Binky 反而更慢——它需要运行一个服务和一个暂时无人使用的黄金模板,直到第二个世界出现才开始收回成本。而且磁盘空间并没有优势:
CREATE DATABASE ... TEMPLATE 会复制文件,而不是采用写时复制,因此每个世界仍然都是完整的数据副本。Binky 去掉的是每个世界对应的服务,而不是每个世界对应的数据量。它也不会降低你自己的应用进程成本——每个工作树运行一个开发服务器,无论哪种方式成本都一样。
python benchmarks/worlds.py --sweep 1,2,4,8 --rows 200000
哪些情况下不需要 Binky
这点值得直说,因为表格的第一行已经说明了:
- 一次只使用一个分支。 如果你从不同时运行两个世界,Binky 就只是一个你用不上的守护进程和配置 文件。等第二个 Agent 出现时再用它。
- 所有内容已经在容器中运行,而且运行得很好。 Binky 的优势在于移除每个世界对应的服务。 如果你的 compose 技术栈足够小,能轻松容纳 N 个副本,那就没有什么可节省的。
- 你需要真正的隔离,而不只是本地隔离。 Binky 会以你的身份在你的机器上运行命令。它不是沙箱——请参阅安全性。
适配器
适配器会从一个共享服务中划分出一个隔离的切片——例如数据库、键前缀或存储桶——而不是为每个工作树运行一个服务。共 16 个名称、10 种实现,并且每一种都在 CI 中针对真实服务进行了测试。
| 切片 | 适配器 |
|---|---|
| 数据库 | postgres · mysql · mariadb · percona · mongodb · clickhouse |
| 键前缀 | redis · valkey |
| 存储桶 / 前缀 | s3 · minio |
| 虚拟主机 / 主题前缀 | rabbitmq · kafka · redpanda |
| 集合 | qdrant |
| 索引前缀 | elasticsearch · opensearch |
协议兼容的名称会共用同一种实现,而不会复制一份——valkey 是 Redis 适配器,percona 是 MySQL 适配器,opensearch 是 Elasticsearch 适配器。
clone_from:迁移和填充数据只需付费一次
使用 clone_from 后,第一次运行 binky up 会构建一个黄金模板——完成配置、迁移和数据填充——之后的每个工作树都从该模板复制而来。模板根据 migrate 和
seed 命令的哈希值确定,因此更改其中任意一项都会自动重建模板。
适用于服务可在服务端复制切片的情况(postgres、mysql、mariadb、percona、mongodb)。如果服务不支持,或者克隆因环境问题失败——例如客户端二进制文件太旧,无法通过身份验证,或缺少授权——Binky 会回退到为该工作树执行配置、迁移和数据填充,并记录 clone_fallback。正确性从不依赖克隆,只有速度才依赖。
还有一个值得注意的细节:哈希涵盖的是迁移和数据填充的命令,而不是这些命令调用的文件。修改命令运行的脚本不会重建模板——删除 ~/.binky/golden/ 下的标记文件即可强制重建。
编写自己的适配器
两个层级,一套契约。两者都实现相同的四个动词:
| 动词 | 作用 |
|---|---|
provision(slice) | 创建切片(CREATE DATABASE ...) |
resolve(slice) | 返回其地址 → ${db.url} |
teardown(slice) | 销毁它 |
capabilities() | 返回它支持的功能,例如 {"clone": true} |
进程内适配器是此仓库中的 Python 类,按名称发现。外部适配器是任何语言编写的可执行文件,通过 PATH 上的 binky-adapter-<name> 发现,并通过一次性子进程 + JSON 协议驱动:
$ binky-adapter-filestore provision <<< '{"slice": "acme_login-fix", "config": {}}'
{"ok": true}
$ binky-adapter-filestore resolve <<< '{"slice": "acme_login-fix", "config": {}}'
{"ok": true, "address": "file:///tmp/binky-filestore/acme_login-fix"}
Python 开发者可以直接使用该协议 — binky.adapter_sdk.run() 会将普通适配器类桥接到该协议。完整且无需依赖的示例见 examples/adapters/filestore.py,仅有 30 行。
命令
| 命令 | 作用 |
|---|---|
binky init [dir] [--force] | 根据 docker-compose.yml + .env 写入初始 binky.toml |
binky check [path] | 验证 binky.toml 并指出易踩的坑 |
binky up <name> [--group g] | 创建或使用分支的工作树、分配端口、配置资源并启动服务 |
binky reload <name> [--group g] | 原地重启 — 停止后,在相同端口和数据上重新启动 |
binky down <name> | 停止工作树的进程(保留其文件和端口) |
binky remove <name> [--force] | 销毁工作树 — 停止进程、删除其切片并移除文件 |
binky status [.] | 列出工作树(按项目分组);. = 仅当前项目 |
binky env <name> [--dotenv] | 输出工作树公开的连接信息,供你或 Agent 使用 |
binky curl <name> <service> [path] | 通过代理调用服务,并自动填入 Host 标头(-X/-d/-H/-i/-f) |
binky verify <name> | 运行项目的 agents.verify 检查;失败时返回非零退出码 |
binky logs <name> | 输出工作树捕获的服务日志 |
binky gc | 清理崩溃后遗留的、配置了一半的孤立适配器切片 |
binky config | 显示机器级全局设置(RAM/磁盘预留、代理端口) |
binky reserve [--ram %] [--disk gb] | 设置 Binky 永久保留的空闲 RAM/磁盘空间 |
binky port [--proxy p] [--https p] | 设置代理/HTTPS 端口 |
binky dashboard | 显示所有工作树的实时 TUI |
binky doctor | 诊断安装和守护进程;出现问题时返回非零退出码 |
binky metrics | 以 JSON 格式输出守护进程的即时指标 |
binky mcp | 运行 MCP 服务,让 Agent 能将 Binky 的动词作为工具调用 |
binky daemon start|stop|status | 管理后台守护进程 |
面向 Agent
binky mcp 通过 stdio 使用 MCP,提供 up、down、status、env 和 verify — 与 CLI 调用相同的动词,使用相同的守护进程。
在 Claude Code 中,只需一行即可完成配置:
claude mcp add binky -- binky mcp
任何其他支持该协议的客户端,也可通过配置块使用同一个 stdio 服务 — 各处配置相同,只是文件不同:
{ "mcpServers": { "binky": { "command": "binky", "args": ["mcp"] } } }
若要在不安装任何内容的情况下运行,可将其交给 uvx — 使用 claude mcp add binky -- uvx --from "binky[mcp]" binky mcp,或用配置块表示:"command": "uvx"、"args": ["--from", "binky[mcp]", "binky", "mcp"]。如果项目的工作树需要配置适配器切片,也要添加相应的额外依赖,例如
binky[mcp,postgres](若要使用完整的适配器矩阵,则用 binky[mcp,all-adapters])。
Binky 已发布到 MCP Registry,名称为
io.github.andreisilva1/binky,因此支持注册表的客户端可以在其中发现并安装它。
工具列表和面向 Agent 的使用指南,请参阅 AGENTS.md。
工作原理
一个全局唯一的守护进程位于工作树与机器之间:
flowchart TB
CLI["agents · CLI · MCP"] -->|"up · down · status"| D(("binky daemon<br/>machine-global"))
D --> ADM{"admission<br/>RAM · disk · max_parallel"}
ADM -.->|"no room → queue / suspend"| Q["waiting"]
ADM -->|"admit"| W["worktree<br/>git worktree per name"]
W --> P["ports<br/>whole map before start"]
P --> R["adapters<br/>isolated slice per worktree"]
R --> S["supervisor<br/>depends_on · health · logs"]
S --> X["proxy<br/>svc.worktree.domain → port"]
D -.->|"reads · writes"| ST[("~/.binky<br/>SQLite + settings")]
- 工作树 — 按名称创建或使用一个 git 工作树。
- 端口 — 全局分配器会在任何进程启动前为其分配端口,并注入完整的映射,因此不会发生冲突(
${self.port}、${api.url}……)。 - 资源 — 适配器会为每个工作树划分出一个隔离的资源片段,并在移除时将其清理。
- 监督 — 按
depends_on顺序启动服务,等待健康检查通过,并收集日志。 - 代理 — 将
<service>.<worktree>.<domain>路由到正确的端口(也包括 WebSocket/HMR)。 设置https = true后,如果 Caddy 位于 PATH 中,Binky 会通过 Caddy 使用本地 CA 提供 TLS。 - 准入 — 使用 FIFO 队列,仅在机器有足够资源时启动工作树:运行中的工作树少于
max_parallel,并且有足够的可用 RAM(保留一定百分比)和可用磁盘空间(固定的 GB 余量)。如果运行中的工作树群之后超过 RAM 保留额度,守护进程会暂停最新的工作树以缓解压力;待资源充足后,再按公平顺序恢复。使用binky reserve调整保留额度;使用binky config查看。
状态存储在 ~/.binky/ 中(一个 SQLite 注册表和一个设置存储,在升级时原地迁移)。所有操作都在本地运行,Binky 绝不会主动联系开发方。
示例
- examples/hello — 最简单的项目。
- examples/rag — 向量搜索,每个工作树都有自己的嵌入式语料库。
这是共享数据库反而有害的场景:两个 Agent 重新索引,一个执行
TRUNCATE。 - examples/adapters/filestore.py — 完整的外部适配器。
安全
Binky 会以你的身份在你的机器上运行你的命令。它是面向单用户工作站的开发工具,不是多租户沙箱——binky.toml 与 Makefile 一样值得信任;在你不信任的仓库上运行 binky up,就如同在该仓库上运行 make。SECURITY.md 中说明了威胁模型以及明确不在防护范围内的事项。
这里值得重申的一条规则是:绝不要将凭据直接写入已提交的 binky.toml。请使用 ${env.NAME} 或 env_file。如果你这样做,binky check 会发出警告,而 binky init 绝不会写入凭据。
遇到问题?
TROUBLESHOOTING.md 涵盖了实际遇到的问题——被外部程序占用的端口、忽略 $PORT 的开发服务器、过时的黄金模板、无法解析的 *.localhost。请从 binky doctor 开始排查。
开发
python -m venv .venv && . .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -e ".[dev,all-adapters]"
pytest
ruff check src tests examples benchmarks
除非指定真实服务器,否则适配器集成测试会跳过——BINKY_TEST_PG、BINKY_TEST_REDIS、BINKY_TEST_MYSQL、BINKY_TEST_MONGO、BINKY_TEST_S3、BINKY_TEST_RABBITMQ、BINKY_TEST_KAFKA、BINKY_TEST_QDRANT、BINKY_TEST_CLICKHOUSE、BINKY_TEST_ELASTICSEARCH。CI 会提供所有这些服务器。
参与贡献
欢迎提交错误报告、新适配器和 PR——请参阅 CONTRIBUTING.md 了解如何设置开发环境,并参阅 ARCHITECTURE.md 了解内部结构。
许可证
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。