clocktopus 1.10.3 → 1.10.4
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 +54 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -268,44 +268,70 @@ program
|
|
|
268
268
|
}
|
|
269
269
|
// Safer restart w/ cooldown; only resume a recent auto-completed session
|
|
270
270
|
let lastResumeAt = 0;
|
|
271
|
+
let resuming = false;
|
|
271
272
|
const RESUME_COOLDOWN_MS = 10000;
|
|
273
|
+
// Shared between lock-state and idle-time watchers so a resume triggered
|
|
274
|
+
// by one path disarms the other (otherwise both fire and we get two starts).
|
|
275
|
+
let isLocked = false;
|
|
276
|
+
let lastIdle = false;
|
|
272
277
|
async function safeRestartTimerIfNeeded() {
|
|
273
278
|
const now = Date.now();
|
|
274
|
-
if (now - lastResumeAt < RESUME_COOLDOWN_MS)
|
|
275
|
-
return;
|
|
276
|
-
// Small delay lets services settle after wake/activity
|
|
277
|
-
await sleep(800);
|
|
278
|
-
const latestSession = getLatestSession();
|
|
279
|
-
if (!latestSession)
|
|
280
|
-
return;
|
|
281
|
-
const twoHoursAgo = Date.now() - 2 * 60 * 60 * 1000;
|
|
282
|
-
const completedMs = latestSession.completedAt ? new Date(latestSession.completedAt).getTime() : 0;
|
|
283
|
-
if (!latestSession.isAutoCompleted || completedMs <= twoHoursAgo)
|
|
279
|
+
if (resuming || now - lastResumeAt < RESUME_COOLDOWN_MS)
|
|
284
280
|
return;
|
|
285
|
-
|
|
286
|
-
|
|
281
|
+
resuming = true;
|
|
282
|
+
// Gate concurrent callers immediately; refine on success/failure below.
|
|
283
|
+
lastResumeAt = now;
|
|
284
|
+
try {
|
|
285
|
+
// Small delay lets services settle after wake/activity
|
|
286
|
+
await sleep(800);
|
|
287
|
+
const latestSession = getLatestSession();
|
|
288
|
+
if (!latestSession)
|
|
287
289
|
return;
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
+
const twoHoursAgo = Date.now() - 2 * 60 * 60 * 1000;
|
|
291
|
+
const completedMs = latestSession.completedAt ? new Date(latestSession.completedAt).getTime() : 0;
|
|
292
|
+
if (!latestSession.isAutoCompleted || completedMs <= twoHoursAgo)
|
|
293
|
+
return;
|
|
294
|
+
if (isClockifyEnabled()) {
|
|
295
|
+
if (!latestSession.projectId)
|
|
296
|
+
return;
|
|
297
|
+
const activeEntry = await (await clockify()).getActiveTimer(workspaceId, userId);
|
|
298
|
+
if (activeEntry) {
|
|
299
|
+
isLocked = false;
|
|
300
|
+
lastIdle = false;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
await (await clockify()).startTimer(workspaceId, latestSession.projectId, latestSession.description, latestSession.jiraTicket ?? undefined);
|
|
304
|
+
console.log(chalk.green('Timer restarted for the last used project.'));
|
|
305
|
+
lastResumeAt = Date.now();
|
|
306
|
+
isLocked = false;
|
|
307
|
+
lastIdle = false;
|
|
290
308
|
return;
|
|
291
|
-
|
|
292
|
-
|
|
309
|
+
}
|
|
310
|
+
// Jira-only resume: new DB session with a fresh uuid, same ticket.
|
|
311
|
+
// Re-read latest to guard against a concurrent insert from another path.
|
|
312
|
+
if (!latestSession.jiraTicket)
|
|
313
|
+
return;
|
|
314
|
+
const fresh = getLatestSession();
|
|
315
|
+
if (fresh && !fresh.completedAt) {
|
|
316
|
+
isLocked = false;
|
|
317
|
+
lastIdle = false;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const { v4: uuidv4 } = await import('uuid');
|
|
321
|
+
const { logSessionStart } = await import('./lib/db.js');
|
|
322
|
+
const sessionId = uuidv4();
|
|
323
|
+
const startedAt = new Date().toISOString();
|
|
324
|
+
logSessionStart(sessionId, latestSession.projectId ?? null, latestSession.description, startedAt, latestSession.jiraTicket);
|
|
325
|
+
console.log(chalk.green(`Resumed Jira timer for ${latestSession.jiraTicket}.`));
|
|
293
326
|
lastResumeAt = Date.now();
|
|
294
|
-
|
|
327
|
+
isLocked = false;
|
|
328
|
+
lastIdle = false;
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
resuming = false;
|
|
295
332
|
}
|
|
296
|
-
// Jira-only resume: new DB session with a fresh uuid, same ticket
|
|
297
|
-
if (!latestSession.jiraTicket)
|
|
298
|
-
return;
|
|
299
|
-
const { v4: uuidv4 } = await import('uuid');
|
|
300
|
-
const { logSessionStart } = await import('./lib/db.js');
|
|
301
|
-
const sessionId = uuidv4();
|
|
302
|
-
const startedAt = new Date().toISOString();
|
|
303
|
-
logSessionStart(sessionId, latestSession.projectId ?? null, latestSession.description, startedAt, latestSession.jiraTicket);
|
|
304
|
-
console.log(chalk.green(`Resumed Jira timer for ${latestSession.jiraTicket}.`));
|
|
305
|
-
lastResumeAt = Date.now();
|
|
306
333
|
}
|
|
307
334
|
console.log(chalk.blue('Monitoring display events (Unified Log) and idle time...'));
|
|
308
|
-
let isLocked = false;
|
|
309
335
|
let pollInterval = null;
|
|
310
336
|
console.log(chalk.blue('Monitoring display/lock state (macos-notification-state) and idle time...'));
|
|
311
337
|
try {
|
|
@@ -351,7 +377,6 @@ program
|
|
|
351
377
|
console.error(err);
|
|
352
378
|
}
|
|
353
379
|
const IDLE_THRESHOLD_SECONDS = 300; // 5 minutes
|
|
354
|
-
let lastIdle = false;
|
|
355
380
|
const idleInterval = setInterval(async () => {
|
|
356
381
|
try {
|
|
357
382
|
const idleModule = await import('desktop-idle');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clocktopus",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.4",
|
|
4
4
|
"description": "Time-tracking automation for Clockify with idle monitoring, Jira integration, Google Calendar sync, CLI, web dashboard, and desktop app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|