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/shellx.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) {
|
|
@@ -18,6 +51,7 @@ exports.createHelpers = createShellX;
|
|
|
18
51
|
exports.createShellXWithShellMonitoring = createShellXWithShellMonitoring;
|
|
19
52
|
exports.createHelpersWithShellMonitoring = createShellXWithShellMonitoring;
|
|
20
53
|
const index_1 = __importDefault(require("./index"));
|
|
54
|
+
const uuid_1 = require("uuid");
|
|
21
55
|
/**
|
|
22
56
|
* ShellX automation utilities for common patterns
|
|
23
57
|
*/
|
|
@@ -45,7 +79,7 @@ class ShellX {
|
|
|
45
79
|
* 处理 shell 输出数据
|
|
46
80
|
*/
|
|
47
81
|
handleShellOutput(chunks) {
|
|
48
|
-
const [
|
|
82
|
+
const [sessionId, len, dataArrays] = chunks;
|
|
49
83
|
try {
|
|
50
84
|
// 将 Uint8Array 数组转换为字符串
|
|
51
85
|
let output = '';
|
|
@@ -66,9 +100,10 @@ class ShellX {
|
|
|
66
100
|
// 更新总输出
|
|
67
101
|
commandPromise.output = this.combineSessionOutputs(commandPromise.sessionOutputs);
|
|
68
102
|
console.log(`📊 [Shell] 命令 ${commandKey} 累积输出长度: ${commandPromise.output.length}`);
|
|
69
|
-
//
|
|
103
|
+
// 调用输出回调(传递清理后的输出)
|
|
70
104
|
if (commandPromise.options.onOutput) {
|
|
71
|
-
|
|
105
|
+
const cleanOutput = this.cleanCommandOutput(output, commandPromise.command);
|
|
106
|
+
commandPromise.options.onOutput(cleanOutput);
|
|
72
107
|
}
|
|
73
108
|
// 检查是否满足完成条件
|
|
74
109
|
this.checkCommandCompletion(commandKey, commandPromise, output);
|
|
@@ -87,10 +122,6 @@ class ShellX {
|
|
|
87
122
|
if (output.includes(command)) {
|
|
88
123
|
return true;
|
|
89
124
|
}
|
|
90
|
-
// 如果输出包含提示符,说明是命令完成
|
|
91
|
-
if (this.containsPrompt(output)) {
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
94
125
|
// 如果有多个命令在等待,按时间顺序分配
|
|
95
126
|
const waitingCommands = Array.from(this.shellCommandPromises.keys());
|
|
96
127
|
if (waitingCommands.length === 1) {
|
|
@@ -99,6 +130,41 @@ class ShellX {
|
|
|
99
130
|
// 多个命令时,根据 sessionId 和命令创建时间进行启发式匹配
|
|
100
131
|
return true; // 暂时返回 true,让所有命令都接收输出
|
|
101
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* 清理命令输出,去除输入的命令内容
|
|
135
|
+
*/
|
|
136
|
+
cleanCommandOutput(output, command) {
|
|
137
|
+
if (!output || !command) {
|
|
138
|
+
return output;
|
|
139
|
+
}
|
|
140
|
+
let cleanOutput = output;
|
|
141
|
+
// 去除命令回显(命令本身)
|
|
142
|
+
cleanOutput = cleanOutput.replace(new RegExp(this.escapeRegExp(command), 'g'), '');
|
|
143
|
+
// 去除可能的命令提示符前缀和后缀
|
|
144
|
+
const promptPatterns = [
|
|
145
|
+
/^\s*[^$#>\s]*[#$>]\s*/, // 匹配提示符前缀
|
|
146
|
+
/^\s*[^$#>\s]*@[^$#>\s]*[#$>]\s*/, // 匹配 user@host 格式
|
|
147
|
+
/^\s*[^$#>\s]*:\s*[^$#>\s]*[#$>]\s*/, // 匹配 path: 格式
|
|
148
|
+
/\s*[^$#>\s]*[#$>]\s*$/, // 匹配提示符后缀
|
|
149
|
+
/\s*[^$#>\s]*@[^$#>\s]*[#$>]\s*$/, // 匹配 user@host 后缀
|
|
150
|
+
/\s*[^$#>\s]*:\s*[^$#>\s]*[#$>]\s*$/, // 匹配 path: 后缀
|
|
151
|
+
/\s*\d+\|[^$#>\s]*[#$>]\s*$/, // 匹配 Android 格式后缀
|
|
152
|
+
];
|
|
153
|
+
for (const pattern of promptPatterns) {
|
|
154
|
+
cleanOutput = cleanOutput.replace(pattern, '');
|
|
155
|
+
}
|
|
156
|
+
// 去除多余的空行和空格
|
|
157
|
+
cleanOutput = cleanOutput.replace(/^\s+|\s+$/g, ''); // 去除首尾空白
|
|
158
|
+
cleanOutput = cleanOutput.replace(/\n\s*\n/g, '\n'); // 去除多余空行
|
|
159
|
+
// 如果清理后为空,返回空字符串
|
|
160
|
+
if (cleanOutput.trim().length === 0) {
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
console.log(`🧹 [Shell] 清理命令输出:`);
|
|
164
|
+
console.log(` 原始: "${output.trim()}"`);
|
|
165
|
+
console.log(` 清理后: "${cleanOutput.trim()}"`);
|
|
166
|
+
return cleanOutput;
|
|
167
|
+
}
|
|
102
168
|
/**
|
|
103
169
|
* 合并多个 session 的输出
|
|
104
170
|
*/
|
|
@@ -112,20 +178,10 @@ class ShellX {
|
|
|
112
178
|
return combined;
|
|
113
179
|
}
|
|
114
180
|
/**
|
|
115
|
-
*
|
|
181
|
+
* 转义正则表达式特殊字符
|
|
116
182
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
/\$ $/,
|
|
120
|
-
/# $/,
|
|
121
|
-
/> $/,
|
|
122
|
-
/\w+@\w+.*\$ $/,
|
|
123
|
-
/shell@.*:.*\$ $/,
|
|
124
|
-
/houji:\/.*\$ $/,
|
|
125
|
-
/\d+\|\w+:\/.*\$ $/, // Android pattern like "127|bluejay:/ $"
|
|
126
|
-
/\w+:\/.*\$ $/ // Android pattern like "bluejay:/ $"
|
|
127
|
-
];
|
|
128
|
-
return promptPatterns.some(pattern => pattern.test(output));
|
|
183
|
+
escapeRegExp(string) {
|
|
184
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
129
185
|
}
|
|
130
186
|
/**
|
|
131
187
|
* 检查命令是否完成
|
|
@@ -133,41 +189,6 @@ class ShellX {
|
|
|
133
189
|
checkCommandCompletion(commandKey, commandPromise, newOutput) {
|
|
134
190
|
const { resolve, startTime, options, output, command } = commandPromise;
|
|
135
191
|
console.log(`🔍 [Shell] 检查命令 "${command}" 完成条件,当前输出长度: ${output.length}`);
|
|
136
|
-
// 检查成功模式
|
|
137
|
-
if (options.successPattern) {
|
|
138
|
-
const pattern = options.successPattern;
|
|
139
|
-
const isMatch = typeof pattern === 'string'
|
|
140
|
-
? output.includes(pattern)
|
|
141
|
-
: pattern.test(output);
|
|
142
|
-
if (isMatch) {
|
|
143
|
-
console.log(`✅ [Shell] 命令 "${command}" 成功完成 (匹配成功模式)`);
|
|
144
|
-
this.resolveCommand(commandKey, {
|
|
145
|
-
success: true,
|
|
146
|
-
output: output.trim(),
|
|
147
|
-
duration: Date.now() - startTime
|
|
148
|
-
});
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// 检查错误模式
|
|
153
|
-
if (options.errorPattern) {
|
|
154
|
-
const pattern = options.errorPattern;
|
|
155
|
-
const isMatch = typeof pattern === 'string'
|
|
156
|
-
? output.includes(pattern)
|
|
157
|
-
: pattern.test(output);
|
|
158
|
-
if (isMatch) {
|
|
159
|
-
console.log(`❌ [Shell] 命令 "${command}" 执行失败 (匹配错误模式)`);
|
|
160
|
-
this.resolveCommand(commandKey, {
|
|
161
|
-
success: false,
|
|
162
|
-
output: output.trim(),
|
|
163
|
-
error: 'Command failed with error pattern',
|
|
164
|
-
duration: Date.now() - startTime
|
|
165
|
-
});
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
// 检查通用完成模式(如命令提示符)
|
|
170
|
-
// 这是最主要的完成检测逻辑,包括对没有输出的命令的支持
|
|
171
192
|
if (this.isCommandComplete(output, command)) {
|
|
172
193
|
console.log(`✅ [Shell] 命令 "${command}" 执行完成 (检测到提示符)`);
|
|
173
194
|
this.resolveCommand(commandKey, {
|
|
@@ -177,26 +198,6 @@ class ShellX {
|
|
|
177
198
|
});
|
|
178
199
|
return;
|
|
179
200
|
}
|
|
180
|
-
// 额外的完成检测:仅当有输出时才进行的检查
|
|
181
|
-
if (output.trim().length > 0) {
|
|
182
|
-
// 检查是否有错误指示器
|
|
183
|
-
if (this.hasErrorIndicators(output)) {
|
|
184
|
-
console.log(`❌ [Shell] 命令 "${command}" 可能执行失败 (检测到错误指示器)`);
|
|
185
|
-
// 不立即失败,继续等待完整的提示符
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
// 检查是否包含命令和结果(备用检测方式)
|
|
189
|
-
if (output.includes(command) && this.containsPrompt(output)) {
|
|
190
|
-
console.log(`✅ [Shell] 命令 "${command}" 执行完成 (命令+提示符备用检测)`);
|
|
191
|
-
this.resolveCommand(commandKey, {
|
|
192
|
-
success: true,
|
|
193
|
-
output: output.trim(),
|
|
194
|
-
duration: Date.now() - startTime
|
|
195
|
-
});
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
// 如果没有检测到完成条件,继续等待
|
|
200
201
|
console.log(`⏳ [Shell] 命令 "${command}" 继续等待完成...`);
|
|
201
202
|
}
|
|
202
203
|
/**
|
|
@@ -215,50 +216,12 @@ class ShellX {
|
|
|
215
216
|
return errorPatterns.some(pattern => pattern.test(output));
|
|
216
217
|
}
|
|
217
218
|
/**
|
|
218
|
-
*
|
|
219
|
+
* 判断命令是否完成(基于时间)
|
|
219
220
|
*/
|
|
220
221
|
isCommandComplete(output, command) {
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
/# $/, // Root shell prompt
|
|
225
|
-
/> $/, // Windows command prompt
|
|
226
|
-
/~\$ $/, // Home directory prompt
|
|
227
|
-
/\w+@\w+.*\$ $/, // User@host prompt
|
|
228
|
-
/shell@.*:.*\$ $/, // Android shell prompt
|
|
229
|
-
/houji:\/.*\$ $/, // Specific Android shell prompt
|
|
230
|
-
/\d+\|\w+:\/.*\$ $/, // Android pattern like "127|bluejay:/ $"
|
|
231
|
-
/\w+:\/.*\$ $/, // Android pattern like "bluejay:/ $"
|
|
232
|
-
/\[.*\]\$ $/, // Bracketed prompt
|
|
233
|
-
/\w+:\w+\$ $/, // Simple path prompt
|
|
234
|
-
/\w+> $/, // Simple greater-than prompt
|
|
235
|
-
/.*:\w+\$ $/, // Path-based prompt
|
|
236
|
-
/\w+@\w+:.* $/, // User@host with path
|
|
237
|
-
/\d+\|.*\$ $/, // Numbered prompt with pipe
|
|
238
|
-
/.*#\s*$/, // Hash prompt with optional space
|
|
239
|
-
/.*\$\s*$/ // Dollar prompt with optional space
|
|
240
|
-
];
|
|
241
|
-
// 分割输出为行,检查最后几行
|
|
242
|
-
const lines = output.split(/\r?\n/).filter(line => line.trim().length > 0);
|
|
243
|
-
const lastLine = lines[lines.length - 1] || '';
|
|
244
|
-
// 清理最后一行的控制字符
|
|
245
|
-
const cleanLastLine = lastLine.replace(/\r/g, '').trim();
|
|
246
|
-
console.log(`🔍 [Shell] 检查命令 "${command}" 最后一行提示符: "${cleanLastLine}"`);
|
|
247
|
-
const hasPrompt = promptPatterns.some(pattern => {
|
|
248
|
-
const matches = pattern.test(cleanLastLine);
|
|
249
|
-
if (matches) {
|
|
250
|
-
console.log(`✅ [Shell] 命令 "${command}" 匹配到提示符模式: ${pattern}`);
|
|
251
|
-
}
|
|
252
|
-
return matches;
|
|
253
|
-
});
|
|
254
|
-
// 如果检测到提示符,则认为命令完成
|
|
255
|
-
// 不再要求输出必须包含命令本身,因为有些命令可能没有输出
|
|
256
|
-
if (hasPrompt) {
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
// 如果没有检测到提示符,但输出包含命令本身,可能命令还在执行中
|
|
260
|
-
// 这种情况下返回false,继续等待
|
|
261
|
-
return false;
|
|
222
|
+
// 简化逻辑:只基于时间判断,不依赖提示符检测
|
|
223
|
+
// 命令完成由超时机制控制
|
|
224
|
+
return false; // 让超时机制处理命令完成
|
|
262
225
|
}
|
|
263
226
|
/**
|
|
264
227
|
* 解析命令 Promise
|
|
@@ -267,7 +230,9 @@ class ShellX {
|
|
|
267
230
|
const commandPromise = this.shellCommandPromises.get(commandKey);
|
|
268
231
|
if (commandPromise) {
|
|
269
232
|
console.log(`✅ [Shell] 解析命令 Promise: ${commandKey}`);
|
|
270
|
-
|
|
233
|
+
// 清理最终输出结果
|
|
234
|
+
const cleanResult = Object.assign(Object.assign({}, result), { output: this.cleanCommandOutput(result.output, commandPromise.command) });
|
|
235
|
+
commandPromise.resolve(cleanResult);
|
|
271
236
|
this.shellCommandPromises.delete(commandKey);
|
|
272
237
|
}
|
|
273
238
|
else {
|
|
@@ -610,7 +575,7 @@ class ShellX {
|
|
|
610
575
|
console.log(`🔨 [Shell] ${title}`);
|
|
611
576
|
console.log(`⏱️ 超时时间: ${timeout}ms`);
|
|
612
577
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
613
|
-
const commandKey =
|
|
578
|
+
const commandKey = (0, uuid_1.v4)();
|
|
614
579
|
console.log(`🔑 [Shell] 生成命令键: ${commandKey}`);
|
|
615
580
|
// 注册命令 Promise
|
|
616
581
|
this.shellCommandPromises.set(commandKey, {
|
|
@@ -627,21 +592,21 @@ class ShellX {
|
|
|
627
592
|
const timeoutId = setTimeout(() => {
|
|
628
593
|
if (this.shellCommandPromises.has(commandKey)) {
|
|
629
594
|
const commandPromise = this.shellCommandPromises.get(commandKey);
|
|
630
|
-
|
|
631
|
-
// 即使超时,如果有输出也返回输出
|
|
595
|
+
this.shellCommandPromises.delete(commandKey);
|
|
632
596
|
if (commandPromise && commandPromise.output.trim().length > 0) {
|
|
633
|
-
console.log(`📤 [Shell] 超时但有输出,返回部分结果`);
|
|
634
|
-
this.shellCommandPromises.delete(commandKey);
|
|
635
597
|
resolve({
|
|
636
|
-
success:
|
|
598
|
+
success: true,
|
|
637
599
|
output: commandPromise.output.trim(),
|
|
638
|
-
error: `Command timeout after ${timeout}ms, but got partial output`,
|
|
639
600
|
duration: Date.now() - startTime
|
|
640
601
|
});
|
|
641
602
|
}
|
|
642
603
|
else {
|
|
643
|
-
|
|
644
|
-
|
|
604
|
+
resolve({
|
|
605
|
+
success: true,
|
|
606
|
+
output: "",
|
|
607
|
+
error: `Command timeout after ${timeout}ms, but got partial output`,
|
|
608
|
+
duration: Date.now() - startTime
|
|
609
|
+
});
|
|
645
610
|
}
|
|
646
611
|
}
|
|
647
612
|
}, timeout);
|
|
@@ -659,10 +624,8 @@ class ShellX {
|
|
|
659
624
|
}
|
|
660
625
|
};
|
|
661
626
|
// 发送命令
|
|
662
|
-
yield this.client.executeAction(shellAction);
|
|
663
|
-
console.log(`📤 [Shell] 命令已发送: ${
|
|
664
|
-
// 等待命令完成(通过 chunks 数据流)
|
|
665
|
-
// Promise 将由 handleShellOutput 解析
|
|
627
|
+
yield this.client.executeAction(shellAction, commandKey);
|
|
628
|
+
console.log(`📤 [Shell] 命令已发送: ${commandKey}`);
|
|
666
629
|
}
|
|
667
630
|
catch (error) {
|
|
668
631
|
clearTimeout(timeoutId);
|
|
@@ -754,7 +717,6 @@ class ShellX {
|
|
|
754
717
|
onError: options === null || options === void 0 ? void 0 : options.onError,
|
|
755
718
|
expectedOutput: options === null || options === void 0 ? void 0 : options.expectedOutput,
|
|
756
719
|
successPattern: options === null || options === void 0 ? void 0 : options.successPattern,
|
|
757
|
-
errorPattern: options === null || options === void 0 ? void 0 : options.errorPattern
|
|
758
720
|
});
|
|
759
721
|
});
|
|
760
722
|
}
|
|
@@ -793,6 +755,74 @@ exports.AutomationHelpers = ShellX;
|
|
|
793
755
|
function createShellX(client) {
|
|
794
756
|
return new ShellX(client);
|
|
795
757
|
}
|
|
758
|
+
/**
|
|
759
|
+
* 获取ofetch实例
|
|
760
|
+
*/
|
|
761
|
+
function getfetch() {
|
|
762
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
763
|
+
try {
|
|
764
|
+
// @ts-ignore - Dynamic import may not have types
|
|
765
|
+
const { ofetch } = yield Promise.resolve().then(() => __importStar(require('ofetch')));
|
|
766
|
+
// 创建配置好的ofetch实例
|
|
767
|
+
const fetchInstance = ofetch.create({
|
|
768
|
+
timeout: 10000,
|
|
769
|
+
retry: 1,
|
|
770
|
+
retryDelay: 1000,
|
|
771
|
+
headers: {
|
|
772
|
+
'User-Agent': 'ShellX/1.0.1'
|
|
773
|
+
},
|
|
774
|
+
onRequest({ request, options }) {
|
|
775
|
+
console.log(`🌐 [Fetch] 请求: ${options.method || 'GET'} ${request}`);
|
|
776
|
+
},
|
|
777
|
+
onResponse({ response }) {
|
|
778
|
+
console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
|
|
779
|
+
},
|
|
780
|
+
onRequestError({ error }) {
|
|
781
|
+
console.error('❌ [Fetch] 请求错误:', error.message);
|
|
782
|
+
},
|
|
783
|
+
onResponseError({ response }) {
|
|
784
|
+
console.error(`❌ [Fetch] 响应错误: ${response.status} ${response.statusText}`);
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
return fetchInstance;
|
|
788
|
+
}
|
|
789
|
+
catch (error) {
|
|
790
|
+
console.warn('⚠️ [Auth] ofetch不可用,使用降级方案');
|
|
791
|
+
return createFallbackFetch();
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* 降级fetch实现
|
|
797
|
+
*/
|
|
798
|
+
function createFallbackFetch() {
|
|
799
|
+
return (url, options) => __awaiter(this, void 0, void 0, function* () {
|
|
800
|
+
// 尝试使用全局fetch
|
|
801
|
+
if (typeof globalThis.fetch !== 'undefined') {
|
|
802
|
+
console.log(`🌐 [Fetch] 请求: ${(options === null || options === void 0 ? void 0 : options.method) || 'GET'} ${url}`);
|
|
803
|
+
const response = yield globalThis.fetch(url, options);
|
|
804
|
+
console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
|
|
805
|
+
if (!response.ok) {
|
|
806
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
807
|
+
}
|
|
808
|
+
return response.json();
|
|
809
|
+
}
|
|
810
|
+
// Node.js环境降级
|
|
811
|
+
try {
|
|
812
|
+
const { default: fetch } = yield Promise.resolve().then(() => __importStar(require('node-fetch')));
|
|
813
|
+
console.log(`🌐 [Fetch] 请求: ${(options === null || options === void 0 ? void 0 : options.method) || 'GET'} ${url}`);
|
|
814
|
+
const response = yield fetch(url, options);
|
|
815
|
+
console.log(`📡 [Fetch] 响应: ${response.status} ${response.statusText}`);
|
|
816
|
+
if (!response.ok) {
|
|
817
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
818
|
+
}
|
|
819
|
+
return response.json();
|
|
820
|
+
}
|
|
821
|
+
catch (fetchError) {
|
|
822
|
+
throw new Error(`Fetch not available: ${fetchError.message}`);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
}
|
|
796
826
|
/**
|
|
797
827
|
* 从ShellX.ai服务认证并获取WebSocket连接信息
|
|
798
828
|
*/
|
|
@@ -804,45 +834,45 @@ function authenticateDevice() {
|
|
|
804
834
|
}
|
|
805
835
|
try {
|
|
806
836
|
console.log('🔑 [Auth] 正在认证设备...');
|
|
807
|
-
|
|
837
|
+
// 获取ofetch实例
|
|
838
|
+
const fetchFn = yield getfetch();
|
|
839
|
+
// ofetch自动处理JSON解析和错误处理
|
|
840
|
+
const data = yield fetchFn(`https://shellx.ai/api/device/${authKey}`, {
|
|
808
841
|
method: 'GET',
|
|
809
842
|
headers: {
|
|
810
843
|
'Content-Type': 'application/json',
|
|
811
844
|
}
|
|
812
845
|
});
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
console.log('✅ [Auth] 设备认证成功');
|
|
821
|
-
console.log(`📡 [Auth] 连接信息: ${data.machine}`);
|
|
822
|
-
return data.machine;
|
|
846
|
+
const jsonData = JSON.parse(data);
|
|
847
|
+
console.log('✅ [Auth] ShellX.ai设备认证成功');
|
|
848
|
+
console.log(`📡 [Auth] 设备ID: ${jsonData.authenticate}`);
|
|
849
|
+
console.log(`📡 [Auth] ShellX.ai服务地址: ${jsonData.machine}`);
|
|
850
|
+
console.log(`📡 [Auth] 注册时间: ${jsonData.registered_at}`);
|
|
851
|
+
console.log(`📡 [Auth] 最后更新: ${jsonData.last_updated}`);
|
|
852
|
+
return jsonData.machine;
|
|
823
853
|
}
|
|
824
854
|
catch (error) {
|
|
825
855
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
826
|
-
console.warn('⚠️ [Auth]
|
|
827
|
-
// 认证失败时使用默认的本地
|
|
856
|
+
console.warn('⚠️ [Auth] ShellX.ai在线认证失败,使用本地服务:', errorMessage);
|
|
857
|
+
// 认证失败时使用默认的本地ShellX服务
|
|
828
858
|
const fallbackUrl = `ws://127.0.0.1:9091/api/s/${authKey}`;
|
|
829
|
-
console.log(`🔄 [Auth]
|
|
859
|
+
console.log(`🔄 [Auth] 使用本地ShellX服务: ${fallbackUrl}`);
|
|
830
860
|
return fallbackUrl;
|
|
831
861
|
}
|
|
832
862
|
});
|
|
833
863
|
}
|
|
834
864
|
/**
|
|
835
865
|
* Create ShellX instance with automatic authentication and shell output monitoring
|
|
836
|
-
*
|
|
866
|
+
* 自动处理ShellX.ai认证和连接,无需外部提供连接地址
|
|
837
867
|
*/
|
|
838
868
|
function createShellXWithShellMonitoring() {
|
|
839
869
|
return __awaiter(this, arguments, void 0, function* (config = {}) {
|
|
840
870
|
try {
|
|
841
|
-
// 认证并获取
|
|
871
|
+
// 认证并获取ShellX.ai服务地址
|
|
842
872
|
const wsUrl = yield authenticateDevice();
|
|
843
873
|
// 先创建 shellx 实例,但不绑定客户端
|
|
844
874
|
const shellx = new ShellX(null);
|
|
845
|
-
//
|
|
875
|
+
// 创建ShellX客户端并设置消息监听器
|
|
846
876
|
const client = new index_1.default(wsUrl, Object.assign(Object.assign({}, config), { onMessage: (message) => {
|
|
847
877
|
// 处理 chunks 数据(pty 终端输出)
|
|
848
878
|
if (message.chunks) {
|
|
@@ -852,10 +882,20 @@ function createShellXWithShellMonitoring() {
|
|
|
852
882
|
if (config.onMessage) {
|
|
853
883
|
config.onMessage(message);
|
|
854
884
|
}
|
|
885
|
+
}, onOpen: () => {
|
|
886
|
+
// 调用原始的onOpen处理器
|
|
887
|
+
if (config.onOpen) {
|
|
888
|
+
config.onOpen();
|
|
889
|
+
}
|
|
855
890
|
} }));
|
|
891
|
+
// 等待ShellX.ai服务连接完成
|
|
892
|
+
console.log('⏳ [ShellX] 等待ShellX.ai服务连接...');
|
|
893
|
+
yield client.waitForInitialization();
|
|
856
894
|
// 绑定客户端到 shellx
|
|
857
895
|
shellx.client = client;
|
|
858
|
-
|
|
896
|
+
// 将 shellx 实例关联到客户端
|
|
897
|
+
client.setShellX(shellx);
|
|
898
|
+
console.log('🚀 [ShellX] 初始化完成,等待ShellX.ai服务响应...');
|
|
859
899
|
return { client, shellx };
|
|
860
900
|
}
|
|
861
901
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shellx-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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",
|
|
@@ -23,7 +23,15 @@
|
|
|
23
23
|
"test:shellx-ai": "ts-node -r dotenv/config examples/shellx.ai/index.ts",
|
|
24
24
|
"test:shell-commands": "ts-node -r dotenv/config examples/shell-commands-demo.ts",
|
|
25
25
|
"test:no-output": "ts-node -r dotenv/config examples/no-output-command-test.ts",
|
|
26
|
-
"test:find-elements": "ts-node -r dotenv/config examples/find-elements-demo.ts"
|
|
26
|
+
"test:find-elements": "ts-node -r dotenv/config examples/find-elements-demo.ts",
|
|
27
|
+
"test:api": "ts-node test-shellx.ts",
|
|
28
|
+
"test:auth": "ts-node test-auth.ts",
|
|
29
|
+
"test:ofetch": "ts-node test-ofetch.ts",
|
|
30
|
+
"test:paytm-connection": "ts-node -r dotenv/config test-paytm-connection.ts",
|
|
31
|
+
"test:prompt-detection": "ts-node -r dotenv/config test-shell-prompt-integration.ts",
|
|
32
|
+
"test:simplified": "ts-node -r dotenv/config test-simplified-shell.ts",
|
|
33
|
+
"install-deps": "node install-deps.js",
|
|
34
|
+
"postinstall": "node install-deps.js"
|
|
27
35
|
},
|
|
28
36
|
"keywords": [
|
|
29
37
|
"websocket",
|
|
@@ -42,6 +50,10 @@
|
|
|
42
50
|
"dotenv": "^16.4.5",
|
|
43
51
|
"uuid": "^11.1.0"
|
|
44
52
|
},
|
|
53
|
+
"optionalDependencies": {
|
|
54
|
+
"node-fetch": "^3.3.2",
|
|
55
|
+
"ws": "^8.14.2"
|
|
56
|
+
},
|
|
45
57
|
"devDependencies": {
|
|
46
58
|
"@types/jest": "^30.0.0",
|
|
47
59
|
"@types/node": "^24.0.13",
|