@cydm/magic-shell-agent-node 0.1.0 → 0.1.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/local-direct-server.js +3 -2
- package/dist/workbench/index.html +3827 -0
- package/dist/workbench/runtime-control.js +180 -0
- package/dist/workbench/runtime-messages.js +80 -0
- package/dist/workbench/runtime-node-ui.js +102 -0
- package/dist/workbench/runtime-render.js +180 -0
- package/dist/workbench/runtime-terminal.js +250 -0
- package/dist/workbench/runtime-transport.js +105 -0
- package/dist/workbench/runtime-worker-ui.js +94 -0
- package/dist/workbench/test.html +325 -0
- package/package.json +10 -3
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Magic Shell - 自动化测试</title>
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 20px;
|
|
12
|
+
background: #1a1a2e;
|
|
13
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
14
|
+
color: #eaeaea;
|
|
15
|
+
}
|
|
16
|
+
#status-panel {
|
|
17
|
+
background: #16213e;
|
|
18
|
+
padding: 15px;
|
|
19
|
+
border-radius: 8px;
|
|
20
|
+
margin-bottom: 15px;
|
|
21
|
+
}
|
|
22
|
+
#terminal {
|
|
23
|
+
height: 400px;
|
|
24
|
+
border: 2px solid #0f3460;
|
|
25
|
+
border-radius: 8px;
|
|
26
|
+
}
|
|
27
|
+
#results {
|
|
28
|
+
background: #0f3460;
|
|
29
|
+
padding: 15px;
|
|
30
|
+
border-radius: 8px;
|
|
31
|
+
margin-top: 15px;
|
|
32
|
+
max-height: 300px;
|
|
33
|
+
overflow-y: auto;
|
|
34
|
+
}
|
|
35
|
+
.success { color: #0f0; }
|
|
36
|
+
.error { color: #f00; }
|
|
37
|
+
.info { color: #0ff; }
|
|
38
|
+
button {
|
|
39
|
+
background: #e94560;
|
|
40
|
+
border: none;
|
|
41
|
+
color: white;
|
|
42
|
+
padding: 10px 20px;
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
margin: 5px;
|
|
46
|
+
}
|
|
47
|
+
button:disabled {
|
|
48
|
+
background: #555;
|
|
49
|
+
}
|
|
50
|
+
</style>
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
<h2>🧪 Magic Shell 自动化测试</h2>
|
|
54
|
+
|
|
55
|
+
<div id="status-panel">
|
|
56
|
+
<div>状态: <span id="status">等待开始...</span></div>
|
|
57
|
+
<div>测试进度: <span id="progress">0/3</span></div>
|
|
58
|
+
<div>收到输出: <span id="output-size">0</span> 字符</div>
|
|
59
|
+
<button id="startBtn" onclick="startTest()">开始三轮测试</button>
|
|
60
|
+
<button id="manualBtn" onclick="toggleManual()">手动输入</button>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div id="terminal"></div>
|
|
64
|
+
|
|
65
|
+
<div id="results">
|
|
66
|
+
<h3>📊 测试结果</h3>
|
|
67
|
+
<div id="test-log"></div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
|
|
71
|
+
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
|
|
72
|
+
|
|
73
|
+
<script>
|
|
74
|
+
// 配置
|
|
75
|
+
const CONFIG = {
|
|
76
|
+
relayUrl: 'ws://localhost:8080',
|
|
77
|
+
nodeId: 'test-pie',
|
|
78
|
+
password: '123456'
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// 测试用例
|
|
82
|
+
const TESTS = [
|
|
83
|
+
{ question: '你好,请简单介绍一下自己', expect: ['Pie', 'AI', '助手'] },
|
|
84
|
+
{ question: '1+1等于几?', expect: ['2', '二'] },
|
|
85
|
+
{ question: '谢谢,再见!', expect: ['再见', '欢迎'] }
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
// 状态
|
|
89
|
+
let ws = null;
|
|
90
|
+
let term = null;
|
|
91
|
+
let sessionId = null;
|
|
92
|
+
let currentTest = -1;
|
|
93
|
+
let allOutputs = [];
|
|
94
|
+
let testResults = [];
|
|
95
|
+
let waitingForReply = false;
|
|
96
|
+
|
|
97
|
+
// 初始化终端
|
|
98
|
+
term = new Terminal({
|
|
99
|
+
fontFamily: '"SF Mono", Monaco, monospace',
|
|
100
|
+
fontSize: 14,
|
|
101
|
+
theme: {
|
|
102
|
+
background: '#1a1a2e',
|
|
103
|
+
foreground: '#eaeaea'
|
|
104
|
+
},
|
|
105
|
+
scrollback: 10000
|
|
106
|
+
});
|
|
107
|
+
const fitAddon = new FitAddon.FitAddon();
|
|
108
|
+
term.loadAddon(fitAddon);
|
|
109
|
+
term.open(document.getElementById('terminal'));
|
|
110
|
+
fitAddon.fit();
|
|
111
|
+
|
|
112
|
+
function log(message, type = 'info') {
|
|
113
|
+
const div = document.createElement('div');
|
|
114
|
+
div.className = type;
|
|
115
|
+
div.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
|
|
116
|
+
document.getElementById('test-log').appendChild(div);
|
|
117
|
+
document.getElementById('results').scrollTop = document.getElementById('results').scrollHeight;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function updateStatus(status) {
|
|
121
|
+
document.getElementById('status').textContent = status;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function updateProgress() {
|
|
125
|
+
document.getElementById('progress').textContent = `${currentTest + 1}/3`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function updateOutputSize() {
|
|
129
|
+
const size = allOutputs.join('').length;
|
|
130
|
+
document.getElementById('output-size').textContent = size;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 连接
|
|
134
|
+
function connect() {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
updateStatus('连接中...');
|
|
137
|
+
ws = new WebSocket(CONFIG.relayUrl);
|
|
138
|
+
|
|
139
|
+
ws.onopen = () => {
|
|
140
|
+
sessionId = 'test-' + Date.now();
|
|
141
|
+
ws.send(JSON.stringify({
|
|
142
|
+
type: 'auth',
|
|
143
|
+
nodeId: CONFIG.nodeId,
|
|
144
|
+
password: CONFIG.password,
|
|
145
|
+
sessionId
|
|
146
|
+
}));
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
ws.onmessage = (event) => {
|
|
150
|
+
const msg = JSON.parse(event.data);
|
|
151
|
+
handleMessage(msg, resolve, reject);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
ws.onerror = (err) => {
|
|
155
|
+
log('连接错误: ' + err.message, 'error');
|
|
156
|
+
reject(err);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
setTimeout(() => reject(new Error('连接超时')), 10000);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function handleMessage(msg, resolve, reject) {
|
|
164
|
+
switch (msg.type) {
|
|
165
|
+
case 'auth_success':
|
|
166
|
+
log('✓ 认证成功', 'success');
|
|
167
|
+
updateStatus('已连接,等待 pie 启动...');
|
|
168
|
+
resolve();
|
|
169
|
+
break;
|
|
170
|
+
|
|
171
|
+
case 'output':
|
|
172
|
+
term.write(msg.data);
|
|
173
|
+
allOutputs.push(msg.data);
|
|
174
|
+
updateOutputSize();
|
|
175
|
+
|
|
176
|
+
// 检查是否收到有效回复
|
|
177
|
+
if (waitingForReply) {
|
|
178
|
+
const clean = stripAnsi(allOutputs.slice(-5).join(''));
|
|
179
|
+
const test = TESTS[currentTest];
|
|
180
|
+
|
|
181
|
+
// 如果包含期望的关键词,认为测试通过
|
|
182
|
+
if (test && test.expect.some(kw => clean.includes(kw))) {
|
|
183
|
+
const result = {
|
|
184
|
+
test: currentTest + 1,
|
|
185
|
+
question: test.question,
|
|
186
|
+
passed: true,
|
|
187
|
+
preview: clean.slice(0, 200)
|
|
188
|
+
};
|
|
189
|
+
testResults.push(result);
|
|
190
|
+
log(`✓ 测试 ${currentTest + 1} 通过: "${test.question}"`, 'success');
|
|
191
|
+
waitingForReply = false;
|
|
192
|
+
setTimeout(runNextTest, 3000);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case 'error':
|
|
198
|
+
log('错误: ' + msg.error, 'error');
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function stripAnsi(str) {
|
|
204
|
+
return str
|
|
205
|
+
.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '')
|
|
206
|
+
.replace(/\r\n/g, '\n')
|
|
207
|
+
.replace(/\r/g, '\n');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 运行单个测试
|
|
211
|
+
function runTest(index) {
|
|
212
|
+
if (index >= TESTS.length) {
|
|
213
|
+
finishTests();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
currentTest = index;
|
|
218
|
+
updateProgress();
|
|
219
|
+
|
|
220
|
+
const test = TESTS[index];
|
|
221
|
+
log(`开始测试 ${index + 1}: "${test.question}"`);
|
|
222
|
+
updateStatus(`测试中: ${test.question}`);
|
|
223
|
+
|
|
224
|
+
waitingForReply = true;
|
|
225
|
+
ws.send(JSON.stringify({
|
|
226
|
+
type: 'input',
|
|
227
|
+
sessionId,
|
|
228
|
+
data: test.question + '\r'
|
|
229
|
+
}));
|
|
230
|
+
|
|
231
|
+
// 超时处理
|
|
232
|
+
setTimeout(() => {
|
|
233
|
+
if (waitingForReply) {
|
|
234
|
+
const result = {
|
|
235
|
+
test: index + 1,
|
|
236
|
+
question: test.question,
|
|
237
|
+
passed: false,
|
|
238
|
+
preview: '超时无回复'
|
|
239
|
+
};
|
|
240
|
+
testResults.push(result);
|
|
241
|
+
log(`✗ 测试 ${index + 1} 失败: 超时`, 'error');
|
|
242
|
+
waitingForReply = false;
|
|
243
|
+
setTimeout(runNextTest, 2000);
|
|
244
|
+
}
|
|
245
|
+
}, 30000);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function runNextTest() {
|
|
249
|
+
runTest(currentTest + 1);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function finishTests() {
|
|
253
|
+
updateStatus('测试完成');
|
|
254
|
+
document.getElementById('startBtn').disabled = false;
|
|
255
|
+
|
|
256
|
+
const passed = testResults.filter(r => r.passed).length;
|
|
257
|
+
const total = testResults.length;
|
|
258
|
+
|
|
259
|
+
log('');
|
|
260
|
+
log('========================================', 'info');
|
|
261
|
+
log(`测试结果: ${passed}/${total} 通过`, passed === total ? 'success' : 'error');
|
|
262
|
+
log('========================================', 'info');
|
|
263
|
+
|
|
264
|
+
testResults.forEach((r, i) => {
|
|
265
|
+
const status = r.passed ? '✓' : '✗';
|
|
266
|
+
const type = r.passed ? 'success' : 'error';
|
|
267
|
+
log(`${status} 测试 ${i+1}: ${r.question}`, type);
|
|
268
|
+
if (r.preview) {
|
|
269
|
+
log(` 回复预览: ${r.preview.slice(0, 100)}...`, 'info');
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
ws.close();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 开始测试
|
|
277
|
+
async function startTest() {
|
|
278
|
+
document.getElementById('startBtn').disabled = true;
|
|
279
|
+
allOutputs = [];
|
|
280
|
+
testResults = [];
|
|
281
|
+
currentTest = -1;
|
|
282
|
+
waitingForReply = false;
|
|
283
|
+
document.getElementById('test-log').innerHTML = '';
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
log('开始连接...');
|
|
287
|
+
await connect();
|
|
288
|
+
|
|
289
|
+
log('等待 pie 初始化(8秒)...');
|
|
290
|
+
await new Promise(r => setTimeout(r, 8000));
|
|
291
|
+
|
|
292
|
+
log('开始三轮测试...');
|
|
293
|
+
runTest(0);
|
|
294
|
+
|
|
295
|
+
} catch (err) {
|
|
296
|
+
log('测试失败: ' + err.message, 'error');
|
|
297
|
+
document.getElementById('startBtn').disabled = false;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 手动输入模式
|
|
302
|
+
let manualMode = false;
|
|
303
|
+
function toggleManual() {
|
|
304
|
+
manualMode = !manualMode;
|
|
305
|
+
if (manualMode) {
|
|
306
|
+
document.getElementById('manualBtn').textContent = '切换到自动测试';
|
|
307
|
+
term.onData((data) => {
|
|
308
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
309
|
+
ws.send(JSON.stringify({ type: 'input', sessionId, data }));
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
log('已切换到手动输入模式,请使用终端输入');
|
|
313
|
+
} else {
|
|
314
|
+
document.getElementById('manualBtn').textContent = '手动输入';
|
|
315
|
+
log('已切换到自动测试模式');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// 页面加载后自动开始
|
|
320
|
+
window.onload = () => {
|
|
321
|
+
log('页面加载完成,点击"开始三轮测试"按钮');
|
|
322
|
+
};
|
|
323
|
+
</script>
|
|
324
|
+
</body>
|
|
325
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cydm/magic-shell-agent-node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Magic Shell Agent Node - Local agent connector",
|
|
5
|
+
"homepage": "https://magicshell.ai",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"magic-shell",
|
|
8
|
+
"agent-node",
|
|
9
|
+
"terminal",
|
|
10
|
+
"relay"
|
|
11
|
+
],
|
|
5
12
|
"type": "module",
|
|
6
13
|
"main": "./dist/index.js",
|
|
7
14
|
"types": "./dist/index.d.ts",
|
|
8
15
|
"bin": {
|
|
9
|
-
"agent-node": "
|
|
16
|
+
"agent-node": "dist/node.js"
|
|
10
17
|
},
|
|
11
18
|
"files": [
|
|
12
19
|
"dist"
|
|
@@ -20,7 +27,7 @@
|
|
|
20
27
|
"start": "node dist/node.js"
|
|
21
28
|
},
|
|
22
29
|
"dependencies": {
|
|
23
|
-
"@cydm/magic-shell-protocol": "0.1.
|
|
30
|
+
"@cydm/magic-shell-protocol": "0.1.1",
|
|
24
31
|
"node-pty": "^1.1.0",
|
|
25
32
|
"ws": "^8.18.0"
|
|
26
33
|
},
|