sandbase-harness
sandbaseai
Local-first, self-hosted AI agent runtime and MCP bridge with sandboxed sessions, memory, credentials, audit/replay, and a local Console.
kira905/dsh-firstaid
Zero-dependency incident toolkit for long-running agent hosts: one entry point for stuck services, frozen UIs, rollbacks and data loss; emits a hand-off-ready on-site report. Read-only by default. | 零依赖急救台:起不来 / 假死 / 要撤销改动 / 数据被删四类现场压成一个入口,产出可整段交出去的体检报告
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:kira905/dsh-firstaid
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
出事时只想知道「跑哪个」——这个工具把「起不来 / 界面假死 / 要撤销改动 / 数据被删」四类现场压成一个入口, 产出一份可整段交给 AI 或同事的现场体检报告。
默认只诊断,不动手。 任何回滚/重启都是写动作,必须有人确认;而且没演练过的恢复脚本永远不会被列进「可一键执行」。
本工具是「ops-handoff-design」所述运维体系的一个实现, 设计依据(对应文档仓约定的固定四问):
_test/演练脚本即"自证可用"那一环设计稿里不写具体仓库地址(写死即死链),实现清单统一收在文档仓的组件索引表里——本仓只负责回链章节。
| 症状 | 它能回答的问题 |
|---|---|
| 服务打不开 / 起不来 | 守护进程在不在、端口有没有人听、进程活着吗、日志尾部报了什么 |
| 界面假死 / 一直转圈 | 是服务端真故障,还是浏览器侧连接池/GPU 争用(90% 是后者) |
| 刚改完的东西想撤销 | 最近几天改了什么、每条的备份还在不在、该跑哪个恢复脚本、要不要停机 |
| 数据 / 文件被删 | 哪些备份介质真的在位、可恢复范围是什么(不承诺不可恢复的东西) |
| 不知道怎么了 | 一键生成体检包(进程/端口/日志尾/最近改动/备份可用性/演练状态) |
反过来,它不做这些事:不替你回滚、不替你重启、不猜你的环境、不在没备份时假装有备份。
firstaid/
├─ firstaid.mjs 症状入口(5 个症状 → 现场体检报告)
├─ timeline.mjs 改动时间轴(变更流水 ∩ 回滚点 ∩ 演练记录)
├─ drill.mjs 恢复点演练编排器(L1 沙箱 / L2 真实)
├─ rollback-index.mjs 回滚脚本索引(症状 → 脚本 → 影响面 → 前置条件 → 是否演练过)
├─ firstaid-launch.ps1 Windows 交互菜单外壳(可选;纯 CLI 用户不需要)
├─ lib/
│ ├─ common.mjs 公共库(进程/端口/HTTP 探活、报告渲染、落盘)
│ └─ runtime-config.mjs 路径与特征解析层(**所有环境相关的值都从这里来**)
├─ scripts/verify-source.mjs 发布前自检(脱敏扫描 + 注入规则 + 扫描器自检)
├─ _test/ 单测与夹具(全部在临时沙箱里跑)
└─ examples/ 配置示例
第 1 步 · 需要 Node
node --version # 需要 Node 18+(本工具只用内置模块,无构建、无 npm install)
第 2 步 · 取到本地
git clone https://github.com/kira905/dsh-firstaid.git firstaid
# 国内镜像:git clone https://gitee.com/kira905/dsh-firstaid.git firstaid
cd firstaid
node firstaid.mjs --list # 看到症状菜单就算装好了
第 3 步 · 告诉它你的环境(可选但强烈建议)
工具默认按「数据根 = ~/.dsh」工作。如果不对,用环境变量指一下:
# 例:数据根、工作区、被诊断服务的端口
export FIRSTAID_HOME=/srv/myservice/.data
export FIRSTAID_WORKSPACE=/srv/myservice
export FIRSTAID_PORT=3080
node firstaid.mjs --symptom 1
Windows PowerShell:
$env:FIRSTAID_HOME = '<盘符>:\myservice\.data'
node .\firstaid.mjs --symptom 1
不想每次设环境变量,就把它们写进自己的启动脚本——本工具不读任何私有的全局配置文件,这是故意的(避免"配置漂移"导致的误诊断)。
node firstaid.mjs --symptom 1 # 起不来
node firstaid.mjs --symptom 3 --days 14 # 想撤销改动(看 14 天时间轴)
node firstaid.mjs --symptom 5 --json # 一键体检包(含机读 JSON 段)
报告落在日志目录(默认 <数据根>/logs),同时写一份 .log 流水。屏幕输出与报告内容一致(--quiet 只打判据与建议)。
未演练的恢复脚本不会被列为「可一键执行」——这条纪律是本工具设计的核心,不是可选项。
# L1 沙箱级(默认档,任何恢复点都该过):只验证「备份源确实能还原目标」,并强断言目标文件 hash 前后一致
node drill.mjs --point "<回滚点目录>" --target "<目标文件>"
# L2 真实级:真造坏 → 真跑恢复 → 真验活 → **无论成败都还原现场**
node drill.mjs --point "<回滚点目录>" --target "<目标文件>" --restore "<恢复脚本.mjs>" --live --yes
演练记录 drill-record-<ts>.md/.json 落在回滚点目录内,时间轴与索引会自动读到它。
node rollback-index.mjs --write # 生成 <落地目录>/README-回滚索引.md
node rollback-index.mjs --check # 巡检:索引与实况是否一致(不一致退出码 3)
node rollback-index.mjs --refs <脚本名> # 归档/改名前的全量引用扫描
数据根解析顺序:FIRSTAID_HOME > DSH_HOME > ~/.dsh(空串/纯空白一律视为未设置,逐级回落)。
| 用途 | 环境变量 | 默认值 |
|---|---|---|
| 数据根 | FIRSTAID_HOME / DSH_HOME |
~/.dsh |
| 工作区(工具链/文档树) | FIRSTAID_WORKSPACE / DSH_WORKSPACE |
未配置(不猜) |
| 日志目录 | FIRSTAID_LOGS |
<数据根>/logs |
| 变更流水目录 | FIRSTAID_CHANGELOG |
<数据根>/changelog |
| 回滚点根 | FIRSTAID_ARCHIVE_ROOT |
<数据根>/_archive |
| 恢复脚本落地目录 | FIRSTAID_LANDING |
<数据根>/landing |
| 冷盘(离线介质)上的回滚点镜像 | FIRSTAID_ROLLBACK_ROOT / ROLLBACK_POINTS_ROOT |
<数据根>/rollback-points |
| 被诊断服务端口 | FIRSTAID_PORT |
3080 |
| 本机其它实例端口(附带检查) | FIRSTAID_OTHER_PORTS |
空 |
| 守护进程命令行特征(正则字符串) | FIRSTAID_GUARD_PATTERN |
guard |
| 守护进程状态文件 / 日志 | FIRSTAID_GUARD_STATE / FIRSTAID_GUARD_LOG |
<数据根>/guard/{guard-state.json,guard.log} |
| 启动日志文件名模式(正则字符串) | FIRSTAID_LOG_PATTERN |
任意 *.log |
| 冷盘落点表 | FIRSTAID_COLD_ROOTS |
空(未配置时报告如实写「未配置」,不假装有备份) |
| 镜像/备份落点表 | FIRSTAID_MIRROR_ROOTS |
工作区 backups/ + 数据根 _archived-sessions/ |
| 备份来源清单(症状 4 的建议) | FIRSTAID_BACKUP_SOURCES |
三条中性提示 |
| 影响面分类规则 | FIRSTAID_IMPACT_RULES |
内置中性表(配置/依赖/数据/启动链/文档/工具链/会话) |
| 停机 / 重启判据 | FIRSTAID_STOP_PATTERNS / FIRSTAID_RESTART_PATTERNS |
内置中性默认 |
| 设备标记词表(时间轴标题剥离) | FIRSTAID_DEVICE_TAGS |
空(只剥离「通用方括号前缀」与「设备:xxx」形态) |
| 历史线索目录(自愈报告等) | FIRSTAID_KB_DIR |
<数据根>/repair-knowledge |
多值写法:分隔符 , ; | 三者等价,例如 FIRSTAID_COLD_ROOTS='archive=<盘符>:\rollback-points;keys=<盘符>:\keys'。
也接受 JSON 数组形态([{"name":"...","root":"..."}])。
时间轴读变更流水文件:<变更流水目录>/YYYY-MM.md。解析口径固定:
- <HH:MM>(- 09:00 xxx),续行自动归上一条;| 或半角 | 分隔;_archive\<点名>\ 形态的路径 → 视为备份线索;restore-*.cmd|mjs|ps1 → 视为回滚入口。没有流水也能用:直接把它们放在回滚点根下,工具会按目录名的时间戳兜底识别,并标注为「未登记改动」。
| 平台 | 状态 | 说明 |
|---|---|---|
| Windows 10/11 | ✅ 实测 | 进程枚举用 PowerShell CIM(回落 wmic / tasklist),端口用 netstat |
| macOS | ⚠️ 未实测 | 路径与配置层是跨平台写法,但进程/端口探针依赖 Windows 命令,需要适配 |
| Linux | ⚠️ 未实测 | 同上 |
| 依赖 | 要求 |
|---|---|
| Node.js | 18+(实测 24.x) |
| npm 包 | 零(全部用 node 内置模块;单测有运行时拦截层,加载第三方包即报错) |
tasklist / netstat / PowerShell CIM 都是 Windows 命令;其他平台会走「无法判定」分支并如实标注(不会静默当成 0)。_test/fixtures.mjs 的夹具条目落在 2026-09-10 附近;用默认 --days 7 跑时间轴时,若当前日期离夹具日期超过 7 天,B17/B18/B21/C19 四条会失败。这是用例的时间脆性,不是判据坏了(探针见交付报告:把窗口放大到 30 天后立即判红)。--run 只做预检,不执行回滚:本版本一律拒绝真执行(红线:只读诊断)。firstaid-launch.ps1),其余平台走命令行。rollback-index --refs 是浅层扫描:默认只扫工作区的 docs/、tools/ 与数据根,不做全盘搜索。# 语法 + 配置层 + 脱敏扫描(注入词表只走环境变量,源码里一个字都不写)
BUILD_MACHINE_NAMES='...' BUILD_USER_NAMES='...' node scripts/verify-source.mjs .
# 阳性对照:故意注入一个必然存在的串,必须报红(证明规则真的在跑)
node scripts/verify-source.mjs . --positive-control <一个确信存在的串>
# 单测(全部在系统临时目录里跑,不碰真实数据)
node _test/test-firstaid-run.mjs
node _test/test-drill.mjs
node _test/test-rollback-index.mjs
注入词表里的值只存在于你的命令行(或 CI secret),不会写进仓库。
本仓以 MIT 许可发布(全文见 LICENSE):你可以自由使用、修改、再分发,包括嵌入自己的闭源工具与 CI,只需保留版权与许可声明。
背景说明(透明化):本仓最初派生的草稿曾以 AGPL-3.0-only 作为占位(定稿前的临时状态)。该占位版本从未对外分发(未推送、未发布);对外首发的即是本 MIT 版本。三处口径(
LICENSE/README/package.json的license字段)已同步为 MIT。
| 项 | 内部版默认 | 本仓默认 | 怎么恢复内部版行为 |
|---|---|---|---|
| 数据根 | 固定工作区路径 | FIRSTAID_HOME > DSH_HOME > ~/.dsh |
设 DSH_HOME 或 FIRSTAID_HOME |
| 变更流水目录 | <工作区>/docs/变更流水 |
<数据根>/changelog |
设 FIRSTAID_CHANGELOG |
| 恢复脚本落地目录 | 桌面上的固定目录 | <数据根>/landing |
设 FIRSTAID_LANDING |
| 守护进程状态/日志 | 系统临时目录 | <数据根>/guard/ |
设 FIRSTAID_GUARD_STATE / FIRSTAID_GUARD_LOG |
| 其它实例端口 | 内置一串固定端口 | 空 | 设 FIRSTAID_OTHER_PORTS |
| 冷盘 / 镜像落点 | 按主机名硬编码盘符 | 空 / 由 home+工作区推出 | 设 FIRSTAID_COLD_ROOTS / FIRSTAID_MIRROR_ROOTS |
| 影响面分类、停机判据、设备标记 | 内含具体项目与主机名 | 中性默认表 | 用 FIRSTAID_IMPACT_RULES / FIRSTAID_STOP_PATTERNS / FIRSTAID_DEVICE_TAGS 注入 |
*恢复脚本自身、_archive 目录名、`restore-.{cmd,mjs,ps1}` 命名约定、时间轴解析口径 —— 这些是设计语义,未改。**
同属 DSH 生态的伴生组件,各自独立仓、独立版本、许可各自独立;它们都回链到同一份文档仓
ops-handoff-design
(Gitee 镜像 https://gitee.com/kira905/ops-handoff-design):
| 组件仓 | 做什么 | 与本组件的关系 |
|---|---|---|
dsh-firstaid |
零依赖急救台:起不来 / 假死 / 要撤销改动 / 数据被删 | 本仓 |
dsh-ecosystem-panel |
只读生态健康面板(六类体检一屏看完) | 日常与应急的分工:它回答「今天怎么样」(持续、只读、三色),本仓回答「现在坏了、下一步做什么」(一次性、可整段交出去的报告)——而且面板自己坏了的时候,本仓是它的兜底 |
dsh-diagnostic-tools |
依赖闭包 / 解耦体检 + 会话图片附件对账 | 同属"只诊断不动手",但场景不同:它做离线专项取证(升级前后主动跑),本仓做现场四类症状的分诊(已经出事时先跑) |
dsh-butler-archive |
会话归档管理(列表 / 预览 / 恢复 / 删除 + 可选自动归档) | 本仓「要撤销改动 / 数据被删」两类现场里,最常撞上的就是会话与归档面;本仓只负责如实标注恢复脚本演练过没有,不替谁回滚 |
dsh-session-title-live |
会话标题随对话实时刷新 + 回合边界状态前缀 | 本仓的体检报告要回答"最近哪些会话没走完",标题轴是那份清单可读性的来源 |
dsh-task-board-local |
自维护任务看板(卡片 = 一次真实会话 + 人工验收闸) | 反过来看:长跑体系的日常编排在它那儿,出事时它自己也可能卡住——本仓负责把现场压成一份能交出去的报告 |
组件之间没有代码依赖,也不共享运行时 —— 之所以互指,是因为它们回答的是同一类人的同一批问题 (长期在自有机器上跑 agent:装得下、找得到、看得见、查得清)。谁装谁不装,互不影响。
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: diagnostics。