shellx-ai 1.1.0 → 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 -586
- 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 +9 -11
- 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 +15 -13
- package/dist/domain-manager.js.map +1 -1
- package/dist/error-handler.js +5 -2
- package/dist/error-handler.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 +197 -86
- 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/dist/utils/retry-helper.js +5 -2
- package/dist/utils/retry-helper.js.map +1 -1
- package/dist/utils.js +6 -3
- package/dist/utils.js.map +1 -1
- package/package.json +25 -4
- package/dist/shell/output-buffer.d.ts +0 -152
- package/dist/shell/output-buffer.js +0 -163
- 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 -348
- package/dist/shell/shell-command-executor.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,586 +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
|
-
**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>
|
|
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) - 详细的架构说明
|