neoagent 2.3.1-beta.4 → 2.3.1-beta.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +45 -0
- package/docs/capabilities.md +2 -2
- package/docs/configuration.md +12 -5
- package/docs/hardware.md +1 -1
- package/docs/integrations.md +2 -3
- package/flutter_app/.metadata +42 -0
- package/flutter_app/README.md +21 -0
- package/flutter_app/analysis_options.yaml +32 -0
- package/flutter_app/android/app/build.gradle.kts +109 -0
- package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
- package/flutter_app/android/app/src/main/AndroidManifest.xml +147 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +747 -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/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/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/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/tray_icon_template.png +0 -0
- package/flutter_app/lib/features/location/location_service.dart +119 -0
- package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
- package/flutter_app/lib/main.dart +99 -0
- package/flutter_app/lib/main_account_settings.dart +1250 -0
- package/flutter_app/lib/main_admin.dart +886 -0
- package/flutter_app/lib/main_app_shell.dart +1682 -0
- package/flutter_app/lib/main_chat.dart +3352 -0
- package/flutter_app/lib/main_controller.dart +6781 -0
- package/flutter_app/lib/main_devices.dart +2301 -0
- package/flutter_app/lib/main_integrations.dart +1129 -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 +3546 -0
- package/flutter_app/lib/main_navigation.dart +193 -0
- package/flutter_app/lib/main_operations.dart +4851 -0
- package/flutter_app/lib/main_recordings.dart +870 -0
- package/flutter_app/lib/main_runtime.dart +806 -0
- package/flutter_app/lib/main_settings.dart +2024 -0
- package/flutter_app/lib/main_shared.dart +2861 -0
- package/flutter_app/lib/main_theme.dart +204 -0
- package/flutter_app/lib/main_voice_assistant.dart +957 -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_launcher_bridge.dart +239 -0
- package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
- package/flutter_app/lib/src/app_release_updater.dart +511 -0
- package/flutter_app/lib/src/backend_client.dart +1833 -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 +77 -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/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 +43 -0
- package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/linux/flutter/generated_plugins.cmake +31 -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 +40 -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 +1088 -0
- package/flutter_app/pubspec.yaml +53 -0
- package/flutter_app/test/messaging_access_summary_test.dart +22 -0
- package/flutter_app/test/recording_payloads_test.dart +53 -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 +47 -0
- package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
- package/flutter_app/windows/flutter/generated_plugins.cmake +35 -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 +227 -6
- package/package.json +3 -1
- package/server/db/database.js +110 -0
- package/server/http/middleware.js +55 -2
- package/server/http/routes.js +1 -0
- package/server/index.js +3 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/NOTICES +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/canvaskit/wimp.wasm +0 -0
- package/server/public/flutter_bootstrap.js +2 -2
- package/server/public/main.dart.js +74324 -73132
- package/server/routes/integrations.js +108 -1
- package/server/routes/memory.js +11 -2
- package/server/{http/routes → routes}/screenHistory.js +2 -2
- package/server/routes/settings.js +75 -2
- package/server/{http/routes → routes}/triggers.js +2 -2
- package/server/routes/wearable.js +67 -0
- package/server/services/ai/models.js +30 -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 +31 -0
- package/server/services/ai/settings.js +20 -0
- package/server/services/ai/toolSelector.js +14 -1
- package/server/services/ai/tools.js +77 -4
- package/server/services/desktop/screenRecorder.js +65 -9
- package/server/services/integrations/env.js +5 -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/home_assistant/provider.js +325 -26
- 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 +4 -0
- package/server/services/integrations/spotify/provider.js +1 -0
- package/server/services/integrations/trello/provider.js +842 -0
- package/server/services/manager.js +46 -1
- package/server/services/mcp/client.js +120 -23
- package/server/services/memory/manager.js +39 -2
- package/server/services/messaging/access_policy.js +10 -0
- package/server/services/messaging/manager.js +49 -0
- package/server/services/messaging/meshtastic.js +260 -0
- package/server/services/messaging/meshtastic_env.js +100 -0
- package/server/services/messaging/meshtastic_protocol.js +476 -0
- package/server/services/messaging/meshtastic_tcp_transport.js +25 -0
- package/server/services/tasks/runtime.js +1 -1
- package/server/services/voice/openaiClient.js +4 -1
- package/server/services/voice/openaiSpeech.js +6 -1
- package/server/services/voice/providers.js +52 -12
- package/server/services/voice/runtimeManager.js +136 -19
- package/server/services/voice/turnRunner.js +29 -9
- package/server/services/wearable/firmware_manifest.js +370 -0
- package/server/services/wearable/gateway.js +350 -0
- package/server/services/wearable/protocol.js +45 -0
- package/server/services/wearable/service.js +244 -0
- package/server/utils/local_secrets.js +56 -0
- package/server/utils/logger.js +37 -9
|
@@ -25,6 +25,7 @@ const { BrowserExtensionRegistry } = require('./browser/extension/registry');
|
|
|
25
25
|
const { DesktopCompanionRegistry } = require('./desktop/registry');
|
|
26
26
|
const { DesktopProvider } = require('./desktop/provider');
|
|
27
27
|
const { ScreenRecorder } = require('./desktop/screenRecorder');
|
|
28
|
+
const { WearableService } = require('./wearable/service');
|
|
28
29
|
const { assertRuntimeValidation, getRuntimeValidation } = require('./runtime/validation');
|
|
29
30
|
const {
|
|
30
31
|
getErrorMessage,
|
|
@@ -429,11 +430,46 @@ function createWidgetService(app) {
|
|
|
429
430
|
return widgetService;
|
|
430
431
|
}
|
|
431
432
|
|
|
433
|
+
function createWearableService(app) {
|
|
434
|
+
const wearableService = registerLocal(
|
|
435
|
+
app,
|
|
436
|
+
'wearableService',
|
|
437
|
+
new WearableService({ app }),
|
|
438
|
+
);
|
|
439
|
+
logServiceReady('Wearable service ready');
|
|
440
|
+
return wearableService;
|
|
441
|
+
}
|
|
442
|
+
|
|
432
443
|
function createScreenRecorder(app) {
|
|
444
|
+
const hasActiveRemoteCaptureSession = () => {
|
|
445
|
+
const desktopRegistry = app.locals.desktopCompanionRegistry;
|
|
446
|
+
if (desktopRegistry?.connectionsByUser instanceof Map) {
|
|
447
|
+
for (const userMap of desktopRegistry.connectionsByUser.values()) {
|
|
448
|
+
if (!(userMap instanceof Map)) continue;
|
|
449
|
+
for (const connection of userMap.values()) {
|
|
450
|
+
if (typeof connection?.isOpen === 'function' && connection.isOpen()) {
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const extensionRegistry = app.locals.browserExtensionRegistry;
|
|
458
|
+
if (extensionRegistry?.connectionsByUser instanceof Map) {
|
|
459
|
+
for (const connection of extensionRegistry.connectionsByUser.values()) {
|
|
460
|
+
if (typeof connection?.isOpen === 'function' && connection.isOpen()) {
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return false;
|
|
467
|
+
};
|
|
468
|
+
|
|
433
469
|
const screenRecorder = registerLocal(
|
|
434
470
|
app,
|
|
435
471
|
'screenRecorder',
|
|
436
|
-
new ScreenRecorder(),
|
|
472
|
+
new ScreenRecorder({ hasActiveRemoteCaptureSession }),
|
|
437
473
|
);
|
|
438
474
|
screenRecorder.start();
|
|
439
475
|
logServiceReady('Screen recorder started');
|
|
@@ -525,6 +561,7 @@ async function startServices(app, io) {
|
|
|
525
561
|
const messagingManager = createMessagingManager(app, io, agentEngine);
|
|
526
562
|
const recordingManager = createRecordingManager(app, io);
|
|
527
563
|
createWidgetService(app);
|
|
564
|
+
createWearableService(app);
|
|
528
565
|
createScreenRecorder(app);
|
|
529
566
|
|
|
530
567
|
restoreMessagingConnections(messagingManager);
|
|
@@ -589,6 +626,14 @@ async function stopServices(app) {
|
|
|
589
626
|
);
|
|
590
627
|
}
|
|
591
628
|
|
|
629
|
+
if (app.locals.wearableGateway?.close) {
|
|
630
|
+
tasks.push(
|
|
631
|
+
app.locals.wearableGateway.close().catch((err) => {
|
|
632
|
+
console.error('[WearableGateway] Shutdown error:', getErrorMessage(err));
|
|
633
|
+
}),
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
|
|
592
637
|
if (app.locals.browserControllers instanceof Map) {
|
|
593
638
|
for (const controller of app.locals.browserControllers.values()) {
|
|
594
639
|
tasks.push(
|
|
@@ -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));
|
|
@@ -12,6 +12,11 @@ const {
|
|
|
12
12
|
const { getMemoryStorageDecision } = require('./policy');
|
|
13
13
|
const { AGENT_DATA_DIR } = require('../../../runtime/paths');
|
|
14
14
|
const { isMainAgent, resolveAgentId } = require('../agents/manager');
|
|
15
|
+
const {
|
|
16
|
+
decryptLocalValue,
|
|
17
|
+
encryptLocalValue,
|
|
18
|
+
isLocalEncryptedValue,
|
|
19
|
+
} = require('../../utils/local_secrets');
|
|
15
20
|
|
|
16
21
|
async function getActiveProvider(userId, agentId = null) {
|
|
17
22
|
try {
|
|
@@ -516,11 +521,43 @@ class MemoryManager {
|
|
|
516
521
|
return {};
|
|
517
522
|
}
|
|
518
523
|
}
|
|
519
|
-
try {
|
|
524
|
+
try {
|
|
525
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
526
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
527
|
+
return {};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
let shouldMigrate = false;
|
|
531
|
+
const normalized = {};
|
|
532
|
+
|
|
533
|
+
for (const [service, rawValue] of Object.entries(parsed)) {
|
|
534
|
+
const value = String(rawValue || '');
|
|
535
|
+
if (!value) {
|
|
536
|
+
normalized[service] = '';
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (!isLocalEncryptedValue(value)) shouldMigrate = true;
|
|
541
|
+
normalized[service] = decryptLocalValue(value);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (shouldMigrate) {
|
|
545
|
+
this.writeApiKeys(normalized, userId);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return normalized;
|
|
549
|
+
} catch {
|
|
550
|
+
return {};
|
|
551
|
+
}
|
|
520
552
|
}
|
|
521
553
|
|
|
522
554
|
writeApiKeys(keys, userId = null) {
|
|
523
|
-
|
|
555
|
+
const encrypted = {};
|
|
556
|
+
for (const [service, rawValue] of Object.entries(keys || {})) {
|
|
557
|
+
const value = String(rawValue || '');
|
|
558
|
+
encrypted[service] = value ? encryptLocalValue(value) : '';
|
|
559
|
+
}
|
|
560
|
+
fs.writeFileSync(this._userApiKeysPath(userId), JSON.stringify(encrypted, null, 2), 'utf-8');
|
|
524
561
|
}
|
|
525
562
|
|
|
526
563
|
setApiKey(service, key, userId = null) {
|
|
@@ -165,6 +165,16 @@ const PLATFORM_CAPABILITIES = Object.freeze({
|
|
|
165
165
|
sharedSpaceRuleScopes: Object.freeze(['channel', 'chat']),
|
|
166
166
|
sharedActorRuleScopes: Object.freeze(['user']),
|
|
167
167
|
}),
|
|
168
|
+
meshtastic: capabilityTemplate({
|
|
169
|
+
supportsDirectPolicy: false,
|
|
170
|
+
supportsSharedPolicy: true,
|
|
171
|
+
supportsMentionGate: false,
|
|
172
|
+
supportsDiscovery: false,
|
|
173
|
+
directRuleScopes: Object.freeze([]),
|
|
174
|
+
sharedSpaceRuleScopes: Object.freeze(['channel', 'chat']),
|
|
175
|
+
sharedActorRuleScopes: Object.freeze(['user']),
|
|
176
|
+
manualEntryHint: 'Add a Meshtastic node number or channel id.',
|
|
177
|
+
}),
|
|
168
178
|
feishu: capabilityTemplate({ supportsDiscovery: false }),
|
|
169
179
|
nextcloud_talk: capabilityTemplate({ supportsDiscovery: false }),
|
|
170
180
|
nostr: capabilityTemplate({ supportsDiscovery: false }),
|
|
@@ -9,6 +9,7 @@ const { WhatsAppPlatform } = require('./whatsapp');
|
|
|
9
9
|
const { TelnyxVoicePlatform } = require('./telnyx');
|
|
10
10
|
const { DiscordPlatform } = require('./discord');
|
|
11
11
|
const { TelegramPlatform } = require('./telegram');
|
|
12
|
+
const { MeshtasticPlatform } = require('./meshtastic');
|
|
12
13
|
const {
|
|
13
14
|
SlackPlatform,
|
|
14
15
|
GoogleChatPlatform,
|
|
@@ -34,6 +35,7 @@ const {
|
|
|
34
35
|
classifyRecentTarget,
|
|
35
36
|
} = require('./access_policy');
|
|
36
37
|
const { decryptValue, encryptValue } = require('../integrations/secrets');
|
|
38
|
+
const { readMeshtasticEnabled } = require('./meshtastic_env');
|
|
37
39
|
|
|
38
40
|
const LEGACY_WHATSAPP_AUTH_DIR = path.join(DATA_DIR, 'whatsapp-auth');
|
|
39
41
|
|
|
@@ -78,6 +80,7 @@ class MessagingManager extends EventEmitter {
|
|
|
78
80
|
feishu: createGenericPlatformClass('feishu'),
|
|
79
81
|
line: LinePlatform,
|
|
80
82
|
mattermost: MattermostPlatform,
|
|
83
|
+
meshtastic: MeshtasticPlatform,
|
|
81
84
|
nextcloud_talk: createGenericPlatformClass('nextcloud_talk'),
|
|
82
85
|
nostr: createGenericPlatformClass('nostr'),
|
|
83
86
|
synology_chat: createGenericPlatformClass('synology_chat'),
|
|
@@ -302,6 +305,9 @@ class MessagingManager extends EventEmitter {
|
|
|
302
305
|
config.accessPolicy = this._loadAccessPolicy(userId, agentId, platformName);
|
|
303
306
|
const PlatformClass = this.platformTypes[platformName];
|
|
304
307
|
if (!PlatformClass) throw new Error(`Unknown platform: ${platformName}`);
|
|
308
|
+
if (platformName === 'meshtastic' && !readMeshtasticEnabled()) {
|
|
309
|
+
throw new Error('Meshtastic is disabled by environment configuration');
|
|
310
|
+
}
|
|
305
311
|
|
|
306
312
|
if (platformName === 'whatsapp' && !config.authDir) {
|
|
307
313
|
config.authDir = this._scopedPlatformAuthDir(userId, agentId, platformName);
|
|
@@ -545,6 +551,14 @@ class MessagingManager extends EventEmitter {
|
|
|
545
551
|
|
|
546
552
|
getPlatformStatus(userId, platformName, options = {}) {
|
|
547
553
|
const agentId = this._agentId(userId, options);
|
|
554
|
+
if (platformName === 'meshtastic' && !readMeshtasticEnabled()) {
|
|
555
|
+
return {
|
|
556
|
+
status: 'disabled',
|
|
557
|
+
authInfo: {
|
|
558
|
+
label: 'Disabled in env',
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
}
|
|
548
562
|
const key = this._key(userId, agentId, platformName);
|
|
549
563
|
const platform = this.platforms.get(key);
|
|
550
564
|
if (!platform) {
|
|
@@ -562,7 +576,21 @@ class MessagingManager extends EventEmitter {
|
|
|
562
576
|
const connections = db.prepare('SELECT platform, status, last_connected, agent_id FROM platform_connections WHERE user_id = ? AND agent_id = ?').all(userId, agentId);
|
|
563
577
|
const statuses = {};
|
|
564
578
|
|
|
579
|
+
if (!readMeshtasticEnabled()) {
|
|
580
|
+
statuses.meshtastic = {
|
|
581
|
+
status: 'disabled',
|
|
582
|
+
agentId,
|
|
583
|
+
lastConnected: null,
|
|
584
|
+
authInfo: {
|
|
585
|
+
label: 'Disabled in env',
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
565
590
|
for (const conn of connections) {
|
|
591
|
+
if (conn.platform === 'meshtastic' && !readMeshtasticEnabled()) {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
566
594
|
const key = this._key(userId, agentId, conn.platform);
|
|
567
595
|
const platform = this.platforms.get(key);
|
|
568
596
|
statuses[conn.platform] = {
|
|
@@ -616,6 +644,11 @@ class MessagingManager extends EventEmitter {
|
|
|
616
644
|
).all();
|
|
617
645
|
for (const row of rows) {
|
|
618
646
|
try {
|
|
647
|
+
if (row.platform === 'meshtastic' && !readMeshtasticEnabled()) {
|
|
648
|
+
db.prepare("UPDATE platform_connections SET status = 'disabled' WHERE user_id = ? AND agent_id = ? AND platform = ?")
|
|
649
|
+
.run(row.user_id, row.agent_id, row.platform);
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
619
652
|
const config = this._decodeStoredConfig(row.config);
|
|
620
653
|
console.log(`[Messaging] Restoring ${row.platform} for user ${row.user_id} agent ${row.agent_id || 'main'}`);
|
|
621
654
|
await this.connectPlatform(row.user_id, row.platform, config, { agentId: row.agent_id });
|
|
@@ -627,6 +660,22 @@ class MessagingManager extends EventEmitter {
|
|
|
627
660
|
}
|
|
628
661
|
}
|
|
629
662
|
|
|
663
|
+
async updateMeshtasticEnabled(enabled) {
|
|
664
|
+
if (enabled) return;
|
|
665
|
+
const disconnects = [];
|
|
666
|
+
for (const [key, platform] of this.platforms.entries()) {
|
|
667
|
+
if (!key.endsWith(':meshtastic')) continue;
|
|
668
|
+
disconnects.push(
|
|
669
|
+
Promise.resolve(platform.disconnect()).catch(() => {}).then(() => {
|
|
670
|
+
this.platforms.delete(key);
|
|
671
|
+
})
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
await Promise.all(disconnects);
|
|
675
|
+
db.prepare("UPDATE platform_connections SET status = 'disabled' WHERE platform = 'meshtastic'")
|
|
676
|
+
.run();
|
|
677
|
+
}
|
|
678
|
+
|
|
630
679
|
async shutdown() {
|
|
631
680
|
this.isShuttingDown = true;
|
|
632
681
|
|