shellx-ai 1.0.10 → 1.0.12
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 +58 -39
- package/dist/shellx.d.ts +3 -29
- package/dist/shellx.js +156 -172
- package/package.json +5 -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
|
@@ -71,7 +71,6 @@ function getWebSocket() {
|
|
|
71
71
|
}
|
|
72
72
|
// Node.js环境下动态导入ws模块
|
|
73
73
|
try {
|
|
74
|
-
// @ts-ignore - Dynamic import may not have types
|
|
75
74
|
const wsModule = yield Promise.resolve().then(() => __importStar(require('ws')));
|
|
76
75
|
return wsModule.default || wsModule;
|
|
77
76
|
}
|
|
@@ -81,6 +80,9 @@ function getWebSocket() {
|
|
|
81
80
|
}
|
|
82
81
|
});
|
|
83
82
|
}
|
|
83
|
+
function isPendingTask(taskType) {
|
|
84
|
+
return taskType && taskType !== "command";
|
|
85
|
+
}
|
|
84
86
|
/**
|
|
85
87
|
* Enhanced WebSocket Task Client with protocol-aware task methods
|
|
86
88
|
*/
|
|
@@ -105,7 +107,11 @@ class ConnectionTaskClient {
|
|
|
105
107
|
var _a, _b;
|
|
106
108
|
try {
|
|
107
109
|
this.wsUrl = yield this.authenticateDevice(this.deviceId);
|
|
108
|
-
|
|
110
|
+
if (!this.wsUrl) {
|
|
111
|
+
this.config.onError(new Event("connection"));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log('Initializing ShellX client wsUrl:' + this.wsUrl + " deviceId: " + this.deviceId);
|
|
109
115
|
// 获取适合当前环境的WebSocket构造函数
|
|
110
116
|
const WebSocketConstructor = yield getWebSocket();
|
|
111
117
|
this.ws = new WebSocketConstructor(this.wsUrl);
|
|
@@ -150,6 +156,7 @@ class ConnectionTaskClient {
|
|
|
150
156
|
}
|
|
151
157
|
authenticateDevice(deviceId) {
|
|
152
158
|
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
+
let fallbackUrl = undefined;
|
|
153
160
|
// 1. 优先检测本地服务
|
|
154
161
|
try {
|
|
155
162
|
// fetch超时实现
|
|
@@ -161,9 +168,9 @@ class ConnectionTaskClient {
|
|
|
161
168
|
if (localResp.ok) {
|
|
162
169
|
const info = yield localResp.json();
|
|
163
170
|
if (info && (info.status === 'ok' || info.status === 1) && info.uuid) {
|
|
164
|
-
if (deviceId == info.uuid) {
|
|
171
|
+
if (deviceId == undefined || deviceId == info.uuid) {
|
|
165
172
|
const localUuid = info.uuid;
|
|
166
|
-
|
|
173
|
+
fallbackUrl = `ws://127.0.0.1:9091/api/s/${localUuid}`;
|
|
167
174
|
console.log('✅ [Auth] 本地ShellX服务可用,使用本地 /info 返回的 uuid:', localUuid, ',服务地址:', fallbackUrl);
|
|
168
175
|
return fallbackUrl;
|
|
169
176
|
}
|
|
@@ -173,11 +180,15 @@ class ConnectionTaskClient {
|
|
|
173
180
|
catch (e) {
|
|
174
181
|
// 本地不可用,继续走远程
|
|
175
182
|
}
|
|
176
|
-
|
|
183
|
+
if (deviceId == undefined) {
|
|
184
|
+
console.warn('❌ [Auth] 设备ID未设置,本地未连接USB,请设置环境变量 SHELLX_DEVICE_ID 或者 USB连接设备');
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
177
187
|
// 2. 远程认证逻辑
|
|
178
188
|
authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
|
|
179
189
|
if (!authKey) {
|
|
180
|
-
|
|
190
|
+
authKey = (0, uuid_1.v4)();
|
|
191
|
+
console.log('✅ [Auth] SHELLX_AUTH_KEY 环境变量未设置, 生成新的authKey: ' + authKey);
|
|
181
192
|
}
|
|
182
193
|
try {
|
|
183
194
|
console.log('🔑 [Auth] 正在认证设备...');
|
|
@@ -189,6 +200,12 @@ class ConnectionTaskClient {
|
|
|
189
200
|
},
|
|
190
201
|
});
|
|
191
202
|
console.log('ShellX.ai设备认证响应:', jsonData);
|
|
203
|
+
// 验证认证响应是否有效
|
|
204
|
+
// 如果接口返回 null 或者缺少必要字段,说明设备未注册
|
|
205
|
+
if (!jsonData || jsonData === null || !jsonData.machine || !jsonData.authenticate) {
|
|
206
|
+
console.error('❌ [Auth] ShellX.ai设备未注册或认证信息无效');
|
|
207
|
+
return `wss://shellx.ai/api/s/${deviceId}`;
|
|
208
|
+
}
|
|
192
209
|
// const jsonData = JSON.parse(data);
|
|
193
210
|
console.log('✅ [Auth] ShellX.ai设备认证成功');
|
|
194
211
|
console.log(`📡 [Auth] 设备ID: ${jsonData.authenticate}`);
|
|
@@ -249,19 +266,16 @@ class ConnectionTaskClient {
|
|
|
249
266
|
try {
|
|
250
267
|
authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
|
|
251
268
|
if (!authKey) {
|
|
252
|
-
|
|
253
|
-
|
|
269
|
+
authKey = (0, uuid_1.v4)();
|
|
270
|
+
console.log('✅ [Auth] SHELLX_AUTH_KEY 环境变量未设置, 生成新的authKey: ' + authKey);
|
|
254
271
|
}
|
|
255
|
-
console.log('🔑 [Auth] 发送认证消息...');
|
|
256
|
-
// 从URL中提取认证密钥(如果URL包含认证信息)
|
|
257
|
-
// 发送认证消息
|
|
258
272
|
const authMessage = { authenticate: authKey };
|
|
259
273
|
console.log('📤 [Auth] 发送认证消息:', { authenticate: authKey });
|
|
260
274
|
if (this.ws && this.wsConnected) {
|
|
261
275
|
this.ws.send((0, cbor_1.encode)(authMessage));
|
|
262
276
|
}
|
|
263
277
|
else {
|
|
264
|
-
console.error('❌ [Auth]
|
|
278
|
+
console.error('❌ [Auth] ShellX 未连接,无法发送认证消息');
|
|
265
279
|
}
|
|
266
280
|
}
|
|
267
281
|
catch (error) {
|
|
@@ -373,42 +387,43 @@ class ConnectionTaskClient {
|
|
|
373
387
|
}
|
|
374
388
|
break;
|
|
375
389
|
case 'action':
|
|
376
|
-
/*
|
|
377
390
|
if (jsonData.action_event) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
391
|
+
responseData = jsonData.action_event;
|
|
392
|
+
shouldResolve = true;
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
case 'actions':
|
|
396
|
+
if (jsonData.actions) {
|
|
397
|
+
responseData = jsonData.actions;
|
|
398
|
+
shouldResolve = true;
|
|
399
|
+
}
|
|
382
400
|
break;
|
|
383
401
|
}
|
|
384
402
|
if (shouldResolve) {
|
|
385
403
|
clearTimeout(task.timer);
|
|
386
404
|
this.pendingTasks.delete(taskId);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
task.resolve(responseData);
|
|
392
|
-
}
|
|
405
|
+
// 始终 resolve,保持原始响应数据结构不变
|
|
406
|
+
task.resolve(responseData);
|
|
393
407
|
break; // Only resolve the first matching task
|
|
394
408
|
}
|
|
395
409
|
}
|
|
396
410
|
}
|
|
397
|
-
sendMessage(message, taskType,
|
|
411
|
+
sendMessage(message, taskType, timeout) {
|
|
398
412
|
return __awaiter(this, void 0, void 0, function* () {
|
|
399
413
|
return new Promise((resolve, reject) => {
|
|
400
414
|
const taskId = (0, uuid_1.v4)();
|
|
401
415
|
if (taskType) {
|
|
402
416
|
const timer = setTimeout(() => {
|
|
403
417
|
this.pendingTasks.delete(taskId);
|
|
404
|
-
|
|
405
|
-
|
|
418
|
+
// 超时时返回 undefined 而不是 reject
|
|
419
|
+
console.log(`⏰ [ShellX] 任务超时: ${taskType}, 返回 undefined`);
|
|
420
|
+
resolve(undefined);
|
|
421
|
+
}, timeout ? timeout : this.config.timeout);
|
|
406
422
|
this.pendingTasks.set(taskId, {
|
|
407
423
|
resolve,
|
|
408
424
|
reject,
|
|
409
425
|
timer,
|
|
410
|
-
type: taskType
|
|
411
|
-
commandType,
|
|
426
|
+
type: taskType
|
|
412
427
|
});
|
|
413
428
|
console.log(`📋 [ShellX] 创建任务: ${taskId}, 类型: ${taskType}}`);
|
|
414
429
|
}
|
|
@@ -423,7 +438,9 @@ class ConnectionTaskClient {
|
|
|
423
438
|
if (taskType) {
|
|
424
439
|
this.pendingTasks.delete(taskId);
|
|
425
440
|
}
|
|
426
|
-
reject
|
|
441
|
+
// 发送失败时返回 undefined 而不是 reject
|
|
442
|
+
console.log(`❌ [ShellX] 发送消息失败,返回 undefined:`, error);
|
|
443
|
+
resolve(undefined);
|
|
427
444
|
}
|
|
428
445
|
}
|
|
429
446
|
else {
|
|
@@ -510,9 +527,9 @@ class ConnectionTaskClient {
|
|
|
510
527
|
/**
|
|
511
528
|
* Execute an action sequence
|
|
512
529
|
*/
|
|
513
|
-
executeAction(actionSequence, taskId) {
|
|
530
|
+
executeAction(actionSequence, taskId, timeeout) {
|
|
514
531
|
return __awaiter(this, void 0, void 0, function* () {
|
|
515
|
-
return this.sendMessageWithTaskId({ actions: actionSequence }, 'action', taskId);
|
|
532
|
+
return this.sendMessageWithTaskId({ actions: actionSequence }, 'action', taskId, timeeout);
|
|
516
533
|
});
|
|
517
534
|
}
|
|
518
535
|
/**
|
|
@@ -579,19 +596,19 @@ class ConnectionTaskClient {
|
|
|
579
596
|
* @param taskType 任务类型
|
|
580
597
|
* @returns Promise<{taskId: string, promise: Promise<any>}>
|
|
581
598
|
*/
|
|
582
|
-
sendMessageWithTaskId(message, taskType, definedTaskId) {
|
|
599
|
+
sendMessageWithTaskId(message, taskType, definedTaskId, timeout) {
|
|
583
600
|
return new Promise((resolve) => {
|
|
584
601
|
let taskId = definedTaskId;
|
|
585
602
|
if (taskId == null) {
|
|
586
603
|
taskId = (0, uuid_1.v4)();
|
|
587
604
|
}
|
|
588
|
-
if (taskType) {
|
|
605
|
+
if (isPendingTask(taskType)) {
|
|
589
606
|
const timer = setTimeout(() => {
|
|
590
607
|
if (taskId != null) {
|
|
591
608
|
this.pendingTasks.delete(taskId);
|
|
592
609
|
}
|
|
593
610
|
console.log(`⏰ [ShellX] 任务超时: ${taskId}, 类型: ${taskType}`);
|
|
594
|
-
}, this.config.timeout);
|
|
611
|
+
}, timeout ? timeout : this.config.timeout);
|
|
595
612
|
this.pendingTasks.set(taskId, {
|
|
596
613
|
resolve: () => { },
|
|
597
614
|
reject: () => { },
|
|
@@ -605,7 +622,7 @@ class ConnectionTaskClient {
|
|
|
605
622
|
console.log(Date.now() + ' 📤 [ShellX] 发送消息:', JSON.stringify(message));
|
|
606
623
|
this.ws.send((0, cbor_1.encode)(message));
|
|
607
624
|
const promise = new Promise((promiseResolve, promiseReject) => {
|
|
608
|
-
if (taskType) {
|
|
625
|
+
if (isPendingTask(taskType)) {
|
|
609
626
|
if (taskId != null) {
|
|
610
627
|
const task = this.pendingTasks.get(taskId);
|
|
611
628
|
if (task) {
|
|
@@ -621,7 +638,7 @@ class ConnectionTaskClient {
|
|
|
621
638
|
resolve({ taskId, promise });
|
|
622
639
|
}
|
|
623
640
|
catch (error) {
|
|
624
|
-
if (taskType) {
|
|
641
|
+
if (isPendingTask(taskType)) {
|
|
625
642
|
this.pendingTasks.delete(taskId);
|
|
626
643
|
}
|
|
627
644
|
resolve({ taskId, promise: Promise.reject(error) });
|
|
@@ -667,7 +684,7 @@ class ConnectionTaskClient {
|
|
|
667
684
|
});
|
|
668
685
|
}
|
|
669
686
|
flushQueue() {
|
|
670
|
-
while (this.messageQueue.length > 0 && this.ws) {
|
|
687
|
+
while (this.messageQueue.length > 0 && this.ws && this.shellxConnected) {
|
|
671
688
|
const message = this.messageQueue.shift();
|
|
672
689
|
if (message) {
|
|
673
690
|
try {
|
|
@@ -676,6 +693,9 @@ class ConnectionTaskClient {
|
|
|
676
693
|
}
|
|
677
694
|
catch (error) {
|
|
678
695
|
console.error('Failed to send queued message:', error);
|
|
696
|
+
// Re-queue the message if sending fails
|
|
697
|
+
this.messageQueue.unshift(message);
|
|
698
|
+
break;
|
|
679
699
|
}
|
|
680
700
|
}
|
|
681
701
|
}
|
|
@@ -760,8 +780,7 @@ class ConnectionTaskClient {
|
|
|
760
780
|
return null;
|
|
761
781
|
}
|
|
762
782
|
return {
|
|
763
|
-
type: task.type
|
|
764
|
-
commandType: task.commandType,
|
|
783
|
+
type: task.type
|
|
765
784
|
};
|
|
766
785
|
}
|
|
767
786
|
/**
|
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
|
*/
|
|
@@ -53,31 +49,7 @@ export declare class ShellX {
|
|
|
53
49
|
*/
|
|
54
50
|
private combineSessionOutputs;
|
|
55
51
|
/**
|
|
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
|
-
/**
|
|
80
|
-
* 通用重试机制
|
|
52
|
+
* 通用重试机制 - 失败时返回 undefined 而不是抛出异常
|
|
81
53
|
*/
|
|
82
54
|
private withRetry;
|
|
83
55
|
/**
|
|
@@ -116,6 +88,8 @@ export declare class ShellX {
|
|
|
116
88
|
* 执行命令 - 兼容现有的 shell 命令执行
|
|
117
89
|
*/
|
|
118
90
|
executeCommand(commandData: Command): Promise<CommandResult>;
|
|
91
|
+
executeCodeEval(context: any, code: string, timeout?: number): Promise<any>;
|
|
92
|
+
executeCode(agentCode: string, context: any, timeout?: number): Promise<any>;
|
|
119
93
|
/**
|
|
120
94
|
* 获取应用信息
|
|
121
95
|
*/
|
package/dist/shellx.js
CHANGED
|
@@ -16,11 +16,7 @@ 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
|
-
// 导入 WebSocketTaskClient 类
|
|
21
19
|
const index_1 = __importDefault(require("./index"));
|
|
22
|
-
// 安全地获取环境变量,兼容浏览器和Node.js环境
|
|
23
|
-
let authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
|
|
24
20
|
const COMMAND_PTY_SID = 999;
|
|
25
21
|
/**
|
|
26
22
|
* ShellX automation utilities for common patterns
|
|
@@ -63,27 +59,24 @@ class ShellX {
|
|
|
63
59
|
for (const data of dataArrays) {
|
|
64
60
|
output += new TextDecoder().decode(data);
|
|
65
61
|
}
|
|
66
|
-
console.log(
|
|
62
|
+
/*console.log(
|
|
63
|
+
`📟 [Shell] 收到输出 (Session ${sessionId}): ${output.trim()}`,
|
|
64
|
+
);*/
|
|
67
65
|
// 为每个等待的命令累积输出
|
|
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);
|
|
66
|
+
for (const [commandKey, commandPromise,] of this.shellCommandPromises.entries()) {
|
|
67
|
+
if (!commandPromise.sessionOutputs.has(sessionId)) {
|
|
68
|
+
commandPromise.sessionOutputs.set(sessionId, '');
|
|
69
|
+
}
|
|
70
|
+
const currentSessionOutput = commandPromise.sessionOutputs.get(sessionId) || '';
|
|
71
|
+
commandPromise.sessionOutputs.set(sessionId, currentSessionOutput + output);
|
|
72
|
+
commandPromise.output = this.combineSessionOutputs(commandPromise.sessionOutputs);
|
|
73
|
+
/*console.log(
|
|
74
|
+
`📊 [Shell] 命令 ${commandKey} 累积输出长度: ${commandPromise.output.length}`,
|
|
75
|
+
);*/
|
|
76
|
+
// 调用输出回调(传递清理后的输出)
|
|
77
|
+
if (commandPromise.options.onOutput) {
|
|
78
|
+
const cleanOutput = this.cleanCommandOutput(output, commandPromise.command);
|
|
79
|
+
commandPromise.options.onOutput(cleanOutput);
|
|
87
80
|
}
|
|
88
81
|
}
|
|
89
82
|
}
|
|
@@ -92,56 +85,11 @@ class ShellX {
|
|
|
92
85
|
}
|
|
93
86
|
}
|
|
94
87
|
}
|
|
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
88
|
/**
|
|
112
89
|
* 清理命令输出,去除输入的命令内容
|
|
113
90
|
*/
|
|
114
91
|
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;
|
|
92
|
+
return output.replace(command, '');
|
|
145
93
|
}
|
|
146
94
|
/**
|
|
147
95
|
* 合并多个 session 的输出
|
|
@@ -156,75 +104,7 @@ class ShellX {
|
|
|
156
104
|
return combined;
|
|
157
105
|
}
|
|
158
106
|
/**
|
|
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
|
-
/**
|
|
227
|
-
* 通用重试机制
|
|
107
|
+
* 通用重试机制 - 失败时返回 undefined 而不是抛出异常
|
|
228
108
|
*/
|
|
229
109
|
withRetry(operation_1) {
|
|
230
110
|
return __awaiter(this, arguments, void 0, function* (operation, options = {}) {
|
|
@@ -235,7 +115,8 @@ class ShellX {
|
|
|
235
115
|
}
|
|
236
116
|
catch (error) {
|
|
237
117
|
if (attempt === retry) {
|
|
238
|
-
|
|
118
|
+
console.log(`❌ [Retry] 重试 ${retry} 次后仍然失败,返回 undefined:`, error);
|
|
119
|
+
return undefined;
|
|
239
120
|
}
|
|
240
121
|
if (onRetry) {
|
|
241
122
|
onRetry(attempt, error);
|
|
@@ -244,7 +125,7 @@ class ShellX {
|
|
|
244
125
|
yield new Promise(resolve => setTimeout(resolve, delay));
|
|
245
126
|
}
|
|
246
127
|
}
|
|
247
|
-
|
|
128
|
+
return undefined;
|
|
248
129
|
});
|
|
249
130
|
}
|
|
250
131
|
/**
|
|
@@ -284,7 +165,7 @@ class ShellX {
|
|
|
284
165
|
click(clickData) {
|
|
285
166
|
return __awaiter(this, void 0, void 0, function* () {
|
|
286
167
|
const startTime = Date.now();
|
|
287
|
-
|
|
168
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
288
169
|
try {
|
|
289
170
|
let target;
|
|
290
171
|
if (clickData.targetElementId) {
|
|
@@ -340,6 +221,15 @@ class ShellX {
|
|
|
340
221
|
throw new Error(`点击操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
341
222
|
}
|
|
342
223
|
}), { retry: clickData.retry, delay: 500 });
|
|
224
|
+
// 如果 withRetry 返回 undefined,返回失败的 ActionResult
|
|
225
|
+
if (!result) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
error: '点击操作失败',
|
|
229
|
+
duration: Date.now() - startTime
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return result;
|
|
343
233
|
});
|
|
344
234
|
}
|
|
345
235
|
/**
|
|
@@ -348,7 +238,7 @@ class ShellX {
|
|
|
348
238
|
input(inputData) {
|
|
349
239
|
return __awaiter(this, void 0, void 0, function* () {
|
|
350
240
|
const startTime = Date.now();
|
|
351
|
-
|
|
241
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
352
242
|
var _a, _b;
|
|
353
243
|
try {
|
|
354
244
|
let target;
|
|
@@ -398,6 +288,14 @@ class ShellX {
|
|
|
398
288
|
throw new Error(`输入操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
399
289
|
}
|
|
400
290
|
}), { retry: inputData.retry, delay: 500 });
|
|
291
|
+
if (!result) {
|
|
292
|
+
return {
|
|
293
|
+
success: false,
|
|
294
|
+
error: '输入操作失败',
|
|
295
|
+
duration: Date.now() - startTime
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return result;
|
|
401
299
|
});
|
|
402
300
|
}
|
|
403
301
|
/**
|
|
@@ -406,7 +304,7 @@ class ShellX {
|
|
|
406
304
|
swipe(swipeData) {
|
|
407
305
|
return __awaiter(this, void 0, void 0, function* () {
|
|
408
306
|
const startTime = Date.now();
|
|
409
|
-
|
|
307
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
410
308
|
try {
|
|
411
309
|
const from = { x: swipeData.fromX, y: swipeData.fromY };
|
|
412
310
|
const to = { x: swipeData.toX, y: swipeData.toY };
|
|
@@ -433,6 +331,14 @@ class ShellX {
|
|
|
433
331
|
throw new Error(`滑动操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
434
332
|
}
|
|
435
333
|
}), { retry: swipeData.retry, delay: 500 });
|
|
334
|
+
if (!result) {
|
|
335
|
+
return {
|
|
336
|
+
success: false,
|
|
337
|
+
error: '滑动操作失败',
|
|
338
|
+
duration: Date.now() - startTime
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
return result;
|
|
436
342
|
});
|
|
437
343
|
}
|
|
438
344
|
/**
|
|
@@ -441,7 +347,7 @@ class ShellX {
|
|
|
441
347
|
pressKey(keyData) {
|
|
442
348
|
return __awaiter(this, void 0, void 0, function* () {
|
|
443
349
|
const startTime = Date.now();
|
|
444
|
-
|
|
350
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
445
351
|
try {
|
|
446
352
|
const action = {
|
|
447
353
|
title: `按键操作: ${keyData.key}${keyData.longPress ? ' (长按)' : ''}`,
|
|
@@ -467,6 +373,14 @@ class ShellX {
|
|
|
467
373
|
throw new Error(`按键操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
468
374
|
}
|
|
469
375
|
}), { retry: keyData.retry, delay: 500 });
|
|
376
|
+
if (!result) {
|
|
377
|
+
return {
|
|
378
|
+
success: false,
|
|
379
|
+
error: '按键操作失败',
|
|
380
|
+
duration: Date.now() - startTime
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
return result;
|
|
470
384
|
});
|
|
471
385
|
}
|
|
472
386
|
/**
|
|
@@ -475,7 +389,7 @@ class ShellX {
|
|
|
475
389
|
wait(waitData) {
|
|
476
390
|
return __awaiter(this, void 0, void 0, function* () {
|
|
477
391
|
const startTime = Date.now();
|
|
478
|
-
|
|
392
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
479
393
|
try {
|
|
480
394
|
const selector = this.convertSelector(waitData);
|
|
481
395
|
const timeout = waitData.timeout || 10000;
|
|
@@ -489,7 +403,12 @@ class ShellX {
|
|
|
489
403
|
visibleOnly: waitData.condition === 'visible',
|
|
490
404
|
clickableOnly: waitData.condition === 'clickable'
|
|
491
405
|
});
|
|
492
|
-
|
|
406
|
+
// 如果服务器返回失败,跳过本次尝试
|
|
407
|
+
if (!result || result.success === false) {
|
|
408
|
+
yield new Promise(resolve => setTimeout(resolve, interval));
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (result.elements && result.elements.length > 0) {
|
|
493
412
|
if (waitData.condition === 'gone') {
|
|
494
413
|
// 等待元素消失,继续等待
|
|
495
414
|
yield new Promise(resolve => setTimeout(resolve, interval));
|
|
@@ -524,6 +443,14 @@ class ShellX {
|
|
|
524
443
|
throw new Error(`等待操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
525
444
|
}
|
|
526
445
|
}), { retry: waitData.retry, delay: 500 });
|
|
446
|
+
if (!result) {
|
|
447
|
+
return {
|
|
448
|
+
success: false,
|
|
449
|
+
error: '等待操作失败',
|
|
450
|
+
duration: Date.now() - startTime
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
527
454
|
});
|
|
528
455
|
}
|
|
529
456
|
/**
|
|
@@ -531,7 +458,7 @@ class ShellX {
|
|
|
531
458
|
*/
|
|
532
459
|
find(findData) {
|
|
533
460
|
return __awaiter(this, void 0, void 0, function* () {
|
|
534
|
-
|
|
461
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
535
462
|
try {
|
|
536
463
|
const selector = this.convertSelector(findData);
|
|
537
464
|
const result = yield this.client.findElement(selector, {
|
|
@@ -542,6 +469,13 @@ class ShellX {
|
|
|
542
469
|
clickableOnly: false,
|
|
543
470
|
multiple: findData.multiple || false
|
|
544
471
|
});
|
|
472
|
+
// 检查服务器是否返回了失败响应
|
|
473
|
+
if (!result || result.success === false) {
|
|
474
|
+
throw new Error((result === null || result === void 0 ? void 0 : result.errorMessage) || '查找元素失败');
|
|
475
|
+
}
|
|
476
|
+
if (!result.elements) {
|
|
477
|
+
throw new Error('未找到元素');
|
|
478
|
+
}
|
|
545
479
|
const elements = result.elements.map(element => this.convertElement(element));
|
|
546
480
|
return {
|
|
547
481
|
elements,
|
|
@@ -554,6 +488,15 @@ class ShellX {
|
|
|
554
488
|
throw new Error(`查找操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
555
489
|
}
|
|
556
490
|
}), { retry: findData.retry, delay: 500 });
|
|
491
|
+
if (!result) {
|
|
492
|
+
return {
|
|
493
|
+
elements: [],
|
|
494
|
+
count: 0,
|
|
495
|
+
success: false,
|
|
496
|
+
found: false
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
557
500
|
});
|
|
558
501
|
}
|
|
559
502
|
/**
|
|
@@ -561,7 +504,7 @@ class ShellX {
|
|
|
561
504
|
*/
|
|
562
505
|
executeCommand(commandData) {
|
|
563
506
|
return __awaiter(this, void 0, void 0, function* () {
|
|
564
|
-
|
|
507
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
565
508
|
try {
|
|
566
509
|
const result = yield this.executeShellCommand(commandData.cmd, {
|
|
567
510
|
title: `执行命令: ${commandData.cmd}`,
|
|
@@ -574,6 +517,35 @@ class ShellX {
|
|
|
574
517
|
throw new Error(`命令执行失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
575
518
|
}
|
|
576
519
|
}), { retry: commandData.retry, delay: 1000 });
|
|
520
|
+
if (!result) {
|
|
521
|
+
return {
|
|
522
|
+
success: false,
|
|
523
|
+
output: '',
|
|
524
|
+
error: '命令执行失败',
|
|
525
|
+
duration: 0
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
return result;
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
executeCodeEval(context, code, timeout) {
|
|
532
|
+
const evalInstance = require('eval');
|
|
533
|
+
return evalInstance(code, context, true);
|
|
534
|
+
}
|
|
535
|
+
executeCode(agentCode, context, timeout) {
|
|
536
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
537
|
+
// console.log('executeCode', agentCode);
|
|
538
|
+
// Interpreter.global = context;
|
|
539
|
+
// const evalFunc = getEvalInstance(context);
|
|
540
|
+
// 如果没有指定 timeout,直接执行
|
|
541
|
+
if (!timeout) {
|
|
542
|
+
return yield this.executeCodeEval(context, agentCode);
|
|
543
|
+
}
|
|
544
|
+
// 使用 timeout 执行 - 修复:await 应该在 Promise.race 上,而不是在内部的 promise 上
|
|
545
|
+
return yield Promise.race([
|
|
546
|
+
this.executeCodeEval(context, agentCode),
|
|
547
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`代码执行超时: ${timeout}ms`)), timeout))
|
|
548
|
+
]);
|
|
577
549
|
});
|
|
578
550
|
}
|
|
579
551
|
/**
|
|
@@ -582,7 +554,7 @@ class ShellX {
|
|
|
582
554
|
getAppInfo(appInfoData) {
|
|
583
555
|
return __awaiter(this, void 0, void 0, function* () {
|
|
584
556
|
const startTime = Date.now();
|
|
585
|
-
|
|
557
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
586
558
|
try {
|
|
587
559
|
const action = {
|
|
588
560
|
title: `获取应用信息: ${appInfoData.package}`,
|
|
@@ -602,6 +574,14 @@ class ShellX {
|
|
|
602
574
|
throw new Error(`获取应用信息失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
603
575
|
}
|
|
604
576
|
}), { retry: appInfoData.retry, delay: 500 });
|
|
577
|
+
if (!result) {
|
|
578
|
+
return {
|
|
579
|
+
success: false,
|
|
580
|
+
error: '获取应用信息失败',
|
|
581
|
+
duration: Date.now() - startTime
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
return result;
|
|
605
585
|
});
|
|
606
586
|
}
|
|
607
587
|
/**
|
|
@@ -610,7 +590,7 @@ class ShellX {
|
|
|
610
590
|
takeScreenshot() {
|
|
611
591
|
return __awaiter(this, arguments, void 0, function* (screenshotData = {}) {
|
|
612
592
|
const startTime = Date.now();
|
|
613
|
-
|
|
593
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
614
594
|
try {
|
|
615
595
|
const options = {
|
|
616
596
|
format: screenshotData.format || 'png',
|
|
@@ -627,6 +607,10 @@ class ShellX {
|
|
|
627
607
|
};
|
|
628
608
|
}
|
|
629
609
|
const screenshot = yield this.client.screenShot(options);
|
|
610
|
+
// 检查服务器是否返回了失败响应
|
|
611
|
+
if (!screenshot || screenshot.success === false) {
|
|
612
|
+
throw new Error((screenshot === null || screenshot === void 0 ? void 0 : screenshot.errorMessage) || '截图失败');
|
|
613
|
+
}
|
|
630
614
|
return {
|
|
631
615
|
success: true,
|
|
632
616
|
data: screenshot,
|
|
@@ -637,6 +621,14 @@ class ShellX {
|
|
|
637
621
|
throw new Error(`截图操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
638
622
|
}
|
|
639
623
|
}), { retry: screenshotData.retry, delay: 500 });
|
|
624
|
+
if (!result) {
|
|
625
|
+
return {
|
|
626
|
+
success: false,
|
|
627
|
+
error: '截图操作失败',
|
|
628
|
+
duration: Date.now() - startTime
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
640
632
|
});
|
|
641
633
|
}
|
|
642
634
|
/**
|
|
@@ -687,11 +679,13 @@ class ShellX {
|
|
|
687
679
|
result = yield this.takeScreenshot(action);
|
|
688
680
|
}
|
|
689
681
|
results.push(result);
|
|
690
|
-
//
|
|
682
|
+
// 记录操作结果但不抛出错误,让调用者决定如何处理失败的操作
|
|
691
683
|
if (!result.success) {
|
|
692
|
-
|
|
684
|
+
console.warn(`⚠️ [Actions] 第 ${index + 1} 个操作失败: ${result.error}`);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
console.log(`✅ [Actions] 第 ${index + 1} 个操作执行成功`);
|
|
693
688
|
}
|
|
694
|
-
console.log(`✅ [Actions] 第 ${index + 1} 个操作执行成功`);
|
|
695
689
|
}
|
|
696
690
|
catch (error) {
|
|
697
691
|
const errorResult = {
|
|
@@ -701,7 +695,7 @@ class ShellX {
|
|
|
701
695
|
};
|
|
702
696
|
results.push(errorResult);
|
|
703
697
|
console.error(`❌ [Actions] 第 ${index + 1} 个操作执行失败:`, error);
|
|
704
|
-
|
|
698
|
+
// 不再抛出错误,继续执行后续操作
|
|
705
699
|
}
|
|
706
700
|
}
|
|
707
701
|
return results;
|
|
@@ -1005,27 +999,17 @@ class ShellX {
|
|
|
1005
999
|
// 设置超时
|
|
1006
1000
|
const timeoutId = setTimeout(() => {
|
|
1007
1001
|
if (this.shellCommandPromises.has(commandKey)) {
|
|
1002
|
+
console.log(`✅ [Shell] 命令 ${command} 执行完成`);
|
|
1008
1003
|
const commandPromise = this.shellCommandPromises.get(commandKey);
|
|
1009
1004
|
this.shellCommandPromises.delete(commandKey);
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
else {
|
|
1018
|
-
resolve({
|
|
1019
|
-
success: true,
|
|
1020
|
-
output: "",
|
|
1021
|
-
error: `Command timeout after ${timeout}ms, but got partial output`,
|
|
1022
|
-
duration: Date.now() - startTime
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1005
|
+
resolve({
|
|
1006
|
+
success: true,
|
|
1007
|
+
output: commandPromise ? commandPromise.output.trim() : "",
|
|
1008
|
+
duration: Date.now() - startTime
|
|
1009
|
+
});
|
|
1025
1010
|
}
|
|
1026
1011
|
}, timeout);
|
|
1027
1012
|
try {
|
|
1028
|
-
// 构建 shell 命令操作
|
|
1029
1013
|
const shellAction = {
|
|
1030
1014
|
title,
|
|
1031
1015
|
actions: [{
|
|
@@ -1038,7 +1022,7 @@ class ShellX {
|
|
|
1038
1022
|
}
|
|
1039
1023
|
};
|
|
1040
1024
|
// 发送命令
|
|
1041
|
-
yield this.client.
|
|
1025
|
+
yield this.client.sendMessageWithTaskId({ actions: shellAction }, 'command', commandKey, timeout);
|
|
1042
1026
|
console.log(`📤 [Shell] 命令已发送: ${commandKey}`);
|
|
1043
1027
|
}
|
|
1044
1028
|
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.12",
|
|
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,9 @@
|
|
|
33
33
|
"cbor": "^9.0.0",
|
|
34
34
|
"dotenv": "^16.4.5",
|
|
35
35
|
"ofetch": "^1.4.1",
|
|
36
|
-
"uuid": "^11.1.0"
|
|
36
|
+
"uuid": "^11.1.0",
|
|
37
|
+
"promptflow-eval": "1.0.0",
|
|
38
|
+
"@babel/standalone": "^7.18.13"
|
|
37
39
|
},
|
|
38
40
|
"optionalDependencies": {
|
|
39
41
|
"node-fetch": "^3.3.2",
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
"langchain": "^0.1.34",
|
|
48
50
|
"promptflow-eval": "1.0.0",
|
|
49
51
|
"promptflow-template": "1.0.0",
|
|
52
|
+
"eval": "^0.1.8",
|
|
50
53
|
"promptflowx": "^0.1.9",
|
|
51
54
|
"ts-jest": "^29.4.0",
|
|
52
55
|
"ts-node": "^10.9.2",
|