shellx-ai 1.0.11 → 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.js +35 -21
- package/dist/shellx.d.ts +3 -1
- package/dist/shellx.js +131 -18
- package/package.json +5 -2
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
|
}
|
|
@@ -108,7 +107,11 @@ class ConnectionTaskClient {
|
|
|
108
107
|
var _a, _b;
|
|
109
108
|
try {
|
|
110
109
|
this.wsUrl = yield this.authenticateDevice(this.deviceId);
|
|
111
|
-
|
|
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);
|
|
112
115
|
// 获取适合当前环境的WebSocket构造函数
|
|
113
116
|
const WebSocketConstructor = yield getWebSocket();
|
|
114
117
|
this.ws = new WebSocketConstructor(this.wsUrl);
|
|
@@ -153,6 +156,7 @@ class ConnectionTaskClient {
|
|
|
153
156
|
}
|
|
154
157
|
authenticateDevice(deviceId) {
|
|
155
158
|
return __awaiter(this, void 0, void 0, function* () {
|
|
159
|
+
let fallbackUrl = undefined;
|
|
156
160
|
// 1. 优先检测本地服务
|
|
157
161
|
try {
|
|
158
162
|
// fetch超时实现
|
|
@@ -164,9 +168,9 @@ class ConnectionTaskClient {
|
|
|
164
168
|
if (localResp.ok) {
|
|
165
169
|
const info = yield localResp.json();
|
|
166
170
|
if (info && (info.status === 'ok' || info.status === 1) && info.uuid) {
|
|
167
|
-
if (deviceId == info.uuid) {
|
|
171
|
+
if (deviceId == undefined || deviceId == info.uuid) {
|
|
168
172
|
const localUuid = info.uuid;
|
|
169
|
-
|
|
173
|
+
fallbackUrl = `ws://127.0.0.1:9091/api/s/${localUuid}`;
|
|
170
174
|
console.log('✅ [Auth] 本地ShellX服务可用,使用本地 /info 返回的 uuid:', localUuid, ',服务地址:', fallbackUrl);
|
|
171
175
|
return fallbackUrl;
|
|
172
176
|
}
|
|
@@ -176,11 +180,15 @@ class ConnectionTaskClient {
|
|
|
176
180
|
catch (e) {
|
|
177
181
|
// 本地不可用,继续走远程
|
|
178
182
|
}
|
|
179
|
-
|
|
183
|
+
if (deviceId == undefined) {
|
|
184
|
+
console.warn('❌ [Auth] 设备ID未设置,本地未连接USB,请设置环境变量 SHELLX_DEVICE_ID 或者 USB连接设备');
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
180
187
|
// 2. 远程认证逻辑
|
|
181
188
|
authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
|
|
182
189
|
if (!authKey) {
|
|
183
|
-
|
|
190
|
+
authKey = (0, uuid_1.v4)();
|
|
191
|
+
console.log('✅ [Auth] SHELLX_AUTH_KEY 环境变量未设置, 生成新的authKey: ' + authKey);
|
|
184
192
|
}
|
|
185
193
|
try {
|
|
186
194
|
console.log('🔑 [Auth] 正在认证设备...');
|
|
@@ -192,6 +200,12 @@ class ConnectionTaskClient {
|
|
|
192
200
|
},
|
|
193
201
|
});
|
|
194
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
|
+
}
|
|
195
209
|
// const jsonData = JSON.parse(data);
|
|
196
210
|
console.log('✅ [Auth] ShellX.ai设备认证成功');
|
|
197
211
|
console.log(`📡 [Auth] 设备ID: ${jsonData.authenticate}`);
|
|
@@ -252,19 +266,16 @@ class ConnectionTaskClient {
|
|
|
252
266
|
try {
|
|
253
267
|
authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
|
|
254
268
|
if (!authKey) {
|
|
255
|
-
|
|
256
|
-
|
|
269
|
+
authKey = (0, uuid_1.v4)();
|
|
270
|
+
console.log('✅ [Auth] SHELLX_AUTH_KEY 环境变量未设置, 生成新的authKey: ' + authKey);
|
|
257
271
|
}
|
|
258
|
-
console.log('🔑 [Auth] 发送认证消息...');
|
|
259
|
-
// 从URL中提取认证密钥(如果URL包含认证信息)
|
|
260
|
-
// 发送认证消息
|
|
261
272
|
const authMessage = { authenticate: authKey };
|
|
262
273
|
console.log('📤 [Auth] 发送认证消息:', { authenticate: authKey });
|
|
263
274
|
if (this.ws && this.wsConnected) {
|
|
264
275
|
this.ws.send((0, cbor_1.encode)(authMessage));
|
|
265
276
|
}
|
|
266
277
|
else {
|
|
267
|
-
console.error('❌ [Auth]
|
|
278
|
+
console.error('❌ [Auth] ShellX 未连接,无法发送认证消息');
|
|
268
279
|
}
|
|
269
280
|
}
|
|
270
281
|
catch (error) {
|
|
@@ -391,12 +402,8 @@ class ConnectionTaskClient {
|
|
|
391
402
|
if (shouldResolve) {
|
|
392
403
|
clearTimeout(task.timer);
|
|
393
404
|
this.pendingTasks.delete(taskId);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
task.resolve(responseData);
|
|
399
|
-
}
|
|
405
|
+
// 始终 resolve,保持原始响应数据结构不变
|
|
406
|
+
task.resolve(responseData);
|
|
400
407
|
break; // Only resolve the first matching task
|
|
401
408
|
}
|
|
402
409
|
}
|
|
@@ -408,7 +415,9 @@ class ConnectionTaskClient {
|
|
|
408
415
|
if (taskType) {
|
|
409
416
|
const timer = setTimeout(() => {
|
|
410
417
|
this.pendingTasks.delete(taskId);
|
|
411
|
-
|
|
418
|
+
// 超时时返回 undefined 而不是 reject
|
|
419
|
+
console.log(`⏰ [ShellX] 任务超时: ${taskType}, 返回 undefined`);
|
|
420
|
+
resolve(undefined);
|
|
412
421
|
}, timeout ? timeout : this.config.timeout);
|
|
413
422
|
this.pendingTasks.set(taskId, {
|
|
414
423
|
resolve,
|
|
@@ -429,7 +438,9 @@ class ConnectionTaskClient {
|
|
|
429
438
|
if (taskType) {
|
|
430
439
|
this.pendingTasks.delete(taskId);
|
|
431
440
|
}
|
|
432
|
-
reject
|
|
441
|
+
// 发送失败时返回 undefined 而不是 reject
|
|
442
|
+
console.log(`❌ [ShellX] 发送消息失败,返回 undefined:`, error);
|
|
443
|
+
resolve(undefined);
|
|
433
444
|
}
|
|
434
445
|
}
|
|
435
446
|
else {
|
|
@@ -673,7 +684,7 @@ class ConnectionTaskClient {
|
|
|
673
684
|
});
|
|
674
685
|
}
|
|
675
686
|
flushQueue() {
|
|
676
|
-
while (this.messageQueue.length > 0 && this.ws) {
|
|
687
|
+
while (this.messageQueue.length > 0 && this.ws && this.shellxConnected) {
|
|
677
688
|
const message = this.messageQueue.shift();
|
|
678
689
|
if (message) {
|
|
679
690
|
try {
|
|
@@ -682,6 +693,9 @@ class ConnectionTaskClient {
|
|
|
682
693
|
}
|
|
683
694
|
catch (error) {
|
|
684
695
|
console.error('Failed to send queued message:', error);
|
|
696
|
+
// Re-queue the message if sending fails
|
|
697
|
+
this.messageQueue.unshift(message);
|
|
698
|
+
break;
|
|
685
699
|
}
|
|
686
700
|
}
|
|
687
701
|
}
|
package/dist/shellx.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export declare class ShellX {
|
|
|
49
49
|
*/
|
|
50
50
|
private combineSessionOutputs;
|
|
51
51
|
/**
|
|
52
|
-
* 通用重试机制
|
|
52
|
+
* 通用重试机制 - 失败时返回 undefined 而不是抛出异常
|
|
53
53
|
*/
|
|
54
54
|
private withRetry;
|
|
55
55
|
/**
|
|
@@ -88,6 +88,8 @@ export declare class ShellX {
|
|
|
88
88
|
* 执行命令 - 兼容现有的 shell 命令执行
|
|
89
89
|
*/
|
|
90
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>;
|
|
91
93
|
/**
|
|
92
94
|
* 获取应用信息
|
|
93
95
|
*/
|
package/dist/shellx.js
CHANGED
|
@@ -16,7 +16,6 @@ exports.ShellX = void 0;
|
|
|
16
16
|
exports.createShellX = createShellX;
|
|
17
17
|
exports.createShellXWithShellMonitoring = createShellXWithShellMonitoring;
|
|
18
18
|
const uuid_1 = require("uuid");
|
|
19
|
-
// 导入 WebSocketTaskClient 类
|
|
20
19
|
const index_1 = __importDefault(require("./index"));
|
|
21
20
|
const COMMAND_PTY_SID = 999;
|
|
22
21
|
/**
|
|
@@ -105,7 +104,7 @@ class ShellX {
|
|
|
105
104
|
return combined;
|
|
106
105
|
}
|
|
107
106
|
/**
|
|
108
|
-
* 通用重试机制
|
|
107
|
+
* 通用重试机制 - 失败时返回 undefined 而不是抛出异常
|
|
109
108
|
*/
|
|
110
109
|
withRetry(operation_1) {
|
|
111
110
|
return __awaiter(this, arguments, void 0, function* (operation, options = {}) {
|
|
@@ -116,7 +115,8 @@ class ShellX {
|
|
|
116
115
|
}
|
|
117
116
|
catch (error) {
|
|
118
117
|
if (attempt === retry) {
|
|
119
|
-
|
|
118
|
+
console.log(`❌ [Retry] 重试 ${retry} 次后仍然失败,返回 undefined:`, error);
|
|
119
|
+
return undefined;
|
|
120
120
|
}
|
|
121
121
|
if (onRetry) {
|
|
122
122
|
onRetry(attempt, error);
|
|
@@ -125,7 +125,7 @@ class ShellX {
|
|
|
125
125
|
yield new Promise(resolve => setTimeout(resolve, delay));
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
|
|
128
|
+
return undefined;
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
@@ -165,7 +165,7 @@ class ShellX {
|
|
|
165
165
|
click(clickData) {
|
|
166
166
|
return __awaiter(this, void 0, void 0, function* () {
|
|
167
167
|
const startTime = Date.now();
|
|
168
|
-
|
|
168
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
169
169
|
try {
|
|
170
170
|
let target;
|
|
171
171
|
if (clickData.targetElementId) {
|
|
@@ -221,6 +221,15 @@ class ShellX {
|
|
|
221
221
|
throw new Error(`点击操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
222
222
|
}
|
|
223
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;
|
|
224
233
|
});
|
|
225
234
|
}
|
|
226
235
|
/**
|
|
@@ -229,7 +238,7 @@ class ShellX {
|
|
|
229
238
|
input(inputData) {
|
|
230
239
|
return __awaiter(this, void 0, void 0, function* () {
|
|
231
240
|
const startTime = Date.now();
|
|
232
|
-
|
|
241
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
233
242
|
var _a, _b;
|
|
234
243
|
try {
|
|
235
244
|
let target;
|
|
@@ -279,6 +288,14 @@ class ShellX {
|
|
|
279
288
|
throw new Error(`输入操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
280
289
|
}
|
|
281
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;
|
|
282
299
|
});
|
|
283
300
|
}
|
|
284
301
|
/**
|
|
@@ -287,7 +304,7 @@ class ShellX {
|
|
|
287
304
|
swipe(swipeData) {
|
|
288
305
|
return __awaiter(this, void 0, void 0, function* () {
|
|
289
306
|
const startTime = Date.now();
|
|
290
|
-
|
|
307
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
291
308
|
try {
|
|
292
309
|
const from = { x: swipeData.fromX, y: swipeData.fromY };
|
|
293
310
|
const to = { x: swipeData.toX, y: swipeData.toY };
|
|
@@ -314,6 +331,14 @@ class ShellX {
|
|
|
314
331
|
throw new Error(`滑动操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
315
332
|
}
|
|
316
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;
|
|
317
342
|
});
|
|
318
343
|
}
|
|
319
344
|
/**
|
|
@@ -322,7 +347,7 @@ class ShellX {
|
|
|
322
347
|
pressKey(keyData) {
|
|
323
348
|
return __awaiter(this, void 0, void 0, function* () {
|
|
324
349
|
const startTime = Date.now();
|
|
325
|
-
|
|
350
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
326
351
|
try {
|
|
327
352
|
const action = {
|
|
328
353
|
title: `按键操作: ${keyData.key}${keyData.longPress ? ' (长按)' : ''}`,
|
|
@@ -348,6 +373,14 @@ class ShellX {
|
|
|
348
373
|
throw new Error(`按键操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
349
374
|
}
|
|
350
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;
|
|
351
384
|
});
|
|
352
385
|
}
|
|
353
386
|
/**
|
|
@@ -356,7 +389,7 @@ class ShellX {
|
|
|
356
389
|
wait(waitData) {
|
|
357
390
|
return __awaiter(this, void 0, void 0, function* () {
|
|
358
391
|
const startTime = Date.now();
|
|
359
|
-
|
|
392
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
360
393
|
try {
|
|
361
394
|
const selector = this.convertSelector(waitData);
|
|
362
395
|
const timeout = waitData.timeout || 10000;
|
|
@@ -370,7 +403,12 @@ class ShellX {
|
|
|
370
403
|
visibleOnly: waitData.condition === 'visible',
|
|
371
404
|
clickableOnly: waitData.condition === 'clickable'
|
|
372
405
|
});
|
|
373
|
-
|
|
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) {
|
|
374
412
|
if (waitData.condition === 'gone') {
|
|
375
413
|
// 等待元素消失,继续等待
|
|
376
414
|
yield new Promise(resolve => setTimeout(resolve, interval));
|
|
@@ -405,6 +443,14 @@ class ShellX {
|
|
|
405
443
|
throw new Error(`等待操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
406
444
|
}
|
|
407
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;
|
|
408
454
|
});
|
|
409
455
|
}
|
|
410
456
|
/**
|
|
@@ -412,7 +458,7 @@ class ShellX {
|
|
|
412
458
|
*/
|
|
413
459
|
find(findData) {
|
|
414
460
|
return __awaiter(this, void 0, void 0, function* () {
|
|
415
|
-
|
|
461
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
416
462
|
try {
|
|
417
463
|
const selector = this.convertSelector(findData);
|
|
418
464
|
const result = yield this.client.findElement(selector, {
|
|
@@ -423,6 +469,13 @@ class ShellX {
|
|
|
423
469
|
clickableOnly: false,
|
|
424
470
|
multiple: findData.multiple || false
|
|
425
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
|
+
}
|
|
426
479
|
const elements = result.elements.map(element => this.convertElement(element));
|
|
427
480
|
return {
|
|
428
481
|
elements,
|
|
@@ -435,6 +488,15 @@ class ShellX {
|
|
|
435
488
|
throw new Error(`查找操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
436
489
|
}
|
|
437
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;
|
|
438
500
|
});
|
|
439
501
|
}
|
|
440
502
|
/**
|
|
@@ -442,7 +504,7 @@ class ShellX {
|
|
|
442
504
|
*/
|
|
443
505
|
executeCommand(commandData) {
|
|
444
506
|
return __awaiter(this, void 0, void 0, function* () {
|
|
445
|
-
|
|
507
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
446
508
|
try {
|
|
447
509
|
const result = yield this.executeShellCommand(commandData.cmd, {
|
|
448
510
|
title: `执行命令: ${commandData.cmd}`,
|
|
@@ -455,6 +517,35 @@ class ShellX {
|
|
|
455
517
|
throw new Error(`命令执行失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
456
518
|
}
|
|
457
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
|
+
]);
|
|
458
549
|
});
|
|
459
550
|
}
|
|
460
551
|
/**
|
|
@@ -463,7 +554,7 @@ class ShellX {
|
|
|
463
554
|
getAppInfo(appInfoData) {
|
|
464
555
|
return __awaiter(this, void 0, void 0, function* () {
|
|
465
556
|
const startTime = Date.now();
|
|
466
|
-
|
|
557
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
467
558
|
try {
|
|
468
559
|
const action = {
|
|
469
560
|
title: `获取应用信息: ${appInfoData.package}`,
|
|
@@ -483,6 +574,14 @@ class ShellX {
|
|
|
483
574
|
throw new Error(`获取应用信息失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
484
575
|
}
|
|
485
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;
|
|
486
585
|
});
|
|
487
586
|
}
|
|
488
587
|
/**
|
|
@@ -491,7 +590,7 @@ class ShellX {
|
|
|
491
590
|
takeScreenshot() {
|
|
492
591
|
return __awaiter(this, arguments, void 0, function* (screenshotData = {}) {
|
|
493
592
|
const startTime = Date.now();
|
|
494
|
-
|
|
593
|
+
const result = yield this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
|
|
495
594
|
try {
|
|
496
595
|
const options = {
|
|
497
596
|
format: screenshotData.format || 'png',
|
|
@@ -508,6 +607,10 @@ class ShellX {
|
|
|
508
607
|
};
|
|
509
608
|
}
|
|
510
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
|
+
}
|
|
511
614
|
return {
|
|
512
615
|
success: true,
|
|
513
616
|
data: screenshot,
|
|
@@ -518,6 +621,14 @@ class ShellX {
|
|
|
518
621
|
throw new Error(`截图操作失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
519
622
|
}
|
|
520
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;
|
|
521
632
|
});
|
|
522
633
|
}
|
|
523
634
|
/**
|
|
@@ -568,11 +679,13 @@ class ShellX {
|
|
|
568
679
|
result = yield this.takeScreenshot(action);
|
|
569
680
|
}
|
|
570
681
|
results.push(result);
|
|
571
|
-
//
|
|
682
|
+
// 记录操作结果但不抛出错误,让调用者决定如何处理失败的操作
|
|
572
683
|
if (!result.success) {
|
|
573
|
-
|
|
684
|
+
console.warn(`⚠️ [Actions] 第 ${index + 1} 个操作失败: ${result.error}`);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
console.log(`✅ [Actions] 第 ${index + 1} 个操作执行成功`);
|
|
574
688
|
}
|
|
575
|
-
console.log(`✅ [Actions] 第 ${index + 1} 个操作执行成功`);
|
|
576
689
|
}
|
|
577
690
|
catch (error) {
|
|
578
691
|
const errorResult = {
|
|
@@ -582,7 +695,7 @@ class ShellX {
|
|
|
582
695
|
};
|
|
583
696
|
results.push(errorResult);
|
|
584
697
|
console.error(`❌ [Actions] 第 ${index + 1} 个操作执行失败:`, error);
|
|
585
|
-
|
|
698
|
+
// 不再抛出错误,继续执行后续操作
|
|
586
699
|
}
|
|
587
700
|
}
|
|
588
701
|
return results;
|
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",
|