shellx-ai 1.0.3 → 1.0.5

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 CHANGED
@@ -1,4 +1,16 @@
1
- import { TaskClientConfig } from './constants';
1
+ export interface TaskClientConfig {
2
+ timeout: number;
3
+ reconnect: boolean;
4
+ reconnectMaxAttempts: number;
5
+ reconnectInterval: number;
6
+ pingInterval: number;
7
+ onOpen: () => void;
8
+ onClose: (event?: CloseEvent) => void;
9
+ onError: (error?: Event) => void;
10
+ onReconnectFailed: () => void;
11
+ onMessage?: (data: any) => void;
12
+ }
13
+ export declare const DEFAULT_CONFIG: TaskClientConfig;
2
14
  import type { WsClient, ActionSequence, ScreenShotOptions, ElementSelector, FindOptions, WaitOptions, UIHierarchy, WAITElement, ScreenShotResponse, ScreenInfoResponse, AppListResponse } from './protocol';
3
15
  export interface TaskResponse<T = any> {
4
16
  taskId?: string;
@@ -11,7 +23,7 @@ export interface TaskResponse<T = any> {
11
23
  export interface PendingTask {
12
24
  resolve: (data: any) => void;
13
25
  reject: (err: Error) => void;
14
- timer: NodeJS.Timeout;
26
+ timer: number;
15
27
  type: string;
16
28
  commandType?: string;
17
29
  }
@@ -41,10 +53,6 @@ export declare class WebSocketTaskClient {
41
53
  * 发送认证消息
42
54
  */
43
55
  private sendAuthenticationMessage;
44
- /**
45
- * 从WebSocket URL中提取认证密钥
46
- */
47
- private extractAuthKeyFromUrl;
48
56
  private handleMessage;
49
57
  private processServerMessage;
50
58
  private handleJsonDataResponse;
@@ -168,5 +176,5 @@ export declare class WebSocketTaskClient {
168
176
  completeTasksByType(taskType: string, result?: any, success?: boolean): number;
169
177
  }
170
178
  export default WebSocketTaskClient;
171
- export { ShellX, createShellX, createShellXWithShellMonitoring } from './shellx';
172
- export type { UIElement, ElementSelector, ActionSequence } from './shellx';
179
+ export type { UIElement, ElementSelector, ActionSequence, WsClient, WsServer, ScreenShotOptions, FindOptions, WaitOptions, UIHierarchy, WAITElement, ScreenShotResponse, ScreenInfoResponse, AppListResponse } from './protocol';
180
+ export { createShellXWithShellMonitoring, ShellX } from './shellx';
package/dist/index.js CHANGED
@@ -42,11 +42,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
42
42
  });
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
- exports.createShellXWithShellMonitoring = exports.createShellX = exports.ShellX = exports.WebSocketTaskClient = void 0;
45
+ exports.ShellX = exports.createShellXWithShellMonitoring = exports.WebSocketTaskClient = exports.DEFAULT_CONFIG = void 0;
46
46
  const uuid_1 = require("uuid");
47
- const constants_1 = require("./constants");
48
47
  const utils_1 = require("./utils");
48
+ // 定义默认配置
49
+ exports.DEFAULT_CONFIG = {
50
+ timeout: 5000,
51
+ reconnect: true,
52
+ reconnectMaxAttempts: 5,
53
+ reconnectInterval: 1000,
54
+ pingInterval: 2000,
55
+ onOpen: () => { },
56
+ onClose: () => { },
57
+ onError: () => { },
58
+ onReconnectFailed: () => { },
59
+ };
49
60
  const cbor_x_1 = require("cbor-x");
61
+ // 安全地获取环境变量,兼容浏览器和Node.js环境
62
+ let authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
50
63
  /**
51
64
  * 获取适合当前环境的WebSocket构造函数
52
65
  */
@@ -84,14 +97,14 @@ class WebSocketTaskClient {
84
97
  this.initializationPromise = null;
85
98
  this.shellx = null; // 关联的 ShellX 实例
86
99
  this.wsUrl = wsUrl;
87
- this.config = Object.assign(Object.assign({}, constants_1.DEFAULT_CONFIG), config);
100
+ this.config = Object.assign(Object.assign({}, exports.DEFAULT_CONFIG), config);
88
101
  this.initializationPromise = this.init();
89
102
  }
90
103
  init() {
91
104
  return __awaiter(this, void 0, void 0, function* () {
92
105
  var _a, _b;
93
106
  try {
94
- console.log('Initializing WebSocket client...');
107
+ console.log('Initializing WebSocket client...' + this.wsUrl);
95
108
  // 获取适合当前环境的WebSocket构造函数
96
109
  const WebSocketConstructor = yield getWebSocket();
97
110
  this.ws = new WebSocketConstructor(this.wsUrl);
@@ -100,7 +113,7 @@ class WebSocketTaskClient {
100
113
  this.ws.binaryType = 'arraybuffer';
101
114
  }
102
115
  this.ws.onopen = () => {
103
- console.log('🔗 [ShellX] WebSocket连接已建立,发送认证消息...');
116
+ console.log('🔗 [ShellX] WebSocket连接已建立,发送认证消息...' + this.wsUrl);
104
117
  this.wsConnected = true;
105
118
  this.reconnectAttempts = 0;
106
119
  // 连接建立后立即发送认证消息
@@ -111,6 +124,7 @@ class WebSocketTaskClient {
111
124
  };
112
125
  this.ws.onclose = (event) => {
113
126
  var _a, _b;
127
+ console.log('❌ [ShellX] WebSocket连接已关闭,正在尝试重新连接...' + event.code + ' ' + this.wsUrl);
114
128
  this.wsConnected = false;
115
129
  this.shellxConnected = false;
116
130
  this.authenticated = false;
@@ -146,20 +160,16 @@ class WebSocketTaskClient {
146
160
  sendAuthenticationMessage() {
147
161
  return __awaiter(this, void 0, void 0, function* () {
148
162
  try {
149
- const authKey = process.env.SHELLX_AUTH_KEY;
163
+ authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
150
164
  if (!authKey) {
151
165
  console.error('❌ [Auth] SHELLX_AUTH_KEY 环境变量未设置');
152
166
  return;
153
167
  }
154
168
  console.log('🔑 [Auth] 发送认证消息...');
155
169
  // 从URL中提取认证密钥(如果URL包含认证信息)
156
- const urlAuthKey = this.extractAuthKeyFromUrl();
157
- const finalAuthKey = urlAuthKey || authKey;
158
- // 创建认证数据(这里使用简单的字符串,实际可能需要更复杂的格式)
159
- const authData = new TextEncoder().encode(finalAuthKey);
160
170
  // 发送认证消息
161
- const authMessage = { authenticate: authData };
162
- console.log('📤 [Auth] 发送认证消息:', { authenticate: finalAuthKey });
171
+ const authMessage = { authenticate: authKey };
172
+ console.log('📤 [Auth] 发送认证消息:', { authenticate: authKey });
163
173
  if (this.ws && this.wsConnected) {
164
174
  this.ws.send((0, cbor_x_1.encode)(authMessage));
165
175
  }
@@ -172,21 +182,6 @@ class WebSocketTaskClient {
172
182
  }
173
183
  });
174
184
  }
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
- }
189
- }
190
185
  handleMessage(event) {
191
186
  let serverMessage;
192
187
  try {
@@ -377,7 +372,18 @@ class WebSocketTaskClient {
377
372
  */
378
373
  screenShot(options) {
379
374
  return __awaiter(this, void 0, void 0, function* () {
380
- return this.sendMessage({ screenShot: options || { format: 'png' } }, 'screenShot');
375
+ return this.sendMessage({ screenShot: options || {
376
+ "format": "png",
377
+ "quality": 30,
378
+ "scale": 1.0,
379
+ "region": {
380
+ "left": 0,
381
+ "top": 0,
382
+ "width": 1080,
383
+ "height": 2340
384
+ }
385
+ }
386
+ }, 'screenShot');
381
387
  });
382
388
  }
383
389
  /**
@@ -484,7 +490,9 @@ class WebSocketTaskClient {
484
490
  }
485
491
  if (taskType) {
486
492
  const timer = setTimeout(() => {
487
- this.pendingTasks.delete(taskId);
493
+ if (taskId != null) {
494
+ this.pendingTasks.delete(taskId);
495
+ }
488
496
  console.log(`⏰ [ShellX] 任务超时: ${taskId}, 类型: ${taskType}`);
489
497
  }, this.config.timeout);
490
498
  this.pendingTasks.set(taskId, {
@@ -501,10 +509,12 @@ class WebSocketTaskClient {
501
509
  this.ws.send((0, cbor_x_1.encode)(message));
502
510
  const promise = new Promise((promiseResolve, promiseReject) => {
503
511
  if (taskType) {
504
- const task = this.pendingTasks.get(taskId);
505
- if (task) {
506
- task.resolve = promiseResolve;
507
- task.reject = promiseReject;
512
+ if (taskId != null) {
513
+ const task = this.pendingTasks.get(taskId);
514
+ if (task) {
515
+ task.resolve = promiseResolve;
516
+ task.reject = promiseReject;
517
+ }
508
518
  }
509
519
  }
510
520
  else {
@@ -546,6 +556,8 @@ class WebSocketTaskClient {
546
556
  reconnect() {
547
557
  return __awaiter(this, void 0, void 0, function* () {
548
558
  var _a, _b;
559
+ // TODO: 本地的需要重连
560
+ console.log(`🔄 [ShellX] 重连中... (${this.reconnectAttempts}/${this.config.reconnectMaxAttempts})` + this.wsUrl);
549
561
  if (this.reconnectAttempts >= this.config.reconnectMaxAttempts) {
550
562
  (_b = (_a = this.config).onReconnectFailed) === null || _b === void 0 ? void 0 : _b.call(_a);
551
563
  return;
@@ -675,9 +687,7 @@ class WebSocketTaskClient {
675
687
  }
676
688
  exports.WebSocketTaskClient = WebSocketTaskClient;
677
689
  exports.default = WebSocketTaskClient;
678
- // Export ShellX automation utilities
690
+ // PromptFlow 模块已移至 examples/ 目录作为示例实现
679
691
  var shellx_1 = require("./shellx");
680
- Object.defineProperty(exports, "ShellX", { enumerable: true, get: function () { return shellx_1.ShellX; } });
681
- Object.defineProperty(exports, "createShellX", { enumerable: true, get: function () { return shellx_1.createShellX; } });
682
692
  Object.defineProperty(exports, "createShellXWithShellMonitoring", { enumerable: true, get: function () { return shellx_1.createShellXWithShellMonitoring; } });
683
- // PromptFlow 模块已移至 examples/ 目录作为示例实现
693
+ Object.defineProperty(exports, "ShellX", { enumerable: true, get: function () { return shellx_1.ShellX; } });
@@ -14,6 +14,7 @@ export type UIElement = {
14
14
  resourceId: string;
15
15
  text: string;
16
16
  describe: string;
17
+ hintText: string;
17
18
  bounds: UIBounds;
18
19
  visible: boolean;
19
20
  clickable: boolean;
@@ -59,6 +60,7 @@ export type ScreenInfoResponse = {
59
60
  timestamp: bigint;
60
61
  success?: boolean;
61
62
  errorMessage?: string;
63
+ bashStatus?: number;
62
64
  };
63
65
  export type App = {
64
66
  packageName: string;
@@ -163,7 +165,7 @@ export type WsClient = {
163
165
  setName?: string;
164
166
  setCursor?: [number, number] | null;
165
167
  setFocus?: number | null;
166
- create?: [number, number];
168
+ create?: [number, number, number, number];
167
169
  close?: Sid;
168
170
  move?: [Sid, WsWinsize | null];
169
171
  data?: [Sid, Uint8Array, bigint];
@@ -197,19 +199,22 @@ export type ShellXActions = {
197
199
  thought?: string;
198
200
  action: ShellCommandAction | ClickAction | InputAction | SwipeAction | KeyAction | WaitAction | findAction | getAppInfoAction | completeAction;
199
201
  };
200
- interface ShellCommandAction {
202
+ export interface ShellCommandAction {
201
203
  title?: string;
202
204
  type: "command";
205
+ activity?: string;
203
206
  command: string;
204
207
  }
205
- interface getAppInfoAction {
208
+ export interface getAppInfoAction {
206
209
  title?: string;
207
210
  type: "get_app_info";
211
+ activity?: string;
208
212
  packageName: string;
209
213
  }
210
- interface ClickAction {
214
+ export interface ClickAction {
211
215
  title?: string;
212
216
  type: 'click';
217
+ activity?: string;
213
218
  target: {
214
219
  type: "elementId";
215
220
  value: string;
@@ -223,15 +228,16 @@ interface ClickAction {
223
228
  "y": number;
224
229
  };
225
230
  };
226
- options?: {
231
+ options: {
227
232
  timeoutMs?: number;
228
233
  durationMs?: number;
229
234
  waitAfterMs?: number;
230
- clickType?: 'normal' | 'long' | 'double';
235
+ clickType?: 'single' | 'normal' | 'long' | 'double';
231
236
  };
232
237
  }
233
- interface InputAction {
238
+ export interface InputAction {
234
239
  title?: string;
240
+ activity?: string;
235
241
  type: "input";
236
242
  text: string;
237
243
  target: {
@@ -247,8 +253,9 @@ interface InputAction {
247
253
  hideKeyboardAfter?: boolean;
248
254
  };
249
255
  }
250
- interface SwipeAction {
256
+ export interface SwipeAction {
251
257
  title?: string;
258
+ activity?: string;
252
259
  type: 'swipe';
253
260
  "from": {
254
261
  "x": number;
@@ -267,8 +274,9 @@ interface SwipeAction {
267
274
  hideKeyboardAfter?: boolean;
268
275
  };
269
276
  }
270
- interface KeyAction {
277
+ export interface KeyAction {
271
278
  title?: string;
279
+ activity?: string;
272
280
  type: "key";
273
281
  keyCode?: string;
274
282
  options?: {
@@ -277,6 +285,7 @@ interface KeyAction {
277
285
  }
278
286
  export interface WaitAction {
279
287
  title?: string;
288
+ activity?: string;
280
289
  type?: "wait";
281
290
  selector: ElementSelector;
282
291
  options?: WaitOptions;
@@ -284,14 +293,17 @@ export interface WaitAction {
284
293
  }
285
294
  export interface findAction {
286
295
  title?: string;
296
+ activity?: string;
287
297
  type?: "find";
288
298
  selector: ElementSelector;
289
299
  options?: FindOptions;
290
300
  }
291
301
  export type completeAction = {
292
302
  title?: string;
303
+ activity?: string;
293
304
  type: "complete";
294
305
  result?: string;
306
+ script?: string;
295
307
  };
296
308
  export type ElementSelector = {
297
309
  elementId?: string;
@@ -309,6 +321,8 @@ export type WaitOptions = {
309
321
  failOnTimeout?: boolean;
310
322
  };
311
323
  export type FindOptions = {
324
+ pressClick?: boolean;
325
+ waitAfterMs?: number;
312
326
  timeout?: number;
313
327
  visibleOnly?: boolean;
314
328
  clickableOnly?: boolean;
package/dist/shellx.d.ts CHANGED
@@ -29,6 +29,10 @@ export declare class ShellX {
29
29
  private shellOutputBuffers;
30
30
  private shellCommandPromises;
31
31
  constructor(client: WebSocketTaskClient);
32
+ /**
33
+ * Get the WebSocket client instance
34
+ */
35
+ getClient(): WebSocketTaskClient;
32
36
  /**
33
37
  * 设置 shell 输出监听器(需要在创建 WebSocketTaskClient 时调用)
34
38
  */
@@ -121,10 +125,6 @@ export declare class ShellX {
121
125
  element: UIElement;
122
126
  selectorIndex: number;
123
127
  } | null>;
124
- /**
125
- * Login helper for common login flows
126
- */
127
- performLogin(usernameSelector: ElementSelector, passwordSelector: ElementSelector, loginButtonSelector: ElementSelector, username: string, password: string): Promise<boolean>;
128
128
  /**
129
129
  * Navigate through app using a series of clicks
130
130
  */
@@ -164,6 +164,13 @@ export declare class ShellX {
164
164
  * Device info commands
165
165
  */
166
166
  getDeviceInfo(): Promise<ShellCommandResult[]>;
167
+ /**
168
+ * Execute key action (press a key)
169
+ */
170
+ executeKeyAction(keyCode: string, options?: {
171
+ longPress?: boolean;
172
+ waitAfterMs?: number;
173
+ }): Promise<boolean>;
167
174
  }
168
175
  /**
169
176
  * Create ShellX instance
package/dist/shellx.js CHANGED
@@ -50,8 +50,12 @@ exports.createShellX = createShellX;
50
50
  exports.createHelpers = createShellX;
51
51
  exports.createShellXWithShellMonitoring = createShellXWithShellMonitoring;
52
52
  exports.createHelpersWithShellMonitoring = createShellXWithShellMonitoring;
53
- const index_1 = __importDefault(require("./index"));
54
53
  const uuid_1 = require("uuid");
54
+ const utils_1 = require("./utils");
55
+ // 导入 WebSocketTaskClient 类
56
+ const index_1 = __importDefault(require("./index"));
57
+ // 安全地获取环境变量,兼容浏览器和Node.js环境
58
+ let authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
55
59
  /**
56
60
  * ShellX automation utilities for common patterns
57
61
  */
@@ -64,6 +68,12 @@ class ShellX {
64
68
  // 监听 shell 输出需要在创建 WebSocketTaskClient 时设置 onMessage 回调
65
69
  console.log('⚠️ [Shell] 注意:要监听 shell 输出,请在创建 WebSocketTaskClient 时设置 onMessage 回调');
66
70
  }
71
+ /**
72
+ * Get the WebSocket client instance
73
+ */
74
+ getClient() {
75
+ return this.client;
76
+ }
67
77
  /**
68
78
  * 设置 shell 输出监听器(需要在创建 WebSocketTaskClient 时调用)
69
79
  */
@@ -483,42 +493,6 @@ class ShellX {
483
493
  return null;
484
494
  });
485
495
  }
486
- /**
487
- * Login helper for common login flows
488
- */
489
- performLogin(usernameSelector, passwordSelector, loginButtonSelector, username, password) {
490
- return __awaiter(this, void 0, void 0, function* () {
491
- try {
492
- console.log('开始登录流程...');
493
- // Input username
494
- yield this.inputText(usernameSelector, username, { clear: true });
495
- console.log('✅ 用户名已输入');
496
- // Input password
497
- yield this.inputText(passwordSelector, password, { clear: true });
498
- console.log('✅ 密码已输入');
499
- // Click login button
500
- const loginElement = yield this.findElementWithRetry(loginButtonSelector);
501
- if (!loginElement) {
502
- throw new Error('未找到登录按钮');
503
- }
504
- const loginAction = {
505
- title: "执行登录",
506
- actions: [{
507
- type: "click",
508
- target: { type: "elementId", value: loginElement.elementId },
509
- options: { waitAfterMs: 2000 }
510
- }]
511
- };
512
- yield this.client.executeAction(loginAction);
513
- console.log('✅ 登录按钮已点击');
514
- return true;
515
- }
516
- catch (error) {
517
- console.error('❌ 登录失败:', error);
518
- return false;
519
- }
520
- });
521
- }
522
496
  /**
523
497
  * Navigate through app using a series of clicks
524
498
  */
@@ -746,6 +720,37 @@ class ShellX {
746
720
  }
747
721
  });
748
722
  }
723
+ /**
724
+ * Execute key action (press a key)
725
+ */
726
+ executeKeyAction(keyCode_1) {
727
+ return __awaiter(this, arguments, void 0, function* (keyCode, options = {}) {
728
+ try {
729
+ console.log(`🔑 [Key] 执行按键操作: ${keyCode}${options.longPress ? ' (长按)' : ''}`);
730
+ const keyAction = {
731
+ title: `按键: ${keyCode}${options.longPress ? ' (长按)' : ''}`,
732
+ actions: [{
733
+ type: "key",
734
+ keyCode,
735
+ options: {
736
+ longPress: options.longPress
737
+ }
738
+ }]
739
+ };
740
+ yield this.client.executeAction(keyAction);
741
+ // 如果设置了等待时间,则等待
742
+ if (options.waitAfterMs) {
743
+ yield new Promise(resolve => setTimeout(resolve, options.waitAfterMs));
744
+ }
745
+ console.log(`✅ [Key] 按键操作完成: ${keyCode}`);
746
+ return true;
747
+ }
748
+ catch (error) {
749
+ console.error(`❌ [Key] 按键操作失败: ${keyCode}`, error);
750
+ return false;
751
+ }
752
+ });
753
+ }
749
754
  }
750
755
  exports.ShellX = ShellX;
751
756
  exports.AutomationHelpers = ShellX;
@@ -826,9 +831,30 @@ function createFallbackFetch() {
826
831
  /**
827
832
  * 从ShellX.ai服务认证并获取WebSocket连接信息
828
833
  */
829
- function authenticateDevice() {
834
+ function authenticateDevice(deviceId) {
830
835
  return __awaiter(this, void 0, void 0, function* () {
831
- const authKey = process.env.SHELLX_AUTH_KEY;
836
+ const fallbackUrl = `ws://127.0.0.1:9091/api/s/${deviceId}`;
837
+ // 1. 优先检测本地服务
838
+ try {
839
+ // fetch超时实现
840
+ const fetchWithTimeout = (url, options, timeout = 1000) => Promise.race([
841
+ fetch(url, options),
842
+ new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
843
+ ]);
844
+ const localResp = yield fetchWithTimeout('http://127.0.0.1:9091/info', { method: 'GET', headers: { 'Accept': 'application/json' } }, 1000);
845
+ if (localResp.ok) {
846
+ const info = yield localResp.json();
847
+ if (info && (info.status === 'ok' || info.status === 1)) {
848
+ console.log('✅ [Auth] 本地ShellX服务可用,直接使用本地服务:', fallbackUrl);
849
+ return fallbackUrl;
850
+ }
851
+ }
852
+ }
853
+ catch (e) {
854
+ // 本地不可用,继续走远程
855
+ }
856
+ // 2. 远程认证逻辑
857
+ authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
832
858
  if (!authKey) {
833
859
  throw new Error('SHELLX_AUTH_KEY environment variable is required');
834
860
  }
@@ -837,7 +863,7 @@ function authenticateDevice() {
837
863
  // 获取ofetch实例
838
864
  const fetchFn = yield getfetch();
839
865
  // ofetch自动处理JSON解析和错误处理
840
- const data = yield fetchFn(`https://shellx.ai/api/device/${authKey}`, {
866
+ const data = yield fetchFn(`https://shellx.ai/api/device/${deviceId}`, {
841
867
  method: 'GET',
842
868
  headers: {
843
869
  'Content-Type': 'application/json',
@@ -849,13 +875,11 @@ function authenticateDevice() {
849
875
  console.log(`📡 [Auth] ShellX.ai服务地址: ${jsonData.machine}`);
850
876
  console.log(`📡 [Auth] 注册时间: ${jsonData.registered_at}`);
851
877
  console.log(`📡 [Auth] 最后更新: ${jsonData.last_updated}`);
852
- return jsonData.machine;
878
+ return jsonData.machine + '/api/s/' + jsonData.authenticate;
853
879
  }
854
880
  catch (error) {
855
881
  const errorMessage = error instanceof Error ? error.message : String(error);
856
882
  console.warn('⚠️ [Auth] ShellX.ai在线认证失败,使用本地服务:', errorMessage);
857
- // 认证失败时使用默认的本地ShellX服务
858
- const fallbackUrl = `ws://127.0.0.1:9091/api/s/${authKey}`;
859
883
  console.log(`🔄 [Auth] 使用本地ShellX服务: ${fallbackUrl}`);
860
884
  return fallbackUrl;
861
885
  }
@@ -868,17 +892,12 @@ function authenticateDevice() {
868
892
  function createShellXWithShellMonitoring() {
869
893
  return __awaiter(this, arguments, void 0, function* (config = {}) {
870
894
  try {
871
- // 认证并获取ShellX.ai服务地址
872
- const wsUrl = yield authenticateDevice();
873
- // 先创建 shellx 实例,但不绑定客户端
895
+ const wsUrl = yield authenticateDevice(config.deviceId);
874
896
  const shellx = new ShellX(null);
875
- // 创建ShellX客户端并设置消息监听器
876
897
  const client = new index_1.default(wsUrl, Object.assign(Object.assign({}, config), { onMessage: (message) => {
877
- // 处理 chunks 数据(pty 终端输出)
878
898
  if (message.chunks) {
879
899
  shellx.handleShellOutput(message.chunks);
880
900
  }
881
- // 调用原始的消息处理器
882
901
  if (config.onMessage) {
883
902
  config.onMessage(message);
884
903
  }
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,18 @@
1
1
  export declare const wait: (ms: number) => Promise<void>;
2
+ /**
3
+ * 安全地获取环境变量,兼容浏览器和Node.js环境
4
+ */
5
+ export declare function getEnvVar(key: string): string | undefined;
6
+ /**
7
+ * 安全地设置环境变量,兼容浏览器和Node.js环境
8
+ */
9
+ export declare function setEnvVar(key: string, value: string): void;
10
+ /**
11
+ * 安全地删除环境变量,兼容浏览器和Node.js环境
12
+ */
13
+ export declare function deleteEnvVar(key: string): void;
2
14
  /**
3
15
  * 检查并获取 WebSocket URL 环境变量
4
- * 如果未设置则显示错误并退出程序
16
+ * 如果未设置则显示错误并抛出异常
5
17
  */
6
18
  export declare function getWebSocketUrl(): string;
package/dist/utils.js CHANGED
@@ -1,37 +1,81 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.wait = void 0;
4
+ exports.getEnvVar = getEnvVar;
5
+ exports.setEnvVar = setEnvVar;
6
+ exports.deleteEnvVar = deleteEnvVar;
4
7
  exports.getWebSocketUrl = getWebSocketUrl;
5
8
  const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
6
9
  exports.wait = wait;
10
+ /**
11
+ * 安全地获取环境变量,兼容浏览器和Node.js环境
12
+ */
13
+ function getEnvVar(key) {
14
+ if (typeof process !== 'undefined' && process.env) {
15
+ return process.env[key];
16
+ }
17
+ else if (typeof window !== 'undefined' && window.localStorage) {
18
+ const item = window.localStorage.getItem(key);
19
+ if (item) {
20
+ console.log("已从 localStorage 中获取环境变量:", key);
21
+ return item;
22
+ }
23
+ }
24
+ return undefined;
25
+ }
26
+ /**
27
+ * 安全地设置环境变量,兼容浏览器和Node.js环境
28
+ */
29
+ function setEnvVar(key, value) {
30
+ if (typeof process !== 'undefined' && process.env) {
31
+ process.env[key] = value;
32
+ }
33
+ else if (typeof window !== 'undefined' && window.localStorage) {
34
+ window.localStorage.setItem(key, value);
35
+ console.log("已保存环境变量:", key, value);
36
+ }
37
+ }
38
+ /**
39
+ * 安全地删除环境变量,兼容浏览器和Node.js环境
40
+ */
41
+ function deleteEnvVar(key) {
42
+ if (typeof process !== 'undefined' && process.env) {
43
+ delete process.env[key];
44
+ }
45
+ else if (typeof window !== 'undefined' && window.localStorage) {
46
+ window.localStorage.removeItem(key);
47
+ console.log("已删除环境变量:", key);
48
+ }
49
+ }
7
50
  /**
8
51
  * 检查并获取 WebSocket URL 环境变量
9
- * 如果未设置则显示错误并退出程序
52
+ * 如果未设置则显示错误并抛出异常
10
53
  */
11
54
  function getWebSocketUrl() {
12
- const wsUrl = process.env.WEBSOCKET_URL;
55
+ // 安全地获取环境变量,兼容浏览器和Node.js环境
56
+ const wsUrl = getEnvVar('WEBSOCKET_URL');
13
57
  if (!wsUrl || wsUrl.trim() === '') {
14
- console.error('❌ 错误: 未设置 WEBSOCKET_URL 环境变量!');
15
- console.error('');
16
- console.error('📋 解决方案:');
17
- console.error('1. 在项目根目录创建 .env 文件');
18
- console.error('2. 在 .env 文件中添加以下内容:');
19
- console.error(' WEBSOCKET_URL=ws://127.0.0.1:9091/api/s/your-session-id');
20
- console.error('');
21
- console.error('💡 提示:');
22
- console.error('- 请将 your-session-id 替换为实际的会话ID');
23
- console.error('- 确保 ShellX 服务器正在运行');
24
- console.error('- 检查端口号是否正确(默认9091)');
25
- console.error('');
26
- process.exit(1);
58
+ const errorMessage = '❌ 错误: 未设置 WEBSOCKET_URL 环境变量!\n\n📋 解决方案:\n1. 在项目根目录创建 .env 文件\n2. 在 .env 文件中添加以下内容:\n WEBSOCKET_URL=ws://127.0.0.1:9091/api/s/your-session-id\n\n💡 提示:\n- 请将 your-session-id 替换为实际的会话ID\n- 确保 ShellX 服务器正在运行\n- 检查端口号是否正确(默认9091)';
59
+ console.error(errorMessage);
60
+ // 在Node.js环境中退出,在浏览器环境中抛出异常
61
+ if (typeof process !== 'undefined' && process.exit) {
62
+ process.exit(1);
63
+ }
64
+ else {
65
+ throw new Error(errorMessage);
66
+ }
27
67
  }
28
68
  // 简单的 URL 格式验证
29
69
  if (!wsUrl.startsWith('ws://') && !wsUrl.startsWith('wss://')) {
30
- console.error('❌ 错误: WEBSOCKET_URL 格式不正确!');
31
- console.error(`当前值: ${wsUrl}`);
32
- console.error('应该以 ws:// 或 wss:// 开头');
33
- console.error('例如: ws://127.0.0.1:9091/api/s/your-session-id');
34
- process.exit(1);
70
+ const errorMessage = `❌ 错误: WEBSOCKET_URL 格式不正确!\n当前值: ${wsUrl}\n应该以 ws:// 或 wss:// 开头\n例如: ws://127.0.0.1:9091/api/s/your-session-id`;
71
+ console.error(errorMessage);
72
+ // 在Node.js环境中退出,在浏览器环境中抛出异常
73
+ if (typeof process !== 'undefined' && process.exit) {
74
+ process.exit(1);
75
+ }
76
+ else {
77
+ throw new Error(errorMessage);
78
+ }
35
79
  }
36
80
  return wsUrl;
37
81
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shellx-ai",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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",
@@ -32,6 +32,7 @@
32
32
  "dependencies": {
33
33
  "cbor-x": "^1.6.0",
34
34
  "dotenv": "^16.4.5",
35
+ "ofetch": "^1.4.1",
35
36
  "uuid": "^11.1.0"
36
37
  },
37
38
  "optionalDependencies": {
@@ -41,14 +42,15 @@
41
42
  "devDependencies": {
42
43
  "@types/jest": "^30.0.0",
43
44
  "@types/node": "^24.0.13",
45
+ "@types/ws": "^8.18.1",
44
46
  "jest": "^30.0.4",
45
- "ts-jest": "^29.4.0",
46
- "ts-node": "^10.9.2",
47
- "typescript": "^5.8.3",
48
47
  "langchain": "^0.1.34",
49
48
  "promptflow-eval": "1.0.0",
50
49
  "promptflow-template": "1.0.0",
51
- "promptflowx": "^0.1.7"
50
+ "promptflowx": "^0.1.9",
51
+ "ts-jest": "^29.4.0",
52
+ "ts-node": "^10.9.2",
53
+ "typescript": "^5.8.3"
52
54
  },
53
55
  "bugs": {
54
56
  "url": "https://github.com/10cl/shellx/issues"