neoagent 2.3.1-beta.89 → 2.3.1-beta.91

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 (31) hide show
  1. package/.env.example +4 -0
  2. package/README.md +16 -7
  3. package/flutter_app/lib/features/location/location_service.dart +2 -4
  4. package/flutter_app/lib/main.dart +1 -0
  5. package/flutter_app/lib/main_app_shell.dart +17 -15
  6. package/flutter_app/lib/main_chat.dart +46 -42
  7. package/flutter_app/lib/main_controller.dart +6 -1
  8. package/flutter_app/lib/main_devices.dart +86 -742
  9. package/flutter_app/lib/main_integrations.dart +3 -3
  10. package/flutter_app/lib/main_settings.dart +50 -0
  11. package/flutter_app/lib/main_spacing.dart +18 -0
  12. package/flutter_app/lib/main_theme.dart +9 -0
  13. package/flutter_app/lib/main_unified.dart +3 -3
  14. package/lib/manager.js +33 -0
  15. package/package.json +1 -1
  16. package/server/db/database.js +74 -16
  17. package/server/guest_agent.js +1 -0
  18. package/server/public/.last_build_id +1 -1
  19. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  20. package/server/public/flutter_bootstrap.js +1 -1
  21. package/server/public/main.dart.js +50396 -50271
  22. package/server/services/ai/capabilityHealth.js +2 -3
  23. package/server/services/android/android_bootstrap_worker.js +18 -3
  24. package/server/services/android/controller.js +460 -2753
  25. package/server/services/runtime/backends/local-vm.js +33 -145
  26. package/server/services/runtime/docker-vm-manager.js +392 -0
  27. package/server/services/runtime/manager.js +53 -38
  28. package/server/services/runtime/settings.js +12 -10
  29. package/server/services/runtime/validation.js +4 -1
  30. package/server/utils/deployment.js +8 -2
  31. package/server/services/runtime/qemu.js +0 -1118
@@ -1,31 +1,51 @@
1
+ 'use strict';
2
+
1
3
  const { LocalVmExecutionBackend } = require('./backends/local-vm');
2
- const { QemuVmManager } = require('./qemu');
4
+ const { DockerVMManager } = require('./docker-vm-manager');
3
5
  const { getRuntimeSettings } = require('./settings');
4
6
  const { ExtensionBrowserProvider } = require('../browser/extension/provider');
5
7
  const { AndroidController } = require('../android/controller');
8
+ const { DesktopProvider } = require('../desktop/provider');
9
+
10
+ // Resource defaults for Docker VMs (overridable via env).
11
+ const DEFAULT_VM_MEMORY_MB = Number(process.env.NEOAGENT_VM_MEMORY_MB ?? 2048);
12
+ const DEFAULT_VM_CPUS = Number(process.env.NEOAGENT_VM_CPUS ?? 2);
6
13
 
7
14
  class RuntimeManager {
8
15
  constructor(options = {}) {
9
16
  this.browserExtensionRegistry = options.browserExtensionRegistry || null;
10
- const browserVmManager = options.browserVmManager || new QemuVmManager({
17
+ this.desktopCompanionRegistry = options.desktopCompanionRegistry || null;
18
+
19
+ const browserVmManager = options.browserVmManager || new DockerVMManager({
11
20
  runtimeProfile: 'browser_cli',
12
- memoryMb: 2048,
13
- cpus: 2,
14
- warmup: false,
21
+ image: 'mcr.microsoft.com/playwright:v1.44.0-focal',
22
+ memoryMb: DEFAULT_VM_MEMORY_MB,
23
+ cpus: DEFAULT_VM_CPUS,
15
24
  });
16
25
  this.browserBackend = new LocalVmExecutionBackend({
17
26
  runtimeProfile: 'browser_cli',
18
27
  vmManager: browserVmManager,
19
28
  artifactStore: options.artifactStore,
20
29
  });
21
- this.androidControllers = new Map();
22
- this.getExtensionBrowserProvider = options.getExtensionBrowserProvider || ((userId) => new ExtensionBrowserProvider({
23
- registry: options.browserExtensionRegistry,
24
- artifactStore: options.artifactStore,
25
- userId,
26
- }));
30
+
31
+ this.artifactStore = options.artifactStore || null;
32
+
33
+ this.getExtensionBrowserProvider = options.getExtensionBrowserProvider
34
+ || ((userId) => new ExtensionBrowserProvider({
35
+ registry: options.browserExtensionRegistry,
36
+ artifactStore: options.artifactStore,
37
+ userId,
38
+ }));
39
+
40
+ this.getDesktopCliProvider = options.getDesktopCliProvider
41
+ || ((userId) => new DesktopProvider({
42
+ registry: options.desktopCompanionRegistry,
43
+ artifactStore: options.artifactStore,
44
+ userId,
45
+ }));
27
46
  }
28
47
 
48
+
29
49
  getSettings(userId) {
30
50
  return getRuntimeSettings(userId);
31
51
  }
@@ -38,8 +58,17 @@ class RuntimeManager {
38
58
  );
39
59
  }
40
60
 
61
+ hasActiveDesktopCompanion(userId) {
62
+ return Boolean(
63
+ this.desktopCompanionRegistry
64
+ && typeof this.desktopCompanionRegistry.isConnected === 'function'
65
+ && this.desktopCompanionRegistry.isConnected(userId)
66
+ );
67
+ }
68
+
41
69
  resolveBackend(userId, requested) {
42
70
  void userId;
71
+ void requested;
43
72
  return this.browserBackend;
44
73
  }
45
74
 
@@ -49,9 +78,6 @@ class RuntimeManager {
49
78
  }
50
79
 
51
80
  hasVmForUser(userId, capability = 'browser') {
52
- if (capability === 'android') {
53
- return Boolean(this.androidControllers.get(String(userId || '').trim()));
54
- }
55
81
  return Boolean(this.browserBackend?.vmManager?.hasVm?.(userId));
56
82
  }
57
83
 
@@ -71,34 +97,25 @@ class RuntimeManager {
71
97
  return this.browserBackend.getBrowserProviderForUser(userId);
72
98
  }
73
99
 
100
+ async getCliProviderForUser(userId) {
101
+ const settings = this.getSettings(userId);
102
+ if (settings.cli_backend === 'desktop' && this.hasActiveDesktopCompanion(userId)) {
103
+ return this.getDesktopCliProvider(userId);
104
+ }
105
+ return this.browserBackend.getCommandExecutorForUser(userId);
106
+ }
107
+
74
108
  async getAndroidProviderForUser(userId) {
75
- const key = String(userId || '').trim();
76
- if (!key) {
109
+ if (userId == null || String(userId).trim() === '') {
77
110
  throw new Error('Android provider requires a user ID.');
78
111
  }
79
- if (!this.androidControllers.has(key)) {
80
- this.androidControllers.set(key, new AndroidController({
81
- userId: key,
82
- runtimeBackend: 'host',
83
- artifactStore: null,
84
- }));
85
- }
86
- return this.androidControllers.get(key);
112
+ return new AndroidController({
113
+ userId: String(userId).trim(),
114
+ artifactStore: this.artifactStore,
115
+ });
87
116
  }
88
117
 
89
118
  async isGuestAgentReadyForUser(userId, timeoutMs = 1000, capability = 'browser') {
90
- if (capability === 'android') {
91
- const controller = this.androidControllers.get(String(userId || '').trim());
92
- if (!controller || typeof controller.getStatus !== 'function') {
93
- return false;
94
- }
95
- try {
96
- const status = await controller.getStatus();
97
- return Boolean(status?.bootstrapped || status?.serial || status?.starting);
98
- } catch {
99
- return false;
100
- }
101
- }
102
119
  if (typeof this.browserBackend?.isGuestAgentReadyForUser !== 'function') {
103
120
  return false;
104
121
  }
@@ -108,9 +125,7 @@ class RuntimeManager {
108
125
  async shutdown() {
109
126
  await Promise.allSettled([
110
127
  this.browserBackend?.shutdown?.(),
111
- ...[...this.androidControllers.values()].map((controller) => controller?.stopEmulator?.().catch?.(() => {})),
112
128
  ]);
113
- this.androidControllers.clear();
114
129
  }
115
130
  }
116
131
 
@@ -9,6 +9,7 @@ function createDefaultRuntimeSettings() {
9
9
  browser_backend: policy.runtimeDefaults.browser_backend,
10
10
  android_backend: policy.runtimeDefaults.android_backend,
11
11
  mcp_backend: policy.runtimeDefaults.mcp_backend,
12
+ cli_backend: policy.runtimeDefaults.cli_backend ?? 'vm',
12
13
  };
13
14
  }
14
15
 
@@ -24,6 +25,7 @@ const BASE_FALLBACK_SETTINGS = Object.freeze({
24
25
  browser_backend: 'vm',
25
26
  android_backend: 'host',
26
27
  mcp_backend: 'host-remote',
28
+ cli_backend: 'vm',
27
29
  });
28
30
 
29
31
  const RUNTIME_SETTING_KEYS = Object.freeze(Object.keys(DEFAULT_RUNTIME_SETTINGS));
@@ -42,6 +44,7 @@ function deriveDefaultsForProfile(profile) {
42
44
  runtime_backend: 'vm',
43
45
  browser_backend: 'vm',
44
46
  android_backend: 'host',
47
+ cli_backend: 'vm',
45
48
  };
46
49
  }
47
50
  }
@@ -53,21 +56,18 @@ function normalizeRuntimeSettings(raw = {}) {
53
56
  const derived = deriveDefaultsForProfile(profile);
54
57
  const runtimeBackend = normalizeChoice(raw.runtime_backend, ['vm'], derived.runtime_backend);
55
58
  const browserBackend = normalizeChoice(raw.browser_backend, ['vm', 'extension'], derived.browser_backend);
56
- const androidBackend = normalizeChoice(raw.android_backend, ['host', 'vm'], derived.android_backend);
59
+ const cliBackend = normalizeChoice(raw.cli_backend, ['vm', 'desktop'], derived.cli_backend ?? 'vm');
60
+ const androidBackend = 'host';
57
61
  return {
58
62
  runtime_profile: profile === 'trusted-host' ? 'secure-vm' : profile,
59
63
  runtime_backend: runtimeBackend,
60
64
  browser_backend: browserBackend === 'extension' ? 'extension' : 'vm',
61
- android_backend: androidBackend === 'vm' ? 'host' : androidBackend,
65
+ android_backend: androidBackend,
62
66
  mcp_backend: 'host-remote',
67
+ cli_backend: cliBackend === 'desktop' ? 'desktop' : 'vm',
63
68
  };
64
69
  }
65
70
 
66
- function deriveCloudBrowserBackend(raw = {}) {
67
- normalizeRuntimeSettings(raw);
68
- return 'vm';
69
- }
70
-
71
71
  function parseStoredRuntimeValue(key, value) {
72
72
  if (typeof value !== 'string') {
73
73
  return value;
@@ -102,8 +102,11 @@ function validateRuntimeSettings(raw = {}) {
102
102
  if (settings.browser_backend !== 'vm' && settings.browser_backend !== 'extension') {
103
103
  issues.push('This deployment requires the VM browser backend or a paired browser extension backend.');
104
104
  }
105
- if (settings.android_backend !== 'host') {
106
- issues.push('This deployment requires the host Android backend.');
105
+ if (settings.android_backend !== 'host' && settings.android_backend !== 'vm') {
106
+ issues.push('This deployment requires a supported Android backend.');
107
+ }
108
+ if (settings.cli_backend !== 'vm' && settings.cli_backend !== 'desktop') {
109
+ issues.push('This deployment requires a supported CLI backend.');
107
110
  }
108
111
  }
109
112
 
@@ -154,7 +157,6 @@ function getRuntimeSettings(userId) {
154
157
  module.exports = {
155
158
  DEFAULT_RUNTIME_SETTINGS,
156
159
  RUNTIME_SETTING_KEYS,
157
- deriveCloudBrowserBackend,
158
160
  ensureDefaultRuntimeSettings,
159
161
  getRuntimeSettings,
160
162
  normalizeRuntimeSettings,
@@ -25,7 +25,10 @@ function getRuntimeValidation(runtimeManager) {
25
25
  return {
26
26
  ready: issues.length === 0,
27
27
  issues,
28
- vm: vmReadiness,
28
+ vm: {
29
+ browser: vmReadiness,
30
+ android: null,
31
+ },
29
32
  guestTokenConfigured: true,
30
33
  policy,
31
34
  };
@@ -54,22 +54,28 @@ function getDeploymentProfile(env = process.env) {
54
54
  return parseDeploymentProfile(env.NEOAGENT_PROFILE);
55
55
  }
56
56
 
57
+ function getAllowSignup(env = process.env) {
58
+ const raw = String(env.NEOAGENT_ALLOW_SIGNUP ?? '').trim().toLowerCase();
59
+ if (raw === 'false' || raw === '0' || raw === 'no') return false;
60
+ return true;
61
+ }
62
+
57
63
  function getDeploymentPolicy(env = process.env) {
58
64
  const profile = getDeploymentProfile(env);
59
65
  const mode = getDeploymentMode(env);
60
- const isProdProfile = profile === DEPLOYMENT_PROFILE_PROD;
61
66
  return {
62
67
  mode,
63
68
  profile,
64
69
  managed: mode === DEPLOYMENT_MODE_MANAGED,
65
70
  allowSelfUpdate: mode !== DEPLOYMENT_MODE_MANAGED,
66
- registrationOpen: isProdProfile,
71
+ registrationOpen: getAllowSignup(env),
67
72
  runtimeDefaults: {
68
73
  runtime_profile: 'secure-vm',
69
74
  runtime_backend: 'vm',
70
75
  browser_backend: 'vm',
71
76
  android_backend: 'host',
72
77
  mcp_backend: 'host-remote',
78
+ cli_backend: 'vm',
73
79
  },
74
80
  allowHostRuntime: false,
75
81
  };