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 +21 -0
- package/README.md +80 -0
- package/README.zh-CN.md +80 -0
- package/README.zh-TW.md +80 -0
- package/bin/clawmonitor.sh +449 -0
- package/package.json +39 -0
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
|
package/README.zh-CN.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
|
+
实时监控 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
|
package/README.zh-TW.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
|
+
即時監控 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
|
+
}
|