用途
fleet-stop 用于终止一次 CocoFleet 运行中的所有正在运行的进程。它会向每个实例发送 SIGTERM 信号,等待最多 10 秒进行优雅关闭,若进程仍未退出则发送 SIGKILL 强制终止,并更新状态文件、生成停止记录。
主要能力
- 检查 .cocoplus/ 目录是否存在,未初始化时给出提示并停止。
- 解析参数 $fleet stop [fleet-id],缺少 fleet-id 时输出用法提示并停止。
- 读取 .cocoplus/fleet/[fleet-id]-state.json,找不到时输出提示并停止。
- 对状态为 running 的每个实例,从 state.json 读取 PID,通过 Bash 工具调用跨平台 Node.js 发送 SIGTERM。
- 最多等待 10 秒进行优雅关闭,每秒检查一次进程存活状态;超时后发送 SIGKILL。
- 更新 state.json:将状态设为 stopped,并记录 completed_at 时间戳。
- 写入 .cocoplus/fleet/[fleet-id]-stop-record.md 停止记录,包含 Fleet ID、停止时间以及每个已停止实例的表格(实例、最后状态、最后检查点、PID)。
- 输出停止结果摘要,包括终止的进程数量和停止记录路径。
使用方式
命令格式:$fleet stop [fleet-id]
执行流程:
- 验证 .cocoplus/ 是否存在,否则输出 "CocoPlus not initialized in this directory. Run $pod init to begin." 并停止。
- 解析 fleet-id,若缺失则输出 "Usage: $fleet stop [fleet-id]" 并停止。
- 读取 .cocoplus/fleet/[fleet-id]-state.json,若不存在则输出 "Fleet state not found for [fleet-id]." 并停止。
- 对每个 running 实例,读取 PID 并发送 SIGTERM: node -e "try{process.kill([pid],'SIGTERM')}catch(e){}"
- 等待最多 10 秒,每秒检查存活,超时后发送 SIGKILL: node -e " const pid=[pid]; let waited=0; const iv=setInterval(()=>{ try{process.kill(pid,0);waited++;} catch(e){clearInterval(iv);} if(waited>=10){try{process.kill(pid,'SIGKILL')}catch(_){}clearInterval(iv);} },1000);"
- 更新 state.json:status = "stopped",completed_at = [timestamp]。
- 写入停止记录文件 .cocoplus/fleet/[fleet-id]-stop-record.md。
- 输出:"Fleet [fleet-id] stopped. [N] processes terminated. Stop record: .cocoplus/fleet/[fleet-id]-stop-record.md."
适用场景
- 需要安全、可审计地停止整个 CocoFleet 运行。
- 需要确保所有相关进程被终止,避免遗留孤儿进程或日志文件。
- 需要留下停止记录作为审计证据,记录何时以及为何停止。
限制与注意事项
- 必须在已初始化 CocoPlus 的目录中运行(存在 .cocoplus/)。
- 必须提供有效的 fleet-id,且对应的状态文件存在。
- 不应在发送信号前更新 state.json,状态必须反映真实情况,只有确认进程死亡后才标记为 stopped。
- 不应跳过 SIGTERM 超时后的 SIGKILL,否则忽略 SIGTERM 的进程会继续运行并产生孤儿日志文件。
- 不应跳过停止记录,否则缺少停止时间和原因的追踪。
退出标准
- 所有先前处于 running 状态的实例在 .cocoplus/fleet/[fleet-id]-state.json 中状态变为 stopped。
- .cocoplus/fleet/[fleet-id]-stop-record.md 存在并列出已停止实例。
- 该 fleet 的所有 PID 均不再存活(通过 node -e "process.kill([pid],0)" 返回错误验证)。