dashcam 1.4.1-beta → 1.4.3-beta
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/bin/dashcam-background.js +32 -4
- package/bin/dashcam.js +147 -3
- package/lib/extension-logs/helpers.js +7 -1
- package/lib/logs/index.js +5 -1
- package/lib/processManager.js +31 -12
- package/lib/recorder.js +10 -10
- package/lib/utilities/jsonl.js +11 -2
- package/lib/websocket/server.js +19 -9
- package/package.json +1 -1
- package/test_workflow.sh +6 -4
|
@@ -161,11 +161,39 @@ async function runBackgroundRecording() {
|
|
|
161
161
|
}
|
|
162
162
|
isShuttingDown = true;
|
|
163
163
|
|
|
164
|
-
logger.info(`Received ${signal},
|
|
165
|
-
console.log('[Background] Received stop signal,
|
|
164
|
+
logger.info(`Received ${signal}, cleaning up child processes`);
|
|
165
|
+
console.log('[Background] Received stop signal, cleaning up...');
|
|
166
|
+
|
|
167
|
+
// Kill any child processes (ffmpeg, etc.)
|
|
168
|
+
try {
|
|
169
|
+
// Get all child processes and kill them
|
|
170
|
+
const { exec } = await import('child_process');
|
|
171
|
+
const platform = process.platform;
|
|
172
|
+
|
|
173
|
+
if (platform === 'darwin' || platform === 'linux') {
|
|
174
|
+
// On Unix, kill the entire process group
|
|
175
|
+
exec(`pkill -P ${process.pid}`, (error) => {
|
|
176
|
+
if (error && error.code !== 1) { // code 1 means no processes found
|
|
177
|
+
logger.warn('Failed to kill child processes', { error: error.message });
|
|
178
|
+
}
|
|
179
|
+
logger.info('Child processes killed');
|
|
180
|
+
});
|
|
181
|
+
} else if (platform === 'win32') {
|
|
182
|
+
// On Windows, use taskkill
|
|
183
|
+
exec(`taskkill /F /T /PID ${process.pid}`, (error) => {
|
|
184
|
+
if (error) {
|
|
185
|
+
logger.warn('Failed to kill child processes on Windows', { error: error.message });
|
|
186
|
+
}
|
|
187
|
+
logger.info('Child processes killed');
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Give it a moment to clean up
|
|
192
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
193
|
+
} catch (error) {
|
|
194
|
+
logger.error('Error during cleanup', { error: error.message });
|
|
195
|
+
}
|
|
166
196
|
|
|
167
|
-
// Don't try to stop recording here - the main process will handle cleanup
|
|
168
|
-
// after killing this process. Just exit.
|
|
169
197
|
logger.info('Background process exiting');
|
|
170
198
|
process.exit(0);
|
|
171
199
|
};
|
package/bin/dashcam.js
CHANGED
|
@@ -622,14 +622,33 @@ program
|
|
|
622
622
|
const { jsonl } = await import('../lib/utilities/jsonl.js');
|
|
623
623
|
const webLogs = jsonl.read(webLogsFile);
|
|
624
624
|
if (webLogs && webLogs.length > 0) {
|
|
625
|
+
// Load web config to get patterns
|
|
626
|
+
const webConfigFile = path.join(process.cwd(), '.dashcam', 'web-config.json');
|
|
627
|
+
let webPatterns = ['*']; // Default to all patterns
|
|
628
|
+
if (fs.existsSync(webConfigFile)) {
|
|
629
|
+
try {
|
|
630
|
+
const webConfig = JSON.parse(fs.readFileSync(webConfigFile, 'utf8'));
|
|
631
|
+
// Extract all patterns from all enabled configs
|
|
632
|
+
webPatterns = Object.values(webConfig)
|
|
633
|
+
.filter(config => config.enabled !== false)
|
|
634
|
+
.flatMap(config => config.patterns || []);
|
|
635
|
+
if (webPatterns.length === 0) {
|
|
636
|
+
webPatterns = ['*'];
|
|
637
|
+
}
|
|
638
|
+
} catch (error) {
|
|
639
|
+
logger.warn('Failed to load web config for patterns', { error: error.message });
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
625
643
|
logTrackingResults.push({
|
|
626
644
|
type: 'web',
|
|
627
645
|
name: 'Web Logs',
|
|
628
646
|
fileLocation: webLogsFile,
|
|
629
647
|
count: webLogs.length,
|
|
630
|
-
trimmedFileLocation: webLogsFile
|
|
648
|
+
trimmedFileLocation: webLogsFile,
|
|
649
|
+
patterns: webPatterns // Include patterns for filtering
|
|
631
650
|
});
|
|
632
|
-
logger.info('Found web logs', { count: webLogs.length, file: webLogsFile });
|
|
651
|
+
logger.info('Found web logs', { count: webLogs.length, file: webLogsFile, patterns: webPatterns });
|
|
633
652
|
}
|
|
634
653
|
}
|
|
635
654
|
|
|
@@ -719,6 +738,7 @@ program
|
|
|
719
738
|
.option('--remove <id>', 'Remove a log tracker by ID')
|
|
720
739
|
.option('--list', 'List all configured log trackers')
|
|
721
740
|
.option('--status', 'Show log tracking status')
|
|
741
|
+
.option('--view [directory]', 'View logs from recording directory (defaults to most recent in /tmp/dashcam/recordings)')
|
|
722
742
|
.option('--name <name>', 'Name for the log tracker (required with --add)')
|
|
723
743
|
.option('--type <type>', 'Type of tracker: "web" or "file" (required with --add)')
|
|
724
744
|
.option('--pattern <pattern>', 'Pattern to track (can be used multiple times)', (value, previous) => {
|
|
@@ -812,6 +832,16 @@ program
|
|
|
812
832
|
console.log(` File trackers: ${status.cliFilesCount}`);
|
|
813
833
|
console.log(` Web trackers: ${status.webAppsCount}`);
|
|
814
834
|
console.log(` Total recent events: ${status.totalEvents}`);
|
|
835
|
+
console.log(` Web daemon running: ${status.webDaemonRunning ? 'Yes' : 'No'}`);
|
|
836
|
+
|
|
837
|
+
// Show WebSocket server info
|
|
838
|
+
const { server } = await import('../lib/websocket/server.js');
|
|
839
|
+
if (server.isListening.value) {
|
|
840
|
+
console.log(` WebSocket server: Listening on port ${server.port}`);
|
|
841
|
+
console.log(` WebSocket clients: ${server._socket?.clients?.size || 0} connected`);
|
|
842
|
+
} else {
|
|
843
|
+
console.log(` WebSocket server: Not listening`);
|
|
844
|
+
}
|
|
815
845
|
|
|
816
846
|
if (status.fileTrackerStats.length > 0) {
|
|
817
847
|
console.log('\n File tracker activity (last minute):');
|
|
@@ -819,13 +849,127 @@ program
|
|
|
819
849
|
console.log(` ${stat.filePath}: ${stat.count} events`);
|
|
820
850
|
});
|
|
821
851
|
}
|
|
852
|
+
|
|
853
|
+
if (status.webApps.length > 0) {
|
|
854
|
+
console.log('\n Web tracker patterns:');
|
|
855
|
+
status.webApps.forEach(app => {
|
|
856
|
+
console.log(` ${app.name}: ${app.patterns.join(', ')}`);
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
} else if (options.view !== undefined) {
|
|
860
|
+
// View logs from a recording directory
|
|
861
|
+
const { jsonl } = await import('../lib/utilities/jsonl.js');
|
|
862
|
+
|
|
863
|
+
let targetDir = options.view;
|
|
864
|
+
|
|
865
|
+
// If no directory specified, find the most recent recording directory
|
|
866
|
+
if (!targetDir || targetDir === true) {
|
|
867
|
+
const recordingsDir = path.join(os.tmpdir(), 'dashcam', 'recordings');
|
|
868
|
+
if (!fs.existsSync(recordingsDir)) {
|
|
869
|
+
console.error('No recordings directory found at:', recordingsDir);
|
|
870
|
+
process.exit(1);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
const entries = fs.readdirSync(recordingsDir, { withFileTypes: true });
|
|
874
|
+
const dirs = entries
|
|
875
|
+
.filter(entry => entry.isDirectory())
|
|
876
|
+
.map(entry => ({
|
|
877
|
+
name: entry.name,
|
|
878
|
+
path: path.join(recordingsDir, entry.name),
|
|
879
|
+
mtime: fs.statSync(path.join(recordingsDir, entry.name)).mtime
|
|
880
|
+
}))
|
|
881
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
882
|
+
|
|
883
|
+
if (dirs.length === 0) {
|
|
884
|
+
console.error('No recording directories found in:', recordingsDir);
|
|
885
|
+
process.exit(1);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
targetDir = dirs[0].path;
|
|
889
|
+
console.log('Viewing logs from most recent recording:', path.basename(targetDir));
|
|
890
|
+
} else if (!fs.existsSync(targetDir)) {
|
|
891
|
+
console.error('Directory does not exist:', targetDir);
|
|
892
|
+
process.exit(1);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
console.log('Directory:', targetDir);
|
|
896
|
+
console.log('');
|
|
897
|
+
|
|
898
|
+
// Check for CLI logs
|
|
899
|
+
const cliLogsFile = path.join(targetDir, 'dashcam_logs_cli.jsonl');
|
|
900
|
+
if (fs.existsSync(cliLogsFile)) {
|
|
901
|
+
const cliLogs = jsonl.read(cliLogsFile);
|
|
902
|
+
if (cliLogs && cliLogs.length > 0) {
|
|
903
|
+
console.log(`📄 CLI Logs (${cliLogs.length} events):`);
|
|
904
|
+
cliLogs.slice(0, 50).forEach((log, index) => {
|
|
905
|
+
const timeSeconds = ((log.time || 0) / 1000).toFixed(2);
|
|
906
|
+
const logFile = log.logFile || 'unknown';
|
|
907
|
+
const content = (log.line || log.content || '').substring(0, 100);
|
|
908
|
+
console.log(` [${timeSeconds}s] ${logFile}: ${content}`);
|
|
909
|
+
});
|
|
910
|
+
if (cliLogs.length > 50) {
|
|
911
|
+
console.log(` ... and ${cliLogs.length - 50} more events`);
|
|
912
|
+
}
|
|
913
|
+
console.log('');
|
|
914
|
+
}
|
|
915
|
+
} else {
|
|
916
|
+
console.log('📄 CLI Logs: No logs found');
|
|
917
|
+
console.log('');
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Check for web logs
|
|
921
|
+
const webLogsFile = path.join(targetDir, 'dashcam_logs_web_events.jsonl');
|
|
922
|
+
if (fs.existsSync(webLogsFile)) {
|
|
923
|
+
const webLogs = jsonl.read(webLogsFile);
|
|
924
|
+
if (webLogs && webLogs.length > 0) {
|
|
925
|
+
console.log(`🌐 Web Logs (${webLogs.length} events):`);
|
|
926
|
+
|
|
927
|
+
// Group by event type
|
|
928
|
+
const eventTypes = {};
|
|
929
|
+
webLogs.forEach(log => {
|
|
930
|
+
eventTypes[log.type] = (eventTypes[log.type] || 0) + 1;
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
console.log(' Event types:');
|
|
934
|
+
Object.entries(eventTypes).forEach(([type, count]) => {
|
|
935
|
+
console.log(` ${type}: ${count}`);
|
|
936
|
+
});
|
|
937
|
+
console.log('');
|
|
938
|
+
|
|
939
|
+
// Show first 20 events
|
|
940
|
+
console.log(' Recent events:');
|
|
941
|
+
webLogs.slice(0, 20).forEach((log, index) => {
|
|
942
|
+
const timeSeconds = ((log.time || 0) / 1000).toFixed(2);
|
|
943
|
+
const type = log.type || 'unknown';
|
|
944
|
+
|
|
945
|
+
if (type === 'LOG_EVENT' || type === 'LOG_ERROR') {
|
|
946
|
+
const message = log.payload?.message || '';
|
|
947
|
+
console.log(` [${timeSeconds}s] ${type}: ${message.substring(0, 80)}`);
|
|
948
|
+
} else if (type.startsWith('NETWORK_')) {
|
|
949
|
+
const url = log.payload?.url || '';
|
|
950
|
+
console.log(` [${timeSeconds}s] ${type}: ${url.substring(0, 80)}`);
|
|
951
|
+
} else {
|
|
952
|
+
console.log(` [${timeSeconds}s] ${type}`);
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
if (webLogs.length > 20) {
|
|
956
|
+
console.log(` ... and ${webLogs.length - 20} more events`);
|
|
957
|
+
}
|
|
958
|
+
console.log('');
|
|
959
|
+
}
|
|
960
|
+
} else {
|
|
961
|
+
console.log('🌐 Web Logs: No logs found');
|
|
962
|
+
console.log('');
|
|
963
|
+
}
|
|
822
964
|
} else {
|
|
823
|
-
console.log('Please specify an action: --add, --remove, --list, or --
|
|
965
|
+
console.log('Please specify an action: --add, --remove, --list, --status, or --view');
|
|
824
966
|
console.log('\nExamples:');
|
|
825
967
|
console.log(' dashcam logs --add --name=social --type=web --pattern="*facebook.com*" --pattern="*twitter.com*"');
|
|
826
968
|
console.log(' dashcam logs --add --name=app-logs --type=file --file=/var/log/app.log');
|
|
827
969
|
console.log(' dashcam logs --list');
|
|
828
970
|
console.log(' dashcam logs --status');
|
|
971
|
+
console.log(' dashcam logs --view # View logs from most recent recording');
|
|
972
|
+
console.log(' dashcam logs --view /path/to/recording # View logs from specific directory');
|
|
829
973
|
console.log('\nUse "dashcam logs --help" for more information');
|
|
830
974
|
}
|
|
831
975
|
|
|
@@ -84,7 +84,13 @@ function filterWebEvents(
|
|
|
84
84
|
event => event.type === 'INITIAL_TABS' || event.payload.tabId
|
|
85
85
|
);
|
|
86
86
|
const patterns = groupLogsStatuses
|
|
87
|
-
.map((status) =>
|
|
87
|
+
.map((status) => {
|
|
88
|
+
// Handle cases where items might not be set (e.g., during upload)
|
|
89
|
+
if (!status.items || !Array.isArray(status.items)) {
|
|
90
|
+
return status.patterns || [];
|
|
91
|
+
}
|
|
92
|
+
return status.items.map((item) => item.item);
|
|
93
|
+
})
|
|
88
94
|
.flat();
|
|
89
95
|
|
|
90
96
|
const newEvents = [];
|
package/lib/logs/index.js
CHANGED
|
@@ -123,7 +123,11 @@ async function trimLogs(groupLogStatuses, startMS, endMS, clientStartDate, clipI
|
|
|
123
123
|
filteredEvents
|
|
124
124
|
);
|
|
125
125
|
} catch (error) {
|
|
126
|
-
logger.error('Error trimming log file', {
|
|
126
|
+
logger.error('Error trimming log file', {
|
|
127
|
+
file: status.fileLocation,
|
|
128
|
+
error: error.message,
|
|
129
|
+
stack: error.stack
|
|
130
|
+
});
|
|
127
131
|
}
|
|
128
132
|
});
|
|
129
133
|
|
package/lib/processManager.js
CHANGED
|
@@ -283,31 +283,50 @@ class ProcessManager {
|
|
|
283
283
|
|
|
284
284
|
logger.info('Killing background process', { pid });
|
|
285
285
|
|
|
286
|
-
//
|
|
287
|
-
//
|
|
286
|
+
// Try graceful shutdown first with SIGTERM (allows cleanup handlers to run)
|
|
287
|
+
// Then use SIGKILL if process doesn't exit
|
|
288
288
|
try {
|
|
289
|
-
process.kill(pid, '
|
|
290
|
-
logger.info('Sent
|
|
289
|
+
process.kill(pid, 'SIGTERM');
|
|
290
|
+
logger.info('Sent SIGTERM to background process');
|
|
291
291
|
} catch (error) {
|
|
292
|
-
logger.error('Failed to
|
|
292
|
+
logger.error('Failed to send SIGTERM to background process', { error });
|
|
293
293
|
throw new Error('Failed to stop background recording process');
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
// Wait
|
|
297
|
-
logger.debug('Waiting for background process to exit...');
|
|
298
|
-
const
|
|
296
|
+
// Wait for graceful shutdown
|
|
297
|
+
logger.debug('Waiting for background process to exit gracefully...');
|
|
298
|
+
const maxGracefulWait = 3000; // 3 seconds for graceful shutdown
|
|
299
299
|
const startWait = Date.now();
|
|
300
300
|
|
|
301
|
-
while (this.isProcessRunning(pid) && (Date.now() - startWait) <
|
|
301
|
+
while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxGracefulWait) {
|
|
302
302
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
// If still running, force kill with SIGKILL
|
|
305
306
|
if (this.isProcessRunning(pid)) {
|
|
306
|
-
logger.
|
|
307
|
-
|
|
307
|
+
logger.warn('Background process did not exit gracefully, sending SIGKILL');
|
|
308
|
+
try {
|
|
309
|
+
process.kill(pid, 'SIGKILL');
|
|
310
|
+
logger.info('Sent SIGKILL to background process');
|
|
311
|
+
|
|
312
|
+
// Wait for SIGKILL to take effect
|
|
313
|
+
const maxKillWait = 2000; // 2 seconds should be plenty for SIGKILL
|
|
314
|
+
const killStart = Date.now();
|
|
315
|
+
while (this.isProcessRunning(pid) && (Date.now() - killStart) < maxKillWait) {
|
|
316
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (this.isProcessRunning(pid)) {
|
|
320
|
+
logger.error('Background process did not exit even after SIGKILL');
|
|
321
|
+
throw new Error('Failed to kill background process');
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
logger.error('Failed to SIGKILL background process', { error });
|
|
325
|
+
throw new Error('Failed to force kill background process');
|
|
326
|
+
}
|
|
308
327
|
}
|
|
309
328
|
|
|
310
|
-
logger.info('Background process
|
|
329
|
+
logger.info('Background process stopped');
|
|
311
330
|
|
|
312
331
|
// Mark status as completed
|
|
313
332
|
this.markStatusCompleted({
|
package/lib/recorder.js
CHANGED
|
@@ -320,8 +320,14 @@ export async function startRecording({
|
|
|
320
320
|
|
|
321
321
|
// Construct FFmpeg command arguments
|
|
322
322
|
const platformArgs = await getPlatformArgs({ fps, includeAudio });
|
|
323
|
+
|
|
324
|
+
// Detect platform for encoder settings
|
|
325
|
+
const platform = os.platform();
|
|
326
|
+
const isWindows = platform === 'win32';
|
|
323
327
|
|
|
324
328
|
const outputArgs = [
|
|
329
|
+
// Convert pixel format first to handle transparency issues on Windows
|
|
330
|
+
'-pix_fmt', 'yuv420p', // Force YUV420P format (no alpha channel)
|
|
325
331
|
'-c:v', 'libvpx', // Use VP9 codec for better quality and compression
|
|
326
332
|
'-quality', 'realtime', // Use realtime quality preset for faster encoding
|
|
327
333
|
'-cpu-used', '8', // Maximum speed (0-8, higher = faster but lower quality)
|
|
@@ -330,6 +336,7 @@ export async function startRecording({
|
|
|
330
336
|
'-r', fps.toString(), // Ensure output framerate matches input
|
|
331
337
|
'-g', fps.toString(), // Keyframe interval = 1 second (every fps frames) - ensures frequent keyframes
|
|
332
338
|
'-force_key_frames', `expr:gte(t,n_forced*1)`, // Force keyframe every 1 second
|
|
339
|
+
'-auto-alt-ref', '0', // Disable auto alternate reference frames (fixes transparency encoding error)
|
|
333
340
|
// WebM options for more frequent disk writes and proper stream handling
|
|
334
341
|
'-f', 'webm', // Force WebM container format
|
|
335
342
|
'-flush_packets', '1', // Flush packets immediately to disk - critical for short recordings
|
|
@@ -407,19 +414,12 @@ export async function startRecording({
|
|
|
407
414
|
reject: false,
|
|
408
415
|
all: true, // Capture both stdout and stderr
|
|
409
416
|
stdin: 'pipe', // Enable stdin for sending 'q' to stop recording
|
|
410
|
-
detached: false,
|
|
417
|
+
detached: false, // Keep attached so it dies with parent
|
|
411
418
|
windowsHide: true // Hide the console window on Windows
|
|
412
419
|
});
|
|
413
420
|
|
|
414
|
-
//
|
|
415
|
-
|
|
416
|
-
try {
|
|
417
|
-
currentRecording.unref();
|
|
418
|
-
} catch (e) {
|
|
419
|
-
// Ignore errors if unref is not available
|
|
420
|
-
logger.debug('Could not unref ffmpeg process');
|
|
421
|
-
}
|
|
422
|
-
}
|
|
421
|
+
// Don't unref - we want FFmpeg to be killed when parent dies
|
|
422
|
+
// Removing unref() ensures orphaned processes don't hang around
|
|
423
423
|
|
|
424
424
|
logger.info('FFmpeg process spawned', {
|
|
425
425
|
pid: currentRecording.pid,
|
package/lib/utilities/jsonl.js
CHANGED
|
@@ -62,14 +62,23 @@ export const jsonl = {
|
|
|
62
62
|
let fd = fs.openSync(file, 'w');
|
|
63
63
|
fs.closeSync(fd);
|
|
64
64
|
} catch (error) {
|
|
65
|
-
throttledLog('info', `jsonl.js failed to initialize file ${error}
|
|
65
|
+
throttledLog('info', `jsonl.js failed to initialize file ${error.message}`, {
|
|
66
|
+
directory,
|
|
67
|
+
fileName,
|
|
68
|
+
error: error.message
|
|
69
|
+
});
|
|
70
|
+
throw error;
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
try {
|
|
69
74
|
let data = arrayOfJsonObjects.map((x) => JSON.stringify(x)).join('\n');
|
|
70
75
|
fs.writeFileSync(file, data);
|
|
71
76
|
} catch (error) {
|
|
72
|
-
throttledLog('info', `jsonl.js failed to write to file ${error}
|
|
77
|
+
throttledLog('info', `jsonl.js failed to write to file ${error.message}`, {
|
|
78
|
+
file,
|
|
79
|
+
error: error.message
|
|
80
|
+
});
|
|
81
|
+
throw error;
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
return file;
|
package/lib/websocket/server.js
CHANGED
|
@@ -51,7 +51,7 @@ class WSServer {
|
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
logger.
|
|
54
|
+
logger.info('WebSocketServer: Starting server, trying ports...', { ports: this.ports });
|
|
55
55
|
for (const port of this.ports) {
|
|
56
56
|
const ws = await new Promise((resolve) => {
|
|
57
57
|
logger.debug('WebSocketServer: Trying port ' + port);
|
|
@@ -104,7 +104,11 @@ class WSServer {
|
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
this.#socket.on('connection', (client) => {
|
|
107
|
-
logger.info('WebSocketServer: New client connection established'
|
|
107
|
+
logger.info('WebSocketServer: New client connection established', {
|
|
108
|
+
clientAddress: client._socket?.remoteAddress,
|
|
109
|
+
clientPort: client._socket?.remotePort,
|
|
110
|
+
totalClients: this.#socket.clients.size
|
|
111
|
+
});
|
|
108
112
|
|
|
109
113
|
let state = states.NEW;
|
|
110
114
|
const failValidation = () => {
|
|
@@ -124,17 +128,22 @@ class WSServer {
|
|
|
124
128
|
|
|
125
129
|
client.on('message', (data, isBinary) => {
|
|
126
130
|
let message = isBinary ? data : data.toString();
|
|
127
|
-
logger.
|
|
131
|
+
logger.info('WebSocketServer: Received message from client', {
|
|
128
132
|
isBinary,
|
|
129
133
|
messageLength: message.length,
|
|
134
|
+
messagePreview: message.substring(0, 100),
|
|
130
135
|
state
|
|
131
136
|
});
|
|
132
137
|
|
|
133
138
|
try {
|
|
134
139
|
message = JSON.parse(message);
|
|
135
|
-
logger.
|
|
140
|
+
logger.info('WebSocketServer: Parsed message', {
|
|
141
|
+
type: message.type,
|
|
142
|
+
hasPayload: !!message.payload,
|
|
143
|
+
payloadKeys: message.payload ? Object.keys(message.payload) : []
|
|
144
|
+
});
|
|
136
145
|
} catch (err) {
|
|
137
|
-
logger.
|
|
146
|
+
logger.info('WebSocketServer: Message is not JSON, treating as raw string', { rawMessage: message });
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
if (state === states.SENT_HEADERS) {
|
|
@@ -152,14 +161,14 @@ class WSServer {
|
|
|
152
161
|
this.emit('message', message, client);
|
|
153
162
|
});
|
|
154
163
|
|
|
155
|
-
logger.
|
|
164
|
+
logger.info('WebSocketServer: Sending connection header to client');
|
|
156
165
|
client.send('dashcam_desktop_socket_connected', (err) => {
|
|
157
166
|
if (err) {
|
|
158
167
|
logger.error('WebSocketServer: Failed to send connection header', { error: err.message });
|
|
159
168
|
client.close();
|
|
160
169
|
clearTimeout(timeout);
|
|
161
170
|
} else {
|
|
162
|
-
logger.
|
|
171
|
+
logger.info('WebSocketServer: Connection header sent, waiting for confirmation');
|
|
163
172
|
state = states.SENT_HEADERS;
|
|
164
173
|
}
|
|
165
174
|
});
|
|
@@ -176,9 +185,10 @@ class WSServer {
|
|
|
176
185
|
throw new Error('Server not currently running');
|
|
177
186
|
}
|
|
178
187
|
|
|
179
|
-
logger.
|
|
188
|
+
logger.info('WebSocketServer: Broadcasting message to all clients', {
|
|
180
189
|
clientCount: this.#socket.clients.size,
|
|
181
|
-
messageType: message.type || 'raw'
|
|
190
|
+
messageType: message.type || 'raw',
|
|
191
|
+
messagePayload: message.payload ? JSON.stringify(message.payload).substring(0, 100) : 'none'
|
|
182
192
|
});
|
|
183
193
|
|
|
184
194
|
this.#socket.clients.forEach((client) => {
|
package/package.json
CHANGED
package/test_workflow.sh
CHANGED
|
@@ -21,11 +21,13 @@ echo "✅ Web tracking configured"
|
|
|
21
21
|
echo ""
|
|
22
22
|
echo "3. Setting up file tracking..."
|
|
23
23
|
TEMP_FILE="/tmp/test-cli-log.txt"
|
|
24
|
-
echo "Using existing test file: $TEMP_FILE"
|
|
25
24
|
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
# Clear the file to start fresh (remove old events from previous test runs)
|
|
26
|
+
> "$TEMP_FILE"
|
|
27
|
+
echo "Created fresh test file: $TEMP_FILE"
|
|
28
|
+
|
|
29
|
+
# File is already tracked from previous tests, check if it exists in config
|
|
30
|
+
if ! ./bin/dashcam.js logs --list 2>/dev/null | grep -q "$TEMP_FILE"; then
|
|
29
31
|
./bin/dashcam.js logs --add --name=temp-file-tracking --type=file --file="$TEMP_FILE"
|
|
30
32
|
fi
|
|
31
33
|
echo "✅ File tracking configured"
|