openclaw-diag-cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -201
- package/bin/openclaw-diag.js +10 -6
- package/ocdiag/__init__.py +1 -1
- package/ocdiag/dispatcher.py +0 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,259 +1,133 @@
|
|
|
1
1
|
# openclaw-diag-cli
|
|
2
2
|
|
|
3
|
-
> OpenClaw / ArkClaw
|
|
3
|
+
> OpenClaw / ArkClaw 故障诊断 CLI。零依赖、只读、人和机器都能用。
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
无需 git clone,通过 npm 拉一份缓存即可(之后离线可用):
|
|
5
|
+
## 安装
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
#
|
|
11
|
-
npx openclaw-diag-cli
|
|
12
|
-
npx openclaw-diag-cli run gateway
|
|
13
|
-
npx openclaw-diag-cli run all --json | jq -s '.'
|
|
8
|
+
# 一次性运行(无需安装,npm 缓存后离线可用)
|
|
9
|
+
npx openclaw-diag-cli
|
|
14
10
|
|
|
15
|
-
# 装到 PATH
|
|
11
|
+
# 装到 PATH
|
|
16
12
|
npm install -g openclaw-diag-cli
|
|
17
|
-
openclaw-diag
|
|
18
|
-
openclaw-diag doctor # 检查环境是否就绪
|
|
19
|
-
openclaw-diag bundle gateway > gw.py # 生成单文件诊断脚本
|
|
13
|
+
openclaw-diag
|
|
20
14
|
```
|
|
21
15
|
|
|
22
|
-
依赖:Node 18
|
|
23
|
-
`python3` 并把参数透传给现有的 dispatcher,所以 `python3 diag/04_gateway.py`
|
|
24
|
-
和 `python3 bin/ocdiag run gateway` 仍然完全可用。
|
|
25
|
-
|
|
26
|
-
## 为什么存在
|
|
27
|
-
|
|
28
|
-
排查 OpenClaw 故障时面对的真实痛点:
|
|
29
|
-
|
|
30
|
-
- **数据散在多个角落**:session.jsonl 在 agents/ 下,配置在 openclaw.json,进程行为在 journalctl,cron 状态在 cron/jobs.json,模型耗时藏在 trajectory 里…… 手敲 jq + grep 组合费时且易漏。
|
|
31
|
-
- **`openclaw-diag.sh` 已成为 4391 行单体 bash**,里面塞着 10 段 heredoc 嵌入的 Python,难修改、难单测、难复用。
|
|
32
|
-
- **诊断脚本应该是"原子操作"**:每条数据有明确来源,每个模块解决一类问题,可以单独跑、可以组合管道、可以被自动化驱动。
|
|
33
|
-
|
|
34
|
-
这个仓库就是把那个 4391 行 bash 拆开重写——每个采集动作独立成一个 Python 脚本,按一组公理设计,让"采集 → 分析 → 上报"变成可推理的工程而不是手工活。
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## 设计公理(First Principles)
|
|
39
|
-
|
|
40
|
-
下面 6 条是**不可让步**的硬约束。所有目录结构、API、输出格式都从这 6 条推导出来。
|
|
41
|
-
|
|
42
|
-
### 1. 只读(Read-Only)
|
|
43
|
-
诊断脚本**永远不能**修改文件、写配置、重启服务。代价:再难拿的数据也要靠"读"获得;不允许走 `openclaw <subcmd>` 修改类入口。
|
|
44
|
-
**收益**:在生产环境、在排查事故现场、在客户机器上跑都安全。
|
|
16
|
+
依赖:Node 18+ 和 Python 3.8+。
|
|
45
17
|
|
|
46
|
-
|
|
47
|
-
**只用 Python 3.8+ 标准库**。不写 `requirements.txt`,不要 `pip install`。唯一例外:`croniter` 在 `06_cron_jobs.py` 中可选导入(缺失时退化到从历史 runs 推算间隔)。
|
|
48
|
-
**收益**:任何能跑 OpenClaw 的节点都能跑诊断(OpenClaw 自己依赖 Node.js,但诊断脚本不依赖 OpenClaw 装在 Python 端的任何包)。`git clone` 完直接 `python3 diag/04_gateway.py`。
|
|
18
|
+
## 五分钟上手
|
|
49
19
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
### 4. 可组合(Composable)
|
|
55
|
-
默认输出是人类可读文本(中文,带 emoji 装饰),加 `--json` 输出**结构化 JSON**。
|
|
56
|
-
- 单脚本:`{"module": "<id>", "status": "ok|error", "data": {...}}`
|
|
57
|
-
- `bin/ocdiag run all --json` 输出 **NDJSON**(每行一个模块的 JSON),可以 `... | jq -s '.'` 聚合,或者 `... | jq 'select(.module=="cron_jobs") | .data'` 抽取。
|
|
58
|
-
**推论**:进度信息走 stderr,永远不污染 stdout 的 JSON 流。
|
|
59
|
-
|
|
60
|
-
### 5. 数据可靠(Data Fidelity)
|
|
61
|
-
脚本输出的每个数字、每个状态都必须能溯源:
|
|
62
|
-
- 系统数据 → `subprocess.run(["free","-m"])`、`/proc/<pid>/environ`、`journalctl ...`
|
|
63
|
-
- OpenClaw 数据 → `~/.openclaw/openclaw.json`、`~/.openclaw/cron/jobs.json`、`~/.openclaw/agents/*/sessions/*.jsonl`
|
|
64
|
-
- 日志数据 → `/tmp/openclaw/openclaw-*.log`(按 mtime 取今日)
|
|
65
|
-
|
|
66
|
-
数据来源在文档里逐模块列清,不允许"看上去合理就行"。同一字段,文本输出和 JSON 输出必须**值一致**。
|
|
20
|
+
```bash
|
|
21
|
+
# 1. 看看能做什么(直接列出所有模块 + 常用命令)
|
|
22
|
+
openclaw-diag
|
|
67
23
|
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
- 单个数据源缺失(配置不存在、日志没生成、session 文件被删)**不能**抛异常,要明确报告"未找到"。
|
|
71
|
-
- 不要 swallow 异常变 silent:失败要在 stderr 留 traceback,rc 非 0。
|
|
24
|
+
# 2. 检查环境是否就绪
|
|
25
|
+
openclaw-diag doctor
|
|
72
26
|
|
|
73
|
-
|
|
27
|
+
# 3. 跑单个模块
|
|
28
|
+
openclaw-diag run gateway
|
|
74
29
|
|
|
75
|
-
|
|
30
|
+
# 4. 全部跑一遍(任一模块崩了不影响其他)
|
|
31
|
+
openclaw-diag run all
|
|
76
32
|
|
|
77
|
-
|
|
78
|
-
openclaw-diag
|
|
79
|
-
├── ocdiag/ 共享原语(公理 #2 推论:库小而稳)
|
|
80
|
-
│ ├── paths.py 路径常量 + 环境变量覆盖
|
|
81
|
-
│ ├── jsonlog.py OpenClaw JSON 日志解析(公理 #5)
|
|
82
|
-
│ ├── timeutil.py ISO/epoch 时间转换 + 人类友好格式化
|
|
83
|
-
│ ├── tokens.py fmt_tokens / percentile / human_size
|
|
84
|
-
│ ├── sensitive.py 密钥/Token 脱敏(公理 #1 的延伸:输出也要安全)
|
|
85
|
-
│ ├── output.py 双模式输出(人类可读 + JSON)— 公理 #4 实现
|
|
86
|
-
│ ├── recent_logs.py 发现今日更新日志
|
|
87
|
-
│ ├── cli.py 公共 argparse(--config / --log-dir / --json)
|
|
88
|
-
│ └── dispatcher.py bin/ocdiag 复用的入口
|
|
89
|
-
│
|
|
90
|
-
├── diag/ 诊断模块(公理 #3:每个能独立跑)
|
|
91
|
-
│ ├── 01_sys_health.py 系统健康(DNS/网络/CPU/内存/磁盘/IO/进程/时间同步)
|
|
92
|
-
│ ├── 02_environment.py OpenClaw 基础环境(版本一致性、Gateway 进程 env)
|
|
93
|
-
│ ├── 03_configuration.py openclaw.json 展平(脱敏后)
|
|
94
|
-
│ ├── 04_gateway.py Gateway 状态(WS 生命周期 + 错误码统一视图)
|
|
95
|
-
│ ├── 05_recent_errors.py 近期错误(多日志聚合 + journalctl + tool 错误)
|
|
96
|
-
│ ├── 06_cron_jobs.py 定时任务(jobs.json + state + runs/ 三源合并)
|
|
97
|
-
│ ├── 07_performance.py 模型/工具性能(慢调用 Top 20 / E2E 延迟 / Cache)
|
|
98
|
-
│ ├── 08_sessions.py Session 数据(六维分析 + Stuck 探测)
|
|
99
|
-
│ ├── 09_plugin_diag.py 插件诊断(一致性 + ERROR/WARN + Hook + Channel + DNS)
|
|
100
|
-
│ └── 10_shell_history.py Shell 历史(高危命令 + openclaw 命令)
|
|
101
|
-
│
|
|
102
|
-
├── tools/ 单点深挖工具(不是采集,是分析特定对象)
|
|
103
|
-
│ ├── oc_session_trace.py 跟踪一条 user 消息从进入到响应的完整时间轴
|
|
104
|
-
│ └── oc_session_extract.py 把 session jsonl 导出为可读格式(含 reset/bak/deleted 全状态)
|
|
105
|
-
│
|
|
106
|
-
└── bin/
|
|
107
|
-
└── ocdiag 可选的总入口(list / run <id> / run all)
|
|
33
|
+
# 5. 输出结构化 JSON
|
|
34
|
+
openclaw-diag run gateway --json
|
|
108
35
|
```
|
|
109
36
|
|
|
110
|
-
|
|
37
|
+
## 诊断模块(10 个)
|
|
111
38
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
公理 #5 的具体落地——下游用任何字段都能查到它从哪来:
|
|
115
|
-
|
|
116
|
-
| 模块 | 数据来源 |
|
|
39
|
+
| 模块 | 看什么 |
|
|
117
40
|
|---|---|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## 用法
|
|
134
|
-
|
|
135
|
-
### 最小用法(独立脚本)
|
|
41
|
+
| `sys_health` | DNS / 网络 / CPU / 内存 / 磁盘 / IO / 进程 / 时间同步 |
|
|
42
|
+
| `environment` | OpenClaw 版本一致性、Gateway 进程环境变量 |
|
|
43
|
+
| `configuration` | `openclaw.json` 展平(敏感字段已脱敏) |
|
|
44
|
+
| `gateway` | Gateway 进程、端口、24h 启停、WS 生命周期、错误码 |
|
|
45
|
+
| `recent_errors` | 应用日志 / journalctl / session 工具调用错误聚合 |
|
|
46
|
+
| `cron_jobs` | 定时任务状态、连续失败、调度漂移、静默检测 |
|
|
47
|
+
| `performance` | 模型/工具耗时 P50/P95、慢调用 Top 20、E2E 延迟、Cache 命中率 |
|
|
48
|
+
| `sessions` | Session 总览、活跃度、Stuck 探测 |
|
|
49
|
+
| `plugin_diag` | 插件状态一致性、ERROR/WARN、Hook 异常、Channel、外部依赖 DNS |
|
|
50
|
+
| `shell_history` | 高危命令、openclaw 命令、最近操作 |
|
|
51
|
+
|
|
52
|
+
## 单点工具(2 个)
|
|
136
53
|
|
|
137
54
|
```bash
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
python3 diag/04_gateway.py # 直接跑,零配置
|
|
141
|
-
python3 diag/04_gateway.py --json # 同样的数据,JSON 格式
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 总入口(可选)
|
|
55
|
+
# 跟踪一条用户消息从进入到响应的完整时间轴
|
|
56
|
+
openclaw-diag tools/oc_session_trace.py <session-uuid> --msg-index 0
|
|
145
57
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
python3 bin/ocdiag run gateway # 跑 04_gateway
|
|
149
|
-
python3 bin/ocdiag run all # 全部跑一遍(任一模块崩了不影响其他)
|
|
150
|
-
python3 bin/ocdiag run all --skip performance,sessions # 跳过重模块
|
|
58
|
+
# 导出 session 为可读格式(支持 reset / bak / deleted 全状态)
|
|
59
|
+
openclaw-diag tools/oc_session_extract.py <session-uuid> --summary
|
|
151
60
|
```
|
|
152
61
|
|
|
153
|
-
|
|
62
|
+
## 常见配方
|
|
154
63
|
|
|
155
64
|
```bash
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
npx openclaw-diag-cli run all --skip performance,sessions
|
|
159
|
-
npx openclaw-diag-cli doctor # 检查 Node/Python/ocdiag/OpenClaw
|
|
160
|
-
npx openclaw-diag-cli bundle 04_gateway > standalone-gateway.py
|
|
161
|
-
```
|
|
65
|
+
# 找出哪个 cron 任务在连续失败
|
|
66
|
+
openclaw-diag run cron_jobs --json | jq '.data.jobs[] | select(.status!="ok")'
|
|
162
67
|
|
|
163
|
-
|
|
68
|
+
# 看哪个模型的 P95 延迟最高
|
|
69
|
+
openclaw-diag run performance | grep -A1 "P95"
|
|
164
70
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
python3 diag/06_cron_jobs.py --json | jq '.data.jobs | length'
|
|
168
|
-
|
|
169
|
-
# 2) run all NDJSON → 聚合为单文档
|
|
170
|
-
python3 bin/ocdiag run all --json 2>/dev/null | jq -s '.' > report.json
|
|
71
|
+
# 哪些插件今天有 ERROR
|
|
72
|
+
openclaw-diag run plugin_diag --json | jq '.data.plugin_errors | to_entries[] | select(.value.error_count > 0)'
|
|
171
73
|
|
|
172
|
-
#
|
|
173
|
-
|
|
74
|
+
# 把所有诊断聚合成单个 JSON 报告
|
|
75
|
+
openclaw-diag run all --json 2>/dev/null | jq -s '.' > report.json
|
|
174
76
|
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
| jq 'select(.module=="cron_jobs") | .data.jobs[] | {name, success_rate}'
|
|
77
|
+
# 找出有 stuck session 的事件
|
|
78
|
+
openclaw-diag run sessions --json | jq '.data.stuck_sessions'
|
|
178
79
|
```
|
|
179
80
|
|
|
180
|
-
|
|
81
|
+
## 离线机器:bundle 出单文件
|
|
181
82
|
|
|
182
83
|
```bash
|
|
183
|
-
#
|
|
184
|
-
|
|
84
|
+
# 在有网的机器
|
|
85
|
+
openclaw-diag bundle gateway > standalone-gateway.py
|
|
185
86
|
|
|
186
|
-
#
|
|
187
|
-
|
|
188
|
-
python3
|
|
87
|
+
# scp 到目标机器(只需要 Python 3.8+,无需安装任何东西)
|
|
88
|
+
scp standalone-gateway.py prod-server:/tmp/
|
|
89
|
+
ssh prod-server "python3 /tmp/standalone-gateway.py --json"
|
|
189
90
|
```
|
|
190
91
|
|
|
191
|
-
|
|
92
|
+
`bundle` 会把脚本和它依赖的共享代码合并成一个 self-contained `.py`,零依赖。
|
|
192
93
|
|
|
193
|
-
|
|
94
|
+
## 配置覆盖
|
|
194
95
|
|
|
195
|
-
|
|
96
|
+
诊断别人机器或容器时,无需改代码:
|
|
97
|
+
|
|
98
|
+
| 环境变量 | 默认值 | 说明 |
|
|
196
99
|
|---|---|---|
|
|
197
100
|
| `OPENCLAW_HOME` | `~/.openclaw` | OpenClaw 主目录 |
|
|
198
101
|
| `OPENCLAW_CONFIG` | `$OPENCLAW_HOME/openclaw.json` | 配置文件 |
|
|
199
102
|
| `OPENCLAW_LOG_DIR` | `/tmp/openclaw` | 日志目录 |
|
|
200
103
|
| `OPENCLAW_SESSIONS` | `$OPENCLAW_HOME/agents` | Session 根 |
|
|
201
|
-
| `OPENCLAW_SERVICE_FILE` | `~/.config/systemd/user/openclaw-gateway.service` | systemd 服务单元 |
|
|
202
|
-
|
|
203
|
-
也可以用 `--config /path/to/openclaw.json --log-dir /path/to/logs` 覆盖单个参数。
|
|
204
104
|
|
|
205
|
-
|
|
105
|
+
也可以用 `--config /path/to/file --log-dir /path/to/logs` 覆盖单次。
|
|
206
106
|
|
|
207
|
-
##
|
|
107
|
+
## 退出码
|
|
208
108
|
|
|
209
109
|
| rc | 含义 |
|
|
210
110
|
|---|---|
|
|
211
|
-
| 0 |
|
|
212
|
-
| 1 | 模块运行成功但报告 `status: "error"
|
|
213
|
-
| 2 |
|
|
111
|
+
| 0 | 模块成功 |
|
|
112
|
+
| 1 | 模块运行成功但报告 `status: "error"`(数据源缺失等) |
|
|
113
|
+
| 2 | 模块崩溃(已隔离,不影响其他模块) |
|
|
214
114
|
|
|
215
|
-
|
|
115
|
+
## 设计原则
|
|
216
116
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
## 扩展:加一个新诊断模块
|
|
220
|
-
|
|
221
|
-
遵循公理即可:
|
|
222
|
-
|
|
223
|
-
1. 新建 `diag/11_my_check.py`,shebang `#!/usr/bin/env python3`
|
|
224
|
-
2. 顶部 docstring 说明:**采集什么 + 数据来源 + 输出含义**
|
|
225
|
-
3. `sys.path.insert(0, str(Path(__file__).resolve().parent.parent))` 接入共享库
|
|
226
|
-
4. `from ocdiag import cli, output, paths` 拿到统一基础设施
|
|
227
|
-
5. `parser = cli.build_common_parser(...); args = parser.parse_args()`
|
|
228
|
-
6. `out = output.init("my_check", json_mode=args.json, ...)`
|
|
229
|
-
7. 业务逻辑——文本输出用 `out.item / out.evidence / out.section`,JSON 数据用 `out.set_data("key", value)`
|
|
230
|
-
8. 流式读 JSONL(`for line in open(...)`),不能 `.read().split('\n')`
|
|
231
|
-
9. 子进程调用必须带 `timeout`
|
|
232
|
-
10. 数据源缺失要明确报告"未找到",不抛异常
|
|
233
|
-
|
|
234
|
-
注册到 `bin/ocdiag` 只需在 `ocdiag/dispatcher.py:MODULES` 列表加一行。
|
|
235
|
-
|
|
236
|
-
---
|
|
237
|
-
|
|
238
|
-
## 不做的事(反模式)
|
|
239
|
-
|
|
240
|
-
| 不做 | 原因 |
|
|
117
|
+
| | |
|
|
241
118
|
|---|---|
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
246
|
-
|
|
|
247
|
-
| 不引入 jq 子进程 | Python 自带 json,更可控 |
|
|
248
|
-
| 不内嵌 Python 在 bash heredoc 里 | 这就是我们要替代的旧形态 |
|
|
249
|
-
|
|
250
|
-
---
|
|
119
|
+
| **只读** | 永远不修改文件、不重启服务 |
|
|
120
|
+
| **零依赖** | 仅 Python 3.8+ 标准库 |
|
|
121
|
+
| **故障隔离** | 单模块崩溃不带崩 `run all` |
|
|
122
|
+
| **数据可靠** | 每个字段都能溯源 |
|
|
123
|
+
| **可组合** | 文本 + JSON 双输出,stderr 与 stdout 分流 |
|
|
251
124
|
|
|
252
|
-
|
|
125
|
+
详细设计 → [docs/DESIGN.md](docs/DESIGN.md)(公理推导、目录结构、扩展指南)
|
|
253
126
|
|
|
254
|
-
|
|
127
|
+
## 反馈
|
|
255
128
|
|
|
256
|
-
|
|
129
|
+
- Issues: https://github.com/wujiaming88/openclaw-diag-cli/issues
|
|
130
|
+
- 来源:从 4391 行的 `openclaw-diag.sh` 拆分重写
|
|
257
131
|
|
|
258
132
|
## License
|
|
259
133
|
|
package/bin/openclaw-diag.js
CHANGED
|
@@ -238,15 +238,19 @@ function main() {
|
|
|
238
238
|
const argv = process.argv.slice(2);
|
|
239
239
|
|
|
240
240
|
if (argv.length === 0) {
|
|
241
|
+
const py = findPython();
|
|
242
|
+
if (!py) pythonNotFound();
|
|
241
243
|
console.log(`openclaw-diag v${PKG.version} — OpenClaw / ArkClaw 诊断 CLI`);
|
|
242
244
|
console.log('');
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
console.log(' npx openclaw-diag-cli doctor 检查环境是否就绪');
|
|
246
|
-
console.log(' npx openclaw-diag-cli --help 查看完整帮助');
|
|
245
|
+
const dispatcher = path.join(REPO_ROOT, 'bin', 'ocdiag');
|
|
246
|
+
spawnSync(py.cmd, [dispatcher, 'list'], { stdio: 'inherit' });
|
|
247
247
|
console.log('');
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
console.log('常用命令:');
|
|
249
|
+
console.log(' openclaw-diag run gateway 跑单个模块');
|
|
250
|
+
console.log(' openclaw-diag run all 全部模块');
|
|
251
|
+
console.log(' openclaw-diag doctor 检查环境');
|
|
252
|
+
console.log(' openclaw-diag --help 完整帮助');
|
|
253
|
+
process.exit(0);
|
|
250
254
|
}
|
|
251
255
|
|
|
252
256
|
const head = argv[0];
|
package/ocdiag/__init__.py
CHANGED
package/ocdiag/dispatcher.py
CHANGED
package/package.json
CHANGED