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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
|
+
const QRCode = require('qrcode');
|
|
2
3
|
const router = express.Router();
|
|
3
4
|
const { requireAuth } = require('../middleware/auth');
|
|
4
5
|
const { sanitizeError } = require('../utils/security');
|
|
@@ -143,6 +144,26 @@ router.get('/oauth/callback', async (req, res) => {
|
|
|
143
144
|
|
|
144
145
|
router.use(requireAuth);
|
|
145
146
|
|
|
147
|
+
router.get('/qr-image', async (req, res) => {
|
|
148
|
+
try {
|
|
149
|
+
const data = String(req.query.data || '').trim();
|
|
150
|
+
if (!data) {
|
|
151
|
+
return res.status(400).send('Missing QR data.');
|
|
152
|
+
}
|
|
153
|
+
const svg = await QRCode.toString(data, {
|
|
154
|
+
errorCorrectionLevel: 'M',
|
|
155
|
+
margin: 1,
|
|
156
|
+
type: 'svg',
|
|
157
|
+
width: 320,
|
|
158
|
+
});
|
|
159
|
+
res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
|
|
160
|
+
res.setHeader('Cache-Control', 'no-store, max-age=0');
|
|
161
|
+
return res.send(svg);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
return res.status(400).send(escapeHtml(sanitizeError(err)));
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
146
167
|
router.get('/:provider/connect/:sessionId', (req, res) => {
|
|
147
168
|
try {
|
|
148
169
|
const manager = getIntegrationManager(req);
|
|
@@ -211,7 +232,7 @@ router.get('/:provider/connect/:sessionId', (req, res) => {
|
|
|
211
232
|
const status = String(data.status || 'connecting');
|
|
212
233
|
if (status === 'awaiting_qr' && data.qr) {
|
|
213
234
|
statusEl.textContent = 'Scan this QR code to continue linking.';
|
|
214
|
-
qrEl.src = '
|
|
235
|
+
qrEl.src = '/api/integrations/qr-image?data=' + encodeURIComponent(data.qr);
|
|
215
236
|
qrEl.style.display = 'block';
|
|
216
237
|
} else if (status === 'connected') {
|
|
217
238
|
qrEl.style.display = 'none';
|
|
@@ -361,4 +382,90 @@ router.get('/:provider/tools/status', (req, res) => {
|
|
|
361
382
|
}
|
|
362
383
|
});
|
|
363
384
|
|
|
385
|
+
router.get('/:provider/config', (req, res) => {
|
|
386
|
+
try {
|
|
387
|
+
const manager = getIntegrationManager(req);
|
|
388
|
+
if (!manager) {
|
|
389
|
+
throw new Error('Official integration manager is not available on app.locals.integrationManager.');
|
|
390
|
+
}
|
|
391
|
+
const provider = manager.getProvider(req.params.provider);
|
|
392
|
+
if (!provider) {
|
|
393
|
+
throw new Error(`Unknown integration provider: ${req.params.provider}`);
|
|
394
|
+
}
|
|
395
|
+
if (typeof provider.getUserConfig !== 'function') {
|
|
396
|
+
return res.status(404).json({
|
|
397
|
+
error: `${provider.label} does not support per-user configuration.`,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
const config = provider.getUserConfig({
|
|
401
|
+
userId: req.session.userId,
|
|
402
|
+
agentId: resolveAgentId(req.session.userId, getAgentIdFromRequest(req)),
|
|
403
|
+
});
|
|
404
|
+
res.json({
|
|
405
|
+
provider: provider.key,
|
|
406
|
+
config,
|
|
407
|
+
});
|
|
408
|
+
} catch (err) {
|
|
409
|
+
res.status(400).json({ error: sanitizeError(err) });
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
router.put('/:provider/config', async (req, res) => {
|
|
414
|
+
try {
|
|
415
|
+
const manager = getIntegrationManager(req);
|
|
416
|
+
if (!manager) {
|
|
417
|
+
throw new Error('Official integration manager is not available on app.locals.integrationManager.');
|
|
418
|
+
}
|
|
419
|
+
const provider = manager.getProvider(req.params.provider);
|
|
420
|
+
if (!provider) {
|
|
421
|
+
throw new Error(`Unknown integration provider: ${req.params.provider}`);
|
|
422
|
+
}
|
|
423
|
+
if (typeof provider.saveUserConfig !== 'function') {
|
|
424
|
+
return res.status(404).json({
|
|
425
|
+
error: `${provider.label} does not support per-user configuration.`,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const config = await provider.saveUserConfig({
|
|
429
|
+
userId: req.session.userId,
|
|
430
|
+
agentId: resolveAgentId(req.session.userId, getAgentIdFromRequest(req)),
|
|
431
|
+
config: req.body?.config || req.body || {},
|
|
432
|
+
});
|
|
433
|
+
res.json({
|
|
434
|
+
provider: provider.key,
|
|
435
|
+
config,
|
|
436
|
+
saved: true,
|
|
437
|
+
});
|
|
438
|
+
} catch (err) {
|
|
439
|
+
res.status(400).json({ error: sanitizeError(err) });
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
router.delete('/:provider/config', async (req, res) => {
|
|
444
|
+
try {
|
|
445
|
+
const manager = getIntegrationManager(req);
|
|
446
|
+
if (!manager) {
|
|
447
|
+
throw new Error('Official integration manager is not available on app.locals.integrationManager.');
|
|
448
|
+
}
|
|
449
|
+
const provider = manager.getProvider(req.params.provider);
|
|
450
|
+
if (!provider) {
|
|
451
|
+
throw new Error(`Unknown integration provider: ${req.params.provider}`);
|
|
452
|
+
}
|
|
453
|
+
if (typeof provider.clearUserConfig !== 'function') {
|
|
454
|
+
return res.status(404).json({
|
|
455
|
+
error: `${provider.label} does not support per-user configuration.`,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
await provider.clearUserConfig({
|
|
459
|
+
userId: req.session.userId,
|
|
460
|
+
agentId: resolveAgentId(req.session.userId, getAgentIdFromRequest(req)),
|
|
461
|
+
});
|
|
462
|
+
res.json({
|
|
463
|
+
provider: provider.key,
|
|
464
|
+
cleared: true,
|
|
465
|
+
});
|
|
466
|
+
} catch (err) {
|
|
467
|
+
res.status(400).json({ error: sanitizeError(err) });
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
364
471
|
module.exports = router;
|
package/server/routes/memory.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const router = express.Router();
|
|
3
|
+
const rateLimit = require('express-rate-limit');
|
|
3
4
|
const { requireAuth } = require('../middleware/auth');
|
|
4
5
|
const { sanitizeError } = require('../utils/security');
|
|
5
6
|
const { getAgentIdFromRequest, resolveAgentId } = require('../services/agents/manager');
|
|
6
7
|
|
|
7
8
|
router.use(requireAuth);
|
|
8
9
|
|
|
10
|
+
const apiKeyMutationLimiter = rateLimit({
|
|
11
|
+
windowMs: 15 * 60 * 1000,
|
|
12
|
+
max: 60,
|
|
13
|
+
message: { error: 'Too many API key update attempts, try again later' },
|
|
14
|
+
standardHeaders: true,
|
|
15
|
+
legacyHeaders: false,
|
|
16
|
+
});
|
|
17
|
+
|
|
9
18
|
function normalizeMemoryIds(value) {
|
|
10
19
|
return [...new Set(
|
|
11
20
|
(Array.isArray(value) ? value : [])
|
|
@@ -232,12 +241,12 @@ router.get('/api-keys', (req, res) => {
|
|
|
232
241
|
res.json(masked);
|
|
233
242
|
});
|
|
234
243
|
|
|
235
|
-
router.put('/api-keys/:service', (req, res) => {
|
|
244
|
+
router.put('/api-keys/:service', apiKeyMutationLimiter, (req, res) => {
|
|
236
245
|
req.app.locals.memoryManager.setApiKey(req.params.service, req.body.key, req.session.userId);
|
|
237
246
|
res.json({ success: true });
|
|
238
247
|
});
|
|
239
248
|
|
|
240
|
-
router.delete('/api-keys/:service', (req, res) => {
|
|
249
|
+
router.delete('/api-keys/:service', apiKeyMutationLimiter, (req, res) => {
|
|
241
250
|
req.app.locals.memoryManager.deleteApiKey(req.params.service, req.session.userId);
|
|
242
251
|
res.json({ success: true });
|
|
243
252
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const express = require('express');
|
|
4
|
-
const db = require('
|
|
5
|
-
const { getErrorMessage } = require('../bootstrap_helpers');
|
|
4
|
+
const db = require('../db/database');
|
|
5
|
+
const { getErrorMessage } = require('../services/bootstrap_helpers');
|
|
6
6
|
|
|
7
7
|
const router = express.Router();
|
|
8
8
|
|
|
@@ -21,6 +21,11 @@ const {
|
|
|
21
21
|
ensureDefaultAiSettings,
|
|
22
22
|
normalizeProviderConfigs,
|
|
23
23
|
} = require('../services/ai/settings');
|
|
24
|
+
const {
|
|
25
|
+
readMeshtasticEnabled,
|
|
26
|
+
setMeshtasticEnabled,
|
|
27
|
+
resetMeshtasticEnabled,
|
|
28
|
+
} = require('../services/messaging/meshtastic_env');
|
|
24
29
|
const {
|
|
25
30
|
ensureDefaultRuntimeSettings,
|
|
26
31
|
getRuntimeSettings,
|
|
@@ -75,6 +80,10 @@ const VOICE_SETTING_KEYS = new Set([
|
|
|
75
80
|
'voice_live_voice',
|
|
76
81
|
]);
|
|
77
82
|
|
|
83
|
+
const ENV_BACKED_SETTING_KEYS = new Set([
|
|
84
|
+
'meshtastic_enabled',
|
|
85
|
+
]);
|
|
86
|
+
|
|
78
87
|
function toOptionalTrimmedString(value) {
|
|
79
88
|
if (typeof value !== 'string') return undefined;
|
|
80
89
|
const trimmed = value.trim();
|
|
@@ -151,6 +160,49 @@ function applyHeadlessSetting(req, value) {
|
|
|
151
160
|
.catch(() => { });
|
|
152
161
|
}
|
|
153
162
|
|
|
163
|
+
function isEnvBackedSettingKey(key) {
|
|
164
|
+
return ENV_BACKED_SETTING_KEYS.has(key);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function readEnvBackedSettingValue(key) {
|
|
168
|
+
switch (key) {
|
|
169
|
+
case 'meshtastic_enabled':
|
|
170
|
+
return readMeshtasticEnabled();
|
|
171
|
+
default:
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function writeEnvBackedSettingValue(req, key, value) {
|
|
177
|
+
switch (key) {
|
|
178
|
+
case 'meshtastic_enabled': {
|
|
179
|
+
const enabled = setMeshtasticEnabled(value);
|
|
180
|
+
const manager = req.app?.locals?.messagingManager;
|
|
181
|
+
if (manager && typeof manager.updateMeshtasticEnabled === 'function') {
|
|
182
|
+
await manager.updateMeshtasticEnabled(enabled);
|
|
183
|
+
}
|
|
184
|
+
return enabled;
|
|
185
|
+
}
|
|
186
|
+
default:
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function resetEnvBackedSettingValue(req, key) {
|
|
192
|
+
switch (key) {
|
|
193
|
+
case 'meshtastic_enabled': {
|
|
194
|
+
resetMeshtasticEnabled();
|
|
195
|
+
const manager = req.app?.locals?.messagingManager;
|
|
196
|
+
if (manager && typeof manager.updateMeshtasticEnabled === 'function') {
|
|
197
|
+
await manager.updateMeshtasticEnabled(readMeshtasticEnabled());
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
default:
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
154
206
|
// Get supported models metadata
|
|
155
207
|
router.get('/meta/models', async (req, res) => {
|
|
156
208
|
const { getSupportedModels } = require('../services/ai/models');
|
|
@@ -209,11 +261,12 @@ router.get('/', (req, res) => {
|
|
|
209
261
|
}
|
|
210
262
|
settings.agentId = agentId;
|
|
211
263
|
settings.ai_provider_configs = normalizeProviderConfigs(settings.ai_provider_configs);
|
|
264
|
+
settings.meshtastic_enabled = readMeshtasticEnabled();
|
|
212
265
|
res.json(settings);
|
|
213
266
|
});
|
|
214
267
|
|
|
215
268
|
// Update settings (batch)
|
|
216
|
-
router.put('/', (req, res) => {
|
|
269
|
+
router.put('/', async (req, res) => {
|
|
217
270
|
const userId = req.session.userId;
|
|
218
271
|
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
219
272
|
ensureDefaultAiSettings(userId, agentId);
|
|
@@ -238,6 +291,12 @@ router.put('/', (req, res) => {
|
|
|
238
291
|
normalizedBody.ai_provider_configs = normalizeProviderConfigs(normalizedBody.ai_provider_configs);
|
|
239
292
|
}
|
|
240
293
|
|
|
294
|
+
for (const key of Object.keys(normalizedBody)) {
|
|
295
|
+
if (isEnvBackedSettingKey(key)) {
|
|
296
|
+
normalizedBody[key] = await writeEnvBackedSettingValue(req, key, normalizedBody[key]);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
241
300
|
if (
|
|
242
301
|
'runtime_profile' in normalizedBody
|
|
243
302
|
|| 'runtime_backend' in normalizedBody
|
|
@@ -264,6 +323,8 @@ router.put('/', (req, res) => {
|
|
|
264
323
|
const v = serializeRuntimeSettingValue(key, value);
|
|
265
324
|
if (isAgentScopedSettingKey(key)) {
|
|
266
325
|
upsertAgent.run(userId, agentId, key, v);
|
|
326
|
+
} else if (isEnvBackedSettingKey(key)) {
|
|
327
|
+
continue;
|
|
267
328
|
} else if (key !== 'agentId' && key !== 'agent_id') {
|
|
268
329
|
upsert.run(userId, key, v);
|
|
269
330
|
}
|
|
@@ -377,6 +438,9 @@ router.get('/token-usage/summary', (req, res) => {
|
|
|
377
438
|
|
|
378
439
|
// Get single setting
|
|
379
440
|
router.get('/:key', (req, res) => {
|
|
441
|
+
if (isEnvBackedSettingKey(req.params.key)) {
|
|
442
|
+
return res.json({ value: readEnvBackedSettingValue(req.params.key) });
|
|
443
|
+
}
|
|
380
444
|
const userId = req.session.userId;
|
|
381
445
|
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
382
446
|
ensureDefaultRuntimeSettings(userId);
|
|
@@ -401,11 +465,15 @@ router.get('/:key', (req, res) => {
|
|
|
401
465
|
});
|
|
402
466
|
|
|
403
467
|
// Set single setting
|
|
404
|
-
router.put('/:key', (req, res) => {
|
|
468
|
+
router.put('/:key', async (req, res) => {
|
|
405
469
|
const userId = req.session.userId;
|
|
406
470
|
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
407
471
|
ensureDefaultRuntimeSettings(userId);
|
|
408
472
|
let value = req.body.value;
|
|
473
|
+
if (isEnvBackedSettingKey(req.params.key)) {
|
|
474
|
+
const saved = await writeEnvBackedSettingValue(req, req.params.key, value);
|
|
475
|
+
return res.json({ success: true, value: saved });
|
|
476
|
+
}
|
|
409
477
|
if (req.params.key === 'platform_whitelist_whatsapp') {
|
|
410
478
|
if (typeof value === 'string') {
|
|
411
479
|
try {
|
|
@@ -467,6 +535,11 @@ router.put('/:key', (req, res) => {
|
|
|
467
535
|
|
|
468
536
|
// Delete setting
|
|
469
537
|
router.delete('/:key', (req, res) => {
|
|
538
|
+
if (isEnvBackedSettingKey(req.params.key)) {
|
|
539
|
+
return resetEnvBackedSettingValue(req, req.params.key)
|
|
540
|
+
.then(() => res.json({ success: true }))
|
|
541
|
+
.catch((err) => res.status(500).json({ success: false, error: err.message }));
|
|
542
|
+
}
|
|
470
543
|
const userId = req.session.userId;
|
|
471
544
|
const agentId = resolveAgentId(userId, getAgentIdFromRequest(req));
|
|
472
545
|
if (
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const express = require('express');
|
|
4
|
-
const db = require('
|
|
5
|
-
const { getErrorMessage } = require('../bootstrap_helpers');
|
|
4
|
+
const db = require('../db/database');
|
|
5
|
+
const { getErrorMessage } = require('../services/bootstrap_helpers');
|
|
6
6
|
const { requireAuth } = require('../middleware/auth');
|
|
7
7
|
|
|
8
8
|
const router = express.Router();
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const { requireAuth } = require('../middleware/auth');
|
|
5
|
+
const { sanitizeError } = require('../utils/security');
|
|
6
|
+
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
|
|
9
|
+
function wearableService(req) {
|
|
10
|
+
return req.app?.locals?.wearableService || null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Intentionally public so an already-provisioned wearable can sync local time
|
|
14
|
+
// before the user finishes session-based pairing on the device.
|
|
15
|
+
router.get('/timezone', (req, res) => {
|
|
16
|
+
try {
|
|
17
|
+
const service = wearableService(req);
|
|
18
|
+
if (!service) {
|
|
19
|
+
return res.status(500).json({ error: 'Wearable service unavailable.' });
|
|
20
|
+
}
|
|
21
|
+
res.json(service.buildTimeConfig(req));
|
|
22
|
+
} catch (err) {
|
|
23
|
+
res.status(400).json({ error: sanitizeError(err) });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
router.use(requireAuth);
|
|
28
|
+
|
|
29
|
+
router.get('/bootstrap', async (req, res) => {
|
|
30
|
+
try {
|
|
31
|
+
const service = wearableService(req);
|
|
32
|
+
if (!service) {
|
|
33
|
+
return res.status(500).json({ error: 'Wearable service unavailable.' });
|
|
34
|
+
}
|
|
35
|
+
res.json(await service.buildBootstrap(req));
|
|
36
|
+
} catch (err) {
|
|
37
|
+
res.status(400).json({ error: sanitizeError(err) });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
router.get('/firmware/manifest', async (req, res) => {
|
|
42
|
+
try {
|
|
43
|
+
const service = wearableService(req);
|
|
44
|
+
if (!service) {
|
|
45
|
+
return res.status(500).json({ error: 'Wearable service unavailable.' });
|
|
46
|
+
}
|
|
47
|
+
res.json(await service.buildFirmwareManifest(req));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
res.status(400).json({ error: sanitizeError(err) });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
router.get('/connections', (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const service = wearableService(req);
|
|
56
|
+
if (!service) {
|
|
57
|
+
return res.status(500).json({ error: 'Wearable service unavailable.' });
|
|
58
|
+
}
|
|
59
|
+
res.json({
|
|
60
|
+
connections: service.listConnections(req.session.userId),
|
|
61
|
+
});
|
|
62
|
+
} catch (err) {
|
|
63
|
+
res.status(400).json({ error: sanitizeError(err) });
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
module.exports = router;
|
|
@@ -3,6 +3,8 @@ const { GoogleProvider } = require('./providers/google');
|
|
|
3
3
|
const { GrokProvider } = require('./providers/grok');
|
|
4
4
|
const { OllamaProvider } = require('./providers/ollama');
|
|
5
5
|
const { OpenAIProvider } = require('./providers/openai');
|
|
6
|
+
const { GithubCopilotProvider } = require('./providers/githubCopilot');
|
|
7
|
+
const { OpenAICodexProvider } = require('./providers/openaiCodex');
|
|
6
8
|
const {
|
|
7
9
|
AI_PROVIDER_DEFINITIONS,
|
|
8
10
|
getProviderConfigs,
|
|
@@ -16,6 +18,30 @@ const STATIC_MODELS = [
|
|
|
16
18
|
provider: 'grok',
|
|
17
19
|
purpose: 'general'
|
|
18
20
|
},
|
|
21
|
+
{
|
|
22
|
+
id: 'gpt-5.3',
|
|
23
|
+
label: 'GPT-5.3 (Copilot Default)',
|
|
24
|
+
provider: 'github-copilot',
|
|
25
|
+
purpose: 'general'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'gpt-4.1',
|
|
29
|
+
label: 'GPT-4.1 (Copilot Fast)',
|
|
30
|
+
provider: 'github-copilot',
|
|
31
|
+
purpose: 'coding'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'gpt-5.3-codex',
|
|
35
|
+
label: 'GPT-5.3 (Codex Default)',
|
|
36
|
+
provider: 'openai-codex',
|
|
37
|
+
purpose: 'general'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'gpt-4.1-codex',
|
|
41
|
+
label: 'GPT-4.1 (Codex Fast)',
|
|
42
|
+
provider: 'openai-codex',
|
|
43
|
+
purpose: 'coding'
|
|
44
|
+
},
|
|
19
45
|
{
|
|
20
46
|
id: 'gpt-5-nano',
|
|
21
47
|
label: 'GPT-5 Nano (Fast / Subagents)',
|
|
@@ -302,6 +328,10 @@ function createProviderInstance(providerStr, userId = null, configOverrides = {}
|
|
|
302
328
|
return new AnthropicProvider({ apiKey: runtime.apiKey, baseUrl: runtime.baseUrl, ...providerOverrides });
|
|
303
329
|
} else if (providerStr === 'ollama') {
|
|
304
330
|
return new OllamaProvider({ baseUrl: runtime.baseUrl, ...providerOverrides });
|
|
331
|
+
} else if (providerStr === 'github-copilot') {
|
|
332
|
+
return new GithubCopilotProvider({ apiKey: runtime.apiKey, ...providerOverrides });
|
|
333
|
+
} else if (providerStr === 'openai-codex') {
|
|
334
|
+
return new OpenAICodexProvider({ apiKey: runtime.apiKey, ...providerOverrides });
|
|
305
335
|
}
|
|
306
336
|
throw new Error(`Unknown provider: ${providerStr}`);
|
|
307
337
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const { OpenAIProvider } = require('./openai');
|
|
2
|
+
|
|
3
|
+
class GithubCopilotProvider extends OpenAIProvider {
|
|
4
|
+
constructor(config = {}) {
|
|
5
|
+
// GitHub Copilot base URL defaults to the individual endpoint
|
|
6
|
+
const defaultBaseUrl = 'https://api.individual.githubcopilot.com';
|
|
7
|
+
const baseUrl = config.baseUrl || process.env.GITHUB_COPILOT_BASE_URL || defaultBaseUrl;
|
|
8
|
+
|
|
9
|
+
super({
|
|
10
|
+
...config,
|
|
11
|
+
apiKey: config.apiKey || process.env.GITHUB_COPILOT_ACCESS_TOKEN,
|
|
12
|
+
baseUrl,
|
|
13
|
+
// Pass special headers required by GitHub Copilot
|
|
14
|
+
defaultHeaders: {
|
|
15
|
+
'Editor-Version': 'vscode/1.90.0',
|
|
16
|
+
'Editor-Plugin-Version': 'copilot-chat/0.15.0',
|
|
17
|
+
'User-Agent': 'GithubCopilot/1.155.0',
|
|
18
|
+
'X-Github-Api-Version': '2023-07-07',
|
|
19
|
+
'Copilot-Integration-Id': 'vscode-chat'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
this.name = 'github-copilot';
|
|
23
|
+
this.githubToken = config.apiKey || process.env.GITHUB_COPILOT_ACCESS_TOKEN;
|
|
24
|
+
this.copilotToken = null;
|
|
25
|
+
this.tokenExpiresAt = 0;
|
|
26
|
+
this._refreshPromise = null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async _refreshCopilotToken() {
|
|
30
|
+
if (this._refreshPromise) return this._refreshPromise;
|
|
31
|
+
|
|
32
|
+
const now = Math.floor(Date.now() / 1000);
|
|
33
|
+
// Refresh token if missing or expiring in less than 5 minutes
|
|
34
|
+
if (this.copilotToken && this.tokenExpiresAt >= now + 300) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this._refreshPromise = (async () => {
|
|
39
|
+
try {
|
|
40
|
+
if (!this.githubToken) {
|
|
41
|
+
throw new Error('GitHub Copilot access token is missing. Please run `neoagent login github-copilot`.');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const res = await fetch('https://api.github.com/copilot_internal/v2/token', {
|
|
45
|
+
headers: {
|
|
46
|
+
'Authorization': `token ${this.githubToken}`,
|
|
47
|
+
'Accept': 'application/json',
|
|
48
|
+
'User-Agent': 'NeoAgent/1.0.0'
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const errorText = await res.text().catch(() => 'Unknown error');
|
|
54
|
+
throw new Error(`Failed to refresh GitHub Copilot token: HTTP ${res.status} - ${errorText}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const data = await res.json();
|
|
58
|
+
if (!data || typeof data.token !== 'string' || !data.token) {
|
|
59
|
+
throw new Error('Invalid token response from GitHub Copilot.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.copilotToken = data.token;
|
|
63
|
+
this.tokenExpiresAt = typeof data.expires_at === 'number'
|
|
64
|
+
? data.expires_at
|
|
65
|
+
: Math.floor(new Date(data.expires_at).getTime() / 1000);
|
|
66
|
+
|
|
67
|
+
if (isNaN(this.tokenExpiresAt)) {
|
|
68
|
+
this.tokenExpiresAt = Math.floor(Date.now() / 1000) + 1800;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Update the client's API key
|
|
72
|
+
this.client.apiKey = this.copilotToken;
|
|
73
|
+
} finally {
|
|
74
|
+
this._refreshPromise = null;
|
|
75
|
+
}
|
|
76
|
+
})();
|
|
77
|
+
|
|
78
|
+
return this._refreshPromise;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async chat(messages, tools = [], options = {}) {
|
|
82
|
+
await this._refreshCopilotToken();
|
|
83
|
+
return super.chat(messages, tools, options);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async *stream(messages, tools = [], options = {}) {
|
|
87
|
+
await this._refreshCopilotToken();
|
|
88
|
+
yield* super.stream(messages, tools, options);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async analyzeImage(options = {}) {
|
|
92
|
+
await this._refreshCopilotToken();
|
|
93
|
+
return super.analyzeImage(options);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { GithubCopilotProvider };
|
|
@@ -29,7 +29,8 @@ class OpenAIProvider extends BaseProvider {
|
|
|
29
29
|
};
|
|
30
30
|
this.client = new OpenAI({
|
|
31
31
|
apiKey: config.apiKey || process.env.OPENAI_API_KEY,
|
|
32
|
-
baseURL: config.baseUrl || process.env.OPENAI_BASE_URL || undefined
|
|
32
|
+
baseURL: config.baseUrl || process.env.OPENAI_BASE_URL || undefined,
|
|
33
|
+
defaultHeaders: config.defaultHeaders || undefined
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { OpenAIProvider } = require('./openai');
|
|
2
|
+
|
|
3
|
+
class OpenAICodexProvider extends OpenAIProvider {
|
|
4
|
+
constructor(config = {}) {
|
|
5
|
+
const officialBaseUrl = 'https://api.openai.com/v1';
|
|
6
|
+
const baseUrl = config.baseUrl || process.env.OPENAI_CODEX_BASE_URL || 'https://chatgpt.com/backend-api/codex';
|
|
7
|
+
|
|
8
|
+
if (!baseUrl.includes('api.openai.com') && !baseUrl.includes('chatgpt.com')) {
|
|
9
|
+
console.warn(`[OpenAICodex] Using non-official base URL: ${baseUrl}`);
|
|
10
|
+
} else if (baseUrl.includes('chatgpt.com')) {
|
|
11
|
+
console.info(`[OpenAICodex] Using ChatGPT subscription endpoint: ${baseUrl}`);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
super({
|
|
15
|
+
...config,
|
|
16
|
+
apiKey: config.apiKey || process.env.OPENAI_CODEX_ACCESS_TOKEN,
|
|
17
|
+
baseUrl,
|
|
18
|
+
defaultHeaders: {
|
|
19
|
+
'Editor-Version': process.env.OPENAI_CODEX_EDITOR_VERSION || 'vscode/1.99.0',
|
|
20
|
+
'Editor-Plugin-Version': process.env.OPENAI_CODEX_EDITOR_PLUGIN_VERSION || 'neoagent/1.0.0',
|
|
21
|
+
'User-Agent': process.env.OPENAI_CODEX_USER_AGENT || 'NeoAgent/1.0.0'
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
this.name = 'openai-codex';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// OpenAI Codex (subscription-based) uses the OAuth token directly as the API key.
|
|
28
|
+
// The base URL routes it through the ChatGPT backend-api.
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { OpenAICodexProvider };
|
|
@@ -59,6 +59,26 @@ const AI_PROVIDER_DEFINITIONS = Object.freeze({
|
|
|
59
59
|
defaultEnabled: false,
|
|
60
60
|
defaultBaseUrl: 'https://api.minimax.io/anthropic'
|
|
61
61
|
},
|
|
62
|
+
'github-copilot': {
|
|
63
|
+
id: 'github-copilot',
|
|
64
|
+
label: 'GitHub Copilot',
|
|
65
|
+
description: 'Use your GitHub Copilot subscription as an AI provider.',
|
|
66
|
+
envKey: 'GITHUB_COPILOT_ACCESS_TOKEN',
|
|
67
|
+
supportsApiKey: true,
|
|
68
|
+
supportsBaseUrl: true,
|
|
69
|
+
defaultEnabled: false,
|
|
70
|
+
defaultBaseUrl: 'https://api.githubcopilot.com'
|
|
71
|
+
},
|
|
72
|
+
'openai-codex': {
|
|
73
|
+
id: 'openai-codex',
|
|
74
|
+
label: 'OpenAI Codex',
|
|
75
|
+
description: 'Use your ChatGPT/Codex subscription as an AI provider.',
|
|
76
|
+
envKey: 'OPENAI_CODEX_ACCESS_TOKEN',
|
|
77
|
+
supportsApiKey: true,
|
|
78
|
+
supportsBaseUrl: true,
|
|
79
|
+
defaultEnabled: false,
|
|
80
|
+
defaultBaseUrl: 'https://api.openai.com/v1'
|
|
81
|
+
},
|
|
62
82
|
ollama: {
|
|
63
83
|
id: 'ollama',
|
|
64
84
|
label: 'Ollama',
|
|
@@ -33,7 +33,20 @@ function selectMcpTools(task, mcpTools = []) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function selectToolsForTask(task, builtInTools = [], mcpTools = [], _options = {}) {
|
|
36
|
-
|
|
36
|
+
const MAX_TOOLS = 128; // Strict provider limit (e.g. Github Copilot / OpenAI)
|
|
37
|
+
const selectedMcp = selectMcpTools(task, mcpTools);
|
|
38
|
+
|
|
39
|
+
if (builtInTools.length + selectedMcp.length <= MAX_TOOLS) {
|
|
40
|
+
return [...builtInTools, ...selectedMcp];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// If we exceed the limit, prioritize base tools and take as many MCP tools as fit
|
|
44
|
+
const remainingSpace = MAX_TOOLS - builtInTools.length;
|
|
45
|
+
if (remainingSpace > 0) {
|
|
46
|
+
return [...builtInTools, ...selectedMcp.slice(0, remainingSpace)];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return builtInTools.slice(0, MAX_TOOLS);
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
module.exports = { selectToolsForTask, selectMcpTools };
|