@cloudcli-ai/cloudcli 1.35.0 → 1.36.0

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 (126) hide show
  1. package/README.md +12 -7
  2. package/dist/api-docs.html +8 -2
  3. package/dist/assets/{index-CtwxqzGG.js → index-S6cIy7hj.js} +271 -260
  4. package/dist/assets/index-uLUE6Qt1.css +32 -0
  5. package/dist/index.html +12 -4
  6. package/dist-server/server/claude-sdk.js +25 -26
  7. package/dist-server/server/claude-sdk.js.map +1 -1
  8. package/dist-server/server/index.js +0 -13
  9. package/dist-server/server/index.js.map +1 -1
  10. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +70 -1
  11. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  12. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +52 -11
  13. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  14. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +138 -1
  15. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  16. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +5 -0
  17. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  18. package/dist-server/server/modules/providers/services/provider-models.service.js +1 -1
  19. package/dist-server/server/modules/providers/tests/opencode-models.test.js +58 -1
  20. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
  21. package/dist-server/server/modules/websocket/services/websocket-server.service.js +0 -5
  22. package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
  23. package/dist-server/server/openai-codex.js +9 -8
  24. package/dist-server/server/openai-codex.js.map +1 -1
  25. package/dist-server/server/opencode-cli.js +20 -2
  26. package/dist-server/server/opencode-cli.js.map +1 -1
  27. package/dist-server/server/routes/agent.js +14 -3
  28. package/dist-server/server/routes/agent.js.map +1 -1
  29. package/electron/desktopWindow.js +0 -15
  30. package/electron/launcher/launcher.js +1 -143
  31. package/electron/main.js +1 -117
  32. package/electron/preload.cjs +0 -3
  33. package/package.json +3 -8
  34. package/public/api-docs.html +8 -2
  35. package/scripts/release/prepare-desktop-app.js +0 -6
  36. package/server/claude-sdk.js +32 -26
  37. package/server/index.js +0 -14
  38. package/server/modules/providers/list/claude/claude-models.provider.ts +73 -1
  39. package/server/modules/providers/list/codex/codex-models.provider.ts +59 -11
  40. package/server/modules/providers/list/opencode/opencode-models.provider.ts +180 -1
  41. package/server/modules/providers/services/provider-capabilities.service.ts +7 -0
  42. package/server/modules/providers/services/provider-models.service.ts +1 -1
  43. package/server/modules/providers/tests/opencode-models.test.ts +62 -0
  44. package/server/modules/websocket/services/websocket-server.service.ts +0 -6
  45. package/server/openai-codex.js +9 -7
  46. package/server/opencode-cli.js +21 -2
  47. package/server/routes/agent.js +14 -3
  48. package/server/shared/types.ts +7 -0
  49. package/dist/assets/index-DlvtxFyt.css +0 -32
  50. package/dist-server/server/computer-use-agent.js +0 -242
  51. package/dist-server/server/computer-use-agent.js.map +0 -1
  52. package/dist-server/server/computer-use-mcp.js +0 -519
  53. package/dist-server/server/computer-use-mcp.js.map +0 -1
  54. package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js +0 -55
  55. package/dist-server/server/modules/computer-use/actions/raw-action-dispatcher.js.map +0 -1
  56. package/dist-server/server/modules/computer-use/actions/raw-action-types.js +0 -2
  57. package/dist-server/server/modules/computer-use/actions/raw-action-types.js.map +0 -1
  58. package/dist-server/server/modules/computer-use/computer-executor.js +0 -212
  59. package/dist-server/server/modules/computer-use/computer-executor.js.map +0 -1
  60. package/dist-server/server/modules/computer-use/computer-semantics.service.js +0 -422
  61. package/dist-server/server/modules/computer-use/computer-semantics.service.js.map +0 -1
  62. package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js +0 -126
  63. package/dist-server/server/modules/computer-use/computer-use-mcp.routes.js.map +0 -1
  64. package/dist-server/server/modules/computer-use/computer-use.routes.js +0 -196
  65. package/dist-server/server/modules/computer-use/computer-use.routes.js.map +0 -1
  66. package/dist-server/server/modules/computer-use/computer-use.service.js +0 -759
  67. package/dist-server/server/modules/computer-use/computer-use.service.js.map +0 -1
  68. package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js +0 -126
  69. package/dist-server/server/modules/computer-use/desktop-agent-relay.service.js.map +0 -1
  70. package/dist-server/server/modules/computer-use/index.js +0 -3
  71. package/dist-server/server/modules/computer-use/index.js.map +0 -1
  72. package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js +0 -59
  73. package/dist-server/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.js.map +0 -1
  74. package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js +0 -5
  75. package/dist-server/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.js.map +0 -1
  76. package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js +0 -2
  77. package/dist-server/server/modules/computer-use/semantics/adapters/semantic-adapter.js.map +0 -1
  78. package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js +0 -5
  79. package/dist-server/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.js.map +0 -1
  80. package/dist-server/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
  81. package/dist-server/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
  82. package/dist-server/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
  83. package/dist-server/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
  84. package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js +0 -106
  85. package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-process.js.map +0 -1
  86. package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js +0 -78
  87. package/dist-server/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.js.map +0 -1
  88. package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js +0 -68
  89. package/dist-server/server/modules/computer-use/semantics/semantic-session-store.js.map +0 -1
  90. package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js +0 -16
  91. package/dist-server/server/modules/computer-use/semantics/semantic-tool-dispatcher.js.map +0 -1
  92. package/dist-server/server/modules/computer-use/semantics/semantic-types.js +0 -2
  93. package/dist-server/server/modules/computer-use/semantics/semantic-types.js.map +0 -1
  94. package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js +0 -39
  95. package/dist-server/server/modules/websocket/services/desktop-agent-websocket.service.js.map +0 -1
  96. package/electron/computerAgent.js +0 -290
  97. package/scripts/build-computer-semantics.mjs +0 -133
  98. package/scripts/copy-computer-semantics-bin.mjs +0 -24
  99. package/server/computer-use-agent.ts +0 -279
  100. package/server/computer-use-mcp.ts +0 -574
  101. package/server/modules/computer-use/actions/raw-action-dispatcher.ts +0 -67
  102. package/server/modules/computer-use/actions/raw-action-types.ts +0 -28
  103. package/server/modules/computer-use/computer-executor.ts +0 -242
  104. package/server/modules/computer-use/computer-semantics.service.ts +0 -460
  105. package/server/modules/computer-use/computer-use-mcp.routes.ts +0 -141
  106. package/server/modules/computer-use/computer-use.routes.ts +0 -211
  107. package/server/modules/computer-use/computer-use.service.ts +0 -920
  108. package/server/modules/computer-use/desktop-agent-relay.service.ts +0 -158
  109. package/server/modules/computer-use/index.ts +0 -2
  110. package/server/modules/computer-use/semantics/adapters/helper-semantic-adapter.ts +0 -82
  111. package/server/modules/computer-use/semantics/adapters/macos/macos-semantic-adapter.ts +0 -5
  112. package/server/modules/computer-use/semantics/adapters/semantic-adapter.ts +0 -23
  113. package/server/modules/computer-use/semantics/adapters/windows/windows-semantic-adapter.ts +0 -5
  114. package/server/modules/computer-use/semantics/bin/darwin-arm64/CloudCLISemantics +0 -0
  115. package/server/modules/computer-use/semantics/bin/darwin-x64/CloudCLISemantics +0 -0
  116. package/server/modules/computer-use/semantics/bin/win32-arm64/CloudCLISemantics.exe +0 -0
  117. package/server/modules/computer-use/semantics/bin/win32-x64/CloudCLISemantics.exe +0 -0
  118. package/server/modules/computer-use/semantics/helpers/macos/CloudCLISemantics.swift +0 -467
  119. package/server/modules/computer-use/semantics/helpers/semantic-helper-process.ts +0 -124
  120. package/server/modules/computer-use/semantics/helpers/semantic-helper-resolver.ts +0 -97
  121. package/server/modules/computer-use/semantics/helpers/windows/CloudCLISemantics.csproj +0 -11
  122. package/server/modules/computer-use/semantics/helpers/windows/Program.cs +0 -534
  123. package/server/modules/computer-use/semantics/semantic-session-store.ts +0 -87
  124. package/server/modules/computer-use/semantics/semantic-tool-dispatcher.ts +0 -17
  125. package/server/modules/computer-use/semantics/semantic-types.ts +0 -58
  126. 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;