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 +3 -5
- package/dist/index.js +33 -25
- package/dist/protocol.d.ts +16 -0
- package/dist/shellx.d.ts +0 -28
- package/dist/shellx.js +29 -157
- package/package.json +1 -2
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,
|
|
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
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
376
|
-
|
|
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,
|
|
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
|
/**
|
package/dist/protocol.d.ts
CHANGED
|
@@ -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(
|
|
63
|
+
/*console.log(
|
|
64
|
+
`📟 [Shell] 收到输出 (Session ${sessionId}): ${output.trim()}`,
|
|
65
|
+
);*/
|
|
67
66
|
// 为每个等待的命令累积输出
|
|
68
|
-
for (const [commandKey, commandPromise] of this.shellCommandPromises.entries()) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
793
|
-
|
|
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
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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.
|
|
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.
|
|
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": {
|