shellx-ai 1.1.1 → 1.1.2
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/README.md +162 -666
- package/dist/automation/element-finder.d.ts +0 -8
- package/dist/automation/element-finder.js +0 -7
- package/dist/automation/element-finder.js.map +1 -1
- package/dist/automation/ui-action-handler.d.ts +1 -1
- package/dist/automation/ui-action-handler.js +58 -31
- package/dist/automation/ui-action-handler.js.map +1 -1
- package/dist/cbor-compat.js +5 -10
- package/dist/cbor-compat.js.map +1 -1
- package/dist/data/index.d.ts +9 -0
- package/dist/data/index.js +11 -0
- package/dist/data/index.js.map +1 -0
- package/dist/data/types.d.ts +351 -0
- package/dist/data/types.js +8 -0
- package/dist/data/types.js.map +1 -0
- package/dist/domain-manager.js +4 -5
- package/dist/domain-manager.js.map +1 -1
- package/dist/errors.js +4 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +36 -26
- package/dist/index.js +193 -53
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +27 -5
- package/dist/shellx.d.ts +139 -56
- package/dist/shellx.js +201 -88
- package/dist/shellx.js.map +1 -1
- package/dist/types.d.ts +38 -1
- package/package.json +25 -4
- package/dist/shell/output-buffer.d.ts +0 -152
- package/dist/shell/output-buffer.js +0 -176
- package/dist/shell/output-buffer.js.map +0 -1
- package/dist/shell/shell-command-executor.d.ts +0 -182
- package/dist/shell/shell-command-executor.js +0 -404
- package/dist/shell/shell-command-executor.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,666 +1,162 @@
|
|
|
1
|
-
# ShellX
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|--------|-------------|---------|
|
|
164
|
-
| `ready()` | Wait for connection to be ready | `Promise<void>` |
|
|
165
|
-
| `getClient()` | Get underlying ConnectionClient | `ConnectionClient` |
|
|
166
|
-
|
|
167
|
-
##### UI Actions
|
|
168
|
-
|
|
169
|
-
| Method | Description | Example |
|
|
170
|
-
|--------|-------------|---------|
|
|
171
|
-
| `click(selector)` | Click element | `await shellx.click('Submit')` |
|
|
172
|
-
| `input(data)` | Input text | `await shellx.input({ text: 'Hello' })` |
|
|
173
|
-
| `swipe(data)` | Swipe gesture | `await shellx.swipe({ fromX: 500, fromY: 1000, toX: 500, toY: 500 })` |
|
|
174
|
-
| `press(key)` | Press key | `await shellx.press('BACK')` |
|
|
175
|
-
| `wait(selector)` | Wait for element | `await shellx.wait('Loading')` |
|
|
176
|
-
| `find(selector)` | Find elements | `await shellx.find('Button', { multiple: true })` |
|
|
177
|
-
|
|
178
|
-
##### Device Operations
|
|
179
|
-
|
|
180
|
-
| Method | Description | Example |
|
|
181
|
-
|--------|-------------|---------|
|
|
182
|
-
| `command(cmd)` | Execute shell command | `await shellx.command('ls -la')` |
|
|
183
|
-
| `getScreenInfo()` | Get screen info | `await shellx.getScreenInfo()` |
|
|
184
|
-
| `takeScreenshot()` | Take screenshot | `await shellx.takeScreenshot({ format: 'png' })` |
|
|
185
|
-
| `getAppInfo(pkg)` | Get app info | `await shellx.getAppInfo('com.example.app')` |
|
|
186
|
-
| `getAppList()` | Get app list | `await shellx.getAppList()` |
|
|
187
|
-
| `clipboard(data)` | Clipboard operations | `await shellx.clipboard({ text: 'Hello' })` |
|
|
188
|
-
|
|
189
|
-
##### Advanced
|
|
190
|
-
|
|
191
|
-
| Method | Description | Example |
|
|
192
|
-
|--------|-------------|---------|
|
|
193
|
-
| `executeActions(actions)` | Execute multiple actions | `await shellx.executeActions([{ text: 'OK' }, { cmd: 'ls' }])` |
|
|
194
|
-
| `sendRawMessage(msg)` | Send raw WebSocket message | `await shellx.sendRawMessage({ screenInfo: {} })` |
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Type Definitions
|
|
199
|
-
|
|
200
|
-
### Click
|
|
201
|
-
|
|
202
|
-
Click on an element by text, ID, or coordinates.
|
|
203
|
-
|
|
204
|
-
**Simplified form (click by text):**
|
|
205
|
-
```typescript
|
|
206
|
-
await shellx.click('Submit');
|
|
207
|
-
await shellx.click('Submit', { clickType: 'long' });
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
**Full form:**
|
|
211
|
-
```typescript
|
|
212
|
-
await shellx.click({
|
|
213
|
-
text: 'Submit', // Click by text
|
|
214
|
-
elementId: 'btn123', // Or by element ID
|
|
215
|
-
resourceId: 'submit_btn', // Or by resource ID
|
|
216
|
-
x: 500, // Or by coordinates
|
|
217
|
-
y: 1000,
|
|
218
|
-
clickType: 'single', // 'single' | 'double' | 'long' | 'normal'
|
|
219
|
-
timeout: 5000
|
|
220
|
-
});
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Input
|
|
224
|
-
|
|
225
|
-
Input text into a field.
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
await shellx.input({
|
|
229
|
-
elementId: 'field123',
|
|
230
|
-
text: 'Hello World',
|
|
231
|
-
clear: true, // Clear field before input
|
|
232
|
-
hideKeyboard: true // Hide keyboard after input
|
|
233
|
-
});
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Swipe
|
|
237
|
-
|
|
238
|
-
Perform swipe gesture.
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
await shellx.swipe({
|
|
242
|
-
fromX: 500,
|
|
243
|
-
fromY: 1000,
|
|
244
|
-
toX: 500,
|
|
245
|
-
toY: 500,
|
|
246
|
-
duration: 800 // Duration in ms
|
|
247
|
-
});
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Press
|
|
251
|
-
|
|
252
|
-
Press hardware key.
|
|
253
|
-
|
|
254
|
-
**Simplified:**
|
|
255
|
-
```typescript
|
|
256
|
-
await shellx.press('BACK');
|
|
257
|
-
await shellx.press('HOME');
|
|
258
|
-
await shellx.press('MENU', { longPress: true });
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
**Full:**
|
|
262
|
-
```typescript
|
|
263
|
-
await shellx.press({
|
|
264
|
-
key: 'BACK',
|
|
265
|
-
longPress: false
|
|
266
|
-
});
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### Wait
|
|
270
|
-
|
|
271
|
-
Wait for an element condition.
|
|
272
|
-
|
|
273
|
-
**Simplified:**
|
|
274
|
-
```typescript
|
|
275
|
-
await shellx.wait('Submit');
|
|
276
|
-
await shellx.wait('Loading', { timeout: 10000 });
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
**Full:**
|
|
280
|
-
```typescript
|
|
281
|
-
await shellx.wait({
|
|
282
|
-
text: 'Submit',
|
|
283
|
-
condition: 'visible', // 'visible' | 'gone' | 'enabled'
|
|
284
|
-
timeout: 5000
|
|
285
|
-
});
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Find
|
|
289
|
-
|
|
290
|
-
Find UI elements.
|
|
291
|
-
|
|
292
|
-
**Simplified:**
|
|
293
|
-
```typescript
|
|
294
|
-
const result = await shellx.find('Button');
|
|
295
|
-
const all = await shellx.find('Button', { multiple: true, maxResults: 10 });
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
**Full:**
|
|
299
|
-
```typescript
|
|
300
|
-
const result = await shellx.find({
|
|
301
|
-
text: 'Submit',
|
|
302
|
-
multiple: false,
|
|
303
|
-
maxResults: 100,
|
|
304
|
-
pressClick: true // Auto-click after find
|
|
305
|
-
});
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### Command
|
|
309
|
-
|
|
310
|
-
Execute shell command.
|
|
311
|
-
|
|
312
|
-
**Simplified:**
|
|
313
|
-
```typescript
|
|
314
|
-
const result = await shellx.command('ls -la');
|
|
315
|
-
const result = await shellx.command('ls -la', { timeout: 5000 });
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
**Full:**
|
|
319
|
-
```typescript
|
|
320
|
-
const result = await shellx.command({
|
|
321
|
-
cmd: 'ls -la',
|
|
322
|
-
timeout: 10000,
|
|
323
|
-
wait: 1000 // Wait after command
|
|
324
|
-
});
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### Screenshot
|
|
328
|
-
|
|
329
|
-
Take screenshot.
|
|
330
|
-
|
|
331
|
-
```typescript
|
|
332
|
-
const screenshot = await shellx.takeScreenshot({
|
|
333
|
-
format: 'png', // 'png' | 'jpeg'
|
|
334
|
-
quality: 100, // 0-100 for JPEG
|
|
335
|
-
saveToFile: true
|
|
336
|
-
});
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### Clipboard
|
|
340
|
-
|
|
341
|
-
Clipboard operations.
|
|
342
|
-
|
|
343
|
-
```typescript
|
|
344
|
-
// Get clipboard
|
|
345
|
-
const result = await shellx.clipboard({ get: true });
|
|
346
|
-
|
|
347
|
-
// Set clipboard
|
|
348
|
-
await shellx.clipboard({ text: 'Hello' });
|
|
349
|
-
|
|
350
|
-
// Paste clipboard
|
|
351
|
-
await shellx.clipboard({ paste: true });
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### ExecuteActions (Batch)
|
|
355
|
-
|
|
356
|
-
Execute multiple actions in sequence.
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
const result = await shellx.executeActions([
|
|
360
|
-
{ text: 'Settings' }, // Click
|
|
361
|
-
{ cmd: 'ls -la' }, // Command
|
|
362
|
-
{ fromX: 500, fromY: 1000, toX: 500, toY: 500 }, // Swipe
|
|
363
|
-
{ key: 'BACK' } // Press
|
|
364
|
-
]);
|
|
365
|
-
|
|
366
|
-
console.log(`Success: ${result.successCount}/${result.results.length}`);
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
---
|
|
370
|
-
|
|
371
|
-
## Complete Working Example
|
|
372
|
-
|
|
373
|
-
Here's a complete example that you can run directly:
|
|
374
|
-
|
|
375
|
-
```typescript
|
|
376
|
-
// File: example.ts
|
|
377
|
-
import { ShellX } from 'shellx-ai';
|
|
378
|
-
|
|
379
|
-
async function main() {
|
|
380
|
-
// 1. Create ShellX instance
|
|
381
|
-
const shellx = new ShellX({
|
|
382
|
-
deviceId: process.env.DEVICE_ID || 'your-device-id',
|
|
383
|
-
onOpen: () => console.log('✅ Connected!')
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
try {
|
|
387
|
-
// 2. Wait for connection
|
|
388
|
-
await shellx.ready();
|
|
389
|
-
console.log('📱 Connected to device');
|
|
390
|
-
|
|
391
|
-
// 3. Get screen info
|
|
392
|
-
const screen = await shellx.getScreenInfo();
|
|
393
|
-
console.log(`Screen: ${screen.width}x${screen.height}`);
|
|
394
|
-
|
|
395
|
-
// 4. Click element
|
|
396
|
-
await shellx.click('Settings');
|
|
397
|
-
console.log('✅ Clicked Settings');
|
|
398
|
-
|
|
399
|
-
// 5. Press back
|
|
400
|
-
await shellx.press('BACK');
|
|
401
|
-
console.log('✅ Pressed BACK');
|
|
402
|
-
|
|
403
|
-
// 6. Execute command
|
|
404
|
-
const result = await shellx.command('getprop ro.build.version.release');
|
|
405
|
-
console.log(`Android: ${result.output.trim()}`);
|
|
406
|
-
|
|
407
|
-
// 7. Take screenshot
|
|
408
|
-
const screenshot = await shellx.takeScreenshot({ format: 'png' });
|
|
409
|
-
console.log(`Screenshot: ${screenshot.imagePath}`);
|
|
410
|
-
|
|
411
|
-
// 8. Batch operations
|
|
412
|
-
const batch = await shellx.executeActions([
|
|
413
|
-
{ text: 'Settings' },
|
|
414
|
-
{ cmd: 'wm size' }
|
|
415
|
-
]);
|
|
416
|
-
console.log(`Batch: ${batch.successCount}/${batch.results.length} successful`);
|
|
417
|
-
|
|
418
|
-
console.log('🎉 All operations completed!');
|
|
419
|
-
} catch (error) {
|
|
420
|
-
console.error('❌ Error:', error);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
main();
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
Compile and run:
|
|
428
|
-
|
|
429
|
-
```bash
|
|
430
|
-
# Install dependencies
|
|
431
|
-
npm install shellx-ai dotenv
|
|
432
|
-
|
|
433
|
-
# Create .env file
|
|
434
|
-
echo "DEVICE_ID=your-device-id" > .env
|
|
435
|
-
|
|
436
|
-
# Run with tsx
|
|
437
|
-
npx tsx example.ts
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
---
|
|
441
|
-
|
|
442
|
-
## Response Types
|
|
443
|
-
|
|
444
|
-
All operations return a result object with the following structure:
|
|
445
|
-
|
|
446
|
-
```typescript
|
|
447
|
-
interface Result {
|
|
448
|
-
success: boolean; // Operation success status
|
|
449
|
-
error?: string; // Error message if failed
|
|
450
|
-
duration: number; // Operation duration in ms
|
|
451
|
-
timestamp: number; // Operation timestamp
|
|
452
|
-
// ... additional fields specific to each operation
|
|
453
|
-
}
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
### CommandResult
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
|
-
{
|
|
460
|
-
success: boolean;
|
|
461
|
-
output: string; // Command output
|
|
462
|
-
error?: string;
|
|
463
|
-
exitCode?: number;
|
|
464
|
-
duration: number;
|
|
465
|
-
cmd: string;
|
|
466
|
-
timestamp: number;
|
|
467
|
-
}
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
### FindResult
|
|
471
|
-
|
|
472
|
-
```typescript
|
|
473
|
-
{
|
|
474
|
-
success: boolean;
|
|
475
|
-
found: boolean;
|
|
476
|
-
count: number; // Number of elements found
|
|
477
|
-
elements: Array<{
|
|
478
|
-
id: string;
|
|
479
|
-
text: string;
|
|
480
|
-
class: string;
|
|
481
|
-
left: number;
|
|
482
|
-
top: number;
|
|
483
|
-
right: number;
|
|
484
|
-
bottom: number;
|
|
485
|
-
visible: boolean;
|
|
486
|
-
clickable: boolean;
|
|
487
|
-
}>;
|
|
488
|
-
duration: number;
|
|
489
|
-
timestamp: number;
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### ScreenInfoResult
|
|
494
|
-
|
|
495
|
-
```typescript
|
|
496
|
-
{
|
|
497
|
-
success: boolean;
|
|
498
|
-
width: number;
|
|
499
|
-
height: number;
|
|
500
|
-
density: number;
|
|
501
|
-
screenOn: boolean;
|
|
502
|
-
screenUnlocked: boolean;
|
|
503
|
-
foregroundApp?: string;
|
|
504
|
-
foregroundActivity?: string;
|
|
505
|
-
model?: string;
|
|
506
|
-
androidVersion?: string;
|
|
507
|
-
manufacturer?: string;
|
|
508
|
-
duration: number;
|
|
509
|
-
timestamp: number;
|
|
510
|
-
}
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
---
|
|
514
|
-
|
|
515
|
-
## Logging
|
|
516
|
-
|
|
517
|
-
Control logging level:
|
|
518
|
-
|
|
519
|
-
```typescript
|
|
520
|
-
const shellx = new ShellX({
|
|
521
|
-
deviceId: 'your-device-id',
|
|
522
|
-
logLevel: 3 // 0=NONE, 1=ERROR, 2=WARN, 3=INFO, 4=DEBUG
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
// Or at runtime
|
|
526
|
-
shellx.setLogLevel(4); // Enable debug logging
|
|
527
|
-
shellx.enableDebugLogging(); // Shortcut
|
|
528
|
-
shellx.disableLogging(); // Disable all logging
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
---
|
|
532
|
-
|
|
533
|
-
## Error Handling
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
try {
|
|
537
|
-
await shellx.click('Settings');
|
|
538
|
-
} catch (error) {
|
|
539
|
-
if (error instanceof Error) {
|
|
540
|
-
console.error('Click failed:', error.message);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Or check result
|
|
545
|
-
const result = await shellx.click('Settings');
|
|
546
|
-
if (!result.success) {
|
|
547
|
-
console.error('Failed:', result.error);
|
|
548
|
-
}
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
---
|
|
552
|
-
|
|
553
|
-
## Advanced Usage
|
|
554
|
-
|
|
555
|
-
### Wait for Multiple Elements
|
|
556
|
-
|
|
557
|
-
```typescript
|
|
558
|
-
const result = await shellx.waitAnyElement([
|
|
559
|
-
{ text: 'OK' },
|
|
560
|
-
{ text: 'Confirm' },
|
|
561
|
-
{ text: 'Submit' }
|
|
562
|
-
], 10000);
|
|
563
|
-
```
|
|
564
|
-
|
|
565
|
-
### Scroll to Find Element
|
|
566
|
-
|
|
567
|
-
```typescript
|
|
568
|
-
const element = await shellx.scrollToFindElement(
|
|
569
|
-
{ text: 'Target Item' },
|
|
570
|
-
5, // max scroll attempts
|
|
571
|
-
'down' // direction
|
|
572
|
-
);
|
|
573
|
-
```
|
|
574
|
-
|
|
575
|
-
### Find with Retry
|
|
576
|
-
|
|
577
|
-
```typescript
|
|
578
|
-
const element = await shellx.findElementWithRetry(
|
|
579
|
-
{ text: 'Submit', visible: true },
|
|
580
|
-
3, // max retries
|
|
581
|
-
1000 // retry delay
|
|
582
|
-
);
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
---
|
|
586
|
-
|
|
587
|
-
## Platform Support
|
|
588
|
-
|
|
589
|
-
### Node.js
|
|
590
|
-
|
|
591
|
-
```bash
|
|
592
|
-
npm install shellx-ai ws
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
```typescript
|
|
596
|
-
import { ShellX } from 'shellx-ai';
|
|
597
|
-
const shellx = new ShellX({ deviceId: 'device-id' });
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
### Browser
|
|
601
|
-
|
|
602
|
-
```bash
|
|
603
|
-
npm install shellx-ai
|
|
604
|
-
```
|
|
605
|
-
|
|
606
|
-
```typescript
|
|
607
|
-
import { ShellX } from 'shellx-ai';
|
|
608
|
-
const shellx = new ShellX({ deviceId: 'device-id' });
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
---
|
|
612
|
-
|
|
613
|
-
## Troubleshooting
|
|
614
|
-
|
|
615
|
-
### Connection Issues
|
|
616
|
-
|
|
617
|
-
```typescript
|
|
618
|
-
const shellx = new ShellX({
|
|
619
|
-
deviceId: 'your-device-id',
|
|
620
|
-
timeout: 10000, // Increase timeout
|
|
621
|
-
reconnect: true, // Enable auto-reconnect
|
|
622
|
-
reconnectMaxAttempts: 10, // More attempts
|
|
623
|
-
onError: (error) => console.error('Connection error:', error)
|
|
624
|
-
});
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
### Element Not Found
|
|
628
|
-
|
|
629
|
-
```typescript
|
|
630
|
-
// Wait for element first
|
|
631
|
-
await shellx.wait('Submit', { timeout: 10000 });
|
|
632
|
-
|
|
633
|
-
// Then click
|
|
634
|
-
await shellx.click('Submit');
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
### Command Timeout
|
|
638
|
-
|
|
639
|
-
```typescript
|
|
640
|
-
const result = await shellx.command({
|
|
641
|
-
cmd: 'long-running-command',
|
|
642
|
-
timeout: 30000 // 30 seconds
|
|
643
|
-
});
|
|
644
|
-
```
|
|
645
|
-
|
|
646
|
-
---
|
|
647
|
-
|
|
648
|
-
## See Also
|
|
649
|
-
|
|
650
|
-
- [API-GUIDE.md](./API-GUIDE.md) - Detailed API guide
|
|
651
|
-
- [API-QUICK-REFERENCE.md](./API-QUICK-REFERENCE.md) - Quick reference
|
|
652
|
-
- [example/](./example/) - Complete working examples
|
|
653
|
-
|
|
654
|
-
---
|
|
655
|
-
|
|
656
|
-
## License
|
|
657
|
-
|
|
658
|
-
MIT License - see [LICENSE](LICENSE) file.
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
## Support
|
|
663
|
-
|
|
664
|
-
- 📚 [Documentation](https://github.com/10cl/shellx)
|
|
665
|
-
- 🐛 [Issues](https://github.com/10cl/shellx/issues)
|
|
666
|
-
- 💬 [Discussions](https://github.com/10cl/shellx/discussions)
|
|
1
|
+
# ShellX-AI 架构文档
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
ShellX-AI 是一个 Android 设备自动化控制库,提供 WebSocket 通信和 HTTP API 支持。
|
|
6
|
+
|
|
7
|
+
## 模块结构
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
shellx-ai/ # 核心库
|
|
11
|
+
├── src/
|
|
12
|
+
│ ├── index.ts # 入口,导出 ConnectionClient 和 ShellX
|
|
13
|
+
│ ├── shellx.ts # 高级 API
|
|
14
|
+
│ ├── protocol.ts # 协议定义
|
|
15
|
+
│ └── data/ # 数据操作类型
|
|
16
|
+
│ └── types.ts # 日历、联系人等数据类型
|
|
17
|
+
|
|
18
|
+
phone-agent/ # Phone Agent
|
|
19
|
+
├── src/
|
|
20
|
+
│ ├── index.ts # Agent 初始化
|
|
21
|
+
│ └── tools.ts # 工具定义
|
|
22
|
+
|
|
23
|
+
shellx-cli/ # HTTP API 服务器
|
|
24
|
+
├── src/
|
|
25
|
+
│ ├── server/ # HTTP 路由
|
|
26
|
+
│ └── cli-core.ts # CLI 核心
|
|
27
|
+
|
|
28
|
+
shellx-cli-v2/ # CLI v2 (pi-tui)
|
|
29
|
+
├── src/
|
|
30
|
+
│ └── agent.ts # Agent 包装器
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 架构层次
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
┌─────────────────────────────────────────────────────┐
|
|
37
|
+
│ Phone Agent (phone-agent) │
|
|
38
|
+
│ - 工具定义和参数验证 │
|
|
39
|
+
│ - LLM 意图转换 │
|
|
40
|
+
└─────────────────────────────────────────────────────┘
|
|
41
|
+
▼
|
|
42
|
+
┌─────────────────────────────────────────────────────┐
|
|
43
|
+
│ ShellX-AI (shellx-ai) │
|
|
44
|
+
│ - ShellX: 高级 API (推荐) │
|
|
45
|
+
│ - ConnectionClient: WebSocket 原始 API │
|
|
46
|
+
└─────────────────────────────────────────────────────┘
|
|
47
|
+
▼
|
|
48
|
+
┌─────────────────────────────────────────────────────┐
|
|
49
|
+
│ Android 设备端 │
|
|
50
|
+
│ (ShellX 服务 + OpenClaw) │
|
|
51
|
+
└─────────────────────────────────────────────────────┘
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 核心功能
|
|
55
|
+
|
|
56
|
+
### ShellX API
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// 连接管理
|
|
60
|
+
const shellx = new ShellX({ deviceId, timeout: 5000 });
|
|
61
|
+
|
|
62
|
+
// UI 操作
|
|
63
|
+
await shellx.click(selector);
|
|
64
|
+
await shellx.input(selector, text);
|
|
65
|
+
await shellx.swipe(from, to);
|
|
66
|
+
|
|
67
|
+
// 数据操作
|
|
68
|
+
await shellx.getCalendarList();
|
|
69
|
+
await shellx.getCalendarEvents({ limit: 10 });
|
|
70
|
+
await shellx.addCalendarEvent({ title, startTime, endTime });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Phone Agent
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { PhoneAgent } from '@shellx/phone-agent';
|
|
77
|
+
|
|
78
|
+
const agent = new PhoneAgent({
|
|
79
|
+
apiKey,
|
|
80
|
+
baseUrl,
|
|
81
|
+
model,
|
|
82
|
+
shellx,
|
|
83
|
+
maxSteps: 10,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await agent.run("帮我添加一个明天下午3点的会议");
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### HTTP API
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# 获取日历事件
|
|
93
|
+
GET /api/data/calendar/events?limit=10
|
|
94
|
+
|
|
95
|
+
# 添加日历事件
|
|
96
|
+
POST /api/data/calendar/add
|
|
97
|
+
{
|
|
98
|
+
"title": "会议",
|
|
99
|
+
"startTime": 1740990000000,
|
|
100
|
+
"endTime": 1740997200000
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## 协议
|
|
105
|
+
|
|
106
|
+
### WebSocket (dataRequest)
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// 请求
|
|
110
|
+
{
|
|
111
|
+
dataRequest: {
|
|
112
|
+
requestType: "getCalendarEvents",
|
|
113
|
+
params: { limit: 10 }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 响应
|
|
118
|
+
{
|
|
119
|
+
dataRequest: {
|
|
120
|
+
requestType: "getCalendarEvents",
|
|
121
|
+
result: { success: true, events: [...] }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 技术栈
|
|
127
|
+
|
|
128
|
+
- **TypeScript** - 类型安全
|
|
129
|
+
- **WebSocket** - 设备通信
|
|
130
|
+
- **CBOR** - 消息编码
|
|
131
|
+
- **@sinclair/typebox** - 参数验证
|
|
132
|
+
- **@mariozechner/pi-agent-core** - Agent 框架
|
|
133
|
+
- **Ink** - TUI 组件库
|
|
134
|
+
|
|
135
|
+
## 环境变量
|
|
136
|
+
|
|
137
|
+
| 变量名 | 用途 | 默认值 |
|
|
138
|
+
|--------|------|--------|
|
|
139
|
+
| `SHELLX_DEVICE_ID` | 设备 ID | - |
|
|
140
|
+
| `SHELLX_SERVER_PORT` | HTTP 端口 | 8080 |
|
|
141
|
+
| `SHELLX_LOG_LEVEL` | 日志级别 | INFO |
|
|
142
|
+
|
|
143
|
+
## 快速开始
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# 安装依赖
|
|
147
|
+
npm install
|
|
148
|
+
|
|
149
|
+
# 构建
|
|
150
|
+
npm run build
|
|
151
|
+
|
|
152
|
+
# 启动设备端服务
|
|
153
|
+
# (需要单独安装 Android 端服务)
|
|
154
|
+
|
|
155
|
+
# 运行 Phone Agent
|
|
156
|
+
npm run build:phone-agent
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## 文档
|
|
160
|
+
|
|
161
|
+
- [API 文档](./API中文版.md) - 完整的 API 参考
|
|
162
|
+
- [架构设计文档](./架构设计文档.md) - 详细的架构说明
|