neoagent 2.3.1-beta.11 → 2.3.1-beta.111

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 (390) hide show
  1. package/.env.example +53 -5
  2. package/README.md +16 -7
  3. package/com.neoagent.plist +1 -1
  4. package/docs/automation.md +82 -41
  5. package/docs/capabilities.md +85 -89
  6. package/docs/configuration.md +87 -94
  7. package/docs/getting-started.md +46 -27
  8. package/docs/hardware.md +1 -1
  9. package/docs/index.md +24 -30
  10. package/docs/integrations.md +46 -64
  11. package/docs/migration.md +41 -146
  12. package/docs/operations.md +23 -20
  13. package/docs/skills.md +20 -20
  14. package/docs/why-neoagent.md +15 -13
  15. package/extensions/chrome-browser/background.mjs +10 -2
  16. package/flutter_app/.metadata +42 -0
  17. package/flutter_app/DESIGN_SYSTEM.md +166 -0
  18. package/flutter_app/README.md +21 -0
  19. package/flutter_app/analysis_options.yaml +32 -0
  20. package/flutter_app/android/app/build.gradle.kts +111 -0
  21. package/flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  22. package/flutter_app/android/app/src/main/AndroidManifest.xml +185 -0
  23. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +888 -0
  24. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/auto/NeoAgentCarAppService.kt +114 -0
  25. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthConnectGateway.kt +280 -0
  26. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncNotifications.kt +113 -0
  27. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncPayload.kt +57 -0
  28. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncScheduler.kt +78 -0
  29. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/HealthSyncWorker.kt +253 -0
  30. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/health/PermissionsRationaleActivity.kt +46 -0
  31. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingBootReceiver.kt +21 -0
  32. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingForegroundService.kt +586 -0
  33. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingStateStore.kt +78 -0
  34. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/recording/RecordingUploadClient.kt +104 -0
  35. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnection.kt +118 -0
  36. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/telecom/NeoAgentConnectionService.kt +86 -0
  37. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiHomeWidgetProvider.kt +457 -0
  38. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/AiWidgetStore.kt +194 -0
  39. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/VoiceLaunchWidgetProvider.kt +67 -0
  40. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetConfigActivity.kt +228 -0
  41. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncScheduler.kt +72 -0
  42. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetSyncWorker.kt +186 -0
  43. package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/widgets/WidgetTaskRunWorker.kt +210 -0
  44. package/flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  45. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_bg.xml +11 -0
  46. package/flutter_app/android/app/src/main/res/drawable/neoagent_ai_widget_task_bg.xml +8 -0
  47. package/flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  48. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget.xml +138 -0
  49. package/flutter_app/android/app/src/main/res/layout/neoagent_ai_widget_task_row.xml +52 -0
  50. package/flutter_app/android/app/src/main/res/layout/neoagent_voice_widget.xml +49 -0
  51. package/flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  52. package/flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  53. package/flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  54. package/flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  55. package/flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  56. package/flutter_app/android/app/src/main/res/values/arrays.xml +8 -0
  57. package/flutter_app/android/app/src/main/res/values/strings.xml +12 -0
  58. package/flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  59. package/flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  60. package/flutter_app/android/app/src/main/res/xml/automotive_app_desc.xml +4 -0
  61. package/flutter_app/android/app/src/main/res/xml/file_paths.xml +6 -0
  62. package/flutter_app/android/app/src/main/res/xml/neoagent_ai_widget_info.xml +12 -0
  63. package/flutter_app/android/app/src/main/res/xml/neoagent_voice_widget_info.xml +12 -0
  64. package/flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  65. package/flutter_app/android/build.gradle.kts +24 -0
  66. package/flutter_app/android/ci-release.keystore +0 -0
  67. package/flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  68. package/flutter_app/android/gradle.properties +3 -0
  69. package/flutter_app/android/key.properties +4 -0
  70. package/flutter_app/android/settings.gradle.kts +26 -0
  71. package/flutter_app/assets/branding/app_icon_1024.png +0 -0
  72. package/flutter_app/assets/branding/app_icon_128.png +0 -0
  73. package/flutter_app/assets/branding/app_icon_192.png +0 -0
  74. package/flutter_app/assets/branding/app_icon_256.png +0 -0
  75. package/flutter_app/assets/branding/app_icon_32.png +0 -0
  76. package/flutter_app/assets/branding/app_icon_512.png +0 -0
  77. package/flutter_app/assets/branding/app_icon_64.png +0 -0
  78. package/flutter_app/assets/branding/onboarding_intro.mp4 +0 -0
  79. package/flutter_app/assets/branding/tray_icon_template.png +0 -0
  80. package/flutter_app/lib/features/location/location_service.dart +117 -0
  81. package/flutter_app/lib/features/notifications/notification_interceptor.dart +73 -0
  82. package/flutter_app/lib/features/onboarding/onboarding_chrome.dart +571 -0
  83. package/flutter_app/lib/features/onboarding/onboarding_messaging_step.dart +275 -0
  84. package/flutter_app/lib/features/onboarding/onboarding_model_step.dart +366 -0
  85. package/flutter_app/lib/features/onboarding/onboarding_shell.dart +57 -0
  86. package/flutter_app/lib/features/onboarding/onboarding_video_step.dart +218 -0
  87. package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +46 -0
  88. package/flutter_app/lib/main.dart +107 -0
  89. package/flutter_app/lib/main_account_settings.dart +1340 -0
  90. package/flutter_app/lib/main_admin.dart +900 -0
  91. package/flutter_app/lib/main_app_shell.dart +2162 -0
  92. package/flutter_app/lib/main_chat.dart +3902 -0
  93. package/flutter_app/lib/main_controller.dart +7229 -0
  94. package/flutter_app/lib/main_devices.dart +1738 -0
  95. package/flutter_app/lib/main_integrations.dart +917 -0
  96. package/flutter_app/lib/main_launcher.dart +959 -0
  97. package/flutter_app/lib/main_launcher_entry.dart +5 -0
  98. package/flutter_app/lib/main_models.dart +3807 -0
  99. package/flutter_app/lib/main_navigation.dart +206 -0
  100. package/flutter_app/lib/main_operations.dart +5249 -0
  101. package/flutter_app/lib/main_recordings.dart +920 -0
  102. package/flutter_app/lib/main_runtime.dart +869 -0
  103. package/flutter_app/lib/main_settings.dart +2288 -0
  104. package/flutter_app/lib/main_shared.dart +3393 -0
  105. package/flutter_app/lib/main_spacing.dart +33 -0
  106. package/flutter_app/lib/main_theme.dart +276 -0
  107. package/flutter_app/lib/main_unified.dart +388 -0
  108. package/flutter_app/lib/main_voice_assistant.dart +957 -0
  109. package/flutter_app/lib/src/analytics_service.dart +294 -0
  110. package/flutter_app/lib/src/android_apk_drop_zone.dart +32 -0
  111. package/flutter_app/lib/src/android_apk_drop_zone_stub.dart +16 -0
  112. package/flutter_app/lib/src/android_apk_drop_zone_web.dart +348 -0
  113. package/flutter_app/lib/src/android_app_installer.dart +22 -0
  114. package/flutter_app/lib/src/android_app_installer_io.dart +122 -0
  115. package/flutter_app/lib/src/android_app_installer_stub.dart +21 -0
  116. package/flutter_app/lib/src/android_auto_bridge.dart +59 -0
  117. package/flutter_app/lib/src/android_launcher_bridge.dart +239 -0
  118. package/flutter_app/lib/src/app_launch_bridge.dart +58 -0
  119. package/flutter_app/lib/src/app_release_updater.dart +511 -0
  120. package/flutter_app/lib/src/backend_client.dart +1870 -0
  121. package/flutter_app/lib/src/desktop_companion.dart +2 -0
  122. package/flutter_app/lib/src/desktop_companion_actions.dart +586 -0
  123. package/flutter_app/lib/src/desktop_companion_io.dart +538 -0
  124. package/flutter_app/lib/src/desktop_companion_stub.dart +59 -0
  125. package/flutter_app/lib/src/desktop_native_bridge.dart +91 -0
  126. package/flutter_app/lib/src/desktop_screen_capture.dart +21 -0
  127. package/flutter_app/lib/src/desktop_screen_capture_io.dart +142 -0
  128. package/flutter_app/lib/src/desktop_screen_capture_stub.dart +12 -0
  129. package/flutter_app/lib/src/diagnostics_logger.dart +119 -0
  130. package/flutter_app/lib/src/health_bridge.dart +136 -0
  131. package/flutter_app/lib/src/live_voice_capture.dart +85 -0
  132. package/flutter_app/lib/src/messaging_access_summary.dart +46 -0
  133. package/flutter_app/lib/src/network/app_http_client.dart +53 -0
  134. package/flutter_app/lib/src/network/app_http_client_factory.dart +6 -0
  135. package/flutter_app/lib/src/network/app_http_client_io.dart +138 -0
  136. package/flutter_app/lib/src/network/app_http_client_stub.dart +3 -0
  137. package/flutter_app/lib/src/network/app_http_client_web.dart +94 -0
  138. package/flutter_app/lib/src/oauth_launcher.dart +33 -0
  139. package/flutter_app/lib/src/oauth_launcher_io.dart +95 -0
  140. package/flutter_app/lib/src/oauth_launcher_stub.dart +33 -0
  141. package/flutter_app/lib/src/oauth_launcher_web.dart +107 -0
  142. package/flutter_app/lib/src/recording_bridge.dart +232 -0
  143. package/flutter_app/lib/src/recording_bridge_io.dart +1019 -0
  144. package/flutter_app/lib/src/recording_bridge_stub.dart +120 -0
  145. package/flutter_app/lib/src/recording_bridge_web.dart +689 -0
  146. package/flutter_app/lib/src/recording_payloads.dart +86 -0
  147. package/flutter_app/lib/src/theme/palette.dart +81 -0
  148. package/flutter_app/lib/src/web_app_update_monitor.dart +17 -0
  149. package/flutter_app/lib/src/web_app_update_monitor_stub.dart +24 -0
  150. package/flutter_app/lib/src/web_app_update_monitor_web.dart +123 -0
  151. package/flutter_app/lib/src/widget_bridge.dart +49 -0
  152. package/flutter_app/linux/CMakeLists.txt +128 -0
  153. package/flutter_app/linux/flutter/CMakeLists.txt +88 -0
  154. package/flutter_app/linux/flutter/generated_plugin_registrant.cc +47 -0
  155. package/flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  156. package/flutter_app/linux/flutter/generated_plugins.cmake +32 -0
  157. package/flutter_app/linux/runner/CMakeLists.txt +26 -0
  158. package/flutter_app/linux/runner/main.cc +6 -0
  159. package/flutter_app/linux/runner/my_application.cc +144 -0
  160. package/flutter_app/linux/runner/my_application.h +18 -0
  161. package/flutter_app/linux/runner/resources/app_icon.png +0 -0
  162. package/flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  163. package/flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  164. package/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +48 -0
  165. package/flutter_app/macos/Podfile +42 -0
  166. package/flutter_app/macos/Podfile.lock +87 -0
  167. package/flutter_app/macos/Runner/AppDelegate.swift +576 -0
  168. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  169. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  170. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  171. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  172. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  173. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  174. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  175. package/flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  176. package/flutter_app/macos/Runner/Base.lproj/MainMenu.xib +342 -0
  177. package/flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  178. package/flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  179. package/flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  180. package/flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  181. package/flutter_app/macos/Runner/DebugProfile.entitlements +16 -0
  182. package/flutter_app/macos/Runner/Info.plist +36 -0
  183. package/flutter_app/macos/Runner/MainFlutterWindow.swift +19 -0
  184. package/flutter_app/macos/Runner/Release.entitlements +12 -0
  185. package/flutter_app/macos/Runner.xcodeproj/project.pbxproj +801 -0
  186. package/flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  187. package/flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  188. package/flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +10 -0
  189. package/flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  190. package/flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  191. package/flutter_app/patch_strings.py +12 -0
  192. package/flutter_app/pubspec.lock +1256 -0
  193. package/flutter_app/pubspec.yaml +59 -0
  194. package/flutter_app/third_party/desktop_audio_capture/LICENSE +21 -0
  195. package/flutter_app/third_party/desktop_audio_capture/README.md +262 -0
  196. package/flutter_app/third_party/desktop_audio_capture/lib/audio_capture.dart +65 -0
  197. package/flutter_app/third_party/desktop_audio_capture/lib/config/mic_audio_config.dart +153 -0
  198. package/flutter_app/third_party/desktop_audio_capture/lib/config/system_adudio_config.dart +110 -0
  199. package/flutter_app/third_party/desktop_audio_capture/lib/mic/mic_audio_capture.dart +461 -0
  200. package/flutter_app/third_party/desktop_audio_capture/lib/model/audio_status.dart +91 -0
  201. package/flutter_app/third_party/desktop_audio_capture/lib/model/decibel_data.dart +106 -0
  202. package/flutter_app/third_party/desktop_audio_capture/lib/model/input_device_type.dart +219 -0
  203. package/flutter_app/third_party/desktop_audio_capture/lib/system/system_audio_capture.dart +336 -0
  204. package/flutter_app/third_party/desktop_audio_capture/linux/CMakeLists.txt +101 -0
  205. package/flutter_app/third_party/desktop_audio_capture/linux/audio_capture_plugin.cc +692 -0
  206. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/audio_capture_plugin.h +35 -0
  207. package/flutter_app/third_party/desktop_audio_capture/linux/include/audio_capture/mic_capture_plugin.h +36 -0
  208. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/audio_capture_plugin.h +32 -0
  209. package/flutter_app/third_party/desktop_audio_capture/linux/include/desktop_audio_capture/mic_capture_plugin.h +32 -0
  210. package/flutter_app/third_party/desktop_audio_capture/linux/mic_capture_plugin.cc +878 -0
  211. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/AudioCapturePlugin.swift +27 -0
  212. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/MicCapturePlugin.swift +1172 -0
  213. package/flutter_app/third_party/desktop_audio_capture/macos/Classes/SystemCapturePlugin.swift +655 -0
  214. package/flutter_app/third_party/desktop_audio_capture/macos/Resources/PrivacyInfo.xcprivacy +12 -0
  215. package/flutter_app/third_party/desktop_audio_capture/macos/desktop_audio_capture.podspec +30 -0
  216. package/flutter_app/third_party/desktop_audio_capture/pubspec.yaml +87 -0
  217. package/flutter_app/third_party/desktop_audio_capture/windows/CMakeLists.txt +105 -0
  218. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.cpp +80 -0
  219. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin.h +31 -0
  220. package/flutter_app/third_party/desktop_audio_capture/windows/audio_capture_plugin_c_api.cpp +12 -0
  221. package/flutter_app/third_party/desktop_audio_capture/windows/include/audio_capture/audio_capture_plugin_c_api.h +23 -0
  222. package/flutter_app/third_party/desktop_audio_capture/windows/include/desktop_audio_capture/audio_capture_plugin.h +25 -0
  223. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.cpp +1117 -0
  224. package/flutter_app/third_party/desktop_audio_capture/windows/mic_capture_plugin.h +115 -0
  225. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.cpp +777 -0
  226. package/flutter_app/third_party/desktop_audio_capture/windows/system_audio_capture_plugin.h +87 -0
  227. package/flutter_app/third_party/flutter_secure_storage_linux/linux/CMakeLists.txt +30 -0
  228. package/flutter_app/third_party/flutter_secure_storage_linux/linux/flutter_secure_storage_linux_plugin.cc +215 -0
  229. package/flutter_app/third_party/flutter_secure_storage_linux/linux/include/flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h +27 -0
  230. package/flutter_app/third_party/flutter_secure_storage_linux/pubspec.yaml +20 -0
  231. package/flutter_app/tool/generate_desktop_branding.py +219 -0
  232. package/flutter_app/web/favicon.png +0 -0
  233. package/flutter_app/web/favicon.svg +12 -0
  234. package/flutter_app/web/icons/Icon-192.png +0 -0
  235. package/flutter_app/web/icons/Icon-512.png +0 -0
  236. package/flutter_app/web/icons/Icon-maskable-192.png +0 -0
  237. package/flutter_app/web/icons/Icon-maskable-512.png +0 -0
  238. package/flutter_app/web/index.html +39 -0
  239. package/flutter_app/web/manifest.json +35 -0
  240. package/flutter_app/windows/CMakeLists.txt +108 -0
  241. package/flutter_app/windows/flutter/CMakeLists.txt +109 -0
  242. package/flutter_app/windows/flutter/generated_plugin_registrant.cc +50 -0
  243. package/flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  244. package/flutter_app/windows/flutter/generated_plugins.cmake +36 -0
  245. package/flutter_app/windows/runner/CMakeLists.txt +41 -0
  246. package/flutter_app/windows/runner/Runner.rc +121 -0
  247. package/flutter_app/windows/runner/flutter_window.cpp +533 -0
  248. package/flutter_app/windows/runner/flutter_window.h +37 -0
  249. package/flutter_app/windows/runner/main.cpp +53 -0
  250. package/flutter_app/windows/runner/resource.h +16 -0
  251. package/flutter_app/windows/runner/resources/app_icon.ico +0 -0
  252. package/flutter_app/windows/runner/runner.exe.manifest +14 -0
  253. package/flutter_app/windows/runner/utils.cpp +65 -0
  254. package/flutter_app/windows/runner/utils.h +19 -0
  255. package/flutter_app/windows/runner/win32_window.cpp +299 -0
  256. package/flutter_app/windows/runner/win32_window.h +102 -0
  257. package/lib/install_helpers.js +31 -0
  258. package/lib/manager.js +579 -122
  259. package/package.json +10 -3
  260. package/runtime/paths.js +137 -0
  261. package/server/config/analytics.js +30 -0
  262. package/server/db/database.js +296 -22
  263. package/server/guest-agent.android.package.json +13 -0
  264. package/server/guest-agent.browser.package.json +14 -0
  265. package/server/guest_agent.js +98 -52
  266. package/server/http/middleware.js +5 -2
  267. package/server/http/routes.js +3 -0
  268. package/server/http/static.js +41 -1
  269. package/server/index.js +15 -16
  270. package/server/public/.last_build_id +1 -1
  271. package/server/public/assets/AssetManifest.bin +1 -1
  272. package/server/public/assets/AssetManifest.bin.json +1 -1
  273. package/server/public/assets/AssetManifest.json +1 -1
  274. package/server/public/assets/NOTICES +644 -155
  275. package/server/public/assets/assets/branding/onboarding_intro.mp4 +0 -0
  276. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  277. package/server/public/assets/packages/mixpanel_flutter/assets/mixpanel.js +3 -0
  278. package/server/public/canvaskit/wimp.wasm +0 -0
  279. package/server/public/flutter_bootstrap.js +2 -2
  280. package/server/public/main.dart.js +96067 -88139
  281. package/server/routes/account.js +26 -1
  282. package/server/routes/agents.js +6 -2
  283. package/server/routes/android.js +22 -6
  284. package/server/routes/auth.js +25 -5
  285. package/server/routes/browser.js +47 -13
  286. package/server/routes/integrations.js +22 -1
  287. package/server/routes/memory.js +168 -6
  288. package/server/routes/runtime.js +14 -0
  289. package/server/routes/settings.js +102 -24
  290. package/server/routes/skills.js +12 -0
  291. package/server/routes/social_video.js +66 -0
  292. package/server/routes/tasks.js +22 -2
  293. package/server/routes/triggers.js +44 -35
  294. package/server/routes/wearable.js +67 -0
  295. package/server/routes/widgets.js +4 -4
  296. package/server/services/ai/capabilityHealth.js +23 -51
  297. package/server/services/ai/compaction.js +1 -1
  298. package/server/services/ai/deliverables/artifact_helpers.js +190 -0
  299. package/server/services/ai/deliverables/contracts.js +113 -0
  300. package/server/services/ai/deliverables/index.js +20 -0
  301. package/server/services/ai/deliverables/selector.js +94 -0
  302. package/server/services/ai/deliverables/validator.js +63 -0
  303. package/server/services/ai/deliverables/workflows.js +207 -0
  304. package/server/services/ai/engine.js +363 -27
  305. package/server/services/ai/hooks.js +127 -0
  306. package/server/services/ai/integrated_tools/index.js +98 -0
  307. package/server/services/ai/integrated_tools/remotion.js +373 -0
  308. package/server/services/ai/integrated_tools/shared.js +204 -0
  309. package/server/services/ai/integrated_tools/slidev.js +207 -0
  310. package/server/services/ai/loopPolicy.js +146 -0
  311. package/server/services/ai/models.js +31 -4
  312. package/server/services/ai/providers/claudeCode.js +49 -0
  313. package/server/services/ai/providers/openai.js +2 -1
  314. package/server/services/ai/providers/openaiCodex.js +422 -15
  315. package/server/services/ai/recordingInsights.js +11 -13
  316. package/server/services/ai/runEvents.js +100 -0
  317. package/server/services/ai/settings.js +12 -2
  318. package/server/services/ai/systemPrompt.js +15 -1
  319. package/server/services/ai/taskAnalysis.js +2 -0
  320. package/server/services/ai/toolResult.js +20 -0
  321. package/server/services/ai/toolRunner.js +46 -6
  322. package/server/services/ai/toolSelector.js +54 -1
  323. package/server/services/ai/tools.js +210 -116
  324. package/server/services/android/android_bootstrap_worker.js +63 -0
  325. package/server/services/android/controller.js +442 -1760
  326. package/server/services/bootstrap_helpers.js +0 -34
  327. package/server/services/browser/controller.js +279 -49
  328. package/server/services/cli/executor.js +40 -1
  329. package/server/services/desktop/screenRecorder.js +65 -9
  330. package/server/services/integrations/figma/provider.js +1 -0
  331. package/server/services/integrations/github/provider.js +3 -3
  332. package/server/services/integrations/google/provider.js +1 -0
  333. package/server/services/integrations/manager.js +25 -5
  334. package/server/services/integrations/microsoft/provider.js +1 -0
  335. package/server/services/integrations/oauth_provider.js +15 -5
  336. package/server/services/integrations/provider_config_store.js +22 -13
  337. package/server/services/integrations/registry.js +2 -4
  338. package/server/services/integrations/spotify/provider.js +1 -0
  339. package/server/services/integrations/trello/provider.js +842 -0
  340. package/server/services/manager.js +88 -155
  341. package/server/services/mcp/client.js +120 -23
  342. package/server/services/memory/llm_transfer.js +218 -0
  343. package/server/services/memory/manager.js +260 -33
  344. package/server/services/messaging/access_policy.js +10 -0
  345. package/server/services/messaging/automation.js +1 -1
  346. package/server/services/messaging/manager.js +64 -1
  347. package/server/services/messaging/meshtastic.js +277 -0
  348. package/server/services/messaging/meshtastic_env.js +40 -0
  349. package/server/services/messaging/meshtastic_protocol.js +530 -0
  350. package/server/services/messaging/meshtastic_tcp_transport.js +25 -0
  351. package/server/services/runtime/backends/local-vm.js +221 -158
  352. package/server/services/runtime/docker-vm-manager.js +392 -0
  353. package/server/services/runtime/guest_bootstrap.js +654 -0
  354. package/server/services/runtime/manager.js +85 -29
  355. package/server/services/runtime/settings.js +22 -31
  356. package/server/services/runtime/validation.js +16 -31
  357. package/server/services/social_video/adapters/base.js +26 -0
  358. package/server/services/social_video/adapters/index.js +27 -0
  359. package/server/services/social_video/adapters/instagram.js +17 -0
  360. package/server/services/social_video/adapters/tiktok.js +17 -0
  361. package/server/services/social_video/adapters/x.js +17 -0
  362. package/server/services/social_video/adapters/youtube.js +17 -0
  363. package/server/services/social_video/captions.js +187 -0
  364. package/server/services/social_video/frame.js +42 -0
  365. package/server/services/social_video/index.js +7 -0
  366. package/server/services/social_video/metadata.js +63 -0
  367. package/server/services/social_video/result.js +63 -0
  368. package/server/services/social_video/service.js +720 -0
  369. package/server/services/social_video/url.js +83 -0
  370. package/server/services/tasks/integration_runtime.js +1 -1
  371. package/server/services/tasks/runtime.js +1 -1
  372. package/server/services/tasks/task_repository.js +3 -6
  373. package/server/services/voice/openaiSpeech.js +6 -1
  374. package/server/services/voice/providers.js +142 -14
  375. package/server/services/voice/runtime.js +8 -8
  376. package/server/services/voice/runtimeManager.js +156 -19
  377. package/server/services/voice/turnRunner.js +29 -9
  378. package/server/services/wearable/firmware_manifest.js +353 -0
  379. package/server/services/wearable/gateway.js +350 -0
  380. package/server/services/wearable/protocol.js +45 -0
  381. package/server/services/wearable/service.js +240 -0
  382. package/server/services/websocket.js +3 -1
  383. package/server/services/widgets/focus_widget.js +137 -0
  384. package/server/services/widgets/service.js +133 -2
  385. package/server/services/workspace/manager.js +301 -0
  386. package/server/utils/deployment.js +16 -9
  387. package/server/services/integrations/home_assistant/provider.js +0 -630
  388. package/server/services/messaging/access_policy.test.js +0 -228
  389. package/server/services/runtime/backends/host.js +0 -37
  390. package/server/services/runtime/qemu.js +0 -391
@@ -0,0 +1,654 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { spawnSync } = require('child_process');
4
+ const { DATA_DIR } = require('../../../runtime/paths');
5
+
6
+ const VM_ROOT = path.join(DATA_DIR, 'runtime-vms');
7
+ const GUEST_BOOTSTRAP_ROOT = path.join(VM_ROOT, 'guest-bootstrap');
8
+ const REPO_ROOT = path.resolve(__dirname, '../../..');
9
+ const GUEST_PAYLOAD_PROFILES = Object.freeze({
10
+ browser_cli: [
11
+ { source: 'server/guest-agent.browser.package.json', target: 'package.json' },
12
+ { source: 'runtime/env.js', target: 'runtime/env.js' },
13
+ { source: 'runtime/paths.js', target: 'runtime/paths.js' },
14
+ { source: 'server/guest_agent.js', target: 'server/guest_agent.js' },
15
+ { source: 'server/services/cli', target: 'server/services/cli' },
16
+ { source: 'server/services/browser', target: 'server/services/browser' },
17
+ ],
18
+ android: [
19
+ { source: 'server/guest-agent.android.package.json', target: 'package.json' },
20
+ { source: 'runtime/env.js', target: 'runtime/env.js' },
21
+ { source: 'runtime/paths.js', target: 'runtime/paths.js' },
22
+ { source: 'server/guest_agent.js', target: 'server/guest_agent.js' },
23
+ { source: 'server/services/cli', target: 'server/services/cli' },
24
+ { source: 'server/services/android', target: 'server/services/android' },
25
+ ],
26
+ });
27
+
28
+ fs.mkdirSync(GUEST_BOOTSTRAP_ROOT, { recursive: true });
29
+
30
+ function encodeGuestToken(value) {
31
+ return Buffer.from(String(value || ''), 'utf8').toString('base64');
32
+ }
33
+
34
+ function normalizeRuntimeProfile(runtimeProfile) {
35
+ return runtimeProfile === 'android' ? 'android' : 'browser_cli';
36
+ }
37
+
38
+ function createGuestPayloadArchive(seedDir, runtimeProfile = 'browser_cli') {
39
+ const seedRoot = path.dirname(seedDir);
40
+ const stagingRoot = path.join(seedRoot, 'guest-payload');
41
+ const archivePath = path.join(seedRoot, 'guest-payload.tar.gz');
42
+ const payloadEntries = GUEST_PAYLOAD_PROFILES[normalizeRuntimeProfile(runtimeProfile)];
43
+ fs.rmSync(stagingRoot, { recursive: true, force: true });
44
+ fs.rmSync(archivePath, { force: true });
45
+ fs.mkdirSync(stagingRoot, { recursive: true });
46
+
47
+ for (const entry of payloadEntries) {
48
+ const sourcePath = path.join(REPO_ROOT, entry.source);
49
+ const targetPath = path.join(stagingRoot, entry.target);
50
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
51
+ if (fs.statSync(sourcePath).isDirectory()) {
52
+ fs.cpSync(sourcePath, targetPath, { recursive: true, force: true });
53
+ } else {
54
+ fs.copyFileSync(sourcePath, targetPath);
55
+ }
56
+ }
57
+
58
+ const tarResult = spawnSync('tar', ['-czf', archivePath, '-C', stagingRoot, '.'], {
59
+ encoding: 'utf8',
60
+ stdio: ['ignore', 'pipe', 'pipe'],
61
+ });
62
+ if (tarResult.status !== 0 || !fs.existsSync(archivePath)) {
63
+ throw new Error(
64
+ String(tarResult.stderr || tarResult.stdout || tarResult.error?.message || 'Failed to create guest payload archive.')
65
+ .trim(),
66
+ );
67
+ }
68
+ fs.rmSync(stagingRoot, { recursive: true, force: true });
69
+ return archivePath;
70
+ }
71
+
72
+ function createCloudInitScript({
73
+ guestToken,
74
+ guestPayloadPath = '/var/lib/neoagent/guest-payload.tar.gz',
75
+ guestAgentPort = 8421,
76
+ runtimeProfile = 'browser_cli',
77
+ }) {
78
+ const normalizedProfile = normalizeRuntimeProfile(runtimeProfile);
79
+ const includeBrowser = normalizedProfile === 'browser_cli';
80
+ const guestUtilityPackages = includeBrowser
81
+ ? 'curl ca-certificates gnupg git rsync unzip xvfb dbus-x11'
82
+ : 'curl ca-certificates gnupg git rsync unzip dbus-x11 adb';
83
+ const guestTokenB64 = encodeGuestToken(guestToken);
84
+ const envFile = '/etc/neoagent/neoagent.env';
85
+ const appDir = '/opt/neoagent';
86
+ const playwrightBrowsersPath = `${appDir}/.playwright-browsers`;
87
+ const bootstrapMarker = '/var/lib/neoagent/bootstrap-complete';
88
+ const browserReadyMarker = '/var/lib/neoagent/browser-runtime-ready';
89
+ const browserDepsMarker = '/var/lib/neoagent/browser-deps-installed';
90
+ const nodeSourceSetupUrl = 'https://deb.nodesource.com/setup_20.x';
91
+
92
+ return [
93
+ '#!/usr/bin/env bash',
94
+ 'set -uo pipefail', // Removed -e to handle non-critical failures gracefully
95
+ '',
96
+ 'export DEBIAN_FRONTEND=noninteractive',
97
+ `APP_DIR=${JSON.stringify(appDir)}`,
98
+ `PLAYWRIGHT_BROWSERS_PATH=${JSON.stringify(playwrightBrowsersPath)}`,
99
+ `BOOTSTRAP_MARKER=${JSON.stringify(bootstrapMarker)}`,
100
+ `BROWSER_READY_MARKER=${JSON.stringify(browserReadyMarker)}`,
101
+ `BROWSER_DEPS_MARKER=${JSON.stringify(browserDepsMarker)}`,
102
+ `ENV_FILE=${JSON.stringify(envFile)}`,
103
+ `GUEST_PAYLOAD_PATH=${JSON.stringify(guestPayloadPath)}`,
104
+ '',
105
+ 'mkdir -p /etc/neoagent /var/lib/neoagent "$APP_DIR"',
106
+ '',
107
+ '# Redirect logs to a guest-local file and console',
108
+ 'LOG_FILE="/var/log/neoagent-bootstrap.log"',
109
+ 'exec > >(tee -a "$LOG_FILE" >/dev/console) 2>&1',
110
+ 'echo "NeoAgent guest bootstrap starting..."',
111
+ 'rm -f "$BOOTSTRAP_MARKER" "$BROWSER_READY_MARKER"',
112
+ '',
113
+ 'function retry_cmd() {',
114
+ ' local n=1',
115
+ ' local max=3',
116
+ ' local delay=5',
117
+ ' while true; do',
118
+ ' "$@" && break || {',
119
+ ' if [[ $n -lt $max ]]; then',
120
+ ' ((n++))',
121
+ ' echo "Command failed. Attempt $n/$max in ${delay}s..."',
122
+ ' sleep $delay',
123
+ ' else',
124
+ ' echo "The command has failed after $n attempts." >&2',
125
+ ' return 1',
126
+ ' fi',
127
+ ' }',
128
+ ' done',
129
+ '}',
130
+ '',
131
+ 'if [ ! -f "$GUEST_PAYLOAD_PATH" ]; then',
132
+ ' echo "Error: Guest payload archive is missing at $GUEST_PAYLOAD_PATH." >&2',
133
+ ' exit 1',
134
+ 'fi',
135
+ 'echo "Extracting guest runtime payload..."',
136
+ 'rm -rf "$APP_DIR"',
137
+ 'mkdir -p "$APP_DIR"',
138
+ 'tar -xzf "$GUEST_PAYLOAD_PATH" -C "$APP_DIR" || { echo "Error: Failed to extract guest runtime payload." >&2; exit 1; }',
139
+ '',
140
+ 'if ! command -v node >/dev/null 2>&1 || ! node -e "process.exit(Number(process.versions.node.split(\'.\')[0]) >= 20 ? 0 : 1)"; then',
141
+ ' echo "Installing Node.js..."',
142
+ ' curl -fsSL ' + JSON.stringify(nodeSourceSetupUrl) + ' | bash - || true',
143
+ ' retry_cmd apt-get install -y --no-install-recommends nodejs || { echo "Error: Failed to install Node.js" >&2; exit 1; }',
144
+ 'fi',
145
+ '',
146
+ `printf '%s\n' ${JSON.stringify(`NEOAGENT_VM_GUEST_TOKEN_B64=${guestTokenB64}`)} > "$ENV_FILE"`,
147
+ `printf '%s\n' ${JSON.stringify(`NEOAGENT_GUEST_AGENT_PORT=${guestAgentPort}`)} >> "$ENV_FILE"`,
148
+ `printf '%s\n' ${JSON.stringify(`NEOAGENT_GUEST_PROFILE=${normalizedProfile}`)} >> "$ENV_FILE"`,
149
+ 'chmod 0600 "$ENV_FILE"',
150
+ '',
151
+ 'cd "$APP_DIR"',
152
+ 'export PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1',
153
+ 'export PLAYWRIGHT_BROWSERS_PATH="$PLAYWRIGHT_BROWSERS_PATH"',
154
+ 'if [ ! -d node_modules ] || [ ! -f node_modules/.neoagent-bootstrap-stamp ] || [ package.json -nt node_modules/.neoagent-bootstrap-stamp ]; then',
155
+ ' echo "Installing npm dependencies..."',
156
+ ' retry_cmd npm install --omit=dev --ignore-scripts --prefer-offline --no-audit --no-fund || { echo "Error: npm install failed." >&2; exit 1; }',
157
+ ' mkdir -p node_modules',
158
+ ' date > node_modules/.neoagent-bootstrap-stamp',
159
+ 'fi',
160
+ '',
161
+ '',
162
+ 'echo "Ensuring guest runtime utilities..."',
163
+ 'retry_cmd apt-get update || echo "Warning: apt-get update failed, proceeding with cached lists."',
164
+ `retry_cmd apt-get install -y --no-install-recommends ${guestUtilityPackages} || { echo "Error: Failed to install required guest runtime utilities." >&2; exit 1; }`,
165
+ '',
166
+ 'echo "NeoAgent guest runtime payload is ready."',
167
+ '',
168
+ ...(includeBrowser
169
+ ? [
170
+ 'echo "Continuing browser runtime provisioning..."',
171
+ 'PLAYWRIGHT_BROWSERS_PATH="$APP_DIR/.playwright-browsers"',
172
+ 'PLAYWRIGHT_STAMP="$PLAYWRIGHT_BROWSERS_PATH/.chromium-installed"',
173
+ 'mkdir -p "$PLAYWRIGHT_BROWSERS_PATH"',
174
+ 'if [ ! -f "$BROWSER_DEPS_MARKER" ]; then',
175
+ ' echo "Installing Playwright browser dependencies..."',
176
+ ' PLAYWRIGHT_BROWSERS_PATH="$PLAYWRIGHT_BROWSERS_PATH" retry_cmd npx playwright install-deps chromium || { echo "Error: Playwright dependency install failed." >&2; exit 1; }',
177
+ ' touch "$BROWSER_DEPS_MARKER"',
178
+ 'fi',
179
+ 'if [ ! -f "$PLAYWRIGHT_STAMP" ] || [ package.json -nt "$PLAYWRIGHT_STAMP" ]; then',
180
+ ' echo "Installing Playwright browsers..."',
181
+ ' PLAYWRIGHT_BROWSERS_PATH="$PLAYWRIGHT_BROWSERS_PATH" retry_cmd npx playwright install chromium || { echo "Error: Playwright browser install failed." >&2; exit 1; }',
182
+ ' date > "$PLAYWRIGHT_STAMP"',
183
+ 'fi',
184
+ 'touch "$BROWSER_READY_MARKER"',
185
+ ]
186
+ : [
187
+ 'rm -f "$BROWSER_READY_MARKER"',
188
+ ]),
189
+ 'touch "$BOOTSTRAP_MARKER"',
190
+ 'echo "NeoAgent guest bootstrap completed."',
191
+ '',
192
+ ].join('\n');
193
+ }
194
+
195
+ function createCloudInitUserData({
196
+ guestToken,
197
+ guestPayloadBase64 = '',
198
+ guestAgentPort = 8421,
199
+ runtimeMode = 'template',
200
+ runtimeProfile = 'browser_cli',
201
+ }) {
202
+ const normalizedProfile = normalizeRuntimeProfile(runtimeProfile);
203
+ const includeBrowser = normalizedProfile === 'browser_cli';
204
+ const guestAgentInnerCommand = includeBrowser
205
+ ? 'set -a; . /etc/neoagent/neoagent.env; set +a; cd /opt/neoagent && env DISPLAY=:99 PLAYWRIGHT_BROWSERS_PATH=/opt/neoagent/.playwright-browsers /usr/bin/env node server/guest_agent.js 2>&1 | tee -a /var/log/neoagent-guest-agent.log >/dev/console'
206
+ : 'set -a; . /etc/neoagent/neoagent.env; set +a; cd /opt/neoagent && /usr/bin/env node server/guest_agent.js 2>&1 | tee -a /var/log/neoagent-guest-agent.log >/dev/console';
207
+ const guestAgentLaunchCommand = `nohup /bin/sh -lc ${JSON.stringify(guestAgentInnerCommand)} </dev/null >/dev/null 2>&1 &`;
208
+ const guestTokenB64 = encodeGuestToken(guestToken);
209
+ const bootstrapScript = createCloudInitScript({
210
+ guestToken,
211
+ guestPayloadPath: '/var/lib/neoagent/guest-payload.tar.gz',
212
+ guestAgentPort,
213
+ runtimeProfile: normalizedProfile,
214
+ });
215
+
216
+ if (runtimeMode === 'user') {
217
+ return [
218
+ '#cloud-config',
219
+ 'package_update: false',
220
+ 'write_files:',
221
+ ' - path: /etc/neoagent/neoagent.env',
222
+ " permissions: '0600'",
223
+ ' owner: root:root',
224
+ ' content: |',
225
+ ` NEOAGENT_VM_GUEST_TOKEN_B64=${guestTokenB64}`,
226
+ ` NEOAGENT_GUEST_AGENT_PORT=${guestAgentPort}`,
227
+ ` NEOAGENT_GUEST_PROFILE=${normalizedProfile}`,
228
+ ...(includeBrowser
229
+ ? [
230
+ ' - path: /etc/systemd/system/neoagent-xvfb.service',
231
+ " permissions: '0644'",
232
+ ' owner: root:root',
233
+ ' content: |',
234
+ ' [Unit]',
235
+ ' Description=NeoAgent virtual display',
236
+ ' After=network-online.target',
237
+ ' Wants=network-online.target',
238
+ '',
239
+ ' [Service]',
240
+ ' Type=simple',
241
+ ' ExecStart=/usr/bin/Xvfb :99 -screen 0 1440x900x24 -ac -nolisten tcp',
242
+ ' Restart=always',
243
+ ' RestartSec=2',
244
+ ' StandardOutput=journal+console',
245
+ ' StandardError=journal+console',
246
+ '',
247
+ ' [Install]',
248
+ ' WantedBy=multi-user.target',
249
+ ]
250
+ : []),
251
+ ' - path: /etc/systemd/system/neoagent-guest-agent.service',
252
+ " permissions: '0644'",
253
+ ' owner: root:root',
254
+ ' content: |',
255
+ ' [Unit]',
256
+ ' Description=NeoAgent guest agent',
257
+ ' After=network-online.target',
258
+ ...(includeBrowser ? [' After=neoagent-xvfb.service'] : []),
259
+ ' ConditionPathExists=/etc/neoagent/neoagent.env',
260
+ ' Wants=network-online.target',
261
+ '',
262
+ ' [Service]',
263
+ ' Type=simple',
264
+ ' EnvironmentFile=/etc/neoagent/neoagent.env',
265
+ ' ExecStartPre=/bin/mkdir -p /var/lib/neoagent',
266
+ ...(includeBrowser
267
+ ? [
268
+ ' ExecStartPre=/usr/bin/touch /var/lib/neoagent/browser-runtime-ready',
269
+ ' ExecStartPre=/bin/sh -lc \'for _ in $(seq 1 30); do [ -S /tmp/.X11-unix/X99 ] && exit 0; sleep 1; done; exit 1\'',
270
+ ' Environment=DISPLAY=:99',
271
+ ' Environment=PLAYWRIGHT_BROWSERS_PATH=/opt/neoagent/.playwright-browsers',
272
+ ]
273
+ : [
274
+ ' ExecStartPre=/bin/sh -lc \'rm -f /var/lib/neoagent/browser-runtime-ready || true\'',
275
+ ]),
276
+ ' ExecStartPre=/usr/bin/touch /var/lib/neoagent/bootstrap-complete',
277
+ ' WorkingDirectory=/opt/neoagent',
278
+ ' ExecStart=/usr/bin/env node /opt/neoagent/server/guest_agent.js',
279
+ ' Restart=always',
280
+ ' RestartSec=5',
281
+ ' StandardOutput=journal+console',
282
+ ' StandardError=journal+console',
283
+ '',
284
+ ' [Install]',
285
+ ' WantedBy=multi-user.target',
286
+ 'runcmd:',
287
+ ' - [bash, -lc, "systemctl daemon-reload"]',
288
+ ...(includeBrowser
289
+ ? [
290
+ ' - [bash, -lc, "systemctl enable neoagent-xvfb.service"]',
291
+ ' - [bash, -lc, "systemctl start neoagent-xvfb.service"]',
292
+ ]
293
+ : []),
294
+ ' - [bash, -lc, "systemctl enable neoagent-guest-agent.service"]',
295
+ ' - [bash, -lc, "systemctl start --no-block neoagent-guest-agent.service"]',
296
+ '',
297
+ ].join('\n');
298
+ }
299
+
300
+ return [
301
+ '#cloud-config',
302
+ 'package_update: false',
303
+ 'write_files:',
304
+ ' - path: /etc/neoagent/neoagent.env',
305
+ " permissions: '0600'",
306
+ ' owner: root:root',
307
+ ' content: |',
308
+ ` NEOAGENT_VM_GUEST_TOKEN_B64=${guestTokenB64}`,
309
+ ` NEOAGENT_GUEST_AGENT_PORT=${guestAgentPort}`,
310
+ ` NEOAGENT_GUEST_PROFILE=${normalizedProfile}`,
311
+ ' - path: /var/lib/neoagent/guest-payload.tar.gz',
312
+ " permissions: '0644'",
313
+ ' owner: root:root',
314
+ " encoding: 'b64'",
315
+ ' content: |',
316
+ ` ${guestPayloadBase64}`,
317
+ ' - path: /usr/local/bin/neoagent-guest-bootstrap.sh',
318
+ " permissions: '0755'",
319
+ ' owner: root:root',
320
+ ' content: |',
321
+ ...bootstrapScript.split('\n').map((line) => ` ${line}`),
322
+ ' - path: /etc/systemd/system/neoagent-guest-agent.service',
323
+ " permissions: '0644'",
324
+ ' owner: root:root',
325
+ ' content: |',
326
+ ' [Unit]',
327
+ ' Description=NeoAgent guest agent',
328
+ ' After=network-online.target',
329
+ ' After=cloud-final.service',
330
+ ' After=neoagent-guest-bootstrap.service',
331
+ ...(includeBrowser ? [' After=neoagent-xvfb.service'] : []),
332
+ ' ConditionPathExists=/etc/neoagent/neoagent.env',
333
+ ...(includeBrowser ? [' Requires=neoagent-xvfb.service'] : []),
334
+ ' Wants=network-online.target',
335
+ '',
336
+ ' [Service]',
337
+ ' Type=simple',
338
+ ' EnvironmentFile=/etc/neoagent/neoagent.env',
339
+ ...(includeBrowser
340
+ ? [
341
+ ' Environment=DISPLAY=:99',
342
+ ' Environment=PLAYWRIGHT_BROWSERS_PATH=/opt/neoagent/.playwright-browsers',
343
+ ]
344
+ : []),
345
+ ' WorkingDirectory=/opt/neoagent',
346
+ ' ExecStart=/usr/bin/env node /opt/neoagent/server/guest_agent.js',
347
+ ' Restart=always',
348
+ ' RestartSec=5',
349
+ ' StandardOutput=journal+console',
350
+ ' StandardError=journal+console',
351
+ '',
352
+ ' [Install]',
353
+ ' WantedBy=multi-user.target',
354
+ ...(includeBrowser
355
+ ? [
356
+ ' - path: /etc/systemd/system/neoagent-xvfb.service',
357
+ " permissions: '0644'",
358
+ ' owner: root:root',
359
+ ' content: |',
360
+ ' [Unit]',
361
+ ' Description=NeoAgent virtual display',
362
+ ' After=network-online.target',
363
+ ' Wants=network-online.target',
364
+ '',
365
+ ' [Service]',
366
+ ' Type=simple',
367
+ ' ExecStart=/usr/bin/Xvfb :99 -screen 0 1440x900x24 -ac -nolisten tcp',
368
+ ' Restart=always',
369
+ ' RestartSec=2',
370
+ ' StandardOutput=journal+console',
371
+ ' StandardError=journal+console',
372
+ '',
373
+ ' [Install]',
374
+ ' WantedBy=multi-user.target',
375
+ ]
376
+ : []),
377
+ ' - path: /etc/systemd/system/neoagent-guest-bootstrap.service',
378
+ " permissions: '0644'",
379
+ ' owner: root:root',
380
+ ' content: |',
381
+ ' [Unit]',
382
+ ' Description=NeoAgent guest bootstrap',
383
+ ' After=network-online.target',
384
+ ' Wants=network-online.target',
385
+ '',
386
+ ' [Service]',
387
+ ' Type=oneshot',
388
+ ' ExecStart=/usr/local/bin/neoagent-guest-bootstrap.sh',
389
+ ' RemainAfterExit=yes',
390
+ '',
391
+ ' [Install]',
392
+ ' WantedBy=multi-user.target',
393
+ 'runcmd:',
394
+ ' - [bash, -lc, "systemctl daemon-reload"]',
395
+ ...(includeBrowser
396
+ ? [
397
+ ' - [bash, -lc, "systemctl enable neoagent-xvfb.service"]',
398
+ ' - [bash, -lc, "systemctl start neoagent-xvfb.service"]',
399
+ ]
400
+ : []),
401
+ ' - [bash, -lc, "/usr/local/bin/neoagent-guest-bootstrap.sh"]',
402
+ ` - [bash, -lc, ${JSON.stringify(guestAgentLaunchCommand)}]`,
403
+ '',
404
+ ].join('\n');
405
+ }
406
+
407
+ function createCloudInitMetaData({ instanceId, localHostName }) {
408
+ return [
409
+ `instance-id: ${instanceId}`,
410
+ `local-hostname: ${localHostName}`,
411
+ '',
412
+ ].join('\n');
413
+ }
414
+
415
+ function resolveCloudInitIdentity(userRoot) {
416
+ const relativePath = path.relative(VM_ROOT, path.resolve(userRoot || ''));
417
+ const normalized = relativePath
418
+ .split(path.sep)
419
+ .filter(Boolean)
420
+ .map((segment) => segment.replace(/[^a-z0-9_-]+/gi, '-').replace(/^-+|-+$/g, '').slice(0, 32))
421
+ .filter(Boolean)
422
+ .join('-');
423
+ const scope = normalized || 'default';
424
+ return {
425
+ instanceId: `neoagent-${scope}`,
426
+ localHostName: `neoagent-${scope}`,
427
+ };
428
+ }
429
+
430
+ function commandExists(command) {
431
+ const probe = spawnSync(
432
+ process.platform === 'win32' ? 'where' : 'bash',
433
+ process.platform === 'win32' ? [command] : ['-lc', `command -v "${command}"`],
434
+ { stdio: 'ignore' },
435
+ );
436
+ return probe.status === 0;
437
+ }
438
+
439
+ function parseDiskutilMountPoint(output) {
440
+ const match = String(output || '').match(/Mount Point:\s+(.+)/);
441
+ return match ? match[1].trim() : null;
442
+ }
443
+
444
+ function copySeedFilesToVolume(volumePath, sourceDir) {
445
+ for (const entry of ['user-data', 'meta-data', 'startup.nsh']) {
446
+ const sourcePath = path.join(sourceDir, entry);
447
+ const targetPath = path.join(volumePath, entry);
448
+ fs.copyFileSync(sourcePath, targetPath);
449
+ }
450
+ }
451
+
452
+ function createFatSeedImage(sourceDir, imagePath) {
453
+ if (process.platform === 'win32') {
454
+ throw new Error('Creating a FAT seed image is not supported on Windows yet.');
455
+ }
456
+
457
+ const hdiutilAvailable = commandExists('hdiutil');
458
+ const newfsMsdosPath = commandExists('/sbin/newfs_msdos') ? '/sbin/newfs_msdos' : (commandExists('newfs_msdos') ? 'newfs_msdos' : null);
459
+ const diskutilAvailable = commandExists('diskutil');
460
+ if (!hdiutilAvailable || !newfsMsdosPath || !diskutilAvailable) {
461
+ throw new Error('Required disk image tools are not available.');
462
+ }
463
+
464
+ fs.mkdirSync(path.dirname(imagePath), { recursive: true });
465
+ fs.rmSync(imagePath, { force: true });
466
+ fs.writeFileSync(imagePath, Buffer.alloc(32 * 1024 * 1024));
467
+
468
+ let device = null;
469
+ try {
470
+ const attachResult = spawnSync('hdiutil', ['attach', '-nomount', imagePath], {
471
+ encoding: 'utf8',
472
+ stdio: ['ignore', 'pipe', 'pipe'],
473
+ });
474
+ if (attachResult.status !== 0) {
475
+ throw new Error(
476
+ String(attachResult.stderr || attachResult.stdout || attachResult.error?.message || 'Failed to attach FAT seed image.')
477
+ .trim(),
478
+ );
479
+ }
480
+
481
+ device = String(attachResult.stdout || '').trim().split('\n').find(Boolean)?.split(/\s+/)[0] || null;
482
+ if (!device) {
483
+ throw new Error('Failed to resolve the temporary FAT seed device.');
484
+ }
485
+ const rawDevice = device.replace('/dev/disk', '/dev/rdisk');
486
+
487
+ const formatResult = spawnSync(newfsMsdosPath, ['-v', 'CIDATA', rawDevice], {
488
+ encoding: 'utf8',
489
+ stdio: ['ignore', 'pipe', 'pipe'],
490
+ });
491
+ if (formatResult.status !== 0) {
492
+ throw new Error(
493
+ String(formatResult.stderr || formatResult.stdout || formatResult.error?.message || 'Failed to format FAT seed image.')
494
+ .trim(),
495
+ );
496
+ }
497
+
498
+ const mountResult = spawnSync('diskutil', ['mount', device], {
499
+ encoding: 'utf8',
500
+ stdio: ['ignore', 'pipe', 'pipe'],
501
+ });
502
+ if (mountResult.status !== 0) {
503
+ throw new Error(
504
+ String(mountResult.stderr || mountResult.stdout || mountResult.error?.message || 'Failed to mount FAT seed image.')
505
+ .trim(),
506
+ );
507
+ }
508
+
509
+ const mountPoint = parseDiskutilMountPoint(mountResult.stdout)
510
+ || parseDiskutilMountPoint(spawnSync('diskutil', ['info', device], {
511
+ encoding: 'utf8',
512
+ stdio: ['ignore', 'pipe', 'pipe'],
513
+ }).stdout)
514
+ || `/Volumes/CIDATA`;
515
+
516
+ if (!fs.existsSync(mountPoint)) {
517
+ throw new Error(`Mounted FAT seed volume is missing at ${mountPoint}.`);
518
+ }
519
+
520
+ copySeedFilesToVolume(mountPoint, sourceDir);
521
+ return imagePath;
522
+ } finally {
523
+ if (device) {
524
+ try {
525
+ spawnSync('diskutil', ['unmount', 'force', device], { stdio: 'ignore' });
526
+ } catch {}
527
+ try {
528
+ spawnSync('hdiutil', ['detach', device], { stdio: 'ignore' });
529
+ } catch {}
530
+ }
531
+ }
532
+ }
533
+
534
+ function createSeedIso(sourceDir, isoPath) {
535
+ const candidates = [
536
+ {
537
+ command: 'xorriso',
538
+ args: ['-as', 'mkisofs', '-output', isoPath, '-volid', 'CIDATA', '-joliet', '-rock', sourceDir],
539
+ },
540
+ {
541
+ command: 'cloud-localds',
542
+ args: [isoPath, path.join(sourceDir, 'user-data'), path.join(sourceDir, 'meta-data')],
543
+ },
544
+ {
545
+ command: 'hdiutil',
546
+ args: ['makehybrid', '-ov', '-o', isoPath, '-iso', '-joliet', '-iso-volume-name', 'CIDATA', '-joliet-volume-name', 'CIDATA', sourceDir],
547
+ },
548
+ {
549
+ command: 'mkisofs',
550
+ args: ['-o', isoPath, '-V', 'CIDATA', '-J', '-r', sourceDir],
551
+ },
552
+ ];
553
+
554
+ let lastError = null;
555
+ for (const candidate of candidates) {
556
+ if (!commandExists(candidate.command)) {
557
+ continue;
558
+ }
559
+
560
+ const result = spawnSync(candidate.command, candidate.args, {
561
+ encoding: 'utf8',
562
+ stdio: ['ignore', 'pipe', 'pipe'],
563
+ });
564
+ if (result.status === 0 && fs.existsSync(isoPath)) {
565
+ return isoPath;
566
+ }
567
+ lastError = new Error(
568
+ String(result.stderr || result.stdout || result.error?.message || `exit status ${result.status ?? 'unknown'}`).trim() || `Failed to create seed ISO with ${candidate.command}.`
569
+ );
570
+ }
571
+
572
+ throw new Error(`Unable to create cloud-init seed ISO: ${lastError ? lastError.message : 'no supported ISO writer was found.'}`);
573
+ }
574
+
575
+ function ensureGuestBootstrapSeed({
576
+ userRoot,
577
+ guestToken,
578
+ guestAgentPort = 8421,
579
+ guestArch = 'x64',
580
+ runtimeMode = 'template',
581
+ runtimeProfile = 'browser_cli',
582
+ }) {
583
+ const seedRoot = path.join(userRoot, 'cloud-init');
584
+ const seedDir = path.join(seedRoot, 'seed');
585
+ const seedImagePath = path.join(seedRoot, 'cidata.img');
586
+ const isoPath = path.join(seedRoot, 'cidata.iso');
587
+ fs.mkdirSync(seedDir, { recursive: true });
588
+
589
+ const userDataPath = path.join(seedDir, 'user-data');
590
+ const metaDataPath = path.join(seedDir, 'meta-data');
591
+ const startupNshPath = path.join(seedDir, 'startup.nsh');
592
+ const guestPayloadBase64 = runtimeMode === 'user'
593
+ ? ''
594
+ : fs.readFileSync(createGuestPayloadArchive(seedDir, runtimeProfile)).toString('base64');
595
+ const userData = createCloudInitUserData({
596
+ guestToken,
597
+ guestPayloadBase64,
598
+ guestAgentPort,
599
+ runtimeMode,
600
+ runtimeProfile,
601
+ });
602
+ const identity = resolveCloudInitIdentity(userRoot);
603
+ const metaData = createCloudInitMetaData(identity);
604
+ const startupNsh = guestArch === 'arm64'
605
+ ? [
606
+ '@echo -off',
607
+ 'map -r',
608
+ 'for %d in fs0 fs1 fs2 fs3 fs4 fs5',
609
+ ' if exist %d:\\EFI\\ubuntu\\shimaa64.efi then',
610
+ ' %d:\\EFI\\ubuntu\\shimaa64.efi',
611
+ ' endif',
612
+ 'endfor',
613
+ '# Fallback',
614
+ '\\EFI\\ubuntu\\shimaa64.efi',
615
+ '\\EFI\\ubuntu\\grubaa64.efi',
616
+ ].join('\r\n')
617
+ : [
618
+ '@echo -off',
619
+ 'map -r',
620
+ 'fs0:',
621
+ '\\EFI\\ubuntu\\shimx64.efi',
622
+ '\\EFI\\ubuntu\\grubx64.efi',
623
+ '\\EFI\\BOOT\\BOOTX64.EFI',
624
+ ].join('\r\n');
625
+
626
+ fs.writeFileSync(userDataPath, userData);
627
+ fs.writeFileSync(metaDataPath, metaData);
628
+ fs.writeFileSync(startupNshPath, startupNsh);
629
+ let createdSeedPath = null;
630
+ try {
631
+ createdSeedPath = createFatSeedImage(seedDir, seedImagePath);
632
+ } catch (error) {
633
+ createdSeedPath = createSeedIso(seedDir, isoPath);
634
+ }
635
+
636
+ return {
637
+ seedRoot,
638
+ seedDir,
639
+ seedImagePath: createdSeedPath,
640
+ isoPath: createdSeedPath === isoPath ? isoPath : null,
641
+ userDataPath,
642
+ metaDataPath,
643
+ startupNshPath,
644
+ };
645
+ }
646
+
647
+ module.exports = {
648
+ createCloudInitMetaData,
649
+ createCloudInitUserData,
650
+ createCloudInitScript,
651
+ createSeedIso,
652
+ ensureGuestBootstrapSeed,
653
+ GUEST_BOOTSTRAP_ROOT,
654
+ };