neoagent 2.3.1-beta.20 → 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 (215) hide show
  1. package/flutter_app/.metadata +42 -0
  2. package/flutter_app/README.md +21 -0
  3. package/flutter_app/analysis_options.yaml +32 -0
  4. package/flutter_app/android/app/build.gradle.kts +109 -0
  5. package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  6. package/flutter_app/android/app/src/main/AndroidManifest.xml +147 -0
  7. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +747 -0
  8. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
  9. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
  10. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
  11. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
  12. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
  13. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
  14. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
  15. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
  16. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
  17. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
  18. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
  19. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
  20. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
  21. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
  22. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
  23. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
  24. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
  25. package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  26. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
  27. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
  28. package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  29. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
  30. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
  31. package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
  32. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  33. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  34. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  35. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  36. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  37. package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
  38. package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  39. package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  40. package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
  41. package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
  42. package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
  43. package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  44. package/flutter_app/android/build.gradle.kts +24 -0
  45. package/flutter_app/android/ci-release.keystore +0 -0
  46. package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  47. package/flutter_app/android/gradle.properties +3 -0
  48. package/flutter_app/android/key.properties +4 -0
  49. package/flutter_app/android/settings.gradle.kts +26 -0
  50. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  51. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  52. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  53. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  54. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  55. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  56. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  57. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  58. package/flutter_app/lib/features/location/location_service.dart +119 -0
  59. package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
  60. package/flutter_app/lib/main.dart +23057 -0
  61. package/flutter_app/lib/main_app_shell.dart +1682 -0
  62. package/flutter_app/lib/main_integrations.dart +931 -0
  63. package/flutter_app/lib/main_launcher.dart +959 -0
  64. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  65. package/flutter_app/lib/main_models.dart +3473 -0
  66. package/flutter_app/lib/main_shared.dart +2861 -0
  67. package/flutter_app/lib/main_theme.dart +204 -0
  68. package/flutter_app/lib/main_voice_assistant.dart +831 -0
  69. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  70. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  71. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  72. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  73. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  74. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  75. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  76. package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
  77. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  78. package/flutter_app/lib/src/backend_client.dart +1833 -0
  79. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  80. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  81. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  82. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  83. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  84. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  85. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  86. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  87. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  88. package/flutter_app/lib/src/health_bridge.dart +136 -0
  89. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  90. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  91. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  92. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  93. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  94. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  95. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  96. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  97. package/flutter_app/lib/src/oauth_launcher_io.dart +77 -0
  98. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  99. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  100. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  101. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  102. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  103. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  104. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  105. package/flutter_app/lib/src/theme/palette.dart +81 -0
  106. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  107. package/flutter_app/linux/CMakeLists.txt +128 -0
  108. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  109. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +43 -0
  110. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  111. package/flutter_app/linux/flutter/generated_plugins.cmake +31 -0
  112. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  113. package/flutter_app/linux/runner/main.cc +6 -0
  114. package/flutter_app/linux/runner/my_application.cc +144 -0
  115. package/flutter_app/linux/runner/my_application.h +18 -0
  116. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  117. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  118. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  119. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +40 -0
  120. package/flutter_app/macos/Podfile +42 -0
  121. package/flutter_app/macos/Podfile.lock +87 -0
  122. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  123. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  124. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  125. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  126. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  127. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  128. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  129. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  130. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  131. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  132. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  133. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  134. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  135. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  136. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  137. package/flutter_app/macos/Runner/Info.plist +36 -0
  138. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  139. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  140. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  141. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  142. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  143. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  144. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  145. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  146. package/flutter_app/patch_strings.py +12 -0
  147. package/flutter_app/pubspec.lock +1088 -0
  148. package/flutter_app/pubspec.yaml +53 -0
  149. package/flutter_app/test/messaging_access_summary_test.dart +22 -0
  150. package/flutter_app/test/recording_payloads_test.dart +53 -0
  151. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  152. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  153. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  154. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  155. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  156. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  157. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  158. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  159. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  160. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  161. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  162. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  163. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  164. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  165. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  166. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  167. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  168. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  169. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  170. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  171. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  172. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  173. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  174. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  175. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  176. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  177. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  178. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  179. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  180. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  181. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  182. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  183. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  184. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  185. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  186. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  187. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  188. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  189. package/flutter_app/web/favicon.png +0 -0
  190. package/flutter_app/web/favicon.svg +12 -0
  191. package/flutter_app/web/icons/Icon-192.png +0 -0
  192. package/flutter_app/web/icons/Icon-512.png +0 -0
  193. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  194. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  195. package/flutter_app/web/index.html +39 -0
  196. package/flutter_app/web/manifest.json +35 -0
  197. package/flutter_app/windows/CMakeLists.txt +108 -0
  198. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  199. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +47 -0
  200. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  201. package/flutter_app/windows/flutter/generated_plugins.cmake +35 -0
  202. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  203. package/flutter_app/windows/runner/Runner.rc +121 -0
  204. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  205. package/flutter_app/windows/runner/flutter_window.h +37 -0
  206. package/flutter_app/windows/runner/main.cpp +53 -0
  207. package/flutter_app/windows/runner/resource.h +16 -0
  208. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  209. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  210. package/flutter_app/windows/runner/utils.cpp +65 -0
  211. package/flutter_app/windows/runner/utils.h +19 -0
  212. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  213. package/flutter_app/windows/runner/win32_window.h +102 -0
  214. package/package.json +2 -1
  215. package/server/public/flutter_bootstrap.js +1 -1
@@ -0,0 +1,576 @@
1
+ import Cocoa
2
+ import FlutterMacOS
3
+ import ApplicationServices
4
+ import CoreGraphics
5
+
6
+ @main
7
+ class AppDelegate: FlutterAppDelegate {
8
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
9
+ return false
10
+ }
11
+
12
+ override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
13
+ return true
14
+ }
15
+ }
16
+
17
+ final class DesktopCompanionNativePlugin: NSObject {
18
+ private static let channelName = "neoagent/desktop_companion_native"
19
+ private static var channelAssociationKey: UInt8 = 0
20
+ private static var instanceAssociationKey: UInt8 = 0
21
+
22
+ static func register(with controller: FlutterViewController) {
23
+ let channel = FlutterMethodChannel(
24
+ name: channelName,
25
+ binaryMessenger: controller.engine.binaryMessenger
26
+ )
27
+
28
+ let instance = DesktopCompanionNativePlugin()
29
+ channel.setMethodCallHandler { call, result in
30
+ instance.handle(call, result: result)
31
+ }
32
+
33
+ objc_setAssociatedObject(
34
+ controller,
35
+ &channelAssociationKey,
36
+ channel,
37
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
38
+ )
39
+ objc_setAssociatedObject(
40
+ controller,
41
+ &instanceAssociationKey,
42
+ instance,
43
+ .OBJC_ASSOCIATION_RETAIN_NONATOMIC
44
+ )
45
+ }
46
+
47
+ private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
48
+ switch call.method {
49
+ case "getStatus":
50
+ result(buildDesktopStatus())
51
+ case "listDisplays":
52
+ result(desktopDisplays())
53
+ case "captureFrame":
54
+ guard preflightScreenCapturePermission() else {
55
+ result(
56
+ FlutterError(
57
+ code: "screen_capture_permission_denied",
58
+ message: "Screen Recording permission is required.",
59
+ details: nil
60
+ )
61
+ )
62
+ return
63
+ }
64
+ let arguments = call.arguments as? [String: Any]
65
+ let displayId = arguments?["displayId"] as? String
66
+ do {
67
+ result(try captureFrame(displayId: displayId))
68
+ } catch {
69
+ result(
70
+ FlutterError(
71
+ code: "capture_failed",
72
+ message: "Desktop capture failed.",
73
+ details: "\(error)"
74
+ )
75
+ )
76
+ }
77
+ case "click":
78
+ guard isAccessibilityTrusted() else {
79
+ result(
80
+ FlutterError(
81
+ code: "accessibility_permission_denied",
82
+ message: "Accessibility permission is required for input control.",
83
+ details: nil
84
+ )
85
+ )
86
+ return
87
+ }
88
+ guard let arguments = call.arguments as? [String: Any],
89
+ let x = arguments["x"] as? NSNumber,
90
+ let y = arguments["y"] as? NSNumber else {
91
+ result(
92
+ FlutterError(
93
+ code: "invalid_arguments",
94
+ message: "Missing click coordinates.",
95
+ details: nil
96
+ )
97
+ )
98
+ return
99
+ }
100
+ let button = (arguments["button"] as? String) ?? "left"
101
+ let displayId = arguments["displayId"] as? String
102
+ performClick(
103
+ x: x.doubleValue,
104
+ y: y.doubleValue,
105
+ button: button,
106
+ displayId: displayId
107
+ )
108
+ result(nil)
109
+ case "drag":
110
+ guard isAccessibilityTrusted() else {
111
+ result(
112
+ FlutterError(
113
+ code: "accessibility_permission_denied",
114
+ message: "Accessibility permission is required for input control.",
115
+ details: nil
116
+ )
117
+ )
118
+ return
119
+ }
120
+ guard let arguments = call.arguments as? [String: Any],
121
+ let x1 = arguments["x1"] as? NSNumber,
122
+ let y1 = arguments["y1"] as? NSNumber,
123
+ let x2 = arguments["x2"] as? NSNumber,
124
+ let y2 = arguments["y2"] as? NSNumber else {
125
+ result(
126
+ FlutterError(
127
+ code: "invalid_arguments",
128
+ message: "Missing drag coordinates.",
129
+ details: nil
130
+ )
131
+ )
132
+ return
133
+ }
134
+ let durationMs = (arguments["durationMs"] as? NSNumber)?.intValue ?? 280
135
+ let displayId = arguments["displayId"] as? String
136
+ performDrag(
137
+ x1: x1.doubleValue,
138
+ y1: y1.doubleValue,
139
+ x2: x2.doubleValue,
140
+ y2: y2.doubleValue,
141
+ durationMs: durationMs,
142
+ displayId: displayId
143
+ )
144
+ result(nil)
145
+ case "scroll":
146
+ guard isAccessibilityTrusted() else {
147
+ result(
148
+ FlutterError(
149
+ code: "accessibility_permission_denied",
150
+ message: "Accessibility permission is required for input control.",
151
+ details: nil
152
+ )
153
+ )
154
+ return
155
+ }
156
+ guard let arguments = call.arguments as? [String: Any] else {
157
+ result(
158
+ FlutterError(
159
+ code: "invalid_arguments",
160
+ message: "Missing scroll payload.",
161
+ details: nil
162
+ )
163
+ )
164
+ return
165
+ }
166
+ let deltaX = (arguments["deltaX"] as? NSNumber)?.int32Value ?? 0
167
+ let deltaY = (arguments["deltaY"] as? NSNumber)?.int32Value ?? 0
168
+ let displayId = arguments["displayId"] as? String
169
+ performScroll(
170
+ deltaX: deltaX,
171
+ deltaY: deltaY,
172
+ displayId: displayId
173
+ )
174
+ result(nil)
175
+ case "typeText":
176
+ guard isAccessibilityTrusted() else {
177
+ result(
178
+ FlutterError(
179
+ code: "accessibility_permission_denied",
180
+ message: "Accessibility permission is required for input control.",
181
+ details: nil
182
+ )
183
+ )
184
+ return
185
+ }
186
+ guard let arguments = call.arguments as? [String: Any] else {
187
+ result(
188
+ FlutterError(
189
+ code: "invalid_arguments",
190
+ message: "Missing text payload.",
191
+ details: nil
192
+ )
193
+ )
194
+ return
195
+ }
196
+ let text = (arguments["text"] as? String) ?? ""
197
+ let pressEnter = (arguments["pressEnter"] as? Bool) ?? false
198
+ typeText(text, pressEnter: pressEnter)
199
+ result(nil)
200
+ case "pressKey":
201
+ guard isAccessibilityTrusted() else {
202
+ result(
203
+ FlutterError(
204
+ code: "accessibility_permission_denied",
205
+ message: "Accessibility permission is required for input control.",
206
+ details: nil
207
+ )
208
+ )
209
+ return
210
+ }
211
+ guard let arguments = call.arguments as? [String: Any],
212
+ let key = arguments["key"] as? String else {
213
+ result(
214
+ FlutterError(
215
+ code: "invalid_arguments",
216
+ message: "Missing key payload.",
217
+ details: nil
218
+ )
219
+ )
220
+ return
221
+ }
222
+ do {
223
+ try pressKey(key)
224
+ result(nil)
225
+ } catch {
226
+ result(
227
+ FlutterError(
228
+ code: "unsupported_key",
229
+ message: "Key is not supported on macOS.",
230
+ details: "\(error)"
231
+ )
232
+ )
233
+ }
234
+ default:
235
+ result(FlutterMethodNotImplemented)
236
+ }
237
+ }
238
+
239
+ private func buildDesktopStatus() -> [String: Any] {
240
+ let accessibilityTrusted = isAccessibilityTrusted()
241
+ var status: [String: Any] = [
242
+ "permissions": [
243
+ "screenCapture": preflightScreenCapturePermission() ? "available" : "required",
244
+ "inputControl": accessibilityTrusted ? "available" : "required",
245
+ "accessibility": accessibilityTrusted ? "available" : "required",
246
+ ],
247
+ "displays": desktopDisplays(),
248
+ "activeDisplayId": defaultDisplayIdentifier(),
249
+ ]
250
+
251
+ if let appName = NSWorkspace.shared.frontmostApplication?.localizedName {
252
+ status["frontmostApp"] = appName
253
+ }
254
+ if let windowTitle = frontmostWindowTitle() {
255
+ status["frontmostWindowTitle"] = windowTitle
256
+ }
257
+ return status
258
+ }
259
+
260
+ private func desktopDisplays() -> [[String: Any]] {
261
+ NSScreen.screens.compactMap { screen in
262
+ guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else {
263
+ return nil
264
+ }
265
+ let frame = screen.frame
266
+ let displayId = CGDirectDisplayID(number.uint32Value)
267
+ return [
268
+ "id": String(displayId),
269
+ "label": screen.localizedName,
270
+ "x": Int(frame.origin.x),
271
+ "y": Int(frame.origin.y),
272
+ "width": Int(frame.width),
273
+ "height": Int(frame.height),
274
+ "scaleFactor": screen.backingScaleFactor,
275
+ "primary": screen == NSScreen.main,
276
+ ]
277
+ }
278
+ }
279
+
280
+ private func captureFrame(displayId: String?) throws -> [String: Any] {
281
+ let resolvedDisplayId = resolveDisplayId(displayId)
282
+ guard let image = CGDisplayCreateImage(resolvedDisplayId) else {
283
+ throw NSError(domain: "NeoAgentDesktop", code: 1)
284
+ }
285
+
286
+ let rep = NSBitmapImageRep(cgImage: image)
287
+ guard let data = rep.representation(using: .png, properties: [:]) else {
288
+ throw NSError(domain: "NeoAgentDesktop", code: 2)
289
+ }
290
+
291
+ return [
292
+ "bytes": FlutterStandardTypedData(bytes: data),
293
+ "mimeType": "image/png",
294
+ "width": image.width,
295
+ "height": image.height,
296
+ "displayId": String(resolvedDisplayId),
297
+ "displays": desktopDisplays(),
298
+ "capturedAt": ISO8601DateFormatter().string(from: Date()),
299
+ "frontmostApp": NSWorkspace.shared.frontmostApplication?.localizedName ?? "",
300
+ "frontmostWindowTitle": frontmostWindowTitle() ?? "",
301
+ ]
302
+ }
303
+
304
+ private func preflightScreenCapturePermission() -> Bool {
305
+ CGPreflightScreenCaptureAccess()
306
+ }
307
+
308
+ private func isAccessibilityTrusted() -> Bool {
309
+ AXIsProcessTrusted()
310
+ }
311
+
312
+ private func resolveDisplayId(_ raw: String?) -> CGDirectDisplayID {
313
+ if let raw,
314
+ let trimmed = optionalTrimmed(raw),
315
+ let screen = screenForDisplayId(trimmed),
316
+ let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber {
317
+ return CGDirectDisplayID(number.uint32Value)
318
+ }
319
+ if let main = NSScreen.main,
320
+ let number = main.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber {
321
+ return CGDirectDisplayID(number.uint32Value)
322
+ }
323
+ return CGMainDisplayID()
324
+ }
325
+
326
+ private func screenForDisplayId(_ raw: String?) -> NSScreen? {
327
+ guard let trimmed = optionalTrimmed(raw),
328
+ let value = UInt32(trimmed) else {
329
+ return nil
330
+ }
331
+ return NSScreen.screens.first { screen in
332
+ guard let number = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber else {
333
+ return false
334
+ }
335
+ return number.uint32Value == value
336
+ }
337
+ }
338
+
339
+ private func optionalTrimmed(_ value: String?) -> String? {
340
+ let trimmed = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
341
+ return trimmed.isEmpty ? nil : trimmed
342
+ }
343
+
344
+ private func defaultDisplayIdentifier() -> String {
345
+ String(resolveDisplayId(nil))
346
+ }
347
+
348
+ private func frontmostWindowTitle() -> String? {
349
+ guard let app = NSWorkspace.shared.frontmostApplication else {
350
+ return nil
351
+ }
352
+ let appElement = AXUIElementCreateApplication(app.processIdentifier)
353
+ var focusedWindow: CFTypeRef?
354
+ let focusedStatus = AXUIElementCopyAttributeValue(
355
+ appElement,
356
+ kAXFocusedWindowAttribute as CFString,
357
+ &focusedWindow
358
+ )
359
+ guard focusedStatus == .success, let focusedWindow else {
360
+ return nil
361
+ }
362
+ let windowElement = unsafeBitCast(focusedWindow, to: AXUIElement.self)
363
+
364
+ var titleValue: CFTypeRef?
365
+ let titleStatus = AXUIElementCopyAttributeValue(
366
+ windowElement,
367
+ kAXTitleAttribute as CFString,
368
+ &titleValue
369
+ )
370
+ guard titleStatus == .success, let title = titleValue as? String, !title.isEmpty else {
371
+ return nil
372
+ }
373
+ return title
374
+ }
375
+
376
+ private func performClick(x: Double, y: Double, button: String, displayId: String?) {
377
+ let point = nativePointForCapturedPixel(x: x, y: y, displayId: displayId)
378
+ let mouseButton = cgMouseButton(button)
379
+ guard let down = CGEvent(
380
+ mouseEventSource: nil,
381
+ mouseType: cgMouseDownType(button),
382
+ mouseCursorPosition: point,
383
+ mouseButton: mouseButton
384
+ ),
385
+ let up = CGEvent(
386
+ mouseEventSource: nil,
387
+ mouseType: cgMouseUpType(button),
388
+ mouseCursorPosition: point,
389
+ mouseButton: mouseButton
390
+ ) else {
391
+ return
392
+ }
393
+ CGWarpMouseCursorPosition(point)
394
+ down.post(tap: .cghidEventTap)
395
+ usleep(12000)
396
+ up.post(tap: .cghidEventTap)
397
+ }
398
+
399
+ private func performDrag(
400
+ x1: Double,
401
+ y1: Double,
402
+ x2: Double,
403
+ y2: Double,
404
+ durationMs: Int,
405
+ displayId: String?
406
+ ) {
407
+ let start = nativePointForCapturedPixel(x: x1, y: y1, displayId: displayId)
408
+ let end = nativePointForCapturedPixel(x: x2, y: y2, displayId: displayId)
409
+ let steps = Swift.max(4, Swift.min(90, durationMs / 16))
410
+
411
+ CGWarpMouseCursorPosition(start)
412
+ guard let down = CGEvent(
413
+ mouseEventSource: nil,
414
+ mouseType: .leftMouseDown,
415
+ mouseCursorPosition: start,
416
+ mouseButton: .left
417
+ ) else {
418
+ return
419
+ }
420
+ down.post(tap: .cghidEventTap)
421
+
422
+ for step in 1...steps {
423
+ let t = Double(step) / Double(steps)
424
+ let point = CGPoint(
425
+ x: start.x + ((end.x - start.x) * t),
426
+ y: start.y + ((end.y - start.y) * t)
427
+ )
428
+ if let dragged = CGEvent(
429
+ mouseEventSource: nil,
430
+ mouseType: .leftMouseDragged,
431
+ mouseCursorPosition: point,
432
+ mouseButton: .left
433
+ ) {
434
+ dragged.post(tap: .cghidEventTap)
435
+ }
436
+ usleep(useconds_t(Swift.max(1, (durationMs * 1000) / Swift.max(1, steps))))
437
+ }
438
+
439
+ if let up = CGEvent(
440
+ mouseEventSource: nil,
441
+ mouseType: .leftMouseUp,
442
+ mouseCursorPosition: end,
443
+ mouseButton: .left
444
+ ) {
445
+ up.post(tap: .cghidEventTap)
446
+ }
447
+ }
448
+
449
+ private func performScroll(deltaX: Int32, deltaY: Int32, displayId: String?) {
450
+ guard let event = CGEvent(
451
+ scrollWheelEvent2Source: nil,
452
+ units: .pixel,
453
+ wheelCount: 2,
454
+ wheel1: -deltaY,
455
+ wheel2: deltaX,
456
+ wheel3: 0
457
+ ) else {
458
+ return
459
+ }
460
+ if displayId != nil {
461
+ event.location = anchorPointForDisplay(displayId)
462
+ }
463
+ event.post(tap: .cghidEventTap)
464
+ }
465
+
466
+ private func typeText(_ text: String, pressEnter: Bool) {
467
+ for scalar in text.unicodeScalars {
468
+ var unicode = UInt16(scalar.value)
469
+ if let down = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: true),
470
+ let up = CGEvent(keyboardEventSource: nil, virtualKey: 0, keyDown: false) {
471
+ down.keyboardSetUnicodeString(stringLength: 1, unicodeString: &unicode)
472
+ up.keyboardSetUnicodeString(stringLength: 1, unicodeString: &unicode)
473
+ down.post(tap: .cghidEventTap)
474
+ up.post(tap: .cghidEventTap)
475
+ }
476
+ }
477
+ if pressEnter {
478
+ postVirtualKey(36)
479
+ }
480
+ }
481
+
482
+ private func pressKey(_ key: String) throws {
483
+ let normalized = key.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
484
+ let keyCode: CGKeyCode
485
+ switch normalized {
486
+ case "enter", "return":
487
+ keyCode = 36
488
+ case "tab":
489
+ keyCode = 48
490
+ case "space":
491
+ keyCode = 49
492
+ case "backspace", "delete":
493
+ keyCode = 51
494
+ case "escape", "esc":
495
+ keyCode = 53
496
+ case "left":
497
+ keyCode = 123
498
+ case "right":
499
+ keyCode = 124
500
+ case "down":
501
+ keyCode = 125
502
+ case "up":
503
+ keyCode = 126
504
+ default:
505
+ throw NSError(domain: "NeoAgentDesktop", code: 3)
506
+ }
507
+ postVirtualKey(keyCode)
508
+ }
509
+
510
+ private func postVirtualKey(_ keyCode: CGKeyCode) {
511
+ let down = CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: true)
512
+ let up = CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: false)
513
+ down?.post(tap: .cghidEventTap)
514
+ up?.post(tap: .cghidEventTap)
515
+ }
516
+
517
+ private func cgMouseButton(_ button: String) -> CGMouseButton {
518
+ switch button.lowercased() {
519
+ case "right":
520
+ return .right
521
+ case "middle":
522
+ return .center
523
+ default:
524
+ return .left
525
+ }
526
+ }
527
+
528
+ private func cgMouseDownType(_ button: String) -> CGEventType {
529
+ switch button.lowercased() {
530
+ case "right":
531
+ return .rightMouseDown
532
+ case "middle":
533
+ return .otherMouseDown
534
+ default:
535
+ return .leftMouseDown
536
+ }
537
+ }
538
+
539
+ private func cgMouseUpType(_ button: String) -> CGEventType {
540
+ switch button.lowercased() {
541
+ case "right":
542
+ return .rightMouseUp
543
+ case "middle":
544
+ return .otherMouseUp
545
+ default:
546
+ return .leftMouseUp
547
+ }
548
+ }
549
+
550
+ private func nativePointForCapturedPixel(x: Double, y: Double, displayId: String?) -> CGPoint {
551
+ let resolvedDisplayId = resolveDisplayId(displayId)
552
+ let displayBounds = CGDisplayBounds(resolvedDisplayId)
553
+ let imageWidth = Double(CGDisplayPixelsWide(resolvedDisplayId))
554
+ let imageHeight = Double(CGDisplayPixelsHigh(resolvedDisplayId))
555
+
556
+ guard imageWidth > 0, imageHeight > 0 else {
557
+ return CGPoint(x: x, y: y)
558
+ }
559
+
560
+ let normalizedX = min(max(x / imageWidth, 0), 1)
561
+ let normalizedY = min(max(y / imageHeight, 0), 1)
562
+ return CGPoint(
563
+ x: displayBounds.origin.x + (displayBounds.width * normalizedX),
564
+ y: displayBounds.origin.y + (displayBounds.height * normalizedY)
565
+ )
566
+ }
567
+
568
+ private func anchorPointForDisplay(_ displayId: String?) -> CGPoint {
569
+ let resolvedDisplayId = resolveDisplayId(displayId)
570
+ let displayBounds = CGDisplayBounds(resolvedDisplayId)
571
+ return CGPoint(
572
+ x: displayBounds.midX,
573
+ y: displayBounds.midY
574
+ )
575
+ }
576
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "images" : [
3
+ {
4
+ "size" : "16x16",
5
+ "idiom" : "mac",
6
+ "filename" : "app_icon_16.png",
7
+ "scale" : "1x"
8
+ },
9
+ {
10
+ "size" : "16x16",
11
+ "idiom" : "mac",
12
+ "filename" : "app_icon_32.png",
13
+ "scale" : "2x"
14
+ },
15
+ {
16
+ "size" : "32x32",
17
+ "idiom" : "mac",
18
+ "filename" : "app_icon_32.png",
19
+ "scale" : "1x"
20
+ },
21
+ {
22
+ "size" : "32x32",
23
+ "idiom" : "mac",
24
+ "filename" : "app_icon_64.png",
25
+ "scale" : "2x"
26
+ },
27
+ {
28
+ "size" : "128x128",
29
+ "idiom" : "mac",
30
+ "filename" : "app_icon_128.png",
31
+ "scale" : "1x"
32
+ },
33
+ {
34
+ "size" : "128x128",
35
+ "idiom" : "mac",
36
+ "filename" : "app_icon_256.png",
37
+ "scale" : "2x"
38
+ },
39
+ {
40
+ "size" : "256x256",
41
+ "idiom" : "mac",
42
+ "filename" : "app_icon_256.png",
43
+ "scale" : "1x"
44
+ },
45
+ {
46
+ "size" : "256x256",
47
+ "idiom" : "mac",
48
+ "filename" : "app_icon_512.png",
49
+ "scale" : "2x"
50
+ },
51
+ {
52
+ "size" : "512x512",
53
+ "idiom" : "mac",
54
+ "filename" : "app_icon_512.png",
55
+ "scale" : "1x"
56
+ },
57
+ {
58
+ "size" : "512x512",
59
+ "idiom" : "mac",
60
+ "filename" : "app_icon_1024.png",
61
+ "scale" : "2x"
62
+ }
63
+ ],
64
+ "info" : {
65
+ "version" : 1,
66
+ "author" : "xcode"
67
+ }
68
+ }