统一管理本地开发服务的 守护进程(daemon) + 一套 asvc CLI。人和 agent 共用同一套命令。
解决的问题:开发时 agent 会帮你启动服务,你自己也会启动,导致端口冲突、进程难管理。 本工具让人和 agent 都通过同一个 daemon 操作服务:
- 启动即注册:只有一个
asvc start。带-c给命令就自动注册并启动,按名去重不重复拉起。 - 人(
asvc start <name> -c "..."):前台运行,像直接敲npm run dev一样实时刷日志,Ctrl-C 停。 - agent(
asvc start <name> -c "..." -d):加-d后台启动、跑完返回拿退出码。 - 任何人都能
asvc logs/asvc list/asvc restart/asvc stop操作同一批服务。 - daemon 按服务名去重:同名服务只有一个实例,agent 和你都不会把它启动两遍 → 不再撞端口。
同一个 asvc start,靠 -d 区分前台/后台:
| 命令 | 谁用 | 行为 |
|---|---|---|
asvc start <name> -c "..." |
人 | 前台:占住终端实时刷日志,像原始命令;Ctrl-C 停止服务 |
asvc start <name> -c "..." -d |
agent | 后台:启动后立即返回,带退出码(成功 0 / 启动失败 1) |
为什么是 CLI 而不是 MCP:agent(如 Claude Code)本来就有 shell,直接跑
asvc命令最简单—— 不用配 MCP server、不用单独的协议,人和 agent 认知统一,看到的输出也一样。
服务进程是 daemon 的子进程,不是某个终端 shell 的子进程。
你在终端 asvc start web 前台跑着(或在另一个终端 asvc logs web -f 跟随)时,
agent 在另一头 asvc restart web,daemon 只是把底层进程换了一个,
你的终端不会断——只会看到一行 » restarting... 然后新日志继续刷。
(注意:只有你自己按 Ctrl-C 才会停服务并退出;agent 的 restart 不会让你的终端退出。)
人的终端 ──┐
(asvc CLI) │ ┌─────────────┐ ┌── 服务进程 web (pid A)
├─ IPC ─▶│ daemon │─────▶├── 服务进程 api (pid B)
agent ──┘ (sock) │ (单实例) │ └── ...
(asvc CLI) └─────────────┘
npm install -g @homeant/asvc装完 asvc / asvc-daemon 就在 PATH 上,agent 和你都能直接敲 asvc。
daemon 会在首次执行任意 asvc 命令时自动拉起,无需手动启动。
# zsh:加到 ~/.zshrc
eval "$(asvc completion zsh)"
# bash:加到 ~/.bashrc
eval "$(asvc completion bash)"补全覆盖子命令、各命令的 flag,以及服务名——asvc restart <TAB> 会实时列出
daemon 里已注册的服务(daemon 未运行时安静地不补全,不会触发自动拉起)。
从源码开发时改用本地构建:
npm install && npm run build
npm link # 把本地构建的 asvc 装到全局 PATH# 启动即注册(前台,人用):像直接敲原始命令,实时刷日志,Ctrl-C 停止服务
asvc start web -c "npm run dev" --port 3000 # cwd 默认当前目录
asvc start web # 已注册过则可省略 -c
# 启动即注册(后台,agent 用):加 -d,跑完即返回、带退出码
asvc start web -c "npm run dev" --port 3000 -d
asvc start api -c "go run ." --cwd /path/api --env KEY=VAL --autorestart -d
asvc list # 列出所有服务及状态
asvc logs web # 看 web 最近 200 行日志
asvc logs web -f # 持续跟随(agent 重启服务也不断开)
asvc logs web -n 500 # 看最近 500 行
asvc restart web # 重启(前台/跟随的终端都不会断)
asvc stop web # 停止(保留定义,可再启动)
asvc rm web # 移除(先停后删)
asvc daemon status # 看 daemon 是否运行
asvc daemon stop # 停 daemon(会先关闭所有服务)start 参数:-c/--cmd(命令,经 shell 执行)、-w/--cwd(默认当前目录)、
-p/--port(仅展示/排查)、-e/--env KEY=VAL(可多个)、--autorestart、-d/--detach(后台)。
register / start / restart 在拉起进程后会观察约 1 秒确认进程没有立刻崩溃,再返回:
- 进程稳定运行 → 打印成功,退出码 0。
- 进程在窗口内退出(最常见就是端口被别的进程占了,
EADDRINUSE)→ 打印最近日志, 识别到EADDRINUSE时给出「请换端口或先停掉占用方」的提示,并以退出码 1 退出。
这样不依赖事先声明 port:无论端口被谁占(你手动起的、孤儿进程、还是另一个服务),
只要进程因此启动失败,走 Bash 的 agent 都能直接从非零退出码 + 输出判定失败和原因,
而不是拿到一个乐观的 “running” 再去翻日志。asvc start(前台)同样:启动即失败会打印提示并以非零码退出。
npm 包内自带一个 Claude Code skill:skill/asvc/SKILL.md。
它教 agent 在该启动/重启/看日志时改用 asvc,并强调后台启动必须带 -d、按退出码判成败。
安装(软链到全局 skills 目录):
npm install -g @homeant/asvc
ln -sfn "$(npm root -g)/@homeant/asvc/skill/asvc" ~/.claude/skills/asvc从源码开发时,把软链指向仓库即可(编辑仓库即同步生效):
ln -sfn "$PWD/skill/asvc" ~/.claude/skills/asvc之后 agent 在「启动服务 / 重启 / 看日志 / 排查起不来」时会自动参考该 skill。
也可以在项目 CLAUDE.md 里加一句兜底约定:
启动/重启/停止开发服务一律用 `asvc`,agent 用 `asvc start <name> -c "<命令>" -d`
(务必带 -d 后台),重启 `asvc restart <name>`,看日志 `asvc logs <name> -n 200`。- 你在 web 目录
asvc start web -c "npm run dev" --port 3000→ 前台跑起来,实时刷日志。 (或让 agentasvc start web -c "npm run dev" --port 3000 -d后台起,你再asvc logs web -f看。) - agent 改完代码执行
asvc restart web→ 你的终端看到» restarting...,随后新日志继续,终端不断开。 - 你不想看了按 Ctrl-C → 服务停止、终端退出(和原始命令一致)。
- 你随时
asvc restart web/asvc stop web手动接管。
- 全局单实例,家目录默认
~/.asvc(可用ASVC_HOME覆盖)。 - socket:
$ASVC_HOME/daemon.sock(可用ASVC_SOCKET覆盖)。 - 每个服务日志落盘
$ASVC_HOME/logs/<name>.log,内存保留最近 2000 行供快速查询。 - 服务定义动态注册,无需手写配置文件;注册表持久化在
$ASVC_HOME/registry.json, daemon 重启后自动恢复定义(不自动拉起进程,asvc start <name>即可再启动,无需重新带-c)。
- IPC:unix domain socket + newline-delimited JSON(请求/响应 + 事件推送)。
- 进程组:
spawn(..., { shell:true, detached:true }),停止时kill(-pid)终止整组, 先SIGTERM,5s 未退再SIGKILL。 - 启动后 1s 稳定窗口:进程在窗口内退出即判失败,返回真实状态而非乐观 running。
- TypeScript +
commander,无其他运行时依赖。