neoagent 2.4.0 → 2.4.1-beta.11

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 (57) hide show
  1. package/LICENSE +619 -21
  2. package/README.md +1 -1
  3. package/extensions/chrome-browser/background.mjs +19 -7
  4. package/extensions/chrome-browser/icons/icon128.png +0 -0
  5. package/extensions/chrome-browser/icons/icon16.png +0 -0
  6. package/extensions/chrome-browser/icons/icon48.png +0 -0
  7. package/extensions/chrome-browser/icons/logo.svg +12 -0
  8. package/extensions/chrome-browser/manifest.json +13 -2
  9. package/extensions/chrome-browser/popup.css +5 -0
  10. package/extensions/chrome-browser/popup.html +7 -5
  11. package/extensions/chrome-browser/popup.js +16 -7
  12. package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +721 -0
  13. package/flutter_app/lib/features/onboarding/onboarding_shell.dart +6 -0
  14. package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +1 -1
  15. package/flutter_app/lib/main.dart +1 -0
  16. package/flutter_app/lib/main_controller.dart +156 -3
  17. package/flutter_app/lib/main_devices.dart +485 -119
  18. package/flutter_app/lib/main_settings.dart +289 -30
  19. package/flutter_app/lib/src/backend_client.dart +89 -0
  20. package/flutter_app/lib/src/desktop_companion_actions.dart +153 -3
  21. package/flutter_app/lib/src/desktop_companion_io.dart +145 -4
  22. package/flutter_app/lib/src/desktop_native_bridge.dart +13 -0
  23. package/flutter_app/lib/src/stream_renderer.dart +286 -0
  24. package/flutter_app/macos/Runner/AppDelegate.swift +56 -1
  25. package/package.json +2 -2
  26. package/server/guest_agent.js +19 -1
  27. package/server/http/routes.js +191 -0
  28. package/server/http/socket.js +1 -1
  29. package/server/index.js +4 -1
  30. package/server/public/.last_build_id +1 -1
  31. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  32. package/server/public/flutter_bootstrap.js +1 -1
  33. package/server/public/main.dart.js +75438 -74005
  34. package/server/routes/browser.js +14 -0
  35. package/server/routes/browser_extension.js +21 -4
  36. package/server/routes/desktop.js +10 -0
  37. package/server/routes/settings.js +4 -0
  38. package/server/routes/stream.js +187 -0
  39. package/server/services/ai/tools.js +40 -29
  40. package/server/services/android/controller.js +41 -2
  41. package/server/services/browser/controller.js +34 -0
  42. package/server/services/browser/extension/manifest.js +33 -0
  43. package/server/services/browser/extension/provider.js +12 -6
  44. package/server/services/browser/extension/registry.js +188 -18
  45. package/server/services/desktop/gateway.js +28 -3
  46. package/server/services/desktop/protocol.js +34 -0
  47. package/server/services/desktop/provider.js +25 -0
  48. package/server/services/desktop/registry.js +92 -10
  49. package/server/services/manager.js +19 -2
  50. package/server/services/runtime/backends/local-vm.js +6 -0
  51. package/server/services/runtime/docker-vm-manager.js +26 -3
  52. package/server/services/runtime/manager.js +36 -5
  53. package/server/services/runtime/settings.js +17 -0
  54. package/server/services/streaming/android-stream.js +298 -0
  55. package/server/services/streaming/browser-stream.js +87 -0
  56. package/server/services/streaming/stream-hub.js +231 -0
  57. package/server/services/websocket.js +73 -0
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
2
2
  import '../../main.dart';
3
3
  import 'onboarding_video_step.dart';
4
4
  import 'onboarding_welcome_step.dart';
5
+ import 'onboarding_companion_step.dart';
5
6
  import 'onboarding_messaging_step.dart';
6
7
  import 'onboarding_model_step.dart';
7
8
 
@@ -45,6 +46,10 @@ class _OnboardingShellState extends State<OnboardingShell> {
45
46
  children: <Widget>[
46
47
  OnboardingVideoStep(onComplete: _nextStep),
47
48
  OnboardingWelcomeStep(onNext: _nextStep),
49
+ OnboardingCompanionStep(
50
+ onNext: _nextStep,
51
+ controller: widget.controller,
52
+ ),
48
53
  OnboardingMessagingStep(
49
54
  onNext: _nextStep,
50
55
  controller: widget.controller,
@@ -55,3 +60,4 @@ class _OnboardingShellState extends State<OnboardingShell> {
55
60
  );
56
61
  }
57
62
  }
63
+
@@ -11,7 +11,7 @@ class OnboardingWelcomeStep extends StatelessWidget {
11
11
  @override
12
12
  Widget build(BuildContext context) {
13
13
  return OnboardingScaffold(
14
- step: 1,
14
+ step: 0,
15
15
  totalSteps: 4,
16
16
  eyebrow: 'WELCOME',
17
17
  title: 'Welcome to\nNeoOS',
@@ -39,6 +39,7 @@ import 'src/messaging_access_summary.dart';
39
39
  import 'src/oauth_launcher.dart';
40
40
  import 'src/recording_bridge.dart';
41
41
  import 'src/recording_payloads.dart';
42
+ import 'src/stream_renderer.dart';
42
43
  import 'src/theme/palette.dart';
43
44
  import 'src/web_app_update_monitor.dart';
44
45
  import 'src/widget_bridge.dart';
@@ -105,6 +105,8 @@ class NeoAgentController extends ChangeNotifier {
105
105
  bool _analyticsConsentResolved = false;
106
106
  bool _analyticsConsentGranted = false;
107
107
 
108
+ io.Socket? get streamSocket => socketConnected ? _socket : null;
109
+
108
110
  bool hasUser = true;
109
111
  bool registrationOpen = false;
110
112
  bool serviceEmailConfigured = false;
@@ -168,6 +170,8 @@ class NeoAgentController extends ChangeNotifier {
168
170
  List<McpServerItem> mcpServers = const <McpServerItem>[];
169
171
  Map<String, dynamic> browserRuntime = const <String, dynamic>{};
170
172
  Map<String, dynamic> browserExtensionStatus = const <String, dynamic>{};
173
+ List<Map<String, dynamic>> browserExtensionTokens =
174
+ const <Map<String, dynamic>>[];
171
175
  Map<String, dynamic> androidRuntime = const <String, dynamic>{};
172
176
  Map<String, dynamic> desktopRuntime = const <String, dynamic>{};
173
177
  List<String> androidInstalledApps = const <String>[];
@@ -176,6 +180,7 @@ class NeoAgentController extends ChangeNotifier {
176
180
  List<Map<String, dynamic>> desktopDisplays = const <Map<String, dynamic>>[];
177
181
  Map<String, dynamic> desktopPermissions = const <String, dynamic>{};
178
182
  String? selectedDesktopDeviceId;
183
+ String? selectedBrowserExtensionTokenId;
179
184
  String? browserScreenshotPath;
180
185
  String? androidScreenshotPath;
181
186
  String? desktopScreenshotPath;
@@ -893,8 +898,9 @@ class NeoAgentController extends ChangeNotifier {
893
898
  // On web, require explicit opt-in (GDPR). On native, consent is implicit
894
899
  // unless the user has previously declined.
895
900
  _analyticsConsentResolved = consentState != null || !kIsWeb;
896
- _analyticsConsentGranted =
897
- kIsWeb ? consentState == true : consentState != false;
901
+ _analyticsConsentGranted = kIsWeb
902
+ ? consentState == true
903
+ : consentState != false;
898
904
  await _analytics.initialize(
899
905
  token: token,
900
906
  consentGranted: _analyticsConsentGranted,
@@ -1040,6 +1046,15 @@ class NeoAgentController extends ChangeNotifier {
1040
1046
  }
1041
1047
  }
1042
1048
 
1049
+ Future<Map<String, dynamic>> testCliRuntime() =>
1050
+ _backendClient.testCli(backendUrl);
1051
+
1052
+ Future<Map<String, dynamic>> testBrowserExtension() =>
1053
+ _backendClient.testExtension(backendUrl);
1054
+
1055
+ Future<Map<String, dynamic>> testDesktopCompanion() =>
1056
+ _backendClient.testDesktop(backendUrl);
1057
+
1043
1058
  Future<void> openAppUpdate() async {
1044
1059
  final release = availableAppUpdate;
1045
1060
  if (release == null || isOpeningAppUpdate) {
@@ -1644,6 +1659,7 @@ class NeoAgentController extends ChangeNotifier {
1644
1659
  mcpServers = const <McpServerItem>[];
1645
1660
  browserRuntime = const <String, dynamic>{};
1646
1661
  browserExtensionStatus = const <String, dynamic>{};
1662
+ browserExtensionTokens = const <Map<String, dynamic>>[];
1647
1663
  androidRuntime = const <String, dynamic>{};
1648
1664
  desktopRuntime = const <String, dynamic>{};
1649
1665
  androidInstalledApps = const <String>[];
@@ -1652,6 +1668,7 @@ class NeoAgentController extends ChangeNotifier {
1652
1668
  desktopDisplays = const <Map<String, dynamic>>[];
1653
1669
  desktopPermissions = const <String, dynamic>{};
1654
1670
  selectedDesktopDeviceId = null;
1671
+ selectedBrowserExtensionTokenId = null;
1655
1672
  browserScreenshotPath = null;
1656
1673
  androidScreenshotPath = null;
1657
1674
  desktopScreenshotPath = null;
@@ -2333,6 +2350,13 @@ class NeoAgentController extends ChangeNotifier {
2333
2350
  browserExtensionStatus = Map<String, dynamic>.from(
2334
2351
  browserExtensionResponse,
2335
2352
  );
2353
+ browserExtensionTokens = _jsonMapList(
2354
+ browserExtensionStatus['tokens'],
2355
+ fallbackToMapValues: true,
2356
+ );
2357
+ selectedBrowserExtensionTokenId = _optionalIdFrom(
2358
+ browserExtensionStatus['selectedTokenId'],
2359
+ );
2336
2360
  androidRuntime = Map<String, dynamic>.from(androidResponse);
2337
2361
  desktopRuntime = Map<String, dynamic>.from(desktopResponse);
2338
2362
  selectedDesktopDeviceId =
@@ -2428,6 +2452,11 @@ class NeoAgentController extends ChangeNotifier {
2428
2452
  return parsed;
2429
2453
  }
2430
2454
 
2455
+ String? _optionalIdFrom(dynamic value) {
2456
+ final normalized = value?.toString().trim() ?? '';
2457
+ return normalized.isEmpty || normalized == 'null' ? null : normalized;
2458
+ }
2459
+
2431
2460
  Future<void> refreshRunsOnly() async {
2432
2461
  try {
2433
2462
  final runsResponse = await _backendClient.fetchRuns(
@@ -2731,6 +2760,13 @@ class NeoAgentController extends ChangeNotifier {
2731
2760
  browserExtensionStatus = Map<String, dynamic>.from(
2732
2761
  browserExtensionResponse,
2733
2762
  );
2763
+ browserExtensionTokens = _jsonMapList(
2764
+ browserExtensionStatus['tokens'],
2765
+ fallbackToMapValues: true,
2766
+ );
2767
+ selectedBrowserExtensionTokenId = _optionalIdFrom(
2768
+ browserExtensionStatus['selectedTokenId'],
2769
+ );
2734
2770
  androidRuntime = Map<String, dynamic>.from(androidResponse);
2735
2771
  desktopRuntime = Map<String, dynamic>.from(desktopResponse);
2736
2772
  selectedDesktopDeviceId =
@@ -2758,6 +2794,13 @@ class NeoAgentController extends ChangeNotifier {
2758
2794
  backendUrl,
2759
2795
  );
2760
2796
  browserExtensionStatus = Map<String, dynamic>.from(response);
2797
+ browserExtensionTokens = _jsonMapList(
2798
+ browserExtensionStatus['tokens'],
2799
+ fallbackToMapValues: true,
2800
+ );
2801
+ selectedBrowserExtensionTokenId = _optionalIdFrom(
2802
+ browserExtensionStatus['selectedTokenId'],
2803
+ );
2761
2804
  notifyListeners();
2762
2805
  } catch (error) {
2763
2806
  errorMessage = _friendlyErrorMessage(error);
@@ -2902,6 +2945,15 @@ class NeoAgentController extends ChangeNotifier {
2902
2945
  );
2903
2946
  }
2904
2947
 
2948
+ Future<void> hoverBrowserPointRuntime({
2949
+ required int x,
2950
+ required int y,
2951
+ }) async {
2952
+ try {
2953
+ await _backendClient.hoverBrowserPoint(backendUrl, x: x, y: y);
2954
+ } catch (_) {}
2955
+ }
2956
+
2905
2957
  Future<void> fillBrowserRuntime({
2906
2958
  required String selector,
2907
2959
  required String value,
@@ -3027,6 +3079,40 @@ class NeoAgentController extends ChangeNotifier {
3027
3079
  } catch (_) {}
3028
3080
  }
3029
3081
 
3082
+ Future<void> startStreamRuntime({
3083
+ required String platform,
3084
+ required String deviceId,
3085
+ int fps = 10,
3086
+ int quality = 70,
3087
+ }) async {
3088
+ final normalizedDeviceId = deviceId.trim();
3089
+ if (normalizedDeviceId.isEmpty) {
3090
+ return;
3091
+ }
3092
+ await _backendClient.startStream(
3093
+ backendUrl,
3094
+ platform: platform,
3095
+ deviceId: normalizedDeviceId,
3096
+ fps: fps,
3097
+ quality: quality,
3098
+ );
3099
+ }
3100
+
3101
+ Future<void> stopStreamRuntime({
3102
+ required String platform,
3103
+ required String deviceId,
3104
+ }) async {
3105
+ final normalizedDeviceId = deviceId.trim();
3106
+ if (normalizedDeviceId.isEmpty) {
3107
+ return;
3108
+ }
3109
+ await _backendClient.stopStream(
3110
+ backendUrl,
3111
+ platform: platform,
3112
+ deviceId: normalizedDeviceId,
3113
+ );
3114
+ }
3115
+
3030
3116
  Future<void> dumpAndroidUiRuntime() async {
3031
3117
  await _runDeviceAction(
3032
3118
  () => _backendClient.dumpAndroidUi(backendUrl),
@@ -3194,6 +3280,44 @@ class NeoAgentController extends ChangeNotifier {
3194
3280
  await refreshDesktopFrameRuntime();
3195
3281
  }
3196
3282
 
3283
+ Future<void> selectBrowserExtensionRuntime(String tokenId) async {
3284
+ isRunningDeviceAction = true;
3285
+ errorMessage = null;
3286
+ notifyListeners();
3287
+ try {
3288
+ final response = await _backendClient.selectBrowserExtensionToken(
3289
+ backendUrl,
3290
+ tokenId: tokenId,
3291
+ );
3292
+ final status = response['status'] is Map
3293
+ ? Map<String, dynamic>.from(response['status'] as Map)
3294
+ : await _backendClient.fetchBrowserExtensionStatus(backendUrl);
3295
+ browserExtensionStatus = Map<String, dynamic>.from(status);
3296
+ browserExtensionTokens = _jsonMapList(
3297
+ browserExtensionStatus['tokens'],
3298
+ fallbackToMapValues: true,
3299
+ );
3300
+ selectedBrowserExtensionTokenId = _optionalIdFrom(
3301
+ browserExtensionStatus['selectedTokenId'],
3302
+ );
3303
+ if (selectedBrowserExtensionTokenId != null) {
3304
+ settings = <String, dynamic>{
3305
+ ...settings,
3306
+ 'browser_extension_token_id': selectedBrowserExtensionTokenId,
3307
+ 'selected_browser_extension_token_id':
3308
+ selectedBrowserExtensionTokenId,
3309
+ };
3310
+ }
3311
+ browserScreenshotPath = null;
3312
+ await refreshBrowserFrameRuntime();
3313
+ } catch (error) {
3314
+ errorMessage = _friendlyErrorMessage(error);
3315
+ } finally {
3316
+ isRunningDeviceAction = false;
3317
+ notifyListeners();
3318
+ }
3319
+ }
3320
+
3197
3321
  Future<void> openDesktopSelectionRuntime() async {
3198
3322
  await refreshDevices();
3199
3323
  errorMessage =
@@ -3272,6 +3396,17 @@ class NeoAgentController extends ChangeNotifier {
3272
3396
  );
3273
3397
  }
3274
3398
 
3399
+ Future<void> hoverDesktopRuntime({required int x, required int y}) async {
3400
+ try {
3401
+ await _backendClient.hoverDesktop(
3402
+ backendUrl,
3403
+ deviceId: selectedDesktopDeviceId,
3404
+ x: x,
3405
+ y: y,
3406
+ );
3407
+ } catch (_) {}
3408
+ }
3409
+
3275
3410
  Future<void> dragDesktopRuntime({
3276
3411
  required int x1,
3277
3412
  required int y1,
@@ -4564,7 +4699,9 @@ class NeoAgentController extends ChangeNotifier {
4564
4699
 
4565
4700
  Future<void> saveSettings({
4566
4701
  required String browserBackend,
4702
+ String? browserExtensionTokenId,
4567
4703
  required String cliBackend,
4704
+ String? cliDesktopDeviceId,
4568
4705
  required bool smarterSelector,
4569
4706
  required List<String> enabledModels,
4570
4707
  required String defaultChatModel,
@@ -4593,7 +4730,11 @@ class NeoAgentController extends ChangeNotifier {
4593
4730
  final payload = <String, dynamic>{
4594
4731
  'headless_browser': true,
4595
4732
  'browser_backend': browserBackend,
4733
+ if (browserExtensionTokenId != null)
4734
+ 'browser_extension_token_id': browserExtensionTokenId,
4596
4735
  'cli_backend': cliBackend,
4736
+ if (cliDesktopDeviceId != null)
4737
+ 'cli_desktop_device_id': cliDesktopDeviceId,
4597
4738
  'smarter_model_selector': smarterSelector,
4598
4739
  'enabled_models': enabledModels,
4599
4740
  'default_chat_model': defaultChatModel,
@@ -6131,6 +6272,11 @@ class NeoAgentController extends ChangeNotifier {
6131
6272
  String get cliBackend =>
6132
6273
  settings['cli_backend']?.toString().trim().toLowerCase() ?? 'vm';
6133
6274
 
6275
+ String? get cliDesktopDeviceId {
6276
+ final v = settings['cli_desktop_device_id']?.toString().trim();
6277
+ return (v == null || v.isEmpty) ? null : v;
6278
+ }
6279
+
6134
6280
  String get cloudBrowserBackend {
6135
6281
  final browser = browserBackend;
6136
6282
  final profile = settings['runtime_profile']
@@ -6154,6 +6300,11 @@ class NeoAgentController extends ChangeNotifier {
6154
6300
  bool get browserExtensionConnected =>
6155
6301
  browserExtensionStatus['connected'] == true;
6156
6302
 
6303
+ String? get browserExtensionTokenId {
6304
+ final v = settings['browser_extension_token_id']?.toString().trim();
6305
+ return (v == null || v.isEmpty || v == 'null') ? null : v;
6306
+ }
6307
+
6157
6308
  bool get smarterSelector => settings['smarter_model_selector'] != false;
6158
6309
 
6159
6310
  Map<String, AiProviderConfig> get aiProviderConfigs {
@@ -6336,7 +6487,9 @@ class NeoAgentController extends ChangeNotifier {
6336
6487
 
6337
6488
  List<ChatEntry> get visibleChatMessages {
6338
6489
  final entries = <ChatEntry>[...chatMessages];
6339
- if (isSendingMessage && activeRun != null && streamingAssistant.trim().isEmpty) {
6490
+ if (isSendingMessage &&
6491
+ activeRun != null &&
6492
+ streamingAssistant.trim().isEmpty) {
6340
6493
  entries.add(
6341
6494
  ChatEntry(
6342
6495
  id: '',