ft-scout 9.0.3 → 9.0.5

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 (84) hide show
  1. package/.firebase/hosting.d2Vi.cache +7 -7
  2. package/README.md +76 -83
  3. package/bin/src/commands/agent.d.ts.map +1 -1
  4. package/bin/src/commands/agent.js +31 -228
  5. package/bin/src/commands/agent.js.map +1 -1
  6. package/bin/src/commands/plan.d.ts.map +1 -1
  7. package/bin/src/commands/plan.js +45 -18
  8. package/bin/src/commands/plan.js.map +1 -1
  9. package/bin/src/engine/actionVerifier.d.ts.map +1 -1
  10. package/bin/src/engine/actionVerifier.js +61 -9
  11. package/bin/src/engine/actionVerifier.js.map +1 -1
  12. package/bin/src/engine/agentEngine.d.ts.map +1 -1
  13. package/bin/src/engine/agentEngine.js +701 -553
  14. package/bin/src/engine/agentEngine.js.map +1 -1
  15. package/bin/src/engine/agentState.d.ts.map +1 -1
  16. package/bin/src/engine/agentState.js +0 -64
  17. package/bin/src/engine/agentState.js.map +1 -1
  18. package/bin/src/engine/browser/browserController.d.ts.map +1 -1
  19. package/bin/src/engine/browser/browserController.js +8 -0
  20. package/bin/src/engine/browser/browserController.js.map +1 -1
  21. package/bin/src/engine/computer/computerController.d.ts.map +1 -1
  22. package/bin/src/engine/computer/computerController.js +686 -240
  23. package/bin/src/engine/computer/computerController.js.map +1 -1
  24. package/bin/src/engine/computer/computerDiagnostic.d.ts.map +1 -0
  25. package/bin/src/engine/computer/computerDiagnostic.js +71 -0
  26. package/bin/src/engine/computer/computerDiagnostic.js.map +1 -0
  27. package/bin/src/engine/computer/index.d.ts.map +1 -1
  28. package/bin/src/engine/computer/index.js +2 -0
  29. package/bin/src/engine/computer/index.js.map +1 -1
  30. package/bin/src/engine/computer/mockComputer.d.ts.map +1 -0
  31. package/bin/src/engine/computer/mockComputer.js +260 -0
  32. package/bin/src/engine/computer/mockComputer.js.map +1 -0
  33. package/bin/src/engine/computer/scoutDriver.cs +873 -0
  34. package/bin/src/engine/computer/scout_driver.exe +0 -0
  35. package/bin/src/engine/runtime/agentRuntime.d.ts.map +1 -0
  36. package/bin/src/engine/runtime/agentRuntime.js +1160 -0
  37. package/bin/src/engine/runtime/agentRuntime.js.map +1 -0
  38. package/bin/src/engine/runtime/index.d.ts.map +1 -0
  39. package/bin/src/engine/runtime/index.js +2 -0
  40. package/bin/src/engine/runtime/index.js.map +1 -0
  41. package/bin/src/engine/safety.d.ts.map +1 -1
  42. package/bin/src/engine/safety.js +59 -25
  43. package/bin/src/engine/safety.js.map +1 -1
  44. package/bin/src/engine/toolRegistry.d.ts.map +1 -1
  45. package/bin/src/engine/toolRegistry.js +293 -68
  46. package/bin/src/engine/toolRegistry.js.map +1 -1
  47. package/bin/src/engine/voiceEngine.d.ts.map +1 -1
  48. package/bin/src/engine/voiceEngine.js +0 -1
  49. package/bin/src/engine/voiceEngine.js.map +1 -1
  50. package/bin/src/engine/workspace/agentWorkspace.d.ts.map +1 -0
  51. package/bin/src/engine/workspace/agentWorkspace.js +505 -0
  52. package/bin/src/engine/workspace/agentWorkspace.js.map +1 -0
  53. package/bin/src/engine/workspace/index.d.ts.map +1 -0
  54. package/bin/src/engine/workspace/index.js +2 -0
  55. package/bin/src/engine/workspace/index.js.map +1 -0
  56. package/bin/src/index.js +4 -5
  57. package/bin/src/index.js.map +1 -1
  58. package/bin/src/server/server.d.ts.map +1 -1
  59. package/bin/src/server/server.js +25 -4
  60. package/bin/src/server/server.js.map +1 -1
  61. package/bin/src/utils/auth.d.ts.map +1 -1
  62. package/bin/src/utils/auth.js +81 -50
  63. package/bin/src/utils/auth.js.map +1 -1
  64. package/bin/src/utils/branding.js +1 -1
  65. package/bin/src/utils/payment.d.ts.map +1 -1
  66. package/bin/src/utils/payment.js +15 -1
  67. package/bin/src/utils/payment.js.map +1 -1
  68. package/bin/src/utils/pricing.js +1 -1
  69. package/bin/src/utils/pricing.js.map +1 -1
  70. package/bin/src/utils/razorpay.d.ts.map +1 -1
  71. package/bin/src/utils/razorpay.js +10 -2
  72. package/bin/src/utils/razorpay.js.map +1 -1
  73. package/downloads/invoice-2026-03.pdf +1 -0
  74. package/package.json +1 -1
  75. package/web/app.js +54 -135
  76. package/web/styles.css +2 -2
  77. package/bin/src/engine/appControl.d.ts.map +0 -1
  78. package/bin/src/engine/appControl.js +0 -2413
  79. package/bin/src/engine/appControl.js.map +0 -1
  80. package/bin/src/engine/liveScreenEngine.d.ts.map +0 -1
  81. package/bin/src/engine/liveScreenEngine.js +0 -421
  82. package/bin/src/engine/liveScreenEngine.js.map +0 -1
  83. package/bin/src/engine/scripts/winCapture.ps1 +0 -248
  84. package/downloads/INVOICE_2026_ACME.pdf +0 -1
@@ -0,0 +1,873 @@
1
+ using System;
2
+ using System.IO;
3
+ using System.Text;
4
+ using System.Threading;
5
+ using System.Drawing;
6
+ using System.Drawing.Imaging;
7
+ using System.Windows.Forms;
8
+ using System.Runtime.InteropServices;
9
+ using System.Diagnostics;
10
+ using System.Collections.Generic;
11
+
12
+ namespace Scout.ComputerControl
13
+ {
14
+ public class ScoutDriver
15
+ {
16
+ [DllImport("user32.dll", SetLastError = true)]
17
+ public static extern IntPtr OpenWindowStation(string lpszWinSta, bool fInherit, uint dwDesiredAccess);
18
+
19
+ [DllImport("user32.dll", SetLastError = true)]
20
+ public static extern bool SetProcessWindowStation(IntPtr hWinSta);
21
+
22
+ [DllImport("user32.dll", SetLastError = true)]
23
+ public static extern IntPtr OpenDesktop(string lpszDesktop, uint dwFlags, bool fInherit, uint dwDesiredAccess);
24
+
25
+ [DllImport("user32.dll", SetLastError = true)]
26
+ public static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
27
+
28
+ [DllImport("user32.dll", SetLastError = true)]
29
+ public static extern bool SetThreadDesktop(IntPtr hDesktop);
30
+
31
+ [DllImport("user32.dll", SetLastError = true)]
32
+ public static extern bool CloseDesktop(IntPtr hDesktop);
33
+
34
+ [DllImport("user32.dll", SetLastError = true)]
35
+ public static extern bool GetCursorPos(out POINT lpPoint);
36
+
37
+ [DllImport("user32.dll", SetLastError = true)]
38
+ public static extern bool SetCursorPos(int X, int Y);
39
+
40
+ [DllImport("user32.dll", SetLastError = true)]
41
+ public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
42
+
43
+ [DllImport("user32.dll")]
44
+ public static extern int GetSystemMetrics(int nIndex);
45
+
46
+ [DllImport("user32.dll")]
47
+ public static extern bool SetProcessDPIAware();
48
+
49
+ [DllImport("user32.dll")]
50
+ public static extern IntPtr GetForegroundWindow();
51
+
52
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
53
+ public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
54
+
55
+ [DllImport("user32.dll")]
56
+ public static extern bool SetForegroundWindow(IntPtr hWnd);
57
+
58
+ [DllImport("user32.dll")]
59
+ public static extern bool BringWindowToTop(IntPtr hWnd);
60
+
61
+ [DllImport("user32.dll")]
62
+ public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
63
+
64
+ [DllImport("user32.dll")]
65
+ public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
66
+ public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
67
+
68
+ [DllImport("user32.dll")]
69
+ [return: MarshalAs(UnmanagedType.Bool)]
70
+ public static extern bool IsWindowVisible(IntPtr hWnd);
71
+
72
+ [DllImport("user32.dll")]
73
+ public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
74
+
75
+ [DllImport("user32.dll")]
76
+ [return: MarshalAs(UnmanagedType.Bool)]
77
+ public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
78
+
79
+ [DllImport("user32.dll")]
80
+ public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
81
+
82
+ [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
83
+ public static extern bool CreateProcess(
84
+ string lpApplicationName,
85
+ string lpCommandLine,
86
+ IntPtr lpProcessAttributes,
87
+ IntPtr lpThreadAttributes,
88
+ bool bInheritHandles,
89
+ uint dwCreationFlags,
90
+ IntPtr lpEnvironment,
91
+ string lpCurrentDirectory,
92
+ ref STARTUPINFO lpStartupInfo,
93
+ out PROCESS_INFORMATION lpProcessInformation
94
+ );
95
+
96
+ public const uint MOUSEEVENTF_MOVE = 0x0001;
97
+ public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
98
+ public const uint MOUSEEVENTF_LEFTUP = 0x0004;
99
+ public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
100
+ public const uint MOUSEEVENTF_RIGHTUP = 0x0010;
101
+ public const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020;
102
+ public const uint MOUSEEVENTF_MIDDLEUP = 0x0040;
103
+ public const uint MOUSEEVENTF_WHEEL = 0x0800;
104
+
105
+ public const int SM_CXSCREEN = 0;
106
+ public const int SM_CYSCREEN = 1;
107
+ public const int SM_CXVIRTUALSCREEN = 78;
108
+ public const int SM_CYVIRTUALSCREEN = 79;
109
+ public const int SW_RESTORE = 9;
110
+
111
+ [StructLayout(LayoutKind.Sequential)]
112
+ public struct RECT
113
+ {
114
+ public int Left;
115
+ public int Top;
116
+ public int Right;
117
+ public int Bottom;
118
+ }
119
+
120
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
121
+ public struct STARTUPINFO
122
+ {
123
+ public int cb;
124
+ public string lpReserved;
125
+ public string lpDesktop;
126
+ public string lpTitle;
127
+ public int dwX;
128
+ public int dwY;
129
+ public int dwXSize;
130
+ public int dwYSize;
131
+ public int dwXCountChars;
132
+ public int dwYCountChars;
133
+ public int dwFillAttribute;
134
+ public int dwFlags;
135
+ public short wShowWindow;
136
+ public short cbReserved2;
137
+ public IntPtr lpReserved2;
138
+ public IntPtr hStdInput;
139
+ public IntPtr hStdOutput;
140
+ public IntPtr hStdError;
141
+ }
142
+
143
+ [StructLayout(LayoutKind.Sequential)]
144
+ public struct PROCESS_INFORMATION
145
+ {
146
+ public IntPtr hProcess;
147
+ public IntPtr hThread;
148
+ public int dwProcessId;
149
+ public int dwThreadId;
150
+ }
151
+
152
+ [StructLayout(LayoutKind.Sequential)]
153
+ public struct POINT
154
+ {
155
+ public int X;
156
+ public int Y;
157
+ }
158
+
159
+ public static void AttachInputDesktop()
160
+ {
161
+ try
162
+ {
163
+ SetProcessDPIAware();
164
+ IntPtr hwinsta = OpenWindowStation("WinSta0", false, 0x10000000);
165
+ if (hwinsta != IntPtr.Zero)
166
+ {
167
+ SetProcessWindowStation(hwinsta);
168
+ }
169
+ IntPtr hdesk = OpenInputDesktop(0, false, 0x10000000);
170
+ if (hdesk == IntPtr.Zero)
171
+ {
172
+ hdesk = OpenDesktop("Default", 0, false, 0x10000000);
173
+ }
174
+ if (hdesk != IntPtr.Zero)
175
+ {
176
+ SetThreadDesktop(hdesk);
177
+ }
178
+ }
179
+ catch { }
180
+ }
181
+
182
+ public static POINT GetPosition()
183
+ {
184
+ POINT pt = new POINT();
185
+ GetCursorPos(out pt);
186
+ return pt;
187
+ }
188
+
189
+ public static bool Move(int x, int y)
190
+ {
191
+ SetCursorPos(x, y);
192
+ Thread.Sleep(20);
193
+ POINT actual = GetPosition();
194
+ return (Math.Abs(actual.X - x) <= 5 && Math.Abs(actual.Y - y) <= 5);
195
+ }
196
+
197
+ public static bool Click(string button, int x, int y, int count)
198
+ {
199
+ Move(x, y);
200
+ Thread.Sleep(30);
201
+
202
+ uint downFlag = MOUSEEVENTF_LEFTDOWN;
203
+ uint upFlag = MOUSEEVENTF_LEFTUP;
204
+
205
+ if (button == "right")
206
+ {
207
+ downFlag = MOUSEEVENTF_RIGHTDOWN;
208
+ upFlag = MOUSEEVENTF_RIGHTUP;
209
+ }
210
+ else if (button == "middle")
211
+ {
212
+ downFlag = MOUSEEVENTF_MIDDLEDOWN;
213
+ upFlag = MOUSEEVENTF_MIDDLEUP;
214
+ }
215
+
216
+ for (int i = 0; i < count; i++)
217
+ {
218
+ mouse_event(downFlag, 0, 0, 0, 0);
219
+ Thread.Sleep(40);
220
+ mouse_event(upFlag, 0, 0, 0, 0);
221
+ if (count > 1 && i < count - 1)
222
+ {
223
+ Thread.Sleep(70);
224
+ }
225
+ }
226
+ return true;
227
+ }
228
+
229
+ public static bool MouseDown(string button, int x, int y)
230
+ {
231
+ if (x >= 0 && y >= 0)
232
+ {
233
+ Move(x, y);
234
+ Thread.Sleep(30);
235
+ }
236
+ uint downFlag = MOUSEEVENTF_LEFTDOWN;
237
+ if (button == "right") downFlag = MOUSEEVENTF_RIGHTDOWN;
238
+ else if (button == "middle") downFlag = MOUSEEVENTF_MIDDLEDOWN;
239
+
240
+ mouse_event(downFlag, 0, 0, 0, 0);
241
+ return true;
242
+ }
243
+
244
+ public static bool MouseUp(string button, int x, int y)
245
+ {
246
+ if (x >= 0 && y >= 0)
247
+ {
248
+ Move(x, y);
249
+ Thread.Sleep(30);
250
+ }
251
+ uint upFlag = MOUSEEVENTF_LEFTUP;
252
+ if (button == "right") upFlag = MOUSEEVENTF_RIGHTUP;
253
+ else if (button == "middle") upFlag = MOUSEEVENTF_MIDDLEUP;
254
+
255
+ mouse_event(upFlag, 0, 0, 0, 0);
256
+ return true;
257
+ }
258
+
259
+ public static bool Drag(int startX, int startY, int endX, int endY)
260
+ {
261
+ Move(startX, startY);
262
+ Thread.Sleep(50);
263
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
264
+ Thread.Sleep(50);
265
+
266
+ int steps = 15;
267
+ for (int i = 1; i <= steps; i++)
268
+ {
269
+ int curX = startX + (int)((endX - startX) * ((double)i / steps));
270
+ int curY = startY + (int)((endY - startY) * ((double)i / steps));
271
+ SetCursorPos(curX, curY);
272
+ Thread.Sleep(15);
273
+ }
274
+
275
+ Move(endX, endY);
276
+ Thread.Sleep(50);
277
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
278
+ Thread.Sleep(30);
279
+ return true;
280
+ }
281
+
282
+ public static bool Scroll(int deltaX, int deltaY)
283
+ {
284
+ if (deltaY != 0)
285
+ {
286
+ int amount = -deltaY * 120;
287
+ mouse_event(MOUSEEVENTF_WHEEL, 0, 0, (uint)amount, 0);
288
+ }
289
+ return true;
290
+ }
291
+
292
+ public static string GetActiveWindowTitle()
293
+ {
294
+ try
295
+ {
296
+ IntPtr hwnd = GetForegroundWindow();
297
+ if (hwnd == IntPtr.Zero) return "Desktop";
298
+ StringBuilder sb = new StringBuilder(512);
299
+ GetWindowText(hwnd, sb, sb.Capacity);
300
+ string title = sb.ToString().Trim();
301
+ return string.IsNullOrEmpty(title) ? "Desktop" : title;
302
+ }
303
+ catch
304
+ {
305
+ return "Desktop";
306
+ }
307
+ }
308
+
309
+ public static List<string> GetVisibleWindows()
310
+ {
311
+ List<string> list = new List<string>();
312
+ try
313
+ {
314
+ EnumWindows((hWnd, lParam) =>
315
+ {
316
+ if (IsWindowVisible(hWnd))
317
+ {
318
+ StringBuilder sb = new StringBuilder(512);
319
+ GetWindowText(hWnd, sb, sb.Capacity);
320
+ string t = sb.ToString().Trim();
321
+ if (!string.IsNullOrEmpty(t) && t != "Default IME" && t != "MSCTFIME UI")
322
+ {
323
+ list.Add(t);
324
+ }
325
+ }
326
+ return true;
327
+ }, IntPtr.Zero);
328
+ }
329
+ catch { }
330
+ return list;
331
+ }
332
+
333
+ public static bool FocusWindow(string titleOrApp)
334
+ {
335
+ try
336
+ {
337
+ string target = titleOrApp.ToLower().Trim();
338
+ IntPtr foundHwnd = IntPtr.Zero;
339
+
340
+ EnumWindows((hWnd, lParam) =>
341
+ {
342
+ if (IsWindowVisible(hWnd))
343
+ {
344
+ StringBuilder sb = new StringBuilder(512);
345
+ GetWindowText(hWnd, sb, sb.Capacity);
346
+ string t = sb.ToString().Trim().ToLower();
347
+
348
+ uint pid;
349
+ GetWindowThreadProcessId(hWnd, out pid);
350
+ string pName = "";
351
+ try { pName = Process.GetProcessById((int)pid).ProcessName.ToLower(); } catch { }
352
+
353
+ if (t.Contains(target) || pName.Contains(target))
354
+ {
355
+ foundHwnd = hWnd;
356
+ return false;
357
+ }
358
+ }
359
+ return true;
360
+ }, IntPtr.Zero);
361
+
362
+ if (foundHwnd != IntPtr.Zero)
363
+ {
364
+ ShowWindow(foundHwnd, SW_RESTORE);
365
+ keybd_event(0x12, 0, 0, UIntPtr.Zero);
366
+ keybd_event(0x12, 0, 0x0002, UIntPtr.Zero);
367
+ SetForegroundWindow(foundHwnd);
368
+ BringWindowToTop(foundHwnd);
369
+ Thread.Sleep(150);
370
+ return true;
371
+ }
372
+ }
373
+ catch { }
374
+ return false;
375
+ }
376
+
377
+ public static RECT? GetWindowBounds(string titleOrApp)
378
+ {
379
+ try
380
+ {
381
+ string target = titleOrApp.ToLower().Trim();
382
+ IntPtr foundHwnd = IntPtr.Zero;
383
+
384
+ EnumWindows((hWnd, lParam) =>
385
+ {
386
+ if (IsWindowVisible(hWnd))
387
+ {
388
+ StringBuilder sb = new StringBuilder(512);
389
+ GetWindowText(hWnd, sb, sb.Capacity);
390
+ string t = sb.ToString().Trim().ToLower();
391
+
392
+ uint pid;
393
+ GetWindowThreadProcessId(hWnd, out pid);
394
+ string pName = "";
395
+ try { pName = Process.GetProcessById((int)pid).ProcessName.ToLower(); } catch { }
396
+
397
+ if (t.Contains(target) || pName.Contains(target))
398
+ {
399
+ foundHwnd = hWnd;
400
+ return false;
401
+ }
402
+ }
403
+ return true;
404
+ }, IntPtr.Zero);
405
+
406
+ if (foundHwnd != IntPtr.Zero)
407
+ {
408
+ RECT rect;
409
+ if (GetWindowRect(foundHwnd, out rect))
410
+ {
411
+ return rect;
412
+ }
413
+ }
414
+ }
415
+ catch { }
416
+ return null;
417
+ }
418
+
419
+ public static bool LaunchApp(string appName, out int pid)
420
+ {
421
+ pid = 0;
422
+ try
423
+ {
424
+ STARTUPINFO si = new STARTUPINFO();
425
+ si.cb = Marshal.SizeOf(si);
426
+ si.lpDesktop = @"WinSta0\Default";
427
+ PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
428
+
429
+ string cmd = appName;
430
+ if (!cmd.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
431
+ {
432
+ cmd += ".exe";
433
+ }
434
+
435
+ bool ok = CreateProcess(null, cmd, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi);
436
+ if (ok)
437
+ {
438
+ pid = pi.dwProcessId;
439
+ return true;
440
+ }
441
+ }
442
+ catch { }
443
+ return false;
444
+ }
445
+
446
+ public static string Screenshot(string savePath)
447
+ {
448
+ int width = GetSystemMetrics(SM_CXSCREEN);
449
+ int height = GetSystemMetrics(SM_CYSCREEN);
450
+ if (width <= 0) width = 1920;
451
+ if (height <= 0) height = 1080;
452
+
453
+ string dir = Path.GetDirectoryName(savePath);
454
+ if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
455
+ {
456
+ Directory.CreateDirectory(dir);
457
+ }
458
+
459
+ using (Bitmap bmp = new Bitmap(width, height))
460
+ {
461
+ using (Graphics g = Graphics.FromImage(bmp))
462
+ {
463
+ g.CopyFromScreen(0, 0, 0, 0, new Size(width, height), CopyPixelOperation.SourceCopy);
464
+ }
465
+ bmp.Save(savePath, ImageFormat.Png);
466
+ }
467
+
468
+ return string.Format("{{\"success\":true,\"action\":\"screenshot\",\"width\":{0},\"height\":{1},\"path\":{2},\"activeWindow\":{3}}}",
469
+ width, height, JsonEscape(savePath), JsonEscape(GetActiveWindowTitle()));
470
+ }
471
+
472
+ public static bool TypeText(string text)
473
+ {
474
+ if (string.IsNullOrEmpty(text)) return true;
475
+ try
476
+ {
477
+ SendKeys.SendWait(EscapeForSendKeys(text));
478
+ return true;
479
+ }
480
+ catch
481
+ {
482
+ return false;
483
+ }
484
+ }
485
+
486
+ public static bool KeyPress(string key)
487
+ {
488
+ string k = key.ToLower().Trim();
489
+ string sendStr = key;
490
+ if (k == "enter" || k == "return") sendStr = "{ENTER}";
491
+ else if (k == "tab") sendStr = "{TAB}";
492
+ else if (k == "esc" || k == "escape") sendStr = "{ESC}";
493
+ else if (k == "backspace") sendStr = "{BACKSPACE}";
494
+ else if (k == "del" || k == "delete") sendStr = "{DEL}";
495
+ else if (k == "up") sendStr = "{UP}";
496
+ else if (k == "down") sendStr = "{DOWN}";
497
+ else if (k == "left") sendStr = "{LEFT}";
498
+ else if (k == "right") sendStr = "{RIGHT}";
499
+ else if (k == "space") sendStr = " ";
500
+ else if (k == "home") sendStr = "{HOME}";
501
+ else if (k == "end") sendStr = "{END}";
502
+ else if (k == "pageup") sendStr = "{PGUP}";
503
+ else if (k == "pagedown") sendStr = "{PGDN}";
504
+
505
+ SendKeys.SendWait(sendStr);
506
+ return true;
507
+ }
508
+
509
+ public static bool Hotkey(string[] keys)
510
+ {
511
+ string prefix = "";
512
+ string mainKey = "";
513
+ foreach (string k in keys)
514
+ {
515
+ string lk = k.ToLower().Trim();
516
+ if (lk == "ctrl" || lk == "control") prefix += "^";
517
+ else if (lk == "alt") prefix += "%";
518
+ else if (lk == "shift") prefix += "+";
519
+ else mainKey = k;
520
+ }
521
+ string sendStr = prefix + mainKey.ToLower();
522
+ SendKeys.SendWait(sendStr);
523
+ return true;
524
+ }
525
+
526
+ public static string EscapeForSendKeys(string text)
527
+ {
528
+ StringBuilder sb = new StringBuilder();
529
+ foreach (char c in text)
530
+ {
531
+ if (c == '+' || c == '^' || c == '%' || c == '~' || c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']')
532
+ {
533
+ sb.Append("{" + c + "}");
534
+ }
535
+ else
536
+ {
537
+ sb.Append(c);
538
+ }
539
+ }
540
+ return sb.ToString();
541
+ }
542
+
543
+ public static string JsonEscape(string text)
544
+ {
545
+ if (text == null) return "null";
546
+ StringBuilder sb = new StringBuilder();
547
+ sb.Append("\"");
548
+ foreach (char c in text)
549
+ {
550
+ if (c == '\\') sb.Append("\\\\");
551
+ else if (c == '\"') sb.Append("\\\"");
552
+ else if (c == '\n') sb.Append("\\n");
553
+ else if (c == '\r') sb.Append("\\r");
554
+ else if (c == '\t') sb.Append("\\t");
555
+ else if (c < ' ') sb.Append(string.Format("\\u{0:x4}", (int)c));
556
+ else sb.Append(c);
557
+ }
558
+ sb.Append("\"");
559
+ return sb.ToString();
560
+ }
561
+
562
+ public static string ExecuteAction(string action, string[] args)
563
+ {
564
+ try
565
+ {
566
+ string act = action.ToLower().Trim();
567
+
568
+ if (act == "move")
569
+ {
570
+ int x = int.Parse(args[0]);
571
+ int y = int.Parse(args[1]);
572
+ bool ok = Move(x, y);
573
+ POINT pos = GetPosition();
574
+ return string.Format("{{\"success\":{0},\"action\":\"move\",\"coordinates\":{{\"x\":{1},\"y\":{2}}},\"activeWindow\":{3}}}",
575
+ ok ? "true" : "false", pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
576
+ }
577
+
578
+ if (act == "click" || act == "left_click")
579
+ {
580
+ int x = int.Parse(args[0]);
581
+ int y = int.Parse(args[1]);
582
+ string btn = (args.Length > 2 && !string.IsNullOrEmpty(args[2])) ? args[2] : "left";
583
+ bool ok = Click(btn, x, y, 1);
584
+ POINT pos = GetPosition();
585
+ return string.Format("{{\"success\":{0},\"action\":\"click\",\"button\":\"{1}\",\"coordinates\":{{\"x\":{2},\"y\":{3}}},\"activeWindow\":{4}}}",
586
+ ok ? "true" : "false", btn, pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
587
+ }
588
+
589
+ if (act == "right_click")
590
+ {
591
+ int x = int.Parse(args[0]);
592
+ int y = int.Parse(args[1]);
593
+ bool ok = Click("right", x, y, 1);
594
+ POINT pos = GetPosition();
595
+ return string.Format("{{\"success\":{0},\"action\":\"right_click\",\"button\":\"right\",\"coordinates\":{{\"x\":{1},\"y\":{2}}},\"activeWindow\":{3}}}",
596
+ ok ? "true" : "false", pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
597
+ }
598
+
599
+ if (act == "double_click")
600
+ {
601
+ int x = int.Parse(args[0]);
602
+ int y = int.Parse(args[1]);
603
+ bool ok = Click("left", x, y, 2);
604
+ POINT pos = GetPosition();
605
+ return string.Format("{{\"success\":{0},\"action\":\"double_click\",\"coordinates\":{{\"x\":{1},\"y\":{2}}},\"activeWindow\":{3}}}",
606
+ ok ? "true" : "false", pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
607
+ }
608
+
609
+ if (act == "mouse_down")
610
+ {
611
+ int x = args.Length > 0 ? int.Parse(args[0]) : -1;
612
+ int y = args.Length > 1 ? int.Parse(args[1]) : -1;
613
+ string btn = (args.Length > 2 && !string.IsNullOrEmpty(args[2])) ? args[2] : "left";
614
+ bool ok = MouseDown(btn, x, y);
615
+ POINT pos = GetPosition();
616
+ return string.Format("{{\"success\":{0},\"action\":\"mouse_down\",\"button\":\"{1}\",\"coordinates\":{{\"x\":{2},\"y\":{3}}}}}",
617
+ ok ? "true" : "false", btn, pos.X, pos.Y);
618
+ }
619
+
620
+ if (act == "mouse_up")
621
+ {
622
+ int x = args.Length > 0 ? int.Parse(args[0]) : -1;
623
+ int y = args.Length > 1 ? int.Parse(args[1]) : -1;
624
+ string btn = (args.Length > 2 && !string.IsNullOrEmpty(args[2])) ? args[2] : "left";
625
+ bool ok = MouseUp(btn, x, y);
626
+ POINT pos = GetPosition();
627
+ return string.Format("{{\"success\":{0},\"action\":\"mouse_up\",\"button\":\"{1}\",\"coordinates\":{{\"x\":{2},\"y\":{3}}}}}",
628
+ ok ? "true" : "false", btn, pos.X, pos.Y);
629
+ }
630
+
631
+ if (act == "drag")
632
+ {
633
+ int sx = int.Parse(args[0]);
634
+ int sy = int.Parse(args[1]);
635
+ int ex = int.Parse(args[2]);
636
+ int ey = int.Parse(args[3]);
637
+ bool ok = Drag(sx, sy, ex, ey);
638
+ POINT pos = GetPosition();
639
+ return string.Format("{{\"success\":{0},\"action\":\"drag\",\"from\":{{\"x\":{1},\"y\":{2}}},\"to\":{{\"x\":{3},\"y\":{4}}},\"coordinates\":{{\"x\":{5},\"y\":{6}}}}}",
640
+ ok ? "true" : "false", sx, sy, ex, ey, pos.X, pos.Y);
641
+ }
642
+
643
+ if (act == "scroll")
644
+ {
645
+ int dx = args.Length > 0 ? int.Parse(args[0]) : 0;
646
+ int dy = args.Length > 1 ? int.Parse(args[1]) : 0;
647
+ bool ok = Scroll(dx, dy);
648
+ return string.Format("{{\"success\":{0},\"action\":\"scroll\",\"deltaX\":{1},\"deltaY\":{2}}}",
649
+ ok ? "true" : "false", dx, dy);
650
+ }
651
+
652
+ if (act == "position")
653
+ {
654
+ POINT pos = GetPosition();
655
+ return string.Format("{{\"success\":true,\"action\":\"position\",\"coordinates\":{{\"x\":{0},\"y\":{1}}}}}",
656
+ pos.X, pos.Y);
657
+ }
658
+
659
+ if (act == "screen_info")
660
+ {
661
+ int width = GetSystemMetrics(SM_CXSCREEN);
662
+ int height = GetSystemMetrics(SM_CYSCREEN);
663
+ POINT pos = GetPosition();
664
+ return string.Format("{{\"success\":true,\"width\":{0},\"height\":{1},\"cursor\":{{\"x\":{2},\"y\":{3}}},\"activeWindow\":{4}}}",
665
+ width, height, pos.X, pos.Y, JsonEscape(GetActiveWindowTitle()));
666
+ }
667
+
668
+ if (act == "screenshot")
669
+ {
670
+ string path = args.Length > 0 ? args[0] : Path.Combine(Directory.GetCurrentDirectory(), "screen.png");
671
+ return Screenshot(path);
672
+ }
673
+
674
+ if (act == "type")
675
+ {
676
+ string text = args.Length > 0 ? args[0] : "";
677
+ bool ok = TypeText(text);
678
+ return string.Format("{{\"success\":{0},\"action\":\"type\",\"chars\":{1},\"activeWindow\":{2}}}",
679
+ ok ? "true" : "false", text.Length, JsonEscape(GetActiveWindowTitle()));
680
+ }
681
+
682
+ if (act == "key" || act == "key_press")
683
+ {
684
+ string key = args.Length > 0 ? args[0] : "";
685
+ bool ok = KeyPress(key);
686
+ return string.Format("{{\"success\":{0},\"action\":\"key_press\",\"key\":{1},\"activeWindow\":{2}}}",
687
+ ok ? "true" : "false", JsonEscape(key), JsonEscape(GetActiveWindowTitle()));
688
+ }
689
+
690
+ if (act == "hotkey")
691
+ {
692
+ string[] keys = args.Length > 0 ? args[0].Split(new char[] { '+', ',' }, StringSplitOptions.RemoveEmptyEntries) : new string[0];
693
+ bool ok = Hotkey(keys);
694
+ return string.Format("{{\"success\":{0},\"action\":\"hotkey\",\"keys\":{1},\"activeWindow\":{2}}}",
695
+ ok ? "true" : "false", JsonEscape(string.Join("+", keys)), JsonEscape(GetActiveWindowTitle()));
696
+ }
697
+
698
+ if (act == "active_window")
699
+ {
700
+ string title = GetActiveWindowTitle();
701
+ return string.Format("{{\"success\":true,\"action\":\"active_window\",\"activeWindow\":{0}}}",
702
+ JsonEscape(title));
703
+ }
704
+
705
+ if (act == "list_windows")
706
+ {
707
+ List<string> wins = GetVisibleWindows();
708
+ StringBuilder sb = new StringBuilder();
709
+ sb.Append("[");
710
+ for (int i = 0; i < wins.Count; i++)
711
+ {
712
+ sb.Append(JsonEscape(wins[i]));
713
+ if (i < wins.Count - 1) sb.Append(",");
714
+ }
715
+ sb.Append("]");
716
+ return string.Format("{{\"success\":true,\"action\":\"list_windows\",\"activeWindow\":{0},\"windows\":{1}}}",
717
+ JsonEscape(GetActiveWindowTitle()), sb.ToString());
718
+ }
719
+
720
+ if (act == "focus_window")
721
+ {
722
+ string win = args.Length > 0 ? args[0] : "";
723
+ bool ok = FocusWindow(win);
724
+ return string.Format("{{\"success\":{0},\"action\":\"focus_window\",\"target\":{1},\"activeWindow\":{2}}}",
725
+ ok ? "true" : "false", JsonEscape(win), JsonEscape(GetActiveWindowTitle()));
726
+ }
727
+
728
+ if (act == "launch" || act == "open_app")
729
+ {
730
+ string app = args.Length > 0 ? args[0] : "";
731
+ int pid;
732
+ bool ok = LaunchApp(app, out pid);
733
+ return string.Format("{{\"success\":{0},\"action\":\"launch\",\"app\":{1},\"pid\":{2}}}",
734
+ ok ? "true" : "false", JsonEscape(app), pid);
735
+ }
736
+
737
+ if (act == "window_bounds")
738
+ {
739
+ string win = args.Length > 0 ? args[0] : "";
740
+ RECT? r = GetWindowBounds(win);
741
+ if (r.HasValue)
742
+ {
743
+ int w = r.Value.Right - r.Value.Left;
744
+ int h = r.Value.Bottom - r.Value.Top;
745
+ return string.Format("{{\"success\":true,\"action\":\"window_bounds\",\"x\":{0},\"y\":{1},\"width\":{2},\"height\":{3}}}",
746
+ r.Value.Left, r.Value.Top, w, h);
747
+ }
748
+ return string.Format("{{\"success\":false,\"action\":\"window_bounds\",\"error\":{0}}}", JsonEscape("Window not found: " + win));
749
+ }
750
+
751
+ if (act == "diagnostic")
752
+ {
753
+ int width = GetSystemMetrics(SM_CXSCREEN);
754
+ int height = GetSystemMetrics(SM_CYSCREEN);
755
+ POINT pos = GetPosition();
756
+ string title = GetActiveWindowTitle();
757
+ List<string> wins = GetVisibleWindows();
758
+
759
+ return string.Format("{{\"success\":true,\"action\":\"diagnostic\",\"width\":{0},\"height\":{1},\"mouse\":{{\"x\":{2},\"y\":{3}}},\"activeWindow\":{4},\"windowCount\":{5}}}",
760
+ width, height, pos.X, pos.Y, JsonEscape(title), wins.Count);
761
+ }
762
+
763
+ return string.Format("{{\"success\":false,\"error\":{0},\"recoverable\":true}}",
764
+ JsonEscape("Unknown action: " + action));
765
+ }
766
+ catch (Exception ex)
767
+ {
768
+ return string.Format("{{\"success\":false,\"action\":{0},\"error\":{1},\"recoverable\":true}}",
769
+ JsonEscape(action), JsonEscape(ex.Message));
770
+ }
771
+ }
772
+
773
+ public static void RunServer()
774
+ {
775
+ AttachInputDesktop();
776
+ string line;
777
+ while ((line = Console.ReadLine()) != null)
778
+ {
779
+ line = line.Trim();
780
+ if (string.IsNullOrEmpty(line)) continue;
781
+ if (line == "exit" || line == "quit") break;
782
+
783
+ // Simple format: action arg1 arg2 ... or base64
784
+ string result = "";
785
+ try
786
+ {
787
+ string[] parts = ParseCommandLine(line);
788
+ if (parts.Length > 0)
789
+ {
790
+ string action = parts[0];
791
+ string[] args = new string[parts.Length - 1];
792
+ Array.Copy(parts, 1, args, 0, args.Length);
793
+ result = ExecuteAction(action, args);
794
+ }
795
+ else
796
+ {
797
+ result = "{\"success\":false,\"error\":\"Empty command\"}";
798
+ }
799
+ }
800
+ catch (Exception ex)
801
+ {
802
+ result = string.Format("{{\"success\":false,\"error\":{0}}}", JsonEscape(ex.Message));
803
+ }
804
+ Console.WriteLine(result);
805
+ Console.Out.Flush();
806
+ }
807
+ }
808
+
809
+ private static string[] ParseCommandLine(string cmdLine)
810
+ {
811
+ List<string> args = new List<string>();
812
+ bool inQuotes = false;
813
+ StringBuilder current = new StringBuilder();
814
+
815
+ for (int i = 0; i < cmdLine.Length; i++)
816
+ {
817
+ char c = cmdLine[i];
818
+ if (c == '\"')
819
+ {
820
+ inQuotes = !inQuotes;
821
+ }
822
+ else if (c == ' ' && !inQuotes)
823
+ {
824
+ if (current.Length > 0)
825
+ {
826
+ args.Add(current.ToString());
827
+ current.Clear();
828
+ }
829
+ }
830
+ else
831
+ {
832
+ current.Append(c);
833
+ }
834
+ }
835
+ if (current.Length > 0)
836
+ {
837
+ args.Add(current.ToString());
838
+ }
839
+ return args.ToArray();
840
+ }
841
+
842
+ [STAThread]
843
+ public static void Main(string[] args)
844
+ {
845
+ // Thread execution with desktop attachment
846
+ Thread t = new Thread(() =>
847
+ {
848
+ AttachInputDesktop();
849
+
850
+ if (args.Length > 0 && args[0] == "--server")
851
+ {
852
+ RunServer();
853
+ return;
854
+ }
855
+
856
+ if (args.Length == 0)
857
+ {
858
+ Console.WriteLine(ExecuteAction("diagnostic", new string[0]));
859
+ return;
860
+ }
861
+
862
+ string action = args[0];
863
+ string[] actionArgs = new string[args.Length - 1];
864
+ Array.Copy(args, 1, actionArgs, 0, actionArgs.Length);
865
+ Console.WriteLine(ExecuteAction(action, actionArgs));
866
+ });
867
+
868
+ t.SetApartmentState(ApartmentState.STA);
869
+ t.Start();
870
+ t.Join();
871
+ }
872
+ }
873
+ }