{"_id":"@bluemanta/portdash","_rev":"4-bb878bb0aad9615b3758a1730e73bc5a","name":"@bluemanta/portdash","dist-tags":{"latest":"0.4.2"},"versions":{"0.1.0":{"name":"@bluemanta/portdash","version":"0.1.0","keywords":["dev-server","localhost","ports","process-manager","dashboard","cli","memory","watchdog"],"author":{"name":"Chris Wang"},"license":"MIT","_id":"@bluemanta/portdash@0.1.0","maintainers":[{"name":"bluemanta","email":"wccshow@gmail.com"}],"homepage":"https://github.com/bluemanta/portdash","bugs":{"url":"https://github.com/bluemanta/portdash/issues"},"os":["darwin","linux"],"bin":{"portdash":"portdash.js"},"dist":{"shasum":"82dbad04b65427a70d7ad04e495747276309678c","tarball":"https://registry.npmjs.org/@bluemanta/portdash/-/portdash-0.1.0.tgz","fileCount":5,"integrity":"sha512-0OJ4P0jqDzpjr6b2cxIhy2LCfiJWgZmwtagBH9JnlSCXJoG6SLIAi1CHSd31eavedZBws1On0CayHk4i5fXedg==","signatures":[{"sig":"MEUCIF2a56RhLcmeNvjliJDgk9Me1z8PYak8O80AsdRh609+AiEA2sa6v4Z4kCiH9lC3uwTXR0PCeIgeonqXAV1uy1sDbRo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47118},"main":"portdash.js","engines":{"node":">=16"},"gitHead":"47c81aa9aab52bd1fbc3057ab4518359de1f66da","_npmUser":{"name":"bluemanta","email":"wccshow@gmail.com"},"repository":{"url":"git+https://github.com/bluemanta/portdash.git","type":"git"},"_npmVersion":"11.6.2","description":"A visual control panel for local dev servers — see every listening port, control it, and freeze (not kill) runaway memory before it takes the machine down.","directories":{},"_nodeVersion":"24.13.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/portdash_0.1.0_1788051902568_0.37528538851641025","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@bluemanta/portdash","version":"0.1.1","keywords":["dev-server","localhost","ports","process-manager","dashboard","cli","memory","watchdog"],"author":{"name":"Chris Wang"},"license":"MIT","_id":"@bluemanta/portdash@0.1.1","maintainers":[{"name":"bluemanta","email":"wccshow@gmail.com"}],"homepage":"https://github.com/bluemanta/portdash","bugs":{"url":"https://github.com/bluemanta/portdash/issues"},"os":["darwin","linux"],"bin":{"portdash":"portdash.js"},"dist":{"shasum":"dc740abd198a44a09f8f194bb9b41854dd61f2a4","tarball":"https://registry.npmjs.org/@bluemanta/portdash/-/portdash-0.1.1.tgz","fileCount":5,"integrity":"sha512-9A9JEelkdwlf2EnfVPZiZMfkMxX6OKDIQaCIaMMBiYeKMzr6Jc9zNnn+cg//o4WsV/QkXFZyPO2fjvl6l7M/Cw==","signatures":[{"sig":"MEUCIEpmCEP592bp7JjmmIN+7fn5j8bcVXhFh/fluTCs+e1zAiEArpE63pTEFxs50X2aADp9kBN2UXQ3mmC/zNknNzMTrx8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQC7BigXgNU3T9bjVFX+h/zMOyriwpDPLyMnRTPedtusvgIgCaZ2UVGi2fh1sBjGKCa6U8yaRdVzT76KmqMKDXBMihI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48702},"main":"portdash.js","engines":{"node":">=16"},"gitHead":"83cce5a653e552b178c96942aecb027ebce391dc","scripts":{"start":"node portdash.js"},"_npmUser":{"name":"bluemanta","email":"wccshow@gmail.com"},"repository":{"url":"git+https://github.com/bluemanta/portdash.git","type":"git"},"_npmVersion":"11.6.2","description":"A visual control panel for local dev servers — see every listening port, control it, and freeze (not kill) runaway memory before it takes the machine down.","directories":{},"_nodeVersion":"24.13.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/portdash_0.1.1_1788057161693_0.8629764809888869","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@bluemanta/portdash","version":"0.4.0","keywords":["dev-server","localhost","ports","process-manager","dashboard","cli","memory","watchdog"],"author":{"name":"Chris Wang"},"license":"MIT","_id":"@bluemanta/portdash@0.4.0","maintainers":[{"name":"bluemanta","email":"wccshow@gmail.com"}],"homepage":"https://github.com/bluemanta/portdash","bugs":{"url":"https://github.com/bluemanta/portdash/issues"},"os":["darwin","linux"],"bin":{"portdash":"portdash.js"},"dist":{"shasum":"d8f29184498983ea3013616cb839e5776e4b99ba","tarball":"https://registry.npmjs.org/@bluemanta/portdash/-/portdash-0.4.0.tgz","fileCount":5,"integrity":"sha512-LDnJjfUbCMH6g/c8oYWuIg19h7Ho+c/hVzYFpxO7RbCoUIDQMgarb55j7y9k8qosbRBtDSRk+W/AYWE24OyYCQ==","signatures":[{"sig":"MEYCIQDKtYYa/IIPqZKr8PCIOPZu1egEZFBaLABIpKcmw5m7YwIhAKr+llAmXcJcCT/Z2mTlJXkeOBngzh0UtFv2iTnuus/a","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQCwtLOZpladLnYElsg1/gKSGdQOVtj66U865/RY7fyvpgIhAPNaQ4TR4PsJp+nBFoGAH9z9QYTusEEb9rE5KFfETvqX","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62789},"main":"portdash.js","engines":{"node":">=16"},"gitHead":"a015e817e5aa39e328c72204d74aeba8f6c32569","scripts":{"start":"node portdash.js"},"_npmUser":{"name":"bluemanta","email":"wccshow@gmail.com"},"repository":{"url":"git+https://github.com/bluemanta/portdash.git","type":"git"},"_npmVersion":"11.6.2","description":"A visual control panel for local dev servers — see every listening port, control it, and freeze (not kill) runaway memory before it takes the machine down.","directories":{},"_nodeVersion":"24.13.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/portdash_0.4.0_1788062729784_0.9954414303040475","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"os":["darwin","linux"],"_id":"@bluemanta/portdash@0.4.2","bin":{"portdash":"portdash.js"},"bugs":{"url":"https://github.com/bluemanta/portdash/issues"},"dist":{"shasum":"07228b477bd75f273479f6b417830046b8a3890c","tarball":"https://registry.npmjs.org/@bluemanta/portdash/-/portdash-0.4.2.tgz","fileCount":5,"integrity":"sha512-ZbDMli1LMIupFzubXEwrhnn0M0G/YVoWWv0Pvus/aV5Vq4bGE279fOuRuEV190xrnLOwDU7XwhOXb7WSnrgvsA==","signatures":[{"sig":"MEUCIQCQTdKRyM3fzCFSb6gzm5l2uY7M/3LDrEdc0Sqh49SZiAIgCU/88lg83+x3XeyLbavu5iiQakbC/spI5WgQS8OOAQs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBYfGI5hs5DLGt70Ee4sR4aW0V+Te/YsQJwtKrPfTjVXAiEA4nWYVN2VHjJ4MDRjEcwG4ZsXpBgzfxdZBtWnlS/6KUE="}],"unpackedSize":65731},"main":"portdash.js","name":"@bluemanta/portdash","author":{"name":"Chris Wang"},"engines":{"node":">=16"},"gitHead":"f1bbfdb8aaddd62f29bb22b90dd7f68a549694c7","license":"MIT","scripts":{"start":"node portdash.js"},"version":"0.4.2","_npmUser":{"name":"bluemanta","email":"wccshow@gmail.com"},"homepage":"https://github.com/bluemanta/portdash","keywords":["dev-server","localhost","ports","process-manager","dashboard","cli","memory","watchdog"],"repository":{"url":"git+https://github.com/bluemanta/portdash.git","type":"git"},"_npmVersion":"11.6.2","description":"A visual control panel for local dev servers — see every listening port, control it, and freeze (not kill) runaway memory before it takes the machine down.","directories":{},"maintainers":[{"name":"bluemanta","email":"wccshow@gmail.com"}],"_nodeVersion":"24.13.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/portdash_0.4.2_1788091090638_0.9899352652616682"}}},"time":{"created":"2026-08-30T01:05:02.442Z","modified":"2026-08-30T11:58:10.915Z","0.1.0":"2026-08-30T01:05:02.717Z","0.1.1":"2026-08-30T02:32:41.768Z","0.4.0":"2026-08-30T04:05:29.866Z","0.4.2":"2026-08-30T11:58:10.740Z"},"bugs":{"url":"https://github.com/bluemanta/portdash/issues"},"author":{"name":"Chris Wang"},"license":"MIT","homepage":"https://github.com/bluemanta/portdash","keywords":["dev-server","localhost","ports","process-manager","dashboard","cli","memory","watchdog"],"repository":{"url":"git+https://github.com/bluemanta/portdash.git","type":"git"},"description":"A visual control panel for local dev servers — see every listening port, control it, and freeze (not kill) runaway memory before it takes the machine down.","maintainers":[{"name":"bluemanta","email":"wccshow@gmail.com"}],"readme":"# PortDash\n\n本地开发服务的可视化控制台。零依赖单文件，只用 Node 自带模块。\n\n[English](README.md)\n\n## 运行\n\n```bash\nnpx @bluemanta/portdash\n```\n\n或者克隆下来直接跑：\n\n```bash\ngit clone https://github.com/bluemanta/portdash.git\ncd portdash\nnode portdash.js\n```\n\n然后打开 http://localhost:7777\n\n也可以在访达里双击 `启动PortDash.command`。\n\n## 让看门狗常驻（可选）\n\n看门狗只在 PortDash 运行时才起作用。关掉它，你的 dev server 会继续跑——但没人看着。想让它常驻后台：\n\n```bash\nnpm install -g @bluemanta/portdash\nportdash --install-agent\n```\n\n这会装一个 macOS LaunchAgent，开机自启、异常退出后自动重启。它**以你自己的身份运行，不是 root**——发信号给你的进程需要同一个用户，而 root 的权限远超它的实际需要。卸载用 `portdash --uninstall-agent`。\n\n两个需要知道的坑：\n\n- 它会记下安装时那个 `node` 的路径。如果那是 nvm 管的版本，之后你把该版本删了，agent 就废了——重装一次，或者指向一个稳定的 Node。\n- **被冻住的服务是安静的。** SIGSTOP 之后进程不崩溃、不报错，只是不响应，如果你忘了后台有东西在管着会很困惑。服务无缘无故没反应时，去看 `~/.portdash/portdash.log`。\n\n## macOS 权限（完全磁盘访问、照片、通讯录……）\n\n如果一个服务你在终端里跑得好好的，通过 PortDash 启动却报 `Operation not permitted`，原因在这里。\n\nmacOS 把隐私权限授予**责任进程**——也就是启动链最顶端的那个 App，而不是真正干活的那个二进制。子进程继承这个身份。进程被 detach、自成进程组**不会**切断继承，唯一起作用的是链顶是谁。\n\n所以解法取决于 PortDash 自己是被谁启动的：\n\n| PortDash 的启动方式 | 责任进程 | 怎么办 |\n|---|---|---|\n| Terminal.app / iTerm | 该终端 | 不用管——直接继承你已经授给终端的权限 |\n| 别的 App（编辑器、AI agent、启动器） | 那个 App | 给那个 App 授权，或者改从终端启动 PortDash |\n| `--install-agent` | `node` 二进制本身 | 直接给 `node` 授权 |\n\n常驻 agent 模式下没有终端可继承，所以要授权给 Node 本身：\n系统设置 → 隐私与安全性 → 完全磁盘访问权限 → `+` → <kbd>⌘⇧G</kbd> → 填 `which node` 的输出。\n\n授权前需要权衡两点：\n\n- 这等于给你机器上**所有** Node 程序都开了这个权限，不只是 PortDash。授权面比大多数人预期的宽，而 macOS 在这里没有更细的粒度。\n- 用一个稳定的解释器路径。nvm、asdf 这类版本管理器每次升级都会换路径，授权不会跟着走。\n\n## 它能做什么\n\n- **看**：所有正在监听端口的进程，按项目归类；每个项目显示实时内存占用\n- **控**：启动 / 暂停 / 恢复 / 重启 / 停止，信号发给整个进程组，`npm run dev` 派生的子进程一起收\n- **暂停是真暂停**：SIGSTOP 挂起，进程冻在原地，内存和端口都保留，恢复（SIGCONT）是瞬间的\n- **日志**：由 PortDash 启动的服务，输出记在 `~/.portdash/logs/`，界面上直接看\n- **外部启动也认**：你在终端里手动跑的 dev server，只要工作目录对得上，也显示在对应项目下并可被停掉\n\n## 内存保护（防止某个服务把整台机器拖死）\n\n看门狗每 2 秒扫一次全系统进程表，五道闸门：\n\n| 闸门 | 触发条件 | 动作 |\n|---|---|---|\n| Node 堆上限 | 启动时注入 `NODE_OPTIONS=--max-old-space-size` | 让 node 自己 OOM 退出，而不是吃光系统内存 |\n| 单项目软上限 | 进程组内存 > `projectRssMB`（默认 4G） | SIGSTOP 冻结，保留现场，界面弹告警 |\n| 单项目硬上限 | > `hardRssMB`（默认 10G） | 直接 SIGKILL，不再客气 |\n| 系统水位 | 可用内存 < 12%，或 swap 用量 > 4G | 冻结当前最占内存的项目；如果元凶不是 PortDash 起的，只告警 |\n| 启动闸门 | 内存已紧张 / 60 秒内同一项目启动超过 3 次 | 拒绝启动，提示先看日志 |\n\n**冻结不是杀死**：进程还在，看完日志你可以「恢复」继续，或者「停止」收工。\n\n阈值都在 `~/.portdash/config.json` 的 `limits` 里改，单个项目还能在界面上单独设。\n嫌它管太宽就把 `limits.enabled` 设成 `false`。\n\n## 配置\n\n都在 `~/.portdash/` 下：\n\n| 文件 | 作用 |\n|---|---|\n| `config.json` | 扫描目录 `scanRoots`、界面端口 `uiPort`、扫描深度 `scanDepth`、内存限额 `limits` |\n| `projects.json` | 项目登记表。首次运行自动扫描生成，之后「重新扫描」只增不改 |\n| `state.json` | PortDash 拉起的进程记录 |\n| `token` | API 令牌，首次运行生成，权限 `0600` |\n| `portdash.log` | PortDash 自己的输出，超过 2M 自动轮转 |\n| `logs/` | 每个项目一个日志文件，启动时超过 5M 自动归档为 .old |\n\n启动命令是扫描时从 `package.json` 的 scripts 猜的（dev > start > serve），猜错了在界面上点「编辑」改掉，之后不会被覆盖。\n\n## 安全\n\n这个 API 能拉起进程，所以做了三层限制：`Host` 头必须是本地（挡 DNS 重绑定）、跨站 `Origin` 一律拒绝、所有 `/api/` 请求必须带上 `~/.portdash/token` 里的令牌（放在 `X-Portdash-Token` 头里）。前两层挡的是网页，令牌挡的是**本机其他进程**——常驻之后这一层才是关键。\n\n脚本调用的话：\n\n```bash\ncurl -H \"X-Portdash-Token: $(cat ~/.portdash/token)\" http://localhost:7777/api/state\n```\n\n## 注意\n\n- 关掉 PortDash 不会影响已经启动的服务，它们继续在后台跑；重开会重新认领它们\n- 看门狗只自动处置 PortDash 自己启动的进程，不会去动你手动跑的东西\n- pid 会被系统回收，所以一条记录只有在占用该 pid 的进程「年龄不小于记录本身」时才被信任，否则宁可丢弃，也不冒险对陌生进程发信号\n- 端口被占时它会等待重试而不是退出，而且只有抢到端口之后才启动看门狗，所以第二个实例不会和第一个抢着管同一批进程\n- 界面只监听 127.0.0.1，不对局域网开放\n","readmeFilename":"README.zh-CN.md"}