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
package/lib/manager.js
CHANGED
|
@@ -542,6 +542,40 @@ async function cmdSetup() {
|
|
|
542
542
|
const normalizedDeploymentMode = parseDeploymentMode(deploymentMode);
|
|
543
543
|
const normalizedReleaseChannel = parseReleaseChannel(releaseChannel) || 'stable';
|
|
544
544
|
|
|
545
|
+
const githubOauthClientId = await askSecret(
|
|
546
|
+
'GitHub OAuth client ID',
|
|
547
|
+
current.GITHUB_OAUTH_CLIENT_ID || ''
|
|
548
|
+
);
|
|
549
|
+
const githubOauthClientSecret = await askSecret(
|
|
550
|
+
'GitHub OAuth client secret',
|
|
551
|
+
current.GITHUB_OAUTH_CLIENT_SECRET || ''
|
|
552
|
+
);
|
|
553
|
+
const githubOauthRedirectUri = await ask(
|
|
554
|
+
'GitHub OAuth redirect URI',
|
|
555
|
+
current.GITHUB_OAUTH_REDIRECT_URI || ''
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
const homeAssistantOauthClientId = await askSecret(
|
|
559
|
+
'Home Assistant OAuth client ID',
|
|
560
|
+
current.HOME_ASSISTANT_OAUTH_CLIENT_ID || ''
|
|
561
|
+
);
|
|
562
|
+
const homeAssistantOauthClientSecret = await askSecret(
|
|
563
|
+
'Home Assistant OAuth client secret',
|
|
564
|
+
current.HOME_ASSISTANT_OAUTH_CLIENT_SECRET || ''
|
|
565
|
+
);
|
|
566
|
+
const homeAssistantOauthRedirectUri = await ask(
|
|
567
|
+
'Home Assistant OAuth redirect URI',
|
|
568
|
+
current.HOME_ASSISTANT_OAUTH_REDIRECT_URI || ''
|
|
569
|
+
);
|
|
570
|
+
const homeAssistantBaseUrl = await ask(
|
|
571
|
+
'Home Assistant base URL (e.g., https://ha.example.com)',
|
|
572
|
+
current.HOME_ASSISTANT_BASE_URL || ''
|
|
573
|
+
);
|
|
574
|
+
const homeAssistantAllowPrivateUrl = current.HOME_ASSISTANT_ALLOW_PRIVATE_BASE_URL === '1' ? 'true' : await ask(
|
|
575
|
+
'Allow local/private Home Assistant base URLs? (true/false)',
|
|
576
|
+
'false'
|
|
577
|
+
);
|
|
578
|
+
|
|
545
579
|
const lines = [
|
|
546
580
|
`NODE_ENV=production`,
|
|
547
581
|
`PORT=${port}`,
|
|
@@ -576,6 +610,14 @@ async function cmdSetup() {
|
|
|
576
610
|
figmaOauthClientId ? `FIGMA_OAUTH_CLIENT_ID=${figmaOauthClientId}` : '',
|
|
577
611
|
figmaOauthClientSecret ? `FIGMA_OAUTH_CLIENT_SECRET=${figmaOauthClientSecret}` : '',
|
|
578
612
|
figmaOauthRedirectUri ? `FIGMA_OAUTH_REDIRECT_URI=${figmaOauthRedirectUri}` : '',
|
|
613
|
+
githubOauthClientId ? `GITHUB_OAUTH_CLIENT_ID=${githubOauthClientId}` : '',
|
|
614
|
+
githubOauthClientSecret ? `GITHUB_OAUTH_CLIENT_SECRET=${githubOauthClientSecret}` : '',
|
|
615
|
+
githubOauthRedirectUri ? `GITHUB_OAUTH_REDIRECT_URI=${githubOauthRedirectUri}` : '',
|
|
616
|
+
homeAssistantOauthClientId ? `HOME_ASSISTANT_OAUTH_CLIENT_ID=${homeAssistantOauthClientId}` : '',
|
|
617
|
+
homeAssistantOauthClientSecret ? `HOME_ASSISTANT_OAUTH_CLIENT_SECRET=${homeAssistantOauthClientSecret}` : '',
|
|
618
|
+
homeAssistantOauthRedirectUri ? `HOME_ASSISTANT_OAUTH_REDIRECT_URI=${homeAssistantOauthRedirectUri}` : '',
|
|
619
|
+
homeAssistantBaseUrl ? `HOME_ASSISTANT_BASE_URL=${homeAssistantBaseUrl}` : '',
|
|
620
|
+
String(homeAssistantAllowPrivateUrl || '').trim().toLowerCase() === 'true' ? `HOME_ASSISTANT_ALLOW_PRIVATE_BASE_URL=1` : '',
|
|
579
621
|
deepgramApiKey ? `DEEPGRAM_API_KEY=${deepgramApiKey}` : '',
|
|
580
622
|
deepgramBaseUrl ? `DEEPGRAM_BASE_URL=${deepgramBaseUrl}` : '',
|
|
581
623
|
deepgramModel ? `DEEPGRAM_MODEL=${deepgramModel}` : '',
|
|
@@ -674,6 +716,172 @@ async function cmdMigrate(args = []) {
|
|
|
674
716
|
});
|
|
675
717
|
}
|
|
676
718
|
|
|
719
|
+
async function cmdLogin(args = []) {
|
|
720
|
+
const provider = args[0];
|
|
721
|
+
if (provider !== 'github-copilot' && provider !== 'openai-codex') {
|
|
722
|
+
throw new Error(`Unsupported login provider: ${provider || 'none'}. Available: github-copilot, openai-codex`);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (provider === 'github-copilot') {
|
|
726
|
+
heading('GitHub Copilot Login');
|
|
727
|
+
const clientId = '01ab8ac9400c4e429b23';
|
|
728
|
+
logInfo('Requesting device code from GitHub...');
|
|
729
|
+
|
|
730
|
+
const reqRes = await fetch('https://github.com/login/device/code', {
|
|
731
|
+
method: 'POST',
|
|
732
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
733
|
+
body: JSON.stringify({ client_id: clientId, scope: 'user:email' })
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
if (!reqRes.ok) {
|
|
737
|
+
throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const { device_code, user_code, verification_uri, interval } = await reqRes.json();
|
|
741
|
+
|
|
742
|
+
console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
|
|
743
|
+
console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
|
|
744
|
+
|
|
745
|
+
logInfo('Waiting for authorization (timeout in 15m)...');
|
|
746
|
+
const startTime = Date.now();
|
|
747
|
+
const timeoutMs = 15 * 60 * 1000;
|
|
748
|
+
let currentPollInterval = (interval || 5) * 1000;
|
|
749
|
+
|
|
750
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
751
|
+
await new Promise((r) => setTimeout(r, currentPollInterval));
|
|
752
|
+
const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
|
|
753
|
+
method: 'POST',
|
|
754
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
755
|
+
body: JSON.stringify({
|
|
756
|
+
client_id: clientId,
|
|
757
|
+
device_code,
|
|
758
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
|
|
759
|
+
})
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
if (!tokenRes.ok) {
|
|
763
|
+
const errorText = await tokenRes.text().catch(() => 'Unknown error');
|
|
764
|
+
throw new Error(`GitHub token request failed: HTTP ${tokenRes.status} - ${errorText}`);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const data = await tokenRes.json();
|
|
768
|
+
if (data.access_token) {
|
|
769
|
+
upsertEnvValue('GITHUB_COPILOT_ACCESS_TOKEN', data.access_token);
|
|
770
|
+
logOk('Successfully authenticated and saved GitHub Copilot access token to .env');
|
|
771
|
+
logInfo('Applying updated provider credentials by restarting NeoAgent...');
|
|
772
|
+
cmdRestart();
|
|
773
|
+
return;
|
|
774
|
+
} else if (data.error === 'authorization_pending') {
|
|
775
|
+
// Continue polling
|
|
776
|
+
} else if (data.error === 'slow_down') {
|
|
777
|
+
currentPollInterval += 5000;
|
|
778
|
+
} else if (data.error) {
|
|
779
|
+
throw new Error(`Authentication failed: ${data.error_description || data.error}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
throw new Error('GitHub authentication timed out after 15 minutes.');
|
|
783
|
+
} else if (provider === 'openai-codex') {
|
|
784
|
+
heading('OpenAI Codex Login');
|
|
785
|
+
const clientId = 'app_EMoamEEZ73f0CkXaXp7hrann';
|
|
786
|
+
logInfo('Requesting device code from OpenAI...');
|
|
787
|
+
|
|
788
|
+
const reqRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/usercode', {
|
|
789
|
+
method: 'POST',
|
|
790
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
791
|
+
body: JSON.stringify({ client_id: clientId, scope: 'openid profile email offline_access model.request model.read model.create' })
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
if (!reqRes.ok) {
|
|
795
|
+
throw new Error(`Failed to request device code: HTTP ${reqRes.status}`);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const data = await reqRes.json();
|
|
799
|
+
const { device_auth_id, interval } = data;
|
|
800
|
+
const user_code = data.user_code || data.usercode;
|
|
801
|
+
const verification_uri = 'https://auth.openai.com/codex/device';
|
|
802
|
+
|
|
803
|
+
console.log(`\n ${COLORS.cyan}Please visit:${COLORS.reset} ${verification_uri}`);
|
|
804
|
+
console.log(` ${COLORS.cyan}And enter the code:${COLORS.reset} ${COLORS.bold}${user_code}${COLORS.reset}\n`);
|
|
805
|
+
|
|
806
|
+
logInfo('Waiting for authorization (timeout in 15m)...');
|
|
807
|
+
const startTime = Date.now();
|
|
808
|
+
const timeoutMs = 15 * 60 * 1000;
|
|
809
|
+
let currentPollInterval = (interval || 5) * 1000;
|
|
810
|
+
let authorizationCode = null;
|
|
811
|
+
let codeVerifier = null;
|
|
812
|
+
|
|
813
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
814
|
+
await new Promise((r) => setTimeout(r, currentPollInterval));
|
|
815
|
+
const tokenRes = await fetch('https://auth.openai.com/api/accounts/deviceauth/token', {
|
|
816
|
+
method: 'POST',
|
|
817
|
+
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
|
818
|
+
body: JSON.stringify({
|
|
819
|
+
device_auth_id: device_auth_id,
|
|
820
|
+
user_code: user_code
|
|
821
|
+
})
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
if (tokenRes.status === 403 || tokenRes.status === 404) {
|
|
825
|
+
// These statuses are returned by OpenAI while authorization is pending
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
if (!tokenRes.ok) {
|
|
830
|
+
const errorText = await tokenRes.text().catch(() => 'Unknown error');
|
|
831
|
+
throw new Error(`OpenAI token request failed: HTTP ${tokenRes.status} - ${errorText}`);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const pollData = await tokenRes.json();
|
|
835
|
+
if (pollData.authorization_code && pollData.code_verifier) {
|
|
836
|
+
authorizationCode = pollData.authorization_code;
|
|
837
|
+
codeVerifier = pollData.code_verifier;
|
|
838
|
+
break;
|
|
839
|
+
} else if (pollData.error === 'authorization_pending') {
|
|
840
|
+
// Continue polling
|
|
841
|
+
} else if (pollData.error === 'slow_down') {
|
|
842
|
+
currentPollInterval += 5000;
|
|
843
|
+
} else if (pollData.error) {
|
|
844
|
+
throw new Error(`Authentication failed: ${pollData.error_description || pollData.error}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
if (!authorizationCode || !codeVerifier) {
|
|
849
|
+
throw new Error('OpenAI authentication timed out after 15 minutes.');
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
logInfo('Exchanging authorization code for access token...');
|
|
853
|
+
const exchangeRes = await fetch('https://auth.openai.com/oauth/token', {
|
|
854
|
+
method: 'POST',
|
|
855
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
856
|
+
body: new URLSearchParams({
|
|
857
|
+
grant_type: 'authorization_code',
|
|
858
|
+
code: authorizationCode,
|
|
859
|
+
redirect_uri: 'https://auth.openai.com/deviceauth/callback',
|
|
860
|
+
client_id: clientId,
|
|
861
|
+
code_verifier: codeVerifier
|
|
862
|
+
})
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
if (!exchangeRes.ok) {
|
|
866
|
+
const errorText = await exchangeRes.text().catch(() => 'Unknown error');
|
|
867
|
+
throw new Error(`OpenAI token exchange failed: HTTP ${exchangeRes.status} - ${errorText}`);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const exchangeData = await exchangeRes.json();
|
|
871
|
+
if (exchangeData.access_token) {
|
|
872
|
+
upsertEnvValue('OPENAI_CODEX_ACCESS_TOKEN', exchangeData.access_token);
|
|
873
|
+
if (exchangeData.refresh_token) {
|
|
874
|
+
upsertEnvValue('OPENAI_CODEX_REFRESH_TOKEN', exchangeData.refresh_token);
|
|
875
|
+
}
|
|
876
|
+
logOk('Successfully authenticated and saved OpenAI Codex tokens to .env');
|
|
877
|
+
logInfo('Applying updated provider credentials by restarting NeoAgent...');
|
|
878
|
+
cmdRestart();
|
|
879
|
+
} else {
|
|
880
|
+
throw new Error('OpenAI token exchange succeeded but did not return an access token.');
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
677
885
|
function installDependencies() {
|
|
678
886
|
heading('Dependencies');
|
|
679
887
|
runOrThrow('npm', ['install', '--omit=dev', '--no-audit', '--no-fund'], {
|
|
@@ -867,6 +1075,11 @@ function cmdRestart() {
|
|
|
867
1075
|
cmdStart();
|
|
868
1076
|
}
|
|
869
1077
|
|
|
1078
|
+
async function cmdRebuildWeb() {
|
|
1079
|
+
heading(`Rebuild Flutter Web Client`);
|
|
1080
|
+
buildBundledWebClientIfPossible();
|
|
1081
|
+
}
|
|
1082
|
+
|
|
870
1083
|
function cmdUninstall() {
|
|
871
1084
|
heading(`Uninstall ${APP_NAME}`);
|
|
872
1085
|
const platform = detectPlatform();
|
|
@@ -962,6 +1175,8 @@ function cmdUpdate(args = []) {
|
|
|
962
1175
|
}
|
|
963
1176
|
const versionBefore = currentInstalledVersionLabel();
|
|
964
1177
|
let versionAfter = versionBefore;
|
|
1178
|
+
const githubInstallRef = releaseChannel === 'beta' ? '#beta' : '';
|
|
1179
|
+
const githubInstallSpec = `git+https://github.com/NeoLabs-Systems/NeoAgent.git${githubInstallRef}`;
|
|
965
1180
|
|
|
966
1181
|
if (fs.existsSync(path.join(APP_DIR, '.git')) && commandExists('git')) {
|
|
967
1182
|
const current = runQuiet('git', ['rev-parse', '--short', 'HEAD']);
|
|
@@ -982,17 +1197,16 @@ function cmdUpdate(args = []) {
|
|
|
982
1197
|
buildBundledWebClientIfPossible();
|
|
983
1198
|
}
|
|
984
1199
|
} else {
|
|
985
|
-
|
|
986
|
-
logWarn(`No git repo detected; attempting npm global update from ${npmTag}.`);
|
|
1200
|
+
logWarn(`No git repo detected; attempting npm global update from ${githubInstallSpec}.`);
|
|
987
1201
|
if (commandExists('npm')) {
|
|
988
1202
|
try {
|
|
989
1203
|
backupRuntimeData();
|
|
990
|
-
runOrThrow('npm', ['install', '-g',
|
|
1204
|
+
runOrThrow('npm', ['install', '-g', githubInstallSpec, '--force'], {
|
|
991
1205
|
env: withInstallEnv()
|
|
992
1206
|
});
|
|
993
|
-
logOk('npm global update completed (forced reinstall)');
|
|
1207
|
+
logOk('npm global update completed (forced reinstall from GitHub)');
|
|
994
1208
|
} catch {
|
|
995
|
-
logWarn(`npm global update failed. Run: npm install -g
|
|
1209
|
+
logWarn(`npm global update failed. Run: npm install -g ${githubInstallSpec} --force`);
|
|
996
1210
|
}
|
|
997
1211
|
} else {
|
|
998
1212
|
logWarn('npm not found. Cannot perform global update.');
|
|
@@ -1062,7 +1276,8 @@ async function cmdEnv(args = []) {
|
|
|
1062
1276
|
function printHelp() {
|
|
1063
1277
|
console.log(`${APP_NAME} manager`);
|
|
1064
1278
|
console.log('Usage: neoagent <command>');
|
|
1065
|
-
console.log('Commands: install | setup | env | channel | update | restart | start | stop | status | logs | uninstall | migrate');
|
|
1279
|
+
console.log('Commands: install | setup | env | channel | update | restart | rebuild-web | start | stop | status | logs | uninstall | migrate | login');
|
|
1280
|
+
console.log('Login usage: neoagent login github-copilot | neoagent login openai-codex');
|
|
1066
1281
|
console.log('Channel usage: neoagent channel | neoagent channel stable | neoagent channel beta');
|
|
1067
1282
|
console.log('Update usage: neoagent update | neoagent update stable | neoagent update beta');
|
|
1068
1283
|
console.log('Env usage: neoagent env list | neoagent env get PORT | neoagent env set PORT 3333 | neoagent env unset PORT');
|
|
@@ -1094,6 +1309,9 @@ async function runCLI(argv) {
|
|
|
1094
1309
|
case 'restart':
|
|
1095
1310
|
cmdRestart();
|
|
1096
1311
|
break;
|
|
1312
|
+
case 'rebuild-web':
|
|
1313
|
+
await cmdRebuildWeb();
|
|
1314
|
+
break;
|
|
1097
1315
|
case 'start':
|
|
1098
1316
|
cmdStart();
|
|
1099
1317
|
break;
|
|
@@ -1112,6 +1330,9 @@ async function runCLI(argv) {
|
|
|
1112
1330
|
case 'migrate':
|
|
1113
1331
|
await cmdMigrate(argv.slice(1));
|
|
1114
1332
|
break;
|
|
1333
|
+
case 'login':
|
|
1334
|
+
await cmdLogin(argv.slice(1));
|
|
1335
|
+
break;
|
|
1115
1336
|
case 'help':
|
|
1116
1337
|
case '--help':
|
|
1117
1338
|
case '-h':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neoagent",
|
|
3
|
-
"version": "2.3.1-beta.
|
|
3
|
+
"version": "2.3.1-beta.41",
|
|
4
4
|
"description": "Proactive personal AI agent with no limits",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"runtime",
|
|
17
17
|
"server",
|
|
18
18
|
"extensions",
|
|
19
|
+
"flutter_app",
|
|
19
20
|
"docs",
|
|
20
21
|
"com.neoagent.plist",
|
|
21
22
|
"LICENSE",
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"puppeteer-core": "^24.40.0",
|
|
77
78
|
"puppeteer-extra": "^3.3.6",
|
|
78
79
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
80
|
+
"qrcode": "^1.5.4",
|
|
79
81
|
"sharp": "^0.34.5",
|
|
80
82
|
"socket.io": "^4.8.1",
|
|
81
83
|
"telegraf": "^4.16.3",
|
package/server/db/database.js
CHANGED
|
@@ -272,6 +272,19 @@ db.exec(`
|
|
|
272
272
|
FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL
|
|
273
273
|
);
|
|
274
274
|
|
|
275
|
+
CREATE TABLE IF NOT EXISTS integration_provider_configs (
|
|
276
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
277
|
+
user_id INTEGER NOT NULL,
|
|
278
|
+
agent_id TEXT,
|
|
279
|
+
provider_key TEXT NOT NULL,
|
|
280
|
+
config_json TEXT DEFAULT '{}',
|
|
281
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
282
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
283
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
284
|
+
FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
|
|
285
|
+
UNIQUE(user_id, agent_id, provider_key)
|
|
286
|
+
);
|
|
287
|
+
|
|
275
288
|
CREATE TABLE IF NOT EXISTS browser_extension_pairing_requests (
|
|
276
289
|
id TEXT PRIMARY KEY,
|
|
277
290
|
user_id INTEGER,
|
|
@@ -875,6 +888,7 @@ function createAgentScopedIndexes() {
|
|
|
875
888
|
const statements = [
|
|
876
889
|
['agent_runs', 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent ON agent_runs(user_id, agent_id, created_at DESC)'],
|
|
877
890
|
['integration_connections', 'CREATE INDEX IF NOT EXISTS idx_integration_connections_agent ON integration_connections(user_id, agent_id, provider_key, app_key)'],
|
|
891
|
+
['integration_provider_configs', 'CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent ON integration_provider_configs(user_id, agent_id, provider_key)'],
|
|
878
892
|
['messages', 'CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(user_id, agent_id, created_at DESC)'],
|
|
879
893
|
['conversations', 'CREATE INDEX IF NOT EXISTS idx_conversations_agent ON conversations(user_id, agent_id, updated_at DESC)'],
|
|
880
894
|
['scheduled_tasks', 'CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_agent ON scheduled_tasks(user_id, agent_id)'],
|
|
@@ -922,6 +936,7 @@ function backfillAgentIds() {
|
|
|
922
936
|
'platform_connections',
|
|
923
937
|
'mcp_servers',
|
|
924
938
|
'integration_connections',
|
|
939
|
+
'integration_provider_configs',
|
|
925
940
|
'integration_oauth_states',
|
|
926
941
|
'scheduled_tasks',
|
|
927
942
|
'conversations',
|
|
@@ -1303,6 +1318,83 @@ function migrateIntegrationOauthStatesTable() {
|
|
|
1303
1318
|
`);
|
|
1304
1319
|
}
|
|
1305
1320
|
|
|
1321
|
+
function migrateIntegrationProviderConfigsTable() {
|
|
1322
|
+
if (
|
|
1323
|
+
tableHasColumn('integration_provider_configs', 'agent_id') &&
|
|
1324
|
+
tableHasUniqueIndex('integration_provider_configs', ['user_id', 'agent_id', 'provider_key'])
|
|
1325
|
+
) {
|
|
1326
|
+
const users = db.prepare('SELECT id FROM users').all();
|
|
1327
|
+
for (const user of users) {
|
|
1328
|
+
const agentId = getMainAgentId(user.id);
|
|
1329
|
+
db.prepare(
|
|
1330
|
+
'UPDATE integration_provider_configs SET agent_id = ? WHERE user_id = ? AND agent_id IS NULL'
|
|
1331
|
+
).run(agentId, user.id);
|
|
1332
|
+
}
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
const rows = db
|
|
1337
|
+
.prepare('SELECT * FROM integration_provider_configs ORDER BY id ASC')
|
|
1338
|
+
.all();
|
|
1339
|
+
|
|
1340
|
+
db.exec('BEGIN');
|
|
1341
|
+
try {
|
|
1342
|
+
db.exec(`
|
|
1343
|
+
ALTER TABLE integration_provider_configs RENAME TO integration_provider_configs_legacy_agent_scope;
|
|
1344
|
+
|
|
1345
|
+
CREATE TABLE integration_provider_configs (
|
|
1346
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1347
|
+
user_id INTEGER NOT NULL,
|
|
1348
|
+
agent_id TEXT,
|
|
1349
|
+
provider_key TEXT NOT NULL,
|
|
1350
|
+
config_json TEXT DEFAULT '{}',
|
|
1351
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
1352
|
+
updated_at TEXT DEFAULT (datetime('now')),
|
|
1353
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
1354
|
+
FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE SET NULL,
|
|
1355
|
+
UNIQUE(user_id, agent_id, provider_key)
|
|
1356
|
+
);
|
|
1357
|
+
`);
|
|
1358
|
+
const insert = db.prepare(`
|
|
1359
|
+
INSERT OR REPLACE INTO integration_provider_configs (
|
|
1360
|
+
id,
|
|
1361
|
+
user_id,
|
|
1362
|
+
agent_id,
|
|
1363
|
+
provider_key,
|
|
1364
|
+
config_json,
|
|
1365
|
+
created_at,
|
|
1366
|
+
updated_at
|
|
1367
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1368
|
+
`);
|
|
1369
|
+
|
|
1370
|
+
for (const row of rows) {
|
|
1371
|
+
insert.run(
|
|
1372
|
+
row.id,
|
|
1373
|
+
row.user_id,
|
|
1374
|
+
row.agent_id || getMainAgentId(row.user_id),
|
|
1375
|
+
row.provider_key,
|
|
1376
|
+
row.config_json,
|
|
1377
|
+
row.created_at,
|
|
1378
|
+
row.updated_at,
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
db.exec(`
|
|
1383
|
+
DROP TABLE integration_provider_configs_legacy_agent_scope;
|
|
1384
|
+
CREATE INDEX IF NOT EXISTS idx_integration_provider_configs_agent
|
|
1385
|
+
ON integration_provider_configs(user_id, agent_id, provider_key);
|
|
1386
|
+
`);
|
|
1387
|
+
db.exec('COMMIT');
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
try {
|
|
1390
|
+
db.exec('ROLLBACK');
|
|
1391
|
+
} catch {
|
|
1392
|
+
// Ignore rollback errors and rethrow the original migration failure.
|
|
1393
|
+
}
|
|
1394
|
+
throw error;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1306
1398
|
function migrateIntegrationSecretStorage() {
|
|
1307
1399
|
try {
|
|
1308
1400
|
const connectionRows = db
|
|
@@ -1335,6 +1427,22 @@ function migrateIntegrationSecretStorage() {
|
|
|
1335
1427
|
} catch {
|
|
1336
1428
|
// Preserve startup even if a row cannot be re-encrypted.
|
|
1337
1429
|
}
|
|
1430
|
+
|
|
1431
|
+
try {
|
|
1432
|
+
const providerRows = db
|
|
1433
|
+
.prepare('SELECT id, config_json FROM integration_provider_configs')
|
|
1434
|
+
.all();
|
|
1435
|
+
const updateProviderConfig = db.prepare(
|
|
1436
|
+
'UPDATE integration_provider_configs SET config_json = ? WHERE id = ?',
|
|
1437
|
+
);
|
|
1438
|
+
for (const row of providerRows) {
|
|
1439
|
+
const current = String(row.config_json || '');
|
|
1440
|
+
if (!current || isEncryptedValue(current)) continue;
|
|
1441
|
+
updateProviderConfig.run(encryptValue(current), row.id);
|
|
1442
|
+
}
|
|
1443
|
+
} catch {
|
|
1444
|
+
// Preserve startup even if a row cannot be re-encrypted.
|
|
1445
|
+
}
|
|
1338
1446
|
}
|
|
1339
1447
|
|
|
1340
1448
|
function backfillVerifiedAccountEmails() {
|
|
@@ -1408,7 +1516,9 @@ rebuildPlatformConnectionsForAgents();
|
|
|
1408
1516
|
rebuildCoreMemoryForAgents();
|
|
1409
1517
|
migrateIntegrationConnectionsTable();
|
|
1410
1518
|
migrateIntegrationOauthStatesTable();
|
|
1519
|
+
migrateIntegrationProviderConfigsTable();
|
|
1411
1520
|
createAgentScopedIndexes();
|
|
1521
|
+
backfillAgentIds();
|
|
1412
1522
|
migrateIntegrationSecretStorage();
|
|
1413
1523
|
backfillVerifiedAccountEmails();
|
|
1414
1524
|
rebuildFtsForAgents();
|
|
@@ -68,6 +68,9 @@ ensureSessionStoreSchema(sessionsDb);
|
|
|
68
68
|
|
|
69
69
|
function buildHelmetOptions({ secureCookies }) {
|
|
70
70
|
const wsConnectSrc = secureCookies ? ['wss:'] : ['ws:', 'wss:'];
|
|
71
|
+
const isDevelopment = String(process.env.NODE_ENV || '').trim() === 'development';
|
|
72
|
+
// Flutter web (CanvasKit) requires external script/connect sources and wasm eval.
|
|
73
|
+
// Keep strict overrides available via env for hardened deployments.
|
|
71
74
|
const allowUnsafeEval = boolEnv('NEOAGENT_CSP_ALLOW_UNSAFE_EVAL', true);
|
|
72
75
|
const allowExternalScriptCdn = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_SCRIPT_CDN', true);
|
|
73
76
|
const allowExternalConnect = boolEnv('NEOAGENT_CSP_ALLOW_EXTERNAL_CONNECT', true);
|
|
@@ -82,20 +85,31 @@ function buildHelmetOptions({ secureCookies }) {
|
|
|
82
85
|
|
|
83
86
|
const connectSrc = ["'self'", ...wsConnectSrc];
|
|
84
87
|
if (allowExternalConnect) {
|
|
85
|
-
connectSrc.push('https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://www.gstatic.com', 'https://
|
|
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/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
|
+
31a20dcc543d16b6423454439e96e69a
|
|
@@ -5556,7 +5556,6 @@ path_provider_platform_interface
|
|
|
5556
5556
|
path_provider_windows
|
|
5557
5557
|
plugin_platform_interface
|
|
5558
5558
|
shared_preferences_linux
|
|
5559
|
-
shared_preferences_platform_interface
|
|
5560
5559
|
shared_preferences_web
|
|
5561
5560
|
shared_preferences_windows
|
|
5562
5561
|
xdg_directories
|
|
@@ -10835,6 +10834,7 @@ path_provider_foundation
|
|
|
10835
10834
|
shared_preferences
|
|
10836
10835
|
shared_preferences_android
|
|
10837
10836
|
shared_preferences_foundation
|
|
10837
|
+
shared_preferences_platform_interface
|
|
10838
10838
|
|
|
10839
10839
|
Copyright 2013 The Flutter Authors
|
|
10840
10840
|
|
|
Binary file
|
|
Binary file
|
|
@@ -33,10 +33,10 @@ addEventListener("message", eventListener);
|
|
|
33
33
|
if (!window._flutter) {
|
|
34
34
|
window._flutter = {};
|
|
35
35
|
}
|
|
36
|
-
_flutter.buildConfig = {"engineRevision":"
|
|
36
|
+
_flutter.buildConfig = {"engineRevision":"42d3d75a56efe1a2e9902f52dc8006099c45d937","builds":[{"compileTarget":"dart2js","renderer":"canvaskit","mainJsPath":"main.dart.js"},{}]};
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "977642556" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|