copilot-liku-cli 0.0.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/ARCHITECTURE.md +411 -0
- package/CONFIGURATION.md +302 -0
- package/CONTRIBUTING.md +225 -0
- package/ELECTRON_README.md +121 -0
- package/INSTALLATION.md +350 -0
- package/LICENSE.md +1 -0
- package/PROJECT_STATUS.md +229 -0
- package/QUICKSTART.md +255 -0
- package/README.md +167 -0
- package/TESTING.md +274 -0
- package/package.json +61 -0
- package/scripts/start.js +30 -0
- package/src/assets/tray-icon.png +0 -0
- package/src/cli/commands/agent.js +327 -0
- package/src/cli/commands/click.js +108 -0
- package/src/cli/commands/drag.js +85 -0
- package/src/cli/commands/find.js +109 -0
- package/src/cli/commands/keys.js +132 -0
- package/src/cli/commands/mouse.js +79 -0
- package/src/cli/commands/repl.js +290 -0
- package/src/cli/commands/screenshot.js +72 -0
- package/src/cli/commands/scroll.js +74 -0
- package/src/cli/commands/start.js +67 -0
- package/src/cli/commands/type.js +57 -0
- package/src/cli/commands/wait.js +84 -0
- package/src/cli/commands/window.js +104 -0
- package/src/cli/liku.js +249 -0
- package/src/cli/util/output.js +174 -0
- package/src/main/agents/base-agent.js +410 -0
- package/src/main/agents/builder.js +484 -0
- package/src/main/agents/index.js +62 -0
- package/src/main/agents/orchestrator.js +362 -0
- package/src/main/agents/researcher.js +511 -0
- package/src/main/agents/state-manager.js +344 -0
- package/src/main/agents/supervisor.js +365 -0
- package/src/main/agents/verifier.js +452 -0
- package/src/main/ai-service.js +1633 -0
- package/src/main/index.js +2208 -0
- package/src/main/inspect-service.js +467 -0
- package/src/main/system-automation.js +1186 -0
- package/src/main/ui-automation/config.js +76 -0
- package/src/main/ui-automation/core/helpers.js +41 -0
- package/src/main/ui-automation/core/index.js +15 -0
- package/src/main/ui-automation/core/powershell.js +82 -0
- package/src/main/ui-automation/elements/finder.js +274 -0
- package/src/main/ui-automation/elements/index.js +14 -0
- package/src/main/ui-automation/elements/wait.js +66 -0
- package/src/main/ui-automation/index.js +164 -0
- package/src/main/ui-automation/interactions/element-click.js +211 -0
- package/src/main/ui-automation/interactions/high-level.js +230 -0
- package/src/main/ui-automation/interactions/index.js +47 -0
- package/src/main/ui-automation/keyboard/index.js +15 -0
- package/src/main/ui-automation/keyboard/input.js +179 -0
- package/src/main/ui-automation/mouse/click.js +186 -0
- package/src/main/ui-automation/mouse/drag.js +88 -0
- package/src/main/ui-automation/mouse/index.js +30 -0
- package/src/main/ui-automation/mouse/movement.js +51 -0
- package/src/main/ui-automation/mouse/scroll.js +116 -0
- package/src/main/ui-automation/screenshot.js +183 -0
- package/src/main/ui-automation/window/index.js +23 -0
- package/src/main/ui-automation/window/manager.js +305 -0
- package/src/main/utils/time.js +62 -0
- package/src/main/visual-awareness.js +597 -0
- package/src/renderer/chat/chat.js +671 -0
- package/src/renderer/chat/index.html +725 -0
- package/src/renderer/chat/preload.js +112 -0
- package/src/renderer/overlay/index.html +648 -0
- package/src/renderer/overlay/overlay.js +782 -0
- package/src/renderer/overlay/preload.js +90 -0
- package/src/shared/grid-math.js +82 -0
- package/src/shared/inspect-types.js +230 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard Input Module
|
|
3
|
+
*
|
|
4
|
+
* Type text and send key combinations.
|
|
5
|
+
* @module ui-automation/keyboard
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { executePowerShellScript } = require('../core/powershell');
|
|
9
|
+
const { log, sleep } = require('../core/helpers');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Type text character by character
|
|
13
|
+
*
|
|
14
|
+
* @param {string} text - Text to type
|
|
15
|
+
* @param {Object} [options] - Type options
|
|
16
|
+
* @param {number} [options.delay=50] - Delay between characters in ms
|
|
17
|
+
* @returns {Promise<{success: boolean}>}
|
|
18
|
+
*/
|
|
19
|
+
async function typeText(text, options = {}) {
|
|
20
|
+
const { delay = 50 } = options;
|
|
21
|
+
|
|
22
|
+
// Escape special chars for PowerShell
|
|
23
|
+
const escapedText = text
|
|
24
|
+
.replace(/\\/g, '\\\\')
|
|
25
|
+
.replace(/'/g, "''")
|
|
26
|
+
.replace(/`/g, '``');
|
|
27
|
+
|
|
28
|
+
const psScript = `
|
|
29
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
30
|
+
$text = '${escapedText}'
|
|
31
|
+
foreach ($char in $text.ToCharArray()) {
|
|
32
|
+
[System.Windows.Forms.SendKeys]::SendWait($char)
|
|
33
|
+
Start-Sleep -Milliseconds ${delay}
|
|
34
|
+
}
|
|
35
|
+
Write-Output "typed"
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const result = await executePowerShellScript(psScript);
|
|
39
|
+
const success = result.stdout.includes('typed');
|
|
40
|
+
log(`TypeText "${text.substring(0, 20)}${text.length > 20 ? '...' : ''}" - ${success ? 'success' : 'failed'}`);
|
|
41
|
+
|
|
42
|
+
return { success };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Send keyboard shortcut or key combination
|
|
47
|
+
*
|
|
48
|
+
* Uses SendKeys format:
|
|
49
|
+
* - ^ = Ctrl
|
|
50
|
+
* - % = Alt
|
|
51
|
+
* - + = Shift
|
|
52
|
+
* - {ENTER}, {TAB}, {ESC}, {DELETE}, {BACKSPACE}
|
|
53
|
+
* - {F1}-{F12}
|
|
54
|
+
* - {UP}, {DOWN}, {LEFT}, {RIGHT}
|
|
55
|
+
* - {HOME}, {END}, {PGUP}, {PGDN}
|
|
56
|
+
*
|
|
57
|
+
* @param {string} keys - Key combination in SendKeys format
|
|
58
|
+
* @returns {Promise<{success: boolean}>}
|
|
59
|
+
*/
|
|
60
|
+
async function sendKeys(keys) {
|
|
61
|
+
const psScript = `
|
|
62
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
63
|
+
[System.Windows.Forms.SendKeys]::SendWait('${keys.replace(/'/g, "''")}')
|
|
64
|
+
Write-Output "sent"
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const result = await executePowerShellScript(psScript);
|
|
68
|
+
const success = result.stdout.includes('sent');
|
|
69
|
+
log(`SendKeys "${keys}" - ${success ? 'success' : 'failed'}`);
|
|
70
|
+
|
|
71
|
+
return { success };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Press a key down (for holding modifiers)
|
|
76
|
+
*
|
|
77
|
+
* @param {number} vkCode - Virtual key code
|
|
78
|
+
* @returns {Promise<{success: boolean}>}
|
|
79
|
+
*/
|
|
80
|
+
async function keyDown(vkCode) {
|
|
81
|
+
const psScript = `
|
|
82
|
+
Add-Type -TypeDefinition @'
|
|
83
|
+
using System;
|
|
84
|
+
using System.Runtime.InteropServices;
|
|
85
|
+
|
|
86
|
+
public class KeyboardHelper {
|
|
87
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
88
|
+
public struct INPUT { public uint type; public KEYBDINPUT ki; ulong padding; }
|
|
89
|
+
|
|
90
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
91
|
+
public struct KEYBDINPUT {
|
|
92
|
+
public ushort wVk, wScan; public uint dwFlags, time; public IntPtr dwExtraInfo;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
[DllImport("user32.dll")]
|
|
96
|
+
public static extern uint SendInput(uint n, INPUT[] inputs, int size);
|
|
97
|
+
|
|
98
|
+
public static void KeyDown(ushort vk) {
|
|
99
|
+
var inp = new INPUT { type = 1, ki = new KEYBDINPUT { wVk = vk } };
|
|
100
|
+
SendInput(1, new[] { inp }, Marshal.SizeOf(typeof(INPUT)));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
'@
|
|
104
|
+
[KeyboardHelper]::KeyDown(${vkCode})
|
|
105
|
+
Write-Output "down"
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
const result = await executePowerShellScript(psScript);
|
|
109
|
+
return { success: result.stdout.includes('down') };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Release a key
|
|
114
|
+
*
|
|
115
|
+
* @param {number} vkCode - Virtual key code
|
|
116
|
+
* @returns {Promise<{success: boolean}>}
|
|
117
|
+
*/
|
|
118
|
+
async function keyUp(vkCode) {
|
|
119
|
+
const psScript = `
|
|
120
|
+
Add-Type -TypeDefinition @'
|
|
121
|
+
using System;
|
|
122
|
+
using System.Runtime.InteropServices;
|
|
123
|
+
|
|
124
|
+
public class KeyboardHelper {
|
|
125
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
126
|
+
public struct INPUT { public uint type; public KEYBDINPUT ki; ulong padding; }
|
|
127
|
+
|
|
128
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
129
|
+
public struct KEYBDINPUT {
|
|
130
|
+
public ushort wVk, wScan; public uint dwFlags, time; public IntPtr dwExtraInfo;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
[DllImport("user32.dll")]
|
|
134
|
+
public static extern uint SendInput(uint n, INPUT[] inputs, int size);
|
|
135
|
+
|
|
136
|
+
public static void KeyUp(ushort vk) {
|
|
137
|
+
var inp = new INPUT { type = 1, ki = new KEYBDINPUT { wVk = vk, dwFlags = 0x0002 } };
|
|
138
|
+
SendInput(1, new[] { inp }, Marshal.SizeOf(typeof(INPUT)));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
'@
|
|
142
|
+
[KeyboardHelper]::KeyUp(${vkCode})
|
|
143
|
+
Write-Output "up"
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
const result = await executePowerShellScript(psScript);
|
|
147
|
+
return { success: result.stdout.includes('up') };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Common virtual key codes
|
|
152
|
+
*/
|
|
153
|
+
const VK = {
|
|
154
|
+
SHIFT: 0x10,
|
|
155
|
+
CTRL: 0x11,
|
|
156
|
+
ALT: 0x12,
|
|
157
|
+
ENTER: 0x0D,
|
|
158
|
+
TAB: 0x09,
|
|
159
|
+
ESC: 0x1B,
|
|
160
|
+
SPACE: 0x20,
|
|
161
|
+
BACKSPACE: 0x08,
|
|
162
|
+
DELETE: 0x2E,
|
|
163
|
+
LEFT: 0x25,
|
|
164
|
+
UP: 0x26,
|
|
165
|
+
RIGHT: 0x27,
|
|
166
|
+
DOWN: 0x28,
|
|
167
|
+
HOME: 0x24,
|
|
168
|
+
END: 0x23,
|
|
169
|
+
PAGEUP: 0x21,
|
|
170
|
+
PAGEDOWN: 0x22,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
module.exports = {
|
|
174
|
+
typeText,
|
|
175
|
+
sendKeys,
|
|
176
|
+
keyDown,
|
|
177
|
+
keyUp,
|
|
178
|
+
VK,
|
|
179
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mouse Click Operations
|
|
3
|
+
*
|
|
4
|
+
* Click and double-click with window focus handling.
|
|
5
|
+
* @module ui-automation/mouse/click
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { CONFIG } = require('../config');
|
|
9
|
+
const { executePowerShellScript } = require('../core/powershell');
|
|
10
|
+
const { sleep, log } = require('../core/helpers');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Click at coordinates using SendInput (most reliable)
|
|
14
|
+
*
|
|
15
|
+
* @param {number} x - X coordinate
|
|
16
|
+
* @param {number} y - Y coordinate
|
|
17
|
+
* @param {'left'|'right'|'middle'} [button='left'] - Mouse button
|
|
18
|
+
* @param {Object} [options] - Click options
|
|
19
|
+
* @param {boolean} [options.focusWindow=true] - Focus window before clicking
|
|
20
|
+
* @returns {Promise<{success: boolean, coordinates: {x: number, y: number}}>}
|
|
21
|
+
*/
|
|
22
|
+
async function clickAt(x, y, button = 'left', options = {}) {
|
|
23
|
+
x = Math.round(x);
|
|
24
|
+
y = Math.round(y);
|
|
25
|
+
const { focusWindow = true } = options;
|
|
26
|
+
|
|
27
|
+
const buttonFlags = {
|
|
28
|
+
left: { down: '0x0002', up: '0x0004' },
|
|
29
|
+
right: { down: '0x0008', up: '0x0010' },
|
|
30
|
+
middle: { down: '0x0020', up: '0x0040' },
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const flags = buttonFlags[button] || buttonFlags.left;
|
|
34
|
+
|
|
35
|
+
const psScript = `
|
|
36
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
37
|
+
Add-Type -TypeDefinition @'
|
|
38
|
+
using System;
|
|
39
|
+
using System.Runtime.InteropServices;
|
|
40
|
+
using System.Text;
|
|
41
|
+
|
|
42
|
+
public class MouseHelper {
|
|
43
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
44
|
+
public struct INPUT {
|
|
45
|
+
public uint type;
|
|
46
|
+
public MOUSEINPUT mi;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
50
|
+
public struct MOUSEINPUT {
|
|
51
|
+
public int dx;
|
|
52
|
+
public int dy;
|
|
53
|
+
public uint mouseData;
|
|
54
|
+
public uint dwFlags;
|
|
55
|
+
public uint time;
|
|
56
|
+
public IntPtr dwExtraInfo;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
60
|
+
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
|
61
|
+
|
|
62
|
+
[DllImport("user32.dll")]
|
|
63
|
+
public static extern IntPtr WindowFromPoint(int x, int y);
|
|
64
|
+
|
|
65
|
+
[DllImport("user32.dll")]
|
|
66
|
+
public static extern IntPtr GetAncestor(IntPtr hwnd, uint gaFlags);
|
|
67
|
+
|
|
68
|
+
[DllImport("user32.dll")]
|
|
69
|
+
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
70
|
+
|
|
71
|
+
[DllImport("user32.dll")]
|
|
72
|
+
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
|
73
|
+
|
|
74
|
+
[DllImport("user32.dll")]
|
|
75
|
+
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
|
|
76
|
+
|
|
77
|
+
[DllImport("kernel32.dll")]
|
|
78
|
+
public static extern uint GetCurrentThreadId();
|
|
79
|
+
|
|
80
|
+
[DllImport("user32.dll")]
|
|
81
|
+
public static extern IntPtr GetForegroundWindow();
|
|
82
|
+
|
|
83
|
+
[DllImport("user32.dll")]
|
|
84
|
+
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
|
85
|
+
|
|
86
|
+
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
|
87
|
+
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
|
88
|
+
|
|
89
|
+
public const int GWL_EXSTYLE = -20;
|
|
90
|
+
public const int WS_EX_LAYERED = 0x80000;
|
|
91
|
+
public const uint GA_ROOT = 2;
|
|
92
|
+
|
|
93
|
+
public static IntPtr GetRealWindow(int x, int y) {
|
|
94
|
+
IntPtr hwnd = WindowFromPoint(x, y);
|
|
95
|
+
if (hwnd == IntPtr.Zero) return IntPtr.Zero;
|
|
96
|
+
|
|
97
|
+
// Skip transparent overlays
|
|
98
|
+
for (int i = 0; i < 10; i++) {
|
|
99
|
+
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
|
100
|
+
bool isLayered = (exStyle & WS_EX_LAYERED) != 0;
|
|
101
|
+
|
|
102
|
+
StringBuilder sb = new StringBuilder(256);
|
|
103
|
+
GetWindowText(hwnd, sb, 256);
|
|
104
|
+
string title = sb.ToString();
|
|
105
|
+
|
|
106
|
+
// Skip layered windows with no title (likely overlays)
|
|
107
|
+
if (!isLayered || !string.IsNullOrEmpty(title)) {
|
|
108
|
+
return GetAncestor(hwnd, GA_ROOT);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
IntPtr parent = GetAncestor(hwnd, 1);
|
|
112
|
+
if (parent == IntPtr.Zero || parent == hwnd) break;
|
|
113
|
+
hwnd = parent;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return GetAncestor(hwnd, GA_ROOT);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public static void ForceForeground(IntPtr hwnd) {
|
|
120
|
+
IntPtr fg = GetForegroundWindow();
|
|
121
|
+
uint fgThread = GetWindowThreadProcessId(fg, IntPtr.Zero);
|
|
122
|
+
uint curThread = GetCurrentThreadId();
|
|
123
|
+
|
|
124
|
+
if (fgThread != curThread) {
|
|
125
|
+
AttachThreadInput(curThread, fgThread, true);
|
|
126
|
+
SetForegroundWindow(hwnd);
|
|
127
|
+
AttachThreadInput(curThread, fgThread, false);
|
|
128
|
+
} else {
|
|
129
|
+
SetForegroundWindow(hwnd);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public static void Click(uint downFlag, uint upFlag) {
|
|
134
|
+
var down = new INPUT { type = 0, mi = new MOUSEINPUT { dwFlags = downFlag } };
|
|
135
|
+
var up = new INPUT { type = 0, mi = new MOUSEINPUT { dwFlags = upFlag } };
|
|
136
|
+
SendInput(1, new[] { down }, Marshal.SizeOf(typeof(INPUT)));
|
|
137
|
+
System.Threading.Thread.Sleep(30);
|
|
138
|
+
SendInput(1, new[] { up }, Marshal.SizeOf(typeof(INPUT)));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
'@
|
|
142
|
+
|
|
143
|
+
# Move cursor
|
|
144
|
+
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${x}, ${y})
|
|
145
|
+
Start-Sleep -Milliseconds ${CONFIG.CLICK_DELAY}
|
|
146
|
+
|
|
147
|
+
${focusWindow ? `
|
|
148
|
+
# Focus the window under cursor
|
|
149
|
+
$hwnd = [MouseHelper]::GetRealWindow(${x}, ${y})
|
|
150
|
+
if ($hwnd -ne [IntPtr]::Zero) {
|
|
151
|
+
[MouseHelper]::ForceForeground($hwnd)
|
|
152
|
+
Start-Sleep -Milliseconds ${CONFIG.FOCUS_DELAY}
|
|
153
|
+
}
|
|
154
|
+
` : ''}
|
|
155
|
+
|
|
156
|
+
# Click
|
|
157
|
+
[MouseHelper]::Click(${flags.down}, ${flags.up})
|
|
158
|
+
Write-Output "clicked"
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
const result = await executePowerShellScript(psScript);
|
|
162
|
+
|
|
163
|
+
const success = result.stdout.includes('clicked');
|
|
164
|
+
log(`${button} click at (${x}, ${y}) - ${success ? 'success' : 'failed'}`);
|
|
165
|
+
|
|
166
|
+
return { success, coordinates: { x, y } };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Double-click at coordinates
|
|
171
|
+
*
|
|
172
|
+
* @param {number} x - X coordinate
|
|
173
|
+
* @param {number} y - Y coordinate
|
|
174
|
+
* @param {'left'|'right'} [button='left'] - Mouse button
|
|
175
|
+
* @returns {Promise<{success: boolean, coordinates: {x: number, y: number}}>}
|
|
176
|
+
*/
|
|
177
|
+
async function doubleClickAt(x, y, button = 'left') {
|
|
178
|
+
await clickAt(x, y, button);
|
|
179
|
+
await sleep(50);
|
|
180
|
+
return await clickAt(x, y, button);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
clickAt,
|
|
185
|
+
doubleClickAt,
|
|
186
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mouse Drag Operations
|
|
3
|
+
*
|
|
4
|
+
* Drag from one point to another.
|
|
5
|
+
* @module ui-automation/mouse/drag
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { executePowerShellScript } = require('../core/powershell');
|
|
9
|
+
const { log } = require('../core/helpers');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Drag from one point to another
|
|
13
|
+
*
|
|
14
|
+
* @param {number} fromX - Start X coordinate
|
|
15
|
+
* @param {number} fromY - Start Y coordinate
|
|
16
|
+
* @param {number} toX - End X coordinate
|
|
17
|
+
* @param {number} toY - End Y coordinate
|
|
18
|
+
* @param {Object} [options] - Drag options
|
|
19
|
+
* @param {number} [options.steps=10] - Number of intermediate steps
|
|
20
|
+
* @param {number} [options.stepDelay=10] - Delay between steps in ms
|
|
21
|
+
* @returns {Promise<{success: boolean}>}
|
|
22
|
+
*/
|
|
23
|
+
async function drag(fromX, fromY, toX, toY, options = {}) {
|
|
24
|
+
const { steps = 10, stepDelay = 10 } = options;
|
|
25
|
+
|
|
26
|
+
const psScript = `
|
|
27
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
28
|
+
Add-Type -TypeDefinition @'
|
|
29
|
+
using System;
|
|
30
|
+
using System.Runtime.InteropServices;
|
|
31
|
+
|
|
32
|
+
public class DragHelper {
|
|
33
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
34
|
+
public struct INPUT { public uint type; public MOUSEINPUT mi; }
|
|
35
|
+
|
|
36
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
37
|
+
public struct MOUSEINPUT {
|
|
38
|
+
public int dx, dy; public uint mouseData, dwFlags, time; public IntPtr dwExtraInfo;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[DllImport("user32.dll")]
|
|
42
|
+
public static extern uint SendInput(uint n, INPUT[] inputs, int size);
|
|
43
|
+
|
|
44
|
+
public static void MouseDown() {
|
|
45
|
+
var inp = new INPUT { type = 0, mi = new MOUSEINPUT { dwFlags = 0x0002 } };
|
|
46
|
+
SendInput(1, new[] { inp }, Marshal.SizeOf(typeof(INPUT)));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public static void MouseUp() {
|
|
50
|
+
var inp = new INPUT { type = 0, mi = new MOUSEINPUT { dwFlags = 0x0004 } };
|
|
51
|
+
SendInput(1, new[] { inp }, Marshal.SizeOf(typeof(INPUT)));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
'@
|
|
55
|
+
|
|
56
|
+
# Move to start
|
|
57
|
+
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${Math.round(fromX)}, ${Math.round(fromY)})
|
|
58
|
+
Start-Sleep -Milliseconds 50
|
|
59
|
+
|
|
60
|
+
# Press down
|
|
61
|
+
[DragHelper]::MouseDown()
|
|
62
|
+
Start-Sleep -Milliseconds 50
|
|
63
|
+
|
|
64
|
+
# Move in steps
|
|
65
|
+
$steps = ${steps}
|
|
66
|
+
for ($i = 1; $i -le $steps; $i++) {
|
|
67
|
+
$progress = $i / $steps
|
|
68
|
+
$x = [int](${Math.round(fromX)} + (${Math.round(toX)} - ${Math.round(fromX)}) * $progress)
|
|
69
|
+
$y = [int](${Math.round(fromY)} + (${Math.round(toY)} - ${Math.round(fromY)}) * $progress)
|
|
70
|
+
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point($x, $y)
|
|
71
|
+
Start-Sleep -Milliseconds ${stepDelay}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
# Release
|
|
75
|
+
[DragHelper]::MouseUp()
|
|
76
|
+
Write-Output "dragged"
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
const result = await executePowerShellScript(psScript);
|
|
80
|
+
const success = result.stdout.includes('dragged');
|
|
81
|
+
log(`Drag from (${fromX}, ${fromY}) to (${toX}, ${toY}) - ${success ? 'success' : 'failed'}`);
|
|
82
|
+
|
|
83
|
+
return { success };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
drag,
|
|
88
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mouse Operations Module
|
|
3
|
+
*
|
|
4
|
+
* @module ui-automation/mouse
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { moveMouse, getMousePosition } = require('./movement');
|
|
8
|
+
const { clickAt, doubleClickAt } = require('./click');
|
|
9
|
+
const { drag } = require('./drag');
|
|
10
|
+
const { scroll, scrollUp, scrollDown, scrollLeft, scrollRight } = require('./scroll');
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
// Movement
|
|
14
|
+
moveMouse,
|
|
15
|
+
getMousePosition,
|
|
16
|
+
|
|
17
|
+
// Clicks
|
|
18
|
+
clickAt,
|
|
19
|
+
doubleClickAt,
|
|
20
|
+
|
|
21
|
+
// Drag
|
|
22
|
+
drag,
|
|
23
|
+
|
|
24
|
+
// Scrolling
|
|
25
|
+
scroll,
|
|
26
|
+
scrollUp,
|
|
27
|
+
scrollDown,
|
|
28
|
+
scrollLeft,
|
|
29
|
+
scrollRight,
|
|
30
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mouse Movement
|
|
3
|
+
*
|
|
4
|
+
* Basic mouse position and movement functions.
|
|
5
|
+
* @module ui-automation/mouse/movement
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { executePowerShellScript } = require('../core/powershell');
|
|
9
|
+
const { log } = require('../core/helpers');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Move mouse to coordinates
|
|
13
|
+
*
|
|
14
|
+
* @param {number} x - X coordinate
|
|
15
|
+
* @param {number} y - Y coordinate
|
|
16
|
+
* @returns {Promise<void>}
|
|
17
|
+
*/
|
|
18
|
+
async function moveMouse(x, y) {
|
|
19
|
+
x = Math.round(x);
|
|
20
|
+
y = Math.round(y);
|
|
21
|
+
|
|
22
|
+
const script = `
|
|
23
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
24
|
+
[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${x}, ${y})
|
|
25
|
+
`;
|
|
26
|
+
await executePowerShellScript(script);
|
|
27
|
+
log(`Mouse moved to (${x}, ${y})`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get current mouse position
|
|
32
|
+
*
|
|
33
|
+
* @returns {Promise<{x: number, y: number}>}
|
|
34
|
+
*/
|
|
35
|
+
async function getMousePosition() {
|
|
36
|
+
const result = await executePowerShellScript(`
|
|
37
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
38
|
+
$pos = [System.Windows.Forms.Cursor]::Position
|
|
39
|
+
Write-Output "$($pos.X),$($pos.Y)"
|
|
40
|
+
`);
|
|
41
|
+
const output = (result.stdout || '').trim();
|
|
42
|
+
const parts = output.split(',');
|
|
43
|
+
const x = parseInt(parts[0], 10) || 0;
|
|
44
|
+
const y = parseInt(parts[1], 10) || 0;
|
|
45
|
+
return { x, y };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
moveMouse,
|
|
50
|
+
getMousePosition,
|
|
51
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mouse Scroll Operations
|
|
3
|
+
*
|
|
4
|
+
* Vertical and horizontal scrolling.
|
|
5
|
+
* @module ui-automation/mouse/scroll
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { executePowerShellScript } = require('../core/powershell');
|
|
9
|
+
const { log } = require('../core/helpers');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Scroll the mouse wheel
|
|
13
|
+
*
|
|
14
|
+
* @param {number} [amount=3] - Lines to scroll (positive = down, negative = up)
|
|
15
|
+
* @param {Object} [options] - Scroll options
|
|
16
|
+
* @param {number} [options.x] - X coordinate (current position if omitted)
|
|
17
|
+
* @param {number} [options.y] - Y coordinate (current position if omitted)
|
|
18
|
+
* @param {boolean} [options.horizontal=false] - Horizontal scroll instead of vertical
|
|
19
|
+
* @returns {Promise<{success: boolean}>}
|
|
20
|
+
*/
|
|
21
|
+
async function scroll(amount = 3, options = {}) {
|
|
22
|
+
const { x, y, horizontal = false } = options;
|
|
23
|
+
|
|
24
|
+
// WHEEL_DELTA = 120 per "click"
|
|
25
|
+
const wheelDelta = Math.round(amount * 120);
|
|
26
|
+
|
|
27
|
+
const psScript = `
|
|
28
|
+
Add-Type -TypeDefinition @'
|
|
29
|
+
using System;
|
|
30
|
+
using System.Runtime.InteropServices;
|
|
31
|
+
|
|
32
|
+
public class ScrollHelper {
|
|
33
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
34
|
+
public struct INPUT { public uint type; public MOUSEINPUT mi; }
|
|
35
|
+
|
|
36
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
37
|
+
public struct MOUSEINPUT {
|
|
38
|
+
public int dx, dy; public uint mouseData, dwFlags, time; public IntPtr dwExtraInfo;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[DllImport("user32.dll")]
|
|
42
|
+
public static extern uint SendInput(uint n, INPUT[] inputs, int size);
|
|
43
|
+
|
|
44
|
+
[DllImport("user32.dll")]
|
|
45
|
+
public static extern bool GetCursorPos(out System.Drawing.Point pt);
|
|
46
|
+
|
|
47
|
+
public static void Scroll(int delta, bool horizontal) {
|
|
48
|
+
// MOUSEEVENTF_WHEEL = 0x0800, MOUSEEVENTF_HWHEEL = 0x01000
|
|
49
|
+
uint flags = horizontal ? 0x01000u : 0x0800u;
|
|
50
|
+
var inp = new INPUT {
|
|
51
|
+
type = 0,
|
|
52
|
+
mi = new MOUSEINPUT { mouseData = (uint)delta, dwFlags = flags }
|
|
53
|
+
};
|
|
54
|
+
SendInput(1, new[] { inp }, Marshal.SizeOf(typeof(INPUT)));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
'@
|
|
58
|
+
|
|
59
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
60
|
+
|
|
61
|
+
${x !== undefined && y !== undefined ? `[System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${Math.round(x)}, ${Math.round(y)}); Start-Sleep -Milliseconds 50` : '# Use current position'}
|
|
62
|
+
|
|
63
|
+
[ScrollHelper]::Scroll(${-wheelDelta}, $${horizontal})
|
|
64
|
+
Write-Output "scrolled"
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const result = await executePowerShellScript(psScript);
|
|
68
|
+
const success = result.stdout.includes('scrolled');
|
|
69
|
+
log(`Scroll ${horizontal ? 'horizontal' : 'vertical'} amount=${amount} - ${success ? 'success' : 'failed'}`);
|
|
70
|
+
|
|
71
|
+
return { success };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Scroll up
|
|
76
|
+
* @param {number} [lines=3] - Lines to scroll
|
|
77
|
+
* @returns {Promise<{success: boolean}>}
|
|
78
|
+
*/
|
|
79
|
+
async function scrollUp(lines = 3) {
|
|
80
|
+
return scroll(-Math.abs(lines));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Scroll down
|
|
85
|
+
* @param {number} [lines=3] - Lines to scroll
|
|
86
|
+
* @returns {Promise<{success: boolean}>}
|
|
87
|
+
*/
|
|
88
|
+
async function scrollDown(lines = 3) {
|
|
89
|
+
return scroll(Math.abs(lines));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Scroll left
|
|
94
|
+
* @param {number} [amount=3] - Amount to scroll
|
|
95
|
+
* @returns {Promise<{success: boolean}>}
|
|
96
|
+
*/
|
|
97
|
+
async function scrollLeft(amount = 3) {
|
|
98
|
+
return scroll(-Math.abs(amount), { horizontal: true });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Scroll right
|
|
103
|
+
* @param {number} [amount=3] - Amount to scroll
|
|
104
|
+
* @returns {Promise<{success: boolean}>}
|
|
105
|
+
*/
|
|
106
|
+
async function scrollRight(amount = 3) {
|
|
107
|
+
return scroll(Math.abs(amount), { horizontal: true });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
scroll,
|
|
112
|
+
scrollUp,
|
|
113
|
+
scrollDown,
|
|
114
|
+
scrollLeft,
|
|
115
|
+
scrollRight,
|
|
116
|
+
};
|