开发工具 · 文章
做了一个看本地端口的Mac菜单栏小工具
最近这段时间,Cursor、Claude Code、Codex 会同时帮我起一堆服务。Next 占 3000,Vite 占 5173,再开一个 Nest,旁边可能还有 Postgres。
编辑器关了,进程不一定走。
下一次启动,终端只丢一句 EADDRINUSE。于是我又切回去敲:
lsof -nP -iTCP:3000 -sTCP:LISTEN
kill …有时候 kill 完,口还在。有时候两个 node,分不清杀的是哪一个。有时候手快,差点动到数据库。
这个问题很小。它不值得开一个仪表盘,也不值得我每次都把命令从脑子里翻出来。但它一天能碰上好几次,而且每次都把注意力扯走。
所以我决定自己做一个。
已经有办法了,但都不是我想要的
我当然知道可以 alias 一条 lsof。活动监视器也能搜进程。Docker Desktop 里也能看见容器。
但这些入口都不在我真正卡住的那一瞬间。
我卡住的时候,人还在编辑器里,手还在键盘上,脑子里只记得「3000 又被占了」。我不想为了这件小事再开一个窗口,也不想在一长串 node 里猜哪一个才是刚才那个前端。
我想要的很窄:
常用的口就在眼前,空的还是被占着的,一眼能看出来。占着的那个要能告诉我是哪个项目,路径在哪。然后我再决定放不放。放完,口要真的空出来。
就这样。没有通知,没有仪表盘,没有任何要我「管理基础设施」的架势。
我不希望它变成又一个要配置的东西。没有 YAML,没有启动脚本,装上就能看口。设置里能改的东西很少:开不开机启动、刷多快、菜单栏上要不要显示数量、系统端口要不要看见。够用就停。
Berth

Berth 是一个只待在 macOS 菜单栏里的小工具。它看着你项目真正占用的本地端口,让你先看清楚是谁占着,再决定能不能放掉。
它不进 Dock。点一下图标,或者 ⌥⌘P,面板出来。看完就收回去。
常用的口可以钉在上面。绿点是空的,橙点是被占着的。我想看的就是这个:这个泊位现在有没有船。
3000、5173、8080、5432 这些,对我来说不是数字,是每天都会碰到的位置。把它们钉住,比每次去搜「谁占用了这个端口」更接近我实际的工作方式。
它不是又一张 lsof 表
lsof 给的是 PID 和 COMMAND。对我来说,真正要判断的不是「有一个 node」,而是「这是哪个项目的 node」。
所以 Berth 按项目归组,把进程、PID、路径摊开。两个 node 会变成两张卡片,而不是两行看起来差不多的命令。Next、Vite 这些,能看出来就看出来,当线索,不当卖点。
搜的时候我也希望像在终端里:跳到某个端口,打开 localhost,或者直接打 release 3000。如果一个效率工具本身还要我学一套新操作,它就已经开始降低效率了。
它底层还是在用 lsof。我没有想替换这个工具本身,只是不想再把「看一眼、决定、停掉、确认口已经空了」拆成四次手动操作。
停掉之前,我得知道停的是谁
一个能结束进程的东西,不能耍聪明。
点释放会先确认。强制结束在那儿,但只有我明确要的时候才会走。系统进程我尽量藏起来,launchd、WindowServer 这类根本不该出现在「一键释放」的路径上。数据库更是这样:Postgres、Redis、Mongo 没有那个最显眼的杀按钮,多一道警告。
杀错了,未保存的工作就没了。这不是功能,是责任。
所以它也不做自动重启,不扫描局域网,不管远程机器。只看本机 TCP 在听的那些口。UDP、出站连接、TIME_WAIT,我都没管。Ghost 进程也好,编辑器走了服务还挂着也好,我只处理这一层:这个口还在不在听。
做小一点,我才敢让它碰 kill。
产品可以简单,但不能草率。
为什么叫 Berth
Berth 是泊位。船停在那儿,占着一段岸线;开走了,这段岸线空出来。
本地端口也是这样。服务来了,口就被占着;走了,口应该干净。我想要的闭环就四个动作:看见谁占着,判断能不能杀,停掉,确认口已经空了。
名字想表达的是一张泊位图,不是一张表。一张表会把所有监听列给你看;一张泊位图只关心你真正在用的那些位置,空着也要看得见。
一个音节,好念,也好写成图标。
现在还很早
八月底才公开。现在是 0.1.1,安装包大概 4.4 MB,要 Apple Silicon,macOS 14 以上。没有 App Store,也没有账号。Intel 的机器我这版还顾不上。
没上架是因为默认不能开 App Sandbox,否则读不全别人的监听信息。这不是我更喜欢直售,是这个工具要看见别的进程,沙盒会把它变成瞎子。
更新走 Sparkle,面板里检查,下载完点一次「重启完成更新」。签名、公证、装订,这些是底线,不是卖点。一个能停掉进程的软件,得先让人觉得它不会偷偷干别的事。所以没有遥测,没有崩溃上报,也没有登录。
源码在 GitHub,Apache 2.0。和 Wane 不同,这次我没有加 Commons Clause。它就是一个给自己用的小工具,也希望别人能直接用、能改。现在还很粗糙,很多边角我自己用着才知道别扭。
最后
Berth 解决不了 vibe coding 把环境弄乱这件事。它只是在下一次 EADDRINUSE 冒出来之前,让我少背一次命令。
版权声明
本文内容版权归作者或相关权利人所有。转载、引用或其他使用请遵循相应授权条款,并保留本文链接。