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.
- package/LICENSE +619 -21
- package/README.md +1 -1
- package/extensions/chrome-browser/background.mjs +19 -7
- package/extensions/chrome-browser/icons/icon128.png +0 -0
- package/extensions/chrome-browser/icons/icon16.png +0 -0
- package/extensions/chrome-browser/icons/icon48.png +0 -0
- package/extensions/chrome-browser/icons/logo.svg +12 -0
- package/extensions/chrome-browser/manifest.json +13 -2
- package/extensions/chrome-browser/popup.css +5 -0
- package/extensions/chrome-browser/popup.html +7 -5
- package/extensions/chrome-browser/popup.js +16 -7
- package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +721 -0
- package/flutter_app/lib/features/onboarding/onboarding_shell.dart +6 -0
- package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +1 -1
- package/flutter_app/lib/main.dart +1 -0
- package/flutter_app/lib/main_controller.dart +156 -3
- package/flutter_app/lib/main_devices.dart +485 -119
- package/flutter_app/lib/main_settings.dart +289 -30
- package/flutter_app/lib/src/backend_client.dart +89 -0
- package/flutter_app/lib/src/desktop_companion_actions.dart +153 -3
- package/flutter_app/lib/src/desktop_companion_io.dart +145 -4
- package/flutter_app/lib/src/desktop_native_bridge.dart +13 -0
- package/flutter_app/lib/src/stream_renderer.dart +286 -0
- package/flutter_app/macos/Runner/AppDelegate.swift +56 -1
- package/package.json +2 -2
- package/server/guest_agent.js +19 -1
- package/server/http/routes.js +191 -0
- package/server/http/socket.js +1 -1
- package/server/index.js +4 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +75438 -74005
- package/server/routes/browser.js +14 -0
- package/server/routes/browser_extension.js +21 -4
- package/server/routes/desktop.js +10 -0
- package/server/routes/settings.js +4 -0
- package/server/routes/stream.js +187 -0
- package/server/services/ai/tools.js +40 -29
- package/server/services/android/controller.js +41 -2
- package/server/services/browser/controller.js +34 -0
- package/server/services/browser/extension/manifest.js +33 -0
- package/server/services/browser/extension/provider.js +12 -6
- package/server/services/browser/extension/registry.js +188 -18
- package/server/services/desktop/gateway.js +28 -3
- package/server/services/desktop/protocol.js +34 -0
- package/server/services/desktop/provider.js +25 -0
- package/server/services/desktop/registry.js +92 -10
- package/server/services/manager.js +19 -2
- package/server/services/runtime/backends/local-vm.js +6 -0
- package/server/services/runtime/docker-vm-manager.js +26 -3
- package/server/services/runtime/manager.js +36 -5
- package/server/services/runtime/settings.js +17 -0
- package/server/services/streaming/android-stream.js +298 -0
- package/server/services/streaming/browser-stream.js +87 -0
- package/server/services/streaming/stream-hub.js +231 -0
- package/server/services/websocket.js +73 -0
|
@@ -170,7 +170,9 @@ const List<_SettingsSection> _settingsSearchSections = <_SettingsSection>[
|
|
|
170
170
|
class _SettingsPanelState extends State<SettingsPanel> {
|
|
171
171
|
late final TextEditingController _searchController;
|
|
172
172
|
late String _browserBackend;
|
|
173
|
+
String? _browserExtensionTokenId;
|
|
173
174
|
late String _cliBackend;
|
|
175
|
+
String? _cliDesktopDeviceId;
|
|
174
176
|
late bool _smarterSelector;
|
|
175
177
|
late Set<String> _enabledModels;
|
|
176
178
|
late String _defaultChatModel;
|
|
@@ -187,6 +189,14 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
187
189
|
<String, TextEditingController>{};
|
|
188
190
|
final Set<String> _expandedProviderIds = <String>{};
|
|
189
191
|
|
|
192
|
+
// Inline runtime test state — ephemeral, not stored in controller.
|
|
193
|
+
bool _cliTestRunning = false;
|
|
194
|
+
Map<String, dynamic>? _cliTestResult;
|
|
195
|
+
bool _extensionTestRunning = false;
|
|
196
|
+
Map<String, dynamic>? _extensionTestResult;
|
|
197
|
+
bool _desktopTestRunning = false;
|
|
198
|
+
Map<String, dynamic>? _desktopTestResult;
|
|
199
|
+
|
|
190
200
|
@override
|
|
191
201
|
void initState() {
|
|
192
202
|
super.initState();
|
|
@@ -224,7 +234,11 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
224
234
|
.map((model) => model.id)
|
|
225
235
|
.toSet();
|
|
226
236
|
_browserBackend = _normalizeBrowserBackend(controller.browserBackend);
|
|
237
|
+
_browserExtensionTokenId =
|
|
238
|
+
controller.browserExtensionTokenId ??
|
|
239
|
+
controller.selectedBrowserExtensionTokenId;
|
|
227
240
|
_cliBackend = _normalizeCliBackend(controller.cliBackend);
|
|
241
|
+
_cliDesktopDeviceId = controller.cliDesktopDeviceId;
|
|
228
242
|
_smarterSelector = controller.smarterSelector;
|
|
229
243
|
_enabledModels = controller.enabledModelIds
|
|
230
244
|
.where((id) => knownModels.contains(id))
|
|
@@ -466,7 +480,11 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
466
480
|
browserBackend: _browserBackend == 'extension'
|
|
467
481
|
? 'extension'
|
|
468
482
|
: 'vm',
|
|
483
|
+
browserExtensionTokenId: _browserBackend == 'extension'
|
|
484
|
+
? _browserExtensionTokenId
|
|
485
|
+
: null,
|
|
469
486
|
cliBackend: _cliBackend == 'desktop' ? 'desktop' : 'vm',
|
|
487
|
+
cliDesktopDeviceId: _cliDesktopDeviceId,
|
|
470
488
|
smarterSelector: _smarterSelector,
|
|
471
489
|
enabledModels: _enabledModels.toList(),
|
|
472
490
|
defaultChatModel: _defaultChatModel,
|
|
@@ -631,36 +649,107 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
631
649
|
],
|
|
632
650
|
onChanged: (value) {
|
|
633
651
|
if (value != null) {
|
|
634
|
-
setState(()
|
|
652
|
+
setState(() {
|
|
653
|
+
_browserBackend = value;
|
|
654
|
+
_browserExtensionTokenId ??=
|
|
655
|
+
controller.selectedBrowserExtensionTokenId;
|
|
656
|
+
});
|
|
635
657
|
}
|
|
636
658
|
},
|
|
637
659
|
),
|
|
638
660
|
const SizedBox(height: 10),
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
+
if (_browserBackend == 'extension') ...<Widget>[
|
|
662
|
+
if (controller.browserExtensionTokens.isNotEmpty) ...<Widget>[
|
|
663
|
+
DropdownButtonFormField<String>(
|
|
664
|
+
initialValue: controller.browserExtensionTokens.any(
|
|
665
|
+
(token) => token['tokenId']?.toString() == _browserExtensionTokenId,
|
|
666
|
+
)
|
|
667
|
+
? _browserExtensionTokenId
|
|
668
|
+
: null,
|
|
669
|
+
decoration: const InputDecoration(
|
|
670
|
+
labelText: 'Default extension',
|
|
671
|
+
helperText: 'Choose which paired Chrome extension controls browser actions.',
|
|
672
|
+
),
|
|
673
|
+
items: controller.browserExtensionTokens.map((token) {
|
|
674
|
+
final tokenId = token['tokenId']?.toString() ?? '';
|
|
675
|
+
final label = token['name']?.toString().trim().isNotEmpty == true
|
|
676
|
+
? token['name'].toString()
|
|
677
|
+
: tokenId;
|
|
678
|
+
final online = token['online'] == true || token['connected'] == true;
|
|
679
|
+
return DropdownMenuItem<String>(
|
|
680
|
+
value: tokenId,
|
|
681
|
+
child: Row(
|
|
682
|
+
children: <Widget>[
|
|
683
|
+
Icon(
|
|
684
|
+
online ? Icons.circle : Icons.circle_outlined,
|
|
685
|
+
size: 10,
|
|
686
|
+
color: online ? Colors.green : Colors.grey,
|
|
687
|
+
),
|
|
688
|
+
const SizedBox(width: 8),
|
|
689
|
+
Flexible(
|
|
690
|
+
child: Text(
|
|
691
|
+
label,
|
|
692
|
+
maxLines: 1,
|
|
693
|
+
overflow: TextOverflow.ellipsis,
|
|
694
|
+
),
|
|
695
|
+
),
|
|
696
|
+
],
|
|
697
|
+
),
|
|
698
|
+
);
|
|
699
|
+
}).toList(),
|
|
700
|
+
onChanged: (value) {
|
|
701
|
+
if (value != null) {
|
|
702
|
+
setState(() => _browserExtensionTokenId = value);
|
|
703
|
+
}
|
|
704
|
+
},
|
|
661
705
|
),
|
|
706
|
+
const SizedBox(height: 10),
|
|
662
707
|
],
|
|
663
|
-
|
|
708
|
+
_buildInlineTestRow(
|
|
709
|
+
label: 'Chrome extension',
|
|
710
|
+
running: _extensionTestRunning,
|
|
711
|
+
result: _extensionTestResult,
|
|
712
|
+
note: controller.browserExtensionConnected
|
|
713
|
+
? 'Connected — tap Test to verify the live link.'
|
|
714
|
+
: 'Not connected — download the extension, load it in Chrome, then pair after login.',
|
|
715
|
+
onTest: () async {
|
|
716
|
+
setState(() { _extensionTestRunning = true; _extensionTestResult = null; });
|
|
717
|
+
try {
|
|
718
|
+
final r = await controller.testBrowserExtension();
|
|
719
|
+
if (mounted) {
|
|
720
|
+
setState(() => _extensionTestResult = r);
|
|
721
|
+
}
|
|
722
|
+
} catch (e) {
|
|
723
|
+
if (mounted) {
|
|
724
|
+
setState(() => _extensionTestResult = <String, dynamic>{'passed': false, 'detail': e.toString()});
|
|
725
|
+
}
|
|
726
|
+
} finally {
|
|
727
|
+
if (mounted) {
|
|
728
|
+
setState(() => _extensionTestRunning = false);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
),
|
|
733
|
+
const SizedBox(height: 10),
|
|
734
|
+
Wrap(
|
|
735
|
+
spacing: 10,
|
|
736
|
+
runSpacing: 10,
|
|
737
|
+
children: <Widget>[
|
|
738
|
+
OutlinedButton.icon(
|
|
739
|
+
onPressed: controller.downloadBrowserExtension,
|
|
740
|
+
icon: Icon(Icons.download_outlined),
|
|
741
|
+
label: Text('Download extension'),
|
|
742
|
+
),
|
|
743
|
+
OutlinedButton.icon(
|
|
744
|
+
onPressed: controller.refreshBrowserExtensionStatus,
|
|
745
|
+
icon: Icon(Icons.sync),
|
|
746
|
+
label: Text('Refresh'),
|
|
747
|
+
),
|
|
748
|
+
],
|
|
749
|
+
),
|
|
750
|
+
] else ...<Widget>[
|
|
751
|
+
Text('Cloud browser runtime is active.', style: TextStyle(color: _textSecondary, height: 1.4)),
|
|
752
|
+
],
|
|
664
753
|
const Divider(height: 32),
|
|
665
754
|
Text(
|
|
666
755
|
'CLI Runtime',
|
|
@@ -691,14 +780,73 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
691
780
|
}
|
|
692
781
|
},
|
|
693
782
|
),
|
|
783
|
+
if (_cliBackend == 'desktop' && controller.desktopDevices.length > 1) ...<Widget>[
|
|
784
|
+
const SizedBox(height: 12),
|
|
785
|
+
DropdownButtonFormField<String>(
|
|
786
|
+
initialValue: controller.desktopDevices.any(
|
|
787
|
+
(d) => d['deviceId']?.toString() == _cliDesktopDeviceId,
|
|
788
|
+
)
|
|
789
|
+
? _cliDesktopDeviceId
|
|
790
|
+
: null,
|
|
791
|
+
decoration: const InputDecoration(
|
|
792
|
+
labelText: 'Desktop device',
|
|
793
|
+
helperText: 'Choose which desktop companion runs CLI commands.',
|
|
794
|
+
),
|
|
795
|
+
items: controller.desktopDevices.map((device) {
|
|
796
|
+
final deviceId = device['deviceId']?.toString() ?? '';
|
|
797
|
+
final label = device['hostname']?.toString().isNotEmpty == true
|
|
798
|
+
? device['hostname']!.toString()
|
|
799
|
+
: deviceId;
|
|
800
|
+
final online = device['online'] == true;
|
|
801
|
+
return DropdownMenuItem<String>(
|
|
802
|
+
value: deviceId,
|
|
803
|
+
child: Row(
|
|
804
|
+
children: <Widget>[
|
|
805
|
+
Icon(
|
|
806
|
+
online ? Icons.circle : Icons.circle_outlined,
|
|
807
|
+
size: 10,
|
|
808
|
+
color: online ? Colors.green : Colors.grey,
|
|
809
|
+
),
|
|
810
|
+
const SizedBox(width: 8),
|
|
811
|
+
Text(label),
|
|
812
|
+
],
|
|
813
|
+
),
|
|
814
|
+
);
|
|
815
|
+
}).toList(),
|
|
816
|
+
onChanged: (value) {
|
|
817
|
+
if (value != null) {
|
|
818
|
+
setState(() => _cliDesktopDeviceId = value);
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
),
|
|
822
|
+
],
|
|
694
823
|
const SizedBox(height: 10),
|
|
695
|
-
|
|
696
|
-
|
|
824
|
+
_buildInlineTestRow(
|
|
825
|
+
label: 'CLI',
|
|
826
|
+
running: _cliTestRunning,
|
|
827
|
+
result: _cliTestResult,
|
|
828
|
+
note: _cliBackend == 'desktop'
|
|
697
829
|
? (controller.desktopCompanionConnected
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
: 'Cloud
|
|
701
|
-
|
|
830
|
+
? 'Desktop app connected — commands route locally through the companion.'
|
|
831
|
+
: 'Desktop app selected but not connected. Commands fall back to cloud VM until the companion is online.')
|
|
832
|
+
: 'Cloud VM — commands run in an isolated container.',
|
|
833
|
+
onTest: () async {
|
|
834
|
+
setState(() { _cliTestRunning = true; _cliTestResult = null; });
|
|
835
|
+
try {
|
|
836
|
+
final r = await controller.testCliRuntime();
|
|
837
|
+
if (mounted) {
|
|
838
|
+
setState(() => _cliTestResult = r);
|
|
839
|
+
}
|
|
840
|
+
} catch (e) {
|
|
841
|
+
if (mounted) {
|
|
842
|
+
setState(() => _cliTestResult = <String, dynamic>{'passed': false, 'detail': e.toString()});
|
|
843
|
+
}
|
|
844
|
+
} finally {
|
|
845
|
+
if (mounted) {
|
|
846
|
+
setState(() => _cliTestRunning = false);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
},
|
|
702
850
|
),
|
|
703
851
|
const Divider(height: 32),
|
|
704
852
|
Text(
|
|
@@ -1344,6 +1492,53 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
1344
1492
|
const SizedBox(height: 12),
|
|
1345
1493
|
_InlineError(message: message),
|
|
1346
1494
|
],
|
|
1495
|
+
const SizedBox(height: 12),
|
|
1496
|
+
_buildInlineTestRow(
|
|
1497
|
+
label: 'Desktop companion',
|
|
1498
|
+
running: _desktopTestRunning,
|
|
1499
|
+
result: _desktopTestResult != null
|
|
1500
|
+
? <String, dynamic>{
|
|
1501
|
+
'passed': _desktopTestResult!['passed'] == true,
|
|
1502
|
+
'detail': _desktopTestResult!['detail']?.toString() ?? '',
|
|
1503
|
+
}
|
|
1504
|
+
: null,
|
|
1505
|
+
note: controller.desktopCompanionConnected
|
|
1506
|
+
? 'Connected — tap Test to fetch live device status from the server.'
|
|
1507
|
+
: 'Not connected. Make sure the desktop app is running on the target machine.',
|
|
1508
|
+
onTest: () async {
|
|
1509
|
+
setState(() { _desktopTestRunning = true; _desktopTestResult = null; });
|
|
1510
|
+
try {
|
|
1511
|
+
final r = await controller.testDesktopCompanion();
|
|
1512
|
+
final active = r['activeDevice'];
|
|
1513
|
+
final multi = r['multipleOnline'] == true;
|
|
1514
|
+
String detail = r['detail']?.toString() ?? '';
|
|
1515
|
+
if (r['passed'] == true && active != null) {
|
|
1516
|
+
final label = active['label']?.toString() ?? 'Device';
|
|
1517
|
+
final plat = active['platform']?.toString() ?? '';
|
|
1518
|
+
final sc = active['permissions']?['screenCapture'] == true;
|
|
1519
|
+
final ic = active['permissions']?['inputControl'] == true;
|
|
1520
|
+
detail = '$label${plat.isNotEmpty ? " ($plat)" : ""}'
|
|
1521
|
+
' — screen: ${sc ? "✓" : "✗"}, input: ${ic ? "✓" : "✗"}';
|
|
1522
|
+
} else if (multi) {
|
|
1523
|
+
detail = '${r['onlineCount']} devices online — select one in Desktop › Devices';
|
|
1524
|
+
}
|
|
1525
|
+
if (mounted) {
|
|
1526
|
+
setState(() => _desktopTestResult = <String, dynamic>{
|
|
1527
|
+
...r,
|
|
1528
|
+
'detail': detail,
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
} catch (e) {
|
|
1532
|
+
if (mounted) {
|
|
1533
|
+
setState(() => _desktopTestResult = <String, dynamic>{'passed': false, 'detail': e.toString()});
|
|
1534
|
+
}
|
|
1535
|
+
} finally {
|
|
1536
|
+
if (mounted) {
|
|
1537
|
+
setState(() => _desktopTestRunning = false);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
),
|
|
1347
1542
|
const SizedBox(height: 14),
|
|
1348
1543
|
Builder(
|
|
1349
1544
|
builder: (context) {
|
|
@@ -1999,6 +2194,70 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
1999
2194
|
)
|
|
2000
2195
|
.toList();
|
|
2001
2196
|
}
|
|
2197
|
+
|
|
2198
|
+
// Shared helper: small "Test" button + inline result row.
|
|
2199
|
+
Widget _buildInlineTestRow({
|
|
2200
|
+
required String label,
|
|
2201
|
+
required bool running,
|
|
2202
|
+
required Map<String, dynamic>? result,
|
|
2203
|
+
required VoidCallback onTest,
|
|
2204
|
+
String? note,
|
|
2205
|
+
}) {
|
|
2206
|
+
final passed = result?['passed'] == true;
|
|
2207
|
+
final detail = result?['detail']?.toString() ?? '';
|
|
2208
|
+
return Row(
|
|
2209
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
2210
|
+
children: <Widget>[
|
|
2211
|
+
Expanded(
|
|
2212
|
+
child: Column(
|
|
2213
|
+
crossAxisAlignment: CrossAxisAlignment.start,
|
|
2214
|
+
children: <Widget>[
|
|
2215
|
+
if (result != null)
|
|
2216
|
+
Row(
|
|
2217
|
+
children: <Widget>[
|
|
2218
|
+
Icon(
|
|
2219
|
+
passed ? Icons.check_circle_rounded : Icons.cancel_rounded,
|
|
2220
|
+
size: 15,
|
|
2221
|
+
color: passed ? const Color(0xFF22C55E) : const Color(0xFFEF4444),
|
|
2222
|
+
),
|
|
2223
|
+
const SizedBox(width: 6),
|
|
2224
|
+
Expanded(
|
|
2225
|
+
child: Text(
|
|
2226
|
+
passed ? (detail.isNotEmpty ? detail : '$label: OK') : detail,
|
|
2227
|
+
style: TextStyle(
|
|
2228
|
+
fontSize: 13,
|
|
2229
|
+
color: passed ? null : const Color(0xFFEF4444),
|
|
2230
|
+
),
|
|
2231
|
+
),
|
|
2232
|
+
),
|
|
2233
|
+
],
|
|
2234
|
+
)
|
|
2235
|
+
else if (note != null)
|
|
2236
|
+
Text(note, style: TextStyle(fontSize: 13, color: _textSecondary, height: 1.4)),
|
|
2237
|
+
],
|
|
2238
|
+
),
|
|
2239
|
+
),
|
|
2240
|
+
const SizedBox(width: 12),
|
|
2241
|
+
SizedBox(
|
|
2242
|
+
width: 80,
|
|
2243
|
+
child: OutlinedButton(
|
|
2244
|
+
onPressed: running ? null : onTest,
|
|
2245
|
+
style: OutlinedButton.styleFrom(
|
|
2246
|
+
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
|
2247
|
+
textStyle: const TextStyle(fontSize: 12),
|
|
2248
|
+
),
|
|
2249
|
+
child: running
|
|
2250
|
+
? const SizedBox(
|
|
2251
|
+
width: 13,
|
|
2252
|
+
height: 13,
|
|
2253
|
+
child: CircularProgressIndicator(strokeWidth: 2),
|
|
2254
|
+
)
|
|
2255
|
+
: const Text('Test'),
|
|
2256
|
+
),
|
|
2257
|
+
),
|
|
2258
|
+
],
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2002
2261
|
}
|
|
2003
2262
|
|
|
2004
2263
|
class _AiProviderCard extends StatelessWidget {
|
|
@@ -476,6 +476,18 @@ class BackendClient {
|
|
|
476
476
|
return getMap(baseUrl, '/api/runtime/config', allowUnauthorized: true);
|
|
477
477
|
}
|
|
478
478
|
|
|
479
|
+
Future<Map<String, dynamic>> testCli(String baseUrl) async {
|
|
480
|
+
return getMap(baseUrl, '/api/system/test/cli');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
Future<Map<String, dynamic>> testExtension(String baseUrl) async {
|
|
484
|
+
return getMap(baseUrl, '/api/system/test/extension');
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
Future<Map<String, dynamic>> testDesktop(String baseUrl) async {
|
|
488
|
+
return getMap(baseUrl, '/api/system/test/desktop');
|
|
489
|
+
}
|
|
490
|
+
|
|
479
491
|
Future<Map<String, dynamic>> fetchBrowserStatus(String baseUrl) async {
|
|
480
492
|
return getMap(baseUrl, '/api/browser/status');
|
|
481
493
|
}
|
|
@@ -486,6 +498,15 @@ class BackendClient {
|
|
|
486
498
|
return getMap(baseUrl, '/api/browser-extension/status');
|
|
487
499
|
}
|
|
488
500
|
|
|
501
|
+
Future<Map<String, dynamic>> selectBrowserExtensionToken(
|
|
502
|
+
String baseUrl, {
|
|
503
|
+
required String tokenId,
|
|
504
|
+
}) async {
|
|
505
|
+
return postMap(baseUrl, '/api/browser-extension/select-token', <String, dynamic>{
|
|
506
|
+
'tokenId': tokenId,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
489
510
|
Future<Map<String, dynamic>> launchBrowser(
|
|
490
511
|
String baseUrl, {
|
|
491
512
|
Map<String, dynamic>? payload,
|
|
@@ -534,6 +555,17 @@ class BackendClient {
|
|
|
534
555
|
});
|
|
535
556
|
}
|
|
536
557
|
|
|
558
|
+
Future<Map<String, dynamic>> hoverBrowserPoint(
|
|
559
|
+
String baseUrl, {
|
|
560
|
+
required int x,
|
|
561
|
+
required int y,
|
|
562
|
+
}) async {
|
|
563
|
+
return postMap(baseUrl, '/api/browser/mouse-move', <String, dynamic>{
|
|
564
|
+
'x': x,
|
|
565
|
+
'y': y,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
537
569
|
Future<Map<String, dynamic>> fillBrowser(
|
|
538
570
|
String baseUrl, {
|
|
539
571
|
required String selector,
|
|
@@ -603,6 +635,50 @@ class BackendClient {
|
|
|
603
635
|
return _postEmpty(baseUrl, '/api/browser/close');
|
|
604
636
|
}
|
|
605
637
|
|
|
638
|
+
Future<Map<String, dynamic>> startStream(
|
|
639
|
+
String baseUrl, {
|
|
640
|
+
required String platform,
|
|
641
|
+
required String deviceId,
|
|
642
|
+
int fps = 15,
|
|
643
|
+
int quality = 80,
|
|
644
|
+
String? displayId,
|
|
645
|
+
}) async {
|
|
646
|
+
return postMap(baseUrl, '/api/stream/start', <String, dynamic>{
|
|
647
|
+
'platform': platform,
|
|
648
|
+
'deviceId': deviceId,
|
|
649
|
+
'fps': fps,
|
|
650
|
+
'quality': quality,
|
|
651
|
+
if (displayId != null && displayId.isNotEmpty) 'displayId': displayId,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
Future<Map<String, dynamic>> stopStream(
|
|
656
|
+
String baseUrl, {
|
|
657
|
+
required String platform,
|
|
658
|
+
required String deviceId,
|
|
659
|
+
}) async {
|
|
660
|
+
return postMap(baseUrl, '/api/stream/stop', <String, dynamic>{
|
|
661
|
+
'platform': platform,
|
|
662
|
+
'deviceId': deviceId,
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
Future<Map<String, dynamic>> fetchStreamStatus(
|
|
667
|
+
String baseUrl, {
|
|
668
|
+
String? platform,
|
|
669
|
+
String? deviceId,
|
|
670
|
+
}) async {
|
|
671
|
+
final query = <String>[];
|
|
672
|
+
if (platform != null && platform.isNotEmpty) {
|
|
673
|
+
query.add('platform=${Uri.encodeQueryComponent(platform)}');
|
|
674
|
+
}
|
|
675
|
+
if (deviceId != null && deviceId.isNotEmpty) {
|
|
676
|
+
query.add('deviceId=${Uri.encodeQueryComponent(deviceId)}');
|
|
677
|
+
}
|
|
678
|
+
final suffix = query.isEmpty ? '' : '?${query.join('&')}';
|
|
679
|
+
return getMap(baseUrl, '/api/stream/status$suffix');
|
|
680
|
+
}
|
|
681
|
+
|
|
606
682
|
Future<Map<String, dynamic>> fetchAndroidStatus(String baseUrl) async {
|
|
607
683
|
return getMap(baseUrl, '/api/android/status');
|
|
608
684
|
}
|
|
@@ -663,6 +739,19 @@ class BackendClient {
|
|
|
663
739
|
});
|
|
664
740
|
}
|
|
665
741
|
|
|
742
|
+
Future<Map<String, dynamic>> hoverDesktop(
|
|
743
|
+
String baseUrl, {
|
|
744
|
+
String? deviceId,
|
|
745
|
+
required int x,
|
|
746
|
+
required int y,
|
|
747
|
+
}) async {
|
|
748
|
+
return postMap(baseUrl, '/api/desktop/mouse-move', <String, dynamic>{
|
|
749
|
+
if (deviceId != null && deviceId.isNotEmpty) 'deviceId': deviceId,
|
|
750
|
+
'x': x,
|
|
751
|
+
'y': y,
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
666
755
|
Future<Map<String, dynamic>> dragDesktop(
|
|
667
756
|
String baseUrl, {
|
|
668
757
|
String? deviceId,
|