dashcam 1.0.1-beta.35 → 1.0.1-beta.36

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.
@@ -15,6 +15,7 @@ import os from 'os';
15
15
  const PROCESS_DIR = path.join(os.homedir(), '.dashcam-cli');
16
16
  const STATUS_FILE = path.join(PROCESS_DIR, 'status.json');
17
17
  const RESULT_FILE = path.join(PROCESS_DIR, 'upload-result.json');
18
+ const STOP_SIGNAL_FILE = path.join(PROCESS_DIR, 'stop-signal.json');
18
19
 
19
20
  // Parse options from command line argument
20
21
  const optionsJson = process.argv[2];
@@ -96,79 +97,74 @@ async function runBackgroundRecording() {
96
97
  startTime: recordingResult.startTime
97
98
  });
98
99
 
99
- // Set up signal handlers for graceful shutdown
100
- const handleShutdown = async (signal) => {
101
- if (isShuttingDown) {
102
- logger.info('Shutdown already in progress...');
103
- return;
104
- }
105
- isShuttingDown = true;
106
-
107
- logger.info(`Received ${signal} signal, stopping background recording...`, { pid: process.pid });
108
-
100
+ // Poll for stop signal file instead of relying on system signals
101
+ // This works reliably across all platforms including Windows
102
+ logger.info('Background recording is now running. Polling for stop signal...');
103
+
104
+ const pollInterval = 1000; // Check every second
105
+
106
+ while (true) {
109
107
  try {
110
- // Stop the recording
111
- logger.info('Calling stopRecording...');
112
- const stopResult = await stopRecording();
113
-
114
- if (stopResult) {
115
- logger.info('Recording stopped successfully', {
116
- outputPath: stopResult.outputPath,
117
- duration: stopResult.duration
118
- });
108
+ if (fs.existsSync(STOP_SIGNAL_FILE)) {
109
+ if (isShuttingDown) {
110
+ logger.info('Shutdown already in progress...');
111
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
112
+ continue;
113
+ }
114
+ isShuttingDown = true;
119
115
 
120
- // Upload the recording
121
- logger.info('Starting upload...');
122
- const uploadResult = await upload(stopResult.outputPath, {
123
- title: options.title || 'Dashcam Recording',
124
- description: options.description || 'Recorded with Dashcam CLI',
125
- project: options.project || options.k,
126
- duration: stopResult.duration,
127
- clientStartDate: stopResult.clientStartDate,
128
- apps: stopResult.apps,
129
- logs: stopResult.logs,
130
- gifPath: stopResult.gifPath,
131
- snapshotPath: stopResult.snapshotPath
132
- });
116
+ logger.info('Stop signal file detected, initiating graceful shutdown');
117
+
118
+ // Read and log the signal
119
+ try {
120
+ const signalData = JSON.parse(fs.readFileSync(STOP_SIGNAL_FILE, 'utf8'));
121
+ logger.info('Stop signal received', signalData);
122
+ } catch (e) {
123
+ logger.warn('Could not parse stop signal file');
124
+ }
125
+
126
+ // Stop the recording
127
+ logger.info('Calling stopRecording...');
128
+ const stopResult = await stopRecording();
133
129
 
134
- logger.info('Upload complete', { shareLink: uploadResult.shareLink });
130
+ if (stopResult) {
131
+ logger.info('Recording stopped successfully', {
132
+ outputPath: stopResult.outputPath,
133
+ duration: stopResult.duration
134
+ });
135
+
136
+ // Write the recording result so stop command can upload it
137
+ logger.info('Writing recording result for stop command to upload');
138
+ writeUploadResult({
139
+ outputPath: stopResult.outputPath,
140
+ duration: stopResult.duration,
141
+ clientStartDate: stopResult.clientStartDate,
142
+ apps: stopResult.apps,
143
+ logs: stopResult.logs,
144
+ gifPath: stopResult.gifPath,
145
+ snapshotPath: stopResult.snapshotPath,
146
+ recordingStopped: true
147
+ });
148
+ logger.info('Recording result written successfully');
149
+ }
135
150
 
136
- // Write upload result for stop command to read
137
- logger.info('About to write upload result...');
138
- writeUploadResult({
139
- shareLink: uploadResult.shareLink,
140
- replayId: uploadResult.replay?.id
151
+ // Update status to indicate recording stopped
152
+ writeStatus({
153
+ isRecording: false,
154
+ completedTime: Date.now(),
155
+ pid: process.pid
141
156
  });
142
- logger.info('Upload result written successfully');
157
+
158
+ logger.info('Background process exiting successfully');
159
+ process.exit(0);
143
160
  }
144
-
145
- // Update status to indicate recording stopped
146
- writeStatus({
147
- isRecording: false,
148
- completedTime: Date.now(),
149
- pid: process.pid
150
- });
151
-
152
- logger.info('Background process exiting successfully');
153
- process.exit(0);
154
161
  } catch (error) {
155
- logger.error('Error during shutdown:', { error: error.message, stack: error.stack });
156
- process.exit(1);
162
+ logger.error('Error during stop signal handling', { error: error.message, stack: error.stack });
157
163
  }
158
- };
159
-
160
- process.on('SIGINT', () => {
161
- logger.info('SIGINT handler triggered');
162
- handleShutdown('SIGINT');
163
- });
164
- process.on('SIGTERM', () => {
165
- logger.info('SIGTERM handler triggered');
166
- handleShutdown('SIGTERM');
167
- });
168
-
169
- // Keep the process alive
170
- logger.info('Background recording is now running. Waiting for stop signal...');
171
- await new Promise(() => {}); // Wait indefinitely for signals
164
+
165
+ // Wait before next poll
166
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
167
+ }
172
168
 
173
169
  } catch (error) {
174
170
  logger.error('Background recording failed:', error);
package/bin/dashcam.js CHANGED
@@ -419,61 +419,56 @@ program
419
419
 
420
420
  console.log('Recording stopped successfully');
421
421
 
422
- // Wait for upload to complete (background process handles this)
423
- logger.debug('Waiting for background upload to complete...');
424
- console.log('⏳ Uploading recording...');
422
+ // Wait for background process to finish recording and write result
423
+ logger.debug('Waiting for background process to finish recording...');
425
424
 
426
- // Wait up to 2 minutes for upload result to appear
427
- const maxWaitForUpload = 120000; // 2 minutes
428
- const startWaitForUpload = Date.now();
429
- let uploadResult = null;
425
+ // Wait up to 30 seconds for recording result to appear
426
+ const maxWaitForRecording = 30000;
427
+ const startWaitForRecording = Date.now();
428
+ let recordingResult = null;
430
429
 
431
- while (!uploadResult && (Date.now() - startWaitForUpload) < maxWaitForUpload) {
432
- uploadResult = processManager.readUploadResult();
433
- if (!uploadResult) {
434
- await new Promise(resolve => setTimeout(resolve, 1000)); // Check every second
430
+ while (!recordingResult && (Date.now() - startWaitForRecording) < maxWaitForRecording) {
431
+ const resultData = processManager.readUploadResult();
432
+ if (resultData && resultData.recordingStopped) {
433
+ recordingResult = resultData;
434
+ logger.debug('Recording result received from background process');
435
+ break;
435
436
  }
437
+ await new Promise(resolve => setTimeout(resolve, 500)); // Check every 500ms
436
438
  }
437
439
 
438
- logger.debug('Upload result read attempt', { found: !!uploadResult, shareLink: uploadResult?.shareLink });
439
-
440
- if (uploadResult && uploadResult.shareLink) {
441
- console.log('📹 Watch your recording:', uploadResult.shareLink);
442
- // Clean up the result file now that we've read it
443
- processManager.cleanup();
444
- process.exit(0);
445
- }
446
-
447
- // Check if files still exist - if not, background process already uploaded
448
- const filesExist = fs.existsSync(result.outputPath) &&
449
- (!result.gifPath || fs.existsSync(result.gifPath)) &&
450
- (!result.snapshotPath || fs.existsSync(result.snapshotPath));
451
-
452
- if (!filesExist) {
453
- console.log('✅ Recording uploaded by background process');
454
- logger.info('Files were cleaned up by background process');
455
- process.exit(0);
440
+ if (!recordingResult || !recordingResult.outputPath) {
441
+ logger.error('Failed to get recording result from background process');
442
+ console.log('⚠️ Failed to get recording from background process');
443
+ process.exit(1);
456
444
  }
457
445
 
458
- // Always attempt to upload - let upload function find project if needed
446
+ // Now upload the recording ourselves
447
+ logger.info('Uploading recording from stop command', { outputPath: recordingResult.outputPath });
459
448
  console.log('Uploading recording...');
449
+
460
450
  try {
461
- const uploadResult = await upload(result.outputPath, {
462
- title: activeStatus?.options?.title,
451
+ const uploadResult = await upload(recordingResult.outputPath, {
452
+ title: activeStatus?.options?.title || 'Dashcam Recording',
463
453
  description: activeStatus?.options?.description,
464
- project: activeStatus?.options?.project, // May be undefined, that's ok
465
- duration: result.duration,
466
- clientStartDate: result.clientStartDate,
467
- apps: result.apps,
468
- icons: result.icons,
469
- gifPath: result.gifPath,
470
- snapshotPath: result.snapshotPath
454
+ project: activeStatus?.options?.project,
455
+ duration: recordingResult.duration,
456
+ clientStartDate: recordingResult.clientStartDate,
457
+ apps: recordingResult.apps,
458
+ logs: recordingResult.logs,
459
+ gifPath: recordingResult.gifPath,
460
+ snapshotPath: recordingResult.snapshotPath
471
461
  });
472
462
 
473
463
  console.log('📹 Watch your recording:', uploadResult.shareLink);
464
+
465
+ // Clean up result file
466
+ processManager.cleanup();
467
+ process.exit(0);
474
468
  } catch (uploadError) {
475
469
  console.error('Upload failed:', uploadError.message);
476
- console.log('Recording saved locally:', result.outputPath);
470
+ console.log('Recording saved locally:', recordingResult.outputPath);
471
+ process.exit(1);
477
472
  }
478
473
  } catch (error) {
479
474
  console.error('Failed to stop recording:', error.message);
@@ -9,6 +9,7 @@ import { logger } from './logger.js';
9
9
  const PROCESS_DIR = path.join(os.homedir(), '.dashcam-cli');
10
10
  const STATUS_FILE = path.join(PROCESS_DIR, 'status.json');
11
11
  const RESULT_FILE = path.join(PROCESS_DIR, 'upload-result.json');
12
+ const STOP_SIGNAL_FILE = path.join(PROCESS_DIR, 'stop-signal.json');
12
13
 
13
14
  // Ensure process directory exists
14
15
  if (!fs.existsSync(PROCESS_DIR)) {
@@ -121,6 +122,10 @@ class ProcessManager {
121
122
  fs.unlinkSync(STATUS_FILE);
122
123
  logger.debug('Deleted STATUS file');
123
124
  }
125
+ if (fs.existsSync(STOP_SIGNAL_FILE)) {
126
+ fs.unlinkSync(STOP_SIGNAL_FILE);
127
+ logger.debug('Deleted STOP_SIGNAL file');
128
+ }
124
129
  if (!preserveResult && fs.existsSync(RESULT_FILE)) {
125
130
  fs.unlinkSync(RESULT_FILE);
126
131
  logger.debug('Deleted RESULT file');
@@ -148,41 +153,27 @@ class ProcessManager {
148
153
  return false;
149
154
  }
150
155
 
151
- const pid = status.pid;
152
- logger.info('Stopping background recording process', { pid, hasStoredPid: !!pid });
156
+ logger.info('Signaling background process to stop recording');
153
157
 
154
- // Try to gracefully stop the process if we have a PID
155
- if (pid) {
156
- const isWindows = process.platform === 'win32';
157
-
158
- if (isWindows) {
159
- logger.info('Windows detected, attempting graceful shutdown first');
160
- try {
161
- const { execSync } = await import('child_process');
162
- try {
163
- execSync(`taskkill /PID ${pid} /T`, { stdio: 'ignore', timeout: 5000 });
164
- logger.info('Sent graceful shutdown signal to process');
165
- } catch (error) {
166
- logger.warn('Graceful shutdown failed, forcing termination');
167
- execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
168
- }
169
- } catch (error) {
170
- logger.warn('Failed to kill process with taskkill', { error: error.message });
171
- }
172
- } else {
173
- try {
174
- process.kill(pid, 'SIGINT');
175
- } catch (error) {
176
- logger.warn('Failed to send SIGINT', { error: error.message });
177
- }
178
- }
158
+ // Write stop signal file instead of killing the process
159
+ // The background process polls for this file
160
+ try {
161
+ fs.writeFileSync(STOP_SIGNAL_FILE, JSON.stringify({
162
+ timestamp: Date.now(),
163
+ requestedBy: 'stop-command'
164
+ }));
165
+ logger.info('Stop signal file written successfully');
166
+ } catch (error) {
167
+ logger.error('Failed to write stop signal file', { error: error.message });
168
+ throw new Error('Failed to signal background process to stop');
179
169
  }
180
170
 
181
- // Mark recording as stopped in status file immediately
171
+ // Mark recording as stopping in status file
182
172
  this.writeStatus({
183
173
  isRecording: false,
174
+ stopping: true,
184
175
  stoppedAt: Date.now(),
185
- pid: pid
176
+ pid: status.pid
186
177
  });
187
178
 
188
179
  if (status) {
@@ -202,7 +193,8 @@ class ProcessManager {
202
193
  logs: []
203
194
  };
204
195
 
205
- this.cleanup({ preserveResult: true });
196
+ // Don't cleanup here - the background process needs to read the stop signal file
197
+ // Cleanup will happen after we get the recording result
206
198
  return result;
207
199
  } else {
208
200
  throw new Error('No status information available for active recording');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashcam",
3
- "version": "1.0.1-beta.35",
3
+ "version": "1.0.1-beta.36",
4
4
  "description": "Minimal CLI version of Dashcam desktop app",
5
5
  "main": "bin/index.js",
6
6
  "bin": {