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.
Files changed (264) hide show
  1. package/.env.example +39 -0
  2. package/README.md +2 -0
  3. package/docs/capabilities.md +2 -2
  4. package/docs/configuration.md +13 -5
  5. package/docs/integrations.md +4 -1
  6. package/flutter_app/.metadata +42 -0
  7. package/flutter_app/README.md +21 -0
  8. package/flutter_app/analysis_options.yaml +32 -0
  9. package/flutter_app/android/app/build.gradle.kts +109 -0
  10. package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  11. package/flutter_app/android/app/src/main/AndroidManifest.xml +147 -0
  12. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +747 -0
  13. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
  14. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
  15. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
  16. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
  17. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
  18. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
  19. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
  20. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
  21. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
  22. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
  23. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
  24. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
  25. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
  26. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
  27. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
  28. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
  29. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
  30. package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  31. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
  32. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
  33. package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  34. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
  35. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
  36. package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
  37. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  38. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  39. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  40. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  41. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  42. package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
  43. package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  44. package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  45. package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
  46. package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
  47. package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
  48. package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  49. package/flutter_app/android/build.gradle.kts +24 -0
  50. package/flutter_app/android/ci-release.keystore +0 -0
  51. package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  52. package/flutter_app/android/gradle.properties +3 -0
  53. package/flutter_app/android/key.properties +4 -0
  54. package/flutter_app/android/settings.gradle.kts +26 -0
  55. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  56. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  57. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  58. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  59. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  60. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  61. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  62. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  63. package/flutter_app/lib/features/location/location_service.dart +119 -0
  64. package/flutter_app/lib/features/notifications/notification_interceptor.dart +97 -0
  65. package/flutter_app/lib/main.dart +23057 -0
  66. package/flutter_app/lib/main_app_shell.dart +1682 -0
  67. package/flutter_app/lib/main_integrations.dart +931 -0
  68. package/flutter_app/lib/main_launcher.dart +959 -0
  69. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  70. package/flutter_app/lib/main_models.dart +3473 -0
  71. package/flutter_app/lib/main_shared.dart +2861 -0
  72. package/flutter_app/lib/main_theme.dart +204 -0
  73. package/flutter_app/lib/main_voice_assistant.dart +831 -0
  74. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  75. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  76. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  77. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  78. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  79. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  80. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  81. package/flutter_app/lib/src/app_launch_bridge.dart +29 -0
  82. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  83. package/flutter_app/lib/src/backend_client.dart +1833 -0
  84. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  85. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  86. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  87. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  88. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  89. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  90. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  91. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  92. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  93. package/flutter_app/lib/src/health_bridge.dart +136 -0
  94. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  95. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  96. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  97. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  98. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  99. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  100. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  101. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  102. package/flutter_app/lib/src/oauth_launcher_io.dart +77 -0
  103. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  104. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  105. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  106. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  107. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  108. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  109. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  110. package/flutter_app/lib/src/theme/palette.dart +81 -0
  111. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  112. package/flutter_app/linux/CMakeLists.txt +128 -0
  113. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  114. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +43 -0
  115. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  116. package/flutter_app/linux/flutter/generated_plugins.cmake +31 -0
  117. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  118. package/flutter_app/linux/runner/main.cc +6 -0
  119. package/flutter_app/linux/runner/my_application.cc +144 -0
  120. package/flutter_app/linux/runner/my_application.h +18 -0
  121. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  122. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  123. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  124. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +40 -0
  125. package/flutter_app/macos/Podfile +42 -0
  126. package/flutter_app/macos/Podfile.lock +87 -0
  127. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  128. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  129. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  130. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  131. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  132. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  133. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  134. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  135. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  136. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  137. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  138. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  139. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  140. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  141. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  142. package/flutter_app/macos/Runner/Info.plist +36 -0
  143. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  144. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  145. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  146. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  147. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  148. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  149. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  150. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  151. package/flutter_app/patch_strings.py +12 -0
  152. package/flutter_app/pubspec.lock +1088 -0
  153. package/flutter_app/pubspec.yaml +53 -0
  154. package/flutter_app/test/messaging_access_summary_test.dart +22 -0
  155. package/flutter_app/test/recording_payloads_test.dart +53 -0
  156. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  157. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  158. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  159. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  160. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  161. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  162. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  163. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  164. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  165. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  166. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  167. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  168. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  169. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  170. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  171. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  172. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  173. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  174. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  175. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  176. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  177. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  178. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  179. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  180. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  181. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  182. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  183. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  184. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  185. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  186. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  187. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  188. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  189. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  190. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  191. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  192. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  193. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  194. package/flutter_app/web/favicon.png +0 -0
  195. package/flutter_app/web/favicon.svg +12 -0
  196. package/flutter_app/web/icons/Icon-192.png +0 -0
  197. package/flutter_app/web/icons/Icon-512.png +0 -0
  198. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  199. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  200. package/flutter_app/web/index.html +39 -0
  201. package/flutter_app/web/manifest.json +35 -0
  202. package/flutter_app/windows/CMakeLists.txt +108 -0
  203. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  204. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +47 -0
  205. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  206. package/flutter_app/windows/flutter/generated_plugins.cmake +35 -0
  207. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  208. package/flutter_app/windows/runner/Runner.rc +121 -0
  209. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  210. package/flutter_app/windows/runner/flutter_window.h +37 -0
  211. package/flutter_app/windows/runner/main.cpp +53 -0
  212. package/flutter_app/windows/runner/resource.h +16 -0
  213. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  214. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  215. package/flutter_app/windows/runner/utils.cpp +65 -0
  216. package/flutter_app/windows/runner/utils.h +19 -0
  217. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  218. package/flutter_app/windows/runner/win32_window.h +102 -0
  219. package/lib/manager.js +231 -7
  220. package/package.json +3 -1
  221. package/server/db/database.js +68 -0
  222. package/server/http/middleware.js +50 -0
  223. package/server/http/routes.js +3 -1
  224. package/server/index.js +1 -0
  225. package/server/public/.last_build_id +1 -1
  226. package/server/public/assets/NOTICES +61 -0
  227. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  228. package/server/public/flutter_bootstrap.js +1 -1
  229. package/server/public/main.dart.js +65262 -64422
  230. package/server/routes/integrations.js +86 -0
  231. package/server/routes/memory.js +11 -2
  232. package/server/routes/screenHistory.js +46 -0
  233. package/server/routes/triggers.js +81 -0
  234. package/server/services/ai/models.js +30 -0
  235. package/server/services/ai/providers/githubCopilot.js +97 -0
  236. package/server/services/ai/providers/openai.js +2 -1
  237. package/server/services/ai/providers/openaiCodex.js +31 -0
  238. package/server/services/ai/settings.js +20 -0
  239. package/server/services/ai/systemPrompt.js +1 -1
  240. package/server/services/ai/tools.js +35 -6
  241. package/server/services/browser/controller.js +47 -3
  242. package/server/services/desktop/screenRecorder.js +172 -0
  243. package/server/services/integrations/env.js +5 -0
  244. package/server/services/integrations/github/common.js +106 -0
  245. package/server/services/integrations/github/provider.js +499 -0
  246. package/server/services/integrations/github/repos.js +1124 -0
  247. package/server/services/integrations/home_assistant/provider.js +306 -26
  248. package/server/services/integrations/manager.js +63 -7
  249. package/server/services/integrations/oauth_provider.js +13 -6
  250. package/server/services/integrations/provider_config_store.js +76 -0
  251. package/server/services/integrations/registry.js +4 -0
  252. package/server/services/integrations/trello/provider.js +744 -0
  253. package/server/services/integrations/whatsapp/provider.js +6 -2
  254. package/server/services/manager.js +22 -0
  255. package/server/services/memory/manager.js +39 -2
  256. package/server/services/skills/base_catalog.js +33 -0
  257. package/server/services/tasks/adapters/index.js +1 -0
  258. package/server/services/tasks/adapters/manual.js +12 -0
  259. package/server/services/tasks/runtime.js +1 -1
  260. package/server/services/voice/openaiClient.js +4 -1
  261. package/server/services/voice/providers.js +2 -1
  262. package/server/services/widgets/service.js +49 -4
  263. package/server/utils/local_secrets.js +56 -0
  264. package/server/utils/logger.js +37 -9
@@ -0,0 +1,499 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const { describeEnvStatus, resolveGithubOAuthConfig } = require('../env');
5
+ const { decryptValue } = require('../secrets');
6
+ const { getConnectionAccessMode } = require('../access');
7
+ const { githubToolDefinitions, executeGithubTool } = require('./repos');
8
+ const { base64UrlSha256 } = require('./common');
9
+
10
+ const GITHUB_ACCOUNT_IDENTITY_SCOPES = [
11
+ 'read:user',
12
+ 'user:email',
13
+ ];
14
+
15
+ const GITHUB_DEFAULT_SCOPES = [
16
+ 'repo',
17
+ 'workflow',
18
+ 'read:org',
19
+ ];
20
+
21
+ const GITHUB_APPS = [
22
+ {
23
+ id: 'repos',
24
+ label: 'Repositories',
25
+ description: 'Full repository management - issues, PRs, code, branches, and CI/CD workflows.',
26
+ scopes: GITHUB_DEFAULT_SCOPES,
27
+ toolDefinitions: githubToolDefinitions,
28
+ executor: executeGithubTool,
29
+ },
30
+ ];
31
+
32
+ const appById = new Map(GITHUB_APPS.map((app) => [app.id, app]));
33
+
34
+ const toolAppMap = new Map(
35
+ githubToolDefinitions.map((tool) => [tool.name, tool.appId || 'repos']),
36
+ );
37
+
38
+ function createOAuthClient() {
39
+ const config = resolveGithubOAuthConfig();
40
+ return {
41
+ config,
42
+ };
43
+ }
44
+
45
+ async function buildAuthorizedClient(connection) {
46
+ let credentials = {};
47
+ try {
48
+ credentials = JSON.parse(
49
+ decryptValue(connection.credentials_json || '{}') || '{}',
50
+ );
51
+ } catch {
52
+ credentials = {};
53
+ }
54
+ return {
55
+ token: credentials.access_token,
56
+ credentials,
57
+ };
58
+ }
59
+
60
+ function getApp(appId) {
61
+ return appById.get(String(appId || '').trim()) || null;
62
+ }
63
+
64
+ function getAppScopes(appId) {
65
+ const app = getApp(appId);
66
+ if (!app) {
67
+ throw new Error(`Unknown GitHub app: ${appId}`);
68
+ }
69
+ return Array.from(new Set([...GITHUB_ACCOUNT_IDENTITY_SCOPES, ...app.scopes]));
70
+ }
71
+
72
+ function getAllGithubScopes() {
73
+ return Array.from(
74
+ new Set([
75
+ ...GITHUB_ACCOUNT_IDENTITY_SCOPES,
76
+ ...GITHUB_APPS.flatMap((app) => app.scopes || []),
77
+ ]),
78
+ );
79
+ }
80
+
81
+ function sortConnections(rows) {
82
+ return rows.slice().sort((left, right) => {
83
+ const leftLogin = String(left.account_email || '').toLowerCase();
84
+ const rightLogin = String(right.account_email || '').toLowerCase();
85
+ if (leftLogin !== rightLogin) return leftLogin.localeCompare(rightLogin);
86
+ return String(right.updated_at || '').localeCompare(String(left.updated_at || ''));
87
+ });
88
+ }
89
+
90
+ function summarizeAccountRow(row, envStatus) {
91
+ if (!envStatus.configured) {
92
+ return {
93
+ id: row?.id || null,
94
+ status: 'env_not_configured',
95
+ connected: false,
96
+ accountEmail: row?.account_email || null,
97
+ lastConnectedAt: row?.last_connected_at || null,
98
+ };
99
+ }
100
+
101
+ if (!row) {
102
+ return {
103
+ id: null,
104
+ status: 'not_connected',
105
+ connected: false,
106
+ accountEmail: null,
107
+ lastConnectedAt: null,
108
+ accessMode: 'read_write',
109
+ };
110
+ }
111
+
112
+ return {
113
+ id: row.id || null,
114
+ status: row.status || 'not_connected',
115
+ connected: row.status === 'connected',
116
+ accountEmail: row.account_email || null,
117
+ lastConnectedAt: row.last_connected_at || null,
118
+ accessMode: getConnectionAccessMode(row),
119
+ };
120
+ }
121
+
122
+ function summarizeAppConnection(app, connectionRows, envStatus) {
123
+ const accounts = sortConnections(connectionRows).map((row) =>
124
+ summarizeAccountRow(row, envStatus),
125
+ );
126
+ const connectedAccounts = accounts.filter((account) => account.connected);
127
+ const latestConnectedAt = connectedAccounts
128
+ .map((account) => account.lastConnectedAt)
129
+ .filter(Boolean)
130
+ .sort()
131
+ .reverse()[0] || null;
132
+ const status = !envStatus.configured
133
+ ? 'env_not_configured'
134
+ : connectedAccounts.length > 0
135
+ ? 'connected'
136
+ : accounts.some((account) => account.status === 'authorizing')
137
+ ? 'authorizing'
138
+ : 'not_connected';
139
+
140
+ return {
141
+ id: app.id,
142
+ label: app.label,
143
+ description: app.description,
144
+ accounts,
145
+ connection: {
146
+ status,
147
+ connected: connectedAccounts.length > 0,
148
+ accountCount: connectedAccounts.length,
149
+ accountEmail:
150
+ connectedAccounts.length === 1
151
+ ? connectedAccounts[0].accountEmail
152
+ : null,
153
+ lastConnectedAt: latestConnectedAt,
154
+ },
155
+ availableToolCount:
156
+ envStatus.configured && connectedAccounts.length > 0
157
+ ? app.toolDefinitions.length
158
+ : 0,
159
+ };
160
+ }
161
+
162
+ async function executeGithubRepoTool(toolName, args, connection) {
163
+ const auth = await buildAuthorizedClient(connection);
164
+ if (!auth.token) {
165
+ throw new Error('GitHub access token is missing or expired. Please reconnect your GitHub account.');
166
+ }
167
+ const appId = toolAppMap.get(String(toolName || '').trim());
168
+ const app = getApp(appId);
169
+ if (!app) {
170
+ throw new Error(`Unknown tool: ${toolName}`);
171
+ }
172
+ const result = await app.executor(toolName, args, auth);
173
+ if (result === null) {
174
+ throw new Error(`Unknown tool: ${toolName}`);
175
+ }
176
+ return { result, credentials: auth.credentials };
177
+ }
178
+
179
+ function buildConnectedAppSummary(appSnapshots) {
180
+ return appSnapshots
181
+ .filter((app) => app.connection.connected)
182
+ .map((app) => {
183
+ const emails = app.accounts
184
+ .filter((account) => account.connected)
185
+ .map((account) => account.accountEmail || `connection ${account.id}`)
186
+ .join(', ');
187
+ return `${app.label}: ${emails}`;
188
+ })
189
+ .join(' | ');
190
+ }
191
+
192
+ function getAppToolNames(appId) {
193
+ const app = getApp(appId);
194
+ return Array.isArray(app?.toolDefinitions)
195
+ ? app.toolDefinitions.map((tool) => tool.name)
196
+ : [];
197
+ }
198
+
199
+ function formatAccountSummary(appSnapshot) {
200
+ const emails = appSnapshot.accounts
201
+ .filter((account) => account.connected)
202
+ .map((account) => account.accountEmail || `connection ${account.id}`);
203
+
204
+ if (emails.length === 0) return 'no connected accounts';
205
+ if (emails.length === 1) return `connected as ${emails[0]}`;
206
+ return `connected with ${emails.length} accounts: ${emails.join(', ')}`;
207
+ }
208
+
209
+ function buildModelStatusLines(appSnapshots) {
210
+ return appSnapshots.map((appSnapshot) => {
211
+ if (appSnapshot.connection.connected) {
212
+ const toolNames = getAppToolNames(appSnapshot.id).join(', ');
213
+ return `- ${appSnapshot.label}: ${formatAccountSummary(appSnapshot)}. Use built-in tools: ${toolNames}`;
214
+ }
215
+
216
+ if (appSnapshot.connection.status === 'authorizing') {
217
+ return `- ${appSnapshot.label}: connection is still being authorized.`;
218
+ }
219
+
220
+ return `- ${appSnapshot.label}: not connected yet.`;
221
+ });
222
+ }
223
+
224
+ function createGithubProvider() {
225
+ return {
226
+ key: 'github',
227
+ label: 'GitHub',
228
+ description: 'Official GitHub integration for repositories, issues, pull requests, code, branches, and CI/CD workflows.',
229
+ icon: 'github',
230
+ apps: GITHUB_APPS.map(({ id, label, description }) => ({
231
+ id,
232
+ label,
233
+ description,
234
+ })),
235
+ getApp(appId) {
236
+ return getApp(appId);
237
+ },
238
+ getToolAppId(toolName) {
239
+ return toolAppMap.get(String(toolName || '').trim()) || null;
240
+ },
241
+ getEnvStatus() {
242
+ return describeEnvStatus(resolveGithubOAuthConfig(), {
243
+ label: 'GitHub',
244
+ });
245
+ },
246
+ getToolDefinitions(options = {}) {
247
+ const connectedAppIds = new Set(options.connectedAppIds || []);
248
+ return githubToolDefinitions.filter((tool) =>
249
+ connectedAppIds.has(tool.appId || 'repos'),
250
+ );
251
+ },
252
+ supportsTool(toolName) {
253
+ return toolAppMap.has(String(toolName || '').trim());
254
+ },
255
+ buildSnapshot(connectionRows) {
256
+ const env = this.getEnvStatus();
257
+ const byApp = new Map();
258
+ for (const row of Array.isArray(connectionRows) ? connectionRows : []) {
259
+ const appId = String(row.app_key || '').trim();
260
+ if (!byApp.has(appId)) byApp.set(appId, []);
261
+ byApp.get(appId).push(row);
262
+ }
263
+
264
+ const apps = GITHUB_APPS.map((app) =>
265
+ summarizeAppConnection(app, byApp.get(app.id) || [], env),
266
+ );
267
+ const connectedApps = apps.filter((app) => app.connection.connected);
268
+ const connectedAccounts = connectedApps.flatMap((app) =>
269
+ app.accounts.filter((account) => account.connected),
270
+ );
271
+
272
+ return {
273
+ id: this.key,
274
+ label: this.label,
275
+ description: this.description,
276
+ icon: this.icon,
277
+ apps,
278
+ env,
279
+ connection: {
280
+ status: !env.configured
281
+ ? 'env_not_configured'
282
+ : connectedAccounts.length > 0
283
+ ? 'connected'
284
+ : 'not_connected',
285
+ connected: connectedAccounts.length > 0,
286
+ accountEmail:
287
+ connectedAccounts.length === 1
288
+ ? connectedAccounts[0].accountEmail
289
+ : null,
290
+ accountCount: connectedAccounts.length,
291
+ appCount: connectedApps.length,
292
+ lastConnectedAt:
293
+ connectedAccounts
294
+ .map((account) => account.lastConnectedAt)
295
+ .filter(Boolean)
296
+ .sort()
297
+ .reverse()[0] || null,
298
+ },
299
+ availableToolCount: apps.reduce(
300
+ (total, app) => total + app.availableToolCount,
301
+ 0,
302
+ ),
303
+ };
304
+ },
305
+ async beginOAuth({ state, codeVerifier, appKey }) {
306
+ const app = getApp(appKey);
307
+ if (!app) {
308
+ throw new Error(`Unknown GitHub app: ${appKey}`);
309
+ }
310
+ const { config } = createOAuthClient();
311
+ if (!config.configured) {
312
+ throw new Error(
313
+ 'GitHub still needs administrator setup before accounts can connect.',
314
+ );
315
+ }
316
+ const codeChallenge = base64UrlSha256(codeVerifier);
317
+ const scopes = getAllGithubScopes().join(' ');
318
+ const params = new URLSearchParams({
319
+ client_id: config.clientId,
320
+ redirect_uri: config.redirectUri,
321
+ scope: scopes,
322
+ state,
323
+ code_challenge: codeChallenge,
324
+ code_challenge_method: 'S256',
325
+ });
326
+ const url = `https://github.com/login/oauth/authorize?${params.toString()}`;
327
+ return { url, appId: app.id };
328
+ },
329
+ async finishOAuth({ code, codeVerifier, appKey }) {
330
+ const app = getApp(appKey);
331
+ if (!app) {
332
+ throw new Error(`Unknown GitHub app: ${appKey}`);
333
+ }
334
+ const { config } = createOAuthClient();
335
+
336
+ const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
337
+ method: 'POST',
338
+ headers: {
339
+ 'Accept': 'application/json',
340
+ 'Content-Type': 'application/json',
341
+ },
342
+ body: JSON.stringify({
343
+ client_id: config.clientId,
344
+ client_secret: config.clientSecret,
345
+ code,
346
+ code_verifier: codeVerifier,
347
+ redirect_uri: config.redirectUri,
348
+ }),
349
+ });
350
+
351
+ const tokenBody = await tokenResponse.text();
352
+ let tokenData = {};
353
+ try {
354
+ tokenData = tokenBody ? JSON.parse(tokenBody) : {};
355
+ } catch {
356
+ tokenData = {};
357
+ }
358
+ if (!tokenResponse.ok) {
359
+ throw new Error(`GitHub OAuth token exchange failed (${tokenResponse.status}): ${tokenBody || 'No response body'}`);
360
+ }
361
+ if (tokenData.error) {
362
+ throw new Error(`GitHub OAuth error: ${tokenData.error_description || tokenData.error}`);
363
+ }
364
+
365
+ const accessToken = tokenData.access_token;
366
+ if (!accessToken) {
367
+ throw new Error('GitHub OAuth did not return an access token.');
368
+ }
369
+
370
+ const userResponse = await fetch('https://api.github.com/user', {
371
+ headers: {
372
+ 'Authorization': `Bearer ${accessToken}`,
373
+ 'Accept': 'application/vnd.github.v3+json',
374
+ },
375
+ });
376
+ if (!userResponse.ok) {
377
+ const errorBody = await userResponse.text().catch(() => 'Unknown error');
378
+ throw new Error(`GitHub user profile request failed (${userResponse.status}): ${errorBody}`);
379
+ }
380
+ const userData = await userResponse.json();
381
+ const accountEmail = String(userData.login || '').trim();
382
+ if (!accountEmail) {
383
+ throw new Error('GitHub API did not return a user login.');
384
+ }
385
+
386
+ return {
387
+ appId: app.id,
388
+ accountEmail,
389
+ credentials: {
390
+ access_token: accessToken,
391
+ token_type: tokenData.token_type,
392
+ scope: tokenData.scope,
393
+ },
394
+ scopes: tokenData.scope ? tokenData.scope.split(' ') : [],
395
+ metadata: {
396
+ appId: app.id,
397
+ userId: userData.id,
398
+ },
399
+ };
400
+ },
401
+ async disconnect(connectionRow) {
402
+ if (!connectionRow?.credentials_json) return;
403
+ try {
404
+ const credentials = JSON.parse(decryptValue(connectionRow.credentials_json));
405
+ const accessToken = String(credentials?.access_token || '').trim();
406
+ if (!accessToken) return;
407
+
408
+ const { config } = createOAuthClient();
409
+ const clientId = String(config?.clientId || '').trim();
410
+ const clientSecret = String(config?.clientSecret || '').trim();
411
+ if (!clientId || !clientSecret) {
412
+ console.warn('[GitHub] Disconnect revoke skipped because OAuth client credentials are missing.');
413
+ return;
414
+ }
415
+
416
+ const basic = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
417
+ const revokeUrl = `https://api.github.com/applications/${encodeURIComponent(clientId)}/token`;
418
+ const revokeResponse = await fetch(revokeUrl, {
419
+ method: 'DELETE',
420
+ headers: {
421
+ 'Authorization': `Basic ${basic}`,
422
+ 'Accept': 'application/json',
423
+ 'Content-Type': 'application/json',
424
+ },
425
+ body: JSON.stringify({
426
+ access_token: accessToken,
427
+ }),
428
+ });
429
+
430
+ if (!revokeResponse.ok) {
431
+ const revokeBody = await revokeResponse.text().catch(() => 'Unknown error');
432
+ console.warn(
433
+ `[GitHub] Failed to revoke token for disconnect (connection ${connectionRow?.id || 'unknown'}): ${revokeResponse.status} ${revokeBody}`,
434
+ );
435
+ }
436
+ } catch (error) {
437
+ console.warn(
438
+ `[GitHub] Failed to revoke token for disconnect (connection ${connectionRow?.id || 'unknown'}): ${error?.message || error}`,
439
+ );
440
+ }
441
+ },
442
+ async executeTool(toolName, args, connectionRow) {
443
+ return executeGithubRepoTool(toolName, args, connectionRow);
444
+ },
445
+ summarizeConnection(connectionRows) {
446
+ const snapshot = this.buildSnapshot(connectionRows);
447
+ if (!snapshot.connection.connected) {
448
+ if (snapshot.connection.status === 'env_not_configured') {
449
+ return 'GitHub still needs administrator setup before accounts can connect.';
450
+ }
451
+ return 'GitHub is not connected.';
452
+ }
453
+
454
+ return snapshot.apps
455
+ .filter((app) => app.connection.connected)
456
+ .map((app) => {
457
+ const accounts = app.accounts
458
+ .filter((account) => account.connected)
459
+ .map((account) => account.accountEmail || 'unknown account')
460
+ .join(', ');
461
+ return `${app.label}: ${accounts}`;
462
+ })
463
+ .join(' | ');
464
+ },
465
+ summarizeForModel(snapshot) {
466
+ if (!snapshot?.env?.configured) {
467
+ return `${this.label}: workspace setup is not complete yet. If the user wants to use it, tell them to open Official Integrations and ask an administrator to finish setup first.`;
468
+ }
469
+
470
+ const statusLines = buildModelStatusLines(snapshot.apps);
471
+
472
+ if (!snapshot.connection.connected) {
473
+ return [
474
+ `${this.label}: workspace setup is ready, but no app accounts are connected yet. If the user wants to use it, tell them to connect an account in Official Integrations first.`,
475
+ ...statusLines,
476
+ ].join('\n');
477
+ }
478
+
479
+ return [
480
+ `${this.label}: some app-specific native access is connected on this server in this run. Only the listed connected apps are available through built-in tools.`,
481
+ `Connected apps: ${buildConnectedAppSummary(snapshot.apps)}`,
482
+ ...statusLines,
483
+ ].join('\n');
484
+ },
485
+ };
486
+ }
487
+
488
+ module.exports = {
489
+ createGithubProvider,
490
+ GITHUB_ACCOUNT_IDENTITY_SCOPES,
491
+ GITHUB_APPS: GITHUB_APPS.map(
492
+ ({ id, label, description, scopes }) => ({
493
+ id,
494
+ label,
495
+ description,
496
+ scopes: scopes.slice(),
497
+ }),
498
+ ),
499
+ };