neoagent 3.2.1-beta.2 → 3.2.1-beta.4
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/extensions/chrome-browser/background.mjs +318 -88
- package/extensions/chrome-browser/http.mjs +136 -0
- package/extensions/chrome-browser/protocol.mjs +511 -89
- package/flutter_app/lib/main_chat.dart +118 -739
- package/flutter_app/lib/main_controller.dart +29 -20
- package/flutter_app/lib/main_models.dart +3 -0
- package/flutter_app/lib/main_operations.dart +334 -321
- package/flutter_app/lib/main_settings.dart +4 -3
- package/flutter_app/lib/main_shared.dart +14 -11
- package/flutter_app/lib/src/desktop_companion_actions.dart +185 -31
- package/flutter_app/lib/src/desktop_companion_io.dart +319 -86
- package/flutter_app/windows/runner/flutter_window.cpp +143 -32
- package/lib/manager.js +106 -89
- package/lib/schema_migrations.js +67 -13
- package/package.json +20 -13
- package/runtime/paths.js +49 -5
- package/server/guest_agent.js +52 -30
- package/server/http/middleware.js +24 -0
- package/server/http/routes.js +4 -6
- 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 +30803 -30805
- package/server/routes/admin.js +1 -1
- package/server/routes/android.js +30 -34
- package/server/routes/browser.js +23 -15
- package/server/routes/desktop.js +18 -1
- package/server/routes/integrations.js +5 -1
- package/server/routes/memory.js +1 -0
- package/server/routes/settings.js +16 -5
- package/server/routes/social_reach.js +12 -3
- package/server/routes/social_video.js +4 -0
- package/server/services/ai/compaction.js +7 -2
- package/server/services/ai/history.js +44 -5
- package/server/services/ai/integrated_tools/http_request.js +8 -0
- package/server/services/ai/loop/agent_engine_core.js +347 -162
- package/server/services/ai/loop/callbacks.js +1 -0
- package/server/services/ai/loop/completion_judge.js +12 -0
- package/server/services/ai/loop/conversation_loop.js +348 -242
- package/server/services/ai/loop/messaging_delivery.js +129 -57
- package/server/services/ai/loop/model_call_guard.js +91 -0
- package/server/services/ai/loop/model_io.js +8 -41
- package/server/services/ai/loop/tool_dispatch.js +19 -8
- package/server/services/ai/loopPolicy.js +24 -19
- package/server/services/ai/model_discovery.js +227 -0
- package/server/services/ai/model_identity.js +71 -0
- package/server/services/ai/models.js +67 -162
- package/server/services/ai/providerRetry.js +17 -59
- package/server/services/ai/provider_selector.js +111 -0
- package/server/services/ai/providers/anthropic.js +2 -2
- package/server/services/ai/providers/claudeCode.js +15 -28
- package/server/services/ai/providers/githubCopilot.js +36 -16
- package/server/services/ai/providers/google.js +23 -5
- package/server/services/ai/providers/grok.js +4 -3
- package/server/services/ai/providers/grokOauth.js +13 -22
- package/server/services/ai/providers/nvidia.js +10 -5
- package/server/services/ai/providers/ollama.js +102 -82
- package/server/services/ai/providers/ollama_stream.js +142 -0
- package/server/services/ai/providers/openai.js +39 -5
- package/server/services/ai/providers/openaiCodex.js +11 -4
- package/server/services/ai/providers/openrouter.js +29 -7
- package/server/services/ai/providers/provider_error.js +36 -0
- package/server/services/ai/settings.js +26 -2
- package/server/services/ai/taskAnalysis.js +5 -1
- package/server/services/ai/terminal_reply.js +45 -0
- package/server/services/ai/toolEvidence.js +58 -29
- package/server/services/ai/tools.js +124 -111
- package/server/services/android/controller.js +770 -237
- package/server/services/android/process.js +140 -0
- package/server/services/android/sdk_download.js +143 -0
- package/server/services/android/uia.js +6 -5
- package/server/services/artifacts/store.js +24 -0
- package/server/services/browser/controller.js +736 -385
- package/server/services/browser/extension/gateway.js +40 -16
- package/server/services/browser/extension/protocol.js +12 -1
- package/server/services/browser/extension/provider.js +59 -47
- package/server/services/browser/extension/registry.js +155 -34
- package/server/services/cli/executor.js +62 -9
- package/server/services/desktop/gateway.js +41 -4
- package/server/services/desktop/protocol.js +3 -0
- package/server/services/desktop/provider.js +39 -42
- package/server/services/desktop/registry.js +137 -52
- package/server/services/integrations/figma/provider.js +78 -12
- package/server/services/integrations/github/common.js +11 -6
- package/server/services/integrations/github/provider.js +52 -53
- package/server/services/integrations/google/provider.js +55 -19
- package/server/services/integrations/home_assistant/network.js +17 -20
- package/server/services/integrations/home_assistant/provider.js +7 -5
- package/server/services/integrations/home_assistant/tools.js +17 -5
- package/server/services/integrations/http.js +51 -0
- package/server/services/integrations/manager.js +158 -53
- package/server/services/integrations/microsoft/provider.js +80 -13
- package/server/services/integrations/neoarchive/provider.js +55 -29
- package/server/services/integrations/neorecall/client.js +17 -10
- package/server/services/integrations/neorecall/provider.js +20 -11
- package/server/services/integrations/notion/provider.js +16 -13
- package/server/services/integrations/oauth_provider.js +115 -51
- package/server/services/integrations/slack/provider.js +98 -9
- package/server/services/integrations/spotify/provider.js +67 -71
- package/server/services/integrations/trello/provider.js +21 -7
- package/server/services/integrations/weather/provider.js +18 -12
- package/server/services/integrations/whatsapp/provider.js +76 -16
- package/server/services/manager.js +87 -1
- package/server/services/memory/embedding_index.js +20 -8
- package/server/services/memory/embeddings.js +151 -90
- package/server/services/memory/ingestion.js +50 -9
- package/server/services/memory/ingestion_documents.js +13 -3
- package/server/services/memory/manager.js +52 -19
- package/server/services/messaging/automation.js +84 -9
- package/server/services/messaging/http_platforms.js +33 -13
- package/server/services/messaging/inbound_queue.js +78 -24
- package/server/services/messaging/inbound_store.js +224 -0
- package/server/services/messaging/manager.js +326 -51
- package/server/services/messaging/typing_keepalive.js +5 -2
- package/server/services/messaging/whatsapp.js +22 -14
- package/server/services/network/http.js +210 -0
- package/server/services/network/safe_request.js +307 -0
- package/server/services/runtime/backends/local-vm.js +214 -66
- package/server/services/runtime/manager.js +17 -12
- package/server/services/social_reach/channels/github.js +10 -4
- package/server/services/social_reach/channels/reddit.js +4 -4
- package/server/services/social_reach/channels/rss.js +2 -2
- package/server/services/social_reach/channels/social_video.js +12 -7
- package/server/services/social_reach/channels/v2ex.js +21 -8
- package/server/services/social_reach/channels/x.js +2 -2
- package/server/services/social_reach/channels/xueqiu.js +5 -5
- package/server/services/social_reach/service.js +9 -6
- package/server/services/social_reach/utils.js +65 -14
- package/server/services/social_video/service.js +160 -50
- package/server/services/tasks/integration_runtime.js +18 -8
- package/server/services/tasks/runtime.js +39 -4
- package/server/services/voice/agentBridge.js +17 -4
- package/server/services/voice/bufferedLiveRelayAdapter.js +5 -0
- package/server/services/voice/liveSession.js +31 -0
- package/server/services/voice/openaiSpeech.js +33 -8
- package/server/services/voice/providers.js +233 -151
- package/server/services/voice/runtimeManager.js +118 -20
- package/server/services/voice/turnRunner.js +6 -0
- package/server/services/wearable/firmware_manifest.js +51 -13
- package/server/services/wearable/service.js +1 -0
- package/server/utils/abort.js +96 -0
- package/server/utils/cloud-security.js +110 -3
- package/server/utils/files.js +31 -0
- package/server/utils/image_payload.js +95 -0
- package/server/utils/retry.js +107 -0
|
@@ -343,10 +343,11 @@ class _SettingsPanelState extends State<SettingsPanel> {
|
|
|
343
343
|
onPopInvokedWithResult: (didPop, result) async {
|
|
344
344
|
if (didPop) return;
|
|
345
345
|
final action = await _showLeaveDialog(context);
|
|
346
|
-
if (
|
|
346
|
+
if (!context.mounted) return;
|
|
347
|
+
if (action == _LeaveAction.save) {
|
|
347
348
|
await _doSave();
|
|
348
|
-
if (mounted) Navigator.of(context).pop();
|
|
349
|
-
} else if (action == _LeaveAction.discard
|
|
349
|
+
if (context.mounted) Navigator.of(context).pop();
|
|
350
|
+
} else if (action == _LeaveAction.discard) {
|
|
350
351
|
_hydrate();
|
|
351
352
|
setState(() => _hasUnsavedChanges = false);
|
|
352
353
|
Navigator.of(context).pop();
|
|
@@ -345,7 +345,6 @@ class _EntranceMotionState extends State<_EntranceMotion> {
|
|
|
345
345
|
|
|
346
346
|
class _GlassSurface extends StatelessWidget {
|
|
347
347
|
const _GlassSurface({
|
|
348
|
-
super.key,
|
|
349
348
|
required this.child,
|
|
350
349
|
this.width,
|
|
351
350
|
this.padding,
|
|
@@ -2850,17 +2849,24 @@ String _ensureModelValue(
|
|
|
2850
2849
|
if (allowAuto && value == 'auto') {
|
|
2851
2850
|
return 'auto';
|
|
2852
2851
|
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
return value;
|
|
2856
|
-
}
|
|
2857
|
-
}
|
|
2852
|
+
final model = _modelForValue(value, models);
|
|
2853
|
+
if (model != null) return model.id;
|
|
2858
2854
|
if (allowAuto) {
|
|
2859
2855
|
return 'auto';
|
|
2860
2856
|
}
|
|
2861
2857
|
return models.isNotEmpty ? models.first.id : value;
|
|
2862
2858
|
}
|
|
2863
2859
|
|
|
2860
|
+
ModelMeta? _modelForValue(String value, List<ModelMeta> models) {
|
|
2861
|
+
for (final model in models) {
|
|
2862
|
+
if (model.id == value) return model;
|
|
2863
|
+
}
|
|
2864
|
+
for (final model in models) {
|
|
2865
|
+
if (model.modelId == value) return model;
|
|
2866
|
+
}
|
|
2867
|
+
return null;
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2864
2870
|
String _firstAvailableModelId(List<ModelMeta> models) {
|
|
2865
2871
|
for (final model in models) {
|
|
2866
2872
|
if (model.available) {
|
|
@@ -2874,11 +2880,8 @@ String _modelLabelForValue(String value, List<ModelMeta> models) {
|
|
|
2874
2880
|
if (value == 'auto' || value.trim().isEmpty) {
|
|
2875
2881
|
return 'Auto';
|
|
2876
2882
|
}
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
return model.label;
|
|
2880
|
-
}
|
|
2881
|
-
}
|
|
2883
|
+
final model = _modelForValue(value, models);
|
|
2884
|
+
if (model != null) return model.label;
|
|
2882
2885
|
return value;
|
|
2883
2886
|
}
|
|
2884
2887
|
|
|
@@ -17,6 +17,59 @@ import 'desktop_screen_capture.dart';
|
|
|
17
17
|
|
|
18
18
|
typedef _JpegArgs = ({Uint8List bytes, int quality});
|
|
19
19
|
|
|
20
|
+
String resolveDesktopDisplaySelection(
|
|
21
|
+
Object? rawDisplays,
|
|
22
|
+
String requested, {
|
|
23
|
+
String? activeDisplayId,
|
|
24
|
+
}) {
|
|
25
|
+
final normalized = requested.trim();
|
|
26
|
+
if (normalized.isEmpty) {
|
|
27
|
+
throw ArgumentError.value(
|
|
28
|
+
requested,
|
|
29
|
+
'displayId',
|
|
30
|
+
'Display ID is required.',
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
final displays = rawDisplays is List
|
|
34
|
+
? rawDisplays
|
|
35
|
+
.whereType<Map>()
|
|
36
|
+
.map(
|
|
37
|
+
(display) =>
|
|
38
|
+
display.map((key, value) => MapEntry(key.toString(), value)),
|
|
39
|
+
)
|
|
40
|
+
.where(
|
|
41
|
+
(display) => display['id']?.toString().trim().isNotEmpty == true,
|
|
42
|
+
)
|
|
43
|
+
.toList(growable: false)
|
|
44
|
+
: const <Map<String, Object?>>[];
|
|
45
|
+
if (displays.isEmpty) {
|
|
46
|
+
throw StateError('No desktop displays are currently available.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (normalized.toLowerCase() == 'primary') {
|
|
50
|
+
for (final display in displays) {
|
|
51
|
+
if (display['primary'] == true) {
|
|
52
|
+
return display['id'].toString().trim();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
final active = activeDisplayId?.trim() ?? '';
|
|
56
|
+
if (active.isNotEmpty &&
|
|
57
|
+
displays.any((display) => display['id']?.toString().trim() == active)) {
|
|
58
|
+
return active;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (final display in displays) {
|
|
63
|
+
final id = display['id']?.toString().trim() ?? '';
|
|
64
|
+
if (id == normalized) return id;
|
|
65
|
+
}
|
|
66
|
+
throw ArgumentError.value(
|
|
67
|
+
requested,
|
|
68
|
+
'displayId',
|
|
69
|
+
'The requested desktop display is not available.',
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
20
73
|
Uint8List _compressJpegInIsolate(_JpegArgs args) {
|
|
21
74
|
final decoded = img.decodeImage(args.bytes);
|
|
22
75
|
if (decoded == null) return args.bytes;
|
|
@@ -47,6 +100,8 @@ class DesktopCompanionActions {
|
|
|
47
100
|
|
|
48
101
|
final DesktopScreenCapture _screenCapture;
|
|
49
102
|
final DesktopNativeBridge _nativeBridge = DesktopNativeBridge();
|
|
103
|
+
final Map<String, Process> _shellProcesses = <String, Process>{};
|
|
104
|
+
final Set<String> _cancelledShellCommandIds = <String>{};
|
|
50
105
|
|
|
51
106
|
bool get isCaptureSupported => _screenCapture.isSupported;
|
|
52
107
|
|
|
@@ -64,6 +119,7 @@ class DesktopCompanionActions {
|
|
|
64
119
|
activeDisplayId: activeDisplayId,
|
|
65
120
|
platformStatus: platformStatus,
|
|
66
121
|
);
|
|
122
|
+
final reportedDisplays = _coerceDisplays(platformStatus['displays']);
|
|
67
123
|
final packageInfo = await PackageInfo.fromPlatform();
|
|
68
124
|
return <String, Object?>{
|
|
69
125
|
'deviceId': deviceId,
|
|
@@ -77,7 +133,7 @@ class DesktopCompanionActions {
|
|
|
77
133
|
'paused': paused,
|
|
78
134
|
'permissions': _permissions(capabilities, platformStatus: platformStatus),
|
|
79
135
|
'capabilities': capabilities,
|
|
80
|
-
'displays': snapshot?.displays ??
|
|
136
|
+
'displays': snapshot?.displays ?? reportedDisplays,
|
|
81
137
|
'activeDisplayId':
|
|
82
138
|
snapshot?.activeDisplayId ??
|
|
83
139
|
platformStatus['activeDisplayId']?.toString() ??
|
|
@@ -144,10 +200,10 @@ class DesktopCompanionActions {
|
|
|
144
200
|
contentType: capture.mimeType,
|
|
145
201
|
width: width,
|
|
146
202
|
height: height,
|
|
147
|
-
activeDisplayId:
|
|
203
|
+
activeDisplayId: 'primary',
|
|
148
204
|
displays: <Map<String, Object?>>[
|
|
149
205
|
<String, Object?>{
|
|
150
|
-
'id':
|
|
206
|
+
'id': 'primary',
|
|
151
207
|
'label': 'Primary Display',
|
|
152
208
|
'width': width,
|
|
153
209
|
'height': height,
|
|
@@ -169,6 +225,7 @@ class DesktopCompanionActions {
|
|
|
169
225
|
activeDisplayId: activeDisplayId,
|
|
170
226
|
platformStatus: platformStatus,
|
|
171
227
|
);
|
|
228
|
+
final reportedDisplays = _coerceDisplays(platformStatus['displays']);
|
|
172
229
|
return <String, Object?>{
|
|
173
230
|
'paused': paused,
|
|
174
231
|
'label': label,
|
|
@@ -177,7 +234,7 @@ class DesktopCompanionActions {
|
|
|
177
234
|
platformStatus['activeDisplayId']?.toString() ??
|
|
178
235
|
activeDisplayId ??
|
|
179
236
|
'primary',
|
|
180
|
-
'displays': snapshot?.displays ??
|
|
237
|
+
'displays': snapshot?.displays ?? reportedDisplays,
|
|
181
238
|
'permissions': _permissions(capabilities, platformStatus: platformStatus),
|
|
182
239
|
'capabilities': capabilities,
|
|
183
240
|
if (platformStatus['frontmostApp'] != null)
|
|
@@ -461,11 +518,17 @@ class DesktopCompanionActions {
|
|
|
461
518
|
}
|
|
462
519
|
|
|
463
520
|
Future<Map<String, Object?>> executeShellCommand({
|
|
521
|
+
required String commandId,
|
|
464
522
|
required String command,
|
|
465
523
|
String? cwd,
|
|
466
524
|
int? timeoutMs,
|
|
467
525
|
String? stdinInput,
|
|
526
|
+
bool requestedPty = false,
|
|
527
|
+
List<String> inputs = const <String>[],
|
|
468
528
|
}) async {
|
|
529
|
+
if (command.trim().isEmpty) {
|
|
530
|
+
throw ArgumentError.value(command, 'command', 'Command is required.');
|
|
531
|
+
}
|
|
469
532
|
final shell = Platform.isWindows
|
|
470
533
|
? 'cmd.exe'
|
|
471
534
|
: (Platform.environment['SHELL'] ?? '/bin/sh');
|
|
@@ -483,9 +546,18 @@ class DesktopCompanionActions {
|
|
|
483
546
|
workingDirectory: workingDir,
|
|
484
547
|
runInShell: false,
|
|
485
548
|
);
|
|
549
|
+
if (commandId.isNotEmpty) {
|
|
550
|
+
_shellProcesses[commandId] = process;
|
|
551
|
+
}
|
|
486
552
|
|
|
487
|
-
if (
|
|
488
|
-
process
|
|
553
|
+
if (_cancelledShellCommandIds.contains(commandId)) {
|
|
554
|
+
await _terminateShellProcess(process);
|
|
555
|
+
} else if ((stdinInput != null && stdinInput.isNotEmpty) ||
|
|
556
|
+
inputs.isNotEmpty) {
|
|
557
|
+
if (stdinInput != null) process.stdin.write(stdinInput);
|
|
558
|
+
for (final input in inputs) {
|
|
559
|
+
process.stdin.write(input);
|
|
560
|
+
}
|
|
489
561
|
await process.stdin.close();
|
|
490
562
|
} else {
|
|
491
563
|
unawaited(process.stdin.close());
|
|
@@ -494,13 +566,41 @@ class DesktopCompanionActions {
|
|
|
494
566
|
const maxChars = 50000;
|
|
495
567
|
final stdoutBuf = StringBuffer();
|
|
496
568
|
final stderrBuf = StringBuffer();
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
569
|
+
var stdoutChars = 0;
|
|
570
|
+
var stderrChars = 0;
|
|
571
|
+
final stdoutDone = Completer<void>();
|
|
572
|
+
final stderrDone = Completer<void>();
|
|
573
|
+
|
|
574
|
+
final stdoutSub = process.stdout
|
|
575
|
+
.transform(utf8.decoder)
|
|
576
|
+
.listen(
|
|
577
|
+
(data) {
|
|
578
|
+
stdoutChars += data.length;
|
|
579
|
+
final remaining = maxChars - stdoutBuf.length;
|
|
580
|
+
if (remaining > 0) {
|
|
581
|
+
stdoutBuf.write(
|
|
582
|
+
data.substring(0, data.length.clamp(0, remaining)),
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
onError: stdoutDone.completeError,
|
|
587
|
+
onDone: stdoutDone.complete,
|
|
588
|
+
);
|
|
589
|
+
final stderrSub = process.stderr
|
|
590
|
+
.transform(utf8.decoder)
|
|
591
|
+
.listen(
|
|
592
|
+
(data) {
|
|
593
|
+
stderrChars += data.length;
|
|
594
|
+
final remaining = maxChars - stderrBuf.length;
|
|
595
|
+
if (remaining > 0) {
|
|
596
|
+
stderrBuf.write(
|
|
597
|
+
data.substring(0, data.length.clamp(0, remaining)),
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
onError: stderrDone.completeError,
|
|
602
|
+
onDone: stderrDone.complete,
|
|
603
|
+
);
|
|
504
604
|
|
|
505
605
|
final effectiveTimeout = Duration(
|
|
506
606
|
milliseconds: (timeoutMs != null && timeoutMs > 0)
|
|
@@ -509,38 +609,81 @@ class DesktopCompanionActions {
|
|
|
509
609
|
);
|
|
510
610
|
|
|
511
611
|
bool timedOut = false;
|
|
612
|
+
bool externallyCancelled = false;
|
|
512
613
|
int? exitCode;
|
|
513
614
|
try {
|
|
514
615
|
exitCode = await process.exitCode.timeout(effectiveTimeout);
|
|
515
616
|
} on TimeoutException {
|
|
516
617
|
timedOut = true;
|
|
517
|
-
process
|
|
518
|
-
|
|
618
|
+
exitCode = await _terminateShellProcess(process);
|
|
619
|
+
} finally {
|
|
620
|
+
externallyCancelled = _cancelledShellCommandIds.contains(commandId);
|
|
621
|
+
if (commandId.isNotEmpty) {
|
|
622
|
+
_shellProcesses.remove(commandId);
|
|
623
|
+
_cancelledShellCommandIds.remove(commandId);
|
|
624
|
+
}
|
|
519
625
|
}
|
|
520
626
|
|
|
521
|
-
|
|
522
|
-
|
|
627
|
+
try {
|
|
628
|
+
await Future.wait<void>(<Future<void>>[
|
|
629
|
+
stdoutDone.future,
|
|
630
|
+
stderrDone.future,
|
|
631
|
+
]).timeout(const Duration(seconds: 2));
|
|
632
|
+
} on TimeoutException {
|
|
633
|
+
await stdoutSub.cancel();
|
|
634
|
+
await stderrSub.cancel();
|
|
635
|
+
}
|
|
523
636
|
|
|
524
|
-
String trimOutput(StringBuffer buf) {
|
|
637
|
+
String trimOutput(StringBuffer buf, int totalChars) {
|
|
525
638
|
final s = buf.toString().trim();
|
|
526
|
-
return
|
|
527
|
-
? '$
|
|
639
|
+
return totalChars > maxChars
|
|
640
|
+
? '$s\n...[truncated, $totalChars total chars]'
|
|
528
641
|
: s;
|
|
529
642
|
}
|
|
530
643
|
|
|
531
644
|
return <String, Object?>{
|
|
532
645
|
'exitCode': exitCode,
|
|
533
|
-
'stdout': trimOutput(stdoutBuf),
|
|
534
|
-
'stderr': trimOutput(stderrBuf),
|
|
646
|
+
'stdout': trimOutput(stdoutBuf, stdoutChars),
|
|
647
|
+
'stderr': trimOutput(stderrBuf, stderrChars),
|
|
535
648
|
'timedOut': timedOut,
|
|
536
|
-
'killed': timedOut,
|
|
649
|
+
'killed': timedOut || externallyCancelled,
|
|
650
|
+
'cancelled': externallyCancelled,
|
|
537
651
|
'durationMs': DateTime.now().difference(startedAt).inMilliseconds,
|
|
538
652
|
'command': command,
|
|
539
653
|
'cwd': workingDir,
|
|
540
654
|
'backend': 'desktop-companion',
|
|
655
|
+
'ptyRequested': requestedPty,
|
|
656
|
+
'ptyAllocated': false,
|
|
541
657
|
};
|
|
542
658
|
}
|
|
543
659
|
|
|
660
|
+
Future<Map<String, Object?>> cancelShellCommand(String commandId) async {
|
|
661
|
+
if (commandId.isEmpty) {
|
|
662
|
+
return <String, Object?>{'success': false, 'cancelled': false};
|
|
663
|
+
}
|
|
664
|
+
_cancelledShellCommandIds.add(commandId);
|
|
665
|
+
final process = _shellProcesses[commandId];
|
|
666
|
+
if (process == null) {
|
|
667
|
+
return <String, Object?>{'success': true, 'cancelled': true};
|
|
668
|
+
}
|
|
669
|
+
await _terminateShellProcess(process);
|
|
670
|
+
return <String, Object?>{'success': true, 'cancelled': true};
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
Future<int?> _terminateShellProcess(Process process) async {
|
|
674
|
+
process.kill(ProcessSignal.sigterm);
|
|
675
|
+
try {
|
|
676
|
+
return await process.exitCode.timeout(const Duration(seconds: 2));
|
|
677
|
+
} on TimeoutException {
|
|
678
|
+
process.kill(ProcessSignal.sigkill);
|
|
679
|
+
try {
|
|
680
|
+
return await process.exitCode.timeout(const Duration(seconds: 2));
|
|
681
|
+
} on TimeoutException {
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
544
687
|
Future<Map<String, Object?>> _capabilities({
|
|
545
688
|
Map<String, Object?>? platformStatus,
|
|
546
689
|
}) async {
|
|
@@ -687,12 +830,13 @@ class DesktopCompanionActions {
|
|
|
687
830
|
'IdleHint',
|
|
688
831
|
]);
|
|
689
832
|
if (result.exitCode == 0) {
|
|
690
|
-
final lines =
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
833
|
+
final lines =
|
|
834
|
+
result.stdout
|
|
835
|
+
?.toString()
|
|
836
|
+
.split(RegExp(r'\r?\n'))
|
|
837
|
+
.map((line) => line.trim())
|
|
838
|
+
.where((line) => line.isNotEmpty)
|
|
839
|
+
.toList(growable: false) ??
|
|
696
840
|
const <String>[];
|
|
697
841
|
for (final line in lines) {
|
|
698
842
|
if (line.startsWith('LockedHint=')) {
|
|
@@ -713,8 +857,7 @@ class DesktopCompanionActions {
|
|
|
713
857
|
if (idleMs != null) {
|
|
714
858
|
final idleSeconds = idleMs / 1000;
|
|
715
859
|
state['idleSeconds'] = idleSeconds;
|
|
716
|
-
state['userIdle'] =
|
|
717
|
-
(state['userIdle'] == true) || idleSeconds >= 300;
|
|
860
|
+
state['userIdle'] = (state['userIdle'] == true) || idleSeconds >= 300;
|
|
718
861
|
}
|
|
719
862
|
}
|
|
720
863
|
} catch (_) {}
|
|
@@ -814,6 +957,17 @@ class DesktopCompanionActions {
|
|
|
814
957
|
];
|
|
815
958
|
}
|
|
816
959
|
|
|
960
|
+
List<Map<String, Object?>> _coerceDisplays(Object? raw) {
|
|
961
|
+
if (raw is! List) return const <Map<String, Object?>>[];
|
|
962
|
+
return raw
|
|
963
|
+
.whereType<Map>()
|
|
964
|
+
.map(
|
|
965
|
+
(item) => item.map((key, value) => MapEntry(key.toString(), value)),
|
|
966
|
+
)
|
|
967
|
+
.where((item) => item['id']?.toString().trim().isNotEmpty == true)
|
|
968
|
+
.toList(growable: false);
|
|
969
|
+
}
|
|
970
|
+
|
|
817
971
|
Future<void> _run(_ShellCommand command) async {
|
|
818
972
|
final result = await Process.run(command.command, command.args);
|
|
819
973
|
if (result.exitCode != 0) {
|