@cloudcli-ai/cloudcli 1.35.0 → 1.35.1
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/README.md +12 -7
- package/dist/assets/index-BvX30NW5.css +32 -0
- package/dist/assets/{index-CtwxqzGG.js → index-D1IdCTT6.js} +269 -258
- package/dist/index.html +12 -4
- package/dist-server/server/index.js +0 -13
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/websocket/services/websocket-server.service.js +0 -5
- package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
- package/electron/desktopWindow.js +0 -15
- package/electron/launcher/launcher.js +1 -143
- package/electron/main.js +1 -117
- package/electron/preload.cjs +0 -3
- package/package.json +3 -8
- package/scripts/release/prepare-desktop-app.js +0 -6
- package/server/index.js +0 -14
- package/server/modules/websocket/services/websocket-server.service.ts +0 -6
- package/dist/assets/index-DlvtxFyt.css +0 -32
- package/dist-server/server/computer-use-agent.js +0 -242
- package/dist-server/server/computer-use-agent.js.map +0 -1
- package/dist-server/server/computer-use-mcp.js +0 -519
- package/dist-server/server/computer-use-mcp.js.map +0 -1
- package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js +0 -55
- package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js.map +0 -1
- package/dist-server/server/modules/computer-use/actions/raw-action-types.js +0 -2
- package/dist-server/server/modules/computer-use/actions/raw-action-types.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-executor.js +0 -212
- package/dist-server/server/modules/computer-use/computer-executor.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-semantics.service.js +0 -422
- package/dist-server/server/modules/computer-use/computer-semantics.service.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js +0 -126
- package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-use.routes.js +0 -196
- package/dist-server/server/modules/computer-use/computer-use.routes.js.map +0 -1
- package/dist-server/server/modules/computer-use/computer-use.service.js +0 -759
- package/dist-server/server/modules/computer-use/computer-use.service.js.map +0 -1
- package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js +0 -126
- package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js.map +0 -1
- package/dist-server/server/modules/computer-use/index.js +0 -3
- package/dist-server/server/modules/computer-use/index.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js +0 -59
- package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js +0 -5
- package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js +0 -2
- package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js +0 -5
- package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
- package/dist-server/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js +0 -106
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js +0 -78
- package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js +0 -68
- package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js +0 -16
- package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js.map +0 -1
- package/dist-server/server/modules/computer-use/semantics/semantic-types.js +0 -2
- package/dist-server/server/modules/computer-use/semantics/semantic-types.js.map +0 -1
- package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js +0 -39
- package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js.map +0 -1
- package/electron/computerAgent.js +0 -290
- package/scripts/build-computer-semantics.mjs +0 -133
- package/scripts/copy-computer-semantics-bin.mjs +0 -24
- package/server/computer-use-agent.ts +0 -279
- package/server/computer-use-mcp.ts +0 -574
- package/server/modules/computer-use/actions/raw-action-dispatcher.ts +0 -67
- package/server/modules/computer-use/actions/raw-action-types.ts +0 -28
- package/server/modules/computer-use/computer-executor.ts +0 -242
- package/server/modules/computer-use/computer-semantics.service.ts +0 -460
- package/server/modules/computer-use/computer-use-mcp.routes.ts +0 -141
- package/server/modules/computer-use/computer-use.routes.ts +0 -211
- package/server/modules/computer-use/computer-use.service.ts +0 -920
- package/server/modules/computer-use/desktop-agent-relay.service.ts +0 -158
- package/server/modules/computer-use/index.ts +0 -2
- package/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.ts +0 -82
- package/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.ts +0 -5
- package/server/modules/computer-use/semantics/adapters/semantic-adapter.ts +0 -23
- package/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.ts +0 -5
- package/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
- package/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
- package/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
- package/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
- package/server/modules/computer-use/semantics/helpers/macos/CloudCLISemantics.swift +0 -467
- package/server/modules/computer-use/semantics/helpers/semantic-helper-process.ts +0 -124
- package/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.ts +0 -97
- package/server/modules/computer-use/semantics/helpers/windows/CloudCLISemantics.csproj +0 -11
- package/server/modules/computer-use/semantics/helpers/windows/Program.cs +0 -534
- package/server/modules/computer-use/semantics/semantic-session-store.ts +0 -87
- package/server/modules/computer-use/semantics/semantic-tool-dispatcher.ts +0 -17
- package/server/modules/computer-use/semantics/semantic-types.ts +0 -58
- package/server/modules/websocket/services/desktop-agent-websocket.service.ts +0 -51
|
@@ -1,534 +0,0 @@
|
|
|
1
|
-
using System.Diagnostics;
|
|
2
|
-
using System.Drawing;
|
|
3
|
-
using System.Drawing.Imaging;
|
|
4
|
-
using System.IO;
|
|
5
|
-
using System.Runtime.InteropServices;
|
|
6
|
-
using System.Text.Json;
|
|
7
|
-
using System.Windows.Automation;
|
|
8
|
-
|
|
9
|
-
static class Program
|
|
10
|
-
{
|
|
11
|
-
private const int MaxStoredStates = 100;
|
|
12
|
-
private static readonly Dictionary<string, List<ElementRecord>> StateElements = new();
|
|
13
|
-
private static readonly Dictionary<string, Dictionary<string, AutomationElement>> StateAutomationElements = new();
|
|
14
|
-
private static readonly Queue<string> StateOrder = new();
|
|
15
|
-
|
|
16
|
-
public static void Main()
|
|
17
|
-
{
|
|
18
|
-
string? line;
|
|
19
|
-
while ((line = Console.ReadLine()) != null)
|
|
20
|
-
{
|
|
21
|
-
try
|
|
22
|
-
{
|
|
23
|
-
using var doc = JsonDocument.Parse(line);
|
|
24
|
-
var root = doc.RootElement;
|
|
25
|
-
var id = root.TryGetProperty("id", out var idValue) ? idValue.Clone() : default;
|
|
26
|
-
var method = root.TryGetProperty("method", out var methodValue) ? methodValue.GetString() ?? "" : "";
|
|
27
|
-
var parameters = root.TryGetProperty("params", out var paramsValue) && paramsValue.ValueKind == JsonValueKind.Object
|
|
28
|
-
? paramsValue.Clone()
|
|
29
|
-
: JsonDocument.Parse("{}").RootElement.Clone();
|
|
30
|
-
|
|
31
|
-
try
|
|
32
|
-
{
|
|
33
|
-
object result = method switch
|
|
34
|
-
{
|
|
35
|
-
"list_apps" => ListApps(),
|
|
36
|
-
"get_app_state" => GetAppState(parameters),
|
|
37
|
-
"click_element" => ClickElement(parameters),
|
|
38
|
-
"perform_secondary_action" => PerformSecondaryAction(parameters),
|
|
39
|
-
"set_value" => SetValue(parameters),
|
|
40
|
-
"type_text" => TypeText(parameters),
|
|
41
|
-
"press_key" => PressKey(parameters),
|
|
42
|
-
"scroll_element" => ScrollElement(parameters),
|
|
43
|
-
"drag" => Drag(parameters),
|
|
44
|
-
_ => throw new InvalidOperationException($"Method is not implemented yet: {method}")
|
|
45
|
-
};
|
|
46
|
-
Write(new Dictionary<string, object?> { ["id"] = JsonValue(id), ["result"] = result });
|
|
47
|
-
}
|
|
48
|
-
catch (Exception ex)
|
|
49
|
-
{
|
|
50
|
-
Write(new Dictionary<string, object?> { ["id"] = JsonValue(id), ["error"] = ex.Message });
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch (Exception ex)
|
|
54
|
-
{
|
|
55
|
-
Write(new Dictionary<string, object?> { ["id"] = null, ["error"] = $"Invalid JSON request: {ex.Message}" });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private static object? JsonValue(JsonElement element)
|
|
61
|
-
{
|
|
62
|
-
return element.ValueKind switch
|
|
63
|
-
{
|
|
64
|
-
JsonValueKind.String => element.GetString(),
|
|
65
|
-
JsonValueKind.Number => element.TryGetInt64(out var number) ? number : element.GetDouble(),
|
|
66
|
-
JsonValueKind.True => true,
|
|
67
|
-
JsonValueKind.False => false,
|
|
68
|
-
_ => null
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private static void Write(object value)
|
|
73
|
-
{
|
|
74
|
-
Console.WriteLine(JsonSerializer.Serialize(value));
|
|
75
|
-
Console.Out.Flush();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private static List<Dictionary<string, object?>> ListApps()
|
|
79
|
-
{
|
|
80
|
-
return Process.GetProcesses()
|
|
81
|
-
.Where(process => process.MainWindowHandle != IntPtr.Zero)
|
|
82
|
-
.OrderBy(process => process.ProcessName)
|
|
83
|
-
.Select(process => new Dictionary<string, object?>
|
|
84
|
-
{
|
|
85
|
-
["id"] = process.Id.ToString(),
|
|
86
|
-
["name"] = process.ProcessName,
|
|
87
|
-
["processName"] = process.ProcessName,
|
|
88
|
-
["pid"] = process.Id,
|
|
89
|
-
["running"] = true,
|
|
90
|
-
["windowTitle"] = process.MainWindowTitle
|
|
91
|
-
})
|
|
92
|
-
.ToList();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private static Process ResolveProcess(string query)
|
|
96
|
-
{
|
|
97
|
-
var normalized = query.Trim();
|
|
98
|
-
if (string.IsNullOrWhiteSpace(normalized))
|
|
99
|
-
{
|
|
100
|
-
throw new InvalidOperationException("app is required.");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
var processes = Process.GetProcesses()
|
|
104
|
-
.Where(process => process.MainWindowHandle != IntPtr.Zero)
|
|
105
|
-
.ToList();
|
|
106
|
-
|
|
107
|
-
return processes.FirstOrDefault(process => process.ProcessName.Equals(normalized, StringComparison.OrdinalIgnoreCase))
|
|
108
|
-
?? processes.FirstOrDefault(process => process.MainWindowTitle.Equals(normalized, StringComparison.OrdinalIgnoreCase))
|
|
109
|
-
?? processes.FirstOrDefault(process => process.MainWindowTitle.Contains(normalized, StringComparison.OrdinalIgnoreCase))
|
|
110
|
-
?? throw new InvalidOperationException($"App is not running: {query}");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private static Dictionary<string, object?> GetAppState(JsonElement parameters)
|
|
114
|
-
{
|
|
115
|
-
var appQuery = ReadString(parameters, "app");
|
|
116
|
-
var process = ResolveProcess(appQuery);
|
|
117
|
-
var root = AutomationElement.FromHandle(process.MainWindowHandle)
|
|
118
|
-
?? throw new InvalidOperationException("No UI Automation root window is available.");
|
|
119
|
-
|
|
120
|
-
var records = new List<ElementRecord>();
|
|
121
|
-
var automationElements = new Dictionary<string, AutomationElement>();
|
|
122
|
-
Walk(root, records, automationElements, 0, 5, 300);
|
|
123
|
-
var stateId = $"state_{Guid.NewGuid()}";
|
|
124
|
-
StateElements[stateId] = records;
|
|
125
|
-
StateAutomationElements[stateId] = automationElements;
|
|
126
|
-
StateOrder.Enqueue(stateId);
|
|
127
|
-
PruneStoredStates();
|
|
128
|
-
|
|
129
|
-
var elements = records.Select(record => record.ToDictionary()).ToList();
|
|
130
|
-
var bounds = root.Current.BoundingRectangle;
|
|
131
|
-
return new Dictionary<string, object?>
|
|
132
|
-
{
|
|
133
|
-
["stateId"] = stateId,
|
|
134
|
-
["app"] = process.ProcessName,
|
|
135
|
-
["platform"] = "win32",
|
|
136
|
-
["screenshotDataUrl"] = CaptureScreen(),
|
|
137
|
-
["displaySize"] = new Dictionary<string, object?>
|
|
138
|
-
{
|
|
139
|
-
["width"] = (int)System.Windows.Forms.Screen.PrimaryScreen!.Bounds.Width,
|
|
140
|
-
["height"] = (int)System.Windows.Forms.Screen.PrimaryScreen!.Bounds.Height
|
|
141
|
-
},
|
|
142
|
-
["window"] = new Dictionary<string, object?>
|
|
143
|
-
{
|
|
144
|
-
["title"] = process.MainWindowTitle,
|
|
145
|
-
["bounds"] = BoundsDictionary(bounds)
|
|
146
|
-
},
|
|
147
|
-
["elements"] = elements,
|
|
148
|
-
["accessibilityTree"] = elements,
|
|
149
|
-
["treeText"] = string.Join("\n", elements.Select(element => $"{element["index"]} {element["role"]} {element.GetValueOrDefault("title")}"))
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private static Dictionary<string, object?> ClickElement(JsonElement parameters)
|
|
154
|
-
{
|
|
155
|
-
var mouseButton = ReadString(parameters, "mouse_button");
|
|
156
|
-
if ((mouseButton == "" || mouseButton == "left") && ReadInt(parameters, "click_count", 1) == 1)
|
|
157
|
-
{
|
|
158
|
-
var element = AutomationElementFor(parameters);
|
|
159
|
-
if (element != null && TryInvoke(element))
|
|
160
|
-
{
|
|
161
|
-
return GetAppState(parameters);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
var point = PointFor(parameters);
|
|
166
|
-
if (point == null)
|
|
167
|
-
{
|
|
168
|
-
throw new InvalidOperationException("click_element requires x/y or stateId + element_index.");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
SendMouseClick(point.Value.X, point.Value.Y, ReadString(parameters, "mouse_button"), ReadInt(parameters, "click_count", 1));
|
|
172
|
-
return GetAppState(parameters);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
private static Dictionary<string, object?> PerformSecondaryAction(JsonElement parameters)
|
|
176
|
-
{
|
|
177
|
-
var point = PointFor(parameters);
|
|
178
|
-
if (point == null)
|
|
179
|
-
{
|
|
180
|
-
throw new InvalidOperationException("perform_secondary_action requires x/y or stateId + element_index.");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
SendMouseClick(point.Value.X, point.Value.Y, "right", 1);
|
|
184
|
-
return GetAppState(parameters);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
private static Dictionary<string, object?> SetValue(JsonElement parameters)
|
|
188
|
-
{
|
|
189
|
-
var value = ReadString(parameters, "value");
|
|
190
|
-
var element = AutomationElementFor(parameters);
|
|
191
|
-
var focused = false;
|
|
192
|
-
if (element != null)
|
|
193
|
-
{
|
|
194
|
-
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out var valuePattern))
|
|
195
|
-
{
|
|
196
|
-
((ValuePattern)valuePattern).SetValue(value);
|
|
197
|
-
return GetAppState(parameters);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
try
|
|
201
|
-
{
|
|
202
|
-
element.SetFocus();
|
|
203
|
-
focused = true;
|
|
204
|
-
}
|
|
205
|
-
catch
|
|
206
|
-
{
|
|
207
|
-
// Fall through to coordinate focus below.
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
var point = PointFor(parameters);
|
|
212
|
-
if (point != null)
|
|
213
|
-
{
|
|
214
|
-
SendMouseClick(point.Value.X, point.Value.Y, "left", 1);
|
|
215
|
-
focused = true;
|
|
216
|
-
}
|
|
217
|
-
else if (!focused && element == null)
|
|
218
|
-
{
|
|
219
|
-
throw new InvalidOperationException("set_value requires x/y or stateId + element_index.");
|
|
220
|
-
}
|
|
221
|
-
else if (!focused)
|
|
222
|
-
{
|
|
223
|
-
throw new InvalidOperationException("set_value could not focus the requested element.");
|
|
224
|
-
}
|
|
225
|
-
System.Windows.Forms.SendKeys.SendWait("^a");
|
|
226
|
-
System.Windows.Forms.SendKeys.SendWait(EscapeSendKeys(value));
|
|
227
|
-
return GetAppState(parameters);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
private static Dictionary<string, object?> TypeText(JsonElement parameters)
|
|
231
|
-
{
|
|
232
|
-
var text = ReadString(parameters, "text");
|
|
233
|
-
System.Windows.Forms.SendKeys.SendWait(EscapeSendKeys(text));
|
|
234
|
-
return GetAppState(parameters);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private static Dictionary<string, object?> PressKey(JsonElement parameters)
|
|
238
|
-
{
|
|
239
|
-
var key = ReadString(parameters, "key");
|
|
240
|
-
System.Windows.Forms.SendKeys.SendWait(ToSendKeysChord(key));
|
|
241
|
-
return GetAppState(parameters);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
private static Dictionary<string, object?> ScrollElement(JsonElement parameters)
|
|
245
|
-
{
|
|
246
|
-
var element = AutomationElementFor(parameters);
|
|
247
|
-
var direction = ReadString(parameters, "direction");
|
|
248
|
-
var pages = ReadDouble(parameters, "pages", 1);
|
|
249
|
-
if (element != null && element.TryGetCurrentPattern(ScrollPattern.Pattern, out var scrollPatternValue))
|
|
250
|
-
{
|
|
251
|
-
var scrollPattern = (ScrollPattern)scrollPatternValue;
|
|
252
|
-
var vertical = direction == "up" ? ScrollAmount.LargeDecrement : direction == "down" ? ScrollAmount.LargeIncrement : ScrollAmount.NoAmount;
|
|
253
|
-
var horizontal = direction == "left" ? ScrollAmount.LargeDecrement : direction == "right" ? ScrollAmount.LargeIncrement : ScrollAmount.NoAmount;
|
|
254
|
-
scrollPattern.Scroll(horizontal, vertical);
|
|
255
|
-
return GetAppState(parameters);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
var point = PointFor(parameters);
|
|
259
|
-
if (point == null)
|
|
260
|
-
{
|
|
261
|
-
throw new InvalidOperationException("scroll_element requires x/y or stateId + element_index.");
|
|
262
|
-
}
|
|
263
|
-
SetCursorPos(point.Value.X, point.Value.Y);
|
|
264
|
-
var wheel = (int)Math.Round(Math.Max(1, pages) * 120);
|
|
265
|
-
if (direction == "down") wheel = -wheel;
|
|
266
|
-
mouse_event(0x0800, 0, 0, unchecked((uint)wheel), UIntPtr.Zero);
|
|
267
|
-
return GetAppState(parameters);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
private static void PruneStoredStates()
|
|
271
|
-
{
|
|
272
|
-
while (StateOrder.Count > MaxStoredStates)
|
|
273
|
-
{
|
|
274
|
-
var evicted = StateOrder.Dequeue();
|
|
275
|
-
StateElements.Remove(evicted);
|
|
276
|
-
StateAutomationElements.Remove(evicted);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
private static Dictionary<string, object?> Drag(JsonElement parameters)
|
|
281
|
-
{
|
|
282
|
-
var fromX = ReadDouble(parameters, "from_x", double.NaN);
|
|
283
|
-
var fromY = ReadDouble(parameters, "from_y", double.NaN);
|
|
284
|
-
var toX = ReadDouble(parameters, "to_x", double.NaN);
|
|
285
|
-
var toY = ReadDouble(parameters, "to_y", double.NaN);
|
|
286
|
-
if (double.IsNaN(fromX) || double.IsNaN(fromY) || double.IsNaN(toX) || double.IsNaN(toY))
|
|
287
|
-
{
|
|
288
|
-
throw new InvalidOperationException("drag requires from_x/from_y/to_x/to_y.");
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
SetCursorPos((int)Math.Round(fromX), (int)Math.Round(fromY));
|
|
292
|
-
mouse_event(0x0002, 0, 0, 0, UIntPtr.Zero);
|
|
293
|
-
Thread.Sleep(80);
|
|
294
|
-
SetCursorPos((int)Math.Round(toX), (int)Math.Round(toY));
|
|
295
|
-
Thread.Sleep(80);
|
|
296
|
-
mouse_event(0x0004, 0, 0, 0, UIntPtr.Zero);
|
|
297
|
-
return GetAppState(parameters);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private static void Walk(AutomationElement element, List<ElementRecord> records, Dictionary<string, AutomationElement> automationElements, int depth, int maxDepth, int limit)
|
|
301
|
-
{
|
|
302
|
-
if (depth > maxDepth || records.Count >= limit) return;
|
|
303
|
-
var index = (records.Count + 1).ToString();
|
|
304
|
-
records.Add(ElementRecord.From(element, index));
|
|
305
|
-
automationElements[index] = element;
|
|
306
|
-
var children = element.FindAll(TreeScope.Children, Condition.TrueCondition);
|
|
307
|
-
foreach (AutomationElement child in children)
|
|
308
|
-
{
|
|
309
|
-
Walk(child, records, automationElements, depth + 1, maxDepth, limit);
|
|
310
|
-
if (records.Count >= limit) return;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
private static string ReadString(JsonElement element, string property)
|
|
315
|
-
{
|
|
316
|
-
return element.TryGetProperty(property, out var value) && value.ValueKind == JsonValueKind.String
|
|
317
|
-
? value.GetString() ?? ""
|
|
318
|
-
: "";
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
private static int ReadInt(JsonElement element, string property, int defaultValue)
|
|
322
|
-
{
|
|
323
|
-
return element.TryGetProperty(property, out var value) && value.TryGetInt32(out var number)
|
|
324
|
-
? number
|
|
325
|
-
: defaultValue;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
private static double ReadDouble(JsonElement element, string property, double defaultValue)
|
|
329
|
-
{
|
|
330
|
-
return element.TryGetProperty(property, out var value) && value.TryGetDouble(out var number)
|
|
331
|
-
? number
|
|
332
|
-
: defaultValue;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
private static AutomationElement? AutomationElementFor(JsonElement parameters)
|
|
336
|
-
{
|
|
337
|
-
var stateId = ReadString(parameters, "stateId");
|
|
338
|
-
var elementIndex = ReadString(parameters, "element_index");
|
|
339
|
-
return !string.IsNullOrWhiteSpace(stateId)
|
|
340
|
-
&& !string.IsNullOrWhiteSpace(elementIndex)
|
|
341
|
-
&& StateAutomationElements.TryGetValue(stateId, out var elements)
|
|
342
|
-
&& elements.TryGetValue(elementIndex, out var element)
|
|
343
|
-
? element
|
|
344
|
-
: null;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private static System.Drawing.Point? PointFor(JsonElement parameters)
|
|
348
|
-
{
|
|
349
|
-
if (parameters.TryGetProperty("x", out var xValue) && parameters.TryGetProperty("y", out var yValue)
|
|
350
|
-
&& xValue.TryGetDouble(out var x) && yValue.TryGetDouble(out var y))
|
|
351
|
-
{
|
|
352
|
-
return new System.Drawing.Point((int)Math.Round(x), (int)Math.Round(y));
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
var stateId = ReadString(parameters, "stateId");
|
|
356
|
-
var elementIndex = ReadString(parameters, "element_index");
|
|
357
|
-
if (string.IsNullOrWhiteSpace(stateId) || string.IsNullOrWhiteSpace(elementIndex)) return null;
|
|
358
|
-
if (!StateElements.TryGetValue(stateId, out var elements)) return null;
|
|
359
|
-
var element = elements.FirstOrDefault(item => item.Index == elementIndex);
|
|
360
|
-
if (element?.Bounds == null) return null;
|
|
361
|
-
return new System.Drawing.Point(
|
|
362
|
-
(int)Math.Round(element.Bounds.Value.Left + element.Bounds.Value.Width / 2),
|
|
363
|
-
(int)Math.Round(element.Bounds.Value.Top + element.Bounds.Value.Height / 2)
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
private static string CaptureScreen()
|
|
368
|
-
{
|
|
369
|
-
var bounds = System.Windows.Forms.Screen.PrimaryScreen!.Bounds;
|
|
370
|
-
using var bitmap = new Bitmap(bounds.Width, bounds.Height);
|
|
371
|
-
using var graphics = Graphics.FromImage(bitmap);
|
|
372
|
-
graphics.CopyFromScreen(bounds.Left, bounds.Top, 0, 0, bounds.Size);
|
|
373
|
-
using var stream = new MemoryStream();
|
|
374
|
-
bitmap.Save(stream, ImageFormat.Png);
|
|
375
|
-
return $"data:image/png;base64,{Convert.ToBase64String(stream.ToArray())}";
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
private static Dictionary<string, object?> BoundsDictionary(System.Windows.Rect rect)
|
|
379
|
-
{
|
|
380
|
-
return new Dictionary<string, object?>
|
|
381
|
-
{
|
|
382
|
-
["x"] = rect.X,
|
|
383
|
-
["y"] = rect.Y,
|
|
384
|
-
["width"] = rect.Width,
|
|
385
|
-
["height"] = rect.Height
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
[DllImport("user32.dll")]
|
|
390
|
-
private static extern bool SetCursorPos(int x, int y);
|
|
391
|
-
|
|
392
|
-
[DllImport("user32.dll")]
|
|
393
|
-
private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
|
|
394
|
-
|
|
395
|
-
private static void SendMouseClick(int x, int y, string button, int clickCount)
|
|
396
|
-
{
|
|
397
|
-
var (down, up) = button switch
|
|
398
|
-
{
|
|
399
|
-
"right" => (0x0008u, 0x0010u),
|
|
400
|
-
"middle" => (0x0020u, 0x0040u),
|
|
401
|
-
_ => (0x0002u, 0x0004u)
|
|
402
|
-
};
|
|
403
|
-
SetCursorPos(x, y);
|
|
404
|
-
for (var i = 0; i < Math.Max(1, clickCount); i++)
|
|
405
|
-
{
|
|
406
|
-
mouse_event(down, 0, 0, 0, UIntPtr.Zero);
|
|
407
|
-
mouse_event(up, 0, 0, 0, UIntPtr.Zero);
|
|
408
|
-
Thread.Sleep(80);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
private static bool TryInvoke(AutomationElement element)
|
|
413
|
-
{
|
|
414
|
-
try
|
|
415
|
-
{
|
|
416
|
-
if (!element.TryGetCurrentPattern(InvokePattern.Pattern, out var pattern)) return false;
|
|
417
|
-
((InvokePattern)pattern).Invoke();
|
|
418
|
-
return true;
|
|
419
|
-
}
|
|
420
|
-
catch
|
|
421
|
-
{
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
private static string EscapeSendKeys(string value)
|
|
427
|
-
{
|
|
428
|
-
return value
|
|
429
|
-
.Replace("{", "{{}")
|
|
430
|
-
.Replace("}", "{}}")
|
|
431
|
-
.Replace("+", "{+}")
|
|
432
|
-
.Replace("^", "{^}")
|
|
433
|
-
.Replace("%", "{%}")
|
|
434
|
-
.Replace("~", "{~}")
|
|
435
|
-
.Replace("(", "{(}")
|
|
436
|
-
.Replace(")", "{)}")
|
|
437
|
-
.Replace("[", "{[}")
|
|
438
|
-
.Replace("]", "{]}");
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
private static string ToSendKeysChord(string key)
|
|
442
|
-
{
|
|
443
|
-
var normalized = key.Trim();
|
|
444
|
-
if (normalized.Contains('+'))
|
|
445
|
-
{
|
|
446
|
-
var parts = normalized.Split('+', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
|
447
|
-
var modifiers = "";
|
|
448
|
-
var last = parts.LastOrDefault() ?? "";
|
|
449
|
-
foreach (var part in parts.Take(parts.Length - 1))
|
|
450
|
-
{
|
|
451
|
-
modifiers += part.ToLowerInvariant() switch
|
|
452
|
-
{
|
|
453
|
-
"ctrl" or "control" => "^",
|
|
454
|
-
"alt" => "%",
|
|
455
|
-
"shift" => "+",
|
|
456
|
-
"cmd" or "win" or "windows" => "^",
|
|
457
|
-
_ => ""
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
return modifiers + SendKeyName(last);
|
|
461
|
-
}
|
|
462
|
-
return SendKeyName(normalized);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
private static string SendKeyName(string key)
|
|
466
|
-
{
|
|
467
|
-
return key.ToLowerInvariant() switch
|
|
468
|
-
{
|
|
469
|
-
"return" or "enter" => "{ENTER}",
|
|
470
|
-
"escape" or "esc" => "{ESC}",
|
|
471
|
-
"tab" => "{TAB}",
|
|
472
|
-
"backspace" => "{BACKSPACE}",
|
|
473
|
-
"delete" or "del" => "{DELETE}",
|
|
474
|
-
"left" => "{LEFT}",
|
|
475
|
-
"right" => "{RIGHT}",
|
|
476
|
-
"up" => "{UP}",
|
|
477
|
-
"down" => "{DOWN}",
|
|
478
|
-
"space" => " ",
|
|
479
|
-
_ => key.Length == 1 ? EscapeSendKeys(key) : $"{{{key.ToUpperInvariant()}}}"
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
private sealed record ElementRecord(
|
|
484
|
-
string Index,
|
|
485
|
-
string Role,
|
|
486
|
-
string? Title,
|
|
487
|
-
string? Value,
|
|
488
|
-
System.Windows.Rect? Bounds,
|
|
489
|
-
List<string> Actions)
|
|
490
|
-
{
|
|
491
|
-
public static ElementRecord From(AutomationElement element, string index)
|
|
492
|
-
{
|
|
493
|
-
var patterns = element.GetSupportedPatterns().Select(pattern => pattern.ProgrammaticName).ToList();
|
|
494
|
-
return new ElementRecord(
|
|
495
|
-
index,
|
|
496
|
-
element.Current.ControlType.ProgrammaticName.Replace("ControlType.", ""),
|
|
497
|
-
element.Current.Name,
|
|
498
|
-
TryValue(element),
|
|
499
|
-
element.Current.BoundingRectangle,
|
|
500
|
-
patterns
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
public Dictionary<string, object?> ToDictionary()
|
|
505
|
-
{
|
|
506
|
-
var output = new Dictionary<string, object?>
|
|
507
|
-
{
|
|
508
|
-
["index"] = Index,
|
|
509
|
-
["role"] = Role,
|
|
510
|
-
["actions"] = Actions
|
|
511
|
-
};
|
|
512
|
-
if (!string.IsNullOrEmpty(Title)) output["title"] = Title;
|
|
513
|
-
if (!string.IsNullOrEmpty(Value)) output["value"] = Value;
|
|
514
|
-
if (Bounds != null) output["bounds"] = BoundsDictionary(Bounds.Value);
|
|
515
|
-
return output;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
private static string? TryValue(AutomationElement element)
|
|
519
|
-
{
|
|
520
|
-
try
|
|
521
|
-
{
|
|
522
|
-
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out var pattern))
|
|
523
|
-
{
|
|
524
|
-
return ((ValuePattern)pattern).Current.Value;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
catch
|
|
528
|
-
{
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
return null;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
|
|
3
|
-
import type { SemanticAppState, SemanticElement } from '@/modules/computer-use/semantics/semantic-types.js';
|
|
4
|
-
|
|
5
|
-
const DEFAULT_STATE_TTL_MS = Number.parseInt(process.env.CLOUDCLI_COMPUTER_SEMANTIC_STATE_TTL_MS || String(10 * 60 * 1000), 10);
|
|
6
|
-
|
|
7
|
-
type StoredState = {
|
|
8
|
-
sessionId: string;
|
|
9
|
-
appKey: string;
|
|
10
|
-
state: SemanticAppState;
|
|
11
|
-
updatedAt: number;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function normalizeAppKey(app: string): string {
|
|
15
|
-
return app.trim().toLowerCase();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class SemanticSessionStore {
|
|
19
|
-
private states = new Map<string, StoredState>();
|
|
20
|
-
private latestBySessionApp = new Map<string, string>();
|
|
21
|
-
|
|
22
|
-
createStateId(): string {
|
|
23
|
-
return `state_${randomUUID()}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
save(sessionId: string, state: SemanticAppState): SemanticAppState {
|
|
27
|
-
const appKey = normalizeAppKey(state.app);
|
|
28
|
-
const nextState = {
|
|
29
|
-
...state,
|
|
30
|
-
stateId: state.stateId || this.createStateId(),
|
|
31
|
-
};
|
|
32
|
-
this.states.set(nextState.stateId, {
|
|
33
|
-
sessionId,
|
|
34
|
-
appKey,
|
|
35
|
-
state: nextState,
|
|
36
|
-
updatedAt: Date.now(),
|
|
37
|
-
});
|
|
38
|
-
this.latestBySessionApp.set(this.latestKey(sessionId, appKey), nextState.stateId);
|
|
39
|
-
return nextState;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
getState(sessionId: string, app: string, stateId?: string): SemanticAppState | null {
|
|
43
|
-
this.expire();
|
|
44
|
-
if (stateId) {
|
|
45
|
-
const entry = this.states.get(stateId);
|
|
46
|
-
const appKey = normalizeAppKey(app);
|
|
47
|
-
return entry && entry.sessionId === sessionId && entry.appKey === appKey ? entry.state : null;
|
|
48
|
-
}
|
|
49
|
-
const latestStateId = this.latestBySessionApp.get(this.latestKey(sessionId, normalizeAppKey(app)));
|
|
50
|
-
return latestStateId ? this.states.get(latestStateId)?.state || null : null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
getElement(sessionId: string, app: string, elementIndex: string, stateId?: string): SemanticElement | null {
|
|
54
|
-
const state = this.getState(sessionId, app, stateId);
|
|
55
|
-
return state?.elements.find((element) => element.index === elementIndex) || null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
clearSession(sessionId: string): void {
|
|
59
|
-
for (const [stateId, entry] of this.states.entries()) {
|
|
60
|
-
if (entry.sessionId === sessionId) {
|
|
61
|
-
this.states.delete(stateId);
|
|
62
|
-
this.latestBySessionApp.delete(this.latestKey(entry.sessionId, entry.appKey));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
expire(now = Date.now()): void {
|
|
68
|
-
const ttl = Number.isFinite(DEFAULT_STATE_TTL_MS) && DEFAULT_STATE_TTL_MS > 0
|
|
69
|
-
? DEFAULT_STATE_TTL_MS
|
|
70
|
-
: 10 * 60 * 1000;
|
|
71
|
-
for (const [stateId, entry] of this.states.entries()) {
|
|
72
|
-
if (now - entry.updatedAt > ttl) {
|
|
73
|
-
this.states.delete(stateId);
|
|
74
|
-
const key = this.latestKey(entry.sessionId, entry.appKey);
|
|
75
|
-
if (this.latestBySessionApp.get(key) === stateId) {
|
|
76
|
-
this.latestBySessionApp.delete(key);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private latestKey(sessionId: string, appKey: string): string {
|
|
83
|
-
return `${sessionId}:${appKey}`;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export const semanticSessionStore = new SemanticSessionStore();
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export const semanticMcpToolMap: Record<string, string> = {
|
|
2
|
-
computer_app_drag: 'drag',
|
|
3
|
-
computer_click_element: 'click_element',
|
|
4
|
-
computer_get_app_state: 'get_app_state',
|
|
5
|
-
computer_list_apps: 'list_apps',
|
|
6
|
-
computer_perform_secondary_action: 'perform_secondary_action',
|
|
7
|
-
computer_press_key: 'press_key',
|
|
8
|
-
computer_scroll_element: 'scroll_element',
|
|
9
|
-
computer_set_value: 'set_value',
|
|
10
|
-
computer_type_text: 'type_text',
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const semanticOperationNames = new Set(Object.values(semanticMcpToolMap));
|
|
14
|
-
|
|
15
|
-
export function semanticOperationForMcpTool(toolName: string): string | null {
|
|
16
|
-
return semanticMcpToolMap[toolName] || null;
|
|
17
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { DisplaySize, Point } from '@/modules/computer-use/computer-executor.js';
|
|
2
|
-
|
|
3
|
-
export type SemanticBounds = {
|
|
4
|
-
x: number;
|
|
5
|
-
y: number;
|
|
6
|
-
width: number;
|
|
7
|
-
height: number;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export type SemanticApp = {
|
|
11
|
-
id?: string;
|
|
12
|
-
name: string;
|
|
13
|
-
bundleIdentifier?: string;
|
|
14
|
-
processName?: string;
|
|
15
|
-
pid?: number;
|
|
16
|
-
running: boolean;
|
|
17
|
-
windowTitle?: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type SemanticElement = {
|
|
21
|
-
index: string;
|
|
22
|
-
role: string;
|
|
23
|
-
title?: string;
|
|
24
|
-
value?: string;
|
|
25
|
-
description?: string;
|
|
26
|
-
enabled?: boolean;
|
|
27
|
-
focused?: boolean;
|
|
28
|
-
selected?: boolean;
|
|
29
|
-
bounds?: SemanticBounds;
|
|
30
|
-
actions?: string[];
|
|
31
|
-
settableValue?: boolean;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type SemanticAppState = {
|
|
35
|
-
stateId: string;
|
|
36
|
-
app: string;
|
|
37
|
-
platform: NodeJS.Platform;
|
|
38
|
-
screenshotDataUrl: string | null;
|
|
39
|
-
displaySize: DisplaySize | null;
|
|
40
|
-
elements: SemanticElement[];
|
|
41
|
-
accessibilityTree: SemanticElement[];
|
|
42
|
-
treeText?: string;
|
|
43
|
-
message?: string;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export type SemanticToolInput = Record<string, unknown> & {
|
|
47
|
-
sessionId?: string;
|
|
48
|
-
app?: string;
|
|
49
|
-
stateId?: string;
|
|
50
|
-
element_index?: string;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export type SemanticToolResult = SemanticAppState | {
|
|
54
|
-
apps: SemanticApp[];
|
|
55
|
-
platform: NodeJS.Platform;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export type SemanticActionPoint = Point;
|