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/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.connected = false;
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
- console.log('Initializing WebSocket client...');
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
- (_b = (_a = this.config).onError) === null || _b === void 0 ? void 0 : _b.call(_a, error);
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('Received server message:', message);
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
- (_b = (_a = this.config).onMessage) === null || _b === void 0 ? void 0 : _b.call(_a, message);
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('Server hello, user ID:', message.hello);
248
+ console.log('👋 [ShellX] 服务器问候,用户ID:', message.hello);
110
249
  }
111
250
  if (message.pong !== undefined) {
112
- console.log('Received pong:', message.pong);
251
+ //console.log('🏓 [ShellX] 收到心跳响应:', message.pong);
113
252
  }
114
253
  if (message.error) {
115
- console.error('Server error:', message.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
- responseData = jsonData.action_event;
157
- shouldResolve = true;
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.connected && this.ws) {
326
+ if (this.shellxConnected && this.ws) {
185
327
  try {
186
- console.log('Sending message:', message);
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.sendMessage({ actions: actionSequence }, 'action');
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('Starting ping...');
533
+ console.log('🏓 [ShellX] 开始心跳检测...');
334
534
  this.pingIntervalId = setInterval(() => {
335
- if (this.ws && this.connected) {
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.connected = false;
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.connected;
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;
@@ -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?: Uint8Array;
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
- errorPattern?: string | RegExp;
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 containsPrompt;
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
- * 自动处理认证和连接,无需外部提供WebSocket URL
174
+ * 自动处理ShellX.ai认证和连接,无需外部提供连接地址
171
175
  */
172
176
  export declare function createShellXWithShellMonitoring(config?: any): Promise<{
173
177
  client: WebSocketTaskClient;