neoagent 2.3.1-beta.2 → 2.3.1-beta.21

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.
Files changed (264) hide show
  1. package/.env.example +39 -0
  2. package/README.md +2 -0
  3. package/docs/capabilities.md +2 -2
  4. package/docs/configuration.md +13 -5
  5. package/docs/integrations.md +4 -1
  6. package/flutter_app/.metadata +42 -0
  7. package/flutter_app/README.md +21 -0
  8. package/flutter_app/analysis_options.yaml +32 -0
  9. package/flutter_app/android/app/build.gradle.kts +109 -0
  10. package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  11. package/flutter_app/android/app/src/main/AndroidManifest.xml +147 -0
  12. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +747 -0
  13. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
  14. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
  15. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
  16. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
  17. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
  18. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
  19. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
  20. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
  21. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
  22. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
  23. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
  24. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
  25. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
  26. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
  27. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
  28. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
  29. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
  30. package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  31. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
  32. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
  33. package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  34. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
  35. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
  36. package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
  37. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  38. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  39. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  40. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  41. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  42. package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
  43. package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  44. package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  45. package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
  46. package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
  47. package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
  48. package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  49. package/flutter_app/android/build.gradle.kts +24 -0
  50. package/flutter_app/android/ci-release.keystore +0 -0
  51. package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  52. package/flutter_app/android/gradle.properties +3 -0
  53. package/flutter_app/android/key.properties +4 -0
  54. package/flutter_app/android/settings.gradle.kts +26 -0
  55. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  56. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  57. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  58. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  59. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  60. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  61. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  62. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  63. package/flutter_app/lib/features/location/location_service.dart +119 -0
  64. package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
  65. package/flutter_app/lib/main.dart +23057 -0
  66. package/flutter_app/lib/main_app_shell.dart +1682 -0
  67. package/flutter_app/lib/main_integrations.dart +931 -0
  68. package/flutter_app/lib/main_launcher.dart +959 -0
  69. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  70. package/flutter_app/lib/main_models.dart +3473 -0
  71. package/flutter_app/lib/main_shared.dart +2861 -0
  72. package/flutter_app/lib/main_theme.dart +204 -0
  73. package/flutter_app/lib/main_voice_assistant.dart +831 -0
  74. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  75. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  76. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  77. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  78. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  79. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  80. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  81. package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
  82. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  83. package/flutter_app/lib/src/backend_client.dart +1833 -0
  84. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  85. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  86. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  87. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  88. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  89. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  90. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  91. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  92. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  93. package/flutter_app/lib/src/health_bridge.dart +136 -0
  94. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  95. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  96. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  97. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  98. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  99. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  100. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  101. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  102. package/flutter_app/lib/src/oauth_launcher_io.dart +77 -0
  103. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  104. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  105. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  106. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  107. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  108. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  109. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  110. package/flutter_app/lib/src/theme/palette.dart +81 -0
  111. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  112. package/flutter_app/linux/CMakeLists.txt +128 -0
  113. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  114. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +43 -0
  115. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  116. package/flutter_app/linux/flutter/generated_plugins.cmake +31 -0
  117. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  118. package/flutter_app/linux/runner/main.cc +6 -0
  119. package/flutter_app/linux/runner/my_application.cc +144 -0
  120. package/flutter_app/linux/runner/my_application.h +18 -0
  121. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  122. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  123. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  124. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +40 -0
  125. package/flutter_app/macos/Podfile +42 -0
  126. package/flutter_app/macos/Podfile.lock +87 -0
  127. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  128. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  129. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  130. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  131. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  132. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  133. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  134. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  135. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  136. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  137. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  138. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  139. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  140. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  141. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  142. package/flutter_app/macos/Runner/Info.plist +36 -0
  143. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  144. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  145. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  146. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  147. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  148. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  149. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  150. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  151. package/flutter_app/patch_strings.py +12 -0
  152. package/flutter_app/pubspec.lock +1088 -0
  153. package/flutter_app/pubspec.yaml +53 -0
  154. package/flutter_app/test/messaging_access_summary_test.dart +22 -0
  155. package/flutter_app/test/recording_payloads_test.dart +53 -0
  156. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  157. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  158. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  159. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  160. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  161. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  162. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  163. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  164. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  165. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  166. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  167. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  168. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  169. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  170. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  171. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  172. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  173. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  174. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  175. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  176. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  177. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  178. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  179. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  180. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  181. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  182. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  183. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  184. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  185. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  186. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  187. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  188. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  189. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  190. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  191. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  192. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  193. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  194. package/flutter_app/web/favicon.png +0 -0
  195. package/flutter_app/web/favicon.svg +12 -0
  196. package/flutter_app/web/icons/Icon-192.png +0 -0
  197. package/flutter_app/web/icons/Icon-512.png +0 -0
  198. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  199. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  200. package/flutter_app/web/index.html +39 -0
  201. package/flutter_app/web/manifest.json +35 -0
  202. package/flutter_app/windows/CMakeLists.txt +108 -0
  203. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  204. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +47 -0
  205. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  206. package/flutter_app/windows/flutter/generated_plugins.cmake +35 -0
  207. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  208. package/flutter_app/windows/runner/Runner.rc +121 -0
  209. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  210. package/flutter_app/windows/runner/flutter_window.h +37 -0
  211. package/flutter_app/windows/runner/main.cpp +53 -0
  212. package/flutter_app/windows/runner/resource.h +16 -0
  213. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  214. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  215. package/flutter_app/windows/runner/utils.cpp +65 -0
  216. package/flutter_app/windows/runner/utils.h +19 -0
  217. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  218. package/flutter_app/windows/runner/win32_window.h +102 -0
  219. package/lib/manager.js +231 -7
  220. package/package.json +3 -1
  221. package/server/db/database.js +68 -0
  222. package/server/http/middleware.js +50 -0
  223. package/server/http/routes.js +3 -1
  224. package/server/index.js +1 -0
  225. package/server/public/.last_build_id +1 -1
  226. package/server/public/assets/NOTICES +61 -0
  227. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  228. package/server/public/flutter_bootstrap.js +1 -1
  229. package/server/public/main.dart.js +65262 -64422
  230. package/server/routes/integrations.js +86 -0
  231. package/server/routes/memory.js +11 -2
  232. package/server/routes/screenHistory.js +46 -0
  233. package/server/routes/triggers.js +81 -0
  234. package/server/services/ai/models.js +30 -0
  235. package/server/services/ai/providers/githubCopilot.js +97 -0
  236. package/server/services/ai/providers/openai.js +2 -1
  237. package/server/services/ai/providers/openaiCodex.js +31 -0
  238. package/server/services/ai/settings.js +20 -0
  239. package/server/services/ai/systemPrompt.js +1 -1
  240. package/server/services/ai/tools.js +35 -6
  241. package/server/services/browser/controller.js +47 -3
  242. package/server/services/desktop/screenRecorder.js +172 -0
  243. package/server/services/integrations/env.js +5 -0
  244. package/server/services/integrations/github/common.js +106 -0
  245. package/server/services/integrations/github/provider.js +499 -0
  246. package/server/services/integrations/github/repos.js +1124 -0
  247. package/server/services/integrations/home_assistant/provider.js +306 -26
  248. package/server/services/integrations/manager.js +63 -7
  249. package/server/services/integrations/oauth_provider.js +13 -6
  250. package/server/services/integrations/provider_config_store.js +76 -0
  251. package/server/services/integrations/registry.js +4 -0
  252. package/server/services/integrations/trello/provider.js +744 -0
  253. package/server/services/integrations/whatsapp/provider.js +6 -2
  254. package/server/services/manager.js +22 -0
  255. package/server/services/memory/manager.js +39 -2
  256. package/server/services/skills/base_catalog.js +33 -0
  257. package/server/services/tasks/adapters/index.js +1 -0
  258. package/server/services/tasks/adapters/manual.js +12 -0
  259. package/server/services/tasks/runtime.js +1 -1
  260. package/server/services/voice/openaiClient.js +4 -1
  261. package/server/services/voice/providers.js +2 -1
  262. package/server/services/widgets/service.js +49 -4
  263. package/server/utils/local_secrets.js +56 -0
  264. package/server/utils/logger.js +37 -9
@@ -0,0 +1,106 @@
1
+ /// Decibel data from audio capture.
2
+ ///
3
+ /// This class represents a single decibel reading with its timestamp.
4
+ /// Used by both microphone and system audio capture to provide volume level
5
+ /// information.
6
+ ///
7
+ /// Example:
8
+ /// ```dart
9
+ /// // From stream
10
+ /// capture.decibelStream?.listen((data) {
11
+ /// print('Decibel: ${data.decibel} dB');
12
+ /// print('Time: ${DateTime.fromMillisecondsSinceEpoch((data.timestamp * 1000).toInt())}');
13
+ /// });
14
+ ///
15
+ /// // Create manually
16
+ /// final data = DecibelData(
17
+ /// decibel: -45.5,
18
+ /// timestamp: DateTime.now().millisecondsSinceEpoch / 1000.0,
19
+ /// );
20
+ ///
21
+ /// // Convert to/from map
22
+ /// final map = data.toMap();
23
+ /// final restored = DecibelData.fromMap(map);
24
+ /// ```
25
+ class DecibelData {
26
+ /// Decibel value in dB, typically ranging from -120 to 0 dB.
27
+ ///
28
+ /// - -120 dB: silence or very quiet
29
+ /// - -60 dB: quiet background noise
30
+ /// - -40 dB: normal speech
31
+ /// - -20 dB: loud speech
32
+ /// - 0 dB: maximum level
33
+ final double decibel;
34
+
35
+ /// Unix timestamp in seconds (not milliseconds).
36
+ ///
37
+ /// This represents when the decibel reading was taken.
38
+ final double timestamp;
39
+
40
+ /// Creates a new [DecibelData] instance.
41
+ ///
42
+ /// [decibel] should be in the range -120 to 0 dB.
43
+ /// [timestamp] should be a Unix timestamp in seconds.
44
+ ///
45
+ /// Example:
46
+ /// ```dart
47
+ /// final data = DecibelData(
48
+ /// decibel: -45.0,
49
+ /// timestamp: DateTime.now().millisecondsSinceEpoch / 1000.0,
50
+ /// );
51
+ /// ```
52
+ const DecibelData({
53
+ required this.decibel,
54
+ required this.timestamp,
55
+ });
56
+
57
+ /// Creates a [DecibelData] instance from a map.
58
+ ///
59
+ /// The map should contain:
60
+ /// - `decibel`: num (will be converted to double)
61
+ /// - `timestamp`: num (will be converted to double)
62
+ ///
63
+ /// If values are missing, defaults to -120.0 dB and current timestamp.
64
+ ///
65
+ /// Example:
66
+ /// ```dart
67
+ /// final map = {
68
+ /// 'decibel': -45.5,
69
+ /// 'timestamp': 1234567890.0,
70
+ /// };
71
+ /// final data = DecibelData.fromMap(map);
72
+ /// ```
73
+ factory DecibelData.fromMap(Map<String, dynamic> map) {
74
+ return DecibelData(
75
+ decibel: (map['decibel'] as num?)?.toDouble() ?? -120.0,
76
+ timestamp: (map['timestamp'] as num?)?.toDouble() ??
77
+ DateTime.now().millisecondsSinceEpoch / 1000.0,
78
+ );
79
+ }
80
+
81
+ /// Converts this [DecibelData] instance to a map.
82
+ ///
83
+ /// Returns a map containing:
84
+ /// - `decibel`: double
85
+ /// - `timestamp`: double
86
+ ///
87
+ /// Example:
88
+ /// ```dart
89
+ /// final data = DecibelData(
90
+ /// decibel: -45.0,
91
+ /// timestamp: 1234567890.0,
92
+ /// );
93
+ /// final map = data.toMap();
94
+ /// // map = {'decibel': -45.0, 'timestamp': 1234567890.0}
95
+ /// ```
96
+ Map<String, dynamic> toMap() {
97
+ return {
98
+ 'decibel': decibel,
99
+ 'timestamp': timestamp,
100
+ };
101
+ }
102
+
103
+ @override
104
+ String toString() =>
105
+ 'DecibelData(decibel: ${decibel.toStringAsFixed(1)} dB, timestamp: $timestamp)';
106
+ }
@@ -0,0 +1,219 @@
1
+ /// Enum representing the type of input device (microphone).
2
+ ///
3
+ /// This enum categorizes input devices into three types: built-in, Bluetooth,
4
+ /// or external (USB, etc.).
5
+ ///
6
+ /// Example:
7
+ /// ```dart
8
+ /// final device = InputDevice(
9
+ /// id: 'device1',
10
+ /// name: 'Built-in Microphone',
11
+ /// type: InputDeviceType.builtIn,
12
+ /// channelCount: 1,
13
+ /// isDefault: true,
14
+ /// );
15
+ ///
16
+ /// // Convert to/from string
17
+ /// final typeString = device.type.toString(); // 'built-in'
18
+ /// final type = InputDeviceType.fromString('bluetooth'); // InputDeviceType.bluetooth
19
+ /// ```
20
+ enum InputDeviceType {
21
+ /// Built-in device (e.g., laptop microphone)
22
+ builtIn,
23
+
24
+ /// Bluetooth device (wireless microphone)
25
+ bluetooth,
26
+
27
+ /// External device (USB microphone, etc.)
28
+ external;
29
+
30
+ /// Creates an [InputDeviceType] from a string.
31
+ ///
32
+ /// Accepts: 'built-in', 'bluetooth', 'external' (case-insensitive).
33
+ /// Returns [InputDeviceType.external] for unknown values.
34
+ ///
35
+ /// Example:
36
+ /// ```dart
37
+ /// final type1 = InputDeviceType.fromString('built-in'); // InputDeviceType.builtIn
38
+ /// final type2 = InputDeviceType.fromString('BLUETOOTH'); // InputDeviceType.bluetooth
39
+ /// final type3 = InputDeviceType.fromString('unknown'); // InputDeviceType.external (default)
40
+ /// ```
41
+ static InputDeviceType fromString(String type) {
42
+ switch (type.toLowerCase()) {
43
+ case 'built-in':
44
+ return InputDeviceType.builtIn;
45
+ case 'bluetooth':
46
+ return InputDeviceType.bluetooth;
47
+ case 'external':
48
+ return InputDeviceType.external;
49
+ default:
50
+ return InputDeviceType.external;
51
+ }
52
+ }
53
+
54
+ /// Converts this [InputDeviceType] to a string representation.
55
+ ///
56
+ /// Returns: 'built-in', 'bluetooth', or 'external'.
57
+ ///
58
+ /// Example:
59
+ /// ```dart
60
+ /// InputDeviceType.builtIn.toString(); // 'built-in'
61
+ /// InputDeviceType.bluetooth.toString(); // 'bluetooth'
62
+ /// InputDeviceType.external.toString(); // 'external'
63
+ /// ```
64
+ @override
65
+ String toString() {
66
+ switch (this) {
67
+ case InputDeviceType.builtIn:
68
+ return 'built-in';
69
+ case InputDeviceType.bluetooth:
70
+ return 'bluetooth';
71
+ case InputDeviceType.external:
72
+ return 'external';
73
+ }
74
+ }
75
+ }
76
+
77
+ /// Class representing information about an input device (microphone).
78
+ ///
79
+ /// This class contains all relevant information about a microphone device,
80
+ /// including its ID, name, type, channel count, and whether it's the default device.
81
+ ///
82
+ /// Example:
83
+ /// ```dart
84
+ /// // Get available devices
85
+ /// final devices = await micCapture.getAvailableInputDevices();
86
+ ///
87
+ /// // Find a specific device
88
+ /// final usbMic = devices.firstWhere(
89
+ /// (device) => device.name.contains('USB'),
90
+ /// );
91
+ ///
92
+ /// print('Device: ${usbMic.name}');
93
+ /// print('Type: ${usbMic.type}');
94
+ /// print('Channels: ${usbMic.channelCount}');
95
+ /// print('Default: ${usbMic.isDefault}');
96
+ ///
97
+ /// // Convert to/from map
98
+ /// final map = usbMic.toMap();
99
+ /// final restored = InputDevice.fromMap(map);
100
+ /// ```
101
+ class InputDevice {
102
+ /// Unique identifier of the device.
103
+ ///
104
+ /// This ID can be used to identify and select a specific device.
105
+ final String id;
106
+
107
+ /// Human-readable name of the device.
108
+ ///
109
+ /// Examples: "Built-in Microphone", "USB Microphone", "AirPods Pro"
110
+ final String name;
111
+
112
+ /// Type of the device (built-in, Bluetooth, or external).
113
+ final InputDeviceType type;
114
+
115
+ /// Number of audio channels supported by the device.
116
+ ///
117
+ /// Typically 1 for mono, 2 for stereo.
118
+ final int channelCount;
119
+
120
+ /// Whether this device is the system default input device.
121
+ final bool isDefault;
122
+
123
+ /// Creates a new [InputDevice] instance.
124
+ ///
125
+ /// All parameters are required.
126
+ ///
127
+ /// Example:
128
+ /// ```dart
129
+ /// final device = InputDevice(
130
+ /// id: 'device-123',
131
+ /// name: 'Built-in Microphone',
132
+ /// type: InputDeviceType.builtIn,
133
+ /// channelCount: 1,
134
+ /// isDefault: true,
135
+ /// );
136
+ /// ```
137
+ const InputDevice({
138
+ required this.id,
139
+ required this.name,
140
+ required this.type,
141
+ required this.channelCount,
142
+ required this.isDefault,
143
+ });
144
+
145
+ /// Creates an [InputDevice] instance from a map.
146
+ ///
147
+ /// The map should contain:
148
+ /// - `id`: String (defaults to empty string if missing)
149
+ /// - `name`: String (defaults to empty string if missing)
150
+ /// - `type`: String (converted via [InputDeviceType.fromString], defaults to external)
151
+ /// - `channelCount`: int (defaults to 0 if missing)
152
+ /// - `isDefault`: bool (defaults to false if missing)
153
+ ///
154
+ /// Example:
155
+ /// ```dart
156
+ /// final map = {
157
+ /// 'id': 'device-123',
158
+ /// 'name': 'USB Microphone',
159
+ /// 'type': 'external',
160
+ /// 'channelCount': 2,
161
+ /// 'isDefault': false,
162
+ /// };
163
+ /// final device = InputDevice.fromMap(map);
164
+ /// ```
165
+ factory InputDevice.fromMap(Map<String, dynamic> map) {
166
+ return InputDevice(
167
+ id: map['id'] as String? ?? '',
168
+ name: map['name'] as String? ?? '',
169
+ type: InputDeviceType.fromString(map['type'] as String? ?? 'external'),
170
+ channelCount: map['channelCount'] as int? ?? 0,
171
+ isDefault: map['isDefault'] as bool? ?? false,
172
+ );
173
+ }
174
+
175
+ /// Converts this [InputDevice] instance to a map.
176
+ ///
177
+ /// Returns a map containing all device information:
178
+ /// - `id`: String
179
+ /// - `name`: String
180
+ /// - `type`: String (from [InputDeviceType.toString])
181
+ /// - `channelCount`: int
182
+ /// - `isDefault`: bool
183
+ ///
184
+ /// Example:
185
+ /// ```dart
186
+ /// final device = InputDevice(
187
+ /// id: 'device-123',
188
+ /// name: 'USB Microphone',
189
+ /// type: InputDeviceType.external,
190
+ /// channelCount: 2,
191
+ /// isDefault: false,
192
+ /// );
193
+ /// final map = device.toMap();
194
+ /// ```
195
+ Map<String, dynamic> toMap() {
196
+ return {
197
+ 'id': id,
198
+ 'name': name,
199
+ 'type': type.toString(),
200
+ 'channelCount': channelCount,
201
+ 'isDefault': isDefault,
202
+ };
203
+ }
204
+
205
+ @override
206
+ String toString() {
207
+ return 'InputDevice(id: $id, name: $name, type: ${type.toString()}, '
208
+ 'channelCount: $channelCount, isDefault: $isDefault)';
209
+ }
210
+
211
+ @override
212
+ bool operator ==(Object other) {
213
+ if (identical(this, other)) return true;
214
+ return other is InputDevice && other.id == id;
215
+ }
216
+
217
+ @override
218
+ int get hashCode => id.hashCode;
219
+ }
@@ -0,0 +1,336 @@
1
+ import 'dart:async';
2
+
3
+ import 'package:desktop_audio_capture/audio_capture.dart';
4
+ import 'package:flutter/services.dart';
5
+
6
+ export 'package:desktop_audio_capture/config/system_adudio_config.dart';
7
+
8
+ enum _SystemAudioMethod {
9
+ startCapture,
10
+ stopCapture,
11
+ requestPermissions,
12
+ }
13
+
14
+ /// Class for capturing system audio (audio output from the device).
15
+ ///
16
+ /// This class allows you to capture audio that is being played by the system,
17
+ /// such as music, videos, or other applications. It requires screen recording
18
+ /// permissions on macOS.
19
+ ///
20
+ /// Example:
21
+ /// ```dart
22
+ /// final systemCapture = SystemAudioCapture(
23
+ /// config: SystemAudioConfig(
24
+ /// sampleRate: 44100,
25
+ /// channels: 2,
26
+ /// ),
27
+ /// );
28
+ ///
29
+ /// await systemCapture.startCapture();
30
+ ///
31
+ /// // Listen to audio stream
32
+ /// systemCapture.audioStream?.listen((audioData) {
33
+ /// // Process audio bytes
34
+ /// print('Received ${audioData.length} bytes');
35
+ /// });
36
+ ///
37
+ /// // Listen to decibel readings
38
+ /// systemCapture.decibelStream?.listen((data) {
39
+ /// print('Decibel: ${data.decibel} dB');
40
+ /// });
41
+ ///
42
+ /// // Stop when done
43
+ /// await systemCapture.stopCapture();
44
+ /// ```
45
+ class SystemAudioCapture extends AudioCapture {
46
+ static const MethodChannel _channel = MethodChannel(
47
+ 'com.system_audio_transcriber/audio_capture',
48
+ );
49
+ static const EventChannel _audioStreamChannel = EventChannel(
50
+ 'com.system_audio_transcriber/audio_stream',
51
+ );
52
+ static const EventChannel _statusStreamChannel = EventChannel(
53
+ 'com.system_audio_transcriber/audio_status',
54
+ );
55
+ static const EventChannel _decibelStreamChannel = EventChannel(
56
+ 'com.system_audio_transcriber/audio_decibel',
57
+ );
58
+
59
+ Stream<Uint8List>? _audioStream;
60
+ Stream<SystemAudioStatus>? _statusStream;
61
+ Stream<DecibelData>? _decibelStream;
62
+ bool _isRecording = false;
63
+
64
+ /// Stream of raw audio data bytes from system audio capture.
65
+ ///
66
+ /// Returns a [Stream<Uint8List>] containing the captured audio data.
67
+ /// The stream is only available after [startCapture] has been called.
68
+ ///
69
+ /// Example:
70
+ /// ```dart
71
+ /// await systemCapture.startCapture();
72
+ ///
73
+ /// systemCapture.audioStream?.listen((audioData) {
74
+ /// // Process audio bytes
75
+ /// final audioBuffer = audioData.buffer.asUint8List();
76
+ /// // Use audio buffer for processing, saving, etc.
77
+ /// });
78
+ /// ```
79
+ Stream<Uint8List>? get audioStream => _audioStream;
80
+
81
+ /// Stream of system audio capture status updates.
82
+ ///
83
+ /// Returns a [Stream<SystemAudioStatus>] containing status information:
84
+ /// - [SystemAudioStatus.isActive]: bool - whether system audio capture is currently active
85
+ ///
86
+ /// Example:
87
+ /// ```dart
88
+ /// systemCapture.statusStream?.listen((status) {
89
+ /// if (status.isActive) {
90
+ /// print('System audio capture is active');
91
+ /// } else {
92
+ /// print('System audio capture is inactive');
93
+ /// }
94
+ /// });
95
+ /// ```
96
+ Stream<SystemAudioStatus>? get statusStream {
97
+ // Create status stream if not already created
98
+ _statusStream ??= _statusStreamChannel.receiveBroadcastStream().map((
99
+ dynamic event,
100
+ ) {
101
+ if (event is Map) {
102
+ return SystemAudioStatus.fromJson(Map<String, dynamic>.from(event));
103
+ }
104
+ return SystemAudioStatus(isActive: false);
105
+ });
106
+ return _statusStream;
107
+ }
108
+
109
+ /// Stream of system audio decibel (dB) readings.
110
+ ///
111
+ /// Returns a [Stream<DecibelData>] containing:
112
+ /// - `decibel`: double - decibel value (-120 to 0 dB)
113
+ /// - `timestamp`: double - Unix timestamp
114
+ ///
115
+ /// The stream is only available while recording is active.
116
+ ///
117
+ /// Example:
118
+ /// ```dart
119
+ /// await systemCapture.startCapture();
120
+ ///
121
+ /// systemCapture.decibelStream?.listen((data) {
122
+ /// print('System audio level: ${data.decibel.toStringAsFixed(1)} dB');
123
+ /// print('Timestamp: ${DateTime.fromMillisecondsSinceEpoch((data.timestamp * 1000).toInt())}');
124
+ /// });
125
+ /// ```
126
+ Stream<DecibelData>? get decibelStream {
127
+ if (!_isRecording) {
128
+ return null;
129
+ }
130
+ // Create decibel stream if not already created
131
+ _decibelStream ??= _decibelStreamChannel.receiveBroadcastStream().map((
132
+ dynamic event,
133
+ ) {
134
+ if (event is Map) {
135
+ return DecibelData.fromMap(Map<String, dynamic>.from(event));
136
+ }
137
+ return DecibelData(
138
+ decibel: -120.0,
139
+ timestamp: DateTime.now().millisecondsSinceEpoch / 1000.0);
140
+ });
141
+ return _decibelStream;
142
+ }
143
+
144
+ SystemAudioConfig _config = SystemAudioConfig();
145
+
146
+ /// Creates a new [SystemAudioCapture] instance.
147
+ ///
148
+ /// [config] is optional. If not provided, default configuration will be used
149
+ /// (sampleRate: 16000, channels: 1).
150
+ ///
151
+ /// Example:
152
+ /// ```dart
153
+ /// final capture = SystemAudioCapture(
154
+ /// config: SystemAudioConfig(
155
+ /// sampleRate: 44100,
156
+ /// channels: 2,
157
+ /// ),
158
+ /// );
159
+ /// ```
160
+ SystemAudioCapture({SystemAudioConfig? config}) {
161
+ _config = config ?? SystemAudioConfig();
162
+ }
163
+
164
+ /// Updates the audio capture configuration.
165
+ ///
166
+ /// This method allows you to change the configuration after the instance
167
+ /// has been created. The new configuration will be applied on the next
168
+ /// [startCapture] call.
169
+ ///
170
+ /// Example:
171
+ /// ```dart
172
+ /// final capture = SystemAudioCapture();
173
+ ///
174
+ /// // Update config before starting
175
+ /// capture.updateConfig(SystemAudioConfig(
176
+ /// sampleRate: 48000,
177
+ /// channels: 2,
178
+ /// ));
179
+ ///
180
+ /// await capture.startCapture();
181
+ /// ```
182
+ void updateConfig(SystemAudioConfig config) {
183
+ _config = config;
184
+ }
185
+
186
+ /// Starts capturing system audio.
187
+ ///
188
+ /// This method will request necessary permissions (screen recording on macOS)
189
+ /// and begin capturing audio from the system output.
190
+ ///
191
+ /// [config] is optional. If provided, it will update the current configuration
192
+ /// before starting capture.
193
+ ///
194
+ /// Throws an [Exception] if:
195
+ /// - Permissions are not granted
196
+ /// - Capture fails to start
197
+ ///
198
+ /// Example:
199
+ /// ```dart
200
+ /// final capture = SystemAudioCapture();
201
+ ///
202
+ /// try {
203
+ /// await capture.startCapture(
204
+ /// config: SystemAudioConfig(
205
+ /// sampleRate: 44100,
206
+ /// channels: 2,
207
+ /// ),
208
+ /// );
209
+ /// print('System audio capture started');
210
+ /// } catch (e) {
211
+ /// print('Failed to start: $e');
212
+ /// }
213
+ /// ```
214
+ Future<void> startCapture({SystemAudioConfig? config}) async {
215
+ if (_isRecording) {
216
+ return;
217
+ }
218
+
219
+ if (config != null) {
220
+ updateConfig(config);
221
+ }
222
+
223
+ try {
224
+ await requestPermissions();
225
+
226
+ final started = await _channel.invokeMethod<bool>(
227
+ _SystemAudioMethod.startCapture.name,
228
+ _config.toMap(),
229
+ );
230
+
231
+ if (started != true) {
232
+ throw Exception('Failed to start system audio capture');
233
+ }
234
+
235
+ // Listen to audio stream
236
+ _audioStream = _audioStreamChannel.receiveBroadcastStream().map((
237
+ dynamic event,
238
+ ) {
239
+ if (event is Uint8List) {
240
+ return event;
241
+ } else if (event is List<int>) {
242
+ return Uint8List.fromList(event);
243
+ }
244
+ throw Exception('Unexpected audio data type: ${event.runtimeType}');
245
+ });
246
+
247
+ // Status stream is created lazily via getter, no need to recreate here
248
+
249
+ _isRecording = true;
250
+ } catch (e) {
251
+ rethrow;
252
+ }
253
+ }
254
+
255
+ /// Stops capturing system audio.
256
+ ///
257
+ /// This method will stop the active capture and close all associated streams.
258
+ /// If capture is not active, this method does nothing.
259
+ ///
260
+ /// Throws an [Exception] if stopping fails.
261
+ ///
262
+ /// Example:
263
+ /// ```dart
264
+ /// await systemCapture.startCapture();
265
+ ///
266
+ /// // ... use audio stream ...
267
+ ///
268
+ /// await systemCapture.stopCapture();
269
+ /// print('System audio capture stopped');
270
+ /// ```
271
+ Future<void> stopCapture() async {
272
+ if (!_isRecording) return;
273
+
274
+ try {
275
+ final stopped = await _channel.invokeMethod<bool>(
276
+ _SystemAudioMethod.stopCapture.name,
277
+ );
278
+
279
+ if (stopped != true) {
280
+ throw Exception("Failed to stop system audio capture");
281
+ }
282
+
283
+ _isRecording = false;
284
+ _audioStream = null;
285
+ _statusStream = null;
286
+ _decibelStream = null;
287
+ } catch (e) {
288
+ rethrow;
289
+ }
290
+ }
291
+
292
+ /// Whether system audio capture is currently recording.
293
+ ///
294
+ /// Returns `true` if capture is active, `false` otherwise.
295
+ ///
296
+ /// Example:
297
+ /// ```dart
298
+ /// if (systemCapture.isRecording) {
299
+ /// print('System audio is being captured');
300
+ /// } else {
301
+ /// print('System audio capture is not active');
302
+ /// }
303
+ /// ```
304
+ @override
305
+ bool get isRecording => _isRecording;
306
+
307
+ /// Requests necessary permissions for system audio capture.
308
+ ///
309
+ /// On macOS, this requests screen recording permission which is required
310
+ /// to capture system audio.
311
+ ///
312
+ /// Returns `true` if permissions are granted.
313
+ ///
314
+ /// Throws an [Exception] if permissions are not granted.
315
+ ///
316
+ /// Example:
317
+ /// ```dart
318
+ /// try {
319
+ /// final hasPermission = await systemCapture.requestPermissions();
320
+ /// if (hasPermission) {
321
+ /// await systemCapture.startCapture();
322
+ /// }
323
+ /// } catch (e) {
324
+ /// print('Permission denied: $e');
325
+ /// }
326
+ /// ```
327
+ Future<bool> requestPermissions() async {
328
+ final hasPermission = await _channel.invokeMethod<bool>(
329
+ _SystemAudioMethod.requestPermissions.name,
330
+ );
331
+ if (hasPermission != true) {
332
+ throw Exception('Screen recording permission not granted');
333
+ }
334
+ return true;
335
+ }
336
+ }