dashcam 1.0.1-beta.34 → 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.
@@ -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;
@@ -83,45 +90,22 @@ class ProcessManager {
83
90
  }
84
91
  }
85
92
 
86
- writePid(pid = process.pid) {
87
- try {
88
- fs.writeFileSync(PID_FILE, pid.toString());
89
- } catch (error) {
90
- logger.error('Failed to write PID file', { error });
91
- }
92
- }
93
-
94
- readPid() {
95
- try {
96
- if (!fs.existsSync(PID_FILE)) return null;
97
- const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim());
98
- return isNaN(pid) ? null : pid;
99
- } catch (error) {
100
- return null;
101
- }
102
- }
103
-
104
- isProcessRunning(pid) {
105
- if (!pid) return false;
106
- try {
107
- process.kill(pid, 0); // Signal 0 just checks if process exists
108
- return true;
109
- } catch (error) {
110
- return false;
111
- }
112
- }
113
-
114
93
  isRecordingActive() {
115
- const pid = this.readPid();
116
94
  const status = this.readStatus();
117
95
 
118
- if (!pid || !this.isProcessRunning(pid)) {
119
- // 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) {
120
104
  this.cleanup({ preserveResult: true });
121
105
  return false;
122
106
  }
123
107
 
124
- return status && status.isRecording;
108
+ return false;
125
109
  }
126
110
 
127
111
  getActiveStatus() {
@@ -133,10 +117,6 @@ class ProcessManager {
133
117
  const { preserveResult = false } = options;
134
118
  try {
135
119
  logger.debug('Cleanup called', { preserveResult, resultFileExists: fs.existsSync(RESULT_FILE) });
136
- if (fs.existsSync(PID_FILE)) {
137
- fs.unlinkSync(PID_FILE);
138
- logger.debug('Deleted PID file');
139
- }
140
120
  if (fs.existsSync(STATUS_FILE)) {
141
121
  fs.unlinkSync(STATUS_FILE);
142
122
  logger.debug('Deleted STATUS file');
@@ -161,64 +141,50 @@ class ProcessManager {
161
141
  this.isStopping = true;
162
142
 
163
143
  try {
164
- const pid = this.readPid();
165
144
  const status = this.readStatus();
166
145
 
167
- if (!pid || !this.isProcessRunning(pid)) {
168
- logger.warn('No active recording process found');
146
+ if (!status || !status.isRecording) {
147
+ logger.warn('No active recording found in status file');
169
148
  return false;
170
149
  }
171
150
 
172
- logger.info('Stopping detached background process', { pid });
173
-
174
- // Send signal to background process
175
- const isWindows = process.platform === 'win32';
151
+ const pid = status.pid;
152
+ logger.info('Stopping background recording process', { pid, hasStoredPid: !!pid });
176
153
 
177
- if (isWindows) {
178
- logger.info('Windows detected, attempting graceful shutdown first');
179
- try {
180
- // First try graceful shutdown without /F (force) flag
181
- // This sends CTRL+C which Node.js can handle
182
- 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');
183
160
  try {
184
- execSync(`taskkill /PID ${pid} /T`, { stdio: 'ignore', timeout: 5000 });
185
- 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
+ }
186
169
  } catch (error) {
187
- // If graceful shutdown fails or times out, force kill
188
- logger.warn('Graceful shutdown failed, forcing termination');
189
- execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
170
+ logger.warn('Failed to kill process with taskkill', { error: error.message });
190
171
  }
191
- } catch (error) {
192
- logger.warn('Failed to kill process with taskkill', { error: error.message });
193
- }
194
- } else {
195
- try {
196
- process.kill(pid, 'SIGINT');
197
- } catch (error) {
198
- logger.warn('Failed to send SIGINT', { error: error.message });
199
- }
200
- }
201
-
202
- // Wait for the process to actually finish
203
- const maxWaitTime = 30000; // 30 seconds
204
- const startWait = Date.now();
205
-
206
- while (this.isProcessRunning(pid) && (Date.now() - startWait) < maxWaitTime) {
207
- await new Promise(resolve => setTimeout(resolve, 500));
208
- }
209
-
210
- if (this.isProcessRunning(pid)) {
211
- logger.warn('Process did not stop within timeout');
212
- if (!isWindows) {
172
+ } else {
213
173
  try {
214
- process.kill(pid, 'SIGKILL');
174
+ process.kill(pid, 'SIGINT');
215
175
  } catch (error) {
216
- logger.warn('Failed to send SIGKILL', { error: error.message });
176
+ logger.warn('Failed to send SIGINT', { error: error.message });
217
177
  }
218
- await new Promise(resolve => setTimeout(resolve, 1000));
219
178
  }
220
179
  }
221
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
+
222
188
  if (status) {
223
189
  logger.info('Recording stopped, returning status', {
224
190
  outputPath: status.outputPath,
@@ -272,9 +238,6 @@ class ProcessManager {
272
238
  child.unref();
273
239
 
274
240
  const pid = child.pid;
275
-
276
- // Write PID file immediately so other commands can find the recording process
277
- this.writePid(pid);
278
241
 
279
242
  logger.info('Background process spawned successfully', {
280
243
  pid,
@@ -282,17 +245,24 @@ class ProcessManager {
282
245
  });
283
246
 
284
247
  // Wait a moment for the background process to write its status
285
- await new Promise(resolve => setTimeout(resolve, 1000));
248
+ await new Promise(resolve => setTimeout(resolve, 1500));
286
249
 
287
250
  // Read the status to get output path and start time
288
251
  const status = this.readStatus();
289
252
 
290
253
  if (!status) {
254
+ logger.error('Background process failed to write status');
291
255
  throw new Error('Background process failed to write status');
292
256
  }
293
257
 
258
+ logger.info('Recording started successfully', {
259
+ pid: status.pid,
260
+ outputPath: status.outputPath,
261
+ startTime: status.startTime
262
+ });
263
+
294
264
  return {
295
- pid,
265
+ pid: status.pid,
296
266
  outputPath: status.outputPath,
297
267
  startTime: status.startTime
298
268
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashcam",
3
- "version": "1.0.1-beta.34",
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": {