dashcam 1.4.11-beta.0 → 1.4.12-beta.0

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.js CHANGED
@@ -531,7 +531,7 @@ program
531
531
  logger.info('Loaded recording result from background process', {
532
532
  outputPath: backgroundResult.outputPath,
533
533
  duration: backgroundResult.duration,
534
- performanceSamples: backgroundResult.performance?.summary?.sampleCount || 0
534
+ hasPerformanceFile: !!backgroundResult.performanceFile
535
535
  });
536
536
  } catch (error) {
537
537
  logger.warn('Failed to read background recording result', { error: error.message });
@@ -730,20 +730,23 @@ program
730
730
  }
731
731
 
732
732
  // Read performance data from file if available
733
- let performanceData = null;
733
+ // Instead of embedding all samples, we'll just pass the file path for S3 upload
734
+ let performanceFile = null;
735
+ let performanceSummary = null;
734
736
  try {
735
- const performanceFile = path.join(recordingDir, 'performance.jsonl');
736
- if (fs.existsSync(performanceFile)) {
737
- logger.info('Found performance file, reading data', { file: performanceFile });
737
+ const perfFile = path.join(recordingDir, 'performance.jsonl');
738
+ if (fs.existsSync(perfFile)) {
739
+ logger.info('Found performance file, will upload to S3', { file: perfFile });
740
+ performanceFile = perfFile;
738
741
 
739
- // Read the file directly and parse samples
740
- const fileContent = fs.readFileSync(performanceFile, 'utf8');
742
+ // Read samples just to calculate summary for logging (not for DB storage)
743
+ const fileContent = fs.readFileSync(perfFile, 'utf8');
741
744
  const lines = fileContent.trim().split('\n').filter(line => line.length > 0);
742
745
  const samples = lines.map(line => JSON.parse(line));
743
746
 
744
747
  logger.info('Parsed performance samples from file', { sampleCount: samples.length });
745
748
 
746
- // Calculate summary statistics
749
+ // Calculate summary statistics just for logging
747
750
  if (samples.length > 0) {
748
751
  const firstSample = samples[0];
749
752
  const lastSample = samples[samples.length - 1];
@@ -770,44 +773,28 @@ program
770
773
  });
771
774
 
772
775
  const count = samples.length;
773
- const finalSample = samples[samples.length - 1];
774
- const totalBytesReceived = finalSample.network?.bytesReceived || 0;
775
- const totalBytesSent = finalSample.network?.bytesSent || 0;
776
776
 
777
- const summary = {
777
+ performanceSummary = {
778
778
  durationMs: lastSample.timestamp - firstSample.timestamp,
779
779
  sampleCount: count,
780
- monitorInterval: 5000,
781
780
  avgProcessCPU: totalProcessCPU / count,
782
781
  maxProcessCPU,
783
- avgProcessMemoryBytes: totalProcessMemory / count,
784
782
  avgProcessMemoryMB: (totalProcessMemory / count) / (1024 * 1024),
785
- maxProcessMemoryBytes: maxProcessMemory,
786
- maxProcessMemoryMB: maxProcessMemory / (1024 * 1024),
787
- avgSystemMemoryUsagePercent: totalSystemMemoryUsage / count,
788
- maxSystemMemoryUsagePercent: maxSystemMemoryUsage,
789
- totalSystemMemoryBytes: firstSample.system?.totalMemory || 0,
790
- totalSystemMemoryGB: (firstSample.system?.totalMemory || 0) / (1024 * 1024 * 1024),
791
- totalBytesReceived,
792
- totalBytesSent,
793
- totalMBReceived: totalBytesReceived / (1024 * 1024),
794
- totalMBSent: totalBytesSent / (1024 * 1024)
783
+ maxProcessMemoryMB: maxProcessMemory / (1024 * 1024)
795
784
  };
796
785
 
797
- performanceData = { samples, summary };
798
-
799
- logger.info('Calculated performance summary', {
800
- sampleCount: summary.sampleCount,
801
- avgCPU: summary.avgProcessCPU.toFixed(1),
802
- maxCPU: summary.maxProcessCPU.toFixed(1),
803
- avgMemoryMB: summary.avgProcessMemoryMB.toFixed(1)
786
+ logger.info('Calculated performance summary for logging', {
787
+ sampleCount: performanceSummary.sampleCount,
788
+ avgCPU: performanceSummary.avgProcessCPU.toFixed(1),
789
+ maxCPU: performanceSummary.maxProcessCPU.toFixed(1),
790
+ avgMemoryMB: performanceSummary.avgProcessMemoryMB.toFixed(1)
804
791
  });
805
792
  }
806
793
  } else {
807
- logger.debug('No performance file found', { expectedPath: performanceFile });
794
+ logger.debug('No performance file found', { expectedPath: perfFile });
808
795
  }
809
796
  } catch (error) {
810
- logger.warn('Failed to load performance data', { error: error.message, stack: error.stack });
797
+ logger.warn('Failed to process performance data', { error: error.message, stack: error.stack });
811
798
  }
812
799
 
813
800
  // The recording is on disk and can be uploaded with full metadata
@@ -821,17 +808,17 @@ program
821
808
  apps: backgroundResult?.apps || appTrackingResults.apps,
822
809
  icons: backgroundResult?.icons || appTrackingResults.icons,
823
810
  logs: backgroundResult?.logs || logTrackingResults,
824
- performance: performanceData || backgroundResult?.performance || null, // Prefer file data, fallback to background result
811
+ performanceFile: performanceFile || backgroundResult?.performanceFile || null, // Pass file path for S3 upload
825
812
  title: activeStatus?.options?.title,
826
813
  description: activeStatus?.options?.description,
827
814
  project: activeStatus?.options?.project
828
815
  };
829
816
 
830
817
  logger.info('Recording result prepared for upload', {
831
- hasPerformanceData: !!recordingResult.performance,
832
- performanceSamples: recordingResult.performance?.summary?.sampleCount || 0,
833
- avgCPU: recordingResult.performance?.summary?.avgProcessCPU?.toFixed(1) || 'N/A',
834
- maxCPU: recordingResult.performance?.summary?.maxProcessCPU?.toFixed(1) || 'N/A'
818
+ hasPerformanceFile: !!recordingResult.performanceFile,
819
+ performanceSamples: performanceSummary?.sampleCount || 0,
820
+ avgCPU: performanceSummary?.avgProcessCPU?.toFixed(1) || 'N/A',
821
+ maxCPU: performanceSummary?.maxProcessCPU?.toFixed(1) || 'N/A'
835
822
  });
836
823
 
837
824
  if (!recordingResult || !recordingResult.outputPath) {
@@ -853,7 +840,7 @@ program
853
840
  apps: recordingResult.apps,
854
841
  icons: recordingResult.icons,
855
842
  logs: recordingResult.logs,
856
- performance: recordingResult.performance, // Include performance data
843
+ performanceFile: recordingResult.performanceFile, // Path to JSONL file for S3 upload
857
844
  gifPath: recordingResult.gifPath,
858
845
  snapshotPath: recordingResult.snapshotPath
859
846
  });
package/lib/auth.js CHANGED
@@ -217,6 +217,40 @@ const auth = {
217
217
  logExit();
218
218
  throw error;
219
219
  } // Return the full response with video, gif, image objects
220
+ },
221
+
222
+ async createPerformanceSts(replayId) {
223
+ const logExit = logFunctionCall('auth.createPerformanceSts');
224
+
225
+ logger.debug('Creating performance STS credentials', { replayId });
226
+ const token = await this.getToken();
227
+
228
+ try {
229
+ const response = await got.post(`${API_ENDPOINT}/api/v1/replay/performance`, {
230
+ headers: {
231
+ Authorization: `Bearer ${token}`
232
+ },
233
+ json: {
234
+ replayId
235
+ },
236
+ timeout: 30000
237
+ }).json();
238
+
239
+ logger.verbose('Performance STS credentials created', {
240
+ hasCredentials: !!response.bucket,
241
+ file: response.file
242
+ });
243
+
244
+ logExit();
245
+ return response;
246
+ } catch (error) {
247
+ logger.error('Failed to create performance STS credentials:', {
248
+ message: error.message,
249
+ statusCode: error.response?.statusCode
250
+ });
251
+ logExit();
252
+ throw error;
253
+ }
220
254
  }
221
255
  };
222
256
 
@@ -403,7 +403,8 @@ class PerformanceTracker {
403
403
 
404
404
  const result = {
405
405
  samples: this.samples,
406
- summary
406
+ summary,
407
+ performanceFile: this.performanceFile // Return file path for S3 upload
407
408
  };
408
409
 
409
410
  // DON'T delete the performance file - keep it for the stop command to read
package/lib/recorder.js CHANGED
@@ -857,13 +857,15 @@ export async function stopRecording() {
857
857
  apps: appTrackingResults.apps, // Include tracked applications
858
858
  icons: appTrackingResults.icons, // Include application icons metadata
859
859
  logs: logTrackingResults, // Include log tracking results
860
- performance: performanceResults // Include performance tracking results
860
+ performanceFile: performanceResults.performanceFile, // Path to JSONL file for S3 upload
861
+ performanceSummary: performanceResults.summary // Just include summary for logging (not uploaded to DB)
861
862
  };
862
863
 
863
864
  logger.info('Recording stopped with performance data', {
864
865
  clientStartDate: recordingStartTime,
865
866
  clientStartDateReadable: new Date(recordingStartTime).toISOString(),
866
867
  duration: result.duration,
868
+ performanceFile: performanceResults.performanceFile,
867
869
  performanceSamples: performanceResults.summary?.sampleCount || 0,
868
870
  avgCPU: performanceResults.summary?.avgProcessCPU?.toFixed(1) || 0,
869
871
  maxCPU: performanceResults.summary?.maxProcessCPU?.toFixed(1) || 0,
@@ -161,18 +161,40 @@ function parsePsOutput(stdout, limit) {
161
161
  return [];
162
162
  }
163
163
 
164
+ // Log first few lines to debug format
165
+ logger.info('ps output sample', {
166
+ firstThreeLines: lines.slice(0, 3)
167
+ });
168
+
164
169
  // Skip header line
165
- const processes = lines.slice(1).map(line => {
170
+ const processes = lines.slice(1).map((line, index) => {
166
171
  const parts = line.trim().split(/\s+/, 4);
167
- return {
172
+ const proc = {
168
173
  pid: Number(parts[0]),
169
174
  cpu: Number(parts[1]) || 0,
170
175
  mem: Number(parts[2]) || 0,
171
176
  name: parts[3] || ''
172
177
  };
173
- }).filter(proc => proc.pid > 0); // Filter out invalid entries
178
+
179
+ // Log first few parsed entries to debug
180
+ if (index < 3) {
181
+ logger.info(`Parsed process ${index}`, {
182
+ line: line.trim(),
183
+ parts: parts,
184
+ parsed: proc
185
+ });
186
+ }
187
+
188
+ return proc;
189
+ }).filter(proc => {
190
+ const isValid = proc.pid > 0;
191
+ if (!isValid) {
192
+ logger.warn('Filtered out invalid process', { proc });
193
+ }
194
+ return isValid;
195
+ });
174
196
 
175
- logger.debug('Parsed processes', {
197
+ logger.info('Parsed processes', {
176
198
  count: processes.length,
177
199
  sample: processes.slice(0, 3)
178
200
  });
package/lib/uploader.js CHANGED
@@ -246,13 +246,14 @@ export async function upload(filePath, metadata = {}) {
246
246
  }
247
247
 
248
248
  // First, create a replay in the cloud (like the desktop app does)
249
+ // Note: Performance data is uploaded separately to S3 as JSONL to reduce DB storage
249
250
  const replayConfig = {
250
251
  duration: metadata.duration || 0,
251
252
  apps: metadata.apps && metadata.apps.length > 0 ? metadata.apps : ['Screen Recording'], // Use tracked apps or fallback
252
253
  title: metadata.title || defaultTitle,
253
254
  system: systemInfo, // Include system information
254
- clientStartDate: metadata.clientStartDate || Date.now(), // Use actual recording start time
255
- performance: metadata.performance // Include performance tracking data
255
+ clientStartDate: metadata.clientStartDate || Date.now() // Use actual recording start time
256
+ // performance data is now uploaded to S3 separately
256
257
  };
257
258
 
258
259
  // Add project if we have one
@@ -266,8 +267,7 @@ export async function upload(filePath, metadata = {}) {
266
267
 
267
268
  logger.verbose('Creating replay with config', {
268
269
  ...replayConfig,
269
- performanceSamples: replayConfig.performance?.samples?.length || 0,
270
- performanceSummary: replayConfig.performance?.summary ? 'included' : 'none'
270
+ performanceDataWillBeUploaded: !!metadata.performanceFile
271
271
  });
272
272
 
273
273
  console.log('Creating replay on server...');
@@ -275,8 +275,7 @@ export async function upload(filePath, metadata = {}) {
275
275
  title: replayConfig.title,
276
276
  duration: replayConfig.duration,
277
277
  apps: replayConfig.apps,
278
- hasPerformanceData: !!replayConfig.performance,
279
- performanceSamples: replayConfig.performance?.samples?.length || 0
278
+ hasPerformanceFile: !!metadata.performanceFile
280
279
  });
281
280
 
282
281
  // Create the replay first
@@ -372,6 +371,14 @@ export async function upload(filePath, metadata = {}) {
372
371
 
373
372
  logger.info('Starting asset uploads', { totalUploads: promises.length });
374
373
  console.log(`Uploading ${promises.length} file(s)...`);
374
+
375
+ // Upload performance data to S3 if available (uses STS from replay-upload)
376
+ if (metadata.performanceFile && fs.existsSync(metadata.performanceFile) && sts.performance) {
377
+ logger.debug('Adding performance data upload to queue', { performanceFile: metadata.performanceFile });
378
+ promises.push(uploader.uploadFile(sts.performance, clip, metadata.performanceFile, 'application', 'jsonl'));
379
+ filesToCleanup.push(metadata.performanceFile);
380
+ logger.info('Performance data upload queued', { file: metadata.performanceFile });
381
+ }
375
382
 
376
383
  // Process and upload logs if available
377
384
  if (metadata.logs && metadata.logs.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dashcam",
3
- "version": "1.4.11-beta.0",
3
+ "version": "1.4.12-beta.0",
4
4
  "description": "Minimal CLI version of Dashcam desktop app",
5
5
  "main": "bin/dashcam.js",
6
6
  "bin": {