clocktopus 1.0.7 → 1.1.1

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/README.md CHANGED
@@ -47,16 +47,18 @@ That's it. Start/stop timers from the Home tab.
47
47
 
48
48
  ### Commands
49
49
 
50
- | Command | Description |
51
- | ----------------------- | ------------------------------------ |
52
- | `clocktopus dash` | Start dashboard (foreground) |
53
- | `clocktopus serve` | Start dashboard as background daemon |
54
- | `clocktopus serve:stop` | Stop the dashboard daemon |
55
- | `clocktopus serve:logs` | View dashboard daemon logs |
56
- | `clocktopus start` | Start a timer (interactive) |
57
- | `clocktopus stop` | Stop the current timer |
58
- | `clocktopus status` | Check timer status |
59
- | `clocktopus monitor` | Start idle monitor (foreground) |
50
+ | Command | Description |
51
+ | ------------------------- | --------------------------------------- |
52
+ | `clocktopus dash` | Start dashboard (foreground) |
53
+ | `clocktopus serve` | Start dashboard as background daemon |
54
+ | `clocktopus serve:stop` | Stop the dashboard daemon |
55
+ | `clocktopus serve:logs` | View dashboard daemon logs |
56
+ | `clocktopus start` | Start a timer (interactive) |
57
+ | `clocktopus stop` | Stop the current timer |
58
+ | `clocktopus status` | Check timer status |
59
+ | `clocktopus monitor` | Start idle monitor as background daemon |
60
+ | `clocktopus monitor:stop` | Stop the idle monitor |
61
+ | `clocktopus monitor:logs` | View idle monitor logs |
60
62
 
61
63
  ### Desktop App (macOS)
62
64
 
@@ -5,7 +5,8 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
  const SCRIPT_PATH = path.resolve(__dirname, '../../index.js');
8
- const PM2_NAME = 'clocktopus-monitor';
8
+ const isDev = SCRIPT_PATH.includes('/Projects/') || SCRIPT_PATH.includes('/src/');
9
+ const PM2_NAME = isDev ? 'clocktopus-monitor-dev' : 'clocktopus-monitor';
9
10
  const monitorRoutes = new Hono();
10
11
  function pm2Exec(command) {
11
12
  try {
@@ -43,7 +44,7 @@ monitorRoutes.post('/monitor/start', (c) => {
43
44
  execSync(`bunx pm2 delete ${PM2_NAME}`, { stdio: 'ignore' });
44
45
  }
45
46
  catch { }
46
- const result = pm2Exec(`bunx pm2 start ${SCRIPT_PATH} --name ${PM2_NAME} --interpreter ${bunPath} -- monitor`);
47
+ const result = pm2Exec(`bunx pm2 start ${SCRIPT_PATH} --name ${PM2_NAME} --interpreter ${bunPath} -- monitor:run`);
47
48
  return c.json(result);
48
49
  });
49
50
  monitorRoutes.post('/monitor/stop', (c) => {
package/dist/index.js CHANGED
@@ -137,8 +137,8 @@ function sleep(ms) {
137
137
  return new Promise((res) => setTimeout(res, ms));
138
138
  }
139
139
  program
140
- .command('monitor')
141
- .description('Monitor system idle time and screen state, and stop the Clockify timer if idle or screen is off.')
140
+ .command('monitor:run', { hidden: true })
141
+ .description('Run monitor in foreground (used by PM2).')
142
142
  .action(async () => {
143
143
  const { workspaceId, userId } = await getWorkspaceAndUser();
144
144
  async function stopTimerAndLog(reason) {
@@ -201,6 +201,9 @@ program
201
201
  if (!getSessionState) {
202
202
  throw new Error('getSessionState not found in module');
203
203
  }
204
+ // Verify the native addon actually works before setting up polling
205
+ const initialState = getSessionState();
206
+ console.log(chalk.gray(`Initial session state: ${initialState}`));
204
207
  pollInterval = setInterval(async () => {
205
208
  try {
206
209
  const state = getSessionState();
@@ -217,6 +220,11 @@ program
217
220
  }
218
221
  catch (error) {
219
222
  console.error('Error polling session state:', error);
223
+ if (pollInterval) {
224
+ clearInterval(pollInterval);
225
+ pollInterval = null;
226
+ console.error(chalk.red('Display monitoring disabled due to repeated errors.'));
227
+ }
220
228
  }
221
229
  }, 3000);
222
230
  }
@@ -275,6 +283,56 @@ program
275
283
  .action(() => {
276
284
  startDashboard();
277
285
  });
286
+ const isDev = __dirname.includes('/Projects/') || __dirname.includes('/src/');
287
+ const MONITOR_PM2_NAME = isDev ? 'clocktopus-monitor-dev' : 'clocktopus-monitor';
288
+ const DASH_PM2_NAME = isDev ? 'clocktopus-dash-dev' : 'clocktopus-dash';
289
+ program
290
+ .command('monitor')
291
+ .description('Start idle monitor as a background daemon.')
292
+ .action(async () => {
293
+ const { execSync } = await import('child_process');
294
+ const bunPath = execSync('which bun', { encoding: 'utf-8' }).trim();
295
+ const scriptPath = path.join(__dirname, 'index.js');
296
+ try {
297
+ try {
298
+ execSync(`bunx pm2 delete ${MONITOR_PM2_NAME}`, { stdio: 'ignore' });
299
+ }
300
+ catch { }
301
+ execSync(`bunx pm2 start ${scriptPath} --name ${MONITOR_PM2_NAME} --interpreter ${bunPath} -- monitor:run`, {
302
+ stdio: 'inherit',
303
+ });
304
+ console.log(chalk.green('Idle monitor started in background.'));
305
+ console.log(chalk.gray(' Stop: clocktopus monitor:stop'));
306
+ console.log(chalk.gray(' Logs: clocktopus monitor:logs'));
307
+ }
308
+ catch {
309
+ console.error(chalk.red('Failed to start monitor.'));
310
+ }
311
+ });
312
+ program
313
+ .command('monitor:stop')
314
+ .description('Stop the idle monitor daemon.')
315
+ .action(async () => {
316
+ const { execSync } = await import('child_process');
317
+ try {
318
+ execSync(`bunx pm2 stop ${MONITOR_PM2_NAME}`, { stdio: 'inherit' });
319
+ }
320
+ catch {
321
+ console.log(chalk.yellow('Monitor is not running.'));
322
+ }
323
+ });
324
+ program
325
+ .command('monitor:logs')
326
+ .description('Show idle monitor logs.')
327
+ .action(async () => {
328
+ const { execSync } = await import('child_process');
329
+ try {
330
+ execSync(`bunx pm2 logs ${MONITOR_PM2_NAME} --lines 50`, { stdio: 'inherit' });
331
+ }
332
+ catch {
333
+ console.log(chalk.yellow('Monitor is not running.'));
334
+ }
335
+ });
278
336
  program
279
337
  .command('serve')
280
338
  .description('Start dashboard as a background daemon (PM2).')
@@ -283,12 +341,11 @@ program
283
341
  const bunPath = execSync('which bun', { encoding: 'utf-8' }).trim();
284
342
  const scriptPath = path.join(__dirname, 'index.js');
285
343
  try {
286
- // Stop existing if running
287
344
  try {
288
- execSync('bunx pm2 delete clocktopus-dash', { stdio: 'ignore' });
345
+ execSync(`bunx pm2 delete ${DASH_PM2_NAME}`, { stdio: 'ignore' });
289
346
  }
290
347
  catch { }
291
- execSync(`bunx pm2 start ${scriptPath} --name clocktopus-dash --interpreter ${bunPath} -- dash`, {
348
+ execSync(`bunx pm2 start ${scriptPath} --name ${DASH_PM2_NAME} --interpreter ${bunPath} -- dash`, {
292
349
  stdio: 'inherit',
293
350
  });
294
351
  console.log(chalk.green('Dashboard running at http://localhost:4001'));
@@ -305,7 +362,7 @@ program
305
362
  .action(async () => {
306
363
  const { execSync } = await import('child_process');
307
364
  try {
308
- execSync('bunx pm2 stop clocktopus-dash', { stdio: 'inherit' });
365
+ execSync(`bunx pm2 stop ${DASH_PM2_NAME}`, { stdio: 'inherit' });
309
366
  }
310
367
  catch {
311
368
  console.log(chalk.yellow('Dashboard is not running.'));
@@ -317,7 +374,7 @@ program
317
374
  .action(async () => {
318
375
  const { execSync } = await import('child_process');
319
376
  try {
320
- execSync('bunx pm2 logs clocktopus-dash --lines 50', { stdio: 'inherit' });
377
+ execSync(`bunx pm2 logs ${DASH_PM2_NAME} --lines 50`, { stdio: 'inherit' });
321
378
  }
322
379
  catch {
323
380
  console.log(chalk.yellow('Dashboard is not running.'));
package/dist/lib/db.js CHANGED
@@ -2,7 +2,15 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { Database } from 'bun:sqlite';
4
4
  import { z } from 'zod';
5
- const DB_DIR = path.join(process.cwd(), 'data/db');
5
+ function getDataDir() {
6
+ const scriptDir = path.dirname(new URL(import.meta.url).pathname);
7
+ const isDev = scriptDir.includes('/Projects/') || scriptDir.includes('/src/');
8
+ if (isDev) {
9
+ return path.join(process.cwd(), 'data/db');
10
+ }
11
+ return path.join(process.env.HOME || '~', '.clocktopus', 'data');
12
+ }
13
+ const DB_DIR = getDataDir();
6
14
  const DB_PATH = path.join(DB_DIR, 'sessions.db');
7
15
  if (!fs.existsSync(DB_DIR)) {
8
16
  fs.mkdirSync(DB_DIR, { recursive: true });
package/dist/lib/jira.js CHANGED
@@ -21,7 +21,10 @@ async function jiraApiRequest(endpoint, method, body) {
21
21
  return response.data;
22
22
  }
23
23
  catch (error) {
24
- if (error instanceof Error) {
24
+ if (axios.isAxiosError(error)) {
25
+ console.error('Error making Jira API request (OAuth):', error.message, error.response?.data);
26
+ }
27
+ else if (error instanceof Error) {
25
28
  console.error('Error making Jira API request (OAuth):', error.message);
26
29
  }
27
30
  return null;
@@ -49,7 +52,10 @@ async function jiraApiRequest(endpoint, method, body) {
49
52
  return response.data;
50
53
  }
51
54
  catch (error) {
52
- if (error instanceof Error) {
55
+ if (axios.isAxiosError(error)) {
56
+ console.error('Error making Jira API request:', error.message, error.response?.data);
57
+ }
58
+ else if (error instanceof Error) {
53
59
  console.error('Error making Jira API request:', error.message);
54
60
  }
55
61
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clocktopus",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -15,15 +15,14 @@
15
15
  "scripts": {
16
16
  "build": "bunx tsc",
17
17
  "prepublishOnly": "bunx tsc",
18
- "postinstall": "cd node_modules/macos-notification-state && node-gyp rebuild 2>/dev/null; cd ../desktop-idle && node-gyp rebuild 2>/dev/null; true",
18
+ "postinstall": "node -e \"var p=require('path').dirname(require.resolve('macos-notification-state/package.json')); require('child_process').execSync('node-gyp rebuild',{cwd:p,stdio:'inherit'})\" 2>/dev/null; node -e \"var p=require('path').dirname(require.resolve('desktop-idle/package.json')); require('child_process').execSync('node-gyp rebuild',{cwd:p,stdio:'inherit'})\" 2>/dev/null; true",
19
19
  "lint": "eslint . --ext .ts",
20
20
  "clock": "bun dist/index.js",
21
- "clockd": "bunx pm2 start dist/index.js --name clocktopus --",
22
- "monitor": "bun run clockd monitor",
23
- "monitor:restart": "bunx pm2 restart clocktopus",
24
- "monitor:stop": "bunx pm2 stop clocktopus",
25
- "monitor:logs": "bunx pm2 logs clocktopus",
26
- "monitor:status": "bunx pm2 status clocktopus",
21
+ "monitor": "bun dist/index.js monitor",
22
+ "monitor:stop": "bun dist/index.js monitor:stop",
23
+ "monitor:restart": "bunx pm2 restart clocktopus-monitor-dev",
24
+ "monitor:logs": "bun dist/index.js monitor:logs",
25
+ "monitor:status": "bunx pm2 status clocktopus-monitor-dev",
27
26
  "prepare": "husky",
28
27
  "dashboard": "bun -e \"import('./dist/dashboard/server.js').then(m => m.startDashboard())\"",
29
28
  "db:cleanup": "bun dist/scripts/db-cleanup.js",