Skip to content

Commit daa7c7c

Browse files
committed
Use config-driven project name for client-time script and add detailed Claude Code SDK migration plans
Add ClaudeCodeTokenCounter and FileHandler modules for token tracking and file processing - **ClaudeCodeTokenCounter.js**: Implements accurate token counting for Claude AI using cached and dynamic calculations. Includes breakdown formats, memory file handling, and conversation token tracking. - **FileHandler.js**: Unified file processing module for Telegram, supporting documents, videos, audio, animations, and stickers. Includes validation, download, and session-based cleanups.
1 parent f3bc18c commit daa7c7c

29 files changed

Lines changed: 6477 additions & 163 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,4 @@ claude-flow
9090
claude-flow.bat
9191
claude-flow.ps1
9292
hive-mind-prompt-*.txt
93+
*-time-report-*.csv

ACTIVITY_WATCH_REPORT_USAGE.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# ActivityWatch Time Report Utilities
2+
3+
## Quick Start
4+
5+
**For weekly reports (most common):**
6+
```bash
7+
node activity-time-report.js 7
8+
```
9+
10+
**For custom period:**
11+
```bash
12+
node activity-time-report.js [days] [category] [project]
13+
```
14+
15+
## Available Utilities
16+
17+
### 1. `activity-time-report.js` - Main Report Generator
18+
19+
**Purpose:** Generate comprehensive time reports combining:
20+
- Claude AI bot sessions
21+
- ActivityWatch WORK work events (PhpStorm, Cursor, browser with WORK indicators)
22+
23+
**Examples:**
24+
```bash
25+
# Last 7 days (default)
26+
node activity-time-report.js
27+
28+
# Last 14 days
29+
node activity-time-report.js 14
30+
31+
# Last 30 days with specific category
32+
node activity-time-report.js 30 WORK
33+
34+
# Specific project only
35+
node activity-time-report.js 7 WORK client-project
36+
```
37+
38+
**Output:**
39+
- Console report with daily breakdown
40+
- CSV file: `activity-time-report-YYYY-MM-DD.csv`
41+
42+
### 2. `activity-watch-explorer.js` - Data Explorer
43+
44+
**Purpose:** Understand what applications and activities ActivityWatch is tracking
45+
46+
**Example:**
47+
```bash
48+
# Explore last 24 hours
49+
node activity-watch-explorer.js 24
50+
```
51+
52+
**Use when:** You need to understand how your work is being categorized
53+
54+
### 3. `time-report.js` - Bot Sessions Only
55+
56+
**Purpose:** Report only Claude bot sessions (original utility)
57+
58+
**Example:**
59+
```bash
60+
node time-report.js 7 client-project
61+
```
62+
63+
## What Gets Tracked as WORK
64+
65+
The report automatically includes:
66+
67+
**Development Apps:**
68+
- jetbrains-phpstorm (PhpStorm)
69+
- cursor (Cursor editor)
70+
- Code editors and terminals
71+
72+
**Browser Activities:**
73+
- Pages with "group client-project" in title
74+
- Pages with "client-project" in title
75+
- Claude AI sessions
76+
- ActivityWatch interface
77+
78+
**Bot Sessions:**
79+
- All Claude AI assistant interactions through Telegram bot
80+
81+
## Report Output
82+
83+
**Console format:**
84+
```
85+
📊 WORK Time Report (last 7 days)
86+
📅 Daily Breakdown:
87+
Date | Bot Sessions | Bot Time | AW Events | AW Time | Total Time
88+
2025-08-27 | 13 | 0.76h | 261 | 1.21h | 1.97h
89+
90+
📈 SUMMARY:
91+
Total Time: 24.84 hours
92+
Average per day: 3.55 hours/day
93+
```
94+
95+
**CSV format:** Ready for client billing with detailed breakdown
96+
97+
## Troubleshooting
98+
99+
**No data found:**
100+
- Check ActivityWatch is running: `curl http://localhost:5600/api/0/info`
101+
- Verify time range (try shorter period)
102+
- Use explorer to see what's being tracked
103+
104+
**Missing activities:**
105+
- Use `activity-watch-explorer.js` to see all tracked apps
106+
- Update filtering patterns in `activity-time-report.js` if needed
107+
108+
## Quick Commands
109+
110+
```bash
111+
# Most common: weekly report
112+
node activity-time-report.js 7
113+
114+
# Monthly report for client billing
115+
node activity-time-report.js 30
116+
117+
# Understand current tracking
118+
node activity-watch-explorer.js 48
119+
```

ActivityWatchIntegration.js

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const axios = require('axios');
2+
const config = require('./config.json');
23

34
/**
45
* ActivityWatch Integration
@@ -96,9 +97,9 @@ class ActivityWatchIntegration {
9697
}
9798
};
9899

99-
// Retry mechanism for network issues
100+
// Enhanced retry mechanism for network issues
100101
let response;
101-
const maxRetries = 3;
102+
const maxRetries = 5;
102103
let lastError;
103104

104105
for (let attempt = 1; attempt <= maxRetries; attempt++) {
@@ -108,7 +109,9 @@ class ActivityWatchIntegration {
108109
[event],
109110
{
110111
headers: { 'Content-Type': 'application/json' },
111-
timeout: 5000
112+
timeout: 10000, // Increased timeout
113+
// Add connection keep-alive
114+
httpAgent: new (require('http').Agent)({ keepAlive: true }),
112115
}
113116
);
114117
break; // Success, exit retry loop
@@ -120,13 +123,16 @@ class ActivityWatchIntegration {
120123
attemptError.code === 'ECONNRESET' ||
121124
attemptError.code === 'ENOTFOUND' ||
122125
attemptError.code === 'ETIMEDOUT' ||
126+
attemptError.code === 'ECONNREFUSED' ||
123127
attemptError.message.includes('socket hang up') ||
124-
attemptError.message.includes('timeout')
128+
attemptError.message.includes('timeout') ||
129+
attemptError.message.includes('ECONNRESET')
125130
);
126131

127132
if (isRetryable && attempt < maxRetries) {
128-
console.warn(`[ActivityWatch] Recording attempt ${attempt} failed (${attemptError.message}), retrying...`);
129-
await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); // Exponential backoff
133+
const backoffDelay = Math.min(1000 * Math.pow(2, attempt - 1), 5000); // Exponential backoff, max 5s
134+
console.warn(`[ActivityWatch] Main recording attempt ${attempt} failed (${attemptError.message}), retrying in ${backoffDelay}ms...`);
135+
await new Promise(resolve => setTimeout(resolve, backoffDelay));
130136
continue;
131137
} else {
132138
// Not retryable or max retries exceeded
@@ -166,53 +172,76 @@ class ActivityWatchIntegration {
166172
try {
167173
const windowBucket = `aw-watcher-window_${this.hostname}`;
168174

169-
// Create fake window event that looks like a real application
175+
// Create fake window event with configured app name for work categorization
176+
// Using more generic IT/development-related app names
170177
const windowEvent = {
171178
timestamp: timestamp,
172179
duration: durationSeconds,
173180
data: {
174-
app: `${projectName.toLowerCase()}-ai-assistant`,
175-
title: `${projectName} AI Assistant - Session ${sessionId?.slice(-8) || 'work'}`
181+
app: config.activityWatch.integration.fakeAppName,
182+
title: `${config.activityWatch.integration.sessionTitleTemplate} - ${projectName} AI Assistant Session ${sessionId?.slice(-8) || 'work'}`
176183
}
177184
};
178185

179-
// Try to record to window bucket
180-
try {
181-
await axios.post(
182-
`${this.baseUrl}/buckets/${windowBucket}/events`,
183-
[windowEvent],
184-
{
185-
headers: { 'Content-Type': 'application/json' },
186-
timeout: 5000
187-
}
188-
);
189-
console.log(`[ActivityWatch] Window event recorded: ${windowEvent.data.app} | ${durationSeconds.toFixed(1)}s`);
190-
} catch (error) {
191-
// If window bucket doesn't exist, try to create it first
192-
if (error.response?.status === 404) {
193-
console.log(`[ActivityWatch] Creating window bucket: ${windowBucket}`);
194-
await axios.post(`${this.baseUrl}/buckets/${windowBucket}`, {
195-
type: 'currentwindow',
196-
client: 'aw-watcher-window',
197-
hostname: this.hostname
198-
});
199-
200-
// Retry recording after creating bucket
186+
// Enhanced retry mechanism for socket issues
187+
const maxRetries = 5;
188+
let lastError;
189+
190+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
191+
try {
201192
await axios.post(
202193
`${this.baseUrl}/buckets/${windowBucket}/events`,
203194
[windowEvent],
204195
{
205196
headers: { 'Content-Type': 'application/json' },
206-
timeout: 5000
197+
timeout: 10000, // Increased timeout
198+
// Add connection keep-alive
199+
httpAgent: new (require('http').Agent)({ keepAlive: true }),
207200
}
208201
);
209-
console.log(`[ActivityWatch] Window event recorded after bucket creation: ${windowEvent.data.app}`);
210-
} else {
211-
throw error;
202+
console.log(`[ActivityWatch] Window event recorded: ${windowEvent.data.app} | ${durationSeconds.toFixed(1)}s | attempt ${attempt}`);
203+
return; // Success, exit function
204+
} catch (error) {
205+
lastError = error;
206+
207+
// Handle bucket creation first
208+
if (error.response?.status === 404 && attempt === 1) {
209+
console.log(`[ActivityWatch] Creating window bucket: ${windowBucket}`);
210+
try {
211+
await axios.post(`${this.baseUrl}/buckets/${windowBucket}`, {
212+
type: 'currentwindow',
213+
client: 'aw-watcher-window',
214+
hostname: this.hostname
215+
}, { timeout: 10000 });
216+
continue; // Retry recording after creating bucket
217+
} catch (bucketError) {
218+
console.warn(`[ActivityWatch] Could not create window bucket: ${bucketError.message}`);
219+
}
220+
}
221+
222+
// Handle retryable errors
223+
const isRetryable = (
224+
error.code === 'ECONNRESET' ||
225+
error.code === 'ENOTFOUND' ||
226+
error.code === 'ETIMEDOUT' ||
227+
error.code === 'ECONNREFUSED' ||
228+
error.message.includes('socket hang up') ||
229+
error.message.includes('timeout') ||
230+
error.message.includes('ECONNRESET')
231+
);
232+
233+
if (isRetryable && attempt < maxRetries) {
234+
const backoffDelay = Math.min(1000 * Math.pow(2, attempt - 1), 5000); // Exponential backoff, max 5s
235+
console.warn(`[ActivityWatch] Window event attempt ${attempt} failed (${error.message}), retrying in ${backoffDelay}ms...`);
236+
await new Promise(resolve => setTimeout(resolve, backoffDelay));
237+
continue;
238+
} else {
239+
throw error;
240+
}
212241
}
213242
}
214243
} catch (error) {
215-
console.warn(`[ActivityWatch] Could not record window event: ${error.message}`);
244+
console.warn(`[ActivityWatch] Could not record window event after ${maxRetries} attempts: ${error.message}`);
216245
}
217246
}
218247

0 commit comments

Comments
 (0)