neoagent 2.3.1-beta.2 → 2.3.1-beta.21
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 +39 -0
- package/README.md +2 -0
- package/docs/capabilities.md +2 -2
- package/docs/configuration.md +13 -5
- package/docs/integrations.md +4 -1
- 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 +23057 -0
- package/flutter_app/lib/main_app_shell.dart +1682 -0
- package/flutter_app/lib/main_integrations.dart +931 -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 +3473 -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 +831 -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/manager.js +231 -7
- package/package.json +3 -1
- package/server/db/database.js +68 -0
- package/server/http/middleware.js +50 -0
- package/server/http/routes.js +3 -1
- package/server/index.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/NOTICES +61 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +65262 -64422
- package/server/routes/integrations.js +86 -0
- package/server/routes/memory.js +11 -2
- package/server/routes/screenHistory.js +46 -0
- package/server/routes/triggers.js +81 -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/systemPrompt.js +1 -1
- package/server/services/ai/tools.js +35 -6
- package/server/services/browser/controller.js +47 -3
- package/server/services/desktop/screenRecorder.js +172 -0
- package/server/services/integrations/env.js +5 -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/home_assistant/provider.js +306 -26
- package/server/services/integrations/manager.js +63 -7
- package/server/services/integrations/oauth_provider.js +13 -6
- package/server/services/integrations/provider_config_store.js +76 -0
- package/server/services/integrations/registry.js +4 -0
- package/server/services/integrations/trello/provider.js +744 -0
- package/server/services/integrations/whatsapp/provider.js +6 -2
- package/server/services/manager.js +22 -0
- package/server/services/memory/manager.js +39 -2
- package/server/services/skills/base_catalog.js +33 -0
- package/server/services/tasks/adapters/index.js +1 -0
- package/server/services/tasks/adapters/manual.js +12 -0
- package/server/services/tasks/runtime.js +1 -1
- package/server/services/voice/openaiClient.js +4 -1
- package/server/services/voice/providers.js +2 -1
- package/server/services/widgets/service.js +49 -4
- package/server/utils/local_secrets.js +56 -0
- package/server/utils/logger.js +37 -9
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import 'dart:typed_data';
|
|
2
|
+
|
|
3
|
+
import 'package:http/http.dart' as http;
|
|
4
|
+
|
|
5
|
+
import 'app_http_client.dart';
|
|
6
|
+
|
|
7
|
+
AppHttpClient createPlatformHttpClient() => IoAppHttpClient();
|
|
8
|
+
|
|
9
|
+
class IoAppHttpClient implements AppHttpClient {
|
|
10
|
+
final http.Client _client = http.Client();
|
|
11
|
+
String? _sessionCookie;
|
|
12
|
+
|
|
13
|
+
HttpResponseData _toResponseData(http.Response response) {
|
|
14
|
+
return HttpResponseData(
|
|
15
|
+
statusCode: response.statusCode,
|
|
16
|
+
body: response.body,
|
|
17
|
+
bodyBytes: response.bodyBytes,
|
|
18
|
+
headers: response.headers,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@override
|
|
23
|
+
Future<HttpResponseData> get(Uri uri, {Map<String, String>? headers}) async {
|
|
24
|
+
final response = await _client.get(uri, headers: _withCookie(headers));
|
|
25
|
+
_storeCookie(response.headers);
|
|
26
|
+
return _toResponseData(response);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@override
|
|
30
|
+
Future<HttpResponseData> post(
|
|
31
|
+
Uri uri, {
|
|
32
|
+
Map<String, String>? headers,
|
|
33
|
+
Object? body,
|
|
34
|
+
}) async {
|
|
35
|
+
final response = await _client.post(
|
|
36
|
+
uri,
|
|
37
|
+
headers: _withCookie(headers),
|
|
38
|
+
body: body,
|
|
39
|
+
);
|
|
40
|
+
_storeCookie(response.headers);
|
|
41
|
+
return _toResponseData(response);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
Future<HttpResponseData> postMultipart(
|
|
46
|
+
Uri uri, {
|
|
47
|
+
Map<String, String>? headers,
|
|
48
|
+
required String fieldName,
|
|
49
|
+
required String filename,
|
|
50
|
+
required Uint8List bytes,
|
|
51
|
+
}) async {
|
|
52
|
+
final request = http.MultipartRequest('POST', uri);
|
|
53
|
+
request.headers.addAll(_withCookie(headers));
|
|
54
|
+
request.files.add(
|
|
55
|
+
http.MultipartFile.fromBytes(fieldName, bytes, filename: filename),
|
|
56
|
+
);
|
|
57
|
+
final response = await http.Response.fromStream(
|
|
58
|
+
await _client.send(request),
|
|
59
|
+
);
|
|
60
|
+
_storeCookie(response.headers);
|
|
61
|
+
return _toResponseData(response);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@override
|
|
65
|
+
Future<HttpResponseData> put(
|
|
66
|
+
Uri uri, {
|
|
67
|
+
Map<String, String>? headers,
|
|
68
|
+
Object? body,
|
|
69
|
+
}) async {
|
|
70
|
+
final response = await _client.put(
|
|
71
|
+
uri,
|
|
72
|
+
headers: _withCookie(headers),
|
|
73
|
+
body: body,
|
|
74
|
+
);
|
|
75
|
+
_storeCookie(response.headers);
|
|
76
|
+
return _toResponseData(response);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@override
|
|
80
|
+
Future<HttpResponseData> delete(
|
|
81
|
+
Uri uri, {
|
|
82
|
+
Map<String, String>? headers,
|
|
83
|
+
Object? body,
|
|
84
|
+
}) async {
|
|
85
|
+
final response = await _client.delete(
|
|
86
|
+
uri,
|
|
87
|
+
headers: _withCookie(headers),
|
|
88
|
+
body: body,
|
|
89
|
+
);
|
|
90
|
+
_storeCookie(response.headers);
|
|
91
|
+
return _toResponseData(response);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
Map<String, String> _withCookie(Map<String, String>? headers) {
|
|
95
|
+
final next = <String, String>{...?headers};
|
|
96
|
+
if (_sessionCookie != null && _sessionCookie!.isNotEmpty) {
|
|
97
|
+
next['Cookie'] = _sessionCookie!;
|
|
98
|
+
}
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void _storeCookie(Map<String, String> headers) {
|
|
103
|
+
final rawCookie = headers['set-cookie'];
|
|
104
|
+
if (rawCookie == null || rawCookie.isEmpty) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
final firstCookieField = rawCookie.split(';').first.trim();
|
|
108
|
+
final separatorIndex = firstCookieField.indexOf('=');
|
|
109
|
+
if (separatorIndex <= 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
final name = firstCookieField.substring(0, separatorIndex).trim();
|
|
113
|
+
final value = firstCookieField.substring(separatorIndex + 1).trim();
|
|
114
|
+
if (name.isEmpty || value.isEmpty) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
_sessionCookie = '$name=$value';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@override
|
|
121
|
+
Future<void> close() async {
|
|
122
|
+
_client.close();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@override
|
|
126
|
+
void clearSession() {
|
|
127
|
+
_sessionCookie = null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@override
|
|
131
|
+
void restoreSession(String? sessionCookie) {
|
|
132
|
+
final normalized = sessionCookie?.trim() ?? '';
|
|
133
|
+
_sessionCookie = normalized.isEmpty ? null : normalized;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@override
|
|
137
|
+
String? get sessionCookie => _sessionCookie;
|
|
138
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import 'dart:typed_data';
|
|
2
|
+
|
|
3
|
+
import 'package:http/browser_client.dart';
|
|
4
|
+
import 'package:http/http.dart' as http;
|
|
5
|
+
|
|
6
|
+
import 'app_http_client.dart';
|
|
7
|
+
|
|
8
|
+
AppHttpClient createPlatformHttpClient() => WebAppHttpClient();
|
|
9
|
+
|
|
10
|
+
class WebAppHttpClient implements AppHttpClient {
|
|
11
|
+
WebAppHttpClient() : _client = BrowserClient()..withCredentials = true;
|
|
12
|
+
|
|
13
|
+
final BrowserClient _client;
|
|
14
|
+
|
|
15
|
+
HttpResponseData _toResponseData(http.Response response) {
|
|
16
|
+
return HttpResponseData(
|
|
17
|
+
statusCode: response.statusCode,
|
|
18
|
+
body: response.body,
|
|
19
|
+
bodyBytes: response.bodyBytes,
|
|
20
|
+
headers: response.headers,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@override
|
|
25
|
+
Future<HttpResponseData> get(Uri uri, {Map<String, String>? headers}) async {
|
|
26
|
+
final response = await _client.get(uri, headers: headers);
|
|
27
|
+
return _toResponseData(response);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@override
|
|
31
|
+
Future<HttpResponseData> post(
|
|
32
|
+
Uri uri, {
|
|
33
|
+
Map<String, String>? headers,
|
|
34
|
+
Object? body,
|
|
35
|
+
}) async {
|
|
36
|
+
final response = await _client.post(uri, headers: headers, body: body);
|
|
37
|
+
return _toResponseData(response);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@override
|
|
41
|
+
Future<HttpResponseData> postMultipart(
|
|
42
|
+
Uri uri, {
|
|
43
|
+
Map<String, String>? headers,
|
|
44
|
+
required String fieldName,
|
|
45
|
+
required String filename,
|
|
46
|
+
required Uint8List bytes,
|
|
47
|
+
}) async {
|
|
48
|
+
final request = http.MultipartRequest('POST', uri);
|
|
49
|
+
if (headers != null) {
|
|
50
|
+
request.headers.addAll(headers);
|
|
51
|
+
}
|
|
52
|
+
request.files.add(
|
|
53
|
+
http.MultipartFile.fromBytes(fieldName, bytes, filename: filename),
|
|
54
|
+
);
|
|
55
|
+
final response = await http.Response.fromStream(
|
|
56
|
+
await _client.send(request),
|
|
57
|
+
);
|
|
58
|
+
return _toResponseData(response);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@override
|
|
62
|
+
Future<HttpResponseData> put(
|
|
63
|
+
Uri uri, {
|
|
64
|
+
Map<String, String>? headers,
|
|
65
|
+
Object? body,
|
|
66
|
+
}) async {
|
|
67
|
+
final response = await _client.put(uri, headers: headers, body: body);
|
|
68
|
+
return _toResponseData(response);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@override
|
|
72
|
+
Future<HttpResponseData> delete(
|
|
73
|
+
Uri uri, {
|
|
74
|
+
Map<String, String>? headers,
|
|
75
|
+
Object? body,
|
|
76
|
+
}) async {
|
|
77
|
+
final response = await _client.delete(uri, headers: headers, body: body);
|
|
78
|
+
return _toResponseData(response);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@override
|
|
82
|
+
Future<void> close() async {
|
|
83
|
+
_client.close();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@override
|
|
87
|
+
void clearSession() {}
|
|
88
|
+
|
|
89
|
+
@override
|
|
90
|
+
void restoreSession(String? sessionCookie) {}
|
|
91
|
+
|
|
92
|
+
@override
|
|
93
|
+
String? get sessionCookie => null;
|
|
94
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import 'oauth_launcher_stub.dart'
|
|
2
|
+
if (dart.library.html) 'oauth_launcher_web.dart'
|
|
3
|
+
if (dart.library.io) 'oauth_launcher_io.dart';
|
|
4
|
+
|
|
5
|
+
OAuthLauncher createOAuthLauncher() => createPlatformOAuthLauncher();
|
|
6
|
+
|
|
7
|
+
abstract class OAuthLauncher {
|
|
8
|
+
Future<OAuthLaunchResult> launch({
|
|
9
|
+
required String url,
|
|
10
|
+
required String provider,
|
|
11
|
+
Duration timeout = const Duration(minutes: 2),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
Future<OAuthLaunchResult> openExternal({
|
|
15
|
+
required String url,
|
|
16
|
+
required String label,
|
|
17
|
+
Duration timeout = const Duration(seconds: 10),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
void dispose() {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class OAuthLaunchResult {
|
|
24
|
+
const OAuthLaunchResult({
|
|
25
|
+
required this.launched,
|
|
26
|
+
required this.completed,
|
|
27
|
+
this.error,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
final bool launched;
|
|
31
|
+
final bool completed;
|
|
32
|
+
final String? error;
|
|
33
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import 'dart:async';
|
|
2
|
+
import 'dart:io';
|
|
3
|
+
|
|
4
|
+
import 'oauth_launcher.dart';
|
|
5
|
+
|
|
6
|
+
OAuthLauncher createPlatformOAuthLauncher() => _IoOAuthLauncher();
|
|
7
|
+
|
|
8
|
+
class _IoOAuthLauncher extends OAuthLauncher {
|
|
9
|
+
_IoOAuthLauncher();
|
|
10
|
+
|
|
11
|
+
@override
|
|
12
|
+
Future<OAuthLaunchResult> launch({
|
|
13
|
+
required String url,
|
|
14
|
+
required String provider,
|
|
15
|
+
Duration timeout = const Duration(minutes: 2),
|
|
16
|
+
}) async {
|
|
17
|
+
try {
|
|
18
|
+
late final ProcessResult result;
|
|
19
|
+
if (Platform.isMacOS) {
|
|
20
|
+
result = await Process.run(
|
|
21
|
+
'open',
|
|
22
|
+
<String>[url],
|
|
23
|
+
).timeout(timeout);
|
|
24
|
+
} else if (Platform.isLinux) {
|
|
25
|
+
result = await Process.run(
|
|
26
|
+
'xdg-open',
|
|
27
|
+
<String>[url],
|
|
28
|
+
).timeout(timeout);
|
|
29
|
+
} else if (Platform.isWindows) {
|
|
30
|
+
result = await Process.run(
|
|
31
|
+
'start',
|
|
32
|
+
<String>[url],
|
|
33
|
+
runInShell: true,
|
|
34
|
+
).timeout(timeout);
|
|
35
|
+
} else {
|
|
36
|
+
return const OAuthLaunchResult(
|
|
37
|
+
launched: false,
|
|
38
|
+
completed: false,
|
|
39
|
+
error: 'External browser launch is not supported on this platform.',
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (result.exitCode != 0) {
|
|
44
|
+
return OAuthLaunchResult(
|
|
45
|
+
launched: false,
|
|
46
|
+
completed: false,
|
|
47
|
+
error: (result.stderr?.toString().trim().isNotEmpty ?? false)
|
|
48
|
+
? result.stderr.toString().trim()
|
|
49
|
+
: 'External browser launch failed with exit code ${result.exitCode}.',
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return const OAuthLaunchResult(launched: true, completed: false);
|
|
54
|
+
} on TimeoutException {
|
|
55
|
+
return const OAuthLaunchResult(
|
|
56
|
+
launched: false,
|
|
57
|
+
completed: false,
|
|
58
|
+
error: 'External browser launch timed out.',
|
|
59
|
+
);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return OAuthLaunchResult(
|
|
62
|
+
launched: false,
|
|
63
|
+
completed: false,
|
|
64
|
+
error: error.toString(),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@override
|
|
70
|
+
Future<OAuthLaunchResult> openExternal({
|
|
71
|
+
required String url,
|
|
72
|
+
required String label,
|
|
73
|
+
Duration timeout = const Duration(seconds: 10),
|
|
74
|
+
}) {
|
|
75
|
+
return launch(url: url, provider: label, timeout: timeout);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import 'oauth_launcher.dart';
|
|
2
|
+
|
|
3
|
+
OAuthLauncher createPlatformOAuthLauncher() => _StubOAuthLauncher();
|
|
4
|
+
|
|
5
|
+
class _StubOAuthLauncher extends OAuthLauncher {
|
|
6
|
+
_StubOAuthLauncher();
|
|
7
|
+
|
|
8
|
+
@override
|
|
9
|
+
Future<OAuthLaunchResult> launch({
|
|
10
|
+
required String url,
|
|
11
|
+
required String provider,
|
|
12
|
+
Duration timeout = const Duration(minutes: 2),
|
|
13
|
+
}) async {
|
|
14
|
+
return const OAuthLaunchResult(
|
|
15
|
+
launched: false,
|
|
16
|
+
completed: false,
|
|
17
|
+
error: 'OAuth launch is not supported on this platform.',
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@override
|
|
22
|
+
Future<OAuthLaunchResult> openExternal({
|
|
23
|
+
required String url,
|
|
24
|
+
required String label,
|
|
25
|
+
Duration timeout = const Duration(seconds: 10),
|
|
26
|
+
}) async {
|
|
27
|
+
return const OAuthLaunchResult(
|
|
28
|
+
launched: false,
|
|
29
|
+
completed: false,
|
|
30
|
+
error: 'External browser launch is not supported on this platform.',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// ignore_for_file: deprecated_member_use, avoid_web_libraries_in_flutter
|
|
2
|
+
|
|
3
|
+
import 'dart:async';
|
|
4
|
+
import 'dart:html' as html;
|
|
5
|
+
|
|
6
|
+
import 'oauth_launcher.dart';
|
|
7
|
+
|
|
8
|
+
OAuthLauncher createPlatformOAuthLauncher() => _WebOAuthLauncher();
|
|
9
|
+
|
|
10
|
+
class _WebOAuthLauncher extends OAuthLauncher {
|
|
11
|
+
StreamSubscription<html.MessageEvent>? _messageSubscription;
|
|
12
|
+
Timer? _timeoutTimer;
|
|
13
|
+
|
|
14
|
+
@override
|
|
15
|
+
Future<OAuthLaunchResult> launch({
|
|
16
|
+
required String url,
|
|
17
|
+
required String provider,
|
|
18
|
+
Duration timeout = const Duration(minutes: 2),
|
|
19
|
+
}) async {
|
|
20
|
+
final expectedOrigin = _deriveExpectedOrigin(url);
|
|
21
|
+
html.window.open(
|
|
22
|
+
url,
|
|
23
|
+
'neoagent_oauth_${provider.replaceAll(RegExp(r'[^a-zA-Z0-9]+'), '_')}',
|
|
24
|
+
'width=640,height=760',
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
final completer = Completer<OAuthLaunchResult>();
|
|
28
|
+
|
|
29
|
+
void finish(OAuthLaunchResult result) {
|
|
30
|
+
if (completer.isCompleted) return;
|
|
31
|
+
_timeoutTimer?.cancel();
|
|
32
|
+
_timeoutTimer = null;
|
|
33
|
+
_messageSubscription?.cancel();
|
|
34
|
+
_messageSubscription = null;
|
|
35
|
+
completer.complete(result);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_messageSubscription = html.window.onMessage.listen((event) {
|
|
39
|
+
if (expectedOrigin != null && event.origin != expectedOrigin) return;
|
|
40
|
+
final data = event.data;
|
|
41
|
+
if (data is! Map) return;
|
|
42
|
+
final type = data['type']?.toString();
|
|
43
|
+
final incomingProvider = data['provider']?.toString();
|
|
44
|
+
if (incomingProvider != null && incomingProvider != provider) return;
|
|
45
|
+
if (type == 'integration_oauth_success' || type == 'auth_oauth_success') {
|
|
46
|
+
finish(const OAuthLaunchResult(launched: true, completed: true));
|
|
47
|
+
} else if (type == 'integration_oauth_error' || type == 'auth_oauth_error') {
|
|
48
|
+
finish(
|
|
49
|
+
OAuthLaunchResult(
|
|
50
|
+
launched: true,
|
|
51
|
+
completed: false,
|
|
52
|
+
error: data['error']?.toString() ?? 'Authentication failed.',
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
_timeoutTimer = Timer(timeout, () {
|
|
59
|
+
finish(
|
|
60
|
+
const OAuthLaunchResult(
|
|
61
|
+
launched: true,
|
|
62
|
+
completed: false,
|
|
63
|
+
error: 'Authentication timed out.',
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return completer.future;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@override
|
|
72
|
+
Future<OAuthLaunchResult> openExternal({
|
|
73
|
+
required String url,
|
|
74
|
+
required String label,
|
|
75
|
+
Duration timeout = const Duration(seconds: 10),
|
|
76
|
+
}) async {
|
|
77
|
+
html.window.open(
|
|
78
|
+
url,
|
|
79
|
+
label.replaceAll(RegExp(r'[^a-zA-Z0-9]+'), '_'),
|
|
80
|
+
'noopener',
|
|
81
|
+
);
|
|
82
|
+
return const OAuthLaunchResult(launched: true, completed: false);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@override
|
|
86
|
+
void dispose() {
|
|
87
|
+
_timeoutTimer?.cancel();
|
|
88
|
+
_timeoutTimer = null;
|
|
89
|
+
_messageSubscription?.cancel();
|
|
90
|
+
_messageSubscription = null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
String? _deriveExpectedOrigin(String url) {
|
|
94
|
+
try {
|
|
95
|
+
final uri = Uri.parse(url);
|
|
96
|
+
final redirect = uri.queryParameters['redirect_uri'];
|
|
97
|
+
if (redirect != null && redirect.trim().isNotEmpty) {
|
|
98
|
+
final redirectUri = Uri.parse(redirect);
|
|
99
|
+
return redirectUri.origin;
|
|
100
|
+
}
|
|
101
|
+
if (uri.hasScheme && uri.host.isNotEmpty) {
|
|
102
|
+
return uri.origin;
|
|
103
|
+
}
|
|
104
|
+
} catch (_) {}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|