shellx-ai 1.0.0 → 1.0.2
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/README.md +104 -7
- package/dist/index.d.ts +66 -4
- package/dist/index.js +332 -47
- package/dist/protocol.d.ts +1 -1
- package/dist/shellx.d.ts +9 -5
- package/dist/shellx.js +190 -150
- package/package.json +14 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
36
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
37
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -14,48 +47,145 @@ const uuid_1 = require("uuid");
|
|
|
14
47
|
const constants_1 = require("./constants");
|
|
15
48
|
const utils_1 = require("./utils");
|
|
16
49
|
const cbor_x_1 = require("cbor-x");
|
|
50
|
+
/**
|
|
51
|
+
* 获取适合当前环境的WebSocket构造函数
|
|
52
|
+
*/
|
|
53
|
+
function getWebSocket() {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
// 检查是否已有全局WebSocket(浏览器环境或Node.js 20+)
|
|
56
|
+
if (typeof globalThis.WebSocket !== 'undefined') {
|
|
57
|
+
return globalThis.WebSocket;
|
|
58
|
+
}
|
|
59
|
+
// Node.js环境下动态导入ws模块
|
|
60
|
+
try {
|
|
61
|
+
// @ts-ignore - Dynamic import may not have types
|
|
62
|
+
const wsModule = yield Promise.resolve().then(() => __importStar(require('ws')));
|
|
63
|
+
return wsModule.default || wsModule;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('❌ [WebSocket] ws模块不可用,请安装: npm install ws');
|
|
67
|
+
throw new Error('WebSocket not available. Please install ws module: npm install ws');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
17
71
|
/**
|
|
18
72
|
* Enhanced WebSocket Task Client with protocol-aware task methods
|
|
19
73
|
*/
|
|
20
74
|
class WebSocketTaskClient {
|
|
21
75
|
constructor(wsUrl, config = {}) {
|
|
22
|
-
this.ws = null;
|
|
23
|
-
this.
|
|
76
|
+
this.ws = null; // Use any to avoid WebSocket type issues
|
|
77
|
+
this.shellxConnected = false; // ShellX.ai 连接状态
|
|
78
|
+
this.wsConnected = false; // WebSocket 连接状态
|
|
79
|
+
this.authenticated = false; // 认证状态
|
|
24
80
|
this.pendingTasks = new Map();
|
|
25
81
|
this.messageQueue = [];
|
|
26
82
|
this.reconnectAttempts = 0;
|
|
27
83
|
this.pingIntervalId = null;
|
|
84
|
+
this.initializationPromise = null;
|
|
85
|
+
this.shellx = null; // 关联的 ShellX 实例
|
|
28
86
|
this.wsUrl = wsUrl;
|
|
29
87
|
this.config = Object.assign(Object.assign({}, constants_1.DEFAULT_CONFIG), config);
|
|
30
|
-
this.init();
|
|
88
|
+
this.initializationPromise = this.init();
|
|
31
89
|
}
|
|
32
90
|
init() {
|
|
33
|
-
|
|
34
|
-
this.ws = new WebSocket(this.wsUrl);
|
|
35
|
-
this.ws.binaryType = 'arraybuffer';
|
|
36
|
-
this.ws.onopen = () => {
|
|
37
|
-
var _a, _b;
|
|
38
|
-
console.log('Connected to WebSocket server');
|
|
39
|
-
this.connected = true;
|
|
40
|
-
this.reconnectAttempts = 0;
|
|
41
|
-
(_b = (_a = this.config).onOpen) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
42
|
-
this.flushQueue();
|
|
43
|
-
this.startPing();
|
|
44
|
-
};
|
|
45
|
-
this.ws.onmessage = (event) => {
|
|
46
|
-
this.handleMessage(event);
|
|
47
|
-
};
|
|
48
|
-
this.ws.onclose = (event) => {
|
|
49
|
-
var _a, _b;
|
|
50
|
-
this.connected = false;
|
|
51
|
-
(_b = (_a = this.config).onClose) === null || _b === void 0 ? void 0 : _b.call(_a, event);
|
|
52
|
-
this.stopPing();
|
|
53
|
-
this.reconnect();
|
|
54
|
-
};
|
|
55
|
-
this.ws.onerror = (error) => {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
56
92
|
var _a, _b;
|
|
57
|
-
|
|
58
|
-
|
|
93
|
+
try {
|
|
94
|
+
console.log('Initializing WebSocket client...');
|
|
95
|
+
// 获取适合当前环境的WebSocket构造函数
|
|
96
|
+
const WebSocketConstructor = yield getWebSocket();
|
|
97
|
+
this.ws = new WebSocketConstructor(this.wsUrl);
|
|
98
|
+
// 设置二进制类型(如果支持)
|
|
99
|
+
if (this.ws.binaryType !== undefined) {
|
|
100
|
+
this.ws.binaryType = 'arraybuffer';
|
|
101
|
+
}
|
|
102
|
+
this.ws.onopen = () => {
|
|
103
|
+
console.log('🔗 [ShellX] WebSocket连接已建立,发送认证消息...');
|
|
104
|
+
this.wsConnected = true;
|
|
105
|
+
this.reconnectAttempts = 0;
|
|
106
|
+
// 连接建立后立即发送认证消息
|
|
107
|
+
this.sendAuthenticationMessage();
|
|
108
|
+
};
|
|
109
|
+
this.ws.onmessage = (event) => {
|
|
110
|
+
this.handleMessage(event);
|
|
111
|
+
};
|
|
112
|
+
this.ws.onclose = (event) => {
|
|
113
|
+
var _a, _b;
|
|
114
|
+
this.wsConnected = false;
|
|
115
|
+
this.shellxConnected = false;
|
|
116
|
+
this.authenticated = false;
|
|
117
|
+
(_b = (_a = this.config).onClose) === null || _b === void 0 ? void 0 : _b.call(_a, event);
|
|
118
|
+
this.stopPing();
|
|
119
|
+
this.reconnect();
|
|
120
|
+
};
|
|
121
|
+
this.ws.onerror = (error) => {
|
|
122
|
+
var _a, _b;
|
|
123
|
+
(_b = (_a = this.config).onError) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error('❌ [WebSocket] 初始化失败:', error);
|
|
128
|
+
(_b = (_a = this.config).onError) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 等待WebSocket初始化完成
|
|
135
|
+
*/
|
|
136
|
+
waitForInitialization() {
|
|
137
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
138
|
+
if (this.initializationPromise) {
|
|
139
|
+
yield this.initializationPromise;
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 发送认证消息
|
|
145
|
+
*/
|
|
146
|
+
sendAuthenticationMessage() {
|
|
147
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
148
|
+
try {
|
|
149
|
+
const authKey = process.env.SHELLX_AUTH_KEY;
|
|
150
|
+
if (!authKey) {
|
|
151
|
+
console.error('❌ [Auth] SHELLX_AUTH_KEY 环境变量未设置');
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.log('🔑 [Auth] 发送认证消息...');
|
|
155
|
+
// 从URL中提取认证密钥(如果URL包含认证信息)
|
|
156
|
+
const urlAuthKey = this.extractAuthKeyFromUrl();
|
|
157
|
+
const finalAuthKey = urlAuthKey || authKey;
|
|
158
|
+
// 创建认证数据(这里使用简单的字符串,实际可能需要更复杂的格式)
|
|
159
|
+
const authData = new TextEncoder().encode(finalAuthKey);
|
|
160
|
+
// 发送认证消息
|
|
161
|
+
const authMessage = { authenticate: authData };
|
|
162
|
+
console.log('📤 [Auth] 发送认证消息:', { authenticate: finalAuthKey });
|
|
163
|
+
if (this.ws && this.wsConnected) {
|
|
164
|
+
this.ws.send((0, cbor_x_1.encode)(authMessage));
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.error('❌ [Auth] WebSocket未连接,无法发送认证消息');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.error('❌ [Auth] 发送认证消息失败:', error);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 从WebSocket URL中提取认证密钥
|
|
177
|
+
*/
|
|
178
|
+
extractAuthKeyFromUrl() {
|
|
179
|
+
try {
|
|
180
|
+
const url = new URL(this.wsUrl);
|
|
181
|
+
// 尝试从路径中提取认证密钥
|
|
182
|
+
const pathParts = url.pathname.split('/');
|
|
183
|
+
const authKey = pathParts[pathParts.length - 1];
|
|
184
|
+
return authKey && authKey !== 's' ? authKey : null;
|
|
185
|
+
}
|
|
186
|
+
catch (error) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
59
189
|
}
|
|
60
190
|
handleMessage(event) {
|
|
61
191
|
let serverMessage;
|
|
@@ -96,23 +226,32 @@ class WebSocketTaskClient {
|
|
|
96
226
|
}
|
|
97
227
|
}
|
|
98
228
|
processServerMessage(message) {
|
|
99
|
-
var _a, _b;
|
|
100
|
-
console.log('
|
|
229
|
+
var _a, _b, _c, _d;
|
|
230
|
+
//console.log('📨 [ShellX] 收到服务器消息:', message);
|
|
231
|
+
this.authenticated = true;
|
|
232
|
+
// 只有在认证成功后才处理其他消息并认为连接成功
|
|
233
|
+
if (this.authenticated && !this.shellxConnected) {
|
|
234
|
+
this.shellxConnected = true;
|
|
235
|
+
console.log('✅ [ShellX] ShellX.ai服务连接成功!');
|
|
236
|
+
(_b = (_a = this.config).onOpen) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
237
|
+
this.flushQueue();
|
|
238
|
+
this.startPing();
|
|
239
|
+
}
|
|
101
240
|
// Call user-defined message handler
|
|
102
|
-
(
|
|
241
|
+
(_d = (_c = this.config).onMessage) === null || _d === void 0 ? void 0 : _d.call(_c, message);
|
|
103
242
|
// Handle specific message types and resolve pending tasks
|
|
104
243
|
if (message.jsonData) {
|
|
105
244
|
this.handleJsonDataResponse(message.jsonData);
|
|
106
245
|
}
|
|
107
246
|
// Handle other server message types (hello, users, shells, etc.)
|
|
108
247
|
if (message.hello !== undefined) {
|
|
109
|
-
console.log('
|
|
248
|
+
console.log('👋 [ShellX] 服务器问候,用户ID:', message.hello);
|
|
110
249
|
}
|
|
111
250
|
if (message.pong !== undefined) {
|
|
112
|
-
console.log('
|
|
251
|
+
//console.log('🏓 [ShellX] 收到心跳响应:', message.pong);
|
|
113
252
|
}
|
|
114
253
|
if (message.error) {
|
|
115
|
-
console.error('
|
|
254
|
+
console.error('❌ [ShellX] 服务器错误:', message.error);
|
|
116
255
|
}
|
|
117
256
|
}
|
|
118
257
|
handleJsonDataResponse(jsonData) {
|
|
@@ -152,10 +291,12 @@ class WebSocketTaskClient {
|
|
|
152
291
|
}
|
|
153
292
|
break;
|
|
154
293
|
case 'action':
|
|
294
|
+
/*
|
|
155
295
|
if (jsonData.action_event) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
296
|
+
responseData = jsonData.action_event;
|
|
297
|
+
shouldResolve = true;
|
|
298
|
+
}*/
|
|
299
|
+
// 命令,按键,滑动,点击,等待,查找,等待,屏幕截图,屏幕信息,屏幕变化,应用列表, 需要自己处理
|
|
159
300
|
break;
|
|
160
301
|
}
|
|
161
302
|
if (shouldResolve) {
|
|
@@ -171,7 +312,7 @@ class WebSocketTaskClient {
|
|
|
171
312
|
}
|
|
172
313
|
}
|
|
173
314
|
}
|
|
174
|
-
sendMessage(message, taskType) {
|
|
315
|
+
sendMessage(message, taskType, commandType) {
|
|
175
316
|
return new Promise((resolve, reject) => {
|
|
176
317
|
const taskId = (0, uuid_1.v4)();
|
|
177
318
|
if (taskType) {
|
|
@@ -179,11 +320,12 @@ class WebSocketTaskClient {
|
|
|
179
320
|
this.pendingTasks.delete(taskId);
|
|
180
321
|
reject(new Error(`Task ${taskType} timeout (${this.config.timeout}ms)`));
|
|
181
322
|
}, this.config.timeout);
|
|
182
|
-
this.pendingTasks.set(taskId, { resolve, reject, timer, type: taskType });
|
|
323
|
+
this.pendingTasks.set(taskId, { resolve, reject, timer, type: taskType, commandType });
|
|
324
|
+
console.log(`📋 [ShellX] 创建任务: ${taskId}, 类型: ${taskType}}`);
|
|
183
325
|
}
|
|
184
|
-
if (this.
|
|
326
|
+
if (this.shellxConnected && this.ws) {
|
|
185
327
|
try {
|
|
186
|
-
console.log('
|
|
328
|
+
console.log('📤 [ShellX] 发送消息:', message);
|
|
187
329
|
this.ws.send((0, cbor_x_1.encode)(message));
|
|
188
330
|
if (!taskType)
|
|
189
331
|
resolve(undefined); // For fire-and-forget messages
|
|
@@ -196,6 +338,7 @@ class WebSocketTaskClient {
|
|
|
196
338
|
}
|
|
197
339
|
}
|
|
198
340
|
else {
|
|
341
|
+
console.log('⏳ [ShellX] 连接未就绪,消息已加入队列');
|
|
199
342
|
this.messageQueue.push(message);
|
|
200
343
|
if (!taskType)
|
|
201
344
|
resolve(undefined);
|
|
@@ -264,9 +407,9 @@ class WebSocketTaskClient {
|
|
|
264
407
|
/**
|
|
265
408
|
* Execute an action sequence
|
|
266
409
|
*/
|
|
267
|
-
executeAction(actionSequence) {
|
|
410
|
+
executeAction(actionSequence, taskId) {
|
|
268
411
|
return __awaiter(this, void 0, void 0, function* () {
|
|
269
|
-
return this.
|
|
412
|
+
return this.sendMessageWithTaskId({ actions: actionSequence }, 'action', taskId);
|
|
270
413
|
});
|
|
271
414
|
}
|
|
272
415
|
/**
|
|
@@ -327,12 +470,69 @@ class WebSocketTaskClient {
|
|
|
327
470
|
return this.sendMessage(message);
|
|
328
471
|
});
|
|
329
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* 发送消息并返回 taskId,允许手动控制任务完成
|
|
475
|
+
* @param message 要发送的消息
|
|
476
|
+
* @param taskType 任务类型
|
|
477
|
+
* @returns Promise<{taskId: string, promise: Promise<any>}>
|
|
478
|
+
*/
|
|
479
|
+
sendMessageWithTaskId(message, taskType, definedTaskId) {
|
|
480
|
+
return new Promise((resolve) => {
|
|
481
|
+
let taskId = definedTaskId;
|
|
482
|
+
if (taskId == null) {
|
|
483
|
+
taskId = (0, uuid_1.v4)();
|
|
484
|
+
}
|
|
485
|
+
if (taskType) {
|
|
486
|
+
const timer = setTimeout(() => {
|
|
487
|
+
this.pendingTasks.delete(taskId);
|
|
488
|
+
console.log(`⏰ [ShellX] 任务超时: ${taskId}, 类型: ${taskType}`);
|
|
489
|
+
}, this.config.timeout);
|
|
490
|
+
this.pendingTasks.set(taskId, {
|
|
491
|
+
resolve: () => { },
|
|
492
|
+
reject: () => { },
|
|
493
|
+
timer,
|
|
494
|
+
type: taskType
|
|
495
|
+
});
|
|
496
|
+
console.log(`📋 [ShellX] 创建可手动控制的任务: ${taskId}, 类型: ${taskType}}`);
|
|
497
|
+
}
|
|
498
|
+
if (this.shellxConnected && this.ws) {
|
|
499
|
+
try {
|
|
500
|
+
console.log('📤 [ShellX] 发送消息:', message);
|
|
501
|
+
this.ws.send((0, cbor_x_1.encode)(message));
|
|
502
|
+
const promise = new Promise((promiseResolve, promiseReject) => {
|
|
503
|
+
if (taskType) {
|
|
504
|
+
const task = this.pendingTasks.get(taskId);
|
|
505
|
+
if (task) {
|
|
506
|
+
task.resolve = promiseResolve;
|
|
507
|
+
task.reject = promiseReject;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
promiseResolve(undefined);
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
resolve({ taskId, promise });
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
if (taskType) {
|
|
518
|
+
this.pendingTasks.delete(taskId);
|
|
519
|
+
}
|
|
520
|
+
resolve({ taskId, promise: Promise.reject(error) });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
console.log('⏳ [ShellX] 连接未就绪,消息已加入队列');
|
|
525
|
+
this.messageQueue.push(message);
|
|
526
|
+
resolve({ taskId, promise: Promise.resolve(undefined) });
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
330
530
|
// ===== CONNECTION MANAGEMENT =====
|
|
331
531
|
startPing() {
|
|
332
532
|
this.stopPing();
|
|
333
|
-
console.log('
|
|
533
|
+
console.log('🏓 [ShellX] 开始心跳检测...');
|
|
334
534
|
this.pingIntervalId = setInterval(() => {
|
|
335
|
-
if (this.ws && this.
|
|
535
|
+
if (this.ws && this.shellxConnected) {
|
|
336
536
|
this.ws.send((0, cbor_x_1.encode)({ ping: BigInt(Date.now()) }));
|
|
337
537
|
}
|
|
338
538
|
}, this.config.pingInterval);
|
|
@@ -371,7 +571,9 @@ class WebSocketTaskClient {
|
|
|
371
571
|
}
|
|
372
572
|
close() {
|
|
373
573
|
var _a;
|
|
374
|
-
this.
|
|
574
|
+
this.shellxConnected = false;
|
|
575
|
+
this.wsConnected = false;
|
|
576
|
+
this.authenticated = false;
|
|
375
577
|
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();
|
|
376
578
|
this.pendingTasks.clear();
|
|
377
579
|
this.messageQueue = [];
|
|
@@ -379,7 +581,13 @@ class WebSocketTaskClient {
|
|
|
379
581
|
}
|
|
380
582
|
// Getters for debugging/monitoring
|
|
381
583
|
get isConnected() {
|
|
382
|
-
return this.
|
|
584
|
+
return this.shellxConnected;
|
|
585
|
+
}
|
|
586
|
+
get isWebSocketConnected() {
|
|
587
|
+
return this.wsConnected;
|
|
588
|
+
}
|
|
589
|
+
get isAuthenticated() {
|
|
590
|
+
return this.authenticated;
|
|
383
591
|
}
|
|
384
592
|
get pendingTaskCount() {
|
|
385
593
|
return this.pendingTasks.size;
|
|
@@ -387,6 +595,83 @@ class WebSocketTaskClient {
|
|
|
387
595
|
get queuedMessageCount() {
|
|
388
596
|
return this.messageQueue.length;
|
|
389
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* 设置关联的 ShellX 实例
|
|
600
|
+
*/
|
|
601
|
+
setShellX(shellx) {
|
|
602
|
+
this.shellx = shellx;
|
|
603
|
+
console.log('🔗 [ShellX] 已关联 ShellX 实例');
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* 获取关联的 ShellX 实例
|
|
607
|
+
*/
|
|
608
|
+
getShellX() {
|
|
609
|
+
return this.shellx;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* 手动完成指定 taskId 的任务
|
|
613
|
+
* @param taskId 任务ID
|
|
614
|
+
* @param result 任务结果
|
|
615
|
+
* @param success 是否成功
|
|
616
|
+
*/
|
|
617
|
+
completeTask(taskId, result, success = true) {
|
|
618
|
+
const task = this.pendingTasks.get(taskId);
|
|
619
|
+
if (!task) {
|
|
620
|
+
console.log(`⚠️ [ShellX] 未找到任务: ${taskId}`);
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
console.log(`✅ [ShellX] 手动完成任务: ${taskId}, 类型: ${task.type}, 成功: ${success}`);
|
|
624
|
+
// 清除超时定时器
|
|
625
|
+
clearTimeout(task.timer);
|
|
626
|
+
// 从待处理任务中移除
|
|
627
|
+
this.pendingTasks.delete(taskId);
|
|
628
|
+
// 根据成功状态调用相应的回调
|
|
629
|
+
if (success) {
|
|
630
|
+
task.resolve(result || { success: true, taskId });
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
task.reject(new Error(`Task ${taskId} manually failed`));
|
|
634
|
+
}
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* 获取所有待处理任务的ID列表
|
|
639
|
+
*/
|
|
640
|
+
getPendingTaskIds() {
|
|
641
|
+
return Array.from(this.pendingTasks.keys());
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* 获取指定任务的信息
|
|
645
|
+
*/
|
|
646
|
+
getTaskInfo(taskId) {
|
|
647
|
+
const task = this.pendingTasks.get(taskId);
|
|
648
|
+
if (!task) {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
651
|
+
return {
|
|
652
|
+
type: task.type,
|
|
653
|
+
commandType: task.commandType
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* 批量完成指定类型的任务
|
|
658
|
+
* @param taskType 任务类型
|
|
659
|
+
* @param result 任务结果
|
|
660
|
+
* @param success 是否成功
|
|
661
|
+
*/
|
|
662
|
+
completeTasksByType(taskType, result, success = true) {
|
|
663
|
+
const taskIds = this.getPendingTaskIds();
|
|
664
|
+
let completedCount = 0;
|
|
665
|
+
for (const taskId of taskIds) {
|
|
666
|
+
const task = this.pendingTasks.get(taskId);
|
|
667
|
+
if (task && task.type === taskType) {
|
|
668
|
+
this.completeTask(taskId, result, success);
|
|
669
|
+
completedCount++;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
console.log(`📦 [ShellX] 批量完成 ${completedCount} 个 ${taskType} 类型的任务`);
|
|
673
|
+
return completedCount;
|
|
674
|
+
}
|
|
390
675
|
}
|
|
391
676
|
exports.WebSocketTaskClient = WebSocketTaskClient;
|
|
392
677
|
exports.default = WebSocketTaskClient;
|
package/dist/protocol.d.ts
CHANGED
|
@@ -159,7 +159,7 @@ export type ScreenShotOptions = {
|
|
|
159
159
|
};
|
|
160
160
|
/** Client message type, see the Rust version. */
|
|
161
161
|
export type WsClient = {
|
|
162
|
-
authenticate?:
|
|
162
|
+
authenticate?: string;
|
|
163
163
|
setName?: string;
|
|
164
164
|
setCursor?: [number, number] | null;
|
|
165
165
|
setFocus?: number | null;
|
package/dist/shellx.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ interface ShellCommandOptions {
|
|
|
19
19
|
onError?: (error: string) => void;
|
|
20
20
|
expectedOutput?: string | RegExp;
|
|
21
21
|
successPattern?: string | RegExp;
|
|
22
|
-
|
|
22
|
+
allowPartialResult?: boolean;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* ShellX automation utilities for common patterns
|
|
@@ -41,14 +41,18 @@ export declare class ShellX {
|
|
|
41
41
|
* 判断输出是否属于特定命令
|
|
42
42
|
*/
|
|
43
43
|
private isOutputForCommand;
|
|
44
|
+
/**
|
|
45
|
+
* 清理命令输出,去除输入的命令内容
|
|
46
|
+
*/
|
|
47
|
+
private cleanCommandOutput;
|
|
44
48
|
/**
|
|
45
49
|
* 合并多个 session 的输出
|
|
46
50
|
*/
|
|
47
51
|
private combineSessionOutputs;
|
|
48
52
|
/**
|
|
49
|
-
*
|
|
53
|
+
* 转义正则表达式特殊字符
|
|
50
54
|
*/
|
|
51
|
-
private
|
|
55
|
+
private escapeRegExp;
|
|
52
56
|
/**
|
|
53
57
|
* 检查命令是否完成
|
|
54
58
|
*/
|
|
@@ -58,7 +62,7 @@ export declare class ShellX {
|
|
|
58
62
|
*/
|
|
59
63
|
private hasErrorIndicators;
|
|
60
64
|
/**
|
|
61
|
-
*
|
|
65
|
+
* 判断命令是否完成(基于时间)
|
|
62
66
|
*/
|
|
63
67
|
private isCommandComplete;
|
|
64
68
|
/**
|
|
@@ -167,7 +171,7 @@ export declare class ShellX {
|
|
|
167
171
|
export declare function createShellX(client: WebSocketTaskClient): ShellX;
|
|
168
172
|
/**
|
|
169
173
|
* Create ShellX instance with automatic authentication and shell output monitoring
|
|
170
|
-
*
|
|
174
|
+
* 自动处理ShellX.ai认证和连接,无需外部提供连接地址
|
|
171
175
|
*/
|
|
172
176
|
export declare function createShellXWithShellMonitoring(config?: any): Promise<{
|
|
173
177
|
client: WebSocketTaskClient;
|