neoagent 2.3.1-beta.6 → 2.3.1-beta.60

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 (312) hide show
  1. package/.env.example +45 -0
  2. package/docs/capabilities.md +2 -2
  3. package/docs/configuration.md +6 -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 +111 -0
  10. package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  11. package/flutter_app/android/app/src/main/AndroidManifest.xml +171 -0
  12. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +804 -0
  13. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/auto/NeoAgentCarAppService.kt +114 -0
  14. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
  15. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
  16. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
  17. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
  18. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
  19. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
  20. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
  21. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
  22. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
  23. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
  24. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnection.kt +118 -0
  25. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnectionService.kt +86 -0
  26. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
  27. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
  28. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
  29. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
  30. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
  31. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
  32. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
  33. package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  34. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
  35. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
  36. package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  37. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
  38. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
  39. package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
  40. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  41. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  42. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  43. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  44. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  45. package/flutter_app/android/app/src/main/res/values/arrays.xml +8 -0
  46. package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
  47. package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  48. package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  49. package/flutter_app/android/app/src/main/res/xml/automotive_app_desc.xml +4 -0
  50. package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
  51. package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
  52. package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
  53. package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  54. package/flutter_app/android/build.gradle.kts +24 -0
  55. package/flutter_app/android/ci-release.keystore +0 -0
  56. package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  57. package/flutter_app/android/gradle.properties +3 -0
  58. package/flutter_app/android/key.properties +4 -0
  59. package/flutter_app/android/settings.gradle.kts +26 -0
  60. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  61. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  62. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  63. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  64. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  65. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  66. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  67. package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
  68. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  69. package/flutter_app/lib/features/location/location_service.dart +119 -0
  70. package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
  71. package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +569 -0
  72. package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +157 -0
  73. package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +233 -0
  74. package/flutter_app/lib/features/onboarding/onboarding_shell.dart +57 -0
  75. package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +218 -0
  76. package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +46 -0
  77. package/flutter_app/lib/main.dart +103 -0
  78. package/flutter_app/lib/main_account_settings.dart +1250 -0
  79. package/flutter_app/lib/main_admin.dart +886 -0
  80. package/flutter_app/lib/main_app_shell.dart +2134 -0
  81. package/flutter_app/lib/main_chat.dart +3316 -0
  82. package/flutter_app/lib/main_controller.dart +6836 -0
  83. package/flutter_app/lib/main_devices.dart +2396 -0
  84. package/flutter_app/lib/main_integrations.dart +909 -0
  85. package/flutter_app/lib/main_launcher.dart +959 -0
  86. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  87. package/flutter_app/lib/main_models.dart +3539 -0
  88. package/flutter_app/lib/main_navigation.dart +188 -0
  89. package/flutter_app/lib/main_operations.dart +4851 -0
  90. package/flutter_app/lib/main_recordings.dart +920 -0
  91. package/flutter_app/lib/main_runtime.dart +838 -0
  92. package/flutter_app/lib/main_settings.dart +2040 -0
  93. package/flutter_app/lib/main_shared.dart +3147 -0
  94. package/flutter_app/lib/main_theme.dart +257 -0
  95. package/flutter_app/lib/main_voice_assistant.dart +957 -0
  96. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  97. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  98. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  99. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  100. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  101. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  102. package/flutter_app/lib/src/android_auto_bridge.dart +59 -0
  103. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  104. package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
  105. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  106. package/flutter_app/lib/src/backend_client.dart +1837 -0
  107. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  108. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  109. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  110. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  111. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  112. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  113. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  114. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  115. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  116. package/flutter_app/lib/src/health_bridge.dart +136 -0
  117. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  118. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  119. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  120. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  121. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  122. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  123. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  124. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  125. package/flutter_app/lib/src/oauth_launcher_io.dart +95 -0
  126. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  127. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  128. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  129. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  130. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  131. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  132. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  133. package/flutter_app/lib/src/theme/palette.dart +81 -0
  134. package/flutter_app/lib/src/web_app_update_monitor.dart +17 -0
  135. package/flutter_app/lib/src/web_app_update_monitor_stub.dart +24 -0
  136. package/flutter_app/lib/src/web_app_update_monitor_web.dart +123 -0
  137. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  138. package/flutter_app/linux/CMakeLists.txt +128 -0
  139. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  140. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +47 -0
  141. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  142. package/flutter_app/linux/flutter/generated_plugins.cmake +32 -0
  143. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  144. package/flutter_app/linux/runner/main.cc +6 -0
  145. package/flutter_app/linux/runner/my_application.cc +144 -0
  146. package/flutter_app/linux/runner/my_application.h +18 -0
  147. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  148. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  149. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  150. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +44 -0
  151. package/flutter_app/macos/Podfile +42 -0
  152. package/flutter_app/macos/Podfile.lock +87 -0
  153. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  154. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  155. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  156. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  157. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  158. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  159. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  160. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  161. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  162. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  163. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  164. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  165. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  166. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  167. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  168. package/flutter_app/macos/Runner/Info.plist +36 -0
  169. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  170. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  171. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  172. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  173. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  174. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  175. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  176. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  177. package/flutter_app/patch_strings.py +12 -0
  178. package/flutter_app/pubspec.lock +1224 -0
  179. package/flutter_app/pubspec.yaml +57 -0
  180. package/flutter_app/test/messaging_access_summary_test.dart +22 -0
  181. package/flutter_app/test/recording_payloads_test.dart +53 -0
  182. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  183. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  184. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  185. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  186. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  187. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  188. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  189. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  190. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  191. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  192. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  193. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  194. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  195. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  196. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  197. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  198. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  199. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  200. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  201. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  202. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  203. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  204. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  205. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  206. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  207. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  208. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  209. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  210. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  211. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  212. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  213. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  214. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  215. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  216. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  217. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  218. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  219. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  220. package/flutter_app/web/favicon.png +0 -0
  221. package/flutter_app/web/favicon.svg +12 -0
  222. package/flutter_app/web/icons/Icon-192.png +0 -0
  223. package/flutter_app/web/icons/Icon-512.png +0 -0
  224. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  225. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  226. package/flutter_app/web/index.html +39 -0
  227. package/flutter_app/web/manifest.json +35 -0
  228. package/flutter_app/windows/CMakeLists.txt +108 -0
  229. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  230. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +50 -0
  231. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  232. package/flutter_app/windows/flutter/generated_plugins.cmake +36 -0
  233. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  234. package/flutter_app/windows/runner/Runner.rc +121 -0
  235. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  236. package/flutter_app/windows/runner/flutter_window.h +37 -0
  237. package/flutter_app/windows/runner/main.cpp +53 -0
  238. package/flutter_app/windows/runner/resource.h +16 -0
  239. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  240. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  241. package/flutter_app/windows/runner/utils.cpp +65 -0
  242. package/flutter_app/windows/runner/utils.h +19 -0
  243. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  244. package/flutter_app/windows/runner/win32_window.h +102 -0
  245. package/lib/install_helpers.js +31 -0
  246. package/lib/manager.js +201 -6
  247. package/package.json +5 -3
  248. package/server/db/database.js +125 -1
  249. package/server/http/middleware.js +55 -2
  250. package/server/http/routes.js +1 -0
  251. package/server/http/static.js +41 -1
  252. package/server/index.js +3 -0
  253. package/server/public/.last_build_id +1 -1
  254. package/server/public/assets/AssetManifest.bin +1 -1
  255. package/server/public/assets/AssetManifest.bin.json +1 -1
  256. package/server/public/assets/NOTICES +281 -1
  257. package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
  258. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  259. package/server/public/canvaskit/wimp.wasm +0 -0
  260. package/server/public/flutter_bootstrap.js +2 -2
  261. package/server/public/main.dart.js +86314 -82363
  262. package/server/routes/auth.js +26 -5
  263. package/server/routes/integrations.js +108 -1
  264. package/server/routes/memory.js +11 -2
  265. package/server/routes/settings.js +84 -2
  266. package/server/routes/wearable.js +67 -0
  267. package/server/services/ai/engine.js +3 -2
  268. package/server/services/ai/models.js +30 -0
  269. package/server/services/ai/providers/githubCopilot.js +97 -0
  270. package/server/services/ai/providers/openai.js +2 -1
  271. package/server/services/ai/providers/openaiCodex.js +31 -0
  272. package/server/services/ai/settings.js +20 -0
  273. package/server/services/ai/toolSelector.js +54 -1
  274. package/server/services/ai/tools.js +88 -5
  275. package/server/services/desktop/screenRecorder.js +65 -9
  276. package/server/services/integrations/env.js +5 -0
  277. package/server/services/integrations/figma/provider.js +1 -0
  278. package/server/services/integrations/github/common.js +106 -0
  279. package/server/services/integrations/github/provider.js +499 -0
  280. package/server/services/integrations/github/repos.js +1124 -0
  281. package/server/services/integrations/google/provider.js +1 -0
  282. package/server/services/integrations/manager.js +88 -12
  283. package/server/services/integrations/microsoft/provider.js +1 -0
  284. package/server/services/integrations/oauth_provider.js +25 -8
  285. package/server/services/integrations/provider_config_store.js +85 -0
  286. package/server/services/integrations/registry.js +4 -2
  287. package/server/services/integrations/spotify/provider.js +1 -0
  288. package/server/services/integrations/trello/provider.js +842 -0
  289. package/server/services/manager.js +46 -1
  290. package/server/services/mcp/client.js +120 -23
  291. package/server/services/memory/manager.js +39 -2
  292. package/server/services/messaging/access_policy.js +10 -0
  293. package/server/services/messaging/manager.js +49 -0
  294. package/server/services/messaging/meshtastic.js +277 -0
  295. package/server/services/messaging/meshtastic_env.js +40 -0
  296. package/server/services/messaging/meshtastic_protocol.js +530 -0
  297. package/server/services/messaging/meshtastic_protocol.test.js +33 -0
  298. package/server/services/messaging/meshtastic_tcp_transport.js +25 -0
  299. package/server/services/tasks/runtime.js +1 -1
  300. package/server/services/voice/openaiClient.js +4 -1
  301. package/server/services/voice/openaiSpeech.js +6 -1
  302. package/server/services/voice/providers.js +144 -15
  303. package/server/services/voice/runtimeManager.js +156 -19
  304. package/server/services/voice/turnRunner.js +29 -9
  305. package/server/services/wearable/firmware_manifest.js +353 -0
  306. package/server/services/wearable/gateway.js +350 -0
  307. package/server/services/wearable/protocol.js +45 -0
  308. package/server/services/wearable/service.js +240 -0
  309. package/server/services/widgets/service.js +3 -0
  310. package/server/utils/local_secrets.js +56 -0
  311. package/server/utils/logger.js +37 -9
  312. package/server/services/integrations/home_assistant/provider.js +0 -350
package/lib/manager.js CHANGED
@@ -542,6 +542,19 @@ 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
+
545
558
  const lines = [
546
559
  `NODE_ENV=production`,
547
560
  `PORT=${port}`,
@@ -576,6 +589,9 @@ async function cmdSetup() {
576
589
  figmaOauthClientId ? `FIGMA_OAUTH_CLIENT_ID=${figmaOauthClientId}` : '',
577
590
  figmaOauthClientSecret ? `FIGMA_OAUTH_CLIENT_SECRET=${figmaOauthClientSecret}` : '',
578
591
  figmaOauthRedirectUri ? `FIGMA_OAUTH_REDIRECT_URI=${figmaOauthRedirectUri}` : '',
592
+ githubOauthClientId ? `GITHUB_OAUTH_CLIENT_ID=${githubOauthClientId}` : '',
593
+ githubOauthClientSecret ? `GITHUB_OAUTH_CLIENT_SECRET=${githubOauthClientSecret}` : '',
594
+ githubOauthRedirectUri ? `GITHUB_OAUTH_REDIRECT_URI=${githubOauthRedirectUri}` : '',
579
595
  deepgramApiKey ? `DEEPGRAM_API_KEY=${deepgramApiKey}` : '',
580
596
  deepgramBaseUrl ? `DEEPGRAM_BASE_URL=${deepgramBaseUrl}` : '',
581
597
  deepgramModel ? `DEEPGRAM_MODEL=${deepgramModel}` : '',
@@ -674,6 +690,172 @@ async function cmdMigrate(args = []) {
674
690
  });
675
691
  }
676
692
 
693
+ async function cmdLogin(args = []) {
694
+ const provider = args[0];
695
+ if (provider !== 'github-copilot' && provider !== 'openai-codex') {
696
+ throw new Error(`Unsupported login provider: ${provider || 'none'}. Available: github-copilot, openai-codex`);
697
+ }
698
+
699
+ if (provider === 'github-copilot') {
700
+ heading('GitHub Copilot Login');
701
+ const clientId = '01ab8ac9400c4e429b23';
702
+ logInfo('Requesting device code from GitHub...');
703
+
704
+ const reqRes = await fetch('https://github.com/login/device/code', {
705
+ method: 'POST',
706
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
707
+ body: JSON.stringify({ client_id: clientId, scope: 'user:email' })
708
+ });
709
+
710
+ if (!reqRes.ok) {
711
+ throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
712
+ }
713
+
714
+ const { device_code, user_code, verification_uri, interval } = await reqRes.json();
715
+
716
+ console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
717
+ console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
718
+
719
+ logInfo('Waiting for authorization (timeout in 15m)...');
720
+ const startTime = Date.now();
721
+ const timeoutMs = 15 * 60 * 1000;
722
+ let currentPollInterval = (interval || 5) * 1000;
723
+
724
+ while (Date.now() - startTime < timeoutMs) {
725
+ await new Promise((r) => setTimeout(r, currentPollInterval));
726
+ const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
727
+ method: 'POST',
728
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
729
+ body: JSON.stringify({
730
+ client_id: clientId,
731
+ device_code,
732
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
733
+ })
734
+ });
735
+
736
+ if (!tokenRes.ok) {
737
+ const errorText = await tokenRes.text().catch(() => 'Unknown error');
738
+ throw new Error(`GitHub token request failed: HTTP ${tokenRes.status} - ${errorText}`);
739
+ }
740
+
741
+ const data = await tokenRes.json();
742
+ if (data.access_token) {
743
+ upsertEnvValue('GITHUB_COPILOT_ACCESS_TOKEN', data.access_token);
744
+ logOk('Successfully authenticated and saved GitHub Copilot access token to .env');
745
+ logInfo('Applying updated provider credentials by restarting NeoAgent...');
746
+ cmdRestart();
747
+ return;
748
+ } else if (data.error === 'authorization_pending') {
749
+ // Continue polling
750
+ } else if (data.error === 'slow_down') {
751
+ currentPollInterval += 5000;
752
+ } else if (data.error) {
753
+ throw new Error(`Authentication failed: ${data.error_description || data.error}`);
754
+ }
755
+ }
756
+ throw new Error('GitHub authentication timed out after 15 minutes.');
757
+ } else if (provider === 'openai-codex') {
758
+ heading('OpenAI Codex Login');
759
+ const clientId = 'app_EMoamEEZ73f0CkXaXp7hrann';
760
+ logInfo('Requesting device code from OpenAI...');
761
+
762
+ const reqRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/usercode', {
763
+ method: 'POST',
764
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
765
+ body: JSON.stringify({ client_id: clientId, scope: 'openid profile email offline_access model.request model.read model.create' })
766
+ });
767
+
768
+ if (!reqRes.ok) {
769
+ throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
770
+ }
771
+
772
+ const data = await reqRes.json();
773
+ const { device_auth_id, interval } = data;
774
+ const user_code = data.user_code || data.usercode;
775
+ const verification_uri = 'https://auth.openai.com/codex/device';
776
+
777
+ console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
778
+ console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
779
+
780
+ logInfo('Waiting for authorization (timeout in 15m)...');
781
+ const startTime = Date.now();
782
+ const timeoutMs = 15 * 60 * 1000;
783
+ let currentPollInterval = (interval || 5) * 1000;
784
+ let authorizationCode = null;
785
+ let codeVerifier = null;
786
+
787
+ while (Date.now() - startTime < timeoutMs) {
788
+ await new Promise((r) => setTimeout(r, currentPollInterval));
789
+ const tokenRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/token', {
790
+ method: 'POST',
791
+ headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
792
+ body: JSON.stringify({
793
+ device_auth_id: device_auth_id,
794
+ user_code: user_code
795
+ })
796
+ });
797
+
798
+ if (tokenRes.status === 403 || tokenRes.status === 404) {
799
+ // These statuses are returned by OpenAI while authorization is pending
800
+ continue;
801
+ }
802
+
803
+ if (!tokenRes.ok) {
804
+ const errorText = await tokenRes.text().catch(() => 'Unknown error');
805
+ throw new Error(`OpenAI token request failed: HTTP ${tokenRes.status} - ${errorText}`);
806
+ }
807
+
808
+ const pollData = await tokenRes.json();
809
+ if (pollData.authorization_code && pollData.code_verifier) {
810
+ authorizationCode = pollData.authorization_code;
811
+ codeVerifier = pollData.code_verifier;
812
+ break;
813
+ } else if (pollData.error === 'authorization_pending') {
814
+ // Continue polling
815
+ } else if (pollData.error === 'slow_down') {
816
+ currentPollInterval += 5000;
817
+ } else if (pollData.error) {
818
+ throw new Error(`Authentication failed: ${pollData.error_description || pollData.error}`);
819
+ }
820
+ }
821
+
822
+ if (!authorizationCode || !codeVerifier) {
823
+ throw new Error('OpenAI authentication timed out after 15 minutes.');
824
+ }
825
+
826
+ logInfo('Exchanging authorization code for access token...');
827
+ const exchangeRes = await fetch('https://auth.openai.com/oauth/token', {
828
+ method: 'POST',
829
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
830
+ body: new URLSearchParams({
831
+ grant_type: 'authorization_code',
832
+ code: authorizationCode,
833
+ redirect_uri: 'https://auth.openai.com/deviceauth/callback',
834
+ client_id: clientId,
835
+ code_verifier: codeVerifier
836
+ })
837
+ });
838
+
839
+ if (!exchangeRes.ok) {
840
+ const errorText = await exchangeRes.text().catch(() => 'Unknown error');
841
+ throw new Error(`OpenAI token exchange failed: HTTP ${exchangeRes.status} - ${errorText}`);
842
+ }
843
+
844
+ const exchangeData = await exchangeRes.json();
845
+ if (exchangeData.access_token) {
846
+ upsertEnvValue('OPENAI_CODEX_ACCESS_TOKEN', exchangeData.access_token);
847
+ if (exchangeData.refresh_token) {
848
+ upsertEnvValue('OPENAI_CODEX_REFRESH_TOKEN', exchangeData.refresh_token);
849
+ }
850
+ logOk('Successfully authenticated and saved OpenAI Codex tokens to .env');
851
+ logInfo('Applying updated provider credentials by restarting NeoAgent...');
852
+ cmdRestart();
853
+ } else {
854
+ throw new Error('OpenAI token exchange succeeded but did not return an access token.');
855
+ }
856
+ }
857
+ }
858
+
677
859
  function installDependencies() {
678
860
  heading('Dependencies');
679
861
  runOrThrow('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], {
@@ -867,6 +1049,11 @@ function cmdRestart() {
867
1049
  cmdStart();
868
1050
  }
869
1051
 
1052
+ async function cmdRebuildWeb() {
1053
+ heading(`Rebuild Flutter Web Client`);
1054
+ buildBundledWebClientIfPossible();
1055
+ }
1056
+
870
1057
  function cmdUninstall() {
871
1058
  heading(`Uninstall ${APP_NAME}`);
872
1059
  const platform = detectPlatform();
@@ -962,6 +1149,8 @@ function cmdUpdate(args = []) {
962
1149
  }
963
1150
  const versionBefore = currentInstalledVersionLabel();
964
1151
  let versionAfter = versionBefore;
1152
+ const githubInstallRef = releaseChannel === 'beta' ? '#beta' : '';
1153
+ const githubInstallSpec = `git+https://github.com/NeoLabs-Systems/NeoAgent.git${githubInstallRef}`;
965
1154
 
966
1155
  if (fs.existsSync(path.join(APP_DIR, '.git')) && commandExists('git')) {
967
1156
  const current = runQuiet('git', ['rev-parse', '--short', 'HEAD']);
@@ -982,17 +1171,16 @@ function cmdUpdate(args = []) {
982
1171
  buildBundledWebClientIfPossible();
983
1172
  }
984
1173
  } else {
985
- const npmTag = resolvePreferredNpmTag(releaseChannel);
986
- logWarn(`No git repo detected; attempting npm global update from ${npmTag}.`);
1174
+ logWarn(`No git repo detected; attempting npm global update from ${githubInstallSpec}.`);
987
1175
  if (commandExists('npm')) {
988
1176
  try {
989
1177
  backupRuntimeData();
990
- runOrThrow('npm', ['install', '-g', `neoagent@${npmTag}`, '--force'], {
1178
+ runOrThrow('npm', ['install', '-g', githubInstallSpec, '--force'], {
991
1179
  env: withInstallEnv()
992
1180
  });
993
- logOk('npm global update completed (forced reinstall)');
1181
+ logOk('npm global update completed (forced reinstall from GitHub)');
994
1182
  } catch {
995
- logWarn(`npm global update failed. Run: npm install -g neoagent@${npmTag} --force`);
1183
+ logWarn(`npm global update failed. Run: npm install -g ${githubInstallSpec} --force`);
996
1184
  }
997
1185
  } else {
998
1186
  logWarn('npm not found. Cannot perform global update.');
@@ -1062,7 +1250,8 @@ async function cmdEnv(args = []) {
1062
1250
  function printHelp() {
1063
1251
  console.log(`${APP_NAME} manager`);
1064
1252
  console.log('Usage: neoagent <command>');
1065
- console.log('Commands: install | setup | env | channel | update | restart | start | stop | status | logs | uninstall | migrate');
1253
+ console.log('Commands: install | setup | env | channel | update | restart | rebuild-web | start | stop | status | logs | uninstall | migrate | login');
1254
+ console.log('Login usage: neoagent login github-copilot | neoagent login openai-codex');
1066
1255
  console.log('Channel usage: neoagent channel | neoagent channel stable | neoagent channel beta');
1067
1256
  console.log('Update usage: neoagent update | neoagent update stable | neoagent update beta');
1068
1257
  console.log('Env usage: neoagent env list | neoagent env get PORT | neoagent env set PORT 3333 | neoagent env unset PORT');
@@ -1094,6 +1283,9 @@ async function runCLI(argv) {
1094
1283
  case 'restart':
1095
1284
  cmdRestart();
1096
1285
  break;
1286
+ case 'rebuild-web':
1287
+ await cmdRebuildWeb();
1288
+ break;
1097
1289
  case 'start':
1098
1290
  cmdStart();
1099
1291
  break;
@@ -1112,6 +1304,9 @@ async function runCLI(argv) {
1112
1304
  case 'migrate':
1113
1305
  await cmdMigrate(argv.slice(1));
1114
1306
  break;
1307
+ case 'login':
1308
+ await cmdLogin(argv.slice(1));
1309
+ break;
1115
1310
  case 'help':
1116
1311
  case '--help':
1117
1312
  case '-h':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.6",
3
+ "version": "2.3.1-beta.60",
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",
@@ -34,8 +35,8 @@
34
35
  "docs:dev": "docusaurus start",
35
36
  "docs:build": "docusaurus build",
36
37
  "docs:preview": "docusaurus serve build",
37
- "flutter:run:web": "cd flutter_app && flutter run -d chrome",
38
- "flutter:build:web": "cd flutter_app && flutter build web --output ../server/public --dart-define=NEOAGENT_BACKEND_URL=${NEOAGENT_BACKEND_URL:-}",
38
+ "flutter:run:web": "cd flutter_app && flutter run -d chrome --dart-define=NEOAGENT_WEB_BUILD_ID=$(node ../scripts/web_build_id.js)",
39
+ "flutter:build:web": "cd flutter_app && flutter build web --output ../server/public --dart-define=NEOAGENT_BACKEND_URL=${NEOAGENT_BACKEND_URL:-} --dart-define=NEOAGENT_WEB_BUILD_ID=$(node ../scripts/web_build_id.js)",
39
40
  "manage": "node bin/neoagent.js",
40
41
  "test": "node --test",
41
42
  "benchmark:tokens": "node scripts/benchmark-token-cost.js",
@@ -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",
@@ -24,7 +24,8 @@ db.exec(`
24
24
  password TEXT NOT NULL,
25
25
  password_login_enabled INTEGER DEFAULT 1,
26
26
  created_at TEXT DEFAULT (datetime('now')),
27
- last_login TEXT
27
+ last_login TEXT,
28
+ has_completed_onboarding INTEGER DEFAULT 0
28
29
  );
29
30
 
30
31
  CREATE TABLE IF NOT EXISTS agents (
@@ -272,6 +273,19 @@ db.exec(`
272
273
  FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
273
274
  );
274
275
 
276
+ CREATE TABLE IF NOT EXISTS integration_provider_configs (
277
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
278
+ user_id INTEGER NOT NULL,
279
+ agent_id TEXT,
280
+ provider_key TEXT NOT NULL,
281
+ config_json TEXT DEFAULT '{}',
282
+ created_at TEXT DEFAULT (datetime('now')),
283
+ updated_at TEXT DEFAULT (datetime('now')),
284
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
285
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
286
+ UNIQUE(user_id, agent_id, provider_key)
287
+ );
288
+
275
289
  CREATE TABLE IF NOT EXISTS browser_extension_pairing_requests (
276
290
  id TEXT PRIMARY KEY,
277
291
  user_id INTEGER,
@@ -875,6 +889,7 @@ function createAgentScopedIndexes() {
875
889
  const statements = [
876
890
  ['agent_runs', 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent ON agent_runs(user_id, agent_id, created_at DESC)'],
877
891
  ['integration_connections', 'CREATE INDEX IF NOT EXISTS idx_integration_connections_agent ON integration_connections(user_id, agent_id, provider_key, app_key)'],
892
+ ['integration_provider_configs', 'CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent ON integration_provider_configs(user_id, agent_id, provider_key)'],
878
893
  ['messages', 'CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(user_id, agent_id, created_at DESC)'],
879
894
  ['conversations', 'CREATE INDEX IF NOT EXISTS idx_conversations_agent ON conversations(user_id, agent_id, updated_at DESC)'],
880
895
  ['scheduled_tasks', 'CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_agent ON scheduled_tasks(user_id, agent_id)'],
@@ -922,6 +937,7 @@ function backfillAgentIds() {
922
937
  'platform_connections',
923
938
  'mcp_servers',
924
939
  'integration_connections',
940
+ 'integration_provider_configs',
925
941
  'integration_oauth_states',
926
942
  'scheduled_tasks',
927
943
  'conversations',
@@ -1303,6 +1319,83 @@ function migrateIntegrationOauthStatesTable() {
1303
1319
  `);
1304
1320
  }
1305
1321
 
1322
+ function migrateIntegrationProviderConfigsTable() {
1323
+ if (
1324
+ tableHasColumn('integration_provider_configs', 'agent_id') &&
1325
+ tableHasUniqueIndex('integration_provider_configs', ['user_id', 'agent_id', 'provider_key'])
1326
+ ) {
1327
+ const users = db.prepare('SELECT id FROM users').all();
1328
+ for (const user of users) {
1329
+ const agentId = getMainAgentId(user.id);
1330
+ db.prepare(
1331
+ 'UPDATE integration_provider_configs SET agent_id = ? WHERE user_id = ? AND agent_id IS NULL'
1332
+ ).run(agentId, user.id);
1333
+ }
1334
+ return;
1335
+ }
1336
+
1337
+ const rows = db
1338
+ .prepare('SELECT * FROM integration_provider_configs ORDER BY id ASC')
1339
+ .all();
1340
+
1341
+ db.exec('BEGIN');
1342
+ try {
1343
+ db.exec(`
1344
+ ALTER TABLE integration_provider_configs RENAME TO integration_provider_configs_legacy_agent_scope;
1345
+
1346
+ CREATE TABLE integration_provider_configs (
1347
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1348
+ user_id INTEGER NOT NULL,
1349
+ agent_id TEXT,
1350
+ provider_key TEXT NOT NULL,
1351
+ config_json TEXT DEFAULT '{}',
1352
+ created_at TEXT DEFAULT (datetime('now')),
1353
+ updated_at TEXT DEFAULT (datetime('now')),
1354
+ FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
1355
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
1356
+ UNIQUE(user_id, agent_id, provider_key)
1357
+ );
1358
+ `);
1359
+ const insert = db.prepare(`
1360
+ INSERT OR REPLACE INTO integration_provider_configs (
1361
+ id,
1362
+ user_id,
1363
+ agent_id,
1364
+ provider_key,
1365
+ config_json,
1366
+ created_at,
1367
+ updated_at
1368
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
1369
+ `);
1370
+
1371
+ for (const row of rows) {
1372
+ insert.run(
1373
+ row.id,
1374
+ row.user_id,
1375
+ row.agent_id || getMainAgentId(row.user_id),
1376
+ row.provider_key,
1377
+ row.config_json,
1378
+ row.created_at,
1379
+ row.updated_at,
1380
+ );
1381
+ }
1382
+
1383
+ db.exec(`
1384
+ DROP TABLE integration_provider_configs_legacy_agent_scope;
1385
+ CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent
1386
+ ON integration_provider_configs(user_id, agent_id, provider_key);
1387
+ `);
1388
+ db.exec('COMMIT');
1389
+ } catch (error) {
1390
+ try {
1391
+ db.exec('ROLLBACK');
1392
+ } catch {
1393
+ // Ignore rollback errors and rethrow the original migration failure.
1394
+ }
1395
+ throw error;
1396
+ }
1397
+ }
1398
+
1306
1399
  function migrateIntegrationSecretStorage() {
1307
1400
  try {
1308
1401
  const connectionRows = db
@@ -1335,6 +1428,22 @@ function migrateIntegrationSecretStorage() {
1335
1428
  } catch {
1336
1429
  // Preserve startup even if a row cannot be re-encrypted.
1337
1430
  }
1431
+
1432
+ try {
1433
+ const providerRows = db
1434
+ .prepare('SELECT id, config_json FROM integration_provider_configs')
1435
+ .all();
1436
+ const updateProviderConfig = db.prepare(
1437
+ 'UPDATE integration_provider_configs SET config_json = ? WHERE id = ?',
1438
+ );
1439
+ for (const row of providerRows) {
1440
+ const current = String(row.config_json || '');
1441
+ if (!current || isEncryptedValue(current)) continue;
1442
+ updateProviderConfig.run(encryptValue(current), row.id);
1443
+ }
1444
+ } catch {
1445
+ // Preserve startup even if a row cannot be re-encrypted.
1446
+ }
1338
1447
  }
1339
1448
 
1340
1449
  function backfillVerifiedAccountEmails() {
@@ -1408,11 +1517,26 @@ rebuildPlatformConnectionsForAgents();
1408
1517
  rebuildCoreMemoryForAgents();
1409
1518
  migrateIntegrationConnectionsTable();
1410
1519
  migrateIntegrationOauthStatesTable();
1520
+ migrateIntegrationProviderConfigsTable();
1411
1521
  createAgentScopedIndexes();
1522
+ backfillAgentIds();
1412
1523
  migrateIntegrationSecretStorage();
1413
1524
  backfillVerifiedAccountEmails();
1414
1525
  rebuildFtsForAgents();
1415
1526
 
1527
+ function migrateUsersOnboarding() {
1528
+ try {
1529
+ const columns = db.pragma('table_info(users)');
1530
+ const hasOnboardingCol = columns.some((c) => c.name === 'has_completed_onboarding');
1531
+ if (!hasOnboardingCol) {
1532
+ db.exec('ALTER TABLE users ADD COLUMN has_completed_onboarding INTEGER DEFAULT 0');
1533
+ }
1534
+ } catch (err) {
1535
+ console.warn('Could not add has_completed_onboarding column:', err.message);
1536
+ }
1537
+ }
1538
+ migrateUsersOnboarding();
1539
+
1416
1540
  try {
1417
1541
  db.exec(`
1418
1542
  INSERT OR REPLACE INTO conversation_history_fts(rowid, content, role, user_id, agent_id, agent_run_id)
@@ -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' }
@@ -7,6 +7,35 @@ const { DATA_DIR } = require('../../runtime/paths');
7
7
  const { requireAuth } = require('../middleware/auth');
8
8
 
9
9
  const FLUTTER_WEB_DIR = path.join(__dirname, '..', 'public');
10
+ const BUILD_ID_PATH = path.join(FLUTTER_WEB_DIR, '.last_build_id');
11
+
12
+ function setNoStoreHeaders(res) {
13
+ res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
14
+ res.set('Pragma', 'no-cache');
15
+ res.set('Expires', '0');
16
+ res.set('Surrogate-Control', 'no-store');
17
+ res.set('CDN-Cache-Control', 'no-store');
18
+ }
19
+
20
+ function readCurrentBuildId() {
21
+ try {
22
+ return fs.readFileSync(BUILD_ID_PATH, 'utf8').trim() || null;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ function setFlutterStaticHeaders(res, filePath) {
29
+ const relativePath = path.relative(FLUTTER_WEB_DIR, filePath).replaceAll('\\', '/');
30
+ if (
31
+ relativePath === 'index.html' ||
32
+ relativePath === 'flutter_bootstrap.js' ||
33
+ relativePath === 'flutter_service_worker.js' ||
34
+ relativePath === 'version.json'
35
+ ) {
36
+ setNoStoreHeaders(res);
37
+ }
38
+ }
10
39
 
11
40
  function registerStaticRoutes(app) {
12
41
  app.use(
@@ -27,7 +56,17 @@ function registerStaticRoutes(app) {
27
56
  express.static(path.join(DATA_DIR, 'screenshots'))
28
57
  );
29
58
 
30
- app.use(express.static(FLUTTER_WEB_DIR, { index: false }));
59
+ app.get('/app-build.json', (req, res) => {
60
+ setNoStoreHeaders(res);
61
+ res.json({
62
+ buildId: readCurrentBuildId(),
63
+ });
64
+ });
65
+
66
+ app.use(express.static(FLUTTER_WEB_DIR, {
67
+ index: false,
68
+ setHeaders: setFlutterStaticHeaders,
69
+ }));
31
70
  app.get(/^\/(?!api|screenshots|telnyx-audio).*/, serveFlutterApp);
32
71
  }
33
72
 
@@ -40,6 +79,7 @@ function serveFlutterApp(req, res) {
40
79
  'Flutter web build not found. Run "npm run flutter:build:web" to generate the bundled client.'
41
80
  );
42
81
  }
82
+ setNoStoreHeaders(res);
43
83
  return res.sendFile(entry);
44
84
  }
45
85
 
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
+ 5688ec90b02e934df4a3c734aec6f6ab