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/README.md
ADDED
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# Stelo
|
|
4
|
+
|
|
5
|
+
### The Most Advanced Universal Desktop Automation Framework
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/stelo)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](.)
|
|
10
|
+
[](.)
|
|
11
|
+
[](.)
|
|
12
|
+
|
|
13
|
+
**Mouse · Keyboard · Screen · Window · Recorder · Safety**
|
|
14
|
+
|
|
15
|
+
*Sub-millisecond native performance powered by Rust + napi-rs*
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Why Stelo?
|
|
22
|
+
|
|
23
|
+
| Feature | RobotJS | nutjs | **Stelo** |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| Active maintenance | ❌ | ⚠️ | ✅ |
|
|
26
|
+
| Cross-platform | ✅ | ✅ | ✅ |
|
|
27
|
+
| Native performance | ✅ | ❌ (screen slow) | ✅ Rust |
|
|
28
|
+
| Smooth mouse movement | ❌ | ✅ basic | ✅ Bezier + Wind-Mouse |
|
|
29
|
+
| **Async non-blocking movement** | ❌ | ❌ | ✅ |
|
|
30
|
+
| **Real-time cursor streaming** | ❌ | ❌ | ✅ |
|
|
31
|
+
| **Parallel action execution** | ❌ | ❌ | ✅ |
|
|
32
|
+
| Humanized input | ❌ | ❌ | ✅ |
|
|
33
|
+
| Window management | ❌ | ✅ | ✅ |
|
|
34
|
+
| Screen capture | ✅ slow | ✅ | ✅ fast |
|
|
35
|
+
| Automation recorder | ❌ | ❌ | ✅ |
|
|
36
|
+
| Safety / failsafe | ❌ | ❌ | ✅ |
|
|
37
|
+
| TypeScript-first | ❌ | ✅ | ✅ |
|
|
38
|
+
| Prebuilt binaries | ❌ (rebuild) | ✅ | ✅ napi-rs |
|
|
39
|
+
| **Enterprise telemetry** | ❌ | ❌ | ✅ |
|
|
40
|
+
| **Circuit breakers** | ❌ | ❌ | ✅ |
|
|
41
|
+
| **Audit logging** | ❌ | ❌ | ✅ |
|
|
42
|
+
| **Structured errors** | ❌ | ❌ | ✅ |
|
|
43
|
+
| **Screen diffing** | ❌ | ❌ | ✅ native Rust |
|
|
44
|
+
| **Change detection** | ❌ | ❌ | ✅ |
|
|
45
|
+
| **Action verification** | ❌ | ❌ | ✅ |
|
|
46
|
+
| **Grid analysis** | ❌ | ❌ | ✅ |
|
|
47
|
+
| **Perceptual hashing** | ❌ | ❌ | ✅ |
|
|
48
|
+
| **Action batching** | ❌ | ❌ | ✅ |
|
|
49
|
+
| **Real-time agent control** | ❌ | ❌ | ✅ |
|
|
50
|
+
| **Streaming cursor control** | ❌ | ❌ | ✅ |
|
|
51
|
+
| **Gesture recording** | ❌ | ❌ | ✅ |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npm install stelo
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { mouse, keyboard, screen } from 'stelo';
|
|
63
|
+
|
|
64
|
+
// Move mouse smoothly with bezier curve
|
|
65
|
+
await mouse.moveSmoothly(500, 400, { duration: 800, curve: 'bezier' });
|
|
66
|
+
|
|
67
|
+
// Click, type, hotkey
|
|
68
|
+
mouse.click();
|
|
69
|
+
keyboard.type('Hello from Stelo!');
|
|
70
|
+
keyboard.hotkey('ctrl', 's');
|
|
71
|
+
|
|
72
|
+
// Screen analysis
|
|
73
|
+
const color = screen.getPixelColor(100, 200);
|
|
74
|
+
console.log(color.hex); // '#3B82F6'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
> **Node.js ≥ 18** required. Prebuilt binaries available — no compiler needed.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Built for Computer-Use Agents
|
|
82
|
+
|
|
83
|
+
Stelo is the premier SDK for building autonomous desktop agents. Every design decision prioritizes the needs of vision-language models and computer-use systems:
|
|
84
|
+
|
|
85
|
+
**🎯 Native Resolution Everywhere**
|
|
86
|
+
- Forces Per-Monitor DPI Awareness V2 on Windows
|
|
87
|
+
- Screenshot coordinates = mouse coordinates = vision model coordinates
|
|
88
|
+
- Zero drift, zero scaling math, single coordinate space end-to-end
|
|
89
|
+
|
|
90
|
+
**👁 Vision Primitives (Rust-native)**
|
|
91
|
+
- **Screen diffing**: Detect what changed between frames
|
|
92
|
+
- **Action verification**: Confirm clicks/keypresses actually worked
|
|
93
|
+
- **Wait for stable**: Pause until animations complete
|
|
94
|
+
- **Grid analysis**: Efficiently identify regions of interest for vision models
|
|
95
|
+
- **Perceptual hashing**: Fast similarity checks without full pixel comparison
|
|
96
|
+
- **Color clustering**: Find UI elements like buttons by color
|
|
97
|
+
|
|
98
|
+
**⚡ Subsecond Feedback Loop**
|
|
99
|
+
- Native Rust screen capture (10-50ms full screen)
|
|
100
|
+
- BGRA capture mode (skip color conversion for vision pipelines)
|
|
101
|
+
- Humanized input that evades bot detection
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { vision, mouse, screen } from 'stelo';
|
|
105
|
+
|
|
106
|
+
// Agent workflow: observe → act → verify → repeat
|
|
107
|
+
const before = vision.captureReference();
|
|
108
|
+
await mouse.click(button.x, button.y);
|
|
109
|
+
|
|
110
|
+
const result = await vision.verifyAction(
|
|
111
|
+
async () => {}, // Already clicked
|
|
112
|
+
0.5, // Require 0.5% visual change
|
|
113
|
+
2000
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
if (!result.verified) {
|
|
117
|
+
// No visual feedback - click may have failed
|
|
118
|
+
// Agent can retry or try alternative approach
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Wait for any animations to complete before next action
|
|
122
|
+
await vision.waitForStable(0.1, 200, 5000);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Real-Time Agent Control
|
|
128
|
+
|
|
129
|
+
For AI agents that need to control mouse and keyboard in real-time with minimal latency, Stelo provides a dedicated `agent` module optimized for continuous control loops:
|
|
130
|
+
|
|
131
|
+
**🚀 Non-Blocking Real-Time Movement**
|
|
132
|
+
- Cursor moves visually on-screen in real-time — no teleporting
|
|
133
|
+
- Event loop stays free during movement — do other things in parallel
|
|
134
|
+
- Cancel movements mid-flight, redirect cursor seamlessly
|
|
135
|
+
- All movement is cancellable via `MovementHandle`
|
|
136
|
+
|
|
137
|
+
**⚡ Parallel Action Execution**
|
|
138
|
+
- Run multiple operations simultaneously via `agent.parallel()`
|
|
139
|
+
- Move mouse while monitoring screen; type while waiting for UI changes
|
|
140
|
+
- Zero blocking — everything is async/non-blocking
|
|
141
|
+
|
|
142
|
+
**🔄 Streaming Cursor Control**
|
|
143
|
+
- Create persistent cursor streams with `agent.createCursorStream()`
|
|
144
|
+
- Send continuous target updates — cursor smoothly follows in real-time
|
|
145
|
+
- Velocity-based control for joystick/AI-style steering
|
|
146
|
+
- Perfect for vision-language models that output continuous coordinates
|
|
147
|
+
|
|
148
|
+
**🎯 Verification-First Design**
|
|
149
|
+
- Every action can verify it had the expected visual effect
|
|
150
|
+
- Know immediately if a click "worked" or needs retry
|
|
151
|
+
- Essential for robust autonomous agents
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { mouse, keyboard, agent, screen } from 'stelo';
|
|
155
|
+
|
|
156
|
+
// ── Non-Blocking Smooth Movement ────────────────────────────────────
|
|
157
|
+
// The cursor visually moves on-screen. No teleporting. No blocking.
|
|
158
|
+
|
|
159
|
+
await mouse.moveSmoothAsync(500, 400, { duration: 600 });
|
|
160
|
+
|
|
161
|
+
// Cancel mid-flight
|
|
162
|
+
const handle = mouse.moveSmoothAsync(800, 600);
|
|
163
|
+
setTimeout(() => handle.cancel(), 200);
|
|
164
|
+
await handle.promise;
|
|
165
|
+
|
|
166
|
+
// Humanized movement with jitter and overshoot — also non-blocking
|
|
167
|
+
await mouse.moveHumanizedAsync(500, 300, {
|
|
168
|
+
duration: 800,
|
|
169
|
+
jitter: 0.5,
|
|
170
|
+
}).promise;
|
|
171
|
+
|
|
172
|
+
// ── Parallel Operations ─────────────────────────────────────────────
|
|
173
|
+
// Move mouse while monitoring screen — simultaneously
|
|
174
|
+
|
|
175
|
+
const result = await agent.parallel({
|
|
176
|
+
move: async () => {
|
|
177
|
+
await mouse.moveSmoothAsync(500, 300, { duration: 600 }).promise;
|
|
178
|
+
},
|
|
179
|
+
monitor: async () => {
|
|
180
|
+
await new Promise(r => setTimeout(r, 300));
|
|
181
|
+
return screen.capture();
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ── Streaming Cursor Control ────────────────────────────────────────
|
|
186
|
+
// For continuous real-time control by AI models
|
|
187
|
+
|
|
188
|
+
const cursor = agent.createCursorStream(120);
|
|
189
|
+
|
|
190
|
+
cursor.moveTo(500, 300); // Smooth movement to target
|
|
191
|
+
setTimeout(() => cursor.moveTo(800, 600), 200); // Redirect mid-flight
|
|
192
|
+
cursor.setVelocity(200, -100); // Velocity-based steering
|
|
193
|
+
cursor.stop(); // Stop all movement
|
|
194
|
+
cursor.destroy(); // Release resources
|
|
195
|
+
|
|
196
|
+
// ── Smooth Click & Type ─────────────────────────────────────────────
|
|
197
|
+
// Visible cursor movement + click + type — all in one async call
|
|
198
|
+
|
|
199
|
+
await agent.smoothClickAndType(500, 300, 'search query', {
|
|
200
|
+
duration: 400,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// ── Non-Blocking Typing ─────────────────────────────────────────────
|
|
204
|
+
// Type while doing other things simultaneously
|
|
205
|
+
|
|
206
|
+
await Promise.all([
|
|
207
|
+
keyboard.typeHumanizedAsync('Hello World').promise,
|
|
208
|
+
(async () => {
|
|
209
|
+
await new Promise(r => setTimeout(r, 100));
|
|
210
|
+
return screen.capture(); // Capture while typing
|
|
211
|
+
})(),
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
// ── Action Batching ─────────────────────────────────────────────────
|
|
215
|
+
// Execute multiple actions atomically in one native call
|
|
216
|
+
|
|
217
|
+
const batch = agent.executeBatch([
|
|
218
|
+
{ type: 'mouseClickAt', x: 500, y: 300 },
|
|
219
|
+
{ type: 'waitForStable', stableMs: 200 },
|
|
220
|
+
{ type: 'type', text: 'search query' },
|
|
221
|
+
{ type: 'keyPress', key: 'Enter' },
|
|
222
|
+
{ type: 'waitForChange', timeout: 3000 }
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
// ── Fluent Sequence Builder ─────────────────────────────────────────
|
|
226
|
+
const workflow = agent.sequence()
|
|
227
|
+
.clickAt(500, 300)
|
|
228
|
+
.waitForStable()
|
|
229
|
+
.type('Hello World')
|
|
230
|
+
.press('Enter')
|
|
231
|
+
.waitForChange()
|
|
232
|
+
.executeAsync(); // Non-blocking version
|
|
233
|
+
|
|
234
|
+
// ── Verification ────────────────────────────────────────────────────
|
|
235
|
+
const verified = await agent.moveClickVerify(500, 300, {
|
|
236
|
+
moveDuration: 400,
|
|
237
|
+
minChangePercent: 0.5,
|
|
238
|
+
});
|
|
239
|
+
if (!verified.verified) {
|
|
240
|
+
console.log('Click had no visual effect - retry needed');
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Supported Batch Actions:**
|
|
245
|
+
| Type | Description | Parameters |
|
|
246
|
+
|------|-------------|------------|
|
|
247
|
+
| `mouseMove` | Instant move | `x, y` |
|
|
248
|
+
| `mouseMoveRel` | Relative move | `dx, dy` |
|
|
249
|
+
| `mouseMoveSmooth` | Animated move | `x, y, duration?` |
|
|
250
|
+
| `mouseClick` | Click at current pos | `button?` |
|
|
251
|
+
| `mouseClickAt` | Move and click | `x, y, button?` |
|
|
252
|
+
| `mouseDoubleClick` | Double click | `button?` |
|
|
253
|
+
| `mouseDown/Up` | Press/release | `button?` |
|
|
254
|
+
| `mouseScroll` | Scroll wheel | `amount, horizontal?` |
|
|
255
|
+
| `mouseDrag` | Drag to position | `toX, toY, button?` |
|
|
256
|
+
| `type` | Type text instantly | `text` |
|
|
257
|
+
| `typeHumanized` | Type naturally | `text, minDelay?, maxDelay?` |
|
|
258
|
+
| `keyPress` | Press and release | `key` |
|
|
259
|
+
| `keyDown/Up` | Hold/release key | `key` |
|
|
260
|
+
| `hotkey` | Key combination | `keys[]` |
|
|
261
|
+
| `delay` | Wait duration | `ms` |
|
|
262
|
+
| `waitForChange` | Wait for visual change | `threshold?, timeout?, region?` |
|
|
263
|
+
| `waitForStable` | Wait for stability | `threshold?, stableMs?, timeout?, region?` |
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Installation
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# npm
|
|
271
|
+
npm install stelo
|
|
272
|
+
|
|
273
|
+
# yarn
|
|
274
|
+
yarn add stelo
|
|
275
|
+
|
|
276
|
+
# pnpm
|
|
277
|
+
pnpm add stelo
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Supported Platforms
|
|
281
|
+
|
|
282
|
+
| OS | Arch | Package |
|
|
283
|
+
|---|---|---|
|
|
284
|
+
| Windows | x64 | `stelo-win32-x64-msvc` |
|
|
285
|
+
| Windows | arm64 | `stelo-win32-arm64-msvc` |
|
|
286
|
+
| macOS | x64 (Intel) | `stelo-darwin-x64` |
|
|
287
|
+
| macOS | arm64 (Apple Silicon) | `stelo-darwin-arm64` |
|
|
288
|
+
| Linux | x64 (glibc) | `stelo-linux-x64-gnu` |
|
|
289
|
+
| Linux | x64 (musl) | `stelo-linux-x64-musl` |
|
|
290
|
+
| Linux | arm64 (glibc) | `stelo-linux-arm64-gnu` |
|
|
291
|
+
| Linux | arm64 (musl) | `stelo-linux-arm64-musl` |
|
|
292
|
+
|
|
293
|
+
Platform packages are auto-installed via `optionalDependencies`.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## API Overview
|
|
298
|
+
|
|
299
|
+
### 🖱 Mouse
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { mouse } from 'stelo';
|
|
303
|
+
|
|
304
|
+
// Position
|
|
305
|
+
mouse.move(x, y); // instant teleport
|
|
306
|
+
const { x, y } = mouse.getPosition(); // current position
|
|
307
|
+
console.log(mouse.x, mouse.y); // shorthand getters
|
|
308
|
+
|
|
309
|
+
// Smooth movement
|
|
310
|
+
await mouse.moveSmoothly(x, y, {
|
|
311
|
+
duration: 500, // ms
|
|
312
|
+
curve: 'bezier' | 'easeInOut' | 'linear',
|
|
313
|
+
});
|
|
314
|
+
await mouse.moveHumanized(x, y); // wind-mouse algorithm
|
|
315
|
+
|
|
316
|
+
// Clicks
|
|
317
|
+
mouse.click(); // left click
|
|
318
|
+
mouse.click('right'); // right click
|
|
319
|
+
mouse.click('middle'); // middle click
|
|
320
|
+
mouse.doubleClick();
|
|
321
|
+
mouse.tripleClick();
|
|
322
|
+
mouse.moveAndClick(x, y); // move + click in one
|
|
323
|
+
|
|
324
|
+
// Press / release
|
|
325
|
+
mouse.down('left');
|
|
326
|
+
mouse.up('left');
|
|
327
|
+
|
|
328
|
+
// Scroll
|
|
329
|
+
mouse.scroll(3, 'up'); // 3 clicks up
|
|
330
|
+
mouse.scroll(5, 'down');
|
|
331
|
+
mouse.scroll(2, 'left'); // horizontal
|
|
332
|
+
|
|
333
|
+
// Drag
|
|
334
|
+
mouse.drag(fromX, fromY, toX, toY);
|
|
335
|
+
mouse.drag(fromX, fromY, toX, toY, 'right'); // right-button drag
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### ⌨️ Keyboard
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { keyboard } from 'stelo';
|
|
342
|
+
|
|
343
|
+
// Typing
|
|
344
|
+
keyboard.type('Hello World'); // instant
|
|
345
|
+
await keyboard.typeHumanized('Hello', { // natural speed
|
|
346
|
+
minDelay: 40,
|
|
347
|
+
maxDelay: 120,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Key operations
|
|
351
|
+
keyboard.press('enter'); // tap a key
|
|
352
|
+
keyboard.down('shift'); // hold
|
|
353
|
+
keyboard.up('shift'); // release
|
|
354
|
+
keyboard.tap('backspace', 5); // repeat N times
|
|
355
|
+
|
|
356
|
+
// Hotkeys
|
|
357
|
+
keyboard.hotkey('ctrl', 'c'); // copy
|
|
358
|
+
keyboard.hotkey('ctrl', 'shift', 'p'); // multi-key combo
|
|
359
|
+
|
|
360
|
+
// Convenience shortcuts
|
|
361
|
+
keyboard.enter();
|
|
362
|
+
keyboard.tab();
|
|
363
|
+
keyboard.escape();
|
|
364
|
+
keyboard.backspace();
|
|
365
|
+
keyboard.selectAll(); // ctrl+a / cmd+a
|
|
366
|
+
keyboard.copy(); // ctrl+c / cmd+c
|
|
367
|
+
keyboard.paste(); // ctrl+v / cmd+v
|
|
368
|
+
keyboard.cut(); // ctrl+x / cmd+x
|
|
369
|
+
keyboard.undo(); // ctrl+z / cmd+z
|
|
370
|
+
keyboard.redo(); // ctrl+y / cmd+shift+z
|
|
371
|
+
keyboard.save(); // ctrl+s / cmd+s
|
|
372
|
+
|
|
373
|
+
// Query
|
|
374
|
+
const keys = keyboard.allKeys(); // all supported key names
|
|
375
|
+
const pressed = keyboard.isPressed('shift'); // is key currently held?
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### 🖥 Screen
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { screen } from 'stelo';
|
|
382
|
+
|
|
383
|
+
// Dimensions
|
|
384
|
+
const { width, height } = screen.getSize();
|
|
385
|
+
|
|
386
|
+
// Pixel colour
|
|
387
|
+
const color = screen.getPixelColor(x, y);
|
|
388
|
+
// → { r: 59, g: 130, b: 246, hex: '#3B82F6' }
|
|
389
|
+
|
|
390
|
+
// Capture
|
|
391
|
+
const img = screen.capture(); // full screen
|
|
392
|
+
const region = screen.capture({ // specific region
|
|
393
|
+
x: 100, y: 100, width: 400, height: 300,
|
|
394
|
+
});
|
|
395
|
+
// img.data is a Buffer of RGBA pixels
|
|
396
|
+
|
|
397
|
+
// All displays
|
|
398
|
+
const displays = screen.getAllDisplays();
|
|
399
|
+
// → [{ id, x, y, width, height, isPrimary, scaleFactor }]
|
|
400
|
+
|
|
401
|
+
// Colour search
|
|
402
|
+
const point = screen.findColor('#FF0000', { tolerance: 10 });
|
|
403
|
+
const matches = screen.pixelMatches(x, y, '#FF0000', 5);
|
|
404
|
+
|
|
405
|
+
// Wait for colour
|
|
406
|
+
await screen.waitForColor(x, y, '#00FF00', {
|
|
407
|
+
tolerance: 10,
|
|
408
|
+
timeoutMs: 5000,
|
|
409
|
+
intervalMs: 100,
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### 🪟 Window
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
import { appWindow } from 'stelo';
|
|
417
|
+
|
|
418
|
+
// Query
|
|
419
|
+
const active = appWindow.getActive();
|
|
420
|
+
const all = appWindow.getAll();
|
|
421
|
+
const found = appWindow.find('Notepad');
|
|
422
|
+
|
|
423
|
+
// Focus
|
|
424
|
+
appWindow.focus(windowId);
|
|
425
|
+
appWindow.focusByTitle('Calculator');
|
|
426
|
+
|
|
427
|
+
// Geometry
|
|
428
|
+
appWindow.resize(id, 800, 600);
|
|
429
|
+
appWindow.moveTo(id, 100, 100);
|
|
430
|
+
appWindow.setBounds(id, { x: 0, y: 0, width: 1024, height: 768 });
|
|
431
|
+
|
|
432
|
+
// State
|
|
433
|
+
appWindow.minimize(id);
|
|
434
|
+
appWindow.maximize(id);
|
|
435
|
+
appWindow.restore(id);
|
|
436
|
+
appWindow.close(id);
|
|
437
|
+
|
|
438
|
+
// Wait for window to appear
|
|
439
|
+
const win = await appWindow.waitFor('MyApp', 10000);
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### 🎬 Recorder
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
import { recorder } from 'stelo';
|
|
446
|
+
|
|
447
|
+
// Build a sequence
|
|
448
|
+
const steps = recorder.sequence()
|
|
449
|
+
.moveTo(100, 200)
|
|
450
|
+
.click()
|
|
451
|
+
.delay(200)
|
|
452
|
+
.type('hello')
|
|
453
|
+
.press('enter')
|
|
454
|
+
.hotkey('ctrl', 's')
|
|
455
|
+
.build();
|
|
456
|
+
|
|
457
|
+
// Play it back
|
|
458
|
+
await recorder.play(steps);
|
|
459
|
+
await recorder.play(steps, { speed: 2.0 }); // 2× speed
|
|
460
|
+
|
|
461
|
+
// Serialize / restore
|
|
462
|
+
const json = recorder.toJSON(steps);
|
|
463
|
+
const restored = recorder.fromJSON(json);
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### 🛡 Safety
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
import { safety } from 'stelo';
|
|
470
|
+
|
|
471
|
+
// Failsafe: move mouse to any screen corner → emergency stop
|
|
472
|
+
safety.enableFailsafe();
|
|
473
|
+
safety.enableFailsafe(10); // 10px threshold
|
|
474
|
+
|
|
475
|
+
// Rate limiting
|
|
476
|
+
safety.setRateLimit(200); // max 200 actions/sec
|
|
477
|
+
|
|
478
|
+
// Emergency stop / reset
|
|
479
|
+
safety.emergencyStop();
|
|
480
|
+
console.log(safety.isStopped()); // true
|
|
481
|
+
safety.reset();
|
|
482
|
+
|
|
483
|
+
// Configure all at once
|
|
484
|
+
safety.configure({
|
|
485
|
+
failsafe: true,
|
|
486
|
+
failsafeThreshold: 5,
|
|
487
|
+
rateLimit: 500,
|
|
488
|
+
});
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### 🏢 Enterprise Features
|
|
492
|
+
|
|
493
|
+
Stelo includes enterprise-grade utilities for production deployments.
|
|
494
|
+
|
|
495
|
+
#### Telemetry & Observability
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
import { telemetry } from 'stelo';
|
|
499
|
+
|
|
500
|
+
// Enable telemetry
|
|
501
|
+
telemetry.enable();
|
|
502
|
+
|
|
503
|
+
// Subscribe to events
|
|
504
|
+
telemetry.onEvent((event) => {
|
|
505
|
+
console.log(`[${event.severity}] ${event.operation}: ${event.message}`);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// Track operations with timing
|
|
509
|
+
await telemetry.trackAsync('my-operation', async () => {
|
|
510
|
+
await mouse.moveSmoothly(500, 400);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Get metrics
|
|
514
|
+
const metrics = telemetry.getMetrics();
|
|
515
|
+
// → { totalOps, successRate, avgLatencyMs, p95LatencyMs, ... }
|
|
516
|
+
|
|
517
|
+
// Health check
|
|
518
|
+
const health = await telemetry.healthCheck();
|
|
519
|
+
// → { status: 'healthy', checks: [...], timestamp: '...' }
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### Circuit Breaker (Fault Tolerance)
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
import { CircuitBreaker } from 'stelo';
|
|
526
|
+
|
|
527
|
+
const breaker = new CircuitBreaker('external-service', {
|
|
528
|
+
failureThreshold: 5,
|
|
529
|
+
resetTimeoutMs: 30000,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
try {
|
|
533
|
+
await breaker.execute(async () => {
|
|
534
|
+
return await riskyOperation();
|
|
535
|
+
});
|
|
536
|
+
} catch (e) {
|
|
537
|
+
if (breaker.getState() === 'open') {
|
|
538
|
+
console.log('Circuit is open, service unavailable');
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
#### Validation & Input Checking
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
import { validate, ValidationError } from 'stelo';
|
|
547
|
+
|
|
548
|
+
// Validate coordinates
|
|
549
|
+
validate.coordinate(x, 'x');
|
|
550
|
+
validate.coordinate(y, 'y');
|
|
551
|
+
|
|
552
|
+
// Validate within screen bounds
|
|
553
|
+
const screenSize = screen.getSize();
|
|
554
|
+
validate.inBounds({ x, y }, screenSize);
|
|
555
|
+
|
|
556
|
+
// Safe validation (returns Result instead of throwing)
|
|
557
|
+
const result = validate.safe.coordinate(x, 'x');
|
|
558
|
+
if (!result.ok) {
|
|
559
|
+
console.error(result.error.message);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Validate regions
|
|
563
|
+
validate.region({ x: 0, y: 0, width: 100, height: 100 });
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
#### Audit Logging & Security
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
import { security } from 'stelo';
|
|
570
|
+
|
|
571
|
+
// Enable audit logging
|
|
572
|
+
security.enableAudit();
|
|
573
|
+
|
|
574
|
+
// Subscribe to audit events
|
|
575
|
+
security.onAudit((entry) => {
|
|
576
|
+
console.log(`[AUDIT] ${entry.operation}: ${entry.success ? 'OK' : 'FAIL'}`);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
// Get audit log
|
|
580
|
+
const log = security.getAuditLog(100); // last 100 entries
|
|
581
|
+
|
|
582
|
+
// Sanitize user input before typing
|
|
583
|
+
const safeText = security.sanitizeText(userInput);
|
|
584
|
+
keyboard.type(safeText);
|
|
585
|
+
|
|
586
|
+
// Detect sandboxed environments
|
|
587
|
+
const { sandboxed, indicators } = security.detectSandbox();
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
#### Enhanced Error Handling
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
import { SteloError, ErrorCode, retryWithBackoff } from 'stelo';
|
|
594
|
+
|
|
595
|
+
try {
|
|
596
|
+
await mouse.move(x, y);
|
|
597
|
+
} catch (e) {
|
|
598
|
+
if (e instanceof SteloError) {
|
|
599
|
+
console.log(`Error ${e.code}: ${e.message}`);
|
|
600
|
+
console.log('Recovery hints:', e.hints);
|
|
601
|
+
console.log('Diagnostics:', e.diagnostics);
|
|
602
|
+
|
|
603
|
+
// Generate detailed error report
|
|
604
|
+
console.log(e.toReport());
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Retry with exponential backoff
|
|
609
|
+
const result = await retryWithBackoff(
|
|
610
|
+
() => riskyOperation(),
|
|
611
|
+
{ maxAttempts: 5, initialDelayMs: 100 }
|
|
612
|
+
);
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
#### Rate Limiting
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
import { RateLimiter } from 'stelo';
|
|
619
|
+
|
|
620
|
+
// Token bucket rate limiter
|
|
621
|
+
const limiter = new RateLimiter(100, 10); // 100 max, 10/sec refill
|
|
622
|
+
|
|
623
|
+
if (limiter.tryConsume()) {
|
|
624
|
+
await doOperation();
|
|
625
|
+
} else {
|
|
626
|
+
console.log('Rate limited, try again later');
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### � Vision & Agent Primitives
|
|
631
|
+
|
|
632
|
+
Advanced screen analysis primitives designed for vision-based desktop automation.
|
|
633
|
+
|
|
634
|
+
#### Screen Diffing & Change Detection
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
import { vision, mouse } from 'stelo';
|
|
638
|
+
|
|
639
|
+
// Take reference screenshot
|
|
640
|
+
const before = vision.captureReference();
|
|
641
|
+
|
|
642
|
+
// Perform action
|
|
643
|
+
await mouse.click(500, 400);
|
|
644
|
+
|
|
645
|
+
// Compare: did the screen change?
|
|
646
|
+
const diff = vision.diff(before);
|
|
647
|
+
console.log(`${diff.changePercentage}% changed`);
|
|
648
|
+
if (diff.changedBounds) {
|
|
649
|
+
console.log('Change occurred at:', diff.changedBounds);
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
#### Action Verification
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
// Verify an action caused visual change
|
|
657
|
+
const result = await vision.verifyAction(
|
|
658
|
+
async () => { await mouse.click(100, 200); },
|
|
659
|
+
0.5, // require at least 0.5% change
|
|
660
|
+
2000 // timeout
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
if (!result.verified) {
|
|
664
|
+
console.log('Button click had no effect - retry?');
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
#### Wait for Screen Stability
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
// Wait for animations/transitions to complete
|
|
672
|
+
mouse.click();
|
|
673
|
+
await vision.waitForStable(
|
|
674
|
+
0.1, // max 0.1% change considered "stable"
|
|
675
|
+
200, // must be stable for 200ms
|
|
676
|
+
5000 // timeout
|
|
677
|
+
);
|
|
678
|
+
// Screen is now stable - safe to continue
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
#### Grid Analysis for Vision Models
|
|
682
|
+
|
|
683
|
+
```typescript
|
|
684
|
+
// Analyze screen as a grid (efficient for vision model region selection)
|
|
685
|
+
const grid = vision.analyzeGrid(16, 9); // 16x9 grid
|
|
686
|
+
|
|
687
|
+
// Find cells likely containing text or UI
|
|
688
|
+
const textCells = grid.cells.filter(c => c.likelyText);
|
|
689
|
+
const uiCells = grid.cells.filter(c => c.likelyUI);
|
|
690
|
+
|
|
691
|
+
// Click the center of cell [3, 2]
|
|
692
|
+
const center = vision.gridCellCenter(grid, 3, 2);
|
|
693
|
+
if (center) await mouse.click(center.x, center.y);
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
#### Perceptual Hashing
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
// Fast similarity check using perceptual hashes
|
|
700
|
+
const hash1 = vision.perceptualHash();
|
|
701
|
+
await performAction();
|
|
702
|
+
const hash2 = vision.perceptualHash();
|
|
703
|
+
|
|
704
|
+
const distance = vision.hashDistance(hash1, hash2);
|
|
705
|
+
if (distance < 5) {
|
|
706
|
+
console.log('Screen looks mostly the same');
|
|
707
|
+
} else if (distance > 20) {
|
|
708
|
+
console.log('Screen changed significantly');
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
#### Color Cluster Detection
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
// Find UI elements by color (e.g., buttons)
|
|
716
|
+
const blueClusters = vision.findColorClusters(
|
|
717
|
+
{ r: 0, g: 120, b: 215 }, // Windows accent blue
|
|
718
|
+
40, // tolerance
|
|
719
|
+
50 // min 50 pixels
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
if (blueClusters.length > 0) {
|
|
723
|
+
// Click center of first blue region
|
|
724
|
+
const btn = blueClusters[0];
|
|
725
|
+
mouse.click(btn.x + btn.width / 2, btn.y + btn.height / 2);
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### Agent (Real-Time Control)
|
|
730
|
+
|
|
731
|
+
Low-latency primitives for controlling mouse and keyboard in real-time.
|
|
732
|
+
|
|
733
|
+
#### Action Batching
|
|
734
|
+
|
|
735
|
+
```typescript
|
|
736
|
+
import { agent } from 'stelo';
|
|
737
|
+
|
|
738
|
+
// Execute multiple actions atomically - minimal latency
|
|
739
|
+
const result = agent.executeBatch([
|
|
740
|
+
{ type: 'mouseClickAt', x: 500, y: 300 },
|
|
741
|
+
{ type: 'waitForStable', stableMs: 200 },
|
|
742
|
+
{ type: 'type', text: 'Hello World' },
|
|
743
|
+
{ type: 'keyPress', key: 'Enter' }
|
|
744
|
+
], { stopOnError: true });
|
|
745
|
+
|
|
746
|
+
// Inspect results
|
|
747
|
+
console.log(`${result.successCount}/${result.results.length} succeeded`);
|
|
748
|
+
console.log(`Total time: ${result.totalDurationMs}ms`);
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
#### Click with Verification
|
|
752
|
+
|
|
753
|
+
```typescript
|
|
754
|
+
// Click and confirm the screen changed
|
|
755
|
+
const result = agent.clickAndVerify(500, 300, {
|
|
756
|
+
button: 'left',
|
|
757
|
+
minChangePercent: 0.5,
|
|
758
|
+
timeoutMs: 2000,
|
|
759
|
+
region: { x: 400, y: 200, width: 200, height: 200 }
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
if (!result.verified) {
|
|
763
|
+
console.log('Click had no visual effect');
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
#### Fluent Sequence Builder
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
// Chain actions with a fluent API
|
|
771
|
+
const result = agent.sequence()
|
|
772
|
+
.clickAt(500, 300)
|
|
773
|
+
.waitForStable()
|
|
774
|
+
.type('search query')
|
|
775
|
+
.press('Enter')
|
|
776
|
+
.waitForChange({ timeout: 3000 })
|
|
777
|
+
.hotkey('ctrl', 'a')
|
|
778
|
+
.execute();
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
#### Gesture Recording & Playback
|
|
782
|
+
|
|
783
|
+
```typescript
|
|
784
|
+
// Record mouse movements
|
|
785
|
+
const trail = agent.recordMouseTrail(3000, 60); // 3 sec at 60Hz
|
|
786
|
+
|
|
787
|
+
// Later, replay the gesture
|
|
788
|
+
agent.replayMouseTrail(trail, 0.5); // Half speed
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
#### Error Recovery
|
|
792
|
+
|
|
793
|
+
```typescript
|
|
794
|
+
// Release all held modifier keys (error recovery)
|
|
795
|
+
agent.releaseAllModifiers();
|
|
796
|
+
|
|
797
|
+
// Type and wait for autocomplete/validation to finish
|
|
798
|
+
const stable = agent.typeAndWaitStable('hello@example.com', {
|
|
799
|
+
stabilityThreshold: 0.1,
|
|
800
|
+
stableDurationMs: 200,
|
|
801
|
+
timeoutMs: 3000
|
|
802
|
+
});
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
### 🛠 Utilities
|
|
806
|
+
|
|
807
|
+
```typescript
|
|
808
|
+
import { delay, retry, waitUntil, batch, bezier } from 'stelo';
|
|
809
|
+
|
|
810
|
+
await delay(500); // sleep
|
|
811
|
+
|
|
812
|
+
const result = await retry(() => riskyOp(), { // auto-retry
|
|
813
|
+
maxAttempts: 5,
|
|
814
|
+
delayMs: 200,
|
|
815
|
+
backoffMultiplier: 1.5,
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
await waitUntil(() => isReady(), { // poll
|
|
819
|
+
timeoutMs: 10000,
|
|
820
|
+
intervalMs: 100,
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
await batch(tasks, { concurrency: 3 }); // parallel with limit
|
|
824
|
+
|
|
825
|
+
const pt = bezier.cubic(p0, p1, p2, p3, 0.5); // bezier evaluation
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
---
|
|
829
|
+
|
|
830
|
+
---
|
|
831
|
+
|
|
832
|
+
## FAQ
|
|
833
|
+
|
|
834
|
+
### Does Stelo require a C++ compiler?
|
|
835
|
+
**No.** Prebuilt binaries are published for all supported platforms. If you're building from source, you need **Rust** (not C++).
|
|
836
|
+
|
|
837
|
+
### How does humanized movement work?
|
|
838
|
+
Stelo implements the **Wind Mouse** algorithm — a physics-inspired model that simulates human hand tremor, acceleration, and overshoot. Combined with cubic bezier curves and arc-length parameterization, mouse paths look indistinguishable from real user input.
|
|
839
|
+
|
|
840
|
+
### Is Stelo safe to use in production?
|
|
841
|
+
Yes. The safety module provides failsafe (mouse-to-corner abort), rate limiting, and emergency stop. Always enable failsafe in automation scripts.
|
|
842
|
+
|
|
843
|
+
### Does Stelo work in headless / CI environments?
|
|
844
|
+
On Linux, use **Xvfb** (X Virtual Framebuffer) to run Stelo without a physical display. Windows and macOS require a desktop session.
|
|
845
|
+
|
|
846
|
+
### What about Wayland on Linux?
|
|
847
|
+
Stelo currently uses X11 / XTest. It works under XWayland on most Wayland compositors. Native Wayland input injection is planned.
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## License
|
|
852
|
+
|
|
853
|
+
[Apache-2.0](LICENSE) © Stelo Contributors
|