openclaw-diag-cli 1.11.1 → 1.12.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/CHANGELOG.md +95 -0
- package/ocdiag/__init__.py +1 -1
- package/ocdiag/main.py +271 -99
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,100 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.12.1 — --help / list / examples output switched to English (2026-06-18)
|
|
4
|
+
|
|
5
|
+
`v1.12.0` made the help comprehensive but in Chinese. This patch translates
|
|
6
|
+
every custom help string surfaced by `--help` (top-level + each subcommand +
|
|
7
|
+
each collector/inspector), `openclaw-diag list`, and `openclaw-diag examples`
|
|
8
|
+
to clear English. argparse framework strings (`usage:`, `options:`, `-h, --help`)
|
|
9
|
+
were already English and are unchanged.
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
Only `ocdiag/main.py` + `tests/test_cli_help.py` are touched. Collectors /
|
|
14
|
+
inspectors business logic, collector `title` attributes, and report rendering
|
|
15
|
+
are untouched.
|
|
16
|
+
|
|
17
|
+
1. `_COMMAND_DESC` values translated to English; `_desc()` unchanged.
|
|
18
|
+
2. `_common_arguments` group renamed to `"global options"`; every flag
|
|
19
|
+
(`--config` / `--log-dir` / `--sessions-base` / `--openclaw-home` /
|
|
20
|
+
`--format` / `--json` / `--no-color` / `--unmask`) carries an English help
|
|
21
|
+
string. The `channel options` group + `--account` help are also English.
|
|
22
|
+
3. `trace` / `extract` / `panorama` parsers: per-command group headers
|
|
23
|
+
(`trace options`, `extract options`, `panorama options`), every flag's
|
|
24
|
+
help text, and the `Examples:` epilog blocks are all English. Description
|
|
25
|
+
line uses the English `_desc()` only — the Chinese collector `title` no
|
|
26
|
+
longer leaks into help.
|
|
27
|
+
4. `_print_help()` rewritten in English (intro / Usage / Health checks / Scan
|
|
28
|
+
diagnostics / Object diagnostics / Helper commands / Global options /
|
|
29
|
+
Exit codes / More).
|
|
30
|
+
5. `cmd_list` pretty + json outputs are fully English. The json `label` field
|
|
31
|
+
now mirrors the English description (no Chinese title leak).
|
|
32
|
+
6. `cmd_examples()` translated; example commands themselves are unchanged.
|
|
33
|
+
7. Per-collector `<id> --help` description switched from
|
|
34
|
+
`f"{coll.title} ({coll.id}) — {desc}"` to `f"{desc} ({coll.id})"` so the
|
|
35
|
+
Chinese title is never printed.
|
|
36
|
+
8. Error messages: `"Error: 未知 ..."` -> `"Error: unknown ..."` and the
|
|
37
|
+
"run `openclaw-diag list`..." hint translated.
|
|
38
|
+
|
|
39
|
+
### Tests
|
|
40
|
+
|
|
41
|
+
`tests/test_cli_help.py`: every assertion that pinned a Chinese substring is
|
|
42
|
+
swapped to its English equivalent. Test intent is preserved 1:1.
|
|
43
|
+
|
|
44
|
+
## v1.12.0 — --help 全面改造:命令/参数清晰说明 + 分组 + 退出码 (2026-06-18)
|
|
45
|
+
|
|
46
|
+
让 `--help`(顶层 + 每个子命令 + 每个 collector/inspector)对**用户和 agent 都
|
|
47
|
+
清晰、明确、直接**。每个命令、每个参数都有一句话说明,零猜测。
|
|
48
|
+
|
|
49
|
+
### 改动
|
|
50
|
+
|
|
51
|
+
唯一业务改动文件:`ocdiag/main.py`(外加 `tests/test_cli_help.py` 新增测试)。
|
|
52
|
+
不动 collectors/inspectors 业务逻辑、不动默认路径/参数 default 值。
|
|
53
|
+
|
|
54
|
+
1. 新增集中式命令描述表 `_COMMAND_DESC`(id → 一句话中文)+ helper `_desc(mid)`,
|
|
55
|
+
供 `list`、`<id> --help` 的 description、顶层 help 复用,未来新增 collector
|
|
56
|
+
缺失时回退空串、不报错。
|
|
57
|
+
2. `_common_arguments` 内全局参数放进 `argument group "全局选项 (global options)"`,
|
|
58
|
+
每个 flag 都补了清晰中文 help(`--config` / `--log-dir` / `--sessions-base` /
|
|
59
|
+
`--openclaw-home` / `--format` / `--json` / `--no-color` / `--unmask`),路径
|
|
60
|
+
类参数加 `metavar=PATH` 让 usage 行更整齐。`--account` 同样进 `"channel 选项"`
|
|
61
|
+
group。
|
|
62
|
+
3. `trace --help` 之前 `--no-trajectory` / `--no-log` / `--show-tool-metas` /
|
|
63
|
+
`--show-plugin-snapshot` / `--mask` 都是裸 flag,本次补齐每个的中文说明,
|
|
64
|
+
并把 trace/extract/panorama 的命令专属参数各自放进 `"trace/extract/panorama 选项"`
|
|
65
|
+
group。
|
|
66
|
+
4. 顶层 `_print_help()` 重写:工具简介 → 用法 → 体检命令 → 扫描类(动态从
|
|
67
|
+
registry 渲染,对齐排版)→ 对象诊断 → 辅助命令 → 全局选项 → 退出码
|
|
68
|
+
(0/1/2/3) → 更多。
|
|
69
|
+
5. `cmd_list` pretty 输出每项追加 `— 描述`;`list --format json` 在每项里加
|
|
70
|
+
`description` 字段,便于 agent / 脚本消费。
|
|
71
|
+
6. 各 collector parser 的 `description` 从 `f"{title} ({id})"` 改为
|
|
72
|
+
`f"{title} ({id}) — {_desc(id)}"`,`<id> --help` 顶部就能看到一句话简介。
|
|
73
|
+
|
|
74
|
+
### Tests
|
|
75
|
+
|
|
76
|
+
`tests/test_cli_help.py` 追加 8 个用例:
|
|
77
|
+
- 顶层 help 含工具简介 / 体检命令 / 扫描类 / 对象诊断 / 辅助命令 / 全局选项 /
|
|
78
|
+
退出码(含 0/1/2/3)等关键 section 标题。
|
|
79
|
+
- 顶层 help 遍历 registry 断言每个 state collector id 出现,未来新增不漏排。
|
|
80
|
+
- 顶层 help 含 trace/extract/panorama 三个 inspector。
|
|
81
|
+
- `trace --help` 五个之前的裸 flag 都带中文 help 片段,且分组标题 `trace 选项`
|
|
82
|
+
+ description 一句话简介都在。
|
|
83
|
+
- `gateway --help` 含 `全局选项` 分组,`--config` / `--unmask` / `--no-color` /
|
|
84
|
+
`--log-dir` / `--sessions-base` / `--openclaw-home` 都带中文说明。
|
|
85
|
+
- `gateway --help` description 拼上 `_COMMAND_DESC['gateway']` 一句话片段。
|
|
86
|
+
- `list` pretty 含已知 collector 的描述片段;`list --format json` 每项含
|
|
87
|
+
`description` key(已登记 id 描述非空)。
|
|
88
|
+
|
|
89
|
+
`pytest tests/ -q` → 234 passed, 1 skipped。
|
|
90
|
+
|
|
91
|
+
### 行为不变
|
|
92
|
+
|
|
93
|
+
- 默认参数值(paths.\*)、各 collector/inspector 业务逻辑、退出码定义、
|
|
94
|
+
argv 解析顺序、扫描结果与 v1.11.x 完全一致。
|
|
95
|
+
- 之前 215+ 用例全绿;本次只增不改测试。
|
|
96
|
+
|
|
97
|
+
|
|
3
98
|
## v1.10.4 — plugin_diag windowed scan filters to dated-in-window (2026-06-16)
|
|
4
99
|
|
|
5
100
|
Minimal correctness hardening of the v1.10.2 full-cache reuse. The 7d/30d
|
package/ocdiag/__init__.py
CHANGED
package/ocdiag/main.py
CHANGED
|
@@ -36,6 +36,37 @@ from .render.ndjson import NdjsonRenderer
|
|
|
36
36
|
_FORMAT_CHOICES = ("pretty", "json", "ndjson")
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
# Centralized command description table (id -> one-line English summary).
|
|
40
|
+
# Reused by `list`, `<id> --help`, and the top-level help. When adding a new
|
|
41
|
+
# collector/inspector, add a row here; missing ids fall back to "" via _desc().
|
|
42
|
+
_COMMAND_DESC = {
|
|
43
|
+
# Scan-type collectors (state)
|
|
44
|
+
"channel": "Scan channel (Feishu/Telegram/etc.) connect + message logs; flag disconnects, expired-discard, auth failures",
|
|
45
|
+
"configuration": "Parse key openclaw.json settings (agents/models/plugins/channels) and flag missing or risky values",
|
|
46
|
+
"cron_jobs": "List every cron job's full config (schedule/payload/sessionTarget/delivery/enabled); detect no-fire / delivery failures",
|
|
47
|
+
"doctor": "Check Node / Python / openclaw-diag / OpenClaw install and versions are ready",
|
|
48
|
+
"environment": "Collect host environment, Gateway process, and OpenClaw version basics",
|
|
49
|
+
"gateway": "Analyze Gateway process lifecycle logs (start/restart/WS connect/crash)",
|
|
50
|
+
"performance": "Measure model-call latency (E2E/TTFT), tool durations, and availability",
|
|
51
|
+
"plugin_diag": "Check plugin load status, hook subscriptions, trust gate, and plugin errors",
|
|
52
|
+
"recent_errors": "Extract and categorize recent errors/exceptions from logs",
|
|
53
|
+
"run_health": "Assess agent run completion rate, stalls, and interruptions",
|
|
54
|
+
"sessions_diag": "Scan session.jsonl; count toolCall/toolResult pairing, orphans, anomalies",
|
|
55
|
+
"shell_history": "Summarize shell commands the agent has executed",
|
|
56
|
+
"sys_health": "Check system-level health: CPU/memory/disk/OOM/processes",
|
|
57
|
+
"task_health": "Assess background task (openclaw tasks) status and health",
|
|
58
|
+
# Object-type inspectors
|
|
59
|
+
"extract": "Export a session file to readable form (incl. active/reset/deleted/backup versions)",
|
|
60
|
+
"panorama": "360° diagnosis: correlate trajectory + app logs + subtasks + cron",
|
|
61
|
+
"trace": "Trace one user message's full lifecycle (prompt->toolCall->toolResult->reply)",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _desc(mid: str) -> str:
|
|
66
|
+
"""Return the one-line English description for a command id, or ""."""
|
|
67
|
+
return _COMMAND_DESC.get(mid, "")
|
|
68
|
+
|
|
69
|
+
|
|
39
70
|
def _paged_print(text: str) -> None:
|
|
40
71
|
"""Print text through a pager when stdout is a TTY and output is long.
|
|
41
72
|
|
|
@@ -99,28 +130,53 @@ def _build_context(args) -> DiagContext:
|
|
|
99
130
|
|
|
100
131
|
|
|
101
132
|
def _common_arguments(p: argparse.ArgumentParser) -> None:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
p.
|
|
106
|
-
|
|
133
|
+
"""Register the global options shared by every subcommand, in a dedicated
|
|
134
|
+
argument group so --help output stays aligned.
|
|
135
|
+
"""
|
|
136
|
+
g = p.add_argument_group("global options")
|
|
137
|
+
g.add_argument(
|
|
138
|
+
"--config", metavar="PATH", default=paths.CONFIG,
|
|
139
|
+
help="Path to openclaw.json (default: ~/.openclaw/openclaw.json)",
|
|
140
|
+
)
|
|
141
|
+
g.add_argument(
|
|
142
|
+
"--log-dir", metavar="PATH", default=paths.LOG_DIR,
|
|
143
|
+
help="OpenClaw log directory (default: /tmp/openclaw)",
|
|
144
|
+
)
|
|
145
|
+
g.add_argument(
|
|
146
|
+
"--sessions-base", metavar="PATH", default=paths.SESSIONS_BASE,
|
|
147
|
+
help="Sessions root directory (default: ~/.openclaw/agents)",
|
|
148
|
+
)
|
|
149
|
+
g.add_argument(
|
|
150
|
+
"--openclaw-home", metavar="PATH", default=paths.OPENCLAW_HOME,
|
|
151
|
+
help="OpenClaw home directory (default: ~/.openclaw)",
|
|
152
|
+
)
|
|
153
|
+
g.add_argument(
|
|
107
154
|
"--format",
|
|
108
155
|
choices=list(_FORMAT_CHOICES),
|
|
109
156
|
default=None,
|
|
110
|
-
help="Output format
|
|
157
|
+
help="Output format pretty|json|ndjson (default: pretty; json/ndjson suit agents/scripts)",
|
|
158
|
+
)
|
|
159
|
+
g.add_argument(
|
|
160
|
+
"--json", action="store_true",
|
|
161
|
+
help="Alias for --format json",
|
|
162
|
+
)
|
|
163
|
+
g.add_argument(
|
|
164
|
+
"--no-color", action="store_true",
|
|
165
|
+
help="Disable ANSI color (use when writing to a file or pipe)",
|
|
166
|
+
)
|
|
167
|
+
g.add_argument(
|
|
168
|
+
"--unmask", action="store_true",
|
|
169
|
+
help="Do not mask; show raw sensitive content (tokens/message bodies). Masked by default",
|
|
111
170
|
)
|
|
112
|
-
p.add_argument("--json", action="store_true", help="Alias for --format json.")
|
|
113
|
-
p.add_argument("--no-color", action="store_true")
|
|
114
|
-
p.add_argument("--unmask", action="store_true")
|
|
115
171
|
|
|
116
172
|
|
|
117
173
|
def _channel_arguments(p: argparse.ArgumentParser) -> None:
|
|
118
|
-
p.
|
|
174
|
+
g = p.add_argument_group("channel options")
|
|
175
|
+
g.add_argument(
|
|
119
176
|
"--account", default=None,
|
|
120
177
|
help="Filter channel signals by account substring "
|
|
121
178
|
"(matched against the channel-prefix portion of the message body, "
|
|
122
|
-
"e.g.
|
|
123
|
-
"Default: no filter.",
|
|
179
|
+
"e.g. --account default keeps only feishu[default]: lines). Default: no filter",
|
|
124
180
|
)
|
|
125
181
|
|
|
126
182
|
|
|
@@ -159,63 +215,67 @@ def cmd_list(args) -> int:
|
|
|
159
215
|
if fmt != "pretty":
|
|
160
216
|
payload = {
|
|
161
217
|
"state_collectors": [
|
|
162
|
-
{"id": c.id, "label": c.
|
|
218
|
+
{"id": c.id, "label": _desc(c.id), "description": _desc(c.id)}
|
|
219
|
+
for c in state
|
|
163
220
|
],
|
|
164
221
|
"object_inspectors": [
|
|
165
|
-
{"id": c.id, "label": c.
|
|
222
|
+
{"id": c.id, "label": _desc(c.id), "description": _desc(c.id)}
|
|
223
|
+
for c in inspectors
|
|
166
224
|
],
|
|
167
225
|
}
|
|
168
226
|
print(json.dumps(payload, ensure_ascii=False))
|
|
169
227
|
return 0
|
|
170
|
-
print("openclaw-diag —
|
|
228
|
+
print("openclaw-diag — available diagnostics (v2)")
|
|
171
229
|
print()
|
|
172
|
-
print("
|
|
230
|
+
print(" Scan type (no args required):")
|
|
173
231
|
for c in state:
|
|
174
|
-
|
|
232
|
+
d = _desc(c.id)
|
|
233
|
+
print(f" {c.id:<16s} {d}")
|
|
175
234
|
print()
|
|
176
235
|
if inspectors:
|
|
177
|
-
print("
|
|
236
|
+
print(" Object type (require session uuid):")
|
|
178
237
|
for c in inspectors:
|
|
179
|
-
|
|
238
|
+
d = _desc(c.id)
|
|
239
|
+
print(f" {c.id:<16s} {d}")
|
|
180
240
|
print()
|
|
181
|
-
print("
|
|
182
|
-
print(" all
|
|
183
|
-
print(" doctor
|
|
184
|
-
print(" examples
|
|
241
|
+
print(" Other commands:")
|
|
242
|
+
print(" all Run all scan-type diagnostics at once")
|
|
243
|
+
print(" doctor Check Node / Python / openclaw-diag / OpenClaw environment")
|
|
244
|
+
print(" examples Print common usage examples")
|
|
185
245
|
return 0
|
|
186
246
|
|
|
187
247
|
|
|
188
248
|
def cmd_examples() -> int:
|
|
189
|
-
print("""openclaw-diag —
|
|
249
|
+
print("""openclaw-diag — common scenarios
|
|
190
250
|
|
|
191
|
-
#
|
|
251
|
+
# Full health check
|
|
192
252
|
openclaw-diag all
|
|
193
253
|
|
|
194
|
-
# JSON
|
|
254
|
+
# JSON output (for agents / scripts)
|
|
195
255
|
openclaw-diag all --format json
|
|
196
256
|
|
|
197
|
-
#
|
|
257
|
+
# Check Gateway status
|
|
198
258
|
openclaw-diag gateway
|
|
199
259
|
|
|
200
|
-
#
|
|
260
|
+
# Trace one message's full lifecycle
|
|
201
261
|
openclaw-diag trace <uuid>
|
|
202
262
|
openclaw-diag trace abc12345 --msg-index 0
|
|
203
263
|
|
|
204
|
-
#
|
|
264
|
+
# Export session conversation content
|
|
205
265
|
openclaw-diag extract <uuid>
|
|
206
266
|
openclaw-diag extract abc12345 --summary
|
|
207
267
|
|
|
208
|
-
#
|
|
268
|
+
# Session panorama diagnosis (correlates trajectory + logs + subtasks + cron)
|
|
209
269
|
openclaw-diag panorama <uuid>
|
|
210
270
|
openclaw-diag panorama abc12345 --strict-correlation --format json
|
|
211
271
|
|
|
212
|
-
#
|
|
272
|
+
# Model performance
|
|
213
273
|
openclaw-diag performance
|
|
214
274
|
|
|
215
|
-
#
|
|
275
|
+
# Cron job status
|
|
216
276
|
openclaw-diag cron_jobs
|
|
217
277
|
|
|
218
|
-
#
|
|
278
|
+
# Quick verdict via jq
|
|
219
279
|
openclaw-diag all --format json | jq '.data.verdict'
|
|
220
280
|
""")
|
|
221
281
|
return 0
|
|
@@ -300,7 +360,7 @@ def cmd_all(args, skip_ids: List[str]) -> int:
|
|
|
300
360
|
def cmd_run_collector(args, mid: str) -> int:
|
|
301
361
|
c = registry.get(mid)
|
|
302
362
|
if c is None:
|
|
303
|
-
print(f"Error:
|
|
363
|
+
print(f"Error: unknown collector '{mid}'", file=sys.stderr)
|
|
304
364
|
return EXIT_INPUT_ERROR
|
|
305
365
|
ctx = _build_context(args)
|
|
306
366
|
t0 = time.time()
|
|
@@ -319,22 +379,22 @@ def cmd_run_collector(args, mid: str) -> int:
|
|
|
319
379
|
return _exit_code(report)
|
|
320
380
|
|
|
321
381
|
|
|
322
|
-
_TRACE_EPILOG = """
|
|
323
|
-
openclaw-diag trace 7e9f3b31 #
|
|
324
|
-
openclaw-diag trace 7e9f3b31 --msg-index 0 #
|
|
325
|
-
openclaw-diag trace 7e9f3b31 --msg-match deploy #
|
|
326
|
-
openclaw-diag trace 7e9f3b31 --all-messages #
|
|
327
|
-
openclaw-diag trace 7e9f3b31 -A --format json #
|
|
382
|
+
_TRACE_EPILOG = """Examples:
|
|
383
|
+
openclaw-diag trace 7e9f3b31 # last user message in the session
|
|
384
|
+
openclaw-diag trace 7e9f3b31 --msg-index 0 # the first one
|
|
385
|
+
openclaw-diag trace 7e9f3b31 --msg-match deploy # match by content
|
|
386
|
+
openclaw-diag trace 7e9f3b31 --all-messages # trace every user message in one run (one block per turn)
|
|
387
|
+
openclaw-diag trace 7e9f3b31 -A --format json # all user messages, JSON output
|
|
328
388
|
"""
|
|
329
389
|
|
|
330
|
-
_EXTRACT_EPILOG = """
|
|
331
|
-
openclaw-diag extract 7e9f3b31 #
|
|
332
|
-
openclaw-diag extract 7e9f3b31 --summary #
|
|
333
|
-
openclaw-diag extract 7e9f3b31 --all #
|
|
390
|
+
_EXTRACT_EPILOG = """Examples:
|
|
391
|
+
openclaw-diag extract 7e9f3b31 # export the active file by default
|
|
392
|
+
openclaw-diag extract 7e9f3b31 --summary # stats only
|
|
393
|
+
openclaw-diag extract 7e9f3b31 --all # include reset / deleted / backup
|
|
334
394
|
openclaw-diag extract 7e9f3b31 --format json
|
|
335
395
|
"""
|
|
336
396
|
|
|
337
|
-
_PANORAMA_EPILOG = """
|
|
397
|
+
_PANORAMA_EPILOG = """Examples:
|
|
338
398
|
openclaw-diag panorama 7e9f3b31 # latest run
|
|
339
399
|
openclaw-diag panorama 7e9f3b31 --all-runs # every run
|
|
340
400
|
openclaw-diag panorama 7e9f3b31 --run-index 0 # first run
|
|
@@ -344,71 +404,139 @@ _PANORAMA_EPILOG = """示例:
|
|
|
344
404
|
|
|
345
405
|
|
|
346
406
|
def _build_trace_parser() -> argparse.ArgumentParser:
|
|
407
|
+
desc_text = _desc("trace")
|
|
408
|
+
description = f"{desc_text} (trace)" if desc_text else "(trace)"
|
|
347
409
|
p = argparse.ArgumentParser(
|
|
348
410
|
prog="openclaw-diag trace",
|
|
411
|
+
description=description,
|
|
349
412
|
add_help=True,
|
|
350
413
|
epilog=_TRACE_EPILOG,
|
|
351
414
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
352
415
|
)
|
|
353
|
-
p.
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
416
|
+
g = p.add_argument_group("trace options")
|
|
417
|
+
g.add_argument(
|
|
418
|
+
"session_id",
|
|
419
|
+
help="Target session UUID (full or 8+ char prefix)",
|
|
420
|
+
)
|
|
421
|
+
g.add_argument(
|
|
422
|
+
"--msg-index", type=int, default=None,
|
|
423
|
+
help="Nth user message (0-based)",
|
|
424
|
+
)
|
|
425
|
+
g.add_argument(
|
|
426
|
+
"--msg-id", default=None,
|
|
427
|
+
help="User message by id field",
|
|
428
|
+
)
|
|
429
|
+
g.add_argument(
|
|
430
|
+
"--msg-match", default=None,
|
|
431
|
+
help="First user message containing TEXT",
|
|
432
|
+
)
|
|
433
|
+
g.add_argument(
|
|
434
|
+
"-A", "--all-messages", action="store_true", dest="all_messages",
|
|
435
|
+
help="Trace every user message in the session "
|
|
436
|
+
"(mutually exclusive with --msg-index/--msg-id/--msg-match)",
|
|
437
|
+
)
|
|
438
|
+
g.add_argument(
|
|
439
|
+
"--no-trajectory", action="store_true",
|
|
440
|
+
help="Do not read trajectory.jsonl; analyze from session.jsonl only",
|
|
441
|
+
)
|
|
442
|
+
g.add_argument(
|
|
443
|
+
"--no-log", action="store_true",
|
|
444
|
+
help="Do not correlate openclaw application logs",
|
|
445
|
+
)
|
|
446
|
+
g.add_argument(
|
|
447
|
+
"--show-tool-metas", action="store_true",
|
|
448
|
+
help="Show full meta for each toolCall",
|
|
449
|
+
)
|
|
450
|
+
g.add_argument(
|
|
451
|
+
"--show-plugin-snapshot", action="store_true",
|
|
452
|
+
help="Show plugin snapshot (hooks/status)",
|
|
453
|
+
)
|
|
454
|
+
g.add_argument(
|
|
455
|
+
"--mask", action="store_true",
|
|
456
|
+
help="Force masking (trace does not mask by default)",
|
|
457
|
+
)
|
|
458
|
+
g.add_argument(
|
|
459
|
+
"--agent", default=None,
|
|
460
|
+
help="Limit to a specific agent",
|
|
461
|
+
)
|
|
369
462
|
_common_arguments(p)
|
|
370
463
|
return p
|
|
371
464
|
|
|
372
465
|
|
|
373
466
|
def _build_extract_parser() -> argparse.ArgumentParser:
|
|
467
|
+
desc_text = _desc("extract")
|
|
468
|
+
description = f"{desc_text} (extract)" if desc_text else "(extract)"
|
|
374
469
|
p = argparse.ArgumentParser(
|
|
375
470
|
prog="openclaw-diag extract",
|
|
471
|
+
description=description,
|
|
376
472
|
add_help=True,
|
|
377
473
|
epilog=_EXTRACT_EPILOG,
|
|
378
474
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
379
475
|
)
|
|
380
|
-
p.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
476
|
+
g = p.add_argument_group("extract options")
|
|
477
|
+
g.add_argument(
|
|
478
|
+
"session_id",
|
|
479
|
+
help="Target session UUID (full or 8+ char prefix)",
|
|
480
|
+
)
|
|
481
|
+
g.add_argument(
|
|
482
|
+
"--summary", action="store_true",
|
|
483
|
+
help="Per-file record-count summary; do not dump record bodies",
|
|
484
|
+
)
|
|
485
|
+
g.add_argument(
|
|
486
|
+
"-a", "--all", action="store_true", dest="all_versions",
|
|
487
|
+
help="Export all versions (active + reset + deleted + backup)",
|
|
488
|
+
)
|
|
489
|
+
g.add_argument(
|
|
490
|
+
"--list", action="store_true", dest="list_only",
|
|
491
|
+
help="List matching files only; do not extract content",
|
|
492
|
+
)
|
|
493
|
+
g.add_argument(
|
|
494
|
+
"--types", default=None,
|
|
495
|
+
help="Filter by record type (comma-separated, e.g. user,assistant,toolCall)",
|
|
496
|
+
)
|
|
497
|
+
g.add_argument(
|
|
498
|
+
"--agent", default=None,
|
|
499
|
+
help="Limit to a specific agent",
|
|
500
|
+
)
|
|
390
501
|
_common_arguments(p)
|
|
391
502
|
return p
|
|
392
503
|
|
|
393
504
|
|
|
394
505
|
def _build_panorama_parser() -> argparse.ArgumentParser:
|
|
506
|
+
desc_text = _desc("panorama")
|
|
507
|
+
description = f"{desc_text} (panorama)" if desc_text else "(panorama)"
|
|
395
508
|
p = argparse.ArgumentParser(
|
|
396
509
|
prog="openclaw-diag panorama",
|
|
510
|
+
description=description,
|
|
397
511
|
add_help=True,
|
|
398
512
|
epilog=_PANORAMA_EPILOG,
|
|
399
513
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
400
514
|
)
|
|
401
|
-
p.
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
515
|
+
g = p.add_argument_group("panorama options")
|
|
516
|
+
g.add_argument(
|
|
517
|
+
"session_id",
|
|
518
|
+
help="Target session UUID (full or 8+ char prefix)",
|
|
519
|
+
)
|
|
520
|
+
g.add_argument(
|
|
521
|
+
"--mask", action="store_true",
|
|
522
|
+
help="Sanitize tool args / message content / api keys",
|
|
523
|
+
)
|
|
524
|
+
g.add_argument(
|
|
525
|
+
"--run-index", type=int, default=None,
|
|
526
|
+
help="Pick the Nth run (default: -1 = latest)",
|
|
527
|
+
)
|
|
528
|
+
g.add_argument(
|
|
529
|
+
"--all-runs", action="store_true",
|
|
530
|
+
help="Include every run in the session",
|
|
531
|
+
)
|
|
532
|
+
g.add_argument(
|
|
533
|
+
"--strict-correlation", action="store_true",
|
|
534
|
+
help="Match only on sessionId / runIds (drop sessionKey and toolCallId hits)",
|
|
535
|
+
)
|
|
536
|
+
g.add_argument(
|
|
537
|
+
"--agent", default=None,
|
|
538
|
+
help="Limit to a specific agent",
|
|
539
|
+
)
|
|
412
540
|
_common_arguments(p)
|
|
413
541
|
return p
|
|
414
542
|
|
|
@@ -416,7 +544,7 @@ def _build_panorama_parser() -> argparse.ArgumentParser:
|
|
|
416
544
|
def cmd_inspector(head: str, rest: List[str]) -> int:
|
|
417
545
|
inspector = registry.get(head)
|
|
418
546
|
if inspector is None or inspector.kind != "inspector":
|
|
419
|
-
print(f"Error:
|
|
547
|
+
print(f"Error: unknown inspector '{head}'", file=sys.stderr)
|
|
420
548
|
return EXIT_INPUT_ERROR
|
|
421
549
|
if head == "trace":
|
|
422
550
|
parser = _build_trace_parser()
|
|
@@ -545,19 +673,61 @@ def _split_skip(rest: List[str]):
|
|
|
545
673
|
|
|
546
674
|
|
|
547
675
|
def _print_help() -> None:
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
676
|
+
"""Render the top-level --help.
|
|
677
|
+
|
|
678
|
+
Structure: intro -> usage -> health checks -> scan diagnostics (rendered
|
|
679
|
+
dynamically from the registry) -> object diagnostics -> helper commands
|
|
680
|
+
-> global options -> exit codes -> more. Scan-list ids/descriptions are
|
|
681
|
+
pulled from registry + _COMMAND_DESC so newly registered collectors are
|
|
682
|
+
never missed.
|
|
683
|
+
"""
|
|
684
|
+
state_ids = [c.id for c in registry.all_state()]
|
|
685
|
+
width = 16
|
|
686
|
+
lines: List[str] = []
|
|
687
|
+
lines.append(f"openclaw-diag v{__version__} — OpenClaw operations diagnostics CLI")
|
|
688
|
+
lines.append("")
|
|
689
|
+
lines.append("Scans config / logs / sessions to pinpoint connection, performance, cron,")
|
|
690
|
+
lines.append("plugin, and run issues in an OpenClaw deployment.")
|
|
691
|
+
lines.append("")
|
|
692
|
+
lines.append("Usage:")
|
|
693
|
+
lines.append(" openclaw-diag <command> [args...]")
|
|
694
|
+
lines.append(" openclaw-diag <command> --help Show detailed args for a command")
|
|
695
|
+
lines.append("")
|
|
696
|
+
lines.append("Health checks:")
|
|
697
|
+
lines.append(f" {'all':<{width}s}Run every scan-type diagnostic at once (recommended first step)")
|
|
698
|
+
lines.append(f" {'doctor':<{width}s}Check the runtime (Node / Python / OpenClaw readiness)")
|
|
699
|
+
lines.append("")
|
|
700
|
+
lines.append("Scan diagnostics (no args required):")
|
|
701
|
+
for sid in state_ids:
|
|
702
|
+
if sid == "doctor":
|
|
703
|
+
# doctor is already listed under Health checks; skip the duplicate.
|
|
704
|
+
continue
|
|
705
|
+
lines.append(f" {sid:<{width}s}{_desc(sid)}")
|
|
706
|
+
lines.append("")
|
|
707
|
+
lines.append("Object diagnostics (require a session uuid):")
|
|
708
|
+
lines.append(f" {'trace <uuid>':<{width}s}{_desc('trace')}")
|
|
709
|
+
lines.append(f" {'extract <uuid>':<{width}s}{_desc('extract')}")
|
|
710
|
+
lines.append(f" {'panorama <uuid>':<{width}s}{_desc('panorama')}")
|
|
711
|
+
lines.append("")
|
|
712
|
+
lines.append("Helper commands:")
|
|
713
|
+
lines.append(f" {'list':<{width}s}List all available diagnostics (supports --format json)")
|
|
714
|
+
lines.append(f" {'examples':<{width}s}Print common usage examples")
|
|
715
|
+
lines.append("")
|
|
716
|
+
lines.append("Global options (all commands):")
|
|
717
|
+
lines.append(" --format pretty|json|ndjson Output format (default: pretty)")
|
|
718
|
+
lines.append(" --json Alias for --format json")
|
|
719
|
+
lines.append(" --no-color Disable colored output")
|
|
720
|
+
lines.append(" --unmask Show raw (unmasked) sensitive content")
|
|
721
|
+
lines.append(" --config / --log-dir / --sessions-base / --openclaw-home Override default paths")
|
|
722
|
+
lines.append("")
|
|
723
|
+
lines.append("Exit codes:")
|
|
724
|
+
lines.append(" 0 OK (no warn/fail)")
|
|
725
|
+
lines.append(" 1 Warn or fail present")
|
|
726
|
+
lines.append(" 2 Input error (bad args/uuid)")
|
|
727
|
+
lines.append(" 3 Runtime error")
|
|
728
|
+
lines.append("")
|
|
729
|
+
lines.append("More: `openclaw-diag list` for all diagnostics, `openclaw-diag <command> --help` for details.")
|
|
730
|
+
print("\n".join(lines))
|
|
561
731
|
|
|
562
732
|
|
|
563
733
|
def main(argv: Optional[List[str]] = None) -> int:
|
|
@@ -618,9 +788,11 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
618
788
|
# parse_known_args (which would otherwise execute the diagnostic).
|
|
619
789
|
# parse_known_args is preserved to keep the existing lenient handling
|
|
620
790
|
# of unrecognized flags from external callers.
|
|
791
|
+
d = _desc(coll.id)
|
|
792
|
+
desc = f"{d} ({coll.id})" if d else f"({coll.id})"
|
|
621
793
|
cparser = argparse.ArgumentParser(
|
|
622
794
|
prog=f"openclaw-diag {head}",
|
|
623
|
-
description=
|
|
795
|
+
description=desc,
|
|
624
796
|
add_help=True,
|
|
625
797
|
)
|
|
626
798
|
_common_arguments(cparser)
|
|
@@ -629,8 +801,8 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
629
801
|
args, _ = cparser.parse_known_args(rest)
|
|
630
802
|
return cmd_run_collector(args, head)
|
|
631
803
|
|
|
632
|
-
print(f"Error:
|
|
633
|
-
print("
|
|
804
|
+
print(f"Error: unknown command '{head}'", file=sys.stderr)
|
|
805
|
+
print("Run `openclaw-diag list` to see all diagnostics.", file=sys.stderr)
|
|
634
806
|
return EXIT_INPUT_ERROR
|
|
635
807
|
|
|
636
808
|
|
package/package.json
CHANGED