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,744 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const db = require('../../../db/database');
|
|
4
|
+
const { resolveAgentId } = require('../../agents/manager');
|
|
5
|
+
const {
|
|
6
|
+
deleteProviderConfig,
|
|
7
|
+
getProviderConfig,
|
|
8
|
+
setProviderConfig,
|
|
9
|
+
} = require('../provider_config_store');
|
|
10
|
+
const { getConnectionAccessMode } = require('../access');
|
|
11
|
+
const { fetchJson } = require('../oauth_provider');
|
|
12
|
+
|
|
13
|
+
const TRELLO_APP = {
|
|
14
|
+
id: 'trello',
|
|
15
|
+
label: 'Trello',
|
|
16
|
+
description: 'Connect Trello for board, list, card, and comment tools.',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const TRELLO_TOOL_DEFINITIONS = [
|
|
20
|
+
{
|
|
21
|
+
appId: TRELLO_APP.id,
|
|
22
|
+
name: 'trello_get_me',
|
|
23
|
+
access: 'read',
|
|
24
|
+
description: 'Get the connected Trello member profile.',
|
|
25
|
+
parameters: { type: 'object', properties: {} },
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
appId: TRELLO_APP.id,
|
|
29
|
+
name: 'trello_list_boards',
|
|
30
|
+
access: 'read',
|
|
31
|
+
description: 'List boards visible to the connected Trello member.',
|
|
32
|
+
parameters: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
filter: { type: 'string', description: 'Optional board filter such as open, closed, or all.' },
|
|
36
|
+
limit: { type: 'number', description: 'Maximum boards to return, default 50.' },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
appId: TRELLO_APP.id,
|
|
42
|
+
name: 'trello_get_board',
|
|
43
|
+
access: 'read',
|
|
44
|
+
description: 'Get a Trello board and optional list/card summary.',
|
|
45
|
+
parameters: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
board_id: { type: 'string', description: 'Trello board ID.' },
|
|
49
|
+
fields: { type: 'string', description: 'Optional comma-separated board fields to return.' },
|
|
50
|
+
lists: { type: 'string', description: 'Optional list expansion, default open.' },
|
|
51
|
+
cards: { type: 'string', description: 'Optional card expansion, default none.' },
|
|
52
|
+
},
|
|
53
|
+
required: ['board_id'],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
appId: TRELLO_APP.id,
|
|
58
|
+
name: 'trello_list_lists',
|
|
59
|
+
access: 'read',
|
|
60
|
+
description: 'List the lists on a Trello board.',
|
|
61
|
+
parameters: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
properties: {
|
|
64
|
+
board_id: { type: 'string', description: 'Trello board ID.' },
|
|
65
|
+
fields: { type: 'string', description: 'Optional comma-separated list fields to return.' },
|
|
66
|
+
},
|
|
67
|
+
required: ['board_id'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
appId: TRELLO_APP.id,
|
|
72
|
+
name: 'trello_list_cards',
|
|
73
|
+
access: 'read',
|
|
74
|
+
description: 'List cards on a Trello board or list.',
|
|
75
|
+
parameters: {
|
|
76
|
+
type: 'object',
|
|
77
|
+
properties: {
|
|
78
|
+
board_id: { type: 'string', description: 'Optional Trello board ID.' },
|
|
79
|
+
list_id: { type: 'string', description: 'Optional Trello list ID.' },
|
|
80
|
+
filter: { type: 'string', description: 'Optional Trello card filter, default open.' },
|
|
81
|
+
fields: { type: 'string', description: 'Optional comma-separated card fields to return.' },
|
|
82
|
+
limit: { type: 'number', description: 'Maximum cards to return, default 100.' },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
appId: TRELLO_APP.id,
|
|
88
|
+
name: 'trello_get_card',
|
|
89
|
+
access: 'read',
|
|
90
|
+
description: 'Get a Trello card by ID.',
|
|
91
|
+
parameters: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
card_id: { type: 'string', description: 'Trello card ID.' },
|
|
95
|
+
fields: { type: 'string', description: 'Optional comma-separated card fields to return.' },
|
|
96
|
+
},
|
|
97
|
+
required: ['card_id'],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
appId: TRELLO_APP.id,
|
|
102
|
+
name: 'trello_search',
|
|
103
|
+
access: 'read',
|
|
104
|
+
description: 'Search Trello boards, cards, members, and organizations.',
|
|
105
|
+
parameters: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
query: { type: 'string', description: 'Search text.' },
|
|
109
|
+
model_types: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: 'Optional comma-separated model types, default cards,boards,lists,members,organizations.',
|
|
112
|
+
},
|
|
113
|
+
limit: { type: 'number', description: 'Maximum combined results to return, default 20.' },
|
|
114
|
+
},
|
|
115
|
+
required: ['query'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
appId: TRELLO_APP.id,
|
|
120
|
+
name: 'trello_create_card',
|
|
121
|
+
access: 'write',
|
|
122
|
+
description: 'Create a Trello card on a list.',
|
|
123
|
+
parameters: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
properties: {
|
|
126
|
+
list_id: { type: 'string', description: 'Target Trello list ID.' },
|
|
127
|
+
name: { type: 'string', description: 'Card title.' },
|
|
128
|
+
desc: { type: 'string', description: 'Optional card description.' },
|
|
129
|
+
due: { type: 'string', description: 'Optional ISO 8601 due date.' },
|
|
130
|
+
},
|
|
131
|
+
required: ['list_id', 'name'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
appId: TRELLO_APP.id,
|
|
136
|
+
name: 'trello_update_card',
|
|
137
|
+
access: 'write',
|
|
138
|
+
description: 'Update a Trello card.',
|
|
139
|
+
parameters: {
|
|
140
|
+
type: 'object',
|
|
141
|
+
properties: {
|
|
142
|
+
card_id: { type: 'string', description: 'Trello card ID.' },
|
|
143
|
+
name: { type: 'string', description: 'Optional new card title.' },
|
|
144
|
+
desc: { type: 'string', description: 'Optional new card description.' },
|
|
145
|
+
closed: { type: 'boolean', description: 'Optional archived state.' },
|
|
146
|
+
idList: { type: 'string', description: 'Optional target list ID.' },
|
|
147
|
+
pos: { type: 'string', description: 'Optional position value such as top, bottom, or a number.' },
|
|
148
|
+
due: { type: 'string', description: 'Optional ISO 8601 due date.' },
|
|
149
|
+
dueComplete: { type: 'boolean', description: 'Optional due-complete state.' },
|
|
150
|
+
},
|
|
151
|
+
required: ['card_id'],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
appId: TRELLO_APP.id,
|
|
156
|
+
name: 'trello_add_comment',
|
|
157
|
+
access: 'write',
|
|
158
|
+
description: 'Add a comment to a Trello card.',
|
|
159
|
+
parameters: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
card_id: { type: 'string', description: 'Trello card ID.' },
|
|
163
|
+
text: { type: 'string', description: 'Comment text.' },
|
|
164
|
+
},
|
|
165
|
+
required: ['card_id', 'text'],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
appId: TRELLO_APP.id,
|
|
170
|
+
name: 'trello_api_request',
|
|
171
|
+
access: 'dynamic_http_method',
|
|
172
|
+
description: 'Make an authenticated Trello REST API request for advanced operations.',
|
|
173
|
+
parameters: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
method: { type: 'string', description: 'HTTP method: GET, POST, PUT, PATCH, or DELETE.' },
|
|
177
|
+
path: { type: 'string', description: 'Trello API path under /1, for example /1/cards/{id}.' },
|
|
178
|
+
query: { type: 'object', description: 'Optional query parameters.' },
|
|
179
|
+
body: { type: 'object', description: 'Optional request fields, sent as Trello query parameters.' },
|
|
180
|
+
},
|
|
181
|
+
required: ['method', 'path'],
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
const toolAppMap = new Map(TRELLO_TOOL_DEFINITIONS.map((tool) => [tool.name, tool.appId]));
|
|
187
|
+
|
|
188
|
+
function trimText(value) {
|
|
189
|
+
return String(value || '').trim();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function requireText(value, label) {
|
|
193
|
+
const text = trimText(value);
|
|
194
|
+
if (!text) {
|
|
195
|
+
throw new Error(`${label} is required.`);
|
|
196
|
+
}
|
|
197
|
+
return text;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function parseConfigInput(rawConfig, existingConfig = {}) {
|
|
201
|
+
const source = rawConfig && typeof rawConfig === 'object' ? rawConfig : {};
|
|
202
|
+
return {
|
|
203
|
+
apiKey: trimText(source.apiKey) || trimText(existingConfig.apiKey),
|
|
204
|
+
token: trimText(source.token) || trimText(existingConfig.token),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function resolveTrelloConfigForUser(userId) {
|
|
209
|
+
const normalizedUserId = Number(userId);
|
|
210
|
+
const config =
|
|
211
|
+
Number.isInteger(normalizedUserId) && normalizedUserId > 0
|
|
212
|
+
? parseConfigInput(getProviderConfig(normalizedUserId, 'trello'))
|
|
213
|
+
: { apiKey: '', token: '' };
|
|
214
|
+
const missing = [];
|
|
215
|
+
if (!config.apiKey) missing.push('apiKey');
|
|
216
|
+
if (!config.token) missing.push('token');
|
|
217
|
+
return { ...config, configured: missing.length === 0, missing };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function sanitizeTrelloUserConfigForClient(rawConfig) {
|
|
221
|
+
const config = parseConfigInput(rawConfig);
|
|
222
|
+
return {
|
|
223
|
+
apiKey: config.apiKey,
|
|
224
|
+
hasToken: Boolean(config.token),
|
|
225
|
+
configured: Boolean(config.apiKey && config.token),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function trelloUrl(path, query = {}, config = {}) {
|
|
230
|
+
const apiKey = trimText(config.apiKey);
|
|
231
|
+
const token = trimText(config.token);
|
|
232
|
+
if (!apiKey) {
|
|
233
|
+
throw new Error('Trello API key is required.');
|
|
234
|
+
}
|
|
235
|
+
if (!token) {
|
|
236
|
+
throw new Error('Trello token is required.');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const rawPath = trimText(path);
|
|
240
|
+
const url = new URL(
|
|
241
|
+
rawPath.startsWith('http')
|
|
242
|
+
? rawPath
|
|
243
|
+
: `https://api.trello.com${rawPath.startsWith('/') ? '' : '/'}${rawPath}`,
|
|
244
|
+
);
|
|
245
|
+
if (url.hostname !== 'api.trello.com') {
|
|
246
|
+
throw new Error('Trello API request URL must target api.trello.com.');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
url.searchParams.set('key', apiKey);
|
|
250
|
+
url.searchParams.set('token', token);
|
|
251
|
+
for (const [key, value] of Object.entries(query || {})) {
|
|
252
|
+
if (value === undefined || value === null) continue;
|
|
253
|
+
const text = String(value).trim();
|
|
254
|
+
if (!text) continue;
|
|
255
|
+
url.searchParams.set(key, text);
|
|
256
|
+
}
|
|
257
|
+
return url.toString();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function trelloRequest(config, options = {}) {
|
|
261
|
+
return fetchJson(
|
|
262
|
+
trelloUrl(options.path, options.query, config),
|
|
263
|
+
{ method: String(options.method || 'GET').toUpperCase() },
|
|
264
|
+
{ serviceName: 'Trello' },
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function fetchTrelloMemberProfile(config) {
|
|
269
|
+
return trelloRequest(config, {
|
|
270
|
+
path: '/1/members/me',
|
|
271
|
+
query: { fields: 'id,username,fullName,url,initials' },
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function trelloAccountEmail(profile = {}) {
|
|
276
|
+
const username = trimText(profile.username);
|
|
277
|
+
const memberId = trimText(profile.id);
|
|
278
|
+
const fallback = trimText(profile.fullName) || 'member';
|
|
279
|
+
return `trello:${username || memberId || fallback}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function summarizeAccountRow(row, envStatus) {
|
|
283
|
+
if (!envStatus.configured) {
|
|
284
|
+
return {
|
|
285
|
+
id: row?.id || null,
|
|
286
|
+
status: 'env_not_configured',
|
|
287
|
+
connected: false,
|
|
288
|
+
accountEmail: row?.account_email || null,
|
|
289
|
+
lastConnectedAt: row?.last_connected_at || null,
|
|
290
|
+
accessMode: 'read_write',
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!row) {
|
|
295
|
+
return {
|
|
296
|
+
id: null,
|
|
297
|
+
status: 'not_connected',
|
|
298
|
+
connected: false,
|
|
299
|
+
accountEmail: null,
|
|
300
|
+
lastConnectedAt: null,
|
|
301
|
+
accessMode: 'read_write',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
id: row.id || null,
|
|
307
|
+
status: row.status || 'not_connected',
|
|
308
|
+
connected: row.status === 'connected',
|
|
309
|
+
accountEmail: row.account_email || null,
|
|
310
|
+
lastConnectedAt: row.last_connected_at || null,
|
|
311
|
+
accessMode: getConnectionAccessMode(row),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function summarizeAppConnection(app, connectionRows, envStatus) {
|
|
316
|
+
const summarizedAccounts = (Array.isArray(connectionRows) ? connectionRows : []).map((row) =>
|
|
317
|
+
summarizeAccountRow(row, envStatus),
|
|
318
|
+
);
|
|
319
|
+
const connectedAccounts = summarizedAccounts.filter((account) => account.connected);
|
|
320
|
+
const latestConnectedAt =
|
|
321
|
+
connectedAccounts
|
|
322
|
+
.map((account) => account.lastConnectedAt)
|
|
323
|
+
.filter(Boolean)
|
|
324
|
+
.sort()
|
|
325
|
+
.reverse()[0] || null;
|
|
326
|
+
const status = !envStatus.configured
|
|
327
|
+
? 'env_not_configured'
|
|
328
|
+
: connectedAccounts.length > 0
|
|
329
|
+
? 'connected'
|
|
330
|
+
: summarizedAccounts.some((account) => account.status === 'authorizing')
|
|
331
|
+
? 'authorizing'
|
|
332
|
+
: 'not_connected';
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
id: app.id,
|
|
336
|
+
label: app.label,
|
|
337
|
+
description: app.description,
|
|
338
|
+
accounts: summarizedAccounts,
|
|
339
|
+
connection: {
|
|
340
|
+
status,
|
|
341
|
+
connected: connectedAccounts.length > 0,
|
|
342
|
+
accountCount: connectedAccounts.length,
|
|
343
|
+
accountEmail:
|
|
344
|
+
connectedAccounts.length === 1 ? connectedAccounts[0].accountEmail : null,
|
|
345
|
+
lastConnectedAt: latestConnectedAt,
|
|
346
|
+
},
|
|
347
|
+
availableToolCount:
|
|
348
|
+
envStatus.configured && connectedAccounts.length > 0 ? TRELLO_TOOL_DEFINITIONS.length : 0,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function buildConnectedAppSummary(appSnapshots) {
|
|
353
|
+
return appSnapshots
|
|
354
|
+
.filter((app) => app.connection.connected)
|
|
355
|
+
.map((app) => {
|
|
356
|
+
const emails = app.accounts
|
|
357
|
+
.filter((account) => account.connected)
|
|
358
|
+
.map((account) => account.accountEmail || `connection ${account.id}`)
|
|
359
|
+
.join(', ');
|
|
360
|
+
return `${app.label}: ${emails}`;
|
|
361
|
+
})
|
|
362
|
+
.join(' | ');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function resolveTrelloEnvStatus(userId) {
|
|
366
|
+
try {
|
|
367
|
+
const config = resolveTrelloConfigForUser(userId);
|
|
368
|
+
return {
|
|
369
|
+
configured: config.configured,
|
|
370
|
+
missing: config.missing,
|
|
371
|
+
summary: config.configured
|
|
372
|
+
? 'Trello is ready for account connections.'
|
|
373
|
+
: 'Add your Trello API key and token in Official Integrations to enable Trello tools.',
|
|
374
|
+
setupMode: 'user',
|
|
375
|
+
};
|
|
376
|
+
} catch (error) {
|
|
377
|
+
return {
|
|
378
|
+
configured: false,
|
|
379
|
+
missing: ['apiKey', 'token'],
|
|
380
|
+
summary: `Trello setup is invalid: ${error?.message || 'unknown error'}`,
|
|
381
|
+
setupMode: 'user',
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function loadExistingAccessMode(userId, agentId) {
|
|
387
|
+
const connection = db
|
|
388
|
+
.prepare(
|
|
389
|
+
`SELECT metadata_json
|
|
390
|
+
FROM integration_connections
|
|
391
|
+
WHERE user_id = ? AND agent_id = ? AND provider_key = ?`,
|
|
392
|
+
)
|
|
393
|
+
.get(userId, agentId, TRELLO_APP.id);
|
|
394
|
+
return getConnectionAccessMode(connection || null);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function upsertTrelloConnection(userId, agentId, profile) {
|
|
398
|
+
const accountEmail = trelloAccountEmail(profile);
|
|
399
|
+
const accessMode = loadExistingAccessMode(userId, agentId);
|
|
400
|
+
|
|
401
|
+
db.prepare(
|
|
402
|
+
'DELETE FROM integration_connections WHERE user_id = ? AND agent_id = ? AND provider_key = ?',
|
|
403
|
+
).run(userId, agentId, TRELLO_APP.id);
|
|
404
|
+
|
|
405
|
+
db.prepare(
|
|
406
|
+
`INSERT INTO integration_connections (
|
|
407
|
+
user_id,
|
|
408
|
+
agent_id,
|
|
409
|
+
provider_key,
|
|
410
|
+
app_key,
|
|
411
|
+
status,
|
|
412
|
+
account_email,
|
|
413
|
+
scopes_json,
|
|
414
|
+
credentials_json,
|
|
415
|
+
metadata_json,
|
|
416
|
+
last_connected_at,
|
|
417
|
+
updated_at
|
|
418
|
+
) VALUES (?, ?, ?, ?, 'connected', ?, ?, ?, ?, datetime('now'), datetime('now'))`,
|
|
419
|
+
).run(
|
|
420
|
+
userId,
|
|
421
|
+
agentId,
|
|
422
|
+
TRELLO_APP.id,
|
|
423
|
+
TRELLO_APP.id,
|
|
424
|
+
accountEmail,
|
|
425
|
+
JSON.stringify(['trello:api']),
|
|
426
|
+
JSON.stringify({}),
|
|
427
|
+
JSON.stringify({
|
|
428
|
+
access_mode: accessMode,
|
|
429
|
+
trelloMemberId: profile.id || null,
|
|
430
|
+
username: profile.username || null,
|
|
431
|
+
fullName: profile.fullName || null,
|
|
432
|
+
url: profile.url || null,
|
|
433
|
+
}),
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
const connection = db
|
|
437
|
+
.prepare(
|
|
438
|
+
`SELECT * FROM integration_connections
|
|
439
|
+
WHERE user_id = ? AND agent_id = ? AND provider_key = ? AND app_key = ? AND account_email = ?`,
|
|
440
|
+
)
|
|
441
|
+
.get(userId, agentId, TRELLO_APP.id, TRELLO_APP.id, accountEmail);
|
|
442
|
+
|
|
443
|
+
return { connection, accountEmail };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function createTrelloConnectionResult(profile) {
|
|
447
|
+
return {
|
|
448
|
+
apiKey: profile.apiKey || '',
|
|
449
|
+
hasToken: true,
|
|
450
|
+
configured: true,
|
|
451
|
+
accountEmail: trelloAccountEmail(profile),
|
|
452
|
+
memberId: profile.id || null,
|
|
453
|
+
username: profile.username || null,
|
|
454
|
+
fullName: profile.fullName || null,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async function executeTrelloTool(toolName, args, connection) {
|
|
459
|
+
const config = resolveTrelloConfigForUser(connection?.user_id);
|
|
460
|
+
if (!config.configured) {
|
|
461
|
+
throw new Error('Trello API key and token are required. Reopen Official Integrations and save your setup again.');
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
switch (toolName) {
|
|
465
|
+
case 'trello_get_me': {
|
|
466
|
+
const me = await fetchTrelloMemberProfile(config);
|
|
467
|
+
return {
|
|
468
|
+
result: {
|
|
469
|
+
id: me?.id || null,
|
|
470
|
+
username: me?.username || null,
|
|
471
|
+
fullName: me?.fullName || null,
|
|
472
|
+
initials: me?.initials || null,
|
|
473
|
+
url: me?.url || null,
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
case 'trello_list_boards': {
|
|
478
|
+
const boards = await trelloRequest(config, {
|
|
479
|
+
path: '/1/members/me/boards',
|
|
480
|
+
query: {
|
|
481
|
+
fields: 'name,url,closed,dateLastActivity',
|
|
482
|
+
filter: trimText(args.filter) || 'open',
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
const limit = Math.max(1, Math.min(Number(args.limit) || 50, 200));
|
|
486
|
+
return { result: Array.isArray(boards) ? boards.slice(0, limit) : [] };
|
|
487
|
+
}
|
|
488
|
+
case 'trello_get_board': {
|
|
489
|
+
const board = await trelloRequest(config, {
|
|
490
|
+
path: `/1/boards/${encodeURIComponent(requireText(args.board_id, 'board_id'))}`,
|
|
491
|
+
query: {
|
|
492
|
+
fields: trimText(args.fields) || 'name,desc,url,closed,dateLastActivity',
|
|
493
|
+
lists: trimText(args.lists) || 'open',
|
|
494
|
+
cards: trimText(args.cards) || 'none',
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
return { result: board };
|
|
498
|
+
}
|
|
499
|
+
case 'trello_list_lists': {
|
|
500
|
+
const lists = await trelloRequest(config, {
|
|
501
|
+
path: `/1/boards/${encodeURIComponent(requireText(args.board_id, 'board_id'))}/lists`,
|
|
502
|
+
query: { fields: trimText(args.fields) || 'name,closed,pos' },
|
|
503
|
+
});
|
|
504
|
+
return { result: Array.isArray(lists) ? lists : [] };
|
|
505
|
+
}
|
|
506
|
+
case 'trello_list_cards': {
|
|
507
|
+
const boardId = trimText(args.board_id);
|
|
508
|
+
const listId = trimText(args.list_id);
|
|
509
|
+
const limit = Math.max(1, Math.min(Number(args.limit) || 100, 500));
|
|
510
|
+
const cards = await trelloRequest(config, {
|
|
511
|
+
path: listId
|
|
512
|
+
? `/1/lists/${encodeURIComponent(listId)}/cards`
|
|
513
|
+
: `/1/boards/${encodeURIComponent(requireText(boardId, 'board_id'))}/cards`,
|
|
514
|
+
query: {
|
|
515
|
+
fields: trimText(args.fields) || 'name,desc,idList,closed,pos,due,dueComplete,url',
|
|
516
|
+
filter: trimText(args.filter) || 'open',
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
return { result: Array.isArray(cards) ? cards.slice(0, limit) : [] };
|
|
520
|
+
}
|
|
521
|
+
case 'trello_get_card': {
|
|
522
|
+
const card = await trelloRequest(config, {
|
|
523
|
+
path: `/1/cards/${encodeURIComponent(requireText(args.card_id, 'card_id'))}`,
|
|
524
|
+
query: { fields: trimText(args.fields) || 'name,desc,idList,closed,pos,due,dueComplete,url' },
|
|
525
|
+
});
|
|
526
|
+
return { result: card };
|
|
527
|
+
}
|
|
528
|
+
case 'trello_search': {
|
|
529
|
+
const queryText = requireText(args.query, 'query');
|
|
530
|
+
const searchResult = await trelloRequest(config, {
|
|
531
|
+
path: '/1/search',
|
|
532
|
+
query: {
|
|
533
|
+
query: queryText,
|
|
534
|
+
modelTypes:
|
|
535
|
+
trimText(args.model_types) || 'cards,boards,lists,members,organizations',
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
const limit = Math.max(1, Math.min(Number(args.limit) || 20, 100));
|
|
539
|
+
const results = [];
|
|
540
|
+
for (const [type, items] of Object.entries(searchResult || {})) {
|
|
541
|
+
if (!Array.isArray(items)) continue;
|
|
542
|
+
for (const item of items) {
|
|
543
|
+
results.push({ type, ...item });
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return { result: { query: queryText, count: results.length, results: results.slice(0, limit) } };
|
|
547
|
+
}
|
|
548
|
+
case 'trello_create_card': {
|
|
549
|
+
const result = await trelloRequest(config, {
|
|
550
|
+
method: 'POST',
|
|
551
|
+
path: '/1/cards',
|
|
552
|
+
query: {
|
|
553
|
+
idList: requireText(args.list_id, 'list_id'),
|
|
554
|
+
name: requireText(args.name, 'name'),
|
|
555
|
+
...(trimText(args.desc) ? { desc: trimText(args.desc) } : {}),
|
|
556
|
+
...(trimText(args.due) ? { due: trimText(args.due) } : {}),
|
|
557
|
+
},
|
|
558
|
+
});
|
|
559
|
+
return { result };
|
|
560
|
+
}
|
|
561
|
+
case 'trello_update_card': {
|
|
562
|
+
const query = {};
|
|
563
|
+
for (const [key, value] of Object.entries({
|
|
564
|
+
name: args.name,
|
|
565
|
+
desc: args.desc,
|
|
566
|
+
closed: args.closed,
|
|
567
|
+
idList: args.idList,
|
|
568
|
+
pos: args.pos,
|
|
569
|
+
due: args.due,
|
|
570
|
+
dueComplete: args.dueComplete,
|
|
571
|
+
})) {
|
|
572
|
+
if (value === undefined || value === null) continue;
|
|
573
|
+
if (typeof value === 'string' && !trimText(value)) continue;
|
|
574
|
+
query[key] = value;
|
|
575
|
+
}
|
|
576
|
+
if (Object.keys(query).length === 0) {
|
|
577
|
+
throw new Error('At least one update field is required.');
|
|
578
|
+
}
|
|
579
|
+
const result = await trelloRequest(config, {
|
|
580
|
+
method: 'PUT',
|
|
581
|
+
path: `/1/cards/${encodeURIComponent(requireText(args.card_id, 'card_id'))}`,
|
|
582
|
+
query,
|
|
583
|
+
});
|
|
584
|
+
return { result };
|
|
585
|
+
}
|
|
586
|
+
case 'trello_add_comment': {
|
|
587
|
+
const result = await trelloRequest(config, {
|
|
588
|
+
method: 'POST',
|
|
589
|
+
path: `/1/cards/${encodeURIComponent(requireText(args.card_id, 'card_id'))}/actions/comments`,
|
|
590
|
+
query: { text: requireText(args.text, 'text') },
|
|
591
|
+
});
|
|
592
|
+
return { result };
|
|
593
|
+
}
|
|
594
|
+
case 'trello_api_request': {
|
|
595
|
+
const result = await trelloRequest(config, {
|
|
596
|
+
method: args.method,
|
|
597
|
+
path: requireText(args.path, 'path'),
|
|
598
|
+
query: {
|
|
599
|
+
...(args.query && typeof args.query === 'object' ? args.query : {}),
|
|
600
|
+
...(args.body && typeof args.body === 'object' ? args.body : {}),
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
return { result };
|
|
604
|
+
}
|
|
605
|
+
default:
|
|
606
|
+
return null;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function createTrelloProvider() {
|
|
611
|
+
return {
|
|
612
|
+
key: 'trello',
|
|
613
|
+
label: 'Trello',
|
|
614
|
+
description:
|
|
615
|
+
'Official Trello integration for user-managed API key and token setup with board, list, card, and comment tools.',
|
|
616
|
+
icon: 'trello',
|
|
617
|
+
apps: [TRELLO_APP],
|
|
618
|
+
connectPrompt:
|
|
619
|
+
'Save your Trello API key and token to expose structured board, list, card, and comment tools.',
|
|
620
|
+
getApp(appId) {
|
|
621
|
+
return String(appId || '').trim() === TRELLO_APP.id ? TRELLO_APP : null;
|
|
622
|
+
},
|
|
623
|
+
getToolAppId(toolName) {
|
|
624
|
+
return toolAppMap.get(String(toolName || '').trim()) || null;
|
|
625
|
+
},
|
|
626
|
+
getEnvStatus(context = {}) {
|
|
627
|
+
return resolveTrelloEnvStatus(context.userId);
|
|
628
|
+
},
|
|
629
|
+
getToolDefinitions(options = {}) {
|
|
630
|
+
const connectedAppIds = new Set(options.connectedAppIds || []);
|
|
631
|
+
return connectedAppIds.has(TRELLO_APP.id) ? TRELLO_TOOL_DEFINITIONS.slice() : [];
|
|
632
|
+
},
|
|
633
|
+
supportsTool(toolName) {
|
|
634
|
+
return toolAppMap.has(String(toolName || '').trim());
|
|
635
|
+
},
|
|
636
|
+
buildSnapshot(connectionRows, context = {}) {
|
|
637
|
+
const env = this.getEnvStatus(context);
|
|
638
|
+
const byApp = new Map();
|
|
639
|
+
for (const row of Array.isArray(connectionRows) ? connectionRows : []) {
|
|
640
|
+
const appId = String(row.app_key || '').trim();
|
|
641
|
+
if (!byApp.has(appId)) byApp.set(appId, []);
|
|
642
|
+
byApp.get(appId).push(row);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const appSnapshots = [TRELLO_APP].map((app) => {
|
|
646
|
+
const snapshot = summarizeAppConnection(app, byApp.get(app.id) || [], env);
|
|
647
|
+
snapshot.availableToolCount =
|
|
648
|
+
env.configured && snapshot.connection.connected
|
|
649
|
+
? TRELLO_TOOL_DEFINITIONS.length
|
|
650
|
+
: 0;
|
|
651
|
+
return snapshot;
|
|
652
|
+
});
|
|
653
|
+
const connectedApps = appSnapshots.filter((app) => app.connection.connected);
|
|
654
|
+
const connectedAccounts = connectedApps.flatMap((app) =>
|
|
655
|
+
app.accounts.filter((account) => account.connected),
|
|
656
|
+
);
|
|
657
|
+
|
|
658
|
+
return {
|
|
659
|
+
id: this.key,
|
|
660
|
+
label: this.label,
|
|
661
|
+
description: this.description,
|
|
662
|
+
icon: this.icon,
|
|
663
|
+
apps: appSnapshots,
|
|
664
|
+
env,
|
|
665
|
+
connection: {
|
|
666
|
+
status: !env.configured
|
|
667
|
+
? 'env_not_configured'
|
|
668
|
+
: connectedAccounts.length > 0
|
|
669
|
+
? 'connected'
|
|
670
|
+
: 'not_connected',
|
|
671
|
+
connected: connectedAccounts.length > 0,
|
|
672
|
+
accountEmail:
|
|
673
|
+
connectedAccounts.length === 1 ? connectedAccounts[0].accountEmail : null,
|
|
674
|
+
accountCount: connectedAccounts.length,
|
|
675
|
+
appCount: connectedApps.length,
|
|
676
|
+
lastConnectedAt:
|
|
677
|
+
connectedAccounts
|
|
678
|
+
.map((account) => account.lastConnectedAt)
|
|
679
|
+
.filter(Boolean)
|
|
680
|
+
.sort()
|
|
681
|
+
.reverse()[0] || null,
|
|
682
|
+
},
|
|
683
|
+
availableToolCount: appSnapshots.reduce((total, app) => total + app.availableToolCount, 0),
|
|
684
|
+
connectPrompt: this.connectPrompt,
|
|
685
|
+
};
|
|
686
|
+
},
|
|
687
|
+
summarizeForModel(snapshot) {
|
|
688
|
+
if (!snapshot?.env?.configured) {
|
|
689
|
+
return 'Trello: setup is not complete for this user yet. Tell them to finish Trello setup in Official Integrations first.';
|
|
690
|
+
}
|
|
691
|
+
if (!snapshot.connection?.connected) {
|
|
692
|
+
return 'Trello: setup is ready, but no Trello account is connected yet. Tell the user to finish setup in Official Integrations first.';
|
|
693
|
+
}
|
|
694
|
+
return 'Trello: native Trello access is connected in this run with board, list, card, comment, and search tools.';
|
|
695
|
+
},
|
|
696
|
+
async executeTool(toolName, args, connection) {
|
|
697
|
+
return executeTrelloTool(toolName, args, connection);
|
|
698
|
+
},
|
|
699
|
+
getUserConfig({ userId }) {
|
|
700
|
+
return sanitizeTrelloUserConfigForClient(getProviderConfig(Number(userId), 'trello'));
|
|
701
|
+
},
|
|
702
|
+
async saveUserConfig({ userId, agentId, config }) {
|
|
703
|
+
const normalizedUserId = Number(userId);
|
|
704
|
+
if (!Number.isInteger(normalizedUserId) || normalizedUserId <= 0) {
|
|
705
|
+
throw new Error('A valid user is required to save Trello configuration.');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const scopedAgentId = resolveAgentId(normalizedUserId, agentId || null);
|
|
709
|
+
const existingConfig = parseConfigInput(getProviderConfig(normalizedUserId, 'trello'));
|
|
710
|
+
const resolvedConfig = parseConfigInput(config, existingConfig);
|
|
711
|
+
|
|
712
|
+
if (!resolvedConfig.apiKey) {
|
|
713
|
+
throw new Error('Trello API key is required.');
|
|
714
|
+
}
|
|
715
|
+
if (!resolvedConfig.token) {
|
|
716
|
+
throw new Error('Trello token is required.');
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const profile = await fetchTrelloMemberProfile(resolvedConfig);
|
|
720
|
+
setProviderConfig(normalizedUserId, 'trello', resolvedConfig);
|
|
721
|
+
upsertTrelloConnection(normalizedUserId, scopedAgentId, profile || {});
|
|
722
|
+
return createTrelloConnectionResult({
|
|
723
|
+
...profile,
|
|
724
|
+
apiKey: resolvedConfig.apiKey,
|
|
725
|
+
});
|
|
726
|
+
},
|
|
727
|
+
clearUserConfig({ userId, agentId }) {
|
|
728
|
+
const normalizedUserId = Number(userId);
|
|
729
|
+
if (!Number.isInteger(normalizedUserId) || normalizedUserId <= 0) {
|
|
730
|
+
throw new Error('A valid user is required to clear Trello configuration.');
|
|
731
|
+
}
|
|
732
|
+
const scopedAgentId = resolveAgentId(normalizedUserId, agentId || null);
|
|
733
|
+
deleteProviderConfig(normalizedUserId, 'trello');
|
|
734
|
+
db.prepare(
|
|
735
|
+
'DELETE FROM integration_connections WHERE user_id = ? AND agent_id = ? AND provider_key = ?',
|
|
736
|
+
).run(normalizedUserId, scopedAgentId, TRELLO_APP.id);
|
|
737
|
+
return { cleared: true };
|
|
738
|
+
},
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
module.exports = {
|
|
743
|
+
createTrelloProvider,
|
|
744
|
+
};
|