dashcam 1.4.2-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 +3 -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
|
@@ -414,19 +414,12 @@ export async function startRecording({
|
|
|
414
414
|
reject: false,
|
|
415
415
|
all: true, // Capture both stdout and stderr
|
|
416
416
|
stdin: 'pipe', // Enable stdin for sending 'q' to stop recording
|
|
417
|
-
detached: false,
|
|
417
|
+
detached: false, // Keep attached so it dies with parent
|
|
418
418
|
windowsHide: true // Hide the console window on Windows
|
|
419
419
|
});
|
|
420
420
|
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
try {
|
|
424
|
-
currentRecording.unref();
|
|
425
|
-
} catch (e) {
|
|
426
|
-
// Ignore errors if unref is not available
|
|
427
|
-
logger.debug('Could not unref ffmpeg process');
|
|
428
|
-
}
|
|
429
|
-
}
|
|
421
|
+
// Don't unref - we want FFmpeg to be killed when parent dies
|
|
422
|
+
// Removing unref() ensures orphaned processes don't hang around
|
|
430
423
|
|
|
431
424
|
logger.info('FFmpeg process spawned', {
|
|
432
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"
|