tanuki-telemetry 1.4.0 → 1.4.2

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.
@@ -0,0 +1,100 @@
1
+ #!/bin/bash
2
+ # Record agent-browser session as MP4 video
3
+ # Usage: record-browser.sh start [output.mp4] [interval_ms]
4
+ # record-browser.sh stop
5
+ # record-browser.sh status
6
+
7
+ PIDFILE="/tmp/browser-recorder.pid"
8
+ FRAMEDIR="/tmp/browser-recording-frames"
9
+ DEFAULT_OUTPUT="/tmp/browser-recording-$(date +%Y%m%d-%H%M%S).mp4"
10
+ DEFAULT_INTERVAL=1000 # 1 screenshot per second
11
+
12
+ case "${1:-status}" in
13
+ start)
14
+ OUTPUT="${2:-$DEFAULT_OUTPUT}"
15
+ INTERVAL="${3:-$DEFAULT_INTERVAL}"
16
+
17
+ if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
18
+ echo "Recording already in progress (PID $(cat "$PIDFILE"))"
19
+ exit 0
20
+ fi
21
+
22
+ rm -rf "$FRAMEDIR"
23
+ mkdir -p "$FRAMEDIR"
24
+
25
+ # Background process: take screenshots at interval
26
+ (
27
+ FRAME=0
28
+ while true; do
29
+ PADDED=$(printf "%06d" $FRAME)
30
+ agent-browser screenshot "$FRAMEDIR/frame-${PADDED}.png" 2>/dev/null
31
+ if [ $? -ne 0 ]; then
32
+ sleep 1
33
+ continue
34
+ fi
35
+ FRAME=$((FRAME + 1))
36
+ sleep "$(echo "scale=3; $INTERVAL/1000" | bc)"
37
+ done
38
+ ) &
39
+
40
+ echo $! > "$PIDFILE"
41
+ echo "$OUTPUT" > /tmp/browser-recording-output
42
+ echo "Recording started (PID $!) → $OUTPUT"
43
+ echo "Taking screenshots every ${INTERVAL}ms"
44
+ echo "Run: record-browser.sh stop — to finish and create video"
45
+ ;;
46
+
47
+ stop)
48
+ if [ ! -f "$PIDFILE" ]; then
49
+ echo "No recording in progress"
50
+ exit 1
51
+ fi
52
+
53
+ PID=$(cat "$PIDFILE")
54
+ kill "$PID" 2>/dev/null
55
+ wait "$PID" 2>/dev/null
56
+ rm -f "$PIDFILE"
57
+
58
+ OUTPUT=$(cat /tmp/browser-recording-output 2>/dev/null || echo "$DEFAULT_OUTPUT")
59
+ FRAME_COUNT=$(ls "$FRAMEDIR"/frame-*.png 2>/dev/null | wc -l | tr -d ' ')
60
+
61
+ if [ "$FRAME_COUNT" -eq 0 ]; then
62
+ echo "No frames captured — nothing to encode"
63
+ exit 1
64
+ fi
65
+
66
+ echo "Encoding $FRAME_COUNT frames → $OUTPUT"
67
+
68
+ # Use ffmpeg to create video from frames
69
+ ffmpeg -y -framerate 1 -i "$FRAMEDIR/frame-%06d.png" \
70
+ -c:v libx264 -pix_fmt yuv420p -preset fast \
71
+ -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" \
72
+ "$OUTPUT" 2>/dev/null
73
+
74
+ if [ $? -eq 0 ]; then
75
+ SIZE=$(du -h "$OUTPUT" | cut -f1)
76
+ echo "✓ Recording saved: $OUTPUT ($SIZE, $FRAME_COUNT frames)"
77
+ rm -rf "$FRAMEDIR"
78
+ else
79
+ echo "✗ ffmpeg encoding failed"
80
+ exit 1
81
+ fi
82
+ ;;
83
+
84
+ status)
85
+ if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
86
+ FRAME_COUNT=$(ls "$FRAMEDIR"/frame-*.png 2>/dev/null | wc -l | tr -d ' ')
87
+ OUTPUT=$(cat /tmp/browser-recording-output 2>/dev/null)
88
+ echo "Recording in progress (PID $(cat "$PIDFILE"))"
89
+ echo " Frames captured: $FRAME_COUNT"
90
+ echo " Output: $OUTPUT"
91
+ else
92
+ echo "Not recording"
93
+ rm -f "$PIDFILE" 2>/dev/null
94
+ fi
95
+ ;;
96
+
97
+ *)
98
+ echo "Usage: record-browser.sh [start|stop|status] [output.mp4] [interval_ms]"
99
+ ;;
100
+ esac
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env bash
2
+ # Revive Watcher — monitors a cmux workspace and auto-sends a resume command after /clear
3
+ #
4
+ # Usage: revive-watcher.sh [options]
5
+ # --workspace <id> Workspace to monitor (default: auto-detect current)
6
+ # --surface <id> Surface to monitor (default: auto-detect Claude surface)
7
+ # --command <cmd> Command to send on resume (default: /coordinate)
8
+ # --interval <secs> Poll interval in seconds (default: 10)
9
+ # --once Send the resume command once, then exit
10
+ # --quiet Suppress output except errors
11
+ #
12
+ # The watcher detects when Claude Code is idle (fresh prompt after /clear) and
13
+ # sends the configured resume command to restart the session.
14
+
15
+ set -euo pipefail
16
+
17
+ # Defaults
18
+ WORKSPACE=""
19
+ SURFACE=""
20
+ RESUME_CMD="/coordinate"
21
+ INTERVAL=10
22
+ ONCE=false
23
+ QUIET=false
24
+ PID_FILE="${HOME}/.claude/revive-watcher.pid"
25
+
26
+ log() {
27
+ if [ "$QUIET" = false ]; then
28
+ echo "$(date '+%H:%M:%S') [revive] $*"
29
+ fi
30
+ }
31
+
32
+ err() {
33
+ echo "$(date '+%H:%M:%S') [revive] ERROR: $*" >&2
34
+ }
35
+
36
+ # Parse args
37
+ while [[ $# -gt 0 ]]; do
38
+ case "$1" in
39
+ --workspace) WORKSPACE="$2"; shift 2 ;;
40
+ --surface) SURFACE="$2"; shift 2 ;;
41
+ --command) RESUME_CMD="$2"; shift 2 ;;
42
+ --interval) INTERVAL="$2"; shift 2 ;;
43
+ --once) ONCE=true; shift ;;
44
+ --quiet) QUIET=true; shift ;;
45
+ --help|-h)
46
+ head -14 "$0" | tail -12
47
+ exit 0
48
+ ;;
49
+ *) err "Unknown option: $1"; exit 1 ;;
50
+ esac
51
+ done
52
+
53
+ # Auto-detect workspace if not specified
54
+ if [ -z "$WORKSPACE" ]; then
55
+ # Try to find the current workspace from cmux
56
+ CURRENT=$(cmux list-workspaces 2>/dev/null | grep -o 'workspace:[0-9]*' | head -1 || true)
57
+ if [ -z "$CURRENT" ]; then
58
+ err "Could not auto-detect workspace. Use --workspace <id>"
59
+ exit 1
60
+ fi
61
+ WORKSPACE="$CURRENT"
62
+ log "Auto-detected workspace: $WORKSPACE"
63
+ fi
64
+
65
+ # Auto-detect Claude surface if not specified
66
+ if [ -z "$SURFACE" ]; then
67
+ # Read all surfaces and find the one running Claude
68
+ SURFACES=$(cmux list-workspaces 2>/dev/null || true)
69
+ # Try common Claude surface patterns
70
+ for s in "surface:5" "surface:3" "surface:1"; do
71
+ SCREEN=$(cmux read-screen --workspace "$WORKSPACE" --surface "$s" --lines 5 2>/dev/null || true)
72
+ if echo "$SCREEN" | grep -qi "claude\|bypass\|permission\|❯"; then
73
+ SURFACE="$s"
74
+ break
75
+ fi
76
+ done
77
+ if [ -z "$SURFACE" ]; then
78
+ # Default to surface:5 (most common for Claude in cmux)
79
+ SURFACE="surface:5"
80
+ log "Could not auto-detect surface, defaulting to $SURFACE"
81
+ else
82
+ log "Auto-detected Claude surface: $SURFACE"
83
+ fi
84
+ fi
85
+
86
+ # Write PID file for management
87
+ echo $$ > "$PID_FILE"
88
+ trap 'rm -f "$PID_FILE"; log "Watcher stopped."; exit 0' EXIT INT TERM
89
+
90
+ log "Watcher started"
91
+ log " Workspace: $WORKSPACE"
92
+ log " Surface: $SURFACE"
93
+ log " Command: $RESUME_CMD"
94
+ log " Interval: ${INTERVAL}s"
95
+ log " PID: $$"
96
+
97
+ # Track state to avoid spamming
98
+ LAST_SEND_TIME=0
99
+ COOLDOWN=60 # seconds between sends
100
+
101
+ is_idle() {
102
+ local screen
103
+ screen=$(cmux read-screen --workspace "$WORKSPACE" --surface "$SURFACE" --lines 5 2>/dev/null || true)
104
+
105
+ if [ -z "$screen" ]; then
106
+ return 1 # Can't read screen — not idle
107
+ fi
108
+
109
+ # Idle = has the prompt marker but NO "esc to interrupt" (which means Claude is working)
110
+ if echo "$screen" | grep -q "esc to interrupt"; then
111
+ return 1 # Claude is actively working
112
+ fi
113
+
114
+ # Check for idle prompt indicators:
115
+ # - Empty prompt (❯ with nothing after)
116
+ # - "bypass permissions" text (settings line visible = idle)
117
+ # - Just the prompt line with no active output
118
+ if echo "$screen" | grep -qE "(^❯\s*$|bypass permissions|What can I help)"; then
119
+ return 0 # Idle
120
+ fi
121
+
122
+ return 1 # Not clearly idle
123
+ }
124
+
125
+ while true; do
126
+ sleep "$INTERVAL"
127
+
128
+ if is_idle; then
129
+ NOW=$(date +%s)
130
+ ELAPSED=$((NOW - LAST_SEND_TIME))
131
+
132
+ if [ "$ELAPSED" -ge "$COOLDOWN" ]; then
133
+ log "Workspace idle — sending resume command: $RESUME_CMD"
134
+ cmux send --workspace "$WORKSPACE" --surface "$SURFACE" "$RESUME_CMD" 2>/dev/null || {
135
+ err "Failed to send to $WORKSPACE $SURFACE"
136
+ continue
137
+ }
138
+ sleep 1
139
+ cmux send-key --workspace "$WORKSPACE" --surface "$SURFACE" Enter 2>/dev/null || true
140
+ LAST_SEND_TIME=$NOW
141
+ log "Resume command sent. Cooling down ${COOLDOWN}s."
142
+
143
+ if [ "$ONCE" = true ]; then
144
+ log "One-shot mode — exiting."
145
+ exit 0
146
+ fi
147
+ fi
148
+ fi
149
+ done