neoagent 2.3.1-beta.0 → 2.3.1-beta.100
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.
- package/.env.example +63 -5
- package/README.md +16 -5
- package/com.neoagent.plist +1 -1
- package/docs/automation.md +82 -41
- package/docs/capabilities.md +85 -89
- package/docs/configuration.md +89 -87
- package/docs/getting-started.md +46 -27
- package/docs/hardware.md +1 -1
- package/docs/index.md +24 -30
- package/docs/integrations.md +46 -60
- package/docs/migration.md +41 -146
- package/docs/operations.md +23 -20
- package/docs/skills.md +20 -20
- package/docs/why-neoagent.md +15 -13
- package/extensions/chrome-browser/background.mjs +10 -2
- package/flutter_app/.metadata +42 -0
- package/flutter_app/DESIGN_SYSTEM.md +166 -0
- package/flutter_app/README.md +21 -0
- package/flutter_app/analysis_options.yaml +32 -0
- package/flutter_app/android/app/build.gradle.kts +111 -0
- package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
- package/flutter_app/android/app/src/main/AndroidManifest.xml +185 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +888 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/auto/NeoAgentCarAppService.kt +114 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnection.kt +118 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnectionService.kt +86 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
- package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
- package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
- package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
- package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
- package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
- package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/flutter_app/android/app/src/main/res/values/arrays.xml +8 -0
- package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
- package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
- package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
- package/flutter_app/android/app/src/main/res/xml/automotive_app_desc.xml +4 -0
- package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
- package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
- package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
- package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
- package/flutter_app/android/build.gradle.kts +24 -0
- package/flutter_app/android/ci-release.keystore +0 -0
- package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/flutter_app/android/gradle.properties +3 -0
- package/flutter_app/android/key.properties +4 -0
- package/flutter_app/android/settings.gradle.kts +26 -0
- package/flutter_app/assets/branding/app_icon_1024.png +0 -0
- package/flutter_app/assets/branding/app_icon_128.png +0 -0
- package/flutter_app/assets/branding/app_icon_192.png +0 -0
- package/flutter_app/assets/branding/app_icon_256.png +0 -0
- package/flutter_app/assets/branding/app_icon_32.png +0 -0
- package/flutter_app/assets/branding/app_icon_512.png +0 -0
- package/flutter_app/assets/branding/app_icon_64.png +0 -0
- package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
- package/flutter_app/assets/branding/tray_icon_template.png +0 -0
- package/flutter_app/lib/features/location/location_service.dart +117 -0
- package/flutter_app/lib/features/notifications/notification_interceptor.dart +73 -0
- package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +571 -0
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +275 -0
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +366 -0
- package/flutter_app/lib/features/onboarding/onboarding_shell.dart +57 -0
- package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +218 -0
- package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +46 -0
- package/flutter_app/lib/main.dart +107 -0
- package/flutter_app/lib/main_account_settings.dart +1340 -0
- package/flutter_app/lib/main_admin.dart +900 -0
- package/flutter_app/lib/main_app_shell.dart +2162 -0
- package/flutter_app/lib/main_chat.dart +3902 -0
- package/flutter_app/lib/main_controller.dart +7229 -0
- package/flutter_app/lib/main_devices.dart +1738 -0
- package/flutter_app/lib/main_integrations.dart +917 -0
- package/flutter_app/lib/main_launcher.dart +959 -0
- package/flutter_app/lib/main_launcher_entry.dart +5 -0
- package/flutter_app/lib/main_models.dart +3807 -0
- package/flutter_app/lib/main_navigation.dart +206 -0
- package/flutter_app/lib/main_operations.dart +5249 -0
- package/flutter_app/lib/main_recordings.dart +920 -0
- package/flutter_app/lib/main_runtime.dart +869 -0
- package/flutter_app/lib/main_settings.dart +2288 -0
- package/flutter_app/lib/main_shared.dart +3393 -0
- package/flutter_app/lib/main_spacing.dart +33 -0
- package/flutter_app/lib/main_theme.dart +276 -0
- package/flutter_app/lib/main_unified.dart +388 -0
- package/flutter_app/lib/main_voice_assistant.dart +957 -0
- package/flutter_app/lib/src/analytics_service.dart +294 -0
- package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
- package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
- package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
- package/flutter_app/lib/src/android_app_installer.dart +22 -0
- package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
- package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
- package/flutter_app/lib/src/android_auto_bridge.dart +59 -0
- package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
- package/flutter_app/lib/src/app_launch_bridge.dart +58 -0
- package/flutter_app/lib/src/app_release_updater.dart +511 -0
- package/flutter_app/lib/src/backend_client.dart +1870 -0
- package/flutter_app/lib/src/desktop_companion.dart +2 -0
- package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
- package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
- package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
- package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
- package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
- package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
- package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
- package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
- package/flutter_app/lib/src/health_bridge.dart +136 -0
- package/flutter_app/lib/src/live_voice_capture.dart +85 -0
- package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
- package/flutter_app/lib/src/network/app_http_client.dart +53 -0
- package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
- package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
- package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
- package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
- package/flutter_app/lib/src/oauth_launcher.dart +33 -0
- package/flutter_app/lib/src/oauth_launcher_io.dart +95 -0
- package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
- package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
- package/flutter_app/lib/src/recording_bridge.dart +232 -0
- package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
- package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
- package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
- package/flutter_app/lib/src/recording_payloads.dart +86 -0
- package/flutter_app/lib/src/theme/palette.dart +81 -0
- package/flutter_app/lib/src/web_app_update_monitor.dart +17 -0
- package/flutter_app/lib/src/web_app_update_monitor_stub.dart +24 -0
- package/flutter_app/lib/src/web_app_update_monitor_web.dart +123 -0
- package/flutter_app/lib/src/widget_bridge.dart +49 -0
- package/flutter_app/linux/CMakeLists.txt +128 -0
- package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
- package/flutter_app/linux/flutter/generated_plugin_registrant.cc +47 -0
- package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/linux/flutter/generated_plugins.cmake +32 -0
- package/flutter_app/linux/runner/CMakeLists.txt +26 -0
- package/flutter_app/linux/runner/main.cc +6 -0
- package/flutter_app/linux/runner/my_application.cc +144 -0
- package/flutter_app/linux/runner/my_application.h +18 -0
- package/flutter_app/linux/runner/resources/app_icon.png +0 -0
- package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
- package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
- package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +48 -0
- package/flutter_app/macos/Podfile +42 -0
- package/flutter_app/macos/Podfile.lock +87 -0
- package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
- package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
- package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
- package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
- package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
- package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
- package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
- package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
- package/flutter_app/macos/Runner/Info.plist +36 -0
- package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
- package/flutter_app/macos/Runner/Release.entitlements +12 -0
- package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
- package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
- package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
- package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
- package/flutter_app/patch_strings.py +12 -0
- package/flutter_app/pubspec.lock +1256 -0
- package/flutter_app/pubspec.yaml +59 -0
- package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
- package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
- package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
- package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
- package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
- package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
- package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
- package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
- package/flutter_app/tool/generate_desktop_branding.py +219 -0
- package/flutter_app/web/favicon.png +0 -0
- package/flutter_app/web/favicon.svg +12 -0
- package/flutter_app/web/icons/Icon-192.png +0 -0
- package/flutter_app/web/icons/Icon-512.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
- package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
- package/flutter_app/web/index.html +39 -0
- package/flutter_app/web/manifest.json +35 -0
- package/flutter_app/windows/CMakeLists.txt +108 -0
- package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.cc +50 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/windows/flutter/generated_plugins.cmake +36 -0
- package/flutter_app/windows/runner/CMakeLists.txt +41 -0
- package/flutter_app/windows/runner/Runner.rc +121 -0
- package/flutter_app/windows/runner/flutter_window.cpp +533 -0
- package/flutter_app/windows/runner/flutter_window.h +37 -0
- package/flutter_app/windows/runner/main.cpp +53 -0
- package/flutter_app/windows/runner/resource.h +16 -0
- package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
- package/flutter_app/windows/runner/runner.exe.manifest +14 -0
- package/flutter_app/windows/runner/utils.cpp +65 -0
- package/flutter_app/windows/runner/utils.h +19 -0
- package/flutter_app/windows/runner/win32_window.cpp +299 -0
- package/flutter_app/windows/runner/win32_window.h +102 -0
- package/lib/install_helpers.js +31 -0
- package/lib/manager.js +465 -43
- package/package.json +11 -3
- package/runtime/paths.js +137 -0
- package/server/config/analytics.js +30 -0
- package/server/db/database.js +363 -21
- package/server/guest-agent.android.package.json +13 -0
- package/server/guest-agent.browser.package.json +14 -0
- package/server/guest_agent.js +98 -52
- package/server/http/middleware.js +55 -2
- package/server/http/routes.js +6 -1
- package/server/http/static.js +41 -1
- package/server/index.js +16 -16
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/AssetManifest.bin +1 -1
- package/server/public/assets/AssetManifest.bin.json +1 -1
- package/server/public/assets/AssetManifest.json +1 -1
- package/server/public/assets/NOTICES +679 -129
- package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/assets/packages/mixpanel_flutter/assets/mixpanel.js +3 -0
- package/server/public/canvaskit/wimp.wasm +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +95866 -87333
- package/server/routes/account.js +26 -1
- package/server/routes/agents.js +6 -2
- package/server/routes/android.js +22 -6
- package/server/routes/auth.js +25 -5
- package/server/routes/browser.js +47 -13
- package/server/routes/integrations.js +119 -9
- package/server/routes/memory.js +179 -8
- package/server/routes/runtime.js +14 -0
- package/server/routes/screenHistory.js +46 -0
- package/server/routes/settings.js +102 -24
- package/server/routes/skills.js +12 -0
- package/server/routes/social_video.js +66 -0
- package/server/routes/tasks.js +22 -2
- package/server/routes/triggers.js +90 -0
- package/server/routes/wearable.js +67 -0
- package/server/routes/widgets.js +4 -4
- package/server/services/ai/capabilityHealth.js +23 -51
- package/server/services/ai/compaction.js +1 -1
- package/server/services/ai/deliverables/artifact_helpers.js +190 -0
- package/server/services/ai/deliverables/contracts.js +113 -0
- package/server/services/ai/deliverables/index.js +20 -0
- package/server/services/ai/deliverables/selector.js +94 -0
- package/server/services/ai/deliverables/validator.js +63 -0
- package/server/services/ai/deliverables/workflows.js +207 -0
- package/server/services/ai/engine.js +372 -27
- package/server/services/ai/hooks.js +127 -0
- package/server/services/ai/integrated_tools/index.js +98 -0
- package/server/services/ai/integrated_tools/remotion.js +373 -0
- package/server/services/ai/integrated_tools/shared.js +204 -0
- package/server/services/ai/integrated_tools/slidev.js +207 -0
- package/server/services/ai/loopPolicy.js +146 -0
- package/server/services/ai/models.js +36 -0
- package/server/services/ai/providers/githubCopilot.js +97 -0
- package/server/services/ai/providers/openai.js +2 -1
- package/server/services/ai/providers/openaiCodex.js +384 -0
- package/server/services/ai/recordingInsights.js +11 -13
- package/server/services/ai/runEvents.js +100 -0
- package/server/services/ai/settings.js +20 -0
- package/server/services/ai/systemPrompt.js +16 -2
- package/server/services/ai/taskAnalysis.js +2 -0
- package/server/services/ai/toolResult.js +20 -0
- package/server/services/ai/toolRunner.js +46 -6
- package/server/services/ai/toolSelector.js +54 -1
- package/server/services/ai/tools.js +360 -127
- package/server/services/android/android_bootstrap_worker.js +63 -0
- package/server/services/android/controller.js +442 -1760
- package/server/services/bootstrap_helpers.js +0 -34
- package/server/services/browser/controller.js +326 -52
- package/server/services/cli/executor.js +40 -1
- package/server/services/desktop/screenRecorder.js +182 -0
- package/server/services/integrations/env.js +10 -0
- package/server/services/integrations/figma/provider.js +1 -0
- package/server/services/integrations/github/common.js +106 -0
- package/server/services/integrations/github/provider.js +499 -0
- package/server/services/integrations/github/repos.js +1124 -0
- package/server/services/integrations/google/provider.js +1 -0
- package/server/services/integrations/manager.js +88 -12
- package/server/services/integrations/microsoft/provider.js +1 -0
- package/server/services/integrations/oauth_provider.js +25 -8
- package/server/services/integrations/provider_config_store.js +85 -0
- package/server/services/integrations/registry.js +8 -2
- package/server/services/integrations/spotify/provider.js +488 -0
- package/server/services/integrations/trello/provider.js +842 -0
- package/server/services/integrations/weather/provider.js +559 -0
- package/server/services/integrations/whatsapp/provider.js +6 -2
- package/server/services/manager.js +109 -154
- package/server/services/mcp/client.js +120 -23
- package/server/services/memory/llm_transfer.js +218 -0
- package/server/services/memory/manager.js +299 -35
- package/server/services/messaging/access_policy.js +10 -0
- package/server/services/messaging/automation.js +1 -1
- package/server/services/messaging/manager.js +93 -8
- package/server/services/messaging/meshtastic.js +277 -0
- package/server/services/messaging/meshtastic_env.js +40 -0
- package/server/services/messaging/meshtastic_protocol.js +530 -0
- package/server/services/messaging/meshtastic_tcp_transport.js +25 -0
- package/server/services/runtime/backends/local-vm.js +221 -158
- package/server/services/runtime/docker-vm-manager.js +392 -0
- package/server/services/runtime/guest_bootstrap.js +654 -0
- package/server/services/runtime/manager.js +85 -29
- package/server/services/runtime/settings.js +22 -31
- package/server/services/runtime/validation.js +16 -31
- package/server/services/skills/base_catalog.js +33 -0
- package/server/services/social_video/adapters/base.js +26 -0
- package/server/services/social_video/adapters/index.js +27 -0
- package/server/services/social_video/adapters/instagram.js +17 -0
- package/server/services/social_video/adapters/tiktok.js +17 -0
- package/server/services/social_video/adapters/x.js +17 -0
- package/server/services/social_video/adapters/youtube.js +17 -0
- package/server/services/social_video/captions.js +187 -0
- package/server/services/social_video/frame.js +42 -0
- package/server/services/social_video/index.js +7 -0
- package/server/services/social_video/metadata.js +63 -0
- package/server/services/social_video/result.js +63 -0
- package/server/services/social_video/service.js +720 -0
- package/server/services/social_video/url.js +83 -0
- package/server/services/tasks/adapters/index.js +2 -0
- package/server/services/tasks/adapters/manual.js +12 -0
- package/server/services/tasks/adapters/schedule.js +33 -5
- package/server/services/tasks/adapters/weather_event.js +84 -0
- package/server/services/tasks/integration_runtime.js +86 -1
- package/server/services/tasks/runtime.js +3 -3
- package/server/services/tasks/task_repository.js +3 -6
- package/server/services/voice/agentBridge.js +20 -4
- package/server/services/voice/message.js +3 -0
- package/server/services/voice/openaiClient.js +4 -1
- package/server/services/voice/openaiSpeech.js +6 -1
- package/server/services/voice/providers.js +144 -15
- package/server/services/voice/runtime.js +8 -8
- package/server/services/voice/runtimeManager.js +292 -20
- package/server/services/voice/turnRunner.js +29 -9
- package/server/services/wearable/firmware_manifest.js +353 -0
- package/server/services/wearable/gateway.js +350 -0
- package/server/services/wearable/protocol.js +45 -0
- package/server/services/wearable/service.js +240 -0
- package/server/services/websocket.js +3 -1
- package/server/services/widgets/focus_widget.js +137 -0
- package/server/services/widgets/service.js +182 -6
- package/server/services/workspace/manager.js +301 -0
- package/server/utils/deployment.js +16 -9
- package/server/utils/local_secrets.js +56 -0
- package/server/utils/logger.js +37 -9
- package/server/services/integrations/home_assistant/provider.js +0 -350
- package/server/services/messaging/access_policy.test.js +0 -228
- package/server/services/runtime/backends/host.js +0 -37
- package/server/services/runtime/qemu.js +0 -391
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
const db = require('../db/database');
|
|
4
4
|
const { MemoryManager } = require('./memory/manager');
|
|
5
5
|
const { MCPClient } = require('./mcp/client');
|
|
6
|
-
const { BrowserController } = require('./browser/controller');
|
|
7
|
-
const { AndroidController } = require('./android/controller');
|
|
8
6
|
const { AgentEngine } = require('./ai/engine');
|
|
9
7
|
const { MultiStepOrchestrator } = require('./ai/multiStep');
|
|
10
8
|
const { SkillRunner } = require('./ai/toolRunner');
|
|
@@ -15,19 +13,21 @@ const { WidgetService } = require('./widgets/service');
|
|
|
15
13
|
const { setupWebSocket } = require('./websocket');
|
|
16
14
|
const { registerMessagingAutomation } = require('./messaging/automation');
|
|
17
15
|
const { RecordingManager } = require('./recordings/manager');
|
|
16
|
+
const { SocialVideoService } = require('./social_video');
|
|
18
17
|
const { VoiceRuntimeManager } = require('./voice/runtimeManager');
|
|
19
|
-
const { CLIExecutor } = require('./cli/executor');
|
|
20
18
|
const { AuthProviderManager } = require('./account/auth_provider_manager');
|
|
21
19
|
const { IntegrationManager } = require('./integrations/manager');
|
|
22
20
|
const { ArtifactStore } = require('./artifacts/store');
|
|
23
21
|
const { RuntimeManager } = require('./runtime/manager');
|
|
22
|
+
const { WorkspaceManager } = require('./workspace/manager');
|
|
24
23
|
const { BrowserExtensionRegistry } = require('./browser/extension/registry');
|
|
25
24
|
const { DesktopCompanionRegistry } = require('./desktop/registry');
|
|
26
25
|
const { DesktopProvider } = require('./desktop/provider');
|
|
27
|
-
const {
|
|
26
|
+
const { ScreenRecorder } = require('./desktop/screenRecorder');
|
|
27
|
+
const { WearableService } = require('./wearable/service');
|
|
28
|
+
const { getRuntimeValidation } = require('./runtime/validation');
|
|
28
29
|
const {
|
|
29
30
|
getErrorMessage,
|
|
30
|
-
restoreBrowserHeadlessPreference,
|
|
31
31
|
runBackgroundTask,
|
|
32
32
|
} = require('./bootstrap_helpers');
|
|
33
33
|
|
|
@@ -40,18 +40,18 @@ function logServiceReady(message) {
|
|
|
40
40
|
console.log(`[Services] ${message}`);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
function createCliExecutor(app) {
|
|
44
|
-
const cliExecutor = registerLocal(app, 'cliExecutor', new CLIExecutor());
|
|
45
|
-
logServiceReady('CLI executor ready');
|
|
46
|
-
return cliExecutor;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
43
|
function createArtifactStore(app) {
|
|
50
44
|
const artifactStore = registerLocal(app, 'artifactStore', new ArtifactStore());
|
|
51
45
|
logServiceReady('Artifact store ready');
|
|
52
46
|
return artifactStore;
|
|
53
47
|
}
|
|
54
48
|
|
|
49
|
+
function createWorkspaceManager(app) {
|
|
50
|
+
const workspaceManager = registerLocal(app, 'workspaceManager', new WorkspaceManager());
|
|
51
|
+
logServiceReady('Workspace manager ready');
|
|
52
|
+
return workspaceManager;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
55
|
function createBrowserExtensionRegistry(app) {
|
|
56
56
|
const registry = registerLocal(app, 'browserExtensionRegistry', new BrowserExtensionRegistry());
|
|
57
57
|
logServiceReady('Browser extension registry ready');
|
|
@@ -197,131 +197,32 @@ function createUserScopedControllerPool(app, {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
function createBrowserController(app, artifactStore) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const row = db
|
|
203
|
-
.prepare('SELECT value FROM user_settings WHERE user_id = ? AND key = ?')
|
|
204
|
-
.get(userId, 'headless_browser');
|
|
205
|
-
if (!row) return true;
|
|
206
|
-
return row.value !== 'false' && row.value !== false && row.value !== '0';
|
|
207
|
-
} catch (err) {
|
|
208
|
-
console.warn('[Services] Failed to read user headless_browser setting, defaulting to true:', getErrorMessage(err));
|
|
209
|
-
return true;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
createUserScopedControllerPool(app, {
|
|
214
|
-
controllersKey: 'browserControllers',
|
|
215
|
-
creationPromisesKey: 'browserControllerCreationPromises',
|
|
216
|
-
lastAccessKey: 'browserControllerLastAccess',
|
|
217
|
-
resolverKey: 'getBrowserControllerForUser',
|
|
218
|
-
defaultControllerKey: 'browserController',
|
|
219
|
-
createController: async (userId) => {
|
|
220
|
-
const controller = new BrowserController({
|
|
221
|
-
userId,
|
|
222
|
-
artifactStore,
|
|
223
|
-
runtimeBackend: 'host',
|
|
224
|
-
});
|
|
225
|
-
controller.headless = getUserHeadlessPreference(userId);
|
|
226
|
-
return controller;
|
|
227
|
-
},
|
|
228
|
-
closeController: async (controller) => {
|
|
229
|
-
if (typeof controller.closeBrowser === 'function') {
|
|
230
|
-
await controller.closeBrowser();
|
|
231
|
-
}
|
|
232
|
-
},
|
|
233
|
-
closeErrorLabel: '[Browser] Failed to close stale browser controller',
|
|
200
|
+
registerLocal(app, 'getBrowserControllerForUser', async () => {
|
|
201
|
+
throw new Error('Host browser controller is disabled. Use the VM browser backend or a paired extension.');
|
|
234
202
|
});
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
'browserController',
|
|
239
|
-
new BrowserController({
|
|
240
|
-
artifactStore,
|
|
241
|
-
runtimeBackend: 'host',
|
|
242
|
-
}),
|
|
243
|
-
);
|
|
244
|
-
const { restored, userCount, headless } = restoreBrowserHeadlessPreference(
|
|
245
|
-
browserController,
|
|
246
|
-
db,
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
if (restored) {
|
|
250
|
-
logServiceReady(`Browser headless setting restored to ${headless}`);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
logServiceReady(`Browser controller ready for ${userCount} user(s)`);
|
|
254
|
-
return browserController;
|
|
203
|
+
registerLocal(app, 'browserController', null);
|
|
204
|
+
logServiceReady('Browser controller disabled in VM-only mode');
|
|
205
|
+
return null;
|
|
255
206
|
}
|
|
256
207
|
|
|
257
|
-
function
|
|
258
|
-
createUserScopedControllerPool(app, {
|
|
259
|
-
controllersKey: 'androidControllers',
|
|
260
|
-
creationPromisesKey: 'androidControllerCreationPromises',
|
|
261
|
-
lastAccessKey: 'androidControllerLastAccess',
|
|
262
|
-
resolverKey: 'getAndroidControllerForUser',
|
|
263
|
-
defaultControllerKey: 'androidController',
|
|
264
|
-
createController: async (userId) => {
|
|
265
|
-
const controller = new AndroidController({
|
|
266
|
-
userId,
|
|
267
|
-
artifactStore,
|
|
268
|
-
runtimeBackend: 'host',
|
|
269
|
-
});
|
|
270
|
-
return controller;
|
|
271
|
-
},
|
|
272
|
-
closeController: async (controller) => {
|
|
273
|
-
if (typeof controller.close === 'function') {
|
|
274
|
-
await controller.close();
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
closeErrorLabel: '[Android] Failed to close stale Android controller',
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
const androidController = registerLocal(
|
|
281
|
-
app,
|
|
282
|
-
'androidController',
|
|
283
|
-
new AndroidController({
|
|
284
|
-
artifactStore,
|
|
285
|
-
runtimeBackend: 'host',
|
|
286
|
-
}),
|
|
287
|
-
);
|
|
288
|
-
logServiceReady('Android controller ready');
|
|
289
|
-
return androidController;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function createRuntimeManager(app, cliExecutor) {
|
|
208
|
+
function createRuntimeManager(app) {
|
|
293
209
|
const runtimeManager = registerLocal(
|
|
294
210
|
app,
|
|
295
211
|
'runtimeManager',
|
|
296
212
|
new RuntimeManager({
|
|
297
|
-
cliExecutor,
|
|
298
213
|
artifactStore: app.locals.artifactStore,
|
|
299
214
|
browserExtensionRegistry: app.locals.browserExtensionRegistry,
|
|
300
|
-
getHostBrowserProvider: (userId) => {
|
|
301
|
-
const resolver = app.locals.getBrowserControllerForUser;
|
|
302
|
-
if (typeof resolver === 'function') {
|
|
303
|
-
return resolver(userId);
|
|
304
|
-
}
|
|
305
|
-
return app.locals.browserController;
|
|
306
|
-
},
|
|
307
|
-
getHostAndroidProvider: (userId) => {
|
|
308
|
-
const resolver = app.locals.getAndroidControllerForUser;
|
|
309
|
-
if (typeof resolver === 'function') {
|
|
310
|
-
return resolver(userId);
|
|
311
|
-
}
|
|
312
|
-
return app.locals.androidController;
|
|
313
|
-
},
|
|
314
215
|
}),
|
|
315
216
|
);
|
|
316
217
|
logServiceReady('Runtime manager ready');
|
|
317
218
|
return runtimeManager;
|
|
318
219
|
}
|
|
319
220
|
|
|
320
|
-
async function createSkillRunner(app,
|
|
221
|
+
async function createSkillRunner(app, runtimeManager) {
|
|
321
222
|
const skillRunner = registerLocal(
|
|
322
223
|
app,
|
|
323
224
|
'skillRunner',
|
|
324
|
-
new SkillRunner({
|
|
225
|
+
new SkillRunner({ runtimeManager }),
|
|
325
226
|
);
|
|
326
227
|
await skillRunner.loadSkills();
|
|
327
228
|
logServiceReady('Skills loaded');
|
|
@@ -332,13 +233,13 @@ function createAgentEngine(
|
|
|
332
233
|
app,
|
|
333
234
|
io,
|
|
334
235
|
{
|
|
335
|
-
cliExecutor,
|
|
336
236
|
memoryManager,
|
|
337
237
|
mcpClient,
|
|
338
238
|
browserController,
|
|
339
239
|
androidController,
|
|
340
240
|
runtimeManager,
|
|
341
241
|
skillRunner,
|
|
242
|
+
workspaceManager,
|
|
342
243
|
},
|
|
343
244
|
) {
|
|
344
245
|
const agentEngine = registerLocal(
|
|
@@ -346,12 +247,12 @@ function createAgentEngine(
|
|
|
346
247
|
'agentEngine',
|
|
347
248
|
new AgentEngine(io, {
|
|
348
249
|
app,
|
|
349
|
-
cliExecutor,
|
|
350
250
|
memoryManager,
|
|
351
251
|
mcpClient,
|
|
352
252
|
browserController,
|
|
353
253
|
androidController,
|
|
354
254
|
runtimeManager,
|
|
255
|
+
workspaceManager,
|
|
355
256
|
messagingManager: null,
|
|
356
257
|
skillRunner,
|
|
357
258
|
}),
|
|
@@ -418,6 +319,19 @@ function createRecordingManager(app, io) {
|
|
|
418
319
|
return recordingManager;
|
|
419
320
|
}
|
|
420
321
|
|
|
322
|
+
function createSocialVideoService(app) {
|
|
323
|
+
const socialVideoService = registerLocal(
|
|
324
|
+
app,
|
|
325
|
+
'socialVideoService',
|
|
326
|
+
new SocialVideoService({
|
|
327
|
+
artifactStore: app.locals.artifactStore,
|
|
328
|
+
runtimeManager: app.locals.runtimeManager,
|
|
329
|
+
}),
|
|
330
|
+
);
|
|
331
|
+
logServiceReady('Social video service ready');
|
|
332
|
+
return socialVideoService;
|
|
333
|
+
}
|
|
334
|
+
|
|
421
335
|
function createWidgetService(app) {
|
|
422
336
|
const widgetService = registerLocal(
|
|
423
337
|
app,
|
|
@@ -428,6 +342,52 @@ function createWidgetService(app) {
|
|
|
428
342
|
return widgetService;
|
|
429
343
|
}
|
|
430
344
|
|
|
345
|
+
function createWearableService(app) {
|
|
346
|
+
const wearableService = registerLocal(
|
|
347
|
+
app,
|
|
348
|
+
'wearableService',
|
|
349
|
+
new WearableService({ app }),
|
|
350
|
+
);
|
|
351
|
+
logServiceReady('Wearable service ready');
|
|
352
|
+
return wearableService;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function createScreenRecorder(app) {
|
|
356
|
+
const hasActiveRemoteCaptureSession = () => {
|
|
357
|
+
const desktopRegistry = app.locals.desktopCompanionRegistry;
|
|
358
|
+
if (desktopRegistry?.connectionsByUser instanceof Map) {
|
|
359
|
+
for (const userMap of desktopRegistry.connectionsByUser.values()) {
|
|
360
|
+
if (!(userMap instanceof Map)) continue;
|
|
361
|
+
for (const connection of userMap.values()) {
|
|
362
|
+
if (typeof connection?.isOpen === 'function' && connection.isOpen()) {
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const extensionRegistry = app.locals.browserExtensionRegistry;
|
|
370
|
+
if (extensionRegistry?.connectionsByUser instanceof Map) {
|
|
371
|
+
for (const connection of extensionRegistry.connectionsByUser.values()) {
|
|
372
|
+
if (typeof connection?.isOpen === 'function' && connection.isOpen()) {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return false;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const screenRecorder = registerLocal(
|
|
382
|
+
app,
|
|
383
|
+
'screenRecorder',
|
|
384
|
+
new ScreenRecorder({ hasActiveRemoteCaptureSession }),
|
|
385
|
+
);
|
|
386
|
+
screenRecorder.start();
|
|
387
|
+
logServiceReady('Screen recorder started');
|
|
388
|
+
return screenRecorder;
|
|
389
|
+
}
|
|
390
|
+
|
|
431
391
|
function restoreMessagingConnections(messagingManager) {
|
|
432
392
|
void runBackgroundTask('[Messaging] Restore error:', () =>
|
|
433
393
|
messagingManager.restoreConnections(),
|
|
@@ -479,8 +439,8 @@ async function startServices(app, io) {
|
|
|
479
439
|
console.log('[Services] Starting service initialization');
|
|
480
440
|
|
|
481
441
|
try {
|
|
482
|
-
const cliExecutor = createCliExecutor(app);
|
|
483
442
|
const artifactStore = createArtifactStore(app);
|
|
443
|
+
createWorkspaceManager(app);
|
|
484
444
|
createBrowserExtensionRegistry(app);
|
|
485
445
|
createDesktopCompanionRegistry(app);
|
|
486
446
|
const memoryManager = createMemoryManager(app);
|
|
@@ -488,19 +448,21 @@ async function startServices(app, io) {
|
|
|
488
448
|
createAuthProviderManager(app);
|
|
489
449
|
const integrationManager = createIntegrationManager(app);
|
|
490
450
|
const browserController = createBrowserController(app, artifactStore);
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
registerLocal(app, 'runtimeValidation',
|
|
494
|
-
|
|
495
|
-
|
|
451
|
+
const runtimeManager = createRuntimeManager(app);
|
|
452
|
+
const runtimeValidation = getRuntimeValidation(runtimeManager);
|
|
453
|
+
registerLocal(app, 'runtimeValidation', runtimeValidation);
|
|
454
|
+
if (!runtimeValidation.ready) {
|
|
455
|
+
console.warn('[Services] Runtime validation is degraded:', runtimeValidation.issues.join(' '));
|
|
456
|
+
}
|
|
457
|
+
const skillRunner = await createSkillRunner(app, runtimeManager);
|
|
496
458
|
const agentEngine = createAgentEngine(app, io, {
|
|
497
|
-
cliExecutor,
|
|
498
459
|
memoryManager,
|
|
499
460
|
mcpClient,
|
|
500
461
|
browserController,
|
|
501
|
-
androidController,
|
|
462
|
+
androidController: null,
|
|
502
463
|
runtimeManager,
|
|
503
464
|
skillRunner,
|
|
465
|
+
workspaceManager: app.locals.workspaceManager,
|
|
504
466
|
});
|
|
505
467
|
|
|
506
468
|
createMultiStep(app, agentEngine, io);
|
|
@@ -512,7 +474,10 @@ async function startServices(app, io) {
|
|
|
512
474
|
|
|
513
475
|
const messagingManager = createMessagingManager(app, io, agentEngine);
|
|
514
476
|
const recordingManager = createRecordingManager(app, io);
|
|
477
|
+
createSocialVideoService(app);
|
|
515
478
|
createWidgetService(app);
|
|
479
|
+
createWearableService(app);
|
|
480
|
+
createScreenRecorder(app);
|
|
516
481
|
|
|
517
482
|
restoreMessagingConnections(messagingManager);
|
|
518
483
|
restoreMcpClients(mcpClient);
|
|
@@ -576,29 +541,19 @@ async function stopServices(app) {
|
|
|
576
541
|
);
|
|
577
542
|
}
|
|
578
543
|
|
|
579
|
-
if (app.locals.
|
|
580
|
-
for (const controller of app.locals.browserControllers.values()) {
|
|
581
|
-
tasks.push(
|
|
582
|
-
controller.closeBrowser().catch((err) => {
|
|
583
|
-
console.error('[Browser] User-scoped shutdown error:', getErrorMessage(err));
|
|
584
|
-
}),
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
if (app.locals.androidController) {
|
|
544
|
+
if (app.locals.wearableGateway?.close) {
|
|
590
545
|
tasks.push(
|
|
591
|
-
app.locals.
|
|
592
|
-
console.error('[
|
|
546
|
+
app.locals.wearableGateway.close().catch((err) => {
|
|
547
|
+
console.error('[WearableGateway] Shutdown error:', getErrorMessage(err));
|
|
593
548
|
}),
|
|
594
549
|
);
|
|
595
550
|
}
|
|
596
551
|
|
|
597
|
-
if (app.locals.
|
|
598
|
-
for (const controller of app.locals.
|
|
552
|
+
if (app.locals.browserControllers instanceof Map) {
|
|
553
|
+
for (const controller of app.locals.browserControllers.values()) {
|
|
599
554
|
tasks.push(
|
|
600
|
-
controller.
|
|
601
|
-
console.error('[
|
|
555
|
+
controller.closeBrowser().catch((err) => {
|
|
556
|
+
console.error('[Browser] User-scoped shutdown error:', getErrorMessage(err));
|
|
602
557
|
}),
|
|
603
558
|
);
|
|
604
559
|
}
|
|
@@ -639,6 +594,15 @@ async function stopServices(app) {
|
|
|
639
594
|
}
|
|
640
595
|
}
|
|
641
596
|
|
|
597
|
+
if (app.locals.screenRecorder) {
|
|
598
|
+
try {
|
|
599
|
+
app.locals.screenRecorder.stop();
|
|
600
|
+
logServiceReady('Screen recorder stopped');
|
|
601
|
+
} catch (err) {
|
|
602
|
+
console.error('[ScreenRecorder] Stop error:', getErrorMessage(err));
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
642
606
|
if (app.locals.browserExtensionRegistry) {
|
|
643
607
|
try {
|
|
644
608
|
app.locals.browserExtensionRegistry.closeAll();
|
|
@@ -657,15 +621,6 @@ async function stopServices(app) {
|
|
|
657
621
|
}
|
|
658
622
|
}
|
|
659
623
|
|
|
660
|
-
if (app.locals.cliExecutor) {
|
|
661
|
-
try {
|
|
662
|
-
app.locals.cliExecutor.killAll('shutdown');
|
|
663
|
-
logServiceReady('CLI executor processes terminated');
|
|
664
|
-
} catch (err) {
|
|
665
|
-
console.error('[CLI] Shutdown error:', getErrorMessage(err));
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
|
|
669
624
|
await Promise.allSettled(tasks);
|
|
670
625
|
logServiceReady('Shutdown tasks settled');
|
|
671
626
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
1
3
|
const EventEmitter = require('events');
|
|
2
4
|
const crypto = require('crypto');
|
|
3
5
|
const db = require('../../db/database');
|
|
@@ -5,6 +7,9 @@ const { Client } = require('@modelcontextprotocol/sdk/client/index.js');
|
|
|
5
7
|
const { SSEClientTransport } = require('@modelcontextprotocol/sdk/client/sse.js');
|
|
6
8
|
const { validateRemoteMcpEndpoint } = require('../runtime/mcp');
|
|
7
9
|
|
|
10
|
+
const CONSECUTIVE_FAIL_LIMIT = 3;
|
|
11
|
+
const RECONNECT_DELAY_MS = 60_000;
|
|
12
|
+
|
|
8
13
|
class DBAuthProvider {
|
|
9
14
|
constructor(serverId, clientId, authServerUrl) {
|
|
10
15
|
this.serverId = serverId;
|
|
@@ -50,7 +55,6 @@ class DBAuthProvider {
|
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
redirectToAuthorization(authorizationUrl) {
|
|
53
|
-
// Throw error so the API route catches it and returns the URL to the frontend
|
|
54
58
|
throw new Error(`OAUTH_REDIRECT:${authorizationUrl.toString()}`);
|
|
55
59
|
}
|
|
56
60
|
|
|
@@ -66,10 +70,30 @@ class DBAuthProvider {
|
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
function extractErrorMessage(err) {
|
|
74
|
+
const raw = err?.message || String(err || 'Unknown error');
|
|
75
|
+
// Strip HTML bodies (e.g. Cloudflare 530 error pages) — keep only the first line
|
|
76
|
+
if (raw.includes('<!doctype') || raw.includes('<html') || raw.includes('<!DOCTYPE')) {
|
|
77
|
+
const httpMatch = raw.match(/HTTP (\d+)/i);
|
|
78
|
+
return httpMatch
|
|
79
|
+
? `Server returned HTTP ${httpMatch[1]} — the MCP endpoint may be down or misconfigured`
|
|
80
|
+
: 'Server returned an HTML error page — the MCP endpoint may be down or misconfigured';
|
|
81
|
+
}
|
|
82
|
+
// ECONNREFUSED: pull out just the host/port
|
|
83
|
+
if (err?.code === 'ECONNREFUSED' || raw.includes('ECONNREFUSED')) {
|
|
84
|
+
const addrMatch = raw.match(/connect ECONNREFUSED ([^\s,]+)/);
|
|
85
|
+
return addrMatch
|
|
86
|
+
? `Connection refused at ${addrMatch[1]} — is the MCP server running?`
|
|
87
|
+
: 'Connection refused — the MCP server is not reachable';
|
|
88
|
+
}
|
|
89
|
+
return raw.split('\n')[0].trim();
|
|
90
|
+
}
|
|
91
|
+
|
|
69
92
|
class MCPClient extends EventEmitter {
|
|
70
93
|
constructor() {
|
|
71
94
|
super();
|
|
72
95
|
this.servers = new Map();
|
|
96
|
+
this._reconnectTimers = new Map();
|
|
73
97
|
}
|
|
74
98
|
|
|
75
99
|
async startServer(serverId, url, name = '', userId = null, options = {}) {
|
|
@@ -90,13 +114,12 @@ class MCPClient extends EventEmitter {
|
|
|
90
114
|
|
|
91
115
|
const transportOpts = {
|
|
92
116
|
requestInit: { headers: {} },
|
|
93
|
-
eventSourceInit: { headers: {} }
|
|
117
|
+
eventSourceInit: { headers: {} },
|
|
94
118
|
};
|
|
95
119
|
|
|
96
120
|
if (authObj.type === 'bearer' && authObj.token) {
|
|
97
121
|
const h = `Bearer ${authObj.token}`;
|
|
98
122
|
transportOpts.requestInit.headers['Authorization'] = h;
|
|
99
|
-
// Native EventSource doesn't support headers well in browsers, but Node.js EventSource / sse.js might
|
|
100
123
|
transportOpts.eventSourceInit.headers['Authorization'] = h;
|
|
101
124
|
} else if (authObj.type === 'oauth') {
|
|
102
125
|
transportOpts.authProvider = new DBAuthProvider(serverId, authObj.clientId, authObj.authServerUrl);
|
|
@@ -105,7 +128,7 @@ class MCPClient extends EventEmitter {
|
|
|
105
128
|
const transport = new SSEClientTransport(new URL(endpoint), transportOpts);
|
|
106
129
|
const client = new Client(
|
|
107
130
|
{ name: 'NeoAgent', version: '1.0.0' },
|
|
108
|
-
{ capabilities: { tools: {} } }
|
|
131
|
+
{ capabilities: { tools: {} } },
|
|
109
132
|
);
|
|
110
133
|
|
|
111
134
|
const serverObj = {
|
|
@@ -119,7 +142,9 @@ class MCPClient extends EventEmitter {
|
|
|
119
142
|
client,
|
|
120
143
|
transport,
|
|
121
144
|
tools: [],
|
|
122
|
-
status: 'starting'
|
|
145
|
+
status: 'starting',
|
|
146
|
+
consecutiveFails: 0,
|
|
147
|
+
lastError: null,
|
|
123
148
|
};
|
|
124
149
|
|
|
125
150
|
this.servers.set(serverId, serverObj);
|
|
@@ -129,17 +154,24 @@ class MCPClient extends EventEmitter {
|
|
|
129
154
|
const server = this.servers.get(serverId);
|
|
130
155
|
if (server) {
|
|
131
156
|
server.status = 'running';
|
|
157
|
+
server.consecutiveFails = 0;
|
|
158
|
+
server.lastError = null;
|
|
132
159
|
this.emit('server_status', { serverId, status: 'running' });
|
|
133
160
|
}
|
|
134
161
|
|
|
135
162
|
return { status: 'running' };
|
|
136
163
|
} catch (err) {
|
|
164
|
+
const message = extractErrorMessage(err);
|
|
137
165
|
const server = this.servers.get(serverId);
|
|
138
166
|
if (server) {
|
|
167
|
+
server.consecutiveFails = (server.consecutiveFails || 0) + 1;
|
|
168
|
+
server.lastError = message;
|
|
139
169
|
server.status = 'error';
|
|
140
|
-
this.emit('server_status', { serverId, status: 'error', error:
|
|
170
|
+
this.emit('server_status', { serverId, status: 'error', error: message });
|
|
141
171
|
}
|
|
142
|
-
|
|
172
|
+
const friendlyErr = new Error(message);
|
|
173
|
+
friendlyErr.originalError = err;
|
|
174
|
+
throw friendlyErr;
|
|
143
175
|
}
|
|
144
176
|
}
|
|
145
177
|
|
|
@@ -149,26 +181,62 @@ class MCPClient extends EventEmitter {
|
|
|
149
181
|
throw new Error(`Server ${serverId} transport not initialized`);
|
|
150
182
|
}
|
|
151
183
|
await server.transport.finishAuth(code);
|
|
152
|
-
await server.client.connect(server.transport).catch(() => {
|
|
184
|
+
await server.client.connect(server.transport).catch(() => {});
|
|
153
185
|
|
|
154
186
|
server.status = 'running';
|
|
187
|
+
server.consecutiveFails = 0;
|
|
188
|
+
server.lastError = null;
|
|
155
189
|
this.emit('server_status', { serverId, status: 'running' });
|
|
156
190
|
}
|
|
157
191
|
|
|
158
192
|
async stopServer(serverId) {
|
|
193
|
+
const timer = this._reconnectTimers.get(serverId);
|
|
194
|
+
if (timer) {
|
|
195
|
+
clearTimeout(timer);
|
|
196
|
+
this._reconnectTimers.delete(serverId);
|
|
197
|
+
}
|
|
198
|
+
|
|
159
199
|
const server = this.servers.get(serverId);
|
|
160
200
|
if (!server) return;
|
|
161
201
|
|
|
162
202
|
try {
|
|
163
203
|
if (server.client) await server.client.close();
|
|
164
204
|
} catch (err) {
|
|
165
|
-
console.error(`Error closing
|
|
205
|
+
console.error(`[MCP] Error closing client ${serverId}:`, err.message);
|
|
166
206
|
}
|
|
167
207
|
|
|
168
208
|
this.servers.delete(serverId);
|
|
169
209
|
this.emit('server_status', { serverId, status: 'stopped' });
|
|
170
210
|
}
|
|
171
211
|
|
|
212
|
+
_scheduleReconnect(serverId, userId, options) {
|
|
213
|
+
if (this._reconnectTimers.has(serverId)) return;
|
|
214
|
+
|
|
215
|
+
const timer = setTimeout(async () => {
|
|
216
|
+
this._reconnectTimers.delete(serverId);
|
|
217
|
+
const server = this.servers.get(serverId);
|
|
218
|
+
if (!server || server.status === 'running') return;
|
|
219
|
+
|
|
220
|
+
const row = db.prepare('SELECT * FROM mcp_servers WHERE id = ? AND enabled = 1').get(serverId);
|
|
221
|
+
if (!row) return;
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
await this.startServer(serverId, row.command, row.name, userId, options);
|
|
225
|
+
await this.listTools(serverId, userId);
|
|
226
|
+
console.log(`[MCP] Reconnected to ${row.name}`);
|
|
227
|
+
} catch (err) {
|
|
228
|
+
const server = this.servers.get(serverId);
|
|
229
|
+
if (server && server.consecutiveFails < CONSECUTIVE_FAIL_LIMIT) {
|
|
230
|
+
this._scheduleReconnect(serverId, userId, options);
|
|
231
|
+
} else {
|
|
232
|
+
console.warn(`[MCP] ${row.name} disabled after ${CONSECUTIVE_FAIL_LIMIT} consecutive failures: ${err.message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}, RECONNECT_DELAY_MS);
|
|
236
|
+
|
|
237
|
+
this._reconnectTimers.set(serverId, timer);
|
|
238
|
+
}
|
|
239
|
+
|
|
172
240
|
_getOwnedServer(serverId, userId = null) {
|
|
173
241
|
const server = this.servers.get(serverId);
|
|
174
242
|
if (!server) return null;
|
|
@@ -189,14 +257,30 @@ class MCPClient extends EventEmitter {
|
|
|
189
257
|
|
|
190
258
|
async callTool(serverId, toolName, args = {}, userId = null) {
|
|
191
259
|
const server = this._getOwnedServer(serverId, userId);
|
|
192
|
-
if (!server
|
|
193
|
-
|
|
260
|
+
if (!server) throw new Error(`Server ${serverId} not found`);
|
|
261
|
+
|
|
262
|
+
if (server.status !== 'running') {
|
|
263
|
+
const hint = server.lastError ? ` (${server.lastError})` : '';
|
|
264
|
+
throw new Error(`MCP server "${server.name}" is not available${hint}`);
|
|
194
265
|
}
|
|
195
266
|
|
|
196
|
-
|
|
197
|
-
name: toolName,
|
|
198
|
-
|
|
199
|
-
|
|
267
|
+
try {
|
|
268
|
+
const result = await server.client.callTool({ name: toolName, arguments: args });
|
|
269
|
+
server.consecutiveFails = 0;
|
|
270
|
+
return result;
|
|
271
|
+
} catch (err) {
|
|
272
|
+
const message = extractErrorMessage(err);
|
|
273
|
+
server.consecutiveFails = (server.consecutiveFails || 0) + 1;
|
|
274
|
+
server.lastError = message;
|
|
275
|
+
|
|
276
|
+
if (server.consecutiveFails >= CONSECUTIVE_FAIL_LIMIT) {
|
|
277
|
+
server.status = 'error';
|
|
278
|
+
this.emit('server_status', { serverId, status: 'error', error: message });
|
|
279
|
+
this._scheduleReconnect(serverId, server.userId, { agentId: server.agentId });
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
throw new Error(`MCP tool "${toolName}" failed: ${message}`);
|
|
283
|
+
}
|
|
200
284
|
}
|
|
201
285
|
|
|
202
286
|
async callToolByName(fullName, args = {}, userId = null, options = {}) {
|
|
@@ -204,10 +288,10 @@ class MCPClient extends EventEmitter {
|
|
|
204
288
|
if (userId != null && server.userId !== userId) continue;
|
|
205
289
|
if (options.agentId && server.agentId && server.agentId !== options.agentId) continue;
|
|
206
290
|
const prefix = `mcp_${server.slug}_`;
|
|
207
|
-
if (fullName.startsWith(prefix))
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
291
|
+
if (!fullName.startsWith(prefix)) continue;
|
|
292
|
+
|
|
293
|
+
const originalName = fullName.substring(prefix.length);
|
|
294
|
+
return await this.callTool(serverId, originalName, args, userId);
|
|
211
295
|
}
|
|
212
296
|
return null;
|
|
213
297
|
}
|
|
@@ -224,7 +308,7 @@ class MCPClient extends EventEmitter {
|
|
|
224
308
|
name: `mcp_${server.slug}_${tool.name}`,
|
|
225
309
|
originalName: tool.name,
|
|
226
310
|
parameters: tool.inputSchema || tool.parameters,
|
|
227
|
-
serverId
|
|
311
|
+
serverId,
|
|
228
312
|
});
|
|
229
313
|
}
|
|
230
314
|
}
|
|
@@ -242,7 +326,9 @@ class MCPClient extends EventEmitter {
|
|
|
242
326
|
command: server.url,
|
|
243
327
|
args: [],
|
|
244
328
|
toolCount: server.tools.length,
|
|
245
|
-
|
|
329
|
+
error: server.lastError || null,
|
|
330
|
+
consecutiveFails: server.consecutiveFails || 0,
|
|
331
|
+
serverInfo: null,
|
|
246
332
|
};
|
|
247
333
|
}
|
|
248
334
|
return statuses;
|
|
@@ -258,8 +344,14 @@ class MCPClient extends EventEmitter {
|
|
|
258
344
|
await this.listTools(srv.id, userId);
|
|
259
345
|
results.push({ id: srv.id, name: srv.name, status: 'running' });
|
|
260
346
|
} catch (err) {
|
|
261
|
-
|
|
262
|
-
|
|
347
|
+
const message = err.message;
|
|
348
|
+
console.error(`[MCP] Failed to start "${srv.name}":`, message);
|
|
349
|
+
// Schedule a reconnect attempt for transient failures (not auth errors)
|
|
350
|
+
const server = this.servers.get(srv.id);
|
|
351
|
+
if (server) {
|
|
352
|
+
this._scheduleReconnect(srv.id, userId, { agentId: srv.agent_id });
|
|
353
|
+
}
|
|
354
|
+
results.push({ id: srv.id, name: srv.name, status: 'error', error: message });
|
|
263
355
|
}
|
|
264
356
|
}
|
|
265
357
|
|
|
@@ -267,6 +359,11 @@ class MCPClient extends EventEmitter {
|
|
|
267
359
|
}
|
|
268
360
|
|
|
269
361
|
async shutdown() {
|
|
362
|
+
for (const timer of this._reconnectTimers.values()) {
|
|
363
|
+
clearTimeout(timer);
|
|
364
|
+
}
|
|
365
|
+
this._reconnectTimers.clear();
|
|
366
|
+
|
|
270
367
|
const promises = [];
|
|
271
368
|
for (const serverId of this.servers.keys()) {
|
|
272
369
|
promises.push(this.stopServer(serverId));
|