@aiscene/aiserver 1.2.3 → 1.2.4
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/debug/websocket-server.d.ts +6 -0
- package/dist/debug/websocket-server.d.ts.map +1 -1
- package/dist/debug/websocket-server.js +128 -59
- package/dist/debug/websocket-server.js.map +1 -1
- package/dist/executor/android-executor.d.ts.map +1 -1
- package/dist/executor/android-executor.js +45 -4
- package/dist/executor/android-executor.js.map +1 -1
- package/dist/web/debug-api.d.ts +9 -0
- package/dist/web/debug-api.d.ts.map +1 -0
- package/dist/web/debug-api.js +368 -0
- package/dist/web/debug-api.js.map +1 -0
- package/dist/web/debug-page.d.ts +7 -0
- package/dist/web/debug-page.d.ts.map +1 -0
- package/dist/web/debug-page.js +1388 -0
- package/dist/web/debug-page.js.map +1 -0
- package/dist/web/debug.html +1069 -0
- package/dist/web/dist/debug.html +1069 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +183 -86
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
package/dist/web/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,MAAM,CAAgB;;IAS9B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;IAuKtB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,eAAe;IAqgBvB,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAOjC,KAAK,IAAI,IAAI;CAGd;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
|
package/dist/web/server.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import axios from 'axios';
|
|
3
4
|
import { createLogger } from '../core/logger.js';
|
|
4
5
|
import { deviceRepo } from '../storage/repositories/device-repo.js';
|
|
5
6
|
import { taskRepo } from '../storage/repositories/task-repo.js';
|
|
6
7
|
import { debugLogRepo } from '../storage/repositories/debug-log-repo.js';
|
|
7
8
|
import { executionLogRepo } from '../storage/repositories/execution-log-repo.js';
|
|
9
|
+
import { getDebugPageHtml } from './debug-page.js';
|
|
8
10
|
const logger = createLogger('WebServer');
|
|
9
11
|
export class WebServer {
|
|
10
12
|
app;
|
|
@@ -95,6 +97,87 @@ export class WebServer {
|
|
|
95
97
|
const limit = parseInt(req.query.limit) || 1000;
|
|
96
98
|
res.json(debugLogRepo.getLogsBySession(req.params.sessionId, limit));
|
|
97
99
|
});
|
|
100
|
+
// ===== Test Case Management (代理后端API) =====
|
|
101
|
+
const BACKEND_API = 'http://nethp-test.jd.com';
|
|
102
|
+
// 获取文件夹配置树
|
|
103
|
+
api.get('/testcases/folder-tree', async (_, res) => {
|
|
104
|
+
try {
|
|
105
|
+
const response = await axios.get(`${BACKEND_API}/rest/ai-validator/folder-config-tree`, { timeout: 10000 });
|
|
106
|
+
res.json({ success: true, data: response.data });
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
logger.warn(`获取文件夹树失败: ${error.message}`);
|
|
110
|
+
res.json({ success: false, data: [], message: error.message });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
// 获取文件夹下的用例列表
|
|
114
|
+
api.get('/testcases/folder/:folderId', async (req, res) => {
|
|
115
|
+
try {
|
|
116
|
+
const response = await axios.get(`${BACKEND_API}/rest/ai-validator/folder-config/${req.params.folderId}`, { timeout: 10000 });
|
|
117
|
+
res.json({ success: true, data: response.data });
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
logger.warn(`获取用例列表失败: ${error.message}`);
|
|
121
|
+
res.json({ success: false, data: [], message: error.message });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// 搜索用例
|
|
125
|
+
api.get('/testcases/search', async (req, res) => {
|
|
126
|
+
try {
|
|
127
|
+
const { configName, updatedBy } = req.query;
|
|
128
|
+
const response = await axios.get(`${BACKEND_API}/rest/ai-validator/configs/search/with-folder`, {
|
|
129
|
+
params: { configName, updatedBy },
|
|
130
|
+
timeout: 10000,
|
|
131
|
+
});
|
|
132
|
+
res.json({ success: true, data: response.data });
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
logger.warn(`搜索用例失败: ${error.message}`);
|
|
136
|
+
res.json({ success: false, data: [], message: error.message });
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
// 保存/更新测试用例
|
|
140
|
+
api.post('/testcases', async (req, res) => {
|
|
141
|
+
try {
|
|
142
|
+
const response = await axios.post(`${BACKEND_API}/rest/ai-validator/configs`, req.body, { timeout: 10000 });
|
|
143
|
+
res.json({ success: true, data: response.data, message: '用例保存成功' });
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
const msg = axios.isAxiosError(error) ? `HTTP ${error.response?.status}: ${JSON.stringify(error.response?.data)}` : error.message;
|
|
147
|
+
logger.warn(`保存用例失败: ${msg}`);
|
|
148
|
+
res.json({ success: false, message: msg });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
api.put('/testcases/:id', async (req, res) => {
|
|
152
|
+
try {
|
|
153
|
+
const response = await axios.put(`${BACKEND_API}/rest/ai-validator/configs/${req.params.id}`, req.body, { timeout: 10000 });
|
|
154
|
+
res.json({ success: true, data: response.data, message: '用例更新成功' });
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const msg = axios.isAxiosError(error) ? `HTTP ${error.response?.status}: ${JSON.stringify(error.response?.data)}` : error.message;
|
|
158
|
+
logger.warn(`更新用例失败: ${msg}`);
|
|
159
|
+
res.json({ success: false, message: msg });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
// 获取在线设备列表(用于调试页面选择设备)
|
|
163
|
+
api.get('/debug/devices', (_, res) => {
|
|
164
|
+
res.json(deviceRepo.getOnline());
|
|
165
|
+
});
|
|
166
|
+
// 调试执行脚本 (通过WebSocket协议的HTTP触发)
|
|
167
|
+
api.post('/debug/execute', async (req, res) => {
|
|
168
|
+
try {
|
|
169
|
+
const { script, deviceId, url, runMode, platform, packageName, bundleId, mobileMode, loginUsername, loginPassword, isRnUrl, skipAppiumDriver } = req.body;
|
|
170
|
+
// 直接返回需要通过WebSocket执行的指令信息
|
|
171
|
+
res.json({
|
|
172
|
+
success: true,
|
|
173
|
+
message: '请通过WebSocket连接执行调试',
|
|
174
|
+
wsUrl: `ws://${req.hostname || 'localhost'}:8002`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
res.json({ success: false, message: error.message });
|
|
179
|
+
}
|
|
180
|
+
});
|
|
98
181
|
this.app.use('/api', api);
|
|
99
182
|
}
|
|
100
183
|
setupStaticFiles() {
|
|
@@ -114,44 +197,44 @@ export class WebServer {
|
|
|
114
197
|
<head>
|
|
115
198
|
<meta charset="UTF-8">
|
|
116
199
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
117
|
-
<title>AIServer
|
|
200
|
+
<title>AIServer 管理面板</title>
|
|
118
201
|
<style>
|
|
119
202
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
120
|
-
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#
|
|
203
|
+
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#fff;color:#111;min-height:100vh}
|
|
121
204
|
.container{max-width:1400px;margin:0 auto;padding:20px}
|
|
122
205
|
.header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px}
|
|
123
206
|
.header h1{font-size:24px;color:#38bdf8}
|
|
124
|
-
.tabs{display:flex;gap:4px;background:#
|
|
207
|
+
.tabs{display:flex;gap:4px;background:#fff;border-radius:8px;padding:4px;margin-bottom:24px}
|
|
125
208
|
.tab{padding:8px 20px;border-radius:6px;cursor:pointer;font-size:14px;font-weight:500;color:#94a3b8;border:none;background:none;transition:all .2s}
|
|
126
209
|
.tab.active{background:#38bdf8;color:#0f172a}
|
|
127
210
|
.tab:hover:not(.active){color:#e2e8f0}
|
|
128
211
|
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:24px}
|
|
129
|
-
.card{background:#
|
|
212
|
+
.card{background:#fff;border-radius:8px;padding:20px;border:1px solid #334155}
|
|
130
213
|
.card h3{font-size:13px;color:#94a3b8;margin-bottom:8px;text-transform:uppercase;letter-spacing:.5px}
|
|
131
214
|
.card .value{font-size:32px;font-weight:700;color:#38bdf8}
|
|
132
215
|
.card .sub{font-size:12px;color:#64748b;margin-top:4px}
|
|
133
216
|
table{width:100%;border-collapse:collapse;margin-top:8px}
|
|
134
217
|
th,td{text-align:left;padding:10px 14px;border-bottom:1px solid #1e293b;font-size:13px}
|
|
135
|
-
th{color:#94a3b8;font-size:11px;text-transform:uppercase;letter-spacing:.5px;background:#
|
|
136
|
-
tr:hover{background:#
|
|
218
|
+
th{color:#94a3b8;font-size:11px;text-transform:uppercase;letter-spacing:.5px;background:#fff;position:sticky;top:0}
|
|
219
|
+
tr:hover{background:#fff}
|
|
137
220
|
.badge{display:inline-block;padding:3px 10px;border-radius:12px;font-size:11px;font-weight:600}
|
|
138
|
-
.b-online{background:#
|
|
139
|
-
.b-offline{background:#
|
|
140
|
-
.b-running{background:#
|
|
141
|
-
.b-completed{background:#
|
|
142
|
-
.b-failed{background:#
|
|
143
|
-
.b-pending{background:#
|
|
144
|
-
.b-idle{background:#
|
|
145
|
-
.b-stopped{background:#
|
|
221
|
+
.b-online{background:#fff;color:#22c55e}
|
|
222
|
+
.b-offline{background:#fff;color:#ef4444}
|
|
223
|
+
.b-running{background:#fff;color:#f59e0b}
|
|
224
|
+
.b-completed{background:#fff;color:#22c55e}
|
|
225
|
+
.b-failed{background:#fff;color:#ef4444}
|
|
226
|
+
.b-pending{background:#fff;color:#38bdf8}
|
|
227
|
+
.b-idle{background:#fff;color:#94a3b8}
|
|
228
|
+
.b-stopped{background:#fff;color:#94a3b8}
|
|
146
229
|
.btn{padding:6px 14px;border-radius:6px;border:none;cursor:pointer;font-size:12px;font-weight:600;transition:all .2s}
|
|
147
|
-
.btn-primary{background:#
|
|
148
|
-
.btn-primary:hover{background:#
|
|
230
|
+
.btn-primary{background:#fff;color:#0f172a}
|
|
231
|
+
.btn-primary:hover{background:#fff}
|
|
149
232
|
.btn-sm{padding:4px 10px;font-size:11px}
|
|
150
233
|
.section-title{font-size:16px;font-weight:600;color:#e2e8f0;margin:20px 0 12px;display:flex;align-items:center;gap:8px}
|
|
151
234
|
.mono{font-family:'SF Mono',Monaco,Consolas,monospace;font-size:12px}
|
|
152
|
-
.log-viewer{background:#
|
|
235
|
+
.log-viewer{background:#fff;border:1px solid #334155;border-radius:8px;padding:16px;max-height:600px;overflow-y:auto;font-family:'SF Mono',Monaco,Consolas,monospace;font-size:12px;line-height:1.6}
|
|
153
236
|
.log-viewer .log-line{padding:2px 0;border-bottom:1px solid #1e293b20}
|
|
154
|
-
.log-viewer .log-line:hover{background:#
|
|
237
|
+
.log-viewer .log-line:hover{background:#fff}
|
|
155
238
|
.log-time{color:#64748b;margin-right:8px}
|
|
156
239
|
.log-level-info{color:#38bdf8}
|
|
157
240
|
.log-level-warn{color:#f59e0b}
|
|
@@ -159,10 +242,10 @@ tr:hover{background:#1e293b}
|
|
|
159
242
|
.log-level-debug{color:#94a3b8}
|
|
160
243
|
.log-content{color:#cbd5e1}
|
|
161
244
|
.log-new{animation:flashLog 0.5s ease-out}
|
|
162
|
-
@keyframes flashLog{0%{background:#
|
|
245
|
+
@keyframes flashLog{0%{background:#fff}100%{background:transparent}}
|
|
163
246
|
.log-live{color:#22c55e;font-size:10px;margin-left:8px;animation:pulse 2s infinite}
|
|
164
247
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}
|
|
165
|
-
.detail-panel{background:#
|
|
248
|
+
.detail-panel{background:#fff;border:1px solid #334155;border-radius:8px;padding:20px;margin-top:16px}
|
|
166
249
|
.detail-row{display:flex;padding:8px 0;border-bottom:1px solid #33415520}
|
|
167
250
|
.detail-label{width:140px;color:#94a3b8;font-size:13px;flex-shrink:0}
|
|
168
251
|
.detail-value{color:#e2e8f0;font-size:13px;word-break:break-all}
|
|
@@ -171,7 +254,7 @@ tr:hover{background:#1e293b}
|
|
|
171
254
|
.refresh-bar{display:flex;align-items:center;gap:12px;margin-bottom:16px}
|
|
172
255
|
.refresh-bar .auto{font-size:12px;color:#64748b}
|
|
173
256
|
.refresh-bar .auto.on{color:#22c55e}
|
|
174
|
-
.search{background:#
|
|
257
|
+
.search{background:#fff;border:1px solid #334155;border-radius:6px;padding:8px 14px;color:#e2e8f0;font-size:13px;width:220px}
|
|
175
258
|
.search:focus{outline:none;border-color:#38bdf8}
|
|
176
259
|
.collapsed{display:none}
|
|
177
260
|
</style>
|
|
@@ -179,22 +262,24 @@ tr:hover{background:#1e293b}
|
|
|
179
262
|
<body>
|
|
180
263
|
<div class="container">
|
|
181
264
|
<div class="header">
|
|
182
|
-
<h1>AIServer
|
|
265
|
+
<h1>AIServer 管理面板</h1>
|
|
183
266
|
<div class="refresh-bar">
|
|
184
|
-
<span class="auto" id="autoLabel"
|
|
185
|
-
<button class="btn btn-primary" onclick="refresh()"
|
|
267
|
+
<span class="auto" id="autoLabel">自动刷新: 开启</span>
|
|
268
|
+
<button class="btn btn-primary" onclick="refresh()">刷新</button>
|
|
186
269
|
</div>
|
|
187
270
|
</div>
|
|
188
271
|
<div class="tabs">
|
|
189
|
-
<button class="tab active" onclick="switchTab('dashboard')"
|
|
190
|
-
<button class="tab" onclick="switchTab('devices')"
|
|
191
|
-
<button class="tab" onclick="switchTab('tasks')"
|
|
192
|
-
<button class="tab" onclick="switchTab('debug')"
|
|
272
|
+
<button class="tab active" onclick="switchTab('dashboard')">仪表盘</button>
|
|
273
|
+
<button class="tab" onclick="switchTab('devices')">设备</button>
|
|
274
|
+
<button class="tab" onclick="switchTab('tasks')">任务</button>
|
|
275
|
+
<button class="tab" onclick="switchTab('debug')">调试会话</button>
|
|
276
|
+
<button class="tab" onclick="switchTab('debugger')">调试执行</button>
|
|
193
277
|
</div>
|
|
194
278
|
<div id="page-dashboard"></div>
|
|
195
279
|
<div id="page-devices" class="collapsed"></div>
|
|
196
280
|
<div id="page-tasks" class="collapsed"></div>
|
|
197
281
|
<div id="page-debug" class="collapsed"></div>
|
|
282
|
+
<div id="page-debugger" class="collapsed">${getDebugPageHtml()}</div>
|
|
198
283
|
</div>
|
|
199
284
|
<script>
|
|
200
285
|
let currentTab='dashboard';
|
|
@@ -321,10 +406,17 @@ function switchTab(tab){
|
|
|
321
406
|
currentTab=tab;
|
|
322
407
|
detailOpen=false;
|
|
323
408
|
document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));
|
|
324
|
-
document.querySelectorAll('.tab').forEach(t=>{
|
|
409
|
+
document.querySelectorAll('.tab').forEach(t=>{
|
|
410
|
+
var txt=t.textContent.toLowerCase();
|
|
411
|
+
if(tab==='dashboard'&&txt.includes('仪表盘'))t.classList.add('active');
|
|
412
|
+
else if(tab==='devices'&&txt.includes('设备'))t.classList.add('active');
|
|
413
|
+
else if(tab==='tasks'&&txt.includes('任务'))t.classList.add('active');
|
|
414
|
+
else if(tab==='debug'&&txt.includes('调试会话'))t.classList.add('active');
|
|
415
|
+
else if(tab==='debugger'&&txt.includes('调试执行'))t.classList.add('active');
|
|
416
|
+
});
|
|
325
417
|
document.querySelectorAll('[id^="page-"]').forEach(p=>p.classList.add('collapsed'));
|
|
326
418
|
document.getElementById('page-'+tab).classList.remove('collapsed');
|
|
327
|
-
refresh();
|
|
419
|
+
if(tab!=='debugger') refresh();
|
|
328
420
|
}
|
|
329
421
|
|
|
330
422
|
function badge(s){
|
|
@@ -337,10 +429,10 @@ function esc(s){return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace
|
|
|
337
429
|
function timeAgo(d){
|
|
338
430
|
if(!d)return '-';
|
|
339
431
|
const diff=(Date.now()-new Date(d).getTime())/1000;
|
|
340
|
-
if(diff<60)return Math.floor(diff)+'
|
|
341
|
-
if(diff<3600)return Math.floor(diff/60)+'
|
|
342
|
-
if(diff<86400)return Math.floor(diff/3600)+'
|
|
343
|
-
return Math.floor(diff/86400)+'
|
|
432
|
+
if(diff<60)return Math.floor(diff)+'秒前';
|
|
433
|
+
if(diff<3600)return Math.floor(diff/60)+'分钟前';
|
|
434
|
+
if(diff<86400)return Math.floor(diff/3600)+'小时前';
|
|
435
|
+
return Math.floor(diff/86400)+'天前';
|
|
344
436
|
}
|
|
345
437
|
|
|
346
438
|
function fmtTime(d){return d?new Date(d).toLocaleString():'-'}
|
|
@@ -354,6 +446,11 @@ async function refresh(){
|
|
|
354
446
|
else if(currentTab==='devices')await loadDevices();
|
|
355
447
|
else if(currentTab==='tasks')await loadTasks();
|
|
356
448
|
else if(currentTab==='debug')await loadDebug();
|
|
449
|
+
else if(currentTab==='debugger')loadDebugger();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function loadDebugger(){
|
|
453
|
+
// 调试页面内容已在服务端渲染时注入
|
|
357
454
|
}
|
|
358
455
|
|
|
359
456
|
// ===== Dashboard =====
|
|
@@ -362,14 +459,14 @@ async function loadDashboard(){
|
|
|
362
459
|
if(!d)return;
|
|
363
460
|
const p=document.getElementById('page-dashboard');
|
|
364
461
|
let h='<div class="grid">';
|
|
365
|
-
h+='<div class="card"><h3
|
|
366
|
-
h+='<div class="card"><h3
|
|
367
|
-
h+='<div class="card"><h3
|
|
462
|
+
h+='<div class="card"><h3>在线设备</h3><div class="value">'+d.devices.online+'/'+d.devices.total+'</div><div class="sub">已连接设备</div></div>';
|
|
463
|
+
h+='<div class="card"><h3>运行中任务</h3><div class="value">'+d.tasks.running+'</div><div class="sub">总任务: '+d.tasks.total+'</div></div>';
|
|
464
|
+
h+='<div class="card"><h3>调试会话</h3><div class="value">'+d.debugSessions.length+'</div><div class="sub">近期会话</div></div>';
|
|
368
465
|
h+='</div>';
|
|
369
466
|
|
|
370
467
|
// Devices overview
|
|
371
468
|
if(d.devices.list&&d.devices.list.length>0){
|
|
372
|
-
h+='<div class="section-title"
|
|
469
|
+
h+='<div class="section-title">设备列表</div><table><tr><th>序列号</th><th>型号</th><th>品牌</th><th>平台</th><th>状态</th><th>最近心跳</th></tr>';
|
|
373
470
|
d.devices.list.forEach(dev=>{
|
|
374
471
|
h+='<tr><td class="mono">'+esc(dev.serialNumber)+'</td><td>'+esc(dev.model||'-')+'</td><td>'+esc(dev.brand||'-')+'</td><td>'+esc(dev.platform)+'</td><td>'+badge(dev.status)+'</td><td>'+timeAgo(dev.lastHeartbeatAt)+'</td></tr>';
|
|
375
472
|
});
|
|
@@ -378,18 +475,18 @@ async function loadDashboard(){
|
|
|
378
475
|
|
|
379
476
|
// Recent tasks
|
|
380
477
|
if(d.tasks.recent&&d.tasks.recent.length>0){
|
|
381
|
-
h+='<div class="section-title"
|
|
478
|
+
h+='<div class="section-title">近期任务</div><table><tr><th>任务ID</th><th>类型</th><th>状态</th><th>开始时间</th><th>完成时间</th><th>操作</th></tr>';
|
|
382
479
|
d.tasks.recent.forEach(t=>{
|
|
383
|
-
h+='<tr><td class="mono">'+esc(t.taskId)+'</td><td>'+esc(t.type)+'</td><td>'+badge(t.status)+'</td><td>'+fmtTime(t.startedAt)+'</td><td>'+fmtTime(t.completedAt)+'</td><td><button class="btn btn-primary btn-sm" onclick="showTaskLogs(\\''+esc(t.taskId)+'\\')"
|
|
480
|
+
h+='<tr><td class="mono">'+esc(t.taskId)+'</td><td>'+esc(t.type)+'</td><td>'+badge(t.status)+'</td><td>'+fmtTime(t.startedAt)+'</td><td>'+fmtTime(t.completedAt)+'</td><td><button class="btn btn-primary btn-sm" onclick="showTaskLogs(\\''+esc(t.taskId)+'\\')">日志</button></td></tr>';
|
|
384
481
|
});
|
|
385
482
|
h+='</table>';
|
|
386
483
|
}
|
|
387
484
|
|
|
388
485
|
// Recent debug sessions
|
|
389
486
|
if(d.debugSessions&&d.debugSessions.length>0){
|
|
390
|
-
h+='<div class="section-title"
|
|
487
|
+
h+='<div class="section-title">近期调试会话</div><table><tr><th>会话ID</th><th>设备</th><th>平台</th><th>状态</th><th>开始时间</th><th>操作</th></tr>';
|
|
391
488
|
d.debugSessions.slice(0,10).forEach(s=>{
|
|
392
|
-
h+='<tr><td class="mono">'+esc(s.sessionId.substring(0,24))+'...</td><td>'+esc(s.deviceId||'-')+'</td><td>'+esc(s.platform||'-')+'</td><td>'+badge(s.status)+'</td><td>'+fmtTime(s.startedAt)+'</td><td><button class="btn btn-primary btn-sm" onclick="showDebugLogs(\\''+esc(s.sessionId)+'\\')"
|
|
489
|
+
h+='<tr><td class="mono">'+esc(s.sessionId.substring(0,24))+'...</td><td>'+esc(s.deviceId||'-')+'</td><td>'+esc(s.platform||'-')+'</td><td>'+badge(s.status)+'</td><td>'+fmtTime(s.startedAt)+'</td><td><button class="btn btn-primary btn-sm" onclick="showDebugLogs(\\''+esc(s.sessionId)+'\\')">日志</button></td></tr>';
|
|
393
490
|
});
|
|
394
491
|
h+='</table>';
|
|
395
492
|
}
|
|
@@ -405,19 +502,19 @@ async function loadDevices(){
|
|
|
405
502
|
const p=document.getElementById('page-devices');
|
|
406
503
|
const online=devs.filter(d=>d.status==='online').length;
|
|
407
504
|
let h='<div class="grid">';
|
|
408
|
-
h+='<div class="card"><h3
|
|
409
|
-
h+='<div class="card"><h3
|
|
410
|
-
h+='<div class="card"><h3
|
|
505
|
+
h+='<div class="card"><h3>设备总数</h3><div class="value">'+devs.length+'</div></div>';
|
|
506
|
+
h+='<div class="card"><h3>在线</h3><div class="value">'+online+'</div></div>';
|
|
507
|
+
h+='<div class="card"><h3>离线</h3><div class="value">'+(devs.length-online)+'</div></div>';
|
|
411
508
|
h+='</div>';
|
|
412
509
|
|
|
413
510
|
if(devs.length>0){
|
|
414
|
-
h+='<div class="section-title"
|
|
511
|
+
h+='<div class="section-title">所有设备</div><table><tr><th>序列号</th><th>型号</th><th>品牌</th><th>平台</th><th>状态</th><th>最近心跳</th><th>创建时间</th></tr>';
|
|
415
512
|
devs.forEach(d=>{
|
|
416
513
|
h+='<tr><td class="mono">'+esc(d.serialNumber)+'</td><td>'+esc(d.model||'-')+'</td><td>'+esc(d.brand||'-')+'</td><td>'+esc(d.platform)+'</td><td>'+badge(d.status)+'</td><td>'+timeAgo(d.lastHeartbeatAt)+'</td><td>'+fmtTime(d.createdAt)+'</td></tr>';
|
|
417
514
|
});
|
|
418
515
|
h+='</table>';
|
|
419
516
|
}else{
|
|
420
|
-
h+='<div class="empty-state"><div class="icon">📱</div><p
|
|
517
|
+
h+='<div class="empty-state"><div class="icon">📱</div><p>暂无设备</p></div>';
|
|
421
518
|
}
|
|
422
519
|
p.innerHTML=h;
|
|
423
520
|
}
|
|
@@ -432,21 +529,21 @@ async function loadTasks(){
|
|
|
432
529
|
const failed=tasks.filter(t=>t.status==='failed').length;
|
|
433
530
|
|
|
434
531
|
let h='<div class="grid">';
|
|
435
|
-
h+='<div class="card"><h3
|
|
436
|
-
h+='<div class="card"><h3
|
|
437
|
-
h+='<div class="card"><h3
|
|
438
|
-
h+='<div class="card"><h3
|
|
532
|
+
h+='<div class="card"><h3>任务总数</h3><div class="value">'+tasks.length+'</div></div>';
|
|
533
|
+
h+='<div class="card"><h3>运行中</h3><div class="value">'+running+'</div></div>';
|
|
534
|
+
h+='<div class="card"><h3>已完成</h3><div class="value">'+completed+'</div></div>';
|
|
535
|
+
h+='<div class="card"><h3>已失败</h3><div class="value">'+failed+'</div></div>';
|
|
439
536
|
h+='</div>';
|
|
440
537
|
|
|
441
538
|
if(tasks.length>0){
|
|
442
|
-
h+='<div class="section-title"
|
|
539
|
+
h+='<div class="section-title">所有任务</div><table><tr><th>任务ID</th><th>执行ID</th><th>类型</th><th>状态</th><th>优先级</th><th>开始时间</th><th>完成时间</th><th>错误</th><th>操作</th></tr>';
|
|
443
540
|
tasks.forEach(t=>{
|
|
444
541
|
const err=t.errorMessage?(t.errorMessage.length>50?esc(t.errorMessage.substring(0,50))+'...':esc(t.errorMessage)):'-';
|
|
445
|
-
h+='<tr><td class="mono">'+esc(t.taskId)+'</td><td class="mono">'+esc(t.executionId||'-')+'</td><td>'+esc(t.type)+'</td><td>'+badge(t.status)+'</td><td>'+t.priority+'</td><td>'+fmtTime(t.startedAt)+'</td><td>'+fmtTime(t.completedAt)+'</td><td style="color:#ef4444;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+err+'</td><td><button class="btn btn-primary btn-sm" onclick="showTaskDetail(\\''+esc(t.taskId)+'\\')"
|
|
542
|
+
h+='<tr><td class="mono">'+esc(t.taskId)+'</td><td class="mono">'+esc(t.executionId||'-')+'</td><td>'+esc(t.type)+'</td><td>'+badge(t.status)+'</td><td>'+t.priority+'</td><td>'+fmtTime(t.startedAt)+'</td><td>'+fmtTime(t.completedAt)+'</td><td style="color:#ef4444;max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+err+'</td><td><button class="btn btn-primary btn-sm" onclick="showTaskDetail(\\''+esc(t.taskId)+'\\')">详情</button></td></tr>';
|
|
446
543
|
});
|
|
447
544
|
h+='</table>';
|
|
448
545
|
}else{
|
|
449
|
-
h+='<div class="empty-state"><div class="icon">📋</div><p
|
|
546
|
+
h+='<div class="empty-state"><div class="icon">📋</div><p>暂无任务</p></div>';
|
|
450
547
|
}
|
|
451
548
|
h+='<div id="task-detail"></div>';
|
|
452
549
|
p.innerHTML=h;
|
|
@@ -459,33 +556,33 @@ async function showTaskDetail(taskId){
|
|
|
459
556
|
const task=await api('/tasks/'+encodeURIComponent(taskId));
|
|
460
557
|
const logs=await api('/tasks/'+encodeURIComponent(taskId)+'/logs');
|
|
461
558
|
const panel=document.getElementById('task-detail');
|
|
462
|
-
if(!task){panel.innerHTML='<p style="color:#ef4444"
|
|
559
|
+
if(!task){panel.innerHTML='<p style="color:#ef4444">任务未找到</p>';detailOpen=false;currentDetailTaskId=null;return}
|
|
463
560
|
|
|
464
561
|
currentDetailTaskStatus=task.status;
|
|
465
562
|
const isRunning=task.status==='running'||task.status==='pending';
|
|
466
563
|
|
|
467
|
-
let h='<div class="detail-panel"><div class="section-title"
|
|
468
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
469
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
470
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
471
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
472
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
473
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
474
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
475
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
476
|
-
if(task.errorMessage)h+='<div class="detail-row"><div class="detail-label"
|
|
564
|
+
let h='<div class="detail-panel"><div class="section-title">任务详情: '+esc(task.taskId)+' <button class="btn btn-sm" style="background:#334155;color:#94a3b8;margin-left:12px" onclick="closeTaskDetail()">关闭</button></div>';
|
|
565
|
+
h+='<div class="detail-row"><div class="detail-label">任务ID</div><div class="detail-value mono">'+esc(task.taskId)+'</div></div>';
|
|
566
|
+
h+='<div class="detail-row"><div class="detail-label">执行ID</div><div class="detail-value mono">'+esc(task.executionId||'-')+'</div></div>';
|
|
567
|
+
h+='<div class="detail-row"><div class="detail-label">类型</div><div class="detail-value">'+esc(task.type)+'</div></div>';
|
|
568
|
+
h+='<div class="detail-row"><div class="detail-label">状态</div><div class="detail-value">'+badge(task.status)+'</div></div>';
|
|
569
|
+
h+='<div class="detail-row"><div class="detail-label">优先级</div><div class="detail-value">'+task.priority+'</div></div>';
|
|
570
|
+
h+='<div class="detail-row"><div class="detail-label">开始时间</div><div class="detail-value">'+fmtTime(task.startedAt)+'</div></div>';
|
|
571
|
+
h+='<div class="detail-row"><div class="detail-label">完成时间</div><div class="detail-value">'+fmtTime(task.completedAt)+'</div></div>';
|
|
572
|
+
h+='<div class="detail-row"><div class="detail-label">创建时间</div><div class="detail-value">'+fmtTime(task.createdAt)+'</div></div>';
|
|
573
|
+
if(task.errorMessage)h+='<div class="detail-row"><div class="detail-label">错误信息</div><div class="detail-value" style="color:#ef4444">'+esc(task.errorMessage)+'</div></div>';
|
|
477
574
|
if(task.config){
|
|
478
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
575
|
+
h+='<div class="detail-row"><div class="detail-label">配置</div><div class="detail-value mono" style="white-space:pre-wrap;font-size:11px">'+esc(typeof task.config==='object'?JSON.stringify(task.config,null,2):task.config)+'</div></div>';
|
|
479
576
|
}
|
|
480
577
|
if(task.result){
|
|
481
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
578
|
+
h+='<div class="detail-row"><div class="detail-label">结果</div><div class="detail-value mono" style="white-space:pre-wrap;font-size:11px">'+esc(typeof task.result==='object'?JSON.stringify(task.result,null,2):task.result)+'</div></div>';
|
|
482
579
|
}
|
|
483
580
|
|
|
484
581
|
// Execution logs with real-time support
|
|
485
582
|
const logSafeId=esc(taskId).replace(/[^a-zA-Z0-9-_]/g,'_');
|
|
486
583
|
const existingLogsCount=logs?logs.length:0;
|
|
487
584
|
if(existingLogsCount>0||isRunning){
|
|
488
|
-
h+='<div class="section-title" style="margin-top:20px"
|
|
585
|
+
h+='<div class="section-title" style="margin-top:20px">执行日志 <span id="task-log-count-'+logSafeId+'" style="color:#64748b;font-size:12px">'+(existingLogsCount||'')+'</span>';
|
|
489
586
|
if(isRunning)h+=' <span style="color:#22c55e;font-size:12px">● LIVE</span>';
|
|
490
587
|
h+='</div>';
|
|
491
588
|
h+='<div class="log-viewer" id="task-log-'+logSafeId+'" style="max-height:400px">';
|
|
@@ -497,7 +594,7 @@ async function showTaskDetail(taskId){
|
|
|
497
594
|
}
|
|
498
595
|
h+='</div>';
|
|
499
596
|
}else{
|
|
500
|
-
h+='<div class="section-title" style="margin-top:20px"
|
|
597
|
+
h+='<div class="section-title" style="margin-top:20px">执行日志</div><p style="color:#64748b">暂无日志</p>';
|
|
501
598
|
}
|
|
502
599
|
|
|
503
600
|
h+='</div>';
|
|
@@ -527,18 +624,18 @@ async function loadDebug(){
|
|
|
527
624
|
const running=sessions.filter(s=>s.status==='running').length;
|
|
528
625
|
|
|
529
626
|
let h='<div class="grid">';
|
|
530
|
-
h+='<div class="card"><h3
|
|
531
|
-
h+='<div class="card"><h3
|
|
627
|
+
h+='<div class="card"><h3>会话总数</h3><div class="value">'+sessions.length+'</div></div>';
|
|
628
|
+
h+='<div class="card"><h3>运行中</h3><div class="value">'+running+'</div></div>';
|
|
532
629
|
h+='</div>';
|
|
533
630
|
|
|
534
631
|
if(sessions.length>0){
|
|
535
|
-
h+='<div class="section-title"
|
|
632
|
+
h+='<div class="section-title">所有调试会话</div><table><tr><th>会话ID</th><th>任务ID</th><th>设备</th><th>平台</th><th>状态</th><th>开始时间</th><th>完成时间</th><th>操作</th></tr>';
|
|
536
633
|
sessions.forEach(s=>{
|
|
537
|
-
h+='<tr><td class="mono">'+esc(s.sessionId.substring(0,28))+'...</td><td class="mono">'+esc(s.taskId||'-')+'</td><td>'+esc(s.deviceId||'-')+'</td><td>'+esc(s.platform||'-')+'</td><td>'+badge(s.status)+'</td><td>'+fmtTime(s.startedAt)+'</td><td>'+fmtTime(s.completedAt)+'</td><td><button class="btn btn-primary btn-sm" onclick="showDebugDetail(\\''+esc(s.sessionId)+'\\')"
|
|
634
|
+
h+='<tr><td class="mono">'+esc(s.sessionId.substring(0,28))+'...</td><td class="mono">'+esc(s.taskId||'-')+'</td><td>'+esc(s.deviceId||'-')+'</td><td>'+esc(s.platform||'-')+'</td><td>'+badge(s.status)+'</td><td>'+fmtTime(s.startedAt)+'</td><td>'+fmtTime(s.completedAt)+'</td><td><button class="btn btn-primary btn-sm" onclick="showDebugDetail(\\''+esc(s.sessionId)+'\\')">详情</button></td></tr>';
|
|
538
635
|
});
|
|
539
636
|
h+='</table>';
|
|
540
637
|
}else{
|
|
541
|
-
h+='<div class="empty-state"><div class="icon">🐛</div><p
|
|
638
|
+
h+='<div class="empty-state"><div class="icon">🐛</div><p>暂无调试会话</p></div>';
|
|
542
639
|
}
|
|
543
640
|
h+='<div id="debug-detail"></div>';
|
|
544
641
|
p.innerHTML=h;
|
|
@@ -549,21 +646,21 @@ async function showDebugDetail(sessionId){
|
|
|
549
646
|
const session=await api('/debug/sessions/'+encodeURIComponent(sessionId));
|
|
550
647
|
const logs=await api('/debug/sessions/'+encodeURIComponent(sessionId)+'/logs');
|
|
551
648
|
const panel=document.getElementById('debug-detail');
|
|
552
|
-
if(!session){panel.innerHTML='<p style="color:#ef4444"
|
|
553
|
-
|
|
554
|
-
let h='<div class="detail-panel"><div class="section-title"
|
|
555
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
556
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
557
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
558
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
559
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
560
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
561
|
-
h+='<div class="detail-row"><div class="detail-label"
|
|
649
|
+
if(!session){panel.innerHTML='<p style="color:#ef4444">会话未找到</p>';detailOpen=false;return}
|
|
650
|
+
|
|
651
|
+
let h='<div class="detail-panel"><div class="section-title">调试会话详情: '+esc(sessionId)+' <button class="btn btn-sm" style="background:#334155;color:#94a3b8;margin-left:12px" onclick="closeDebugDetail()">关闭</button></div>';
|
|
652
|
+
h+='<div class="detail-row"><div class="detail-label">会话ID</div><div class="detail-value mono">'+esc(session.sessionId)+'</div></div>';
|
|
653
|
+
h+='<div class="detail-row"><div class="detail-label">任务ID</div><div class="detail-value mono">'+esc(session.taskId||'-')+'</div></div>';
|
|
654
|
+
h+='<div class="detail-row"><div class="detail-label">设备</div><div class="detail-value mono">'+esc(session.deviceId||'-')+'</div></div>';
|
|
655
|
+
h+='<div class="detail-row"><div class="detail-label">平台</div><div class="detail-value">'+esc(session.platform||'-')+'</div></div>';
|
|
656
|
+
h+='<div class="detail-row"><div class="detail-label">状态</div><div class="detail-value">'+badge(session.status)+'</div></div>';
|
|
657
|
+
h+='<div class="detail-row"><div class="detail-label">开始时间</div><div class="detail-value">'+fmtTime(session.startedAt)+'</div></div>';
|
|
658
|
+
h+='<div class="detail-row"><div class="detail-label">完成时间</div><div class="detail-value">'+fmtTime(session.completedAt)+'</div></div>';
|
|
562
659
|
h+='</div>';
|
|
563
660
|
|
|
564
661
|
// Session logs
|
|
565
662
|
if(logs&&logs.length>0){
|
|
566
|
-
h+='<div class="detail-panel" style="margin-top:16px"><div class="section-title"
|
|
663
|
+
h+='<div class="detail-panel" style="margin-top:16px"><div class="section-title">会话日志 ('+logs.length+')</div>';
|
|
567
664
|
h+='<div class="log-viewer">';
|
|
568
665
|
logs.forEach(l=>{
|
|
569
666
|
const lvl='log-level-'+esc(l.type==='log_output'?'info':l.type==='error'?'error':'info');
|
|
@@ -572,7 +669,7 @@ async function showDebugDetail(sessionId){
|
|
|
572
669
|
});
|
|
573
670
|
h+='</div></div>';
|
|
574
671
|
}else{
|
|
575
|
-
h+='<div class="detail-panel" style="margin-top:16px"><div class="section-title"
|
|
672
|
+
h+='<div class="detail-panel" style="margin-top:16px"><div class="section-title">会话日志</div><p style="color:#64748b">暂无日志</p></div>';
|
|
576
673
|
}
|
|
577
674
|
|
|
578
675
|
panel.innerHTML=h;
|
package/dist/web/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,2CAA2C,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"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"}
|