@cloudbase/cloudbase-mcp 1.8.3 → 1.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +215 -47
  2. package/dist/cli.cjs +6099 -0
  3. package/dist/cli.cjs.map +1 -0
  4. package/dist/cli.d.cts +1 -0
  5. package/dist/cli.d.ts +0 -2
  6. package/dist/cli.js +6062 -60
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.cjs +6112 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +213 -0
  11. package/dist/index.d.ts +213 -5
  12. package/dist/index.js +6069 -7
  13. package/dist/index.js.map +1 -1
  14. package/package.json +14 -5
  15. package/dist/auth.d.ts +0 -3
  16. package/dist/auth.d.ts.map +0 -1
  17. package/dist/auth.js +0 -26
  18. package/dist/auth.js.map +0 -1
  19. package/dist/cli.d.ts.map +0 -1
  20. package/dist/cloudbase-manager.d.ts +0 -30
  21. package/dist/cloudbase-manager.d.ts.map +0 -1
  22. package/dist/cloudbase-manager.js +0 -156
  23. package/dist/cloudbase-manager.js.map +0 -1
  24. package/dist/index.d.ts.map +0 -1
  25. package/dist/interactive-server.d.ts +0 -34
  26. package/dist/interactive-server.d.ts.map +0 -1
  27. package/dist/interactive-server.js +0 -2545
  28. package/dist/interactive-server.js.map +0 -1
  29. package/dist/server.d.ts +0 -25
  30. package/dist/server.d.ts.map +0 -1
  31. package/dist/server.js +0 -60
  32. package/dist/server.js.map +0 -1
  33. package/dist/tools/database.d.ts +0 -3
  34. package/dist/tools/database.d.ts.map +0 -1
  35. package/dist/tools/database.js +0 -1329
  36. package/dist/tools/database.js.map +0 -1
  37. package/dist/tools/download.d.ts +0 -3
  38. package/dist/tools/download.d.ts.map +0 -1
  39. package/dist/tools/download.js +0 -259
  40. package/dist/tools/download.js.map +0 -1
  41. package/dist/tools/env.d.ts +0 -3
  42. package/dist/tools/env.d.ts.map +0 -1
  43. package/dist/tools/env.js +0 -213
  44. package/dist/tools/env.js.map +0 -1
  45. package/dist/tools/file.d.ts +0 -3
  46. package/dist/tools/file.d.ts.map +0 -1
  47. package/dist/tools/file.js +0 -211
  48. package/dist/tools/file.js.map +0 -1
  49. package/dist/tools/functions.d.ts +0 -5
  50. package/dist/tools/functions.d.ts.map +0 -1
  51. package/dist/tools/functions.js +0 -540
  52. package/dist/tools/functions.js.map +0 -1
  53. package/dist/tools/gateway.d.ts +0 -3
  54. package/dist/tools/gateway.d.ts.map +0 -1
  55. package/dist/tools/gateway.js +0 -39
  56. package/dist/tools/gateway.js.map +0 -1
  57. package/dist/tools/hosting.d.ts +0 -3
  58. package/dist/tools/hosting.d.ts.map +0 -1
  59. package/dist/tools/hosting.js +0 -231
  60. package/dist/tools/hosting.js.map +0 -1
  61. package/dist/tools/interactive.d.ts +0 -13
  62. package/dist/tools/interactive.d.ts.map +0 -1
  63. package/dist/tools/interactive.js +0 -195
  64. package/dist/tools/interactive.js.map +0 -1
  65. package/dist/tools/rag.d.ts +0 -3
  66. package/dist/tools/rag.d.ts.map +0 -1
  67. package/dist/tools/rag.js +0 -119
  68. package/dist/tools/rag.js.map +0 -1
  69. package/dist/tools/setup.d.ts +0 -3
  70. package/dist/tools/setup.d.ts.map +0 -1
  71. package/dist/tools/setup.js +0 -248
  72. package/dist/tools/setup.js.map +0 -1
  73. package/dist/tools/storage.d.ts +0 -3
  74. package/dist/tools/storage.d.ts.map +0 -1
  75. package/dist/tools/storage.js +0 -53
  76. package/dist/tools/storage.js.map +0 -1
  77. package/dist/types.d.ts +0 -62
  78. package/dist/types.d.ts.map +0 -1
  79. package/dist/types.js +0 -2
  80. package/dist/types.js.map +0 -1
  81. package/dist/utils/logger.d.ts +0 -50
  82. package/dist/utils/logger.d.ts.map +0 -1
  83. package/dist/utils/logger.js +0 -120
  84. package/dist/utils/logger.js.map +0 -1
  85. package/dist/utils/telemetry.d.ts +0 -73
  86. package/dist/utils/telemetry.d.ts.map +0 -1
  87. package/dist/utils/telemetry.js +0 -288
  88. package/dist/utils/telemetry.js.map +0 -1
  89. package/dist/utils/tool-wrapper.d.ts +0 -12
  90. package/dist/utils/tool-wrapper.d.ts.map +0 -1
  91. package/dist/utils/tool-wrapper.js +0 -100
  92. package/dist/utils/tool-wrapper.js.map +0 -1
@@ -1,2545 +0,0 @@
1
- import express from 'express';
2
- import http from 'http';
3
- import { WebSocketServer } from 'ws';
4
- import open from 'open';
5
- import path from 'path';
6
- import { fileURLToPath } from 'url';
7
- import { debug, info, warn, error, getLogs, getLoggerStatus, clearLogs } from './utils/logger.js';
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = path.dirname(__filename);
10
- export class InteractiveServer {
11
- app;
12
- server;
13
- wss;
14
- port = 0;
15
- isRunning = false;
16
- currentResolver = null;
17
- sessionData = new Map();
18
- // 固定端口配置
19
- DEFAULT_PORT = 3721;
20
- FALLBACK_PORTS = [3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, 3732, 3733, 3734, 3735];
21
- constructor() {
22
- this.app = express();
23
- this.server = http.createServer(this.app);
24
- this.wss = new WebSocketServer({ server: this.server });
25
- this.setupExpress();
26
- this.setupWebSocket();
27
- // 确保进程退出时清理资源
28
- process.on('exit', () => this.cleanup());
29
- process.on('SIGINT', () => this.cleanup());
30
- process.on('SIGTERM', () => this.cleanup());
31
- }
32
- cleanup() {
33
- if (this.isRunning) {
34
- debug('Cleaning up interactive server resources...');
35
- this.server.close();
36
- this.wss.close();
37
- this.isRunning = false;
38
- }
39
- }
40
- setupExpress() {
41
- this.app.use(express.json());
42
- this.app.use(express.static(path.join(__dirname, '../static')));
43
- // 环境ID收集页面
44
- this.app.get('/env-setup/:sessionId', (req, res) => {
45
- const { sessionId } = req.params;
46
- const sessionData = this.sessionData.get(sessionId);
47
- if (!sessionData) {
48
- res.status(404).send('会话不存在或已过期');
49
- return;
50
- }
51
- res.send(this.getEnvSetupHTML(sessionData.envs));
52
- });
53
- // 需求澄清页面
54
- this.app.get('/clarification/:sessionId', (req, res) => {
55
- const { sessionId } = req.params;
56
- const sessionData = this.sessionData.get(sessionId);
57
- if (!sessionData) {
58
- res.status(404).send('会话不存在或已过期');
59
- return;
60
- }
61
- res.send(this.getClarificationHTML(sessionData.message, sessionData.options));
62
- });
63
- // 日志查看页面
64
- this.app.get('/debug/logs', async (req, res) => {
65
- try {
66
- const logs = await getLogs(1000);
67
- const status = getLoggerStatus();
68
- res.send(this.getLogsHTML(logs, status));
69
- }
70
- catch (err) {
71
- res.status(500).send('获取日志失败');
72
- }
73
- });
74
- // 日志API
75
- this.app.get('/api/logs', async (req, res) => {
76
- try {
77
- const maxLines = parseInt(req.query.maxLines) || 1000;
78
- const logs = await getLogs(maxLines);
79
- const status = getLoggerStatus();
80
- res.json({ logs, status, success: true });
81
- }
82
- catch (err) {
83
- res.status(500).json({ success: false, error: 'Failed to get logs' });
84
- }
85
- });
86
- this.app.post('/api/logs/clear', async (req, res) => {
87
- try {
88
- await clearLogs();
89
- res.json({ success: true });
90
- }
91
- catch (err) {
92
- res.status(500).json({ success: false, error: 'Failed to clear logs' });
93
- }
94
- });
95
- // API接口
96
- this.app.post('/api/submit', (req, res) => {
97
- const { type, data } = req.body;
98
- debug('🔥 === /api/submit 收到请求 ===');
99
- debug('🔥 请求体 req.body:', JSON.stringify(req.body, null, 2));
100
- debug('🔥 解析后 type:', type);
101
- debug('🔥 解析后 data:', data);
102
- debug('🔥 当前 currentResolver 状态:', this.currentResolver ? '有' : '无');
103
- debug('Received submit request', { type, data });
104
- if (this.currentResolver) {
105
- info('Resolving with user data');
106
- debug('🔥 调用 currentResolver,参数:', { type, data });
107
- this.currentResolver({ type, data });
108
- this.currentResolver = null;
109
- debug('🔥 已清空 currentResolver');
110
- }
111
- else {
112
- warn('No resolver waiting for response');
113
- debug('🔥 ❌ 没有等待响应的resolver!');
114
- }
115
- debug('🔥 返回成功响应');
116
- res.json({ success: true });
117
- });
118
- this.app.post('/api/cancel', (req, res) => {
119
- info('Received cancel request');
120
- if (this.currentResolver) {
121
- info('Resolving with cancelled status');
122
- this.currentResolver({ type: 'clarification', data: null, cancelled: true });
123
- this.currentResolver = null;
124
- }
125
- else {
126
- warn('No resolver waiting for cancellation');
127
- }
128
- res.json({ success: true });
129
- });
130
- }
131
- setupWebSocket() {
132
- this.wss.on('connection', (ws) => {
133
- debug('WebSocket client connected');
134
- ws.on('message', (message) => {
135
- try {
136
- const data = JSON.parse(message.toString());
137
- debug('WebSocket message received', data);
138
- if (this.currentResolver) {
139
- this.currentResolver(data);
140
- this.currentResolver = null;
141
- }
142
- }
143
- catch (err) {
144
- error('WebSocket message parsing error', err);
145
- }
146
- });
147
- ws.on('close', () => {
148
- debug('WebSocket client disconnected');
149
- });
150
- });
151
- }
152
- async start() {
153
- if (this.isRunning) {
154
- debug(`Interactive server already running on port ${this.port}`);
155
- return this.port;
156
- }
157
- return new Promise((resolve, reject) => {
158
- info('Starting interactive server...');
159
- const tryPorts = [this.DEFAULT_PORT, ...this.FALLBACK_PORTS];
160
- let currentIndex = 0;
161
- const tryNextPort = () => {
162
- if (currentIndex >= tryPorts.length) {
163
- const err = new Error(`All ${tryPorts.length} ports are in use (${tryPorts.join(', ')}), failed to start server`);
164
- error('Server start failed', err);
165
- reject(err);
166
- return;
167
- }
168
- const portToTry = tryPorts[currentIndex];
169
- currentIndex++;
170
- debug(`Trying to start server on port ${portToTry} (attempt ${currentIndex}/${tryPorts.length})`);
171
- // 清除之前的所有监听器
172
- this.server.removeAllListeners('error');
173
- this.server.removeAllListeners('listening');
174
- // 设置错误处理
175
- const errorHandler = (err) => {
176
- if (err.code === 'EADDRINUSE') {
177
- warn(`Port ${portToTry} is in use, trying next port...`);
178
- // 清理当前尝试
179
- this.server.removeAllListeners('error');
180
- this.server.removeAllListeners('listening');
181
- tryNextPort();
182
- }
183
- else {
184
- error('Server error', err);
185
- reject(err);
186
- }
187
- };
188
- // 设置成功监听处理
189
- const listeningHandler = () => {
190
- const address = this.server.address();
191
- if (address && typeof address === 'object') {
192
- this.port = address.port;
193
- this.isRunning = true;
194
- info(`Interactive server started successfully on http://localhost:${this.port}`);
195
- // 移除临时监听器
196
- this.server.removeListener('error', errorHandler);
197
- this.server.removeListener('listening', listeningHandler);
198
- resolve(this.port);
199
- }
200
- else {
201
- const err = new Error('Failed to get server address');
202
- error('Server start error', err);
203
- reject(err);
204
- }
205
- };
206
- this.server.once('error', errorHandler);
207
- this.server.once('listening', listeningHandler);
208
- try {
209
- this.server.listen(portToTry, '127.0.0.1');
210
- }
211
- catch (err) {
212
- error(`Failed to bind to port ${portToTry}:`, err);
213
- tryNextPort();
214
- }
215
- };
216
- tryNextPort();
217
- });
218
- }
219
- async stop() {
220
- if (!this.isRunning) {
221
- debug('Interactive server is not running, nothing to stop');
222
- return;
223
- }
224
- info('Stopping interactive server...');
225
- return new Promise((resolve, reject) => {
226
- // 设置超时,防止无限等待
227
- const timeout = setTimeout(() => {
228
- warn('Server close timeout, forcing cleanup');
229
- this.isRunning = false;
230
- this.port = 0;
231
- resolve();
232
- }, 30000);
233
- try {
234
- // 首先关闭WebSocket服务器
235
- this.wss.close(() => {
236
- debug('WebSocket server closed');
237
- });
238
- // 然后关闭HTTP服务器
239
- this.server.close((err) => {
240
- clearTimeout(timeout);
241
- if (err) {
242
- error('Error closing server:', err);
243
- reject(err);
244
- }
245
- else {
246
- info('Interactive server stopped successfully');
247
- this.isRunning = false;
248
- this.port = 0;
249
- resolve();
250
- }
251
- });
252
- }
253
- catch (err) {
254
- clearTimeout(timeout);
255
- error('Error stopping server:', err);
256
- this.isRunning = false;
257
- this.port = 0;
258
- reject(err);
259
- }
260
- });
261
- }
262
- async collectEnvId(availableEnvs) {
263
- try {
264
- info('Starting environment ID collection...');
265
- debug(`Available environments: ${availableEnvs.length}`);
266
- const port = await this.start();
267
- const sessionId = Math.random().toString(36).substring(2, 15);
268
- this.sessionData.set(sessionId, { envs: availableEnvs });
269
- debug(`Created session: ${sessionId}`);
270
- setTimeout(() => {
271
- this.sessionData.delete(sessionId);
272
- debug(`Session ${sessionId} expired`);
273
- }, 5 * 60 * 1000);
274
- const url = `http://localhost:${port}/env-setup/${sessionId}`;
275
- info(`Opening browser: ${url}`);
276
- try {
277
- // 使用默认浏览器打开一个新窗口
278
- await open(url, { wait: false });
279
- info('Browser opened successfully');
280
- }
281
- catch (browserError) {
282
- error('Failed to open browser', browserError);
283
- warn(`Please manually open: ${url}`);
284
- }
285
- info('Waiting for user selection...');
286
- return new Promise((resolve) => {
287
- this.currentResolver = resolve;
288
- setTimeout(() => {
289
- if (this.currentResolver === resolve) {
290
- warn('Request timeout, resolving with cancelled');
291
- this.currentResolver = null;
292
- resolve({ type: 'envId', data: null, cancelled: true });
293
- }
294
- }, 10 * 60 * 1000);
295
- });
296
- }
297
- catch (err) {
298
- error('Error in collectEnvId', err);
299
- throw err;
300
- }
301
- }
302
- async clarifyRequest(message, options) {
303
- const port = await this.start();
304
- // 生成会话ID并存储数据
305
- const sessionId = Math.random().toString(36).substring(2, 15);
306
- this.sessionData.set(sessionId, { message, options });
307
- // 设置会话过期时间(5分钟)
308
- setTimeout(() => {
309
- this.sessionData.delete(sessionId);
310
- }, 5 * 60 * 1000);
311
- const url = `http://localhost:${port}/clarification/${sessionId}`;
312
- // 打开浏览器
313
- await open(url);
314
- return new Promise((resolve) => {
315
- this.currentResolver = resolve;
316
- });
317
- }
318
- getEnvSetupHTML(envs) {
319
- return `
320
- <!DOCTYPE html>
321
- <html lang="zh-CN">
322
- <head>
323
- <meta charset="UTF-8">
324
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
325
- <title>CloudBase AI Toolkit - 环境配置</title>
326
- <style>
327
- @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
328
-
329
- * { margin: 0; padding: 0; box-sizing: border-box; }
330
- :root {
331
- --primary-color: #1a1a1a;
332
- --primary-hover: #000000;
333
- --accent-color: #67E9E9;
334
- --accent-hover: #2BCCCC;
335
- --text-primary: #ffffff;
336
- --text-secondary: #a0a0a0;
337
- --border-color: rgba(255, 255, 255, 0.15);
338
- --bg-secondary: rgba(255, 255, 255, 0.08);
339
- --bg-glass: rgba(26, 26, 26, 0.95);
340
- --shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
341
- --font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
342
- --header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
343
- }
344
-
345
- body {
346
- font-family: var(--font-mono);
347
- background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
348
- min-height: 100vh;
349
- display: flex;
350
- align-items: center;
351
- justify-content: center;
352
- padding: 20px;
353
- position: relative;
354
- overflow-x: hidden;
355
- overflow-y: auto;
356
- }
357
-
358
- /* Custom scrollbar styles */
359
- ::-webkit-scrollbar {
360
- width: 8px;
361
- }
362
-
363
- ::-webkit-scrollbar-track {
364
- background: rgba(255, 255, 255, 0.05);
365
- border-radius: 4px;
366
- }
367
-
368
- ::-webkit-scrollbar-thumb {
369
- background: var(--accent-color);
370
- border-radius: 4px;
371
- }
372
-
373
- ::-webkit-scrollbar-thumb:hover {
374
- background: var(--accent-hover);
375
- }
376
-
377
- body::before {
378
- content: '';
379
- position: fixed;
380
- top: 0; left: 0; right: 0; bottom: 0;
381
- background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M 20 0 L 0 0 0 20" fill="none" stroke="rgba(255,255,255,0.02)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>') repeat;
382
- pointer-events: none;
383
- z-index: -1;
384
- }
385
-
386
- body::after {
387
- content: '';
388
- position: fixed;
389
- top: 50%; left: 50%;
390
- width: 500px; height: 500px;
391
- background: radial-gradient(circle, rgba(103, 233, 233, 0.05) 0%, transparent 70%);
392
- transform: translate(-50%, -50%);
393
- pointer-events: none;
394
- z-index: -1;
395
- animation: pulse 8s ease-in-out infinite;
396
- }
397
-
398
- @keyframes pulse {
399
- 0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
400
- 50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
401
- }
402
-
403
- .modal {
404
- background: var(--bg-glass);
405
- backdrop-filter: blur(20px);
406
- border-radius: 20px;
407
- box-shadow: var(--shadow);
408
- border: 2px solid var(--border-color);
409
- width: 100%;
410
- max-width: 520px;
411
- overflow: hidden;
412
- animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
413
- position: relative;
414
- }
415
-
416
- .modal::before {
417
- content: '';
418
- position: absolute;
419
- top: 0; left: 0; right: 0; bottom: 0;
420
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
421
- animation: shimmer 3s infinite;
422
- pointer-events: none;
423
- }
424
-
425
- @keyframes shimmer {
426
- 0% { transform: translateX(-100%); }
427
- 100% { transform: translateX(100%); }
428
- }
429
-
430
- @keyframes modalIn {
431
- from {
432
- opacity: 0;
433
- transform: scale(0.9) translateY(-20px);
434
- }
435
- to {
436
- opacity: 1;
437
- transform: scale(1) translateY(0);
438
- }
439
- }
440
-
441
- .header {
442
- background: var(--header-bg);
443
- color: var(--text-primary);
444
- padding: 24px 28px;
445
- display: flex;
446
- align-items: center;
447
- justify-content: space-between;
448
- position: relative;
449
- overflow: hidden;
450
- }
451
-
452
- .header::before {
453
- content: '';
454
- position: absolute;
455
- top: 0; left: 0; right: 0; bottom: 0;
456
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.03) 50%, transparent 70%);
457
- animation: headerShimmer 4s infinite;
458
- pointer-events: none;
459
- }
460
-
461
- @keyframes headerShimmer {
462
- 0% { transform: translateX(-100%); }
463
- 100% { transform: translateX(100%); }
464
- }
465
-
466
- .header-left {
467
- display: flex;
468
- align-items: center;
469
- gap: 16px;
470
- z-index: 1;
471
- }
472
-
473
- .logo {
474
- width: 32px;
475
- height: 32px;
476
- filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
477
- animation: logoFloat 3s ease-in-out infinite;
478
- }
479
-
480
- @keyframes logoFloat {
481
- 0%, 100% { transform: translateY(0px); }
482
- 50% { transform: translateY(-3px); }
483
- }
484
-
485
- .title {
486
- font-size: 20px;
487
- font-weight: 700;
488
- text-shadow: 0 2px 4px rgba(0,0,0,0.1);
489
- }
490
-
491
- .github-link {
492
- color: var(--text-primary);
493
- text-decoration: none;
494
- display: flex;
495
- align-items: center;
496
- gap: 8px;
497
- font-size: 14px;
498
- background: rgba(255,255,255,0.08);
499
- border: 1px solid rgba(255, 255, 255, 0.12);
500
- backdrop-filter: blur(10px);
501
- padding: 8px 16px;
502
- border-radius: 8px;
503
- font-weight: 500;
504
- z-index: 1;
505
- transition: all 0.3s ease;
506
- }
507
-
508
- .github-link:hover {
509
- background: rgba(255,255,255,0.15);
510
- transform: translateY(-1px);
511
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
512
- }
513
-
514
- .content {
515
- padding: 32px 24px;
516
- position: relative;
517
- }
518
-
519
- .content-title {
520
- font-size: 24px;
521
- font-weight: 700;
522
- color: var(--text-primary);
523
- margin-bottom: 8px;
524
- animation: fadeInUp 0.8s ease-out 0.2s both;
525
- }
526
-
527
- .content-subtitle {
528
- color: var(--text-secondary);
529
- margin-bottom: 24px;
530
- line-height: 1.5;
531
- animation: fadeInUp 0.8s ease-out 0.4s both;
532
- }
533
-
534
- @keyframes fadeInUp {
535
- from {
536
- opacity: 0;
537
- transform: translateY(20px);
538
- }
539
- to {
540
- opacity: 1;
541
- transform: translateY(0);
542
- }
543
- }
544
-
545
- .env-list {
546
- border: 1px solid var(--border-color);
547
- border-radius: 12px;
548
- margin-bottom: 24px;
549
- max-height: 300px;
550
- overflow-y: auto;
551
- overflow-x: hidden;
552
- background: rgba(255, 255, 255, 0.03);
553
- animation: fadeInUp 0.8s ease-out 0.6s both;
554
- }
555
-
556
- .env-item {
557
- padding: 16px 20px;
558
- border-bottom: 1px solid var(--border-color);
559
- cursor: pointer;
560
- transition: all 0.3s ease;
561
- display: flex;
562
- align-items: center;
563
- gap: 14px;
564
- position: relative;
565
- overflow: hidden;
566
- color: var(--text-primary);
567
- }
568
-
569
- .env-item::before {
570
- content: '';
571
- position: absolute;
572
- left: 0; top: 0; bottom: 0;
573
- width: 0;
574
- background: var(--accent-color);
575
- transition: width 0.3s ease;
576
- }
577
-
578
- .env-item:last-child {
579
- border-bottom: none;
580
- }
581
-
582
- .env-item:hover {
583
- background: var(--bg-secondary);
584
- transform: translateX(5px);
585
- }
586
-
587
- .env-item:hover::before {
588
- width: 4px;
589
- }
590
-
591
- .env-item.selected {
592
- background: rgba(103, 233, 233, 0.1);
593
- border-left: 4px solid var(--accent-color);
594
- transform: translateX(5px);
595
- }
596
-
597
- .env-icon {
598
- width: 20px;
599
- height: 20px;
600
- color: var(--accent-color);
601
- flex-shrink: 0;
602
- animation: iconGlow 2s ease-in-out infinite;
603
- }
604
-
605
- @keyframes iconGlow {
606
- 0%, 100% { filter: drop-shadow(0 0 2px rgba(103, 233, 233, 0.3)); }
607
- 50% { filter: drop-shadow(0 0 8px rgba(103, 233, 233, 0.6)); }
608
- }
609
-
610
- .env-info {
611
- flex: 1;
612
- }
613
-
614
- .env-name {
615
- font-weight: 600;
616
- color: var(--text-primary);
617
- margin-bottom: 4px;
618
- }
619
-
620
- .env-alias {
621
- color: var(--text-secondary);
622
- font-size: 14px;
623
- }
624
-
625
- .actions {
626
- display: flex;
627
- gap: 12px;
628
- justify-content: flex-end;
629
- animation: fadeInUp 0.8s ease-out 0.8s both;
630
- }
631
-
632
- .btn {
633
- padding: 12px 20px;
634
- border: none;
635
- border-radius: 8px;
636
- font-size: 14px;
637
- font-weight: 600;
638
- cursor: pointer;
639
- transition: all 0.3s ease;
640
- display: flex;
641
- align-items: center;
642
- gap: 8px;
643
- font-family: var(--font-mono);
644
- position: relative;
645
- overflow: hidden;
646
- }
647
-
648
- .btn::before {
649
- content: '';
650
- position: absolute;
651
- top: 50%; left: 50%;
652
- width: 0; height: 0;
653
- background: rgba(255,255,255,0.2);
654
- border-radius: 50%;
655
- transition: all 0.3s ease;
656
- transform: translate(-50%, -50%);
657
- }
658
-
659
- .btn:hover::before {
660
- width: 100px; height: 100px;
661
- }
662
-
663
- .btn-primary {
664
- background: var(--primary-color);
665
- color: var(--text-primary);
666
- border: 1px solid var(--border-color);
667
- }
668
-
669
- .btn-primary:hover:not(:disabled) {
670
- background: var(--primary-hover);
671
- transform: translateY(-2px);
672
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
673
- }
674
-
675
- .btn-secondary {
676
- background: var(--bg-secondary);
677
- color: var(--text-secondary);
678
- border: 1px solid var(--border-color);
679
- }
680
-
681
- .btn-secondary:hover {
682
- background: rgba(255, 255, 255, 0.15);
683
- color: var(--text-primary);
684
- }
685
-
686
- .btn:disabled {
687
- opacity: 0.5;
688
- cursor: not-allowed;
689
- }
690
-
691
- .loading {
692
- display: none;
693
- align-items: center;
694
- justify-content: center;
695
- gap: 8px;
696
- margin-top: 16px;
697
- color: var(--text-secondary);
698
- font-size: 14px;
699
- }
700
-
701
- .spinner {
702
- width: 16px;
703
- height: 16px;
704
- border: 2px solid var(--border-color);
705
- border-top: 2px solid var(--accent-color);
706
- border-radius: 50%;
707
- animation: spin 1s linear infinite;
708
- }
709
-
710
- @keyframes spin {
711
- 0% { transform: rotate(0deg); }
712
- 100% { transform: rotate(360deg); }
713
- }
714
- </style>
715
- </head>
716
- <body>
717
- <div class="modal">
718
- <div class="header">
719
- <div class="header-left">
720
- <img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
721
- <span class="title">CloudBase AI Toolkit</span>
722
- </div>
723
- <a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" target="_blank" class="github-link">
724
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
725
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
726
- </svg>
727
- GitHub
728
- </a>
729
- </div>
730
-
731
- <div class="content">
732
- <h1 class="content-title">选择云开发环境</h1>
733
- <p class="content-subtitle">请选择您要使用的云开发环境</p>
734
-
735
- <div class="env-list" id="envList">
736
- ${(envs || []).map((env, index) => `
737
- <div class="env-item" onclick="selectEnv('${env.EnvId}', this)" style="animation-delay: ${index * 0.1}s;">
738
- <svg class="env-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
739
- <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
740
- </svg>
741
- <div class="env-info">
742
- <div class="env-name">${env.EnvId}</div>
743
- <div class="env-alias">${env.Alias || '无别名'}</div>
744
- </div>
745
- </div>
746
- `).join('')}
747
- </div>
748
-
749
- <div class="actions">
750
- <button class="btn btn-secondary" onclick="cancel()">
751
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
752
- <path d="M18 6L6 18M6 6l12 12"/>
753
- </svg>
754
- 取消
755
- </button>
756
- <button class="btn btn-primary" id="confirmBtn" onclick="confirm()" disabled>
757
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
758
- <path d="M20 6L9 17l-5-5"/>
759
- </svg>
760
- 确认选择
761
- </button>
762
- </div>
763
-
764
- <div class="loading" id="loading">
765
- <div class="spinner"></div>
766
- <span>正在配置环境...</span>
767
- </div>
768
- </div>
769
- </div>
770
-
771
- <script>
772
- let selectedEnvId = null;
773
-
774
- function selectEnv(envId, element) {
775
- console.log('=== 环境选择事件触发 ===');
776
- console.log('传入的envId:', envId);
777
- console.log('传入的element:', element);
778
- console.log('element类名:', element ? element.className : 'null');
779
-
780
- selectedEnvId = envId;
781
- console.log('设置selectedEnvId为:', selectedEnvId);
782
-
783
- // Remove selected class from all items
784
- const allItems = document.querySelectorAll('.env-item');
785
- console.log('找到的所有环境项数量:', allItems.length);
786
- allItems.forEach(item => {
787
- item.classList.remove('selected');
788
- });
789
-
790
- // Add selected class to current item
791
- if (element) {
792
- element.classList.add('selected');
793
- console.log('✅ 已添加selected样式到当前项');
794
- console.log('当前项的最终类名:', element.className);
795
- } else {
796
- console.error('❌ element为空,无法添加选中样式');
797
- }
798
-
799
- // Enable confirm button
800
- const confirmBtn = document.getElementById('confirmBtn');
801
- if (confirmBtn) {
802
- confirmBtn.disabled = false;
803
- console.log('✅ 确认按钮已启用');
804
- } else {
805
- console.error('❌ 找不到确认按钮');
806
- }
807
- }
808
-
809
- function confirm() {
810
- console.log('=== CONFIRM BUTTON CLICKED ===');
811
- console.log('selectedEnvId:', selectedEnvId);
812
-
813
- if (!selectedEnvId) {
814
- console.error('❌ 没有选择环境ID!');
815
- alert('请先选择一个环境');
816
- return;
817
- }
818
-
819
- console.log('✅ 环境ID验证通过,开始发送请求...');
820
- document.getElementById('loading').style.display = 'flex';
821
- document.getElementById('confirmBtn').disabled = true;
822
-
823
- const requestBody = {
824
- type: 'envId',
825
- data: selectedEnvId
826
- };
827
-
828
- console.log('📤 发送请求体:', JSON.stringify(requestBody, null, 2));
829
-
830
- fetch('/api/submit', {
831
- method: 'POST',
832
- headers: { 'Content-Type': 'application/json' },
833
- body: JSON.stringify(requestBody)
834
- }).then(response => {
835
- console.log('📥 收到响应状态:', response.status);
836
- console.log('📥 响应头:', [...response.headers.entries()]);
837
- return response.json();
838
- }).then(data => {
839
- console.log('📥 响应数据:', data);
840
- if (data.success) {
841
- console.log('✅ 请求成功,即将关闭窗口');
842
- window.close();
843
- } else {
844
- console.error('❌ 请求失败:', data);
845
- alert('选择环境失败: ' + (data.error || '未知错误'));
846
- document.getElementById('loading').style.display = 'none';
847
- document.getElementById('confirmBtn').disabled = false;
848
- }
849
- }).catch(err => {
850
- console.error('❌ 网络请求错误:', err);
851
- alert('网络请求失败: ' + err.message);
852
- document.getElementById('loading').style.display = 'none';
853
- document.getElementById('confirmBtn').disabled = false;
854
- });
855
- }
856
-
857
- function cancel() {
858
- fetch('/api/cancel', {
859
- method: 'POST',
860
- headers: { 'Content-Type': 'application/json' }
861
- }).then(() => {
862
- window.close();
863
- });
864
- }
865
- </script>
866
- </body>
867
- </html>`;
868
- }
869
- getLogsHTML(logs, status) {
870
- return `
871
- <!DOCTYPE html>
872
- <html lang="zh-CN">
873
- <head>
874
- <meta charset="UTF-8">
875
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
876
- <title>CloudBase MCP 调试日志</title>
877
- <style>
878
- @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
879
-
880
- * { margin: 0; padding: 0; box-sizing: border-box; }
881
- :root {
882
- --primary-color: #1a1a1a;
883
- --primary-hover: #000000;
884
- --accent-color: #67E9E9;
885
- --accent-hover: #2BCCCC;
886
- --text-primary: #ffffff;
887
- --text-secondary: #a0a0a0;
888
- --border-color: rgba(255, 255, 255, 0.15);
889
- --bg-secondary: rgba(255, 255, 255, 0.08);
890
- --bg-glass: rgba(26, 26, 26, 0.95);
891
- --shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
892
- --font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
893
- --header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
894
- }
895
-
896
- body {
897
- font-family: var(--font-mono);
898
- background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
899
- min-height: 100vh;
900
- padding: 20px;
901
- position: relative;
902
- overflow-x: hidden;
903
- overflow-y: auto;
904
- }
905
-
906
- /* Custom scrollbar styles */
907
- ::-webkit-scrollbar {
908
- width: 8px;
909
- }
910
-
911
- ::-webkit-scrollbar-track {
912
- background: rgba(255, 255, 255, 0.05);
913
- border-radius: 4px;
914
- }
915
-
916
- ::-webkit-scrollbar-thumb {
917
- background: var(--accent-color);
918
- border-radius: 4px;
919
- }
920
-
921
- ::-webkit-scrollbar-thumb:hover {
922
- background: var(--accent-hover);
923
- }
924
-
925
- body::before {
926
- content: '';
927
- position: fixed;
928
- top: 0; left: 0; right: 0; bottom: 0;
929
- background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M 20 0 L 0 0 0 20" fill="none" stroke="rgba(255,255,255,0.02)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>') repeat;
930
- pointer-events: none;
931
- z-index: -1;
932
- }
933
-
934
- body::after {
935
- content: '';
936
- position: fixed;
937
- top: 50%; left: 50%;
938
- width: 500px; height: 500px;
939
- background: radial-gradient(circle, rgba(103, 233, 233, 0.05) 0%, transparent 70%);
940
- transform: translate(-50%, -50%);
941
- pointer-events: none;
942
- z-index: -1;
943
- animation: pulse 8s ease-in-out infinite;
944
- }
945
-
946
- @keyframes pulse {
947
- 0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
948
- 50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
949
- }
950
-
951
- .container {
952
- background: var(--bg-glass);
953
- backdrop-filter: blur(20px);
954
- border-radius: 20px;
955
- padding: 30px;
956
- box-shadow: var(--shadow);
957
- border: 2px solid var(--border-color);
958
- max-width: 1200px;
959
- margin: 0 auto;
960
- animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
961
- position: relative;
962
- }
963
-
964
- .container::before {
965
- content: '';
966
- position: absolute;
967
- top: 0; left: 0; right: 0; bottom: 0;
968
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
969
- animation: shimmer 3s infinite;
970
- pointer-events: none;
971
- border-radius: 20px;
972
- }
973
-
974
- @keyframes shimmer {
975
- 0% { transform: translateX(-100%); }
976
- 100% { transform: translateX(100%); }
977
- }
978
-
979
- @keyframes modalIn {
980
- from {
981
- opacity: 0;
982
- transform: scale(0.9) translateY(-20px);
983
- }
984
- to {
985
- opacity: 1;
986
- transform: scale(1) translateY(0);
987
- }
988
- }
989
-
990
- .header {
991
- text-align: center;
992
- margin-bottom: 30px;
993
- position: relative;
994
- z-index: 1;
995
- }
996
-
997
- .header-top {
998
- display: flex;
999
- align-items: center;
1000
- justify-content: space-between;
1001
- margin-bottom: 20px;
1002
- }
1003
-
1004
- .header-left {
1005
- display: flex;
1006
- align-items: center;
1007
- gap: 16px;
1008
- }
1009
-
1010
- .logo {
1011
- width: 40px;
1012
- height: 40px;
1013
- filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
1014
- animation: logoFloat 3s ease-in-out infinite;
1015
- }
1016
-
1017
- @keyframes logoFloat {
1018
- 0%, 100% { transform: translateY(0px); }
1019
- 50% { transform: translateY(-3px); }
1020
- }
1021
-
1022
- .github-link {
1023
- color: var(--text-primary);
1024
- text-decoration: none;
1025
- display: flex;
1026
- align-items: center;
1027
- gap: 8px;
1028
- font-size: 14px;
1029
- background: rgba(255,255,255,0.08);
1030
- border: 1px solid rgba(255, 255, 255, 0.12);
1031
- backdrop-filter: blur(10px);
1032
- padding: 8px 16px;
1033
- border-radius: 8px;
1034
- font-weight: 500;
1035
- transition: all 0.3s ease;
1036
- }
1037
-
1038
- .github-link:hover {
1039
- background: rgba(255,255,255,0.15);
1040
- transform: translateY(-1px);
1041
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1042
- }
1043
-
1044
- h1 {
1045
- color: var(--text-primary);
1046
- margin-bottom: 10px;
1047
- font-size: 28px;
1048
- font-weight: 700;
1049
- animation: fadeInUp 0.8s ease-out 0.2s both;
1050
- }
1051
-
1052
- .subtitle {
1053
- color: var(--text-secondary);
1054
- font-size: 16px;
1055
- animation: fadeInUp 0.8s ease-out 0.4s both;
1056
- }
1057
-
1058
- @keyframes fadeInUp {
1059
- from {
1060
- opacity: 0;
1061
- transform: translateY(20px);
1062
- }
1063
- to {
1064
- opacity: 1;
1065
- transform: translateY(0);
1066
- }
1067
- }
1068
-
1069
- .status {
1070
- background: rgba(255, 255, 255, 0.05);
1071
- border: 1px solid var(--border-color);
1072
- border-radius: 12px;
1073
- padding: 20px;
1074
- margin-bottom: 20px;
1075
- display: grid;
1076
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
1077
- gap: 15px;
1078
- animation: fadeInUp 0.8s ease-out 0.6s both;
1079
- }
1080
-
1081
- .status-item {
1082
- display: flex;
1083
- justify-content: space-between;
1084
- align-items: center;
1085
- }
1086
-
1087
- .status-label {
1088
- font-weight: 600;
1089
- color: var(--text-secondary);
1090
- }
1091
-
1092
- .status-value {
1093
- color: var(--text-primary);
1094
- font-family: var(--font-mono);
1095
- font-size: 14px;
1096
- }
1097
-
1098
- .enabled {
1099
- color: var(--accent-color);
1100
- }
1101
-
1102
- .disabled {
1103
- color: #ff6b6b;
1104
- }
1105
-
1106
- .controls {
1107
- display: flex;
1108
- gap: 15px;
1109
- margin-bottom: 20px;
1110
- justify-content: space-between;
1111
- align-items: center;
1112
- flex-wrap: wrap;
1113
- animation: fadeInUp 0.8s ease-out 0.8s both;
1114
- }
1115
-
1116
- .controls-left {
1117
- display: flex;
1118
- gap: 15px;
1119
- }
1120
-
1121
- .btn {
1122
- padding: 10px 20px;
1123
- border: none;
1124
- border-radius: 8px;
1125
- font-size: 14px;
1126
- font-weight: 600;
1127
- cursor: pointer;
1128
- transition: all 0.3s ease;
1129
- font-family: var(--font-mono);
1130
- position: relative;
1131
- overflow: hidden;
1132
- }
1133
-
1134
- .btn::before {
1135
- content: '';
1136
- position: absolute;
1137
- top: 50%; left: 50%;
1138
- width: 0; height: 0;
1139
- background: rgba(255,255,255,0.2);
1140
- border-radius: 50%;
1141
- transition: all 0.3s ease;
1142
- transform: translate(-50%, -50%);
1143
- }
1144
-
1145
- .btn:hover::before {
1146
- width: 100px; height: 100px;
1147
- }
1148
-
1149
- .btn-primary {
1150
- background: var(--accent-color);
1151
- color: var(--primary-color);
1152
- }
1153
-
1154
- .btn-primary:hover {
1155
- background: var(--accent-hover);
1156
- transform: translateY(-2px);
1157
- box-shadow: 0 8px 25px rgba(103, 233, 233, 0.3);
1158
- }
1159
-
1160
- .btn-danger {
1161
- background: #ff6b6b;
1162
- color: white;
1163
- }
1164
-
1165
- .btn-danger:hover {
1166
- background: #ff5252;
1167
- transform: translateY(-2px);
1168
- box-shadow: 0 8px 25px rgba(255, 107, 107, 0.3);
1169
- }
1170
-
1171
- .btn-secondary {
1172
- background: var(--bg-secondary);
1173
- color: var(--text-secondary);
1174
- border: 1px solid var(--border-color);
1175
- }
1176
-
1177
- .btn-secondary:hover {
1178
- background: rgba(255, 255, 255, 0.15);
1179
- color: var(--text-primary);
1180
- }
1181
-
1182
- .log-container {
1183
- background: rgba(0, 0, 0, 0.5);
1184
- border: 1px solid var(--border-color);
1185
- border-radius: 12px;
1186
- padding: 20px;
1187
- height: 500px;
1188
- overflow-y: auto;
1189
- font-family: var(--font-mono);
1190
- font-size: 13px;
1191
- line-height: 1.4;
1192
- animation: fadeInUp 0.8s ease-out 1s both;
1193
- }
1194
-
1195
- .log-container::-webkit-scrollbar {
1196
- width: 8px;
1197
- }
1198
-
1199
- .log-container::-webkit-scrollbar-track {
1200
- background: rgba(255, 255, 255, 0.1);
1201
- border-radius: 4px;
1202
- }
1203
-
1204
- .log-container::-webkit-scrollbar-thumb {
1205
- background: var(--accent-color);
1206
- border-radius: 4px;
1207
- }
1208
-
1209
- .log-container::-webkit-scrollbar-thumb:hover {
1210
- background: var(--accent-hover);
1211
- }
1212
-
1213
- .log-line {
1214
- color: var(--text-primary);
1215
- margin-bottom: 2px;
1216
- word-break: break-all;
1217
- animation: logSlideIn 0.3s ease-out;
1218
- }
1219
-
1220
- @keyframes logSlideIn {
1221
- from {
1222
- opacity: 0;
1223
- transform: translateX(-10px);
1224
- }
1225
- to {
1226
- opacity: 1;
1227
- transform: translateX(0);
1228
- }
1229
- }
1230
-
1231
- .log-line.debug {
1232
- color: var(--text-secondary);
1233
- }
1234
-
1235
- .log-line.info {
1236
- color: #74c0fc;
1237
- }
1238
-
1239
- .log-line.warn {
1240
- color: #ffd43b;
1241
- }
1242
-
1243
- .log-line.error {
1244
- color: #ff8787;
1245
- }
1246
-
1247
- .timestamp {
1248
- color: var(--text-secondary);
1249
- }
1250
-
1251
- .level {
1252
- font-weight: bold;
1253
- margin: 0 8px;
1254
- }
1255
-
1256
- .empty-state {
1257
- text-align: center;
1258
- color: var(--text-secondary);
1259
- padding: 40px;
1260
- font-style: italic;
1261
- }
1262
-
1263
- .log-count {
1264
- color: var(--text-secondary);
1265
- font-size: 14px;
1266
- }
1267
- </style>
1268
- </head>
1269
- <body>
1270
- <div class="container">
1271
- <div class="header">
1272
- <div class="header-top">
1273
- <div class="header-left">
1274
- <img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
1275
- <div style="text-align: left;">
1276
- <h1>CloudBase MCP 调试日志</h1>
1277
- <p class="subtitle">实时查看 MCP 服务器运行日志</p>
1278
- </div>
1279
- </div>
1280
- <a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" target="_blank" class="github-link">
1281
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
1282
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
1283
- </svg>
1284
- GitHub
1285
- </a>
1286
- </div>
1287
- </div>
1288
-
1289
- <div class="status">
1290
- <div class="status-item">
1291
- <span class="status-label">日志状态:</span>
1292
- <span class="status-value ${status.enabled ? 'enabled' : 'disabled'}">
1293
- ${status.enabled ? '🟢 启用' : '🔴 禁用'}
1294
- </span>
1295
- </div>
1296
- <div class="status-item">
1297
- <span class="status-label">日志级别:</span>
1298
- <span class="status-value">${status.level}</span>
1299
- </div>
1300
- <div class="status-item">
1301
- <span class="status-label">日志文件:</span>
1302
- <span class="status-value">${status.logFile || '无'}</span>
1303
- </div>
1304
- <div class="status-item">
1305
- <span class="status-label">控制台输出:</span>
1306
- <span class="status-value ${status.useConsole ? 'enabled' : 'disabled'}">
1307
- ${status.useConsole ? '🟢 启用' : '🔴 禁用'}
1308
- </span>
1309
- </div>
1310
- </div>
1311
-
1312
- <div class="controls">
1313
- <div class="controls-left">
1314
- <button class="btn btn-primary" onclick="refreshLogs()">
1315
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
1316
- <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
1317
- <path d="M21 3v5h-5"/>
1318
- <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/>
1319
- <path d="M8 16H3v5"/>
1320
- </svg>
1321
- 刷新日志
1322
- </button>
1323
- <button class="btn btn-danger" onclick="clearLogs()">
1324
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
1325
- <path d="M3 6h18"/>
1326
- <path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/>
1327
- <path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>
1328
- </svg>
1329
- 清空日志
1330
- </button>
1331
- <button class="btn btn-secondary" onclick="window.close()">
1332
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 6px;">
1333
- <path d="M18 6L6 18M6 6l12 12"/>
1334
- </svg>
1335
- 关闭
1336
- </button>
1337
- </div>
1338
- <div>
1339
- <span class="log-count">📊 共 ${logs.length} 条日志</span>
1340
- </div>
1341
- </div>
1342
-
1343
- <div class="log-container" id="logContainer">
1344
- ${logs.length > 0 ? logs.map(line => {
1345
- const match = line.match(/\[(.*?)\] \[(.*?)\] (.*)/);
1346
- if (match) {
1347
- const [, timestamp, level, message] = match;
1348
- const levelClass = level.toLowerCase();
1349
- return `<div class="log-line ${levelClass}"><span class="timestamp">[${timestamp}]</span><span class="level ${levelClass}">[${level}]</span>${message}</div>`;
1350
- }
1351
- return `<div class="log-line">${line}</div>`;
1352
- }).join('') : '<div class="empty-state">📝 暂无日志记录</div>'}
1353
- </div>
1354
- </div>
1355
-
1356
- <script>
1357
- function refreshLogs() {
1358
- fetch('/api/logs')
1359
- .then(response => response.json())
1360
- .then(data => {
1361
- if (data.success) {
1362
- location.reload();
1363
- }
1364
- })
1365
- .catch(error => {
1366
- alert('刷新日志失败: ' + error.message);
1367
- });
1368
- }
1369
-
1370
- function clearLogs() {
1371
- if (confirm('确定要清空所有日志吗?此操作不可恢复。')) {
1372
- fetch('/api/logs/clear', { method: 'POST' })
1373
- .then(response => response.json())
1374
- .then(data => {
1375
- if (data.success) {
1376
- location.reload();
1377
- } else {
1378
- alert('清空日志失败');
1379
- }
1380
- })
1381
- .catch(error => {
1382
- alert('清空日志失败: ' + error.message);
1383
- });
1384
- }
1385
- }
1386
-
1387
- // 自动滚动到底部
1388
- const logContainer = document.getElementById('logContainer');
1389
- logContainer.scrollTop = logContainer.scrollHeight;
1390
-
1391
- // 每5秒自动刷新
1392
- setInterval(() => {
1393
- const isAtBottom = logContainer.scrollHeight - logContainer.clientHeight <= logContainer.scrollTop + 1;
1394
-
1395
- fetch('/api/logs')
1396
- .then(response => response.json())
1397
- .then(data => {
1398
- if (data.success && data.logs.length > 0) {
1399
- const newContent = data.logs.map(line => {
1400
- const match = line.match(/\\[(.*?)\\] \\[(.*?)\\] (.*)/);
1401
- if (match) {
1402
- const [, timestamp, level, message] = match;
1403
- const levelClass = level.toLowerCase();
1404
- return \`<div class="log-line \${levelClass}"><span class="timestamp">[\${timestamp}]</span><span class="level \${levelClass}">[\${level}]</span>\${message}</div>\`;
1405
- }
1406
- return \`<div class="log-line">\${line}</div>\`;
1407
- }).join('');
1408
-
1409
- logContainer.innerHTML = newContent || '<div class="empty-state">📝 暂无日志记录</div>';
1410
-
1411
- if (isAtBottom) {
1412
- logContainer.scrollTop = logContainer.scrollHeight;
1413
- }
1414
- }
1415
- })
1416
- .catch(error => {
1417
- console.error('获取日志失败:', error);
1418
- });
1419
- }, 5000);
1420
- </script>
1421
- </body>
1422
- </html>`;
1423
- }
1424
- getClarificationHTML(message, options) {
1425
- const optionsArray = options || null;
1426
- return `
1427
- <!DOCTYPE html>
1428
- <html lang="zh-CN">
1429
- <head>
1430
- <meta charset="UTF-8">
1431
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1432
- <title>CloudBase AI Toolkit - 需求澄清</title>
1433
- <style>
1434
- @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
1435
-
1436
- * { margin: 0; padding: 0; box-sizing: border-box; }
1437
- :root {
1438
- --primary-color: #1a1a1a;
1439
- --primary-hover: #000000;
1440
- --accent-color: #67E9E9;
1441
- --accent-hover: #2BCCCC;
1442
- --text-primary: #ffffff;
1443
- --text-secondary: #a0a0a0;
1444
- --border-color: rgba(255, 255, 255, 0.15);
1445
- --bg-secondary: rgba(255, 255, 255, 0.08);
1446
- --bg-glass: rgba(26, 26, 26, 0.95);
1447
- --shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
1448
- --font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
1449
- --header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
1450
- }
1451
-
1452
- body {
1453
- font-family: var(--font-mono);
1454
- background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
1455
- min-height: 100vh;
1456
- display: flex;
1457
- align-items: center;
1458
- justify-content: center;
1459
- padding: 20px;
1460
- position: relative;
1461
- overflow-x: hidden;
1462
- overflow-y: auto;
1463
- }
1464
-
1465
- /* Custom scrollbar styles */
1466
- ::-webkit-scrollbar {
1467
- width: 8px;
1468
- }
1469
-
1470
- ::-webkit-scrollbar-track {
1471
- background: rgba(255, 255, 255, 0.05);
1472
- border-radius: 4px;
1473
- }
1474
-
1475
- ::-webkit-scrollbar-thumb {
1476
- background: var(--accent-color);
1477
- border-radius: 4px;
1478
- }
1479
-
1480
- ::-webkit-scrollbar-thumb:hover {
1481
- background: var(--accent-hover);
1482
- }
1483
-
1484
- body::before {
1485
- content: '';
1486
- position: fixed;
1487
- top: 0; left: 0; right: 0; bottom: 0;
1488
- background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M 20 0 L 0 0 0 20" fill="none" stroke="rgba(255,255,255,0.02)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>') repeat;
1489
- pointer-events: none;
1490
- z-index: -1;
1491
- }
1492
-
1493
- body::after {
1494
- content: '';
1495
- position: fixed;
1496
- top: 50%; left: 50%;
1497
- width: 500px; height: 500px;
1498
- background: radial-gradient(circle, rgba(103, 233, 233, 0.05) 0%, transparent 70%);
1499
- transform: translate(-50%, -50%);
1500
- pointer-events: none;
1501
- z-index: -1;
1502
- animation: pulse 8s ease-in-out infinite;
1503
- }
1504
-
1505
- @keyframes pulse {
1506
- 0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
1507
- 50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
1508
- }
1509
-
1510
- .modal {
1511
- background: var(--bg-glass);
1512
- backdrop-filter: blur(20px);
1513
- border-radius: 20px;
1514
- box-shadow: var(--shadow);
1515
- border: 2px solid var(--border-color);
1516
- width: 100%;
1517
- max-width: 600px;
1518
- overflow: hidden;
1519
- animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
1520
- position: relative;
1521
- }
1522
-
1523
- .modal::before {
1524
- content: '';
1525
- position: absolute;
1526
- top: 0; left: 0; right: 0; bottom: 0;
1527
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
1528
- animation: shimmer 3s infinite;
1529
- pointer-events: none;
1530
- }
1531
-
1532
- @keyframes shimmer {
1533
- 0% { transform: translateX(-100%); }
1534
- 100% { transform: translateX(100%); }
1535
- }
1536
-
1537
- @keyframes modalIn {
1538
- from {
1539
- opacity: 0;
1540
- transform: scale(0.9) translateY(-20px);
1541
- }
1542
- to {
1543
- opacity: 1;
1544
- transform: scale(1) translateY(0);
1545
- }
1546
- }
1547
-
1548
- .header {
1549
- background: var(--header-bg);
1550
- color: var(--text-primary);
1551
- padding: 24px 28px;
1552
- display: flex;
1553
- align-items: center;
1554
- justify-content: space-between;
1555
- position: relative;
1556
- overflow: hidden;
1557
- }
1558
-
1559
- .header::before {
1560
- content: '';
1561
- position: absolute;
1562
- top: 0; left: 0; right: 0; bottom: 0;
1563
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.03) 50%, transparent 70%);
1564
- animation: headerShimmer 4s infinite;
1565
- pointer-events: none;
1566
- }
1567
-
1568
- @keyframes headerShimmer {
1569
- 0% { transform: translateX(-100%); }
1570
- 100% { transform: translateX(100%); }
1571
- }
1572
-
1573
- .header-left {
1574
- display: flex;
1575
- align-items: center;
1576
- gap: 16px;
1577
- z-index: 1;
1578
- }
1579
-
1580
- .logo {
1581
- width: 32px;
1582
- height: 32px;
1583
- filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
1584
- animation: logoFloat 3s ease-in-out infinite;
1585
- }
1586
-
1587
- @keyframes logoFloat {
1588
- 0%, 100% { transform: translateY(0px); }
1589
- 50% { transform: translateY(-3px); }
1590
- }
1591
-
1592
- .title {
1593
- font-size: 20px;
1594
- font-weight: 700;
1595
- text-shadow: 0 2px 4px rgba(0,0,0,0.1);
1596
- }
1597
-
1598
- .github-link {
1599
- color: var(--text-primary);
1600
- text-decoration: none;
1601
- display: flex;
1602
- align-items: center;
1603
- gap: 8px;
1604
- font-size: 14px;
1605
- background: rgba(255,255,255,0.08);
1606
- border: 1px solid rgba(255, 255, 255, 0.12);
1607
- backdrop-filter: blur(10px);
1608
- padding: 8px 16px;
1609
- border-radius: 8px;
1610
- font-weight: 500;
1611
- z-index: 1;
1612
- transition: all 0.3s ease;
1613
- }
1614
-
1615
- .github-link:hover {
1616
- background: rgba(255,255,255,0.15);
1617
- transform: translateY(-1px);
1618
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
1619
- }
1620
-
1621
- .content {
1622
- padding: 32px 24px;
1623
- position: relative;
1624
- }
1625
-
1626
- .content-title {
1627
- font-size: 24px;
1628
- font-weight: 700;
1629
- color: var(--text-primary);
1630
- margin-bottom: 8px;
1631
- animation: fadeInUp 0.8s ease-out 0.2s both;
1632
- }
1633
-
1634
- @keyframes fadeInUp {
1635
- from {
1636
- opacity: 0;
1637
- transform: translateY(20px);
1638
- }
1639
- to {
1640
- opacity: 1;
1641
- transform: translateY(0);
1642
- }
1643
- }
1644
-
1645
- .message {
1646
- background: rgba(103, 233, 233, 0.1);
1647
- border: 1px solid var(--accent-color);
1648
- border-left: 4px solid var(--accent-color);
1649
- padding: 20px;
1650
- border-radius: 12px;
1651
- margin-bottom: 24px;
1652
- font-size: 15px;
1653
- line-height: 1.6;
1654
- color: var(--text-primary);
1655
- animation: fadeInUp 0.8s ease-out 0.4s both;
1656
- position: relative;
1657
- overflow: hidden;
1658
- }
1659
-
1660
- .message::before {
1661
- content: '';
1662
- position: absolute;
1663
- top: 0; left: 0;
1664
- width: 100%; height: 2px;
1665
- background: linear-gradient(90deg, var(--accent-color), transparent);
1666
- animation: progress 2s ease-out;
1667
- }
1668
-
1669
- @keyframes progress {
1670
- from { width: 0%; }
1671
- to { width: 100%; }
1672
- }
1673
-
1674
- .options {
1675
- margin-bottom: 24px;
1676
- animation: fadeInUp 0.8s ease-out 0.6s both;
1677
- }
1678
-
1679
- .option-item {
1680
- padding: 16px 20px;
1681
- border: 1px solid var(--border-color);
1682
- border-radius: 12px;
1683
- margin-bottom: 12px;
1684
- cursor: pointer;
1685
- transition: all 0.3s ease;
1686
- display: flex;
1687
- align-items: center;
1688
- gap: 14px;
1689
- background: rgba(255, 255, 255, 0.03);
1690
- position: relative;
1691
- overflow: hidden;
1692
- color: var(--text-primary);
1693
- }
1694
-
1695
- .option-item::before {
1696
- content: '';
1697
- position: absolute;
1698
- left: 0; top: 0; bottom: 0;
1699
- width: 0;
1700
- background: var(--accent-color);
1701
- transition: width 0.3s ease;
1702
- }
1703
-
1704
- .option-item:hover {
1705
- background: var(--bg-secondary);
1706
- border-color: var(--accent-color);
1707
- transform: translateX(5px);
1708
- }
1709
-
1710
- .option-item:hover::before {
1711
- width: 4px;
1712
- }
1713
-
1714
- .option-item.selected {
1715
- background: rgba(103, 233, 233, 0.1);
1716
- border-color: var(--accent-color);
1717
- transform: translateX(5px);
1718
- }
1719
-
1720
- .option-item.selected::before {
1721
- width: 4px;
1722
- }
1723
-
1724
- .option-icon {
1725
- width: 20px;
1726
- height: 20px;
1727
- color: var(--accent-color);
1728
- flex-shrink: 0;
1729
- animation: iconGlow 2s ease-in-out infinite;
1730
- }
1731
-
1732
- @keyframes iconGlow {
1733
- 0%, 100% { filter: drop-shadow(0 0 2px rgba(103, 233, 233, 0.3)); }
1734
- 50% { filter: drop-shadow(0 0 8px rgba(103, 233, 233, 0.6)); }
1735
- }
1736
-
1737
- .custom-input {
1738
- margin-bottom: 24px;
1739
- animation: fadeInUp 0.8s ease-out 0.8s both;
1740
- }
1741
-
1742
- .custom-input textarea {
1743
- width: 100%;
1744
- min-height: 120px;
1745
- padding: 16px;
1746
- border: 1px solid var(--border-color);
1747
- border-radius: 12px;
1748
- font-size: 15px;
1749
- font-family: var(--font-mono);
1750
- resize: vertical;
1751
- transition: all 0.3s ease;
1752
- line-height: 1.5;
1753
- background: rgba(255, 255, 255, 0.03);
1754
- color: var(--text-primary);
1755
- }
1756
-
1757
- .custom-input textarea::placeholder {
1758
- color: var(--text-secondary);
1759
- }
1760
-
1761
- .custom-input textarea:focus {
1762
- outline: none;
1763
- border-color: var(--accent-color);
1764
- box-shadow: 0 0 0 3px rgba(103, 233, 233, 0.1);
1765
- background: rgba(255, 255, 255, 0.05);
1766
- }
1767
-
1768
- .actions {
1769
- display: flex;
1770
- gap: 12px;
1771
- justify-content: flex-end;
1772
- animation: fadeInUp 0.8s ease-out 1s both;
1773
- }
1774
-
1775
- .btn {
1776
- padding: 12px 20px;
1777
- border: none;
1778
- border-radius: 8px;
1779
- font-size: 14px;
1780
- font-weight: 600;
1781
- cursor: pointer;
1782
- transition: all 0.3s ease;
1783
- display: flex;
1784
- align-items: center;
1785
- gap: 8px;
1786
- font-family: var(--font-mono);
1787
- position: relative;
1788
- overflow: hidden;
1789
- }
1790
-
1791
- .btn::before {
1792
- content: '';
1793
- position: absolute;
1794
- top: 50%; left: 50%;
1795
- width: 0; height: 0;
1796
- background: rgba(255,255,255,0.2);
1797
- border-radius: 50%;
1798
- transition: all 0.3s ease;
1799
- transform: translate(-50%, -50%);
1800
- }
1801
-
1802
- .btn:hover::before {
1803
- width: 100px; height: 100px;
1804
- }
1805
-
1806
- .btn-primary {
1807
- background: var(--primary-color);
1808
- color: var(--text-primary);
1809
- border: 1px solid var(--border-color);
1810
- }
1811
-
1812
- .btn-primary:hover:not(:disabled) {
1813
- background: var(--primary-hover);
1814
- transform: translateY(-2px);
1815
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
1816
- }
1817
-
1818
- .btn-secondary {
1819
- background: var(--bg-secondary);
1820
- color: var(--text-secondary);
1821
- border: 1px solid var(--border-color);
1822
- }
1823
-
1824
- .btn-secondary:hover {
1825
- background: rgba(255, 255, 255, 0.15);
1826
- color: var(--text-primary);
1827
- }
1828
-
1829
- .btn:disabled {
1830
- opacity: 0.5;
1831
- cursor: not-allowed;
1832
- }
1833
-
1834
- .loading {
1835
- display: none;
1836
- align-items: center;
1837
- justify-content: center;
1838
- gap: 8px;
1839
- margin-top: 16px;
1840
- color: var(--text-secondary);
1841
- font-size: 14px;
1842
- }
1843
-
1844
- .spinner {
1845
- width: 16px;
1846
- height: 16px;
1847
- border: 2px solid var(--border-color);
1848
- border-top: 2px solid var(--accent-color);
1849
- border-radius: 50%;
1850
- animation: spin 1s linear infinite;
1851
- }
1852
-
1853
- @keyframes spin {
1854
- 0% { transform: rotate(0deg); }
1855
- 100% { transform: rotate(360deg); }
1856
- }
1857
- </style>
1858
- </head>
1859
- <body>
1860
- <div class="modal">
1861
- <div class="header">
1862
- <div class="header-left">
1863
- <img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
1864
- <span class="title">CloudBase AI Toolkit</span>
1865
- </div>
1866
- <a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" target="_blank" class="github-link">
1867
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
1868
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
1869
- </svg>
1870
- GitHub
1871
- </a>
1872
- </div>
1873
-
1874
- <div class="content">
1875
- <h1 class="content-title">需求澄清</h1>
1876
- <div class="message">${message}</div>
1877
-
1878
- ${optionsArray ? `
1879
- <div class="options" id="options">
1880
- ${optionsArray.map((option, index) => `
1881
- <div class="option-item" onclick="selectOption('${option}')" style="animation-delay: ${index * 0.1}s;">
1882
- <svg class="option-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1883
- <path d="M21 11.5a8.38 8.38 0 01-.9 3.8 8.5 8.5 0 01-7.6 4.7 8.38 8.38 0 01-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 01-.9-3.8 8.5 8.5 0 014.7-7.6 8.38 8.38 0 013.8-.9h.5a8.48 8.48 0 018 8v.5z"/>
1884
- </svg>
1885
- <span>${option}</span>
1886
- </div>
1887
- `).join('')}
1888
- </div>
1889
- ` : ''}
1890
-
1891
- <div class="custom-input">
1892
- <textarea id="customInput" placeholder="请输入您的具体需求或建议..." onkeyup="updateSubmitButton()"></textarea>
1893
- </div>
1894
-
1895
- <div class="actions">
1896
- <button class="btn btn-secondary" onclick="cancel()">
1897
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1898
- <path d="M18 6L6 18M6 6l12 12"/>
1899
- </svg>
1900
- 取消
1901
- </button>
1902
- <button class="btn btn-primary" id="submitBtn" onclick="submit()">
1903
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1904
- <path d="M20 6L9 17l-5-5"/>
1905
- </svg>
1906
- 提交反馈
1907
- </button>
1908
- </div>
1909
-
1910
- <div class="loading" id="loading">
1911
- <div class="spinner"></div>
1912
- <span>正在提交...</span>
1913
- </div>
1914
- </div>
1915
- </div>
1916
-
1917
- <script>
1918
- let selectedOption = null;
1919
-
1920
- function selectOption(option) {
1921
- selectedOption = option;
1922
-
1923
- document.querySelectorAll('.option-item').forEach(item => {
1924
- item.classList.remove('selected');
1925
- });
1926
- event.currentTarget.classList.add('selected');
1927
-
1928
- updateSubmitButton();
1929
- }
1930
-
1931
- function updateSubmitButton() {
1932
- const customInput = document.getElementById('customInput').value.trim();
1933
- const submitBtn = document.getElementById('submitBtn');
1934
-
1935
- if (selectedOption || customInput) {
1936
- submitBtn.disabled = false;
1937
- submitBtn.style.opacity = '1';
1938
- } else {
1939
- submitBtn.disabled = true;
1940
- submitBtn.style.opacity = '0.5';
1941
- }
1942
- }
1943
-
1944
- function submit() {
1945
- const customInput = document.getElementById('customInput').value.trim();
1946
- const data = selectedOption || customInput;
1947
-
1948
- if (!data) return;
1949
-
1950
- document.getElementById('loading').style.display = 'flex';
1951
- document.getElementById('submitBtn').disabled = true;
1952
-
1953
- fetch('/api/submit', {
1954
- method: 'POST',
1955
- headers: { 'Content-Type': 'application/json' },
1956
- body: JSON.stringify({
1957
- type: 'clarification',
1958
- data: data
1959
- })
1960
- }).then(response => response.json())
1961
- .then(result => {
1962
- if (result.success) {
1963
- window.close();
1964
- }
1965
- }).catch(err => {
1966
- console.error('Error:', err);
1967
- document.getElementById('loading').style.display = 'none';
1968
- document.getElementById('submitBtn').disabled = false;
1969
- });
1970
- }
1971
-
1972
- function cancel() {
1973
- fetch('/api/cancel', {
1974
- method: 'POST',
1975
- headers: { 'Content-Type': 'application/json' }
1976
- }).then(() => {
1977
- window.close();
1978
- });
1979
- }
1980
-
1981
- // Initialize
1982
- updateSubmitButton();
1983
- </script>
1984
- </body>
1985
- </html>`;
1986
- }
1987
- getConfirmationHTML(message, risks, options) {
1988
- const availableOptions = options || ['确认执行', '取消操作'];
1989
- return `
1990
- <!DOCTYPE html>
1991
- <html lang="zh-CN">
1992
- <head>
1993
- <meta charset="UTF-8">
1994
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1995
- <title>CloudBase AI Toolkit - 操作确认</title>
1996
- <style>
1997
- @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
1998
-
1999
- * { margin: 0; padding: 0; box-sizing: border-box; }
2000
- :root {
2001
- --primary-color: #1a1a1a;
2002
- --primary-hover: #000000;
2003
- --accent-color: #67E9E9;
2004
- --accent-hover: #2BCCCC;
2005
- --text-primary: #ffffff;
2006
- --text-secondary: #a0a0a0;
2007
- --border-color: rgba(255, 255, 255, 0.15);
2008
- --bg-secondary: rgba(255, 255, 255, 0.08);
2009
- --bg-glass: rgba(26, 26, 26, 0.95);
2010
- --warning-color: #ff6b6b;
2011
- --warning-bg: rgba(255, 107, 107, 0.1);
2012
- --warning-border: rgba(255, 107, 107, 0.3);
2013
- --shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
2014
- --font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
2015
- --header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
2016
- }
2017
-
2018
- body {
2019
- font-family: var(--font-mono);
2020
- background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
2021
- min-height: 100vh;
2022
- display: flex;
2023
- align-items: center;
2024
- justify-content: center;
2025
- padding: 20px;
2026
- position: relative;
2027
- overflow-x: hidden;
2028
- overflow-y: auto;
2029
- }
2030
-
2031
- /* Custom scrollbar styles */
2032
- ::-webkit-scrollbar {
2033
- width: 8px;
2034
- }
2035
-
2036
- ::-webkit-scrollbar-track {
2037
- background: rgba(255, 255, 255, 0.05);
2038
- border-radius: 4px;
2039
- }
2040
-
2041
- ::-webkit-scrollbar-thumb {
2042
- background: var(--accent-color);
2043
- border-radius: 4px;
2044
- }
2045
-
2046
- ::-webkit-scrollbar-thumb:hover {
2047
- background: var(--accent-hover);
2048
- }
2049
-
2050
- body::before {
2051
- content: '';
2052
- position: fixed;
2053
- top: 0; left: 0; right: 0; bottom: 0;
2054
- background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M 20 0 L 0 0 0 20" fill="none" stroke="rgba(255,255,255,0.02)" stroke-width="1"/></pattern></defs><rect width="100" height="100" fill="url(%23grid)"/></svg>') repeat;
2055
- pointer-events: none;
2056
- z-index: -1;
2057
- }
2058
-
2059
- body::after {
2060
- content: '';
2061
- position: fixed;
2062
- top: 50%; left: 50%;
2063
- width: 500px; height: 500px;
2064
- background: radial-gradient(circle, rgba(255, 107, 107, 0.03) 0%, transparent 70%);
2065
- transform: translate(-50%, -50%);
2066
- pointer-events: none;
2067
- z-index: -1;
2068
- animation: pulse 8s ease-in-out infinite;
2069
- }
2070
-
2071
- @keyframes pulse {
2072
- 0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
2073
- 50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
2074
- }
2075
-
2076
- .modal {
2077
- background: var(--bg-glass);
2078
- backdrop-filter: blur(20px);
2079
- border-radius: 20px;
2080
- box-shadow: var(--shadow);
2081
- border: 2px solid var(--border-color);
2082
- width: 100%;
2083
- max-width: 600px;
2084
- overflow: hidden;
2085
- animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
2086
- position: relative;
2087
- }
2088
-
2089
- .modal::before {
2090
- content: '';
2091
- position: absolute;
2092
- top: 0; left: 0; right: 0; bottom: 0;
2093
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
2094
- animation: shimmer 3s infinite;
2095
- pointer-events: none;
2096
- }
2097
-
2098
- @keyframes shimmer {
2099
- 0% { transform: translateX(-100%); }
2100
- 100% { transform: translateX(100%); }
2101
- }
2102
-
2103
- @keyframes modalIn {
2104
- from {
2105
- opacity: 0;
2106
- transform: scale(0.9) translateY(-20px);
2107
- }
2108
- to {
2109
- opacity: 1;
2110
- transform: scale(1) translateY(0);
2111
- }
2112
- }
2113
-
2114
- .header {
2115
- background: var(--header-bg);
2116
- color: var(--text-primary);
2117
- padding: 24px 28px;
2118
- display: flex;
2119
- align-items: center;
2120
- justify-content: space-between;
2121
- position: relative;
2122
- overflow: hidden;
2123
- }
2124
-
2125
- .header::before {
2126
- content: '';
2127
- position: absolute;
2128
- top: 0; left: 0; right: 0; bottom: 0;
2129
- background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.03) 50%, transparent 70%);
2130
- animation: headerShimmer 4s infinite;
2131
- pointer-events: none;
2132
- }
2133
-
2134
- @keyframes headerShimmer {
2135
- 0% { transform: translateX(-100%); }
2136
- 100% { transform: translateX(100%); }
2137
- }
2138
-
2139
- .header-left {
2140
- display: flex;
2141
- align-items: center;
2142
- gap: 16px;
2143
- z-index: 1;
2144
- }
2145
-
2146
- .logo {
2147
- width: 32px;
2148
- height: 32px;
2149
- filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
2150
- animation: logoFloat 3s ease-in-out infinite;
2151
- }
2152
-
2153
- @keyframes logoFloat {
2154
- 0%, 100% { transform: translateY(0px); }
2155
- 50% { transform: translateY(-3px); }
2156
- }
2157
-
2158
- .title {
2159
- font-size: 20px;
2160
- font-weight: 700;
2161
- text-shadow: 0 2px 4px rgba(0,0,0,0.1);
2162
- }
2163
-
2164
- .github-link {
2165
- color: var(--text-primary);
2166
- text-decoration: none;
2167
- display: flex;
2168
- align-items: center;
2169
- gap: 8px;
2170
- font-size: 14px;
2171
- background: rgba(255,255,255,0.08);
2172
- border: 1px solid rgba(255, 255, 255, 0.12);
2173
- backdrop-filter: blur(10px);
2174
- padding: 8px 16px;
2175
- border-radius: 8px;
2176
- font-weight: 500;
2177
- z-index: 1;
2178
- transition: all 0.3s ease;
2179
- }
2180
-
2181
- .github-link:hover {
2182
- background: rgba(255,255,255,0.15);
2183
- transform: translateY(-1px);
2184
- box-shadow: 0 4px 12px rgba(0,0,0,0.3);
2185
- }
2186
-
2187
- .content {
2188
- padding: 32px 24px;
2189
- position: relative;
2190
- }
2191
-
2192
- @keyframes fadeInUp {
2193
- from {
2194
- opacity: 0;
2195
- transform: translateY(20px);
2196
- }
2197
- to {
2198
- opacity: 1;
2199
- transform: translateY(0);
2200
- }
2201
- }
2202
-
2203
- .content-title {
2204
- font-size: 24px;
2205
- margin-bottom: 8px;
2206
- color: var(--text-primary);
2207
- display: flex;
2208
- align-items: center;
2209
- gap: 12px;
2210
- position: relative;
2211
- animation: fadeInUp 0.8s ease-out 0.2s both;
2212
- }
2213
-
2214
- .message {
2215
- background: rgba(103, 233, 233, 0.1);
2216
- border: 1px solid var(--accent-color);
2217
- border-left: 4px solid var(--accent-color);
2218
- padding: 20px;
2219
- border-radius: 12px;
2220
- margin-bottom: 24px;
2221
- font-size: 15px;
2222
- line-height: 1.6;
2223
- color: var(--text-primary);
2224
- animation: fadeInUp 0.8s ease-out 0.4s both;
2225
- position: relative;
2226
- overflow: scroll;
2227
- white-space: pre;
2228
- max-height: 300px;
2229
- }
2230
-
2231
- .message::before {
2232
- content: '';
2233
- position: absolute;
2234
- top: 0; left: 0;
2235
- width: 100%; height: 2px;
2236
- background: linear-gradient(90deg, var(--accent-color), transparent);
2237
- animation: progress 2s ease-out;
2238
- }
2239
-
2240
- @keyframes progress {
2241
- from { width: 0%; }
2242
- to { width: 100%; }
2243
- }
2244
-
2245
- .risks {
2246
- background: var(--warning-bg);
2247
- border: 1px solid var(--warning-border);
2248
- border-radius: 12px;
2249
- padding: 20px;
2250
- margin-bottom: 24px;
2251
- animation: fadeInUp 0.8s ease-out 0.6s both;
2252
- }
2253
-
2254
- .risks-title {
2255
- color: var(--warning-color);
2256
- font-weight: 600;
2257
- margin-bottom: 12px;
2258
- display: flex;
2259
- align-items: center;
2260
- gap: 8px;
2261
- animation: warningGlow 2s ease-in-out infinite;
2262
- }
2263
-
2264
- @keyframes warningGlow {
2265
- 0%, 100% { filter: drop-shadow(0 0 2px rgba(255, 107, 107, 0.3)); }
2266
- 50% { filter: drop-shadow(0 0 8px rgba(255, 107, 107, 0.6)); }
2267
- }
2268
-
2269
- .risk-item {
2270
- color: var(--text-primary);
2271
- margin-bottom: 8px;
2272
- padding-left: 24px;
2273
- position: relative;
2274
- }
2275
-
2276
- .risk-item:before {
2277
- content: "⚠️";
2278
- position: absolute;
2279
- left: 0;
2280
- color: var(--warning-color);
2281
- }
2282
-
2283
- .options {
2284
- margin-bottom: 24px;
2285
- animation: fadeInUp 0.8s ease-out 0.8s both;
2286
- }
2287
-
2288
- .option-item {
2289
- padding: 16px 20px;
2290
- border: 1px solid var(--border-color);
2291
- border-radius: 12px;
2292
- margin-bottom: 12px;
2293
- cursor: pointer;
2294
- transition: all 0.3s ease;
2295
- display: flex;
2296
- align-items: center;
2297
- gap: 14px;
2298
- background: rgba(255, 255, 255, 0.03);
2299
- position: relative;
2300
- overflow: hidden;
2301
- color: var(--text-primary);
2302
- }
2303
-
2304
- .option-item::before {
2305
- content: '';
2306
- position: absolute;
2307
- left: 0; top: 0; bottom: 0;
2308
- width: 0;
2309
- background: var(--accent-color);
2310
- transition: width 0.3s ease;
2311
- }
2312
-
2313
- .option-item.confirm::before {
2314
- background: var(--accent-color);
2315
- }
2316
-
2317
- .option-item.cancel::before {
2318
- background: var(--warning-color);
2319
- }
2320
-
2321
- .option-item:hover {
2322
- background: var(--bg-secondary);
2323
- transform: translateX(5px);
2324
- }
2325
-
2326
- .option-item:hover::before {
2327
- width: 4px;
2328
- }
2329
-
2330
- .option-item.selected {
2331
- background: rgba(103, 233, 233, 0.1);
2332
- border-color: var(--accent-color);
2333
- transform: translateX(5px);
2334
- }
2335
-
2336
- .option-item.selected.cancel {
2337
- background: rgba(255, 107, 107, 0.1);
2338
- border-color: var(--warning-color);
2339
- }
2340
-
2341
- .option-item.selected::before {
2342
- width: 4px;
2343
- }
2344
-
2345
- .option-icon {
2346
- width: 20px;
2347
- height: 20px;
2348
- color: var(--accent-color);
2349
- flex-shrink: 0;
2350
- }
2351
-
2352
- .option-item.cancel .option-icon {
2353
- color: var(--warning-color);
2354
- }
2355
-
2356
- .loading {
2357
- display: none;
2358
- align-items: center;
2359
- justify-content: center;
2360
- gap: 8px;
2361
- margin-top: 16px;
2362
- color: var(--text-secondary);
2363
- font-size: 14px;
2364
- animation: fadeInUp 0.8s ease-out 1s both;
2365
- }
2366
-
2367
- .spinner {
2368
- width: 16px;
2369
- height: 16px;
2370
- border: 2px solid var(--border-color);
2371
- border-top: 2px solid var(--accent-color);
2372
- border-radius: 50%;
2373
- animation: spin 1s linear infinite;
2374
- }
2375
-
2376
- @keyframes spin {
2377
- 0% { transform: rotate(0deg); }
2378
- 100% { transform: rotate(360deg); }
2379
- }
2380
- </style>
2381
- </head>
2382
- <body>
2383
- <div class="modal">
2384
- <div class="header">
2385
- <div class="header-left">
2386
- <img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
2387
- <span class="title">CloudBase AI Toolkit</span>
2388
- </div>
2389
- <a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" target="_blank" class="github-link">
2390
- <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
2391
- <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
2392
- </svg>
2393
- GitHub
2394
- </a>
2395
- </div>
2396
-
2397
- <div class="content">
2398
- <h1 class="content-title">
2399
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2400
- <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
2401
- <line x1="12" y1="9" x2="12" y2="13"/>
2402
- <line x1="12" y1="17" x2="12.01" y2="17"/>
2403
- </svg>
2404
- 操作确认
2405
- </h1>
2406
- <div class="message">${message}</div>
2407
-
2408
- ${risks && risks.length > 0 ? `
2409
- <div class="risks">
2410
- <div class="risks-title">
2411
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2412
- <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
2413
- <line x1="12" y1="9" x2="12" y2="13"/>
2414
- <line x1="12" y1="17" x2="12.01" y2="17"/>
2415
- </svg>
2416
- 风险提示
2417
- </div>
2418
- ${risks.map(risk => `<div class="risk-item">${risk}</div>`).join('')}
2419
- </div>
2420
- ` : ''}
2421
-
2422
- <div class="options">
2423
- ${availableOptions.map((option, index) => {
2424
- const isCancel = option.includes('取消') || option.toLowerCase().includes('cancel');
2425
- const className = isCancel ? 'cancel' : 'confirm';
2426
- const iconPath = isCancel
2427
- ? '<path d="M18 6L6 18M6 6l12 12"/>'
2428
- : '<path d="M20 6L9 17l-5-5"/>';
2429
- return `
2430
- <div class="option-item ${className}" onclick="selectOption('${option}')" style="animation-delay: ${index * 0.1}s;">
2431
- <svg class="option-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
2432
- ${iconPath}
2433
- </svg>
2434
- <span>${option}</span>
2435
- </div>
2436
- `;
2437
- }).join('')}
2438
- </div>
2439
-
2440
- <div class="loading" id="loading">
2441
- <div class="spinner"></div>
2442
- <span>正在处理...</span>
2443
- </div>
2444
- </div>
2445
- </div>
2446
-
2447
- <script>
2448
- let selectedOption = null;
2449
-
2450
- function selectOption(option) {
2451
- selectedOption = option;
2452
-
2453
- document.querySelectorAll('.option-item').forEach(item => {
2454
- item.classList.remove('selected');
2455
- });
2456
- event.currentTarget.classList.add('selected');
2457
-
2458
- // Auto submit after selection
2459
- setTimeout(() => {
2460
- submit();
2461
- }, 500);
2462
- }
2463
-
2464
- function submit() {
2465
- if (!selectedOption) return;
2466
-
2467
- document.getElementById('loading').style.display = 'flex';
2468
-
2469
- const isConfirmed = !selectedOption.includes('取消') && !selectedOption.toLowerCase().includes('cancel');
2470
-
2471
- fetch('/api/submit', {
2472
- method: 'POST',
2473
- headers: { 'Content-Type': 'application/json' },
2474
- body: JSON.stringify({
2475
- type: 'confirmation',
2476
- data: {
2477
- confirmed: isConfirmed,
2478
- option: selectedOption
2479
- }
2480
- })
2481
- }).then(response => response.json())
2482
- .then(result => {
2483
- if (result.success) {
2484
- window.close();
2485
- }
2486
- }).catch(err => {
2487
- console.error('Error:', err);
2488
- document.getElementById('loading').style.display = 'none';
2489
- });
2490
- }
2491
-
2492
- function cancel() {
2493
- fetch('/api/cancel', {
2494
- method: 'POST',
2495
- headers: { 'Content-Type': 'application/json' }
2496
- }).then(() => {
2497
- window.close();
2498
- });
2499
- }
2500
- </script>
2501
- </body>
2502
- </html>`;
2503
- }
2504
- // 公共方法获取运行状态
2505
- get running() {
2506
- return this.isRunning;
2507
- }
2508
- // 公共方法获取端口
2509
- get currentPort() {
2510
- return this.port;
2511
- }
2512
- }
2513
- // 单例实例
2514
- let interactiveServerInstance = null;
2515
- export function getInteractiveServer() {
2516
- if (!interactiveServerInstance) {
2517
- interactiveServerInstance = new InteractiveServer();
2518
- }
2519
- return interactiveServerInstance;
2520
- }
2521
- export async function resetInteractiveServer() {
2522
- if (interactiveServerInstance) {
2523
- try {
2524
- await interactiveServerInstance.stop();
2525
- }
2526
- catch (err) {
2527
- error('Error stopping existing server instance:', err);
2528
- }
2529
- interactiveServerInstance = null;
2530
- }
2531
- }
2532
- export async function getInteractiveServerSafe() {
2533
- // 如果当前实例存在但不在运行状态,先清理
2534
- if (interactiveServerInstance && !interactiveServerInstance.running) {
2535
- try {
2536
- await interactiveServerInstance.stop();
2537
- }
2538
- catch (err) {
2539
- debug('Error stopping non-running server:', err);
2540
- }
2541
- interactiveServerInstance = null;
2542
- }
2543
- return getInteractiveServer();
2544
- }
2545
- //# sourceMappingURL=interactive-server.js.map