dashcam 1.4.11-beta.0 → 1.4.12-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.js +26 -39
- package/lib/auth.js +34 -0
- package/lib/performanceTracker.js +2 -1
- package/lib/recorder.js +3 -1
- package/lib/topProcesses.js +26 -4
- package/lib/uploader.js +13 -6
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
|
736
|
-
if (fs.existsSync(
|
|
737
|
-
logger.info('Found performance file,
|
|
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
|
|
740
|
-
const fileContent = fs.readFileSync(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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:
|
|
794
|
+
logger.debug('No performance file found', { expectedPath: perfFile });
|
|
808
795
|
}
|
|
809
796
|
} catch (error) {
|
|
810
|
-
logger.warn('Failed to
|
|
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
|
-
|
|
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
|
-
|
|
832
|
-
performanceSamples:
|
|
833
|
-
avgCPU:
|
|
834
|
-
maxCPU:
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/lib/topProcesses.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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()
|
|
255
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|