neoagent 2.3.1-beta.6 → 2.3.1-beta.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +45 -0
- package/docs/capabilities.md +2 -2
- package/docs/configuration.md +6 -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 +111 -0
- package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
- package/flutter_app/android/app/src/main/AndroidManifest.xml +171 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +804 -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 +119 -0
- package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
- package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +569 -0
- package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +157 -0
- package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +233 -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 +103 -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 +2134 -0
- package/flutter_app/lib/main_chat.dart +3316 -0
- package/flutter_app/lib/main_controller.dart +6836 -0
- package/flutter_app/lib/main_devices.dart +2396 -0
- package/flutter_app/lib/main_integrations.dart +909 -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 +3539 -0
- package/flutter_app/lib/main_navigation.dart +188 -0
- package/flutter_app/lib/main_operations.dart +4851 -0
- package/flutter_app/lib/main_recordings.dart +920 -0
- package/flutter_app/lib/main_runtime.dart +838 -0
- package/flutter_app/lib/main_settings.dart +2040 -0
- package/flutter_app/lib/main_shared.dart +3147 -0
- package/flutter_app/lib/main_theme.dart +257 -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_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 +29 -0
- package/flutter_app/lib/src/app_release_updater.dart +511 -0
- package/flutter_app/lib/src/backend_client.dart +1837 -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 +44 -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 +1224 -0
- package/flutter_app/pubspec.yaml +57 -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 +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 +201 -6
- package/package.json +5 -3
- package/server/db/database.js +125 -1
- package/server/http/middleware.js +55 -2
- package/server/http/routes.js +1 -0
- package/server/http/static.js +41 -1
- package/server/index.js +3 -0
- 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/NOTICES +281 -1
- package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
- 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 +86314 -82363
- package/server/routes/auth.js +26 -5
- package/server/routes/integrations.js +108 -1
- package/server/routes/memory.js +11 -2
- package/server/routes/settings.js +84 -2
- package/server/routes/wearable.js +67 -0
- package/server/services/ai/engine.js +3 -2
- 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 +54 -1
- package/server/services/ai/tools.js +88 -5
- 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/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 -2
- 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 +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_protocol.test.js +33 -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 +144 -15
- package/server/services/voice/runtimeManager.js +156 -19
- 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/widgets/service.js +3 -0
- 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/lib/manager.js
CHANGED
|
@@ -542,6 +542,19 @@ async function cmdSetup() {
|
|
|
542
542
|
const normalizedDeploymentMode = parseDeploymentMode(deploymentMode);
|
|
543
543
|
const normalizedReleaseChannel = parseReleaseChannel(releaseChannel) || 'stable';
|
|
544
544
|
|
|
545
|
+
const githubOauthClientId = await askSecret(
|
|
546
|
+
'GitHub OAuth client ID',
|
|
547
|
+
current.GITHUB_OAUTH_CLIENT_ID || ''
|
|
548
|
+
);
|
|
549
|
+
const githubOauthClientSecret = await askSecret(
|
|
550
|
+
'GitHub OAuth client secret',
|
|
551
|
+
current.GITHUB_OAUTH_CLIENT_SECRET || ''
|
|
552
|
+
);
|
|
553
|
+
const githubOauthRedirectUri = await ask(
|
|
554
|
+
'GitHub OAuth redirect URI',
|
|
555
|
+
current.GITHUB_OAUTH_REDIRECT_URI || ''
|
|
556
|
+
);
|
|
557
|
+
|
|
545
558
|
const lines = [
|
|
546
559
|
`NODE_ENV=production`,
|
|
547
560
|
`PORT=${port}`,
|
|
@@ -576,6 +589,9 @@ async function cmdSetup() {
|
|
|
576
589
|
figmaOauthClientId ? `FIGMA_OAUTH_CLIENT_ID=${figmaOauthClientId}` : '',
|
|
577
590
|
figmaOauthClientSecret ? `FIGMA_OAUTH_CLIENT_SECRET=${figmaOauthClientSecret}` : '',
|
|
578
591
|
figmaOauthRedirectUri ? `FIGMA_OAUTH_REDIRECT_URI=${figmaOauthRedirectUri}` : '',
|
|
592
|
+
githubOauthClientId ? `GITHUB_OAUTH_CLIENT_ID=${githubOauthClientId}` : '',
|
|
593
|
+
githubOauthClientSecret ? `GITHUB_OAUTH_CLIENT_SECRET=${githubOauthClientSecret}` : '',
|
|
594
|
+
githubOauthRedirectUri ? `GITHUB_OAUTH_REDIRECT_URI=${githubOauthRedirectUri}` : '',
|
|
579
595
|
deepgramApiKey ? `DEEPGRAM_API_KEY=${deepgramApiKey}` : '',
|
|
580
596
|
deepgramBaseUrl ? `DEEPGRAM_BASE_URL=${deepgramBaseUrl}` : '',
|
|
581
597
|
deepgramModel ? `DEEPGRAM_MODEL=${deepgramModel}` : '',
|
|
@@ -674,6 +690,172 @@ async function cmdMigrate(args = []) {
|
|
|
674
690
|
});
|
|
675
691
|
}
|
|
676
692
|
|
|
693
|
+
async function cmdLogin(args = []) {
|
|
694
|
+
const provider = args[0];
|
|
695
|
+
if (provider !== 'github-copilot' && provider !== 'openai-codex') {
|
|
696
|
+
throw new Error(`Unsupported login provider: ${provider || 'none'}. Available: github-copilot, openai-codex`);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (provider === 'github-copilot') {
|
|
700
|
+
heading('GitHub Copilot Login');
|
|
701
|
+
const clientId = '01ab8ac9400c4e429b23';
|
|
702
|
+
logInfo('Requesting device code from GitHub...');
|
|
703
|
+
|
|
704
|
+
const reqRes = await fetch('https://github.com/login/device/code', {
|
|
705
|
+
method: 'POST',
|
|
706
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
707
|
+
body: JSON.stringify({ client_id: clientId, scope: 'user:email' })
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
if (!reqRes.ok) {
|
|
711
|
+
throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const { device_code, user_code, verification_uri, interval } = await reqRes.json();
|
|
715
|
+
|
|
716
|
+
console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
|
|
717
|
+
console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
|
|
718
|
+
|
|
719
|
+
logInfo('Waiting for authorization (timeout in 15m)...');
|
|
720
|
+
const startTime = Date.now();
|
|
721
|
+
const timeoutMs = 15 * 60 * 1000;
|
|
722
|
+
let currentPollInterval = (interval || 5) * 1000;
|
|
723
|
+
|
|
724
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
725
|
+
await new Promise((r) => setTimeout(r, currentPollInterval));
|
|
726
|
+
const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
|
|
727
|
+
method: 'POST',
|
|
728
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
729
|
+
body: JSON.stringify({
|
|
730
|
+
client_id: clientId,
|
|
731
|
+
device_code,
|
|
732
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
|
|
733
|
+
})
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
if (!tokenRes.ok) {
|
|
737
|
+
const errorText = await tokenRes.text().catch(() => 'Unknown error');
|
|
738
|
+
throw new Error(`GitHub token request failed: HTTP ${tokenRes.status} - ${errorText}`);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const data = await tokenRes.json();
|
|
742
|
+
if (data.access_token) {
|
|
743
|
+
upsertEnvValue('GITHUB_COPILOT_ACCESS_TOKEN', data.access_token);
|
|
744
|
+
logOk('Successfully authenticated and saved GitHub Copilot access token to .env');
|
|
745
|
+
logInfo('Applying updated provider credentials by restarting NeoAgent...');
|
|
746
|
+
cmdRestart();
|
|
747
|
+
return;
|
|
748
|
+
} else if (data.error === 'authorization_pending') {
|
|
749
|
+
// Continue polling
|
|
750
|
+
} else if (data.error === 'slow_down') {
|
|
751
|
+
currentPollInterval += 5000;
|
|
752
|
+
} else if (data.error) {
|
|
753
|
+
throw new Error(`Authentication failed: ${data.error_description || data.error}`);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
throw new Error('GitHub authentication timed out after 15 minutes.');
|
|
757
|
+
} else if (provider === 'openai-codex') {
|
|
758
|
+
heading('OpenAI Codex Login');
|
|
759
|
+
const clientId = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
760
|
+
logInfo('Requesting device code from OpenAI...');
|
|
761
|
+
|
|
762
|
+
const reqRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/usercode', {
|
|
763
|
+
method: 'POST',
|
|
764
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
765
|
+
body: JSON.stringify({ client_id: clientId, scope: 'openid profile email offline_access model.request model.read model.create' })
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
if (!reqRes.ok) {
|
|
769
|
+
throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const data = await reqRes.json();
|
|
773
|
+
const { device_auth_id, interval } = data;
|
|
774
|
+
const user_code = data.user_code || data.usercode;
|
|
775
|
+
const verification_uri = 'https://auth.openai.com/codex/device';
|
|
776
|
+
|
|
777
|
+
console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
|
|
778
|
+
console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
|
|
779
|
+
|
|
780
|
+
logInfo('Waiting for authorization (timeout in 15m)...');
|
|
781
|
+
const startTime = Date.now();
|
|
782
|
+
const timeoutMs = 15 * 60 * 1000;
|
|
783
|
+
let currentPollInterval = (interval || 5) * 1000;
|
|
784
|
+
let authorizationCode = null;
|
|
785
|
+
let codeVerifier = null;
|
|
786
|
+
|
|
787
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
788
|
+
await new Promise((r) => setTimeout(r, currentPollInterval));
|
|
789
|
+
const tokenRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/token', {
|
|
790
|
+
method: 'POST',
|
|
791
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
792
|
+
body: JSON.stringify({
|
|
793
|
+
device_auth_id: device_auth_id,
|
|
794
|
+
user_code: user_code
|
|
795
|
+
})
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
if (tokenRes.status === 403 || tokenRes.status === 404) {
|
|
799
|
+
// These statuses are returned by OpenAI while authorization is pending
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (!tokenRes.ok) {
|
|
804
|
+
const errorText = await tokenRes.text().catch(() => 'Unknown error');
|
|
805
|
+
throw new Error(`OpenAI token request failed: HTTP ${tokenRes.status} - ${errorText}`);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
const pollData = await tokenRes.json();
|
|
809
|
+
if (pollData.authorization_code && pollData.code_verifier) {
|
|
810
|
+
authorizationCode = pollData.authorization_code;
|
|
811
|
+
codeVerifier = pollData.code_verifier;
|
|
812
|
+
break;
|
|
813
|
+
} else if (pollData.error === 'authorization_pending') {
|
|
814
|
+
// Continue polling
|
|
815
|
+
} else if (pollData.error === 'slow_down') {
|
|
816
|
+
currentPollInterval += 5000;
|
|
817
|
+
} else if (pollData.error) {
|
|
818
|
+
throw new Error(`Authentication failed: ${pollData.error_description || pollData.error}`);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (!authorizationCode || !codeVerifier) {
|
|
823
|
+
throw new Error('OpenAI authentication timed out after 15 minutes.');
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
logInfo('Exchanging authorization code for access token...');
|
|
827
|
+
const exchangeRes = await fetch('https://auth.openai.com/oauth/token', {
|
|
828
|
+
method: 'POST',
|
|
829
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
830
|
+
body: new URLSearchParams({
|
|
831
|
+
grant_type: 'authorization_code',
|
|
832
|
+
code: authorizationCode,
|
|
833
|
+
redirect_uri: 'https://auth.openai.com/deviceauth/callback',
|
|
834
|
+
client_id: clientId,
|
|
835
|
+
code_verifier: codeVerifier
|
|
836
|
+
})
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
if (!exchangeRes.ok) {
|
|
840
|
+
const errorText = await exchangeRes.text().catch(() => 'Unknown error');
|
|
841
|
+
throw new Error(`OpenAI token exchange failed: HTTP ${exchangeRes.status} - ${errorText}`);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const exchangeData = await exchangeRes.json();
|
|
845
|
+
if (exchangeData.access_token) {
|
|
846
|
+
upsertEnvValue('OPENAI_CODEX_ACCESS_TOKEN', exchangeData.access_token);
|
|
847
|
+
if (exchangeData.refresh_token) {
|
|
848
|
+
upsertEnvValue('OPENAI_CODEX_REFRESH_TOKEN', exchangeData.refresh_token);
|
|
849
|
+
}
|
|
850
|
+
logOk('Successfully authenticated and saved OpenAI Codex tokens to .env');
|
|
851
|
+
logInfo('Applying updated provider credentials by restarting NeoAgent...');
|
|
852
|
+
cmdRestart();
|
|
853
|
+
} else {
|
|
854
|
+
throw new Error('OpenAI token exchange succeeded but did not return an access token.');
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
677
859
|
function installDependencies() {
|
|
678
860
|
heading('Dependencies');
|
|
679
861
|
runOrThrow('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], {
|
|
@@ -867,6 +1049,11 @@ function cmdRestart() {
|
|
|
867
1049
|
cmdStart();
|
|
868
1050
|
}
|
|
869
1051
|
|
|
1052
|
+
async function cmdRebuildWeb() {
|
|
1053
|
+
heading(`Rebuild Flutter Web Client`);
|
|
1054
|
+
buildBundledWebClientIfPossible();
|
|
1055
|
+
}
|
|
1056
|
+
|
|
870
1057
|
function cmdUninstall() {
|
|
871
1058
|
heading(`Uninstall ${APP_NAME}`);
|
|
872
1059
|
const platform = detectPlatform();
|
|
@@ -962,6 +1149,8 @@ function cmdUpdate(args = []) {
|
|
|
962
1149
|
}
|
|
963
1150
|
const versionBefore = currentInstalledVersionLabel();
|
|
964
1151
|
let versionAfter = versionBefore;
|
|
1152
|
+
const githubInstallRef = releaseChannel === 'beta' ? '#beta' : '';
|
|
1153
|
+
const githubInstallSpec = `git+https://github.com/NeoLabs-Systems/NeoAgent.git${githubInstallRef}`;
|
|
965
1154
|
|
|
966
1155
|
if (fs.existsSync(path.join(APP_DIR, '.git')) && commandExists('git')) {
|
|
967
1156
|
const current = runQuiet('git', ['rev-parse', '--short', 'HEAD']);
|
|
@@ -982,17 +1171,16 @@ function cmdUpdate(args = []) {
|
|
|
982
1171
|
buildBundledWebClientIfPossible();
|
|
983
1172
|
}
|
|
984
1173
|
} else {
|
|
985
|
-
|
|
986
|
-
logWarn(`No git repo detected; attempting npm global update from ${npmTag}.`);
|
|
1174
|
+
logWarn(`No git repo detected; attempting npm global update from ${githubInstallSpec}.`);
|
|
987
1175
|
if (commandExists('npm')) {
|
|
988
1176
|
try {
|
|
989
1177
|
backupRuntimeData();
|
|
990
|
-
runOrThrow('npm', ['install', '-g',
|
|
1178
|
+
runOrThrow('npm', ['install', '-g', githubInstallSpec, '--force'], {
|
|
991
1179
|
env: withInstallEnv()
|
|
992
1180
|
});
|
|
993
|
-
logOk('npm global update completed (forced reinstall)');
|
|
1181
|
+
logOk('npm global update completed (forced reinstall from GitHub)');
|
|
994
1182
|
} catch {
|
|
995
|
-
logWarn(`npm global update failed. Run: npm install -g
|
|
1183
|
+
logWarn(`npm global update failed. Run: npm install -g ${githubInstallSpec} --force`);
|
|
996
1184
|
}
|
|
997
1185
|
} else {
|
|
998
1186
|
logWarn('npm not found. Cannot perform global update.');
|
|
@@ -1062,7 +1250,8 @@ async function cmdEnv(args = []) {
|
|
|
1062
1250
|
function printHelp() {
|
|
1063
1251
|
console.log(`${APP_NAME} manager`);
|
|
1064
1252
|
console.log('Usage: neoagent <command>');
|
|
1065
|
-
console.log('Commands: install | setup | env | channel | update | restart | start | stop | status | logs | uninstall | migrate');
|
|
1253
|
+
console.log('Commands: install | setup | env | channel | update | restart | rebuild-web | start | stop | status | logs | uninstall | migrate | login');
|
|
1254
|
+
console.log('Login usage: neoagent login github-copilot | neoagent login openai-codex');
|
|
1066
1255
|
console.log('Channel usage: neoagent channel | neoagent channel stable | neoagent channel beta');
|
|
1067
1256
|
console.log('Update usage: neoagent update | neoagent update stable | neoagent update beta');
|
|
1068
1257
|
console.log('Env usage: neoagent env list | neoagent env get PORT | neoagent env set PORT 3333 | neoagent env unset PORT');
|
|
@@ -1094,6 +1283,9 @@ async function runCLI(argv) {
|
|
|
1094
1283
|
case 'restart':
|
|
1095
1284
|
cmdRestart();
|
|
1096
1285
|
break;
|
|
1286
|
+
case 'rebuild-web':
|
|
1287
|
+
await cmdRebuildWeb();
|
|
1288
|
+
break;
|
|
1097
1289
|
case 'start':
|
|
1098
1290
|
cmdStart();
|
|
1099
1291
|
break;
|
|
@@ -1112,6 +1304,9 @@ async function runCLI(argv) {
|
|
|
1112
1304
|
case 'migrate':
|
|
1113
1305
|
await cmdMigrate(argv.slice(1));
|
|
1114
1306
|
break;
|
|
1307
|
+
case 'login':
|
|
1308
|
+
await cmdLogin(argv.slice(1));
|
|
1309
|
+
break;
|
|
1115
1310
|
case 'help':
|
|
1116
1311
|
case '--help':
|
|
1117
1312
|
case '-h':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neoagent",
|
|
3
|
-
"version": "2.3.1-beta.
|
|
3
|
+
"version": "2.3.1-beta.60",
|
|
4
4
|
"description": "Proactive personal AI agent with no limits",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"runtime",
|
|
17
17
|
"server",
|
|
18
18
|
"extensions",
|
|
19
|
+
"flutter_app",
|
|
19
20
|
"docs",
|
|
20
21
|
"com.neoagent.plist",
|
|
21
22
|
"LICENSE",
|
|
@@ -34,8 +35,8 @@
|
|
|
34
35
|
"docs:dev": "docusaurus start",
|
|
35
36
|
"docs:build": "docusaurus build",
|
|
36
37
|
"docs:preview": "docusaurus serve build",
|
|
37
|
-
"flutter:run:web": "cd flutter_app && flutter run -d chrome",
|
|
38
|
-
"flutter:build:web": "cd flutter_app && flutter build web --output ../server/public --dart-define=NEOAGENT_BACKEND_URL=${NEOAGENT_BACKEND_URL:-}",
|
|
38
|
+
"flutter:run:web": "cd flutter_app && flutter run -d chrome --dart-define=NEOAGENT_WEB_BUILD_ID=$(node ../scripts/web_build_id.js)",
|
|
39
|
+
"flutter:build:web": "cd flutter_app && flutter build web --output ../server/public --dart-define=NEOAGENT_BACKEND_URL=${NEOAGENT_BACKEND_URL:-} --dart-define=NEOAGENT_WEB_BUILD_ID=$(node ../scripts/web_build_id.js)",
|
|
39
40
|
"manage": "node bin/neoagent.js",
|
|
40
41
|
"test": "node --test",
|
|
41
42
|
"benchmark:tokens": "node scripts/benchmark-token-cost.js",
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"puppeteer-core": "^24.40.0",
|
|
77
78
|
"puppeteer-extra": "^3.3.6",
|
|
78
79
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
80
|
+
"qrcode": "^1.5.4",
|
|
79
81
|
"sharp": "^0.34.5",
|
|
80
82
|
"socket.io": "^4.8.1",
|
|
81
83
|
"telegraf": "^4.16.3",
|
package/server/db/database.js
CHANGED
|
@@ -24,7 +24,8 @@ db.exec(`
|
|
|
24
24
|
password TEXT NOT NULL,
|
|
25
25
|
password_login_enabled INTEGER DEFAULT 1,
|
|
26
26
|
created_at TEXT DEFAULT (datetime('now')),
|
|
27
|
-
last_login TEXT
|
|
27
|
+
last_login TEXT,
|
|
28
|
+
has_completed_onboarding INTEGER DEFAULT 0
|
|
28
29
|
);
|
|
29
30
|
|
|
30
31
|
CREATE TABLE IF NOT EXISTS agents (
|
|
@@ -272,6 +273,19 @@ db.exec(`
|
|
|
272
273
|
FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
|
|
273
274
|
);
|
|
274
275
|
|
|
276
|
+
CREATE TABLE IF NOT EXISTS integration_provider_configs (
|
|
277
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
278
|
+
user_id INTEGER NOT NULL,
|
|
279
|
+
agent_id TEXT,
|
|
280
|
+
provider_key TEXT NOT NULL,
|
|
281
|
+
config_json TEXT DEFAULT '{}',
|
|
282
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
283
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
284
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
285
|
+
FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
|
|
286
|
+
UNIQUE(user_id, agent_id, provider_key)
|
|
287
|
+
);
|
|
288
|
+
|
|
275
289
|
CREATE TABLE IF NOT EXISTS browser_extension_pairing_requests (
|
|
276
290
|
id TEXT PRIMARY KEY,
|
|
277
291
|
user_id INTEGER,
|
|
@@ -875,6 +889,7 @@ function createAgentScopedIndexes() {
|
|
|
875
889
|
const statements = [
|
|
876
890
|
['agent_runs', 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent ON agent_runs(user_id, agent_id, created_at DESC)'],
|
|
877
891
|
['integration_connections', 'CREATE INDEX IF NOT EXISTS idx_integration_connections_agent ON integration_connections(user_id, agent_id, provider_key, app_key)'],
|
|
892
|
+
['integration_provider_configs', 'CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent ON integration_provider_configs(user_id, agent_id, provider_key)'],
|
|
878
893
|
['messages', 'CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(user_id, agent_id, created_at DESC)'],
|
|
879
894
|
['conversations', 'CREATE INDEX IF NOT EXISTS idx_conversations_agent ON conversations(user_id, agent_id, updated_at DESC)'],
|
|
880
895
|
['scheduled_tasks', 'CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_agent ON scheduled_tasks(user_id, agent_id)'],
|
|
@@ -922,6 +937,7 @@ function backfillAgentIds() {
|
|
|
922
937
|
'platform_connections',
|
|
923
938
|
'mcp_servers',
|
|
924
939
|
'integration_connections',
|
|
940
|
+
'integration_provider_configs',
|
|
925
941
|
'integration_oauth_states',
|
|
926
942
|
'scheduled_tasks',
|
|
927
943
|
'conversations',
|
|
@@ -1303,6 +1319,83 @@ function migrateIntegrationOauthStatesTable() {
|
|
|
1303
1319
|
`);
|
|
1304
1320
|
}
|
|
1305
1321
|
|
|
1322
|
+
function migrateIntegrationProviderConfigsTable() {
|
|
1323
|
+
if (
|
|
1324
|
+
tableHasColumn('integration_provider_configs', 'agent_id') &&
|
|
1325
|
+
tableHasUniqueIndex('integration_provider_configs', ['user_id', 'agent_id', 'provider_key'])
|
|
1326
|
+
) {
|
|
1327
|
+
const users = db.prepare('SELECT id FROM users').all();
|
|
1328
|
+
for (const user of users) {
|
|
1329
|
+
const agentId = getMainAgentId(user.id);
|
|
1330
|
+
db.prepare(
|
|
1331
|
+
'UPDATE integration_provider_configs SET agent_id = ? WHERE user_id = ? AND agent_id IS NULL'
|
|
1332
|
+
).run(agentId, user.id);
|
|
1333
|
+
}
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
const rows = db
|
|
1338
|
+
.prepare('SELECT * FROM integration_provider_configs ORDER BY id ASC')
|
|
1339
|
+
.all();
|
|
1340
|
+
|
|
1341
|
+
db.exec('BEGIN');
|
|
1342
|
+
try {
|
|
1343
|
+
db.exec(`
|
|
1344
|
+
ALTER TABLE integration_provider_configs RENAME TO integration_provider_configs_legacy_agent_scope;
|
|
1345
|
+
|
|
1346
|
+
CREATE TABLE integration_provider_configs (
|
|
1347
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1348
|
+
user_id INTEGER NOT NULL,
|
|
1349
|
+
agent_id TEXT,
|
|
1350
|
+
provider_key TEXT NOT NULL,
|
|
1351
|
+
config_json TEXT DEFAULT '{}',
|
|
1352
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
1353
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
1354
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
1355
|
+
FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
|
|
1356
|
+
UNIQUE(user_id, agent_id, provider_key)
|
|
1357
|
+
);
|
|
1358
|
+
`);
|
|
1359
|
+
const insert = db.prepare(`
|
|
1360
|
+
INSERT OR REPLACE INTO integration_provider_configs (
|
|
1361
|
+
id,
|
|
1362
|
+
user_id,
|
|
1363
|
+
agent_id,
|
|
1364
|
+
provider_key,
|
|
1365
|
+
config_json,
|
|
1366
|
+
created_at,
|
|
1367
|
+
updated_at
|
|
1368
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1369
|
+
`);
|
|
1370
|
+
|
|
1371
|
+
for (const row of rows) {
|
|
1372
|
+
insert.run(
|
|
1373
|
+
row.id,
|
|
1374
|
+
row.user_id,
|
|
1375
|
+
row.agent_id || getMainAgentId(row.user_id),
|
|
1376
|
+
row.provider_key,
|
|
1377
|
+
row.config_json,
|
|
1378
|
+
row.created_at,
|
|
1379
|
+
row.updated_at,
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
db.exec(`
|
|
1384
|
+
DROP TABLE integration_provider_configs_legacy_agent_scope;
|
|
1385
|
+
CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent
|
|
1386
|
+
ON integration_provider_configs(user_id, agent_id, provider_key);
|
|
1387
|
+
`);
|
|
1388
|
+
db.exec('COMMIT');
|
|
1389
|
+
} catch (error) {
|
|
1390
|
+
try {
|
|
1391
|
+
db.exec('ROLLBACK');
|
|
1392
|
+
} catch {
|
|
1393
|
+
// Ignore rollback errors and rethrow the original migration failure.
|
|
1394
|
+
}
|
|
1395
|
+
throw error;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1306
1399
|
function migrateIntegrationSecretStorage() {
|
|
1307
1400
|
try {
|
|
1308
1401
|
const connectionRows = db
|
|
@@ -1335,6 +1428,22 @@ function migrateIntegrationSecretStorage() {
|
|
|
1335
1428
|
} catch {
|
|
1336
1429
|
// Preserve startup even if a row cannot be re-encrypted.
|
|
1337
1430
|
}
|
|
1431
|
+
|
|
1432
|
+
try {
|
|
1433
|
+
const providerRows = db
|
|
1434
|
+
.prepare('SELECT id, config_json FROM integration_provider_configs')
|
|
1435
|
+
.all();
|
|
1436
|
+
const updateProviderConfig = db.prepare(
|
|
1437
|
+
'UPDATE integration_provider_configs SET config_json = ? WHERE id = ?',
|
|
1438
|
+
);
|
|
1439
|
+
for (const row of providerRows) {
|
|
1440
|
+
const current = String(row.config_json || '');
|
|
1441
|
+
if (!current || isEncryptedValue(current)) continue;
|
|
1442
|
+
updateProviderConfig.run(encryptValue(current), row.id);
|
|
1443
|
+
}
|
|
1444
|
+
} catch {
|
|
1445
|
+
// Preserve startup even if a row cannot be re-encrypted.
|
|
1446
|
+
}
|
|
1338
1447
|
}
|
|
1339
1448
|
|
|
1340
1449
|
function backfillVerifiedAccountEmails() {
|
|
@@ -1408,11 +1517,26 @@ rebuildPlatformConnectionsForAgents();
|
|
|
1408
1517
|
rebuildCoreMemoryForAgents();
|
|
1409
1518
|
migrateIntegrationConnectionsTable();
|
|
1410
1519
|
migrateIntegrationOauthStatesTable();
|
|
1520
|
+
migrateIntegrationProviderConfigsTable();
|
|
1411
1521
|
createAgentScopedIndexes();
|
|
1522
|
+
backfillAgentIds();
|
|
1412
1523
|
migrateIntegrationSecretStorage();
|
|
1413
1524
|
backfillVerifiedAccountEmails();
|
|
1414
1525
|
rebuildFtsForAgents();
|
|
1415
1526
|
|
|
1527
|
+
function migrateUsersOnboarding() {
|
|
1528
|
+
try {
|
|
1529
|
+
const columns = db.pragma('table_info(users)');
|
|
1530
|
+
const hasOnboardingCol = columns.some((c) => c.name === 'has_completed_onboarding');
|
|
1531
|
+
if (!hasOnboardingCol) {
|
|
1532
|
+
db.exec('ALTER TABLE users ADD COLUMN has_completed_onboarding INTEGER DEFAULT 0');
|
|
1533
|
+
}
|
|
1534
|
+
} catch (err) {
|
|
1535
|
+
console.warn('Could not add has_completed_onboarding column:', err.message);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
migrateUsersOnboarding();
|
|
1539
|
+
|
|
1416
1540
|
try {
|
|
1417
1541
|
db.exec(`
|
|
1418
1542
|
INSERT OR REPLACE INTO conversation_history_fts(rowid, content, role, user_id, agent_id, agent_run_id)
|
|
@@ -68,6 +68,9 @@ ensureSessionStoreSchema(sessionsDb);
|
|
|
68
68
|
|
|
69
69
|
function buildHelmetOptions({ secureCookies }) {
|
|
70
70
|
const wsConnectSrc = secureCookies ? ['wss:'] : ['ws:', 'wss:'];
|
|
71
|
+
const isDevelopment = String(process.env.NODE_ENV || '').trim() === 'development';
|
|
72
|
+
// Flutter web (CanvasKit) requires external script/connect sources and wasm eval.
|
|
73
|
+
// Keep strict overrides available via env for hardened deployments.
|
|
71
74
|
const allowUnsafeEval = boolEnv('NEOAGENT_CSP_ALLOW_UNSAFE_EVAL', true);
|
|
72
75
|
const allowExternalScriptCdn = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_SCRIPT_CDN', true);
|
|
73
76
|
const allowExternalConnect = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_CONNECT', true);
|
|
@@ -82,20 +85,31 @@ function buildHelmetOptions({ secureCookies }) {
|
|
|
82
85
|
|
|
83
86
|
const connectSrc = ["'self'", ...wsConnectSrc];
|
|
84
87
|
if (allowExternalConnect) {
|
|
85
|
-
connectSrc.push('https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.gstatic.com', 'https://
|
|
88
|
+
connectSrc.push('https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.gstatic.com', 'https://cdn.jsdelivr.net');
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
return {
|
|
89
92
|
strictTransportSecurity: false,
|
|
90
93
|
crossOriginOpenerPolicy: false,
|
|
94
|
+
crossOriginResourcePolicy: { policy: 'same-site' },
|
|
91
95
|
originAgentCluster: false,
|
|
96
|
+
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
|
|
97
|
+
permissionsPolicy: {
|
|
98
|
+
features: {
|
|
99
|
+
camera: ['self'],
|
|
100
|
+
geolocation: ['self'],
|
|
101
|
+
microphone: ['self'],
|
|
102
|
+
payment: [],
|
|
103
|
+
usb: [],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
92
106
|
contentSecurityPolicy: {
|
|
93
107
|
directives: {
|
|
94
108
|
defaultSrc: ["'self'"],
|
|
95
109
|
scriptSrc,
|
|
96
110
|
scriptSrcAttr: ["'unsafe-inline'"],
|
|
97
111
|
styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
|
|
98
|
-
imgSrc: ["'self'", 'data:', 'blob:'
|
|
112
|
+
imgSrc: ["'self'", 'data:', 'blob:'],
|
|
99
113
|
mediaSrc: ["'self'", 'data:', 'blob:'],
|
|
100
114
|
connectSrc,
|
|
101
115
|
fontSrc: ["'self'", 'data:', 'https://fonts.gstatic.com'],
|
|
@@ -154,6 +168,16 @@ function applyHttpMiddleware(app, { secureCookies, trustProxy, sessionMiddleware
|
|
|
154
168
|
const path = `${value}`.split('?')[0];
|
|
155
169
|
return path === '/socket.io/' || path === '/socket.io' || path.startsWith('/socket.io/');
|
|
156
170
|
};
|
|
171
|
+
const isStateChangingMethod = (method = '') => ['POST', 'PUT', 'PATCH', 'DELETE'].includes(String(method).toUpperCase());
|
|
172
|
+
const extractOriginFromReferer = (referer = '') => {
|
|
173
|
+
const value = String(referer || '').trim();
|
|
174
|
+
if (!value) return '';
|
|
175
|
+
try {
|
|
176
|
+
return new URL(value).origin;
|
|
177
|
+
} catch {
|
|
178
|
+
return '';
|
|
179
|
+
}
|
|
180
|
+
};
|
|
157
181
|
const requestPath = (req) => req.originalUrl || req.url || req.path || '';
|
|
158
182
|
const applyOnlyToRecordingChunk = (handler) => (req, res, next) => (
|
|
159
183
|
isRecordingChunkPath(requestPath(req)) ? handler(req, res, next) : next()
|
|
@@ -187,6 +211,35 @@ function applyHttpMiddleware(app, { secureCookies, trustProxy, sessionMiddleware
|
|
|
187
211
|
});
|
|
188
212
|
})
|
|
189
213
|
);
|
|
214
|
+
app.use((req, res, next) => {
|
|
215
|
+
const path = `${req.originalUrl || req.url || req.path || ''}`.split('?')[0];
|
|
216
|
+
if (path.startsWith('/api/')) {
|
|
217
|
+
res.setHeader('Cache-Control', 'no-store, max-age=0');
|
|
218
|
+
res.setHeader('Pragma', 'no-cache');
|
|
219
|
+
res.setHeader('Expires', '0');
|
|
220
|
+
}
|
|
221
|
+
next();
|
|
222
|
+
});
|
|
223
|
+
app.use((req, res, next) => {
|
|
224
|
+
const path = `${req.originalUrl || req.url || req.path || ''}`.split('?')[0];
|
|
225
|
+
if (!path.startsWith('/api/')) return next();
|
|
226
|
+
if (!isStateChangingMethod(req.method)) return next();
|
|
227
|
+
|
|
228
|
+
const origin = String(req.get('origin') || '').trim() || extractOriginFromReferer(req.get('referer'));
|
|
229
|
+
if (!origin) return next();
|
|
230
|
+
|
|
231
|
+
const allowBrowserExtensionOrigin = isBrowserExtensionCorsPath(path);
|
|
232
|
+
return validateOrigin(origin, (error) => {
|
|
233
|
+
if (error) {
|
|
234
|
+
logRequestSummary('warn', req, 'blocked state-changing request due to invalid origin', { origin });
|
|
235
|
+
return res.status(403).json({ error: 'Origin not allowed' });
|
|
236
|
+
}
|
|
237
|
+
return next();
|
|
238
|
+
}, {
|
|
239
|
+
allowChromeExtension: allowBrowserExtensionOrigin,
|
|
240
|
+
allowMissingOrigin: false,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
190
243
|
app.use((req, res, next) => {
|
|
191
244
|
const startedAt = Date.now();
|
|
192
245
|
|
package/server/http/routes.js
CHANGED
|
@@ -26,6 +26,7 @@ const routeRegistry = [
|
|
|
26
26
|
{ basePath: '/api/desktop', modulePath: '../routes/desktop' },
|
|
27
27
|
{ basePath: '/api/recordings', modulePath: '../routes/recordings' },
|
|
28
28
|
{ basePath: '/api/voice-assistant', modulePath: '../routes/voice_assistant' },
|
|
29
|
+
{ basePath: '/api/wearable', modulePath: '../routes/wearable' },
|
|
29
30
|
{ basePath: '/api/mobile/health', modulePath: '../routes/mobile-health' },
|
|
30
31
|
{ basePath: '/api/screen-history', modulePath: '../routes/screenHistory' },
|
|
31
32
|
{ basePath: '/api/triggers', modulePath: '../routes/triggers' }
|
package/server/http/static.js
CHANGED
|
@@ -7,6 +7,35 @@ const { DATA_DIR } = require('../../runtime/paths');
|
|
|
7
7
|
const { requireAuth } = require('../middleware/auth');
|
|
8
8
|
|
|
9
9
|
const FLUTTER_WEB_DIR = path.join(__dirname, '..', 'public');
|
|
10
|
+
const BUILD_ID_PATH = path.join(FLUTTER_WEB_DIR, '.last_build_id');
|
|
11
|
+
|
|
12
|
+
function setNoStoreHeaders(res) {
|
|
13
|
+
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
14
|
+
res.set('Pragma', 'no-cache');
|
|
15
|
+
res.set('Expires', '0');
|
|
16
|
+
res.set('Surrogate-Control', 'no-store');
|
|
17
|
+
res.set('CDN-Cache-Control', 'no-store');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function readCurrentBuildId() {
|
|
21
|
+
try {
|
|
22
|
+
return fs.readFileSync(BUILD_ID_PATH, 'utf8').trim() || null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function setFlutterStaticHeaders(res, filePath) {
|
|
29
|
+
const relativePath = path.relative(FLUTTER_WEB_DIR, filePath).replaceAll('\\', '/');
|
|
30
|
+
if (
|
|
31
|
+
relativePath === 'index.html' ||
|
|
32
|
+
relativePath === 'flutter_bootstrap.js' ||
|
|
33
|
+
relativePath === 'flutter_service_worker.js' ||
|
|
34
|
+
relativePath === 'version.json'
|
|
35
|
+
) {
|
|
36
|
+
setNoStoreHeaders(res);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
10
39
|
|
|
11
40
|
function registerStaticRoutes(app) {
|
|
12
41
|
app.use(
|
|
@@ -27,7 +56,17 @@ function registerStaticRoutes(app) {
|
|
|
27
56
|
express.static(path.join(DATA_DIR, 'screenshots'))
|
|
28
57
|
);
|
|
29
58
|
|
|
30
|
-
app.
|
|
59
|
+
app.get('/app-build.json', (req, res) => {
|
|
60
|
+
setNoStoreHeaders(res);
|
|
61
|
+
res.json({
|
|
62
|
+
buildId: readCurrentBuildId(),
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
app.use(express.static(FLUTTER_WEB_DIR, {
|
|
67
|
+
index: false,
|
|
68
|
+
setHeaders: setFlutterStaticHeaders,
|
|
69
|
+
}));
|
|
31
70
|
app.get(/^\/(?!api|screenshots|telnyx-audio).*/, serveFlutterApp);
|
|
32
71
|
}
|
|
33
72
|
|
|
@@ -40,6 +79,7 @@ function serveFlutterApp(req, res) {
|
|
|
40
79
|
'Flutter web build not found. Run "npm run flutter:build:web" to generate the bundled client.'
|
|
41
80
|
);
|
|
42
81
|
}
|
|
82
|
+
setNoStoreHeaders(res);
|
|
43
83
|
return res.sendFile(entry);
|
|
44
84
|
}
|
|
45
85
|
|
package/server/index.js
CHANGED
|
@@ -35,6 +35,7 @@ const { registerErrorHandler } = require('./http/errors');
|
|
|
35
35
|
const { startServices, stopServices } = require('./services/manager');
|
|
36
36
|
const { bindBrowserExtensionGateway } = require('./services/browser/extension/gateway');
|
|
37
37
|
const { bindDesktopCompanionGateway } = require('./services/desktop/gateway');
|
|
38
|
+
const { bindWearableGateway } = require('./services/wearable/gateway');
|
|
38
39
|
|
|
39
40
|
function parseBooleanFlag(value, fallback = false) {
|
|
40
41
|
const normalized = String(value || '').trim().toLowerCase();
|
|
@@ -97,6 +98,7 @@ if (!configuredSessionSecret()) {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
const app = express();
|
|
101
|
+
app.disable('x-powered-by');
|
|
100
102
|
const httpServer = createServer(app);
|
|
101
103
|
const io = createSocketServer(httpServer, { validateOrigin });
|
|
102
104
|
app.locals.httpRuntimeConfig = {
|
|
@@ -123,6 +125,7 @@ registerStaticRoutes(app);
|
|
|
123
125
|
registerErrorHandler(app);
|
|
124
126
|
bindBrowserExtensionGateway(httpServer, app);
|
|
125
127
|
bindDesktopCompanionGateway(httpServer, app, sessionMiddleware);
|
|
128
|
+
bindWearableGateway(httpServer, app, sessionMiddleware);
|
|
126
129
|
|
|
127
130
|
let shuttingDown = false;
|
|
128
131
|
let shutdownExitCode = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5688ec90b02e934df4a3c734aec6f6ab
|