@dalmasonto/taskflow-mcp 1.0.1 → 1.0.3
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/dist/index.js +14 -6
- package/dist/tools/tasks.js +10 -0
- package/dist/tools/timer.js +17 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,21 +3,29 @@ import { startSSEServer } from './sse.js';
|
|
|
3
3
|
import { getDb } from './db.js';
|
|
4
4
|
import { broadcast } from './sse.js';
|
|
5
5
|
const httpOnly = process.argv.includes('--http-only');
|
|
6
|
-
// Close
|
|
7
|
-
//
|
|
6
|
+
// Close orphaned sessions left from a previous crash.
|
|
7
|
+
// Uses a grace period so that sessions from the current conversation
|
|
8
|
+
// (stdio transport restarts the server on each tool call) are preserved.
|
|
9
|
+
const ORPHAN_GRACE_MS = 5 * 60 * 1000; // 5 minutes
|
|
8
10
|
function cleanupOrphanedSessions() {
|
|
9
11
|
const db = getDb();
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
+
const nowMs = Date.now();
|
|
13
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
14
|
+
const cutoff = new Date(nowMs - ORPHAN_GRACE_MS).toISOString();
|
|
15
|
+
// Only close sessions that started more than ORPHAN_GRACE_MS ago
|
|
16
|
+
const orphaned = db.prepare('SELECT * FROM sessions WHERE end IS NULL AND start < ?').all(cutoff);
|
|
12
17
|
if (orphaned.length === 0)
|
|
13
18
|
return;
|
|
14
|
-
|
|
19
|
+
// Close each orphaned session with end = now (preserves the real elapsed time)
|
|
20
|
+
for (const session of orphaned) {
|
|
21
|
+
db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(nowIso, session.id);
|
|
22
|
+
}
|
|
15
23
|
// Set orphaned in_progress tasks back to paused
|
|
16
24
|
const taskIds = [...new Set(orphaned.map(s => s.task_id))];
|
|
17
25
|
for (const taskId of taskIds) {
|
|
18
26
|
const task = db.prepare('SELECT status FROM tasks WHERE id = ?').get(taskId);
|
|
19
27
|
if (task?.status === 'in_progress') {
|
|
20
|
-
db.prepare("UPDATE tasks SET status = 'paused', updated_at = ? WHERE id = ?").run(
|
|
28
|
+
db.prepare("UPDATE tasks SET status = 'paused', updated_at = ? WHERE id = ?").run(nowIso, taskId);
|
|
21
29
|
broadcast('task_updated', { entity: 'task', action: 'task_status_changed', payload: db.prepare('SELECT * FROM tasks WHERE id = ?').get(taskId) });
|
|
22
30
|
}
|
|
23
31
|
}
|
package/dist/tools/tasks.js
CHANGED
|
@@ -258,6 +258,16 @@ export async function updateTaskStatus(params) {
|
|
|
258
258
|
return errorResponse(`Cannot transition from "${currentStatus}" to "${params.status}"`, 'INVALID_TRANSITION');
|
|
259
259
|
}
|
|
260
260
|
const ts = now();
|
|
261
|
+
// Auto-start a timer session when entering in_progress
|
|
262
|
+
if (params.status === 'in_progress') {
|
|
263
|
+
const existingOpen = db.prepare('SELECT id FROM sessions WHERE task_id = ? AND end IS NULL').get(params.id);
|
|
264
|
+
if (!existingOpen) {
|
|
265
|
+
const result = db.prepare('INSERT INTO sessions (task_id, start, end) VALUES (?, ?, ?)').run(params.id, ts, null);
|
|
266
|
+
const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(result.lastInsertRowid);
|
|
267
|
+
logActivity('timer_started', row.title, { entityType: 'task', entityId: params.id });
|
|
268
|
+
broadcastChange('timer', 'timer_started', { task_id: params.id, session, task_status: 'in_progress' });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
261
271
|
// Auto-close any active timer session when leaving an active state
|
|
262
272
|
const terminalStatuses = ['done', 'partial_done', 'blocked', 'paused'];
|
|
263
273
|
if (terminalStatuses.includes(params.status)) {
|
package/dist/tools/timer.js
CHANGED
|
@@ -35,12 +35,23 @@ export async function pauseTimer(params) {
|
|
|
35
35
|
return errorResponse('Task not found', 'NOT_FOUND');
|
|
36
36
|
// Find open session
|
|
37
37
|
const session = db.prepare('SELECT * FROM sessions WHERE task_id = ? AND end IS NULL').get(params.task_id);
|
|
38
|
-
if (!session)
|
|
39
|
-
return errorResponse('No active timer session for this task', 'NO_ACTIVE_SESSION');
|
|
40
38
|
const endTime = now();
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
let duration;
|
|
40
|
+
let closedSession;
|
|
41
|
+
if (session) {
|
|
42
|
+
// Normal path — close the active session
|
|
43
|
+
duration = new Date(endTime).getTime() - new Date(session.start).getTime();
|
|
44
|
+
db.prepare('UPDATE sessions SET end = ? WHERE id = ?').run(endTime, session.id);
|
|
45
|
+
closedSession = { ...session, end: endTime };
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Fallback — session already closed (orphan cleanup, auto-close)
|
|
49
|
+
const lastSession = db.prepare('SELECT * FROM sessions WHERE task_id = ? ORDER BY start DESC LIMIT 1').get(params.task_id);
|
|
50
|
+
if (!lastSession)
|
|
51
|
+
return errorResponse('No timer sessions found for this task', 'NO_ACTIVE_SESSION');
|
|
52
|
+
duration = new Date(lastSession.end).getTime() - new Date(lastSession.start).getTime();
|
|
53
|
+
closedSession = lastSession;
|
|
54
|
+
}
|
|
44
55
|
// Update task to paused
|
|
45
56
|
db.prepare('UPDATE tasks SET status = ?, updated_at = ? WHERE id = ?').run('paused', endTime, params.task_id);
|
|
46
57
|
logActivity('timer_paused', task.title, {
|
|
@@ -48,7 +59,7 @@ export async function pauseTimer(params) {
|
|
|
48
59
|
entityType: 'task',
|
|
49
60
|
entityId: params.task_id,
|
|
50
61
|
});
|
|
51
|
-
const pausedSession = { ...
|
|
62
|
+
const pausedSession = { ...closedSession, duration };
|
|
52
63
|
broadcastChange('timer', 'timer_paused', { task_id: params.task_id, session: pausedSession, task_status: 'paused' });
|
|
53
64
|
return successResponse(pausedSession);
|
|
54
65
|
}
|