clocktopus 1.1.2 → 1.1.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/README.md CHANGED
@@ -150,6 +150,22 @@ Go to **System Settings > Notifications** and ensure **terminal-notifier** has n
150
150
 
151
151
  Enable **Require password immediately** in System Settings > Lock Screen.
152
152
 
153
+ ### Bun installs an old version
154
+
155
+ Bun caches registry data aggressively. Clear the cache and reinstall:
156
+
157
+ ```bash
158
+ bun pm cache rm && bun i -g clocktopus@latest
159
+ ```
160
+
161
+ ### Native addons not built (untrusted postinstall)
162
+
163
+ If `bun install -g` skips the postinstall script, the monitor will auto-build native addons on first run (requires Node.js for `npx`). Alternatively, trust the package and reinstall:
164
+
165
+ ```bash
166
+ bun pm trust clocktopus && bun i -g clocktopus
167
+ ```
168
+
153
169
  ### Linux
154
170
 
155
171
  ```bash
@@ -7,6 +7,7 @@ const __dirname = path.dirname(__filename);
7
7
  const SCRIPT_PATH = path.resolve(__dirname, '../../index.js');
8
8
  const isDev = SCRIPT_PATH.includes('/Projects/') || SCRIPT_PATH.includes('/src/');
9
9
  const PM2_NAME = isDev ? 'clocktopus-monitor-dev' : 'clocktopus-monitor';
10
+ const pm2Bin = path.resolve(__dirname, '../../node_modules/.bin/pm2');
10
11
  const monitorRoutes = new Hono();
11
12
  function pm2Exec(command) {
12
13
  try {
@@ -20,7 +21,7 @@ function pm2Exec(command) {
20
21
  }
21
22
  monitorRoutes.get('/monitor/status', (c) => {
22
23
  try {
23
- const output = execSync('bunx pm2 jlist', { encoding: 'utf-8', timeout: 10000 });
24
+ const output = execSync('${pm2Bin} jlist', { encoding: 'utf-8', timeout: 10000 });
24
25
  const processes = JSON.parse(output);
25
26
  const proc = processes.find((p) => p.name === PM2_NAME);
26
27
  if (!proc) {
@@ -41,18 +42,18 @@ monitorRoutes.post('/monitor/start', (c) => {
41
42
  const bunPath = execSync('which bun', { encoding: 'utf-8' }).trim();
42
43
  // Delete any existing process to avoid duplicates
43
44
  try {
44
- execSync(`bunx pm2 delete ${PM2_NAME}`, { stdio: 'ignore' });
45
+ execSync(`${pm2Bin} delete ${PM2_NAME}`, { stdio: 'ignore' });
45
46
  }
46
47
  catch { }
47
- const result = pm2Exec(`bunx pm2 start ${SCRIPT_PATH} --name ${PM2_NAME} --interpreter ${bunPath} -- monitor:run`);
48
+ const result = pm2Exec(`${pm2Bin} start ${SCRIPT_PATH} --name ${PM2_NAME} --interpreter ${bunPath} -- monitor:run`);
48
49
  return c.json(result);
49
50
  });
50
51
  monitorRoutes.post('/monitor/stop', (c) => {
51
- const result = pm2Exec(`bunx pm2 stop ${PM2_NAME}`);
52
+ const result = pm2Exec(`${pm2Bin} stop ${PM2_NAME}`);
52
53
  return c.json(result);
53
54
  });
54
55
  monitorRoutes.post('/monitor/restart', (c) => {
55
- const result = pm2Exec(`bunx pm2 restart ${PM2_NAME}`);
56
+ const result = pm2Exec(`${pm2Bin} restart ${PM2_NAME}`);
56
57
  return c.json(result);
57
58
  });
58
59
  export default monitorRoutes;
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { fileURLToPath } from 'url';
9
9
  import { completeLatestSession, getLatestSession } from './lib/db.js';
10
10
  import { stopJiraTimer } from './lib/jira.js';
11
11
  import { startDashboard } from './dashboard/server.js';
12
+ import { ensureNativeAddons } from './lib/ensure-native-addons.js';
12
13
  const __filename = fileURLToPath(import.meta.url);
13
14
  const __dirname = path.dirname(__filename);
14
15
  const program = new Command();
@@ -286,19 +287,21 @@ program
286
287
  const isDev = __dirname.includes('/Projects/') || __dirname.includes('/src/');
287
288
  const MONITOR_PM2_NAME = isDev ? 'clocktopus-monitor-dev' : 'clocktopus-monitor';
288
289
  const DASH_PM2_NAME = isDev ? 'clocktopus-dash-dev' : 'clocktopus-dash';
290
+ const pm2Bin = path.join(__dirname, '..', 'node_modules', '.bin', 'pm2');
289
291
  program
290
292
  .command('monitor')
291
293
  .description('Start idle monitor as a background daemon.')
292
294
  .action(async () => {
295
+ ensureNativeAddons();
293
296
  const { execSync } = await import('child_process');
294
297
  const bunPath = execSync('which bun', { encoding: 'utf-8' }).trim();
295
298
  const scriptPath = path.join(__dirname, 'index.js');
296
299
  try {
297
300
  try {
298
- execSync(`bunx pm2 delete ${MONITOR_PM2_NAME}`, { stdio: 'ignore' });
301
+ execSync(`${pm2Bin} delete ${MONITOR_PM2_NAME}`, { stdio: 'ignore' });
299
302
  }
300
303
  catch { }
301
- execSync(`bunx pm2 start ${scriptPath} --name ${MONITOR_PM2_NAME} --interpreter ${bunPath} -- monitor:run`, {
304
+ execSync(`${pm2Bin} start ${scriptPath} --name ${MONITOR_PM2_NAME} --interpreter ${bunPath} -- monitor:run`, {
302
305
  stdio: 'inherit',
303
306
  });
304
307
  console.log(chalk.green('Idle monitor started in background.'));
@@ -315,7 +318,7 @@ program
315
318
  .action(async () => {
316
319
  const { execSync } = await import('child_process');
317
320
  try {
318
- execSync(`bunx pm2 stop ${MONITOR_PM2_NAME}`, { stdio: 'inherit' });
321
+ execSync(`${pm2Bin} stop ${MONITOR_PM2_NAME}`, { stdio: 'inherit' });
319
322
  }
320
323
  catch {
321
324
  console.log(chalk.yellow('Monitor is not running.'));
@@ -327,7 +330,7 @@ program
327
330
  .action(async () => {
328
331
  const { execSync } = await import('child_process');
329
332
  try {
330
- execSync(`bunx pm2 logs ${MONITOR_PM2_NAME} --lines 50`, { stdio: 'inherit' });
333
+ execSync(`${pm2Bin} logs ${MONITOR_PM2_NAME} --lines 50`, { stdio: 'inherit' });
331
334
  }
332
335
  catch {
333
336
  console.log(chalk.yellow('Monitor is not running.'));
@@ -342,10 +345,10 @@ program
342
345
  const scriptPath = path.join(__dirname, 'index.js');
343
346
  try {
344
347
  try {
345
- execSync(`bunx pm2 delete ${DASH_PM2_NAME}`, { stdio: 'ignore' });
348
+ execSync(`${pm2Bin} delete ${DASH_PM2_NAME}`, { stdio: 'ignore' });
346
349
  }
347
350
  catch { }
348
- execSync(`bunx pm2 start ${scriptPath} --name ${DASH_PM2_NAME} --interpreter ${bunPath} -- dash`, {
351
+ execSync(`${pm2Bin} start ${scriptPath} --name ${DASH_PM2_NAME} --interpreter ${bunPath} -- dash`, {
349
352
  stdio: 'inherit',
350
353
  });
351
354
  console.log(chalk.green('Dashboard running at http://localhost:4001'));
@@ -362,7 +365,7 @@ program
362
365
  .action(async () => {
363
366
  const { execSync } = await import('child_process');
364
367
  try {
365
- execSync(`bunx pm2 stop ${DASH_PM2_NAME}`, { stdio: 'inherit' });
368
+ execSync(`${pm2Bin} stop ${DASH_PM2_NAME}`, { stdio: 'inherit' });
366
369
  }
367
370
  catch {
368
371
  console.log(chalk.yellow('Dashboard is not running.'));
@@ -374,7 +377,7 @@ program
374
377
  .action(async () => {
375
378
  const { execSync } = await import('child_process');
376
379
  try {
377
- execSync(`bunx pm2 logs ${DASH_PM2_NAME} --lines 50`, { stdio: 'inherit' });
380
+ execSync(`${pm2Bin} logs ${DASH_PM2_NAME} --lines 50`, { stdio: 'inherit' });
378
381
  }
379
382
  catch {
380
383
  console.log(chalk.yellow('Dashboard is not running.'));
@@ -0,0 +1,76 @@
1
+ import { execSync } from 'child_process';
2
+ import { createRequire } from 'module';
3
+ import * as path from 'path';
4
+ import * as fs from 'fs';
5
+ const require = createRequire(import.meta.url);
6
+ const NATIVE_MODULES = [
7
+ { name: 'macos-notification-state', addonName: 'notificationstate' },
8
+ { name: 'desktop-idle', addonName: 'desktopIdle' },
9
+ ];
10
+ function hasBuiltAddon(moduleName, addonName) {
11
+ try {
12
+ const modulePath = path.dirname(require.resolve(`${moduleName}/package.json`));
13
+ const nodePath = path.join(modulePath, 'build', 'Release', `${addonName}.node`);
14
+ return fs.existsSync(nodePath);
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ function findNodeGyp() {
21
+ try {
22
+ execSync('node-gyp --version', { stdio: 'ignore' });
23
+ return 'node-gyp';
24
+ }
25
+ catch { }
26
+ try {
27
+ execSync('npx --version', { stdio: 'ignore' });
28
+ return 'npx node-gyp';
29
+ }
30
+ catch { }
31
+ return null;
32
+ }
33
+ function buildModule(moduleName, nodeGypCmd) {
34
+ let modulePath;
35
+ try {
36
+ modulePath = path.dirname(require.resolve(`${moduleName}/package.json`));
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ if (!fs.existsSync(path.join(modulePath, 'binding.gyp')))
42
+ return false;
43
+ try {
44
+ console.log(`Building native addon: ${moduleName}...`);
45
+ execSync(`${nodeGypCmd} rebuild`, { cwd: modulePath, stdio: 'inherit' });
46
+ console.log(`Built ${moduleName} successfully.`);
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ export function ensureNativeAddons() {
54
+ if (process.platform !== 'darwin')
55
+ return;
56
+ const missing = NATIVE_MODULES.filter((m) => !hasBuiltAddon(m.name, m.addonName));
57
+ if (missing.length === 0)
58
+ return;
59
+ console.log(`Native addons not built: ${missing.map((m) => m.name).join(', ')}. Attempting to build...`);
60
+ const nodeGypCmd = findNodeGyp();
61
+ if (!nodeGypCmd) {
62
+ console.warn('Warning: node-gyp not found. Native addons could not be built.');
63
+ console.warn(' Install Node.js (includes npx) then restart, or run:');
64
+ console.warn(' bun pm trust clocktopus && bun install -g clocktopus');
65
+ return;
66
+ }
67
+ const failed = [];
68
+ for (const mod of missing) {
69
+ if (!buildModule(mod.name, nodeGypCmd)) {
70
+ failed.push(mod.name);
71
+ }
72
+ }
73
+ if (failed.length > 0) {
74
+ console.warn(`Warning: Failed to build: ${failed.join(', ')}. Monitor features may not work.`);
75
+ }
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clocktopus",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -21,9 +21,9 @@
21
21
  "clock": "bun dist/index.js",
22
22
  "monitor": "bun dist/index.js monitor",
23
23
  "monitor:stop": "bun dist/index.js monitor:stop",
24
- "monitor:restart": "bunx pm2 restart clocktopus-monitor-dev",
24
+ "monitor:restart": "node_modules/.bin/pm2 restart clocktopus-monitor-dev",
25
25
  "monitor:logs": "bun dist/index.js monitor:logs",
26
- "monitor:status": "bunx pm2 status clocktopus-monitor-dev",
26
+ "monitor:status": "node_modules/.bin/pm2 status clocktopus-monitor-dev",
27
27
  "prepare": "husky",
28
28
  "dashboard": "bun -e \"import('./dist/dashboard/server.js').then(m => m.startDashboard())\"",
29
29
  "db:cleanup": "bun dist/scripts/db-cleanup.js",