node-mac-recorder 2.16.0 โ†’ 2.16.2

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.
@@ -43,7 +43,10 @@
43
43
  "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐ŸŽต Testing both microphone and system audio'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testBothAudio() {\n try {\n const outputPath = ''./test-output/both-audio-test.mov'';\n console.log(''๐Ÿ“น Starting recording with both audio sources...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: true, // Mikrophone Aร‡IK\n includeSystemAudio: true // Sistem sesi Aร‡IK\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 5 seconds'');\n await new Promise(resolve => setTimeout(resolve, 5000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Both audio test completed!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''Stack:'', error.stack);\n }\n}\n\ntestBothAudio();\n\")",
44
44
  "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐ŸŽค Testing microphone only'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testMicrophoneOnly() {\n try {\n const outputPath = ''./test-output/microphone-only-test.mov'';\n console.log(''๐Ÿ“น Starting microphone only recording...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: false,\n includeMicrophone: true, // Sadece mikrofon Aร‡IK\n includeSystemAudio: false // Sistem sesi KAPALI\n });\n \n if (success) {\n console.log(''โœ… Microphone recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping microphone recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Microphone only test completed!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestMicrophoneOnly();\n\")",
45
45
  "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿš€ Full stress test - All features + Both audio sources'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function fullStressTest() {\n try {\n const outputPath = ''./test-output/full-stress-test.mov'';\n console.log(''๐Ÿ“น Starting full featured recording (stress test)...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true, // Cursor kayฤฑt\n includeMicrophone: true, // Mikrofon kayฤฑt\n includeSystemAudio: true, // Sistem sesi kayฤฑt\n captureArea: { // Crop area\n x: 300,\n y: 300,\n width: 800,\n height: 500\n }\n });\n \n if (success) {\n console.log(''โœ… Full stress test started - waiting 6 seconds'');\n await new Promise(resolve => setTimeout(resolve, 6000));\n \n console.log(''๐Ÿ›‘ Stopping full stress test...'');\n await recorder.stopRecording();\n console.log(''๐ŸŽ‰ FULL STRESS TEST COMPLETED WITHOUT CRASH!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''Stack:'', error.stack);\n }\n}\n\nfullStressTest();\n\")",
46
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿงช Testing version detection and fallback system'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testVersionDetection() {\n try {\n const outputPath = ''./test-output/version-detection-test.mov'';\n console.log(''๐Ÿ“น Starting recording with version detection...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Version detection test completed successfully!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestVersionDetection();\n\")"
46
+ "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿงช Testing version detection and fallback system'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testVersionDetection() {\n try {\n const outputPath = ''./test-output/version-detection-test.mov'';\n console.log(''๐Ÿ“น Starting recording with version detection...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Version detection test completed successfully!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestVersionDetection();\n\")",
47
+ "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿงช Testing Electron safety measures'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testElectronSafety() {\n try {\n const outputPath = ''./test-output/electron-safe-test.mov'';\n console.log(''๐Ÿ“น Starting recording in Electron environment...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started safely in Electron - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Electron safety test completed successfully!'');\n } else {\n console.log(''โš ๏ธ Recording failed to start (expected in some cases)'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestElectronSafety();\n\")",
48
+ "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing AVFoundation fallback path'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testAVFoundationPath() {\n try {\n const outputPath = ''./test-output/force-avfoundation-test.mov'';\n console.log(''๐Ÿ“น Testing AVFoundation (force mode)...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started'');\n await new Promise(resolve => setTimeout(resolve, 2000));\n \n await recorder.stopRecording();\n console.log(''โœ… AVFoundation test completed!'');\n } else {\n console.log(''โŒ Recording failed'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestAVFoundationPath();\n\")",
49
+ "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing AVFoundation fallback path (macOS 14 simulation)'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testAVFoundationPath() {\n try {\n const outputPath = ''./test-output/force-avfoundation-test.mov'';\n console.log(''๐Ÿ“น Testing AVFoundation (simulating macOS 14)...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started with AVFoundation'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n await recorder.stopRecording();\n console.log(''โœ… AVFoundation test completed successfully!'');\n console.log(''This is how it should work on macOS 14'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestAVFoundationPath();\n\")"
47
50
  ],
48
51
  "deny": []
49
52
  }
@@ -0,0 +1,111 @@
1
+ # macOS 13/14 Recording Troubleshooting Guide
2
+
3
+ ## Expected Behavior by macOS Version
4
+
5
+ The system automatically detects macOS version and uses the most compatible recording method:
6
+
7
+ - **macOS 15+**: Uses ScreenCaptureKit (30fps, full features)
8
+ - **macOS 14**: Uses AVFoundation fallback (15fps, stable)
9
+ - **macOS 13**: Uses AVFoundation fallback (15fps, limited features)
10
+ - **macOS < 13**: Not supported
11
+
12
+ ## Debug Steps for macOS 13/14 Issues
13
+
14
+ ### 1. Check Console Logs
15
+
16
+ When running your application, check for these log messages:
17
+
18
+ **macOS 14:**
19
+ ```
20
+ ๐Ÿ–ฅ๏ธ macOS Version: 14.x.x
21
+ ๐ŸŽฏ macOS 14 detected - using AVFoundation for better compatibility
22
+ ๐ŸŽฅ Using AVFoundation for macOS 14 compatibility
23
+ ๐ŸŽฅ RECORDING METHOD: AVFoundation (Fallback)
24
+ ```
25
+
26
+ **macOS 13:**
27
+ ```
28
+ ๐Ÿ–ฅ๏ธ macOS Version: 13.x.x
29
+ ๐ŸŽฏ macOS 13 detected - using AVFoundation (limited features)
30
+ ๐ŸŽฅ Using AVFoundation for macOS 13 compatibility (limited features)
31
+ ๐ŸŽฅ RECORDING METHOD: AVFoundation (Fallback)
32
+ ```
33
+
34
+ ### 2. Test AVFoundation Directly
35
+
36
+ You can force AVFoundation mode for testing:
37
+
38
+ ```javascript
39
+ // Set environment variable before running
40
+ process.env.FORCE_AVFOUNDATION = "1";
41
+
42
+ const MacRecorder = require('node-mac-recorder');
43
+ const recorder = new MacRecorder();
44
+
45
+ async function test() {
46
+ const success = await recorder.startRecording('./test.mov', {
47
+ captureCursor: true,
48
+ includeMicrophone: false,
49
+ includeSystemAudio: true
50
+ });
51
+
52
+ if (success) {
53
+ console.log('โœ… AVFoundation recording started');
54
+ setTimeout(async () => {
55
+ await recorder.stopRecording();
56
+ console.log('โœ… Recording completed');
57
+ }, 3000);
58
+ } else {
59
+ console.log('โŒ Recording failed');
60
+ }
61
+ }
62
+
63
+ test();
64
+ ```
65
+
66
+ ### 3. Common Issues and Solutions
67
+
68
+ **Issue**: Recording starts but no video file created
69
+ - **Cause**: Permission issues
70
+ - **Solution**: Check Screen Recording permission in System Preferences
71
+
72
+ **Issue**: Audio not recorded
73
+ - **Cause**: Microphone permission missing
74
+ - **Solution**: Check Microphone permission in System Preferences
75
+
76
+ **Issue**: Recording fails silently
77
+ - **Cause**: Invalid display ID or output path
78
+ - **Solution**: Use default display (don't specify displayId) and ensure output directory exists
79
+
80
+ ### 4. Permission Requirements
81
+
82
+ macOS 14 requires these permissions:
83
+ - โœ… Screen Recording (System Preferences > Privacy & Security)
84
+ - โœ… Microphone (if includeMicrophone: true)
85
+ - โœ… Accessibility (for cursor tracking)
86
+
87
+ ### 5. Technical Details
88
+
89
+ **AVFoundation Implementation (macOS 14):**
90
+ - Video: H.264 encoding at 15fps
91
+ - Audio: AAC encoding at 44.1kHz
92
+ - Screen capture: CGDisplayCreateImage
93
+ - Memory management: Automatic cleanup
94
+
95
+ **Differences from ScreenCaptureKit:**
96
+ - Lower frame rate (15fps vs 30fps) for stability
97
+ - No automatic window exclusion
98
+ - Simpler audio routing
99
+
100
+ **macOS 13 Specific Limitations:**
101
+ - Audio features may have reduced compatibility
102
+ - Some advanced recording options may not work
103
+ - Recommended to test thoroughly on target systems
104
+
105
+ ## Contact
106
+
107
+ If recording still fails on macOS 14 after following this guide, please provide:
108
+ 1. macOS version (`sw_vers`)
109
+ 2. Console logs from the application
110
+ 3. Permission status screenshots
111
+ 4. Minimal reproduction code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.16.0",
3
+ "version": "2.16.2",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -51,8 +51,14 @@ void cleanupRecording() {
51
51
  }
52
52
  }
53
53
 
54
- // AVFoundation cleanup
55
- if (isAVFoundationRecording()) {
54
+ // AVFoundation cleanup (only in non-Electron environments)
55
+ BOOL isElectron = (NSBundle.mainBundle.bundleIdentifier &&
56
+ [NSBundle.mainBundle.bundleIdentifier containsString:@"electron"]) ||
57
+ (NSProcessInfo.processInfo.processName &&
58
+ [NSProcessInfo.processInfo.processName containsString:@"Electron"]) ||
59
+ (NSProcessInfo.processInfo.environment[@"ELECTRON_RUN_AS_NODE"] != nil);
60
+
61
+ if (!isElectron && isAVFoundationRecording()) {
56
62
  stopAVFoundationRecording();
57
63
  }
58
64
 
@@ -186,18 +192,23 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
186
192
 
187
193
  // Check macOS version for ScreenCaptureKit compatibility
188
194
  NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
189
- BOOL isM15Plus = (osVersion.majorVersion > 15 ||
190
- (osVersion.majorVersion == 15 && osVersion.minorVersion >= 0));
191
- BOOL isM14Plus = (osVersion.majorVersion > 14 ||
192
- (osVersion.majorVersion == 14 && osVersion.minorVersion >= 0));
195
+ BOOL isM15Plus = (osVersion.majorVersion >= 15);
196
+ BOOL isM14Plus = (osVersion.majorVersion >= 14);
197
+ BOOL isM13Plus = (osVersion.majorVersion >= 13);
193
198
 
194
199
  NSLog(@"๐Ÿ–ฅ๏ธ macOS Version: %ld.%ld.%ld",
195
200
  (long)osVersion.majorVersion, (long)osVersion.minorVersion, (long)osVersion.patchVersion);
196
201
 
197
- // Try ScreenCaptureKit on macOS 14+ (with better compatibility on 15+)
198
- if (@available(macOS 12.3, *) && isM14Plus) {
199
- NSLog(@"โœ… macOS 14+ detected - ScreenCaptureKit available with %@ compatibility",
200
- isM15Plus ? @"full" : @"limited");
202
+ // Force AVFoundation for debugging/testing
203
+ BOOL forceAVFoundation = (getenv("FORCE_AVFOUNDATION") != NULL);
204
+ if (forceAVFoundation) {
205
+ NSLog(@"๐Ÿ”ง FORCE_AVFOUNDATION environment variable detected - skipping ScreenCaptureKit");
206
+ }
207
+
208
+ // Use ScreenCaptureKit only on macOS 15+ for maximum stability
209
+ // macOS 14 should use AVFoundation fallback
210
+ if (@available(macOS 12.3, *) && isM15Plus && !forceAVFoundation) {
211
+ NSLog(@"โœ… macOS 15+ detected - ScreenCaptureKit available with full compatibility");
201
212
 
202
213
  // Try ScreenCaptureKit with extensive safety measures
203
214
  @try {
@@ -269,13 +280,32 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
269
280
  NSLog(@"โŒ Exception during ScreenCaptureKit availability check: %@", availabilityException.reason);
270
281
  return Napi::Boolean::New(env, false);
271
282
  }
283
+ } else if (isM14Plus) {
284
+ // macOS 14 - directly use AVFoundation for better compatibility
285
+ NSLog(@"๐ŸŽฏ macOS 14 detected - using AVFoundation for better compatibility");
286
+ } else if (isM13Plus) {
287
+ // macOS 13 - use AVFoundation (limited features)
288
+ NSLog(@"๐ŸŽฏ macOS 13 detected - using AVFoundation (limited features)");
272
289
  } else {
273
- NSLog(@"โŒ macOS version too old for ScreenCaptureKit (< 12.3) - Recording not supported");
290
+ NSLog(@"โŒ macOS version too old (< 13.0) - Recording not supported");
274
291
  return Napi::Boolean::New(env, false);
275
292
  }
276
293
 
277
- // If we get here, ScreenCaptureKit failed - try AVFoundation fallback
278
- NSLog(@"๐Ÿ”„ ScreenCaptureKit failed - attempting AVFoundation fallback");
294
+ // AVFoundation fallback logic
295
+ if (isElectron) {
296
+ NSLog(@"โŒ ScreenCaptureKit failed in Electron - AVFoundation disabled for stability");
297
+ NSLog(@"โŒ Recording not available in Electron when ScreenCaptureKit fails");
298
+ return Napi::Boolean::New(env, false);
299
+ }
300
+
301
+ // Try AVFoundation fallback (ScreenCaptureKit failed or macOS 13/14)
302
+ if (isM15Plus) {
303
+ NSLog(@"๐Ÿ”„ ScreenCaptureKit failed on macOS 15+ - attempting AVFoundation fallback");
304
+ } else if (isM14Plus) {
305
+ NSLog(@"๐ŸŽฅ Using AVFoundation for macOS 14 compatibility");
306
+ } else if (isM13Plus) {
307
+ NSLog(@"๐ŸŽฅ Using AVFoundation for macOS 13 compatibility (limited features)");
308
+ }
279
309
 
280
310
  @try {
281
311
  // Import AVFoundation recording functions (if available)
@@ -327,24 +357,32 @@ Napi::Value StopRecording(const Napi::CallbackInfo& info) {
327
357
  }
328
358
  }
329
359
 
330
- // Try AVFoundation fallback
331
- extern bool isAVFoundationRecording();
332
- extern bool stopAVFoundationRecording();
360
+ // Try AVFoundation fallback (only in non-Electron environments)
361
+ BOOL isElectron = (NSBundle.mainBundle.bundleIdentifier &&
362
+ [NSBundle.mainBundle.bundleIdentifier containsString:@"electron"]) ||
363
+ (NSProcessInfo.processInfo.processName &&
364
+ [NSProcessInfo.processInfo.processName containsString:@"Electron"]) ||
365
+ (NSProcessInfo.processInfo.environment[@"ELECTRON_RUN_AS_NODE"] != nil);
333
366
 
334
- @try {
335
- if (isAVFoundationRecording()) {
336
- NSLog(@"๐Ÿ›‘ Stopping AVFoundation recording");
337
- if (stopAVFoundationRecording()) {
338
- g_isRecording = false;
339
- return Napi::Boolean::New(env, true);
340
- } else {
341
- NSLog(@"โŒ Failed to stop AVFoundation recording");
342
- g_isRecording = false;
343
- return Napi::Boolean::New(env, false);
367
+ if (!isElectron) {
368
+ extern bool isAVFoundationRecording();
369
+ extern bool stopAVFoundationRecording();
370
+
371
+ @try {
372
+ if (isAVFoundationRecording()) {
373
+ NSLog(@"๐Ÿ›‘ Stopping AVFoundation recording");
374
+ if (stopAVFoundationRecording()) {
375
+ g_isRecording = false;
376
+ return Napi::Boolean::New(env, true);
377
+ } else {
378
+ NSLog(@"โŒ Failed to stop AVFoundation recording");
379
+ g_isRecording = false;
380
+ return Napi::Boolean::New(env, false);
381
+ }
344
382
  }
383
+ } @catch (NSException *exception) {
384
+ NSLog(@"โŒ Exception stopping AVFoundation: %@", exception.reason);
345
385
  }
346
- } @catch (NSException *exception) {
347
- NSLog(@"โŒ Exception stopping AVFoundation: %@", exception.reason);
348
386
  }
349
387
 
350
388
  NSLog(@"โš ๏ธ No active recording found to stop");
@@ -606,7 +644,7 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
606
644
  Napi::Value GetRecordingStatus(const Napi::CallbackInfo& info) {
607
645
  Napi::Env env = info.Env();
608
646
 
609
- // Check both recording methods
647
+ // Check recording methods
610
648
  bool isRecording = g_isRecording;
611
649
 
612
650
  if (@available(macOS 12.3, *)) {
@@ -615,7 +653,14 @@ Napi::Value GetRecordingStatus(const Napi::CallbackInfo& info) {
615
653
  }
616
654
  }
617
655
 
618
- if (isAVFoundationRecording()) {
656
+ // Check AVFoundation only in non-Electron environments
657
+ BOOL isElectron = (NSBundle.mainBundle.bundleIdentifier &&
658
+ [NSBundle.mainBundle.bundleIdentifier containsString:@"electron"]) ||
659
+ (NSProcessInfo.processInfo.processName &&
660
+ [NSProcessInfo.processInfo.processName containsString:@"Electron"]) ||
661
+ (NSProcessInfo.processInfo.environment[@"ELECTRON_RUN_AS_NODE"] != nil);
662
+
663
+ if (!isElectron && isAVFoundationRecording()) {
619
664
  isRecording = true;
620
665
  }
621
666
 
@@ -0,0 +1,56 @@
1
+ const MacRecorder = require('./index');
2
+
3
+ async function testMacOS14Recording() {
4
+ const recorder = new MacRecorder();
5
+
6
+ console.log('๐Ÿงช macOS 14 Recording Test');
7
+ console.log('This will test AVFoundation fallback path when ScreenCaptureKit fails or is not available');
8
+ console.log('');
9
+
10
+ try {
11
+ const outputPath = './test-output/macos14-test.mov';
12
+
13
+ console.log('๐Ÿ“น Starting recording test...');
14
+ console.log('Expected behavior on macOS 14:');
15
+ console.log(' 1. System detects macOS 14.x');
16
+ console.log(' 2. Skips ScreenCaptureKit (only for macOS 15+)');
17
+ console.log(' 3. Uses AVFoundation fallback');
18
+ console.log(' 4. Records at 15fps with H.264 encoding');
19
+ console.log('');
20
+
21
+ const success = await recorder.startRecording(outputPath, {
22
+ captureCursor: true,
23
+ includeMicrophone: false,
24
+ includeSystemAudio: true,
25
+ displayId: null // Use main display
26
+ });
27
+
28
+ if (success) {
29
+ console.log('โœ… Recording started successfully');
30
+ console.log('โฑ๏ธ Recording for 3 seconds...');
31
+
32
+ await new Promise(resolve => setTimeout(resolve, 3000));
33
+
34
+ console.log('๐Ÿ›‘ Stopping recording...');
35
+ await recorder.stopRecording();
36
+
37
+ console.log('โœ… macOS 14 test completed successfully!');
38
+ console.log('๐Ÿ“ Output file: ' + outputPath);
39
+ } else {
40
+ console.log('โŒ Recording failed to start');
41
+ console.log('');
42
+ console.log('Troubleshooting steps:');
43
+ console.log('1. Check Screen Recording permission in System Preferences');
44
+ console.log('2. Ensure macOS version is 14.0 or later');
45
+ console.log('3. Check console logs for detailed error messages');
46
+ }
47
+ } catch (error) {
48
+ console.log('โŒ Error during test:', error.message);
49
+ if (error.stack) {
50
+ console.log('Stack trace:', error.stack);
51
+ }
52
+ }
53
+ }
54
+
55
+ // Run the test
56
+ testMacOS14Recording();