neoagent 2.3.1-beta.4 → 2.3.1-beta.41

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 (290) hide show
  1. package/.env.example +45 -0
  2. package/docs/capabilities.md +2 -2
  3. package/docs/configuration.md +12 -5
  4. package/docs/hardware.md +1 -1
  5. package/docs/integrations.md +2 -3
  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 +99 -0
  66. package/flutter_app/lib/main_account_settings.dart +1250 -0
  67. package/flutter_app/lib/main_admin.dart +886 -0
  68. package/flutter_app/lib/main_app_shell.dart +1682 -0
  69. package/flutter_app/lib/main_chat.dart +3352 -0
  70. package/flutter_app/lib/main_controller.dart +6781 -0
  71. package/flutter_app/lib/main_devices.dart +2301 -0
  72. package/flutter_app/lib/main_integrations.dart +1129 -0
  73. package/flutter_app/lib/main_launcher.dart +959 -0
  74. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  75. package/flutter_app/lib/main_models.dart +3546 -0
  76. package/flutter_app/lib/main_navigation.dart +193 -0
  77. package/flutter_app/lib/main_operations.dart +4851 -0
  78. package/flutter_app/lib/main_recordings.dart +870 -0
  79. package/flutter_app/lib/main_runtime.dart +806 -0
  80. package/flutter_app/lib/main_settings.dart +2024 -0
  81. package/flutter_app/lib/main_shared.dart +2861 -0
  82. package/flutter_app/lib/main_theme.dart +204 -0
  83. package/flutter_app/lib/main_voice_assistant.dart +957 -0
  84. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  85. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  86. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  87. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  88. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  89. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  90. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  91. package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
  92. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  93. package/flutter_app/lib/src/backend_client.dart +1833 -0
  94. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  95. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  96. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  97. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  98. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  99. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  100. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  101. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  102. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  103. package/flutter_app/lib/src/health_bridge.dart +136 -0
  104. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  105. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  106. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  107. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  108. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  109. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  110. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  111. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  112. package/flutter_app/lib/src/oauth_launcher_io.dart +77 -0
  113. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  114. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  115. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  116. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  117. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  118. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  119. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  120. package/flutter_app/lib/src/theme/palette.dart +81 -0
  121. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  122. package/flutter_app/linux/CMakeLists.txt +128 -0
  123. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  124. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +43 -0
  125. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  126. package/flutter_app/linux/flutter/generated_plugins.cmake +31 -0
  127. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  128. package/flutter_app/linux/runner/main.cc +6 -0
  129. package/flutter_app/linux/runner/my_application.cc +144 -0
  130. package/flutter_app/linux/runner/my_application.h +18 -0
  131. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  132. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  133. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  134. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +40 -0
  135. package/flutter_app/macos/Podfile +42 -0
  136. package/flutter_app/macos/Podfile.lock +87 -0
  137. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  138. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  139. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  140. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  141. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  142. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  143. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  144. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  145. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  146. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  147. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  148. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  149. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  150. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  151. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  152. package/flutter_app/macos/Runner/Info.plist +36 -0
  153. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  154. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  155. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  156. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  157. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  158. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  159. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  160. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  161. package/flutter_app/patch_strings.py +12 -0
  162. package/flutter_app/pubspec.lock +1088 -0
  163. package/flutter_app/pubspec.yaml +53 -0
  164. package/flutter_app/test/messaging_access_summary_test.dart +22 -0
  165. package/flutter_app/test/recording_payloads_test.dart +53 -0
  166. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  167. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  168. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  169. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  170. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  171. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  172. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  173. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  174. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  175. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  176. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  177. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  178. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  179. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  180. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  181. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  182. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  183. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  184. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  185. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  186. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  187. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  188. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  189. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  190. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  191. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  192. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  193. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  194. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  195. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  196. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  197. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  198. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  199. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  200. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  201. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  202. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  203. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  204. package/flutter_app/web/favicon.png +0 -0
  205. package/flutter_app/web/favicon.svg +12 -0
  206. package/flutter_app/web/icons/Icon-192.png +0 -0
  207. package/flutter_app/web/icons/Icon-512.png +0 -0
  208. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  209. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  210. package/flutter_app/web/index.html +39 -0
  211. package/flutter_app/web/manifest.json +35 -0
  212. package/flutter_app/windows/CMakeLists.txt +108 -0
  213. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  214. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +47 -0
  215. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  216. package/flutter_app/windows/flutter/generated_plugins.cmake +35 -0
  217. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  218. package/flutter_app/windows/runner/Runner.rc +121 -0
  219. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  220. package/flutter_app/windows/runner/flutter_window.h +37 -0
  221. package/flutter_app/windows/runner/main.cpp +53 -0
  222. package/flutter_app/windows/runner/resource.h +16 -0
  223. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  224. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  225. package/flutter_app/windows/runner/utils.cpp +65 -0
  226. package/flutter_app/windows/runner/utils.h +19 -0
  227. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  228. package/flutter_app/windows/runner/win32_window.h +102 -0
  229. package/lib/install_helpers.js +31 -0
  230. package/lib/manager.js +227 -6
  231. package/package.json +3 -1
  232. package/server/db/database.js +110 -0
  233. package/server/http/middleware.js +55 -2
  234. package/server/http/routes.js +1 -0
  235. package/server/index.js +3 -0
  236. package/server/public/.last_build_id +1 -1
  237. package/server/public/assets/NOTICES +1 -1
  238. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  239. package/server/public/canvaskit/wimp.wasm +0 -0
  240. package/server/public/flutter_bootstrap.js +2 -2
  241. package/server/public/main.dart.js +74324 -73132
  242. package/server/routes/integrations.js +108 -1
  243. package/server/routes/memory.js +11 -2
  244. package/server/{http/routes → routes}/screenHistory.js +2 -2
  245. package/server/routes/settings.js +75 -2
  246. package/server/{http/routes → routes}/triggers.js +2 -2
  247. package/server/routes/wearable.js +67 -0
  248. package/server/services/ai/models.js +30 -0
  249. package/server/services/ai/providers/githubCopilot.js +97 -0
  250. package/server/services/ai/providers/openai.js +2 -1
  251. package/server/services/ai/providers/openaiCodex.js +31 -0
  252. package/server/services/ai/settings.js +20 -0
  253. package/server/services/ai/toolSelector.js +14 -1
  254. package/server/services/ai/tools.js +77 -4
  255. package/server/services/desktop/screenRecorder.js +65 -9
  256. package/server/services/integrations/env.js +5 -0
  257. package/server/services/integrations/figma/provider.js +1 -0
  258. package/server/services/integrations/github/common.js +106 -0
  259. package/server/services/integrations/github/provider.js +499 -0
  260. package/server/services/integrations/github/repos.js +1124 -0
  261. package/server/services/integrations/google/provider.js +1 -0
  262. package/server/services/integrations/home_assistant/provider.js +325 -26
  263. package/server/services/integrations/manager.js +88 -12
  264. package/server/services/integrations/microsoft/provider.js +1 -0
  265. package/server/services/integrations/oauth_provider.js +25 -8
  266. package/server/services/integrations/provider_config_store.js +85 -0
  267. package/server/services/integrations/registry.js +4 -0
  268. package/server/services/integrations/spotify/provider.js +1 -0
  269. package/server/services/integrations/trello/provider.js +842 -0
  270. package/server/services/manager.js +46 -1
  271. package/server/services/mcp/client.js +120 -23
  272. package/server/services/memory/manager.js +39 -2
  273. package/server/services/messaging/access_policy.js +10 -0
  274. package/server/services/messaging/manager.js +49 -0
  275. package/server/services/messaging/meshtastic.js +260 -0
  276. package/server/services/messaging/meshtastic_env.js +100 -0
  277. package/server/services/messaging/meshtastic_protocol.js +476 -0
  278. package/server/services/messaging/meshtastic_tcp_transport.js +25 -0
  279. package/server/services/tasks/runtime.js +1 -1
  280. package/server/services/voice/openaiClient.js +4 -1
  281. package/server/services/voice/openaiSpeech.js +6 -1
  282. package/server/services/voice/providers.js +52 -12
  283. package/server/services/voice/runtimeManager.js +136 -19
  284. package/server/services/voice/turnRunner.js +29 -9
  285. package/server/services/wearable/firmware_manifest.js +370 -0
  286. package/server/services/wearable/gateway.js +350 -0
  287. package/server/services/wearable/protocol.js +45 -0
  288. package/server/services/wearable/service.js +244 -0
  289. package/server/utils/local_secrets.js +56 -0
  290. package/server/utils/logger.js +37 -9
package/lib/manager.js CHANGED
@@ -542,6 +542,40 @@ async function cmdSetup() {
542
542
  const normalizedDeploymentMode = parseDeploymentMode(deploymentMode);
543
543
  const normalizedReleaseChannel = parseReleaseChannel(releaseChannel) || 'stable';
544
544
 
545
+ const githubOauthClientId = await askSecret(
546
+ 'GitHub OAuth client ID',
547
+ current.GITHUB_OAUTH_CLIENT_ID || ''
548
+ );
549
+ const githubOauthClientSecret = await askSecret(
550
+ 'GitHub OAuth client secret',
551
+ current.GITHUB_OAUTH_CLIENT_SECRET || ''
552
+ );
553
+ const githubOauthRedirectUri = await ask(
554
+ 'GitHub OAuth redirect URI',
555
+ current.GITHUB_OAUTH_REDIRECT_URI || ''
556
+ );
557
+
558
+ const homeAssistantOauthClientId = await askSecret(
559
+ 'Home Assistant OAuth client ID',
560
+ current.HOME_ASSISTANT_OAUTH_CLIENT_ID || ''
561
+ );
562
+ const homeAssistantOauthClientSecret = await askSecret(
563
+ 'Home Assistant OAuth client secret',
564
+ current.HOME_ASSISTANT_OAUTH_CLIENT_SECRET || ''
565
+ );
566
+ const homeAssistantOauthRedirectUri = await ask(
567
+ 'Home Assistant OAuth redirect URI',
568
+ current.HOME_ASSISTANT_OAUTH_REDIRECT_URI || ''
569
+ );
570
+ const homeAssistantBaseUrl = await ask(
571
+ 'Home Assistant base URL (e.g., https://ha.example.com)',
572
+ current.HOME_ASSISTANT_BASE_URL || ''
573
+ );
574
+ const homeAssistantAllowPrivateUrl = current.HOME_ASSISTANT_ALLOW_PRIVATE_BASE_URL === '1' ? 'true' : await ask(
575
+ 'Allow local/private Home Assistant base URLs? (true/false)',
576
+ 'false'
577
+ );
578
+
545
579
  const lines = [
546
580
  `NODE_ENV=production`,
547
581
  `PORT=${port}`,
@@ -576,6 +610,14 @@ async function cmdSetup() {
576
610
  figmaOauthClientId ? `FIGMA_OAUTH_CLIENT_ID=${figmaOauthClientId}` : '',
577
611
  figmaOauthClientSecret ? `FIGMA_OAUTH_CLIENT_SECRET=${figmaOauthClientSecret}` : '',
578
612
  figmaOauthRedirectUri ? `FIGMA_OAUTH_REDIRECT_URI=${figmaOauthRedirectUri}` : '',
613
+ githubOauthClientId ? `GITHUB_OAUTH_CLIENT_ID=${githubOauthClientId}` : '',
614
+ githubOauthClientSecret ? `GITHUB_OAUTH_CLIENT_SECRET=${githubOauthClientSecret}` : '',
615
+ githubOauthRedirectUri ? `GITHUB_OAUTH_REDIRECT_URI=${githubOauthRedirectUri}` : '',
616
+ homeAssistantOauthClientId ? `HOME_ASSISTANT_OAUTH_CLIENT_ID=${homeAssistantOauthClientId}` : '',
617
+ homeAssistantOauthClientSecret ? `HOME_ASSISTANT_OAUTH_CLIENT_SECRET=${homeAssistantOauthClientSecret}` : '',
618
+ homeAssistantOauthRedirectUri ? `HOME_ASSISTANT_OAUTH_REDIRECT_URI=${homeAssistantOauthRedirectUri}` : '',
619
+ homeAssistantBaseUrl ? `HOME_ASSISTANT_BASE_URL=${homeAssistantBaseUrl}` : '',
620
+ String(homeAssistantAllowPrivateUrl || '').trim().toLowerCase() === 'true' ? `HOME_ASSISTANT_ALLOW_PRIVATE_BASE_URL=1` : '',
579
621
  deepgramApiKey ? `DEEPGRAM_API_KEY=${deepgramApiKey}` : '',
580
622
  deepgramBaseUrl ? `DEEPGRAM_BASE_URL=${deepgramBaseUrl}` : '',
581
623
  deepgramModel ? `DEEPGRAM_MODEL=${deepgramModel}` : '',
@@ -674,6 +716,172 @@ async function cmdMigrate(args = []) {
674
716
  });
675
717
  }
676
718
 
719
+ async function cmdLogin(args = []) {
720
+ const provider = args[0];
721
+ if (provider !== 'github-copilot' && provider !== 'openai-codex') {
722
+ throw new Error(`Unsupported login provider: ${provider || 'none'}. Available: github-copilot, openai-codex`);
723
+ }
724
+
725
+ if (provider === 'github-copilot') {
726
+ heading('GitHub Copilot Login');
727
+ const clientId = '01ab8ac9400c4e429b23';
728
+ logInfo('Requesting device code from GitHub...');
729
+
730
+ const reqRes = await fetch('https://github.com/login/device/code', {
731
+ method: 'POST',
732
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
733
+ body: JSON.stringify({ client_id: clientId, scope: 'user:email' })
734
+ });
735
+
736
+ if (!reqRes.ok) {
737
+ throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
738
+ }
739
+
740
+ const { device_code, user_code, verification_uri, interval } = await reqRes.json();
741
+
742
+ console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
743
+ console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
744
+
745
+ logInfo('Waiting for authorization (timeout in 15m)...');
746
+ const startTime = Date.now();
747
+ const timeoutMs = 15 * 60 * 1000;
748
+ let currentPollInterval = (interval || 5) * 1000;
749
+
750
+ while (Date.now() - startTime < timeoutMs) {
751
+ await new Promise((r) => setTimeout(r, currentPollInterval));
752
+ const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
753
+ method: 'POST',
754
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
755
+ body: JSON.stringify({
756
+ client_id: clientId,
757
+ device_code,
758
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
759
+ })
760
+ });
761
+
762
+ if (!tokenRes.ok) {
763
+ const errorText = await tokenRes.text().catch(() => 'Unknown error');
764
+ throw new Error(`GitHub token request failed: HTTP ${tokenRes.status} - ${errorText}`);
765
+ }
766
+
767
+ const data = await tokenRes.json();
768
+ if (data.access_token) {
769
+ upsertEnvValue('GITHUB_COPILOT_ACCESS_TOKEN', data.access_token);
770
+ logOk('Successfully authenticated and saved GitHub Copilot access token to .env');
771
+ logInfo('Applying updated provider credentials by restarting NeoAgent...');
772
+ cmdRestart();
773
+ return;
774
+ } else if (data.error === 'authorization_pending') {
775
+ // Continue polling
776
+ } else if (data.error === 'slow_down') {
777
+ currentPollInterval += 5000;
778
+ } else if (data.error) {
779
+ throw new Error(`Authentication failed: ${data.error_description || data.error}`);
780
+ }
781
+ }
782
+ throw new Error('GitHub authentication timed out after 15 minutes.');
783
+ } else if (provider === 'openai-codex') {
784
+ heading('OpenAI Codex Login');
785
+ const clientId = 'app_EMoamEEZ73f0CkXaXp7hrann';
786
+ logInfo('Requesting device code from OpenAI...');
787
+
788
+ const reqRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/usercode', {
789
+ method: 'POST',
790
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
791
+ body: JSON.stringify({ client_id: clientId, scope: 'openid profile email offline_access model.request model.read model.create' })
792
+ });
793
+
794
+ if (!reqRes.ok) {
795
+ throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
796
+ }
797
+
798
+ const data = await reqRes.json();
799
+ const { device_auth_id, interval } = data;
800
+ const user_code = data.user_code || data.usercode;
801
+ const verification_uri = 'https://auth.openai.com/codex/device';
802
+
803
+ console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
804
+ console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
805
+
806
+ logInfo('Waiting for authorization (timeout in 15m)...');
807
+ const startTime = Date.now();
808
+ const timeoutMs = 15 * 60 * 1000;
809
+ let currentPollInterval = (interval || 5) * 1000;
810
+ let authorizationCode = null;
811
+ let codeVerifier = null;
812
+
813
+ while (Date.now() - startTime < timeoutMs) {
814
+ await new Promise((r) => setTimeout(r, currentPollInterval));
815
+ const tokenRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/token', {
816
+ method: 'POST',
817
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
818
+ body: JSON.stringify({
819
+ device_auth_id: device_auth_id,
820
+ user_code: user_code
821
+ })
822
+ });
823
+
824
+ if (tokenRes.status === 403 || tokenRes.status === 404) {
825
+ // These statuses are returned by OpenAI while authorization is pending
826
+ continue;
827
+ }
828
+
829
+ if (!tokenRes.ok) {
830
+ const errorText = await tokenRes.text().catch(() => 'Unknown error');
831
+ throw new Error(`OpenAI token request failed: HTTP ${tokenRes.status} - ${errorText}`);
832
+ }
833
+
834
+ const pollData = await tokenRes.json();
835
+ if (pollData.authorization_code && pollData.code_verifier) {
836
+ authorizationCode = pollData.authorization_code;
837
+ codeVerifier = pollData.code_verifier;
838
+ break;
839
+ } else if (pollData.error === 'authorization_pending') {
840
+ // Continue polling
841
+ } else if (pollData.error === 'slow_down') {
842
+ currentPollInterval += 5000;
843
+ } else if (pollData.error) {
844
+ throw new Error(`Authentication failed: ${pollData.error_description || pollData.error}`);
845
+ }
846
+ }
847
+
848
+ if (!authorizationCode || !codeVerifier) {
849
+ throw new Error('OpenAI authentication timed out after 15 minutes.');
850
+ }
851
+
852
+ logInfo('Exchanging authorization code for access token...');
853
+ const exchangeRes = await fetch('https://auth.openai.com/oauth/token', {
854
+ method: 'POST',
855
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
856
+ body: new URLSearchParams({
857
+ grant_type: 'authorization_code',
858
+ code: authorizationCode,
859
+ redirect_uri: 'https://auth.openai.com/deviceauth/callback',
860
+ client_id: clientId,
861
+ code_verifier: codeVerifier
862
+ })
863
+ });
864
+
865
+ if (!exchangeRes.ok) {
866
+ const errorText = await exchangeRes.text().catch(() => 'Unknown error');
867
+ throw new Error(`OpenAI token exchange failed: HTTP ${exchangeRes.status} - ${errorText}`);
868
+ }
869
+
870
+ const exchangeData = await exchangeRes.json();
871
+ if (exchangeData.access_token) {
872
+ upsertEnvValue('OPENAI_CODEX_ACCESS_TOKEN', exchangeData.access_token);
873
+ if (exchangeData.refresh_token) {
874
+ upsertEnvValue('OPENAI_CODEX_REFRESH_TOKEN', exchangeData.refresh_token);
875
+ }
876
+ logOk('Successfully authenticated and saved OpenAI Codex tokens to .env');
877
+ logInfo('Applying updated provider credentials by restarting NeoAgent...');
878
+ cmdRestart();
879
+ } else {
880
+ throw new Error('OpenAI token exchange succeeded but did not return an access token.');
881
+ }
882
+ }
883
+ }
884
+
677
885
  function installDependencies() {
678
886
  heading('Dependencies');
679
887
  runOrThrow('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], {
@@ -867,6 +1075,11 @@ function cmdRestart() {
867
1075
  cmdStart();
868
1076
  }
869
1077
 
1078
+ async function cmdRebuildWeb() {
1079
+ heading(`Rebuild Flutter Web Client`);
1080
+ buildBundledWebClientIfPossible();
1081
+ }
1082
+
870
1083
  function cmdUninstall() {
871
1084
  heading(`Uninstall ${APP_NAME}`);
872
1085
  const platform = detectPlatform();
@@ -962,6 +1175,8 @@ function cmdUpdate(args = []) {
962
1175
  }
963
1176
  const versionBefore = currentInstalledVersionLabel();
964
1177
  let versionAfter = versionBefore;
1178
+ const githubInstallRef = releaseChannel === 'beta' ? '#beta' : '';
1179
+ const githubInstallSpec = `git+https://github.com/NeoLabs-Systems/NeoAgent.git${githubInstallRef}`;
965
1180
 
966
1181
  if (fs.existsSync(path.join(APP_DIR, '.git')) && commandExists('git')) {
967
1182
  const current = runQuiet('git', ['rev-parse', '--short', 'HEAD']);
@@ -982,17 +1197,16 @@ function cmdUpdate(args = []) {
982
1197
  buildBundledWebClientIfPossible();
983
1198
  }
984
1199
  } else {
985
- const npmTag = resolvePreferredNpmTag(releaseChannel);
986
- logWarn(`No git repo detected; attempting npm global update from ${npmTag}.`);
1200
+ logWarn(`No git repo detected; attempting npm global update from ${githubInstallSpec}.`);
987
1201
  if (commandExists('npm')) {
988
1202
  try {
989
1203
  backupRuntimeData();
990
- runOrThrow('npm', ['install', '-g', `neoagent@${npmTag}`, '--force'], {
1204
+ runOrThrow('npm', ['install', '-g', githubInstallSpec, '--force'], {
991
1205
  env: withInstallEnv()
992
1206
  });
993
- logOk('npm global update completed (forced reinstall)');
1207
+ logOk('npm global update completed (forced reinstall from GitHub)');
994
1208
  } catch {
995
- logWarn(`npm global update failed. Run: npm install -g neoagent@${npmTag} --force`);
1209
+ logWarn(`npm global update failed. Run: npm install -g ${githubInstallSpec} --force`);
996
1210
  }
997
1211
  } else {
998
1212
  logWarn('npm not found. Cannot perform global update.');
@@ -1062,7 +1276,8 @@ async function cmdEnv(args = []) {
1062
1276
  function printHelp() {
1063
1277
  console.log(`${APP_NAME} manager`);
1064
1278
  console.log('Usage: neoagent <command>');
1065
- console.log('Commands: install | setup | env | channel | update | restart | start | stop | status | logs | uninstall | migrate');
1279
+ console.log('Commands: install | setup | env | channel | update | restart | rebuild-web | start | stop | status | logs | uninstall | migrate | login');
1280
+ console.log('Login usage: neoagent login github-copilot | neoagent login openai-codex');
1066
1281
  console.log('Channel usage: neoagent channel | neoagent channel stable | neoagent channel beta');
1067
1282
  console.log('Update usage: neoagent update | neoagent update stable | neoagent update beta');
1068
1283
  console.log('Env usage: neoagent env list | neoagent env get PORT | neoagent env set PORT 3333 | neoagent env unset PORT');
@@ -1094,6 +1309,9 @@ async function runCLI(argv) {
1094
1309
  case 'restart':
1095
1310
  cmdRestart();
1096
1311
  break;
1312
+ case 'rebuild-web':
1313
+ await cmdRebuildWeb();
1314
+ break;
1097
1315
  case 'start':
1098
1316
  cmdStart();
1099
1317
  break;
@@ -1112,6 +1330,9 @@ async function runCLI(argv) {
1112
1330
  case 'migrate':
1113
1331
  await cmdMigrate(argv.slice(1));
1114
1332
  break;
1333
+ case 'login':
1334
+ await cmdLogin(argv.slice(1));
1335
+ break;
1115
1336
  case 'help':
1116
1337
  case '--help':
1117
1338
  case '-h':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.4",
3
+ "version": "2.3.1-beta.41",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -16,6 +16,7 @@
16
16
  "runtime",
17
17
  "server",
18
18
  "extensions",
19
+ "flutter_app",
19
20
  "docs",
20
21
  "com.neoagent.plist",
21
22
  "LICENSE",
@@ -76,6 +77,7 @@
76
77
  "puppeteer-core": "^24.40.0",
77
78
  "puppeteer-extra": "^3.3.6",
78
79
  "puppeteer-extra-plugin-stealth": "^2.11.2",
80
+ "qrcode": "^1.5.4",
79
81
  "sharp": "^0.34.5",
80
82
  "socket.io": "^4.8.1",
81
83
  "telegraf": "^4.16.3",
@@ -272,6 +272,19 @@ db.exec(`
272
272
  FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
273
273
  );
274
274
 
275
+ CREATE TABLE IF NOT EXISTS integration_provider_configs (
276
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
277
+ user_id INTEGER NOT NULL,
278
+ agent_id TEXT,
279
+ provider_key TEXT NOT NULL,
280
+ config_json TEXT DEFAULT '{}',
281
+ created_at TEXT DEFAULT (datetime('now')),
282
+ updated_at TEXT DEFAULT (datetime('now')),
283
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
284
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
285
+ UNIQUE(user_id, agent_id, provider_key)
286
+ );
287
+
275
288
  CREATE TABLE IF NOT EXISTS browser_extension_pairing_requests (
276
289
  id TEXT PRIMARY KEY,
277
290
  user_id INTEGER,
@@ -875,6 +888,7 @@ function createAgentScopedIndexes() {
875
888
  const statements = [
876
889
  ['agent_runs', 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent ON agent_runs(user_id, agent_id, created_at DESC)'],
877
890
  ['integration_connections', 'CREATE INDEX IF NOT EXISTS idx_integration_connections_agent ON integration_connections(user_id, agent_id, provider_key, app_key)'],
891
+ ['integration_provider_configs', 'CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent ON integration_provider_configs(user_id, agent_id, provider_key)'],
878
892
  ['messages', 'CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(user_id, agent_id, created_at DESC)'],
879
893
  ['conversations', 'CREATE INDEX IF NOT EXISTS idx_conversations_agent ON conversations(user_id, agent_id, updated_at DESC)'],
880
894
  ['scheduled_tasks', 'CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_agent ON scheduled_tasks(user_id, agent_id)'],
@@ -922,6 +936,7 @@ function backfillAgentIds() {
922
936
  'platform_connections',
923
937
  'mcp_servers',
924
938
  'integration_connections',
939
+ 'integration_provider_configs',
925
940
  'integration_oauth_states',
926
941
  'scheduled_tasks',
927
942
  'conversations',
@@ -1303,6 +1318,83 @@ function migrateIntegrationOauthStatesTable() {
1303
1318
  `);
1304
1319
  }
1305
1320
 
1321
+ function migrateIntegrationProviderConfigsTable() {
1322
+ if (
1323
+ tableHasColumn('integration_provider_configs', 'agent_id') &&
1324
+ tableHasUniqueIndex('integration_provider_configs', ['user_id', 'agent_id', 'provider_key'])
1325
+ ) {
1326
+ const users = db.prepare('SELECT id FROM users').all();
1327
+ for (const user of users) {
1328
+ const agentId = getMainAgentId(user.id);
1329
+ db.prepare(
1330
+ 'UPDATE integration_provider_configs SET agent_id = ? WHERE user_id = ? AND agent_id IS NULL'
1331
+ ).run(agentId, user.id);
1332
+ }
1333
+ return;
1334
+ }
1335
+
1336
+ const rows = db
1337
+ .prepare('SELECT * FROM integration_provider_configs ORDER BY id ASC')
1338
+ .all();
1339
+
1340
+ db.exec('BEGIN');
1341
+ try {
1342
+ db.exec(`
1343
+ ALTER TABLE integration_provider_configs RENAME TO integration_provider_configs_legacy_agent_scope;
1344
+
1345
+ CREATE TABLE integration_provider_configs (
1346
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1347
+ user_id INTEGER NOT NULL,
1348
+ agent_id TEXT,
1349
+ provider_key TEXT NOT NULL,
1350
+ config_json TEXT DEFAULT '{}',
1351
+ created_at TEXT DEFAULT (datetime('now')),
1352
+ updated_at TEXT DEFAULT (datetime('now')),
1353
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
1354
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
1355
+ UNIQUE(user_id, agent_id, provider_key)
1356
+ );
1357
+ `);
1358
+ const insert = db.prepare(`
1359
+ INSERT OR REPLACE INTO integration_provider_configs (
1360
+ id,
1361
+ user_id,
1362
+ agent_id,
1363
+ provider_key,
1364
+ config_json,
1365
+ created_at,
1366
+ updated_at
1367
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
1368
+ `);
1369
+
1370
+ for (const row of rows) {
1371
+ insert.run(
1372
+ row.id,
1373
+ row.user_id,
1374
+ row.agent_id || getMainAgentId(row.user_id),
1375
+ row.provider_key,
1376
+ row.config_json,
1377
+ row.created_at,
1378
+ row.updated_at,
1379
+ );
1380
+ }
1381
+
1382
+ db.exec(`
1383
+ DROP TABLE integration_provider_configs_legacy_agent_scope;
1384
+ CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent
1385
+ ON integration_provider_configs(user_id, agent_id, provider_key);
1386
+ `);
1387
+ db.exec('COMMIT');
1388
+ } catch (error) {
1389
+ try {
1390
+ db.exec('ROLLBACK');
1391
+ } catch {
1392
+ // Ignore rollback errors and rethrow the original migration failure.
1393
+ }
1394
+ throw error;
1395
+ }
1396
+ }
1397
+
1306
1398
  function migrateIntegrationSecretStorage() {
1307
1399
  try {
1308
1400
  const connectionRows = db
@@ -1335,6 +1427,22 @@ function migrateIntegrationSecretStorage() {
1335
1427
  } catch {
1336
1428
  // Preserve startup even if a row cannot be re-encrypted.
1337
1429
  }
1430
+
1431
+ try {
1432
+ const providerRows = db
1433
+ .prepare('SELECT id, config_json FROM integration_provider_configs')
1434
+ .all();
1435
+ const updateProviderConfig = db.prepare(
1436
+ 'UPDATE integration_provider_configs SET config_json = ? WHERE id = ?',
1437
+ );
1438
+ for (const row of providerRows) {
1439
+ const current = String(row.config_json || '');
1440
+ if (!current || isEncryptedValue(current)) continue;
1441
+ updateProviderConfig.run(encryptValue(current), row.id);
1442
+ }
1443
+ } catch {
1444
+ // Preserve startup even if a row cannot be re-encrypted.
1445
+ }
1338
1446
  }
1339
1447
 
1340
1448
  function backfillVerifiedAccountEmails() {
@@ -1408,7 +1516,9 @@ rebuildPlatformConnectionsForAgents();
1408
1516
  rebuildCoreMemoryForAgents();
1409
1517
  migrateIntegrationConnectionsTable();
1410
1518
  migrateIntegrationOauthStatesTable();
1519
+ migrateIntegrationProviderConfigsTable();
1411
1520
  createAgentScopedIndexes();
1521
+ backfillAgentIds();
1412
1522
  migrateIntegrationSecretStorage();
1413
1523
  backfillVerifiedAccountEmails();
1414
1524
  rebuildFtsForAgents();
@@ -68,6 +68,9 @@ ensureSessionStoreSchema(sessionsDb);
68
68
 
69
69
  function buildHelmetOptions({ secureCookies }) {
70
70
  const wsConnectSrc = secureCookies ? ['wss:'] : ['ws:', 'wss:'];
71
+ const isDevelopment = String(process.env.NODE_ENV || '').trim() === 'development';
72
+ // Flutter web (CanvasKit) requires external script/connect sources and wasm eval.
73
+ // Keep strict overrides available via env for hardened deployments.
71
74
  const allowUnsafeEval = boolEnv('NEOAGENT_CSP_ALLOW_UNSAFE_EVAL', true);
72
75
  const allowExternalScriptCdn = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_SCRIPT_CDN', true);
73
76
  const allowExternalConnect = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_CONNECT', true);
@@ -82,20 +85,31 @@ function buildHelmetOptions({ secureCookies }) {
82
85
 
83
86
  const connectSrc = ["'self'", ...wsConnectSrc];
84
87
  if (allowExternalConnect) {
85
- connectSrc.push('https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.gstatic.com', 'https://api.qrserver.com');
88
+ connectSrc.push('https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.gstatic.com', 'https://cdn.jsdelivr.net');
86
89
  }
87
90
 
88
91
  return {
89
92
  strictTransportSecurity: false,
90
93
  crossOriginOpenerPolicy: false,
94
+ crossOriginResourcePolicy: { policy: 'same-site' },
91
95
  originAgentCluster: false,
96
+ referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
97
+ permissionsPolicy: {
98
+ features: {
99
+ camera: ['self'],
100
+ geolocation: ['self'],
101
+ microphone: ['self'],
102
+ payment: [],
103
+ usb: [],
104
+ },
105
+ },
92
106
  contentSecurityPolicy: {
93
107
  directives: {
94
108
  defaultSrc: ["'self'"],
95
109
  scriptSrc,
96
110
  scriptSrcAttr: ["'unsafe-inline'"],
97
111
  styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
98
- imgSrc: ["'self'", 'data:', 'blob:', 'https://api.qrserver.com'],
112
+ imgSrc: ["'self'", 'data:', 'blob:'],
99
113
  mediaSrc: ["'self'", 'data:', 'blob:'],
100
114
  connectSrc,
101
115
  fontSrc: ["'self'", 'data:', 'https://fonts.gstatic.com'],
@@ -154,6 +168,16 @@ function applyHttpMiddleware(app, { secureCookies, trustProxy, sessionMiddleware
154
168
  const path = `${value}`.split('?')[0];
155
169
  return path === '/socket.io/' || path === '/socket.io' || path.startsWith('/socket.io/');
156
170
  };
171
+ const isStateChangingMethod = (method = '') => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(String(method).toUpperCase());
172
+ const extractOriginFromReferer = (referer = '') => {
173
+ const value = String(referer || '').trim();
174
+ if (!value) return '';
175
+ try {
176
+ return new URL(value).origin;
177
+ } catch {
178
+ return '';
179
+ }
180
+ };
157
181
  const requestPath = (req) => req.originalUrl || req.url || req.path || '';
158
182
  const applyOnlyToRecordingChunk = (handler) => (req, res, next) => (
159
183
  isRecordingChunkPath(requestPath(req)) ? handler(req, res, next) : next()
@@ -187,6 +211,35 @@ function applyHttpMiddleware(app, { secureCookies, trustProxy, sessionMiddleware
187
211
  });
188
212
  })
189
213
  );
214
+ app.use((req, res, next) => {
215
+ const path = `${req.originalUrl || req.url || req.path || ''}`.split('?')[0];
216
+ if (path.startsWith('/api/')) {
217
+ res.setHeader('Cache-Control', 'no-store, max-age=0');
218
+ res.setHeader('Pragma', 'no-cache');
219
+ res.setHeader('Expires', '0');
220
+ }
221
+ next();
222
+ });
223
+ app.use((req, res, next) => {
224
+ const path = `${req.originalUrl || req.url || req.path || ''}`.split('?')[0];
225
+ if (!path.startsWith('/api/')) return next();
226
+ if (!isStateChangingMethod(req.method)) return next();
227
+
228
+ const origin = String(req.get('origin') || '').trim() || extractOriginFromReferer(req.get('referer'));
229
+ if (!origin) return next();
230
+
231
+ const allowBrowserExtensionOrigin = isBrowserExtensionCorsPath(path);
232
+ return validateOrigin(origin, (error) => {
233
+ if (error) {
234
+ logRequestSummary('warn', req, 'blocked state-changing request due to invalid origin', { origin });
235
+ return res.status(403).json({ error: 'Origin not allowed' });
236
+ }
237
+ return next();
238
+ }, {
239
+ allowChromeExtension: allowBrowserExtensionOrigin,
240
+ allowMissingOrigin: false,
241
+ });
242
+ });
190
243
  app.use((req, res, next) => {
191
244
  const startedAt = Date.now();
192
245
 
@@ -26,6 +26,7 @@ const routeRegistry = [
26
26
  { basePath: '/api/desktop', modulePath: '../routes/desktop' },
27
27
  { basePath: '/api/recordings', modulePath: '../routes/recordings' },
28
28
  { basePath: '/api/voice-assistant', modulePath: '../routes/voice_assistant' },
29
+ { basePath: '/api/wearable', modulePath: '../routes/wearable' },
29
30
  { basePath: '/api/mobile/health', modulePath: '../routes/mobile-health' },
30
31
  { basePath: '/api/screen-history', modulePath: '../routes/screenHistory' },
31
32
  { basePath: '/api/triggers', modulePath: '../routes/triggers' }
package/server/index.js CHANGED
@@ -35,6 +35,7 @@ const { registerErrorHandler } = require('./http/errors');
35
35
  const { startServices, stopServices } = require('./services/manager');
36
36
  const { bindBrowserExtensionGateway } = require('./services/browser/extension/gateway');
37
37
  const { bindDesktopCompanionGateway } = require('./services/desktop/gateway');
38
+ const { bindWearableGateway } = require('./services/wearable/gateway');
38
39
 
39
40
  function parseBooleanFlag(value, fallback = false) {
40
41
  const normalized = String(value || '').trim().toLowerCase();
@@ -97,6 +98,7 @@ if (!configuredSessionSecret()) {
97
98
  }
98
99
 
99
100
  const app = express();
101
+ app.disable('x-powered-by');
100
102
  const httpServer = createServer(app);
101
103
  const io = createSocketServer(httpServer, { validateOrigin });
102
104
  app.locals.httpRuntimeConfig = {
@@ -123,6 +125,7 @@ registerStaticRoutes(app);
123
125
  registerErrorHandler(app);
124
126
  bindBrowserExtensionGateway(httpServer, app);
125
127
  bindDesktopCompanionGateway(httpServer, app, sessionMiddleware);
128
+ bindWearableGateway(httpServer, app, sessionMiddleware);
126
129
 
127
130
  let shuttingDown = false;
128
131
  let shutdownExitCode = 0;
@@ -1 +1 @@
1
- dc2ac3821de37958afef0c20433c5630
1
+ 31a20dcc543d16b6423454439e96e69a
@@ -5556,7 +5556,6 @@ path_provider_platform_interface
5556
5556
  path_provider_windows
5557
5557
  plugin_platform_interface
5558
5558
  shared_preferences_linux
5559
- shared_preferences_platform_interface
5560
5559
  shared_preferences_web
5561
5560
  shared_preferences_windows
5562
5561
  xdg_directories
@@ -10835,6 +10834,7 @@ path_provider_foundation
10835
10834
  shared_preferences
10836
10835
  shared_preferences_android
10837
10836
  shared_preferences_foundation
10837
+ shared_preferences_platform_interface
10838
10838
 
10839
10839
  Copyright 2013 The Flutter Authors
10840
10840
 
Binary file
@@ -33,10 +33,10 @@ addEventListener("message", eventListener);
33
33
  if (!window._flutter) {
34
34
  window._flutter = {};
35
35
  }
36
- _flutter.buildConfig = {"engineRevision":"59aa584fdf100e6c78c785d8a5b565d1de4b48ab","builds":[{"compileTarget":"dart2js","renderer":"canvaskit","mainJsPath":"main.dart.js"},{}]};
36
+ _flutter.buildConfig = {"engineRevision":"42d3d75a56efe1a2e9902f52dc8006099c45d937","builds":[{"compileTarget":"dart2js","renderer":"canvaskit","mainJsPath":"main.dart.js"},{}]};
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "2595994922" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "977642556" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });