shellx-ai 1.0.12 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +586 -0
- package/dist/automation/element-finder.d.ts +189 -0
- package/dist/automation/element-finder.js +322 -0
- package/dist/automation/element-finder.js.map +1 -0
- package/dist/automation/ui-action-handler.d.ts +330 -0
- package/dist/automation/ui-action-handler.js +873 -0
- package/dist/automation/ui-action-handler.js.map +1 -0
- package/dist/cbor-compat.d.ts +27 -0
- package/dist/cbor-compat.js +108 -0
- package/dist/cbor-compat.js.map +1 -0
- package/dist/domain-manager.d.ts +80 -0
- package/dist/domain-manager.js +158 -0
- package/dist/domain-manager.js.map +1 -0
- package/dist/error-handler.d.ts +87 -0
- package/dist/error-handler.js +148 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/errors.d.ts +114 -0
- package/dist/errors.js +139 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +163 -54
- package/dist/index.js +706 -480
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +81 -0
- package/dist/logger.js +128 -0
- package/dist/logger.js.map +1 -0
- package/dist/protocol.d.ts +147 -31
- package/dist/protocol.js +2 -2
- package/dist/protocol.js.map +1 -0
- package/dist/shell/output-buffer.d.ts +152 -0
- package/dist/shell/output-buffer.js +163 -0
- package/dist/shell/output-buffer.js.map +1 -0
- package/dist/shell/shell-command-executor.d.ts +182 -0
- package/dist/shell/shell-command-executor.js +348 -0
- package/dist/shell/shell-command-executor.js.map +1 -0
- package/dist/shellx.d.ts +681 -178
- package/dist/shellx.js +762 -1159
- package/dist/shellx.js.map +1 -0
- package/dist/types.d.ts +132 -57
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -0
- package/dist/utils/retry-helper.d.ts +73 -0
- package/dist/utils/retry-helper.js +92 -0
- package/dist/utils/retry-helper.js.map +1 -0
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +17 -23
- package/dist/utils.js.map +1 -0
- package/package.json +95 -62
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 10cl <notice@toscl.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
# ShellX AI
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**A powerful TypeScript library for Android device automation and UI control**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.org/package/shellx-ai)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://www.typescriptlang.org/)
|
|
11
|
+
[](https://prettier.io)
|
|
12
|
+
|
|
13
|
+
[Features](#-features) • [Quick Start](#-quick-start) • [API Guide](#-api-guide) • [API Reference](#-api-reference) • [Examples](#-examples) • [Contributing](#-contributing)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🎯 Which API Should I Use?
|
|
20
|
+
|
|
21
|
+
ShellX provides two API levels. Choose the one that matches your needs:
|
|
22
|
+
|
|
23
|
+
### 🟢 ShellX (High-Level API) - **Recommended for 95% of users**
|
|
24
|
+
|
|
25
|
+
Perfect for most automation tasks:
|
|
26
|
+
- ✅ Simple, intuitive methods
|
|
27
|
+
- ✅ Automatic error handling & retry
|
|
28
|
+
- ✅ Type-safe with TypeScript
|
|
29
|
+
- ✅ Less code to write
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { ShellX } from 'shellx-ai';
|
|
33
|
+
|
|
34
|
+
const shellx = new ShellX({ deviceId: 'your-device-id' });
|
|
35
|
+
await shellx.connect();
|
|
36
|
+
|
|
37
|
+
// Easy to use!
|
|
38
|
+
await shellx.click('Submit');
|
|
39
|
+
await shellx.input({ text: 'Hello World' });
|
|
40
|
+
await shellx.swipe({ fromX: 500, fromY: 1000, toX: 500, toY: 500 });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 🔵 ConnectionClient (Low-Level API) - Advanced users only
|
|
44
|
+
|
|
45
|
+
For special cases requiring direct WebSocket access:
|
|
46
|
+
- Custom protocol implementations
|
|
47
|
+
- Debugging WebSocket communication
|
|
48
|
+
- Non-standard operations
|
|
49
|
+
|
|
50
|
+
**⚠️ Warning:** Steeper learning curve. Most users don't need this.
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { ConnectionClient } from 'shellx-ai';
|
|
54
|
+
|
|
55
|
+
const client = new ConnectionClient('device-id');
|
|
56
|
+
await client.ensureConnected();
|
|
57
|
+
|
|
58
|
+
// Low-level protocol access
|
|
59
|
+
await client.sendMessage({ click: { elementId: 'element123' } });
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**📚 See [API-GUIDE.md](./API-GUIDE.md) for detailed comparison and examples.**
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## ✨ Features
|
|
67
|
+
|
|
68
|
+
ShellX AI provides a comprehensive suite of tools for Android device automation:
|
|
69
|
+
|
|
70
|
+
- 🎯 **Smart UI Automation** - Advanced element finding with retry logic and multiple selector strategies
|
|
71
|
+
- 🔧 **Shell Command Execution** - Execute shell commands with real-time output monitoring
|
|
72
|
+
- 📸 **Screen Operations** - Screenshots, screen info, and visual element capture
|
|
73
|
+
- 🔄 **Automatic Retry Logic** - Built-in retry mechanism for robust operations
|
|
74
|
+
- 🛠️ **Modular Architecture** - Clean, maintainable code structure with separation of concerns
|
|
75
|
+
- 📝 **Type-Safe API** - Full TypeScript support with comprehensive type definitions
|
|
76
|
+
- 🌍 **Global Optimization** - Automatic domain selection based on user location
|
|
77
|
+
- 🧪 **Well-Tested** - Comprehensive test coverage for reliability
|
|
78
|
+
|
|
79
|
+
## 📦 Installation
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm install shellx-ai
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Requirements
|
|
86
|
+
|
|
87
|
+
- **Node.js**: >= 14.0.0
|
|
88
|
+
- **TypeScript**: >= 4.0.0 (recommended)
|
|
89
|
+
|
|
90
|
+
## 🚀 Quick Start
|
|
91
|
+
|
|
92
|
+
### Basic Usage
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { ShellX } from 'shellx-ai';
|
|
96
|
+
|
|
97
|
+
// Create ShellX instance
|
|
98
|
+
const shellx = new ShellX({
|
|
99
|
+
deviceId: 'your-device-id',
|
|
100
|
+
onOpen: () => console.log('Connected!'),
|
|
101
|
+
onMessage: (message) => console.log('Message:', message)
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Wait for connection to be ready
|
|
105
|
+
await shellx.ready();
|
|
106
|
+
|
|
107
|
+
// Execute shell command
|
|
108
|
+
const result = await shellx.command({ cmd: 'getprop ro.build.version.release' });
|
|
109
|
+
console.log('Android version:', result.output);
|
|
110
|
+
|
|
111
|
+
// Click UI element (simplified API)
|
|
112
|
+
await shellx.click('Settings');
|
|
113
|
+
|
|
114
|
+
// Or use full API
|
|
115
|
+
await shellx.click({ text: 'Settings', clickType: 'single' });
|
|
116
|
+
|
|
117
|
+
// Find elements
|
|
118
|
+
const elements = await shellx.find({
|
|
119
|
+
text: 'Submit',
|
|
120
|
+
multiple: true,
|
|
121
|
+
maxResults: 10
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
console.log(`Found ${elements.count} elements`);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### With Connection Callbacks
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { ShellX } from 'shellx-ai';
|
|
131
|
+
|
|
132
|
+
const shellx = new ShellX({
|
|
133
|
+
deviceId: 'your-device-id',
|
|
134
|
+
timeout: 5000,
|
|
135
|
+
reconnect: true,
|
|
136
|
+
reconnectMaxAttempts: 5,
|
|
137
|
+
onOpen: () => console.log('✅ Connected'),
|
|
138
|
+
onClose: () => console.log('❌ Disconnected'),
|
|
139
|
+
onError: (error) => console.error('⚠️ Error:', error),
|
|
140
|
+
onMessage: (message) => console.log('📨 Message:', message)
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Wait for connection
|
|
144
|
+
await shellx.ready();
|
|
145
|
+
|
|
146
|
+
// Ready to use!
|
|
147
|
+
await shellx.command('pm list packages');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 🎯 API Reference
|
|
151
|
+
|
|
152
|
+
### Core Classes
|
|
153
|
+
|
|
154
|
+
#### `ShellX`
|
|
155
|
+
|
|
156
|
+
Main class providing high-level automation utilities.
|
|
157
|
+
|
|
158
|
+
**Constructor:**
|
|
159
|
+
```typescript
|
|
160
|
+
constructor(options: ShellXOptions)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**ShellXOptions:**
|
|
164
|
+
```typescript
|
|
165
|
+
interface ShellXOptions {
|
|
166
|
+
deviceId: string; // Required: Device ID
|
|
167
|
+
timeout?: number; // Connection timeout (default: 5000ms)
|
|
168
|
+
reconnect?: boolean; // Enable auto-reconnect (default: true)
|
|
169
|
+
reconnectMaxAttempts?: number; // Max reconnect attempts (default: 5)
|
|
170
|
+
reconnectInterval?: number; // Reconnect interval (default: 1000ms)
|
|
171
|
+
pingInterval?: number; // Ping interval (default: 2000ms)
|
|
172
|
+
onOpen?: () => void; // Connection opened callback
|
|
173
|
+
onClose?: () => void; // Connection closed callback
|
|
174
|
+
onError?: (error?: Event) => void; // Error callback
|
|
175
|
+
onReconnectFailed?: () => void; // Reconnect failed callback
|
|
176
|
+
onMessage?: (message: WsServer) => void; // Message callback
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Methods:**
|
|
181
|
+
|
|
182
|
+
| Method | Description | Return Type |
|
|
183
|
+
|--------|-------------|-------------|
|
|
184
|
+
| `ready()` | Wait for connection to be ready | `Promise<void>` |
|
|
185
|
+
| `click(data)` | Click element by ID, coordinates, or selector | `Promise<ActionResult>` |
|
|
186
|
+
| `input(data)` | Input text into element | `Promise<ActionResult>` |
|
|
187
|
+
| `swipe(data)` | Perform swipe gesture | `Promise<ActionResult>` |
|
|
188
|
+
| `pressKey(data)` | Press hardware key | `Promise<ActionResult>` |
|
|
189
|
+
| `wait(data)` | Wait for element condition | `Promise<ActionResult>` |
|
|
190
|
+
| `find(data)` | Find UI elements | `Promise<FindResult>` |
|
|
191
|
+
| `clipboard(data)` | Clipboard operations (get/set/paste) | `Promise<ActionResult>` |
|
|
192
|
+
| `takeScreenshot(data)` | Capture screenshot | `Promise<ActionResult>` |
|
|
193
|
+
| `getScreenInfo()` | Get screen information | `Promise<ScreenInfoResponse>` |
|
|
194
|
+
| `executeActions(actions)` | Execute multiple actions in sequence | `Promise<ActionResult[]>` |
|
|
195
|
+
|
|
196
|
+
**Element Finder Methods:**
|
|
197
|
+
|
|
198
|
+
| Method | Description | Return Type |
|
|
199
|
+
|--------|-------------|-------------|
|
|
200
|
+
| `findElementWithRetry(selector, maxRetries, retryDelay)` | Find single element with retry | `Promise<UIElement \| null>` |
|
|
201
|
+
| `findElementsWithRetry(selector, maxRetries, retryDelay, options)` | Find multiple elements | `Promise<UIElement[]>` |
|
|
202
|
+
| `waitForAnyElement(selectors, timeout)` | Wait for any element to appear | `Promise<{element, selectorIndex} \| null>` |
|
|
203
|
+
| `scrollToFindElement(selector, maxScrolls, direction)` | Scroll to find element | `Promise<UIElement \| null>` |
|
|
204
|
+
|
|
205
|
+
**Shell Command Methods:**
|
|
206
|
+
|
|
207
|
+
| Method | Description | Return Type |
|
|
208
|
+
|--------|-------------|-------------|
|
|
209
|
+
| `executeShellCommand(command, options)` | Execute shell command with monitoring | `Promise<ShellCommandResult>` |
|
|
210
|
+
| `executeSimpleShellCommand(command, options)` | Execute simple shell command | `Promise<ShellCommandResult>` |
|
|
211
|
+
| `executeShellCommands(commands, options)` | Execute multiple commands | `Promise<ShellCommandResult[]>` |
|
|
212
|
+
| `adbCommand(command, options)` | Execute ADB command | `Promise<ShellCommandResult>` |
|
|
213
|
+
| `executeCode(code, context, timeout)` | Execute code in sandboxed environment | `Promise<any>` |
|
|
214
|
+
|
|
215
|
+
**Device Info Methods:**
|
|
216
|
+
|
|
217
|
+
| Method | Description | Return Type |
|
|
218
|
+
|--------|-------------|-------------|
|
|
219
|
+
| `getDeviceInfo()` | Get comprehensive device information | `Promise<ShellCommandResult[]>` |
|
|
220
|
+
| `getDeviceModel()` | Get device model | `Promise<string \| undefined>` |
|
|
221
|
+
| `getAndroidVersion()` | Get Android version | `Promise<string \| undefined>` |
|
|
222
|
+
| `getScreenSize()` | Get screen size | `Promise<string \| undefined>` |
|
|
223
|
+
| `getBatteryInfo()` | Get battery information | `Promise<BatteryInfo \| undefined>` |
|
|
224
|
+
| `getDeviceInfoSummary()` | Get device information summary | `Promise<DeviceInfo>` |
|
|
225
|
+
|
|
226
|
+
**Navigation Methods:**
|
|
227
|
+
|
|
228
|
+
| Method | Description | Return Type |
|
|
229
|
+
|--------|-------------|-------------|
|
|
230
|
+
| `navigateByPath(textPath)` | Navigate using text path | `Promise<boolean>` |
|
|
231
|
+
| `clickByText(text, exact)` | Click element by text | `Promise<boolean>` |
|
|
232
|
+
| `inputText(selector, text, options)` | Input text into field | `Promise<boolean>` |
|
|
233
|
+
|
|
234
|
+
### Type Definitions
|
|
235
|
+
|
|
236
|
+
#### `Click`
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
interface Click {
|
|
240
|
+
targetElementId?: string;
|
|
241
|
+
targetResourceId?: string;
|
|
242
|
+
targetText?: string;
|
|
243
|
+
targetClass?: string;
|
|
244
|
+
targetX?: number;
|
|
245
|
+
targetY?: number;
|
|
246
|
+
clickType?: 'single' | 'double' | 'long';
|
|
247
|
+
wait?: number;
|
|
248
|
+
retry?: number;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### `Input`
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
interface Input {
|
|
256
|
+
targetElementId?: string;
|
|
257
|
+
targetResourceId?: string;
|
|
258
|
+
targetText?: string;
|
|
259
|
+
targetClass?: string;
|
|
260
|
+
text: string;
|
|
261
|
+
clear?: boolean;
|
|
262
|
+
hideKeyboard?: boolean;
|
|
263
|
+
wait?: number;
|
|
264
|
+
retry?: number;
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### `Swipe`
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
interface Swipe {
|
|
272
|
+
fromX: number;
|
|
273
|
+
fromY: number;
|
|
274
|
+
toX: number;
|
|
275
|
+
toY: number;
|
|
276
|
+
duration?: number;
|
|
277
|
+
wait?: number;
|
|
278
|
+
retry?: number;
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### `Command`
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
interface Command {
|
|
286
|
+
cmd: string;
|
|
287
|
+
timeout?: number;
|
|
288
|
+
wait?: number;
|
|
289
|
+
retry?: number;
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### `ActionResult`
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
interface ActionResult {
|
|
297
|
+
success: boolean;
|
|
298
|
+
data?: any;
|
|
299
|
+
error?: string;
|
|
300
|
+
duration: number;
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## 📚 Examples
|
|
305
|
+
|
|
306
|
+
### Example 1: UI Automation Workflow
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { createShellXWithShellMonitoring } from 'shellx-ai';
|
|
310
|
+
|
|
311
|
+
async function automateApp() {
|
|
312
|
+
const shellx = await createShellXWithShellMonitoring({
|
|
313
|
+
deviceId: process.env.SHELLX_DEVICE_ID
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
// Navigate to settings
|
|
318
|
+
await shellx.clickByText('Settings');
|
|
319
|
+
await shellx.wait({ targetText: 'Accounts', condition: 'visible', timeout: 5000 });
|
|
320
|
+
|
|
321
|
+
// Click on account item
|
|
322
|
+
await shellx.click({ targetText: 'Accounts' });
|
|
323
|
+
|
|
324
|
+
// Input text
|
|
325
|
+
await shellx.input({
|
|
326
|
+
targetResourceId: 'username_field',
|
|
327
|
+
text: 'user@example.com',
|
|
328
|
+
clear: true
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Take screenshot
|
|
332
|
+
await shellx.takeScreenshot({
|
|
333
|
+
format: 'png',
|
|
334
|
+
quality: 90,
|
|
335
|
+
saveToFile: true
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
console.log('Automation completed successfully!');
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error('Automation failed:', error);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
automateApp();
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Example 2: Batch Command Execution
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { createShellX } from 'shellx-ai';
|
|
351
|
+
import ConnectionTaskClient from 'shellx-ai';
|
|
352
|
+
|
|
353
|
+
async function executeDeviceCommands() {
|
|
354
|
+
const client = new ConnectionTaskClient('device-id');
|
|
355
|
+
await client.waitForInitialization();
|
|
356
|
+
const shellx = createShellX(client);
|
|
357
|
+
|
|
358
|
+
// Execute multiple commands
|
|
359
|
+
const commands = [
|
|
360
|
+
{ command: 'pm list packages', title: 'List packages' },
|
|
361
|
+
{ command: 'dumpsys battery', title: 'Get battery info' },
|
|
362
|
+
{ command: 'wm size', title: 'Get screen size' }
|
|
363
|
+
];
|
|
364
|
+
|
|
365
|
+
const results = await shellx.executeShellCommands(commands, {
|
|
366
|
+
continueOnError: true,
|
|
367
|
+
timeout: 10000
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
results.forEach((result, index) => {
|
|
371
|
+
console.log(`\n${commands[index].title}:`);
|
|
372
|
+
console.log(`Success: ${result.success}`);
|
|
373
|
+
console.log(`Output: ${result.output}`);
|
|
374
|
+
if (result.error) {
|
|
375
|
+
console.error(`Error: ${result.error}`);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
executeDeviceCommands();
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Example 3: Advanced Element Finding
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
async function findAndInteract() {
|
|
387
|
+
const shellx = await createShellXWithShellMonitoring({
|
|
388
|
+
deviceId: 'device-id'
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// Find element with retry
|
|
392
|
+
const element = await shellx.findElementWithRetry(
|
|
393
|
+
{ text: 'Submit Button', visible: true, clickable: true },
|
|
394
|
+
5, // max retries
|
|
395
|
+
1000 // retry delay
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
if (element) {
|
|
399
|
+
console.log('Found element:', element.elementId);
|
|
400
|
+
shellx.printElementInfo(element);
|
|
401
|
+
} else {
|
|
402
|
+
console.log('Element not found');
|
|
403
|
+
|
|
404
|
+
// Try scrolling to find it
|
|
405
|
+
const foundElement = await shellx.scrollToFindElement(
|
|
406
|
+
{ text: 'Submit Button' },
|
|
407
|
+
5,
|
|
408
|
+
'down'
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
if (foundElement) {
|
|
412
|
+
console.log('Found after scrolling!');
|
|
413
|
+
await shellx.click({ targetElementId: foundElement.elementId });
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
findAndInteract();
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Example 4: Action Sequence
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
async function executeActionSequence() {
|
|
425
|
+
const shellx = await createShellXWithShellMonitoring({
|
|
426
|
+
deviceId: 'device-id'
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Execute multiple actions
|
|
430
|
+
const actions = [
|
|
431
|
+
// Click settings
|
|
432
|
+
{ targetText: 'Settings' },
|
|
433
|
+
|
|
434
|
+
// Wait for menu
|
|
435
|
+
{ targetText: 'Network', condition: 'visible', timeout: 5000 },
|
|
436
|
+
|
|
437
|
+
// Click network
|
|
438
|
+
{ targetText: 'Network' },
|
|
439
|
+
|
|
440
|
+
// Execute command
|
|
441
|
+
{ cmd: 'dumpsys wifi' },
|
|
442
|
+
|
|
443
|
+
// Take screenshot
|
|
444
|
+
{ format: 'png' }
|
|
445
|
+
];
|
|
446
|
+
|
|
447
|
+
const results = await shellx.executeActions(actions);
|
|
448
|
+
|
|
449
|
+
results.forEach((result, index) => {
|
|
450
|
+
const status = result.success ? '✅' : '❌';
|
|
451
|
+
console.log(`Action ${index + 1}: ${status}`);
|
|
452
|
+
if (!result.success) {
|
|
453
|
+
console.error(` Error: ${result.error}`);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
executeActionSequence();
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## 🛠️ Development
|
|
462
|
+
|
|
463
|
+
### Setup
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
# Clone repository
|
|
467
|
+
git clone https://github.com/10cl/shellx.git
|
|
468
|
+
cd shellx
|
|
469
|
+
|
|
470
|
+
# Install dependencies
|
|
471
|
+
npm install
|
|
472
|
+
|
|
473
|
+
# Build project
|
|
474
|
+
npm run build
|
|
475
|
+
|
|
476
|
+
# Run tests
|
|
477
|
+
npm test
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Available Scripts
|
|
481
|
+
|
|
482
|
+
| Script | Description |
|
|
483
|
+
|--------|-------------|
|
|
484
|
+
| `npm run build` | Compile TypeScript to JavaScript |
|
|
485
|
+
| `npm run build:watch` | Watch mode for compilation |
|
|
486
|
+
| `npm run type-check` | Type check without emitting files |
|
|
487
|
+
| `npm run lint` | Run ESLint |
|
|
488
|
+
| `npm run lint:fix` | Fix ESLint errors automatically |
|
|
489
|
+
| `npm run format` | Format code with Prettier |
|
|
490
|
+
| `npm run format:check` | Check code formatting |
|
|
491
|
+
| `npm test` | Run tests |
|
|
492
|
+
| `npm run test:coverage` | Run tests with coverage |
|
|
493
|
+
| `npm run clean` | Remove dist directory |
|
|
494
|
+
|
|
495
|
+
### Project Structure
|
|
496
|
+
|
|
497
|
+
```
|
|
498
|
+
shellx-ai/
|
|
499
|
+
├── src/
|
|
500
|
+
│ ├── automation/
|
|
501
|
+
│ │ ├── element-finder.ts # Element finding with retry logic
|
|
502
|
+
│ │ ├── ui-action-handler.ts # UI action execution
|
|
503
|
+
│ │ └── device-info-helper.ts # Device information retrieval
|
|
504
|
+
│ ├── shell/
|
|
505
|
+
│ │ ├── output-buffer.ts # Shell output buffering
|
|
506
|
+
│ │ └── shell-command-executor.ts # Shell command execution
|
|
507
|
+
│ ├── utils/
|
|
508
|
+
│ │ └── retry-helper.ts # Generic retry mechanism
|
|
509
|
+
│ ├── index.ts # Main connection client
|
|
510
|
+
│ ├── shellx.ts # Main ShellX class
|
|
511
|
+
│ ├── protocol.ts # Protocol type definitions
|
|
512
|
+
│ ├── types.ts # Simplified type definitions
|
|
513
|
+
│ ├── domain-manager.ts # Domain management
|
|
514
|
+
│ └── utils.ts # Utility functions
|
|
515
|
+
├── dist/ # Compiled output
|
|
516
|
+
├── package.json
|
|
517
|
+
├── tsconfig.json
|
|
518
|
+
├── jest.config.js
|
|
519
|
+
├── .eslintrc.json
|
|
520
|
+
├── .prettierrc.json
|
|
521
|
+
└── README.md
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## 🧪 Testing
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
# Run all tests
|
|
528
|
+
npm test
|
|
529
|
+
|
|
530
|
+
# Run tests in watch mode
|
|
531
|
+
npm run test:watch
|
|
532
|
+
|
|
533
|
+
# Generate coverage report
|
|
534
|
+
npm run test:coverage
|
|
535
|
+
|
|
536
|
+
# Type checking
|
|
537
|
+
npm run type-check
|
|
538
|
+
|
|
539
|
+
# Linting
|
|
540
|
+
npm run lint
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## 🤝 Contributing
|
|
544
|
+
|
|
545
|
+
We welcome contributions! Please follow these steps:
|
|
546
|
+
|
|
547
|
+
1. Fork the repository
|
|
548
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
549
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
550
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
551
|
+
5. Open a Pull Request
|
|
552
|
+
|
|
553
|
+
### Code Style
|
|
554
|
+
|
|
555
|
+
- Use TypeScript for all new features
|
|
556
|
+
- Follow ESLint rules (`npm run lint`)
|
|
557
|
+
- Format code with Prettier (`npm run format`)
|
|
558
|
+
- Add JSDoc comments for public APIs
|
|
559
|
+
- Write tests for new functionality
|
|
560
|
+
- Ensure all tests pass before submitting
|
|
561
|
+
|
|
562
|
+
## 📝 License
|
|
563
|
+
|
|
564
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
565
|
+
|
|
566
|
+
## 🆘 Support
|
|
567
|
+
|
|
568
|
+
- 📚 [Documentation](https://github.com/10cl/shellx/wiki)
|
|
569
|
+
- 🐛 [Issue Tracker](https://github.com/10cl/shellx/issues)
|
|
570
|
+
- 💬 [Discussions](https://github.com/10cl/shellx/discussions)
|
|
571
|
+
|
|
572
|
+
## 🌟 Acknowledgments
|
|
573
|
+
|
|
574
|
+
- Built with [TypeScript](https://www.typescriptlang.org/)
|
|
575
|
+
- Powered by [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
|
576
|
+
- Tested with [Jest](https://jestjs.io/)
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
<div align="center">
|
|
581
|
+
|
|
582
|
+
**Built with ❤️ for the automation community**
|
|
583
|
+
|
|
584
|
+
[⬆ Back to Top](#shellx-ai)
|
|
585
|
+
|
|
586
|
+
</div>
|