codexmeter 1.0.6 → 1.0.7

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.html CHANGED
@@ -7,8 +7,8 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com" />
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-ATBSFYaH.js"></script>
11
- <link rel="stylesheet" crossorigin href="/assets/index-DhDq9dI8.css">
10
+ <script type="module" crossorigin src="/assets/index-DxtrMXuP.js"></script>
11
+ <link rel="stylesheet" crossorigin href="/assets/index-CVvcsIkn.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="root"></div>
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "codexmeter",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "Local telemetry dashboard for Codex CLI usage",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "codexmeter": "./bin/codexmeter.js"
8
8
  },
9
+ "engines": {
10
+ "node": "^20.19.0 || >=22.12.0"
11
+ },
9
12
  "files": [
10
13
  "bin",
11
14
  "server",
@@ -17,31 +20,33 @@
17
20
  "scripts": {
18
21
  "dev": "node dev-runner.js",
19
22
  "build": "vite build",
20
- "test": "node --test test/*.test.js",
23
+ "prepack": "npm run build",
24
+ "pretest": "npm run build",
25
+ "test": "node --test \"test/*.test.js\" \"test/**/*.test.js\"",
21
26
  "start": "node bin/codexmeter.js",
22
27
  "preview": "npm run build && node bin/codexmeter.js",
23
28
  "prepublishOnly": "npm run build"
24
29
  },
25
30
  "dependencies": {
26
- "better-sqlite3": "^11.8.1",
27
- "commander": "^13.1.0",
31
+ "better-sqlite3": "^12.8.0",
32
+ "commander": "^14.0.3",
28
33
  "express": "^5.1.0",
29
34
  "fast-glob": "^3.3.3",
30
35
  "ffmpeg-static": "^5.2.0",
31
- "open": "^10.1.0",
36
+ "open": "^11.0.0",
32
37
  "playwright-core": "^1.58.2"
33
38
  },
34
39
  "devDependencies": {
35
40
  "@tanstack/react-table": "^8.21.3",
36
- "@tanstack/react-virtual": "^3.13.6",
41
+ "@tanstack/react-virtual": "^3.13.23",
37
42
  "@types/react": "^19.0.0",
38
43
  "@types/react-dom": "^19.0.0",
39
- "@vitejs/plugin-react": "^4.4.1",
40
- "echarts": "^5.6.0",
44
+ "@vitejs/plugin-react": "^6.0.1",
45
+ "echarts": "^6.0.0",
41
46
  "echarts-for-react": "^3.0.2",
42
47
  "nodemon": "^3.1.14",
43
48
  "react": "^19.1.0",
44
49
  "react-dom": "^19.1.0",
45
- "vite": "^6.3.5"
50
+ "vite": "^8.0.0"
46
51
  }
47
52
  }
@@ -0,0 +1,73 @@
1
+ import fsSync from 'fs';
2
+ import path from 'path';
3
+ import { spawnSync } from 'child_process';
4
+
5
+ export function findSupportedBrowserExecutable() {
6
+ if (process.env.CODEXMETER_EXPORT_DEBUG_FORCE_UNSUPPORTED === '1') {
7
+ return null;
8
+ }
9
+ if (process.env.CODEXMETER_EXPORT_BROWSER && fsSync.existsSync(process.env.CODEXMETER_EXPORT_BROWSER)) {
10
+ return process.env.CODEXMETER_EXPORT_BROWSER;
11
+ }
12
+
13
+ const candidates = process.platform === 'win32'
14
+ ? [
15
+ process.env['PROGRAMFILES'] ? path.join(process.env['PROGRAMFILES'], 'Google', 'Chrome', 'Application', 'chrome.exe') : null,
16
+ process.env['PROGRAMFILES(X86)'] ? path.join(process.env['PROGRAMFILES(X86)'], 'Google', 'Chrome', 'Application', 'chrome.exe') : null,
17
+ process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Google', 'Chrome', 'Application', 'chrome.exe') : null,
18
+ process.env['PROGRAMFILES'] ? path.join(process.env['PROGRAMFILES'], 'Microsoft', 'Edge', 'Application', 'msedge.exe') : null,
19
+ process.env['PROGRAMFILES(X86)'] ? path.join(process.env['PROGRAMFILES(X86)'], 'Microsoft', 'Edge', 'Application', 'msedge.exe') : null,
20
+ process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Microsoft', 'Edge', 'Application', 'msedge.exe') : null,
21
+ ]
22
+ : process.platform === 'darwin'
23
+ ? [
24
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
25
+ path.join(process.env.HOME || '', 'Applications', 'Google Chrome.app', 'Contents', 'MacOS', 'Google Chrome'),
26
+ '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
27
+ path.join(process.env.HOME || '', 'Applications', 'Microsoft Edge.app', 'Contents', 'MacOS', 'Microsoft Edge'),
28
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
29
+ path.join(process.env.HOME || '', 'Applications', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'),
30
+ ]
31
+ : [
32
+ '/usr/bin/google-chrome',
33
+ '/usr/bin/google-chrome-stable',
34
+ '/usr/bin/chromium',
35
+ '/usr/bin/chromium-browser',
36
+ '/usr/bin/microsoft-edge',
37
+ '/snap/bin/chromium',
38
+ ];
39
+
40
+ for (const candidate of candidates.filter(Boolean)) {
41
+ if (fsSync.existsSync(candidate)) return candidate;
42
+ }
43
+
44
+ return findBrowserOnPath();
45
+ }
46
+
47
+ function findBrowserOnPath() {
48
+ const commands = process.platform === 'win32'
49
+ ? ['chrome', 'msedge', 'chromium']
50
+ : ['google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser', 'microsoft-edge'];
51
+
52
+ for (const command of commands) {
53
+ const resolved = resolveCommand(command);
54
+ if (resolved) return resolved;
55
+ }
56
+
57
+ return null;
58
+ }
59
+
60
+ function resolveCommand(command) {
61
+ const lookup = process.platform === 'win32'
62
+ ? spawnSync('where.exe', [command], { encoding: 'utf8' })
63
+ : spawnSync('which', [command], { encoding: 'utf8' });
64
+
65
+ if (lookup.status !== 0) return null;
66
+
67
+ const firstLine = lookup.stdout
68
+ .split(/\r?\n/)
69
+ .map((line) => line.trim())
70
+ .find(Boolean);
71
+
72
+ return firstLine && fsSync.existsSync(firstLine) ? firstLine : null;
73
+ }
@@ -1,5 +1,4 @@
1
1
  import fs from 'fs/promises';
2
- import fsSync from 'fs';
3
2
  import os from 'os';
4
3
  import path from 'path';
5
4
  import { randomUUID } from 'crypto';
@@ -9,6 +8,7 @@ import { createRequire } from 'module';
9
8
  import ffmpegPath from 'ffmpeg-static';
10
9
  import { chromium } from 'playwright-core';
11
10
  import { OVERVIEW_INGEST_ANIMATION } from '../src/utils/animationsDefault.js';
11
+ import { findSupportedBrowserExecutable } from './browser-detection.js';
12
12
 
13
13
  const EXPORT_WIDTH = OVERVIEW_INGEST_ANIMATION.videoExport?.width ?? 1080;
14
14
  const EXPORT_HEIGHT = OVERVIEW_INGEST_ANIMATION.videoExport?.height ?? 864;
@@ -705,39 +705,6 @@ function resolvePlaywrightCliPath() {
705
705
  return path.join(packageRoot, 'cli.js');
706
706
  }
707
707
 
708
- function findSupportedBrowserExecutable() {
709
- if (process.env.CODEXMETER_EXPORT_DEBUG_FORCE_UNSUPPORTED === '1') {
710
- return null;
711
- }
712
- if (process.env.CODEXMETER_EXPORT_BROWSER && fsSync.existsSync(process.env.CODEXMETER_EXPORT_BROWSER)) {
713
- return process.env.CODEXMETER_EXPORT_BROWSER;
714
- }
715
-
716
- const candidates = process.platform === 'win32'
717
- ? [
718
- process.env['PROGRAMFILES'] ? path.join(process.env['PROGRAMFILES'], 'Google', 'Chrome', 'Application', 'chrome.exe') : null,
719
- process.env['PROGRAMFILES(X86)'] ? path.join(process.env['PROGRAMFILES(X86)'], 'Google', 'Chrome', 'Application', 'chrome.exe') : null,
720
- process.env['PROGRAMFILES'] ? path.join(process.env['PROGRAMFILES'], 'Microsoft', 'Edge', 'Application', 'msedge.exe') : null,
721
- process.env['PROGRAMFILES(X86)'] ? path.join(process.env['PROGRAMFILES(X86)'], 'Microsoft', 'Edge', 'Application', 'msedge.exe') : null,
722
- ]
723
- : process.platform === 'darwin'
724
- ? [
725
- '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
726
- '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
727
- ]
728
- : [
729
- '/usr/bin/google-chrome',
730
- '/usr/bin/chromium',
731
- '/usr/bin/chromium-browser',
732
- '/usr/bin/microsoft-edge',
733
- ];
734
-
735
- for (const candidate of candidates.filter(Boolean)) {
736
- if (fsSync.existsSync(candidate)) return candidate;
737
- }
738
- return null;
739
- }
740
-
741
708
  async function encodeFramesToMp4(framesDir, outputPath, { captureFormat, fps, frameCount, durationMs, crf, encoderPreset, outputWidth, outputHeight }) {
742
709
  const extension = captureFormat === 'jpeg' ? 'jpg' : 'png';
743
710
  const inputPattern = path.join(framesDir, `%05d.${extension}`);
package/server/ingest.js CHANGED
@@ -12,10 +12,20 @@ import { createRolloutWorkerPool } from './rollout-worker-pool.js';
12
12
  import { beginReplayCapture, createReplayCaptureState, failReplayCapture, getReplaySnapshot, recordReplayEvent, resetReplayCapture } from './export-replay.js';
13
13
  import { OVERVIEW_INGEST_ANIMATION } from '../src/utils/animationsDefault.js';
14
14
 
15
- const LIVE_FRAME_INTERVAL_MS = 50;
15
+ const LIVE_FRAME_INTERVAL_MS = Math.max(
16
+ 16,
17
+ Math.round(Number(OVERVIEW_INGEST_ANIMATION.live?.frameIntervalMs) || 50)
18
+ );
16
19
  const LIVE_DAYS_PER_SECOND = 6;
17
- const LIVE_OVERVIEW_CADENCE_MS = Math.round(1000 / 10);
18
- const LIVE_DAY_KEYS_PER_EMIT = 1;
20
+ const LIVE_OVERVIEW_HZ = Math.max(
21
+ 1,
22
+ Number(OVERVIEW_INGEST_ANIMATION.live?.overviewHz) || 10
23
+ );
24
+ const LIVE_OVERVIEW_CADENCE_MS = Math.round(1000 / LIVE_OVERVIEW_HZ);
25
+ const LIVE_DAY_KEYS_PER_EMIT = Math.max(
26
+ 1,
27
+ Math.round(Number(OVERVIEW_INGEST_ANIMATION.live?.dayKeysPerEmit) || 1)
28
+ );
19
29
  const LIVE_SESSION_REORDER_BUFFER = 160;
20
30
 
21
31
  export function createIngestState() {
@@ -696,5 +706,5 @@ function broadcastBootstrap(state) {
696
706
  };
697
707
  broadcastLive(state, 'bootstrap', payload);
698
708
  }
699
-
700
-
709
+
710
+
@@ -19,8 +19,13 @@ const AUTO = 'auto';
19
19
  */
20
20
  export const OVERVIEW_INGEST_ANIMATION = {
21
21
  speed: 1,
22
+ live: {
23
+ frameIntervalMs: 33,
24
+ overviewHz: 20,
25
+ dayKeysPerEmit: 1,
26
+ },
22
27
  main: {
23
- presentationDurationMs: 300,
28
+ presentationDurationMs: 220,
24
29
  chartAppearDurationMs: 0,
25
30
  chartUpdateDurationMs: 200,
26
31
  easing: 'linear',
@@ -30,19 +35,19 @@ export const OVERVIEW_INGEST_ANIMATION = {
30
35
  // Master switch for the end-of-ingest slowdown behavior.
31
36
  enabled: true,
32
37
  // Progress threshold where the tail mode starts, expressed from 0..1.
33
- startPercent: 0.85,
38
+ startPercent: 0.5,
34
39
  // Fixed presentation duration in ms during the tail; use AUTO to derive it from main.durationScale.
35
40
  durationMs: AUTO,
36
41
  // Multiplier applied to the normal presentation duration when durationMs is AUTO.
37
- durationScale: 20,
42
+ durationScale: 10,
38
43
  // Shared easing applied by the Overview presentation animator during the tail.
39
44
  easing: 'cubicOut',
40
45
  // Backend Overview live-update cadence during the tail, in patches per second.
41
- overviewHz: 5,
46
+ overviewHz: 10,
42
47
  },
43
48
  daily: {
44
49
  chartAppearDurationMs: AUTO,
45
- chartUpdateDurationMs: 30,
50
+ chartUpdateDurationMs: 60,
46
51
  easing: AUTO,
47
52
  easingUpdate: AUTO,
48
53
  },