shellx-ai 1.0.9 → 1.0.11

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
@@ -25,7 +25,6 @@ export interface PendingTask {
25
25
  reject: (err: Error) => void;
26
26
  timer: number;
27
27
  type: string;
28
- commandType?: string;
29
28
  }
30
29
  /**
31
30
  * Enhanced WebSocket Task Client with protocol-aware task methods
@@ -59,7 +58,7 @@ export declare class ConnectionTaskClient {
59
58
  private handleMessage;
60
59
  private processServerMessage;
61
60
  private handleJsonDataResponse;
62
- sendMessage(message: WsClient, taskType?: string, commandType?: string): Promise<any>;
61
+ sendMessage(message: WsClient, taskType?: string, timeout?: number): Promise<any>;
63
62
  /**
64
63
  * Find UI elements on the screen
65
64
  */
@@ -90,7 +89,7 @@ export declare class ConnectionTaskClient {
90
89
  /**
91
90
  * Execute an action sequence
92
91
  */
93
- executeAction(actionSequence: ActionSequence, taskId?: string): Promise<any>;
92
+ executeAction(actionSequence: ActionSequence, taskId?: string, timeeout?: number): Promise<any>;
94
93
  /**
95
94
  * Execute promptflow actions
96
95
  */
@@ -130,7 +129,7 @@ export declare class ConnectionTaskClient {
130
129
  * @param taskType 任务类型
131
130
  * @returns Promise<{taskId: string, promise: Promise<any>}>
132
131
  */
133
- sendMessageWithTaskId(message: WsClient, taskType?: string, definedTaskId?: string): Promise<{
132
+ sendMessageWithTaskId(message: WsClient, taskType: string, definedTaskId?: string, timeout?: number): Promise<{
134
133
  taskId: string;
135
134
  promise: Promise<any>;
136
135
  }>;
@@ -168,7 +167,6 @@ export declare class ConnectionTaskClient {
168
167
  */
169
168
  getTaskInfo(taskId: string): {
170
169
  type: string;
171
- commandType?: string;
172
170
  } | null;
173
171
  /**
174
172
  * 批量完成指定类型的任务
package/dist/index.js CHANGED
@@ -81,6 +81,9 @@ function getWebSocket() {
81
81
  }
82
82
  });
83
83
  }
84
+ function isPendingTask(taskType) {
85
+ return taskType && taskType !== "command";
86
+ }
84
87
  /**
85
88
  * Enhanced WebSocket Task Client with protocol-aware task methods
86
89
  */
@@ -161,10 +164,12 @@ class ConnectionTaskClient {
161
164
  if (localResp.ok) {
162
165
  const info = yield localResp.json();
163
166
  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;
167
+ if (deviceId == info.uuid) {
168
+ const localUuid = info.uuid;
169
+ const fallbackUrl = `ws://127.0.0.1:9091/api/s/${localUuid}`;
170
+ console.log('✅ [Auth] 本地ShellX服务可用,使用本地 /info 返回的 uuid:', localUuid, ',服务地址:', fallbackUrl);
171
+ return fallbackUrl;
172
+ }
168
173
  }
169
174
  }
170
175
  }
@@ -180,13 +185,14 @@ class ConnectionTaskClient {
180
185
  try {
181
186
  console.log('🔑 [Auth] 正在认证设备...');
182
187
  const featGlobal = this.getFetch();
183
- const data = yield featGlobal(`https://shellx.ai/api/device/${deviceId}`, {
188
+ const jsonData = yield featGlobal(`https://shellx.ai/api/device/${deviceId}`, {
184
189
  method: 'GET',
185
190
  headers: {
186
191
  'Content-Type': 'application/json',
187
192
  },
188
193
  });
189
- const jsonData = JSON.parse(data);
194
+ console.log('ShellX.ai设备认证响应:', jsonData);
195
+ // const jsonData = JSON.parse(data);
190
196
  console.log('✅ [Auth] ShellX.ai设备认证成功');
191
197
  console.log(`📡 [Auth] 设备ID: ${jsonData.authenticate}`);
192
198
  console.log(`📡 [Auth] ShellX.ai服务地址: ${jsonData.machine}`);
@@ -370,12 +376,16 @@ class ConnectionTaskClient {
370
376
  }
371
377
  break;
372
378
  case 'action':
373
- /*
374
379
  if (jsonData.action_event) {
375
- responseData = jsonData.action_event;
376
- shouldResolve = true;
377
- }*/
378
- // 命令,按键,滑动,点击,等待,查找,等待,屏幕截图,屏幕信息,屏幕变化,应用列表, 需要自己处理
380
+ responseData = jsonData.action_event;
381
+ shouldResolve = true;
382
+ }
383
+ break;
384
+ case 'actions':
385
+ if (jsonData.actions) {
386
+ responseData = jsonData.actions;
387
+ shouldResolve = true;
388
+ }
379
389
  break;
380
390
  }
381
391
  if (shouldResolve) {
@@ -391,21 +401,20 @@ class ConnectionTaskClient {
391
401
  }
392
402
  }
393
403
  }
394
- sendMessage(message, taskType, commandType) {
404
+ sendMessage(message, taskType, timeout) {
395
405
  return __awaiter(this, void 0, void 0, function* () {
396
406
  return new Promise((resolve, reject) => {
397
407
  const taskId = (0, uuid_1.v4)();
398
408
  if (taskType) {
399
409
  const timer = setTimeout(() => {
400
410
  this.pendingTasks.delete(taskId);
401
- reject(new Error(`Task ${taskType} timeout (${this.config.timeout}ms)`));
402
- }, this.config.timeout);
411
+ reject(new Error(`Task ${taskType} timeout (${timeout ? timeout : this.config.timeout}ms)`));
412
+ }, timeout ? timeout : this.config.timeout);
403
413
  this.pendingTasks.set(taskId, {
404
414
  resolve,
405
415
  reject,
406
416
  timer,
407
- type: taskType,
408
- commandType,
417
+ type: taskType
409
418
  });
410
419
  console.log(`📋 [ShellX] 创建任务: ${taskId}, 类型: ${taskType}}`);
411
420
  }
@@ -507,9 +516,9 @@ class ConnectionTaskClient {
507
516
  /**
508
517
  * Execute an action sequence
509
518
  */
510
- executeAction(actionSequence, taskId) {
519
+ executeAction(actionSequence, taskId, timeeout) {
511
520
  return __awaiter(this, void 0, void 0, function* () {
512
- return this.sendMessageWithTaskId({ actions: actionSequence }, 'action', taskId);
521
+ return this.sendMessageWithTaskId({ actions: actionSequence }, 'action', taskId, timeeout);
513
522
  });
514
523
  }
515
524
  /**
@@ -576,19 +585,19 @@ class ConnectionTaskClient {
576
585
  * @param taskType 任务类型
577
586
  * @returns Promise<{taskId: string, promise: Promise<any>}>
578
587
  */
579
- sendMessageWithTaskId(message, taskType, definedTaskId) {
588
+ sendMessageWithTaskId(message, taskType, definedTaskId, timeout) {
580
589
  return new Promise((resolve) => {
581
590
  let taskId = definedTaskId;
582
591
  if (taskId == null) {
583
592
  taskId = (0, uuid_1.v4)();
584
593
  }
585
- if (taskType) {
594
+ if (isPendingTask(taskType)) {
586
595
  const timer = setTimeout(() => {
587
596
  if (taskId != null) {
588
597
  this.pendingTasks.delete(taskId);
589
598
  }
590
599
  console.log(`⏰ [ShellX] 任务超时: ${taskId}, 类型: ${taskType}`);
591
- }, this.config.timeout);
600
+ }, timeout ? timeout : this.config.timeout);
592
601
  this.pendingTasks.set(taskId, {
593
602
  resolve: () => { },
594
603
  reject: () => { },
@@ -602,7 +611,7 @@ class ConnectionTaskClient {
602
611
  console.log(Date.now() + ' 📤 [ShellX] 发送消息:', JSON.stringify(message));
603
612
  this.ws.send((0, cbor_1.encode)(message));
604
613
  const promise = new Promise((promiseResolve, promiseReject) => {
605
- if (taskType) {
614
+ if (isPendingTask(taskType)) {
606
615
  if (taskId != null) {
607
616
  const task = this.pendingTasks.get(taskId);
608
617
  if (task) {
@@ -618,7 +627,7 @@ class ConnectionTaskClient {
618
627
  resolve({ taskId, promise });
619
628
  }
620
629
  catch (error) {
621
- if (taskType) {
630
+ if (isPendingTask(taskType)) {
622
631
  this.pendingTasks.delete(taskId);
623
632
  }
624
633
  resolve({ taskId, promise: Promise.reject(error) });
@@ -757,8 +766,7 @@ class ConnectionTaskClient {
757
766
  return null;
758
767
  }
759
768
  return {
760
- type: task.type,
761
- commandType: task.commandType,
769
+ type: task.type
762
770
  };
763
771
  }
764
772
  /**
@@ -43,6 +43,8 @@ export type ScreenShotResponse = {
43
43
  dimensions: ScreenDimensions;
44
44
  success?: boolean;
45
45
  errorMessage?: string;
46
+ isVideo?: boolean;
47
+ videoPath?: string;
46
48
  };
47
49
  export type ScreenInfoResponse = {
48
50
  displayId?: number;
@@ -159,6 +161,9 @@ export type ScreenShotOptions = {
159
161
  quality?: number;
160
162
  scale?: number;
161
163
  region?: ScreenRegion;
164
+ isRecording?: boolean;
165
+ saveToFile?: boolean;
166
+ duration?: number;
162
167
  };
163
168
  /** Client message type, see the Rust version. */
164
169
  export type WsClient = {
@@ -206,6 +211,17 @@ export interface ShellCommandAction {
206
211
  activity?: string;
207
212
  command: string;
208
213
  }
214
+ export interface ScreenShotAction {
215
+ title?: string;
216
+ type: "screenShot";
217
+ activity?: string;
218
+ format: 'png' | 'jpeg';
219
+ quality?: number;
220
+ scale?: number;
221
+ region?: ScreenRegion;
222
+ isRecording?: boolean;
223
+ duration?: number;
224
+ }
209
225
  export interface getAppInfoAction {
210
226
  title?: string;
211
227
  type: "get_app_info";
package/dist/shellx.d.ts CHANGED
@@ -40,10 +40,6 @@ export declare class ShellX {
40
40
  * 处理 shell 输出数据
41
41
  */
42
42
  handleShellOutput(chunks: [any, number, Uint8Array[]]): void;
43
- /**
44
- * 判断输出是否属于特定命令
45
- */
46
- private isOutputForCommand;
47
43
  /**
48
44
  * 清理命令输出,去除输入的命令内容
49
45
  */
@@ -52,30 +48,6 @@ export declare class ShellX {
52
48
  * 合并多个 session 的输出
53
49
  */
54
50
  private combineSessionOutputs;
55
- /**
56
- * 转义正则表达式特殊字符
57
- */
58
- private escapeRegExp;
59
- /**
60
- * 检查命令是否完成
61
- */
62
- private checkCommandCompletion;
63
- /**
64
- * 检查输出是否包含错误指示器
65
- */
66
- private hasErrorIndicators;
67
- /**
68
- * 判断命令是否完成(基于时间)
69
- */
70
- private isCommandComplete;
71
- /**
72
- * 解析命令 Promise
73
- */
74
- private resolveCommand;
75
- /**
76
- * 生成命令唯一标识
77
- */
78
- private generateCommandKey;
79
51
  /**
80
52
  * 通用重试机制
81
53
  */
package/dist/shellx.js CHANGED
@@ -16,11 +16,8 @@ exports.ShellX = void 0;
16
16
  exports.createShellX = createShellX;
17
17
  exports.createShellXWithShellMonitoring = createShellXWithShellMonitoring;
18
18
  const uuid_1 = require("uuid");
19
- const utils_1 = require("./utils");
20
19
  // 导入 WebSocketTaskClient 类
21
20
  const index_1 = __importDefault(require("./index"));
22
- // 安全地获取环境变量,兼容浏览器和Node.js环境
23
- let authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
24
21
  const COMMAND_PTY_SID = 999;
25
22
  /**
26
23
  * ShellX automation utilities for common patterns
@@ -63,27 +60,24 @@ class ShellX {
63
60
  for (const data of dataArrays) {
64
61
  output += new TextDecoder().decode(data);
65
62
  }
66
- console.log(`📟 [Shell] 收到输出 (Session ${sessionId}): ${output.trim()}`);
63
+ /*console.log(
64
+ `📟 [Shell] 收到输出 (Session ${sessionId}): ${output.trim()}`,
65
+ );*/
67
66
  // 为每个等待的命令累积输出
68
- for (const [commandKey, commandPromise] of this.shellCommandPromises.entries()) {
69
- // 检查是否该命令相关的输出
70
- if (this.isOutputForCommand(output, commandPromise.command, sessionId)) {
71
- // 存储该 session 的输出
72
- if (!commandPromise.sessionOutputs.has(sessionId)) {
73
- commandPromise.sessionOutputs.set(sessionId, '');
74
- }
75
- const currentSessionOutput = commandPromise.sessionOutputs.get(sessionId) || '';
76
- commandPromise.sessionOutputs.set(sessionId, currentSessionOutput + output);
77
- // 更新总输出
78
- commandPromise.output = this.combineSessionOutputs(commandPromise.sessionOutputs);
79
- console.log(`📊 [Shell] 命令 ${commandKey} 累积输出长度: ${commandPromise.output.length}`);
80
- // 调用输出回调(传递清理后的输出)
81
- if (commandPromise.options.onOutput) {
82
- const cleanOutput = this.cleanCommandOutput(output, commandPromise.command);
83
- commandPromise.options.onOutput(cleanOutput);
84
- }
85
- // 检查是否满足完成条件
86
- this.checkCommandCompletion(commandKey, commandPromise, output);
67
+ for (const [commandKey, commandPromise,] of this.shellCommandPromises.entries()) {
68
+ if (!commandPromise.sessionOutputs.has(sessionId)) {
69
+ commandPromise.sessionOutputs.set(sessionId, '');
70
+ }
71
+ const currentSessionOutput = commandPromise.sessionOutputs.get(sessionId) || '';
72
+ commandPromise.sessionOutputs.set(sessionId, currentSessionOutput + output);
73
+ commandPromise.output = this.combineSessionOutputs(commandPromise.sessionOutputs);
74
+ /*console.log(
75
+ `📊 [Shell] 命令 ${commandKey} 累积输出长度: ${commandPromise.output.length}`,
76
+ );*/
77
+ // 调用输出回调(传递清理后的输出)
78
+ if (commandPromise.options.onOutput) {
79
+ const cleanOutput = this.cleanCommandOutput(output, commandPromise.command);
80
+ commandPromise.options.onOutput(cleanOutput);
87
81
  }
88
82
  }
89
83
  }
@@ -92,56 +86,11 @@ class ShellX {
92
86
  }
93
87
  }
94
88
  }
95
- /**
96
- * 判断输出是否属于特定命令
97
- */
98
- isOutputForCommand(output, command, sessionId) {
99
- // 如果输出包含命令本身,说明是命令回显
100
- if (output.includes(command)) {
101
- return true;
102
- }
103
- // 如果有多个命令在等待,按时间顺序分配
104
- const waitingCommands = Array.from(this.shellCommandPromises.keys());
105
- if (waitingCommands.length === 1) {
106
- return true; // 只有一个命令在等待,所有输出都属于它
107
- }
108
- // 多个命令时,根据 sessionId 和命令创建时间进行启发式匹配
109
- return true; // 暂时返回 true,让所有命令都接收输出
110
- }
111
89
  /**
112
90
  * 清理命令输出,去除输入的命令内容
113
91
  */
114
92
  cleanCommandOutput(output, command) {
115
- if (!output || !command) {
116
- return output;
117
- }
118
- let cleanOutput = output;
119
- // 去除命令回显(命令本身)
120
- cleanOutput = cleanOutput.replace(new RegExp(this.escapeRegExp(command), 'g'), '');
121
- // 去除可能的命令提示符前缀和后缀
122
- const promptPatterns = [
123
- /^\s*[^$#>\s]*[#$>]\s*/, // 匹配提示符前缀
124
- /^\s*[^$#>\s]*@[^$#>\s]*[#$>]\s*/, // 匹配 user@host 格式
125
- /^\s*[^$#>\s]*:\s*[^$#>\s]*[#$>]\s*/, // 匹配 path: 格式
126
- /\s*[^$#>\s]*[#$>]\s*$/, // 匹配提示符后缀
127
- /\s*[^$#>\s]*@[^$#>\s]*[#$>]\s*$/, // 匹配 user@host 后缀
128
- /\s*[^$#>\s]*:\s*[^$#>\s]*[#$>]\s*$/, // 匹配 path: 后缀
129
- /\s*\d+\|[^$#>\s]*[#$>]\s*$/, // 匹配 Android 格式后缀
130
- ];
131
- for (const pattern of promptPatterns) {
132
- cleanOutput = cleanOutput.replace(pattern, '');
133
- }
134
- // 去除多余的空行和空格
135
- cleanOutput = cleanOutput.replace(/^\s+|\s+$/g, ''); // 去除首尾空白
136
- cleanOutput = cleanOutput.replace(/\n\s*\n/g, '\n'); // 去除多余空行
137
- // 如果清理后为空,返回空字符串
138
- if (cleanOutput.trim().length === 0) {
139
- return '';
140
- }
141
- console.log(`🧹 [Shell] 清理命令输出:`);
142
- console.log(` 原始: "${output.trim()}"`);
143
- console.log(` 清理后: "${cleanOutput.trim()}"`);
144
- return cleanOutput;
93
+ return output.replace(command, '');
145
94
  }
146
95
  /**
147
96
  * 合并多个 session 的输出
@@ -155,74 +104,6 @@ class ShellX {
155
104
  }
156
105
  return combined;
157
106
  }
158
- /**
159
- * 转义正则表达式特殊字符
160
- */
161
- escapeRegExp(string) {
162
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
163
- }
164
- /**
165
- * 检查命令是否完成
166
- */
167
- checkCommandCompletion(commandKey, commandPromise, newOutput) {
168
- const { resolve, startTime, options, output, command } = commandPromise;
169
- console.log(`🔍 [Shell] 检查命令 "${command}" 完成条件,当前输出长度: ${output.length}`);
170
- if (this.isCommandComplete(output, command)) {
171
- console.log(`✅ [Shell] 命令 "${command}" 执行完成 (检测到提示符)`);
172
- this.resolveCommand(commandKey, {
173
- success: true,
174
- output: output.trim(),
175
- duration: Date.now() - startTime
176
- });
177
- return;
178
- }
179
- console.log(`⏳ [Shell] 命令 "${command}" 继续等待完成...`);
180
- }
181
- /**
182
- * 检查输出是否包含错误指示器
183
- */
184
- hasErrorIndicators(output) {
185
- const errorPatterns = [
186
- /error/i,
187
- /failed/i,
188
- /not found/i,
189
- /permission denied/i,
190
- /command not found/i,
191
- /no such file/i,
192
- /syntax error/i
193
- ];
194
- return errorPatterns.some(pattern => pattern.test(output));
195
- }
196
- /**
197
- * 判断命令是否完成(基于时间)
198
- */
199
- isCommandComplete(output, command) {
200
- // 简化逻辑:只基于时间判断,不依赖提示符检测
201
- // 命令完成由超时机制控制
202
- return false; // 让超时机制处理命令完成
203
- }
204
- /**
205
- * 解析命令 Promise
206
- */
207
- resolveCommand(commandKey, result) {
208
- const commandPromise = this.shellCommandPromises.get(commandKey);
209
- if (commandPromise) {
210
- console.log(`✅ [Shell] 解析命令 Promise: ${commandKey}`);
211
- // 清理最终输出结果
212
- const cleanResult = Object.assign(Object.assign({}, result), { output: this.cleanCommandOutput(result.output, commandPromise.command) });
213
- commandPromise.resolve(cleanResult);
214
- this.shellCommandPromises.delete(commandKey);
215
- }
216
- else {
217
- console.log(`⚠️ [Shell] 未找到命令 Promise: ${commandKey}`);
218
- }
219
- }
220
- /**
221
- * 生成命令唯一标识
222
- */
223
- generateCommandKey(command) {
224
- return `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
225
- }
226
107
  /**
227
108
  * 通用重试机制
228
109
  */
@@ -789,9 +670,10 @@ class ShellX {
789
670
  return;
790
671
  }
791
672
  console.log(`\n${title || `✅ 找到 ${elements.length} 个元素`}:`);
792
- elements.forEach((element, index) => {
793
- this.printElementInfo(element, index);
794
- });
673
+ /*
674
+ elements.forEach((element, index) => {
675
+ this.printElementInfo(element, index);
676
+ });*/
795
677
  // 统计信息
796
678
  const visibleCount = elements.filter(e => e.visible).length;
797
679
  const clickableCount = elements.filter(e => e.clickable).length;
@@ -1004,27 +886,17 @@ class ShellX {
1004
886
  // 设置超时
1005
887
  const timeoutId = setTimeout(() => {
1006
888
  if (this.shellCommandPromises.has(commandKey)) {
889
+ console.log(`✅ [Shell] 命令 ${command} 执行完成`);
1007
890
  const commandPromise = this.shellCommandPromises.get(commandKey);
1008
891
  this.shellCommandPromises.delete(commandKey);
1009
- if (commandPromise && commandPromise.output.trim().length > 0) {
1010
- resolve({
1011
- success: true,
1012
- output: commandPromise.output.trim(),
1013
- duration: Date.now() - startTime
1014
- });
1015
- }
1016
- else {
1017
- resolve({
1018
- success: true,
1019
- output: "",
1020
- error: `Command timeout after ${timeout}ms, but got partial output`,
1021
- duration: Date.now() - startTime
1022
- });
1023
- }
892
+ resolve({
893
+ success: true,
894
+ output: commandPromise ? commandPromise.output.trim() : "",
895
+ duration: Date.now() - startTime
896
+ });
1024
897
  }
1025
898
  }, timeout);
1026
899
  try {
1027
- // 构建 shell 命令操作
1028
900
  const shellAction = {
1029
901
  title,
1030
902
  actions: [{
@@ -1037,7 +909,7 @@ class ShellX {
1037
909
  }
1038
910
  };
1039
911
  // 发送命令
1040
- yield this.client.executeAction(shellAction, commandKey);
912
+ yield this.client.sendMessageWithTaskId({ actions: shellAction }, 'command', commandKey, timeout);
1041
913
  console.log(`📤 [Shell] 命令已发送: ${commandKey}`);
1042
914
  }
1043
915
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellx-ai",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "shellx is a powerful WebSocket-based client for controlling shell commands and UI automation on remote devices.",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/10cl/shellx.git",
@@ -33,7 +33,6 @@
33
33
  "cbor": "^9.0.0",
34
34
  "dotenv": "^16.4.5",
35
35
  "ofetch": "^1.4.1",
36
- "shellx-ai": "file:",
37
36
  "uuid": "^11.1.0"
38
37
  },
39
38
  "optionalDependencies": {