shellx-ai 1.0.8 → 1.0.9

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/dist/index.d.ts CHANGED
@@ -30,8 +30,9 @@ export interface PendingTask {
30
30
  /**
31
31
  * Enhanced WebSocket Task Client with protocol-aware task methods
32
32
  */
33
- export declare class WebSocketTaskClient {
33
+ export declare class ConnectionTaskClient {
34
34
  private wsUrl;
35
+ private deviceId;
35
36
  private config;
36
37
  ws: any;
37
38
  private shellxConnected;
@@ -43,8 +44,10 @@ export declare class WebSocketTaskClient {
43
44
  private pingIntervalId;
44
45
  private initializationPromise;
45
46
  private shellx;
46
- constructor(wsUrl: string, config?: Partial<TaskClientConfig>);
47
+ constructor(deviceId: string, config?: Partial<TaskClientConfig>);
47
48
  private init;
49
+ private authenticateDevice;
50
+ getFetch(): (url: string, options?: any) => Promise<any>;
48
51
  /**
49
52
  * 等待WebSocket初始化完成
50
53
  */
@@ -56,7 +59,7 @@ export declare class WebSocketTaskClient {
56
59
  private handleMessage;
57
60
  private processServerMessage;
58
61
  private handleJsonDataResponse;
59
- private sendMessage;
62
+ sendMessage(message: WsClient, taskType?: string, commandType?: string): Promise<any>;
60
63
  /**
61
64
  * Find UI elements on the screen
62
65
  */
@@ -175,6 +178,7 @@ export declare class WebSocketTaskClient {
175
178
  */
176
179
  completeTasksByType(taskType: string, result?: any, success?: boolean): number;
177
180
  }
178
- export default WebSocketTaskClient;
181
+ export default ConnectionTaskClient;
179
182
  export type { UIElement, ElementSelector, ActionSequence, WsClient, WsServer, ScreenShotOptions, FindOptions, WaitOptions, UIHierarchy, WAITElement, ScreenShotResponse, ScreenInfoResponse, AppListResponse } from './protocol';
183
+ export type { Click, Input, Swipe, Key, Wait, Find, Command, AppInfo, Screenshot, ActionResult, Element, FindResult, CommandResult, Action, Actions, } from './types';
180
184
  export { createShellXWithShellMonitoring, ShellX } from './shellx';
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42
42
  });
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.ShellX = exports.createShellXWithShellMonitoring = exports.WebSocketTaskClient = exports.DEFAULT_CONFIG = void 0;
45
+ exports.ShellX = exports.createShellXWithShellMonitoring = exports.ConnectionTaskClient = exports.DEFAULT_CONFIG = void 0;
46
46
  const uuid_1 = require("uuid");
47
47
  const utils_1 = require("./utils");
48
48
  // 定义默认配置
@@ -84,8 +84,8 @@ function getWebSocket() {
84
84
  /**
85
85
  * Enhanced WebSocket Task Client with protocol-aware task methods
86
86
  */
87
- class WebSocketTaskClient {
88
- constructor(wsUrl, config = {}) {
87
+ class ConnectionTaskClient {
88
+ constructor(deviceId, config = {}) {
89
89
  this.ws = null; // Use any to avoid WebSocket type issues
90
90
  this.shellxConnected = false; // ShellX.ai 连接状态
91
91
  this.wsConnected = false; // WebSocket 连接状态
@@ -96,7 +96,7 @@ class WebSocketTaskClient {
96
96
  this.pingIntervalId = null;
97
97
  this.initializationPromise = null;
98
98
  this.shellx = null; // 关联的 ShellX 实例
99
- this.wsUrl = wsUrl;
99
+ this.deviceId = deviceId;
100
100
  this.config = Object.assign(Object.assign({}, exports.DEFAULT_CONFIG), config);
101
101
  this.initializationPromise = this.init();
102
102
  }
@@ -104,6 +104,7 @@ class WebSocketTaskClient {
104
104
  return __awaiter(this, void 0, void 0, function* () {
105
105
  var _a, _b;
106
106
  try {
107
+ this.wsUrl = yield this.authenticateDevice(this.deviceId);
107
108
  console.log('Initializing WebSocket client...' + this.wsUrl);
108
109
  // 获取适合当前环境的WebSocket构造函数
109
110
  const WebSocketConstructor = yield getWebSocket();
@@ -124,7 +125,10 @@ class WebSocketTaskClient {
124
125
  };
125
126
  this.ws.onclose = (event) => {
126
127
  var _a, _b;
127
- console.log('❌ [ShellX] WebSocket连接已关闭,正在尝试重新连接...' + event.code + ' ' + this.wsUrl);
128
+ console.log('❌ [ShellX] WebSocket连接已关闭,正在尝试重新连接...' +
129
+ event.code +
130
+ ' ' +
131
+ this.wsUrl);
128
132
  this.wsConnected = false;
129
133
  this.shellxConnected = false;
130
134
  this.authenticated = false;
@@ -144,6 +148,86 @@ class WebSocketTaskClient {
144
148
  }
145
149
  });
146
150
  }
151
+ authenticateDevice(deviceId) {
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ // 1. 优先检测本地服务
154
+ try {
155
+ // fetch超时实现
156
+ const fetchWithTimeout = (url, options, timeout = 1000) => Promise.race([
157
+ fetch(url, options),
158
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),
159
+ ]);
160
+ const localResp = (yield fetchWithTimeout('http://127.0.0.1:9091/info', { method: 'GET', headers: { Accept: 'application/json' } }, 1000));
161
+ if (localResp.ok) {
162
+ const info = yield localResp.json();
163
+ if (info && (info.status === 'ok' || info.status === 1) && info.uuid) {
164
+ const localUuid = info.uuid;
165
+ const fallbackUrl = `ws://127.0.0.1:9091/api/s/${localUuid}`;
166
+ console.log('✅ [Auth] 本地ShellX服务可用,使用本地 /info 返回的 uuid:', localUuid, ',服务地址:', fallbackUrl);
167
+ return fallbackUrl;
168
+ }
169
+ }
170
+ }
171
+ catch (e) {
172
+ // 本地不可用,继续走远程
173
+ }
174
+ const fallbackUrl = `ws://127.0.0.1:9091/api/s/${deviceId}`;
175
+ // 2. 远程认证逻辑
176
+ authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
177
+ if (!authKey) {
178
+ throw new Error('SHELLX_AUTH_KEY environment variable is required');
179
+ }
180
+ try {
181
+ console.log('🔑 [Auth] 正在认证设备...');
182
+ const featGlobal = this.getFetch();
183
+ const data = yield featGlobal(`https://shellx.ai/api/device/${deviceId}`, {
184
+ method: 'GET',
185
+ headers: {
186
+ 'Content-Type': 'application/json',
187
+ },
188
+ });
189
+ const jsonData = JSON.parse(data);
190
+ console.log('✅ [Auth] ShellX.ai设备认证成功');
191
+ console.log(`📡 [Auth] 设备ID: ${jsonData.authenticate}`);
192
+ console.log(`📡 [Auth] ShellX.ai服务地址: ${jsonData.machine}`);
193
+ console.log(`📡 [Auth] 注册时间: ${jsonData.registered_at}`);
194
+ console.log(`📡 [Auth] 最后更新: ${jsonData.last_updated}`);
195
+ return jsonData.machine + '/api/s/' + jsonData.authenticate;
196
+ }
197
+ catch (error) {
198
+ const errorMessage = error instanceof Error ? error.message : String(error);
199
+ console.warn('⚠️ [Auth] ShellX.ai在线认证失败,使用本地服务:', JSON.stringify(errorMessage));
200
+ return fallbackUrl;
201
+ }
202
+ });
203
+ }
204
+ getFetch() {
205
+ return (url, options) => __awaiter(this, void 0, void 0, function* () {
206
+ // 尝试使用全局fetch
207
+ if (typeof globalThis.fetch !== 'undefined') {
208
+ console.log(`🌐 [Fetch] 请求: ${(options === null || options === void 0 ? void 0 : options.method) || 'GET'} ${url}`);
209
+ const response = yield globalThis.fetch(url, options);
210
+ console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
211
+ if (!response.ok) {
212
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
213
+ }
214
+ return response.json();
215
+ }
216
+ try {
217
+ const { default: fetch } = yield Promise.resolve().then(() => __importStar(require('node-fetch')));
218
+ console.log(`🌐 [Fetch] 请求: ${(options === null || options === void 0 ? void 0 : options.method) || 'GET'} ${url}`);
219
+ const response = yield fetch(url, options);
220
+ console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
221
+ if (!response.ok) {
222
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
223
+ }
224
+ return response.json();
225
+ }
226
+ catch (fetchError) {
227
+ throw new Error(`Fetch not available: ${fetchError.message}`);
228
+ }
229
+ });
230
+ }
147
231
  /**
148
232
  * 等待WebSocket初始化完成
149
233
  */
@@ -308,36 +392,45 @@ class WebSocketTaskClient {
308
392
  }
309
393
  }
310
394
  sendMessage(message, taskType, commandType) {
311
- return new Promise((resolve, reject) => {
312
- const taskId = (0, uuid_1.v4)();
313
- if (taskType) {
314
- const timer = setTimeout(() => {
315
- this.pendingTasks.delete(taskId);
316
- reject(new Error(`Task ${taskType} timeout (${this.config.timeout}ms)`));
317
- }, this.config.timeout);
318
- this.pendingTasks.set(taskId, { resolve, reject, timer, type: taskType, commandType });
319
- console.log(`📋 [ShellX] 创建任务: ${taskId}, 类型: ${taskType}}`);
320
- }
321
- if (this.shellxConnected && this.ws) {
322
- try {
323
- console.log('📤 [ShellX] 发送消息:', message);
324
- this.ws.send((0, cbor_1.encode)(message));
325
- if (!taskType)
326
- resolve(undefined); // For fire-and-forget messages
327
- }
328
- catch (error) {
329
- if (taskType) {
395
+ return __awaiter(this, void 0, void 0, function* () {
396
+ return new Promise((resolve, reject) => {
397
+ const taskId = (0, uuid_1.v4)();
398
+ if (taskType) {
399
+ const timer = setTimeout(() => {
330
400
  this.pendingTasks.delete(taskId);
401
+ reject(new Error(`Task ${taskType} timeout (${this.config.timeout}ms)`));
402
+ }, this.config.timeout);
403
+ this.pendingTasks.set(taskId, {
404
+ resolve,
405
+ reject,
406
+ timer,
407
+ type: taskType,
408
+ commandType,
409
+ });
410
+ console.log(`📋 [ShellX] 创建任务: ${taskId}, 类型: ${taskType}}`);
411
+ }
412
+ if (this.shellxConnected && this.ws) {
413
+ try {
414
+ console.log('📤 [ShellX] 发送消息:', JSON.stringify(message));
415
+ this.ws.send((0, cbor_1.encode)(message));
416
+ if (!taskType)
417
+ resolve(undefined); // For fire-and-forget messages
418
+ }
419
+ catch (error) {
420
+ if (taskType) {
421
+ this.pendingTasks.delete(taskId);
422
+ }
423
+ reject(error);
331
424
  }
332
- reject(error);
333
425
  }
334
- }
335
- else {
336
- console.log('⏳ [ShellX] 连接未就绪,消息已加入队列');
337
- this.messageQueue.push(message);
338
- if (!taskType)
339
- resolve(undefined);
340
- }
426
+ else {
427
+ console.log('⏳ [ShellX] 连接未就绪,消息已加入队列');
428
+ this.reconnect();
429
+ this.messageQueue.push(message);
430
+ if (!taskType)
431
+ resolve(undefined);
432
+ }
433
+ });
341
434
  });
342
435
  }
343
436
  // ===== PROTOCOL-AWARE TASK METHODS =====
@@ -349,7 +442,7 @@ class WebSocketTaskClient {
349
442
  const findAction = {
350
443
  type: 'find',
351
444
  selector,
352
- options
445
+ options,
353
446
  };
354
447
  return this.sendMessage({ findElement: findAction }, 'findElement');
355
448
  });
@@ -362,7 +455,7 @@ class WebSocketTaskClient {
362
455
  const waitAction = {
363
456
  type: 'wait',
364
457
  selector,
365
- options
458
+ options,
366
459
  };
367
460
  return this.sendMessage({ waitElement: waitAction }, 'waitElement');
368
461
  });
@@ -372,17 +465,18 @@ class WebSocketTaskClient {
372
465
  */
373
466
  screenShot(options) {
374
467
  return __awaiter(this, void 0, void 0, function* () {
375
- return this.sendMessage({ screenShot: options || {
376
- "format": "png",
377
- "quality": 30,
378
- "scale": 1.0,
379
- "region": {
380
- "left": 0,
381
- "top": 0,
382
- "width": 1080,
383
- "height": 2340
384
- }
385
- }
468
+ return this.sendMessage({
469
+ screenShot: options || {
470
+ format: 'png',
471
+ quality: 30,
472
+ scale: 1.0,
473
+ region: {
474
+ left: 0,
475
+ top: 0,
476
+ width: 1080,
477
+ height: 2340,
478
+ },
479
+ },
386
480
  }, 'screenShot');
387
481
  });
388
482
  }
@@ -432,7 +526,7 @@ class WebSocketTaskClient {
432
526
  screenChange(options) {
433
527
  return __awaiter(this, void 0, void 0, function* () {
434
528
  return this.sendMessage({
435
- screenChange: options || { enable: true }
529
+ screenChange: options || { enable: true },
436
530
  });
437
531
  });
438
532
  }
@@ -499,13 +593,13 @@ class WebSocketTaskClient {
499
593
  resolve: () => { },
500
594
  reject: () => { },
501
595
  timer,
502
- type: taskType
596
+ type: taskType,
503
597
  });
504
598
  console.log(`📋 [ShellX] 创建可手动控制的任务: ${taskId}, 类型: ${taskType}}`);
505
599
  }
506
600
  if (this.shellxConnected && this.ws) {
507
601
  try {
508
- console.log('📤 [ShellX] 发送消息:', message);
602
+ console.log(Date.now() + ' 📤 [ShellX] 发送消息:', JSON.stringify(message));
509
603
  this.ws.send((0, cbor_1.encode)(message));
510
604
  const promise = new Promise((promiseResolve, promiseReject) => {
511
605
  if (taskType) {
@@ -543,7 +637,7 @@ class WebSocketTaskClient {
543
637
  console.log('🏓 [ShellX] 开始心跳检测...');
544
638
  this.pingIntervalId = setInterval(() => {
545
639
  if (this.ws && this.shellxConnected) {
546
- this.ws.send((0, cbor_1.encode)({ ping: BigInt(Date.now()) }));
640
+ this.ws.send((0, cbor_1.encode)({ ping: Date.now() }));
547
641
  }
548
642
  }, this.config.pingInterval);
549
643
  }
@@ -557,8 +651,10 @@ class WebSocketTaskClient {
557
651
  return __awaiter(this, void 0, void 0, function* () {
558
652
  var _a, _b;
559
653
  // TODO: 本地的需要重连
560
- console.log(`🔄 [ShellX] 重连中... (${this.reconnectAttempts}/${this.config.reconnectMaxAttempts})` + this.wsUrl);
654
+ console.log(`🔄 [ShellX] 重连中... (${this.reconnectAttempts}/${this.config.reconnectMaxAttempts})` +
655
+ this.wsUrl);
561
656
  if (this.reconnectAttempts >= this.config.reconnectMaxAttempts) {
657
+ console.error('❌ [ShellX] 重连失败,达到最大重连次数');
562
658
  (_b = (_a = this.config).onReconnectFailed) === null || _b === void 0 ? void 0 : _b.call(_a);
563
659
  return;
564
660
  }
@@ -662,7 +758,7 @@ class WebSocketTaskClient {
662
758
  }
663
759
  return {
664
760
  type: task.type,
665
- commandType: task.commandType
761
+ commandType: task.commandType,
666
762
  };
667
763
  }
668
764
  /**
@@ -685,9 +781,8 @@ class WebSocketTaskClient {
685
781
  return completedCount;
686
782
  }
687
783
  }
688
- exports.WebSocketTaskClient = WebSocketTaskClient;
689
- exports.default = WebSocketTaskClient;
690
- // PromptFlow 模块已移至 examples/ 目录作为示例实现
784
+ exports.ConnectionTaskClient = ConnectionTaskClient;
785
+ exports.default = ConnectionTaskClient;
691
786
  var shellx_1 = require("./shellx");
692
787
  Object.defineProperty(exports, "createShellXWithShellMonitoring", { enumerable: true, get: function () { return shellx_1.createShellXWithShellMonitoring; } });
693
788
  Object.defineProperty(exports, "ShellX", { enumerable: true, get: function () { return shellx_1.ShellX; } });
@@ -101,6 +101,7 @@ export interface ActionSequence {
101
101
  }
102
102
  export type JSONData = {
103
103
  findElement?: UIHierarchy;
104
+ findAllElement?: string;
104
105
  waitElement?: WAITElement;
105
106
  screenShot?: ScreenShotResponse;
106
107
  screenInfo?: ScreenInfoResponse;
package/dist/shellx.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- import type { ElementSelector, ActionSequence, UIElement, ScreenShotOptions, WsServer } from './protocol';
2
- import WebSocketTaskClient from './index';
3
- export type { UIElement, ElementSelector, ActionSequence };
4
- export { ShellX as AutomationHelpers };
5
- export { createShellX as createHelpers };
6
- export { createShellXWithShellMonitoring as createHelpersWithShellMonitoring };
1
+ import type { ElementSelector, ActionSequence as LegacyActionSequence, UIElement, ScreenShotOptions, WsServer } from './protocol';
2
+ import type { ActionResult, Click, Input, Swipe, Key, Wait, Find, Command, AppInfo, Screenshot, Point, Element, FindResult, CommandResult, Action, Actions } from './types';
3
+ import ConnectionTaskClient from './index';
4
+ export type { UIElement, ElementSelector, LegacyActionSequence };
5
+ export type { ActionResult, Click, Input, Swipe, Key, Wait, Find, Command, AppInfo, Screenshot, Point, Element, FindResult, CommandResult, Action, Actions };
7
6
  interface ShellCommandResult {
8
7
  success: boolean;
9
8
  output: string;
@@ -28,11 +27,11 @@ export declare class ShellX {
28
27
  private client;
29
28
  private shellOutputBuffers;
30
29
  private shellCommandPromises;
31
- constructor(client: WebSocketTaskClient);
30
+ constructor(client: ConnectionTaskClient);
32
31
  /**
33
32
  * Get the WebSocket client instance
34
33
  */
35
- getClient(): WebSocketTaskClient;
34
+ getClient(): ConnectionTaskClient;
36
35
  /**
37
36
  * 设置 shell 输出监听器(需要在创建 WebSocketTaskClient 时调用)
38
37
  */
@@ -77,6 +76,58 @@ export declare class ShellX {
77
76
  * 生成命令唯一标识
78
77
  */
79
78
  private generateCommandKey;
79
+ /**
80
+ * 通用重试机制
81
+ */
82
+ private withRetry;
83
+ /**
84
+ * 转换精简选择器为原始选择器
85
+ */
86
+ private convertSelector;
87
+ /**
88
+ * 转换精简元素为原始元素
89
+ */
90
+ private convertElement;
91
+ /**
92
+ * 点击操作 - 支持元素ID、坐标或选择器
93
+ */
94
+ click(clickData: Click): Promise<ActionResult>;
95
+ /**
96
+ * 输入操作 - 支持元素ID、资源ID或选择器
97
+ */
98
+ input(inputData: Input): Promise<ActionResult>;
99
+ /**
100
+ * 滑动操作 - 支持坐标点
101
+ */
102
+ swipe(swipeData: Swipe): Promise<ActionResult>;
103
+ /**
104
+ * 按键操作
105
+ */
106
+ pressKey(keyData: Key): Promise<ActionResult>;
107
+ /**
108
+ * 等待操作 - 等待元素出现或消失
109
+ */
110
+ wait(waitData: Wait): Promise<ActionResult>;
111
+ /**
112
+ * 查找操作 - 查找元素
113
+ */
114
+ find(findData: Find): Promise<FindResult>;
115
+ /**
116
+ * 执行命令 - 兼容现有的 shell 命令执行
117
+ */
118
+ executeCommand(commandData: Command): Promise<CommandResult>;
119
+ /**
120
+ * 获取应用信息
121
+ */
122
+ getAppInfo(appInfoData: AppInfo): Promise<ActionResult>;
123
+ /**
124
+ * 截图操作
125
+ */
126
+ takeScreenshot(screenshotData?: Screenshot): Promise<ActionResult>;
127
+ /**
128
+ * 执行操作序列 - 支持多个操作连续执行
129
+ */
130
+ executeActions(actions: Array<Click | Input | Swipe | Key | Wait | Find | Command | AppInfo | Screenshot>): Promise<ActionResult[]>;
80
131
  /**
81
132
  * Smart element finder with retry logic
82
133
  */
@@ -108,10 +159,6 @@ export declare class ShellX {
108
159
  clear?: boolean;
109
160
  hideKeyboard?: boolean;
110
161
  }): Promise<boolean>;
111
- /**
112
- * Swipe in a direction
113
- */
114
- swipe(direction: 'up' | 'down' | 'left' | 'right', distance?: number, duration?: number): Promise<void>;
115
162
  /**
116
163
  * Take screenshot and save info
117
164
  */
@@ -175,12 +222,9 @@ export declare class ShellX {
175
222
  /**
176
223
  * Create ShellX instance
177
224
  */
178
- export declare function createShellX(client: WebSocketTaskClient): ShellX;
225
+ export declare function createShellX(client: ConnectionTaskClient): ShellX;
179
226
  /**
180
227
  * Create ShellX instance with automatic authentication and shell output monitoring
181
228
  * 自动处理ShellX.ai认证和连接,无需外部提供连接地址
182
229
  */
183
- export declare function createShellXWithShellMonitoring(config?: any): Promise<{
184
- client: WebSocketTaskClient;
185
- shellx: ShellX;
186
- }>;
230
+ export declare function createShellXWithShellMonitoring(config?: any): Promise<ShellX>;