stelo 1.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/LICENSE +184 -0
- package/README.md +853 -0
- package/dist/accessibility.d.ts +227 -0
- package/dist/accessibility.d.ts.map +1 -0
- package/dist/accessibility.js +602 -0
- package/dist/accessibility.js.map +1 -0
- package/dist/agent.d.ts +870 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +1107 -0
- package/dist/agent.js.map +1 -0
- package/dist/audio-stream.d.ts +114 -0
- package/dist/audio-stream.d.ts.map +1 -0
- package/dist/audio-stream.js +167 -0
- package/dist/audio-stream.js.map +1 -0
- package/dist/clipboard.d.ts +99 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +352 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/config.d.ts +183 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +477 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +213 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +387 -0
- package/dist/context.js.map +1 -0
- package/dist/cortex.d.ts +548 -0
- package/dist/cortex.d.ts.map +1 -0
- package/dist/cortex.js +1479 -0
- package/dist/cortex.js.map +1 -0
- package/dist/errors.d.ts +133 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +278 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +227 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +429 -0
- package/dist/events.js.map +1 -0
- package/dist/executor.d.ts +212 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +545 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +167 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +159 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +533 -0
- package/dist/integration.js.map +1 -0
- package/dist/keyboard.d.ts +276 -0
- package/dist/keyboard.d.ts.map +1 -0
- package/dist/keyboard.js +404 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/logger.d.ts +198 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +516 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware.d.ts +183 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +493 -0
- package/dist/middleware.js.map +1 -0
- package/dist/monitor.d.ts +136 -0
- package/dist/monitor.d.ts.map +1 -0
- package/dist/monitor.js +341 -0
- package/dist/monitor.js.map +1 -0
- package/dist/mouse.d.ts +290 -0
- package/dist/mouse.d.ts.map +1 -0
- package/dist/mouse.js +466 -0
- package/dist/mouse.js.map +1 -0
- package/dist/plugin.d.ts +157 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +409 -0
- package/dist/plugin.js.map +1 -0
- package/dist/process.d.ts +106 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +326 -0
- package/dist/process.js.map +1 -0
- package/dist/recorder.d.ts +100 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +258 -0
- package/dist/recorder.js.map +1 -0
- package/dist/safety.d.ts +59 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +98 -0
- package/dist/safety.js.map +1 -0
- package/dist/scheduler.d.ts +152 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +615 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/screen.d.ts +96 -0
- package/dist/screen.d.ts.map +1 -0
- package/dist/screen.js +154 -0
- package/dist/screen.js.map +1 -0
- package/dist/session.d.ts +209 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +479 -0
- package/dist/session.js.map +1 -0
- package/dist/stream.d.ts +168 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/stream.js +298 -0
- package/dist/stream.js.map +1 -0
- package/dist/telemetry.d.ts +223 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +433 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +165 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/bezier.d.ts +51 -0
- package/dist/utils/bezier.d.ts.map +1 -0
- package/dist/utils/bezier.js +117 -0
- package/dist/utils/bezier.js.map +1 -0
- package/dist/utils/helpers.d.ts +90 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +143 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/validation.d.ts +254 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +478 -0
- package/dist/validation.js.map +1 -0
- package/dist/vision.d.ts +719 -0
- package/dist/vision.d.ts.map +1 -0
- package/dist/vision.js +1197 -0
- package/dist/vision.js.map +1 -0
- package/dist/window.d.ts +80 -0
- package/dist/window.d.ts.map +1 -0
- package/dist/window.js +170 -0
- package/dist/window.js.map +1 -0
- package/dist/workflow.d.ts +224 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +578 -0
- package/dist/workflow.js.map +1 -0
- package/index.d.ts +840 -0
- package/index.js +495 -0
- package/package.json +91 -0
package/dist/agent.js
ADDED
|
@@ -0,0 +1,1107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Real-time Agent Control Module
|
|
4
|
+
*
|
|
5
|
+
* Designed for low-latency control of mouse and keyboard with:
|
|
6
|
+
* - Action batching (minimizes round-trips)
|
|
7
|
+
* - Visual verification (confirms actions had effect)
|
|
8
|
+
* - Async non-blocking movement (cursor moves visually in real-time)
|
|
9
|
+
* - Parallel action execution (multiple things at once)
|
|
10
|
+
* - Streaming cursor control (continuous, cancellable)
|
|
11
|
+
* - Velocity-based streaming control (continuous, low-latency)
|
|
12
|
+
* - Gesture recording and playback
|
|
13
|
+
*
|
|
14
|
+
* @module agent
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.agent = exports.ActionSequence = void 0;
|
|
18
|
+
exports.executeBatch = executeBatch;
|
|
19
|
+
exports.clickAndVerify = clickAndVerify;
|
|
20
|
+
exports.hotkeyAndVerify = hotkeyAndVerify;
|
|
21
|
+
exports.moveRelative = moveRelative;
|
|
22
|
+
exports.moveRelativeSmooth = moveRelativeSmooth;
|
|
23
|
+
exports.typeAndWaitStable = typeAndWaitStable;
|
|
24
|
+
exports.recordMouseTrail = recordMouseTrail;
|
|
25
|
+
exports.replayMouseTrail = replayMouseTrail;
|
|
26
|
+
exports.releaseAllModifiers = releaseAllModifiers;
|
|
27
|
+
exports.sequence = sequence;
|
|
28
|
+
exports.clickAndWait = clickAndWait;
|
|
29
|
+
exports.typeAndEnter = typeAndEnter;
|
|
30
|
+
exports.parallel = parallel;
|
|
31
|
+
exports.createCursorStream = createCursorStream;
|
|
32
|
+
exports.moveClickVerify = moveClickVerify;
|
|
33
|
+
exports.executeBatchAsync = executeBatchAsync;
|
|
34
|
+
exports.smoothClickAt = smoothClickAt;
|
|
35
|
+
exports.smoothClickAndType = smoothClickAndType;
|
|
36
|
+
exports.nativeParallel = nativeParallel;
|
|
37
|
+
exports.interleave = interleave;
|
|
38
|
+
exports.ocr = ocr;
|
|
39
|
+
exports.findText = findText;
|
|
40
|
+
exports.clickText = clickText;
|
|
41
|
+
exports.waitForText = waitForText;
|
|
42
|
+
exports.waitForTextAndClick = waitForTextAndClick;
|
|
43
|
+
exports.watchRegion = watchRegion;
|
|
44
|
+
exports.onRegionChange = onRegionChange;
|
|
45
|
+
exports.onHotkey = onHotkey;
|
|
46
|
+
exports.waitForHotkey = waitForHotkey;
|
|
47
|
+
const index_js_1 = require("../index.js");
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
// Core Functions
|
|
50
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
/**
|
|
52
|
+
* Execute multiple actions as an atomic batch.
|
|
53
|
+
*
|
|
54
|
+
* This is the primary way applications should interact with the system.
|
|
55
|
+
* All actions execute sequentially in a single native call,
|
|
56
|
+
* minimizing latency and round-trips.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* // Click a button, wait for response, then type
|
|
61
|
+
* const result = await agent.executeBatch([
|
|
62
|
+
* { type: 'mouseClickAt', x: 500, y: 300 },
|
|
63
|
+
* { type: 'waitForStable', stableMs: 200 },
|
|
64
|
+
* { type: 'type', text: 'Hello World' },
|
|
65
|
+
* { type: 'keyPress', key: 'Enter' }
|
|
66
|
+
* ]);
|
|
67
|
+
*
|
|
68
|
+
* if (result.allSucceeded) {
|
|
69
|
+
* console.log('All actions completed in', result.totalDurationMs, 'ms');
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @param actions Array of actions to execute
|
|
74
|
+
* @param options Execution options
|
|
75
|
+
* @returns Batch result with per-action details
|
|
76
|
+
*/
|
|
77
|
+
function executeBatch(actions, options = {}) {
|
|
78
|
+
const nativeResult = (0, index_js_1.agentExecuteBatch)(actions, options.stopOnError ?? true);
|
|
79
|
+
return {
|
|
80
|
+
results: nativeResult.results.map((r) => ({
|
|
81
|
+
index: r.index,
|
|
82
|
+
success: r.success,
|
|
83
|
+
error: r.error ?? undefined,
|
|
84
|
+
durationMs: r.durationMs,
|
|
85
|
+
mousePosition: r.mouseX != null && r.mouseY != null
|
|
86
|
+
? { x: r.mouseX, y: r.mouseY }
|
|
87
|
+
: undefined,
|
|
88
|
+
changeDetected: r.changeDetected ?? undefined,
|
|
89
|
+
})),
|
|
90
|
+
totalDurationMs: nativeResult.totalDurationMs,
|
|
91
|
+
successCount: nativeResult.successCount,
|
|
92
|
+
failureCount: nativeResult.failureCount,
|
|
93
|
+
firstFailureIndex: nativeResult.firstFailureIndex,
|
|
94
|
+
allSucceeded: nativeResult.allSucceeded,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Click at a position and verify visual change occurred.
|
|
99
|
+
*
|
|
100
|
+
* Useful for confirming clicks had an effect.
|
|
101
|
+
* Captures screen before and after click, computes diff.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const result = await agent.clickAndVerify(500, 300);
|
|
106
|
+
* if (!result.verified) {
|
|
107
|
+
* console.log('Click had no visual effect - may need retry');
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*
|
|
111
|
+
* @param x Target X coordinate
|
|
112
|
+
* @param y Target Y coordinate
|
|
113
|
+
* @param options Click options
|
|
114
|
+
* @returns Verification result
|
|
115
|
+
*/
|
|
116
|
+
function clickAndVerify(x, y, options = {}) {
|
|
117
|
+
const result = (0, index_js_1.agentClickAndVerify)(x, y, options.button, options.minChangePercent, options.timeoutMs, options.region?.x, options.region?.y, options.region?.width, options.region?.height);
|
|
118
|
+
return {
|
|
119
|
+
verified: result.verified,
|
|
120
|
+
changePercentage: result.changePercentage,
|
|
121
|
+
durationMs: result.durationMs,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Execute a hotkey combination and verify visual change.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* // Open file dialog and verify it appeared
|
|
130
|
+
* const result = await agent.hotkeyAndVerify(['ctrl', 'o']);
|
|
131
|
+
* if (result.verified) {
|
|
132
|
+
* console.log('Dialog opened');
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @param keys Array of keys to press together
|
|
137
|
+
* @param options Verification options
|
|
138
|
+
* @returns Verification result
|
|
139
|
+
*/
|
|
140
|
+
function hotkeyAndVerify(keys, options = {}) {
|
|
141
|
+
const result = (0, index_js_1.agentHotkeyAndVerify)(keys, options.minChangePercent, options.timeoutMs, options.region?.x, options.region?.y, options.region?.width, options.region?.height);
|
|
142
|
+
return {
|
|
143
|
+
verified: result.verified,
|
|
144
|
+
changePercentage: result.changePercentage,
|
|
145
|
+
durationMs: result.durationMs,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
149
|
+
// Relative Mouse Movement
|
|
150
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
151
|
+
/**
|
|
152
|
+
* Move mouse relative to current position.
|
|
153
|
+
*
|
|
154
|
+
* @param dx Horizontal offset (positive = right)
|
|
155
|
+
* @param dy Vertical offset (positive = down)
|
|
156
|
+
* @returns New mouse position
|
|
157
|
+
*/
|
|
158
|
+
function moveRelative(dx, dy) {
|
|
159
|
+
const pos = (0, index_js_1.agentMoveRelative)(dx, dy);
|
|
160
|
+
return { x: pos.x, y: pos.y };
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Move mouse relative to current position with smooth animation.
|
|
164
|
+
*
|
|
165
|
+
* @param dx Horizontal offset
|
|
166
|
+
* @param dy Vertical offset
|
|
167
|
+
* @param durationMs Animation duration (default: 300ms)
|
|
168
|
+
* @returns New mouse position
|
|
169
|
+
*/
|
|
170
|
+
function moveRelativeSmooth(dx, dy, durationMs) {
|
|
171
|
+
const pos = (0, index_js_1.agentMoveRelativeSmooth)(dx, dy, durationMs);
|
|
172
|
+
return { x: pos.x, y: pos.y };
|
|
173
|
+
}
|
|
174
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
175
|
+
// Text Input with Verification
|
|
176
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
177
|
+
/**
|
|
178
|
+
* Type text and wait for screen to stabilize.
|
|
179
|
+
*
|
|
180
|
+
* Useful for form input where you want to wait for
|
|
181
|
+
* autocomplete or validation to finish.
|
|
182
|
+
*
|
|
183
|
+
* @param text Text to type
|
|
184
|
+
* @param options Wait options
|
|
185
|
+
* @returns True if screen stabilized within timeout
|
|
186
|
+
*/
|
|
187
|
+
function typeAndWaitStable(text, options = {}) {
|
|
188
|
+
return (0, index_js_1.agentTypeAndWaitStable)(text, options.stabilityThreshold, options.stableDurationMs, options.timeoutMs);
|
|
189
|
+
}
|
|
190
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
191
|
+
// Gesture Recording
|
|
192
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
193
|
+
/**
|
|
194
|
+
* Record mouse movements for a duration.
|
|
195
|
+
*
|
|
196
|
+
* Captures mouse trail for gesture analysis or replay.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* // Record 3 seconds of mouse movement
|
|
201
|
+
* const trail = agent.recordMouseTrail(3000);
|
|
202
|
+
* console.log(`Recorded ${trail.length} points`);
|
|
203
|
+
*
|
|
204
|
+
* // Replay at half speed
|
|
205
|
+
* agent.replayMouseTrail(trail, 0.5);
|
|
206
|
+
* ```
|
|
207
|
+
*
|
|
208
|
+
* @param durationMs How long to record
|
|
209
|
+
* @param sampleRateHz Sample rate (default: 60)
|
|
210
|
+
* @returns Array of trail points with timestamps
|
|
211
|
+
*/
|
|
212
|
+
function recordMouseTrail(durationMs, sampleRateHz) {
|
|
213
|
+
const trail = (0, index_js_1.agentRecordMouseTrail)(durationMs, sampleRateHz);
|
|
214
|
+
return trail.map((p) => ({
|
|
215
|
+
timestampMs: p.timestampMs,
|
|
216
|
+
x: p.x,
|
|
217
|
+
y: p.y,
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Replay a recorded mouse trail.
|
|
222
|
+
*
|
|
223
|
+
* @param trail Previously recorded trail
|
|
224
|
+
* @param speedMultiplier Playback speed (1.0 = normal, 0.5 = half speed)
|
|
225
|
+
*/
|
|
226
|
+
function replayMouseTrail(trail, speedMultiplier) {
|
|
227
|
+
const native = trail.map((p) => ({
|
|
228
|
+
timestampMs: p.timestampMs,
|
|
229
|
+
x: p.x,
|
|
230
|
+
y: p.y,
|
|
231
|
+
}));
|
|
232
|
+
(0, index_js_1.agentReplayMouseTrail)(native, speedMultiplier);
|
|
233
|
+
}
|
|
234
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
235
|
+
// Error Recovery
|
|
236
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
237
|
+
/**
|
|
238
|
+
* Release all held modifier keys.
|
|
239
|
+
*
|
|
240
|
+
* Call this for error recovery when modifier keys might be stuck,
|
|
241
|
+
* or after complex keyboard sequences.
|
|
242
|
+
*/
|
|
243
|
+
function releaseAllModifiers() {
|
|
244
|
+
(0, index_js_1.agentReleaseAllModifiers)();
|
|
245
|
+
}
|
|
246
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
247
|
+
// Action Builders (Fluent API)
|
|
248
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
249
|
+
/**
|
|
250
|
+
* Fluent action builder for creating complex sequences.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* const result = agent.sequence()
|
|
255
|
+
* .click(500, 300)
|
|
256
|
+
* .waitForStable()
|
|
257
|
+
* .type('Hello World')
|
|
258
|
+
* .press('Enter')
|
|
259
|
+
* .waitForChange()
|
|
260
|
+
* .execute();
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
class ActionSequence {
|
|
264
|
+
actions = [];
|
|
265
|
+
/** Move mouse to absolute position */
|
|
266
|
+
moveTo(x, y) {
|
|
267
|
+
this.actions.push({ type: 'mouseMove', x, y });
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
/** Move mouse smoothly to absolute position */
|
|
271
|
+
moveToSmooth(x, y, duration) {
|
|
272
|
+
this.actions.push({ type: 'mouseMoveSmooth', x, y, duration });
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
/** Move mouse relative to current position */
|
|
276
|
+
moveBy(dx, dy) {
|
|
277
|
+
this.actions.push({ type: 'mouseMoveRel', dx, dy });
|
|
278
|
+
return this;
|
|
279
|
+
}
|
|
280
|
+
/** Click at current position */
|
|
281
|
+
click(button) {
|
|
282
|
+
this.actions.push({ type: 'mouseClick', button });
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
/** Click at specific coordinates */
|
|
286
|
+
clickAt(x, y, button) {
|
|
287
|
+
this.actions.push({ type: 'mouseClickAt', x, y, button });
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
/** Double click */
|
|
291
|
+
doubleClick(button) {
|
|
292
|
+
this.actions.push({ type: 'mouseDoubleClick', button });
|
|
293
|
+
return this;
|
|
294
|
+
}
|
|
295
|
+
/** Press mouse button down */
|
|
296
|
+
mouseDown(button) {
|
|
297
|
+
this.actions.push({ type: 'mouseDown', button });
|
|
298
|
+
return this;
|
|
299
|
+
}
|
|
300
|
+
/** Release mouse button */
|
|
301
|
+
mouseUp(button) {
|
|
302
|
+
this.actions.push({ type: 'mouseUp', button });
|
|
303
|
+
return this;
|
|
304
|
+
}
|
|
305
|
+
/** Scroll wheel */
|
|
306
|
+
scroll(amount, horizontal) {
|
|
307
|
+
this.actions.push({ type: 'mouseScroll', amount, horizontal });
|
|
308
|
+
return this;
|
|
309
|
+
}
|
|
310
|
+
/** Drag from current position to target */
|
|
311
|
+
dragTo(toX, toY, button) {
|
|
312
|
+
this.actions.push({ type: 'mouseDrag', toX, toY, button });
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
/** Type text instantly */
|
|
316
|
+
type(text) {
|
|
317
|
+
this.actions.push({ type: 'type', text });
|
|
318
|
+
return this;
|
|
319
|
+
}
|
|
320
|
+
/** Type text with human-like delays */
|
|
321
|
+
typeHuman(text, minDelay, maxDelay) {
|
|
322
|
+
this.actions.push({ type: 'typeHumanized', text, minDelay, maxDelay });
|
|
323
|
+
return this;
|
|
324
|
+
}
|
|
325
|
+
/** Press and release a key */
|
|
326
|
+
press(key) {
|
|
327
|
+
this.actions.push({ type: 'keyPress', key });
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
/** Press key down (remember to release!) */
|
|
331
|
+
keyDown(key) {
|
|
332
|
+
this.actions.push({ type: 'keyDown', key });
|
|
333
|
+
return this;
|
|
334
|
+
}
|
|
335
|
+
/** Release key */
|
|
336
|
+
keyUp(key) {
|
|
337
|
+
this.actions.push({ type: 'keyUp', key });
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
/** Press a hotkey combination */
|
|
341
|
+
hotkey(...keys) {
|
|
342
|
+
this.actions.push({ type: 'hotkey', keys });
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
/** Wait for specified duration */
|
|
346
|
+
delay(ms) {
|
|
347
|
+
this.actions.push({ type: 'delay', ms });
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
350
|
+
/** Alias for delay */
|
|
351
|
+
wait(ms) {
|
|
352
|
+
return this.delay(ms);
|
|
353
|
+
}
|
|
354
|
+
/** Wait for visual change */
|
|
355
|
+
waitForChange(options) {
|
|
356
|
+
this.actions.push({
|
|
357
|
+
type: 'waitForChange',
|
|
358
|
+
threshold: options?.threshold,
|
|
359
|
+
timeout: options?.timeout,
|
|
360
|
+
region: options?.region,
|
|
361
|
+
});
|
|
362
|
+
return this;
|
|
363
|
+
}
|
|
364
|
+
/** Wait for screen to stabilize */
|
|
365
|
+
waitForStable(options) {
|
|
366
|
+
this.actions.push({
|
|
367
|
+
type: 'waitForStable',
|
|
368
|
+
threshold: options?.threshold,
|
|
369
|
+
stableMs: options?.stableMs,
|
|
370
|
+
timeout: options?.timeout,
|
|
371
|
+
region: options?.region,
|
|
372
|
+
});
|
|
373
|
+
return this;
|
|
374
|
+
}
|
|
375
|
+
/** Get the built actions array */
|
|
376
|
+
build() {
|
|
377
|
+
return [...this.actions];
|
|
378
|
+
}
|
|
379
|
+
/** Execute all queued actions */
|
|
380
|
+
execute(options) {
|
|
381
|
+
return executeBatch(this.actions, options);
|
|
382
|
+
}
|
|
383
|
+
/** Execute all queued actions — non-blocking async version */
|
|
384
|
+
async executeAsync(options) {
|
|
385
|
+
return executeBatchAsync(this.actions, options);
|
|
386
|
+
}
|
|
387
|
+
/** Clear all queued actions */
|
|
388
|
+
clear() {
|
|
389
|
+
this.actions = [];
|
|
390
|
+
return this;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
exports.ActionSequence = ActionSequence;
|
|
394
|
+
/**
|
|
395
|
+
* Create a new action sequence builder.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```typescript
|
|
399
|
+
* const result = agent.sequence()
|
|
400
|
+
* .clickAt(500, 300)
|
|
401
|
+
* .waitForStable()
|
|
402
|
+
* .type('search query')
|
|
403
|
+
* .press('Enter')
|
|
404
|
+
* .execute();
|
|
405
|
+
* ```
|
|
406
|
+
*/
|
|
407
|
+
function sequence() {
|
|
408
|
+
return new ActionSequence();
|
|
409
|
+
}
|
|
410
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
411
|
+
// Convenience Functions
|
|
412
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
413
|
+
/**
|
|
414
|
+
* Simple click with optional wait for visual feedback.
|
|
415
|
+
* Combines click and waitForStable in one call.
|
|
416
|
+
*
|
|
417
|
+
* @param x Target X coordinate
|
|
418
|
+
* @param y Target Y coordinate
|
|
419
|
+
* @param options Click options
|
|
420
|
+
* @returns Batch result
|
|
421
|
+
*/
|
|
422
|
+
function clickAndWait(x, y, options = {}) {
|
|
423
|
+
return executeBatch([
|
|
424
|
+
{ type: 'mouseClickAt', x, y, button: options.button },
|
|
425
|
+
{ type: 'waitForStable', stableMs: options.stableMs ?? 200 },
|
|
426
|
+
]);
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Type text followed by Enter key.
|
|
430
|
+
*
|
|
431
|
+
* @param text Text to type before pressing Enter
|
|
432
|
+
* @param options Type options
|
|
433
|
+
* @returns Batch result
|
|
434
|
+
*/
|
|
435
|
+
function typeAndEnter(text, options = {}) {
|
|
436
|
+
const actions = options.humanized
|
|
437
|
+
? [{ type: 'typeHumanized', text }, { type: 'keyPress', key: 'Enter' }]
|
|
438
|
+
: [{ type: 'type', text }, { type: 'keyPress', key: 'Enter' }];
|
|
439
|
+
return executeBatch(actions);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Execute multiple independent actions in parallel.
|
|
443
|
+
*
|
|
444
|
+
* This is the key primitive for AI agents that need to do multiple things
|
|
445
|
+
* simultaneously — e.g., move the mouse while monitoring the screen,
|
|
446
|
+
* or type while watching for visual changes.
|
|
447
|
+
*
|
|
448
|
+
* Each task is a named async function. All run concurrently via Promise.all.
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* // Move mouse and monitor screen simultaneously
|
|
453
|
+
* const result = await agent.parallel({
|
|
454
|
+
* move: async () => {
|
|
455
|
+
* // Non-blocking smooth movement
|
|
456
|
+
* const handle = mouse.moveSmoothAsync(500, 300, { duration: 600 });
|
|
457
|
+
* return handle.promise;
|
|
458
|
+
* },
|
|
459
|
+
* monitor: async () => {
|
|
460
|
+
* // Capture screen while mouse is moving
|
|
461
|
+
* await sleep(200);
|
|
462
|
+
* return screen.capture();
|
|
463
|
+
* },
|
|
464
|
+
* });
|
|
465
|
+
*
|
|
466
|
+
* // All done
|
|
467
|
+
* console.log(result.allSucceeded); // true
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
async function parallel(tasks) {
|
|
471
|
+
const start = Date.now();
|
|
472
|
+
const names = Object.keys(tasks);
|
|
473
|
+
const promises = names.map(async (name) => {
|
|
474
|
+
const taskStart = Date.now();
|
|
475
|
+
try {
|
|
476
|
+
const value = await tasks[name]();
|
|
477
|
+
return {
|
|
478
|
+
name,
|
|
479
|
+
success: true,
|
|
480
|
+
durationMs: Date.now() - taskStart,
|
|
481
|
+
value,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
return {
|
|
486
|
+
name,
|
|
487
|
+
success: false,
|
|
488
|
+
error: err?.message ?? String(err),
|
|
489
|
+
durationMs: Date.now() - taskStart,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
const results = await Promise.all(promises);
|
|
494
|
+
return {
|
|
495
|
+
results,
|
|
496
|
+
totalDurationMs: Date.now() - start,
|
|
497
|
+
allSucceeded: results.every(r => r.success),
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Create a real-time cursor control stream.
|
|
502
|
+
*
|
|
503
|
+
* Returns a handle that lets you continuously direct the cursor in real-time.
|
|
504
|
+
* The cursor moves smoothly towards targets with no blocking. Multiple
|
|
505
|
+
* `moveTo()` calls seamlessly redirect the cursor mid-flight.
|
|
506
|
+
*
|
|
507
|
+
* This is the preferred way for AI agents to control the cursor because:
|
|
508
|
+
* - Movement is always visible and smooth
|
|
509
|
+
* - Targets can be updated any time (no waiting for previous move to finish)
|
|
510
|
+
* - Velocity-based control for continuous steering
|
|
511
|
+
* - Zero blocking of the Node.js event loop
|
|
512
|
+
*
|
|
513
|
+
* @param updateRateHz How many times per second to update position (default: 120)
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```typescript
|
|
517
|
+
* const cursor = agent.createCursorStream(120);
|
|
518
|
+
*
|
|
519
|
+
* // Smooth movement to target
|
|
520
|
+
* cursor.moveTo(500, 300);
|
|
521
|
+
*
|
|
522
|
+
* // Change target mid-flight — cursor redirects smoothly
|
|
523
|
+
* setTimeout(() => cursor.moveTo(800, 600), 200);
|
|
524
|
+
*
|
|
525
|
+
* // Velocity-based control (e.g., from joystick or AI output)
|
|
526
|
+
* cursor.setVelocity(200, -100); // 200px/s right, 100px/s up
|
|
527
|
+
*
|
|
528
|
+
* // When done
|
|
529
|
+
* cursor.destroy();
|
|
530
|
+
* ```
|
|
531
|
+
*/
|
|
532
|
+
function createCursorStream(updateRateHz = 120) {
|
|
533
|
+
let active = true;
|
|
534
|
+
let targetX = null;
|
|
535
|
+
let targetY = null;
|
|
536
|
+
let velocityX = 0;
|
|
537
|
+
let velocityY = 0;
|
|
538
|
+
let timer = null;
|
|
539
|
+
const intervalMs = Math.max(1, Math.round(1000 / updateRateHz));
|
|
540
|
+
// The update loop runs on a setInterval — non-blocking
|
|
541
|
+
timer = setInterval(() => {
|
|
542
|
+
if (!active)
|
|
543
|
+
return;
|
|
544
|
+
try {
|
|
545
|
+
const pos = (0, index_js_1.mouseGetPosition)();
|
|
546
|
+
if (targetX !== null && targetY !== null) {
|
|
547
|
+
const dx = targetX - pos.x;
|
|
548
|
+
const dy = targetY - pos.y;
|
|
549
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
550
|
+
if (dist < 2) {
|
|
551
|
+
// Arrived at target
|
|
552
|
+
(0, index_js_1.mouseMove)(targetX, targetY);
|
|
553
|
+
targetX = null;
|
|
554
|
+
targetY = null;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
// Move towards target — speed proportional to distance
|
|
558
|
+
const speed = Math.min(3000, dist * 6);
|
|
559
|
+
const moveX = Math.round((dx / dist) * speed / updateRateHz);
|
|
560
|
+
const moveY = Math.round((dy / dist) * speed / updateRateHz);
|
|
561
|
+
(0, index_js_1.mouseMove)(pos.x + moveX, pos.y + moveY);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
else if (velocityX !== 0 || velocityY !== 0) {
|
|
565
|
+
// Velocity-based movement
|
|
566
|
+
const moveX = Math.round(velocityX / updateRateHz);
|
|
567
|
+
const moveY = Math.round(velocityY / updateRateHz);
|
|
568
|
+
if (moveX !== 0 || moveY !== 0) {
|
|
569
|
+
(0, index_js_1.mouseMove)(pos.x + moveX, pos.y + moveY);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
// Silently continue on error (e.g., if mouse API fails momentarily)
|
|
575
|
+
}
|
|
576
|
+
}, intervalMs);
|
|
577
|
+
const handle = {
|
|
578
|
+
moveTo(x, y) {
|
|
579
|
+
targetX = x;
|
|
580
|
+
targetY = y;
|
|
581
|
+
velocityX = 0;
|
|
582
|
+
velocityY = 0;
|
|
583
|
+
},
|
|
584
|
+
jumpTo(x, y) {
|
|
585
|
+
targetX = null;
|
|
586
|
+
targetY = null;
|
|
587
|
+
velocityX = 0;
|
|
588
|
+
velocityY = 0;
|
|
589
|
+
(0, index_js_1.mouseMove)(x, y);
|
|
590
|
+
},
|
|
591
|
+
setVelocity(vx, vy) {
|
|
592
|
+
velocityX = vx;
|
|
593
|
+
velocityY = vy;
|
|
594
|
+
targetX = null;
|
|
595
|
+
targetY = null;
|
|
596
|
+
},
|
|
597
|
+
stop() {
|
|
598
|
+
targetX = null;
|
|
599
|
+
targetY = null;
|
|
600
|
+
velocityX = 0;
|
|
601
|
+
velocityY = 0;
|
|
602
|
+
},
|
|
603
|
+
destroy() {
|
|
604
|
+
active = false;
|
|
605
|
+
if (timer !== null) {
|
|
606
|
+
clearInterval(timer);
|
|
607
|
+
timer = null;
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
get active() { return active; },
|
|
611
|
+
get position() {
|
|
612
|
+
const p = (0, index_js_1.mouseGetPosition)();
|
|
613
|
+
return { x: p.x, y: p.y };
|
|
614
|
+
},
|
|
615
|
+
};
|
|
616
|
+
return handle;
|
|
617
|
+
}
|
|
618
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
619
|
+
// Non-Blocking Agent Primitives
|
|
620
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
621
|
+
/**
|
|
622
|
+
* Move to a position smoothly, click, and verify — all non-blocking.
|
|
623
|
+
*
|
|
624
|
+
* The cursor visually moves to the target, clicks, then verifies
|
|
625
|
+
* that the screen changed. The entire operation is async.
|
|
626
|
+
*
|
|
627
|
+
* @example
|
|
628
|
+
* ```typescript
|
|
629
|
+
* const result = await agent.moveClickVerify(500, 300, {
|
|
630
|
+
* moveDuration: 400,
|
|
631
|
+
* button: 'left',
|
|
632
|
+
* });
|
|
633
|
+
* console.log(result.verified); // true if screen changed after click
|
|
634
|
+
* ```
|
|
635
|
+
*/
|
|
636
|
+
async function moveClickVerify(x, y, options = {}) {
|
|
637
|
+
// Non-blocking smooth movement
|
|
638
|
+
const from = (0, index_js_1.mouseGetPosition)();
|
|
639
|
+
const duration = options.moveDuration ?? 400;
|
|
640
|
+
const steps = Math.max(2, Math.round((120 * duration) / 1000));
|
|
641
|
+
const delayMs = Math.max(1, Math.round(duration / steps));
|
|
642
|
+
// Simple easing path
|
|
643
|
+
const dx = x - from.x;
|
|
644
|
+
const dy = y - from.y;
|
|
645
|
+
for (let i = 1; i <= steps; i++) {
|
|
646
|
+
const t = i / steps;
|
|
647
|
+
const e = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
648
|
+
(0, index_js_1.mouseMove)(Math.round(from.x + dx * e), Math.round(from.y + dy * e));
|
|
649
|
+
await new Promise(r => setTimeout(r, delayMs));
|
|
650
|
+
}
|
|
651
|
+
// Click and verify
|
|
652
|
+
const result = clickAndVerify(x, y, {
|
|
653
|
+
button: options.button,
|
|
654
|
+
minChangePercent: options.minChangePercent,
|
|
655
|
+
timeoutMs: options.verifyTimeoutMs,
|
|
656
|
+
region: options.region,
|
|
657
|
+
});
|
|
658
|
+
return {
|
|
659
|
+
...result,
|
|
660
|
+
finalPosition: { x, y },
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Execute a batch of actions with async wrapper.
|
|
665
|
+
*
|
|
666
|
+
* Same as `executeBatch()` but returns a Promise, so it can be used
|
|
667
|
+
* in `Promise.all()` with other async operations.
|
|
668
|
+
*/
|
|
669
|
+
async function executeBatchAsync(actions, options = {}) {
|
|
670
|
+
// Run the synchronous batch in a microtask to allow interleaving
|
|
671
|
+
return new Promise((resolve) => {
|
|
672
|
+
setImmediate(() => {
|
|
673
|
+
resolve(executeBatch(actions, options));
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Convenience: smoothly move to position and click — async.
|
|
679
|
+
* The cursor is visible moving on screen the whole time.
|
|
680
|
+
*/
|
|
681
|
+
async function smoothClickAt(x, y, options = {}) {
|
|
682
|
+
const from = (0, index_js_1.mouseGetPosition)();
|
|
683
|
+
const duration = options.duration ?? 350;
|
|
684
|
+
const steps = Math.max(2, Math.round((120 * duration) / 1000));
|
|
685
|
+
const delayMs = Math.max(1, Math.round(duration / steps));
|
|
686
|
+
const dx = x - from.x;
|
|
687
|
+
const dy = y - from.y;
|
|
688
|
+
for (let i = 1; i <= steps; i++) {
|
|
689
|
+
const t = i / steps;
|
|
690
|
+
const e = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
691
|
+
(0, index_js_1.mouseMove)(Math.round(from.x + dx * e), Math.round(from.y + dy * e));
|
|
692
|
+
if (i < steps)
|
|
693
|
+
await new Promise(r => setTimeout(r, delayMs));
|
|
694
|
+
}
|
|
695
|
+
(0, index_js_1.mouseClick)(options.button);
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Convenience: smoothly move to a text field, click it, type text.
|
|
699
|
+
* Fully async and visually smooth.
|
|
700
|
+
*/
|
|
701
|
+
async function smoothClickAndType(x, y, text, options = {}) {
|
|
702
|
+
await smoothClickAt(x, y, { duration: options.duration });
|
|
703
|
+
await new Promise(r => setTimeout(r, 50));
|
|
704
|
+
(0, index_js_1.keyboardType)(text, options.typeDelay);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Execute multiple action groups in TRUE parallel on native OS threads.
|
|
708
|
+
*
|
|
709
|
+
* Each group runs on its own Rust thread. Within a group, actions execute
|
|
710
|
+
* sequentially. Groups start simultaneously (or with optional start delays).
|
|
711
|
+
*
|
|
712
|
+
* This is the core primitive that makes Stelo faster than any other SDK —
|
|
713
|
+
* true OS-level parallelism, not async pretend.
|
|
714
|
+
*
|
|
715
|
+
* @example
|
|
716
|
+
* ```typescript
|
|
717
|
+
* // Move mouse AND type AND capture screen — all at the same time
|
|
718
|
+
* const result = agent.nativeParallel([
|
|
719
|
+
* {
|
|
720
|
+
* groupId: 'mouse',
|
|
721
|
+
* actions: [
|
|
722
|
+
* { type: 'mouseMoveSmooth', x: 500, y: 300, duration: 500 },
|
|
723
|
+
* { type: 'mouseClick' },
|
|
724
|
+
* ],
|
|
725
|
+
* },
|
|
726
|
+
* {
|
|
727
|
+
* groupId: 'keyboard',
|
|
728
|
+
* actions: [
|
|
729
|
+
* { type: 'delay', ms: 200 }, // Start typing 200ms after mouse starts
|
|
730
|
+
* { type: 'type', text: 'hello world' },
|
|
731
|
+
* ],
|
|
732
|
+
* },
|
|
733
|
+
* ]);
|
|
734
|
+
* console.log(result.totalDurationMs); // ~500ms, not ~700ms sequential
|
|
735
|
+
* ```
|
|
736
|
+
*/
|
|
737
|
+
function nativeParallel(groups) {
|
|
738
|
+
const nativeResult = (0, index_js_1.parallelExecute)(groups);
|
|
739
|
+
return mapParallelResult(nativeResult);
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Execute actions with interleaved timing — human-like overlapping input.
|
|
743
|
+
*
|
|
744
|
+
* Each action has a startAtMs offset. Actions with the same start time
|
|
745
|
+
* run in parallel on separate OS threads. This enables realistic
|
|
746
|
+
* overlapping mouse + keyboard behavior.
|
|
747
|
+
*
|
|
748
|
+
* @example
|
|
749
|
+
* ```typescript
|
|
750
|
+
* // Start moving mouse immediately, start typing while mouse still moving
|
|
751
|
+
* agent.interleave([
|
|
752
|
+
* { startAtMs: 0, type: 'mouseMoveSmooth', x: 500, y: 300, duration: 500 },
|
|
753
|
+
* { startAtMs: 150, type: 'type', text: 'hello' }, // starts while mouse moving
|
|
754
|
+
* { startAtMs: 500, type: 'mouseClick' }, // after mouse arrives
|
|
755
|
+
* ]);
|
|
756
|
+
* ```
|
|
757
|
+
*/
|
|
758
|
+
function interleave(timedActions) {
|
|
759
|
+
const nativeResult = (0, index_js_1.interleavedExecute)(timedActions);
|
|
760
|
+
return mapParallelResult(nativeResult);
|
|
761
|
+
}
|
|
762
|
+
function mapParallelResult(nr) {
|
|
763
|
+
return {
|
|
764
|
+
groups: nr.groups.map((g) => ({
|
|
765
|
+
groupId: g.groupId,
|
|
766
|
+
results: g.results.map((r) => ({
|
|
767
|
+
index: r.index,
|
|
768
|
+
success: r.success,
|
|
769
|
+
error: r.error ?? undefined,
|
|
770
|
+
durationMs: r.durationMs,
|
|
771
|
+
mousePosition: r.mouseX != null && r.mouseY != null
|
|
772
|
+
? { x: r.mouseX, y: r.mouseY }
|
|
773
|
+
: undefined,
|
|
774
|
+
changeDetected: r.changeDetected ?? undefined,
|
|
775
|
+
})),
|
|
776
|
+
durationMs: g.durationMs,
|
|
777
|
+
successCount: g.successCount,
|
|
778
|
+
failureCount: g.failureCount,
|
|
779
|
+
})),
|
|
780
|
+
totalDurationMs: nr.totalDurationMs,
|
|
781
|
+
totalActions: nr.totalActions,
|
|
782
|
+
totalSuccess: nr.totalSuccess,
|
|
783
|
+
totalFailures: nr.totalFailures,
|
|
784
|
+
allSucceeded: nr.allSucceeded,
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Perform OCR on a screen region. Returns recognized text with positions.
|
|
789
|
+
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```typescript
|
|
792
|
+
* // OCR the entire screen
|
|
793
|
+
* const result = agent.ocr();
|
|
794
|
+
* console.log(result.text);
|
|
795
|
+
*
|
|
796
|
+
* // OCR a specific region
|
|
797
|
+
* const result = agent.ocr({ x: 100, y: 200, width: 400, height: 50 });
|
|
798
|
+
* for (const word of result.lines[0]?.words ?? []) {
|
|
799
|
+
* console.log(`"${word.text}" at (${word.center.x}, ${word.center.y})`);
|
|
800
|
+
* }
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
function ocr(region) {
|
|
804
|
+
const raw = (0, index_js_1.ocrRecognize)(region?.x, region?.y, region?.width, region?.height);
|
|
805
|
+
return {
|
|
806
|
+
text: raw.text,
|
|
807
|
+
durationMs: raw.durationMs,
|
|
808
|
+
lines: raw.lines.map((line) => ({
|
|
809
|
+
text: line.text,
|
|
810
|
+
x: line.x,
|
|
811
|
+
y: line.y,
|
|
812
|
+
width: line.width,
|
|
813
|
+
height: line.height,
|
|
814
|
+
words: line.words.map((w) => ({
|
|
815
|
+
text: w.text,
|
|
816
|
+
x: w.x,
|
|
817
|
+
y: w.y,
|
|
818
|
+
width: w.width,
|
|
819
|
+
height: w.height,
|
|
820
|
+
confidence: w.confidence,
|
|
821
|
+
center: { x: w.x + w.width / 2, y: w.y + w.height / 2 },
|
|
822
|
+
})),
|
|
823
|
+
})),
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Find text on screen via OCR and return its position.
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```typescript
|
|
831
|
+
* const submit = agent.findText('Submit');
|
|
832
|
+
* if (submit) {
|
|
833
|
+
* mouse.clickAt(submit.center.x, submit.center.y);
|
|
834
|
+
* }
|
|
835
|
+
* ```
|
|
836
|
+
*/
|
|
837
|
+
function findText(needle, region) {
|
|
838
|
+
const raw = (0, index_js_1.ocrFindText)(needle, region?.x, region?.y, region?.width, region?.height);
|
|
839
|
+
if (!raw)
|
|
840
|
+
return null;
|
|
841
|
+
return {
|
|
842
|
+
text: raw.text,
|
|
843
|
+
x: raw.x,
|
|
844
|
+
y: raw.y,
|
|
845
|
+
width: raw.width,
|
|
846
|
+
height: raw.height,
|
|
847
|
+
confidence: raw.confidence,
|
|
848
|
+
center: { x: raw.x + raw.width / 2, y: raw.y + raw.height / 2 },
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Find text on screen and click its center. Game-changer for AI agents.
|
|
853
|
+
*
|
|
854
|
+
* @example
|
|
855
|
+
* ```typescript
|
|
856
|
+
* // Click the "Submit" button — no coordinates needed
|
|
857
|
+
* agent.clickText('Submit');
|
|
858
|
+
*
|
|
859
|
+
* // Click "Cancel" with right-click
|
|
860
|
+
* agent.clickText('Cancel', { button: 'right' });
|
|
861
|
+
* ```
|
|
862
|
+
*
|
|
863
|
+
* @throws Error if text not found on screen
|
|
864
|
+
*/
|
|
865
|
+
function clickText(needle, options = {}) {
|
|
866
|
+
const pos = (0, index_js_1.ocrClickText)(needle, options.button, options.region?.x, options.region?.y, options.region?.width, options.region?.height);
|
|
867
|
+
return { x: pos.x, y: pos.y };
|
|
868
|
+
}
|
|
869
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
870
|
+
// Smart Reactions — Wait for visual/text conditions before acting
|
|
871
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
872
|
+
/**
|
|
873
|
+
* Wait until OCR detects the specified text on screen.
|
|
874
|
+
* Polls at a configurable interval. Returns the word position, or null on timeout.
|
|
875
|
+
*
|
|
876
|
+
* @example
|
|
877
|
+
* ```typescript
|
|
878
|
+
* // Wait up to 10s for "Loading complete" to appear
|
|
879
|
+
* const word = agent.waitForText('Loading complete', { timeout: 10000 });
|
|
880
|
+
* if (word) console.log('Found at', word.center);
|
|
881
|
+
* ```
|
|
882
|
+
*/
|
|
883
|
+
function waitForText(needle, options = {}) {
|
|
884
|
+
const raw = (0, index_js_1.waitForText)(needle, options.timeout, options.interval, options.region?.x, options.region?.y, options.region?.width, options.region?.height);
|
|
885
|
+
if (!raw)
|
|
886
|
+
return null;
|
|
887
|
+
return {
|
|
888
|
+
text: raw.text,
|
|
889
|
+
x: raw.x,
|
|
890
|
+
y: raw.y,
|
|
891
|
+
width: raw.width,
|
|
892
|
+
height: raw.height,
|
|
893
|
+
confidence: raw.confidence,
|
|
894
|
+
center: { x: raw.x + raw.width / 2, y: raw.y + raw.height / 2 },
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Wait for text to appear on screen, then click it.
|
|
899
|
+
* Combines waitForText + click in a single native call for lower latency.
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* ```typescript
|
|
903
|
+
* // Wait for "Submit" button to appear, then click it
|
|
904
|
+
* const pos = agent.waitForTextAndClick('Submit', { timeout: 5000 });
|
|
905
|
+
* if (pos) console.log('Clicked at', pos);
|
|
906
|
+
* ```
|
|
907
|
+
*/
|
|
908
|
+
function waitForTextAndClick(needle, options = {}) {
|
|
909
|
+
const raw = (0, index_js_1.waitForTextAndClick)(needle, options.timeout, options.interval, options.button, options.region?.x, options.region?.y, options.region?.width, options.region?.height);
|
|
910
|
+
return raw ? { x: raw.x, y: raw.y } : null;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Watch a screen region and wait for it to change.
|
|
914
|
+
* Returns the change percentage when threshold is exceeded, or null on timeout.
|
|
915
|
+
*
|
|
916
|
+
* @example
|
|
917
|
+
* ```typescript
|
|
918
|
+
* // Watch a button area for any visual change
|
|
919
|
+
* const change = agent.watchRegion(
|
|
920
|
+
* { x: 200, y: 300, width: 100, height: 40 },
|
|
921
|
+
* { threshold: 2.0, timeout: 5000 }
|
|
922
|
+
* );
|
|
923
|
+
* if (change) console.log(`Region changed by ${change.toFixed(1)}%`);
|
|
924
|
+
* ```
|
|
925
|
+
*/
|
|
926
|
+
function watchRegion(region, options = {}) {
|
|
927
|
+
return (0, index_js_1.watchRegionOnce)(region.x, region.y, region.width, region.height, options.threshold, options.timeout, options.interval);
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Continuously watch a screen region and call a handler when it changes.
|
|
931
|
+
* Returns a stop function.
|
|
932
|
+
*
|
|
933
|
+
* @example
|
|
934
|
+
* ```typescript
|
|
935
|
+
* const stop = agent.onRegionChange(
|
|
936
|
+
* { x: 200, y: 300, width: 100, height: 40 },
|
|
937
|
+
* (changePct) => console.log(`Changed: ${changePct}%`),
|
|
938
|
+
* { threshold: 1.0, interval: 300 }
|
|
939
|
+
* );
|
|
940
|
+
* // Later...
|
|
941
|
+
* stop();
|
|
942
|
+
* ```
|
|
943
|
+
*/
|
|
944
|
+
function onRegionChange(region, handler, options = {}) {
|
|
945
|
+
let running = true;
|
|
946
|
+
const interval = options.interval ?? 300;
|
|
947
|
+
const threshold = options.threshold ?? 1.0;
|
|
948
|
+
const poll = () => {
|
|
949
|
+
if (!running)
|
|
950
|
+
return;
|
|
951
|
+
const change = (0, index_js_1.watchRegionOnce)(region.x, region.y, region.width, region.height, threshold, interval + 100, // slight timeout buffer
|
|
952
|
+
interval);
|
|
953
|
+
if (change !== null && running) {
|
|
954
|
+
handler(change);
|
|
955
|
+
}
|
|
956
|
+
if (running) {
|
|
957
|
+
setTimeout(poll, 0);
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
setTimeout(poll, 0);
|
|
961
|
+
return () => { running = false; };
|
|
962
|
+
}
|
|
963
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
964
|
+
// Hotkey System — Register global keyboard shortcuts
|
|
965
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
966
|
+
/**
|
|
967
|
+
* Register a global hotkey handler. Starts listening for keyboard events
|
|
968
|
+
* and triggers the callback when the key combination is detected.
|
|
969
|
+
*
|
|
970
|
+
* @example
|
|
971
|
+
* ```typescript
|
|
972
|
+
* // Register Ctrl+Shift+A
|
|
973
|
+
* const unregister = agent.onHotkey('ctrl+shift+a', () => {
|
|
974
|
+
* console.log('Hotkey triggered!');
|
|
975
|
+
* });
|
|
976
|
+
*
|
|
977
|
+
* // Later: stop listening
|
|
978
|
+
* unregister();
|
|
979
|
+
* ```
|
|
980
|
+
*/
|
|
981
|
+
function onHotkey(combo, handler, options = {}) {
|
|
982
|
+
// Parse combo: "ctrl+shift+a" → { ctrl: true, shift: true, alt: false, key: 'a' }
|
|
983
|
+
const parts = combo.toLowerCase().split('+').map(s => s.trim());
|
|
984
|
+
const modifiers = {
|
|
985
|
+
ctrl: parts.includes('ctrl') || parts.includes('control'),
|
|
986
|
+
shift: parts.includes('shift'),
|
|
987
|
+
alt: parts.includes('alt'),
|
|
988
|
+
meta: parts.includes('meta') || parts.includes('win') || parts.includes('cmd'),
|
|
989
|
+
};
|
|
990
|
+
const key = parts.find(p => !['ctrl', 'control', 'shift', 'alt', 'meta', 'win', 'cmd'].includes(p)) || '';
|
|
991
|
+
let running = true;
|
|
992
|
+
const pollMs = options.pollInterval ?? 50;
|
|
993
|
+
// Use hooks to poll for key events
|
|
994
|
+
const { startHooks, drainEvents, stopHooks } = require('../index.js');
|
|
995
|
+
startHooks({ captureKeyboard: true, captureMouseClicks: false, captureMouseMove: false, captureMouseWheel: false, bufferSize: 64 });
|
|
996
|
+
const held = new Set();
|
|
997
|
+
const poll = () => {
|
|
998
|
+
if (!running)
|
|
999
|
+
return;
|
|
1000
|
+
try {
|
|
1001
|
+
const events = drainEvents();
|
|
1002
|
+
for (const ev of events) {
|
|
1003
|
+
if (ev.type === 'keydown' || ev.type === 'KeyDown') {
|
|
1004
|
+
held.add((ev.keyName || '').toLowerCase());
|
|
1005
|
+
}
|
|
1006
|
+
else if (ev.type === 'keyup' || ev.type === 'KeyUp') {
|
|
1007
|
+
held.delete((ev.keyName || '').toLowerCase());
|
|
1008
|
+
}
|
|
1009
|
+
// Check if combo matches on keydown
|
|
1010
|
+
if (ev.type === 'keydown' || ev.type === 'KeyDown') {
|
|
1011
|
+
const keyMatch = key === '' || (ev.keyName || '').toLowerCase() === key;
|
|
1012
|
+
const ctrlMatch = !modifiers.ctrl || held.has('lcontrol') || held.has('rcontrol') || held.has('control');
|
|
1013
|
+
const shiftMatch = !modifiers.shift || held.has('lshift') || held.has('rshift') || held.has('shift');
|
|
1014
|
+
const altMatch = !modifiers.alt || held.has('lalt') || held.has('ralt') || held.has('alt') || held.has('lmenu') || held.has('rmenu');
|
|
1015
|
+
if (keyMatch && ctrlMatch && shiftMatch && altMatch) {
|
|
1016
|
+
handler();
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
catch { /* ignore errors during polling */ }
|
|
1022
|
+
if (running)
|
|
1023
|
+
setTimeout(poll, pollMs);
|
|
1024
|
+
};
|
|
1025
|
+
setTimeout(poll, 0);
|
|
1026
|
+
return () => {
|
|
1027
|
+
running = false;
|
|
1028
|
+
try {
|
|
1029
|
+
stopHooks();
|
|
1030
|
+
}
|
|
1031
|
+
catch { /* ignore */ }
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Wait for a hotkey combo to be pressed. Returns a promise that resolves
|
|
1036
|
+
* when the combo is detected, or rejects on timeout.
|
|
1037
|
+
*
|
|
1038
|
+
* @example
|
|
1039
|
+
* ```typescript
|
|
1040
|
+
* await agent.waitForHotkey('ctrl+shift+s', { timeout: 30000 });
|
|
1041
|
+
* console.log('User pressed Ctrl+Shift+S!');
|
|
1042
|
+
* ```
|
|
1043
|
+
*/
|
|
1044
|
+
function waitForHotkey(combo, options = {}) {
|
|
1045
|
+
const timeout = options.timeout ?? 30000;
|
|
1046
|
+
return new Promise((resolve, reject) => {
|
|
1047
|
+
const timer = setTimeout(() => {
|
|
1048
|
+
unregister();
|
|
1049
|
+
reject(new Error(`Hotkey '${combo}' not pressed within ${timeout}ms`));
|
|
1050
|
+
}, timeout);
|
|
1051
|
+
const unregister = onHotkey(combo, () => {
|
|
1052
|
+
clearTimeout(timer);
|
|
1053
|
+
unregister();
|
|
1054
|
+
resolve();
|
|
1055
|
+
});
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1059
|
+
// Default Export
|
|
1060
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
1061
|
+
exports.agent = {
|
|
1062
|
+
// Core
|
|
1063
|
+
executeBatch,
|
|
1064
|
+
executeBatchAsync,
|
|
1065
|
+
clickAndVerify,
|
|
1066
|
+
hotkeyAndVerify,
|
|
1067
|
+
// Async / Non-blocking
|
|
1068
|
+
parallel,
|
|
1069
|
+
moveClickVerify,
|
|
1070
|
+
smoothClickAt,
|
|
1071
|
+
smoothClickAndType,
|
|
1072
|
+
// Streaming control
|
|
1073
|
+
createCursorStream,
|
|
1074
|
+
// Relative movement
|
|
1075
|
+
moveRelative,
|
|
1076
|
+
moveRelativeSmooth,
|
|
1077
|
+
// Text input
|
|
1078
|
+
typeAndWaitStable,
|
|
1079
|
+
// Gesture
|
|
1080
|
+
recordMouseTrail,
|
|
1081
|
+
replayMouseTrail,
|
|
1082
|
+
// Recovery
|
|
1083
|
+
releaseAllModifiers,
|
|
1084
|
+
// Fluent API
|
|
1085
|
+
sequence,
|
|
1086
|
+
ActionSequence,
|
|
1087
|
+
// Convenience
|
|
1088
|
+
clickAndWait,
|
|
1089
|
+
typeAndEnter,
|
|
1090
|
+
// Native Parallel Pipeline (true OS-thread parallelism)
|
|
1091
|
+
nativeParallel,
|
|
1092
|
+
interleave,
|
|
1093
|
+
// OCR (native text recognition)
|
|
1094
|
+
ocr,
|
|
1095
|
+
findText,
|
|
1096
|
+
clickText,
|
|
1097
|
+
// Smart Reactions
|
|
1098
|
+
waitForText,
|
|
1099
|
+
waitForTextAndClick,
|
|
1100
|
+
watchRegion,
|
|
1101
|
+
onRegionChange,
|
|
1102
|
+
// Hotkey System
|
|
1103
|
+
onHotkey,
|
|
1104
|
+
waitForHotkey,
|
|
1105
|
+
};
|
|
1106
|
+
exports.default = exports.agent;
|
|
1107
|
+
//# sourceMappingURL=agent.js.map
|