clawmonitor 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ **[English](README.md)** · [简体中文](README.zh-CN.md) · [繁體中文](README.zh-TW.md)
2
+
3
+ ---
4
+
5
+ # ClawMonitor
6
+
7
+ Real-time OpenClaw tool call monitor. Watch all agent sessions with readable names and sorted output.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ # Run without installing
13
+ npx clawmonitor
14
+
15
+ # Install globally
16
+ npm install -g clawmonitor
17
+ clawmonitor
18
+ ```
19
+
20
+ ## Features
21
+
22
+ - 🔧 Real-time monitoring of all OpenClaw agent tool calls
23
+ - 📜 Shows last 10 history entries on startup (cross-session, sorted by time)
24
+ - 📋 Readable session names (auto-parsed from conversation_label)
25
+ - 🔄 Auto-tracks newly created sessions
26
+ - 🎨 Colored terminal output (respects `NO_COLOR`)
27
+ - 🖥️ Cross-platform: Linux, macOS, Windows (Git Bash / WSL)
28
+
29
+ ## Usage
30
+
31
+ ```
32
+ clawmonitor [options]
33
+
34
+ Options:
35
+ --all Monitor all sessions (no time filter)
36
+ --compact Compact one-line output
37
+ --history N Show last N history entries (default: 10)
38
+ --help Show help
39
+ ```
40
+
41
+ ## Examples
42
+
43
+ ```bash
44
+ # Default: last 30 min sessions + 10 history
45
+ clawmonitor
46
+
47
+ # Compact mode with 20 history entries
48
+ clawmonitor --compact --history 20
49
+
50
+ # Monitor all sessions regardless of time
51
+ clawmonitor --all
52
+ ```
53
+
54
+ ## Requirements
55
+
56
+ - **jq** — the only external dependency
57
+ - Linux: `sudo apt install jq`
58
+ - macOS: `brew install jq`
59
+ - Windows: `pacman -S jq` (Git Bash) or use WSL
60
+
61
+ Everything else (`tail`, `date`, `bash`) comes pre-installed.
62
+
63
+ ## Environment Variables
64
+
65
+ | Variable | Description |
66
+ |---|---|
67
+ | `OPENCLAW_HOME` | Custom OpenClaw data directory |
68
+ | `NO_COLOR` | Disable colored output |
69
+
70
+ ## Development
71
+
72
+ ```bash
73
+ git clone https://github.com/reopenpilot/clawmonitor.git
74
+ cd clawmonitor
75
+ bash bin/clawmonitor.sh --help
76
+ ```
77
+
78
+ ## License
79
+
80
+ MIT
@@ -0,0 +1,80 @@
1
+ [English](README.md) · **[简体中文](README.zh-CN.md)** · [繁體中文](README.zh-TW.md)
2
+
3
+ ---
4
+
5
+ # ClawMonitor
6
+
7
+ 实时监控 OpenClaw 所有 agent 的 tool calls,跨 session、按时间排序。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ # 无需安装直接运行
13
+ npx clawmonitor
14
+
15
+ # 全局安装
16
+ npm install -g clawmonitor
17
+ clawmonitor
18
+ ```
19
+
20
+ ## 功能
21
+
22
+ - 🔧 实时监控所有 OpenClaw agent 的 tool calls
23
+ - 📜 启动时显示最近 10 条历史记录(跨 session,按时间排序)
24
+ - 📋 可读的 session 名称(自动解析 conversation_label)
25
+ - 🔄 自动追踪新建的 session
26
+ - 🎨 彩色终端输出(支持 `NO_COLOR` 环境变量)
27
+ - 🖥️ 跨平台:Linux、macOS、Windows(Git Bash / WSL)
28
+
29
+ ## 使用方法
30
+
31
+ ```
32
+ clawmonitor [选项]
33
+
34
+ 选项:
35
+ --all 监控所有 session(不限时间)
36
+ --compact 精简一行输出
37
+ --history N 显示最近 N 条历史记录(默认 10)
38
+ --help 显示帮助
39
+ ```
40
+
41
+ ## 示例
42
+
43
+ ```bash
44
+ # 默认:最近 30 分钟的 session + 10 条历史
45
+ clawmonitor
46
+
47
+ # 精简模式,显示 20 条历史
48
+ clawmonitor --compact --history 20
49
+
50
+ # 监控所有 session(不限时间)
51
+ clawmonitor --all
52
+ ```
53
+
54
+ ## 依赖
55
+
56
+ - **jq** — 唯一的外部依赖
57
+ - Linux: `sudo apt install jq`
58
+ - macOS: `brew install jq`
59
+ - Windows: `pacman -S jq`(Git Bash)或使用 WSL
60
+
61
+ 其他(`tail`、`date`、`bash`)系统自带。
62
+
63
+ ## 环境变量
64
+
65
+ | 变量 | 说明 |
66
+ |---|---|
67
+ | `OPENCLAW_HOME` | 自定义 OpenClaw 数据目录 |
68
+ | `NO_COLOR` | 禁用彩色输出 |
69
+
70
+ ## 开发
71
+
72
+ ```bash
73
+ git clone https://github.com/reopenpilot/clawmonitor.git
74
+ cd clawmonitor
75
+ bash bin/clawmonitor.sh --help
76
+ ```
77
+
78
+ ## 许可证
79
+
80
+ MIT
@@ -0,0 +1,80 @@
1
+ [English](README.md) · [简体中文](README.zh-CN.md) · **[繁體中文](README.zh-TW.md)**
2
+
3
+ ---
4
+
5
+ # ClawMonitor
6
+
7
+ 即時監控 OpenClaw 所有 agent 的 tool calls,跨 session、按時間排序。
8
+
9
+ ## 安裝
10
+
11
+ ```bash
12
+ # 不用安裝直接跑
13
+ npx clawmonitor
14
+
15
+ # 全域安裝
16
+ npm install -g clawmonitor
17
+ clawmonitor
18
+ ```
19
+
20
+ ## 功能
21
+
22
+ - 🔧 即時監控所有 OpenClaw agent 的 tool calls
23
+ - 📜 啟動時顯示最近 10 筆歷史記錄(跨 session,按時間排序)
24
+ - 📋 可讀的 session 名稱(自動解析 conversation_label)
25
+ - 🔄 自動追蹤新建的 session
26
+ - 🎨 彩色終端機輸出(支援 `NO_COLOR` 環境變數)
27
+ - 🖥️ 跨平台:Linux、macOS、Windows(Git Bash / WSL)
28
+
29
+ ## 使用方式
30
+
31
+ ```
32
+ clawmonitor [選項]
33
+
34
+ 選項:
35
+ --all 監控所有 session(不限時間)
36
+ --compact 精簡一行輸出
37
+ --history N 顯示最近 N 筆歷史記錄(預設 10)
38
+ --help 顯示說明
39
+ ```
40
+
41
+ ## 範例
42
+
43
+ ```bash
44
+ # 預設:最近 30 分鐘的 session + 10 筆歷史
45
+ clawmonitor
46
+
47
+ # 精簡模式,顯示 20 筆歷史
48
+ clawmonitor --compact --history 20
49
+
50
+ # 監控所有 session(不限時間)
51
+ clawmonitor --all
52
+ ```
53
+
54
+ ## 依賴
55
+
56
+ - **jq** — 唯一的外部依賴
57
+ - Linux: `sudo apt install jq`
58
+ - macOS: `brew install jq`
59
+ - Windows: `pacman -S jq`(Git Bash)或使用 WSL
60
+
61
+ 其他(`tail`、`date`、`bash`)系統內建。
62
+
63
+ ## 環境變數
64
+
65
+ | 變數 | 說明 |
66
+ |---|---|
67
+ | `OPENCLAW_HOME` | 自訂 OpenClaw 資料目錄 |
68
+ | `NO_COLOR` | 停用彩色輸出 |
69
+
70
+ ## 開發
71
+
72
+ ```bash
73
+ git clone https://github.com/reopenpilot/clawmonitor.git
74
+ cd clawmonitor
75
+ bash bin/clawmonitor.sh --help
76
+ ```
77
+
78
+ ## 授權
79
+
80
+ MIT
@@ -0,0 +1,449 @@
1
+ #!/usr/bin/env bash
2
+ # clawmonitor — Real-time OpenClaw tool call monitor
3
+ #
4
+ # Usage:
5
+ # clawmonitor # Default: last 30 min sessions + 10 history
6
+ # clawmonitor --all # Monitor all sessions
7
+ # clawmonitor --compact # Compact output
8
+ # clawmonitor --history N # Show N recent tool calls
9
+ #
10
+ # npx clawmonitor # Run without installing
11
+
12
+ set -euo pipefail
13
+
14
+ # === Platform detection ===
15
+ OS="$(uname -s 2>/dev/null || echo "unknown")"
16
+ ARCH="$(uname -m 2>/dev/null || echo "unknown")"
17
+
18
+ IS_WINDOWS=false
19
+ if [[ "$OS" == "MINGW"* || "$OS" == "MSYS"* || "$OS" == "CYGWIN"* ]]; then
20
+ IS_WINDOWS=true
21
+ fi
22
+
23
+ # === Detect OpenClaw data directory ===
24
+ if [[ -n "${OPENCLAW_HOME:-}" ]]; then
25
+ AGENTS_DIR="$OPENCLAW_HOME/agents"
26
+ elif [[ -d "$HOME/.openclaw/agents" ]]; then
27
+ AGENTS_DIR="$HOME/.openclaw/agents"
28
+ elif [[ -n "${XDG_DATA_HOME:-}" ]] && [[ -d "$XDG_DATA_HOME/openclaw/agents" ]]; then
29
+ AGENTS_DIR="$XDG_DATA_HOME/openclaw/agents"
30
+ elif [[ "$IS_WINDOWS" == "true" ]] && [[ -d "$APPDATA/openclaw/agents" ]]; then
31
+ AGENTS_DIR="$APPDATA/openclaw/agents"
32
+ else
33
+ echo "❌ Cannot find OpenClaw data directory."
34
+ echo ""
35
+ echo " Tried:"
36
+ echo " - \$OPENCLAW_HOME/agents"
37
+ echo " - ~/.openclaw/agents"
38
+ echo " - \$XDG_DATA_HOME/openclaw/agents"
39
+ if [[ "$IS_WINDOWS" == "true" ]]; then
40
+ echo " - %APPDATA%/openclaw/agents"
41
+ fi
42
+ echo ""
43
+ echo " Set OPENCLAW_HOME to your OpenClaw data directory."
44
+ exit 1
45
+ fi
46
+
47
+ # === Check dependencies ===
48
+ MISSING=()
49
+ for cmd in jq tail date; do
50
+ if ! command -v "$cmd" &>/dev/null; then
51
+ MISSING+=("$cmd")
52
+ fi
53
+ done
54
+
55
+ if [[ ${#MISSING[@]} -gt 0 ]]; then
56
+ echo "❌ Missing required commands: ${MISSING[*]}"
57
+ echo ""
58
+ echo " Install them:"
59
+ echo ""
60
+ if [[ "$IS_WINDOWS" == "true" ]]; then
61
+ echo " 🪟 Windows (Git Bash / MSYS2):"
62
+ echo " pacman -S jq"
63
+ echo ""
64
+ echo " Or use WSL:"
65
+ echo " sudo apt install jq"
66
+ elif [[ "$OS" == "Darwin" ]]; then
67
+ echo " 🍎 macOS:"
68
+ echo " brew install jq"
69
+ echo ""
70
+ echo " No Homebrew? Install it first:"
71
+ echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
72
+ else
73
+ echo " 🐧 Linux:"
74
+ echo " sudo apt install jq # Debian/Ubuntu"
75
+ echo " sudo dnf install jq # Fedora"
76
+ echo " sudo pacman -S jq # Arch"
77
+ echo " sudo apk add jq # Alpine"
78
+ fi
79
+ echo ""
80
+ echo " jq is the only external dependency. tail and date are usually pre-installed."
81
+ exit 1
82
+ fi
83
+
84
+ # === Cross-platform helpers ===
85
+
86
+ # Get file modification time (HH:MM:SS)
87
+ get_mod_time() {
88
+ local file="$1"
89
+ if [[ "$OS" == "Darwin" ]]; then
90
+ stat -f '%Sm' -t '%H:%M:%S' "$file" 2>/dev/null || echo "???"
91
+ elif [[ "$IS_WINDOWS" == "true" ]]; then
92
+ stat -c '%y' "$file" 2>/dev/null | cut -c12-19 || echo "???"
93
+ else
94
+ stat -c '%y' "$file" 2>/dev/null | cut -c12-19 || echo "???"
95
+ fi
96
+ }
97
+
98
+ # Format ISO timestamp → HH:MM:SS
99
+ format_timestamp() {
100
+ local ts="$1"
101
+ if [[ -z "$ts" || "$ts" == "null" || "$ts" == "???" ]]; then
102
+ echo "???"
103
+ return
104
+ fi
105
+
106
+ if [[ "$OS" == "Darwin" ]]; then
107
+ if date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$ts" | cut -c1-19)" "+%H:%M:%S" 2>/dev/null; then
108
+ return
109
+ fi
110
+ echo "$ts" | cut -c12-19
111
+ else
112
+ date -d "$ts" "+%H:%M:%S" 2>/dev/null || echo "$ts" | cut -c12-19
113
+ fi
114
+ }
115
+
116
+ # Portable grep (no -P on macOS)
117
+ grep_p() {
118
+ if [[ "$OS" == "Darwin" ]]; then
119
+ grep -E "$@"
120
+ else
121
+ grep -P "$@"
122
+ fi
123
+ }
124
+
125
+ # === Argument parsing ===
126
+ COMPACT=false
127
+ SHOW_HISTORY=false
128
+ HISTORY_N=10
129
+ TIME_RANGE=30 # minutes, 0 = all
130
+
131
+ while [[ $# -gt 0 ]]; do
132
+ case "$1" in
133
+ --compact)
134
+ COMPACT=true
135
+ shift
136
+ ;;
137
+ --history)
138
+ SHOW_HISTORY=true
139
+ if [[ $# -gt 1 ]] && [[ "$2" =~ ^[0-9]+$ ]]; then
140
+ HISTORY_N="$2"
141
+ shift 2
142
+ else
143
+ shift
144
+ fi
145
+ ;;
146
+ --all)
147
+ TIME_RANGE=0
148
+ shift
149
+ ;;
150
+ --help|-h)
151
+ echo "clawmonitor — Real-time OpenClaw tool call monitor"
152
+ echo ""
153
+ echo "Usage: clawmonitor [options]"
154
+ echo ""
155
+ echo " --all Monitor all sessions (no time filter)"
156
+ echo " --compact Show only tool name + brief args"
157
+ echo " --history Show recent history before live tail"
158
+ echo " --history N Show last N tool calls from history (default: 10)"
159
+ echo " --help Show this help"
160
+ echo ""
161
+ echo " Monitors all agents under ~/.openclaw/agents/"
162
+ echo " Supports Linux, macOS, and Windows (Git Bash / WSL)"
163
+ echo ""
164
+ echo " Environment variables:"
165
+ echo " OPENCLAW_HOME Custom OpenClaw data directory"
166
+ echo " NO_COLOR Disable colored output"
167
+ exit 0
168
+ ;;
169
+ [0-9]*)
170
+ if $SHOW_HISTORY; then
171
+ HISTORY_N="$1"
172
+ shift
173
+ else
174
+ echo "Unknown argument: $1"
175
+ exit 1
176
+ fi
177
+ ;;
178
+ *)
179
+ echo "Unknown argument: $1"
180
+ exit 1
181
+ ;;
182
+ esac
183
+ done
184
+
185
+ # === Color setup ===
186
+ if [[ -t 1 ]] && [[ "${NO_COLOR:-}" == "" ]]; then
187
+ RST='\033[0m' BLD='\033[1m' DIM='\033[2m'
188
+ CYN='\033[36m' GRN='\033[32m' YLW='\033[33m' MAG='\033[35m' RED='\033[31m'
189
+ BLU='\033[34m' WHT='\033[37m'
190
+ else
191
+ RST='' BLD='' DIM=''
192
+ CYN='' GRN='' YLW='' MAG='' RED=''
193
+ BLU='' WHT=''
194
+ fi
195
+
196
+ # === Session management ===
197
+
198
+ find_sessions() {
199
+ local find_cmd=(find "$AGENTS_DIR" -path "*/sessions/*.jsonl" -type f)
200
+ if [[ "$TIME_RANGE" -eq 0 ]]; then
201
+ "${find_cmd[@]}"
202
+ else
203
+ local now_epoch
204
+ now_epoch=$(date +%s)
205
+
206
+ "${find_cmd[@]}" | while IFS= read -r file; do
207
+ local mod_epoch
208
+ if [[ "$OS" == "Darwin" ]]; then
209
+ mod_epoch=$(stat -f '%m' "$file" 2>/dev/null || echo 0)
210
+ else
211
+ mod_epoch=$(stat -c '%Y' "$file" 2>/dev/null || echo 0)
212
+ fi
213
+
214
+ if [[ "$mod_epoch" -gt 0 ]] && (( now_epoch - mod_epoch <= TIME_RANGE * 60 )); then
215
+ echo "$file"
216
+ fi
217
+ done
218
+ fi
219
+ }
220
+
221
+ # Extract readable session name from conversation_label
222
+ get_session_label() {
223
+ local file="$1"
224
+ local agent_name
225
+ agent_name=$(echo "$file" | sed "s|$AGENTS_DIR/||;s|/sessions/.*||")
226
+
227
+ local filename
228
+ filename=$(basename "$file" .jsonl)
229
+
230
+ local conv_label
231
+ conv_label=$(jq -r 'select(.type=="message" and .message.role=="user") |
232
+ .message.content[] | select(.type=="text") | .text' "$file" 2>/dev/null | \
233
+ head -20 | \
234
+ grep_p -o '"conversation_label"[[:space:]]*:[[:space:]]*"[^"]*"' 2>/dev/null | head -1 | \
235
+ sed 's/"conversation_label" : "//;s/"conversation_label": "//;s/"$//')
236
+
237
+ if [[ -n "$conv_label" ]]; then
238
+ local group topic group_name
239
+ group=$(echo "$conv_label" | grep_p -o 'id:[^ ]+' 2>/dev/null | sed 's/id://')
240
+ topic=$(echo "$conv_label" | grep_p -o 'topic:[0-9]+' 2>/dev/null | sed 's/topic:/t/')
241
+ group_name=$(echo "$conv_label" | grep_p -o '^[^ ]+' 2>/dev/null)
242
+
243
+ if [[ -n "$topic" ]]; then
244
+ echo "${agent_name}/${group_name}/${topic}"
245
+ elif [[ -n "$group_name" ]]; then
246
+ echo "${agent_name}/${group_name}"
247
+ else
248
+ echo "${agent_name}/${conv_label}"
249
+ fi
250
+ else
251
+ local short_id topic_suffix
252
+ short_id=$(echo "$filename" | sed 's/-topic-[0-9]*$//;s/-\([0-9a-f]\{4\}\).*/..*\1/')
253
+ topic_suffix=$(echo "$filename" | grep_p -o 'topic-[0-9]+' 2>/dev/null || true)
254
+ if [[ -n "$topic_suffix" ]]; then
255
+ echo "${agent_name}/DM/${topic_suffix}"
256
+ else
257
+ echo "${agent_name}/DM/${short_id}"
258
+ fi
259
+ fi
260
+ }
261
+
262
+ declare -A SESSION_LABELS
263
+
264
+ cache_labels() {
265
+ local files="$1"
266
+ for f in $files; do
267
+ local key
268
+ key=$(basename "$f")
269
+ SESSION_LABELS["$key"]=$(get_session_label "$f")
270
+ done
271
+ }
272
+
273
+ # Extract tool calls from a single session JSONL
274
+ extract_tool_calls() {
275
+ local file="$1"
276
+ local limit="${2:-0}"
277
+ local filename
278
+ filename=$(basename "$file")
279
+
280
+ local filter='select(.type=="message" and .message.role=="assistant") |
281
+ {ts: (.timestamp // .message.timestamp), file: "'"${filename}"'", content: [.message.content[] | select(.type=="toolCall")]} |
282
+ select(.content | length > 0)'
283
+
284
+ if [[ "$limit" -gt 0 ]]; then
285
+ jq -c "$filter" "$file" 2>/dev/null | tail -"$limit"
286
+ else
287
+ jq -c "$filter" "$file" 2>/dev/null
288
+ fi
289
+ }
290
+
291
+ # Format a single tool call entry
292
+ format_tc() {
293
+ local json="$1"
294
+ local ts filename label
295
+ ts=$(echo "$json" | jq -r '.ts // "???"')
296
+ filename=$(echo "$json" | jq -r '.file // "?"')
297
+ label="${SESSION_LABELS[$filename]:-${filename}}"
298
+
299
+ local time_str
300
+ time_str=$(format_timestamp "$ts")
301
+
302
+ echo "$json" | jq -c '.content[]' 2>/dev/null | while IFS= read -r tc; do
303
+ local tool_name tool_id
304
+ tool_name=$(echo "$tc" | jq -r '.name // "?"')
305
+ tool_id=$(echo "$tc" | jq -r '.id // "?"' | cut -c1-12)
306
+
307
+ if $COMPACT; then
308
+ local brief_args
309
+ brief_args=$(echo "$tc" | jq -r '
310
+ .arguments | to_entries[:2] |
311
+ map("\(.key)=\(.value | tostring | .[0:80])") | join(" ")
312
+ ' 2>/dev/null)
313
+ printf "${GRN}%-13s${RST} ${BLU}%-28s${RST} ${CYN}%-9s${RST} %s\n" \
314
+ "$tool_name" "${label:0:28}" "$time_str" "$brief_args"
315
+ else
316
+ printf "\n${BLD}${YLW}⏱ %s${RST} ${BLU}%s${RST} ${MAG}%s${RST}\n" \
317
+ "$time_str" "${label}" "$tool_id"
318
+ printf " ${GRN}▶ %s${RST}\n" "$tool_name"
319
+
320
+ echo "$tc" | jq -c '.arguments | to_entries[]' 2>/dev/null | while IFS= read -r entry; do
321
+ local key val
322
+ key=$(echo "$entry" | jq -r '.key')
323
+ val=$(echo "$entry" | jq -r '.value | tostring | .[0:300]')
324
+ [[ ${#val} -ge 300 ]] && val="${val}…"
325
+ printf " ${CYN}%-14s${RST} %s\n" "$key" "$val"
326
+ done
327
+ fi
328
+ done
329
+ }
330
+
331
+ # === Main ===
332
+
333
+ sessions=$(find_sessions | sort)
334
+ session_count=$(echo "$sessions" | grep -c . 2>/dev/null || echo 0)
335
+
336
+ if [[ "$session_count" -eq 0 ]]; then
337
+ echo -e "${RED}No sessions found under ${AGENTS_DIR}${RST}"
338
+ echo "Try --all or wait for activity."
339
+ exit 1
340
+ fi
341
+
342
+ cache_labels "$sessions"
343
+
344
+ echo -e "${BLD}🔧 OpenClaw Tool Call Monitor${RST}"
345
+ echo -e "${DIM}Watching ${session_count} session(s) on ${OS} — Ctrl+C to stop${RST}"
346
+ echo ""
347
+
348
+ # List all sessions
349
+ echo -e "${BLD}📋 Active sessions:${RST}"
350
+ for f in $sessions; do
351
+ filename=$(basename "$f")
352
+ label="${SESSION_LABELS[$filename]}"
353
+ mod_time=$(get_mod_time "$f")
354
+ printf " ${DIM}%-9s${RST} ${BLU}%-35s${RST} ${DIM}(last: %s)${RST}\n" \
355
+ "$(echo "$filename" | cut -c1-8)" "${label:0:35}" "$mod_time"
356
+ done
357
+
358
+ if $COMPACT; then
359
+ printf "\n${DIM}%-13s %-28s %-9s %s${RST}\n" "TOOL" "SESSION" "TIME" "ARGS"
360
+ echo "-------------------------------------------------------------------------"
361
+ fi
362
+
363
+ # Default: show last 10 history entries
364
+ if ! $SHOW_HISTORY; then
365
+ SHOW_HISTORY=true
366
+ fi
367
+
368
+ # Show history sorted by time
369
+ if $SHOW_HISTORY; then
370
+ echo -e "\n${BLD}📜 Recent tool calls (sorted by time):${RST}"
371
+ for f in $sessions; do
372
+ extract_tool_calls "$f" 0
373
+ done | jq -s 'sort_by(.ts)' | jq -c '.[]' > /tmp/openclaw-tc-history.jsonl
374
+
375
+ if [[ "$HISTORY_N" -gt 0 ]]; then
376
+ tail -"$HISTORY_N" /tmp/openclaw-tc-history.jsonl | while IFS= read -r line; do
377
+ format_tc "$line"
378
+ done
379
+ else
380
+ cat /tmp/openclaw-tc-history.jsonl | while IFS= read -r line; do
381
+ format_tc "$line"
382
+ done
383
+ fi
384
+ rm -f /tmp/openclaw-tc-history.jsonl
385
+
386
+ echo -e "\n${BLD}🔴 Live monitoring:${RST}"
387
+ if $COMPACT; then
388
+ echo "-------------------------------------------------------------------------"
389
+ fi
390
+ fi
391
+
392
+ # Live monitoring with glob pattern for auto-tracking new sessions
393
+ SESSIONS_GLOB="${AGENTS_DIR}/*/sessions/*.jsonl"
394
+ shopt -s nullglob
395
+ live_session_files=("$AGENTS_DIR"/*/sessions/*.jsonl)
396
+ shopt -u nullglob
397
+
398
+ tail -F -n 0 $SESSIONS_GLOB 2>/dev/null | {
399
+ current_file=""
400
+ if [[ "${#live_session_files[@]}" -eq 1 ]]; then
401
+ current_file="${live_session_files[0]}"
402
+ fi
403
+ while IFS= read -r line; do
404
+ # Detect tail -F file switch header
405
+ if [[ "$line" == "==>"* ]]; then
406
+ current_file=$(echo "$line" | sed 's/==> \(.*\) <==/\1/')
407
+ continue
408
+ fi
409
+
410
+ # Quick filter
411
+ echo "$line" | grep -q '"toolCall"' || continue
412
+ echo "$line" | grep -q '"assistant"' || continue
413
+
414
+ filename=$(basename "${current_file}")
415
+ # Dynamic label lookup for new sessions
416
+ if [[ -z "${SESSION_LABELS[$filename]:-}" ]]; then
417
+ SESSION_LABELS["$filename"]=$(get_session_label "${current_file}")
418
+ fi
419
+ label="${SESSION_LABELS[$filename]}"
420
+
421
+ ts=$(echo "$line" | jq -r '.timestamp // .message.timestamp // "???"' 2>/dev/null)
422
+ time_str=$(format_timestamp "$ts")
423
+
424
+ echo "$line" | jq -c '.message.content[] | select(.type=="toolCall")' 2>/dev/null | while IFS= read -r tc; do
425
+ tool_name=$(echo "$tc" | jq -r '.name // "?"')
426
+ tool_id=$(echo "$tc" | jq -r '.id // "?"' | cut -c1-12)
427
+
428
+ if $COMPACT; then
429
+ brief_args=$(echo "$tc" | jq -r '
430
+ .arguments | to_entries[:2] |
431
+ map("\(.key)=\(.value | tostring | .[0:80])") | join(" ")
432
+ ' 2>/dev/null)
433
+ printf "${GRN}%-13s${RST} ${BLU}%-28s${RST} ${CYN}%-9s${RST} %s\n" \
434
+ "$tool_name" "${label:0:28}" "$time_str" "$brief_args"
435
+ else
436
+ printf "\n${BLD}${YLW}⏱ %s${RST} ${BLU}%s${RST} ${MAG}%s${RST}\n" \
437
+ "$time_str" "${label}" "$tool_id"
438
+ printf " ${GRN}▶ %s${RST}\n" "$tool_name"
439
+
440
+ echo "$tc" | jq -c '.arguments | to_entries[]' 2>/dev/null | while IFS= read -r entry; do
441
+ key=$(echo "$entry" | jq -r '.key')
442
+ val=$(echo "$entry" | jq -r '.value | tostring | .[0:300]')
443
+ [[ ${#val} -ge 300 ]] && val="${val}…"
444
+ printf " ${CYN}%-14s${RST} %s\n" "$key" "$val"
445
+ done
446
+ fi
447
+ done
448
+ done
449
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "clawmonitor",
3
+ "version": "1.0.0",
4
+ "description": "Real-time OpenClaw tool call monitor — watch all agent sessions with readable names and sorted output",
5
+ "bin": {
6
+ "clawmonitor": "./bin/clawmonitor.sh"
7
+ },
8
+ "scripts": {
9
+ "test": "bash -n bin/clawmonitor.sh"
10
+ },
11
+ "keywords": [
12
+ "openclaw",
13
+ "monitor",
14
+ "tool-calls",
15
+ "debug",
16
+ "cli"
17
+ ],
18
+ "author": "reopenpilot",
19
+ "license": "MIT",
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
23
+ "files": [
24
+ "bin/",
25
+ "README.md",
26
+ "README.zh-CN.md",
27
+ "README.zh-TW.md",
28
+ "LICENSE"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/reopenpilot/clawmonitor.git"
33
+ },
34
+ "dependencies": {},
35
+ "homepage": "https://github.com/reopenpilot/clawmonitor#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/reopenpilot/clawmonitor/issues"
38
+ }
39
+ }