neoagent 3.1.1-beta.9 → 3.2.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.
Files changed (150) hide show
  1. package/.env.example +2 -2
  2. package/README.md +5 -1
  3. package/docs/architecture.md +1 -1
  4. package/docs/benchmarking.md +68 -87
  5. package/docs/capabilities.md +1 -1
  6. package/docs/clients-and-devices.md +3 -4
  7. package/docs/configuration.md +1 -1
  8. package/docs/devices.md +1 -2
  9. package/docs/health.md +24 -0
  10. package/docs/index.md +2 -2
  11. package/docs/integrations-architecture.md +6 -0
  12. package/docs/integrations.md +15 -1
  13. package/docs/models.md +1 -1
  14. package/docs/persistence.md +1 -1
  15. package/docs/why-neoagent.md +1 -1
  16. package/flutter_app/README.md +0 -5
  17. package/flutter_app/android/app/src/main/AndroidManifest.xml +0 -14
  18. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +0 -98
  19. package/flutter_app/lib/main.dart +0 -6
  20. package/flutter_app/lib/main_app_shell.dart +0 -2
  21. package/flutter_app/lib/main_controller.dart +2 -867
  22. package/flutter_app/lib/main_devices.dart +0 -73
  23. package/flutter_app/lib/main_integrations.dart +339 -187
  24. package/flutter_app/lib/main_launcher.dart +2 -212
  25. package/flutter_app/lib/main_models.dart +0 -259
  26. package/flutter_app/lib/main_navigation.dart +2 -14
  27. package/flutter_app/lib/main_operations.dart +322 -364
  28. package/flutter_app/lib/main_runtime.dart +9 -208
  29. package/flutter_app/lib/main_settings.dart +17 -199
  30. package/flutter_app/lib/main_shared.dart +20 -591
  31. package/flutter_app/lib/main_timeline.dart +26 -122
  32. package/flutter_app/lib/main_voice_assistant.dart +0 -5
  33. package/flutter_app/lib/src/backend_client.dart +0 -102
  34. package/flutter_app/lib/src/desktop_companion_io.dart +1 -65
  35. package/flutter_app/lib/src/desktop_companion_stub.dart +0 -12
  36. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +0 -4
  37. package/flutter_app/linux/flutter/generated_plugins.cmake +0 -1
  38. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +0 -2
  39. package/flutter_app/macos/Podfile.lock +56 -5
  40. package/flutter_app/pubspec.lock +0 -7
  41. package/flutter_app/pubspec.yaml +0 -2
  42. package/flutter_app/web/index.html +174 -0
  43. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +0 -3
  44. package/flutter_app/windows/flutter/generated_plugins.cmake +0 -1
  45. package/landing/index.html +0 -2
  46. package/lib/manager.js +1 -1
  47. package/lib/schema_migrations.js +75 -0
  48. package/package.json +1 -4
  49. package/server/db/database.js +1 -133
  50. package/server/http/middleware.js +2 -16
  51. package/server/http/routes.js +0 -2
  52. package/server/public/.last_build_id +1 -1
  53. package/server/public/assets/NOTICES +0 -25
  54. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  55. package/server/public/flutter_bootstrap.js +1 -1
  56. package/server/public/index.html +174 -0
  57. package/server/public/main.dart.js +88081 -90036
  58. package/server/routes/auth.js +11 -2
  59. package/server/routes/settings.js +0 -5
  60. package/server/routes/voice_assistant.js +1 -128
  61. package/server/services/account/erasure.js +0 -3
  62. package/server/services/ai/deliverables/artifact_helpers.js +1 -1
  63. package/server/services/ai/loop/conversation_loop.js +6 -2
  64. package/server/services/ai/loop/tool_dispatch.js +0 -3
  65. package/server/services/ai/messagingFallback.js +1 -1
  66. package/server/services/ai/settings.js +0 -6
  67. package/server/services/ai/toolEvidence.js +1 -4
  68. package/server/services/ai/tools.js +0 -234
  69. package/server/services/desktop/auth.js +0 -3
  70. package/server/services/desktop/registry.js +2 -50
  71. package/server/services/integrations/neoarchive/provider.js +616 -0
  72. package/server/services/integrations/neorecall/client.js +140 -0
  73. package/server/services/integrations/neorecall/constants.js +87 -0
  74. package/server/services/integrations/neorecall/provider.js +163 -0
  75. package/server/services/integrations/registry.js +4 -2
  76. package/server/services/manager.js +0 -22
  77. package/server/services/memory/manager.js +44 -19
  78. package/server/services/memory/retrieval_reasoning.js +36 -14
  79. package/server/services/messaging/whatsapp.js +18 -0
  80. package/server/services/security/tool_categories.js +0 -5
  81. package/server/services/social_video/service.js +2 -20
  82. package/server/services/tasks/adapters/index.js +1 -1
  83. package/server/services/tasks/integration_runtime.js +3 -67
  84. package/server/services/tasks/runtime.js +1 -6
  85. package/server/services/tasks/task_repository.js +6 -13
  86. package/server/services/timeline/service.js +0 -224
  87. package/server/services/voice/message.js +0 -24
  88. package/server/services/voice/providers.js +42 -1
  89. package/server/services/voice/runtimeManager.js +9 -5
  90. package/server/services/voice/turnRunner.js +0 -22
  91. package/server/services/wearable/service.js +0 -5
  92. package/docs/recordings-and-health.md +0 -41
  93. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +0 -21
  94. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +0 -586
  95. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +0 -78
  96. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +0 -104
  97. package/flutter_app/lib/main_recordings.dart +0 -920
  98. package/flutter_app/lib/src/desktop_ocr_bridge.dart +0 -2
  99. package/flutter_app/lib/src/desktop_ocr_bridge_io.dart +0 -125
  100. package/flutter_app/lib/src/desktop_ocr_bridge_stub.dart +0 -30
  101. package/flutter_app/lib/src/desktop_passive_history.dart +0 -332
  102. package/flutter_app/lib/src/recording_bridge.dart +0 -232
  103. package/flutter_app/lib/src/recording_bridge_io.dart +0 -1027
  104. package/flutter_app/lib/src/recording_bridge_stub.dart +0 -120
  105. package/flutter_app/lib/src/recording_bridge_web.dart +0 -702
  106. package/flutter_app/lib/src/recording_chunk_queue.dart +0 -149
  107. package/flutter_app/lib/src/recording_chunk_queue_io.dart +0 -182
  108. package/flutter_app/lib/src/recording_payloads.dart +0 -95
  109. package/flutter_app/third_party/desktop_audio_capture/LICENSE +0 -21
  110. package/flutter_app/third_party/desktop_audio_capture/README.md +0 -262
  111. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +0 -65
  112. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +0 -153
  113. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +0 -110
  114. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +0 -461
  115. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +0 -91
  116. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +0 -106
  117. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +0 -219
  118. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +0 -336
  119. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +0 -101
  120. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +0 -692
  121. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +0 -35
  122. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +0 -36
  123. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +0 -32
  124. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +0 -32
  125. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +0 -878
  126. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +0 -27
  127. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +0 -1172
  128. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +0 -655
  129. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +0 -12
  130. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +0 -30
  131. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +0 -87
  132. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +0 -105
  133. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +0 -80
  134. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +0 -31
  135. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +0 -12
  136. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +0 -23
  137. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +0 -25
  138. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +0 -1117
  139. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +0 -115
  140. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +0 -777
  141. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +0 -87
  142. package/server/routes/recordings.js +0 -335
  143. package/server/routes/screenHistory.js +0 -190
  144. package/server/services/ai/recordingInsights.js +0 -105
  145. package/server/services/integrations/neomail/provider.js +0 -993
  146. package/server/services/recordings/deepgram.js +0 -58
  147. package/server/services/recordings/manager.js +0 -1273
  148. package/server/services/tasks/adapters/neomail_email_received.js +0 -46
  149. package/server/services/voice/screenshotContext.js +0 -73
  150. package/server/services/voice/turnCoordinator.js +0 -51
@@ -1,149 +0,0 @@
1
- import 'dart:async';
2
- import 'dart:typed_data';
3
-
4
- /// Metadata plus bytes for one recording chunk awaiting upload.
5
- class PendingChunk {
6
- const PendingChunk({
7
- required this.sourceKey,
8
- required this.sequence,
9
- required this.startMs,
10
- required this.endMs,
11
- required this.mimeType,
12
- required this.bytes,
13
- });
14
-
15
- final String sourceKey;
16
- final int sequence;
17
- final int startMs;
18
- final int endMs;
19
- final String mimeType;
20
- final Uint8List bytes;
21
-
22
- Map<String, Object?> toMeta() => <String, Object?>{
23
- 'sourceKey': sourceKey,
24
- 'sequence': sequence,
25
- 'startMs': startMs,
26
- 'endMs': endMs,
27
- 'mimeType': mimeType,
28
- };
29
-
30
- static PendingChunk fromMeta(Map<String, Object?> meta, Uint8List bytes) {
31
- return PendingChunk(
32
- sourceKey: '${meta['sourceKey']}',
33
- sequence: (meta['sequence'] as num?)?.toInt() ?? 0,
34
- startMs: (meta['startMs'] as num?)?.toInt() ?? 0,
35
- endMs: (meta['endMs'] as num?)?.toInt() ?? 0,
36
- mimeType: meta['mimeType'] as String? ?? 'application/octet-stream',
37
- bytes: bytes,
38
- );
39
- }
40
- }
41
-
42
- /// Uploads a single chunk. Must throw on any failure so the queue retries.
43
- typedef ChunkUploader = Future<void> Function(PendingChunk chunk);
44
-
45
- typedef QueueLogger =
46
- void Function(String event, {Map<String, Object?> data, Object? error});
47
-
48
- /// In-memory, ordered, retry-until-success upload queue.
49
- ///
50
- /// Chunks upload one at a time in enqueue order. A chunk that fails is kept and
51
- /// retried (with backoff) until it succeeds or [dispose] is called, so a
52
- /// transient network failure never silently drops audio while new chunks keep
53
- /// buffering. Used on web, where no filesystem is available; the queue lives
54
- /// only for the lifetime of the tab.
55
- class MemoryChunkUploadQueue {
56
- MemoryChunkUploadQueue({
57
- required ChunkUploader uploader,
58
- this.maxAttemptsPerPass = 3,
59
- this.retryBackoff = const Duration(seconds: 2),
60
- QueueLogger? logger,
61
- }) : _uploader = uploader,
62
- _logger = logger;
63
-
64
- final ChunkUploader _uploader;
65
- final int maxAttemptsPerPass;
66
- final Duration retryBackoff;
67
- final QueueLogger? _logger;
68
-
69
- final List<PendingChunk> _pending = <PendingChunk>[];
70
- bool _draining = false;
71
- bool _disposed = false;
72
- Timer? _retryTimer;
73
-
74
- int get pendingCount => _pending.length;
75
-
76
- void enqueue(PendingChunk chunk) {
77
- if (_disposed) {
78
- return;
79
- }
80
- _pending.add(chunk);
81
- unawaited(drain());
82
- }
83
-
84
- Future<void> drain() async {
85
- if (_draining || _disposed) {
86
- return;
87
- }
88
- _draining = true;
89
- try {
90
- while (_pending.isNotEmpty && !_disposed) {
91
- final chunk = _pending.first;
92
- final uploaded = await _attemptUpload(chunk);
93
- if (!uploaded) {
94
- _scheduleRetry();
95
- break;
96
- }
97
- if (_pending.isNotEmpty && identical(_pending.first, chunk)) {
98
- _pending.removeAt(0);
99
- }
100
- }
101
- } finally {
102
- _draining = false;
103
- }
104
- }
105
-
106
- Future<bool> _attemptUpload(PendingChunk chunk) async {
107
- for (var attempt = 0; attempt < maxAttemptsPerPass && !_disposed; attempt += 1) {
108
- try {
109
- await _uploader(chunk);
110
- return true;
111
- } catch (error) {
112
- _logger?.call(
113
- 'chunk.upload.failed',
114
- data: <String, Object?>{
115
- 'sourceKey': chunk.sourceKey,
116
- 'sequence': chunk.sequence,
117
- 'attempt': attempt + 1,
118
- },
119
- error: error,
120
- );
121
- await Future<void>.delayed(retryBackoff * (attempt + 1));
122
- }
123
- }
124
- return false;
125
- }
126
-
127
- void _scheduleRetry() {
128
- _retryTimer?.cancel();
129
- _retryTimer = Timer(retryBackoff, () => unawaited(drain()));
130
- }
131
-
132
- /// Wait until every queued chunk has uploaded, or [timeout] elapses.
133
- Future<void> flush({Duration timeout = const Duration(seconds: 30)}) async {
134
- final deadline = DateTime.now().add(timeout);
135
- while (_pending.isNotEmpty && !_disposed && DateTime.now().isBefore(deadline)) {
136
- await drain();
137
- if (_pending.isNotEmpty) {
138
- await Future<void>.delayed(const Duration(milliseconds: 200));
139
- }
140
- }
141
- }
142
-
143
- Future<void> dispose() async {
144
- _disposed = true;
145
- _retryTimer?.cancel();
146
- _retryTimer = null;
147
- _pending.clear();
148
- }
149
- }
@@ -1,182 +0,0 @@
1
- import 'dart:async';
2
- import 'dart:convert';
3
- import 'dart:io';
4
-
5
- import 'recording_chunk_queue.dart';
6
-
7
- /// Disk-backed, ordered, retry-until-success upload queue for desktop.
8
- ///
9
- /// Each chunk is persisted atomically before upload, so a network outage or
10
- /// backend hiccup mid-meeting never loses audio: the chunk stays on disk and is
11
- /// re-sent on the next drain. Files are deleted only after a confirmed upload.
12
- class DiskChunkUploadQueue {
13
- DiskChunkUploadQueue({
14
- required Directory directory,
15
- required ChunkUploader uploader,
16
- this.maxAttemptsPerPass = 3,
17
- this.retryBackoff = const Duration(seconds: 2),
18
- QueueLogger? logger,
19
- }) : _dir = directory,
20
- _uploader = uploader,
21
- _logger = logger;
22
-
23
- final Directory _dir;
24
- final ChunkUploader _uploader;
25
- final int maxAttemptsPerPass;
26
- final Duration retryBackoff;
27
- final QueueLogger? _logger;
28
-
29
- bool _draining = false;
30
- bool _disposed = false;
31
- Timer? _retryTimer;
32
- final Set<Future<void>> _pendingEnqueues = <Future<void>>{};
33
-
34
- Future<void> enqueue(PendingChunk chunk) async {
35
- if (_disposed) {
36
- return;
37
- }
38
- final write = _persistChunk(chunk);
39
- _pendingEnqueues.add(write);
40
- try {
41
- await write;
42
- } finally {
43
- _pendingEnqueues.remove(write);
44
- }
45
- unawaited(drain());
46
- }
47
-
48
- Future<void> drain() async {
49
- if (_draining || _disposed) {
50
- return;
51
- }
52
- _draining = true;
53
- try {
54
- while (!_disposed) {
55
- final metaFiles = await _listMetaFiles();
56
- if (metaFiles.isEmpty) {
57
- break;
58
- }
59
- final metaFile = metaFiles.first;
60
- final audioFile = File(
61
- metaFile.path.replaceFirst(RegExp(r'\.json$'), '.bin'),
62
- );
63
- if (!await audioFile.exists()) {
64
- await _deleteQuietly(metaFile);
65
- continue;
66
- }
67
-
68
- final PendingChunk chunk;
69
- try {
70
- final meta =
71
- jsonDecode(await metaFile.readAsString()) as Map<String, dynamic>;
72
- chunk = PendingChunk.fromMeta(meta, await audioFile.readAsBytes());
73
- } catch (error) {
74
- // Corrupt metadata: drop the pair rather than blocking the queue.
75
- _logger?.call('chunk.read.failed', error: error);
76
- await _deleteQuietly(metaFile);
77
- await _deleteQuietly(audioFile);
78
- continue;
79
- }
80
-
81
- final uploaded = await _attemptUpload(chunk);
82
- if (!uploaded) {
83
- _scheduleRetry();
84
- break;
85
- }
86
- await _deleteQuietly(audioFile);
87
- await _deleteQuietly(metaFile);
88
- }
89
- } finally {
90
- _draining = false;
91
- }
92
- }
93
-
94
- Future<bool> _attemptUpload(PendingChunk chunk) async {
95
- for (var attempt = 0; attempt < maxAttemptsPerPass && !_disposed; attempt += 1) {
96
- try {
97
- await _uploader(chunk);
98
- return true;
99
- } catch (error) {
100
- _logger?.call(
101
- 'chunk.upload.failed',
102
- data: <String, Object?>{
103
- 'sourceKey': chunk.sourceKey,
104
- 'sequence': chunk.sequence,
105
- 'attempt': attempt + 1,
106
- },
107
- error: error,
108
- );
109
- await Future<void>.delayed(retryBackoff * (attempt + 1));
110
- }
111
- }
112
- return false;
113
- }
114
-
115
- Future<List<File>> _listMetaFiles() async {
116
- if (!await _dir.exists()) {
117
- return const <File>[];
118
- }
119
- final entries = await _dir.list().toList();
120
- final metaFiles = entries
121
- .whereType<File>()
122
- .where((file) => file.path.endsWith('.json'))
123
- .toList()
124
- ..sort((a, b) => a.path.compareTo(b.path));
125
- return metaFiles;
126
- }
127
-
128
- Future<int> pendingCount() async => (await _listMetaFiles()).length;
129
-
130
- Future<void> _persistChunk(PendingChunk chunk) async {
131
- await _dir.create(recursive: true);
132
- final base = '${chunk.sourceKey}-${chunk.sequence.toString().padLeft(6, '0')}';
133
- final audioFile = File('${_dir.path}/$base.bin');
134
- final metaFile = File('${_dir.path}/$base.json');
135
- final audioTmp = File('${audioFile.path}.tmp');
136
- final metaTmp = File('${metaFile.path}.tmp');
137
- await audioTmp.writeAsBytes(chunk.bytes, flush: true);
138
- await metaTmp.writeAsString(jsonEncode(chunk.toMeta()), flush: true);
139
- await audioTmp.rename(audioFile.path);
140
- await metaTmp.rename(metaFile.path);
141
- }
142
-
143
- Future<void> _waitForPendingEnqueues() async {
144
- while (_pendingEnqueues.isNotEmpty && !_disposed) {
145
- await Future.wait<void>(List<Future<void>>.from(_pendingEnqueues));
146
- }
147
- }
148
-
149
- void _scheduleRetry() {
150
- _retryTimer?.cancel();
151
- _retryTimer = Timer(retryBackoff, () => unawaited(drain()));
152
- }
153
-
154
- Future<void> _deleteQuietly(File file) async {
155
- try {
156
- if (await file.exists()) {
157
- await file.delete();
158
- }
159
- } catch (error) {
160
- _logger?.call('chunk.delete.failed', error: error);
161
- }
162
- }
163
-
164
- /// Wait until every persisted chunk has uploaded, or [timeout] elapses.
165
- Future<void> flush({Duration timeout = const Duration(seconds: 30)}) async {
166
- final deadline = DateTime.now().add(timeout);
167
- while (!_disposed && DateTime.now().isBefore(deadline)) {
168
- await _waitForPendingEnqueues();
169
- await drain();
170
- if (_pendingEnqueues.isEmpty && await pendingCount() == 0) {
171
- break;
172
- }
173
- await Future<void>.delayed(const Duration(milliseconds: 200));
174
- }
175
- }
176
-
177
- Future<void> dispose() async {
178
- _disposed = true;
179
- _retryTimer?.cancel();
180
- _retryTimer = null;
181
- }
182
- }
@@ -1,95 +0,0 @@
1
- Map<String, dynamic> buildWebScreenAndMicRecordingPayload() {
2
- return <String, dynamic>{
3
- 'platform': 'web',
4
- 'screenAnalysisReady': true,
5
- 'sources': const <Map<String, dynamic>>[
6
- <String, dynamic>{
7
- 'sourceKey': 'screen',
8
- 'sourceKind': 'screen-share',
9
- 'mediaKind': 'video',
10
- 'mimeType': 'video/webm',
11
- 'metadata': <String, dynamic>{
12
- 'analysisReady': true,
13
- 'transcribe': false,
14
- },
15
- },
16
- <String, dynamic>{
17
- 'sourceKey': 'system',
18
- 'sourceKind': 'system-audio',
19
- 'mediaKind': 'audio',
20
- 'mimeType': 'audio/webm',
21
- 'metadata': <String, dynamic>{
22
- 'transcribe': true,
23
- },
24
- },
25
- <String, dynamic>{
26
- 'sourceKey': 'microphone',
27
- 'sourceKind': 'microphone',
28
- 'mediaKind': 'audio',
29
- 'mimeType': 'audio/webm',
30
- },
31
- ],
32
- };
33
- }
34
-
35
- Map<String, dynamic> buildWebMicrophoneRecordingPayload() {
36
- return <String, dynamic>{
37
- 'platform': 'web',
38
- 'screenAnalysisReady': false,
39
- 'sources': const <Map<String, dynamic>>[
40
- <String, dynamic>{
41
- 'sourceKey': 'microphone',
42
- 'sourceKind': 'microphone',
43
- 'mediaKind': 'audio',
44
- 'mimeType': 'audio/webm',
45
- },
46
- ],
47
- };
48
- }
49
-
50
- Map<String, dynamic> buildAndroidBackgroundRecordingPayload() {
51
- return <String, dynamic>{
52
- 'platform': 'android',
53
- 'screenAnalysisReady': false,
54
- 'sources': const <Map<String, dynamic>>[
55
- <String, dynamic>{
56
- 'sourceKey': 'microphone',
57
- 'sourceKind': 'microphone',
58
- 'mediaKind': 'audio',
59
- 'mimeType': 'audio/wav',
60
- 'metadata': <String, dynamic>{'backgroundCapable': true},
61
- },
62
- ],
63
- };
64
- }
65
-
66
- Map<String, dynamic> buildDesktopRecordingPayload() {
67
- return <String, dynamic>{
68
- 'platform': 'desktop',
69
- 'screenAnalysisReady': false,
70
- 'sources': const <Map<String, dynamic>>[
71
- <String, dynamic>{
72
- 'sourceKey': 'microphone',
73
- 'sourceKind': 'microphone',
74
- 'mediaKind': 'audio',
75
- 'mimeType': 'audio/wav',
76
- 'metadata': <String, dynamic>{
77
- 'backgroundCapable': true,
78
- 'foregroundRole': 'user',
79
- },
80
- },
81
- <String, dynamic>{
82
- 'sourceKey': 'system',
83
- 'sourceKind': 'system-audio',
84
- 'mediaKind': 'audio',
85
- 'mimeType': 'audio/wav',
86
- 'metadata': <String, dynamic>{
87
- 'backgroundCapable': true,
88
- 'floatingToolbarCapable': true,
89
- 'globalHotkeyReady': true,
90
- },
91
- },
92
- ],
93
- 'capturePlan': 'desktop-dual-source',
94
- };
95
- }
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 desktop_audio_capture
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,262 +0,0 @@
1
- # desktop_audio_capture
2
-
3
- A Flutter plugin for capturing audio on desktop platforms (macOS, Linux, Windows). Supports capturing from microphone and system audio with features like audio data streaming, decibel monitoring, device management, and permission handling.
4
-
5
- ## Features
6
-
7
- - 🎤 **Microphone Capture**: Capture audio from microphone with flexible configuration
8
- - 🔊 **System Audio Capture**: Capture audio from system (system audio)
9
- - 📊 **Decibel Monitoring**: Real-time audio level monitoring
10
- - 🎛️ **Device Management**: List and manage available input devices
11
- - ⚙️ **Configurable**: Configure sample rate, channels, bit depth, gain boost
12
- - 🔐 **Permissions Handling**: Automatic permission handling for microphone and screen recording
13
-
14
- ## Platform Support
15
-
16
- - ✅ **macOS 13.0+** (Ventura or later) - Fully tested and working
17
- - ✅ **Linux** - Supported
18
- - ✅ **Windows** - Supported
19
-
20
- > **Note**:
21
- > - macOS requires **macOS 13.0 (Ventura) or later** because System Audio Capture uses APIs available from macOS 13.0+
22
-
23
- ## Screenshots
24
-
25
- ### Home Screen
26
- ![Example Home](doc/example_home.png)
27
-
28
- ### Microphone Settings
29
- ![Example Setting Mic](doc/example_setting_mic.png)
30
-
31
- ### System Audio Settings
32
- ![Example Setting System](doc/example_setting_system.png)
33
-
34
- ## Installation
35
-
36
- Add the dependency to your `pubspec.yaml`:
37
-
38
- ```yaml
39
- dependencies:
40
- desktop_audio_capture: ^0.0.1
41
- ```
42
-
43
- Then run:
44
-
45
- ```bash
46
- flutter pub get
47
- ```
48
-
49
- ## Usage
50
-
51
- ### Microphone Capture
52
-
53
- ```dart
54
- import 'package:desktop_audio_capture/audio_capture.dart';
55
-
56
- // Create instance
57
- final micCapture = MicAudioCapture(
58
- config: MicAudioConfig(
59
- sampleRate: 16000,
60
- channels: 1,
61
- bitDepth: 16,
62
- gainBoost: 2.5,
63
- inputVolume: 1.0,
64
- ),
65
- );
66
-
67
- // Start capture
68
- await micCapture.startCapture();
69
-
70
- // Listen to audio stream
71
- micCapture.audioStream?.listen((audioData) {
72
- // Process audio data (Uint8List)
73
- print('Received ${audioData.length} bytes');
74
- });
75
-
76
- // Listen to decibel readings
77
- micCapture.decibelStream?.listen((decibelData) {
78
- print('Decibel: ${decibelData.decibel} dB');
79
- print('Timestamp: ${decibelData.timestamp}');
80
- });
81
-
82
- // Listen to status updates
83
- micCapture.statusStream?.listen((status) {
84
- print('Mic active: ${status.isActive}');
85
- if (status.deviceName != null) {
86
- print('Device: ${status.deviceName}');
87
- }
88
- });
89
-
90
- // Stop capture
91
- await micCapture.stopCapture();
92
- ```
93
-
94
- ### System Audio Capture
95
-
96
- ```dart
97
- import 'package:desktop_audio_capture/audio_capture.dart';
98
-
99
- // Create instance
100
- final systemCapture = SystemAudioCapture(
101
- config: SystemAudioConfig(
102
- sampleRate: 44100,
103
- channels: 2,
104
- ),
105
- );
106
-
107
- // Start capture
108
- await systemCapture.startCapture();
109
-
110
- // Listen to audio stream
111
- systemCapture.audioStream?.listen((audioData) {
112
- // Process audio data (Uint8List)
113
- print('Received ${audioData.length} bytes');
114
- });
115
-
116
- // Listen to decibel readings
117
- systemCapture.decibelStream?.listen((decibelData) {
118
- print('Decibel: ${decibelData.decibel} dB');
119
- print('Timestamp: ${decibelData.timestamp}');
120
- });
121
-
122
- // Listen to status updates
123
- systemCapture.statusStream?.listen((status) {
124
- print('System audio active: ${status.isActive}');
125
- });
126
-
127
- // Stop capture
128
- await systemCapture.stopCapture();
129
- ```
130
-
131
- ### Device Management (Microphone)
132
-
133
- ```dart
134
- // Check if input device is available
135
- final hasDevice = await micCapture.hasInputDevice();
136
-
137
- // Get list of available devices
138
- final devices = await micCapture.getAvailableInputDevices();
139
- for (final device in devices) {
140
- print('Device: ${device.name}');
141
- print('Type: ${device.type.toString()}');
142
- print('Channels: ${device.channelCount}');
143
- print('Default: ${device.isDefault}');
144
- }
145
- ```
146
-
147
- ## API Reference
148
-
149
- ### MicAudioCapture
150
-
151
- #### Methods
152
-
153
- - `startCapture({MicAudioConfig? config})`: Start capturing from microphone
154
- - `stopCapture()`: Stop capture
155
- - `requestPermissions()`: Request microphone access permission
156
- - `hasInputDevice()`: Check if input device is available
157
- - `getAvailableInputDevices()`: Get list of available input devices
158
- - `updateConfig(MicAudioConfig config)`: Update configuration
159
-
160
- #### Streams
161
-
162
- - `audioStream`: Stream of audio data (Uint8List)
163
- - `decibelStream`: Stream of decibel readings (DecibelData)
164
- - `statusStream`: Stream of status updates (MicAudioStatus)
165
-
166
- #### Properties
167
-
168
- - `isRecording`: Whether currently recording or not
169
-
170
- ### SystemAudioCapture
171
-
172
- #### Methods
173
-
174
- - `startCapture({SystemAudioConfig? config})`: Start capturing system audio
175
- - `stopCapture()`: Stop capture
176
- - `requestPermissions()`: Request screen recording permission (macOS)
177
- - `updateConfig(SystemAudioConfig config)`: Update configuration
178
-
179
- #### Streams
180
-
181
- - `audioStream`: Stream of audio data (Uint8List)
182
- - `decibelStream`: Stream of decibel readings (DecibelData)
183
- - `statusStream`: Stream of status updates (SystemAudioStatus)
184
-
185
- #### Properties
186
-
187
- - `isRecording`: Whether currently recording or not
188
-
189
- ### MicAudioConfig
190
-
191
- - `sampleRate` (int): Sample rate (default: 16000 Hz)
192
- - `channels` (int): Number of audio channels (default: 1)
193
- - `bitDepth` (int): Bit depth (default: 16)
194
- - `gainBoost` (double): Gain boost multiplier (default: 2.5, range: 0.1-10.0)
195
- - `inputVolume` (double): Input volume (default: 1.0, range: 0.0-1.0)
196
-
197
- ### SystemAudioConfig
198
-
199
- - `sampleRate` (int): Sample rate (default: 16000 Hz)
200
- - `channels` (int): Number of audio channels (default: 1)
201
-
202
- ### DecibelData
203
-
204
- - `decibel` (double): Decibel value (-120 to 0 dB)
205
- - `timestamp` (double): Unix timestamp in seconds
206
-
207
- ### MicAudioStatus
208
-
209
- - `isActive` (bool): Whether microphone capture is currently active
210
- - `deviceName` (String?): Name of the microphone device (if available)
211
-
212
- ### SystemAudioStatus
213
-
214
- - `isActive` (bool): Whether system audio capture is currently active
215
-
216
- ### InputDevice
217
-
218
- - `id` (String): Unique identifier of the device
219
- - `name` (String): Human-readable name of the device
220
- - `type` (InputDeviceType): Type of device (builtIn, bluetooth, external)
221
- - `channelCount` (int): Number of audio channels supported
222
- - `isDefault` (bool): Whether this device is the system default input device
223
-
224
- ## Permissions
225
-
226
- ### macOS (13.0+)
227
-
228
- **System Requirements**: macOS 13.0 (Ventura) or later is required.
229
-
230
- - **Microphone**: Requires microphone access permission in `Info.plist`:
231
- ```xml
232
- <key>NSMicrophoneUsageDescription</key>
233
- <string>This app needs access to the microphone to capture audio.</string>
234
- ```
235
-
236
- - **System Audio**: Requires Screen Recording permission in System Preferences. Add to `Info.plist`:
237
- ```xml
238
- <key>NSScreenCaptureDescription</key>
239
- <string>This app needs Screen Recording permission to capture system audio.</string>
240
- ```
241
-
242
- After adding to `Info.plist`, users need to grant permission in **System Preferences > Security & Privacy > Screen Recording**.
243
-
244
- ### Linux
245
-
246
- - Requires access to audio devices (usually automatic)
247
-
248
- ### Windows
249
-
250
- - Requires microphone access (usually automatic)
251
-
252
- ## Example
253
-
254
- See detailed example in the [example](example/) directory.
255
-
256
- ## License
257
-
258
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
259
-
260
- ## Homepage
261
-
262
- [https://github.com/hiuhuyn/desktop_audio_capture](https://github.com/hiuhuyn/desktop_audio_capture)