openclaw-diag-cli
v1.13.0
Published
OpenClaw observer-only diagnostic CLI. Zero-dependency Python scripts wrapped in Node for npx-friendly install.
Maintainers
Readme
openclaw-diag
Observer-only diagnostic CLI for OpenClaw — built for humans and AI Agents. 14 diagnostic modules, 3 inspectors, structured output, zero dependencies.
Install · Quick Start · Commands · AI Agent Skill · Output Formats · Examples · Contributing
About & Scope
- Relationship to OpenClaw — Independent, community-maintained companion tool for OpenClaw. NOT an official OpenClaw product and not affiliated with the OpenClaw maintainers. It reads OpenClaw's on-disk artifacts (
openclaw.json,/tmp/openclaw/*.log,agents/*/sessions/*.jsonl,cron/,state/openclaw.sqlite,tasks/runs.sqlite) — it does not embed or modify OpenClaw itself. - Maintenance scope — Covers 14 diagnostic modules + 3 session inspectors (trace / extract / panorama), tracking current OpenClaw releases. Zero runtime dependencies (Python stdlib + Node thin-shell). Out of scope by design: no remediation, no config writes, no service restarts, no telemetry — diagnosis only.
- Security & masking — Observer-only: read-only file reads plus read-only connectivity probes; no OpenClaw state is ever modified. The default run keeps everything on-host. Masking is per-command:
extractis masked by default;traceandpanoramaare unmasked by default (pass--maskbefore sharing externally); config/log collectors always redact API keys / tokens / secrets.
Why openclaw-diag?
- Agent-Native Design — Structured JSON output, error codes, explicit verdicts. AI Agents can diagnose OpenClaw systems with zero extra parsing
- Zero Dependencies — Pure Python stdlib + Node thin-shell. No pip packages, no build steps
- Observer-Only — Never modifies system state. Safe to run anytime, anywhere
- Explicit Verdicts — Every check produces
ok/warn/failwith clear thresholds, not regex-guessed - 14 Diagnostic Modules — From system health to model performance to cron jobs to IM channels, comprehensive coverage
- Session Forensics — Trace a single message's full lifecycle, or extract entire session history
- Default Sanitization — API keys, tokens, secrets masked in config/log output. Trajectory-sourced fields are plaintext by default; use
trace --maskwhen sharing output
Features
| Module | What it checks |
|--------|----------------|
| 🖥️ sys_health | Disk, memory, CPU, DNS, NTP, network, IO wait |
| ⚙️ environment | OpenClaw version, Node/Python, env vars, version drift (14d) |
| 📋 configuration | openclaw.json validation, sensitive field sanitization |
| 🌐 gateway | Process status, port, restarts, WebSocket lifecycle, error codes, model API endpoints |
| ❗ recent_errors | Log aggregation by level, journalctl, session tool-call errors |
| ⏰ cron_jobs | Schedule parsing, consecutive failures, drift, silent detection, full job config (payload/delivery/schedule, message sanitized) |
| 📊 performance | Model/tool P50/P95, slow calls Top N, cache hit rate, throughput |
| 💬 sessions_diag | Session count, sizes, stuck detection |
| 🔌 plugin_diag | Plugin status, hook errors, DNS, trajectory drift |
| 🏃 run_health | 24h/7d/30d run health, abort rate, active leak detection |
| 📋 task_health | Task/subagent success rate, failures, timeouts, stuck detection |
| 📡 channel | IM channel diagnostics (Feishu / Lark / DingTalk / WeCom): passive log-signal scan for connection / message-drop signatures, with --account scoping |
| 🐚 shell_history | Dangerous command detection in shell history |
| 🩺 doctor | Environment self-check (Node, Python, OpenClaw, paths) |
| Inspector | Purpose | |-----------|---------| | 🔍 trace | Trace one user message: receive → model → tools → response timeline | | 📤 extract | Export session.jsonl to readable format with stats | | 🌐 panorama | Session 360° diagnosis: all correlated data from every source |
Installation
Requirements: Node.js 18+, Python 3.8+
# Option 1: Run directly with npx (recommended)
npx openclaw-diag-cli all
# Option 2: Install globally
npm install -g openclaw-diag-cli
openclaw-diag all
# Option 3: Install AI Agent skill
npx openclaw-diag-cli skill-installQuick Start
# Full health check
openclaw-diag all
# Something slow? Check model performance
openclaw-diag performance
# Session stuck? Trace the last message
openclaw-diag trace <session-uuid>
# Full session 360° diagnosis
openclaw-diag panorama <session-uuid>
# See all session content
openclaw-diag extract <session-uuid>
# JSON output for scripts / AI Agents
openclaw-diag all --format jsonQuick Start (AI Agent)
If you are an AI Agent helping a user diagnose OpenClaw:
# 1. Install (if not already)
npx openclaw-diag-cli skill-install
# 2. Run full diagnostic
openclaw-diag all --format json
# 3. Drill into specific issues based on verdict
openclaw-diag gateway --format json # if gateway verdict != ok
openclaw-diag performance --format json # if performance verdict != okCommands
State Collectors (no arguments needed)
openclaw-diag all # Run all collectors
openclaw-diag all --skip gateway,cron_jobs # Skip specific modules
openclaw-diag <module-id> # Run single module
openclaw-diag list # List all available modules
openclaw-diag doctor # Environment self-check
openclaw-diag examples # Show usage examplesInspectors (require session UUID)
# Trace: follow one message's lifecycle
openclaw-diag trace <uuid> # Last user message
openclaw-diag trace <uuid> --msg-index 0 # First message
openclaw-diag trace <uuid> --msg-match "deploy" # Match by content
openclaw-diag trace <uuid> --all-messages # Every user turn, one block each
openclaw-diag trace <uuid> -A --format json # Same (-A = --all-messages), JSON output
openclaw-diag trace <uuid> --no-trajectory # Skip trajectory enrichment
# Extract: dump session content
openclaw-diag extract <uuid> # Full records
openclaw-diag extract <uuid> --summary # Stats only
openclaw-diag extract <uuid> --all # Include backups/deleted
openclaw-diag extract <uuid> --types message # Filter by record type
# Panorama: 360° session diagnosis
openclaw-diag panorama <uuid> # Latest run (default)
openclaw-diag panorama <uuid> --all-runs # All runs in session
openclaw-diag panorama <uuid> --strict-correlation # Only sessionId/runId matches
openclaw-diag panorama <uuid> --unmask # Show full tool args/results
openclaw-diag panorama <uuid> --format json # JSON output for programmatic useChannel (IM channel diagnostics)
openclaw-diag channel # Passive: log-signature scan for connection / message-drop signals
openclaw-diag channel --account main # Scope to one account (multi-account hosts)
openclaw-diag channel --format json # JSON output for AI AgentsCovers four variants: feishu-bundled, feishu-lark, dingtalk, wecom (Bot + Agent dual-mode). Passive log-signal scan only — it reads on-disk channel logs to flag connection / message-drop signatures; no outbound calls and no config interpretation.
Utility
openclaw-diag skill-install # Deploy skill to AI Agent frameworks
openclaw-diag --version # Show versionOutput Formats
--format pretty # Colored human output (default when TTY)
--format json # Structured JSON envelope
--format ndjson # One JSON line per section (streaming/pipes)
--json # Alias for --format json (backward compat)JSON Envelope
Success:
{
"ok": true,
"data": {
"module": "gateway",
"verdict": "warn",
"summary": {"pass": 5, "warn": 1, "fail": 0, "total": 6},
"elapsed_ms": 1234,
"data_scope": [
{"source": "gateway_status", "window": "current"},
{"source": "trajectory", "window": "24h", "detail": "37 runs"}
],
"sections": [...],
"data": {...},
"status": "ok"
},
"error": null
}Every successful report carries a data.data_scope array describing the
data window it actually scanned (e.g. trajectory:7d, app_logs:today,
session:<uuid8>). Pretty output mirrors this with a 数据口径 line in
the banner; NDJSON output emits a leading {"kind":"scope", ...} line
before the section stream.
data_scope is zero-drift by contract: window is the actual scan
window (derived from the same ms value passed into collect_runs, via
the window_token() helper — never a parallel literal); detail is the
real size of the returned/scanned list, captured at the scan site. So a
report claiming trajectory:7d (240 runs) reflects what was read from
disk, not a filtered/capped sample. Analysis thresholds (e.g.
task_health 24h orphan cutoff, sessions_diag 7d active threshold,
run_health 24h/7d/30d window slices applied to a full scan) belong in
detail text — never as the window token. Two distinct scans yield
two distinct scope items: e.g. performance emits both
sessions: latest-20 (20 files) (perf sample) and sessions: 7d
(M files) (daily trend) instead of collapsing them into one.
plugin_diag additionally surfaces
data.trajectory_plugins.trajectory_scan_scope (7d | 30d |
full_fallback | none) so callers can see which layered fallback
window produced the result, and
data.trajectory_plugins.trajectory_runs_scanned for the real number of
runs read in that window (distinct from samples, which is the filtered
top-30 sample size).
Error:
{
"ok": false,
"data": null,
"error": {
"code": "SESSION_NOT_FOUND",
"message": "找不到 session 'abc123'",
"retryable": false,
"hint": "recent sessions: 7e9f3b31, b371118e"
}
}Exit Codes
| Code | Meaning | |------|---------| | 0 | Success, verdict = ok | | 1 | Success, verdict = warn or fail | | 2 | User input error (bad command, missing arg) | | 3 | Runtime error (file unreadable, crash) |
AI Agent Skill
openclaw-diag ships with a SKILL.md compatible with multiple AI Agent frameworks.
Install
openclaw-diag skill-installThis deploys the skill to all detected frameworks:
| Framework | Path |
|-----------|------|
| OpenClaw | ~/.openclaw/skills/openclaw-diag/SKILL.md |
| Claude Code | ~/.claude/commands/openclaw-diag.md |
| Codex | ~/.codex/instructions/openclaw-diag.md |
| Cursor | ~/.cursor/rules/openclaw-diag.mdc |
Intent Routing (for Agents)
| Symptom | Command |
|---------|---------|
| General health check | openclaw-diag all --format json |
| Slow responses | openclaw-diag performance --format json |
| Can't connect / Gateway down | openclaw-diag gateway --format json |
| Session stuck | openclaw-diag trace <uuid> --format json |
| Full session health check | openclaw-diag panorama <uuid> --format json |
| Recent errors | openclaw-diag recent_errors --format json |
| Cron not firing | openclaw-diag cron_jobs --format json |
| Plugin issues | openclaw-diag plugin_diag --format json |
Examples
# Quick health summary with jq
openclaw-diag all --format json | jq -r '.data | "\(.module): \(.verdict)"'
# Find modules with problems
openclaw-diag all --format json | jq 'select(.data.verdict != "ok") | .data.module'
# Model P95 latency
openclaw-diag performance --format json | jq '.data.data.model_p95_max'
# Trace a slow message
openclaw-diag trace abc12345 --msg-index 0
# Trace EVERY user turn in one run (one block each) — handy for whole-session review
openclaw-diag trace abc12345 --all-messages
openclaw-diag trace abc12345 -A --format json
# Export session and pipe to file
openclaw-diag extract abc12345 > session-dump.txt
# Full session panorama — is it slow? tools ok? model perf? stuck?
openclaw-diag panorama abc12345
openclaw-diag panorama abc12345 --all-runs --format json
# NDJSON for monitoring pipeline
openclaw-diag all --format ndjson | while read line; do
echo "$line" | jq -r 'select(.verdict != "ok") | "\(.module)/\(.section): \(.verdict)"'
doneArchitecture
bin/
openclaw-diag.js Node thin-shell (npx entry, spawns Python)
ocdiag Python direct entry
ocdiag/
main.py CLI dispatch + argument parsing
core/ Types (Check/Section/Report/Verdict), registry, context
collectors/ 13 state collectors (one file each, @register decorator)
inspectors/ trace + extract + panorama
render/ human / json / ndjson renderers
(shared utilities) sessions, trajectory, sensitive, paths, ...
skill/
openclaw-diag/ Agent skill (SKILL.md only)
scripts/
install-skill.py Deploys skill to agent frameworksAdding a new collector: create one file in ocdiag/collectors/, add @register — done. No other files to edit.
Global Flags
| Flag | Effect |
|------|--------|
| --format pretty\|json\|ndjson | Output format |
| --json | Alias for --format json |
| --no-color | Disable ANSI colors |
| --mask | Redact secrets/args/log bodies (opt-in; default varies by command) |
| --unmask | Show full plaintext (opt-in; default varies by command) |
| --config PATH | Custom openclaw.json path |
| --log-dir PATH | Custom log directory |
| --sessions-base PATH | Custom sessions base directory |
| --openclaw-home PATH | Custom OpenClaw home directory |
Masking default is per-command: extract is masked by default (--unmask for trusted local analysis); trace and panorama are unmasked by default (pass --mask before sharing output externally). Config/log state collectors always redact API keys/tokens/secrets regardless of flag.
Security
- Observer-only diagnostics: diagnostic commands never write, delete, or modify OpenClaw state.
skill-installwrites skill files to agent framework paths by explicit request - Default sanitization: API keys, tokens, secrets are masked in config/log collectors. Trajectory-sourced free-form fields (message content, tool output) are plaintext by default — use
trace --maskwhen sharing output externally;extractis masked by default, use--unmaskonly for trusted local analysis - Read-only connectivity probes: sys_health, gateway, and plugin_diag perform DNS lookups, TCP connects, or HTTP GET/HEAD probes for connectivity checks — no service restarts, no runtime state mutation
- No dependencies: no supply-chain attack surface beyond Node.js + Python stdlib
Contributing
Issues and PRs welcome. For new collectors:
- Create
ocdiag/collectors/your_module.py - Use
@registerdecorator - Return
Reportwith explicitVerdicton everyCheck - Run
python3 -m ocdiag.main your_moduleto test - Add tests in
tests/
Running tests
The runtime ocdiag package is zero-dep. Tests fall into two groups:
- Stdlib-only (no install needed) —
python3 tests/run_collector_tests.py python3 tests/run_sessions_tests.py python3 tests/run_trajectory_tests.py - pytest (
tests/test_panorama.py,tests/test_v2_*.py) — install the optionaldevextras first, then run pytest:pip install -e ".[dev]" pytest tests/
pytest is declared under [project.optional-dependencies].dev in
pyproject.toml; it is not a runtime dependency.
