开发工具 · 文章

做了一个看本地端口的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

image.png

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 冒出来之前,让我少背一次命令。

版权声明

本文内容版权归作者或相关权利人所有。转载、引用或其他使用请遵循相应授权条款,并保留本文链接。

本文链接:https://xuyi.dev/2026-09-02-gyjasj