shellx-ai 1.0.8 → 1.0.9

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/shellx.js CHANGED
@@ -1,37 +1,4 @@
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
- })();
35
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -45,11 +12,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
45
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
46
13
  };
47
14
  Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.ShellX = exports.AutomationHelpers = void 0;
15
+ exports.ShellX = void 0;
49
16
  exports.createShellX = createShellX;
50
- exports.createHelpers = createShellX;
51
17
  exports.createShellXWithShellMonitoring = createShellXWithShellMonitoring;
52
- exports.createHelpersWithShellMonitoring = createShellXWithShellMonitoring;
53
18
  const uuid_1 = require("uuid");
54
19
  const utils_1 = require("./utils");
55
20
  // 导入 WebSocketTaskClient 类
@@ -258,6 +223,490 @@ class ShellX {
258
223
  generateCommandKey(command) {
259
224
  return `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
260
225
  }
226
+ /**
227
+ * 通用重试机制
228
+ */
229
+ withRetry(operation_1) {
230
+ return __awaiter(this, arguments, void 0, function* (operation, options = {}) {
231
+ const { retry = 3, delay = 1000, onRetry } = options;
232
+ for (let attempt = 1; attempt <= retry; attempt++) {
233
+ try {
234
+ return yield operation();
235
+ }
236
+ catch (error) {
237
+ if (attempt === retry) {
238
+ throw error;
239
+ }
240
+ if (onRetry) {
241
+ onRetry(attempt, error);
242
+ }
243
+ console.log(`🔄 [Retry] 第 ${attempt} 次尝试失败,${delay}ms 后重试...`);
244
+ yield new Promise(resolve => setTimeout(resolve, delay));
245
+ }
246
+ }
247
+ throw new Error('重试次数已用完');
248
+ });
249
+ }
250
+ /**
251
+ * 转换精简选择器为原始选择器
252
+ */
253
+ convertSelector(selector) {
254
+ return {
255
+ elementId: selector.targetElementId,
256
+ resourceId: selector.targetResourceId,
257
+ className: selector.targetClass,
258
+ text: selector.targetText,
259
+ textContains: undefined,
260
+ visible: selector.visible,
261
+ clickable: selector.clickable
262
+ };
263
+ }
264
+ /**
265
+ * 转换精简元素为原始元素
266
+ */
267
+ convertElement(uiElement) {
268
+ return {
269
+ id: uiElement.elementId,
270
+ text: uiElement.text,
271
+ class: uiElement.className,
272
+ left: uiElement.bounds.left,
273
+ top: uiElement.bounds.top,
274
+ right: uiElement.bounds.right,
275
+ bottom: uiElement.bounds.bottom,
276
+ visible: uiElement.visible,
277
+ clickable: uiElement.clickable
278
+ };
279
+ }
280
+ // ==================== 精简 Action 封装函数 ====================
281
+ /**
282
+ * 点击操作 - 支持元素ID、坐标或选择器
283
+ */
284
+ click(clickData) {
285
+ return __awaiter(this, void 0, void 0, function* () {
286
+ const startTime = Date.now();
287
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
288
+ try {
289
+ let target;
290
+ if (clickData.targetElementId) {
291
+ // 元素ID
292
+ target = { type: "elementId", value: clickData.targetElementId };
293
+ }
294
+ else if (clickData.targetResourceId) {
295
+ // 精简元素
296
+ target = { type: "resourceId", value: clickData.targetResourceId };
297
+ }
298
+ else if (clickData.targetX !== undefined && clickData.targetY !== undefined) {
299
+ // 坐标
300
+ target = { type: "coordinate", value: { x: clickData.targetX, y: clickData.targetY } };
301
+ }
302
+ else if (clickData.targetText || clickData.targetClass) {
303
+ // 选择器
304
+ const element = yield this.findElementWithRetry(this.convertSelector(clickData), clickData.retry || 3);
305
+ if (!element) {
306
+ throw new Error('未找到目标元素');
307
+ }
308
+ target = { type: "elementId", value: element.elementId };
309
+ }
310
+ else {
311
+ throw new Error('必须指定目标:targetElementId、targetResourceId、坐标(targetX/targetY)或选择器(targetText/targetClass)');
312
+ }
313
+ const action = {
314
+ title: `点击操作: ${clickData.clickType || 'single'}`,
315
+ actions: [{
316
+ type: "click",
317
+ target,
318
+ options: {
319
+ clickType: clickData.clickType || 'single',
320
+ waitAfterMs: clickData.wait || 1000
321
+ }
322
+ }]
323
+ };
324
+ yield this.client.executeAction(action);
325
+ return {
326
+ success: true,
327
+ data: {
328
+ targetElementId: clickData.targetElementId,
329
+ targetResourceId: clickData.targetResourceId,
330
+ targetText: clickData.targetText,
331
+ targetClass: clickData.targetClass,
332
+ targetX: clickData.targetX,
333
+ targetY: clickData.targetY,
334
+ clickType: clickData.clickType
335
+ },
336
+ duration: Date.now() - startTime
337
+ };
338
+ }
339
+ catch (error) {
340
+ throw new Error(`点击操作失败: ${error instanceof Error ? error.message : String(error)}`);
341
+ }
342
+ }), { retry: clickData.retry, delay: 500 });
343
+ });
344
+ }
345
+ /**
346
+ * 输入操作 - 支持元素ID、资源ID或选择器
347
+ */
348
+ input(inputData) {
349
+ return __awaiter(this, void 0, void 0, function* () {
350
+ const startTime = Date.now();
351
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
352
+ var _a, _b;
353
+ try {
354
+ let target;
355
+ if (inputData.targetElementId) {
356
+ target = { type: "elementId", value: inputData.targetElementId };
357
+ }
358
+ else if (inputData.targetResourceId) {
359
+ target = { type: "resourceId", value: inputData.targetResourceId };
360
+ }
361
+ else if (inputData.targetText || inputData.targetClass) {
362
+ const element = yield this.findElementWithRetry(this.convertSelector(inputData), inputData.retry || 3);
363
+ if (!element) {
364
+ throw new Error('未找到目标元素');
365
+ }
366
+ target = { type: "elementId", value: element.elementId };
367
+ }
368
+ else {
369
+ throw new Error('必须指定目标:targetElementId、targetResourceId、targetText或targetClass');
370
+ }
371
+ const action = {
372
+ title: `输入文本: ${inputData.text}`,
373
+ actions: [{
374
+ type: "input",
375
+ text: inputData.text,
376
+ target,
377
+ options: {
378
+ replaceExisting: (_a = inputData.clear) !== null && _a !== void 0 ? _a : true,
379
+ hideKeyboardAfter: (_b = inputData.hideKeyboard) !== null && _b !== void 0 ? _b : false,
380
+ waitAfterMs: inputData.wait || 500
381
+ }
382
+ }]
383
+ };
384
+ yield this.client.executeAction(action);
385
+ return {
386
+ success: true,
387
+ data: {
388
+ text: inputData.text,
389
+ targetElementId: inputData.targetElementId,
390
+ targetResourceId: inputData.targetResourceId,
391
+ targetText: inputData.targetText,
392
+ targetClass: inputData.targetClass
393
+ },
394
+ duration: Date.now() - startTime
395
+ };
396
+ }
397
+ catch (error) {
398
+ throw new Error(`输入操作失败: ${error instanceof Error ? error.message : String(error)}`);
399
+ }
400
+ }), { retry: inputData.retry, delay: 500 });
401
+ });
402
+ }
403
+ /**
404
+ * 滑动操作 - 支持坐标点
405
+ */
406
+ swipe(swipeData) {
407
+ return __awaiter(this, void 0, void 0, function* () {
408
+ const startTime = Date.now();
409
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
410
+ try {
411
+ const from = { x: swipeData.fromX, y: swipeData.fromY };
412
+ const to = { x: swipeData.toX, y: swipeData.toY };
413
+ const action = {
414
+ title: `滑动操作: ${from.x},${from.y} → ${to.x},${to.y}`,
415
+ actions: [{
416
+ type: "swipe",
417
+ from,
418
+ to,
419
+ options: {
420
+ durationMs: swipeData.duration || 800,
421
+ waitAfterMs: swipeData.wait || 500
422
+ }
423
+ }]
424
+ };
425
+ yield this.client.executeAction(action);
426
+ return {
427
+ success: true,
428
+ data: { from, to, duration: swipeData.duration },
429
+ duration: Date.now() - startTime
430
+ };
431
+ }
432
+ catch (error) {
433
+ throw new Error(`滑动操作失败: ${error instanceof Error ? error.message : String(error)}`);
434
+ }
435
+ }), { retry: swipeData.retry, delay: 500 });
436
+ });
437
+ }
438
+ /**
439
+ * 按键操作
440
+ */
441
+ pressKey(keyData) {
442
+ return __awaiter(this, void 0, void 0, function* () {
443
+ const startTime = Date.now();
444
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
445
+ try {
446
+ const action = {
447
+ title: `按键操作: ${keyData.key}${keyData.longPress ? ' (长按)' : ''}`,
448
+ actions: [{
449
+ type: "key",
450
+ keyCode: keyData.key,
451
+ options: {
452
+ longPress: keyData.longPress || false
453
+ }
454
+ }]
455
+ };
456
+ yield this.client.executeAction(action);
457
+ if (keyData.wait) {
458
+ yield new Promise(resolve => setTimeout(resolve, keyData.wait));
459
+ }
460
+ return {
461
+ success: true,
462
+ data: { key: keyData.key, longPress: keyData.longPress },
463
+ duration: Date.now() - startTime
464
+ };
465
+ }
466
+ catch (error) {
467
+ throw new Error(`按键操作失败: ${error instanceof Error ? error.message : String(error)}`);
468
+ }
469
+ }), { retry: keyData.retry, delay: 500 });
470
+ });
471
+ }
472
+ /**
473
+ * 等待操作 - 等待元素出现或消失
474
+ */
475
+ wait(waitData) {
476
+ return __awaiter(this, void 0, void 0, function* () {
477
+ const startTime = Date.now();
478
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
479
+ try {
480
+ const selector = this.convertSelector(waitData);
481
+ const timeout = waitData.timeout || 10000;
482
+ const interval = 500;
483
+ const maxAttempts = Math.floor(timeout / interval);
484
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
485
+ try {
486
+ const result = yield this.client.findElement(selector, {
487
+ timeout: interval,
488
+ maxResults: 1,
489
+ visibleOnly: waitData.condition === 'visible',
490
+ clickableOnly: waitData.condition === 'clickable'
491
+ });
492
+ if (result.elements.length > 0) {
493
+ if (waitData.condition === 'gone') {
494
+ // 等待元素消失,继续等待
495
+ yield new Promise(resolve => setTimeout(resolve, interval));
496
+ continue;
497
+ }
498
+ else {
499
+ // 等待元素出现,已找到
500
+ return {
501
+ success: true,
502
+ data: { element: this.convertElement(result.elements[0]) },
503
+ duration: Date.now() - startTime
504
+ };
505
+ }
506
+ }
507
+ else if (waitData.condition === 'gone') {
508
+ // 等待元素消失,已消失
509
+ return {
510
+ success: true,
511
+ data: { element: null },
512
+ duration: Date.now() - startTime
513
+ };
514
+ }
515
+ }
516
+ catch (error) {
517
+ // 查找失败,继续等待
518
+ }
519
+ yield new Promise(resolve => setTimeout(resolve, interval));
520
+ }
521
+ throw new Error(`等待超时: ${waitData.condition || 'visible'}`);
522
+ }
523
+ catch (error) {
524
+ throw new Error(`等待操作失败: ${error instanceof Error ? error.message : String(error)}`);
525
+ }
526
+ }), { retry: waitData.retry, delay: 500 });
527
+ });
528
+ }
529
+ /**
530
+ * 查找操作 - 查找元素
531
+ */
532
+ find(findData) {
533
+ return __awaiter(this, void 0, void 0, function* () {
534
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
535
+ try {
536
+ const selector = this.convertSelector(findData);
537
+ const result = yield this.client.findElement(selector, {
538
+ pressClick: findData.pressClick,
539
+ waitAfterMs: findData.waitAfterMs || 5000,
540
+ maxResults: findData.maxResults || 1000,
541
+ visibleOnly: true,
542
+ clickableOnly: false,
543
+ multiple: findData.multiple || false
544
+ });
545
+ const elements = result.elements.map(element => this.convertElement(element));
546
+ return {
547
+ elements,
548
+ count: elements.length,
549
+ success: true,
550
+ found: result.found,
551
+ };
552
+ }
553
+ catch (error) {
554
+ throw new Error(`查找操作失败: ${error instanceof Error ? error.message : String(error)}`);
555
+ }
556
+ }), { retry: findData.retry, delay: 500 });
557
+ });
558
+ }
559
+ /**
560
+ * 执行命令 - 兼容现有的 shell 命令执行
561
+ */
562
+ executeCommand(commandData) {
563
+ return __awaiter(this, void 0, void 0, function* () {
564
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
565
+ try {
566
+ const result = yield this.executeShellCommand(commandData.cmd, {
567
+ title: `执行命令: ${commandData.cmd}`,
568
+ timeout: commandData.timeout,
569
+ waitAfterMs: commandData.wait
570
+ });
571
+ return result;
572
+ }
573
+ catch (error) {
574
+ throw new Error(`命令执行失败: ${error instanceof Error ? error.message : String(error)}`);
575
+ }
576
+ }), { retry: commandData.retry, delay: 1000 });
577
+ });
578
+ }
579
+ /**
580
+ * 获取应用信息
581
+ */
582
+ getAppInfo(appInfoData) {
583
+ return __awaiter(this, void 0, void 0, function* () {
584
+ const startTime = Date.now();
585
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
586
+ try {
587
+ const action = {
588
+ title: `获取应用信息: ${appInfoData.package}`,
589
+ actions: [{
590
+ type: "get_app_info",
591
+ packageName: appInfoData.package
592
+ }]
593
+ };
594
+ yield this.client.executeAction(action);
595
+ return {
596
+ success: true,
597
+ data: { package: appInfoData.package },
598
+ duration: Date.now() - startTime
599
+ };
600
+ }
601
+ catch (error) {
602
+ throw new Error(`获取应用信息失败: ${error instanceof Error ? error.message : String(error)}`);
603
+ }
604
+ }), { retry: appInfoData.retry, delay: 500 });
605
+ });
606
+ }
607
+ /**
608
+ * 截图操作
609
+ */
610
+ takeScreenshot() {
611
+ return __awaiter(this, arguments, void 0, function* (screenshotData = {}) {
612
+ const startTime = Date.now();
613
+ return this.withRetry(() => __awaiter(this, void 0, void 0, function* () {
614
+ try {
615
+ const options = {
616
+ format: screenshotData.format || 'png',
617
+ quality: screenshotData.quality,
618
+ scale: 0.10,
619
+ };
620
+ if (screenshotData.regionX !== undefined && screenshotData.regionY !== undefined &&
621
+ screenshotData.regionWidth !== undefined && screenshotData.regionHeight !== undefined) {
622
+ options.region = {
623
+ left: screenshotData.regionX,
624
+ top: screenshotData.regionY,
625
+ width: screenshotData.regionWidth,
626
+ height: screenshotData.regionHeight
627
+ };
628
+ }
629
+ const screenshot = yield this.client.screenShot(options);
630
+ return {
631
+ success: true,
632
+ data: screenshot,
633
+ duration: Date.now() - startTime
634
+ };
635
+ }
636
+ catch (error) {
637
+ throw new Error(`截图操作失败: ${error instanceof Error ? error.message : String(error)}`);
638
+ }
639
+ }), { retry: screenshotData.retry, delay: 500 });
640
+ });
641
+ }
642
+ /**
643
+ * 执行操作序列 - 支持多个操作连续执行
644
+ */
645
+ executeActions(actions) {
646
+ return __awaiter(this, void 0, void 0, function* () {
647
+ const results = [];
648
+ for (const [index, action] of actions.entries()) {
649
+ try {
650
+ console.log(`🔨 [Actions] 执行第 ${index + 1}/${actions.length} 个操作`);
651
+ let result;
652
+ if ('text' in action && ('targetElementId' in action || 'targetResourceId' in action || 'targetText' in action || 'targetClass' in action)) {
653
+ result = yield this.input(action);
654
+ }
655
+ else if ('targetElementId' in action || 'targetResourceId' in action || 'targetText' in action || 'targetClass' in action || 'targetX' in action || 'targetY' in action) {
656
+ result = yield this.click(action);
657
+ }
658
+ else if ('fromX' in action && 'fromY' in action && 'toX' in action && 'toY' in action) {
659
+ result = yield this.swipe(action);
660
+ }
661
+ else if ('key' in action) {
662
+ result = yield this.pressKey(action);
663
+ }
664
+ else if ('condition' in action && ('targetElementId' in action || 'targetResourceId' in action || 'targetText' in action || 'targetClass' in action)) {
665
+ result = yield this.wait(action);
666
+ }
667
+ else if ('multiple' in action && ('targetElementId' in action || 'targetResourceId' in action || 'targetText' in action || 'targetClass' in action)) {
668
+ const findResult = yield this.find(action);
669
+ result = {
670
+ success: findResult.success,
671
+ data: findResult,
672
+ duration: 0
673
+ };
674
+ }
675
+ else if ('cmd' in action) {
676
+ const cmdResult = yield this.executeCommand(action);
677
+ result = {
678
+ success: cmdResult.success,
679
+ data: cmdResult,
680
+ duration: cmdResult.duration
681
+ };
682
+ }
683
+ else if ('package' in action) {
684
+ result = yield this.getAppInfo(action);
685
+ }
686
+ else {
687
+ result = yield this.takeScreenshot(action);
688
+ }
689
+ results.push(result);
690
+ // 如果操作失败且不允许继续,则抛出错误
691
+ if (!result.success) {
692
+ throw new Error(`操作 ${index + 1} 失败: ${result.error}`);
693
+ }
694
+ console.log(`✅ [Actions] 第 ${index + 1} 个操作执行成功`);
695
+ }
696
+ catch (error) {
697
+ const errorResult = {
698
+ success: false,
699
+ error: error instanceof Error ? error.message : String(error),
700
+ duration: 0
701
+ };
702
+ results.push(errorResult);
703
+ console.error(`❌ [Actions] 第 ${index + 1} 个操作执行失败:`, error);
704
+ throw error; // 停止执行后续操作
705
+ }
706
+ }
707
+ return results;
708
+ });
709
+ }
261
710
  /**
262
711
  * Smart element finder with retry logic
263
712
  */
@@ -411,47 +860,6 @@ class ShellX {
411
860
  return true;
412
861
  });
413
862
  }
414
- /**
415
- * Swipe in a direction
416
- */
417
- swipe(direction_1) {
418
- return __awaiter(this, arguments, void 0, function* (direction, distance = 400, duration = 800) {
419
- // Get screen info to calculate coordinates
420
- const screenInfo = yield this.client.getScreenInfo();
421
- const centerX = (screenInfo.width || 1080) / 2;
422
- const centerY = (screenInfo.height || 1920) / 2;
423
- let from;
424
- let to;
425
- switch (direction) {
426
- case 'up':
427
- from = { x: centerX, y: centerY + distance / 2 };
428
- to = { x: centerX, y: centerY - distance / 2 };
429
- break;
430
- case 'down':
431
- from = { x: centerX, y: centerY - distance / 2 };
432
- to = { x: centerX, y: centerY + distance / 2 };
433
- break;
434
- case 'left':
435
- from = { x: centerX + distance / 2, y: centerY };
436
- to = { x: centerX - distance / 2, y: centerY };
437
- break;
438
- case 'right':
439
- from = { x: centerX - distance / 2, y: centerY };
440
- to = { x: centerX + distance / 2, y: centerY };
441
- break;
442
- }
443
- const swipeAction = {
444
- title: `向${direction}滑动`,
445
- actions: [{
446
- type: "swipe",
447
- from,
448
- to,
449
- options: { durationMs: duration, waitAfterMs: 500 }
450
- }]
451
- };
452
- yield this.client.executeAction(swipeAction);
453
- });
454
- }
455
863
  /**
456
864
  * Take screenshot and save info
457
865
  */
@@ -530,7 +938,35 @@ class ShellX {
530
938
  // Try scrolling to find
531
939
  for (let i = 0; i < maxScrolls; i++) {
532
940
  console.log(`滚动查找第 ${i + 1} 次...`);
533
- yield this.swipe(direction);
941
+ // 获取屏幕信息来计算坐标
942
+ const screenInfo = yield this.client.getScreenInfo();
943
+ const centerX = (screenInfo.width || 1080) / 2;
944
+ const centerY = (screenInfo.height || 1920) / 2;
945
+ let from, to;
946
+ const distance = 400;
947
+ if (direction === 'up') {
948
+ from = { x: centerX, y: centerY + distance / 2 };
949
+ to = { x: centerX, y: centerY - distance / 2 };
950
+ }
951
+ else if (direction === 'down') {
952
+ from = { x: centerX, y: centerY - distance / 2 };
953
+ to = { x: centerX, y: centerY + distance / 2 };
954
+ }
955
+ else if (direction === 'left') {
956
+ from = { x: centerX + distance / 2, y: centerY };
957
+ to = { x: centerX - distance / 2, y: centerY };
958
+ }
959
+ else {
960
+ from = { x: centerX - distance / 2, y: centerY };
961
+ to = { x: centerX + distance / 2, y: centerY };
962
+ }
963
+ yield this.swipe({
964
+ fromX: from.x,
965
+ fromY: from.y,
966
+ toX: to.x,
967
+ toY: to.y,
968
+ duration: 800
969
+ });
534
970
  element = yield this.findElementWithRetry(selector, 1, 0);
535
971
  if (element) {
536
972
  console.log('✅ 滚动后找到元素');
@@ -756,138 +1192,12 @@ class ShellX {
756
1192
  }
757
1193
  }
758
1194
  exports.ShellX = ShellX;
759
- exports.AutomationHelpers = ShellX;
760
1195
  /**
761
1196
  * Create ShellX instance
762
1197
  */
763
1198
  function createShellX(client) {
764
1199
  return new ShellX(client);
765
1200
  }
766
- /**
767
- * 获取ofetch实例
768
- */
769
- function getfetch() {
770
- return __awaiter(this, void 0, void 0, function* () {
771
- try {
772
- // @ts-ignore - Dynamic import may not have types
773
- const { ofetch } = yield Promise.resolve().then(() => __importStar(require('ofetch')));
774
- // 创建配置好的ofetch实例
775
- const fetchInstance = ofetch.create({
776
- timeout: 10000,
777
- retry: 1,
778
- retryDelay: 1000,
779
- headers: {
780
- 'User-Agent': 'ShellX/1.0.1'
781
- },
782
- onRequest({ request, options }) {
783
- console.log(`🌐 [Fetch] 请求: ${options.method || 'GET'} ${request}`);
784
- },
785
- onResponse({ response }) {
786
- console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
787
- },
788
- onRequestError({ error }) {
789
- console.error('❌ [Fetch] 请求错误:', error.message);
790
- },
791
- onResponseError({ response }) {
792
- console.error(`❌ [Fetch] 响应错误: ${response.status} ${response.statusText}`);
793
- }
794
- });
795
- return fetchInstance;
796
- }
797
- catch (error) {
798
- console.warn('⚠️ [Auth] ofetch不可用,使用降级方案');
799
- return createFallbackFetch();
800
- }
801
- });
802
- }
803
- /**
804
- * 降级fetch实现
805
- */
806
- function createFallbackFetch() {
807
- return (url, options) => __awaiter(this, void 0, void 0, function* () {
808
- // 尝试使用全局fetch
809
- if (typeof globalThis.fetch !== 'undefined') {
810
- console.log(`🌐 [Fetch] 请求: ${(options === null || options === void 0 ? void 0 : options.method) || 'GET'} ${url}`);
811
- const response = yield globalThis.fetch(url, options);
812
- console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
813
- if (!response.ok) {
814
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
815
- }
816
- return response.json();
817
- }
818
- // Node.js环境降级
819
- try {
820
- const { default: fetch } = yield Promise.resolve().then(() => __importStar(require('node-fetch')));
821
- console.log(`🌐 [Fetch] 请求: ${(options === null || options === void 0 ? void 0 : options.method) || 'GET'} ${url}`);
822
- const response = yield fetch(url, options);
823
- console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
824
- if (!response.ok) {
825
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
826
- }
827
- return response.json();
828
- }
829
- catch (fetchError) {
830
- throw new Error(`Fetch not available: ${fetchError.message}`);
831
- }
832
- });
833
- }
834
- /**
835
- * 从ShellX.ai服务认证并获取WebSocket连接信息
836
- */
837
- function authenticateDevice(deviceId) {
838
- return __awaiter(this, void 0, void 0, function* () {
839
- const fallbackUrl = `ws://127.0.0.1:9091/api/s/${deviceId}`;
840
- // 1. 优先检测本地服务
841
- try {
842
- // fetch超时实现
843
- const fetchWithTimeout = (url, options, timeout = 1000) => Promise.race([
844
- fetch(url, options),
845
- new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
846
- ]);
847
- const localResp = yield fetchWithTimeout('http://127.0.0.1:9091/info', { method: 'GET', headers: { 'Accept': 'application/json' } }, 1000);
848
- if (localResp.ok) {
849
- const info = yield localResp.json();
850
- if (info && (info.status === 'ok' || info.status === 1)) {
851
- console.log('✅ [Auth] 本地ShellX服务可用,直接使用本地服务:', fallbackUrl);
852
- return fallbackUrl;
853
- }
854
- }
855
- }
856
- catch (e) {
857
- // 本地不可用,继续走远程
858
- }
859
- // 2. 远程认证逻辑
860
- authKey = (0, utils_1.getEnvVar)('SHELLX_AUTH_KEY');
861
- if (!authKey) {
862
- throw new Error('SHELLX_AUTH_KEY environment variable is required');
863
- }
864
- try {
865
- console.log('🔑 [Auth] 正在认证设备...');
866
- // 获取ofetch实例
867
- const fetchFn = yield getfetch();
868
- // ofetch自动处理JSON解析和错误处理
869
- const data = yield fetchFn(`https://shellx.ai/api/device/${deviceId}`, {
870
- method: 'GET',
871
- headers: {
872
- 'Content-Type': 'application/json',
873
- }
874
- });
875
- const jsonData = JSON.parse(data);
876
- console.log('✅ [Auth] ShellX.ai设备认证成功');
877
- console.log(`📡 [Auth] 设备ID: ${jsonData.authenticate}`);
878
- console.log(`📡 [Auth] ShellX.ai服务地址: ${jsonData.machine}`);
879
- console.log(`📡 [Auth] 注册时间: ${jsonData.registered_at}`);
880
- console.log(`📡 [Auth] 最后更新: ${jsonData.last_updated}`);
881
- return jsonData.machine + '/api/s/' + jsonData.authenticate;
882
- }
883
- catch (error) {
884
- const errorMessage = error instanceof Error ? error.message : String(error);
885
- console.warn('⚠️ [Auth] ShellX.ai在线认证失败,使用本地服务:', errorMessage);
886
- console.log(`🔄 [Auth] 使用本地ShellX服务: ${fallbackUrl}`);
887
- return fallbackUrl;
888
- }
889
- });
890
- }
891
1201
  /**
892
1202
  * Create ShellX instance with automatic authentication and shell output monitoring
893
1203
  * 自动处理ShellX.ai认证和连接,无需外部提供连接地址
@@ -895,9 +1205,8 @@ function authenticateDevice(deviceId) {
895
1205
  function createShellXWithShellMonitoring() {
896
1206
  return __awaiter(this, arguments, void 0, function* (config = {}) {
897
1207
  try {
898
- const wsUrl = yield authenticateDevice(config.deviceId);
899
1208
  const shellx = new ShellX(null);
900
- const client = new index_1.default(wsUrl, Object.assign(Object.assign({}, config), { onMessage: (message) => {
1209
+ const client = new index_1.default(config.deviceId, Object.assign(Object.assign({}, config), { onMessage: (message) => {
901
1210
  if (message.chunks) {
902
1211
  shellx.handleShellOutput(message.chunks);
903
1212
  }
@@ -918,7 +1227,7 @@ function createShellXWithShellMonitoring() {
918
1227
  // 将 shellx 实例关联到客户端
919
1228
  client.setShellX(shellx);
920
1229
  console.log('🚀 [ShellX] 初始化完成,等待ShellX.ai服务响应...');
921
- return { client, shellx };
1230
+ return shellx;
922
1231
  }
923
1232
  catch (error) {
924
1233
  console.error('❌ [ShellX] 初始化失败:', error);