dashcam 1.0.1-beta.33 → 1.0.1-beta.35

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.
@@ -55,8 +55,13 @@ function writeUploadResult(result) {
55
55
  timestamp: Date.now()
56
56
  }, null, 2));
57
57
  logger.info('Successfully wrote upload result to file');
58
+ // Verify the file was written
59
+ if (fs.existsSync(RESULT_FILE)) {
60
+ const content = fs.readFileSync(RESULT_FILE, 'utf8');
61
+ logger.info('Verified upload result file exists and contains', { content: content.substring(0, 100) });
62
+ }
58
63
  } catch (error) {
59
- logger.error('Failed to write upload result file', { error });
64
+ logger.error('Failed to write upload result file', { error: error.message, stack: error.stack });
60
65
  }
61
66
  }
62
67
 
@@ -99,10 +104,11 @@ async function runBackgroundRecording() {
99
104
  }
100
105
  isShuttingDown = true;
101
106
 
102
- logger.info(`Received ${signal}, stopping background recording...`);
107
+ logger.info(`Received ${signal} signal, stopping background recording...`, { pid: process.pid });
103
108
 
104
109
  try {
105
110
  // Stop the recording
111
+ logger.info('Calling stopRecording...');
106
112
  const stopResult = await stopRecording();
107
113
 
108
114
  if (stopResult) {
@@ -128,10 +134,12 @@ async function runBackgroundRecording() {
128
134
  logger.info('Upload complete', { shareLink: uploadResult.shareLink });
129
135
 
130
136
  // Write upload result for stop command to read
137
+ logger.info('About to write upload result...');
131
138
  writeUploadResult({
132
139
  shareLink: uploadResult.shareLink,
133
140
  replayId: uploadResult.replay?.id
134
141
  });
142
+ logger.info('Upload result written successfully');
135
143
  }
136
144
 
137
145
  // Update status to indicate recording stopped
@@ -144,13 +152,19 @@ async function runBackgroundRecording() {
144
152
  logger.info('Background process exiting successfully');
145
153
  process.exit(0);
146
154
  } catch (error) {
147
- logger.error('Error during shutdown:', error);
155
+ logger.error('Error during shutdown:', { error: error.message, stack: error.stack });
148
156
  process.exit(1);
149
157
  }
150
158
  };
151
159
 
152
- process.on('SIGINT', () => handleShutdown('SIGINT'));
153
- process.on('SIGTERM', () => handleShutdown('SIGTERM'));
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
+ });
154
168
 
155
169
  // Keep the process alive
156
170
  logger.info('Background recording is now running. Waiting for stop signal...');
@@ -7,7 +7,6 @@ import { logger } from './logger.js';
7
7
 
8
8
  // Use a fixed directory in the user's home directory for cross-process communication
9
9
  const PROCESS_DIR = path.join(os.homedir(), '.dashcam-cli');
10
- const PID_FILE = path.join(PROCESS_DIR, 'recording.pid');
11
10
  const STATUS_FILE = path.join(PROCESS_DIR, 'status.json');
12
11
  const RESULT_FILE = path.join(PROCESS_DIR, 'upload-result.json');
13
12
 
@@ -42,7 +41,15 @@ class ProcessManager {
42
41
  try {
43
42
  if (!fs.existsSync(STATUS_FILE)) return null;
44
43
  const data = fs.readFileSync(STATUS_FILE, 'utf8');
45
- return JSON.parse(data);
44
+ const status = JSON.parse(data);
45
+
46
+ // Check if status is stale (older than 24 hours)
47
+ if (status.timestamp && (Date.now() - status.timestamp) > 24 * 60 * 60 * 1000) {
48
+ logger.warn('Status file is stale, ignoring');
49
+ return null;
50
+ }
51
+
52
+ return status;
46
53
  } catch (error) {
47
54
  logger.error('Failed to read status file', { error });
48
55
  return null;
@@ -59,65 +66,46 @@ class ProcessManager {
59
66
  logger.info('Successfully wrote upload result to file');
60
67
  // Verify it was written
61
68
  if (fs.existsSync(RESULT_FILE)) {
62
- logger.info('Verified upload result file exists');
69
+ const fileSize = fs.statSync(RESULT_FILE).size;
70
+ logger.info('Verified upload result file exists', { size: fileSize });
63
71
  } else {
64
72
  logger.error('Upload result file does not exist after write!');
65
73
  }
66
74
  } catch (error) {
67
- logger.error('Failed to write upload result file', { error });
75
+ logger.error('Failed to write upload result file', { error: error.message, stack: error.stack });
68
76
  }
69
77
  }
70
78
 
71
79
  readUploadResult() {
72
80
  try {
81
+ logger.debug('Checking for upload result file', { path: RESULT_FILE, exists: fs.existsSync(RESULT_FILE) });
73
82
  if (!fs.existsSync(RESULT_FILE)) return null;
74
83
  const data = fs.readFileSync(RESULT_FILE, 'utf8');
75
- return JSON.parse(data);
76
- } catch (error) {
77
- logger.error('Failed to read upload result file', { error });
78
- return null;
79
- }
80
- }
81
-
82
- writePid(pid = process.pid) {
83
- try {
84
- fs.writeFileSync(PID_FILE, pid.toString());
85
- } catch (error) {
86
- logger.error('Failed to write PID file', { error });
87
- }
88
- }
89
-
90
- readPid() {
91
- try {
92
- if (!fs.existsSync(PID_FILE)) return null;
93
- const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim());
94
- return isNaN(pid) ? null : pid;
84
+ const result = JSON.parse(data);
85
+ logger.debug('Successfully read upload result', { shareLink: result.shareLink });
86
+ return result;
95
87
  } catch (error) {
88
+ logger.error('Failed to read upload result file', { error: error.message, stack: error.stack });
96
89
  return null;
97
90
  }
98
91
  }
99
92
 
100
- isProcessRunning(pid) {
101
- if (!pid) return false;
102
- try {
103
- process.kill(pid, 0); // Signal 0 just checks if process exists
104
- return true;
105
- } catch (error) {
106
- return false;
107
- }
108
- }
109
-
110
93
  isRecordingActive() {
111
- const pid = this.readPid();
112
94
  const status = this.readStatus();
113
95
 
114
- if (!pid || !this.isProcessRunning(pid)) {
115
- // Clean up but preserve upload result in case the background process just finished uploading
96
+ // Recording is active if we have a status file with isRecording=true
97
+ // and it's not stale
98
+ if (status && status.isRecording) {
99
+ return true;
100
+ }
101
+
102
+ // Clean up stale files if recording is not active
103
+ if (!status || !status.isRecording) {
116
104
  this.cleanup({ preserveResult: true });
117
105
  return false;
118
106
  }
119
107
 
120
- return status && status.isRecording;
108
+ return false;
121
109
  }
122
110
 
123
111
  getActiveStatus() {
@@ -128,11 +116,19 @@ class ProcessManager {
128
116
  cleanup(options = {}) {
129
117
  const { preserveResult = false } = options;
130
118
  try {
131
- if (fs.existsSync(PID_FILE)) fs.unlinkSync(PID_FILE);
132
- if (fs.existsSync(STATUS_FILE)) fs.unlinkSync(STATUS_FILE);
133
- if (!preserveResult && fs.existsSync(RESULT_FILE)) fs.unlinkSync(RESULT_FILE);
119
+ logger.debug('Cleanup called', { preserveResult, resultFileExists: fs.existsSync(RESULT_FILE) });
120
+ if (fs.existsSync(STATUS_FILE)) {
121
+ fs.unlinkSync(STATUS_FILE);
122
+ logger.debug('Deleted STATUS file');
123
+ }
124
+ if (!preserveResult && fs.existsSync(RESULT_FILE)) {
125
+ fs.unlinkSync(RESULT_FILE);
126
+ logger.debug('Deleted RESULT file');
127
+ } else if (preserveResult && fs.existsSync(RESULT_FILE)) {
128
+ logger.debug('Preserved RESULT file');
129
+ }
134
130
  } catch (error) {
135
- logger.error('Failed to cleanup process files', { error });
131
+ logger.error('Failed to cleanup process files', { error: error.message });
136
132
  }
137
133
  }
138
134
 
@@ -145,64 +141,50 @@ class ProcessManager {
145
141
  this.isStopping = true;
146
142
 
147
143
  try {
148
- const pid = this.readPid();
149
144
  const status = this.readStatus();
150
145
 
151
- if (!pid || !this.isProcessRunning(pid)) {
152
- logger.warn('No active recording process found');
146
+ if (!status || !status.isRecording) {
147
+ logger.warn('No active recording found in status file');
153
148
  return false;
154
149
  }
155
150
 
156
- logger.info('Stopping detached background process', { pid });
157
-
158
- // Send signal to background process
159
- const isWindows = process.platform === 'win32';
151
+ const pid = status.pid;
152
+ logger.info('Stopping background recording process', { pid, hasStoredPid: !!pid });
160
153
 
161
- if (isWindows) {
162
- logger.info('Windows detected, attempting graceful shutdown first');
163
- try {
164
- // First try graceful shutdown without /F (force) flag
165
- // This sends CTRL+C which Node.js can handle
166
- const { execSync } = await import('child_process');
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');
167
160
  try {
168
- execSync(`taskkill /PID ${pid} /T`, { stdio: 'ignore', timeout: 5000 });
169
- logger.info('Sent graceful shutdown signal to process');
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
+ }
170
169
  } catch (error) {
171
- // If graceful shutdown fails or times out, force kill
172
- logger.warn('Graceful shutdown failed, forcing termination');
173
- execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
170
+ logger.warn('Failed to kill process with taskkill', { error: error.message });
174
171
  }
175
- } catch (error) {
176
- logger.warn('Failed to kill process with taskkill', { error: error.message });
177
- }
178
- } else {
179
- try {
180
- process.kill(pid, 'SIGINT');
181
- } catch (error) {
182
- logger.warn('Failed to send SIGINT', { error: error.message });
183
- }
184
- }
185
-
186
- // Wait for the process to actually finish
187
- const maxWaitTime = 30000; // 30 seconds
188
- const startWait = Date.now();
189
-
190
- while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxWaitTime) {
191
- await new Promise(resolve => setTimeout(resolve, 500));
192
- }
193
-
194
- if (this.isProcessRunning(pid)) {
195
- logger.warn('Process did not stop within timeout');
196
- if (!isWindows) {
172
+ } else {
197
173
  try {
198
- process.kill(pid, 'SIGKILL');
174
+ process.kill(pid, 'SIGINT');
199
175
  } catch (error) {
200
- logger.warn('Failed to send SIGKILL', { error: error.message });
176
+ logger.warn('Failed to send SIGINT', { error: error.message });
201
177
  }
202
- await new Promise(resolve => setTimeout(resolve, 1000));
203
178
  }
204
179
  }
205
180
 
181
+ // Mark recording as stopped in status file immediately
182
+ this.writeStatus({
183
+ isRecording: false,
184
+ stoppedAt: Date.now(),
185
+ pid: pid
186
+ });
187
+
206
188
  if (status) {
207
189
  logger.info('Recording stopped, returning status', {
208
190
  outputPath: status.outputPath,
@@ -256,9 +238,6 @@ class ProcessManager {
256
238
  child.unref();
257
239
 
258
240
  const pid = child.pid;
259
-
260
- // Write PID file immediately so other commands can find the recording process
261
- this.writePid(pid);
262
241
 
263
242
  logger.info('Background process spawned successfully', {
264
243
  pid,
@@ -266,17 +245,24 @@ class ProcessManager {
266
245
  });
267
246
 
268
247
  // Wait a moment for the background process to write its status
269
- await new Promise(resolve => setTimeout(resolve, 1000));
248
+ await new Promise(resolve => setTimeout(resolve, 1500));
270
249
 
271
250
  // Read the status to get output path and start time
272
251
  const status = this.readStatus();
273
252
 
274
253
  if (!status) {
254
+ logger.error('Background process failed to write status');
275
255
  throw new Error('Background process failed to write status');
276
256
  }
277
257
 
258
+ logger.info('Recording started successfully', {
259
+ pid: status.pid,
260
+ outputPath: status.outputPath,
261
+ startTime: status.startTime
262
+ });
263
+
278
264
  return {
279
- pid,
265
+ pid: status.pid,
280
266
  outputPath: status.outputPath,
281
267
  startTime: status.startTime
282
268
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashcam",
3
- "version": "1.0.1-beta.33",
3
+ "version": "1.0.1-beta.35",
4
4
  "description": "Minimal CLI version of Dashcam desktop app",
5
5
  "main": "bin/index.js",
6
6
  "bin": {